diff --git a/apps/web/app/components/charts/report-card.tsx b/apps/web/app/components/charts/report-card.tsx index 12e4d7a33a0..0b2af7a81dc 100644 --- a/apps/web/app/components/charts/report-card.tsx +++ b/apps/web/app/components/charts/report-card.tsx @@ -76,12 +76,12 @@ type PanelData = { function panelColSpan(size?: string): string { switch (size) { case "full": - return "col-span-6"; + return "col-span-2 sm:col-span-4 lg:col-span-6"; case "third": - return "col-span-2"; + return "col-span-1 sm:col-span-2 lg:col-span-2"; case "half": default: - return "col-span-3"; + return "col-span-2 sm:col-span-2 lg:col-span-3"; } } @@ -298,7 +298,7 @@ export function ReportCard({ config }: ReportCardProps) { transition={{ duration: 0.25, ease: "easeInOut" }} className="overflow-hidden" > -
+
{config.panels.map((panel) => ( -
+
+
{config.panels.map((panel) => ( +
)} {!editing && ( -
- {/* Edit */} -
) : (showHeroState && !mounted) ? ( -
+
) : showHeroState ? ( -
+
{/* Hero greeting */} {greeting && (

{greeting} @@ -2385,21 +2385,21 @@ export const ChatPanel = forwardRef( )} {/* Centered input bar */} -
+
{inputBarContainer(handleInputDragOver, handleInputDragLeave, handleInputDrop)}
{/* Prompt suggestion pills */} -
-
- {visiblePrompts.slice(0, 3).map((template) => { +
+
+ {visiblePrompts.slice(0, compact ? 4 : 3).map((template) => { const Icon = template.icon; return (
-
- {visiblePrompts.slice(3, 7).map((template) => { +
+ {visiblePrompts.slice(compact ? 4 : 3, 7).map((template) => { const Icon = template.icon; return (
-
+
{!job.state.runningAtMs && job.enabled && ( + ); + })} + +
+ {/* Right panel: Data / Query */}
{queryMode ? ( diff --git a/apps/web/app/components/workspace/object-filter-bar.tsx b/apps/web/app/components/workspace/object-filter-bar.tsx index 94b9ec07fa3..f7f8b284716 100644 --- a/apps/web/app/components/workspace/object-filter-bar.tsx +++ b/apps/web/app/components/workspace/object-filter-bar.tsx @@ -146,7 +146,7 @@ function Dropdown({
onOpenChange(!open)}>{trigger}
{open && (
onChange(e.target.value)} placeholder={placeholder ?? "Value..."} - className="px-2 py-1 rounded-md text-xs outline-none min-w-[120px]" + className="px-2 py-1 rounded-md text-xs outline-none min-w-[80px] sm:min-w-[120px]" style={{ background: "var(--color-bg)", border: "1px solid var(--color-border)", @@ -776,7 +776,7 @@ export function ObjectFilterBar({ return ( diff --git a/apps/web/app/components/workspace/object-table.tsx b/apps/web/app/components/workspace/object-table.tsx index 3fbf2a6c993..0d1c5794686 100644 --- a/apps/web/app/components/workspace/object-table.tsx +++ b/apps/web/app/components/workspace/object-table.tsx @@ -934,7 +934,7 @@ function AddEntryModal({ style={{ background: "rgba(0, 0, 0, 0.5)", backdropFilter: "blur(2px)" }} >
("files"); const [chatSidebarOpen, setChatSidebarOpen] = useState(false); + const [mobileChatSessionsOpen, setMobileChatSessionsOpen] = useState(false); + const [mobileFileChatOpen, setMobileFileChatOpen] = useState(false); // Terminal drawer state const [terminalOpen, setTerminalOpen] = useState(false); @@ -586,8 +588,12 @@ function WorkspacePageInner() { return matchingParentTab.id; } } + if (tabState.activeTabId === HOME_TAB_ID) { + const blankTab = mainChatTabs.find((tab) => !tab.sessionId && !tab.sessionKey); + if (blankTab) return blankTab.id; + } return mainChatTabs[0]?.id ?? null; - }, [activeTab, activeSessionId, activeSubagentKey, mainChatTabs]); + }, [activeTab, activeSessionId, activeSubagentKey, mainChatTabs, tabState.activeTabId]); useEffect(() => { if (!isChatTab(activeTab)) { @@ -616,8 +622,14 @@ function WorkspacePageInner() { }, []); const handleChatTabSessionChange = useCallback((tabId: string, sessionId: string | null) => { - setTabState((prev) => bindParentSessionToChatTab(prev, tabId, sessionId)); - if (tabState.activeTabId === tabId || visibleMainChatTabId === tabId) { + setTabState((prev) => { + let next = bindParentSessionToChatTab(prev, tabId, sessionId); + if (sessionId && prev.activeTabId === HOME_TAB_ID) { + next = activateTab(next, tabId); + } + return next; + }); + if (tabState.activeTabId === tabId || tabState.activeTabId === HOME_TAB_ID || visibleMainChatTabId === tabId) { setActiveSessionId(sessionId); setActiveSubagentKey(null); } @@ -1177,7 +1189,18 @@ function WorkspacePageInner() { // Tab handler callbacks (defined after loadContent is available) const handleTabActivate = useCallback((tabId: string) => { if (tabId === HOME_TAB_ID) { - setTabState((prev) => activateTab(prev, tabId)); + setTabState((prev) => { + let next = activateTab(prev, tabId); + const chatTabs = next.tabs.filter((t) => t.id !== HOME_TAB_ID && isChatTab(t)); + const hasBlankChat = chatTabs.some((t) => !t.sessionId && !t.sessionKey); + if (!hasBlankChat) { + const blank = createBlankChatTab(); + next = { tabs: [...next.tabs, blank], activeTabId: HOME_TAB_ID }; + } + return next; + }); + setActiveSessionId(null); + setActiveSubagentKey(null); applyActivatedTab(undefined); return; } @@ -2194,43 +2217,125 @@ function WorkspacePageInner() {
{/* Mobile top bar — always visible on mobile */} {isMobile && ( -
- -
- {activePath ? activePath.split("/").pop() : (context?.organization?.name || "Workspace")} -
-
- {activePath && content.kind !== "none" && ( + +
+ {activePath ? activePath.split("/").pop() : (context?.organization?.name || "Workspace")} +
+
+ {activePath && content.kind !== "none" && ( + + )} + {!showMainChat && fileContext && ( + + )} + {showMainChat && ( + + )} - )} + +
-
+ {/* Mobile tab strip */} + {tabState.tabs.length > 1 && ( +
+ {tabState.tabs.map((tab) => { + const isActive = tab.id === tabState.activeTabId; + const isLive = liveChatTabIds.has(tab.id); + return ( + + ); + })} +
+ )} + )} {/* Tab bar (desktop only, always visible -- home tab is always present) */} @@ -2517,6 +2622,59 @@ function WorkspacePageInner() { )}
+ {/* Mobile chat sessions drawer */} + {isMobile && mobileChatSessionsOpen && ( + { + const session = sessions.find((entry) => entry.id === sessionId); + openSessionChatTab(sessionId, session?.title); + setMobileChatSessionsOpen(false); + }} + onNewSession={() => { + openBlankChatTab(); + setMobileChatSessionsOpen(false); + }} + onSelectSubagent={(key) => { + handleSelectSubagent(key); + setMobileChatSessionsOpen(false); + }} + onDeleteSession={handleDeleteSession} + onRenameSession={handleRenameSession} + onStopSession={(sessionId) => { void stopParentSession(sessionId); }} + onStopSubagent={(sessionKey) => { void stopSubagentSession(sessionKey); }} + mobile + width={280} + onClose={() => setMobileChatSessionsOpen(false)} + /> + )} + + {/* Mobile file-context chat drawer */} + {isMobile && mobileFileChatOpen && fileContext && ( +
setMobileFileChatOpen(false)}> + {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */} +
e.stopPropagation()} className="fixed inset-y-0 right-0 z-50 drawer-right" style={{ width: "min(85vw, 360px)" }}> +
+ { handleFilePathClickFromChat(path); setMobileFileChatOpen(false); }} + onActiveSessionChange={setFileChatSessionId} + /> +
+
+
+ )} + {/* Terminal drawer (Cmd+J) */} {terminalOpen && ( setTerminalOpen(false)} cwd={workspaceRoot ?? undefined} /> diff --git a/package.json b/package.json index e015d18abb9..65efe0e29b9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "denchclaw", - "version": "2.3.11", + "version": "2.3.12", "description": "Fully Managed OpenClaw Framework for managing your CRM, Sales Automation and Outreach agents. The only local productivity tool you need.", "keywords": [], "homepage": "https://github.com/DenchHQ/DenchClaw#readme", diff --git a/packages/dench/package.json b/packages/dench/package.json index 07b216af84f..6ebb357c0ba 100644 --- a/packages/dench/package.json +++ b/packages/dench/package.json @@ -1,6 +1,6 @@ { "name": "dench", - "version": "2.3.11", + "version": "2.3.12", "description": "Shorthand alias for denchclaw — AI-powered CRM platform CLI", "license": "MIT", "repository": { @@ -16,7 +16,7 @@ ], "type": "module", "dependencies": { - "denchclaw": "^2.3.11" + "denchclaw": "^2.3.12" }, "engines": { "node": ">=22.12.0"