From 88a9cc23413b694b80fb0b90ec1426608e14517c Mon Sep 17 00:00:00 2001 From: Kelvin Poon <35914355+kwp-lab@users.noreply.github.com> Date: Wed, 16 Sep 2026 00:21:59 +0800 Subject: [PATCH 1/2] feat: refine recording library list density --- CHANGELOG.md | 3 + docs/design-system.md | 15 ++++- docs/testing.md | 15 +++++ src/components/RecordingsWorkspace.test.tsx | 34 ++++++++++ src/components/RecordingsWorkspace.tsx | 36 ++++++---- src/recording-detail.css | 10 +-- src/styles.css | 73 ++++++++++++++++----- 7 files changed, 149 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f3cf2f..121d747 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht ### Changed +- Refined the recording library into compact two-line rows with flatter + selection, quieter progressive quick-play actions, a narrow scrollbar, and + playback controls free of the redundant persistent network-use reminder. - Reorganized recording details into a full-height reading workspace with a compact contextual toolbar, document/version selectors, bottom audio player, a collapsible recording list controlled from the left of the detail title, diff --git a/docs/design-system.md b/docs/design-system.md index 64a8df1..ea3fbb1 100644 --- a/docs/design-system.md +++ b/docs/design-system.md @@ -225,6 +225,18 @@ available when the list is hidden. The compact title, detail tabs, contextual toolbar, reading region, and native audio player follow DOM order. The list and reading region scroll independently; the outer detail pane must not scroll. Playback remains mounted across tabs. +The recording list uses compact two-line rows rather than individual cards. A +row contains the recording title followed by date, duration, and transcription +state on one metadata line; file size remains available in the selected +recording header instead of being repeated throughout the list. Status uses a +compact semantic dot plus text, not a pill or a second leading icon. Subtle +inset dividers establish row rhythm, while the selected row adds a short jade +selection marker and flat brand surface. The quick-play action appears for +hover, keyboard focus, selection, or active playback, and Space toggles +playback for the focused row. Its circular raised surface distinguishes the +row action from the selection target without returning to a second permanent +icon column. The list scrollbar uses a narrow neutral thumb on a transparent +track while retaining a visible high-contrast fallback. AI documents use shared select styling for document/version selection instead of a permanent nested document sidebar. AI revision and copying stay directly @@ -245,7 +257,8 @@ At 1280×800 and 980×640, normal completed-document content targets at least that area. Controls must not wrap into multiple toolbar rows, and long titles, models, and paths must not cause page-level horizontal overflow. Use existing semantic tokens; do not shrink body type to achieve density. The playback -footer retains the local-recording/network-use reminder. +footer contains only playback controls. Network-use disclosure belongs at the +transcription or AI-generation action boundary, not beneath local playback. ## Change and Review Checklist diff --git a/docs/testing.md b/docs/testing.md index 044cd24..34bbfee 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -113,6 +113,21 @@ Automated coverage must include: dismissal, focus restoration, and an accessible name for every option; - PID rebinding by executable path and no silent switch to another application. +## Recording Library Visual Regression Matrix + +Automated coverage and Windows acceptance must include: + +- compact two-line rows that keep title, date, duration, and transcription + state readable without repeating file size or a leading recording icon; +- quick playback exposed on row hover, keyboard focus, selection, and active + playback, with Space toggling playback while Enter keeps its selection role; +- flat selected, context-menu, processing, completed, and failed states with + visible focus and no card border or elevation; +- an independently scrolling list with a narrow neutral scrollbar, transparent + track, and usable forced-colors behavior; +- a playback footer with no persistent network-use note, verified at 100%, + 125%, and 150% Windows scaling and at the 980×640 minimum window size. + ## Audio Import Regression Matrix Automated coverage must include: diff --git a/src/components/RecordingsWorkspace.test.tsx b/src/components/RecordingsWorkspace.test.tsx index 6bdab86..f27933b 100644 --- a/src/components/RecordingsWorkspace.test.tsx +++ b/src/components/RecordingsWorkspace.test.tsx @@ -315,6 +315,40 @@ describe("RecordingsWorkspace", () => { expect(screen.getByText("客户访谈")).toBeInTheDocument(); }); + it("renders recordings as compact two-line rows without redundant list chrome", async () => { + const actions = renderWorkspace(); + await waitFor(() => expect(actions.onPreparePlayback).toHaveBeenCalled()); + + const list = screen.getByLabelText("录音列表"); + const row = within(list).getByText("产品周会").closest("article")!; + const metadata = row.querySelector(".history-item-meta"); + + expect(screen.getByLabelText("2 条录音")).toHaveTextContent("2 条"); + expect(row.querySelector(".history-item-icon")).toBeNull(); + expect(row.querySelector(".history-status-dot")).toBeInTheDocument(); + expect(metadata).toHaveTextContent("1:02"); + expect(metadata).toHaveTextContent("已转写"); + expect(within(list).queryByText("1.0 MB")).not.toBeInTheDocument(); + expect(screen.queryByText(/本地录音;仅在转写或手动生成 AI 文档时连接所选服务/)).not.toBeInTheDocument(); + }); + + it("plays the focused recording with Space while Enter remains selection", async () => { + const play = vi.spyOn(HTMLMediaElement.prototype, "play").mockResolvedValue(); + const actions = renderWorkspace(); + await waitFor(() => expect(document.querySelector("audio")).toHaveAttribute( + "src", + `asset://${completed.path}`, + )); + const select = within(screen.getByLabelText("录音列表")) + .getByText("产品周会") + .closest("button")!; + + fireEvent.keyDown(select, { key: "Enter" }); + expect(play).not.toHaveBeenCalled(); + fireEvent.keyDown(select, { key: " " }); + expect(play).toHaveBeenCalledOnce(); + }); + it("shows provider speaker labels and timestamp controls without inventing roles", () => { renderWorkspace(); expect(screen.getByText("speaker_1")).toBeInTheDocument(); diff --git a/src/components/RecordingsWorkspace.tsx b/src/components/RecordingsWorkspace.tsx index 4c3f729..25d2229 100644 --- a/src/components/RecordingsWorkspace.tsx +++ b/src/components/RecordingsWorkspace.tsx @@ -1,7 +1,6 @@ import { convertFileSrc } from "@tauri-apps/api/core"; import { AlertCircle, - Check, Clipboard, Download, FileAudio, @@ -706,7 +705,9 @@ export function RecordingsWorkspace(props: RecordingsWorkspaceProps) { 导入录音 - {props.items.length} + + {props.items.length} 条 +