diff --git a/bun.lock b/bun.lock index 34a6488ba0..58cbbf12b6 100644 --- a/bun.lock +++ b/bun.lock @@ -23,7 +23,7 @@ }, "packages/app": { "name": "@opencode-ai/app", - "version": "1.1.34", + "version": "1.1.35", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -73,7 +73,7 @@ }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.1.34", + "version": "1.1.35", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -107,7 +107,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.1.34", + "version": "1.1.35", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -134,7 +134,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.1.34", + "version": "1.1.35", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -158,7 +158,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.1.34", + "version": "1.1.35", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -182,7 +182,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.1.34", + "version": "1.1.35", "dependencies": { "@opencode-ai/app": "workspace:*", "@opencode-ai/ui": "workspace:*", @@ -211,7 +211,7 @@ }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.1.34", + "version": "1.1.35", "dependencies": { "@opencode-ai/ui": "workspace:*", "@opencode-ai/util": "workspace:*", @@ -240,7 +240,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.1.34", + "version": "1.1.35", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "catalog:", @@ -256,7 +256,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.1.34", + "version": "1.1.35", "bin": { "opencode": "./bin/opencode", }, @@ -360,7 +360,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.1.34", + "version": "1.1.35", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -380,9 +380,9 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.1.34", + "version": "1.1.35", "devDependencies": { - "@hey-api/openapi-ts": "0.90.4", + "@hey-api/openapi-ts": "0.90.10", "@tsconfig/node22": "catalog:", "@types/node": "catalog:", "@typescript/native-preview": "catalog:", @@ -391,7 +391,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.1.34", + "version": "1.1.35", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -404,7 +404,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.1.34", + "version": "1.1.35", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -423,6 +423,7 @@ "marked": "catalog:", "marked-katex-extension": "5.1.6", "marked-shiki": "catalog:", + "morphdom": "2.7.8", "remeda": "catalog:", "shiki": "catalog:", "solid-js": "catalog:", @@ -445,7 +446,7 @@ }, "packages/util": { "name": "@opencode-ai/util", - "version": "1.1.34", + "version": "1.1.35", "dependencies": { "zod": "catalog:", }, @@ -456,7 +457,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.1.34", + "version": "1.1.35", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", @@ -928,11 +929,13 @@ "@happy-dom/global-registrator": ["@happy-dom/global-registrator@20.0.11", "", { "dependencies": { "@types/node": "^20.0.0", "happy-dom": "^20.0.11" } }, "sha512-GqNqiShBT/lzkHTMC/slKBrvN0DsD4Di8ssBk4aDaVgEn+2WMzE6DXxq701ndSXj7/0cJ8mNT71pM7Bnrr6JRw=="], - "@hey-api/codegen-core": ["@hey-api/codegen-core@0.5.2", "", { "dependencies": { "ansi-colors": "4.1.3", "color-support": "1.1.3" }, "peerDependencies": { "typescript": ">=5.5.3" } }, "sha512-88cqrrB2cLXN8nMOHidQTcVOnZsJ5kebEbBefjMCifaUCwTA30ouSSWvTZqrOX4O104zjJyu7M8Gcv/NNYQuaA=="], + "@hey-api/codegen-core": ["@hey-api/codegen-core@0.5.5", "", { "dependencies": { "@hey-api/types": "0.1.2", "ansi-colors": "4.1.3", "c12": "3.3.3", "color-support": "1.1.3" }, "peerDependencies": { "typescript": ">=5.5.3" } }, "sha512-f2ZHucnA2wBGAY8ipB4wn/mrEYW+WUxU2huJmUvfDO6AE2vfILSHeF3wCO39Pz4wUYPoAWZByaauftLrOfC12Q=="], "@hey-api/json-schema-ref-parser": ["@hey-api/json-schema-ref-parser@1.2.2", "", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.1", "lodash": "^4.17.21" } }, "sha512-oS+5yAdwnK20lSeFO1d53Ku+yaGCsY8PcrmSq2GtSs3bsBfRnHAbpPKSVzQcaxAOrzj5NB+f34WhZglVrNayBA=="], - "@hey-api/openapi-ts": ["@hey-api/openapi-ts@0.90.4", "", { "dependencies": { "@hey-api/codegen-core": "^0.5.2", "@hey-api/json-schema-ref-parser": "1.2.2", "ansi-colors": "4.1.3", "c12": "3.3.3", "color-support": "1.1.3", "commander": "14.0.2", "open": "11.0.0", "semver": "7.7.3" }, "peerDependencies": { "typescript": ">=5.5.3" }, "bin": { "openapi-ts": "bin/run.js" } }, "sha512-9l++kjcb0ui4JqPlueZ6OZ9zKn6eK/8//Z2jHcIXb5MRwDRgubOOSpTU5llEv3uvWfT10VzcMp99dySWq0AASw=="], + "@hey-api/openapi-ts": ["@hey-api/openapi-ts@0.90.10", "", { "dependencies": { "@hey-api/codegen-core": "^0.5.5", "@hey-api/json-schema-ref-parser": "1.2.2", "@hey-api/types": "0.1.2", "ansi-colors": "4.1.3", "color-support": "1.1.3", "commander": "14.0.2", "open": "11.0.0", "semver": "7.7.3" }, "peerDependencies": { "typescript": ">=5.5.3" }, "bin": { "openapi-ts": "bin/run.js" } }, "sha512-o0wlFxuLt1bcyIV/ZH8DQ1wrgODTnUYj/VfCHOOYgXUQlLp9Dm2PjihOz+WYrZLowhqUhSKeJRArOGzvLuOTsg=="], + + "@hey-api/types": ["@hey-api/types@0.1.2", "", {}, "sha512-uNNtiVAWL7XNrV/tFXx7GLY9lwaaDazx1173cGW3+UEaw4RUPsHEmiB4DSpcjNxMIcrctfz2sGKLnVx5PBG2RA=="], "@hono/node-server": ["@hono/node-server@1.19.7", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw=="], @@ -3102,6 +3105,8 @@ "mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="], + "morphdom": ["morphdom@2.7.8", "", {}, "sha512-D/fR4xgGUyVRbdMGU6Nejea1RFzYxYtyurG4Fbv2Fi/daKlWKuXGLOdXtl+3eIwL110cI2hz1ZojGICjjFLgTg=="], + "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], diff --git a/nix/hashes.json b/nix/hashes.json index d96583cd52..d5559fdfb1 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,8 +1,8 @@ { "nodeModules": { - "x86_64-linux": "sha256-spng4G0hikHyNhplZiXL/K4XzSBsQNwFCrtzatuY+e4=", - "aarch64-linux": "sha256-j6kTzWlSPWwoyvOR6nJAx8VpikMsy/U3ZAJGI2PxPE0=", - "aarch64-darwin": "sha256-H4XA72cYxaBBpT6wYAAAOhW4gHduUTxiqT/cNCjW5zc=", - "x86_64-darwin": "sha256-4pljrneppSsxS5EYoXX0914L9z9jLHcKatm5T8L6/54=" + "x86_64-linux": "sha256-olTZ+tKugAY3LxizsJMlbK3TW78HZUoM03PigvQLP4A=", + "aarch64-linux": "sha256-xdKDeqMEnYM2+vGySfb8pbcYyo/xMmgxG/ZhPCKaZEg=", + "aarch64-darwin": "sha256-fihCTrHIiUG+py4vuqdr+YshqSKm2/B5onY50b97sPM=", + "x86_64-darwin": "sha256-inlQQPNAOdkmKK6HQAMI2bG/ZFlfwmUQu9a6vm6Q0jQ=" } } diff --git a/packages/app/package.json b/packages/app/package.json index b4a15cec08..e10c23eeba 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/app", - "version": "1.1.34", + "version": "1.1.35", "description": "", "type": "module", "exports": { diff --git a/packages/app/src/components/dialog-select-model.tsx b/packages/app/src/components/dialog-select-model.tsx index 5569d7780f..2ee1d9db1a 100644 --- a/packages/app/src/components/dialog-select-model.tsx +++ b/packages/app/src/components/dialog-select-model.tsx @@ -1,5 +1,6 @@ import { Popover as Kobalte } from "@kobalte/core/popover" -import { Component, ComponentProps, createMemo, createSignal, JSX, Show, ValidComponent } from "solid-js" +import { Component, ComponentProps, createEffect, createMemo, JSX, onCleanup, Show, ValidComponent } from "solid-js" +import { createStore } from "solid-js/store" import { useLocal } from "@/context/local" import { useDialog } from "@opencode-ai/ui/context/dialog" import { popularProviders } from "@/hooks/use-providers" @@ -92,26 +93,118 @@ export function ModelSelectorPopover(props: { triggerAs?: T triggerProps?: ComponentProps }) { - const [open, setOpen] = createSignal(false) + const [store, setStore] = createStore<{ + open: boolean + dismiss: "escape" | "outside" | null + trigger?: HTMLElement + content?: HTMLElement + }>({ + open: false, + dismiss: null, + trigger: undefined, + content: undefined, + }) const dialog = useDialog() const handleManage = () => { - setOpen(false) + setStore("open", false) dialog.show(() => ) } const language = useLanguage() + createEffect(() => { + if (!store.open) return + + const inside = (node: Node | null | undefined) => { + if (!node) return false + const el = store.content + if (el && el.contains(node)) return true + const anchor = store.trigger + if (anchor && anchor.contains(node)) return true + return false + } + + const onKeyDown = (event: KeyboardEvent) => { + if (event.key !== "Escape") return + setStore("dismiss", "escape") + setStore("open", false) + event.preventDefault() + event.stopPropagation() + } + + const onPointerDown = (event: PointerEvent) => { + const target = event.target + if (!(target instanceof Node)) return + if (inside(target)) return + setStore("dismiss", "outside") + setStore("open", false) + } + + const onFocusIn = (event: FocusEvent) => { + if (!store.content) return + const target = event.target + if (!(target instanceof Node)) return + if (inside(target)) return + setStore("dismiss", "outside") + setStore("open", false) + } + + window.addEventListener("keydown", onKeyDown, true) + window.addEventListener("pointerdown", onPointerDown, true) + window.addEventListener("focusin", onFocusIn, true) + + onCleanup(() => { + window.removeEventListener("keydown", onKeyDown, true) + window.removeEventListener("pointerdown", onPointerDown, true) + window.removeEventListener("focusin", onFocusIn, true) + }) + }) + return ( - - + { + if (next) setStore("dismiss", null) + setStore("open", next) + }} + modal={false} + placement="top-start" + gutter={8} + > + setStore("trigger", el)} + as={props.triggerAs ?? "div"} + {...(props.triggerProps as any)} + > {props.children} - + setStore("content", el)} + class="w-72 h-80 flex flex-col rounded-md border border-border-base bg-surface-raised-stronger-non-alpha shadow-md z-50 outline-none overflow-hidden" + onEscapeKeyDown={(event) => { + setStore("dismiss", "escape") + setStore("open", false) + event.preventDefault() + event.stopPropagation() + }} + onPointerDownOutside={() => { + setStore("dismiss", "outside") + setStore("open", false) + }} + onFocusOutside={() => { + setStore("dismiss", "outside") + setStore("open", false) + }} + onCloseAutoFocus={(event) => { + if (store.dismiss === "outside") event.preventDefault() + setStore("dismiss", null) + }} + > {language.t("dialog.model.select.title")} setOpen(false)} + onSelect={() => setStore("open", false)} class="p-1" action={ function AddRow(props: AddRowProps) { return ( -
-
-
+
+
+
{ + // Position relative to input-wrapper + requestAnimationFrame(() => { + const wrapper = el.parentElement?.querySelector('[data-slot="input-wrapper"]') + if (wrapper instanceof HTMLElement) { + wrapper.style.position = "relative" + wrapper.appendChild(el) + } + }) + }} + />
@@ -134,7 +147,14 @@ export function DialogSelectServer() { status: undefined as boolean | undefined, }, }) - const [defaultUrl, defaultUrlActions] = createResource(() => platform.getDefaultServerUrl?.()) + const [defaultUrl, defaultUrlActions] = createResource( + async () => { + const url = await platform.getDefaultServerUrl?.() + if (!url) return null + return normalizeServerUrl(url) ?? null + }, + { initialValue: null }, + ) const isDesktop = platform.platform === "desktop" const looksComplete = (value: string) => { @@ -344,17 +364,23 @@ export function DialogSelectServer() { return ( -
+
x} onSelect={(x) => { if (x) select(x) }} + onFilter={(value) => { + if (value && store.addServer.showForm && !store.addServer.adding) { + resetAdd() + } + }} divider={true} - class="[&_[data-slot=list-scroll]]:max-h-[300px] [&_[data-slot=list-scroll]]:overflow-y-auto [&_[data-slot=list-items]]:bg-surface-raised-base [&_[data-slot=list-items]]:rounded-md [&_[data-slot=list-item]]:py-3" + class="px-5 [&_[data-slot=list-search-wrapper]]:w-full [&_[data-slot=list-scroll]]:max-h-[300px] [&_[data-slot=list-scroll]]:overflow-y-auto [&_[data-slot=list-items]]:bg-surface-raised-base [&_[data-slot=list-items]]:rounded-md [&_[data-slot=list-item]]:h-14 [&_[data-slot=list-item]]:p-3 [&_[data-slot=list-item]]:!bg-transparent [&_[data-slot=list-item-add]]:px-0" add={ store.addServer.showForm ? { @@ -375,7 +401,35 @@ export function DialogSelectServer() { } > {(i) => { - const [popoverOpen, setPopoverOpen] = createSignal(false) + const [truncated, setTruncated] = createSignal(false) + let nameRef: HTMLSpanElement | undefined + let versionRef: HTMLSpanElement | undefined + + const check = () => { + const nameTruncated = nameRef ? nameRef.scrollWidth > nameRef.clientWidth : false + const versionTruncated = versionRef ? versionRef.scrollWidth > versionRef.clientWidth : false + setTruncated(nameTruncated || versionTruncated) + } + + createEffect(() => { + check() + window.addEventListener("resize", check) + onCleanup(() => window.removeEventListener("resize", check)) + }) + + const tooltipValue = () => { + const name = serverDisplayName(i) + const version = store.status[i]?.version + return ( + + {name} + + {version} + + + ) + } + return (
} > -
+
- {serverDisplayName(i)} - - {store.status[i]?.version} - - - - {language.t("dialog.server.status.default")} + class="flex items-center gap-3 px-4 min-w-0 flex-1" + classList={{ "opacity-50": store.status[i]?.healthy === false }} + > +
+ + {serverDisplayName(i)} - -
+ + + {store.status[i]?.version} + + + + + {language.t("dialog.server.status.default")} + + +
+
-
+

{language.t("dialog.server.current")}

-
e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}> - event.stopPropagation()} - /> - } - class="w-max !min-w-fit !max-w-none" - > -
- + {language.t("dialog.server.menu.edit")} + - + + {language.t("dialog.server.menu.default")} + + - + + {language.t("dialog.server.menu.defaultRemove")} + + -
- -
- -
+ {language.t("dialog.server.menu.delete")} + + + +
@@ -508,7 +546,7 @@ export function DialogSelectServer() { }} -
+
diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 3eddbb303e..074a035829 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -184,6 +184,7 @@ export const PromptInput: Component = (props) => { if (!item.commentID) return comments.setFocus({ file: item.path, id: item.commentID }) + comments.setActive({ file: item.path, id: item.commentID }) view().reviewPanel.open() if (item.commentOrigin === "review") { @@ -1711,15 +1712,19 @@ export const PromptInput: Component = (props) => {
{(item) => { + const active = () => { + const a = comments.active() + return !!item.commentID && item.commentID === a?.id && item.path === a?.file + } return ( - {getDirectory(item.path)} + {getDirectory(item.path)} {getFilename(item.path)} @@ -1729,8 +1734,11 @@ export const PromptInput: Component = (props) => { >
{ openComment(item) diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index 4b7f9f4ad1..808fbb1124 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -163,7 +163,10 @@ export function SessionHeader() { ? language.t("session.share.popover.description.shared") : language.t("session.share.popover.description.unshared") } - gutter={8} + gutter={6} + placement="bottom-end" + shift={-64} + class="rounded-xl [&_[data-slot=popover-close-button]]:hidden" triggerAs={Button} triggerProps={{ variant: "secondary", @@ -192,8 +195,8 @@ export function SessionHeader() {
} > -
- +
+
-