Skip to content
Open
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
5 changes: 5 additions & 0 deletions apps/desktop/src/components/main/body/sessions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { commands as miscCommands } from "@hypr/plugin-misc";

import AudioPlayer from "../../../../contexts/audio-player";
import { useListener } from "../../../../contexts/listener";
import { useIsSessionEnhancing } from "../../../../hooks/useEnhancedNotes";
import * as main from "../../../../store/tinybase/main";
import { rowIdfromTab, type Tab } from "../../../../store/zustand/tabs";
import { StandardTabWrapper } from "../index";
Expand Down Expand Up @@ -35,15 +36,19 @@ export const TabItemNote: TabItem<Extract<Tab, { type: "sessions" }>> = ({
main.STORE_ID,
);
const sessionMode = useListener((state) => state.getSessionMode(tab.id));
const isEnhancing = useIsSessionEnhancing(tab.id);
const isActive =
sessionMode === "running_active" || sessionMode === "finalizing";
const isFinalizing = sessionMode === "finalizing";
const showSpinner = isFinalizing || isEnhancing;

return (
<TabItemBase
icon={<StickyNoteIcon className="w-4 h-4" />}
title={title || "Untitled"}
selected={tab.active}
active={isActive}
finalizing={showSpinner}
tabIndex={tabIndex}
handleCloseThis={() => handleCloseThis(tab)}
handleSelectThis={() => handleSelectThis(tab)}
Expand Down
9 changes: 6 additions & 3 deletions apps/desktop/src/components/main/body/shared.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { X } from "lucide-react";
import { useState } from "react";

import { Kbd, KbdGroup } from "@hypr/ui/components/ui/kbd";
import { Spinner } from "@hypr/ui/components/ui/spinner";
import { cn } from "@hypr/utils";

import { useCmdKeyPressed } from "../../../hooks/useCmdKeyPressed";
Expand All @@ -20,7 +21,7 @@ type TabItemBaseProps = {
title: React.ReactNode;
selected: boolean;
active?: boolean;
isEmptyTab?: boolean;
finalizing?: boolean;
tabIndex?: number;
} & {
handleCloseThis: () => void;
Expand All @@ -38,7 +39,7 @@ export function TabItemBase({
title,
selected,
active = false,
isEmptyTab = false,
finalizing = false,
tabIndex,
handleCloseThis,
handleSelectThis,
Expand Down Expand Up @@ -110,7 +111,9 @@ export function TabItemBase({
isHovered ? "opacity-0" : "opacity-100",
])}
>
{active ? (
{finalizing ? (
<Spinner size={16} />
) : active ? (
<div className="relative size-2">
<div className="absolute inset-0 rounded-full bg-red-600"></div>
<div className="absolute inset-0 rounded-full bg-red-300 animate-ping"></div>
Expand Down
27 changes: 23 additions & 4 deletions apps/desktop/src/components/main/sidebar/timeline/item.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { memo, useCallback, useMemo } from "react";

import { commands as analyticsCommands } from "@hypr/plugin-analytics";
import { ContextMenuItem } from "@hypr/ui/components/ui/context-menu";
import { Spinner } from "@hypr/ui/components/ui/spinner";
import { cn } from "@hypr/utils";

import { useListener } from "../../../../contexts/listener";
import { useIsSessionEnhancing } from "../../../../hooks/useEnhancedNotes";
import * as main from "../../../../store/tinybase/main";
import { type TabInput, useTabs } from "../../../../store/zustand/tabs";
import { id } from "../../../../utils";
Expand Down Expand Up @@ -35,6 +39,14 @@ export const TimelineItemComponent = memo(
const timestamp =
item.type === "event" ? item.data.started_at : item.data.created_at;

const sessionId = item.type === "session" ? item.id : null;
const sessionMode = useListener((state) =>
sessionId ? state.getSessionMode(sessionId) : "inactive",
);
const isEnhancing = useIsSessionEnhancing(sessionId ?? "");
const isFinalizing = sessionMode === "finalizing";
const showSpinner = isFinalizing || isEnhancing;

const handleClick = () => {
if (item.type === "event") {
handleEventClick(false);
Expand Down Expand Up @@ -162,11 +174,18 @@ export const TimelineItemComponent = memo(
!selected && "hover:bg-neutral-100",
])}
>
<div className="flex flex-col gap-0.5">
<div className="text-sm font-normal truncate">{title}</div>
{displayTime && (
<div className="text-xs text-neutral-500">{displayTime}</div>
<div className="flex items-start gap-2">
{showSpinner && (
<div className="flex-shrink-0 mt-0.5">
<Spinner size={14} />
</div>
)}
<div className="flex flex-col gap-0.5 min-w-0">
<div className="text-sm font-normal truncate">{title}</div>
{displayTime && (
<div className="text-xs text-neutral-500">{displayTime}</div>
)}
</div>
</div>
</InteractiveButton>
);
Expand Down
25 changes: 24 additions & 1 deletion apps/desktop/src/hooks/useEnhancedNotes.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { useCallback, useEffect } from "react";
import { useCallback, useEffect, useMemo } from "react";

import { useHasTranscript } from "../components/main/body/sessions/shared";
import { useAITask } from "../contexts/ai-task";
import { useListener } from "../contexts/listener";
import * as main from "../store/tinybase/main";
import { createTaskId } from "../store/zustand/ai-task/task-configs";

export function useCreateEnhancedNote() {
const store = main.UI.useStore(main.STORE_ID) as main.Store | undefined;
Expand Down Expand Up @@ -170,3 +172,24 @@ export function useEnsureDefaultSummary(sessionId: string) {
createEnhancedNote,
]);
}

export function useIsSessionEnhancing(sessionId: string): boolean {
const enhancedNoteIds = main.UI.useSliceRowIds(
main.INDEXES.enhancedNotesBySession,
sessionId,
main.STORE_ID,
);

const taskIds = useMemo(
() => (enhancedNoteIds || []).map((id) => createTaskId(id, "enhance")),
[enhancedNoteIds],
);

const isEnhancing = useAITask((state) => {
return taskIds.some(
(taskId) => state.tasks[taskId]?.status === "generating",
);
});

return isEnhancing;
}
Loading