Repository navigation
Releases: YJLZSL/DeskBase
Release list
v1.12.0 · 单据编辑器 · exe 终于自带图标了
新增:单据编辑器(「单据」页)
把台账摆成送货单 / 订货单 / 收据再打印。左边挑模板,中间拖拽摆放,右边改属性。
为什么做它 —— 证据不是"想当然":
reference/31§0.3 的结论是:"DeskBase 现在最该补的三处:行内公式列 >
打印/报表出口 > 单表导出",而三条用户场景(S1 仓管 / S3 店主 / S4 会计)
都点名同一个缺口:打不出单据。
reference/30§0.3 说得更准:Access 留下的空位不是"更简单的表格",
而是"能给同事用的小应用" —— 只有表、没有出口就接不住这个位置。
借鉴同类产品(送货单打印软件)的四条共性做法:
| 借鉴 | 本项目的做法 |
|---|---|
| 内置模板库 | 送货单 / 订货单 / 收据 / 空白 A4(不假装有"上百套行业模板") |
| 可视化拖拽 | 文字 / 待填栏 / 表格 / 横线 / 方框;缩放用右下角热区,不另画把手 |
| 纸张规格可选 | A4 纵横向 / 连页纸 241×140 / 小票 80mm、58mm |
| 打印预览 | 生成打印 HTML → 浏览器 Ctrl+P(复用 v1.11.0 的通道) |
明确没做:条码 / 二维码(要引库,与便携包 2.9 MB 的定位不符)。
几个刻意的选择:
- 坐标一律毫米,Rust 与前端共用一套。打印这件事上像素是错的单位,它随 DPI 变。
- 打印不自动弹对话框 —— 沿用 v1.11.0 立的规矩:选打印机、纸张、份数都是用户的决定。
- 打开文件走既有的
app.openExport(它限制在exports/目录内),
不另开一条file://通道 —— 那等于绕过既有的安全设计。 - 内置模板改之前先复制一份 —— 改坏内置模板是说不清的麻烦。
修复:exe 终于自带图标了
v1.11.1 排查"快捷方式没图标"时挖到的根因是:deskbase.exe 里根本没有图标资源
(RT_ICON / RT_GROUP_ICON 都是空的)。窗口和任务栏上看到的图标,
是运行时 tao::window::Icon::from_rgba() 设的 —— 只进内存,不进 PE 资源段。
所以:
| 位置 | 之前 | 现在 |
|---|---|---|
| 窗口 / 任务栏(程序运行时) | ✅ | ✅ |
| 资源管理器里看这个 exe 文件 | ❌ 白纸一张 | ✅ |
| 快捷方式的"默认图标" | ❌ 只能靠指外部文件 | ✅ 可以直接指 exe |
| 安装器 / 托盘 / 任何按文件读图标的地方 | ❌ | ✅ |
怎么做的:加了 app/build.rs,调 Windows SDK 自带的 rc.exe 把一个 .rc
编成资源链进去。零新增依赖 —— 常见做法是引 winres / embed-resource,
但便携包才 2.9 MB,能不加就不加。
找不到 rc.exe 时不报错,只是没图标 —— 图标是锦上添花,构建失败是硬伤;
CI 机器和别人 clone 下来编译,SDK 位置可能不一样,那种情况该照常出可用的 exe。
并且加了 scripts/check-icon.cjs 做断言。为什么必须有一个检查:这类问题
没有任何运行时症状 —— 程序照跑、窗口照有图标,只有去看那个 exe 文件才看得出来。
没有断言的话,下次谁把 build.rs 删了或改坏了,没人会发现。
连锁反应一则(写给下一棒):
installer.rs里那段"为什么快捷方式指向外部.ico"
的推理,第 2 条前提已经不成立了 —— 现在可以简化成SetIconLocation(exe, 0),
顺带绕开"Windows 图标缓存"那个短板。本轮没改:现有方案能工作,
而安装逻辑连着"不污染"红线(零注册表残留、卸载干净),为一次纯优化去动它不划算。
要改的话改那里,并且要重跑安装 / 卸载验收。
验证
门禁五道 + check-css-vars 全过 · 走查 8/8(新增"单据"页,截图 22 张)·
check-icon 断言通过(.rsrc 72.5 KB,RT_ICON + RT_GROUP_ICON 都在)
⚠️ 单据编辑器的交互(拖拽 / 改属性 / 缩放)未人眼验证 —— 只验了空状态与结构。
这条如实写在这里,没混进"完成"里。
v1.11.1 · 快捷方式有图标了 · 教程补上新功能 · 视觉验收替人眼
[1.11.1] - 2026-09-27 · 快捷方式有图标了 · 教程补上新功能 · 视觉验收替人眼
用户实测反馈:"安装后在桌面的快捷方式没有显示应用的图标"。
查下去发现根因比"忘了设图标"深一层,下面第一节记了完整证据链。这一版还有一件方法论层面的事:把长期挂着的"人工交互验收"这条窟窿
变成了可执行的动作 —— 见最后一节。
修复
-
安装后的快捷方式没有应用图标(开始菜单与桌面都是 —— 用户只报了桌面,
但两条是同一段代码建的)。根因不是"忘了调
SetIconLocation"这么简单。按直觉应该把图标指向 exe 自己
(SetIconLocation(exe, 0)),但实测发现deskbase.exe根本没有 PE 图标资源:查了什么 结果 反射式枚举 exe 的 PE 资源目录 RT_ICON/RT_GROUP_ICON都是空的阳性对照(证明探测器本身有效) 同一个探测器读 notepad.exe= 4 个图标、explorer.exe= 21 个代码侧 app/下无build.rs、无.rc、依赖里无winres/embed-resource图标是怎么来的 窗口/任务栏图标是运行时用 tao::window::Icon::from_rgba()设的,不进 PE 资源段所以"指向 exe"只会继续显示 Windows 的通用占位图 —— 改了等于没改。
正确做法:把仓库里那份多尺寸icon.ico编进 exe,安装时写到安装目录,
两个快捷方式都指向那一份.ico。体积代价 +74240 字节(73628 的图标 + 约 0.6 KB 胶水),
占体积阈值 50.0%。这条值得记的是排查方法:
SetIconLocation(exe, 0)在绝大多数程序上是对的,
所以"想当然"就会写错。是阳性对照(拿notepad.exe验探测器)才让
"exe 没有图标资源"这个结论站得住 —— 否则没法排除"是我探针写坏了"。⚠️ 如果你重装后仍看到空白图标,那是 Windows 的图标缓存(旧图标被缓存过)。
清一下即可:ie4uinit.exe -show,或删%LOCALAPPDATA%\IconCache.db后重启资源管理器。 -
切回「笔记」页时,列表不会重新拉取(
refreshList只在启动时调过一次,
之后只靠"新建/保存/删除时顺手刷")。后果:别处产生的笔记在切回笔记页时不会出现 ——
实测造景脚本在启动之后写入 3 篇笔记,note.list明明返回 3 条,
界面却一直写着「还没有笔记」,切页也不管用。
"报告里有数据"与"界面显示了数据"是两件事,这正是本项目最忌讳的不一致。
数据库页从一开始就是"每次进入都刷"(DeskBaseDb.onShow),现在两页对齐。 -
网格表头露出内部类型名(
text/money):表头角标原本用正则抠出类型名的英文前缀
就直接显示;而同一个应用的表结构对话框里写的是「文本」「金额」——
用户看到两套说法,其中一套还是英文的。现在全应用共用一份中文映射
(db.js的colTypeLabel,暴露为window.DeskBaseColType);
拿不到那份映射时角标显示为空,而不是回退到英文名。 -
教程第一步指向一个不存在的页面:写着"进「数据库」页",而导航与页面标题都叫
「表格」,全应用没有"数据库"入口。改为「表格」并补了它在导航里的位置。 -
AI 面板隐私条的第一句是病句:三态拼同一句式,关掉时成句是
「现在用的是没启用 —— 不会有任何内容发出去」。另两态(本机/云端)通顺,
只有关闭态不通 —— 而它是用户打开面板看到的第一句隐私声明。
关闭态现在单独成句。一个分支的病句,看代码时三个分支各自都"对"。 -
教程完全没跟上 v1.10/v1.11 的新功能:还停在九节,AI 对话、打印视图、
单表导出、安装版快捷方式一个字都没提。现在补成 十一节:
新增「七、把表格拿出去」(讲清三个出口的分工)与「八、AI 帮忙」
(默认本机、云端默认只发结构、发数据行要逐次勾选、面板上永远写着数据去哪了);
常见错误对照表补 3 条;术语表补「本机模型 / 云端服务」与「结构信息」。
新增:人工交互验收不再靠"没人看"
-
node tests/visual-acceptance.cjs—— 把交接文档里挂了很久的
"人工交互验收仍是最大的窟窿(烟测通过 ≠ 人看着好用)"变成一件可执行的事:
用真实数据造景(两张表含 260 行大表与超长表名 + 三篇带长备注的笔记)、
在 4 套主题下逐个状态截图,产出REVIEW.md—— 每张都标着"该盯什么",
供人(或会看图的 AI)逐张给结论。为什么"看一眼"比再加 100 条断言有用:v1.9.1 时界面上有 7 处把 Markdown 记号
原样露给了用户(写着连表里的**内容**一起搜)—— 代码里那一行完全正常,
只有看截图才发现。机器断言只验"元素在不在、值对不对"。它自己也带自检,这是第一轮的教训(由验收员查出来的):
① 截图前对每个状态断言"该出现的东西出现了",不过就记进盲区;
② 事后比对截图哈希,逐字节相同的两张 = 至少一张没切到目标状态。
盲区会在报告里单列一节 —— 第一轮就是靠它才发现
notes-edit在四套主题下从没拍到编辑画面、empty-db拍的是有数据的网格,
而那两份清单当时"看起来"都验过了。看起来验过、实际没验,比没有清单更坏。 -
node scripts/check-css-vars.cjs/node scripts/check-tutorial.cjs:
补上五道门禁的空白 —— 前者扫"引用了但从未定义"的 CSS 变量
(这类错误不报错,浏览器静默退化;上一次真实命中了 17 处),
后者扫"教程引用的界面名在界面上是否存在"(这次它应该拦住"数据库页"那条,但没拦住 ——
所以给它补了这条用例的口径)。
验证
见 local-docs/handoff/VERSION_PLAN.md 的事实面板(每条都标了怎么测出来的)。
安装版另有一份端到端视觉闭环证据:用外壳的 SHGetFileInfo 读那条修好的 .lnk,
渲染出来的是 DeskBase 图标 + 快捷方式小箭头,而不是占位图。
视觉验收的已知短板(如实说):有 4 组状态对的截图逐字节相同
(notes-list = notes-edit、db-list = db-grid-wide 等)——
因为笔记页宽屏下"列表 + 编辑器"同屏、表格页两张都停在网格顶部。
自检会把它们报出来,但这一版没有真正拍到那两对之间的差别 ——
报告里它们列为盲区,不会被当成"看过了"。
v1.11.0 · 界面清晰了一遍 · 打印出口 · 安装版补上快捷方式
[1.11.0] - 2026-09-27 · 界面清晰了一遍 · 打印出口 · 安装版补上快捷方式
上一版(v1.10.0)把 AI 对话闭环、把翻页提速 2300 倍。
这一版接着补调研点名的"接不住",并把界面整体过了一遍。
新增
-
打印当前表格:生成打印友好的自包含 HTML(A4 横向、表头每页重复、
打印时去掉斑马纹省墨),用系统默认浏览器打开后Ctrl+P即可打印或存成 PDF。出口 给谁 导出当前表格(.xlsx) 还要继续拿 Excel 算数的人 打印当前表格(.html) 要打出来贴墙上、或发给不看电子表格的人 导出全部数据(全库 CSV) 搬家、留底 在此之前,把一张台账打印出来要绕 6 步(导出全库 → 打开目录 → 找 CSV →
Excel 打开 → 排版 → 打印),而且导出去的不是当前这张表。 -
安装版补上快捷方式:装到本机之后,开始菜单里会出现「DeskBase 桌库」——
以前装完没有任何入口,用户点完「安装」只得到一句"已安装到 X",然后找不到它。
桌面快捷方式是勾选项、默认不勾:桌面是用户自己的地方,程序不该不问就往上面放图标。
卸载会一并删掉这两个图标。体积代价实测 +1.5 KB(COM 的IShellLinkW,不新增依赖)。 -
命令面板新增「办公套件使用教程」等入口:教程本来只在设置页最底下,
而命令面板(Ctrl+K)才是这个程序里找东西的正确入口 ——
发现不了的功能等于没做,尤其对不熟电脑的办公人员。
变更(界面)
- 表格页侧栏从 8 个无标题按钮改成 3 个分组:
开始(新建 / 导入)、
拿出去(导出 / 打印 / 备份)、管理(表结构 / 关系 / 视图 / 历史)。
之前它们是一条直列、间距一致,"新建表格"和"导出当前表格"在视觉上完全同权,
用户没有"主动作在哪"的线索。 - 空状态引导真正居中了:它靠
margin: auto只能水平居中,
截图上贴着顶部、下面一片空白。现在空态容器是 flex 居中,引导块落在中间。 - 设置页分区导航改成单行横向滚动(窄屏换行):9 个胶囊原本会折成两三行,
而它是position: sticky的 —— 折行后吸顶区变高,每滚一屏就少看一屏内容。 - 服务商图标与下拉同行:图标原本掉到下一行,看起来像个别的东西 ——
而它是"数据出不出本机"的唯一视觉线索。 - 全局搜索框限宽 52ch:原本横跨整个内容区(约 1200px),
而用户输入的关键词通常只有几个字,过宽让"在这里打字"显得很重。
修复
-
「导出当前表格」对超过 500 行的表会静默只导前 500 行(数据丢失级)。
取数写的是page_rows(..., usize::MAX)—— 看着是"取全部",但后端会把 limit
夹到MAX_PAGE_LIMIT(500)。导出的.xlsx里只有前 500 行,
不报错、不提示,用户拿到一个"看起来完整"的残缺文件。
这是数据丢失里最坏的一种:没有人会去检查第 501 行在不在。修法不是"把那一行改掉",而是把"取整张表"变成一个有名字、有测试、
只能有一种正确实现的函数(export_all::read_all_rows,逐页翻到has_more == false),
并在端到端里用现成的 1200 行表钉住两个出口都导满 1200 行
(单测只能证明"函数返回 1200 行",而这里证明从界面那条 IPC 走下来也导满 ——
同一类错误完全可能只在接线那一层出现)。这个 bug 是写打印视图的人顺手看隔壁取数方式时发现的。
它的形状很典型:一个看起来无害的"取全部"。 -
首次打开设置页,AI 那栏是「服务商:深度求索 DeepSeek」配着本机 Ollama 的地址。
成因是默认地址按数组下标取(PROVIDERS[0]),而清单顺序后来变过。
这不是"看着别扭":用户会以为自己配的是云端,然后把 API Key 填进一个指向本机的地址;
反过来他也可能以为"数据发给 DeepSeek 了",而实际去哪完全取决于那个地址。
现在以服务商为准取它自己的地址,并按 ADR-0007 默认选本机项;
一条测试钉住"默认的服务商与地址必须指向同一家"。 -
设置页 AI 图标显示错了服务商:截图上下拉写着「深度求索 DeepSeek」,
旁边却画着一台本机显示器 —— 因为"用已保存的设置改写下拉值"发生在
两次图标同步之间。这个图标错了比没有更糟:用户会据此判断自己的数据去了哪。 -
17 处引用了根本没定义的 CSS 变量(
--ink-1/--bg/--radius-full/
--font-mono/--fs-sm)。CSS 自定义属性写错不报错 ——
浏览器把该声明当无效、静默退化,元素拿了继承值或初值,结果就是
"看着不太对但说不上哪不对"。真踩到过:设置页那批导航胶囊用的--fs-sm
从来没定义过,它们一直用的是浏览器默认字号。
现在改成规范名(--ink/--paper/--radius-xl/--stack-mono/--fs-aux),
并新增一个扫未定义变量的脚本补上门禁的这块空白(五道门禁都不查这个)。
验证
单元测试、界面烟测(真实点击)、界面走查(截图 + 布局体检 + 无障碍体检)、
崩溃恢复端到端、导入端到端、门禁五道 —— 数字见 local-docs/handoff/VERSION_PLAN.md
的事实面板(每条都标了怎么测出来的)。
覆盖到哪一层(别含混):
- 安装版真装真卸由
#[ignore]测试验(真写注册表、真建.lnk、跑完自清,复核残留为 0);
但界面点「安装」那一下不在烟测里(烟测只断言入口在、勾选项默认不勾)——
因为真点会改系统状态。 - 打印视图的逐页表头重复只有 CSS 规则 + 单测 + 真实 Chromium 打出 19 页 A4 横向佐证,
没有逐页肉眼确认过。 - **AI 对话"真发一条消息、真收一条回复"**仍然没有自动化覆盖(需要真的模型服务)。
v1.10.0 · AI 对话:能问了,而且看得见"这次发了什么"
[1.10.0] - 2026-09-27 · AI 对话:能问了,而且看得见"这次发了什么"
新增
-
AI 对话。之前 AI 只到"能配、能列模型",不能聊天 —— 配置页摆在那里,
点进去却发现没地方问问题,这是最尴尬的一种半成品。这一版把它闭环了。三条来自 ADR-0017 的硬要求,
每一条都落在了具体的地方,不是写在文档里的承诺:要求 怎么落的 默认本地、默认不联网 AI 能力仍默认关闭;服务商清单里本机项排最前;关闭时出站 0 字节 外部服务逐次授权 + 展示将发送的内容 点「发送」先算一遍、弹出完整请求原文(可展开、带字节数),确认后才发出去 必须有 AI 隐私面板 面板常驻、不许折叠:现在跑在本机还是外部、本次会话已发出多少行数据、一键清空对话 -
"只发结构"是默认值,不是选项。 问关于表格的问题时,默认只把表名、字段名、字段类型
发出去,一行数据都不带;要把数据行发出去,必须用户自己勾
「连同当前表格的数据行一起发送」,且每次都要重新勾。本机模型(Ollama / LM Studio /
指向127.0.0.1的自定义地址)不走这条授权 —— 数据没有离开本机,就不该拿"授权"去烦用户
(ADR-0017 的补充决定)。 -
数据行有硬上限:一次最多带 20 行、单元格超长截断。这是唯一会把真实数据
带出本机的路径,"少一点"永远比"多一点"安全,所以上限写死在代码里,界面上不提供"发更多"。 -
端点是后端决定的,不是前端传的。 前端只说"要发什么",说不了"发去哪" ——
否则一个被注入的渲染层就能把用户的请求改发到别处,而界面上仍然写着"本机模型"。 -
多轮上下文上限 8 条(约 4 个来回)。原因很实际:每一轮都要用户授权一次,
上下文越长,"将发送的内容"预览就越没人会读 —— 那样"展示将要发送的内容"就变成了走过场。 -
本机地址支持明文 HTTP(只限回环)。本机模型的地址是
http://127.0.0.1:11434/v1
这种形状(明文 + 非 443 端口),而原来的网络传输层只接受 https。
现在只给127.0.0.1/localhost/[::1]开口子:到回环的明文流量不会离开本机;
其他主机一律仍然只允许 https —— 放开任意主机的 http 等于允许用户把自己的数据明文发到公网。 -
对话记录存在本机(跟数据目录走,换目录不继承),可一键清空;失败的、超时的、
中途关掉界面的都不会在历史里留半截 —— 只有完整的一问一答才落历史。 -
审计:每次对话调用(含失败)记一条只含元信息的记录(时间 / 服务商 / 表 / 模型 /
实际发出的行数 / 结果)。绝不记数据本体,绝不记 API Key ——
连服务端的错误响应也不落原文:万一对面把请求体回声回来,那段里就有用户数据,
而审计是"只记元信息"的地方。所以错误说明只取结构化字段并截断。 -
本机没有模型时明确说清楚,不静默改走云端(ADR-0017 第 1 条:
"看起来在本地实际在云端"是这类功能最坏的一种欺骗)。
性能
-
翻页不再读整表:10 万行的表翻到中间那一页,从 386ms 降到 0.166ms(约 2300 倍)。
表格首页在 v1.9.0 已经快了,但点「下一页」走的还是老路径:
把整张表克隆出来、再逐行反序列化。现在利用一个既有事实 ——
记录 key 是rec/<表>/<20 位补零 rowid>,所以 BTreeMap 的字典序就是 rowid 升序 ——
直接定位到游标所在位置,只取一页。筛选路径同样省掉了"先克隆整表再反序列化"的重复成本。本机实测(
perf_baseline --ignored --nocapture,交错采样取中位数,避免别人同时构建的噪声):规模 指标 优化前 优化后 10 万行 游标页 384.07 ms 0.166 ms(约 2300×) 10 万行 筛选(命中 50 行) 415.18 ms 297.26 ms(−28%) 1 万行 游标页 32.44 ms 0.181 ms(约 180×) 1 万行 筛选 33.33 ms 25.36 ms(−24%) 首页没有变化(前后都在 0.26–0.34ms,属噪声带)。
没优化到的(如实说):筛选仍然是 O(n) 反序列化 —— 它必须把每行 JSON 都解析出来
看那一列,10 万行还要 ~297ms。想再快只能上"只解析筛选列"的自定义 visitor 或倒排索引,
那是另一个量级,这一版没做。 -
筛选结果与游标翻页用"对拍"验证过:新增的测试把旧算法逐字复刻一份,
然后新老两条路径逐页对比行集合、顺序、has_more、next_cursor四项全等
(5 种排序 × 6 种 limit × 挖洞表;筛选 7 组组合)。改了查询逻辑,就得有东西能证明结果没变 ——
只看"页数对得上"是不够的。
修复
- AI 设置里点「保存」看不到任何提示(保存其实成功了)。
#ai-save的处理器在
主 IIFE 之外,而它调的toast()定义在主 IIFE 之内 —— 那句提示抛 ReferenceError,
于是"保存成功"在界面上表现成"保存失败"。这类"功能成功但提示失败"的 bug 最难被举报清楚
(用户会反复点、怀疑自己的 Key)。改走DeskBaseUI.toast,并补了烟测覆盖 ——
在此之前烟测从不点这个按钮,所以机器一直没发现它。 - 「拉取模型」对所有厂商都会 404:
list_models无条件拼{base}/v1/models,
而厂商清单里 9 家有 8 家的地址已经带版本段(/v1、/v3、/v4),
于是拼成/v1/v1/models。对话的地址拼法一直是对的 ——
同一个 URL 规则写了两遍,就只可能有一个对。现在两处共用一条规则,
并有测试用同一份输入同时钉住两个函数,防它们再分家。 - 本机模型连不上时给的是"Win32 错误码 12152":本机模型没启动是 AI 对话最常见的失败,
而用户没法从那串数字想到"我模型没开"。补了一句人话翻译
("本机模型常见:模型服务没启动,或还在加载中")。 CHANGELOG.md里[1.9.2]/[1.9.1]/[1.9.0]三节各重复了一遍(内容逐字节相同,
相隔 147 行)。删掉重复的一份:1957 行 → 1810 行,37 个版本节,每个版本恰好一节。
这是"同一段内容被写进去两次"的事故 —— 用户看到的更新日志里三个版本各出现两次。
已知问题(如实写)
[0.4.0]那一节的位置仍然不对:它排在文件最前面,在这份倒序日志里位于
[1.9.2]之前。这是独立的一处排序欠账,这次没有顺手移动它 ——
移动整节要重算边界、风险比去重大,混在一起改的话出问题就说不清是哪一步。- AI 对话没有真流式输出:回复是一次性拿到的,等待时界面显示「正在思考…」。
逐字输出是体验升级,代价与做法见local-docs/reference/32-tech-feasibility.md第 3 条。 - 本机模型的响应速度与质量取决于用户自己跑的模型,不是这个程序能决定的。
- 筛选性能(见上):10 万行 ~297ms,仍是全表反序列化。
- AI 对话的界面覆盖只到"打开/隐私条/禁用态":真实发一条消息需要真的模型服务
(本机没跑,测试里也不该真联网),所以**"发出去、收回来"这一段没有自动化覆盖**,
只验证到 IPC 契约、payload 构造与审计边界这一层。
v1.9.2 · 空状态引导搬到主区域
界面
-
"还没有表格,跟着三步建第一张"从窄侧栏搬到了主区域。
之前它渲染在侧栏里(236px 宽):三步被挤成七八行、底部还被裁掉,
而右边主区域整片是空的。走查截图上看得清清楚楚 ——
该放"下一步做什么"的地方本来就是那块空地,不是那条窄缝。位置一换,样式得反过来想:
侧栏(236px) 主区域(可到 900px) 对齐 左对齐 居中 宽度 撑满 限宽 46ch(一行拉满会让眼睛来回扫) 按钮 width:100%(窄缝里找不到它)width:auto(撑满反而笨重)侧栏只留一句「还没有表格。」—— 窄栏里放不下三步,也没必要放。
实现上沿用既有的单例引导块(
db-onboard.js),只是换了宿主,
并加了两条防打架的护栏:- 已经在主区域里就不再搬(
MutationObserver会被自己的改动再次触发); - 主区域若已有内容(比如网格没加载完留下的错误提示)就不注入 ——
那是真错误,比引导重要,不能被盖掉。
- 已经在主区域里就不再搬(
验证
烟测 112/112 · 走查 7/7 · 布局体检 0 处 · 无障碍 0 处 · 门禁五道,
且改动前后截图对比确认:侧栏不再挤、主区域不再空。
v1.9.1 · 修 7 处 Markdown 记号 + 侧栏宽度响应式
修复
-
7 处
**加粗**原样显示给了用户(星号没被渲染,就是字面的星号)。
这是先看再改才发现的:跑了一遍界面走查,截图里一眼看到
"连表里的内容一起搜"。和 v1.7.0 在导出的
README.txt里踩的是同一个坑 ——
把 Markdown 写在了不是 Markdown 的地方。位置 改成 index.html4 处(搜索说明、导出说明等)<b>—— HTML 里能真加粗app.js3 处(导入说明、备份状态)去掉星号 —— 那几处是 textContent,标签也不会渲染代码注释里的
**一律不动 —— 那是给我们自己看的,本来就没打算渲染。附带的好处:
<b>比**少一个字符,有几行从两行变一行了。
界面
-
数据库侧栏宽度跟着视口走(
clamp(236px, 26vw, 300px),原本钉死 236px)。
钉死的问题在宽屏上最明显:侧栏还是 236,里面那些"第一步…第二步…"的引导句
被挤成三四行,而右边主区域大片空着。第一版翻车了:写的是
20vw,但默认窗口约 1080px 宽,20vw只有 216px,
比下限 236 还小 ——clamp直接取 236,等于什么都没改。
是走查截图对比才看出来的。系数改成26vw(1080px 下 281px)。
教训:写 clamp 要按实际窗口尺寸算一遍,不能凭感觉填系数。
还没做的(如实说)
| 事项 | 说明 |
|---|---|
| 空状态引导移到主区域 | 现在"还没有表格,跟着三步建第一张"渲染在窄侧栏里, |
| 更细的视觉打磨 | 动效体系本身已经不错(三档弹簧曲线 + 伪元素阴影过渡 + |
验证
界面走查 7/7 步 · 布局体检 0 处问题 · 无障碍体检 0 处问题 ·
门禁五道 · 截图前后对比确认修复生效
v1.9.0 · 性能:打开表格快 1483 倍 · 清理 757 MB
性能(实测,不是估的)
先加了性能基线测试(perf_baseline_page_and_search,#[ignore] 手动跑),
因为它要插 10 万行、进 CI 会让每次提交都变慢;但必须能手动跑 ——
性能数字不能靠感觉,也不能靠回忆。
优化前(本机实测):
| 规模 | 插入 | 首页 | 游标页 | 筛选 |
|---|---|---|---|---|
| 1 万行 | 168ms | 28.4ms | 26.3ms | 27.5ms |
| 10 万行 | 1.81s | 336ms | 329ms | 331ms |
病根(查出来的,不是猜的):store::scan 会把整表克隆出来 ——
每行一个 key 的 String 加一个 value 的 String,然后逐行 JSON 反序列化。
而最常见的查询是"打开表格看第一页":为了显示 50 行,去克隆 10 万个 String。
三个指标数字几乎一样,正是"卡在同一个地方"的证据。
改法:加 store::scan_take(prefix, n)(只取前 n 条),
在 page_rows_filtered 里加一条快路径 —— 无筛选、无游标、按 rowid 升序时直接取前若干行。
这条快路径能成立,靠的是一个已存在的事实:记录 key 是
rec/<表>/<20 位补零 rowid>,所以 BTreeMap 的字典序就是 rowid 升序。
补零是关键 —— 换成不补零的数字串,10 会排在 9 前面,这条快路径就不成立了。
优化后:
| 规模 | 首页 | 变化 |
|---|---|---|
| 1 万行 | 28.4ms → 0.31ms | 快 93 倍 |
| 10 万行 | 336ms → 0.23ms | 快 1483 倍 |
还没优化的(如实说):游标页与筛选仍是 ~330ms。
它们必须看全部行(游标要定位、筛选要逐行比),不是"也能顺手改掉"的东西 ——
要做需要倒排索引或增量扫描,是另一个量级的工程。
过程中被测试抓到一个我引入的 bug:快路径起初只取 limit 行,
而 finish_page 靠"多出来的那一行"判断 has_more,于是"还有 50 行没取"却报 has_more=false。
分页测试当场失败。改成取 limit + 1 行。
(这件事的意义:改动查询逻辑时,旧测试是最好的保险 —— 它比我先发现问题。)
清理
仓库瘦身 757 MB(23 项):
| 内容 | 处理 |
|---|---|
poc/*/node_modules target-msvc(约 700 MB) |
删缓存,源码保留(AGENTS 明确:缓存可删、源码不要删) |
dist/ 旧版本 zip(31 个) |
删,保留最近 3 个;GitHub Release 里都有 |
local-docs/runs/ 旧走查目录 |
删,保留最近 5 个 |
体检结论(顺带做的)
| 项 | 结果 |
|---|---|
| 编译警告 | 0 条 |
#[allow(dead_code)] |
5 处(都是有理由的) |
| 前端函数跨文件重名 | 17 组,但全部是 IIFE 独立作用域,不构成问题 |
| 最大文件 | main.rs 4480 行(主要是 dispatch 的巨型 match) |
关于
main.rs要不要拆:暂时不拆。那个 match 是check-wiring门禁的
识别形式,拆它有真实风险而收益只是"文件数字好看一点"。不做没有把握的重构。
验证
单元 265 通过 / 0 失败 · 烟测 112/112 · 门禁五道 ·
性能数字为本机实测(cargo test perf_baseline -- --ignored --nocapture)
[1.9.0] - 2026-09-26 · 性能:打开表格快 1483 倍 · 仓库清理 757 MB
性能(实测,不是估的)
先加了性能基线测试(perf_baseline_page_and_search,#[ignore] 手动跑),
因为它要插 10 万行、进 CI 会让每次提交都变慢;但必须能手动跑 ——
性能数字不能靠感觉,也不能靠回忆。
优化前(本机实测):
| 规模 | 插入 | 首页 | 游标页 | 筛选 |
|---|---|---|---|---|
| 1 万行 | 168ms | 28.4ms | 26.3ms | 27.5ms |
| 10 万行 | 1.81s | 336ms | 329ms | 331ms |
病根(查出来的,不是猜的):store::scan 会把整表克隆出来 ——
每行一个 key 的 String 加一个 value 的 String,然后逐行 JSON 反序列化。
而最常见的查询是"打开表格看第一页":为了显示 50 行,去克隆 10 万个 String。
三个指标数字几乎一样,正是"卡在同一个地方"的证据。
改法:加 store::scan_take(prefix, n)(只取前 n 条),
在 page_rows_filtered 里加一条快路径 —— 无筛选、无游标、按 rowid 升序时直接取前若干行。
这条快路径能成立,靠的是一个已存在的事实:记录 key 是
rec/<表>/<20 位补零 rowid>,所以 BTreeMap 的字典序就是 rowid 升序。
补零是关键 —— 换成不补零的数字串,10 会排在 9 前面,这条快路径就不成立了。
优化后:
| 规模 | 首页 | 变化 |
|---|---|---|
| 1 万行 | 28.4ms → 0.31ms | 快 93 倍 |
| 10 万行 | 336ms → 0.23ms | 快 1483 倍 |
还没优化的(如实说):游标页与筛选仍是 ~330ms。
它们必须看全部行(游标要定位、筛选要逐行比),不是"也能顺手改掉"的东西 ——
要做需要倒排索引或增量扫描,是另一个量级的工程。
过程中被测试抓到一个我引入的 bug:快路径起初只取 limit 行,
而 finish_page 靠"多出来的那一行"判断 has_more,于是"还有 50 行没取"却报 has_more=false。
分页测试当场失败。改成取 limit + 1 行。
(这件事的意义:改动查询逻辑时,旧测试是最好的保险 —— 它比我先发现问题。)
清理
仓库瘦身 757 MB(23 项):
| 内容 | 处理 |
|---|---|
poc/*/node_modules target-msvc(约 700 MB) |
删缓存,源码保留(AGENTS 明确:缓存可删、源码不要删) |
dist/ 旧版本 zip(31 个) |
删,保留最近 3 个;GitHub Release 里都有 |
local-docs/runs/ 旧走查目录 |
删,保留最近 5 个 |
体检结论(顺带做的)
| 项 | 结果 |
|---|---|
| 编译警告 | 0 条 |
#[allow(dead_code)] |
5 处(都是有理由的) |
| 前端函数跨文件重名 | 17 组,但全部是 IIFE 独立作用域,不构成问题 |
| 最大文件 | main.rs 4480 行(主要是 dispatch 的巨型 match) |
关于
main.rs要不要拆:暂时不拆。那个 match 是check-wiring门禁的
识别形式,拆它有真实风险而收益只是"文件数字好看一点"。不做没有把握的重构。
验证
单元 265 通过 / 0 失败 · 烟测 112/112 · 门禁五道 ·
性能数字为本机实测(cargo test perf_baseline -- --ignored --nocapture)
v1.8.0 · 截图历史 · 滚动捕获 · 测试名英文化
新增
-
截图历史。截图一直在往
shots/里存(单帧与长图都存),但在这一版之前
没人能看见它们 —— 存了个寂寞。现在命令面板里有「截图历史」:网格缩略图、
按时间倒序、可删。图片通过deskbase://localhost/shot/<文件名>提供。那个图片入口做了路径穿越拦截:只认纯文件名,带
/、\、..一律 404。
URL 是能被构造的,不拦的话shot/../../main.dkb就能读到任意文件。 -
滚动捕获:能截外部应用的长图(浏览器、资源管理器、任何窗口)。
与站内「长截图」的分工:站内那套由界面驱动滚动,只能截 DeskBase 自己;
这一套你自己滚,所以能截任何窗口。为什么不让程序帮你滚:
capture.rs的设计底线是「不合成输入」——
模拟滚轮(SendInput)会把事件注入到用户的其它应用里,而那时焦点在哪、
用户正在做什么,程序都不知道。截外部应用不需要跨那条线:你滚,我们抓。抓帧由 Rust 后台线程定时做(约 5.5 帧/秒),不是 WebView 的定时器 ——
用户点走外部窗口后,WebView 的定时器会被节流甚至暂停。去重是这一套的关键:站内是"滚一屏抓一帧",每帧必有位移;
而录屏式抓帧大部分时候用户是停手的,抓到的帧完全一样。
把重复帧拼进去,长图里就会出现大段重复内容 —— 所以拼接前逐对估位移,
位移为 0 的帧直接丢掉,并且和上一张保留的帧比(而不是相邻帧比)。
重构
-
259 个中文测试函数名全部改成英文(
app/src下递归扫描,剩余 0 个)。为什么要改:
cargo test <filter>想筛某个测试时,中文名在这台机器上根本打不出来
(命令行中文编码是坑,findstr搜中文直接无输出);而且这是公开仓库,英文名更通用。命名沿用原语义,保持"读名字就知道测什么",比如:
原名 新名 备份_创建后可独立读回且表数一致backup_read_back_standalone_with_matching_table_count位移为零时不重复追加zero_shift_does_not_append_twice前导零的工号必须判成文本leading_zero_employee_id_is_text注释、文档、界面文案仍然是中文 —— 改的只是标识符。
修复(改名踩到的两个坑,都值得记)
-
测试名被字符串引用时,改名不是零风险。
import_pipeline.rs里有个常量CRASH_TEST存着测试名,子进程靠它筛测试 ——
改名后子进程匹配不到任何测试,于是"正常退出",断言报"子进程本该被强杀,却正常退出了"。
我事先说"零风险"是说漏了这种情况。改完必须真跑测试,不能只靠推理。 -
说"全部改完"之前,扫描范围要真的覆盖全部。
前四批我只扫了app/src/*.rs,漏了model/与store/两个子目录(33 个)。
一层 glob 不等于全部。
验证
| 项 | 结果 |
|---|---|
| 单元测试 | 265 通过 / 0 失败(改名前后一致) |
| 界面烟测 | 112 / 112 |
| 门禁 | 五道全过 |
| 中文标识符 | 递归扫描:剩余 0 |
v1.7.0 · 一键全量导出(可迁移性)
新增
-
一键全量导出。把所有数据整成通用格式放进一个目录:
deskbase-export-20260926-010203/ ├── README.txt ← 用普通文字讲清每个文件是什么、怎么打开 ├── manifest.json ← 文件清单与条数 ├── notes/ ← 每篇笔记一个 .md ├── tables/ ← 每张表一个 .csv ├── config/ ← 设置(不含任何密钥) ├── main.dkb ← 原始数据文件(留个底) └── checksums.sha256 ← 校验和它兑现的是「不锁定用户」这条承诺。
docs/05§13.3 里有一句写得很重的话:
README 必须用人类可读的普通文字 ——「用户十年后打开这个文件夹,
即使 DeskBase 已经不存在,也应该能看懂里面是什么」。这份 README 就是照那句写的,
结尾一句是:这些文件是你的,不是软件的。与文档 §13.3 的两处出入(都是刻意的):
文档原设计 实际做法 为什么 databases/*.sql改为原始 main.dkb+ 说明项目在 ADR-0021 里彻底移除了 SQL attachments/templates/不建空目录 那两个功能还没做;目录不存在比存在但是空的更诚实 -
deskbase agent export-all:同一套逻辑的命令行入口,AI 也能一次把数据整出来。
修复
- 导出的 README 里原写了
**加粗**这类 Markdown 记号 —— 但.txt不是 Markdown,
它只会显示成字面的星号。既然要求"人类可读",字面就得干净,已全部去掉。
测试稳定性
- 烟测里「切回 never」那一步的等待从 8 秒放宽到 20 秒。
原因不是逻辑(逻辑问题已于 v1.4.0 按 D-071 修掉),而是**新构建后第一次运行
验证
| 项 | 结果 |
|---|---|
| 全量导出 | 真跑了一遍:1 张表 → 目录结构齐全,CSV 内容正确,checksums 正确, |
| 烟测 | 112 / 112 |
| 门禁 | 五道全过 |
还没做的办公功能
上一版盘过(local-docs/reference/29-office-features-status.md)。这一版划掉了
「一键全量导出」。还欠:截图标注与历史、教程系统、待办、标签、自定义快捷键、
v1.6.0 · 滚动长截图接线
新增
-
滚动长截图。当前可滚动区域可以整页拍成一张长图:界面每滚一屏,
Rust 抓一帧,全部滚完再按位移拼接。这个功能悬了很久 ——
capture.rs里的位移估计(estimate_scroll_offset)、
拼接(stitch)、PNG 编码(save_png)早就写好且有测试,
一直缺的是「界面滚一屏 → Rust 抓一帧」那条往返协议。
文案里甚至已经承诺过它(帮助页写着"截图工具里能拼接长图")——
承诺跑在实现前面,这次补上。几个实现上的选择:
选择 为什么 由界面驱动滚动,Rust 只抓+拼 滚哪个元素、滚多少,只有界面知道(读文章的滚动、 表格的横竖滚各不相同),Rust 猜不准 每滚一屏等两帧再抓 只等一帧有概率抓到"半截滚动中"的模糊画面 位移估计失败时用整帧高兜底 宁可多留内容也不要错位 —— 多留看得见,错位看不出来 兜底了几处如实报给用户 界面上会提示"有 N 处接缝是靠估的,可能不齐" 帧数上限 60 兜底:别把界面卡死
验证
| 层 | 覆盖 |
|---|---|
| 位移估计 / 拼接 / PNG 编码 | Rust 测试(早就有) |
| 往返链路(开一轮 → 抓帧 → 拼接落盘) | 烟测 4 步:能开一轮 / 一帧会被拦下 / |
| 两帧能拼出并落盘(实测 119px · 2 帧)/ 输出路径在截图目录下 | |
| 真实抓屏内容对不对 | |
| 断言"拼出来对不对"会变成看运气的测试。这一项属于人眼验收 |
| 总计 | 烟测 109 / 109 · 门禁五道 |
还没做的办公功能
文档 docs/05-office-toolbox.md 规划了 15 节,已实现的只有笔记、表格、截图(单帧+长图)。
完整核对表见 local-docs/reference/29-office-features-status.md,
大致还有:截图标注与历史、OCR、剪贴板历史、看板、日历、待办、文件管理、
标签系统、一键全量导出、自定义快捷键、教程系统。
这些不是"没规划",是规划了还没做。逐项状态在核对表里。