Compare commits

...
Author SHA1 Message Date
bincxz 4ca8ff9693 fix(terminal): retain absent local shell display snapshots 2026-10-15 14:17:52 +08:00
5 changed files with 39 additions and 8 deletions

No files matched your search

+14 -1
View File
@@ -4,7 +4,7 @@ import test from "node:test";
import type { Host } from "../../domain/models";
import { prepareSerialConfigForSavedHost } from "../../domain/serialBackspace";
import { buildTelnetDeepLinkConnectionHost } from "../../domain/telnetDeepLink";
import { resolveEffectiveTerminalHost } from "../../domain/terminalHostResolution";
import { resolveEffectiveTerminalHost, resolveTerminalSessionHost } from "../../domain/terminalHostResolution";
import {
createHostTerminalSession,
createSerialTerminalSession,
@@ -171,3 +171,16 @@ test("host session factories snapshot plugin connection configuration", () => {
assert.deepEqual(workspace.pluginConnection, pluginConnection);
assert.notEqual(workspace.pluginConnection, pluginConnection);
});
test("local sessions preserve intentionally absent display metadata when the saved shell changes", () => {
const original = host({ protocol: "local", hostname: "localhost", localShell: "/bin/bash" });
const source = createHostTerminalSession("original-bash", original);
const edited = { ...original, localShell: "/bin/zsh", localShellName: "Zsh", localShellIcon: "zsh" };
const copied = resolveTerminalSessionHost({ session: source, hosts: [edited], groupConfigs: [], proxyProfiles: [], localOs: "macos" });
assert.equal(copied.localShell, "/bin/bash");
assert.equal(copied.localShellName, "");
assert.equal(copied.localShellIcon, "");
const named = createHostTerminalSession("named-zsh", edited);
assert.equal(named.localShellName, "Zsh");
assert.equal(named.localShellIcon, "zsh");
});
+2 -2
View File
@@ -153,8 +153,8 @@ export const createHostTerminalSession = (
// Preserve field presence: an edited host must not contribute its args
// to a session that was launched with an explicit shell and no args.
localShellArgs: host.localShellArgs ?? EMPTY_LOCAL_SHELL_ARGS,
localShellName: host.localShellName,
localShellIcon: host.localShellIcon,
localShellName: host.localShellName ?? "",
localShellIcon: host.localShellIcon ?? "",
// Effective directory from resolveHostDefaultLocalShell, including a
// Settings directory the saved host inherits. Copy Tab overlays this
// snapshot, so a later settings or vault edit cannot move the relaunch.
+1 -1
View File
@@ -86,7 +86,7 @@ const SessionTabIcon: React.FC<{
// Local protocol → shell-specific icon if available, else OS-specific icon
if (protocol === 'local' || host?.protocol === 'local' || (!protocol && !host)) {
// Use shell icon from discovery when available
const iconId = shellIcon || host?.localShellIcon;
const iconId = shellIcon ?? host?.localShellIcon;
if (iconId) {
return (
<img
+18
View File
@@ -875,3 +875,21 @@ test("restore retains an explicit backend-default local shell snapshot", () => {
assert.deepEqual(restored.sessions[0].localShellArgs, []);
assert.equal(restored.sessions[0].localStartDir, "~");
});
test("restore retains absent local display snapshots while legacy fields remain absent", () => {
const payload = buildSessionRestorePayload({
sessions: [
{ ...session("empty-display"), protocol: "local", localShellName: "", localShellIcon: "" },
{ ...session("named-display"), protocol: "local", localShellName: "Bash", localShellIcon: "bash" },
{ ...session("legacy-display"), protocol: "local" },
],
workspaces: [], tabOrder: [], activeTabId: "empty-display", now: 123,
});
const restored = sanitizeSessionRestorePayload(JSON.parse(JSON.stringify(payload)));
assert.equal(restored.sessions[0].localShellName, "");
assert.equal(restored.sessions[0].localShellIcon, "");
assert.equal(restored.sessions[1].localShellName, "Bash");
assert.equal(restored.sessions[1].localShellIcon, "bash");
assert.equal(restored.sessions[2].localShellName, undefined);
assert.equal(restored.sessions[2].localShellIcon, undefined);
});
+4 -4
View File
@@ -154,8 +154,8 @@ const restoreSession = (session: TerminalSession): RestoredTerminalSession => {
...(serialConfig ? { serialConfig } : {}),
...(session.localShell !== undefined ? { localShell: session.localShell } : {}),
...(session.localShellArgs ? { localShellArgs: [...session.localShellArgs] } : {}),
...(session.localShellName ? { localShellName: session.localShellName } : {}),
...(session.localShellIcon ? { localShellIcon: session.localShellIcon } : {}),
...(session.localShellName !== undefined ? { localShellName: session.localShellName } : {}),
...(session.localShellIcon !== undefined ? { localShellIcon: session.localShellIcon } : {}),
...(session.localStartDir ? { localStartDir: session.localStartDir } : {}),
...(session.fontSize !== undefined ? { fontSize: session.fontSize } : {}),
...(session.fontSizeOverride !== undefined ? { fontSizeOverride: session.fontSizeOverride } : {}),
@@ -197,8 +197,8 @@ const restoreSessionFromUnknown = (value: unknown): RestoredTerminalSession | nu
...(serialConfig ? { serialConfig } : {}),
...(readStringAllowEmpty(value, "localShell") !== undefined ? { localShell: readStringAllowEmpty(value, "localShell") } : {}),
...(Array.isArray(value.localShellArgs) ? { localShellArgs: value.localShellArgs.filter((arg): arg is string => typeof arg === "string") } : {}),
...(readString(value, "localShellName") ? { localShellName: readString(value, "localShellName") } : {}),
...(readString(value, "localShellIcon") ? { localShellIcon: readString(value, "localShellIcon") } : {}),
...(readStringAllowEmpty(value, "localShellName") !== undefined ? { localShellName: readStringAllowEmpty(value, "localShellName") } : {}),
...(readStringAllowEmpty(value, "localShellIcon") !== undefined ? { localShellIcon: readStringAllowEmpty(value, "localShellIcon") } : {}),
...(readString(value, "localStartDir") ? { localStartDir: readString(value, "localStartDir") } : {}),
...(readNumber(value, "fontSize") !== undefined ? { fontSize: readNumber(value, "fontSize") } : {}),
...(readBoolean(value, "fontSizeOverride") !== undefined ? { fontSizeOverride: readBoolean(value, "fontSizeOverride") } : {}),