diff --git a/docs/MANAGED-FORK.md b/docs/MANAGED-FORK.md new file mode 100644 index 0000000..e57d170 --- /dev/null +++ b/docs/MANAGED-FORK.md @@ -0,0 +1,48 @@ +# Managed fork workflow + +This is a **managed fork** of `nicobailon/visual-explainer`. It carries local +customizations (a standard self-contained favicon, house-style notes) on top of +upstream, and stays updatable. + +## Philosophy: control over automatic + +Updates are **on-demand, not automatic**. Nothing pulls upstream behind your +back — so your local modifications and pinned behavior are never disturbed by a +surprise merge. When you *do* want to update, it's one command, and a pi agent +resolves any conflicts with full repository context. + +If a session is already current, everything just loads normally with zero work. + +## Remotes + +| Remote | Points at | Role | +|------------|------------------------------------|------------------------------| +| `origin` | `zereraz/visual-explainer` | your fork (your commits) | +| `upstream` | `nicobailon/visual-explainer` | the original project | + +## Updating + +From this clone (`~/Code/Zereraz/visual-explainer`): + +```bash +bash scripts/update-fork.sh --check # status only; never modifies +bash scripts/update-fork.sh --apply # fetch + merge upstream + push + pi update +# if conflicts (exit 20): resolve files, then: +bash scripts/update-fork.sh --continue +``` + +Or, inside pi, just run the command (loaded with this plugin): + +``` +/update-visual-explainer +``` + +The command checks first, applies if behind, and if a merge conflict occurs the +agent resolves it — **keeping both** upstream's new behavior and our additive +customizations — then commits, pushes, and refreshes the install. + +## Making your own changes + +Commit directly on `main` (small tweaks) or via a feature branch + PR to your +own fork. Because our changes are additive, upstream merges are usually clean. +`scripts/update-fork.sh` verifies the favicon customizations survived each update. diff --git a/plugins/visual-explainer/SKILL.md b/plugins/visual-explainer/SKILL.md index ed59377..bb1e17b 100644 --- a/plugins/visual-explainer/SKILL.md +++ b/plugins/visual-explainer/SKILL.md @@ -19,6 +19,17 @@ Generate self-contained HTML pages that explain systems, code changes, plans, da - Write files to `~/.agent/diagrams/` or the explicit eval output path. Use descriptive filenames. - Open generated pages in the browser when running normally. In Pi package installs, use `visual_explainer` with `prepare` for planning/context and `render` only after the complete HTML document exists. - The final page must be a complete self-contained HTML document, including embedded CSS and any needed JS. +- Always include the standard self-contained data-URI favicon immediately after `` (see "Favicon" below). Never leave a page without a favicon. + +## Favicon + +Every generated page must include this exact self-contained data-URI favicon, placed immediately after the `` tag. It needs no external file and is a small node-graph glyph that matches the dark/accent palette: + +```html + +``` + +If math is rendered with KaTeX, escape `<` as `<` inside `$$...$$` (e.g. `y_{<t}`); a bare `<` makes the HTML parser truncate the formula. ## Reference routing @@ -68,6 +79,7 @@ Read only the references needed for the current output: - Good font pair families: DM Sans + Fira Code; Instrument Serif + JetBrains Mono; IBM Plex Sans + IBM Plex Mono; Bricolage Grotesque + Fragment Mono; Plus Jakarta Sans + Azeret Mono. - Good accent directions: terracotta+sage, teal+slate, rose+cranberry, amber+emerald, deep blue+gold. - Prevent overflow: `min-width: 0` on grid/flex children, `overflow-wrap: break-word` for long text, and scroll containers for wide tables/code. +- **Labels must never be clipped.** When a badge/tag straddles a box edge (e.g. a code-block filename pill at `top: -12px`), the box must NOT have `overflow: auto/hidden` — that slices the label. Put the scroll on an inner element instead (`overflow: visible` on the badge container, `overflow-x: auto` on the inner `
`/scroller). Likewise keep SVG/mermaid `` inside the viewBox and tab/section labels within their pill. - Do not set `display: flex` directly on ` ` when list markers matter. - Use depth sparingly: hero/elevated only for primary sections; flat/recessed for reference material. - Use entrance/hover animation only when it clarifies hierarchy. Respect `prefers-reduced-motion`. Do not use continuous glow, pulse, or breathing effects on static content. @@ -83,15 +95,63 @@ Use slides only when explicitly requested or when a command asks for slides. Sli - Do not drop content to fit a fixed slide count. Add slides instead. - Use the 10 slide types from `slide-patterns.md`: Title, Section Divider, Content, Split, Diagram, Dashboard, Table, Code, Quote, Full-Bleed. +## Reader-first defaults (pages are made to be read and re-read) + +The primary use is a reader consuming the page, often over multiple sittings. Build for that: + +- **Navigation**: ship the upgraded `SlideEngine` from `slide-deck.html` — it adds deep-link hashes (`#slide-7`), resume-where-you-left-off (localStorage), reading percent in the counter, an outline overlay (press `O`), and a keyboard help panel (press `?`). Don't ship a stripped engine. +- **Comprehension**: open each section with its one-line takeaway, then expand (TL;DR-first). End a multi-section page with a short "what to remember" recap. +- **Glossary**: wrap domain jargon in `term` so a reader can hover/tap for meaning without leaving the page. +- **Don't gate content on motion**: every slide must be fully readable even if its animation never plays; animations enhance, never reveal essential text. +- **Accessibility**: don't rely on color alone (pair with shape/label); respect `prefers-reduced-motion`; keep body text ≥ 16px with generous line-height; ensure keyboard nav reaches everything. + +## House design language v2 (make it beautiful) + +Default look for decks (reference implementation: any deck styled like `the-gradient-view`): + +- **Layout is left-anchored assertion–evidence (research-grounded)**: eye-tracking shows readers scan from a strong left edge (F-pattern) — so slides are **left-aligned and top-anchored**, never a small centered core in empty space. The headline is a **full-sentence assertion** (left-justified, ≤2 lines, consistent position across slides = the eye's anchor); the body is **evidence that fills the canvas** (content column up to ~1160px, blocks stretch full-width, base type larger). Related pairs (equation ↔ English reading ↔ numeric trace) sit **side-by-side** in a 2-column `.duo` grid on wide screens (Mayer's spatial-contiguity principle), collapsing to one column under 980px. Slides never compress content to fit the viewport: the slide is `height:100dvh; overflow-y:auto` so tall content **scrolls within the slide** at its natural size (children `flex-shrink:0`; no forced equal-height block pairs; no max-height squeeze media-queries that shrink type). The content group is **vertically centered** (`justify-content:center` + `gap:clamp(6px,1.6vh,18px)`) so slides never leave a dead bottom half. Scale for presence: assertion headline `clamp(26px,4.4vw,52px)`, display math `clamp(19px,2.7vw,28px)` with generous card padding, callouts/traces at reading size `clamp(15px,2vw,19px)` / mono `clamp(12.5px,1.65vw,17px)` — the equation should dominate the slide, not whisper. Verify layout with a headless-Chrome screenshot (`--headless=new --screenshot ... '#slide-N'`, no virtual-time flags) and actually look at it before delivering. +- **Type system**: `Space Grotesk` for display (titles/heads, tight tracking), `Inter` body, `JetBrains Mono` for code/kickers, and `Instrument Serif` *italic* as the accent voice — `` inside leads/heads renders serif-italic in gold. Title = gradient text (ink → gold → peach). +- **Depth, not flatness**: body gets two fixed layers — aurora radial glows (gold top-left, sky bottom-right, mint accent) + an SVG-noise grain at ~3% opacity. Surfaces are **glass**: `rgba(255,255,255,.035)` + `backdrop-filter: blur(12-14px)` + 1px `rgba(255,255,255,.09)` border + deep soft shadow + inset top highlight. +- **Editorial details**: huge **ghost slide numerals** (Space Grotesk, ~4% opacity) top-right of every slide; kickers flanked by gradient hairlines; progress bar is a glowing gold→peach gradient. +- **Motion**: reveals rise with slight scale + **blur-in** (`blur(6px)→0`), staggered ~90ms; MCQ options nudge right + glow on hover; correct answers get a soft green glow. All gated by `prefers-reduced-motion`. +- **Color voice**: gold = emphasis/labels, mint = "in english"/success, sky = structure/where, rose = errors/failures, dim slate for prose. Light scheme maps to warm paper (#f7f3ea). +- Callout grammar: `.eng` (mint glass, ◍ prefix) for English readings; `.trace` (dark glass, ⌁ prefix) for numeric traces; `.ww` chips for where/why. **Blocks size to their content** (`width:fit-content`, capped by the column, sensible min-widths) — never full-width slabs around two words. **No colored left-border accents** on callouts; differentiate by the small-caps label color and surface tint only. + +## Learning-first rules (for teaching/explainer decks) + +When the deck's purpose is to teach (concepts, math, papers, systems): + +- **Honest evidence tags**: label every claim's status with a small pill — `real` (measured, from actual runs/files), `building` (in progress), `planned`, `polluted/void` (known-bad measurement). Never present illustrative numbers as real. +- **Code + concept travel together**: a formula or concept slide either includes the minimal code it corresponds to, or explicitly signposts "next slide: the code". +- **Retrieval practice**: end substantial teaching decks with a "Test yourself" slide before the final bleed — 2–3 **clickable multiple-choice questions** (`.mcq` pattern in `templates/slide-deck.html`: click an option → correct answer highlights green, wrong pick red, explanation appears). Plausible distractors, instant feedback. Recall beats re-reading; feedback beats bare recall. +- **Prediction before reveal**: for the 1–2 headline results, pose the question on its own slide first ("guess before you advance") and reveal on the next. Prediction-then-feedback beats passive presentation — delegated reading scores <40% on comprehension; active inquiry scores far higher. +- **Shared vocabulary**: name each core concept once with one vivid metaphor (e.g. KL = "leash", mixed data = "soup", repair = "orthogonal fix") and reuse the exact same term + color across every deck in the series. Never introduce synonyms for an established metaphor — shared words are what let author and reader riff. +- **At most one explorable**: a single small interactive element (slider/toggle driving a formula or plot) is allowed per deck when direct manipulation genuinely teaches the idea (e.g. vary ε and watch the clip region). Never decorative; the deck must teach fully with it inert. +- **Verify-yourself ending**: decks explaining work an agent did (code, training runs, changes) end with the one command or check the reader can run themselves to confirm the headline claim — the goal is a reader deeper in the loop, not just informed. +- **Depth-first (do not hide the concept)**: for technical/teaching decks, derive — don't assert. Every mechanism gets three layers on adjacent slides: (1) the math, derived in steps small enough to follow; (2) the concept the math encodes, named plainly; (3) *why it works* — the gradient/information/geometry-level reason. Plug the run's actual numbers into the derivations (real logprobs, real dims, real lr). Status/narrative slides are allowed only as connective tissue between depth slides, never the substance of the deck. +- **Executable traces**: when a trace derives from raw values (logprobs, logits), compute it **in-page with JS** from the embedded raw data (`.xtrace[data-lps]` and `.xslider` helpers in `templates/slide-deck.html`) instead of hand-typing results — hand arithmetic can silently be wrong; computed traces can be poked. +- **Fact ledger (provenance + retraction)**: projects with evolving claims keep a ledger (`scripts/facts.py`; JSONL of id/claim/value/source/status). Deck elements citing a ledger fact carry `data-fact="id"`. Retraction = `facts.py void` (one flag), then `facts.py verify *.html` finds every deck citing dead facts. Never state a `real` number that has no source. +- **Equation anatomy (no untranslated math)**: every displayed equation must be accompanied, on the same slide, by: (a) **where it fits** — one line locating it in the pipeline; (b) **why it exists** — the problem it solves; (c) **its English reading** — the formula spoken as a sentence, symbol by symbol ("loss = how surprised you were by the correct token"); (d) **a raw numeric trace** — concrete input values flowing through the formula to a concrete output (`p=0.344 → L=−ln 0.344=1.068 nats`). An equation without its sentence and its trace is not done. +- **Series wiring**: when 3+ decks cover one topic, create/refresh an index page (card grid, reading order, one-line summaries) and wire each deck with `data-series-prev/next/index` attributes on `` — the template's series-nav script renders prev/series/next links (keys `N`/`P`). The last deck's `next` points at the index. +- **Resume never lands on the end**: the engine must not restore a saved position equal to the final slide (start fresh instead); mid-deck resume stays. + ## Optional generated images If `surf` is available, generated images may be embedded as base64 for hero banners, conceptual illustrations, or educational visuals. Skip images for data-heavy, structural, or Mermaid/CSS-suitable content. Pages must stand on CSS, typography, and diagrams without images. +## Deck build discipline (agent workflow) + +- **Engine source of truth**: inject the SlideEngine from `templates/engine.js` in this skill's directory (resolve relative to SKILL.md). Never depend on `/tmp` copies or retype it. +- **One validation gate**: after writing any deck, run `python3 /scripts/check-deck.py .html` (managed clone: `~/Code/Zereraz/visual-explainer`). Fix every FAIL before delivering. Do not hand-roll per-deck validation snippets. + ## Final checklist Before delivery, verify: - complete HTML document; +- standard favicon `` present immediately after ``; +- decks use the upgraded SlideEngine (outline `O`, help `?`, deep-link + resume, reading %); +- each slide readable with animations disabled; no content depends on motion; - output written to the requested path; - no console errors when opened; - no horizontal overflow at normal desktop width; diff --git a/plugins/visual-explainer/commands/update-visual-explainer.md b/plugins/visual-explainer/commands/update-visual-explainer.md new file mode 100644 index 0000000..ead865d --- /dev/null +++ b/plugins/visual-explainer/commands/update-visual-explainer.md @@ -0,0 +1,28 @@ +--- +name: update-visual-explainer +description: Sync this visual-explainer fork with upstream, preserving local customizations; an agent resolves any merge conflicts. +--- + +Update the managed `visual-explainer` fork. This is **on-demand** — only run because the user asked. + +Managed clone location (try in order; use the first that exists): +- `~/Code/Zereraz/visual-explainer` +- the installed package dir: `~/.pi/agent/git/github.com/zereraz/visual-explainer` + +Steps: + +1. **Check first.** Run `bash scripts/update-fork.sh --check` from the managed clone. + - Exit `0` → already up to date. Tell the user "already current (fork is N commits ahead with your customizations)" and stop. + - Exit `10` → behind upstream; show the listed upstream commits, then continue. + +2. **Apply.** Run `bash scripts/update-fork.sh --apply`. + - Exit `0` → clean merge, pushed to the fork, and `pi update` refreshed the install. Report what upstream commits were merged and confirm the local customizations (favicon / house-style) survived. Done. + - Exit `20` → **merge conflict**. Do NOT abandon. Resolve it: + a. Read each conflicted file (`git diff --name-only --diff-filter=U`). + b. For each conflict, **keep the intent of BOTH sides**: upstream's new behavior AND our customizations (the favicon injection in `extension.ts`, favicon lines in `templates/*.html` and `SKILL.md`, the KaTeX `<` note). Our changes are additive — prefer integrating, not discarding. + c. After resolving every file, run `bash scripts/update-fork.sh --continue` to commit, push, and reinstall. + - Exit `2` → precondition failure (dirty tree / wrong branch). Read the message, fix it (commit/stash local work, or `git checkout main`), then retry. + +3. **Verify** after success: confirm `grep -c ensureFavicon plugins/visual-explainer/extension.ts` is non-zero and the 4 templates still contain `rel="icon"`. If a customization was lost in the merge, re-apply it and amend. + +Report concisely: what changed upstream, whether conflicts occurred, how they were resolved, and that customizations are intact. $@ diff --git a/plugins/visual-explainer/extension.ts b/plugins/visual-explainer/extension.ts index 57de38f..ab0c9c8 100644 --- a/plugins/visual-explainer/extension.ts +++ b/plugins/visual-explainer/extension.ts @@ -128,6 +128,41 @@ function outputFilename(input: string) { return /\.html?$/i.test(raw) ? raw : `${raw}.html`; } +const STANDARD_FAVICON = + ''; + +// Guarantees the house-style favicon is present, regardless of what the agent emitted. +function ensureFavicon(html: string): string { + if (/]*rel\s*=\s*["']?(?:shortcut\s+)?icon/i.test(html)) return html; + if (/<\/title>/i.test(html)) return html.replace(/<\/title>/i, `\n${STANDARD_FAVICON}`); + if (/]*>/i.test(html)) return html.replace(/]*>/i, (m) => `${m}\n${STANDARD_FAVICON}`); + return html; +} + +// Reader-safety: inside $$...$$ display math, escape < and > so the HTML +// parser doesn't treat e.g. y_{ { + const fixed = inner.replace(//g, ">"); + return `$$${fixed}$$`; + }); +} + +// Reader-safety: a complete, well-formed document for any browser/offline read. +function ensureDocMeta(html: string): string { + let out = html; + if (!/]*\blang=/i.test(out)) out = out.replace(/]*>/i.test(out)) { + if (!/]+name=["']viewport/i.test(out)) + out = out.replace(/]*>/i, (m) => `${m}\n`); + if (!/]+name=["']theme-color/i.test(out)) + out = out.replace(/]*>/i, (m) => `${m}\n`); + } + return out; +} + function assertHtmlDocument(html: string) { const trimmed = html.trim(); if (!trimmed) throw new Error("html is required"); @@ -276,7 +311,9 @@ async function renderVisualExplanation(params: VisualExplainerParams, signal?: A } signal?.throwIfAborted(); - writeFileSync(outputPath, params.html, "utf8"); + // Reader-safety pipeline: fix truncating math, complete the doc head, guarantee favicon. + const finalHtml = ensureFavicon(ensureDocMeta(fixDisplayMath(params.html))); + writeFileSync(outputPath, finalHtml, "utf8"); signal?.throwIfAborted(); diff --git a/plugins/visual-explainer/templates/architecture.html b/plugins/visual-explainer/templates/architecture.html index edc1b43..87f3548 100644 --- a/plugins/visual-explainer/templates/architecture.html +++ b/plugins/visual-explainer/templates/architecture.html @@ -4,6 +4,7 @@ Architecture Diagram — Reference Template +