Skip to content

Commit 7dc1823

Browse files
StiensWoutcodex
andauthored
[codex] fix: show nightly badge from primary web server version (#3103)
Co-authored-by: codex <codex@users.noreply.github.com>
1 parent 13917df commit 7dc1823

3 files changed

Lines changed: 59 additions & 2 deletions

File tree

apps/web/src/components/Sidebar.logic.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
resolveProjectStatusIndicator,
1515
resolveSidebarNewThreadSeedContext,
1616
resolveSidebarNewThreadEnvMode,
17+
resolveSidebarStageBadgeLabel,
1718
resolveThreadRowClassName,
1819
resolveThreadStatusPill,
1920
shouldClearThreadSelectionOnMouseDown,
@@ -36,6 +37,44 @@ import {
3637

3738
const localEnvironmentId = EnvironmentId.make("environment-local");
3839

40+
describe("resolveSidebarStageBadgeLabel", () => {
41+
it("returns Nightly for nightly primary server versions", () => {
42+
expect(
43+
resolveSidebarStageBadgeLabel({
44+
primaryServerVersion: "0.0.28-nightly.20260616.12",
45+
fallbackStageLabel: "Alpha",
46+
}),
47+
).toBe("Nightly");
48+
});
49+
50+
it("returns the fallback label for stable primary server versions", () => {
51+
expect(
52+
resolveSidebarStageBadgeLabel({
53+
primaryServerVersion: "0.0.27",
54+
fallbackStageLabel: "Alpha",
55+
}),
56+
).toBe("Alpha");
57+
});
58+
59+
it("returns the fallback label when the primary server version is missing", () => {
60+
expect(
61+
resolveSidebarStageBadgeLabel({
62+
primaryServerVersion: null,
63+
fallbackStageLabel: "Dev",
64+
}),
65+
).toBe("Dev");
66+
});
67+
68+
it("returns the fallback label for malformed nightly prerelease versions", () => {
69+
expect(
70+
resolveSidebarStageBadgeLabel({
71+
primaryServerVersion: "0.0.28-nightly.20260616",
72+
fallbackStageLabel: "Alpha",
73+
}),
74+
).toBe("Alpha");
75+
});
76+
});
77+
3978
function makeLatestTurn(overrides?: {
4079
completedAt?: string | null;
4180
startedAt?: string | null;

apps/web/src/components/Sidebar.logic.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { isLatestTurnSettled } from "../session-logic";
1212

1313
export const THREAD_SELECTION_SAFE_SELECTOR = "[data-thread-item], [data-thread-selection-safe]";
1414
export const THREAD_JUMP_HINT_SHOW_DELAY_MS = 100;
15+
const NIGHTLY_SERVER_VERSION_PATTERN = /-nightly\.\d{8}\.\d+$/;
1516
// Visible sidebar rows are prewarmed into the thread-detail cache so opening a
1617
// nearby thread usually reuses an already-hot subscription.
1718
export const SIDEBAR_THREAD_PREWARM_LIMIT = 10;
@@ -64,6 +65,16 @@ export interface ThreadJumpHintVisibilityController {
6465
dispose: () => void;
6566
}
6667

68+
export function resolveSidebarStageBadgeLabel(input: {
69+
primaryServerVersion: string | null | undefined;
70+
fallbackStageLabel: string;
71+
}): string {
72+
return input.primaryServerVersion &&
73+
NIGHTLY_SERVER_VERSION_PATTERN.test(input.primaryServerVersion)
74+
? "Nightly"
75+
: input.fallbackStageLabel;
76+
}
77+
6778
export function createThreadJumpHintVisibilityController(input: {
6879
delayMs: number;
6980
onVisibilityChange: (visible: boolean) => void;

apps/web/src/components/Sidebar.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ import {
188188
orderItemsByPreferredIds,
189189
shouldClearThreadSelectionOnMouseDown,
190190
sortProjectsForSidebar,
191+
resolveSidebarStageBadgeLabel,
191192
useThreadJumpHintVisibility,
192193
ThreadStatusPill,
193194
} from "./Sidebar.logic";
@@ -197,7 +198,7 @@ import { useCopyToClipboard } from "~/hooks/useCopyToClipboard";
197198
import { useIsMobile } from "~/hooks/useMediaQuery";
198199
import { CommandDialogTrigger } from "./ui/command";
199200
import { useSettings, useUpdateSettings } from "~/hooks/useSettings";
200-
import { primaryServerKeybindingsAtom } from "../state/server";
201+
import { primaryServerConfigAtom, primaryServerKeybindingsAtom } from "../state/server";
201202
import {
202203
derivePhysicalProjectKey,
203204
deriveProjectGroupingOverrideKey,
@@ -2680,6 +2681,12 @@ const SidebarChromeHeader = memo(function SidebarChromeHeader({
26802681
}: {
26812682
isElectron: boolean;
26822683
}) {
2684+
const primaryServerVersion =
2685+
useAtomValue(primaryServerConfigAtom)?.environment.serverVersion ?? null;
2686+
const stageBadgeLabel = resolveSidebarStageBadgeLabel({
2687+
primaryServerVersion,
2688+
fallbackStageLabel: APP_STAGE_LABEL,
2689+
});
26832690
const wordmark = (
26842691
<div className="flex items-center gap-2">
26852692
<SidebarTrigger className="shrink-0 md:hidden" />
@@ -2696,7 +2703,7 @@ const SidebarChromeHeader = memo(function SidebarChromeHeader({
26962703
Code
26972704
</span>
26982705
<span className="rounded-full bg-muted/50 px-1.5 py-0.5 text-[8px] font-medium uppercase tracking-[0.18em] text-muted-foreground/60">
2699-
{APP_STAGE_LABEL}
2706+
{stageBadgeLabel}
27002707
</span>
27012708
</Link>
27022709
}

0 commit comments

Comments
 (0)