A quick map of how the app is put together, so changes don't require re-reading everything. For the chronological change history see DEVLOG.md.
- Shell: Tauri 2 (Rust backend + system WebView).
- Frontend: React 19 + TypeScript + Vite 8, Tailwind CSS v4.
- Editor: TipTap 3 (ProseMirror). Mind map: React Flow (
@xyflow/react12). - Icons: lucide-react. Grammar: LanguageTool API (
grammar-service.ts).
Project { id, name, description?, author?, created_at, path?, documents[], folders[] }
Folder { id, name, order, parentId? } # parentId null ⇒ project root
Document { id, title, content, updated_at, docType: 'text'|'mindmap', order, folderId? }
textdoc →contentis rich-text HTML (TipTap).mindmapdoc →contentis JSON{ nodes, edges }(React Flow).- Directories:
folders[]is a nestable tree (any depth viaparentId). A document'sfolderIdis the directory it lives in (null/absent ⇒ root). Folders persist inproject.json(saved viasave_project);folderIdpersists on eachdocuments/<id>.json(saved viasave_document). Move = updatefolderId/parentId+ re-save. Deleting a folder lifts its children to the parent (no documents are deleted). The explorer supports both drag-and-drop and a right-click "Move to" menu. - New Rust fields use
#[serde(default)]so olderproject.jsonfiles still load.
A project is a folder (custom path, or ~/.mnemoscript/projects/<id> by default):
<project>/
project.json # metadata (+ a documents snapshot)
documents/<id>.json # one file per document (source of truth on open)
assets/<uuid>.<ext> # imported images
A global registry at ~/.mnemoscript/registry.json maps project id → folder path.
list_projects reads metadata only; opening a project uses load_project, which reads the
documents/ folder (so newly added docs show up).
All return an ApiResponse<T> = { success, data, error } envelope.
create_project · save_project · load_project · list_projects · open_project_by_path ·
create_document (takes docType/order) · save_document · load_document ·
import_image (picker → copy to assets/ → returns absolute path) · select_directory.
src/lib/api.ts— the only place that callsinvoke; typed, unwrapsApiResponse, throws on failure. Always go through this.src/lib/assets.ts—toAssetUrl(path)andresolveImagesInHtml(html). Imagesrcis stored as a raw disk path and resolved to a Tauri asset URL (convertFileSrc) only at render time. Asset protocol is enabled intauri.conf.json(app.security.assetProtocol) +Cargo.toml(protocol-assetfeature).App.tsx— owns app state, persistence (auto-save loop + manual save via refs), and routing: renders<MindMap>fordocType==='mindmap', else<Editor>. localStorage holds only UI prefs.
StarterKit (+ heading/list keymaps) · Placeholder · TextAlign · TaskList/TaskItem
(/todo checkbox lists, nestable) · LinguisticCheck (grammar) · ImageWithAsset
(asset-rendering image node) · SlashCommand (the / menu).
The slash menu (SlashCommand.ts + SlashMenu.tsx + slashItems.ts) is built on
@tiptap/suggestion; its React popup is positioned at the caret via the suggestion clientRect
(same manual technique as the grammar popover — no tippy.js).
To-do lists are a Notes-only feature: the Editor + RightSidebar ("To-do List" button)
render only for docType==='text', so task lists never appear in mind maps or fantasy maps.
Type /todo (or click the sidebar button) to insert a checkbox list; checked items strike
through. They serialise to HTML in Document.content like every other block — no extra
persistence. Checkbox styling lives in the .ProseMirror ul[data-type="taskList"] rules.
MindMap.tsx— React Flow canvas; serializes{nodes,edges}tocontent(debounced).BookCompiler.tsx— File → "Compile to PDF Book": builds a print-CSS book HTML in a hidden iframe and callsprint()→ "Save as PDF". Text chapters only (mind maps excluded for now).
Theme tokens (6 themes) live in src/index.css (body.theme-* CSS variables + Tailwind v4
@theme). Feature CSS (.mindmap-*, .slash-menu, .editor-image) is appended there.
Note: src/App.css is legacy and not imported — don't edit it; use index.css.
cd app && npm run lint && npm run build · cd app/src-tauri && cargo check.