Skip to content

Overall redesign final 0930#1514

Merged
yujonglee merged 54 commits intomainfrom
overall-redesign-final-0930
Sep 30, 2025
Merged

Overall redesign final 0930#1514
yujonglee merged 54 commits intomainfrom
overall-redesign-final-0930

Conversation

@duckduckhero
Copy link
Contributor

No description provided.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 30, 2025

📝 Walkthrough

Walkthrough

Introduces a tabbed editor area with “Summary/Enhanced”, “Memos/Raw”, and “Transcript” tabs; adds TranscriptViewer, EnhancedNoteSubHeader, and LocalSearchBar with search/replace styling. Updates header chips styling and title autofocus. Refactors right panel to always show chat with dynamic quick actions and model info modal. Adds session activeTab state and TipTap search integration.

Changes

Cohort / File(s) Summary
Editor Area Core & New UI
apps/desktop/src/components/editor-area/index.tsx, .../local-search-bar.tsx, .../transcript-viewer.tsx, .../metadata-modal.tsx
Converts single-surface editor to tabbed layout; integrates TranscriptViewer; adds LocalSearchBar with search/replace; adds hover MetadataModal. Keyboard shortcut toggles local search.
Note Header & Tabs
apps/desktop/src/components/editor-area/note-header/index.tsx, .../note-header/tab-header.tsx, .../note-header/tab-sub-header.tsx, .../note-header/sub-headers/enhanced-note-sub-header.tsx, .../note-header/sub-headers/transcript-sub-header.tsx, .../note-header/title-input.tsx
Adds TabHeader/TabSubHeader with auto-switching and visibility; introduces Enhanced/Transcript sub-headers; adds TitleInput autoFocus behavior and minor spacing tweaks.
Header Chips Styling
.../note-header/chips/event-chip.tsx, .../note-header/chips/participants-chip.tsx, .../note-header/chips/tag-chip.tsx, .../note-header/chips/index.tsx
Neutralizes icon/text colors; exports ParticipantsChipInner; removes ShareChip rendering.
Right Panel Chat Stack
apps/desktop/src/components/right-panel/index.tsx, .../views/chat-view.tsx, .../components/chat/chat-input.tsx, .../components/chat/empty-chat-state.tsx, .../components/chat-model-info-modal.tsx, .../utils/dynamic-quickactions.ts
Always renders ChatView; adds fixed FloatingActionButtons area; introduces ChatModelInfoModal and model badge; EmptyChatState gains session-aware dynamic quick actions.
Panels, Routes, Toolbar
apps/desktop/src/contexts/right-panel.tsx, apps/desktop/src/routes/app.note.$id.tsx, apps/desktop/src/components/toolbar/bars/main-toolbar.tsx, apps/desktop/src/components/pro-gate-modal/index.tsx, apps/desktop/src/components/search-bar.tsx
Default right panel view set to chat; minor padding change; re-enables ShareButton, removes TranscriptPanelButton; adjusts pro-gate navigation delay; makes top search bar non-interactive.
TipTap Editor & Styling
packages/tiptap/src/editor/index.tsx, packages/tiptap/src/shared/extensions.ts, packages/tiptap/src/transcript/index.tsx, packages/tiptap/src/styles/transcript.css, apps/desktop/src/styles/globals.css, packages/ui/src/components/ui/command.tsx
Adds Document extension; wires SearchAndReplace with result classes; adjusts transcript editor padding/wrapping; adds CSS for search result highlights; widens CommandDialog container.
Session Store
packages/utils/src/stores/session.ts
Adds activeTab state and setActiveTab action; syncs showRaw for raw/enhanced tabs; initializes activeTab to "raw".

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant EA as EditorArea
  participant TH as TabHeader
  participant SS as Session Store
  participant EN as EnhancedNoteSubHeader
  participant TV as TranscriptViewer
  participant ED as Editor

  U->>EA: Open note
  EA->>TH: Render tabs
  TH->>SS: Read activeTab / status
  alt activeTab == transcript
    EA->>TV: Mount TranscriptViewer(sessionId)
  else activeTab == enhanced
    EA->>ED: Render Enhanced content
    EA->>EN: Render Enhanced sub-header
  else activeTab == raw
    EA->>ED: Render Raw editor/renderer
  end
  EN-->>EA: onEnhance({manual|template})
  EA->>SS: Mark enhancing/progress
  TH->>SS: Auto-switch to enhanced when enhancing/finished
Loading
sequenceDiagram
  autonumber
  actor U as User
  participant LSB as LocalSearchBar
  participant TER as TranscriptEditorRef
  participant SAR as Search&Replace storage

  U->>LSB: Open (Cmd/Ctrl+F)
  LSB->>TER: commands.setSearchTerm(term)
  TER->>SAR: Update results
  SAR-->>LSB: {index,total}
  U->>LSB: Next/Prev
  LSB->>TER: nextSearchResult / previousSearchResult
  TER->>SAR: Current index
  SAR-->>LSB: index
  U->>LSB: Replace / Replace All
  LSB->>TER: replace / replaceAll
  U->>LSB: Escape
  LSB->>LSB: Clear + onClose
Loading
sequenceDiagram
  autonumber
  actor U as User
  participant CI as ChatInput
  participant MI as ChatModelInfoModal
  participant W as Windows API
  participant Nav as Navigation

  U->>CI: Click model badge
  CI->>MI: Open modal
  U->>MI: Click "Choose"
  MI->>W: windowShow({settings})
  Note over MI: wait 800ms
  MI->>Nav: emit navigate /app/settings?tab=ai-llm
  MI->>MI: Close
Loading
sequenceDiagram
  autonumber
  participant CV as ChatView
  participant ECS as EmptyChatState
  participant DQA as getDynamicQuickActions
  participant SES as Session/Env Services

  CV->>ECS: Render(sessionId)
  ECS->>DQA: fetch actions(sessionId)
  DQA->>SES: read session, participants, LLM state
  SES-->>DQA: context data
  DQA-->>ECS: QuickAction[]
  ECS-->>CV: Render buttons
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, leaving reviewers without any context or summary of the changes and objectives contained in this PR. Please add a concise description that outlines the scope, goals, and key modifications of this pull request to guide the review process.
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 title “Overall redesign final 0930” is too generic and includes a date suffix rather than succinctly summarizing the core change introduced by this PR, making it difficult for teammates to quickly grasp its primary purpose. Please revise the title to clearly and concisely highlight the main change—such as “Add multi-tab editing with transcript and local search”—and remove the date tag to improve clarity.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch overall-redesign-final-0930

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
🧪 Early access (Sonnet 4.5): enabled

We are currently testing the Sonnet 4.5 model, which is expected to improve code review quality. However, this model may lead to increased noise levels in the review comments. Please disable the early access features if the noise level causes any inconvenience.

Note:

  • Public repositories are always opted into early access features.
  • You can enable or disable early access features from the CodeRabbit UI or by updating the CodeRabbit configuration file.

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: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/desktop/src/components/search-bar.tsx (1)

57-82: Remove unused event handlers.

The handleInputChange and handleKeyDown functions are now dead code since the input field is disabled (line 146) and has pointer-events-none (line 145). These handlers will never fire, violating the coding guideline against unused functions.

Apply this diff to remove the unused handlers:

-  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
-    const value = e.target.value;
-    setSearchQuery(value);
-    setShowHistory(value === "" && isFocused);
-  };
-
-  const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
-    switch (e.key) {
-      case "ArrowDown":
-        e.preventDefault();
-        navigateResults("down");
-        break;
-      case "ArrowUp":
-        e.preventDefault();
-        navigateResults("up");
-        break;
-      case "Enter":
-        e.preventDefault();
-        selectResult();
-        break;
-      case "Escape":
-        e.preventDefault();
-        clearSearch();
-        break;
-    }
-  };
-

And remove their references from the input element:

         <input
           ref={searchInputRef}
           type="text"
           value={searchQuery}
-          onChange={handleInputChange}
-          onKeyDown={handleKeyDown}
           onFocus={() => {
🧹 Nitpick comments (37)
apps/desktop/src/components/toolbar/bars/main-toolbar.tsx (2)

60-60: Verify redundant route checking is intentional.

The ShareButton is conditionally rendered when isNote is true (line 60), which checks if the route matches /app/note/$id. However, examining the ShareButton implementation in apps/desktop/src/components/toolbar/buttons/share-button.tsx (lines 27-30), it internally performs the same route check using useParams and returns null if the param doesn't exist.

This creates redundant checking:

  • Outer: {isNote && <ShareButton />}
  • Inner: return param ? <ShareButtonInNote /> : null

If this defensive double-check is intentional for clarity or future-proofing, no action needed. Otherwise, consider removing the outer isNote condition since ShareButton handles the null case internally.

If you prefer to simplify, you could apply this diff:

-            {isNote && <ShareButton />}
+            <ShareButton />

62-62: Remove commented-out code.

The commented-out TranscriptPanelButton should be removed entirely rather than left as a comment. The component is no longer imported and cannot be uncommented without errors. Git history preserves the removal if needed later.

Apply this diff:

             {isNote && <ShareButton />}
             <ChatPanelButton />
-            {/*<TranscriptPanelButton />*/}
           </>
packages/ui/src/components/ui/command.tsx (1)

72-72: Simplify the excessive width value.

The w-[2000px] is an unusually large fixed width that will always be overridden by max-w-[450px]. This appears to be unintentional or a leftover from experimentation.

Consider simplifying to a more reasonable default width or removing the fixed width entirely:

-          "w-[2000px] max-w-[450px]",
+          "w-full max-w-[450px]",

This achieves the same visual result (450px max width) with clearer intent.

packages/tiptap/src/styles/transcript.css (1)

34-36: Review the white-space change and remove redundant properties.

Two observations:

  1. Behavior change: Switching from white-space: normal to white-space: pre-wrap will now preserve all whitespace and line breaks in the transcript content. This is a significant visual change—verify this is intentional for your transcript display use case.

  2. Redundant properties: Both word-break: break-word and overflow-wrap: break-word achieve the same word-wrapping behavior. The word-break: break-word property is deprecated; overflow-wrap: break-word alone is sufficient.

Apply this diff to remove the redundancy:

   white-space: pre-wrap;
-  word-break: break-word;
   overflow-wrap: break-word;
apps/desktop/src/styles/globals.css (1)

137-148: Consider using theme variables for search highlight colors.

The search result styles use hardcoded color values (#ffeb3b, #31e054). For better maintainability and theme consistency, consider using CSS custom properties or Tailwind theme tokens instead.

Example refactor using CSS variables:

+:root {
+  --search-result-bg: #ffeb3b;
+  --search-result-current-bg: #31e054;
+}
+
 .search-result {
-  background-color: #ffeb3b;
+  background-color: var(--search-result-bg);
   border-radius: 2px;
   padding: 1px 0;
 }

 .search-result-current {
-  background-color: #31e054 !important;
+  background-color: var(--search-result-current-bg) !important;
   border-radius: 2px;
   padding: 1px 0;
 }
apps/desktop/src/components/right-panel/index.tsx (1)

20-21: Consider removing commented code.

The commented conditional render on Line 20 can be removed to keep the codebase clean, unless you intend to restore it soon.

Apply this diff if the conditional is no longer needed:

-      {/*{(currentView === "transcript") ? <TranscriptView /> : <ChatView />}*/}
       <ChatView />
apps/desktop/src/components/editor-area/note-header/tab-sub-header.tsx (2)

5-13: Unused prop: transcriptEditorRef.

The transcriptEditorRef prop is declared but only used in the commented-out transcript sub-header code (Line 35). Consider removing it if the transcript feature is not imminent, or add a TODO comment if it's planned for future work.

Apply this diff if the prop is not needed yet:

 interface TabSubHeaderProps {
   sessionId: string;
   onEnhance?: (params: { triggerType: "manual" | "template"; templateId?: string | null }) => void;
   isEnhancing?: boolean;
-  transcriptEditorRef?: TranscriptEditorRef | null;
   progress?: number;
   showProgress?: boolean;
   hashtags?: string[];
 }

15-31: Unused prop: hashtags.

The hashtags prop is accepted by TabSubHeader but is not forwarded to EnhancedNoteSubHeader or used elsewhere. If EnhancedNoteSubHeader does not require hashtags, consider removing the prop from TabSubHeaderProps.

If hashtags is not needed, apply this diff:

 interface TabSubHeaderProps {
   sessionId: string;
   onEnhance?: (params: { triggerType: "manual" | "template"; templateId?: string | null }) => void;
   isEnhancing?: boolean;
   transcriptEditorRef?: TranscriptEditorRef | null;
   progress?: number;
   showProgress?: boolean;
-  hashtags?: string[];
 }
apps/desktop/src/components/right-panel/utils/dynamic-quickactions.ts (1)

213-281: Consider extracting lastAction logic to reduce duplication.

The lastAction generation logic (Lines 214-226 and 249-261) is duplicated between generatePostMeetingCustomWithTools and generatePostMeetingHyprCloud. Extracting it into a helper function would improve maintainability.

Example helper function:

function generateLastAction(hasParticipants: boolean, participants: any[]): QuickAction {
  return hasParticipants && participants.length > 0
    ? {
        shownTitle: `Past conversations with ${participants[0].full_name}...`,
        actualPrompt: `Search my Hyprnote for all past conversations and meetings with ${
          participants.map(p => p.full_name).join(", ")
        }`,
        eventName: "chat_dynamic_quickaction",
      }
    : {
        shownTitle: "Analyze related past meetings",
        actualPrompt: "Search my Hyprnote for related past meetings and show patterns or recurring topics",
        eventName: "chat_dynamic_quickaction",
      };
}

Then replace the duplicated blocks with calls to generateLastAction(hasParticipants, participants).

apps/desktop/src/components/right-panel/components/chat/empty-chat-state.tsx (1)

142-157: Remove commented-out code.

The beta notice is commented out (lines 143-157) and appears to be dead code. Per the coding guidelines, unused code should be removed rather than commented out.

Apply this diff to remove the dead code:

-      {/* Beta notice moved to bottom */}
-      {
-        /* <div className="mt-8 p-3 rounded-lg bg-neutral-50 border border-neutral-200 max-w-[280px]">
-        <p className="text-xs text-neutral-600 text-left">
-          <Trans>
-            Chat feature is in beta. For best results, we recommend you to use{" "}
-            <span
-              onClick={handleCustomEndpointsClick}
-              className="text-blue-600 hover:text-blue-800 cursor-pointer underline"
-            >
-              custom endpoints
-            </span>
-            .
-          </Trans>
-        </p>
-      </div> */
-      }
apps/desktop/src/components/right-panel/components/chat/chat-input.tsx (3)

53-58: Verify the aggressive refetch interval for LLM connection.

The refetchInterval: 5000 polls the LLM connection every 5 seconds. For a connection setting that typically changes infrequently, this may create unnecessary overhead and database/IPC calls.

Consider increasing the interval to 30000 (30s) or using refetchOnWindowFocus: true alone, unless the connection state is expected to change very frequently during active use.


60-65: Verify the aggressive refetch interval for custom LLM model.

Similar to the LLM connection query, refetchInterval: 5000 polls every 5 seconds. Custom model settings are unlikely to change frequently.

Consider increasing the interval to 30000 or removing it entirely, relying on refetchOnWindowFocus or manual invalidation when settings change.


67-71: Verify the aggressive refetch interval for HyprCloud enabled state.

The refetchInterval: 5000 polls every 5 seconds. The HyprCloud enabled state is a configuration setting that typically doesn't change during normal operation.

Consider increasing the interval or removing it, relying instead on refetchOnWindowFocus or manual cache invalidation when the user changes settings.

apps/desktop/src/components/editor-area/local-search-bar.tsx (1)

84-95: Remove commented-out code.

Per coding guidelines, comments should be minimal and explain "why" not "what". Commented-out code should be removed unless there's a specific reason to keep it for reference.

Apply this diff to remove the commented-out block:

-  // Close on outside click
-  /*
-  useEffect(() => {
-    if (!isVisible) return;
-    const handleClickOutside = (event: MouseEvent) => {
-      if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
-        handleClose();
-      }
-    };
-    document.addEventListener("mousedown", handleClickOutside);
-    return () => document.removeEventListener("mousedown", handleClickOutside);
-  }, [isVisible]);
-  */
-

If this functionality is intentionally deferred for future implementation, consider opening an issue to track it instead of leaving commented code.

apps/desktop/src/components/editor-area/note-header/sub-headers/enhanced-note-sub-header.tsx (6)

85-93: Remove unnecessary try-catch block per coding guidelines.

Per the coding guidelines: "Do not add any error handling. Keep the existing one." This try-catch adds error handling where none existed before. The console.error also converts the failure into a silent no-op, which may hide genuine issues.

Based on coding guidelines

Remove the try-catch and let errors propagate naturally:

   const handleAddTemplate = async () => {
     setIsTemplateDropdownOpen(false);
-    try {
-      await windowsCommands.windowShow({ type: "settings" });
-      await windowsCommands.windowNavigate({ type: "settings" }, "/app/settings?tab=templates");
-    } catch (error) {
-      console.error("Failed to open settings/templates:", error);
-    }
+    await windowsCommands.windowShow({ type: "settings" });
+    await windowsCommands.windowNavigate({ type: "settings" }, "/app/settings?tab=templates");
   };

95-101: Remove commented-out code to improve maintainability.

Commented-out code adds clutter and should be removed. Use version control to retrieve old code if needed.

Based on coding guidelines

-  // Commented out share functionality
-  // const handleShareOpenChange = (newOpen: boolean) => {
-  //   setIsShareDropdownOpen(newOpen);
-  //   if (hasEnhancedNote) {
-  //     handleOpenStateChange(newOpen);
-  //   }
-  // };

147-169: Remove large commented-out JSX block.

This commented-out share functionality spans 23 lines. Remove it to reduce clutter.

Based on coding guidelines

-        {
-          /* Share button
-        <Popover open={isShareDropdownOpen} onOpenChange={handleShareOpenChange}>
-          <PopoverTrigger asChild>
-            <Button
-              variant="outline"
-              size="sm"
-              className="text-xs h-6 px-3 hover:bg-neutral-100"
-            >
-              <Share2 size={14} className="mr-1.5" />
-              Share
-            </Button>
-          </PopoverTrigger>
-          <PopoverContent
-            className="w-80 p-3 focus:outline-none focus:ring-0 focus:ring-offset-0"
-            align="end"
-            sideOffset={4}
-          >
-            <SharePopoverContent />
-          </PopoverContent>
-        </Popover>
-        */
-        }

228-242: Remove commented-out chevron button code.

Another block of commented-out code that should be removed.

Based on coding guidelines

-          {/* Commented out separate chevron button */}
-          {
-            /*
-          <PopoverTrigger asChild>
-            <Button
-              variant="outline"
-              size="sm"
-              disabled={isEnhancing}
-              className="rounded-l-none px-1.5 h-[28px] border-l-0 hover:bg-neutral-100 disabled:opacity-100"
-            >
-              <ChevronDownIcon size={14} />
-            </Button>
-          </PopoverTrigger>
-          */
-          }

13-13: Unused import should be removed.

The commented-out import useShareLogic is not used and should be removed per coding guidelines.

Based on coding guidelines

- // import { useShareLogic } from "../share-button-header";

36-37: Remove commented-out share logic.

This commented code adds no value and should be removed.

Based on coding guidelines

-  // Share functionality (currently commented out)
-  // const { hasEnhancedNote } = useShareLogic();
apps/desktop/src/components/editor-area/floating-search-box.tsx (6)

21-34: Comment explains "what" rather than "why".

The comment on line 21 states "NO useCallback, we want fresh ref every time" but doesn't explain why we want a fresh ref or what problem this solves. Comments should focus on "why" not "what" per coding guidelines.

Based on coding guidelines

Either remove the comment or explain the reasoning:

-  // Get the editor - NO useCallback, we want fresh ref every time
+  // Access editor without memoization to ensure we read the latest ref value

Or simply remove it if the code is self-explanatory.


40-61: Remove unnecessary try-catch and explanatory comments.

Lines 44-56 wrap commands in try-catch with console.warn. Per coding guidelines, avoid adding error handling. The comments "Editor might not be ready yet, ignore" explain "what" not "why."

Based on coding guidelines

Remove the try-catch and comments:

   const debouncedSetSearchTerm = useDebouncedCallback(
     (value: string) => {
       const editor = getEditor();
       if (editor && editor.commands) {
-        try {
-          editor.commands.setSearchTerm(value);
-          editor.commands.resetIndex();
-          setTimeout(() => {
-            const storage = editor.storage?.searchAndReplace;
-            const results = storage?.results || [];
-            setResultCount(results.length);
-            setCurrentIndex((storage?.resultIndex ?? 0) + 1);
-          }, 100);
-        } catch (e) {
-          // Editor might not be ready yet, ignore
-          console.warn("Editor not ready for search:", e);
-        }
+        editor.commands.setSearchTerm(value);
+        editor.commands.resetIndex();
+        setTimeout(() => {
+          const storage = editor.storage?.searchAndReplace;
+          const results = storage?.results || [];
+          setResultCount(results.length);
+          setCurrentIndex((storage?.resultIndex ?? 0) + 1);
+        }, 100);
       }
     },
-    [], // Empty deps to prevent infinite re-creation
+    [],
     300,
   );

67-76: Remove unnecessary try-catch block.

Similar to previous comment, this try-catch adds error handling that silently swallows errors.

Based on coding guidelines

   useEffect(() => {
     const editor = getEditor();
     if (editor && editor.commands) {
-      try {
-        editor.commands.setReplaceTerm(replaceTerm);
-      } catch (e) {
-        // Editor might not be ready yet, ignore
-      }
+      editor.commands.setReplaceTerm(replaceTerm);
     }
-  }, [replaceTerm]); // Removed getEditor from deps
+  }, [replaceTerm]);

112-124: Remove try-catch block from scroll logic.

Another unnecessary try-catch with a "what" comment.

Based on coding guidelines

   const scrollCurrentResultIntoView = useCallback(() => {
     const editor = getEditor();
     if (!editor) {
       return;
     }

-    try {
-      const editorElement = editor.view.dom;
-      const current = editorElement.querySelector(".search-result-current") as HTMLElement | null;
-      if (current) {
-        current.scrollIntoView({
-          behavior: "smooth",
-          block: "center",
-          inline: "nearest",
-        });
-      }
-    } catch (e) {
-      // Editor view not ready yet, ignore
-    }
+    const editorElement = editor.view.dom;
+    const current = editorElement.querySelector(".search-result-current") as HTMLElement | null;
+    if (current) {
+      current.scrollIntoView({
+        behavior: "smooth",
+        block: "center",
+        inline: "nearest",
+      });
+    }
   }, []);

218-228: Redundant wrapper div for search input.

Lines 219-228 wrap the Input in a div that only applies border/styling. The Input component can accept these classes directly, eliminating a DOM node.

-          <div className="flex items-center gap-1 bg-transparent border border-neutral-200 rounded px-2 py-1 flex-1">
-            <Input
-              className="h-6 border-0 focus-visible:ring-0 focus-visible:ring-offset-0 px-1 bg-transparent flex-1 text-sm"
-              value={searchTerm}
-              onChange={(e) => setSearchTerm(e.target.value)}
-              onKeyDown={handleKeyDown}
-              placeholder="Search..."
-              autoFocus
-            />
-          </div>
+          <Input
+            className="h-6 border border-neutral-200 rounded px-2 py-1 focus-visible:ring-0 focus-visible:ring-offset-0 bg-transparent flex-1 text-sm"
+            value={searchTerm}
+            onChange={(e) => setSearchTerm(e.target.value)}
+            onKeyDown={handleKeyDown}
+            placeholder="Search..."
+            autoFocus
+          />

Verify the Input component supports these classes without issues.


271-279: Redundant wrapper div for replace input.

Same issue as the search input wrapper.

-          <div className="flex items-center gap-1 bg-transparent border border-neutral-200 rounded px-2 py-1 flex-1">
-            <Input
-              className="h-6 border-0 focus-visible:ring-0 focus-visible:ring-offset-0 px-1 bg-transparent flex-1 text-sm"
-              value={replaceTerm}
-              onChange={(e) => setReplaceTerm(e.target.value)}
-              onKeyDown={handleKeyDown}
-              placeholder="Replace..."
-            />
-          </div>
+          <Input
+            className="h-6 border border-neutral-200 rounded px-2 py-1 focus-visible:ring-0 focus-visible:ring-offset-0 bg-transparent flex-1 text-sm"
+            value={replaceTerm}
+            onChange={(e) => setReplaceTerm(e.target.value)}
+            onKeyDown={handleKeyDown}
+            placeholder="Replace..."
+          />
apps/desktop/src/components/editor-area/note-header/tab-header.tsx (7)

30-44: Comments explain "what" rather than "why".

Comments on lines 30, 38, 41 describe what the code does rather than why it's structured this way. Per coding guidelines, prefer minimal comments focused on "why."

Based on coding guidelines

Consider removing or condensing these comments:

-    // Check if this is a meeting session (has transcript or is currently recording)
     const hasTranscript = session.words && session.words.length > 0;
     const isCurrentlyRecording = ongoingSessionStatus === "running_active" && ongoingSessionId === sessionId;
     const isSessionInactive = ongoingSessionStatus === "inactive" || session.id !== ongoingSessionId;
     const hasEnhancedMemo = !!session?.enhanced_memo_html;

     const canEnhanceTranscript = hasTranscript && isSessionInactive;

-    // Keep the "meeting session" concept for overall tab visibility
     const isMeetingSession = hasTranscript || isCurrentlyRecording || isEnhancing;

-    // BUT use floating button logic for Enhanced tab visibility
     const isEnhancePending = useEnhancePendingState(sessionId);
     const shouldShowEnhancedTab = hasEnhancedMemo || isEnhancePending || canEnhanceTranscript;
-
-    // Automatic tab switching logic following existing conventions

47-52: Comment on line 48 is verbose and explains "what".

The comment describes the condition rather than explaining any non-obvious reasoning.

Based on coding guidelines

Remove or simplify:

     useEffect(() => {
-      // When enhancement starts (immediately after recording ends) -> switch to enhanced note
       if (isEnhancePending || (ongoingSessionStatus === "inactive" && hasTranscript && shouldShowEnhancedTab)) {
         setActiveTab("enhanced");
       }
     }, [isEnhancePending, ongoingSessionStatus, hasTranscript, shouldShowEnhancedTab, setActiveTab]);

54-59: Comment explains "what" not "why".

Line 54 describes what the effect does.

Based on coding guidelines

-    // Set default tab to 'raw' for blank notes (no meeting session)
     useEffect(() => {
       if (!isMeetingSession) {
         setActiveTab("raw");
       }
     }, [isMeetingSession, setActiveTab]);

77-80: Comment is redundant with the code.

Line 77 repeats what the return statement clearly shows.

Based on coding guidelines

-    // Don't render tabs at all for blank notes (no meeting session)
     if (!isMeetingSession) {
       return null;
     }

84-131: Excessive inline JSX comments describing UI structure.

Comments on lines 84, 88, 90, 118 describe what each section is (e.g., "Tab container", "Raw Note Tab"). These are redundant with the JSX structure itself.

Based on coding guidelines

Remove these comments:

       <div className="relative">
-        {/* Tab container */}
         <div className="bg-white">
           <div className="flex px-8">
             <div className="flex border-b border-neutral-100 w-full">
-              {/* Raw Note Tab */}
-
-              {/* Enhanced Note Tab - show when session ended OR transcript exists OR enhanced memo exists */}
               {shouldShowEnhancedTab && (
                 <button
                   ...
                 >
                   Summary
                 </button>
               )}

               <button
                 ...
               >
                 Memos
               </button>

-              {/* Transcript Tab - always show */}
               <button
                 ...
               >
                 Transcript
                 {isCurrentlyRecording && <div className="w-2 h-2 bg-red-500 rounded-full animate-pulse" />}
               </button>
             </div>
           </div>
         </div>
       </div>

65-68: Nullish coalescing is unnecessary.

Line 67 uses isMeetingSession ?? false, but isMeetingSession is already a boolean (derived from boolean expressions on lines 32-39). The ?? false is redundant.

     useImperativeHandle(ref, () => ({
-      isVisible: isMeetingSession ?? false,
+      isVisible: isMeetingSession,
     }), [isMeetingSession]);

70-75: Redundant nullish coalescing in visibility notification.

Same issue as above—isMeetingSession is already a boolean.

     useEffect(() => {
       if (onVisibilityChange) {
-        onVisibilityChange(isMeetingSession ?? false);
+        onVisibilityChange(isMeetingSession);
       }
     }, [isMeetingSession, onVisibilityChange]);
apps/desktop/src/components/editor-area/index.tsx (4)

8-8: Remove commented code.

Commented imports and logic should be removed rather than left in the codebase. Use version control to preserve history if needed.

Apply this diff to remove commented code:

-// import { useRightPanel } from "@/contexts/right-panel";
-  // const { isExpanded: isRightPanelExpanded, togglePanel: toggleRightPanel } = useRightPanel();
-            } else {
-              // comment out to turn off auto-open chat panel
-
-              // if (!isRightPanelExpanded) {
-              // toggleRightPanel("chat");
-              // }
-            }
+            }

As per coding guidelines.

Also applies to: 133-133, 217-222


249-252: Simplify useMemo dependency array.

The dependency array [showRaw, showRaw ? rawContent : enhancedContent] is unnecessarily complex. While it works, listing all relevant dependencies is clearer and follows React best practices.

Apply this diff:

   const noteContent = useMemo(
     () => (showRaw ? rawContent : enhancedContent),
-    [showRaw, showRaw ? rawContent : enhancedContent],
+    [showRaw, rawContent, enhancedContent],
   );

312-329: Remove commented placeholder code.

The commented date placeholder and MetadataModal code should be removed to keep the codebase clean.

Apply this diff:

-      {/* Date placeholder - closer when search bar is visible */}
-      <div
-        className={cn([
-          "flex justify-center pb-4 px-8",
-          isFloatingSearchVisible ? "pt-1" : "pt-1", // ← Less top padding when search bar is visible
-        ])}
-      >
-        {
-          /*
-        <MetadataModal sessionId={sessionId} hashtags={hashtags}>
-          <div className="cursor-pointer px-2 py-1">
-            <span className="text-xs text-neutral-300 font-medium transition-colors">
-              Today, December 19, 2024
-            </span>
-          </div>
-        </MetadataModal>
-        */
-        }
-      </div>
-

As per coding guidelines.


405-415: Remove commented FloatingButton code.

Since the FloatingButton has been replaced by the new tab-based UI, the commented code should be removed to keep the codebase clean.

Apply this diff:

-      {
-        /**
-         * FloatingSearchBox temporarily disabled in favor of LocalSearchBar
-         * <FloatingSearchBox
-         *   key={activeTab}
-         *   editorRef={activeTab === 'transcript' ? transcriptRef : editorRef}
-         *   onClose={() => setIsFloatingSearchVisible(false)}
-         *   isVisible={isFloatingSearchVisible}
-         * />
-         */
-      }
-
-      {
-        /*<AnimatePresence>
-        <motion.div
-          className="absolute bottom-4 w-full flex justify-center items-center pointer-events-none z-10"
-          initial={{ y: 50, opacity: 0 }}
-          animate={{ y: 0, opacity: 1 }}
-          exit={{ y: 50, opacity: 0 }}
-          transition={{ duration: 0.2 }}
-        >
-
-          <div className="pointer-events-auto">
-            <FloatingButton
-              key={`floating-button-${sessionId}`}
-              handleEnhance={handleClickEnhance}
-              handleEnhanceWithTemplate={handleEnhanceWithTemplate}
-              templates={templatesQuery.data || []}
-              session={sessionStore.session}
-              isError={enhance.status === "error" && !isCancelled}
-              progress={progress}
-              showProgress={llmConnectionQuery.data?.type === "HyprLocal" && sessionId !== onboardingSessionId}
-              userId={userId}
-            />
-          </div>
-        </motion.div>
-      </AnimatePresence>*/
-      }

As per coding guidelines.

Also applies to: 437-462

📜 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 26d2ec0 and a4c6fb5.

⛔ Files ignored due to path filters (2)
  • apps/desktop/src/locales/en/messages.po is excluded by !**/*.po
  • apps/desktop/src/locales/ko/messages.po is excluded by !**/*.po
📒 Files selected for processing (33)
  • apps/desktop/src/components/editor-area/floating-search-box.tsx (1 hunks)
  • apps/desktop/src/components/editor-area/index.tsx (11 hunks)
  • apps/desktop/src/components/editor-area/local-search-bar.tsx (1 hunks)
  • apps/desktop/src/components/editor-area/metadata-modal.tsx (1 hunks)
  • apps/desktop/src/components/editor-area/note-header/chips/event-chip.tsx (3 hunks)
  • apps/desktop/src/components/editor-area/note-header/chips/index.tsx (1 hunks)
  • apps/desktop/src/components/editor-area/note-header/chips/participants-chip.tsx (2 hunks)
  • apps/desktop/src/components/editor-area/note-header/chips/tag-chip.tsx (1 hunks)
  • apps/desktop/src/components/editor-area/note-header/index.tsx (5 hunks)
  • apps/desktop/src/components/editor-area/note-header/sub-headers/enhanced-note-sub-header.tsx (1 hunks)
  • apps/desktop/src/components/editor-area/note-header/sub-headers/transcript-sub-header.tsx (1 hunks)
  • apps/desktop/src/components/editor-area/note-header/tab-header.tsx (1 hunks)
  • apps/desktop/src/components/editor-area/note-header/tab-sub-header.tsx (1 hunks)
  • apps/desktop/src/components/editor-area/note-header/title-input.tsx (3 hunks)
  • apps/desktop/src/components/editor-area/transcript-viewer.tsx (1 hunks)
  • apps/desktop/src/components/pro-gate-modal/index.tsx (1 hunks)
  • apps/desktop/src/components/right-panel/components/chat-model-info-modal.tsx (1 hunks)
  • apps/desktop/src/components/right-panel/components/chat/chat-input.tsx (6 hunks)
  • apps/desktop/src/components/right-panel/components/chat/empty-chat-state.tsx (3 hunks)
  • apps/desktop/src/components/right-panel/index.tsx (2 hunks)
  • apps/desktop/src/components/right-panel/utils/dynamic-quickactions.ts (1 hunks)
  • apps/desktop/src/components/right-panel/views/chat-view.tsx (1 hunks)
  • apps/desktop/src/components/search-bar.tsx (2 hunks)
  • apps/desktop/src/components/toolbar/bars/main-toolbar.tsx (2 hunks)
  • apps/desktop/src/contexts/right-panel.tsx (1 hunks)
  • apps/desktop/src/routes/app.note.$id.tsx (1 hunks)
  • apps/desktop/src/styles/globals.css (1 hunks)
  • packages/tiptap/src/editor/index.tsx (2 hunks)
  • packages/tiptap/src/shared/extensions.ts (2 hunks)
  • packages/tiptap/src/styles/transcript.css (1 hunks)
  • packages/tiptap/src/transcript/index.tsx (1 hunks)
  • packages/ui/src/components/ui/command.tsx (1 hunks)
  • packages/utils/src/stores/session.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{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:

  • packages/tiptap/src/editor/index.tsx
  • apps/desktop/src/components/pro-gate-modal/index.tsx
  • apps/desktop/src/components/editor-area/note-header/chips/participants-chip.tsx
  • packages/ui/src/components/ui/command.tsx
  • apps/desktop/src/components/editor-area/note-header/index.tsx
  • apps/desktop/src/components/editor-area/note-header/chips/index.tsx
  • apps/desktop/src/components/editor-area/note-header/tab-sub-header.tsx
  • apps/desktop/src/components/editor-area/metadata-modal.tsx
  • apps/desktop/src/components/right-panel/utils/dynamic-quickactions.ts
  • packages/utils/src/stores/session.ts
  • apps/desktop/src/routes/app.note.$id.tsx
  • apps/desktop/src/components/editor-area/note-header/chips/tag-chip.tsx
  • apps/desktop/src/components/editor-area/note-header/title-input.tsx
  • apps/desktop/src/components/right-panel/views/chat-view.tsx
  • packages/tiptap/src/shared/extensions.ts
  • apps/desktop/src/components/right-panel/components/chat/chat-input.tsx
  • apps/desktop/src/components/search-bar.tsx
  • apps/desktop/src/components/editor-area/note-header/chips/event-chip.tsx
  • apps/desktop/src/components/editor-area/index.tsx
  • apps/desktop/src/components/editor-area/transcript-viewer.tsx
  • apps/desktop/src/components/right-panel/components/chat/empty-chat-state.tsx
  • packages/tiptap/src/transcript/index.tsx
  • apps/desktop/src/components/editor-area/local-search-bar.tsx
  • apps/desktop/src/components/editor-area/note-header/tab-header.tsx
  • apps/desktop/src/components/editor-area/floating-search-box.tsx
  • apps/desktop/src/components/right-panel/components/chat-model-info-modal.tsx
  • apps/desktop/src/components/editor-area/note-header/sub-headers/enhanced-note-sub-header.tsx
  • apps/desktop/src/contexts/right-panel.tsx
  • apps/desktop/src/components/toolbar/bars/main-toolbar.tsx
  • apps/desktop/src/components/editor-area/note-header/sub-headers/transcript-sub-header.tsx
  • apps/desktop/src/components/right-panel/index.tsx
🧬 Code graph analysis (18)
apps/desktop/src/components/editor-area/note-header/index.tsx (3)
packages/utils/src/contexts/sessions.tsx (1)
  • useSession (49-74)
apps/desktop/src/hooks/enhance-pending.ts (1)
  • useTitleGenerationPendingState (18-30)
apps/desktop/src/components/editor-area/note-header/title-shimmer.tsx (1)
  • TitleShimmer (11-46)
apps/desktop/src/components/editor-area/note-header/tab-sub-header.tsx (3)
packages/tiptap/src/transcript/index.tsx (1)
  • TranscriptEditorRef (33-41)
packages/utils/src/contexts/sessions.tsx (1)
  • useSession (49-74)
apps/desktop/src/components/editor-area/note-header/sub-headers/enhanced-note-sub-header.tsx (1)
  • EnhancedNoteSubHeader (23-299)
apps/desktop/src/components/editor-area/metadata-modal.tsx (3)
apps/desktop/src/components/editor-area/note-header/chips/event-chip.tsx (1)
  • EventChip (37-307)
apps/desktop/src/components/editor-area/note-header/chips/participants-chip.tsx (1)
  • ParticipantsChip (53-113)
apps/desktop/src/components/editor-area/note-header/chips/tag-chip.tsx (1)
  • TagChip (20-77)
apps/desktop/src/components/right-panel/views/chat-view.tsx (5)
apps/desktop/src/components/right-panel/components/chat/floating-action-buttons.tsx (1)
  • FloatingActionButtons (19-67)
apps/desktop/src/components/right-panel/components/chat/chat-history-view.tsx (1)
  • ChatHistoryView (17-89)
apps/desktop/src/components/right-panel/components/chat/empty-chat-state.tsx (1)
  • EmptyChatState (14-160)
apps/desktop/src/components/right-panel/components/chat/chat-messages-view.tsx (1)
  • ChatMessagesView (45-136)
apps/desktop/src/components/right-panel/components/chat/chat-input.tsx (1)
  • ChatInput (34-540)
packages/tiptap/src/shared/extensions.ts (1)
packages/tiptap/src/transcript/extensions/search-and-replace.ts (1)
  • SearchAndReplace (229-381)
apps/desktop/src/components/right-panel/components/chat/chat-input.tsx (1)
apps/desktop/src/components/right-panel/components/chat-model-info-modal.tsx (1)
  • ChatModelInfoModal (12-134)
apps/desktop/src/components/editor-area/note-header/chips/event-chip.tsx (1)
packages/utils/src/datetime.ts (1)
  • formatRelativeWithDay (88-132)
apps/desktop/src/components/editor-area/index.tsx (6)
packages/utils/src/contexts/sessions.tsx (1)
  • useSession (49-74)
packages/tiptap/src/transcript/index.tsx (1)
  • TranscriptEditorRef (33-41)
apps/desktop/src/components/editor-area/note-header/tab-header.tsx (2)
  • TabHeaderRef (15-17)
  • TabHeader (19-137)
apps/desktop/src/components/editor-area/local-search-bar.tsx (1)
  • LocalSearchBar (15-290)
apps/desktop/src/components/editor-area/note-header/sub-headers/enhanced-note-sub-header.tsx (1)
  • EnhancedNoteSubHeader (23-299)
apps/desktop/src/components/editor-area/transcript-viewer.tsx (1)
  • TranscriptViewer (27-164)
apps/desktop/src/components/editor-area/transcript-viewer.tsx (5)
packages/tiptap/src/transcript/index.tsx (1)
  • TranscriptEditorRef (33-41)
apps/desktop/src/components/right-panel/hooks/useTranscript.ts (1)
  • useTranscript (8-91)
apps/desktop/src/contexts/hypr.tsx (1)
  • useHypr (63-69)
packages/utils/src/contexts/ongoing-session.tsx (1)
  • useOngoingSession (32-46)
apps/desktop/src/components/editor-area/note-header/chips/participants-chip.tsx (1)
  • ParticipantsChipInner (115-130)
apps/desktop/src/components/right-panel/components/chat/empty-chat-state.tsx (2)
apps/desktop/src/contexts/hypr.tsx (1)
  • useHypr (63-69)
apps/desktop/src/components/right-panel/utils/dynamic-quickactions.ts (1)
  • getDynamicQuickActions (33-91)
apps/desktop/src/components/editor-area/local-search-bar.tsx (2)
packages/tiptap/src/transcript/index.tsx (1)
  • TranscriptEditorRef (33-41)
packages/ui/src/components/ui/button.tsx (1)
  • Button (37-89)
apps/desktop/src/components/editor-area/note-header/tab-header.tsx (5)
apps/desktop/src/components/editor-area/note-header/index.tsx (2)
  • TabHeaderRef (91-91)
  • TabHeader (90-90)
packages/utils/src/contexts/sessions.tsx (1)
  • useSession (49-74)
packages/utils/src/contexts/ongoing-session.tsx (1)
  • useOngoingSession (32-46)
apps/desktop/src/hooks/enhance-pending.ts (1)
  • useEnhancePendingState (4-16)
packages/ui/src/lib/utils.ts (1)
  • cn (4-6)
apps/desktop/src/components/editor-area/floating-search-box.tsx (2)
packages/tiptap/src/transcript/index.tsx (1)
  • TranscriptEditorRef (33-41)
packages/ui/src/components/ui/button.tsx (1)
  • Button (37-89)
apps/desktop/src/components/right-panel/components/chat-model-info-modal.tsx (2)
packages/ui/src/components/ui/modal.tsx (4)
  • Modal (23-83)
  • ModalBody (99-105)
  • ModalTitle (130-136)
  • ModalDescription (143-148)
packages/ui/src/components/ui/button.tsx (1)
  • Button (37-89)
apps/desktop/src/components/editor-area/note-header/sub-headers/enhanced-note-sub-header.tsx (2)
packages/utils/src/contexts/ongoing-session.tsx (1)
  • useOngoingSession (32-46)
apps/desktop/src/utils/template-service.ts (1)
  • TemplateService (5-66)
apps/desktop/src/components/toolbar/bars/main-toolbar.tsx (1)
apps/desktop/src/components/toolbar/buttons/share-button.tsx (1)
  • ShareButton (28-31)
apps/desktop/src/components/editor-area/note-header/sub-headers/transcript-sub-header.tsx (2)
packages/tiptap/src/transcript/index.tsx (1)
  • TranscriptEditorRef (33-41)
packages/ui/src/components/ui/button.tsx (1)
  • Button (37-89)
apps/desktop/src/components/right-panel/index.tsx (2)
apps/desktop/src/contexts/right-panel.tsx (1)
  • useRightPanel (230-236)
apps/desktop/src/components/right-panel/views/chat-view.tsx (1)
  • ChatView (24-349)
🪛 Biome (2.1.2)
apps/desktop/src/components/right-panel/components/chat/chat-input.tsx

[error] 401-401: This code will never be reached ...

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

⏰ 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). (2)
  • GitHub Check: ci (macos, macos-14)
  • GitHub Check: ci (windows, windows-latest)
🔇 Additional comments (49)
apps/desktop/src/components/toolbar/bars/main-toolbar.tsx (1)

13-13: LGTM: Import correctly activated.

The ShareButton import is now active and used at line 60, consistent with re-enabling the share functionality.

apps/desktop/src/components/pro-gate-modal/index.tsx (1)

38-51: Verify event-based window sync or annotate the 800ms delay.

  • Confirm whether @hypr/plugin-windows provides a lifecycle event (e.g. window-ready/show) to avoid an arbitrary 800ms timeout.
  • If no event exists, add a comment explaining why 800ms was chosen.
packages/tiptap/src/editor/index.tsx (1)

4-4: LGTM!

The Document extension is correctly imported and added to the extensions array. This is a standard TipTap extension integration with no issues.

Also applies to: 36-36

apps/desktop/src/components/editor-area/note-header/chips/tag-chip.tsx (1)

60-60: LGTM!

The text-neutral-500 styling for the TagsIcon provides consistent neutral coloring.

apps/desktop/src/components/editor-area/note-header/title-input.tsx (1)

2-2: LGTM! Clean autofocus implementation.

The autofocus logic is correctly implemented with:

  • Proper ref usage and focus call
  • Correct useEffect dependencies
  • Cleanup to prevent memory leaks from the timeout

The 200ms delay is a reasonable debounce to allow the component to settle before focusing.

Also applies to: 10-10, 19-19, 22-22, 38-46, 50-50

apps/desktop/src/components/editor-area/note-header/chips/event-chip.tsx (1)

112-113: LGTM! Consistent neutral styling applied.

The text-neutral-500 color has been applied uniformly across all three rendering paths (onboarding, event with link, blank note), ensuring visual consistency throughout the chip display.

Also applies to: 148-152, 253-256

apps/desktop/src/components/editor-area/note-header/sub-headers/transcript-sub-header.tsx (1)

16-21: LGTM! Polling approach is appropriate for audio file existence check.

The 2.5-second polling interval is reasonable for tracking audio file availability without excessive server load.

packages/tiptap/src/shared/extensions.ts (1)

10-10: LGTM! SearchAndReplace extension properly configured.

The extension is correctly imported and configured with appropriate settings:

  • searchResultClass aligns with the CSS styling for search results
  • disableRegex: true simplifies the search UX by disabling regex patterns

Also applies to: 93-96

apps/desktop/src/components/editor-area/note-header/chips/index.tsx (1)

50-50: LGTM! ShareChip intentionally removed.

The ShareChip has been cleanly removed from the header chips, aligning with the broader UI redesign that shifts focus to tabbed editing and local search capabilities.

apps/desktop/src/components/search-bar.tsx (1)

121-121: LGTM! Search input correctly disabled for display-only mode.

The input has been properly converted to display-only by adding cursor-pointer to the container, pointer-events-none to the input, and the disabled attribute. This aligns with the new LocalSearchBar component handling actual search interactions.

Also applies to: 145-146

apps/desktop/src/routes/app.note.$id.tsx (1)

164-164: LGTM - layout adjustment.

The padding reduction from pt-6 to pt-4 aligns with the broader UI redesign for the tabbed editor layout.

packages/tiptap/src/transcript/index.tsx (1)

185-187: LGTM - improved scroll container layout.

The addition of min-h-0 on the scroll container (line 185) correctly prevents flex overflow issues, and the updated padding classes improve content spacing for the tabbed editor UI.

apps/desktop/src/components/editor-area/note-header/chips/participants-chip.tsx (2)

102-103: LGTM - consistent color styling.

The updated neutral color (text-neutral-500) for both the icon and label improves visual consistency with the redesigned UI.


115-130: LGTM - intentional public export.

Making ParticipantsChipInner a public export enables reuse in other parts of the editor area components, aligning with the broader PR's modular UI redesign.

apps/desktop/src/components/right-panel/index.tsx (1)

1-7: LGTM!

Import and hook usage updated correctly. Removed TranscriptView import and currentView destructuring align with the fixed ChatView render path.

apps/desktop/src/components/editor-area/note-header/index.tsx (4)

10-11: LGTM!

TabHeader and TabSubHeader imports are correctly wired and re-exported for external use.


27-33: LGTM!

The isNewNote flag logic correctly identifies a fresh note by checking title, HTML content, and words. The conditions handle edge cases appropriately.


59-71: LGTM!

Reduced padding and conditional autoFocus logic are correct. The autoFocus prop is appropriately gated by isNewNote and editable.


89-91: LGTM!

Public exports for TabHeader, TabSubHeader, and TabHeaderRef are correctly defined and align with the multi-tab editor layout.

apps/desktop/src/components/editor-area/note-header/tab-sub-header.tsx (1)

39-44: LGTM!

The empty sub-header for the "raw" tab maintains consistent layout spacing, and returning null for unmapped tabs is correct.

apps/desktop/src/components/right-panel/utils/dynamic-quickactions.ts (4)

4-31: LGTM!

The QuickAction interface and DEFAULT_ACTIONS are well-defined and provide a sensible fallback.


33-91: LGTM!

The main logic correctly handles session state, participants, LLM connection, and strategy selection. Error handling appropriately falls back to DEFAULT_ACTIONS.


93-182: LGTM!

Pre-meeting helper functions are well-structured. The delegation pattern in generatePreMeetingCustomNoTools avoids duplication, and the participant-aware actions in generatePreMeetingHyprCloud are context-appropriate.


1-3: LGTM!

Imports are clean and correctly scoped to the required plugin commands.

apps/desktop/src/components/right-panel/components/chat-model-info-modal.tsx (3)

1-10: LGTM!

Imports and interface are clean and correctly defined.


29-31: LGTM!

Early return when isOpen is false prevents unnecessary rendering.


33-133: LGTM!

Modal UI structure is correct. The custom backdrop and showOverlay={false} prevent duplicate overlays, and the "Choose" button is appropriately accessible.

packages/utils/src/stores/session.ts (2)

10-10: LGTM!

The activeTab state field and setActiveTab action are properly typed with a union of three tab values. The addition is clean and follows the existing store pattern.

Also applies to: 17-17


47-60: Backward compatibility logic is correct.

The setActiveTab implementation correctly maintains showRaw synchronization for "raw" and "enhanced" tabs while leaving it unchanged for "transcript". The comment on line 51 appropriately explains the reasoning.

apps/desktop/src/components/editor-area/metadata-modal.tsx (1)

28-34: Nice UX pattern for extended hover area.

The negative margin trick (line 30: p-4 -m-4) elegantly extends the hoverable area to prevent the popover from disappearing when the cursor moves between the trigger and popover. The arrow seamlessly connects the two elements.

apps/desktop/src/components/right-panel/views/chat-view.tsx (2)

271-280: Verify the reserved height inconsistency.

The reserved space for FloatingActionButtons differs between the history view (h-16 on line 273) and the main chat view (h-14 on line 301). This inconsistency could cause the floating buttons to be positioned differently or clipped.

Confirm whether this height difference is intentional (e.g., due to different button sizes or spacing requirements) or if both should use the same height for visual consistency.

Also applies to: 299-308


314-318: Good: sessionId prop enables dynamic quick actions.

Passing sessionId to EmptyChatState allows it to fetch session-specific quick actions via getDynamicQuickActions, which improves the user experience by showing contextually relevant suggestions.

apps/desktop/src/components/right-panel/components/chat/empty-chat-state.tsx (2)

26-28: Dynamic quick actions implementation is correct.

The useEffect properly fetches session-specific quick actions when sessionId changes, and the fallback to null ensures the function handles missing session IDs gracefully.


30-54: ResizeObserver pattern is correctly implemented.

The responsive sizing logic with ResizeObserver is properly set up with cleanup, and the width-based thresholds (300, 400) provide good breakpoints for small/medium/large layouts.

apps/desktop/src/components/right-panel/components/chat/chat-input.tsx (3)

470-470: LGTM! Updated placeholder text improves UX clarity.

The placeholder text now explicitly mentions the @ trigger for adding contexts, which helps guide users.


502-502: LGTM! Contextual placeholder during generation.

The updated placeholder text clearly indicates how to add context while generating, improving the user experience.


507-514: LGTM! Model selector button integrates well.

The model button with icon and dynamic label provides clear access to model information. The modal flow is properly wired with state management.

apps/desktop/src/components/editor-area/transcript-viewer.tsx (5)

34-49: Verify the polling interval approach for editor ref readiness.

The component uses a 100ms interval to repeatedly check if the editor ref is set, which could trigger multiple unnecessary onEditorRefChange callbacks if the ref is set and unset during the component lifecycle.

Consider tracking whether the notification has been sent to avoid duplicate callbacks:

   useEffect(() => {
+    let notified = false;
     // Initial notification
     if (onEditorRefChange) {
       onEditorRefChange(editorRef.current);
+      if (editorRef.current?.editor) notified = true;
     }
 
     // Check if ref gets set later
     const checkInterval = setInterval(() => {
-      if (editorRef.current?.editor && onEditorRefChange) {
+      if (!notified && editorRef.current?.editor && onEditorRefChange) {
         onEditorRefChange(editorRef.current);
+        notified = true;
         clearInterval(checkInterval);
       }
     }, 100);
 
     return () => clearInterval(checkInterval);
   }, [onEditorRefChange]);

Alternatively, consider using a callback ref pattern or the ref callback form to be notified exactly when the ref is attached.


77-84: Verify the conditional logic for setting words in the editor.

The condition words && words.length > 0 && !isLive ensures words are only set when the transcript is not live. However, this effect depends on isAtBottom and isLive, which may cause the effect to run multiple times unnecessarily when those values change without words changing.

Consider adding words to a separate ref or using a more specific dependency array to avoid redundant setWords calls:

   useEffect(() => {
     if (words && words.length > 0 && !isLive) {
       editorRef.current?.setWords(words);
       if (isAtBottom && editorRef.current?.isNearBottom()) {
         editorRef.current?.scrollToBottom();
       }
     }
-  }, [words, isAtBottom, isLive]);
+  }, [words, isLive]);

The isAtBottom check is only used for the scroll action, not for deciding whether to set words.


96-102: LGTM! Transcript update handler correctly persists changes.

The handleUpdate function fetches the current session and upserts the updated words, ensuring persistence of transcript edits.


114-150: LGTM! Live transcript rendering with auto-scroll is well implemented.

The live view correctly displays final and partial words with visual distinction, and the "Go to bottom" button appears appropriately when the user scrolls up.


171-267: LGTM! Speaker selector provides robust speaker assignment UI.

The memoized speaker selector with range options (current/all/fromHere) and participant integration provides a comprehensive speaker management experience. The conditional rendering based on session state and inactive status is appropriate.

apps/desktop/src/components/editor-area/local-search-bar.tsx (5)

44-60: Verify the hardcoded 100ms delay for reading search results.

The setTimeout(() => {...}, 100) delay is used to wait for the editor's storage to update after setting the search term. This hardcoded delay could be fragile if the editor update timing changes.

Consider whether the TipTap SearchAndReplace extension provides a callback or event when results are computed, which would be more reliable than a fixed timeout. If not, document why the delay is necessary:

// Wait for editor storage to update after setSearchTerm
setTimeout(() => {
  // ...
}, 100);

125-147: Verify consistent use of hardcoded delays in navigation handlers.

Both handleNext and handlePrevious use setTimeout(() => {...}, 100) to read updated state from editor storage. This pattern is repeated multiple times throughout the file.

If the 100ms delay is necessary due to TipTap's async update behavior, consider extracting it into a named constant at the top of the file to make the intent clear and enable easy adjustment:

const EDITOR_STORAGE_UPDATE_DELAY = 100; // ms to wait for editor storage updates

// Then use it consistently:
setTimeout(() => {
  // ...
}, EDITOR_STORAGE_UPDATE_DELAY);

24-33: LGTM! Type-safe editor extraction with proper null handling.

The getEditor helper correctly handles both TranscriptEditorRef and nested editor shapes, with appropriate null checks.


189-205: LGTM! Comprehensive keyboard navigation support.

The handleEnterNav function properly handles Enter and F3 keys with Shift modifier for bidirectional navigation, providing a good keyboard-driven UX.


207-289: LGTM! Clean and accessible search/replace UI.

The component renders a comprehensive search and replace interface with proper accessibility attributes (aria-hidden), disabled states, and visual feedback for result counts and navigation.

apps/desktop/src/components/editor-area/note-header/sub-headers/enhanced-note-sub-header.tsx (1)

67-67: Missing error handling for fetch call may cause silent failures.

The fetch call to cancel the local LLM endpoint lacks error handling. Network failures or server errors will fail silently, leaving no trace for debugging.

Add error handling:

       if (localLlmBaseUrl.data) {
-        fetch(`${localLlmBaseUrl.data}/cancel`, { method: "GET" });
+        fetch(`${localLlmBaseUrl.data}/cancel`, { method: "GET" }).catch((error) => {
+          console.error("Failed to cancel local LLM:", error);
+        });
       }

Likely an incorrect or invalid review comment.

apps/desktop/src/components/editor-area/index.tsx (1)

384-401: Sync showRaw with activeTab
The Editor/Renderer toggle relies on both activeTab and showRaw. Verify that switching tabs (e.g. to “enhanced” or “raw”) updates showRaw accordingly—via a useEffect or in the session store—to prevent mismatched views.

@yujonglee yujonglee added this to the v0.0.83 milestone Sep 30, 2025
@yujonglee yujonglee moved this to In Progress in Hyprnote Sep 30, 2025
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: 0

♻️ Duplicate comments (1)
apps/desktop/src/components/right-panel/components/chat/chat-input.tsx (1)

388-404: Verify unreachable code fix.

A previous review flagged unreachable code in this function (duplicate return statement in the "HyprLocal" case). The current code structure appears correct with the switch statement returning "Local LLM" for "HyprLocal" and falling through to the default case properly. Please confirm the unreachable return statement has been removed.

🧹 Nitpick comments (1)
apps/desktop/src/components/right-panel/components/chat/chat-input.tsx (1)

53-71: Consider increasing the refetch interval for configuration queries.

The 5-second polling interval for LLM configuration queries may be aggressive, as these settings typically change infrequently. Consider increasing the interval (e.g., 30-60 seconds) or using event-driven updates when settings change.

📜 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 a4c6fb5 and 28058cb.

📒 Files selected for processing (3)
  • apps/desktop/src/components/editor-area/note-header/chips/tag-chip.tsx (1 hunks)
  • apps/desktop/src/components/right-panel/components/chat/chat-input.tsx (6 hunks)
  • apps/desktop/src/components/right-panel/index.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/desktop/src/components/editor-area/note-header/chips/tag-chip.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{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/desktop/src/components/right-panel/components/chat/chat-input.tsx
  • apps/desktop/src/components/right-panel/index.tsx
🧬 Code graph analysis (2)
apps/desktop/src/components/right-panel/components/chat/chat-input.tsx (1)
apps/desktop/src/components/right-panel/components/chat-model-info-modal.tsx (1)
  • ChatModelInfoModal (12-134)
apps/desktop/src/components/right-panel/index.tsx (2)
apps/desktop/src/contexts/right-panel.tsx (1)
  • useRightPanel (230-236)
apps/desktop/src/components/right-panel/views/chat-view.tsx (1)
  • ChatView (24-349)
⏰ 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). (2)
  • GitHub Check: ci (macos, macos-14)
  • GitHub Check: ci (windows, windows-latest)
🔇 Additional comments (9)
apps/desktop/src/components/right-panel/components/chat/chat-input.tsx (6)

2-2: LGTM!

The new imports (BrainIcon, connectorCommands, ChatModelInfoModal) are all used in the file.

Also applies to: 8-8, 15-15


50-50: LGTM!

The isModelModalOpen state is properly initialized and used to control the modal.


408-421: LGTM!

The entity badge UI is well-structured with proper conditional rendering, icon display, and text truncation.


506-513: LGTM!

The model selection button is properly implemented with appropriate styling and state management.


532-536: LGTM!

The modal integration follows the standard controlled component pattern with proper state management.


469-469: LGTM!

The placeholder text updates provide clear guidance on the @ mention functionality. Minor note: "contexts" (line 469) vs "context" (line 501) is slightly inconsistent but acceptable.

Also applies to: 501-501

apps/desktop/src/components/right-panel/index.tsx (3)

20-20: LGTM: Simplified render logic.

The unconditional rendering of ChatView aligns with the PR objective to refactor the right panel to always show chat. The removal of view-switching logic simplifies the component and is consistent with the broader redesign.


4-4: Verify TranscriptView usage with broad search

Run the following script to locate any remaining references to TranscriptView in the repository:

#!/bin/bash
echo "=== TranscriptView occurrences across repo ==="
rg -n "TranscriptView" -C2 .

7-7: Ignore context API change suggestion. The RightPanelContext still exposes currentView, and it’s valid for components to destructure only the values they use.

Likely an incorrect or invalid review comment.

@yujonglee yujonglee merged commit 763f7e7 into main Sep 30, 2025
11 checks passed
@yujonglee yujonglee deleted the overall-redesign-final-0930 branch September 30, 2025 04:22
@github-project-automation github-project-automation bot moved this from In Progress to Done in Hyprnote Sep 30, 2025
@coderabbitai coderabbitai bot mentioned this pull request Nov 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

No open projects
Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants