mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 21:07:01 -06:00
fix(ui): remove context compact button (#130775)
* fix(ui): remove context compact button * fix(ui): remove context compact button OpenClaw-Publication: 37fcd8f7-5da2-4fe3-afcf-c77609e0ab33 * fix(ui): remove context compact button OpenClaw-Publication: 8779879a-59ce-40a4-a6ea-26a4f468f1ca --------- Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
This commit is contained in:
@@ -561,7 +561,7 @@ Chat error banners, including cloud runner failures, keep a compact preview. Ope
|
||||
- If you send a message while a model picker change for the same session is still saving, the composer waits for that session patch before calling `chat.send` so the send uses the selected model.
|
||||
- Typing `/new` creates and switches to the same fresh dashboard session as New Chat, except when `session.dmScope: "main"` is configured and the current parent is the agent's main session; then it resets the main session in place. Typing `/reset` keeps the Gateway's explicit in-place reset for the current session.
|
||||
- The chat model picker requests the Gateway's configured model view. If `agents.defaults.modelPolicy.allow` is non-empty, that policy drives the picker, including `provider/*` entries that keep provider-scoped catalogs dynamic. Otherwise the picker shows configured entries plus providers with usable auth; aliases and settings under `agents.defaults.models` do not restrict it. The full catalog stays available through the debug `models.list` RPC with `view: "all"`.
|
||||
- When fresh Gateway session usage reports include current context tokens, the chat composer toolbar shows a small context usage ring with the used percentage. Open the ring for the current context window, latest-run token counts and estimated total cost, provider/model identity, and the latest provider response's input/output/cache cost breakdown when reported. The ring switches to warning styling at high context pressure and, at recommended compaction levels, shows a compact button that runs the normal session compaction path. Stale token snapshots are hidden until the Gateway reports fresh usage again.
|
||||
- When fresh Gateway session usage reports include current context tokens, the chat composer toolbar shows a small context usage ring with the used percentage. Open the ring for the current context window, latest-run token counts and estimated total cost, provider/model identity, and the latest provider response's input/output/cache cost breakdown when reported. The ring switches to warning styling at high context pressure. Stale token snapshots are hidden until the Gateway reports fresh usage again.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Talk mode (browser realtime)">
|
||||
|
||||
@@ -554,7 +554,7 @@ suite.define(() => {
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps stale context visible as approximate without warning or compaction", async () => {
|
||||
it("keeps stale context visible as approximate without warning", async () => {
|
||||
const context = await suite.newBrowserContext({
|
||||
locale: "en-US",
|
||||
serviceWorkers: "block",
|
||||
@@ -598,7 +598,6 @@ suite.define(() => {
|
||||
await expect
|
||||
.poll(() => page.locator(".context-usage__popover").textContent())
|
||||
.toContain("~190k / 200k · ~95%");
|
||||
expect(await page.locator(".context-ring__action").count()).toBe(0);
|
||||
} finally {
|
||||
await suite.closeBrowserContext(context);
|
||||
}
|
||||
|
||||
@@ -52,6 +52,11 @@ const gatewayInjectedSessions = {
|
||||
ts: Date.now(),
|
||||
};
|
||||
|
||||
const highPressureSessions = {
|
||||
...gatewayInjectedSessions,
|
||||
sessions: [{ ...gatewayInjectedSessions.sessions[0], totalTokens: 190_000 }],
|
||||
};
|
||||
|
||||
const claudeSubscriptionAuthStatus = {
|
||||
ts: baseTime,
|
||||
providers: [
|
||||
@@ -144,6 +149,31 @@ async function closeChat(fixture: { context: BrowserContext; page: Page }): Prom
|
||||
}
|
||||
|
||||
suite.define(() => {
|
||||
it("shows high context pressure without a compact action", async () => {
|
||||
const fixture = await openChat(authStatusWithUsage, {
|
||||
"sessions.list": highPressureSessions,
|
||||
});
|
||||
const { page } = fixture;
|
||||
try {
|
||||
const contextRing = page.locator(".context-ring");
|
||||
await contextRing.waitFor({ state: "visible" });
|
||||
expect(await contextRing.getAttribute("aria-label")).toBe(
|
||||
"Session context usage: 190k of 200k (95%)",
|
||||
);
|
||||
expect(
|
||||
await contextRing.evaluate((element) =>
|
||||
element.classList.contains("context-ring--warning"),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(await page.locator(".context-usage button").count()).toBe(0);
|
||||
await page.screenshot({
|
||||
path: path.join(artifactDir, "00-high-context-without-compact-action.png"),
|
||||
});
|
||||
} finally {
|
||||
await closeChat(fixture);
|
||||
}
|
||||
});
|
||||
|
||||
it("renders provider usage inside the desktop context popover", async () => {
|
||||
const fixture = await openChat(authStatusWithUsage, {
|
||||
"sessions.list": gatewayInjectedSessions,
|
||||
|
||||
@@ -5997,8 +5997,6 @@ export const en: TranslationMap = {
|
||||
runInterrupted: "Interrupted",
|
||||
runStatus: "Run status: {status}",
|
||||
compactingContext: "Compacting context...",
|
||||
compacting: "Compacting",
|
||||
compact: "Compact",
|
||||
contextCompacted: "Context compacted",
|
||||
fallbackActive: "Fallback active: {model}",
|
||||
fallbackCleared: "Fallback cleared: {model}",
|
||||
@@ -6017,7 +6015,6 @@ export const en: TranslationMap = {
|
||||
browserAnnotationRemoved: "Browser annotation removed.",
|
||||
browserAnnotationUndoUnavailable:
|
||||
"Undo is unavailable because the browser annotation limit has been reached.",
|
||||
compactRecommendedContext: "Compact recommended session context",
|
||||
removeAttachment: "Remove attachment",
|
||||
removeBrowserAnnotation: "Remove browser annotation: {name}",
|
||||
addAttachment: "Add attachment",
|
||||
|
||||
@@ -354,10 +354,8 @@ describe("renderChatComposer context usage", () => {
|
||||
expect(container.textContent).not.toContain("Model:");
|
||||
});
|
||||
|
||||
it("warns on fresh high usage but keeps stale usage approximate and nonactionable", () => {
|
||||
const onCompact = vi.fn();
|
||||
it("warns on fresh high usage but keeps stale usage approximate", () => {
|
||||
let container = renderComposer({
|
||||
onCompact,
|
||||
sessions: {
|
||||
sessions: [
|
||||
{
|
||||
@@ -371,17 +369,14 @@ describe("renderChatComposer context usage", () => {
|
||||
defaults: { contextTokens: 200_000 },
|
||||
} as never,
|
||||
});
|
||||
const compact = container.querySelector<HTMLButtonElement>(".context-ring__compact");
|
||||
expect(compact?.getAttribute("aria-label")).toContain(
|
||||
const contextRing = container.querySelector(".context-ring");
|
||||
expect(contextRing?.getAttribute("aria-label")).toBe(
|
||||
"Session context usage: 190k of 200k (95%)",
|
||||
);
|
||||
expect(container.querySelector(".context-ring")).toBeNull();
|
||||
expect(compact?.textContent).toContain("Compact");
|
||||
compact?.click();
|
||||
expect(onCompact).toHaveBeenCalledOnce();
|
||||
expect(contextRing?.classList).toContain("context-ring--warning");
|
||||
expect(container.textContent).not.toContain("Compact");
|
||||
|
||||
container = renderComposer({
|
||||
onCompact,
|
||||
sessions: {
|
||||
sessions: [
|
||||
{
|
||||
@@ -402,6 +397,5 @@ describe("renderChatComposer context usage", () => {
|
||||
expect(container.querySelector(".context-ring")?.classList).not.toContain(
|
||||
"context-ring--warning",
|
||||
);
|
||||
expect(container.querySelector(".context-ring__compact")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -266,7 +266,6 @@ export class ChatPane extends ChatPaneLayoutRender {
|
||||
hasLocalRun: () => Boolean(state.chatRunId),
|
||||
sessionParticipationBlocked,
|
||||
onDenied: (reason) => this.publishHeaderError(reason),
|
||||
onCompact: () => void state.handleSendChat("/compact"),
|
||||
onAbort: () => void state.handleAbortChat({ preserveDraft: true }),
|
||||
onRewind: (entryId) => this.rewindToMessage(entryId),
|
||||
onFork: (entryId) => this.forkFromMessage(entryId),
|
||||
@@ -567,7 +566,6 @@ export class ChatPane extends ChatPaneLayoutRender {
|
||||
followUpModeOverride ? { followUpMode: followUpModeOverride } : undefined,
|
||||
submissionAction,
|
||||
),
|
||||
onCompact: sessionActionCallbacks.onCompact,
|
||||
// Checkpoint deep-link carries the archived filter so the row stays findable.
|
||||
onOpenSessionCheckpoints: () => {
|
||||
const status = selectedSessionArchived ? "&status=archived" : "";
|
||||
|
||||
@@ -31,7 +31,7 @@ type SessionActionAccess = ReturnType<typeof readChatSessionActionAccess>;
|
||||
type SessionAction = keyof SessionActionAccess;
|
||||
type SessionActionCallbacks = Pick<
|
||||
ChatProps,
|
||||
"onAbort" | "onClearHistory" | "onCompact" | "onForkMessage" | "onRewindMessage"
|
||||
"onAbort" | "onClearHistory" | "onForkMessage" | "onRewindMessage"
|
||||
>;
|
||||
|
||||
export function readChatPaneMutationAccess(
|
||||
@@ -219,7 +219,6 @@ export function createChatPaneSessionActionCallbacks(params: {
|
||||
hasLocalRun: () => boolean;
|
||||
sessionParticipationBlocked: boolean;
|
||||
onDenied: (reason: string) => void;
|
||||
onCompact: () => void;
|
||||
onAbort: () => void;
|
||||
onRewind: (entryId: string) => Promise<boolean>;
|
||||
onFork: (entryId: string) => Promise<void>;
|
||||
@@ -235,13 +234,6 @@ export function createChatPaneSessionActionCallbacks(params: {
|
||||
return false;
|
||||
};
|
||||
return {
|
||||
onCompact: access.compact.allowed
|
||||
? () => {
|
||||
if (requireCurrent("compact")) {
|
||||
params.onCompact();
|
||||
}
|
||||
}
|
||||
: undefined,
|
||||
onAbort:
|
||||
params.sessionParticipationBlocked || !access.abort.allowed
|
||||
? undefined
|
||||
|
||||
@@ -701,7 +701,6 @@ function createChatProps(overrides: Partial<ChatProps> = {}): ChatProps {
|
||||
onDraftChange: () => undefined,
|
||||
onRequestUpdate: () => undefined,
|
||||
onSend: () => undefined,
|
||||
onCompact: () => undefined,
|
||||
onToggleRealtimeTalk: () => undefined,
|
||||
onToggleRealtimeCamera: () => undefined,
|
||||
onDismissError: () => undefined,
|
||||
@@ -3167,7 +3166,6 @@ describe("chat loading skeleton", () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
onCompact: () => undefined,
|
||||
});
|
||||
|
||||
expect(
|
||||
|
||||
@@ -227,7 +227,6 @@ export type ChatProps = ChatTaskSuggestionTrayProps &
|
||||
onSlashIntent?: () => void | Promise<void>;
|
||||
onSlashCommand?: (command: string) => void;
|
||||
onSend: ChatComposerProps["onSend"];
|
||||
onCompact?: () => void | Promise<void>;
|
||||
onOpenSessionCheckpoints?: () => void | Promise<void>;
|
||||
onToggleRealtimeTalk?: () => void;
|
||||
onToggleRealtimeCamera?: () => void;
|
||||
@@ -485,7 +484,6 @@ export function renderChat(props: ChatProps) {
|
||||
onSlashIntent: props.onSlashIntent,
|
||||
onSlashCommand: props.onSlashCommand,
|
||||
onSend: props.onSend,
|
||||
onCompact: props.suggestionComposer ? undefined : props.onCompact,
|
||||
onToggleRealtimeTalk: props.suggestionComposer ? undefined : props.onToggleRealtimeTalk,
|
||||
onToggleRealtimeCamera: props.onToggleRealtimeCamera,
|
||||
onSwitchRealtimeCamera: props.onSwitchRealtimeCamera,
|
||||
|
||||
@@ -19,13 +19,9 @@ import {
|
||||
import { handleChatComposerDetailsToggle } from "./chat-picker-overlay.ts";
|
||||
|
||||
const CONTEXT_NOTICE_RATIO = 0.85;
|
||||
const CONTEXT_COMPACT_RATIO = 0.9;
|
||||
|
||||
type ContextNoticeOptions = {
|
||||
compactBusy?: boolean;
|
||||
compactDisabled?: boolean;
|
||||
messages?: unknown[];
|
||||
onCompact?: () => void | Promise<void>;
|
||||
providerUsage?: ProviderUsageDisplayProps;
|
||||
};
|
||||
|
||||
@@ -125,7 +121,6 @@ function getContextNoticeViewModel(
|
||||
color: string;
|
||||
bg: string;
|
||||
warning: boolean;
|
||||
compactRecommended: boolean;
|
||||
approximate: boolean;
|
||||
} | null {
|
||||
const used = session?.totalTokens;
|
||||
@@ -163,7 +158,6 @@ function getContextNoticeViewModel(
|
||||
color: "var(--muted)",
|
||||
bg: "color-mix(in srgb, var(--muted) 8%, transparent)",
|
||||
warning,
|
||||
compactRecommended: false,
|
||||
approximate,
|
||||
};
|
||||
}
|
||||
@@ -184,7 +178,6 @@ function getContextNoticeViewModel(
|
||||
color,
|
||||
bg,
|
||||
warning,
|
||||
compactRecommended: ratio >= CONTEXT_COMPACT_RATIO,
|
||||
approximate,
|
||||
};
|
||||
}
|
||||
@@ -329,8 +322,6 @@ export function renderContextNotice(
|
||||
if (!model && quotaGroups.length === 0) {
|
||||
return nothing;
|
||||
}
|
||||
const canRenderCompact = Boolean(model?.compactRecommended && options.onCompact);
|
||||
const compactDisabled = options.compactDisabled === true || options.compactBusy === true;
|
||||
const summary = model
|
||||
? t("chat.composer.contextUsage.summary", {
|
||||
used: `${model.approximate ? "~" : ""}${formatCompactTokenCount(model.used)}`,
|
||||
@@ -385,135 +376,92 @@ export function renderContextNotice(
|
||||
class="context-usage"
|
||||
style=${model ? `--ctx-color:${model.color};--ctx-bg:${model.bg}` : ""}
|
||||
>
|
||||
${canRenderCompact
|
||||
? html`<button
|
||||
class="context-ring__compact ${options.compactBusy
|
||||
? "context-ring__compact--busy"
|
||||
: ""}"
|
||||
type="button"
|
||||
aria-label=${`${summary}. ${t("chat.composer.compactRecommendedContext")}`}
|
||||
title=${summary}
|
||||
?disabled=${compactDisabled}
|
||||
@click=${() => {
|
||||
if (!compactDisabled) {
|
||||
void options.onCompact?.();
|
||||
}
|
||||
}}
|
||||
<details @toggle=${handleChatComposerDetailsToggle}>
|
||||
<summary
|
||||
class="context-ring ${model?.warning ? "context-ring--warning" : ""}"
|
||||
aria-label=${summary}
|
||||
title=${t("chat.composer.contextUsage.open")}
|
||||
>
|
||||
<svg
|
||||
class="context-ring__dial"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
height="16"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg
|
||||
class="context-ring__dial"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
height="16"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<circle class="context-ring__track" cx="8" cy="8" r=${RING_RADIUS} />
|
||||
<circle
|
||||
class="context-ring__fill"
|
||||
cx="8"
|
||||
cy="8"
|
||||
r=${RING_RADIUS}
|
||||
stroke-dasharray=${RING_CIRCUMFERENCE.toFixed(2)}
|
||||
stroke-dashoffset=${dashOffset.toFixed(2)}
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
>${options.compactBusy
|
||||
? t("chat.composer.compacting")
|
||||
: t("chat.composer.compact")}</span
|
||||
>
|
||||
</button>`
|
||||
: html`<details @toggle=${handleChatComposerDetailsToggle}>
|
||||
<summary
|
||||
class="context-ring ${model?.warning ? "context-ring--warning" : ""}"
|
||||
aria-label=${summary}
|
||||
title=${t("chat.composer.contextUsage.open")}
|
||||
>
|
||||
<svg
|
||||
class="context-ring__dial"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
height="16"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<circle class="context-ring__track" cx="8" cy="8" r=${RING_RADIUS} />
|
||||
<circle
|
||||
class="context-ring__fill"
|
||||
cx="8"
|
||||
cy="8"
|
||||
r=${RING_RADIUS}
|
||||
stroke-dasharray=${RING_CIRCUMFERENCE.toFixed(2)}
|
||||
stroke-dashoffset=${dashOffset.toFixed(2)}
|
||||
/>
|
||||
</svg>
|
||||
</summary>
|
||||
<section
|
||||
class="context-usage__popover"
|
||||
aria-label=${t("chat.composer.contextUsage.title")}
|
||||
>
|
||||
${model
|
||||
? html`
|
||||
<div class="context-usage__header">
|
||||
<span class="context-usage__title"
|
||||
>${t("chat.composer.contextUsage.contextWindow")}</span
|
||||
>
|
||||
<strong class="context-usage__context-value"
|
||||
>${model.detail} · ${percentage}</strong
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="context-usage__bar"
|
||||
role="progressbar"
|
||||
aria-label=${summary}
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
aria-valuenow=${model.pct}
|
||||
>
|
||||
<span style="width: ${model.pct}%"></span>
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
${model
|
||||
? html`
|
||||
<div class="context-usage__section-label">
|
||||
${t("chat.composer.contextUsage.latestRunTokens")}
|
||||
</div>
|
||||
<dl class="context-usage__stats">
|
||||
<div>
|
||||
<dt>${t("usage.breakdown.input")}</dt>
|
||||
<dd>${formatStat(model.input)}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>${t("usage.breakdown.output")}</dt>
|
||||
<dd>${formatStat(model.output)}</dd>
|
||||
</div>
|
||||
${!showCosts || model.cost === null
|
||||
? nothing
|
||||
: html`
|
||||
<div>
|
||||
<dt>${t("chat.composer.contextUsage.estimatedCost")}</dt>
|
||||
<dd>${formatCost(model.cost)}</dd>
|
||||
</div>
|
||||
`}
|
||||
</dl>
|
||||
`
|
||||
: nothing}
|
||||
${showCosts && providerCosts && hasProviderCosts
|
||||
? html`
|
||||
<div class="context-usage__section-label">
|
||||
${t("usage.breakdown.costByType")}
|
||||
</div>
|
||||
<dl class="context-usage__stats">
|
||||
${renderCostStat(t("usage.breakdown.input"), providerCosts.input)}
|
||||
${renderCostStat(t("usage.breakdown.output"), providerCosts.output)}
|
||||
${renderCostStat(t("usage.breakdown.cacheRead"), providerCosts.cacheRead)}
|
||||
${renderCostStat(t("usage.breakdown.cacheWrite"), providerCosts.cacheWrite)}
|
||||
</dl>
|
||||
`
|
||||
: nothing}
|
||||
${planGroups.map((group) => renderQuotaGroup(group, usageHref))}
|
||||
</section>
|
||||
</details>`}
|
||||
<circle class="context-ring__track" cx="8" cy="8" r=${RING_RADIUS} />
|
||||
<circle
|
||||
class="context-ring__fill"
|
||||
cx="8"
|
||||
cy="8"
|
||||
r=${RING_RADIUS}
|
||||
stroke-dasharray=${RING_CIRCUMFERENCE.toFixed(2)}
|
||||
stroke-dashoffset=${dashOffset.toFixed(2)}
|
||||
/>
|
||||
</svg>
|
||||
</summary>
|
||||
<section class="context-usage__popover" aria-label=${t("chat.composer.contextUsage.title")}>
|
||||
${model
|
||||
? html`
|
||||
<div class="context-usage__header">
|
||||
<span class="context-usage__title"
|
||||
>${t("chat.composer.contextUsage.contextWindow")}</span
|
||||
>
|
||||
<strong class="context-usage__context-value"
|
||||
>${model.detail} · ${percentage}</strong
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="context-usage__bar"
|
||||
role="progressbar"
|
||||
aria-label=${summary}
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
aria-valuenow=${model.pct}
|
||||
>
|
||||
<span style="width: ${model.pct}%"></span>
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
${model
|
||||
? html`
|
||||
<div class="context-usage__section-label">
|
||||
${t("chat.composer.contextUsage.latestRunTokens")}
|
||||
</div>
|
||||
<dl class="context-usage__stats">
|
||||
<div>
|
||||
<dt>${t("usage.breakdown.input")}</dt>
|
||||
<dd>${formatStat(model.input)}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>${t("usage.breakdown.output")}</dt>
|
||||
<dd>${formatStat(model.output)}</dd>
|
||||
</div>
|
||||
${!showCosts || model.cost === null
|
||||
? nothing
|
||||
: html`
|
||||
<div>
|
||||
<dt>${t("chat.composer.contextUsage.estimatedCost")}</dt>
|
||||
<dd>${formatCost(model.cost)}</dd>
|
||||
</div>
|
||||
`}
|
||||
</dl>
|
||||
`
|
||||
: nothing}
|
||||
${showCosts && providerCosts && hasProviderCosts
|
||||
? html`
|
||||
<div class="context-usage__section-label">${t("usage.breakdown.costByType")}</div>
|
||||
<dl class="context-usage__stats">
|
||||
${renderCostStat(t("usage.breakdown.input"), providerCosts.input)}
|
||||
${renderCostStat(t("usage.breakdown.output"), providerCosts.output)}
|
||||
${renderCostStat(t("usage.breakdown.cacheRead"), providerCosts.cacheRead)}
|
||||
${renderCostStat(t("usage.breakdown.cacheWrite"), providerCosts.cacheWrite)}
|
||||
</dl>
|
||||
`
|
||||
: nothing}
|
||||
${planGroups.map((group) => renderQuotaGroup(group, usageHref))}
|
||||
</section>
|
||||
</details>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -124,7 +124,6 @@ export type ChatComposerProps = ChatAttachmentControlsProps & {
|
||||
onSlashIntent?: () => void | Promise<void>;
|
||||
onSlashCommand?: (command: string) => void;
|
||||
onSend: (followUpModeOverride?: "steer", submissionAction?: Event) => void;
|
||||
onCompact?: () => void | Promise<void>;
|
||||
onToggleRealtimeTalk?: () => void;
|
||||
onToggleRealtimeCamera?: () => void;
|
||||
onSwitchRealtimeCamera?: () => void;
|
||||
|
||||
@@ -97,8 +97,6 @@ export function renderChatComposer(props: ChatComposerProps) {
|
||||
sendingForCurrentSession || showAbortableUi || Boolean(submittedProgress)
|
||||
? { phase: "in-progress" as const }
|
||||
: props.runStatus;
|
||||
const compactBusy =
|
||||
props.compactionStatus?.phase === "active" || props.compactionStatus?.phase === "retrying";
|
||||
const activeSession = props.sessions?.sessions?.find((row) =>
|
||||
areUiSessionKeysEquivalent(row.key, props.sessionKey),
|
||||
);
|
||||
@@ -142,10 +140,7 @@ export function renderChatComposer(props: ChatComposerProps) {
|
||||
activeSession,
|
||||
props.sessions?.defaults?.contextTokens ?? null,
|
||||
{
|
||||
compactBusy,
|
||||
compactDisabled: !props.connected || !canCompose || isBusy || showAbortableUi,
|
||||
messages: props.messages,
|
||||
onCompact: props.onCompact,
|
||||
providerUsage: props.providerUsage,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1132,46 +1132,6 @@ openclaw-chat-page {
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
.context-ring__compact {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
height: 24px;
|
||||
padding: 0 9px 0 5px;
|
||||
border-radius: var(--radius-full);
|
||||
border: 0;
|
||||
background: color-mix(in srgb, var(--warn) 10%, transparent);
|
||||
color: color-mix(in srgb, var(--warn) 88%, var(--text));
|
||||
font: inherit;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
cursor: var(--cursor-action);
|
||||
transition:
|
||||
background 150ms ease-out,
|
||||
color 150ms ease-out,
|
||||
opacity 150ms ease-out;
|
||||
}
|
||||
|
||||
.context-ring__compact:hover:not(:disabled) {
|
||||
background: color-mix(in srgb, var(--warn) 15%, transparent);
|
||||
color: color-mix(in srgb, var(--warn) 94%, var(--text-strong));
|
||||
}
|
||||
|
||||
.context-ring__compact:active:not(:disabled) {
|
||||
background: color-mix(in srgb, var(--warn) 19%, transparent);
|
||||
}
|
||||
|
||||
.context-ring__compact:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
.context-ring__compact--busy .context-ring__dial {
|
||||
animation: compaction-spin 1s linear infinite;
|
||||
}
|
||||
|
||||
/* Message images (sent images displayed in chat) */
|
||||
.chat-message-images {
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user