Repository navigation
Conversation
Design Review (Opus 5.5, fork) — ✅ PASSDesign-level review of Design-Verdict: PASS Nothing blocks: trying the absolute path first keeps today's first request unchanged, and every fallback goes through Notes
[DESIGN-REVIEWED] 0d92d4a Description read[DESCRIPTION-READ] 7f7ad464cca346aa4cd061c446bbe2c6c33709ffe55bf14af74f726e97377b94 That sha256 names everything this verdict read: the PR description, plus the 1 evidence file(s) the workflow collected from it and handed this review. It covers that evidence because the media strip replaces every attachment URL with the same placeholder, so an edit swapping one attachment for another would leave the description's captured text identical while what this review judged changed. Recompute it with |
UX Review (Opus 5.5, fork) — ✅ PASSUX-level review of UX-Verdict: PASS Nothing blocks. The committed screenshots show the resolved image and the new chip-plus-notice failure state in both themes. No blind reader has seen them, a limit of this fork lane; a same-repo run would add that read. Notes
[UX-REVIEWED] 0d92d4a Description read[DESCRIPTION-READ] 7b07c2b45fba922f6c47793f27b6937bf58a549bf7bd6e47240d9f495c4163c1 That sha256 names everything this verdict read: the PR description, plus the 6 evidence file(s) the workflow collected from it and handed this review. It covers that evidence because the media strip replaces every attachment URL with the same placeholder, so an edit swapping one attachment for another would leave the description's captured text identical while what this review judged changed. Recompute it with |
First Principles Review (Opus 5.5, fork) — ✅ PASSPremise-level review of First-Principles-Verdict: PASS Nothing blocks: the fallback walk fixes #23623 in What this change shipsInventory (7 items) — 7 justifiedIntent: in a file preview, show markdown images written as
[FIRST-PRINCIPLES-REVIEWED] 722f1e4 Description read[DESCRIPTION-READ] 4ce01793daa03d657663b8156bcd36bc561c7780055fee5899415e02ee19d343 That sha256 names the PR description this verdict read. Recompute it with |
Opus 5.5 Review (fork) — ✅ no blocking findingsReviewed |
GPT 6.1 Review (fork) — 🔴 changes requested (blocking)Reviewed Adjudication upheld 1 of 1 blocking finding(s); those must be resolved before merging. BLOCKING -- website/src/components/markdown/ImgWithFallback.tsx:177 -- Windows fallbacks omit the previewed drive’s root
[BLOCK-MERGE] 0d92d4a Adjudication (Opus 5.5) — is blocking on each finding proportionate?F1 — LOW harm. Confirmed in the patch: the ancestor loop stops before adding a bare drive dir like |
3e3a39a to
21629b6
Compare
|
Thanks for the reviews. Pushed GPT 6.1 BLOCKING — Design Review notes.
UX Review note (chip wording after eight failed paths). Not changed here. The "Image file no longer exists" wording comes from the existing HEAD probe and predates this PR; rewording it affects every surface that shows the chip, chat included, so it belongs in its own change rather than this fix. Fork PR, so the new head may need its workflows approved again. |
dc255af to
2803085
Compare
|
Fixed in 2803085 (re: the Opus 5.5 blocking finding on 21629b6, which has since been overwritten in place by the fd9d994 run). The finding: when the image exists but the server refuses it (403, e.g. an unsupported format or a sensitive path), every site-root fallback candidate 404s, and the broken-image chip only probed the last candidate, so it claimed "Image file no longer exists" for a file that is there. The chip now probes every candidate it tried. It says the file no longer exists only when all of them return 404; any other response gives the generic "could not load" chip. Two regression tests cover the 403-then-404 chain and the all-404 case; both fail with the fix reverted. Also rebased onto main 5d7a22d to drop the two "Merge branch main" commits that took the PR past the two-commit limit. The PR content is unchanged apart from this fix. |
2803085 to
d9ba318
Compare
d9ba318 to
3af253c
Compare
|
Both blocking findings on d9ba318 are fixed in 3af253c (still two commits). Opus 5.5: loaded image stuck under the skeleton. Fixed. The reset no longer keys on GPT 6.1: Checks: tsc, eslint, docs lint, markdown vitest (1,028) pass locally. |
3af253c to
722f1e4
Compare
|
GPT 6.1 finding on 3af253c (
Checks on this head: every test |
722f1e4 to
0d92d4a
Compare
|
Opus 5.5 and GPT 6.1 on 722f1e4 (the hand-off in the meeting note Preview): fixed in 0d92d4a by dropping the hand-off from the broken-image notice. The fallback renders in every Test: the |
A markdown file opened in the file preview showed every image written as /images/... as "Image file no longer exists", although the files were on disk. GitHub, Zenn, Docusaurus and similar setups write a leading slash to mean the root of the repository or site, but the preview sent the path to /api/file-raw as an absolute filesystem path. Inside a file preview (a BasePathCtx is set), a root-relative image now tries the absolute path first, so a real absolute path still loads on the first request, then each directory above the file, nearest first, up to eight levels. The broken-image chip still names the path as written. Outside a file preview nothing changes. Fixes kirodotdev#23623
0d92d4a to
ff1414b
Compare
|
GPT 6.1 on 0d92d4a (Windows fallbacks omit the previewed drive's root): fixed in ff1414b.
Checks on this head: every test |
Problem / Motivation
Goal: A markdown file opened in the file preview shows images written as root-relative paths (
/images/…) instead of "Image file no longer exists".Every image written as
rendered as the broken-image chip "Image file no longer exists", although the files were on disk next to the markdown. The preview sent the path to/api/file-rawas an absolute filesystem path (/images/…at the root of the disk), which 404s.Why it matters
A leading slash is the conventional way to reference an image from the root of the repository or site a markdown file belongs to: GitHub resolves it against the repository root in a README, and Zenn, Docusaurus and similar setups write images the same way. Anyone previewing such a repository in the dashboard sees none of its images, and the chip tells them the files are gone when they are not. The only workaround, rewriting the link relative to the file, breaks the image on the site the markdown is published to.
Not a goal
resolveNoteImageSrc), which knows the vault root and should use it directly; tracked in Notes app: root-relative images (/images/…) do not resolve against the vault root #23630.static/, Hugostatic/) to map/img/…onto a different directory./api/file-raw.What changed (motivation → approach → change)
Symptom: root-relative images in a previewed file render as missing. Root cause: in
ImgWithFallback, theBasePathCtxbranch resolves only paths that do not start with/,~or a drive letter, so a leading-slash path went to/api/file-rawverbatim.Nothing in the file says where its site root is, so the directories above the file are the candidates. When a
BasePathCtxis set andsrcstarts with a single/, the image now tries:/Users/…/shot.png) still loads on the first request;<dir>/<path>for each directory above the file, nearest first (starting with the file's own directory), up to eight levels.The next candidate is tried on each
<img>error; the broken-image fallback appears only after the last one fails, and still names and probes the path as written (the same single HEAD probe as onmain, so a refused (403) as-written file keeps the generic wording).siteRootImageCandidatesbuilds the list; it treats\as a separator only when the file sits on a drive-rooted Windows path, matching the existing rule that a backslash is a legal POSIX file-name character. The attempt index resets whensrcchanges. When the list of candidate paths changes (another previewed file), the index and the failed state reset, because an unkeyedFileViewerreuses the same image instance across tabs; the loaded state resets only when the path on screen changes, since an unchanged<img src>fires no newloadevent. Every candidate goes through/api/file-raw, so the fallback asks for nothing the absolute path could not already ask for.Because this changes when the broken-image fallback appears, the fallback moves to
ErrorNotice(errors-use-error-notice): the failure ("Image failed to load" / "Image file no longer exists") is an inlineErrorNoticewithout the agent hand-off, with aNo hand-offcomment naming the draft it protects: the same renderer previews the meeting note's unsaved draft (NoteSidebar), which withholds the hand-off on its own notice because the navigation would unmount that draft, and a shared renderer cannot tell which host holds one. The chip beside it keeps copying the on-disk path. That applies to every surface that renders markdown images, not only file previews.Alternatives considered: resolving against the project directory or the git root needs the backend (the frontend knows neither for an arbitrary previewed file), and a single fixed guess (the file's parent) fails for deeper layouts like
docs/guide/page.md.website/docs/frontend-conventions.mddocuments the behaviour next to the existing Windows-path rule.website/capture/adds the harness used for the screenshots below.Backwards compatibility
Compatible: the absolute path is still tried first and the fallback runs only after it 404s, so every image that loads today loads from the same path on the same first request.
Removes nothing: the change only adds fallback candidates after an image would already have failed.
Tests
website/src/test/MarkdownRenderer.rootRelativeImage.test.tsx:siteRootImageCandidateswalks nearest-first without the disk root, stops at a Windows drive, keeps a POSIX backslash as a file-name character, caps at eight levels, and returns nothing without a directory.<img>tries the absolute path, then each directory above the file, and stops at the one that loads with no chip.website/src/test/MarkdownRenderer.brokenImage.test.tsx: the failure is anErrorNotice(role="alert", the wording, and no "Ask the agent" button) and the wording is not in the copy chip; the existing copy, keyboard and probe tests now address the chip by test id.src, or a different previewed file, starts over from the absolute path, including after every candidate failed for the previous file.Proof: not provable -- frontend test (prove.py runs Python only)
Run by hand instead: with
ImgWithFallback.tsxreverted tomain, 8 of the first 10 tests in the new file failed (the two that passed pin unchanged behaviour); with the fix, all pass. Shuffled order, five runs, green.Also run, on the final head: every test
vitest relatedfinds forImgWithFallback.tsx(923 files, 13,967 tests),tsc, eslint,i18n:check,scripts/docs-lint.sh. Earlier rounds:vitestonMarkdownRenderer*,MarkdownPanel*,mdNotebook*,FileViewer*,ContentRenderer*andoverlayKeyGuard.inventory(95 files, 1,689 tests; after the review fix, the same set minusmdNotebook*andoverlayKeyGuard: 82 files, 1,364 tests),tsc -b, eslint,i18n:check,scripts/docs-lint.sh, jscpd.Manual verification
website/capture/shoot-root-relative-image.mjsbuilds a throwaway repository on disk (articles/post.md,images/post/architecture.png), renders the realMarkdownRendererinside theBasePathCtxthe file preview provides, and answers every/api/file-rawrequest from the real filesystem (existing file served, anything else 404). Its log showed the order:/images/post/architecture.png404,<repo>/articles/images/post/architecture.png404,<repo>/images/post/architecture.png200. The truly missing image tried eight directories and then showed the chip.Not yet checked in a running gateway with a real repository open in the file panel.
Screenshots / video
Before (current
main): the site-root image renders as "Image file no longer exists".After: the site-root image loads; an image that really is missing shows the path chip and an inline error notice.
After, light theme
Related Issues
Fixes #23623
Sibling sweep
Swept:
git grep -nE "file-raw\?path=" -- website/src ':!website/src/test'for every place that turns a markdown or file path into a/api/file-rawURL.Sibling: website/src/apps/md-notebook/utils.ts:165 -- tracked in #23630
Sibling: website/src/components/FileRenderers.tsx:92 -- not affected -- serves the previewed file itself by its own absolute path, no markdown reference
Sibling: website/src/pages/chat/ToolImageThumbnails.tsx:29 -- not affected -- tool output paths are absolute, no file to anchor a site root
Sibling: website/src/components/chat-input/FilePreviewStrip.tsx:116 -- not affected -- attachments are absolute paths picked by the user
Pattern harvest
Not generalizable: the defect is one missing branch in the single markdown image resolver; the other resolver (Notes app) has a different, known root and is tracked separately.
Checklist
feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert: ...)Contribution License Agreement
Contributions are accepted under the project's Apache-2.0 license; see the Licensing section of CONTRIBUTING.md.