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..eba8aefd7f 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -24,9 +24,6 @@ { "files": [ "apps/desktop/src/store/tinybase/**", - "apps/desktop/src/**/hooks/**", - "apps/desktop/src/**/hooks.ts", - "apps/desktop/src/**/hooks.tsx", "apps/desktop/src/**/*.test.ts", "apps/desktop/src/**/*.test.tsx", "apps/desktop/src/**/*.spec.ts", @@ -38,35 +35,37 @@ }, { "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" + "apps/desktop/src/session/hooks/storage.ts", + "apps/desktop/src/session/hooks/internal.ts", + "apps/desktop/src/session/hooks/queries.ts", + "apps/desktop/src/session/hooks/sessions.ts", + "apps/desktop/src/session/hooks/participants.ts", + "apps/desktop/src/session/hooks/tags.ts", + "apps/desktop/src/session/hooks/search.ts", + "apps/desktop/src/session/hooks/transcripts.ts", + "apps/desktop/src/session/hooks/runtime.ts", + "apps/desktop/src/session/hooks/enhanced-notes.ts", + "apps/desktop/src/session/hooks/useEnhancedNotes.ts", + "apps/desktop/src/calendar/hooks.ts", + "apps/desktop/src/contacts/hooks.ts", + "apps/desktop/src/chat_shortcuts/hooks.ts", + "apps/desktop/src/chat/hooks/useCreateChatMessage.ts", + "apps/desktop/src/chat/hooks/use-chat-actions.ts", + "apps/desktop/src/chat/hooks/persisted-messages.ts", + "apps/desktop/src/chat/hooks/chat-messages.ts", + "apps/desktop/src/chat/hooks/chat-groups.ts", + "apps/desktop/src/settings/memory/hooks.ts", + "apps/desktop/src/tasks/hooks.tsx" ], "rules": { "hypr/no-raw-tinybase": "off" } + }, + { + "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..8acf780df3 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, + 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 { useGetOrCreateSessionForEvent } from "~/session/hooks/runtime"; 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?.calendarId ?? null); if (!event || !event.title) { return null; } - const isAllDay = !!event.is_all_day; + const isAllDay = event.isAllDay; const color = calendarColor ?? "#888"; - const startedAt = event.started_at - ? format(toTz(event.started_at as string, tz), "h:mm a") + const startedAt = event.startedAt + ? format(toTz(event.startedAt, 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} ) : ( @@ -361,20 +224,12 @@ export function DetailsColumn({ } function EditablePersonNameField({ personId }: { personId: string }) { - const value = main.UI.useCell("humans", personId, "name", main.STORE_ID); - - const handleChange = main.UI.useSetCellCallback( - "humans", - personId, - "name", - (e: React.ChangeEvent) => e.target.value, - [], - main.STORE_ID, - ); + const value = useHumanCell(personId, "name"); + const handleChange = useUpdateHumanStringCell(personId, "name"); return ( ) => e.target.value, - [], - main.STORE_ID, - ); + const value = useHumanCell(personId, "job_title"); + const handleChange = useUpdateHumanStringCell(personId, "job_title"); return (
Job Title
) => e.target.value, - [], - main.STORE_ID, - ); + const value = useHumanCell(personId, "email"); + const handleChange = useUpdateHumanStringCell(personId, "email"); return (
@@ -427,7 +266,7 @@ function EditablePersonEmailField({ personId }: { personId: string }) {
) => e.target.value, - [], - main.STORE_ID, - ); + const value = useHumanCell(personId, "linkedin_username"); + const handleChange = useUpdateHumanStringCell(personId, "linkedin_username"); return (
LinkedIn
) => e.target.value, - [], - main.STORE_ID, - ); + const value = useHumanCell(personId, "memo"); + const handleChange = useUpdateHumanStringCell(personId, "memo"); return (
Notes