diff --git a/packages/web/src/app/globals.css b/packages/web/src/app/globals.css index 848737729..b5f8a592b 100644 --- a/packages/web/src/app/globals.css +++ b/packages/web/src/app/globals.css @@ -1311,10 +1311,75 @@ html.light .xterm .xterm-viewport:hover::-webkit-scrollbar-thumb:active { animation: ready-dot-pulse 2.8s ease-in-out infinite; } -/* ── Quick-reply section (respond cards, mobile only) ────────────────── */ +/* ── Quick-reply section (respond cards) ─────────────────────────────── */ .quick-reply { - display: none; + display: flex; + flex-direction: column; + gap: 8px; + padding: 10px 12px; + border-top: 1px solid var(--color-border-default); +} + +.quick-reply__summary { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + font-size: 12px; + line-height: 1.5; + color: var(--color-text-secondary); + margin: 0; +} + +.quick-reply__presets { + display: flex; + gap: 6px; +} + +.quick-reply__preset-btn { + min-height: 44px; + padding: 0 14px; + border: 1px solid var(--color-border-default); + background: var(--color-bg-subtle); + color: var(--color-text-secondary); + font-family: var(--font-sans); + font-size: 12px; + cursor: pointer; + transition: border-color 0.12s ease, color 0.12s ease, background 0.12s ease; +} + +.quick-reply__preset-btn:hover { + border-color: var(--color-accent); + color: var(--color-accent); + background: var(--color-tint-blue); +} + +.quick-reply__input { + min-height: 44px; + height: 44px; + padding: 12px; + border: 1px solid var(--color-border-default); + background: var(--color-bg-subtle); + color: var(--color-text-primary); + font-family: var(--font-sans); + font-size: 13px; + resize: none; + overflow: hidden; + transition: height 0.15s ease, border-color 0.12s ease; + box-sizing: border-box; + width: 100%; +} + +.quick-reply__input::placeholder { + color: var(--color-text-muted); +} + +.quick-reply__input:focus { + outline: none; + border-color: var(--color-accent); + height: 80px; + overflow: auto; } /* ── Done cards — compact, de-emphasized ─────────────────────────────── */ @@ -2848,76 +2913,6 @@ html.light .xterm .xterm-viewport:hover::-webkit-scrollbar-thumb:active { overflow: hidden; } - /* -- Quick-reply section on respond cards -- */ - .quick-reply { - display: flex; - flex-direction: column; - gap: 8px; - padding: 10px 12px; - border-top: 1px solid var(--color-border-default); - } - - .quick-reply__summary { - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 2; - overflow: hidden; - font-size: 12px; - line-height: 1.5; - color: var(--color-text-secondary); - margin: 0; - } - - .quick-reply__presets { - display: flex; - gap: 6px; - } - - .quick-reply__preset-btn { - min-height: 44px; - padding: 0 14px; - border: 1px solid var(--color-border-default); - background: var(--color-bg-subtle); - color: var(--color-text-secondary); - font-family: var(--font-sans); - font-size: 12px; - cursor: pointer; - transition: border-color 0.12s ease, color 0.12s ease, background 0.12s ease; - } - - .quick-reply__preset-btn:hover { - border-color: var(--color-accent); - color: var(--color-accent); - background: var(--color-tint-blue); - } - - .quick-reply__input { - min-height: 44px; - height: 44px; - padding: 12px; - border: 1px solid var(--color-border-default); - background: var(--color-bg-subtle); - color: var(--color-text-primary); - font-family: var(--font-sans); - font-size: 13px; - resize: none; - overflow: hidden; - transition: height 0.15s ease, border-color 0.12s ease; - box-sizing: border-box; - width: 100%; - } - - .quick-reply__input::placeholder { - color: var(--color-text-muted); - } - - .quick-reply__input:focus { - outline: none; - border-color: var(--color-accent); - height: 80px; - overflow: auto; - } - .mobile-pr-list { display: flex; flex-direction: column; diff --git a/packages/web/src/app/sessions/[id]/page.tsx b/packages/web/src/app/sessions/[id]/page.tsx index e75e80015..c1449086a 100644 --- a/packages/web/src/app/sessions/[id]/page.tsx +++ b/packages/web/src/app/sessions/[id]/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { useEffect, useState, useCallback } from "react"; +import { useEffect, useState, useCallback, useRef } from "react"; import { useParams } from "next/navigation"; import { isOrchestratorSession } from "@composio/ao-core/types"; import { SessionDetail } from "@/components/SessionDetail"; @@ -58,6 +58,7 @@ export default function SessionPage() { const [error, setError] = useState(null); const sessionProjectId = session?.projectId ?? null; const sessionIsOrchestrator = session ? isOrchestratorSession(session) : false; + const hasResolvedProjectSessionsRef = useRef(false); // Update document title based on session data useEffect(() => { @@ -91,7 +92,7 @@ export default function SessionPage() { const fetchProjectSessions = useCallback(async () => { if (!sessionProjectId) return; - if (!sessionIsOrchestrator && projectOrchestratorId !== undefined) return; + if (!sessionIsOrchestrator && hasResolvedProjectSessionsRef.current) return; try { const res = await fetch(`/api/sessions?project=${encodeURIComponent(sessionProjectId)}`); if (!res.ok) return; @@ -101,7 +102,8 @@ export default function SessionPage() { body.orchestratorId ?? body.orchestrators?.find((orchestrator) => orchestrator.projectId === sessionProjectId)?.id ?? null; - setProjectOrchestratorId(orchestratorId); + hasResolvedProjectSessionsRef.current = true; + setProjectOrchestratorId((current) => (current === orchestratorId ? current : orchestratorId)); if (!sessionIsOrchestrator) return; @@ -122,7 +124,15 @@ export default function SessionPage() { } catch { // non-critical - status strip just won't show } - }, [projectOrchestratorId, sessionIsOrchestrator, sessionProjectId]); + }, [sessionIsOrchestrator, sessionProjectId]); + + useEffect(() => { + hasResolvedProjectSessionsRef.current = false; + setProjectOrchestratorId(undefined); + if (!sessionIsOrchestrator) { + setZoneCounts(null); + } + }, [sessionIsOrchestrator, sessionProjectId]); // Initial fetch — session first, zone counts after (avoids blocking on slow /api/sessions) useEffect(() => {