-
Notifications
You must be signed in to change notification settings - Fork 379
Overall redesign add empty chat #1499
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…all-redesign-start
…all-redesign-start
…all-redesign-0925
…all-redesign-add-empty-chat
📝 WalkthroughWalkthroughAdds in-editor search-and-replace UIs (floating and full-width), tabbed note header/sub-headers and TranscriptViewer, right-panel chat/model UI with dynamic quick actions, TipTap extensions (Document, SearchAndReplace) and styles, UI color/tiny layout tweaks, and session activeTab state. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant UI as LocalSearch / FloatingSearch
participant Editor as TipTap Editor
participant Ext as SearchAndReplace Storage
User->>UI: Type search term
UI->>Editor: editor.commands.setSearchTerm(term)
Editor->>Ext: update matches/storage
UI->>Ext: read storage.searchAndReplace
Ext-->>UI: resultCount/currentIndex
User->>UI: Next/Prev or Enter/F3
UI->>Editor: editor.commands.goToNext()/goToPrev()
Editor->>Ext: set current match
UI->>Editor: scroll current match into view
User->>UI: Replace / Replace All
UI->>Editor: editor.commands.replace()/replaceAll()
Editor->>Ext: apply replacements
UI->>Ext: read updated resultCount/currentIndex
sequenceDiagram
participant Header as TabHeader
participant Store as Session Store
participant Area as Editor Area
participant Viewer as TranscriptViewer
participant Memo as Memo Editor
Header->>Store: setActiveTab(tab)
Store-->>Area: activeTab changed
alt tab == "transcript"
Area->>Viewer: render TranscriptViewer(sessionId)
else tab == "raw"
Area->>Memo: render raw memo editor
else tab == "enhanced"
Area->>Memo: render enhanced memo editor
end
sequenceDiagram
actor User
participant Chat as ChatInput
participant Modal as ChatModelInfoModal
participant Conn as connectorCommands
participant Nav as windowsCommands/router
Chat->>Conn: getLlmConnection/getCustomLlmModel/getHyprcloudEnabled
Conn-->>Chat: connection/model flags
User->>Chat: Click model badge
Chat->>Modal: open modal
User->>Modal: Click "Choose"
Modal->>Nav: showSettingsWindow() + navigate to /app/settings?tab=llms
Modal->>Modal: close
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (3 warnings)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
apps/desktop/src/components/search-bar.tsx (1)
113-147
: Disabling the input breaks keyboard focus & history UXTurning the input into a disabled, pointer-events-none element means it can no longer receive focus or key events. That prevents
onFocus
,onBlur
, andonKeyDown
handlers from ever firing, so keyboard navigation (navigateResults
,selectResult
,clearSearch
) and the recent-history dropdown are now dead code.focusSearch
(still exposed byuseHyprSearch
) will also fail because browsers refuse to focus disabled controls. Net effect: keyboard users lose the search entry point entirely. Please keep the element focusable (e.g. removedisabled
/pointer-events-none, or replace it with a real button that forwards focus/keyboard events into the palette).apps/desktop/src/components/editor-area/note-header/title-input.tsx (1)
24-29
: Don’t trap Shift+Tab; only intercept Tab forward.Preventing default on any Tab breaks reverse navigation. Limit to Tab without Shift.
- if (e.key === "Enter" || e.key === "Tab") { + if (e.key === "Enter" || (e.key === "Tab" && !e.shiftKey)) {apps/desktop/src/components/right-panel/views/chat-view.tsx (2)
34-35
: Remove unused state setter.
_setChatHistory
is unused. Destructure only the value to satisfy no-unused-vars.- const [chatHistory, _setChatHistory] = useState<ChatSession[]>([]); + const [chatHistory] = useState<ChatSession[]>([]);As per coding guidelines
245-247
: Selecting a chat from history doesn’t load it.
handleSelectChat
ignores the selected id, so history selection won’t switch conversations.- const handleSelectChat = (_chatId: string) => { - setShowHistory(false); - }; + const handleSelectChat = (chatId: string) => { + setCurrentConversationId(chatId); + setShowHistory(false); + };apps/desktop/src/components/right-panel/components/chat/chat-input.tsx (2)
346-353
: Prevent double-submit on Enter (duplicated handling).Enter is handled both via TipTap handleKeyDown and a DOM keydown listener, causing potential double submission.
Apply this diff to remove the duplicate Enter handling in the DOM listener:
- if (event.key === "Enter" && !event.shiftKey) { - event.preventDefault(); - - if (inputValue.trim()) { - handleSubmit(); - } - }
260-289
: Escape inserted HTML to avoid injection via titles/labels.selectionRef and data-label embed unescaped note/title text. Escape special chars before composing HTML.
Apply this diff:
- const selectedHtml = pendingSelection.text || ""; + const selectedHtml = pendingSelection.text || ""; @@ - const selectedText = stripHtml(selectedHtml).trim(); + const selectedText = stripHtml(selectedHtml).trim(); + const escapeHtml = (s: string) => + s + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + const safeNoteName = escapeHtml(noteName); + const safeSelectedText = escapeHtml(selectedText); @@ - const textPreview = selectedText.length > 0 + const textPreview = safeSelectedText.length > 0 ? (selectedText.length > 6 - ? `'${selectedText.slice(0, 6)}...'` // Use single quotes instead! - : `'${selectedText}'`) + ? `'${safeSelectedText.slice(0, 6)}...'` // Why: prevent HTML injection + : `'${safeSelectedText}'`) : "NO_TEXT"; @@ - const selectionRef = textPreview !== "NO_TEXT" - ? `[${noteName} - ${textPreview}(${pendingSelection.startOffset}:${pendingSelection.endOffset})]` - : `[${noteName} - ${pendingSelection.startOffset}:${pendingSelection.endOffset}]`; + const selectionRef = textPreview !== "NO_TEXT" + ? `[${safeNoteName} - ${textPreview}(${pendingSelection.startOffset}:${pendingSelection.endOffset})]` + : `[${safeNoteName} - ${pendingSelection.startOffset}:${pendingSelection.endOffset}]`; @@ - const escapedSelectionRef = selectionRef.replace(/"/g, """); + const escapedSelectionRef = selectionRef; // already escaped
🧹 Nitpick comments (25)
apps/desktop/src/routes/app.note.$id.tsx (1)
163-167
: Reuse computed isMainWindow; avoid duplicate window label callsYou already computed isMainWindow (Line 120). Use it here to prevent redundant calls and keep intent clear.
- <EditorArea editable={getCurrentWebviewWindowLabel() === "main"} sessionId={sessionId} /> + <EditorArea editable={isMainWindow} sessionId={sessionId} />apps/desktop/src/contexts/right-panel.tsx (1)
39-39
: Defaulting to “chat” may bypass ChatRightPanel flag gatingInitializing currentView to "chat" shows chat by default even if the ChatRightPanel flag resolves to false later. Confirm this is intended; otherwise, consider syncing the default with the flag.
Minimal follow-up (outside this hunk) to keep transcript as fallback when chat is disabled:
+ useEffect(() => { + if (chatPanelEnabled === false && currentView === "chat") { + setCurrentView("transcript"); + } + }, [chatPanelEnabled]);apps/desktop/src/components/right-panel/components/chat-model-info-modal.tsx (1)
35-43
: Use Modal’s built-in overlay instead of a custom oneSimplifies layering and keeps behavior consistent across modals.
- <div className="fixed inset-0 z-50 bg-black/25 backdrop-blur-sm" onClick={handleClose} /> - <Modal open={isOpen} onClose={handleClose} size="md" - showOverlay={false} className="bg-background w-[480px] max-w-[90vw]" >apps/desktop/src/components/editor-area/note-header/chips/participants-chip.tsx (1)
162-169
: Rename ‘ParticipentItem’ typo for clarityConsistent naming improves readability.
- <ParticipentItem + <ParticipantItem key={member.id} member={member} sessionId={sessionId} handleClickHuman={handleClickHuman} allowRemove={allowMutate} selected={member.id === selectedHuman?.id} />-function ParticipentItem({ +function ParticipantItem({ member, sessionId, handleClickHuman, allowRemove = true, selected = false, }: {Also applies to: 178-191
packages/tiptap/src/styles/transcript.css (1)
25-29
: Optional: align speaker-content white-space with containerTo avoid inconsistent wrapping between container and speaker lines:
.transcript-speaker-content { display: inline; line-height: 1.7; - white-space: normal; + white-space: pre-wrap; }apps/desktop/src/components/editor-area/note-header/sub-headers/enhanced-note-sub-header.tsx (1)
113-119
: Use Extended_Pictographic for precise emoji matching
\p{Emoji} is a broad binary property (it includes digits, punctuation, etc.); \p{Extended_Pictographic} more accurately targets actual emoji code points and is supported in all modern Chromium, WebKit, and Gecko engines.- const emojiMatch = title.match(/^(\p{Emoji})\s*/u); + const emojiMatch = title.match(/^(\p{Extended_Pictographic})\s*/u);apps/desktop/src/components/editor-area/metadata-modal.tsx (1)
16-20
: Add keyboard support for the hover modal trigger.Enable focus/blur to mirror hover so keyboard users can access the modal.
- <div - className="relative inline-block cursor-pointer" - onMouseEnter={() => setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} - > + <div + className="relative inline-block cursor-pointer" + onMouseEnter={() => setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + tabIndex={0} + onFocus={() => setIsHovered(true)} + onBlur={() => setIsHovered(false)} + >apps/desktop/src/components/right-panel/components/chat/chat-input.tsx (3)
53-71
: Avoid frequent polling; prefer cache semantics.Refetching all three queries every 5s is costly. Use staleTime and focus-based refetch instead.
Apply this diff:
const llmConnectionQuery = useQuery({ queryKey: ["llm-connection"], queryFn: () => connectorCommands.getLlmConnection(), - refetchOnWindowFocus: true, - refetchInterval: 5000, + refetchOnWindowFocus: true, + staleTime: 30_000, }); const customLlmModelQuery = useQuery({ queryKey: ["custom-llm-model"], queryFn: () => connectorCommands.getCustomLlmModel(), enabled: llmConnectionQuery.data?.type === "Custom", - refetchInterval: 5000, + staleTime: 30_000, }); const hyprCloudEnabledQuery = useQuery({ queryKey: ["hypr-cloud-enabled"], queryFn: () => connectorCommands.getHyprcloudEnabled(), - refetchInterval: 5000, + staleTime: 30_000, });
360-361
: Remove ineffectivereturn false
from DOM listener.
return false
in addEventListener handlers has no effect (unlike jQuery). You already call preventDefault/stopPropagation.Apply this diff:
- return false;
21-26
:onKeyDown
prop is effectively unused.It’s declared but not wired to the editor. Either pass it through to the editor or remove it from props and deps.
Would you like me to prepare a follow-up diff to remove the prop and its usage?
Also applies to: 35-47, 372-372
apps/desktop/src/components/right-panel/utils/dynamic-quickactions.ts (2)
49-54
: Avoid unnecessary remote call for custom model.Only fetch the custom model when the connection type is Custom.
Apply this diff:
- const llmConnection = await connectorCommands.getLlmConnection(); - const { type } = llmConnection; - const apiBase = llmConnection.connection?.api_base; - const customModel = await connectorCommands.getCustomLlmModel(); - const modelId = type === "Custom" && customModel ? customModel : "gpt-4"; + const llmConnection = await connectorCommands.getLlmConnection(); + const { type } = llmConnection; + const apiBase = llmConnection.connection?.api_base; + let modelId = "gpt-4"; + if (type === "Custom") { + const customModel = await connectorCommands.getCustomLlmModel(); + modelId = customModel || modelId; + }
148-182
: Consider typing participants and de-duping action builders.participants: any[] obscures shape; define a Participant type with full_name. Also, HyprCloud/Custom-with-tools post/pre builders are mostly identical—consider a shared generator to reduce drift.
Also applies to: 213-246, 248-281
apps/desktop/src/components/right-panel/components/chat/empty-chat-state.tsx (3)
26-29
: Handle slow fetch UX (optional).While quickActions load, consider showing DEFAULT_ACTIONS or a lightweight skeleton to avoid an empty list on first render.
115-119
: Add accessible alt text for the image.Empty alt hides the image from screen readers; provide a short descriptive alt or mark it decorative with role="presentation".
Apply this diff:
- <img - src="/assets/dynamic.gif" - alt="" + <img + src="/assets/dynamic.gif" + alt="Suggestions" className={`${currentSize.icon} mx-auto`} />
130-139
: Prefer stable keys over index.If actions reorder, index keys can cause odd re-renders. Use a composite key (e.g.,
${action.eventName}:${action.shownTitle}
).apps/desktop/src/components/editor-area/local-search-bar.tsx (1)
15-175
: Deduplicate search logic via a shared hook.This mirrors FloatingSearchBox behavior. Extract a
useEditorSearchReplace
hook to centralize applySearch, navigation, replace, and scrolling.apps/desktop/src/components/editor-area/transcript-viewer.tsx (3)
33-49
: Replace polling ref-notifier with a callback ref.The interval loop is unnecessary and wasteful. Use a callback ref to notify parent immediately when the editor mounts.
Apply:
- // Notify parent when editor ref changes - check periodically for ref to be set - useEffect(() => { - // Initial notification - if (onEditorRefChange) { - onEditorRefChange(editorRef.current); - } - - // Check if ref gets set later - const checkInterval = setInterval(() => { - if (editorRef.current?.editor && onEditorRefChange) { - onEditorRefChange(editorRef.current); - clearInterval(checkInterval); - } - }, 100); - - return () => clearInterval(checkInterval); - }, [onEditorRefChange]); + const setEditorRef = useCallback((ref: TranscriptEditorRef | null) => { + editorRef.current = ref; + onEditorRefChange?.(ref); + }, [onEditorRefChange]);- <TranscriptEditor - ref={editorRef} + <TranscriptEditor + ref={setEditorRef} initialWords={words} editable={true} onUpdate={handleUpdate} c={SpeakerSelector} />Also applies to: 135-141
97-101
: Simplify empty-state condition.
sessionId &&
is redundant sincesessionId
is a required prop. Minor cleanup.- const showEmptyMessage = sessionId && words.length <= 0 && !isLive; + const showEmptyMessage = words.length === 0 && !isLive;
191-197
: Return null instead of emptyplaceholders.
Avoid extra DOM nodes when selector is hidden.
- if (!sessionId) { - return <p></p>; - } + if (!sessionId) { + return null; + }- if (!inactive) { - return <p></p>; - } + if (!inactive) { + return null; + }apps/desktop/src/components/editor-area/index.tsx (1)
363-369
: Don’t derive render styles from an imperative ref during render.
tabHeaderRef.current?.isVisible
isn’t reactive; the padding may never update when visibility changes.Minimal, stable alternative:
- className={cn([ - activeTab === "transcript" - ? "h-full overflow-hidden pt-0" - : `h-full overflow-y-auto ${tabHeaderRef.current?.isVisible ? "pt-6" : "pt-3"}`, - enhancedContent && activeTab !== "transcript" && "pb-10", - ])} + className={cn([ + activeTab === "transcript" + ? "h-full overflow-hidden pt-0" + : "h-full overflow-y-auto pt-6", + enhancedContent && activeTab !== "transcript" && "pb-10", + ])}If dynamic spacing is required, lift
isVisible
into state via a callback prop from TabHeader instead of reading a ref in render.apps/desktop/src/components/editor-area/note-header/chips/tag-chip.tsx (1)
65-65
: Remove redundant ternary in className.Both branches return the same class; simplify for clarity.
- <span className={`truncate ${totalTags === 0 ? "text-neutral-500" : "text-neutral-500"}`}> + <span className="truncate text-neutral-500">apps/desktop/src/components/editor-area/note-header/chips/index.tsx (1)
50-52
: Commented-out ShareChip: consider adding a brief “why” or TODO.Helps avoid long-lived commented code without context.
apps/desktop/src/components/right-panel/index.tsx (1)
20-22
: Always rendering ChatView may hide TranscriptView.If intentional for interim UI, fine; otherwise this removes the ability to switch views.
If temporary, add a brief “why” comment with link/issue to re-enable.
apps/desktop/src/components/editor-area/note-header/index.tsx (1)
27-34
: Make “new note” detection robust to markup variants.Exact equality against "
" is brittle; strip tags and trim before checking.- const isNewNote = !sessionTitle?.trim() - && (!session.raw_memo_html || session.raw_memo_html === "<p></p>") - && (!session.enhanced_memo_html || session.enhanced_memo_html === "<p></p>") - && session.words.length === 0; + const isNewNote = !sessionTitle?.trim() + && (!session.raw_memo_html || session.raw_memo_html.replace(/<[^>]*>/g, "").trim().length === 0) + && (!session.enhanced_memo_html || session.enhanced_memo_html.replace(/<[^>]*>/g, "").trim().length === 0) + && (session.words?.length ?? 0) === 0;apps/desktop/src/styles/globals.css (1)
136-148
: Use theme tokens for search highlight colors
In apps/desktop/src/styles/globals.css, replace hard-coded#ffeb3b
and#31e054
with Tailwind theme colors or CSS custom properties (e.g.theme('colors.yellow.400')
,theme('colors.green.500')
or correspondingvar(--…)
).
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ 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:
apps/desktop/src/contexts/right-panel.tsx
apps/desktop/src/routes/app.note.$id.tsx
apps/desktop/src/components/editor-area/metadata-modal.tsx
packages/tiptap/src/transcript/index.tsx
apps/desktop/src/components/editor-area/note-header/chips/index.tsx
apps/desktop/src/components/editor-area/note-header/chips/tag-chip.tsx
apps/desktop/src/components/search-bar.tsx
apps/desktop/src/components/editor-area/note-header/tab-sub-header.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
apps/desktop/src/components/toolbar/bars/main-toolbar.tsx
apps/desktop/src/components/right-panel/utils/dynamic-quickactions.ts
apps/desktop/src/components/editor-area/note-header/index.tsx
apps/desktop/src/components/right-panel/views/chat-view.tsx
apps/desktop/src/components/editor-area/local-search-bar.tsx
packages/utils/src/stores/session.ts
apps/desktop/src/components/editor-area/note-header/tab-header.tsx
apps/desktop/src/components/right-panel/components/chat-model-info-modal.tsx
packages/tiptap/src/shared/extensions.ts
apps/desktop/src/components/right-panel/components/chat/chat-input.tsx
apps/desktop/src/components/editor-area/note-header/title-input.tsx
apps/desktop/src/components/editor-area/floating-search-box.tsx
apps/desktop/src/components/pro-gate-modal/index.tsx
apps/desktop/src/components/editor-area/transcript-viewer.tsx
packages/tiptap/src/editor/index.tsx
apps/desktop/src/components/right-panel/components/chat/empty-chat-state.tsx
apps/desktop/src/components/right-panel/index.tsx
packages/ui/src/components/ui/command.tsx
apps/desktop/src/components/editor-area/note-header/sub-headers/transcript-sub-header.tsx
apps/desktop/src/components/editor-area/index.tsx
apps/desktop/src/components/editor-area/note-header/chips/participants-chip.tsx
🧬 Code graph analysis (18)
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/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/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)
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/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/right-panel/views/chat-view.tsx (3)
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/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/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)
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/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/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/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)
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/index.tsx (6)
packages/utils/src/contexts/sessions.tsx (1)
useSession
(49-74)apps/desktop/src/contexts/right-panel.tsx (1)
useRightPanel
(230-236)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-129)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)
🪛 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 (24)
apps/desktop/src/components/pro-gate-modal/index.tsx (1)
44-49
: Delay increase looks reasonableThe longer wait should give the settings window enough time to mount before the navigation emit fires, reducing the risk of the billing tab navigation being dropped.
apps/desktop/src/components/toolbar/bars/main-toolbar.tsx (1)
60-61
: Share button gating looks correctRe-enabling
ShareButton
behind the existingisNote
guard keeps the hook usage scoped to note routes and restores sharing for note tabs without affecting other contexts. Nice catch.packages/tiptap/src/shared/extensions.ts (1)
10-10
: Approve search-and-replace extension; highlight styles are present
Found.search-result
and.search-result-current
rules inpackages/tiptap/src/styles/transcript.css
andapps/desktop/src/styles/globals.css
.apps/desktop/src/components/editor-area/note-header/chips/event-chip.tsx (4)
112-114
: Styling tweak LGTMNeutral-muted icon/text looks consistent with the redesign.
148-153
: Meeting vs speech icon + muted date LGTMConditional icon + subdued date read well.
253-256
: Calendar chip muted style LGTMConsistent with Event and Onboarding states.
156-157
: Remove unsupported date-fns ‘zzz’ token
date-fns (without tz) doesn’t support ‘z/zz/zzz’ and may render literally or throw. Drop the timezone token, use a supported offset (e.g. OOO), or switch to date-fns-tz.- {format(new Date(date), "EEE, MMM d, yyyy 'at' h:mm a zzz")} + {format(new Date(date), "EEE, MMM d, yyyy 'at' h:mm a")}apps/desktop/src/components/editor-area/note-header/chips/participants-chip.tsx (2)
102-104
: Muted icon/text LGTMMatches the new visual tone for chips.
115-130
: Exporting ParticipantsChipInner LGTM. Verified import only in apps/desktop/src/components/editor-area/transcript-viewer.tsx.packages/tiptap/src/styles/transcript.css (1)
34-36
: Verify transcript rendering under pre-wrap + break-word
.transcript-speaker-content remains inline and inherits pre-wrap/break-word; manually check for layout regressions with long words, pasted content, and search highlights.apps/desktop/src/components/editor-area/floating-search-box.tsx (2)
15-304
: Overall LGTM.Robust editor resolution, keyboard handling, and result syncing. Good use of storage and safe fallbacks.
40-61
: VerifieduseDebouncedCallback
signature
beautiful-react-hooks
definesuseDebouncedCallback(fn, dependencies?, wait?, options?)
, so passing[]
and300
correctly applies debouncing.apps/desktop/src/components/editor-area/local-search-bar.tsx (1)
214-289
: Nice integration and UX.Focus management, compact UI, and keyboard nav feel solid.
apps/desktop/src/components/editor-area/note-header/sub-headers/transcript-sub-header.tsx (2)
16-21
: LGTM on polling and conditional UI.Polling every 2.5s with enabled + queryKey scoping is fine; button visibility correctly follows data presence.
Also applies to: 45-55
6-6
: Remove unusededitorRef
prop andTranscriptEditorRef
import. No active call sites; drop the import, theeditorRef
line inTranscriptSubHeaderProps
, and its parameter in the function signature.apps/desktop/src/components/editor-area/index.tsx (1)
302-309
: Search integration wiring looks good.Hotkey handler, tab-aware editorRef routing, and reset via
key={activeTab}
are consistent. No unused vars observed.Also applies to: 336-343, 381-399
apps/desktop/src/components/editor-area/note-header/chips/tag-chip.tsx (1)
60-60
: Icon color normalization looks good.Unifying the TagsIcon color to text-neutral-500 matches the broader muted scheme.
packages/tiptap/src/transcript/index.tsx (1)
185-188
: Padding/min-height adjustments LGTM.Improves scrollable area and content spacing without affecting logic.
apps/desktop/src/components/right-panel/index.tsx (1)
4-8
: Import/usage cleanup for ChatView is fine.No unused imports; minimal surface.
apps/desktop/src/components/editor-area/note-header/index.tsx (4)
10-11
: Re-exporting TabHeader/TabSubHeader looks good.Keeps consumers co-located with NoteHeader.
59-61
: Spacing tweak LGTM.Reduced bottom padding aligns with new sub-header density.
71-71
: Auto-focus on new notes is a good UX touch.Conditioned by editable; safe.
89-92
: Export surface additions LGTM.Keeps API tidy and discoverable from the index.
packages/utils/src/stores/session.ts (1)
47-60
: session.ts only mutates showRaw/activeTab in setActiveTab
There are no setShowRaw, updateRawNote or updateEnhancedNote actions—so no desynchronization risk.Likely an incorrect or invalid review comment.
export const TabHeader = forwardRef<TabHeaderRef, TabHeaderProps>( | ||
({ sessionId, onEnhance, isEnhancing, progress = 0, showProgress = false }, ref) => { | ||
const [activeTab, setActiveTab] = useSession(sessionId, (s) => [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Remove/alias unused props in TabHeader.
onEnhance
, progress
, and showProgress
are not used here. Alias to underscores to avoid unused bindings.
-export const TabHeader = forwardRef<TabHeaderRef, TabHeaderProps>(
- ({ sessionId, onEnhance, isEnhancing, progress = 0, showProgress = false }, ref) => {
+export const TabHeader = forwardRef<TabHeaderRef, TabHeaderProps>(
+ ({ sessionId, onEnhance: _onEnhance, isEnhancing, progress: _progress = 0, showProgress: _showProgress = false }, ref) => {
As per coding guidelines
📝 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.
export const TabHeader = forwardRef<TabHeaderRef, TabHeaderProps>( | |
({ sessionId, onEnhance, isEnhancing, progress = 0, showProgress = false }, ref) => { | |
const [activeTab, setActiveTab] = useSession(sessionId, (s) => [ | |
export const TabHeader = forwardRef<TabHeaderRef, TabHeaderProps>( | |
({ sessionId, onEnhance: _onEnhance, isEnhancing, progress: _progress = 0, showProgress: _showProgress = false }, ref) => { | |
const [activeTab, setActiveTab] = useSession(sessionId, (s) => [ |
🤖 Prompt for AI Agents
In apps/desktop/src/components/editor-area/note-header/tab-header.tsx around
lines 18 to 20, alias the unused props to underscore-prefixed names in the
component parameter destructuring to avoid unused binding warnings — e.g. change
onEnhance to onEnhance: _onEnhance, progress to progress: _progress (keep
default = 0), and showProgress to showProgress: _showProgress — leave
isEnhancing and sessionId unchanged so functionality is preserved and ESLint
no-unused-vars is satisfied.
export function TabSubHeader( | ||
{ sessionId, onEnhance, isEnhancing, transcriptEditorRef, progress, showProgress, hashtags }: TabSubHeaderProps, | ||
) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Silence unused props to satisfy no-unused-vars.
transcriptEditorRef
and hashtags
aren’t used (transcript path is commented out). Alias them to underscores in the destructuring.
-export function TabSubHeader(
- { sessionId, onEnhance, isEnhancing, transcriptEditorRef, progress, showProgress, hashtags }: TabSubHeaderProps,
-) {
+export function TabSubHeader(
+ { sessionId, onEnhance, isEnhancing, transcriptEditorRef: _transcriptEditorRef, progress, showProgress, hashtags: _hashtags }: TabSubHeaderProps,
+) {
As per coding guidelines
📝 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.
export function TabSubHeader( | |
{ sessionId, onEnhance, isEnhancing, transcriptEditorRef, progress, showProgress, hashtags }: TabSubHeaderProps, | |
) { | |
export function TabSubHeader( | |
{ sessionId, onEnhance, isEnhancing, transcriptEditorRef: _transcriptEditorRef, progress, showProgress, hashtags: _hashtags }: TabSubHeaderProps, | |
) { |
🤖 Prompt for AI Agents
In apps/desktop/src/components/editor-area/note-header/tab-sub-header.tsx around
lines 15 to 17, the props transcriptEditorRef and hashtags are unused and
trigger no-unused-vars; silence them by aliasing in the function parameter
destructuring (e.g., rename to _transcriptEditorRef and _hashtags) so linters
accept unused values while preserving the prop positions and types.
switch (connectionType) { | ||
case "Custom": | ||
return customLlmModelQuery.data || "Custom Model"; | ||
case "HyprLocal": | ||
return "Local LLM"; | ||
return "HyprLLM"; | ||
default: | ||
return "Model"; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove unreachable return (Biome error) and verify intended case.
In getCurrentModelName, the second return inside the "HyprLocal" case is unreachable.
Apply this diff:
switch (connectionType) {
case "Custom":
return customLlmModelQuery.data || "Custom Model";
case "HyprLocal":
return "Local LLM";
- return "HyprLLM";
default:
return "Model";
}
If "HyprLLM" is an actual connection type, add an explicit case instead of the unreachable return.
📝 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.
switch (connectionType) { | |
case "Custom": | |
return customLlmModelQuery.data || "Custom Model"; | |
case "HyprLocal": | |
return "Local LLM"; | |
return "HyprLLM"; | |
default: | |
return "Model"; | |
} | |
switch (connectionType) { | |
case "Custom": | |
return customLlmModelQuery.data || "Custom Model"; | |
case "HyprLocal": | |
return "Local LLM"; | |
default: | |
return "Model"; | |
} |
🧰 Tools
🪛 Biome (2.1.2)
[error] 401-401: This code will never be reached ...
... because this statement will return from the function beforehand
(lint/correctness/noUnreachable)
🤖 Prompt for AI Agents
In apps/desktop/src/components/right-panel/components/chat/chat-input.tsx around
lines 396 to 404, the "HyprLocal" switch case contains an unreachable second
return ("HyprLLM"); remove the unreachable return and decide intended behavior:
either return "HyprLocal" (e.g., "Local LLM") only, or if "HyprLLM" is a valid
connectionType add an explicit case 'HyprLLM' that returns "HyprLLM" and keep
'HyprLocal' returning "Local LLM"; update tests or usages if needed.
import "../styles/tiptap.css"; | ||
import "../styles/mention.css"; | ||
|
||
import Document from "@tiptap/extension-document"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove duplicate Document extension to avoid schema/extension conflicts
StarterKit (in shared.extensions) already brings Document. Adding Document again can cause duplicate extension name/schema conflicts. Register Document only once.
Apply this diff to remove the duplicate:
-import Document from "@tiptap/extension-document";
@@
extensions: [
...shared.extensions,
- Document,
mention(mentionConfig),
],
If you need a custom Document schema, disable it in StarterKit where it’s configured and add Document once there instead (shared/extensions.ts).
Also applies to: 36-38
🤖 Prompt for AI Agents
In packages/tiptap/src/editor/index.tsx around lines 4 (and similarly lines
36-38), you import Document from "@tiptap/extension-document" while StarterKit
(shared.extensions) already provides Document, causing duplicate
extension/schema conflicts; remove the duplicate import(s) and any additional
registration of Document here so Document is registered only once (either rely
on StarterKit or, if you need a custom Document, disable it in StarterKit in
shared/extensions.ts and add a single Document import/registration there).
className={cn( | ||
"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]", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed 2000px width breaks responsiveness; restore viewport-based width
Using w-[2000px] with max-w-[450px] forces ~450px width even on narrow viewports, causing overflow. Restore responsive width.
- "w-[2000px] max-w-[450px]",
+ "w-[90vw] max-w-[450px]",
🤖 Prompt for AI Agents
In packages/ui/src/components/ui/command.tsx around line 72, the hardcoded
"w-[2000px] max-w-[450px]" forces the element to stay ~450px and overflow on
narrow viewports; replace the fixed 2000px with a responsive width such as
"w-full max-w-[450px]" (or simply remove the w-[2000px] token) so the component
uses the viewport width up to the 450px max; update the class string
accordingly.
activeTab: "raw", | ||
get, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initialize activeTab from existing session state to avoid drift.
If enhanced content exists, start on “enhanced”; otherwise “raw”.
- activeTab: "raw",
+ 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.
activeTab: "raw", | |
get, | |
activeTab: session.enhanced_memo_html ? "enhanced" : "raw", | |
get, |
🤖 Prompt for AI Agents
In packages/utils/src/stores/session.ts around lines 31-32, replace the
hardcoded activeTab: "raw" with a computed initialization that reads the current
store/session state via get() and sets activeTab to "enhanced" when enhanced
content exists, otherwise "raw" (e.g. activeTab: get()?.session?.enhancedContent
? "enhanced" : "raw"), so the tab reflects existing session state and avoids
drift.
…all-redesign-add-empty-chat
…all-redesign-add-empty-chat
…all-redesign-add-empty-chat
…all-redesign-add-empty-chat
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/desktop/src/components/editor-area/note-header/sub-headers/enhanced-note-sub-header.tsx
(1 hunks)apps/desktop/src/components/editor-area/transcript-viewer.tsx
(1 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/transcript-viewer.tsx
apps/desktop/src/components/editor-area/note-header/sub-headers/enhanced-note-sub-header.tsx
🧬 Code graph analysis (2)
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/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)
⏰ 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)
interface EnhancedNoteSubHeaderProps { | ||
sessionId: string; | ||
onEnhance?: (params: { triggerType: "manual" | "template"; templateId?: string | null }) => void; | ||
isEnhancing?: boolean; | ||
progress?: number; | ||
showProgress?: boolean; | ||
} | ||
|
||
export function EnhancedNoteSubHeader({ | ||
sessionId, | ||
onEnhance, | ||
isEnhancing, | ||
progress = 0, | ||
showProgress = false, | ||
}: EnhancedNoteSubHeaderProps) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Drop the unused sessionId
prop
sessionId
is still declared in the props and destructured in the component, but nothing ever reads it. Our lint rules reject unused values, so this needs to be removed (or actually used) before we can merge.
-interface EnhancedNoteSubHeaderProps {
- sessionId: string;
+interface EnhancedNoteSubHeaderProps {
onEnhance?: (params: { triggerType: "manual" | "template"; templateId?: string | null }) => void;
isEnhancing?: boolean;
progress?: number;
showProgress?: boolean;
}
export function EnhancedNoteSubHeader({
- sessionId,
onEnhance,
isEnhancing,
progress = 0,
showProgress = false,
}: EnhancedNoteSubHeaderProps) {
📝 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.
interface EnhancedNoteSubHeaderProps { | |
sessionId: string; | |
onEnhance?: (params: { triggerType: "manual" | "template"; templateId?: string | null }) => void; | |
isEnhancing?: boolean; | |
progress?: number; | |
showProgress?: boolean; | |
} | |
export function EnhancedNoteSubHeader({ | |
sessionId, | |
onEnhance, | |
isEnhancing, | |
progress = 0, | |
showProgress = false, | |
}: EnhancedNoteSubHeaderProps) { | |
interface EnhancedNoteSubHeaderProps { | |
onEnhance?: (params: { triggerType: "manual" | "template"; templateId?: string | null }) => void; | |
isEnhancing?: boolean; | |
progress?: number; | |
showProgress?: boolean; | |
} | |
export function EnhancedNoteSubHeader({ | |
onEnhance, | |
isEnhancing, | |
progress = 0, | |
showProgress = false, | |
}: EnhancedNoteSubHeaderProps) { | |
// …rest of implementation… | |
} |
🤖 Prompt for AI Agents
In
apps/desktop/src/components/editor-area/note-header/sub-headers/enhanced-note-sub-header.tsx
around lines 15 to 29, the sessionId prop is declared and destructured but never
used, causing lint failures for unused variables; remove sessionId from the
EnhancedNoteSubHeaderProps interface and from the component parameter list (and
any related defaulting) so only used props remain, or alternatively use it if
intended — but for this change delete the unused prop and update any callers if
necessary.
<Popover open={isTemplateDropdownOpen} onOpenChange={setIsTemplateDropdownOpen}> | ||
<PopoverTrigger asChild> | ||
<Button | ||
variant="outline" | ||
size="sm" | ||
onClick={isEnhancing ? handleRegenerateOrCancel : () => setIsTemplateDropdownOpen(true)} | ||
onMouseEnter={() => setIsHovered(true)} | ||
onMouseLeave={() => setIsHovered(false)} | ||
disabled={false} | ||
className="text-xs h-[28px] px-3 hover:bg-neutral-100 relative w-[180px] shadow-sm" | ||
> | ||
{/* Main content - centered in available space */} | ||
<div className="absolute inset-0 flex items-center justify-center pr-6"> | ||
<div className="flex items-center"> | ||
{isEnhancing | ||
? ( | ||
isHovered | ||
? ( | ||
<> | ||
<div className="mr-1.5 w-3 h-3 flex items-center justify-center"> | ||
<XIcon size={12} className="text-red-600" /> | ||
</div> | ||
<span className="text-red-600">Cancel</span> | ||
{shouldShowProgress && ( | ||
<span className="ml-2 text-xs font-mono"> | ||
{Math.round(progress * 100)}% | ||
</span> | ||
)} | ||
</> | ||
) | ||
: ( | ||
<> | ||
<Spinner className="mr-1.5 w-3 h-3" /> | ||
<span>Generating...</span> | ||
{shouldShowProgress && ( | ||
<span className="ml-2 text-xs font-mono"> | ||
{Math.round(progress * 100)}% | ||
</span> | ||
)} | ||
</> | ||
) | ||
) | ||
: ( | ||
<> | ||
<RefreshCwIcon size={14} className="mr-1.5" /> | ||
Regenerate | ||
</> | ||
)} | ||
</div> | ||
</div> | ||
|
||
{/* Chevron - fixed position on right */} | ||
<ChevronDownIcon size={14} className="absolute right-3 top-1/2 -translate-y-1/2 text-neutral-400" /> | ||
</Button> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prevent the template popover from opening while cancelling
When isEnhancing
is true and the user clicks to cancel, Radix still fires onOpenChange(true)
, so the template picker opens right after the cancel request. Likewise, because the trigger’s onClick
always forces setIsTemplateDropdownOpen(true)
, the dropdown can’t be closed by clicking the button again. That’s a confusing UX regression. Gate the onOpenChange
callback while enhancing and stop forcing the open state in the click handler so the popover behaves like a normal toggle.
- <Popover open={isTemplateDropdownOpen} onOpenChange={setIsTemplateDropdownOpen}>
+ <Popover
+ open={isTemplateDropdownOpen}
+ onOpenChange={nextOpen => {
+ if (isEnhancing) {
+ return;
+ }
+ setIsTemplateDropdownOpen(nextOpen);
+ }}
+ >
<PopoverTrigger asChild>
<Button
variant="outline"
size="sm"
- onClick={isEnhancing ? handleRegenerateOrCancel : () => setIsTemplateDropdownOpen(true)}
+ onClick={event => {
+ if (isEnhancing) {
+ event.preventDefault();
+ handleRegenerateOrCancel();
+ }
+ }}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
disabled={false}
className="text-xs h-[28px] px-3 hover:bg-neutral-100 relative w-[180px] shadow-sm"
>
📝 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.
<Popover open={isTemplateDropdownOpen} onOpenChange={setIsTemplateDropdownOpen}> | |
<PopoverTrigger asChild> | |
<Button | |
variant="outline" | |
size="sm" | |
onClick={isEnhancing ? handleRegenerateOrCancel : () => setIsTemplateDropdownOpen(true)} | |
onMouseEnter={() => setIsHovered(true)} | |
onMouseLeave={() => setIsHovered(false)} | |
disabled={false} | |
className="text-xs h-[28px] px-3 hover:bg-neutral-100 relative w-[180px] shadow-sm" | |
> | |
{/* Main content - centered in available space */} | |
<div className="absolute inset-0 flex items-center justify-center pr-6"> | |
<div className="flex items-center"> | |
{isEnhancing | |
? ( | |
isHovered | |
? ( | |
<> | |
<div className="mr-1.5 w-3 h-3 flex items-center justify-center"> | |
<XIcon size={12} className="text-red-600" /> | |
</div> | |
<span className="text-red-600">Cancel</span> | |
{shouldShowProgress && ( | |
<span className="ml-2 text-xs font-mono"> | |
{Math.round(progress * 100)}% | |
</span> | |
)} | |
</> | |
) | |
: ( | |
<> | |
<Spinner className="mr-1.5 w-3 h-3" /> | |
<span>Generating...</span> | |
{shouldShowProgress && ( | |
<span className="ml-2 text-xs font-mono"> | |
{Math.round(progress * 100)}% | |
</span> | |
)} | |
</> | |
) | |
) | |
: ( | |
<> | |
<RefreshCwIcon size={14} className="mr-1.5" /> | |
Regenerate | |
</> | |
)} | |
</div> | |
</div> | |
{/* Chevron - fixed position on right */} | |
<ChevronDownIcon size={14} className="absolute right-3 top-1/2 -translate-y-1/2 text-neutral-400" /> | |
</Button> | |
<Popover | |
open={isTemplateDropdownOpen} | |
onOpenChange={nextOpen => { | |
if (isEnhancing) { | |
return; | |
} | |
setIsTemplateDropdownOpen(nextOpen); | |
}} | |
> | |
<PopoverTrigger asChild> | |
<Button | |
variant="outline" | |
size="sm" | |
onClick={event => { | |
if (isEnhancing) { | |
event.preventDefault(); | |
handleRegenerateOrCancel(); | |
} | |
}} | |
onMouseEnter={() => setIsHovered(true)} | |
onMouseLeave={() => setIsHovered(false)} | |
disabled={false} | |
className="text-xs h-[28px] px-3 hover:bg-neutral-100 relative w-[180px] shadow-sm" | |
> | |
{/* Main content - centered in available space */} | |
<div className="absolute inset-0 flex items-center justify-center pr-6"> | |
<div className="flex items-center"> | |
{isEnhancing | |
? ( | |
isHovered | |
? ( | |
<> | |
<div className="mr-1.5 w-3 h-3 flex items-center justify-center"> | |
<XIcon size={12} className="text-red-600" /> | |
</div> | |
<span className="text-red-600">Cancel</span> | |
{shouldShowProgress && ( | |
<span className="ml-2 text-xs font-mono"> | |
{Math.round(progress * 100)}% | |
</span> | |
)} | |
</> | |
) | |
: ( | |
<> | |
<Spinner className="mr-1.5 w-3 h-3" /> | |
<span>Generating...</span> | |
{shouldShowProgress && ( | |
<span className="ml-2 text-xs font-mono"> | |
{Math.round(progress * 100)}% | |
</span> | |
)} | |
</> | |
) | |
) | |
: ( | |
<> | |
<RefreshCwIcon size={14} className="mr-1.5" /> | |
Regenerate | |
</> | |
)} | |
</div> | |
</div> | |
{/* Chevron - fixed position on right */} | |
<ChevronDownIcon size={14} className="absolute right-3 top-1/2 -translate-y-1/2 text-neutral-400" /> | |
</Button> |
🤖 Prompt for AI Agents
In
apps/desktop/src/components/editor-area/note-header/sub-headers/enhanced-note-sub-header.tsx
around lines 172-225, Radix Popover is opening when the user clicks to cancel
because onOpenChange and the trigger onClick always force open; change
onOpenChange to ignore changes while isEnhancing (e.g. onOpenChange={(open) => {
if (!isEnhancing) setIsTemplateDropdownOpen(open) }}), and update the Button
onClick to stop forcing true (e.g. onClick={() => { if (isEnhancing)
handleRegenerateOrCancel(); else setIsTemplateDropdownOpen(prev => !prev); }}),
so the popover behaves as a normal toggle and will not open during cancel.
useEffect(() => { | ||
if (human) { | ||
onSpeakerChange(human, speakerRange); | ||
} | ||
}, [human, speakerRange]); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Include onSpeakerChange
in the dependency list
This effect closes over onSpeakerChange
but does not list it as a dependency, so if the editor swaps out that handler (e.g., when the transcript editor remounts or the session changes), we’ll keep invoking the stale callback and silently drop new updates. Please add onSpeakerChange
to the dependency array so the effect reacts to handler changes.
useEffect(() => {
if (human) {
onSpeakerChange(human, speakerRange);
}
- }, [human, speakerRange]);
+ }, [human, speakerRange, onSpeakerChange]);
📝 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.
useEffect(() => { | |
if (human) { | |
onSpeakerChange(human, speakerRange); | |
} | |
}, [human, speakerRange]); | |
useEffect(() => { | |
if (human) { | |
onSpeakerChange(human, speakerRange); | |
} | |
}, [human, speakerRange, onSpeakerChange]); |
🤖 Prompt for AI Agents
In apps/desktop/src/components/editor-area/transcript-viewer.tsx around lines
192 to 197, the useEffect closes over onSpeakerChange but doesn't list it in
dependencies; update the effect to include onSpeakerChange in the dependency
array so the effect re-runs when the handler changes (i.e., change the
dependency list from [human, speakerRange] to [human, speakerRange,
onSpeakerChange]) to avoid calling a stale callback.
useEffect(() => { | ||
const foundHuman = participants.find((s) => s.id === speakerId); | ||
|
||
if (foundHuman) { | ||
setHuman(foundHuman); | ||
} | ||
}, [participants, speakerId]); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reset human
when the current speaker disappears
When the assigned speaker ID no longer matches any participant (e.g., speaker removed or cleared), we leave the previous human
in state, so the UI still displays and emits changes for the wrong person. Make sure we also clear the human
state when we don’t find a match.
useEffect(() => {
const foundHuman = participants.find((s) => s.id === speakerId);
if (foundHuman) {
setHuman(foundHuman);
+ } else {
+ setHuman(null);
}
}, [participants, speakerId]);
📝 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.
useEffect(() => { | |
const foundHuman = participants.find((s) => s.id === speakerId); | |
if (foundHuman) { | |
setHuman(foundHuman); | |
} | |
}, [participants, speakerId]); | |
useEffect(() => { | |
const foundHuman = participants.find((s) => s.id === speakerId); | |
if (foundHuman) { | |
setHuman(foundHuman); | |
} else { | |
setHuman(null); | |
} | |
}, [participants, speakerId]); |
🤖 Prompt for AI Agents
In apps/desktop/src/components/editor-area/transcript-viewer.tsx around lines
198 to 205, the useEffect only sets `human` when a matching participant is found
but does not clear it when no match exists; update the effect so that if
`participants.find(s => s.id === speakerId)` returns undefined you call
`setHuman(null)` (or `setHuman(undefined)` consistent with the component's state
type) to clear the previous value, keeping the UI and emitted changes in sync
with the current participant list.
DON'T MERGE THIS YET