diff options
| author | Adam Malczewski <[email protected]> | 2026-06-12 18:26:00 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-12 18:26:00 +0900 |
| commit | 1764e3e5dff836255d121a933dd92542368346f9 (patch) | |
| tree | b835055de0f0f1fd9750741764dac8b30f7498bf /src/features/smart-scroll/ui | |
| parent | 4001274e3ba25a3946df1e9f2dc82ca6781cd2bf (diff) | |
| download | dispatch-web-1764e3e5dff836255d121a933dd92542368346f9.tar.gz dispatch-web-1764e3e5dff836255d121a933dd92542368346f9.zip | |
feat(chat): chat limit — bulk quarter-unload, 75% fresh-load window, show-earlier page-in
Long transcripts no longer grow unbounded: past the chat limit (default 256
chunks, localStorage dispatch.chatLimit) the oldest ceil(limit/4) committed
chunks are unloaded in ONE bulk pass — never one-per-delta (old Dispatch's
scroll-jump-per-step bug) — and only while the reader is stuck to the bottom
(scrolled-up readers defer the trim; it catches up in whole quarters). A fresh
page load windows to the newest floor(0.75*limit). Unloading is purely local
(IndexedDB cache + server keep everything); a hiddenBeforeSeq watermark keeps
history merges from resurrecting unloaded chunks, and a 'Show earlier messages'
affordance pages a quarter back in from the cache with scroll-anchor
preservation. Thinking-collapse render keys stay stable across trims via a
hiddenThinkingCount ordinal base.
- core/chunks/trim.ts: pure policy (trim/window/restore/normalize) + tests
- chat store: chatLimit + canUnload deps, windowed load, showEarlier()
- composition root: dispatch.chatLimit localStorage knob + unload gate wired
to smart-scroll isAtBottom()
- backend CR-5 OPENED (not a blocker): ?limit=/?beforeSeq= on
GET /conversations/:id (courier backend-handoff-chat-limit.md)
- scripts/live-probe.ts: fix pre-existing stale TurnMetricsEntry reads
(m1.usage -> total.usage) that crashed the probe; 17/17 live checks pass
Diffstat (limited to 'src/features/smart-scroll/ui')
| -rw-r--r-- | src/features/smart-scroll/ui/controller.svelte.ts | 10 |
1 files changed, 10 insertions, 0 deletions
diff --git a/src/features/smart-scroll/ui/controller.svelte.ts b/src/features/smart-scroll/ui/controller.svelte.ts index 99d53ca..dbe65d1 100644 --- a/src/features/smart-scroll/ui/controller.svelte.ts +++ b/src/features/smart-scroll/ui/controller.svelte.ts @@ -22,6 +22,12 @@ export interface SmartScrollController { /** Reactive: show the "scroll to bottom" affordance (the user has scrolled up). */ readonly showButton: boolean; /** + * Non-reactive point-in-time query: is the view stuck to the bottom right now? + * For imperative callers (e.g. the chat-limit unload gate) that poll at event + * time rather than subscribing — reads the reducer state, not a rune. + */ + isAtBottom(): boolean; + /** * Attach to the scroll container; returns a teardown to call on unmount. * Pass the inner CONTENT element to also follow height changes that aren't a * transcript update (async markdown/highlight, image loads, a collapse toggling, @@ -84,6 +90,10 @@ export function createSmartScrollController(): SmartScrollController { return showButton; }, + isAtBottom(): boolean { + return state.stuck; + }, + attach(node: HTMLElement, content?: HTMLElement): () => void { el = node; node.addEventListener("scroll", handleScroll, { passive: true }); |
