feat: status property — Notion-style dropdown with color chips (#97)

* fix: resolve search panel freeze by making search_vault async

The search_vault Tauri command was synchronous (fn, not async fn),
blocking the main thread for 30+ seconds during hybrid/semantic
search on large vaults (9200+ files). This caused the macOS beachball.

Changes:
- Make search_vault async with tokio::spawn_blocking (runs qmd off main thread)
- Cache collection name per vault path (avoid repeated qmd collection list calls)
- Cancel inflight searches and debounce timers when panel closes
- Add regression test for stale result cancellation on panel close

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* design: add status property dropdown wireframes

Three frames: closed pill state, open dropdown with suggested/vault
statuses, and custom status creation flow.

Also bump flaky NoteList 9000-entry test timeout from 15s to 30s.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: replace status text edit with Notion-style dropdown

- Extract STATUS_STYLES to shared statusStyles.ts utility
- New StatusDropdown component: filterable popover with colored pills
- StatusPill reusable component for consistent status chip rendering
- Vault-wide status aggregation from entries prop
- Dropdown shows "From vault" and "Suggested" sections
- Custom status creation via type-and-Enter
- Escape/backdrop click cancels without saving
- Keyboard navigation (ArrowUp/Down + Enter)
- 22 new tests covering dropdown behavior + integration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: cargo fmt

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Luca Rossi
2026-02-26 13:40:27 +01:00
committed by GitHub
parent 9c81caca46
commit d6b7343dac
11 changed files with 648 additions and 77 deletions

43
src/utils/statusStyles.ts Normal file
View File

@@ -0,0 +1,43 @@
export interface StatusStyle {
bg: string
color: string
}
export const STATUS_STYLES: Record<string, StatusStyle> = {
Active: { bg: 'var(--accent-green-light)', color: 'var(--accent-green)' },
Done: { bg: 'var(--accent-blue-light)', color: 'var(--accent-blue)' },
Paused: { bg: 'var(--accent-yellow-light)', color: 'var(--accent-yellow)' },
Archived: { bg: 'var(--accent-blue-light)', color: 'var(--muted-foreground)' },
Dropped: { bg: 'var(--accent-red-light)', color: 'var(--accent-red)' },
Open: { bg: 'var(--accent-green-light)', color: 'var(--accent-green)' },
Closed: { bg: 'var(--accent-blue-light)', color: 'var(--muted-foreground)' },
'Not started': { bg: 'var(--accent-blue-light)', color: 'var(--muted-foreground)' },
Draft: { bg: 'var(--accent-yellow-light)', color: 'var(--accent-yellow)' },
Mixed: { bg: 'var(--accent-yellow-light)', color: 'var(--accent-yellow)' },
Published: { bg: 'var(--accent-green-light)', color: 'var(--accent-green)' },
'In progress': { bg: 'var(--accent-purple-light)', color: 'var(--accent-purple)' },
Blocked: { bg: 'var(--accent-red-light)', color: 'var(--accent-red)' },
Cancelled: { bg: 'var(--accent-red-light)', color: 'var(--accent-red)' },
Pending: { bg: 'var(--accent-yellow-light)', color: 'var(--accent-yellow)' },
}
export const DEFAULT_STATUS_STYLE: StatusStyle = {
bg: 'var(--accent-blue-light)',
color: 'var(--muted-foreground)',
}
/** Default suggested statuses shown in the dropdown */
export const SUGGESTED_STATUSES = [
'Not started',
'In progress',
'Active',
'Done',
'Blocked',
'Paused',
'Draft',
'Archived',
]
export function getStatusStyle(status: string): StatusStyle {
return STATUS_STYLES[status] ?? DEFAULT_STATUS_STYLE
}