Skip to content

Conversation

duckduckhero
Copy link
Collaborator

DON'T MERGE THIS

Copy link

coderabbitai bot commented Sep 25, 2025

📝 Walkthrough

Walkthrough

Adds tabbed editor flows (raw/enhanced/transcript), new search/replace UIs (LocalSearchBar, FloatingSearchBox) wired to a SearchAndReplace tiptap extension, introduces TranscriptViewer and speaker controls, updates note-header tabs/sub-headers, tweaks UI/spacing/styling, and adds session.activeTab state and Document extension.

Changes

Cohort / File(s) Summary
Editor Area (tabbed) & integration
apps/desktop/src/components/editor-area/index.tsx
Reworks EditorArea to support tabs (raw/enhanced/transcript), manages editor/transcript refs, adds global Ctrl/Cmd+F to toggle local search, wires TabHeader/TabSubHeader/TranscriptViewer and adjusts enhance-related hooks/flows.
Search & Replace UI
apps/desktop/src/components/editor-area/local-search-bar.tsx, apps/desktop/src/components/editor-area/floating-search-box.tsx
Adds LocalSearchBar and FloatingSearchBox components: search/replace inputs, result counts, navigation, replace/replace-all, keyboard handling, and bindings to editor.commands and storage.searchAndReplace.
Transcript viewer & speaker controls
apps/desktop/src/components/editor-area/transcript-viewer.tsx
Adds TranscriptViewer with live vs finished modes, editor initialization for finished transcripts, periodic editor-ref propagation, word upserts, and embedded SpeakerSelector with range selection and participant integration.
Note header: tabs, sub-headers, enhanced flows
apps/desktop/src/components/editor-area/note-header/tab-header.tsx, .../tab-sub-header.tsx, .../sub-headers/enhanced-note-sub-header.tsx, apps/desktop/src/components/editor-area/note-header/index.tsx
Adds TabHeader (with TabHeaderRef), TabSubHeader, and EnhancedNoteSubHeader components; exposes TabHeader/TabSubHeader exports and adjusts header spacing/title autofocus logic.
Header chips styling & exports
apps/desktop/src/components/editor-area/note-header/chips/event-chip.tsx, .../chips/participants-chip.tsx, .../chips/tag-chip.tsx, .../chips/index.tsx
Applies neutral text color classes to icons/text, exports ParticipantsChipInner, and comments out ShareChip rendering/import.
Session store: active tab state
packages/utils/src/stores/session.ts
Adds `activeTab: 'raw'
Tiptap extensions & editor changes
packages/tiptap/src/shared/extensions.ts, packages/tiptap/src/editor/index.tsx
Registers SearchAndReplace extension (configured with searchResultClass/disableRegex) and includes Document extension in the editor setup.
Styling and transcript CSS
apps/desktop/src/styles/globals.css, packages/tiptap/src/styles/transcript.css, packages/tiptap/src/transcript/index.tsx
Adds .search-result and .search-result-current styles; adjusts transcript white-space/wrapping rules and transcript editor container paddings.
Misc UI/layout tweaks
apps/desktop/src/components/toolbar/bars/main-toolbar.tsx, apps/desktop/src/components/search-bar.tsx, apps/desktop/src/components/right-panel/index.tsx, apps/desktop/src/components/right-panel/views/chat-view.tsx, apps/desktop/src/components/right-panel/components/chat/chat-input.tsx, apps/desktop/src/contexts/right-panel.tsx, apps/desktop/src/routes/app.note.$id.tsx, packages/ui/src/components/ui/command.tsx
Re-enables ShareButton in toolbar, makes search bar container clickable while disabling input, defaults right panel to chat and simplifies rendering, reserves space for FloatingActionButtons in chat views, updates chat input placeholders, reduces editor top padding, and widens CommandDialog container class.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant EditorArea
  participant TabHeader
  participant SessionStore
  participant Content as Editor/TranscriptViewer

  User->>EditorArea: Open note
  EditorArea->>SessionStore: read activeTab
  EditorArea->>TabHeader: render with sessionId
  TabHeader-->>EditorArea: user selects tab / auto-switch
  EditorArea->>SessionStore: setActiveTab(tab)
  EditorArea->>Content: render view per activeTab
Loading
sequenceDiagram
  autonumber
  actor User
  participant EditorArea
  participant LocalSearchBar
  participant Tiptap as TiptapEditor

  Note over EditorArea,LocalSearchBar: Ctrl/Cmd+F toggles LocalSearchBar
  User->>EditorArea: Press Ctrl/Cmd+F
  EditorArea->>LocalSearchBar: isVisible = true
  User->>LocalSearchBar: Type searchTerm
  LocalSearchBar->>Tiptap: commands.setSearchTerm(term)
  LocalSearchBar->>Tiptap: read storage.searchAndReplace (results, index)
  User->>LocalSearchBar: Enter / F3 / Shift+Enter
  LocalSearchBar->>Tiptap: nextResult()/prevResult()
  User->>LocalSearchBar: Replace / Replace All
  LocalSearchBar->>Tiptap: replace()/replaceAll()
  User->>LocalSearchBar: Escape / Close
  LocalSearchBar-->>EditorArea: onClose()
Loading
sequenceDiagram
  autonumber
  participant TranscriptViewer
  participant useTranscript
  participant TranscriptEditor
  participant DB as dbCommands

  TranscriptViewer->>useTranscript: subscribe(sessionId)
  useTranscript-->>TranscriptViewer: words, isLive
  alt isLive
    TranscriptViewer->>TranscriptViewer: render live words (scrolling)
  else finished
    TranscriptViewer->>TranscriptEditor: init with words
    TranscriptEditor-->>TranscriptViewer: onUpdate(delta)
    TranscriptViewer->>DB: upsertSession(words)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60–90 minutes

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The description “DON’T MERGE THIS” does not describe any part of the changeset and provides no context about the implemented features or objectives. This makes it impossible for reviewers to understand the purpose or scope of the pull request. Please update the pull request description to include a summary of the key changes, objectives, and any important background information to guide reviewers.
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% 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 0925” is overly generic and does not convey the primary changes made in the pull request or highlight any specific feature or component that was redesigned. It also includes a date code that adds noise without clarifying intent. As a result, it fails to provide clear context for future readers scanning the project history. Please choose a more descriptive title that succinctly summarizes the main change, such as “Refactor EditorArea to support tabbed views with LocalSearchBar and enhanced note headers.”
✨ 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-0925

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link

@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: 11

Caution

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

⚠️ Outside diff range comments (9)
apps/desktop/src/components/editor-area/note-header/chips/index.tsx (1)

7-7: Remove unused ShareChip import.

With the JSX commented out, ShareChip is no longer referenced. This will trigger our “no unused imports” rule and break lint/CI until the import is removed (or the chip restored). Please drop the import while it’s unused.

-import { ShareChip } from "./share-chip";
apps/desktop/src/components/search-bar.tsx (2)

113-124: Make the clickable container keyboard-accessible.

Disabled input removes native keyboard access. Add role, tabIndex, and Enter/Space handlers.

-      <div
+      <div
         className={clsx([
           "w-60 flex items-center gap-2 h-[34px]",
           "text-neutral-500 hover:text-neutral-600",
           "border border-border rounded-md px-2 py-2 bg-transparent",
           "hover:bg-white",
           isFocused && "bg-white",
           "transition-colors duration-200",
           "cursor-pointer",
         ])}
-        onClick={() => setShowCommandPalette(true)}
+        role="button"
+        tabIndex={0}
+        onClick={() => setShowCommandPalette(true)}
+        onKeyDown={(e) => {
+          if (e.key === "Enter" || e.key === " ") {
+            e.preventDefault();
+            setShowCommandPalette(true);
+          }
+        }}
       >

191-197: Prevent palette from opening when clearing search.

Clicking the XIcon bubbles to the parent and opens the Command Palette.

-          <XIcon
-            onClick={() => clearSearch()}
+          <XIcon
+            onClick={(e) => { e.stopPropagation(); clearSearch(); }}
             className="h-4 w-4 text-neutral-400 hover:text-neutral-600"
           />
apps/desktop/src/components/right-panel/components/chat/chat-input.tsx (2)

241-266: Fix XSS risk when constructing HTML for selection reference

selectionRef (derived from user/session data) is injected into both attribute and inner HTML without fully escaping. Escape all HTML entities for both the attribute and inner text.

Apply this diff:

-        // Escape quotes for HTML attribute
-        const escapedSelectionRef = selectionRef.replace(/"/g, "&quot;");
-
-        const referenceText =
-          `<a class="mention selection-ref" data-mention="true" data-id="selection-${pendingSelection.startOffset}-${pendingSelection.endOffset}" data-type="selection" data-label="${escapedSelectionRef}" contenteditable="false">${selectionRef}</a> `;
+        const escapeHtml = (s: string) =>
+          s.replace(/&/g, "&amp;")
+           .replace(/</g, "&lt;")
+           .replace(/>/g, "&gt;")
+           .replace(/"/g, "&quot;")
+           .replace(/'/g, "&#39;");
+        const escapedSelectionRef = escapeHtml(selectionRef);
+
+        const referenceText =
+          `<a class="mention selection-ref" data-mention="true" data-id="selection-${pendingSelection.startOffset}-${pendingSelection.endOffset}" data-type="selection" data-label="${escapedSelectionRef}" contenteditable="false">${escapedSelectionRef}</a> `;

281-309: Prevent double-submit on Enter

Enter handling is implemented twice (TipTap handleKeyDown + DOM keydown listener), which can trigger duplicate submissions. Keep the TipTap handler; remove the Enter branch from the DOM listener.

Apply this diff:

       const handleKeyDown = (event: KeyboardEvent) => {
         if (event.metaKey || event.ctrlKey) {
           if (["b", "i", "u", "k"].includes(event.key.toLowerCase())) {
             event.preventDefault();
             return;
           }
         }
-
-        if (event.key === "Enter" && !event.shiftKey) {
-          event.preventDefault();
-
-          if (inputValue.trim()) {
-            handleSubmit();
-          }
-        }
       };

Also applies to: 311-349

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

160-171: globalEditorRef may not update when the ref attaches. Use a callback ref.

Effects don’t re-run on ref.current mutations; globalEditorRef can remain null. Swap to a callback ref to reliably set/clear the global reference.

-  // Assign editor to global ref for access by other components (like chat tools)
-  useEffect(() => {
-    if (editorRef.current?.editor) {
-      globalEditorRef.current = editorRef.current.editor;
-    }
-    // Clear on unmount
-    return () => {
-      if (globalEditorRef.current === editorRef.current?.editor) {
-        globalEditorRef.current = null;
-      }
-    };
-  }, [editorRef.current?.editor]);
+  // Assign editor to global ref via a stable callback ref
+  const setEditorInstance = useCallback((instance: { editor: TiptapEditor | null } | null) => {
+    editorRef.current = instance;
+    globalEditorRef.current = instance?.editor ?? null;
+  }, []);
@@
-            <Editor
+            <Editor
               key={editorKey}
-              ref={editorRef}
+              ref={setEditorInstance}
               handleChange={handleChangeNote}
               initialContent={noteContent}
               editable={enhance.status !== "pending"}
               setContentFromOutside={!showRaw && enhance.status === "pending"}
               mentionConfig={{
                 trigger: "@",
                 handleSearch: handleMentionSearch,
               }}
             />
@@
-            <Renderer ref={editorRef} initialContent={noteContent} />
+            <Renderer ref={setEditorInstance} initialContent={noteContent} />

Also applies to: 386-399


251-259: Drop unused enhancement handlers.

These are only used in commented code.

-  const handleEnhanceWithTemplate = useCallback((templateId: string) => {
-    const targetTemplateId = templateId === "auto" ? null : templateId;
-    enhance.mutate({ templateId: targetTemplateId });
-  }, [enhance]);
-
-  const handleClickEnhance = useCallback(() => {
-    enhance.mutate({});
-  }, [enhance]);
+  // (handlers removed; re‑add if the FloatingButton returns)

643-645: Fix condition: contextText is always included (logic bug).

The OR chain is always true; this injects empty context into prompts. Use a truthy/string check.

-          ...((contextText !== "" || contextText !== undefined || contextText !== null) ? { contextText } : {}),
+          ...(contextText.trim() ? { contextText } : {}),

618-619: Safeguard optional sections access.

sections may be undefined on some templates.

-      const grammarSections = selectedTemplate?.sections.map(s => s.title) || null;
+      const grammarSections = selectedTemplate?.sections?.map(s => s.title) || null;
🧹 Nitpick comments (20)
apps/desktop/src/components/toolbar/bars/main-toolbar.tsx (1)

14-14: Prefer the @/ alias for consistency.

Every other toolbar button import in this file comes through the @/components/... alias; switching this one back to the same alias keeps the module specifiers uniform.

-import { ShareButton } from "../buttons/share-button";
+import { ShareButton } from "@/components/toolbar/buttons/share-button";
apps/desktop/src/contexts/right-panel.tsx (1)

63-69: DRY up repeated focus logic (tiny helper/const)

Focus-with-timeout logic and the 350ms delay are duplicated. Extract a small helper and a shared constant to reduce drift.

Also applies to: 76-88, 116-126, 130-135

apps/desktop/src/components/editor-area/floating-search-box.tsx (1)

123-132: Avoid magic numbers for storage sync delay

The 100ms timeouts recur in multiple places. Extract a STORAGE_SYNC_DELAY const to keep these in sync.

Also applies to: 139-145, 151-157, 164-170, 45-51

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

105-141: De‑duplicate extractEmojiAndName

This helper is copied from floating-button.tsx. Consider centralizing it (e.g., utils/templates.ts) to keep behavior consistent.

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

6-6: Remove redundant Document extension (StarterKit already includes it).

Avoid duplicate node definitions. StarterKit already brings Document; including it again is unnecessary and can cause confusion.

Apply:

-import Document from "@tiptap/extension-document";
       extensions: [
         ...shared.extensions,
-        Document,
         mention(mentionConfig),
       ],

Also applies to: 36-37

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

60-66: Simplify redundant className ternary.

Both branches return the same class.

-            <span className={`truncate ${totalTags === 0 ? "text-neutral-500" : "text-neutral-500"}`}>
+            <span className="truncate text-neutral-500">
apps/desktop/src/components/search-bar.tsx (1)

145-147: Prune unreachable input handlers/state.

With disabled + pointer-events-none, the input won’t fire focus/blur or key handlers; consider removing related state updates and handlers for clarity.

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

15-15: Good addition; consider moving the extension out of transcript to avoid cross‑module coupling.

Importing from ../transcript/... creates an odd dependency from shared → transcript. Prefer relocating search-and-replace under a shared extensions folder and importing from there.

Also applies to: 93-97

apps/desktop/src/styles/globals.css (1)

137-148: Deduplicate search highlight CSS.

.search-result* styles exist here and in packages/tiptap/src/styles/transcript.css. Centralize in one place to avoid drift.

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

221-226: Honor ChatInput autoFocus by focusing the editor

Currently autoFocus is unused. Focus the editor when enabled and not generating.

Apply this diff:

   useEffect(() => {
     if (chatInputRef && typeof chatInputRef === "object" && editorRef.current?.editor) {
       (chatInputRef as any).current = editorRef.current.editor.view.dom;
     }
   }, [chatInputRef]);
+
+  // Focus editor on mount when requested
+  useEffect(() => {
+    if (autoFocus && editorRef.current?.editor && !isGenerating) {
+      editorRef.current.editor.commands.focus("end");
+    }
+  }, [autoFocus, isGenerating]);

15-30: Remove unused onKeyDown prop (not wired) or wire it through

The onKeyDown prop is accepted but never invoked. Prefer deleting it to avoid confusion and lint noise.

Apply this diff:

 interface ChatInputProps {
   inputValue: string;
   onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
   onSubmit: (
     mentionedContent?: Array<{ id: string; type: string; label: string }>,
     selectionData?: SelectionData,
     htmlContent?: string,
   ) => void;
-  onKeyDown: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;
   autoFocus?: boolean;
   entityId?: string;
   entityType?: BadgeType;
   onNoteBadgeClick?: () => void;
   isGenerating?: boolean;
   onStop?: () => void;
 }
@@
   {
     inputValue,
     onChange,
     onSubmit,
-    onKeyDown,
     autoFocus = false,
     entityId,
     entityType = "note",
     onNoteBadgeClick,
     isGenerating = false,
     onStop,
   }: ChatInputProps,
 ) {
@@
-  }, [editorRef.current?.editor, onKeyDown, handleSubmit, inputValue]);
+  }, [editorRef.current?.editor, handleSubmit, inputValue]);

Follow-up: remove the onKeyDown prop at call sites (see ChatView).

Also applies to: 32-45, 348-349

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

12-14: Prefer direct re-exports to avoid local imports

Slightly cleaner module surface and avoids potential circular import issues later.

Apply this diff:

-import { TabHeader } from "./tab-header";
-import { TabSubHeader } from "./tab-sub-header";
+// (no local imports needed for re-exports)
@@
-// Export the TabHeader and TabSubHeader components for use outside this directory
-export { TabHeader, TabSubHeader };
-export type { TabHeaderRef } from "./tab-header";
+export { TabHeader } from "./tab-header";
+export { TabSubHeader } from "./tab-sub-header";
+export type { TabHeaderRef } from "./tab-header";

Also applies to: 92-95

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

88-96: Keep activeTab in sync when updating enhanced content

When updating enhanced note, also set activeTab='enhanced' for consistency.

Apply this diff:

     updateEnhancedNote: (note: string) => {
       set((state) => {
         const next = mutate(state, (draft) => {
           draft.showRaw = false;
+          draft.activeTab = 'enhanced';
           draft.session.enhanced_memo_html = note;
         });
         get().persistSession(next.session);
         return next;
       });
     },
apps/desktop/src/components/editor-area/note-header/tab-sub-header.tsx (1)

6-14: Trim unused props (transcriptEditorRef, hashtags)

Props are declared but not used, causing lint issues.

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[];
 }
 
-export function TabSubHeader({ sessionId, onEnhance, isEnhancing, transcriptEditorRef, progress, showProgress, hashtags }: TabSubHeaderProps) {
+export function TabSubHeader({ sessionId, onEnhance, isEnhancing, progress, showProgress }: TabSubHeaderProps) {

Also applies to: 16-16

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

337-338: Align with ChatInput prop removal (onKeyDown)

If you remove the unused onKeyDown prop per ChatInput review, drop it here too.

Apply this diff:

-          onKeyDown={handleKeyDown}
apps/desktop/src/components/editor-area/metadata-modal.tsx (1)

15-53: Improve accessibility: add keyboard/focus support for the popover.

Hover-only popover is not reachable via keyboard. Consider a focusable trigger (button) with onFocus/onBlur or a click-to-toggle state and aria attributes. Keeps UX parity and removes an a11y blocker.

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

187-193: Return null instead of empty elements for non-render cases.

Avoid stray nodes; return null when sessionId is unavailable or session is active.

-  if (!sessionId) {
-    return <p></p>;
-  }
+  if (!sessionId) {
+    return null;
+  }
@@
-  if (!inactive) {
-    return <p></p>;
-  }
+  if (!inactive) {
+    return null;
+  }

159-167: Avoid router coupling inside SpeakerSelector.

Using useMatch to pull sessionId ties this component to a specific route. Prefer passing sessionId from TranscriptViewer (e.g., define SpeakerSelector inside TranscriptViewer to capture it, or thread a prop through).

Also applies to: 162-167

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

89-97: Include handleClose in the Escape listener’s deps to avoid stale closures.

Prevents edge cases if onClose changes.

-  useEffect(() => {
+  useEffect(() => {
     if (!isVisible) return;
     const handleKeyDown = (e: KeyboardEvent) => {
       if (e.key === "Escape") handleClose();
     };
     document.addEventListener("keydown", handleKeyDown);
     return () => document.removeEventListener("keydown", handleKeyDown);
-  }, [isVisible]);
+  }, [isVisible, handleClose]);
apps/desktop/src/components/editor-area/index.tsx (1)

318-321: Remove no-op conditional class.

Both branches use "pt-1". Simplify the className to reduce noise.

-      <div className={cn([
-        "flex justify-center pb-4 px-8",
-        isFloatingSearchVisible ? "pt-1" : "pt-1"  // ← Less top padding when search bar is visible
-      ])}>
+      <div className={cn(["flex justify-center pb-4 px-8", "pt-1"])}>
📜 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 341cd33 and c236284.

📒 Files selected for processing (29)
  • 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 (4 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/right-panel/components/chat/chat-input.tsx (2 hunks)
  • apps/desktop/src/components/right-panel/index.tsx (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:

  • apps/desktop/src/components/editor-area/note-header/chips/index.tsx
  • apps/desktop/src/contexts/right-panel.tsx
  • packages/tiptap/src/shared/extensions.ts
  • apps/desktop/src/components/editor-area/note-header/tab-sub-header.tsx
  • apps/desktop/src/components/right-panel/views/chat-view.tsx
  • packages/tiptap/src/editor/index.tsx
  • packages/tiptap/src/transcript/index.tsx
  • apps/desktop/src/components/editor-area/note-header/chips/event-chip.tsx
  • apps/desktop/src/components/editor-area/note-header/chips/tag-chip.tsx
  • apps/desktop/src/components/editor-area/note-header/tab-header.tsx
  • apps/desktop/src/components/editor-area/note-header/chips/participants-chip.tsx
  • apps/desktop/src/components/editor-area/note-header/title-input.tsx
  • packages/ui/src/components/ui/command.tsx
  • apps/desktop/src/routes/app.note.$id.tsx
  • apps/desktop/src/components/right-panel/components/chat/chat-input.tsx
  • apps/desktop/src/components/editor-area/local-search-bar.tsx
  • apps/desktop/src/components/editor-area/metadata-modal.tsx
  • apps/desktop/src/components/editor-area/note-header/sub-headers/transcript-sub-header.tsx
  • apps/desktop/src/components/search-bar.tsx
  • apps/desktop/src/components/editor-area/note-header/index.tsx
  • apps/desktop/src/components/editor-area/note-header/sub-headers/enhanced-note-sub-header.tsx
  • apps/desktop/src/components/editor-area/floating-search-box.tsx
  • apps/desktop/src/components/editor-area/index.tsx
  • packages/utils/src/stores/session.ts
  • apps/desktop/src/components/editor-area/transcript-viewer.tsx
  • apps/desktop/src/components/right-panel/index.tsx
  • apps/desktop/src/components/toolbar/bars/main-toolbar.tsx
🧬 Code graph analysis (15)
packages/tiptap/src/shared/extensions.ts (1)
packages/tiptap/src/transcript/extensions/search-and-replace.ts (1)
  • SearchAndReplace (229-381)
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-291)
apps/desktop/src/components/right-panel/views/chat-view.tsx (4)
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/chat-messages-view.tsx (1)
  • ChatMessagesView (45-136)
apps/desktop/src/components/right-panel/components/chat/chat-input.tsx (1)
  • ChatInput (32-482)
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/note-header/tab-header.tsx (5)
packages/utils/src/contexts/sessions.tsx (1)
  • useSession (49-74)
apps/desktop/src/utils/template-service.ts (1)
  • TemplateService (5-66)
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/local-search-bar.tsx (3)
packages/tiptap/src/transcript/index.tsx (1)
  • TranscriptEditorRef (33-41)
packages/tiptap/src/editor/index.tsx (1)
  • TiptapEditor (11-11)
packages/ui/src/components/ui/button.tsx (1)
  • Button (37-89)
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-303)
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/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/editor-area/note-header/index.tsx (4)
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/listen-button.tsx (1)
  • ListenButton (52-149)
apps/desktop/src/components/editor-area/note-header/sub-headers/enhanced-note-sub-header.tsx (3)
packages/utils/src/contexts/ongoing-session.tsx (1)
  • useOngoingSession (32-46)
apps/desktop/src/utils/template-service.ts (1)
  • TemplateService (5-66)
packages/ui/src/components/ui/button.tsx (1)
  • Button (37-89)
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/editor-area/index.tsx (5)
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 (21-23)
  • TabHeader (25-215)
apps/desktop/src/components/editor-area/local-search-bar.tsx (1)
  • LocalSearchBar (15-260)
apps/desktop/src/components/editor-area/transcript-viewer.tsx (1)
  • TranscriptViewer (27-140)
apps/desktop/src/components/editor-area/transcript-viewer.tsx (6)
packages/tiptap/src/transcript/index.tsx (1)
  • TranscriptEditorRef (33-41)
apps/desktop/src/components/right-panel/hooks/useTranscript.ts (1)
  • useTranscript (8-91)
packages/ui/src/components/ui/button.tsx (1)
  • Button (37-89)
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/index.tsx (1)
apps/desktop/src/components/right-panel/views/chat-view.tsx (1)
  • ChatView (24-348)
apps/desktop/src/components/toolbar/bars/main-toolbar.tsx (1)
apps/desktop/src/components/toolbar/buttons/share-button.tsx (1)
  • ShareButton (28-31)
⏰ 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: build (macos, aarch64-apple-darwin, macos-14)
  • GitHub Check: ci (windows, windows-latest)
🔇 Additional comments (18)
apps/desktop/src/components/toolbar/bars/main-toolbar.tsx (1)

58-64: Re‑enabling the share action in the note toolbar looks good.

The isNote and isMain guards keep the button scoped exactly where it should surface, with ShareButton still self‑guarding via useParams, so the UI regains the feature without side effects.

apps/desktop/src/contexts/right-panel.tsx (2)

39-39: Default to chat: aligns with chat‑first UX

Good change; matches the rest of the PR’s chat‑first direction.


107-139: Should chat switching respect the ChatRightPanel flag?

sendSelectionToChat unconditionally opens the chat view. If the ChatRightPanel flag disables chat (or for non‑main windows), do we want to suppress switching/focusing chat here as well?

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

164-165: Spacing tweak looks good

pt-6 → pt-4 reduces top whitespace; should help compact the editor area without layout side effects.

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

112-114: Icon/date tinting to neutral‑500: LGTM

Consistent neutral styling for Calendar/Video/Speech and date text reads cleaner and matches surrounding UI.

Also applies to: 148-151, 251-253

apps/desktop/src/components/editor-area/floating-search-box.tsx (2)

38-43: Verify beautiful-react-hooks import usage

Confirm that default import from "beautiful-react-hooks/useDebouncedCallback" matches the version in your repo; some setups export it as a named export.


207-213: Tailwind class min-w-96: confirm availability

min-w-96 isn’t in Tailwind’s core minWidth scale by default. If you haven’t extended minWidth with spacing, consider using an arbitrary value (e.g., min-w-[24rem]) or confirm theme config.

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

60-75: Cancel path: light sanity check

On cancel, you signal cancelEnhance and hit GET {base}/cancel if local LLM is active. Confirm that duplicate clicks are harmless server‑side (idempotent) and that the endpoint exists across connector versions.

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

185-188: LGTM: layout/padding tweaks improve scroll area behavior.

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

34-36: Whitespace/wrapping change alters rendering; verify transcript appearance.

pre-wrap preserves whitespace and may change line breaks. Confirm this matches desired transcript formatting across locales/content lengths.

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

412-417: Placeholder copy update looks good

String-only change; no behavior impact.


444-445: Overlay guidance copy LGTM

Clearer instruction for mentions when generating.

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

38-46: Autofocus behavior is sound

Delayed focus with cleanup is correct and guarded by editable/isGenerating. No further changes needed.

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

102-105: UI polish LGTM

Neutral text color on icon/label improves consistency.


115-117: Exporting ParticipantsChipInner

Public export is fine; confirms reuse in other surfaces.

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

27-34: isNewNote heuristic looks reasonable

Covers empty title, empty raw/enhanced HTML, and no words.


71-72: Good: TitleInput gets autoFocus for new notes

Matches the intended UX.

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

145-214: Tab switching and visibility logic: LGTM.

Active tab control, visibility gating (meeting session), and auto-switch on enhancement/transcript presence look consistent.

Comment on lines +9 to +13
interface FloatingSearchBoxProps {
editorRef: React.RefObject<TranscriptEditorRef | null> | React.RefObject<{ editor: TiptapEditor | null }>;
onClose: () => void;
isVisible: boolean;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Type compile error: React.RefObject used without importing React types

Using React.RefObject in a type position requires importing the React type (or switching to RefObject). Add a type‑only import to avoid “Cannot find namespace 'React'”.

Apply this diff:

-import { Button } from "@hypr/ui/components/ui/button";
+import { Button } from "@hypr/ui/components/ui/button";
 import { Input } from "@hypr/ui/components/ui/input";
 import useDebouncedCallback from "beautiful-react-hooks/useDebouncedCallback";
 import { ChevronDownIcon, ChevronUpIcon, XIcon } from "lucide-react";
 import { useCallback, useEffect, useRef, useState } from "react";
 import { type TranscriptEditorRef } from "@hypr/tiptap/transcript";
 import { type TiptapEditor } from "@hypr/tiptap/editor";
+import type React from "react";
📝 Committable suggestion

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

Suggested change
interface FloatingSearchBoxProps {
editorRef: React.RefObject<TranscriptEditorRef | null> | React.RefObject<{ editor: TiptapEditor | null }>;
onClose: () => void;
isVisible: boolean;
}
import { Button } from "@hypr/ui/components/ui/button";
import { Input } from "@hypr/ui/components/ui/input";
import useDebouncedCallback from "beautiful-react-hooks/useDebouncedCallback";
import { ChevronDownIcon, ChevronUpIcon, XIcon } from "lucide-react";
import { useCallback, useEffect, useRef, useState } from "react";
import { type TranscriptEditorRef } from "@hypr/tiptap/transcript";
import { type TiptapEditor } from "@hypr/tiptap/editor";
import type React from "react";
interface FloatingSearchBoxProps {
editorRef: React.RefObject<TranscriptEditorRef | null> | React.RefObject<{ editor: TiptapEditor | null }>;
onClose: () => void;
isVisible: boolean;
}
🤖 Prompt for AI Agents
In apps/desktop/src/components/editor-area/floating-search-box.tsx around lines
9 to 13, the prop types use React.RefObject in a type position but React types
are not imported, causing a “Cannot find namespace 'React'” compile error; add a
type-only import and switch to RefObject to fix it (for example import type {
RefObject } from 'react' and update the prop type to use RefObject<...>), or
alternatively add import type React from 'react' so React.RefObject is
recognized.

Comment on lines +1 to +2
import { useState } from "react";
import { EventChip } from "./note-header/chips/event-chip";
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Fix React type import to avoid TS “Cannot find namespace 'React'”.

Use ReactNode type import instead of React.ReactNode in props.

 import { useState } from "react";
+import type { ReactNode } from "react";
@@
 interface MetadataModalProps {
   sessionId: string;
-  children: React.ReactNode;
+  children: ReactNode;
   hashtags?: string[];
 }

Also applies to: 6-10

🤖 Prompt for AI Agents
In apps/desktop/src/components/editor-area/metadata-modal.tsx around lines 1-2
(and similarly lines 6-10), the component props use the React.ReactNode
namespace which causes TS errors like "Cannot find namespace 'React'"; replace
that usage by importing the ReactNode type from React (e.g. import type {
ReactNode } from "react") and update prop type annotations to use ReactNode
instead of React.ReactNode; ensure any other React.* types in the file are
converted to explicit type imports from "react".

Comment on lines +106 to +119
const extractEmojiAndName = (title: string) => {
if (!title) {
return {
emoji: "📄",
name: "Untitled",
};
}
const emojiMatch = title.match(/^(\p{Emoji})\s*/u);
if (emojiMatch) {
return {
emoji: emojiMatch[1],
name: title.replace(/^(\p{Emoji})\s*/u, "").trim(),
};
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Regex uses nonstandard Unicode property ‘Emoji’ — can crash at parse time

/^(\p{Emoji})\s*/u isn’t reliably supported across engines (WebView2/WebKit). This can throw a SyntaxError when the module loads. Replace with a safer range‑based check.

Apply this diff:

-    const emojiMatch = title.match(/^(\p{Emoji})\s*/u);
-    if (emojiMatch) {
-      return {
-        emoji: emojiMatch[1],
-        name: title.replace(/^(\p{Emoji})\s*/u, "").trim(),
-      };
-    }
+    // Detect a leading emoji without Unicode property escapes for cross‑engine safety
+    const firstChar = Array.from(title)[0] || "";
+    const leadingEmojiMatch = firstChar.match(/^[\u{1F300}-\u{1FAFF}\u{1F600}-\u{1F64F}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/u);
+    if (leadingEmojiMatch) {
+      return {
+        emoji: leadingEmojiMatch[0],
+        name: title.slice(leadingEmojiMatch[0].length).trim(),
+      };
+    }
🤖 Prompt for AI Agents
In
apps/desktop/src/components/editor-area/note-header/sub-headers/enhanced-note-sub-header.tsx
around lines 106 to 119, the regex /^(\p{Emoji})\s*/u uses the nonstandard
Unicode property 'Emoji' and can throw a SyntaxError in some engines; replace it
with a safe, engine-compatible approach such as matching common emoji codepoint
ranges (e.g. include relevant Unicode blocks like U+1F300–U+1F5FF,
U+1F600–U+1F64F, U+1F680–U+1F6FF, U+2600–U+26FF, etc.) in the regex or use a
small helper that checks the first codepoint against those ranges, then extract
the emoji and name using that safe check and trim the remainder; ensure the
implementation avoids \p{Emoji} so the module never fails at parse time.

Comment on lines 5 to 14
import { Button } from "@hypr/ui/components/ui/button";
import { commands as miscCommands } from "@hypr/plugin-misc";
import { type TranscriptEditorRef } from "@hypr/tiptap/transcript";
import { EventChip } from "../chips/event-chip";
import { ParticipantsChip } from "../chips/participants-chip";

interface TranscriptSubHeaderProps {
sessionId: string;
editorRef?: React.RefObject<TranscriptEditorRef | null>;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Remove unused imports/props

EventChip, ParticipantsChip, and TranscriptEditorRef/editorRef are unused (copy button code is commented). This will trigger lint errors.

Apply this diff:

-import { Button } from "@hypr/ui/components/ui/button";
-import { commands as miscCommands } from "@hypr/plugin-misc";
-import { type TranscriptEditorRef } from "@hypr/tiptap/transcript";
-import { EventChip } from "../chips/event-chip";
-import { ParticipantsChip } from "../chips/participants-chip";
+import { Button } from "@hypr/ui/components/ui/button";
+import { commands as miscCommands } from "@hypr/plugin-misc";
@@
-interface TranscriptSubHeaderProps {
-  sessionId: string;
-  editorRef?: React.RefObject<TranscriptEditorRef | null>;
-}
+interface TranscriptSubHeaderProps {
+  sessionId: string;
+}
@@
-export function TranscriptSubHeader({ sessionId, editorRef }: TranscriptSubHeaderProps) {
+export function TranscriptSubHeader({ sessionId }: TranscriptSubHeaderProps) {
📝 Committable suggestion

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

Suggested change
import { Button } from "@hypr/ui/components/ui/button";
import { commands as miscCommands } from "@hypr/plugin-misc";
import { type TranscriptEditorRef } from "@hypr/tiptap/transcript";
import { EventChip } from "../chips/event-chip";
import { ParticipantsChip } from "../chips/participants-chip";
interface TranscriptSubHeaderProps {
sessionId: string;
editorRef?: React.RefObject<TranscriptEditorRef | null>;
}
import { Button } from "@hypr/ui/components/ui/button";
import { commands as miscCommands } from "@hypr/plugin-misc";
interface TranscriptSubHeaderProps {
sessionId: string;
}
export function TranscriptSubHeader({ sessionId }: TranscriptSubHeaderProps) {
// …
}
🤖 Prompt for AI Agents
In
apps/desktop/src/components/editor-area/note-header/sub-headers/transcript-sub-header.tsx
around lines 5 to 14, there are unused imports EventChip and ParticipantsChip
and an unused type/prop TranscriptEditorRef/editorRef (the copy button code is
commented out); remove the unused imports and delete the editorRef prop from the
TranscriptSubHeaderProps interface (and any related commented-out editorRef
usage) so the file only imports/declares what’s actually used to prevent lint
errors.

Comment on lines +20 to 22
{/*{(currentView === "transcript") ? <TranscriptView /> : <ChatView />}*/}
<ChatView />
</ResizablePanel>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Fix unused variable/import by restoring conditional render.

currentView and TranscriptView are unused with forced ChatView. Restore the conditional to satisfy lint and keep intent (chat‑first unless transcript selected).

-      {/*{(currentView === "transcript") ? <TranscriptView /> : <ChatView />}*/}
-      <ChatView />
+      {(currentView === "transcript") ? <TranscriptView /> : <ChatView />}
📝 Committable suggestion

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

Suggested change
{/*{(currentView === "transcript") ? <TranscriptView /> : <ChatView />}*/}
<ChatView />
</ResizablePanel>
{(currentView === "transcript") ? <TranscriptView /> : <ChatView />}
</ResizablePanel>
🤖 Prompt for AI Agents
In apps/desktop/src/components/right-panel/index.tsx around lines 20 to 22, the
component is forcing <ChatView /> which leaves currentView and TranscriptView
unused; restore the conditional JSX so it renders <TranscriptView /> when
currentView === "transcript" and <ChatView /> otherwise, and make sure
TranscriptView is imported and currentView is referenced from
props/state/context so the linter no longer flags unused variables/imports.

Comment on lines 71 to 74
"fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2",
"w-[90vw] max-w-[450px]",
"w-[2000px] max-w-[450px]",
"overflow-hidden rounded-lg bg-background shadow-lg",
)}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Restore responsive width constraint.

Setting w-[2000px] forces the dialog to render at 450 px (due to the max-w cap) even on sub-450 px viewports, causing horizontal overflow/regression compared to the previous 90vw behavior.

Apply this diff to keep the dialog responsive:

-          "w-[2000px] max-w-[450px]",
+          "w-[90vw] max-w-[450px]",
🤖 Prompt for AI Agents
In packages/ui/src/components/ui/command.tsx around lines 71 to 74, the dialog
classes include "w-[2000px] max-w-[450px]" which breaks responsiveness and
causes horizontal overflow on viewports narrower than 450px; replace the fixed
"w-[2000px]" with a responsive width such as "w-[90vw]" (keeping
"max-w-[450px]") so the dialog scales down on small screens while still
respecting the max width.

Comment on lines 31 to 33
activeTab: 'raw',
get,
refresh: async () => {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Initialize activeTab consistently with session state

Setting activeTab to 'raw' unconditionally can desync UI when enhanced content already exists (showRaw=false, activeTab='raw'). Initialize from session.

Apply this diff:

-    showRaw: !session.enhanced_memo_html,
-    activeTab: 'raw',
+    showRaw: !session.enhanced_memo_html,
+    activeTab: session.enhanced_memo_html ? 'enhanced' : 'raw',
📝 Committable suggestion

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

Suggested change
activeTab: 'raw',
get,
refresh: async () => {
showRaw: !session.enhanced_memo_html,
activeTab: session.enhanced_memo_html ? 'enhanced' : 'raw',
get,
refresh: async () => {
🤖 Prompt for AI Agents
In packages/utils/src/stores/session.ts around lines 31-33, the store
unconditionally sets activeTab: 'raw' which can desync the UI when session
already contains state; change the initializer to derive activeTab from the
stored session (e.g. use get().session?.activeTab if present, otherwise fall
back to get().session?.showRaw ? 'raw' : 'enhanced') so the store reflects
existing session state on initialization.

Copy link

@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

Caution

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

⚠️ Outside diff range comments (3)
packages/tiptap/src/editor/index.tsx (1)

4-37: Avoid double-registering the Document extension.

shared.extensions already ships StarterKit with document left enabled, so adding Document here registers the doc node twice and TipTap/ProseMirror throws RangeError: Duplicate schema item name 'doc' the moment the editor mounts. Please drop the extra import/entry (or disable document on the StarterKit before re-adding it explicitly).

Apply this diff to fix it:

-import Document from "@tiptap/extension-document";-        Document,
apps/desktop/src/components/editor-area/index.tsx (2)

476-489: Guard uninitialized ‘unlisten’ in cleanup to avoid runtime error

If the promise hasn’t resolved before unmount, calling an undefined unlisten will throw.

-  useEffect(() => {
-    let unlisten: () => void;
+  useEffect(() => {
+    let unlisten: (() => void) | undefined;
     localLlmEvents.llmEvent.listen(({ payload }) => {
       if (payload.progress) {
         setProgress(payload.progress);
       }
     }).then((fn) => {
       unlisten = fn;
     });
 
     return () => {
-      unlisten();
+      if (unlisten) {
+        unlisten();
+      }
     };
   }, []);

638-639: Fix always-true condition when adding contextText

ORs make this condition always true; use ANDs so empty/undefined/null context isn’t passed.

-          ...((contextText !== "" || contextText !== undefined || contextText !== null) ? { contextText } : {}),
+          ...((contextText !== "" && contextText !== undefined && contextText !== null) ? { contextText } : {}),
🧹 Nitpick comments (7)
apps/desktop/src/components/editor-area/local-search-bar.tsx (1)

98-109: Stabilize Escape handler by including handleClose in deps

Prevents stale closure if onClose changes.

-  }, [isVisible]);
+  }, [isVisible, handleClose]);
apps/desktop/src/components/editor-area/note-header/tab-sub-header.tsx (1)

1-3: Remove unused TranscriptEditorRef and transcriptEditorRef prop

The transcript sub-header render is commented out; drop the unused type import and prop to satisfy “no unused” guideline.

-import { type TranscriptEditorRef } from "@hypr/tiptap/transcript";
 import { useSession } from "@hypr/utils/contexts";
 import { EnhancedNoteSubHeader } from "./sub-headers/enhanced-note-sub-header";

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

 export function TabSubHeader(
-  { sessionId, onEnhance, isEnhancing, transcriptEditorRef, progress, showProgress, hashtags }: TabSubHeaderProps,
+  { sessionId, onEnhance, isEnhancing, progress, showProgress, hashtags }: TabSubHeaderProps,
 ) {
   const activeTab = useSession(sessionId, (s) => s.activeTab);

   /*
   if (activeTab === 'transcript') {
-    return <TranscriptSubHeader sessionId={sessionId} editorRef={{ current: transcriptEditorRef || null }} />;
+    return <TranscriptSubHeader sessionId={sessionId} />;
   }
   */

Also applies to: 5-13, 15-17, 33-37

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

6-12: Alias unused props in TabHeader

export const TabHeader = forwardRef<TabHeaderRef, TabHeaderProps>(
-  ({ sessionId, onEnhance, isEnhancing, progress = 0, showProgress = false }, ref) => {
+  ({ sessionId, onEnhance: _onEnhance, isEnhancing, progress: _progress = 0, showProgress: _showProgress = false }, ref) => {

Preserves the API while silencing unused-prop errors (callers in apps/desktop/src/components/editor-area/index.tsx still pass these props).

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

16-20: Add keyboard accessibility (focus/blur) parity with hover

Mirror hover with onFocus/onBlur so keyboard users can open/close the popover.

   return (
     <div
       className="relative inline-block cursor-pointer"
       onMouseEnter={() => setIsHovered(true)}
       onMouseLeave={() => setIsHovered(false)}
+      onFocus={() => setIsHovered(true)}
+      onBlur={() => setIsHovered(false)}
+      tabIndex={0}
+      role="button"
     >
apps/desktop/src/components/editor-area/transcript-viewer.tsx (3)

97-101: Simplify empty-state condition to a boolean

Removes reliance on truthy string short-circuiting.

-  const showEmptyMessage = sessionId && words.length <= 0 && !isLive;
+  const showEmptyMessage = words.length === 0 && !isLive;

191-197: Return null instead of empty elements

Avoids rendering meaningless

nodes.

-  if (!sessionId) {
-    return <p></p>;
-  }
+  if (!sessionId) {
+    return null;
+  }
 
-  if (!inactive) {
-    return <p></p>;
-  }
+  if (!inactive) {
+    return null;
+  }

163-170: Avoid coupling to route for sessionId inside SpeakerSelector

Derive sessionId from props/context instead of useMatch to keep this component usable outside /app/note/$id routes.

Would you like me to sketch a small refactor that closes over the parent sessionId when passing c={SpeakerSelector}?

📜 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 c236284 and d8e2e36.

⛔ 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 (20)
  • apps/desktop/src/components/editor-area/floating-search-box.tsx (1 hunks)
  • apps/desktop/src/components/editor-area/index.tsx (9 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/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/right-panel/index.tsx (2 hunks)
  • apps/desktop/src/components/right-panel/views/chat-view.tsx (1 hunks)
  • apps/desktop/src/components/toolbar/bars/main-toolbar.tsx (2 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/utils/src/stores/session.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • apps/desktop/src/components/right-panel/index.tsx
  • apps/desktop/src/components/editor-area/floating-search-box.tsx
  • packages/tiptap/src/styles/transcript.css
  • packages/tiptap/src/shared/extensions.ts
  • apps/desktop/src/components/right-panel/views/chat-view.tsx
  • apps/desktop/src/components/toolbar/bars/main-toolbar.tsx
  • packages/utils/src/stores/session.ts
🧰 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/editor-area/note-header/tab-header.tsx
  • apps/desktop/src/components/editor-area/metadata-modal.tsx
  • apps/desktop/src/components/editor-area/note-header/sub-headers/transcript-sub-header.tsx
  • apps/desktop/src/components/editor-area/note-header/title-input.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/transcript-viewer.tsx
  • apps/desktop/src/components/editor-area/index.tsx
  • apps/desktop/src/components/editor-area/local-search-bar.tsx
  • packages/tiptap/src/editor/index.tsx
  • apps/desktop/src/components/editor-area/note-header/sub-headers/enhanced-note-sub-header.tsx
  • apps/desktop/src/components/editor-area/note-header/chips/event-chip.tsx
🧬 Code graph analysis (10)
apps/desktop/src/components/editor-area/note-header/tab-header.tsx (4)
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/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/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/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/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/editor-area/index.tsx (5)
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 (14-16)
  • TabHeader (18-127)
apps/desktop/src/components/editor-area/local-search-bar.tsx (1)
  • LocalSearchBar (15-290)
apps/desktop/src/components/editor-area/transcript-viewer.tsx (1)
  • TranscriptViewer (27-144)
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/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/editor-area/note-header/chips/event-chip.tsx (1)
packages/utils/src/datetime.ts (1)
  • formatRelativeWithDay (88-132)
⏰ 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 (windows, windows-latest)
  • GitHub Check: ci (macos, macos-14)
🔇 Additional comments (9)
apps/desktop/src/components/editor-area/note-header/title-input.tsx (1)

2-6: Autofocus implementation looks solid

Ref + effect wiring is correct, guarded by editable/isGenerating, and cleans up the timeout. No unused imports.

Also applies to: 10-11, 19-23, 38-46, 50-51

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

6-12: Remove unused type and prop (prevents lint failures)

TranscriptEditorRef import and editorRef prop are unused. Clean them up.

 import { useQuery } from "@tanstack/react-query";
 import { AudioLinesIcon } from "lucide-react";
 import { useCallback } from "react";

 import { commands as miscCommands } from "@hypr/plugin-misc";
-import { type TranscriptEditorRef } from "@hypr/tiptap/transcript";
 import { Button } from "@hypr/ui/components/ui/button";

 interface TranscriptSubHeaderProps {
   sessionId: string;
-  editorRef?: React.RefObject<TranscriptEditorRef | null>;
 }
 
-export function TranscriptSubHeader({ sessionId, editorRef }: TranscriptSubHeaderProps) {
+export function TranscriptSubHeader({ sessionId }: TranscriptSubHeaderProps) {

Also applies to: 14-15

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

50-53: LGTM on temporarily disabling ShareChip

Change is safe and keeps the rest of the chip row intact.

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

112-114: Consistent neutral tone styling

Icon/text color adjustments are consistent and improve visual hierarchy. No functional impact.

Also applies to: 147-153, 253-256

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

27-34: Good: new-note autofocus and exports

  • isNewNote heuristic is reasonable and guarded.
  • Wiring autoFocus={isNewNote && editable} aligns with the TitleInput behavior.
  • Re-exports for TabHeader/TabSubHeader simplify consumption.

Also applies to: 59-62, 71-72, 89-91

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

295-301: LGTM: LocalSearchBar integration per active tab

Keying by activeTab and switching editorRef between transcript/editor is clean and avoids stale refs.

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

1-1: Import ReactNode type to avoid TS “Cannot find namespace 'React'”

Use a type import and stop referencing the React namespace in props.

-import { useState } from "react";
+import { useState, type ReactNode } from "react";

6-10: Use ReactNode instead of React.ReactNode in props

Prevents namespace lookup issues under react-jsx runtime.

 interface MetadataModalProps {
   sessionId: string;
-  children: React.ReactNode;
+  children: ReactNode;
   hashtags?: string[];
 }
apps/desktop/src/components/editor-area/note-header/sub-headers/enhanced-note-sub-header.tsx (1)

103-121: Replace unsupported Unicode property escape in regex

\p{Emoji} can throw at parse time in some engines. Use a safe codepoint-range check.

   const extractEmojiAndName = (title: string) => {
     if (!title) {
       return {
         emoji: "📄",
         name: "Untitled",
       };
     }
-    const emojiMatch = title.match(/^(\p{Emoji})\s*/u);
-    if (emojiMatch) {
-      return {
-        emoji: emojiMatch[1],
-        name: title.replace(/^(\p{Emoji})\s*/u, "").trim(),
-      };
-    }
+    // Detect a leading emoji without Unicode property escapes for cross‑engine safety
+    const firstChar = Array.from(title)[0] || "";
+    const leadingEmojiMatch = firstChar.match(/^[\u{1F300}-\u{1FAFF}\u{1F600}-\u{1F64F}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/u);
+    if (leadingEmojiMatch) {
+      return {
+        emoji: leadingEmojiMatch[0],
+        name: title.slice(leadingEmojiMatch[0].length).trim(),
+      };
+    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant