-
Notifications
You must be signed in to change notification settings - Fork 364
Overall redesign 0925 #1496
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?
Overall redesign 0925 #1496
Conversation
…all-redesign-start
…all-redesign-start
📝 WalkthroughWalkthroughAdds 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
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
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()
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60–90 minutes Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (2 warnings, 1 inconclusive)
✨ 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: 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 unusedShareChip
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 referenceselectionRef (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, """); - - 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, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + 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 EnterEnter 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 delayThe 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 extractEmojiAndNameThis 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 relocatingsearch-and-replace
under a sharedextensions
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 inpackages/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 editorCurrently 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 throughThe 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 importsSlightly 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 contentWhen 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
📒 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
andisMain
guards keep the button scoped exactly where it should surface, withShareButton
still self‑guarding viauseParams
, 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 UXGood 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 goodpt-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: LGTMConsistent 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 usageConfirm 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 availabilitymin-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 checkOn 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 goodString-only change; no behavior impact.
444-445
: Overlay guidance copy LGTMClearer instruction for mentions when generating.
apps/desktop/src/components/editor-area/note-header/title-input.tsx (1)
38-46
: Autofocus behavior is soundDelayed 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 LGTMNeutral text color on icon/label improves consistency.
115-117
: Exporting ParticipantsChipInnerPublic export is fine; confirms reuse in other surfaces.
apps/desktop/src/components/editor-area/note-header/index.tsx (2)
27-34
: isNewNote heuristic looks reasonableCovers empty title, empty raw/enhanced HTML, and no words.
71-72
: Good: TitleInput gets autoFocus for new notesMatches 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.
interface FloatingSearchBoxProps { | ||
editorRef: React.RefObject<TranscriptEditorRef | null> | React.RefObject<{ editor: TiptapEditor | null }>; | ||
onClose: () => void; | ||
isVisible: boolean; | ||
} |
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.
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.
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.
import { useState } from "react"; | ||
import { EventChip } from "./note-header/chips/event-chip"; |
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
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".
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(), | ||
}; | ||
} |
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.
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.
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>; | ||
} |
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 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.
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.
apps/desktop/src/components/editor-area/note-header/tab-sub-header.tsx
Outdated
Show resolved
Hide resolved
{/*{(currentView === "transcript") ? <TranscriptView /> : <ChatView />}*/} | ||
<ChatView /> | ||
</ResizablePanel> |
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
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.
{/*{(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.
"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", | ||
)} |
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.
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.
packages/utils/src/stores/session.ts
Outdated
activeTab: 'raw', | ||
get, | ||
refresh: async () => { |
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 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.
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.
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: 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 shipsStarterKit
withdocument
left enabled, so addingDocument
here registers thedoc
node twice and TipTap/ProseMirror throwsRangeError: Duplicate schema item name 'doc'
the moment the editor mounts. Please drop the extra import/entry (or disabledocument
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 errorIf 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 contextTextORs 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 includinghandleClose
in depsPrevents 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 andtranscriptEditorRef
propThe 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 TabHeaderexport 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 hoverMirror 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 booleanRemoves 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 elementsAvoids 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 SpeakerSelectorDerive 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
⛔ 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 solidRef + 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 andeditorRef
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 ShareChipChange 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 stylingIcon/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 tabKeying 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 propsPrevents 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(), + }; + }
DON'T MERGE THIS