mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-29 03:44:06 +08:00
Merge pull request #13529 from Kilo-Org/optimize-harness-resource-usage
fix: reduce offscreen animation and transcript memory overhead
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"kilo-code": patch
|
||||
"@kilocode/kilo-ui": patch
|
||||
"@opencode-ai/ui": patch
|
||||
---
|
||||
|
||||
Stop offscreen loading animations while preserving their original appearance, and release obsolete transcript pages from memory.
|
||||
@@ -0,0 +1,5 @@
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
[data-component="spinner"] > rect {
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/** @jsxImportSource solid-js */
|
||||
import type { Meta, StoryObj } from "storybook-solidjs-vite"
|
||||
import { Spinner } from "@opencode-ai/ui/spinner"
|
||||
import { Spinner } from "@kilocode/kilo-ui/spinner"
|
||||
|
||||
const meta: Meta<typeof Spinner> = {
|
||||
title: "Components/Spinner",
|
||||
@@ -20,6 +20,16 @@ export const Large: Story = {
|
||||
render: () => <Spinner style={{ width: "48px", height: "48px" }} />,
|
||||
}
|
||||
|
||||
export const Parallel: Story = {
|
||||
render: () => (
|
||||
<div style={{ display: "flex", gap: "8px", "flex-wrap": "wrap" }}>
|
||||
{Array.from({ length: 24 }, () => (
|
||||
<Spinner style={{ width: "16px", height: "16px" }} />
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
}
|
||||
|
||||
export const Colored: Story = {
|
||||
render: () => <Spinner style={{ width: "24px", height: "24px", color: "var(--text-interactive-base)" }} />,
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
@import "../components/select.css";
|
||||
@import "../components/session.css";
|
||||
@import "../components/settings-sidebar.css";
|
||||
@import "../components/spinner.css";
|
||||
@import "../components/status-indicator.css";
|
||||
@import "../components/switch.css";
|
||||
@import "../components/tabs.css";
|
||||
|
||||
@@ -14,6 +14,7 @@ Object.assign(globalThis, {
|
||||
HTMLTextAreaElement: window.HTMLTextAreaElement,
|
||||
SVGElement: window.SVGElement,
|
||||
MutationObserver: window.MutationObserver,
|
||||
IntersectionObserver: window.IntersectionObserver,
|
||||
ResizeObserver: window.ResizeObserver,
|
||||
CustomEvent: window.CustomEvent,
|
||||
Event: window.Event,
|
||||
|
||||
@@ -1473,6 +1473,19 @@ export const SessionProvider: ParentComponent = (props) => {
|
||||
// proxy creation for each message object dominated the trace (~900ms).
|
||||
// "prepend" / "reconcile": reconcile to preserve existing proxies.
|
||||
if (mode === "replace") {
|
||||
const keep = new Set(merged.map((message) => message.id))
|
||||
const removed = current.filter((message) => !keep.has(message.id)).map((message) => message.id)
|
||||
clearHiddenErrors(removed)
|
||||
setStore(
|
||||
"parts",
|
||||
produce((parts) => {
|
||||
for (const id of removed) {
|
||||
stash.remove(id)
|
||||
optimisticParts.delete(id)
|
||||
delete parts[id]
|
||||
}
|
||||
}),
|
||||
)
|
||||
setStore("messages", sessionID, merged)
|
||||
} else {
|
||||
setStore("messages", sessionID, reconcile(merged, { key: "id" }))
|
||||
|
||||
@@ -4,3 +4,9 @@
|
||||
width: 18px;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
/* kilocode_change start */
|
||||
[data-component="spinner"][data-paused] > rect {
|
||||
animation: none !important;
|
||||
}
|
||||
/* kilocode_change end */
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ComponentProps, For } from "solid-js"
|
||||
import { observe } from "../kilocode/spinner" // kilocode_change
|
||||
|
||||
const outerIndices = new Set([1, 2, 4, 7, 8, 11, 13, 14])
|
||||
const cornerIndices = new Set([0, 3, 12, 15])
|
||||
@@ -19,6 +20,7 @@ export function Spinner(props: {
|
||||
}) {
|
||||
return (
|
||||
<svg
|
||||
ref={observe /* kilocode_change */}
|
||||
{...props}
|
||||
viewBox="0 0 15 15"
|
||||
data-component="spinner"
|
||||
@@ -37,7 +39,7 @@ export function Spinner(props: {
|
||||
height="3"
|
||||
rx="1"
|
||||
style={{
|
||||
opacity: square.corner ? 0 : undefined,
|
||||
opacity: square.corner ? 0 : square.outer ? 0.15 : 0.4, // kilocode_change
|
||||
animation: square.corner
|
||||
? undefined
|
||||
: `${square.outer ? "pulse-opacity-dim" : "pulse-opacity"} ${square.duration}s ease-in-out infinite`,
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { onCleanup, onMount } from "solid-js"
|
||||
|
||||
const roots = new Map<Element, boolean>()
|
||||
let observer: IntersectionObserver | undefined
|
||||
|
||||
function update(root: Element, visible: boolean) {
|
||||
root.toggleAttribute("data-paused", !visible || document.visibilityState === "hidden")
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
for (const [root, visible] of roots) update(root, visible)
|
||||
}
|
||||
|
||||
export function observe(root: SVGSVGElement) {
|
||||
onMount(() => {
|
||||
if (!observer) {
|
||||
observer = new IntersectionObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
if (!roots.has(entry.target)) continue
|
||||
const visible = entry.isIntersecting && entry.intersectionRatio > 0
|
||||
roots.set(entry.target, visible)
|
||||
update(entry.target, visible)
|
||||
}
|
||||
})
|
||||
document.addEventListener("visibilitychange", refresh)
|
||||
}
|
||||
roots.set(root, false)
|
||||
update(root, false)
|
||||
observer.observe(root)
|
||||
onCleanup(() => {
|
||||
observer?.unobserve(root)
|
||||
roots.delete(root)
|
||||
root.removeAttribute("data-paused")
|
||||
if (roots.size > 0) return
|
||||
observer?.disconnect()
|
||||
observer = undefined
|
||||
document.removeEventListener("visibilitychange", refresh)
|
||||
})
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user