(scope: ServerScope, dir: string, id: string | undefined)
| 35 | } |
| 36 | |
| 37 | function createViewSession(scope: ServerScope, dir: string, id: string | undefined) { |
| 38 | const legacyViewKey = `${dir}/file${id ? "/" + id : ""}.v1` |
| 39 | |
| 40 | const [view, setView, _, ready] = persisted( |
| 41 | Persist.serverScoped(scope, dir, id, "file-view", [legacyViewKey]), |
| 42 | createStore<{ |
| 43 | file: Record<string, FileViewState> |
| 44 | }>({ |
| 45 | file: {}, |
| 46 | }), |
| 47 | ) |
| 48 | |
| 49 | const meta = { pruned: false } |
| 50 | |
| 51 | const pruneView = (keep?: string) => { |
| 52 | const keys = Object.keys(view.file) |
| 53 | if (keys.length <= MAX_VIEW_FILES) return |
| 54 | |
| 55 | const drop = keys.filter((key) => key !== keep).slice(0, keys.length - MAX_VIEW_FILES) |
| 56 | if (drop.length === 0) return |
| 57 | |
| 58 | setView( |
| 59 | produce((draft) => { |
| 60 | for (const key of drop) { |
| 61 | delete draft.file[key] |
| 62 | } |
| 63 | }), |
| 64 | ) |
| 65 | } |
| 66 | |
| 67 | createEffect(() => { |
| 68 | if (!ready()) return |
| 69 | if (meta.pruned) return |
| 70 | meta.pruned = true |
| 71 | pruneView() |
| 72 | }) |
| 73 | |
| 74 | const scrollTop = (path: string) => view.file[path]?.scrollTop |
| 75 | const scrollLeft = (path: string) => view.file[path]?.scrollLeft |
| 76 | const selectedLines = (path: string) => view.file[path]?.selectedLines |
| 77 | |
| 78 | const setScrollTop = (path: string, top: number) => { |
| 79 | setView( |
| 80 | produce((draft) => { |
| 81 | const file = draft.file[path] ?? (draft.file[path] = {}) |
| 82 | if (file.scrollTop === top) return |
| 83 | file.scrollTop = top |
| 84 | }), |
| 85 | ) |
| 86 | pruneView(path) |
| 87 | } |
| 88 | |
| 89 | const setScrollLeft = (path: string, left: number) => { |
| 90 | setView( |
| 91 | produce((draft) => { |
| 92 | const file = draft.file[path] ?? (draft.file[path] = {}) |
| 93 | if (file.scrollLeft === left) return |
| 94 | file.scrollLeft = left |
no test coverage detected