diff --git a/docs/research-story-editing.md b/docs/research-story-editing.md new file mode 100644 index 0000000..a5e00c2 --- /dev/null +++ b/docs/research-story-editing.md @@ -0,0 +1,174 @@ +# Story Editing Research + +Brainstorming session — 2026-06-20. Notes on options for improving the story editing +experience in Admin2. Not a plan — a reference to revisit once real story writing reveals +what actually matters. + +--- + +## The core problem + +Stories use shortcode syntax (`[scrolly-section image="x.jpg"]…[/scrolly-section]`) authored +in a single big markdown textarea in Admin2. Three pain points, roughly equal weight: + +1. **Fragile syntax** — easy to typo a shortcode and get no useful error +2. **Writing blind** — no preview while editing; you don't see the result until you view the page +3. **Mobile unusable** — Admin2 is desktop-focused; the markdown textarea on a phone is painful + +--- + +## What Keystatic / Sanity / Shorthand taught us + +All three tools represent content as a **typed, ordered list of blocks** — not a text string. + +In Keystatic the editing flow is: write prose normally → click "+" → pick a block type from a +list → fill in a form with labelled fields → the block appears as an opaque card in the editor. +Authors never see markup. Each block type (hero, gallery, scrolly section) has a typed schema +(image picker, text fields, selects). Sanity's Portable Text uses the same model. Shorthand +(used by BBC, Reuters, National Geographic) is a purpose-built CMS for exactly this kind of +immersive storytelling — their section vocabulary is the best reference for what block types +matter in practice. + +**Key insight:** the gap between Grav and these tools is entirely on the authoring side. +Grav's rendering (Twig templates, shortcodes, parallax effects) is perfectly capable. +The problem is that Admin2 was not designed for structured block content authoring. + +--- + +## Snow Fall block vocabulary + +The canonical block types that appear across all immersive storytelling platforms: + +| Block | What it does | Fields | +|---|---|---| +| **Hero** | Full-bleed opening image/video + title. Chapter opener. | image, headline, subtitle, text position, animation (static / ken-burns) | +| **Narrative text** | Prose reading column. The writing block. | body (markdown) | +| **Full-bleed media** | Single image/video edge-to-edge, no text. Visual pause. | image, caption, credit | +| **Image + caption** | Photo at configurable width with caption below. | image, caption, credit, width (column / full / bleed) | +| **Scrollytelling** | Text panels scroll over a fixed or animated background. | background image, panels (each: headline + body) | +| **Photo gallery** | Multi-image carousel/grid → lightbox. | images (each: file + caption + credit) | +| **Pull quote** | Typographically large extracted quote. | quote text, attribution, optional background | +| **Chapter break** | Major section transition with background image. | image, title, chapter number | +| **Grid / side-by-side** | 2–3 column photo+text pairs. | columns array | +| **Embed** | YouTube, Vimeo, audio, map. | URL, caption | + +Current Grav shortcodes cover: hero (ken-burns), scrollytelling (scrolly-section), gallery +(snap-gallery), pull-quote, chapter-break. Missing from the vocabulary: narrative text as an +explicit block, full-bleed media, image+caption, grid, embed. + +--- + +## The two fundamental approaches + +### A — Blueprint-as-blocks (no shortcodes) + +Replace the markdown body with a `list` field in the story blueprint. Each list item is a +block with a `type` selector + type-specific sub-fields. Content lives in frontmatter YAML; +the Twig template loops over blocks and renders each one with the right partial. No shortcode +syntax at all. + +**Solves all three pain points.** Admin2 form fields are mobile-reasonable. Structure is +explicit and impossible to mis-type. + +**One limitation:** Grav's native `list` field doesn't hide/show fields based on the selected +type. Every block card shows ALL fields for ALL block types; the template ignores the unused +ones. It's visually cluttered but functionally correct. This is a solvable UX problem (Grav +roadmap, or a future Admin2 extension) — the data model stays the same when it improves. + +### B — Enhanced markdown (keep shortcodes, improve authoring UX) + +Keep the markdown textarea; add tooling on top to reduce friction. Multiple options here +(see section below), but all hit an architectural constraint: Admin2 serves its SPA via +`echo $html; exit` which bypasses Grav's entire output pipeline. Standard plugin hooks +for injecting assets don't fire in Admin2. Any JS-based editor enhancement requires either +a fragile output-buffering hack or patching Admin2's pre-built `app/index.html` directly. + +--- + +## 15 options researched + +### Options that work natively (no Admin2 hacking required) + +**1. Blueprint-as-blocks** — structured YAML fields per block, native Admin2 form rendering. +Best long-term solution for non-technical story editing and mobile. Medium effort (blueprint +YAML + Twig template rework). The field-clutter limitation is real but acceptable. + +**2. page-inject sub-pages** — each story block is a standalone Grav sub-page; the parent +story injects them in sequence via `[plugin:page-inject]`. Editing = opening sub-pages +individually. More flexible than blueprint-as-blocks for reusable content but creates +more pages to manage and Admin2 navigation friction. + +**3. New shortcodes via YAML + Twig** — shortcode-core supports YAML-configured Twig +shortcodes out of the box (`shortcode-core.yaml` + a Twig file in the theme). No plugin +needed. Used to add `full-bleed` and `image-caption` to the story-blocks plugin. + +**4. Obsidian + Gitea git sync** — write stories in Obsidian on desktop or mobile with +snippet templates for shortcodes. Push to Gitea; webhook deploys to production. Zero dev +work. Good mobile writing experience. No image upload from mobile; requires comfort with git. + +**5. `/story-editor` custom page** — a purpose-built page (like `/gpx-manager`) that presents +story blocks as drag-reorderable cards with typed forms, talks to the Grav API, designed +mobile-first. Highest effort; best mobile result. Would use the "one custom plugin" slot. + +### Options blocked by Admin2's architecture + +All of these require injecting JavaScript or CSS into Admin2 pages, which is architecturally +blocked (Admin2 bypasses Grav's output pipeline with `echo $html; exit`): + +- EasyMDE / SimpleMDE drop-in (split-pane markdown preview) +- CodeMirror 6 autocomplete + syntax highlighting for shortcodes +- Toast UI Editor (WYSIWYG ↔ markdown toggle) +- Tiptap custom shortcode nodes +- Milkdown +- Slash-command / shortcode palette +- Toolbar-at-bottom CSS (mobile improvement) +- Web Speech API dictation button + +**Workaround:** patch Admin2's pre-built `app/index.html` directly. Survives until the next +Admin2 update; a `make patch-admin2` command would re-apply it. Viable for a personal blog +but adds a maintenance step. + +### Paid option + +**Grav Editor Pro ($75)** — TipTap/ProseMirror-based WYSIWYM editor, confirmed Admin2- +compatible (listed as an optional dependency in the Admin2 README ≥ v2.0.1). When paired +with shortcode-core, shortcodes appear as visual green blocks with a modal form per type. +This is the cleanest story editing experience available without building it yourself. Ruled +out based on preference to avoid paid tools. + +--- + +## Honest CMS comparison: Grav vs Keystatic for storytelling + +**Wrong conclusion:** Grav can't do Snow Fall storytelling. + +**Right conclusion:** Grav's rendering side (parallax, scrollytelling, ken-burns, galleries) +is fully capable. The authoring experience for structured block content is the genuine weak +point — and the options to improve it cleanly within Admin2 are limited. + +Keystatic + a modern frontend (Astro, Next.js) would give a better editorial experience for +story authoring, but at the cost of migrating away from Grav and building a separate frontend. +For a solo travel blogger who is the only author, Grav with an improved shortcode setup or +blueprint-as-blocks is good enough. Keystatic's advantage is significant for publications with +multiple non-technical editors writing daily. + +--- + +## What was actually done (2026-06-20) + +- Added `full-bleed` shortcode to `story-blocks` plugin (PHP class + CSS) +- Added `image-caption` shortcode to `story-blocks` plugin (PHP class + CSS), with + `width` parameter: `column` (default) / `full` / `bleed` +- Next step when ready to revisit: write some actual stories, then decide whether + blueprint-as-blocks or the app/index.html patch route is worth pursuing + +--- + +## References + +- [Keystatic content components](https://keystatic.com/docs/content-components) +- [Sanity Portable Text](https://www.sanity.io/docs/portable-text) +- [Shorthand storytelling sections](https://shorthand.com/features/sections/) +- [Grav Editor Pro](https://getgrav.org/premium/editor-pro) +- [Admin2 GitHub](https://github.com/getgrav/grav-plugin-admin2) +- [shortcode-core](https://github.com/getgrav/grav-plugin-shortcode-core)