feat(app): scope sdk/sync hooks per-route so /new-session targets its draft server (#32290)
This commit is contained in:
parent
c81cd3202c
commit
010b456dfd
53 changed files with 524 additions and 458 deletions
|
|
@ -20,7 +20,7 @@ export function DirectoryDataProvider(props: ParentProps<{ directory: string; dr
|
|||
createEffect(() => {
|
||||
// A draft lives at /new-session?draftId=… and has no directory segment to normalize.
|
||||
if (props.draftID) return
|
||||
const next = sync.data.path.directory
|
||||
const next = sync().data.path.directory
|
||||
if (!next || next === props.directory) return
|
||||
const path = location.pathname.slice(slug().length + 1)
|
||||
navigate(`/${base64Encode(next)}${path}${location.search}${location.hash}`, { replace: true })
|
||||
|
|
@ -28,12 +28,12 @@ export function DirectoryDataProvider(props: ParentProps<{ directory: string; dr
|
|||
|
||||
createResource(
|
||||
() => params.id,
|
||||
(id) => sync.session.sync(id).catch(() => {}),
|
||||
(id) => sync().session.sync(id).catch(() => {}),
|
||||
)
|
||||
|
||||
return (
|
||||
<DataProvider
|
||||
data={sync.data}
|
||||
data={sync().data}
|
||||
directory={props.directory}
|
||||
onNavigateToSession={(sessionID: string) => navigate(`/${slug()}/session/${sessionID}`)}
|
||||
onSessionHref={(sessionID: string) => `/${slug()}/session/${sessionID}`}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import { DialogSelectServer, useServerManagementController } from "@/components/
|
|||
import { DialogServerV2 } from "@/components/settings-v2/dialog-server-v2"
|
||||
import { ServerConnection, useServer } from "@/context/server"
|
||||
import { sessionHasOpenTab, useTabs } from "@/context/tabs"
|
||||
import { useServerSync } from "@/context/server-sync"
|
||||
import { useServerSync, type ServerSync } from "@/context/server-sync"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { useNotification } from "@/context/notification"
|
||||
import {
|
||||
|
|
@ -80,7 +80,7 @@ const HOME_SEARCH_RESULT_META =
|
|||
let pendingHomeNavigation: { server: ServerConnection.Key; href: string } | undefined
|
||||
|
||||
function buildHomeSessionRecords(input: {
|
||||
sync: Pick<ReturnType<typeof useServerSync>, "child">
|
||||
sync: Pick<ServerSync, "child">
|
||||
projectDirectories: () => string[]
|
||||
projects: () => LocalProject[]
|
||||
projectByID: () => Map<string, LocalProject>
|
||||
|
|
@ -149,7 +149,7 @@ function HomeDesign() {
|
|||
if (!conn) return
|
||||
return global.createServerCtx(conn)
|
||||
})
|
||||
const focusedSync = () => focusedServerCtx()?.sync ?? sync
|
||||
const focusedSync = () => focusedServerCtx()?.sync ?? sync()
|
||||
const projects = createMemo(() => focusedServerCtx()?.projects.list() ?? layout.projects.list())
|
||||
const selectedProject = createMemo(() => projects().find((project) => project.worktree === state.selection.directory))
|
||||
const newSessionProject = createMemo(
|
||||
|
|
@ -1102,9 +1102,9 @@ function LegacyHome() {
|
|||
const global = useGlobal()
|
||||
const server = useServer()
|
||||
const language = useLanguage()
|
||||
const homedir = createMemo(() => sync.data.path.home)
|
||||
const homedir = createMemo(() => sync().data.path.home)
|
||||
const recent = createMemo(() => {
|
||||
return sync.data.project
|
||||
return sync().data.project
|
||||
.slice()
|
||||
.sort((a, b) => (b.time.updated ?? b.time.created) - (a.time.updated ?? a.time.created))
|
||||
.slice(0, 5)
|
||||
|
|
@ -1164,7 +1164,7 @@ function LegacyHome() {
|
|||
{server.name}
|
||||
</Button>
|
||||
<Switch>
|
||||
<Match when={sync.data.project.length > 0}>
|
||||
<Match when={sync().data.project.length > 0}>
|
||||
<div class="mt-20 w-full flex flex-col gap-4">
|
||||
<div class="flex gap-2 items-center justify-between pl-3">
|
||||
<div class="text-14-medium text-text-strong">{language.t("home.recentProjects")}</div>
|
||||
|
|
@ -1191,7 +1191,7 @@ function LegacyHome() {
|
|||
</ul>
|
||||
</div>
|
||||
</Match>
|
||||
<Match when={!sync.ready}>
|
||||
<Match when={!sync().ready}>
|
||||
<div class="mt-30 mx-auto flex flex-col items-center gap-3">
|
||||
<div class="text-12-regular text-text-weak">{language.t("common.loading")}</div>
|
||||
<Button class="px-3" onClick={chooseProject}>
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ import { SidebarContent } from "./layout/sidebar-shell"
|
|||
export default function Layout(props: ParentProps) {
|
||||
const serverSDK = useServerSDK()
|
||||
const [store, setStore, , ready] = persisted(
|
||||
Persist.serverGlobal(serverSDK.scope, "layout.page", ["layout.page.v1"]),
|
||||
Persist.serverGlobal(serverSDK().scope, "layout.page", ["layout.page.v1"]),
|
||||
createStore({
|
||||
lastProjectSession: {} as { [directory: string]: { directory: string; id: string; at: number } },
|
||||
activeProject: undefined as string | undefined,
|
||||
|
|
@ -140,7 +140,7 @@ export default function Layout(props: ParentProps) {
|
|||
if (!slug) return { slug, dir: "" }
|
||||
const dir = decode64(slug)
|
||||
if (!dir) return { slug, dir: "" }
|
||||
const store = serverSync.peek(dir, { bootstrap: false })
|
||||
const store = serverSync().peek(dir, { bootstrap: false })
|
||||
return {
|
||||
slug,
|
||||
store,
|
||||
|
|
@ -213,7 +213,7 @@ export default function Layout(props: ParentProps) {
|
|||
active: () => state.hoverProject,
|
||||
el: () => state.nav?.querySelector<HTMLElement>("[data-component='sidebar-rail']") ?? state.nav,
|
||||
onActivate: (directory) => {
|
||||
serverSync.child(directory)
|
||||
serverSync().child(directory)
|
||||
setState("hoverProject", directory)
|
||||
},
|
||||
})
|
||||
|
|
@ -397,17 +397,17 @@ export default function Layout(props: ParentProps) {
|
|||
alertedAtBySession.delete(sessionKey)
|
||||
}
|
||||
|
||||
const unsub = serverSDK.event.listen((e) => {
|
||||
const unsub = serverSDK().event.listen((e) => {
|
||||
if (e.details?.type === "worktree.ready") {
|
||||
setBusy(e.name, false)
|
||||
WorktreeState.ready(serverSDK.scope, e.name)
|
||||
WorktreeState.ready(serverSDK().scope, e.name)
|
||||
return
|
||||
}
|
||||
|
||||
if (e.details?.type === "worktree.failed") {
|
||||
setBusy(e.name, false)
|
||||
WorktreeState.failed(
|
||||
serverSDK.scope,
|
||||
serverSDK().scope,
|
||||
e.name,
|
||||
e.details.properties?.message ?? language.t("common.requestFailed"),
|
||||
)
|
||||
|
|
@ -435,7 +435,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] = serverSync.child(directory, { bootstrap: false })
|
||||
const [store] = serverSync().child(directory, { bootstrap: false })
|
||||
const session = store.session.find((s) => s.id === props.sessionID)
|
||||
const sessionKey = `${directory}:${props.sessionID}`
|
||||
|
||||
|
|
@ -498,7 +498,7 @@ export default function Layout(props: ParentProps) {
|
|||
if (!currentDir() || !currentSession) return
|
||||
const sessionKey = `${currentDir()}:${currentSession}`
|
||||
dismissSessionAlert(sessionKey)
|
||||
const [store] = serverSync.child(currentDir(), { bootstrap: false })
|
||||
const [store] = serverSync().child(currentDir(), { bootstrap: false })
|
||||
const childSessions = store.session.filter((s) => s.parentID === currentSession)
|
||||
for (const child of childSessions) {
|
||||
dismissSessionAlert(`${currentDir()}:${child.id}`)
|
||||
|
|
@ -536,11 +536,11 @@ export default function Layout(props: ParentProps) {
|
|||
const direct = projects.find((p) => pathKey(p.worktree) === key)
|
||||
if (direct) return direct
|
||||
|
||||
const [child] = serverSync.child(directory, { bootstrap: false })
|
||||
const [child] = serverSync().child(directory, { bootstrap: false })
|
||||
const id = child.project
|
||||
if (!id) return
|
||||
|
||||
const meta = serverSync.data.project.find((p) => p.id === id)
|
||||
const meta = serverSync().data.project.find((p) => p.id === id)
|
||||
const root = meta?.worktree
|
||||
if (!root) return
|
||||
|
||||
|
|
@ -631,7 +631,7 @@ export default function Layout(props: ParentProps) {
|
|||
|
||||
const result: Session[] = []
|
||||
for (const dir of dirs) {
|
||||
const [dirStore] = serverSync.child(dir, { bootstrap: true })
|
||||
const [dirStore] = serverSync().child(dir, { bootstrap: true })
|
||||
const dirSessions = sortedRootSessions(dirStore, now)
|
||||
result.push(...dirSessions)
|
||||
}
|
||||
|
|
@ -683,10 +683,10 @@ export default function Layout(props: ParentProps) {
|
|||
|
||||
createEffect(() => {
|
||||
route()
|
||||
serverSDK.url
|
||||
serverSDK().url
|
||||
|
||||
prefetchToken.value += 1
|
||||
clearSessionPrefetchInflight(serverSDK.scope)
|
||||
clearSessionPrefetchInflight(serverSDK().scope)
|
||||
prefetchQueues.clear()
|
||||
})
|
||||
|
||||
|
|
@ -730,17 +730,17 @@ export default function Layout(props: ParentProps) {
|
|||
}
|
||||
|
||||
async function prefetchMessages(directory: string, sessionID: string, token: number) {
|
||||
const [store, setStore] = serverSync.child(directory, { bootstrap: false })
|
||||
const [store, setStore] = serverSync().child(directory, { bootstrap: false })
|
||||
|
||||
return runSessionPrefetch({
|
||||
scope: serverSDK.scope,
|
||||
scope: serverSDK().scope,
|
||||
directory,
|
||||
sessionID,
|
||||
task: (rev) =>
|
||||
retry(() => serverSDK.client.session.messages({ directory, sessionID, limit: prefetchChunk }))
|
||||
retry(() => serverSDK().client.session.messages({ directory, sessionID, limit: prefetchChunk }))
|
||||
.then((messages) => {
|
||||
if (prefetchToken.value !== token) return
|
||||
if (!isSessionPrefetchCurrent(serverSDK.scope, directory, sessionID, rev)) return
|
||||
if (!isSessionPrefetchCurrent(serverSDK().scope, directory, sessionID, rev)) return
|
||||
|
||||
const items = (messages.data ?? []).filter((x) => !!x?.info?.id)
|
||||
const next = items.map((x) => x.info).filter((m): m is Message => !!m?.id)
|
||||
|
|
@ -755,9 +755,9 @@ export default function Layout(props: ParentProps) {
|
|||
}
|
||||
|
||||
if (stale.length > 0) {
|
||||
clearSessionPrefetch(serverSDK.scope, directory, stale)
|
||||
clearSessionPrefetch(serverSDK().scope, directory, stale)
|
||||
for (const id of stale) {
|
||||
serverSync.todo.set(id, undefined)
|
||||
serverSync().todo.set(id, undefined)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -767,7 +767,7 @@ export default function Layout(props: ParentProps) {
|
|||
sorted,
|
||||
)
|
||||
|
||||
if (!isSessionPrefetchCurrent(serverSDK.scope, directory, sessionID, rev)) return
|
||||
if (!isSessionPrefetchCurrent(serverSDK().scope, directory, sessionID, rev)) return
|
||||
|
||||
batch(() => {
|
||||
if (stale.length > 0) {
|
||||
|
|
@ -779,7 +779,7 @@ export default function Layout(props: ParentProps) {
|
|||
}
|
||||
|
||||
setStore("message", sessionID, reconcile(merged, { key: "id" }))
|
||||
setSessionPrefetch({ scope: serverSDK.scope, directory, sessionID, ...meta })
|
||||
setSessionPrefetch({ scope: serverSDK().scope, directory, sessionID, ...meta })
|
||||
|
||||
for (const message of items) {
|
||||
const currentParts = store.part[message.info.id] ?? []
|
||||
|
|
@ -822,9 +822,9 @@ export default function Layout(props: ParentProps) {
|
|||
const directory = session.directory
|
||||
if (!directory) return
|
||||
|
||||
const [store] = serverSync.child(directory, { bootstrap: false })
|
||||
const [store] = serverSync().child(directory, { bootstrap: false })
|
||||
const cached = untrack(() => {
|
||||
const info = getSessionPrefetch(serverSDK.scope, directory, session.id)
|
||||
const info = getSessionPrefetch(serverSDK().scope, directory, session.id)
|
||||
return shouldSkipSessionPrefetch({
|
||||
message: store.message[session.id] !== undefined,
|
||||
info,
|
||||
|
|
@ -927,7 +927,7 @@ export default function Layout(props: ParentProps) {
|
|||
if (!target) return
|
||||
|
||||
// warm up child store to prevent flicker
|
||||
serverSync.child(target.worktree)
|
||||
serverSync().child(target.worktree)
|
||||
void openProject(target.worktree)
|
||||
}
|
||||
|
||||
|
|
@ -936,7 +936,7 @@ export default function Layout(props: ParentProps) {
|
|||
const target = projects[index]
|
||||
if (!target) return
|
||||
|
||||
serverSync.child(target.worktree)
|
||||
serverSync().child(target.worktree)
|
||||
void openProject(target.worktree)
|
||||
}
|
||||
|
||||
|
|
@ -965,12 +965,12 @@ export default function Layout(props: ParentProps) {
|
|||
}
|
||||
|
||||
async function archiveSession(session: Session) {
|
||||
const [store, setStore] = serverSync.child(session.directory)
|
||||
const [store, setStore] = serverSync().child(session.directory)
|
||||
const sessions = store.session ?? []
|
||||
const index = sessions.findIndex((s) => s.id === session.id)
|
||||
const nextSession = sessions[index + 1] ?? sessions[index - 1]
|
||||
|
||||
await serverSDK.client.session.update({
|
||||
await serverSDK().client.session.update({
|
||||
directory: session.directory,
|
||||
sessionID: session.id,
|
||||
time: { archived: Date.now() },
|
||||
|
|
@ -1241,11 +1241,11 @@ export default function Layout(props: ParentProps) {
|
|||
)
|
||||
if (known) return known[0]
|
||||
|
||||
const [child] = serverSync.child(directory, { bootstrap: false })
|
||||
const [child] = serverSync().child(directory, { bootstrap: false })
|
||||
const id = child.project
|
||||
if (!id) return directory
|
||||
|
||||
const meta = serverSync.data.project.find((item) => item.id === id)
|
||||
const meta = serverSync().data.project.find((item) => item.id === id)
|
||||
return meta?.worktree ?? directory
|
||||
}
|
||||
|
||||
|
|
@ -1293,7 +1293,7 @@ export default function Layout(props: ParentProps) {
|
|||
}
|
||||
const refreshDirs = async (target?: string) => {
|
||||
if (!target || target === root || canOpen(target)) return canOpen(target)
|
||||
const listed = await serverSDK.client.worktree
|
||||
const listed = await serverSDK().client.worktree
|
||||
.list({ directory: root })
|
||||
.then((x) => x.data ?? [])
|
||||
.catch(() => [] as string[])
|
||||
|
|
@ -1302,13 +1302,13 @@ export default function Layout(props: ParentProps) {
|
|||
}
|
||||
const openSession = async (target: { directory: string; id: string }) => {
|
||||
if (!canOpen(target.directory)) return false
|
||||
const [data] = serverSync.child(target.directory, { bootstrap: false })
|
||||
const [data] = serverSync().child(target.directory, { bootstrap: false })
|
||||
if (data.session.some((item) => item.id === target.id)) {
|
||||
setStore("lastProjectSession", root, { directory: target.directory, id: target.id, at: Date.now() })
|
||||
navigateWithSidebarReset(`/${base64Encode(target.directory)}/session/${target.id}`)
|
||||
return true
|
||||
}
|
||||
const resolved = await serverSDK.client.session
|
||||
const resolved = await serverSDK().client.session
|
||||
.get({ sessionID: target.id })
|
||||
.then((x) => x.data)
|
||||
.catch(() => undefined)
|
||||
|
|
@ -1328,7 +1328,7 @@ export default function Layout(props: ParentProps) {
|
|||
}
|
||||
|
||||
const latest = latestRootSession(
|
||||
dirs.map((item) => serverSync.child(item, { bootstrap: false })[0]),
|
||||
dirs.map((item) => serverSync().child(item, { bootstrap: false })[0]),
|
||||
Date.now(),
|
||||
)
|
||||
if (latest && (await openSession(latest))) {
|
||||
|
|
@ -1339,7 +1339,7 @@ export default function Layout(props: ParentProps) {
|
|||
await Promise.all(
|
||||
dirs.map(async (item) => ({
|
||||
path: { directory: item },
|
||||
session: await serverSDK.client.session
|
||||
session: await serverSDK().client.session
|
||||
.list({ directory: item })
|
||||
.then((x) => x.data ?? [])
|
||||
.catch(() => []),
|
||||
|
|
@ -1402,11 +1402,11 @@ export default function Layout(props: ParentProps) {
|
|||
const name = next === getFilename(project.worktree) ? "" : next
|
||||
|
||||
if (project.id && project.id !== "global") {
|
||||
await serverSDK.client.project.update({ projectID: project.id, directory: project.worktree, name })
|
||||
await serverSDK().client.project.update({ projectID: project.id, directory: project.worktree, name })
|
||||
return
|
||||
}
|
||||
|
||||
serverSync.project.meta(project.worktree, { name })
|
||||
serverSync().project.meta(project.worktree, { name })
|
||||
}
|
||||
|
||||
const renameWorkspace = (directory: string, next: string, projectId?: string, branch?: string) => {
|
||||
|
|
@ -1495,7 +1495,7 @@ export default function Layout(props: ParentProps) {
|
|||
|
||||
setBusy(directory, true)
|
||||
|
||||
const result = await serverSDK.client.worktree
|
||||
const result = await serverSDK().client.worktree
|
||||
.remove({ directory: root, worktreeRemoveInput: { directory } })
|
||||
.then((x) => x.data)
|
||||
.catch((err) => {
|
||||
|
|
@ -1514,7 +1514,7 @@ export default function Layout(props: ParentProps) {
|
|||
clearLastProjectSession(root)
|
||||
}
|
||||
|
||||
serverSync.set(
|
||||
serverSync().set(
|
||||
"project",
|
||||
produce((draft) => {
|
||||
const project = draft.find((item) => item.worktree === root)
|
||||
|
|
@ -1553,7 +1553,7 @@ export default function Layout(props: ParentProps) {
|
|||
})
|
||||
const dismiss = () => toaster.dismiss(progress)
|
||||
|
||||
const sessions: Session[] = await serverSDK.client.session
|
||||
const sessions: Session[] = await serverSDK().client.session
|
||||
.list({ directory })
|
||||
.then((x) => x.data ?? [])
|
||||
.catch(() => [])
|
||||
|
|
@ -1562,11 +1562,11 @@ export default function Layout(props: ParentProps) {
|
|||
directory,
|
||||
sessions.map((s) => s.id),
|
||||
platform,
|
||||
serverSDK.scope,
|
||||
serverSDK().scope,
|
||||
)
|
||||
await serverSDK.client.instance.dispose({ directory }).catch(() => undefined)
|
||||
await serverSDK().client.instance.dispose({ directory }).catch(() => undefined)
|
||||
|
||||
const result = await serverSDK.client.worktree
|
||||
const result = await serverSDK().client.worktree
|
||||
.reset({ directory: root, worktreeResetInput: { directory } })
|
||||
.then((x) => x.data)
|
||||
.catch((err) => {
|
||||
|
|
@ -1588,7 +1588,7 @@ export default function Layout(props: ParentProps) {
|
|||
sessions
|
||||
.filter((session) => session.time.archived === undefined)
|
||||
.map((session) =>
|
||||
serverSDK.client.session
|
||||
serverSDK().client.session
|
||||
.update({
|
||||
sessionID: session.id,
|
||||
directory: session.directory,
|
||||
|
|
@ -1629,7 +1629,7 @@ export default function Layout(props: ParentProps) {
|
|||
})
|
||||
|
||||
onMount(() => {
|
||||
serverSDK.client.vcs
|
||||
serverSDK().client.vcs
|
||||
.status({ directory: props.directory })
|
||||
.then((x) => {
|
||||
const files = x.data ?? []
|
||||
|
|
@ -1688,7 +1688,7 @@ export default function Layout(props: ParentProps) {
|
|||
})
|
||||
|
||||
const refresh = async () => {
|
||||
const sessions = await serverSDK.client.session
|
||||
const sessions = await serverSDK().client.session
|
||||
.list({ directory: props.directory })
|
||||
.then((x) => x.data ?? [])
|
||||
.catch(() => [])
|
||||
|
|
@ -1697,7 +1697,7 @@ export default function Layout(props: ParentProps) {
|
|||
}
|
||||
|
||||
onMount(() => {
|
||||
serverSDK.client.vcs
|
||||
serverSDK().client.vcs
|
||||
.status({ directory: props.directory })
|
||||
.then((x) => {
|
||||
const files = x.data ?? []
|
||||
|
|
@ -1831,7 +1831,7 @@ export default function Layout(props: ParentProps) {
|
|||
const next = new Set(dirs)
|
||||
for (const directory of next) {
|
||||
if (loadedSessionDirs.has(directory)) continue
|
||||
void serverSync.project.loadSessions(directory)
|
||||
void serverSync().project.loadSessions(directory)
|
||||
}
|
||||
|
||||
loadedSessionDirs.clear()
|
||||
|
|
@ -1876,7 +1876,7 @@ export default function Layout(props: ParentProps) {
|
|||
directory && pathKey(directory) !== pathKey(local) && !dirs.some((item) => pathKey(item) === pathKey(directory))
|
||||
? directory
|
||||
: undefined
|
||||
const pending = extra ? WorktreeState.get(serverSDK.scope, extra)?.status === "pending" : false
|
||||
const pending = extra ? WorktreeState.get(serverSDK().scope, extra)?.status === "pending" : false
|
||||
|
||||
const ordered = effectiveWorkspaceOrder(local, dirs, store.workspaceOrder[project.worktree])
|
||||
if (pending && extra) return [local, extra, ...ordered.filter((item) => item !== local)]
|
||||
|
|
@ -1928,7 +1928,7 @@ export default function Layout(props: ParentProps) {
|
|||
|
||||
const createWorkspace = async (project: LocalProject) => {
|
||||
clearSidebarHoverState()
|
||||
const created = await serverSDK.client.worktree
|
||||
const created = await serverSDK().client.worktree
|
||||
.create({ directory: project.worktree })
|
||||
.then((x) => x.data)
|
||||
.catch((err) => {
|
||||
|
|
@ -1948,7 +1948,7 @@ export default function Layout(props: ParentProps) {
|
|||
const root = pathKey(local)
|
||||
|
||||
setBusy(created.directory, true)
|
||||
WorktreeState.pending(serverSDK.scope, created.directory)
|
||||
WorktreeState.pending(serverSDK().scope, created.directory)
|
||||
setStore("workspaceExpanded", key, true)
|
||||
if (key !== created.directory) {
|
||||
setStore("workspaceExpanded", created.directory, true)
|
||||
|
|
@ -1962,7 +1962,7 @@ export default function Layout(props: ParentProps) {
|
|||
return [created.directory, ...next]
|
||||
})
|
||||
|
||||
serverSync.child(created.directory)
|
||||
serverSync().child(created.directory)
|
||||
navigateWithSidebarReset(`/${base64Encode(created.directory)}/session`)
|
||||
}
|
||||
|
||||
|
|
@ -2067,7 +2067,7 @@ export default function Layout(props: ParentProps) {
|
|||
if (!item) return false
|
||||
return item.vcs === "git" || layout.sidebar.workspaces(item.worktree)()
|
||||
})
|
||||
const homedir = createMemo(() => serverSync.data.path.home)
|
||||
const homedir = createMemo(() => serverSync().data.path.home)
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export function useSessionTabAvatarState(
|
|||
const permission = usePermission()
|
||||
const hasPermissions = createMemo(() => {
|
||||
if (!active()) return false
|
||||
const [store] = globalSync.child(directory(), { bootstrap: false })
|
||||
const [store] = globalSync().child(directory(), { bootstrap: false })
|
||||
return !!sessionPermissionRequest(store.session, store.permission, sessionId(), (item) => {
|
||||
return !permission.autoResponds(item, directory())
|
||||
})
|
||||
|
|
@ -23,7 +23,7 @@ export function useSessionTabAvatarState(
|
|||
const loading = createMemo(() => {
|
||||
if (!active()) return false
|
||||
if (hasPermissions()) return false
|
||||
const [store] = globalSync.child(directory(), { bootstrap: false })
|
||||
const [store] = globalSync().child(directory(), { bootstrap: false })
|
||||
return store.session_working(sessionId())
|
||||
})
|
||||
return { unread, loading }
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export const ProjectIcon = (props: {
|
|||
const hasError = createMemo(() => dirs().some((directory) => notification.project.unseenHasError(directory)))
|
||||
const hasPermissions = createMemo(() =>
|
||||
dirs().some((directory) => {
|
||||
const [store] = serverSync.child(directory, { bootstrap: false })
|
||||
const [store] = serverSync().child(directory, { bootstrap: false })
|
||||
return hasProjectPermissions(store.permission, (item) => !permission.autoResponds(item, directory))
|
||||
}),
|
||||
)
|
||||
|
|
@ -149,7 +149,7 @@ export const SessionItem = (props: SessionItemProps): JSX.Element => {
|
|||
const serverSync = useServerSync()
|
||||
const unseenCount = createMemo(() => notification.session.unseenCount(props.session.id))
|
||||
const hasError = createMemo(() => notification.session.unseenHasError(props.session.id))
|
||||
const [sessionStore] = serverSync.child(props.session.directory)
|
||||
const [sessionStore] = serverSync().child(props.session.directory)
|
||||
const hasPermissions = createMemo(() => {
|
||||
return !!sessionPermissionRequest(sessionStore.session, sessionStore.permission, props.session.id, (item) => {
|
||||
return !permission.autoResponds(item, props.session.directory)
|
||||
|
|
|
|||
|
|
@ -294,23 +294,23 @@ export const SortableProject = (props: {
|
|||
const hoverOpen = () => isHoverProject() && preview() && !selected() && !state.menu
|
||||
|
||||
const label = (directory: string) => {
|
||||
const [data] = serverSync.child(directory, { bootstrap: false })
|
||||
const [data] = serverSync().child(directory, { bootstrap: false })
|
||||
const kind =
|
||||
directory === props.project.worktree ? language.t("workspace.type.local") : language.t("workspace.type.sandbox")
|
||||
const name = props.ctx.workspaceLabel(directory, data.vcs?.branch, props.project.id)
|
||||
return `${kind} : ${name}`
|
||||
}
|
||||
|
||||
const projectStore = createMemo(() => serverSync.child(props.project.worktree, { bootstrap: false })[0])
|
||||
const projectStore = createMemo(() => serverSync().child(props.project.worktree, { bootstrap: false })[0])
|
||||
const isWorking = createMemo(() =>
|
||||
dirs().some((directory) => {
|
||||
const [store] = serverSync.child(directory, { bootstrap: false })
|
||||
const [store] = serverSync().child(directory, { bootstrap: false })
|
||||
return Object.keys(store.session_status).some((id) => store.session_working(id))
|
||||
}),
|
||||
)
|
||||
const projectSessions = createMemo(() => sortedRootSessions(projectStore(), props.sortNow()))
|
||||
const workspaceSessions = (directory: string) => {
|
||||
const [data] = serverSync.child(directory, { bootstrap: false })
|
||||
const [data] = serverSync().child(directory, { bootstrap: false })
|
||||
return sortedRootSessions(data, props.sortNow())
|
||||
}
|
||||
const tile = () => (
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ export const WorkspaceDragOverlay = (props: {
|
|||
const directory = props.activeWorkspace()
|
||||
if (!directory) return
|
||||
|
||||
const [workspaceStore] = serverSync.child(directory, { bootstrap: false })
|
||||
const [workspaceStore] = serverSync().child(directory, { bootstrap: false })
|
||||
const kind =
|
||||
directory === project.worktree ? language.t("workspace.type.local") : language.t("workspace.type.sandbox")
|
||||
const name = props.workspaceLabel(directory, workspaceStore.vcs?.branch, project.id)
|
||||
|
|
@ -303,7 +303,7 @@ export const SortableWorkspace = (props: {
|
|||
const queryOptions = useQueryOptions()
|
||||
const language = useLanguage()
|
||||
const sortable = createSortable(props.directory)
|
||||
const [workspaceStore, setWorkspaceStore] = serverSync.child(props.directory, { bootstrap: false })
|
||||
const [workspaceStore, setWorkspaceStore] = serverSync().child(props.directory, { bootstrap: false })
|
||||
const [menu, setMenu] = createStore({
|
||||
open: false,
|
||||
pendingRename: false,
|
||||
|
|
@ -321,14 +321,14 @@ export const SortableWorkspace = (props: {
|
|||
const boot = createMemo(() => open() || active())
|
||||
const count = createMemo(() => sessions()?.length ?? 0)
|
||||
const hasMore = createMemo(() => workspaceStore.sessionTotal > count())
|
||||
const fetching = useIsFetching(() => queryOptions.sessions(pathKey(props.directory)))
|
||||
const fetching = useIsFetching(() => queryOptions().sessions(pathKey(props.directory)))
|
||||
const busy = createMemo(() => props.ctx.isBusy(props.directory))
|
||||
const loading = () => fetching() > 0 && count() === 0
|
||||
const touch = createMediaQuery("(hover: none)")
|
||||
const showNew = createMemo(() => !loading() && (touch() || count() === 0 || (active() && !params.id)))
|
||||
const loadMore = async () => {
|
||||
setWorkspaceStore("limit", (limit) => (limit ?? 0) + 5)
|
||||
await serverSync.project.loadSessions(props.directory)
|
||||
await serverSync().project.loadSessions(props.directory)
|
||||
}
|
||||
|
||||
const workspaceEditActive = createMemo(() => props.ctx.editorOpen(`workspace:${props.directory}`))
|
||||
|
|
@ -357,7 +357,7 @@ export const SortableWorkspace = (props: {
|
|||
|
||||
createEffect(() => {
|
||||
if (!boot()) return
|
||||
serverSync.child(props.directory, { bootstrap: true })
|
||||
serverSync().child(props.directory, { bootstrap: true })
|
||||
})
|
||||
|
||||
return (
|
||||
|
|
@ -450,18 +450,18 @@ export const LocalWorkspace = (props: {
|
|||
const queryOptions = useQueryOptions()
|
||||
const language = useLanguage()
|
||||
const workspace = createMemo(() => {
|
||||
const [store, setStore] = serverSync.child(props.project.worktree)
|
||||
const [store, setStore] = serverSync().child(props.project.worktree)
|
||||
return { store, setStore }
|
||||
})
|
||||
const slug = createMemo(() => base64Encode(props.project.worktree))
|
||||
const sessions = createMemo(() => sortedRootSessions(workspace().store, props.sortNow()))
|
||||
const count = createMemo(() => sessions()?.length ?? 0)
|
||||
const fetching = useIsFetching(() => queryOptions.sessions(pathKey(props.project.worktree)))
|
||||
const fetching = useIsFetching(() => queryOptions().sessions(pathKey(props.project.worktree)))
|
||||
const hasMore = createMemo(() => workspace().store.sessionTotal > count())
|
||||
const loading = () => fetching() > 0 && count() === 0
|
||||
const loadMore = async () => {
|
||||
workspace().setStore("limit", (limit) => (limit ?? 0) + 5)
|
||||
await serverSync.project.loadSessions(props.project.worktree)
|
||||
await serverSync().project.loadSessions(props.project.worktree)
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ export default function NewSessionPage() {
|
|||
|
||||
const newSessionWorktree = createMemo(() => {
|
||||
if (store.worktree === "create") return "create"
|
||||
const project = sync.project
|
||||
if (project && sdk.directory !== project.worktree) return sdk.directory
|
||||
const project = sync().project
|
||||
if (project && sdk().directory !== project.worktree) return sdk().directory
|
||||
return "main"
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -308,10 +308,10 @@ export default function Page() {
|
|||
if (!view().reviewPanel.opened()) view().reviewPanel.open()
|
||||
}
|
||||
|
||||
const info = createMemo(() => (params.id ? sync.session.get(params.id) : undefined))
|
||||
const info = createMemo(() => (params.id ? sync().session.get(params.id) : undefined))
|
||||
const isChildSession = createMemo(() => !!info()?.parentID)
|
||||
const diffs = createMemo(() => (params.id ? list(sync.data.session_diff[params.id]) : []))
|
||||
const canReview = createMemo(() => !!sync.project)
|
||||
const diffs = createMemo(() => (params.id ? list(sync().data.session_diff[params.id]) : []))
|
||||
const canReview = createMemo(() => !!sync().project)
|
||||
const reviewTab = createMemo(() => isDesktop())
|
||||
const tabState = createSessionTabs({
|
||||
tabs,
|
||||
|
|
@ -323,21 +323,21 @@ export default function Page() {
|
|||
const activeTab = tabState.activeTab
|
||||
const activeFileTab = tabState.activeFileTab
|
||||
const revertMessageID = createMemo(() => info()?.revert?.messageID)
|
||||
const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : []))
|
||||
const messages = createMemo(() => (params.id ? (sync().data.message[params.id] ?? []) : []))
|
||||
const messagesReady = createMemo(() => {
|
||||
const id = params.id
|
||||
if (!id) return true
|
||||
return sync.data.message[id] !== undefined
|
||||
return sync().data.message[id] !== undefined
|
||||
})
|
||||
const historyMore = createMemo(() => {
|
||||
const id = params.id
|
||||
if (!id) return false
|
||||
return sync.session.history.more(id)
|
||||
return sync().session.history.more(id)
|
||||
})
|
||||
const historyLoading = createMemo(() => {
|
||||
const id = params.id
|
||||
if (!id) return false
|
||||
return sync.session.history.loading(id)
|
||||
return sync().session.history.loading(id)
|
||||
})
|
||||
const userMessages = createMemo(
|
||||
() => messages().filter((m) => m.role === "user") as UserMessage[],
|
||||
|
|
@ -397,7 +397,7 @@ export default function Page() {
|
|||
})
|
||||
|
||||
const [followup, setFollowup] = persisted(
|
||||
Persist.serverWorkspace(serverSDK.scope, sdk.directory, "followup", ["followup.v1"]),
|
||||
Persist.serverWorkspace(serverSDK().scope, sdk().directory, "followup", ["followup.v1"]),
|
||||
createStore<{
|
||||
items: Record<string, FollowupItem[] | undefined>
|
||||
failed: Record<string, string | undefined>
|
||||
|
|
@ -444,16 +444,16 @@ export default function Page() {
|
|||
}, desktopReviewOpen())
|
||||
|
||||
const turnDiffs = createMemo(() => list(lastUserMessage()?.summary?.diffs))
|
||||
const nogit = createMemo(() => !!sync.project && sync.project.vcs !== "git")
|
||||
const nogit = createMemo(() => {
|
||||
const project = sync().project
|
||||
return !!project && project.vcs !== "git"
|
||||
})
|
||||
const changesOptions = createMemo<ChangeMode[]>(() => {
|
||||
const list: ChangeMode[] = []
|
||||
if (sync.project?.vcs === "git") list.push("git")
|
||||
if (
|
||||
sync.project?.vcs === "git" &&
|
||||
sync.data.vcs?.branch &&
|
||||
sync.data.vcs?.default_branch &&
|
||||
sync.data.vcs.branch !== sync.data.vcs.default_branch
|
||||
) {
|
||||
const project = sync().project
|
||||
const vcs = sync().data.vcs
|
||||
if (project?.vcs === "git") list.push("git")
|
||||
if (project?.vcs === "git" && vcs?.branch && vcs?.default_branch && vcs.branch !== vcs.default_branch) {
|
||||
list.push("branch")
|
||||
}
|
||||
list.push("turn")
|
||||
|
|
@ -469,18 +469,18 @@ export default function Page() {
|
|||
if (store.changes === "git" || store.changes === "branch") return store.changes
|
||||
})
|
||||
const vcsKey = createMemo(
|
||||
() => ["session-vcs", sdk.directory, sync.data.vcs?.branch ?? "", sync.data.vcs?.default_branch ?? ""] as const,
|
||||
() => ["session-vcs", sdk().directory, sync().data.vcs?.branch ?? "", sync().data.vcs?.default_branch ?? ""] as const,
|
||||
)
|
||||
const vcsQuery = createQuery(() => {
|
||||
const mode = vcsMode()
|
||||
const enabled = wantsReview() && sync.project?.vcs === "git"
|
||||
const enabled = wantsReview() && sync().project?.vcs === "git"
|
||||
|
||||
return {
|
||||
queryKey: [...vcsKey(), mode] as const,
|
||||
enabled,
|
||||
queryFn: mode
|
||||
? () =>
|
||||
sdk.client.vcs
|
||||
sdk().client.vcs
|
||||
.diff({ mode })
|
||||
.then((result) => list(result.data))
|
||||
.catch((error) => {
|
||||
|
|
@ -506,8 +506,8 @@ export default function Page() {
|
|||
|
||||
const newSessionWorktree = createMemo(() => {
|
||||
if (store.newSessionWorktree === "create") return "create"
|
||||
const project = sync.project
|
||||
if (project && sdk.directory !== project.worktree) return sdk.directory
|
||||
const project = sync().project
|
||||
if (project && sdk().directory !== project.worktree) return sdk().directory
|
||||
return "main"
|
||||
})
|
||||
|
||||
|
|
@ -569,11 +569,11 @@ export default function Page() {
|
|||
}
|
||||
|
||||
function upsert(next: Project) {
|
||||
const list = serverSync.data.project
|
||||
sync.set("project", next.id)
|
||||
const list = serverSync().data.project
|
||||
sync().set("project", next.id)
|
||||
const idx = list.findIndex((item) => item.id === next.id)
|
||||
if (idx >= 0) {
|
||||
serverSync.set(
|
||||
serverSync().set(
|
||||
"project",
|
||||
list.map((item, i) => (i === idx ? { ...item, ...next } : item)),
|
||||
)
|
||||
|
|
@ -581,14 +581,14 @@ export default function Page() {
|
|||
}
|
||||
const at = list.findIndex((item) => item.id > next.id)
|
||||
if (at >= 0) {
|
||||
serverSync.set("project", [...list.slice(0, at), next, ...list.slice(at)])
|
||||
serverSync().set("project", [...list.slice(0, at), next, ...list.slice(at)])
|
||||
return
|
||||
}
|
||||
serverSync.set("project", [...list, next])
|
||||
serverSync().set("project", [...list, next])
|
||||
}
|
||||
|
||||
const gitMutation = useMutation(() => ({
|
||||
mutationFn: () => sdk.client.project.initGit(),
|
||||
mutationFn: () => sdk().client.project.initGit(),
|
||||
onSuccess: (x) => {
|
||||
if (!x.data) return
|
||||
upsert(x.data)
|
||||
|
|
@ -632,7 +632,7 @@ export default function Page() {
|
|||
const hasScrollGesture = () => Date.now() - ui.scrollGesture < scrollGestureWindowMs
|
||||
|
||||
const [sessionSync] = createResource(
|
||||
() => [sdk.directory, params.id] as const,
|
||||
() => [sdk().directory, params.id] as const,
|
||||
([directory, id]) => {
|
||||
if (refreshFrame !== undefined) cancelAnimationFrame(refreshFrame)
|
||||
if (refreshTimer !== undefined) window.clearTimeout(refreshTimer)
|
||||
|
|
@ -640,11 +640,11 @@ export default function Page() {
|
|||
refreshTimer = undefined
|
||||
if (!id) return
|
||||
|
||||
const cached = untrack(() => sync.data.message[id] !== undefined)
|
||||
const cached = untrack(() => sync().data.message[id] !== undefined)
|
||||
const stale = !cached
|
||||
? false
|
||||
: (() => {
|
||||
const info = getSessionPrefetch(serverSDK.scope, directory, id)
|
||||
const info = getSessionPrefetch(serverSDK().scope, directory, id)
|
||||
if (!info) return true
|
||||
return Date.now() - info.at > SESSION_PREFETCH_TTL
|
||||
})()
|
||||
|
|
@ -655,12 +655,12 @@ export default function Page() {
|
|||
refreshTimer = undefined
|
||||
if (params.id !== id) return
|
||||
untrack(() => {
|
||||
if (stale) void sync.session.sync(id, { force: true })
|
||||
if (stale) void sync().session.sync(id, { force: true })
|
||||
})
|
||||
}, 0)
|
||||
})
|
||||
|
||||
return sync.session.sync(id)
|
||||
return sync().session.sync(id)
|
||||
},
|
||||
)
|
||||
|
||||
|
|
@ -669,9 +669,9 @@ export default function Page() {
|
|||
() => {
|
||||
const id = params.id
|
||||
return [
|
||||
sdk.directory,
|
||||
sdk().directory,
|
||||
id,
|
||||
id ? (sync.data.session_status[id]?.type ?? "idle") : "idle",
|
||||
id ? (sync().data.session_status[id]?.type ?? "idle") : "idle",
|
||||
id ? composer.blocked() : false,
|
||||
] as const
|
||||
},
|
||||
|
|
@ -682,15 +682,15 @@ export default function Page() {
|
|||
todoTimer = undefined
|
||||
if (!id) return
|
||||
if (status === "idle" && !blocked) return
|
||||
const cached = untrack(() => sync.data.todo[id] !== undefined || serverSync.data.session_todo[id] !== undefined)
|
||||
const cached = untrack(() => sync().data.todo[id] !== undefined || serverSync().data.session_todo[id] !== undefined)
|
||||
|
||||
todoFrame = requestAnimationFrame(() => {
|
||||
todoFrame = undefined
|
||||
todoTimer = window.setTimeout(() => {
|
||||
todoTimer = undefined
|
||||
if (sdk.directory !== dir || params.id !== id) return
|
||||
if (sdk().directory !== dir || params.id !== id) return
|
||||
untrack(() => {
|
||||
void sync.session.todo(id, cached ? { force: true } : undefined)
|
||||
void sync().session.todo(id, cached ? { force: true } : undefined)
|
||||
})
|
||||
}, 0)
|
||||
})
|
||||
|
|
@ -723,7 +723,7 @@ export default function Page() {
|
|||
),
|
||||
)
|
||||
|
||||
const stopVcs = sdk.event.listen((evt) => {
|
||||
const stopVcs = sdk().event.listen((evt) => {
|
||||
if (evt.details.type !== "file.watcher.updated") return
|
||||
const props =
|
||||
typeof evt.details.properties === "object" && evt.details.properties
|
||||
|
|
@ -866,7 +866,7 @@ export default function Page() {
|
|||
|
||||
createEffect(
|
||||
on(
|
||||
() => sync.data.session_status[params.id ?? ""]?.type,
|
||||
() => sync().data.session_status[params.id ?? ""]?.type,
|
||||
(next, prev) => {
|
||||
if (next !== "idle" || prev === undefined || prev === "idle") return
|
||||
refreshVcs()
|
||||
|
|
@ -1137,10 +1137,10 @@ export default function Page() {
|
|||
if (!id) return
|
||||
|
||||
if (!wantsReview()) return
|
||||
if (sync.data.session_diff[id] !== undefined) return
|
||||
if (sync.status === "loading") return
|
||||
if (sync().data.session_diff[id] !== undefined) return
|
||||
if (sync().status === "loading") return
|
||||
|
||||
void sync.session.diff(id)
|
||||
void sync().session.diff(id)
|
||||
})
|
||||
|
||||
createEffect(
|
||||
|
|
@ -1155,14 +1155,14 @@ export default function Page() {
|
|||
|
||||
const id = params.id
|
||||
if (!id) return
|
||||
if (!untrack(() => sync.data.session_diff[id] !== undefined)) return
|
||||
if (!untrack(() => sync().data.session_diff[id] !== undefined)) return
|
||||
|
||||
diffFrame = requestAnimationFrame(() => {
|
||||
diffFrame = undefined
|
||||
diffTimer = window.setTimeout(() => {
|
||||
diffTimer = undefined
|
||||
if (sessionKey() !== key) return
|
||||
void sync.session.diff(id, { force: true })
|
||||
void sync().session.diff(id, { force: true })
|
||||
}, 0)
|
||||
})
|
||||
},
|
||||
|
|
@ -1172,10 +1172,10 @@ export default function Page() {
|
|||
|
||||
let treeDir: string | undefined
|
||||
createEffect(() => {
|
||||
const dir = sdk.directory
|
||||
const dir = sdk().directory
|
||||
if (!isDesktop()) return
|
||||
if (!layout.fileTree.opened()) return
|
||||
if (sync.status === "loading") return
|
||||
if (sync().status === "loading") return
|
||||
|
||||
fileTreeTab()
|
||||
const refresh = treeDir !== dir
|
||||
|
|
@ -1185,7 +1185,7 @@ export default function Page() {
|
|||
|
||||
createEffect(
|
||||
on(
|
||||
() => sdk.directory,
|
||||
() => sdk().directory,
|
||||
() => {
|
||||
const tab = activeFileTab()
|
||||
if (!tab) return
|
||||
|
|
@ -1285,7 +1285,7 @@ export default function Page() {
|
|||
visibleUserMessages,
|
||||
historyMore,
|
||||
historyLoading,
|
||||
loadMore: (sessionID) => sync.session.history.loadMore(sessionID),
|
||||
loadMore: (sessionID) => sync().session.history.loadMore(sessionID),
|
||||
userScrolled: autoScroll.userScrolled,
|
||||
scroller: () => scroller,
|
||||
})
|
||||
|
|
@ -1329,8 +1329,8 @@ export default function Page() {
|
|||
)
|
||||
|
||||
const draft = (id: string) =>
|
||||
extractPromptFromParts(sync.data.part[id] ?? [], {
|
||||
directory: sdk.directory,
|
||||
extractPromptFromParts(sync().data.part[id] ?? [], {
|
||||
directory: sdk().directory,
|
||||
attachmentName: language.t("common.attachment"),
|
||||
})
|
||||
|
||||
|
|
@ -1353,7 +1353,7 @@ export default function Page() {
|
|||
}
|
||||
|
||||
const merge = (next: NonNullable<ReturnType<typeof info>>) =>
|
||||
sync.set("session", (list) => {
|
||||
sync().set("session", (list) => {
|
||||
const idx = list.findIndex((item) => item.id === next.id)
|
||||
if (idx < 0) return list
|
||||
const out = list.slice()
|
||||
|
|
@ -1362,7 +1362,7 @@ export default function Page() {
|
|||
})
|
||||
|
||||
const roll = (sessionID: string, next: NonNullable<ReturnType<typeof info>>["revert"]) =>
|
||||
sync.set("session", (list) => {
|
||||
sync().set("session", (list) => {
|
||||
const idx = list.findIndex((item) => item.id === sessionID)
|
||||
if (idx < 0) return list
|
||||
const out = list.slice()
|
||||
|
|
@ -1370,7 +1370,7 @@ export default function Page() {
|
|||
return out
|
||||
})
|
||||
|
||||
const busy = (sessionID: string) => sync.data.session_working(sessionID)
|
||||
const busy = (sessionID: string) => sync().data.session_working(sessionID)
|
||||
|
||||
const queuedFollowups = createMemo(() => {
|
||||
const id = params.id
|
||||
|
|
@ -1393,11 +1393,11 @@ export default function Page() {
|
|||
setFollowup("failed", input.sessionID, undefined)
|
||||
|
||||
const ok = await sendFollowupDraft({
|
||||
client: sdk.client,
|
||||
sync,
|
||||
serverSync,
|
||||
client: sdk().client,
|
||||
sync: sync(),
|
||||
serverSync: serverSync(),
|
||||
draft: item,
|
||||
optimisticBusy: item.sessionDirectory === sdk.directory,
|
||||
optimisticBusy: item.sessionDirectory === sdk().directory,
|
||||
}).catch((err) => {
|
||||
setFollowup("failed", input.sessionID, input.id)
|
||||
fail(err)
|
||||
|
|
@ -1455,7 +1455,7 @@ export default function Page() {
|
|||
const followupDock = createMemo(() => queuedFollowups().map((item) => ({ id: item.id, text: followupText(item) })))
|
||||
|
||||
const sendFollowup = (sessionID: string, id: string, opts?: { manual?: boolean }) => {
|
||||
if (sync.session.get(sessionID)?.parentID) return Promise.resolve()
|
||||
if (sync().session.get(sessionID)?.parentID) return Promise.resolve()
|
||||
const item = (followup.items[sessionID] ?? []).find((entry) => entry.id === id)
|
||||
if (!item) return Promise.resolve()
|
||||
if (followupBusy(sessionID)) return Promise.resolve()
|
||||
|
|
@ -1487,7 +1487,7 @@ export default function Page() {
|
|||
}
|
||||
|
||||
const halt = (sessionID: string) =>
|
||||
busy(sessionID) ? sdk.client.session.abort({ sessionID }).catch(() => {}) : Promise.resolve()
|
||||
busy(sessionID) ? sdk().client.session.abort({ sessionID }).catch(() => {}) : Promise.resolve()
|
||||
|
||||
const revertMutation = useMutation(() => ({
|
||||
mutationFn: async (input: { sessionID: string; messageID: string }) => {
|
||||
|
|
@ -1499,7 +1499,7 @@ export default function Page() {
|
|||
prompt.set(value)
|
||||
})
|
||||
await halt(input.sessionID)
|
||||
.then(() => sdk.client.session.revert(input))
|
||||
.then(() => sdk().client.session.revert(input))
|
||||
.then((result) => {
|
||||
if (result.data) merge(result.data)
|
||||
})
|
||||
|
|
@ -1532,9 +1532,9 @@ export default function Page() {
|
|||
})
|
||||
|
||||
const task = !next
|
||||
? halt(sessionID).then(() => sdk.client.session.unrevert({ sessionID }))
|
||||
? halt(sessionID).then(() => sdk().client.session.unrevert({ sessionID }))
|
||||
: halt(sessionID).then(() =>
|
||||
sdk.client.session.revert({
|
||||
sdk().client.session.revert({
|
||||
sessionID,
|
||||
messageID: next.id,
|
||||
}),
|
||||
|
|
@ -1622,7 +1622,7 @@ export default function Page() {
|
|||
visibleUserMessages,
|
||||
historyMore,
|
||||
historyLoading,
|
||||
loadMore: (sessionID) => sync.session.history.loadMore(sessionID),
|
||||
loadMore: (sessionID) => sync().session.history.loadMore(sessionID),
|
||||
currentMessageId: () => store.messageId,
|
||||
pendingMessage: () => ui.pendingMessage,
|
||||
setPendingMessage: (value) => setUi("pendingMessage", value),
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ export function SessionComposerRegion(props: {
|
|||
const view = layout.view(route.sessionKey)
|
||||
|
||||
const handoffPrompt = createMemo(() => getSessionHandoff(route.sessionKey())?.prompt)
|
||||
const info = createMemo(() => (route.params.id ? sync.session.get(route.params.id) : undefined))
|
||||
const info = createMemo(() => (route.params.id ? sync().session.get(route.params.id) : undefined))
|
||||
const parentID = createMemo(() => info()?.parentID)
|
||||
const child = createMemo(() => !!parentID())
|
||||
const showComposer = createMemo(() => !props.state.blocked() || child())
|
||||
|
|
|
|||
|
|
@ -32,12 +32,12 @@ export function createSessionComposerState(options?: { closeMs?: number | (() =>
|
|||
const permission = usePermission()
|
||||
|
||||
const questionRequest = createMemo((): QuestionRequest | undefined => {
|
||||
return sessionQuestionRequest(sync.data.session, sync.data.question, params.id)
|
||||
return sessionQuestionRequest(sync().data.session, sync().data.question, params.id)
|
||||
})
|
||||
|
||||
const permissionRequest = createMemo((): PermissionRequest | undefined => {
|
||||
return sessionPermissionRequest(sync.data.session, sync.data.permission, params.id, (item) => {
|
||||
return !permission.autoResponds(item, sdk.directory)
|
||||
return sessionPermissionRequest(sync().data.session, sync().data.permission, params.id, (item) => {
|
||||
return !permission.autoResponds(item, sdk().directory)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -50,14 +50,14 @@ export function createSessionComposerState(options?: { closeMs?: number | (() =>
|
|||
const todos = createMemo((): Todo[] => {
|
||||
const id = params.id
|
||||
if (!id) return []
|
||||
return serverSync.data.session_todo[id] ?? []
|
||||
return serverSync().data.session_todo[id] ?? []
|
||||
})
|
||||
|
||||
const done = createMemo(
|
||||
() => todos().length > 0 && todos().every((todo) => todo.status === "completed" || todo.status === "cancelled"),
|
||||
)
|
||||
|
||||
const live = createMemo(() => sync.data.session_working(params.id ?? "") || blocked())
|
||||
const live = createMemo(() => sync().data.session_working(params.id ?? "") || blocked())
|
||||
|
||||
const [store, setStore] = createStore({
|
||||
responding: undefined as string | undefined,
|
||||
|
|
@ -78,7 +78,7 @@ export function createSessionComposerState(options?: { closeMs?: number | (() =>
|
|||
if (store.responding === perm.id) return
|
||||
|
||||
setStore("responding", perm.id)
|
||||
sdk.client.permission
|
||||
sdk().client.permission
|
||||
.respond({ sessionID: perm.sessionID, permissionID: perm.id, response })
|
||||
.catch((err: unknown) => {
|
||||
const description = err instanceof Error ? err.message : String(err)
|
||||
|
|
@ -111,8 +111,8 @@ export function createSessionComposerState(options?: { closeMs?: number | (() =>
|
|||
const clear = () => {
|
||||
const id = params.id
|
||||
if (!id) return
|
||||
serverSync.todo.set(id, [])
|
||||
sync.set("todo", id, [])
|
||||
serverSync().todo.set(id, [])
|
||||
sync().set("todo", id, [])
|
||||
}
|
||||
|
||||
createEffect(
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
|
|||
const sdk = useSDK()
|
||||
const serverSDK = useServerSDK()
|
||||
const language = useLanguage()
|
||||
const cacheKey = ScopedKey.from(serverSDK.scope, props.request.id)
|
||||
const cacheKey = ScopedKey.from(serverSDK().scope, props.request.id)
|
||||
|
||||
const questions = createMemo(() => props.request.questions)
|
||||
const total = createMemo(() => questions().length)
|
||||
|
|
@ -209,7 +209,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
|
|||
}
|
||||
|
||||
const replyMutation = useMutation(() => ({
|
||||
mutationFn: (answers: QuestionAnswer[]) => sdk.client.question.reply({ requestID: props.request.id, answers }),
|
||||
mutationFn: (answers: QuestionAnswer[]) => sdk().client.question.reply({ requestID: props.request.id, answers }),
|
||||
onMutate: () => {
|
||||
props.onSubmit()
|
||||
},
|
||||
|
|
@ -221,7 +221,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
|
|||
}))
|
||||
|
||||
const rejectMutation = useMutation(() => ({
|
||||
mutationFn: () => sdk.client.question.reject({ requestID: props.request.id }),
|
||||
mutationFn: () => sdk().client.question.reject({ requestID: props.request.id }),
|
||||
onMutate: () => {
|
||||
props.onSubmit()
|
||||
},
|
||||
|
|
|
|||
|
|
@ -300,7 +300,7 @@ export function MessageTimeline(props: {
|
|||
const sessionMessages = createMemo(() => {
|
||||
const id = sessionID()
|
||||
if (!id) return emptyMessages
|
||||
return sync.data.message[id] ?? emptyMessages
|
||||
return sync().data.message[id] ?? emptyMessages
|
||||
})
|
||||
const messageByID = createMemo(() => new Map(sessionMessages().map((message) => [message.id, message] as const)))
|
||||
const assistantMessagesByParent = createMemo(() => {
|
||||
|
|
@ -324,10 +324,10 @@ export function MessageTimeline(props: {
|
|||
const sessionStatus = createMemo(() => {
|
||||
const id = sessionID()
|
||||
if (!id) return idle
|
||||
return sync.data.session_status[id] ?? idle
|
||||
return sync().data.session_status[id] ?? idle
|
||||
})
|
||||
const working = createMemo(() => sessionStatus().type !== "idle")
|
||||
const tint = createMemo(() => messageAgentColor(sessionMessages(), sync.data.agent))
|
||||
const tint = createMemo(() => messageAgentColor(sessionMessages(), sync().data.agent))
|
||||
|
||||
const [timeoutDone, setTimeoutDone] = createSignal(true)
|
||||
|
||||
|
|
@ -366,25 +366,25 @@ export function MessageTimeline(props: {
|
|||
const info = createMemo(() => {
|
||||
const id = sessionID()
|
||||
if (!id) return
|
||||
return sync.session.get(id)
|
||||
return sync().session.get(id)
|
||||
})
|
||||
const titleValue = createMemo(() => info()?.title)
|
||||
const titleLabel = createMemo(() => sessionTitle(titleValue()))
|
||||
const shareUrl = createMemo(() => info()?.share?.url)
|
||||
const shareEnabled = createMemo(() => sync.data.config.share !== "disabled")
|
||||
const shareEnabled = createMemo(() => sync().data.config.share !== "disabled")
|
||||
const parentID = createMemo(() => info()?.parentID)
|
||||
const parent = createMemo(() => {
|
||||
const id = parentID()
|
||||
if (!id) return
|
||||
return sync.session.get(id)
|
||||
return sync().session.get(id)
|
||||
})
|
||||
const parentMessages = createMemo(() => {
|
||||
const id = parentID()
|
||||
if (!id) return emptyMessages
|
||||
return sync.data.message[id] ?? emptyMessages
|
||||
return sync().data.message[id] ?? emptyMessages
|
||||
})
|
||||
const parentTitle = createMemo(() => sessionTitle(parent()?.title) ?? language.t("command.session.new"))
|
||||
const getMsgParts = (msgId: string) => sync.data.part[msgId] ?? emptyParts
|
||||
const getMsgParts = (msgId: string) => sync().data.part[msgId] ?? emptyParts
|
||||
const childTaskDescription = createMemo(() => {
|
||||
const id = sessionID()
|
||||
if (!id) return
|
||||
|
|
@ -730,14 +730,14 @@ export function MessageTimeline(props: {
|
|||
}
|
||||
|
||||
const shareMutation = useMutation(() => ({
|
||||
mutationFn: (id: string) => serverSDK.client.session.share({ sessionID: id, directory: sdk.directory }),
|
||||
mutationFn: (id: string) => serverSDK().client.session.share({ sessionID: id, directory: sdk().directory }),
|
||||
onError: (err) => {
|
||||
console.error("Failed to share session", err)
|
||||
},
|
||||
}))
|
||||
|
||||
const unshareMutation = useMutation(() => ({
|
||||
mutationFn: (id: string) => serverSDK.client.session.unshare({ sessionID: id, directory: sdk.directory }),
|
||||
mutationFn: (id: string) => serverSDK().client.session.unshare({ sessionID: id, directory: sdk().directory }),
|
||||
onError: (err) => {
|
||||
console.error("Failed to unshare session", err)
|
||||
},
|
||||
|
|
@ -745,9 +745,9 @@ export function MessageTimeline(props: {
|
|||
|
||||
const titleMutation = useMutation(() => ({
|
||||
mutationFn: (input: { id: string; title: string }) =>
|
||||
sdk.client.session.update({ sessionID: input.id, title: input.title }),
|
||||
sdk().client.session.update({ sessionID: input.id, title: input.title }),
|
||||
onSuccess: (_, input) => {
|
||||
sync.set(
|
||||
sync().set(
|
||||
produce((draft) => {
|
||||
const index = draft.session.findIndex((s) => s.id === input.id)
|
||||
if (index !== -1) draft.session[index].title = input.title
|
||||
|
|
@ -797,8 +797,8 @@ export function MessageTimeline(props: {
|
|||
() => [parentID(), childTaskDescription()] as const,
|
||||
([id, description]) => {
|
||||
if (!id || description) return
|
||||
if (sync.data.message[id] !== undefined) return
|
||||
void sync.session.sync(id)
|
||||
if (sync().data.message[id] !== undefined) return
|
||||
void sync().session.sync(id)
|
||||
},
|
||||
{ defer: true },
|
||||
),
|
||||
|
|
@ -846,25 +846,25 @@ export function MessageTimeline(props: {
|
|||
}
|
||||
|
||||
const archiveSession = async (sessionID: string) => {
|
||||
const session = sync.session.get(sessionID)
|
||||
const session = sync().session.get(sessionID)
|
||||
if (!session) return
|
||||
|
||||
const sessions = sync.data.session ?? []
|
||||
const sessions = sync().data.session ?? []
|
||||
const index = sessions.findIndex((s) => s.id === sessionID)
|
||||
const nextSession = index === -1 ? undefined : (sessions[index + 1] ?? sessions[index - 1])
|
||||
|
||||
await sdk.client.session
|
||||
await sdk().client.session
|
||||
.update({ sessionID, time: { archived: Date.now() } })
|
||||
.then(() => {
|
||||
sync.set(
|
||||
sync().set(
|
||||
produce((draft) => {
|
||||
const index = draft.session.findIndex((s) => s.id === sessionID)
|
||||
if (index !== -1) draft.session.splice(index, 1)
|
||||
}),
|
||||
)
|
||||
sync.session.evict(sessionID)
|
||||
sync().session.evict(sessionID)
|
||||
navigateAfterSessionRemoval(sessionID, session.parentID, nextSession?.id)
|
||||
notifySessionTabsRemoved({ directory: sdk.directory, sessionIDs: [sessionID] })
|
||||
notifySessionTabsRemoved({ directory: sdk().directory, sessionIDs: [sessionID] })
|
||||
})
|
||||
.catch((err) => {
|
||||
showToast({
|
||||
|
|
@ -875,14 +875,14 @@ export function MessageTimeline(props: {
|
|||
}
|
||||
|
||||
const deleteSession = async (sessionID: string) => {
|
||||
const session = sync.session.get(sessionID)
|
||||
const session = sync().session.get(sessionID)
|
||||
if (!session) return false
|
||||
|
||||
const sessions = (sync.data.session ?? []).filter((s) => !s.parentID && !s.time?.archived)
|
||||
const sessions = (sync().data.session ?? []).filter((s) => !s.parentID && !s.time?.archived)
|
||||
const index = sessions.findIndex((s) => s.id === sessionID)
|
||||
const nextSession = index === -1 ? undefined : (sessions[index + 1] ?? sessions[index - 1])
|
||||
|
||||
const result = await sdk.client.session
|
||||
const result = await sdk().client.session
|
||||
.delete({ sessionID })
|
||||
.then((x) => x.data)
|
||||
.catch((err) => {
|
||||
|
|
@ -897,7 +897,7 @@ export function MessageTimeline(props: {
|
|||
|
||||
const removed = new Set<string>([sessionID])
|
||||
const byParent = new Map<string, string[]>()
|
||||
for (const item of sync.data.session) {
|
||||
for (const item of sync().data.session) {
|
||||
const parentID = item.parentID
|
||||
if (!parentID) continue
|
||||
const existing = byParent.get(parentID)
|
||||
|
|
@ -925,16 +925,16 @@ export function MessageTimeline(props: {
|
|||
|
||||
navigateAfterSessionRemoval(sessionID, session.parentID, nextSession?.id)
|
||||
|
||||
sync.set(
|
||||
sync().set(
|
||||
produce((draft) => {
|
||||
draft.session = draft.session.filter((s) => !removed.has(s.id))
|
||||
}),
|
||||
)
|
||||
|
||||
for (const id of removed) {
|
||||
sync.session.evict(id)
|
||||
sync().session.evict(id)
|
||||
}
|
||||
notifySessionTabsRemoved({ directory: sdk.directory, sessionIDs: [...removed] })
|
||||
notifySessionTabsRemoved({ directory: sdk().directory, sessionIDs: [...removed] })
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -946,7 +946,7 @@ export function MessageTimeline(props: {
|
|||
|
||||
function DialogDeleteSession(props: { sessionID: string }) {
|
||||
const name = createMemo(
|
||||
() => sessionTitle(sync.session.get(props.sessionID)?.title) ?? language.t("command.session.new"),
|
||||
() => sessionTitle(sync().session.get(props.sessionID)?.title) ?? language.t("command.session.new"),
|
||||
)
|
||||
const handleDelete = async () => {
|
||||
await deleteSession(props.sessionID)
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export function SessionReviewTab(props: SessionReviewTabProps) {
|
|||
const layout = useLayout()
|
||||
|
||||
const readFile = async (path: string) => {
|
||||
return sdk.client.file
|
||||
return sdk().client.file
|
||||
.read({ path })
|
||||
.then((x) => x.data)
|
||||
.catch((error) => {
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export function useUsageExceededDialogs() {
|
|||
)
|
||||
|
||||
onCleanup(
|
||||
sdk.event.on("session.status", (evt) => {
|
||||
sdk().event.on("session.status", (evt) => {
|
||||
if (evt.properties.sessionID !== params.id) return
|
||||
if (evt.properties.status.type !== "retry") return
|
||||
const { action } = evt.properties.status
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|||
const info = () => {
|
||||
const id = params.id
|
||||
if (!id) return
|
||||
return sync.session.get(id)
|
||||
return sync().session.get(id)
|
||||
}
|
||||
const hasReview = () => !!params.id
|
||||
const normalizeTab = (tab: string) => {
|
||||
|
|
@ -73,7 +73,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|||
const messages = () => {
|
||||
const id = params.id
|
||||
if (!id) return []
|
||||
return sync.data.message[id] ?? []
|
||||
return sync().data.message[id] ?? []
|
||||
}
|
||||
const userMessages = () => messages().filter((m) => m.role === "user") as UserMessage[]
|
||||
const visibleUserMessages = () => {
|
||||
|
|
@ -122,8 +122,8 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|||
|
||||
const isAutoAcceptActive = () => {
|
||||
const sessionID = params.id
|
||||
if (sessionID) return permission.isAutoAccepting(sessionID, sdk.directory)
|
||||
return permission.isAutoAcceptingDirectory(sdk.directory)
|
||||
if (sessionID) return permission.isAutoAccepting(sessionID, sdk().directory)
|
||||
return permission.isAutoAcceptingDirectory(sdk().directory)
|
||||
}
|
||||
const write = async (value: string) => {
|
||||
const body = typeof document === "undefined" ? undefined : document.body
|
||||
|
|
@ -175,7 +175,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|||
return
|
||||
}
|
||||
|
||||
const url = await sdk.client.session
|
||||
const url = await sdk().client.session
|
||||
.share({ sessionID })
|
||||
.then((res) => res.data?.share?.url)
|
||||
.catch(() => undefined)
|
||||
|
|
@ -195,7 +195,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|||
const sessionID = params.id
|
||||
if (!sessionID) return
|
||||
|
||||
await sdk.client.session
|
||||
await sdk().client.session
|
||||
.unshare({ sessionID })
|
||||
.then(() =>
|
||||
showToast({
|
||||
|
|
@ -263,12 +263,12 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|||
|
||||
const toggleAutoAccept = () => {
|
||||
const sessionID = params.id
|
||||
if (sessionID) permission.toggleAutoAccept(sessionID, sdk.directory)
|
||||
else permission.toggleAutoAcceptDirectory(sdk.directory)
|
||||
if (sessionID) permission.toggleAutoAccept(sessionID, sdk().directory)
|
||||
else permission.toggleAutoAcceptDirectory(sdk().directory)
|
||||
|
||||
const active = sessionID
|
||||
? permission.isAutoAccepting(sessionID, sdk.directory)
|
||||
: permission.isAutoAcceptingDirectory(sdk.directory)
|
||||
? permission.isAutoAccepting(sessionID, sdk().directory)
|
||||
: permission.isAutoAcceptingDirectory(sdk().directory)
|
||||
showToast({
|
||||
title: active
|
||||
? language.t("toast.permissions.autoaccept.on.title")
|
||||
|
|
@ -283,18 +283,18 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|||
const sessionID = params.id
|
||||
if (!sessionID) return
|
||||
|
||||
if (sync.data.session_working(params.id ?? "")) {
|
||||
await sdk.client.session.abort({ sessionID }).catch(() => {})
|
||||
if (sync().data.session_working(params.id ?? "")) {
|
||||
await sdk().client.session.abort({ sessionID }).catch(() => {})
|
||||
}
|
||||
|
||||
const revert = info()?.revert?.messageID
|
||||
const message = findLast(userMessages(), (x) => !revert || x.id < revert)
|
||||
if (!message) return
|
||||
|
||||
await sdk.client.session.revert({ sessionID, messageID: message.id })
|
||||
const parts = sync.data.part[message.id]
|
||||
await sdk().client.session.revert({ sessionID, messageID: message.id })
|
||||
const parts = sync().data.part[message.id]
|
||||
if (parts) {
|
||||
const restored = extractPromptFromParts(parts, { directory: sdk.directory })
|
||||
const restored = extractPromptFromParts(parts, { directory: sdk().directory })
|
||||
prompt.set(restored)
|
||||
}
|
||||
|
||||
|
|
@ -311,14 +311,14 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|||
|
||||
const next = userMessages().find((x) => x.id > revertMessageID)
|
||||
if (!next) {
|
||||
await sdk.client.session.unrevert({ sessionID })
|
||||
await sdk().client.session.unrevert({ sessionID })
|
||||
prompt.reset()
|
||||
const last = findLast(userMessages(), (x) => x.id >= revertMessageID)
|
||||
setActiveMessage(last)
|
||||
return
|
||||
}
|
||||
|
||||
await sdk.client.session.revert({ sessionID, messageID: next.id })
|
||||
await sdk().client.session.revert({ sessionID, messageID: next.id })
|
||||
const prev = findLast(userMessages(), (x) => x.id < next.id)
|
||||
setActiveMessage(prev)
|
||||
}
|
||||
|
|
@ -336,7 +336,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|||
return
|
||||
}
|
||||
|
||||
await sdk.client.session.summarize({
|
||||
await sdk().client.session.summarize({
|
||||
sessionID,
|
||||
modelID: model.id,
|
||||
providerID: model.provider.id,
|
||||
|
|
@ -350,7 +350,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|||
}
|
||||
|
||||
const shareCmds = () => {
|
||||
if (sync.data.config.share === "disabled") return []
|
||||
if (sync().data.config.share === "disabled") return []
|
||||
return [
|
||||
sessionCommand({
|
||||
id: "session.share",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue