Markdown slideshows: deck design system and present mode - #189
Merged
Conversation
…ring Phase 1 of markdown slideshows (design: CoPlan plan 01a01696-aad6, rev 4). A deck is a rendering convention over the plan's single markdown blob — nothing new is persisted, so versioning, diffs, and comment anchors keep working unchanged. - PlanType gains a behavior column (document/slideshow); Plan#slideshow? delegates to it, so retyping a plan converts it. Ships a Slideshow default type (installable via coplan:plan_types:install_defaults) whose template teaches the conventions, plus a seeded showcase deck. - Slideshows::Split turns markdown into slides at top-level `---` breaks (AST-driven: fences, blockquotes, setext, `***` never split), extracts <!-- notes: --> speaker notes, and gathers footnote/link-reference definitions as positioned blocks so slides can render in isolation. Candidate definitions are validated by a parser round-trip, so prose lookalikes are never hoisted onto other slides. - render_slideshow renders each slide through the standard document pipeline (same sanitization, mentions, checkboxes) inside section.deck-slide wrappers. Definitions are prepended per slide (skipping footnote keys the slide defines — commonmarker swallows fragments containing duplicated footnote definitions), footnote marks are renumbered to match the document-wide References back matter, and checkbox data-line stays document-absolute via render_markdown's new line_offset. Verified with two adversarial review workflows; every confirmed finding is pinned by a regression spec. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 40c45e1283
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex review of #189 flagged that per-slide rendering restarts both id generators — comrak heading anchors (intro, intro-1, …) and numbered section ids (section-1, section-1-2 via unique_dom_id) — so links written against document mode went dead in the deck. The document-mode render the deck already uses for footnote ordinals is now the ground truth for everything numbered per document: - align_heading_ids copies each body anchor's id+href and each heading's id positionally from the document render, gated on equal counts and pairwise content fingerprints (text, image sources, link destinations, checkbox states) so author HTML left open across a slide break — which parses differently per slide than in the document (foster-parenting, swallowed siblings) — can only skip the pass, never misassign an id. - mirror_section_link_enhancement makes section preview affordances match document mode in both directions: cross-slide links gain them, stale per-slide ones (target id lost to an earlier claimant) lose them. - drop_misleading_ids backstops the skips: any deck id whose document-mode owner shows different content is dropped rather than left pointing at the wrong thing (anchors validate by the heading they mark). - renumber_deck_footnotes no longer counts author elements claiming to be a heading anchor and a footnote ref at once, so impostors can't shift real references off their back-matter backrefs. Also pins the other Codex finding as specs: thematic breaks nested in list items never split (doc.each only walks top-level nodes). Verified with three adversarial review workflow rounds (9 agents); all 12 confirmed breaks fixed and spec-pinned, and the final round's 33-document mechanical parity sweep found deck output id-for-id identical to document mode on legitimate content. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The user-facing type is "Presentation" (behavior enum, default template, seeds); the internal mechanism keeps its slideshow naming. Plan show now branches on presentation? with behavior in the fragment-cache key, the slideshows helper is registered on the engine controller, and a provisional deck stylesheet renders slides as 16:9 cards pending the real design system. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n-slideshows-38c443 * origin/main: Let people choose their push-to-talk key (#188) # Conflicts: # db/schema.rb
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.
Markdown slideshows, foundation through present mode. A deck is a rendering convention over the plan's single markdown blob — nothing new is persisted, so versioning, diffs, and comment anchoring keep working unchanged. Design doc: CoPlan plan
01a01696-aad6-762b-ad66-7681bbadd98c(rev 6).Phase 1 — foundation
Plan-type behavior. Plan types gain a
behaviorcolumn (document/slideshow, string enum + inclusion validator), andPlan#slideshow?delegates to it — retyping a plan is what converts a document into a deck and back. Ships a Presentation default type (installable viacoplan:plan_types:install_defaults) whose template teaches the authoring conventions, plus admin support, a presentation icon, and seeded showcase decks.Slideshows::Split. Splits markdown into slides at top-level---thematic breaks, AST-driven so a---inside a code fence, blockquote, or setext heading never splits, and***/___stay visible rules. Extracts<!-- notes: ... -->speaker notes and gathers footnote/link-reference definitions as positioned blocks so slides can render in isolation. Link-definition candidates are validated by a parser round-trip (parse the candidate alone; it must be fully consumed), which is what keeps[looks]: like-a-definitionprose from being hoisted onto every slide.SlideshowsHelper#render_slideshow. Renders each slide through the standard document pipeline — same sanitization, mention chips, interactive checkboxes — insidesection.deck-slidewrappers. Checkboxdata-linestays document-absolute via a newline_offsetparam onrender_markdown. Footnote sections are excluded per slide and marks are renumbered to match the document-wide References back matter, including document-modefnrefids so every ↩ backref resolves.Phase 2 — the deck design system
docs/SLIDE_SPEC.mdis the product boundary, and its fencedconformanceexamples ARE the test suite:spec/services/slideshows/conformance_spec.rbexecutes every fixture in the document, so the spec cannot drift from the implementation, and a future implementation in another language proves itself against the same fixtures.Slideshows::Classify. A pure function assigning each slide a layout pattern (title,stage,code,quote,table,columns,split,content) and one of four type-scale steps, from content shape alone — no layout syntax, no measurement, no AI in the layout path. Authors and agents move an image in the source to move it on the slide.deck.css+ three token themes (coplan,graphite,poster): 16:9 cards sized in container-query units so a slide is the same design at any width, per-pattern art direction (title canvas + accent bar, staged media, oversized pull quotes, side-by-side panes via grid with zero DOM reordering), and discrete type steps instead of shrink-to-fit. Themes are validated custom-property sets applied bydata-deck-theme; user CSS is never accepted. Slide chrome (numbers, kickers, quote marks) is CSS-generated, invisible to the visible-text counts comment anchoring relies on.Markup decoration. After the deck/document parity passes, the renderer applies the spec's markup contract: the neutral
.deck-contentname and, on split slides, the.deck-media/.deck-bodypanes — skipped entirely on any classified-shape/DOM mismatch (sanitized-away raw HTML), so the stylesheet degrades to the content layout rather than wrap the wrong node.Present mode, the directory pattern, and full-canvas stage media
Present mode.
por the Present button runs the deck as a fullscreen 16:9 show — what a Zoom/Meet screen-share needs. The deck itself is promoted to the top layer (popover API, plus native fullscreen on the wrapper), which is the load-bearing choice: the plan card's glassbackdrop-filtermakes it the containing block forposition: fixed, so a merely-fixed overlay would render at card size inside the card's stacking context. Top-layer elements always position against the viewport. Arrows/space/page keys/Backspace/Home/End/clicks navigate (links and checkboxes on slides still work); Escape or leaving fullscreen ends the show;#present-Nresumes; the "N / total" counter is CSSattr()chrome. The presenter wrapper sits outside the live-update swap target and re-applies show state via a childList observer, so a collaborator's edit landing mid-show swaps slides under the presenter without ending it. The show tears down onturbo:before-cache(a cached snapshot would otherwise restore a closed-popover,display: nonedeck), and the Present toolbar lives outside the text-selection content target so its label never enters the comment-anchor text model.directorypattern. One list of ≥15 short entries (one paragraph, ≤60 chars, no images/breaks — comment-only blocks inside an entry don't count, comments never influence layout) is an inventory, not an argument: it flows into two balanced CSS columns and bills at ⌈units/2⌉ for the type scale, so a forty-project list lands as two readable columns instead of one column running off the canvas.Stage sizing. Stage slides now give the visual the whole canvas: thinner padding, media up to 44cqi, and mermaid's inline natural-size
max-widthoverridden so small diagrams scale up to the slide instead of rendering as thumbnails.RENDER_CACHE_VERSION→ 11.Notes for reviewers
Slideshows::services, own helper,deck-*class namespace, spec + stylesheets with no host-CSS reliance) so the layout engine can be extracted as a standalone package later.display: none, toolbar text entering the comment-anchor occurrence model, and a nested speaker note flipping a directory slide to single-column.Still to come (separate PRs): deck review loop (slide rail, changed-slides, theme picker), speaker-notes drawer +
/presentdeep link, agent fit report.🤖 Generated with Claude Code