feat(slides): import slidey-flavored markdown decks#583
Open
njreid wants to merge 27 commits into
Open
Conversation
Captures grammar additions (per-slide frontmatter, ## Notes, FA shortcodes, mermaid fences, columns, ::boxes::/::arrows::), AST shape, layout mapping, asset pipeline (jsDelivr SVG-direct, mmdc PNG), and test strategy for the upcoming gog slides create-from-markdown rework. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Breaks the spec into 20 bite-sized TDD tasks: AST scaffold, frontmatter splitter, inline/block parsers, layout helpers, asset pipeline (FA fetch + mmdc + Drive upload + cleanup), renderer per layout, end-to-end fixture, docs + CHANGELOG. Each task is self-contained with failing-test, implementation, verification, and commit steps. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduces Slide, Block, Inline, IconRef, ImageRef and per-block types in slides_markdown_ast.go. Replaces the legacy types/parser/renderer in slides_markdown.go and slides_formatter.go with compilable stubs so the package builds; Tasks 2-8 fill the parser, Tasks 15-18 fill the renderer. Updates slides.go call site to match new ParseMarkdownToSlides signature. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code-review feedback: Go convention drops type-prefix repetition on struct fields. Field has zero callers yet, so rename is risk-free. Plan updated to match. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements the §4.1 disambiguation rule: a "---" opens frontmatter only when followed by a YAML key line; otherwise it is a slide separator. Unclosed frontmatter is a fatal error with a line-numbered message. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code-review hardening: strip \r\n at the entry point so downstream TrimSpace and yaml.Unmarshal calls don't see lingering \r bytes from Windows-authored decks. Pure addition; tests unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tokenizes a line of markdown into TextRun and IconRef inlines. Style derivation matches §4.6 (fa→default, fas→solid, far→regular, fab→brands, fal/fad→solid with free-tier substitution). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Walks slide body line-by-line and emits Block AST nodes. Inline parsing delegates to parseInlines from Task 3. Column, boxes/arrows, and mermaid marker handling is added in subsequent tasks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rkers Recursive call into parseBlocks per column body. Accepts ::right:: as a synonym for ::col2:: (slidey allows both for the 2-column case). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each row may begin with a leading FA shortcode (boxes) or a heading prefix (arrows). Trailing text becomes the row label. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Other languages remain CodeBlock. Each diagram gets a stable ID so the asset pipeline (Task 11) can pair it with an uploaded image. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ParseMarkdownToSlides now: splits frontmatter blocks, parses each slide's body via parseBlocks, hoists the first h1 (or h2 fallback) as the slide title for non-hero/title/statement layouts, extracts the trailing "## Notes" section as raw speaker notes (FA shortcodes stripped). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MapSlideyLayout collapses the slidey frontmatter values into a small LayoutKind enum. ColumnBoxes/SingleBodyBox/TitleBox compute box rectangles in points from the presentation page size. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AssetMap pairs AST IconRefs/DiagramBlocks with their Drive ImageRefs. DefaultAssetPipelineConfig sets a 30s HTTP timeout and the standard mmdc binary. The FA URL builder targets the FA Free 6.x jsDelivr path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fetchFAIconFromURL performs a GET against jsDelivr (or any URL injected in tests via httptest) and returns the SVG bytes. 404/non-200 are wrapped errors. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mmdcCommandArgs builds the standardized invocation; renderMermaidWithBinary writes a temp .mmd, runs mmdc with a transparent background and 2x scale, and returns the rendered PNG. Temp files are cleaned up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AssetPipeline.Resolve walks all slides for unique IconRefs and DiagramBlocks, fetches/renders each, and uploads via the Uploader abstraction. Per-asset failures warn-and-skip unless Strict. Cleanup deletes the tracked Drive files unless KeepTempImages. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DriveUploader wraps drive.Service to upload bytes, set anyone-reader permission, and return the WebContentLink — mirrors the pattern in slides_add_slide.go. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
RenderSlides emits CreateSlide + title box + body box for default and center layouts. Returns a SlideNotesPlan slice consumed by the second BatchUpdate (Task 18). Body text is the legacy "flatten blocks to one string" form; columns and image insertion follow in Tasks 16/17. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SectionHeader-kind layouts emit one centered body box with the first line styled at 44pt bold. two-cols/three-cols emit N side-by-side body boxes positioned via the geometry helpers from Task 9. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diagram blocks render as full-width images below the title. Bullets whose first inline is an IconRef get a small (18pt) icon image rendered to the left of the body box, vertically aligned to the bullet line. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CreatePresentationFromMarkdownV2 runs the full pipeline: create presentation → derive geometry → resolve assets → first BatchUpdate → re-fetch → second BatchUpdate for notes → cleanup. SlidesCreateFromMarkdownCmd gains --fa-style, --mmdc, --strict, --keep-temp-images, --no-notes. Existing --dry-run prints the would-be BatchUpdate JSON without any network for fetch/render. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copies univrs/slidey/slides/index.md into testdata and asserts the parser produces all expected layout kinds, notes, icons, and diagrams, then runs the renderer with a fake asset map. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the synthetic 30-slide fallback fixture with the actual univrs/slidey/slides/index.md deck (969 lines). Tests still pass — this gives the e2e check real-world content shape. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds reference for per-slide frontmatter, ## Notes, FA shortcodes, mermaid blocks, columns, and ::boxes::/::arrows::. Updates the flag table for the new --fa-style/--mmdc/--strict/--keep-temp-images/ --no-notes flags. CHANGELOG entry under 0.17.0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Drive uploader now best-effort deletes the uploaded file when Permissions.Create or Files.Get fails, so a permission failure no longer orphans assets in the user's Drive. - Drop the legacy CreatePresentationFromMarkdown stub and the unused slideElementTitle const; the CLI now calls CreatePresentationFromMarkdownV2 directly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three issues surfaced by running the import against a live Google
account on the univrs/slidey/slides/index.md deck:
- Notes BatchUpdate failed with "Invalid requests[0].deleteText: The
startIndex 0 must be less than the endIndex 0" because freshly-created
slides have empty notes boxes and DeleteText{ALL} rejects that. Drop
the leading DeleteText and just InsertText — notes are always empty
on a slide we just created.
- Font Awesome shortcodes like `:fa-dev:` 404 because "dev" is only
published under brands/, not solid/. Add a fallback that re-tries
the brands/regular/solid styles when the requested style 404s. Other
errors (network, 5xx) still fail immediately without guessing.
- mmdc errors only surfaced as bare exit codes ("exit status 1"). Use
CombinedOutput so stderr (puppeteer chromium download issues, mermaid
syntax errors, etc.) ends up in the warning message.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Extends
gog slides create-from-markdownto import slidey-flavored markdown decks (theunivrs/slidey/slides/index.md-style format) into Google Slides.layout: title|hero|center|default|two-cols|three-cols|statement,content: wide|narrow(parsed; not yet applied to text-box widths).## Notesspeaker notes — the trailing## Notes(or### Notes) section is hoisted into the slide's speaker notes; FA shortcodes inside notes are stripped to plain text.:fa-name:/:fas-name:/:far-name:/:fab-name:(and free-tier:fal-/:fad-substitutions) resolve to FA Free SVGs from jsDelivr, uploaded to Drive and inserted as images.```mermaidblocks render to PNG via localmmdc(configurable, optional) and insert as full-width images.::cols::/::col2::/::col3::/::right::(synonym for::col2::) /::/cols::.::boxes::/::arrows::blocks — flatten to bulleted body lines with optional inline icons.gog slides create-from-markdown:--fa-style(defaultsolid) — default FA style for prefix-less shortcodes--mmdc(defaultmmdc) — path to mermaid CLI--strict— treat any skipped FA/diagram asset as fatal--keep-temp-images— don't delete temporary Drive uploads--no-notes— discard## Notessections--dry-runprints the would-beBatchUpdatePresentationRequestJSON withgogcli://pending/...placeholder URLs (no network).Built as a clean three-stage pipeline: pure parser → asset pipeline (Drive uploads behind an
Uploaderinterface) → renderer (typedSlideAST →[]*slides.Request). Asset failures warn-and-skip unless--strict. Drive uploads are tracked and best-effort cleaned up on completion.Design + Plan
docs/superpowers/specs/2026-05-13-slidey-import-design.mddocs/superpowers/plans/2026-05-13-slidey-import.mdTest plan
go build ./... && go vet ./...cleango test ./internal/cmd/...passes (only pre-existingTestRequireAccount_Missingfails — env-dependent, unrelated)internal/cmd/slides_e2e_test.goparses the real 969-lineunivrs/slidey/slides/index.mdand asserts presence of every layout kind, notes, FA icons, and mermaid blocks; renderer produces non-empty output with a fake asset mapgog slides create-from-markdown "Smoke" --content-file testdata/slidey/index.md --dry-runproduces a cleanBatchUpdatePresentationRequestJSON withgogcli://pending/fa-...andgogcli://pending/diagram-...placeholdersgog slides create-from-markdown "Slidey Import Test" --content-file testdata/slidey/index.md --account <you>and confirm icons + mermaid render in the resulting deckNotes for reviewers
internal/cmd/slides_markdown_*.go,internal/cmd/slides_layout.go,internal/cmd/slides_assets.go,internal/cmd/slides_formatter.go.Slide/SlideElementtypes and the oldSlidesToAPIRequests/CreatePresentationFromMarkdownwere replaced (the new replacement reusesSlidesToAPIRequestsas a thin wrapper for back-compat).docs/commands/gog-slides-create-from-markdown.mdwas hand-edited; if you have a docs generator (make docs-commands?) it should be re-run before release to pick up the 5 new flags.RenderSlideswalks top-levelslide.Bodyonly — diagrams/icons insideColumnsBlockare silently skipped (v1 limitation, documented in code).🤖 Generated with Claude Code