diff --git a/frontend/src/app/components/Layout/hooks/useUrlInterception.ts b/frontend/src/app/components/Layout/hooks/useUrlInterception.ts index 696cdc28..0c03b15d 100644 --- a/frontend/src/app/components/Layout/hooks/useUrlInterception.ts +++ b/frontend/src/app/components/Layout/hooks/useUrlInterception.ts @@ -4,7 +4,7 @@ import { useAppDispatch } from '@/shared/hooks'; import { CREATE_DASHBOARD } from '@/shared/backend-bridge/apps/dashboards'; import { addBrowserCard, addBrowserTab } from '@/shared/state/dashboardLayoutSlice'; import { setPendingBrowserUrl } from '@/shared/state/tempStateSlice'; -import { findBrowserByWebContentsId } from '@/shared/browserRegistry'; +import { findBrowserByWebContentsId } from '@/shared/browsers/browserRegistry'; export function useUrlInterception(dashboardList: { id: string }[]) { const dispatch = useAppDispatch(); diff --git a/frontend/src/app/pages/AgentChat/toolkit/nativeToolkit/components/CodeBlock/components/CodeBlockRoot/CodeBlockRoot.tsx b/frontend/src/app/pages/AgentChat/toolkit/nativeToolkit/components/CodeBlock/components/CodeBlockRoot/CodeBlockRoot.tsx index b63707e7..362cadea 100644 --- a/frontend/src/app/pages/AgentChat/toolkit/nativeToolkit/components/CodeBlock/components/CodeBlockRoot/CodeBlockRoot.tsx +++ b/frontend/src/app/pages/AgentChat/toolkit/nativeToolkit/components/CodeBlock/components/CodeBlockRoot/CodeBlockRoot.tsx @@ -5,7 +5,7 @@ import { useCallback, useEffect, createContext, - use, + useContext, type ReactNode, } from "react"; import { type Highlighter } from "shiki/bundle/web"; @@ -40,7 +40,7 @@ type CodeBlockSharedState = { const CodeBlockContext = createContext(null); export function useCodeBlock(): CodeBlockSharedState { - const context = use(CodeBlockContext); + const context = useContext(CodeBlockContext); if (!context) { throw new Error( "CodeBlock subcomponents must be used within .", diff --git a/frontend/src/app/pages/AgentChat/toolkit/nativeToolkit/components/CodeBlock/components/CodeBlockRoot/helpers.tsx b/frontend/src/app/pages/AgentChat/toolkit/nativeToolkit/components/CodeBlock/components/CodeBlockRoot/helpers.tsx index aeb844be..1240d331 100644 --- a/frontend/src/app/pages/AgentChat/toolkit/nativeToolkit/components/CodeBlock/components/CodeBlockRoot/helpers.tsx +++ b/frontend/src/app/pages/AgentChat/toolkit/nativeToolkit/components/CodeBlock/components/CodeBlockRoot/helpers.tsx @@ -5,6 +5,7 @@ import { createJavaScriptRegexEngine } from "shiki/engine/javascript"; import pierreDarkTheme from "../../../_shared/pierre-dark-theme.js"; import pierreLightTheme from "../../../_shared/pierre-light-theme.js"; import type { CodeBlockLineNumbersMode } from "../../schema"; +import { useEffect, useState } from "react"; const MAX_HTML_CACHE_ENTRIES = 64; @@ -53,4 +54,54 @@ export function setCachedHtml(cacheKey: string, html: string): void { } htmlCache.set(cacheKey, html); +} + + + +function getSystemTheme(): "light" | "dark" { + if (typeof window === "undefined") return "light"; + return window.matchMedia?.("(prefers-color-scheme: dark)").matches + ? "dark" + : "light"; +} + +function getDocumentTheme(): "light" | "dark" | null { + if (typeof document === "undefined") return null; + const root = document.documentElement; + const dataTheme = root.getAttribute("data-theme")?.toLowerCase(); + if (dataTheme === "dark") return "dark"; + if (dataTheme === "light") return "light"; + if (root.classList.contains("dark")) return "dark"; + if (root.classList.contains("light")) return "light"; + return null; +} + +export function useResolvedTheme(): "light" | "dark" { + const [theme, setTheme] = useState<"light" | "dark">(() => { + return getDocumentTheme() ?? getSystemTheme(); + }); + + useEffect(() => { + if (typeof window === "undefined" || typeof document === "undefined") { + return; + } + + const update = () => setTheme(getDocumentTheme() ?? getSystemTheme()); + + const mql = window.matchMedia?.("(prefers-color-scheme: dark)"); + mql?.addEventListener("change", update); + + const observer = new MutationObserver(update); + observer.observe(document.documentElement, { + attributes: true, + attributeFilter: ["class", "data-theme"], + }); + + return () => { + mql?.removeEventListener("change", update); + observer.disconnect(); + }; + }, []); + + return theme; } \ No newline at end of file diff --git a/frontend/src/app/pages/AgentChat/toolkit/nativeToolkit/components/CodeDiff/CodeDiff.tsx b/frontend/src/app/pages/AgentChat/toolkit/nativeToolkit/components/CodeDiff/CodeDiff.tsx index fbf7c4cc..4179846a 100644 --- a/frontend/src/app/pages/AgentChat/toolkit/nativeToolkit/components/CodeDiff/CodeDiff.tsx +++ b/frontend/src/app/pages/AgentChat/toolkit/nativeToolkit/components/CodeDiff/CodeDiff.tsx @@ -3,7 +3,6 @@ import { useState, useCallback, - useEffect, useMemo, createContext, useContext, @@ -19,6 +18,7 @@ import { Copy, Check, ChevronDown, ChevronUp } from "lucide-react"; import type { CodeDiffProps } from "./schema"; import { useCopyToClipboard } from "../_shared/useCopyToClipboard"; import { Button, cn, Collapsible, CollapsibleTrigger } from "./_adapter"; +import { useResolvedTheme } from "../CodeBlock/components/CodeBlockRoot/helpers"; /* * Pierre's shared_highlighter registers custom themes with dynamic imports @@ -38,54 +38,6 @@ const COPY_ID = "codediff-code"; /* ── Theme detection (mirrors CodeBlock) ────────────────────────── */ -function getSystemTheme(): "light" | "dark" { - if (typeof window === "undefined") return "light"; - return window.matchMedia?.("(prefers-color-scheme: dark)").matches - ? "dark" - : "light"; -} - -function getDocumentTheme(): "light" | "dark" | null { - if (typeof document === "undefined") return null; - const root = document.documentElement; - const dataTheme = root.getAttribute("data-theme")?.toLowerCase(); - if (dataTheme === "dark") return "dark"; - if (dataTheme === "light") return "light"; - if (root.classList.contains("dark")) return "dark"; - if (root.classList.contains("light")) return "light"; - return null; -} - -function useResolvedTheme(): "light" | "dark" { - const [theme, setTheme] = useState<"light" | "dark">(() => { - return getDocumentTheme() ?? getSystemTheme(); - }); - - useEffect(() => { - if (typeof window === "undefined" || typeof document === "undefined") { - return; - } - - const update = () => setTheme(getDocumentTheme() ?? getSystemTheme()); - - const mql = window.matchMedia?.("(prefers-color-scheme: dark)"); - mql?.addEventListener("change", update); - - const observer = new MutationObserver(update); - observer.observe(document.documentElement, { - attributes: true, - attributeFilter: ["class", "data-theme"], - }); - - return () => { - mql?.removeEventListener("change", update); - observer.disconnect(); - }; - }, []); - - return theme; -} - /* ── Language display names (mirrors CodeBlock) ─────────────────── */ const LANGUAGE_DISPLAY_NAMES: Record = { diff --git a/frontend/src/app/pages/Dashboard/DashboardCanvas/cards/BrowserCard/BrowserCard.tsx b/frontend/src/app/pages/Dashboard/DashboardCanvas/cards/BrowserCard/BrowserCard.tsx index eb227c82..8c2d0a5f 100644 --- a/frontend/src/app/pages/Dashboard/DashboardCanvas/cards/BrowserCard/BrowserCard.tsx +++ b/frontend/src/app/pages/Dashboard/DashboardCanvas/cards/BrowserCard/BrowserCard.tsx @@ -4,8 +4,8 @@ import Typography from '@mui/material/Typography'; import { setBrowserCardPosition, setBrowserCardSize, updateBrowserTabUrl, type BrowserTab } from '@/shared/state/dashboardLayoutSlice'; import { useAppDispatch, useAppSelector } from '@/shared/hooks'; import { useClaudeTokens } from '@/shared/styles/ThemeContext'; -import { useBrowserActivity } from '@/shared/useBrowserActivity'; -import { resolveInput, isGoogleSearch } from '@/shared/resolveUrl'; +import { useBrowserActivity } from './hooks/useBrowserActivity'; +import { resolveInput, isGoogleSearch } from '@/shared/browsers/resolveUrl'; import BrowserAgentOverlay from './components/BrowserAgentOverlay/BrowserAgentOverlay'; import { useOverlayScrollPassthrough } from '../shared/useOverlayScrollPassthrough'; import { useElementSelection } from '@/app/pages/_shared/element_selection/useElementSelection'; diff --git a/frontend/src/app/pages/Dashboard/DashboardCanvas/cards/BrowserCard/components/BrowserActionOverlay.tsx b/frontend/src/app/pages/Dashboard/DashboardCanvas/cards/BrowserCard/components/BrowserActionOverlay.tsx index 92c38a6a..2f2e52ec 100644 --- a/frontend/src/app/pages/Dashboard/DashboardCanvas/cards/BrowserCard/components/BrowserActionOverlay.tsx +++ b/frontend/src/app/pages/Dashboard/DashboardCanvas/cards/BrowserCard/components/BrowserActionOverlay.tsx @@ -3,7 +3,7 @@ import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import CircularProgress from '@mui/material/CircularProgress'; import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined'; -import { getActionLabel } from '@/shared/browserCommandHandler'; +import { getActionLabel } from '@/shared/browsers/browserCommandHandler/browserCommandTypes'; interface BrowserActionOverlayProps { agentAction: string | null; diff --git a/frontend/src/shared/useBrowserActivity.ts b/frontend/src/app/pages/Dashboard/DashboardCanvas/cards/BrowserCard/hooks/useBrowserActivity.ts similarity index 97% rename from frontend/src/shared/useBrowserActivity.ts rename to frontend/src/app/pages/Dashboard/DashboardCanvas/cards/BrowserCard/hooks/useBrowserActivity.ts index b0ff9b9e..740bfe6a 100644 --- a/frontend/src/shared/useBrowserActivity.ts +++ b/frontend/src/app/pages/Dashboard/DashboardCanvas/cards/BrowserCard/hooks/useBrowserActivity.ts @@ -4,7 +4,7 @@ import { getActivity, type BrowserActivity, type BrowserAction, -} from './browserCommandHandler'; +} from '@/shared/browsers/browserCommandHandler/browserCommandTypes'; interface BrowserActivityState { active: boolean; diff --git a/frontend/src/app/pages/Dashboard/DashboardCanvas/cards/BrowserCard/hooks/useWebviewLifecycle.ts b/frontend/src/app/pages/Dashboard/DashboardCanvas/cards/BrowserCard/hooks/useWebviewLifecycle.ts index 9b57dfe6..dd58291c 100644 --- a/frontend/src/app/pages/Dashboard/DashboardCanvas/cards/BrowserCard/hooks/useWebviewLifecycle.ts +++ b/frontend/src/app/pages/Dashboard/DashboardCanvas/cards/BrowserCard/hooks/useWebviewLifecycle.ts @@ -5,7 +5,7 @@ import { unregisterWebview, // only used in this file, maybe an aNr opportunity? -HD setActiveTab as setRegistryActiveTab, type BrowserWebview, -} from '@/shared/browserRegistry'; +} from '@/shared/browsers/browserRegistry'; import { updateBrowserTabUrl, updateBrowserTabTitle, diff --git a/frontend/src/app/pages/Dashboard/hooks/useDashboardInit.ts b/frontend/src/app/pages/Dashboard/hooks/useDashboardInit.ts index ee2b8ced..0a7b6565 100644 --- a/frontend/src/app/pages/Dashboard/hooks/useDashboardInit.ts +++ b/frontend/src/app/pages/Dashboard/hooks/useDashboardInit.ts @@ -12,7 +12,7 @@ import { } from '@/shared/state/dashboardLayoutSlice'; import { LIST_APPS } from '@/shared/backend-bridge/apps/app_builder'; import { agentsWs } from '@/shared/ws/WebSocketManager'; -import { initBrowserCommandHandler } from '@/shared/browserCommandHandler'; +import { initBrowserCommandHandler } from '@/shared/browsers/browserCommandHandler/browserCommandHandler'; import { clearPendingBrowserUrl, clearPendingFocusAgentId } from '@/shared/state/tempStateSlice'; import { type CanvasActions } from '@/app/pages/Dashboard/_shared/types'; diff --git a/frontend/src/shared/browserActionHandlers.ts b/frontend/src/shared/browsers/browserCommandHandler/browserActionHandlers.ts similarity index 99% rename from frontend/src/shared/browserActionHandlers.ts rename to frontend/src/shared/browsers/browserCommandHandler/browserActionHandlers.ts index f7a71085..26f187c6 100644 --- a/frontend/src/shared/browserActionHandlers.ts +++ b/frontend/src/shared/browsers/browserCommandHandler/browserActionHandlers.ts @@ -1,5 +1,5 @@ -import { type BrowserWebview } from './browserRegistry'; -import { resolveInput } from './resolveUrl'; +import { type BrowserWebview } from '../browserRegistry'; +import { resolveInput } from '../resolveUrl'; export async function handleScreenshot(wv: BrowserWebview): Promise> { const nativeImage = await wv.capturePage(); diff --git a/frontend/src/shared/browserCommandHandler.ts b/frontend/src/shared/browsers/browserCommandHandler/browserCommandHandler.ts similarity index 90% rename from frontend/src/shared/browserCommandHandler.ts rename to frontend/src/shared/browsers/browserCommandHandler/browserCommandHandler.ts index e01968d4..a3734572 100644 --- a/frontend/src/shared/browserCommandHandler.ts +++ b/frontend/src/shared/browsers/browserCommandHandler/browserCommandHandler.ts @@ -1,5 +1,5 @@ -import { getWebview } from './browserRegistry'; -import { agentsWs } from './ws/WebSocketManager'; +import { getWebview } from '../browserRegistry'; +import { agentsWs } from '@/shared/ws/WebSocketManager'; import { type BrowserAction, setActivity } from './browserCommandTypes'; import { handleScreenshot, @@ -13,9 +13,6 @@ import { handleEvaluate, } from './browserActionHandlers'; -export type { BrowserAction, BrowserActivity } from './browserCommandTypes'; -export { getActivity, subscribeActivity, getActionLabel } from './browserCommandTypes'; - let initialized = false; async function handleBrowserCommand(data: Record) { diff --git a/frontend/src/shared/browserCommandTypes.ts b/frontend/src/shared/browsers/browserCommandHandler/browserCommandTypes.ts similarity index 100% rename from frontend/src/shared/browserCommandTypes.ts rename to frontend/src/shared/browsers/browserCommandHandler/browserCommandTypes.ts diff --git a/frontend/src/shared/browserRegistry.ts b/frontend/src/shared/browsers/browserRegistry.ts similarity index 100% rename from frontend/src/shared/browserRegistry.ts rename to frontend/src/shared/browsers/browserRegistry.ts diff --git a/frontend/src/shared/resolveUrl.ts b/frontend/src/shared/browsers/resolveUrl.ts similarity index 100% rename from frontend/src/shared/resolveUrl.ts rename to frontend/src/shared/browsers/resolveUrl.ts