@my-life-buddies/cli 0.13.3 → 0.15.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 (193) hide show
  1. package/README.md +74 -22
  2. package/dist/bin/buddy.js +29 -10
  3. package/dist/bin/buddy.js.map +2 -2
  4. package/dist/bin/core.js +1480 -611
  5. package/dist/bin/core.js.map +4 -4
  6. package/dist/bin/preview.js +324 -27
  7. package/dist/bin/preview.js.map +4 -4
  8. package/dist/web/app.css +65 -6
  9. package/dist/web/app.js +103 -39
  10. package/dist/web/diagnostics.js +184 -0
  11. package/dist/web/index.html +34 -34
  12. package/dist/web/widgets.css +59 -28
  13. package/dist/web/widgets.js +230 -76
  14. package/package.json +3 -3
  15. package/resources/README.md +6 -50
  16. package/resources/agent-template/package-lock.json +4 -4
  17. package/resources/agent-template/package.json +2 -2
  18. package/resources/buddy-creator/INSTALL.md +0 -25
  19. package/resources/buddy-creator/PATCHES.md +0 -53
  20. package/resources/buddy-creator/SKILL.md +0 -76
  21. package/resources/buddy-creator/THIRD_PARTY_NOTICES.md +0 -154
  22. package/resources/buddy-creator/agents/openai.yaml +0 -4
  23. package/resources/buddy-creator/assets/licenses/bail.txt +0 -26
  24. package/resources/buddy-creator/assets/licenses/ccount.txt +0 -26
  25. package/resources/buddy-creator/assets/licenses/character-entities-html4.txt +0 -26
  26. package/resources/buddy-creator/assets/licenses/character-entities-legacy.txt +0 -26
  27. package/resources/buddy-creator/assets/licenses/character-entities.txt +0 -26
  28. package/resources/buddy-creator/assets/licenses/character-reference-invalid.txt +0 -26
  29. package/resources/buddy-creator/assets/licenses/comma-separated-tokens.txt +0 -26
  30. package/resources/buddy-creator/assets/licenses/debug.txt +0 -24
  31. package/resources/buddy-creator/assets/licenses/decode-named-character-reference.txt +0 -26
  32. package/resources/buddy-creator/assets/licenses/dequal.txt +0 -25
  33. package/resources/buddy-creator/assets/licenses/devlop.txt +0 -26
  34. package/resources/buddy-creator/assets/licenses/escape-string-regexp.txt +0 -13
  35. package/resources/buddy-creator/assets/licenses/estree-util-is-identifier-name.txt +0 -26
  36. package/resources/buddy-creator/assets/licenses/extend.txt +0 -27
  37. package/resources/buddy-creator/assets/licenses/hast-util-to-jsx-runtime.txt +0 -26
  38. package/resources/buddy-creator/assets/licenses/hast-util-whitespace.txt +0 -26
  39. package/resources/buddy-creator/assets/licenses/html-url-attributes.txt +0 -25
  40. package/resources/buddy-creator/assets/licenses/inline-style-parser.txt +0 -13
  41. package/resources/buddy-creator/assets/licenses/is-alphabetical.txt +0 -26
  42. package/resources/buddy-creator/assets/licenses/is-alphanumerical.txt +0 -26
  43. package/resources/buddy-creator/assets/licenses/is-decimal.txt +0 -26
  44. package/resources/buddy-creator/assets/licenses/is-hexadecimal.txt +0 -26
  45. package/resources/buddy-creator/assets/licenses/is-plain-obj.txt +0 -13
  46. package/resources/buddy-creator/assets/licenses/longest-streak.txt +0 -26
  47. package/resources/buddy-creator/assets/licenses/markdown-table.txt +0 -26
  48. package/resources/buddy-creator/assets/licenses/mdast-util-find-and-replace.txt +0 -26
  49. package/resources/buddy-creator/assets/licenses/mdast-util-from-markdown.txt +0 -26
  50. package/resources/buddy-creator/assets/licenses/mdast-util-gfm-autolink-literal.txt +0 -26
  51. package/resources/buddy-creator/assets/licenses/mdast-util-gfm-footnote.txt +0 -26
  52. package/resources/buddy-creator/assets/licenses/mdast-util-gfm-strikethrough.txt +0 -26
  53. package/resources/buddy-creator/assets/licenses/mdast-util-gfm-table.txt +0 -26
  54. package/resources/buddy-creator/assets/licenses/mdast-util-gfm-task-list-item.txt +0 -26
  55. package/resources/buddy-creator/assets/licenses/mdast-util-gfm.txt +0 -26
  56. package/resources/buddy-creator/assets/licenses/mdast-util-mdx-expression.txt +0 -26
  57. package/resources/buddy-creator/assets/licenses/mdast-util-mdx-jsx.txt +0 -26
  58. package/resources/buddy-creator/assets/licenses/mdast-util-mdxjs-esm.txt +0 -26
  59. package/resources/buddy-creator/assets/licenses/mdast-util-phrasing.txt +0 -27
  60. package/resources/buddy-creator/assets/licenses/mdast-util-to-hast.txt +0 -26
  61. package/resources/buddy-creator/assets/licenses/mdast-util-to-markdown.txt +0 -26
  62. package/resources/buddy-creator/assets/licenses/mdast-util-to-string.txt +0 -26
  63. package/resources/buddy-creator/assets/licenses/micromark-core-commonmark.txt +0 -26
  64. package/resources/buddy-creator/assets/licenses/micromark-extension-gfm-autolink-literal.txt +0 -26
  65. package/resources/buddy-creator/assets/licenses/micromark-extension-gfm-footnote.txt +0 -26
  66. package/resources/buddy-creator/assets/licenses/micromark-extension-gfm-strikethrough.txt +0 -26
  67. package/resources/buddy-creator/assets/licenses/micromark-extension-gfm-table.txt +0 -26
  68. package/resources/buddy-creator/assets/licenses/micromark-extension-gfm-tagfilter.txt +0 -26
  69. package/resources/buddy-creator/assets/licenses/micromark-extension-gfm-task-list-item.txt +0 -26
  70. package/resources/buddy-creator/assets/licenses/micromark-extension-gfm.txt +0 -26
  71. package/resources/buddy-creator/assets/licenses/micromark-factory-destination.txt +0 -26
  72. package/resources/buddy-creator/assets/licenses/micromark-factory-label.txt +0 -26
  73. package/resources/buddy-creator/assets/licenses/micromark-factory-space.txt +0 -26
  74. package/resources/buddy-creator/assets/licenses/micromark-factory-title.txt +0 -26
  75. package/resources/buddy-creator/assets/licenses/micromark-factory-whitespace.txt +0 -26
  76. package/resources/buddy-creator/assets/licenses/micromark-util-character.txt +0 -26
  77. package/resources/buddy-creator/assets/licenses/micromark-util-chunked.txt +0 -26
  78. package/resources/buddy-creator/assets/licenses/micromark-util-classify-character.txt +0 -26
  79. package/resources/buddy-creator/assets/licenses/micromark-util-combine-extensions.txt +0 -26
  80. package/resources/buddy-creator/assets/licenses/micromark-util-decode-numeric-character-reference.txt +0 -26
  81. package/resources/buddy-creator/assets/licenses/micromark-util-decode-string.txt +0 -26
  82. package/resources/buddy-creator/assets/licenses/micromark-util-encode.txt +0 -26
  83. package/resources/buddy-creator/assets/licenses/micromark-util-html-tag-name.txt +0 -26
  84. package/resources/buddy-creator/assets/licenses/micromark-util-normalize-identifier.txt +0 -26
  85. package/resources/buddy-creator/assets/licenses/micromark-util-resolve-all.txt +0 -26
  86. package/resources/buddy-creator/assets/licenses/micromark-util-sanitize-uri.txt +0 -26
  87. package/resources/buddy-creator/assets/licenses/micromark-util-subtokenize.txt +0 -26
  88. package/resources/buddy-creator/assets/licenses/micromark-util-symbol.txt +0 -26
  89. package/resources/buddy-creator/assets/licenses/micromark-util-types.txt +0 -26
  90. package/resources/buddy-creator/assets/licenses/micromark.txt +0 -26
  91. package/resources/buddy-creator/assets/licenses/ms.txt +0 -25
  92. package/resources/buddy-creator/assets/licenses/parse-entities.txt +0 -26
  93. package/resources/buddy-creator/assets/licenses/phosphor-icons--react.txt +0 -25
  94. package/resources/buddy-creator/assets/licenses/property-information.txt +0 -26
  95. package/resources/buddy-creator/assets/licenses/react-dom.txt +0 -25
  96. package/resources/buddy-creator/assets/licenses/react-markdown.txt +0 -25
  97. package/resources/buddy-creator/assets/licenses/react.txt +0 -25
  98. package/resources/buddy-creator/assets/licenses/remark-gfm.txt +0 -26
  99. package/resources/buddy-creator/assets/licenses/remark-parse.txt +0 -25
  100. package/resources/buddy-creator/assets/licenses/remark-rehype.txt +0 -26
  101. package/resources/buddy-creator/assets/licenses/remark-stringify.txt +0 -25
  102. package/resources/buddy-creator/assets/licenses/scheduler.txt +0 -25
  103. package/resources/buddy-creator/assets/licenses/space-separated-tokens.txt +0 -26
  104. package/resources/buddy-creator/assets/licenses/stringify-entities.txt +0 -26
  105. package/resources/buddy-creator/assets/licenses/style-to-js.txt +0 -26
  106. package/resources/buddy-creator/assets/licenses/style-to-object.txt +0 -26
  107. package/resources/buddy-creator/assets/licenses/trim-lines.txt +0 -26
  108. package/resources/buddy-creator/assets/licenses/trough.txt +0 -25
  109. package/resources/buddy-creator/assets/licenses/types--debug.txt +0 -25
  110. package/resources/buddy-creator/assets/licenses/types--estree-jsx.txt +0 -25
  111. package/resources/buddy-creator/assets/licenses/types--estree.txt +0 -25
  112. package/resources/buddy-creator/assets/licenses/types--hast.txt +0 -25
  113. package/resources/buddy-creator/assets/licenses/types--mdast.txt +0 -25
  114. package/resources/buddy-creator/assets/licenses/types--ms.txt +0 -25
  115. package/resources/buddy-creator/assets/licenses/types--unist.txt +0 -25
  116. package/resources/buddy-creator/assets/licenses/ungap--structured-clone.txt +0 -19
  117. package/resources/buddy-creator/assets/licenses/unified.txt +0 -25
  118. package/resources/buddy-creator/assets/licenses/unist-util-is.txt +0 -26
  119. package/resources/buddy-creator/assets/licenses/unist-util-position.txt +0 -26
  120. package/resources/buddy-creator/assets/licenses/unist-util-stringify-position.txt +0 -26
  121. package/resources/buddy-creator/assets/licenses/unist-util-visit-parents.txt +0 -26
  122. package/resources/buddy-creator/assets/licenses/unist-util-visit.txt +0 -26
  123. package/resources/buddy-creator/assets/licenses/vfile-message.txt +0 -26
  124. package/resources/buddy-creator/assets/licenses/vfile.txt +0 -25
  125. package/resources/buddy-creator/assets/licenses/zwitch.txt +0 -26
  126. package/resources/buddy-creator/assets/preview/assets/index-B5VmkoSx.js +0 -39
  127. package/resources/buddy-creator/assets/preview/assets/index-DWKP6b-3.css +0 -4
  128. package/resources/buddy-creator/assets/preview/brand-logo.png +0 -0
  129. package/resources/buddy-creator/assets/preview/brand.css +0 -100
  130. package/resources/buddy-creator/assets/preview/index.html +0 -7
  131. package/resources/buddy-creator/assets/preview/reader.css +0 -165
  132. package/resources/buddy-creator/assets/preview/reader.js +0 -135
  133. package/resources/buddy-creator/assets/preview/widgets.css +0 -1
  134. package/resources/buddy-creator/assets/preview/widgets.js +0 -168
  135. package/resources/buddy-creator/assets/widget-reference/conversation.png +0 -0
  136. package/resources/buddy-creator/assets/widget-reference/expanded.png +0 -0
  137. package/resources/buddy-creator/assets/widget-v2/app.js +0 -62
  138. package/resources/buddy-creator/assets/widget-v2/style.css +0 -1
  139. package/resources/buddy-creator/assets/widget-v3/app.js +0 -65
  140. package/resources/buddy-creator/assets/widget-v3/style.css +0 -103
  141. package/resources/buddy-creator/assets/widget-v4/app.js +0 -66
  142. package/resources/buddy-creator/assets/widget-v4/style.css +0 -187
  143. package/resources/buddy-creator/references/artifact-schema.md +0 -70
  144. package/resources/buddy-creator/references/catalog.json +0 -401
  145. package/resources/buddy-creator/references/definition.md +0 -24
  146. package/resources/buddy-creator/references/dialogue-examples.md +0 -133
  147. package/resources/buddy-creator/references/host-guide.md +0 -219
  148. package/resources/buddy-creator/references/interview.md +0 -235
  149. package/resources/buddy-creator/references/knowledge.md +0 -40
  150. package/resources/buddy-creator/references/methods.md +0 -77
  151. package/resources/buddy-creator/references/opening.md +0 -14
  152. package/resources/buddy-creator/references/preview-panel.md +0 -66
  153. package/resources/buddy-creator/references/recovery.md +0 -46
  154. package/resources/buddy-creator/references/service.md +0 -76
  155. package/resources/buddy-creator/references/widgets-protocol.md +0 -83
  156. package/resources/buddy-creator/references/widgets.md +0 -99
  157. package/resources/buddy-creator/scripts/buddy.py +0 -108
  158. package/resources/buddy-creator/scripts/buddy_client.py +0 -67
  159. package/resources/buddy-creator/scripts/buddy_core.py +0 -865
  160. package/resources/buddy-creator/scripts/completion.py +0 -296
  161. package/resources/buddy-creator/scripts/interview.py +0 -215
  162. package/resources/buddy-creator/scripts/preview.py +0 -617
  163. package/resources/buddy-creator/scripts/preview_panel.py +0 -80
  164. package/resources/buddy-creator/scripts/sources.py +0 -239
  165. package/resources/buddy-creator/scripts/widget_render.py +0 -66
  166. package/resources/buddy-creator/scripts/widget_render_v1.py +0 -176
  167. package/resources/buddy-creator/scripts/widget_render_v2.py +0 -98
  168. package/resources/buddy-creator/scripts/widget_render_v3.py +0 -86
  169. package/resources/buddy-creator/scripts/widget_render_v4.py +0 -121
  170. package/resources/buddy-creator/scripts/widgets.py +0 -227
  171. package/resources/buddy-creator/version.json +0 -8
  172. package/resources/buddy-creator.manifest.json +0 -791
  173. package/resources/dada-object-avatar/SKILL.md +0 -37
  174. package/resources/dada-object-avatar/agents/openai.yaml +0 -4
  175. package/resources/dada-object-avatar/assets/examples/cat.png +0 -0
  176. package/resources/dada-object-avatar/assets/examples/cooking.png +0 -0
  177. package/resources/dada-object-avatar/assets/examples/divination.png +0 -0
  178. package/resources/dada-object-avatar/assets/examples/doctor.png +0 -0
  179. package/resources/dada-object-avatar/assets/examples/relationship.png +0 -0
  180. package/resources/dada-object-avatar/assets/examples/work.png +0 -0
  181. package/resources/dada-object-avatar/assets/preview.png +0 -0
  182. package/resources/dada-object-avatar/references/evaluation-rubric.md +0 -22
  183. package/resources/dada-object-avatar/references/examples.json +0 -8
  184. package/resources/dada-object-avatar/references/prompt-template.md +0 -27
  185. package/resources/dada-object-avatar/references/style-system.md +0 -54
  186. package/resources/dada-object-avatar/references/subject-design.md +0 -23
  187. package/resources/dada-object-avatar/references/training-guide.md +0 -14
  188. package/resources/dada-object-avatar/references/training-log.jsonl +0 -0
  189. package/resources/dada-object-avatar/references/training-record.schema.json +0 -20
  190. package/resources/dada-object-avatar/scripts/choose_gaze.py +0 -36
  191. package/resources/dada-object-avatar/scripts/record_example.py +0 -22
  192. package/resources/dada-object-avatar/scripts/validate_png.py +0 -52
  193. package/resources/dada-object-avatar.manifest.json +0 -85
@@ -1,168 +0,0 @@
1
- // Read-only mirror of the host's saved design workflow. No confirmation writes.
2
- const el = (tag, text, cls) => {
3
- const node = document.createElement(tag);
4
- if (text !== undefined) node.textContent = text;
5
- if (cls) node.className = cls;
6
- return node;
7
- };
8
- const root = document.getElementById('root');
9
- const nav = el('nav', undefined, 'design-navigation');
10
- nav.setAttribute('aria-label', '创作内容');
11
- const bookButton = el('button', '四册 Booklet');
12
- const designButton = el('button', '小挂件设计');
13
- const progress = el('span', '正在读取创作进度', 'design-progress');
14
- nav.append(bookButton, designButton, progress);
15
- document.body.prepend(nav);
16
- const panel = el('main', undefined, 'design-workspace');
17
- panel.hidden = true;
18
- document.body.append(panel);
19
- let chosen = false, previousPhase, version;
20
- const selectedStates = new Map();
21
- function choose(design, manual = false) {
22
- if (manual) chosen = true;
23
- root.hidden = design; panel.hidden = !design;
24
- bookButton.setAttribute('aria-pressed', String(!design));
25
- designButton.setAttribute('aria-pressed', String(design));
26
- // Do not stop the upstream reader before its first snapshot selects a stage.
27
- if (manual && root.querySelector('.preview-header')) document.dispatchEvent(new Event('buddy:reader-browse'));
28
- }
29
- bookButton.onclick = () => choose(false, true);
30
- designButton.onclick = () => choose(true, true);
31
- choose(false);
32
- const card = title => { const node = el('section', undefined, 'design-card'); node.append(el('h2', title)); return node; };
33
- const badge = (text, pass = false) => el('span', text, 'design-badge' + (pass ? ' passed' : ''));
34
- function field(parent, label, value) { const row = el('div', undefined, 'design-field'); row.append(el('dt', label), el('dd', value)); parent.append(row); }
35
- const reviewLabels = { 'booklet-fit': '符合搭子需求', 'reading-order': '阅读顺序与长内容', 'chat-consistency': '对话卡片与展开页一致', 'conversation-update': '数据随对话逐步更新', 'read-only': '只读交互边界' };
36
- function render(snapshot) {
37
- const design = snapshot.widgetDesign;
38
- if (!design) { nav.hidden = true; return; }
39
- nav.hidden = false;
40
- const index = design.steps.findIndex(step => step.id === design.phase);
41
- progress.textContent = design.enabled ? design.steps.map((step, i) => (i < index ? '✓ ' : '') + step.label).join(' → ') : '已有作品';
42
- if (!chosen && design.phase !== 'booklets' && design.phase !== 'legacy' && previousPhase !== design.phase) choose(true);
43
- previousPhase = design.phase;
44
- panel.replaceChildren();
45
- const heading = el('div', undefined, 'design-heading');
46
- heading.append(el('p', 'BOOKLET / 交互设计', 'design-eyebrow'), el('h1', '小挂件,让对话里的内容更好读'), el('p', design.nextStep));
47
- if (snapshot.paused) heading.append(badge('创作已暂停;确认进度已保留'));
48
- panel.append(heading);
49
- const steps = el('ol', undefined, 'design-steps');
50
- design.steps.forEach((step, i) => {
51
- const li = el('li', undefined, i === index ? 'current' : i < index ? 'done' : '');
52
- li.append(el('span', i < index ? '✓' : String(i + 1)), el('strong', step.label));
53
- if (i === index) li.setAttribute('aria-current', 'step');
54
- steps.append(li);
55
- });
56
- panel.append(steps, el('p', '此页同步清单、原型和验收进度。确认、补充和修改请在主对话中提出。', 'design-callout'));
57
- const list = card('01 / 小挂件清单');
58
- list.append(badge(design.catalogConfirmed ? '开发者已确认' : design.phase === 'booklets' ? '四册确认后开始' : '待开发者确认', design.catalogConfirmed));
59
- if (!design.catalog) list.append(el('p', '完成四册后,将根据搭子的任务、持续服务和数据需求提出清单,供你确认、补充或调整。'));
60
- else if (!design.items.length) list.append(el('p', design.catalog.data.noWidgetsReason), el('p', design.catalogConfirmed ? '已明确确认本作品不使用小挂件。' : '“不使用小挂件”也需要在主对话明确确认。'));
61
- else {
62
- for (const item of design.items) {
63
- const entry = el('details', undefined, 'design-list-item'); entry.open = true;
64
- entry.append(el('summary', item.name));
65
- const fields = el('dl', undefined, 'design-specs');
66
- for (const [label, key] of [['用途', 'purpose'], ['何时创建 / 展示', 'creationTrigger'], ['首次展示', 'initialContent'], ['对话更新规则', 'updateRules'], ['实例沿用 / 新建', 'instancePolicy'], ['对话摘要', 'conversationSummary']]) field(fields, label, item[key]);
67
- field(fields, 'Booklet 依据', item.bookletRefs.map(ref => {
68
- const source = snapshot.artifacts.find(a => a.id === ref.id);
69
- const [stage, chapter] = ref.id.split('.');
70
- const label = { definition: '定义', knowledge: '知识', methods: '方法', service: '服务' }[stage];
71
- return label + '手册 · ' + (source?.title && source.title !== ref.id ? source.title : '第 ' + chapter + ' 章');
72
- }).join(';'));
73
- field(fields, '阅读能力', item.readInteractions.map(key => ({ tabs: '分组切换', filter: '预设筛选', collapse: '折叠', detail: '只读详情' })[key]).join('、') || '滚动阅读');
74
- entry.append(fields);
75
- const table = el('table'); const head = el('tr'); ['展示字段', '数据来源', '尚未补充时'].forEach(label => head.append(el('th', label))); table.append(head);
76
- item.fields.forEach(f => { const row = el('tr'); [f.label, f.source, f.emptyText].forEach(value => row.append(el('td', value))); table.append(row); });
77
- entry.append(table); list.append(entry);
78
- }
79
- }
80
- panel.append(list);
81
- const prototypes = card('02 / 原型确认');
82
- prototypes.append(el('p', '同一份数据,两种展示方式。切换下方示例状态,再试用小挂件内的阅读操作。', 'design-muted'));
83
- if (!design.items.length) prototypes.append(el('p', design.catalogConfirmed ? '已确认不需要小挂件,本阶段无需原型。' : '清单确认后生成原型。'));
84
- for (const item of design.items) {
85
- const section = el('section', undefined, 'design-prototype');
86
- section.append(el('h3', item.name), badge(item.prototypeConfirmed ? '当前原型已确认' : '待确认原型', item.prototypeConfirmed));
87
- if (!item.prototype || !item.automaticCheck.pass || Object.keys(item.blockers).length) {
88
- section.append(el('p', !item.prototype ? '确认清单后生成该小挂件的原型。' : Object.keys(item.blockers).length ? '依据或清单已调整,请在主对话继续校准这一项。' : item.automaticCheck.message));
89
- } else {
90
- const prototype = item.prototype, data = prototype.data;
91
- let selected = selectedStates.get(item.id) || 'initial';
92
- const control = el('div', undefined, 'design-state-tabs'); control.setAttribute('aria-label', item.name + '示例状态');
93
- const grid = el('div', undefined, 'design-views');
94
- const refresh = () => {
95
- grid.replaceChildren();
96
- for (const view of ['chat', 'expanded']) {
97
- const figure = el('figure');
98
- figure.append(el('figcaption', view === 'chat' ? '出现在对话中' : '展开后阅读'));
99
- const frame = el('iframe'); frame.title = item.name + ' / ' + (view === 'chat' ? '对话卡片' : '展开页');
100
- frame.width = data.design.screenWidth; frame.height = data.design.screenHeight;
101
- frame.sandbox = 'allow-scripts';
102
- frame.src = './widget-assets/' + prototype.hash + '/index.html?view=' + view + '&state=' + selected;
103
- frame.loading = 'lazy'; figure.append(frame);
104
- const link = el('a', '查看示意图'); link.target = '_blank'; link.rel = 'noopener';
105
- link.href = './widget-assets/' + prototype.hash + '/' + view + '-' + selected + '.svg'; figure.append(link);
106
- grid.append(figure);
107
- }
108
- };
109
- for (const sample of data.states) {
110
- const button = el('button', sample.label); button.setAttribute('aria-pressed', String(sample.id === selected));
111
- button.onclick = () => { selected = sample.id; selectedStates.set(item.id, selected); for (const b of control.children) b.setAttribute('aria-pressed', String(b === button)); refresh(); };
112
- control.append(button);
113
- }
114
- const size = data.design;
115
- section.append(el('p', '示例画布 ' + size.screenWidth + ' × ' + size.screenHeight + ';展开页 ' + (size.screenWidth - 32) + ' × ' + Math.round(size.screenHeight * .65) + '。', 'design-muted'), control, grid);
116
- refresh();
117
- section.append(el('p', '原型版本 ' + prototype.hash.slice(0, 10) + ' · 以上为示例数据,用于确认样式和交互。', 'design-muted'));
118
- }
119
- prototypes.append(section);
120
- }
121
- panel.append(prototypes);
122
- const acceptance = card('03 / 设计验收');
123
- acceptance.append(el('p', '自动检查验证结构与资源。开发者验收确认需求、阅读体验和对话更新是否符合预期。生产环境接入另行验证。', 'design-muted'));
124
- for (const item of design.items) {
125
- const group = el('section', undefined, 'design-acceptance');
126
- group.append(el('h3', item.name), badge(item.accepted ? '开发者已验收当前版本' : '待开发者验收', item.accepted));
127
- group.append(el('p', (item.automaticCheck.pass ? '✓ 自动检查:' : '○ 自动检查:') + item.automaticCheck.message));
128
- const table = el('table'); const header = el('tr'); ['开发者检查项', '检查记录', '确认状态'].forEach(t => header.append(el('th', t))); table.append(header);
129
- for (const key of design.reviewChecks) { const row = el('tr'); row.append(el('td', reviewLabels[key]), el('td', item.acceptance?.data.reviews[key] || '尚未记录'), el('td', item.accepted ? '已确认' : '待确认')); table.append(row); }
130
- group.append(table);
131
- if (item.acceptance?.unresolved?.length) group.append(el('p', '待处理:' + item.acceptance.unresolved.join(';')));
132
- acceptance.append(group);
133
- }
134
- if (!design.items.length) acceptance.append(el('p', design.catalogConfirmed ? '已确认不需要小挂件,无需逐项设计验收。' : '原型确认后逐项验收。'));
135
- panel.append(acceptance);
136
- const deliverables = card('04 / 交付与开发衔接');
137
- if (snapshot.completion?.status === 'ready' && snapshot.completion.files) {
138
- deliverables.append(badge('成果已生成', true), el('p', '包含四册、服务模式图、小挂件清单、两视图三态原型和设计验收记录。'));
139
- deliverables.append(el('p', '完整创建任务将在此处自动衔接开发:由当前 Agent 读取已确认手册与小挂件设计,继续实现,无需再次确认开始。仅设计、流程测试或已暂停的任务按原范围结束。实际开发进度请在主对话查看。', 'design-muted'));
140
- const downloads = el('details'); downloads.append(el('summary', '查看全部成果文件'));
141
- for (const file of snapshot.completion.files) {
142
- const a = el('a', file); a.href = './deliverables/' + snapshot.revision + '/' + file; a.download = file.split('/').pop();
143
- const row = el('p'); row.append(a); downloads.append(row);
144
- if (file === 'BUDDY_MANUAL.md' || file === 'WIDGET_DESIGN.md') { const quick = a.cloneNode(true); quick.className = 'design-download'; quick.textContent = file === 'BUDDY_MANUAL.md' ? '下载创作手册' : '下载小挂件设计说明'; deliverables.append(quick); }
145
- }
146
- deliverables.append(downloads);
147
- } else deliverables.append(el('p', snapshot.completion?.message || '清单、当前原型及设计验收确认完成后生成交付。四册内容可先在 Booklet 中阅读。'));
148
- panel.append(deliverables);
149
- }
150
- let loading = false;
151
- async function update() {
152
- if (loading) return;
153
- loading = true;
154
- try {
155
- const response = await fetch('./api/snapshot');
156
- if (!response.ok) throw new Error('snapshot unavailable');
157
- const snapshot = await response.json();
158
- const next = JSON.stringify([snapshot.widgetDesign, snapshot.completion, snapshot.paused]);
159
- if (next !== version) { version = next; render(snapshot); }
160
- } catch { progress.textContent = '页面暂时无法同步;已展示的内容保留,请回主对话查看进度。'; }
161
- finally { loading = false; }
162
- }
163
- update();
164
- const events = new EventSource('./api/events');
165
- events.addEventListener('changed', update);
166
- events.addEventListener('ready', update);
167
- events.addEventListener('unavailable', () => { progress.textContent = '内容暂时无法读取,请回主对话检查。'; });
168
- document.addEventListener('visibilitychange', () => { if (!document.hidden) update(); });
@@ -1,62 +0,0 @@
1
- "use strict";
2
- const params = new URLSearchParams(location.search);
3
- const requested = params.get('state') || 'initial';
4
- const screens = [...document.querySelectorAll('.screen')];
5
- const screen = screens.find(node => node.dataset.state === requested) || screens[0];
6
- for (const node of screens) node.hidden = node !== screen;
7
- const overlay = screen.querySelector('.overlay');
8
- const details = screen.querySelector('.detail-layer');
9
- const open = screen.querySelector('.open-sheet');
10
- const close = screen.querySelector('.sheet-close');
11
- const detailClose = screen.querySelector('.detail-close');
12
- let returnFocus = null;
13
- function closeDetail() {
14
- details.hidden = true;
15
- if (returnFocus) returnFocus.focus();
16
- }
17
- function closeSheet() {
18
- closeDetail();
19
- overlay.hidden = true;
20
- screen.querySelector('.chat-body').inert = false;
21
- open.focus();
22
- }
23
- open.addEventListener('click', () => {
24
- overlay.hidden = false;
25
- screen.querySelector('.chat-body').inert = true;
26
- close.focus();
27
- });
28
- close.addEventListener('click', closeSheet);
29
- detailClose.addEventListener('click', closeDetail);
30
- for (const button of screen.querySelectorAll('[data-group]')) {
31
- button.addEventListener('click', () => {
32
- const selected = button.dataset.group;
33
- for (const option of screen.querySelectorAll('[data-group]')) option.setAttribute('aria-pressed', String(option.dataset.group === selected));
34
- for (const section of screen.querySelectorAll('[data-section]')) section.hidden = selected !== 'all' && section.dataset.section !== selected;
35
- });
36
- }
37
- for (const button of screen.querySelectorAll('.detail-open')) {
38
- button.addEventListener('click', () => {
39
- returnFocus = button;
40
- const row = button.closest('.row');
41
- screen.querySelector('.detail-title').textContent = row.querySelector('.row-label').textContent;
42
- screen.querySelector('.detail-text').textContent = row.querySelector('.detail-copy').textContent;
43
- details.hidden = false;
44
- detailClose.focus();
45
- });
46
- }
47
- document.addEventListener('keydown', event => {
48
- if (event.key === 'Escape') {
49
- if (!details.hidden) closeDetail();
50
- else if (!overlay.hidden) closeSheet();
51
- }
52
- if (event.key === 'Tab' && !overlay.hidden) {
53
- const scope = details.hidden ? screen.querySelector('.sheet') : screen.querySelector('.detail-box');
54
- const buttons = [...scope.querySelectorAll('button,summary')].filter(el => el.getClientRects().length);
55
- const first = buttons[0], last = buttons[buttons.length - 1];
56
- if (event.shiftKey && document.activeElement === first) {event.preventDefault();last.focus();}
57
- else if (!event.shiftKey && document.activeElement === last) {event.preventDefault();first.focus();}
58
- }
59
- });
60
- overlay.hidden = params.get('view') !== 'expanded';
61
- screen.querySelector('.chat-body').inert = !overlay.hidden;
62
- if (!overlay.hidden) close.focus({preventScroll:true});
@@ -1 +0,0 @@
1
- *{box-sizing:border-box}html,body{margin:0;width:100%;height:100%;font:400 16px/1.5 -apple-system,BlinkMacSystemFont,"PingFang SC","Segoe UI",sans-serif;color:#243C37;background:#FCFCF9}button{font:inherit;color:inherit;cursor:pointer}button,summary{-webkit-tap-highlight-color:transparent}button:focus-visible,summary:focus-visible{outline:3px solid var(--accent);outline-offset:3px}[hidden]{display:none!important}h1,h2,h3,p{margin:0}h1,h2,h3{font-size:var(--title-size,16px);font-weight:500}svg{display:block;flex:none}button{border:0}button:active{transform:scale(.97)}.screen{font:400 16px/1.5 -apple-system,BlinkMacSystemFont,"PingFang SC","Segoe UI",sans-serif;color:#243C37;position:relative;width:100%;height:100%;overflow:hidden;--accent:#28645B;--soft:#EAF1E9;--muted:#75817B;--line:#E5E9E1;background:#FCFCF9}.icon{width:22px;height:22px;stroke:currentColor;stroke-width:1.65;fill:none;stroke-linecap:round;stroke-linejoin:round}.statusbar{height:38px;padding:12px 25px 0;display:flex;justify-content:space-between;align-items:center;font-size:13px;font-weight:500}.statusbar .device{display:flex;align-items:center;gap:5px}.device .icon{width:17px;height:17px}.chat-header{height:61px;padding:5px 19px 14px;display:flex;align-items:center;gap:12px;border-bottom:1px solid #E9ECE6;background:#FCFCF9}.chat-header .back{margin-right:2px}.buddy-identity{flex:1}.buddy-identity strong{display:block;font-size:16px;font-weight:500;letter-spacing:-.3px}.buddy-identity small{display:block;font-size:11px;color:var(--muted);margin-top:1px}.header-mark{width:33px;height:33px;border-radius:12px;background:var(--accent);color:white;display:grid;place-items:center;font-size:16px;font-weight:500}.chat-body{height:calc(100% - 178px);overflow-y:auto;padding:20px 16px 30px;scrollbar-width:none}.chat-date{text-align:center;font-size:11px;color:#929A93;margin:0 0 17px;letter-spacing:.3px}.message{display:flex;gap:9px;margin:13px 0;align-items:flex-start}.avatar{width:26px;height:26px;border-radius:10px;background:var(--soft);color:var(--accent);display:grid;place-items:center;font-size:13px;font-weight:500;flex:none;margin-top:3px}.bubble{padding:12px 14px;max-width:calc(100% - 41px);font-size:16px;line-height:1.65;border-radius:16px;white-space:pre-wrap;overflow-wrap:anywhere}.message.user{justify-content:flex-end;margin-bottom:20px}.user .bubble{background:#E6EEEB;border-top-right-radius:5px}.assistant .bubble{background:#F1F2EC;border-top-left-radius:5px}.chat-card{margin:16px 0 0 35px;border:1px solid #E2E7DC;border-radius:21px;background:#fff;overflow:hidden;box-shadow:0 5px 22px #334F3710}.card-top{padding:16px 16px 13px;display:flex;align-items:center;gap:11px}.identity-icon{display:grid;place-items:center;width:37px;height:37px;flex:none;border-radius:13px;color:var(--accent);background:var(--soft)}.card-kind{font-size:11px;color:var(--muted);letter-spacing:.4px;margin-bottom:2px}.chat-card h1{font-size:16px;letter-spacing:-.3px}.card-summary{font-size:16px;line-height:1.65;white-space:pre-wrap;overflow-wrap:anywhere;padding:0 16px 14px}.card-meta{margin:0 16px 15px;padding:10px 0;border-top:1px solid var(--line);display:flex;gap:16px}.card-meta .metric{flex:1;min-width:0}.metric-label{font-size:11px;color:var(--muted);display:block}.metric-value{font-size:16px;font-weight:500;display:block;margin-top:3px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.open-sheet{width:calc(100% - 32px);margin:0 16px 15px;padding:10px 14px;border-radius:12px;background:var(--accent);color:#fff;display:flex;align-items:center;justify-content:space-between;font-size:14px;font-weight:500}.open-sheet .icon{width:17px;height:17px}.card-foot{font-size:11px;color:var(--muted);padding:0 16px 14px;display:flex;align-items:center;gap:5px}.card-foot .icon{width:12px;height:12px}.composer{position:absolute;bottom:19px;left:16px;right:16px;display:flex;align-items:center;gap:10px;border:1px solid #E5E8DE;border-radius:16px;background:#fff;padding:12px;color:#859087;font-size:12px;min-height:49px;box-shadow:0 3px 17px #334F3708}.composer span{flex:1}.home-indicator{position:absolute;bottom:7px;left:36%;right:36%;height:4px;background:#35433E;border-radius:9px}.overlay{position:absolute;inset:0;display:grid;place-items:center;background:#263F3552;backdrop-filter:blur(2px);z-index:2}.sheet{width:calc(100% - 32px);height:65%;border-radius:25px;background:#FFFEFB;box-shadow:0 22px 60px #17362B30;display:flex;flex-direction:column;overflow:hidden;position:relative}.sheet-header{padding:14px 16px;display:flex;gap:10px;align-items:center;flex:none;min-height:66px;border-bottom:1px solid #E8ECE2}.sheet-header .identity-icon{width:32px;height:32px;border-radius:11px}.sheet-header .icon{width:19px;height:19px}.sheet-header h2{flex:1;font-size:16px;letter-spacing:-.2px}.close{width:32px;height:32px;flex:none;border:1px solid #E5E8DF;border-radius:50%;background:#FFFEFB;display:grid;place-items:center;color:#526B5E}.close .icon{width:16px;height:16px}.sheet-body{overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:#CBD3C5 transparent;min-height:0;flex:1;padding:0 16px 16px}.overview{padding:18px 0 15px;border-bottom:1px solid var(--line)}.eyebrow{font-size:10px;color:var(--accent);letter-spacing:1.2px;font-weight:500;margin-bottom:6px}.overview-title{font-size:20px;line-height:1.4;letter-spacing:-.5px;font-weight:500;color:#203D31}.overview-subtitle{font-size:12px;color:var(--muted);margin-top:5px;line-height:1.5;white-space:pre-wrap}.overview-metrics{display:grid;grid-template-columns:repeat(var(--metric-count),1fr);gap:10px;margin-top:14px}.overview-metrics .metric{padding-left:10px;border-left:1px solid #DDE4D7;min-width:0}.overview-metrics .metric:first-child{padding-left:0;border-left:0}.overview-metrics .metric-value{font-size:20px;line-height:1.35;letter-spacing:-.4px}.progress-track{height:5px;background:#E4EADB;border-radius:9px;overflow:hidden;margin-top:14px}.progress-fill{height:100%;background:var(--accent);border-radius:9px}.progress-caption{font-size:11px;color:var(--muted);margin-top:6px}.reading-nav{position:sticky;top:0;z-index:1;background:#FFFEFB;padding:13px 0 10px;display:flex;gap:7px;overflow-x:auto;scrollbar-width:none;box-shadow:0 3px 0 #FFFEFB}.reading-nav button{border:1px solid #E3E8DC;background:#F5F6EF;border-radius:10px;padding:8px 12px;flex:none;font-size:13px;white-space:nowrap;color:#6B7C6D}.reading-nav button[aria-pressed=true]{border-color:var(--accent);background:var(--accent);color:#fff;box-shadow:0 3px 7px #205C4120}.section{margin:8px 0 18px}.section-heading{font-size:13px;font-weight:500;color:#53694F;margin:0 0 9px;padding:5px 0;display:flex;align-items:center;gap:8px;cursor:default;list-style:none}.section-heading::-webkit-details-marker{display:none}.section-heading .icon{width:14px;height:14px;margin-left:auto;transition:transform .16s ease}summary.section-heading{cursor:pointer}details[open]>summary .icon{transform:rotate(90deg)}.section-count{font-size:10px;color:#809077;background:#EEF1E7;padding:1px 6px;border-radius:5px}.row{display:flex;gap:10px;position:relative;align-items:flex-start;padding:13px 0;border-bottom:1px solid #E8ECE2}.row:last-child{border-bottom:0}.row-symbol{width:29px;height:29px;flex:none;color:var(--accent);border:1px solid #E0E7D8;background:#F4F6EE;border-radius:10px;display:grid;place-items:center;margin-top:1px}.row-symbol .icon{width:17px;height:17px}.row-content{flex:1;min-width:0}.row-label{font-size:16px;font-weight:500;line-height:1.45;overflow-wrap:anywhere}.row-value{font-size:16px;font-weight:400;line-height:1.6;margin-top:3px;white-space:pre-wrap;overflow-wrap:anywhere;color:#405348}.row-caption{font-size:12px;line-height:1.55;color:var(--muted);margin-top:5px;white-space:pre-wrap;overflow-wrap:anywhere}.status{display:inline-flex;align-items:center;gap:4px;font-size:10px;line-height:1.5;padding:3px 7px;border-radius:6px;margin-top:7px;color:#687565;background:#EEF1E8}.status:before{content:"";width:4px;height:4px;border-radius:50%;background:currentColor}.status.positive{color:#2D6A50;background:#EAF2E7}.status.pending{color:#92662E;background:#F7EFDC}.status.changed{color:#416B7A;background:#E9F0F1}.status.neutral{color:#73806E;background:#EDF0E8}.detail-open{flex:none;background:transparent;padding:8px 0 8px 4px;border-radius:9px;color:#849477;margin-top:2px;min-width:28px;min-height:36px}.detail-open .icon{width:17px;height:17px}.timeline .row{border:0;padding-left:63px;min-height:88px;padding-top:7px;padding-bottom:17px}.timeline .row:before{content:"";position:absolute;left:46px;top:15px;bottom:-8px;width:1px;background:#DAE3D1}.timeline .row:last-child:before{bottom:15px}.timeline .row:after{content:"";position:absolute;left:42px;top:13px;width:9px;height:9px;background:var(--accent);border:2px solid #FFFEFB;border-radius:50%;box-shadow:0 0 0 1px #BDD0B6}.timeline .row-label{position:absolute;left:0;top:10px;width:35px;font-size:11px;line-height:1.5;font-weight:500;font-variant-numeric:tabular-nums;color:#6F826A}.timeline .row-value{font-size:16px;color:#243C37;margin:0}.timeline .row-symbol{display:none}.cards .row{background:#F5F6F0;border:1px solid #E6EADF;border-radius:16px;padding:14px 12px;margin-bottom:10px}.cards .row:last-child{border-bottom:1px solid #E6EADF}.cards .row-symbol{background:#FFFEFB}.ledger .row{align-items:center}.ledger .row-content{display:grid;grid-template-columns:1fr auto;gap:2px 10px;align-items:baseline}.ledger .row-label{font-size:16px;font-weight:400}.ledger .row-value{font-size:16px;font-weight:500;margin:0;text-align:right;font-variant-numeric:tabular-nums}.ledger .row-caption,.ledger .status{grid-column:1/-1}.ledger .status{justify-self:start}.checklist .row{padding:12px 0}.checklist .row-symbol{border-radius:50%;width:24px;height:24px;margin-top:2px;background:transparent}.checklist .row.positive .row-symbol{background:#EAF2E7;color:#2D6A50;border-color:#C4D4BD}.checklist .row-value{font-size:16px;color:#687B60}.note{display:flex;align-items:flex-start;gap:6px;border-top:1px solid var(--line);padding-top:13px;font-size:11px;color:#8A9580;line-height:1.5}.note .icon{width:13px;height:13px;margin-top:1px}.detail-layer{position:absolute;inset:0;background:#263F3566;display:grid;place-items:center;z-index:3}.detail-box{width:calc(100% - 32px);max-height:65%;overflow-y:auto;background:#FFFEFB;border-radius:23px;padding:18px;box-shadow:0 15px 40px #18382B26}.detail-head{display:flex;align-items:center;gap:10px;margin-bottom:14px}.detail-head h3{flex:1;font-size:16px}.detail-text{font-size:16px;line-height:1.75;white-space:pre-wrap;overflow-wrap:anywhere}.detail-foot{font-size:11px;color:var(--muted);border-top:1px solid var(--line);margin-top:18px;padding-top:12px}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@media(prefers-reduced-motion:no-preference){button{transition:background .16s ease,transform .16s ease,box-shadow .16s ease}.overlay{animation:veil .18s ease both}.sheet{animation:arrive .24s cubic-bezier(.2,.8,.2,1) both}.detail-box{animation:arrive .2s ease both}@keyframes arrive{from{transform:translateY(8px) scale(.99);opacity:.3}to{transform:none;opacity:1}}@keyframes veil{from{opacity:0}to{opacity:1}}}.svg-export *{animation:none!important;transition:none!important}.svg-export .overlay{backdrop-filter:none}
@@ -1,65 +0,0 @@
1
- "use strict";
2
- const params = new URLSearchParams(location.search);
3
- const requested = params.get('state') || 'initial';
4
- const screens = [...document.querySelectorAll('.screen')];
5
- const screen = screens.find(node => node.dataset.state === requested) || screens[0];
6
- for (const node of screens) node.hidden = node !== screen;
7
- const overlay = screen.querySelector('.overlay');
8
- const details = screen.querySelector('.detail-layer');
9
- const sheet = screen.querySelector('.sheet');
10
- const open = screen.querySelector('.open-sheet');
11
- const close = screen.querySelector('.sheet-close');
12
- const detailClose = screen.querySelector('.detail-close');
13
- let returnFocus = null;
14
- function closeDetail() {
15
- details.hidden = true;
16
- sheet.inert = false;
17
- if (returnFocus) returnFocus.focus();
18
- }
19
- function closeSheet() {
20
- closeDetail();
21
- overlay.hidden = true;
22
- screen.querySelector('.chat-body').inert = false;
23
- open.focus();
24
- }
25
- open.addEventListener('click', event => {
26
- overlay.hidden = false;
27
- screen.querySelector('.chat-body').inert = true;
28
- (event.detail === 0 ? close : sheet).focus({preventScroll:true});
29
- });
30
- close.addEventListener('click', closeSheet);
31
- detailClose.addEventListener('click', closeDetail);
32
- for (const button of screen.querySelectorAll('[data-group]')) {
33
- button.addEventListener('click', () => {
34
- const selected = button.dataset.group;
35
- for (const option of screen.querySelectorAll('[data-group]')) option.setAttribute('aria-pressed', String(option.dataset.group === selected));
36
- for (const section of screen.querySelectorAll('[data-section]')) section.hidden = selected !== 'all' && section.dataset.section !== selected;
37
- });
38
- }
39
- for (const button of screen.querySelectorAll('.detail-open')) {
40
- button.addEventListener('click', () => {
41
- returnFocus = button;
42
- const row = button.closest('.row');
43
- screen.querySelector('.detail-title').textContent = row.querySelector('.row-label, .row-value').textContent;
44
- screen.querySelector('.detail-text').textContent = row.querySelector('.detail-copy').textContent;
45
- details.hidden = false;
46
- sheet.inert = true;
47
- detailClose.focus();
48
- });
49
- }
50
- document.addEventListener('keydown', event => {
51
- if (event.key === 'Escape') {
52
- if (!details.hidden) closeDetail();
53
- else if (!overlay.hidden) closeSheet();
54
- }
55
- if (event.key === 'Tab' && !overlay.hidden) {
56
- const scope = details.hidden ? screen.querySelector('.sheet') : screen.querySelector('.detail-box');
57
- const buttons = [...scope.querySelectorAll('button,summary')].filter(el => el.getClientRects().length);
58
- const first = buttons[0], last = buttons[buttons.length - 1];
59
- if (event.shiftKey && (document.activeElement === first || document.activeElement === sheet)) {event.preventDefault();last.focus();}
60
- else if (!event.shiftKey && document.activeElement === last) {event.preventDefault();first.focus();}
61
- }
62
- });
63
- overlay.hidden = params.get('view') !== 'expanded';
64
- screen.querySelector('.chat-body').inert = !overlay.hidden;
65
- if (!overlay.hidden) sheet.focus({preventScroll:true});
@@ -1,103 +0,0 @@
1
- * { box-sizing: border-box; }
2
- html, body { margin: 0; width: 100%; height: 100%; background: #fff; }
3
- button { font: inherit; color: inherit; border: 0; cursor: pointer; }
4
- button, summary { -webkit-tap-highlight-color: transparent; }
5
- button:focus-visible, summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
6
- [hidden] { display: none !important; }
7
- h1, h2, h3, p { margin: 0; }
8
- h1, h2, h3 { font-size: var(--title-size, 16px); font-weight: 500; }
9
- .screen { font: 400 16px/1.5 -apple-system, BlinkMacSystemFont, "PingFang SC", "Segoe UI", sans-serif; color: #202733; position: relative; width: 100%; height: 100%; overflow: hidden; background: #fff; --accent: #326BC4; --muted: #7C8490; --line: #E9EDF2; }
10
- .icon { display: block; flex: none; width: 22px; height: 22px; stroke: currentColor; stroke-width: 1.6; fill: none; stroke-linecap: round; stroke-linejoin: round; }
11
- .statusbar { height: 40px; padding: 10px 25px 0; display: flex; align-items: center; justify-content: space-between; font-size: 13px; font-weight: 500; }
12
- .device { display: flex; gap: 5px; }
13
- .device .icon { width: 16px; height: 16px; }
14
- .chat-header { height: 56px; padding: 0 20px 10px; display: flex; align-items: center; gap: 16px; border-bottom: 1px solid var(--line); }
15
- .chat-header strong { flex: 1; text-align: center; font-size: 16px; font-weight: 500; }
16
- .back { font-size: 28px; line-height: 1; }
17
- .chat-body { height: calc(100% - 176px); overflow-y: auto; padding: 20px 16px 28px; scrollbar-width: none; }
18
- .chat-date { color: #9299A3; text-align: center; font-size: 11px; margin-bottom: 18px; }
19
- .message { display: flex; gap: 9px; align-items: flex-start; margin: 14px 0; }
20
- .avatar { flex: none; width: 26px; height: 26px; border-radius: 8px; background: #E9F0FB; color: var(--accent); display: grid; place-items: center; font-size: 12px; font-weight: 500; margin-top: 3px; }
21
- .bubble { max-width: calc(100% - 38px); font-size: 16px; line-height: 1.65; padding: 12px 14px; border-radius: 12px; white-space: pre-wrap; overflow-wrap: anywhere; }
22
- .user { justify-content: flex-end; }
23
- .user .bubble { background: #EAF1FB; border-top-right-radius: 3px; }
24
- .assistant .bubble { background: #F4F5F7; border-top-left-radius: 3px; }
25
- .chat-card { margin: 16px 0 0 35px; border: 1px solid #DFE5ED; border-radius: 14px; background: #fff; overflow: hidden; }
26
- .card-content { padding: 17px 16px 18px; }
27
- .card-kind { font-size: 12px; color: var(--accent); margin-bottom: 8px; }
28
- .chat-card h1 { font-size: 16px; font-weight: 500; line-height: 1.45; }
29
- .card-summary { font-size: 16px; line-height: 1.65; color: #66707E; margin-top: 9px; white-space: pre-wrap; overflow-wrap: anywhere; }
30
- .open-sheet { width: 100%; padding: 12px 16px; display: flex; align-items: center; justify-content: space-between; border-top: 1px solid var(--line); background: #fff; color: var(--accent); font-size: 14px; font-weight: 500; }
31
- .open-sheet .icon { width: 17px; height: 17px; }
32
- .open-sheet:hover { background: #F6F8FC; }
33
- .composer { position: absolute; bottom: 19px; left: 16px; right: 16px; display: flex; align-items: center; gap: 10px; min-height: 49px; padding: 12px; border: 1px solid #E6E9EF; border-radius: 14px; background: #fff; color: #9299A3; font-size: 12px; }
34
- .composer span { flex: 1; }
35
- .home-indicator { position: absolute; bottom: 7px; left: 36%; right: 36%; height: 4px; border-radius: 8px; background: #252C37; }
36
- .overlay { position: absolute; inset: 0; z-index: 2; background: #17243B45; display: grid; place-items: center; }
37
- .sheet { width: calc(100% - 32px); height: 65%; border-radius: 22px; background: #fff; display: flex; flex-direction: column; overflow: hidden; position: relative; box-shadow: 0 12px 36px #192B4720; outline: none; }
38
- .sheet-header { flex: none; height: 60px; display: flex; align-items: center; padding: 0 16px 0 20px; border-bottom: 1px solid var(--line); }
39
- .sheet-header h2 { flex: 1; font-size: 16px; font-weight: 500; }
40
- .close { width: 32px; height: 32px; background: transparent; color: #8490A0; border-radius: 8px; display: grid; place-items: center; flex: none; }
41
- .close .icon { width: 20px; height: 20px; }
42
- .close:hover { background: #F3F5F8; color: #26344C; }
43
- .sheet-body { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 0 20px 20px; scrollbar-width: thin; scrollbar-color: #D5DBE5 transparent; }
44
- .overview { padding: 22px 0 15px; }
45
- .overview h3 { font-size: 20px; line-height: 1.4; font-weight: 500; letter-spacing: -.4px; }
46
- .overview-subtitle { color: var(--muted); font-size: 13px; margin-top: 6px; line-height: 1.6; white-space: pre-wrap; }
47
- .reading-meta { color: #606E81; font-size: 13px; margin-top: 12px; }
48
- .overview-metrics { display: grid; grid-template-columns: repeat(var(--metric-count), 1fr); gap: 16px; margin-top: 22px; }
49
- .metric-label { display: block; font-size: 12px; color: var(--muted); }
50
- .metric-value { display: block; font-size: 20px; font-weight: 500; line-height: 1.4; margin-top: 4px; letter-spacing: -.3px; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
51
- .overview-metrics .metric:first-child .metric-value { color: var(--accent); }
52
- .progress-track { height: 3px; overflow: hidden; background: #ECF0F5; margin-top: 16px; }
53
- .progress-track > div { height: 100%; background: var(--accent); }
54
- .progress-caption { font-size: 11px; color: var(--muted); margin-top: 7px; }
55
- .reading-nav { position: sticky; top: 0; z-index: 1; display: flex; gap: 22px; background: #fff; border-bottom: 1px solid var(--line); overflow-x: auto; scrollbar-width: none; }
56
- .reading-nav button { flex: none; position: relative; padding: 12px 0; font-size: 14px; background: transparent; color: #8A93A1; white-space: nowrap; }
57
- .reading-nav button[aria-pressed=true] { color: var(--accent); font-weight: 500; }
58
- .reading-nav button[aria-pressed=true]:after { position: absolute; content: ""; height: 2px; background: var(--accent); left: 0; right: 0; bottom: 0; }
59
- .reading-nav button:hover { color: var(--accent); }
60
- .section { margin: 20px 0 24px; }
61
- .section-heading { display: flex; align-items: center; gap: 8px; margin-bottom: 7px; color: #8A93A1; font-size: 12px; font-weight: 400; line-height: 1.5; list-style: none; }
62
- .section-heading::-webkit-details-marker { display: none; }
63
- summary.section-heading { cursor: pointer; padding: 4px 0; }
64
- .section-heading .icon { margin-left: auto; width: 13px; height: 13px; }
65
- details[open] > summary .icon { transform: rotate(90deg); }
66
- .row { display: flex; align-items: flex-start; gap: 10px; padding: 15px 0; border-bottom: 1px solid var(--line); }
67
- .row:last-child { border-bottom: none; }
68
- .row-content { flex: 1; min-width: 0; }
69
- .row-label { font-size: 16px; font-weight: 500; line-height: 1.5; overflow-wrap: anywhere; }
70
- .row-value { font-size: 16px; font-weight: 400; line-height: 1.6; margin-top: 4px; color: #263243; white-space: pre-wrap; overflow-wrap: anywhere; }
71
- .row-caption { font-size: 13px; color: #87909D; line-height: 1.6; margin-top: 6px; white-space: pre-wrap; overflow-wrap: anywhere; }
72
- .status { display: inline-block; font-size: 12px; line-height: 1.5; color: #8893A2; margin-top: 8px; }
73
- .status.pending { color: #956B40; }
74
- .status.changed { color: var(--accent); }
75
- .detail-open { min-height: 38px; width: 26px; background: transparent; color: #9BA5B3; display: grid; place-items: center; padding: 0; flex: none; border-radius: 5px; }
76
- .detail-open .icon { width: 15px; height: 15px; }
77
- .timeline .row { position: relative; border: 0; gap: 21px; padding: 9px 0 23px; min-height: 82px; }
78
- .timeline .time { flex: none; width: 36px; color: #738196; font-size: 12px; font-variant-numeric: tabular-nums; line-height: 1.6; padding-top: 3px; overflow-wrap: anywhere; }
79
- .timeline .row:before { position: absolute; content: ""; left: 45px; top: 19px; bottom: -11px; width: 1px; background: #E2EAF6; }
80
- .timeline .row:last-child:before { display: none; }
81
- .timeline .row:after { position: absolute; content: ""; left: 43px; top: 17px; width: 5px; height: 5px; border-radius: 50%; background: var(--accent); }
82
- .timeline .row-value { margin-top: 0; }
83
- .cards .row { padding: 16px 0 18px; }
84
- .cards .row-value { color: #68758A; }
85
- .ledger .row-content { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 12px; align-items: baseline; }
86
- .ledger .row-label { font-size: 16px; font-weight: 400; }
87
- .ledger .row-value { margin: 0; text-align: right; font-variant-numeric: tabular-nums; }
88
- .ledger .row-caption, .ledger .status { grid-column: 1 / -1; }
89
- .checklist .row-value { color: #718098; }
90
- .note { border-top: 1px solid var(--line); padding-top: 14px; color: #A1A8B2; font-size: 11px; }
91
- .detail-layer { position: absolute; inset: 0; z-index: 3; background: #17243B45; display: grid; place-items: center; }
92
- .detail-box { width: calc(100% - 32px); max-height: 65%; overflow-y: auto; padding: 20px; border-radius: 22px; background: #fff; box-shadow: 0 12px 36px #192B4720; }
93
- .detail-head { display: flex; align-items: center; gap: 10px; margin-bottom: 18px; }
94
- .detail-head h3 { flex: 1; font-size: 16px; }
95
- .detail-text { font-size: 16px; line-height: 1.75; white-space: pre-wrap; overflow-wrap: anywhere; }
96
- .detail-foot { color: var(--muted); font-size: 11px; border-top: 1px solid var(--line); margin-top: 20px; padding-top: 14px; }
97
- @media (prefers-reduced-motion: no-preference) {
98
- button { transition: color .15s, background .15s; }
99
- button:active { opacity: .7; }
100
- .sheet, .detail-box { animation: enter .18s ease-out both; }
101
- @keyframes enter { from { transform: translateY(6px); opacity: .5; } to { transform: none; opacity: 1; } }
102
- }
103
- .svg-export * { animation: none !important; transition: none !important; }
@@ -1,66 +0,0 @@
1
- "use strict";
2
- const params = new URLSearchParams(location.search);
3
- const requested = params.get('state') || 'initial';
4
- const screens = [...document.querySelectorAll('.screen')];
5
- const screen = screens.find(node => node.dataset.state === requested) || screens[0];
6
- for (const node of screens) node.hidden = node !== screen;
7
- const overlay = screen.querySelector('.overlay');
8
- const details = screen.querySelector('.detail-layer');
9
- const sheet = screen.querySelector('.sheet');
10
- const open = screen.querySelector('.open-sheet');
11
- const close = screen.querySelector('.sheet-close');
12
- const detailClose = screen.querySelector('.detail-close');
13
- let returnFocus = null;
14
- function closeDetail() {
15
- details.hidden = true;
16
- sheet.inert = false;
17
- if (returnFocus) returnFocus.focus();
18
- }
19
- function closeSheet() {
20
- closeDetail();
21
- overlay.hidden = true;
22
- screen.querySelector('.chat-body').inert = false;
23
- open.focus();
24
- }
25
- open.addEventListener('click', event => {
26
- overlay.hidden = false;
27
- screen.querySelector('.chat-body').inert = true;
28
- (event.detail === 0 ? close : sheet).focus({preventScroll:true});
29
- });
30
- close.addEventListener('click', closeSheet);
31
- detailClose.addEventListener('click', closeDetail);
32
- for (const button of screen.querySelectorAll('[data-group]')) {
33
- button.addEventListener('click', () => {
34
- const selected = button.dataset.group;
35
- for (const option of screen.querySelectorAll('[data-group]')) option.setAttribute('aria-pressed', String(option.dataset.group === selected));
36
- for (const section of screen.querySelectorAll('[data-section]')) section.hidden = selected !== 'all' && section.dataset.section !== selected;
37
- });
38
- }
39
- for (const button of screen.querySelectorAll('.detail-open')) {
40
- button.addEventListener('click', () => {
41
- returnFocus = button;
42
- const row = button.closest('.row');
43
- screen.querySelector('.detail-title').textContent = row.querySelector('.row-label, .row-value').textContent;
44
- screen.querySelector('.detail-text').textContent = row.querySelector('.detail-copy').textContent;
45
- details.hidden = false;
46
- sheet.inert = true;
47
- detailClose.focus();
48
- });
49
- }
50
- document.addEventListener('keydown', event => {
51
- if (event.key === 'Escape') {
52
- if (!details.hidden) closeDetail();
53
- else if (!overlay.hidden) closeSheet();
54
- }
55
- if (event.key === 'Tab' && !overlay.hidden) {
56
- const scope = details.hidden ? screen.querySelector('.sheet') : screen.querySelector('.detail-box');
57
- const buttons = [...scope.querySelectorAll('button,summary')].filter(el => el.getClientRects().length);
58
- const first = buttons[0], last = buttons[buttons.length - 1];
59
- if (event.shiftKey && (document.activeElement === first || document.activeElement === sheet)) {event.preventDefault();last.focus();}
60
- else if (!event.shiftKey && document.activeElement === last) {event.preventDefault();first.focus();}
61
- }
62
- });
63
- overlay.hidden = params.get('view') !== 'expanded';
64
- screen.querySelector('.chat-body').inert = !overlay.hidden;
65
- // Embedded examples must not steal the Booklet's focus or reading position.
66
- if (!overlay.hidden && window === window.top) sheet.focus({preventScroll:true});