Conversation
DocxDef/PptxDef/XlsxDef share a poster screenshot slot whose capture-only component renders the extracted structure's first unit — the document's opening text flow, the deck's title slide, the workbook's first sheet — as a page-shaped poster. Rasterization goes through the screenshot engine by design: there is no in-browser OOXML decode path, so the extracted rendering is the office viewer and the capture engine shoots it. The render is synchronous DOM over extracted fields, so the engine's settle is the readiness signal. keyBy 'file-content' + useAsThumbnail route it to the fitted cell through the view model's thumbnail seam, with the typed placeholder as the fallback. Part of CS-12527. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…x/xlsx The office poster capture always rendered — an oversize document the extractor skipped, or a format with no preview payload, still produced a filename-on-white page that persisted as `poster` and, through `useAsThumbnail`, displaced the typed fitted placeholder (format badge + count) in the thumbnail chain. Unlike the PDF sibling, it had no opt-out. Gate the capture on a renderable first unit: `hasPosterContent` holds only when the extracted structure carries document blocks, a titled/bulleted first slide, or a first sheet with rows. When it does not, the component keeps `data-screenshot-pending` standing so the engine's bounded wait declines the slot and the typed placeholder stays the tile. The signal is set once at the initial synchronous render off the getter — never flipped from an async continuation — so the tracked-update caveat does not apply. Fold the docx test into a per-family loop so the pptx (title slide) and xlsx (first sheet) template branches are each exercised against the same land-on-the-file-row contract, via the shared `writeAndSettle` helper. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014GsYGsuCqHJz9GAti4jheG
…ecline Declining the slot left the prerendered fitted tile an empty box: the render pass injects meta.screenshots from the declaration, so the fitted cell serves the poster URL regardless of capture outcome and its <img> has no error fallback. Instead, the capture component's no-first-unit branch now renders the same paper + format badge + structural count the fitted placeholder draws (badge/count logic shared via office-preview exports), so the always-served URL resolves to an informative tile and the prerendered and live paths agree. Covers the degenerate path with an in-test stored-zip OOXML fixture — an empty-body document that parses as a valid package but yields no preview — asserting the placeholder poster lands on the file row. Garbage bytes would not exercise the branch: a FileContentMismatchError falls the file back to plain FileDef, which declares no poster slot. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lukemelia
left a comment
There was a problem hiding this comment.
[Claude Code 🤖] Reviewed this as a re-land: whether the three commits still hold against the code that moved underneath them (the roster merge and file-content gate in getScreenshots, the declaration-injected meta.screenshots in the render route, the screenshotsMeta thumbnail seam, and the fitted stage's <img>), plus the hasPosterContent gate and the placeholder-branch test. I did not re-review the PDF poster machinery or the capture engine.
No blocking issues. The change lands cleanly on current main, and the settled decision — capture the typed placeholder rather than decline the slot — is what the code does and what the header says. Three non-blocking asks.
- The placeholder e2e proves the slot lands, not that the placeholder branch rendered; pin the gate with a host render test — thread on
declared-screenshots-file-test.ts. - The placeholder paper is now drawn twice; one component is the follow-up — thread on the
.placeholderCSS inoffice-captures.gts. - Document poster: a synthetic filename heading sits above a
titleblock that renders at body size — thread on the.pagetemplate inoffice-captures.gts.
PR text: the description's opening paragraph is delivery history; the current-shape rule for PR descriptions keeps only the note that the video and 3D poster branches stack on this one.
CI: Lint and all six realm-server shards are green on this head; host shards were still running when this was written and nothing in this diff touches host code.
Adjacent, out of scope: pdf-captures.gts's fail comment says an uncaptured poster URL is "a 404 the fitted cell's image fallback absorbs". FilePreviewStage renders generated-thumbnail with no error handler — the very fact this PR's header relies on — so the two sibling headers now contradict each other and the PDF one is the wrong one. For whoever touches the PDF capture next, not this PR.
| }); | ||
| } | ||
|
|
||
| test('an office file whose extraction yields no first unit captures the typed-placeholder poster', async function (assert) { |
There was a problem hiding this comment.
[Claude Code 🤖] Coverage, non-blocking: this pins "captures rather than declines", not the placeholder branch its name claims. Delete the hasPosterContent gate and the empty-body file takes the word branch — .page with the filename heading and zero blocks — which is still a PNG on the row and still the poster URL in the fitted HTML, so all four assertions stay green. The one thing the two branches differ on, what is in the pixels, is the one thing nothing observes.
The cheap pin is a host integration render of OfficePosterCapture next to the other content-only components in content-preview-components-test.gts: a plain { name: 'empty.docx' } model asserting .placeholder .ph-badge reads DOCX, and a model whose officeMetadata.previewJson carries one block asserting .page is present and .placeholder is not. That covers the gate in both directions without Chrome, and leaves this e2e its one real job — the slot must land.
There was a problem hiding this comment.
[Claude Code 🤖] Covered in 44bbaa8. office-poster-capture-test renders OfficePosterCapture directly and asserts which branch the extracted structure selects, in both directions: no metadata at all, metadata with facts but no preview payload, and a text-less first slide take the placeholder; a title-led flow, an untitled flow, a deck first slide and a workbook first sheet take their own first-unit branches.
It discriminates. Making hasPosterContent return true unconditionally turns exactly the three placeholder tests red (10 of 21 assertions), and restoring it returns the module to 21 of 21. This e2e keeps its own job — the slot must land on the file row — unchanged.
| max-width: 48px; | ||
| } | ||
|
|
||
| /* The typed-placeholder branch, mirroring the fitted cell's uncaptured |
There was a problem hiding this comment.
[Claude Code 🤖] Follow-up, non-blocking: the badge and count strings are shared, but the paper is now drawn twice — .off-fitted / .paper* in office-preview and .ph-* here: the same 3:4 vs 4:3 aspect switch, the same ruled-lines and grid overlays, the same badge chrome, restated as literals. The "cannot drift" guarantee stops at the strings; the next tweak to the lined-paper overlay or the deck aspect lands on one copy.
One home would be a small placeholder-paper component (the markup and scoped CSS from office-preview, tokens keeping their literal fallbacks) that both the fitted branch and this capture render, with the capture wrapping it in a root that pins --border, --card, --fd-slate and the rest to the literals used here. That keeps the capture theme-independent — the reason this copy exists — without a second drawing. A follow-up, not this PR.
There was a problem hiding this comment.
[Claude Code 🤖] Taken in 44bbaa8. OfficePlaceholder in office-preview is now the one drawing — paper, per-kind aspect, the ruled-line and grid overlays, badge and count — and both the fitted branch and the capture's no-first-unit branch render it. The .ph-* copy is gone.
The capture stays theme-independent without restating the drawing: the placeholder's colors read theme tokens with literal fallbacks, and the capture pins those tokens on its .placeholder wrapper. The badge and count helpers are module-private again, since the component is now the shared surface.
| <div class='page'> | ||
| <div class='page-title'>{{this.heading}}</div> |
There was a problem hiding this comment.
[Claude Code 🤖] Design nit, non-blocking: the document poster is the one branch that does not render the first unit as-is. It prepends a bold heading (core title, else the filename) that no first page carries, and a first block the extractor classified title falls through to body size beneath it — .page-block[data-style='heading'] is styled, data-style='title' is not — so a titled document reads as filename big, title small. The deck branch already treats heading as a fallback only. Suggest the same here: a [data-style='title'] rule at or above the heading size, and the synthetic heading only when the first block is not a title.
There was a problem hiding this comment.
[Claude Code 🤖] Fixed in 44bbaa8, both halves. A [data-style="title"] rule now renders a title block at the heading's own size and weight, and the synthetic heading is drawn only when the first extracted block is not a title — the same fallback shape the deck branch already used.
Pinned by the title-led and untitled document cases in the new host test; forcing the heading to always render turns the title case red.
Preview deploymentsHost Test Results 1 files ±0 1 suites ±0 2h 54m 4s ⏱️ + 13m 13s Results for commit b2a9a24. ± Comparison against earlier commit 44bbaa8. Realm Server Test Results 1 files ±0 241 suites ±0 1h 23m 39s ⏱️ + 7m 54s Results for commit b2a9a24. ± Comparison against earlier commit 44bbaa8. |
… its title Three review findings on the office poster slot. The typed placeholder was drawn twice — once as the fitted cell's `.off-fitted`/`.paper` in office-preview, once as the capture's `.ph-*` copy in office-captures — with only the badge and count strings shared. The paper shape, the per-kind aspect switch, the ruled-line and grid overlays and the badge chrome were restated as literals, so the next tweak to any of them would have landed on one copy. Both now render one `OfficePlaceholder` component. Its colors read theme tokens with literal fallbacks; the capture pins those tokens on its own wrapper, which is what keeps a capture from following the viewer's theme without redrawing it. The document poster prepended a synthetic heading (core title, else the filename) above a text flow that may open with its own title block, and that block rendered at body size because only `heading` was styled. A document whose first unit is a title now leads with it, styled as a title; the synthetic heading stands in only when the flow carries none. The poster capture had no test of its own: the realm-server e2e asserts the slot lands on the file row, which both branches satisfy, so removing the content gate left it green. A host integration test renders the capture component directly and pins which branch each extracted structure selects, in both directions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Looking at these templates, I wonder if you have the latest skills. Pull the latest into the packages/skills-realm/contents and ask ai to update templates based on boxel skills. |
The UI guidelines' token contract says a contract token never carries a literal fallback, a plain color is set through background-color, and the family's out-of-contract `--fd-*` tokens resolve once on a component root rather than restating a literal at every use. These templates predated that and did all three the old way. Both now resolve `--fd-*` on their root — the badge onto `--tooltip`, the one inverted pairing the theme guarantees — and read the contract bare below. The viewer takes the theme's own ladders: the eyebrow role for the tracked-out mono kickers, the label role for the sheet tabs, the spacing and radius scales, the shadow scale for elevation, and nothing at all for `.doc-title` / `.doc-heading`, which are an h1 and an h2 the container already gives the heading roles to. The poster capture keeps fixed values, since a capture keyed on file content must not follow the viewer's theme — but it pins them once as tokens on the capture root instead of scattering hex through the rules, so the drawing reads the same tokens it would under a live theme, and its fallback partners are pinned too so no chain can escape. The format badge becomes one component rendered by the placeholder paper and the viewer header alike, the same treatment the placeholder itself already had. Sheet tabs gained the `aria-selected` their `role='tab'` implies. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
[Claude Code 🤖] Pulled What the skills changed here
The capture is the one place that can't follow the theme, since the poster is keyed on file content and two viewers must get the same image. It keeps fixed values, but now pins them as tokens on the capture root and reads tokens below, instead of scattering hex through the rules. Statically checked: every token the capture subtree reads is pinned, including each fallback partner. Verified against a dev stack, not just by eye — computed styles in a real render: badge 11px on Two judgment calls, flagged rather than silently taken:
One thing I could not bring into line: |
| /* A capture keyed on file content must come out the same for every | ||
| viewer, so the theme's tokens are pinned here, once, on the capture | ||
| root: the drawing below — and `OfficePlaceholder`, which renders into | ||
| the same root — reads them exactly as it does under a live theme, and | ||
| no viewer's theme can reach them. Each fallback partner is pinned | ||
| too, so no chain can escape to the viewer's theme if a token ahead of | ||
| it is ever dropped. The type scale is likewise held here rather than | ||
| scattered through the rules: these are the fixed proportions of a | ||
| 170×250 page, not a themed ladder. */ |
There was a problem hiding this comment.
Not a blocker but if it's NOT meant to be themed and if there's a valid reason for not using preexisting boxel variables from boxel-ui variables.css, I'd rather have local variable names used with --poster prefix so there's no confusion. The mix of px and rem values is also strange. We're saying the font sizes are scalable but not the margins or paddings. I'll make a note.
What this does
DocxDef/PptxDef/XlsxDefshare aposterscreenshot slot (170×250 @ dsf 2,keyBy: 'file-content',useAsThumbnail). The capture-only component (file-formats/office-captures) renders the extracted structure's first unit as a page-shaped poster: a document's opening text flow, a deck's title slide, a workbook's first sheet.<img>has no error fallback, so a declined slot would leave the tile empty. Capturing the placeholder keeps the prerendered and live tiles agreeing.OfficePlaceholder(exported fromfile-formats/office-preview) is the paper, per-kind aspect, badge and structural count, rendered both by the fitted cell for an uncaptured file and by the capture's no-first-unit branch. Its colors read theme tokens with literal fallbacks, and the capture pins those tokens on its own wrapper, so a capture never follows the viewer's theme.The video and 3D poster branches stack on this one.
Test plan
Realm-server e2e (extends the file-capture suite): writes the licensed
docx-simple.docx,pptx-simple.pptxandxlsx-simple.xlsxfixtures, asserts each family's poster manifest entry and that the prerendered fitted HTML embeds the durable poster URL; writes an empty-body.docxand asserts the placeholder poster lands. CI-validated (the cached-fixture suite doesn't run locally).Host integration (
office-poster-capture-test): renders the capture component directly and pins which branch each extracted structure selects — no first unit, facts but no preview payload, a title-led flow, an untitled flow, deck and workbook first units, and a text-less first slide. Verified to discriminate: neutralizing the content gate turns the three placeholder tests red, and neutralizing the title-lead rule turns the title test red.🤖 Generated with Claude Code