diff --git a/apps/desktop/src/session/components/floating/index.tsx b/apps/desktop/src/session/components/floating/index.tsx deleted file mode 100644 index ff0170298b..0000000000 --- a/apps/desktop/src/session/components/floating/index.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import { type ReactNode, useEffect, useRef, useState } from "react"; - -import { cn } from "@hypr/utils"; - -import { ListenButton } from "./listen"; - -import { useShell } from "~/contexts/shell"; -import { useCaretPosition } from "~/session/components/caret-position-context"; -import { - useCurrentNoteTab, - useHasTranscript, -} from "~/session/components/shared"; -import type { Tab } from "~/store/zustand/tabs/schema"; - -const SIDEBAR_WIDTH = 280; -const LAYOUT_PADDING = 4; - -export function FloatingActionButton({ - tab, -}: { - tab: Extract; -}) { - const currentTab = useCurrentNoteTab(tab); - const hasTranscript = useHasTranscript(tab.id); - - if (!(currentTab.type === "raw" && !hasTranscript)) { - return null; - } - - return ( - - - - ); -} - -function FloatingButtonContainer({ children }: { children: ReactNode }) { - const containerRef = useRef(null); - const caretPosition = useCaretPosition(); - const { leftsidebar, chat } = useShell(); - const isCaretNearBottom = caretPosition?.isCaretNearBottom ?? false; - const [chatPanelWidth, setChatPanelWidth] = useState(0); - const [isMouseNearButton, setIsMouseNearButton] = useState(false); - - const isChatPanelOpen = chat.mode === "RightPanelOpen"; - - useEffect(() => { - if (!isChatPanelOpen) { - setChatPanelWidth(0); - return; - } - - const updateChatWidth = () => { - const panels = document.querySelectorAll("[data-panel-id]"); - const lastPanel = panels[panels.length - 1]; - if (lastPanel) { - setChatPanelWidth(lastPanel.getBoundingClientRect().width); - } - }; - - updateChatWidth(); - window.addEventListener("resize", updateChatWidth); - - // Use ResizeObserver on the specific panel instead of MutationObserver on document.body - // MutationObserver on document.body with subtree:true causes high CPU usage - const resizeObserver = new ResizeObserver(updateChatWidth); - const panels = document.querySelectorAll("[data-panel-id]"); - const lastPanel = panels[panels.length - 1]; - if (lastPanel) { - resizeObserver.observe(lastPanel); - } - - return () => { - window.removeEventListener("resize", updateChatWidth); - resizeObserver.disconnect(); - }; - }, [isChatPanelOpen]); - - useEffect(() => { - if (!isCaretNearBottom) { - setIsMouseNearButton(false); - return; - } - - const handleMouseMove = (e: MouseEvent) => { - if (!containerRef.current) return; - const rect = containerRef.current.getBoundingClientRect(); - const threshold = 60; - const isNear = - e.clientX >= rect.left - threshold && - e.clientX <= rect.right + threshold && - e.clientY >= rect.top - threshold && - e.clientY <= rect.bottom + threshold; - setIsMouseNearButton(isNear); - }; - - window.addEventListener("mousemove", handleMouseMove); - return () => window.removeEventListener("mousemove", handleMouseMove); - }, [isCaretNearBottom]); - - const shouldHide = isCaretNearBottom && !isMouseNearButton; - - const leftOffset = leftsidebar.expanded - ? (SIDEBAR_WIDTH + LAYOUT_PADDING) / 2 - : 0; - const rightOffset = chatPanelWidth / 2; - const totalOffset = leftOffset - rightOffset; - - return ( -
- {children} -
- ); -} diff --git a/apps/desktop/src/session/components/floating/listen.tsx b/apps/desktop/src/session/components/floating/listen.tsx deleted file mode 100644 index b816bdc2e2..0000000000 --- a/apps/desktop/src/session/components/floating/listen.tsx +++ /dev/null @@ -1,184 +0,0 @@ -import { HeadsetIcon } from "lucide-react"; -import { useCallback, useEffect, useState } from "react"; - -import { commands as openerCommands } from "@hypr/plugin-opener2"; -import { Spinner } from "@hypr/ui/components/ui/spinner"; - -import { ListenActionButton } from "../listen-action"; -import { OptionsMenu } from "./options-menu"; -import { ActionableTooltipContent, FloatingButton } from "./shared"; - -import { useShell } from "~/contexts/shell"; -import { - RecordingIcon, - useListenButtonState, -} from "~/session/components/shared"; -import { useEventCountdown } from "~/session/hooks/useEventCountdown"; -import { - type RemoteMeeting, - useRemoteMeeting, -} from "~/session/hooks/useRemoteMeeting"; -import { type Tab, useTabs } from "~/store/zustand/tabs"; -import { useListener } from "~/stt/contexts"; -import { useStartListening } from "~/stt/useStartListening"; - -export function ListenButton({ - tab, -}: { - tab: Extract; -}) { - const { shouldRender } = useListenButtonState(tab.id); - const { loading, stop } = useListener((state) => ({ - loading: state.live.loading, - stop: state.stop, - })); - const remote = useRemoteMeeting(tab.id); - - if (loading) { - return ( - - - - ); - } - - if (!shouldRender) { - return null; - } - - if (remote) { - return ; - } - - return ; -} - -const SIDEBAR_WIDTH = 280; -const LAYOUT_PADDING = 4; -const EDITOR_WIDTH_THRESHOLD = 590; - -function SplitMeetingButtons({ - remote, - tab, -}: { - remote: RemoteMeeting; - tab: Extract; -}) { - const { isDisabled, warningMessage } = useListenButtonState(tab.id); - const startListening = useStartListening(tab.id); - const openNew = useTabs((state) => state.openNew); - const countdown = useEventCountdown(tab.id, { - onExpire: () => { - if (!isDisabled) { - startListening(); - } - }, - }); - const { leftsidebar } = useShell(); - const [isNarrow, setIsNarrow] = useState(false); - - useEffect(() => { - const calculateIsNarrow = () => { - const sidebarOffset = leftsidebar.expanded - ? SIDEBAR_WIDTH + LAYOUT_PADDING - : 0; - const availableWidth = window.innerWidth - sidebarOffset; - setIsNarrow(availableWidth < EDITOR_WIDTH_THRESHOLD); - }; - - calculateIsNarrow(); - window.addEventListener("resize", calculateIsNarrow); - return () => window.removeEventListener("resize", calculateIsNarrow); - }, [leftsidebar.expanded]); - - const handleJoin = useCallback(() => { - if (remote.url) { - void openerCommands.openUrl(remote.url, null); - } - }, [remote.url]); - - const handleConfigure = useCallback(() => { - startListening(); - openNew({ type: "ai", state: { tab: "transcription" } }); - }, [startListening, openNew]); - - const getMeetingIcon = () => { - switch (remote.type) { - case "zoom": - return ; - case "google-meet": - return ; - case "webex": - return ; - case "teams": - return ; - default: - return ; - } - }; - - const getMeetingName = () => { - switch (remote.type) { - case "zoom": - return "Zoom"; - case "google-meet": - return "Meet"; - case "webex": - return "Webex"; - case "teams": - return "Teams"; - } - }; - - return ( -
- {!isNarrow && ( - - Join - {getMeetingIcon()} - {getMeetingName()} - - )} - - - ), - } - : undefined - } - > - - Start listening - - - - {countdown.label && ( -
- {countdown.label} -
- )} -
- ); -} diff --git a/apps/desktop/src/session/index.tsx b/apps/desktop/src/session/index.tsx index 71c076638d..cdd52187a0 100644 --- a/apps/desktop/src/session/index.tsx +++ b/apps/desktop/src/session/index.tsx @@ -9,7 +9,6 @@ import { commands as fsSyncCommands } from "@hypr/plugin-fs-sync"; import { cn } from "@hypr/utils"; import { CaretPositionProvider } from "./components/caret-position-context"; -import { FloatingActionButton } from "./components/floating"; import { NoteInput } from "./components/note-input"; import { SearchProvider } from "./components/note-input/transcript/search/context"; import { OuterHeader } from "./components/outer-header"; @@ -252,7 +251,6 @@ function TabContentNoteInner({ <> } - floatingButton={} showTimeline={showTimeline} >