diff --git a/.gitignore b/.gitignore index 2eaca73..23900ff 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,5 @@ dist-ssr target AGENTS.md coverage -.sandbox \ No newline at end of file +.sandbox +.fallow/ diff --git a/docs/release/v0.3.0.md b/docs/release/v0.3.0.md index d9c6be9..e945c5d 100644 --- a/docs/release/v0.3.0.md +++ b/docs/release/v0.3.0.md @@ -1,7 +1,7 @@ --- title: v0.3.0 Release Notes -date: 2026-03-24 -status: draft +date: 2026-04-01 +status: published --- ## Added diff --git a/docs/specs/find.md b/docs/specs/find.md new file mode 100644 index 0000000..938f717 --- /dev/null +++ b/docs/specs/find.md @@ -0,0 +1,51 @@ +--- +title: Find in Document +updated: 2026-04-15 +--- + +> Goal: Add in-document find (and replace) using CodeMirror's `@codemirror/search`, separate from the existing cross-document search. + +## Problem + +`Cmd+F` currently toggles focus mode. Cross-document search lives behind `Cmd+Shift+F`. There is no way to find text within the active document — a basic editor expectation. + +## Design + +### Keybinding changes + +| Shortcut | Current | New | +| ------------- | ---------------- | -------------------------------------- | +| `Cmd+F` | Focus mode | Find in document | +| `Cmd+Shift+F` | Cross-doc search | Cross-doc search (unchanged) | +| `Cmd+Shift+H` | — | Find & replace in document | +| `Cmd+E` | — | Focus mode (relocated) | +| `Escape` | — | Close find bar, return focus to editor | + +### CodeMirror integration + +`@codemirror/search` is already a dependency. Wire it up as a CM extension: + +```ts +import { + search, + searchKeymap, + openSearchPanel, + closeSearchPanel, +} from "@codemirror/search"; +``` + +- Add `search()` and `keymap.of(searchKeymap)` to the editor's extension list via a new compartment (`compartments.search`). +- The built-in search panel renders inside the CM DOM — no custom React overlay needed. +- Theme the panel to match oxocarbon light/dark via `EditorView.theme` selectors (`.cm-search`, `.cm-search input`, `.cm-button`). + +### State + +No new Zustand state. CM owns the find panel visibility and query internally. The only app-level change is relocating the focus-mode shortcut. + +### Scope + +- **In scope**: find, find-next/prev, replace, replace-all, case-sensitive toggle, regex toggle. + +#### Future + +- find across multiple open tabs, persistent find-bar state across document switches. diff --git a/docs/specs/outline.md b/docs/specs/outline.md new file mode 100644 index 0000000..b5b2569 --- /dev/null +++ b/docs/specs/outline.md @@ -0,0 +1,64 @@ +--- +title: Document Outline +updated: 2026-04-15 +--- + +> Goal: Surface the Rust-generated `metadata.outline` in the UI so users can see document structure and jump to headings. + +## Problem + +The markdown crate already extracts an outline (`Vec`) during render, and the TS types mirror it (`Heading[]` on `DocumentMetadata`). Neither the frontend nor the preview consume it — the data is discarded. + +## Design + +### Anchor generation (Rust) + +The parser currently pushes `anchor: None` for every heading. To support scroll-to-heading the anchors need to be populated and injected into the rendered HTML. + +- Derive anchors from heading text: lowercase, strip non-alphanumeric, replace spaces with `-` (GitHub-flavored style). +- Deduplicate by appending `-1`, `-2`, etc. for collisions. +- Inject `id=""` on the rendered `

`–`

` tags. +- Populate `Heading.anchor` so the frontend receives usable IDs. + +### State + +Add to the app store: + +```ts +outlinePanelOpen: boolean; // persisted in workspace prefs +``` + +Selector: `useOutline()` — returns `Heading[]` from the active document's most recent render result (already in state via preview). + +### Outline panel (React) + +New component `src/components/OutlinePanel/OutlinePanel.tsx`. + +- Renders a flat list of headings indented by level (padding-left per level). +- Each item shows the heading text, truncated with ellipsis if needed. +- Click dispatches a scroll command to the preview/editor. +- Empty state: muted "No headings" text when outline is empty. +- Panel visibility toggled via toolbar button + keyboard shortcut. + +### Scroll-to-heading + +Two targets depending on active view: + +| View | Mechanism | +|---|---| +| Preview (webview) | `postMessage({ type: 'scroll-to', anchor })` → preview JS calls `document.getElementById(anchor).scrollIntoView()` | +| Editor (CodeMirror) | Find heading line in raw markdown, dispatch CM `scrollIntoView` for that line | + +### Layout integration + +The outline panel sits in the right sidebar area (alongside diagnostics). Only one right-panel is visible at a time — toggling outline closes diagnostics and vice versa. + +### Keyboard shortcut + +`Cmd+Shift+O` — toggle outline panel. + +## Scope exclusions + +- Drag-to-reorder headings. +- Heading renaming from the outline. +- Outline in exported PDF (separate TOC feature). diff --git a/docs/tasks/parking-lot.md b/docs/tasks/parking-lot.md index d1e95b9..13a3d30 100644 --- a/docs/tasks/parking-lot.md +++ b/docs/tasks/parking-lot.md @@ -2,51 +2,45 @@ title: "Parking Lot" description: > A collection of ideas/proposals for new features and quick bug notes. -updated: 2026-04-01 +updated: 2026-04-15 --- -- Consider using [ignore](https://crates.io/crates/ignore) crate for directory walking. - ---- - -1. **CJK Font Support**[^1][^2][^3] ✅ - - Maple Mono and Noto Sans CJK SC are now bundled for PDF rendering. `@react-pdf/renderer` has no fallback chains[^4] — CJK fonts must be registered and selected explicitly. - - Noto SC/JP/KR subsets remain loaded for editor/browser display via `@fontsource`, while the PDF renderer uses bundled OTF/TTF assets from `public/fonts/`. +## Ideas - - [x] Bundle Maple Mono TTF in `public/fonts/` (`maple-mono-{400,700}-{normal,italic}.ttf`) - - [x] Extend `FontName` (`src/pdf/types.ts`), `EditorFontFamily` (`src/types.ts`), `FONT_PATHS` + `BUILTIN_FONT_FAMILY_MAP` (`src/pdf/fonts.ts`) - - [x] Add `@font-face` rules in `src/styles/fonts.css`; import Noto SC/JP/KR subsets for editor - - [x] Add `FontConfig` entries in `src/pdf/fonts.ts` - - [x] Add Maple Mono to `EDITOR_FONT_OPTIONS` (`FontRows.tsx`) - - [x] `hasCjkContent` + `resolvePdfFont` in `src/pdf/fonts.ts`; auto-switch to Maple Mono or Noto Sans CJK SC when the selected rendered font lacks CJK coverage; `PdfCjkWarning` banner in ExportOptions - - [x] Bundle Noto Sans CJK SC OTF assets in `public/fonts/` and register them like the other PDF fonts - -2. **Corrupted DMG / Gatekeeper quarantine**[^5][^6] - - `release.yml` now imports the Apple Developer certificate on macOS runners, resolves a `Developer ID Application` signing identity, and passes the notarization environment to Tauri.[^7][^8] This still depends on the repository secrets being configured correctly and should be verified with a real test release. - - **Subtasks:** - - [ ] Obtain Apple Developer credentials (Developer ID cert `.p12`, Apple ID, app-specific password, Team ID) - - [ ] Add GitHub secrets: `APPLE_CERTIFICATE`, `APPLE_CERTIFICATE_PASSWORD`, `APPLE_ID`, `APPLE_PASSWORD`, `APPLE_TEAM_ID` - - [x] Import the Apple certificate and resolve the signing identity in `release.yml` - - [x] Pass Apple signing and notarization env vars to `tauri-apps/tauri-action` in `release.yml` - - [ ] Verify with `spctl --assess` after a test release -3. **Outline utilization** - - Use Rust-generated `metadata.outline` from `markdown_render` in the UI for document structure navigation/jump-to-heading behavior +- Consider using [ignore](https://crates.io/crates/ignore) crate for directory walking. +- Semantic search? +- Audio support? + +## Planned + +1. **CJK Font Support** + Released in v0.3.0 +2. **Corrupted DMG / Gatekeeper quarantine** + Resolved as of v0.3.0 +3. **Outline utilization** → [spec](../specs/outline.md) + - [ ] **Anchor generation** — populate `Heading.anchor` in `crates/markdown/src/parser.rs`, inject `id` attrs on rendered `

`–`

`, deduplicate collisions + - [ ] **State & selector** — add `outlinePanelOpen` to store, `useOutline()` selector returning `Heading[]` from active doc render result + - [ ] **OutlinePanel component** — `src/components/OutlinePanel/OutlinePanel.tsx`, indented heading list, truncation, empty state + - [ ] **Scroll-to-heading** — preview: `postMessage` → `scrollIntoView`; editor: find heading line → CM `scrollIntoView` + - [ ] **Layout integration** — right sidebar slot (mutual-exclusive with diagnostics), toolbar toggle, `Cmd+Shift+O` shortcut + - [ ] **Tests** — Rust: anchor generation + dedup; Frontend: OutlinePanel render, click→scroll, empty state 4. **Perf** - Incremental render scheduling (debounce, worker thread) - Indexing in background with progress events with UI feedback 5. **Recovery** - Corrupt settings/workspace → app resets safely - Missing location root → UI prompts to relink/remove - -[^1]: [Noto Sans CJK — Google Fonts](https://fonts.google.com/noto/specimen/Noto+Sans+SC) -[^2]: [Maple Mono — GitHub](https://github.com/subframe7536/maple-font) -[^3]: [IBM Plex CJK coverage gap](https://github.com/IBM/plex/issues/148) — IBM Plex does not include CJK glyphs. -[^4]: [`@react-pdf/renderer` font registration — no fallback chain](https://react-pdf.org/fonts#register) -[^5]: [Apple Gatekeeper and notarization](https://developer.apple.com/documentation/security/notarizing-macos-software-before-distribution) -[^6]: [`xattr -c` workaround for quarantined DMGs](https://support.apple.com/en-us/102445) -[^7]: [`tauri-apps/tauri-action` signing docs](https://v2.tauri.app/distribute/sign/macos/) -[^8]: [Tauri environment variables reference](https://v2.tauri.app/reference/environment-variables/) +6. **Wikilinks** → [tasks](./syntax-extensions.md#wikilinks) + - [ ] Enable Comrak `wikilinks_title_after_pipe` on `GfmSafe`/`Extended` profiles + - [ ] `wikilink_resolve` command — fuzzy-match target against indexed docs + - [ ] Extract `metadata.wikilinks` during parse + - [ ] Editor `[[` autocomplete via `@codemirror/autocomplete` + `doc_list` + - [ ] Preview `data-wikilink` click → navigate + - [ ] Broken-link diagnostic + - [ ] Tests (Rust + Frontend) +7. **Find in document** → [spec](../specs/find.md) + - [ ] Relocate focus mode from `Cmd+F` → `Cmd+E` + - [ ] Wire `@codemirror/search` extension + `searchKeymap` into editor (new compartment) + - [ ] Theme `.cm-search` panel for oxocarbon light/dark + - [ ] `Cmd+Shift+H` for find & replace + - [ ] Tests — shortcut rebinding, panel open/close diff --git a/docs/tasks/syntax-extensions.md b/docs/tasks/syntax-extensions.md index 30419fd..7c19182 100644 --- a/docs/tasks/syntax-extensions.md +++ b/docs/tasks/syntax-extensions.md @@ -1,11 +1,11 @@ --- title: Syntax Extensions -updated: 2026-03-20 +updated: 2026-04-15 --- Allow embedding external Markdown files, images, and CSV data into a master document using `/filename` syntax, as well as drag-and-drop of files into the editor. -## Tasks +## Content Blocks 1. **Syntax definition** - `/path/to/file.md` on its own line = transclude that file's rendered content @@ -23,3 +23,17 @@ Allow embedding external Markdown files, images, and CSV data into a master docu - Expand during PDF/HTML export so final output is self-contained 5. **CSV → table rendering** - Parse CSV, emit GFM table Markdown, feed into Comrak pipeline + +## Wikilinks + +`[[Page Name]]` and `[[Page Name|display text]]` syntax for linking between documents within a location. + +### Tasks + +1. **Comrak wikilinks** — enable `wikilinks_title_after_pipe: true` on `GfmSafe` and `Extended` profiles in `crates/markdown/src/lib.rs`. Comrak renders `[[target|text]]` as `text`. Map the `href` to a resolved relative path at render time. +2. **Rust resolution command** — `wikilink_resolve(location_id, from_path, target) -> Option`. Fuzzy-match `target` against indexed document titles/filenames in the location's `documents` table. Return the relative path or `None`. +3. **Parser metadata** — extract wikilinks during `extract_metadata` (new `metadata.wikilinks: Vec` with `target`, `display_text`, `resolved_path`). Powers backlink queries and broken-link diagnostics. +4. **Editor autocomplete** — Lezer `@lezer/markdown` `MarkdownExtension` to parse `[[` as an inline node. Register a `@codemirror/autocomplete` source that triggers on `[[`, fetches document titles via the `doc_list` command, and inserts `[[Title]]`. Close on `]]` or `Escape`. +5. **Preview click handling** — intercept `data-wikilink` anchor clicks in preview webview, resolve to `(location_id, rel_path)`, and navigate via the existing `onSelectDocument` callback. +6. **Broken-link diagnostic** — if `wikilink_resolve` returns `None`, emit a warning diagnostic. Surface in the diagnostics panel. +7. **Tests** — Rust: resolution fuzzy matching, metadata extraction, broken-link diagnostic. Frontend: autocomplete trigger/insert, preview click navigation. diff --git a/tsconfig.json b/tsconfig.json index cdb0ced..d7adca8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -29,7 +29,8 @@ "$constants": ["./constants.ts"], "$dnd": ["./dnd/index.ts"], "$dnd/*": ["./dnd/*"] - } + }, + "ignoreDeprecations": "6.0" }, "include": ["src"], "references": [{ "path": "./tsconfig.node.json" }]