From efa357c260614af0141e13bb59f3c07a871abad2 Mon Sep 17 00:00:00 2001 From: DCCONSTRUCTIONS Date: Mon, 27 Apr 2026 21:19:36 +0300 Subject: [PATCH] =?UTF-8?q?UI=20-=20=D0=9C=D0=95=D0=96=D0=9F=D0=A0=D0=9E?= =?UTF-8?q?=D0=95=D0=9A=D0=A2=D0=9D=D0=90=D0=AF=20=D0=9A=D0=9E=D0=9C=D0=9C?= =?UTF-8?q?=D0=A3=D0=9D=D0=98=D0=9A=D0=90=D0=A6=D0=98=D0=AF:=20=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=BD=D0=BE=D1=81=20Voice=20Tasker=20=D0=B2?= =?UTF-8?q?=20=D0=BD=D0=B8=D0=B6=D0=BD=D0=B8=D0=B9=20dock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/core/components/core/app-header.tsx | 1 + .../voice-tasker/global-control.tsx | 50 ++++++++++++------- plane-src/apps/web/styles/globals.css | 28 +++++++++++ 3 files changed, 62 insertions(+), 17 deletions(-) diff --git a/plane-src/apps/web/core/components/core/app-header.tsx b/plane-src/apps/web/core/components/core/app-header.tsx index 988e9a6..fab9005 100644 --- a/plane-src/apps/web/core/components/core/app-header.tsx +++ b/plane-src/apps/web/core/components/core/app-header.tsx @@ -69,6 +69,7 @@ export const AppHeader = observer(function AppHeader(props: AppHeaderProps) { )} > +
{mobileHeader && mobileHeader}
diff --git a/plane-src/apps/web/core/components/voice-tasker/global-control.tsx b/plane-src/apps/web/core/components/voice-tasker/global-control.tsx index 7ad127c..f340fe8 100644 --- a/plane-src/apps/web/core/components/voice-tasker/global-control.tsx +++ b/plane-src/apps/web/core/components/voice-tasker/global-control.tsx @@ -6,6 +6,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import type { ElementType, MouseEvent, ReactNode } from "react"; +import { createPortal } from "react-dom"; import { useParams } from "next/navigation"; import useSWR from "swr"; import { @@ -760,6 +761,7 @@ export function VoiceTaskerGlobalControl({ workspaceSlug }: Props) { const [commitResult, setCommitResult] = useState(null); const [hasDraftChanges, setHasDraftChanges] = useState(false); const [selectedTargetIssue, setSelectedTargetIssue] = useState(null); + const [dockSlot, setDockSlot] = useState(null); const mediaRecorderRef = useRef(null); const discardedRecorderRef = useRef(null); @@ -787,6 +789,22 @@ export function VoiceTaskerGlobalControl({ workspaceSlug }: Props) { return UNAVAILABLE_LABELS[preflight.reason ?? "not_configured"]; }, [preflight]); + useEffect(() => { + if (typeof document === "undefined") return; + + const updateDockSlot = () => { + setDockSlot(document.querySelector("[data-nodedc-voice-task-dock-slot]")); + }; + + updateDockSlot(); + const observer = new MutationObserver(updateDockSlot); + observer.observe(document.body, { childList: true, subtree: true }); + + return () => { + observer.disconnect(); + }; + }, []); + const clearTimer = useCallback(() => { if (timerRef.current) { window.clearInterval(timerRef.current); @@ -1171,23 +1189,21 @@ export function VoiceTaskerGlobalControl({ workspaceSlug }: Props) { return ( <> -
- - - -
+ {isAvailable && dockSlot + ? createPortal( + + + , + dockSlot + ) + : null}