Prohlížeč zdrojového kódu

docs/specs/example-content.md

Náhled Zdrojový kód

80# Spec: Rich Example Content
- **Status:** draft
- **Created:** 2026-03-12
- **Related code:** `app/examples/`, `app/services/example_entry.rb`, `app/views/examples/show.html.erb`
## Overview
Each example has a rich content file (`example.md`) that defines the detail page layout. The Markdown file is the single source of truth for how the example's detail page is structured — authors write free-form Czech prose and embed the source code and scenario forms at the desired positions using placeholder tags. This lets each example tell its own story with the code and interactive elements woven in naturally.
## Behaviour
### Content file
- Every example directory must include an `example.md` file.
- `example.md` drives the detail page layout entirely on the detail page.
- The `description` field in `example.yml` remains — it is used on the index page (example cards) but not on the detail page.
- `ExampleEntry` exposes the raw Markdown content (read from disk on each request, same pattern as source code).
### Placeholder tags
The Markdown content supports these placeholder tags, written on their own line:
- `<!-- source -->` — replaced with the syntax-highlighted source code block. Raises an error if the example has no `source_file`.
- `<!-- scenario:INDEX -->` — replaced with the scenario form and result frame for the scenario at position INDEX (0-based), e.g. `<!-- scenario:0 -->`.
- `<!-- scenarios -->` — replaced with all scenario forms in order (equivalent to listing each one individually). Cannot be combined with individual `<!-- scenario:N -->` tags in the same file.
### Rendering
- The Markdown is rendered to HTML using the existing Redcarpet pipeline.
- After Markdown rendering, placeholder tags (which survive as HTML comments) are replaced with the corresponding rendered partials.
- If a placeholder references a scenario index that doesn't exist, it is silently removed.
- The title and tags are still rendered from `example.yml` above the content, same as today.
- Code blocks in example content (both fenced Markdown blocks and the `<!-- source -->` placeholder) are vertically constrained to 80% of the viewport height. When content exceeds this height, the block scrolls vertically. This prevents tall code blocks from dominating the page layout.
- Two consecutive fenced code blocks separated by a `<!-- compare -->` or `<!-- compare:LabelA:LabelB -->` tag are rendered side by side in a 50/50 split. When labels are provided (colon-delimited), each pane displays its label above the block. Both panes' scroll positions are linked — scrolling either pane horizontally or vertically moves the other to match.
### Migration
- All existing examples get an `example.md` file reproducing the current detail page layout (description text, then source placeholder, then scenarios placeholder).
## Implementation Notes
- `ExampleEntry#content_markdown` reads `example.md` from `@base_path` on each request (same pattern as `source_code`).
- `HighlightHelper#render_example_content` renders the Markdown via `render_markdown`, then replaces placeholder HTML comments via `gsub` with rendered partials and highlighted code. The result is wrapped in `raw` to mark it as HTML-safe for output. The `<!-- source -->` gsub raises `ArgumentError` when `example.source_code` is `nil`.
- `HighlightHelper#render_scenario_block` renders the `examples/scenario_block` partial for each scenario.
- `_scenario_block.html.erb` was extracted from the old `show.html.erb` — wraps scenario name, description, form, and Turbo Frame result tag.
- `show.html.erb` wraps `render_example_content(@example)` in `<div class="example-content">` — this scopes code block height limiting to example pages only, leaving the source browser unaffected.
- `compare_scroll_controller.js` (Stimulus) — on connect, collects both `pre` elements within the `.code-compare` wrapper and syncs `scrollTop`/`scrollLeft` on each scroll event. Auto-registered via `eagerLoadControllersFrom`.
- `_code.scss``.code-compare` is a flex container with `gap: 1rem`; each `.code-compare__pane` is `flex: 1; min-width: 0` (prevents flex blowout on wide code); `.code-compare__label` is small muted text. The `<!-- compare -->` post-processing is a `gsub` step inside `render_example_content`.
- `_code.scss` scopes the height constraint to `.example-content .code-block`: the outer `.code-block` is set to `overflow: hidden` (overriding its base `overflow-x: auto`) and the inner `pre` becomes the 2D scroll container (`max-height: 80vh; overflow: auto`). This ensures both horizontal and vertical scrollbars are simultaneously visible at the edges of the `pre` element, rather than the horizontal bar being hidden below the vertical overflow clip.
## Tests
- `spec/services/example_entry_spec.rb``content_markdown` reads from disk
- `spec/helpers/highlight_helper_spec.rb` — Markdown rendering, source placeholder replacement, invalid scenario index removal, compare block rendering (unlabelled and labelled)
- `spec/requests/examples_spec.rb` — integration: detail page renders syntax-highlighted code and scenario forms
## See Also
- [Examples System](examples.md) — defines example metadata, registry, and the current detail page layout that this spec replaces.
- [Examples Runner](examples-runner.md) — scenario forms and execution, embedded via placeholder tags.
- [Markdown Code Block Syntax Highlighting](markdown-code-highlighting.md) — fenced code blocks in `example.md` files are highlighted via the Markdown renderer.
## Change Log
- 2026-03-12: Initial prospective spec.
- 2026-03-17: Added behaviour: code blocks in example content are height-limited to 80vh with vertical scroll.
- 2026-03-17: Added side-by-side code comparison via `<!-- compare -->` tag with linked scrolling.
- 2026-03-23: `<!-- source -->` silently removed when example has no `source_file`.