ocaml-gslides: add read-only Google Slides client with Marp/Typst output
Mirrors the [ocaml-gdocs] / [ocaml-gsheets] shape: lib/bin/test layout,
[Gauth]-backed auth, XDG-stored client/token (atomic write via tmp +
rename), [Cmdliner] CLI. Implements a small slice of the Google Slides
v1 REST API: [presentations.get] -> a typed [Presentation.t] containing
slides, page elements, paragraphs, runs, and speaker notes.
Two renderers consume the same parsed structure:
- [Gslides.Marp.of_presentation ?comments p] emits Marp-flavored
markdown. YAML frontmatter for the deck title; [---] between slides;
level-1 headings as slide titles; native bullet/numbered lists;
inline style as [**bold**], [*italic*], [~~strike~~], plus HTML
escape hatches ([<u>], [<sup>], [<span style="color: ...">]) for
features without a CommonMark form. Speaker notes via Marp's
[<!-- _notes -->] pragma; hidden slides via [<!-- _hide: true -->].
Best for "review the deck in a PR diff".
- [Gslides.Typst.of_presentation ?comments p] emits Typst source using
[polylux] (version-pinned to 0.4.0; the value is exposed as
[Typst.polylux_version]). [#slide[...]] per slide, [#strong]/[#emph]/
[#strike]/[#underline]/[#link] for inline style, [#text(fill: rgb...)]
for color, [#table(...)] for tables, [#image("url", alt: ...)] for
images, [#speaker-note[...]] for notes. Best for "compile to a
typeset PDF".
Comments support:
- [Gslides.Comments] is a Drive-API fetch (drive.readonly scope).
Slides comments live on the underlying Drive file, not the Slides
surface itself.
- Both renderers take an optional [?comments] argument. When supplied,
each comment is anchored to its slide via the [pageObjectId]
embedded in the Drive [anchor] field, and rendered as a footnote
(Marp: GFM [[^slcN]] inline + definitions at the bottom; Typst:
[#footnote[...]] inside the slide block).
- Comments whose anchor doesn't reference a slide land in a trailing
[## Comments] section.
What's deliberately not exported: spatial information (positions,
transforms, rotations), shape-as-decoration (callout/flowchart shape
types, connector lines), animations (which the Slides API doesn't
expose anyway), and image bytes (the API gives short-lived
[contentUrl]s; stable references are a follow-up).
CLI:
- [gslides install] -- one-time OAuth client setup.
- [gslides login] -- OAuth flow with both Slides and
Drive comments scopes preauthorized.
- [gslides get <id>] -- print presentation JSON.
- [gslides md <id> [-c]] -- emit Marp markdown.
- [gslides typst <id> [-c]] -- emit polylux Typst.
85 alcotest cases covering the parser (paragraph/run extraction,
inline style decoding for bold/italic/strike/link/color/sup-sub/
monospace, bullet nesting, alignment), Marp rendering (frontmatter,
title -> h1, all inline-style emitters, bullets/nested/numbered, slide
separators, speaker notes, hidden slides, image refs, video/line/etc
placeholders, anchored and unanchored comments), Typst rendering (the
matching set against polylux primitives), Comments (parsing, anchor
extraction, drive scope), and Store (XDG roundtrips, 0600 perms,
atomic-write tightening).
Add the [llms.txt] entry between [gsheets] and [nox-git].
Note on duplication: [Comments] is structurally identical across
[ocaml-gdocs], [ocaml-gsheets] (pending), and [ocaml-gslides], and
each package has its own [Store] keyed off its own XDG dir
(~/.config/gdocs, /gsheets, /gslides). A follow-up will lift these
into a shared [Google_store] (likely in [gauth] or a small new
package) so all three Google tools share one client + one token
covering the union of scopes.