|
1 | 1 | import { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react"; |
| 2 | +import RefreshCw from "lucide-react/dist/esm/icons/refresh-cw"; |
2 | 3 | import "./styles/base.css"; |
3 | 4 | import "./styles/ds-tokens.css"; |
4 | 5 | import "./styles/ds-modal.css"; |
@@ -192,6 +193,7 @@ function MainApp() { |
192 | 193 | const [activeTab, setActiveTab] = useState< |
193 | 194 | "home" | "projects" | "codex" | "git" | "log" |
194 | 195 | >("codex"); |
| 196 | + const [mobileThreadRefreshLoading, setMobileThreadRefreshLoading] = useState(false); |
195 | 197 | const tabletTab = |
196 | 198 | activeTab === "projects" || activeTab === "home" ? "codex" : activeTab; |
197 | 199 | const { |
@@ -568,6 +570,37 @@ function MainApp() { |
568 | 570 | onMessageActivity: handleThreadMessageActivity, |
569 | 571 | threadSortKey: threadListSortKey, |
570 | 572 | }); |
| 573 | + |
| 574 | + const handleMobileThreadRefresh = useCallback(() => { |
| 575 | + if (mobileThreadRefreshLoading || !activeWorkspace) { |
| 576 | + return; |
| 577 | + } |
| 578 | + setMobileThreadRefreshLoading(true); |
| 579 | + void (async () => { |
| 580 | + let threadId = activeThreadId; |
| 581 | + if (!threadId) { |
| 582 | + threadId = await startThreadForWorkspace(activeWorkspace.id, { |
| 583 | + activate: true, |
| 584 | + }); |
| 585 | + } |
| 586 | + if (!threadId) { |
| 587 | + return; |
| 588 | + } |
| 589 | + await refreshThread(activeWorkspace.id, threadId); |
| 590 | + })() |
| 591 | + .catch(() => { |
| 592 | + // Errors are surfaced through debug entries/toasts in existing thread actions. |
| 593 | + }) |
| 594 | + .finally(() => { |
| 595 | + setMobileThreadRefreshLoading(false); |
| 596 | + }); |
| 597 | + }, [ |
| 598 | + activeThreadId, |
| 599 | + activeWorkspace, |
| 600 | + mobileThreadRefreshLoading, |
| 601 | + refreshThread, |
| 602 | + startThreadForWorkspace, |
| 603 | + ]); |
571 | 604 | const { |
572 | 605 | updaterState, |
573 | 606 | startUpdate, |
@@ -1810,6 +1843,11 @@ function MainApp() { |
1810 | 1843 | }); |
1811 | 1844 |
|
1812 | 1845 | useMenuAcceleratorController({ appSettings, onDebug: addDebugEntry }); |
| 1846 | + const showCompactCodexThreadActions = |
| 1847 | + Boolean(activeWorkspace) && |
| 1848 | + isCompact && |
| 1849 | + ((isPhone && activeTab === "codex") || (isTablet && tabletTab === "codex")); |
| 1850 | + |
1813 | 1851 | const { |
1814 | 1852 | sidebarNode, |
1815 | 1853 | messagesNode, |
@@ -1950,14 +1988,33 @@ function MainApp() { |
1950 | 1988 | onSaveLaunchScript: launchScriptState.onSaveLaunchScript, |
1951 | 1989 | launchScriptsState, |
1952 | 1990 | mainHeaderActionsNode: ( |
1953 | | - <MainHeaderActions |
1954 | | - centerMode={centerMode} |
1955 | | - gitDiffViewStyle={gitDiffViewStyle} |
1956 | | - onSelectDiffViewStyle={setGitDiffViewStyle} |
1957 | | - isCompact={isCompact} |
1958 | | - rightPanelCollapsed={rightPanelCollapsed} |
1959 | | - sidebarToggleProps={sidebarToggleProps} |
1960 | | - /> |
| 1991 | + <> |
| 1992 | + {showCompactCodexThreadActions ? ( |
| 1993 | + <button |
| 1994 | + type="button" |
| 1995 | + className="ghost main-header-action" |
| 1996 | + onClick={handleMobileThreadRefresh} |
| 1997 | + data-tauri-drag-region="false" |
| 1998 | + aria-label="Refresh current thread from server" |
| 1999 | + title="Refresh current thread from server" |
| 2000 | + disabled={mobileThreadRefreshLoading} |
| 2001 | + > |
| 2002 | + <RefreshCw |
| 2003 | + className={`compact-codex-refresh-icon${mobileThreadRefreshLoading ? " spinning" : ""}`} |
| 2004 | + size={14} |
| 2005 | + aria-hidden |
| 2006 | + /> |
| 2007 | + </button> |
| 2008 | + ) : null} |
| 2009 | + <MainHeaderActions |
| 2010 | + centerMode={centerMode} |
| 2011 | + gitDiffViewStyle={gitDiffViewStyle} |
| 2012 | + onSelectDiffViewStyle={setGitDiffViewStyle} |
| 2013 | + isCompact={isCompact} |
| 2014 | + rightPanelCollapsed={rightPanelCollapsed} |
| 2015 | + sidebarToggleProps={sidebarToggleProps} |
| 2016 | + /> |
| 2017 | + </> |
1961 | 2018 | ), |
1962 | 2019 | filePanelMode, |
1963 | 2020 | onFilePanelModeChange: setFilePanelMode, |
@@ -2304,6 +2361,16 @@ function MainApp() { |
2304 | 2361 | ) : null; |
2305 | 2362 |
|
2306 | 2363 | const mainMessagesNode = showWorkspaceHome ? workspaceHomeNode : messagesNode; |
| 2364 | + const codexTopbarActionsNode = showCompactCodexThreadActions ? ( |
| 2365 | + <span |
| 2366 | + className={`compact-workspace-live-indicator ${ |
| 2367 | + activeWorkspace?.connected ? "is-live" : "is-disconnected" |
| 2368 | + }`} |
| 2369 | + title={activeWorkspace?.connected ? "Connected to backend" : "Disconnected from backend"} |
| 2370 | + > |
| 2371 | + {activeWorkspace?.connected ? "Live" : "Disconnected"} |
| 2372 | + </span> |
| 2373 | + ) : null; |
2307 | 2374 |
|
2308 | 2375 | const desktopTopbarLeftNodeWithToggle = !isCompact ? ( |
2309 | 2376 | <div className="topbar-leading"> |
@@ -2354,6 +2421,7 @@ function MainApp() { |
2354 | 2421 | homeNode={homeNode} |
2355 | 2422 | mainHeaderNode={mainHeaderNode} |
2356 | 2423 | desktopTopbarLeftNode={desktopTopbarLeftNodeWithToggle} |
| 2424 | + codexTopbarActionsNode={codexTopbarActionsNode} |
2357 | 2425 | tabletNavNode={tabletNavNode} |
2358 | 2426 | tabBarNode={tabBarNode} |
2359 | 2427 | gitDiffPanelNode={gitDiffPanelNode} |
|
0 commit comments