summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src
diff options
context:
space:
mode:
authoradamelmore <[email protected]>2026-01-26 22:58:29 -0600
committeradamelmore <[email protected]>2026-01-27 06:29:18 -0600
commit58b9b54600f12ba4ec1d80a2d1b7dee3879a0479 (patch)
tree797755369dc2d81ff04c32f0d0b4780e2db84d03 /packages/app/src
parentc0a5f853497de6778f1430b691e393caa54c59a7 (diff)
downloadopencode-58b9b54600f12ba4ec1d80a2d1b7dee3879a0479.tar.gz
opencode-58b9b54600f12ba4ec1d80a2d1b7dee3879a0479.zip
feat(app): forward and back buttons
Diffstat (limited to 'packages/app/src')
-rw-r--r--packages/app/src/components/titlebar.tsx135
-rw-r--r--packages/app/src/context/platform.tsx6
-rw-r--r--packages/app/src/entry.tsx6
-rw-r--r--packages/app/src/i18n/en.ts1
4 files changed, 121 insertions, 27 deletions
diff --git a/packages/app/src/components/titlebar.tsx b/packages/app/src/components/titlebar.tsx
index 159216a4f..1c8256069 100644
--- a/packages/app/src/components/titlebar.tsx
+++ b/packages/app/src/components/titlebar.tsx
@@ -1,8 +1,10 @@
-import { createEffect, createMemo, Show } from "solid-js"
+import { createEffect, createMemo, Show, untrack } from "solid-js"
+import { createStore } from "solid-js/store"
+import { useLocation, useNavigate } from "@solidjs/router"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { Icon } from "@opencode-ai/ui/icon"
import { Button } from "@opencode-ai/ui/button"
-import { TooltipKeybind } from "@opencode-ai/ui/tooltip"
+import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
import { useTheme } from "@opencode-ai/ui/theme"
import { useLayout } from "@/context/layout"
@@ -16,11 +18,68 @@ export function Titlebar() {
const command = useCommand()
const language = useLanguage()
const theme = useTheme()
+ const navigate = useNavigate()
+ const location = useLocation()
const mac = createMemo(() => platform.platform === "desktop" && platform.os === "macos")
const windows = createMemo(() => platform.platform === "desktop" && platform.os === "windows")
const web = createMemo(() => platform.platform === "web")
+ const [history, setHistory] = createStore({
+ stack: [] as string[],
+ index: 0,
+ action: undefined as "back" | "forward" | undefined,
+ })
+
+ const path = () => `${location.pathname}${location.search}${location.hash}`
+
+ createEffect(() => {
+ const current = path()
+
+ untrack(() => {
+ if (!history.stack.length) {
+ const stack = current === "/" ? ["/"] : ["/", current]
+ setHistory({ stack, index: stack.length - 1 })
+ return
+ }
+
+ const active = history.stack[history.index]
+ if (current === active) {
+ if (history.action) setHistory("action", undefined)
+ return
+ }
+
+ if (history.action) {
+ setHistory("action", undefined)
+ return
+ }
+
+ const next = history.stack.slice(0, history.index + 1).concat(current)
+ setHistory({ stack: next, index: next.length - 1 })
+ })
+ })
+
+ const canBack = createMemo(() => history.index > 0)
+ const canForward = createMemo(() => history.index < history.stack.length - 1)
+
+ const back = () => {
+ if (!canBack()) return
+ const index = history.index - 1
+ const to = history.stack[index]
+ if (!to) return
+ setHistory({ index, action: "back" })
+ navigate(to)
+ }
+
+ const forward = () => {
+ if (!canForward()) return
+ const index = history.index + 1
+ const to = history.stack[index]
+ if (!to) return
+ setHistory({ index, action: "forward" })
+ navigate(to)
+ }
+
const getWin = () => {
if (platform.platform !== "desktop") return
@@ -106,34 +165,56 @@ export function Titlebar() {
/>
</div>
</Show>
- <TooltipKeybind
- class={web() ? "hidden xl:flex shrink-0 ml-14" : "hidden xl:flex shrink-0 ml-2"}
- placement="bottom"
- title={language.t("command.sidebar.toggle")}
- keybind={command.keybind("sidebar.toggle")}
- >
- <Button
- variant="ghost"
- class="group/sidebar-toggle size-6 p-0"
- onClick={layout.sidebar.toggle}
- aria-label={language.t("command.sidebar.toggle")}
- aria-expanded={layout.sidebar.opened()}
+ <div class="flex items-center gap-1 shrink-0">
+ <TooltipKeybind
+ class={web() ? "hidden xl:flex shrink-0 ml-14" : "hidden xl:flex shrink-0 ml-2"}
+ placement="bottom"
+ title={language.t("command.sidebar.toggle")}
+ keybind={command.keybind("sidebar.toggle")}
>
- <div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0">
- <Icon
- size="small"
- name={layout.sidebar.opened() ? "layout-left-full" : "layout-left"}
- class="group-hover/sidebar-toggle:hidden"
+ <Button
+ variant="ghost"
+ class="group/sidebar-toggle size-6 p-0"
+ onClick={layout.sidebar.toggle}
+ aria-label={language.t("command.sidebar.toggle")}
+ aria-expanded={layout.sidebar.opened()}
+ >
+ <div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0">
+ <Icon
+ size="small"
+ name={layout.sidebar.opened() ? "layout-left-full" : "layout-left"}
+ class="group-hover/sidebar-toggle:hidden"
+ />
+ <Icon size="small" name="layout-left-partial" class="hidden group-hover/sidebar-toggle:inline-block" />
+ <Icon
+ size="small"
+ name={layout.sidebar.opened() ? "layout-left" : "layout-left-full"}
+ class="hidden group-active/sidebar-toggle:inline-block"
+ />
+ </div>
+ </Button>
+ </TooltipKeybind>
+ <div class="hidden xl:flex items-center gap-1 shrink-0">
+ <Tooltip placement="bottom" value={language.t("common.goBack")}>
+ <IconButton
+ icon="arrow-left"
+ variant="ghost"
+ disabled={!canBack()}
+ onClick={back}
+ aria-label={language.t("common.goBack")}
/>
- <Icon size="small" name="layout-left-partial" class="hidden group-hover/sidebar-toggle:inline-block" />
- <Icon
- size="small"
- name={layout.sidebar.opened() ? "layout-left" : "layout-left-full"}
- class="hidden group-active/sidebar-toggle:inline-block"
+ </Tooltip>
+ <Tooltip placement="bottom" value={language.t("common.goForward")}>
+ <IconButton
+ icon="arrow-right"
+ variant="ghost"
+ disabled={!canForward()}
+ onClick={forward}
+ aria-label={language.t("common.goForward")}
/>
- </div>
- </Button>
- </TooltipKeybind>
+ </Tooltip>
+ </div>
+ </div>
<div id="opencode-titlebar-left" class="flex items-center gap-3 min-w-0 px-2" data-tauri-drag-region />
<div class="flex-1 h-full" data-tauri-drag-region />
<div
diff --git a/packages/app/src/context/platform.tsx b/packages/app/src/context/platform.tsx
index b97f70fea..f6fb157f0 100644
--- a/packages/app/src/context/platform.tsx
+++ b/packages/app/src/context/platform.tsx
@@ -17,6 +17,12 @@ export type Platform = {
/** Restart the app */
restart(): Promise<void>
+ /** Navigate back in history */
+ back(): void
+
+ /** Navigate forward in history */
+ forward(): void
+
/** Send a system notification (optional deep link) */
notify(title: string, description?: string, href?: string): Promise<void>
diff --git a/packages/app/src/entry.tsx b/packages/app/src/entry.tsx
index 7fe03bb6a..aa52fa1e7 100644
--- a/packages/app/src/entry.tsx
+++ b/packages/app/src/entry.tsx
@@ -31,6 +31,12 @@ const platform: Platform = {
openLink(url: string) {
window.open(url, "_blank")
},
+ back() {
+ window.history.back()
+ },
+ forward() {
+ window.history.forward()
+ },
restart: async () => {
window.location.reload()
},
diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts
index bca08275f..abbe497dc 100644
--- a/packages/app/src/i18n/en.ts
+++ b/packages/app/src/i18n/en.ts
@@ -167,6 +167,7 @@ export const dict = {
"common.search.placeholder": "Search",
"common.goBack": "Go back",
+ "common.goForward": "Go forward",
"common.loading": "Loading",
"common.loading.ellipsis": "...",
"common.cancel": "Cancel",