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
36 changes: 36 additions & 0 deletions apps/desktop/src/session/components/floating/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,42 @@ describe("FloatingActionButton", () => {
expect(wrapper?.className).toContain("group-hover:translate-y-0");
});

it("keeps the chat FAB tucked during active meetings", () => {
hoisted.sessionMode = "active";

render(<FloatingActionButton tab={tab} />);

const wrapper = screen.getByText("Ask Anarlog anything").parentElement;
const hoverZone = wrapper?.parentElement;

expect(hoverZone?.className).toContain("group");
expect(hoverZone?.className).toContain("pointer-events-auto");
expect(wrapper?.getAttribute("aria-hidden")).toBe("true");
expect(wrapper?.style.getPropertyValue("--floating-fab-tuck-offset")).toBe(
"calc(100% - 0.5rem + 18px)",
);
expect(wrapper?.className).toContain("pointer-events-none");
expect(wrapper?.className).toContain("group-hover:pointer-events-auto");
expect(wrapper?.className).toContain("group-hover:translate-y-0");
});

it("shows the tucked chat FAB during active meetings before transcript exists", () => {
hoisted.sessionMode = "active";
hoisted.hasTranscript = false;

render(<FloatingActionButton tab={tab} />);

const wrapper = screen.getByText("Ask Anarlog anything").parentElement;

expect(
screen.queryByRole("button", { name: "Start listening" }),
).toBeNull();
expect(wrapper?.getAttribute("aria-hidden")).toBe("true");
expect(wrapper?.style.getPropertyValue("--floating-fab-tuck-offset")).toBe(
"calc(100% - 0.5rem + 18px)",
);
});

it("tucks the listen FAB near the editor caret instead of scroll state", () => {
hoisted.hasTranscript = false;
hoisted.isCaretNearBottom = true;
Expand Down
32 changes: 24 additions & 8 deletions apps/desktop/src/session/components/floating/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,17 @@ export function FloatingActionButton({
skipReason?: string | null;
tab: Extract<Tab, { type: "sessions" }>;
}) {
const shouldShowListen = useShouldShowListeningFab(tab);
const shouldShowChat = useShouldShowChatFab(tab);
const sessionMode = useListener((state) => state.getSessionMode(tab.id));
const isLiveSessionActive = sessionMode === "active";
const shouldShowListen = useShouldShowListeningFab(tab, sessionMode);
const shouldShowChat = useShouldShowChatFab(tab, sessionMode);
const isCaretNearBottom = useCaretPosition()?.isCaretNearBottom ?? false;
const showSkipReason = !!skipReason;
const showAction = shouldShowListen || shouldShowChat;
const tuckAction =
!showSkipReason &&
showAction &&
(isCaretNearBottom || (shouldShowChat && hidden));
(isCaretNearBottom || (shouldShowChat && (hidden || isLiveSessionActive)));

if (!showSkipReason && !showAction) {
return null;
Expand Down Expand Up @@ -91,16 +93,23 @@ export function FloatingActionButton({
);
}

function useShouldShowListeningFab(tab: Extract<Tab, { type: "sessions" }>) {
function useShouldShowListeningFab(
tab: Extract<Tab, { type: "sessions" }>,
sessionMode: string,
) {
const currentTab = useCurrentNoteTab(tab);
const hasTranscript = useHasTranscript(tab.id);

return currentTab.type === "raw" && !hasTranscript;
return (
sessionMode === "inactive" && currentTab.type === "raw" && !hasTranscript
);
}

function useShouldShowChatFab(tab: Extract<Tab, { type: "sessions" }>) {
function useShouldShowChatFab(
tab: Extract<Tab, { type: "sessions" }>,
sessionMode: string,
) {
const hasTranscript = useHasTranscript(tab.id);
const sessionMode = useListener((state) => state.getSessionMode(tab.id));
const currentTab = useCurrentNoteTab(tab);
const enhancedNoteId = currentTab.type === "enhanced" ? currentTab.id : null;
const taskId = enhancedNoteId
Expand All @@ -125,7 +134,14 @@ function useShouldShowChatFab(tab: Extract<Tab, { type: "sessions" }>) {
!hasContent &&
isBlockingLLMStatus(llmStatus)));

return hasTranscript && sessionMode === "inactive" && !hasVisibleIssue;
const canShowForSessionMode =
sessionMode === "inactive" || sessionMode === "active";

return (
canShowForSessionMode &&
(hasTranscript || sessionMode === "active") &&
!hasVisibleIssue
);
}

function isBlockingLLMStatus(status: LLMConnectionStatus) {
Expand Down
Loading