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() {
1}> ( - <> - - - - - {props.children} - - - - + + + + {props.children} + + )} > 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 ( - - {(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) + { + return ( + + + + ) + }} + > + + {(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 ( -
-
-
- - - -
-
- - -
-
-
-
-
0, - "px-6 max-w-2xl": diffs().length === 0, - }} - > -
-
-
- -
v{info().version}
-
-
- -
{model()?.name ?? modelID()}
-
-
- {DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")} -
-
-
{info().title}
-
-
- 1}> - - - -
- -
-
-
+ const title = ( +
+
+
+ +
v{info().version}
- -
- +
+ +
{model()?.name ?? modelID()}
+
+
+ {DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")} +
+
+
{info().title}
+
+ ) + + return ( +
+
+
+ + + +
+
+ + +
+
+
+
+
0, + "px-6 max-w-2xl": diffs().length === 0, + }} + > + {title} +
+ 1}> + <> +
0, + "absolute right-full": diffs().length === 0, + }} + > + 0, + }} + messages={messages()} + current={activeMessage()} + onMessageSelect={setActiveMessage} + size={!diffs().length ? "normal" : "compact"} + /> +
+
0, + "absolute right-full": diffs().length === 0, + }} + > + 0, + }} + messages={messages()} + current={activeMessage()} + onMessageSelect={setActiveMessage} + size={!diffs().length ? "normal" : "compact"} + /> +
+ +
+ +
+ +
+
+
- + +
+ +
+
+
-
- ) - })} - - )} - + ) + })} + + )} + + ) } 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 Binary files /dev/null and b/packages/ui/src/assets/fonts/tx-02.otf 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 Binary files /dev/null and b/packages/ui/src/assets/fonts/tx-02.ttf 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 Binary files /dev/null and b/packages/ui/src/assets/fonts/tx-02.woff2 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 = () => { } `} - + ) } 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 = (
    {(message) => { @@ -28,13 +30,9 @@ export function MessageNav(
  • - +