From 504a5994737cc0b4ecec92446251702c5930e6d6 Mon Sep 17 00:00:00 2001 From: opencode Date: Fri, 31 Oct 2025 06:12:16 +0000 Subject: [PATCH 01/14] release: v0.15.30 --- bun.lock | 22 +++++++++++----------- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/sdk/js/src/gen/types.gen.ts | 6 ++++++ packages/slack/package.json | 2 +- packages/ui/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 15 files changed, 30 insertions(+), 24 deletions(-) diff --git a/bun.lock b/bun.lock index d1f6cfd21..02968de7f 100644 --- a/bun.lock +++ b/bun.lock @@ -37,7 +37,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "0.15.29", + "version": "0.15.30", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -64,7 +64,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "0.15.29", + "version": "0.15.30", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -88,7 +88,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "0.15.29", + "version": "0.15.30", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -109,7 +109,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "0.15.29", + "version": "0.15.30", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -148,7 +148,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "0.15.29", + "version": "0.15.30", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -164,7 +164,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "0.15.29", + "version": "0.15.30", "bin": { "opencode": "./bin/opencode", }, @@ -232,7 +232,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "0.15.29", + "version": "0.15.30", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -252,7 +252,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "0.15.29", + "version": "0.15.30", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -263,7 +263,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "0.15.29", + "version": "0.15.30", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -276,7 +276,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "0.15.29", + "version": "0.15.30", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -306,7 +306,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "0.15.29", + "version": "0.15.30", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/console/app/package.json b/packages/console/app/package.json index ec70d7c4d..ad0b2f4db 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -7,7 +7,7 @@ "dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev", "build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json", "start": "vinxi start", - "version": "0.15.29" + "version": "0.15.30" }, "dependencies": { "@ibm/plex": "6.4.1", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 36e37c3bf..21a82227b 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "0.15.29", + "version": "0.15.30", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 6f13c9481..a7b3a9bd7 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "0.15.29", + "version": "0.15.30", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index b01703b4d..5f7b34578 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "0.15.29", + "version": "0.15.30", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 32fe27b83..e34910bba 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "0.15.29", + "version": "0.15.30", "description": "", "type": "module", "scripts": { diff --git a/packages/function/package.json b/packages/function/package.json index eb1858092..e42ba6b71 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "0.15.29", + "version": "0.15.30", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index d8ffc7510..ac903ef0b 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.15.29", + "version": "0.15.30", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 53b90e0c4..41707d924 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.15.29", + "version": "0.15.30", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 3c00bafcb..29ace13ac 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.15.29", + "version": "0.15.30", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index 0ff1f032f..df110326b 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -430,6 +430,9 @@ export type Config = { options?: { [key: string]: unknown } + headers?: { + [key: string]: string + } provider?: { npm: string } @@ -991,6 +994,9 @@ export type Model = { options: { [key: string]: unknown } + headers?: { + [key: string]: string + } provider?: { npm: string } diff --git a/packages/slack/package.json b/packages/slack/package.json index 28df57736..e9e3a638b 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "0.15.29", + "version": "0.15.30", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/ui/package.json b/packages/ui/package.json index 91ab3867e..7fcb26fb4 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "0.15.29", + "version": "0.15.30", "type": "module", "exports": { ".": "./src/components/index.ts", diff --git a/packages/web/package.json b/packages/web/package.json index 797bf3a62..59bad65b7 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "0.15.29", + "version": "0.15.30", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index c65cb039b..9f845c2bc 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.15.29", + "version": "0.15.30", "publisher": "sst-dev", "repository": { "type": "git", From dafb63cfb33bce8d184d23501348d381cf4af531 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 31 Oct 2025 12:04:46 +0000 Subject: [PATCH 02/14] ignore: update download stats 2025-10-31 --- STATS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/STATS.md b/STATS.md index aedfa87ce..d2b518c99 100644 --- a/STATS.md +++ b/STATS.md @@ -125,3 +125,4 @@ | 2025-10-29 | 606,259 (+10,483) | 542,064 (+9,626) | 1,148,323 (+20,109) | | 2025-10-30 | 613,746 (+7,487) | 542,064 (+0) | 1,155,810 (+7,487) | | 2025-10-30 | 617,846 (+4,100) | 555,026 (+12,962) | 1,172,872 (+17,062) | +| 2025-10-31 | 626,612 (+8,766) | 564,579 (+9,553) | 1,191,191 (+18,319) | From 543eee78a666c32139e87fcfe97a110ebdd9d0bc Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 31 Oct 2025 06:36:26 -0500 Subject: [PATCH 03/14] wip: desktop work --- packages/desktop/index.html | 2 +- packages/desktop/public/favicon.svg | 1 + packages/desktop/src/assets/favicon.svg | 5 ----- .../src/assets/favicon/apple-touch-icon.png | Bin 0 -> 1520 bytes .../ui/src/assets/favicon/favicon-96x96.png | Bin 0 -> 681 bytes packages/ui/src/assets/favicon/favicon.ico | Bin 0 -> 15086 bytes packages/ui/src/assets/favicon/favicon.svg | 18 +++++++++++++++ .../ui/src/assets/favicon/site.webmanifest | 21 ++++++++++++++++++ .../favicon/web-app-manifest-192x192.png | Bin 0 -> 1667 bytes .../favicon/web-app-manifest-512x512.png | Bin 0 -> 7299 bytes packages/ui/src/components/favicon.tsx | 19 ++++++++++++++++ packages/ui/src/components/index.ts | 1 + 12 files changed, 61 insertions(+), 6 deletions(-) create mode 120000 packages/desktop/public/favicon.svg delete mode 100644 packages/desktop/src/assets/favicon.svg create mode 100644 packages/ui/src/assets/favicon/apple-touch-icon.png create mode 100644 packages/ui/src/assets/favicon/favicon-96x96.png create mode 100644 packages/ui/src/assets/favicon/favicon.ico create mode 100644 packages/ui/src/assets/favicon/favicon.svg create mode 100644 packages/ui/src/assets/favicon/site.webmanifest create mode 100644 packages/ui/src/assets/favicon/web-app-manifest-192x192.png create mode 100644 packages/ui/src/assets/favicon/web-app-manifest-512x512.png create mode 100644 packages/ui/src/components/favicon.tsx diff --git a/packages/desktop/index.html b/packages/desktop/index.html index 9209acfc7..e88b49ac8 100644 --- a/packages/desktop/index.html +++ b/packages/desktop/index.html @@ -4,7 +4,7 @@ - + OpenCode diff --git a/packages/desktop/public/favicon.svg b/packages/desktop/public/favicon.svg new file mode 120000 index 000000000..80804d257 --- /dev/null +++ b/packages/desktop/public/favicon.svg @@ -0,0 +1 @@ +../../ui/src/assets/favicon/favicon.svg \ No newline at end of file diff --git a/packages/desktop/src/assets/favicon.svg b/packages/desktop/src/assets/favicon.svg deleted file mode 100644 index 3c81bbdb4..000000000 --- a/packages/desktop/src/assets/favicon.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/packages/ui/src/assets/favicon/apple-touch-icon.png b/packages/ui/src/assets/favicon/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..13d79dbec4fa8f8b2a4adb3611419f1d42f8de3b GIT binary patch literal 1520 zcma)6X;4#F7=1|tyin7IV5o>_44WG0P?rP{Bs@Wa0%B1RX%K|s0z|Q4OF@D@2p|w( zkf;^(DK2d>SP_B&4Tv&`g}j0*2q^-R4zg$?jDSgRr!)OkX8PmaJLfLnJ>U73yr4i| zis@2Q2!benJnyf;dh7228-v-S(|9ce8C~}C_S%_Bc+4ajfFgIU;^j#DV(x(V*M)$wh?oVGa{mL{G#OmOr51< zXRe_#gxMCIQ(MIN;Z^g&SrEp$)zG%cXPO}_VHPGbxmt4*l@H*>D`|5vW9afuXqY6J zU&V_)2MpXF?5#=3gQ;#i!Y&TjbG3a1)jV%8p4UNp&n6(6*ccxs5e?i~eoaAkx7Uq- zyv~gbC87g#MQgo%afntXlMP;-ndDnJr+(X5ml{5PK16$6{G7ufAfpr8XUzzRRNPQu zUp%rJt6gt5$|u1YU)Z0#2`HCe8E!fWVUzb_c32S6uI{pPDS#aRwA|_4mox(6duVgH zgiM`{LfN^1Pf2g_NE8u0wgjUv%^1Qec1+so<|~Njxh`Y)S6i<3OtYgjK#cz~S0b<{ zAZk)X!=Fk*Okk=SL1?G>xa-L06^EyTmBByzSG_L{-h|6`K~Rj?I?65_P)e zYxL##IQ^QD7_Xa+_oQHbte{UJOfk`;Z=^AF@&@br4Fu$H!1D!RmQD76h&{&vbaF0- zEPv93!mP?8(qfQMfnqQo73`kX+f1e~v6<-cDr=Z3GHEY*E`=6?qz`j5nG%(L zK3XwWQX5fQ^h%+!)1gGQTD>_Zjwzb+MFllnCkT~4**|v>?vKdY1Ht^C)zdvB3uOie za%6C|MtC|+24O?VHZO=oGy>{$NF-C;1e{W*e-3Z1&7v!8botH+N$vz>g(i!{0`4vZ zTk1#yo&Q7cX!CZKFkaqm4Ps}KmGXv7g6~j58<+n#{78CTZL3?h=V^`uS35-~yGS5x z6u-#^gd{b*W>*sIAt|pL1iaIIo)JYHkf&dn&HM;(QpPLac9m{~n zxe#V`sz2)SEyUBqgJMXZd)+H;Vf9tNyGnOs9l5Qr<7yK=^k(b_C1{yiHMAynrSL(B zmD*d|HWIq50;h*3xSL~Evb~8yBs9M6tv_1nsA;Vx!4-9`w;g~`w3CuBphetwN4JA0 z+gf~HOJfM1z8( z1`P^)xbXz8MF802`1@HAjn`a iZ=BJCzHR(>!~O(vi~oJNgi*E+>_C1#f!=Z?^7vmtu%`_G literal 0 HcmV?d00001 diff --git a/packages/ui/src/assets/favicon/favicon-96x96.png b/packages/ui/src/assets/favicon/favicon-96x96.png new file mode 100644 index 0000000000000000000000000000000000000000..c9d3cbf0eefae129e0966185139cac21578e7ecb GIT binary patch literal 681 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7Ro>V2bl}aSW+od^6eFkJ(Vf)jg!c zRUq)vW;HQJ?ZiOAkj6H(*$S~IMDiY=|NOOdlJO+d*1z|*p1-&|nrq+Lw9kLO{HREo zY8)|BbU|zv%M_DGKGKXVTMA!?wKn8w+?G;Y&>Ov7i>1W*<~0t++&jBM6&FaK+UCZQ z@%Y+WF2=h7+e~-)GUl#P3FVlOpqt;-P!u+4GC}5HR+;Ik)m*hR3)hxU4zRoY<;cS` z>oQ)K@4tU;hs$}}+hq$HHhXoG^S@2YFH@;9fj+1jFwK1YFRoMpWjke zaQE2L&tUi*v53XMYKmmar|3eC3PT_(EX z2HV_*L+F?x{xU}ttqowFdcgYF)&sSIAAN)_VHb6g%qo2dWqkc$ObQMxRXXh9F zzAVqY))6dyA^((3@Sk!aXV6cU4_^uT$ub1}Wcl!w z=A)nW9?w%qcz^Ts@!;xh`uY34beEscBLCUUjgNO%5B|ZwrU9o9pVeaP;Pd6}>g$lJ zmV?XBXSLWm__Vw~#koeb@OEi9yp-#l)&}NF-qVU12Mp_bTFvIh>8$l6V{l69f$_nA z9rY*gbI4v=8AyzFEmia{edX;D=lL-{_=o-`pZD?kD}VpR`ZM44x82^oPm-it1`-qM z^UwYC)CaZ#r^I0VFzXNa7cI$efn^X>f8IS@P3xMk%Kb;{r>YO*m%sm2{VFyDKt)teB5>BC~B+KWrDe&wJGxD#kX2JZ`U`sDx}W! zSrni5-4F3b{$5^ti2@6Md@p($pBq<}I^X<-h>xPpt`5uilg}nD=TYmf$=T65%ejkl V))F(?#>G7}h%9*gAQFtX{RSG~hcN&E literal 0 HcmV?d00001 diff --git a/packages/ui/src/assets/favicon/favicon.svg b/packages/ui/src/assets/favicon/favicon.svg new file mode 100644 index 000000000..ffb5f7298 --- /dev/null +++ b/packages/ui/src/assets/favicon/favicon.svg @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/packages/ui/src/assets/favicon/site.webmanifest b/packages/ui/src/assets/favicon/site.webmanifest new file mode 100644 index 000000000..f7522f8f3 --- /dev/null +++ b/packages/ui/src/assets/favicon/site.webmanifest @@ -0,0 +1,21 @@ +{ + "name": "OpenCode", + "short_name": "OpenCode", + "icons": [ + { + "src": "/web-app-manifest-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/web-app-manifest-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} \ No newline at end of file diff --git a/packages/ui/src/assets/favicon/web-app-manifest-192x192.png b/packages/ui/src/assets/favicon/web-app-manifest-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..ca53fa8daa61b3dbf9807186aca2b2be12a944ee GIT binary patch literal 1667 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 literal 0 HcmV?d00001 diff --git a/packages/ui/src/components/favicon.tsx b/packages/ui/src/components/favicon.tsx new file mode 100644 index 000000000..de32ed2af --- /dev/null +++ b/packages/ui/src/components/favicon.tsx @@ -0,0 +1,19 @@ +import { Link, Meta } from "@solidjs/meta" +import favicon96 from "../assets/favicon/favicon-96x96.png" +import faviconSvg from "../assets/favicon/favicon.svg" +import faviconIco from "../assets/favicon/favicon.ico" +import appleTouchIcon from "../assets/favicon/apple-touch-icon.png" +import siteWebmanifest from "../assets/favicon/site.webmanifest" + +export const Favicon = () => { + return ( + <> + + + + + + + + ) +} diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index 8d6ddc89c..115e5f14f 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -9,6 +9,7 @@ export * from "./diff-changes" export * from "./icon" export * from "./icon-button" export * from "./input" +export * from "./favicon" export * from "./fonts" export * from "./list" export * from "./markdown" From 485135cf5c4af64d1449aa3cadcdd0aef92201a3 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 31 Oct 2025 07:30:02 -0500 Subject: [PATCH 04/14] wip: desktop work --- packages/ui/src/components/message-part.css | 15 ++++++++++----- packages/ui/src/components/message-part.tsx | 12 +++++++----- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/ui/src/components/message-part.css b/packages/ui/src/components/message-part.css index fa251a2b3..dcfadeff6 100644 --- a/packages/ui/src/components/message-part.css +++ b/packages/ui/src/components/message-part.css @@ -29,20 +29,21 @@ [data-component="tool-error"] { display: flex; - align-items: center; + align-items: start; gap: 8px; - [data-slot="icon"] { + [data-slot="tool-error-icon"] { color: var(--icon-critical-active); + margin-top: 4px; } - [data-slot="content"] { + [data-slot="tool-error-content"] { display: flex; - align-items: center; + align-items: start; gap: 8px; } - [data-slot="title"] { + [data-slot="tool-error-title"] { font-family: var(--font-family-sans); font-size: var(--font-size-small); font-style: normal; @@ -51,7 +52,11 @@ letter-spacing: var(--letter-spacing-normal); color: var(--ember-light-11); text-transform: capitalize; + white-space: nowrap; } + + /* [data-slot="tool-error-message"] { */ + /* } */ } [data-component="tool-output"] { diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index 1aaab751a..272a716a4 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -151,15 +151,17 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) { return (
- + -
-
{title}
- {rest.join(": ")} +
+
{title}
+ {rest.join(": ")}
- {cleaned} + + {cleaned} +
From 0ac943de909fb5a685a608d7894248cdd3a9e129 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 31 Oct 2025 09:45:57 -0500 Subject: [PATCH 05/14] wip: desktop work --- packages/desktop/src/pages/index.tsx | 15 ++++--- packages/ui/src/components/index.ts | 1 + packages/ui/src/components/typewriter.css | 14 ++++++ packages/ui/src/components/typewriter.tsx | 54 +++++++++++++++++++++++ packages/ui/src/styles/index.css | 1 + 5 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 packages/ui/src/components/typewriter.css create mode 100644 packages/ui/src/components/typewriter.tsx diff --git a/packages/desktop/src/pages/index.tsx b/packages/desktop/src/pages/index.tsx index 5237d78bb..01fe4b508 100644 --- a/packages/desktop/src/pages/index.tsx +++ b/packages/desktop/src/pages/index.tsx @@ -13,6 +13,7 @@ import { DiffChanges, ProgressCircle, Message, + Typewriter, } from "@opencode-ai/ui" import { FileIcon } from "@/ui" import FileTree from "@/components/file-tree" @@ -544,7 +545,6 @@ export default function Page() { {(message) => { const diffs = createMemo(() => message.summary?.diffs ?? []) - return (
  • {(message) => { + const [initialized, setInitialized] = createSignal(!!message.summary?.title) const [expanded, setExpanded] = createSignal(false) const parts = createMemo(() => sync.data.part[message.id]) - const prompt = createMemo(() => local.session.getMessageText(message)) const title = createMemo(() => message.summary?.title) const summary = createMemo(() => message.summary?.body) const assistantMessages = createMemo(() => { @@ -581,6 +581,9 @@ export default function Page() { ) as AssistantMessageType[] }) const working = createMemo(() => !summary()) + createEffect(() => { + setTimeout(() => setInitialized(!!title()), 10_000) + }) return (
    {/* Title */}
    -

    - {title() ?? prompt()} -

    +
    + }> +

    {title()}

    +
    +
    diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index 115e5f14f..499f2e27f 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -20,6 +20,7 @@ export * from "./select-dialog" export * from "./tabs" export * from "./basic-tool" export * from "./tooltip" +export * from "./typewriter" export * from "../context/helper" export * from "../context/shiki" diff --git a/packages/ui/src/components/typewriter.css b/packages/ui/src/components/typewriter.css new file mode 100644 index 000000000..e978312a9 --- /dev/null +++ b/packages/ui/src/components/typewriter.css @@ -0,0 +1,14 @@ +@keyframes blink { + 0%, + 50% { + opacity: 1; + } + 51%, + 100% { + opacity: 0; + } +} + +.blinking-cursor { + animation: blink 1s step-end infinite; +} diff --git a/packages/ui/src/components/typewriter.tsx b/packages/ui/src/components/typewriter.tsx new file mode 100644 index 000000000..9adb267ad --- /dev/null +++ b/packages/ui/src/components/typewriter.tsx @@ -0,0 +1,54 @@ +import { createEffect, Show, type ValidComponent } from "solid-js" +import { createStore } from "solid-js/store" +import { Dynamic } from "solid-js/web" + +export const Typewriter = (props: { + text?: string + class?: string + as?: T +}) => { + const [store, setStore] = createStore({ + typing: false, + displayed: "", + cursor: true, + }) + + createEffect(() => { + const text = props.text + if (!text) return + + let i = 0 + setStore("typing", true) + setStore("displayed", "") + setStore("cursor", true) + + const getTypingDelay = () => { + const random = Math.random() + if (random < 0.05) return 150 + Math.random() * 100 + if (random < 0.15) return 80 + Math.random() * 60 + return 30 + Math.random() * 50 + } + + const type = () => { + if (i < text.length) { + setStore("displayed", text.slice(0, i + 1)) + i++ + setTimeout(type, getTypingDelay()) + } else { + setStore("typing", false) + setTimeout(() => setStore("cursor", false), 2000) + } + } + + setTimeout(type, 200) + }) + + return ( + + {store.displayed} + + + + + ) +} diff --git a/packages/ui/src/styles/index.css b/packages/ui/src/styles/index.css index cea5a082d..146d957e2 100644 --- a/packages/ui/src/styles/index.css +++ b/packages/ui/src/styles/index.css @@ -25,5 +25,6 @@ @import "../components/select-dialog.css" layer(components); @import "../components/tabs.css" layer(components); @import "../components/tooltip.css" layer(components); +@import "../components/typewriter.css" layer(components); @import "./utilities.css" layer(utilities); From a8bf1ad40f9aa48c8207d92ce3fee794f9d38279 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 31 Oct 2025 10:08:24 -0500 Subject: [PATCH 06/14] wip: desktop work --- packages/desktop/src/context/sync.tsx | 5 +- packages/desktop/src/pages/index.tsx | 56 +++++++++++---------- packages/ui/script/colors.txt | 39 +++++++------- packages/ui/src/components/message-part.css | 10 ++-- packages/ui/src/components/message-part.tsx | 2 +- packages/ui/src/styles/tailwind/colors.css | 13 +++-- packages/ui/src/styles/theme.css | 34 +++++++------ 7 files changed, 90 insertions(+), 69 deletions(-) diff --git a/packages/desktop/src/context/sync.tsx b/packages/desktop/src/context/sync.tsx index ce2fa1ea4..c60206b0b 100644 --- a/packages/desktop/src/context/sync.tsx +++ b/packages/desktop/src/context/sync.tsx @@ -124,7 +124,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ const absolute = (path: string) => (store.path.directory + "/" + path).replace("//", "/") const sanitizePart = (part: Part) => { if (part.type === "tool") { - if (part.state.status === "completed") { + if (part.state.status === "completed" || part.state.status === "error") { for (const key in part.state.metadata) { if (typeof part.state.metadata[key] === "string") { part.state.metadata[key] = sanitize(part.state.metadata[key] as string) @@ -135,6 +135,9 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ part.state.input[key] = sanitize(part.state.input[key] as string) } } + if ("error" in part.state) { + part.state.error = sanitize(part.state.error as string) + } } } return part diff --git a/packages/desktop/src/pages/index.tsx b/packages/desktop/src/pages/index.tsx index 01fe4b508..5b2f4ecc1 100644 --- a/packages/desktop/src/pages/index.tsx +++ b/packages/desktop/src/pages/index.tsx @@ -53,10 +53,11 @@ export default function Page() { const [activeItem, setActiveItem] = createSignal(undefined) createEffect(() => { - if (!local.session.activeMessage()) return - if (!messageScrollElement) return - const element = messageScrollElement.querySelector(`[data-message="${local.session.activeMessage()?.id}"]`) - element?.scrollIntoView({ block: "start", behavior: "instant" }) + // Set first message as active if none selected + const userMessages = local.session.userMessages() + if (userMessages.length > 0 && !local.session.activeMessage()) { + local.session.setActiveMessage(userMessages[0].id) + } }) const MOD = typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform) ? "Meta" : "Control" @@ -567,28 +568,29 @@ export default function Page() {
    -
    - - {(message) => { - const [initialized, setInitialized] = createSignal(!!message.summary?.title) - const [expanded, setExpanded] = createSignal(false) - const parts = createMemo(() => sync.data.part[message.id]) - const title = createMemo(() => message.summary?.title) - const summary = createMemo(() => message.summary?.body) - const assistantMessages = createMemo(() => { - return sync.data.message[activeSession().id]?.filter( - (m) => m.role === "assistant" && m.parentID == message.id, - ) as AssistantMessageType[] - }) - const working = createMemo(() => !summary()) - createEffect(() => { - setTimeout(() => setInitialized(!!title()), 10_000) - }) + + {(message) => { + const isActive = createMemo(() => local.session.activeMessage()?.id === message.id) + const [initialized, setInitialized] = createSignal(!!message.summary?.title) + const [expanded, setExpanded] = createSignal(false) + const parts = createMemo(() => sync.data.part[message.id]) + const title = createMemo(() => message.summary?.title) + const summary = createMemo(() => message.summary?.body) + const assistantMessages = createMemo(() => { + return sync.data.message[activeSession().id]?.filter( + (m) => m.role === "assistant" && m.parentID == message.id, + ) as AssistantMessageType[] + }) + const working = createMemo(() => !summary()) + createEffect(() => { + setTimeout(() => setInitialized(!!title()), 10_000) + }) - return ( + return ( +
    {/* Title */}
    @@ -774,10 +776,10 @@ export default function Page() {
    - ) - }} -
    -
    + + ) + }} +
    diff --git a/packages/ui/script/colors.txt b/packages/ui/script/colors.txt index b022e8a11..5b5cfa73a 100644 --- a/packages/ui/script/colors.txt +++ b/packages/ui/script/colors.txt @@ -2,8 +2,8 @@ --background-weak: var(--smoke-light-3); --background-strong: var(--smoke-light-1); --background-stronger: #FCFCFC; ---base: var(--smoke-light-alpha-2); --surface-base: var(--smoke-light-alpha-2); +--base: var(--smoke-light-alpha-2); --surface-base-hover: #0500000F; --surface-base-active: var(--smoke-light-alpha-3); --surface-base-interactive-active: var(--cobalt-light-alpha-3); @@ -17,6 +17,7 @@ --surface-float-base: var(--smoke-dark-1); --surface-float-base-hover: var(--smoke-dark-2); --surface-raised-base-hover: var(--smoke-light-alpha-2); +--surface-raised-base-active: var(--smoke-light-alpha-3); --surface-raised-strong: var(--smoke-light-1); --surface-raised-strong-hover: var(--white); --surface-raised-stronger: var(--white); @@ -43,8 +44,8 @@ --surface-info-base: var(--lilac-light-3); --surface-info-weak: var(--lilac-light-2); --surface-info-strong: var(--lilac-light-9); ---surface-diff-hidden-base: var(--blue-light-3); --surface-diff-skip-base: var(--smoke-light-2); +--surface-diff-hidden-base: var(--blue-light-3); --surface-diff-unchanged-base: #FFFFFF00; --surface-diff-hidden-weak: var(--blue-light-2); --surface-diff-hidden-weaker: var(--blue-light-1); @@ -74,19 +75,22 @@ --text-on-brand-base: var(--smoke-light-alpha-11); --text-on-interactive-base: var(--smoke-light-1); --text-on-interactive-weak: var(--smoke-dark-alpha-11); ---text-on-success-base: var(--smoke-dark-alpha-11); +--text-on-success-base: var(--apple-light-10); +--text-on-critical-base: var(--ember-light-10); +--text-on-critical-weak: var(--ember-light-8); +--text-on-critical-strong: var(--ember-light-12); --text-on-warning-base: var(--smoke-dark-alpha-11); --text-on-info-base: var(--smoke-dark-alpha-11); --text-diff-add-base: var(--mint-light-11); ---text-diff-delete-base: var(--ember-light-11); +--text-diff-delete-base: var(--ember-light-10); --text-diff-delete-strong: var(--ember-light-12); --text-diff-add-strong: var(--mint-light-12); --text-on-info-weak: var(--smoke-dark-alpha-9); --text-on-info-strong: var(--smoke-dark-alpha-12); --text-on-warning-weak: var(--smoke-dark-alpha-9); --text-on-warning-strong: var(--smoke-dark-alpha-12); ---text-on-success-weak: var(--smoke-dark-alpha-9); ---text-on-success-strong: var(--smoke-dark-alpha-12); +--text-on-success-weak: var(--apple-light-6); +--text-on-success-strong: var(--apple-light-12); --text-on-brand-weak: var(--smoke-light-alpha-9); --text-on-brand-weaker: var(--smoke-light-alpha-8); --text-on-brand-strong: var(--smoke-light-alpha-12); @@ -134,19 +138,19 @@ --icon-selected: var(--smoke-light-12); --icon-disabled: var(--smoke-light-8); --icon-focus: var(--smoke-light-12); ---icon-weak-base: var(--smoke-light-7); --icon-invert-base: #FFFFFF; +--icon-weak-base: var(--smoke-light-7); --icon-weak-hover: var(--smoke-light-8); --icon-weak-active: var(--smoke-light-9); --icon-weak-selected: var(--smoke-light-10); --icon-weak-disabled: var(--smoke-light-6); --icon-weak-focus: var(--smoke-light-9); --icon-strong-base: var(--smoke-light-12); ---icon-strong-hover: var(--smoke-light-12); ---icon-strong-active: var(--smoke-light-12); ---icon-strong-selected: var(--smoke-light-12); +--icon-strong-hover: #151313; +--icon-strong-active: #020202; +--icon-strong-selected: #020202; --icon-strong-disabled: var(--smoke-light-8); ---icon-strong-focus: var(--smoke-light-12); +--icon-strong-focus: #020202; --icon-brand-base: var(--smoke-light-12); --icon-interactive-base: var(--cobalt-light-9); --icon-success-base: var(--apple-light-7); @@ -155,9 +159,9 @@ --icon-warning-base: var(--amber-light-7); --icon-warning-hover: var(--amber-light-8); --icon-warning-active: var(--amber-light-11); ---icon-critical-base: var(--ember-light-7); ---icon-critical-hover: var(--ember-light-8); ---icon-critical-active: var(--ember-light-11); +--icon-critical-base: var(--ember-light-10); +--icon-critical-hover: var(--ember-light-11); +--icon-critical-active: var(--ember-light-12); --icon-info-base: var(--lilac-light-7); --icon-info-hover: var(--lilac-light-8); --icon-info-active: var(--lilac-light-11); @@ -184,9 +188,8 @@ --icon-diff-add-base: var(--mint-light-11); --icon-diff-add-hover: var(--mint-light-12); --icon-diff-add-active: var(--mint-light-12); ---icon-diff-delete-base: var(--ember-light-9); ---icon-diff-delete-hover: var(--ember-light-10); ---icon-diff-delete-active: var(--ember-light-11); +--icon-diff-delete-base: var(--ember-light-10); +--icon-diff-delete-hover: var(--ember-light-11); --syntax-comment: #8A8A8A; --syntax-string: #D68C27; --syntax-keyword: #3B7DD8; @@ -222,3 +225,5 @@ --border-weaker-selected: var(--cobalt-light-alpha-4); --border-weaker-disabled: var(--smoke-light-alpha-2); --border-weaker-focus: var(--smoke-light-alpha-6); +--button-ghost-hover: var(--smoke-light-alpha-2); +--button-ghost-hover2: var(--smoke-light-alpha-3); diff --git a/packages/ui/src/components/message-part.css b/packages/ui/src/components/message-part.css index dcfadeff6..ab839481d 100644 --- a/packages/ui/src/components/message-part.css +++ b/packages/ui/src/components/message-part.css @@ -33,7 +33,7 @@ gap: 8px; [data-slot="tool-error-icon"] { - color: var(--icon-critical-active); + color: var(--icon-critical-base); margin-top: 4px; } @@ -50,13 +50,13 @@ font-weight: var(--font-weight-medium); line-height: var(--line-height-large); letter-spacing: var(--letter-spacing-normal); - color: var(--ember-light-11); - text-transform: capitalize; + color: var(--text-on-critical-base); white-space: nowrap; } - /* [data-slot="tool-error-message"] { */ - /* } */ + [data-slot="tool-error-message"] { + color: var(--text-on-critical-weak); + } } [data-component="tool-output"] { diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index 272a716a4..01a58025a 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -153,7 +153,7 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) {
    - +
    {title}
    {rest.join(": ")} diff --git a/packages/ui/src/styles/tailwind/colors.css b/packages/ui/src/styles/tailwind/colors.css index e2f6788ab..527c43109 100644 --- a/packages/ui/src/styles/tailwind/colors.css +++ b/packages/ui/src/styles/tailwind/colors.css @@ -7,8 +7,8 @@ --color-background-weak: var(--background-weak); --color-background-strong: var(--background-strong); --color-background-stronger: var(--background-stronger); - --color-base: var(--base); --color-surface-base: var(--surface-base); + --color-base: var(--base); --color-surface-base-hover: var(--surface-base-hover); --color-surface-base-active: var(--surface-base-active); --color-surface-base-interactive-active: var(--surface-base-interactive-active); @@ -22,6 +22,7 @@ --color-surface-float-base: var(--surface-float-base); --color-surface-float-base-hover: var(--surface-float-base-hover); --color-surface-raised-base-hover: var(--surface-raised-base-hover); + --color-surface-raised-base-active: var(--surface-raised-base-active); --color-surface-raised-strong: var(--surface-raised-strong); --color-surface-raised-strong-hover: var(--surface-raised-strong-hover); --color-surface-raised-stronger: var(--surface-raised-stronger); @@ -48,8 +49,8 @@ --color-surface-info-base: var(--surface-info-base); --color-surface-info-weak: var(--surface-info-weak); --color-surface-info-strong: var(--surface-info-strong); - --color-surface-diff-hidden-base: var(--surface-diff-hidden-base); --color-surface-diff-skip-base: var(--surface-diff-skip-base); + --color-surface-diff-hidden-base: var(--surface-diff-hidden-base); --color-surface-diff-unchanged-base: var(--surface-diff-unchanged-base); --color-surface-diff-hidden-weak: var(--surface-diff-hidden-weak); --color-surface-diff-hidden-weaker: var(--surface-diff-hidden-weaker); @@ -80,6 +81,9 @@ --color-text-on-interactive-base: var(--text-on-interactive-base); --color-text-on-interactive-weak: var(--text-on-interactive-weak); --color-text-on-success-base: var(--text-on-success-base); + --color-text-on-critical-base: var(--text-on-critical-base); + --color-text-on-critical-weak: var(--text-on-critical-weak); + --color-text-on-critical-strong: var(--text-on-critical-strong); --color-text-on-warning-base: var(--text-on-warning-base); --color-text-on-info-base: var(--text-on-info-base); --color-text-diff-add-base: var(--text-diff-add-base); @@ -139,8 +143,8 @@ --color-icon-selected: var(--icon-selected); --color-icon-disabled: var(--icon-disabled); --color-icon-focus: var(--icon-focus); - --color-icon-weak-base: var(--icon-weak-base); --color-icon-invert-base: var(--icon-invert-base); + --color-icon-weak-base: var(--icon-weak-base); --color-icon-weak-hover: var(--icon-weak-hover); --color-icon-weak-active: var(--icon-weak-active); --color-icon-weak-selected: var(--icon-weak-selected); @@ -191,7 +195,6 @@ --color-icon-diff-add-active: var(--icon-diff-add-active); --color-icon-diff-delete-base: var(--icon-diff-delete-base); --color-icon-diff-delete-hover: var(--icon-diff-delete-hover); - --color-icon-diff-delete-active: var(--icon-diff-delete-active); --color-syntax-comment: var(--syntax-comment); --color-syntax-string: var(--syntax-string); --color-syntax-keyword: var(--syntax-keyword); @@ -227,4 +230,6 @@ --color-border-weaker-selected: var(--border-weaker-selected); --color-border-weaker-disabled: var(--border-weaker-disabled); --color-border-weaker-focus: var(--border-weaker-focus); + --color-button-ghost-hover: var(--button-ghost-hover); + --color-button-ghost-hover2: var(--button-ghost-hover2); } \ No newline at end of file diff --git a/packages/ui/src/styles/theme.css b/packages/ui/src/styles/theme.css index c401dcae9..2458a599b 100644 --- a/packages/ui/src/styles/theme.css +++ b/packages/ui/src/styles/theme.css @@ -114,9 +114,9 @@ --surface-info-base: var(--lilac-light-3); --surface-info-weak: var(--lilac-light-2); --surface-info-strong: var(--lilac-light-9); - --surface-diff-unchanged-base: #ffffff00; --surface-diff-skip-base: var(--smoke-light-2); --surface-diff-hidden-base: var(--blue-light-3); + --surface-diff-unchanged-base: #ffffff00; --surface-diff-hidden-weak: var(--blue-light-2); --surface-diff-hidden-weaker: var(--blue-light-1); --surface-diff-hidden-strong: var(--blue-light-5); @@ -145,7 +145,10 @@ --text-on-brand-base: var(--smoke-light-alpha-11); --text-on-interactive-base: var(--smoke-light-1); --text-on-interactive-weak: var(--smoke-dark-alpha-11); - --text-on-success-base: var(--smoke-dark-alpha-11); + --text-on-success-base: var(--apple-light-10); + --text-on-critical-base: var(--ember-light-10); + --text-on-critical-weak: var(--ember-light-8); + --text-on-critical-strong: var(--ember-light-12); --text-on-warning-base: var(--smoke-dark-alpha-11); --text-on-info-base: var(--smoke-dark-alpha-11); --text-diff-add-base: var(--mint-light-11); @@ -156,8 +159,8 @@ --text-on-info-strong: var(--smoke-dark-alpha-12); --text-on-warning-weak: var(--smoke-dark-alpha-9); --text-on-warning-strong: var(--smoke-dark-alpha-12); - --text-on-success-weak: var(--smoke-dark-alpha-9); - --text-on-success-strong: var(--smoke-dark-alpha-12); + --text-on-success-weak: var(--apple-light-6); + --text-on-success-strong: var(--apple-light-12); --text-on-brand-weak: var(--smoke-light-alpha-9); --text-on-brand-weaker: var(--smoke-light-alpha-8); --text-on-brand-strong: var(--smoke-light-alpha-12); @@ -226,9 +229,9 @@ --icon-warning-base: var(--amber-light-7); --icon-warning-hover: var(--amber-light-8); --icon-warning-active: var(--amber-light-11); - --icon-critical-base: var(--ember-light-7); - --icon-critical-hover: var(--ember-light-8); - --icon-critical-active: var(--ember-light-11); + --icon-critical-base: var(--ember-light-10); + --icon-critical-hover: var(--ember-light-11); + --icon-critical-active: var(--ember-light-12); --icon-info-base: var(--lilac-light-7); --icon-info-hover: var(--lilac-light-8); --icon-info-active: var(--lilac-light-11); @@ -346,9 +349,9 @@ --surface-info-base: var(--lilac-light-3); --surface-info-weak: var(--lilac-light-2); --surface-info-strong: var(--lilac-light-9); - --surface-diff-unchanged-base: var(--smoke-dark-1); --surface-diff-skip-base: var(--smoke-dark-alpha-1); --surface-diff-hidden-base: var(--blue-dark-2); + --surface-diff-unchanged-base: var(--smoke-dark-1); --surface-diff-hidden-weak: var(--blue-dark-1); --surface-diff-hidden-weaker: var(--blue-dark-3); --surface-diff-hidden-strong: var(--blue-dark-5); @@ -377,7 +380,10 @@ --text-on-brand-base: var(--smoke-dark-alpha-11); --text-on-interactive-base: var(--smoke-dark-12); --text-on-interactive-weak: var(--smoke-dark-alpha-11); - --text-on-success-base: var(--smoke-dark-alpha-11); + --text-on-success-base: var(--apple-dark-9); + --text-on-critical-base: var(--ember-dark-9); + --text-on-critical-weak: var(--ember-dark-8); + --text-on-critical-strong: var(--ember-dark-12); --text-on-warning-base: var(--smoke-dark-alpha-11); --text-on-info-base: var(--smoke-dark-alpha-11); --text-diff-add-base: var(--mint-dark-11); @@ -388,8 +394,8 @@ --text-on-info-strong: var(--smoke-dark-alpha-12); --text-on-warning-weak: var(--smoke-dark-alpha-9); --text-on-warning-strong: var(--smoke-dark-alpha-12); - --text-on-success-weak: var(--smoke-dark-alpha-9); - --text-on-success-strong: var(--smoke-dark-alpha-12); + --text-on-success-weak: var(--apple-dark-8); + --text-on-success-strong: var(--apple-dark-12); --text-on-brand-weak: var(--smoke-dark-alpha-9); --text-on-brand-weaker: var(--smoke-dark-alpha-8); --text-on-brand-strong: var(--smoke-dark-alpha-12); @@ -458,9 +464,9 @@ --icon-warning-base: var(--amber-dark-7); --icon-warning-hover: var(--amber-dark-8); --icon-warning-active: var(--amber-dark-11); - --icon-critical-base: var(--ember-dark-7); - --icon-critical-hover: var(--ember-dark-8); - --icon-critical-active: var(--ember-dark-11); + --icon-critical-base: var(--ember-dark-9); + --icon-critical-hover: var(--ember-dark-11); + --icon-critical-active: var(--ember-dark-12); --icon-info-base: var(--lilac-dark-7); --icon-info-hover: var(--lilac-dark-8); --icon-info-active: var(--lilac-dark-11); From 46d675b98046df5c10e3f642aefbce9f1f204c81 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Fri, 31 Oct 2025 10:48:51 -0500 Subject: [PATCH 07/14] tweak: filter out deprecated models --- packages/opencode/src/provider/models.ts | 2 +- packages/opencode/src/provider/provider.ts | 77 +++++++++++++++++----- 2 files changed, 60 insertions(+), 19 deletions(-) diff --git a/packages/opencode/src/provider/models.ts b/packages/opencode/src/provider/models.ts index 34640706a..57555c544 100644 --- a/packages/opencode/src/provider/models.ts +++ b/packages/opencode/src/provider/models.ts @@ -35,7 +35,7 @@ export namespace ModelsDev { }) .optional(), experimental: z.boolean().optional(), - status: z.enum(["alpha", "beta"]).optional(), + status: z.enum(["alpha", "beta", "deprecated"]).optional(), options: z.record(z.string(), z.any()), headers: z.record(z.string(), z.string()).optional(), provider: z.object({ npm: z.string() }).optional(), diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 9bcff4b48..730ba3963 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -78,12 +78,18 @@ export namespace Provider { } }, "amazon-bedrock": async () => { - if (!process.env["AWS_PROFILE"] && !process.env["AWS_ACCESS_KEY_ID"] && !process.env["AWS_BEARER_TOKEN_BEDROCK"]) + if ( + !process.env["AWS_PROFILE"] && + !process.env["AWS_ACCESS_KEY_ID"] && + !process.env["AWS_BEARER_TOKEN_BEDROCK"] + ) return { autoload: false } const region = process.env["AWS_REGION"] ?? "us-east-1" - const { fromNodeProviderChain } = await import(await BunProc.install("@aws-sdk/credential-providers")) + const { fromNodeProviderChain } = await import( + await BunProc.install("@aws-sdk/credential-providers") + ) return { autoload: true, options: { @@ -118,9 +124,13 @@ export namespace Provider { "eu-south-1", "eu-south-2", ].some((r) => region.includes(r)) - const modelRequiresPrefix = ["claude", "nova-lite", "nova-micro", "llama3", "pixtral"].some((m) => - modelID.includes(m), - ) + const modelRequiresPrefix = [ + "claude", + "nova-lite", + "nova-micro", + "llama3", + "pixtral", + ].some((m) => modelID.includes(m)) if (regionRequiresPrefix && modelRequiresPrefix) { modelID = `${regionPrefix}.${modelID}` } @@ -130,13 +140,15 @@ export namespace Provider { const isAustraliaRegion = ["ap-southeast-2", "ap-southeast-4"].includes(region) if ( isAustraliaRegion && - ["anthropic.claude-sonnet-4-5", "anthropic.claude-haiku"].some((m) => modelID.includes(m)) + ["anthropic.claude-sonnet-4-5", "anthropic.claude-haiku"].some((m) => + modelID.includes(m), + ) ) { regionPrefix = "au" modelID = `${regionPrefix}.${modelID}` } else { - const modelRequiresPrefix = ["claude", "nova-lite", "nova-micro", "nova-pro"].some((m) => - modelID.includes(m), + const modelRequiresPrefix = ["claude", "nova-lite", "nova-micro", "nova-pro"].some( + (m) => modelID.includes(m), ) if (modelRequiresPrefix) { regionPrefix = "apac" @@ -174,8 +186,12 @@ export namespace Provider { } }, "google-vertex": async () => { - const project = process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCP_PROJECT"] ?? process.env["GCLOUD_PROJECT"] - const location = process.env["GOOGLE_CLOUD_LOCATION"] ?? process.env["VERTEX_LOCATION"] ?? "us-east5" + const project = + process.env["GOOGLE_CLOUD_PROJECT"] ?? + process.env["GCP_PROJECT"] ?? + process.env["GCLOUD_PROJECT"] + const location = + process.env["GOOGLE_CLOUD_LOCATION"] ?? process.env["VERTEX_LOCATION"] ?? "us-east5" const autoload = Boolean(project) if (!autoload) return { autoload: false } return { @@ -191,8 +207,12 @@ export namespace Provider { } }, "google-vertex-anthropic": async () => { - const project = process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCP_PROJECT"] ?? process.env["GCLOUD_PROJECT"] - const location = process.env["GOOGLE_CLOUD_LOCATION"] ?? process.env["VERTEX_LOCATION"] ?? "us-east5" + const project = + process.env["GOOGLE_CLOUD_PROJECT"] ?? + process.env["GCP_PROJECT"] ?? + process.env["GCLOUD_PROJECT"] + const location = + process.env["GOOGLE_CLOUD_LOCATION"] ?? process.env["VERTEX_LOCATION"] ?? "us-east5" const autoload = Boolean(project) if (!autoload) return { autoload: false } return { @@ -223,7 +243,13 @@ export namespace Provider { } = {} const models = new Map< string, - { providerID: string; modelID: string; info: ModelsDev.Model; language: LanguageModel; npm?: string } + { + providerID: string + modelID: string + info: ModelsDev.Model + language: LanguageModel + npm?: string + } >() const sdk = new Map() // Maps `${provider}/${key}` to the provider’s actual model ID for custom aliases. @@ -355,7 +381,10 @@ export namespace Provider { const auth = await Auth.get(providerID) if (!auth) continue if (!plugin.auth.loader) continue - const options = await plugin.auth.loader(() => Auth.get(providerID) as any, database[plugin.auth.provider]) + const options = await plugin.auth.loader( + () => Auth.get(providerID) as any, + database[plugin.auth.provider], + ) mergeProvider(plugin.auth.provider, options ?? {}, "custom") } @@ -370,12 +399,15 @@ export namespace Provider { // Filter out blacklisted models .filter( ([modelID]) => - modelID !== "gpt-5-chat-latest" && !(providerID === "openrouter" && modelID === "openai/gpt-5-chat"), + modelID !== "gpt-5-chat-latest" && + !(providerID === "openrouter" && modelID === "openai/gpt-5-chat"), ) // Filter out experimental models .filter( ([, model]) => - (!model.experimental && model.status !== "alpha") || Flag.OPENCODE_ENABLE_EXPERIMENTAL_MODELS, + ((!model.experimental && model.status !== "alpha") || + Flag.OPENCODE_ENABLE_EXPERIMENTAL_MODELS) && + model.status !== "deprecated", ), ) provider.info.models = filteredModels @@ -421,7 +453,9 @@ export namespace Provider { // In addition, Bun's dynamic import logic does not support subpath imports, // so we patch the import path to load directly from `dist`. const modPath = - provider.id === "google-vertex-anthropic" ? `${installedPath}/dist/anthropic/index.mjs` : installedPath + provider.id === "google-vertex-anthropic" + ? `${installedPath}/dist/anthropic/index.mjs` + : installedPath const mod = await import(modPath) if (options["timeout"] !== undefined && options["timeout"] !== null) { // Only override fetch if user explicitly sets timeout @@ -518,7 +552,14 @@ export namespace Provider { const provider = await state().then((state) => state.providers[providerID]) if (!provider) return - let priority = ["claude-haiku-4-5", "claude-haiku-4.5", "3-5-haiku", "3.5-haiku", "gemini-2.5-flash", "gpt-5-nano"] + let priority = [ + "claude-haiku-4-5", + "claude-haiku-4.5", + "3-5-haiku", + "3.5-haiku", + "gemini-2.5-flash", + "gpt-5-nano", + ] // claude-haiku-4.5 is considered a premium model in github copilot, we shouldn't use premium requests for title gen if (providerID === "github-copilot") { priority = priority.filter((m) => m !== "claude-haiku-4.5") From b628c580c28a5352f56844dd4762ebf6df973c73 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Fri, 31 Oct 2025 10:49:52 -0500 Subject: [PATCH 08/14] update types --- packages/sdk/js/src/gen/types.gen.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index df110326b..ae492c9af 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -426,7 +426,7 @@ export type Config = { output: Array<"text" | "audio" | "image" | "video" | "pdf"> } experimental?: boolean - status?: "alpha" | "beta" + status?: "alpha" | "beta" | "deprecated" options?: { [key: string]: unknown } @@ -990,7 +990,7 @@ export type Model = { output: Array<"text" | "audio" | "image" | "video" | "pdf"> } experimental?: boolean - status?: "alpha" | "beta" + status?: "alpha" | "beta" | "deprecated" options: { [key: string]: unknown } From a5bcb76bbf34cff3b4d382c4fb634c78577f6121 Mon Sep 17 00:00:00 2001 From: David Hill Date: Fri, 31 Oct 2025 16:03:33 +0000 Subject: [PATCH 09/14] Added border as shadow and updated button --- packages/ui/src/components/button.css | 4 ++-- packages/ui/src/styles/theme.css | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/components/button.css b/packages/ui/src/components/button.css index 0db3fcf37..d052b7fd1 100644 --- a/packages/ui/src/components/button.css +++ b/packages/ui/src/components/button.css @@ -28,10 +28,10 @@ } &[data-variant="secondary"] { - border-color: var(--border-weak-base); + border-color: transparent; background-color: var(--button-secondary-base); color: var(--text-strong); - box-shadow: var(--shadow-xs); + box-shadow: var(--shadow-xs-border); &:hover:not(:disabled) { border-color: var(--border-hover); diff --git a/packages/ui/src/styles/theme.css b/packages/ui/src/styles/theme.css index 2458a599b..6df46b593 100644 --- a/packages/ui/src/styles/theme.css +++ b/packages/ui/src/styles/theme.css @@ -55,6 +55,11 @@ --shadow-md: 0 6px 8px -4px rgba(19, 16, 16, 0.12), 0 4px 3px -2px rgba(19, 16, 16, 0.12), 0 1px 2px -1px rgba(19, 16, 16, 0.12); + --shadow-xs-border: + 0 0 0 1px var(--border-base, rgba(11, 6, 0, 0.20)), + 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); --shadow-xs-border-selected: 0 0 0 3px var(--border-weak-selected, rgba(1, 103, 255, 0.29)), 0 0 0 1px var(--border-selected, rgba(0, 74, 255, 0.99)), 0 1px 2px -1px rgba(19, 16, 16, 0.25), From 36b48a44ac1f0c9593a4abdf1d21980a2bfaee22 Mon Sep 17 00:00:00 2001 From: Steven Martin <111119082+S-A-Martin@users.noreply.github.com> Date: Fri, 31 Oct 2025 16:41:44 +0000 Subject: [PATCH 10/14] tweak - normalise unix-like identifiers to support git bash for windows (#2100) Co-authored-by: Steven Martin --- install | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/install b/install index ed3171c62..42ae846ef 100755 --- a/install +++ b/install @@ -10,10 +10,14 @@ NC='\033[0m' # No Color requested_version=${VERSION:-} -os=$(uname -s | tr '[:upper:]' '[:lower:]') -if [[ "$os" == "darwin" ]]; then - os="darwin" -fi +raw_os=$(uname -s) +os=$(echo "$raw_os" | tr '[:upper:]' '[:lower:]') +# Normalize various Unix-like identifiers +case "$raw_os" in + Darwin*) os="darwin" ;; + Linux*) os="linux" ;; + MINGW*|MSYS*|CYGWIN*) os="windows" ;; + esac arch=$(uname -m) if [[ "$arch" == "aarch64" ]]; then From ffc889b99e61c6f21ce68985ee398c3031a5b19b Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 31 Oct 2025 11:54:27 -0500 Subject: [PATCH 11/14] wip: desktop work --- .../src/components/message-progress.tsx | 82 +++++++++++ packages/desktop/src/components/spinner.tsx | 39 ++++++ packages/desktop/src/pages/index.tsx | 130 ++++-------------- packages/ui/src/components/diff-changes.tsx | 10 -- packages/ui/src/components/markdown.css | 7 + packages/ui/src/styles/animations.css | 13 ++ packages/ui/src/styles/index.css | 1 + packages/ui/src/styles/tailwind/index.css | 2 + packages/ui/src/styles/utilities.css | 65 --------- 9 files changed, 174 insertions(+), 175 deletions(-) create mode 100644 packages/desktop/src/components/message-progress.tsx create mode 100644 packages/desktop/src/components/spinner.tsx create mode 100644 packages/ui/src/styles/animations.css diff --git a/packages/desktop/src/components/message-progress.tsx b/packages/desktop/src/components/message-progress.tsx new file mode 100644 index 000000000..f77e196b5 --- /dev/null +++ b/packages/desktop/src/components/message-progress.tsx @@ -0,0 +1,82 @@ +import { For, Match, Switch, createEffect, createMemo, createSignal, onCleanup } from "solid-js" +import { Part } from "@opencode-ai/ui" +import { useSync } from "@/context/sync" +import type { AssistantMessage as AssistantMessageType } from "@opencode-ai/sdk" + +export function MessageProgress(props: { assistantMessages: () => AssistantMessageType[] }) { + const sync = useSync() + const items = createMemo(() => props.assistantMessages().flatMap((m) => sync.data.part[m.id])) + + const finishedItems = createMemo(() => [ + "", + "", + "Loading...", + ...items().filter( + (p) => + p?.type === "text" || + (p?.type === "reasoning" && p.time?.end) || + (p?.type === "tool" && p.state.status === "completed"), + ), + "", + ]) + + const MINIMUM_DELAY = 400 + const [visibleCount, setVisibleCount] = createSignal(1) + + createEffect(() => { + const total = finishedItems().length + if (total > visibleCount()) { + const timer = setTimeout(() => { + setVisibleCount((prev) => prev + 1) + }, MINIMUM_DELAY) + onCleanup(() => clearTimeout(timer)) + } else if (total < visibleCount()) { + setVisibleCount(total) + } + }) + + const translateY = createMemo(() => { + const total = visibleCount() + if (total < 2) return "0px" + return `-${(total - 2) * 40 - 8}px` + }) + + return ( +
    +
    + + {(part) => { + if (typeof part === "string") return
    {part}
    + const message = createMemo(() => sync.data.message[part.sessionID].find((m) => m.id === part.messageID)) + return ( +
    + + + {(p) => ( +
    + )} + + + {(p) => } + + {(p) => } + +
    + ) + }} + +
    +
    + ) +} diff --git a/packages/desktop/src/components/spinner.tsx b/packages/desktop/src/components/spinner.tsx new file mode 100644 index 000000000..5fc4cda64 --- /dev/null +++ b/packages/desktop/src/components/spinner.tsx @@ -0,0 +1,39 @@ +import { ComponentProps, For } from "solid-js" + +export function Spinner(props: { class?: string; classList?: ComponentProps<"div">["classList"] }) { + const squares = Array.from({ length: 16 }, (_, i) => ({ + id: i, + x: (i % 4) * 4, + y: Math.floor(i / 4) * 4, + delay: Math.random() * 3, + duration: 2 + Math.random() * 2, + })) + + return ( + + + {(square) => ( + + )} + + + ) +} diff --git a/packages/desktop/src/pages/index.tsx b/packages/desktop/src/pages/index.tsx index 5b2f4ecc1..3eea97b6c 100644 --- a/packages/desktop/src/pages/index.tsx +++ b/packages/desktop/src/pages/index.tsx @@ -17,6 +17,7 @@ import { } from "@opencode-ai/ui" import { FileIcon } from "@/ui" import FileTree from "@/components/file-tree" +import { MessageProgress } from "@/components/message-progress" import { For, onCleanup, onMount, Show, Match, Switch, createSignal, createEffect, createMemo } from "solid-js" import { useLocal, type LocalFile } from "@/context/local" import { createStore } from "solid-js/store" @@ -39,6 +40,7 @@ import { useSync } from "@/context/sync" import { useSDK } from "@/context/sdk" import { type AssistantMessage as AssistantMessageType } from "@opencode-ai/sdk" import { Markdown } from "@opencode-ai/ui" +import { Spinner } from "@/components/spinner" export default function Page() { const local = useLocal() @@ -546,21 +548,31 @@ export default function Page() { {(message) => { const diffs = createMemo(() => message.summary?.diffs ?? []) + const working = createMemo(() => !message.summary?.title) return ( -
  • local.session.setActiveMessage(message.id)} - > - -
    +
    + + + + + + + + +
    + {message.summary?.title ?? local.session.getMessageText(message)} +
    +
  • ) }} @@ -600,19 +612,15 @@ export default function Page() {
    - -
    - -
    -
    +
    + +
    {/* Summary */}

    Summary

    - - - + {(summary) => }
    @@ -666,85 +674,7 @@ export default function Page() {
    - {(_) => { - const items = createMemo(() => - assistantMessages().flatMap((m) => sync.data.part[m.id]), - ) - const finishedItems = createMemo(() => - items().filter( - (p) => - (p?.type === "text" && p.time?.end) || - (p?.type === "reasoning" && p.time?.end) || - (p?.type === "tool" && p.state.status === "completed"), - ), - ) - - const MINIMUM_DELAY = 800 - const [visibleCount, setVisibleCount] = createSignal(1) - - createEffect(() => { - const total = finishedItems().length - if (total > visibleCount()) { - const timer = setTimeout(() => { - setVisibleCount((prev) => prev + 1) - }, MINIMUM_DELAY) - onCleanup(() => clearTimeout(timer)) - } else if (total < visibleCount()) { - setVisibleCount(total) - } - }) - - const translateY = createMemo(() => { - const total = visibleCount() - if (total < 2) return "0px" - return `-${(total - 2) * 48 - 8}px` - }) - - return ( -
    -
    -
    - - {(part) => { - const message = createMemo(() => - sync.data.message[part.sessionID].find( - (m) => m.id === part.messageID, - ), - ) - return ( -
    - - - {(p) => ( -
    - )} - - - {(p) => } - - - {(p) => } - - -
    - ) - }} - -
    -
    -
    - ) - }} + diff --git a/packages/ui/src/components/diff-changes.tsx b/packages/ui/src/components/diff-changes.tsx index 433c47f39..e6c04f519 100644 --- a/packages/ui/src/components/diff-changes.tsx +++ b/packages/ui/src/components/diff-changes.tsx @@ -16,16 +16,6 @@ export function DiffChanges(props: { diff: FileDiff | FileDiff[]; variant?: "def ) const total = createMemo(() => (additions() ?? 0) + (deletions() ?? 0)) - const countLines = (text: string) => { - if (!text) return 0 - return text.split("\n").length - } - - const totalBeforeLines = createMemo(() => { - if (!Array.isArray(props.diff)) return countLines(props.diff.before || "") - return props.diff.reduce((acc, diff) => acc + countLines(diff.before || ""), 0) - }) - const blockCounts = createMemo(() => { const TOTAL_BLOCKS = 5 diff --git a/packages/ui/src/components/markdown.css b/packages/ui/src/components/markdown.css index abc505a9e..6af0f550e 100644 --- a/packages/ui/src/components/markdown.css +++ b/packages/ui/src/components/markdown.css @@ -4,6 +4,7 @@ overflow: auto; scrollbar-width: none; color: var(--text-base); + text-wrap: pretty; /* text-14-regular */ font-family: var(--font-family-sans); @@ -34,4 +35,10 @@ margin-top: 16px; margin-bottom: 16px; } + + hr { + margin-top: 8px; + margin-bottom: 16px; + border-color: var(--border-weaker-base); + } } diff --git a/packages/ui/src/styles/animations.css b/packages/ui/src/styles/animations.css new file mode 100644 index 000000000..ba93e65e4 --- /dev/null +++ b/packages/ui/src/styles/animations.css @@ -0,0 +1,13 @@ +:root { + --animate-pulse: pulse-opacity 2s ease-in-out infinite; +} + +@keyframes pulse-opacity { + 0%, + 100% { + opacity: 0; + } + 50% { + opacity: 1; + } +} diff --git a/packages/ui/src/styles/index.css b/packages/ui/src/styles/index.css index 146d957e2..e3cffc6cc 100644 --- a/packages/ui/src/styles/index.css +++ b/packages/ui/src/styles/index.css @@ -28,3 +28,4 @@ @import "../components/typewriter.css" layer(components); @import "./utilities.css" layer(utilities); +@import "./animations.css" layer(utilities); diff --git a/packages/ui/src/styles/tailwind/index.css b/packages/ui/src/styles/tailwind/index.css index 76d8c7d3e..658809df4 100644 --- a/packages/ui/src/styles/tailwind/index.css +++ b/packages/ui/src/styles/tailwind/index.css @@ -64,6 +64,8 @@ --shadow-xs: var(--shadow-xs); --shadow-md: var(--shadow-md); --shadow-xs-border-selected: var(--shadow-xs-border-selected); + + --animate-pulse: var(--animate-pulse); } @import "./colors.css"; diff --git a/packages/ui/src/styles/utilities.css b/packages/ui/src/styles/utilities.css index 9c6b73f9c..99b7760a0 100644 --- a/packages/ui/src/styles/utilities.css +++ b/packages/ui/src/styles/utilities.css @@ -48,71 +48,6 @@ border-width: 0; } -.scroller { - /* --fade-height: 1.5rem; */ - /**/ - /* --mask-top: linear-gradient(to bottom, transparent, black var(--fade-height)); */ - /* --mask-bottom: linear-gradient(to top, transparent, black var(--fade-height)); */ - /**/ - /* mask-image: var(--mask-top), var(--mask-bottom); */ - /* mask-repeat: no-repeat; */ - /* mask-size: 100% var(--fade-height); */ - - animation: scroll-fade linear; - animation-timeline: scroll(self); -} - -/* Define the keyframes for the mask. - These percentages now map to scroll positions: - 0% = Scrolled to the top - 100% = Scrolled to the bottom -*/ -@keyframes scroll-fade { - /* At the very top (0% scroll) */ - 0% { - mask-image: linear-gradient( - to bottom, - black 90%, - /* Opaque, but start fade to bottom */ transparent 100% - ); - } - - /* A small amount scrolled (e.g., 5%) - This is where the top fade should be fully visible. - */ - 5% { - mask-image: linear-gradient( - to bottom, - transparent 0%, - black 10%, - /* Fade-in top */ black 90%, - /* Fade-out bottom */ transparent 100% - ); - } - - /* Nearing the bottom (e.g., 95%) - The bottom fade should start disappearing. - */ - 95% { - mask-image: linear-gradient( - to bottom, - transparent 0%, - black 10%, - /* Fade-in top */ black 90%, - /* Fade-out bottom */ transparent 100% - ); - } - - /* At the very bottom (100% scroll) */ - 100% { - mask-image: linear-gradient( - to bottom, - transparent 0%, - black 10% /* Opaque, but start fade from top */ - ); - } -} - .truncate-start { text-overflow: ellipsis; overflow: hidden; From 76e080b2cbe13b260324461eb2705d18fabfed35 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 31 Oct 2025 12:00:41 -0500 Subject: [PATCH 12/14] wip: desktop work --- packages/desktop/src/pages/index.tsx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/desktop/src/pages/index.tsx b/packages/desktop/src/pages/index.tsx index 3eea97b6c..2b723c55b 100644 --- a/packages/desktop/src/pages/index.tsx +++ b/packages/desktop/src/pages/index.tsx @@ -588,11 +588,17 @@ export default function Page() { const parts = createMemo(() => sync.data.part[message.id]) const title = createMemo(() => message.summary?.title) const summary = createMemo(() => message.summary?.body) + const diffs = createMemo(() => message.summary?.diffs ?? []) const assistantMessages = createMemo(() => { return sync.data.message[activeSession().id]?.filter( (m) => m.role === "assistant" && m.parentID == message.id, ) as AssistantMessageType[] }) + const hasToolPart = createMemo(() => + assistantMessages() + ?.flatMap((m) => sync.data.part[m.id]) + .some((p) => p.type === "tool"), + ) const working = createMemo(() => !summary()) createEffect(() => { setTimeout(() => setInitialized(!!title()), 10_000) @@ -619,11 +625,16 @@ export default function Page() {
    -

    Summary

    +

    + + Summary + Response + +

    {(summary) => }
    - + {(diff) => ( @@ -676,7 +687,7 @@ export default function Page() { - +
    From 16f5e16395544298a6638f6fc7831720a4ab878d Mon Sep 17 00:00:00 2001 From: David Hill Date: Fri, 31 Oct 2025 17:02:12 +0000 Subject: [PATCH 13/14] wip: Tweaking button styles --- packages/ui/src/components/button.css | 19 ++++++++++++------- packages/ui/src/styles/theme.css | 14 +++++++++++--- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/packages/ui/src/components/button.css b/packages/ui/src/components/button.css index d052b7fd1..112d648e6 100644 --- a/packages/ui/src/components/button.css +++ b/packages/ui/src/components/button.css @@ -7,6 +7,7 @@ border-radius: 6px; text-decoration: none; user-select: none; + outline: none; &[data-variant="primary"] { border-color: var(--border-base); @@ -28,22 +29,26 @@ } &[data-variant="secondary"] { - border-color: transparent; + border: transparent; background-color: var(--button-secondary-base); color: var(--text-strong); box-shadow: var(--shadow-xs-border); &:hover:not(:disabled) { - border-color: var(--border-hover); - background-color: var(--surface-hover); + background-color: var(--button-secondary-hover); } &:active:not(:disabled) { - border-color: var(--border-active); - background-color: var(--surface-active); + background-color: var(--button-secondary-base); } &:focus:not(:disabled) { - border-color: var(--border-focus); - background-color: var(--surface-focus); + background-color: var(--button-secondary-base); + } + &:focus-visible:not(:active) { + background-color: var(--button-secondary-base); + box-shadow: var(--shadow-xs-border-focus); + } + &:focus-visible:active { + box-shadow: none; } [data-slot="icon"] { diff --git a/packages/ui/src/styles/theme.css b/packages/ui/src/styles/theme.css index 6df46b593..20c43b10e 100644 --- a/packages/ui/src/styles/theme.css +++ b/packages/ui/src/styles/theme.css @@ -60,10 +60,18 @@ 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); - --shadow-xs-border-selected: + --shadow-xs-border-select: 0 0 0 3px var(--border-weak-selected, rgba(1, 103, 255, 0.29)), 0 0 0 1px var(--border-selected, rgba(0, 74, 255, 0.99)), 0 1px 2px -1px rgba(19, 16, 16, 0.25), 0 1px 2px 0 rgba(19, 16, 16, 0.08), 0 1px 3px 0 rgba(19, 16, 16, 0.12); + --shadow-xs-border-focus: + 0 0 0 1px var(--border-base, rgba(11, 6, 0, 0.20)), + 0 1px 2px -1px rgba(19, 16, 16, 0.25), + 0 1px 2px 0 rgba(19, 16, 16, 0.08), + 0 1px 3px 0 rgba(19, 16, 16, 0.12), + 0 0 0 2px var(--background-weak, #F1F0F0), + 0 0 0 3px var(--border-selected, rgba(0, 74, 255, 0.99)); + --text-mix-blend-mode: multiply; } @@ -170,7 +178,7 @@ --text-on-brand-weaker: var(--smoke-light-alpha-8); --text-on-brand-strong: var(--smoke-light-alpha-12); --button-secondary-base: #fdfcfc; - --button-secondary-base-hover: #faf9f9; + --button-secondary-hover: #fafafa; --border-base: var(--smoke-light-alpha-7); --border-hover: var(--smoke-light-alpha-8); --border-active: var(--smoke-light-alpha-9); @@ -405,7 +413,7 @@ --text-on-brand-weaker: var(--smoke-dark-alpha-8); --text-on-brand-strong: var(--smoke-dark-alpha-12); --button-secondary-base: var(--smoke-dark-4); - --button-secondary-base-hover: #2a2727; + --button-secondary-hover: #2A2727; --border-base: var(--smoke-dark-alpha-7); --border-hover: var(--smoke-dark-alpha-8); --border-active: var(--smoke-dark-alpha-9); From a529b0324d462967d4502555374aaac8b588113a Mon Sep 17 00:00:00 2001 From: David Hill Date: Fri, 31 Oct 2025 17:02:44 +0000 Subject: [PATCH 14/14] wip: Tweaking select styles This ghost style is placeholder for now until we style this properly with an icon and all the states. --- packages/desktop/src/components/prompt-input.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/desktop/src/components/prompt-input.tsx b/packages/desktop/src/components/prompt-input.tsx index f1c19388e..10487612b 100644 --- a/packages/desktop/src/components/prompt-input.tsx +++ b/packages/desktop/src/components/prompt-input.tsx @@ -365,6 +365,7 @@ export const PromptInput: Component = (props) => { current={local.agent.current().name} onSelect={local.agent.set} class="capitalize" + variant="ghost" />