summaryrefslogtreecommitdiffhomepage
path: root/packages/desktop/src/context
diff options
context:
space:
mode:
authorAdam <[email protected]>2025-11-03 14:44:25 -0600
committerAdam <[email protected]>2025-11-03 15:42:10 -0600
commit3d43214075b1f4ba0a3c71d5fea799532bbb7bd9 (patch)
tree46e6bc7cf9535f638eb2fa27fd5edcaedf0dc579 /packages/desktop/src/context
parent178a14ce3ed622b2470079ed317490b796ba64fe (diff)
downloadopencode-3d43214075b1f4ba0a3c71d5fea799532bbb7bd9.tar.gz
opencode-3d43214075b1f4ba0a3c71d5fea799532bbb7bd9.zip
wip: desktop work
Diffstat (limited to 'packages/desktop/src/context')
-rw-r--r--packages/desktop/src/context/local.tsx278
1 files changed, 108 insertions, 170 deletions
diff --git a/packages/desktop/src/context/local.tsx b/packages/desktop/src/context/local.tsx
index 2b844536d..283dddc2c 100644
--- a/packages/desktop/src/context/local.tsx
+++ b/packages/desktop/src/context/local.tsx
@@ -1,16 +1,7 @@
import { createStore, produce, reconcile } from "solid-js/store"
import { batch, createEffect, createMemo } from "solid-js"
-import { pipe, sumBy, uniqueBy } from "remeda"
-import type {
- FileContent,
- FileNode,
- Model,
- Provider,
- File as FileStatus,
- Part,
- Message,
- AssistantMessage,
-} from "@opencode-ai/sdk"
+import { uniqueBy } from "remeda"
+import type { FileContent, FileNode, Model, Provider, File as FileStatus } from "@opencode-ai/sdk"
import { createSimpleContext } from "./helper"
import { useSDK } from "./sdk"
import { useSync } from "./sync"
@@ -204,18 +195,18 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
const file = (() => {
const [store, setStore] = createStore<{
node: Record<string, LocalFile>
- opened: string[]
- active?: string
+ // opened: string[]
+ // active?: string
}>({
node: Object.fromEntries(sync.data.node.map((x) => [x.path, x])),
- opened: [],
+ // opened: [],
})
- const active = createMemo(() => {
- if (!store.active) return undefined
- return store.node[store.active]
- })
- const opened = createMemo(() => store.opened.map((x) => store.node[x]))
+ // const active = createMemo(() => {
+ // if (!store.active) return undefined
+ // return store.node[store.active]
+ // })
+ // const opened = createMemo(() => store.opened.map((x) => store.node[x]))
const changeset = createMemo(() => new Set(sync.data.changes.map((f) => f.path)))
const changes = createMemo(() => Array.from(changeset()).sort((a, b) => a.localeCompare(b)))
@@ -303,16 +294,16 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
const open = async (path: string, options?: { pinned?: boolean; view?: LocalFile["view"] }) => {
const relativePath = relative(path)
if (!store.node[relativePath]) await fetch(path)
- setStore("opened", (x) => {
- if (x.includes(relativePath)) return x
- return [
- ...opened()
- .filter((x) => x.pinned)
- .map((x) => x.path),
- relativePath,
- ]
- })
- setStore("active", relativePath)
+ // setStore("opened", (x) => {
+ // if (x.includes(relativePath)) return x
+ // return [
+ // ...opened()
+ // .filter((x) => x.pinned)
+ // .map((x) => x.path),
+ // relativePath,
+ // ]
+ // })
+ // 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)
@@ -363,22 +354,11 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
})
return {
- active,
- opened,
node: (path: string) => store.node[path],
update: (path: string, node: LocalFile) => setStore("node", path, reconcile(node)),
open,
load,
init,
- close(path: string) {
- setStore("opened", (opened) => opened.filter((x) => x !== path))
- if (store.active === path) {
- const index = store.opened.findIndex((f) => f === path)
- const previous = store.opened[Math.max(0, index - 1)]
- setStore("active", previous)
- }
- resetNode(path)
- },
expand(path: string) {
setStore("node", path, "expanded", true)
if (store.node[path].loaded) return
@@ -394,17 +374,6 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
scroll(path: string, scrollTop: number) {
setStore("node", path, "scrollTop", scrollTop)
},
- move(path: string, to: number) {
- const index = store.opened.findIndex((f) => f === path)
- if (index === -1) return
- setStore(
- "opened",
- produce((opened) => {
- opened.splice(to, 0, opened.splice(index, 1)[0])
- }),
- )
- setStore("node", path, "pinned", true)
- },
view(path: string): View {
const n = store.node[path]
return n && n.view ? n.view : "raw"
@@ -444,14 +413,48 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
},
search,
relative,
+ // active,
+ // opened,
+ // close(path: string) {
+ // setStore("opened", (opened) => opened.filter((x) => x !== path))
+ // if (store.active === path) {
+ // const index = store.opened.findIndex((f) => f === path)
+ // const previous = store.opened[Math.max(0, index - 1)]
+ // setStore("active", previous)
+ // }
+ // resetNode(path)
+ // },
+ // move(path: string, to: number) {
+ // const index = store.opened.findIndex((f) => f === path)
+ // if (index === -1) return
+ // setStore(
+ // "opened",
+ // produce((opened) => {
+ // opened.splice(to, 0, opened.splice(index, 1)[0])
+ // }),
+ // )
+ // setStore("node", path, "pinned", true)
+ // },
}
})()
const session = (() => {
const [store, setStore] = createStore<{
active?: string
- activeMessage?: string
- }>({})
+ tabs: Record<
+ string,
+ {
+ active?: string
+ opened: string[]
+ }
+ >
+ }>({
+ tabs: {
+ "": {
+ opened: [],
+ },
+ },
+ })
const active = createMemo(() => {
if (!store.active) return undefined
@@ -461,134 +464,69 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
createEffect(() => {
if (!store.active) return
sync.session.sync(store.active)
- })
- const valid = (part: Part) => {
- if (!part) return false
- switch (part.type) {
- case "step-start":
- case "step-finish":
- case "file":
- case "patch":
- return false
- case "text":
- return !part.synthetic && part.text.trim()
- case "reasoning":
- return part.text.trim()
- case "tool":
- switch (part.tool) {
- case "todoread":
- case "todowrite":
- case "list":
- case "grep":
- return false
- }
- return true
- default:
- return true
+ if (!store.tabs[store.active]) {
+ setStore("tabs", store.active, {
+ opened: [],
+ })
}
- }
-
- const hasValidParts = (message: Message) => {
- return sync.data.part[message.id]?.filter(valid).length > 0
- }
- // const hasTextPart = (message: Message) => {
- // return !!sync.data.part[message.id]?.filter(valid).find((p) => p.type === "text")
- // }
-
- const messages = createMemo(() => (store.active ? (sync.data.message[store.active] ?? []) : []))
- const messagesWithValidParts = createMemo(() => messages().filter(hasValidParts) ?? [])
- const userMessages = createMemo(() =>
- messages()
- .filter((m) => m.role === "user")
- .sort((a, b) => b.id.localeCompare(a.id)),
- )
-
- const cost = createMemo(() => {
- const total = pipe(
- messages(),
- sumBy((x) => (x.role === "assistant" ? x.cost : 0)),
- )
- return new Intl.NumberFormat("en-US", {
- style: "currency",
- currency: "USD",
- }).format(total)
- })
-
- const last = createMemo(() => {
- return messages().findLast((x) => x.role === "assistant") as AssistantMessage
- })
-
- const lastUserMessage = createMemo(() => {
- return userMessages()?.at(0)
- })
-
- const activeMessage = createMemo(() => {
- if (!store.active || !store.activeMessage) return lastUserMessage()
- return sync.data.message[store.active]?.find((m) => m.id === store.activeMessage)
- })
-
- const model = createMemo(() => {
- if (!last()) return
- const model = sync.data.provider.find((x) => x.id === last().providerID)?.models[last().modelID]
- return model
})
- const tokens = createMemo(() => {
- if (!last()) return
- const tokens = last().tokens
- const total = tokens.input + tokens.output + tokens.reasoning + tokens.cache.read + tokens.cache.write
- return new Intl.NumberFormat("en-US", {
- notation: "compact",
- compactDisplay: "short",
- }).format(total)
- })
-
- const context = createMemo(() => {
- if (!last()) return
- if (!model()?.limit.context) return 0
- const tokens = last().tokens
- const total = tokens.input + tokens.output + tokens.reasoning + tokens.cache.read + tokens.cache.write
- return Math.round((total / model()!.limit.context) * 100)
- })
-
- const getMessageText = (message: Message | Message[] | undefined): string => {
- if (!message) return ""
- if (Array.isArray(message)) return message.map((m) => getMessageText(m)).join(" ")
- return sync.data.part[message.id]
- ?.filter((p) => p.type === "text")
- ?.filter((p) => !p.synthetic)
- .map((p) => p.text)
- .join(" ")
- }
+ const tabs = createMemo(() => store.tabs[store.active ?? ""])
return {
active,
- activeMessage,
- lastUserMessage,
- cost,
- last,
- model,
- tokens,
- context,
- messages,
- messagesWithValidParts,
- userMessages,
- // working,
- getMessageText,
setActive(sessionId: string | undefined) {
setStore("active", sessionId)
- setStore("activeMessage", undefined)
},
clearActive() {
setStore("active", undefined)
- setStore("activeMessage", undefined)
},
- setActiveMessage(messageId: string | undefined) {
- setStore("activeMessage", messageId)
+ tabs,
+ copyTabs(from: string, to: string) {
+ setStore("tabs", to, {
+ opened: store.tabs[from]?.opened ?? [],
+ })
+ },
+ setActiveTab(tab: string | undefined) {
+ setStore("tabs", store.active ?? "", "active", tab)
+ },
+ async open(tab: string) {
+ if (tab !== "chat") {
+ await file.open(tab)
+ }
+ if (!tabs()?.opened?.includes(tab)) {
+ setStore("tabs", store.active ?? "", "opened", [...(tabs()?.opened ?? []), tab])
+ }
+ setStore("tabs", store.active ?? "", "active", tab)
},
- clearActiveMessage() {
- setStore("activeMessage", undefined)
+ close(tab: string) {
+ batch(() => {
+ if (!tabs()) return
+ setStore("tabs", store.active ?? "", {
+ active: tabs()!.active,
+ opened: tabs()!.opened.filter((x) => x !== tab),
+ })
+ if (tabs()!.active === tab) {
+ const index = tabs()!.opened.findIndex((f) => f === tab)
+ const previous = tabs()!.opened[Math.max(0, index - 1)]
+ setStore("tabs", store.active ?? "", "active", previous)
+ }
+ })
+ },
+ move(tab: string, to: number) {
+ if (!tabs()) return
+ const index = tabs()!.opened.findIndex((f) => f === tab)
+ if (index === -1) return
+ setStore(
+ "tabs",
+ store.active ?? "",
+ "opened",
+ produce((opened) => {
+ opened.splice(to, 0, opened.splice(index, 1)[0])
+ }),
+ )
+ // setStore("node", path, "pinned", true)
},
}
})()
@@ -611,9 +549,9 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
all() {
return store.items
},
- active() {
- return store.activeTab ? file.active() : undefined
- },
+ // active() {
+ // return store.activeTab ? file.active() : undefined
+ // },
addActive() {
setStore("activeTab", true)
},