Skip to content

Commit 551af0d

Browse files
committed
feat: add compact Codex refresh action and live status badge
1 parent c61c3b8 commit 551af0d

5 files changed

Lines changed: 129 additions & 10 deletions

File tree

src/App.tsx

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react";
2+
import RefreshCw from "lucide-react/dist/esm/icons/refresh-cw";
23
import "./styles/base.css";
34
import "./styles/ds-tokens.css";
45
import "./styles/ds-modal.css";
@@ -192,6 +193,7 @@ function MainApp() {
192193
const [activeTab, setActiveTab] = useState<
193194
"home" | "projects" | "codex" | "git" | "log"
194195
>("codex");
196+
const [mobileThreadRefreshLoading, setMobileThreadRefreshLoading] = useState(false);
195197
const tabletTab =
196198
activeTab === "projects" || activeTab === "home" ? "codex" : activeTab;
197199
const {
@@ -568,6 +570,37 @@ function MainApp() {
568570
onMessageActivity: handleThreadMessageActivity,
569571
threadSortKey: threadListSortKey,
570572
});
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+
]);
571604
const {
572605
updaterState,
573606
startUpdate,
@@ -1810,6 +1843,11 @@ function MainApp() {
18101843
});
18111844

18121845
useMenuAcceleratorController({ appSettings, onDebug: addDebugEntry });
1846+
const showCompactCodexThreadActions =
1847+
Boolean(activeWorkspace) &&
1848+
isCompact &&
1849+
((isPhone && activeTab === "codex") || (isTablet && tabletTab === "codex"));
1850+
18131851
const {
18141852
sidebarNode,
18151853
messagesNode,
@@ -1950,14 +1988,33 @@ function MainApp() {
19501988
onSaveLaunchScript: launchScriptState.onSaveLaunchScript,
19511989
launchScriptsState,
19521990
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+
</>
19612018
),
19622019
filePanelMode,
19632020
onFilePanelModeChange: setFilePanelMode,
@@ -2304,6 +2361,16 @@ function MainApp() {
23042361
) : null;
23052362

23062363
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;
23072374

23082375
const desktopTopbarLeftNodeWithToggle = !isCompact ? (
23092376
<div className="topbar-leading">
@@ -2354,6 +2421,7 @@ function MainApp() {
23542421
homeNode={homeNode}
23552422
mainHeaderNode={mainHeaderNode}
23562423
desktopTopbarLeftNode={desktopTopbarLeftNodeWithToggle}
2424+
codexTopbarActionsNode={codexTopbarActionsNode}
23572425
tabletNavNode={tabletNavNode}
23582426
tabBarNode={tabBarNode}
23592427
gitDiffPanelNode={gitDiffPanelNode}

src/features/app/components/AppLayout.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ type AppLayoutProps = {
2424
homeNode: ReactNode;
2525
mainHeaderNode: ReactNode;
2626
desktopTopbarLeftNode: ReactNode;
27+
codexTopbarActionsNode?: ReactNode;
2728
tabletNavNode: ReactNode;
2829
tabBarNode: ReactNode;
2930
gitDiffPanelNode: ReactNode;
@@ -62,6 +63,7 @@ export const AppLayout = memo(function AppLayout({
6263
homeNode,
6364
mainHeaderNode,
6465
desktopTopbarLeftNode,
66+
codexTopbarActionsNode,
6567
tabletNavNode,
6668
tabBarNode,
6769
gitDiffPanelNode,
@@ -94,6 +96,7 @@ export const AppLayout = memo(function AppLayout({
9496
compactEmptyGitNode={compactEmptyGitNode}
9597
compactGitBackNode={compactGitBackNode}
9698
topbarLeftNode={mainHeaderNode}
99+
codexTopbarActionsNode={codexTopbarActionsNode}
97100
messagesNode={messagesNode}
98101
composerNode={composerNode}
99102
gitDiffPanelNode={gitDiffPanelNode}
@@ -117,6 +120,7 @@ export const AppLayout = memo(function AppLayout({
117120
tabletTab={tabletTab}
118121
onSidebarResizeStart={onSidebarResizeStart}
119122
topbarLeftNode={mainHeaderNode}
123+
codexTopbarActionsNode={codexTopbarActionsNode}
120124
messagesNode={messagesNode}
121125
composerNode={composerNode}
122126
gitDiffPanelNode={gitDiffPanelNode}

src/features/layout/components/PhoneLayout.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ type PhoneLayoutProps = {
1515
compactEmptyGitNode: ReactNode;
1616
compactGitBackNode: ReactNode;
1717
topbarLeftNode: ReactNode;
18+
codexTopbarActionsNode?: ReactNode;
1819
messagesNode: ReactNode;
1920
composerNode: ReactNode;
2021
gitDiffPanelNode: ReactNode;
@@ -36,6 +37,7 @@ export function PhoneLayout({
3637
compactEmptyGitNode,
3738
compactGitBackNode,
3839
topbarLeftNode,
40+
codexTopbarActionsNode,
3941
messagesNode,
4042
composerNode,
4143
gitDiffPanelNode,
@@ -53,7 +55,11 @@ export function PhoneLayout({
5355
<div className="compact-panel">
5456
{activeWorkspace ? (
5557
<>
56-
<MainTopbar leftNode={topbarLeftNode} className="compact-topbar" />
58+
<MainTopbar
59+
leftNode={topbarLeftNode}
60+
actionsNode={codexTopbarActionsNode}
61+
className="compact-topbar"
62+
/>
5763
<div className="content compact-content">{messagesNode}</div>
5864
{composerNode}
5965
</>

src/features/layout/components/TabletLayout.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type TabletLayoutProps = {
1313
tabletTab: "projects" | "codex" | "git" | "log";
1414
onSidebarResizeStart: (event: MouseEvent<HTMLDivElement>) => void;
1515
topbarLeftNode: ReactNode;
16+
codexTopbarActionsNode?: ReactNode;
1617
messagesNode: ReactNode;
1718
composerNode: ReactNode;
1819
gitDiffPanelNode: ReactNode;
@@ -32,6 +33,7 @@ export function TabletLayout({
3233
tabletTab,
3334
onSidebarResizeStart,
3435
topbarLeftNode,
36+
codexTopbarActionsNode,
3537
messagesNode,
3638
composerNode,
3739
gitDiffPanelNode,
@@ -56,7 +58,11 @@ export function TabletLayout({
5658
{showHome && homeNode}
5759
{showWorkspace && (
5860
<>
59-
<MainTopbar leftNode={topbarLeftNode} className="tablet-topbar" />
61+
<MainTopbar
62+
leftNode={topbarLeftNode}
63+
actionsNode={tabletTab === "codex" ? codexTopbarActionsNode : undefined}
64+
className="tablet-topbar"
65+
/>
6066
{tabletTab === "codex" && (
6167
<>
6268
<div className="content tablet-content">{messagesNode}</div>

src/styles/main.css

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,41 @@
840840
-webkit-app-region: no-drag;
841841
}
842842

843+
.compact-workspace-live-indicator {
844+
display: inline-flex;
845+
align-items: center;
846+
margin-left: 10px;
847+
border-radius: 999px;
848+
border: 1px solid var(--border-subtle);
849+
padding: 3px 8px;
850+
font-size: 11px;
851+
font-weight: 600;
852+
line-height: 1;
853+
white-space: nowrap;
854+
}
855+
856+
.compact-workspace-live-indicator.is-live {
857+
color: var(--status-success);
858+
border-color: color-mix(in srgb, var(--status-success) 38%, transparent);
859+
background: color-mix(in srgb, var(--status-success) 12%, transparent);
860+
}
861+
862+
.compact-workspace-live-indicator.is-disconnected {
863+
color: var(--status-error);
864+
border-color: color-mix(in srgb, var(--status-error) 42%, transparent);
865+
background: color-mix(in srgb, var(--status-error) 12%, transparent);
866+
}
867+
868+
.compact-codex-refresh-icon.spinning {
869+
animation: compact-codex-refresh-spin 0.8s linear infinite;
870+
}
871+
872+
@keyframes compact-codex-refresh-spin {
873+
to {
874+
transform: rotate(360deg);
875+
}
876+
}
877+
843878
.content {
844879
display: flex;
845880
min-height: 0;

0 commit comments

Comments
 (0)