Skip to content

Releases: YJLZSL/DeskBase

v1.12.0 · 单据编辑器 · exe 终于自带图标了

Choose a tag to compare

@YJLZSL YJLZSL released this 29 Sep 07:06

新增:单据编辑器(「单据」页)

把台账摆成送货单 / 订货单 / 收据再打印。左边挑模板,中间拖拽摆放,右边改属性。

为什么做它 —— 证据不是"想当然":

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 · 快捷方式有图标了 · 教程补上新功能 · 视觉验收替人眼

Choose a tag to compare

@YJLZSL YJLZSL released this 26 Sep 16:22

[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 · 界面清晰了一遍 · 打印出口 · 安装版补上快捷方式

Choose a tag to compare

@YJLZSL YJLZSL released this 26 Sep 15:28

[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 对话:能问了,而且看得见"这次发了什么"

Choose a tag to compare

@YJLZSL YJLZSL released this 26 Sep 14:15

[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 · 空状态引导搬到主区域

Choose a tag to compare

@YJLZSL YJLZSL released this 26 Sep 04:59

界面

  • "还没有表格,跟着三步建第一张"从窄侧栏搬到了主区域。

    之前它渲染在侧栏里(236px 宽):三步被挤成七八行、底部还被裁掉,
    而右边主区域整片是空的。走查截图上看得清清楚楚 ——
    该放"下一步做什么"的地方本来就是那块空地,不是那条窄缝。

    位置一换,样式得反过来想:

    侧栏(236px) 主区域(可到 900px)
    对齐 左对齐 居中
    宽度 撑满 限宽 46ch(一行拉满会让眼睛来回扫)
    按钮 width:100%(窄缝里找不到它) width:auto(撑满反而笨重)

    侧栏只留一句「还没有表格。」—— 窄栏里放不下三步,也没必要放。

    实现上沿用既有的单例引导块(db-onboard.js),只是换了宿主,
    并加了两条防打架的护栏:

    • 已经在主区域里就不再搬(MutationObserver 会被自己的改动再次触发);
    • 主区域若已有内容(比如网格没加载完留下的错误提示)就不注入 ——
      那是真错误,比引导重要,不能被盖掉。

验证

烟测 112/112 · 走查 7/7 · 布局体检 0 处 · 无障碍 0 处 · 门禁五道,
且改动前后截图对比确认:侧栏不再挤、主区域不再空。


v1.9.1 · 修 7 处 Markdown 记号 + 侧栏宽度响应式

Choose a tag to compare

@YJLZSL YJLZSL released this 26 Sep 04:41

修复

  • 7 处 **加粗** 原样显示给了用户(星号没被渲染,就是字面的星号)。
    这是先看再改才发现的:跑了一遍界面走查,截图里一眼看到
    "连表里的内容一起搜"。

    和 v1.7.0 在导出的 README.txt 里踩的是同一个坑 ——
    把 Markdown 写在了不是 Markdown 的地方。

    位置 改成
    index.html 4 处(搜索说明、导出说明等) <b> —— HTML 里能真加粗
    app.js 3 处(导入说明、备份状态) 去掉星号 —— 那几处是 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

Choose a tag to compare

@YJLZSL YJLZSL released this 26 Sep 03:55

性能(实测,不是估的)

先加了性能基线测试(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 · 截图历史 · 滚动捕获 · 测试名英文化

Choose a tag to compare

@YJLZSL YJLZSL released this 26 Sep 03:09

新增

  • 截图历史。截图一直在往 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

    注释、文档、界面文案仍然是中文 —— 改的只是标识符。

修复(改名踩到的两个坑,都值得记)

  1. 测试名被字符串引用时,改名不是零风险。
    import_pipeline.rs 里有个常量 CRASH_TEST 存着测试名,子进程靠它筛测试 ——
    改名后子进程匹配不到任何测试,于是"正常退出",断言报"子进程本该被强杀,却正常退出了"。
    我事先说"零风险"是说漏了这种情况。改完必须真跑测试,不能只靠推理。

  2. 说"全部改完"之前,扫描范围要真的覆盖全部。
    前四批我只扫了 app/src/*.rs,漏了 model/ 与 store/ 两个子目录(33 个)。
    一层 glob 不等于全部。

验证

项 结果
单元测试 265 通过 / 0 失败(改名前后一致)
界面烟测 112 / 112
门禁 五道全过
中文标识符 递归扫描:剩余 0

v1.7.0 · 一键全量导出(可迁移性)

Choose a tag to compare

@YJLZSL YJLZSL released this 25 Sep 17:15

新增

  • 一键全量导出。把所有数据整成通用格式放进一个目录:

    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 · 滚动长截图接线

Choose a tag to compare

@YJLZSL YJLZSL released this 25 Sep 16:59

新增

  • 滚动长截图。当前可滚动区域可以整页拍成一张长图:界面每滚一屏,
    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、剪贴板历史、看板、日历、待办、文件管理、
标签系统、一键全量导出、自定义快捷键、教程系统。

这些不是"没规划",是规划了还没做。逐项状态在核对表里。