Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@hypr/plugin-windows": "workspace:*",
"@hypr/tiptap": "workspace:^",
"@hypr/ui": "workspace:^",
"@hypr/utils": "workspace:^",
"@iconify-icon/react": "^3.0.1",
"@lobehub/icons": "^2.43.1",
"@orama/highlight": "^0.1.9",
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/components/chat/body/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ChatStatus } from "ai";
import { useEffect, useRef } from "react";

import { cn } from "@hypr/ui/lib/utils";
import { cn } from "@hypr/utils";
import type { HyprUIMessage } from "../../../chat/types";
import { useShell } from "../../../contexts/shell";
import { ChatBodyEmpty } from "./empty";
Expand Down
4 changes: 2 additions & 2 deletions apps/desktop/src/components/chat/header.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { formatDistanceToNow } from "date-fns";
import { formatDistanceToNow } from "@hypr/utils";
import { ChevronDown, MessageCircle, PanelRightIcon, PictureInPicture2Icon, Plus, X } from "lucide-react";
import { useState } from "react";

import { Button } from "@hypr/ui/components/ui/button";
import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from "@hypr/ui/components/ui/dropdown-menu";
import { cn } from "@hypr/ui/lib/utils";
import { cn } from "@hypr/utils";
import { useShell } from "../../contexts/shell";
import * as persisted from "../../store/tinybase/persisted";

Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/components/chat/input.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Editor from "@hypr/tiptap/editor";
import { Button } from "@hypr/ui/components/ui/button";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@hypr/ui/components/ui/select";
import { cn } from "@hypr/ui/lib/utils";
import { cn } from "@hypr/utils";

import { MicIcon, PaperclipIcon, SendIcon } from "lucide-react";
import { useCallback, useEffect, useRef, useState } from "react";
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/components/chat/interactive.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Resizable } from "re-resizable";
import { type ReactNode, useState } from "react";

import { cn } from "@hypr/ui/lib/utils";
import { cn } from "@hypr/utils";

export function InteractiveContainer(
{
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/components/chat/message/normal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { formatDistanceToNow } from "date-fns";
import { formatDistanceToNow } from "@hypr/utils";
import { BrainIcon, RotateCcw } from "lucide-react";
import { Streamdown } from "streamdown";

Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/components/chat/message/shared.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ChevronRight, Loader2 } from "lucide-react";
import { type ReactNode } from "react";

import { cn } from "@hypr/ui/lib/utils";
import { cn } from "@hypr/utils";

export function MessageContainer({
align = "start",
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/components/chat/trigger.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { cn } from "@hypr/ui/lib/utils";
import { cn } from "@hypr/utils";

export function ChatTrigger({ onClick }: { onClick: () => void }) {
return (
Expand Down
69 changes: 54 additions & 15 deletions apps/desktop/src/components/main/body/calendars.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { addMonths, eachDayOfInterval, endOfMonth, format, getDay, isSameMonth, startOfMonth } from "@hypr/utils";
import { clsx } from "clsx";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Use cn from @hypr/utils and remove incorrect clsx named import.

Named import clsx breaks (it’s a default export), and our standard is cn for Tailwind merging. Replace usages accordingly. Based on learnings.

Apply:

-import { clsx } from "clsx";
+// use cn (clsx + tailwind-merge) from utils
+// ref: packages/utils/src/cn.ts

And merge into the first import:

-import { addMonths, eachDayOfInterval, endOfMonth, format, getDay, isSameMonth, startOfMonth } from "@hypr/utils";
+import { addMonths, eachDayOfInterval, endOfMonth, format, getDay, isSameMonth, startOfMonth, cn } from "@hypr/utils";

Replace usages:

-      className={clsx([
+      className={cn(
         "h-32 relative flex flex-col border-b border-neutral-200",
-        isWeekend ? "bg-neutral-50" : "bg-white",
-      ])}
+        isWeekend ? "bg-neutral-50" : "bg-white",
+      )}
-        <div className={clsx("flex items-end gap-1", isToday && "items-center")}>
+        <div className={cn("flex items-end gap-1", isToday && "items-center")}>
-          <div
-            className={clsx(
+          <div
+            className={cn(
               isToday && "bg-red-500 rounded-full w-6 h-6 flex items-center justify-center",
-            )}
+            )}
           >
             <span
-              className={clsx(
+              className={cn(
                 isToday
                   ? "text-white font-medium"
                   : !isCurrentMonth
                   ? "text-neutral-400"
                   : isWeekend
                   ? "text-neutral-500"
                   : "text-neutral-700",
-              )}
+              )}
             >

Also applies to: 163-167, 169-177, 176-184

🤖 Prompt for AI Agents
In apps/desktop/src/components/main/body/calendars.tsx around line 2 (and also
applicable to usages at lines ~163-167, 169-177, 176-184), the file incorrectly
imports a named `clsx` and uses it; replace this with the project's `cn` helper
from `@hypr/utils`. Remove the `import { clsx } from "clsx";` line, merge an
import for `cn` into the first import statement at the top of the file (e.g. add
`cn` to that import), and then change all calls to `clsx(...)` to `cn(...)` so
Tailwind class merging uses the standard helper.

import { addMonths, eachDayOfInterval, endOfMonth, format, getDay, isSameMonth, startOfMonth } from "date-fns";
import { CalendarIcon, FileTextIcon, Pen } from "lucide-react";
import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon, FileTextIcon, Pen } from "lucide-react";
import { useState } from "react";

import { CalendarStructure } from "@hypr/ui/components/block/calendar-structure";
import { Button } from "@hypr/ui/components/ui/button";
import { Popover, PopoverContent, PopoverTrigger } from "@hypr/ui/components/ui/popover";
import * as persisted from "../../../store/tinybase/persisted";
import { type Tab, useTabs } from "../../../store/zustand/tabs";
Expand Down Expand Up @@ -43,6 +43,7 @@ export function TabContentCalendar({ tab }: { tab: Tab }) {
const days = eachDayOfInterval({ start: monthStart, end: monthEnd }).map((day) => format(day, "yyyy-MM-dd"));
const startDayOfWeek = getDay(monthStart);
const weekDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const monthLabel = format(tab.month, "MMMM yyyy");

const handlePreviousMonth = () => {
openCurrent({ ...tab, month: addMonths(tab.month, -1) });
Expand All @@ -58,18 +59,56 @@ export function TabContentCalendar({ tab }: { tab: Tab }) {

return (
<StandardTabWrapper>
<CalendarStructure
monthLabel={format(tab.month, "MMMM yyyy")}
weekDays={weekDays}
startDayOfWeek={startDayOfWeek}
onPreviousMonth={handlePreviousMonth}
onNextMonth={handleNextMonth}
onToday={handleToday}
>
{days.map((day) => (
<TabContentCalendarDay key={day} day={day} isCurrentMonth={isSameMonth(new Date(day), tab.month)} />
))}
</CalendarStructure>
<div className="flex flex-col h-full rounded-lg border">
<div className="p-4 pb-2 flex items-center relative ">
<div className="text-xl font-semibold absolute left-1/2 transform -translate-x-1/2">{monthLabel}</div>
<div className="flex h-fit rounded-md overflow-clip border border-neutral-200 ml-auto">
<Button
variant="outline"
className="p-0.5 rounded-none border-none"
onClick={handlePreviousMonth}
>
<ChevronLeftIcon size={16} />
</Button>

<Button
variant="outline"
className="text-sm px-1 py-0.5 rounded-none border-none"
onClick={handleToday}
>
Today
</Button>

<Button
variant="outline"
className="p-0.5 rounded-none border-none"
onClick={handleNextMonth}
>
<ChevronRightIcon size={16} />
</Button>
</div>
</div>
<div className="h-full">
<div className="grid grid-cols-7 border-b border-neutral-200">
{weekDays.map((day) => (
<div
key={day}
className="text-center text-sm font-medium text-muted-foreground p-2"
>
{day}
</div>
))}
</div>
<div className="grid grid-cols-7 divide-x divide-neutral-200 h-[calc(100%-48px)] grid-rows-6 gap-0">
{Array.from({ length: startDayOfWeek }).map((_, i) => (
<div key={`empty-${i}`} className="border-b border-neutral-200" />
))}
{days.map((day) => (
<TabContentCalendarDay key={day} day={day} isCurrentMonth={isSameMonth(new Date(day), tab.month)} />
))}
</div>
</div>
Comment on lines +102 to +110
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fill trailing empty cells to complete the 6×7 calendar grid.

Only leading blanks are rendered; trailing cells are missing, causing broken borders/layout in shorter months.

Apply:

@@
-  const monthLabel = format(tab.month, "MMMM yyyy");
+  const monthLabel = format(tab.month, "MMMM yyyy");
+  // ensure grid is always 6x7
+  const TOTAL_CELLS = 42;
+  const trailingEmptyCount = Math.max(TOTAL_CELLS - (startDayOfWeek + days.length), 0);
@@
-          <div className="grid grid-cols-7 divide-x divide-neutral-200 h-[calc(100%-48px)] grid-rows-6 gap-0">
+          <div className="grid grid-cols-7 divide-x divide-neutral-200 h-[calc(100%-48px)] grid-rows-6 gap-0">
             {Array.from({ length: startDayOfWeek }).map((_, i) => (
               <div key={`empty-${i}`} className="border-b border-neutral-200" />
             ))}
             {days.map((day) => (
               <TabContentCalendarDay key={day} day={day} isCurrentMonth={isSameMonth(new Date(day), tab.month)} />
             ))}
+            {Array.from({ length: trailingEmptyCount }).map((_, i) => (
+              <div key={`empty-trailing-${i}`} className="border-b border-neutral-200" />
+            ))}
           </div>

Also applies to: 46-47

🤖 Prompt for AI Agents
In apps/desktop/src/components/main/body/calendars.tsx around lines 102-110
(also applies to lines 46-47), the calendar only renders leading blank cells
which leaves trailing cells out and breaks the 6x7 grid; after mapping leading
blanks and the month days, compute remaining = 42 - startDayOfWeek - days.length
and render that many trailing empty cells (use the same element structure,
classes and unique keys as the leading blanks) so the total rendered cells
always equals 42 and borders/layout stay consistent.

</div>
</StandardTabWrapper>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Building2, CornerDownLeft, Pencil, User } from "lucide-react";
import React, { useState } from "react";

import { cn } from "@hypr/ui/lib/utils";
import { cn } from "@hypr/utils";
import * as persisted from "../../../../store/tinybase/persisted";
import { ColumnHeader, type SortOption } from "./shared";

Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/components/main/body/contacts/people.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useState } from "react";

import { cn } from "@hypr/ui/lib/utils";
import { cn } from "@hypr/utils";
import * as persisted from "../../../../store/tinybase/persisted";
import { ColumnHeader, getInitials, type SortOption } from "./shared";

Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/components/main/body/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Button } from "@hypr/ui/components/ui/button";
import { cn } from "@hypr/ui/lib/utils";
import { cn } from "@hypr/utils";

import { useRouteContext } from "@tanstack/react-router";
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/components/main/body/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Loader2Icon, SearchIcon, XIcon } from "lucide-react";
import { useRef, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";

import { cn } from "@hypr/ui/lib/utils";
import { cn } from "@hypr/utils";
import { useSearch } from "../../../contexts/search/ui";

export function Search() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { SparklesIcon } from "lucide-react";
import { useState } from "react";

import { cn } from "@hypr/ui/lib/utils";
import { cn } from "@hypr/utils";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

LGTM! Import path updated consistently.

The refactoring to centralize utilities appears complete across all reviewed files. Consider running a codebase-wide verification to ensure no imports were missed.

Run the following script to verify all import paths have been updated consistently:


🏁 Script executed:

#!/bin/bash
# Verify all utility imports have been migrated to @hypr/utils

echo "=== Checking for any remaining old cn imports ==="
rg -n "from ['\"]@hypr/ui/lib/utils['\"]" -g '*.{ts,tsx}' -g '!node_modules'

echo -e "\n=== Verifying new @hypr/utils imports ==="
rg -n "from ['\"]@hypr/utils['\"]" -g '*.{ts,tsx}' -g '!node_modules' | head -20

echo -e "\n=== Checking @hypr/utils package exists and exports cn ==="
cat packages/utils/src/index.ts 2>/dev/null || echo "Could not find packages/utils/src/index.ts"
cat packages/utils/src/cn.ts 2>/dev/null || echo "Could not find packages/utils/src/cn.ts"

Length of output: 2852


Incomplete import migration detected. Two files still use the old path.

The refactoring is incomplete. While most imports have been updated to @hypr/utils, two files still reference the old @hypr/ui/lib/utils path:

  • packages/ui/src/components/ui/progressive-blur.tsx:2
  • packages/ui/src/components/ui/kbd.tsx:1

These must be updated to import from @hypr/utils to complete the consolidation.

🤖 Prompt for AI Agents
In apps/desktop/src/components/main/body/sessions/floating/generate.tsx around
line 4 the project is still referencing the old util path; update any imports
that use "@hypr/ui/lib/utils" to the consolidated package "@hypr/utils".
Specifically, change the import statements in the two remaining files
packages/ui/src/components/ui/progressive-blur.tsx (line 2) and
packages/ui/src/components/ui/kbd.tsx (line 1) to import from "@hypr/utils",
then run the build/tests to verify no other files reference the old path.

import * as persisted from "../../../../../store/tinybase/persisted";

import { FloatingButton } from "./shared";
Expand Down
18 changes: 16 additions & 2 deletions apps/desktop/src/components/main/body/sessions/floating/listen.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Icon } from "@iconify-icon/react";
import useMediaQuery from "beautiful-react-hooks/useMediaQuery";
import { useCallback, useState } from "react";
import { useCallback, useEffect, useState } from "react";

import { SoundIndicator } from "@hypr/ui/components/block/sound-indicator";
import { DancingSticks } from "@hypr/ui/components/ui/dancing-sticks";
import { Spinner } from "@hypr/ui/components/ui/spinner";
import { useListener } from "../../../../../contexts/listener";
import { useSTTConnection } from "../../../../../hooks/useSTTConnection";
Expand Down Expand Up @@ -88,6 +88,20 @@ function BeforeMeeingButton({ tab }: { tab: Extract<Tab, { type: "sessions" }> }
);
}

function SoundIndicator({ value, color }: { value: number | Array<number>; color?: string }) {
const [amplitude, setAmplitude] = useState(0);

const u16max = 65535;
useEffect(() => {
const sample = Array.isArray(value)
? (value.reduce((sum, v) => sum + v, 0) / value.length) / u16max
: value / u16max;
setAmplitude(Math.min(sample, 1));
}, [value]);

return <DancingSticks amplitude={amplitude} color={color} size="long" />;
}
Comment on lines +91 to +103
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Handle empty array edge case and verify value range assumptions.

The amplitude calculation has a potential edge case issue:

  1. Empty array bug (Line 96-98): If value is an empty array, the calculation 0 / value.length produces NaN, which will propagate through to DancingSticks. Consider adding a guard:
 const u16max = 65535;
 useEffect(() => {
+  if (Array.isArray(value) && value.length === 0) {
+    setAmplitude(0);
+    return;
+  }
   const sample = Array.isArray(value)
     ? (value.reduce((sum, v) => sum + v, 0) / value.length) / u16max
     : value / u16max;
   setAmplitude(Math.min(sample, 1));
 }, [value]);
  1. Verify value range: The use of u16max = 65535 assumes unsigned 16-bit values. If amplitude.mic or amplitude.speaker can be negative (signed audio samples), the normalization will produce negative results. Please verify that these values are guaranteed to be in the 0-65535 range.

🏁 Script executed:

#!/bin/bash
# Search for amplitude value assignments in listener context to verify range
rg -nP --type=ts --type=tsx -C5 'amplitude\s*[=:].*\{.*mic.*speaker.*\}' 

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Search for where amplitude object with mic/speaker properties is created/assigned
rg -nP -C8 'amplitude\s*[=:]\s*\{[^}]*mic' -g '*.ts' -g '*.tsx'

Length of output: 2245


🏁 Script executed:

#!/bin/bash
# Search for SoundIndicator usage and the amplitude values being passed
rg -nP -C5 'SoundIndicator' -g '*.ts' -g '*.tsx'

Length of output: 2205


🏁 Script executed:

#!/bin/bash
# Search for audio capture or amplitude processing that creates mic/speaker values
rg -nP -C5 '\.mic|\.speaker' -g '*.ts' -g '*.tsx' | head -100

Length of output: 8082


🏁 Script executed:

#!/bin/bash
# Check if there's any validation on payload values or plugin listener type definitions
rg -nP -C10 'audioAmplitude' -g '*.ts' -g '*.tsx'

Length of output: 3987


🏁 Script executed:

#!/bin/bash
# Search for @hypr/plugin-listener to understand the types
rg -nP '@hypr/plugin-listener' -g '*.ts' -g '*.tsx' -g '*.json'

Length of output: 490


🏁 Script executed:

#!/bin/bash
# Look for any normalization or validation of audio amplitude values
rg -nP -C5 'payload\.(mic|speaker)' -g '*.ts' -g '*.tsx'

Length of output: 966


Remove incorrect empty array concern; fix normalization to clamp negative values.

The empty array edge case is not applicable—SoundIndicator always receives exactly two elements [amplitude.mic, amplitude.speaker].

However, the normalization logic has a real issue: Math.min(sample, 1) only clamps the upper bound. If payload.mic or payload.speaker are negative (which is possible with signed audio samples), negative amplitudes will be passed to DancingSticks. The fix should ensure the 0-1 range:

 const u16max = 65535;
 useEffect(() => {
   const sample = Array.isArray(value)
     ? (value.reduce((sum, v) => sum + v, 0) / value.length) / u16max
     : value / u16max;
-  setAmplitude(Math.min(sample, 1));
+  setAmplitude(Math.max(0, Math.min(sample, 1)));
 }, [value]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function SoundIndicator({ value, color }: { value: number | Array<number>; color?: string }) {
const [amplitude, setAmplitude] = useState(0);
const u16max = 65535;
useEffect(() => {
const sample = Array.isArray(value)
? (value.reduce((sum, v) => sum + v, 0) / value.length) / u16max
: value / u16max;
setAmplitude(Math.min(sample, 1));
}, [value]);
return <DancingSticks amplitude={amplitude} color={color} size="long" />;
}
function SoundIndicator({ value, color }: { value: number | Array<number>; color?: string }) {
const [amplitude, setAmplitude] = useState(0);
const u16max = 65535;
useEffect(() => {
const sample = Array.isArray(value)
? (value.reduce((sum, v) => sum + v, 0) / value.length) / u16max
: value / u16max;
setAmplitude(Math.max(0, Math.min(sample, 1)));
}, [value]);
return <DancingSticks amplitude={amplitude} color={color} size="long" />;
}
🤖 Prompt for AI Agents
In apps/desktop/src/components/main/body/sessions/floating/listen.tsx around
lines 91 to 103, remove the unnecessary empty-array handling (SoundIndicator
always receives exactly two elements) and normalize/clamp amplitudes into the
0–1 range: compute the sample as either the average of the two-array values or
value as-is, divide by u16max, then ensure you clamp both lower and upper bounds
(e.g., use a clamp like Math.max(0, Math.min(sample, 1))) or take absolute value
before normalization if signed samples should be treated as magnitude; finally
call setAmplitude with the clamped 0–1 value.


function DuringMeetingButton() {
const stop = useListener((state) => state.stop);
const { amplitude, seconds } = useListener(({ amplitude, seconds }) => ({ amplitude, seconds }));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useRef } from "react";

import type { TiptapEditor } from "@hypr/tiptap/editor";
import { cn } from "@hypr/ui/lib/utils";
import { cn } from "@hypr/utils";
import { useListener } from "../../../../../contexts/listener";
import * as persisted from "../../../../../store/tinybase/persisted";
import { type Tab, useTabs } from "../../../../../store/zustand/tabs";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Effect, pipe } from "effect";
import { forwardRef, useCallback } from "react";

import type { PlaceholderFunction } from "@hypr/tiptap/shared";
import { cn } from "@hypr/ui/lib/utils";
import { cn } from "@hypr/utils";
import * as persisted from "../../../../../store/tinybase/persisted";
import { commands } from "../../../../../types/tauri.gen";

Expand Down
130 changes: 0 additions & 130 deletions apps/desktop/src/components/main/body/sessions/outer-header/event.tsx

This file was deleted.

Loading
Loading