Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
7c96096
feat(tauri): add macOS DMG configuration and background image
Loosand Aug 19, 2026
20811b8
Merge branch 'Stack-Cairn:main' into main
Loosand Aug 20, 2026
162bbee
feat(macos): Enhance DMG build process with new settings and verifica…
Loosand Aug 20, 2026
0ffabf3
Merge branch 'Stack-Cairn:main' into main
Loosand Aug 21, 2026
cb40e24
Merge branch 'main' of https://github.com/Loosand/LiveAgent
Loosand Aug 25, 2026
beea7a1
Add chat UI gallery
Loosand Aug 25, 2026
0819900
Add new chat UI components: ApprovalCard, CodeBlock, ContextCards, Di…
Loosand Aug 25, 2026
6879600
Refactor chat gallery components and styles for improved layout and f…
Loosand Aug 25, 2026
33ee1a5
Refine chat markdown typography
Loosand Aug 25, 2026
c9c73b4
Remove conversation loading scan animation
Loosand Aug 25, 2026
47cf47f
Compact assistant message metadata
Loosand Aug 25, 2026
0a799ec
Match Tessera reasoning and search UI
Loosand Aug 25, 2026
c9111f9
Merge branch 'Stack-Cairn:main' into codex/chat-ui-gallery
Loosand Aug 25, 2026
976ee67
feat(chat): add TaskRows component for task progress visualization
Loosand Aug 25, 2026
290796e
fix(tests): update assertions in AskUserQuestionCard and AssistantSta…
Loosand Aug 25, 2026
f11be26
feat(chat): enhance ToolCallItem and ToolTraceGroup components with c…
Loosand Aug 25, 2026
79df9ea
refactor(chat): improve ToolCallItem result display logic
Loosand Aug 25, 2026
f1620e0
Merge branch 'main' of https://github.com/Loosand/LiveAgent
Loosand Aug 26, 2026
4b073a5
Merge branch 'main' of https://github.com/Loosand/LiveAgent
Loosand Aug 26, 2026
87c5ec3
Merge branch 'main' into codex/chat-ui-gallery
Loosand Aug 26, 2026
529fe80
refactor(chat): streamline GatewayTranscript component and enhance st…
Loosand Aug 26, 2026
9daea95
refactor(chat): enhance interaction handling in AssistantWorkTrace an…
Loosand Aug 26, 2026
09854f8
test(chat): add tests for activity disclosure state and mention popup…
Loosand Aug 26, 2026
d52120f
refactor(chat): enhance TaskProgressIndicator with panel toggle funct…
Loosand Aug 26, 2026
9a588e9
Merge branch 'codex/chat-ui-gallery' of https://github.com/Loosand/Li…
Loosand Aug 27, 2026
8c9b3c7
feat(chat): introduce Tool Category Gallery and enhance chat gallery …
Loosand Aug 27, 2026
51c1566
feat(chat): enhance chat component styles and typography
Loosand Aug 27, 2026
c100b8a
feat(chat): refactor AssistantBubble and enhance tool interaction han…
Loosand Aug 28, 2026
43e73de
Merge branch 'main' into codex/chat-ui-gallery
Loosand Aug 31, 2026
cc1bafe
feat(chat): add collapseAfterAnswer prop to AssistantWorkTrace for au…
Loosand Aug 31, 2026
331c247
remove(chat-gallery): delete chat gallery files and related configura…
Loosand Aug 31, 2026
3792804
refactor(chat): enhance thinking activity integration and UI updates
Loosand Aug 31, 2026
c818949
delete(chatui): remove obsolete chat UI components
Loosand Sep 1, 2026
87f1814
feat(chat): enhance assistant bubble utilities and UI components
Loosand Sep 1, 2026
21445e3
refactor(chat): update chat components and tests for improved functio…
Loosand Sep 1, 2026
2be2c98
feat(chat): enhance user interaction handling and visual feedback
Loosand Sep 1, 2026
66c51e2
fix(tests): update assertions and styles for improved layout consistency
Loosand Sep 1, 2026
cd0ec26
feat(tests): add new assertions for layout consistency in chat compon…
Loosand Sep 1, 2026
78d5542
Merge remote-tracking branch 'origin/main' into codex/chat-ui-gallery
su-fen Sep 1, 2026
5d2e227
fix(merge): 恢复合并时被误回退的 customHeaders/providerUtils
su-fen Sep 1, 2026
d2ddf58
fix(ui): 圆角改用 --radius 派生的标准 rounded token
su-fen Sep 1, 2026
4a9a57d
fix(gui): 修复合并后的 biome 格式偏移并同步圆角测试断言
su-fen Sep 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/desktop-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,15 @@ jobs:
--password "$APPLE_APP_SPECIFIC_PASSWORD" \
--validate

- name: Install deterministic DMG builder
run: |
set -euo pipefail
python3 -m venv "$RUNNER_TEMP/dmgbuild-venv"
"$RUNNER_TEMP/dmgbuild-venv/bin/python" -m pip install \
--disable-pip-version-check \
"dmgbuild==1.6.5"
echo "$RUNNER_TEMP/dmgbuild-venv/bin" >> "$GITHUB_PATH"

- name: Build signed and notarized DMG
env:
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
Expand Down
16 changes: 16 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ DESKTOP_LINUX_BUNDLES ?= appimage deb rpm
DESKTOP_MACOS_APP_NAME ?= LiveAgent
DESKTOP_MACOS_NOTARY_PROFILE ?= liveagent-notary
DESKTOP_MACOS_TAURI_CONFIG ?= src-tauri/tauri.macos.conf.json
DESKTOP_MACOS_DMG_SETTINGS ?= scripts/release/macos-dmg-settings.py
DESKTOP_MACOS_DMG_BACKGROUND ?= $(AGENT_GUI_DIR)/src-tauri/dmg/background.png
DESKTOP_MACOS_DMG_VOLUME_ICON ?= $(AGENT_GUI_DIR)/src-tauri/icons/icon.icns
DESKTOP_MACOS_DMG_VERIFY ?= scripts/release/verify-macos-dmg.sh
DESKTOP_WINDOWS_TAURI_CONFIG ?= src-tauri/tauri.windows.conf.json
DESKTOP_RELEASE_TAURI_CONFIG ?= src-tauri/tauri.macos.release.conf.json
DESKTOP_RELEASE_TAURI_CONFIG_FLAGS ?= --config $(DESKTOP_RELEASE_TAURI_CONFIG) $(if $(LIVEAGENT_TAURI_VERSION_CONFIG),--config $(LIVEAGENT_TAURI_VERSION_CONFIG))
Expand Down Expand Up @@ -56,11 +60,23 @@ desktop-build-macos-release: check-rust-target-$(DESKTOP_MACOS_TARGET) check-mac
dmg_path="$$(find "target/$(DESKTOP_MACOS_TARGET)/release/bundle/dmg" -maxdepth 1 -name '$(DESKTOP_MACOS_APP_NAME)_*.dmg' -print -quit)"; \
if [ ! -d "$$app_path" ]; then echo "macOS app not found: $$app_path"; exit 1; fi; \
if [ -z "$$dmg_path" ] || [ ! -f "$$dmg_path" ]; then echo "macOS dmg not found under target/$(DESKTOP_MACOS_TARGET)/release/bundle/dmg"; exit 1; fi; \
if ! command -v dmgbuild >/dev/null 2>&1; then echo "dmgbuild is required. Install dmgbuild==1.6.5 before building a macOS release."; exit 1; fi; \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

范围外改动:macOS DMG 打包

这条 dmgbuild 依赖检查、下面的 dmgbuild 调用 / mv 替换 / verify-macos-dmg.sh,加上 scripts/release/ 两个新文件和 .github/workflows/desktop-release.yml 的安装步骤,跟"restyle chat ui"没有关系。

发布流水线的改动风险特征和前端样式完全不同(改坏了要等到下次 release 才发现),混在一个 89 文件的 PR 里既难 review 也难回滚。建议拆成独立 PR。

改动本身读下来是合理的:venv 装 dmgbuild==1.6.5 并加进 GITHUB_PATHtrap 清理临时 dmg、mv 之后再签名和公证,顺序正确。

codesign --verify --deep --strict --verbose=4 "$$app_path"; \
styled_dmg_path="$${dmg_path%.dmg}.styled.dmg"; \
trap 'rm -f "$$styled_dmg_path"' EXIT; \
dmgbuild \
-s "$(DESKTOP_MACOS_DMG_SETTINGS)" \
-D app="$$app_path" \
-D background="$(DESKTOP_MACOS_DMG_BACKGROUND)" \
-D volume_icon="$(DESKTOP_MACOS_DMG_VOLUME_ICON)" \
"$(DESKTOP_MACOS_APP_NAME)" "$$styled_dmg_path"; \
mv -f "$$styled_dmg_path" "$$dmg_path"; \
trap - EXIT; \
codesign --force --timestamp --sign "$(APPLE_SIGNING_IDENTITY)" "$$dmg_path"; \
xcrun notarytool submit "$$dmg_path" --keychain-profile "$(DESKTOP_MACOS_NOTARY_PROFILE)" --wait; \
xcrun stapler staple "$$dmg_path"; \
xcrun stapler validate -v "$$dmg_path"; \
bash "$(DESKTOP_MACOS_DMG_VERIFY)" "$$dmg_path"; \
spctl --assess --type execute --verbose=4 "$$app_path"; \
spctl --assess --type open --context context:primary-signature --verbose=4 "$$dmg_path"; \
echo "macOS release dmg is ready: $$dmg_path"
Expand Down
54 changes: 29 additions & 25 deletions crates/agent-gateway/web/src/components/GatewayTranscript.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import { UserAttachmentCards } from "@liveagent/ui/components/chat/UserAttachmentCards";
import { Loader2 } from "@liveagent/ui/components/IconSet";
import { useLocale } from "@liveagent/ui/i18n/LocaleContext";
import { normalizeLiveToolStatus, VIBING_STATUS } from "@liveagent/ui/lib/chat/assistantStatus";
import { normalizeLiveToolStatus } from "@liveagent/ui/lib/chat/assistantStatus";
import type { ChatFileLink } from "@liveagent/ui/lib/chat/chatFileLinks";
import type { ConversationMentionReference } from "@liveagent/ui/lib/chat/mentionReferences";
import {
Expand Down Expand Up @@ -160,15 +160,6 @@ function resolveNearestScrollViewport(element: HTMLElement | null) {
return element?.closest("[data-scroll-viewport]") as HTMLDivElement | null;
}

function LiveStatusFooter(props: { status: string; isCompaction?: boolean }) {
const { status, isCompaction = false } = props;
return (
<div className="gateway-live-status-footer ml-9 min-w-0 overflow-hidden pt-1">
<LiveAssistantStatus status={status} isCompaction={isCompaction} className="w-full" />
</div>
);
}

function HistoryLoadingState(props: { title?: string }) {
const title = props.title?.trim();
return (
Expand Down Expand Up @@ -345,8 +336,6 @@ const GatewayUserMessageRowBody = memo(function GatewayUserMessageRowBody(props:
);
});

// Retry actions render only for mounted assistant rows. Resolve their prompt
// locally instead of rebuilding an all-history map on every streamed token.
function findRetryTarget(rows: readonly TranscriptRow[], assistantIndex: number) {
for (let index = assistantIndex - 1; index >= 0; index -= 1) {
const row = rows[index];
Expand All @@ -355,14 +344,14 @@ function findRetryTarget(rows: readonly TranscriptRow[], assistantIndex: number)
return null;
}

// Shared assistant-row hover actions (copy / retry). Retry re-sends the
// nearest preceding user prompt through the edit-resend pipeline: this reply
// and everything after it are discarded, same as editing that prompt
// unchanged. Both transcript regions render it below the bubble.
// Compact assistant footer: primary reply actions stay one click away while
// detailed usage remains behind the info popover.
const GatewayAssistantMessageActions = memo(function GatewayAssistantMessageActions(props: {
row: Extract<TranscriptRow, { kind: "assistant" }>;
retryTarget: Extract<TranscriptRow, { kind: "user" }> | null;
isStreaming: boolean;
showUsage?: boolean;
usageContextWindow?: number;
isCopied: boolean;
setCopiedMessageId: Dispatch<SetStateAction<string | null>>;
onResendFromEdit?: (
Expand All @@ -378,6 +367,8 @@ const GatewayAssistantMessageActions = memo(function GatewayAssistantMessageActi
row,
retryTarget,
isStreaming,
showUsage,
usageContextWindow,
isCopied,
setCopiedMessageId,
onResendFromEdit,
Expand All @@ -389,6 +380,15 @@ const GatewayAssistantMessageActions = memo(function GatewayAssistantMessageActi
.map((round) => getRoundText(round).trim())
.filter((text) => text.length > 0)
.join("\n\n");
const usageEntries = useMemo(
() =>
showUsage
? row.rounds.flatMap((round) =>
round.meta?.usage ? [{ key: `round-${round.round}`, usage: round.meta.usage }] : [],
)
: undefined,
[row.rounds, showUsage],
);
const retryMessageRef = retryTarget?.messageRef;
const retryDisabled = isStreaming || !onResendFromEdit || !retryMessageRef;
const retryTitle = retryMessageRef
Expand All @@ -415,6 +415,8 @@ const GatewayAssistantMessageActions = memo(function GatewayAssistantMessageActi
}, 1500);
});
}}
usageEntries={usageEntries}
usageContextWindow={showUsage ? usageContextWindow : undefined}
retryDisabled={retryDisabled}
retryTitle={retryTitle}
onRetry={() => {
Expand Down Expand Up @@ -916,6 +918,11 @@ const GatewayTranscriptListRegion = memo(function GatewayTranscriptListRegion(pr
const rowIndex = virtualRow.index - leadingOffset;
const isLatestLiveAssistant = rowIndex === liveAssistantIndex;
const isLatestLiveStreaming = isStreaming && isLatestLiveAssistant;
const retryTarget = findRetryTarget(rows, rowIndex);
const durationMs =
row.timestamp !== undefined && retryTarget?.timestamp !== undefined
? Math.max(0, row.timestamp - retryTarget.timestamp)
: undefined;
return (
<article
key={virtualRow.key}
Expand All @@ -928,22 +935,17 @@ const GatewayTranscriptListRegion = memo(function GatewayTranscriptListRegion(pr
<div className="group/assistant min-w-0 w-full max-w-full space-y-1">
<AssistantBubble
rounds={row.rounds}
showUsage={showUsage}
usageContextWindow={usageContextWindow}
isLive={isLatestLiveAssistant}
isStreaming={isLatestLiveStreaming}
renderMode={rowRenderMode(row)}
toolStatus={isLatestLiveStreaming ? displayedToolStatus : null}
toolStatusVariant={displayedToolStatusIsCompaction ? "compaction" : "default"}
durationMs={durationMs}
readOnly={readOnly}
redactToolContent={redactToolContent}
workdir={workspaceRoot}
onOpenFileLink={onOpenFileLink}
/>
{isLatestLiveStreaming ? (
<LiveStatusFooter
status={displayedToolStatus ?? VIBING_STATUS}
isCompaction={displayedToolStatusIsCompaction}
/>
) : null}
{isLatestLiveStreaming &&
!shouldShowPendingLiveBubble &&
retryAttempts &&
Expand All @@ -955,8 +957,10 @@ const GatewayTranscriptListRegion = memo(function GatewayTranscriptListRegion(pr
{!readOnly && !isLatestLiveStreaming ? (
<GatewayAssistantMessageActions
row={row}
retryTarget={findRetryTarget(rows, rowIndex)}
retryTarget={retryTarget}
isStreaming={isStreaming}
showUsage={showUsage}
usageContextWindow={usageContextWindow}
isCopied={copiedMessageId === row.key}
setCopiedMessageId={setCopiedMessageId}
onResendFromEdit={onResendFromEdit}
Expand Down
13 changes: 9 additions & 4 deletions crates/agent-gateway/web/src/styles/base-chat.css
Original file line number Diff line number Diff line change
Expand Up @@ -933,10 +933,6 @@ html[data-liveagent-webui="gateway"] [role="textbox"]:focus-visible {
html[data-liveagent-webui="gateway"] .changed-file-row-action {
display: none;
}
.chat-user-bubble-actions,
.chat-assistant-actions {
opacity: 1;
}

.chat-user-bubble-action,
.chat-assistant-action {
Expand All @@ -949,6 +945,15 @@ html[data-liveagent-webui="gateway"] [role="textbox"]:focus-visible {
}
}

/* Only force the action row visible when *no* hover-capable pointer exists.
* `(pointer: coarse)` alone matches many desktop 2-in-1s that still have a mouse. */
@media (any-hover: none) {
.chat-user-bubble-actions,
.chat-assistant-actions {
opacity: 1;
}
}

.gateway-bubble {
width: min(100%, 760px);
border-radius: 22px;
Expand Down
78 changes: 75 additions & 3 deletions crates/agent-gateway/web/test/assistant-bubble-utils.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import { createWebModuleLoader } from "../../test/helpers/load-web-module.mjs";
const rootDir = fileURLToPath(new URL("../", import.meta.url));
const loader = createWebModuleLoader({ rootDir });
const { BUILTIN_TOOL_CATALOG } = loader.loadModule("@liveagent/ui/lib/tools/builtinToolCatalog.ts");
const { groupRoundBlocks, isBuiltinShareToolName } = loader.loadModule(
"@liveagent/ui/components/chat/assistant-bubble/assistantBubbleUtils.ts",
);
const { groupRoundBlocks, isBuiltinShareToolName, resolveAssistantTurnLayout } =
loader.loadModule("@liveagent/ui/components/chat/assistant-bubble/assistantBubbleUtils.ts");

test("shared history recognizes every catalog tool as builtin", () => {
for (const entry of BUILTIN_TOOL_CATALOG) {
Expand Down Expand Up @@ -101,3 +100,76 @@ test("task tools stay standalone so transcript filtering cannot hide ordinary to
["Read", "Read"],
);
});

test("final native images and hosted search results stay in the answer layer", () => {
const displayImageTool = {
kind: "tool",
item: {
toolCall: { type: "toolCall", id: "image-1", name: "Image", arguments: {} },
toolResult: {
role: "toolResult",
toolCallId: "image-1",
content: [],
isError: false,
details: {
kind: "display_image",
images: [{ path: "/workspace/icon.png", mimeType: "image/png" }],
loadMode: "inline",
},
},
},
};
const text = (id, value) => ({ kind: "text", id, text: value });
const terminalMeta = { stopReason: "stop" };

const textThenImage = resolveAssistantTurnLayout(
[
{
round: 1,
meta: terminalMeta,
blocks: [text("intro", "图像开始"), displayImageTool],
},
],
{ live: false },
);
assert.deepEqual(
textThenImage.answer.map((entry) => entry.block.kind),
["text", "tool"],
);
assert.deepEqual(textThenImage.work, []);

const imageThenText = resolveAssistantTurnLayout(
[
{
round: 1,
meta: terminalMeta,
blocks: [displayImageTool, text("done", "图像测试完成")],
},
],
{ live: false },
);
assert.deepEqual(
imageThenText.answer.map((entry) => entry.block.kind),
["tool", "text"],
);
assert.deepEqual(imageThenText.work, []);

const textThenSearch = resolveAssistantTurnLayout(
[
{
round: 1,
meta: terminalMeta,
blocks: [
text("summary", "搜索完成"),
{ kind: "hostedSearch", item: { id: "search-1", query: "OpenAI" } },
],
},
],
{ live: false },
);
assert.deepEqual(
textThenSearch.answer.map((entry) => entry.block.kind),
["text", "hostedSearchGroup"],
);
assert.deepEqual(textThenSearch.work, []);
});
14 changes: 3 additions & 11 deletions crates/agent-gateway/web/test/assistant-status.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,22 @@ import { fileURLToPath } from "node:url";

import { createWebModuleLoader } from "../../test/helpers/load-web-module.mjs";

function Loader2(props) {
return { type: "Loader2", props };
}

const loader = createWebModuleLoader({
rootDir: fileURLToPath(new URL("../", import.meta.url)),
mocks: {
"@liveagent/ui/components/IconSet": { Loader2 },
"@liveagent/ui/i18n/index": { useLocale: () => ({ t: (key) => key }) },
},
});

const { AssistantStatus } = loader.loadModule("@liveagent/ui/components/chat/AssistantStatus");

test("assistant running status keeps its spinner animated", () => {
test("assistant running status renders one compact animated text label", () => {
const status = AssistantStatus({ children: "Vibing" });
const icon = status.props.children[0];
const text = status.props.children[1];
const text = status.props.children;

assert.equal(icon.type, Loader2);
assert.match(icon.props.className, /(?:^|\s)animate-spin(?:\s|$)/);
assert.doesNotMatch(icon.props.className, /(?:^|\s)motion-reduce:animate-none(?:\s|$)/);
assert.match(status.props.className, /(?:^|\s)min-w-0(?:\s|$)/);
assert.match(status.props.className, /(?:^|\s)max-w-full(?:\s|$)/);
assert.match(text.props.className, /(?:^|\s)shimmer(?:\s|$)/);
assert.match(text.props.className, /(?:^|\s)truncate(?:\s|$)/);
assert.match(text.props.className, /(?:^|\s)whitespace-nowrap(?:\s|$)/);
});
16 changes: 10 additions & 6 deletions crates/agent-gateway/web/test/chat-file-links.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ test("Gateway historical and streaming rows keep the explicit file-open prop cha
const files = [
"../src/app/GatewayAppView.tsx",
"../src/components/GatewayTranscript.tsx",
"../../../agent-ui/src/components/chat/ThinkingActivity.tsx",
"../../../agent-ui/src/components/chat/AssistantBubble.tsx",
"../../../agent-ui/src/components/chat/assistant-bubble/RoundContent.tsx",
];
Expand All @@ -109,15 +108,20 @@ test("Gateway historical and streaming rows keep the explicit file-open prop cha
assert.ok((roundContent.match(/onOpenFileLink=\{onOpenFileLink\}/g) ?? []).length >= 2);
assert.ok((roundContent.match(/workdir=\{workdir\}/g) ?? []).length >= 2);

const thinkingActivity = fs.readFileSync(
// Reasoning disclosures render Markdown, so file links inside reasoning
// text keep the same explicit open-prop chain as answer prose.
const thinkingDisclosure = fs.readFileSync(
fileURLToPath(
new URL("../../../agent-ui/src/components/chat/ThinkingActivity.tsx", import.meta.url),
new URL(
"../../../agent-ui/src/components/chat/assistant-bubble/ThinkingDisclosure.tsx",
import.meta.url,
),
),
"utf8",
);
assert.match(thinkingActivity, /<Markdown/);
assert.match(thinkingActivity, /onOpenFileLink=\{onOpenFileLink\}/);
assert.match(thinkingActivity, /workdir=\{workdir\}/);
assert.match(thinkingDisclosure, /<Markdown/);
assert.match(thinkingDisclosure, /onOpenFileLink=\{onOpenFileLink\}/);
assert.match(thinkingDisclosure, /workdir=\{workdir\}/);

const gatewayProjectTools = fs.readFileSync(
fileURLToPath(new URL("../src/app/hooks/useGatewayProjectTools.ts", import.meta.url)),
Expand Down
13 changes: 8 additions & 5 deletions crates/agent-gateway/web/test/live-status-layout.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,21 @@ const sharedAssistantStatusSource = fs.readFileSync(
"utf8",
);

test("the streaming assistant always owns one stable live-status footer", () => {
test("the streaming assistant keeps live status inside its stable work trace", () => {
assert.doesNotMatch(transcriptSource, /function shouldShowLiveStatusForRounds/);
assert.doesNotMatch(transcriptSource, /function LiveStatusFooter/);
assert.match(
transcriptSource,
/isLatestLiveStreaming\s*\?\s*\(\s*<LiveStatusFooter[\s\S]*?status=\{displayedToolStatus \?\? VIBING_STATUS\}/,
/<AssistantBubble[\s\S]*?toolStatus=\{isLatestLiveStreaming \? displayedToolStatus : null\}/,
);
assert.match(transcriptSource, /data-row-key=\{row\.key\}/);
assert.match(transcriptSource, /gateway-live-status-footer ml-9 min-w-0 overflow-hidden pt-1/);
assert.match(transcriptSource, /<LiveAssistantStatus status=\{status\}/);
assert.match(
transcriptSource,
/toolStatusVariant=\{displayedToolStatusIsCompaction \? "compaction" : "default"\}/,
);
assert.match(sharedAssistantStatusSource, /export function LiveAssistantStatus/);
assert.match(sharedAssistantStatusSource, /if \(isCompaction\) return <CompactingText/);
assert.match(sharedAssistantStatusSource, /return <VibingText/);
assert.match(sharedAssistantStatusSource, /return <LiveSparkle/);
assert.match(sharedAssistantStatusSource, /return <AssistantStatus/);
});

Expand Down
Loading
Loading