From bcc72c0228f266e63ffaa2708e431f17117e23f6 Mon Sep 17 00:00:00 2001 From: ciregenz Date: Sat, 23 May 2026 02:40:11 -0700 Subject: [PATCH] [eric] cleanup: drop dead exports, fix invoke-result regex, strip em-dashes (frontend) --- .../src/app/pages/AgentChat/AgentChat.tsx | 18 +++++------ .../app/pages/AgentChat/ToolCallBubble.tsx | 2 +- .../src/app/pages/Dashboard/AgentCard.tsx | 16 +++++----- .../src/app/pages/Dashboard/Dashboard.tsx | 32 +++++++++---------- .../app/pages/Dashboard/DashboardToolbar.tsx | 2 +- .../pages/Dashboard/useDashboardSelection.ts | 2 +- frontend/src/shared/browserRegistry.ts | 16 ---------- frontend/src/shared/hooks/useReducedMotion.ts | 7 ---- frontend/src/shared/serviceClient.ts | 11 ------- .../src/shared/state/dashboardLayoutSlice.ts | 10 +++--- frontend/src/shared/state/outputsSlice.ts | 10 ------ frontend/src/shared/state/store.ts | 2 +- .../src/shared/state/subscriptionsSlice.ts | 9 ------ frontend/src/shared/styles/getStyleValue.tsx | 12 ------- frontend/src/shared/styles/motionTokens.ts | 16 ---------- frontend/src/shared/ws/WebSocketManager.ts | 30 ++++++++--------- 16 files changed, 57 insertions(+), 138 deletions(-) delete mode 100644 frontend/src/shared/styles/getStyleValue.tsx diff --git a/frontend/src/app/pages/AgentChat/AgentChat.tsx b/frontend/src/app/pages/AgentChat/AgentChat.tsx index f19c90a9..06682b7f 100644 --- a/frontend/src/app/pages/AgentChat/AgentChat.tsx +++ b/frontend/src/app/pages/AgentChat/AgentChat.tsx @@ -225,7 +225,7 @@ const AgentChat: React.FC = ({ sessionId: sessionIdProp, onClose // events starting at last_seq=0, which includes every stream_* // event for messages that finished before the disconnect. The // replay-skip guard in WebSocketManager._messageAlreadyComplete - // checks `session.messages` to decide whether to drop deltas — so + // checks `session.messages` to decide whether to drop deltas , so // if we connect first, the slice is empty when the replay arrives, // the guard returns false, and the user sees the chat type itself // out again. Awaiting fetchSession before connect makes the slice @@ -234,7 +234,7 @@ const AgentChat: React.FC = ({ sessionId: sessionIdProp, onClose try { await dispatch(fetchSession(id)); } catch { - // Even if the REST hydrate fails, still connect — the WS resume + // Even if the REST hydrate fails, still connect , the WS resume // protocol can hydrate from buffered events as a fallback. } if (cancelled) return; @@ -885,7 +885,7 @@ const AgentChat: React.FC = ({ sessionId: sessionIdProp, onClose if (!(session.cost_usd > 0)) return null; // The SDK reports a per-call $ figure regardless of how // the request was routed. For requests that went through - // a subscription path, that figure is misleading — the + // a subscription path, that figure is misleading , the // user pays flat-rate. Show "subscription" instead in // those cases. Show $ only when the call was actually // metered (Anthropic API key, OpenAI API key, etc.). @@ -919,7 +919,7 @@ const AgentChat: React.FC = ({ sessionId: sessionIdProp, onClose subscription @@ -1013,18 +1013,18 @@ const AgentChat: React.FC = ({ sessionId: sessionIdProp, onClose overflow: 'auto', px: 2, py: 1, - // Smoothness bundle (perf-only — no behavior change): - // 1. overflow-anchor: auto — Chromium's native scroll + // Smoothness bundle (perf-only , no behavior change): + // 1. overflow-anchor: auto , Chromium's native scroll // anchoring keeps the viewport pinned to the user's // visible content as siblings above/below resize. // Eliminates the "transcript snaps back" feel during // streaming and parallel tool fan-outs. Runs on the // compositor thread, free. - // 2. contain: layout — tells the browser layout shifts + // 2. contain: layout , tells the browser layout shifts // inside this scroll container don't affect siblings // outside it. Prevents reflow from cascading up to // the dashboard layout when bubbles grow. - // 3. overscroll-behavior: contain — keeps over-scroll + // 3. overscroll-behavior: contain , keeps over-scroll // gestures from leaking up to the dashboard pan/zoom // when the user hits the chat top/bottom. overflowAnchor: 'auto', @@ -1649,7 +1649,7 @@ const AgentChat: React.FC = ({ sessionId: sessionIdProp, onClose Haiku is the fastest Claude model but holds the least at once. Each connected app adds instructions Claude has to read first. If your message fails with “Prompt is too long,” turn off a few - apps (Microsoft 365 is the heaviest) or switch to Sonnet/Opus — + apps (Microsoft 365 is the heaviest) or switch to Sonnet/Opus, both have 5× more room. diff --git a/frontend/src/app/pages/AgentChat/ToolCallBubble.tsx b/frontend/src/app/pages/AgentChat/ToolCallBubble.tsx index 9f92628d..94a339af 100644 --- a/frontend/src/app/pages/AgentChat/ToolCallBubble.tsx +++ b/frontend/src/app/pages/AgentChat/ToolCallBubble.tsx @@ -1241,7 +1241,7 @@ function parseCreateAgentResult(rawText: string): string { function parseInvokeAgentResult(rawText: string): InvokeAgentParsed | null { const headerMatch = rawText.match( - /\*\*Invoked Agent Result\*\*(?:\s*—\s*(.+?))?\s*\(forked session:\s*([a-f0-9]+)\)/, + /\*\*Invoked Agent Result\*\*(?:\s*;\s*(.+?))?\s*\(forked session:\s*([a-f0-9]+)\)/, ); if (!headerMatch) return null; diff --git a/frontend/src/app/pages/Dashboard/AgentCard.tsx b/frontend/src/app/pages/Dashboard/AgentCard.tsx index 3fdca4c6..2507f7f9 100644 --- a/frontend/src/app/pages/Dashboard/AgentCard.tsx +++ b/frontend/src/app/pages/Dashboard/AgentCard.tsx @@ -106,7 +106,7 @@ function getAgentWorkTime( // True wall-clock duration: how long the user actually waited, from // their prompt to the LAST assistant/system message of that turn. // Covers thinking + every tool call + assistant text generation + - // any subagent/MCP work — anything that consumed user attention. + // any subagent/MCP work , anything that consumed user attention. // // This is intentionally NOT the sum of `thinking.elapsed_ms` (which // would cover only reasoning time and miss tool execution). The @@ -115,13 +115,13 @@ function getAgentWorkTime( // did this take?" which is a different question. // // For each user message we find the LAST adjacent assistant/system - // message before the next user message — that's the turn boundary. + // message before the next user message , that's the turn boundary. // If the turn is still in flight (last user message has no assistant // reply yet AND session is running/waiting), extrapolate to now so // the timer ticks live. // // Hidden messages (auto-continuation prompts from MCPActivate, etc.) - // are skipped — they're system-internal turns the user didn't see + // are skipped , they're system-internal turns the user didn't see // and shouldn't be billed for. const visible = messages.filter((m) => !m.hidden); let totalMs = 0; @@ -259,7 +259,7 @@ const HANDLE_DEFS: { dir: ResizeDir; sx: Record }[] = [ interface OuterProps { sessionId: string; expanded: boolean; - // Stable getter — cards read pan/zoom on demand (drag math) instead of + // Stable getter , cards read pan/zoom on demand (drag math) instead of // receiving them as props. Without this, every wheel/pan tick on the // canvas re-rendered every card, even though the canvas root's CSS // transform is what actually moves them visually. Cards only need the @@ -346,7 +346,7 @@ const AgentCard: React.FC = ({ // through so the layout reconciles to the truth right then. let suppressedHeight: number | null = null; const ro = new ResizeObserver((entries) => { - // Short-circuit when dashboard is hidden — observer stays attached so + // Short-circuit when dashboard is hidden , observer stays attached so // the next resize after returning to the dashboard fires correctly. if (!isDashboardActiveRef.current) return; // Short-circuit during active canvas interaction (pan/drag/wheel). @@ -690,7 +690,7 @@ const AgentCard: React.FC = ({ position: 'relative', // contain: streaming chat updates inside don't reflow the dashboard. // Skipping `paint` here because the highlighted/selected/glow - // boxShadows legitimately extend past the card border — `paint` + // boxShadows legitimately extend past the card border , `paint` // containment would clip those visuals. contain: 'layout style', // Promote each card to its own compositor layer so paint @@ -902,7 +902,7 @@ const AgentCard: React.FC = ({ /> ))} - {/* Selection overlay – blocks click interaction while selected, enabling drag from anywhere */} + {/* Selection overlay , blocks click interaction while selected, enabling drag from anywhere */} {isSelected && ( = ({ /> )} - {/* Drag zone: header + metadata – entire region above separator is draggable */} + {/* Drag zone: header + metadata , entire region above separator is draggable */} = ({ dashboardId, isActive = true const [pendingSelectSessionId, setPendingSelectSessionId] = useState(null); const [focusedCardId, setFocusedCardId] = useState(null); const [newAgentBounce, setNewAgentBounce] = useState(false); - // Cleanup any leftover walkthrough localStorage from v1 — the v2 panel + // Cleanup any leftover walkthrough localStorage from v1 , the v2 panel // ignores it but it would otherwise hang around forever. useEffect(() => { try { @@ -213,7 +213,7 @@ const DashboardInner: React.FC = ({ dashboardId, isActive = true const restoredExpandedRef = useRef(false); const canvasStateRef = useRef({ panX: canvas.panX, panY: canvas.panY, zoom: canvas.zoom }); canvasStateRef.current = { panX: canvas.panX, panY: canvas.panY, zoom: canvas.zoom }; - // Stable getter — AgentCards read pan/zoom on demand during drag math. + // Stable getter , AgentCards read pan/zoom on demand during drag math. const getCanvasState = useCallback(() => canvasStateRef.current, []); // Notify the currently dragging card (if any) that pan/zoom changed so // it can re-pin to the cursor. useEffect rather than render-body @@ -402,7 +402,7 @@ const DashboardInner: React.FC = ({ dashboardId, isActive = true if (e.button !== 0) return; if (isCardTarget(e.target, e.currentTarget)) return; - // Canvas click — drop any lingering input focus so arrow-key nav + // Canvas click , drop any lingering input focus so arrow-key nav // works immediately without the user having to press Escape first. const active = document.activeElement as HTMLElement | null; const activeTag = active?.tagName; @@ -522,7 +522,7 @@ const DashboardInner: React.FC = ({ dashboardId, isActive = true ); for (const s of dashSessions) { if (warmAbort.signal.aborted) break; - // Fire-and-forget — the endpoint always 200s and the side + // Fire-and-forget , the endpoint always 200s and the side // effect is invisible cache population. fetch(`${API_BASE}/agents/sessions/${s.id}/warm-cache`, { method: 'POST', @@ -584,7 +584,7 @@ const DashboardInner: React.FC = ({ dashboardId, isActive = true || Object.keys(allCards.viewCards).length > 0 || Object.keys(allCards.browserCards).length > 0; if (!hasCards) { - // Empty dashboard — queue a thumbnail clear (sent on exit alongside + // Empty dashboard , queue a thumbnail clear (sent on exit alongside // the existing capture-update path). Backend treats '' as "set to // empty"; null in PUT body means "don't update". pendingThumbnailRef.current = ''; @@ -710,7 +710,7 @@ const DashboardInner: React.FC = ({ dashboardId, isActive = true const prevParentStatusRef = useRef>({}); useEffect(() => { - if (!isActive) return; // Heavy logic — pause when dashboard is hidden + if (!isActive) return; // Heavy logic , pause when dashboard is hidden if (!layoutInitialized || !autoRevealSubAgents) return; const subSessions = Object.values(sessions).filter( @@ -816,7 +816,7 @@ const DashboardInner: React.FC = ({ dashboardId, isActive = true const pendingSaveRef = useRef[0] | null>(null); useEffect(() => { - if (!isActive) return; // Don't persist layout while dashboard is hidden — save buffers in pendingSaveRef and flushes on resume + if (!isActive) return; // Don't persist layout while dashboard is hidden , save buffers in pendingSaveRef and flushes on resume if (!layoutInitialized || !dashboardId) return; if (skipInitialSave.current) { skipInitialSave.current = false; @@ -1132,7 +1132,7 @@ const DashboardInner: React.FC = ({ dashboardId, isActive = true const handleKey = (e: KeyboardEvent) => { if (!isActive) return; // Don't fire shortcuts when dashboard is hidden - // Escape blurs any active input and restores focus to the canvas — + // Escape blurs any active input and restores focus to the canvas , // so you can quickly "unstick" keyboard focus and start navigating. if (e.key === 'Escape') { const active = document.activeElement as HTMLElement | null; @@ -1172,7 +1172,7 @@ const DashboardInner: React.FC = ({ dashboardId, isActive = true const target = findNearestCard(currentFocused, direction); if (!target) { - // No card in that direction — shake + // No card in that direction , shake if (shakeTimerRef.current) clearTimeout(shakeTimerRef.current); setShakeDirection(direction); shakeTimerRef.current = setTimeout(() => { @@ -1311,7 +1311,7 @@ const DashboardInner: React.FC = ({ dashboardId, isActive = true if (bc) { // Use placeCard (collision-aware) instead of // setCardPosition (blind setter). The "left of the - // browser" anchor is the IDEAL spot — but if it's + // browser" anchor is the IDEAL spot , but if it's // already taken by an existing chat (e.g. step 3's // YouTube agent that's still on canvas when step 5 // creates a new chat for the same browser), placeCard @@ -1466,7 +1466,7 @@ const DashboardInner: React.FC = ({ dashboardId, isActive = true }, [dispatch, canvas.actions]); useEffect(() => { - if (!isActive) return; // Heavy geometry recalculation — pause when dashboard is hidden + if (!isActive) return; // Heavy geometry recalculation , pause when dashboard is hidden const DRIFT_THRESHOLD = 60; // Group tethered sub-agent cards by source, only including those still in the spawn column @@ -1507,7 +1507,7 @@ const DashboardInner: React.FC = ({ dashboardId, isActive = true }, [isActive, expandedSessionIds, glowingAgentCards, cards, dispatch, measuredHeightsTick]); useEffect(() => { - if (!isActive) return; // Heavy geometry recalculation — pause when dashboard is hidden + if (!isActive) return; // Heavy geometry recalculation , pause when dashboard is hidden const DRIFT_THRESHOLD = 60; const sourceToSiblings = new Map(); @@ -1604,8 +1604,8 @@ const DashboardInner: React.FC = ({ dashboardId, isActive = true }).filter(Boolean) as Array<{ key: string; path: string; labelX: number; labelY: number; label: string; fading: boolean }>; // Build browser tethers from TWO sources and merge: - // 1. glowingBrowserCards — the short-lived "flash" when a browser is first assigned - // 2. Active browser-agent sessions — persistent as long as the agent runs + // 1. glowingBrowserCards , the short-lived "flash" when a browser is first assigned + // 2. Active browser-agent sessions , persistent as long as the agent runs // // Source #2 is the fix for tethers disappearing when the parent session // completes a turn (which clears glowingBrowserCards even though the @@ -1713,7 +1713,7 @@ const DashboardInner: React.FC = ({ dashboardId, isActive = true if (t) glowTethers.set(browserId, t); } - // Source 2: active browser-agent sessions (persistent — survives parent turn completion) + // Source 2: active browser-agent sessions (persistent , survives parent turn completion) for (const s of sessionList) { if (s.mode !== 'browser-agent') continue; if (s.status !== 'running' && s.status !== 'waiting_approval') continue; diff --git a/frontend/src/app/pages/Dashboard/DashboardToolbar.tsx b/frontend/src/app/pages/Dashboard/DashboardToolbar.tsx index 84808933..1ea5e13e 100644 --- a/frontend/src/app/pages/Dashboard/DashboardToolbar.tsx +++ b/frontend/src/app/pages/Dashboard/DashboardToolbar.tsx @@ -107,7 +107,7 @@ const DashboardToolbar = React.forwardRef( // backend. Without the settingsLoaded guard, the effect fires against the // Redux initialState ('sonnet') before the real default has loaded, and // the settingsApplied flag then locks out the real default for the rest - // of the session — so new chats spawn under the stale value. + // of the session , so new chats spawn under the stale value. const settingsApplied = useRef(false); useEffect(() => { if (settingsLoaded && !settingsApplied.current) { diff --git a/frontend/src/app/pages/Dashboard/useDashboardSelection.ts b/frontend/src/app/pages/Dashboard/useDashboardSelection.ts index c89590a0..211234c5 100644 --- a/frontend/src/app/pages/Dashboard/useDashboardSelection.ts +++ b/frontend/src/app/pages/Dashboard/useDashboardSelection.ts @@ -244,7 +244,7 @@ export function useDashboardSelection( // Inject (once) a global CSS rule that makes browser webviews and iframes // transparent to mouse events while a marquee drag is active. Without this, - // the Electron hit-tests the cursor at the OS level — when the + // the Electron hit-tests the cursor at the OS level , when the // cursor lands on an interactable element inside the browser (button, // link, text), the webview steals the cursor and the marquee drag visually // freezes until the cursor escapes. Setting `pointer-events: none` makes diff --git a/frontend/src/shared/browserRegistry.ts b/frontend/src/shared/browserRegistry.ts index eed8f78b..604508e5 100644 --- a/frontend/src/shared/browserRegistry.ts +++ b/frontend/src/shared/browserRegistry.ts @@ -44,14 +44,6 @@ export function getWebview(browserId: string, tabId?: string): BrowserWebview | return registry.get(makeKey(browserId, resolvedTabId)); } -export function getActiveTabId(browserId: string): string | undefined { - return activeTabMap.get(browserId); -} - -export function getAllWebviews(): Map { - return new Map(registry); -} - export function findBrowserByWebContentsId(wcId: number): string | undefined { for (const [key, wv] of registry.entries()) { if ((wv as any).getWebContentsId?.() === wcId) { @@ -60,11 +52,3 @@ export function findBrowserByWebContentsId(wcId: number): string | undefined { } return undefined; } - -export function unregisterAllForBrowser(browserId: string): void { - const prefix = `${browserId}:`; - for (const key of registry.keys()) { - if (key.startsWith(prefix)) registry.delete(key); - } - activeTabMap.delete(browserId); -} diff --git a/frontend/src/shared/hooks/useReducedMotion.ts b/frontend/src/shared/hooks/useReducedMotion.ts index 2205c4f4..c933ae84 100644 --- a/frontend/src/shared/hooks/useReducedMotion.ts +++ b/frontend/src/shared/hooks/useReducedMotion.ts @@ -22,10 +22,3 @@ function getServerSnapshot(): boolean { export function useReducedMotion(): boolean { return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); } - -/** Returns 0 (or `opts.floor`) when reduced-motion is on, else `ms`. */ -export function useMotionDuration(ms: number, opts: { floor?: number } = {}): number { - const reduced = useReducedMotion(); - if (!reduced) return ms; - return opts.floor ?? 0; -} diff --git a/frontend/src/shared/serviceClient.ts b/frontend/src/shared/serviceClient.ts index 014a3021..762857cb 100644 --- a/frontend/src/shared/serviceClient.ts +++ b/frontend/src/shared/serviceClient.ts @@ -93,16 +93,5 @@ export function getSessionTraceState(): { }; } -export function _resetForTest(): void { - _queue.length = 0; - if (_flushTimer != null) { - clearTimeout(_flushTimer); - _flushTimer = null; - } - _appStart = Date.now(); - _lastTs = _appStart; - _recentReports.length = 0; -} - const serviceClient = { sync, report, getSessionTraceState, getRecentActions }; export default serviceClient; diff --git a/frontend/src/shared/state/dashboardLayoutSlice.ts b/frontend/src/shared/state/dashboardLayoutSlice.ts index 33ea67ae..cc360f82 100644 --- a/frontend/src/shared/state/dashboardLayoutSlice.ts +++ b/frontend/src/shared/state/dashboardLayoutSlice.ts @@ -247,7 +247,7 @@ export function findOpenGridCell( // sibling). Spirals outward from the anchor on a grid, snapping to // cell-aligned positions so the result still looks intentional, not // dropped from orbit. Caps the spiral search at ~1000 cells to avoid -// pathological work in adversarial layouts — falls back to +// pathological work in adversarial layouts , falls back to // findOpenGridCell after that. // // Cost: O(rects × cells_scanned). Spawn events are rare (not per-frame), @@ -304,7 +304,7 @@ export function findOpenSpotNear( } } - // Pathological — full canvas occupied near anchor. Fall back to the + // Pathological , full canvas occupied near anchor. Fall back to the // global first-empty scan so we never return an overlap. return findOpenGridCell(occupiedRects, newW, newH); } @@ -347,8 +347,8 @@ const dashboardLayoutSlice = createSlice({ height: number; // Optional: which existing sessions are currently expanded // (showing their full chat history). Without this, the collision - // check uses each card's STORED height — which is the collapsed - // value — even when the card is currently rendering at the + // check uses each card's STORED height , which is the collapsed + // value , even when the card is currently rendering at the // expanded ~620px. Result: new sub-agent cards spawn into the // collapsed footprint but overlap the visually expanded one. // Caller (Dashboard.tsx) passes the current expanded set so @@ -559,7 +559,7 @@ const dashboardLayoutSlice = createSlice({ const h = card.height || DEFAULT_BROWSER_CARD_H; // Collision-resolve the backend-proposed position. Backend agents // often spawn sub-browsers at the parent's coordinates or at a - // default (0,0) — without this guard, the new card lands on top + // default (0,0) , without this guard, the new card lands on top // of an existing one and the user sees a single card with // multiple titles fighting for the z-index. Bias toward the // proposed position so the spawn still LOOKS related to wherever diff --git a/frontend/src/shared/state/outputsSlice.ts b/frontend/src/shared/state/outputsSlice.ts index 08a9f0b7..718b452c 100644 --- a/frontend/src/shared/state/outputsSlice.ts +++ b/frontend/src/shared/state/outputsSlice.ts @@ -39,16 +39,6 @@ export function buildServeUrl( return `${SERVE_BASE}/${outputId}/serve/index.html?_d=${encodeURIComponent(encoded)}`; } -export function buildWorkspaceServeUrl( - workspaceId: string, - inputData: Record = {}, - backendResult: Record | null = null, -): string { - const dataPayload = JSON.stringify({ i: inputData, r: backendResult }); - const encoded = btoa(unescape(encodeURIComponent(dataPayload))); - return `${SERVE_BASE}/workspace/${workspaceId}/serve/index.html?_d=${encodeURIComponent(encoded)}`; -} - export interface OutputExecuteResult { output_id: string; output_name: string; diff --git a/frontend/src/shared/state/store.ts b/frontend/src/shared/state/store.ts index ada25436..93b53081 100644 --- a/frontend/src/shared/state/store.ts +++ b/frontend/src/shared/state/store.ts @@ -44,7 +44,7 @@ export const store = configureStore({ // "SerializableStateInvariantMiddleware took 41ms" repeatedly under load. // // Production builds skip these middlewares anyway, so disabling them in - // dev makes dev behavior match prod — no surprises at packaging time. + // dev makes dev behavior match prod , no surprises at packaging time. // Trade-off: serializability bugs (e.g. accidentally putting a Map or // Date directly into state) won't be caught at dev time. We've shipped // many versions with stable slice shapes; that risk is now low. diff --git a/frontend/src/shared/state/subscriptionsSlice.ts b/frontend/src/shared/state/subscriptionsSlice.ts index 918cc132..864c96ac 100644 --- a/frontend/src/shared/state/subscriptionsSlice.ts +++ b/frontend/src/shared/state/subscriptionsSlice.ts @@ -71,15 +71,6 @@ export function selectSubscriptionConnections( return providers.connections ?? []; } -export function isProviderConnected( - state: WithSubscriptions, - providerId: string, -): boolean { - return selectSubscriptionConnections(state).some( - (p) => p.provider === providerId && (p.isActive || p.testStatus === 'active'), - ); -} - export function hasAnyActiveSubscription(state: WithSubscriptions): boolean { return selectSubscriptionConnections(state).some( (p) => p.isActive || p.testStatus === 'active', diff --git a/frontend/src/shared/styles/getStyleValue.tsx b/frontend/src/shared/styles/getStyleValue.tsx deleted file mode 100644 index 7f6162fd..00000000 --- a/frontend/src/shared/styles/getStyleValue.tsx +++ /dev/null @@ -1,12 +0,0 @@ -export const getStyleValue = (className: string, property: string, defaultValue: string = "none"): string => { - if (typeof document !== 'undefined') { - const element = document.createElement("div"); - element.setAttribute("class", className); - document.body.appendChild(element); - const style = window.getComputedStyle(element); - const value = style.getPropertyValue(property); - document.body.removeChild(element); - return value || defaultValue; - } - return defaultValue; // Return default value if not in a browser environment -}; \ No newline at end of file diff --git a/frontend/src/shared/styles/motionTokens.ts b/frontend/src/shared/styles/motionTokens.ts index 10f283d3..e8815d8b 100644 --- a/frontend/src/shared/styles/motionTokens.ts +++ b/frontend/src/shared/styles/motionTokens.ts @@ -24,22 +24,6 @@ export const EASE = { pulse: 'cubic-bezier(0.4, 0, 0.6, 1)', } as const; -/** Array-form easing for framer-motion; same curves as EASE. */ -export const FRAMER_EASE = { - out: [0.16, 1, 0.3, 1] as [number, number, number, number], - inOut: [0.4, 0, 0.2, 1] as [number, number, number, number], - spring: [0.34, 1.56, 0.64, 1] as [number, number, number, number], - pulse: [0.4, 0, 0.6, 1] as [number, number, number, number], -}; - -/** Shared fadeIn keyframe; import once. */ -export const fadeInKeyframes = { - '@keyframes openswarmFadeIn': { - from: { opacity: 0 }, - to: { opacity: 1 }, - }, -}; - /** Shared skeleton/indicator pulse keyframe. */ export const pulseKeyframes = { '@keyframes openswarmPulse': { diff --git a/frontend/src/shared/ws/WebSocketManager.ts b/frontend/src/shared/ws/WebSocketManager.ts index 315e8e03..667f3670 100644 --- a/frontend/src/shared/ws/WebSocketManager.ts +++ b/frontend/src/shared/ws/WebSocketManager.ts @@ -30,7 +30,7 @@ import { notifyAgentCompletion } from '../notifications'; // Thin wrapper around getAuthToken so the connect() call site stays // synchronous. If the token isn't cached yet, returns '' and the WS -// handshake will 4401 — onclose catches that and refreshes the token +// handshake will 4401 , onclose catches that and refreshes the token // before the next reconnect. const _getAuthTokenSafe = (): string => { try { return getAuthToken() || ''; } catch { return ''; } @@ -38,7 +38,7 @@ const _getAuthTokenSafe = (): string => { const _genUuid = (): string => { - // Avoid pulling in `crypto.randomUUID` for compat — this is a + // Avoid pulling in `crypto.randomUUID` for compat , this is a // disambiguator, not a security boundary, so a 96-bit hex string is // plenty. const a = Math.floor(Math.random() * 2 ** 32).toString(16).padStart(8, '0'); @@ -89,7 +89,7 @@ class WebSocketManager { // Resume state. lastSeq is the highest server-assigned seq this // client has applied; it's sent on every (re)connect so the server // can replay missed events. Persists for the lifetime of this - // WebSocketManager instance — when the user navigates away and a + // WebSocketManager instance , when the user navigates away and a // new createSessionWs() is constructed, lastSeq starts at 0 and we // get a full replay. private connectionUuid: string; @@ -112,7 +112,7 @@ class WebSocketManager { private pongTimeoutTimer: ReturnType | null = null; // Outbound queue. Frames the user enqueues while the WS isn't - // OPEN — or while OPEN but pre-resume-ack — wait here and flush + // OPEN , or while OPEN but pre-resume-ack , wait here and flush // after the resume handshake completes. Queue is in-memory only: // surviving a full app restart isn't worth the localStorage // complexity given how rare that case is for a transient drop. @@ -122,7 +122,7 @@ class WebSocketManager { // Frame-aligned message coalescer. Buffers incoming WS messages from // all WebSocketManager instances and flushes them in ONE batched // React render per animation frame. Without this, N concurrent agents - // each cause their own renders on every WS message — dozens of full + // each cause their own renders on every WS message , dozens of full // app re-renders per second, fanning out to every useSelector. With // it: max one render per frame regardless of message volume. private static _messageQueue: Array<{ mgr: WebSocketManager; msg: WSEvent }> = []; @@ -220,7 +220,7 @@ class WebSocketManager { // Buffer incoming messages and flush them per animation frame // in a single React batch. With N concurrent agents/browsers // streaming, each WS instance used to trigger its own React - // render — dozens per frame, fanning out to every useSelector + // render , dozens per frame, fanning out to every useSelector // subscriber, starving the main thread. Coalescing flips that // to ONE batched render per frame regardless of how many // messages arrived. Stream deltas dispatch directly into Redux @@ -254,7 +254,7 @@ class WebSocketManager { }; this.ws.onerror = () => { - // Force the close path to run — onclose will mark state + // Force the close path to run , onclose will mark state // reconnecting and schedule a retry. this.ws?.close(); }; @@ -312,7 +312,7 @@ class WebSocketManager { try { this.ws.send(JSON.stringify({ event: 'client:ping', data: { nonce } })); } catch { - // socket dying — let the close handler take over + // socket dying , let the close handler take over return; } if (this.pongTimeoutTimer != null) clearTimeout(this.pongTimeoutTimer); @@ -397,7 +397,7 @@ class WebSocketManager { // REST so the slice's view doesn't have a silent gap. if (session_id) { store.dispatch(fetchSession(session_id)); - // Reset lastSeq — the REST refetch is the new authoritative + // Reset lastSeq , the REST refetch is the new authoritative // baseline; subsequent server events with seq numbers will // re-establish the high-water mark. Also wipe the cross-mount // persistent map so a remount during this gap window doesn't @@ -514,7 +514,7 @@ class WebSocketManager { // The discriminator is `resumeAcked`: it flips to true when // server:hello arrives, which the server sends AFTER the replay // completes. Any stream_* event arriving while !resumeAcked is - // replay-from-buffer (historical) and can be dropped — the REST + // replay-from-buffer (historical) and can be dropped , the REST // snapshot we awaited before connect is authoritative for any // already-finalized message, and any genuinely live turn the // server is pushing will continue emitting events after the ack. @@ -600,7 +600,7 @@ class WebSocketManager { // compacted_through_msg_id locally so the renderer can drop a // visible "N earlier turns summarized" chip into the transcript. // Other reasons (cleared, etc.) flow through this same event but - // don't currently need a chip — ignore them for now. + // don't currently need a chip , ignore them for now. if (session_id && data.reason === 'compacted') { store.dispatch(recordCompaction({ sessionId: session_id, @@ -623,7 +623,7 @@ class WebSocketManager { break; case 'agent:auth_error': - // Re-uses the context_overflow card slot — both are "this session is + // Re-uses the context_overflow card slot , both are "this session is // blocked, here's what to do" cards. Reason field disambiguates. if (session_id) { store.dispatch(setContextOverflow({ @@ -690,7 +690,7 @@ class WebSocketManager { dashboard_id: data.dashboard_id, })); // Auto-delete browsers spawned by this agent when it finishes - // normally or errors out. We intentionally skip 'stopped' — the + // normally or errors out. We intentionally skip 'stopped' , the // user may want to inspect the browser after manually stopping. if (closedStatus === 'completed' || closedStatus === 'error') { const browserCards = store.getState().dashboardLayout.browserCards; @@ -746,7 +746,7 @@ class WebSocketManager { send(event: string, data: Record) { // Queue if the socket isn't open OR resume hasn't been ack'd yet. // The pre-ack gate prevents an outbound user message from racing - // the resume replay — the server might process the message + // the resume replay , the server might process the message // before the replay finishes, leaving the slice's view of // history incomplete. const open = this.ws?.readyState === WebSocket.OPEN; @@ -815,7 +815,7 @@ export const dashboardWs = new WebSocketManager(`${WS_BASE}/ws/dashboard`, { ski // the user sees their completed chat "type itself out" on every reopen. // // Lifetime: tied to the JS module load, which means the page tab. Lost -// on full app reload (intentional — that should re-hydrate from REST). +// on full app reload (intentional , that should re-hydrate from REST). // On backend restart the buffers are wiped anyway, so a stale // lastSeq pointing past the buffer top falls into the "fresh client" // path on the server (last_seq>0 but no buffer) which short-circuits