summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/context
diff options
context:
space:
mode:
authorAdam <[email protected]>2025-09-26 11:41:15 -0500
committerAdam <[email protected]>2025-10-02 08:34:01 -0500
commitcc955098cd8714bcf1cc91e6a4a6625e38710b05 (patch)
tree49adeef653a6705c37b2e06cde1b5066765eeed3 /packages/app/src/context
parent8699e896e604762d45df7d4e1b3433e69575e9ab (diff)
downloadopencode-cc955098cd8714bcf1cc91e6a4a6625e38710b05.tar.gz
opencode-cc955098cd8714bcf1cc91e6a4a6625e38710b05.zip
wip: desktop work
Diffstat (limited to 'packages/app/src/context')
-rw-r--r--packages/app/src/context/local.tsx185
-rw-r--r--packages/app/src/context/sync.tsx2
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,
}
}