Repository navigation
fix(app-shell): a marketplace README fits its detail page; badges render as images, tables as tables - #30426
Merged
Conversation
…h (red) Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CZbFePHuuidesS6BLC
… as images, tables as tables Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CZbFePHuuidesS6BLC
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CZbFePHuuidesS6BLC
…ithout a control-character regex Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CZbFePHuuidesS6BLC
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CZbFePHuuidesS6BLC
…arkdown extension Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CZbFePHuuidesS6BLC
Contributor
Author
|
domain:ui#2 · os-bill · session_01Utp6Cb5R6JDdfyvtj3dPR2 维护者速读
席位核对 — ACCEPT · domain:ui#2 · head
|
This was referenced Nov 5, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Bill · project thread
domain:ui#2 · os-bill · session_01Utp6Cb5R6JDdfyvtj3dPR2
Fixes #30330
Clause-②: no
Metadata-protocol: none
维护者速读
---显示成分隔线,不再露出 Markdown 原文。 …字面。README 是应用的销售页,半截句子和原始标记让成品像半成品。MarkdownText、详情页两列加min-w-0)及测试与 changeset,不动 spec、服务端与共享内联渲染器。图片只认绝对https:地址并以 no-referrer 加载,其余来源整段略去;控制台 CSP 本就允许img-src https:。回滚即 revert 本 PR。ui/packages/app-shell/src/console/marketplace/MarkdownText.tsx(renderInline的图片切分与absoluteUrl、表格块规则)→MarketplacePackagePage.tsx(两个网格单元的min-w-0)→ 测试MarkdownText.wideReadme-30330.test.tsx。pnpm --dir ui exec vitest run packages/app-shell/src/console/marketplace/__tests__/MarkdownText.wideReadme-30330.test.tsx packages/app-shell/src/console/marketplace/__tests__/MarketplacePackagePage.readmeWidth-30330.test.tsx,预期全部通过;在 main 的代码上同一命令 26 条红、5 条对照绿。What changes
Which element forced the width (the card left it open). Below the
lgbreakpoint the detail page'sgrid gap-4 lg:grid-cols-3has one implicitautocolumn, and a grid item's automatic minimum width is its content's min-content width. HotCRM's README has a fencedcurl … -d '{"jsonrpc":…}'line of 259 characters; apredoes not wrap, so its min-content width is that line, and it sized the column, the About card and the README at about 1926 px. Theprealready hadoverflow-x-auto, but it never became narrower than its line, so it never scrolled. (Atlgand up the template isrepeat(3, minmax(0, 1fr)), which has no automatic minimum, so the defect showed only on narrower windows.)MarketplacePackagePage.tsx. Both grid cells (README and Versions) carrymin-w-0, so the column keeps the page's width and the fence scrolls inside itself.MarkdownTextroot.min-w-0 break-words, so a long unbroken word (a URL, a long code span) wraps instead of overflowing; a list item's text span getsmin-w-0beside its bullet for the same reason. Aprekeepswhite-space: preand its own scroller; a table scrolls in its ownoverflow-x-autobox.Why the badges printed as markup. HotCRM writes
[](LICENSE). The renderer had no image rule, and the shared inline link rule read[as a link whose text isas text, which reads exactly as the card measured.MarkdownTextnow matchesand the linked form[](href)before handing the rest of the text to@object-ui/components'renderInlineMarkdown(unchanged; it is shared withelement:textand outside this card's surface). An image renders only from an absolutehttps:URL, withreferrerPolicy="no-referrer",loading="lazy"andmax-w-full; any other source (a repository-relative path,/…,//…,http:,data:,javascript:, any whitespace or control character) is left out, alt text included. A linked image keeps its anchor only for an absolutehttp(s):destination (new tab,noopener noreferrer, the README link look): a relative destination such asLICENSEwould resolve against the console, not the repository. A code span is matched first, so``stays code. No fetch path is invented for relative sources.tablewith the delimiter row's alignment, cells drawn by the same inline pass, short rows padded and extra cells dropped,\|read as a literal pipe. A pipe in prose with no delimiter row stays text.---,***,___(spaces allowed) between blocks render ashr;* * *is a rule, not a list item. A---line directly under a paragraph line stays that paragraph's text, as before (CommonMark reads it as a setext underline, which this renderer does not draw). HotCRM's README has four such rules that printed as---.The file header's "If a README really needs tables or images, the publisher can host their own docs" is replaced by the rules above. No raw HTML is rendered and no dependency is added.
Tests
MarkdownText.wideReadme-30330.test.tsx(new): HotCRM's badge row renders fourimgand no in a paragraph and in a list item; a refused link scheme keeps the image without the anchor; an image title, and image syntax inside a code span staying code; tables (cells, inline marks, escaped pipe, alignment, own scroller, padding/truncation, HotCRM's screenshot table); five thematic-break spellings; the compact size; the width classes. CONTROL: headings, links, lists, quotes and fences render as before, a pipe in prose stays text, a---under a paragraph line stays text, a relative text link keeps itshref.MarketplacePackagePage.readmeWidth-30330.test.tsx(new, thereleaseNotes-28891harness): both grid cells carrymin-w-0; CONTROL: the README fence renders in the About cell with its own scroller, and the Versions notes render.Runs (all under
scripts/pm/os-verify-lock.sh,pnpm --dir ui exec vitest run …):origin/main'sMarkdownText.tsxandMarketplacePackagePage.tsx(checked out over the fix, then restored withgit checkout HEAD --,git diff HEADempty):Tests 26 failed | 5 passed (31), the 5 being the CONTROL rows.MarkdownText.*andMarketplacePackagePage*test in the directory (21 files):Test Files 21 passed (21),Tests 159 passed (159). On the final head6a86665c: the 7 files that renderMarkdownTextplusscripts/__tests__/markdown-test-inputs.test.ts:Tests 80 passed (80).pnpm exec turbo run type-check --filter=@object-ui/app-shell --concurrency=2:Tasks: 68 successful, 68 total; on the final head,pnpm --filter @object-ui/app-shell type-check:VERDICT command-exit 0. Lint:eslinton the touched files: 0 errors (the warnings are pre-existingno-explicit-anyinMarketplacePackagePage.tsx).The whole change is the ablation: the red run above is the new pins against the old code, each flipped by the code it names.
Real-browser width measurement (Chromium, 883 px viewport)
jsdom does no layout, so the tests pin the classes. The mechanism was measured in Chromium with a static page whose CSS mirrors the Tailwind classes (an 835 px
overflow: hiddenparent as the card measured, a one-column grid, the card's 24 px padding, the README's flex column, HotCRM's 259-character fence line at 12 px monospace, and a 200-character unbroken URL in a paragraph):min-width: 0, nooverflow-wrap): parentscrollWidth 1947inclientWidth 835; card1945wide;pre1895wide with nothing to scroll; the paragraph's right edge at1922px.min-width: 0on the grid cell and the README root,overflow-wrap: break-word): parent835/835; card833;preclientWidth 783,scrollWidth 1895(scrolls inside itself); the paragraph's right edge at810px.The measurement page is a reconstruction, not the Console build; the Console's own render on a live stack was not exercised.
Acceptance notes
ui/AGENTS.md✨ Set up Copilot instructions #2 (docs-driven): no page undercontent/docs/describes which Markdown a marketplace README may use (deployment/marketplace-handbook.mdxnames thereadmePathandreadmefields only), so no doc changes; that tree is also outside this card's surface.[the guide](docs/guide)) still resolves against the console, as before; the shared inline pass owns that rule andelement:textreads it too, so it is untouched here. Observation, not filed.[see ](z)) is not a shape this renderer reads; the image renders or drops and the link's brackets print. Not seen in any measured README.Verification
Derived gates (dispatch-gates --commands on merge head d13ed5b; re-runs on final head 6a86665): 35 run, all exit 0
node scripts/check-adr-0087-registration.mjs --base origin/main:: exit 0node scripts/check-adr-0087-registration.mjs --self-test:: exit 0node scripts/check-changeset-no-major.mjs --base origin/main:: exit 0node scripts/check-closing-keyword-parity.mjs:: exit 0node scripts/check-closing-keyword-parity.mjs --self-test:: exit 0node scripts/check-comment-mask-corpus.mjs:: exit 0node scripts/check-empty-changeset.mjs --base origin/main:: exit 0node scripts/check-empty-changeset.mjs --self-test:: exit 0node scripts/check-sdui-manifest.mjs:: exit 0node scripts/check-sdui-manifest.mjs --self-test:: exit 0node scripts/pm/release-rehearsal-clone.mjs --self-test:: exit 0node scripts/release-pending-publish.mjs --self-test:: exit 0pnpm --dir ui exec vitest run scripts/:: exit 0pnpm --filter @objectstack/spec run check:duration-unit-keys:: exit 0pnpm check:changeset-gate-self-tests:: exit 0pnpm check:closed-ceremony-text:: exit 0pnpm check:cross-package-test-inputs:: exit 0pnpm check:dispatcher-error-vocabulary:: exit 0pnpm check:driver-memory-census:: exit 0pnpm check:engine-double-contract:: exit 0pnpm check:gitlink-declared:: exit 0pnpm check:nul-bytes:: exit 0pnpm check:published-files:: exit 0pnpm check:query-options-erasure:: exit 0pnpm check:refd-timer-probe:: exit 0pnpm check:registry-log-declared:: exit 0pnpm check:rest-log-spy-declared:: exit 0pnpm check:skill-identifier-liveness:: exit 0pnpm check:test-source-alias:: exit 0pnpm check:tier-file-adoption:: exit 0pnpm check:type-check-coverage:: exit 0pnpm check:type-check-debt:: exit 0pnpm check:undeclared-dep-imports:: exit 0pnpm check:watch-hint-literal:: exit 0pnpm check:where-matcher:: exit 0pnpm --dir ui exec vitest run scripts/first exited 1:markdown-test-inputs.test.tsread the new test'sCHANGELOG.mdbadge fixture as a markdown input the ledger does not describe. The fixture now spells the pathCHANGELOG; re-run on the final head:Test Files 170 passed | 2 skipped (172), exit 0. Because that commit landed while the loop ran, the gates that read test files were re-run on the final head, all exit 0:check:test-source-alias,check:cross-package-test-inputs,check:nul-bytes,check:undeclared-dep-imports,check-comment-mask-corpus,check-sdui-manifest.node scripts/pm/dispatch-gates.mjs --ran:35 derived famil(ies) accounted for — 35 run, 0 NOT-MEASURED.Standing ui gates
node scripts/check-sdui-manifest.mjs(and--self-test): in the derived list above, exit 0.pnpm --dir ui check:new-line-citations:VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0.Dogfood battery: not owed (no
!in the title, no(narrowing)arm).Generated by Claude Code