-
+
diff --git a/THIRD_PARTY_NOTICES.txt b/THIRD_PARTY_NOTICES.txt
index b129cac..899a904 100644
--- a/THIRD_PARTY_NOTICES.txt
+++ b/THIRD_PARTY_NOTICES.txt
@@ -4242,7 +4242,7 @@ developed by Chen Jiaju, licensed under the MIT License and the Apache License 2
-------------------------------------------------------------------------------
MIT License (MIT)
Used by:
-- nota 0.10.0
+- nota 0.11.0
- brotli-decompressor 5.0.3
- cargo_toml 0.22.3
- dpi 0.1.2
diff --git a/bom.cyclonedx.json b/bom.cyclonedx.json
index 691cdcc..f7694e6 100644
--- a/bom.cyclonedx.json
+++ b/bom.cyclonedx.json
@@ -1,14 +1,14 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
- "serialNumber": "urn:uuid:2e51a386-9432-4a50-a3c3-0e7d998d7106",
+ "serialNumber": "urn:uuid:30a8310e-8b01-43a9-a4ca-542abf91333f",
"version": 1,
"metadata": {
"component": {
"type": "application",
"bom-ref": "pkg:github/kwp-lab/nota",
"name": "Nota",
- "version": "0.10.0",
+ "version": "0.11.0",
"licenses": [
{
"license": {
diff --git a/docs/design-system.md b/docs/design-system.md
index 48f21ca..01665de 100644
--- a/docs/design-system.md
+++ b/docs/design-system.md
@@ -11,7 +11,7 @@ It is intentionally separate from marketing artwork and release assets.
## Product Character
Nota should feel calm, trustworthy, local, and precise. The visual language
-uses warm neutral surfaces and a restrained jade accent so recording state and
+uses clean neutral surfaces and a restrained jade accent so recording state and
meeting content remain more prominent than decoration. Interfaces should feel
native to Windows 11 without impersonating a Microsoft product.
@@ -225,6 +225,25 @@ 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 a regular-weight, single-line recording title followed by a
+two-ended metadata line: date and duration stay at the leading edge, while a
+compact semantic transcription badge aligns to the trailing edge. Long titles
+truncate with an ellipsis without displacing the fixed quick-play action; the
+full title remains available to assistive technology and on pointer hover. File
+size remains available in the selected recording header instead of being
+repeated throughout the list. Status badges use Caption text, a small radius,
+semantic foreground/surface colors, and a subtle border instead of relying on
+text color alone. Quiet inter-row spacing, comfortable vertical padding, and
+subtle inset hairline separators establish rhythm without turning rows into
+individual cards or restoring a permanent leading icon. Separators sit between
+rows and disappear when a row is hovered, selected, or owns an open context
+menu so the interactive surface reads as one unit. The selected row uses a
+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 compact trailing slot remains reserved so row content does not shift when
+the action appears. 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. The document selector may grow to a
@@ -268,7 +287,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 65fcd60..06031c4 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/package-lock.json b/package-lock.json
index 0370a83..6306c66 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "nota",
- "version": "0.10.0",
+ "version": "0.11.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "nota",
- "version": "0.10.0",
+ "version": "0.11.0",
"license": "MIT",
"dependencies": {
"@radix-ui/react-select": "^2.3.7",
diff --git a/package.json b/package.json
index fcef732..a3970cd 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "nota",
"private": true,
- "version": "0.10.0",
+ "version": "0.11.0",
"license": "MIT",
"repository": {
"type": "git",
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index 74bea21..07e3b70 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -2299,7 +2299,7 @@ checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
[[package]]
name = "nota"
-version = "0.10.0"
+version = "0.11.0"
dependencies = [
"anyhow",
"arboard",
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index ec3990d..4c5e060 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "nota"
-version = "0.10.0"
+version = "0.11.0"
description = "A private, local-first Windows meeting recorder"
authors = ["Nota Contributors"]
license = "MIT"
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index df2ab66..16bd508 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Nota",
- "version": "0.10.0",
+ "version": "0.11.0",
"identifier": "com.local.meetingnote",
"build": {
"beforeDevCommand": "npm run dev:web",
diff --git a/src/components/RecordingsWorkspace.test.tsx b/src/components/RecordingsWorkspace.test.tsx
index d83c304..efec842 100644
--- a/src/components/RecordingsWorkspace.test.tsx
+++ b/src/components/RecordingsWorkspace.test.tsx
@@ -315,6 +315,47 @@ 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");
+ const title = within(row).getByText("产品周会");
+ const status = within(row).getByText("已转写");
+
+ expect(screen.getByLabelText("2 条录音")).toHaveTextContent("2 条");
+ expect(row.querySelector(".history-item-icon")).toBeNull();
+ expect(row.querySelector(".history-status-dot")).toBeNull();
+ expect(title).toHaveClass("history-item-title");
+ expect(title).toHaveAttribute("title", "产品周会");
+ expect(metadata?.firstElementChild).toHaveClass("history-item-facts");
+ expect(metadata?.lastElementChild).toBe(status);
+ expect(status).toHaveClass("transcription-badge", "history-transcription-status", "status-completed");
+ 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 bb8f302..b41ffe7 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,
@@ -707,7 +706,9 @@ export function RecordingsWorkspace(props: RecordingsWorkspaceProps) {
导入录音
- {props.items.length}
+
+ {props.items.length} 条
+