feat: add task comments feature with CRUD operations

- Introduced a new `task_comments` table in the database schema.
- Implemented task comments API endpoints for listing, creating, and deleting comments.
- Enhanced the task detail dialog to display comments and allow users to add new comments.
- Updated task row component to handle click events for viewing task details.
- Added a theme provider to manage light/dark mode across the application.
- Refactored Milkdown editor to use Crepe for improved markdown editing experience.
- Updated global styles to accommodate new editor and theme changes.
- Enhanced task filtering and sorting functionality in the tasks page.
This commit is contained in:
Roberto Musso
2026-02-23 12:54:14 +01:00
parent 98acf6220e
commit c1aa6829c9
24 changed files with 996 additions and 234 deletions

View File

@@ -316,3 +316,38 @@
- `useRef` for curtain open state avoids stale closures in `useEffect` wheel/keyboard handlers — the boolean ref is updated synchronously alongside `useState` setter
- `{ passive: true }` on wheel listener is correct when not calling `preventDefault()` — avoids Chrome console warnings
---
## 2026-02-23 - US-018
- What was implemented:
- Installed `keytar` native module for OS keychain token storage; added to `vite.main.config.mts` externals
- Created provider-agnostic AI architecture under `src/main/ai/`:
- `token.ts` — keychain CRUD via keytar with safeStorage fallback (handles missing libsecret on WSL/Linux), keyed by provider name
- `provider.ts` — `AIProvider` interface with `name`, `displayName`, `initialize(token)`, `isReady()` methods; provider registry (`Map`), `initAI()` startup function, `saveTokenAndInit()`, `hasActiveToken()`
- `copilot.ts` — GitHub Copilot provider implementation (initial default); registers via `registerProvider()` on import
- Updated `src/main/store.ts` — added `aiProvider: string` and `encryptedTokens: Record<string, string>` to `AppSettings`
- Updated `src/main/index.ts` — made `app.on('ready')` async, calls `await initAI()` after `initDb()`
- Updated `src/main/router/index.ts` — `ai.setToken` mutation calls `saveTokenAndInit(input.token)`, `ai.hasToken` query calls `hasActiveToken()`
- Created `/settings` route at `src/renderer/routes/settings.tsx`:
- Full settings page with left nav sidebar (shadcn pattern) + content area
- "AI Provider" section with password Input for token, Save Button, green "Saved" feedback
- Shows "A token is currently stored" indicator when `ai.hasToken` returns true
- Invalidates `ai.hasToken` query cache on successful save
- Updated `src/renderer/components/layout/AppShell.tsx`:
- Settings `SidebarMenuButton` with gear icon in sidebar footer links to `/settings` route
- Removed Dialog-based settings in favor of full route page
- Clean separation: no settings state lifted to AppShell
- Updated `src/renderer/components/ai/AIChatPanel.tsx`:
- Calls `trpc.ai.hasToken.useQuery()`; if `false`, renders `Card` with `KeyRound` icon + "AI provider not configured" + Link to `/settings`
- If `true`, shows existing "AI Chat — coming soon" placeholder
- Typecheck passes (zero errors)
- Files changed: `package.json`, `package-lock.json`, `vite.main.config.mts`, `src/main/ai/token.ts` (new), `src/main/ai/provider.ts` (new), `src/main/ai/copilot.ts` (new), `src/main/store.ts`, `src/main/index.ts`, `src/main/router/index.ts`, `src/renderer/routes/settings.tsx` (new), `src/renderer/components/layout/AppShell.tsx`, `src/renderer/components/ai/AIChatPanel.tsx`, `src/renderer/routeTree.gen.ts` (auto-generated), `prd.json`, `progress.txt`
- **Learnings for future iterations:**
- `keytar` requires `libsecret` on Linux (system dependency) — on WSL it's often missing. Use try/catch lazy require + Electron `safeStorage` as fallback to keep the app functional everywhere
- `safeStorage.encryptString()` returns a Buffer; store as base64 in electron-store. `safeStorage.decryptString()` takes a Buffer back.
- Provider-agnostic pattern: `AIProvider` interface + `Map<string, AIProvider>` registry + `electron-store` for active provider name = swap providers by adding a new implementation + `registerProvider()` call
- `initAI()` uses dynamic `await import('./copilot')` to trigger side-effect registration before reading the provider from the registry
- Settings as a full route (not a Dialog) is better for extensibility — left nav allows adding sections (Appearance, Notifications, etc.) without cluttering the sidebar
- TanStack Router route tree must be regenerated after adding a new route file: `npx @tanstack/router-cli generate` or just `npm start` (Vite plugin does it)
- Token is stored per-provider so multiple providers can have tokens stored simultaneously; switching providers just changes which key is read
- `electron-store` dot-notation access (`store.get('a.b')`) works but loses type safety; prefer `store.get('encryptedTokens')` then access the nested key on the result object
---