nx-sk 0.23.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +150 -86
  2. package/README.md +0 -14
  3. package/assets/nx-sk/SKILL.md +6 -1
  4. package/assets/nx-sk/references/10-sections-entries.md +55 -79
  5. package/assets/nx-sk/references/15-special-fields.md +27 -14
  6. package/assets/nx-sk/references/60-transfer.md +4 -10
  7. package/assets/sk-source/SKILL.md +2 -2
  8. package/assets/sk-source/references/20-intake.md +1 -1
  9. package/package.json +2 -12
  10. package/src/core/attachments.js +1 -34
  11. package/src/core/paths.js +0 -11
  12. package/src/core/render.js +6 -13
  13. package/src/core/store.js +7 -51
  14. package/src/core/vault.js +2 -18
  15. package/src/modules/entries/index.js +0 -147
  16. package/src/modules/entries/service.js +0 -517
  17. package/src/modules/entries/view.jsx +65 -157
  18. package/src/modules/sections/index.js +3 -66
  19. package/src/modules/sections/service.js +31 -291
  20. package/src/modules/settings/service.js +7 -90
  21. package/src/modules/skill/index.js +2 -1
  22. package/src/modules/skill/service.js +38 -1
  23. package/src/modules/system/service.js +2 -15
  24. package/src/modules/transfer/service.js +4 -138
  25. package/src/shared/blockLayout.js +367 -0
  26. package/src/web/frontend/App.jsx +234 -200
  27. package/src/web/frontend/activeView.js +0 -56
  28. package/src/web/frontend/components/blockBoard.jsx +307 -0
  29. package/src/web/frontend/components/fieldEditor.jsx +1 -1
  30. package/src/web/frontend/components/sourcePanel.jsx +65 -23
  31. package/src/web/frontend/components/tableField.jsx +317 -159
  32. package/src/web/frontend/main.jsx +0 -4
  33. package/src/web/frontend/styles.css +394 -441
  34. package/src/web/public/assets/{files-BoJqTGyZ.js → files--kTIDQLp.js} +1 -1
  35. package/src/web/public/assets/index-DDEmDb0K.css +1 -0
  36. package/src/web/public/assets/index-Dk-VfQfE.js +41 -0
  37. package/src/web/public/assets/view-BaXP_gJq.js +1 -0
  38. package/src/web/public/assets/{view-xwEy1P7C.js → view-Bcy062Uw.js} +1 -1
  39. package/src/web/public/assets/{view-B04bzsph.js → view-Bji535Kc.js} +1 -1
  40. package/src/web/public/assets/view-CgLZr6WS.css +1 -0
  41. package/src/web/public/assets/{view-DNT9of0L.js → view-ChbT0W1t.js} +1 -1
  42. package/src/web/public/assets/view-D_mRuEro.js +46 -0
  43. package/src/web/public/index.html +2 -2
  44. package/src/modules/entries/aiPending.js +0 -303
  45. package/src/modules/entries/blockPanel.jsx +0 -239
  46. package/src/modules/entries/boardView.jsx +0 -136
  47. package/src/modules/entries/entriesTable.jsx +0 -119
  48. package/src/shared/blocks.js +0 -400
  49. package/src/shared/navTree.js +0 -252
  50. package/src/shared/views.js +0 -221
  51. package/src/web/frontend/blocks/registry.jsx +0 -304
  52. package/src/web/frontend/components/aiBar.jsx +0 -192
  53. package/src/web/frontend/components/blockDnd.jsx +0 -69
  54. package/src/web/frontend/components/sidebar.jsx +0 -354
  55. package/src/web/frontend/components/viewChips.jsx +0 -43
  56. package/src/web/frontend/panels.jsx +0 -75
  57. package/src/web/frontend/workspace.js +0 -253
  58. package/src/web/public/assets/index-DDcA8xZc.js +0 -64
  59. package/src/web/public/assets/index-FDvbnX_f.css +0 -1
  60. package/src/web/public/assets/view-CvJr_elT.js +0 -42
  61. package/src/web/public/assets/view-tNIpJ3PQ.js +0 -1
package/CHANGELOG.md CHANGED
@@ -2,79 +2,168 @@
2
2
 
3
3
  本文件格式参考 [Keep a Changelog](https://keepachangelog.com/),中文正文。
4
4
 
5
+ ## 0.27.0 (2026-11-08)
6
+
7
+ **表格字段重做:列头 ⋮ 属性菜单、行列都有抓手、去掉了那一整块「列设置」表单。**
8
+
9
+ 原来的表格能用,但长得像后台配置页:表格下面铺着一整块「列设置」,一列一行、七八个输入框
10
+ (key / 名称 / 类型 / 汇总 / 单位 / 默认值 / 密文 / ←→×),而行与列的操作散在两端。
11
+ 这一版按 Notion 数据库的手感重排:**表头就是属性入口,操作都收在 hover 里,静止时这页只有数据**。
12
+
13
+ ### Changed
14
+
15
+ - **列头 ⋮ 打开属性菜单**:列名 / 类型(含「计算列(JS)」)/ key / 单位 / 默认值 / 表尾统计 / 密文 /
16
+ 下拉选项 / 公式,以及左移右移、左右插入、复制属性、删除属性。弹层按锚点定位,空间不够自动翻到上方。
17
+ - **表头末尾 `+`**:加列的入口长在列该长的位置;原来底栏那个「+ 添加列」只在空表时出现。
18
+ - **行抓手挪到左侧沟槽**:静止显示行号,hover 变成 `⋮`(在上/下方插入、复制、删除)+ `⠿`(拖动排序)。
19
+ 原来行操作挤在行尾 78px 的操作列里,现在表格右侧干净了。
20
+ - **表尾「新建一行」**:加行不用回工具栏;回车在最后一格仍然能续行。
21
+ - **质感**:格子不再是「描边输入框」——无边框、无底色,只有 1px 网格线;hover 整行浮起,
22
+ focus 才在格子里显一圈极淡边界。表头小字灰色、计算列浅底、统计行加粗、数字等宽右对齐。
23
+ - 删掉了整块「列设置」面板(`.tf-cols` / `.tf-colrow`),列定义现在只在列头弹层里改。
24
+
25
+ ### Fixed
26
+
27
+ - **勾选列被撑成一整格黑块**:复选框套了给文本框用的 `.tf-input`(width/height 100%),
28
+ 现在单独用 `.tf-check`(15px)。
29
+ - **弹层跑到屏幕左上角**:行/列的 `⋮` 原本用 `display:none` 藏在 hover 里,键盘或脚本点开时
30
+ `getBoundingClientRect()` 全是 0。改成「常驻布局 + opacity 切换」,行号绝对定位叠在同一格上。
31
+
32
+ ## 0.26.0 (2026-11-08)
33
+
34
+ **原始资料(`source`)改成真正的侧边栏;块头 / 面板标题整行都能拖。**
35
+
36
+ 它本来就有 master-detail 的骨架(左清单 / 右正文),但外面套的是「全屏遮罩 + 模态 dialog」:
37
+ 打开时整页被压暗、还被块的 `overflow: hidden` 裁掉一角——整理素材时看不见原文,
38
+ 而「整理」恰恰要求两边对着看。
39
+
40
+ ### Changed
41
+
42
+ - `SourceDrawer` 用 `createPortal` 挂到 `document.body`,定位改为**窗口右侧通栏**
43
+ (`position: fixed`,宽 `min(720px, 46vw)`,窄屏 94vw),带一条柔和投影与轻微的滑入动画。
44
+ - **去掉遮罩与 `aria-modal`**:改成 `role="complementary"`,左边条目仍然可读、可滚;
45
+ 关闭走「关闭」按钮 / `Esc`(焦点仍然归还折叠条)。
46
+ - 层级排好:侧边栏 45 < 弹窗 50,toast 提到 70(不会被侧边栏盖住)。
47
+ - **块头、面板标题行整条都是抓手**(不再只靠那个 10px 的 `⠿`):hover 给浅底、`⠿` 平时淡显
48
+ hover 变实、光标 `cursor: grab`——三件事一起说「这行能拖」。块体(输入区)仍然不能拖,
49
+ 否则选词会变成搬块;面板行内按钮也不会触发拖动。
50
+
51
+ ### Fixed
52
+
53
+ - **拖块不再「卡手」:改成插入式排序**(用户反馈「只能显示在可以显示的位置」)。
54
+ 网格里全宽块只有 `x=0` 一个落点、半宽块只有两个,用引擎的自由拖拽时块只能在「合法格」
55
+ 之间跳,还会被纵向压实实时吸回去——拖到哪儿都不对。现在:
56
+ - 拖动只表达**顺序**:拖到某块的上/下半,出现一条 2px 插入线(与表格搬行同一套语义),
57
+ 松手按新顺序重新铺格(`repackBlocks`,**保留每块原来的宽度**)。
58
+ - 宽度仍是独立自由度:拖块的左右边缘改宽,所以「单列变多列」照旧成立。
59
+ - 拖动过程中不再有实时重排(引擎的 drag 已关掉),落点所见即所得。
60
+ - **「拖拽有点难触发」的真因**:上一版把 `Block` 组件直接当成网格的子元素,而
61
+ `react-grid-layout` 靠 `cloneElement` + `ref` 给子元素注入定位与拖拽手柄——函数组件接不住
62
+ ref,按下时抛 `<DraggableCore> not mounted on DragStart`,拖动因此在 mousedown 就断了。
63
+ 现在子元素回到原生 `div`(`.blk-cell`),块放在里面。
64
+
65
+ ## 0.25.0 (2026-11-08)
66
+
67
+ **正文改成「按内容长」的文档:md 有多长就多高,界面去掉边框,读的内容回到第一眼。**
68
+
69
+ 上一版把正文块化了,但块高是固定的:md 正文被塞进一个 220px 的框里滚动——那还是「面板」的思路,
70
+ 不是「读文档」的思路。这一版把高度的决定权还给内容,并顺手把视觉收干净。
71
+
72
+ ### Changed
73
+
74
+ - **块高由内容决定**:行高压到 8px 一档,用 `ResizeObserver` 量块里的内容(md 正文、附件列表、
75
+ 清单/表格、自动增高的 textarea),显示高度 = max(存储的最小高, 内容高度)。内容删掉就缩回最小高。
76
+ - 存的 `h` 因此变成**最小高**:版式 JSON 描述「这个栏目怎么排」,不是「这一条内容有多高」——
77
+ 后者每条记录都不一样,不该落盘(视图期算出来的高度会在回写时剥掉)。
78
+ - **只保留左右缩放手柄**:高度既然跟内容走,再叠一个手动高度只会互相打架(拖宽 → 内容重排 → 高度自己跟)。
79
+ - **md 正文不再限高滚动**:`.md-preview` 去掉 `max-height: 340px / 560px`,阅读态连灰底也去掉,
80
+ 正文直接落在页面上;工具行(字数 / 图片数 / 编辑)退到 hover 才出现。
81
+ - **无边框**:新增 `--line`(rgba 8.5%)代替原来的实色分隔线;块与面板去掉边框与底色,
82
+ 靠留白与字号分层;把手、字段 key、面板操作平时不出现,hover / 聚焦才浮出。
83
+ - **现代控件**:输入框改成「无边框 + 浅底,focus 才显边界」;按钮/标签去掉浮雕投影,改为平色块 + hover 加深;
84
+ 顶部 tab 扁平化(选中只加深底色);浮层统一一层柔和投影。
85
+ - **阅读优先的排版**:条目名抬到标题级(16px / 600,仍是就地可改的输入);
86
+ 「+ 直接加字段」收成折叠项,不再常驻一行;块内正文字号 13px(工具条仍是 12px)。
87
+
88
+ ### Notes
89
+
90
+ - 版式 JSON 的 `h` 语义变了(最小高 / 8px 一档),但**结构与读写口径不变**:
91
+ `section get --json` / `section update --layout` 照旧;老版式读进来会被夹到最小高,
92
+ 视觉上相当于「内容多长就多长」,不需要迁移脚本。
93
+ - 全局样式改动波及所有页面(条目 / 栏目 / 附件 / 导出 / 设置),逐页截图确认过没有退化。
94
+
95
+ ## 0.24.0 (2026-11-08)
96
+
97
+ **条目正文块逻辑化:一个分组 = 一块面板,一个字段 = 面板里的一个块——能拖、能并排、窄屏自动折列。**
98
+
99
+ 过去正文是「分组小标题 + 自动填充的字段矩阵」:顺序由字段字典决定,想把「岗位」挪到「公司名称」
100
+ 右边、想让两块并排、想让长文独占一整行,都只能去改字段字典。现在**版式是独立的一份 JSON**
101
+ (`section.layout`),拖动即改、自动落盘,与「字段值」是两条保存链——拖一下版式不会把没填完的值一起提交。
102
+
103
+ ### Added
104
+
105
+ - **块面板**:每组一块面板,面板里每个字段一个块(块头 = 标签 + 密文/填写策略标签 + 字段 key)。
106
+ 底层用 `react-grid-layout`(MIT)做网格引擎:拖块头 `⠿` 搬块、拖右下角改宽高、单列变多列、
107
+ 窄屏按断点自动折成一列。碰撞 / 回弹 / 折行都交给引擎,**不自己实现面板引擎**——
108
+ 这一层最容易出错,也最没有复用价值。
109
+ - **版式单独存**(`section.layout`):
110
+ `{v, mode, panels[], groups{分组:{collapsed, layouts{断点:[{i,x,y,w,h}]}}}}`。面板顺序、折叠态、
111
+ 每个块的位置/宽度/高度都在里面,`i` 就是字段 key;坐标是列单位(lg 12 / md 10 / sm 6 / xs 4 / xxs 2)。
112
+ - **结构变了版式自愈**(幂等,读写两条路都过一遍):删字段 → 对应块消失且不留空位;加字段 →
113
+ 接到该组最底部(不插队打散已排好的版式);删分组整块移除、新分组接末尾。
114
+ 没存过版式 = 按默认编排铺一遍(窄字段半列、宽字段整行、窄屏单列)。
115
+ - **CLI 对等**:`section get --json` 带出**已按当前字段对齐**的 `layout`;
116
+ `section update --layout @layout.json` 整体写版式、`--clear-layout` 回到默认编排。
117
+ 端到端冒烟把「面板走的那条 HTTP PATCH」与 CLI 两头都钉住了。
118
+ - **落位吸附**:半宽的块从「缝上」(x=3 这种)吸附到 0 / 6 这类档位,两块并排才齐;
119
+ 用户特意拉的自由宽度(除不尽列数,如 5 列)保持原样,不被「自动摆正」。
120
+ - **表单视图**:一键切回单列流式(长文 `md` / 表格字段多时更好读),版式保留、切回来即恢复。
121
+
122
+ ### Notes
123
+
124
+ - 新增依赖 `react-grid-layout@^2.3.0`(含 `react-draggable` / `react-resizable`);构建产物 +约 105KB gzip
125
+ 在条目视图的懒加载分片里,首屏不受影响。
126
+ - 版式**不影响**条目值、完整度与 CLI 读写口径;老 store 没有 `layout` 也能直接用(读一次就按默认编排渲染)。
127
+
128
+ ## 0.23.1 (2026-11-05)
129
+
130
+ ### Added
131
+
132
+ - **`skill install --all`:一次装齐 `assets/` 下全部 skill,且直接覆盖**。范围取目录
133
+ 扫描(事实源 `assets/<x>/SKILL.md`),不依赖分组表;覆盖隐式生效,不用再补 `--force`。
134
+ 与 `[name]` 互斥;`--mode symlink` / `--dry-run` 对整批同样适用。
135
+
5
136
  ## 0.23.0 (2026-10-31)
6
137
 
7
- **应用壳换成可停靠的工作区(Notion 式目录树 + FlexLayout);看板视图落地;条目有了块级 AI 的「提议 → 审 → 应用/拒绝」流;栏目结构在面板里可自己搭。**
138
+ **栏目结构在面板里可自己搭了:分组能增删改序,字段能改名 / 换组 / 换类型 / 换位置。**
8
139
 
9
- 壳是「面板的布局由你自己摆」:栏目只从目录树进,布局跨会话恢复。
10
- AI **不自带大模型**——提议由调用方(agent 或外部模型)交进来,人只做评审。
11
- **栏目结构**(分组 + 字段)过去只有命令行能改:面板连建一个分组都要回 CLI 或换模板——
12
- 于是「这个栏目长什么样」这件事面板说了不算。这次把四个分组操作与两个字段操作做成
13
- action(面板与 CLI 同一份),面板「编辑栏目」里直接可改。
140
+ 过去面板只能「加字段」,连建一个分组都要回 CLI 或换模板——于是「这个栏目长什么样」
141
+ 这件事实际上只有命令行能控制,面板就不是控制面板。这次把结构的四个分组操作与两个字段操作
142
+ 做成 action(面板与 CLI 同一份),面板「编辑栏目」里直接可改。
14
143
 
15
144
  ### Added
16
145
 
17
- - **Notion 式工作区壳**:
18
- - 顶栏只留全局动作(品牌 / 存储 / 打码 / +面板 / 撤销重做 / 设置 / 刷新);
19
- **栏目 tab 从顶栏移除**——栏目只从目录树进,两处入口必然分叉。
20
- - 目录树:栏目 + 分组 + 条目子节点;**拖拽重排与嵌套**(第一次拖动把「从旧导航配置
21
- 推断出的树」固化成显式 `settings.navTree`,之前始终只读推断、不写回);
22
- 隐藏的栏目收在树底(一键恢复,数据不动)。
23
- - 布局 = FlexLayout 模型:面板可拖到任意一边拆分、弹出成浮窗;布局存
24
- localStorage(`nx-sk-ui.workspace`),跨会话恢复;撤销/重做按「整段拖拽 = 一步」。
25
- - 深链升级为两段:`#/栏目` 开该栏目的数据库面板,`#/栏目/条目` 再定位到条目;
26
- 栏目 / 条目缺失时明确提示是哪一级没了。
27
- - 正文块支持拖拽排序(块头把手);数据库面板即工作区里的一块面板,可与其他面板并列。
28
- - **看板视图**:列 = select 字段的候选值;**拖卡 = 写操作**,等于 `entry update --set 字段=值`;
29
- 空值与候选外的值落「未分组」列,不静默丢,「未分组」不是落点。
30
- - **块级 AI(提议 → 审 → 应用/拒绝)**:
31
- - CLI:`entry ai list | suggest | apply | reject`(HTTP 同名路由)。
32
- `suggest` 收 `--from @proposal.json`(AI 给出的**最终正文**)与 `--prompt`(指令留痕),
33
- 只落一条 pending;`apply` 才写 store;`reject` 只删文件。
34
- - **pending 落 `~/nx-sk/pending/`(运行态)**:不进 `store.json`、不进导出包、不进迁移副本
35
- ——里面对 secret 块是明文,落进数据面就等于写进备份与导出面。删目录即清空。
36
- - diff 是**字符级**的:`same / del / ins` 三段,面板上红删绿增;容器块的改动由子块表达
37
- (不把父块一起报成改动);secret 块只报「变了」,明文不经面板接口。
38
- - **冲突检测**:pending 记每块应用前的 `baseHash`(服务端算),块被改过 → `CONFLICT`,
39
- 一个字都不写;`--force` 才覆盖。没有差异的提议返回 `noop`,不留空的待审。
40
- - secret 块的差异按**明文**比对(store 里是密文、提议里是明文),否则一个没动的密钥
41
- 会被报成「密文块已变更」。
146
+ - **分组骨架**:`section update --add-group / --rename-group / --move-group / --remove-group`。
147
+ 改名可以**连 id 一起换**(组里字段的 `group` 引用跟着改,不会整组从表单里消失)。
148
+ 三条规矩全是**拦下来**而不是悄悄做:id 重复报 `CONFLICT`(不覆盖同名分组)、
149
+ **非空分组不给删**(报 `BLOCKED` 并列出是哪几个字段)、内置「原始资料」分组不给删。
150
+ - **字段级改动**:`section update --set-field`(标签 / 类型 / 分组 / 提示 / 必填策略)
151
+ 与 `--move-field`(在自己分组内上下挪)。**key 不可改**——值按 key 存在条目里,
152
+ 改 key 等于让已有值读不出来;要换就新增字段再搬值。
153
+ - **面板**:「编辑栏目」弹窗里两块——「分组」(id / 名字就地改、↑↓ 换序、空组才能删)
154
+ 与「字段字典」(每行可改标签 / 类型 / 分组 / 提示 / 必填策略,组内 ↑↓,删除)。
155
+ 一次改动一次落盘(每次改动都要过后端校验,出错时知道是哪一步),改完立刻回读。
156
+ 清单类字段(`entrylist` / `table`)的**类型置灰**:列是字段自己的结构,
157
+ 就地换类型等于换一个字段——要建就在「新增字段」里连列一起建。
158
+ - 每次改动都把**新旧值**打进输出(`字段 phone:分组 contact → reach`),
159
+ 不再只说「已更新栏目 job:fields」——那种输出改错了没法回溯。
160
+
42
161
  - **表格 / 清单:行与列都能拖**(Notion 那种手感):拖**表头**搬列、拖行尾的 ⠿ 搬行,
43
162
  松手前一条**插入线**告诉你落在哪(只高亮整行说不清「会插到哪」)。排序算法单独一个
44
163
  `reorderAt`(插入位语义,有单测)——差一的表现是「拖到第二行结果跑到第三行」。
45
164
  整行不做 `draggable`:那会跟单元格里选文本打架,把手是唯一的拖拽入口。
46
165
  ↑↓ / ←→ 保留但**悬停才浮出**,行内操作键统一收成 16px 小图标键(密集表里的按钮
47
166
  不该抢内容的注意力);entrylist 那颗「正常大小的删除按钮」也换成了小图标键。
48
- - **降级运行的主动提示**:store 由更新版本写入时,`bootstrap` 与 `health`(CLI 输出与 JSON)
49
- 会明确说明「磁盘版本 > 当前版本,写入会被拒」——不等用户改一次被 BLOCKED 才知道。
50
- - **栏目结构在面板里可自己搭**:分组能增删改序,字段能改名 / 换组 / 换类型 / 换位置。
51
- - **分组骨架**:`section update --add-group / --rename-group / --move-group / --remove-group`。
52
- 改名可以**连 id 一起换**(组里字段的 `group` 引用跟着改,不会整组从表单里消失)。
53
- 三条规矩全是**拦下来**而不是悄悄做:id 重复报 `CONFLICT`(不覆盖同名分组)、
54
- **非空分组不给删**(报 `BLOCKED` 并列出是哪几个字段)、内置「原始资料」分组不给删。
55
- - **字段级改动**:`section update --set-field`(标签 / 类型 / 分组 / 提示 / 必填策略)
56
- 与 `--move-field`(在自己分组内上下挪)。**key 不可改**——值按 key 存在条目里,
57
- 改 key 等于让已有值读不出来;要换就新增字段再搬值。
58
- - **面板**:「编辑栏目」弹窗里两块——「分组」(id / 名字就地改、↑↓ 换序、空组才能删)
59
- 与「字段字典」(每行可改标签 / 类型 / 分组 / 提示 / 必填策略,组内 ↑↓,删除)。
60
- 一次改动一次落盘(每次改动都要过后端校验,出错时知道是哪一步),改完立刻回读。
61
- 清单类字段(`entrylist` / `table`)的**类型置灰**:列是字段自己的结构,
62
- 就地换类型等于换一个字段——要建就在「新增字段」里连列一起建。
63
- - 每次改动都把**新旧值**打进输出(`字段 phone:分组 contact → reach`),
64
- 不再只说「已更新栏目 job:fields」——那种输出改错了没法回溯。
65
-
66
- ### Fixed
67
-
68
- - **导入会把打码的密钥块加密存盘**(与 0.19 字段路径同类缺陷):导入包里的 `******`
69
- 占位会被当成真密钥加密落盘。现在值清空待重填,并在预览里计数与警告;
70
- 导出预览的掩码统计同步扩到块。
71
- - **表格/看板视图把详情列挤成一条竖缝**(S2 起的老缺陷,列多的栏目才暴露):
72
- 表格列的 flex 基准用的是内容宽度(75 列 ≈ 7300px),行溢出时按基准比例收缩,
73
- 详情列被抽走全部溢出量。现在表格列 basis 为 0(「拿剩余空间」),
74
- 横向滚动交给表格与看板自己的 overflow。
75
- - **布局里的空面板组自愈**:拖动/关闭面板可能留下空 tabset,FlexLayout 会把它原样
76
- 存盘、恢复时渲染成一块白板。现在存盘与恢复两端都剪掉空面板组——
77
- 已经存坏的布局**刷新即自愈**,不用点「重置布局」。
78
167
 
79
168
  ## 0.22.0 (2026-10-31)
80
169
 
@@ -126,31 +215,6 @@ action(面板与 CLI 同一份),面板「编辑栏目」里直接可改。
126
215
  表头写「已删字段」、单元格「—」(不炸、也不静默消失)。
127
216
  - 面板:栏目页顶部视图 chips + 表格视图(横向投影,密文列在表格里一律打码)。
128
217
  看板**渲染**随下一版(配置与 action 已可用)。
129
- - **Notion 式工作区壳**(S3):
130
- - 顶栏只留全局动作(品牌 / 存储 / 打码 / +面板 / 撤销重做 / 设置 / 刷新);
131
- **栏目 tab 从顶栏移除**——栏目只从目录树进,两处入口必然分叉。
132
- - 目录树:栏目 + 分组 + 条目子节点;**拖拽重排与嵌套**(第一次拖动把「从旧导航配置
133
- 推断出的树」固化成显式 `settings.navTree`,之前始终只读推断、不写回);
134
- 隐藏的栏目收在树底(一键恢复,数据不动)。
135
- - 布局 = FlexLayout 模型:面板可拖到任意一边拆分、弹出成浮窗;布局存
136
- localStorage(`nx-sk-ui.workspace`),跨会话恢复;撤销/重做按「整段拖拽 = 一步」。
137
- - 深链升级为两段:`#/栏目` 开该栏目的数据库面板,`#/栏目/条目` 再定位到条目;
138
- 栏目 / 条目缺失时明确提示是哪一级没了。
139
- - 正文块支持拖拽排序(块头把手);数据库面板即工作区里的一块面板,可与其他面板并列。
140
- - **块级 AI(提议 → 审 → 应用/拒绝)**(S4):
141
- - CLI:`entry ai list | suggest | apply | reject`(HTTP 同名路由)。
142
- `suggest` 收 `--from @proposal.json`(AI 给出的**最终正文**)与 `--prompt`(指令留痕),
143
- 只落一条 pending;`apply` 才写 store;`reject` 只删文件。
144
- - **nx-sk 不自带大模型**:提议由调用方(agent 或外部模型)产出后交进来。这样
145
- 「agent 走 CLI」与「人在面板上审同一条 pending」是同一套语义,不需要第二个 AI 入口。
146
- - **pending 落 `~/nx-sk/pending/`(运行态)**:不进 `store.json`、不进导出包、不进迁移副本
147
- ——里面对 secret 块是明文,落进数据面就等于写进备份与导出面。删目录即清空。
148
- - diff 是**字符级**的:`same / del / ins` 三段,面板上红删绿增;容器块的改动由子块表达
149
- (不把父块一起报成改动);secret 块只报「变了」,明文不经面板接口。
150
- - **冲突检测**:pending 记每块应用前的 `baseHash`(服务端算),块被改过 → `CONFLICT`,
151
- 一个字都不写;`--force` 才覆盖。没有差异的提议返回 `noop`,不留空的待审。
152
- - secret 块的差异按**明文**比对(store 里是密文、提议里是明文),否则一个没动的密钥
153
- 会被报成「密文块已变更」。
154
218
 
155
219
  ### Changed
156
220
 
package/README.md CHANGED
@@ -172,20 +172,6 @@ CLI 与 API 同源,skill 驱动 agent**。落到具体功能上:
172
172
  | `nx-sk section update <id> --set-field / --move-field` | 改**字段**:改标签 / 类型 / 分组 / 提示 / 必填策略,组内上下挪(key 不可改)。面板「编辑栏目」同源 |
173
173
  | `nx-sk entry list \| get \| add \| update \| remove` | 条目 CRUD(PATCH 语义) |
174
174
  | `nx-sk entry fields [--section job]` | 字段字典 —— 填之前先看这个,别猜字段名 |
175
- | `nx-sk entry block add <ref> --type <段落\\|标题\\|图文\\|表格\\|清单\\|附件\\|密钥> [--data @x.json] [--after <块id>]` | 追加**块正文**(`entry.body`,可选键);详细页下半部分同源 |
176
- | `nx-sk entry block update <ref> <块id> [--data @x.json]` | 改块(PATCH 语义);密钥块在打码态下写回会被拒(星号不能存成密钥) |
177
- | `nx-sk entry block move <ref> <块id> --to <index>` | 移动块(拖拽落点 = 一次 move) |
178
- | `nx-sk entry block remove \\| remove-many <ref> <块id…>` | 删块;批量时找不到的跳过并报告 |
179
- | `nx-sk entry body <ref> [--mask]` | 读块正文(人读渲染;`--mask` 只作用于密钥块) |
180
- | `nx-sk entry ai suggest <ref> --prompt "改短一点" --from @proposal.json [--block <块id>]` | 生成一条**AI 提议**:只落 pending,不写 store。`--from` 是 AI 给出的最终正文(nx-sk 不自带大模型) |
181
- | `nx-sk entry ai list <ref>` | 看待审提议(新的在前)。pending 是运行态,不进 store、不进导出包 |
182
- | `nx-sk entry ai apply <pendingId> [--block <块id>] [--force]` | 应用提议(**唯一会写 store 的一步**);块被改过 → `CONFLICT`,`--force` 才覆盖 |
183
- | `nx-sk entry ai reject <pendingId>` | 拒绝提议:删掉 pending 文件,数据一个字不动 |
184
- | `nx-sk section view list <id>` | 列视图配置;**没配过时给缺省推断**(表格/精简/看板,标 provisional,不落盘) |
185
- | `nx-sk section view add <id> --type table\\|board\\|kv [--title 名] [--columns 姓名,状态] [--groupby 字段] [--sort 字段:desc]` | 新增视图(第一次 add 把当前生效的推断视图一起固化,chips 不会凭空消失) |
186
- | `nx-sk section view update <id> <viewId> [--title] [--columns] [--sort] [--set col.字段.width=120]` | 改视图;列宽拖拽结束 = 一次 update |
187
- | `nx-sk section view remove <id> <viewId>` | 删视图;删光回到缺省推断态(`defaultView` 一并清掉) |
188
- | `nx-sk section view set-default <id> <viewId>` | 设缺省视图(写 `section.defaultView` —— 是数据,不是面板设置) |
189
175
  | `nx-sk file list \| get \| add \| update \| remove` | 附件 CRUD(原件在 `~/nx-sk/files/`,同内容去重) |
190
176
  | `nx-sk file add --path <文件> [--copy] [--category C]` | 导入附件:**默认移动原件**,`--copy` 仅复制 |
191
177
  | `nx-sk file list [--section <栏目\|none>] [--q 关键字]` | 列附件:按「哪些栏目在引用」筛选(`none` = 未引用);返回栏目汇总与存储目录 |
@@ -193,6 +193,11 @@ nx-sk file open f_xxxx # 系统默认程序打开原件
193
193
  - 附件中心:**「清理未引用(N)」**一键删掉所有没人引用的附件(判据与「未引用」筛选片同一套);
194
194
  - 条目表单:**保存按钮固定在右上角**,有改动时顶栏出现「N 处改动未保存 · 保存」,
195
195
  长栏目滚到一半也看得见(底部也还有一个)。
196
+ - **正文是块面板**:一个分组一块面板、一个字段一块,拖块头整行换顺序、拖左右边缘改宽(可并排成多列);
197
+ 高度**跟内容走**(md 正文/附件/清单有多长就多高,不在块内滚);版式(`section.layout`)自动保存,
198
+ 与字段值两条保存链。想用命令改版式:
199
+ `nx-sk section get <栏目> --json`(读,已按当前字段对齐)/ `section update <栏目> --layout @layout.json`
200
+ / `section update <栏目> --clear-layout`(回默认编排)。形状与自愈规则见 [[10-sections-entries]] 第六节。
196
201
 
197
202
  求职条目里的「简历附件 / 头像图片 / 成绩单 / 证书扫描件」是 `file` 字段,
198
203
  它的值是**附件 id 的列表**——一个字段可以挂**多个**附件(简历多版本、头像多张)
@@ -278,7 +283,7 @@ nx-sk source update <条目> --id <itemId> --mapped 字段key # 标注「这
278
283
  nx-sk source ensure # 给老栏目补上这个字段(模板是后加的,老栏目没有)
279
284
  ```
280
285
 
281
- 面板上:条目表单底部一条**折叠条**「原始资料 · N 条」,点开是右侧抽屉(左清单、中正文),
286
+ 面板上:条目表单底部一条**折叠条**「原始资料 · N 条」,点开是右侧**侧边栏**(左清单、中正文;不遮不挡,左边条目还能读),
282
287
  文件可拖进去(也给了「选择文件」按钮与粘贴框——拖拽不能是唯一入口,WCAG 2.2 AA)。
283
288
  三条纪律:**原文不改写**、**不确定就留空**、**敏感内容不进袋**(袋是明文的;
284
289
  密码走场景栏目的密文字段,不进袋)。
@@ -7,7 +7,7 @@
7
7
  ```
8
8
  store.json(~/nx-sk/store.json 或 $NX_SK_STORE)
9
9
  ├─ settings 全局配置(可读 JSON/Markdown 导出见 60-transfer)
10
- ├─ sections[] 栏目:id / title / order / template / titleField / groups[] / fields[]
10
+ ├─ sections[] 栏目:id / title / order / template / titleField / groups[] / fields[] / layout
11
11
  └─ entries[] 条目:id(e_xxx) / section / title / tags[] / values{字段key: 值} / 时间戳
12
12
  ```
13
13
 
@@ -105,64 +105,7 @@ nx-sk entry get 张三 --json
105
105
  - 身份证号、手机号、紧急联系人电话这类字段,写之前跟用户确认一次来源。
106
106
  - 每次批量写之前先 `--dry-run`。
107
107
 
108
- ## 五、块正文与 AI 提议(entry.body / entry ai)
109
-
110
- 条目除了结构化字段还有**块正文**(`entry.body`,可选键):段落 / 标题 / 图文 / 表格 / 清单 /
111
- 附件 / 密钥七种叶块,加内嵌栏目(`section`)与分栏(`column`)两种容器块。
112
-
113
- ```bash
114
- # 建块 / 看块
115
- nx-sk entry block add <ref> --type paragraph --data '{"text":"第一段"}'
116
- nx-sk entry block list <ref> # secret 块默认原文,--mask 打码
117
- nx-sk entry block update <ref> <块id> --data @块.json
118
- nx-sk entry block move <ref> <块id> --to 0
119
-
120
- # AI 提议:**AI 永不直接改数据**
121
- # ① 你(agent / 外部模型)产出提议正文 = 该条目的最终块数组
122
- # ② suggest 只落一条 pending(~/nx-sk/pending/,运行态,不进 store / 导出包)
123
- # ③ 人或你在面板/CLI 上 apply(才写 store)或 reject(只删文件)
124
- nx-sk entry ai suggest <ref> --prompt "把自我介绍改短" --from @proposal.json
125
- nx-sk entry ai list <ref>
126
- nx-sk entry ai apply <pendingId> # 块被改过 → CONFLICT,--force 才覆盖
127
- nx-sk entry ai reject <pendingId>
128
- ```
129
-
130
- **纪律**:
131
-
132
- - proposal.json 是 `{"blocks":[…]}` 或直接块数组;给**最终正文**,不是补丁。
133
- - 建议先 `nx-sk entry block list <ref> --json` 拿现正文再改,块 id 保持不变
134
- (同 id 不同内容 = 一次「修改」,出现新 id = 「新增」,缺了的 id = 「删除」)。
135
- - 密钥块在提议里是明文——pending 落在本机运行态目录(0600),这是有意设计;
136
- 但**不要**把 pending 文件内容贴进对话或日志。
137
- - 应用前想留底就先 `transfer export`(apply 写 store,与手改同一条边界)。
138
-
139
- ## 六、数据库视图(section.views / section view)
140
-
141
- 视图是**同一批条目的多种投影**:表格(列 = 字段投影)、精简(两列)、看板(按 select 字段分列)、
142
- KV 栏目的键值面。配置存在 `section.views` / `section.defaultView`(可选键)。
143
-
144
- ```bash
145
- nx-sk section view list <id> # 没配过时给缺省推断(provisional: true,不落盘)
146
- nx-sk section view add <id> --type board --title 按状态 --groupby status
147
- nx-sk section view update <id> <viewId> --set col.状态.width=180 --sort 状态:desc
148
- nx-sk section view set-default <id> <viewId>
149
- nx-sk section view remove <id> <viewId>
150
- ```
151
-
152
- **语义**:
153
-
154
- - **缺省视图是推断的、不落盘**:非 KV 栏目给「表格 / 精简 / 看板」,KV 栏目给「KV」。
155
- 第一次改视图才固化(把当时生效的那套推断视图一起写进去——只写用户新建的那条,
156
- 其余 chips 会凭空消失)。`views: []` 与「没配过」行为一致,都走推断。
157
- - 投影只存 `key / width / hidden`;标签与类型回查字段字典。引用的字段被删 → 列还在,
158
- 表头写「已删字段」、单元格「—」(不炸、也不静默消失)。
159
- - **看板的拖卡 = 写操作**:等于 `entry update --set <groupby字段>=<列值>`;
160
- 空值与候选外的值落「未分组」列——展示可以,**不是拖拽落点**(拖进去等于写一个不存在的值)。
161
- - **没有筛选 DSL**(`--filter` 明确不做):存一个不生效的配置比没有更糟。
162
- - 视图随导出包往返;导入只在「新建栏目」或「目标没配过视图」时应用——
163
- 目标已有视图说明用户自己配过,不静默覆盖(预览里给警告)。
164
-
165
- ## 七、扩字段字典(不写代码)
108
+ ## 五、扩字段字典(不写代码)
166
109
 
167
110
  **最省事的做法:直接写。** 字典里没有的键会自动登记(键名就是它本身),类型按值推断
168
111
  (`是/否` → bool,数组 → tags,其余 → text)。
@@ -228,7 +171,8 @@ nx-sk section update job --fields @fields.json
228
171
  > 还能有**计算列**(JS 表达式)与**表尾统计**。最根本的差别是**结构归数据**:列定义、行、公式、
229
172
  > 统计配置全部存在**条目值里**(`{v, columns, rows, totals?, summary?}`),字段定义里的 `columns`
230
173
  > 只是「新建时的初始列」。所以「这一条想多一列」不必改 schema,也不影响同栏目其它条目。
231
- > 面板上**行与列都能拖**:拖表头搬列、拖行尾的 ⠿ 搬行(松手前有插入线)。
174
+ > 面板上**列头有 ⋮ 属性菜单**(改名 / 换类型 / 单位 / 汇总 / 默认值 / 密文 / 选项 / 公式 + 左右插入 / 复制 / 删除),
175
+ > 表头末尾有 `+` 加列;**行与列都能拖**(拖表头搬列、拖行左缘的 ⠿ 搬行,松手前有插入线)。
232
176
  >
233
177
  > ```bash
234
178
  > nx-sk section update mysec --add-field '{"key":"tasks","label":"任务表","type":"table"}'
@@ -244,7 +188,7 @@ nx-sk section update job --fields @fields.json
244
188
  > 杂乱资料(邮件、截图、PDF 摘录、手抄的表)先**原样**丢进袋里,之后由 agent 或人
245
189
  > 按字段字典整理进上面的字段——「先乱入、后整理」因此成立:没有这个袋子,用户只有
246
190
  > 把乱数据硬塞进结构化字段(把结构搞脏)或干脆不存(信息丢了)两个坏选择。
247
- > - 面板上:条目表单底部一条**折叠条**「原始资料 · N 条」,点开是右侧抽屉——
191
+ > - 面板上:条目表单底部一条**折叠条**「原始资料 · N 条」,点开是右侧**侧边栏**(不遮不挡,左边条目还能读)——
248
192
  > 左清单、中间正文,文件可拖进去(也给了「选择文件」按钮与粘贴框:拖拽不能是唯一入口)。
249
193
  > - CLI:`nx-sk source list / get / add / update / remove / ensure`(细节见 `sk-source` skill)。
250
194
  > - 三条纪律:**原文不改写**(整理结果写进字段或新的一条)、**不确定就留空**、
@@ -325,24 +269,56 @@ nx-sk entry get 张三
325
269
  > ② 两者都**不要**出现在「还缺什么」的清单里;③ 用户说「这个不用填」时,
326
270
  > 用 `--fill <字段>=avoid` 记下来,而不是靠下一轮上下文记住。
327
271
 
328
- ## 八、Web 面板
329
-
330
- `nx-sk serve` 打开的是**可停靠的工作区**(Notion 式壳),不是一栏一页:
331
-
332
- - **目录树**(左侧面板):栏目 + 分组 + 条目子节点。拖拽重排与嵌套——第一次拖动会把
333
- 「从旧导航配置推断出的树」固化成显式 `settings.navTree`(`settings get` 里
334
- `navTreeSource: explicit`,之前始终 `inferred` 只读不写回);隐藏的栏目收在树底,一键恢复。
335
- - **布局 = FlexLayout 模型**:数据库 / 资料 / 文件 / 导出 / 设置都是面板,可拖到任意一边拆分、
336
- 弹出成浮窗;布局存 localStorage(`nx-sk-ui.workspace`),跨会话恢复,顶栏可撤销/重做。
337
- - **两段深链**:`#/栏目` 开该栏目的数据库面板,`#/栏目/条目` 再定位到条目;
338
- 栏目 / 条目缺失时明确提示是哪一级没了。
339
- - 栏目页(数据库面板)顶部是**视图 chips**(表格 / 精简 / 看板 / KV,见 §六);
340
- 左侧条目列表带完整度,右侧按分组渲染的字段表单。
341
- - 密文字段默认显示原文;顶栏「打码」等价于 `--mask`(选择持久化在 localStorage);
342
- - **栏目结构自己能搭**:栏目面板里点某个栏目的「编辑」,弹窗里分组可新增 / 改名(含换 id)/
343
- 上下挪 / 删(空的才让删),字段可改标签 / 类型 / 分组 / 提示 / 必填策略、组内上下挪、删除
344
- —— 一次改动一次落盘,与 CLI 的 `--add-group` 等是同一批 action(见 §七「栏目结构」)。
272
+ ## 六、Web 面板
273
+
274
+ `nx-sk serve` → 每个栏目一个 tab,共用同一个条目视图:
275
+
276
+ - 左侧条目列表(带完整度),右侧**按分组渲染的块面板**:一个分组 = 一块面板,一个字段 = 面板里的一个块;
277
+ - **块布局(默认)**:拖**块头整行**(不用对准 ⠿)**换顺序**——拖到某块的上/下半就插到它前面/后面;
278
+ 拖块的**左右边缘**改宽度(单列可变多列);拖**面板标题行**换面板顺序。
279
+ 块可以并排,窄屏自动折成一列(响应式由网格引擎按断点算,不是手写媒体查询)。
280
+ **高度跟内容走**:`md` 正文、附件列表、清单表格有多长就多高,不在块内滚(阅读优先);删掉内容再缩回最小高。
281
+ **版式改动自动保存**,与字段值的「保存」互不影响——拖一下版式不会把没填完的值一起提交;
282
+ - **表单视图**:同一份数据按字段顺序单列铺开(长文 `md`、`table` 字段多时更好读);切回块布局版式原样恢复;
283
+ - **「栏目」页的「编辑」弹窗能改整个结构**:分组可新增 / 改名(含换 id)/ 上下挪 / 删(空的才让删),
284
+ 字段可改标签、类型、分组、提示、必填策略、组内上下挪、删除 —— 一次改动一次落盘;
285
+ - 密文字段默认显示原文;「打码显示(投屏/截图时用)」勾选等价于 `--mask`(选择持久化在 localStorage);
345
286
  - 保存是 **PATCH**:只有被你改动过的字段才会发出去(面板会显示「N 处改动」);
346
- - 详细页下半部分是**块面板**(见 §五),AI 提议是评审条:渲染 diff + 应用 / 拒绝。
287
+ - 每个页面底部会派生显示该模块的 CLI 等价命令(可点击复制)。
288
+
289
+ ### 版式(`section.layout`):**显示关系单独存**
290
+
291
+ 「有哪些字段」(字段字典)与「这些块怎么摆」(版式)是两件事,所以版式是栏目上**另一份 JSON**:
292
+
293
+ ```json
294
+ {
295
+ "v": 1,
296
+ "mode": "grid", // grid = 面板块 · flow = 单列表单
297
+ "panels": ["basic", "contact"], // 面板(分组)顺序
298
+ "groups": {
299
+ "basic": {
300
+ "collapsed": false,
301
+ "layouts": { // 按断点各一份,坐标是**列单位**
302
+ "lg": [{ "i": "name", "x": 0, "y": 0, "w": 6, "h": 2 }]
303
+ }
304
+ }
305
+ }
306
+ }
307
+ ```
308
+
309
+ - `i` = 字段 key;`x / y / w / h` 是列单位(`lg` 12 列、`md` 10、`sm` 6、`xs` 4、`xxs` 2)。
310
+ `h` 是**最小高**(一档 8px + 10px 间距):实际显示高度 = max(最小高, 内容量到的高度),
311
+ 所以同一份版式套在不同内容上都能读。底层是 `react-grid-layout` 的网格形状,面板只管喂 JSON;
312
+ 碰撞、回弹、折列都由引擎算。
313
+ - **结构变了版式自愈**(幂等,读写两条路都过一遍):删字段 → 对应块消失且不留空位;
314
+ 加字段 → 接到该组最底部(不插队打散已排好的版式);删分组 → 整块移除;新分组 → 面板接在末尾。
315
+ - 没存过版式 = 按默认编排铺一遍(窄字段半列、宽字段整行,窄屏单列),**不是**没有版式。
316
+ - 版式不参与完整度、不影响条目值;`flow` 模式下版式仍保留。
317
+
318
+ ```bash
319
+ nx-sk section get job --json # 含 layout(已按当前字段对齐)
320
+ nx-sk section update job --layout @layout.json # 整体写版式(面板拖完回写的就是这条)
321
+ nx-sk section update job --clear-layout # 回到默认编排(不是删成 null)
322
+ ```
347
323
 
348
324
  面板上没有 CLI 做不到的事——两边共享同一套 action 声明。
@@ -13,11 +13,11 @@
13
13
 
14
14
  | type | 值形状 | 结构(列)归谁 | 面板怎么编 | 读它 |
15
15
  | --- | --- | --- | --- | --- |
16
- | `table` | `{v, columns, rows, totals?, summary?}` | **数据自己**(字段定义只给初始列) | 表格控件 + 「列设置」;**拖表头搬列、拖行尾 ⠿ 搬行** | `entry table` / `entry get` |
16
+ | `table` | `{v, columns, rows, totals?, summary?}` | **数据自己**(字段定义只给初始列) | 表格控件:**列头 ⋮ 属性菜单**、表头 + 加列;拖表头搬列、拖行左缘 ⠿ 搬行 | `entry table` / `entry get` |
17
17
  | `entrylist` | `[{列key: 值}, …]` | 字段字典的 `fields` | 密集迷你表(直接增删行,**拖 ⠿ 调行序**) | `entry get` |
18
18
  | `md` | 一段 markdown 字符串 | — | 图文编辑器(Ctrl+V 贴图) | `entry get`(图片还原成路径) |
19
19
  | `file` | `[附件id, …]` | 字段字典的 `kind` / `maxItems` | 附件选择器(上传 / 从库里选) | `entry get`(附件名 + 物理路径) |
20
- | `source` | `[{id, kind, name, text\|fileId\|url, addedAt, …}]` | 内置(每个栏目自带一个) | 底部折叠条 → 右侧抽屉 | `source get` |
20
+ | `source` | `[{id, kind, name, text\|fileId\|url, addedAt, …}]` | 内置(每个栏目自带一个) | 底部折叠条 → 右侧侧边栏 | `source get` |
21
21
  | `secret` | 一个字符串(落盘是密文) | — | 输入框 | `key get` / `entry get` |
22
22
 
23
23
  共同点:**写入即校验**(不合法就整条拒绝,不会存半条)、**空值有明确语义**、
@@ -120,20 +120,33 @@
120
120
  默认值只在**新增行**时用(面板「添加行」按列的 `default` 预填),不会回头改已有行;
121
121
  字段级的默认是:初始列 + 表尾自动统计(数字 sum / 勾选 countTrue)+ 列类型默认 text。
122
122
 
123
- ### 面板上怎么调顺序(拖)
123
+ ### 面板上怎么改结构、怎么调顺序
124
124
 
125
- 排序是表格最常见的编辑动作之一,所以**行与列都能拖**,不用去点箭头:
125
+ **列管理在列头上**:鼠标移到表头 → 右侧出现 `⋮` → 打开这一列的属性菜单:
126
126
 
127
- - **搬列**:直接拖**表头**(表头里没有输入框,不会跟选词打架);「列设置」里每列前面的 ⠿
128
- 也能拖,效果一样。
129
- - **搬行**:拖行尾的 ⠿。整行 `draggable` 不这么做——那会跟单元格里选文本冲突
130
- (想选一段字结果把行拖走了),所以把手是唯一的拖拽入口。
131
- - 松手前会有一条**插入线**落在上/下半(行)或左/右半(列)的位置:线在哪就插到哪。
132
- 只高亮「这一行」是做不到这件事的。
133
- - ↑↓ / ←→ 保留(鼠标悬停在行上才浮出来),键盘与「就挪一格」的场景仍然走得通;
134
- 行内操作键统一是 16px 的小图标键——密集表里的按钮不该抢内容的注意力。
127
+ | 菜单里能做什么 | 说明 |
128
+ | --- | --- |
129
+ | 列名 / 类型 | 就地改;改成「计算列(JS)」后填公式 |
130
+ | key / 单位 / 默认值 | key 是公式里引用的名字(改 key 会连值一起搬) |
131
+ | 统计 | 表尾这一格怎么算(求和 / 平均 / 计数 / 不统计…) |
132
+ | 密文 | 这一列的值以 AES-256-GCM 密文落盘 |
133
+ | 选项 | `select` 类型的候选项,逗号分隔 |
134
+ | 左移 / 右移 | 挪一格 |
135
+ | 向左 / 向右插入列 | 在它旁边插一列空列 |
136
+ | 复制属性 | 连**值**一起复制出一列(`xxxCopy`) |
137
+ | 删除属性 | 连值一起删(只删定义会留下「没有列的数据」) |
138
+
139
+ 表头**末尾有一个 `+`**:加列就在列该长的位置,不用回头去工具栏找。
140
+
141
+ 排序:行与列都能拖,拖到哪儿有一条**插入线**告诉你会插在哪:
142
+
143
+ - **搬列**:直接拖**表头**(表头里没有输入框,不会跟选词打架)。
144
+ - **搬行**:拖行**左侧沟槽**的 ⠿(hover 时行号会变成 `⋮`(行菜单:上下插入 / 复制 / 删除)+ `⠿`)。
145
+ 整行 `draggable` 不这么做——那会跟单元格里选文本冲突(想选一段字结果把行拖走了)。
146
+ - 表尾有一整行「**新建一行**」:加行不用回工具栏;回车在最后一格也能直接续一行。
147
+ - 行内操作统一是小图标,hover 才出现——密集表里的按钮不该抢内容的注意力。
135
148
 
136
- `entrylist` 的行同理(拖 ⠿),删除键也从「一颗正常大小的按钮」换成了小图标键。
149
+ `entrylist` 的行同理(拖 ⠿),删除键也是小图标键。
137
150
 
138
151
  ### 常见错 → 改法
139
152
 
@@ -173,7 +186,7 @@
173
186
  - 每个非 KV 栏目自带一个:杂乱资料先**原样**入袋(不改写),之后按字段字典整理。
174
187
  - **`text` 的底层是 markdown**——聊天记录图文混排,图和文不能分开存:
175
188
  正文里直接写 `![说明](f_xxx)`(附件库的图)或 `![说明](https://…)`(外链图);
176
- 面板抽屉里图片 Ctrl+V / 拖进编辑器就自动上传并插入引用。
189
+ 面板侧边栏里图片 Ctrl+V / 拖进编辑器就自动上传并插入引用。
177
190
  普通文字本来就是合法 markdown,老数据不用迁移。
178
191
  - 不计完整度(`fill: optional`);袋里是明文,**敏感内容不要进袋**。
179
192
 
@@ -74,14 +74,9 @@ bundle.json 包清单:包含哪些栏目、整体口令说明
74
74
  sections/<id>.zip 每个栏目一个上面的单栏目包(逐栏目复用同一套逻辑)
75
75
  ```
76
76
 
77
- - 只打包**条目实际引用**的附件:`file` 字段的 id + `md` 正文里的 `![…](f_x)` + `file` **块**里的 id。
77
+ - 只打包**条目实际引用**的附件:`file` 字段的 id + `md` 正文里的 `![…](f_x)`。
78
78
  - 导入时附件按 **sha256 去重**:本机已有同一份文件就复用,不会存两份。
79
- - 条目里的旧 file id 会**自动重映射**到新 id(`file` 字段数组、`file` 块与 md 图片引用都改)。
80
- - 包版本(`manifest.formatVersion`):**2** 起,包里多 `entries[].body`(块正文)与
81
- `section.views`(视图配置)。老版本读到 2 号包会明确报「版本过新」,
82
- 不会把这两个键静默丢掉;manifest 里**没有这个键**(0.20 及更早的包)视为 1 号包——
83
- 无正文/无视图,照常导入。
84
- - 导入时视图配置只在「新建栏目」或「目标没配过视图」时应用——目标已有自己的视图就不覆盖(预览里会提示)。
79
+ - 条目里的旧 file id 会**自动重映射**到新 id(`file` 字段数组与 md 图片引用都改)。
85
80
  - `import-new` 会看包结构自动判断是单栏目包还是整库包,不用手动挑命令。
86
81
 
87
82
  ## 口令加密(transfer 层)
@@ -107,9 +102,8 @@ sections/<id>.zip 每个栏目一个上面的单栏目包(逐栏目复
107
102
  包里**恒为明文**,保护只靠口令 —— 没有「打码 / 明文」二选(0.11.0 之前的 `--no-secrets` 已移除)。
108
103
  要发给别人的包就设口令;不设口令的包自己收好,别当分享件用。
109
104
 
110
- 读取侧仍兼容旧打码包:遇到 `******` 会跳过该字段并计数,不会把打码串当新密码存进去;
111
- **密钥块**同一条口径——打码的密钥块保留、值清空待重填(预览会统计「密钥块打码 N 个」)。
112
- 导入后敏感字段与密钥块用**本机 vaultKey** 重新加密落盘,与手填的没有区别。
105
+ 读取侧仍兼容旧打码包:遇到 `******` 会跳过该字段并计数,不会把打码串当新密码存进去。
106
+ 导入后敏感字段用**本机 vaultKey** 重新加密落盘,与手填的字段没有区别。
113
107
 
114
108
  ## 限制与坑
115
109
 
@@ -94,7 +94,7 @@ nx-sk source add <条目> --url "https://qcloud.sicau.edu.cn/login"
94
94
  ```
95
95
 
96
96
  - `--file` **默认复制不搬走**原件(这是「加一条资料」,不是 `file add` 的搬运语义)。
97
- - 一次要收很多份:面板里在「原始资料」抽屉中**多选文件**拖进去,或用 `--file` 重复调用。
97
+ - 一次要收很多份:面板里在「原始资料」侧边栏中**多选文件**拖进去,或用 `--file` 重复调用。
98
98
  - 文件分发细则(PDF 怎么拆、截图怎么批量进、表格怎么变成清单行)见 [[20-intake]]。
99
99
 
100
100
  ### 第 2 步:读袋子
@@ -160,7 +160,7 @@ nx-sk section dump <栏目> --json # 完整度与未填清单(袋子
160
160
  ## 面板里怎么用
161
161
 
162
162
  条目表单底部有一条**折叠条**「原始资料 · N 条」(暂存区不该在日常填表时占地方)。
163
- 点开是右侧抽屉:左边清单、中间正文。
163
+ 点开是右侧侧边栏:左边清单、中间正文(不遮挡条目)。
164
164
 
165
165
  - 拖文件进左栏 → 自动上传并入袋
166
166
  - 「+ 选择文件」按钮 → 同样的事(**拖拽不是唯一入口**:WCAG 2.2 AA 要求键盘/单指针也能完成)
@@ -43,7 +43,7 @@ nx-sk source add <条目> --file "D:\资料\用户指南.pdf" --name "平台用
43
43
 
44
44
  ## 批量进一批文件
45
45
 
46
- 面板最快:条目表单底部「原始资料」折叠条 → 抽屉 → 「+ 选择文件」(可多选)
46
+ 面板最快:条目表单底部「原始资料」折叠条 → 右侧侧边栏 → 「+ 选择文件」(可多选)
47
47
  或直接把一批文件拖进左栏。
48
48
 
49
49
  CLI 逐个加(每条一次调用):