From 9c7704663ba8194d02a04269c84628bdda5dcb7c Mon Sep 17 00:00:00 2001
From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
Date: Sun, 8 Nov 2026 15:05:28 +0800
Subject: [PATCH] fix(settings): contain long names in configuration lists
---
e2e/settingsListLayout.spec.ts | 73 +++++++++++++++++++
.../settings/pages/Workflow.module.css | 16 ++++
src/features/settings/pages/runTargets.tsx | 9 ++-
src/features/settings/pages/ssh.tsx | 8 +-
src/features/settings/pages/tools.tsx | 8 +-
5 files changed, 108 insertions(+), 6 deletions(-)
create mode 100644 e2e/settingsListLayout.spec.ts
diff --git a/e2e/settingsListLayout.spec.ts b/e2e/settingsListLayout.spec.ts
new file mode 100644
index 000000000..9d81f61c6
--- /dev/null
+++ b/e2e/settingsListLayout.spec.ts
@@ -0,0 +1,73 @@
+import { expect, test } from "./fixtures";
+
+for (const kind of ["external", "ssh", "target"] as const) {
+ test(`${kind} settings keep long names and details inside their selection row`, async ({ workbench: page }) => {
+ await page.keyboard.press("Meta+,");
+ await expect(page.getByRole("dialog", { name: "Settings", exact: true })).toBeVisible();
+ await page.evaluate(async (kind) => {
+ const path = "/src/features/settings/model/staging.ts";
+ const { selectPage, stage } = await import(/* @vite-ignore */ path);
+ const remotePath = "/src/platform/remoteSettings.ts";
+ const workflowPath = "/src/platform/workflowSettings.ts";
+ const { DEFAULT_SSH_CONFIG, DEFAULT_RUN_TARGET } = await import(/* @vite-ignore */ remotePath);
+ const { DEFAULT_EXTERNAL_TOOL } = await import(/* @vite-ignore */ workflowPath);
+ const name = "Run integration tests for the current project with the configured environment";
+ const detail = "Shared infrastructure for the staging environment";
+ if (kind === "external") {
+ stage("tools.externalTools", "global", "externalTools", [
+ { ...DEFAULT_EXTERNAL_TOOL, id: "qa", name, group: detail },
+ ]);
+ selectPage("tools.externalTools");
+ } else if (kind === "ssh") {
+ stage("tools.sshConfigurations", "global", "ssh.configs", [
+ { ...DEFAULT_SSH_CONFIG, id: "qa", name, host: detail, user: "qa" },
+ ]);
+ selectPage("tools.sshConfigurations");
+ } else {
+ stage("build.runTargets", "global", "runTargets", [
+ { ...DEFAULT_RUN_TARGET, id: "qa", name, kind: "docker", image: detail },
+ ]);
+ selectPage("build.runTargets");
+ }
+ }, kind);
+ const row = page.getByRole("dialog", { name: "Settings", exact: true }).getByRole("option").first();
+ await expect(row).toBeVisible();
+ for (const width of [1000, 640, 520]) {
+ await page.setViewportSize({ width, height: 760 });
+ const fits = await row.evaluate((row) => {
+ const box = row.getBoundingClientRect();
+ return [...row.querySelectorAll(":scope > span")].every((span) => {
+ const range = document.createRange();
+ range.selectNodeContents(span);
+ return [...range.getClientRects()].every((r) => r.top >= box.top - 1 && r.bottom <= box.bottom + 1);
+ });
+ });
+ expect(fits).toBe(true);
+ }
+ await expect(row.locator(":scope > span").first()).toHaveAttribute(
+ "title",
+ "Run integration tests for the current project with the configured environment",
+ );
+ await page.evaluate(async (kind) => {
+ const path = "/src/features/settings/model/staging.ts";
+ const { stage } = await import(/* @vite-ignore */ path);
+ const [id, key] =
+ kind === "external"
+ ? ["tools.externalTools", "externalTools"]
+ : kind === "ssh"
+ ? ["tools.sshConfigurations", "ssh.configs"]
+ : ["build.runTargets", "runTargets"];
+ stage(id, "global", key, []);
+ }, kind);
+ const empty = page.getByRole("dialog", { name: "Settings", exact: true }).getByRole("listbox");
+ await expect(row).toHaveCount(0);
+ const hintFits = await empty.evaluate((list) => {
+ const hint = list.firstElementChild!;
+ const box = hint.getBoundingClientRect();
+ const range = document.createRange();
+ range.selectNodeContents(hint);
+ return [...range.getClientRects()].every((r) => r.top >= box.top - 1 && r.bottom <= box.bottom + 1);
+ });
+ expect(hintFits).toBe(true);
+ });
+}
diff --git a/src/features/settings/pages/Workflow.module.css b/src/features/settings/pages/Workflow.module.css
index 5f004af6e..2a1f6d684 100644
--- a/src/features/settings/pages/Workflow.module.css
+++ b/src/features/settings/pages/Workflow.module.css
@@ -166,6 +166,21 @@
padding: 0 8px;
cursor: default;
}
+.toolRow > span {
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.toolName {
+ flex: 1;
+}
+.toolRow.empty {
+ height: auto;
+ min-height: var(--h-list-row);
+ padding-block: 4px;
+ overflow-wrap: anywhere;
+}
.toolRow:hover {
background: var(--bg-tree-hover);
}
@@ -174,6 +189,7 @@
background: var(--bg-tree-selected);
}
.toolGroup {
+ max-width: 45%;
color: var(--fg-subtle);
margin-left: auto;
}
diff --git a/src/features/settings/pages/runTargets.tsx b/src/features/settings/pages/runTargets.tsx
index a19603a53..fd1a2c2b6 100644
--- a/src/features/settings/pages/runTargets.tsx
+++ b/src/features/settings/pages/runTargets.tsx
@@ -491,8 +491,13 @@ function RunTargetsEditor() {
className={clsx(styles.toolRow, t.id === selected && styles.toolRowSelected)}
onMouseDown={() => setSelected(t.id)}
>
- {t.name || t.id}
-
+
+ {t.name || t.id}
+
+
{t.kind === "ssh" ? (t.sshConfig ? sshName(t.sshConfig) : "") : containerSummary(t)}
diff --git a/src/features/settings/pages/ssh.tsx b/src/features/settings/pages/ssh.tsx
index 0a5eca0fb..ee2b9f9cb 100644
--- a/src/features/settings/pages/ssh.tsx
+++ b/src/features/settings/pages/ssh.tsx
@@ -339,8 +339,12 @@ function SshConfigsEditor() {
className={clsx(styles.toolRow, c.id === selected && styles.toolRowSelected)}
onMouseDown={() => setSelected(c.id)}
>
- {c.name.trim() || sshLabel(c) || c.id}
- {sshLabel(c)}
+
+ {c.name.trim() || sshLabel(c) || c.id}
+
+
+ {sshLabel(c)}
+
))}
diff --git a/src/features/settings/pages/tools.tsx b/src/features/settings/pages/tools.tsx
index 1b08c0c17..57ee4a765 100644
--- a/src/features/settings/pages/tools.tsx
+++ b/src/features/settings/pages/tools.tsx
@@ -480,8 +480,12 @@ function ExternalToolsEditor() {
checked={t.enabled}
onChange={(on) => setTools(tools.map((x) => (x.id === t.id ? { ...x, enabled: on } : x)))}
/>
- {t.name}
- {t.group}
+
+ {t.name}
+
+
+ {t.group}
+
))}