diff --git a/.STATUS b/.STATUS index 269a25c7..78fe4065 100644 --- a/.STATUS +++ b/.STATUS @@ -1,15 +1,15 @@ --- status: active priority: P1 -version: 1.17.0 +version: 1.18.0 sprint: 37 started: 2026-01-08 updated: 2026-02-22 released: 2026-01-24 editor: hybrid-markdown++ next_focus: Fix 67 test file TypeScript errors (~2.5h) / v1.17.0 Three-Tab Sidebar System -tests: 2187 -unit_tests: 2187 +tests: 2190 +unit_tests: 2190 e2e_tests: 109 test_file_errors: 67 (non-blocking, documented in docs/planning/TEST-FILE-TYPESCRIPT-ERRORS-2026-01-24.md) tags: diff --git a/CHANGELOG.md b/CHANGELOG.md index f637ebea..d1f19fa7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- +## [v1.18.0] - 2026-02-22 — Sidebar Vault Fix + +### Fixed + +- **Sidebar vault expansion showing wrong notes** (PR #43) - Vault dots now filter to the correct single project instead of passing all projects. Breadcrumb syncs on vault toggle. Fixed DexieError2 race condition in browser-mode initialization. +- **Broken brainstorm link** in MCP app spec + +### Added + +- 3 new tests for vault toggle → `onSelectProject` wiring (MissionSidebar) + +--- + ## [v1.16.3] - 2026-01-26 ### Automated Release Pipeline diff --git a/README.md b/README.md index 9aec44d3..0658eee2 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@ > **ADHD-Friendly Distraction-Free Writer** [![Status](https://img.shields.io/badge/status-active-brightgreen)]() -[![Version](https://img.shields.io/badge/version-1.17.0-blue)]() +[![Version](https://img.shields.io/badge/version-1.18.0-blue)]() [![Progress](https://img.shields.io/badge/progress-100%25-brightgreen)]() -[![Tests](https://img.shields.io/badge/tests-2187%20passing-brightgreen)]() +[![Tests](https://img.shields.io/badge/tests-2190%20passing-brightgreen)]() [![Tauri](https://img.shields.io/badge/tauri-2-blue)]() [![React](https://img.shields.io/badge/react-18-blue)]() diff --git a/BRAINSTORM-mcp-app-2026-02-22.md b/docs/archive/brainstorms/BRAINSTORM-mcp-app-2026-02-22.md similarity index 100% rename from BRAINSTORM-mcp-app-2026-02-22.md rename to docs/archive/brainstorms/BRAINSTORM-mcp-app-2026-02-22.md diff --git a/WORKFLOW-FIX-SUMMARY.md b/docs/archive/completed/WORKFLOW-FIX-SUMMARY.md similarity index 100% rename from WORKFLOW-FIX-SUMMARY.md rename to docs/archive/completed/WORKFLOW-FIX-SUMMARY.md diff --git a/docs/index.md b/docs/index.md index d9204bc2..e52dcc66 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,9 +3,9 @@ > **ADHD-Friendly Distraction-Free Writer** ![Status](https://img.shields.io/badge/status-active-brightgreen) -![Version](https://img.shields.io/badge/version-1.17.0-blue) +![Version](https://img.shields.io/badge/version-1.18.0-blue) ![Progress](https://img.shields.io/badge/progress-100%25-brightgreen) -![Tests](https://img.shields.io/badge/tests-2187%20passing-brightgreen) +![Tests](https://img.shields.io/badge/tests-2190%20passing-brightgreen) --- diff --git a/docs/reference/CHANGELOG.md b/docs/reference/CHANGELOG.md index 95e7f553..65ffc87a 100644 --- a/docs/reference/CHANGELOG.md +++ b/docs/reference/CHANGELOG.md @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- +## [v1.18.0] - 2026-02-22 — Sidebar Vault Fix + +### Fixed + +- **Sidebar vault expansion showing wrong notes** (PR #43) - Vault dots now filter to the correct single project instead of passing all projects. Breadcrumb syncs on vault toggle. Fixed DexieError2 race condition in browser-mode initialization. +- **Broken brainstorm link** in MCP app spec + +### Added + +- 3 new tests for vault toggle → `onSelectProject` wiring (MissionSidebar) + +--- + ## [v1.17.0] - 2026-02-22 — Quarto Autocomplete ### Added @@ -25,6 +38,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Escaped `\$` handling** — literal dollar signs no longer break math context tracking - **Migration loop prevented** — dedicated version flag for localStorage migrations - **70 TypeScript errors resolved** across 22 test files and 1 source file +- **Tauri JS/Rust package alignment** — updated @tauri-apps/api (2.9.1→2.10.1), plugin-dialog (2.4.2→2.6.0), CLI (2.9.6→2.10.0) to match Cargo.lock resolved versions +- **DragRegion test mock** fixed with `vi.hoisted()` for dynamic Tauri imports +- **5 broken relative links** fixed in documentation site --- diff --git a/docs/reference/CLAUDE.md b/docs/reference/CLAUDE.md index 7441ae49..c8b87504 100644 --- a/docs/reference/CLAUDE.md +++ b/docs/reference/CLAUDE.md @@ -240,14 +240,21 @@ scribe help --all # Full reference --- -## 🎯 Current Status: v1.17.0 - Quarto Autocomplete ✅ +## 🎯 Current Status: v1.18.0 - Sidebar Vault Fix ✅ -**Released:** v1.17.0 (stable) -**Dev Branch:** v1.17.0 (Quarto autocomplete, test fixes, doc sync) +**Released:** v1.18.0 (stable) +**Dev Branch:** v1.18.0 (Sidebar vault expansion fix, browser-mode init fix) **Install Stable:** `brew install --cask data-wise/tap/scribe` (v1.14.0) -**Tests:** 2,187 passing (71 files) +**Tests:** 2,190 passing (71 files) -### Latest Work: Quarto Autocomplete Stabilization (PR #40) +### Latest Work: Sidebar Vault Expansion Fix (PR #43) + +- ✅ Vault dots filter to correct single project (not all projects) +- ✅ Breadcrumb syncs via `onSelectProject` on vault toggle +- ✅ Fixed DexieError2 race condition in browser-mode init +- ✅ 3 new vault toggle wiring tests + +### Previous: Quarto Autocomplete Stabilization (PR #40) - ✅ Context-aware LaTeX completions (math-only scoping, suppressed in code blocks) - ✅ Code chunk completions (R, Python, Julia, OJS, Mermaid, Graphviz) diff --git a/docs/reference/TESTS_SUMMARY.md b/docs/reference/TESTS_SUMMARY.md index 922d47e4..3d27b343 100644 --- a/docs/reference/TESTS_SUMMARY.md +++ b/docs/reference/TESTS_SUMMARY.md @@ -1,7 +1,7 @@ # Test Coverage Summary - Scribe Editor **Generated:** 2026-01-24 -**Total Tests:** 2,187 passing (71 test files) +**Total Tests:** 2,190 passing (71 test files) **Test Framework:** Vitest + Testing Library + happy-dom **TypeScript:** 0 production errors, 67 test file warnings (documented) diff --git a/docs/specs/SPEC-mcp-app-claude-desktop-2026-02-22.md b/docs/specs/SPEC-mcp-app-claude-desktop-2026-02-22.md index 20a58351..3127c05e 100644 --- a/docs/specs/SPEC-mcp-app-claude-desktop-2026-02-22.md +++ b/docs/specs/SPEC-mcp-app-claude-desktop-2026-02-22.md @@ -5,7 +5,7 @@ **Generated:** 2026-02-22 **Status:** Draft **Priority:** P1 (New Distribution Channel) -**From Brainstorm:** [BRAINSTORM-mcp-app-2026-02-22.md](../../BRAINSTORM-mcp-app-2026-02-22.md) +**From Brainstorm:** [BRAINSTORM-mcp-app-2026-02-22.md](../archive/brainstorms/BRAINSTORM-mcp-app-2026-02-22.md) --- diff --git a/package.json b/package.json index 054c22dd..3b02182d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "scribe", - "version": "1.17.0", + "version": "1.18.0", "description": "Scribe - ADHD-friendly distraction-free writer", "main": "dist-electron/main/index.js", "author": "Stat-Wise", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index c1c2d357..0432bbe7 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -3216,7 +3216,7 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scribe" -version = "1.17.0" +version = "1.18.0" dependencies = [ "chrono", "dirs 5.0.1", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 46776a21..3154da5a 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "scribe" -version = "1.17.0" +version = "1.18.0" description = "Scribe - ADHD-friendly distraction-free writer" authors = ["Stat-Wise"] license = "MIT" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index b498a285..aa963150 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", "productName": "Scribe", - "version": "1.17.0", + "version": "1.18.0", "identifier": "com.scribe.app", "build": { "frontendDist": "../dist", diff --git a/src/renderer/src/__tests__/ExpandedIconPanel.component.test.tsx b/src/renderer/src/__tests__/ExpandedIconPanel.component.test.tsx index b2e37d1f..c7275859 100644 --- a/src/renderer/src/__tests__/ExpandedIconPanel.component.test.tsx +++ b/src/renderer/src/__tests__/ExpandedIconPanel.component.test.tsx @@ -290,9 +290,9 @@ describe('ExpandedIconPanel Component', () => { }) /** - * EIP-09: Shows all projects for pinned project vault + * EIP-09: Shows only the expanded project for pinned project vault */ - it('shows all projects when expanded icon is pinned project', () => { + it('shows only the expanded project when expanded icon is pinned project', () => { const props = createDefaultProps() const projects: Project[] = [ { @@ -320,9 +320,9 @@ describe('ExpandedIconPanel Component', () => { render() - // CompactListView should receive all projects (not filtered) + // CompactListView should receive only the expanded project (filtered) const compactView = screen.getByTestId('compact-list-view') - expect(compactView).toHaveAttribute('data-project-count', '2') + expect(compactView).toHaveAttribute('data-project-count', '1') }) /** diff --git a/src/renderer/src/__tests__/MissionSidebar.test.tsx b/src/renderer/src/__tests__/MissionSidebar.test.tsx index 38d3997d..1888977f 100644 --- a/src/renderer/src/__tests__/MissionSidebar.test.tsx +++ b/src/renderer/src/__tests__/MissionSidebar.test.tsx @@ -1,7 +1,7 @@ -import { describe, it, expect, vi } from 'vitest' -import { render, screen } from '@testing-library/react' +import { describe, it, expect, vi, beforeEach } from 'vitest' +import { render, screen, fireEvent } from '@testing-library/react' import { MissionSidebar } from '../components/sidebar/MissionSidebar' -import { Project, Note } from '../types' +import { Project, Note, SmartIconId } from '../types' /** * MissionSidebar Integration Tests (v1.16.0 Icon-Centric) @@ -12,6 +12,59 @@ import { Project, Note } from '../types' * - No collapse/expand toggle (icons control expansion) */ +// Hoisted mocks for store state — can be mutated per test +const { mockToggleIcon, mockExpandedIcon, mockPinnedVaults } = vi.hoisted(() => ({ + mockToggleIcon: vi.fn(), + mockExpandedIcon: { current: null as { type: 'vault' | 'smart'; id: string } | null }, + mockPinnedVaults: { + current: [ + { id: 'inbox', label: 'Inbox', order: 0, isPermanent: true, preferredMode: 'compact' as const }, + { id: '1', label: 'Test Research Project', order: 1, isPermanent: false, preferredMode: 'compact' as const }, + ] + } +})) + +vi.mock('../store/useAppViewStore', () => ({ + useAppViewStore: vi.fn((selector?: (state: any) => any) => { + const state = { + expandedIcon: mockExpandedIcon.current, + sidebarWidth: 280, + pinnedVaults: mockPinnedVaults.current, + smartIcons: [ + { id: 'research' as SmartIconId, label: 'Research', icon: 'flask', color: '#3b82f6', projectType: 'research' as const, isVisible: true, isExpanded: false, order: 0, preferredMode: 'compact' as const }, + { id: 'teaching' as SmartIconId, label: 'Teaching', icon: 'graduation-cap', color: '#f59e0b', projectType: 'teaching' as const, isVisible: true, isExpanded: false, order: 1, preferredMode: 'compact' as const }, + { id: 'packages' as SmartIconId, label: 'R Packages', icon: 'package', color: '#10b981', projectType: 'package' as const, isVisible: true, isExpanded: false, order: 2, preferredMode: 'compact' as const }, + { id: 'devtools' as SmartIconId, label: 'Dev Tools', icon: 'wrench', color: '#8b5cf6', projectType: 'development' as const, isVisible: true, isExpanded: false, order: 3, preferredMode: 'compact' as const }, + ], + toggleIcon: mockToggleIcon, + setIconMode: vi.fn(), + collapseAll: vi.fn(), + setSidebarWidth: vi.fn(), + reorderPinnedVaults: vi.fn(), + recentNotes: [], + clearRecentNotes: vi.fn(), + compactModeWidth: 280, + cardModeWidth: 360, + } + return selector ? selector(state) : state + }), + SIDEBAR_WIDTHS: { + icon: 48, + compact: { min: 180, default: 280, max: 400 }, + card: { min: 280, default: 360, max: 500 }, + } +})) + +vi.mock('../store/useSettingsStore', () => ({ + useSettingsStore: vi.fn((selector?: (state: any) => any) => { + const state = { + settings: { 'appearance.sidebarWidth': 'medium' }, + updateSetting: vi.fn(), + } + return selector ? selector(state) : state + }) +})) + describe('MissionSidebar Integration (v1.16.0)', () => { const mockProjects: Project[] = [ { @@ -50,7 +103,7 @@ describe('MissionSidebar Integration (v1.16.0)', () => { }, ] - const mockHandlers = { + const createMockHandlers = () => ({ onSelectProject: vi.fn(), onSelectNote: vi.fn(), onCreateProject: vi.fn(), @@ -67,7 +120,12 @@ describe('MissionSidebar Integration (v1.16.0)', () => { onMoveNoteToProject: vi.fn(), onDuplicateNote: vi.fn(), onDeleteNote: vi.fn(), - } + }) + + beforeEach(() => { + vi.clearAllMocks() + mockExpandedIcon.current = null + }) it('renders MissionSidebar component with icon-centric class', () => { const { container } = render( @@ -75,7 +133,7 @@ describe('MissionSidebar Integration (v1.16.0)', () => { projects={mockProjects} notes={mockNotes} currentProjectId={null} - {...mockHandlers} + {...createMockHandlers()} /> ) @@ -89,7 +147,7 @@ describe('MissionSidebar Integration (v1.16.0)', () => { projects={mockProjects} notes={mockNotes} currentProjectId={null} - {...mockHandlers} + {...createMockHandlers()} /> ) @@ -104,7 +162,7 @@ describe('MissionSidebar Integration (v1.16.0)', () => { projects={mockProjects} notes={mockNotes} currentProjectId={null} - {...mockHandlers} + {...createMockHandlers()} /> ) @@ -122,7 +180,7 @@ describe('MissionSidebar Integration (v1.16.0)', () => { projects={mockProjects} notes={mockNotes} currentProjectId={null} - {...mockHandlers} + {...createMockHandlers()} /> ) @@ -137,7 +195,7 @@ describe('MissionSidebar Integration (v1.16.0)', () => { projects={mockProjects} notes={mockNotes} currentProjectId={null} - {...mockHandlers} + {...createMockHandlers()} /> ) @@ -153,7 +211,7 @@ describe('MissionSidebar Integration (v1.16.0)', () => { projects={mockProjects} notes={mockNotes} currentProjectId={null} - {...mockHandlers} + {...createMockHandlers()} /> ) @@ -162,4 +220,82 @@ describe('MissionSidebar Integration (v1.16.0)', () => { // Should have data-mode attribute expect(sidebar).toHaveAttribute('data-mode') }) + + /** + * Vault toggle → onSelectProject wiring tests + * + * When a vault dot is clicked to expand, onSelectProject should be called + * so the breadcrumb and search scope update to match the expanded project. + * When collapsing, onSelectProject should NOT be called. + */ + describe('vault toggle updates currentProject', () => { + it('calls onSelectProject when vault dot is expanded', () => { + // expandedIcon is null → clicking vault '1' is an expansion + mockExpandedIcon.current = null + const handlers = createMockHandlers() + + render( + + ) + + // Click the pinned project vault dot + const vaultDot = screen.getByTestId('project-icon-1') + fireEvent.click(vaultDot) + + // Should call toggleIcon (store action) + expect(mockToggleIcon).toHaveBeenCalledWith('vault', '1') + // Should call onSelectProject with the project ID (breadcrumb update) + expect(handlers.onSelectProject).toHaveBeenCalledWith('1') + }) + + it('does not call onSelectProject when vault dot is collapsed', () => { + // expandedIcon already pointing to vault '1' → clicking it is a collapse + mockExpandedIcon.current = { type: 'vault', id: '1' } + const handlers = createMockHandlers() + + render( + + ) + + // Click the same vault dot (collapsing it) + const vaultDot = screen.getByTestId('project-icon-1') + fireEvent.click(vaultDot) + + // Should still call toggleIcon (to collapse) + expect(mockToggleIcon).toHaveBeenCalledWith('vault', '1') + // Should NOT call onSelectProject (we're collapsing, not selecting) + expect(handlers.onSelectProject).not.toHaveBeenCalled() + }) + + it('calls onSelectProject(null) when inbox vault is expanded', () => { + mockExpandedIcon.current = null + const handlers = createMockHandlers() + + render( + + ) + + // Click the inbox button + const inboxBtn = screen.getByTestId('inbox-icon-button') + fireEvent.click(inboxBtn) + + // Should call onSelectProject(null) — inbox has no project + expect(handlers.onSelectProject).toHaveBeenCalledWith(null) + }) + }) }) diff --git a/src/renderer/src/components/sidebar/ExpandedIconPanel.tsx b/src/renderer/src/components/sidebar/ExpandedIconPanel.tsx index fd12a74d..fda36ce1 100644 --- a/src/renderer/src/components/sidebar/ExpandedIconPanel.tsx +++ b/src/renderer/src/components/sidebar/ExpandedIconPanel.tsx @@ -89,11 +89,11 @@ export function ExpandedIconPanel({ filteredProjects: [] } } - // Pinned project: Show all projects with this one selected + // Pinned project: Show only this project return { label: projects.find(p => p.id === expandedIcon.id)?.name || 'Project', showInboxNotes: false, - filteredProjects: projects + filteredProjects: projects.filter(p => p.id === expandedIcon.id) } } diff --git a/src/renderer/src/components/sidebar/MissionSidebar.tsx b/src/renderer/src/components/sidebar/MissionSidebar.tsx index d15873f8..046f045e 100644 --- a/src/renderer/src/components/sidebar/MissionSidebar.tsx +++ b/src/renderer/src/components/sidebar/MissionSidebar.tsx @@ -186,7 +186,15 @@ export function MissionSidebar({ projects={projects} notes={notes} expandedIcon={expandedIcon} - onToggleVault={(id) => toggleIcon('vault', id)} + onToggleVault={(id) => { + // If this vault is already expanded, we're collapsing — don't update project + const isCollapsing = expandedIcon?.type === 'vault' && expandedIcon?.id === id + toggleIcon('vault', id) + if (!isCollapsing) { + // Expanding a vault: update currentProjectId so breadcrumb + search scope follow + onSelectProject(id === 'inbox' ? null : id) + } + }} onToggleSmartIcon={(id) => toggleIcon('smart', id)} onSearch={onSearch} onDaily={onDaily} diff --git a/src/renderer/src/lib/browser-api.ts b/src/renderer/src/lib/browser-api.ts index 7c18642d..3c4e4389 100644 --- a/src/renderer/src/lib/browser-api.ts +++ b/src/renderer/src/lib/browser-api.ts @@ -669,6 +669,7 @@ export const reindexAllNotes = async (): Promise => { */ export const initializeBrowserApi = async (): Promise => { console.log('[Scribe] Using IndexedDB for persistence') + await db.initialize() await seedDemoData() // Reindex existing notes for wiki links and tags diff --git a/src/renderer/src/lib/browser-db.ts b/src/renderer/src/lib/browser-db.ts index 5b7bbb77..127b8d3c 100644 --- a/src/renderer/src/lib/browser-db.ts +++ b/src/renderer/src/lib/browser-db.ts @@ -260,11 +260,6 @@ export const seedDemoData = async (): Promise => { return true } -// Auto-run seed on module import (after db is initialized) -// Skip in test environment to avoid interfering with test data -if (!import.meta.env.VITEST) { - ;(async () => { - await db.initialize() - await seedDemoData() - })().catch(console.error) -} +// NOTE: Auto-initialization removed — browser-api.ts is the single init entry point. +// This prevents double-init race conditions (DexieError2) when both modules +// fire concurrent async init chains on import.