Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 35 additions & 27 deletions packages/app-bundle/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"packages/opencode/pasqal-live-test.sh": "0bb0089eb413956f0252939f933a139c2f11520466887ad91fcae362f1698e41",
"packages/session-ui/bunfig.toml": "468114e9b52c3cf926d2385c12d93385992cd70c97ea9e32ed22154617b7799d",
"packages/session-ui/happydom.ts": "d81d6b14e988f058fa502c841df34b55307c57fc94a26fb4f8a5deb6aa0d4834",
"packages/session-ui/package.json": "f354c8675067dc8dd1493924cd6c8f561fb838ff97ae6cbd7e024868cbf0e0e8",
"packages/session-ui/package.json": "9661896519610a5909b1ce9d9b9aa66c113704b1c91f288ecf81207cb1a2fc86",
"packages/session-ui/preload-worker-mock.ts": "b53280029a042575734d7bfdf27713f6c5cd6a6665599dc429d839a3e3372bd1",
"packages/ui/package.json": "b1d168d0371e9094faae1107fc6c00be197f09bc69daa2247a3890d607f4b629",
"packages/app/public/amico.svg": "7c64b145bdd063bacd53318336a423f0654ec702de2b4e0c4a4a384694052804",
Expand Down Expand Up @@ -108,7 +108,7 @@
"packages/app/src/components/amicode-default-model.test.ts": "941750d074706bd5f4e433c9737b19ebc0ac0f9ba9cd1faaa599a24b22126e05",
"packages/app/src/components/amicode-default-model.tsx": "16a2de1feb12e07416fe5fbb25879391f912ee0909b553c868c1b531f2ed1f2d",
"packages/app/src/components/amicode-defaults-capsule.tsx": "94dc081bc47abb7e50566fcd8e9e53124f9d7632d61f93eff4f1627e0c2efb2d",
"packages/app/src/components/amicode-file-ref-bridge.tsx": "bd5c295080ccd80fe89208008c02db209ba301e431f61bc4432d3c8f2a72401f",
"packages/app/src/components/amicode-file-ref-bridge.tsx": "b1292ac91ad1d07a374251199fc92392a5cf68ee6933083dbf6d3978fceb5d9d",
"packages/app/src/components/connection-banner.tsx": "82194291181165ed7b1b1b3a6801a1bdcd2c2b7cdd733c28486bd69f86477a24",
"packages/app/src/components/dialog-command-palette-v2.css": "85984957adaf15efcc0828310f3a0b438cedaaba776a9fec2eddb3ad83487fa2",
"packages/app/src/components/dialog-command-palette-v2.tsx": "e12da3e5e9e503089dd9139b20e04f6b566cf74fa445f23a2e193f5883f7c5f3",
Expand Down Expand Up @@ -493,13 +493,16 @@
"packages/session-ui/src/components/build-trace.test.ts": "6259c529fa667a3514bfee1c771e8f623bcb8c69e37ceb17ea541aa42e207451",
"packages/session-ui/src/components/build-trace.ts": "153e3182c3a37e4aca9c88bf0e2052b0ff4ec117a60d74d2c81567ab91180066",
"packages/session-ui/src/components/group-parts.test.ts": "a31ce19009e1d62e75e83b682190a4613da0eed334420409ed73a9e21834ae84",
"packages/session-ui/src/components/image-lightbox.tsx": "9626b81323ac4741ce4cd9276e510a619c5be64cb2b96e0b0e9c0b7ff4a05e9c",
"packages/session-ui/src/components/line-comment-styles.ts": "2077836c13bd3c3f1c39c67722119e448a2dfa123377f94bfd3676634e955d00",
"packages/session-ui/src/components/markdown-file-refs.test.ts": "2172d17a0ee5e49636c8bd323f4fb797a7f41e469d0098864b82d374f0c10144",
"packages/session-ui/src/components/markdown-file-refs.ts": "417417f3660fe18f04a13ca4765b155b5479d7ce41b3638a57ba1abaa5830054",
"packages/session-ui/src/components/markdown-local-images.test.ts": "1482381f4bce78cf224a76ea7f44a7b6104445b9d8b932788ac215903e839d26",
"packages/session-ui/src/components/markdown-local-images.ts": "6115ef3a47a77aa4c528e3aa9a307e433032b905154fde3f4ca0407d6e3f9edb",
"packages/session-ui/src/components/markdown-stream.test.ts": "b73b1a15ffa2cfea5358b2d6313ce17d26a5a11302b0e3b7d823cde0c98cbc83",
"packages/session-ui/src/components/markdown-stream.ts": "50e38abd5d4ff919564655799678be10463dec58502fc5487c1c97a9a8db5399",
"packages/session-ui/src/components/markdown.css": "24e7c19218620386647656a5d27e116cdeca911f1916c5d5b8609edca11ff165",
"packages/session-ui/src/components/markdown.tsx": "00a2248ca301e39eb5f2a9b78abfe9d11068ea5daa970516e1f8700c44878c48",
"packages/session-ui/src/components/markdown.css": "500c69fcef79c356bba9bbdfa421489442a1b7233b34fdaf8b57eb8b32e5744f",
"packages/session-ui/src/components/markdown.tsx": "b74c66e03e9df8ed7ca95d04d4ab712b71c0e16d6318031ff7ea74373014c263",
"packages/session-ui/src/components/message-part-css.test.ts": "bc164695d0195684d646ebf490ba7bb4ed72adcf2bf3d01b00de47f4f03bcd4f",
"packages/session-ui/src/components/message-part-groups.ts": "37be069bf46e926fcde83d3744ee706911ff002e6b532e279290d841aeee96da",
"packages/session-ui/src/components/message-part-skill.test.ts": "453bdcb25e4df91ad1004635b8766070a60421355adf18ee53f43cd2fa7d0486",
Expand All @@ -525,7 +528,8 @@
"packages/session-ui/src/components/tool-status-title.tsx": "ae4561b58f55985d2294a352802e723b18943ac9a6551d97f1c4a6cc95368c8a",
"packages/session-ui/src/context/data.tsx": "7b840b5bf6929445d21da2cbd8e8006316c6950a09d559ea77e8c397e728cd99",
"packages/session-ui/src/pierre/worker.ts": "7f210f931777956567eaeea15f2f5478befc7874a64a4b685504c59c66503fee",
"packages/session-ui/src/util/clipboard.ts": "c37ba53914714ed04135e5a254e8ea90186f097d7470fa0873a83de82f3fef2d",
"packages/session-ui/src/util/clipboard.ts": "f0a5e5b2b4f89b7aceea245e9ecda4e6d36edf9e6169451f52993f9bf7dbd8a0",
"packages/session-ui/src/util/save-image.ts": "599b4fe69c191c41db1d231303b94a9acdf0fe2e4c2056e914367c321039a24c",
"packages/ui/src/amicode/activity-dot.tsx": "fc3c9e620cdd204e930f0d53a0a44456e0c886679174c91f0efbf9707daa7bc6",
"packages/ui/src/amicode/activity.test.ts": "1674ca5138780242767b0eb42fc444a9b8ce7868ac119117dd1487b7209180b2",
"packages/ui/src/amicode/activity.ts": "a2c9b4fe27175a400ea0bcfeb433dfcb572cd793766bf0fd232c741feef5cfbf",
Expand Down Expand Up @@ -698,24 +702,24 @@
"packages/ui/src/context/marked-math.test.ts": "0a53b2701d79ef16abe3132a514831ada60a535324991e4d72ee4e0ced9a0ae6",
"packages/ui/src/context/marked-parser.tsx": "2d8d0c2e5e28278f54296640d1e851fff4ef3b2902c7f70d02cdbcd82545bf39",
"packages/ui/src/hooks/create-auto-scroll.tsx": "4a8e9aa41f075b5e1815e647ab3a8a5333340d00e20ff7414f5e6c1f9a738337",
"packages/ui/src/i18n/ar.ts": "8113d320fe4a5300f1bd70150ae1695d68cb783e1c6e2be43b8a429b7dbdb711",
"packages/ui/src/i18n/br.ts": "cbde2da43ef820fe2664cb96cb973fd722156a6c8a3c5e16d18638bf014e5196",
"packages/ui/src/i18n/bs.ts": "23ea0ae34a4edaa4599b13f63f481be861e28da3b0d898987e291c7e91a9f11e",
"packages/ui/src/i18n/da.ts": "e8083f8a7ab01f73990a1eabcba817afdc663858d524e56e652ea69238cefc47",
"packages/ui/src/i18n/de.ts": "eb854fdd43f16fc2346ec43d7d1629d2714709991b3e5f7ac5ee99011324aa43",
"packages/ui/src/i18n/en.ts": "1b412a1cd0ea8f7d0ce6069d3bd94dc40778cc38c23c60191cd90071d4187ac8",
"packages/ui/src/i18n/es.ts": "a34d132adc2ad68ae3da98b6afd5070be48a0cd6cb15c4866cc7ea99deefc4ab",
"packages/ui/src/i18n/fr.ts": "4a8296dec41894e6ecb209e44564ac79c04cae73035b5447eb233cd984ef4bae",
"packages/ui/src/i18n/ja.ts": "6c13f118491c8badfac33baf449bee55779f6058792b6a54973bfe975e4c25c3",
"packages/ui/src/i18n/ko.ts": "b97f70d80c9ab7561c05ab91d461e9323c4c846d6a27bed0a41ad5fbcdf0c1db",
"packages/ui/src/i18n/no.ts": "120e5d11f5aebde00822def6fda9ae12db47cd020bdd04114cdb098d5ba67449",
"packages/ui/src/i18n/pl.ts": "8133cedc013364bf84a3902399b431f4029d6c1d57acf23f12a739127d4583e9",
"packages/ui/src/i18n/ru.ts": "11d79d60f2f678597ee2b33b51fa666f38f1e78b7ed051a686229ae0dd8aac72",
"packages/ui/src/i18n/th.ts": "afa5a46ff445461db8f069338aae760cb9cac09b370c47ced5542549c92c9235",
"packages/ui/src/i18n/tr.ts": "fac27e4bec327ff83976d9cdd8f54c2a241cb26b44c644a93c591e99f09290ed",
"packages/ui/src/i18n/uk.ts": "3531fcf92efaec359381fcc2f0cdbb644d52936a20037067451b057ef327ea1c",
"packages/ui/src/i18n/zh.ts": "6aac9e8c8973fb538effee075740035ce58a631615d39c0c8fb49375d45a56e5",
"packages/ui/src/i18n/zht.ts": "d50b7119139f53180eac64a6317341f54efc87ff76d56961a3e86ef74e3ad29b",
"packages/ui/src/i18n/ar.ts": "97076b6ad801490738d826b32a85ae319a71de80bf54913b2df336b9bac20253",
"packages/ui/src/i18n/br.ts": "4980710f624237a2431d9c998fb96f3a639a57ad17294673a0c5686e5bccb786",
"packages/ui/src/i18n/bs.ts": "76d4a6bbf6c1a2a70c5faa7f3f92be184527e2c4f35caf671e78383466acd0d3",
"packages/ui/src/i18n/da.ts": "3a0f67922cca3e51295f94ae6359ddd8f6d0aae3d083b555f7270eec971df61e",
"packages/ui/src/i18n/de.ts": "2aa33b5f4999b2b5e86fa4c1aa87a4e1065d50404f380516d9bf74d42d546f04",
"packages/ui/src/i18n/en.ts": "09cef9a7387c1520608e121a7ba3ea49e634fc1a5fa2699719ce83d7878f329c",
"packages/ui/src/i18n/es.ts": "60edc806b466c94a23442a0aab94ecffb392582a55253e0b24005881937a5900",
"packages/ui/src/i18n/fr.ts": "fd05b582176bd197fa895e8f88a03af52a9b5c375818cd06e935b81c66d729e2",
"packages/ui/src/i18n/ja.ts": "7d0034042e62bce6875da6ea9b5cc3624af9b32120676b6e6ba8285b87b6bb27",
"packages/ui/src/i18n/ko.ts": "a2bb5964089bf5b96470eb2c8f5b4f2a474fd35bcda36697d8a831d966d950b7",
"packages/ui/src/i18n/no.ts": "4950514b740d052ac097644559e8e8e52d304feeee56a24c9bd6fa40ae3aac85",
"packages/ui/src/i18n/pl.ts": "c9a31c1fc2352755136459ece574688465c42788101b3a39490fe92f2cb1f4c5",
"packages/ui/src/i18n/ru.ts": "5ddb560fa1155f1b0242be83c6e2a88533efd3c2b3f37856207da526b2b95944",
"packages/ui/src/i18n/th.ts": "54b511d48e6d606da7a44601400317075852c3b26cf67f8802f13c40561f170c",
"packages/ui/src/i18n/tr.ts": "f862f1c8bf700a9087e507e32a4c4094d834c37e23f3107d6551213b74328ca0",
"packages/ui/src/i18n/uk.ts": "5c5566b45e5ea82b28c417f3c4c83c380e61de7934da7815449f4e3e423fbb1e",
"packages/ui/src/i18n/zh.ts": "fde440e791fc340728374dd04690c9fd286da1293298074439d5a9526f86738b",
"packages/ui/src/i18n/zht.ts": "41944597b6dada282c3ca0483d85bd188c797bd906561cb8025411d0190546ca",
"packages/ui/src/styles/index.css": "2eae4e3d82e992c3d99f4a91ff25b3d6e9e514b38e9bebd232e6c41009cdc1fd",
"packages/ui/src/styles/theme.css": "0dc88bf675df5cdf08cfe0cc53c11eaf030a1bc0cf8b36cbeb3977367c2b362b",
"packages/ui/src/theme/context.tsx": "93b0b3f8a258bc8a33d4b6a4cda7016e91f6d999bd32c60a43aaaf45eadd1227",
Expand Down Expand Up @@ -932,7 +936,7 @@
"packages/ui/src/v2/components/badge-v2.css": "32135a9df5bad856a10138e00a2cb3ede7a6fc0cc2398797ad6bed9335d1f845",
"packages/ui/src/v2/components/dialog-v2.css": "ae6f7b1de755929ba529b9dbb0e837c80f620f00a77188b9d2c59f10b0f28bbd",
"packages/ui/src/v2/components/icon-button-v2.tsx": "966e535e3efe503f054045be4476fc20d2fad36092410b52f75e4cf8de6fce6f",
"packages/ui/src/v2/components/icon.tsx": "9f702e9c307524f79dfa9cad70f9bea32bda85672f917ffdb0871bb14cf13437",
"packages/ui/src/v2/components/icon.tsx": "9beb0192869e9acf61ab148e806f399c155cd9b6c1b414576fe1652b94dcff45",
"packages/ui/src/v2/components/menu-v2.css": "2f262a6c456a8e3cb9027f0b8b3bb3c4327794bd80cadd3cf8c16d08e6125d4d",
"packages/ui/src/v2/components/menu-v2.stories.tsx": "4ed1498d7e4cd51ce9c7459061f3540efdfae71751ff618901c78ee413951559",
"packages/ui/src/v2/components/radio-v2.css": "bf6eb3457a7fc13e48356f5a7dcf3cbf462382580250a3144a745402ff88e2e6",
Expand Down Expand Up @@ -1031,7 +1035,7 @@
"packages/ui/src/v2/components/text-shimmer-v2.tsx"
],
"exceptions": [],
"extracted_at": "2026-10-26T18:34:42.306Z",
"extracted_at": "2026-10-30T02:38:43.036Z",
"per_package": {
"packages/app": {
"A": 257,
Expand Down Expand Up @@ -1069,7 +1073,7 @@
"D": 0
},
"packages/session-ui": {
"A": 45,
"A": 49,
"M": 32,
"D": 0
},
Expand All @@ -1080,7 +1084,7 @@
}
},
"counts": {
"overlay_total": 1009,
"overlay_total": 1013,
"deletions": 7,
"server_coupled": 0
},
Expand Down Expand Up @@ -1570,9 +1574,12 @@
"packages/session-ui/src/components/build-trace.test.ts": "A",
"packages/session-ui/src/components/build-trace.ts": "A",
"packages/session-ui/src/components/group-parts.test.ts": "A",
"packages/session-ui/src/components/image-lightbox.tsx": "A",
"packages/session-ui/src/components/line-comment-styles.ts": "M",
"packages/session-ui/src/components/markdown-file-refs.test.ts": "A",
"packages/session-ui/src/components/markdown-file-refs.ts": "A",
"packages/session-ui/src/components/markdown-local-images.test.ts": "A",
"packages/session-ui/src/components/markdown-local-images.ts": "A",
"packages/session-ui/src/components/markdown-stream.test.ts": "M",
"packages/session-ui/src/components/markdown-stream.ts": "M",
"packages/session-ui/src/components/markdown.css": "M",
Expand Down Expand Up @@ -1603,6 +1610,7 @@
"packages/session-ui/src/context/data.tsx": "M",
"packages/session-ui/src/pierre/worker.ts": "M",
"packages/session-ui/src/util/clipboard.ts": "A",
"packages/session-ui/src/util/save-image.ts": "A",
"packages/ui/src/amicode/activity-dot.tsx": "A",
"packages/ui/src/amicode/activity.test.ts": "A",
"packages/ui/src/amicode/activity.ts": "A",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// with the bridge unmounted no resolver is registered and pills stay pills.
import { onCleanup, onMount } from "solid-js"
import { registerFileRefResolver } from "@opencode-ai/session-ui/markdown-file-refs"
import { registerLocalImageLoader } from "@opencode-ai/session-ui/markdown-local-images"
import { ServerConnection, useServer } from "@/context/server"
import { useGlobal } from "@/context/global"
import { amicodeGet } from "@/utils/amicode-fetch"
Expand All @@ -15,22 +16,42 @@ export function AmicodeFileRefBridge() {
const server = useServer()
const global = useGlobal()
let unregister: (() => void) | undefined
let unregisterImages: (() => void) | undefined
const pick = () =>
pickVaultServer({
current: server.current,
list: server.list,
healthy: (c) => global.servers.health[ServerConnection.key(c)]?.healthy === true,
})
onMount(() => {
// #1845: keep the unregister handle — it clears only THIS registration,
// so a remounting shell's late cleanup can't wipe its successor's resolver.
unregister = registerFileRefResolver(async (text) => {
const conn = pickVaultServer({
current: server.current,
list: server.list,
healthy: (c) => global.servers.health[ServerConnection.key(c)]?.healthy === true,
})
const conn = pick()
if (!conn) return null
const raw = (await amicodeGet(conn, `/amicode/resolve-file?path=${encodeURIComponent(text)}`)) as
| { ok?: boolean; found?: boolean; path?: unknown }
| undefined
return raw?.ok === true && raw.found === true && typeof raw.path === "string" ? raw.path : null
})
// Chat images authored as `![](/abs/path.png)`: read the bytes through the
// server (the iframe can't reach its disk, and a bare <img> can't carry the
// credential) and hand session-ui a data: URL.
unregisterImages = registerLocalImageLoader(async (path) => {
const raw = (await amicodeGet(pick(), `/file/content?path=${encodeURIComponent(path)}`)) as
| { type?: string; content?: unknown; encoding?: string; mimeType?: unknown }
| undefined
if (typeof raw?.content !== "string" || raw.content === "") return null
if (raw.type === "binary" && raw.encoding === "base64" && typeof raw.mimeType === "string" && raw.mimeType.startsWith("image/"))
return `data:${raw.mimeType};base64,${raw.content}`
if (raw.type === "text" && path.toLowerCase().endsWith(".svg"))
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(raw.content)}`
return null
})
})
onCleanup(() => {
unregister?.()
unregisterImages?.()
})
onCleanup(() => unregister?.())
return null
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"./markdown-stream": "./src/components/markdown-stream.ts",
"./markdown-cache": "./src/components/markdown-cache.tsx",
"./markdown-file-refs": "./src/components/markdown-file-refs.ts",
"./markdown-local-images": "./src/components/markdown-local-images.ts",
"./line-comment-styles": "./src/components/line-comment-styles.ts",
"./pierre": "./src/pierre/index.ts",
"./pierre/*": "./src/pierre/*.ts",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// AMICODE: full-size preview for a chat image, with download and copy. The
// stock ImagePreview (ui) is close-only; agent-produced figures (equations,
// plots) are usually opened to be copied or saved into a deck or doc, so the
// header carries download + copy next to close. Same data-component as ImagePreview so it rides
// the shared image-preview styles.
import { Dialog as Kobalte } from "@kobalte/core/dialog"
import { createSignal, onCleanup } from "solid-js"
import { useI18n } from "@opencode-ai/ui/context/i18n"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { copyImageToClipboard } from "../util/clipboard"
import { saveImage } from "../util/save-image"

export function ImageLightbox(props: { src: string; alt?: string; filename?: string }) {
const i18n = useI18n()
const [copied, setCopied] = createSignal(false)
let timer: ReturnType<typeof setTimeout> | undefined
onCleanup(() => clearTimeout(timer))
const copy = async () => {
if (!(await copyImageToClipboard(props.src))) return
setCopied(true)
clearTimeout(timer)
timer = setTimeout(() => setCopied(false), 2000)
}
return (
<div data-component="image-preview">
<div data-slot="image-preview-container">
<Kobalte.Content data-slot="image-preview-content">
<div data-slot="image-preview-header">
<IconButton
data-slot="image-preview-download"
icon="download"
variant="ghost"
aria-label={i18n.t("ui.message.download")}
title={i18n.t("ui.message.download")}
onClick={() => void saveImage(props.src, props.filename ?? "image.png")}
/>
<IconButton
data-slot="image-preview-copy"
icon={copied() ? "check-small" : "copy"}
variant="ghost"
aria-label={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copy")}
title={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copy")}
onClick={() => void copy()}
/>
<Kobalte.CloseButton
data-slot="image-preview-close"
as={IconButton}
icon="close"
variant="ghost"
aria-label={i18n.t("ui.common.close")}
/>
</div>
<div data-slot="image-preview-body">
<img src={props.src} alt={props.alt || i18n.t("ui.imagePreview.alt")} data-slot="image-preview-image" />
</div>
</Kobalte.Content>
</div>
</div>
)
}
Loading
Loading