diff --git a/docs/README.md b/docs/README.md index 1063cee3..679f10c7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -11,6 +11,7 @@ polish and hardening. - [Contributing](../CONTRIBUTING.md): contributor workflow, code style, testing expectations, versioning, and release preparation. - [Project Architecture](project-architecture.md): high-level map of the monorepo, Electron runtime, IPC boundaries, Rust sidecar, shared crates, web clipper, ACP runtimes, and major data flows. - [Testing and Validation](testing.md): the command matrix for Rust, desktop, Electron smoke tests, web clipper checks, CI parity, and area-specific validation. +- [Settings Scope](settings-scope.md): inventory of global, per-vault, and mixed-scope settings, preferences, local UI state, migrations, and storage keys. ## AI And Change Control @@ -49,4 +50,4 @@ tokens, provider credentials, or personally sensitive paths. These currently live next to their implementation/release artifacts because they are tightly coupled to package-specific workflows. -Last updated: May 11, 2026. +Last updated: May 30, 2026. diff --git a/docs/settings-scope.md b/docs/settings-scope.md new file mode 100644 index 00000000..98732909 --- /dev/null +++ b/docs/settings-scope.md @@ -0,0 +1,247 @@ +# Settings Scope + +This page documents the current settings and preference storage boundaries in +NeverWrite. It is meant as a maintenance reference for changes to Settings, +review behavior, AI preferences, vault-specific state, and local UI state. + +The short version: + +- Most desktop Settings values are scoped to the current vault and are stored in + `neverwrite:settings:`. +- Vim settings are global even though they are shown inside Editor settings. +- Theme is vault-scoped after migration, with a global fallback for first run and + legacy data. +- AI chat preferences are mostly global, except auto-context, which is + per-vault. +- Some persisted values are UI state or caches, not user-facing settings. They + are listed separately because they still affect debugging and privacy. + +Related implementation references: + +- [`settingsStore.ts`](../apps/desktop/src/app/store/settingsStore.ts) +- [`SettingsPanel.tsx`](../apps/desktop/src/features/settings/SettingsPanel.tsx) +- [`themeStore.ts`](../apps/desktop/src/app/store/themeStore.ts) +- [`chatStore.ts`](../apps/desktop/src/features/ai/store/chatStore.ts) +- [`graphSettingsStore.ts`](../apps/desktop/src/features/graph/graphSettingsStore.ts) +- [`data-and-privacy.md`](data-and-privacy.md) + +## Scope Model + +Desktop renderer preferences use the `safeStorage` wrapper, which prefers +`window.localStorage` and falls back to in-memory storage when localStorage is +unavailable. + +The main settings store uses these keys: + +| Storage key | Scope | Contents | +| --- | --- | --- | +| `neverwrite:settings` | Global fallback | Legacy fallback data plus the explicitly global Vim settings. | +| `neverwrite:settings:` | Per-vault | Main `Settings` values for the vault, excluding explicitly global keys. | +| `neverwrite:lastVaultPath` | Global app state | Initial vault path lookup for hydration, not a user-facing setting itself. | + +`GLOBAL_SETTING_KEYS` currently contains only: + +| Setting | Scope | +| --- | --- | +| `vimModeEnabled` | Global | +| `vimRelativeLineNumbers` | Global | + +When a vault is open, `settingsStore` writes all other `Settings` values to +`neverwrite:settings:` and writes the Vim values back to +`neverwrite:settings`. When no vault is available, the fallback key can contain a +full `Settings` object. + +## Settings Panel Matrix + +These are the settings exposed through the desktop Settings panel or backed by +the same Settings stores. + +| UI area | Setting | Scope | Default | Storage / source | Notes | +| --- | --- | --- | --- | --- | --- | +| General / Startup | `openLastVaultOnLaunch` | Per-vault, fallback when no vault is open | `true` | `neverwrite:settings:` | Semantically startup-like, but persisted with the current vault when one is open. | +| General / Tabs | `tabOpenBehavior` | Per-vault | `history` | `neverwrite:settings:` | Valid values are `history` and `new_tab`. | +| Appearance / Mode | `mode` | Per-vault, legacy global fallback | `system` | `neverwrite:theme:` | Valid values are `system`, `light`, and `dark`. | +| Appearance / Mode | `themeName` | Per-vault, legacy global fallback | `default` | `neverwrite:theme:` | `isDark` is derived from `mode` plus OS preference. | +| Appearance / Navigation | `fileTreeScale` | Per-vault | `114` | `neverwrite:settings:` | Clamped to `90..140`. | +| Appearance / Navigation | `agentsSidebarScale` | Per-vault | `100` | `neverwrite:settings:` | Clamped to `90..140`. | +| Appearance / Navigation | `fileTreeStickyFolders` | Per-vault | `true` | `neverwrite:settings:` | Controls sticky parent folders in the file tree. | +| Appearance / Zoom | `appZoom` | Global | `1` | `neverwrite:appZoom` | Stored outside `settingsStore`; normalized by `appZoom.ts`. | +| Editor / Typography | `editorFontSize` | Per-vault | `14` | `neverwrite:settings:` | Clamped to `10..24`. | +| Editor / Typography | `editorFontFamily` | Per-vault | `system` | `neverwrite:settings:` | Validated against `EDITOR_FONT_FAMILY_OPTIONS`. | +| Editor / Typography | `editorLineHeight` | Per-vault | `175` | `neverwrite:settings:` | Percentage, clamped to `120..220`. | +| Editor / Typography | `editorAutosaveDelayMs` | Per-vault | `300` | `neverwrite:settings:` | Clamped to `50..5000`. | +| Editor / Formatting | `lineWrapping` | Per-vault | `true` | `neverwrite:settings:` | Used by editor and review surfaces. | +| Editor / Formatting | `justifyText` | Per-vault | `false` | `neverwrite:settings:` | Only meaningful when wrapping is enabled. | +| Editor / Formatting | `livePreviewEnabled` | Per-vault | `true` | `neverwrite:settings:` | Controls source vs live-preview editor mode; also exposed through quick actions outside the Settings panel. | +| Editor / Formatting | `tabSize` | Per-vault | `2` | `neverwrite:settings:` | Normalized to `2` or `4`. | +| Editor / Vim | `vimModeEnabled` | Global | `false` | `neverwrite:settings` | Migrated from vault-scoped data if found. | +| Editor / Vim | `vimRelativeLineNumbers` | Global | `false` | `neverwrite:settings` | Migrated from vault-scoped data if found. | +| Editor / Layout | `editorContentWidth` | Per-vault | `940` | `neverwrite:settings:` | Clamped to `600..1200`. | +| PDF toolbar | `pdfFilter` | Per-vault | `none` | `neverwrite:settings:` | Cycled from the PDF tab toolbar. Valid values are `none`, `dark`, `sepia`, and `grayscale`. | +| AI / Context | `inlineReviewEnabled` | Per-vault | `true` | `neverwrite:settings:` | Gates inline review in source mode. This is a review-system correctness setting. | +| AI / Context | `autoContextEnabled` | Per-vault, global fallback | `false` | `neverwrite.ai.auto-context:` | Legacy `neverwrite.ai.preferences.autoContextEnabled` is still read as fallback. | +| AI / Chat | `chatFontFamily` | Global | `system` | `neverwrite.ai.preferences` | Validated with editor font-family normalization. | +| AI / Chat | `chatFontSize` | Global | `14` | `neverwrite.ai.preferences` | Chat transcript font size. | +| AI / Chat | `historyRetentionDays` | Global preference, applied to current vault histories | `0` | `neverwrite.ai.preferences` | `0` means forever; pruning operates on the currently open vault's `.neverwrite/sessions/`. | +| AI / Composer | `requireCmdEnterToSend` | Global | `false` | `neverwrite.ai.preferences` | Changes Enter behavior in the AI composer. | +| AI / Composer | `contextUsageBarEnabled` | Global | `true` | `neverwrite.ai.preferences` | Shows or hides composer context usage. | +| AI / Composer | `screenshotRetentionSeconds` | Global | `0` | `neverwrite.ai.preferences` | `0` means forever. | +| AI / Composer | `composerFontFamily` | Global | `system` | `neverwrite.ai.preferences` | Validated with editor font-family normalization. | +| AI / Composer | `composerFontSize` | Global | `14` | `neverwrite.ai.preferences` | Composer input font size. | +| AI Providers | `defaultRuntimeId` | Global | Runtime-dependent | `neverwrite.ai.preferences` | Preferred runtime for new chats. | +| AI Providers | `modelId` | Global | Runtime-dependent | `neverwrite.ai.preferences` | Last selected model preference when supported by the active runtime. | +| AI Providers | `modeId` | Global | Runtime-dependent | `neverwrite.ai.preferences` | Last selected mode preference when supported by the active runtime. | +| AI Providers | `configOptions` | Global | Runtime-dependent | `neverwrite.ai.preferences` | Last selected runtime config options. Model and mode option categories also update `modelId` and `modeId`. | +| AI Providers | Runtime setup metadata | Global app-data | Empty | `/ai/runtime-setup.json` | Stores non-secret env values, auth method, custom binary path, auth invalidation time, and names of configured secret keys. | +| AI Providers | Runtime secret values | Global OS credential store | Empty | `NeverWrite AI Provider Secrets` | API keys and secret headers are stored in the OS keyring, not localStorage. | +| Spellcheck / Languages | `editorSpellcheck` | Per-vault | `false` | `neverwrite:settings:` | The global-to-vault migration explicitly resets this to `false` for the new vault entry. | +| Spellcheck / Languages | `spellcheckPrimaryLanguage` | Per-vault | `system` | `neverwrite:settings:` | Legacy `spellcheckLanguage` is still migrated. | +| Spellcheck / Languages | `spellcheckSecondaryLanguage` | Per-vault | `null` | `neverwrite:settings:` | Normalization prevents duplicating the primary language. | +| Spellcheck / Grammar Check | `grammarCheckEnabled` | Per-vault | `false` | `neverwrite:settings:` | Enables LanguageTool grammar checks. | +| Spellcheck / Grammar Check | `grammarCheckServerUrl` | Per-vault | `""` | `neverwrite:settings:` | Trimmed on load; empty means the built-in/public default path used by the feature. | +| Terminal / Font | `terminalFontFamily` | Per-vault | `""` | `neverwrite:settings:` | Empty string means use the built-in terminal font stack. | +| Terminal / Font | `terminalFontSize` | Per-vault | `13` | `neverwrite:settings:` | Clamped to `8..24`. | +| Terminal / Shell Environment | `claudeCodeOptimized` | Per-vault | `false` | `neverwrite:settings:` | Adds `CLAUDE_CODE_NO_FLICKER=1` to newly opened Claude Code terminals. | +| Terminal / Claude Code | `claudeCodeSkipPermissions` | Per-vault | `false` | `neverwrite:settings:` | Enables the Claude Code skip-permissions launch flag. | +| Terminal / Claude Code | `claudeCodeModel` | Per-vault | `""` | `neverwrite:settings:` | Empty string means Claude Code default. | +| Terminal / Claude Code | `claudeCodeContinueSession` | Per-vault | `false` | `neverwrite:settings:` | Adds continue/resume behavior for new Claude Code terminal launches. | +| Terminal / Claude Code | `claudeCodeMaxTurns` | Per-vault | `0` | `neverwrite:settings:` | `0` means unlimited; storage normalization clamps to `0..1000`. | +| File Tree | `fileTreeContentMode` | Per-vault | `notes_only` | `neverwrite:settings:` | Valid values are `notes_only` and `all_files`. Affects file tree, file pickers, mentions, and wikilink suggestions. | +| File Tree | `fileTreeShowExtensions` | Per-vault | `false` | `neverwrite:settings:` | Shows full filenames with extensions. | +| File Tree | `fileTreeExtensionFilter` | Per-vault | `[]` | `neverwrite:settings:` | Lowercase extension allowlist; normalized by stripping leading dots and duplicates. | +| Vault | Recent vaults | Global | `[]` | `neverwrite:recentVaults` | Recent and pinned vault metadata. The main process also mirrors a shortened list to `/recent_vaults.json`. | +| Vault | Last vault path | Global | `null` | `neverwrite:lastVaultPath` | Used for startup and initial settings/theme hydration. | +| Updates | Update configuration/status | Derived runtime state | N/A | Electron updater APIs | Settings shows version, channel, endpoint, status, and available update. These are not persisted user preferences in the renderer. | +| Shortcuts | Shortcut reference | Static/derived | N/A | Shortcut registry | Settings currently displays registered shortcuts; it does not persist user shortcut overrides. | +| Feedback / Sponsors | Links | Static | N/A | Settings UI | No persisted settings. | + +## Graph Settings + +Graph settings use Zustand `persist` under the key `vault-graph-settings`. Most +graph settings are global preferences, with one explicit per-vault map: +`defaultModeByVault`. + +| Setting | Scope | Default | Notes | +| --- | --- | --- | --- | +| `graphMode` | Global | `global` | Current graph mode. | +| `rendererMode` | Global | `2d` | Valid values are `2d` and `3d`. | +| `localDepth` | Global | `2` | Local graph traversal depth. | +| `qualityMode` | Global | `auto` | Can resolve to quality presets. | +| `layoutStrategy` | Global | `preset` | Force/preset/overview/cluster strategy. | +| `defaultModeByVault` | Per-vault map inside global key | `{}` | Maps vault paths to their default graph mode. | +| `centerForce` | Global | `0.3` | Force layout tuning. | +| `repelForce` | Global | `80` | Force layout tuning. | +| `linkForce` | Global | `0.3` | Force layout tuning. | +| `linkDistance` | Global | `60` | Force layout tuning. | +| `nodeSize` | Global | `3` | Graph display. | +| `linkThickness` | Global | `0.5` | Graph display. | +| `showTitles` | Global | `true` | Graph label display. | +| `textFadeThreshold` | Global | `0.6` | Graph label fade threshold. | +| `arrows` | Global | `false` | Link arrow display. | +| `glowIntensity` | Global | `50` | Graph display. | +| `maxGlobalNodes` | Global | `8000` | Global graph cap. | +| `maxGlobalLinks` | Global | `24000` | Global graph cap. | +| `maxOverviewNodes` | Global | `400` | Overview graph cap. | +| `maxOverviewLinks` | Global | `1200` | Overview graph cap. | +| `maxLocalNodes` | Global | `2500` | Local graph cap. | +| `maxLocalLinks` | Global | `12000` | Local graph cap. | +| `searchFilter` | Global persisted UI state | `""` | Persisted filter text; not usually treated as a durable setting. | +| `showOrphans` | Global | `true` | Graph filter. | +| `showTagNodes` | Global | `false` | Graph filter. | +| `showAttachmentNodes` | Global | `false` | Graph filter. | +| `groups` | Global | `[]` | Ordered graph groups. | +| `panelOpen` | Global persisted UI state | `false` | Panel visibility, not a behavioral setting. | + +Graph layout snapshots are caches, not settings. They are stored with the +`vault-graph-layout:v1:` prefix and include the vault path in the serialized key. + +## Other Persisted Desktop State + +These values are not all Settings-panel controls, but they are user-visible +preferences, workspace state, or privacy-relevant local state. + +| Storage key | Scope | Default | Owner | Notes | +| --- | --- | --- | --- | --- | +| `neverwrite:theme` | Global fallback / legacy | `{ mode: "system", themeName: "default" }` | `themeStore.ts` | Migrated into `neverwrite:theme:` when a vault opens. | +| `neverwrite:theme:` | Per-vault | Global theme or default | `themeStore.ts` | Active theme preference for the vault. | +| `neverwrite:bookmarks:` | Per-vault | Empty folders/items | `bookmarkStore.ts` | Bookmark folders and entries for the vault. | +| `neverwrite.session.tabs` | Global fallback / legacy | None | `editorSession.ts` | Legacy fallback for workspace tabs. | +| `neverwrite.session.tabs:` | Per-vault | Current workspace | `editorSession.ts` | Editor tabs and workspace restore state. | +| `neverwrite.chat.tabs:` | Per-vault | Initial chat tab state | `chatTabsStore.ts` | Chat tab workspace for the vault. | +| `neverwrite.ai.review.view::` | Per-vault plus session | None | `reviewTabPersistence.ts` | Review tab UI state such as expanded files, scroll, anchors, zoom, and wide mode. | +| `neverwrite.devtools.terminal.tabs:` | Per-vault | Initial terminal tab | `useTerminalTabs.ts` | Terminal workspace tabs for the vault. | +| `neverwrite.workspace.terminal.legacyMigrated:` | Per-vault migration marker | None | `legacyTerminalMigration.ts` | Marks migration from older terminal workspace state. | +| `neverwrite.terminal.replay:` | Per-terminal | None | `terminalRuntimeStore.ts` | Terminal replay buffer snapshot; cache/state, not a setting. | +| `neverwrite.sidebar.width` | Global layout | `280` | `layoutStore.ts` | Sidebar width. | +| `neverwrite.sidebar.collapsed` | Global layout | `false` | `layoutStore.ts` | Sidebar collapsed state. | +| `neverwrite.sidebar.view` | Global layout | `files` | `layoutStore.ts` | Active left sidebar view. | +| `neverwrite.rightpanel.width` | Global layout | `280` | `layoutStore.ts` | Right panel width. | +| `neverwrite.rightpanel.collapsed` | Global layout | `false` | `layoutStore.ts` | Right panel collapsed state. | +| `neverwrite.rightpanel.view` | Global layout | `outline` | `layoutStore.ts` | Active right panel view. | +| `neverwrite.editor-pane.sizes` | Global layout | `[1]` | `layoutStore.ts` | Editor pane split ratios. | +| `neverwrite:sort-mode` | Global file tree preference | `name_asc` | `FileTree.tsx` | File tree sort mode. | +| `neverwrite:reveal-active` | Global file tree preference | `false` | `FileTree.tsx` | Whether the file tree reveals the active tab. | +| `neverwrite:file-tree-expanded-folders:` | Per-vault file tree state | None | `FileTree.tsx` | Expanded folder paths. | +| `neverwrite.fileTree.clipboard` | Global transient state | None | `fileTreeClipboard.ts` | File tree copy/cut payload. | +| `neverwrite.search.history` | Global preference/state | `[]` | `searchHistory.ts` | Recent search queries. | +| `neverwrite.chats.pinnedIds` | Global preference/state | `[]` | `pinnedChatsStore.ts` | Pinned chat session ids. | +| `neverwrite.ai.agentsSidebar.collapsedParents` | Global UI state | `[]` | `AgentsSidebarPanel.tsx` | Collapsed parent groups in the agents sidebar. | +| `neverwrite.ai.runtime-catalog` | Global cache | `{}` | `chatStore.ts` | Cached runtime models, modes, and config option catalogs. | +| `neverwrite:debug-log-scopes` | Global developer preference | None | `runtimeLog.ts` | Enables scoped debug logging. | +| `neverwrite:perf-probe` | Global developer preference | `false` | `perfInstrumentation.ts` | Enables performance probe instrumentation. | +| `neverwrite:window-operational-state: