Download packages/app/src/context/layout-scroll.ts from SaylorTwift/opencode: direct link, hf CLI and curl.
- Browser
- Download file 2.82 kB
-
https://huggingface.co/SaylorTwift/opencode/resolve/main/packages/app/src/context/layout-scroll.ts
- Command line
-
hf download hf://SaylorTwift/opencode/packages/app/src/context/layout-scroll.ts
-
curl -L -o layout-scroll.ts https://huggingface.co/SaylorTwift/opencode/resolve/main/packages/app/src/context/layout-scroll.ts
2.82 kB
| import { createStore, produce } from "solid-js/store" | |
| export type SessionScroll = { | |
| x: number | |
| y: number | |
| } | |
| type ScrollMap = Record<string, SessionScroll> | |
| type Options = { | |
| debounceMs?: number | |
| getSnapshot: (sessionKey: string) => ScrollMap | undefined | |
| onFlush: (sessionKey: string, scroll: ScrollMap) => void | |
| } | |
| export function createScrollPersistence(opts: Options) { | |
| const wait = opts.debounceMs ?? 200 | |
| const [cache, setCache] = createStore<Record<string, ScrollMap>>({}) | |
| const dirty = new Set<string>() | |
| const timers = new Map<string, ReturnType<typeof setTimeout>>() | |
| function clone(input?: ScrollMap) { | |
| const out: ScrollMap = {} | |
| if (!input) return out | |
| for (const key of Object.keys(input)) { | |
| const pos = input[key] | |
| if (!pos) continue | |
| out[key] = { x: pos.x, y: pos.y } | |
| } | |
| return out | |
| } | |
| function seed(sessionKey: string) { | |
| const next = clone(opts.getSnapshot(sessionKey)) | |
| const current = cache[sessionKey] | |
| if (!current) { | |
| setCache(sessionKey, next) | |
| return | |
| } | |
| if (Object.keys(current).length > 0) return | |
| if (Object.keys(next).length === 0) return | |
| setCache(sessionKey, next) | |
| } | |
| function scroll(sessionKey: string, tab: string) { | |
| seed(sessionKey) | |
| return cache[sessionKey]?.[tab] ?? opts.getSnapshot(sessionKey)?.[tab] | |
| } | |
| function schedule(sessionKey: string) { | |
| const prev = timers.get(sessionKey) | |
| if (prev) clearTimeout(prev) | |
| timers.set( | |
| sessionKey, | |
| setTimeout(() => flush(sessionKey), wait), | |
| ) | |
| } | |
| function setScroll(sessionKey: string, tab: string, pos: SessionScroll) { | |
| seed(sessionKey) | |
| const prev = cache[sessionKey]?.[tab] | |
| if (prev?.x === pos.x && prev?.y === pos.y) return | |
| setCache(sessionKey, tab, { x: pos.x, y: pos.y }) | |
| dirty.add(sessionKey) | |
| schedule(sessionKey) | |
| } | |
| function flush(sessionKey: string) { | |
| const timer = timers.get(sessionKey) | |
| if (timer) clearTimeout(timer) | |
| timers.delete(sessionKey) | |
| if (!dirty.has(sessionKey)) return | |
| dirty.delete(sessionKey) | |
| opts.onFlush(sessionKey, clone(cache[sessionKey])) | |
| } | |
| function flushAll() { | |
| const keys = Array.from(dirty) | |
| if (keys.length === 0) return | |
| for (const key of keys) { | |
| flush(key) | |
| } | |
| } | |
| function drop(keys: string[]) { | |
| if (keys.length === 0) return | |
| for (const key of keys) { | |
| const timer = timers.get(key) | |
| if (timer) clearTimeout(timer) | |
| timers.delete(key) | |
| dirty.delete(key) | |
| } | |
| setCache( | |
| produce((draft) => { | |
| for (const key of keys) { | |
| delete draft[key] | |
| } | |
| }), | |
| ) | |
| } | |
| function dispose() { | |
| drop(Array.from(timers.keys())) | |
| } | |
| return { | |
| cache, | |
| drop, | |
| flush, | |
| flushAll, | |
| scroll, | |
| seed, | |
| setScroll, | |
| dispose, | |
| } | |
| } | |