hectorqin
ff976a4cf6
feat(settings): manage business configuration from admin pages
2026-09-28 12:38:54 +08:00
hectorqin
996f0c555e
fix(media): accelerate remote scans and expose scan diagnostics
2026-09-28 10:58:02 +08:00
hectorqin
73f9eb6e20
feat(media): add isolated media libraries and playback
...
Support local and OpenList libraries, metadata, independent media navigation, themes, and audio/video playback. Preserve approved v2 prototypes and current documentation; archive superseded review artifacts and keep generated validation output outside version control.
2026-09-27 18:22:51 +08:00
hectorqin
06b3d09e22
feat(admin): manage users and invite-based registration
2026-09-24 17:19:48 +08:00
hectorqin
316dd72611
fix(auth): retain renewable sessions and support LAN annotations
2026-09-24 16:59:28 +08:00
hectorqin
1d86efab34
feat(reader): add selection annotations and server-side book search
2026-09-24 16:03:35 +08:00
hectorqin
0b7f901db5
chore(server): change default listening port to 5888
2026-09-24 14:50:13 +08:00
hectorqin
563a321898
fix(docker): include system Chromium for Legado WebView
2026-09-24 12:07:52 +08:00
hectorqin
2d85c39756
feat(search): log stream termination reasons and plugin diagnostics
2026-09-24 11:48:42 +08:00
hectorqin
524d015b35
feat(plugins): load dependent forms on tab and selection changes
2026-09-24 11:26:57 +08:00
hectorqin
4cc7bff5f0
feat(plugins): add rule debugging workbench layout
2026-09-24 11:09:27 +08:00
hectorqin
aa7aee051d
feat(sources): organize source login and plugin management
2026-09-24 10:32:47 +08:00
hectorqin
9918141705
fix(docker): normalize shell entrypoint line endings
2026-09-24 07:53:10 +08:00
hectorqin
7bff77eaf8
feat(reader): deliver P0-P2 reading tools and ecosystem integrations
...
Add reliable reading progress, verified backup and restore, reading tools, offline downloads, source quality checks, personal credential status, OPDS serving and verified WebDAV backup uploads.
2026-09-24 07:47:32 +08:00
hectorqin
e3cabf4b1f
feat(web): add PWA installation and offline app shell
2026-09-24 06:34:21 +08:00
hectorqin
95cb3134db
feat(sources): support default source and refine switching UI
2026-09-23 15:16:15 +08:00
hectorqin
4896e5efc7
feat(reader): stream alternative sources and refresh current chapters
...
Add book information dialogs and compact toolbar actions with source provenance. Reuse Streamable HTTP for alternative discovery and preserve cached text when a forced refresh fails.
2026-09-23 13:16:54 +08:00
hectorqin
0a468992be
feat(plugins): update installed packages while preserving source data
2026-09-23 12:42:14 +08:00
hectorqin
cb96d48d62
fix(search): stop on stream disconnect and allow concurrent book actions
2026-09-23 11:41:24 +08:00
hectorqin
a852682e0c
fix(plugins): isolate all request timeouts and cancellations
2026-09-23 11:14:41 +08:00
hectorqin
0782202dae
fix(plugins): isolate search stop failures and float operation feedback
...
A searchCancel deadline previously killed the shared plugin process and prevented subsequent acquisition. Release only the stop request and allow late cleanup to finish. Keep ordinary hard cancellation unchanged. Cover process and HTTP acquisition regressions and compact extension feedback without layout shifts.
2026-09-23 09:46:02 +08:00
hectorqin
d7ebc3c898
feat(plugins): support web npm and tgz installation
2026-09-22 19:21:37 +08:00
hectorqin
72c4405435
chore: keep host repository plugin agnostic
2026-09-22 17:47:21 +08:00
hectorqin
4af639d3b8
docs: document streamed source search and upload semantics
2026-09-22 16:54:18 +08:00
hectorqin
be219e5090
docs: align library write model and deployment guidance
2026-09-22 16:49:06 +08:00
hectorqin
a131a8cc45
feat(plugins): add bounded diagnostic output
2026-09-22 15:56:22 +08:00
hectorqin
5f8554eadd
feat(search): stream source results over HTTP
2026-09-22 14:38:36 +08:00
hectorqin
171297dc39
feat(search): add rolling multi-source sessions
2026-09-22 13:33:44 +08:00
hectorqin
b49caf0ffc
feat(sources): add cancellable batch search and configurable concurrency
2026-09-22 10:30:46 +08:00
hectorqin
3420bf07ce
fix(library): 限定管理员上传并完善文件管理导航与布局
2026-09-21 15:25:28 +08:00
hectorqin
8e8ca23d3b
fix(sources): 完善配置交互并增加真实端到端测试
2026-09-21 13:03:30 +08:00
hectorqin
c27c505f24
feat(sources): 按书源实例隔离插件配置并拆分管理 Tab
2026-09-21 12:28:50 +08:00
hectorqin
964a666f5b
feat(plugins): 支持独立配置页、多书源订阅与目录换源
2026-09-21 11:50:21 +08:00
hectorqin
5ecf918551
feat(plugins): support independently installed npm providers
2026-09-21 10:29:17 +08:00
hectorqin
c192fc2f12
feat(sources): add configuration pages and chapter updates
2026-09-20 23:59:12 +08:00
hectorqin
a6fddab953
feat(reader): 接入插件章节阅读与目录刷新
...
接入远程章节出版物、稳定定位、服务端与设备端缓存及手动刷新;补充账号隔离、同步并发保护、API 和设计文档。
2026-09-20 18:40:09 +08:00
hectorqin
037bdd6880
feat(sources): 增加内置 local、OPDS 与进程插件系统
2026-09-20 17:51:12 +08:00
hectorqin
31b5459a47
fix(web,server): 下架按下架的书 id 定位,书库两半各归其位
2026-09-20 12:32:43 +08:00
hectorqin
ae397b6181
feat(web,server): 补全书架的加入/移除两侧并统一可见性判据
2026-09-20 11:52:15 +08:00
hectorqin
558f44e206
feat(web,server): 书架默认按最近阅读排,书库拆成预览与文件两页
2026-09-19 14:17:55 +08:00
hectorqin
f2701da703
fix(web,server): 继续阅读带上完整书对象,书库能上架,放大镜回到中线
...
三处 #40 后续:继续阅读卡片点了报「不在书架上」、书库的书加不回书架、
书架搜索框的放大镜高了一行。
2026-09-19 11:46:42 +08:00
hectorqin
69993d7b37
Merge remote-tracking branch 'origin/main' into auto/reader-paragraphs-layout-images-eb50
2026-09-19 09:13:54 +08:00
hectorqin
0551f21e4e
fix(web,server): 段落不再合成一块,插图重新画出来,工具栏对齐
2026-09-19 09:09:12 +08:00
hectorqin
d489c45cf3
Merge remote-tracking branch 'origin/main' into auto/shelf-library-pagination-d4fc
2026-09-19 08:49:21 +08:00
hectorqin
c7d065bd52
feat(web): 书架与书库拆成两个可分页的屏幕
2026-09-19 08:45:37 +08:00
hectorqin
b8fda56c34
fix(web,server): 底部滑杆量章节内页数,快捷列随工具栏收放,chapter-full 改名
2026-09-19 08:17:46 +08:00
hectorqin
1c2d07ba4c
fix(web,server): 让服务端只交章节,并去掉成对的 sync 请求
...
服务端仍在做排版:`chapter-html:<n>` 自己断段、自己提升标题、自己吐
`<p>`。于是 reader 设置里的缩进和段间距只能作用于服务端已经渲染好的那一份,
流式那条路(`chapter:<n>`)拿不到段落,同一本书因为走哪条传输而看起来不一样。
客户端的 `retypePlainText` 于是把服务端断好的段落再拆一遍——同一个判断被两个
地方执行,两次的结果就会分歧。
`POST /api/v1/sync` 的响应本来就是合并后的状态(服务端在两个接口上返回同一份
`pull`),但客户端把它丢掉,然后又为同一份数据发一个 `GET`。读者每翻一页都发
一个 POST 加一个 GET,网络面板里成对出现。另外 `setState` 每次调用都广播,而
阅读页的状态监听会重绘整棵 chrome 树,一个同步循环通知三次。
现在服务端只把整章字符交出来(`text/plain`,`chapter-html:` 不截断),断段、
去段前空格、提升标题全部由 `web/src/formats/segments.ts` 一处决定;push 的返回
值直接喂给 `applyPulled`,空闲轮询才真的发 GET;`setState` 值未变就不广播。
「这一章是不是纯文本」改由 manifest 声明(item 的 `format: 'html'` →
`Section.plainText` → `isPlainText()`),不再靠 `.txt-body` 嗅探:服务端现在发
裸字符,嗅探对一章节全是纯文本的章节会答「不是」,读者会看到一本没有段落、
没有缩进、样式表也没挂上的小说。`.txt-body` 标记降级为旧服务端/旧缓存的兜底。
放弃的方案:保留服务端渲染、只在客户端覆盖样式。缩进和段间距是阅读偏好,服务端
渲染就等于把每次调整变成一次往返,而这个往返正是问题本身。也考虑过让
`chapter-html:` 直接返回服务端渲染的裸 `<p>`(不带 wrapper),但那仍然有两个
地方在判断「一段是什么」。
Ref: #34
2026-09-18 23:39:55 +08:00
hectorqin
7b9c786c83
feat(web): 纯文本排版改在客户端,并让顶栏底栏一起收放
...
现状问题。TXT 的段落和缩进原本由服务端决定:`chapter-html:<n>` 把章节渲染成
带 `<p>` 的片段,客户端只是把它塞进 shadow root。于是三件事同时不对。一是缩进
设定只对服务端已经渲染好的那份生效,windowed 路径拿到的 `chapter:<n>` 是纯字符
没有段落,同一个读者同一本书会因为走哪条传输而长得不一样。二是抓取来的文件里
用来当缩进的段前空格被原样留下,和读者的缩进叠加,恰好在最需要这个设定的文件
上看起来像没生效。三是这些设定是每台设备各自的阅读偏好,烧进响应就等于把每次
调整变成一次往返。另一件事是顶栏和底栏可以各自隐藏,读者收起导航后底栏仍压着
每一页的最后两行。
方案依据。把「一段是什么」收敛到客户端的唯一定义(`formats/segments.ts`),
本地加载、windowed、流式三条路都走它,服务端的职责缩小为交出完整章节和在原文
上断段。`ReaderView` 在渲染前对纯文本章节再过一遍这个拆分(按块读回、拼回空行
再拆,所以服务端断得对不对不影响观感),缩进、段间距、去掉段前空格因此无需请求
即可作用于两条路径。顶栏与前底栏由 `.reader-screen` 上的一个 `data-chrome` 统一
开关,收起时两条栏一起归零高度并滑出、正文长满整屏,同时右侧浮出一列快捷按钮,
否则收起工具栏会变成「必须离开才能操作」的状态。
放弃方案。把服务端的 `chapter-html` 直接删掉:`href` 同时是阅读位置的标识,且
`format: 'html'` 字段已是既有契约,删掉会让旧客户端与旧位置一起失效,所以保留
作为传输,只去掉它承担的排版职责。让缩进靠服务端属性(`data-indent`)继续驱动:
那正是「设定是响应的属性」这一问题的根源。收起态用两个独立开关:写法上就能只藏
一半,而这恰是读者报的那个 bug。
改动点。新增 `web/src/formats/segments.ts`(分段、去段前空格、HTML/节点两种形
式、跨 chunk 的 carry 判定);`reader-view.ts` 增加 `retypePlainText` 并去掉旧
排版表里读服务端属性的规则;`render/text.ts` 按 `carry` 拼接跨块段落;服务端
`text-html.ts` 去掉 `indent` 选项、不再截断;`reader-chrome.tsx` 增加侧边快捷列
与设置面板顶部的「快捷」一行;`reader.css` 增加两条栏的收放与按钮列样式;图标
集新增 `text-indent` / `text-type` / `spacing`;`docs/ui.md` 补 §3.7 的边界说明
与 §3.8 沉浸式阅读。
验证方式。`cd web && npm test`(369 条)、`cd server && npm test`(205 条)全绿;
`npm run build` 通过;`npm run ui:review` 全绿,并新增「收起工具栏: 顶栏与底栏一
起走 / 都不|不可见 / 正文占满整屏 / 右侧仍有快捷按钮」四条尺寸断言(截图见
`docs/ui-review/03-reader-no-chrome.png`),评审夹具改用全角空格缩进,使「段前
空格被去掉」在截图上可见。
Ref: #34
2026-09-18 19:09:51 +08:00
hectorqin
cdc95d8d27
fix(web,server): 阅读页可读性与面板、翻页、同步频率、事件层级
...
用户报的八条里,这一提交覆盖了除图标对齐之外的七条。每一条都先做成能复现的
测试或量出来的数字,再改。
## 2. txt 没有处理就渲染,没有默认样式,也无法调整
三层都缺,所以修了三层。
服务端新增 `chapter-html:<n>`,把纯文本渲染成带 p 的片段
(`indexer/formats/text-html.ts`)。空行断段;「上一行以句末标点结尾、下一行以
句子开头」也断段——抓取来的网文常常每行一段且没有空行,少了这条整本书是一块。
章节标题行提升成 h3,否则正文会变成「第二章 落雨雨来了。」。引用仍是
`chapter:<n>`(它同时是阅读位置的标识,改它会把现有读者打回第一章),新增
`format` 字段说明取哪一份;`chapter:` 的 256KB 截断也不再作用于这份渲染结果——
那个截断会把长章节切一半。
客户端新增 `TXT_STYLESHEET`,只在 `format === 'txt'` 时注入 shadow root,作用域
`.txt-body`,所以 EPUB 作者自己的样式一点不受影响。默认两字符首行缩进,用
`2em` 而不是 `2rem`:缩进要跟字号缩放。
阅读设置新增「正文」一节(段落缩进 / 段间距 / 编码),只在 TXT 上出现,前两项
是写在 stage 上的自定义属性,所以调整不需要重新注入章节。
## 3. 翻页异常,往前翻页还会来回跳
`previous()` 用 `scrollTop > 8` 判断「还有没有地方可退」,`next()` 用
`scrollTop < limit - 8` 判断「还有没有地方可进」,然后两边都以 0.9 屏步进。
这三个数字互不相容:forward 落到的位置,back 不认为那是边界。读者看到的就是
来回跳。
改成按**页序号**算术:整屏步进,页边界由滚动**范围**(`scrollHeight -
clientHeight`)而不是内容高度推出。这一个区别是页数和可翻次数对不上的原因——
2.31 屏的一章,按内容高度算 3 页并请求 offset 0/723/1446/2169,而 2169 会被
夹到 1674,也就是第 2 个边界,于是第四次按下的位置计数器不认。
`stepSection` 改成 await:它在跨章时是 fire-and-forget,而 `turnPage` 在
`previous()` 返回后就播放动画——动画演的是读者刚离开的那一章。
## 4. sync 接口会不停的重复渲染,sync 接口调用太过频繁
两处。`onPosition` 每个位置调三次 `patch()`,而 `patch` 会重绘整棵 chrome
树,滚动时每帧三次;合并成一次。
`flushProgress` 无条件 `syncNow()`,于是 1.5 秒一轮 POST+GET,读者只要在读就
一直发。`SyncEngine.schedule()` 去抖并**保证尾次**——没有尾次的话,读者翻完
最后一页就关书,那一页永远发不出去。
## 5/6. 布局样式,以及目录/设置改半屏
面板从 `inset: 0` 改成底部半屏 + 遮罩 + 把手,上半屏正文仍然看得见:选章节、
调字号都是对下面那一页的调整,全屏让读者为了做调整而离开那一页。
状态行从 flex 列里的一行改成浮在正文上的药丸。横条每出现一次就把正文推下去
一行再收回来,也就是把读者正在读的那句挪走。`ui:review` 有一条断言盯着它。
页脚改成两行不同职责:控制行居中、读数是三个固定位置的等宽数字。目录行改成
「序号 + 标题」,当前章高亮一次而不是每行都划线;当前窗口到不了的行标记出来
而不是静静地没反应。
## 7. 点击页面的事件处理层次
一次触摸只属于一个目标,最里面那个赢。两个真缺陷:
- 必须用 `composedPath()` 而不是 `event.target`。书的正文在 shadow root 里,
shadow 边界会**重定向**事件,所以 target 是 shadow host——用 target 写不出
「段落」和「同一个 shadow 树里的按钮」的区别。
- **阅读面自己是个滚动容器**。滚动模式就是让 shadow host 滚起来,于是「可滚动
的祖先拿走手势」这条规则让它吃掉了每一个手势:翻页层级完全正确,但永远
到不了。这是「点击页面没反应」的真正原因。
另外,按到控件的触摸不再被吞掉 click——之前工具栏按钮可见、可点、没反应。
面板打开时它拥有点击:旁边露出的那一条正文不翻页。
## 8. 端到端测试要做 UI 评审
新增 `web/tools/ui-review/`:真实 Chromium(390×844、2x)加载**生产产物**,
对着替身 API 逐屏截图并测量。截图给人看(「只是丑」不是测试失败,但是缺陷),
尺寸给流水线断言(面板不遮住整页、留出 ≥120px 正文、有遮罩和把手、状态提示
不推动正文、控件 ≥28px 且有名字、没有横向溢出)。接进 `client-ui-review`
流水线,只在界面相关文件变化时触发。
这套工具第一次跑就发现了四个真缺陷:`api.toc()` 返回形状不符时 `openStaged`
用 TypeError 打死整个 open(现在回退到 manifest 自己的条目);状态行一旦进入
loading 就再也清不掉(`hideStatus` 的守卫挡住了唯一的出口);翻页步进的 0.9 屏;
以及上面那条阅读面吃掉手势。
## 测试
web 351 项、server 206 项全过。新增:`gestures.test.ts`(事件层级优先级)、
`reader-paging.test.ts`(页序号算术,含子像素取整与滚动范围)、
`mount.test.ts`(单次渲染与卸载后丢弃更新)、`text-html.test.ts`(分段、
转义、标题提升、不截断)。
2026-09-18 12:39:45 +08:00
cnb.dQC5J6ohAGA
780849c1b4
fix: 修复跨文件系统上传报 EXDEV 错误
2026-09-17 17:30:59 +08:00