diff --git a/apps/rowboat/app/projects/[projectId]/copilot/app.tsx b/apps/rowboat/app/projects/[projectId]/copilot/app.tsx
index ca765952..f690bbf4 100644
--- a/apps/rowboat/app/projects/[projectId]/copilot/app.tsx
+++ b/apps/rowboat/app/projects/[projectId]/copilot/app.tsx
@@ -11,7 +11,7 @@ import { Action as WorkflowDispatch } from "@/app/projects/[projectId]/workflow/
import { Panel } from "@/components/common/panel-common";
import { ComposeBoxCopilot } from "@/components/common/compose-box-copilot";
import { Messages } from "./components/messages";
-import { CopyIcon, CheckIcon, PlusIcon, XIcon, InfoIcon, Sparkles } from "lucide-react";
+import { CopyIcon, CheckIcon, PlusIcon, XIcon, InfoIcon, Sparkles, MessageCircle } from "lucide-react";
import { useCopilot } from "./use-copilot";
import { BillingUpgradeModal } from "@/components/common/billing-upgrade-modal";
import { SHOW_COPILOT_MARQUEE } from "@/app/lib/feature_flags";
@@ -376,6 +376,19 @@ export const Copilot = forwardRef<{ handleUserMessage: (message: string) => void
subtitle="Build your assistant"
rightActions={
+ {/* Draft-only: switch to Playground */}
+ {onTogglePanel && (
+
+ )}
}
- {/* View controls (hidden in live mode) */}
- {!isLive && (
- {(() => {
- // Current visibility booleans
- const showAgents = viewMode !== "two_chat_skipper";
- const showChat = viewMode !== "two_agents_skipper";
- const showSkipper = viewMode !== "two_agents_chat";
-
- // Determine selected radio option
- type RadioKey = 'show-all' | 'hide-agents' | 'hide-chat' | 'hide-skipper';
- let selectedKey: RadioKey = 'show-all';
- if (!(showAgents && showChat && showSkipper)) {
- if (!showAgents) selectedKey = 'hide-agents';
- else if (!showChat) selectedKey = 'hide-chat';
- else if (!showSkipper) selectedKey = 'hide-skipper';
- }
-
- // Map radio selection to viewMode
- const setByKey = (key: RadioKey) => {
- switch (key) {
- case 'show-all':
- onSetViewMode('three_all');
- break;
- case 'hide-agents':
- onSetViewMode('two_chat_skipper');
- break;
- case 'hide-chat':
- onSetViewMode('two_agents_skipper');
- break;
- case 'hide-skipper':
- onSetViewMode('two_agents_chat');
- break;
- }
- };
-
- // Disable rules
- // When there are zero agents, allow only Show All and Hide Chat
- const zeroAgents = !hasAgents;
- const disableShowAll = false; // always allow switching to 3-pane view
- const disableHideAgents = zeroAgents; // cannot hide agents if none exist
- const disableHideChat = false; // allow hide chat even with zero agents (default)
- const disableHideSkipper = zeroAgents; // keep skipper visible when no agents
-
- return (
-
-
-
- {/* 3-pane layout icon */}
-
-
-
-
- {
- const key = Array.from(keys as Set)[0] as RadioKey;
- const zeroAgents = !hasAgents;
- // Allow only permitted options when zero agents
- if (zeroAgents && key !== 'show-all' && key !== 'hide-chat') return;
- if (key === 'hide-chat' && disableHideChat) return;
- setByKey(key);
- }}>
- }>Show All
- }>Hide Agents
- }>Hide Chat
- }>Hide Skipper
-
-
- );
- })()}
-
)}
+ {/* Layout dropdown removed per design */}
{/* Deploy CTA - conditional based on auto-publish mode */}
@@ -376,6 +303,7 @@ export function TopBar({
}
onPress={() => {
onUseAssistantClick();
diff --git a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx
index 476d8366..629db9be 100644
--- a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx
+++ b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx
@@ -1023,24 +1023,17 @@ export function WorkflowEditor({
// View mode state controls top-level layout visibility (not unmounting panes)
type ViewMode = "two_agents_chat" | "two_agents_skipper" | "two_chat_skipper" | "three_all";
const [viewMode, setViewMode] = useState(() => {
- if (typeof window === 'undefined') return "three_all";
+ if (typeof window === 'undefined') return isLive ? "three_all" : "two_agents_skipper";
const fromUrl = new URLSearchParams(window.location.search).get('view');
const valid: ViewMode[] = ["two_agents_chat", "two_agents_skipper", "two_chat_skipper", "three_all"];
if (fromUrl && (valid as string[]).includes(fromUrl)) {
localStorage.setItem('workflow_view_mode', fromUrl);
return fromUrl as ViewMode;
}
-
const storedViewMode = localStorage.getItem('workflow_view_mode') as ViewMode;
- const hasAgents = workflow.agents.length > 0;
-
- // If workflow has agents and stored view mode is "Hide chat" (two_agents_skipper),
- // override to show all panels by default
- if (hasAgents && storedViewMode === 'two_agents_skipper') {
- return "three_all";
- }
-
- return storedViewMode || "three_all";
+ if (storedViewMode) return storedViewMode;
+ // Default: in draft show Agents + Copilot (hide chat). In live keep prior default.
+ return isLive ? "three_all" : "two_agents_skipper";
});
const updateViewMode = useCallback((mode: ViewMode) => {
@@ -1053,6 +1046,12 @@ export function WorkflowEditor({
dispatch({ type: "unselect_agent" });
}
+ // Clear preserved sizes when switching to a different view mode
+ // This ensures fresh ratios are used unless we're toggling between two_agents_chat and two_agents_skipper
+ if (mode !== 'two_agents_chat' && mode !== 'two_agents_skipper') {
+ setPreservedSizes({});
+ }
+
if (typeof window !== 'undefined') {
localStorage.setItem('workflow_view_mode', mode);
const url = new URL(window.location.href);
@@ -1088,6 +1087,7 @@ export function WorkflowEditor({
const saving = useRef(false);
const [showCopySuccess, setShowCopySuccess] = useState(false);
const [activePanel, setActivePanel] = useState<'playground' | 'copilot'>('copilot');
+ const [preservedSizes, setPreservedSizes] = useState<{[key: string]: number}>({});
const [isInitialState, setIsInitialState] = useState(true);
const [showBuildModeBanner, setShowBuildModeBanner] = useState(false);
const [isLeftPanelCollapsed, setIsLeftPanelCollapsed] = useState(false);
@@ -1850,12 +1850,32 @@ export function WorkflowEditor({
setActivePanel(activePanel === 'playground' ? 'copilot' : 'playground');
return;
}
- if (viewMode === 'two_agents_chat') updateViewMode('two_agents_skipper');
- else if (viewMode === 'two_agents_skipper') updateViewMode('two_agents_chat');
+ if (viewMode === 'two_agents_chat') {
+ const agentsPct = getAgentsPanelPercent();
+ // Maintain ratio: agents vs chat -> agents vs skipper
+ setPreservedSizes({ entities: agentsPct, copilot: Math.max(0, 100 - agentsPct) });
+ updateViewMode('two_agents_skipper');
+ } else if (viewMode === 'two_agents_skipper') {
+ const agentsPct = getAgentsPanelPercent();
+ // Maintain ratio: agents vs skipper -> agents vs chat
+ setPreservedSizes({ entities: agentsPct, chat: Math.max(0, 100 - agentsPct) });
+ updateViewMode('two_agents_chat');
+ }
else if (viewMode === 'two_chat_skipper') updateViewMode('two_chat_skipper');
}
}
+ // Helper: percentage width of Agents panel within the visible panel group
+ function getAgentsPanelPercent() {
+ if (typeof window === 'undefined') return getPanelRatios(viewMode).entityList;
+ const entitiesPanel = document.querySelector('[data-panel-id="entities"]');
+ if (!entitiesPanel || !entitiesPanel.parentElement) return getPanelRatios(viewMode).entityList;
+ const rect = (entitiesPanel as HTMLElement).getBoundingClientRect();
+ const parentRect = (entitiesPanel.parentElement as HTMLElement).getBoundingClientRect();
+ if (!parentRect.width) return getPanelRatios(viewMode).entityList;
+ return (rect.width / parentRect.width) * 100;
+ }
+
function handleToggleLeftPanel() {
setIsLeftPanelCollapsed(!isLeftPanelCollapsed);
}
@@ -2031,8 +2051,9 @@ export function WorkflowEditor({
@@ -2089,22 +2110,22 @@ export function WorkflowEditor({
)}
{(viewMode === 'two_agents_chat' || viewMode === 'three_all') && (
-
+
{/* Minimal mount of Chat during SSR hydration */}
)}
{(viewMode === 'three_all') && ()}
{(viewMode === 'two_agents_skipper' || viewMode === 'three_all') && (
-
+
)}
{(viewMode === 'two_chat_skipper') && (
<>
-
+
-
+
>
)}
@@ -2115,8 +2136,9 @@ export function WorkflowEditor({
@@ -2183,7 +2205,7 @@ export function WorkflowEditor({
)}
{/* Playground column - always mounted; hide via viewMode */}
-
+
+