skill-schedule 0.3.3 → 0.3.13

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 (187) hide show
  1. package/SKILL.md +12 -3
  2. package/dist/.gen-inputs.json +4 -4
  3. package/dist/admin/adminDocs.d.ts +57 -0
  4. package/dist/admin/adminDocs.d.ts.map +1 -0
  5. package/dist/admin/adminDocs.js +190 -0
  6. package/dist/admin/adminDocs.js.map +1 -0
  7. package/dist/admin/adminParts.d.ts +37 -0
  8. package/dist/admin/adminParts.d.ts.map +1 -0
  9. package/dist/admin/adminParts.js +83 -0
  10. package/dist/admin/adminParts.js.map +1 -0
  11. package/dist/admin/handlers.d.ts.map +1 -1
  12. package/dist/admin/handlers.js +196 -6
  13. package/dist/admin/handlers.js.map +1 -1
  14. package/dist/admin/routes.d.ts +10 -1
  15. package/dist/admin/routes.d.ts.map +1 -1
  16. package/dist/admin/routes.js +2 -0
  17. package/dist/admin/routes.js.map +1 -1
  18. package/dist/analyze/analyzeDocs.d.ts +51 -0
  19. package/dist/analyze/analyzeDocs.d.ts.map +1 -0
  20. package/dist/analyze/analyzeDocs.js +442 -0
  21. package/dist/analyze/analyzeDocs.js.map +1 -0
  22. package/dist/analyze/analyzeParts.d.ts +53 -0
  23. package/dist/analyze/analyzeParts.d.ts.map +1 -0
  24. package/dist/analyze/analyzeParts.js +229 -0
  25. package/dist/analyze/analyzeParts.js.map +1 -0
  26. package/dist/analyze/handlers.d.ts.map +1 -1
  27. package/dist/analyze/handlers.js +41 -4
  28. package/dist/analyze/handlers.js.map +1 -1
  29. package/dist/cli/cmd_read.js +8 -2
  30. package/dist/cli/cmd_read.js.map +1 -1
  31. package/dist/cli/config.d.ts +0 -9
  32. package/dist/cli/config.d.ts.map +1 -1
  33. package/dist/cli/config.js +20 -1
  34. package/dist/cli/config.js.map +1 -1
  35. package/dist/fetch/feishu.d.ts +6 -0
  36. package/dist/fetch/feishu.d.ts.map +1 -1
  37. package/dist/fetch/feishu.js +18 -7
  38. package/dist/fetch/feishu.js.map +1 -1
  39. package/dist/fetch/index.d.ts +1 -1
  40. package/dist/fetch/index.d.ts.map +1 -1
  41. package/dist/fetch/index.js +1 -1
  42. package/dist/fetch/index.js.map +1 -1
  43. package/dist/health.d.ts +11 -0
  44. package/dist/health.d.ts.map +1 -1
  45. package/dist/health.js +48 -22
  46. package/dist/health.js.map +1 -1
  47. package/dist/help/index.d.ts +1 -0
  48. package/dist/help/index.d.ts.map +1 -1
  49. package/dist/help/index.js +3 -0
  50. package/dist/help/index.js.map +1 -1
  51. package/dist/plan/discussDocs.d.ts +8 -0
  52. package/dist/plan/discussDocs.d.ts.map +1 -0
  53. package/dist/plan/discussDocs.js +321 -0
  54. package/dist/plan/discussDocs.js.map +1 -0
  55. package/dist/plan/feishuDocs.d.ts +19 -0
  56. package/dist/plan/feishuDocs.d.ts.map +1 -0
  57. package/dist/plan/feishuDocs.js +257 -0
  58. package/dist/plan/feishuDocs.js.map +1 -0
  59. package/dist/plan/handlers.d.ts.map +1 -1
  60. package/dist/plan/handlers.js +107 -3
  61. package/dist/plan/handlers.js.map +1 -1
  62. package/dist/plan/index.d.ts +2 -0
  63. package/dist/plan/index.d.ts.map +1 -1
  64. package/dist/plan/index.js +16 -9
  65. package/dist/plan/index.js.map +1 -1
  66. package/dist/plan/iso.d.ts +2 -0
  67. package/dist/plan/iso.d.ts.map +1 -1
  68. package/dist/plan/iso.js +6 -2
  69. package/dist/plan/iso.js.map +1 -1
  70. package/dist/plan/planDocs.d.ts +47 -0
  71. package/dist/plan/planDocs.d.ts.map +1 -0
  72. package/dist/plan/planDocs.js +159 -0
  73. package/dist/plan/planDocs.js.map +1 -0
  74. package/dist/plan/planParts.d.ts +38 -0
  75. package/dist/plan/planParts.d.ts.map +1 -0
  76. package/dist/plan/planParts.js +96 -0
  77. package/dist/plan/planParts.js.map +1 -0
  78. package/dist/plan/probe.d.ts +28 -0
  79. package/dist/plan/probe.d.ts.map +1 -0
  80. package/dist/plan/probe.js +114 -0
  81. package/dist/plan/probe.js.map +1 -0
  82. package/dist/plan/receipt.d.ts +12 -0
  83. package/dist/plan/receipt.d.ts.map +1 -1
  84. package/dist/plan/receipt.js +17 -0
  85. package/dist/plan/receipt.js.map +1 -1
  86. package/dist/plan/receiptDocs.d.ts +26 -0
  87. package/dist/plan/receiptDocs.d.ts.map +1 -0
  88. package/dist/plan/receiptDocs.js +341 -0
  89. package/dist/plan/receiptDocs.js.map +1 -0
  90. package/dist/plan/replayDocs.d.ts +165 -0
  91. package/dist/plan/replayDocs.d.ts.map +1 -0
  92. package/dist/plan/replayDocs.js +226 -0
  93. package/dist/plan/replayDocs.js.map +1 -0
  94. package/dist/plan/replaySections.d.ts +5 -0
  95. package/dist/plan/replaySections.d.ts.map +1 -0
  96. package/dist/plan/replaySections.js +471 -0
  97. package/dist/plan/replaySections.js.map +1 -0
  98. package/dist/plan/reviewDocs.d.ts +4 -0
  99. package/dist/plan/reviewDocs.d.ts.map +1 -0
  100. package/dist/plan/reviewDocs.js +130 -0
  101. package/dist/plan/reviewDocs.js.map +1 -0
  102. package/dist/plan/sync.d.ts.map +1 -1
  103. package/dist/plan/sync.js +5 -0
  104. package/dist/plan/sync.js.map +1 -1
  105. package/dist/policy/index.d.ts +1 -1
  106. package/dist/policy/index.d.ts.map +1 -1
  107. package/dist/policy/index.js +1 -1
  108. package/dist/policy/index.js.map +1 -1
  109. package/dist/policy/plan.d.ts +8 -0
  110. package/dist/policy/plan.d.ts.map +1 -1
  111. package/dist/policy/plan.js +20 -0
  112. package/dist/policy/plan.js.map +1 -1
  113. package/dist/query/handlers.d.ts +3 -0
  114. package/dist/query/handlers.d.ts.map +1 -1
  115. package/dist/query/handlers.js +86 -12
  116. package/dist/query/handlers.js.map +1 -1
  117. package/dist/query/queryDocs.d.ts +64 -0
  118. package/dist/query/queryDocs.d.ts.map +1 -0
  119. package/dist/query/queryDocs.js +554 -0
  120. package/dist/query/queryDocs.js.map +1 -0
  121. package/dist/query/routes.d.ts +11 -1
  122. package/dist/query/routes.d.ts.map +1 -1
  123. package/dist/query/routes.js +5 -1
  124. package/dist/query/routes.js.map +1 -1
  125. package/dist/render/copyArea.d.ts +61 -0
  126. package/dist/render/copyArea.d.ts.map +1 -0
  127. package/dist/render/copyArea.js +80 -0
  128. package/dist/render/copyArea.js.map +1 -0
  129. package/dist/render/html.d.ts.map +1 -1
  130. package/dist/render/html.js +3 -1
  131. package/dist/render/html.js.map +1 -1
  132. package/dist/render/index.d.ts +3 -0
  133. package/dist/render/index.d.ts.map +1 -1
  134. package/dist/render/index.js +2 -0
  135. package/dist/render/index.js.map +1 -1
  136. package/dist/shared/dayPage.d.ts +67 -0
  137. package/dist/shared/dayPage.d.ts.map +1 -0
  138. package/dist/shared/dayPage.js +92 -0
  139. package/dist/shared/dayPage.js.map +1 -0
  140. package/dist/shared/detailPage.d.ts +55 -0
  141. package/dist/shared/detailPage.d.ts.map +1 -0
  142. package/dist/shared/detailPage.js +65 -0
  143. package/dist/shared/detailPage.js.map +1 -0
  144. package/dist/shared/docPage.d.ts +22 -0
  145. package/dist/shared/docPage.d.ts.map +1 -0
  146. package/dist/shared/docPage.js +52 -0
  147. package/dist/shared/docPage.js.map +1 -0
  148. package/dist/shared/overviewPage.d.ts +52 -0
  149. package/dist/shared/overviewPage.d.ts.map +1 -0
  150. package/dist/shared/overviewPage.js +65 -0
  151. package/dist/shared/overviewPage.js.map +1 -0
  152. package/dist/shared/pageNav.d.ts +24 -0
  153. package/dist/shared/pageNav.d.ts.map +1 -0
  154. package/dist/shared/pageNav.js +70 -0
  155. package/dist/shared/pageNav.js.map +1 -0
  156. package/dist/shared/pageParts.d.ts +45 -0
  157. package/dist/shared/pageParts.d.ts.map +1 -0
  158. package/dist/shared/pageParts.js +154 -0
  159. package/dist/shared/pageParts.js.map +1 -0
  160. package/dist/shared/planPage.d.ts +49 -0
  161. package/dist/shared/planPage.d.ts.map +1 -0
  162. package/dist/shared/planPage.js +48 -0
  163. package/dist/shared/planPage.js.map +1 -0
  164. package/dist/shared/rangePage.d.ts +44 -0
  165. package/dist/shared/rangePage.d.ts.map +1 -0
  166. package/dist/shared/rangePage.js +50 -0
  167. package/dist/shared/rangePage.js.map +1 -0
  168. package/dist/shared/weekPage.d.ts +36 -0
  169. package/dist/shared/weekPage.d.ts.map +1 -0
  170. package/dist/shared/weekPage.js +42 -0
  171. package/dist/shared/weekPage.js.map +1 -0
  172. package/dist/triggers/routes.generated.d.ts +23 -1
  173. package/dist/triggers/routes.generated.d.ts.map +1 -1
  174. package/dist/triggers/routes.generated.js +4 -1
  175. package/dist/triggers/routes.generated.js.map +1 -1
  176. package/dist/write/handlers.d.ts.map +1 -1
  177. package/dist/write/handlers.js +101 -5
  178. package/dist/write/handlers.js.map +1 -1
  179. package/dist/write/writeDocs.d.ts +27 -0
  180. package/dist/write/writeDocs.d.ts.map +1 -0
  181. package/dist/write/writeDocs.js +423 -0
  182. package/dist/write/writeDocs.js.map +1 -0
  183. package/dist/write/writeParts.d.ts +54 -0
  184. package/dist/write/writeParts.d.ts.map +1 -0
  185. package/dist/write/writeParts.js +117 -0
  186. package/dist/write/writeParts.js.map +1 -0
  187. package/package.json +3 -3
@@ -0,0 +1,321 @@
1
+ /** #787 · 「商量计划」的**两张页**(口径与页装配):过程型预览(f12)与制定次日计划结果(f17)。
2
+ *
3
+ * 为什么两张页分开(照地图裁决):预览**不写库**,结果页写完之后才有——「先出候选、确认后才写库」,
4
+ * 两张各自能独立跑(`op=preview` 与 `op=upsert`)。
5
+ *
6
+ * 口径住这里(共用位不许出现任何一个能力的名字):
7
+ * · **空隙**=候选之间没排上的连续时段——与「查日程」的空档同一条规则,函数只此一处(`planDocs`);
8
+ * · **重叠**=候选段与这一天原有事件在时间上搭着边(不要求整段包住);
9
+ * · **历史贴**=拿这一天往前 7 天的**作息记录**按整点归类,取该事件覆盖时段里出现最多的那个分类做主档
10
+ * (老侧 `plan_scenarios.py` 的 `build_history_habits` 与 `fit_events`,默认窗口 7 天);
11
+ * 候选那条的一级分类……**不折算**,就按库里那一串分类名比(老侧同口径)。
12
+ *
13
+ * **页上文案纪律**:不出现命令键、库列名、参数名、票号;并列关系用表格列与对照行表达,不拿
14
+ * `·`/`|`/`~`/`、`/`;` 顶替设计。范围一律写「至」。
15
+ */
16
+ import { renderCaliberLine, renderConclusionBar, renderDataTable, renderDisclosure, renderFeedbackBlock, renderKpiGrid, renderListRows, renderPreBlock, } from 'base-paint/blocks';
17
+ import { renderFactStrip } from 'base-paint';
18
+ import { listPlanEvents, listRecordsRange } from '../fetch/index.js';
19
+ import { LEVEL1_WHITELIST, fmtDur, fmtDurShort, l1Of, } from '../policy/index.js';
20
+ import { scheduleCopyArea } from '../render/copyArea.js';
21
+ import { assembleDocPage } from '../shared/docPage.js';
22
+ import { pageSections } from '../shared/pageNav.js';
23
+ import { hourCellsOf, renderHourBand } from '../shared/pageParts.js';
24
+ import { clockOf, gapsOf, minutesOfDayEnd } from './planDocs.js';
25
+ import { shiftDay } from './iso.js';
26
+ import { REMOTE_LABEL, planCopyArea } from './receipt.js';
27
+ import { planPartsCss } from './planParts.js';
28
+ const EYEBROW = '作息管家 日程与计划';
29
+ /** 一级分类的权威顺序(色带配色与图例都照它),定义只此一处:`policy` 的白名单。 */
30
+ const L1_ORDER = [...LEVEL1_WHITELIST];
31
+ /** 历史窗口(天):老侧 `plan_scenarios.DEFAULT_HISTORY_DAYS`。 */
32
+ const HISTORY_DAYS = 7;
33
+ const HOURS_PER_DAY = 24;
34
+ const span = (start, end) => start + ' 至 ' + end;
35
+ const categoryOf = (c) => (c === null || c === undefined || c === '' ? '未分类' : c);
36
+ /** 钟点 → 分钟(**这一天末尾那一格按 24:00 算**,与空档同一条口径):定义在 `planDocs`,本件只引用。 */
37
+ const minutesOf = minutesOfDayEnd;
38
+ function cellsOf(events) {
39
+ return hourCellsOf(events.map((e) => ({
40
+ start: minutesOf(e.time_start),
41
+ end: minutesOf(e.time_end),
42
+ key: l1Of(e.category ?? ''),
43
+ })));
44
+ }
45
+ /** 这一段覆盖到哪几个整点(末段 24:00 收在 23 点)。 */
46
+ function hoursOf(start, end) {
47
+ const from = Math.floor(minutesOf(start) / 60);
48
+ const to = Math.ceil(minutesOf(end) / 60);
49
+ const out = [];
50
+ for (let h = from; h < to && h < HOURS_PER_DAY; h += 1)
51
+ out.push(h);
52
+ return out;
53
+ }
54
+ /** 历史作息按整点归类:小时 → 分类 → 出现次数(老侧 `build_history_habits` 同口径)。 */
55
+ function habitsOf(records) {
56
+ const out = new Map();
57
+ for (const r of records) {
58
+ const name = categoryOf(r.category);
59
+ for (const h of hoursOf(r.time_start, r.time_end)) {
60
+ const bucket = out.get(h) ?? new Map();
61
+ bucket.set(name, (bucket.get(name) ?? 0) + 1);
62
+ out.set(h, bucket);
63
+ }
64
+ }
65
+ return out;
66
+ }
67
+ /** 某几个整点里出现最多的那个分类(次数并列时取字典序在前的那个,读数才可复现)。 */
68
+ function dominantOf(habits, hours) {
69
+ const total = new Map();
70
+ for (const h of hours) {
71
+ for (const [name, n] of habits.get(h) ?? [])
72
+ total.set(name, (total.get(name) ?? 0) + n);
73
+ }
74
+ let best = null;
75
+ for (const [category, count] of [...total.entries()].sort((a, b) => (a[0] < b[0] ? -1 : 1))) {
76
+ if (best === null || count > best.count)
77
+ best = { category, count };
78
+ }
79
+ return best;
80
+ }
81
+ /** 一条候选与历史的贴合:贴合/偏离/无参考 + 一句人话提示(老侧 `fit_events` 的三档)。 */
82
+ function fitOf(category, habits, start, end) {
83
+ const top = dominantOf(habits, hoursOf(start, end));
84
+ if (top === null)
85
+ return { fit: 'none', hint: '该时段近几天没有作息记录,这一条没有历史可参照。' };
86
+ const mine = categoryOf(category);
87
+ if (category === null || category === undefined || category === '') {
88
+ return { fit: 'none', hint: '这一条没有标分类,历史该时段主要是「' + top.category + '」。' };
89
+ }
90
+ if (mine === top.category) {
91
+ return { fit: 'match', hint: '贴合历史:该时段近几天主要是「' + top.category + '」。' };
92
+ }
93
+ return {
94
+ fit: 'drift',
95
+ hint: '与历史不同:该时段近几天主要是「' + top.category + '」(' + String(top.count) + ' 次),这一条排的是「' + mine + '」。',
96
+ };
97
+ }
98
+ function overlapsOf(candidates, locked) {
99
+ const out = [];
100
+ for (const c of candidates) {
101
+ for (const l of locked) {
102
+ const lo = Math.max(minutesOf(c.time_start), minutesOf(l.time_start));
103
+ const hi = Math.min(minutesOf(c.time_end), minutesOf(l.time_end));
104
+ if (hi > lo) {
105
+ out.push({
106
+ candidate: c.title + '(' + span(c.time_start, c.time_end) + ')',
107
+ locked: l.title + '(' + span(l.time_start, l.time_end) + ')',
108
+ overlap: span(clockOf(lo), clockOf(hi)),
109
+ minutes: hi - lo,
110
+ });
111
+ }
112
+ }
113
+ }
114
+ return out;
115
+ }
116
+ /* ─────────────────────── ① 商量计划预览(老侧 f12,过程型) ─────────────────────── */
117
+ /** 「商量计划预览」整页:候选事件表 + 锁定事件区 + 空隙提示。**这一页不写库**。 */
118
+ export function previewPage(handle, date, candidates) {
119
+ const locked = listPlanEvents(handle, date);
120
+ const gaps = gapsOf(candidates);
121
+ const overlaps = overlapsOf(candidates, locked);
122
+ const covered = candidates.reduce((sum, e) => sum + (minutesOf(e.time_end) - minutesOf(e.time_start)), 0);
123
+ const rows = candidates.map((e) => ({
124
+ time: span(e.time_start, e.time_end),
125
+ title: e.title,
126
+ category: categoryOf(e.category),
127
+ notes: e.notes === undefined || e.notes === null || e.notes === '' ? '—' : e.notes,
128
+ }));
129
+ const lockedRows = locked.map((e) => ({
130
+ left: span(e.time_start, e.time_end),
131
+ main: e.title,
132
+ right: categoryOf(e.category),
133
+ }));
134
+ const head = {
135
+ docTitle: '作息管家 商量计划预览',
136
+ eyebrow: EYEBROW,
137
+ title: '商量计划预览',
138
+ subtitle: '候选 ' + String(candidates.length) + ' 段排的是 ' + date + ',这一步只预览,没有写库。',
139
+ };
140
+ const { toc, body } = pageSections([
141
+ { html: renderConclusionBar('候选 ' + String(candidates.length) + ' 段把 ' + date + ' 的 24 小时接满,'
142
+ + '共 ' + fmtDur(covered) + '。这一天现在已有 ' + String(locked.length) + ' 件,其中 '
143
+ + String(overlaps.length) + ' 处与候选时段重叠。') },
144
+ { navText: '这一版候选的读数', html: renderKpiGrid([
145
+ { label: '候选段数', value: String(candidates.length) + ' 段', detail: '这一版商量出来的' },
146
+ {
147
+ label: '24 小时覆盖', value: fmtDur(covered),
148
+ detail: covered >= HOURS_PER_DAY * 60 ? '整天接满,可以落盘' : '还没接满,先补上缺口',
149
+ bar: { pct: Math.min(100, Math.round((covered / (HOURS_PER_DAY * 60)) * 100)) },
150
+ },
151
+ { label: '与已有重叠', value: String(overlaps.length) + ' 处', detail: overlaps.length === 0 ? '与原有排布不搭边' : '落盘前先看一眼' },
152
+ { label: '这一天已有', value: String(locked.length) + ' 件', detail: '现在库里的排布' },
153
+ ], { title: '这一版候选的读数' }) },
154
+ // 段名是「候选的 24 小时」;目录项逐字用它。
155
+ { navText: '候选的 24 小时', html: renderHourBand(cellsOf(candidates), { order: L1_ORDER, title: '候选的 24 小时', height: 120 }) },
156
+ { html: renderDataTable({
157
+ caption: '候选事件',
158
+ columns: [
159
+ { key: 'time', label: '时段' },
160
+ { key: 'title', label: '标题' },
161
+ { key: 'category', label: '分类' },
162
+ { key: 'notes', label: '备注' },
163
+ ],
164
+ rows,
165
+ emptyText: '这一版一段也没给',
166
+ }) },
167
+ { html: renderCaliberLine('候选是把这一天整段重排 | 落盘时先按日期整日覆盖,候选之外的那些会被标记成不再算') },
168
+ { html: renderDisclosure({
169
+ title: '锁定事件区',
170
+ contentHtml: (locked.length === 0
171
+ ? '<p class="sch-pl-note">这一天现在库里没有别的安排,落盘不会顶掉任何一件。</p>'
172
+ : '<p class="sch-pl-note">这些是这一天现在库里的排布,落盘时会被候选整段换掉。</p>')
173
+ + renderListRows({ items: lockedRows, emptyText: '这一天现在库里没有别的安排' }),
174
+ }) },
175
+ { html: renderDisclosure({
176
+ title: '空隙提示',
177
+ contentHtml: renderListRows({
178
+ items: gaps.map((g) => ({ left: span(clockOf(g.start), clockOf(g.end)), main: '空隙', right: fmtDurShort(g.end - g.start) })),
179
+ emptyText: '候选把 24 小时一段接一段接满了,没有空隙。',
180
+ }),
181
+ }) },
182
+ { html: renderPreBlock({
183
+ label: '认可这一版就说这一句',
184
+ command: '按这一版把 ' + date + ' 的计划落下来',
185
+ actionId: 'ilife-sch-plan-copy-next',
186
+ copyLabel: '复制这句话',
187
+ }) },
188
+ { navText: '复制与留档', html: scheduleCopyArea({
189
+ title: '复制与留档',
190
+ dataActionId: 'ilife-sch-plan-copy-data',
191
+ logActionId: 'ilife-sch-plan-copy-log',
192
+ ...planCopyArea({
193
+ message: '商量计划预览:日期 ' + date + ',候选 ' + String(candidates.length) + ' 段共 ' + fmtDur(covered)
194
+ + ',与已有重叠 ' + String(overlaps.length) + ' 处',
195
+ command: 'schedule-cmd-read schedule.plan.write --params {"op":"preview","date":"' + date + '"}',
196
+ source: '日程计划表与作息记录表(近 ' + String(HISTORY_DAYS) + ' 天)',
197
+ }),
198
+ }) },
199
+ ]);
200
+ return assembleDocPage({ head, content: toc + body, extraCss: planPartsCss() });
201
+ }
202
+ /* ─────────────────────── ② 制定次日计划结果(老侧 f17) ─────────────────────── */
203
+ /** 「制定次日计划结果」整页:时间轴与分类色带 + 历史贴合提示 + 冲突与警告 + 复制 prompt 位。 */
204
+ export function resultPage(handle, date, candidates, locked, remote) {
205
+ const written = listPlanEvents(handle, date);
206
+ const records = listRecordsRange(handle, shiftDay(date, -HISTORY_DAYS), shiftDay(date, -1));
207
+ const habits = habitsOf(records);
208
+ const fits = candidates.map((e) => ({ event: e, ...fitOf(e.category, habits, e.time_start, e.time_end) }));
209
+ const match = fits.filter((f) => f.fit === 'match').length;
210
+ const drift = fits.filter((f) => f.fit === 'drift').length;
211
+ const none = fits.filter((f) => f.fit === 'none').length;
212
+ const referred = match + drift;
213
+ const rate = referred === 0 ? null : Math.round((match / referred) * 1000) / 10;
214
+ const overlaps = overlapsOf(candidates, locked);
215
+ const covered = candidates.reduce((sum, e) => sum + (minutesOf(e.time_end) - minutesOf(e.time_start)), 0);
216
+ /** 逐段贴合(四列:时段/标题/贴合/说明):**不再单列分类那一栏**——说明句里已经点名
217
+ * 两边各是什么,五列挤在 880 里会把「贴合」两个字折成两行(复看时抓到的版式债)。 */
218
+ const fitRows = fits.map((f) => ({
219
+ time: span(f.event.time_start, f.event.time_end),
220
+ title: f.event.title,
221
+ fit: f.fit === 'match' ? '贴合' : (f.fit === 'drift' ? '偏离' : '无参考'),
222
+ hint: f.hint,
223
+ }));
224
+ const head = {
225
+ docTitle: '作息管家 制定次日计划结果',
226
+ eyebrow: EYEBROW,
227
+ title: '制定次日计划结果',
228
+ subtitle: '落下来的是 ' + date + ',候选 ' + String(candidates.length) + ' 段,'
229
+ + '贴历史 ' + String(match) + ' 段,偏离 ' + String(drift) + ' 段。',
230
+ };
231
+ const { toc, body } = pageSections([
232
+ { html: renderConclusionBar('这一版已经落盘:' + date + ' 现在排着 ' + String(written.length) + ' 件,'
233
+ + '共 ' + fmtDur(covered) + '。' + (rate === null
234
+ ? '近 ' + String(HISTORY_DAYS) + ' 天没有作息记录,贴合与否无从比较。'
235
+ : '与近 ' + String(HISTORY_DAYS) + ' 天的作息习惯贴合率 ' + String(rate) + '%。')) },
236
+ { navText: '这一版的读数', html: renderKpiGrid([
237
+ { label: '候选段数', value: String(candidates.length) + ' 段', detail: date },
238
+ {
239
+ label: '历史贴合率', value: rate === null ? '—' : String(rate) + '%',
240
+ detail: rate === null ? '没有可比的记录' : '贴合 ' + String(match) + ' 段,偏离 ' + String(drift) + ' 段',
241
+ ...(rate === null ? {} : { bar: { pct: Math.round(rate) } }),
242
+ },
243
+ { label: '偏离与无参考', value: String(drift) + ' 段偏离', detail: '另有 ' + String(none) + ' 段无历史可参照' },
244
+ { label: '冲突', value: String(overlaps.length) + ' 处', detail: overlaps.length === 0 ? '与原有排布不搭边' : '采纳前先看一眼' },
245
+ ], { title: '这一版的读数' }) },
246
+ // 段名是「24 小时时间轴与分类色带」;目录项逐字用它。
247
+ { navText: '24 小时时间轴与分类色带', html: renderHourBand(cellsOf(written), { order: L1_ORDER, title: '24 小时时间轴与分类色带', height: 140 }) },
248
+ { html: renderFactStrip({
249
+ items: [
250
+ { label: '历史窗口', value: String(HISTORY_DAYS) + ' 天' },
251
+ { label: '这段窗口的作息记录', value: String(records.length) + ' 条' },
252
+ { label: '远端侧', value: REMOTE_LABEL[remote] },
253
+ { label: '这一天现在', value: String(written.length) + ' 件' },
254
+ ],
255
+ }) },
256
+ { navText: '历史贴合提示', html: renderFeedbackBlock({
257
+ title: '历史贴合提示',
258
+ toast: {
259
+ icon: rate === null ? 'info' : (drift === 0 ? 'ok' : 'warn'),
260
+ msg: rate === null
261
+ ? '近 ' + String(HISTORY_DAYS) + ' 天没有作息记录,这一版没有历史可参照,逐段都按无参考算。'
262
+ : '拿近 ' + String(HISTORY_DAYS) + ' 天的作息记录按整点归类:贴合 ' + String(match) + ' 段,偏离 '
263
+ + String(drift) + ' 段,无参考 ' + String(none) + ' 段。'
264
+ + (drift === 0 ? '' : '逐段原因见下表「逐段贴合」。'),
265
+ },
266
+ staticNotice: true,
267
+ }) },
268
+ { html: renderDataTable({
269
+ caption: '逐段贴合',
270
+ columns: [
271
+ { key: 'time', label: '时段' },
272
+ { key: 'title', label: '标题' },
273
+ { key: 'fit', label: '贴合' },
274
+ { key: 'hint', label: '说明' },
275
+ ],
276
+ rows: fitRows,
277
+ emptyText: '这一版一段也没给',
278
+ }) },
279
+ { navText: drift === 0 ? '' : '偏离警示', html: drift === 0 ? '' : renderFeedbackBlock({
280
+ title: '偏离警示',
281
+ toast: {
282
+ icon: 'warn',
283
+ msg: '有 ' + String(drift) + ' 段与近 ' + String(HISTORY_DAYS) + ' 天的作息习惯不同,可能是刻意安排,也可能是排错了。',
284
+ lines: fits.filter((f) => f.fit === 'drift').map((f) => span(f.event.time_start, f.event.time_end) + ' ' + f.event.title),
285
+ },
286
+ staticNotice: true,
287
+ }) },
288
+ { navText: '冲突与警告', html: renderFeedbackBlock({
289
+ title: '冲突与警告',
290
+ toast: overlaps.length === 0
291
+ ? { icon: 'ok', msg: '这一版候选与这一天原来的排布没有时段重叠。' }
292
+ : {
293
+ icon: 'danger',
294
+ msg: '有 ' + String(overlaps.length) + ' 处候选与落盘前原有的事件时段重叠。',
295
+ lines: overlaps.map((o) => o.candidate + ' ↔ ' + o.locked + ',重叠 ' + o.overlap + '(' + String(o.minutes) + ' 分钟)'),
296
+ },
297
+ staticNotice: true,
298
+ }) },
299
+ { html: renderPreBlock({
300
+ label: '要再改这一版就说这一句',
301
+ command: '把 ' + date + ' 的计划再调整一版',
302
+ actionId: 'ilife-sch-plan-copy-next',
303
+ copyLabel: '复制这句话',
304
+ }) },
305
+ { navText: '复制与留档', html: scheduleCopyArea({
306
+ title: '复制与留档',
307
+ dataActionId: 'ilife-sch-plan-copy-data',
308
+ logActionId: 'ilife-sch-plan-copy-log',
309
+ ...planCopyArea({
310
+ message: '制定次日计划结果:日期 ' + date + ',候选 ' + String(candidates.length) + ' 段共 ' + fmtDur(covered)
311
+ + ',历史窗口近 ' + String(HISTORY_DAYS) + ' 天共 ' + String(records.length) + ' 条作息记录,贴合率 '
312
+ + (rate === null ? '—' : String(rate) + '%') + '(贴合 ' + String(match) + ' 段,偏离 ' + String(drift)
313
+ + ' 段,无参考 ' + String(none) + ' 段),远端侧 ' + REMOTE_LABEL[remote],
314
+ command: 'schedule-cmd-read schedule.plan.write --params {"op":"upsert","date":"' + date + '"}',
315
+ source: '日程计划表(' + date + ' 落库 ' + String(written.length) + ' 件)',
316
+ }),
317
+ }) },
318
+ ]);
319
+ return assembleDocPage({ head, content: toc + body, extraCss: planPartsCss() });
320
+ }
321
+ //# sourceMappingURL=discussDocs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discussDocs.js","sourceRoot":"","sources":["../../src/plan/discussDocs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EACL,iBAAiB,EAAE,mBAAmB,EAAE,eAAe,EAAE,gBAAgB,EACzE,mBAAmB,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,GAEnE,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EACL,gBAAgB,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,GAC5C,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAiB,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAoB,MAAM,cAAc,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,MAAM,OAAO,GAAG,YAAY,CAAC;AAC7B,kDAAkD;AAClD,MAAM,QAAQ,GAAsB,CAAC,GAAG,gBAAgB,CAAC,CAAC;AAC1D,wDAAwD;AACxD,MAAM,YAAY,GAAG,CAAC,CAAC;AACvB,MAAM,aAAa,GAAG,EAAE,CAAC;AAEzB,MAAM,IAAI,GAAG,CAAC,KAAa,EAAE,GAAW,EAAU,EAAE,CAAC,KAAK,GAAG,KAAK,GAAG,GAAG,CAAC;AACzE,MAAM,UAAU,GAAG,CAAC,CAA4B,EAAU,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACrH,oEAAoE;AACpE,MAAM,SAAS,GAAG,eAAe,CAAC;AAElC,SAAS,OAAO,CAAC,MAAuG;IACtH,OAAO,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACpC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC;QAC9B,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC1B,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC;KAC5B,CAAC,CAAC,CAAC,CAAC;AACP,CAAC;AAED,qCAAqC;AACrC,SAAS,OAAO,CAAC,KAAa,EAAE,GAAW;IACzC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;IAC/C,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,aAAa,EAAE,CAAC,IAAI,CAAC;QAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpE,OAAO,GAAG,CAAC;AACb,CAAC;AAED,+DAA+D;AAC/D,SAAS,QAAQ,CAAC,OAAkC;IAClD,MAAM,GAAG,GAAG,IAAI,GAAG,EAA+B,CAAC;IACnD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACpC,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;YAClD,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,EAAkB,CAAC;YACvD,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC9C,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,8CAA8C;AAC9C,SAAS,UAAU,CACjB,MAAwD,EAAE,KAAwB;IAElF,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE;YAAE,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3F,CAAC;IACD,IAAI,IAAI,GAA+C,IAAI,CAAC;IAC5D,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5F,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK;YAAE,IAAI,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IACtE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAID,0DAA0D;AAC1D,SAAS,KAAK,CACZ,QAAmC,EAAE,MAAwD,EAC7F,KAAa,EAAE,GAAW;IAE1B,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;IACpD,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,0BAA0B,EAAE,CAAC;IAC3E,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;QACnE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,GAAG,GAAG,CAAC,QAAQ,GAAG,IAAI,EAAE,CAAC;IAC3E,CAAC;IACD,IAAI,IAAI,KAAK,GAAG,CAAC,QAAQ,EAAE,CAAC;QAC1B,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,iBAAiB,GAAG,GAAG,CAAC,QAAQ,GAAG,IAAI,EAAE,CAAC;IACzE,CAAC;IACD,OAAO;QACL,GAAG,EAAE,OAAO;QACZ,IAAI,EAAE,kBAAkB,GAAG,GAAG,CAAC,QAAQ,GAAG,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,aAAa,GAAG,IAAI,GAAG,IAAI;KACjG,CAAC;AACJ,CAAC;AAUD,SAAS,UAAU,CACjB,UAAqC,EAAE,MAA4B;IAEnE,MAAM,GAAG,GAAc,EAAE,CAAC;IAC1B,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;YACtE,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;YAClE,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;gBACZ,GAAG,CAAC,IAAI,CAAC;oBACP,SAAS,EAAE,CAAC,CAAC,KAAK,GAAG,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,GAAG;oBAC/D,MAAM,EAAE,CAAC,CAAC,KAAK,GAAG,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,GAAG;oBAC5D,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;oBACvC,OAAO,EAAE,EAAE,GAAG,EAAE;iBACjB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,0EAA0E;AAE1E,kDAAkD;AAClD,MAAM,UAAU,WAAW,CAAC,MAAkB,EAAE,IAAY,EAAE,UAAqC;IACjG,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;IAChC,MAAM,QAAQ,GAAG,UAAU,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1G,MAAM,IAAI,GAAmB,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAClD,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC;QACpC,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC;QAChC,KAAK,EAAE,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK;KACnF,CAAC,CAAC,CAAC;IAAE,MAAM,UAAU,GAAmB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1D,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC;QACpC,IAAI,EAAE,CAAC,CAAC,KAAK;QACb,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC;KAC9B,CAAC,CAAC,CAAC;IACJ,MAAM,IAAI,GAAa;QACrB,QAAQ,EAAE,aAAa;QACvB,OAAO,EAAE,OAAO;QAChB,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,QAAQ,GAAG,IAAI,GAAG,eAAe;KAChF,CAAC;IACF,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,YAAY,CAAC;QACjC,EAAE,IAAI,EAAE,mBAAmB,CAAC,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,IAAI,GAAG,aAAa;kBACzF,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,QAAQ;kBACvE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,YAAY,CAAC,EAAE;QAC7C,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,CAAC;gBACzC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE;gBAC9E;oBACE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC;oBACxC,MAAM,EAAE,OAAO,IAAI,aAAa,GAAG,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY;oBAClE,GAAG,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,CAAC,aAAa,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE;iBAChF;gBACD,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,EAAE;gBACjH,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE;aAC3E,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE;QAC3B,0BAA0B;QAC1B,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,cAAc,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE;QACzH,EAAE,IAAI,EAAE,eAAe,CAAC;gBACtB,OAAO,EAAE,MAAM;gBACf,OAAO,EAAE;oBACP,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE;oBAC5B,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE;oBAC7B,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE;oBAChC,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE;iBAC9B;gBACD,IAAI;gBACJ,SAAS,EAAE,UAAU;aACtB,CAAC,EAAE;QACJ,EAAE,IAAI,EAAE,iBAAiB,CAAC,2CAA2C,CAAC,EAAE;QACxE,EAAE,IAAI,EAAE,gBAAgB,CAAC;gBACvB,KAAK,EAAE,OAAO;gBACd,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;oBAC/B,CAAC,CAAC,sDAAsD;oBACxD,CAAC,CAAC,uDAAuD,CAAC;sBACxD,cAAc,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC;aACtE,CAAC,EAAE;QACJ,EAAE,IAAI,EAAE,gBAAgB,CAAC;gBACvB,KAAK,EAAE,MAAM;gBACb,WAAW,EAAE,cAAc,CAAC;oBAC1B,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;oBAC3H,SAAS,EAAE,yBAAyB;iBACrC,CAAC;aACH,CAAC,EAAE;QACJ,EAAE,IAAI,EAAE,cAAc,CAAC;gBACrB,KAAK,EAAE,YAAY;gBACnB,OAAO,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS;gBACpC,QAAQ,EAAE,0BAA0B;gBACpC,SAAS,EAAE,OAAO;aACnB,CAAC,EAAE;QACJ,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,CAAC;gBACzC,KAAK,EAAE,OAAO;gBACd,YAAY,EAAE,0BAA0B;gBACxC,WAAW,EAAE,yBAAyB;gBACtC,GAAG,YAAY,CAAC;oBACd,OAAO,EAAE,YAAY,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC;0BACxF,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI;oBAC9C,OAAO,EAAE,yEAAyE,GAAG,IAAI,GAAG,IAAI;oBAChG,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,KAAK;iBACxD,CAAC;aACH,CAAC,EAAE;KACL,CAAC,CAAC;IACH,OAAO,eAAe,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,GAAG,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC;AAClF,CAAC;AAED,wEAAwE;AAExE,4DAA4D;AAC5D,MAAM,UAAU,UAAU,CACxB,MAAkB,EAAE,IAAY,EAAE,UAAqC,EACvE,MAA4B,EAAE,MAAmB;IAEjD,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,gBAAgB,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5F,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3G,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;IAC3D,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;IAC3D,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;IACzD,MAAM,QAAQ,GAAG,KAAK,GAAG,KAAK,CAAC;IAC/B,MAAM,IAAI,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAChF,MAAM,QAAQ,GAAG,UAAU,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1G;qDACiD;IACjD,MAAM,OAAO,GAAmB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC/C,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC;QAChD,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK;QACpB,GAAG,EAAE,CAAC,CAAC,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;QAClE,IAAI,EAAE,CAAC,CAAC,IAAI;KACb,CAAC,CAAC,CAAC;IACJ,MAAM,IAAI,GAAa;QACrB,QAAQ,EAAE,eAAe;QACzB,OAAO,EAAE,OAAO;QAChB,KAAK,EAAE,UAAU;QACjB,QAAQ,EAAE,QAAQ,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,KAAK;cAClE,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK;KAC9D,CAAC;IACF,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,YAAY,CAAC;QACjC,EAAE,IAAI,EAAE,mBAAmB,CAAC,UAAU,GAAG,IAAI,GAAG,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,KAAK;kBACrF,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,GAAG,GAAG,CAAC,IAAI,KAAK,IAAI;gBAC7C,CAAC,CAAC,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,oBAAoB;gBACpD,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE;QAC5E,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,aAAa,CAAC;gBACvC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;gBACxE;oBACE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG;oBAC/D,MAAM,EAAE,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI;oBAC3F,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;iBAC7D;gBACD,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,UAAU,EAAE;gBAC7F,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,EAAE;aAC/G,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE;QACzB,8BAA8B;QAC9B,EAAE,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE;QAC9H,EAAE,IAAI,EAAE,eAAe,CAAC;gBACtB,KAAK,EAAE;oBACL,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,YAAY,CAAC,GAAG,IAAI,EAAE;oBACrD,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE;oBAC5D,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE;oBAC7C,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE;iBACzD;aACF,CAAC,EAAE;QACJ,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,mBAAmB,CAAC;gBAC7C,KAAK,EAAE,QAAQ;gBACf,KAAK,EAAE;oBACL,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;oBAC5D,GAAG,EAAE,IAAI,KAAK,IAAI;wBAChB,CAAC,CAAC,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,+BAA+B;wBAC/D,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,kBAAkB,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ;8BAC1E,MAAM,CAAC,KAAK,CAAC,GAAG,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK;8BAChD,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC;iBAC5C;gBACD,YAAY,EAAE,IAAI;aACnB,CAAC,EAAE;QACJ,EAAE,IAAI,EAAE,eAAe,CAAC;gBACtB,OAAO,EAAE,MAAM;gBACf,OAAO,EAAE;oBACP,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE;oBAC5B,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE;oBAC7B,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE;oBAC3B,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE;iBAC7B;gBACD,IAAI,EAAE,OAAO;gBACb,SAAS,EAAE,UAAU;aACtB,CAAC,EAAE;QACJ,EAAE,OAAO,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,mBAAmB,CAAC;gBACjF,KAAK,EAAE,MAAM;gBACb,KAAK,EAAE;oBACL,IAAI,EAAE,MAAM;oBACZ,GAAG,EAAE,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,4BAA4B;oBACzF,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;iBAC1H;gBACD,YAAY,EAAE,IAAI;aACnB,CAAC,EAAE;QACJ,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,mBAAmB,CAAC;gBAC5C,KAAK,EAAE,OAAO;gBACd,KAAK,EAAE,QAAQ,CAAC,MAAM,KAAK,CAAC;oBAC1B,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,uBAAuB,EAAE;oBAC9C,CAAC,CAAC;wBACA,IAAI,EAAE,QAAQ;wBACd,GAAG,EAAE,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,oBAAoB;wBAC1D,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,KAAK,GAAG,CAAC,CAAC,MAAM,GAAG,MAAM,GAAG,CAAC,CAAC,OAAO,GAAG,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC;qBACnH;gBACH,YAAY,EAAE,IAAI;aACnB,CAAC,EAAE;QACJ,EAAE,IAAI,EAAE,cAAc,CAAC;gBACrB,KAAK,EAAE,aAAa;gBACpB,OAAO,EAAE,IAAI,GAAG,IAAI,GAAG,WAAW;gBAClC,QAAQ,EAAE,0BAA0B;gBACpC,SAAS,EAAE,OAAO;aACnB,CAAC,EAAE;QACJ,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,CAAC;gBACzC,KAAK,EAAE,OAAO;gBACd,YAAY,EAAE,0BAA0B;gBACxC,WAAW,EAAE,yBAAyB;gBACtC,GAAG,YAAY,CAAC;oBACd,OAAO,EAAE,cAAc,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC;0BAC1F,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,aAAa;0BAClF,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC;0BAC9F,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,UAAU,GAAG,YAAY,CAAC,MAAM,CAAC;oBAChE,OAAO,EAAE,wEAAwE,GAAG,IAAI,GAAG,IAAI;oBAC/F,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,KAAK;iBAClE,CAAC;aACH,CAAC,EAAE;KACL,CAAC,CAAC;IACH,OAAO,eAAe,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,GAAG,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC;AAClF,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { ScheduleDb } from '../fetch/db.js';
2
+ import { type RemoteState } from './receipt.js';
3
+ import { type TierReport } from './probe.js';
4
+ /** 「飞书探测」整页:三档结论 + 下一步 + 本地这一天的账(**这一趟没写任何对象**)。
5
+ *
6
+ * #896 · 「授权登录过」与进度条读 `report.authenticated`(布尔位),不读 `report.openId` 原值:
7
+ * 回执不带 openId 原串(隐私),`handlers.ts` 重建时 `openId` 置 null 仅占形状,显示以布尔位为准。
8
+ */
9
+ export declare function probePage(handle: ScheduleDb, date: string, report: TierReport): string;
10
+ export interface SyncPayload {
11
+ readonly message: string;
12
+ readonly remote: RemoteState;
13
+ readonly counts: Record<string, number>;
14
+ readonly notes: readonly string[];
15
+ readonly errors: readonly string[];
16
+ }
17
+ /** 「日程管家同步」整页:这一趟做了什么分格报出来,没成的逐条点名。 */
18
+ export declare function syncPage(handle: ScheduleDb, date: string, payload: SyncPayload): string;
19
+ //# sourceMappingURL=feishuDocs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feishuDocs.d.ts","sourceRoot":"","sources":["../../src/plan/feishuDocs.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAa,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAQ5D,OAAO,EAA8B,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAC5E,OAAO,EAAkC,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAiD7E;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAqFtF;AAID,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAuBD,uCAAuC;AACvC,wBAAgB,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,MAAM,CAiFvF"}
@@ -0,0 +1,257 @@
1
+ /** #788 · 飞书两条唤醒词的**两张页**:能力探测(只读)与日程管家同步(回执)。
2
+ *
3
+ * 为什么两张分开(照老侧的分工,也与 #787 的接缝对上):
4
+ * · 「飞书探测」(`op=sync` + `dryRun`)——**先看**:三档能力门探一遍,一门写都不发;
5
+ * · 「日程管家同步」(`op=sync`)——**执行**:回填标识、补齐远端、清孤儿,然后把这一趟做了什么
6
+ * 分格报出来。老侧那种「写之前先问一句」的交互,落在这两张页的分工上:要问就先跑探测,
7
+ * 要写就说同步(#787 的证据件把这一问划给了本票)。
8
+ *
9
+ * **页上文案纪律**:不出现命令键、库列名、参数名、票号与远端对象标识(那一串是内部标识,只进回执,
10
+ * 不进页);并列关系用读数卡与表格表达,不拿 `·`/`;`/`|`/`、`/`~` 顶替。
11
+ */
12
+ import { renderCaliberLine, renderConclusionBar, renderDataTable, renderFeedbackBlock, renderKpiGrid, renderListRows, renderPreBlock, } from 'base-paint/blocks';
13
+ import { renderFactStrip } from 'base-paint';
14
+ import { listPlanEvents, shortLarkVersion } from '../fetch/index.js';
15
+ import { fmtDurShort, l1Of } from '../policy/index.js';
16
+ import { scheduleCopyArea } from '../render/copyArea.js';
17
+ import { assembleDocPage } from '../shared/docPage.js';
18
+ import { pageSections } from '../shared/pageNav.js';
19
+ import { minutesOfDayEnd } from './planDocs.js';
20
+ import { planPartsCss, renderSectionTitle } from './planParts.js';
21
+ import { REMOTE_LABEL, planCopyArea } from './receipt.js';
22
+ import { TIER_CN, TIER_NEXT, TIER_VALUE } from './probe.js';
23
+ const EYEBROW = '作息管家 日程与计划';
24
+ /** 外部来的原话(命令行 stderr、回执 message、错误串)上屏前过分隔符门(#516):
25
+ * 页自己的文案本来就照纪律写,这几处是**别的进程吐出来的字**——真让它把 `/`/`、` 带上屏,
26
+ * 就是设计债进门。做法是把那几种并列符号换成行文逗号,只动上屏这一份,回执载荷一字不改。 */
27
+ function plain(text) {
28
+ return text
29
+ .replace(/[·;;||//、+~~]/g, ',')
30
+ .replace(/,{2,}/g, ',')
31
+ .replace(/,([))】」])/g, '$1');
32
+ }
33
+ /** 事实条「命令行版本」只印数字(口径唯一定义地是 `fetch` 门的 `shortLarkVersion`)。
34
+ * `null`(没装)印「没装」,`unknown`/无数字印「读不到」。 */
35
+ /** 同步回执的分格(`runSync` 的 `counts`)在页上的名字与读法,一处定义。 */
36
+ const COUNT_LABELS = [
37
+ { key: 'backfilled', label: '回填标识', hint: '远端本来就有但本地缺标识的,这一趟认回来的' },
38
+ { key: 'created', label: '远端新建', hint: '本地有但远端没有,这一趟补上去的' },
39
+ { key: 'updated', label: '远端改动', hint: '两边标题或备注不一样,按本地那一条改过去的' },
40
+ { key: 'timeSynced', label: '时段回写', hint: '你在飞书里改过时段,尊重你,把它写回本地的' },
41
+ { key: 'deleted', label: '远端清理', hint: '孤儿与同槽冗余,这一趟清掉的' },
42
+ { key: 'pastSkipped', label: '过去日期跳过', hint: '昨天以前的计划不补建,免得死灰复燃' },
43
+ { key: 'duplicates', label: '同槽冗余', hint: '同一时段里历史留下的重复对象' },
44
+ { key: 'failed', label: '失败', hint: '这一趟没成的那几件' },
45
+ ];
46
+ function eventRows(events) {
47
+ return events.map((p) => ({
48
+ time: p.time_start + ' 至 ' + p.time_end,
49
+ title: p.title,
50
+ category: l1Of(p.category ?? ''),
51
+ remote: p.feishu_event_id === null ? '没同步过' : '已同步飞书',
52
+ }));
53
+ }
54
+ function localCounts(handle, date) {
55
+ const events = listPlanEvents(handle, date);
56
+ return {
57
+ total: events.length,
58
+ synced: events.filter((p) => p.feishu_event_id !== null).length,
59
+ minutes: events.reduce((sum, p) => sum + Math.max(0, minutesOfDayEnd(p.time_end) - minutesOfDayEnd(p.time_start)), 0),
60
+ };
61
+ }
62
+ /* ─────────────────────── ① 飞书探测(只读,三档) ─────────────────────── */
63
+ /** 「飞书探测」整页:三档结论 + 下一步 + 本地这一天的账(**这一趟没写任何对象**)。
64
+ *
65
+ * #896 · 「授权登录过」与进度条读 `report.authenticated`(布尔位),不读 `report.openId` 原值:
66
+ * 回执不带 openId 原串(隐私),`handlers.ts` 重建时 `openId` 置 null 仅占形状,显示以布尔位为准。
67
+ */
68
+ export function probePage(handle, date, report) {
69
+ const local = localCounts(handle, date);
70
+ const kpis = [
71
+ { label: '能力档位', value: TIER_VALUE[report.tier], detail: plain(report.why) },
72
+ { label: '这一天本地计划', value: String(local.total), unit: '条', detail: '共 ' + fmtDurShort(local.minutes) },
73
+ { label: '带远端标识的', value: String(local.synced), unit: '条', detail: '同步过的那些' },
74
+ {
75
+ label: '这一趟写了几笔',
76
+ value: '0',
77
+ detail: '只读探测,远端一行没动',
78
+ // 三档全通时给满,其余档按三门过了几门给(读数同源:探测结果那一份)。
79
+ // #896 · 读 authenticated 布尔位,不读 openId 原值(回执不带原串,见 probe.ts 件头)。
80
+ bar: { pct: Math.round(([report.cliPath === null ? 0 : 1, report.authenticated ? 1 : 0, report.calendar ? 1 : 0]
81
+ .reduce((a, b) => a + b, 0) / 3) * 100) },
82
+ },
83
+ ];
84
+ const head = {
85
+ docTitle: '作息管家 飞书探测',
86
+ eyebrow: EYEBROW,
87
+ title: '飞书探测',
88
+ subtitle: '看本机的飞书能力到哪一档,以及 ' + date + ' 这一天本地排了什么。这一步只读,不写任何对象。',
89
+ };
90
+ const steps = [
91
+ { step: '装有命令行', state: report.cliPath === null ? '没找到' : '在场', why: report.cliPath === null ? '家目录与查找路径里都没有' : '找到了可执行文件' },
92
+ { step: '授权登录过', state: report.authenticated ? '过了' : '没过', why: report.authenticated ? '查到了登录身份' : '终端里查不到登录身份' },
93
+ { step: '日历拉得动', state: report.calendar ? '拉得动' : '拉不动', why: report.calendar ? '今日议程这一问有回应' : '今日议程那一问没成' },
94
+ ];
95
+ const { toc, body } = pageSections([
96
+ { navText: '这一趟探测的结论', html: renderKpiGrid(kpis, { title: '这一趟探测的结论' }) },
97
+ { html: renderConclusionBar('飞书能力:' + TIER_CN[report.tier] + '。' + plain(report.why)
98
+ + '。要动远端就说「日程管家同步」,要留个只读的快照就是这一页。') },
99
+ { navText: '三道门逐道看', html: renderSectionTitle('三道门逐道看')
100
+ + renderDataTable({
101
+ columns: [
102
+ { key: 'step', label: '探什么' },
103
+ { key: 'state', label: '结果' },
104
+ { key: 'why', label: '怎么看出来的' },
105
+ ],
106
+ rows: steps,
107
+ }) },
108
+ { html: renderFactStrip({
109
+ items: [
110
+ { label: '命令行版本', value: report.version === null ? '没装' : shortLarkVersion(report.version) },
111
+ { label: '探测日期', value: date },
112
+ { label: '本地这一天的计划', value: String(local.total) + ' 条' },
113
+ { label: '远端这一趟', value: '没碰' },
114
+ ],
115
+ }) },
116
+ { navText: '下一步怎么走', html: renderSectionTitle('下一步怎么走')
117
+ + renderListRows({
118
+ items: TIER_NEXT[report.tier].map((text) => ({ main: text })),
119
+ emptyText: '没有下一步',
120
+ }) },
121
+ { navText: '本地这一天的账', html: renderSectionTitle('本地这一天的账')
122
+ + '<p class="sch-pl-note">这一份读的是本地库:这一天排了几条计划,其中几条已经带上远端标识。远端那一边这一趟没拉,故不报对方的条数。</p>'
123
+ + renderDataTable({
124
+ columns: [
125
+ { key: 'time', label: '时段' },
126
+ { key: 'title', label: '计划' },
127
+ { key: 'category', label: '分类' },
128
+ { key: 'remote', label: '远端标识' },
129
+ ],
130
+ rows: eventRows(listPlanEvents(handle, date)),
131
+ emptyText: '这一天本地没有计划',
132
+ })
133
+ + renderCaliberLine('探测只读,三道门都只问不写,本地库这一趟也没动过') },
134
+ { html: renderPreBlock({
135
+ label: '要真同步就说这一句',
136
+ command: '同步 ' + date + ' 的日程到飞书',
137
+ actionId: 'ilife-sch-probe-copy-next',
138
+ copyLabel: '复制这句话',
139
+ }) },
140
+ { navText: '复制与留档', html: scheduleCopyArea({
141
+ title: '复制与留档',
142
+ dataActionId: 'ilife-sch-probe-copy-data',
143
+ logActionId: 'ilife-sch-probe-copy-log',
144
+ ...planCopyArea({
145
+ message: '飞书探测:日期 ' + date + ',档位 ' + TIER_CN[report.tier] + ',原因 ' + report.why
146
+ + ',本地这一天计划 ' + String(local.total) + ' 条、带远端标识 ' + String(local.synced) + ' 条,这一趟写入 0 笔',
147
+ command: 'schedule-cmd-read schedule.plan.write --params {"op":"sync","dryRun":true,"date":"' + date + '"}',
148
+ source: '本机命令行探测与本地日程计划表',
149
+ }),
150
+ }) },
151
+ ]);
152
+ return assembleDocPage({ head, content: toc + body, extraCss: planPartsCss() });
153
+ }
154
+ /** 这一趟的结论(页自己的句子,不照抄回执那一串以并列符号分隔的计数):
155
+ * 远端在场就报对齐结果,远端没成/跳过也照实说,失败数单独点名。 */
156
+ function syncConclusion(payload) {
157
+ const c = payload.counts;
158
+ const moved = (c.backfilled ?? 0) + (c.created ?? 0) + (c.updated ?? 0) + (c.timeSynced ?? 0) + (c.deleted ?? 0);
159
+ const head = payload.remote === 'unavailable'
160
+ ? '这一趟远端没成,本地那边没有改动。'
161
+ : (moved === 0 ? '两边本来就是对得上,这一趟没有要动的东西。' : '这一趟一共动了 ' + String(moved) + ' 处。');
162
+ const parts = [
163
+ '回填标识 ' + String(c.backfilled ?? 0) + ' 笔',
164
+ '远端新建 ' + String(c.created ?? 0) + ' 笔',
165
+ '远端改动 ' + String(c.updated ?? 0) + ' 笔',
166
+ '时段回写 ' + String(c.timeSynced ?? 0) + ' 笔',
167
+ '远端清理 ' + String(c.deleted ?? 0) + ' 笔',
168
+ ].join(',');
169
+ const tail = (c.failed ?? 0) === 0
170
+ ? '失败 0 笔。'
171
+ : '失败 ' + String(c.failed ?? 0) + ' 笔,逐条在下面。';
172
+ return head + parts + '。' + tail;
173
+ }
174
+ /** 「日程管家同步」整页:这一趟做了什么分格报出来,没成的逐条点名。 */
175
+ export function syncPage(handle, date, payload) {
176
+ const events = listPlanEvents(handle, date);
177
+ const failed = payload.counts.failed ?? 0;
178
+ const kpis = COUNT_LABELS.map((c) => ({
179
+ label: c.label,
180
+ value: String(payload.counts[c.key] ?? 0),
181
+ unit: '笔',
182
+ detail: c.hint,
183
+ }));
184
+ const { toc, body } = pageSections([
185
+ { navText: '这一趟的账', html: renderKpiGrid(kpis, { title: '这一趟的账' }) },
186
+ { html: renderConclusionBar(syncConclusion(payload)) },
187
+ { html: renderFactStrip({
188
+ items: [
189
+ { label: '同步的日期', value: date },
190
+ { label: '远端侧', value: REMOTE_LABEL[payload.remote] },
191
+ { label: '本地这一天的计划', value: String(events.length) + ' 条' },
192
+ { label: '没成的', value: String(failed) + ' 笔' },
193
+ ],
194
+ }) },
195
+ { navText: '这一趟的结果', html: renderFeedbackBlock({
196
+ title: '这一趟的结果',
197
+ toast: failed === 0
198
+ ? { icon: payload.remote === 'unavailable' ? 'warn' : 'ok', msg: plain(payload.message) }
199
+ : { icon: 'danger', msg: plain(payload.message), lines: payload.errors.map(plain) },
200
+ staticNotice: true,
201
+ }) },
202
+ { navText: payload.notes.length === 0 ? '' : '顺带记下的', html: payload.notes.length === 0 ? '' : renderSectionTitle('顺带记下的')
203
+ + renderListRows({ items: payload.notes.map((text) => ({ main: plain(text) })), emptyText: '没有顺带记下的' }) },
204
+ { navText: payload.errors.length === 0 ? '' : '没成的逐条名单', html: payload.errors.length === 0 ? '' : renderSectionTitle('没成的逐条名单')
205
+ + renderListRows({ items: payload.errors.map((text) => ({ main: plain(text) })), emptyText: '没有没成的' }) },
206
+ { navText: '本地这一天的排布', html: renderSectionTitle('本地这一天的排布')
207
+ + renderCaliberLine('远端只动带归属标记的对象,你在飞书日历上自己加的那些一律不碰')
208
+ + renderDataTable({
209
+ columns: [
210
+ { key: 'time', label: '时段' },
211
+ { key: 'title', label: '计划' },
212
+ { key: 'category', label: '分类' },
213
+ { key: 'remote', label: '远端标识' },
214
+ ],
215
+ rows: eventRows(events),
216
+ emptyText: '这一天本地没有计划',
217
+ }) },
218
+ { navText: '下一步', html: renderSectionTitle('下一步')
219
+ + renderListRows({
220
+ items: [
221
+ { main: '要看另一天的差异,把日期一并说清楚,比如「同步 2026-09-22 的日程」。' },
222
+ { main: '只想先看一眼而不动远端,就说「飞书探测」,那一页只读。' },
223
+ ],
224
+ emptyText: '没有下一步',
225
+ }) },
226
+ { html: renderPreBlock({
227
+ label: '再同步一天就说这一句',
228
+ command: '同步 ' + date + ' 的日程到飞书',
229
+ actionId: 'ilife-sch-sync-copy-next',
230
+ copyLabel: '复制这句话',
231
+ }) },
232
+ { navText: '复制与留档', html: scheduleCopyArea({
233
+ title: '复制与留档',
234
+ dataActionId: 'ilife-sch-sync-copy-data',
235
+ logActionId: 'ilife-sch-sync-copy-log',
236
+ ...planCopyArea({
237
+ message: '日程管家同步:日期 ' + date + ',' + payload.message + ','
238
+ + COUNT_LABELS.map((c) => c.label + ' ' + String(payload.counts[c.key] ?? 0) + ' 笔').join(',')
239
+ + (payload.errors.length === 0 ? '' : ',没成的 ' + payload.errors.join(',')),
240
+ command: 'schedule-cmd-read schedule.plan.write --params {"op":"sync","date":"' + date + '"}',
241
+ source: '本地日程计划表与飞书日历',
242
+ ok: payload.errors.length === 0,
243
+ }),
244
+ }) },
245
+ ]);
246
+ return assembleDocPage({
247
+ head: {
248
+ docTitle: '作息管家 日程管家同步',
249
+ eyebrow: EYEBROW,
250
+ title: '日程管家同步',
251
+ subtitle: date + ' 这一天本地与飞书对了一遍,这一页报的是这一趟真做了什么。',
252
+ },
253
+ content: toc + body,
254
+ extraCss: planPartsCss(),
255
+ });
256
+ }
257
+ //# sourceMappingURL=feishuDocs.js.map