Skip to content

Commit 7e16a50

Browse files
committed
fix: simplify chat session search
1 parent 0556958 commit 7e16a50

10 files changed

Lines changed: 27 additions & 116 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Docs: https://docs.openclaw.ai
4848
- Diffs: continue hydrating later diff cards when one card fails so a single broken card no longer blanks the whole diff viewer. (#84775) Thanks @cosmopolitan033.
4949
- Mac app: use the native settings sidebar window chrome so the sidebar toggle stays on the left and content no longer clips under oversized titlebar padding.
5050
- Gateway/agents: preserve fresh session overrides and metadata when stale cached agent-session entries race with store updates, so subagent model/provider overrides and routing policy survive concurrent writes. (#19328) Thanks @CodeReclaimers.
51-
- Control UI/chat: keep chat session search inline with the session selector so the header no longer shows a duplicate standalone search row.
51+
- Control UI/chat: keep chat session search always inline with the session selector, remove the extra search/clear icon buttons, and keep picker chevrons/checkmarks visible in dark mode.
5252
- Codex app-server: restart the native app-server and retry once when server-side compaction times out, so preflight compaction stalls recover instead of failing every dispatch. (#85500)
5353
- Restore Control UI gateway token pairing [AI]. (#85459) Thanks @pgondhi987.
5454
- CLI/update: repair managed npm plugin `openclaw` peer links during post-core convergence and reject stale or wrong-target peer links before restart. (#83794) Thanks @fuller-stack-dev.

ui/src/styles/chat/layout.css

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1450,19 +1450,13 @@
14501450
.chat-controls__session-trigger-icon svg {
14511451
width: 16px;
14521452
height: 16px;
1453-
}
1454-
1455-
.chat-controls__session-actions {
1456-
display: flex;
1457-
align-items: center;
1458-
gap: 6px;
1459-
width: auto;
1460-
min-width: 0;
1453+
stroke: currentColor;
1454+
fill: none;
1455+
stroke-width: 1.5px;
14611456
}
14621457

14631458
.chat-controls__session-search {
14641459
position: relative;
1465-
flex: 1 1 220px;
14661460
min-width: 160px;
14671461
max-width: none;
14681462
}
@@ -1501,8 +1495,8 @@
15011495
stroke-linejoin: round;
15021496
}
15031497

1504-
.chat-controls__session-search-toggle {
1505-
flex: 0 0 auto;
1498+
.chat-controls__session-actions {
1499+
min-width: 0;
15061500
}
15071501

15081502
.chat-session-picker {
@@ -1523,20 +1517,6 @@
15231517
overflow: hidden;
15241518
}
15251519

1526-
.chat-session-picker .chat-session-picker__icon-button.btn--icon {
1527-
flex: 0 0 36px;
1528-
width: 36px;
1529-
min-width: 36px;
1530-
height: 36px;
1531-
padding: 0;
1532-
border-radius: var(--radius-md);
1533-
}
1534-
1535-
.chat-session-picker .chat-session-picker__icon-button.btn--icon svg {
1536-
width: 16px;
1537-
height: 16px;
1538-
}
1539-
15401520
.chat-session-picker__list {
15411521
display: flex;
15421522
flex-direction: column;
@@ -1608,6 +1588,9 @@
16081588
.chat-session-picker__option-check svg {
16091589
width: 16px;
16101590
height: 16px;
1591+
stroke: currentColor;
1592+
fill: none;
1593+
stroke-width: 1.5px;
16111594
}
16121595

16131596
.chat-session-picker__status {

ui/src/styles/chat/layout.test.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,15 @@ describe("chat layout styles", () => {
5858
expect(css).toContain("height: 22px;");
5959
});
6060

61-
it("keeps chat session picker search icon buttons fixed size", () => {
61+
it("keeps chat session picker glyphs color-aware", () => {
6262
const css = readLayoutCss();
6363

64-
expect(css).toContain(".chat-session-picker .chat-session-picker__icon-button.btn--icon {");
65-
expect(css).toContain("flex: 0 0 36px;");
66-
expect(css).toContain("width: 36px;");
67-
expect(css).toContain("min-width: 36px;");
64+
expect(css).toMatch(
65+
/\.chat-controls__session-trigger-icon svg \{[\s\S]*stroke: currentColor;[\s\S]*fill: none;/,
66+
);
67+
expect(css).toMatch(
68+
/\.chat-session-picker__option-check svg \{[\s\S]*stroke: currentColor;[\s\S]*fill: none;/,
69+
);
6870
});
6971

7072
it("keeps composer controls labeled and large enough without shrinking mobile taps", () => {

ui/src/styles/layout.mobile.css

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -546,17 +546,6 @@
546546
padding-left: 42px;
547547
}
548548

549-
.chat-mobile-controls-wrapper
550-
.chat-controls-dropdown
551-
.chat-session-picker
552-
.chat-session-picker__icon-button.btn--icon {
553-
flex: 0 0 44px;
554-
width: 44px;
555-
min-width: 44px;
556-
height: 44px;
557-
padding: 0;
558-
}
559-
560549
.chat-mobile-controls-wrapper .chat-controls-dropdown .chat-controls__model {
561550
grid-area: model;
562551
}

ui/src/styles/layout.mobile.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@ describe("chat header responsive mobile styles", () => {
4747
expect(css).toContain("height: 44px;");
4848
});
4949

50-
it("keeps chat session picker search icons from stretching in mobile controls", () => {
50+
it("keeps chat session picker search aligned in mobile controls", () => {
5151
const css = readMobileCss();
5252

53-
expect(css).toContain(".chat-session-picker__icon-button.btn--icon {");
54-
expect(css).toContain("flex: 0 0 44px;");
55-
expect(css).toContain("width: 44px;");
56-
expect(css).toContain("min-width: 44px;");
53+
expect(css).toContain(".chat-controls__session-row--session-search-open");
54+
expect(css).toContain("grid-template-columns: minmax(96px, 0.7fr) minmax(0, 1fr);");
55+
expect(css).toContain(".chat-controls__session-search input");
56+
expect(css).toContain("padding-left: 42px;");
5757
});
5858
});
5959

ui/src/ui/app-view-state.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ export type AppViewState = {
115115
chatModelCatalog: ModelCatalogEntry[];
116116
sessionSwitchNotice: { id: number; text: string } | null;
117117
sessionSwitchFlashKey: string | null;
118-
chatSessionSearchOpen: boolean;
119118
chatSessionPickerOpen: boolean;
120119
chatSessionPickerSurface: "desktop" | "mobile" | null;
121120
chatSessionPickerQuery: string;

ui/src/ui/app.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,6 @@ export class OpenClawApp extends LitElement {
234234
@state() chatModelCatalog: ModelCatalogEntry[] = [];
235235
@state() sessionSwitchNotice: { id: number; text: string } | null = null;
236236
@state() sessionSwitchFlashKey: string | null = null;
237-
@state() chatSessionSearchOpen = false;
238237
@state() chatSessionPickerOpen = false;
239238
@state() chatSessionPickerSurface: "desktop" | "mobile" | null = null;
240239
@state() chatSessionPickerQuery = "";

ui/src/ui/chat/session-controls.ts

Lines changed: 3 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,13 @@ export function renderChatSessionSelect(
5454
const surface = options.surface ?? "desktop";
5555
const selectedSessionLabel = resolveSelectedChatSessionLabel(state, sessionGroups);
5656
const pickerOpen = state.chatSessionPickerOpen && state.chatSessionPickerSurface === surface;
57-
const sessionSearchVisible =
58-
state.chatSessionSearchOpen ||
59-
(state.chatSessionPickerQuery ?? "").trim() !== "" ||
60-
(state.chatSessionPickerAppliedQuery ?? "").trim() !== "";
6157
const flashSession = state.sessionSwitchFlashKey === state.sessionKey;
6258
const rowClass = [
6359
"chat-controls__session-row",
6460
hasAgentSelect ? "" : "chat-controls__session-row--single-agent",
6561
quotaPill ? "chat-controls__session-row--has-quota" : "",
6662
flashSession ? "chat-controls__session-row--flash" : "",
67-
sessionSearchVisible ? "chat-controls__session-row--session-search-open" : "",
63+
"chat-controls__session-row--session-search-open",
6864
]
6965
.filter(Boolean)
7066
.join(" ");
@@ -140,11 +136,6 @@ function closeChatSessionPicker(state: AppViewState) {
140136
requestHostUpdate(state);
141137
}
142138

143-
function openChatSessionSearch(state: AppViewState, surface: ChatSessionSelectSurface) {
144-
state.chatSessionSearchOpen = true;
145-
openChatSessionPicker(state, surface);
146-
}
147-
148139
function toggleChatSessionPicker(state: AppViewState, surface: ChatSessionSelectSurface) {
149140
if (state.chatSessionPickerOpen && state.chatSessionPickerSurface === surface) {
150141
closeChatSessionPicker(state);
@@ -258,7 +249,6 @@ async function applyChatSessionPickerSearch(state: AppViewState) {
258249
}
259250

260251
function clearChatSessionPickerSearch(state: AppViewState) {
261-
state.chatSessionSearchOpen = false;
262252
state.chatSessionPickerQuery = "";
263253
state.chatSessionPickerAppliedQuery = "";
264254
state.chatSessionPickerError = null;
@@ -369,28 +359,7 @@ function renderChatSessionPickerSearchControls(
369359
state: AppViewState,
370360
surface: ChatSessionSelectSurface,
371361
) {
372-
const hasQuery =
373-
(state.chatSessionPickerQuery ?? "").trim() !== "" ||
374-
(state.chatSessionPickerAppliedQuery ?? "").trim() !== "";
375-
const searchVisible = state.chatSessionSearchOpen || hasQuery;
376362
const disabled = !state.connected || !state.client || state.chatSessionPickerLoading;
377-
if (!searchVisible) {
378-
return html`
379-
<div class="chat-controls__session-actions">
380-
<button
381-
class="btn btn--ghost btn--icon chat-controls__session-search-toggle"
382-
data-chat-session-search-toggle="true"
383-
type="button"
384-
title=${t("chat.selectors.sessionSearch")}
385-
aria-label=${t("chat.selectors.sessionSearch")}
386-
?disabled=${disabled}
387-
@click=${() => openChatSessionSearch(state, surface)}
388-
>
389-
${icons.search}
390-
</button>
391-
</div>
392-
`;
393-
}
394363
return html`
395364
<div class="chat-controls__session-actions">
396365
<label class="field chat-controls__session-search">
@@ -403,12 +372,14 @@ function renderChatSessionPickerSearchControls(
403372
aria-label=${t("chat.selectors.sessionSearch")}
404373
.value=${state.chatSessionPickerQuery ?? ""}
405374
?disabled=${disabled}
375+
@focus=${() => openChatSessionPicker(state, surface)}
406376
@input=${(event: Event) => {
407377
state.chatSessionPickerQuery = (event.target as HTMLInputElement).value;
408378
}}
409379
@keydown=${(event: KeyboardEvent) => {
410380
if (event.key === "Enter") {
411381
event.preventDefault();
382+
openChatSessionPicker(state, surface);
412383
void applyChatSessionPickerSearch(state);
413384
} else if (event.key === "Escape") {
414385
event.preventDefault();
@@ -418,28 +389,6 @@ function renderChatSessionPickerSearchControls(
418389
}}
419390
/>
420391
</label>
421-
<button
422-
class="btn btn--ghost btn--icon"
423-
data-chat-session-search-submit="true"
424-
type="button"
425-
title=${t("common.search")}
426-
aria-label=${t("common.search")}
427-
?disabled=${disabled}
428-
@click=${() => void applyChatSessionPickerSearch(state)}
429-
>
430-
${icons.search}
431-
</button>
432-
<button
433-
class="btn btn--ghost btn--icon"
434-
data-chat-session-search-clear="true"
435-
type="button"
436-
title=${t("chat.selectors.clearSessionSearch")}
437-
aria-label=${t("chat.selectors.clearSessionSearch")}
438-
?disabled=${disabled}
439-
@click=${() => clearChatSessionPickerSearch(state)}
440-
>
441-
${icons.x}
442-
</button>
443392
</div>
444393
`;
445394
}

ui/src/ui/e2e/chat-picker-pagination.e2e.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,6 @@ describeControlUiE2e("Control UI chat picker mocked Gateway E2E", () => {
154154
try {
155155
await page.goto(`${server.baseUrl}chat`);
156156
await page.getByRole("button", { name: "Chat session" }).click();
157-
await page.locator('[data-chat-session-search-toggle="true"]').last().click();
158157

159158
const searchInput = page.locator('[data-chat-session-picker-search="true"]').last();
160159
await searchInput.waitFor({ state: "visible", timeout: 10_000 });
@@ -174,7 +173,7 @@ describeControlUiE2e("Control UI chat picker mocked Gateway E2E", () => {
174173
await page.getByRole("option", { name: /Alpha planning/u }).waitFor({ timeout: 10_000 });
175174

176175
await searchInput.fill(" telegram ");
177-
await page.locator('[data-chat-session-search-submit="true"]').last().click();
176+
await searchInput.press("Enter");
178177

179178
const searchRequest = await waitForSessionsRequest(
180179
gateway,

ui/src/ui/views/chat.test.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,24 +1310,15 @@ describe("chat session controls", () => {
13101310
const container = document.createElement("div");
13111311
render(renderChatSessionSelect(state), container);
13121312

1313-
expect(container.querySelector('input[data-chat-session-picker-search="true"]')).toBeNull();
1314-
container
1315-
.querySelector<HTMLButtonElement>('button[data-chat-session-search-toggle="true"]')!
1316-
.click();
1317-
render(renderChatSessionSelect(state), container);
13181313
const input = container.querySelector<HTMLInputElement>(
13191314
'input[data-chat-session-picker-search="true"]',
13201315
);
1321-
const submit = container.querySelector<HTMLButtonElement>(
1322-
'button[data-chat-session-search-submit="true"]',
1323-
);
13241316

13251317
input!.value = " telegram ";
13261318
input!.dispatchEvent(new Event("input", { bubbles: true }));
13271319
expect(state.chatSessionPickerQuery).toBe(" telegram ");
1320+
input!.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true }));
13281321
expect(state.chatSessionPickerOpen).toBe(true);
1329-
expect(submit?.disabled).toBe(false);
1330-
submit!.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true }));
13311322
await vi.waitFor(() => expect(state.chatSessionPickerAppliedQuery).toBe("telegram"));
13321323
render(renderChatSessionSelect(state), container);
13331324

@@ -1367,7 +1358,7 @@ describe("chat session controls", () => {
13671358
render(renderChatSessionSelect(state), desktopContainer);
13681359

13691360
desktopContainer
1370-
.querySelector<HTMLButtonElement>('button[data-chat-session-search-toggle="true"]')!
1361+
.querySelector<HTMLButtonElement>('button[data-chat-session-select="true"]')!
13711362
.click();
13721363
render(renderChatSessionSelect(state, undefined, { surface: "mobile" }), mobileContainer);
13731364
render(renderChatSessionSelect(state), desktopContainer);

0 commit comments

Comments
 (0)