diff options
| author | Dax Raad <[email protected]> | 2025-08-11 16:12:26 -0400 |
|---|---|---|
| committer | Dax Raad <[email protected]> | 2025-08-11 16:12:26 -0400 |
| commit | 0ce7d92a8b67c5660669b7850d758131d39b076d (patch) | |
| tree | 808c250f6a0bb970bd3021cb306389d1aceeec8d | |
| parent | 7a67fe7dded32e206890e14a887da5dd803caa93 (diff) | |
| download | opencode-0ce7d92a8b67c5660669b7850d758131d39b076d.tar.gz opencode-0ce7d92a8b67c5660669b7850d758131d39b076d.zip | |
ignore: fix share page
| -rw-r--r-- | bun.lock | 28 | ||||
| -rw-r--r-- | packages/opencode/src/provider/provider.ts | 2 | ||||
| -rw-r--r-- | packages/web/package.json | 2 | ||||
| -rw-r--r-- | packages/web/src/components/Share.tsx | 391 |
4 files changed, 220 insertions, 203 deletions
@@ -13,7 +13,7 @@ }, "cloud/core": { "name": "@opencode/cloud-core", - "version": "0.4.18", + "version": "0.4.19", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "drizzle-orm": "0.41.0", @@ -27,7 +27,7 @@ }, "cloud/function": { "name": "@opencode/cloud-function", - "version": "0.4.18", + "version": "0.4.19", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -47,7 +47,7 @@ }, "cloud/web": { "name": "@opencode/cloud-web", - "version": "0.4.18", + "version": "0.4.19", "dependencies": { "@kobalte/core": "0.13.9", "@openauthjs/solid": "0.0.0-20250322224806", @@ -66,7 +66,7 @@ }, "packages/function": { "name": "@opencode/function", - "version": "0.4.18", + "version": "0.4.19", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -81,7 +81,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "0.4.18", + "version": "0.4.19", "bin": { "opencode": "./bin/opencode", }, @@ -133,7 +133,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "0.4.18", + "version": "0.4.19", "dependencies": { "@opencode-ai/sdk": "workspace:*", }, @@ -145,7 +145,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "0.4.18", + "version": "0.4.19", "devDependencies": { "@hey-api/openapi-ts": "0.80.1", "@tsconfig/node22": "catalog:", @@ -154,9 +154,9 @@ }, "packages/web": { "name": "@opencode/web", - "version": "0.4.18", + "version": "0.4.19", "dependencies": { - "@astrojs/cloudflare": "^12.5.4", + "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", "@astrojs/solid-js": "5.1.0", "@astrojs/starlight": "0.34.3", @@ -229,11 +229,11 @@ "@apidevtools/json-schema-ref-parser": ["@apidevtools/[email protected]", "", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0" } }, "sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ=="], - "@astrojs/cloudflare": ["@astrojs/[email protected]", "", { "dependencies": { "@astrojs/internal-helpers": "0.6.1", "@astrojs/underscore-redirects": "1.0.0", "@cloudflare/workers-types": "^4.20250507.0", "tinyglobby": "^0.2.13", "vite": "^6.3.5", "wrangler": "^4.14.1" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-pQ8bokC59GEiXvyXpC4swBNoL7C/EknP+82KFzQwgR/Aeo5N1oPiAoPHgJbpPya/YF4E26WODdCQfBQDvLRfuw=="], + "@astrojs/cloudflare": ["@astrojs/[email protected]", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.1", "@astrojs/underscore-redirects": "1.0.0", "@cloudflare/workers-types": "^4.20250507.0", "tinyglobby": "^0.2.13", "vite": "^6.3.5", "wrangler": "^4.14.1" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-xhJptF5tU2k5eo70nIMyL1Udma0CqmUEnGSlGyFflLqSY82CRQI6nWZ/xZt0ZvmXuErUjIx0YYQNfZsz5CNjLQ=="], "@astrojs/compiler": ["@astrojs/[email protected]", "", {}, "sha512-w2zfvhjNCkNMmMMOn5b0J8+OmUaBL1o40ipMvqcG6NRpdC+lKxmTi48DT8Xw0SzJ3AfmeFLB45zXZXtmbsjcgw=="], - "@astrojs/internal-helpers": ["@astrojs/[email protected]", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], + "@astrojs/internal-helpers": ["@astrojs/[email protected]", "", {}, "sha512-7dwEVigz9vUWDw3nRwLQ/yH/xYovlUA0ZD86xoeKEBmkz9O6iELG1yri67PgAPW6VLL/xInA4t7H0CK6VmtkKQ=="], "@astrojs/markdown-remark": ["@astrojs/[email protected]", "", { "dependencies": { "@astrojs/internal-helpers": "0.6.1", "@astrojs/prism": "3.2.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.1.0", "js-yaml": "^4.1.0", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.1", "remark-smartypants": "^3.0.2", "shiki": "^3.0.0", "smol-toml": "^1.3.1", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1", "vfile": "^6.0.3" } }, "sha512-c5F5gGrkczUaTVgmMW9g1YMJGzOtRvjjhw6IfGuxarM6ct09MpwysP10US729dy07gg8y+ofVifezvP3BNsWZg=="], @@ -2569,6 +2569,8 @@ "@astrojs/cloudflare/vite": ["[email protected]", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], + "@astrojs/markdown-remark/@astrojs/internal-helpers": ["@astrojs/[email protected]", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], + "@astrojs/mdx/@astrojs/markdown-remark": ["@astrojs/[email protected]", "", { "dependencies": { "@astrojs/internal-helpers": "0.6.1", "@astrojs/prism": "3.3.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.1.0", "js-yaml": "^4.1.0", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "shiki": "^3.2.1", "smol-toml": "^1.3.4", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1", "vfile": "^6.0.3" } }, "sha512-DDRtD1sPvAuA7ms2btc9A7/7DApKqgLMNrE6kh5tmkfy8utD0Z738gqd3p5aViYYdUtHIyEJ1X4mCMxfCfu15w=="], "@astrojs/mdx/source-map": ["[email protected]", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], @@ -2757,6 +2759,8 @@ "anymatch/picomatch": ["[email protected]", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "astro/@astrojs/internal-helpers": ["@astrojs/[email protected]", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], + "astro/diff": ["[email protected]", "", {}, "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A=="], "astro/esbuild": ["[email protected]", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.8", "@esbuild/android-arm": "0.25.8", "@esbuild/android-arm64": "0.25.8", "@esbuild/android-x64": "0.25.8", "@esbuild/darwin-arm64": "0.25.8", "@esbuild/darwin-x64": "0.25.8", "@esbuild/freebsd-arm64": "0.25.8", "@esbuild/freebsd-x64": "0.25.8", "@esbuild/linux-arm": "0.25.8", "@esbuild/linux-arm64": "0.25.8", "@esbuild/linux-ia32": "0.25.8", "@esbuild/linux-loong64": "0.25.8", "@esbuild/linux-mips64el": "0.25.8", "@esbuild/linux-ppc64": "0.25.8", "@esbuild/linux-riscv64": "0.25.8", "@esbuild/linux-s390x": "0.25.8", "@esbuild/linux-x64": "0.25.8", "@esbuild/netbsd-arm64": "0.25.8", "@esbuild/netbsd-x64": "0.25.8", "@esbuild/openbsd-arm64": "0.25.8", "@esbuild/openbsd-x64": "0.25.8", "@esbuild/openharmony-arm64": "0.25.8", "@esbuild/sunos-x64": "0.25.8", "@esbuild/win32-arm64": "0.25.8", "@esbuild/win32-ia32": "0.25.8", "@esbuild/win32-x64": "0.25.8" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q=="], @@ -2933,6 +2937,8 @@ "@astrojs/cloudflare/vite/esbuild": ["[email protected]", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.8", "@esbuild/android-arm": "0.25.8", "@esbuild/android-arm64": "0.25.8", "@esbuild/android-x64": "0.25.8", "@esbuild/darwin-arm64": "0.25.8", "@esbuild/darwin-x64": "0.25.8", "@esbuild/freebsd-arm64": "0.25.8", "@esbuild/freebsd-x64": "0.25.8", "@esbuild/linux-arm": "0.25.8", "@esbuild/linux-arm64": "0.25.8", "@esbuild/linux-ia32": "0.25.8", "@esbuild/linux-loong64": "0.25.8", "@esbuild/linux-mips64el": "0.25.8", "@esbuild/linux-ppc64": "0.25.8", "@esbuild/linux-riscv64": "0.25.8", "@esbuild/linux-s390x": "0.25.8", "@esbuild/linux-x64": "0.25.8", "@esbuild/netbsd-arm64": "0.25.8", "@esbuild/netbsd-x64": "0.25.8", "@esbuild/openbsd-arm64": "0.25.8", "@esbuild/openbsd-x64": "0.25.8", "@esbuild/openharmony-arm64": "0.25.8", "@esbuild/sunos-x64": "0.25.8", "@esbuild/win32-arm64": "0.25.8", "@esbuild/win32-ia32": "0.25.8", "@esbuild/win32-x64": "0.25.8" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q=="], + "@astrojs/mdx/@astrojs/markdown-remark/@astrojs/internal-helpers": ["@astrojs/[email protected]", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], + "@astrojs/mdx/@astrojs/markdown-remark/@astrojs/prism": ["@astrojs/[email protected]", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ=="], "@astrojs/solid-js/vite/esbuild": ["[email protected]", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.8", "@esbuild/android-arm": "0.25.8", "@esbuild/android-arm64": "0.25.8", "@esbuild/android-x64": "0.25.8", "@esbuild/darwin-arm64": "0.25.8", "@esbuild/darwin-x64": "0.25.8", "@esbuild/freebsd-arm64": "0.25.8", "@esbuild/freebsd-x64": "0.25.8", "@esbuild/linux-arm": "0.25.8", "@esbuild/linux-arm64": "0.25.8", "@esbuild/linux-ia32": "0.25.8", "@esbuild/linux-loong64": "0.25.8", "@esbuild/linux-mips64el": "0.25.8", "@esbuild/linux-ppc64": "0.25.8", "@esbuild/linux-riscv64": "0.25.8", "@esbuild/linux-s390x": "0.25.8", "@esbuild/linux-x64": "0.25.8", "@esbuild/netbsd-arm64": "0.25.8", "@esbuild/netbsd-x64": "0.25.8", "@esbuild/openbsd-arm64": "0.25.8", "@esbuild/openbsd-x64": "0.25.8", "@esbuild/openharmony-arm64": "0.25.8", "@esbuild/sunos-x64": "0.25.8", "@esbuild/win32-arm64": "0.25.8", "@esbuild/win32-ia32": "0.25.8", "@esbuild/win32-x64": "0.25.8" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q=="], diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 184b938da..c72ae7ef3 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -448,7 +448,7 @@ export namespace Provider { } } - const priority = ["gemini-2.5-pro-preview", "codex-mini", "claude-sonnet-4"] + const priority = ["gemini-2.5-pro-preview", "gpt-5", "claude-sonnet-4"] export function sort(models: ModelsDev.Model[]) { return sortBy( models, diff --git a/packages/web/package.json b/packages/web/package.json index a60f38dba..d3c8baaab 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -11,7 +11,7 @@ "astro": "astro" }, "dependencies": { - "@astrojs/cloudflare": "^12.5.4", + "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", "@astrojs/solid-js": "5.1.0", "@astrojs/starlight": "0.34.3", diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx index 646ee7710..2b0e52c1a 100644 --- a/packages/web/src/components/Share.tsx +++ b/packages/web/src/components/Share.tsx @@ -1,7 +1,6 @@ import { For, Show, onMount, Suspense, onCleanup, createMemo, createSignal, SuspenseList, createEffect } from "solid-js" import { DateTime } from "luxon" import { createStore, reconcile, unwrap } from "solid-js/store" -import { mapValues } from "remeda" import { IconArrowDown } from "./icons" import { IconOpencode } from "./icons/custom" import styles from "./share.module.css" @@ -42,7 +41,6 @@ export default function Share(props: { id: string api: string info: Session.Info - messages: Record<string, MessageWithParts> }) { let lastScrollY = 0 let hasScrolledToAnchor = false @@ -50,7 +48,6 @@ export default function Share(props: { let scrollSentinel: HTMLElement | undefined let scrollObserver: IntersectionObserver | undefined - const id = props.id const params = new URLSearchParams(window.location.search) const debug = params.get("debug") === "true" @@ -61,17 +58,27 @@ export default function Share(props: { const [store, setStore] = createStore<{ info?: Session.Info messages: Record<string, MessageWithParts> - }>({ info: props.info, messages: {} }) + }>({ + info: { + id: props.id, + title: props.info.title, + version: props.info.version, + time: { + created: props.info.time.created, + updated: props.info.time.updated, + }, + }, messages: {} + }) const messages = createMemo(() => Object.values(store.messages).toSorted((a, b) => a.id?.localeCompare(b.id))) const [connectionStatus, setConnectionStatus] = createSignal<[Status, string?]>(["disconnected", "Disconnected"]) - // createEffect(() => { - // console.log(unwrap(store)) - // }) + createEffect(() => { + console.log(unwrap(store)) + }) onMount(() => { const apiUrl = props.api - if (!id) { + if (!props.id) { setConnectionStatus(["error", "id not found"]) return } @@ -96,7 +103,7 @@ export default function Share(props: { // Always use secure WebSocket protocol (wss) const wsBaseUrl = apiUrl.replace(/^https?:\/\//, "wss://") - const wsUrl = `${wsBaseUrl}/share_poll?id=${id}` + const wsUrl = `${wsBaseUrl}/share_poll?id=${props.id}` console.log("Connecting to WebSocket URL:", wsUrl) // Create WebSocket connection @@ -261,7 +268,9 @@ export default function Share(props: { }, } - result.created = props.info.time.created + if (!store.info) return result + + result.created = store.info.time.created const msgs = messages() for (let i = 0; i < msgs.length; i++) { @@ -290,197 +299,199 @@ export default function Share(props: { }) return ( - <main classList={{ [styles.root]: true, "not-content": true }}> - <div data-component="header"> - <h1 data-component="header-title">{store.info?.title}</h1> - <div data-component="header-details"> - <ul data-component="header-stats"> - <li title="opencode version" data-slot="item"> - <div data-slot="icon" title="opencode"> - <IconOpencode width={16} height={16} /> - </div> - <Show when={store.info?.version} fallback="v0.0.1"> - <span>v{store.info?.version}</span> - </Show> - </li> - {Object.values(data().models).length > 0 ? ( - <For each={Object.values(data().models)}> - {([provider, model]) => ( - <li data-slot="item"> - <div data-slot="icon" title={provider}> - <ProviderIcon model={model} /> - </div> - <span data-slot="model">{model}</span> - </li> - )} - </For> - ) : ( - <li> - <span data-element-label>Models</span> - <span data-placeholder>—</span> + <Show when={store.info}> + <main classList={{ [styles.root]: true, "not-content": true }}> + <div data-component="header"> + <h1 data-component="header-title">{store.info?.title}</h1> + <div data-component="header-details"> + <ul data-component="header-stats"> + <li title="opencode version" data-slot="item"> + <div data-slot="icon" title="opencode"> + <IconOpencode width={16} height={16} /> + </div> + <Show when={store.info?.version} fallback="v0.0.1"> + <span>v{store.info?.version}</span> + </Show> </li> - )} - </ul> - <div - data-component="header-time" - title={DateTime.fromMillis(data().created || 0).toLocaleString(DateTime.DATETIME_FULL_WITH_SECONDS)} - > - {DateTime.fromMillis(data().created || 0).toLocaleString(DateTime.DATETIME_MED)} + {Object.values(data().models).length > 0 ? ( + <For each={Object.values(data().models)}> + {([provider, model]) => ( + <li data-slot="item"> + <div data-slot="icon" title={provider}> + <ProviderIcon model={model} /> + </div> + <span data-slot="model">{model}</span> + </li> + )} + </For> + ) : ( + <li> + <span data-element-label>Models</span> + <span data-placeholder>—</span> + </li> + )} + </ul> + <div + data-component="header-time" + title={DateTime.fromMillis(data().created || 0).toLocaleString(DateTime.DATETIME_FULL_WITH_SECONDS)} + > + {DateTime.fromMillis(data().created || 0).toLocaleString(DateTime.DATETIME_MED)} + </div> </div> </div> - </div> - - <div> - <Show when={data().messages.length > 0} fallback={<p>Waiting for messages...</p>}> - <div class={styles.parts}> - <SuspenseList revealOrder="forwards"> - <For each={data().messages}> - {(msg, msgIndex) => { - const filteredParts = createMemo(() => - msg.parts.filter((x, index) => { - if (x.type === "step-start" && index > 0) return false - if (x.type === "snapshot") return false - if (x.type === "patch") return false - if (x.type === "step-finish") return false - if (x.type === "text" && x.synthetic === true) return false - if (x.type === "tool" && x.tool === "todoread") return false - if (x.type === "text" && !x.text) return false - if (x.type === "tool" && (x.state.status === "pending" || x.state.status === "running")) - return false - return true - }), - ) - - return ( - <Suspense> - <For each={filteredParts()}> - {(part, partIndex) => { - const last = createMemo( - () => - data().messages.length === msgIndex() + 1 && filteredParts().length === partIndex() + 1, - ) - - onMount(() => { - const hash = window.location.hash.slice(1) - // Wait till all parts are loaded - if ( - hash !== "" && - !hasScrolledToAnchor && - filteredParts().length === partIndex() + 1 && - data().messages.length === msgIndex() + 1 - ) { - hasScrolledToAnchor = true - scrollToAnchor(hash) - } - }) - - return <Part last={last()} part={part} index={partIndex()} message={msg} /> - }} - </For> - </Suspense> - ) - }} - </For> - </SuspenseList> - <div data-section="part" data-part-type="summary"> - <div data-section="decoration"> - <span data-status={connectionStatus()[0]}></span> + + <div> + <Show when={data().messages.length > 0} fallback={<p>Waiting for messages...</p>}> + <div class={styles.parts}> + <SuspenseList revealOrder="forwards"> + <For each={data().messages}> + {(msg, msgIndex) => { + const filteredParts = createMemo(() => + msg.parts.filter((x, index) => { + if (x.type === "step-start" && index > 0) return false + if (x.type === "snapshot") return false + if (x.type === "patch") return false + if (x.type === "step-finish") return false + if (x.type === "text" && x.synthetic === true) return false + if (x.type === "tool" && x.tool === "todoread") return false + if (x.type === "text" && !x.text) return false + if (x.type === "tool" && (x.state.status === "pending" || x.state.status === "running")) + return false + return true + }), + ) + + return ( + <Suspense> + <For each={filteredParts()}> + {(part, partIndex) => { + const last = createMemo( + () => + data().messages.length === msgIndex() + 1 && filteredParts().length === partIndex() + 1, + ) + + onMount(() => { + const hash = window.location.hash.slice(1) + // Wait till all parts are loaded + if ( + hash !== "" && + !hasScrolledToAnchor && + filteredParts().length === partIndex() + 1 && + data().messages.length === msgIndex() + 1 + ) { + hasScrolledToAnchor = true + scrollToAnchor(hash) + } + }) + + return <Part last={last()} part={part} index={partIndex()} message={msg} /> + }} + </For> + </Suspense> + ) + }} + </For> + </SuspenseList> + <div data-section="part" data-part-type="summary"> + <div data-section="decoration"> + <span data-status={connectionStatus()[0]}></span> + </div> + <div data-section="content"> + <p data-section="copy">{getStatusText(connectionStatus())}</p> + <ul data-section="stats"> + <li> + <span data-element-label>Cost</span> + {data().cost !== undefined ? ( + <span>${data().cost.toFixed(2)}</span> + ) : ( + <span data-placeholder>—</span> + )} + </li> + <li> + <span data-element-label>Input Tokens</span> + {data().tokens.input ? <span>{data().tokens.input}</span> : <span data-placeholder>—</span>} + </li> + <li> + <span data-element-label>Output Tokens</span> + {data().tokens.output ? <span>{data().tokens.output}</span> : <span data-placeholder>—</span>} + </li> + <li> + <span data-element-label>Reasoning Tokens</span> + {data().tokens.reasoning ? ( + <span>{data().tokens.reasoning}</span> + ) : ( + <span data-placeholder>—</span> + )} + </li> + </ul> + </div> </div> - <div data-section="content"> - <p data-section="copy">{getStatusText(connectionStatus())}</p> - <ul data-section="stats"> - <li> - <span data-element-label>Cost</span> - {data().cost !== undefined ? ( - <span>${data().cost.toFixed(2)}</span> - ) : ( - <span data-placeholder>—</span> - )} - </li> - <li> - <span data-element-label>Input Tokens</span> - {data().tokens.input ? <span>{data().tokens.input}</span> : <span data-placeholder>—</span>} - </li> - <li> - <span data-element-label>Output Tokens</span> - {data().tokens.output ? <span>{data().tokens.output}</span> : <span data-placeholder>—</span>} - </li> - <li> - <span data-element-label>Reasoning Tokens</span> - {data().tokens.reasoning ? ( - <span>{data().tokens.reasoning}</span> - ) : ( - <span data-placeholder>—</span> + </div> + </Show> + </div> + + <Show when={debug}> + <div style={{ margin: "2rem 0" }}> + <div + style={{ + border: "1px solid #ccc", + padding: "1rem", + "overflow-y": "auto", + }} + > + <Show when={data().messages.length > 0} fallback={<p>Waiting for messages...</p>}> + <ul style={{ "list-style-type": "none", padding: 0 }}> + <For each={data().messages}> + {(msg) => ( + <li + style={{ + padding: "0.75rem", + margin: "0.75rem 0", + "box-shadow": "0 1px 3px rgba(0,0,0,0.1)", + }} + > + <div> + <strong>Key:</strong> {msg.id} + </div> + <pre>{JSON.stringify(msg, null, 2)}</pre> + </li> )} - </li> + </For> </ul> - </div> + </Show> </div> </div> </Show> - </div> - - <Show when={debug}> - <div style={{ margin: "2rem 0" }}> - <div - style={{ - border: "1px solid #ccc", - padding: "1rem", - "overflow-y": "auto", + + <Show when={showScrollButton()}> + <button + type="button" + class={styles["scroll-button"]} + onClick={() => document.body.scrollIntoView({ behavior: "smooth", block: "end" })} + onMouseEnter={() => { + setIsButtonHovered(true) + if (scrollTimeout) { + clearTimeout(scrollTimeout) + } }} + onMouseLeave={() => { + setIsButtonHovered(false) + if (showScrollButton()) { + scrollTimeout = window.setTimeout(() => { + if (!isButtonHovered()) { + setShowScrollButton(false) + } + }, 3000) + } + }} + title="Scroll to bottom" + aria-label="Scroll to bottom" > - <Show when={data().messages.length > 0} fallback={<p>Waiting for messages...</p>}> - <ul style={{ "list-style-type": "none", padding: 0 }}> - <For each={data().messages}> - {(msg) => ( - <li - style={{ - padding: "0.75rem", - margin: "0.75rem 0", - "box-shadow": "0 1px 3px rgba(0,0,0,0.1)", - }} - > - <div> - <strong>Key:</strong> {msg.id} - </div> - <pre>{JSON.stringify(msg, null, 2)}</pre> - </li> - )} - </For> - </ul> - </Show> - </div> - </div> - </Show> - - <Show when={showScrollButton()}> - <button - type="button" - class={styles["scroll-button"]} - onClick={() => document.body.scrollIntoView({ behavior: "smooth", block: "end" })} - onMouseEnter={() => { - setIsButtonHovered(true) - if (scrollTimeout) { - clearTimeout(scrollTimeout) - } - }} - onMouseLeave={() => { - setIsButtonHovered(false) - if (showScrollButton()) { - scrollTimeout = window.setTimeout(() => { - if (!isButtonHovered()) { - setShowScrollButton(false) - } - }, 3000) - } - }} - title="Scroll to bottom" - aria-label="Scroll to bottom" - > - <IconArrowDown width={20} height={20} /> - </button> - </Show> - </main> + <IconArrowDown width={20} height={20} /> + </button> + </Show> + </main> + </Show> ) } |
