clearai-dsh 0.1.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 (154) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/LICENSE +201 -0
  3. package/README.md +138 -0
  4. package/README.zh-CN.md +138 -0
  5. package/bin/clearai.mjs +224 -0
  6. package/brand/README.md +41 -0
  7. package/brand/logo-512-dark.png +0 -0
  8. package/brand/logo-512.png +0 -0
  9. package/brand/logo-lockup-dark.png +0 -0
  10. package/brand/logo-lockup.png +0 -0
  11. package/brand/logo-lockup.svg +12 -0
  12. package/brand/logo-wordmark.svg +6 -0
  13. package/brand/logo.svg +19 -0
  14. package/cordis.patch.yml +39 -0
  15. package/lib/client.js +3071 -0
  16. package/lib/fold.js +1576 -0
  17. package/lib/host.js +605 -0
  18. package/package.json +65 -0
  19. package/presets/clearai/agent.cordis.yml +226 -0
  20. package/presets/clearai/plugins/brain.js +547 -0
  21. package/presets/clearai/plugins/clearai-kernel.js +5485 -0
  22. package/presets/clearai/plugins/ontology.js +306 -0
  23. package/presets/clearai/plugins/prompts.js +312 -0
  24. package/presets/clearai/preset.yml +5 -0
  25. package/presets/clearai/skills/clearai-loop/SKILL.md +89 -0
  26. package/presets/clearai/template/knowledge/README.md +25 -0
  27. package/presets/clearai/template/memory/README.md +34 -0
  28. package/presets/clearai/template/project.md +49 -0
  29. package/presets/clearai/template/skills/README.md +37 -0
  30. package/presets/clearai/template/skills/chart-diagram-qa/SKILL.md +43 -0
  31. package/presets/clearai/template/skills/citation-management/SKILL.md +73 -0
  32. package/presets/clearai/template/skills/citation-management/references/bibtex_formatting.md +908 -0
  33. package/presets/clearai/template/skills/citation-management/references/citation_validation.md +794 -0
  34. package/presets/clearai/template/skills/citation-management/references/google_scholar_search.md +725 -0
  35. package/presets/clearai/template/skills/citation-management/references/metadata_extraction.md +870 -0
  36. package/presets/clearai/template/skills/citation-management/references/pubmed_search.md +839 -0
  37. package/presets/clearai/template/skills/citation-management/scripts/doi_to_bibtex.py +204 -0
  38. package/presets/clearai/template/skills/citation-management/scripts/extract_metadata.py +569 -0
  39. package/presets/clearai/template/skills/citation-management/scripts/format_bibtex.py +349 -0
  40. package/presets/clearai/template/skills/citation-management/scripts/generate_schematic.py +139 -0
  41. package/presets/clearai/template/skills/citation-management/scripts/generate_schematic_ai.py +817 -0
  42. package/presets/clearai/template/skills/citation-management/scripts/search_google_scholar.py +282 -0
  43. package/presets/clearai/template/skills/citation-management/scripts/search_pubmed.py +398 -0
  44. package/presets/clearai/template/skills/citation-management/scripts/validate_citations.py +497 -0
  45. package/presets/clearai/template/skills/data-analysis/SKILL.md +92 -0
  46. package/presets/clearai/template/skills/data-analysis/checklists/readiness_check.md +23 -0
  47. package/presets/clearai/template/skills/data-analysis/templates/analysis_report.md.tpl +63 -0
  48. package/presets/clearai/template/skills/data-analysis/templates/cleaning_rules_draft.yaml.tpl +32 -0
  49. package/presets/clearai/template/skills/data-analysis/templates/data_dictionary.md.tpl +12 -0
  50. package/presets/clearai/template/skills/data-analysis/templates/domain_knowledge_template.md.tpl +316 -0
  51. package/presets/clearai/template/skills/data-analysis/templates/feature_candidates.json.tpl +20 -0
  52. package/presets/clearai/template/skills/data-analysis/templates/quality_scorecard.md.tpl +30 -0
  53. package/presets/clearai/template/skills/data-analysis/workflows/01-data-profiling.md +42 -0
  54. package/presets/clearai/template/skills/data-analysis/workflows/02-quality-audit.md +36 -0
  55. package/presets/clearai/template/skills/data-analysis/workflows/03-physical-correlation.md +25 -0
  56. package/presets/clearai/template/skills/data-analysis/workflows/04-unstructured-mining.md +26 -0
  57. package/presets/clearai/template/skills/data-qa-analysis/SKILL.md +102 -0
  58. package/presets/clearai/template/skills/data-qa-analysis/checklists/readiness_check.md +62 -0
  59. package/presets/clearai/template/skills/data-qa-analysis/templates/best_in_class_report.md.tpl +56 -0
  60. package/presets/clearai/template/skills/data-qa-analysis/templates/cleaning_rules_draft.yaml.tpl +56 -0
  61. package/presets/clearai/template/skills/data-qa-analysis/templates/data_dictionary.md.tpl +13 -0
  62. package/presets/clearai/template/skills/data-qa-analysis/templates/data_source_inventory_and_lineage.md.tpl +146 -0
  63. package/presets/clearai/template/skills/data-qa-analysis/templates/data_status_report.md.tpl +60 -0
  64. package/presets/clearai/template/skills/data-qa-analysis/templates/steady_state_rules.yaml.tpl +41 -0
  65. package/presets/clearai/template/skills/data-qa-analysis/templates/subsystem_registry.md.tpl +101 -0
  66. package/presets/clearai/template/skills/data-qa-analysis/templates/unified_execution_plan.md.tpl +100 -0
  67. package/presets/clearai/template/skills/data-qa-analysis/workflows/01-data-source-inventory-and-lineage.md +194 -0
  68. package/presets/clearai/template/skills/data-qa-analysis/workflows/02-data-alignment-and-tag-semantics.md +122 -0
  69. package/presets/clearai/template/skills/data-qa-analysis/workflows/03-steady-state-identification.md +126 -0
  70. package/presets/clearai/template/skills/data-qa-analysis/workflows/04-consumption-analysis.md +152 -0
  71. package/presets/clearai/template/skills/data-qa-analysis/workflows/05-best-in-class-and-optimization-space.md +78 -0
  72. package/presets/clearai/template/skills/domain-presearch/SKILL.md +131 -0
  73. package/presets/clearai/template/skills/domain-presearch/checklists/domain_checklist.md +24 -0
  74. package/presets/clearai/template/skills/domain-presearch/references/figure_code.md +78 -0
  75. package/presets/clearai/template/skills/domain-presearch/references/strategic_frameworks.md +38 -0
  76. package/presets/clearai/template/skills/exploration-loop/SKILL.md +81 -0
  77. package/presets/clearai/template/skills/exploratory-data-analysis/SKILL.md +77 -0
  78. package/presets/clearai/template/skills/exploratory-data-analysis/references/bioinformatics_genomics_formats.md +664 -0
  79. package/presets/clearai/template/skills/exploratory-data-analysis/references/chemistry_molecular_formats.md +664 -0
  80. package/presets/clearai/template/skills/exploratory-data-analysis/references/general_scientific_formats.md +518 -0
  81. package/presets/clearai/template/skills/exploratory-data-analysis/references/microscopy_imaging_formats.md +620 -0
  82. package/presets/clearai/template/skills/exploratory-data-analysis/references/proteomics_metabolomics_formats.md +517 -0
  83. package/presets/clearai/template/skills/exploratory-data-analysis/references/spectroscopy_analytical_formats.md +633 -0
  84. package/presets/clearai/template/skills/exploratory-data-analysis/scripts/eda_analyzer.py +547 -0
  85. package/presets/clearai/template/skills/hypothesis-generation/SKILL.md +73 -0
  86. package/presets/clearai/template/skills/hypothesis-generation/references/experimental_design_patterns.md +329 -0
  87. package/presets/clearai/template/skills/hypothesis-generation/references/hypothesis_quality_criteria.md +198 -0
  88. package/presets/clearai/template/skills/hypothesis-generation/references/literature_search_strategies.md +622 -0
  89. package/presets/clearai/template/skills/hypothesis-generation/scripts/generate_schematic.py +139 -0
  90. package/presets/clearai/template/skills/hypothesis-generation/scripts/generate_schematic_ai.py +817 -0
  91. package/presets/clearai/template/skills/literature-review/SKILL.md +72 -0
  92. package/presets/clearai/template/skills/literature-review/references/citation_styles.md +166 -0
  93. package/presets/clearai/template/skills/literature-review/references/database_strategies.md +455 -0
  94. package/presets/clearai/template/skills/literature-review/scripts/generate_pdf.py +176 -0
  95. package/presets/clearai/template/skills/literature-review/scripts/generate_schematic.py +139 -0
  96. package/presets/clearai/template/skills/literature-review/scripts/generate_schematic_ai.py +817 -0
  97. package/presets/clearai/template/skills/literature-review/scripts/search_databases.py +303 -0
  98. package/presets/clearai/template/skills/literature-review/scripts/verify_citations.py +221 -0
  99. package/presets/clearai/template/skills/paper-lookup/SKILL.md +59 -0
  100. package/presets/clearai/template/skills/paper-lookup/references/arxiv.md +161 -0
  101. package/presets/clearai/template/skills/paper-lookup/references/biorxiv.md +118 -0
  102. package/presets/clearai/template/skills/paper-lookup/references/core.md +150 -0
  103. package/presets/clearai/template/skills/paper-lookup/references/crossref.md +181 -0
  104. package/presets/clearai/template/skills/paper-lookup/references/medrxiv.md +104 -0
  105. package/presets/clearai/template/skills/paper-lookup/references/openalex.md +174 -0
  106. package/presets/clearai/template/skills/paper-lookup/references/pmc.md +152 -0
  107. package/presets/clearai/template/skills/paper-lookup/references/pubmed.md +124 -0
  108. package/presets/clearai/template/skills/paper-lookup/references/semantic-scholar.md +203 -0
  109. package/presets/clearai/template/skills/paper-lookup/references/unpaywall.md +127 -0
  110. package/presets/clearai/template/skills/process-presearch/SKILL.md +196 -0
  111. package/presets/clearai/template/skills/process-presearch/checklists/process_checklist.md +18 -0
  112. package/presets/clearai/template/skills/process-presearch/references/figure_code.md +107 -0
  113. package/presets/clearai/template/skills/process-presearch/references/source_attribution_example.md +22 -0
  114. package/presets/clearai/template/skills/process-understanding-extraction/SKILL.md +69 -0
  115. package/presets/clearai/template/skills/process-understanding-extraction/checklists/readiness_check.md +34 -0
  116. package/presets/clearai/template/skills/process-understanding-extraction/templates/docx_raw_dump_extractor.py.tpl +132 -0
  117. package/presets/clearai/template/skills/process-understanding-extraction/templates/entity_map_unit_topology.json.tpl +86 -0
  118. package/presets/clearai/template/skills/process-understanding-extraction/templates/process_brief.md.tpl +89 -0
  119. package/presets/clearai/template/skills/process-understanding-extraction/templates/process_brief_builder_from_raw_dump.py.tpl +203 -0
  120. package/presets/clearai/template/skills/process-understanding-extraction/templates/process_flow_mermaid.md.tpl +41 -0
  121. package/presets/clearai/template/skills/process-understanding-extraction/templates/unified_execution_plan.md.tpl +53 -0
  122. package/presets/clearai/template/skills/process-understanding-extraction/workflows/01-process-doc-discovery.md +173 -0
  123. package/presets/clearai/template/skills/process-understanding-extraction/workflows/02-process-understanding-and-diagramming.md +106 -0
  124. package/presets/clearai/template/skills/scientific-brainstorming/SKILL.md +64 -0
  125. package/presets/clearai/template/skills/scientific-brainstorming/references/brainstorming_methods.md +326 -0
  126. package/presets/clearai/template/skills/scientific-critical-thinking/SKILL.md +72 -0
  127. package/presets/clearai/template/skills/scientific-critical-thinking/references/common_biases.md +364 -0
  128. package/presets/clearai/template/skills/scientific-critical-thinking/references/evidence_hierarchy.md +485 -0
  129. package/presets/clearai/template/skills/scientific-critical-thinking/references/experimental_design.md +496 -0
  130. package/presets/clearai/template/skills/scientific-critical-thinking/references/logical_fallacies.md +478 -0
  131. package/presets/clearai/template/skills/scientific-critical-thinking/references/scientific_method.md +169 -0
  132. package/presets/clearai/template/skills/scientific-critical-thinking/references/statistical_pitfalls.md +506 -0
  133. package/presets/clearai/template/skills/skill-creator/SKILL.md +109 -0
  134. package/presets/clearai/template/skills/skill-creator/references/authoring-guide.md +89 -0
  135. package/presets/clearai/template/skills/statistical-analysis/SKILL.md +79 -0
  136. package/presets/clearai/template/skills/statistical-analysis/references/assumptions_and_diagnostics.md +369 -0
  137. package/presets/clearai/template/skills/statistical-analysis/references/bayesian_statistics.md +653 -0
  138. package/presets/clearai/template/skills/statistical-analysis/references/effect_sizes_and_power.md +578 -0
  139. package/presets/clearai/template/skills/statistical-analysis/references/reporting_standards.md +469 -0
  140. package/presets/clearai/template/skills/statistical-analysis/references/test_selection_guide.md +129 -0
  141. package/presets/clearai/template/skills/statistical-analysis/scripts/assumption_checks.py +538 -0
  142. package/presets/clearai/template/skills/web-artifact/SKILL.md +165 -0
  143. package/presets/clearai/template/skills/web-artifact/assets/renderer/renderer.css +229 -0
  144. package/presets/clearai/template/skills/web-artifact/assets/renderer/renderer.js +373 -0
  145. package/presets/clearai/template/skills/web-artifact/assets/vendor/elkjs/LICENSE +263 -0
  146. package/presets/clearai/template/skills/web-artifact/assets/vendor/elkjs/UPSTREAM.md +26 -0
  147. package/presets/clearai/template/skills/web-artifact/assets/vendor/elkjs/elk.bundled.js +6605 -0
  148. package/presets/clearai/template/skills/web-artifact/references/when-drawing-a-topology.md +150 -0
  149. package/presets/clearai/template/skills/web-artifact/references/when-the-page-must-work-offline.md +62 -0
  150. package/presets/clearai/template/skills/web-artifact/scripts/check_artifact.py +167 -0
  151. package/presets/clearai/template/skills/web-artifact/scripts/render_topology.js +272 -0
  152. package/presets/clearai/template/skills/what-if-oracle/LICENSE.txt +5 -0
  153. package/presets/clearai/template/skills/what-if-oracle/SKILL.md +72 -0
  154. package/presets/clearai/template/skills/what-if-oracle/references/scenario-templates.md +154 -0
@@ -0,0 +1,229 @@
1
+ /* 单文件网页制品渲染器 · 零依赖
2
+ 调性取自工程蓝图与控制室仪表盘:冷中性底、信号橙作唯一强调色。
3
+ 字体只用系统栈——运行时零网络请求意味着连 Google Fonts 也不能引。 */
4
+
5
+ :root {
6
+ --bg: #f4f6f8;
7
+ --surface: #ffffff;
8
+ --surface-2: #eef1f5;
9
+ --ink: #171a1f;
10
+ --ink-dim: #5a6473;
11
+ --ink-faint: #8a93a1;
12
+ --line: #d3d9e0;
13
+ --line-soft: #e4e8ee;
14
+ --accent: #b4530a;
15
+ --accent-soft: #f5e3d4;
16
+ --r-main: #2c3440;
17
+ --r-branch: #6b7686;
18
+ --r-seed: #2f6b46;
19
+ --r-recycle: #2b5c8a;
20
+ --r-utility: #94a0b0;
21
+ --r-unknown: #a83232;
22
+ --shadow: 0 1px 2px rgba(16, 20, 26, .06), 0 8px 24px rgba(16, 20, 26, .07);
23
+ --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
24
+ --sans: system-ui, -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
25
+ }
26
+
27
+ @media (prefers-color-scheme: dark) {
28
+ :root:not([data-theme="light"]) {
29
+ --bg: #0e1116;
30
+ --surface: #161a21;
31
+ --surface-2: #1d222a;
32
+ --ink: #e4e8ee;
33
+ --ink-dim: #98a2b0;
34
+ --ink-faint: #6b7684;
35
+ --line: #2a313b;
36
+ --line-soft: #212730;
37
+ --accent: #ff9d4d;
38
+ --accent-soft: #3a2a18;
39
+ --r-main: #c3ccd8;
40
+ --r-branch: #7d8896;
41
+ --r-seed: #5aab7c;
42
+ --r-recycle: #5f9ed1;
43
+ --r-utility: #5c6673;
44
+ --r-unknown: #e0716b;
45
+ --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 8px 24px rgba(0, 0, 0, .35);
46
+ }
47
+ }
48
+
49
+ :root[data-theme="dark"] {
50
+ --bg: #0e1116;
51
+ --surface: #161a21;
52
+ --surface-2: #1d222a;
53
+ --ink: #e4e8ee;
54
+ --ink-dim: #98a2b0;
55
+ --ink-faint: #6b7684;
56
+ --line: #2a313b;
57
+ --line-soft: #212730;
58
+ --accent: #ff9d4d;
59
+ --accent-soft: #3a2a18;
60
+ --r-main: #c3ccd8;
61
+ --r-branch: #7d8896;
62
+ --r-seed: #5aab7c;
63
+ --r-recycle: #5f9ed1;
64
+ --r-utility: #5c6673;
65
+ --r-unknown: #e0716b;
66
+ --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 8px 24px rgba(0, 0, 0, .35);
67
+ }
68
+
69
+ * { box-sizing: border-box; }
70
+
71
+ body {
72
+ margin: 0;
73
+ background: var(--bg);
74
+ color: var(--ink);
75
+ font: 14px/1.55 var(--sans);
76
+ -webkit-font-smoothing: antialiased;
77
+ display: flex;
78
+ flex-direction: column;
79
+ height: 100vh;
80
+ overflow: hidden;
81
+ }
82
+
83
+ /* ── 顶栏:标题 + 入口区 + 工具 ── */
84
+ header {
85
+ flex: none;
86
+ display: flex;
87
+ align-items: center;
88
+ gap: 18px;
89
+ padding: 12px 18px;
90
+ background: var(--surface);
91
+ border-bottom: 1px solid var(--line);
92
+ }
93
+ header h1 { margin: 0; font-size: 15px; font-weight: 600; letter-spacing: .01em; }
94
+ header .counts { color: var(--ink-dim); font: 12px/1 var(--mono); }
95
+ header .spacer { flex: 1; }
96
+
97
+ .chip {
98
+ border: 1px solid var(--line);
99
+ background: var(--surface-2);
100
+ color: var(--ink-dim);
101
+ border-radius: 6px;
102
+ padding: 5px 10px;
103
+ font: 12px/1.2 var(--sans);
104
+ cursor: pointer;
105
+ transition: .12s;
106
+ }
107
+ .chip:hover { border-color: var(--ink-faint); color: var(--ink); }
108
+ .chip[aria-pressed="true"] { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
109
+ .chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
110
+
111
+ /* 入口区:让读者知道从哪开始看。没有可报的内容时整块不渲染。 */
112
+ #entry {
113
+ flex: none;
114
+ display: flex;
115
+ flex-wrap: wrap;
116
+ gap: 8px;
117
+ align-items: center;
118
+ padding: 10px 18px;
119
+ background: var(--accent-soft);
120
+ border-bottom: 1px solid var(--line);
121
+ font-size: 13px;
122
+ }
123
+ #entry b { font-weight: 600; }
124
+ #entry .lead { color: var(--ink-dim); margin-right: 4px; }
125
+
126
+ main { flex: 1; display: flex; min-height: 0; }
127
+
128
+ /* ── 画布 ── */
129
+ #stage { flex: 1; position: relative; min-width: 0; overflow: hidden; }
130
+ #canvas { width: 100%; height: 100%; display: block; cursor: grab; touch-action: none; }
131
+ #canvas.dragging { cursor: grabbing; }
132
+
133
+ .node rect {
134
+ fill: var(--surface);
135
+ stroke: var(--line);
136
+ stroke-width: 1.2;
137
+ rx: 5;
138
+ transition: stroke .12s, fill .12s;
139
+ }
140
+ .node text { fill: var(--ink); font: 12px var(--sans); pointer-events: none; }
141
+ .node .sub { fill: var(--ink-faint); font-size: 10.5px; }
142
+ .node { cursor: pointer; }
143
+ .node:hover rect { stroke: var(--ink-faint); }
144
+ .node.selected rect { stroke: var(--accent); stroke-width: 2.2; fill: var(--accent-soft); }
145
+ .node.dimmed { opacity: .22; }
146
+
147
+ .edge path { fill: none; stroke: var(--r-branch); stroke-width: 1.6; transition: opacity .12s; }
148
+ .edge.role-main path { stroke: var(--r-main); stroke-width: 2.1; }
149
+ .edge.role-branch path { stroke: var(--r-branch); }
150
+ .edge.role-seed path { stroke: var(--r-seed); }
151
+ .edge.role-recycle path { stroke: var(--r-recycle); stroke-dasharray: 7 4; }
152
+ .edge.role-utility path { stroke: var(--r-utility); stroke-dasharray: 3 4; }
153
+ .edge.role-unknown path { stroke: var(--r-unknown); stroke-dasharray: 2 3; }
154
+ .edge text {
155
+ font: 10.5px var(--sans);
156
+ fill: var(--ink-dim);
157
+ paint-order: stroke;
158
+ stroke: var(--bg);
159
+ stroke-width: 3.5;
160
+ stroke-linejoin: round;
161
+ }
162
+ .edge { cursor: pointer; }
163
+ .edge.selected path { stroke: var(--accent); stroke-width: 3; }
164
+ .edge.dimmed { opacity: .12; }
165
+ .edge.hidden, .node.hidden { display: none; }
166
+
167
+ /* ── 侧栏:透传任意属性 ── */
168
+ #side {
169
+ flex: none;
170
+ width: 300px;
171
+ background: var(--surface);
172
+ border-left: 1px solid var(--line);
173
+ overflow-y: auto;
174
+ padding: 16px;
175
+ }
176
+ #side.empty { color: var(--ink-faint); font-size: 13px; }
177
+ #side h2 { margin: 0 0 2px; font-size: 15px; font-weight: 600; }
178
+ #side .kind { color: var(--ink-faint); font: 11px/1.4 var(--mono); margin-bottom: 14px; }
179
+ #side dl { margin: 0 0 16px; display: grid; grid-template-columns: auto 1fr; gap: 6px 12px; font-size: 12.5px; }
180
+ #side dt { color: var(--ink-dim); }
181
+ #side dd { margin: 0; word-break: break-word; }
182
+ #side dd.mono { font-family: var(--mono); font-size: 11.5px; }
183
+ #side textarea {
184
+ width: 100%; min-height: 74px; resize: vertical;
185
+ background: var(--surface-2); color: var(--ink);
186
+ border: 1px solid var(--line); border-radius: 6px;
187
+ padding: 8px; font: 13px/1.5 var(--sans);
188
+ }
189
+ #side textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
190
+ #side .note-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 6px; }
191
+ #side .note-head span { font-size: 12px; color: var(--ink-dim); }
192
+
193
+ .legend { display: flex; flex-wrap: wrap; gap: 6px 14px; padding: 8px 18px; border-top: 1px solid var(--line-soft); background: var(--surface); }
194
+ .legend i { display: inline-block; width: 18px; height: 0; border-top-width: 2px; border-top-style: solid; vertical-align: middle; margin-right: 6px; }
195
+ .legend label { font-size: 11.5px; color: var(--ink-dim); cursor: pointer; user-select: none; }
196
+ .legend label.off { opacity: .4; text-decoration: line-through; }
197
+
198
+ /* ── minimap ── */
199
+ #minimap {
200
+ position: absolute; right: 14px; bottom: 14px;
201
+ width: 168px; height: 116px;
202
+ background: var(--surface); border: 1px solid var(--line);
203
+ border-radius: 7px; box-shadow: var(--shadow); overflow: hidden;
204
+ }
205
+ #minimap svg { width: 100%; height: 100%; display: block; }
206
+ #minimap .mm-node { fill: var(--ink-faint); }
207
+ #minimap .mm-view { fill: var(--accent); fill-opacity: .13; stroke: var(--accent); stroke-width: 1.5; }
208
+
209
+ /* ── 页脚:自陈来历 ── */
210
+ footer {
211
+ flex: none;
212
+ padding: 9px 18px;
213
+ background: var(--surface-2);
214
+ border-top: 1px solid var(--line);
215
+ color: var(--ink-faint);
216
+ font: 11px/1.7 var(--mono);
217
+ max-height: 30vh;
218
+ overflow-y: auto;
219
+ }
220
+ footer b { color: var(--ink-dim); font-weight: 500; }
221
+ footer .claims { margin-top: 4px; font-family: var(--sans); font-size: 11.5px; }
222
+
223
+ @media (max-width: 820px) {
224
+ #side { display: none; }
225
+ #side.open { display: block; position: absolute; right: 0; top: 0; bottom: 0; z-index: 5; box-shadow: var(--shadow); }
226
+ }
227
+ @media (prefers-reduced-motion: reduce) {
228
+ * { transition: none !important; }
229
+ }
@@ -0,0 +1,373 @@
1
+ /* 单文件网页制品渲染器 · 零依赖零外部请求
2
+ *
3
+ * 输入:window.__ARTIFACT__ —— 由生成器写进 HTML 的一段 JSON。
4
+ * 数据无关:节点与边上除渲染必需字段(id/x/y/width/height/points)之外的一切属性,
5
+ * 原样透传到侧栏。渲染器不规定数据里必须有什么。
6
+ */
7
+ (function () {
8
+ 'use strict';
9
+
10
+ var A = window.__ARTIFACT__ || {};
11
+ var G = A.graph || { nodes: [], edges: [] };
12
+ var NODES = G.nodes || [], EDGES = G.edges || [];
13
+ var SVGNS = 'http://www.w3.org/2000/svg';
14
+ var RESERVED = { id: 1, x: 1, y: 1, width: 1, height: 1, points: 1, source: 1, target: 1, labelX: 1, labelY: 1 };
15
+ var ROLES = ['main', 'branch', 'seed', 'recycle', 'utility', 'unknown'];
16
+ var ROLE_CN = { main: '主链', branch: '支线', seed: '晶种', recycle: '返回', utility: '公用', unknown: '存疑' };
17
+
18
+ var byId = {};
19
+ NODES.forEach(function (n) { byId[n.id] = n; });
20
+
21
+ var el = function (id) { return document.getElementById(id); };
22
+ var mk = function (tag, attrs) {
23
+ var e = document.createElementNS(SVGNS, tag);
24
+ for (var k in attrs) if (attrs[k] != null) e.setAttribute(k, attrs[k]);
25
+ return e;
26
+ };
27
+
28
+ /* ── 视图状态:四个,各自的影响都是局部的 ── */
29
+ var view = { x: 0, y: 0, k: 1 };
30
+ var selected = null;
31
+ var hiddenRoles = {};
32
+ var notes = {};
33
+ (A.annotations || []).forEach(function (a) { if (a && a.id) notes[a.id] = a.note; });
34
+
35
+ /* ── 建图 ── */
36
+ var svg = el('canvas');
37
+ var root = mk('g');
38
+ svg.appendChild(root);
39
+
40
+ var bb = { x0: Infinity, y0: Infinity, x1: -Infinity, y1: -Infinity };
41
+ function grow(x, y) {
42
+ if (x < bb.x0) bb.x0 = x; if (y < bb.y0) bb.y0 = y;
43
+ if (x > bb.x1) bb.x1 = x; if (y > bb.y1) bb.y1 = y;
44
+ }
45
+
46
+ var gEdges = mk('g'), gNodes = mk('g');
47
+ root.appendChild(gEdges); root.appendChild(gNodes);
48
+
49
+ EDGES.forEach(function (e) {
50
+ var pts = e.points || [];
51
+ if (pts.length < 2) return;
52
+ var cls = 'edge' + (ROLES.indexOf(e.role) >= 0 ? ' role-' + e.role : '');
53
+ var g = mk('g', { 'class': cls, 'data-id': e.id });
54
+ var d = 'M' + pts.map(function (p) { return p[0] + ' ' + p[1]; }).join(' L');
55
+ pts.forEach(function (p) { grow(p[0], p[1]); });
56
+ g.appendChild(mk('path', { d: d, 'marker-end': 'url(#arrow)' }));
57
+ if (e.label) {
58
+ var t = mk('text', { x: e.labelX != null ? e.labelX : pts[pts.length >> 1][0],
59
+ y: e.labelY != null ? e.labelY : pts[pts.length >> 1][1],
60
+ 'text-anchor': 'middle' });
61
+ t.textContent = e.label;
62
+ g.appendChild(t);
63
+ }
64
+ gEdges.appendChild(g);
65
+ });
66
+
67
+ NODES.forEach(function (n) {
68
+ var w = n.width || 120, h = n.height || 40;
69
+ grow(n.x, n.y); grow(n.x + w, n.y + h);
70
+ var g = mk('g', { 'class': 'node', 'data-id': n.id, tabindex: '0', role: 'button' });
71
+ g.appendChild(mk('rect', { x: n.x, y: n.y, width: w, height: h }));
72
+ var hasSub = !!n.sub;
73
+ var t = mk('text', { x: n.x + w / 2, y: n.y + h / 2 + (hasSub ? -3 : 4), 'text-anchor': 'middle' });
74
+ t.textContent = n.label || n.id;
75
+ g.appendChild(t);
76
+ if (hasSub) {
77
+ var s = mk('text', { x: n.x + w / 2, y: n.y + h / 2 + 12, 'text-anchor': 'middle', 'class': 'sub' });
78
+ s.textContent = n.sub;
79
+ g.appendChild(s);
80
+ }
81
+ g.setAttribute('aria-label', (n.label || n.id) + (hasSub ? ',' + n.sub : ''));
82
+ gNodes.appendChild(g);
83
+ });
84
+
85
+ var defs = mk('defs');
86
+ var marker = mk('marker', { id: 'arrow', viewBox: '0 0 10 10', refX: '9', refY: '5',
87
+ markerWidth: '6', markerHeight: '6', orient: 'auto-start-reverse' });
88
+ marker.appendChild(mk('path', { d: 'M0 0 L10 5 L0 10 z', fill: 'context-stroke' }));
89
+ defs.appendChild(marker);
90
+ svg.insertBefore(defs, root);
91
+
92
+ if (!isFinite(bb.x0)) bb = { x0: 0, y0: 0, x1: 100, y1: 100 };
93
+
94
+ /* ── 视图变换 ── */
95
+ function apply() {
96
+ root.setAttribute('transform', 'translate(' + view.x + ' ' + view.y + ') scale(' + view.k + ')');
97
+ drawViewport();
98
+ }
99
+ // 首次取景必须等到 SVG 真有尺寸。脚本在首次布局完成前执行时
100
+ // getBoundingClientRect() 返回 0×0,算出来的位移是 -(x0+x1)/2,
101
+ // 表现为「打开就偏到画布外」。
102
+ function fit() {
103
+ var r = svg.getBoundingClientRect();
104
+ if (!r.width || !r.height) return false;
105
+ var w = bb.x1 - bb.x0 + 80, h = bb.y1 - bb.y0 + 80;
106
+ view.k = Math.min(r.width / w, r.height / h, 1.6) || 1;
107
+ view.x = (r.width - (bb.x0 + bb.x1) * view.k) / 2;
108
+ view.y = (r.height - (bb.y0 + bb.y1) * view.k) / 2;
109
+ apply();
110
+ return true;
111
+ }
112
+
113
+ var drag = null;
114
+ svg.addEventListener('pointerdown', function (ev) {
115
+ if (ev.button !== 0) return;
116
+ drag = { x: ev.clientX, y: ev.clientY, vx: view.x, vy: view.y, moved: false };
117
+ svg.classList.add('dragging');
118
+ svg.setPointerCapture(ev.pointerId);
119
+ });
120
+ svg.addEventListener('pointermove', function (ev) {
121
+ if (!drag) return;
122
+ var dx = ev.clientX - drag.x, dy = ev.clientY - drag.y;
123
+ if (Math.abs(dx) + Math.abs(dy) > 3) drag.moved = true;
124
+ view.x = drag.vx + dx; view.y = drag.vy + dy;
125
+ apply();
126
+ });
127
+ svg.addEventListener('pointerup', function (ev) {
128
+ if (drag && !drag.moved) {
129
+ var hit = ev.target.closest ? ev.target.closest('.node, .edge') : null;
130
+ select(hit ? hit.getAttribute('data-id') : null);
131
+ }
132
+ drag = null; svg.classList.remove('dragging');
133
+ });
134
+ svg.addEventListener('wheel', function (ev) {
135
+ ev.preventDefault();
136
+ var r = svg.getBoundingClientRect();
137
+ var mx = ev.clientX - r.left, my = ev.clientY - r.top;
138
+ var k2 = Math.min(4, Math.max(0.08, view.k * (ev.deltaY < 0 ? 1.12 : 1 / 1.12)));
139
+ view.x = mx - (mx - view.x) * (k2 / view.k);
140
+ view.y = my - (my - view.y) * (k2 / view.k);
141
+ view.k = k2;
142
+ apply();
143
+ }, { passive: false });
144
+ svg.addEventListener('keydown', function (ev) {
145
+ var g = ev.target.closest && ev.target.closest('.node');
146
+ if (g && (ev.key === 'Enter' || ev.key === ' ')) { ev.preventDefault(); select(g.getAttribute('data-id')); }
147
+ });
148
+ document.addEventListener('keydown', function (ev) { if (ev.key === 'Escape') select(null); });
149
+
150
+ /* ── 选中与侧栏:任意属性原样展示 ── */
151
+ function select(id) {
152
+ selected = (selected === id) ? null : id;
153
+ var neighbours = {};
154
+ if (selected) {
155
+ neighbours[selected] = 1;
156
+ EDGES.forEach(function (e) {
157
+ if (e.id === selected) { neighbours[e.source] = 1; neighbours[e.target] = 1; }
158
+ if (e.source === selected || e.target === selected) { neighbours[e.id] = 1; neighbours[e.source] = 1; neighbours[e.target] = 1; }
159
+ });
160
+ }
161
+ [].forEach.call(svg.querySelectorAll('.node, .edge'), function (g) {
162
+ var gid = g.getAttribute('data-id');
163
+ g.classList.toggle('selected', !!selected && gid === selected);
164
+ g.classList.toggle('dimmed', !!selected && !neighbours[gid]);
165
+ });
166
+ renderSide();
167
+ }
168
+
169
+ function renderSide() {
170
+ var side = el('side');
171
+ side.textContent = '';
172
+ if (!selected) {
173
+ side.className = 'empty';
174
+ side.textContent = '点选任意节点或连线查看详情;再点一次取消。';
175
+ return;
176
+ }
177
+ side.className = '';
178
+ var obj = byId[selected] || EDGES.filter(function (e) { return e.id === selected; })[0];
179
+ if (!obj) return;
180
+ var isEdge = !byId[selected];
181
+
182
+ var h = document.createElement('h2');
183
+ h.textContent = obj.label || obj.id;
184
+ side.appendChild(h);
185
+ var kind = document.createElement('div');
186
+ kind.className = 'kind';
187
+ kind.textContent = (isEdge ? '连线 ' : '节点 ') + obj.id;
188
+ side.appendChild(kind);
189
+
190
+ var dl = document.createElement('dl');
191
+ if (isEdge) {
192
+ addRow(dl, '从', (byId[obj.source] || {}).label || obj.source);
193
+ addRow(dl, '到', (byId[obj.target] || {}).label || obj.target);
194
+ }
195
+ Object.keys(obj).forEach(function (k) {
196
+ if (RESERVED[k] || k === 'label' || k === 'sub') return;
197
+ var v = obj[k];
198
+ if (v == null || v === '') return;
199
+ addRow(dl, k === 'role' ? '关系' : k,
200
+ k === 'role' ? (ROLE_CN[v] || v) : (typeof v === 'object' ? JSON.stringify(v) : String(v)),
201
+ typeof v === 'object');
202
+ });
203
+ if (dl.children.length) side.appendChild(dl);
204
+
205
+ var head = document.createElement('div');
206
+ head.className = 'note-head';
207
+ var lab = document.createElement('strong'); lab.textContent = '我的意见';
208
+ var hint = document.createElement('span'); hint.textContent = '会随反馈一起导出';
209
+ head.appendChild(lab); head.appendChild(hint);
210
+ side.appendChild(head);
211
+
212
+ var ta = document.createElement('textarea');
213
+ ta.value = notes[selected] || '';
214
+ ta.setAttribute('aria-label', '对 ' + (obj.label || obj.id) + ' 的意见');
215
+ ta.addEventListener('input', function () {
216
+ if (ta.value.trim()) notes[selected] = ta.value; else delete notes[selected];
217
+ refreshCounts();
218
+ });
219
+ side.appendChild(ta);
220
+ }
221
+
222
+ function addRow(dl, k, v, mono) {
223
+ var dt = document.createElement('dt'); dt.textContent = k;
224
+ var dd = document.createElement('dd'); dd.textContent = v;
225
+ if (mono) dd.className = 'mono';
226
+ dl.appendChild(dt); dl.appendChild(dd);
227
+ }
228
+
229
+ /* ── 按关系类别过滤 ── */
230
+ function buildLegend() {
231
+ var box = el('legend');
232
+ var present = {};
233
+ EDGES.forEach(function (e) { if (ROLES.indexOf(e.role) >= 0) present[e.role] = 1; });
234
+ ROLES.filter(function (r) { return present[r]; }).forEach(function (r) {
235
+ var lab = document.createElement('label');
236
+ var cb = document.createElement('input');
237
+ cb.type = 'checkbox'; cb.checked = true; cb.style.marginRight = '4px';
238
+ cb.addEventListener('change', function () {
239
+ if (cb.checked) delete hiddenRoles[r]; else hiddenRoles[r] = 1;
240
+ lab.classList.toggle('off', !cb.checked);
241
+ [].forEach.call(svg.querySelectorAll('.edge.role-' + r), function (g) { g.classList.toggle('hidden', !cb.checked); });
242
+ });
243
+ var i = document.createElement('i');
244
+ i.style.borderTopColor = 'var(--r-' + r + ')';
245
+ if (r === 'recycle' || r === 'utility' || r === 'unknown') i.style.borderTopStyle = 'dashed';
246
+ lab.appendChild(cb); lab.appendChild(i);
247
+ lab.appendChild(document.createTextNode(ROLE_CN[r]));
248
+ box.appendChild(lab);
249
+ });
250
+ }
251
+
252
+ /* ── minimap ── */
253
+ var mmView;
254
+ function buildMinimap() {
255
+ var mm = el('minimap');
256
+ var s = mk('svg', { viewBox: (bb.x0 - 20) + ' ' + (bb.y0 - 20) + ' ' + (bb.x1 - bb.x0 + 40) + ' ' + (bb.y1 - bb.y0 + 40) });
257
+ NODES.forEach(function (n) {
258
+ s.appendChild(mk('rect', { 'class': 'mm-node', x: n.x, y: n.y, width: n.width || 120, height: n.height || 40, rx: 2 }));
259
+ });
260
+ mmView = mk('rect', { 'class': 'mm-view' });
261
+ s.appendChild(mmView);
262
+ mm.appendChild(s);
263
+ }
264
+ function drawViewport() {
265
+ if (!mmView) return;
266
+ var r = svg.getBoundingClientRect();
267
+ mmView.setAttribute('x', -view.x / view.k);
268
+ mmView.setAttribute('y', -view.y / view.k);
269
+ mmView.setAttribute('width', r.width / view.k);
270
+ mmView.setAttribute('height', r.height / view.k);
271
+ }
272
+
273
+ /* ── 入口区:数据里带了什么就报什么,形状由生成器决定 ── */
274
+ function buildEntry() {
275
+ var box = el('entry');
276
+ var items = (A.entry || []).filter(function (it) { return it && it.count; });
277
+ if (!items.length) { box.remove(); return; }
278
+ var lead = document.createElement('span');
279
+ lead.className = 'lead'; lead.textContent = '建议从这里开始看:';
280
+ box.appendChild(lead);
281
+ items.forEach(function (it) {
282
+ var b = document.createElement('button');
283
+ b.className = 'chip';
284
+ b.textContent = it.label + ' ' + it.count;
285
+ b.addEventListener('click', function () {
286
+ var ids = it.ids || [];
287
+ if (!ids.length) return;
288
+ var i = (b.__i || 0) % ids.length;
289
+ b.__i = i + 1;
290
+ selected = null; select(ids[i]);
291
+ focusOn(ids[i]);
292
+ });
293
+ box.appendChild(b);
294
+ });
295
+ }
296
+ function focusOn(id) {
297
+ var n = byId[id];
298
+ var cx, cy;
299
+ if (n) { cx = n.x + (n.width || 120) / 2; cy = n.y + (n.height || 40) / 2; }
300
+ else {
301
+ var e = EDGES.filter(function (x) { return x.id === id; })[0];
302
+ if (!e || !e.points || !e.points.length) return;
303
+ var mid = e.points[e.points.length >> 1];
304
+ cx = mid[0]; cy = mid[1];
305
+ }
306
+ var r = svg.getBoundingClientRect();
307
+ view.k = Math.max(view.k, 0.75);
308
+ view.x = r.width / 2 - cx * view.k;
309
+ view.y = r.height / 2 - cy * view.k;
310
+ apply();
311
+ }
312
+
313
+ /* ── 反馈导出 ── */
314
+ function refreshCounts() {
315
+ var n = Object.keys(notes).length;
316
+ var btn = el('export');
317
+ btn.textContent = n ? '导出反馈(' + n + ')' : '导出反馈';
318
+ btn.disabled = !n;
319
+ }
320
+ function exportNotes() {
321
+ var p = A.provenance || {};
322
+ var payload = {
323
+ artifact: A.title || '',
324
+ source: p.source || null,
325
+ sourceDigest: p.digest || null,
326
+ generatedAt: p.generatedAt || null,
327
+ exportedAt: new Date().toISOString(),
328
+ annotations: Object.keys(notes).map(function (id) {
329
+ var o = byId[id] || EDGES.filter(function (e) { return e.id === id; })[0] || {};
330
+ return { id: id, label: o.label || id, kind: byId[id] ? 'node' : 'edge', note: notes[id] };
331
+ })
332
+ };
333
+ var blob = new Blob([JSON.stringify(payload, null, 2)], { type: 'application/json' });
334
+ var a = document.createElement('a');
335
+ a.href = URL.createObjectURL(blob);
336
+ a.download = 'annotations.json';
337
+ a.click();
338
+ setTimeout(function () { URL.revokeObjectURL(a.href); }, 1000);
339
+ }
340
+
341
+ /* ── 主题:跟随系统,可手动覆盖 ── */
342
+ function toggleTheme() {
343
+ var cur = document.documentElement.getAttribute('data-theme');
344
+ var next = cur === 'dark' ? 'light' : cur === 'light' ? null : 'dark';
345
+ if (next) document.documentElement.setAttribute('data-theme', next);
346
+ else document.documentElement.removeAttribute('data-theme');
347
+ el('theme').setAttribute('aria-pressed', next === 'dark' ? 'true' : 'false');
348
+ }
349
+
350
+ /* ── 启动 ── */
351
+ el('counts').textContent = NODES.length + ' 节点 · ' + EDGES.length + ' 连线';
352
+ buildEntry();
353
+ buildLegend();
354
+ buildMinimap();
355
+ renderSide();
356
+ refreshCounts();
357
+ el('fit').addEventListener('click', fit);
358
+ el('export').addEventListener('click', exportNotes);
359
+ el('theme').addEventListener('click', toggleTheme);
360
+
361
+ var fitted = false;
362
+ function tryFit() { if (!fitted && fit()) fitted = true; }
363
+ if (window.ResizeObserver) {
364
+ new ResizeObserver(function () { tryFit(); drawViewport(); }).observe(svg);
365
+ } else {
366
+ window.addEventListener('resize', function () { tryFit(); drawViewport(); });
367
+ }
368
+ tryFit();
369
+ if (!fitted) requestAnimationFrame(tryFit);
370
+ window.addEventListener('load', tryFit);
371
+
372
+ window.__RENDERED__ = { nodes: NODES.length, edges: EDGES.length, labels: EDGES.filter(function (e) { return e.label; }).length };
373
+ })();