-
Notifications
You must be signed in to change notification settings - Fork 416
Refactor utilities and cleanup legacy components #1583
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
📝 WalkthroughWalkthroughThe PR consolidates utility functions ( Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Rationale: This PR involves substantial structural changes across 50+ files with heterogeneous patterns: mechanical import migrations (40+ files with consistent changes), multiple UI component deletions (9 components removed from packages/ui), logic-dense refactorings (metadata.tsx, calendars.tsx with new inline rendering), new utility module creation, and removal of significant features (SessionEvent, SessionParticipants components). While many changes are repetitive (import path updates), the component removals, refactorings, and the need to verify inlined replacement logic require careful, diverse reasoning across different file categories. Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
apps/desktop/src/components/main/body/calendars.tsx (3)
141-144: ClampvisibleCountto avoid negatives if layout constants change.Prevents slice with negative indices and rendering glitches.
- const visibleCount = totalItems > maxPossibleEvents - ? maxPossibleEvents - 1 - : totalItems; + const maxVisible = Math.max(maxPossibleEvents - 1, 0); + const visibleCount = Math.min(totalItems, maxVisible);
267-274: Use semantic buttons for popover triggers (keyboard/a11y).
divis not keyboard-focusable; switch tobutton type="button". Improves a11y without behavior change.- <PopoverTrigger asChild> - <div className="flex items-center space-x-1 px-0.5 py-0.5 cursor-pointer rounded hover:bg-neutral-200 transition-colors h-5"> + <PopoverTrigger asChild> + <button type="button" className="flex items-center space-x-1 px-0.5 py-0.5 cursor-pointer rounded hover:bg-neutral-200 transition-colors h-5"> <CalendarIcon className="w-2.5 h-2.5 text-neutral-500 flex-shrink-0" /> <div className="flex-1 text-xs text-neutral-800 truncate"> {event.title} </div> - </div> + </button> </PopoverTrigger>- <PopoverTrigger asChild> - <div className="flex items-center space-x-1 px-0.5 py-0.5 cursor-pointer rounded hover:bg-neutral-200 transition-colors h-5"> + <PopoverTrigger asChild> + <button type="button" className="flex items-center space-x-1 px-0.5 py-0.5 cursor-pointer rounded hover:bg-neutral-200 transition-colors h-5"> <FileTextIcon className="w-2.5 h-2.5 text-neutral-500 flex-shrink-0" /> <div className="flex-1 text-xs text-neutral-800 truncate"> {session.title} </div> - </div> + </button> </PopoverTrigger>- <PopoverTrigger asChild> - <div className="text-xs text-neutral-600 rounded py-0.5 cursor-pointer hover:bg-neutral-200 px-0.5 h-5"> + <PopoverTrigger asChild> + <button type="button" className="text-xs text-neutral-600 rounded py-0.5 cursor-pointer hover:bg-neutral-200 px-0.5 h-5"> +{hiddenCount} more - </div> + </button> </PopoverTrigger>Also applies to: 339-346, 385-387
323-334: Guard against invalidcreated_atto avoid “Invalid Date/NaN” output.If
session.created_atis missing/invalid, current logic renders broken timestamps.- const formatSessionTime = () => { - const created = new Date(session.created_at || ""); + const formatSessionTime = () => { + if (!session.created_at) return ""; + const created = new Date(session.created_at); + if (Number.isNaN(created.getTime())) return ""; const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; const hours = created.getHours(); const minutes = created.getMinutes(); const ampm = hours >= 12 ? "PM" : "AM"; const displayHours = hours % 12 || 12;apps/desktop/src/components/main/body/sessions/outer-header/metadata.tsx (2)
404-406: Hardenwindow.openagainst tabnabbing.Add
noopener,noreferrer(and nullify opener) when opening external links.- const handleJoinMeeting = useCallback((meetingLink: string) => { - window.open(meetingLink, "_blank"); - }, []); + const handleJoinMeeting = useCallback((meetingLink: string) => { + const w = window.open(meetingLink, "_blank", "noopener,noreferrer"); + if (w) w.opener = null; + }, []);
376-381: Fix participant search filter to require name OR email match.Current condition can include non-matching rows when
nameis missing. Enforce proper matching.- if ( - name && !name.toLowerCase().includes(normalizedQuery) - && (!email || !email.toLowerCase().includes(normalizedQuery)) - ) { - return; - } + const matchesName = name ? name.toLowerCase().includes(normalizedQuery) : false; + const matchesEmail = email ? email.toLowerCase().includes(normalizedQuery) : false; + if (!matchesName && !matchesEmail) { + return; + }
🧹 Nitpick comments (5)
packages/utils/src/date.ts (1)
34-54: Custom formatDate implementation is correct.The lightweight date formatter works correctly with proper token ordering in the regex (dd before d) to avoid matching conflicts. The implementation is well-documented as an intentional alternative to date-fns format() for simple cases.
If you prefer to consolidate on a single formatting approach, consider using the re-exported date-fns
format()function directly informatDateRange(lines 68-69) instead of the customformatDate. However, the current lightweight implementation is valid and may offer performance benefits for simple format strings.apps/desktop/src/components/main/body/calendars.tsx (1)
66-72: Add aria-labels to icon-only navigation buttons.Improves accessibility for screen readers.
- <Button + <Button variant="outline" className="p-0.5 rounded-none border-none" + aria-label="Previous month" onClick={handlePreviousMonth} > @@ - <Button + <Button variant="outline" className="p-0.5 rounded-none border-none" + aria-label="Next month" onClick={handleNextMonth} >Also applies to: 82-88
apps/desktop/src/components/main/body/sessions/outer-header/metadata.tsx (3)
55-62: Make initials generation robust to extra spaces and short names.Trim and filter empties to avoid empty/undefined chars.
- const getInitials = (name: string) => { - return name - .split(" ") - .map((n) => n[0]) - .join("") - .toUpperCase() - .slice(0, 2); - }; + const getInitials = (name: string) => { + return name + .trim() + .split(/\s+/) + .filter(Boolean) + .map((n) => n.charAt(0)) + .join("") + .toUpperCase() + .slice(0, 2) || "?"; + };
101-109: Explicit button type to avoid accidental form submission.Safe default, minor a11y polish.
- <button + <button + type="button" onClick={(e) => { e.stopPropagation(); onRemove?.(); }} className="text-red-400 hover:text-red-600 transition-colors opacity-0 group-hover:opacity-100" >
218-248: Optional: add ARIA roles for the suggestions popup.Improve screen reader support by marking listbox/options and active state.
- {isFocused && searchQuery.trim() && ( - <div className="absolute top-full left-0 right-0 mt-1 flex flex-col w-full rounded border border-neutral-200 overflow-hidden bg-white shadow-md z-10 max-h-60 overflow-y-auto"> + {isFocused && searchQuery.trim() && ( + <div + role="listbox" + aria-label="Participants" + className="absolute top-full left-0 right-0 mt-1 flex flex-col w-full rounded border border-neutral-200 overflow-hidden bg-white shadow-md z-10 max-h-60 overflow-y-auto" + > @@ - <button + <button + role="option" + aria-selected={selectedIndex === index} key={participant.id} type="button"
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (38)
packages/ui/src/components/ui/accordion.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/avatar.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/badge.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/bottom-sheet.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/breadcrumb.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/button.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/card.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/carousel.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/checkbox.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/command.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/context-menu.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/cursor-tooltip.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/dialog.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/dropdown-menu.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/form.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/input-group.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/input.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/label.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/modal.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/particles.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/popover.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/progress.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/pushable-button.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/radio-group.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/resizable.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/scroll-area.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/select.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/separator.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/shimmer-button.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/shiny-button.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/spinner.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/splash.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/switch.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/tabs.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/text-animate.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/textarea.tsxis excluded by!packages/ui/src/components/ui/**packages/ui/src/components/ui/tooltip.tsxis excluded by!packages/ui/src/components/ui/**pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (55)
apps/desktop/package.json(1 hunks)apps/desktop/src/components/chat/body/index.tsx(1 hunks)apps/desktop/src/components/chat/header.tsx(1 hunks)apps/desktop/src/components/chat/input.tsx(1 hunks)apps/desktop/src/components/chat/interactive.tsx(1 hunks)apps/desktop/src/components/chat/message/normal.tsx(1 hunks)apps/desktop/src/components/chat/message/shared.tsx(1 hunks)apps/desktop/src/components/chat/trigger.tsx(1 hunks)apps/desktop/src/components/main/body/calendars.tsx(3 hunks)apps/desktop/src/components/main/body/contacts/organizations.tsx(1 hunks)apps/desktop/src/components/main/body/contacts/people.tsx(1 hunks)apps/desktop/src/components/main/body/index.tsx(1 hunks)apps/desktop/src/components/main/body/search.tsx(1 hunks)apps/desktop/src/components/main/body/sessions/floating/generate.tsx(1 hunks)apps/desktop/src/components/main/body/sessions/floating/listen.tsx(2 hunks)apps/desktop/src/components/main/body/sessions/note-input/index.tsx(1 hunks)apps/desktop/src/components/main/body/sessions/note-input/raw.tsx(1 hunks)apps/desktop/src/components/main/body/sessions/outer-header/event.tsx(0 hunks)apps/desktop/src/components/main/body/sessions/outer-header/metadata.tsx(3 hunks)apps/desktop/src/components/main/body/sessions/outer-header/participant.tsx(0 hunks)apps/desktop/src/components/main/body/sessions/title-input.tsx(1 hunks)apps/desktop/src/components/main/sidebar/profile/banner.tsx(1 hunks)apps/desktop/src/components/main/sidebar/profile/notification.tsx(1 hunks)apps/desktop/src/components/main/sidebar/search/group.tsx(1 hunks)apps/desktop/src/components/main/sidebar/search/index.tsx(1 hunks)apps/desktop/src/components/main/sidebar/search/item.tsx(1 hunks)apps/desktop/src/components/main/sidebar/timeline/index.tsx(1 hunks)apps/desktop/src/components/main/sidebar/timeline/item.tsx(1 hunks)apps/desktop/src/components/settings/ai/llm/configure.tsx(1 hunks)apps/desktop/src/components/settings/ai/llm/select.tsx(1 hunks)apps/desktop/src/components/settings/ai/shared/model-combobox.tsx(1 hunks)apps/desktop/src/components/settings/ai/stt/configure.tsx(1 hunks)apps/desktop/src/components/settings/ai/stt/select.tsx(1 hunks)apps/desktop/src/contexts/audio-player/timeline.tsx(1 hunks)apps/desktop/src/devtool/index.tsx(1 hunks)apps/desktop/src/devtool/tinytick.tsx(1 hunks)apps/desktop/src/routes/app/onboarding.tsx(3 hunks)apps/desktop/src/routes/app/settings/_layout.tsx(1 hunks)apps/desktop/src/store/tinybase/persisted.ts(1 hunks)apps/desktop/src/utils/timeline.ts(1 hunks)packages/ui/package.json(1 hunks)packages/ui/src/components/block/calendar-structure.tsx(0 hunks)packages/ui/src/components/block/chat-panel-button.tsx(0 hunks)packages/ui/src/components/block/event-chip.tsx(0 hunks)packages/ui/src/components/block/listen-button.tsx(0 hunks)packages/ui/src/components/block/meeting-metadata-chip.tsx(0 hunks)packages/ui/src/components/block/participants-chip.tsx(0 hunks)packages/ui/src/components/block/sound-indicator.tsx(0 hunks)packages/ui/src/components/block/tab-header.tsx(0 hunks)packages/ui/src/components/block/title-input.tsx(0 hunks)packages/ui/src/lib/utils.ts(0 hunks)packages/utils/package.json(1 hunks)packages/utils/src/cn.ts(1 hunks)packages/utils/src/date.ts(1 hunks)packages/utils/src/index.ts(1 hunks)
💤 Files with no reviewable changes (12)
- apps/desktop/src/components/main/body/sessions/outer-header/event.tsx
- apps/desktop/src/components/main/body/sessions/outer-header/participant.tsx
- packages/ui/src/components/block/title-input.tsx
- packages/ui/src/components/block/chat-panel-button.tsx
- packages/ui/src/lib/utils.ts
- packages/ui/src/components/block/sound-indicator.tsx
- packages/ui/src/components/block/event-chip.tsx
- packages/ui/src/components/block/tab-header.tsx
- packages/ui/src/components/block/calendar-structure.tsx
- packages/ui/src/components/block/listen-button.tsx
- packages/ui/src/components/block/meeting-metadata-chip.tsx
- packages/ui/src/components/block/participants-chip.tsx
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
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"`
📚 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:
apps/desktop/src/components/main/sidebar/search/group.tsxapps/desktop/src/components/chat/input.tsxpackages/utils/src/cn.tsapps/desktop/src/components/chat/interactive.tsxapps/desktop/src/components/chat/body/index.tsxapps/desktop/src/components/main/body/search.tsxapps/desktop/src/components/main/sidebar/profile/notification.tsxapps/desktop/src/components/main/body/sessions/note-input/index.tsxapps/desktop/src/devtool/index.tsxapps/desktop/src/components/main/sidebar/search/index.tsxapps/desktop/src/devtool/tinytick.tsxapps/desktop/src/components/main/body/sessions/floating/generate.tsxapps/desktop/src/routes/app/settings/_layout.tsxapps/desktop/src/components/main/sidebar/profile/banner.tsxapps/desktop/src/components/main/sidebar/timeline/item.tsxapps/desktop/src/components/main/sidebar/timeline/index.tsxapps/desktop/src/components/main/body/index.tsxapps/desktop/src/components/chat/trigger.tsxapps/desktop/src/components/main/sidebar/search/item.tsxapps/desktop/src/components/main/body/sessions/title-input.tsxapps/desktop/src/components/main/body/sessions/note-input/raw.tsx
📚 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} : Split cn array entries by logical grouping when composing Tailwind classNames
Applied to files:
packages/utils/src/cn.tsapps/desktop/src/components/main/body/index.tsx
📚 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} : Always pass an array to cn when composing Tailwind classNames
Applied to files:
packages/utils/src/cn.ts
🧬 Code graph analysis (4)
apps/desktop/src/routes/app/onboarding.tsx (1)
packages/ui/src/components/ui/button.tsx (1)
Button(53-53)
apps/desktop/src/components/main/body/sessions/outer-header/metadata.tsx (3)
packages/ui/src/components/icons/linkedin.tsx (1)
LinkedInIcon(7-23)packages/utils/src/cn.ts (1)
cn(20-22)packages/utils/src/date.ts (2)
getMeetingDomain(84-91)formatDateRange(64-76)
apps/desktop/src/components/main/body/calendars.tsx (1)
packages/ui/src/components/ui/button.tsx (1)
Button(53-53)
apps/desktop/src/components/main/body/sessions/floating/listen.tsx (1)
packages/ui/src/components/ui/dancing-sticks.tsx (1)
DancingSticks(49-74)
🔇 Additional comments (44)
apps/desktop/src/components/main/sidebar/profile/notification.tsx (1)
6-6: LGTM! Clean import path migration.The import path change from
@hypr/ui/lib/utilsto@hypr/utilsaligns with the centralized utilities refactor. The usage ofcnthroughout the component remains unchanged and follows best practices for managing conditional Tailwind classes.apps/desktop/src/components/main/sidebar/timeline/item.tsx (1)
5-5: LGTM! Consistent with the centralized utilities migration.The import path update is clean and consistent with the broader refactoring effort.
apps/desktop/src/components/main/sidebar/search/group.tsx (1)
4-4: LGTM! Import migration looks good.apps/desktop/src/components/main/body/sessions/note-input/index.tsx (1)
4-4: LGTM! Import path updated correctly.packages/ui/package.json (1)
19-19: LGTM! Necessary dependency for the centralized utilities.The addition of
@hypr/utilswith workspace protocol is correct for the monorepo structure and enables the UI package to consume centralized utilities.apps/desktop/src/routes/app/settings/_layout.tsx (1)
14-14: LGTM! Import path migrated correctly.apps/desktop/src/components/settings/ai/llm/select.tsx (1)
5-5: LGTM! Import path updated correctly.apps/desktop/src/components/chat/message/normal.tsx (1)
1-1: All verification points confirmed. The import change is safe.The
@hypr/utilspackage properly exportsformatDistanceToNowthroughexport * from "date-fns", ensuring the function signature and behavior match date-fns. The underlying date-fns version (4.1.0) is modern and supports all standard options.apps/desktop/src/components/settings/ai/stt/select.tsx (1)
5-5: LGTM! Import path updated correctly.The cn utility import has been successfully migrated to
@hypr/utilsas part of the broader refactoring to centralize utilities. Usage remains consistent throughout the file.apps/desktop/src/components/main/body/sessions/note-input/raw.tsx (1)
9-9: LGTM! Import path updated correctly.The cn utility import has been successfully migrated to
@hypr/utils, consistent with the project-wide refactoring effort.apps/desktop/src/components/settings/ai/shared/model-combobox.tsx (1)
15-15: LGTM! Import path updated correctly.The cn utility import has been successfully migrated to
@hypr/utils. The function is used extensively throughout the file for managing conditional Tailwind classes, and all usages remain correct.apps/desktop/src/components/main/sidebar/profile/banner.tsx (1)
5-5: LGTM! Import path updated correctly.The cn utility import has been successfully migrated to
@hypr/utils, aligning with the broader refactoring to centralize utilities.apps/desktop/src/components/chat/interactive.tsx (1)
4-4: LGTM! Import path updated correctly.The cn utility import has been successfully migrated to
@hypr/utils, consistent with the project-wide utility consolidation effort.apps/desktop/src/components/main/body/contacts/organizations.tsx (1)
4-4: LGTM! Import path updated correctly.The cn utility import has been successfully migrated to
@hypr/utils. Usage throughout the file remains consistent and correct.apps/desktop/src/components/main/body/search.tsx (1)
5-5: LGTM! Import path updated correctly.The cn utility import has been successfully migrated to
@hypr/utils. This file makes extensive use of cn for conditional Tailwind classes, which aligns with the project's coding guidelines.apps/desktop/src/devtool/tinytick.tsx (1)
4-4: LGTM! Import path updated for centralized utilities.The import path change from
@hypr/ui/lib/utilsto@hypr/utilsaligns with the project-wide refactoring to consolidate utilities into a centralized package.apps/desktop/src/components/chat/message/shared.tsx (1)
4-4: LGTM! Import path updated consistently.The cn utility import has been updated to use the centralized
@hypr/utilspackage.apps/desktop/src/components/chat/body/index.tsx (1)
4-4: LGTM! Consistent with the utilities refactoring.apps/desktop/src/components/main/sidebar/search/item.tsx (1)
4-4: LGTM! Import path updated.apps/desktop/src/components/main/body/sessions/title-input.tsx (1)
1-1: LGTM! Consistent import path update.apps/desktop/src/components/main/sidebar/timeline/index.tsx (1)
2-2: LGTM! Import path updated.apps/desktop/src/store/tinybase/persisted.ts (1)
1-1: The review comment is based on an incorrect understanding of the change.The import change from
"date-fns"to"@hypr/utils"is a simple path refactoring, not a replacement with a custom implementation. The@hypr/utilspackage re-exports all date-fns functions directly viaexport * from "date-fns", making the importedformatfunction identical to date-fns's format function.Key findings:
@hypr/utils/src/date.tsline 9 re-exports all date-fns functions unchanged- The
formatDatefunction defined in that file is a separate custom utility, not the importedformat- The
formatfunction called at lines 441 and 465 with"yyyy-MM-dd"tokens is the exact same date-fns function- API compatibility, edge case handling, and performance are guaranteed because it's the identical function
This is actually a beneficial refactoring—centralizing date utilities through
@hypr/utilsprovides a single point of control for date library imports.Likely an incorrect or invalid review comment.
apps/desktop/src/contexts/audio-player/timeline.tsx (1)
3-3: LGTM! Clean import path migration.The import path update for
cnfrom the UI package to the centralized utils package is consistent with the PR's consolidation objective.apps/desktop/src/components/chat/input.tsx (1)
4-4: LGTM! Import path updated correctly.The
cnutility import has been successfully migrated to the centralized utils package.apps/desktop/src/components/main/body/index.tsx (1)
2-2: LGTM! Consistent with the codebase-wide migration.The import path update aligns with the centralized utilities approach.
apps/desktop/src/devtool/index.tsx (1)
6-6: LGTM! Import migration applied correctly.The devtool component now uses the centralized
cnutility.apps/desktop/src/components/settings/ai/stt/configure.tsx (1)
13-13: LGTM! Settings component updated correctly.The import path migration is consistent with the broader refactoring effort.
apps/desktop/package.json (1)
35-35: LGTM! Required dependency added.The new
@hypr/utilsworkspace dependency enables the centralized utilities pattern used throughout the application.apps/desktop/src/components/chat/header.tsx (2)
7-7: LGTM! Import path updated correctly.The
cnutility import follows the same migration pattern as other files in this PR.
1-1: Import change is safe—API compatibility verified.The
@hypr/utilspackage properly re-exportsformatDistanceToNowfromdate-fnsviaexport * from "date-fns"inpackages/utils/src/date.ts, maintaining full API compatibility with the usage at line 172.apps/desktop/src/routes/app/onboarding.tsx (3)
15-15: LGTM! Import path migrated successfully.The
cnutility import has been updated to use the centralized utils package.
192-194: Second Button replacement looks consistent.This replacement follows the same pattern as the first one on lines 79-84, maintaining consistency throughout the onboarding flow.
79-84: Migration from PushableButton to Button is complete and correct.Verification confirms:
- No remaining PushableButton references in the codebase
- Button component properly imported and used in onboarding.tsx with correct props (onClick, className)
- ButtonProps extends React.ButtonHTMLAttributes, fully supporting onClick, className, and standard button behavior
- Visual styling maintained through CVA-based variants and className composition
- All functionality preserved in the migration
apps/desktop/src/components/main/body/contacts/people.tsx (1)
3-3: Import path updated correctly.The cn utility import has been successfully migrated to the centralized @hypr/utils package, aligning with the PR's consolidation objective.
apps/desktop/src/components/chat/trigger.tsx (1)
1-1: Import path migrated correctly.The transition to @hypr/utils is consistent with the project-wide utility consolidation effort.
apps/desktop/src/components/main/sidebar/search/index.tsx (1)
3-3: Utility import centralization complete.The import path change to @hypr/utils is correct and consistent with the refactoring strategy.
packages/utils/package.json (1)
16-16: Dependencies added for cn utility support.The clsx (v2.1.1) and tailwind-merge (v2.6.0) dependencies are the correct libraries and versions for the centralized cn utility implementation. Based on learnings.
Also applies to: 21-21
apps/desktop/src/utils/timeline.ts (1)
1-1: Date utilities consolidated effectively.The import consolidation to @hypr/utils is correct, and all required date-fns functions are properly imported and used throughout the file.
packages/utils/src/index.ts (1)
1-2: Public API surface expanded correctly.The re-exports for cn and date utilities properly expose the new centralized utilities alongside existing fetch exports.
packages/utils/src/cn.ts (1)
1-22: Well-implemented utility with excellent documentation.The cn utility correctly combines clsx for conditional class composition and tailwind-merge for conflict resolution. The comprehensive JSDoc documentation with practical examples will help developers use this utility effectively.
packages/utils/src/date.ts (2)
64-76: Date range formatting logic is sound.The intelligent same-day detection using
isSameDayensures appropriate formatting for both single-day and multi-day ranges.
84-91: URL parsing is defensive and correct.The try-catch wrapper appropriately handles malformed URLs by falling back to the original string.
apps/desktop/src/components/main/body/calendars.tsx (1)
1-1: Replaceclsximports withcnutility from@hypr/utilsto standardize class merging.Nine files incorrectly import
clsxdirectly from the "clsx" package and should usecninstead. Thecnutility (defined in packages/utils/src/cn.ts) wraps clsx withtwMergefor proper Tailwind class conflict resolution. Update the following files:
- apps/desktop/src/routes/app/auth.tsx:5
- apps/desktop/src/components/main/body/calendars.tsx:2
- apps/desktop/src/components/main/body/shared.tsx:4
- apps/desktop/src/components/main/sidebar/index.tsx:3
- apps/desktop/src/components/main/sidebar/timeline/index.tsx:4
- apps/desktop/src/components/main/sidebar/profile/notification.tsx:3
- apps/desktop/src/components/main/sidebar/profile/shared.tsx:1
- apps/desktop/src/components/main/sidebar/profile/index.tsx:3
- apps/desktop/src/components/main/sidebar/profile/ota.tsx:7
Replace
import { clsx } from "clsx"withimport { cn } from "@hypr/utils"and update allclsx(...)calls tocn(...).⛔ Skipped due to learnings
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"`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} : Split cn array entries by logical grouping when composing Tailwind classNamesLearnt 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} : Always pass an array to cn when composing Tailwind classNamesapps/desktop/src/components/main/body/sessions/floating/listen.tsx (1)
3-5: LGTM - Import changes support the local SoundIndicator implementation.The addition of
useEffectand the change from importingSoundIndicatortoDancingStickscorrectly support the new local implementation that replaces the removed UI package component.
| @@ -1,9 +1,9 @@ | |||
| import { addMonths, eachDayOfInterval, endOfMonth, format, getDay, isSameMonth, startOfMonth } from "@hypr/utils"; | |||
| import { clsx } from "clsx"; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.tsAnd 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.
| <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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| import { useState } from "react"; | ||
|
|
||
| import { cn } from "@hypr/ui/lib/utils"; | ||
| import { cn } from "@hypr/utils"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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:2packages/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.
| 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" />; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Handle empty array edge case and verify value range assumptions.
The amplitude calculation has a potential edge case issue:
- Empty array bug (Line 96-98): If
valueis an empty array, the calculation0 / value.lengthproducesNaN, which will propagate through toDancingSticks. 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]);- Verify value range: The use of
u16max = 65535assumes unsigned 16-bit values. Ifamplitude.micoramplitude.speakercan 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 -100Length 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.
| 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.
| const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { | ||
| if (!searchQuery.trim() || searchResults.length === 0) { | ||
| return; | ||
| } | ||
|
|
||
| if (e.key === "ArrowDown") { | ||
| e.preventDefault(); | ||
| setSelectedIndex((prev) => (prev < searchResults.length - 1 ? prev + 1 : prev)); | ||
| } else if (e.key === "ArrowUp") { | ||
| e.preventDefault(); | ||
| setSelectedIndex((prev) => (prev > 0 ? prev - 1 : 0)); | ||
| } else if (e.key === "Enter") { | ||
| e.preventDefault(); | ||
| if (selectedIndex >= 0 && selectedIndex < searchResults.length) { | ||
| handleSelectParticipant(searchResults[selectedIndex].id); | ||
| } | ||
| } else if (e.key === "Escape") { | ||
| e.preventDefault(); | ||
| setIsFocused(false); | ||
| onSearchChange?.(""); | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Always handle Escape; don’t early-return before key processing.
Escape should clear/close even when there are no results.
- const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
- if (!searchQuery.trim() || searchResults.length === 0) {
- return;
- }
-
- if (e.key === "ArrowDown") {
+ const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
+ if (e.key === "Escape") {
+ e.preventDefault();
+ setIsFocused(false);
+ onSearchChange?.("");
+ return;
+ }
+ if (!searchQuery.trim() || searchResults.length === 0) {
+ return;
+ }
+ if (e.key === "ArrowDown") {
e.preventDefault();
setSelectedIndex((prev) => (prev < searchResults.length - 1 ? prev + 1 : prev));
} else if (e.key === "ArrowUp") {
e.preventDefault();
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : 0));
} else if (e.key === "Enter") {
e.preventDefault();
if (selectedIndex >= 0 && selectedIndex < searchResults.length) {
handleSelectParticipant(searchResults[selectedIndex].id);
}
- } else if (e.key === "Escape") {
- e.preventDefault();
- setIsFocused(false);
- onSearchChange?.("");
}
};📝 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.
| const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { | |
| if (!searchQuery.trim() || searchResults.length === 0) { | |
| return; | |
| } | |
| if (e.key === "ArrowDown") { | |
| e.preventDefault(); | |
| setSelectedIndex((prev) => (prev < searchResults.length - 1 ? prev + 1 : prev)); | |
| } else if (e.key === "ArrowUp") { | |
| e.preventDefault(); | |
| setSelectedIndex((prev) => (prev > 0 ? prev - 1 : 0)); | |
| } else if (e.key === "Enter") { | |
| e.preventDefault(); | |
| if (selectedIndex >= 0 && selectedIndex < searchResults.length) { | |
| handleSelectParticipant(searchResults[selectedIndex].id); | |
| } | |
| } else if (e.key === "Escape") { | |
| e.preventDefault(); | |
| setIsFocused(false); | |
| onSearchChange?.(""); | |
| } | |
| }; | |
| const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { | |
| if (e.key === "Escape") { | |
| e.preventDefault(); | |
| setIsFocused(false); | |
| onSearchChange?.(""); | |
| return; | |
| } | |
| if (!searchQuery.trim() || searchResults.length === 0) { | |
| return; | |
| } | |
| if (e.key === "ArrowDown") { | |
| e.preventDefault(); | |
| setSelectedIndex((prev) => (prev < searchResults.length - 1 ? prev + 1 : prev)); | |
| } else if (e.key === "ArrowUp") { | |
| e.preventDefault(); | |
| setSelectedIndex((prev) => (prev > 0 ? prev - 1 : 0)); | |
| } else if (e.key === "Enter") { | |
| e.preventDefault(); | |
| if (selectedIndex >= 0 && selectedIndex < searchResults.length) { | |
| handleSelectParticipant(searchResults[selectedIndex].id); | |
| } | |
| } | |
| }; |
🤖 Prompt for AI Agents
In apps/desktop/src/components/main/body/sessions/outer-header/metadata.tsx
around lines 139 to 160, the current early return when searchQuery is empty or
searchResults is empty prevents the Escape key from being handled; move the
Escape handling above that early return (or remove the early return and guard
only key-specific branches) so that when e.key === "Escape" you always call
e.preventDefault(), setIsFocused(false), and call onSearchChange?.("")
regardless of search state; keep the existing behavior for ArrowDown, ArrowUp,
and Enter but only run those when there are results.
|
|
||
| import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@hypr/ui/components/ui/accordion"; | ||
| import { cn } from "@hypr/ui/lib/utils"; | ||
| import { cn } from "@hypr/utils"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
LGTM! Import path updated correctly.
The cn utility import has been successfully migrated to @hypr/utils. This change is part of the broader effort to consolidate utilities into a centralized package.
Since this is a widespread change across many files, please verify that all cn imports throughout the codebase have been updated consistently:
🏁 Script executed:
#!/bin/bash
# Description: Verify all cn imports are using the new @hypr/utils path
# and identify any remaining old imports
echo "=== Checking for old cn imports from @hypr/ui/lib/utils ==="
rg -n 'from ["\x27]@hypr/ui/lib/utils["\x27]' --type tsx --type ts -g '!node_modules' -g '!dist' -g '!build'
echo ""
echo "=== Checking for new cn imports from @hypr/utils ==="
rg -n 'from ["\x27]@hypr/utils["\x27]' --type tsx --type ts -g '!node_modules' -g '!dist' -g '!build' -C 0 | head -20
echo ""
echo "=== Verifying @hypr/utils package exists ==="
fd -t f 'package.json' packages/utilsLength of output: 243
🏁 Script executed:
#!/bin/bash
# Description: Verify cn import migration with glob patterns
echo "=== Checking for old cn imports from @hypr/ui/lib/utils ==="
rg -n 'from ["\x27]@hypr/ui/lib/utils["\x27]' -g '*.ts' -g '*.tsx' -g '!node_modules' -g '!dist' -g '!build'
echo ""
echo "=== Checking for new cn imports from @hypr/utils ==="
rg -n 'from ["\x27]@hypr/utils["\x27]' -g '*.ts' -g '*.tsx' -g '!node_modules' -g '!dist' -g '!build'
echo ""
echo "=== Verify specific file has correct import ==="
grep -n 'import.*cn.*from.*@hypr/utils' apps/desktop/src/components/settings/ai/llm/configure.tsx
echo ""
echo "=== Verify cn is actually used in the file ==="
grep -n '\bcn(' apps/desktop/src/components/settings/ai/llm/configure.tsx | head -5Length of output: 6557
Migration incomplete: Two files still using old import path.
The file apps/desktop/src/components/settings/ai/llm/configure.tsx has been correctly updated to import cn from @hypr/utils. However, the broader codebase migration is incomplete. Two files still import from the old path and must be updated:
packages/ui/src/components/ui/progressive-blur.tsx:2packages/ui/src/components/ui/kbd.tsx:1
Update these files to import cn from @hypr/utils to complete the refactoring effort.
🤖 Prompt for AI Agents
packages/ui/src/components/ui/progressive-blur.tsx line 2 and
packages/ui/src/components/ui/kbd.tsx line 1: both files still import cn from
the old path; update their import statements to import cn from "@hypr/utils"
instead of the previous module path, and ensure any eslint/tsconfig aliases are
satisfied so the new path resolves correctly.
• Added date utilities and class name merger
• Updated import path for class names
• Removed unused legacy code
• Removed pushable button component
• Implemented direct implementation