mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix(ui): tighten Labs settings spacing (#112038)
This commit is contained in:
@@ -23,6 +23,7 @@ Experimental features are preview surfaces behind explicit flags. They need more
|
||||
| Codex harness | `plugins.entries.codex.config.appServer.experimental.sandboxExecServer` | You want native Codex app-server 0.132.0 or newer to target an OpenClaw sandbox-backed exec-server instead of disabling Code Mode | [Codex harness reference](/plugins/codex-harness-reference#sandboxed-native-execution) |
|
||||
| Structured planning tool | `tools.experimental.planTool` | You want the structured `update_plan` tool exposed for multi-step work tracking in compatible runtimes and UIs | [Gateway configuration reference](/gateway/config-tools#toolsexperimental) |
|
||||
| Code Mode | `tools.codeMode.enabled` | You want compact code-orchestrated access to a hidden OpenClaw tool catalog | [Code Mode](/tools/code-mode) |
|
||||
| Swarm | `tools.swarm.enabled` | You want Code Mode scripts to orchestrate bounded groups of sub-agents in parallel | [Swarm](/tools/swarm) |
|
||||
|
||||
## Control UI Labs
|
||||
|
||||
@@ -31,9 +32,9 @@ Control UI switch. Enabling or disabling a lab patches the canonical Gateway
|
||||
config immediately; the page shows a restart hint only when a feature requires
|
||||
one.
|
||||
|
||||
Code Mode is currently the only shipped Labs entry. Swarm is not exposed yet:
|
||||
its config shape has not shipped, so Control UI does not write a speculative
|
||||
key that would invalidate the operator's config.
|
||||
Code Mode and Swarm are the currently shipped Labs entries. Both switches
|
||||
write existing validated config keys and normally take effect for future agent
|
||||
runs without restarting the Gateway.
|
||||
|
||||
## Local model lean mode
|
||||
|
||||
|
||||
@@ -244,7 +244,7 @@ select it to open the owning Approvals page.
|
||||
- Settings navigation starts with Ask OpenClaw, then groups pages by attention: General, Appearance, and Notifications up top; Connections (Connection, Channels, Communications, Devices); Agents & Tools (Agents, AI & Agents, Model Providers, MCP, Automation, Labs); Privacy & Security (Security, Approvals); and System (Infrastructure, Advanced, Debug, Logs, About). General is a slim hub with model defaults, language, and gateway host stats; every other setting lives on exactly one page.
|
||||
- Privacy & Security: curated rows for gateway auth, exec policy, browser enablement, tool profile, device auth, and mobile pairing, above the schema-backed `security`/`approvals` sections.
|
||||
- Approvals includes newest-first, 30-day history for resolved exec, plugin, and system-agent requests. Filter by kind or page through older rows to review the decision, reason, source session, and resolver attribution recorded by the Gateway.
|
||||
- Labs exposes shipped experimental switches. Code Mode is the current entry and saves `tools.codeMode.enabled` immediately; unshipped experiments do not appear or write speculative config keys.
|
||||
- Labs exposes shipped experimental switches. Code Mode and Swarm are the current entries and save `tools.codeMode.enabled` and `tools.swarm.enabled` immediately; unshipped experiments do not appear or write speculative config keys.
|
||||
- Notifications: browser web-push status, subscribe/unsubscribe, and a test send.
|
||||
- Advanced: every config section without a curated home, plus the raw JSON5 editor (previously the General page's Advanced mode).
|
||||
- Model Setup (`/settings/model-setup`) is a subpage of Model Providers, launched from its header.
|
||||
|
||||
@@ -59,14 +59,18 @@ async function mountPage(sourceConfig: Record<string, unknown>): Promise<{
|
||||
return { page, provider, runtimeConfig };
|
||||
}
|
||||
|
||||
function codeModeToggle(page: LabsPageElement) {
|
||||
const toggle = page.querySelector<HTMLElement & { checked: boolean }>("wa-switch");
|
||||
function labToggle(page: LabsPageElement, index: number, label: string) {
|
||||
const toggle = page.querySelectorAll<HTMLElement & { checked: boolean }>("wa-switch").item(index);
|
||||
if (!toggle) {
|
||||
throw new Error("Code Mode toggle not rendered");
|
||||
throw new Error(`${label} toggle not rendered`);
|
||||
}
|
||||
return toggle;
|
||||
}
|
||||
|
||||
function codeModeToggle(page: LabsPageElement) {
|
||||
return labToggle(page, 0, "Code Mode");
|
||||
}
|
||||
|
||||
describe("LabsPage", () => {
|
||||
beforeEach(async () => {
|
||||
await i18n.setLocale("en");
|
||||
@@ -125,19 +129,32 @@ describe("LabsPage", () => {
|
||||
expect(runtimeConfig.refresh).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("writes true at the registered config path when enabling", async () => {
|
||||
const { page, runtimeConfig } = await mountPage({
|
||||
tools: { codeMode: { enabled: false } },
|
||||
});
|
||||
const toggle = codeModeToggle(page);
|
||||
it.each([
|
||||
{
|
||||
label: "Code Mode",
|
||||
index: 0,
|
||||
sourceConfig: { tools: { codeMode: { enabled: false } } },
|
||||
expectedPatch: { tools: { codeMode: { enabled: true } } },
|
||||
note: "labs: update codeMode",
|
||||
},
|
||||
{
|
||||
label: "Swarm",
|
||||
index: 1,
|
||||
sourceConfig: { tools: { swarm: { enabled: false } } },
|
||||
expectedPatch: { tools: { swarm: { enabled: true } } },
|
||||
note: "labs: update swarm",
|
||||
},
|
||||
])("writes true at the registered config path when enabling $label", async (testCase) => {
|
||||
const { page, runtimeConfig } = await mountPage(testCase.sourceConfig);
|
||||
const toggle = labToggle(page, testCase.index, testCase.label);
|
||||
|
||||
toggle.checked = true;
|
||||
toggle.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
|
||||
|
||||
await vi.waitFor(() => expect(runtimeConfig.patch).toHaveBeenCalledOnce());
|
||||
expect(runtimeConfig.patch).toHaveBeenCalledWith({
|
||||
raw: { tools: { codeMode: { enabled: true } } },
|
||||
note: "labs: update codeMode",
|
||||
raw: testCase.expectedPatch,
|
||||
note: testCase.note,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -76,12 +76,23 @@
|
||||
gap: var(--space-8);
|
||||
}
|
||||
|
||||
/* An intro belongs to the first section rather than forming a separate block.
|
||||
Keep later sections at the normal --space-8 separation. */
|
||||
.settings-page:has(> .settings-page__intro) {
|
||||
gap: var(--space-5);
|
||||
}
|
||||
|
||||
.settings-page:has(> .settings-page__intro) > .settings-section + .settings-section {
|
||||
margin-top: calc(var(--space-8) - var(--space-5));
|
||||
}
|
||||
|
||||
/* Table/list-heavy pages (sessions, automations, plugins) need more width. */
|
||||
.settings-page--wide {
|
||||
max-width: 1120px;
|
||||
}
|
||||
|
||||
.settings-page__intro {
|
||||
margin: 0;
|
||||
font-size: var(--control-ui-text-sm);
|
||||
line-height: 1.5;
|
||||
color: var(--muted);
|
||||
|
||||
Reference in New Issue
Block a user