Files
openchamber/packages/ui/src/sync/scoped-blocking-requests.test.ts
T
bashrusakh 59ecd86b4b perf: isolate chat streaming renders and reduce sidebar render cost (#1672)
Reworks the chat and session-sidebar render paths to cut render cascades, memory
  churn, and UI jank on large sessions and big session trees. Behavior is preserved;
  the changes are about *when* and *how much* the UI re-renders.

  ## Chat streaming
  - Freeze the streaming message's parts in the bulk turn projection during streaming,
    and re-inject live parts only in an isolated tail leaf, so a ~60/sec delta stream
    no longer re-runs the whole-session projection or re-renders unrelated rows.
    session with referential reuse of unchanged turns.
  - Memoize message rows with field-aware comparators instead of reference equality.
  - Replace the manual child-session polling in the task tool with the live SSE
    stream + a one-shot load, removing a fetch/settle state machine.

  ## History loading & scroll
  - Load an initial page fast, then prepend one older page in the background so the
    scroll container has headroom and "load older on scroll-up" fires before the user
    hits the absolute top.
  - Compensate scroll synchronously (in a layout effect, before paint) for prepends —
    including background prepends that don't originate from a user scroll — so the
    viewport stays stable instead of judder-correcting on the next frame.

  ## Markdown rendering
  - Render markdown synchronously *styled* on first paint (paragraphs, lists, code
    cards, tables, inline code) instead of raw escaped text; the async pass then only
    upgrades syntax-highlight colors. Eliminates the flash of full-width raw text.
  - Load KaTeX CSS eagerly with the main bundle instead of inside the lazy markdown
    chunk, avoiding a late stylesheet injection on first render.

  ## Sidebar
  - Hoist per-row recursive tree walks out of row comparators into per-group
    precomputed sets/keys; batch live-session lookups into a single map; add a
    group-level memo boundary.
  - Isolate rename drafts so per-keystroke typing doesn't repaint the row tree.

  ## Sync layer
  - Add a staleness guard so a slow message fetch can't repopulate a session the user
    navigated away from.
  - Throw on fetch failure for authoritative loaders so a transient blip can't read as
    an empty server response.

  ## Cleanup
  - Remove dead code (unused hooks, params, duplicated inline types) surfaced while
    reworking the above.

  ## Known issue
  - A rare, purely cosmetic first-paint width flash can still appear on large sessions;
    it has no behavioral or data impact and is tracked for a follow-up runtime trace.
2026-06-18 00:43:16 +03:00

55 lines
1.9 KiB
TypeScript

import { describe, expect, test } from "bun:test"
import type { Session } from "@opencode-ai/sdk/v2"
import {
areRequestArraysReferentiallyEqual,
collectScopedBlockingRequests,
} from "./scoped-blocking-requests"
const session = (id: string, parentID?: string): Session => ({ id, parentID }) as Session
describe("scoped blocking requests", () => {
test("collects requests for the current session subtree", () => {
const rootRequest = { id: "perm_root" }
const childRequest = { id: "perm_child" }
const grandchildRequest = { id: "perm_grandchild" }
const siblingRequest = { id: "perm_sibling" }
const empty: Array<typeof rootRequest> = []
const result = collectScopedBlockingRequests(
[
session("ses_root"),
session("ses_child", "ses_root"),
session("ses_grandchild", "ses_child"),
session("ses_sibling"),
],
{
ses_root: [rootRequest],
ses_child: [childRequest],
ses_grandchild: [grandchildRequest],
ses_sibling: [siblingRequest],
},
"ses_root",
empty,
)
expect(result).toEqual([rootRequest, childRequest, grandchildRequest])
})
test("returns the provided empty array when no scoped requests exist", () => {
const empty: Array<{ id: string }> = []
expect(collectScopedBlockingRequests([session("ses_root")], {}, "ses_root", empty)).toBe(empty)
expect(collectScopedBlockingRequests([session("ses_root")], {}, null, empty)).toBe(empty)
})
test("compares request arrays by item identity", () => {
const first = { id: "perm_1" }
const second = { id: "perm_2" }
expect(areRequestArraysReferentiallyEqual([first, second], [first, second])).toBe(true)
expect(areRequestArraysReferentiallyEqual([first, second], [second, first])).toBe(false)
expect(areRequestArraysReferentiallyEqual([first], [{ id: "perm_1" }])).toBe(false)
})
})