diff options
| author | Adam <[email protected]> | 2025-09-26 11:41:15 -0500 |
|---|---|---|
| committer | Adam <[email protected]> | 2025-10-02 08:34:01 -0500 |
| commit | cc955098cd8714bcf1cc91e6a4a6625e38710b05 (patch) | |
| tree | 49adeef653a6705c37b2e06cde1b5066765eeed3 /packages/app/src/context | |
| parent | 8699e896e604762d45df7d4e1b3433e69575e9ab (diff) | |
| download | opencode-cc955098cd8714bcf1cc91e6a4a6625e38710b05.tar.gz opencode-cc955098cd8714bcf1cc91e6a4a6625e38710b05.zip | |
wip: desktop work
Diffstat (limited to 'packages/app/src/context')
| -rw-r--r-- | packages/app/src/context/local.tsx | 185 | ||||
| -rw-r--r-- | packages/app/src/context/sync.tsx | 2 |
2 files changed, 146 insertions, 41 deletions
diff --git a/packages/app/src/context/local.tsx b/packages/app/src/context/local.tsx index 03d180a4d..4c2a3d3be 100644 --- a/packages/app/src/context/local.tsx +++ b/packages/app/src/context/local.tsx @@ -25,6 +25,9 @@ export type LocalModel = Omit<Model, "provider"> & { } export type ModelKey = { providerID: string; modelID: string } +export type FileContext = { type: "file"; path: string; selection?: TextSelection } +export type ContextItem = FileContext + function init() { const sdk = useSDK() const sync = useSync() @@ -163,7 +166,16 @@ function init() { } const resetNode = (path: string) => { - setStore("node", path, undefined!) + setStore("node", path, { + loaded: undefined, + pinned: undefined, + content: undefined, + selection: undefined, + scrollTop: undefined, + folded: undefined, + view: undefined, + selectedChange: undefined, + }) } const relative = (path: string) => path.replace(sync.data.path.directory + "/", "") @@ -203,6 +215,7 @@ function init() { ] }) setStore("active", relativePath) + context.addActive() if (options?.pinned) setStore("node", path, "pinned", true) if (options?.view && store.node[relativePath].view === undefined) setStore("node", path, "view", options.view) if (store.node[relativePath].loaded) return @@ -336,52 +349,103 @@ function init() { })() const layout = (() => { - const [store, setStore] = createStore<{ - rightPane: boolean - leftWidth: number - rightWidth: number - }>({ - rightPane: false, - leftWidth: 200, // Default 50 * 4px (w-50 = 12.5rem = 200px) - rightWidth: 320, // Default 80 * 4px (w-80 = 20rem = 320px) - }) - - const value = localStorage.getItem("layout") - if (value) { - const v = JSON.parse(value) - if (typeof v?.rightPane === "boolean") setStore("rightPane", v.rightPane) - if (typeof v?.leftWidth === "number") setStore("leftWidth", Math.max(150, Math.min(400, v.leftWidth))) - if (typeof v?.rightWidth === "number") setStore("rightWidth", Math.max(200, Math.min(500, v.rightWidth))) + type PaneState = { size: number; visible: boolean } + type LayoutState = { panes: Record<string, PaneState>; order: string[] } + type PaneDefault = number | { size: number; visible?: boolean } + + const [store, setStore] = createStore<Record<string, LayoutState>>({}) + + const raw = localStorage.getItem("layout") + if (raw) { + const data = JSON.parse(raw) + if (data && typeof data === "object" && !Array.isArray(data)) { + const first = Object.values(data)[0] as LayoutState + if (first && typeof first === "object" && "panes" in first) { + setStore(() => data as Record<string, LayoutState>) + } + } } + createEffect(() => { localStorage.setItem("layout", JSON.stringify(store)) }) + const normalize = (value: PaneDefault): PaneState => { + if (typeof value === "number") return { size: value, visible: true } + return { size: value.size, visible: value.visible ?? true } + } + + const ensure = (id: string, defaults: Record<string, PaneDefault>) => { + const entries = Object.entries(defaults) + if (!entries.length) return + setStore(id, (current) => { + if (current) return current + return { + panes: Object.fromEntries(entries.map(([pane, config]) => [pane, normalize(config)])), + order: entries.map(([pane]) => pane), + } + }) + for (const [pane, config] of entries) { + if (!store[id]?.panes[pane]) { + setStore(id, "panes", pane, () => normalize(config)) + } + if (!(store[id]?.order ?? []).includes(pane)) { + setStore(id, "order", (list) => [...list, pane]) + } + } + } + + const ensurePane = (id: string, pane: string, fallback?: PaneDefault) => { + if (!store[id]) { + const value = normalize(fallback ?? { size: 0, visible: true }) + setStore(id, () => ({ + panes: { [pane]: value }, + order: [pane], + })) + return + } + if (!store[id].panes[pane]) { + const value = normalize(fallback ?? { size: 0, visible: true }) + setStore(id, "panes", pane, () => value) + } + if (!store[id].order.includes(pane)) { + setStore(id, "order", (list) => [...list, pane]) + } + } + + const size = (id: string, pane: string) => store[id]?.panes[pane]?.size ?? 0 + const visible = (id: string, pane: string) => store[id]?.panes[pane]?.visible ?? false + + const setSize = (id: string, pane: string, value: number) => { + if (!store[id]?.panes[pane]) return + const next = Number.isFinite(value) ? Math.max(0, Math.min(100, value)) : 0 + setStore(id, "panes", pane, "size", next) + } + + const setVisible = (id: string, pane: string, value: boolean) => { + if (!store[id]?.panes[pane]) return + setStore(id, "panes", pane, "visible", value) + } + + const toggle = (id: string, pane: string) => { + setVisible(id, pane, !visible(id, pane)) + } + + const show = (id: string, pane: string) => setVisible(id, pane, true) + const hide = (id: string, pane: string) => setVisible(id, pane, false) + const order = (id: string) => store[id]?.order ?? [] + return { - rightPane() { - return store.rightPane - }, - leftWidth() { - return store.leftWidth - }, - rightWidth() { - return store.rightWidth - }, - toggleRightPane() { - setStore("rightPane", (x) => !x) - }, - openRightPane() { - setStore("rightPane", true) - }, - closeRightPane() { - setStore("rightPane", false) - }, - setLeftWidth(width: number) { - setStore("leftWidth", Math.max(150, Math.min(400, width))) - }, - setRightWidth(width: number) { - setStore("rightWidth", Math.max(200, Math.min(500, width))) - }, + ensure, + ensurePane, + size, + visible, + setSize, + setVisible, + toggle, + show, + hide, + order, } })() @@ -406,12 +470,51 @@ function init() { } })() + const context = (() => { + const [store, setStore] = createStore<{ + activeTab: boolean + items: (ContextItem & { key: string })[] + }>({ + activeTab: true, + items: [], + }) + + return { + all() { + return store.items + }, + active() { + return store.activeTab ? file.active() : undefined + }, + addActive() { + setStore("activeTab", true) + }, + removeActive() { + setStore("activeTab", false) + }, + add(item: ContextItem) { + let key = item.type + switch (item.type) { + case "file": + key += `${item.path}:${item.selection?.startLine}:${item.selection?.endLine}` + break + } + if (store.items.find((x) => x.key === key)) return + setStore("items", (x) => [...x, { key, ...item }]) + }, + remove(key: string) { + setStore("items", (x) => x.filter((x) => x.key !== key)) + }, + } + })() + const result = { model, agent, file, layout, session, + context, } return result } diff --git a/packages/app/src/context/sync.tsx b/packages/app/src/context/sync.tsx index 796919676..5d64f3ee7 100644 --- a/packages/app/src/context/sync.tsx +++ b/packages/app/src/context/sync.tsx @@ -115,6 +115,7 @@ function init() { const sanitizer = createMemo(() => new RegExp(`${store.path.directory}/`, "g")) const sanitize = (text: string) => text.replace(sanitizer(), "") + const absolute = (path: string) => (store.path.directory + "/" + path).replace("//", "/") return { data: store, @@ -146,6 +147,7 @@ function init() { }, }, load, + absolute, sanitize, } } |
