diff --git a/.cnb.yml b/.cnb.yml index ce7b628..9db2497 100644 --- a/.cnb.yml +++ b/.cnb.yml @@ -56,8 +56,8 @@ script: cd web && npm run ui:review:only - name: archive-screenshots script: | - echo "UI 评审截图:docs/ui-review/" - ls -la docs/ui-review/ + echo "UI 评审产物:artifacts/ui-review/react-app/" + ls -la artifacts/ui-review/react-app/ main: push: diff --git a/.gitignore b/.gitignore index 0127901..4bb5a4d 100644 --- a/.gitignore +++ b/.gitignore @@ -24,14 +24,6 @@ server/data/ # web client build output (regenerated by `npm run build`) web/dist/ -# UI review screenshots: regenerated by `npm run ui:review`, and a binary diff on -# every run is noise in a review. The *report* is committed — that is the part a -# reviewer reads, and the part CI asserts on. -docs/ui-review/*.png -docs/ui-review/mobile/*.png -docs/ui-review/p1/*.png -docs/ui-review/p2/*.png - # Generated media validation artifacts /artifacts/ /docs/media-*-result.json diff --git a/docs/README.md b/docs/README.md index a82f954..b07ee67 100644 --- a/docs/README.md +++ b/docs/README.md @@ -26,18 +26,14 @@ The project README introduces reader. Use the guides below for installation, eve | 文档 / Document | 内容 / Contents | | --- | --- | | [架构与数据边界 / Architecture](architecture.md) | 数据目录、身份、同步和客户端架构 / Data boundaries, identity, sync, client architecture | +| [前端 React 架构](frontend-react-migration.md) | Web 入口、Provider、路由、feature 边界和长生命周期资源 | | [API](api.md) | 鉴权、书籍、资源、进度、文件管理、OPDS 与影音 / Auth, books, resources, progress, file management, OPDS and media | | [界面规范 / UI guidelines](ui.md) | 布局、交互、可访问性 / Layout, interaction, accessibility | ## 验证记录 · Review records -这些是特定功能交付时的验证记录,当前结果以对应提交的测试和 CI 输出为准。 -These records describe checks at specific delivery points. Use tests and CI for the current commit's results. - -- [UI 评审工具 / UI review tooling](ui-review/README.md) -- [书库与阅读界面 / Library and reader UI](ui-review/collections-ux.md) - -- [P1 阅读工具实施与验证](p1-progress-2026-09-23.md) +特定功能的验证产物写入被 Git 忽略的 `artifacts/` 目录;当前结果以对应提交的测试和 CI 输出为准。 +Feature review artifacts are written to the ignored `artifacts/` directory. Use tests and CI for the current commit's results. - [互通与客户端覆盖](ecosystem-roadmap.zh-CN.md) diff --git a/docs/architecture.md b/docs/architecture.md index 6c0daba..6c171fe 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -318,22 +318,23 @@ Readium 用 Readium CSS 主动接管排版、覆盖出版方样式,这与本 ### UI 框架:完整 React 应用 -前端现在由 React 19 负责组件树和页面生命周期,入口是 -`web/src/main.tsx` 的 `createRoot()`。所有登录后页面都挂在 -`AppProviders` 下,由成熟的 Provider 统一承载运行时、服务端状态和设计系统: +前端由 React 19 负责应用页面和页面生命周期,入口是 +`web/src/main.tsx` 的 `createRoot()`。启动阶段先由 +`web/src/app/runtime.ts` 组装平台、API、同步、离线、播放器和设置资源,再由 +`AppProviders` 把它们注入 React 树: -- `react-router-dom` 的 `createHashRouter` 负责路由、嵌套路由、参数和错误边界; +- `react-router-dom` 的 `createHashRouter` 负责 Hash 路由、嵌套路由、参数、Outlet 和错误边界; - `@tanstack/react-query` 的 `QueryClientProvider` 负责服务端状态、缓存、加载、重试和失效; - Zustand stores 负责认证、设置、同步和播放器等跨页面客户端状态; - Mantine 提供基础组件、主题和通知,Lucide React 提供统一图标; -- `RuntimeContext` 只暴露 API、同步、播放和设置等应用运行时依赖。 +- `RuntimeContext` 暴露 API、同步、播放、设置和平台能力,页面不自行组装这些依赖。 -业务按 `features/` 拆分,每个领域拥有自己的 `pages`、`components`、 -`queries`、`mutations`、`services` 和 `styles`。页面只组合查询、变更和组件, -不再通过单文件的 URL 判断来调度所有屏幕。影音的实现位于 -`web/src/features/media`,阅读器仍保留一个命令式排版边界,因为它需要直接测量 -`getBoundingClientRect()`、维护 shadow root 和处理章节分页;这个边界由 -`ReaderPage` 的 effect 创建和销毁,不会把整个应用退回到手写 Screen 调度。 +业务按 `features/` 拆分,每个领域按实际需要拥有 `pages`、`components`、 +`queries`、`mutations`、`services` 和 `styles`。影音实现位于 +`web/src/features/media`;登录、书架、书库、书源、设置和阅读页面也通过各自 +feature 接入同一棵路由树。旧的 `web/src/media`、旧 Screen 页面、旧手写生产路由 +和 `mountUI` 已从生产入口移除。`web/src/app.ts` 与 +`app/legacy-compat.ts` 只保留 Android 诊断和旧测试需要的兼容形状,不负责页面调度。 React 组件只声明布局和状态,副作用放在 effect、query 或 service 中。这样登录、 书架、书库、书源、设置和影音页面共享同一套生命周期语义,加载、错误、空状态和 @@ -344,7 +345,7 @@ React 组件只声明布局和状态,副作用放在 effect、query 或 servic 界面的第二个结构性问题不是「怎么画」,而是「在哪」。最初的 shell 用 `showShelf()` / `openBook()` / `showManager()` 加一个 `pendingBook` 字段来换屏, URL 从头到尾不变。手机上这一点几乎看不见——直到读者按返回键:Android 的返回手势 -交给 Activity,Activity 看到 WebView 没有历史,于是**直接从书架上退出应用**。 +交给 Activity,Activity 看到 WebView 没有历史,于是直接从书架上退出应用。 浏览器上则更糟,地址栏是自托管书库唯一的分发方式,而它既不能分享也不能收藏: 一本书链接不出去,刷新永远回到书架。 @@ -363,31 +364,49 @@ URL 从头到尾不变。手机上这一点几乎看不见——直到读者按 不再由每个 Screen 自己维护一份 history。嵌套页面通过显式返回按钮回到父路由,浏览器 的 Back/Forward 和刷新都能重建同一页面状态。 -当前核心路由是: +当前路由树的稳定入口包括: | 路由 | 页面 | | --- | --- | | `#/shelf` | 书架 | -| `#/library`、`#/library/files` | 书库浏览、文件管理 | -| `#/sources`、`#/settings` | 书源、设置 | +| `#/library`、`#/library/books`、`#/library/files` | 书库浏览、书籍列表、文件管理 | +| `#/sources` 及 `#/sources/...` | 书源中心、浏览、凭据、扩展和插件管理 | +| `#/settings` | 兼容旧深链接,重定向到书架 | | `#/book/:bookId` | 阅读器 | -| `#/media/:channel` | 影音频道首页 | -| `#/media/:channel/items/:itemId` | 影音详情 | +| `#/media/:channel` | 影音频道目录 | +| `#/media/:channel/items/:itemId` | 影音详情及管理子页 | | `#/media/:channel/player?item=&part=` | 播放器 | -| `#/media/:channel/settings/...` | 影音设置及管理子页 | +| `#/media/:channel/settings/...` | 影音设置及媒体库管理 | -书架、书库、书源、设置和影音是独立页面;影音内部再按频道、收藏、历史、队列、 -搜索、详情和管理功能拆成嵌套路由。分页、筛选和播放器参数放在路由参数或 query 中, -页面不会通过单文件里的 pathname 分支来决定渲染哪一组业务页面。 +`/sources/search`、旧的 `/sources/:sourceId/:pageId` 扩展页形状和 +`/media`、`/media/music`、`/media/audiobook` 默认入口仍由路由表兼容; +新页面应使用当前命名空间和嵌套路由。未知路径回到 `/media/video`。影音内部按频道、 +收藏、历史、队列、搜索、详情和管理功能拆成嵌套路由,分页、筛选和播放器参数放在 +路由参数或 query 中。 Hash 路由适配 WebView 和静态服务器:宿主只需要返回同一个 `index.html`,客户端从 `#` 后解析页面,不依赖服务端为每个深层路径配置回退规则。登录是认证边界而不是业务 页面;未认证时由 `AuthBoundary` 显示登录表单,认证完成后 React Router 渲染目标地址。 -阅读器是唯一的特殊生命周期边界:`ReaderPage` 创建 `ReaderScreen`,将其舞台 -通过 `StageHost` 插入 React 树,并在 effect 清理时调用 `dispose()`。页面离开后, -reader 的异步请求和分页更新不会再写入已卸载的 DOM;其它页面完全遵循 React 的 -挂载、更新和卸载语义。 +### 阅读器与长生命周期资源 + +阅读器是唯一的特殊生命周期边界:`ReaderPage` 在 effect 中取得书籍后创建 +`ReaderScreen`,把它的 `screen.element` 放入 route host,并通过 +`runtime.registerReader()` 注册进度刷新回调。页面离开时先注销回调,再调用 +`screen.dispose()` 并清空 host。ReaderScreen 内部的章节加载、分页量测、手势、 +朗读和 Shadow DOM 仍是命令式 DOM;其 chrome 使用 React,`StageHost` 只负责把 +ReaderScreen 持有的舞台节点放到 chrome 树中的正确位置。React 不介入书籍正文的布局量测。 + +播放器是应用级资源。AppProviders 把 `runtime.player.element` 挂到 +`document.body`,启动 `PlaybackService` 对 `MediaPlayer` 的监听。路由切换只卸载 +控制页面,不销毁播放会话;播放器发出 `open-controls` 时由 `AuthenticatedShell` +导航到带 `item` 和 `part` 查询参数的播放器路由。Provider 卸载时停止监听并移除 +播放器节点,应用隐藏或离开时统一调用 runtime flush,重新联网时续期会话。 + +影音主题只在 `MediaLayout` 挂载期间生效。它创建 `MediaThemeController`,作用域来自 +`runtime.mediaApi.preferenceScope()`,并只向 `body` 写入带 `--media-theme-` 前缀的 +变量;系统主题、同页主题事件和存储变化会更新该作用域。离开影音路由时清理变量和监听器, +因此不会污染书架、设置或阅读器主题。 ### 只有一份渲染层 diff --git a/docs/development.en.md b/docs/development.en.md index bdfed59..5d4310d 100644 --- a/docs/development.en.md +++ b/docs/development.en.md @@ -71,7 +71,7 @@ npm run build --prefix web Web tests use both Vitest and Node's test runner. UI reviews use real Chromium; prepare it as described above. Historical test counts are not a substitute for current results. -For UI changes, run `npm run ui:review --prefix web`; source workflows have `npm run ui:sources --prefix web`. See [UI review (中文)](ui-review/README.md) for tooling and requirements. Reviews generate screenshots and reports; screenshots are ignored by Git by default. +For UI changes, run `npm run ui:review --prefix web`; source workflows have `npm run ui:sources --prefix web`. The review scripts live under `web/tools/ui-review/` and write screenshots and reports to the Git-ignored `artifacts/ui-review/` directory. Do not commit one-off screenshots under `docs/`. ## Build and run diff --git a/docs/development.zh-CN.md b/docs/development.zh-CN.md index 2af4b64..1e2d4ca 100644 --- a/docs/development.zh-CN.md +++ b/docs/development.zh-CN.md @@ -71,7 +71,7 @@ npm run build --prefix web Web 测试同时包含 Vitest 和 Node 测试。UI 评审使用真实 Chromium;浏览器缺失时按上面的步骤准备环境。不要把历史测试数量当作当前验证结果。 -UI 改动可运行 `npm run ui:review --prefix web`,书源流程可运行 `npm run ui:sources --prefix web`。评审工具及环境说明见 [UI 评审](ui-review/README.md),会生成截图和报告;截图目录默认不纳入 Git。 +UI 改动可运行 `npm run ui:review --prefix web`,书源流程可运行 `npm run ui:sources --prefix web`。评审脚本位于 `web/tools/ui-review/`,会把截图和报告写入被 Git 忽略的 `artifacts/ui-review/`;不要把一次运行的截图提交到 `docs/`。 ## 构建与运行 diff --git a/docs/frontend-react-migration.md b/docs/frontend-react-migration.md index 9fa419b..31d8f41 100644 --- a/docs/frontend-react-migration.md +++ b/docs/frontend-react-migration.md @@ -1,121 +1,85 @@ -# 前端 React 架构重构方案 +# 前端 React 架构说明 -## 目标 +本文保留原“前端 React 迁移”文件名以避免已有链接断开,内容描述当前实现约定。生产 Web 入口已经使用 React 19;后续页面和组件应遵循本文及[架构与数据边界](architecture.md)中的边界,不再向旧的 Screen 调度链添加代码。 -将 `web` 从 Preact + 自研 Screen/Router 的混合实现迁移为完整的 React 19 应用。路由、页面生命周期、服务端状态、客户端共享状态和基础交互组件分别由成熟方案负责,业务页面按领域拆分。 +## 当前技术栈 -第一阶段优先迁移影音模块;第二阶段迁移登录、书架、书库、书源、设置等非阅读页面;最后只保留阅读内容引擎的命令式 DOM 实现,并由 React 页面托管其生命周期。 - -## 已确定的技术选择 - -| 能力 | 方案 | 责任 | +| 能力 | 当前实现 | 责任 | | --- | --- | --- | -| UI 运行时 | React 19 + React DOM | 所有页面和组件的唯一 UI 运行时 | -| 路由 | React Router 7 `createHashRouter` | Hash URL、嵌套路由、Layout、Outlet、错误边界和权限守卫 | +| UI 运行时 | React 19 + React DOM | 页面、组件和生命周期 | +| 路由 | React Router 7 `createHashRouter` | Hash URL、嵌套路由、Outlet、参数、错误边界和导航 | | 服务端状态 | `@tanstack/react-query` | 查询缓存、去重、mutation、失效和预取 | -| 客户端共享状态 | Zustand | 认证、设置、同步、播放等跨页面状态 | -| 基础组件 | Mantine | 对话框、表单、菜单、选择器、通知、加载和管理界面 | +| 客户端共享状态 | Zustand | 认证、设置、同步和播放状态 | +| 基础组件 | Mantine | 主题、表单、菜单、通知、加载和管理界面 | | 图标 | `lucide-react` | React 图标组件 | -| 构建 | Vite + TypeScript | 保持 `base: './'` 和 Android 单 Bundle 约束 | +| 构建 | Vite + TypeScript | H5 与 Android WebView 共用的相对资源单 Bundle | -Hash 路由继续使用,因为同一份构建产物由 H5 服务和 Android WebView 消费,Android 资源地址不支持任意路径 fallback。旧路由兼容不是目标;新路由以 React Router 的标准嵌套路由为准。 +继续使用 Hash 路由是因为同一份产物同时由 H5 服务和 Android WebView 加载;两个宿主都不能为任意路径提供客户端回退。新的链接应使用 `#/...` 形式。 -## 目录约定 +## 启动与运行时 + +`web/src/main.tsx` 创建 `AppRuntime`,然后调用 `createRoot()` 渲染 `AppProviders` 和 `RouterProvider`。`web/src/app/runtime.ts` 只负责组装共享资源,不选择页面: + +- 根据环境选择 Web 或 Android 平台,创建 `ReaderApi`、`MediaApi`、`MediaPlayer`、`PlaybackService`、`SyncEngine`、`OfflineStore`、`SettingsStore` 和 Query Client; +- 恢复本地会话、服务地址、离线作用域和同步状态; +- 暴露 flush、登录状态、阅读器注册和设置更新等宿主能力。 + +`AppProviders` 提供 `RuntimeContext`、`QueryClientProvider`、`MantineProvider` 和全局通知。Provider 生命周期负责启动/停止播放服务、连接会话与同步监听、写入根主题属性,并在页面隐藏或离开时刷新进度、离线数据和播放器状态,重新联网时续期会话。 + +## 目录边界 + +当前 Web 代码按以下实际目录组织: ```text web/src/ - app/ - App.tsx - providers/ - router/ - layouts/ + app/ # AppShell、Provider、运行时和 React Router features/ - auth/ - shelf/ - library/ - sources/ - media/ - pages/ - components/ - api/ - queries/ - mutations/ - stores/ - services/ - styles/ - reader/ - pages/ - components/ - services/ - shared/ - api/ - query/ - stores/ - ui/ - hooks/ - platform/ - types/ + auth/pages/ # 登录 + library/pages/ # 书库浏览和文件管理 + media/ # 影音 API、页面、组件、查询、变更、服务、状态和样式 + reader/pages/ # 阅读路由页面 + settings/pages/ # 用户与服务设置 + shelf/pages/,queries/ # 书架 + sources/hooks/,pages/ # 书源及扩展页面 + shared/query/,stores/,ui/ # 跨领域查询、状态和 UI 边界 + api/, core/, formats/, net/ # API、平台、格式和网络基础能力 + render/ # 分页、PDF、文本、朗读等渲染服务 + store/ # 离线、出版物、设备设置存储 + ui/ # 阅读器命令式舞台及可复用交互组件 + styles/ # 应用和阅读器全局样式 ``` -页面组件只处理页面展示和交互。API 调用通过领域 query/mutation hooks;跨页面状态通过领域 store;页面临时状态留在组件内部。路由页面不得通过布尔字段判断其它页面。 +页面组件只组合页面展示、查询和交互。API 请求通过领域 API、query 或 mutation 访问;跨页面状态放在 Zustand store;播放器、主题和阅读器等长生命周期行为放在 service。旧的 `web/src/media`、旧 Screen 页面、旧手写生产路由和 `mountUI` 已从生产入口移除。`web/src/app.ts` 与 `app/legacy-compat.ts` 仅保留 Android 诊断和旧测试所需的兼容形状,不负责生产页面调度。 -影音领域的 React 组件、API 类型、跨页面服务和样式统一放在 `features/media` 下;旧的 `src/media` 目录已删除,测试与 Android 诊断也直接引用新目录,没有旧媒体路径转导层。领域内同目录组件使用 `./` 导入,跨职责目录使用 `../api`、`../services`、`../styles` 等明确路径;样式唯一存放在 `styles`,避免组件目录与样式目录维护重复副本。路由入口集中在 `app/router/routes.tsx`。 +## 路由与兼容 -阅读器的命令式引擎和 Android 宿主仍保留在 `ui`/`core` 边界,由 `features/reader/pages/ReaderPage` 明确托管生命周期。`app.ts` 的诊断兼容接口只提供平台、flush 等宿主能力,不参与生产页面调度。 +路由定义集中在 `web/src/app/router/routes.tsx`,根级 `AppShell` 先经过 `AuthBoundary`,认证后渲染页面 Outlet。影音由 `MediaLayout` 提供频道上下文、主题生命周期和公共频道入口,具体页面继续按路由拆分。 -## 生命周期边界 +保留的兼容入口是有意的: -播放器是应用级服务。`PlaybackService` 管理 audio/video/native bridge、播放队列和进度保存,`playback.store` 暴露可观察状态。离开播放页面只卸载控制界面,不销毁播放会话。 +- `/settings` 重定向到 `/shelf`,兼容旧书架设置深链接; +- `/sources/search` 仍打开书源中心; +- `/sources/:sourceId/:pageId` 与新的 `/sources/:sourceId/pages/:pageId` 都可打开扩展页; +- `/media`、`/media/music` 和 `/media/audiobook` 重定向到对应频道默认页,未知路径回到 `/media/video`。 -阅读器内容仍由 `ReaderEngine`、分页器、Shadow DOM、朗读和 Android fixed-page host 负责。`ReaderPage` 使用 React effect 创建、更新和销毁引擎;React 不介入书籍内容的布局量测。 +兼容路由只保证旧链接仍能进入正确的 React 页面;新代码应直接使用当前路由树中的命名空间和嵌套路径。 -## 影音路由边界 +## 阅读器托管边界 -`MediaLayout` 只提供公共导航、频道上下文和 ``。每个可刷新、可返回的页面有独立组件,包括频道首页、分类、搜索、收藏、历史、队列、详情、元数据、匹配、章节、播放器、歌词、设置、媒体库管理、权限、任务、文件夹和文件。 +`features/reader/pages/ReaderPage.tsx` 是 React 与阅读器命令式实现之间的边界。页面取得书籍后,在 effect 中创建 `ReaderScreen`,把 `screen.element` 放入 route host,并通过 `runtime.registerReader()` 注册进度刷新回调。清理 effect 时先注销回调,再调用 `screen.dispose()` 并清空 host。 -## 迁移顺序 +`ReaderScreen` 内部仍负责章节加载、Shadow DOM、分页量测、手势、朗读和阅读工具;React 只托管它的创建、更新和销毁,不介入书籍正文的布局量测。ReaderScreen 的 chrome 使用 React 组件,正文舞台保持命令式 DOM,以确保 `getBoundingClientRect()` 和章节分页的节点生命周期稳定。 -1. React 运行时、Vite 插件、React Router、React Query、Zustand、Mantine 和应用 Provider。 -2. React 根应用、Hash Router、错误边界和权限边界。 -3. 影音 Layout、路由树、Query/Mutation 层和目录页面。 -4. 详情、元数据、章节、媒体库管理、设置和任务页面。 -5. PlaybackService、播放器页和 MiniPlayer。 -6. 删除 `MediaScreen`、旧媒体路由分支、旧 `media` 目录和媒体侧 `mountUI` 使用。 -7. 迁移其它非阅读页面。 -8. React 化阅读器外围页面,保留 ReaderEngine。 -9. 删除 Preact、`lucide-preact`、旧 `mountUI` 和旧 Screen 调度链。 +## 播放器和媒体主题生命周期 -## 验收标准 +播放器是应用级资源。`AppProviders` 把 `runtime.player.element` 挂到 `document.body`,调用 `runtime.playback.start()` 建立 `PlaybackService` 对 `MediaPlayer` 的监听;路由切换只卸载控制界面,不销毁播放会话。`AuthenticatedShell` 根据当前 URL 控制播放器可见性,并处理播放器发出的 `open-controls` 事件,将用户带到带有 `item` 和 `part` 查询参数的播放器路由。Provider 卸载时停止监听并移除播放器节点。 -- 所有页面由 React Router 管理,`App` 不再手写页面 switch。 -- `MediaScreen` 删除,旧 `src/media` 目录删除,媒体页面按 route/page/query/mutation 拆分。 -- React Query 统一管理服务端状态,Zustand 管理客户端共享状态。 -- 播放器跨路由持续播放,阅读引擎拥有明确的 React 托管生命周期。 -- Mantine 负责基础交互组件;品牌样式只做必要覆盖,不强行改写组件内部实现。 -- 保持 Android WebView 单 Bundle、相对资源和 PWA 能力。 -- 现有媒体、认证、同步、播放器、阅读器关键回归测试通过。 -- 新增页面不需要修改一个巨型 Screen 文件。 +影音主题由 `MediaLayout` 创建 `MediaThemeController`,作用域来自 `runtime.mediaApi.preferenceScope()`。控制器只向 `body` 写入带 `--media-theme-` 前缀的变量,响应系统主题、同页主题事件和存储变化;离开影音路由时清理变量和监听器,因此不会污染书架或阅读器主题。 -## 当前实施状态 - -已完成第一轮可运行切换: - -- `main.tsx` 使用 `createRoot`、`AppProviders` 和 `RouterProvider`,应用启动不再经过旧 `App.start()`。 -- 路由已改为 `createHashRouter`,影音的频道、分类、搜索、收藏、历史、队列、详情和设置入口拥有独立 React 路由。 -- 媒体列表和详情页面通过 React Query 查询层访问 `MediaApi`,基础交互使用 Mantine。 -- 书架、书库、书源和设置已建立独立 React 页面与路由,旧 Screen 不再参与新的入口调度。 -- `MediaPlayer` 已由 `PlaybackService` 管理并在 Provider 中启动,跨路由播放状态通过 Zustand 暴露,播放器 DOM 不随页面路由销毁。 -- 已用浏览器验证开发服务器能够启动并显示 React 登录页面;`npm run typecheck` 与 `npm run build` 通过。 - -当前迁移已完成生产入口切换和影音目录收敛。影音管理、播放器控制、登录、书架、书库、书源、设置和阅读路由均由 React Router 管理;旧 `MediaScreen`、旧媒体路由、旧 `mountUI`、Preact 适配层、旧 Screen 页面和 `src/media` 目录已删除。阅读内容引擎仍作为 `ReaderPage` 的命令式服务边界存在,并由 React effect 负责创建、刷新和销毁。 - -最终验证结果: - -- `npm run typecheck` 通过。 -- `npm run build` 通过;Vite 完成约 2633 个模块构建,产物仍为 Android WebView 可加载的相对资源单 Bundle。仅保留大 chunk 的性能提示,未阻断构建。 -- `npm test` 通过:97 个 Vitest 测试文件、811 个测试全部通过;Node 测试 29 项全部通过。 -- `npm run ui:review:only` 通过:40 张 React 页面截图,详情、视频播放、历史和队列页面均完成视觉回归。 -- `git diff --check` 通过;输出的换行符提示属于工作区 CRLF 转换提示,不是 whitespace error。 - -至此生产入口已经完成 React 化。后续新增页面应继续按 `app / features / shared` 边界实现,由 React Router 接入路由,不再向旧 Screen 调度链添加逻辑。 +## 后续改动约定 +- 新页面放入对应的 `features//pages`,通过 `routes.tsx` 接入; +- 领域组件、查询、变更、服务和样式留在各自 feature 内,跨领域依赖经过 `shared` 或 `app` 边界; +- 需要长生命周期的资源必须由 Provider 或明确的 service 管理,并在清理阶段释放; +- 阅读器正文继续通过 `ReaderScreen` 托管,不把命令式舞台改写为普通 React 子树; +- 新链接使用 Hash 路由和当前嵌套路由,不新增旧 Screen 或手写 pathname 分支。 diff --git a/docs/media-implementation.md b/docs/media-implementation.md index 2321fa6..eb5b7f3 100644 --- a/docs/media-implementation.md +++ b/docs/media-implementation.md @@ -29,4 +29,4 @@ Web 音频使用浏览器媒体能力,视频采用 DPlayer;Android 使用 Me - 服务端:[media 模块](../server/src/media/)、[HTTP 路由](../server/src/http/routes/media.ts)。 - 前端:[media feature 模块](../web/src/features/media/)、[React 页面路由](../web/src/app/router/routes.tsx)。 - [使用说明](media-user-guide.zh-CN.md)、[导航约定](media-navigation.md)、[验证方式](media-validation.md)、[性能测量](media-catalog-performance.md)。 -- [冻结的 v2 原型](prototypes/media/v2-review/index.html)是布局参考;实际功能与地址以源码及当前说明为准。 +- [冻结的 v2 原型](prototypes/media/v2-review/index.html)是布局参考;实际功能、地址和主题生命周期以源码及当前说明为准。浏览器评审截图和报告统一写入被忽略的 `artifacts/media`,不再提交到 `docs/`。 diff --git a/docs/media-navigation.md b/docs/media-navigation.md index 6d9e9d4..52c39b2 100644 --- a/docs/media-navigation.md +++ b/docs/media-navigation.md @@ -31,6 +31,6 @@ channel 表示 video、music 或 audiobook;itemId、part、libraryId 均为真 | 文件夹、文件 | #/media/:channel/folders/:libraryId?path=…&offset=…;文件继续使用该路由并追加 asset=… | | 演播者、演播作品 | #/media/audiobook/narrators/:narrator,以及 /media/audiobook/narrators/:narrator/works/:workId | -旧路由不承担兼容承诺。返回按实际浏览器历史和保存的来源状态处理;不为每个页面生成 return 查询参数。播放器的 item、part 查询参数保存作品及播放部分,可刷新后重新校验授权并恢复页面。 +路由表之外保留少量旧深链接兼容:`/media`、`/media/music` 和 `/media/audiobook` 会进入对应频道默认页;未知媒体路径回到 `/media/video`。书源与系统旧入口的兼容规则见[React 架构说明](frontend-react-migration.md)。返回按实际浏览器历史和保存的来源状态处理;不为每个页面生成 return 查询参数。播放器的 item、part 查询参数保存作品及播放部分,可刷新后重新校验授权并恢复页面。 协议实现以[React 页面路由](../web/src/app/router/routes.tsx)及[影音 feature](../web/src/features/media/)为准。冻结 v2 原型内的示例路由只用于设计演示。 diff --git a/docs/media-validation.md b/docs/media-validation.md index be40459..63db061 100644 --- a/docs/media-validation.md +++ b/docs/media-validation.md @@ -10,7 +10,7 @@ 先构建 web,再从 web 目录运行: -- `npm run ui:review:only`:启动真实服务端媒体夹具,使用生产 React bundle 在 390/1120px 检查书架、书库、影音频道、详情、收藏、播放器、设置和权限页面;同时覆盖搜索、收藏筛选分页、收藏写回、播放会话恢复、媒体库增改和普通用户权限边界。报告写入忽略的 `artifacts/ui-review/react-app/verification.json`,要求 `passed=true`、38 张截图、无页面错误和横向溢出。 +- `npm run ui:review:only`:启动真实服务端媒体夹具,使用生产 React bundle 在 390/1120px 检查书架、书库、影音频道、详情、收藏、播放器、设置和权限页面;同时覆盖搜索、收藏筛选分页、收藏写回、播放会话恢复、媒体库增改和普通用户权限边界。报告写入忽略的 `artifacts/ui-review/react-app/verification.json`,以报告中的 `passed`、`screenshots`、页面错误和横向溢出结果为准,不把历史截图数量写入文档。 - node tools/media-desktop-openlist-review.cjs:桌面分类、选择器、收藏、OpenList 表单及视口边界。 - node tools/media-openlist-e2e.cjs:生产页面、真实 Reader 服务、临时数据库,以及本机模拟 OpenList 的建库、扫描、播放和凭据维护闭环。 diff --git a/docs/p1-progress-2026-09-23.md b/docs/p1-progress-2026-09-23.md deleted file mode 100644 index 03f30bd..0000000 --- a/docs/p1-progress-2026-09-23.md +++ /dev/null @@ -1,40 +0,0 @@ -# P1 实施与验证记录(2026-09-23) - -本次实现原需求分析 D/E/F 的阅读工具主流程,并补充万本索引的查询基线。改动保留在工作区,未提交、未部署。 - -## 已实现 - -| 能力 | 交付行为 | -| --- | --- | -| 书内检索 | EPUB/TXT逐章搜索、摘要、200 条上限、取消、命中高亮、返回原阅读位置;不对图片或扫描 PDF 做 OCR | -| 批注 | 正文选区高亮、批注、书签、编辑、删除、回正文;沿用现有离线同步和墓碑机制;修复同 ID 笔记跨账号覆盖 | -| EPUB 目录 | Web/服务端解析有序 nav/NCX 树,保留层级、fragment、spine,支持折叠和章内定位 | -| 个人纠错 | 精确文字替换、选区过滤、移除及撤销;按账号/书籍保存在 DATA_DIR/reader.db,版本冲突返回 409;不改原书 | -| TXT 目录 | 中文、英文、方括号模板和自定义标题前缀;预览后应用,规则持久化并可撤销 | -| 设备离线 | TXT范围下载;EPUB/PDF/CBZ 整文件下载,保留 EPUB 内部资源;暂停、继续、取消;失败可重试并跳过完成资源 | -| 缓存管理 | 服务地址与账号隔离;默认 256 MiB 配额、实际资源字节占用、按书清理;配额写入串行,失败回滚旧资源 | -| 朗读诊断 | 当前引擎试听;HTTP 实际合成检测,分别报告配置、鉴权、响应体超时、非音频、空音频;停止和关面板释放试听 | -| 大书库基线 | 10,000 条索引的首尾分页、搜索、增量同步验证,单次查询按页返回 | - -主要实现位于 web/src/ui/reading-tools.tsx、text-anchor.ts、reading-overrides.ts、core/offline-download.ts、store/publications.ts,以及 server/src/services/reading-overrides.ts 和对应路由。 - -## 验证 - -- Web 全量 Vitest:40 个测试文件、585 项通过。此后新增 1 项下载重试/自定义 TXT 测试,相关 34 项定向回归通过。 -- Web Node 测试:29 项通过。 -- 服务端全量:338 项,336 通过、2 项 Windows 环境跳过;最后的覆盖层参数校验和万本测试再次通过。 -- 两端 TypeScript 检查、生产构建通过;git diff --check 通过。 -- 真实 Chromium + 真实 HTTP/SQLite + 生产 Web:320/390/1280 宽度无横向溢出;搜索高亮、返回、三级目录折叠/展开、选区批注、整 EPUB 缓存后阻断所有 API 并重开、纠错刷新保留/撤销、TXT 预览应用通过。 -- 浏览器验证脚本:web/tools/ui-review/p1-reading.mjs。在 web 运行 node --import tsx tools/ui-review/p1-reading.mjs;截图在 docs/ui-review/p1/。 -- 万本基线:内存 SQLite,10,000 条书籍/文件/书架记录;本机独立运行三次查询合计约 91ms。并发测试受负载影响,数值只用于基线,不作为 NAS/机械盘性能承诺。 - -## 明确边界 - -- 本轮完成的是上述可用流程;真实 iOS/Android、HTTPS 反代、后台音频和跨真机恢复尚待实机验收。 -- 未做磁盘上万本真实书籍的全量/增量扫描压测;内存索引基线不能替代该验收。 -- TXT 自定义规则是有界标题前缀匹配,不支持任意正则或跨书自动套用的规则库;变更章节结构后从首章打开,旧定位可能失效。 -- 纠错规则联网保存,已缓存规则离线应用;其他设备下次打开书籍拉取,不通过进度同步接口实时推送。空替换只过滤选区,未实现全书正则过滤。 -- 下载任务关闭面板即暂停。整文件下载重试从头请求;章节下载按已完成资源续传。图片目录不支持整书预下载。 -- 配额统计是新账号作用域下的正文/整文件缓存,未包含封面、服务端缓存和旧版本非账号隔离缓存;不自动淘汰。 -- 浏览器缓存可能被系统回收;未缓存的章节仍需联网。正文版本变化或目录重排后的旧笔记不会强行匹配到不确定文本。 -- Docker 引擎未启动,本轮未做容器和实际部署验收。 diff --git a/docs/prototypes/media/README.md b/docs/prototypes/media/README.md index 1aacbb1..bce558d 100644 --- a/docs/prototypes/media/README.md +++ b/docs/prototypes/media/README.md @@ -1,9 +1,7 @@ # 影音设计参考 -本目录保留当前仍使用的设计基准和最近一轮参考图集。早期原型、反复覆盖的截图、失效的构建哈希和逐轮进度记录已移出仓库。 +本目录只保留当前仍使用的设计基准。早期原型、反复覆盖的截图、失效的构建哈希和逐轮进度记录已移出仓库。 - [冻结 v2 高保真原型](v2-review/index.html):已采用的布局基准,包含桌面和手机、主题及状态。示例内容和旧示例路由不代表生产数据或当前路由协议。 -- [桌面分类、选择器、收藏和 OpenList 表单](desktop-openlist-review/index.html):最近一轮实际页面与参考对照。 -- [OpenList 浏览器闭环](openlist-e2e-review/index.html):使用本机模拟上游的生产页面流程,未连接用户真实实例。 -后续验收结果生成到 artifacts/media(已忽略),需要长期保留的截图应人工挑选;不要提交整批重复截图。运行方式见[验证说明](../../media-validation.md)。 +后续验收结果生成到被 Git 忽略的 `artifacts/media`,需要长期保留的截图应人工挑选;不要提交整批重复截图。运行方式见[验证说明](../../media-validation.md)。 diff --git a/docs/prototypes/media/desktop-openlist-review/README.md b/docs/prototypes/media/desktop-openlist-review/README.md deleted file mode 100644 index 79c8c18..0000000 --- a/docs/prototypes/media/desktop-openlist-review/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# 桌面分类、选择器、收藏与 OpenList 复核 - -[布局与下拉截图](index.html) · [OpenList 浏览器闭环](../openlist-e2e-review/index.html) · [OpenList 配置说明](../../../media-openlist.md) - -## 本次修正 - -- 桌面分类栏原来的固定父容器容纳不了 tab 的高度与外边距,造成文字、下划线和分隔线错位。改为随内容计算父栏高度,tab 保持 44px,桌面顶部间距 5px、底部间距 16px。 -- 影音选择器改为统一主题弹层,覆盖媒体库、版本、季、排序、权限、播放偏好等;支持选中标记、键盘操作、外部点击和 Escape 关闭,靠近视口底部向上展开。Plyr 自带控件不在替换范围。 -- 收藏统一到 `#/media/favorites`,旧频道收藏链接继续可用。类型筛选放在弹窗内,`scope` 与分页随 URL 恢复。列表按 v2 使用最大 850px 的居中内容区、紧凑行、计数和筛选入口。 -- 新建媒体库增加 OpenList。输入服务地址、远端目录,以及可选令牌和目录密码;创建前验证连接,随后扫描。编辑页面只显示凭据是否已配置,可更新或显式清除,留空保留。 - -## 视觉核对 - -生产构建在 390、1120、1308px 下生成 20 张截图。人工检查了桌面分类栏、浅色与深色下拉、收藏列表、新建 OpenList 表单和窄屏向上展开的下拉。图集中分类和收藏附有冻结 v2 原型对照;原型本身未修改。原型中的播放进度、数量和资料仅为示例,实现按可用数据展示。收藏的规范路由按本次需求更新,冻结原型内旧路由文案不代表本次实现。 - -结构断言验证 tab 高度、按钮边界、下划线、隐藏滚动条、列表居中宽度、菜单视口边界及页面无横向溢出。交互验证旧路由规范化、收藏筛选刷新恢复和选择器关闭。结构断言用于补充图片审查。 - -## 功能验证与边界 - -- 前端全量 Vitest 运行通过;新增选择器交互与收藏行为另有定向回归,生产构建通过。 -- 后端 OpenList、本地存储、扫描、权限、元数据、迁移和启动隔离相关回归通过。依赖 FFmpeg 的 5 项真实内嵌资源测试因环境未配置而跳过,未记为通过。 -- 浏览器闭环使用生产页面、真实 Reader 接口、扫描器与临时 SQLite 数据库;OpenList 上游是本机同契约模拟服务。验证了创建、扫描、实际解码播放、精确 Range 字节读取、凭据保留与轮换,以及下载请求不转发 API 令牌。 -- 未连接用户的真实 OpenList 或具体网盘驱动;未进行 Android 真机测试。OpenList 当前使用服务端代理播放,远端文件暂不读取内嵌标签、封面和章节,支持外置资料;不包含转码。 -- 阅读页面、书架设置与默认阅读入口保持原有行为。 - -复跑命令(从 `web` 目录运行,先完成 `npm run build`): - -```powershell -node tools/media-desktop-openlist-review.cjs -node tools/media-openlist-e2e.cjs -``` - -可通过 `PROTOTYPE_CHROMIUM` 指定本机 Chromium 路径。所有验收服务和数据均为临时实例。 diff --git a/docs/prototypes/media/desktop-openlist-review/access-select-1120-actual.png b/docs/prototypes/media/desktop-openlist-review/access-select-1120-actual.png deleted file mode 100644 index 0d61352..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/access-select-1120-actual.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/access-select-1308-actual.png b/docs/prototypes/media/desktop-openlist-review/access-select-1308-actual.png deleted file mode 100644 index 2e6c1eb..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/access-select-1308-actual.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/access-select-390-actual.png b/docs/prototypes/media/desktop-openlist-review/access-select-390-actual.png deleted file mode 100644 index 19d22ae..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/access-select-390-actual.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/checks.json b/docs/prototypes/media/desktop-openlist-review/checks.json deleted file mode 100644 index 46e3794..0000000 --- a/docs/prototypes/media/desktop-openlist-review/checks.json +++ /dev/null @@ -1,217 +0,0 @@ -{ - "checks": [ - { - "key": "tabs-390", - "overflow": 0 - }, - { - "key": "tab-geometry-390", - "top": 56, - "bottom": 100, - "height": 44, - "buttonTop": 56, - "buttonBottom": 100, - "scrollbar": "none", - "border": "2px" - }, - { - "key": "library-select-390", - "overflow": 0 - }, - { - "key": "favorites-390", - "overflow": 0 - }, - { - "key": "favorite-filter-390", - "overflow": 0 - }, - { - "key": "openlist-390", - "overflow": 0 - }, - { - "key": "access-select-390", - "overflow": 0 - }, - { - "key": "tabs-1120", - "overflow": 0 - }, - { - "key": "tab-geometry-1120", - "top": 73, - "bottom": 117, - "height": 44, - "buttonTop": 73, - "buttonBottom": 117, - "scrollbar": "none", - "border": "2px" - }, - { - "key": "library-select-1120", - "overflow": 0 - }, - { - "key": "favorites-1120", - "overflow": 0 - }, - { - "key": "favorite-filter-1120", - "overflow": 0 - }, - { - "key": "openlist-1120", - "overflow": 0 - }, - { - "key": "access-select-1120", - "overflow": 0 - }, - { - "key": "tabs-1308", - "overflow": 0 - }, - { - "key": "tab-geometry-1308", - "top": 73, - "bottom": 117, - "height": 44, - "buttonTop": 73, - "buttonBottom": 117, - "scrollbar": "none", - "border": "2px" - }, - { - "key": "library-select-1308", - "overflow": 0 - }, - { - "key": "favorites-1308", - "overflow": 0 - }, - { - "key": "favorite-filter-1308", - "overflow": 0 - }, - { - "key": "openlist-1308", - "overflow": 0 - }, - { - "key": "access-select-1308", - "overflow": 0 - }, - { - "key": "video-desktop", - "overflow": 0 - }, - { - "key": "select-dark", - "overflow": 0 - } - ], - "captures": [ - { - "key": "tabs-390", - "title": "频道分类 · 390", - "reference": true - }, - { - "key": "library-select-390", - "title": "媒体库下拉 · 390", - "reference": false - }, - { - "key": "favorites-390", - "title": "收藏列表 · 390", - "reference": true - }, - { - "key": "favorite-filter-390", - "title": "收藏类型筛选 · 390", - "reference": false - }, - { - "key": "openlist-390", - "title": "新建 OpenList 媒体库 · 390", - "reference": false - }, - { - "key": "access-select-390", - "title": "表单下拉 · 390", - "reference": false - }, - { - "key": "tabs-1120", - "title": "频道分类 · 1120", - "reference": true - }, - { - "key": "library-select-1120", - "title": "媒体库下拉 · 1120", - "reference": false - }, - { - "key": "favorites-1120", - "title": "收藏列表 · 1120", - "reference": true - }, - { - "key": "favorite-filter-1120", - "title": "收藏类型筛选 · 1120", - "reference": false - }, - { - "key": "openlist-1120", - "title": "新建 OpenList 媒体库 · 1120", - "reference": false - }, - { - "key": "access-select-1120", - "title": "表单下拉 · 1120", - "reference": false - }, - { - "key": "tabs-1308", - "title": "频道分类 · 1308", - "reference": false - }, - { - "key": "library-select-1308", - "title": "媒体库下拉 · 1308", - "reference": false - }, - { - "key": "favorites-1308", - "title": "收藏列表 · 1308", - "reference": false - }, - { - "key": "favorite-filter-1308", - "title": "收藏类型筛选 · 1308", - "reference": false - }, - { - "key": "openlist-1308", - "title": "新建 OpenList 媒体库 · 1308", - "reference": false - }, - { - "key": "access-select-1308", - "title": "表单下拉 · 1308", - "reference": false - }, - { - "key": "video-desktop", - "title": "影视频道顶部分类", - "reference": false - }, - { - "key": "select-dark", - "title": "深海主题下拉", - "reference": false - } - ], - "errors": [] -} \ No newline at end of file diff --git a/docs/prototypes/media/desktop-openlist-review/favorite-filter-1120-actual.png b/docs/prototypes/media/desktop-openlist-review/favorite-filter-1120-actual.png deleted file mode 100644 index 603526b..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/favorite-filter-1120-actual.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/favorite-filter-1308-actual.png b/docs/prototypes/media/desktop-openlist-review/favorite-filter-1308-actual.png deleted file mode 100644 index 8c540be..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/favorite-filter-1308-actual.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/favorite-filter-390-actual.png b/docs/prototypes/media/desktop-openlist-review/favorite-filter-390-actual.png deleted file mode 100644 index 61f73b0..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/favorite-filter-390-actual.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/favorites-1120-actual.png b/docs/prototypes/media/desktop-openlist-review/favorites-1120-actual.png deleted file mode 100644 index d34ec50..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/favorites-1120-actual.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/favorites-1120-reference.png b/docs/prototypes/media/desktop-openlist-review/favorites-1120-reference.png deleted file mode 100644 index ce7a6ab..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/favorites-1120-reference.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/favorites-1308-actual.png b/docs/prototypes/media/desktop-openlist-review/favorites-1308-actual.png deleted file mode 100644 index 2d7211d..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/favorites-1308-actual.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/favorites-390-actual.png b/docs/prototypes/media/desktop-openlist-review/favorites-390-actual.png deleted file mode 100644 index 6be9479..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/favorites-390-actual.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/favorites-390-reference.png b/docs/prototypes/media/desktop-openlist-review/favorites-390-reference.png deleted file mode 100644 index de796fd..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/favorites-390-reference.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/index.html b/docs/prototypes/media/desktop-openlist-review/index.html deleted file mode 100644 index 2e67aae..0000000 --- a/docs/prototypes/media/desktop-openlist-review/index.html +++ /dev/null @@ -1 +0,0 @@ -桌面分类、收藏与 OpenList 验收

桌面分类、收藏与 OpenList 验收

使用生产构建。对照冻结 v2 原型的分类栏与收藏布局;下拉和 OpenList 接入表单为本次新增。展示数据是临时验收数据,未写入用户媒体库。OpenList 接入另由接口集成测试验证,页面中地址为示例。

频道分类 · 390

冻结的 v2 原型
本次实现

媒体库下拉 · 390

本次实现

收藏列表 · 390

冻结的 v2 原型
本次实现

收藏类型筛选 · 390

本次实现

新建 OpenList 媒体库 · 390

本次实现

表单下拉 · 390

本次实现

频道分类 · 1120

冻结的 v2 原型
本次实现

媒体库下拉 · 1120

本次实现

收藏列表 · 1120

冻结的 v2 原型
本次实现

收藏类型筛选 · 1120

本次实现

新建 OpenList 媒体库 · 1120

本次实现

表单下拉 · 1120

本次实现

频道分类 · 1308

本次实现

媒体库下拉 · 1308

本次实现

收藏列表 · 1308

本次实现

收藏类型筛选 · 1308

本次实现

新建 OpenList 媒体库 · 1308

本次实现

表单下拉 · 1308

本次实现

影视频道顶部分类

本次实现

深海主题下拉

本次实现
\ No newline at end of file diff --git a/docs/prototypes/media/desktop-openlist-review/library-select-1120-actual.png b/docs/prototypes/media/desktop-openlist-review/library-select-1120-actual.png deleted file mode 100644 index fa8ae47..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/library-select-1120-actual.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/library-select-1308-actual.png b/docs/prototypes/media/desktop-openlist-review/library-select-1308-actual.png deleted file mode 100644 index 832d965..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/library-select-1308-actual.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/library-select-390-actual.png b/docs/prototypes/media/desktop-openlist-review/library-select-390-actual.png deleted file mode 100644 index a4234e8..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/library-select-390-actual.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/openlist-1120-actual.png b/docs/prototypes/media/desktop-openlist-review/openlist-1120-actual.png deleted file mode 100644 index c7b67d0..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/openlist-1120-actual.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/openlist-1308-actual.png b/docs/prototypes/media/desktop-openlist-review/openlist-1308-actual.png deleted file mode 100644 index e38d676..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/openlist-1308-actual.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/openlist-390-actual.png b/docs/prototypes/media/desktop-openlist-review/openlist-390-actual.png deleted file mode 100644 index a139b74..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/openlist-390-actual.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/select-dark-actual.png b/docs/prototypes/media/desktop-openlist-review/select-dark-actual.png deleted file mode 100644 index 8544fc9..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/select-dark-actual.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/tabs-1120-actual.png b/docs/prototypes/media/desktop-openlist-review/tabs-1120-actual.png deleted file mode 100644 index 5e3b31e..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/tabs-1120-actual.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/tabs-1120-reference.png b/docs/prototypes/media/desktop-openlist-review/tabs-1120-reference.png deleted file mode 100644 index 94840ea..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/tabs-1120-reference.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/tabs-1308-actual.png b/docs/prototypes/media/desktop-openlist-review/tabs-1308-actual.png deleted file mode 100644 index 14cbf4e..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/tabs-1308-actual.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/tabs-390-actual.png b/docs/prototypes/media/desktop-openlist-review/tabs-390-actual.png deleted file mode 100644 index 83fbf63..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/tabs-390-actual.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/tabs-390-reference.png b/docs/prototypes/media/desktop-openlist-review/tabs-390-reference.png deleted file mode 100644 index 39cb023..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/tabs-390-reference.png and /dev/null differ diff --git a/docs/prototypes/media/desktop-openlist-review/video-desktop-actual.png b/docs/prototypes/media/desktop-openlist-review/video-desktop-actual.png deleted file mode 100644 index 462bc6a..0000000 Binary files a/docs/prototypes/media/desktop-openlist-review/video-desktop-actual.png and /dev/null differ diff --git a/docs/prototypes/media/openlist-e2e-review/01-create-openlist.png b/docs/prototypes/media/openlist-e2e-review/01-create-openlist.png deleted file mode 100644 index a50d5a6..0000000 Binary files a/docs/prototypes/media/openlist-e2e-review/01-create-openlist.png and /dev/null differ diff --git a/docs/prototypes/media/openlist-e2e-review/02-scanned-track.png b/docs/prototypes/media/openlist-e2e-review/02-scanned-track.png deleted file mode 100644 index d8ca726..0000000 Binary files a/docs/prototypes/media/openlist-e2e-review/02-scanned-track.png and /dev/null differ diff --git a/docs/prototypes/media/openlist-e2e-review/03-remote-audio-playing.png b/docs/prototypes/media/openlist-e2e-review/03-remote-audio-playing.png deleted file mode 100644 index 6f50e38..0000000 Binary files a/docs/prototypes/media/openlist-e2e-review/03-remote-audio-playing.png and /dev/null differ diff --git a/docs/prototypes/media/openlist-e2e-review/04-redacted-edit.png b/docs/prototypes/media/openlist-e2e-review/04-redacted-edit.png deleted file mode 100644 index 6df4bc9..0000000 Binary files a/docs/prototypes/media/openlist-e2e-review/04-redacted-edit.png and /dev/null differ diff --git a/docs/prototypes/media/openlist-e2e-review/checks.json b/docs/prototypes/media/openlist-e2e-review/checks.json deleted file mode 100644 index 0b9d183..0000000 --- a/docs/prototypes/media/openlist-e2e-review/checks.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "date": "2026-09-27T09:10:13.223Z", - "scope": "生产构建页面及真实 Reader HTTP 接口、数据库、扫描与播放服务;OpenList 上游由本机模拟,未使用 page.route 模拟业务接口。", - "checks": [ - "生产页面表单向真实 Reader 接口提交 OpenList 连接、音乐类型、访问范围和防重复创建的请求标识。", - "真实扫描任务完成并读取远程 NFO;服务地址和目录按规范保存,配置接口只返回凭据是否已配置,不回显令牌或密码。", - "Chromium 通过 Reader 代理成功解码并播放生成的 WAV;指定范围读取精确返回 20 字节、206 状态和正确的 Content-Range。", - "编辑页不回显凭据;改名时保留原令牌与目录密码,单独更新令牌后保留目录密码,后续真实范围读取继续成功。", - "OpenList 授权头没有转发给原始媒体下载地址;浏览器没有页面运行错误。" - ], - "screenshots": [ - "01-create-openlist", - "02-scanned-track", - "03-remote-audio-playing", - "04-redacted-edit" - ], - "upstreamCalls": 16, - "upstreamRanges": [ - "bytes=0-352843", - "bytes=44-63", - "bytes=64-83" - ], - "errors": [] -} \ No newline at end of file diff --git a/docs/prototypes/media/openlist-e2e-review/index.html b/docs/prototypes/media/openlist-e2e-review/index.html deleted file mode 100644 index c641a50..0000000 --- a/docs/prototypes/media/openlist-e2e-review/index.html +++ /dev/null @@ -1 +0,0 @@ -OpenList 浏览器验收

OpenList 创建、扫描与播放验收

生产构建页面、真实 Reader 服务与临时数据库。OpenList 上游为本机模拟服务;音频为程序生成的 8 秒 WAV。未拦截或模拟 Reader 业务 API。

  • 生产页面表单向真实 Reader 接口提交 OpenList 连接、音乐类型、访问范围和防重复创建的请求标识。
  • 真实扫描任务完成并读取远程 NFO;服务地址和目录按规范保存,配置接口只返回凭据是否已配置,不回显令牌或密码。
  • Chromium 通过 Reader 代理成功解码并播放生成的 WAV;指定范围读取精确返回 20 字节、206 状态和正确的 Content-Range。
  • 编辑页不回显凭据;改名时保留原令牌与目录密码,单独更新令牌后保留目录密码,后续真实范围读取继续成功。
  • OpenList 授权头没有转发给原始媒体下载地址;浏览器没有页面运行错误。
01-create-openlist
02-scanned-track
03-remote-audio-playing
04-redacted-edit
\ No newline at end of file diff --git a/docs/prototypes/media/v2-review/index.html b/docs/prototypes/media/v2-review/index.html index c238e10..857aefd 100644 --- a/docs/prototypes/media/v2-review/index.html +++ b/docs/prototypes/media/v2-review/index.html @@ -12,7 +12,7 @@ .app .seek::-webkit-slider-runnable-track{height:4px;border-radius:4px;background:linear-gradient(to right,var(--accent) 38%,var(--line) 38%)}.app .seek::-webkit-slider-thumb{-webkit-appearance:none;width:12px;height:12px;border-radius:50%;background:var(--accent);margin-top:-4px;border:0}.app .seek::-moz-range-track{height:4px;border:0;border-radius:4px;background:var(--line)}.app .seek::-moz-range-progress{height:4px;background:var(--accent)}.app .seek::-moz-range-thumb{width:12px;height:12px;border-radius:50%;background:var(--accent);border:0}.app .plyr-demo{--accent:#c0d4a6;--line:#778279}.app .video-layout.film-layout{display:block;max-width:960px;margin:auto}@container(min-width:700px){.app .film-layout .video-info{max-width:900px;margin:auto}.app .film-layout .video-tool-row{max-width:440px}}
reader. 影音设计评审 / v2待确认方案 · 全部为示例内容与模拟交互 · 未修改业务页面
-

设计提案 · 等待确认

审查报告与优化优先级截图总览历史原型原型不连接服务、不读取账号、不播放真实媒体。阅读及书架设置沿用现有界面。

+

冻结设计基准

验证方式原型不连接服务、不读取账号、不播放真实媒体。阅读及书架设置沿用现有界面。