diff --git a/packages/app/e2e/snap/session-trow.snap.ts b/packages/app/e2e/snap/session-trow.snap.ts index 41dc4b75..3299d330 100644 --- a/packages/app/e2e/snap/session-trow.snap.ts +++ b/packages/app/e2e/snap/session-trow.snap.ts @@ -6,6 +6,7 @@ import { composeGrid, snapOutputPath, type Shot } from "./_compose" test.use({ viewport: { width: 900, height: 560 }, deviceScaleFactor: 2 }) const LANGUAGE_KEY = "pawwork.global.dat:language" +const ACTIVE_SHIMMER = '[data-slot="trow-summary-text"] [data-component="text-shimmer"][data-active="true"]' const fixturePath = fileURLToPath(new URL("./fixtures/trow-snap-fixture.tsx", import.meta.url)) async function captureBlock(name: string, block: Locator): Promise { await expect(block).toBeVisible({ timeout: 30_000 }) @@ -37,15 +38,18 @@ test("session-trow", async ({ page }) => { const shots: Shot[] = [] const running = page.locator('[data-snap="running-current"]') await expect(running).toContainText("执行命令 third command", { timeout: 30_000 }) + await expect(running.locator(ACTIVE_SHIMMER)).toBeVisible({ timeout: 30_000 }) shots.push(await captureBlock("running-current", running)) const activitySummary = page.locator('[data-snap="activity-summary-collapsed"]') await expect(activitySummary).toContainText("读取 1 个文件,运行 1 条命令,搜索文件 1 次", { timeout: 30_000 }) await expect(activitySummary).toContainText("使用 1 个工具", { timeout: 30_000 }) + await expect(activitySummary.locator(ACTIVE_SHIMMER)).toHaveCount(0) shots.push(await captureBlock("activity-summary-collapsed", activitySummary)) const failedSummary = page.locator('[data-snap="failed-summary-collapsed"]') await expect(failedSummary).toContainText("运行 1 条命令,读取 1 个文件,1 个失败", { timeout: 30_000 }) + await expect(failedSummary.locator(ACTIVE_SHIMMER)).toHaveCount(0) shots.push(await captureBlock("failed-summary-collapsed", failedSummary)) shots.push(await captureBlock("mixed-collapsed", page.locator('[data-snap="mixed-collapsed"]'))) diff --git a/packages/ui/src/components/session-turn-trow-block.css b/packages/ui/src/components/session-turn-trow-block.css index 73e4ac4e..cbfbeaf9 100644 --- a/packages/ui/src/components/session-turn-trow-block.css +++ b/packages/ui/src/components/session-turn-trow-block.css @@ -142,12 +142,10 @@ transform: rotate(0deg); } -/* Running state — summary shimmer hook. The actual pw-shimmer animation - * is wired by the caller (session-turn.css owns the `.pw-shimmer` class - * application against `[data-running]` after the Phase 2a rewrite); this - * file leaves the data attribute exposed so the shimmer can attach. */ +/* Running state marker remains on the block; summary text uses TextShimmer + * directly so the shared reduced-motion and swap behavior stay centralized. */ [data-component="session-turn-trow-block"][data-running] [data-slot="trow-summary-text"] { - /* Hook for shimmer; intentionally no animation rule here. */ + /* Marker target for state-specific overrides; no local animation here. */ } /* Body — the outer wrapper that holds the per-tool sub-rows. AstroHan diff --git a/packages/ui/src/components/session-turn-trow-block.tsx b/packages/ui/src/components/session-turn-trow-block.tsx index c09c23e6..dfddf2ef 100644 --- a/packages/ui/src/components/session-turn-trow-block.tsx +++ b/packages/ui/src/components/session-turn-trow-block.tsx @@ -2,6 +2,7 @@ import { For, Show, createMemo, createSignal, type JSX } from "solid-js" import type { ToolPart } from "@opencode-ai/sdk/v2" import { patchFiles } from "./apply-patch-file" import { Icon, type IconName } from "./icon" +import { TextShimmer } from "./text-shimmer" import "./session-turn-trow-block.css" // ============================================================================ @@ -230,8 +231,12 @@ export function TrowBlock(props: TrowBlockProps) { - - {(text) => {text}} + + {(text) => ( + + + + )}