mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 20:35:39 -06:00
fix(ui): restore tool activity interaction polish (#125790)
* fix(ui): restore tool activity interaction polish * fix(ui): restore final tool activity visuals * fix(ui): keep running tool activity compact * fix(ui): preserve tool disclosure state ownership * fix(ui): preserve disclosure scroll after resize --------- Co-authored-by: vyctorbrzezowski <krzyszchweski@gmail.com> Co-authored-by: RoboClaw <309084314+roboclaw-bot@users.noreply.github.com>
This commit is contained in:
@@ -161,6 +161,14 @@ export const toolIcons = {
|
||||
<path d="M6 21V9a9 9 0 0 0 9 9" />`),
|
||||
terminal: strokeIcon(svg` <polyline points="4 17 10 11 4 5" />
|
||||
<line x1="12" x2="20" y1="19" y2="19" />`),
|
||||
squareTerminal: strokeIcon(svg` <path d="m7 11 2-2-2-2" />
|
||||
<path d="M11 13h4" />
|
||||
<rect width="18" height="18" x="3" y="3" rx="2" ry="2" />`),
|
||||
listTree: strokeIcon(svg` <path d="M21 12h-8" />
|
||||
<path d="M21 6H8" />
|
||||
<path d="M21 18h-8" />
|
||||
<path d="M3 6v4c0 1.1.9 2 2 2h3" />
|
||||
<path d="M3 10v6c0 1.1.9 2 2 2h3" />`),
|
||||
claw: strokeIcon(svg` <path
|
||||
d="M8.2 10 A5.2 5.2 0 1 0 8.2 20.4 A5.2 5.2 0 0 0 8.2 10 Z M10.2 20 C14.5 20.8 19 18.6 22.3 13.2 C21 12.9 19.7 12.7 18.4 12.8 L17.5 14.6 L16 12.9 L14.3 14.5 L13.5 13 L11.5 14.2 Z"
|
||||
/>
|
||||
|
||||
@@ -3,6 +3,7 @@ import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { expect, it } from "vitest";
|
||||
import { controlUiSessionUrl, installMockGateway } from "../test-helpers/control-ui-e2e.ts";
|
||||
import { chatThreadDistanceFromBottom, waitForChatScrollIdle } from "./chat-flow.test-support.ts";
|
||||
import { createControlUiE2eSuite } from "./control-ui-e2e-suite.test-support.ts";
|
||||
|
||||
const suite = createControlUiE2eSuite({
|
||||
@@ -60,6 +61,84 @@ async function expandCompletedWorkGroups(page: import("playwright").Page) {
|
||||
}
|
||||
|
||||
suite.define(() => {
|
||||
it("keeps the final activity row anchored while its disclosure opens", async () => {
|
||||
const context = await suite.browser.newContext({ viewport: { height: 600, width: 900 } });
|
||||
const page = await context.newPage();
|
||||
const transcriptPrefix = Array.from({ length: 12 }, (_, index) => [
|
||||
{
|
||||
role: "user",
|
||||
content: `Earlier prompt ${index + 1}: keep enough transcript above the active row to make the pane scroll.`,
|
||||
timestamp: index * 2 + 1,
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: `Earlier response ${index + 1}.` }],
|
||||
timestamp: index * 2 + 2,
|
||||
},
|
||||
]).flat();
|
||||
await installMockGateway(page, {
|
||||
historyMessages: [
|
||||
...transcriptPrefix,
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
type: "toolCall",
|
||||
id: "call-anchor",
|
||||
name: "bash",
|
||||
arguments: { command: "pnpm test ui/src/pages/chat" },
|
||||
},
|
||||
{
|
||||
type: "toolCall",
|
||||
id: "call-anchor-read",
|
||||
name: "read",
|
||||
arguments: { path: "ui/src/pages/chat/components/chat-tool-cards.ts" },
|
||||
},
|
||||
],
|
||||
timestamp: 100,
|
||||
},
|
||||
{
|
||||
role: "toolResult",
|
||||
toolCallId: "call-anchor",
|
||||
toolName: "bash",
|
||||
content: [{ type: "text", text: "All focused tests passed." }],
|
||||
timestamp: 101,
|
||||
},
|
||||
{
|
||||
role: "toolResult",
|
||||
toolCallId: "call-anchor-read",
|
||||
toolName: "read",
|
||||
content: [{ type: "text", text: "export function renderToolCard() {}" }],
|
||||
timestamp: 102,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await page.goto(`${suite.server.baseUrl}chat`);
|
||||
const activity = page.locator(".chat-group--activity .chat-activity-group__summary");
|
||||
await activity.waitFor();
|
||||
await waitForChatScrollIdle(page);
|
||||
expect(Math.abs(await chatThreadDistanceFromBottom(page))).toBeLessThanOrEqual(2);
|
||||
const virtualRow = page.locator(".chat-virtual-row").filter({ has: activity });
|
||||
const rowTop = async () =>
|
||||
virtualRow.evaluate((row) => {
|
||||
const thread = row.closest<HTMLElement>(".chat-thread");
|
||||
if (!thread) {
|
||||
throw new Error("Expected activity row inside the chat thread");
|
||||
}
|
||||
return row.getBoundingClientRect().top - thread.getBoundingClientRect().top;
|
||||
});
|
||||
const topBefore = await rowTop();
|
||||
|
||||
await activity.click();
|
||||
await page.locator(".chat-activity-group__body:not([hidden])").waitFor();
|
||||
await waitForChatScrollIdle(page);
|
||||
|
||||
expect(Math.abs((await rowTop()) - topBefore)).toBeLessThanOrEqual(2);
|
||||
await captureToolActivityProof(page, "activity-disclosure-scroll-anchor");
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it("keeps an earlier autonomous failure visible after a later turn recovers", async () => {
|
||||
const context = await suite.browser.newContext({ viewport: { height: 800, width: 1200 } });
|
||||
const page = await context.newPage();
|
||||
|
||||
@@ -202,7 +202,7 @@ function activityAlignmentHtml() {
|
||||
return `
|
||||
<div class="chat-thread" role="log">
|
||||
<div class="chat-thread-inner">
|
||||
<div class="chat-group tool chat-group--activity">
|
||||
<div class="chat-group tool chat-group--activity chat-group--with-footer">
|
||||
<div class="chat-group-messages">
|
||||
<div class="chat-activity-group is-open">
|
||||
<button class="chat-inline-disclosure chat-activity-group__summary" type="button" aria-expanded="true">
|
||||
@@ -1197,8 +1197,10 @@ describeBrowserLayout.concurrent("chat responsive browser layout", () => {
|
||||
const activityGroup = await getRect(page, ".chat-activity-group");
|
||||
const activitySummary = await getRect(page, ".chat-activity-group__summary");
|
||||
const failedSummary = await getRect(page, "[data-failed-call-row]");
|
||||
const thread = await getRect(page, ".chat-thread-inner");
|
||||
expect(activitySummary.width).toBeLessThan(activityGroup.width);
|
||||
expect(failedSummary.width).toBeLessThan(activityGroup.width);
|
||||
expect(activityGroup.left - thread.left).toBeCloseTo(51, 0);
|
||||
const styles = await page.evaluate(() => {
|
||||
const activity = document.querySelector<HTMLElement>(".chat-activity-group__summary")!;
|
||||
const label = activity.querySelector<HTMLElement>(".chat-activity-group__label")!;
|
||||
@@ -1604,6 +1606,32 @@ describeBrowserLayout.concurrent("chat responsive browser layout", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps tool-card header actions visible without hover", async () => {
|
||||
const page = await openBrowserPage(430, 720);
|
||||
try {
|
||||
await page.setContent(
|
||||
`<!doctype html><html><head><style>${readUiCss()}</style></head><body>
|
||||
<div class="chat-tool-card">
|
||||
<div class="chat-tool-card__header">
|
||||
<span>ui/src/styles/chat/tool-cards.css</span>
|
||||
<div class="chat-tool-card__actions">
|
||||
<button class="chat-tool-card__action-btn" type="button">${iconSvg()}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body></html>`,
|
||||
);
|
||||
|
||||
expect(
|
||||
await page
|
||||
.locator(".chat-tool-card__header > .chat-tool-card__actions")
|
||||
.evaluate((node) => getComputedStyle(node).opacity),
|
||||
).toBe("1");
|
||||
} finally {
|
||||
await closeBrowserPage(page);
|
||||
}
|
||||
});
|
||||
|
||||
it(
|
||||
"remeasures a populated composer when the viewport width changes",
|
||||
FULL_APP_TEST_OPTIONS,
|
||||
|
||||
@@ -65,10 +65,10 @@ import {
|
||||
renderToolCard,
|
||||
renderToolOutcome,
|
||||
renderToolPreview,
|
||||
isRunningToolCard,
|
||||
resolveCollapsedToolDetail,
|
||||
shouldToggleSelectableDisclosure,
|
||||
syncToolDisclosureOverflow,
|
||||
toggleToolDisclosureKeepingScroll,
|
||||
} from "./chat-tool-cards.ts";
|
||||
|
||||
function renderChatIcon(name: string) {
|
||||
@@ -84,7 +84,7 @@ function renderInlineToolCards(
|
||||
onOpenSidebar?: (content: SidebarContent) => void;
|
||||
onOpenWorkspaceFile?: (target: { path: string; line?: number | null }) => void;
|
||||
isToolExpanded?: (toolCardId: string) => boolean;
|
||||
onToggleToolExpanded?: (toolCardId: string) => void;
|
||||
onToggleToolExpanded?: (toolCardId: string, expanded?: boolean) => void;
|
||||
runActive?: boolean;
|
||||
canvasPluginSurfaceUrl?: string | null;
|
||||
embedSandboxMode?: EmbedSandboxMode;
|
||||
@@ -93,12 +93,14 @@ function renderInlineToolCards(
|
||||
) {
|
||||
return html`
|
||||
<div class="chat-tools-inline">
|
||||
${toolCards.map((card, index) =>
|
||||
renderToolCard(card, {
|
||||
expanded: opts.isToolExpanded?.(`${opts.messageKey}:toolcard:${index}`) ?? false,
|
||||
${toolCards.map((card, index) => {
|
||||
const disclosureId = `${opts.messageKey}:toolcard:${index}`;
|
||||
const expanded = opts.isToolExpanded?.(disclosureId) ?? false;
|
||||
return renderToolCard(card, {
|
||||
expanded,
|
||||
runActive: opts.runActive,
|
||||
onToggleExpanded: opts.onToggleToolExpanded
|
||||
? () => opts.onToggleToolExpanded?.(`${opts.messageKey}:toolcard:${index}`)
|
||||
? () => opts.onToggleToolExpanded?.(disclosureId, expanded)
|
||||
: () => undefined,
|
||||
sessionKey: opts.sessionKey,
|
||||
agentId: opts.agentId,
|
||||
@@ -107,8 +109,8 @@ function renderInlineToolCards(
|
||||
canvasPluginSurfaceUrl: opts.canvasPluginSurfaceUrl,
|
||||
embedSandboxMode: opts.embedSandboxMode ?? "scripts",
|
||||
allowExternalEmbedUrls: opts.allowExternalEmbedUrls ?? false,
|
||||
}),
|
||||
)}
|
||||
});
|
||||
})}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -228,7 +230,7 @@ export function renderGroupedMessage(
|
||||
assistantMessageDisclosure?: AssistantMessageDisclosure;
|
||||
actionMarkdown?: string;
|
||||
isToolExpanded?: (toolCardId: string) => boolean;
|
||||
onToggleToolExpanded?: (toolCardId: string) => void;
|
||||
onToggleToolExpanded?: (toolCardId: string, expanded?: boolean) => void;
|
||||
onRequestUpdate?: () => void;
|
||||
canvasPluginSurfaceUrl?: string | null;
|
||||
basePath?: string;
|
||||
@@ -330,9 +332,7 @@ export function renderGroupedMessage(
|
||||
}
|
||||
|
||||
const toolMessageDisclosureId = `toolmsg:${messageKey}`;
|
||||
const toolMessageExpanded =
|
||||
toolCards.some((card) => isRunningToolCard(card, opts.runActive)) ||
|
||||
(opts.isToolMessageExpanded?.(toolMessageDisclosureId) ?? false);
|
||||
const toolMessageExpanded = opts.isToolMessageExpanded?.(toolMessageDisclosureId) ?? false;
|
||||
const toolNames = [...new Set(toolCards.map((c) => c.name))];
|
||||
const singleToolCard = toolCards.length === 1 ? toolCards[0] : null;
|
||||
// One expanded card already closes with its own outcome line; every other
|
||||
@@ -488,7 +488,12 @@ export function renderGroupedMessage(
|
||||
@focus=${syncToolDisclosureOverflow}
|
||||
@click=${(event: MouseEvent) => {
|
||||
if (shouldToggleSelectableDisclosure(event)) {
|
||||
opts.onToggleToolMessageExpanded?.(toolMessageDisclosureId);
|
||||
toggleToolDisclosureKeepingScroll(event, () =>
|
||||
opts.onToggleToolMessageExpanded?.(
|
||||
toolMessageDisclosureId,
|
||||
toolMessageExpanded,
|
||||
),
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -44,6 +44,7 @@ import {
|
||||
resolveToolRowText,
|
||||
shouldToggleSelectableDisclosure,
|
||||
syncToolDisclosureOverflow,
|
||||
toggleToolDisclosureKeepingScroll,
|
||||
} from "./chat-tool-cards.ts";
|
||||
import { renderTurnRecapRow } from "./chat-working-indicator.ts";
|
||||
|
||||
@@ -72,7 +73,7 @@ type RenderMessageGroupOptions = {
|
||||
getAssistantMessageExpansion?: (messageId: string) => AssistantMessageExpansionState | undefined;
|
||||
onToggleAssistantMessageExpanded?: (messageId: string) => void;
|
||||
isToolExpanded?: (toolCardId: string) => boolean;
|
||||
onToggleToolExpanded?: (toolCardId: string) => void;
|
||||
onToggleToolExpanded?: (toolCardId: string, expanded?: boolean) => void;
|
||||
onRequestUpdate?: () => void;
|
||||
onAssistantAttachmentLoaded?: () => void;
|
||||
onRequestOpenImage?: () => number;
|
||||
@@ -247,8 +248,7 @@ export function renderActivityGroup(
|
||||
: summarizeToolGroup(cards.map((card) => ({ name: card.name, args: card.args })));
|
||||
const activityDisclosureId = `activity:${firstGroup.key}`;
|
||||
const activityBodyId = `activity-body-${fnv1aUtf16(firstGroup.key).toString(16)}`;
|
||||
const activityExpanded =
|
||||
Boolean(runningCard) || (opts.isToolMessageExpanded?.(activityDisclosureId) ?? false);
|
||||
const activityExpanded = opts.isToolMessageExpanded?.(activityDisclosureId) ?? false;
|
||||
return html`
|
||||
<div
|
||||
class="chat-group tool chat-group--activity chat-group--with-footer"
|
||||
@@ -265,11 +265,13 @@ export function renderActivityGroup(
|
||||
@focus=${syncToolDisclosureOverflow}
|
||||
@click=${(event: MouseEvent) => {
|
||||
if (shouldToggleSelectableDisclosure(event)) {
|
||||
opts.onToggleToolMessageExpanded?.(activityDisclosureId, activityExpanded);
|
||||
toggleToolDisclosureKeepingScroll(event, () =>
|
||||
opts.onToggleToolMessageExpanded?.(activityDisclosureId, activityExpanded),
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span class="chat-activity-group__icon">${icons.activity}</span>
|
||||
<span class="chat-activity-group__icon">${icons.listTree}</span>
|
||||
<span class="chat-tool-disclosure__content">
|
||||
<span class="chat-activity-group__label" title=${groupSummaryLabel}
|
||||
>${groupSummaryLabel}</span
|
||||
|
||||
@@ -11,7 +11,11 @@ import { renderGroupedMessage } from "./chat-message-bubble.ts";
|
||||
import { renderChatTimestamp } from "./chat-message-timestamp.ts";
|
||||
import { renderChatQuestionSummary } from "./chat-question-card.ts";
|
||||
import type { SidebarContent } from "./chat-sidebar.ts";
|
||||
import { shouldToggleSelectableDisclosure, syncToolDisclosureOverflow } from "./chat-tool-cards.ts";
|
||||
import {
|
||||
shouldToggleSelectableDisclosure,
|
||||
syncToolDisclosureOverflow,
|
||||
toggleToolDisclosureKeepingScroll,
|
||||
} from "./chat-tool-cards.ts";
|
||||
import { renderChatWorkingIndicator } from "./chat-working-indicator.ts";
|
||||
|
||||
/** A contiguous run of in-flight streaming items rendered under one assistant group. */
|
||||
@@ -166,7 +170,7 @@ export function renderWorkGroupSummary(
|
||||
@focus=${syncToolDisclosureOverflow}
|
||||
@click=${(event: MouseEvent) => {
|
||||
if (shouldToggleSelectableDisclosure(event)) {
|
||||
opts.onToggle();
|
||||
toggleToolDisclosureKeepingScroll(event, opts.onToggle);
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -2428,8 +2428,8 @@ describe("grouped chat rendering", () => {
|
||||
".chat-activity-group__summary",
|
||||
HTMLButtonElement,
|
||||
);
|
||||
expect(container.querySelector(".chat-activity-group.is-open")).not.toBeNull();
|
||||
expect(activitySummary.getAttribute("aria-expanded")).toBe("true");
|
||||
expect(container.querySelector(".chat-activity-group.is-open")).toBeNull();
|
||||
expect(activitySummary.getAttribute("aria-expanded")).toBe("false");
|
||||
expect(activitySummary.getAttribute("aria-label")).toBeNull();
|
||||
expect(activitySummary.classList.contains("chat-activity-group__summary--error")).toBe(false);
|
||||
expect(container.querySelector(".chat-activity-group__label")?.textContent).toBe(
|
||||
|
||||
@@ -134,6 +134,27 @@ describe("tool-cards", () => {
|
||||
container.remove();
|
||||
});
|
||||
|
||||
it("keeps a running card closed by default", () => {
|
||||
const container = document.createElement("div");
|
||||
render(
|
||||
renderToolCard(
|
||||
{
|
||||
id: "msg:running",
|
||||
name: "bash",
|
||||
args: { command: "pnpm test" },
|
||||
live: true,
|
||||
},
|
||||
{ expanded: false, runActive: true, onToggleExpanded: vi.fn() },
|
||||
),
|
||||
container,
|
||||
);
|
||||
|
||||
expect(container.querySelector(".chat-tool-msg-body")).toBeNull();
|
||||
expect(container.querySelector(".chat-tool-msg-summary")?.getAttribute("aria-expanded")).toBe(
|
||||
"false",
|
||||
);
|
||||
});
|
||||
|
||||
it("renders expanded cards with key-value args and an output section", () => {
|
||||
const container = document.createElement("div");
|
||||
const toggle = vi.fn();
|
||||
|
||||
@@ -288,9 +288,9 @@ function resolveToolRowVerb(view: ToolCallView, outcome: ToolCardOutcome): strin
|
||||
}
|
||||
|
||||
const TOOL_ROW_ICONS: Partial<Record<ToolCallView["kind"], string>> = {
|
||||
command: "terminal",
|
||||
command: "squareTerminal",
|
||||
read: "fileText",
|
||||
edit: "penLine",
|
||||
edit: "pencil",
|
||||
write: "fileCode",
|
||||
search: "search",
|
||||
fetch: "globe",
|
||||
@@ -319,6 +319,26 @@ export function syncToolDisclosureOverflow(event: Event): void {
|
||||
);
|
||||
}
|
||||
|
||||
export function toggleToolDisclosureKeepingScroll(event: Event, toggle: () => void): void {
|
||||
const target = event.currentTarget;
|
||||
const row = target instanceof Element ? target.closest<HTMLElement>(".chat-virtual-row") : null;
|
||||
const scroller = row?.closest<HTMLElement>(".chat-thread");
|
||||
const rowTop = row?.getBoundingClientRect().top;
|
||||
toggle();
|
||||
if (!row || !scroller || rowTop === undefined) {
|
||||
return;
|
||||
}
|
||||
requestAnimationFrame(() => {
|
||||
// ResizeObserver runs after rAF. Wait one more frame so TanStack applies
|
||||
// its end-anchor delta before we restore this disclosure row's position.
|
||||
requestAnimationFrame(() => {
|
||||
if (row.isConnected) {
|
||||
scroller.scrollTop += row.getBoundingClientRect().top - rowTop;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function renderToolRowContent(card: ToolCard, view: ToolCallView, outcome: ToolCardOutcome) {
|
||||
if (view.kind === "command" && view.command) {
|
||||
const commandPreview = firstCommandLine(view.command);
|
||||
@@ -771,7 +791,7 @@ export function renderToolCard(
|
||||
const view = resolveToolCallView({ name: card.name, args: card.args, details: card.details });
|
||||
const display = resolveToolDisplay({ name: card.name, args: card.args, detailMode: "explain" });
|
||||
const isRunning = outcome === "running";
|
||||
const expanded = opts.expanded || isRunning;
|
||||
const expanded = opts.expanded;
|
||||
const icon = TOOL_ROW_ICONS[view.kind] ?? display.icon;
|
||||
const workspaceFilePath =
|
||||
view.kind === "read" || view.kind === "edit" || view.kind === "write"
|
||||
@@ -794,7 +814,8 @@ export function renderToolCard(
|
||||
type="button"
|
||||
aria-expanded=${String(expanded)}
|
||||
aria-label=${resolveToolRowText(card, opts.runActive)}
|
||||
@click=${() => opts.onToggleExpanded(card.id)}
|
||||
@click=${(event: MouseEvent) =>
|
||||
toggleToolDisclosureKeepingScroll(event, () => opts.onToggleExpanded(card.id))}
|
||||
></button>
|
||||
<span class="chat-tool-msg-summary__icon">${renderToolIcon(icon)}</span>
|
||||
<span class="chat-tool-disclosure__content"
|
||||
@@ -818,7 +839,7 @@ export function renderToolCard(
|
||||
@focus=${syncToolDisclosureOverflow}
|
||||
@click=${(event: MouseEvent) => {
|
||||
if (shouldToggleSelectableDisclosure(event)) {
|
||||
opts.onToggleExpanded(card.id);
|
||||
toggleToolDisclosureKeepingScroll(event, () => opts.onToggleExpanded(card.id));
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -238,8 +238,12 @@ export function projectChatTranscript(
|
||||
const questionPrompts = new Map(
|
||||
(props.questionPrompts ?? []).map((prompt) => [prompt.id, prompt]),
|
||||
);
|
||||
const toggleToolCardExpanded = (toolCardId: string) => {
|
||||
setExpansionState(expandedToolCards, toolCardId, !expandedToolCards.get(toolCardId));
|
||||
const toggleToolCardExpanded = (toolCardId: string, expanded?: boolean) => {
|
||||
setExpansionState(
|
||||
expandedToolCards,
|
||||
toolCardId,
|
||||
!(expanded ?? expandedToolCards.get(toolCardId) ?? false),
|
||||
);
|
||||
requestUpdate();
|
||||
};
|
||||
const toggleAssistantMessageExpanded = (messageId: string) => {
|
||||
|
||||
@@ -110,8 +110,8 @@
|
||||
.chat-group.tool {
|
||||
--chat-message-column-max: var(--chat-message-max-width, min(980px, 100%));
|
||||
display: block;
|
||||
margin-inline-start: 0;
|
||||
padding-inline-start: 0;
|
||||
margin-inline-start: 4px;
|
||||
padding-inline-start: 46px;
|
||||
}
|
||||
|
||||
.chat-group.tool.chat-group--with-footer {
|
||||
@@ -120,6 +120,7 @@
|
||||
|
||||
.chat-group.tool.chat-group--with-footer > .chat-group-messages {
|
||||
max-width: var(--chat-message-column-max);
|
||||
margin-inline-start: 1px;
|
||||
}
|
||||
|
||||
.chat-group.user .chat-group-footer {
|
||||
@@ -678,6 +679,11 @@ img.chat-avatar.chat-avatar--logo {
|
||||
--chat-message-column-max: var(--chat-message-max-width, min(980px, 100%));
|
||||
}
|
||||
|
||||
.chat-thread--direct .chat-group.tool {
|
||||
margin-inline-start: 0;
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
|
||||
.chat-duplicate-count {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -656,12 +656,6 @@
|
||||
}
|
||||
|
||||
.chat-tool-card__header > .chat-tool-card__actions {
|
||||
opacity: 0;
|
||||
transition: opacity 120ms ease-out;
|
||||
}
|
||||
|
||||
.chat-tool-card__header:hover > .chat-tool-card__actions,
|
||||
.chat-tool-card__header:focus-within > .chat-tool-card__actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@@ -677,8 +671,7 @@
|
||||
}
|
||||
|
||||
@media (pointer: coarse) {
|
||||
.chat-tool-card--flush > .chat-tool-card__actions,
|
||||
.chat-tool-card__header > .chat-tool-card__actions {
|
||||
.chat-tool-card--flush > .chat-tool-card__actions {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user