# Ferrite v0.3.1 — Mermaid Wave 2, Embeds, Multi-Window, Data UX & Polish > **Status:** Formal PRD — source of truth for v0.3.1 scope. > **Consumers:** Human orchestrator + AI implementation sessions. Parse into the custom task orchestrator (Task Master is retired — see §9). > **Supersedes:** The Task Master-era `prd-v0.3.1.md`. Merges `pre-prd-v0.3.1.md` (updated 2026-06-09) including the **LSP deferral pivot**, product decisions, and GitHub [#142](https://github.com/OlaProeis/Ferrite/issues/142), [#144](https://github.com/OlaProeis/Ferrite/issues/144), [#145](https://github.com/OlaProeis/Ferrite/issues/145). ## 1. Overview Ferrite is a cross-platform Rust + egui markdown editor. **v0.3.0** (May 2026) shipped the platform refresh (egui **0.34.2**, Rust **1.92** MSRV), PDF/HTML export, executable code blocks, the rendered edit session, and Mermaid first wave. **v0.3.1** is the next feature release. **Theme (revised):** **Mermaid wave 2**, rich embeds, multi-window, data/table UX, GitHub HTML subset, and polish — **not LSP**. The LSP epic is deferred in full (§1 deferral table); v0.3.1 engineering capacity goes to Mermaid instead. ### Release pillars 1. **Mermaid second wave (headline)** — Git graph rewrite ([#83](https://github.com/OlaProeis/Ferrite/issues/83) parity), mmdr evaluation, manual `%% @pos` layout, flowchart polish (FC-83b, `linkStyle`). 2. **Embedded video rendering** — `wry` WebView with mandatory thumbnail fallback ([#119](https://github.com/OlaProeis/Ferrite/issues/119)). Parsing already landed (§4). 3. **Multi-window** — OS-level second window for side-by-side document work ([#125](https://github.com/OlaProeis/Ferrite/issues/125)). 4. **GitHub HTML parity** — Phases 1–2 only (block + inline safe subset). 5. **Data & table UX** — GFM table column alignment ([#140](https://github.com/OlaProeis/Ferrite/issues/140)), raw-mode column guides. 6. **Preview & workflow QoL** — Preview lock mode ([#144](https://github.com/OlaProeis/Ferrite/issues/144)), word-wrap toggle shortcut ([#145](https://github.com/OlaProeis/Ferrite/issues/145)), external open fallback for unsupported files ([#142](https://github.com/OlaProeis/Ferrite/issues/142)). ### Product decisions (resolved 2026-06-09) | Question | Decision | |----------|----------| | **LSP in v0.3.1** | **Deferred — all phases** (v0.3.2 or later). Stays behind the `lsp` Cargo feature flag; no problems panel, no flag removal this release. Capacity goes to Mermaid. | | Preview lock default | **Unlocked** (current editable behaviour) | | Preview lock persistence | **Stays locked/unlocked until the user toggles** — survives view-mode switches, tab switches, and **session restore** | | Preview lock scope | **All preview/rendered panes** — markdown WYSIWYG, CSV/TSV table, Tree viewer, any split-right viewer | | Word wrap toolbar icon | **No** — shortcut + command palette only; toolbar icon is **Tier C, explicitly deferred** | | Completed work (video parse, CSV editing) | **Excluded from the task list entirely** — no implementation tasks, no verification tasks | ### Deferred to a future release (do not plan in v0.3.1) | Feature | Target | Notes | |---------|--------|-------| | **LSP integration (all phases)** | **v0.3.2 or later** (confirm in ROADMAP sync) | Remains behind the `lsp` feature flag; deferral noted in CHANGELOG | | macOS Developer ID signing / notarization | Not planned (cost) | [#130](https://github.com/OlaProeis/Ferrite/issues/130) stays docs-only: [`docs/install/macos.md`](../../install/macos.md) | | FerriteEditor crate extraction | v0.3.2 | | | Mermaid standalone crate extraction | v0.3.2 | | | GitHub HTML Phase 3 (nested HTML, HTML tables) | v0.3.2 | | | Additional file-format viewers (XML, INI, log) | v0.3.2 | | | RTL/BiDi Phases 3–4 | v0.4.0 | | | LaTeX math rendering | v0.4.0 | | | Office documents (DOCX/XLSX/ODT) | v0.4.0 | Until then, #142 opens them externally | ### GitHub issues mapped to this PRD | Issue | Scope | Tier | |-------|-------|------| | [#106](https://github.com/OlaProeis/Ferrite/issues/106) | Wayland keyboard verification (§5.1) | A | | [#111](https://github.com/OlaProeis/Ferrite/issues/111) | Sonoma keyboard verification (§5.1) | A | | [#112](https://github.com/OlaProeis/Ferrite/issues/112) | Windows borderless verification (§5.1) | A | | [#119](https://github.com/OlaProeis/Ferrite/issues/119) | Video embed rendering (§5.2) | A | | [#83](https://github.com/OlaProeis/Ferrite/issues/83) | Mermaid git graph parity (§5.3) | A | | [#125](https://github.com/OlaProeis/Ferrite/issues/125) | Multi-window (§5.4) | A | | [#140](https://github.com/OlaProeis/Ferrite/issues/140) | Table column alignment (§6.2) | B | | [#135](https://github.com/OlaProeis/Ferrite/issues/135) | File tree hover/active emphasis (§6.3) | B | | [#144](https://github.com/OlaProeis/Ferrite/issues/144) | **Preview lock mode (§6.8)** | **B** | | [#145](https://github.com/OlaProeis/Ferrite/issues/145) | **Word wrap toggle shortcut (§6.9)** | **B** | | [#142](https://github.com/OlaProeis/Ferrite/issues/142) | **External file open fallback (§6.10)** | **B** | | [#115](https://github.com/OlaProeis/Ferrite/issues/115) | Optional native title bar (§7.1) | C | --- ## 2. Goals - **Diagrams (headline):** Git graph is visually credible (horizontal lanes); the mmdr path is evaluated with a written decision; manual layout is authorable via `%% @pos`. - **Rich preview:** Trusted video embeds render interactively, with a safe thumbnail fallback when the native WebView path fails. - **Productivity:** Two documents visible in two OS windows; unsupported files open in the OS default app instead of erroring (#142). - **Publishing fidelity:** Common GitHub HTML constructs render in the preview without scripts/iframes. - **Safety & QoL:** Users can lock any preview pane so reading never mutates files (#144); word wrap toggles instantly from the keyboard (#145). - **Polish:** Close v0.3.0 follow-ups (cursor precision, code-run edge cases, FC-83b, table alignment #140). ## 3. Non-goals - **No LSP work in v0.3.1** — the entire epic (transport hardening, problems panel, hover, go-to-def, completion, flag removal) is deferred to v0.3.2 or later. The `lsp` feature flag and existing feature-gated code stay as-is. CHANGELOG must note the deferral. - No Apple code signing or notarization for CI artifacts. - No FerriteEditor or Mermaid **crate extraction** (v0.3.2). - No new Mermaid diagram types beyond the git-graph rewrite + polish unless the mmdr spike proves trivial (types land in v0.3.2). - No full iframe / arbitrary HTML / script execution in rendered view. - No replacement of single-instance with many unrelated Ferrite processes unless required by the multi-window design (prefer one process, multiple viewports). - No re-implementation (or "verification" tasks) for already-completed work: video embed **parsing** and CSV rendered cell editing are done (§4). - Preview lock does **not** make the Raw editor read-only — Raw and the Split raw pane are always editable. - No word wrap toolbar/ribbon icon in Tier B (Tier C, explicitly deferred). --- ## 4. Completed work — context only, generate NO tasks > **Rule for the orchestrator:** the following are **done in the local codebase** (not yet pushed). Do **not** generate implementation tasks or verification tasks for them. | Former task | Feature | Status | Evidence | |-------------|---------|--------|----------| | Task 7 | Video embed **parsing** | **Done locally** | `src/markdown/video_embed.rs` (allowlist, `extract_video_embeds()`, unit tests); `parser.rs` `VideoProvider` / `VideoEmbedInfo` / `MarkdownNodeType::VideoEmbed`; `widgets.rs` round-trips `source_text`; docs [`video-embed-parsing.md`](../../technical/markdown/video-embed-parsing.md) | | Task 15 | CSV rendered cell editing | **Done locally** | `csv_viewer.rs` (`CsvCellEditParams`, double-click edit, Enter/Escape, arrow navigation, `serialize_csv_rows` RFC 4180 + tests, 1 MB gate + banner); undo wired in `central_panel.rs`; docs [`csv-viewer.md`](../../technical/viewers/csv-viewer.md) § Rendered Cell Editing | Video **rendering** (wry + thumbnail fallback) is *not* done — `markdown/editor.rs` has no `VideoEmbed` render path yet. That is Tier A work (§5.2). Also shipped in v0.3.0 (context, no tasks): CSV cell overflow fix, rendered edit session (tasks 94–105, archived in `docs/ai-workflow/tasks/tasks-v0.3.0-archive-2026-05-31.json`). --- ## 5. Functional requirements — Tier A (must ship, release blockers) ### 5.1 Platform verification (carry-over from v0.3.0) - Execute [`v0.3.0-regression-matrix.md`](../../technical/platform/v0.3.0-regression-matrix.md) rows still blank on target OSes, focusing on **KBD-8** (Wayland, #106) and **KBD-9** (Sonoma, #111), plus Windows borderless (#112). - Document outcomes in CHANGELOG; close issues when confirmed fixed, or file scoped follow-ups. - **Key files:** none (manual QA + docs). ### 5.2 Embedded video — rendering ([#119](https://github.com/OlaProeis/Ferrite/issues/119)) Parsing is **done** (§4). This is rendering only: - **Primary path:** [`wry`](https://lib.rs/crates/wry) child WebView positioned over the embed rect each frame; hide when off-screen or tab inactive; only for `VideoEmbedInfo { trusted: true }`. - **Fallback (required, build first):** YouTube thumbnail (`img.youtube.com/vi//...`) + play affordance → open system browser. Must work even if wry fails or is unavailable on a platform. Untrusted embeds (`trusted: false`) get text/thumbnail fallback only — never a WebView. - **Extensibility:** keep `VideoProvider` open for future providers (Vimeo etc., v0.3.2+). - **Security:** no arbitrary URL iframes; the allowlist in `video_embed.rs` is the single gate for the WebView path. - **Preview lock interaction:** playing a video is a read action — allowed while locked (§6.8). - **Key files:** `src/markdown/editor.rs` (render path for `MarkdownNodeType::VideoEmbed`), new render module (e.g. `src/markdown/video_render.rs`), `Cargo.toml` (wry), `src/app/central_panel.rs` (overlay lifecycle). - **Docs:** new `docs/technical/markdown/video-embeds.md` (rendering, wry, fallback, security). ### 5.3 Mermaid — second wave (headline epic) This is the headline engineering investment of v0.3.1 (it absorbs the capacity freed by the LSP deferral). Four sub-features, independent of each other except where noted. All rendering is **native egui** — Ferrite has no JS/web runtime for diagrams. **Current state (verified in code, 2026-06-09):** - Mermaid lives in `src/markdown/mermaid/` — flowchart is modular (`flowchart/{types,parser,layout/,render/}.rs` with a Sugiyama layered layout), other diagram types are single files (`git_graph.rs`, `sequence.rs`, `state.rs`, …). - Parse + layout results are cached per code block via blake3 content hashing ([`mermaid-caching.md`](../../technical/mermaid/mermaid-caching.md)) — any new syntax (e.g. `@pos` hints) participates automatically because the hash covers the full source, including comment lines. - Parse-time validation shows a warning header (line + hint), falls back to last-good render, and squiggles the raw editor ([`mermaid-inline-validation.md`](../../technical/mermaid/mermaid-inline-validation.md)). New parse errors/warnings must integrate with this pipeline, not panic. - Rendering is panic-guarded with `catch_unwind` ([`flowchart-crash-prevention.md`](../../technical/mermaid/flowchart-crash-prevention.md)). - The feature/status map vs Mermaid.js is [`mermaid-parity-matrix.md`](../../technical/mermaid/mermaid-parity-matrix.md); repro fixtures live in `test_md/` (FC-83a/FC-83b in `test_md/test_mermaid_issue_83.md`). #### 5.3.1 Git graph rewrite ([#83](https://github.com/OlaProeis/Ferrite/issues/83) parity) **Problem.** `git_graph.rs` (~290 lines) renders a **vertical list stack**: each commit gets its own row (`y = commit_index × spacing`), branches are fixed x-columns, and commit labels sit in a left gutter. Mermaid.js renders the opposite orientation by default: **time flows left→right on the x-axis and each branch is a horizontal lane (row)**, with curved merge/branch connectors between lanes. The current output is not visually credible for real-world graphs (parity matrix: "Partial — vertical list; not horizontal lane layout"). **Parser — current coverage and required additions.** The existing parser handles `commit` (with `id:"…"` and `msg:"…"` options), `branch `, `checkout ` (implicitly creating unknown branches), and `merge ` (with `id:`). The rewrite must extend it with the remaining core Mermaid gitGraph grammar: | Syntax | Current | Required | |--------|---------|----------| | `commit id:"…"` / `msg:"…"` | Parsed | Keep | | `commit tag:"v1.0"` | **Ignored** | Parse; render tag label near the commit dot | | `commit type: NORMAL\|REVERSE\|HIGHLIGHT` | **Ignored** | Parse; distinct dot rendering (e.g. cross for REVERSE, filled ring for HIGHLIGHT) | | `branch order: ` | `order:` ignored | Parse; controls lane ordering | | `checkout` / `switch` | `checkout` only | Accept `switch` as alias | | `cherry-pick id:"…"` | **Ignored (silently dropped)** | Parse; render dot + dashed connector to source commit; unknown id → validation warning | | `gitGraph LR:` / `gitGraph TB:` header | **Ignored** | Parse; LR (default) = horizontal lanes, TB = current vertical orientation retained as the TB mode | | Quoted branch names (`branch "feat/x"`) | Unquoted only | Strip quotes | Unknown statements must produce a **validation warning** (via the §5.3 validation pipeline), not silent omission and not a hard parse error. **Layout model (the core of the rewrite).** - Assign each branch a **lane index** (row): `main` (or the first branch seen) at lane 0, then declaration order, overridden by `order:` when present. - Assign each commit a **sequence index** (column) in declaration order — Mermaid does the same; no topological re-sort is needed. - Commit dot position = `(margin + seq × commit_spacing, margin + lane × lane_spacing)` for LR; transpose for TB (mirrors the axis-transform approach in [`flowchart-direction.md`](../../technical/mermaid/flowchart-direction.md)). - **Branch line:** for each branch, a horizontal polyline in the branch color from its first to last commit; the line starts with a **branch-off curve** from the parent branch's commit where the branch was created. - **Merge connector:** curved line from the last commit of the source lane into the merge commit's dot on the target lane (the existing 3-segment bezier approximation may be reused; quality bar is "smooth, no overlap with dots"). - Keep the existing per-branch color cycling and dark/light palettes; merge-commit dots keep the distinct outlined style. - Labels: branch name labels at the lane's left edge (LR); `id`/`msg` under or above the dot, truncated with tooltip on hover if wide; `tag:` rendered as a small rounded label offset from the dot. - Sizing: the painter must be allocated from real computed bounds (sum of lanes / max sequence), not hardcoded constants — same lesson as FC-83a (see [`flowchart-edge-obstacle-routing.md`](../../technical/mermaid/flowchart-edge-obstacle-routing.md), "painter sized from real node bounds"). **Module shape.** Split `git_graph.rs` into a submodule if it grows past ~500 lines (`git_graph/{types,parser,layout,render}.rs`), following the flowchart modular refactor pattern ([`flowchart-modular-refactor.md`](../../technical/mermaid/flowchart-modular-refactor.md)). **Acceptance criteria:** 1. 2–3 real-world `gitGraph` fixtures (feature-branch + merge; multi-branch with `order:`; tags + cherry-pick) added under `test_md/` and compared manually against [Mermaid Live](https://mermaid.live) — lane structure, merge topology, and labels must match (pixel-exactness not required). 2. `LR` (default) and `TB` orientations both render. 3. `tag:`, `type:`, `cherry-pick` visible per the table above; unknown statements warn, never panic. 4. Parser unit tests for every new grammar row; layout unit tests for lane assignment and merge endpoints. 5. Existing blake3 cache and `catch_unwind` guard still wrap the new render path. **Docs:** new `docs/technical/mermaid/git-graph-layout.md` (lane model, grammar table, fixtures). #### 5.3.2 mmdr evaluation (deliverable = decision doc + spike, NOT integration) **Question to answer:** should Ferrite adopt [mmdr](https://github.com/1jehuang/mermaid-rs-renderer) (a Rust Mermaid parser/renderer) as a **parser frontend** for diagram types Ferrite lacks, instead of hand-writing more parsers in v0.3.2? - **Spike:** add mmdr with `default-features = false` in a throwaway branch/worktree; feed it 3–5 diagram sources Ferrite does not support today (candidates from the parity matrix gap list: quadrant, requirement, C4, sankey, xychart). Inspect the AST it produces. - **Evaluate and write up:** API stability/versioning, dependency weight (transitive deps, compile time, binary size), license compatibility, AST→Ferrite-layout mapping effort per diagram type, maintenance risk (bus factor, release cadence). - **Deliverable:** `docs/technical/mermaid/mmdr-evaluation.md` with a clear **adopt / partial-adopt / reject** recommendation and a proposed v0.3.2 rollout order if positive. - **Hard constraint:** the native egui render pipeline is **not** replaced in v0.3.1; no mmdr code ships in the release binary. #### 5.3.3 Manual layout — `%% @pos` hints **Goal:** let authors pin node positions when the automatic Sugiyama layout fights them, without breaking Mermaid.js compatibility — `%%` lines are comments to Mermaid, so hinted diagrams still render (auto-laid-out) everywhere else. - **Syntax:** `%% @pos ` on its own line anywhere in a `flowchart`/`graph` block. `x`/`y` are layout-space coordinates (document the unit and origin in the doc deliverable). **Scope: flowcharts only in v0.3.1**; state explicitly in docs that other types ignore hints. - **Parse:** collect hints in `flowchart/parser.rs` (which currently skips `%%` comment lines — they already reach the parser, so no pipeline change). Store as `HashMap` on the parsed graph. - **Layout:** run the normal Sugiyama pass first, then **override** positions of hinted nodes. Edges connected to hinted nodes re-anchor to the overridden rects (the obstacle-routing pass from FC-83a must consume final positions, not pre-override ones). Hinted nodes are excluded from `resolve_layer_overlaps` adjustments. - **Validation:** unknown node id, malformed coordinates, or duplicate hints for one node → warning via the inline-validation header (line number + hint); the hint is ignored and auto-layout used for that node. Never a hard error. - **Caching:** no special handling needed — hints are part of the source text, so the blake3 key changes when hints change. - **Out of scope (Tier C, §7.2):** drag-to-reposition in the rendered view with `@pos` write-back to source. **Acceptance criteria:** 1. A fixture diagram with 2+ hinted nodes renders them at hinted positions while unhinted nodes auto-layout; edges stay attached. 2. Invalid hints produce the standard warning header and do not affect other nodes. 3. The same source renders identically in Mermaid Live (hints ignored as comments) — round-trip compatibility confirmed in the fixture file. **Docs:** new `docs/technical/mermaid/manual-layout.md`. #### 5.3.4 Flowchart polish (FC-83b + linkStyle) Both gaps are documented in the parity matrix and reproduce in `test_md/test_mermaid_issue_83.md`: - **FC-83b — `fa:fa-*` icon labels:** labels like `B["fa:fa-car Car"]` currently render the literal `fa:fa-car` text (no handling anywhere in `mermaid/`). Strip the `fa:fa-` / `fab:fa-` prefix from the displayed label (optionally substitute a generic Phosphor placeholder icon — author's choice, document it). Applies to node labels and edge labels. - **`linkStyle … interpolate basis`:** the `interpolate` property is currently ignored (straight segments remain). Either (a) degrade gracefully: accept and discard the property **and** document it as unsupported in the Mermaid help (F1) + parity matrix, or (b) implement a curved (bezier/catmull-rom) edge path when `interpolate basis` is present. (a) is the minimum bar; (b) is preferred if edge-routing work makes it cheap. - **Stretch (same code area, take if trivial):** `linkStyle … stroke-dasharray` (parity matrix P2). **Acceptance criteria:** FC-83a/FC-83b repro files render without literal `fa:` text and without parse warnings for `interpolate`; parity matrix + F1 Mermaid help updated to reflect the final supported/unsupported status. - **Key files (whole epic):** `src/markdown/mermaid/git_graph.rs` (→ possible submodule), `flowchart/{parser,types}.rs`, `flowchart/layout/` (sugiyama, config), `flowchart/render/edges.rs`, `validation.rs`, `mermaid-parity-matrix.md`, `ui/about.rs` (F1 Mermaid help). ### 5.4 Multi-window ([#125](https://github.com/OlaProeis/Ferrite/issues/125)) - **Design doc before implementation:** `docs/technical/platform/multi-window.md` — process model (prefer one process + multiple viewports), tab ownership, single-instance interaction, session persistence shape. - **User-visible MVP:** menu/window action **New Window**; second OS window with its own tab strip; open file in the focused window. - **Single-instance:** second OS launch either opens a new window in the existing process or focuses an existing window and opens tabs there — document the chosen behaviour ([`single-instance.md`](../../technical/platform/single-instance.md)). - **Not required for MVP:** Productivity Hub pop-out on second monitor (follow-up uses the same viewport APIs). - **QA:** Windows, macOS, Linux X11 + Wayland. - **Key files:** `src/app/` (window lifecycle), `src/state.rs` (tab ownership), `src/platform/`, `src/config/session.rs`. ### 5.5 Release docs sweep - New/updated docs per §10, `docs/index.md` links, `ROADMAP.md` + `CHANGELOG.md` updated, deferral notes (**LSP epic**, any cut Tier B) with issue links. --- ## 6. Functional requirements — Tier B (should ship; cut only with CHANGELOG justification) ### 6.1 GitHub HTML parity — Phases 1 & 2 **Phase 1 — Block** - `
` … `
` - `
` / `` (collapsible; document default open state) - `
` where comrak does not already emit breaks **Phase 2 — Inline** - ``, ``, `` - `` (respect dimensions in rendered view) Safe subset only: **no** `