From f0547e940f73b3456883ae779db25c3bb371cf16 Mon Sep 17 00:00:00 2001 From: Yujong Lee Date: Fri, 17 Apr 2026 16:55:09 +0900 Subject: [PATCH 01/12] wip --- .agents/skills/migrate-to-sqlite/SKILL.md | 49 +- .oxlintrc.json | 28 +- .../components/apple/calendar-selection.tsx | 23 +- .../src/calendar/components/event-chip.tsx | 50 +- .../components/oauth/calendar-selection.tsx | 40 +- .../src/calendar/components/session-chip.tsx | 23 +- apps/desktop/src/calendar/hooks.ts | 315 ++++++++++- .../src/chat/components/chat-panel.tsx | 8 +- apps/desktop/src/chat/components/header.tsx | 24 +- .../chat/components/message/tool/search.tsx | 10 - .../src/chat/components/session-provider.tsx | 58 +- .../chat/context/use-chat-context-pipeline.ts | 17 +- apps/desktop/src/chat/hooks/chat-groups.ts | 42 ++ apps/desktop/src/chat/hooks/chat-messages.ts | 79 +++ apps/desktop/src/chat/hooks/chat-store.ts | 20 + .../src/chat/hooks/context-renderers.ts | 85 +++ .../persisted-messages.test.ts | 0 .../{store => hooks}/persisted-messages.ts | 0 .../chat/{store => hooks}/use-chat-actions.ts | 0 .../{store => hooks}/useCreateChatMessage.ts | 0 apps/desktop/src/chat/tools/edit-summary.ts | 5 +- .../src/chat/transport/use-transport.ts | 100 +--- apps/desktop/src/chat_shortcuts/details.tsx | 38 +- apps/desktop/src/chat_shortcuts/hooks.ts | 95 ++++ apps/desktop/src/chat_shortcuts/index.tsx | 75 +-- apps/desktop/src/composer/index.tsx | 14 +- apps/desktop/src/contacts/details.tsx | 321 ++--------- apps/desktop/src/contacts/hooks.ts | 510 ++++++++++++++++++ apps/desktop/src/contacts/humans.tsx | 6 +- apps/desktop/src/contacts/index.tsx | 24 +- apps/desktop/src/contacts/new-person-form.tsx | 22 +- .../src/contacts/organization-details.tsx | 61 +-- .../src/contacts/organization-item.tsx | 43 +- apps/desktop/src/contacts/person-item.tsx | 39 +- apps/desktop/src/edit/tab-content.tsx | 4 +- .../editor/node-views/session-view.test.tsx | 10 + .../src/editor/node-views/session-view.tsx | 14 +- .../editor/node-views/task-item-view.test.tsx | 2 +- .../src/editor/node-views/task-item-view.tsx | 3 +- apps/desktop/src/editor/session/index.tsx | 3 +- apps/desktop/src/editor/tasks.test.ts | 3 +- apps/desktop/src/folders/index.tsx | 24 +- apps/desktop/src/main/layout.tsx | 2 +- apps/desktop/src/main/lifecycle.tsx | 12 +- apps/desktop/src/main2/home/note-editor.tsx | 22 +- apps/desktop/src/main2/layout.tsx | 2 +- apps/desktop/src/main2/lifecycle.tsx | 6 +- apps/desktop/src/main2/profile-menu.tsx | 11 +- .../src/search/components/sidebar/item.tsx | 8 +- .../src/search/contexts/engine/listeners.ts | 5 +- apps/desktop/src/services/calendar/ctx.ts | 5 +- apps/desktop/src/services/enhancer/index.ts | 9 +- apps/desktop/src/services/event-listeners.tsx | 4 +- apps/desktop/src/services/task-manager.tsx | 14 +- .../bottom-accessory/during-session.tsx | 31 +- .../components/note-input/enhanced/editor.tsx | 42 +- .../note-input/enhanced/enhance-error.tsx | 9 +- .../components/note-input/enhanced/index.tsx | 11 +- .../session/components/note-input/header.tsx | 64 +-- .../src/session/components/note-input/raw.tsx | 39 +- .../note-input/transcript/export-data.ts | 29 +- .../transcript/renderer/data-hooks.ts | 41 +- .../transcript/renderer/segment-header.tsx | 4 +- .../transcript/renderer/speaker-assign.tsx | 28 +- .../transcript/renderer/transcript.tsx | 4 +- .../components/note-input/transcript/state.ts | 17 +- .../components/outer-header/folder/index.tsx | 9 +- .../folder/searchable-dropdown.tsx | 16 +- .../components/outer-header/metadata/date.tsx | 21 +- .../outer-header/metadata/index.tsx | 10 +- .../metadata/participants/chip.tsx | 48 +- .../metadata/participants/input.tsx | 116 ++-- .../outer-header/metadata/tags/chip.tsx | 26 +- .../outer-header/metadata/tags/input.tsx | 29 +- .../outer-header/overflow/delete.tsx | 6 +- .../outer-header/overflow/export-modal.tsx | 56 +- .../outer-header/overflow/export-pdf.test.tsx | 24 +- .../outer-header/overflow/export-pdf.tsx | 56 +- .../components/session-preview-card.tsx | 45 +- .../desktop/src/session/components/shared.tsx | 16 +- .../src/session/components/title-input.tsx | 14 +- apps/desktop/src/session/hooks/storage.ts | 505 +++++++++++++++++ .../src/session/hooks/useEventCountdown.ts | 2 +- .../src/session/hooks/useRemoteMeeting.ts | 2 +- apps/desktop/src/session/index.tsx | 13 +- apps/desktop/src/settings/data/index.tsx | 9 +- apps/desktop/src/settings/index.tsx | 3 + apps/desktop/src/settings/lab/dictation.tsx | 196 +++++++ .../src/settings/memory/custom-vocabulary.tsx | 66 +-- apps/desktop/src/settings/memory/hooks.ts | 68 +++ apps/desktop/src/shared/open-note-dialog.tsx | 6 +- apps/desktop/src/sidebar/contacts.tsx | 149 ++--- apps/desktop/src/sidebar/devtool.tsx | 6 +- apps/desktop/src/sidebar/profile/index.tsx | 7 +- apps/desktop/src/sidebar/settings.tsx | 2 + apps/desktop/src/sidebar/timeline/index.tsx | 23 +- apps/desktop/src/sidebar/timeline/item.tsx | 27 +- .../src/sidebar/toast/undo-delete-toast.tsx | 4 +- .../src/store/tinybase/hooks/index.test.tsx | 111 ---- .../src/store/tinybase/hooks/index.tsx | 431 --------------- apps/desktop/src/store/zustand/tabs/schema.ts | 2 + apps/desktop/src/stt/contexts.tsx | 6 +- apps/desktop/src/stt/useKeywords.ts | 11 +- apps/desktop/src/stt/useRunBatch.ts | 15 +- .../desktop/src/stt/useStartListening.test.ts | 7 + apps/desktop/src/stt/useStartListening.ts | 12 +- apps/desktop/src/stt/useUploadFile.ts | 6 +- .../task-storage.tsx => tasks/hooks.tsx} | 9 +- apps/desktop/src/templates/utils.ts | 11 +- eslint-plugin-hypr.mjs | 15 +- 110 files changed, 2725 insertions(+), 2197 deletions(-) create mode 100644 apps/desktop/src/chat/hooks/chat-groups.ts create mode 100644 apps/desktop/src/chat/hooks/chat-messages.ts create mode 100644 apps/desktop/src/chat/hooks/chat-store.ts create mode 100644 apps/desktop/src/chat/hooks/context-renderers.ts rename apps/desktop/src/chat/{store => hooks}/persisted-messages.test.ts (100%) rename apps/desktop/src/chat/{store => hooks}/persisted-messages.ts (100%) rename apps/desktop/src/chat/{store => hooks}/use-chat-actions.ts (100%) rename apps/desktop/src/chat/{store => hooks}/useCreateChatMessage.ts (100%) create mode 100644 apps/desktop/src/chat_shortcuts/hooks.ts create mode 100644 apps/desktop/src/contacts/hooks.ts create mode 100644 apps/desktop/src/session/hooks/storage.ts create mode 100644 apps/desktop/src/settings/lab/dictation.tsx create mode 100644 apps/desktop/src/settings/memory/hooks.ts delete mode 100644 apps/desktop/src/store/tinybase/hooks/index.test.tsx delete mode 100644 apps/desktop/src/store/tinybase/hooks/index.tsx rename apps/desktop/src/{editor/task-storage.tsx => tasks/hooks.tsx} (99%) diff --git a/.agents/skills/migrate-to-sqlite/SKILL.md b/.agents/skills/migrate-to-sqlite/SKILL.md index 2d9fbb02a6..cb8a0edc6b 100644 --- a/.agents/skills/migrate-to-sqlite/SKILL.md +++ b/.agents/skills/migrate-to-sqlite/SKILL.md @@ -8,39 +8,68 @@ description: Migrate a TinyBase table to SQLite. Use when asked to move a data d Keep up to date as each PR lands. Outer box = fully done across both phases. Sub-bullets track sub-states where relevant. +Phase 0 is essentially complete — every non-entrypoint consumer of the +main TinyBase store's `UI.*` surface goes through `~//hooks*`. +`apps/desktop/src/main.tsx` (the app entry wiring up the +`TinyBaseProvider`) is the only consumer that still touches `UI.*` +directly. + +Non-UI imperative helpers (`store/sessions`, `store/save`, +`store/deleteSession`, `store/importer`, `store/settings`, `persister/*`) +are still imported directly from a handful of consumer files. These are +outside phase-0 scope — phase 0 targets reactive UI access only — and +the `hypr/no-raw-tinybase` rule permits them. + - [x] `templates` — already Drizzle, no Phase 0 needed - [ ] `calendars` - - [x] Phase 0 reads (PR 2: `useCalendar`, `useEnabledCalendars`) - - [ ] Phase 0 writes — `services/calendar/ctx.ts` has a cross-domain - calendars+events transaction; lands with events PR + - [x] Phase 0 — `calendar/hooks.ts` - [ ] Phase 1 — Rust migration + ops exist - [ ] `events` - - [ ] Phase 0 + - [x] Phase 0 — `calendar/hooks.ts` + `services/calendar/*` (sync via `ctx.store`) - [ ] Phase 1 — Rust migration + ops exist - [ ] `sessions` + - [x] Phase 0 — `session/hooks/storage.ts` used by session UI, STT, sidebar, main2/home, editor, services + - [ ] Phase 1 - [ ] `transcripts` + - [x] Phase 0 — `session/hooks/storage.ts` + STT hooks + - [ ] Phase 1 - [ ] `humans` + - [x] Phase 0 — `contacts/hooks.ts` + - [ ] Phase 1 - [ ] `organizations` + - [x] Phase 0 — `contacts/hooks.ts` + - [ ] Phase 1 - [ ] `enhanced_notes` - - [x] Phase 0 reads — `session/hooks/useEnhancedNotes.ts` - - [ ] Phase 0 writes + - [x] Phase 0 — `session/hooks/useEnhancedNotes.ts` + `session/hooks/storage.ts` - [ ] Phase 1 - [ ] `mapping_session_participant` + - [x] Phase 0 — `session/hooks/storage.ts` (reads + writes) + - [ ] Phase 1 - [ ] `mapping_tag_session` + - [x] Phase 0 — `session/hooks/storage.ts` + - [ ] Phase 1 - [ ] `mapping_mention` - [ ] `tags` + - [x] Phase 0 — `session/hooks/storage.ts` + - [ ] Phase 1 - [ ] `chat_groups` + - [x] Phase 0 — `chat/hooks/chat-groups.ts` + - [ ] Phase 1 - [ ] `chat_messages` - - [x] Phase 0 writes (partial) — `chat/store/*` - - [ ] Phase 0 reads + - [x] Phase 0 — `chat/hooks/*` - [ ] Phase 1 - [ ] `chat_shortcuts` + - [x] Phase 0 — `chat_shortcuts/hooks.ts` + - [ ] Phase 1 - [ ] `tasks` + - [x] Phase 0 — `tasks/hooks.tsx` + - [ ] Phase 1 - [ ] `memories` - - [x] Phase 0 writes — `settings/memory/custom-vocabulary.tsx` - - [ ] Phase 0 reads + - [x] Phase 0 — `settings/memory/hooks.ts` - [ ] Phase 1 - [ ] `daily_notes` + - [x] Phase 0 — `session/hooks/storage.ts` (`useUpdateDailyNoteContent`) + `main2/home/note-editor.tsx` uses it + - [ ] Phase 1 ## Strategy diff --git a/.oxlintrc.json b/.oxlintrc.json index 6d85c1adeb..a7f933c439 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -37,33 +37,7 @@ } }, { - "files": [ - "apps/desktop/src/ai/**", - "apps/desktop/src/auth/**", - "apps/desktop/src/calendar/**", - "apps/desktop/src/chat/**", - "apps/desktop/src/chat_shortcuts/**", - "apps/desktop/src/composer/**", - "apps/desktop/src/contacts/**", - "apps/desktop/src/edit/**", - "apps/desktop/src/editor/**", - "apps/desktop/src/folders/**", - "apps/desktop/src/main.tsx", - "apps/desktop/src/main/**", - "apps/desktop/src/main2/**", - "apps/desktop/src/onboarding/**", - "apps/desktop/src/search/**", - "apps/desktop/src/services/**", - "apps/desktop/src/session/**", - "apps/desktop/src/settings/**", - "apps/desktop/src/shared/**", - "apps/desktop/src/sidebar/**", - "apps/desktop/src/store/zustand/**", - "apps/desktop/src/stt/**", - "apps/desktop/src/templates/template-body.tsx", - "apps/desktop/src/templates/template-form.tsx", - "apps/desktop/src/templates/utils.ts" - ], + "files": ["apps/desktop/src/main.tsx"], "rules": { "hypr/no-raw-tinybase": "off" } diff --git a/apps/desktop/src/calendar/components/apple/calendar-selection.tsx b/apps/desktop/src/calendar/components/apple/calendar-selection.tsx index b5e48181af..0b6849d29e 100644 --- a/apps/desktop/src/calendar/components/apple/calendar-selection.tsx +++ b/apps/desktop/src/calendar/components/apple/calendar-selection.tsx @@ -7,8 +7,11 @@ import { type CalendarItem, CalendarSelection, } from "~/calendar/components/calendar-selection"; +import { + useCalendarsByProvider, + useSetCalendarEnabled, +} from "~/calendar/hooks"; import { useMountEffect } from "~/shared/hooks/useMountEffect"; -import * as main from "~/store/tinybase/store/main"; const SUBSCRIBED_SOURCE_NAME = "Subscribed Calendars"; @@ -43,23 +46,19 @@ export function useAppleCalendarSelection() { const { cancelDebouncedSync, status, scheduleDebouncedSync, scheduleSync } = useSync(); - const store = main.UI.useStore(main.STORE_ID); - const calendars = main.UI.useTable("calendars", main.STORE_ID); + const calendars = useCalendarsByProvider("apple"); + const setCalendarEnabled = useSetCalendarEnabled(); const groups = useMemo((): CalendarGroup[] => { - const appleCalendars = Object.entries(calendars).filter( - ([_, cal]) => cal.provider === "apple", - ); - const grouped = new Map(); - for (const [id, cal] of appleCalendars) { + for (const cal of calendars) { const source = cal.source || "Apple Calendar"; if (!grouped.has(source)) grouped.set(source, []); grouped.get(source)!.push({ - id, + id: cal.id, title: cal.name || "Untitled", color: cal.color ?? "#888", - enabled: cal.enabled ?? false, + enabled: cal.enabled, }); } @@ -77,10 +76,10 @@ export function useAppleCalendarSelection() { const handleToggle = useCallback( (calendar: CalendarItem, enabled: boolean) => { - store?.setPartialRow("calendars", calendar.id, { enabled }); + setCalendarEnabled(calendar.id, enabled); scheduleDebouncedSync(); }, - [store, scheduleDebouncedSync], + [setCalendarEnabled, scheduleDebouncedSync], ); const handleRefresh = useCallback(() => { diff --git a/apps/desktop/src/calendar/components/event-chip.tsx b/apps/desktop/src/calendar/components/event-chip.tsx index 8036eec954..54c66172cd 100644 --- a/apps/desktop/src/calendar/components/event-chip.tsx +++ b/apps/desktop/src/calendar/components/event-chip.tsx @@ -10,11 +10,15 @@ import { } from "@hypr/ui/components/ui/popover"; import { cn } from "@hypr/utils"; -import { toTz, useCalendar, useTimezone } from "~/calendar/hooks"; +import { + toTz, + useCalendar, + useEvent, + useGetOrCreateSessionForEventId, + useTimelineEvent, + useTimezone, +} from "~/calendar/hooks"; import { EventDisplay } from "~/session/components/outer-header/metadata"; -import { useEvent } from "~/store/tinybase/hooks"; -import * as main from "~/store/tinybase/store/main"; -import { getOrCreateSessionForEventId } from "~/store/tinybase/store/sessions"; import { useTabs } from "~/store/zustand/tabs"; function useCalendarColor(calendarId: string | null): string | null { @@ -24,24 +28,18 @@ function useCalendarColor(calendarId: string | null): string | null { export function EventChip({ eventId }: { eventId: string }) { const tz = useTimezone(); - const event = main.UI.useResultRow( - main.QUERIES.timelineEvents, - eventId, - main.STORE_ID, - ); - const calendarColor = useCalendarColor( - (event?.calendar_id as string) ?? null, - ); + const event = useTimelineEvent(eventId); + const calendarColor = useCalendarColor(event?.calendar_id ?? null); if (!event || !event.title) { return null; } - const isAllDay = !!event.is_all_day; + const isAllDay = event.is_all_day; const color = calendarColor ?? "#888"; const startedAt = event.started_at - ? format(toTz(event.started_at as string, tz), "h:mm a") + ? format(toTz(event.started_at, tz), "h:mm a") : null; return ( @@ -55,7 +53,7 @@ export function EventChip({ eventId }: { eventId: string }) { ])} style={{ backgroundColor: color }} > - {event.title as string} + {event.title} ) : (