skill-bill 0.1.0 → 0.2.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 (298) hide show
  1. package/SKILL.md +127 -0
  2. package/dist/.gen-inputs.json +14 -0
  3. package/dist/cli/cmd_read.js +133 -142
  4. package/dist/cli/cmd_read.js.map +1 -1
  5. package/dist/cli/registry.d.ts +15 -0
  6. package/dist/cli/registry.d.ts.map +1 -0
  7. package/dist/cli/registry.js +22 -0
  8. package/dist/cli/registry.js.map +1 -0
  9. package/dist/fetch/db.d.ts +4 -0
  10. package/dist/fetch/db.d.ts.map +1 -1
  11. package/dist/fetch/db.js +12 -0
  12. package/dist/fetch/db.js.map +1 -1
  13. package/dist/fetch/index.d.ts +1 -1
  14. package/dist/fetch/index.d.ts.map +1 -1
  15. package/dist/fetch/index.js +1 -1
  16. package/dist/fetch/index.js.map +1 -1
  17. package/dist/help/index.d.ts +1 -0
  18. package/dist/help/index.d.ts.map +1 -1
  19. package/dist/help/index.js +1 -0
  20. package/dist/help/index.js.map +1 -1
  21. package/dist/help/lookup.d.ts.map +1 -1
  22. package/dist/help/lookup.js +3 -2
  23. package/dist/help/lookup.js.map +1 -1
  24. package/dist/help/writeWire.d.ts +16 -0
  25. package/dist/help/writeWire.d.ts.map +1 -0
  26. package/dist/help/writeWire.js +109 -0
  27. package/dist/help/writeWire.js.map +1 -0
  28. package/dist/output.d.ts +21 -0
  29. package/dist/output.d.ts.map +1 -0
  30. package/dist/output.js +48 -0
  31. package/dist/output.js.map +1 -0
  32. package/dist/policy/category.d.ts +5 -0
  33. package/dist/policy/category.d.ts.map +1 -1
  34. package/dist/policy/category.js +9 -2
  35. package/dist/policy/category.js.map +1 -1
  36. package/dist/policy/index.d.ts +1 -1
  37. package/dist/policy/index.d.ts.map +1 -1
  38. package/dist/policy/index.js +1 -1
  39. package/dist/policy/index.js.map +1 -1
  40. package/dist/policy/record.d.ts +12 -0
  41. package/dist/policy/record.d.ts.map +1 -1
  42. package/dist/policy/record.js +27 -0
  43. package/dist/policy/record.js.map +1 -1
  44. package/dist/policy/wakewords.d.ts +4 -0
  45. package/dist/policy/wakewords.d.ts.map +1 -1
  46. package/dist/policy/wakewords.js +12 -7
  47. package/dist/policy/wakewords.js.map +1 -1
  48. package/dist/query/commands.d.ts +19 -0
  49. package/dist/query/commands.d.ts.map +1 -0
  50. package/dist/query/commands.js +40 -0
  51. package/dist/query/commands.js.map +1 -0
  52. package/dist/query/detail.d.ts +26 -0
  53. package/dist/query/detail.d.ts.map +1 -0
  54. package/dist/query/detail.js +115 -0
  55. package/dist/query/detail.js.map +1 -0
  56. package/dist/query/index.d.ts +13 -0
  57. package/dist/query/index.d.ts.map +1 -0
  58. package/dist/query/index.js +12 -0
  59. package/dist/query/index.js.map +1 -0
  60. package/dist/query/list.d.ts +78 -0
  61. package/dist/query/list.d.ts.map +1 -0
  62. package/dist/query/list.js +110 -0
  63. package/dist/query/list.js.map +1 -0
  64. package/dist/query/read.d.ts +21 -0
  65. package/dist/query/read.d.ts.map +1 -0
  66. package/dist/query/read.js +301 -0
  67. package/dist/query/read.js.map +1 -0
  68. package/dist/record/collect.d.ts +18 -0
  69. package/dist/record/collect.d.ts.map +1 -0
  70. package/dist/record/collect.js +15 -0
  71. package/dist/record/collect.js.map +1 -0
  72. package/dist/record/collectBody.d.ts +29 -0
  73. package/dist/record/collectBody.d.ts.map +1 -0
  74. package/dist/record/collectBody.js +196 -0
  75. package/dist/record/collectBody.js.map +1 -0
  76. package/dist/record/commands.d.ts +11 -0
  77. package/dist/record/commands.d.ts.map +1 -0
  78. package/dist/record/commands.js +22 -0
  79. package/dist/record/commands.js.map +1 -0
  80. package/dist/record/index.d.ts +14 -0
  81. package/dist/record/index.d.ts.map +1 -0
  82. package/dist/record/index.js +12 -0
  83. package/dist/record/index.js.map +1 -0
  84. package/dist/record/receipt.d.ts +16 -0
  85. package/dist/record/receipt.d.ts.map +1 -0
  86. package/dist/record/receipt.js +11 -0
  87. package/dist/record/receipt.js.map +1 -0
  88. package/dist/record/receiptBody.d.ts +5 -0
  89. package/dist/record/receiptBody.d.ts.map +1 -0
  90. package/dist/record/receiptBody.js +112 -0
  91. package/dist/record/receiptBody.js.map +1 -0
  92. package/dist/record/scene-batch.d.ts +3 -0
  93. package/dist/record/scene-batch.d.ts.map +1 -0
  94. package/dist/record/scene-batch.js +211 -0
  95. package/dist/record/scene-batch.js.map +1 -0
  96. package/dist/record/scene-borrow.d.ts +3 -0
  97. package/dist/record/scene-borrow.d.ts.map +1 -0
  98. package/dist/record/scene-borrow.js +285 -0
  99. package/dist/record/scene-borrow.js.map +1 -0
  100. package/dist/record/scene-collect.d.ts +3 -0
  101. package/dist/record/scene-collect.d.ts.map +1 -0
  102. package/dist/record/scene-collect.js +294 -0
  103. package/dist/record/scene-collect.js.map +1 -0
  104. package/dist/record/scene-expense.d.ts +3 -0
  105. package/dist/record/scene-expense.d.ts.map +1 -0
  106. package/dist/record/scene-expense.js +21 -0
  107. package/dist/record/scene-expense.js.map +1 -0
  108. package/dist/record/scene-income.d.ts +3 -0
  109. package/dist/record/scene-income.d.ts.map +1 -0
  110. package/dist/record/scene-income.js +219 -0
  111. package/dist/record/scene-income.js.map +1 -0
  112. package/dist/record/scene-installment.d.ts +3 -0
  113. package/dist/record/scene-installment.d.ts.map +1 -0
  114. package/dist/record/scene-installment.js +248 -0
  115. package/dist/record/scene-installment.js.map +1 -0
  116. package/dist/record/scene-lend.d.ts +3 -0
  117. package/dist/record/scene-lend.d.ts.map +1 -0
  118. package/dist/record/scene-lend.js +273 -0
  119. package/dist/record/scene-lend.js.map +1 -0
  120. package/dist/record/scene-photo.d.ts +3 -0
  121. package/dist/record/scene-photo.d.ts.map +1 -0
  122. package/dist/record/scene-photo.js +262 -0
  123. package/dist/record/scene-photo.js.map +1 -0
  124. package/dist/record/scene-plain.d.ts +3 -0
  125. package/dist/record/scene-plain.d.ts.map +1 -0
  126. package/dist/record/scene-plain.js +221 -0
  127. package/dist/record/scene-plain.js.map +1 -0
  128. package/dist/record/scene-refund.d.ts +3 -0
  129. package/dist/record/scene-refund.d.ts.map +1 -0
  130. package/dist/record/scene-refund.js +281 -0
  131. package/dist/record/scene-refund.js.map +1 -0
  132. package/dist/record/scene-reimburse-done.d.ts +3 -0
  133. package/dist/record/scene-reimburse-done.d.ts.map +1 -0
  134. package/dist/record/scene-reimburse-done.js +288 -0
  135. package/dist/record/scene-reimburse-done.js.map +1 -0
  136. package/dist/record/scene-reimburse.d.ts +3 -0
  137. package/dist/record/scene-reimburse.d.ts.map +1 -0
  138. package/dist/record/scene-reimburse.js +224 -0
  139. package/dist/record/scene-reimburse.js.map +1 -0
  140. package/dist/record/scene-repay.d.ts +3 -0
  141. package/dist/record/scene-repay.d.ts.map +1 -0
  142. package/dist/record/scene-repay.js +294 -0
  143. package/dist/record/scene-repay.js.map +1 -0
  144. package/dist/record/scene-restore.d.ts +3 -0
  145. package/dist/record/scene-restore.d.ts.map +1 -0
  146. package/dist/record/scene-restore.js +250 -0
  147. package/dist/record/scene-restore.js.map +1 -0
  148. package/dist/record/scene-undo.d.ts +3 -0
  149. package/dist/record/scene-undo.d.ts.map +1 -0
  150. package/dist/record/scene-undo.js +262 -0
  151. package/dist/record/scene-undo.js.map +1 -0
  152. package/dist/record/scene-update.d.ts +3 -0
  153. package/dist/record/scene-update.d.ts.map +1 -0
  154. package/dist/record/scene-update.js +249 -0
  155. package/dist/record/scene-update.js.map +1 -0
  156. package/dist/record/scene.d.ts +79 -0
  157. package/dist/record/scene.d.ts.map +1 -0
  158. package/dist/record/scene.js +39 -0
  159. package/dist/record/scene.js.map +1 -0
  160. package/dist/record/slots.d.ts +16 -0
  161. package/dist/record/slots.d.ts.map +1 -0
  162. package/dist/record/slots.js +72 -0
  163. package/dist/record/slots.js.map +1 -0
  164. package/dist/record/write.d.ts +9 -0
  165. package/dist/record/write.d.ts.map +1 -0
  166. package/dist/record/write.js +186 -0
  167. package/dist/record/write.js.map +1 -0
  168. package/dist/render/envelope.d.ts.map +1 -1
  169. package/dist/render/envelope.js +21 -8
  170. package/dist/render/envelope.js.map +1 -1
  171. package/dist/render/errors.d.ts +1 -1
  172. package/dist/render/errors.d.ts.map +1 -1
  173. package/dist/render/errors.js.map +1 -1
  174. package/dist/render/helpFile.d.ts +85 -0
  175. package/dist/render/helpFile.d.ts.map +1 -0
  176. package/dist/render/helpFile.js +110 -0
  177. package/dist/render/helpFile.js.map +1 -0
  178. package/dist/render/helpPaths.d.ts +19 -0
  179. package/dist/render/helpPaths.d.ts.map +1 -0
  180. package/dist/render/helpPaths.js +19 -0
  181. package/dist/render/helpPaths.js.map +1 -0
  182. package/dist/render/html.js +1 -1
  183. package/dist/render/html.js.map +1 -1
  184. package/dist/render/index.d.ts +5 -1
  185. package/dist/render/index.d.ts.map +1 -1
  186. package/dist/render/index.js +4 -1
  187. package/dist/render/index.js.map +1 -1
  188. package/dist/render/templates.js +1 -1
  189. package/dist/render/templates.js.map +1 -1
  190. package/dist/render/views.d.ts +0 -22
  191. package/dist/render/views.d.ts.map +1 -1
  192. package/dist/render/views.js +1 -16
  193. package/dist/render/views.js.map +1 -1
  194. package/dist/shared/blockedSlots.d.ts +39 -0
  195. package/dist/shared/blockedSlots.d.ts.map +1 -0
  196. package/dist/shared/blockedSlots.js +105 -0
  197. package/dist/shared/blockedSlots.js.map +1 -0
  198. package/dist/shared/candidatePick.d.ts +50 -0
  199. package/dist/shared/candidatePick.d.ts.map +1 -0
  200. package/dist/shared/candidatePick.js +115 -0
  201. package/dist/shared/candidatePick.js.map +1 -0
  202. package/dist/shared/collectFrame.d.ts +15 -0
  203. package/dist/shared/collectFrame.d.ts.map +1 -0
  204. package/dist/shared/collectFrame.js +34 -0
  205. package/dist/shared/collectFrame.js.map +1 -0
  206. package/dist/shared/commandSpec.d.ts +72 -0
  207. package/dist/shared/commandSpec.d.ts.map +1 -0
  208. package/dist/shared/commandSpec.js +2 -0
  209. package/dist/shared/commandSpec.js.map +1 -0
  210. package/dist/shared/copyArea.d.ts +51 -0
  211. package/dist/shared/copyArea.d.ts.map +1 -0
  212. package/dist/shared/copyArea.js +122 -0
  213. package/dist/shared/copyArea.js.map +1 -0
  214. package/dist/shared/diffTable.d.ts +26 -0
  215. package/dist/shared/diffTable.d.ts.map +1 -0
  216. package/dist/shared/diffTable.js +77 -0
  217. package/dist/shared/diffTable.js.map +1 -0
  218. package/dist/shared/docPage.d.ts +17 -0
  219. package/dist/shared/docPage.d.ts.map +1 -0
  220. package/dist/shared/docPage.js +111 -0
  221. package/dist/shared/docPage.js.map +1 -0
  222. package/dist/shared/duplicateNote.d.ts +40 -0
  223. package/dist/shared/duplicateNote.d.ts.map +1 -0
  224. package/dist/shared/duplicateNote.js +96 -0
  225. package/dist/shared/duplicateNote.js.map +1 -0
  226. package/dist/shared/emptyNote.d.ts +15 -0
  227. package/dist/shared/emptyNote.d.ts.map +1 -0
  228. package/dist/shared/emptyNote.js +27 -0
  229. package/dist/shared/emptyNote.js.map +1 -0
  230. package/dist/shared/errorReceipt.d.ts +21 -0
  231. package/dist/shared/errorReceipt.d.ts.map +1 -0
  232. package/dist/shared/errorReceipt.js +37 -0
  233. package/dist/shared/errorReceipt.js.map +1 -0
  234. package/dist/shared/flowSteps.d.ts +25 -0
  235. package/dist/shared/flowSteps.d.ts.map +1 -0
  236. package/dist/shared/flowSteps.js +49 -0
  237. package/dist/shared/flowSteps.js.map +1 -0
  238. package/dist/shared/installmentPreview.d.ts +23 -0
  239. package/dist/shared/installmentPreview.d.ts.map +1 -0
  240. package/dist/shared/installmentPreview.js +144 -0
  241. package/dist/shared/installmentPreview.js.map +1 -0
  242. package/dist/shared/outsideScan.d.ts +34 -0
  243. package/dist/shared/outsideScan.d.ts.map +1 -0
  244. package/dist/shared/outsideScan.js +116 -0
  245. package/dist/shared/outsideScan.js.map +1 -0
  246. package/dist/shared/pageIdentity.d.ts +30 -0
  247. package/dist/shared/pageIdentity.d.ts.map +1 -0
  248. package/dist/shared/pageIdentity.js +33 -0
  249. package/dist/shared/pageIdentity.js.map +1 -0
  250. package/dist/shared/pageShell.d.ts +24 -0
  251. package/dist/shared/pageShell.d.ts.map +1 -0
  252. package/dist/shared/pageShell.js +42 -0
  253. package/dist/shared/pageShell.js.map +1 -0
  254. package/dist/shared/photoEscape.d.ts +53 -0
  255. package/dist/shared/photoEscape.d.ts.map +1 -0
  256. package/dist/shared/photoEscape.js +139 -0
  257. package/dist/shared/photoEscape.js.map +1 -0
  258. package/dist/shared/prefillNote.d.ts +21 -0
  259. package/dist/shared/prefillNote.d.ts.map +1 -0
  260. package/dist/shared/prefillNote.js +78 -0
  261. package/dist/shared/prefillNote.js.map +1 -0
  262. package/dist/shared/receiptParts.d.ts +12 -0
  263. package/dist/shared/receiptParts.d.ts.map +1 -0
  264. package/dist/shared/receiptParts.js +38 -0
  265. package/dist/shared/receiptParts.js.map +1 -0
  266. package/dist/shared/recentPicks.d.ts +26 -0
  267. package/dist/shared/recentPicks.d.ts.map +1 -0
  268. package/dist/shared/recentPicks.js +41 -0
  269. package/dist/shared/recentPicks.js.map +1 -0
  270. package/dist/shared/recordPicker.d.ts +32 -0
  271. package/dist/shared/recordPicker.d.ts.map +1 -0
  272. package/dist/shared/recordPicker.js +201 -0
  273. package/dist/shared/recordPicker.js.map +1 -0
  274. package/dist/shared/rowEditorTable.d.ts +31 -0
  275. package/dist/shared/rowEditorTable.d.ts.map +1 -0
  276. package/dist/shared/rowEditorTable.js +103 -0
  277. package/dist/shared/rowEditorTable.js.map +1 -0
  278. package/dist/shared/summaryRow.d.ts +44 -0
  279. package/dist/shared/summaryRow.d.ts.map +1 -0
  280. package/dist/shared/summaryRow.js +82 -0
  281. package/dist/shared/summaryRow.js.map +1 -0
  282. package/dist/shared/typeBadge.d.ts +21 -0
  283. package/dist/shared/typeBadge.d.ts.map +1 -0
  284. package/dist/shared/typeBadge.js +68 -0
  285. package/dist/shared/typeBadge.js.map +1 -0
  286. package/dist/shared/userWording.d.ts +54 -0
  287. package/dist/shared/userWording.d.ts.map +1 -0
  288. package/dist/shared/userWording.js +131 -0
  289. package/dist/shared/userWording.js.map +1 -0
  290. package/dist/shared/writeParts.d.ts +62 -0
  291. package/dist/shared/writeParts.d.ts.map +1 -0
  292. package/dist/shared/writeParts.js +39 -0
  293. package/dist/shared/writeParts.js.map +1 -0
  294. package/dist/triggers/wake-assets.d.ts +37 -0
  295. package/dist/triggers/wake-assets.d.ts.map +1 -0
  296. package/dist/triggers/wake-assets.js +951 -0
  297. package/dist/triggers/wake-assets.js.map +1 -0
  298. package/package.json +10 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commandSpec.js","sourceRoot":"","sources":["../../src/shared/commandSpec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,51 @@
1
+ import type { CopyLogFields, DataTextInput, LogTextInput } from 'base-paint';
2
+ /** 本次执行时刻串(页面入参 `actionAt`/回执事实 `actionAt` 的**唯一定义地**):本地时钟 `YYYY-MM-DD HH:MM:SS`。
3
+ * 写域与查询域两张页都读它——搬迁前它分别是 `src/record/write.ts` 的文件级函数与查询域现取的钟,
4
+ * 第二个用法(查询域列表页,票 #411)长出来之后收成这一处;**取时钟仍在各域处理体那一步调它**,
5
+ * 共用位不自己在模块顶层取时钟(那样一次调用两次读数会不一致)。 */
6
+ export declare function actionStamp(): string;
7
+ /** `copyArea` 的可填位:给了什么出什么,0–3 颗按钮。 */
8
+ interface CopyAreaInput {
9
+ /** 给了就出「prompt 预览块 + 复制按钮」。 */
10
+ readonly prompt?: string | {
11
+ readonly text: string;
12
+ readonly label?: string;
13
+ };
14
+ /** 给了就出「复制数据」(三格式菜单),内部走 `buildDataText`。 */
15
+ readonly data?: DataTextInput;
16
+ /** 给了就出「复制日志」,内部走 `buildLogText`。 */
17
+ readonly log?: LogTextInput;
18
+ /** 三样全没给时的那句话(缺省也有一句,见 `COPY_EMPTY_TEXT`)。 */
19
+ readonly emptyText?: string;
20
+ }
21
+ /** 复制日志的入参:本次执行的过程证据(第 1 段「场景标识」由 envelope 派生,不在这里填)。 */
22
+ interface CopyLogInput {
23
+ /** 渲染本页(或写库)的命令原文,可照抄重跑。 */
24
+ readonly command: string;
25
+ /** 本次数据来源(第 3 段),如「biscuit_accountant.db · bills(写库回执)」;库文件名由调用方给。 */
26
+ readonly source?: string;
27
+ /** 写库那一行的过程说明(第 4 段后半,如「影响 1 行 · 字段 category」)。 */
28
+ readonly detail?: string;
29
+ /** 时间戳(第 5 段):**必填,本件不自己取时钟**——共用位不反向依赖页面装配那一层,时间戳由页面层供给。 */
30
+ readonly actionAt: string;
31
+ /** 文档版本(第 5 段后半);不给则不写这半句。 */
32
+ readonly version?: string;
33
+ }
34
+ /** ① 复制 prompt 区:prompt 预览(`renderPreBlock`)+ 它自带的复制按钮。
35
+ * **`actionId` 必给**:公共层的 `renderPreBlock` 只在给了 `actionId` 时才渲染复制按钮
36
+ * (`packages/base-render/src/blocks.ts:729-736`),只给 `copyText` 会出一个「写着复制、其实没有按钮」的
37
+ * 有标签没按钮位——那正是本票线上实测发现的一处,本件补齐。
38
+ * 第二参可选:不给=小标题「复制给助手:这一句可以直接复制」;`null`=不出小标题;给字符串就用它。 */
39
+ export declare function promptCopyArea(prompt: string, label?: string | null): string;
40
+ /** ② 复制区:prompt/数据/日志给了什么出什么;三样全不给=一句空态、**不出按钮**。 */
41
+ export declare function copyArea(input: CopyAreaInput): string;
42
+ /** ③ 复制日志的第 2–6 段入参:本页由哪条命令渲染、数据从哪来、写了多少行、什么时候。 */
43
+ export declare function copyLog(input: CopyLogInput): CopyLogFields;
44
+ /** ④ 回执页退出口:危险色出口标记 + 一句去向说明(t407 根因整改二 D2 去重后)。
45
+ * 改前这里另带一枚「复制数据」(撤销指令直拷,`ilife-exit-undo-copy`)+ base #336
46
+ * 自动补的一枚置灰「复制日志」,与下面复制区那组「复制数据/复制日志」上下重复。
47
+ * 改后退出口只留标记与说明、不带任何复制按钮与 `data-t`;撤销指令走下面复制区那颗
48
+ * 「复制数据」(复制载荷里仍带可重跑命令,`data-t` 形状不动)。 */
49
+ export declare function undoExit(recordId: number): string;
50
+ export {};
51
+ //# sourceMappingURL=copyArea.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copyArea.d.ts","sourceRoot":"","sources":["../../src/shared/copyArea.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAS7E;;;sCAGsC;AACtC,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAKD,uCAAuC;AACvC,UAAU,aAAa;IACrB,+BAA+B;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9E,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC;IAC9B,qCAAqC;IACrC,QAAQ,CAAC,GAAG,CAAC,EAAE,YAAY,CAAC;IAC5B,8CAA8C;IAC9C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,yDAAyD;AACzD,UAAU,YAAY;IACpB,4BAA4B;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,mDAAmD;IACnD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,8BAA8B;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAQD;;;;0DAI0D;AAC1D,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAS5E;AAED,qDAAqD;AACrD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAiBrD;AAuBD,mDAAmD;AACnD,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,aAAa,CAQ1D;AAQD;;;;0CAI0C;AAC1C,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKjD"}
@@ -0,0 +1,122 @@
1
+ /** 复制与提示共用件:复制 prompt 区、复制区(数据位+日志位)、复制日志的入参。
2
+ *
3
+ * 谁在用(两个能力,指名):
4
+ * ① `src/record/`——写入域:过程型采集页(复制 prompt 区 + 复制数据/日志)与结果型回执整页(复制数据/日志);
5
+ * ② `src/query/`——查询域:通用查询列表页的复制数据/日志两条通道(不设 prompt 区——查询不写库,没有要交回助手的一句)。
6
+ * (写域 32 份产物按本件的字面量冻结,查询域只引用、不改这里的任何一句。)
7
+ *
8
+ * 三件东西**只接线、不重造**:
9
+ * ① 复制按钮与区块 → `base-paint/blocks` 的 `renderPreBlock`(带 `copyText` 自带复制按钮)与 `renderCopyBlock`;
10
+ * ② 复制文本 → `base-paint` 的 `buildDataText`(数据投影)/`buildLogText`(六段日志);
11
+ * ③ 复制成功与失败的提示 → 页面运行时 `buildSharedHelpersJs` 自带(本文件不产第二条提示通道)。
12
+ *
13
+ * 口径出处:照 `packages/skill-calorie/src/shared/copyArea.ts`(照结构,不照文件)。
14
+ * 两处按饼干的情形改了口径:
15
+ * - 卡路里的 `promptCopyArea` 走它包内的 `render/copy.js`(`copyActionHtml`,缺省文案是卡路里说法);
16
+ * 本件改走 base 现成组件的 `renderPreBlock({copyText})`——**不新增 base 组件、不动别包**,
17
+ * 复制按钮与逻辑全在 base 侧;
18
+ * - 日志第 3 段的库文件名**由调用方给**(`CopyLogInput.source`),共用件不取本包文件名
19
+ * (`docs/skills/skill-bill/t406-共用件依赖与提升改造清单.md` 第二节 `DB_FILENAME` 行)。
20
+ */
21
+ import { renderCaliberLine, renderCopyBlock, renderEmptyBlock, renderPreBlock } from 'base-paint/blocks';
22
+ import { buildDataText, buildLogText } from 'base-paint';
23
+ /** 日志第 2 段(助手思考链):本仓页面一律由本地 CLI 渲染,不落 `(未知)` 占位。 */
24
+ const LOG_THINKING = '本页由本地渲染,无助手链';
25
+ /** 日志第 6 段(异常):正常产出即「无」。 */
26
+ const LOG_EXCEPTION = '无';
27
+ /** 复制区空态缺省句(三样全没给时出这一句、不出按钮——点了没反应的死按钮就是问题)。 */
28
+ const COPY_EMPTY_TEXT = '本页没有可复制的数据';
29
+ /** 本次执行时刻串(页面入参 `actionAt`/回执事实 `actionAt` 的**唯一定义地**):本地时钟 `YYYY-MM-DD HH:MM:SS`。
30
+ * 写域与查询域两张页都读它——搬迁前它分别是 `src/record/write.ts` 的文件级函数与查询域现取的钟,
31
+ * 第二个用法(查询域列表页,票 #411)长出来之后收成这一处;**取时钟仍在各域处理体那一步调它**,
32
+ * 共用位不自己在模块顶层取时钟(那样一次调用两次读数会不一致)。 */
33
+ export function actionStamp() {
34
+ return new Date().toISOString().slice(0, 19).replace('T', ' ');
35
+ }
36
+ /** 复制数据三格式菜单里三项的用途提示(纯文本/JSON/CSV,顺序=`COPY_FORMATS`)。
37
+ * 格式名保留(那是数据格式本来的样子,不是内部标识),只说清它拿去做什么。 */
38
+ const MENU_HINTS = ['纯文本 粘贴给助手或自己看', 'JSON 结构化存档', 'CSV 表格导入'];
39
+ /** 复制 prompt 区那段 prompt 的复制按钮动作号(固定一枚:同页只出一次 prompt 区)。
40
+ * 小标题缺省那句 `复制 prompt(必走)` 里的 `prompt` 不上屏(本轮整改)——改「复制给助手:这一句可以直接复制」
41
+ * (总则②:写清这块复制走给谁;仍含老字串,护栏不断言全等只认包含)。 */
42
+ const PROMPT_COPY_ACTION = 'ilife-copy-prompt';
43
+ const PROMPT_COPY_HINT = '复制给助手:这一句可以直接复制';
44
+ /** ① 复制 prompt 区:prompt 预览(`renderPreBlock`)+ 它自带的复制按钮。
45
+ * **`actionId` 必给**:公共层的 `renderPreBlock` 只在给了 `actionId` 时才渲染复制按钮
46
+ * (`packages/base-render/src/blocks.ts:729-736`),只给 `copyText` 会出一个「写着复制、其实没有按钮」的
47
+ * 有标签没按钮位——那正是本票线上实测发现的一处,本件补齐。
48
+ * 第二参可选:不给=小标题「复制给助手:这一句可以直接复制」;`null`=不出小标题;给字符串就用它。 */
49
+ export function promptCopyArea(prompt, label) {
50
+ const heading = label === undefined ? PROMPT_COPY_HINT : label;
51
+ return renderPreBlock({
52
+ ...(heading === null ? {} : { label: heading }),
53
+ command: prompt,
54
+ actionId: PROMPT_COPY_ACTION,
55
+ copyText: prompt,
56
+ copyLabel: PROMPT_COPY_HINT,
57
+ });
58
+ }
59
+ /** ② 复制区:prompt/数据/日志给了什么出什么;三样全不给=一句空态、**不出按钮**。 */
60
+ export function copyArea(input) {
61
+ const parts = [];
62
+ const prompt = input.prompt;
63
+ if (typeof prompt === 'string') {
64
+ if (prompt !== '')
65
+ parts.push(promptCopyArea(prompt));
66
+ }
67
+ else if (prompt !== undefined && prompt.text !== '') {
68
+ parts.push(promptCopyArea(prompt.text, prompt.label ?? null));
69
+ }
70
+ if (input.data !== undefined || input.log !== undefined) {
71
+ parts.push(renderCopyBlock({
72
+ ...(input.data === undefined ? {} : { dataFormats: formatsOf(input.data) }),
73
+ ...(input.log === undefined ? {} : { logText: buildLogText(input.log) }),
74
+ }));
75
+ return parts.join('');
76
+ }
77
+ if (parts.length > 0)
78
+ return parts.join('');
79
+ return renderEmptyBlock({ text: input.emptyText ?? COPY_EMPTY_TEXT });
80
+ }
81
+ /** 数据位的那份数据 → 三种格式各算一份 + 菜单提示(`buildDataText` 是本仓复制文本的唯一出口)。
82
+ * 上级裁定第 2 条:复制载荷头行 `【bill · record.add】` 算上屏一并改——`text` 那份显式给 `title`,
83
+ * 不再让公共层按 `envelope.skill/key` 拼出命令名;`title` 只写用户说法,不带命令名与 `·`。 */
84
+ function formatsOf(data) {
85
+ const title = typeof data.title === 'string'
86
+ && (data.title ?? '').trim() !== ''
87
+ ? data.title
88
+ : '记账数据';
89
+ return {
90
+ text: buildDataText({ ...data, format: 'text', title }),
91
+ json: buildDataText({ ...data, format: 'json' }),
92
+ csv: buildDataText({ ...data, format: 'csv' }),
93
+ hints: MENU_HINTS,
94
+ };
95
+ }
96
+ /** ③ 复制日志的第 2–6 段入参:本页由哪条命令渲染、数据从哪来、写了多少行、什么时候。 */
97
+ export function copyLog(input) {
98
+ return {
99
+ thinking: LOG_THINKING,
100
+ dataStructure: input.source === undefined || input.source === '' ? COPY_EMPTY_TEXT : input.source,
101
+ callChain: input.detail === undefined || input.detail === '' ? input.command : input.command + ' | ' + input.detail,
102
+ timestamp: input.version === undefined || input.version === '' ? input.actionAt : input.actionAt + ' · 版本 ' + input.version,
103
+ exception: LOG_EXCEPTION,
104
+ };
105
+ }
106
+ /** 退出口那枚危险色出口标记的动作号。**故意不绑动作**:动作条场景按钮不带 `data-t`,点了不复制也不写库。 */
107
+ const EXIT_MARK_ACTION = 'ilife-exit-undo';
108
+ /** 退出口的标题与说明(本轮整改:`危险色出口` 是设计自指词,`restore` 是内部词,都不上屏)。 */
109
+ const EXIT_TITLE = '想反悔(撤销这一笔)';
110
+ const EXIT_NOTE = '要撤销这一笔,点下面那颗「复制数据」,里面带着一句撤销的话;撤销之后记录还在,随时可以恢复。';
111
+ /** ④ 回执页退出口:危险色出口标记 + 一句去向说明(t407 根因整改二 D2 去重后)。
112
+ * 改前这里另带一枚「复制数据」(撤销指令直拷,`ilife-exit-undo-copy`)+ base #336
113
+ * 自动补的一枚置灰「复制日志」,与下面复制区那组「复制数据/复制日志」上下重复。
114
+ * 改后退出口只留标记与说明、不带任何复制按钮与 `data-t`;撤销指令走下面复制区那颗
115
+ * 「复制数据」(复制载荷里仍带可重跑命令,`data-t` 形状不动)。 */
116
+ export function undoExit(recordId) {
117
+ return renderCopyBlock({
118
+ title: EXIT_TITLE,
119
+ buttons: [{ label: '↩︎ 撤销这一笔', kind: 'red', actionId: EXIT_MARK_ACTION }],
120
+ }) + renderCaliberLine(EXIT_NOTE + '(记录编号 ' + recordId + ')');
121
+ }
122
+ //# sourceMappingURL=copyArea.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copyArea.js","sourceRoot":"","sources":["../../src/shared/copyArea.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /** 一行:哪个字段、改前、改后、算不算真改动。 */
2
+ export interface DiffRow {
3
+ readonly field: string;
4
+ readonly before: string;
5
+ readonly after: string;
6
+ /** 原值与新值写成的文本不一样=真改动(判定住 `diffOf`,别处不许重算)。 */
7
+ readonly changed: boolean;
8
+ }
9
+ /** 造 diff 行:按 `fields` 的顺序逐列比,**改了没改由这里算**。 */
10
+ export declare function diffOf(input: {
11
+ readonly fields: readonly string[];
12
+ readonly before: Readonly<Record<string, unknown>>;
13
+ readonly after: Readonly<Record<string, unknown>>;
14
+ }): readonly DiffRow[];
15
+ /** diff 表整块:改了哪一项/改前/改后三列;零改动(或一个字段都没比)只出空态。行数为零时也不出空表。
16
+ * R2 走映射:行名过 `fieldLabelOf`(库列名不上屏;`deleted_at`→撤销标记、`op`→这一步做什么);
17
+ * 列头不再用「字段/原值/新值」这类表述。口径句与「未设置」字面沿用(护栏逐字钉着)。 */
18
+ export declare function diffTable(input: {
19
+ readonly rows: readonly DiffRow[];
20
+ readonly caption?: string;
21
+ }): string;
22
+ /** 同一批改动的紧凑形态:一行「改了哪一项 | 改前 → 改后」(走 base 的 `renderChangeRows`)。零改动返回空串。 */
23
+ export declare function diffChangeRows(input: {
24
+ readonly rows: readonly DiffRow[];
25
+ }): string;
26
+ //# sourceMappingURL=diffTable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diffTable.d.ts","sourceRoot":"","sources":["../../src/shared/diffTable.ts"],"names":[],"mappings":"AAwBA,4BAA4B;AAC5B,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAUD,+CAA+C;AAC/C,wBAAgB,MAAM,CAAC,KAAK,EAAE;IAC5B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACnD,GAAG,SAAS,OAAO,EAAE,CAMrB;AASD;;iDAEiD;AACjD,wBAAgB,SAAS,CAAC,KAAK,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAkBzG;AAED,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,KAAK,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,CAAA;CAAE,GAAG,MAAM,CAMnF"}
@@ -0,0 +1,77 @@
1
+ /** diff 表(缺口块之一,**唯一定义地**):改记录用,原值/新值对照。
2
+ *
3
+ * 谁在用(本票:**不接任何页面**,理由见下;件头按将来实数写,写得出第二个用法):
4
+ * ① 批量与修正族(改记录)——落库前给一张「字段/原值/新值」,让用户核一眼再落;
5
+ * ② 同族的撤销(那一件回执页)——撤销前后 `deleted_at` 这一列的原值与新值,
6
+ * 与改记录是同一张表、同一个判定,只是字段只有一个。
7
+ * 两个用法的分界:①要改的几个字段并排核(多行),②只有一列在动(一行)。
8
+ *
9
+ * 本票为什么先冻不接:本票的红线是**不改任何页面的可见行为**,接进去就会改(改记录现在出通用页)。
10
+ * 故本件这一票只做「件 + 断言」,接入落在批量与修正族那一件场景件里(`src/record/scene-update.ts`)。
11
+ *
12
+ * 老侧出处:`templates/写入/update_confirm.html:113-116`(原值 → 新值两栏)。
13
+ *
14
+ * 判定只有一条,住 `diffOf`:**原值与新值按同一个口径写成文本,文本不一样才算改动**。
15
+ * 「给了一个新值但它和原值一样」不算改动(`changed: false`),`diffTable` 也就不列它——
16
+ * 列出来会让用户以为改了。没给这列(`undefined`)写成「未设置」,与给了空串同面。
17
+ *
18
+ * 一件不自造:表格走 `renderDataTable`、改前→改后那行紧凑形态走 base 的 `renderChangeRows`、
19
+ * 一行口径走 `renderCaliberLine`、零改动空态走共用位的 `./emptyNote.js`(它包 base 的 `renderEmptyBlock`)。
20
+ */
21
+ import { renderCaliberLine, renderChangeRows, renderDataTable } from 'base-paint/blocks';
22
+ import { emptyNote } from './emptyNote.js';
23
+ import { fieldLabelOf } from './userWording.js';
24
+ /** 一个值写成文本:`undefined`/`null`/空白串**同面**,一律写「未设置」,其余按十进制串写。
25
+ * 同面这条要紧:没给这一列与给了空串如果在文本上不一样,同一件事就会被算成一次改动。 */
26
+ function textOf(v) {
27
+ if (v === undefined || v === null)
28
+ return '未设置';
29
+ const s = String(v);
30
+ return s.trim() === '' ? '未设置' : s;
31
+ }
32
+ /** 造 diff 行:按 `fields` 的顺序逐列比,**改了没改由这里算**。 */
33
+ export function diffOf(input) {
34
+ return input.fields.map((f) => {
35
+ const before = textOf(input.before[f]);
36
+ const after = textOf(input.after[f]);
37
+ return { field: f, before, after, changed: before !== after };
38
+ });
39
+ }
40
+ /** 真改动那一行那一句口径。 */
41
+ function caliberOf(rows) {
42
+ const changed = rows.filter((r) => r.changed).length;
43
+ return '共比了 ' + rows.length + ' 个字段,真改动 ' + changed + ' 个'
44
+ + (changed === rows.length ? '(每一个都动了)' : '(原值与新值一样的那些没列出来)') + '。';
45
+ }
46
+ /** diff 表整块:改了哪一项/改前/改后三列;零改动(或一个字段都没比)只出空态。行数为零时也不出空表。
47
+ * R2 走映射:行名过 `fieldLabelOf`(库列名不上屏;`deleted_at`→撤销标记、`op`→这一步做什么);
48
+ * 列头不再用「字段/原值/新值」这类表述。口径句与「未设置」字面沿用(护栏逐字钉着)。 */
49
+ export function diffTable(input) {
50
+ const changed = input.rows.filter((r) => r.changed);
51
+ if (changed.length === 0) {
52
+ return emptyNote({
53
+ title: '没有一处改动',
54
+ text: input.rows.length === 0 ? '这一次没有要比对的字段。' : '给的新值与原值一模一样,等于没改。',
55
+ next: '请把要改成什么说清(改哪一项、改成什么),再跟助手说一遍。',
56
+ });
57
+ }
58
+ return renderDataTable({
59
+ columns: [
60
+ { key: 'field', label: '改了哪一项' },
61
+ { key: 'before', label: '改前' },
62
+ { key: 'after', label: '改后' },
63
+ ],
64
+ rows: changed.map((r) => ({ field: fieldLabelOf(r.field), before: r.before, after: r.after })),
65
+ caption: input.caption ?? '本次改动的字段(' + changed.length + ' 项)',
66
+ }) + renderCaliberLine(caliberOf(input.rows));
67
+ }
68
+ /** 同一批改动的紧凑形态:一行「改了哪一项 | 改前 → 改后」(走 base 的 `renderChangeRows`)。零改动返回空串。 */
69
+ export function diffChangeRows(input) {
70
+ const changed = input.rows.filter((r) => r.changed);
71
+ if (changed.length === 0)
72
+ return '';
73
+ return renderChangeRows({
74
+ rows: changed.map((r) => ({ label: fieldLabelOf(r.field), before: r.before, after: r.after })),
75
+ });
76
+ }
77
+ //# sourceMappingURL=diffTable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diffTable.js","sourceRoot":"","sources":["../../src/shared/diffTable.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /** 整页装配的入参。 */
2
+ interface DocPageInput {
3
+ /** head 的 `<title>` 文本。 */
4
+ readonly docTitle: string;
5
+ /** 正文标题(页面模板的 H1)。 */
6
+ readonly title: string;
7
+ /** 正文眉标(空串=不写这一行)。 */
8
+ readonly eyebrow: string;
9
+ /** 正文副标题(空串=不写这一行)。 */
10
+ readonly subtitle: string;
11
+ /** 已组合好的区块 HTML。 */
12
+ readonly content: string;
13
+ }
14
+ /** 整页装配:区块 HTML + 标题三件套 → 完整文档。 */
15
+ export declare function assembleDocPage(input: DocPageInput): string;
16
+ export {};
17
+ //# sourceMappingURL=docPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docPage.d.ts","sourceRoot":"","sources":["../../src/shared/docPage.ts"],"names":[],"mappings":"AAgBA,eAAe;AACf,UAAU,YAAY;IACpB,2BAA2B;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,sBAAsB;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,sBAAsB;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,uBAAuB;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,oBAAoB;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAwFD,mCAAmC;AACnC,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAY3D"}
@@ -0,0 +1,111 @@
1
+ /** 整页装配共用件:区块 HTML 拼成完整文档(最后一公里)。
2
+ *
3
+ * 谁在用(两个能力,指名):
4
+ * ① `src/record/`——写入域:结果型回执整页与过程型采集页;
5
+ * ② `src/query/`——查询域:通用查询列表页。
6
+ * 两域走的都是 `./pageShell.ts` 那一层,本件是它下面的文档装配层。
7
+ *
8
+ * 包裹约定(沿卡路里同件的 `docPage.ts`,不新增):
9
+ * 内容=`base-paint/blocks` 的区块;文档=`fillTemplate` 包裹(资产**裸文本**传入+由填充器包裹);
10
+ * `sharedCss = buildStyleSheet().css + blocksCss()`——**`blocksCss()` 必须拼进 `TemplateAssets.sharedCssText`**,
11
+ * 不得走 `StyleSheetInput.extraCss`(硬口径,见 `docs/skills/skill-bill/t406-base组件总表.md` 第 1.1 节 `blocksCss` 行)。
12
+ * 本文件不做取数、不装命令名与领域常量,只按参数拼页;零包内依赖(只 import `base-paint`)。
13
+ */
14
+ import { blocksCss, renderPageShell } from 'base-paint/blocks';
15
+ import { buildSharedHelpersJs, buildStyleSheet, fillTemplate } from 'base-paint';
16
+ /** 整页模板(裸标记+CONTENT 槽;`wrap ilife-page` 兼容既有 `--html` 断言)。
17
+ * 标记不得预包裹:资产由 `fillTemplate` 按 `ASSET_WRAPPERS` 自己包。 */
18
+ function docShell(docTitle) {
19
+ return '<!DOCTYPE html>\n<html lang="zh-CN">\n<head>\n<meta charset="utf-8">\n'
20
+ + '<meta name="viewport" content="width=device-width,initial-scale=1">\n'
21
+ + '<title>' + docTitle + '</title>\n<!--SHARED-CSS-->\n</head>\n<body>\n'
22
+ + '<div class="wrap ilife-page">\n<!--CONTENT-->\n</div>\n<!--SHARED-HELPERS-->\n'
23
+ + '</body>\n</html>';
24
+ }
25
+ /** 桌面端补丁(t407 根因整改二 D1:内容列用满宽+卡片网格换大格,只改呈现)。
26
+ *
27
+ * 为什么落在本件:公共层样式(`base-paint`)不许动,本页 body 后也不加样式块
28
+ * (机审「本页样式块/内联样式」两列要保持 0);唯一的落点就是本件拼 `sharedCssText`
29
+ * 的这一处——32 份产物走同一条代码路径,head 样式仍 32/32 同一份。
30
+ * 只在 `@media (min-width:1200px)` 里生效:手机端(`≤640px` 两列那档)一行不动,
31
+ * 复量不得回退(见证据件)。 */
32
+ const DESKTOP_CSS = [
33
+ '/* t407-D1:桌面端内容列与卡片网格(只 1200px 以上生效,手机端不动) */',
34
+ '@media (min-width:1200px) {',
35
+ ' .ilife-block-page-shell { max-width: 1120px; }',
36
+ ' .ilife-block-page-shell .ilife-block-kpi-card-grid { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }',
37
+ /* t403-P1:桌面端数据表与内容列同宽(只 1200px 以上生效,手机端不动)。
38
+ * 为什么落在本件:公共层 `base-paint` 的 `.ilife-block-data-table` 锁 `max-width:680px` 居中,
39
+ * 1120 版心下右约 40% 留白、密集页被迫折行(t403 视觉验收 D1);base 包只建议不动,唯一的落点
40
+ * 就是本件拼 `sharedCssText` 的这一处(与上面 D1 同一条路)。查询域列表/详情与写入域回执
41
+ * 同走本件,两域的表同宽;`≤640px` 行卡化那档一行不动。 */
42
+ ' .ilife-block-page-shell .ilife-block-data-table { max-width: none; }',
43
+ '}',
44
+ ].join('\n');
45
+ /** 手机端黑底说明块补丁(t407 第 3 轮返工 B;与上面 D1 同一处、同一条路)。
46
+ *
47
+ * 块是 `base-paint` 的 `renderToast` 那一条(`.ilife-toast`):左栏标题(「…标签流转:这一笔打…」,
48
+ * 窄栏里折成两到三行)+ 右栏标签胶囊(永远一行)。实测毛病两处:
49
+ * ① 两栏不等高——标题折了行、胶囊只有一行,整条横幅一边高一边矮(评审判「栏高不齐把整页拖重」);
50
+ * ② 行距偏紧——标题行贴着下面那几行说明,只隔 2px。
51
+ * 只动呈现:两栏拉到同高(`stretch` + 胶囊内的字居中对齐)、标题与说明的行距统一到 1.5 以上、
52
+ * 标题行与说明之间留 6px。文字、块序、复制载荷一处不动;公共层样式仍不许动,落点仍只在本件。 */
53
+ const TOAST_CSS = [
54
+ '/* t407-r3 B:黑底说明块(.ilife-toast)两栏等高+行距 */',
55
+ '.ilife-toast { line-height: 1.5; }',
56
+ '.ilife-toast-title-row { flex-wrap: nowrap; align-items: stretch; margin-bottom: 6px; }',
57
+ '.ilife-toast-title-row > .ilife-toast-title { flex: 1 1 auto; min-width: 0; margin-bottom: 0; line-height: 1.5; }',
58
+ '.ilife-toast-title-row > .ilife-toast-chip { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; border-radius: 10px; }',
59
+ '.ilife-toast-lines { line-height: 1.6; }',
60
+ ].join('\n');
61
+ /** 查询详情移动版式(t403-P2:只 640px 以下生效,桌面端不动)。
62
+ *
63
+ * 为什么落在本件:详情字段表(字段/值两列)在窄屏行卡化时会逐行重复表头标签
64
+ * (t403 视觉验收 D2);`renderDataTable` 的 `data-label` 恒取列头、无逐行标签选项,
65
+ * base 包只建议不动;本页 body 后也不加样式块(机审「本页样式块/内联样式」两列要保持 0),
66
+ * 唯一的落点就是本件拼 `sharedCssText` 的这一处(与上面 D1/TOAST 同一条路)。
67
+ * 桌面端(≥641px)只见表格、移动端(≤640px)只见键值列表——两者由 `display` 切换,
68
+ * 每端恰出一套(读屏器同 CSS 一起切,不存在两套同读);打印走桌面那一套。 */
69
+ const KV_CSS = [
70
+ '/* t403-P2:详情键值列表(桌面藏,移动端替表格) */',
71
+ '.ilife-query-kv-list { display: none; }',
72
+ '@media (max-width:640px) {',
73
+ ' .ilife-query-kv-table { display: none; }',
74
+ ' .ilife-query-kv-list { display: block; margin: 16px 0; border: 1px solid var(--line); border-radius: 14px; background: var(--card); }',
75
+ ' .ilife-query-kv-list > div { display: flex; align-items: baseline; justify-content: space-between; gap: 2px 10px; padding: 8px 12px; border-top: 1px solid rgba(210, 210, 215, .6); }',
76
+ ' .ilife-query-kv-list > div:first-child { border-top: 0; }',
77
+ ' .ilife-query-kv-list dt { flex: none; color: var(--fg3); font-size: 11.5px; font-weight: 600; }',
78
+ ' .ilife-query-kv-list dd { margin: 0; color: var(--fg); font-size: 12px; text-align: right; overflow-wrap: anywhere; }',
79
+ '}',
80
+ ].join('\n');
81
+ /** 退出口单钮独占整行(t410 终审 n20:撤销页「撤销这一笔」在 390 下实测 175px 半宽)。
82
+ *
83
+ * 根因在公共层(`base-render` 的 `.ilife-action-row` 恒两列,单钮只占半格),本包不许动公共层
84
+ * (#567 在途),落点只能是本件拼 `sharedCssText` 的这一处(与上面 D1/TOAST/KV 同一条路)。
85
+ * 只收退出口那一格(`copyArea.ts` 的 `undoExit`,动作号 `ilife-exit-undo`):该格恒只有一颗红钮,
86
+ * 把它所在行的列改成单列;页上别的动作行(两颗复制按钮平分的那种)一行不动。桌面端同形
87
+ * (危险动作独占一行,本就是层级所需)。`:has` 不支持时回退到半宽,不比改前差。 */
88
+ const EXIT_CSS = [
89
+ '/* t410:退出口单钮独占整行(只收 ilife-exit-undo 这一格) */',
90
+ '.ilife-block-copy-block:has([data-action-id="ilife-exit-undo"]) .ilife-action-row {',
91
+ ' grid-template-columns: 1fr;',
92
+ '}',
93
+ '.ilife-block-copy-block:has([data-action-id="ilife-exit-undo"]) .ilife-action-btn {',
94
+ ' width: 100%;',
95
+ '}',
96
+ ].join('\n');
97
+ /** 整页装配:区块 HTML + 标题三件套 → 完整文档。 */
98
+ export function assembleDocPage(input) {
99
+ const assets = {
100
+ sharedCssText: buildStyleSheet().css + '\n' + blocksCss() + '\n' + DESKTOP_CSS + '\n' + TOAST_CSS + '\n' + KV_CSS + '\n' + EXIT_CSS,
101
+ sharedHelpersJs: buildSharedHelpersJs(),
102
+ };
103
+ const body = renderPageShell({
104
+ title: input.title,
105
+ ...(input.eyebrow ? { eyebrow: input.eyebrow } : {}),
106
+ ...(input.subtitle ? { subtitle: input.subtitle } : {}),
107
+ content: input.content,
108
+ });
109
+ return fillTemplate({ template: docShell(input.docTitle), assets, content: body }).html;
110
+ }
111
+ //# sourceMappingURL=docPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docPage.js","sourceRoot":"","sources":["../../src/shared/docPage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAgBjF;yDACyD;AACzD,SAAS,QAAQ,CAAC,QAAgB;IAChC,OAAO,wEAAwE;UAC3E,uEAAuE;UACvE,SAAS,GAAG,QAAQ,GAAG,gDAAgD;UACvE,gFAAgF;UAChF,kBAAkB,CAAC;AACzB,CAAC;AAED;;;;;;mBAMmB;AACnB,MAAM,WAAW,GAAG;IAClB,gDAAgD;IAChD,6BAA6B;IAC7B,kDAAkD;IAClD,uHAAuH;IACvH;;;;0CAIsC;IACtC,wEAAwE;IACxE,GAAG;CACJ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb;;;;;;;sDAOsD;AACtD,MAAM,SAAS,GAAG;IAChB,4CAA4C;IAC5C,oCAAoC;IACpC,yFAAyF;IACzF,mHAAmH;IACnH,yJAAyJ;IACzJ,0CAA0C;CAC3C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb;;;;;;;6CAO6C;AAC7C,MAAM,MAAM,GAAG;IACb,kCAAkC;IAClC,yCAAyC;IACzC,4BAA4B;IAC5B,4CAA4C;IAC5C,yIAAyI;IACzI,yLAAyL;IACzL,6DAA6D;IAC7D,mGAAmG;IACnG,yHAAyH;IACzH,GAAG;CACJ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb;;;;;;iDAMiD;AACjD,MAAM,QAAQ,GAAG;IACf,8CAA8C;IAC9C,qFAAqF;IACrF,+BAA+B;IAC/B,GAAG;IACH,qFAAqF;IACrF,gBAAgB;IAChB,GAAG;CACJ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,mCAAmC;AACnC,MAAM,UAAU,eAAe,CAAC,KAAmB;IACjD,MAAM,MAAM,GAAG;QACb,aAAa,EAAE,eAAe,EAAE,CAAC,GAAG,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,IAAI,GAAG,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,QAAQ;QACnI,eAAe,EAAE,oBAAoB,EAAE;KACxC,CAAC;IACF,MAAM,IAAI,GAAG,eAAe,CAAC;QAC3B,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpD,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAC,CAAC;IACH,OAAO,YAAY,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;AAC1F,CAAC"}
@@ -0,0 +1,40 @@
1
+ import type { BillRow } from '../fetch/db.js';
2
+ /** 一次比对的探针:本次要写的金额/分类/日期(`account` 只用来加一格「同账户」)。 */
3
+ export interface DuplicateProbe {
4
+ readonly amount: number | null;
5
+ readonly category: string;
6
+ /** 日期或时刻串,只取前十位。 */
7
+ readonly date: string;
8
+ readonly account?: string;
9
+ /** 本次自己那条记录(回执页用:写完再报时要把自己排除)。 */
10
+ readonly excludeId?: number;
11
+ }
12
+ /** 一条撞上的记录。 */
13
+ export interface DuplicateHit {
14
+ readonly id: number;
15
+ readonly time: string;
16
+ readonly amount: number;
17
+ readonly category: string;
18
+ readonly account: string;
19
+ /** 账户也与本次一致(提示里加重一格)。 */
20
+ readonly sameAccount: boolean;
21
+ }
22
+ /** 判定:在「近期记录」里找出与探针撞上的那些条。**空数组=没撞上,页上不出提示条**。
23
+ * **分类没给即返回空数组**(分类是三条判据之一,缺了就不判,见件头口径)。 */
24
+ export declare function findDuplicates(recent: readonly BillRow[], probe: DuplicateProbe): DuplicateHit[];
25
+ /** 提示条:撞上了才出(`hits` 为空返回空串)。逐条一句话说全:编号/时刻/分类/金额/账户,
26
+ * 键名齐、顿号逗号断句,不拿裸空格拼字段(t410 round2:n26 弹窗整改)。
27
+ *
28
+ * 形态按页分(回执页不许出弹窗):
29
+ * - 采集页(缺省 `toast`):写库前预警,深色卡+关闭按钮,徽章与标题齐出;
30
+ * - 回执页(传 `'static'`):写库已成,重复信息只是补充说明,出浅色静态横幅
31
+ * (`staticNotice`:无关闭按钮、不进 toast 栈;静态形态不出徽章,故不传 `badge`)。
32
+ *
33
+ * 本轮整改(照 `docs/skills/skill-bill/t407-文字审查.md` 第 48、49 条):提示行与副行都是**版式位**,
34
+ * 行内不再拿 `·` 当版式——逐条那几行改「一句话说全」,标题里那句「黄条:提示,不阻断」是实现说明,删。
35
+ * t410 round2 补:标题「像不像重复」与首句「看着像重复」同义反复,标题改用户动作「先核一眼再定」;
36
+ * 回执页转静态后徽章不再出现(`test/record-write.test.mjs` 对应针脚同步改认「看着像重复」);
37
+ * 明细行不用 `·`(同文件第 309 行反向钉)。
38
+ */
39
+ export declare function duplicateNote(hits: readonly DuplicateHit[], probe: DuplicateProbe, mode?: 'toast' | 'static'): string;
40
+ //# sourceMappingURL=duplicateNote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicateNote.d.ts","sourceRoot":"","sources":["../../src/shared/duplicateNote.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAG9C,oDAAoD;AACpD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,oBAAoB;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,kCAAkC;IAClC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,eAAe;AACf,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,yBAAyB;IACzB,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B;AAED;6CAC6C;AAC7C,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,EAAE,KAAK,EAAE,cAAc,GAAG,YAAY,EAAE,CAuBhG;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,SAAS,YAAY,EAAE,EAC7B,KAAK,EAAE,cAAc,EACrB,IAAI,GAAE,OAAO,GAAG,QAAkB,GACjC,MAAM,CA2BR"}
@@ -0,0 +1,96 @@
1
+ /** 重复检测提示条(缺口块之一,**唯一定义地**):同日同额同分类(账户也同=加重一格)近期已有记录时给黄条提示。
2
+ *
3
+ * 谁在用(两个调用点,指名):
4
+ * ① `src/record/collect.ts`——过程型采集页:写库前先把疑似重复亮出来(老侧 `expense_form` 的形态);
5
+ * ② `src/record/receipt.ts`——结果型回执整页:写完之后再报一次「这一笔与哪几笔撞了」(排除本次这条 id)。
6
+ * 第二个消费者:`src/query/`(随兄弟图 #403 的查询域一起到位,同一份判定直接调)。
7
+ * 本票实测:本包 `src/` 下真引用本件的就是上面两个调用点,再无第三处。
8
+ * (件头原来写的「记收入/记报销/记一笔」是以后才有的调用点,不是今天的调用方,按实际改掉。)
9
+ *
10
+ * 判定口径(**一处定义**,三条判据 + 一条加分项):
11
+ * - 必中三条一起:**已给分类**(整串比,三级写全才算同一类)+ **同一天**(取 `time` 的前十位,逐字比)
12
+ * + **同金额**(两位小数比,避免浮点尾巴)。**分类没给=不出条**——采集页正是还没填分类的那一步,
13
+ * 按「同日同额」比会把常见金额天天报成疑似重复(宁可漏提示,不误报);
14
+ * - 加分:账户也同 → 那一笔标「同账户」(老侧口径含账户,本仓把它当加重而不是门槛,免得漏报不同账户的撞单);
15
+ * - 软删记录不参与(取数层已排除 `deleted_at` 非空的行)。
16
+ * 提示色取黄系(`warn`),**独立一块**,不与别的提示共容器(施工图第三节第 8 行原话)。
17
+ * 金额的两位小数文本不在这里另写一份:走 `./summaryRow.ts` 的 `money2`。
18
+ */
19
+ import { renderFeedbackBlock } from 'base-paint/blocks';
20
+ import { money2 } from './summaryRow.js';
21
+ /** 判定:在「近期记录」里找出与探针撞上的那些条。**空数组=没撞上,页上不出提示条**。
22
+ * **分类没给即返回空数组**(分类是三条判据之一,缺了就不判,见件头口径)。 */
23
+ export function findDuplicates(recent, probe) {
24
+ const amount = probe.amount;
25
+ if (amount === null || !Number.isFinite(amount))
26
+ return [];
27
+ const category = probe.category.trim();
28
+ if (category === '')
29
+ return [];
30
+ const day = probe.date.slice(0, 10);
31
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(day))
32
+ return [];
33
+ const account = (probe.account ?? '').trim();
34
+ return recent
35
+ .filter((r) => {
36
+ if (probe.excludeId !== undefined && r.id === probe.excludeId)
37
+ return false;
38
+ if (r.time.slice(0, 10) !== day)
39
+ return false;
40
+ if (money2(r.amount) !== money2(amount))
41
+ return false;
42
+ return r.category === category;
43
+ })
44
+ .map((r) => ({
45
+ id: r.id,
46
+ time: r.time,
47
+ amount: r.amount,
48
+ category: r.category,
49
+ account: r.account,
50
+ sameAccount: account !== '' && r.account === account,
51
+ }));
52
+ }
53
+ /** 提示条:撞上了才出(`hits` 为空返回空串)。逐条一句话说全:编号/时刻/分类/金额/账户,
54
+ * 键名齐、顿号逗号断句,不拿裸空格拼字段(t410 round2:n26 弹窗整改)。
55
+ *
56
+ * 形态按页分(回执页不许出弹窗):
57
+ * - 采集页(缺省 `toast`):写库前预警,深色卡+关闭按钮,徽章与标题齐出;
58
+ * - 回执页(传 `'static'`):写库已成,重复信息只是补充说明,出浅色静态横幅
59
+ * (`staticNotice`:无关闭按钮、不进 toast 栈;静态形态不出徽章,故不传 `badge`)。
60
+ *
61
+ * 本轮整改(照 `docs/skills/skill-bill/t407-文字审查.md` 第 48、49 条):提示行与副行都是**版式位**,
62
+ * 行内不再拿 `·` 当版式——逐条那几行改「一句话说全」,标题里那句「黄条:提示,不阻断」是实现说明,删。
63
+ * t410 round2 补:标题「像不像重复」与首句「看着像重复」同义反复,标题改用户动作「先核一眼再定」;
64
+ * 回执页转静态后徽章不再出现(`test/record-write.test.mjs` 对应针脚同步改认「看着像重复」);
65
+ * 明细行不用 `·`(同文件第 309 行反向钉)。
66
+ */
67
+ export function duplicateNote(hits, probe, mode = 'toast') {
68
+ if (hits.length === 0)
69
+ return '';
70
+ const lines = hits.map((h) => '记录编号 ' + h.id + '(' + h.time + '):' + h.category
71
+ + ',' + money2(h.amount) + ',账户' + (h.account === '' ? '没设置' : h.account)
72
+ + (h.sameAccount ? '(同一个账户)' : ''));
73
+ const detail = '对的是 ' + probe.date.slice(0, 10) + ' 这一天,金额 ' + money2(probe.amount)
74
+ + ',分类 ' + probe.category.trim();
75
+ if (mode === 'static') {
76
+ return renderFeedbackBlock({
77
+ title: '先核一眼再定',
78
+ toast: {
79
+ msg: '看着像重复:同一天、同金额、同分类,已经有 ' + hits.length + ' 笔了',
80
+ detail,
81
+ icon: 'warn',
82
+ lines,
83
+ },
84
+ staticNotice: true,
85
+ });
86
+ }
87
+ const toast = {
88
+ msg: '看着像重复:同一天、同金额、同分类,已经有 ' + hits.length + ' 笔了',
89
+ detail,
90
+ icon: 'warn',
91
+ badge: { text: '疑似重复', type: 'warn' },
92
+ lines,
93
+ };
94
+ return renderFeedbackBlock({ title: '先核一眼再定', toast });
95
+ }
96
+ //# sourceMappingURL=duplicateNote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicateNote.js","sourceRoot":"","sources":["../../src/shared/duplicateNote.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /** 空态入参:`next`=下一步(必填,非空)。 */
2
+ interface EmptyNoteInput {
3
+ /** 空态小标题(如「没有可选的历史账户」)。 */
4
+ readonly title?: string;
5
+ /** 现在是什么都没有。 */
6
+ readonly text: string;
7
+ /** 接下来怎么办(必填:一句能照着做的下一步)。 */
8
+ readonly next: string;
9
+ /** 可选的补救动作 HTML(受信透传,由调用方用 base 件拼好)。 */
10
+ readonly actionHtml?: string;
11
+ }
12
+ /** 空态区块:一句「什么都没有」+ 一句「接下来怎么办」。 */
13
+ export declare function emptyNote(input: EmptyNoteInput): string;
14
+ export {};
15
+ //# sourceMappingURL=emptyNote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emptyNote.d.ts","sourceRoot":"","sources":["../../src/shared/emptyNote.ts"],"names":[],"mappings":"AAeA,8BAA8B;AAC9B,UAAU,cAAc;IACtB,2BAA2B;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB;IAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6BAA6B;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yCAAyC;IACzC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,kCAAkC;AAClC,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAUvD"}