Ferrite - AI Context

June 9, 2026 · View on GitHub

Rust (edition 2021) + egui 0.34.2 markdown editor. Immediate-mode GUI — no retained widget state, UI rebuilds each frame.

Rules (DO NOT UPDATE)

  • Never auto-update this file or current-handover-prompt.md — only update when explicitly requested.
  • Only do the task specified; do not start the next task or go over scope.
  • Run cargo build or cargo check after changes to verify code compiles.
  • Follow existing code patterns and conventions.
  • Document by feature (e.g., lsp-inline-diagnostics.md), not by task.
  • Update docs/index.md when adding new documentation.
  • Use Context7 MCP tool to fetch library documentation when needed (resolve library ID first, then fetch docs).

Tech Stack

  • Language: Rust 2021 (MSRV 1.92), egui 0.34.2 + eframe (glow on Windows; bumped 0.28 → 0.31 → 0.34 — see docs/technical/platform/eframe-egui-031-upgrade.md, eframe-egui-034-upgrade.md)
  • Text: ropey (rope buffer), comrak (Markdown AST), syntect (syntax highlighting), harfrust (OTL shaping)
  • Terminal: portable-pty + vte | VCS: git2 | Dialogs: rfd | i18n: rust-i18n | Hashing: blake3 | PDF read: hayro | PDF write: krilla + krilla-svg
  • Memory: mimalloc (Windows), jemalloc (Unix)

Architecture

ModulePurpose
app/Main application (~15 modules: keyboard, file_ops, formatting, navigation, etc.)
state.rsAll application state (AppState, Tab, TabKind, SpecialTabKind, FileType)
editor/ferrite/Rope-based editor (ropey): buffer, cursor, history, view, rendering, line_cache
editor/widget.rsEditorWidget wrapper, integrates FerriteEditor via egui memory
markdown/editor.rs (rendered view), rendered_session.rs (edit session coordinator), rendered_commit_undo.rs (commit-boundary undo queue), code_execution.rs, …
terminal/Integrated terminal (PTY, VTE, screen, themes, split layouts)
ui/Panels: ribbon, settings, file_tree, outline, search, terminal, productivity, frontmatter, welcome, command_palette
config/Settings persistence, session/crash recovery, snippets
fonts.rsFont loading, lazy CJK, complex script lazy loading (11 families)
theme/Light/dark themes, user accent (accent.rs, ThemeManager sync)
vcs/git.rs, workspaces/, export/, preview/, platform/Git, folder mode, HTML+PDF export (export/pdf/ = krilla 2-pass renderer), sync scroll, platform-specific

FerriteEditor: src/editor/ferrite/ — rope-based, O(log n) ops, virtual scrolling, multi-cursor, code folding, IME/CJK. ~1x file size RAM. EditorWidget creates/retrieves from egui memory. Docs: docs/technical/editor/architecture.md

Critical Patterns

// Always use saturating math for line indices
let idx = line_number.saturating_sub(1);

// Never unwrap in library code
if let Some(tab) = self.tabs.get_mut(self.active_tab) { ... }

// Prefer borrowing over clone
fn process(text: &str) -> Vec<&str> { text.lines().collect() }

Common Gotchas

IssueWrongRight
Byte vs char indextext[start..end] with char posUse text.char_indices() or byte offsets
Line indexingMixing 0/1-indexedExplicit: line.saturating_sub(1)
CPU spinAlways request_repaint()Use request_repaint_after() when idle

Conventions

  • Logging: log::info!, log::error! (not println!)
  • i18n: t!("key.path"), keys in locales/en.yaml
  • State: Tab for per-tab, AppState for global
  • Errors: User-facing via show_toast(), technical via log::error!
  • Large files (>1MB): Hash-based is_modified(), reduced undo groups (200 vs 500), no original_bytes
  • Background file loading (≥5MB): open_file_smart() on FerriteApp spawns background thread; Tab.tab_content (TabContent::Loading/Ready/Error) tracks state; FileLoadMsg channel polled in update()
  • Per-frame caching: Tab.content_version (u64) gates cached is_modified(), text_stats(), needs_cjk_cached(), needs_complex_script_cached() — never scan full content per frame

Where Things Live (common)

Want to...Look in...
Add a settingconfig/settings.rsSettings struct
Add keyboard shortcutapp/keyboard.rshandle_keyboard_shortcuts()
Add command to paletteconfig/settings.rsShortcutCommand, app/commands.rs → icon, app/central_panel.rs → dispatch
Add/modify a UI panelui/ → create or edit panel module
Modify editor coreeditor/ferrite/editor.rs (behavior), buffer.rs (text), view.rs (viewport)
Modify markdown renderingmarkdown/editor.rs or markdown/widgets.rs
Rendered edit sessionmarkdown/rendered_session.rs; hub: rendered-edit-session.md; block wiring in editor.rs
Rendered commit undomarkdown/rendered_commit_undo.rs (pre/post snapshot queue); Tab::apply_rendered_commit_undo_entries in state.rs; drained from central_panel.rs after MarkdownEditor::show
Session / crash recoveryconfig/session.rs (RecoveryContent, autosave); state.rs resolve_tab_contentsession-persistence.md
Rendered widget id scopemarkdown/editor.rs push_id(editor_id + source_epoch)rendered-widget-identity.md
External invalidation epoch (source_epoch)state.rsTab::source_epoch(), bump_source_epoch(), record_external_edit_from_snapshot()
Rendered table cells (hits, Tab focus)markdown/widgets.rs EditableTabletable-cell-focus-navigation.md; session/force-commit in rendered-edit-session-tables.md
Modify markdown parsingmarkdown/parser.rs
Video embed parsingmarkdown/video_embed.rs, parser.rs (VideoEmbed AST) — video-embed-parsing.md
Modify central panelapp/central_panel.rs
Add special tabstate.rsSpecialTabKind, app/central_panel.rs
Add viewer tabstate.rsTabKind variant + state struct, app/central_panel.rs → render method
Add global/per-tab statestate.rsAppState / Tab struct
Add i18n stringlocales/en.yaml + t!("key")
Mermaid diagramsmarkdown/mermaid/ (flowchart has types, parser, layout/, render/); insert snippets: mermaid/templates.rs; F1 Mermaid help: ui/about.rs + docs/technical/mermaid/mermaid-syntax-help.md; validation: mermaid/validation.rs
Themed HTML exportexport/html.rs, export/html_options.rs, export/flowchart_svg.rs; app/dialogs.rs (HTML export dialog)
PDF print previewapp/export.rs (handle_print_preview), state.rs (PdfViewerState ephemeral temp + session skip); docs: docs/technical/viewers/print-preview.md
Terminalterminal/ (pty, screen, widget, layout)
Git/VCSvcs/git.rs
Workspace file indexworkspaces/file_index.rs — full-tree walk for Ctrl+P / search (not lazy tree)

Performance Rules (FerriteEditor)

TierWhen AllowedExamples
O(1)Alwaysline_count(), is_dirty()
O(log N)Alwaysget_line(idx), index conversions
O(visible)Per-frameSyntax highlighting visible lines
O(N)User-initiated ONLYFind All, Save, Export

Never call buffer.to_string() in per-frame code.

Recently Changed

  • 2026-05: Workspace file index — Background walkdir index for Ctrl+P and Ctrl+Shift+F (full tree, not lazy sidebar); progress bar on large folders. See docs/technical/files/workspace-file-index.md.
  • 2026-05: v0.3.0 — egui/eframe 0.34.2, Rust 1.92 MSRV, skrifa text backend, Popup/Tooltip API migrations, HarfRust validation, Phosphor 0.12. See docs/technical/platform/eframe-egui-034-upgrade.md.
  • 2026-05: User-configurable Ferrite accent (Settings.accent_color): Settings/Welcome color picker; drives headings, selection tint, tabs, view R/S/V segment, productivity hub, status LSP/branch; markdown links unchanged. See docs/technical/ui/theme-system.md.

Build & Test

cargo build          # Build debug
cargo run            # Run app
cargo clippy         # Lint
cargo test           # Run tests