diff options
| author | Adam <[email protected]> | 2025-12-31 14:04:44 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2025-12-31 14:04:44 -0600 |
| commit | d4a2652eda926dbfa03878ef3e46e55ef53517ca (patch) | |
| tree | 6aa0d1e3d21a41210cdcebf6ee3d4e1f87f2110d | |
| parent | 7a4bfbe56d0115996dd37ccf9d73f95716571ecf (diff) | |
| download | opencode-d4a2652eda926dbfa03878ef3e46e55ef53517ca.tar.gz opencode-d4a2652eda926dbfa03878ef3e46e55ef53517ca.zip | |
feat(desktop): better affordance for auto-accept
| -rw-r--r-- | packages/app/src/app.tsx | 54 | ||||
| -rw-r--r-- | packages/app/src/components/prompt-input.tsx | 35 | ||||
| -rw-r--r-- | packages/app/src/context/permission.tsx | 14 | ||||
| -rw-r--r-- | packages/app/src/pages/session.tsx | 33 | ||||
| -rw-r--r-- | packages/ui/src/components/icon.tsx | 1 |
5 files changed, 89 insertions, 48 deletions
diff --git a/packages/app/src/app.tsx b/packages/app/src/app.tsx index cc1f052f6..b4bae7dc8 100644 --- a/packages/app/src/app.tsx +++ b/packages/app/src/app.tsx @@ -67,36 +67,36 @@ export function App() { <ServerKey> <GlobalSDKProvider> <GlobalSyncProvider> - <PermissionProvider> - <LayoutProvider> - <NotificationProvider> - <Router - root={(props) => ( + <Router + root={(props) => ( + <PermissionProvider> + <LayoutProvider> + <NotificationProvider> <CommandProvider> <Layout>{props.children}</Layout> </CommandProvider> - )} - > - <Route path="/" component={Home} /> - <Route path="/:dir" component={DirectoryLayout}> - <Route path="/" component={() => <Navigate href="session" />} /> - <Route - path="/session/:id?" - component={(p) => ( - <Show when={p.params.id ?? "new"} keyed> - <TerminalProvider> - <PromptProvider> - <Session /> - </PromptProvider> - </TerminalProvider> - </Show> - )} - /> - </Route> - </Router> - </NotificationProvider> - </LayoutProvider> - </PermissionProvider> + </NotificationProvider> + </LayoutProvider> + </PermissionProvider> + )} + > + <Route path="/" component={Home} /> + <Route path="/:dir" component={DirectoryLayout}> + <Route path="/" component={() => <Navigate href="session" />} /> + <Route + path="/session/:id?" + component={(p) => ( + <Show when={p.params.id ?? "new"} keyed> + <TerminalProvider> + <PromptProvider> + <Session /> + </PromptProvider> + </TerminalProvider> + </Show> + )} + /> + </Route> + </Router> </GlobalSyncProvider> </GlobalSDKProvider> </ServerKey> diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 44d3fbef8..afa6538a2 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -23,6 +23,7 @@ import { useCommand } from "@/context/command" import { persisted } from "@/utils/persist" import { Identifier } from "@/utils/id" import { SessionContextUsage } from "@/components/session-context-usage" +import { usePermission } from "@/context/permission" const ACCEPTED_IMAGE_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"] const ACCEPTED_FILE_TYPES = [...ACCEPTED_IMAGE_TYPES, "application/pdf"] @@ -80,6 +81,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { const dialog = useDialog() const providers = useProviders() const command = useCommand() + const permission = usePermission() let editorRef!: HTMLDivElement let fileInputRef!: HTMLInputElement let scrollRef!: HTMLDivElement @@ -1346,7 +1348,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { </Show> </div> <div class="relative p-3 flex items-center justify-between"> - <div class="flex items-center justify-start gap-1"> + <div class="flex items-center justify-start gap-0.5"> <Switch> <Match when={store.mode === "shell"}> <div class="flex items-center gap-2 px-2 h-6"> @@ -1393,16 +1395,43 @@ export const PromptInput: Component<PromptInputProps> = (props) => { </Button> </Tooltip> <Show when={local.model.variant.list().length > 0}> - <TooltipKeybind placement="top" title="Thinking effort" keybind={command.keybind("model.variant")}> + <TooltipKeybind + placement="top" + title="Thinking effort" + keybind={command.keybind("model.variant.cycle")} + > <Button variant="ghost" + class="text-text-base _hidden group-hover/prompt-input:inline-block" onClick={() => local.model.variant.cycle()} - class="text-text-base hidden group-hover/prompt-input:inline-block" > <span class="capitalize text-12-regular">{local.model.variant.current() ?? "Default"}</span> </Button> </TooltipKeybind> </Show> + <Show when={permission.permissionsEnabled() && params.id}> + <TooltipKeybind + placement="top" + title="Auto-accept edits" + keybind={command.keybind("permissions.autoaccept")} + > + <Button + variant="ghost" + onClick={() => permission.toggleAutoAccept(params.id!, sdk.directory)} + classList={{ + "_hidden group-hover/prompt-input:flex size-6 items-center justify-center": true, + "text-text-base": !permission.isAutoAccepting(params.id!), + "bg-surface-success-base": permission.isAutoAccepting(params.id!), + }} + > + <Icon + name="chevron-double-right" + size="small" + classList={{ "text-icon-success-base": permission.isAutoAccepting(params.id!) }} + /> + </Button> + </TooltipKeybind> + </Show> </Match> </Switch> </div> diff --git a/packages/app/src/context/permission.tsx b/packages/app/src/context/permission.tsx index d82a8392c..a0ad1ee05 100644 --- a/packages/app/src/context/permission.tsx +++ b/packages/app/src/context/permission.tsx @@ -1,9 +1,11 @@ -import { onCleanup } from "solid-js" +import { createMemo, onCleanup } from "solid-js" import { createStore } from "solid-js/store" import { createSimpleContext } from "@opencode-ai/ui/context" import type { Permission } from "@opencode-ai/sdk/v2/client" import { persisted } from "@/utils/persist" import { useGlobalSDK } from "@/context/global-sdk" +import { useGlobalSync } from "./global-sync" +import { useParams } from "@solidjs/router" type PermissionRespondFn = (input: { sessionID: string @@ -21,7 +23,16 @@ function shouldAutoAccept(perm: Permission) { export const { use: usePermission, provider: PermissionProvider } = createSimpleContext({ name: "Permission", init: () => { + const params = useParams() const globalSDK = useGlobalSDK() + const globalSync = useGlobalSync() + + const permissionsEnabled = createMemo(() => { + if (!params.dir) return false + const [store] = globalSync.child(params.dir) + return store.config.permission !== undefined + }) + const [store, setStore, _, ready] = persisted( "permission.v3", createStore({ @@ -106,6 +117,7 @@ export const { use: usePermission, provider: PermissionProvider } = createSimple disableAutoAccept(sessionID: string) { disable(sessionID) }, + permissionsEnabled, } }, }) diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index 1cea23de4..24d7bb94f 100644 --- a/packages/app/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -447,21 +447,6 @@ export default function Page() { slash: "open", onSelect: () => dialog.show(() => <DialogSelectFile />), }, - // { - // id: "theme.toggle", - // title: "Toggle theme", - // description: "Switch between themes", - // category: "View", - // keybind: "ctrl+t", - // slash: "theme", - // onSelect: () => { - // const currentTheme = localStorage.getItem("theme") ?? "oc-1" - // const themes = ["oc-1", "oc-2-paper"] - // const nextTheme = themes[(themes.indexOf(currentTheme) + 1) % themes.length] - // localStorage.setItem("theme", nextTheme) - // document.documentElement.setAttribute("data-theme", nextTheme) - // }, - // }, { id: "terminal.toggle", title: "Toggle terminal", @@ -551,14 +536,28 @@ export default function Page() { onSelect: () => local.agent.move(-1), }, { + id: "model.variant.cycle", + title: "Cycle thinking effort", + description: "Switch to the next effort level", + category: "Model", + keybind: "shift+mod+t", + onSelect: () => { + local.model.variant.cycle() + showToast({ + title: "Thinking effort changed", + description: "The thinking effort has been changed to " + (local.model.variant.current() ?? "Default"), + }) + }, + }, + { id: "permissions.autoaccept", title: params.id && permission.isAutoAccepting(params.id) ? "Stop auto-accepting edits" : "Auto-accept edits", category: "Permissions", - disabled: !params.id, + keybind: "mod+shift+a", + disabled: !params.id || !permission.permissionsEnabled(), onSelect: () => { const sessionID = params.id if (!sessionID) return - permission.toggleAutoAccept(sessionID, sdk.directory) showToast({ title: permission.isAutoAccepting(sessionID) ? "Auto-accepting edits" : "Stopped auto-accepting edits", diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index ffd34d851..9debb0a71 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -12,6 +12,7 @@ const icons = { "chevron-down": `<path d="M6.6665 8.33325L9.99984 11.6666L13.3332 8.33325" stroke="currentColor" stroke-linecap="square"/>`, "chevron-right": `<path d="M8.33301 13.3327L11.6663 9.99935L8.33301 6.66602" stroke="currentColor" stroke-linecap="square"/>`, "chevron-grabber-vertical": `<path d="M6.66675 12.4998L10.0001 15.8332L13.3334 12.4998M6.66675 7.49984L10.0001 4.1665L13.3334 7.49984" stroke="currentColor" stroke-linecap="square"/>`, + "chevron-double-right": `<path d="M11.6654 13.3346L14.9987 10.0013L11.6654 6.66797M5.83203 13.3346L9.16536 10.0013L5.83203 6.66797" stroke="currentColor" stroke-linecap="square"/>`, "circle-x": `<path fill-rule="evenodd" clip-rule="evenodd" d="M1.6665 10.0003C1.6665 5.39795 5.39746 1.66699 9.99984 1.66699C14.6022 1.66699 18.3332 5.39795 18.3332 10.0003C18.3332 14.6027 14.6022 18.3337 9.99984 18.3337C5.39746 18.3337 1.6665 14.6027 1.6665 10.0003ZM7.49984 6.91107L6.91058 7.50033L9.41058 10.0003L6.91058 12.5003L7.49984 13.0896L9.99984 10.5896L12.4998 13.0896L13.0891 12.5003L10.5891 10.0003L13.0891 7.50033L12.4998 6.91107L9.99984 9.41107L7.49984 6.91107Z" fill="currentColor"/>`, close: `<path d="M3.75 3.75L16.25 16.25M16.25 3.75L3.75 16.25" stroke="currentColor" stroke-linecap="square"/>`, checklist: `<path d="M9.58342 13.7498H17.0834M9.58342 6.24984H17.0834M2.91675 6.6665L4.58341 7.9165L7.08341 4.1665M2.91675 14.1665L4.58341 15.4165L7.08341 11.6665" stroke="currentColor" stroke-linecap="square"/>`, |
