Repository navigation
Description
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.
Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: 69feb72e65224dadb29e413fa6db88c9
Branch: dev/2-bubble-image-delegate
Base: e40cecf
At: 2026-11-04T00:25:10Z
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment.
Written-by: FlyBot Lead
What is wrong
After v0.2.22 (e40cecf6), an image inside a chat bubble renders as its alt text only. A message that contains , with screenshots/a.png present in the session workspace, shows the word shot and no picture; it cannot be opened in the lightbox either. v0.2.21 showed the image.
This is a regression introduced by #2313 (the #2201 link fix), which is in the v0.2.22 release.
Root cause (verified on live main e40cecf6 and the pinned host @deepseek-ai/dsh@0.2.0-rc.2)
Two facts combine:
-
The host hands
fileImagesto the view, not to the node.dsh-client-ui-chat/lib/client.js:5148builds the resolver (fileMediaUrl(document.baseURI, resolveWorkspacePath(cwd, path))) and:5280-5283passes it to theMarkdownDelegateProviderthat wrapsChatNodeList. The per-nodeownerobject handed to a node renderer (:1727-1738) carriescwd, openFile, openSkill, inspectCall, forkAt, loadImage, renderMessageImages, fileMentions, turnProcess— nofileImages. FlyBot's cells are fed by exactly that owner object, sofileImagesisundefinedeverywhere in our tree (bundlecore/client.js:6839/6850/7062/7182/7236;face()andassistantFacealso carry none). -
A nested
MarkdownDelegateProviderreplaces the enclosing capabilities.dsh-client-ui-primitives/lib/index.js:11040-11052builds the context value from exactly its own{openExternalLink, openFile, fileImages}props —"Nested providers replace the enclosing capabilities". SobubbleMarkdown(core/src/client/cells.js:33,:49) wrapsMarkdownTextin a provider whosefileImagesisundefined, shadowing the host's resolver that used to reach the bubble.MarkdownImage(:11543-11549) then getsfileImages?.resolve(...) ?? imageSource(destination, pathImages)→undefined→ it draws<span class="imageAlt">{alt}</span>.
Before #2313, core/src/client/cells.js (at a11983c5) imported only MarkdownText and rendered it bare at the same three sites, so the nearest provider was the host's and images resolved. Call sites now: :85 (Bubbles), :614 (EarlierParts), :1002 (GroupReplies).
useMarkdownDelegate is not exported by the primitives package (export list, lib/index.js:12381), so the delegate cannot be read back from context and merged.
Reproduction
- Run v0.2.22 and open a chat whose message text is
. - Expected: the image, clickable into the lightbox (
preview: fileImagesatlib/index.js:11554). - Actual: only the alt text
shot; no image, no lightbox.
Why the new tests stayed green
core/test/chat-link-open.test.js:27-31 replaces the provider with a stub that does Object.assign(delegate, { openExternalLink, openFile, fileImages }) and returns children. That models inheritance, not replacement, and it never renders an image, so a shadowed resolver is invisible to it. The whole file only exercises http(s) anchors.
Scope of the fix
- Keep
MarkdownTextbare at:85,:614,:1002— drop the nestedMarkdownDelegateProviderso the host'sfileImages/openFilereach the bubble again, exactly as in v0.2.21. - Move the client: a link in a chat opens the host's right panel behind FlyBot's own fixed panel, so the page looks blank until the panel is closed #2201 link interception out of the delegate and into capture-phase click handling on the containers:
Bubblesalready hasonClickCapture(:55-75);EarlierPartsandGroupRepliesneed the same. Handle onlya[href]with anhttp/httpslink,preventDefault()+stopPropagation(), then the existing behaviour:actions.browserNewTab(subject)→actions.browserNavigate(tabId, href),window.openfallback when no panel. - Leave modifier-click and non-http(s) links (file links, mention anchors) to the host.
- Regenerate the bundle:
node core/scripts/build-client.mjs(--checkmust pass).
Rejected alternative: rebuilding a fileImages resolver inside FlyBot from cwd would need the host-internal fileMediaUrl/resolveWorkspacePath pair, duplicating the host's URL scheme and drifting from it.
The pull request carries changelog.d/2319-bubble-image-delegate.fixed.md — .fixed.md, because this is a
regression fix, and changelog.d/README.md asks for one file per user-visible change. The CI check
(node .github/scripts/changelog.mjs check) only validates fragments that exist, so the fragment has to
be written deliberately rather than left to the gate.
Acceptance
- A test that models the real semantics: a provider stub that replaces its context and an image renderer that reads the resolver from it; the image case must be red at
e40cecf6and green after the fix. - The existing http(s) anchor cases still pass, and a modifier-click / file-link case proves those are left to the host.
core/scripts/build-client.mjs --check,npm --prefix core testandnpm run verifygreen at the tip.
Deduplication
- Code search
repo:ThinkFlowLab/FlyBot "fileImages": only the cells chain, the bundle, the patch and the test stub — no other resolver path. - Issue search (
image markdown, open): only client: a link in a chat opens the host's right panel behind FlyBot's own fixed panel, so the page looks blank until the panel is closed #2313 (merged, the cause), Fix the UI defects a click-through found: drawer wrapping, protocol previews, blank chats, failure copy #1346 and Rewrite the README for what people do with FlyBot #1385 (closed, unrelated); no open duplicate.
Out of scope
- The
window.openfallback is in scope after all: this fix keeps bothnoopenerandnoreferrerin the third argument, so the dropped-noreferrergap named at client: a link in a chat opens the host's right panel behind FlyBot's own fixed panel, so the page looks blank until the panel is closed #2313 is closed here rather than tracked separately. What stays separate is the host-side gap (the host handsopenFile/cwdbut notfileImagesto a plugin node view, so a local image destination inside a bubble keeps rendering as alt text until the host supplies the resolver) and extra argument-level coverage forGroupMessage/AssistantCell. - A toolbar entry to open a panel page in the user's own browser — separate enhancement.
Activity
HugoXK commentedon Nov 4, 2026
pickup-ok — FlyBot implementation, claimed and being built now.
69feb72e65224dadb29e413fa6db88c9dev/2-bubble-image-delegateDev-2core/src/client/**,core/test/**,patches/ds-bot/**,docs/**and onechangelog.dfragment)The pull request follows from this branch and carries
Fixes #2319.Written-by: FlyBot Lead
HugoXK commentedon Nov 4, 2026
Release
Written-by: FlyBot Lead
Released: 69feb72e65224dadb29e413fa6db88c9
Reason: merged as #2322 (squash e8edff6)
At: 2026-11-04T01:30:11Z