summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam <[email protected]>2025-11-24 11:56:00 -0600
committerAdam <[email protected]>2025-11-24 15:10:40 -0600
commitacf1dd85006a0bb4009ef601ae7f7a60eb36af4d (patch)
treec8c7c0ed92e580016b968a64f066ea32bad17e2f
parent3fb57044d120618c92214b734d1fc831955e39a2 (diff)
downloadopencode-acf1dd85006a0bb4009ef601ae7f7a60eb36af4d.tar.gz
opencode-acf1dd85006a0bb4009ef601ae7f7a60eb36af4d.zip
wip(share): more styling
-rw-r--r--packages/desktop/src/pages/session.tsx2
-rw-r--r--packages/enterprise/src/app.css17
-rw-r--r--packages/enterprise/src/app.tsx17
-rw-r--r--packages/enterprise/src/routes/share/[sessionID].tsx279
-rw-r--r--packages/opencode/src/bun/index.ts2
-rw-r--r--packages/opencode/src/cli/ui.ts2
-rw-r--r--packages/opencode/src/config/config.ts2
-rw-r--r--packages/opencode/src/config/markdown.ts2
-rw-r--r--packages/opencode/src/file/fzf.ts2
-rw-r--r--packages/opencode/src/file/ripgrep.ts2
-rw-r--r--packages/opencode/src/ide/index.ts2
-rw-r--r--packages/opencode/src/index.ts2
-rw-r--r--packages/opencode/src/installation/index.ts2
-rw-r--r--packages/opencode/src/lsp/client.ts2
-rw-r--r--packages/opencode/src/mcp/index.ts2
-rw-r--r--packages/opencode/src/provider/auth.ts2
-rw-r--r--packages/opencode/src/provider/provider.ts2
-rw-r--r--packages/opencode/src/server/server.ts2
-rw-r--r--packages/opencode/src/session/message-v2.ts2
-rw-r--r--packages/opencode/src/session/message.ts2
-rw-r--r--packages/opencode/src/session/prompt.ts2
-rw-r--r--packages/opencode/src/storage/storage.ts2
-rw-r--r--packages/ui/src/assets/fonts/tx-02.otfbin0 -> 85940 bytes
-rw-r--r--packages/ui/src/assets/fonts/tx-02.ttfbin0 -> 129876 bytes
-rw-r--r--packages/ui/src/assets/fonts/tx-02.woff2bin0 -> 51664 bytes
-rw-r--r--packages/ui/src/components/fonts.tsx12
-rw-r--r--packages/ui/src/components/message-nav.css57
-rw-r--r--packages/ui/src/components/message-nav.tsx30
-rw-r--r--packages/ui/src/styles/theme.css4
-rw-r--r--packages/util/src/error.ts (renamed from packages/opencode/src/util/error.ts)0
30 files changed, 264 insertions, 190 deletions
diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx
index f2c572c3f..84c7d64f2 100644
--- a/packages/desktop/src/pages/session.tsx
+++ b/packages/desktop/src/pages/session.tsx
@@ -339,7 +339,7 @@ export default function Page() {
<div class="flex items-start justify-start h-full min-h-0">
<Show when={session.messages.user().length > 1}>
<MessageNav
- classList={{ "mt-1.5 mr-3": wide(), "mt-3 mr-8": !wide() }}
+ classList={{ "mt-0.5 mr-3 absolute right-full": wide(), "mt-3 mr-8": !wide() }}
messages={session.messages.user()}
current={session.messages.active()}
onMessageSelect={session.messages.setActive}
diff --git a/packages/enterprise/src/app.css b/packages/enterprise/src/app.css
index 01c61e62c..4af87bca6 100644
--- a/packages/enterprise/src/app.css
+++ b/packages/enterprise/src/app.css
@@ -1,18 +1 @@
@import "@opencode-ai/ui/styles/tailwind";
-
-:root {
- --background-rgb: 214, 219, 220;
- --foreground-rgb: 0, 0, 0;
-}
-
-@media (prefers-color-scheme: dark) {
- :root {
- --background-rgb: 0, 0, 0;
- --foreground-rgb: 255, 255, 255;
- }
-}
-
-body {
- /* background: rgb(var(--background-rgb)); */
- /* color: rgb(var(--foreground-rgb)); */
-}
diff --git a/packages/enterprise/src/app.tsx b/packages/enterprise/src/app.tsx
index fd10899f2..77e9d8677 100644
--- a/packages/enterprise/src/app.tsx
+++ b/packages/enterprise/src/app.tsx
@@ -1,6 +1,5 @@
import { Router } from "@solidjs/router"
import { FileRoutes } from "@solidjs/start/router"
-import { Suspense } from "solid-js"
import { Fonts } from "@opencode-ai/ui/fonts"
import { MetaProvider } from "@solidjs/meta"
import { MarkedProvider } from "@opencode-ai/ui/context/marked"
@@ -10,16 +9,12 @@ export default function App() {
return (
<Router
root={(props) => (
- <>
- <Suspense>
- <MarkedProvider>
- <MetaProvider>
- <Fonts />
- {props.children}
- </MetaProvider>
- </MarkedProvider>
- </Suspense>
- </>
+ <MarkedProvider>
+ <MetaProvider>
+ <Fonts />
+ {props.children}
+ </MetaProvider>
+ </MarkedProvider>
)}
>
<FileRoutes />
diff --git a/packages/enterprise/src/routes/share/[sessionID].tsx b/packages/enterprise/src/routes/share/[sessionID].tsx
index a2365aa1d..96a651391 100644
--- a/packages/enterprise/src/routes/share/[sessionID].tsx
+++ b/packages/enterprise/src/routes/share/[sessionID].tsx
@@ -3,15 +3,26 @@ import { SessionTurn } from "@opencode-ai/ui/session-turn"
import { SessionReview } from "@opencode-ai/ui/session-review"
import { DataProvider } from "@opencode-ai/ui/context"
import { createAsync, query, RouteDefinition, useParams } from "@solidjs/router"
-import { createMemo, Show } from "solid-js"
+import { createMemo, ErrorBoundary, Show } from "solid-js"
import { Share } from "~/core/share"
import { Logo, Mark } from "@opencode-ai/ui/logo"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { iife } from "@opencode-ai/util/iife"
import { Binary } from "@opencode-ai/util/binary"
+import { NamedError } from "@opencode-ai/util/error"
import { DateTime } from "luxon"
import { MessageNav } from "@opencode-ai/ui/message-nav"
import { createStore } from "solid-js/store"
+import z from "zod"
+import NotFound from "../[...404]"
+
+const SessionDataMissingError = NamedError.create(
+ "SessionDataMissingError",
+ z.object({
+ sessionID: z.string(),
+ message: z.string().optional(),
+ }),
+)
const getData = query(async (sessionID) => {
const data = await Share.data(sessionID)
@@ -70,6 +81,8 @@ const getData = query(async (sessionID) => {
break
}
}
+ const match = Binary.search(result.session, sessionID!, (s) => s.id)
+ if (!match.found) throw new SessionDataMissingError({ sessionID })
return result
}, "getShareData")
@@ -80,126 +93,172 @@ export const route = {
export default function () {
const params = useParams()
const data = createAsync(async () => {
- if (!params.sessionID) return
+ if (!params.sessionID) throw new Error("Missing sessionID")
return getData(params.sessionID)
})
return (
- <Show when={data()}>
- {(data) => (
- <DataProvider data={data()}>
- {iife(() => {
- const [store, setStore] = createStore({
- messageId: undefined as string | undefined,
- })
- const match = createMemo(() => Binary.search(data().session, params.sessionID!, (s) => s.id))
- if (!match().found) throw new Error(`Session ${params.sessionID} not found`)
- const info = createMemo(() => data().session[match().index])
- const messages = createMemo(() =>
- params.sessionID ? (data().message[params.sessionID]?.filter((m) => m.role === "user") ?? []) : [],
- )
- const firstUserMessage = createMemo(() => messages().at(0))
- const activeMessage = createMemo(
- () => messages().find((m) => m.id === store.messageId) ?? firstUserMessage(),
- )
- function setActiveMessage(message: UserMessage | undefined) {
- if (message) {
- setStore("messageId", message.id)
- } else {
- setStore("messageId", undefined)
+ <ErrorBoundary
+ fallback={(e) => {
+ return (
+ <Show when={e.message === "SessionDataMissingError"}>
+ <NotFound />
+ </Show>
+ )
+ }}
+ >
+ <Show when={data()}>
+ {(data) => (
+ <DataProvider data={data()}>
+ {iife(() => {
+ const [store, setStore] = createStore({
+ messageId: undefined as string | undefined,
+ })
+ const match = createMemo(() => Binary.search(data().session, params.sessionID!, (s) => s.id))
+ if (!match().found) throw new Error(`Session ${params.sessionID} not found`)
+ const info = createMemo(() => data().session[match().index])
+ const messages = createMemo(() =>
+ params.sessionID
+ ? (data().message[params.sessionID]?.filter((m) => m.role === "user") ?? []).sort(
+ (a, b) => b.time.created - a.time.created,
+ )
+ : [],
+ )
+ const firstUserMessage = createMemo(() => messages().at(0))
+ const activeMessage = createMemo(
+ () => messages().find((m) => m.id === store.messageId) ?? firstUserMessage(),
+ )
+ function setActiveMessage(message: UserMessage | undefined) {
+ if (message) {
+ setStore("messageId", message.id)
+ } else {
+ setStore("messageId", undefined)
+ }
}
- }
- const provider = createMemo(() => activeMessage()?.model?.providerID)
- const modelID = createMemo(() => activeMessage()?.model?.modelID)
- const model = createMemo(() => data().model[params.sessionID!]?.find((m) => m.id === modelID()))
- const diffs = createMemo(() => data().session_diff[params.sessionID!] ?? [])
+ const provider = createMemo(() => activeMessage()?.model?.providerID)
+ const modelID = createMemo(() => activeMessage()?.model?.modelID)
+ const model = createMemo(() => data().model[params.sessionID!]?.find((m) => m.id === modelID()))
+ const diffs = createMemo(() => data().session_diff[params.sessionID!] ?? [])
- return (
- <div class="relative bg-background-stronger w-screen h-screen overflow-hidden flex flex-col">
- <header class="h-12 px-6 py-2 flex items-center justify-between self-stretch bg-background-base border-b border-border-weak-base">
- <div class="">
- <a href="https://opencode.ai">
- <Mark />
- </a>
- </div>
- <div class="flex gap-3 items-center">
- <IconButton
- as={"a"}
- href="https://github.com/sst/opencode"
- target="_blank"
- icon="github"
- variant="ghost"
- />
- <IconButton
- as={"a"}
- href="https://opencode.ai/discord"
- target="_blank"
- icon="discord"
- variant="ghost"
- />
+ const title = (
+ <div class="flex flex-col gap-4 shrink-0">
+ <div class="h-8 flex gap-4 items-center justify-start self-stretch">
+ <div class="pl-[2.5px] pr-2 flex items-center gap-1.75 bg-surface-strong shadow-xs-border-base">
+ <Mark class="shrink-0 w-3 my-0.5" />
+ <div class="text-12-mono text-text-base">v{info().version}</div>
+ </div>
+ <div class="flex gap-2 items-center">
+ <img src={`https://models.dev/logos/${provider()}.svg`} class="size-3.5 shrink-0 dark:invert" />
+ <div class="text-12-regular text-text-base">{model()?.name ?? modelID()}</div>
+ </div>
+ <div class="text-12-regular text-text-weaker">
+ {DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")}
+ </div>
</div>
- </header>
- <div class="select-text flex flex-col flex-1 min-h-0">
- <div class="w-full flex-1 min-h-0 flex">
- <div
- classList={{
- "@container relative shrink-0 pt-14 flex flex-col gap-10 min-h-0 w-full mx-auto": true,
- "px-21 @4xl:px-6 max-w-2xl": diffs().length > 0,
- "px-6 max-w-2xl": diffs().length === 0,
- }}
- >
- <div class="flex flex-col gap-4 shrink-0">
- <div class="h-8 flex gap-4 items-center justify-start self-stretch">
- <div class="pl-[2.5px] pr-2 flex items-center gap-1.75 bg-surface-strong shadow-xs-border-base">
- <Mark class="shrink-0 w-3 my-0.5" />
- <div class="text-12-mono text-text-base">v{info().version}</div>
- </div>
- <div class="flex gap-2 items-center">
- <img
- src={`https://models.dev/logos/${provider()}.svg`}
- class="size-4 shrink-0 dark:invert"
- />
- <div class="text-12-regular text-text-base">{model()?.name ?? modelID()}</div>
- </div>
- <div class="text-12-regular text-text-weaker">
- {DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")}
- </div>
+ <div class="text-left text-16-medium text-text-strong">{info().title}</div>
+ </div>
+ )
+
+ return (
+ <div class="relative bg-background-stronger w-screen h-screen overflow-hidden flex flex-col">
+ <header class="h-12 px-6 py-2 flex items-center justify-between self-stretch bg-background-base border-b border-border-weak-base">
+ <div class="">
+ <a href="https://opencode.ai">
+ <Mark />
+ </a>
+ </div>
+ <div class="flex gap-3 items-center">
+ <IconButton
+ as={"a"}
+ href="https://github.com/sst/opencode"
+ target="_blank"
+ icon="github"
+ variant="ghost"
+ />
+ <IconButton
+ as={"a"}
+ href="https://opencode.ai/discord"
+ target="_blank"
+ icon="discord"
+ variant="ghost"
+ />
+ </div>
+ </header>
+ <div class="select-text flex flex-col flex-1 min-h-0">
+ <div class="w-full flex-1 min-h-0 flex">
+ <div
+ classList={{
+ "@container relative shrink-0 pt-14 flex flex-col gap-10 min-h-0 w-full mx-auto": true,
+ "px-21 @4xl:px-6 max-w-2xl": diffs().length > 0,
+ "px-6 max-w-2xl": diffs().length === 0,
+ }}
+ >
+ {title}
+ <div class="flex items-start justify-start h-full min-h-0">
+ <Show when={messages().length > 1}>
+ <>
+ <div
+ classList={{
+ "xl:hidden": true,
+ "absolute right-[90%]": diffs().length > 0,
+ "absolute right-full": diffs().length === 0,
+ }}
+ >
+ <MessageNav
+ classList={{
+ "mt-0.5 mr-8": diffs().length === 0,
+ "mt-2.5 mr-3": diffs().length > 0,
+ }}
+ messages={messages()}
+ current={activeMessage()}
+ onMessageSelect={setActiveMessage}
+ size={!diffs().length ? "normal" : "compact"}
+ />
+ </div>
+ <div
+ classList={{
+ "hidden xl:block": true,
+ "absolute right-[90%]": diffs().length > 0,
+ "absolute right-full": diffs().length === 0,
+ }}
+ >
+ <MessageNav
+ classList={{
+ "mt-0.5 mr-8": diffs().length === 0,
+ "mt-2.5 mr-3": diffs().length > 0,
+ }}
+ messages={messages()}
+ current={activeMessage()}
+ onMessageSelect={setActiveMessage}
+ size={!diffs().length ? "normal" : "compact"}
+ />
+ </div>
+ </>
+ </Show>
+ <SessionTurn
+ sessionID={params.sessionID!}
+ messageID={store.messageId ?? firstUserMessage()!.id!}
+ classes={{ root: "grow", content: "flex flex-col justify-between", container: "pb-20" }}
+ >
+ <div class="flex items-center justify-center pb-8 shrink-0">
+ <Logo class="w-58.5 opacity-12" />
+ </div>
+ </SessionTurn>
</div>
- <div class="text-left text-16-medium text-text-strong">{info().title}</div>
- </div>
- <div class="flex items-start justify-start h-full min-h-0">
- <Show when={messages().length > 1}>
- <MessageNav
- classList={{ "mt-2 mr-3": true }}
- messages={messages()}
- current={activeMessage()}
- onMessageSelect={setActiveMessage}
- size={!diffs().length ? "normal" : "compact"}
- />
- </Show>
- <SessionTurn
- sessionID={params.sessionID!}
- messageID={store.messageId ?? firstUserMessage()!.id!}
- classes={{ root: "grow", content: "flex flex-col justify-between", container: "pb-20" }}
- >
- <div class="flex items-center justify-center pb-8 shrink-0">
- <Logo class="w-58.5 opacity-12" />
- </div>
- </SessionTurn>
</div>
+ <Show when={diffs().length}>
+ <div class="relative grow px-6 pt-14 flex-1 min-h-0 border-l border-border-weak-base">
+ <SessionReview diffs={diffs()} class="pb-20" />
+ </div>
+ </Show>
</div>
- <Show when={diffs().length}>
- <div class="relative grow px-6 pt-14 flex-1 min-h-0 border-l border-border-weak-base">
- <SessionReview diffs={diffs()} class="pb-20" />
- </div>
- </Show>
</div>
</div>
- </div>
- )
- })}
- </DataProvider>
- )}
- </Show>
+ )
+ })}
+ </DataProvider>
+ )}
+ </Show>
+ </ErrorBoundary>
)
}
diff --git a/packages/opencode/src/bun/index.ts b/packages/opencode/src/bun/index.ts
index 7c19c303b..edf74c310 100644
--- a/packages/opencode/src/bun/index.ts
+++ b/packages/opencode/src/bun/index.ts
@@ -2,7 +2,7 @@ import z from "zod"
import { Global } from "../global"
import { Log } from "../util/log"
import path from "path"
-import { NamedError } from "../util/error"
+import { NamedError } from "@opencode-ai/util/error"
import { readableStreamToText } from "bun"
import { createRequire } from "module"
import { Lock } from "../util/lock"
diff --git a/packages/opencode/src/cli/ui.ts b/packages/opencode/src/cli/ui.ts
index 43760a65a..acd1383a0 100644
--- a/packages/opencode/src/cli/ui.ts
+++ b/packages/opencode/src/cli/ui.ts
@@ -1,6 +1,6 @@
import z from "zod"
import { EOL } from "os"
-import { NamedError } from "../util/error"
+import { NamedError } from "@opencode-ai/util/error"
export namespace UI {
const LOGO = [
diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts
index 79f969eea..779a4e8e2 100644
--- a/packages/opencode/src/config/config.ts
+++ b/packages/opencode/src/config/config.ts
@@ -8,7 +8,7 @@ import { mergeDeep, pipe } from "remeda"
import { Global } from "../global"
import fs from "fs/promises"
import { lazy } from "../util/lazy"
-import { NamedError } from "../util/error"
+import { NamedError } from "@opencode-ai/util/error"
import { Flag } from "../flag/flag"
import { Auth } from "../auth"
import { type ParseError as JsoncParseError, parse as parseJsonc, printParseErrorCode } from "jsonc-parser"
diff --git a/packages/opencode/src/config/markdown.ts b/packages/opencode/src/config/markdown.ts
index 3e84bbf43..f20842c41 100644
--- a/packages/opencode/src/config/markdown.ts
+++ b/packages/opencode/src/config/markdown.ts
@@ -1,4 +1,4 @@
-import { NamedError } from "@/util/error"
+import { NamedError } from "@opencode-ai/util/error"
import matter from "gray-matter"
import { z } from "zod"
diff --git a/packages/opencode/src/file/fzf.ts b/packages/opencode/src/file/fzf.ts
index cd0aa4fc8..50db8901d 100644
--- a/packages/opencode/src/file/fzf.ts
+++ b/packages/opencode/src/file/fzf.ts
@@ -2,7 +2,7 @@ import path from "path"
import { Global } from "../global"
import fs from "fs/promises"
import z from "zod"
-import { NamedError } from "../util/error"
+import { NamedError } from "@opencode-ai/util/error"
import { lazy } from "../util/lazy"
import { Log } from "../util/log"
import { ZipReader, BlobReader, BlobWriter } from "@zip.js/zip.js"
diff --git a/packages/opencode/src/file/ripgrep.ts b/packages/opencode/src/file/ripgrep.ts
index 7c871fafb..00d9e8c38 100644
--- a/packages/opencode/src/file/ripgrep.ts
+++ b/packages/opencode/src/file/ripgrep.ts
@@ -3,7 +3,7 @@ import path from "path"
import { Global } from "../global"
import fs from "fs/promises"
import z from "zod"
-import { NamedError } from "../util/error"
+import { NamedError } from "@opencode-ai/util/error"
import { lazy } from "../util/lazy"
import { $ } from "bun"
diff --git a/packages/opencode/src/ide/index.ts b/packages/opencode/src/ide/index.ts
index 035bccecf..268f115fc 100644
--- a/packages/opencode/src/ide/index.ts
+++ b/packages/opencode/src/ide/index.ts
@@ -1,6 +1,6 @@
import { spawn } from "bun"
import z from "zod"
-import { NamedError } from "../util/error"
+import { NamedError } from "@opencode-ai/util/error"
import { Log } from "../util/log"
import { Bus } from "../bus"
diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts
index acd7ee1c0..38b6b5a3f 100644
--- a/packages/opencode/src/index.ts
+++ b/packages/opencode/src/index.ts
@@ -9,7 +9,7 @@ import { UpgradeCommand } from "./cli/cmd/upgrade"
import { ModelsCommand } from "./cli/cmd/models"
import { UI } from "./cli/ui"
import { Installation } from "./installation"
-import { NamedError } from "./util/error"
+import { NamedError } from "@opencode-ai/util/error"
import { FormatError } from "./cli/error"
import { ServeCommand } from "./cli/cmd/serve"
import { DebugCommand } from "./cli/cmd/debug"
diff --git a/packages/opencode/src/installation/index.ts b/packages/opencode/src/installation/index.ts
index cfb590013..7ac2980c4 100644
--- a/packages/opencode/src/installation/index.ts
+++ b/packages/opencode/src/installation/index.ts
@@ -1,7 +1,7 @@
import path from "path"
import { $ } from "bun"
import z from "zod"
-import { NamedError } from "../util/error"
+import { NamedError } from "@opencode-ai/util/error"
import { Bus } from "../bus"
import { Log } from "../util/log"
diff --git a/packages/opencode/src/lsp/client.ts b/packages/opencode/src/lsp/client.ts
index 02363a599..f8b7db7ae 100644
--- a/packages/opencode/src/lsp/client.ts
+++ b/packages/opencode/src/lsp/client.ts
@@ -6,7 +6,7 @@ import { LANGUAGE_EXTENSIONS } from "./language"
import { Bus } from "../bus"
import z from "zod"
import type { LSPServer } from "./server"
-import { NamedError } from "../util/error"
+import { NamedError } from "@opencode-ai/util/error"
import { withTimeout } from "../util/timeout"
import { Instance } from "../project/instance"
diff --git a/packages/opencode/src/mcp/index.ts b/packages/opencode/src/mcp/index.ts
index 8ef1a138e..a68a1716f 100644
--- a/packages/opencode/src/mcp/index.ts
+++ b/packages/opencode/src/mcp/index.ts
@@ -5,7 +5,7 @@ import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
import { Config } from "../config/config"
import { Log } from "../util/log"
-import { NamedError } from "../util/error"
+import { NamedError } from "@opencode-ai/util/error"
import z from "zod/v4"
import { Instance } from "../project/instance"
import { withTimeout } from "@/util/timeout"
diff --git a/packages/opencode/src/provider/auth.ts b/packages/opencode/src/provider/auth.ts
index fb0016039..d06253ab4 100644
--- a/packages/opencode/src/provider/auth.ts
+++ b/packages/opencode/src/provider/auth.ts
@@ -4,7 +4,7 @@ import { map, filter, pipe, fromEntries, mapValues } from "remeda"
import z from "zod"
import { fn } from "@/util/fn"
import type { AuthOuathResult, Hooks } from "@opencode-ai/plugin"
-import { NamedError } from "@/util/error"
+import { NamedError } from "@opencode-ai/util/error"
import { Auth } from "@/auth"
export namespace ProviderAuth {
diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts
index e6f7f5f88..8267b458a 100644
--- a/packages/opencode/src/provider/provider.ts
+++ b/packages/opencode/src/provider/provider.ts
@@ -7,7 +7,7 @@ import { Log } from "../util/log"
import { BunProc } from "../bun"
import { Plugin } from "../plugin"
import { ModelsDev } from "./models"
-import { NamedError } from "../util/error"
+import { NamedError } from "@opencode-ai/util/error"
import { Auth } from "../auth"
import { Instance } from "../project/instance"
import { Flag } from "../flag/flag"
diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts
index f6530ee99..fec174454 100644
--- a/packages/opencode/src/server/server.ts
+++ b/packages/opencode/src/server/server.ts
@@ -9,7 +9,7 @@ import { Session } from "../session"
import z from "zod"
import { Provider } from "../provider/provider"
import { mapValues } from "remeda"
-import { NamedError } from "../util/error"
+import { NamedError } from "@opencode-ai/util/error"
import { ModelsDev } from "../provider/models"
import { Ripgrep } from "../file/ripgrep"
import { Config } from "../config/config"
diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts
index c451ae2b3..20b612f54 100644
--- a/packages/opencode/src/session/message-v2.ts
+++ b/packages/opencode/src/session/message-v2.ts
@@ -1,6 +1,6 @@
import z from "zod"
import { Bus } from "../bus"
-import { NamedError } from "../util/error"
+import { NamedError } from "@opencode-ai/util/error"
import { Message } from "./message"
import { APICallError, convertToModelMessages, LoadAPIKeyError, type ModelMessage, type UIMessage } from "ai"
import { Identifier } from "../id/id"
diff --git a/packages/opencode/src/session/message.ts b/packages/opencode/src/session/message.ts
index 4471f9235..5c950d0e4 100644
--- a/packages/opencode/src/session/message.ts
+++ b/packages/opencode/src/session/message.ts
@@ -1,5 +1,5 @@
import z from "zod"
-import { NamedError } from "../util/error"
+import { NamedError } from "@opencode-ai/util/error"
export namespace Message {
export const OutputLengthError = NamedError.create("MessageOutputLengthError", z.object({}))
diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts
index b3c3c4671..741e3cc7e 100644
--- a/packages/opencode/src/session/prompt.ts
+++ b/packages/opencode/src/session/prompt.ts
@@ -43,7 +43,7 @@ import { Command } from "../command"
import { $, fileURLToPath } from "bun"
import { ConfigMarkdown } from "../config/markdown"
import { SessionSummary } from "./summary"
-import { NamedError } from "@/util/error"
+import { NamedError } from "@opencode-ai/util/error"
import { fn } from "@/util/fn"
import { SessionProcessor } from "./processor"
import { TaskTool } from "@/tool/task"
diff --git a/packages/opencode/src/storage/storage.ts b/packages/opencode/src/storage/storage.ts
index 5d4f63943..8b4042ea1 100644
--- a/packages/opencode/src/storage/storage.ts
+++ b/packages/opencode/src/storage/storage.ts
@@ -5,7 +5,7 @@ import { Global } from "../global"
import { lazy } from "../util/lazy"
import { Lock } from "../util/lock"
import { $ } from "bun"
-import { NamedError } from "@/util/error"
+import { NamedError } from "@opencode-ai/util/error"
import z from "zod"
export namespace Storage {
diff --git a/packages/ui/src/assets/fonts/tx-02.otf b/packages/ui/src/assets/fonts/tx-02.otf
new file mode 100644
index 000000000..dfdd2800b
--- /dev/null
+++ b/packages/ui/src/assets/fonts/tx-02.otf
Binary files differ
diff --git a/packages/ui/src/assets/fonts/tx-02.ttf b/packages/ui/src/assets/fonts/tx-02.ttf
new file mode 100644
index 000000000..dcad94c21
--- /dev/null
+++ b/packages/ui/src/assets/fonts/tx-02.ttf
Binary files differ
diff --git a/packages/ui/src/assets/fonts/tx-02.woff2 b/packages/ui/src/assets/fonts/tx-02.woff2
new file mode 100644
index 000000000..d18fc3912
--- /dev/null
+++ b/packages/ui/src/assets/fonts/tx-02.woff2
Binary files differ
diff --git a/packages/ui/src/components/fonts.tsx b/packages/ui/src/components/fonts.tsx
index ff4fb7588..0b28e8f3c 100644
--- a/packages/ui/src/components/fonts.tsx
+++ b/packages/ui/src/components/fonts.tsx
@@ -1,6 +1,6 @@
import { Style, Link } from "@solidjs/meta"
import geist from "../assets/fonts/geist.woff2"
-import geistMono from "../assets/fonts/geist-mono.woff2"
+import tx02 from "../assets/fonts/tx-02.woff2"
export const Fonts = () => {
return (
@@ -22,14 +22,14 @@ export const Fonts = () => {
line-gap-override: 1%;
}
@font-face {
- font-family: "Geist Mono";
- src: url("${geistMono}") format("woff2-variations");
+ font-family: "Berkeley Mono";
+ src: url("${tx02}") format("woff2-variations");
font-display: swap;
font-style: normal;
- font-weight: 100 900;
+ font-weight: 400 700;
}
@font-face {
- font-family: "Geist Mono Fallback";
+ font-family: "Berkeley Mono Fallback";
src: local("Courier New");
size-adjust: 100%;
ascent-override: 97%;
@@ -38,7 +38,7 @@ export const Fonts = () => {
}
`}</Style>
<Link rel="preload" href={geist} as="font" type="font/woff2" crossorigin="anonymous" />
- <Link rel="preload" href={geistMono} as="font" type="font/woff2" crossorigin="anonymous" />
+ <Link rel="preload" href={tx02} as="font" type="font/woff2" crossorigin="anonymous" />
</>
)
}
diff --git a/packages/ui/src/components/message-nav.css b/packages/ui/src/components/message-nav.css
index 6e9d96a26..57316fbde 100644
--- a/packages/ui/src/components/message-nav.css
+++ b/packages/ui/src/components/message-nav.css
@@ -1,6 +1,4 @@
[data-component="message-nav"] {
- /* margin-right: 32px; */
- /* margin-top: 12px; */
flex-shrink: 0;
display: flex;
flex-direction: column;
@@ -9,15 +7,8 @@
list-style: none;
&[data-size="normal"] {
- position: absolute;
- right: 100%;
width: 240px;
- /* margin-top: 12px; */
-
- @media (min-width: 80rem) {
- gap: 8px;
- /* margin-top: 4px; */
- }
+ gap: 4px;
}
}
@@ -36,10 +27,8 @@
display: flex;
align-items: center;
justify-content: flex-start;
- height: 8px;
- width: 32px;
- /* margin-right: -12px; */
- cursor: pointer;
+ height: 12px;
+ width: 24px;
border: none;
background: none;
padding: 0;
@@ -52,7 +41,7 @@
[data-slot="message-nav-tick-line"] {
height: 1px;
- width: 20px;
+ width: 16px;
background-color: var(--icon-base);
transition:
width 0.2s,
@@ -69,11 +58,12 @@
align-items: center;
align-self: stretch;
width: 100%;
- column-gap: 8px;
+ column-gap: 12px;
cursor: default;
border: none;
background: none;
- padding: 0;
+ padding: 4px 12px;
+ border-radius: var(--radius-sm);
}
[data-slot="message-nav-title-preview"] {
@@ -90,6 +80,37 @@
}
}
-[data-slot="message-nav-item"]:hover [data-slot="message-nav-title-preview"] {
+[data-slot="message-nav-item"]:hover [data-slot="message-nav-message-button"] {
+ background-color: var(--surface-base);
+}
+[data-slot="message-nav-item"]:active [data-slot="message-nav-message-button"] {
+ background-color: var(--surface-base-active);
+}
+
+[data-slot="message-nav-item"]:active [data-slot="message-nav-title-preview"] {
color: var(--text-base);
}
+
+[data-slot="message-nav-tooltip"] {
+ z-index: 1000;
+}
+
+[data-slot="message-nav-tooltip-content"] {
+ display: flex;
+ padding: 4px 4px 6px 4px;
+ justify-content: center;
+ align-items: center;
+ border-radius: var(--radius-md);
+ background: var(--surface-raised-stronger-non-alpha);
+
+ /* border/shadow-xs/base */
+ box-shadow:
+ 0 0 0 1px var(--border-weak-base, rgba(17, 0, 0, 0.12)),
+ 0 1px 2px -1px rgba(19, 16, 16, 0.04),
+ 0 1px 2px 0 rgba(19, 16, 16, 0.06),
+ 0 1px 3px 0 rgba(19, 16, 16, 0.08);
+
+ * {
+ margin: 0 !important;
+ }
+}
diff --git a/packages/ui/src/components/message-nav.tsx b/packages/ui/src/components/message-nav.tsx
index 8475c3206..333ad3743 100644
--- a/packages/ui/src/components/message-nav.tsx
+++ b/packages/ui/src/components/message-nav.tsx
@@ -2,6 +2,8 @@ import { UserMessage } from "@opencode-ai/sdk"
import { ComponentProps, createMemo, For, Match, Show, splitProps, Switch } from "solid-js"
import { DiffChanges } from "./diff-changes"
import { Spinner } from "./spinner"
+import { HoverCard } from "@kobalte/core/hover-card"
+import { Tooltip } from "@kobalte/core/tooltip"
export function MessageNav(
props: ComponentProps<"ul"> & {
@@ -17,7 +19,7 @@ export function MessageNav(
return local.messages?.at(0)
})
- return (
+ const content = (
<ul role="list" data-component="message-nav" data-size={local.size} {...others}>
<For each={local.messages}>
{(message) => {
@@ -28,13 +30,9 @@ export function MessageNav(
<li data-slot="message-nav-item">
<Switch>
<Match when={local.size === "compact"}>
- <button
- data-slot="message-nav-tick-button"
- data-active={message.id === local.current?.id || undefined}
- onClick={handleClick}
- >
+ <div data-slot="message-nav-tick-button" data-active={message.id === local.current?.id || undefined}>
<div data-slot="message-nav-tick-line" />
- </button>
+ </div>
</Match>
<Match when={local.size === "normal"}>
<button data-slot="message-nav-message-button" onClick={handleClick}>
@@ -63,4 +61,22 @@ export function MessageNav(
</For>
</ul>
)
+
+ return (
+ <Switch>
+ <Match when={local.size === "compact"}>
+ <Tooltip openDelay={0} closeDelay={0} placement="top-start" gutter={-65} shift={-16} overlap>
+ <Tooltip.Trigger as="div">{content}</Tooltip.Trigger>
+ <Tooltip.Portal>
+ <Tooltip.Content data-slot="message-nav-tooltip">
+ <div data-slot="message-nav-tooltip-content">
+ <MessageNav {...props} size="normal" />
+ </div>
+ </Tooltip.Content>
+ </Tooltip.Portal>
+ </Tooltip>
+ </Match>
+ <Match when={local.size === "normal"}>{content}</Match>
+ </Switch>
+ )
}
diff --git a/packages/ui/src/styles/theme.css b/packages/ui/src/styles/theme.css
index c5c9fade1..6df7ccf09 100644
--- a/packages/ui/src/styles/theme.css
+++ b/packages/ui/src/styles/theme.css
@@ -1,8 +1,8 @@
:root {
--font-family-sans: "Geist", "Geist Fallback";
--font-family-sans--font-feature-settings: "ss03" 1;
- --font-family-mono: "Geist Mono", "Geist Mono Fallback";
- --font-family-mono--font-feature-settings: "ss02" 1;
+ --font-family-mono: "Berkeley Mono", "Berkeley Mono Fallback";
+ --font-family-mono--font-feature-settings: "ss01" 1;
--font-size-small: 12px;
--font-size-base: 14px;
diff --git a/packages/opencode/src/util/error.ts b/packages/util/src/error.ts
index 12c27a0a7..12c27a0a7 100644
--- a/packages/opencode/src/util/error.ts
+++ b/packages/util/src/error.ts