mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 12:56:01 -06:00
refactor(ui): remove retired chat control styles (#130201)
* refactor(ui): remove retired chat control styles * fix(ui): keep background config refreshes from blocking saves
This commit is contained in:
committed by
GitHub
parent
adb3fb2967
commit
a665de9c1d
@@ -224,7 +224,7 @@ export async function executeConfigExternalMutation<T>(
|
||||
|
||||
export async function loadConfig(
|
||||
state: RuntimeConfigState,
|
||||
options: LoadConfigOptions = {},
|
||||
options: LoadConfigOptions & { background?: boolean } = {},
|
||||
isCurrentLoad: () => boolean = () => true,
|
||||
): Promise<boolean> {
|
||||
const client = state.client;
|
||||
@@ -233,7 +233,9 @@ export async function loadConfig(
|
||||
}
|
||||
const connectionEpoch = currentConfigConnectionEpoch(state);
|
||||
const version = nextRequestVersion(state, "config");
|
||||
state.configLoading = true;
|
||||
if (!options.background) {
|
||||
state.configLoading = true;
|
||||
}
|
||||
state.lastError = null;
|
||||
state.chatError = null;
|
||||
try {
|
||||
@@ -249,7 +251,10 @@ export async function loadConfig(
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
if (isCurrentRequest(state, "config", version, client, connectionEpoch)) {
|
||||
if (
|
||||
!options.background &&
|
||||
isCurrentRequest(state, "config", version, client, connectionEpoch)
|
||||
) {
|
||||
state.configLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,9 +344,12 @@ describe("runtime config capability", () => {
|
||||
const { runtimeConfig } = createConfigCapabilityHarness(
|
||||
request as GatewayBrowserClient["request"],
|
||||
);
|
||||
await runtimeConfig.ensureLoaded();
|
||||
const initialLoad = runtimeConfig.ensureLoaded();
|
||||
expect(runtimeConfig.state.configLoading).toBe(true);
|
||||
await initialLoad;
|
||||
|
||||
await vi.advanceTimersByTimeAsync(250);
|
||||
expect(runtimeConfig.state.configLoading).toBe(false);
|
||||
runtimeConfig.patchForm(["count"], 2);
|
||||
await vi.advanceTimersByTimeAsync(CONFIG_FORM_AUTO_SAVE_DEBOUNCE_MS);
|
||||
expect(runtimeConfig.state.configSnapshot?.hash).toBe("hash-2");
|
||||
|
||||
@@ -142,7 +142,8 @@ export function createRuntimeConfigCapability(
|
||||
state.connected &&
|
||||
state.configNeedsApply &&
|
||||
state.configSnapshot?.appliedConfigHash !== undefined,
|
||||
refresh: (isCurrent) => loadOnce("config", () => loadConfig(state, {}, isCurrent)),
|
||||
refresh: (isCurrent) =>
|
||||
loadOnce("config", () => loadConfig(state, { background: true }, isCurrent)),
|
||||
});
|
||||
const refreshConnectionState = () => {
|
||||
const config = run(() => loadConfig(state));
|
||||
|
||||
@@ -139,7 +139,6 @@ type ChatFixtureOptions = {
|
||||
crowdedComposerFooter?: boolean;
|
||||
direct?: boolean;
|
||||
sessionRailBody?: string;
|
||||
singleAgent?: boolean;
|
||||
slashMenu?: boolean;
|
||||
};
|
||||
|
||||
@@ -464,40 +463,6 @@ function chatFooterActionsHtml() {
|
||||
`;
|
||||
}
|
||||
|
||||
function chatControlsHtml(opts: { agent?: boolean } = {}) {
|
||||
const showAgent = opts.agent !== false;
|
||||
return `
|
||||
<div class="chat-mobile-controls-wrapper">
|
||||
<button class="btn btn--sm btn--icon chat-controls-mobile-toggle" aria-expanded="true" aria-controls="chat-mobile-controls-dropdown">${iconSvg()}</button>
|
||||
<div id="chat-mobile-controls-dropdown" class="chat-controls-dropdown open">
|
||||
<div class="chat-controls">
|
||||
<div class="chat-controls__session-row${showAgent ? "" : " chat-controls__session-row--single-agent"}">
|
||||
${
|
||||
showAgent
|
||||
? `<label class="field chat-controls__session chat-controls__agent">
|
||||
<select data-chat-agent-filter="true" aria-label="Filter sessions by agent"><option>Alpha</option><option>Beta</option></select>
|
||||
</label>`
|
||||
: ""
|
||||
}
|
||||
<label class="field chat-controls__session chat-controls__session-picker">
|
||||
<select data-chat-session-select="true" aria-label="Chat thread"><option>Daily planning</option></select>
|
||||
</label>
|
||||
<details class="chat-controls__session chat-controls__inline-select chat-controls__model">
|
||||
<summary class="chat-controls__inline-select-trigger" data-chat-model-select="true" data-chat-thinking-select="true" data-chat-select-value="" data-chat-thinking-value="" aria-label="Chat model">gpt-5 · High</summary>
|
||||
</details>
|
||||
</div>
|
||||
<div class="chat-controls__thinking">
|
||||
<button class="btn btn--sm btn--icon active">${iconSvg()}</button>
|
||||
<button class="btn btn--sm btn--icon active">${iconSvg()}</button>
|
||||
<button class="btn btn--sm btn--icon">${iconSvg()}</button>
|
||||
<button class="btn btn--sm btn--icon active">${iconSvg()}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function composerControlsHtml() {
|
||||
return `
|
||||
<div class="agent-chat__composer-controls">
|
||||
@@ -543,38 +508,6 @@ function composerControlsHtml() {
|
||||
`;
|
||||
}
|
||||
|
||||
function chatHeaderControlsHtml(hidden = false) {
|
||||
return `
|
||||
<main class="content content--chat" data-chat-header-responsive-fixture>
|
||||
<section class="content-header${hidden ? " content-header--chat-hidden" : ""}"${hidden ? ' inert aria-hidden="true"' : ""}>
|
||||
<div>
|
||||
<div class="chat-controls__session-row">
|
||||
<label class="field chat-controls__session chat-controls__agent">
|
||||
<select data-chat-agent-filter="true" aria-label="Filter sessions by agent"><option>Valentina</option></select>
|
||||
</label>
|
||||
<label class="field chat-controls__session chat-controls__session-picker">
|
||||
<select data-chat-session-select="true" aria-label="Chat thread"><option>main</option></select>
|
||||
</label>
|
||||
<details class="chat-controls__session chat-controls__inline-select chat-controls__model">
|
||||
<summary class="chat-controls__inline-select-trigger" data-chat-model-select="true" data-chat-thinking-select="true" data-chat-select-value="gpt-5.5" data-chat-thinking-value="" aria-label="Chat model">gpt-5.5 · High</summary>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-meta">
|
||||
<div class="chat-controls">
|
||||
<button class="btn btn--sm btn--icon" aria-label="Refresh chat data">${iconSvg()}</button>
|
||||
<span class="chat-controls__separator">|</span>
|
||||
<button class="btn btn--sm btn--icon active" aria-label="Toggle assistant thinking">${iconSvg()}</button>
|
||||
<button class="btn btn--sm btn--icon active" aria-label="Toggle tool calls">${iconSvg()}</button>
|
||||
<button class="btn btn--sm btn--icon active" aria-label="Show cron sessions">${iconSvg()}</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="card chat"></section>
|
||||
</main>
|
||||
`;
|
||||
}
|
||||
|
||||
function chatHtml(opts: ChatFixtureOptions = {}, mobileNavLayout = false) {
|
||||
return `
|
||||
<div class="shell shell--chat${mobileNavLayout ? " shell--mobile-nav" : ""}" data-chat-responsive-fixture>
|
||||
@@ -582,7 +515,6 @@ function chatHtml(opts: ChatFixtureOptions = {}, mobileNavLayout = false) {
|
||||
<div class="topnav-shell">
|
||||
<div class="topnav-shell__actions">
|
||||
<button class="topbar-search">${iconSvg()}</button>
|
||||
<div>${chatControlsHtml({ agent: !opts.singleAgent })}</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -917,19 +849,6 @@ function rectsOverlap(
|
||||
);
|
||||
}
|
||||
|
||||
async function openHeaderFixture(width: number, height: number, opts: { hidden?: boolean } = {}) {
|
||||
const page = await openBrowserPage(width, height);
|
||||
try {
|
||||
await page.setContent(
|
||||
`<!doctype html><html><head><style>${readUiCss()}</style></head><body>${chatHeaderControlsHtml(Boolean(opts.hidden))}</body></html>`,
|
||||
);
|
||||
return page;
|
||||
} catch (error) {
|
||||
await closeBrowserPage(page);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function expectNoHorizontalOverflow(page: Page) {
|
||||
const metrics = await page.evaluate(() => ({
|
||||
body: document.body.scrollWidth,
|
||||
@@ -2780,66 +2699,6 @@ describeBrowserLayout.concurrent("chat responsive browser layout", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it.each([
|
||||
[1120, 740],
|
||||
[1366, 900],
|
||||
[1440, 900],
|
||||
] as const)("keeps desktop chat controls in one row at %sx%s", async (width, height) => {
|
||||
const page = await openHeaderFixture(width, height);
|
||||
try {
|
||||
await expectNoHorizontalOverflow(page);
|
||||
const controls = await page.evaluate(() => {
|
||||
const rectFor = (selector: string) => {
|
||||
const node = document.querySelector(selector);
|
||||
const rect = node?.getBoundingClientRect();
|
||||
return rect ? { x: rect.x, y: rect.y, width: rect.width, height: rect.height } : null;
|
||||
};
|
||||
return {
|
||||
session: rectFor('[data-chat-session-select="true"]'),
|
||||
agent: rectFor('[data-chat-agent-filter="true"]'),
|
||||
model: rectFor('[data-chat-model-select="true"]'),
|
||||
action: rectFor(".page-meta .btn--icon"),
|
||||
};
|
||||
});
|
||||
const rowY = [
|
||||
controls.session?.y,
|
||||
controls.agent?.y,
|
||||
controls.model?.y,
|
||||
controls.action?.y,
|
||||
].filter((value): value is number => typeof value === "number");
|
||||
expect(rowY.length).toBe(4);
|
||||
expect(Math.max(...rowY) - Math.min(...rowY)).toBeLessThanOrEqual(4);
|
||||
const agent = expectControlRect(controls.agent, "agent");
|
||||
const session = expectControlRect(controls.session, "session");
|
||||
expect(agent.x).toBeLessThan(session.x);
|
||||
expect(session.width / agent.width).toBeGreaterThan(1.25);
|
||||
expect(session.width / agent.width).toBeLessThan(1.55);
|
||||
} finally {
|
||||
await closeBrowserPage(page);
|
||||
}
|
||||
});
|
||||
|
||||
it("collapses the desktop chat controls row when scroll state hides it", async () => {
|
||||
const page = await openHeaderFixture(1366, 900, { hidden: true });
|
||||
try {
|
||||
const hiddenState = await page.evaluate(() => {
|
||||
const header = document.querySelector(".content-header") as HTMLElement | null;
|
||||
const rect = header?.getBoundingClientRect();
|
||||
const style = header ? getComputedStyle(header) : null;
|
||||
return {
|
||||
height: rect?.height ?? -1,
|
||||
opacity: style?.opacity ?? "",
|
||||
pointerEvents: style?.pointerEvents ?? "",
|
||||
};
|
||||
});
|
||||
expect(hiddenState.height).toBeLessThanOrEqual(1);
|
||||
expect(hiddenState.opacity).toBe("0");
|
||||
expect(hiddenState.pointerEvents).toBe("none");
|
||||
} finally {
|
||||
await closeBrowserPage(page);
|
||||
}
|
||||
});
|
||||
|
||||
it.each(VIEWPORTS)("keeps the chat shell inside the viewport at %sx%s", async (width, height) => {
|
||||
const page = await openFixture(width, height);
|
||||
try {
|
||||
@@ -3022,73 +2881,6 @@ describeBrowserLayout.concurrent("chat responsive browser layout", () => {
|
||||
},
|
||||
);
|
||||
|
||||
it.each(["dark", "light"] as const)(
|
||||
"keeps mobile controls inside the viewport with touch targets in %s mode",
|
||||
async (themeMode) => {
|
||||
const page = await openFixture(320, 568);
|
||||
try {
|
||||
await page.evaluate(
|
||||
(mode) => document.documentElement.setAttribute("data-theme-mode", mode),
|
||||
themeMode,
|
||||
);
|
||||
const dropdown = await getBoundingBox(page, ".chat-controls-dropdown.open");
|
||||
expect(dropdown.x).toBeGreaterThanOrEqual(8);
|
||||
expect(dropdown.x + dropdown.width).toBeLessThanOrEqual(312);
|
||||
await expectNoHorizontalOverflow(page);
|
||||
const mobileControls = await page.evaluate(() => {
|
||||
const rectFor = (selector: string) => {
|
||||
const node = document.querySelector(selector) as HTMLElement | null;
|
||||
if (!node) {
|
||||
return null;
|
||||
}
|
||||
const rect = node.getBoundingClientRect();
|
||||
return {
|
||||
x: rect.x,
|
||||
y: rect.y,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
text: node.textContent?.trim() ?? "",
|
||||
display: getComputedStyle(node).display,
|
||||
};
|
||||
};
|
||||
return {
|
||||
agent: rectFor('[data-chat-agent-filter="true"]'),
|
||||
session: rectFor('[data-chat-session-select="true"]'),
|
||||
model: rectFor('[data-chat-model-select="true"]'),
|
||||
compactCount: document.querySelectorAll('[data-chat-thinking-select-compact="true"]')
|
||||
.length,
|
||||
};
|
||||
});
|
||||
const agent = expectControlRect(mobileControls.agent, "agent");
|
||||
const session = expectControlRect(mobileControls.session, "session");
|
||||
const model = expectControlRect(mobileControls.model, "model");
|
||||
expect(session.y).toBe(agent.y);
|
||||
expect(agent.x).toBeLessThan(session.x);
|
||||
expect(session.width / agent.width).toBeGreaterThan(1.25);
|
||||
expect(session.width / agent.width).toBeLessThan(1.55);
|
||||
expect(model.display).not.toBe("none");
|
||||
expect(model.text).toBe("gpt-5 · High");
|
||||
expect(mobileControls.compactCount).toBe(0);
|
||||
|
||||
const sizes = await page
|
||||
.locator(".chat-controls-mobile-toggle, .chat-controls-dropdown .btn--icon")
|
||||
.evaluateAll((nodes) =>
|
||||
nodes.map((node) => {
|
||||
const rect = (node as HTMLElement).getBoundingClientRect();
|
||||
return { width: rect.width, height: rect.height };
|
||||
}),
|
||||
);
|
||||
expect(sizes.length).toBeGreaterThan(0);
|
||||
for (const size of sizes) {
|
||||
expect(size.width).toBeGreaterThanOrEqual(TOUCH_TARGET_MIN_PX);
|
||||
expect(size.height).toBeGreaterThanOrEqual(TOUCH_TARGET_MIN_PX);
|
||||
}
|
||||
} finally {
|
||||
await closeBrowserPage(page);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it("keeps primary composer actions desktop-sized on phones", async () => {
|
||||
const page = await openFixture(320, 568);
|
||||
try {
|
||||
@@ -4450,20 +4242,6 @@ describeBrowserLayout.concurrent("chat responsive browser layout", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("uses the compact mobile grid when the agent filter is not rendered", async () => {
|
||||
const page = await openFixture(320, 568, { singleAgent: true });
|
||||
try {
|
||||
await expectNoHorizontalOverflow(page);
|
||||
expect(await page.locator('[data-chat-agent-filter="true"]').count()).toBe(0);
|
||||
const session = await getBoundingBox(page, '[data-chat-session-select="true"]');
|
||||
const model = await getBoundingBox(page, '[data-chat-model-select="true"]');
|
||||
expect(model.y).toBeGreaterThan(session.y);
|
||||
expect(model.width).toBe(session.width);
|
||||
} finally {
|
||||
await closeBrowserPage(page);
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps the embedded side-chat composer trailing and flush with the tab strip", async () => {
|
||||
const page = await openBrowserPage(1024, 768);
|
||||
try {
|
||||
|
||||
@@ -78,17 +78,6 @@ openclaw-chat-page {
|
||||
}
|
||||
}
|
||||
|
||||
.chat-error__dot {
|
||||
flex-shrink: 0;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
/* Optically centers the dot on the first text line when the message wraps. */
|
||||
margin-top: 6px;
|
||||
border-radius: var(--radius-full);
|
||||
background: var(--danger);
|
||||
box-shadow: 0 0 8px color-mix(in srgb, var(--danger) 55%, transparent);
|
||||
}
|
||||
|
||||
.chat-error__content {
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
@@ -207,25 +196,12 @@ openclaw-chat-page {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.chat-cloud-disk-space-notice__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.chat-cloud-disk-space-notice__title svg {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.chat-workspace-conflict-notice__content p,
|
||||
.chat-workspace-conflict-event p {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.chat-workspace-conflict-notice__title,
|
||||
.chat-workspace-conflict-event__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -233,7 +209,6 @@ openclaw-chat-page {
|
||||
color: color-mix(in srgb, var(--warn) 82%, var(--text-strong));
|
||||
}
|
||||
|
||||
.chat-workspace-conflict-notice__title svg,
|
||||
.chat-workspace-conflict-event__header svg {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
@@ -3823,18 +3798,6 @@ button.chat-pr__diff {
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
.agent-chat__composer-controls .chat-controls {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.agent-chat__composer-controls .chat-controls__session-row {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.chat-composer-model-control {
|
||||
display: inline-flex;
|
||||
flex: 0 0 auto;
|
||||
@@ -3848,14 +3811,6 @@ button.chat-pr__diff {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.agent-chat__stt-interim {
|
||||
padding: 10px 14px 0;
|
||||
color: var(--muted);
|
||||
font-size: var(--control-ui-text-sm);
|
||||
line-height: 1.35;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.agent-chat__composer-input-row {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
@@ -4098,24 +4053,6 @@ button.chat-pr__diff {
|
||||
}
|
||||
}
|
||||
|
||||
.agent-chat__input-btn--selected:not(:disabled) {
|
||||
position: relative;
|
||||
color: var(--accent);
|
||||
background: color-mix(in srgb, var(--accent) 12%, transparent);
|
||||
}
|
||||
|
||||
.agent-chat__input-btn--selected::after {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border: 1px solid var(--bg);
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.agent-chat__input-btn:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
@@ -4416,11 +4353,6 @@ button.chat-pr__diff {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.agent-chat__composer-error-text {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.agent-chat__composer-error .callout__dismiss {
|
||||
margin: -4px -4px -4px 0;
|
||||
border-radius: var(--radius-full);
|
||||
@@ -4697,12 +4629,6 @@ button.chat-pr__diff {
|
||||
height: 13px;
|
||||
}
|
||||
|
||||
@keyframes chat-run-status-spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Send is the primary action of the whole surface, so it gets the same solid
|
||||
accent treatment as .btn.primary; variants (--stop, --voice*) override the
|
||||
fill instead of reintroducing borders. */
|
||||
@@ -6103,13 +6029,6 @@ button.chat-pr__diff {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.chat-attachment-file__type {
|
||||
color: var(--muted);
|
||||
font-size: 10px;
|
||||
line-height: 1.1;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.chat-attachment-file--pasted-text .chat-attachment-file__name {
|
||||
font-size: 0.86rem;
|
||||
line-height: 1.2;
|
||||
@@ -6162,50 +6081,11 @@ button.chat-pr__diff {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Chat controls - moved to content-header area, left aligned */
|
||||
.chat-controls {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
min-height: 36px;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.chat-controls__session {
|
||||
min-width: 0;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.chat-controls__agent {
|
||||
min-width: 0;
|
||||
max-width: none;
|
||||
grid-area: agent;
|
||||
}
|
||||
|
||||
.chat-controls__session-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(116px, 5fr) minmax(132px, 7fr) minmax(180px, 8fr);
|
||||
grid-template-areas: "agent session model";
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
width: 100%;
|
||||
min-height: 36px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.chat-controls__session-row--single-agent {
|
||||
grid-template-columns: minmax(132px, 7fr) minmax(180px, 8fr);
|
||||
grid-template-areas: "session model";
|
||||
}
|
||||
|
||||
.chat-controls__session-picker {
|
||||
grid-area: session;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.chat-controls__model {
|
||||
grid-area: model;
|
||||
min-width: 0;
|
||||
@@ -6344,42 +6224,6 @@ button.chat-pr__diff {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
.chat-controls__trigger-meta {
|
||||
color: var(--muted);
|
||||
flex: 0 0 auto;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.chat-controls__trigger-provider-wrap {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.chat-controls__model-capability-alert {
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
right: -1px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
border-radius: var(--radius-full);
|
||||
background: var(--chat-composer-surface);
|
||||
color: color-mix(in srgb, var(--warn) 82%, var(--text));
|
||||
}
|
||||
|
||||
.chat-controls__model-capability-alert svg {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.chat-controls__model-capability-alert:focus-visible {
|
||||
outline: 2px solid var(--ring);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
.chat-controls__model-trigger-skeleton {
|
||||
display: block;
|
||||
width: 100%;
|
||||
@@ -7340,48 +7184,7 @@ button.chat-pr__diff {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.chat-controls__thinking {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 12px;
|
||||
padding: 4px 10px;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
/* Controls separator: the visible divider is the background; any glyph content
|
||||
is clipped by the 1px overflow-hidden box. */
|
||||
.chat-controls__separator {
|
||||
align-self: center;
|
||||
flex: 0 0 1px;
|
||||
width: 1px;
|
||||
height: 22px;
|
||||
margin: 0 3px;
|
||||
overflow: hidden;
|
||||
background: color-mix(in srgb, var(--border-strong) 72%, transparent);
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
font-weight: 300;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.chat-controls .btn--icon {
|
||||
width: 36px;
|
||||
min-width: 36px;
|
||||
height: 36px;
|
||||
padding: 0;
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
:root[data-theme-mode="light"] .chat-controls__separator {
|
||||
background: rgba(16, 24, 40, 0.18);
|
||||
}
|
||||
|
||||
.chat-controls__session select,
|
||||
.chat-controls__agent select {
|
||||
.chat-controls__session select {
|
||||
height: 36px;
|
||||
min-height: 36px;
|
||||
padding: 0 34px 0 12px;
|
||||
@@ -7395,12 +7198,6 @@ button.chat-pr__diff {
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* Light theme thinking indicator override */
|
||||
:root[data-theme-mode="light"] .chat-controls__thinking {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border-color: rgba(16, 24, 40, 0.15);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.chat-main__conversation {
|
||||
--chat-composer-top-margin: 6px;
|
||||
@@ -7415,20 +7212,11 @@ button.chat-pr__diff {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.chat-controls__agent {
|
||||
min-width: 120px;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.chat-controls__model {
|
||||
min-width: 140px;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.chat-controls {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.agent-chat__input-btn,
|
||||
.chat-send-btn {
|
||||
width: 44px;
|
||||
@@ -7574,10 +7362,6 @@ button.chat-pr__diff {
|
||||
padding-inline: 0;
|
||||
}
|
||||
|
||||
.agent-chat__input--chat .context-ring__pct {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.agent-chat__input--chat .context-ring__dial,
|
||||
.agent-chat__input--chat .chat-controls__permission-icon,
|
||||
.agent-chat__input--chat .chat-controls__permission-lock,
|
||||
@@ -7761,15 +7545,6 @@ button.chat-pr__diff {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.agent-chat__composer-controls .chat-controls {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.chat-controls {
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.chat-controls__session {
|
||||
min-width: 120px;
|
||||
}
|
||||
@@ -8078,6 +7853,3 @@ button.chat-pr__diff {
|
||||
min-height: 44px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile dropdown toggle — hidden on desktop */
|
||||
/* Mobile gear toggle + dropdown are hidden by default in layout.css */
|
||||
|
||||
@@ -1487,13 +1487,6 @@ select.input {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
:root[data-theme-mode="light"] .chat-controls .btn--icon.active {
|
||||
border-color: var(--accent);
|
||||
background: var(--accent-subtle);
|
||||
color: var(--accent);
|
||||
box-shadow: 0 0 0 1px var(--accent-subtle);
|
||||
}
|
||||
|
||||
/* Ghost stays chromeless in light mode too: without the border-color reset,
|
||||
the higher-specificity light .btn--icon override above re-adds a 1px border
|
||||
that dark mode never shows. Chrome appears on hover only. */
|
||||
|
||||
@@ -4113,17 +4113,6 @@ html:not(.openclaw-native-macos):not(.openclaw-native-nav):not(.openclaw-native-
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.content--chat .content-header.content-header--chat-hidden {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
min-height: 0;
|
||||
max-height: 0px;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 22px;
|
||||
font-weight: 650;
|
||||
@@ -4182,12 +4171,6 @@ html:not(.openclaw-native-macos):not(.openclaw-native-nav):not(.openclaw-native-
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.content--chat .chat-controls {
|
||||
flex-shrink: 0;
|
||||
flex-wrap: nowrap;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
/* ===========================================
|
||||
Grid Utilities
|
||||
=========================================== */
|
||||
@@ -4243,20 +4226,6 @@ html:not(.openclaw-native-macos):not(.openclaw-native-nav):not(.openclaw-native-
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile chat controls — hidden on desktop, shown in layout.mobile.css */
|
||||
.chat-mobile-controls-wrapper {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.chat-controls-mobile-toggle {
|
||||
display: none;
|
||||
border-radius: var(--radius-full);
|
||||
}
|
||||
|
||||
.chat-controls-dropdown {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Standalone terminal/desktop documents: centered notice when the gateway
|
||||
refuses or lacks the requested surface. */
|
||||
.terminal-view-unavailable,
|
||||
|
||||
@@ -26,46 +26,17 @@
|
||||
row-gap: 0;
|
||||
}
|
||||
|
||||
.content--chat .chat-controls {
|
||||
margin-left: 0;
|
||||
justify-content: flex-end;
|
||||
row-gap: 0;
|
||||
}
|
||||
|
||||
.chat-controls__session-row {
|
||||
grid-template-columns: minmax(96px, 5fr) minmax(112px, 7fr) minmax(164px, 8fr);
|
||||
grid-template-areas: "agent session model";
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chat-controls__session-row--single-agent {
|
||||
grid-template-columns: minmax(112px, 7fr) minmax(164px, 8fr);
|
||||
grid-template-areas: "session model";
|
||||
}
|
||||
|
||||
.chat-controls__agent {
|
||||
grid-area: agent;
|
||||
}
|
||||
|
||||
.chat-controls__session-picker {
|
||||
grid-area: session;
|
||||
}
|
||||
|
||||
.chat-controls__model {
|
||||
grid-area: model;
|
||||
}
|
||||
|
||||
.chat-controls__session,
|
||||
.chat-controls__agent,
|
||||
.chat-controls__model {
|
||||
min-width: 0;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.chat-controls__session select,
|
||||
.chat-controls__agent select,
|
||||
.chat-controls__inline-select-trigger {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
@@ -339,134 +310,6 @@ html.openclaw-native-macos body .shell--mobile-nav .topnav-shell__actions {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/* Show the mobile gear toggle (lives in topbar now) */
|
||||
.chat-mobile-controls-wrapper {
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.chat-mobile-controls-wrapper .chat-controls-mobile-toggle {
|
||||
display: flex;
|
||||
width: 44px;
|
||||
min-width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
/* The dropdown panel is viewport-clamped so narrow phones never crop controls. */
|
||||
.chat-mobile-controls-wrapper .chat-controls-dropdown {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: calc(var(--shell-topbar-height) + 8px);
|
||||
right: max(8px, var(--safe-area-right));
|
||||
left: auto;
|
||||
z-index: 100;
|
||||
width: min(420px, calc(100vw - var(--safe-area-left) - var(--safe-area-right) - 16px));
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 10px;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
max-height: min(72vh, 520px);
|
||||
overflow: auto;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
.chat-mobile-controls-wrapper .chat-controls-dropdown.open {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.chat-mobile-controls-wrapper .chat-controls-dropdown .chat-controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chat-mobile-controls-wrapper .chat-controls-dropdown .chat-controls__session-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
|
||||
grid-template-areas:
|
||||
"agent session"
|
||||
"model model";
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chat-mobile-controls-wrapper .chat-controls-dropdown .chat-controls__session-row--single-agent {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
grid-template-areas:
|
||||
"session"
|
||||
"model";
|
||||
}
|
||||
|
||||
.chat-mobile-controls-wrapper .chat-controls-dropdown .chat-controls__session {
|
||||
min-width: unset;
|
||||
max-width: unset;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chat-mobile-controls-wrapper .chat-controls-dropdown .chat-controls__agent {
|
||||
grid-area: agent;
|
||||
min-width: unset;
|
||||
max-width: unset;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chat-mobile-controls-wrapper .chat-controls-dropdown .chat-controls__session-picker {
|
||||
grid-area: session;
|
||||
}
|
||||
|
||||
.chat-mobile-controls-wrapper .chat-controls-dropdown .chat-controls__model {
|
||||
grid-area: model;
|
||||
}
|
||||
|
||||
.chat-mobile-controls-wrapper .chat-controls-dropdown .chat-controls__inline-select-trigger {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
min-height: 44px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.chat-mobile-controls-wrapper .chat-controls-dropdown .chat-controls__model-menu,
|
||||
.chat-mobile-controls-wrapper .chat-controls-dropdown .chat-controls__effort-menu {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chat-mobile-controls-wrapper .chat-controls-dropdown .chat-controls__session select {
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
min-height: 44px;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.chat-mobile-controls-wrapper .chat-controls-dropdown .chat-controls__agent select {
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
min-height: 44px;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.chat-mobile-controls-wrapper .chat-controls-dropdown .chat-controls__thinking {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
align-items: center;
|
||||
justify-content: stretch;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.chat-mobile-controls-wrapper .chat-controls-dropdown .btn--icon {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 4px 4px 16px;
|
||||
gap: 12px;
|
||||
@@ -791,23 +634,6 @@ html.openclaw-native-macos body .shell--mobile-nav .topnav-shell__actions {
|
||||
max-width: 76px;
|
||||
}
|
||||
|
||||
.sessions-table .session-status-badge {
|
||||
max-width: 72px;
|
||||
gap: 5px;
|
||||
padding: 2px 6px;
|
||||
}
|
||||
|
||||
.sessions-table .session-goal-chip {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
max-width: 72px;
|
||||
gap: 0;
|
||||
padding: 2px 6px;
|
||||
}
|
||||
|
||||
.sessions-table .session-goal-chip__objective {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.data-table th,
|
||||
.data-table td {
|
||||
padding: 6px 4px;
|
||||
|
||||
@@ -246,24 +246,6 @@
|
||||
min-width: min(280px, 100%);
|
||||
}
|
||||
|
||||
.workboard-toggle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
min-height: var(--workboard-control-height);
|
||||
color: var(--muted);
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.workboard-toggle input {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.workboard-draft {
|
||||
display: grid;
|
||||
grid-template-rows: auto minmax(0, 1fr) auto;
|
||||
|
||||
Reference in New Issue
Block a user