Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions apps/desktop/src/components/editor-area/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { showTipsModal } from "../tips-modal/service";
import { enhanceFailedToast } from "../toast/shared";
import { AnnotationBox } from "./annotation-box";
import { LocalSearchBar } from "./local-search-bar";
import { NoteHeader, TabHeader, type TabHeaderRef } from "./note-header";
import { NoteHeader, TabHeader } from "./note-header";
import { EnhancedNoteSubHeader } from "./note-header/sub-headers/enhanced-note-sub-header";
import { TranscriptSubHeader } from "./note-header/sub-headers/transcript-sub-header";
import { TextSelectionPopover } from "./text-selection-popover";
Expand Down Expand Up @@ -159,7 +159,6 @@ export default function EditorArea({

const editorRef = useRef<{ editor: TiptapEditor | null }>({ editor: null });
const transcriptRef = useRef<TranscriptEditorRef | null>(null);
const tabHeaderRef = useRef<TabHeaderRef>(null);
const [transcriptEditorRef, setTranscriptEditorRef] = useState<TranscriptEditorRef | null>(null);
const [isFloatingSearchVisible, setIsFloatingSearchVisible] = useState(false);
const [isTabHeaderVisible, setIsTabHeaderVisible] = useState(false);
Expand Down Expand Up @@ -335,7 +334,6 @@ export default function EditorArea({
/>

<TabHeader
ref={tabHeaderRef}
sessionId={sessionId}
onEnhance={enhance.mutate}
isEnhancing={enhance.status === "pending"}
Expand Down
55 changes: 0 additions & 55 deletions apps/desktop/src/components/editor-area/metadata-modal.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,3 @@ export function NoteHeader(

// Export the TabHeader and TabSubHeader components for use outside this directory
export { TabHeader, TabSubHeader };
export type { TabHeaderRef } from "./tab-header";
171 changes: 41 additions & 130 deletions apps/desktop/src/components/editor-area/note-header/tab-header.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { useEnhancePendingState } from "@/hooks/enhance-pending";
import { cn } from "@hypr/ui/lib/utils";
import { TabHeader as TabHeaderUI } from "@hypr/ui/components/block/tab-header";
import { useOngoingSession, useSession } from "@hypr/utils/contexts";
import { forwardRef, useEffect, useImperativeHandle } from "react";

interface TabHeaderProps {
sessionId: string;
Expand All @@ -12,131 +11,43 @@ interface TabHeaderProps {
onVisibilityChange?: (isVisible: boolean) => void;
}

export interface TabHeaderRef {
isVisible: boolean;
}

export const TabHeader = forwardRef<TabHeaderRef, TabHeaderProps>(
({ sessionId, onEnhance, isEnhancing, progress = 0, showProgress = false, onVisibilityChange }, ref) => {
const [activeTab, setActiveTab] = useSession(sessionId, (s) => [
s.activeTab,
s.setActiveTab,
]);
const session = useSession(sessionId, (s) => s.session);

const ongoingSessionStatus = useOngoingSession((s) => s.status);
const ongoingSessionId = useOngoingSession((s) => s.sessionId);

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

const canEnhanceTranscript = hasTranscript && isSessionInactive;

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

// BUT use floating button logic for Enhanced tab visibility
const isEnhancePending = useEnhancePendingState(sessionId);
const shouldShowEnhancedTab = hasEnhancedMemo || isEnhancePending || canEnhanceTranscript;

// Automatic tab switching logic following existing conventions

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

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

const handleTabClick = (tab: "raw" | "enhanced" | "transcript") => {
setActiveTab(tab);
};

// Expose visibility state via ref
useImperativeHandle(ref, () => ({
isVisible: isMeetingSession ?? false,
}), [isMeetingSession]);

// Notify parent when visibility changes
useEffect(() => {
if (onVisibilityChange) {
onVisibilityChange(isMeetingSession ?? false);
}
}, [isMeetingSession, onVisibilityChange]);

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

return (
<div className="relative">
{/* Tab container */}
<div className="bg-white">
<div className="flex px-8">
<div className="flex border-b border-neutral-100 w-full">
{/* Raw Note Tab */}

{/* Enhanced Note Tab - show when session ended OR transcript exists OR enhanced memo exists */}
{shouldShowEnhancedTab && (
<button
onClick={() => handleTabClick("enhanced")}
className={cn(
"relative px-2 py-2 text-xs pl-1 font-medium transition-all duration-200 border-b-2 -mb-px flex items-center gap-1.5",
activeTab === "enhanced"
? "text-neutral-900 border-neutral-900"
: "text-neutral-600 border-transparent hover:text-neutral-800",
)}
>
Summary
</button>
)}

<button
onClick={() => handleTabClick("raw")}
className={cn(
"relative py-2 text-xs font-medium transition-all duration-200 border-b-2 -mb-px flex items-center gap-1.5",
shouldShowEnhancedTab ? "pl-3 px-4" : "pl-1 px-2",
activeTab === "raw"
? "text-neutral-900 border-neutral-900"
: "text-neutral-600 border-transparent hover:text-neutral-800",
)}
>
Memos
</button>

{/* Transcript Tab - always show */}
<button
onClick={() => handleTabClick("transcript")}
className={cn(
"relative px-4 py-2 text-xs pl-3 font-medium transition-all duration-200 border-b-2 -mb-px flex items-center gap-1.5",
activeTab === "transcript"
? "text-neutral-900 border-neutral-900"
: "text-neutral-600 border-transparent hover:text-neutral-800",
)}
>
Transcript
{isCurrentlyRecording && (
<div className="relative h-2 w-2">
<div className="absolute inset-0 rounded-full bg-red-500/30"></div>
<div className="absolute inset-0 rounded-full bg-red-500 animate-ping"></div>
</div>
)}
</button>
</div>
</div>
</div>
</div>
);
},
);
export const TabHeader = (
{ sessionId, onEnhance, isEnhancing, progress = 0, showProgress = false, onVisibilityChange }: TabHeaderProps,
) => {
const [activeTab, setActiveTab] = useSession(sessionId, (s) => [
s.activeTab,
s.setActiveTab,
]);

const session = useSession(sessionId, (s) => s.session);

const ongoingSessionStatus = useOngoingSession((s) => s.status);
const ongoingSessionId = useOngoingSession((s) => s.sessionId);

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

const canEnhanceTranscript = hasTranscript && isSessionInactive;

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

// BUT use floating button logic for Enhanced tab visibility
const isEnhancePending = useEnhancePendingState(sessionId);
const shouldShowEnhancedTab = hasEnhancedMemo || isEnhancePending || canEnhanceTranscript;

return (
<TabHeaderUI
isEnhancing={isEnhancing}
onVisibilityChange={onVisibilityChange}
onTabChange={setActiveTab}
currentTab={activeTab}
isCurrentlyRecording={isCurrentlyRecording}
shouldShowTab={shouldShowTab}
shouldShowEnhancedTab={shouldShowEnhancedTab}
/>
);
};
Loading
Loading