From 8655e91607f972c1dd40622acec17d65792658f9 Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 23 Feb 2026 10:11:35 -0700 Subject: [PATCH 1/8] fix: prevent DexieError2 race condition in browser mode Replace fire-and-forget auto-init in browser-api.ts with a singleton browserReady() promise. App render in main.tsx now awaits API initialization before mounting React, with graceful fallback if init fails. Eliminates IndexedDB race conditions during HMR and cold starts. Co-Authored-By: Claude Opus 4.6 --- src/renderer/src/lib/api.ts | 11 ++++++++++- src/renderer/src/lib/browser-api.ts | 19 ++++++++++++++++--- src/renderer/src/main.tsx | 21 ++++++++++++++++----- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/renderer/src/lib/api.ts b/src/renderer/src/lib/api.ts index 5a11fb8d..d9179992 100644 --- a/src/renderer/src/lib/api.ts +++ b/src/renderer/src/lib/api.ts @@ -8,7 +8,7 @@ import { invoke as tauriInvoke } from '@tauri-apps/api/core' import { listen, UnlistenFn } from '@tauri-apps/api/event' import { isTauri, logPlatformInfo } from './platform' -import { browserApi } from './browser-api' +import { browserApi, browserReady } from './browser-api' import { Note, Tag, TagWithCount, Folder, Project, ProjectType, ProjectSettings } from '../types' // Log platform on first import @@ -390,6 +390,15 @@ const tauriApi = { */ const rawApi = isTauri() ? tauriApi : browserApi +/** + * Wait for the API backend to be fully initialized. + * In Tauri mode: resolves immediately (Rust backend is always ready). + * In Browser mode: waits for IndexedDB init to complete. + */ +export function waitForApi(): Promise { + return isTauri() ? Promise.resolve() : browserReady() +} + // ============================================================================ // Toast Notifications for API Operations // ============================================================================ diff --git a/src/renderer/src/lib/browser-api.ts b/src/renderer/src/lib/browser-api.ts index 3c4e4389..33f4281c 100644 --- a/src/renderer/src/lib/browser-api.ts +++ b/src/renderer/src/lib/browser-api.ts @@ -676,7 +676,20 @@ export const initializeBrowserApi = async (): Promise => { await reindexAllNotes() } -// Auto-initialize on import (browser mode only, skip in tests) -if (typeof import.meta.env?.VITEST === 'undefined') { - initializeBrowserApi().catch(console.error) +// Singleton ready promise — deduplicates init and lets callers await readiness +let readyPromise: Promise | null = null + +/** + * Returns a promise that resolves when the browser DB is fully initialized. + * Safe to call multiple times — only runs init once. + * Skipped in test environment (VITEST). + */ +export function browserReady(): Promise { + if (typeof import.meta.env?.VITEST !== 'undefined') { + return Promise.resolve() + } + if (!readyPromise) { + readyPromise = initializeBrowserApi() + } + return readyPromise } diff --git a/src/renderer/src/main.tsx b/src/renderer/src/main.tsx index 3e127215..1051488f 100644 --- a/src/renderer/src/main.tsx +++ b/src/renderer/src/main.tsx @@ -2,6 +2,7 @@ import React from 'react' import ReactDOM from 'react-dom/client' import App from './App' import { isTauri, logPlatformInfo } from './lib/platform' +import { waitForApi } from './lib/api' import 'katex/dist/katex.min.css' // KaTeX styles for math rendering import './index.css' @@ -55,8 +56,18 @@ class ErrorBoundary extends React.Component< } } -ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( - - - -) +// Ensure API backend is ready before rendering (prevents DexieError2 race in browser mode) +const renderApp = () => { + ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( + + + + ) +} + +waitForApi() + .then(renderApp) + .catch((err) => { + console.error('[Scribe] API init failed, rendering anyway:', err) + renderApp() + }) From 21647734acfd3cf155ef0ebd5f1addb5f2bb2f2d Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 23 Feb 2026 10:41:43 -0700 Subject: [PATCH 2/8] fix: scope settings modal to editor area, not full viewport Settings modal overlay previously used `fixed inset-0` which covered the entire viewport including the left sidebar. Now reads sidebarWidth from Zustand store and offsets the left edge dynamically, keeping the sidebar fully accessible while settings is open. Co-Authored-By: Claude Opus 4.6 --- .STATUS | 16 ++++++++++++++-- src/renderer/src/components/SettingsModal.tsx | 10 ++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/.STATUS b/.STATUS index 78fe4065..f14b4225 100644 --- a/.STATUS +++ b/.STATUS @@ -4,10 +4,10 @@ priority: P1 version: 1.18.0 sprint: 37 started: 2026-01-08 -updated: 2026-02-22 +updated: 2026-02-23 released: 2026-01-24 editor: hybrid-markdown++ -next_focus: Fix 67 test file TypeScript errors (~2.5h) / v1.17.0 Three-Tab Sidebar System +next_focus: Pomodoro Timer (feature/pomodoro) — Increment 1 of 4 tests: 2190 unit_tests: 2190 e2e_tests: 109 @@ -26,6 +26,10 @@ tags: ## Current Focus (Sprint 37) +### In Progress + +1. **Pomodoro Timer** (WIP) — Status bar countdown timer with auto-save, break nudges, Cmd+Shift+P shortcut. Branch: `feature/pomodoro`. Spec: `docs/specs/SPEC-pomodoro-2026-02-23.md`. 4 increments planned. + ### Next Up 1. **Fix 67 test file TypeScript errors** (~2.5h) — Non-blocking but noisy. Documented in `docs/planning/TEST-FILE-TYPESCRIPT-ERRORS-2026-01-24.md` @@ -44,6 +48,13 @@ tags: ## Recently Completed +### v1.18.0 (2026-02-22) — Sidebar Vault Expansion Fix + DexieError2 Fix + +- Fixed vault dots showing wrong project's notes (PR #43) +- Fixed sidebar breadcrumb not updating on vault selection +- Fixed DexieError2 race condition in browser mode (singleton ready promise) +- 3 new tests (2,190 total) + ### v1.16.2 (2026-01-24) — Tech Debt Phase 1 - App.tsx & SettingsModal.tsx modularization (-881 lines) @@ -106,6 +117,7 @@ tags: | Spec | Status | Target | |------|--------|--------| +| Pomodoro Timer | In Progress | v1.19.0 | | Three-Tab Sidebar | Design Approved | v1.17.0 | | Quarto Enhancements | Partially Implemented | v1.15+ | | LaTeX Editor | Proposal | v2.0 | diff --git a/src/renderer/src/components/SettingsModal.tsx b/src/renderer/src/components/SettingsModal.tsx index 21e49533..a739f9db 100644 --- a/src/renderer/src/components/SettingsModal.tsx +++ b/src/renderer/src/components/SettingsModal.tsx @@ -56,6 +56,7 @@ import { Base16Scheme } from '../lib/themes' import { api } from '../lib/api' +import { useAppViewStore } from '../store/useAppViewStore' import { GeneralSettingsTab } from './Settings/GeneralSettingsTab' import { EditorSettingsTab } from './Settings/EditorSettingsTab' @@ -92,6 +93,7 @@ export function SettingsModal({ themeShortcuts, onThemeShortcutsChange, }: SettingsModalProps) { + const sidebarWidth = useAppViewStore((s) => s.sidebarWidth) const [activeTab, setActiveTab] = useState('general') const [showCustomCreator, setShowCustomCreator] = useState(false) const [customThemeName, setCustomThemeName] = useState('') @@ -370,7 +372,7 @@ export function SettingsModal({ } return ( -
+
{/* Backdrop */}
+
{ @@ -1488,7 +1490,7 @@ export function SettingsModal({ {/* Export Modal */} {showExportModal && ( -
+
{ @@ -1559,7 +1561,7 @@ export function SettingsModal({ {/* Theme Preview Bar */} {previewTheme && previewTheme !== currentTheme && ( -
+
Previewing: {themes[previewTheme]?.name} From 340af794eae4005aecbf59e911110b32c20e587e Mon Sep 17 00:00:00 2001 From: Davood Tofighi Date: Mon, 23 Feb 2026 11:53:15 -0700 Subject: [PATCH 3/8] feat: pomodoro focus timer in status bar (#45) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: add pomodoro timer spec and orchestration plan Minimal pomodoro timer for Scribe's editor status bar: - Countdown timer between WritingProgress and Quick Chat - Auto-save note on pomodoro completion - Gentle break nudges (toast, no modals) - Today's count only, resets at midnight - Zustand store for timer state, survives note switches - Settings in General tab, Cmd+Shift+P shortcut Co-Authored-By: Claude Opus 4.6 * feat: add pomodoro Zustand store and preferences - Add 5 new preference fields (pomodoroEnabled, work/short/long durations, interval) - Create usePomodoroStore with idle/work/break state machine - Midnight reset, preference sync, onComplete callback pattern - 35 new store tests covering all transitions and edge cases Co-Authored-By: Claude Opus 4.6 * feat: add PomodoroTimer component to editor status bar - PomodoroTimer component with click-to-toggle, right-click-to-reset - Emoji icons (tomato for work, coffee for break), count badge (2/4) - Integrated between WritingProgress and Quick Chat in status bar - Auto-save via api.updateNote() on pomodoro completion - Break nudge toasts (success/info) on work and break completion - aria-live="polite", descriptive labels, prefers-reduced-motion support - 27 new component tests Co-Authored-By: Claude Opus 4.6 * feat: add pomodoro settings and Cmd+Shift+P shortcut - Focus Timer section in Settings > General with enable toggle and configurable durations (work, short/long break, interval) - Cmd+Shift+P toggles pomodoro start/pause - New Project reassigned to Cmd+Shift+N Co-Authored-By: Claude Opus 4.6 * docs: update changelog for pomodoro feature Co-Authored-By: Claude Opus 4.6 * fix: auto-pin new projects to sidebar and remove pomodoro shortcut New projects now appear in the sidebar immediately by auto-pinning on creation. Removed Cmd+Shift+P pomodoro shortcut to avoid conflicting with the native menu's "New Project" accelerator. Co-Authored-By: Claude Opus 4.6 * docs: add comprehensive Scribe documentation Co-Authored-By: Claude Opus 4.6 * chore: remove stale ⌘⇧P references from pomodoro UI and docs The pomodoro shortcut was removed — update tooltips, settings description, changelog, and documentation to reflect click-only interaction. Co-Authored-By: Claude Opus 4.6 * fix: address PR #45 code review issues - Fix redundant loadPreferences() calls in GeneralSettingsTab — cache prefs in useState, read once per render instead of 4+ localStorage hits - Add syncPreferences() reactivity — pomodoro store syncs immediately when settings change, not just on next timer start - Make tick() callback symmetric — onBreakComplete fires from store's tick() alongside onComplete, replacing fragile useEffect detection - Update ⌘⇧N shortcut labels in IconBar, QuickActions, and tests to match KeyboardShortcutHandler (was showing old ⌘⇧P) - Remove ORCHESTRATE-pomodoro.md working artifact from PR - Add 3 new store tests for onBreakComplete in tick() - Add clarifying comment for count display rounding math All 2,255 tests pass (73 files, 0 failures). Co-Authored-By: Claude Opus 4.6 * docs: fix stale ⌘⇧P references and store interface in documentation - Replace 5 stale ⌘⇧P → ⌘⇧N in SCRIBE-DOCUMENTATION.md (shortcut changed in commit 6) - Fix store interface: phase→status, workSeconds→workDuration, shortBreakSeconds→shortBreakDuration, longBreakSeconds→longBreakDuration - Fix tick signature: tick(onComplete?) → tick(onComplete?, onBreakComplete?) - Add missing lastResetDate field to documented interface - Mark removed keyboard shortcut in SPEC acceptance criteria Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Test User Co-authored-by: Claude Opus 4.6 --- CHANGELOG.md | 16 + docs/SCRIBE-DOCUMENTATION.md | 1015 +++++++++++++++++ docs/specs/SPEC-pomodoro-2026-02-23.md | 213 ++++ package-lock.json | 4 +- src/renderer/src/App.tsx | 32 +- .../src/__tests__/IconBar.component.test.tsx | 4 +- .../src/__tests__/PomodoroTimer.test.tsx | 276 +++++ src/renderer/src/__tests__/testUtils.ts | 5 + .../src/__tests__/usePomodoroStore.test.ts | 383 +++++++ .../src/components/EditorOrchestrator.tsx | 16 +- src/renderer/src/components/HybridEditor.tsx | 14 +- .../components/KeyboardShortcutHandler.tsx | 4 +- src/renderer/src/components/PomodoroTimer.tsx | 134 +++ src/renderer/src/components/QuickActions.tsx | 2 +- .../Settings/GeneralSettingsTab.tsx | 145 ++- .../src/components/sidebar/IconBar.tsx | 2 +- src/renderer/src/lib/preferences.ts | 14 + src/renderer/src/store/usePomodoroStore.ts | 147 +++ 18 files changed, 2405 insertions(+), 21 deletions(-) create mode 100644 docs/SCRIBE-DOCUMENTATION.md create mode 100644 docs/specs/SPEC-pomodoro-2026-02-23.md create mode 100644 src/renderer/src/__tests__/PomodoroTimer.test.tsx create mode 100644 src/renderer/src/__tests__/usePomodoroStore.test.ts create mode 100644 src/renderer/src/components/PomodoroTimer.tsx create mode 100644 src/renderer/src/store/usePomodoroStore.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index d1f19fa7..629e8637 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- +## [Unreleased] — Pomodoro Focus Timer + +### Added + +- **Pomodoro timer in status bar** — Minimal focus timer between WritingProgress and Quick Chat. Click to start/pause, right-click to reset. Shows countdown (MM:SS) and today's session count (e.g., 2/4). +- **Zustand store** (`usePomodoroStore`) — Timer state survives note switches. Tracks status (idle/work/short-break/long-break), seconds remaining, completed today count with midnight reset. +- **Auto-save on pomodoro completion** — Calls `api.updateNote()` when a work session finishes, ensuring a save point every 25 minutes. +- **Gentle break toasts** — "Time for a break! ☕ Note saved." on work completion, "Break's over — ready to write?" on break completion. Auto-dismiss, no modals. +- **Focus Timer settings** — Settings > General > "Focus Timer" section with enable toggle and configurable durations (work, short break, long break, interval). +- **New projects auto-pinned to sidebar** — Creating a project now immediately shows it in the icon bar. +- **5 new preferences** — `pomodoroEnabled`, `pomodoroWorkMinutes`, `pomodoroShortBreakMinutes`, `pomodoroLongBreakMinutes`, `pomodoroLongBreakInterval` +- **Accessibility** — `aria-live="polite"` for screen readers, descriptive `aria-label` per state, `prefers-reduced-motion` support (no blink animation) +- **62 new tests** — 35 store tests + 27 component tests covering all states, transitions, callbacks, and edge cases + +--- + ## [v1.18.0] - 2026-02-22 — Sidebar Vault Fix ### Fixed diff --git a/docs/SCRIBE-DOCUMENTATION.md b/docs/SCRIBE-DOCUMENTATION.md new file mode 100644 index 00000000..fc44da44 --- /dev/null +++ b/docs/SCRIBE-DOCUMENTATION.md @@ -0,0 +1,1015 @@ +# Scribe — Comprehensive Technical Documentation + +**Version:** 1.19 (Pomodoro release) +**Last Updated:** 2026-02-23 +**Branch:** feature/pomodoro + +--- + +## Table of Contents + +1. [Overview](#1-overview) +2. [Getting Started](#2-getting-started) +3. [Features Guide](#3-features-guide) +4. [Keyboard Shortcuts Reference](#4-keyboard-shortcuts-reference) +5. [Editor Modes](#5-editor-modes) +6. [Project Management](#6-project-management) +7. [Sidebar System](#7-sidebar-system) +8. [Pomodoro Timer](#8-pomodoro-timer) +9. [AI Integration](#9-ai-integration) +10. [Themes and Appearance](#10-themes-and-appearance) +11. [Settings Reference](#11-settings-reference) +12. [Architecture Overview](#12-architecture-overview) +13. [API Reference](#13-api-reference) +14. [Development Guide](#14-development-guide) + +--- + +## 1. Overview + +Scribe is a desktop knowledge management and writing application built for researchers, academics, and focused writers. It combines a markdown note editor with project organization, a knowledge graph, AI assistants, academic citation management, and — as of v1.19 — a built-in Pomodoro timer. + +### What Makes Scribe Different + +Scribe is intentionally opinionated. Rather than being a general-purpose note tool, it is designed around the workflows of people doing sustained intellectual work: literature reviews, R package development, course preparation, long-form writing. It ships with five project archetypes (Research, Teaching, R Package, R Dev, Generic) that encode those workflows directly into the UI. + +The design philosophy is ADHD-friendly: information is surfaced when needed, not buried in menus. The icon-centric sidebar (introduced in v1.16) keeps the primary workspace uncluttered while making every major function reachable within one click or one keyboard shortcut. + +### Technology Stack + +| Layer | Technology | +|-------|-----------| +| Desktop shell | Tauri 2.10 (Rust) | +| UI framework | React 18 + TypeScript 5.5 | +| Build toolchain | Vite 5 | +| State management | Zustand 4.5 | +| Source editor | CodeMirror | +| Terminal emulator | xterm.js | +| Knowledge graph | D3.js | +| Math rendering | KaTeX | +| Database (desktop) | SQLite via Tauri | +| Database (browser) | IndexedDB | +| Styling | Tailwind CSS | + +Tauri provides the native shell: window management, native menus, file system access, and IPC between the Rust backend and the React frontend. The entire UI is React; Tauri handles only the things a browser cannot. + +### At a Glance — Core Capabilities + +- **Notes** — Create, edit, tag, link, and search markdown notes +- **Wiki Links** — `[[note name]]` syntax with automatic backlink tracking +- **Projects** — Five typed project archetypes with scoped note collections +- **Knowledge Graph** — Interactive D3 visualization of note connections +- **Three Editor Modes** — Source (raw markdown), Live Preview (split), Reading (rendered) +- **Export** — PDF, DOCX, LaTeX, HTML via Pandoc +- **Focus Mode** — Distraction-free full-screen writing +- **Quick Capture** — Instant note from anywhere in the app +- **Command Palette** — Keyboard-driven command access +- **Tabs** — Pin, reorder, close, reopen tabs +- **10 Built-in Themes** — Plus custom CSS and time-based auto-switching +- **AI Chat** — Claude and Gemini integration +- **Terminal** — Embedded xterm.js shell +- **Academic Citations** — BibTeX bibliography management +- **Writing Streaks** — Daily streak tracking with milestones +- **Word Count Goals** — Per-session targets +- **Pomodoro Timer** (v1.19) — Status bar timer with configurable work/break cycles + +--- + +## 2. Getting Started + +### Prerequisites + +| Requirement | Version | +|-------------|---------| +| macOS | 13 Ventura or later (primary platform) | +| Node.js | 18 or later | +| Rust toolchain | Latest stable (for development builds) | +| Pandoc | Optional — required for PDF/DOCX/LaTeX export | + +### Installation + +Download the latest `.dmg` from the GitHub releases page and drag Scribe into your Applications folder. On first launch, macOS may prompt you to allow the app from an unidentified developer — open System Settings > Privacy & Security and click "Open Anyway." + +For development builds, see [Section 14 — Development Guide](#14-development-guide). + +### First Launch + +On first launch Scribe will: + +1. Create a default **Research** project automatically. +2. Initialize the local SQLite database (on Tauri) or IndexedDB (in browser dev mode). +3. Load the default theme (Sepia). +4. Show the empty editor with the sidebar in icon-only mode. + +**Recommended first steps:** + +1. Press `⌘N` to create your first note. +2. Press `⌘,` to open Settings and review preferences. +3. Press `⌘⇧G` to see the (empty) knowledge graph — it fills in as you create linked notes. +4. If you write in R or have research projects, press `⌘⇧N` to create a typed project. + +--- + +## 3. Features Guide + +### Notes + +Notes are the atomic unit of Scribe. Every note is a markdown document stored in the database with associated metadata (tags, project membership, timestamps, word count). + +**Creating notes:** +- `⌘N` — new blank note +- `⌘D` — today's daily note (creates one if it does not exist) +- `⌘⇧C` — Quick Capture modal (title + body, saves immediately) + +**Organizing notes:** + +Tags are extracted automatically from `#hashtag` syntax in the note body and are also manageable from the right sidebar Tags panel. Notes can belong to one project at a time; you change a note's project from the Properties panel in the right sidebar. + +**Wiki Links:** + +Type `[[` anywhere in a note to start a wiki link. Scribe auto-extracts all `[[note name]]` references on save and updates the backlink index. Backlinks for the current note appear in the right sidebar Backlinks panel. + +**Search:** + +Press `⌘F` to open the search panel. Search uses SQLite full-text search (FTS) on Tauri or a JavaScript FTS implementation in browser mode. Results are ranked by relevance. + +### Quick Capture + +`⌘⇧C` opens a lightweight modal overlay usable from any screen in the app. Type a title, add body text, and press Enter to save. The note is created in the current project and appears in the sidebar immediately. + +This is intentionally minimal — the goal is to capture an idea before it disappears, not to compose a finished note. + +### Command Palette + +The Command Palette surfaces frequently-used actions by keyboard. It is filtered by Fuse.js fuzzy search, so you do not need to type exact command names. It includes up to 5 default quick actions plus custom user-defined ones (max 10 total). + +### Tabs + +Scribe uses a tab bar above the editor. Tabs represent open notes. Behavior: + +- **Pin a tab** — right-click the tab +- **Reorder tabs** — drag and drop +- **Close tab** — `⌘W` or click the X +- **Reopen closed tab** — `⌘⇧T` (restores from a history of up to 10 closed tabs) +- **Switch to tab N** — `⌘1` through `⌘9` + +Tab state (open tabs, order, pinned state) persists to `localStorage` via `useAppViewStore`. + +### Focus Mode + +`⌘⇧F` (or View menu) enters Focus Mode: hides both sidebars, maximizes the editor, and removes all UI chrome. Press `Escape` or `⌘⇧F` again to exit. Focus Mode state persists across launches. + +### Knowledge Graph + +`⌘⇧G` opens the Knowledge Graph — an interactive D3 force-directed graph where nodes are notes and edges are wiki links. You can: + +- Click a node to open that note +- Drag nodes to rearrange the layout +- Zoom with scroll/pinch +- Filter by project or tag + +The graph updates in real time as you create or delete links. + +### Writing Streaks and Word Count Goals + +Scribe tracks a daily writing streak (days with at least some editing activity). Opt-in via Settings > Writing > Streak Display. Milestones can trigger celebration animations (also opt-in). + +Word count goals are set per-session (default 500 words). Progress appears in the status bar. The goal resets at midnight. + +### Export + +`⌘⇧E` opens the Export dialog. Scribe uses Pandoc for document conversion. Available formats: + +| Format | Requires Pandoc | +|--------|----------------| +| HTML | No | +| PDF | Yes | +| DOCX | Yes | +| LaTeX | Yes | + +The app checks for Pandoc availability at startup via `api.isPandocAvailable()`. If Pandoc is not installed, PDF/DOCX/LaTeX options are disabled with a link to install it. + +### Academic Citations + +Scribe supports BibTeX bibliography files. Set your `.bib` file path in Settings > Academic. Once configured: + +- `api.getCitations()` loads all entries +- `api.searchCitations(query)` filters by author, title, year, or key +- Citations can be inserted into notes in standard Pandoc citation format (`[@key]`) + +--- + +## 4. Keyboard Shortcuts Reference + +### Global Shortcuts + +| Shortcut | Action | +|----------|--------| +| `⌘N` | New Note | +| `⌘D` | Daily Note | +| `⌘⇧N` | New Project | +| `⌘⇧C` | Quick Capture | +| `⌘F` | Search Notes | +| `⌘⇧E` | Export | +| `⌘⇧G` | Knowledge Graph | +| `⌘⇧F` | Focus Mode toggle | +| `⌘,` | Settings | +| `⌘?` or `⌘/` | Keyboard Shortcuts help dialog | + +### Sidebar + +| Shortcut | Action | +|----------|--------| +| `⌘B` | Left Sidebar toggle | +| `⌘⇧B` | Right Sidebar toggle | +| `⌘⇧[` | Collapse All (left sidebar) | +| `⌘⇧]` | Right Sidebar toggle (alias) | +| `⌘[` | Right sidebar — previous tab | +| `⌘]` | Right sidebar — next tab | + +### Tabs + +| Shortcut | Action | +|----------|--------| +| `⌘1`–`⌘9` | Switch to tab N | +| `⌘W` | Close current tab | +| `⌘⇧T` | Reopen last closed tab | + +### Editor View Modes + +| Shortcut | Action | +|----------|--------| +| `⌘1` (View menu) | Source mode | +| `⌘2` (View menu) | Live Preview mode | +| `⌘3` (View menu) | Reading mode | + +### Tools + +| Shortcut | Action | +|----------|--------| +| `⌘⌥T` | Terminal tab toggle | + +### Smart Icons (Project Quick-Switch) + +| Shortcut | Action | +|----------|--------| +| `⌘⇧1` | Research project icon | +| `⌘⇧2` | Teaching project icon | +| `⌘⇧3` | R Packages project icon | +| `⌘⇧4` | Dev Tools project icon | + +### Theme Shortcuts + +| Shortcut | Action | +|----------|--------| +| `⌘⌥0`–`⌘⌥9` | Switch to theme slot 0–9 | + +> Note: `⌘⇧N` opens New Project via the native Tauri menu. The pomodoro timer has no keyboard shortcut — use click to start/pause. + +--- + +## 5. Editor Modes + +The editor has three distinct rendering modes, switchable from the View menu or via the mode selector in the editor toolbar. + +### Source Mode + +Raw markdown editing via CodeMirror. This is the default for users who prefer direct control over markup. CodeMirror provides: + +- Syntax highlighting for markdown, fenced code blocks (with language tags), and inline code +- Line numbers +- vim/emacs keybindings (if configured) +- KaTeX rendering for `$inline$` and `$$block$$` math expressions (rendered as overlays) + +**Best for:** Power users, those who need precise control over formatting, writing documents that will be exported. + +### Live Preview Mode + +A split view: source on the left, rendered HTML on the right. Changes in the source panel update the preview in real time. This mode uses `ReactMarkdown` for rendering with custom renderers for wiki links, tags, and math. + +**Best for:** Users who want to see the output while writing, without leaving the editing surface. + +### Reading Mode + +Full rendered view with no editing surface. The note is displayed as final HTML with all markdown, math, and wiki links rendered. Click a wiki link to navigate to that note. + +**Best for:** Reviewing a finished note, reading reference material, following wiki link trails. + +### Switching Modes + +Mode is stored in preferences (`editorMode` key) and persists across launches. The current mode appears in the View menu with a checkmark. Modes apply globally — switching mode changes the view for all open tabs. + +--- + +## 6. Project Management + +### Project Types + +Scribe ships with five project archetypes. Each type customizes the sidebar layout, default tags, and available smart icons. + +| Type | Icon | Description | +|------|------|-------------| +| Research | Microscope | Literature reviews, research notes, paper drafts | +| Teaching | Graduation cap | Course materials, lecture notes, syllabi | +| R Package | Package box | R package documentation, vignettes, NEWS | +| R Dev | Code brackets | R scripts, analysis notebooks, data notes | +| Generic | Folder | Any other project structure | + +The type is cosmetic in terms of data storage — all projects use the same underlying schema — but it determines which smart icon slot is used and what the default sidebar grouping looks like. + +### Creating a Project + +Press `⌘⇧N` to open the Create Project modal. You choose: + +1. Project name +2. Project type +3. Optional description + +On save, `useProjectStore.createProject()` calls `api.createProject()` and the new project is automatically pinned to the sidebar. + +### Pinned Vaults + +The sidebar supports up to **5 pinned vaults** (projects). They appear as icons in the icon bar. If you have more than 5 projects, older pins are evicted (LRU). You can manually reorder or unpin from the sidebar context menu. + +### Switching Projects + +Click a project icon in the sidebar to make it the active project. The note list in the expanded sidebar panel updates to show only notes belonging to that project. Smart icon shortcuts (`⌘⇧1`–`⌘⇧4`) jump directly to the four typed project slots. + +### Default Project + +On first launch, Scribe creates a default **Research** project automatically via `useProjectStore`'s initialization logic. This ensures new notes always have a project to belong to. + +--- + +## 7. Sidebar System + +The sidebar was redesigned in v1.16 to use an icon-centric architecture that keeps the writing surface as wide as possible by default. + +### Structure + +``` +App +└── MissionSidebar + ├── IconBar (48px wide, always visible) + │ ├── Pinned vault icons (up to 5) + │ ├── Smart icons (4 typed project shortcuts) + │ └── System icons (Search, Graph, Terminal, Settings) + └── ExpandedIconPanel (collapsible, 2 styles) + ├── Compact view (dense list) + └── Card view (richer preview) +``` + +### Icon Bar + +The icon bar is always visible at 48px wide. It contains: + +- **Pinned vaults** — up to 5 project icons at the top. Click to expand that project's note list. +- **Smart icons** — 4 slots for the four typed project archetypes (Research, Teaching, R Package, R Dev). These are fixed positions; the icon changes to reflect whichever project of that type is active. +- **System icons** — at the bottom: Search, Knowledge Graph, Terminal, Settings. + +### Expanded Panel + +Clicking an icon in the icon bar expands the panel to the right of the icon bar. The expanded panel shows the note list for that project (or search results, graph preview, etc. depending on which icon was clicked). + +Two display styles are available from Settings: + +- **Compact** — high-density list, icon + title + date +- **Card** — larger cards with a preview excerpt + +### Right Sidebar + +The right sidebar (`⌘⇧B`) contains contextual panels for the active note: + +| Panel | Contents | +|-------|---------| +| Properties | Note title, project, creation/modified dates | +| Backlinks | Notes that link to the current note | +| Tags | Tags on the current note (add/remove) | +| Stats | Word count, reading time, character count | +| Claude | AI chat panel (context-aware) | +| Terminal | Embedded xterm.js terminal | + +Navigate between right sidebar panels with `⌘[` and `⌘]`. + +### Sidebar State Persistence + +All sidebar state persists via `useAppViewStore` to `localStorage`: + +- `expandedIcon` — which icon is currently expanded +- `sidebarWidth` — the user-dragged panel width +- `pinnedVaults` — ordered list of up to 5 pinned project IDs +- `smartIcons` — the 4 typed project slot assignments + +--- + +## 8. Pomodoro Timer + +The Pomodoro Timer is new in v1.19. It is a state machine timer embedded in the status bar that implements the classic Pomodoro Technique: 25-minute work sessions separated by short (5 min) and long (15 min) breaks. + +### Enabling the Timer + +Open Settings (`⌘,`) and navigate to the Pomodoro section. Toggle **Enable Pomodoro Timer** on. The timer icon appears in the status bar at the bottom of the editor. + +### Status Bar Display + +When enabled, the timer displays in the editor status bar: + +- **Idle** — shows a tomato icon and "Pomodoro" label +- **Work** — shows remaining time in `MM:SS` format with a red indicator +- **Short Break** — shows remaining time with a green indicator +- **Long Break** — shows remaining time with a blue indicator +- **Paused** — shows the remaining time with a pause icon overlay + +### Interactions + +| Interaction | Action | +|-------------|--------| +| Click the timer | Start (if idle) / Pause (if running) / Resume (if paused) | +| Right-click the timer | Reset to idle state | + +### State Machine + +The Pomodoro timer follows a strict state machine: + +``` +idle + → start() + → work (25 min default) + → tick() each second + → onComplete fires at 0 + → if completedToday % longBreakInterval == 0 + → long-break (15 min default) + else + → short-break (5 min default) + → break tick() to 0 + → reset to idle +``` + +The `completedToday` counter increments each time a work session completes. It resets to zero when the app restarts (it is not persisted — it is a session counter). + +The `longBreakInterval` preference (default 4) controls how frequently long breaks occur. After every 4th completed work session, the next break is a long break instead of a short one. + +### Auto-Save on Work Complete + +When a work session completes (`onComplete` fires), the timer notifies the active note to save. This ensures writing progress is committed before the break begins. The auto-save uses the same code path as the manual save triggered by `useNotesStore.updateNote()`. + +### Configuration + +All Pomodoro preferences are stored in `localStorage` under the `scribe-preferences` key: + +| Preference | Default | Description | +|------------|---------|-------------| +| `pomodoroEnabled` | `true` | Show/hide the timer in the status bar | +| `pomodoroWorkMinutes` | `25` | Duration of a work session in minutes | +| `pomodoroShortBreakMinutes` | `5` | Duration of a short break in minutes | +| `pomodoroLongBreakMinutes` | `15` | Duration of a long break in minutes | +| `pomodoroLongBreakInterval` | `4` | Work sessions between long breaks | + +Changes to these preferences take effect on the next timer start. If the timer is currently running when preferences change, `syncPreferences()` is called to update the store without resetting the running session. + +### Zustand Store: usePomodoroStore + +The timer state lives in `src/renderer/src/store/usePomodoroStore.ts`. + +**State shape:** + +```typescript +interface PomodoroState { + status: 'idle' | 'work' | 'short-break' | 'long-break' + secondsRemaining: number + isPaused: boolean + completedToday: number + lastResetDate: string // ISO date (YYYY-MM-DD) + workDuration: number // seconds, derived from pomodoroWorkMinutes pref + shortBreakDuration: number // seconds, derived from pomodoroShortBreakMinutes pref + longBreakDuration: number // seconds, derived from pomodoroLongBreakMinutes pref + longBreakInterval: number // every N pomodoros +} +``` + +**Actions:** + +| Action | Description | +|--------|-------------| +| `start()` | Transition from idle to work phase | +| `pause()` | Toggle isPaused on a running timer | +| `reset()` | Return to idle, clear secondsRemaining | +| `tick(onComplete?, onBreakComplete?)` | Decrement secondsRemaining by 1; fire onComplete on work→break, onBreakComplete on break→work | +| `syncPreferences()` | Re-read preferences and update duration values without resetting phase | + +**Rendering:** + +`PomodoroTimer` is a React component rendered in the editor status bar. It subscribes to `usePomodoroStore` and sets up a `setInterval` (1000ms) that calls `tick()` when the timer is running and not paused. The interval is cleared on component unmount and whenever `isPaused` becomes true. + +### Settings UI + +The Pomodoro settings appear as a dedicated section in `Settings > General`. They use the same `SettingsSection` wrapper and toggle pill style as all other settings sections: + +- Enable/disable toggle +- Number inputs for each duration (validated: min 1 min, max 60 min for work/break; max 10 for interval) + +--- + +## 9. AI Integration + +### Claude Chat + +Claude is integrated as a chat panel in the right sidebar. The panel is context-aware: it sends the current note's content along with your message, so Claude can answer questions about what you are writing. + +Under the hood, `api.runClaude(prompt, context)` sends an IPC message to the Tauri backend, which calls the Anthropic API and streams the response back to the UI. + +**Use cases:** +- "Summarize this note" +- "What sources should I cite for this claim?" +- "Help me restructure this argument" +- "Translate this paragraph to formal academic English" + +### Gemini Chat + +The same right sidebar panel can switch to Gemini via `api.runGemini(prompt, context)`. The UX is identical to Claude. Gemini is useful as a second opinion or when you prefer its strengths for certain tasks (e.g., code generation). + +### Quick Actions + +Quick Actions are pre-configured prompts that appear in the command palette. The Settings store manages up to 10 quick actions (5 defaults + 5 user-defined). Each action has: + +- A label (shown in command palette) +- A prompt template (can include `{{note}}` to inject current note content) +- A target AI (Claude or Gemini) + +Default quick actions include things like "Summarize note," "Extract key ideas," and "Generate outline." + +--- + +## 10. Themes and Appearance + +### Built-in Themes + +Scribe ships with 10 built-in themes. Themes are implemented as sets of CSS custom properties applied to `:root`. + +| Slot | Theme Name | +|------|-----------| +| 0 | Sepia (default) | +| 1 | Dark | +| 2 | Light | +| 3 | Nord | +| 4 | Solarized | +| 5 | Dracula | +| 6 | Gruvbox | +| 7 | Catppuccin | +| 8 | Tokyo Night | +| 9 | Monokai | + +Switch themes via `⌘⌥0` through `⌘⌥9` or from the Appearance section in Settings. + +### Custom Themes + +In Settings > Appearance > Custom CSS, you can write CSS that is injected after the base theme. Enable custom CSS with the toggle in that section. The preference keys are `customCSS` (string) and `customCSSEnabled` (boolean). + +Custom CSS can override any CSS variable or add new rules. Example to change the note background: + +```css +:root { + --note-background: #1a1a2e; + --note-text: #e0e0e0; +} +``` + +### Auto-Theme (Time-Based Switching) + +Scribe can automatically switch themes based on time of day. Configure two themes (day and night) and the transition times in Settings > Appearance > Auto Theme. The app checks the current time every 60 seconds and switches if needed. + +This uses a `setInterval` in the theme loader — not a system event — so the switch happens within one minute of the configured time. + +### Appearance Preferences + +| Preference | Description | +|------------|-------------| +| `tabBarStyle` | Tab bar style: default, minimal, or pill | +| `borderStyle` | Border rendering: sharp, rounded, or none | +| `activeTabStyle` | How the active tab is highlighted | +| `iconGlowEffect` | Enable glow effect on sidebar icons | +| `iconGlowIntensity` | Glow intensity (0–100) | + +--- + +## 11. Settings Reference + +All preferences are stored in `localStorage` under the key `scribe-preferences`. On load, stored preferences are merged with defaults: + +```typescript +const prefs = { ...DEFAULTS, ...JSON.parse(localStorage.getItem('scribe-preferences') ?? '{}') } +``` + +This merge-on-load pattern ensures new preference keys added in future versions always have a default value even for existing users. + +### Writing Preferences + +| Key | Default | Description | +|-----|---------|-------------| +| `defaultWordGoal` | `500` | Daily word count goal | +| `focusModeEnabled` | `false` | Whether Focus Mode is active | +| `streakDisplayOptIn` | `false` | Show writing streak in status bar | +| `celebrateMilestones` | `false` | Animate milestone celebrations | + +### Editor Preferences + +| Key | Default | Description | +|-----|---------|-------------| +| `editorMode` | `'source'` | Default editor mode: `source`, `live-preview`, or `reading` | +| `customCSS` | `''` | Custom CSS string | +| `customCSSEnabled` | `false` | Whether custom CSS is injected | + +### UI Style Preferences + +| Key | Default | Description | +|-----|---------|-------------| +| `tabBarStyle` | `'default'` | Tab bar visual style | +| `borderStyle` | `'rounded'` | Border style for panels | +| `activeTabStyle` | `'underline'` | Active tab highlight style | + +### Sidebar Preferences + +| Key | Default | Description | +|-----|---------|-------------| +| `sidebarTabSize` | `'compact'` | Note list density: `compact` or `card` | +| `sidebarTabOrder` | `[]` | Ordered array of visible sidebar tab IDs | +| `sidebarHiddenTabs` | `[]` | Array of tab IDs to hide | + +### Icon Bar Preferences + +| Key | Default | Description | +|-----|---------|-------------| +| `iconGlowEffect` | `false` | Glow effect on sidebar icons | +| `iconGlowIntensity` | `50` | Glow intensity 0–100 | + +### Pomodoro Preferences + +| Key | Default | Description | +|-----|---------|-------------| +| `pomodoroEnabled` | `true` | Show Pomodoro timer in status bar | +| `pomodoroWorkMinutes` | `25` | Work session duration (minutes) | +| `pomodoroShortBreakMinutes` | `5` | Short break duration (minutes) | +| `pomodoroLongBreakMinutes` | `15` | Long break duration (minutes) | +| `pomodoroLongBreakInterval` | `4` | Work sessions per long break cycle | + +--- + +## 12. Architecture Overview + +### Component Tree + +``` +App.tsx +├── KeyboardShortcutHandler (headless — no rendered output) +├── MissionSidebar +│ ├── IconBar (48px, always visible) +│ └── ExpandedIconPanel (collapsible) +│ ├── Note list (compact or card view) +│ └── Search results / Graph preview / etc. +├── EditorTabs (tab bar above editor) +├── EditorOrchestrator +│ └── HybridEditor +│ ├── CodeMirrorEditor (source mode) +│ └── ReactMarkdown renderer (reading / live preview) +├── Right Sidebar +│ ├── PropertiesPanel +│ ├── BacklinksPanel +│ ├── TagsPanel +│ ├── StatsPanel +│ ├── ClaudePanel +│ └── TerminalPanel (xterm.js) +├── PomodoroTimer (status bar) +└── Dialogs (modal overlays) + ├── SettingsDialog + ├── CreateProjectModal + ├── ExportDialog + ├── GraphDialog (D3) + └── QuickCaptureModal +``` + +### Zustand Stores + +Scribe uses five Zustand stores. All stores live in `src/renderer/src/store/` (note: singular `store`, not `stores`). + +| Store | File | Responsibility | +|-------|------|----------------| +| `usePomodoroStore` | `usePomodoroStore.ts` | Pomodoro timer state machine | +| `useAppViewStore` | `useAppViewStore.ts` | Sidebar state, tabs, pinned vaults | +| `useNotesStore` | `useNotesStore.ts` | Notes list, selected note, CRUD | +| `useProjectStore` | `useProjectStore.ts` | Projects list, current project | +| `useSettingsStore` | `useSettingsStore.ts` | App settings, quick actions, categories | + +Stores communicate with the database exclusively through the API layer (`api.ts`). Stores do not call Tauri IPC or IndexedDB directly. + +### Data Flow + +The canonical data flow for note editing: + +``` +User types in editor + → HybridEditor.onChange(newContent) + → useNotesStore.updateNote(id, { content: newContent }) + → api.updateNote(id, data) + → Tauri IPC: invoke('update_note', data) [or IndexedDB.put() in browser] + → SQLite write (Rust handler) + → Promise resolves + → Store state updated + → React re-render (subscribing components) +``` + +The flow is deliberately linear and synchronous from the UI's perspective. There is no optimistic update / rollback logic — the store updates only after the API call resolves. + +### API Layer (Factory Pattern) + +`src/renderer/src/api.ts` exports a single `api` object constructed by a factory function that detects the runtime environment: + +```typescript +const api = createApi() // returns TauriApi or BrowserApi +``` + +`TauriApi` implements each method via `invoke(commandName, args)` (Tauri IPC to Rust). +`BrowserApi` implements each method via IndexedDB operations (for browser-based development without Tauri). + +Both implementations satisfy the same TypeScript interface, so all application code is runtime-agnostic. + +### Keyboard Shortcut Handling + +`KeyboardShortcutHandler` is a headless React component (renders `null`) that registers a single `keydown` listener on `document` at mount time. It is placed at the top of the component tree (inside `App.tsx`) so it captures all keyboard events. + +The handler checks `event.metaKey`, `event.shiftKey`, `event.altKey`, and `event.key` to dispatch actions. Each action calls the appropriate store method or opens the relevant dialog. + +**Important:** The native Tauri menu also registers shortcuts (e.g., `⌘N`, `⌘⇧N`). When both the native menu and `KeyboardShortcutHandler` register the same shortcut, the native menu fires first (Tauri intercepts it before the WebView). + +### Theme System + +Themes are applied by `loadSelectedTheme()`, which: + +1. Reads the selected theme name from preferences +2. Loads the theme object (a map of CSS variable names to values) +3. Calls `applyTheme(themeObject)`, which iterates the map and sets each variable on `document.documentElement` (`:root`) + +Custom CSS is injected as a `