Skip to content

AI Setting Final#1580

Merged
yujonglee merged 26 commits intomainfrom
ai-setting-final
Oct 20, 2025
Merged

AI Setting Final#1580
yujonglee merged 26 commits intomainfrom
ai-setting-final

Conversation

@yujonglee
Copy link
Contributor

No description provided.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 19, 2025

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Refactors 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

Cohort / File(s) Summary
Dependency updates
apps/desktop2/package.json, apps/pro/package.json, apps/web/package.json, package.json, packages/db/package.json, packages/tiptap/package.json, packages/ui/package.json, packages/utils/package.json
Add AI SDK provider deps, bump multiple packages (tanstack router/devtools/plugins, @types/node, supabase-js, ai, lucide-react, etc.).
AI settings — LLM (new)
apps/desktop2/src/components/settings/ai/llm/index.tsx, .../llm/select.tsx, .../llm/configure.tsx, .../llm/shared.tsx
New modular LLM UI: provider list (PROVIDERS/ProviderId), SelectProviderAndModel, ConfigureProviders form with validation, and composite LLM component.
AI settings — STT (new)
apps/desktop2/src/components/settings/ai/stt/index.tsx, .../stt/select.tsx, .../stt/configure.tsx, .../stt/shared.tsx
New STT UI: provider definitions, provider/model selector, ConfigureProviders with Hypr local/cloud rows, local-model download lifecycle and react-query status helpers.
AI settings — removals / shared
apps/desktop2/src/components/settings/ai/llm.tsx (removed), apps/desktop2/src/components/settings/ai/stt.tsx (removed), apps/desktop2/src/components/settings/ai/shared.tsx (removed), apps/desktop2/src/components/settings/ai/index.tsx, apps/desktop2/src/components/settings/ai/shared/index.tsx, .../shared/model-combobox.tsx
Deleted legacy shared UI and monolithic LLM/STT files; added useProvider hook, FormField, ModelCombobox (with openaiCompatibleListModels) and updated settings Tabs to include icons.
Store: tinybase & synchronizers
apps/desktop2/src/store/tinybase/internal.ts, .../persisted.ts, .../shared.ts, .../localSynchronizer.ts (removed), .../localPersister.ts
Replace local synchronizer with broadcast-channel synchronizer, add useProvideSynchronizer wiring, remove BROADCAST_CHANNEL_NAME/LOCAL_PERSISTER_ID exports, add current_llm_model/current_stt_model schema fields, remove per-provider model field.
Task manager & STT download workflow
apps/desktop2/src/components/task-manager.tsx
Add DOWNLOAD_MODEL_TASK_ID, per-model progress callback registration/unregistration, Channel-based download task invoking localSttCommands.downloadModel and propagating progress.
Language model integration & transport
apps/desktop2/src/chat/transport.ts, apps/desktop2/src/hooks/useLLMConnection.ts, apps/desktop2/src/components/chat/session.tsx
CustomChatTransport now accepts an injected LanguageModel; add useLLMConnection/useLanguageModel hook to resolve provider/baseUrl/apiKey and construct SDK LanguageModel; ChatSession uses a useTransport helper.
React Query provider
apps/desktop2/src/main.tsx
Add QueryClient and wrap the app with QueryClientProvider for react-query usage.
Settings layout styling
apps/desktop2/src/routes/app/settings/_layout.tsx
Adjust margins/paddings and tab text sizing/spacing.
TAURI capabilities
apps/desktop2/src-tauri/capabilities/default.json
Add opener and fs permissions (open-url, open-path, exists, write-file) for specified paths.
UI library — components & APIs
packages/ui/src/components/ui/button.tsx, dialog.tsx, command.tsx, select.tsx, popover.tsx, input.tsx, textarea.tsx, accordion.tsx, tabs.tsx, input-group.tsx
Large refactors: new Dialog wrapper, CommandDialog → Dialog, Button → CVA variants + asChild, simplified PopoverContent and added PopoverAnchor, Select exposes scroll buttons and simplified Trigger props, Input/Textarea/InputGroup style and import path changes, plus other class/token updates.
Misc config
.coderabbit.yaml, various package.json under packages
Replace path_instructions with path_filters and miscellaneous package/dependency bumps.

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
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description Check ⚠️ Warning No pull request description was provided by the author. The changeset contains substantial modifications including refactoring of AI settings components, schema changes to TinyBase store configuration, UI component updates, new provider configuration workflows, and dependency version updates. Without any description, there is no information available to evaluate whether the author's intent aligns with the changeset or to understand the rationale behind these changes.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The pull request title "AI Setting Final" is related to the changeset, which consists primarily of a major refactoring of AI settings UI components (LLM and STT provider configuration). However, the title is vague and lacks specificity. The word "Final" is non-descriptive and does not convey meaningful information about what was actually changed—whether this is a refactoring, feature completion, UI reorganization, or something else. While the title does reference "AI Setting," which aligns with the changeset content, it does not clearly summarize the nature or scope of the primary changes.

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8928582 and 56752ba.

📒 Files selected for processing (4)
  • 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/hooks/useLLMConnection.ts (1 hunks)

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 default type="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 when asChild is true.

Non-button children (e.g., ) ignore disabled. Mirror it to aria-disabled for 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 cn from lib/utils within packages/ui—some files use the @hypr/ui alias, others use relative paths. Relative imports like ../../lib/utils are 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 cast

Cast 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 churn

Unless 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‑splitting

Desktop (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 defaultValue

Rely 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_model and current_stt_model have no defaults. If generalSchema is 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.

aiProviderSchema is imported directly and available on internal. 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: Consider toLowerCase() over toLocaleLowerCase() 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

📥 Commits

Reviewing files that changed from the base of the PR and between ae5c149 and c412b5f.

⛔ Files ignored due to path filters (2)
  • apps/desktop2/public/assets/icon.png is excluded by !**/*.png
  • pnpm-lock.yaml is 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 as import { 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.tsx
  • apps/desktop2/src/main.tsx
  • apps/desktop2/src/routes/app/settings/_layout.tsx
  • apps/desktop2/src/components/settings/ai/stt/select.tsx
  • apps/desktop2/src/components/settings/ai/llm/select.tsx
  • apps/desktop2/src/components/settings/ai/llm/shared.tsx
  • apps/desktop2/src/components/settings/ai/stt/shared.tsx
  • apps/desktop2/src/components/settings/ai/index.tsx
  • apps/desktop2/src/components/settings/ai/shared/model-combobox.tsx
  • apps/desktop2/src/components/settings/ai/llm/configure.tsx
  • apps/desktop2/src/components/settings/ai/shared/index.tsx
  • apps/desktop2/src/components/settings/ai/stt/index.tsx
  • apps/desktop2/src/components/settings/ai/stt/configure.tsx
  • apps/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.tsx
  • packages/ui/src/components/ui/textarea.tsx
  • packages/ui/src/components/ui/input-group.tsx
  • apps/desktop2/src/main.tsx
  • apps/desktop2/src/routes/app/settings/_layout.tsx
  • packages/ui/src/components/ui/accordion.tsx
  • apps/desktop2/src/components/settings/ai/stt/select.tsx
  • packages/ui/tailwind.config.ts
  • apps/desktop2/src/components/settings/ai/llm/select.tsx
  • apps/desktop2/src/components/settings/ai/llm/shared.tsx
  • apps/desktop2/src/store/tinybase/internal.ts
  • packages/ui/src/components/ui/button.tsx
  • apps/desktop2/src/components/settings/ai/stt/shared.tsx
  • apps/desktop2/src/components/settings/ai/index.tsx
  • apps/desktop2/src/components/settings/ai/shared/model-combobox.tsx
  • apps/desktop2/src/components/settings/ai/llm/configure.tsx
  • apps/desktop2/src/components/settings/ai/shared/index.tsx
  • apps/desktop2/src/components/settings/ai/stt/index.tsx
  • apps/desktop2/src/components/settings/ai/stt/configure.tsx
  • apps/desktop2/src/components/task-manager.tsx
  • packages/ui/src/components/ui/tabs.tsx
  • packages/ui/src/components/ui/input.tsx
  • packages/ui/src/components/ui/dialog.tsx
  • packages/ui/src/components/ui/popover.tsx
  • packages/ui/src/components/ui/command.tsx
  • packages/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.tsx
  • apps/desktop2/src/routes/app/settings/_layout.tsx
  • packages/ui/src/components/ui/accordion.tsx
  • packages/ui/src/components/ui/tabs.tsx
  • packages/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-sm and text-left on the trigger button and text-muted-foreground on the ChevronDown icon provide better text sizing, alignment, and visual hierarchy without affecting functionality.


41-52: LGTM: Transition handling refined.

Removing transition-all is appropriate since the accordion animations are explicitly controlled by the animate-accordion-up and animate-accordion-down classes. 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-colors enhances 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 in packages/ui/tsconfig.json. The import from "@hypr/ui/components/ui/dialog" correctly resolves to packages/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/utils is 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 safe

All 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, or closeOnClickOutside across the entire codebase, and the actual popover.tsx file shows that PopoverContent only accepts className, 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 buttonVariants is 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:

  1. isLoading prop: No usages found in the codebase. No breaking change.

  2. Variant drift: All Button variants used in the codebase (default, destructive, outline, secondary, ghost) are within the supported list. No breaking change.

  3. Size drift: The size="xs" in packages/ui/src/components/ui/input-group.tsx:94 is NOT passed to the Button component. It belongs to inputGroupButtonVariants, a separate CVA used by the InputGroupButton wrapper 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/utils is 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/utils is 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 size or variant props found. All 5 call sites in the codebase already rely on className for 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/select without 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 SelectProviderAndModel and ConfigureProviders. 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 SelectProviderAndModel

SelectProviderAndModel’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 icons

The 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 tweaks

Good 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 styles

Typographic 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 order

QueryClientProvider -> 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 providers

Ollama 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’s models list 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 openaiCompatibleListModels function 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 cn utility 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 FormField component handles error states properly, supports accessibility with aria-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 baseUrl set to "https://api.openai.com/v1" (line 33), which appears incorrect for a Deepgram provider. This could be:

  1. A copy-paste error that should point to a Deepgram endpoint
  2. An intentional placeholder since immutable: false allows users to change it

Please 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 sttModelQueries helpers 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 PROVIDERS array is well-structured with consistent shape, proper type safety via as const, and clear disabled flags for providers not yet ready. The icon handling with JSX elements is appropriate.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 provided

useDidFinishTransactionListener 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 queryKey includes the listModels function 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:

  1. Accept a stable queryKey prop from the parent, or
  2. Require the parent to pass a memoized listModels function (via useMemo/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_key when 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 any type and assumes model.id or model.name exist as strings, but doesn't validate this. While the fallback to String(model) provides safety, calling String() 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 | null improves readability and avoids implicit any in 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 via ChatRequestOptions or 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 safely

Drop 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 keep void if it’s fire-and-forget.

apps/desktop2/src/store/tinybase/internal.ts (3)

104-109: Synchronizer: remove async factory and logging; start safely

The 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 await if it’s a Promise-returning start.


19-23: Optional values vs ValuesSchema: confirm undefined handling

generalSchema 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

📥 Commits

Reviewing files that changed from the base of the PR and between 5c6242d and 8928582.

📒 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 as import { 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.tsx
  • apps/desktop2/src/components/settings/ai/stt/configure.tsx
  • apps/desktop2/src/components/chat/session.tsx
  • apps/desktop2/src/components/settings/ai/stt/select.tsx
  • apps/desktop2/src/components/settings/ai/llm/shared.tsx
  • apps/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-query in 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, and model-combobox.tsx) use TanStack Query, while the custom useQuery hook is used only in legacy session management code. The app-level setup in main.tsx confirms 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, useChat may fall back to default API routes; in Desktop this likely fails. Disable send/regenerate or show a setup prompt when transport is null.

Also applies to: 92-92

apps/desktop2/src/hooks/useLLMConnection.ts (1)

36-38: Follow-up: dependency on apiKey for local providers.

Once ollama/lmstudio are set to apiKey: false in PROVIDERS, this guard will allow keyless connections as intended.

apps/desktop2/src/store/tinybase/persisted.ts (2)

32-32: Broadcast-channel wiring LGTM

Import, 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 here

internal.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 store2internalStore for clarity.

apps/desktop2/src/store/tinybase/internal.ts (1)

83-85: Provisioning LGTM

Providing the synchronizer through UI wiring matches the new pattern.

Also applies to: 153-154

@yujonglee yujonglee merged commit f222a68 into main Oct 20, 2025
3 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant