Markdown format: full rendering pipeline and tooling (CS-10780 → CS-10798)#4412
Markdown format: full rendering pipeline and tooling (CS-10780 → CS-10798)#4412
Conversation
Add `markdown` to the `Format` type union and the `formats` array so downstream format resolution (via `cardOrField[effectiveFormat]` in `getBoxComponent()`), the format chooser UI, and prerender routes can pick it up automatically. Foundational unblocker for the Markdown Format project (CS-10780). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Host Test Results 1 files ± 0 1 suites ±0 2h 31m 55s ⏱️ -14s For more details on these errors, see this check. Results for commit 1f0eceb. ± Comparison against base commit 5d53272. This pull request removes 107 and adds 130 tests. Note that renamed tests count towards both.♻️ This comment has been updated with latest results. |
…781) Wrap markdown-format renders in a `<div data-markdown-render-container>` with `white-space: pre` in the prerender render route template. The dedicated data attribute gives downstream extraction a tight target so surrounding route-template whitespace does not leak into the captured markdown string, and `white-space: pre` keeps authored newlines and indentation intact. Also teach `defaultFieldFormats` to recurse in `markdown` when the containing format is `markdown` so `<@fields.x />` delegation inside a markdown template composes markdown output from its children, not embedded/fitted HTML. Other formats are unaffected: non-markdown renders still use the existing `<@model.Component>` invocation with no wrapper. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Teach the headless-browser prerender capture to handle the `markdown` format end-to-end on the extraction side: * `renderHTML` selects `textContent` as the capture mode for `markdown` (instead of `innerHTML`/`outerHTML`), and skips `cleanCapturedHTML` on the result since the cleanup regex only makes sense for HTML. * `captureResult`'s `textContent` branch now prefers the `[data-markdown-render-container]` element (CS-10781) when present, so the extracted string is the raw markdown body without surrounding route-template whitespace. The existing `renderMeta` caller also uses `textContent` mode; its markup has no markdown container, so the fallback to `resolvedElement.textContent` keeps that flow intact. Note: this lands the rendering-pipeline half of CS-10782. HTTP serving with `content-type: text/markdown`, storage in `boxel_index`, and the end-to-end integration test require a DB migration + new HTTP route, which will land in follow-ups. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
markdown in the Format type and formats array
Preview deployments |
Exports `markdownEscape` from `@cardstack/boxel-ui/helpers` so card authors can safely interpolate user-supplied content into a `static markdown` template without accidentally triggering formatting. The helper emits CommonMark backslash escapes for all ASCII-punctuation metacharacters (`* _ \` [ ] ( ) < > | ~ ! # + -` and `\\` itself), plus a line-start pass that escapes numeric ordered-list prefixes like `1.`. Null/undefined inputs return `''`; non-string inputs are coerced via `String()`. The return value is a plain string (not a SafeString) so Glimmer HTML-escapes it in the DOM, the CS-10782 textContent capture decodes the entities, and the markdown parser sees the literal backslash escapes. Unit tests in `packages/boxel-ui/test-app/tests/unit/markdown-escape-test.ts` cover every character class called out by the Linear acceptance criteria plus null/undefined/non-string/empty-string inputs. Note: following the existing boxel-ui helper convention, this is an explicit-import helper (the codebase has no auto-scope mechanism for card templates); the Linear issue's "without explicit import" phrasing is reconciled to match the codebase pattern used by `eq`, `cn`, etc. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Default `static markdown` template that renders the HTML fallback (`isolated` for cards, `embedded` for fields) into a hidden source container and converts its innerHTML to markdown via turndown + @joplin/turndown-plugin-gfm. The converter is installed on `globalThis.__boxelHtmlToMarkdown` from the host bundle, so `packages/base` has zero dependency on turndown and the realm-server import graph stays clean (enforced by a grep-based isolation test). Subclass overrides of `static markdown` still win via the existing format-resolver bracket-notation lookup. The prerender pipeline now prefers `[data-markdown-output]` over `[data-markdown-render-container]` so the hidden HTML source doesn't contaminate the captured text. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement the AC table from CS-10785 by adding `static markdown` slots to the primitive field types in packages/base/card-api.gts: - StringField, ReadOnlyField, NumberField → markdownEscape the value, so metacharacters like `*`, `[`, `]`, `#`, and line-start `1.`/`-` don't trigger formatting when interpolated into a surrounding document. - TextAreaField → escape per line, then convert single `\n` to a CommonMark hard break (` \n`) so multi-line text renders as stacked lines instead of collapsing into one paragraph. - MarkdownField → raw passthrough (overrides the StringField inherited template) so author-written markdown is not double-escaped. - CSSField → emit a fenced ```css block; the fence width is the longest embedded backtick run + 1 (min 3) so pathological content can't close the block prematurely. - MaybeBase64Field → emit `[binary content]` for `data:` URIs to keep base64 payloads out of the markdown output; non-base64 strings fall back to escaped text. The StringField and TextAreaField slots are typed as `BaseDefComponent` to prevent TS from forcing structural compatibility on subclass overrides (MaybeBase64Field, MarkdownField, CSSField). Tests in packages/host/tests/integration/components/field-markdown-primitives-test.gts verify each primitive's markdown output, including escape behavior, hard breaks, fenced-block fence-width adjustment for embedded backticks, and a composition test that renders all primitives in a single card. Drive-by fix to the CS-10784 subclass-override test, which was querying `[data-markdown-render-container]` — a wrapper that only exists on the render route, not in rendering tests. Replaced with `this.element.textContent` to read the actual rendered markdown. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two corrections after running the field-markdown-primitives tests against the freshly built host bundle: 1. NumberField negative/decimal expectation — `markdownEscape` only escapes line-anchored numeric prefixes (`/^(\s*\d+)\./gm`). For `-3.14`, after the always-escape pass produces `\-3.14`, the digits are no longer at the start of a line, so the `.` correctly stays unescaped. Updated the expected value from `\-3\.14` → `\-3.14`. 2. TextAreaField / MarkdownField / CSSField tests — were comparing raw `textContent` against an exact string, but the FieldComponent wrapper chain (CardContextConsumer → ... → DefaultFormatsProvider → Markdown) pads each level with template-driven whitespace that ends up around the rendered markdown content. Switched to the `readMarkdown` helper (which trims leading/trailing whitespace) and verified the inner content is exactly what the static markdown templates emit. All 13 field-markdown-primitives tests pass, and the 7 CS-10784 markdown-fallback tests still pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds field-specific `static markdown` slots to 19 specialized fields spanning date, scalar, link, and composite categories. Introduces `packages/base/markdown-helpers.ts` with shared `formatDateForMarkdown`, `formatDateTimeForMarkdown`, `formatDateRangeForMarkdown`, and `markdownLink` helpers so date formatting stays consistent across DateField/DateTimeField/DateRangeField and link construction is centralized (text escaped, href URL-encoded, parens quoted). Covers: - Date family: Date, DateTime, DateRange (shared formatter) - Scalars: Boolean, BigInteger, PhoneNumber, Color, Percentage, Country, LLMModel, EthereumAddress - Links: Email, Url, Website (markdown link format) - Composites: Address (hard-break rows), Coordinate, CodeRef (inline code span), Base64Image (binary placeholder), RichMarkdown (content passthrough) Tests live in packages/host/tests/integration/components/ field-markdown-specialized-test.gts (20 tests). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds field-specific `static markdown` slots to domain/reference, file, theme, and brand fields. Extends `packages/base/markdown-helpers.ts` with `fencedCodeBlock` (auto-widens the fence beyond any interior backtick run) and `markdownImage` helpers. Covers: - Reference/relation: RealmField (self-linked), ResponseField (status placeholder) - FileDef subclasses: MarkdownDef (passthrough), TsFileDef, GtsFileDef, JsonFileDef, CsvFileDef, TextFileDef (fenced code with per-subclass `static markdownLanguage`), ImageDef (markdown image reference) - Theme/style: CSSValueField (backtick fence with padding when value touches a backtick), TypographyField (bulleted properties), ThemeVarField/ThemeTypographyField (bulleted CSS variable entries), StructuredTheme (title/description/version + Typography + Root Variables sections) - Brand: MarkField (markdown image), BrandLogo (bulleted mark URLs with labels), BrandFunctionalPalette (bulleted palette colors) - enumField factory: renders the matching option's label (falls back to escaped raw value) Tests live in packages/host/tests/integration/components/ field-markdown-domain-test.gts (25 tests). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a `markdown` column to `boxel_index` and `boxel_index_working` and plumb the new format end-to-end: prerender step, `RenderResponse` / `FileRenderResponse` payload, writer `InstanceEntry` / `FileEntry` input, indexer destructure, and `IndexedInstance` / `IndexedFile` read results. Regenerate the SQLite schema snapshot. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Register a `GET /.*` route that dispatches on `Accept: text/markdown` and returns the indexed markdown body for a card instance. Returns 404 when the card is unknown, 406 when the markdown slot is unavailable (index error or null column), and responds with `Content-Type: text/markdown; charset=utf-8` on success. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Covers the full CS-10782..CS-10787 + CS-10798 surface via HTTP: format resolution, whitespace preservation, field delegation, MarkdownField passthrough, markdownEscape, HTML-to-markdown fallback, override precedence, frontmatter preservation, nested card composition, content-type header, and cache invalidation after a source rewrite. Uses `setupPermissionedRealmCached` with all card sources and instances seeded into the template DB so per-test cost is a fast DB restore. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use namespace imports (import type * as X) for base realm modules in markdown field tests to access .default correctly - Fix StructuredTheme test to set cardInfo instead of computed cardTitle - Add markdown-fallback and markdown-helpers to expected deps in realm-indexing tests - Add markdown: null to prerender-proxy-test mock and assertion - Use trim() instead of trimEnd() in markdown endpoint frontmatter test Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…10791) Wire markdown format into code-mode preview panel and playground with a dedicated MarkdownPreview component that captures raw markdown text from the card's markdown template and offers Source (monospace pre) and Rendered (markdownToHtml) toggle views. Add acceptance test assertions for markdown format button and integration tests for toggle behavior. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a copyCardMarkdownToClipboard utility that fetches the card's markdown representation from the realm server (Accept: text/markdown) and writes it to the clipboard. Wire it into the interact-mode context menu via getDefaultCardMenuItems. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the direct fetch in copyCardMarkdownToClipboard with a proper command that uses NetworkService.authedFetch, ensuring authentication headers are sent correctly. Remove the boxel-ui utility function in favor of the command pattern. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TurndownService doesn't remove <style> or <script> tags by default, so CSS text content from card templates was leaking into the markdown output as plain text. Add converter.remove(['style', 'script']) to strip these elements during HTML-to-markdown conversion. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… test expected refs The new CopyCardAsMarkdownCommand import and ClipboardCopy icon import in menu-items.ts added two new module references that the realm indexing tests need to expect. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Card HTML often wraps link text in nested elements (icon spans, divs) with newlines between them. Turndown preserves that internal whitespace, producing multiline `[\n Contact](url)` which is invalid markdown link syntax. Added a compactLinks turndown rule that collapses whitespace in link text to a single space. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the HTML-to-markdown fallback encounters a card container (identified by the production-safe data-boxel-card-id attribute), it now emits a card reference directive instead of inlining the full card HTML: - Atom-format cards → `:card[CARD_ID]` (inline directive) - Fitted/embedded cards → `::card[CARD_ID]` (block directive) Added data-boxel-card-id and data-boxel-card-format attributes to the field component's CardContainer alongside the existing data-test-* attrs, since the latter are stripped by ember-test-selectors in production builds. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… panel The markdown preview "Rendered" tab now uses a dedicated RenderedMarkdown component that mirrors the base-realm MarkdownTemplate. It shares the same utilities (markdownToHtml, extractCardReferenceUrls, cardTypeName) and follows the same rendering pattern: convert markdown to HTML with BFM extensions, capture card-reference placeholders via a modifier, load referenced cards from the store, and render them into BFM slots using CardRenderer with #in-element. Unresolved references show as muted Pill badges. CSS is mirrored from the base-realm MarkdownTemplate to keep visual parity with .md file rendering. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
12 integration tests for the RenderedMarkdown component covering: - Basic markdown rendering (headings, paragraphs, lists, links, code) - Table wrapping - BFM inline :card[URL] placeholder creation - BFM block ::card[URL] placeholder creation - URL text stripping from BFM placeholders - Unresolved card references showing pill fallback (inline and block) - Multiple card references creating separate placeholders - Card references inside code blocks not being converted - Block card reference size spec data attributes Fixed CSS bleeding by wrapping markdown styles in @layer baseComponent, matching the base-realm MarkdownTemplate. This ensures unlayered card styles take cascade precedence over the generic markdown typography. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Consume CardContext (provided by the preview panel) and apply its cardComponentModifier to BFM card slot elements. This registers the embedded cards with the ElementTracker/Overlays system, which adds click handlers and pointer cursor — matching the behavior of cards embedded in .md files and AI assistant messages. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Overlays component was only rendered in the non-markdown format branches of the preview panel. This adds it to the markdown branch so that inline and block card references in the markdown preview are clickable and navigate to the referenced card in code mode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The .base-overlay CSS was missing pointer-events: none, which caused the overlay div to intercept mouse events on the card elements underneath. This was never noticed before because all other usages go through OperatorModeOverlays which uses .actions-overlay (which already has pointer-events: none). The base Overlays class is now used directly in the markdown preview panel, where the bug surfaced. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Now that the base .base-overlay class includes pointer-events: none, the spec-preview-overlay no longer needs to declare it separately. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add custom `static markdown` template to RatingsSummary with ASCII star rendering - Add 'markdown' to field format chooser options in playground panel - Widen playground instance-chooser container (380px → 460px) to fit all format buttons - Wrap markdown preview in CardContainer for white background in playground - Update field playground test to cover markdown format selection Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add four new helpers to `packages/base/markdown-helpers.ts`: - `markdownLinkForCard(card, text?)` — renders `[text](card.id)` - `markdownLinksForCards(cards, options?)` — list or inline card links - `markdownEmbedForCard(card, options?)` — renders `:card[url]` or `::card[url | spec]` - `markdownEmbedsForCards(cards, options?)` — multiple BFM card embeds Add custom `static markdown` template to BlogPost exercising all helpers: links for authors/categories/blog, image for featured image, embeds for author bios in footer. 26 unit tests covering all helpers, edge cases, and null handling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix prettier formatting across markdown-related files - Remove unused imports (waitFor, Format) - Prefix unused parameter with underscore (_options) - Add scoped attribute to style tag in markdown-fallback-test - Fix DEFAULT_CARD_CONTEXT type in rendered-markdown.gts - Update realm-indexing tests to include data-boxel-card-id/format attrs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The `#` and `{{@model.title}}` were on separate lines, producing
`#\nTest Title` instead of `# Test Title`. ATX headings require
the marker and text on the same line.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
LivePrerenderedSearchResource had no destructor, unlike its sibling PrerenderedSearchResource. Without cleanup, the buildPrerenderedCards task was never cancelled on destruction, _instances (TrackedArray of PrerenderedCard objects with HTML strings) was never cleared, and in-flight async renders held the resource alive past component teardown. This caused ~58MB/test retention in create-listing-modal tests, OOMing shard 15 in CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Markdown ATX headings require `#` at the start of the line. Prettier reformats the template content with indentation, which breaks heading parsing. Add prettier-ignore to preserve the intentional formatting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR lands end-to-end support for a new markdown render format across the stack: template authoring (static markdown), prerender capture, persistence in boxel_index, HTTP serving via Accept: text/markdown, UI preview tooling, and helper utilities/tests to keep the pipeline robust.
Changes:
- Adds a first-class
markdownformat (type registration, host render wrapper, prerender capture, and new markdown-aware helpers/templates). - Persists prerendered markdown in
boxel_indexand serves it from the realm via anAccept: text/markdownendpoint. - Adds extensive unit/integration/e2e coverage plus UI affordances (preview panel + “Copy as Markdown”).
Reviewed changes
Copilot reviewed 96 out of 98 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Locks new markdown conversion deps (turndown + GFM plugin + types). |
| packages/workspace-sync-cli/tests/helpers/start-test-realm.ts | Updates test realm payload to include markdown. |
| packages/runtime-common/supported-mime-type.ts | Adds text/markdown to supported MIME types. |
| packages/runtime-common/realm.ts | Adds Accept: text/markdown card endpoint backed by indexed markdown. |
| packages/runtime-common/index.ts | Extends render response types to include markdown. |
| packages/runtime-common/index-writer.ts | Writes markdown into index rows for instances/files. |
| packages/runtime-common/index-structure.ts | Adds markdown column to index table typing. |
| packages/runtime-common/index-runner/file-indexer.ts | Plumbs markdown from render into file indexing. |
| packages/runtime-common/index-runner/card-indexer.ts | Plumbs markdown from render into card indexing. |
| packages/runtime-common/index-query-engine.ts | Returns markdown from index queries + includes it in SELECT. |
| packages/runtime-common/formats.ts | Registers markdown in Format and formats list. |
| packages/runtime-common/error.ts | Adds a 406 notAcceptable() response helper. |
| packages/realm-server/tests/realm-endpoints/markdown-test.ts | New e2e tests validating served markdown behavior. |
| packages/realm-server/tests/prerendering-test.ts | Updates prerender test fixtures to include markdown. |
| packages/realm-server/tests/prerender-proxy-test.ts | Updates proxy test fixtures to include markdown. |
| packages/realm-server/tests/markdown-fallback-server-isolation-test.ts | Ensures turndown deps don’t leak into server-side packages. |
| packages/realm-server/tests/index.ts | Registers new markdown-related tests. |
| packages/realm-server/prerender/utils.ts | Captures textContent for markdown; narrows capture target. |
| packages/realm-server/prerender/render-runner.ts | Adds markdown render step for card/file prerendering. |
| packages/postgres/migrations/1776285887495_add-markdown-to-index.js | Adds markdown column to boxel_index + working table. |
| packages/host/types/@joplin/turndown-plugin-gfm/index.d.ts | Adds a type shim for the untyped CJS plugin. |
| packages/host/tests/unit/markdown-helpers-test.ts | Unit tests for markdown link/embed helper functions. |
| packages/host/tests/unit/index-writer-test.ts | Verifies markdown persistence round-trip through index writer/query. |
| packages/host/tests/unit/card-def-menu-items-test.ts | Ensures “Copy as Markdown” appears in menu items. |
| packages/host/tests/integration/realm-indexing-test.gts | Updates integration snapshots for new attributes/modules/icons. |
| packages/host/tests/integration/components/rendered-markdown-test.gts | Tests markdown→HTML rendering + BFM card placeholders. |
| packages/host/tests/integration/components/markdown-preview-test.gts | Tests preview panel source/rendered toggling. |
| packages/host/tests/integration/components/markdown-fallback-test.gts | Tests default HTML→markdown fallback behavior. |
| packages/host/tests/integration/components/field-markdown-specialized-test.gts | Tests specialized field markdown templates. |
| packages/host/tests/integration/components/field-markdown-primitives-test.gts | Tests primitive field markdown templates. |
| packages/host/tests/helpers/base-realm.ts | Exposes CSSField for new markdown field tests. |
| packages/host/tests/acceptance/code-submode/field-playground-test.gts | Adds markdown format to field playground acceptance coverage. |
| packages/host/tests/acceptance/code-submode/card-playground-test.gts | Adds markdown format to card playground acceptance coverage. |
| packages/host/tests/acceptance/code-submode-test.ts | Adds markdown/head format chooser assertions. |
| packages/host/package.json | Adds turndown + plugin + types to host deps. |
| packages/host/config/schema/1776285887495_schema.sql | Adds markdown column to host schema snapshots. |
| packages/host/app/templates/render/html.gts | Adds whitespace-preserving wrapper for markdown renders. |
| packages/host/app/resources/live-prerendered-search.ts | Adds destructor cleanup to prevent task/leak issues. |
| packages/host/app/lib/html-to-markdown.ts | Implements browser-only turndown conversion and global hook. |
| packages/host/app/instance-initializers/register-html-to-markdown.ts | Ensures HTML→MD converter is loaded at boot. |
| packages/host/app/components/operator-mode/preview-panel/markdown-preview.gts | Adds markdown preview component (source/rendered). |
| packages/host/app/components/operator-mode/preview-panel/index.gts | Wires markdown preview into preview panel. |
| packages/host/app/components/operator-mode/overlays.gts | Sets base overlays to not intercept pointer events. |
| packages/host/app/components/operator-mode/code-submode/spec-preview.gts | Adjusts overlay styling for spec preview. |
| packages/host/app/components/operator-mode/code-submode/playground/playground-preview.gts | Adds markdown preview rendering path in playground. |
| packages/host/app/components/operator-mode/code-submode/playground/playground-panel.gts | Adds markdown to field formats; widens chooser UI. |
| packages/host/app/components/card-prerender.gts | Adds markdown to client-side prerendering results. |
| packages/host/app/commands/index.ts | Registers new “copy card as markdown” host command module. |
| packages/host/app/commands/copy-card-as-markdown.ts | Implements “Copy as Markdown” command via markdown endpoint. |
| packages/experiments-realm/ratings-summary.gts | Adds markdown rendering for RatingsSummary field. |
| packages/experiments-realm/blog-post.gts | Adds markdown template using new markdown helpers. |
| packages/experiments-realm/Spec/fields/rating-field.json | Updates spec JSON for contained examples/metadata changes. |
| packages/experiments-realm/BlogPost/mad-as-a-hatter.json | Updates BlogPost JSON structure/meta/relationships. |
| packages/boxel-ui/test-app/tests/unit/markdown-escape-test.ts | Adds unit coverage for markdown escaping helper. |
| packages/boxel-ui/addon/src/helpers/markdown-escape.ts | Implements CommonMark/GFM escaping helper. |
| packages/boxel-ui/addon/src/helpers.ts | Exports markdownEscape from the helpers barrel. |
| packages/boxel-cli/tests/helpers/integration.ts | Updates noop prerenderer to include markdown. |
| packages/base/website.gts | Adds markdown output for WebsiteField. |
| packages/base/url.gts | Adds markdown output for UrlField. |
| packages/base/typography.gts | Adds markdown output for TypographyField. |
| packages/base/ts-file-def.gts | Adds markdown code-fence output + language tagging. |
| packages/base/text-file-def.gts | Adds markdown fenced-block output for text files. |
| packages/base/structured-theme.gts | Adds concise markdown summary for themes. |
| packages/base/structured-theme-variables.gts | Adds markdown output for theme variable fields. |
| packages/base/rich-markdown.gts | Adds passthrough markdown output for RichMarkdownField. |
| packages/base/response-field.gts | Adds placeholder markdown output for ResponseField. |
| packages/base/realm.gts | Adds markdown output (self-link) for realm URL field. |
| packages/base/phone-number.gts | Adds markdown output (tel link) for phone numbers. |
| packages/base/percentage.gts | Adds markdown output for percentages. |
| packages/base/menu-items.ts | Adds “Copy as Markdown” menu item. |
| packages/base/markdown-helpers.ts | Adds shared markdown helpers (links/embeds/images/fences/dates). |
| packages/base/markdown-file-def.gts | Adds markdown passthrough for markdown files. |
| packages/base/llm-model.gts | Adds markdown output for LLM model field. |
| packages/base/json-file-def.gts | Adds markdown fenced JSON output for JSON files. |
| packages/base/gts-file-def.gts | Sets markdown language for GTS modules. |
| packages/base/field-component.gts | Adds markdown recursion defaults + card container data attrs. |
| packages/base/ethereum-address.gts | Adds markdown output for Ethereum address. |
| packages/base/enum.gts | Adds markdown output selecting enum label. |
| packages/base/email.gts | Adds markdown mailto-link output for email. |
| packages/base/default-templates/markdown-fallback.gts | Adds default HTML→markdown fallback template. |
| packages/base/datetime.gts | Adds markdown output for DateTimeField using shared formatter. |
| packages/base/date.gts | Adds markdown output for DateField using shared formatter. |
| packages/base/date-range-field.gts | Adds markdown output for DateRangeField using shared formatter. |
| packages/base/csv-file-def.gts | Adds markdown fenced CSV output for CSV files. |
| packages/base/css-value.gts | Adds inline-code markdown output for CSS values. |
| packages/base/country.gts | Adds markdown output for country display name. |
| packages/base/coordinate.gts | Adds markdown output for coordinates. |
| packages/base/color.gts | Adds markdown output for color string (escaped). |
| packages/base/code-ref.gts | Adds inline-code markdown output for code refs. |
| packages/base/card-api.gts | Wires default markdown fallbacks + many field markdown templates. |
| packages/base/brand-logo.gts | Adds markdown output for brand logo URLs and mark image. |
| packages/base/brand-functional-palette.gts | Adds markdown output for palette entries. |
| packages/base/boolean.gts | Adds markdown output for booleans. |
| packages/base/big-integer.gts | Adds markdown output for big integers (escaped). |
| packages/base/base64-image.gts | Adds placeholder markdown output (no base64 payload). |
| packages/base/address.gts | Adds markdown output for address rows with hard breaks. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The previous commit incorrectly assumed .base-overlay's pointer-events rule would apply to spec-preview overlays, but Overlays uses @overlayClassName as a replacement for the default 'base-overlay' class, not an addition. Without this rule, spec-preview overlays intercept scroll/hover events on the underlying card content. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Landing the complete Markdown Format project on a single branch. Each commit is self-contained.
Issues
markdownin theFormattype andformatsarraymarkdownformattextContentformarkdownformatmarkdownEscapetemplate helperCardDef/FieldDef/FileDefstatic markdowntemplates for primitive fieldsstatic markdowntemplates for specialized fieldsstatic markdowntemplates for domain fieldsboxel_indexCommits
1. CS-10780 — Register
markdownin Format'markdown'to theFormattype union and theformatsarray inpackages/runtime-common/formats.tsswitchonFormat; the two with meaningful semantics (defaultFieldFormatsandcaptureModeForFormat) both havedefaultbranches that absorbed the new variant cleanly2. CS-10781 — Whitespace-preserving container
packages/host/app/templates/render/html.gts: wrap markdown-format renders in<div data-markdown-render-container>withwhite-space: prein a scoped<style>blockpackages/base/field-component.gts:defaultFieldFormatsnow returns{ fieldDef: 'markdown', cardDef: 'markdown' }when the containing format ismarkdown, so<@fields.x />delegation composes markdown output from children3. CS-10782 — Prerender pipeline extraction
packages/realm-server/prerender/utils.ts:renderHTMLnow selectstextContentas the capture mode formarkdown, skipscleanCapturedHTML.captureResult'stextContentbranch prefers[data-markdown-output]→[data-markdown-render-container]→ resolved element4. CS-10783 —
markdownEscapetemplate helperSafeString) so Glimmer HTML-escapes it in the DOM; thetextContentcapture decodes the entities before the markdown parser sees the output5. CS-10784 — HTML-to-markdown fallback on
CardDef/FieldDef/FileDefDefaultMarkdownFallbackTemplaterenders the HTML fallback format into a hidden[data-markdown-fallback-source]div, converts viaglobalThis.__boxelHtmlToMarkdown, emits into[data-markdown-output]@joplin/turndown-plugin-gfmsingleton with custom rules for card containers (:card[id]/::card[id]), compact links, and style/script strippingMutationObserverre-converts when the source subtree mutates (async data loads, linked cards resolve)6. CS-10785 — Default
static markdowntemplates for primitive fieldsStringField,ReadOnlyField,NumberField→markdownEscapeTextAreaField→ escape per line + hard breaksMarkdownField→ raw passthroughCSSField→ fenced```cssblock with adaptive fence widthMaybeBase64Field→[binary content]fordata:URIs7. CS-10786 — Explicit templates for specialized fields
BooleanField,CodeRefField,DateField,DatetimeField,BigIntegerField,EthereumAddressField8. CS-10787 — Explicit templates for domain fields
9. CS-10796 — Persist markdown in
boxel_indexmarkdowncolumn toboxel_indexIndexQueryEnginereturns markdown in search results10. CS-10798 — Serve card markdown via HTTP endpoint
content-type: text/markdown11. CS-10789 — E2E tests for the markdown rendering pipeline
12. CS-10790/CS-10791 — Markdown preview panel
MarkdownPreviewcomponent with source/rendered togglestatic markdowntemplatemarked, renders embedded:card/::carddirectives viaRenderedMarkdowncomponent13. CS-10794 — "Copy as Markdown" command
CopyCardAsMarkdownCommandfetches markdown from the realm server endpoint and copies to clipboard14. CS-10797 — Markdown helpers for card links and BFM card embeds
markdownLinkForCard(card, text?)— single card link[title](id)markdownLinksForCards(cards, { style })— list or inline card linksmarkdownEmbedForCard(card, { kind, size })—:card[id]or::card[id | size]markdownEmbedsForCards(cards, { separator })— multiple card embedsstatic markdowntemplates onRatingsSummaryandBlogPostin experiments-realm exercising all four helpers15. Playground + polish
'markdown'to the field playground format chooserMarkdownPreviewinCardContainerfor proper white backgroundVerification
pnpm lint:typespasses for host, runtime-common, realm-server, boxel-uipnpm lint:js+pnpm lint:hbsclean🤖 Generated with Claude Code