perf(app): performance improvements
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { createStore } from "solid-js/store"
|
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 { useParams } from "@solidjs/router"
|
||||||
import { createSimpleContext } from "@opencode-ai/ui/context"
|
import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||||
import { useGlobalSDK } from "./global-sdk"
|
import { useGlobalSDK } from "./global-sdk"
|
||||||
@@ -52,6 +52,15 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
|
|||||||
const settings = useSettings()
|
const settings = useSettings()
|
||||||
const language = useLanguage()
|
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(
|
const [store, setStore, _, ready] = persisted(
|
||||||
Persist.global("notification", ["notification.v1"]),
|
Persist.global("notification", ["notification.v1"]),
|
||||||
createStore({
|
createStore({
|
||||||
@@ -72,13 +81,59 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
|
|||||||
setStore("list", (list) => pruneNotifications([...list, notification]))
|
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 unsub = globalSDK.event.listen((e) => {
|
||||||
const directory = e.name
|
|
||||||
const event = e.details
|
const event = e.details
|
||||||
|
if (event.type !== "session.idle" && event.type !== "session.error") return
|
||||||
|
|
||||||
|
const directory = e.name
|
||||||
const time = Date.now()
|
const time = Date.now()
|
||||||
const activeDirectory = params.dir ? base64Decode(params.dir) : undefined
|
|
||||||
const activeSession = params.id
|
|
||||||
const viewed = (sessionID?: string) => {
|
const viewed = (sessionID?: string) => {
|
||||||
|
const activeDirectory = currentDirectory()
|
||||||
|
const activeSession = currentSession()
|
||||||
if (!activeDirectory) return false
|
if (!activeDirectory) return false
|
||||||
if (!activeSession) return false
|
if (!activeSession) return false
|
||||||
if (!sessionID) return false
|
if (!sessionID) return false
|
||||||
@@ -148,10 +203,10 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
|
|||||||
ready,
|
ready,
|
||||||
session: {
|
session: {
|
||||||
all(session: string) {
|
all(session: string) {
|
||||||
return store.list.filter((n) => n.session === session)
|
return index().session.all.get(session) ?? empty
|
||||||
},
|
},
|
||||||
unseen(session: string) {
|
unseen(session: string) {
|
||||||
return store.list.filter((n) => n.session === session && !n.viewed)
|
return index().session.unseen.get(session) ?? empty
|
||||||
},
|
},
|
||||||
markViewed(session: string) {
|
markViewed(session: string) {
|
||||||
setStore("list", (n) => n.session === session, "viewed", true)
|
setStore("list", (n) => n.session === session, "viewed", true)
|
||||||
@@ -159,10 +214,10 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
|
|||||||
},
|
},
|
||||||
project: {
|
project: {
|
||||||
all(directory: string) {
|
all(directory: string) {
|
||||||
return store.list.filter((n) => n.directory === directory)
|
return index().project.all.get(directory) ?? empty
|
||||||
},
|
},
|
||||||
unseen(directory: string) {
|
unseen(directory: string) {
|
||||||
return store.list.filter((n) => n.directory === directory && !n.viewed)
|
return index().project.unseen.get(directory) ?? empty
|
||||||
},
|
},
|
||||||
markViewed(directory: string) {
|
markViewed(directory: string) {
|
||||||
setStore("list", (n) => n.directory === directory, "viewed", true)
|
setStore("list", (n) => n.directory === directory, "viewed", true)
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ export default function Home() {
|
|||||||
const server = useServer()
|
const server = useServer()
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
const homedir = createMemo(() => sync.data.path.home)
|
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) {
|
function openProject(directory: string) {
|
||||||
layout.projects.open(directory)
|
layout.projects.open(directory)
|
||||||
@@ -84,11 +89,7 @@ export default function Home() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<ul class="flex flex-col gap-2">
|
<ul class="flex flex-col gap-2">
|
||||||
<For
|
<For each={recent()}>
|
||||||
each={sync.data.project
|
|
||||||
.toSorted((a, b) => (b.time.updated ?? b.time.created) - (a.time.updated ?? a.time.created))
|
|
||||||
.slice(0, 5)}
|
|
||||||
>
|
|
||||||
{(project) => (
|
{(project) => (
|
||||||
<Button
|
<Button
|
||||||
size="large"
|
size="large"
|
||||||
|
|||||||
@@ -350,7 +350,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
const props = e.details.properties
|
const props = e.details.properties
|
||||||
if (e.details.type === "permission.asked" && permission.autoResponds(e.details.properties, directory)) return
|
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 session = store.session.find((s) => s.id === props.sessionID)
|
||||||
const sessionKey = `${directory}:${props.sessionID}`
|
const sessionKey = `${directory}:${props.sessionID}`
|
||||||
|
|
||||||
@@ -419,7 +419,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
toastBySession.delete(sessionKey)
|
toastBySession.delete(sessionKey)
|
||||||
alertedAtBySession.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)
|
const childSessions = store.session.filter((s) => s.parentID === currentSession)
|
||||||
for (const child of childSessions) {
|
for (const child of childSessions) {
|
||||||
const childKey = `${currentDir}:${child.id}`
|
const childKey = `${currentDir}:${child.id}`
|
||||||
@@ -433,17 +433,18 @@ export default function Layout(props: ParentProps) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
function sortSessions(a: Session, b: Session) {
|
function sortSessions(now: number) {
|
||||||
const now = Date.now()
|
|
||||||
const oneMinuteAgo = now - 60 * 1000
|
const oneMinuteAgo = now - 60 * 1000
|
||||||
const aUpdated = a.time.updated ?? a.time.created
|
return (a: Session, b: Session) => {
|
||||||
const bUpdated = b.time.updated ?? b.time.created
|
const aUpdated = a.time.updated ?? a.time.created
|
||||||
const aRecent = aUpdated > oneMinuteAgo
|
const bUpdated = b.time.updated ?? b.time.created
|
||||||
const bRecent = bUpdated > oneMinuteAgo
|
const aRecent = aUpdated > oneMinuteAgo
|
||||||
if (aRecent && bRecent) return a.id.localeCompare(b.id)
|
const bRecent = bUpdated > oneMinuteAgo
|
||||||
if (aRecent && !bRecent) return -1
|
if (aRecent && bRecent) return a.id.localeCompare(b.id)
|
||||||
if (!aRecent && bRecent) return 1
|
if (aRecent && !bRecent) return -1
|
||||||
return bUpdated - aUpdated
|
if (!aRecent && bRecent) return 1
|
||||||
|
return bUpdated - aUpdated
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const [scrollSessionKey, setScrollSessionKey] = createSignal<string | undefined>(undefined)
|
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)
|
const direct = projects.find((p) => p.worktree === directory)
|
||||||
if (direct) return direct
|
if (direct) return direct
|
||||||
|
|
||||||
const [child] = globalSync.child(directory)
|
const [child] = globalSync.child(directory, { bootstrap: false })
|
||||||
const id = child.project
|
const id = child.project
|
||||||
if (!id) return
|
if (!id) return
|
||||||
|
|
||||||
@@ -596,6 +597,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
const currentSessions = createMemo(() => {
|
const currentSessions = createMemo(() => {
|
||||||
const project = currentProject()
|
const project = currentProject()
|
||||||
if (!project) return [] as Session[]
|
if (!project) return [] as Session[]
|
||||||
|
const compare = sortSessions(Date.now())
|
||||||
if (workspaceSetting()) {
|
if (workspaceSetting()) {
|
||||||
const dirs = workspaceIds(project)
|
const dirs = workspaceIds(project)
|
||||||
const activeDir = params.dir ? base64Decode(params.dir) : ""
|
const activeDir = params.dir ? base64Decode(params.dir) : ""
|
||||||
@@ -608,7 +610,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
const dirSessions = dirStore.session
|
const dirSessions = dirStore.session
|
||||||
.filter((session) => session.directory === dirStore.path.directory)
|
.filter((session) => session.directory === dirStore.path.directory)
|
||||||
.filter((session) => !session.parentID && !session.time?.archived)
|
.filter((session) => !session.parentID && !session.time?.archived)
|
||||||
.toSorted(sortSessions)
|
.toSorted(compare)
|
||||||
result.push(...dirSessions)
|
result.push(...dirSessions)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
@@ -617,7 +619,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
return projectStore.session
|
return projectStore.session
|
||||||
.filter((session) => session.directory === projectStore.path.directory)
|
.filter((session) => session.directory === projectStore.path.directory)
|
||||||
.filter((session) => !session.parentID && !session.time?.archived)
|
.filter((session) => !session.parentID && !session.time?.archived)
|
||||||
.toSorted(sortSessions)
|
.toSorted(compare)
|
||||||
})
|
})
|
||||||
|
|
||||||
type PrefetchQueue = {
|
type PrefetchQueue = {
|
||||||
@@ -659,7 +661,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function prefetchMessages(directory: string, sessionID: string, token: number) {
|
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 }))
|
return retry(() => globalSDK.client.session.messages({ directory, sessionID, limit: prefetchChunk }))
|
||||||
.then((messages) => {
|
.then((messages) => {
|
||||||
@@ -717,7 +719,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
const directory = session.directory
|
const directory = session.directory
|
||||||
if (!directory) return
|
if (!directory) return
|
||||||
|
|
||||||
const [store] = globalSync.child(directory)
|
const [store] = globalSync.child(directory, { bootstrap: false })
|
||||||
const cached = untrack(() => store.message[session.id] !== undefined)
|
const cached = untrack(() => store.message[session.id] !== undefined)
|
||||||
if (cached) return
|
if (cached) return
|
||||||
|
|
||||||
@@ -1817,7 +1819,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
const directory = store.activeWorkspace
|
const directory = store.activeWorkspace
|
||||||
if (!directory) return
|
if (!directory) return
|
||||||
|
|
||||||
const [workspaceStore] = globalSync.child(directory)
|
const [workspaceStore] = globalSync.child(directory, { bootstrap: false })
|
||||||
const kind =
|
const kind =
|
||||||
directory === project.worktree ? language.t("workspace.type.local") : language.t("workspace.type.sandbox")
|
directory === project.worktree ? language.t("workspace.type.local") : language.t("workspace.type.sandbox")
|
||||||
const name = workspaceLabel(directory, workspaceStore.vcs?.branch, project.id)
|
const name = workspaceLabel(directory, workspaceStore.vcs?.branch, project.id)
|
||||||
@@ -1843,7 +1845,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
workspaceStore.session
|
workspaceStore.session
|
||||||
.filter((session) => session.directory === workspaceStore.path.directory)
|
.filter((session) => session.directory === workspaceStore.path.directory)
|
||||||
.filter((session) => !session.parentID && !session.time?.archived)
|
.filter((session) => !session.parentID && !session.time?.archived)
|
||||||
.toSorted(sortSessions),
|
.toSorted(sortSessions(Date.now())),
|
||||||
)
|
)
|
||||||
const local = createMemo(() => props.directory === props.project.worktree)
|
const local = createMemo(() => props.directory === props.project.worktree)
|
||||||
const active = createMemo(() => {
|
const active = createMemo(() => {
|
||||||
@@ -2048,7 +2050,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
const [open, setOpen] = createSignal(false)
|
const [open, setOpen] = createSignal(false)
|
||||||
|
|
||||||
const label = (directory: string) => {
|
const label = (directory: string) => {
|
||||||
const [data] = globalSync.child(directory)
|
const [data] = globalSync.child(directory, { bootstrap: false })
|
||||||
const kind =
|
const kind =
|
||||||
directory === props.project.worktree ? language.t("workspace.type.local") : language.t("workspace.type.sandbox")
|
directory === props.project.worktree ? language.t("workspace.type.local") : language.t("workspace.type.sandbox")
|
||||||
const name = workspaceLabel(directory, data.vcs?.branch, props.project.id)
|
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 sessions = (directory: string) => {
|
||||||
const [data] = globalSync.child(directory)
|
const [data] = globalSync.child(directory, { bootstrap: false })
|
||||||
|
const root = workspaceKey(directory)
|
||||||
return data.session
|
return data.session
|
||||||
.filter((session) => session.directory === data.path.directory)
|
.filter((session) => workspaceKey(session.directory) === root)
|
||||||
.filter((session) => !session.parentID && !session.time?.archived)
|
.filter((session) => !session.parentID && !session.time?.archived)
|
||||||
.toSorted(sortSessions)
|
.toSorted(sortSessions(Date.now()))
|
||||||
.slice(0, 2)
|
.slice(0, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
const projectSessions = () => {
|
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
|
return data.session
|
||||||
.filter((session) => session.directory === data.path.directory)
|
.filter((session) => workspaceKey(session.directory) === root)
|
||||||
.filter((session) => !session.parentID && !session.time?.archived)
|
.filter((session) => !session.parentID && !session.time?.archived)
|
||||||
.toSorted(sortSessions)
|
.toSorted(sortSessions(Date.now()))
|
||||||
.slice(0, 2)
|
.slice(0, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2196,7 +2201,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
workspaceStore.session
|
workspaceStore.session
|
||||||
.filter((session) => session.directory === workspaceStore.path.directory)
|
.filter((session) => session.directory === workspaceStore.path.directory)
|
||||||
.filter((session) => !session.parentID && !session.time?.archived)
|
.filter((session) => !session.parentID && !session.time?.archived)
|
||||||
.toSorted(sortSessions),
|
.toSorted(sortSessions(Date.now())),
|
||||||
)
|
)
|
||||||
const loading = createMemo(() => workspaceStore.status !== "complete" && sessions().length === 0)
|
const loading = createMemo(() => workspaceStore.status !== "complete" && sessions().length === 0)
|
||||||
const hasMore = createMemo(() => workspaceStore.sessionTotal > workspaceStore.session.length)
|
const hasMore = createMemo(() => workspaceStore.sessionTotal > workspaceStore.session.length)
|
||||||
|
|||||||
Reference in New Issue
Block a user