Skip to content
This repository was archived by the owner on May 29, 2026. It is now read-only.

Commit da97db9

Browse files
staticoclaude
andcommitted
Add channel config editing: PSK, uplink, downlink
- [e] edit name, [r] cycle role (existing) - [p] edit PSK as base64 (supports "default", empty for unencrypted) - [u] toggle uplink enabled - [D] toggle downlink enabled - Expand saveChannel to support all channel settings 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 2a7ae01 commit da97db9

2 files changed

Lines changed: 79 additions & 10 deletions

File tree

src/ui/App.tsx

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,7 +1186,7 @@ export function App({ address, packetStore, nodeStore, skipConfig = false, skipN
11861186
}
11871187
}, [myNodeNum, transport, configOwner, showNotification, batchEditMode, batchEditCount]);
11881188

1189-
const saveChannel = useCallback(async (channelIndex: number, updates: { name?: string; role?: number }) => {
1189+
const saveChannel = useCallback(async (channelIndex: number, updates: { name?: string; role?: number; psk?: Uint8Array; uplinkEnabled?: boolean; downlinkEnabled?: boolean }) => {
11901190
if (!transport || !myNodeNum) return;
11911191
const channel = configChannels.find(c => c.index === channelIndex);
11921192
if (!channel) return;
@@ -1198,6 +1198,9 @@ export function App({ address, packetStore, nodeStore, skipConfig = false, skipN
11981198
settings: create(Mesh.ChannelSettingsSchema, {
11991199
...channel.settings,
12001200
name: updates.name !== undefined ? updates.name : channel.settings?.name,
1201+
psk: updates.psk !== undefined ? updates.psk : channel.settings?.psk,
1202+
uplinkEnabled: updates.uplinkEnabled !== undefined ? updates.uplinkEnabled : channel.settings?.uplinkEnabled,
1203+
downlinkEnabled: updates.downlinkEnabled !== undefined ? updates.downlinkEnabled : channel.settings?.downlinkEnabled,
12011204
}),
12021205
});
12031206
const binary = adminHelper.createSetChannelRequest(updatedChannel, { myNodeNum });
@@ -2000,11 +2003,35 @@ export function App({ address, packetStore, nodeStore, skipConfig = false, skipN
20002003
setLocalMeshViewUrl(newUrl);
20012004
showNotification(newUrl ? `MeshView URL set to ${newUrl}` : "MeshView URL cleared");
20022005
} else if (configSection === "channels" && configEditing?.startsWith("channel")) {
2003-
// Save channel name - parse channel index from field key like "channel0_name"
2004-
const match = configEditing.match(/^channel(\d+)_name$/);
2005-
if (match) {
2006-
const channelIndex = parseInt(match[1], 10);
2006+
// Save channel settings - parse channel index from field key like "channel0_name" or "channel0_psk"
2007+
const nameMatch = configEditing.match(/^channel(\d+)_name$/);
2008+
const pskMatch = configEditing.match(/^channel(\d+)_psk$/);
2009+
if (nameMatch) {
2010+
const channelIndex = parseInt(nameMatch[1], 10);
20072011
saveChannel(channelIndex, { name: configEditValue });
2012+
} else if (pskMatch) {
2013+
const channelIndex = parseInt(pskMatch[1], 10);
2014+
// Parse base64 PSK
2015+
try {
2016+
const trimmed = configEditValue.trim();
2017+
if (trimmed === "" || trimmed === "0") {
2018+
// Empty or "0" means unencrypted
2019+
saveChannel(channelIndex, { psk: new Uint8Array([0]) });
2020+
} else if (trimmed === "1" || trimmed.toLowerCase() === "default") {
2021+
// "1" or "default" means default key
2022+
saveChannel(channelIndex, { psk: new Uint8Array([1]) });
2023+
} else {
2024+
// Try to decode as base64
2025+
const decoded = atob(trimmed);
2026+
const psk = new Uint8Array(decoded.length);
2027+
for (let i = 0; i < decoded.length; i++) {
2028+
psk[i] = decoded.charCodeAt(i);
2029+
}
2030+
saveChannel(channelIndex, { psk });
2031+
}
2032+
} catch {
2033+
showNotification("Invalid base64 key");
2034+
}
20082035
}
20092036
}
20102037
setConfigEditing(null);
@@ -2082,6 +2109,44 @@ export function App({ address, packetStore, nodeStore, skipConfig = false, skipN
20822109
}
20832110
return;
20842111
}
2112+
// 'p' to edit PSK
2113+
if (input === "p") {
2114+
const channel = validChannels[selectedChannelIndex];
2115+
if (channel) {
2116+
setConfigEditing(`channel${channel.index}_psk`);
2117+
// Show current PSK as base64
2118+
const psk = channel.settings?.psk;
2119+
if (psk && psk.length > 0 && !(psk.length === 1 && psk[0] === 0)) {
2120+
const binary = String.fromCharCode(...psk);
2121+
try {
2122+
setConfigEditValue(btoa(binary));
2123+
} catch {
2124+
setConfigEditValue("");
2125+
}
2126+
} else {
2127+
setConfigEditValue("");
2128+
}
2129+
}
2130+
return;
2131+
}
2132+
// 'u' to toggle uplink
2133+
if (input === "u") {
2134+
const channel = validChannels[selectedChannelIndex];
2135+
if (channel) {
2136+
const newValue = !channel.settings?.uplinkEnabled;
2137+
saveChannel(channel.index, { uplinkEnabled: newValue });
2138+
}
2139+
return;
2140+
}
2141+
// 'D' to toggle downlink (capital to avoid conflict with DM shortcut)
2142+
if (input === "D") {
2143+
const channel = validChannels[selectedChannelIndex];
2144+
if (channel) {
2145+
const newValue = !channel.settings?.downlinkEnabled;
2146+
saveChannel(channel.index, { downlinkEnabled: newValue });
2147+
}
2148+
return;
2149+
}
20852150
}
20862151
}
20872152
}

src/ui/components/ConfigPanel.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -800,7 +800,7 @@ function ChannelsConfigView({ channels, selectedIndex = 0, editingField, editVal
800800
<Box flexDirection="column" marginTop={1} borderStyle="single" borderColor={theme.border.normal} borderTop borderBottom={false} borderLeft={false} borderRight={false}>
801801
<Box marginBottom={1}>
802802
<Text color={theme.fg.accent} bold>Channel {selectedChannel.index}</Text>
803-
<Text color={theme.fg.muted}> j/k=navigate e=edit name r=cycle role</Text>
803+
<Text color={theme.fg.muted}> [e]name [r]role [p]psk [u]up [D]down</Text>
804804
</Box>
805805
<EditableConfigRow
806806
label="Name"
@@ -816,10 +816,14 @@ function ChannelsConfigView({ channels, selectedIndex = 0, editingField, editVal
816816
{Channel.Channel_Role[selectedChannel.role]}
817817
</Text>
818818
</Box>
819-
<Box>
820-
<Text color={theme.fg.muted}>{"Encryption".padEnd(24)}</Text>
821-
<Text color={theme.packet.encrypted}>{formatPsk(selectedChannel.settings?.psk)}</Text>
822-
</Box>
819+
<EditableConfigRow
820+
label="Encryption (base64)"
821+
value={formatPsk(selectedChannel.settings?.psk)}
822+
fieldKey={`channel${selectedChannel.index}_psk`}
823+
editingField={editingField}
824+
editValue={editValue}
825+
hint=""
826+
/>
823827
<Box>
824828
<Text color={theme.fg.muted}>{"Uplink Enabled".padEnd(24)}</Text>
825829
<Text color={selectedChannel.settings?.uplinkEnabled ? theme.status.online : theme.fg.muted}>

0 commit comments

Comments
 (0)