From 954519279dd9d49bf10f315f2169f25075f6c424 Mon Sep 17 00:00:00 2001 From: WangWilly Date: Sat, 18 Jul 2026 18:20:34 +0800 Subject: [PATCH 01/16] docs: define automation logging variants --- ...7-18-automation-logging-variants-design.md | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-18-automation-logging-variants-design.md diff --git a/docs/superpowers/specs/2026-07-18-automation-logging-variants-design.md b/docs/superpowers/specs/2026-07-18-automation-logging-variants-design.md new file mode 100644 index 00000000..8b4f19bc --- /dev/null +++ b/docs/superpowers/specs/2026-07-18-automation-logging-variants-design.md @@ -0,0 +1,33 @@ +# Automation Logging Variants + +## Goal + +Make parallel sync easier to start, then compare three logging presentations without changing the underlying task flow. + +## Shared launch flow + +- Hero shows only `14 個任務可以同步執行` and one `同步全部` action. +- Remove the summary counts and `調整任務` action. +- The confirmation sheet keeps only its title, credential readiness, selected task list, `取消`, and `開始同步`. +- Remove the running-task check, dependency check, downstream-unlock check, multi-window warning, and acknowledgement checkbox. + +## Logging variants + +All variants use the same live run state and realistic timestamped events. The selected mode is controlled by the `logging` query parameter so each version has a direct link. + +1. `drawer`: after launch, the right sheet becomes `即時日誌`, with aggregate progress, latest events, error emphasis, and stop-all. +2. `inline`: each running task row exposes a `日誌` control; the selected task expands beneath its row with recent events. +3. `console`: a bottom panel shows a dense chronological stream across all tasks, with a collapse control. + +## Behavior and error handling + +- Starting sync launches every selected task with one click. +- Logs advance from the same timer as progress, so all three presentations stay consistent. +- A simulated failure event is visually distinct and identifies the task; no backend, persistence, scheduling, or retry system is added. +- Existing credential and run-history dialogs remain available. + +## Verification + +- Unit-test log event generation and existing parallel-run state. +- Verify launch and log visibility for all three query-parameter variants. +- Check responsive behavior, browser console, production build, and update the existing Sites deployment. From 2e2670660ba3be0eb838742f7af79be791015a92 Mon Sep 17 00:00:00 2001 From: WangWilly Date: Sat, 18 Jul 2026 18:22:37 +0800 Subject: [PATCH 02/16] docs: plan automation logging variants --- .../2026-07-18-automation-logging-variants.md | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-18-automation-logging-variants.md diff --git a/docs/superpowers/plans/2026-07-18-automation-logging-variants.md b/docs/superpowers/plans/2026-07-18-automation-logging-variants.md new file mode 100644 index 00000000..d640ff1b --- /dev/null +++ b/docs/superpowers/plans/2026-07-18-automation-logging-variants.md @@ -0,0 +1,161 @@ +# Automation Logging Variants Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Simplify parallel sync launch and provide three directly comparable live-log presentations. + +**Architecture:** Keep one React app and one parallel-run state. Select `drawer`, `inline`, or `console` from `?logging=`; each presentation renders the same deterministic log event list, so only layout changes between links. + +**Tech Stack:** React 19, Vite 6, Phosphor Icons, native CSS, Node test runner, Sites hosting. + +## Global Constraints + +- Reuse the existing task data, timer, dialogs, sidebar, and three-stage workflow. +- Do not add routes, packages, backend calls, persistence, scheduling, queues, or retry logic. +- Keep Traditional Chinese product copy. +- Keep the existing `.openai/hosting.json` project and workspace-wide access. + +--- + +### Task 1: Shared live-log events + +**Files:** +- Modify: `prototypes/automation-guided-flow/src/task-state.test.mjs` +- Modify: `prototypes/automation-guided-flow/src/task-state.mjs` + +**Interfaces:** +- Produces: `buildRunLogs(progress: number, elapsedSeconds: number): Array<{time: string, task: string, message: string, level: "info" | "success" | "error"}>` + +- [ ] **Step 1: Write the failing test** + +Add a test that imports `buildRunLogs`, checks that progress `4` returns only the dispatch event, and checks that progress `60` includes the failed 渣打 event with `level: "error"`. + +```js +test("buildRunLogs reveals chronological task events as progress advances", () => { + assert.equal(buildRunLogs(4, 0).length, 1); + const logs = buildRunLogs(60, 18); + assert.equal(logs.at(-1).task, "渣打銀行對帳單"); + assert.equal(logs.at(-1).level, "error"); +}); +``` + +- [ ] **Step 2: Verify the test fails for the missing export** + +Run: `node --test src/task-state.test.mjs` + +Expected: FAIL because `buildRunLogs` is not exported. + +- [ ] **Step 3: Implement the smallest deterministic event builder** + +Use one ordered event array with progress thresholds and return only reached events. Format time from `elapsedSeconds` without timers or I/O. + +```js +export function buildRunLogs(progress, elapsedSeconds) { + const now = (offset) => `00:${String(Math.max(0, elapsedSeconds - offset)).padStart(2, "0")}`; + return [ + [0, "系統", "已送出 14 個同步任務", "info", 0], + [10, "富邦全部對帳單", "已連線銀行網站", "info", 3], + [24, "玉山信用卡對帳單", "登入完成,開始下載", "info", 7], + [42, "富邦全部對帳單", "已下載 5 個檔案", "success", 12], + [58, "渣打銀行對帳單", "登入逾時,需要重新執行", "error", 18], + ].filter(([threshold]) => progress >= threshold).map(([, task, message, level, offset]) => ({ time: now(offset), task, message, level })); +} +``` + +- [ ] **Step 4: Verify all state tests pass** + +Run: `node --test src/task-state.test.mjs` + +Expected: 5 tests pass. + +### Task 2: Simplified launch and three presentations + +**Files:** +- Modify: `prototypes/automation-guided-flow/src/App.jsx` +- Modify: `prototypes/automation-guided-flow/src/styles.css` + +**Interfaces:** +- Consumes: `buildRunLogs(progress, elapsedSeconds)` from Task 1. +- Produces: direct variants at `?logging=drawer`, `?logging=inline`, and `?logging=console`. + +- [ ] **Step 1: Simplify the idle hero and confirmation sheet** + +Change the hero to one heading and one `同步全部` button. Remove summary counts and `調整任務`. Trim `PreflightSheet` to credential readiness, selected tasks, `取消`, and `開始同步`; delete warning and acknowledgement state/props. + +```jsx +
+

{count} 個任務可以同步執行

+ +
+``` + +- [ ] **Step 2: Select the presentation from the query parameter** + +Normalize once at module load and default invalid values to `drawer`. + +```js +const requestedLoggingMode = new URLSearchParams(window.location.search).get("logging"); +const loggingMode = ["drawer", "inline", "console"].includes(requestedLoggingMode) ? requestedLoggingMode : "drawer"; +``` + +- [ ] **Step 3: Add one shared log renderer** + +Render time, task, message, and error/success color from `buildRunLogs`. Keep the markup reusable in all three surfaces. + +```jsx +function LogEntries({ logs }) { + return
    {logs.map((log) =>
  1. {log.task}{log.message}
  2. )}
; +} +``` + +- [ ] **Step 4: Add drawer, inline, and console wrappers** + +- Drawer: fixed right panel with aggregate progress, `LogEntries`, close, and stop-all. +- Inline: add a unique `日誌` button per included task row and expand the selected row directly beneath it. +- Console: fixed bottom panel with chronological `LogEntries`, collapse, and stop-all. + +All three wrappers receive only `run`, `logs`, visibility callbacks, and `onStop`; they do not own run state. + +- [ ] **Step 5: Connect launch and visibility** + +On confirm, start the existing parallel run, close confirmation, then open the selected log surface. During a run, the hero contains `查看日誌` and `停止全部`; `查看日誌` reopens the relevant surface. + +- [ ] **Step 6: Style only the new surfaces and delete obsolete rules** + +Remove `.preflight-warning`, `.acknowledge`, and unused multi-check spacing. Add `.live-log-drawer`, `.inline-log`, `.log-console`, and shared `.log-entries` rules using existing colors and borders. Keep the drawer responsive and the console above the 64px sidebar breakpoint. + +- [ ] **Step 7: Build and verify** + +Run: `node --test src/task-state.test.mjs && npm run build && git diff --check` + +Expected: tests pass, Vite emits `dist/server/index.js`, and diff check is clean. + +### Task 3: Browser QA and existing Sites deployment + +**Files:** +- Modify: `prototypes/automation-guided-flow/design-qa.md` +- Generated build output only: `prototypes/automation-guided-flow/dist/**` + +**Interfaces:** +- Consumes the three query-parameter variants from Task 2. +- Produces an updated version at the existing Sites URL. + +- [ ] **Step 1: Verify all three local variants** + +For each query parameter, launch sync and confirm the expected logging surface appears. Check that removed copy and controls are absent, `同步全部` is present, and the browser console has no fresh errors. + +- [ ] **Step 2: Verify responsive layout** + +At the existing narrow browser viewport, confirm no document-level horizontal overflow and that the active logging surface can be closed and reopened. + +- [ ] **Step 3: Update design QA** + +Record the simplified launch flow, three tested variants, interaction checks, browser-console result, and `final result: passed` in `design-qa.md`. + +- [ ] **Step 4: Publish the validated source** + +Push the exact source state to the existing Sites source repository, package the current `dist`, save version 2 with the pushed commit SHA, deploy it, poll to `succeeded`, and retain `workspace_all` access. + +- [ ] **Step 5: Return three direct links** + +Return the same deployed base URL with `?logging=drawer`, `?logging=inline`, and `?logging=console` so the user can compare without a new route or selector UI. From 48feea973796dcc641a3143362de1cd6eb6c8ddb Mon Sep 17 00:00:00 2001 From: WangWilly Date: Sun, 19 Jul 2026 13:36:35 +0800 Subject: [PATCH 03/16] Redesign automation task execution --- package-lock.json | 11 + package.json | 1 + .../automation/AutomationDashboard.check.ts | 85 ++ src/lib/automation/AutomationDashboard.svelte | 1070 ++++++++++++++--- src/lib/automation/server/page-model.check.ts | 31 + src/lib/automation/server/page-model.ts | 72 +- src/lib/automation/types.ts | 1 + src/lib/i18n/i18n.ts | 50 + 8 files changed, 1140 insertions(+), 181 deletions(-) create mode 100644 src/lib/automation/AutomationDashboard.check.ts diff --git a/package-lock.json b/package-lock.json index 8aa7e94f..c29d03e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "@electron-forge/cli": "^7.11.2", "@electron-forge/maker-dmg": "^7.11.2", "@electron-forge/maker-zip": "^7.11.2", + "@lucide/svelte": "^1.25.0", "@sveltejs/adapter-static": "^3.0.10", "@sveltejs/kit": "^2.68.0", "@types/node": "^26.0.0", @@ -1717,6 +1718,16 @@ "node": ">=18" } }, + "node_modules/@lucide/svelte": { + "version": "1.25.0", + "resolved": "https://registry.npmjs.org/@lucide/svelte/-/svelte-1.25.0.tgz", + "integrity": "sha512-v9m+dD68jxVnqkU3K59mG/RSRFlPGzmKCGSyMfnXcaGv9jODDQMyQkcp1CGvk3Y/cUj9v7f8rw1n//K0B53xGQ==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "svelte": "^5" + } + }, "node_modules/@malept/cross-spawn-promise": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", diff --git a/package.json b/package.json index 2aab3103..abf53c06 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "@electron-forge/cli": "^7.11.2", "@electron-forge/maker-dmg": "^7.11.2", "@electron-forge/maker-zip": "^7.11.2", + "@lucide/svelte": "^1.25.0", "@sveltejs/adapter-static": "^3.0.10", "@sveltejs/kit": "^2.68.0", "@types/node": "^26.0.0", diff --git a/src/lib/automation/AutomationDashboard.check.ts b/src/lib/automation/AutomationDashboard.check.ts new file mode 100644 index 00000000..b9e70b36 --- /dev/null +++ b/src/lib/automation/AutomationDashboard.check.ts @@ -0,0 +1,85 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; + +const source = readFileSync(new URL("./AutomationDashboard.svelte", import.meta.url), "utf8"); +const runTaskSource = source.slice(source.indexOf("async function runTask"), source.indexOf("async function runParallelTasks")); +const runParallelTasksSource = source.slice(source.indexOf("async function runParallelTasks"), source.indexOf("async function stopAllTasks")); + +assert.doesNotMatch(runTaskSource, /expandedLogTaskId\s*=/); +assert.doesNotMatch(runParallelTasksSource, /expandedLogTaskId\s*=/); +assert.match(source, /import \{ slide \} from "svelte\/transition"/); +assert.match(source, /function disclosureSlide\(node: Element\)/); +assert.match(source, /matchMedia\("\(prefers-reduced-motion: reduce\)"\)\.matches \? 0 : 220/); +assert.match(source, /class="stage-body"[^>]*transition:disclosureSlide/); +assert.match(source, /class="inline-log-panel"[^>]*transition:disclosureSlide/); +assert.match(source, /async function toggleCollectTasks\(event: MouseEvent\)/); +assert.match(source, /class="table-reveal"/); +assert.match(source, /container\.animate\(/); +assert.doesNotMatch(source, /class="task-row"[^>]*transition:disclosureSlide/); +assert.match(source, /transition: transform 180ms ease/); +assert.match(source, /class="inline-task-log"/); +assert.doesNotMatch(source, /activeLogsOpen/); +assert.doesNotMatch(source, /openActiveLogs/); +assert.doesNotMatch(source, /aria-labelledby="active-logs-title"/); +assert.doesNotMatch(source, /\$t\.automation\.viewLogs/); +assert.match(source, /from "@lucide\/svelte"/); +assert.match(source, /ArrowLeftRight/); +assert.match(source, /CircleEllipsis/); +assert.match(source, /CloudDownload/); +assert.match(source, /Import as ImportIcon/); +assert.match(source, /Landmark/); +assert.match(source, /class="active-task-jump-list"/); +assert.match(source, /class="active-task-filter"/); +assert.match(source, /\$: iconTasks = automation\.tasks\.filter\([\s\S]*?task\.status === "failed"/); +assert.match(source, /\{#if iconTasks\.length\}/); +assert.match(source, /\{#each iconTasks as task \(task\.id\)\}/); +assert.match(source, /class="active-task-jump"/); +assert.match(source, /class:failed=\{task\.status === "failed"\}/); +assert.match(source, /title=\{taskLabel\(task, \$t\)\}/); +assert.match(source, /onclick=\{\(\) => handleActiveTaskClick\(task\)\}/); +assert.match(source, /function handleActiveTaskClick\(task: AutomationTaskRow\)/); +assert.match(source, /task\.status === "waiting_for_human" && task\.humanSession/); +assert.match(source, /openHumanViewer\(task\)/); +assert.match(source, /async function revealTaskLog\(task: AutomationTaskRow\)/); +assert.match(source, /expandedLogTaskId = task\.id/); +assert.match(source, /showAllCollectTasks = true/); +assert.match(source, /]*id=\{`\$\{task\.id\}-task-row`\}/); +assert.match(source, /getElementById\(`\$\{task\.id\}-task-row`\)/); +assert.match(source, /scrollIntoView\(\{ behavior: reducedMotion \? "auto" : "smooth", block: "start" \}\)/); +assert.match(source, /\.task-row\s*\{\s*scroll-margin-top: 88px;/); +assert.match(source, /focus\(\{ preventScroll: true \}\)/); +assert.match(source, /overflow-x:\s*auto/); +assert.match(source, /\.active-task-filter\s*\{[\s\S]*?border: 1px solid var\(--border\)/); +assert.match(source, /\.active-task-jump-list\s*\{[\s\S]*?flex-wrap: nowrap/); +assert.match(source, /scrollbar-width: none/); +assert.match(source, /onwheel=\{scrollActiveTasks\}/); +assert.match(source, /function scrollActiveTasks\(event: WheelEvent\)/); +assert.match(source, /class="active-task-tooltip"/); +assert.match(source, /role="tooltip"/); +assert.match(source, /onpointerenter=\{\(event\) => showTaskTooltip\(task, event\)\}/); +assert.match(source, /onpointerleave=\{hideTaskTooltip\}/); +assert.match(source, /\$t\.automation\.statusLabels\[hoveredTask\.status\]/); +assert.match(source, /border-radius: 50%/); +assert.match(source, /\.active-task-jump\.failed\s*\{[\s\S]*?var\(--danger\)/); +const resumeHumanViewerSource = source.slice(source.indexOf("async function resumeHumanViewer"), source.indexOf("function pointerPoint")); +assert.match(resumeHumanViewerSource, /automation\.resume\(task\.id\)/); +assert.doesNotMatch(resumeHumanViewerSource, /runTask\(task\)/); +assert.doesNotMatch(source, /aggregateProgress/); +assert.doesNotMatch(source, /combinedTaskProgress/); +assert.doesNotMatch(source, /class="aggregate-progress"/); +assert.match(source, /class="progress-cell"/); +assert.match(source, /class="stage-toggle-action"/); +assert.match(source, /aria-expanded=\{stageOpen\[stage\.id\]\}/); +assert.doesNotMatch(source, /
openSyncSheet\(stage\.tasks\)\}/); +assert.match(source, /grid-template-columns: repeat\(2, minmax\(0, 1fr\)\)/); +assert.match(source, /:global\(html\) \{\s*overflow-y: scroll;/); +assert.match(source, /class="card workflow-card"/); +assert.match(source, /class="sync-sheet"/); +assert.doesNotMatch(source, /\$t\.automation\.commandId/); +assert.doesNotMatch(source, /class="task-command"/); +assert.match(source, /[\s\S]*width: 32%[\s\S]*width: 14%[\s\S]*width: 22%[\s\S]*width: 12%[\s\S]*width: 20%[\s\S]*<\/colgroup>/); +assert.match(source, /\.automation-table\s*\{\s*table-layout: fixed;/); +assert.match(source, /colspan="5"/); diff --git a/src/lib/automation/AutomationDashboard.svelte b/src/lib/automation/AutomationDashboard.svelte index c4d77007..65df363d 100644 --- a/src/lib/automation/AutomationDashboard.svelte +++ b/src/lib/automation/AutomationDashboard.svelte @@ -1,5 +1,7 @@ +``` + +- [ ] **Step 4: Run focused and project verification** + +Run: `node --no-warnings --experimental-strip-types --test src/lib/automation/AutomationDashboard.check.ts` + +Expected: PASS. + +Run: `npm run typecheck` + +Expected: exit 0 with no Svelte or TypeScript errors. + +Run: `npm run build` + +Expected: exit 0 and renderer/electron bundles emitted. + +- [ ] **Step 5: Verify the live Electron interactions** + +Connect to `http://127.0.0.1:9222`, reload the built page, and verify stage collapse/expand, inline-log collapse/expand, and show-all/collapse. Capture the implementation at the same desktop viewport and confirm there are no fresh console errors. + +- [ ] **Step 6: Record design QA** + +Update `design-qa.md` with the source screenshot paths, implementation screenshot path, viewport/state, the three interactions, console result, focused comparison, and `final result: passed` only when no actionable P0/P1/P2 mismatch remains. diff --git a/docs/superpowers/plans/2026-07-19-automation-log-redesign-options.md b/docs/superpowers/plans/2026-07-19-automation-log-redesign-options.md new file mode 100644 index 00000000..1946d4bb --- /dev/null +++ b/docs/superpowers/plans/2026-07-19-automation-log-redesign-options.md @@ -0,0 +1,88 @@ +# Automation Log Redesign Options — Implementation Plan + +> **For Codex:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Remove the aggregate progress bar from the production Automation running summary and deliver three interactive, directly comparable log-viewer prototypes. + +**Architecture:** Keep the production change narrowly scoped to `AutomationDashboard.svelte` and its source-level regression check. Build the three visual directions as isolated, self-contained HTML prototypes under `prototypes/automation-log-options/`, using the existing Automation shell, Traditional Chinese labels, realistic concurrent task data, and no Electron or banking side effects. + +**Tech Stack:** Svelte 5 source checks for production; standalone HTML/CSS/JavaScript for prototypes. + +--- + +### Task 1: Remove the production aggregate progress bar + +**Files:** +- Modify: `src/lib/automation/AutomationDashboard.check.ts` +- Modify: `src/lib/automation/AutomationDashboard.svelte` + +**Step 1: Write the failing regression check** + +- Replace the assertions for `combinedTaskProgress` with assertions that the running-summary aggregate progress markup and state are absent. +- Keep assertions proving per-task progress UI remains available. + +**Step 2: Run the focused check to verify RED** + +Run: `npx tsx src/lib/automation/AutomationDashboard.check.ts` +Expected: FAIL because the aggregate progress state/function/markup still exists. + +**Step 3: Implement the minimal production change** + +- Remove `aggregateProgress` reactive state. +- Remove `combinedTaskProgress`. +- Remove the hero `aggregate-progress` block only. +- Preserve running task count, 「查看日誌」, 「停止全部」, and per-task progress/status. + +**Step 4: Run the focused check to verify GREEN** + +Run: `npx tsx src/lib/automation/AutomationDashboard.check.ts` +Expected: PASS. + +### Task 2: Build three isolated interactive log prototypes + +**Files:** +- Create: `prototypes/automation-log-options/task-stack.html` +- Create: `prototypes/automation-log-options/console-matrix.html` +- Create: `prototypes/automation-log-options/merged-stream.html` +- Create: `prototypes/automation-log-options/index.html` + +**Step 1: Build the three directions in parallel** + +- Option 1 — Task stack: full-width stacked log sections with sticky task headers and independent collapse. +- Option 2 — Console matrix: equal responsive consoles with independent scroll and follow-tail controls. +- Option 3 — Merged stream: one chronological stream with task tags, filter/search, error-only, and pause/follow controls. +- In every option, one modal must show multiple active task logs simultaneously. +- Do not include the aggregate hero progress bar. + +**Step 2: Add the comparison hub** + +- Add a simple index page linking all three options and summarizing their operational trade-offs. +- Keep every option directly addressable by URL. + +**Step 3: Render all three at one viewport** + +- Produce same-size preview screenshots for direct visual comparison. +- Check modal fit, content density, controls, overflow, and task-state legibility. + +### Task 3: Verify implementation and hand off for selection + +**Files:** +- Modify: `design-qa.md` + +**Step 1: Verify production code** + +Run the focused dashboard check, typecheck, and build. Record any unrelated pre-existing failures without changing their scope. + +**Step 2: Verify prototype interactions** + +- Confirm open/close and Escape behavior. +- Confirm each option displays at least four active tasks at once. +- Confirm option-specific controls work. +- Confirm no option auto-opens on simulated task start. + +**Step 3: Record visual QA and share links** + +- Append a concise comparison result to `design-qa.md`. +- Return all three prototype links and ask the user to choose 1, 2, or 3 before production log UI implementation. + +**Repository note:** Keep the current dirty worktree uncommitted; do not include unrelated user changes in a commit. diff --git a/docs/superpowers/specs/2026-07-19-automation-active-task-jump-design.md b/docs/superpowers/specs/2026-07-19-automation-active-task-jump-design.md new file mode 100644 index 00000000..a1c529a3 --- /dev/null +++ b/docs/superpowers/specs/2026-07-19-automation-active-task-jump-design.md @@ -0,0 +1,59 @@ +# Automation Active Task Jump — Design Spec + +## Goal + +Replace the running-summary 「查看日誌」 action with a compact list of active tasks. Selecting a task reveals and scrolls to that task's inline log on the Automation page. + +## Scope + +- Prototype first; do not change the production app in this pass. +- Reuse the current Automation visual system and page structure. +- Keep 「停止全部」 as the only summary-level action. +- Do not add a log modal, filters, progress aggregation, or new task-management behavior. + +## Running Summary + +- The heading continues to show the number of concurrently running tasks. +- Below the heading, render every active task as a compact clickable row or pill containing: + - task name; + - current state such as 「執行中」 or 「等待操作」. +- Items wrap when space is insufficient and remain readable with long task names. +- The former 「查看日誌」 button is absent. + +## Selection Interaction + +When an active task is selected: + +1. Open the workflow stage containing that task if it is collapsed. +2. Reveal that task's inline log if it is collapsed. +3. Preserve any other manually opened inline logs. +4. Smooth-scroll the selected inline log heading into view with enough top offset for the fixed application header. +5. Apply a short blue focus highlight to the target log container, then remove it automatically. +6. Move keyboard focus to the inline log heading without triggering a second scroll. + +Starting or polling a task must not automatically expand logs or move the viewport. + +## Prototype State + +- Show three concurrently active tasks in the summary. +- Place them across at least two workflow stages so the prototype proves collapsed-stage opening. +- Include realistic Traditional Chinese task names, statuses, timestamps, and log output. +- Provide a reset control only if needed to repeat the interaction; it remains secondary. + +## Accessibility + +- Active-task items use buttons, not generic clickable containers. +- Each button names both the task and the action, for example 「前往玉山信用卡對帳單日誌」. +- The target log heading receives programmatic focus. +- Smooth scrolling is disabled when `prefers-reduced-motion: reduce` is active; the focus highlight remains. +- Status is conveyed by text, not color alone. + +## Verification + +- The prototype initially shows no inline logs expanded. +- Selecting each summary task opens the correct stage and log. +- Other open logs remain open. +- Repeated selection of the same task still repositions and highlights it. +- Simulated task start does not expand or scroll. +- Escape has no special behavior because this design uses no modal. +- Verify desktop layout at 1600 × 1000 and a narrow responsive state. diff --git a/docs/superpowers/specs/2026-07-19-automation-disclosure-motion-design.md b/docs/superpowers/specs/2026-07-19-automation-disclosure-motion-design.md new file mode 100644 index 00000000..db012366 --- /dev/null +++ b/docs/superpowers/specs/2026-07-19-automation-disclosure-motion-design.md @@ -0,0 +1,25 @@ +# Automation Disclosure Motion Design + +## Goal + +Add a consistent expanding and collapsing motion to the automation workflow disclosures shown in the approved screenshots. + +## Interaction + +- Stage bodies expand and collapse vertically in 220 ms. +- Inline task logs use the same vertical motion beneath the selected task. +- Additional collect-stage task rows animate when `顯示全部任務` is toggled. +- Stage carets rotate over 180 ms so the control state changes with the content. +- Reduced-motion users receive an immediate state change. + +## Constraints + +- Reuse Svelte's installed `slide` transition and the existing component state. +- Do not add packages, routes, data state, or new visual styling. +- Preserve table semantics, fixed column widths, labels, and existing controls. + +## Verification + +- Add a source-level regression check for the shared transition and each disclosure surface. +- Run the focused check, typecheck, and production build. +- Use the running Electron app over CDP to open and close the three disclosure surfaces and inspect console errors. diff --git a/docs/superpowers/specs/2026-07-19-automation-log-redesign-options-design.md b/docs/superpowers/specs/2026-07-19-automation-log-redesign-options-design.md new file mode 100644 index 00000000..720877ea --- /dev/null +++ b/docs/superpowers/specs/2026-07-19-automation-log-redesign-options-design.md @@ -0,0 +1,53 @@ +# Automation Log Redesign Options + +## Goal + +Create three interactive HTML prototypes for the shared modal that displays every currently running task's live log, then let the user choose one before production implementation. Independently remove the aggregate progress bar from the production Automation hero. + +## Shared constraints + +- Keep the existing Automation visual system, Traditional Chinese copy, modal backdrop, task data, semantic states, and desktop density. +- The modal shows all currently running tasks at the same time; it is not a one-task switcher. +- Opening the modal must not change task execution. Starting a task must not open the modal or an inline log. +- Each prototype uses realistic raw log text, long paths, success/running/error states, and at least four concurrent tasks. +- The close action, Escape key, scrolling, and visible task-state updates must work. +- Prototype work remains isolated from the production app until an option is selected. + +## Direction 1: Task stack + +- One full-width task section per running task in a single modal scroll area. +- Sticky task header contains name, state, elapsed time, and collapse control. +- Every task is expanded initially; users may collapse noisy tasks while leaving several logs visible. +- Best for readability, raw-log fidelity, and the smallest production implementation. + +## Direction 2: Console matrix + +- Responsive two-column grid of equal task consoles, collapsing to one column at narrow widths. +- Each console has an independent vertical scroll, follow-tail toggle, and compact state header. +- The modal itself keeps a stable header and footer while consoles use the available height. +- Best for operators comparing several tasks side by side. + +## Direction 3: Merged event stream + +- One chronological stream combines entries from every running task. +- Each entry includes timestamp, task label, level, and message; task chips filter the shared stream without hiding the fact that multiple tasks are running. +- Search, pause-follow, and error-only controls are interactive in the prototype. +- Best for quickly locating failures, but production implementation would require reliable per-line timestamps or ingestion metadata. + +## Production hero change + +- Remove the aggregate progress bar and percentage from the active Automation hero. +- Keep the running-task count on the left and `查看日誌` plus `停止全部` actions on the right. +- Preserve per-task status/progress in the task table; only the hero aggregate is removed. + +## Prototype delivery + +- Deliver one local prototype with three directly addressable variants and an in-prototype option switcher. +- Use mock-only state; do not call Electron automation APIs or launch banking workflows. +- Verify all primary controls, responsive layout, and console output before handoff. + +## Verification + +- Add a focused production source check proving the hero no longer renders the aggregate progress block. +- Run the focused check, typecheck, production build, and `git diff --check`. +- Capture each prototype at the same desktop viewport and record a design-QA comparison against the supplied modal screenshots. From 0a8ce45cacc294868882e8b0d12c41fdba6e7696 Mon Sep 17 00:00:00 2001 From: WangWilly Date: Sun, 19 Jul 2026 14:12:53 +0800 Subject: [PATCH 05/16] Fix automation stage availability --- src/lib/automation/AutomationDashboard.check.ts | 3 +++ src/lib/automation/AutomationDashboard.svelte | 13 ++----------- src/lib/automation/server/page-model.check.ts | 15 +++++++++++++++ src/lib/automation/server/page-model.ts | 2 +- src/lib/i18n/i18n.ts | 10 ++-------- 5 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/lib/automation/AutomationDashboard.check.ts b/src/lib/automation/AutomationDashboard.check.ts index b9e70b36..e7a6eaa1 100644 --- a/src/lib/automation/AutomationDashboard.check.ts +++ b/src/lib/automation/AutomationDashboard.check.ts @@ -74,6 +74,9 @@ assert.doesNotMatch(source, /
openSyncSheet\(stage\.tasks\)\}/); +assert.match(source, /class:muted=\{!stageRunnableTasks\(stage\.tasks\)\.length\}/); +assert.doesNotMatch(source, /stage\.description/); +assert.match(source, /\$t\.automation\.startImportHeading/); assert.match(source, /grid-template-columns: repeat\(2, minmax\(0, 1fr\)\)/); assert.match(source, /:global\(html\) \{\s*overflow-y: scroll;/); assert.match(source, /class="card workflow-card"/); diff --git a/src/lib/automation/AutomationDashboard.svelte b/src/lib/automation/AutomationDashboard.svelte index 65df363d..1c40e682 100644 --- a/src/lib/automation/AutomationDashboard.svelte +++ b/src/lib/automation/AutomationDashboard.svelte @@ -66,19 +66,16 @@ { id: "collect", title: $t.automation.collectStage, - description: $t.automation.collectStageDescription, tasks: automation.tasks.filter((task) => task.kind === "crawler"), }, { id: "import", title: $t.automation.importStage, - description: $t.automation.importStageDescription, tasks: automation.tasks.filter((task) => task.kind === "import"), }, { id: "sync", title: $t.automation.syncStage, - description: $t.automation.syncStageDescription, tasks: automation.tasks.filter((task) => task.kind === "sync"), }, ]; @@ -584,7 +581,7 @@

{automation.active ? $t.automation.runningTaskHeading(automation.activeTaskCount) - : $t.automation.runnableTaskHeading(parallelTasks.length)} + : $t.automation.startImportHeading}

{#if iconTasks.length}
@@ -641,7 +638,7 @@
- +

{stage.title}

@@ -649,7 +646,6 @@ {$t.common.importLocked} {/if}
- {stage.description}