summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authoradamelmore <[email protected]>2026-01-26 06:06:10 -0600
committeradamelmore <[email protected]>2026-01-26 06:08:09 -0600
commitc87232d5dfee75d117f450181457ebf328c6e5c6 (patch)
treee60105f57d4e9c67d56c1534fae398f0787d8557
parentd03c5f6b3f0ef9996776e082788cbc0889a75800 (diff)
downloadopencode-c87232d5dfee75d117f450181457ebf328c6e5c6.tar.gz
opencode-c87232d5dfee75d117f450181457ebf328c6e5c6.zip
perf(app): performance improvements
-rw-r--r--packages/app/src/context/notification.tsx71
-rw-r--r--packages/app/src/pages/home.tsx11
-rw-r--r--packages/app/src/pages/layout.tsx59
3 files changed, 101 insertions, 40 deletions
diff --git a/packages/app/src/context/notification.tsx b/packages/app/src/context/notification.tsx
index 1c5953b3f..5e35f6ac0 100644
--- a/packages/app/src/context/notification.tsx
+++ b/packages/app/src/context/notification.tsx
@@ -1,5 +1,5 @@
import { createStore } from "solid-js/store"
-import { createEffect, onCleanup } from "solid-js"
+import { createEffect, createMemo, onCleanup } from "solid-js"
import { useParams } from "@solidjs/router"
import { createSimpleContext } from "@opencode-ai/ui/context"
import { useGlobalSDK } from "./global-sdk"
@@ -52,6 +52,15 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
const settings = useSettings()
const language = useLanguage()
+ const empty: Notification[] = []
+
+ const currentDirectory = createMemo(() => {
+ if (!params.dir) return
+ return base64Decode(params.dir)
+ })
+
+ const currentSession = createMemo(() => params.id)
+
const [store, setStore, _, ready] = persisted(
Persist.global("notification", ["notification.v1"]),
createStore({
@@ -72,13 +81,59 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
setStore("list", (list) => pruneNotifications([...list, notification]))
}
+ const index = createMemo(() => {
+ const sessionAll = new Map<string, Notification[]>()
+ const sessionUnseen = new Map<string, Notification[]>()
+ const projectAll = new Map<string, Notification[]>()
+ const projectUnseen = new Map<string, Notification[]>()
+
+ for (const notification of store.list) {
+ const session = notification.session
+ if (session) {
+ const list = sessionAll.get(session)
+ if (list) list.push(notification)
+ else sessionAll.set(session, [notification])
+ if (!notification.viewed) {
+ const unseen = sessionUnseen.get(session)
+ if (unseen) unseen.push(notification)
+ else sessionUnseen.set(session, [notification])
+ }
+ }
+
+ const directory = notification.directory
+ if (directory) {
+ const list = projectAll.get(directory)
+ if (list) list.push(notification)
+ else projectAll.set(directory, [notification])
+ if (!notification.viewed) {
+ const unseen = projectUnseen.get(directory)
+ if (unseen) unseen.push(notification)
+ else projectUnseen.set(directory, [notification])
+ }
+ }
+ }
+
+ return {
+ session: {
+ all: sessionAll,
+ unseen: sessionUnseen,
+ },
+ project: {
+ all: projectAll,
+ unseen: projectUnseen,
+ },
+ }
+ })
+
const unsub = globalSDK.event.listen((e) => {
- const directory = e.name
const event = e.details
+ if (event.type !== "session.idle" && event.type !== "session.error") return
+
+ const directory = e.name
const time = Date.now()
- const activeDirectory = params.dir ? base64Decode(params.dir) : undefined
- const activeSession = params.id
const viewed = (sessionID?: string) => {
+ const activeDirectory = currentDirectory()
+ const activeSession = currentSession()
if (!activeDirectory) return false
if (!activeSession) return false
if (!sessionID) return false
@@ -148,10 +203,10 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
ready,
session: {
all(session: string) {
- return store.list.filter((n) => n.session === session)
+ return index().session.all.get(session) ?? empty
},
unseen(session: string) {
- return store.list.filter((n) => n.session === session && !n.viewed)
+ return index().session.unseen.get(session) ?? empty
},
markViewed(session: string) {
setStore("list", (n) => n.session === session, "viewed", true)
@@ -159,10 +214,10 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
},
project: {
all(directory: string) {
- return store.list.filter((n) => n.directory === directory)
+ return index().project.all.get(directory) ?? empty
},
unseen(directory: string) {
- return store.list.filter((n) => n.directory === directory && !n.viewed)
+ return index().project.unseen.get(directory) ?? empty
},
markViewed(directory: string) {
setStore("list", (n) => n.directory === directory, "viewed", true)
diff --git a/packages/app/src/pages/home.tsx b/packages/app/src/pages/home.tsx
index 0c04c0767..10f7dac53 100644
--- a/packages/app/src/pages/home.tsx
+++ b/packages/app/src/pages/home.tsx
@@ -23,6 +23,11 @@ export default function Home() {
const server = useServer()
const language = useLanguage()
const homedir = createMemo(() => sync.data.path.home)
+ const recent = createMemo(() => {
+ return sync.data.project
+ .toSorted((a, b) => (b.time.updated ?? b.time.created) - (a.time.updated ?? a.time.created))
+ .slice(0, 5)
+ })
function openProject(directory: string) {
layout.projects.open(directory)
@@ -84,11 +89,7 @@ export default function Home() {
</Button>
</div>
<ul class="flex flex-col gap-2">
- <For
- each={sync.data.project
- .toSorted((a, b) => (b.time.updated ?? b.time.created) - (a.time.updated ?? a.time.created))
- .slice(0, 5)}
- >
+ <For each={recent()}>
{(project) => (
<Button
size="large"
diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx
index 485deae99..2702f119b 100644
--- a/packages/app/src/pages/layout.tsx
+++ b/packages/app/src/pages/layout.tsx
@@ -350,7 +350,7 @@ export default function Layout(props: ParentProps) {
const props = e.details.properties
if (e.details.type === "permission.asked" && permission.autoResponds(e.details.properties, directory)) return
- const [store] = globalSync.child(directory)
+ const [store] = globalSync.child(directory, { bootstrap: false })
const session = store.session.find((s) => s.id === props.sessionID)
const sessionKey = `${directory}:${props.sessionID}`
@@ -419,7 +419,7 @@ export default function Layout(props: ParentProps) {
toastBySession.delete(sessionKey)
alertedAtBySession.delete(sessionKey)
}
- const [store] = globalSync.child(currentDir)
+ const [store] = globalSync.child(currentDir, { bootstrap: false })
const childSessions = store.session.filter((s) => s.parentID === currentSession)
for (const child of childSessions) {
const childKey = `${currentDir}:${child.id}`
@@ -433,17 +433,18 @@ export default function Layout(props: ParentProps) {
})
})
- function sortSessions(a: Session, b: Session) {
- const now = Date.now()
+ function sortSessions(now: number) {
const oneMinuteAgo = now - 60 * 1000
- const aUpdated = a.time.updated ?? a.time.created
- const bUpdated = b.time.updated ?? b.time.created
- const aRecent = aUpdated > oneMinuteAgo
- const bRecent = bUpdated > oneMinuteAgo
- if (aRecent && bRecent) return a.id.localeCompare(b.id)
- if (aRecent && !bRecent) return -1
- if (!aRecent && bRecent) return 1
- return bUpdated - aUpdated
+ return (a: Session, b: Session) => {
+ const aUpdated = a.time.updated ?? a.time.created
+ const bUpdated = b.time.updated ?? b.time.created
+ const aRecent = aUpdated > oneMinuteAgo
+ const bRecent = bUpdated > oneMinuteAgo
+ if (aRecent && bRecent) return a.id.localeCompare(b.id)
+ if (aRecent && !bRecent) return -1
+ if (!aRecent && bRecent) return 1
+ return bUpdated - aUpdated
+ }
}
const [scrollSessionKey, setScrollSessionKey] = createSignal<string | undefined>(undefined)
@@ -475,7 +476,7 @@ export default function Layout(props: ParentProps) {
const direct = projects.find((p) => p.worktree === directory)
if (direct) return direct
- const [child] = globalSync.child(directory)
+ const [child] = globalSync.child(directory, { bootstrap: false })
const id = child.project
if (!id) return
@@ -596,6 +597,7 @@ export default function Layout(props: ParentProps) {
const currentSessions = createMemo(() => {
const project = currentProject()
if (!project) return [] as Session[]
+ const compare = sortSessions(Date.now())
if (workspaceSetting()) {
const dirs = workspaceIds(project)
const activeDir = params.dir ? base64Decode(params.dir) : ""
@@ -608,7 +610,7 @@ export default function Layout(props: ParentProps) {
const dirSessions = dirStore.session
.filter((session) => session.directory === dirStore.path.directory)
.filter((session) => !session.parentID && !session.time?.archived)
- .toSorted(sortSessions)
+ .toSorted(compare)
result.push(...dirSessions)
}
return result
@@ -617,7 +619,7 @@ export default function Layout(props: ParentProps) {
return projectStore.session
.filter((session) => session.directory === projectStore.path.directory)
.filter((session) => !session.parentID && !session.time?.archived)
- .toSorted(sortSessions)
+ .toSorted(compare)
})
type PrefetchQueue = {
@@ -659,7 +661,7 @@ export default function Layout(props: ParentProps) {
}
async function prefetchMessages(directory: string, sessionID: string, token: number) {
- const [, setStore] = globalSync.child(directory)
+ const [, setStore] = globalSync.child(directory, { bootstrap: false })
return retry(() => globalSDK.client.session.messages({ directory, sessionID, limit: prefetchChunk }))
.then((messages) => {
@@ -717,7 +719,7 @@ export default function Layout(props: ParentProps) {
const directory = session.directory
if (!directory) return
- const [store] = globalSync.child(directory)
+ const [store] = globalSync.child(directory, { bootstrap: false })
const cached = untrack(() => store.message[session.id] !== undefined)
if (cached) return
@@ -1817,7 +1819,7 @@ export default function Layout(props: ParentProps) {
const directory = store.activeWorkspace
if (!directory) return
- const [workspaceStore] = globalSync.child(directory)
+ const [workspaceStore] = globalSync.child(directory, { bootstrap: false })
const kind =
directory === project.worktree ? language.t("workspace.type.local") : language.t("workspace.type.sandbox")
const name = workspaceLabel(directory, workspaceStore.vcs?.branch, project.id)
@@ -1843,7 +1845,7 @@ export default function Layout(props: ParentProps) {
workspaceStore.session
.filter((session) => session.directory === workspaceStore.path.directory)
.filter((session) => !session.parentID && !session.time?.archived)
- .toSorted(sortSessions),
+ .toSorted(sortSessions(Date.now())),
)
const local = createMemo(() => props.directory === props.project.worktree)
const active = createMemo(() => {
@@ -2048,7 +2050,7 @@ export default function Layout(props: ParentProps) {
const [open, setOpen] = createSignal(false)
const label = (directory: string) => {
- const [data] = globalSync.child(directory)
+ const [data] = globalSync.child(directory, { bootstrap: false })
const kind =
directory === props.project.worktree ? language.t("workspace.type.local") : language.t("workspace.type.sandbox")
const name = workspaceLabel(directory, data.vcs?.branch, props.project.id)
@@ -2056,20 +2058,23 @@ export default function Layout(props: ParentProps) {
}
const sessions = (directory: string) => {
- const [data] = globalSync.child(directory)
+ const [data] = globalSync.child(directory, { bootstrap: false })
+ const root = workspaceKey(directory)
return data.session
- .filter((session) => session.directory === data.path.directory)
+ .filter((session) => workspaceKey(session.directory) === root)
.filter((session) => !session.parentID && !session.time?.archived)
- .toSorted(sortSessions)
+ .toSorted(sortSessions(Date.now()))
.slice(0, 2)
}
const projectSessions = () => {
- const [data] = globalSync.child(props.project.worktree)
+ const directory = props.project.worktree
+ const [data] = globalSync.child(directory, { bootstrap: false })
+ const root = workspaceKey(directory)
return data.session
- .filter((session) => session.directory === data.path.directory)
+ .filter((session) => workspaceKey(session.directory) === root)
.filter((session) => !session.parentID && !session.time?.archived)
- .toSorted(sortSessions)
+ .toSorted(sortSessions(Date.now()))
.slice(0, 2)
}
@@ -2196,7 +2201,7 @@ export default function Layout(props: ParentProps) {
workspaceStore.session
.filter((session) => session.directory === workspaceStore.path.directory)
.filter((session) => !session.parentID && !session.time?.archived)
- .toSorted(sortSessions),
+ .toSorted(sortSessions(Date.now())),
)
const loading = createMemo(() => workspaceStore.status !== "complete" && sessions().length === 0)
const hasMore = createMemo(() => workspaceStore.sessionTotal > workspaceStore.session.length)