diff options
| author | Adam <[email protected]> | 2025-12-27 05:16:39 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2025-12-27 14:43:42 -0600 |
| commit | 21eba5f987482b4e2e75ab1c564815bd7b0613f4 (patch) | |
| tree | 2d8cad03e54baa29d83e1e835a7ef2e64d3897e4 /packages/app/src/pages | |
| parent | c523ca412747d66e0236865a4fa2481f7d50f64e (diff) | |
| download | opencode-21eba5f987482b4e2e75ab1c564815bd7b0613f4.tar.gz opencode-21eba5f987482b4e2e75ab1c564815bd7b0613f4.zip | |
feat(desktop): permissions
Diffstat (limited to 'packages/app/src/pages')
| -rw-r--r-- | packages/app/src/pages/directory-layout.tsx | 11 | ||||
| -rw-r--r-- | packages/app/src/pages/layout.tsx | 50 |
2 files changed, 58 insertions, 3 deletions
diff --git a/packages/app/src/pages/directory-layout.tsx b/packages/app/src/pages/directory-layout.tsx index c909a373d..04f90bdcb 100644 --- a/packages/app/src/pages/directory-layout.tsx +++ b/packages/app/src/pages/directory-layout.tsx @@ -1,6 +1,6 @@ import { createMemo, Show, type ParentProps } from "solid-js" import { useParams } from "@solidjs/router" -import { SDKProvider } from "@/context/sdk" +import { SDKProvider, useSDK } from "@/context/sdk" import { SyncProvider, useSync } from "@/context/sync" import { LocalProvider } from "@/context/local" import { base64Decode } from "@opencode-ai/util/encode" @@ -18,8 +18,15 @@ export default function Layout(props: ParentProps) { <SyncProvider> {iife(() => { const sync = useSync() + const sdk = useSDK() return ( - <DataProvider data={sync.data} directory={directory()}> + <DataProvider + data={sync.data} + directory={directory()} + onPermissionRespond={(input) => { + sdk.client.permission.respond(input) + }} + > <LocalProvider>{props.children}</LocalProvider> </DataProvider> ) diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index 5efba6d99..538a3b840 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -117,6 +117,39 @@ export default function Layout(props: ParentProps) { } }) + onMount(() => { + const unsub = globalSync.permission.onUpdated(({ directory, permission }) => { + const currentDir = params.dir ? base64Decode(params.dir) : undefined + const currentSession = params.id + if (directory === currentDir && permission.sessionID === currentSession) return + const [store] = globalSync.child(directory) + const session = store.session.find((s) => s.id === permission.sessionID) + if (directory === currentDir && session?.parentID === currentSession) return + const sessionTitle = session?.title ?? "New session" + const projectName = getFilename(directory) + showToast({ + persistent: true, + icon: "checklist", + title: "Permission required", + description: `${sessionTitle} in ${projectName} needs permission`, + actions: [ + { + label: "Go to session", + onClick: () => { + navigate(`/${base64Encode(directory)}/session/${permission.sessionID}`) + }, + dismissAfter: true, + }, + { + label: "Dismiss", + onClick: "dismiss", + }, + ], + }) + }) + onCleanup(unsub) + }) + function sortSessions(a: Session, b: Session) { const now = Date.now() const oneMinuteAgo = now - 60 * 1000 @@ -454,8 +487,20 @@ export default function Layout(props: ParentProps) { const updated = createMemo(() => DateTime.fromMillis(props.session.time.updated)) const notifications = createMemo(() => notification.session.unseen(props.session.id)) const hasError = createMemo(() => notifications().some((n) => n.type === "error")) + const hasPermissions = createMemo(() => { + const store = globalSync.child(props.project.worktree)[0] + const permissions = store.permission?.[props.session.id] ?? [] + if (permissions.length > 0) return true + const childSessions = store.session.filter((s) => s.parentID === props.session.id) + for (const child of childSessions) { + const childPermissions = store.permission?.[child.id] ?? [] + if (childPermissions.length > 0) return true + } + return false + }) const isWorking = createMemo(() => { if (props.session.id === params.id) return false + if (hasPermissions()) return false const status = globalSync.child(props.project.worktree)[0].session_status[props.session.id] return status?.type === "busy" || status?.type === "retry" }) @@ -486,6 +531,9 @@ export default function Layout(props: ParentProps) { <Match when={isWorking()}> <Spinner class="size-2.5 mr-0.5" /> </Match> + <Match when={hasPermissions()}> + <div class="size-1.5 mr-1.5 rounded-full bg-surface-warning-strong" /> + </Match> <Match when={hasError()}> <div class="size-1.5 mr-1.5 rounded-full bg-text-diff-delete-base" /> </Match> @@ -587,7 +635,7 @@ export default function Layout(props: ParentProps) { <DropdownMenu.Portal> <DropdownMenu.Content> <DropdownMenu.Item onSelect={() => closeProject(props.project.worktree)}> - <DropdownMenu.ItemLabel>Close Project</DropdownMenu.ItemLabel> + <DropdownMenu.ItemLabel>Close project</DropdownMenu.ItemLabel> </DropdownMenu.Item> </DropdownMenu.Content> </DropdownMenu.Portal> |
