Skip to content

Commit 77d1a09

Browse files
staticoclaude
andcommitted
Refactor nav bar: extract NavBar component with data-driven TABS array
- Extract NavBar as React.memo component to avoid unnecessary re-renders - Create unified TABS config array used by both rendering and key handling - Show number keys in tab labels (e.g. 1:PACKETS | 2:NODES) - Replace bracket styling with dimmed pipe separators - Use dimColor for inactive tabs (more portable than hex colors) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> (cherry picked from commit 137482c)
1 parent 0948497 commit 77d1a09

1 file changed

Lines changed: 50 additions & 42 deletions

File tree

src/ui/App.tsx

Lines changed: 50 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,44 @@ const ROUTING_ERROR_NAMES: Record<number, string> = {
6565

6666
type AppMode = "packets" | "nodes" | "chat" | "dm" | "config" | "log" | "meshview";
6767

68+
interface TabDef {
69+
mode: AppMode;
70+
label: string;
71+
short: string;
72+
meshviewOnly?: boolean;
73+
onEnter?: string;
74+
}
75+
76+
const TABS: TabDef[] = [
77+
{ mode: "packets", label: "PACKETS", short: "P" },
78+
{ mode: "nodes", label: "NODES", short: "N" },
79+
{ mode: "chat", label: "CHAT", short: "C" },
80+
{ mode: "dm", label: "DM", short: "D" },
81+
{ mode: "log", label: "LOG", short: "L" },
82+
{ mode: "meshview", label: "MESHVIEW", short: "M", meshviewOnly: true },
83+
{ mode: "config", label: "CONFIG", short: "CFG", onEnter: "startBatchEdit" },
84+
];
85+
86+
const NavBar = React.memo(function NavBar({ mode, terminalWidth, hasMeshView }: { mode: AppMode; terminalWidth: number; hasMeshView: boolean }) {
87+
const compact = terminalWidth <= 90;
88+
const visibleTabs = TABS.filter(t => !t.meshviewOnly || hasMeshView);
89+
return (
90+
<Text>
91+
{visibleTabs.map((tab, i) => {
92+
const active = mode === tab.mode;
93+
const key = String(i + 1);
94+
const label = compact ? `${key}:${tab.short}` : `${key}:${tab.label}`;
95+
return (
96+
<React.Fragment key={tab.mode}>
97+
{i > 0 && <Text dimColor> | </Text>}
98+
<Text bold={active} {...(active ? { color: theme.fg.accent } : { dimColor: true })}>{label}</Text>
99+
</React.Fragment>
100+
);
101+
})}
102+
</Text>
103+
);
104+
});
105+
68106
export interface ChannelInfo {
69107
index: number;
70108
name: string;
@@ -2060,17 +2098,17 @@ export function App({ address, packetStore, nodeStore, skipConfig = false, skipN
20602098

20612099
// Mode switching (allow only when input not focused)
20622100
if (!isInputFocused) {
2063-
if (input === "1") { setMode("packets"); setChatInputFocused(false); setDmInputFocused(false); return; }
2064-
if (input === "2") { setMode("nodes"); setChatInputFocused(false); setDmInputFocused(false); return; }
2065-
if (input === "3") { setMode("chat"); return; }
2066-
if (input === "4") { setMode("dm"); return; }
2067-
if (input === "5") { setMode("log"); setChatInputFocused(false); setDmInputFocused(false); return; }
2068-
if (input === "6" && localMeshViewUrl) { setMode("meshview"); setChatInputFocused(false); setDmInputFocused(false); return; }
2069-
if (input === (localMeshViewUrl ? "7" : "6")) { setMode("config"); setChatInputFocused(false); setDmInputFocused(false); if (!batchEditMode) startBatchEdit(); return; }
2101+
const visibleTabs = TABS.filter(t => !t.meshviewOnly || localMeshViewUrl);
2102+
const numKey = parseInt(input);
2103+
if (numKey >= 1 && numKey <= visibleTabs.length) {
2104+
const tab = visibleTabs[numKey - 1];
2105+
setMode(tab.mode);
2106+
if (tab.mode !== "chat" && tab.mode !== "dm") { setChatInputFocused(false); setDmInputFocused(false); }
2107+
if (tab.onEnter === "startBatchEdit" && !batchEditMode) startBatchEdit();
2108+
return;
2109+
}
20702110
// Bracket keys for tab switching
2071-
const modes: AppMode[] = localMeshViewUrl
2072-
? ["packets", "nodes", "chat", "dm", "log", "meshview", "config"]
2073-
: ["packets", "nodes", "chat", "dm", "log", "config"];
2111+
const modes: AppMode[] = visibleTabs.map(t => t.mode);
20742112
if (input === "[") {
20752113
const idx = modes.indexOf(mode);
20762114
const newMode = modes[(idx - 1 + modes.length) % modes.length];
@@ -3140,37 +3178,7 @@ export function App({ address, packetStore, nodeStore, skipConfig = false, skipN
31403178

31413179
const selectedPacket = packets[selectedPacketIndex];
31423180

3143-
const getModeLabel = () => {
3144-
const p = mode === "packets";
3145-
const n = mode === "nodes";
3146-
const c = mode === "chat";
3147-
const d = mode === "dm";
3148-
const cfg = mode === "config";
3149-
const l = mode === "log";
3150-
const mv = mode === "meshview";
3151-
const compact = terminalWidth <= 90;
3152-
return (
3153-
<Text>
3154-
<Text color={p ? theme.fg.accent : theme.fg.muted} bold={p}>{compact ? "[P]" : "[PACKETS]"}</Text>
3155-
{" "}
3156-
<Text color={n ? theme.fg.accent : theme.fg.muted} bold={n}>{compact ? "[N]" : "[NODES]"}</Text>
3157-
{" "}
3158-
<Text color={c ? theme.fg.accent : theme.fg.muted} bold={c}>{compact ? "[C]" : "[CHAT]"}</Text>
3159-
{" "}
3160-
<Text color={d ? theme.fg.accent : theme.fg.muted} bold={d}>{compact ? "[D]" : "[DM]"}</Text>
3161-
{" "}
3162-
<Text color={l ? theme.fg.accent : theme.fg.muted} bold={l}>{compact ? "[L]" : "[LOG]"}</Text>
3163-
{localMeshViewUrl && (
3164-
<>
3165-
{" "}
3166-
<Text color={mv ? theme.fg.accent : theme.fg.muted} bold={mv}>{compact ? "[M]" : "[MESHVIEW]"}</Text>
3167-
</>
3168-
)}
3169-
{" "}
3170-
<Text color={cfg ? theme.fg.accent : theme.fg.muted} bold={cfg}>{compact ? "[CFG]" : "[CONFIG]"}</Text>
3171-
</Text>
3172-
);
3173-
};
3181+
const hasMeshView = !!localMeshViewUrl;
31743182

31753183
const statusColor = status === "connected" ? theme.status.online : theme.status.offline;
31763184
const nodeCount = nodes.length;
@@ -3226,7 +3234,7 @@ export function App({ address, packetStore, nodeStore, skipConfig = false, skipN
32263234
{truncatedNodeName} <Text color={theme.fg.muted}>{formatNodeId(myNodeNum)}</Text>
32273235
</Text>
32283236
<Box flexShrink={0}>
3229-
{getModeLabel()}
3237+
<NavBar mode={mode} terminalWidth={terminalWidth} hasMeshView={hasMeshView} />
32303238
</Box>
32313239
</Box>
32323240

0 commit comments

Comments
 (0)