Conversation
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughRefactors AI settings into separate LLM and STT modules, adds provider configuration UIs and model comboboxes, implements STT model download task + progress callbacks, migrates TinyBase synchronizer to broadcast-channel, introduces React Query provider, updates chat transport to accept injected LanguageModel, and performs wide UI library and dependency changes. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant UI as Settings UI
participant Select as ProviderSelect
participant ModelCB as ModelCombobox
participant Store
participant API as OpenAI/Anthropic
User->>UI: open LLM settings
UI->>Select: render provider/model selectors
Select->>Store: read provider config
Select->>ModelCB: request models (baseUrl, apiKey)
ModelCB->>API: fetch listModels(...)
API-->>ModelCB: return model list
ModelCB-->>Select: show options
User->>Select: choose provider/model
Select->>Store: update current_llm_provider/current_llm_model
sequenceDiagram
participant User
participant STTUI as STT Configure UI
participant TaskMgr as TaskManager
participant LocalSTT as localSttCommands
participant Channel as ProgressChannel
participant UI as Download UI
User->>STTUI: click "Download model"
STTUI->>TaskMgr: schedule DOWNLOAD_MODEL_TASK(model)
TaskMgr->>Channel: create Channel<number>
TaskMgr->>LocalSTT: downloadModel(model, channel)
LocalSTT->>Channel: emit progress events
Channel->>TaskMgr: deliver progress
TaskMgr->>UI: invoke registered progress callbacks
UI-->>User: update progress bar/status
LocalSTT-->>TaskMgr: complete
TaskMgr->>UI: mark model downloaded
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (2 warnings, 1 inconclusive)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (4)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/ui/src/components/ui/select.tsx (1)
75-82: Likely scroll behavior bug: overflow applied to Content instead of Viewport.Radix Select expects the Viewport to be the scroll container. Putting overflow on Content can break ScrollUp/DownButton visibility and create nested scrollbars.
Apply this minimal fix:
- "relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-select-content-transform-origin]", + "relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-select-content-transform-origin]",Optional: if you want the list itself to scroll, keep overflow on Viewport (default Radix behavior handles it without extra classes).
🧹 Nitpick comments (16)
packages/ui/src/components/ui/input-group.tsx (1)
17-17: Consider removing "what" comments.The inline comments describe what the code does rather than why. Per coding guidelines, comments should explain "why" not "what". While these comments do help organize the long className strings, consider whether they're necessary.
Based on coding guidelines.
Also applies to: 22-22, 24-24
packages/ui/src/components/ui/command.tsx (1)
28-28: Input height conflict (h-12 vs h-10) — pick one to avoid cascade race.Parent forces [&_[cmdk-input]]:h-12 while the input sets h-10. Tailwind order can make this unpredictable. Align both to h-10 (or both to h-12).
Minimal diff to align on h-10:
- <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"> + <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-10 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">Also applies to: 45-46
packages/ui/src/components/ui/dialog.tsx (1)
109-120: Re‑export DialogProps from this module to decouple consumers from Radix.Let consumers import the prop type from here instead of @radix-ui/react-dialog.
Add:
import * as DialogPrimitive from "@radix-ui/react-dialog"; @@ export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, }; + +// Optional: re-export prop type for consumers +export type DialogProps = React.ComponentPropsWithoutRef<typeof DialogPrimitive.Root>;Then update consumers:
- import { type DialogProps } from "@radix-ui/react-dialog"; + import type { DialogProps } from "@hypr/ui/components/ui/dialog";packages/ui/src/components/ui/button.tsx (2)
39-47: Set a safe defaulttype="button"(avoid accidental form submit).Without an explicit type, native buttons default to “submit”. Provide a default only when rendering a real button.
- <Comp - className={cn(buttonVariants({ variant, size }), className)} - ref={ref} - {...props} - /> + <Comp + className={cn(buttonVariants({ variant, size }), className)} + type={asChild ? undefined : (props.type ?? "button")} + ref={ref} + {...props} + />
43-47: Preserve disabled semantics whenasChildis true.Non-button children (e.g., ) ignore
disabled. Mirror it toaria-disabledfor a11y.<Comp className={cn(buttonVariants({ variant, size }), className)} + aria-disabled={asChild ? props.disabled : undefined} type={asChild ? undefined : (props.type ?? "button")} ref={ref} {...props} />packages/ui/src/components/ui/select.tsx (2)
118-120: Avoid potential clipping: align ItemIndicator container and icon sizes.Container is 14px (h/w 3.5), icon is 16px (h/w 4). This can crop on some renderers.
Either shrink the icon or grow the container. Example (grow container):
- <span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center"> + <span className="absolute right-2 flex h-4 w-4 items-center justify-center"> <SelectPrimitive.ItemIndicator> - <Check className="h-4 w-4" /> + <Check className="h-4 w-4" /> </SelectPrimitive.ItemIndicator> </span>Also applies to: 123-126
5-5: Standardize to relative import for same-package utility imports.The codebase has mixed patterns for importing
cnfromlib/utilswithin packages/ui—some files use the@hypr/uialias, others use relative paths. Relative imports like../../lib/utilsare preferable for same-package imports to avoid potential bundler resolution issues.-import { cn } from "@hypr/ui/lib/utils"; +import { cn } from "../../lib/utils";Note: Similar components in the same directory already use relative imports (e.g.,
tooltip.tsx,spinner.tsx).apps/desktop2/src/components/settings/ai/index.tsx (1)
12-21: Tighten onValueChange typing; avoid castCast can be dropped with an explicitly typed handler.
- <Tabs value={activeTab} onValueChange={(v) => setActiveTab(v as typeof activeTab)} className="w-full"> + <Tabs + value={activeTab} + onValueChange={(v: "transcription" | "intelligence") => setActiveTab(v)} + className="w-full" + >apps/desktop2/src/main.tsx (1)
24-24: Consider sane React Query defaults to reduce background churnUnless you rely on the library defaults, set explicit defaults to avoid surprising refetches and heavy retries in a desktop app.
-const queryClient = new QueryClient(); +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 60_000, + retry: 1, + refetchOnWindowFocus: false, + }, + }, +});apps/desktop2/package.json (1)
17-25: New AI SDK deps — watch binary size, consider code‑splittingDesktop (Tauri) bundles can bloat quickly. If only some providers are used at runtime, prefer dynamic imports for provider‑specific clients and keep them out of the default chunk. Also ensure tree‑shaking works with ESM builds.
Also applies to: 36-46, 55-57, 79-83
apps/desktop2/src/components/settings/ai/llm/configure.tsx (2)
80-101: Let form defaults drive base_url; don’t override with Field defaultValueRely on form defaultValues so hidden/immutable fields don’t drift.
- <form.Field name="base_url" defaultValue={providerConfig.baseUrl.value}> + <form.Field name="base_url"> {(field) => ( <FormField field={field} hidden={providerConfig.baseUrl.immutable} label="Base URL" icon="mdi:web" /> )} </form.Field>
108-121: Provider help copy“Hyprnote is great” reads like placeholder. Consider a one‑liner linking to provider docs or API dashboard.
apps/desktop2/src/store/tinybase/internal.ts (1)
19-23: Consider defaults for new model fields.
current_llm_modelandcurrent_stt_modelhave no defaults. IfgeneralSchemais ever used to parse existing persisted values, these can fail validation. Defaulting to empty string aligns with UI defaults.- current_llm_provider: z.string().default("hyprnote"), - current_llm_model: z.string(), - current_stt_provider: z.string().default("hyprnote"), - current_stt_model: z.string(), + current_llm_provider: z.string().default("hyprnote"), + current_llm_model: z.string().default(""), + current_stt_provider: z.string().default("hyprnote"), + current_stt_model: z.string().default(""),apps/desktop2/src/components/task-manager.tsx (1)
13-21: Type the progress-callback map by model type for clarity.Keeps key type aligned with the API and improves IntelliSense; no behavior change.
-const downloadProgressCallbacks = new Map<string, (progress: number) => void>(); +const downloadProgressCallbacks = new Map<SupportedSttModel, (progress: number) => void>();Also applies to: 32-48
apps/desktop2/src/components/settings/ai/stt/configure.tsx (1)
12-14: Minor import duplication.
aiProviderSchemais imported directly and available oninternal. Prefer one source to avoid drift.-import * as internal from "../../../../store/tinybase/internal"; -import { aiProviderSchema } from "../../../../store/tinybase/internal"; +import * as internal from "../../../../store/tinybase/internal"; @@ - validators: { onChange: aiProviderSchema }, + validators: { onChange: internal.aiProviderSchema },Also applies to: 56-58
apps/desktop2/src/components/settings/ai/shared/model-combobox.tsx (1)
51-51: ConsidertoLowerCase()overtoLocaleLowerCase()for model names.Model names are typically alphanumeric English strings, so locale-aware comparison adds unnecessary overhead. Using
toLowerCase()is more common and performs better for this use case.Apply this diff:
- const hasExactMatch = options.some((option) => option.toLocaleLowerCase() === trimmedQuery.toLocaleLowerCase()); + const hasExactMatch = options.some((option) => option.toLowerCase() === trimmedQuery.toLowerCase());And in the filter function:
filter={(value, search) => { - const v = value.toLocaleLowerCase(); - const s = search.toLocaleLowerCase(); + const v = value.toLowerCase(); + const s = search.toLowerCase(); if (v.includes(s)) { return 1; } return 0; }}Also applies to: 84-85
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
apps/desktop2/public/assets/icon.pngis excluded by!**/*.pngpnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (37)
apps/desktop2/package.json(4 hunks)apps/desktop2/src/components/settings/ai/index.tsx(2 hunks)apps/desktop2/src/components/settings/ai/llm.tsx(0 hunks)apps/desktop2/src/components/settings/ai/llm/configure.tsx(1 hunks)apps/desktop2/src/components/settings/ai/llm/index.tsx(1 hunks)apps/desktop2/src/components/settings/ai/llm/select.tsx(1 hunks)apps/desktop2/src/components/settings/ai/llm/shared.tsx(1 hunks)apps/desktop2/src/components/settings/ai/shared.tsx(0 hunks)apps/desktop2/src/components/settings/ai/shared/index.tsx(1 hunks)apps/desktop2/src/components/settings/ai/shared/model-combobox.tsx(1 hunks)apps/desktop2/src/components/settings/ai/stt.tsx(0 hunks)apps/desktop2/src/components/settings/ai/stt/configure.tsx(1 hunks)apps/desktop2/src/components/settings/ai/stt/index.tsx(1 hunks)apps/desktop2/src/components/settings/ai/stt/select.tsx(1 hunks)apps/desktop2/src/components/settings/ai/stt/shared.tsx(1 hunks)apps/desktop2/src/components/task-manager.tsx(2 hunks)apps/desktop2/src/main.tsx(3 hunks)apps/desktop2/src/routes/app/settings/_layout.tsx(5 hunks)apps/desktop2/src/store/tinybase/internal.ts(2 hunks)apps/pro/package.json(1 hunks)apps/web/package.json(2 hunks)package.json(1 hunks)packages/db/package.json(1 hunks)packages/tiptap/package.json(2 hunks)packages/ui/package.json(1 hunks)packages/ui/src/components/ui/accordion.tsx(3 hunks)packages/ui/src/components/ui/button.tsx(1 hunks)packages/ui/src/components/ui/command.tsx(4 hunks)packages/ui/src/components/ui/dialog.tsx(1 hunks)packages/ui/src/components/ui/input-group.tsx(1 hunks)packages/ui/src/components/ui/input.tsx(1 hunks)packages/ui/src/components/ui/popover.tsx(1 hunks)packages/ui/src/components/ui/select.tsx(7 hunks)packages/ui/src/components/ui/tabs.tsx(3 hunks)packages/ui/src/components/ui/textarea.tsx(2 hunks)packages/ui/tailwind.config.ts(1 hunks)packages/utils/package.json(1 hunks)
💤 Files with no reviewable changes (3)
- apps/desktop2/src/components/settings/ai/llm.tsx
- apps/desktop2/src/components/settings/ai/stt.tsx
- apps/desktop2/src/components/settings/ai/shared.tsx
🧰 Additional context used
📓 Path-based instructions (2)
apps/desktop2/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/desktop2/.cursor/rules/style.mdc)
apps/desktop2/**/*.{tsx,jsx}: When there are many Tailwind classNames with conditional logic, use the cn utility imported asimport { cn } from "@hypr/ui/lib/utils"
Always pass an array to cn when composing Tailwind classNames
Split cn array entries by logical grouping when composing Tailwind classNames
Files:
apps/desktop2/src/components/settings/ai/llm/index.tsxapps/desktop2/src/main.tsxapps/desktop2/src/routes/app/settings/_layout.tsxapps/desktop2/src/components/settings/ai/stt/select.tsxapps/desktop2/src/components/settings/ai/llm/select.tsxapps/desktop2/src/components/settings/ai/llm/shared.tsxapps/desktop2/src/components/settings/ai/stt/shared.tsxapps/desktop2/src/components/settings/ai/index.tsxapps/desktop2/src/components/settings/ai/shared/model-combobox.tsxapps/desktop2/src/components/settings/ai/llm/configure.tsxapps/desktop2/src/components/settings/ai/shared/index.tsxapps/desktop2/src/components/settings/ai/stt/index.tsxapps/desktop2/src/components/settings/ai/stt/configure.tsxapps/desktop2/src/components/task-manager.tsx
**/*.{js,ts,tsx,rs}
⚙️ CodeRabbit configuration file
**/*.{js,ts,tsx,rs}: 1. Do not add any error handling. Keep the existing one.
2. No unused imports, variables, or functions.
3. For comments, keep it minimal. It should be about "Why", not "What".
Files:
apps/desktop2/src/components/settings/ai/llm/index.tsxpackages/ui/src/components/ui/textarea.tsxpackages/ui/src/components/ui/input-group.tsxapps/desktop2/src/main.tsxapps/desktop2/src/routes/app/settings/_layout.tsxpackages/ui/src/components/ui/accordion.tsxapps/desktop2/src/components/settings/ai/stt/select.tsxpackages/ui/tailwind.config.tsapps/desktop2/src/components/settings/ai/llm/select.tsxapps/desktop2/src/components/settings/ai/llm/shared.tsxapps/desktop2/src/store/tinybase/internal.tspackages/ui/src/components/ui/button.tsxapps/desktop2/src/components/settings/ai/stt/shared.tsxapps/desktop2/src/components/settings/ai/index.tsxapps/desktop2/src/components/settings/ai/shared/model-combobox.tsxapps/desktop2/src/components/settings/ai/llm/configure.tsxapps/desktop2/src/components/settings/ai/shared/index.tsxapps/desktop2/src/components/settings/ai/stt/index.tsxapps/desktop2/src/components/settings/ai/stt/configure.tsxapps/desktop2/src/components/task-manager.tsxpackages/ui/src/components/ui/tabs.tsxpackages/ui/src/components/ui/input.tsxpackages/ui/src/components/ui/dialog.tsxpackages/ui/src/components/ui/popover.tsxpackages/ui/src/components/ui/command.tsxpackages/ui/src/components/ui/select.tsx
🧠 Learnings (1)
📚 Learning: 2025-10-12T11:14:50.462Z
Learnt from: CR
PR: fastrepl/hyprnote#0
File: apps/desktop2/.cursor/rules/style.mdc:0-0
Timestamp: 2025-10-12T11:14:50.462Z
Learning: Applies to apps/desktop2/**/*.{tsx,jsx} : When there are many Tailwind classNames with conditional logic, use the cn utility imported as `import { cn } from "hypr/ui/lib/utils"`
Applied to files:
packages/ui/src/components/ui/textarea.tsxapps/desktop2/src/routes/app/settings/_layout.tsxpackages/ui/src/components/ui/accordion.tsxpackages/ui/src/components/ui/tabs.tsxpackages/ui/src/components/ui/input.tsx
🧬 Code graph analysis (20)
apps/desktop2/src/components/settings/ai/llm/index.tsx (2)
apps/desktop2/src/components/settings/ai/llm/select.tsx (1)
SelectProviderAndModel(9-110)apps/desktop2/src/components/settings/ai/llm/configure.tsx (1)
ConfigureProviders(11-28)
packages/ui/src/components/ui/input-group.tsx (1)
packages/ui/src/lib/utils.ts (1)
cn(4-6)
apps/desktop2/src/routes/app/settings/_layout.tsx (1)
packages/ui/src/lib/utils.ts (1)
cn(4-6)
apps/desktop2/src/components/settings/ai/stt/select.tsx (3)
apps/desktop2/src/components/settings/ai/llm/select.tsx (1)
SelectProviderAndModel(9-110)packages/ui/src/lib/utils.ts (1)
cn(4-6)apps/desktop2/src/components/settings/ai/stt/shared.tsx (3)
PROVIDERS(10-54)ProviderId(8-8)sttModelQueries(56-69)
apps/desktop2/src/components/settings/ai/llm/select.tsx (4)
apps/desktop2/src/components/settings/ai/stt/select.tsx (1)
SelectProviderAndModel(9-105)packages/ui/src/lib/utils.ts (1)
cn(4-6)apps/desktop2/src/components/settings/ai/llm/shared.tsx (1)
PROVIDERS(5-46)apps/desktop2/src/components/settings/ai/shared/model-combobox.tsx (2)
openaiCompatibleListModels(178-211)ModelCombobox(17-176)
apps/desktop2/src/components/settings/ai/llm/shared.tsx (1)
apps/desktop2/src/components/settings/ai/stt/shared.tsx (2)
ProviderId(8-8)PROVIDERS(10-54)
packages/ui/src/components/ui/button.tsx (1)
packages/ui/src/lib/utils.ts (1)
cn(4-6)
apps/desktop2/src/components/settings/ai/stt/shared.tsx (2)
apps/desktop2/src/components/settings/ai/llm/shared.tsx (2)
ProviderId(3-3)PROVIDERS(5-46)plugins/local-stt/js/bindings.gen.ts (1)
SupportedSttModel(88-88)
apps/desktop2/src/components/settings/ai/index.tsx (2)
apps/desktop2/src/components/settings/ai/stt/index.tsx (1)
STT(4-11)apps/desktop2/src/components/settings/ai/llm/index.tsx (1)
LLM(4-11)
apps/desktop2/src/components/settings/ai/shared/model-combobox.tsx (2)
apps/desktop2/src/hooks/useQuery.ts (1)
useQuery(3-46)packages/ui/src/lib/utils.ts (1)
cn(4-6)
apps/desktop2/src/components/settings/ai/llm/configure.tsx (4)
apps/desktop2/src/components/settings/ai/llm/shared.tsx (2)
PROVIDERS(5-46)ProviderId(3-3)apps/desktop2/src/components/settings/ai/shared/index.tsx (2)
useProvider(9-21)FormField(23-70)apps/desktop2/src/store/tinybase/internal.ts (2)
AIProvider(31-31)aiProviderSchema(25-29)packages/ui/src/lib/utils.ts (1)
cn(4-6)
apps/desktop2/src/components/settings/ai/shared/index.tsx (1)
apps/desktop2/src/store/tinybase/internal.ts (1)
AIProvider(31-31)
apps/desktop2/src/components/settings/ai/stt/index.tsx (2)
apps/desktop2/src/components/settings/ai/stt/select.tsx (1)
SelectProviderAndModel(9-105)apps/desktop2/src/components/settings/ai/stt/configure.tsx (1)
ConfigureProviders(22-43)
apps/desktop2/src/components/settings/ai/stt/configure.tsx (6)
apps/desktop2/src/components/settings/ai/stt/shared.tsx (3)
PROVIDERS(10-54)ProviderId(8-8)sttModelQueries(56-69)plugins/local-stt/js/bindings.gen.ts (2)
setProvider(61-63)SupportedSttModel(88-88)apps/desktop2/src/components/settings/ai/shared/index.tsx (2)
useProvider(9-21)FormField(23-70)apps/desktop2/src/store/tinybase/internal.ts (2)
AIProvider(31-31)aiProviderSchema(25-29)apps/desktop2/src/hooks/useQuery.ts (1)
useQuery(3-46)apps/desktop2/src/components/task-manager.tsx (3)
registerDownloadProgressCallback(15-17)unregisterDownloadProgressCallback(19-21)DOWNLOAD_MODEL_TASK_ID(11-11)
apps/desktop2/src/components/task-manager.tsx (1)
plugins/local-stt/js/bindings.gen.ts (1)
SupportedSttModel(88-88)
packages/ui/src/components/ui/input.tsx (1)
packages/ui/src/lib/utils.ts (1)
cn(4-6)
packages/ui/src/components/ui/dialog.tsx (1)
packages/ui/src/lib/utils.ts (1)
cn(4-6)
packages/ui/src/components/ui/popover.tsx (1)
packages/ui/src/lib/utils.ts (1)
cn(4-6)
packages/ui/src/components/ui/command.tsx (1)
packages/ui/src/components/ui/dialog.tsx (2)
Dialog(110-110)DialogContent(112-112)
packages/ui/src/components/ui/select.tsx (1)
packages/ui/src/lib/utils.ts (1)
cn(4-6)
🔇 Additional comments (52)
packages/ui/src/components/ui/accordion.tsx (3)
5-5: LGTM: Import path refactored to use public package.The import path update from relative to the public package path (
@hypr/ui/lib/utils) aligns with the PR's modular restructuring objectives.
21-38: LGTM: Styling improvements enhance visual hierarchy.The additions of
text-smandtext-lefton the trigger button andtext-muted-foregroundon the ChevronDown icon provide better text sizing, alignment, and visual hierarchy without affecting functionality.
41-52: LGTM: Transition handling refined.Removing
transition-allis appropriate since the accordion animations are explicitly controlled by theanimate-accordion-upandanimate-accordion-downclasses. This makes the animation behavior more predictable and avoids unintended transitions on other properties.packages/ui/src/components/ui/textarea.tsx (1)
3-12: LGTM! Consistent design system updates.The import centralization and styling token updates align with the broader UI package refactoring. The changes improve visual consistency across components while maintaining the same functional behavior.
packages/ui/src/components/ui/input.tsx (1)
3-11: LGTM! Well-coordinated styling refinements.The import path update and styling changes mirror the updates in textarea.tsx and tabs.tsx, creating a cohesive design system. The addition of
transition-colorsenhances the user experience with smooth state transitions.packages/ui/src/components/ui/tabs.tsx (3)
4-4: LGTM! Import centralization is consistent.The cn import update matches the pattern across all UI components in this PR.
15-15: LGTM! TabsList styling updates enhance visual hierarchy.The height reduction and rounded-lg border create a more refined appearance while maintaining proper visual hierarchy with the TabsTrigger component.
30-30: LGTM! TabsTrigger styling updates improve consistency.The padding and border radius adjustments align with the broader design system updates, and the active state styling provides clear visual feedback.
packages/ui/src/components/ui/command.tsx (2)
24-33: Nice consolidation onto Dialog primitives.Switching CommandDialog to Radix/Hypr Dialog unifies focus management and accessibility across the UI.
6-7: Path alias resolution verified—import is correct.The tsconfig path mapping
@hypr/ui/*→./src/*is properly configured inpackages/ui/tsconfig.json. The importfrom "@hypr/ui/components/ui/dialog"correctly resolves topackages/ui/src/components/ui/dialog.tsx, which exists. The import will resolve correctly outside the ui package.packages/ui/src/components/ui/dialog.tsx (1)
30-51: LGTM — Content/Overlay composition and a11y affordances look solid.Portal + Overlay + Close affordances align with Radix patterns; classes are consistent with the design tokens.
packages/ui/src/components/ui/popover.tsx (3)
4-4: No changes needed for deep import path.The import path
@hypr/ui/lib/utilsis already established and widely used across 40+ files in the codebase. The package exports properly map"./lib/*": "./src/lib/*.ts"and tsconfig paths are configured correctly. This is not a new or risky pattern—it's the established standard throughout the repository.Likely an incorrect or invalid review comment.
31-31: Exports verified: PopoverAnchor addition is safeAll 8 consumers across the codebase import only
{ Popover, PopoverContent, PopoverTrigger }, which remain in the export list. PopoverAnchor is a new export with no existing imports, so this change introduces no breaking changes.
14-28: The review comment is incorrect—these props were never implemented in this codebase.The search found zero matches for
showArrow,animationDuration, orcloseOnClickOutsideacross the entire codebase, and the actualpopover.tsxfile shows thatPopoverContentonly acceptsclassName,align,sideOffset, and standard Radix UI props via...props. These three props are not standard Radix UI popover properties and were never part of this wrapper implementation, so there is no breaking API change here.The suggestion to export
PopoverArrow(a real Radix component) is a separate enhancement unrelated to prop removal.Likely an incorrect or invalid review comment.
packages/ui/src/components/ui/button.tsx (3)
7-31: CVA setup looks solid.Variants/sizes are coherent; base classes cover focus-visible and icon sizing. Exporting
buttonVariantsis useful for composition.If you want parity with other primitives, consider adding ring offset classes for better focus contrast:
focus-visible:ring-offset-2 ring-offset-background. Optional.
33-37: Review comment is incorrect and should be disregarded.Verification findings:
isLoading prop: No usages found in the codebase. No breaking change.
Variant drift: All Button variants used in the codebase (
default,destructive,outline,secondary,ghost) are within the supported list. No breaking change.Size drift: The
size="xs"inpackages/ui/src/components/ui/input-group.tsx:94is NOT passed to the Button component. It belongs toinputGroupButtonVariants, a separate CVA used by theInputGroupButtonwrapper component. The Button type signature explicitly omits size:Omit<React.ComponentProps<typeof Button>, "size">. No breaking change.The ButtonProps changes do not introduce the breaking changes described in the review comment.
Likely an incorrect or invalid review comment.
5-5: The review comment is factually incorrect.The import
@hypr/ui/lib/utilsis valid and explicitly supported by the package.json exports ("./lib/*": "./src/lib/*.ts"). The package.json does not break with this subpath import—it permits it.Additionally, the suggestion to use
@/lib/utilsis not viable: the packages/ui tsconfig.json only defines"@hypr/ui/*", not@/*(the latter exists in apps/web). This path would fail to resolve.The codebase does have an inconsistency: some components use relative paths (
../../lib/utils) while others use the full package path (@hypr/ui/lib/utils). However, both patterns are technically supported. Standardizing on one approach is optional, not a bug.Likely an incorrect or invalid review comment.
packages/ui/src/components/ui/select.tsx (6)
9-11: Re-exports look good.Keeping SelectGroup and SelectValue as aliases maintains Radix parity and keeps the surface consistent.
19-22: Trigger classes and icon sizing look solid.Utility classes are sane; ChevronDown at 16px aligns with text scale.
Also applies to: 27-27
45-46: Scroll button icon sizing LGTM.Consistent 16px chevrons fit typical density.
Also applies to: 62-63
105-106: Label weight change is fine.font-semibold improves group separation without affecting layout.
15-16: SelectTrigger breaking change verified—all call sites already compliant.No usages of SelectTrigger with
sizeorvariantprops found. All 5 call sites in the codebase already rely onclassNamefor styling, confirming the breaking change has been properly handled.
145-156: Package already exposes SelectScrollDownButton/SelectScrollUpButton publicly via wildcard exports.The concern in the review assumes a barrel file structure that doesn't exist in this codebase. The package.json uses an explicit wildcard pattern (
"./components/*": "./src/components/*.tsx") that directly exposes individual component files. The new exports are properly accessible to consumers via@hypr/ui/components/ui/selectwithout requiring barrel re-exports.Likely an incorrect or invalid review comment.
packages/db/package.json (1)
23-23: LGTM: Routine type definitions update.Patch-level bump for Node.js type definitions.
packages/ui/package.json (1)
52-52: LGTM: Routine type definitions update.Patch-level bump for Node.js type definitions.
packages/utils/package.json (1)
15-15: LGTM: AI SDK patch update.Aligns with the PR's AI provider integration updates.
packages/tiptap/package.json (2)
22-22: LGTM: TanStack Router patch update.Aligns with router upgrades across the monorepo.
51-51: LGTM: Routine type definitions update.Patch-level bump for Node.js type definitions.
apps/pro/package.json (2)
17-17: LGTM: Hono framework patch update.Routine patch-level update.
22-22: LGTM: Routine type definitions update.Patch-level bump for Node.js type definitions.
apps/web/package.json (4)
16-16: LGTM: Supabase client patch update.Routine patch-level update.
19-26: LGTM: Coordinated TanStack ecosystem updates.All TanStack packages updated to align with version 1.133.13 across the monorepo.
40-40: LGTM: Routine type definitions update.Patch-level bump for Node.js type definitions.
44-44: LGTM: jsdom patch update.Routine patch-level update for the test environment DOM implementation.
package.json (1)
7-7: LGTM: AI SDK React patch update.Aligns with the PR's AI provider integration updates.
apps/desktop2/src/components/settings/ai/llm/index.tsx (1)
1-11: LGTM: Clean composition pattern.The component follows a clear separation of concerns by composing
SelectProviderAndModelandConfigureProviders. The structure is straightforward and adheres to coding guidelines.apps/desktop2/src/components/settings/ai/stt/index.tsx (1)
4-11: Confirm model persistence from SelectProviderAndModelSelectProviderAndModel’s onSubmit (per stt/select.tsx) updates only current_stt_provider. If model selection should persist too, wire it here or in the select component to avoid losing user intent.
apps/desktop2/src/components/settings/ai/index.tsx (1)
13-29: LGTM — clear tab UI with iconsThe tab structure and icons look good and consistent with the LLM/STT pages.
apps/desktop2/src/routes/app/settings/_layout.tsx (2)
45-99: LGTM — consistent cn([...]) usage and spacing tweaksGood adherence to cn([...]) guidance and improved density without functional changes.
Please sanity‑check scrolling and focus behavior after the padding/margin reductions on small window heights.
118-141: LGTM — tab button stylesTypographic and active/hover states read well; accessible focus should be preserved by default button focus.
apps/desktop2/src/main.tsx (1)
62-71: LGTM — provider stack orderQueryClientProvider -> TinyTickProvider -> TinyBaseProvider looks correct; all children get Query context.
apps/desktop2/src/components/settings/ai/llm/shared.tsx (1)
31-45: Verify apiKey requirements and defaults for local providersOllama and LM Studio typically don’t require API keys and commonly run on localhost ports (Ollama 11434, LM Studio often 1234). Confirm apiKey: true and port 8000 are intentional. If not, flip apiKey to false and adjust defaults.
apps/desktop2/src/components/settings/ai/llm/select.tsx (1)
42-46: cn usage LGTM.Array-form cn composition and conditional grouping are consistent with guidelines.
apps/desktop2/src/components/settings/ai/stt/select.tsx (1)
33-37: cn usage and model dropdown logic look good.Provider-state styling and model disabled-state handling are sound.
Please ensure
PROVIDERS’smodelslist stays in sync with any backend-supported options.Also applies to: 82-99
apps/desktop2/src/components/task-manager.tsx (1)
32-48: Download task wiring LGTM.Channel plumbing and optional progress callback are correct; no blocking issues.
apps/desktop2/src/components/settings/ai/stt/configure.tsx (1)
140-147: STT model download UI flow LGTM.Download state, progress subscription, and task scheduling are cohesive and responsive.
Also applies to: 181-268
apps/desktop2/src/components/settings/ai/shared/model-combobox.tsx (2)
178-211: LGTM!The
openaiCompatibleListModelsfunction handles various response formats gracefully, returns empty arrays on errors (which works well for the UI), and includes defensive fallbacks for unexpected data shapes.
132-135: LGTM!All
cnutility calls properly use arrays as per the coding guidelines for this path pattern.Also applies to: 138-141, 161-164
apps/desktop2/src/components/settings/ai/shared/index.tsx (1)
23-70: LGTM!The
FormFieldcomponent handles error states properly, supports accessibility witharia-invalid, and provides a clean, reusable form field UI with appropriate error messaging.apps/desktop2/src/components/settings/ai/stt/shared.tsx (3)
29-35: Verify baseUrl for "deepgram-custom" provider.The "Deepgram (Custom)" provider has
baseUrlset to"https://api.openai.com/v1"(line 33), which appears incorrect for a Deepgram provider. This could be:
- A copy-paste error that should point to a Deepgram endpoint
- An intentional placeholder since
immutable: falseallows users to change itPlease verify this is intentional. If it's a placeholder, consider using an empty string or a Deepgram URL instead to avoid confusion.
56-69: LGTM!The
sttModelQuerieshelpers use appropriate refetch intervals: faster polling (500ms) for active downloads and slower (1500ms) for checking download completion status. This is a sensible optimization.
10-54: LGTM!The
PROVIDERSarray is well-structured with consistent shape, proper type safety viaas const, and clear disabled flags for providers not yet ready. The icon handling with JSX elements is appropriate.
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/desktop2/src/store/tinybase/persisted.ts (1)
230-254: Hook order: register listener after the store is provideduseDidFinishTransactionListener uses STORE_ID lookup; calling it before useProvideStore likely binds to a missing store and can no-op or throw depending on implementation. Create and provide the store first, then register listeners.
Suggested steps:
- Move the store creation block (Lines 255-260) above the listener.
- Call useProvideStore(STORE_ID, store) immediately after creation.
- Then register useDidFinishTransactionListener, persister, synchronizer, relationships, queries, indexes, metrics, and providers.
If helpful, I can draft a precise reorder diff; let me know.
Also applies to: 484-491
♻️ Duplicate comments (2)
apps/desktop2/src/components/settings/ai/shared/model-combobox.tsx (1)
33-37: Unstable queryKey defeats React Query caching.As flagged in the previous review, the
queryKeyincludes thelistModelsfunction reference (line 34), which changes identity on every render if the parent passes an inline function or recreates it. This breaks React Query's caching mechanism and causes unnecessary refetches.Please implement one of the solutions from the previous review:
- Accept a stable
queryKeyprop from the parent, or- Require the parent to pass a memoized
listModelsfunction (viauseMemo/useCallback)apps/desktop2/src/components/settings/ai/llm/configure.tsx (1)
43-64: Form defaults + schema break autosave for keyless/immutable providers (Hyprnote) — fix defaults and dynamic schema.Same issue as previously flagged; still present. Initialize from provider config and relax
api_keywhen not required.@@ -import { useForm } from "@tanstack/react-form"; +import { useForm } from "@tanstack/react-form"; +import { z } from "zod"; @@ - const form = useForm({ - onSubmit: ({ value }) => setProvider(value), - defaultValues: provider - ?? ({ - type: "llm", - base_url: "", - api_key: "", - } satisfies internal.AIProvider), - listeners: { + const schema = providerConfig.apiKey + ? aiProviderSchema + : aiProviderSchema.extend({ api_key: z.string().min(1).optional() }); + + const defaultValues = + provider ?? + ({ + type: "llm", + base_url: providerConfig.baseUrl.value, + ...(providerConfig.apiKey ? { api_key: "" } : {}), + } as Partial<internal.AIProvider>); + + const form = useForm({ + onSubmit: ({ value }) => setProvider(value), + defaultValues, + listeners: { onChange: ({ formApi }) => { queueMicrotask(() => { const { form: { errors } } = formApi.getAllErrors(); if (errors.length > 0) { - console.log(errors); + // optionally surface inline; avoid logging secrets } formApi.handleSubmit(); }); }, }, - validators: { onChange: aiProviderSchema }, + validators: { onChange: schema }, });Also applies to: 1-10
🧹 Nitpick comments (18)
apps/desktop2/src-tauri/capabilities/default.json (1)
28-33: Consider consolidating redundant path patterns.Both
$APPDATA/*and$APPDATA/**are specified (likewise for $DOWNLOAD). In Tauri, the**pattern recursively matches all nested paths and implicitly covers the*(single-level) case. To reduce redundancy, you could retain only the**variants.This is a minor clarification and doesn't affect functionality—feel free to keep both for explicit intent if preferred.
Also applies to: 41-46
apps/desktop2/src/components/settings/ai/shared/model-combobox.tsx (2)
164-197: Consider logging errors for debugging and observability.The function silently returns an empty array on all errors (lines 179-181, 193, 195). While this provides a smooth UX fallback, it makes debugging configuration issues or API failures difficult. Consider adding console warnings or error logging to help diagnose issues during development and troubleshooting.
} catch (error) { + console.warn("Failed to fetch models from OpenAI-compatible endpoint:", error); return []; }Also consider logging non-ok responses:
if (!response.ok) { + console.warn(`Failed to fetch models: ${response.status} ${response.statusText}`); return []; }
186-186: Consider adding type guards for model object properties.The code uses
anytype and assumesmodel.idormodel.nameexist as strings, but doesn't validate this. While the fallback toString(model)provides safety, callingString()on an object would yield"[object Object]"which isn't a useful model identifier.Consider adding basic type validation:
if (data.data && Array.isArray(data.data)) { - return data.data.map((model: any) => model.id || model.name || String(model)); + return data.data + .map((model: any) => { + if (typeof model === 'string') return model; + if (typeof model?.id === 'string') return model.id; + if (typeof model?.name === 'string') return model.name; + return null; + }) + .filter((id): id is string => id !== null); }Apply similar logic to line 190.
apps/desktop2/src/components/settings/ai/stt/select.tsx (3)
76-77: Disable rule should reflect “no models available”.
An empty array is truthy, so!configuredProviders[id]won’t disable. Use length.- disabled={provider.disabled || !configuredProviders[provider.id]} + disabled={ + provider.disabled || + !(configuredProviders[provider.id]?.length) + }
127-133: Avoid hard-coding hyprnote models in queries; derive from PROVIDERS for future additions.- const [p2, p3] = useQueries({ - queries: [ - sttModelQueries.isDownloaded("am-parakeet-v2"), - sttModelQueries.isDownloaded("am-parakeet-v3"), - ], - }); + const hyprModels = (PROVIDERS.find(p => p.id === "hyprnote")?.models ?? []); + const results = useQueries({ + queries: hyprModels.map((m) => sttModelQueries.isDownloaded(m as any)), + });- if (provider.id === "hyprnote") { - return [ - provider.id, - [ - p2.data ? "am-parakeet-v2" : null, - p3.data ? "am-parakeet-v3" : null, - ].filter(Boolean) as string[], - ]; - } + if (provider.id === "hyprnote") { + return [ + provider.id, + hyprModels.filter((_, i) => results[i]?.data) as string[], + ]; + }Also applies to: 136-143
33-41: Noise in production logs: guard error logging behind env check or use a debug logger.- const { form: { errors } } = formApi.getAllErrors(); - if (errors.length > 0) { - console.log(errors); - } + const { form: { errors } } = formApi.getAllErrors(); + if (errors.length > 0 && process.env.NODE_ENV !== "production") { + console.debug(errors); + }apps/desktop2/src/components/settings/ai/stt/configure.tsx (4)
50-58: Default base_url should use the provider’s default when no saved row exists; otherwise validation (z.url) fails and you’ll auto-submit invalid data.- const form = useForm({ + const form = useForm({ onSubmit: ({ value }) => setProvider(value), - defaultValues: provider - ?? ({ - type: "stt", - base_url: "", - api_key: "", - } satisfies internal.AIProvider), + defaultValues: provider ?? ({ + type: "stt", + base_url: config.baseUrl.value, + api_key: "", + } as internal.AIProvider),With this change the field-level
defaultValue={config.baseUrl.value}becomes redundant, but keeping it is harmless.Also applies to: 99-107
59-67: Auto-submit only when the form is valid to avoid churn and accidental writes.- onChange: ({ formApi }) => { + onChange: ({ formApi }) => { queueMicrotask(() => { const { form: { errors } } = formApi.getAllErrors(); - if (errors.length > 0) { - console.log(errors); - } - - formApi.handleSubmit(); + if (errors.length === 0) { + formApi.handleSubmit(); + } else if (process.env.NODE_ENV !== "production") { + console.debug(errors); + } }); },Also applies to: 70-71
231-234: Button is never disabled. Consider disabling while task is running to prevent rapid double-scheduling in edge cases.- disabled={false} + disabled={showProgress}
367-371: Nit: the backslash for line continuation in template literals isn’t needed; it may reduce readability in markdown output. Consider using a plain multiline string without the backslash.Also applies to: 377-381
apps/desktop2/src/components/chat/session.tsx (1)
127-140: Minor: add explicit return type for useTransport and ensure stable deps.Return type
CustomChatTransport | nullimproves readability and avoids implicitanyin consumers.-function useTransport() { +function useTransport(): CustomChatTransport | null { const registry = useToolRegistry(); const model = useLanguageModel(); const transport = useMemo(() => { if (!model) { return null; } return new CustomChatTransport(registry, model); }, [registry, model]); return transport; }apps/desktop2/src/chat/transport.ts (1)
22-29: Expose step cap as an option instead of hardcoding 5.Hardcoding
stopWhen: stepCountIs(5)may truncate replies unpredictably. Make it configurable viaChatRequestOptionsor a ctor param with a sensible default.apps/desktop2/src/components/settings/ai/llm/configure.tsx (2)
52-59: Avoid logging validation errors that may include sensitive context.Remove
console.log(errors)or gate behind a dev flag; prefer inline error UI (already present).
67-75: Nit: group Tailwind classes in cn arrays by concern for readability.Split structural vs. visual styles into separate array entries.
- className={cn(["rounded-lg border-2 border-dashed bg-gray-50"])} + className={cn([ + // structure + "rounded-lg", + // borders + "border-2 border-dashed", + // background + "bg-gray-50", + ])}-<AccordionTrigger className={cn(["capitalize gap-2 px-4"])}> +<AccordionTrigger + className={cn([ + "capitalize", + "gap-2 px-4", + ])} +>Also applies to: 71-71, 69-69
apps/desktop2/src/store/tinybase/persisted.ts (1)
273-279: Synchronizer: avoid async factory and .then(console.log); handle startSync safelyDrop the unnecessary async wrapper and remove .then(console.log). Use a plain factory and explicitly ignore or await startSync based on its return type to prevent surprises and noisy logs.
- const synchronizer = useCreateSynchronizer( - store, - async (store) => createBroadcastChannelSynchronizer(store, "hypr-persisted-sync"), - [], - (sync) => sync.startSync().then(console.log).catch(console.error), - ); + const synchronizer = useCreateSynchronizer( + store, + (s) => createBroadcastChannelSynchronizer(s, "hypr-persisted-sync"), + [], + (sync) => { + // If startSync returns a Promise, await or void it; if it returns cleanup, just call it. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + void sync.startSync(); + }, + );Please confirm startSync’s return type in your TinyBase version and adjust to either
await sync.startSync()inside a microtask-safe effect or keepvoidif it’s fire-and-forget.apps/desktop2/src/store/tinybase/internal.ts (3)
104-109: Synchronizer: remove async factory and logging; start safelyThe factory needn’t be async; chaining then(console.log) is noisy and may mis-handle non-Promise returns. Prefer a plain factory and explicitly ignore/await startSync.
- const synchronizer = useCreateSynchronizer( - store, - async (store) => createBroadcastChannelSynchronizer(store, "hypr-internal-sync"), - [], - (sync) => sync.startSync().then(console.log).catch(console.error), - ); + const synchronizer = useCreateSynchronizer( + store, + (s) => createBroadcastChannelSynchronizer(s, "hypr-internal-sync"), + [], + (sync) => { + // eslint-disable-next-line @typescript-eslint/no-floating-promises + void sync.startSync(); + }, + );Confirm startSync return type in your TinyBase version and switch to
awaitif it’s a Promise-returning start.
19-23: Optional values vs ValuesSchema: confirm undefined handlinggeneralSchema marks current_* fields optional, while ValuesSchema types are string. Ensure you set or delete values; do not write undefined into the store.
Consider central helpers:
setOrDelete(store, key, value)to avoid undefined writes.Also applies to: 51-54
123-147: Secret exposure in queries (api_key)Selecting api_key into UI-layer queries can leak in logs/devtools. Prefer masking or a separate query without secrets, and fetch the raw key only in controlled forms.
Would you like a masked-field renderer and a non-secret query variant?
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
apps/desktop2/src-tauri/capabilities/default.json(1 hunks)apps/desktop2/src/chat/transport.ts(2 hunks)apps/desktop2/src/components/chat/session.tsx(4 hunks)apps/desktop2/src/components/settings/ai/llm/configure.tsx(1 hunks)apps/desktop2/src/components/settings/ai/llm/select.tsx(1 hunks)apps/desktop2/src/components/settings/ai/llm/shared.tsx(1 hunks)apps/desktop2/src/components/settings/ai/shared/model-combobox.tsx(1 hunks)apps/desktop2/src/components/settings/ai/stt/configure.tsx(1 hunks)apps/desktop2/src/components/settings/ai/stt/select.tsx(1 hunks)apps/desktop2/src/components/settings/ai/stt/shared.tsx(1 hunks)apps/desktop2/src/hooks/useLLMConnection.ts(1 hunks)apps/desktop2/src/store/tinybase/internal.ts(6 hunks)apps/desktop2/src/store/tinybase/localPersister.ts(0 hunks)apps/desktop2/src/store/tinybase/localSynchronizer.ts(0 hunks)apps/desktop2/src/store/tinybase/persisted.ts(4 hunks)apps/desktop2/src/store/tinybase/shared.ts(0 hunks)
💤 Files with no reviewable changes (3)
- apps/desktop2/src/store/tinybase/shared.ts
- apps/desktop2/src/store/tinybase/localPersister.ts
- apps/desktop2/src/store/tinybase/localSynchronizer.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/desktop2/src/components/settings/ai/llm/select.tsx
- apps/desktop2/src/components/settings/ai/stt/shared.tsx
🧰 Additional context used
📓 Path-based instructions (1)
apps/desktop2/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/desktop2/.cursor/rules/style.mdc)
apps/desktop2/**/*.{tsx,jsx}: When there are many Tailwind classNames with conditional logic, use the cn utility imported asimport { cn } from "@hypr/ui/lib/utils"
Always pass an array to cn when composing Tailwind classNames
Split cn array entries by logical grouping when composing Tailwind classNames
Files:
apps/desktop2/src/components/settings/ai/shared/model-combobox.tsxapps/desktop2/src/components/settings/ai/stt/configure.tsxapps/desktop2/src/components/chat/session.tsxapps/desktop2/src/components/settings/ai/stt/select.tsxapps/desktop2/src/components/settings/ai/llm/shared.tsxapps/desktop2/src/components/settings/ai/llm/configure.tsx
🧬 Code graph analysis (9)
apps/desktop2/src/chat/transport.ts (2)
apps/desktop2/src/chat/types.ts (1)
HyprUIMessage(9-9)apps/desktop2/src/contexts/tool.tsx (1)
ToolRegistry(3-9)
apps/desktop2/src/components/settings/ai/shared/model-combobox.tsx (2)
apps/desktop2/src/hooks/useQuery.ts (1)
useQuery(3-46)packages/ui/src/lib/utils.ts (1)
cn(4-6)
apps/desktop2/src/components/settings/ai/stt/configure.tsx (6)
apps/desktop2/src/components/settings/ai/stt/shared.tsx (3)
PROVIDERS(24-68)ProviderId(8-8)sttModelQueries(70-83)plugins/local-stt/js/bindings.gen.ts (2)
setProvider(61-63)SupportedSttModel(88-88)apps/desktop2/src/components/settings/ai/shared/index.tsx (2)
useProvider(9-21)FormField(23-70)apps/desktop2/src/store/tinybase/internal.ts (2)
AIProvider(31-31)aiProviderSchema(25-29)apps/desktop2/src/hooks/useQuery.ts (1)
useQuery(3-46)apps/desktop2/src/components/task-manager.tsx (3)
registerDownloadProgressCallback(15-17)unregisterDownloadProgressCallback(19-21)DOWNLOAD_MODEL_TASK_ID(11-11)
apps/desktop2/src/components/chat/session.tsx (3)
apps/desktop2/src/contexts/tool.tsx (1)
useToolRegistry(69-75)apps/desktop2/src/hooks/useLLMConnection.ts (1)
useLanguageModel(49-65)apps/desktop2/src/chat/transport.ts (1)
CustomChatTransport(7-48)
apps/desktop2/src/components/settings/ai/stt/select.tsx (4)
apps/desktop2/src/components/settings/ai/llm/select.tsx (1)
SelectProviderAndModel(10-120)packages/ui/src/lib/utils.ts (1)
cn(4-6)apps/desktop2/src/components/settings/ai/stt/shared.tsx (4)
PROVIDERS(24-68)ProviderId(8-8)displayModelId(10-22)sttModelQueries(70-83)apps/desktop2/src/store/tinybase/internal.ts (1)
AIProviderStorage(32-32)
apps/desktop2/src/components/settings/ai/llm/shared.tsx (1)
apps/desktop2/src/components/settings/ai/stt/shared.tsx (2)
ProviderId(8-8)PROVIDERS(24-68)
apps/desktop2/src/components/settings/ai/llm/configure.tsx (4)
apps/desktop2/src/components/settings/ai/llm/shared.tsx (2)
PROVIDERS(5-54)ProviderId(3-3)apps/desktop2/src/components/settings/ai/shared/index.tsx (2)
useProvider(9-21)FormField(23-70)apps/desktop2/src/store/tinybase/internal.ts (2)
AIProvider(31-31)aiProviderSchema(25-29)packages/ui/src/lib/utils.ts (1)
cn(4-6)
apps/desktop2/src/store/tinybase/internal.ts (1)
apps/desktop2/src/store/tinybase/persisted.ts (1)
STORE_ID(37-37)
apps/desktop2/src/hooks/useLLMConnection.ts (1)
apps/desktop2/src/store/tinybase/internal.ts (1)
AIProviderStorage(32-32)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: zizmor
🔇 Additional comments (9)
apps/desktop2/src-tauri/capabilities/default.json (1)
22-47: Permissions are appropriately scoped for core app workflows.The new capability entries enable URL opening, file path operations, and directory writes—all reasonably constrained to user-writable directories (APPDATA, DOWNLOAD) and system-level checks (/Applications on macOS). The Obsidian protocol integration is sensible for a note-taking app.
JSON structure and Tauri permission identifiers are correct.
apps/desktop2/src/components/settings/ai/shared/model-combobox.tsx (1)
1-1: Pattern verified as intentional and consistent—no action needed.The use of
@tanstack/react-queryin this file is consistent with the established pattern across AI settings components. All AI-related components (stt/shared.tsx,stt/select.tsx,stt/configure.tsx, andmodel-combobox.tsx) use TanStack Query, while the customuseQueryhook is used only in legacy session management code. The app-level setup inmain.tsxconfirms TanStack Query is the intended pattern for newer features.apps/desktop2/src/components/settings/ai/stt/select.tsx (2)
10-11: Resolved: provider and model now both persist to the store on submit. LGTM.Also applies to: 42-45
148-150: Return type bug: useConfiguredMapping promises string[] but returns null here.
This will fail type-checking and can cause downstream truthiness bugs.Apply fix:
- if (!config) { - return [provider.id, null]; - } + if (!config) { + return [provider.id, []]; + }Likely an incorrect or invalid review comment.
apps/desktop2/src/components/chat/session.tsx (1)
31-31: Guard UX when transport is null (no model configured).With
transport ?? undefined,useChatmay fall back to default API routes; in Desktop this likely fails. Disable send/regenerate or show a setup prompt whentransportis null.Also applies to: 92-92
apps/desktop2/src/hooks/useLLMConnection.ts (1)
36-38: Follow-up: dependency onapiKeyfor local providers.Once
ollama/lmstudioare set toapiKey: falsein PROVIDERS, this guard will allow keyless connections as intended.apps/desktop2/src/store/tinybase/persisted.ts (2)
32-32: Broadcast-channel wiring LGTMImport, exposure via useProvideSynchronizer, and provisioning look consistent.
Please confirm the channel names ("hypr-persisted-sync" vs "hypr-internal-sync") are intentionally distinct for isolation.
Also applies to: 220-221, 489-491
228-229: Avoid creating a second “internal” store hereinternal.useStore() creates and provides a new “internal” store instance, potentially overriding another provider elsewhere and writing “changes” to the wrong instance.
Prefer retrieving the already-provided internal store from context instead of creating one. For example:
- If available:
const internalStore = internal.UI.useStore(internal.STORE_ID);- Otherwise: pass the internal store down as a prop to this component.
Also rename
store2→internalStorefor clarity.apps/desktop2/src/store/tinybase/internal.ts (1)
83-85: Provisioning LGTMProviding the synchronizer through UI wiring matches the new pattern.
Also applies to: 153-154
No description provided.