From 88be00b1500bd9d4219522b477040602a431d9d4 Mon Sep 17 00:00:00 2001 From: Nicholas Reid Date: Wed, 13 May 2026 10:45:07 -0700 Subject: [PATCH 01/27] docs(slides): add slidey markdown import design spec 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) --- .../specs/2026-05-13-slidey-import-design.md | 267 ++++++++++++++++++ 1 file changed, 267 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-13-slidey-import-design.md diff --git a/docs/superpowers/specs/2026-05-13-slidey-import-design.md b/docs/superpowers/specs/2026-05-13-slidey-import-design.md new file mode 100644 index 00000000..e1ccf9b2 --- /dev/null +++ b/docs/superpowers/specs/2026-05-13-slidey-import-design.md @@ -0,0 +1,267 @@ +# Slidey-flavored markdown import for `gog slides create-from-markdown` + +**Status:** Draft for review +**Date:** 2026-05-13 +**Author:** Nicholas Reid (with Claude) +**Related code:** `internal/cmd/slides_markdown.go`, `internal/cmd/slides_formatter.go`, `internal/cmd/slides.go` +**Reference inputs:** `../univrs/slidey/DESIGN.md`, `../univrs/slidey/slides/index.md` + +## 1. Goal + +Extend `gog slides create-from-markdown` so it can faithfully import decks authored for the slidey Rust slide engine. The current parser handles only flat `## title / bullets / paragraphs / code` content; slidey decks use per-slide frontmatter, layout names, column markers, Font Awesome icon shortcodes, mermaid diagrams, and a trailing `## Notes` section for speaker notes. + +The user's `slides/index.md` is the canonical example to satisfy. + +## 2. Scope + +### In scope + +1. Per-slide YAML frontmatter (`layout:`, `content:`). +2. Trailing `## Notes` section per slide → Google Slides speaker notes. +3. Font Awesome shortcodes (`:fa-*:`, `:fas-*:`, `:far-*:`, `:fab-*:`) → SVG fetched from jsDelivr CDN, uploaded to Drive, inserted as image (SVG-direct, no local raster). +4. Mermaid fenced code blocks → rendered to PNG via local `mmdc` CLI, uploaded to Drive, inserted as image. +5. Column markers `::cols::`, `::col2::`, `::col3::`, `::right::`, `::/cols::`. +6. `::boxes::` and `::arrows::` blocks flattened to bulleted lists (one row per item; icon-prefix preserved). +7. Layouts: `title`, `hero`, `statement` → centered section-header; `center`, `default` → title + body; `two-cols`, `three-cols` → custom-positioned boxes on `BLANK`. + +### Out of scope (deferred) + +- `content: wide | narrow` — parsed and stored, ignored by the renderer this PR. +- KDL syntax highlighting — code blocks render as plain monospace. +- Mermaid rendering when `mmdc` is missing — skipped with warning (or fatal under `--strict`). +- D2 diagrams (slidey is migrating; not in `index.md` yet). +- Inline HTML tags (``). +- PNG rasterization fallback for FA icons. + +## 3. CLI surface + +`gog slides create-from-markdown` keeps its existing flags (`--content`, `--content-file`, `--parent`, `--debug`, `--dry-run`). New flags: + +| Flag | Default | Purpose | +|------|---------|---------| +| `--fa-style` | `solid` | Default style when shortcode is `:fa-x:` (no style prefix). `:fas-`/`:far-`/`:fab-` always win. | +| `--mmdc` | `mmdc` | Path/name of the mermaid CLI. Missing binary → mermaid blocks skipped with warning. | +| `--strict` | `false` | Treat any skipped asset (FA fetch fail, mermaid render fail) as fatal. | +| `--keep-temp-images` | `false` | Don't delete the Drive uploads for icons/diagrams after the presentation is built. | +| `--no-notes` | `false` | Discard `## Notes` sections instead of inserting them as speaker notes. | + +Existing `--debug` gains additional output: parsed AST as JSON and per-stage asset-pipeline progress. Existing `--dry-run` runs parse + a stub asset pipeline (records what *would* be fetched) + render with placeholder URLs (`gogcli://pending/fa-truck-fast`); no network for fetch/render in dry-run. + +## 4. Markdown grammar additions + +### 4.1 Per-slide frontmatter + +A `---` line followed immediately by `key: value` lines and a closing `---` is treated as that slide's frontmatter. Parsed with `gopkg.in/yaml.v3` (already in `go.mod`). Recognized keys: `layout`, `content`. Unknown keys retained on `Slide.Frontmatter.Raw` and ignored. + +A bare `---` line that does *not* open a frontmatter block remains the slide separator (current behavior). + +Disambiguation rule (deterministic, no lookahead-of-arbitrary-length): + +1. A `---` at file start, or immediately following another `---` separator (with only blank lines between), opens a *frontmatter candidate*. +2. The next non-blank line must match `^[A-Za-z_][A-Za-z0-9_-]*:\s` (a YAML key). If not, the original `---` is treated as a slide separator and the candidate is abandoned. +3. From the candidate's opening `---`, scan forward; the first line that is exactly `---` (after trim) closes the frontmatter. If no closing `---` is found before EOF, the parser emits a fatal error naming the offending line. + +### 4.2 Title hoisting + +- Layouts `title`, `hero`, `statement`: the first `# h1` (or `## h2` if no h1) stays in body — no title hoisting. Body box renders the heading at large size. +- All other layouts: the first `# h1` is the slide title. If no h1 exists, fall back to the first `## h2`. (Back-compat with existing decks that use only h2.) + +### 4.3 `## Notes` section + +Hard-matched: a heading line whose trimmed text is exactly `Notes` (case-sensitive, level 2 or 3). Everything from that heading until the next slide separator becomes `Slide.Notes` as raw text. FA shortcodes inside notes are stripped to plain words (e.g. `:fa-truck-fast: Orders` → `Orders`). Diagrams inside notes are dropped. + +### 4.4 Columns + +``` +::cols:: + +content of column 1 + +::col2:: + +content of column 2 + +::col3:: + +content of column 3 + +::/cols:: +``` + +`::right::` is accepted as a synonym for `::col2::` (slidey allows both for the 2-col case). Three columns require either `two-cols`/`three-cols` layout or render on `default` as side-by-side text boxes (renderer infers column count from how many `::colN::` markers appear). + +### 4.5 `::boxes::` and `::arrows::` + +``` +::boxes:: +:fa-rectangle-ad: Campaigns +:fa-headset: Support Tickets +::/boxes:: +``` + +Each line becomes an `IconRow{icon: optional, text: string}`. Rendered as a bulleted list — bullets use the icon image when available, plain bullet otherwise. `::arrows::` rows are rendered the same shape, just with an arrow glyph (`→`) prefix instead of a bullet. + +### 4.6 Font Awesome shortcodes + +Regex: `:fa[srlbd]?-[a-z0-9-]+:` matched anywhere in text. + +Style derivation from prefix: + +| Prefix | Style | +|--------|-------| +| `fa-` | `--fa-style` default (`solid`) | +| `fas-` | `solid` | +| `far-` | `regular` | +| `fab-` | `brands` | +| `fal-`, `fad-` | `solid` (FA Free has no light/duotone — substitute and warn once per icon) | + +URL: `https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6/svgs/