summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src
diff options
context:
space:
mode:
authorAdam <[email protected]>2025-12-31 14:04:44 -0600
committerAdam <[email protected]>2025-12-31 14:04:44 -0600
commitd4a2652eda926dbfa03878ef3e46e55ef53517ca (patch)
tree6aa0d1e3d21a41210cdcebf6ee3d4e1f87f2110d /packages/app/src
parent7a4bfbe56d0115996dd37ccf9d73f95716571ecf (diff)
downloadopencode-d4a2652eda926dbfa03878ef3e46e55ef53517ca.tar.gz
opencode-d4a2652eda926dbfa03878ef3e46e55ef53517ca.zip
feat(desktop): better affordance for auto-accept
Diffstat (limited to 'packages/app/src')
-rw-r--r--packages/app/src/app.tsx54
-rw-r--r--packages/app/src/components/prompt-input.tsx35
-rw-r--r--packages/app/src/context/permission.tsx14
-rw-r--r--packages/app/src/pages/session.tsx33
4 files changed, 88 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",