@research-copilot/plugin 1.1.17 → 1.1.19

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 (800) hide show
  1. package/README.md +18 -9
  2. package/dist/.claude-plugin/plugin.json +1 -1
  3. package/dist/.codex-plugin/plugin.toml +1 -1
  4. package/dist/.cursor-plugin/plugin.json +1 -1
  5. package/dist/.gemini-plugin/plugin.json +1 -1
  6. package/dist/.opencode-plugin/plugin.json +1 -1
  7. package/dist/.windsurf-plugin/plugin.json +1 -1
  8. package/dist/skills/ablation-planner/SKILL.md +126 -0
  9. package/dist/skills/alphaxiv/SKILL.md +186 -0
  10. package/dist/skills/analyze-results/SKILL.md +45 -0
  11. package/dist/skills/arxiv/SKILL.md +210 -0
  12. package/dist/skills/auto-paper-improvement-loop/SKILL.md +574 -0
  13. package/dist/skills/auto-review-loop/SKILL.md +370 -0
  14. package/dist/skills/auto-review-loop-llm/SKILL.md +247 -0
  15. package/dist/skills/auto-review-loop-minimax/SKILL.md +290 -0
  16. package/dist/skills/benchmark-paper-template/SKILL.md +142 -0
  17. package/dist/skills/benchmark-paper-template/references/benchmark-design.md +180 -0
  18. package/dist/skills/benchmark-paper-template/references/checklist.md +113 -0
  19. package/dist/skills/benchmark-paper-template/references/construction-pipeline.md +127 -0
  20. package/dist/skills/benchmark-paper-template/references/experiments.md +159 -0
  21. package/dist/skills/benchmark-paper-template/references/gap-analysis.md +86 -0
  22. package/dist/skills/benchmark-paper-template/references/instantiation-template.md +194 -0
  23. package/dist/skills/benchmark-paper-template/references/orchestrator-notes.md +96 -0
  24. package/dist/skills/benchmark-paper-template/references/paper-structure.md +209 -0
  25. package/dist/skills/citation-audit/SKILL.md +494 -0
  26. package/dist/skills/claims-drafting/SKILL.md +239 -0
  27. package/dist/skills/comm-lit-review/SKILL.md +299 -0
  28. package/dist/skills/comm-lit-review/references/domain-taxonomy.md +57 -0
  29. package/dist/skills/comm-lit-review/references/output-template.md +37 -0
  30. package/dist/skills/comm-lit-review/references/source-policy.md +99 -0
  31. package/dist/skills/comm-lit-review/references/venue-tiering.md +112 -0
  32. package/dist/skills/composer/SKILL.md +840 -0
  33. package/dist/skills/composer/references/section_guides.md +675 -0
  34. package/dist/skills/composer/references/writing_standards.md +629 -0
  35. package/dist/skills/composer/scripts/chapter_quality_check.py +470 -0
  36. package/dist/skills/composer/scripts/final_evaluation.py +550 -0
  37. package/dist/skills/deepxiv/SKILL.md +142 -0
  38. package/dist/skills/dse-loop/SKILL.md +277 -0
  39. package/dist/skills/embodiment-description/SKILL.md +129 -0
  40. package/dist/skills/exa-search/SKILL.md +192 -0
  41. package/dist/skills/experiment-audit/SKILL.md +262 -0
  42. package/dist/skills/experiment-bridge/SKILL.md +316 -0
  43. package/dist/skills/experiment-plan/SKILL.md +249 -0
  44. package/dist/skills/experiment-queue/SKILL.md +380 -0
  45. package/dist/skills/experiment-queue/scripts/build_manifest.py +142 -0
  46. package/dist/skills/experiment-queue/scripts/queue_manager.py +426 -0
  47. package/dist/skills/feishu-notify/SKILL.md +155 -0
  48. package/dist/skills/figure-description/SKILL.md +138 -0
  49. package/dist/skills/figure-designer/SKILL.md +207 -0
  50. package/dist/skills/figure-designer/references/design-rules.md +99 -0
  51. package/dist/skills/figure-designer/references/experimental-results.md +127 -0
  52. package/dist/skills/figure-designer/references/motivated-example.md +138 -0
  53. package/dist/skills/figure-designer/references/solution-overview.md +145 -0
  54. package/dist/skills/figure-designer/references/tools.md +124 -0
  55. package/dist/skills/figure-spec/SKILL.md +252 -0
  56. package/dist/skills/figure-spec/scripts/figure_renderer.py +799 -0
  57. package/dist/skills/formula-derivation/SKILL.md +280 -0
  58. package/dist/skills/gemini-search/SKILL.md +205 -0
  59. package/dist/skills/grant-proposal/SKILL.md +625 -0
  60. package/dist/skills/humanizer/AGENTS.md +25 -0
  61. package/dist/skills/humanizer/SKILL.md +621 -0
  62. package/dist/skills/idea-creator/SKILL.md +311 -0
  63. package/dist/skills/idea-discovery/SKILL.md +340 -0
  64. package/dist/skills/idea-discovery-robot/SKILL.md +362 -0
  65. package/dist/skills/idea-evaluator/SKILL.md +265 -0
  66. package/dist/skills/idea-evaluator/references/fatal-flaws.md +192 -0
  67. package/dist/skills/idea-evaluator/references/five-dimensions.md +251 -0
  68. package/dist/skills/idea-evaluator/references/lifecycle-capability-matching.md +177 -0
  69. package/dist/skills/idea-evaluator/references/paradigm-elephant.md +112 -0
  70. package/dist/skills/idea-evaluator/references/paradigm-examples.md +107 -0
  71. package/dist/skills/idea-evaluator/references/paradigm-first-principles.md +115 -0
  72. package/dist/skills/idea-evaluator/references/paradigm-hamming.md +137 -0
  73. package/dist/skills/idea-evaluator/references/paradigm-shift-probe.md +160 -0
  74. package/dist/skills/idea-evaluator/references/paradigm-technology-cycle.md +116 -0
  75. package/dist/skills/idea-evaluator/references/worked-examples.md +206 -0
  76. package/dist/skills/interview-cheatsheet/SKILL.md +245 -0
  77. package/dist/skills/intro-drafter/SKILL.md +250 -0
  78. package/dist/skills/intro-drafter/references/contribution-patterns.md +116 -0
  79. package/dist/skills/intro-drafter/references/flowchart.md +204 -0
  80. package/dist/skills/intro-drafter/references/paper-types.md +155 -0
  81. package/dist/skills/intro-drafter/references/running-example.md +133 -0
  82. package/dist/skills/intro-drafter/references/worked-examples.md +161 -0
  83. package/dist/skills/invention-structuring/SKILL.md +188 -0
  84. package/dist/skills/jurisdiction-format/SKILL.md +192 -0
  85. package/dist/skills/kill-argument/SKILL.md +384 -0
  86. package/dist/skills/llm-wiki/.mise.toml +2 -0
  87. package/dist/skills/llm-wiki/.nvmrc +1 -0
  88. package/dist/skills/llm-wiki/AGENTS.md +132 -0
  89. package/dist/skills/llm-wiki/CHANGELOG.md +459 -0
  90. package/dist/skills/llm-wiki/CLAUDE.md +109 -0
  91. package/dist/skills/llm-wiki/HERMES.md +44 -0
  92. package/dist/skills/llm-wiki/SKILL.md +1133 -0
  93. package/dist/skills/llm-wiki/assets/graph-demo.gif +0 -0
  94. package/dist/skills/llm-wiki/assets/graph-preview.png +0 -0
  95. package/dist/skills/llm-wiki/deps/LICENSE-d3.txt +13 -0
  96. package/dist/skills/llm-wiki/deps/LICENSE-marked.txt +44 -0
  97. package/dist/skills/llm-wiki/deps/LICENSE-purify.txt +568 -0
  98. package/{LICENSE → dist/skills/llm-wiki/deps/LICENSE-roughjs.txt} +1 -1
  99. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/SKILL.md +261 -0
  100. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/references/config/first-time-setup.md +106 -0
  101. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/cdp.ts +179 -0
  102. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/constants.ts +13 -0
  103. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/defuddle-converter.ts +58 -0
  104. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/html-to-markdown.ts +135 -0
  105. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/legacy-converter.ts +629 -0
  106. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/main.ts +314 -0
  107. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/markdown-conversion-shared.ts +305 -0
  108. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/media-localizer.ts +317 -0
  109. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/package.json +14 -0
  110. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/paths.ts +29 -0
  111. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/vendor/baoyu-chrome-cdp/package.json +9 -0
  112. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/vendor/baoyu-chrome-cdp/src/index.test.ts +307 -0
  113. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/vendor/baoyu-chrome-cdp/src/index.ts +523 -0
  114. package/dist/skills/llm-wiki/deps/d3.min.js +2 -0
  115. package/dist/skills/llm-wiki/deps/marked.min.js +6 -0
  116. package/dist/skills/llm-wiki/deps/purify.min.js +3 -0
  117. package/dist/skills/llm-wiki/deps/rough.min.js +7 -0
  118. package/dist/skills/llm-wiki/deps/youtube-transcript/SKILL.md +47 -0
  119. package/dist/skills/llm-wiki/deps/youtube-transcript/scripts/get_transcript.py +72 -0
  120. package/dist/skills/llm-wiki/docs/design/oriental-atlas/DESIGN.md +183 -0
  121. package/dist/skills/llm-wiki/docs/design/oriental-atlas/design-brief.md +170 -0
  122. package/dist/skills/llm-wiki/docs/design/oriental-atlas/oriental-editorial-atlas.html +1908 -0
  123. package/dist/skills/llm-wiki/docs/design/oriental-atlas/reviews/result-review.md +68 -0
  124. package/dist/skills/llm-wiki/docs/plans/2026-06-15-omp-tool-status-events-phased-plan.md +655 -0
  125. package/dist/skills/llm-wiki/docs/plans/2026-06-15-omp-tool-status-events-progress.json +913 -0
  126. package/dist/skills/llm-wiki/docs/spark/2026-06-15-omp-tool-status-events-design.md +174 -0
  127. package/dist/skills/llm-wiki/install.ps1 +87 -0
  128. package/dist/skills/llm-wiki/install.sh +722 -0
  129. package/dist/skills/llm-wiki/package-lock.json +10309 -0
  130. package/dist/skills/llm-wiki/package.json +24 -0
  131. package/dist/skills/llm-wiki/packages/graph-engine/package.json +33 -0
  132. package/dist/skills/llm-wiki/packages/graph-engine/src/anim/.gitkeep +1 -0
  133. package/dist/skills/llm-wiki/packages/graph-engine/src/anim/index.ts +179 -0
  134. package/dist/skills/llm-wiki/packages/graph-engine/src/diff.ts +121 -0
  135. package/dist/skills/llm-wiki/packages/graph-engine/src/graph-node.ts +37 -0
  136. package/dist/skills/llm-wiki/packages/graph-engine/src/index.ts +140 -0
  137. package/dist/skills/llm-wiki/packages/graph-engine/src/model/atlas.ts +18 -0
  138. package/dist/skills/llm-wiki/packages/graph-engine/src/model/index.ts +6 -0
  139. package/dist/skills/llm-wiki/packages/graph-engine/src/model/labels.ts +11 -0
  140. package/dist/skills/llm-wiki/packages/graph-engine/src/model/learning.ts +8 -0
  141. package/dist/skills/llm-wiki/packages/graph-engine/src/model/legacy-helpers.ts +1373 -0
  142. package/dist/skills/llm-wiki/packages/graph-engine/src/model/queue.ts +7 -0
  143. package/dist/skills/llm-wiki/packages/graph-engine/src/model/storage.ts +4 -0
  144. package/dist/skills/llm-wiki/packages/graph-engine/src/model/visibility.ts +9 -0
  145. package/dist/skills/llm-wiki/packages/graph-engine/src/render/.gitkeep +1 -0
  146. package/dist/skills/llm-wiki/packages/graph-engine/src/render/index.ts +59 -0
  147. package/dist/skills/llm-wiki/packages/graph-engine/src/render/legend.ts +25 -0
  148. package/dist/skills/llm-wiki/packages/graph-engine/src/render/model.ts +593 -0
  149. package/dist/skills/llm-wiki/packages/graph-engine/src/render/preview.ts +65 -0
  150. package/dist/skills/llm-wiki/packages/graph-engine/src/render/search.ts +48 -0
  151. package/dist/skills/llm-wiki/packages/graph-engine/src/render/static-renderer.ts +2783 -0
  152. package/dist/skills/llm-wiki/packages/graph-engine/src/render/toolbar.ts +40 -0
  153. package/dist/skills/llm-wiki/packages/graph-engine/src/render/viewport.ts +272 -0
  154. package/dist/skills/llm-wiki/packages/graph-engine/src/select/.gitkeep +1 -0
  155. package/dist/skills/llm-wiki/packages/graph-engine/src/select/index.ts +189 -0
  156. package/dist/skills/llm-wiki/packages/graph-engine/src/sim/index.ts +298 -0
  157. package/dist/skills/llm-wiki/packages/graph-engine/src/sim/pins.ts +96 -0
  158. package/dist/skills/llm-wiki/packages/graph-engine/src/themes/.gitkeep +1 -0
  159. package/dist/skills/llm-wiki/packages/graph-engine/src/themes/index.ts +8 -0
  160. package/dist/skills/llm-wiki/packages/graph-engine/src/themes/tokens.ts +107 -0
  161. package/dist/skills/llm-wiki/packages/graph-engine/src/types.ts +313 -0
  162. package/dist/skills/llm-wiki/packages/graph-engine/test/anim.test.ts +100 -0
  163. package/dist/skills/llm-wiki/packages/graph-engine/test/diff.test.ts +133 -0
  164. package/dist/skills/llm-wiki/packages/graph-engine/test/helpers.test.ts +275 -0
  165. package/dist/skills/llm-wiki/packages/graph-engine/test/learning.test.ts +259 -0
  166. package/dist/skills/llm-wiki/packages/graph-engine/test/preview.test.ts +56 -0
  167. package/dist/skills/llm-wiki/packages/graph-engine/test/queue.test.ts +114 -0
  168. package/dist/skills/llm-wiki/packages/graph-engine/test/render-model.test.ts +322 -0
  169. package/dist/skills/llm-wiki/packages/graph-engine/test/runtime-state.test.ts +281 -0
  170. package/dist/skills/llm-wiki/packages/graph-engine/test/search-and-legend.test.ts +111 -0
  171. package/dist/skills/llm-wiki/packages/graph-engine/test/select.test.ts +168 -0
  172. package/dist/skills/llm-wiki/packages/graph-engine/test/sim.test.ts +123 -0
  173. package/dist/skills/llm-wiki/packages/graph-engine/test/themes.test.ts +62 -0
  174. package/dist/skills/llm-wiki/packages/graph-engine/test/toolbar.test.ts +64 -0
  175. package/dist/skills/llm-wiki/packages/graph-engine/test/viewport.test.ts +167 -0
  176. package/dist/skills/llm-wiki/packages/graph-engine/tsconfig.json +17 -0
  177. package/dist/skills/llm-wiki/packages/graph-engine/vite.config.ts +15 -0
  178. package/dist/skills/llm-wiki/platforms/claude/CLAUDE.md +37 -0
  179. package/dist/skills/llm-wiki/platforms/claude/companions/llm-wiki-upgrade/SKILL.md +95 -0
  180. package/dist/skills/llm-wiki/platforms/codex/AGENTS.md +23 -0
  181. package/dist/skills/llm-wiki/scripts/adapter-state.sh +424 -0
  182. package/dist/skills/llm-wiki/scripts/build-graph-data.sh +395 -0
  183. package/dist/skills/llm-wiki/scripts/build-graph-html.sh +453 -0
  184. package/dist/skills/llm-wiki/scripts/cache.sh +352 -0
  185. package/dist/skills/llm-wiki/scripts/create-source-page.sh +100 -0
  186. package/dist/skills/llm-wiki/scripts/delete-helper.sh +68 -0
  187. package/dist/skills/llm-wiki/scripts/graph-analysis.js +732 -0
  188. package/dist/skills/llm-wiki/scripts/hook-session-start.sh +46 -0
  189. package/dist/skills/llm-wiki/scripts/init-wiki.sh +95 -0
  190. package/dist/skills/llm-wiki/scripts/lib/source-signal-eligibility.js +158 -0
  191. package/dist/skills/llm-wiki/scripts/lint-fix.sh +114 -0
  192. package/dist/skills/llm-wiki/scripts/lint-runner.sh +217 -0
  193. package/dist/skills/llm-wiki/scripts/runtime-context.sh +77 -0
  194. package/dist/skills/llm-wiki/scripts/shared-config.sh +83 -0
  195. package/dist/skills/llm-wiki/scripts/source-record-contract.tsv +10 -0
  196. package/dist/skills/llm-wiki/scripts/source-registry.sh +349 -0
  197. package/dist/skills/llm-wiki/scripts/source-registry.tsv +10 -0
  198. package/dist/skills/llm-wiki/scripts/source-signal-coverage.js +83 -0
  199. package/dist/skills/llm-wiki/scripts/validate-step1.sh +144 -0
  200. package/dist/skills/llm-wiki/scripts/wiki-compat.sh +267 -0
  201. package/dist/skills/llm-wiki/setup.sh +8 -0
  202. package/dist/skills/llm-wiki/templates/entity-template.md +30 -0
  203. package/dist/skills/llm-wiki/templates/graph-styles/wash/footer.html +27 -0
  204. package/dist/skills/llm-wiki/templates/graph-styles/wash/graph-wash-helpers.js +1326 -0
  205. package/dist/skills/llm-wiki/templates/graph-styles/wash/graph-wash.js +1009 -0
  206. package/dist/skills/llm-wiki/templates/graph-styles/wash/header.html +2002 -0
  207. package/dist/skills/llm-wiki/templates/index-en-template.md +51 -0
  208. package/dist/skills/llm-wiki/templates/index-template.md +51 -0
  209. package/dist/skills/llm-wiki/templates/log-en-template.md +11 -0
  210. package/dist/skills/llm-wiki/templates/log-template.md +11 -0
  211. package/dist/skills/llm-wiki/templates/overview-en-template.md +41 -0
  212. package/dist/skills/llm-wiki/templates/overview-template.md +41 -0
  213. package/dist/skills/llm-wiki/templates/purpose-en-template.md +16 -0
  214. package/dist/skills/llm-wiki/templates/purpose-template.md +16 -0
  215. package/dist/skills/llm-wiki/templates/query-template.md +19 -0
  216. package/dist/skills/llm-wiki/templates/schema-template.md +185 -0
  217. package/dist/skills/llm-wiki/templates/source-template.md +51 -0
  218. package/dist/skills/llm-wiki/templates/synthesis-template.md +25 -0
  219. package/dist/skills/llm-wiki/templates/topic-template.md +35 -0
  220. package/dist/skills/llm-wiki/tests/adapter-state.sh +338 -0
  221. package/dist/skills/llm-wiki/tests/browser/graph-density-preview.mjs +53 -0
  222. package/dist/skills/llm-wiki/tests/browser/graph-html-insights.mjs +41 -0
  223. package/dist/skills/llm-wiki/tests/browser/graph-node-slim.mjs +71 -0
  224. package/dist/skills/llm-wiki/tests/browser/graph-stage-4-5.mjs +717 -0
  225. package/dist/skills/llm-wiki/tests/expected/graph-data-empty.json +60 -0
  226. package/dist/skills/llm-wiki/tests/expected/graph-data-sample.json +396 -0
  227. package/dist/skills/llm-wiki/tests/expected/graph-interactive-basic.html +1736 -0
  228. package/dist/skills/llm-wiki/tests/expected/lint-output.txt +44 -0
  229. package/dist/skills/llm-wiki/tests/fixtures/coverage-sample-wiki/index.md +1 -0
  230. package/dist/skills/llm-wiki/tests/fixtures/coverage-sample-wiki/wiki/entities/Alpha.md +10 -0
  231. package/dist/skills/llm-wiki/tests/fixtures/coverage-sample-wiki/wiki/entities/Beta.md +7 -0
  232. package/dist/skills/llm-wiki/tests/fixtures/coverage-sample-wiki/wiki/entities/Delta.md +8 -0
  233. package/dist/skills/llm-wiki/tests/fixtures/coverage-sample-wiki/wiki/entities/Gamma.md +8 -0
  234. package/dist/skills/llm-wiki/tests/fixtures/coverage-sample-wiki/wiki/queries/20260422-abc.md +10 -0
  235. package/dist/skills/llm-wiki/tests/fixtures/coverage-sample-wiki/wiki/synthesis/Crystal.md +8 -0
  236. package/dist/skills/llm-wiki/tests/fixtures/coverage-sample-wiki/wiki/topics/Overview.md +9 -0
  237. package/dist/skills/llm-wiki/tests/fixtures/graph-data-empty-wiki/wiki/entities/.gitkeep +0 -0
  238. package/dist/skills/llm-wiki/tests/fixtures/graph-data-sample-wiki/purpose.md +3 -0
  239. package/dist/skills/llm-wiki/tests/fixtures/graph-data-sample-wiki/wiki/entities/Attention.md +7 -0
  240. package/dist/skills/llm-wiki/tests/fixtures/graph-data-sample-wiki/wiki/entities/Decoder.md +7 -0
  241. package/dist/skills/llm-wiki/tests/fixtures/graph-data-sample-wiki/wiki/entities/Encoder.md +7 -0
  242. package/dist/skills/llm-wiki/tests/fixtures/graph-data-sample-wiki/wiki/entities/GPT.md +7 -0
  243. package/dist/skills/llm-wiki/tests/fixtures/graph-data-sample-wiki/wiki/entities/Transformer.md +13 -0
  244. package/dist/skills/llm-wiki/tests/fixtures/graph-data-sample-wiki/wiki/sources/paper.md +9 -0
  245. package/dist/skills/llm-wiki/tests/fixtures/graph-data-sample-wiki/wiki/topics/arch.md +12 -0
  246. package/dist/skills/llm-wiki/tests/fixtures/graph-data-sample-wiki/wiki/topics/finetune.md +9 -0
  247. package/dist/skills/llm-wiki/tests/fixtures/graph-interactive-basic/wiki/graph-data.json +40 -0
  248. package/dist/skills/llm-wiki/tests/fixtures/graph-interactive-dense/wiki/graph-data.json +3965 -0
  249. package/dist/skills/llm-wiki/tests/fixtures/graph-interactive-multicomm/wiki/graph-data.json +212 -0
  250. package/dist/skills/llm-wiki/tests/fixtures/lint-sample-wiki/index.md +14 -0
  251. package/dist/skills/llm-wiki/tests/fixtures/lint-sample-wiki/wiki/entities/C++.md +6 -0
  252. package/dist/skills/llm-wiki/tests/fixtures/lint-sample-wiki/wiki/entities/Lonely.md +4 -0
  253. package/dist/skills/llm-wiki/tests/fixtures/lint-sample-wiki/wiki/entities/Other.md +8 -0
  254. package/dist/skills/llm-wiki/tests/fixtures/lint-sample-wiki/wiki/entities/Real.md +7 -0
  255. package/dist/skills/llm-wiki/tests/fixtures/lint-sample-wiki/wiki/sources/2026-01-01-test-source.md +15 -0
  256. package/dist/skills/llm-wiki/tests/fixtures/lint-sample-wiki/wiki/sources/2026-02-01-inline-img.md +12 -0
  257. package/dist/skills/llm-wiki/tests/fixtures/lint-sample-wiki/wiki/topics/AIbasic.md +3 -0
  258. package/dist/skills/llm-wiki/tests/graph-analysis-helper.regression-1.sh +237 -0
  259. package/dist/skills/llm-wiki/tests/graph-browser-stage-4-5.regression-1.sh +157 -0
  260. package/dist/skills/llm-wiki/tests/graph-build-failures.regression-1.sh +91 -0
  261. package/dist/skills/llm-wiki/tests/graph-data-confidence-merge.regression-1.sh +309 -0
  262. package/dist/skills/llm-wiki/tests/graph-data-source-paths.regression-1.sh +49 -0
  263. package/dist/skills/llm-wiki/tests/graph-html-a11y.regression-1.sh +47 -0
  264. package/dist/skills/llm-wiki/tests/graph-html-brand-link.regression-1.sh +47 -0
  265. package/dist/skills/llm-wiki/tests/graph-html-density.regression-1.sh +228 -0
  266. package/dist/skills/llm-wiki/tests/graph-html-drawer-neighbors.regression-1.sh +47 -0
  267. package/dist/skills/llm-wiki/tests/graph-html-insights.regression-1.sh +80 -0
  268. package/dist/skills/llm-wiki/tests/graph-html-long-label.regression-1.sh +92 -0
  269. package/dist/skills/llm-wiki/tests/graph-html-minimap.regression-1.sh +51 -0
  270. package/dist/skills/llm-wiki/tests/graph-html-mobile.regression-1.sh +48 -0
  271. package/dist/skills/llm-wiki/tests/graph-html-oriental-atlas-contract.regression-1.sh +94 -0
  272. package/dist/skills/llm-wiki/tests/graph-html-oriental-design-contract.regression-1.sh +82 -0
  273. package/dist/skills/llm-wiki/tests/graph-html-search.regression-1.sh +52 -0
  274. package/dist/skills/llm-wiki/tests/graph-html-styles.regression-1.sh +66 -0
  275. package/dist/skills/llm-wiki/tests/graph-html-toolbar.regression-1.sh +62 -0
  276. package/dist/skills/llm-wiki/tests/js/graph-wash-bootstrap.test.js +110 -0
  277. package/dist/skills/llm-wiki/tests/js/graph-wash-helpers.test.js +296 -0
  278. package/dist/skills/llm-wiki/tests/js/graph-wash-learning.test.js +260 -0
  279. package/dist/skills/llm-wiki/tests/js/graph-wash-queue.test.js +115 -0
  280. package/dist/skills/llm-wiki/tests/js/graph-wash-runtime-state.test.js +282 -0
  281. package/dist/skills/llm-wiki/tests/js/source-signal-coverage.test.js +48 -0
  282. package/dist/skills/llm-wiki/tests/js/source-signal-eligibility.test.js +202 -0
  283. package/dist/skills/llm-wiki/tests/lib/graph-html-engine-helpers.sh +110 -0
  284. package/dist/skills/llm-wiki/tests/lint-output.regression-1.sh +32 -0
  285. package/dist/skills/llm-wiki/tests/regression.sh +1754 -0
  286. package/dist/skills/llm-wiki/workbench/.claude/skills/.gitkeep +0 -0
  287. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/SKILL.md +590 -0
  288. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/__init__.py +1 -0
  289. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/accept_changes.py +135 -0
  290. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/comment.py +318 -0
  291. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/helpers/__init__.py +0 -0
  292. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/helpers/merge_runs.py +199 -0
  293. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/helpers/simplify_redlines.py +197 -0
  294. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/pack.py +159 -0
  295. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  296. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  297. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  298. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  299. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  300. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  301. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  302. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  303. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  304. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  305. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  306. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  307. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  308. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  309. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  310. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  311. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  312. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  313. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  314. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  315. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  316. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  317. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  318. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  319. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  320. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  321. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  322. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  323. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  324. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  325. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  326. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/mce/mc.xsd +75 -0
  327. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  328. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  329. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  330. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  331. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  332. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  333. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  334. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/soffice.py +183 -0
  335. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/unpack.py +132 -0
  336. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/validate.py +111 -0
  337. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/validators/__init__.py +15 -0
  338. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/validators/base.py +847 -0
  339. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/validators/docx.py +446 -0
  340. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/validators/pptx.py +275 -0
  341. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/validators/redlining.py +247 -0
  342. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/templates/comments.xml +3 -0
  343. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/templates/commentsExtended.xml +3 -0
  344. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
  345. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/templates/commentsIds.xml +3 -0
  346. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/templates/people.xml +3 -0
  347. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/SKILL.md +314 -0
  348. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/forms.md +294 -0
  349. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/reference.md +612 -0
  350. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/scripts/check_bounding_boxes.py +65 -0
  351. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/scripts/check_fillable_fields.py +11 -0
  352. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/scripts/convert_pdf_to_images.py +33 -0
  353. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/scripts/create_validation_image.py +37 -0
  354. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/scripts/extract_form_field_info.py +122 -0
  355. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/scripts/extract_form_structure.py +115 -0
  356. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/scripts/fill_fillable_fields.py +98 -0
  357. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
  358. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/SKILL.md +232 -0
  359. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/editing.md +205 -0
  360. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/pptxgenjs.md +420 -0
  361. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/__init__.py +0 -0
  362. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/add_slide.py +195 -0
  363. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/clean.py +286 -0
  364. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/helpers/__init__.py +0 -0
  365. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/helpers/merge_runs.py +199 -0
  366. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/helpers/simplify_redlines.py +197 -0
  367. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/pack.py +159 -0
  368. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  369. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  370. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  371. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  372. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  373. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  374. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  375. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  376. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  377. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  378. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  379. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  380. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  381. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  382. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  383. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  384. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  385. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  386. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  387. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  388. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  389. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  390. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  391. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  392. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  393. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  394. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  395. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  396. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  397. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  398. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  399. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/mce/mc.xsd +75 -0
  400. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  401. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  402. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  403. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  404. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  405. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  406. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  407. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/soffice.py +183 -0
  408. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/unpack.py +132 -0
  409. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/validate.py +111 -0
  410. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/validators/__init__.py +15 -0
  411. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/validators/base.py +847 -0
  412. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/validators/docx.py +446 -0
  413. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/validators/pptx.py +275 -0
  414. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/validators/redlining.py +247 -0
  415. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/thumbnail.py +289 -0
  416. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/SKILL.md +292 -0
  417. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/helpers/__init__.py +0 -0
  418. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/helpers/merge_runs.py +199 -0
  419. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/helpers/simplify_redlines.py +197 -0
  420. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/pack.py +159 -0
  421. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  422. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  423. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  424. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  425. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  426. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  427. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  428. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  429. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  430. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  431. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  432. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  433. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  434. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  435. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  436. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  437. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  438. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  439. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  440. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  441. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  442. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  443. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  444. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  445. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  446. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  447. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  448. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  449. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  450. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  451. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  452. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/mce/mc.xsd +75 -0
  453. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  454. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  455. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  456. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  457. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  458. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  459. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  460. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/soffice.py +183 -0
  461. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/unpack.py +132 -0
  462. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/validate.py +111 -0
  463. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/validators/__init__.py +15 -0
  464. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/validators/base.py +847 -0
  465. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/validators/docx.py +446 -0
  466. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/validators/pptx.py +275 -0
  467. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/validators/redlining.py +247 -0
  468. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/recalc.py +184 -0
  469. package/dist/skills/llm-wiki/workbench/AGENTS.md +110 -0
  470. package/dist/skills/llm-wiki/workbench/CLAUDE.md +54 -0
  471. package/dist/skills/llm-wiki/workbench/PRODUCT.md +1268 -0
  472. package/dist/skills/llm-wiki/workbench/docs/current-kb-retrieval-design.md +587 -0
  473. package/dist/skills/llm-wiki/workbench/docs/graph-evolution-1-design.md +210 -0
  474. package/dist/skills/llm-wiki/workbench/docs/stage-2-design.md +1139 -0
  475. package/dist/skills/llm-wiki/workbench/docs/stage-3-design.md +1269 -0
  476. package/dist/skills/llm-wiki/workbench/docs/stage-3.5-design.md +1164 -0
  477. package/dist/skills/llm-wiki/workbench/docs/stage-4-design.md +504 -0
  478. package/dist/skills/llm-wiki/workbench/docs/stage-4.5-design.md +195 -0
  479. package/dist/skills/llm-wiki/workbench/docs/superpowers/specs/2026-05-28-resizable-preview-layout-design.md +165 -0
  480. package/dist/skills/llm-wiki/workbench/docs/superpowers/specs/2026-05-28-settings-panel-scroll-fix.md +50 -0
  481. package/dist/skills/llm-wiki/workbench/server/package.json +29 -0
  482. package/dist/skills/llm-wiki/workbench/server/src/agent.ts +457 -0
  483. package/dist/skills/llm-wiki/workbench/server/src/artifacts.ts +248 -0
  484. package/dist/skills/llm-wiki/workbench/server/src/auth.ts +164 -0
  485. package/dist/skills/llm-wiki/workbench/server/src/config.ts +130 -0
  486. package/dist/skills/llm-wiki/workbench/server/src/conversations.test.ts +121 -0
  487. package/dist/skills/llm-wiki/workbench/server/src/conversations.ts +255 -0
  488. package/dist/skills/llm-wiki/workbench/server/src/digest/batch.ts +189 -0
  489. package/dist/skills/llm-wiki/workbench/server/src/digest/concurrency.test.ts +36 -0
  490. package/dist/skills/llm-wiki/workbench/server/src/digest/concurrency.ts +31 -0
  491. package/dist/skills/llm-wiki/workbench/server/src/digest/subagent.ts +91 -0
  492. package/dist/skills/llm-wiki/workbench/server/src/extensions/artifacts.ts +81 -0
  493. package/dist/skills/llm-wiki/workbench/server/src/extensions/knowledge-base.ts +263 -0
  494. package/dist/skills/llm-wiki/workbench/server/src/extensions/new-wiki.ts +42 -0
  495. package/dist/skills/llm-wiki/workbench/server/src/extensions/synthesis.ts +172 -0
  496. package/dist/skills/llm-wiki/workbench/server/src/graph-watcher.test.ts +196 -0
  497. package/dist/skills/llm-wiki/workbench/server/src/graph.test.ts +84 -0
  498. package/dist/skills/llm-wiki/workbench/server/src/graph.ts +379 -0
  499. package/dist/skills/llm-wiki/workbench/server/src/index.ts +1148 -0
  500. package/dist/skills/llm-wiki/workbench/server/src/knowledge-bases.test.ts +30 -0
  501. package/dist/skills/llm-wiki/workbench/server/src/knowledge-bases.ts +321 -0
  502. package/dist/skills/llm-wiki/workbench/server/src/pages.test.ts +16 -0
  503. package/dist/skills/llm-wiki/workbench/server/src/pages.ts +158 -0
  504. package/dist/skills/llm-wiki/workbench/server/src/retrieval.test.ts +151 -0
  505. package/dist/skills/llm-wiki/workbench/server/src/retrieval.ts +427 -0
  506. package/dist/skills/llm-wiki/workbench/server/src/tool-status-events.test.ts +363 -0
  507. package/dist/skills/llm-wiki/workbench/server/src/tool-status-events.ts +616 -0
  508. package/dist/skills/llm-wiki/workbench/server/src/wiki-init.test.ts +19 -0
  509. package/dist/skills/llm-wiki/workbench/server/src/wiki-init.ts +188 -0
  510. package/dist/skills/llm-wiki/workbench/server/tsconfig.json +10 -0
  511. package/dist/skills/llm-wiki/workbench/tsconfig.json +20 -0
  512. package/dist/skills/llm-wiki/workbench/web/components.json +21 -0
  513. package/dist/skills/llm-wiki/workbench/web/eslint.config.js +22 -0
  514. package/dist/skills/llm-wiki/workbench/web/index.html +13 -0
  515. package/dist/skills/llm-wiki/workbench/web/package.json +48 -0
  516. package/dist/skills/llm-wiki/workbench/web/src/App.tsx +749 -0
  517. package/dist/skills/llm-wiki/workbench/web/src/components/AddExternalDialog.tsx +373 -0
  518. package/dist/skills/llm-wiki/workbench/web/src/components/ArtifactView.tsx +15 -0
  519. package/dist/skills/llm-wiki/workbench/web/src/components/BatchDigestPanel.tsx +106 -0
  520. package/dist/skills/llm-wiki/workbench/web/src/components/ChatPanel.tsx +821 -0
  521. package/dist/skills/llm-wiki/workbench/web/src/components/CommandMenu.tsx +65 -0
  522. package/dist/skills/llm-wiki/workbench/web/src/components/ExportButtons.tsx +54 -0
  523. package/dist/skills/llm-wiki/workbench/web/src/components/GraphPanel.tsx +516 -0
  524. package/dist/skills/llm-wiki/workbench/web/src/components/GraphReader.tsx +48 -0
  525. package/dist/skills/llm-wiki/workbench/web/src/components/GraphSelection.tsx +100 -0
  526. package/dist/skills/llm-wiki/workbench/web/src/components/MarkdownView.tsx +56 -0
  527. package/dist/skills/llm-wiki/workbench/web/src/components/NewWikiDialog.tsx +101 -0
  528. package/dist/skills/llm-wiki/workbench/web/src/components/RefMenu.tsx +40 -0
  529. package/dist/skills/llm-wiki/workbench/web/src/components/RightDrawer.tsx +227 -0
  530. package/dist/skills/llm-wiki/workbench/web/src/components/SettingsPanel.tsx +331 -0
  531. package/dist/skills/llm-wiki/workbench/web/src/components/Sidebar.tsx +436 -0
  532. package/dist/skills/llm-wiki/workbench/web/src/components/ToolHistorySummary.tsx +123 -0
  533. package/dist/skills/llm-wiki/workbench/web/src/components/ToolStatusRunway.tsx +90 -0
  534. package/dist/skills/llm-wiki/workbench/web/src/components/renderers/DownloadOnlyRenderer.tsx +88 -0
  535. package/dist/skills/llm-wiki/workbench/web/src/components/renderers/HtmlRenderer.tsx +92 -0
  536. package/dist/skills/llm-wiki/workbench/web/src/components/ui/button.tsx +52 -0
  537. package/dist/skills/llm-wiki/workbench/web/src/components/ui/command.tsx +50 -0
  538. package/dist/skills/llm-wiki/workbench/web/src/components/ui/dialog.tsx +156 -0
  539. package/dist/skills/llm-wiki/workbench/web/src/components/ui/input.tsx +21 -0
  540. package/dist/skills/llm-wiki/workbench/web/src/components/ui/tooltip.tsx +57 -0
  541. package/dist/skills/llm-wiki/workbench/web/src/index.css +1737 -0
  542. package/dist/skills/llm-wiki/workbench/web/src/lib/api.ts +648 -0
  543. package/dist/skills/llm-wiki/workbench/web/src/lib/drawer-state.ts +79 -0
  544. package/dist/skills/llm-wiki/workbench/web/src/lib/graph-reader.ts +25 -0
  545. package/dist/skills/llm-wiki/workbench/web/src/lib/graph-selection-drawer.ts +35 -0
  546. package/dist/skills/llm-wiki/workbench/web/src/lib/graph-selection.ts +96 -0
  547. package/dist/skills/llm-wiki/workbench/web/src/lib/legacy-tool-status.ts +113 -0
  548. package/dist/skills/llm-wiki/workbench/web/src/lib/sse.ts +72 -0
  549. package/dist/skills/llm-wiki/workbench/web/src/lib/tool-status-format.ts +170 -0
  550. package/dist/skills/llm-wiki/workbench/web/src/lib/tool-status-model.ts +392 -0
  551. package/dist/skills/llm-wiki/workbench/web/src/lib/utils.ts +10 -0
  552. package/dist/skills/llm-wiki/workbench/web/src/lib/wiki-links.ts +32 -0
  553. package/dist/skills/llm-wiki/workbench/web/src/main.tsx +10 -0
  554. package/dist/skills/llm-wiki/workbench/web/test/api.test.ts +61 -0
  555. package/dist/skills/llm-wiki/workbench/web/test/chat-panel-tool-status.test.tsx +26 -0
  556. package/dist/skills/llm-wiki/workbench/web/test/graph-drawer-state.test.ts +78 -0
  557. package/dist/skills/llm-wiki/workbench/web/test/graph-reader-request.test.ts +32 -0
  558. package/dist/skills/llm-wiki/workbench/web/test/graph-reader.test.ts +62 -0
  559. package/dist/skills/llm-wiki/workbench/web/test/graph-selection-drawer.test.ts +56 -0
  560. package/dist/skills/llm-wiki/workbench/web/test/graph-selection.test.ts +83 -0
  561. package/dist/skills/llm-wiki/workbench/web/test/tool-history-summary.test.tsx +124 -0
  562. package/dist/skills/llm-wiki/workbench/web/test/tool-status-format.test.ts +107 -0
  563. package/dist/skills/llm-wiki/workbench/web/test/tool-status-model.test.ts +324 -0
  564. package/dist/skills/llm-wiki/workbench/web/test/tool-status-runway.test.ts +134 -0
  565. package/dist/skills/llm-wiki/workbench/web/test/wiki-links.test.ts +23 -0
  566. package/dist/skills/llm-wiki/workbench/web/tsconfig.app.json +30 -0
  567. package/dist/skills/llm-wiki/workbench/web/tsconfig.json +7 -0
  568. package/dist/skills/llm-wiki/workbench/web/tsconfig.node.json +24 -0
  569. package/dist/skills/llm-wiki/workbench/web/vite.config.ts +26 -0
  570. package/dist/skills/mean-reviewer/SECURITY.md +23 -0
  571. package/dist/skills/mean-reviewer/SKILL.md +203 -0
  572. package/dist/skills/mean-reviewer/examples/13446_1000_Layer_Networks_for_.pdf +0 -0
  573. package/dist/skills/mean-reviewer/examples/13446_mean_reviewer.md +116 -0
  574. package/dist/skills/mean-reviewer/examples/13446_mean_reviewer_author_rebuttal_simulated.md +282 -0
  575. package/dist/skills/mean-reviewer/examples/13446_mean_reviewer_reply.md +91 -0
  576. package/dist/skills/mean-reviewer/examples/13446_official_review.json +973 -0
  577. package/dist/skills/mean-reviewer/examples/ChatGPT_AC_Roleplay.jpeg +0 -0
  578. package/dist/skills/medical-imaging-review/SKILL.md +295 -0
  579. package/dist/skills/medical-imaging-review/references/CITATION_INTEGRITY.md +233 -0
  580. package/dist/skills/medical-imaging-review/references/DOMAINS.md +302 -0
  581. package/dist/skills/medical-imaging-review/references/HALLUCINATION_PATTERNS.md +211 -0
  582. package/dist/skills/medical-imaging-review/references/MCP_SETUP.md +216 -0
  583. package/dist/skills/medical-imaging-review/references/PARADIGM.md +221 -0
  584. package/dist/skills/medical-imaging-review/references/QUALITY_CHECKLIST.md +169 -0
  585. package/dist/skills/medical-imaging-review/references/TEMPLATES.md +291 -0
  586. package/dist/skills/medical-imaging-review/references/WORKFLOW.md +289 -0
  587. package/dist/skills/mermaid-diagram/SKILL.md +379 -0
  588. package/dist/skills/meta-apply/SKILL.md +137 -0
  589. package/dist/skills/meta-optimize/SKILL.md +253 -0
  590. package/dist/skills/monitor-experiment/SKILL.md +98 -0
  591. package/dist/skills/novelty-check/SKILL.md +89 -0
  592. package/dist/skills/openalex/SKILL.md +228 -0
  593. package/dist/skills/overleaf-sync/SKILL.md +220 -0
  594. package/dist/skills/paper-claim-audit/SKILL.md +340 -0
  595. package/dist/skills/paper-compile/SKILL.md +253 -0
  596. package/dist/skills/paper-figure/SKILL.md +279 -0
  597. package/dist/skills/paper-illustration/SKILL.md +690 -0
  598. package/dist/skills/paper-illustration-image2/SKILL.md +383 -0
  599. package/dist/skills/paper-illustration-image2/scripts/paper_illustration_image2.py +255 -0
  600. package/dist/skills/paper-plan/SKILL.md +278 -0
  601. package/dist/skills/paper-poster/SKILL.md +19 -0
  602. package/dist/skills/paper-poster-html/DESIGN_FINAL.md +176 -0
  603. package/dist/skills/paper-poster-html/IMPLEMENTATION_CONVENTIONS.md +161 -0
  604. package/dist/skills/paper-poster-html/LICENSES/posterly-MIT.txt +21 -0
  605. package/dist/skills/paper-poster-html/NOTICE.md +57 -0
  606. package/dist/skills/paper-poster-html/SKILL.md +357 -0
  607. package/dist/skills/paper-poster-html/scripts/_posterly/__init__.py +0 -0
  608. package/dist/skills/paper-poster-html/scripts/_posterly/canvas.py +200 -0
  609. package/dist/skills/paper-poster-html/scripts/_posterly/measure.py +588 -0
  610. package/dist/skills/paper-poster-html/scripts/_posterly/polish.py +498 -0
  611. package/dist/skills/paper-poster-html/scripts/_posterly/preflight.py +489 -0
  612. package/dist/skills/paper-poster-html/scripts/_posterly/render.py +215 -0
  613. package/dist/skills/paper-poster-html/scripts/_posterly/textutil.py +16 -0
  614. package/dist/skills/paper-poster-html/scripts/_posterly/verify_final.py +171 -0
  615. package/dist/skills/paper-poster-html/scripts/asset_check.py +897 -0
  616. package/dist/skills/paper-poster-html/scripts/extract_pdf_figures.py +666 -0
  617. package/dist/skills/paper-poster-html/scripts/poster_check.py +251 -0
  618. package/dist/skills/paper-poster-html/scripts/preprocess_figures.py +238 -0
  619. package/dist/skills/paper-poster-html/scripts/render_preview.py +217 -0
  620. package/dist/skills/paper-poster-html/scripts/run_gates.py +556 -0
  621. package/dist/skills/paper-poster-html/scripts/style_check.py +1324 -0
  622. package/dist/skills/paper-poster-html/templates/COMPONENTS.md +462 -0
  623. package/dist/skills/paper-poster-html/templates/landscape_4col.html +1032 -0
  624. package/dist/skills/paper-poster-html/templates/landscape_hero.html +1046 -0
  625. package/dist/skills/paper-poster-html/templates/portrait_2col.html +947 -0
  626. package/dist/skills/paper-poster-html/templates/tokens/acl.json +9 -0
  627. package/dist/skills/paper-poster-html/templates/tokens/cvpr.json +9 -0
  628. package/dist/skills/paper-poster-html/templates/tokens/generic.json +9 -0
  629. package/dist/skills/paper-poster-html/templates/tokens/iclr.json +9 -0
  630. package/dist/skills/paper-poster-html/templates/tokens/icml.json +9 -0
  631. package/dist/skills/paper-poster-html/templates/tokens/neurips.json +9 -0
  632. package/dist/skills/paper-slide-deck/SKILL.md +464 -0
  633. package/dist/skills/paper-slide-deck/references/analysis-framework.md +392 -0
  634. package/dist/skills/paper-slide-deck/references/base-prompt.md +70 -0
  635. package/dist/skills/paper-slide-deck/references/content-rules.md +95 -0
  636. package/dist/skills/paper-slide-deck/references/figure-container-template.md +177 -0
  637. package/dist/skills/paper-slide-deck/references/modification-guide.md +85 -0
  638. package/dist/skills/paper-slide-deck/references/outline-template.md +437 -0
  639. package/dist/skills/paper-slide-deck/references/styles/academic-paper.md +109 -0
  640. package/dist/skills/paper-slide-deck/references/styles/blueprint.md +67 -0
  641. package/dist/skills/paper-slide-deck/references/styles/bold-editorial.md +70 -0
  642. package/dist/skills/paper-slide-deck/references/styles/chalkboard.md +68 -0
  643. package/dist/skills/paper-slide-deck/references/styles/corporate.md +69 -0
  644. package/dist/skills/paper-slide-deck/references/styles/dark-atmospheric.md +69 -0
  645. package/dist/skills/paper-slide-deck/references/styles/editorial-infographic.md +73 -0
  646. package/dist/skills/paper-slide-deck/references/styles/fantasy-animation.md +69 -0
  647. package/dist/skills/paper-slide-deck/references/styles/intuition-machine.md +72 -0
  648. package/dist/skills/paper-slide-deck/references/styles/minimal.md +64 -0
  649. package/dist/skills/paper-slide-deck/references/styles/notion.md +69 -0
  650. package/dist/skills/paper-slide-deck/references/styles/pixel-art.md +67 -0
  651. package/dist/skills/paper-slide-deck/references/styles/scientific.md +73 -0
  652. package/dist/skills/paper-slide-deck/references/styles/sketch-notes.md +66 -0
  653. package/dist/skills/paper-slide-deck/references/styles/vector-illustration.md +72 -0
  654. package/dist/skills/paper-slide-deck/references/styles/vintage.md +73 -0
  655. package/dist/skills/paper-slide-deck/references/styles/watercolor.md +68 -0
  656. package/dist/skills/paper-slide-deck/scripts/apply-template.ts +282 -0
  657. package/dist/skills/paper-slide-deck/scripts/detect-figures.ts +283 -0
  658. package/dist/skills/paper-slide-deck/scripts/extract-figure.ts +140 -0
  659. package/dist/skills/paper-slide-deck/scripts/generate-slides.py +182 -0
  660. package/dist/skills/paper-slide-deck/scripts/merge-to-pdf.ts +116 -0
  661. package/dist/skills/paper-slide-deck/scripts/merge-to-pptx.ts +137 -0
  662. package/dist/skills/paper-slide-deck/scripts/package-lock.json +899 -0
  663. package/dist/skills/paper-slide-deck/scripts/package.json +8 -0
  664. package/dist/skills/paper-slides/SKILL.md +571 -0
  665. package/dist/skills/paper-talk/SKILL.md +381 -0
  666. package/dist/skills/paper-write/SKILL.md +411 -0
  667. package/dist/skills/paper-write/templates/IEEEtran.bst +2409 -0
  668. package/dist/skills/paper-write/templates/IEEEtran.cls +6347 -0
  669. package/dist/skills/paper-write/templates/aaai2026.bst +1493 -0
  670. package/dist/skills/paper-write/templates/aaai2026.sty +315 -0
  671. package/dist/skills/paper-write/templates/aaai2026.tex +952 -0
  672. package/dist/skills/paper-write/templates/acl.sty +312 -0
  673. package/dist/skills/paper-write/templates/acl2026.tex +377 -0
  674. package/dist/skills/paper-write/templates/acl_natbib.bst +1940 -0
  675. package/dist/skills/paper-write/templates/acm.bst +3081 -0
  676. package/dist/skills/paper-write/templates/acm_mm2026.tex +204 -0
  677. package/dist/skills/paper-write/templates/acmart.cls +3520 -0
  678. package/dist/skills/paper-write/templates/cvpr.bst +1448 -0
  679. package/dist/skills/paper-write/templates/cvpr.sty +508 -0
  680. package/dist/skills/paper-write/templates/cvpr2026.tex +63 -0
  681. package/dist/skills/paper-write/templates/iclr2026.tex +84 -0
  682. package/dist/skills/paper-write/templates/iclr2026_conference.bst +1440 -0
  683. package/dist/skills/paper-write/templates/iclr2026_conference.sty +246 -0
  684. package/dist/skills/paper-write/templates/icml2025.tex +87 -0
  685. package/dist/skills/paper-write/templates/icml2026.sty +767 -0
  686. package/dist/skills/paper-write/templates/icml2026.tex +662 -0
  687. package/dist/skills/paper-write/templates/ieee_conference.tex +89 -0
  688. package/dist/skills/paper-write/templates/ieee_journal.tex +93 -0
  689. package/dist/skills/paper-write/templates/math_commands.tex +48 -0
  690. package/dist/skills/paper-write/templates/neurips2025.tex +80 -0
  691. package/dist/skills/paper-write/templates/neurips2026.tex +493 -0
  692. package/dist/skills/paper-write/templates/neurips_2026.sty +437 -0
  693. package/dist/skills/paper-writing/SKILL.md +632 -0
  694. package/dist/skills/patent-novelty-check/SKILL.md +153 -0
  695. package/dist/skills/patent-pipeline/SKILL.md +344 -0
  696. package/dist/skills/patent-review/SKILL.md +202 -0
  697. package/dist/skills/pixel-art/SKILL.md +139 -0
  698. package/dist/skills/pre-submission-reviewer/SKILL.md +247 -0
  699. package/dist/skills/pre-submission-reviewer/references/forbidden-patterns.md +163 -0
  700. package/dist/skills/pre-submission-reviewer/references/grammar-rules.md +135 -0
  701. package/dist/skills/pre-submission-reviewer/references/latex-rules.md +158 -0
  702. package/dist/skills/pre-submission-reviewer/references/logic-and-structure.md +125 -0
  703. package/dist/skills/pre-submission-reviewer/references/section-guides.md +169 -0
  704. package/dist/skills/prior-art-search/SKILL.md +146 -0
  705. package/dist/skills/proof-checker/SKILL.md +499 -0
  706. package/dist/skills/proof-writer/SKILL.md +222 -0
  707. package/dist/skills/qzcli/SKILL.md +316 -0
  708. package/dist/skills/rebuttal/SKILL.md +304 -0
  709. package/dist/skills/render-html/SKILL.md +299 -0
  710. package/dist/skills/render-html/scripts/render_html.py +905 -0
  711. package/dist/skills/render-html/scripts/templates/academic.html +342 -0
  712. package/dist/skills/render-html/scripts/templates/dashboard.html +333 -0
  713. package/dist/skills/research-lit/SKILL.md +353 -0
  714. package/dist/skills/research-paper-writing/SKILL.md +99 -0
  715. package/dist/skills/research-paper-writing/agents/openai.yaml +4 -0
  716. package/dist/skills/research-paper-writing/references/abstract.md +102 -0
  717. package/dist/skills/research-paper-writing/references/conclusion.md +35 -0
  718. package/dist/skills/research-paper-writing/references/does-my-writing-flow-source.md +45 -0
  719. package/dist/skills/research-paper-writing/references/examples/abstract/template-a.md +21 -0
  720. package/dist/skills/research-paper-writing/references/examples/abstract/template-b.md +34 -0
  721. package/dist/skills/research-paper-writing/references/examples/abstract/template-c.md +28 -0
  722. package/dist/skills/research-paper-writing/references/examples/abstract-examples.md +13 -0
  723. package/dist/skills/research-paper-writing/references/examples/index.md +21 -0
  724. package/dist/skills/research-paper-writing/references/examples/introduction/novel-task-challenge-decomposition.md +18 -0
  725. package/dist/skills/research-paper-writing/references/examples/introduction/pipeline-not-recommended-abstract-only.md +30 -0
  726. package/dist/skills/research-paper-writing/references/examples/introduction/pipeline-version-1-one-contribution-multi-advantages.md +30 -0
  727. package/dist/skills/research-paper-writing/references/examples/introduction/pipeline-version-2-two-contributions.md +34 -0
  728. package/dist/skills/research-paper-writing/references/examples/introduction/pipeline-version-3-new-module-on-existing-pipeline.md +18 -0
  729. package/dist/skills/research-paper-writing/references/examples/introduction/pipeline-version-4-observation-driven.md +16 -0
  730. package/dist/skills/research-paper-writing/references/examples/introduction/technical-challenge-version-1-existing-task.md +32 -0
  731. package/dist/skills/research-paper-writing/references/examples/introduction/technical-challenge-version-2-existing-task-insight-backed-by-traditional.md +33 -0
  732. package/dist/skills/research-paper-writing/references/examples/introduction/technical-challenge-version-3-novel-task.md +21 -0
  733. package/dist/skills/research-paper-writing/references/examples/introduction/version-1-task-then-application.md +14 -0
  734. package/dist/skills/research-paper-writing/references/examples/introduction/version-2-application-first.md +10 -0
  735. package/dist/skills/research-paper-writing/references/examples/introduction/version-3-general-to-specific-setting.md +14 -0
  736. package/dist/skills/research-paper-writing/references/examples/introduction/version-4-open-with-challenge.md +20 -0
  737. package/dist/skills/research-paper-writing/references/examples/introduction-examples.md +25 -0
  738. package/dist/skills/research-paper-writing/references/examples/method/example-of-the-three-elements.md +67 -0
  739. package/dist/skills/research-paper-writing/references/examples/method/method-writing-common-issues-note.md +10 -0
  740. package/dist/skills/research-paper-writing/references/examples/method/module-design-instant-ngp.md +55 -0
  741. package/dist/skills/research-paper-writing/references/examples/method/module-motivation-patterns.md +15 -0
  742. package/dist/skills/research-paper-writing/references/examples/method/module-triad-neural-body.md +19 -0
  743. package/dist/skills/research-paper-writing/references/examples/method/neural-body-annotated-figure-text.md +66 -0
  744. package/dist/skills/research-paper-writing/references/examples/method/overview-template.md +30 -0
  745. package/dist/skills/research-paper-writing/references/examples/method/pre-writing-questions.md +17 -0
  746. package/dist/skills/research-paper-writing/references/examples/method/section-skeleton.md +9 -0
  747. package/dist/skills/research-paper-writing/references/examples/method-examples.md +24 -0
  748. package/dist/skills/research-paper-writing/references/experiments.md +102 -0
  749. package/dist/skills/research-paper-writing/references/introduction.md +408 -0
  750. package/dist/skills/research-paper-writing/references/method.md +196 -0
  751. package/dist/skills/research-paper-writing/references/paper-review.md +86 -0
  752. package/dist/skills/research-paper-writing/references/related-work.md +41 -0
  753. package/dist/skills/research-pipeline/SKILL.md +257 -0
  754. package/dist/skills/research-proposal/SKILL.md +573 -0
  755. package/dist/skills/research-proposal/assets/proposal_scaffold_en.md +323 -0
  756. package/dist/skills/research-proposal/assets/proposal_scaffold_zh.md +346 -0
  757. package/dist/skills/research-proposal/references/DOMAIN_TEMPLATES.md +518 -0
  758. package/dist/skills/research-proposal/references/LITERATURE_WORKFLOW.md +529 -0
  759. package/dist/skills/research-proposal/references/QUALITY_CHECKLIST.md +325 -0
  760. package/dist/skills/research-proposal/references/STRUCTURE_GUIDE.md +398 -0
  761. package/dist/skills/research-proposal/references/WRITING_STYLE_GUIDE.md +661 -0
  762. package/dist/skills/research-refine/SKILL.md +716 -0
  763. package/dist/skills/research-refine-pipeline/SKILL.md +186 -0
  764. package/dist/skills/research-review/SKILL.md +119 -0
  765. package/dist/skills/research-wiki/SKILL.md +373 -0
  766. package/dist/skills/resubmit-pipeline/SKILL.md +427 -0
  767. package/dist/skills/result-to-claim/SKILL.md +190 -0
  768. package/dist/skills/run-experiment/SKILL.md +227 -0
  769. package/dist/skills/scientific-figure-making/SKILL.md +38 -0
  770. package/dist/skills/scientific-figure-making/references/api.md +152 -0
  771. package/dist/skills/scientific-figure-making/references/common-patterns.md +74 -0
  772. package/dist/skills/scientific-figure-making/references/demos.md +30 -0
  773. package/dist/skills/scientific-figure-making/references/design-theory.md +138 -0
  774. package/dist/skills/scientific-figure-making/references/tutorials.md +135 -0
  775. package/dist/skills/semantic-scholar/SKILL.md +219 -0
  776. package/dist/skills/serverless-modal/SKILL.md +324 -0
  777. package/dist/skills/slides-polish/SKILL.md +563 -0
  778. package/dist/skills/specification-writing/SKILL.md +211 -0
  779. package/dist/skills/strategist/SKILL.md +670 -0
  780. package/dist/skills/strategist/references/quality_standards.md +336 -0
  781. package/dist/skills/strategist/references/search_strategy.md +459 -0
  782. package/dist/skills/strategist/scripts/evaluate_samples.py +300 -0
  783. package/dist/skills/strategist/scripts/gap_analysis.py +399 -0
  784. package/dist/skills/system-profile/SKILL.md +103 -0
  785. package/dist/skills/tech-paper-template/SKILL.md +192 -0
  786. package/dist/skills/tech-paper-template/references/consistency-checks.md +166 -0
  787. package/dist/skills/tech-paper-template/references/paper-types.md +127 -0
  788. package/dist/skills/tech-paper-template/references/thinking-template.md +194 -0
  789. package/dist/skills/tech-paper-template/references/worked-examples.md +124 -0
  790. package/dist/skills/training-check/SKILL.md +83 -0
  791. package/dist/skills/vast-gpu/SKILL.md +380 -0
  792. package/dist/skills/vibe-research-workflow/SKILL.md +189 -0
  793. package/dist/skills/vibe-research-workflow/references/behavior-guidelines.md +149 -0
  794. package/dist/skills/vibe-research-workflow/references/tool-selection.md +144 -0
  795. package/dist/skills/vibe-research-workflow/references/vibe-coding.md +154 -0
  796. package/dist/skills/vibe-research-workflow/references/vibe-figure.md +131 -0
  797. package/dist/skills/vibe-research-workflow/references/vibe-writing.md +181 -0
  798. package/dist/skills/wiki-enrich/SKILL.md +254 -0
  799. package/dist/skills/writing-systems-papers/SKILL.md +184 -0
  800. package/package.json +1 -1
@@ -0,0 +1,2783 @@
1
+ import type {
2
+ CommunityId,
3
+ GraphFocusInput,
4
+ GraphTypeFilters,
5
+ GraphData,
6
+ GraphDiff,
7
+ GraphNode,
8
+ GraphOpenPagePayload,
9
+ NodeId,
10
+ PinMap,
11
+ SelectionInput,
12
+ ThemeId,
13
+ WikiPath
14
+ } from "../types";
15
+ import { createLiveGraphSimulation, PinState, pinsToPositions, type LiveGraphSimulation } from "../sim";
16
+ import { resolveSelectionForCapabilities } from "../select";
17
+ import { getCommunityColor, getThemeTokens, themeTokensToCssVars } from "../themes";
18
+ import {
19
+ buildRenderableGraph,
20
+ createRenderPathCache,
21
+ makeEdgePathFromPoints,
22
+ nodeDisplayModeForDensity,
23
+ screenEffectiveDensityMode,
24
+ type DensityMode,
25
+ type NodeDisplayMode,
26
+ type RenderableGraph,
27
+ type RenderableNode,
28
+ type RenderPositionMap
29
+ } from "./model";
30
+ import { graphNodeTypeLabel, wikiPathForGraphNode } from "../graph-node";
31
+ import { buildCommunityLegend, type CommunityLegendRow } from "./legend";
32
+ import {
33
+ DEFAULT_RENDERER_VIEWPORT,
34
+ applyRendererViewportTransform,
35
+ centerRendererViewportOnPoint,
36
+ createViewportFrameCommitter,
37
+ fitRendererViewportToPoints,
38
+ panRendererViewport,
39
+ rendererViewportToMinimapRect,
40
+ viewportAfterResize,
41
+ viewportAfterWheelZoom,
42
+ type RendererViewport
43
+ } from "./viewport";
44
+ import { resolveGraphSearchState, resolveNextGraphSearchFocus } from "./search";
45
+ import { buildHoverPreview, type GraphHoverPreview } from "./preview";
46
+ import {
47
+ nextToolbarPanelState,
48
+ readToolbarPanelState,
49
+ shouldBlankClickCloseToolbar,
50
+ toolbarPanelStateAfterBlankClick,
51
+ writeToolbarPanelState,
52
+ type GraphToolbarPanelState
53
+ } from "./toolbar";
54
+
55
+ // 聚焦单个社区时,子集包围盒常很小;用默认 4× fit 会把少量节点放大成糊屏巨卡。
56
+ // 聚焦 fit 限制到适度放大,让节点保持可读、社区居中留白(镜头推进而非贴脸)。
57
+ // 大社区包围盒大、fit 算出的 scale 本就 < 此上限,不受影响。
58
+ const FOCUS_FIT_MAX_SCALE = 1.5;
59
+
60
+ interface StaticRendererOptions {
61
+ data: GraphData;
62
+ pins?: PinMap;
63
+ theme: ThemeId;
64
+ onOpenPage?: (payload: GraphOpenPagePayload) => void;
65
+ onSelectionChange?: (selection: SelectionInput) => void;
66
+ onSelectionClear?: () => void;
67
+ persistPins?: (pins: PinMap) => Promise<void>;
68
+ onDragStateChange?: (dragging: boolean) => void;
69
+ toolbarContainer?: HTMLElement | null;
70
+ focus?: GraphFocusInput;
71
+ typeFilters?: GraphTypeFilters;
72
+ live?: boolean;
73
+ }
74
+
75
+ export interface StaticGraphRenderer {
76
+ root: HTMLElement;
77
+ graph: RenderableGraph;
78
+ render(next?: Partial<StaticRendererOptions> & { selectedNodeId?: string | null; selection?: SelectionInput | null }): void;
79
+ applyDiff(diff: GraphDiff, options?: { reducedMotion?: boolean; durationMs?: number }): Promise<void>;
80
+ isDragging(): boolean;
81
+ setTheme(theme: ThemeId): void;
82
+ setPins(pins: PinMap): void;
83
+ focusNode(pathOrId: WikiPath): void;
84
+ focusCommunity(id: CommunityId): void;
85
+ setTypeFilters(filters: GraphTypeFilters): void;
86
+ resetView(): void;
87
+ select(selection: SelectionInput): void;
88
+ clearSelection(): void;
89
+ clearInteraction(): void;
90
+ resetLayout(): void;
91
+ destroy(): void;
92
+ }
93
+
94
+ const SVG_NS = "http://www.w3.org/2000/svg";
95
+ const WORLD_WIDTH = 1000;
96
+ const WORLD_HEIGHT = 680;
97
+
98
+ interface PaintedGraphDom {
99
+ contentLayer: HTMLElement | null;
100
+ edgeElements: Map<string, SVGPathElement>;
101
+ communityWashElements: Map<string, SVGEllipseElement>;
102
+ nodeElements: Map<string, HTMLButtonElement>;
103
+ miniNodeElements: Map<string, SVGCircleElement>;
104
+ miniViewportElement: SVGRectElement | null;
105
+ basePoints: Map<string, { x: number; y: number }>;
106
+ readerElement: HTMLElement | null;
107
+ selectionElement: HTMLElement | null;
108
+ searchElement: HTMLElement | null;
109
+ searchInput: HTMLInputElement | null;
110
+ searchStatusElement: HTMLElement | null;
111
+ toolbarElement: HTMLElement | null;
112
+ toolbarPanelElement: HTMLElement | null;
113
+ legendElement: HTMLElement | null;
114
+ legendRows: Map<string, HTMLButtonElement>;
115
+ previewElement: HTMLElement | null;
116
+ }
117
+
118
+ export function createStaticGraphRenderer(container: HTMLElement, options: StaticRendererOptions): StaticGraphRenderer {
119
+ let data = options.data;
120
+ let pins = options.pins || {};
121
+ let theme = options.theme;
122
+ let selectedNodeId: string | null = null;
123
+ let selection: SelectionInput | null = null;
124
+ let manualNodeIds: NodeId[] = [];
125
+ let destroyed = false;
126
+ let simulation: LiveGraphSimulation | null = null;
127
+ let dom: PaintedGraphDom = emptyPaintedDom();
128
+ let viewport: RendererViewport = DEFAULT_RENDERER_VIEWPORT;
129
+ let activeDiff: GraphDiff | null = null;
130
+ let searchOpen = false;
131
+ let searchQuery = "";
132
+ let searchFocusedNodeId: NodeId | null = null;
133
+ let focus: GraphFocusInput = options.focus || null;
134
+ let typeFilters: GraphTypeFilters = options.typeFilters || {};
135
+ let availableTypeFilters: GraphTypeFilters = {};
136
+ let searchIndex: ReturnType<typeof resolveGraphSearchState>["searchIndex"] | undefined;
137
+ let hoveredCommunityId: string | null = null;
138
+ let previewNodeId: NodeId | null = null;
139
+ let previewEdgeId: string | null = null;
140
+ let previewTimer: ReturnType<typeof setTimeout> | null = null;
141
+ const pathCache = createRenderPathCache();
142
+ const root = document.createElement("div");
143
+ root.className = "llm-wiki-graph-engine";
144
+ root.dataset.llmWikiGraphRoot = "true";
145
+ root.tabIndex = 0;
146
+ container.replaceChildren(root);
147
+ const toolbarContainer = options.toolbarContainer || root;
148
+ const hasExternalToolbarContainer = toolbarContainer !== root;
149
+ ensureStaticRendererStyles(container.ownerDocument || document);
150
+ const ownerDocument = container.ownerDocument || document;
151
+ let legendCollapsed = readLegendCollapsed(ownerDocument);
152
+ let toolbarPanelState: GraphToolbarPanelState = readToolbarPanelState(ownerDocument.defaultView?.localStorage);
153
+ const handleDocumentKeydown = (event: KeyboardEvent) => {
154
+ const key = event.key.toLowerCase();
155
+ if ((event.metaKey || event.ctrlKey) && key === "f" && isGraphFocusActive()) {
156
+ event.preventDefault();
157
+ openSearch();
158
+ return;
159
+ }
160
+ if (event.key === "Escape" && (searchOpen || searchQuery || searchFocusedNodeId)) {
161
+ event.preventDefault();
162
+ event.stopPropagation();
163
+ closeSearch();
164
+ return;
165
+ }
166
+ if (event.key === "Escape" && shouldBlankClickCloseToolbar(toolbarPanelState)) {
167
+ event.preventDefault();
168
+ event.stopPropagation();
169
+ closeToolbarPanel();
170
+ return;
171
+ }
172
+ if (event.key !== "Escape" || !hasInteractionState()) return;
173
+ event.preventDefault();
174
+ event.stopPropagation();
175
+ if (focus) {
176
+ resetViewState();
177
+ return;
178
+ }
179
+ clearInteractionState();
180
+ };
181
+ ownerDocument.addEventListener("keydown", handleDocumentKeydown);
182
+ const viewportCommitter = createViewportFrameCommitter(commitViewport, root.ownerDocument.defaultView || undefined);
183
+ let blankPan: { pointerId: number; lastX: number; lastY: number; startX: number; startY: number; moved: boolean } | null = null;
184
+ let viewportAnimationTimer: ReturnType<typeof setTimeout> | null = null;
185
+ let lastEffectiveDensityMode: DensityMode | null = null;
186
+ let lastViewportSize = viewportSize();
187
+ let resizeObserver: ResizeObserver | null = null;
188
+
189
+ let graph = buildRenderableGraph(data, { pins, theme, selectedNodeId, selection, focus, typeFilters, pathCache });
190
+ let pinState = new PinState(graph, pins);
191
+ bindViewportHandlers();
192
+ bindResizeObserver();
193
+
194
+ function render(next: Partial<StaticRendererOptions> & { selectedNodeId?: string | null; selection?: SelectionInput | null } = {}): void {
195
+ assertActive();
196
+ data = next.data || data;
197
+ pins = next.pins || pins;
198
+ theme = next.theme || theme;
199
+ if (Object.hasOwn(next, "focus")) focus = next.focus || null;
200
+ if (Object.hasOwn(next, "typeFilters")) typeFilters = next.typeFilters || {};
201
+ if (Object.hasOwn(next, "selectedNodeId")) selectedNodeId = next.selectedNodeId || null;
202
+ if (Object.hasOwn(next, "selection")) selection = next.selection || null;
203
+ graph = buildRenderableGraph(data, { pins, theme, selectedNodeId, selection, focus, typeFilters, pathCache });
204
+ availableTypeFilters = graph.typeFilters;
205
+ searchIndex = undefined;
206
+ pinState = new PinState(graph, pins);
207
+ applyTheme(root, theme);
208
+ dom = paint(root, graph, theme, Boolean(options.onOpenPage), {
209
+ onCommunitySelect: (id) => {
210
+ selectCommunity(id);
211
+ },
212
+ onNodeClick: (id, additive) => {
213
+ if (!additive) {
214
+ manualNodeIds = [];
215
+ selection = null;
216
+ selectedNodeId = id;
217
+ options.onOpenPage?.(openPagePayloadForNode(data, id));
218
+ render({ selectedNodeId: id, selection: null });
219
+ return;
220
+ }
221
+ const nextSelection = shiftSelection(id, manualNodeIds.length ? manualNodeIds : selectedNodeIds(selection));
222
+ manualNodeIds = nextSelection.kind === "nodes" ? nextSelection.ids : nextSelection.kind === "node" ? [nextSelection.id] : [];
223
+ selection = nextSelection;
224
+ selectedNodeId = null;
225
+ options.onSelectionChange?.(nextSelection);
226
+ render({ selection: nextSelection });
227
+ },
228
+ onDragStart: (id, event) => {
229
+ if (!simulation) return;
230
+ simulation.beginDrag(id);
231
+ simulation.dragTo(id, eventToGraphPoint(root, event));
232
+ root.dataset.dragging = id;
233
+ options.onDragStateChange?.(true);
234
+ },
235
+ onDragMove: (id, event) => {
236
+ if (!simulation || root.dataset.dragging !== id) return;
237
+ simulation.dragTo(id, eventToGraphPoint(root, event));
238
+ },
239
+ onDragEnd: (id) => {
240
+ if (!simulation || root.dataset.dragging !== id) return;
241
+ const snapshot = simulation.endDrag({ keepFixed: true });
242
+ const position = snapshot.positions[id];
243
+ if (position) {
244
+ const nextState = pinState.pin(id, position);
245
+ pins = nextState.pins;
246
+ applyMotionFrame(snapshot.positions);
247
+ markPinnedNodes(nextState.pinnedNodeIds);
248
+ void options.persistPins?.(nextState.pins);
249
+ }
250
+ delete root.dataset.dragging;
251
+ options.onDragStateChange?.(false);
252
+ },
253
+ onNodeDoubleClick: (id) => {
254
+ if (!pinState.isPinned(id)) return false;
255
+ const nextState = pinState.unpin(id);
256
+ pins = nextState.pins;
257
+ simulation?.setFixed(id, null);
258
+ markPinnedNodes(nextState.pinnedNodeIds);
259
+ void options.persistPins?.(nextState.pins);
260
+ return true;
261
+ },
262
+ onNodePreviewEnter: (id) => {
263
+ scheduleHoverPreview(id);
264
+ },
265
+ onEdgePreviewEnter: (id) => {
266
+ showEdgeHoverPreview(id);
267
+ },
268
+ onNodePreviewLeave: () => {
269
+ clearHoverPreview();
270
+ }
271
+ });
272
+ lastEffectiveDensityMode = null;
273
+ mountSearchControl();
274
+ mountGraphToolbar();
275
+ applySearchQuery(searchQuery);
276
+ applyCommunityHover();
277
+ commitViewport(viewport);
278
+ if (activeDiff && root.dataset.diffState === "playing") markDiffElements(activeDiff);
279
+ renderReader();
280
+ renderSelectionPanel();
281
+ renderHoverPreview();
282
+ restartSimulation();
283
+ }
284
+
285
+ render();
286
+
287
+ return {
288
+ root,
289
+ get graph() {
290
+ return graph;
291
+ },
292
+ render,
293
+ applyDiff(diff, animationOptions = {}): Promise<void> {
294
+ assertActive();
295
+ return animateDiff(diff, animationOptions);
296
+ },
297
+ isDragging(): boolean {
298
+ return Boolean(root.dataset.dragging);
299
+ },
300
+ setTheme(nextTheme: ThemeId): void {
301
+ render({ theme: nextTheme });
302
+ },
303
+ setPins(nextPins: PinMap): void {
304
+ pins = nextPins;
305
+ render({ pins });
306
+ },
307
+ focusNode(pathOrId: WikiPath): void {
308
+ const node = graph.nodes.find((item) => item.id === pathOrId || item.sourcePath === pathOrId);
309
+ render({ selectedNodeId: node ? node.id : pathOrId });
310
+ root.dataset.focus = pathOrId;
311
+ },
312
+ focusCommunity(id: CommunityId): void {
313
+ focusCommunity(id);
314
+ },
315
+ setTypeFilters(filters: GraphTypeFilters): void {
316
+ render({ typeFilters: filters });
317
+ },
318
+ resetView(): void {
319
+ resetViewState();
320
+ },
321
+ select(nextSelection: SelectionInput): void {
322
+ manualNodeIds = nextSelection.kind === "nodes" ? nextSelection.ids : [];
323
+ render({ selection: nextSelection });
324
+ },
325
+ clearSelection(): void {
326
+ retreatFocusedView();
327
+ },
328
+ clearInteraction(): void {
329
+ clearInteractionState();
330
+ },
331
+ resetLayout(): void {
332
+ const nextState = pinState.reset();
333
+ pins = nextState.pins;
334
+ render({ pins });
335
+ void options.persistPins?.(nextState.pins);
336
+ },
337
+ destroy(): void {
338
+ if (destroyed) return;
339
+ destroyed = true;
340
+ simulation?.destroy();
341
+ simulation = null;
342
+ resizeObserver?.disconnect();
343
+ resizeObserver = null;
344
+ ownerDocument.removeEventListener("keydown", handleDocumentKeydown);
345
+ if (previewTimer) clearTimeout(previewTimer);
346
+ if (viewportAnimationTimer) clearTimeout(viewportAnimationTimer);
347
+ pathCache.clear();
348
+ root.remove();
349
+ if (hasExternalToolbarContainer && dom.toolbarElement && toolbarContainer.contains(dom.toolbarElement)) {
350
+ toolbarContainer.replaceChildren();
351
+ }
352
+ }
353
+ };
354
+
355
+ function assertActive(): void {
356
+ if (destroyed) throw new Error("Graph renderer has been destroyed");
357
+ }
358
+
359
+ function clearInteractionState(): void {
360
+ manualNodeIds = [];
361
+ selection = null;
362
+ selectedNodeId = null;
363
+ focus = null;
364
+ searchFocusedNodeId = null;
365
+ hoveredCommunityId = null;
366
+ previewNodeId = null;
367
+ previewEdgeId = null;
368
+ if (previewTimer) {
369
+ clearTimeout(previewTimer);
370
+ previewTimer = null;
371
+ }
372
+ delete root.dataset.focus;
373
+ options.onSelectionClear?.();
374
+ render({ selectedNodeId: null, selection: null, focus: null });
375
+ }
376
+
377
+ function hasInteractionState(): boolean {
378
+ return Boolean(selectedNodeId || selection || focus || root.dataset.focus);
379
+ }
380
+
381
+ function isGraphFocusActive(): boolean {
382
+ const active = ownerDocument.activeElement;
383
+ if (active === root || Boolean(active && root.contains(active))) return true;
384
+ return !isTextEditingElement(active);
385
+ }
386
+
387
+ function openSearch(): void {
388
+ searchOpen = true;
389
+ root.dataset.searchOpen = "true";
390
+ if (dom.searchElement) dom.searchElement.dataset.state = "open";
391
+ if (dom.searchInput) {
392
+ dom.searchInput.focus();
393
+ dom.searchInput.select();
394
+ }
395
+ }
396
+
397
+ function mountSearchControl(): void {
398
+ const control = createSearchControl(ownerDocument, {
399
+ open: searchOpen,
400
+ query: searchQuery,
401
+ onOpen: () => openSearch(),
402
+ onQuery: (query) => applySearchQuery(query),
403
+ onNext: () => focusNextSearchResult(),
404
+ onClose: () => closeSearch()
405
+ });
406
+ dom.searchElement = control.element;
407
+ dom.searchInput = control.input;
408
+ dom.searchStatusElement = control.status;
409
+ root.prepend(control.element);
410
+ root.dataset.searchOpen = searchOpen ? "true" : "false";
411
+ }
412
+
413
+ function applySearchQuery(query: string): void {
414
+ if (query !== searchQuery) searchFocusedNodeId = null;
415
+ searchQuery = query;
416
+ const state = resolveGraphSearchState(data.nodes, searchQuery, searchIndex);
417
+ searchIndex = state.searchIndex;
418
+ root.dataset.searchActive = state.query ? "true" : "false";
419
+ root.dataset.searchQuery = state.query;
420
+ if (!state.matchIds.includes(searchFocusedNodeId || "")) searchFocusedNodeId = null;
421
+ for (const node of state.nodes) {
422
+ const element = dom.nodeElements.get(node.id);
423
+ if (!element) continue;
424
+ element.dataset.searchState = node.searchState;
425
+ element.dataset.searchFocus = node.id === searchFocusedNodeId ? "true" : "false";
426
+ }
427
+ if (dom.searchInput && dom.searchInput.value !== searchQuery) dom.searchInput.value = searchQuery;
428
+ if (dom.searchStatusElement) {
429
+ const focusedIndex = searchFocusedNodeId ? state.matchIds.indexOf(searchFocusedNodeId) : -1;
430
+ dom.searchStatusElement.textContent = state.query
431
+ ? focusedIndex >= 0
432
+ ? `${focusedIndex + 1}/${state.matchIds.length}`
433
+ : `${state.matchIds.length} 个结果`
434
+ : "输入关键词";
435
+ }
436
+ }
437
+
438
+ function focusNextSearchResult(): void {
439
+ const state = resolveGraphSearchState(data.nodes, searchQuery, searchIndex);
440
+ searchIndex = state.searchIndex;
441
+ const next = resolveNextGraphSearchFocus(state.matchIds, searchFocusedNodeId);
442
+ searchFocusedNodeId = next.id;
443
+ if (!next.id) {
444
+ applySearchQuery(searchQuery);
445
+ return;
446
+ }
447
+ const node = graph.nodes.find((item) => item.id === next.id);
448
+ if (node) {
449
+ setViewportAnimating(true);
450
+ viewportCommitter.schedule(centerRendererViewportOnPoint(node.point, viewport, viewportSize()));
451
+ }
452
+ applySearchQuery(searchQuery);
453
+ }
454
+
455
+ function closeSearch(): void {
456
+ searchOpen = false;
457
+ searchFocusedNodeId = null;
458
+ if (dom.searchElement) dom.searchElement.dataset.state = "closed";
459
+ root.dataset.searchOpen = "false";
460
+ applySearchQuery("");
461
+ root.focus({ preventScroll: true });
462
+ }
463
+
464
+ function mountCommunityLegend(): void {
465
+ const rows = buildCommunityLegend(graph.communities, graph.nodes);
466
+ const legend = createCommunityLegend(ownerDocument, {
467
+ rows,
468
+ collapsed: legendCollapsed,
469
+ onToggle: () => {
470
+ legendCollapsed = !legendCollapsed;
471
+ writeLegendCollapsed(ownerDocument, legendCollapsed);
472
+ mountCommunityLegend();
473
+ },
474
+ onHover: (id) => {
475
+ hoveredCommunityId = id;
476
+ applyCommunityHover();
477
+ },
478
+ onSelect: (id) => selectCommunity(id)
479
+ });
480
+ dom.legendElement = legend.element;
481
+ dom.legendRows = legend.rows;
482
+ root.dataset.legendCollapsed = legendCollapsed ? "true" : "false";
483
+ }
484
+
485
+ function mountGraphToolbar(): void {
486
+ mountCommunityLegend();
487
+ const toolbar = createGraphToolbar(ownerDocument, {
488
+ panelState: toolbarPanelState,
489
+ typeFilters: graph.typeFilters,
490
+ onPanelToggle: (panel) => {
491
+ toolbarPanelState = nextToolbarPanelState(toolbarPanelState, panel);
492
+ writeToolbarPanelState(ownerDocument.defaultView?.localStorage, toolbarPanelState);
493
+ render();
494
+ },
495
+ onTypeFilterToggle: (type, enabled) => {
496
+ render({ typeFilters: { ...availableTypeFilters, [type]: enabled } });
497
+ },
498
+ onReset: () => {
499
+ resetViewState();
500
+ }
501
+ });
502
+ if (dom.legendElement) toolbar.filtersPanel.appendChild(dom.legendElement);
503
+ dom.toolbarElement = toolbar.element;
504
+ dom.toolbarPanelElement = toolbar.panel;
505
+ if (hasExternalToolbarContainer) {
506
+ toolbarContainer.replaceChildren(toolbar.element);
507
+ } else {
508
+ root.prepend(toolbar.element);
509
+ }
510
+ root.dataset.toolbarPanel = toolbarPanelState;
511
+ root.dataset.toolbarOpen = toolbarPanelState === "closed" ? "false" : "true";
512
+ toolbarContainer.dataset.toolbarPanel = toolbarPanelState;
513
+ toolbarContainer.dataset.toolbarOpen = toolbarPanelState === "closed" ? "false" : "true";
514
+ }
515
+
516
+ function closeToolbarPanel(): void {
517
+ toolbarPanelState = toolbarPanelStateAfterBlankClick(toolbarPanelState);
518
+ writeToolbarPanelState(ownerDocument.defaultView?.localStorage, toolbarPanelState);
519
+ if (dom.toolbarPanelElement) dom.toolbarPanelElement.dataset.state = toolbarPanelState;
520
+ if (dom.toolbarElement) dom.toolbarElement.dataset.panel = toolbarPanelState;
521
+ root.dataset.toolbarPanel = toolbarPanelState;
522
+ root.dataset.toolbarOpen = "false";
523
+ toolbarContainer.dataset.toolbarPanel = toolbarPanelState;
524
+ toolbarContainer.dataset.toolbarOpen = "false";
525
+ }
526
+
527
+ function selectCommunity(id: string): void {
528
+ manualNodeIds = [];
529
+ const nextSelection: SelectionInput = { kind: "community", id };
530
+ selection = nextSelection;
531
+ selectedNodeId = null;
532
+ options.onSelectionChange?.(nextSelection);
533
+ focusCommunity(id);
534
+ }
535
+
536
+ function focusCommunity(id: string): void {
537
+ render({ focus: { kind: "community", id }, selection });
538
+ const points = graph.nodes.map((node) => node.point);
539
+ if (!points.length) return;
540
+ setViewportAnimating(true);
541
+ viewportCommitter.schedule(fitRendererViewportToPoints(points, viewportSize(), { maxScale: FOCUS_FIT_MAX_SCALE }));
542
+ }
543
+
544
+ function resetViewState(): void {
545
+ manualNodeIds = [];
546
+ selection = null;
547
+ selectedNodeId = null;
548
+ focus = null;
549
+ searchFocusedNodeId = null;
550
+ hoveredCommunityId = null;
551
+ previewNodeId = null;
552
+ previewEdgeId = null;
553
+ delete root.dataset.focus;
554
+ options.onSelectionClear?.();
555
+ render({ selectedNodeId: null, selection: null, focus: null });
556
+ setViewportAnimating(true);
557
+ viewportCommitter.schedule(fitRendererViewportToPoints(graph.nodes.map((node) => node.point), viewportSize()));
558
+ }
559
+
560
+ function retreatFocusedView(): void {
561
+ manualNodeIds = [];
562
+ selection = null;
563
+ selectedNodeId = null;
564
+ searchFocusedNodeId = null;
565
+ hoveredCommunityId = null;
566
+ previewNodeId = null;
567
+ previewEdgeId = null;
568
+ delete root.dataset.focus;
569
+ options.onSelectionClear?.();
570
+ render({ selectedNodeId: null, selection: null, focus });
571
+ }
572
+
573
+ function applyCommunityHover(): void {
574
+ const active = hoveredCommunityId;
575
+ root.dataset.legendHover = active || "";
576
+ for (const [id, row] of dom.legendRows) {
577
+ row.dataset.communityState = active ? (id === active ? "active" : "faded") : "none";
578
+ }
579
+ const nodeCommunity = new Map<string, string>();
580
+ for (const [id, element] of dom.nodeElements) {
581
+ const community = element.dataset.community || "";
582
+ nodeCommunity.set(id, community);
583
+ element.dataset.communityState = active ? (community === active ? "active" : "faded") : "none";
584
+ }
585
+ for (const [id, element] of dom.communityWashElements) {
586
+ element.dataset.communityState = active ? (id === active ? "active" : "faded") : "none";
587
+ }
588
+ for (const edge of graph.edges) {
589
+ const element = dom.edgeElements.get(edge.id);
590
+ if (!element) continue;
591
+ const inCommunity = nodeCommunity.get(edge.source) === active && nodeCommunity.get(edge.target) === active;
592
+ element.dataset.communityState = active ? (inCommunity ? "active" : "faded") : "none";
593
+ }
594
+ }
595
+
596
+ function restartSimulation(): void {
597
+ simulation?.destroy();
598
+ simulation = null;
599
+ if (options.live === false || !graph.nodes.length) return;
600
+ simulation = createLiveGraphSimulation(graph, {
601
+ onTick: (snapshot) => applyMotionFrame(snapshot.positions)
602
+ });
603
+ for (const [id, position] of Object.entries(pinsToPositions(graph, pins))) {
604
+ simulation.setFixed(id, position);
605
+ }
606
+ simulation.startCold();
607
+ markPinnedNodes(pinState.snapshot().pinnedNodeIds);
608
+ }
609
+
610
+ function applyMotionFrame(positions: RenderPositionMap): void {
611
+ if (destroyed) return;
612
+ graph = buildRenderableGraph(data, { pins, theme, selectedNodeId, selection, focus, typeFilters, positions, pathCache });
613
+ const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
614
+ for (const node of graph.nodes) {
615
+ const element = dom.nodeElements.get(node.id);
616
+ const base = dom.basePoints.get(node.id);
617
+ if (!element || !base) continue;
618
+ const dx = node.point.x - base.x;
619
+ const dy = node.point.y - base.y;
620
+ element.style.translate = `calc(-50% + ${round(dx)}px) calc(-50% + ${round(dy)}px)`;
621
+ element.dataset.liveX = String(round(node.point.x));
622
+ element.dataset.liveY = String(round(node.point.y));
623
+ }
624
+ for (const edge of graph.edges) {
625
+ const element = dom.edgeElements.get(edge.id);
626
+ const source = nodeById.get(edge.source);
627
+ const target = nodeById.get(edge.target);
628
+ if (!element || !source || !target) continue;
629
+ element.setAttribute("d", makeEdgePathFromPoints(source.point, target.point, edge.curveOffset));
630
+ }
631
+ for (const community of graph.communities) {
632
+ const element = dom.communityWashElements.get(community.id);
633
+ if (!element || !community.wash) continue;
634
+ element.setAttribute("cx", String(community.wash.cx));
635
+ element.setAttribute("cy", String(community.wash.cy));
636
+ element.setAttribute("rx", String(community.wash.rx));
637
+ element.setAttribute("ry", String(community.wash.ry));
638
+ element.setAttribute("opacity", String(community.wash.opacity));
639
+ }
640
+ for (const miniNode of graph.minimap.nodes) {
641
+ const element = dom.miniNodeElements.get(miniNode.id);
642
+ if (!element) continue;
643
+ element.setAttribute("cx", String(miniNode.x));
644
+ element.setAttribute("cy", String(miniNode.y));
645
+ }
646
+ renderMotionOverlays();
647
+ }
648
+
649
+ function markPinnedNodes(pinnedNodeIds: string[]): void {
650
+ const pinned = new Set(pinnedNodeIds);
651
+ root.dataset.pinnedCount = String(pinned.size);
652
+ for (const [id, element] of dom.nodeElements) {
653
+ element.classList.toggle("is-pinned", pinned.has(id));
654
+ element.dataset.pinned = pinned.has(id) ? "true" : "false";
655
+ }
656
+ }
657
+
658
+ function bindViewportHandlers(): void {
659
+ root.addEventListener("wheel", (event) => {
660
+ if (!isBlankViewportTarget(event.target)) return;
661
+ event.preventDefault();
662
+ setViewportAnimating(false);
663
+ const rect = root.getBoundingClientRect();
664
+ viewportCommitter.schedule(viewportAfterWheelZoom(
665
+ viewport,
666
+ { deltaY: event.deltaY, deltaMode: event.deltaMode },
667
+ {
668
+ x: event.clientX - rect.left,
669
+ y: event.clientY - rect.top
670
+ },
671
+ viewportSize()
672
+ ));
673
+ }, { passive: false });
674
+ root.addEventListener("pointerdown", (event) => {
675
+ if (event.button !== 0 || !isBlankViewportTarget(event.target)) return;
676
+ // 空白按下一律先准备平移;究竟是"单击手势"(关弹层 / 退一层)还是"拖动平移",
677
+ // 留到 pointerup 时按"指针是否移动过"判定——否则在聚焦视图里一按下就退层会吃掉拖动。
678
+ root.focus({ preventScroll: true });
679
+ blankPan = {
680
+ pointerId: event.pointerId,
681
+ lastX: event.clientX,
682
+ lastY: event.clientY,
683
+ startX: event.clientX,
684
+ startY: event.clientY,
685
+ moved: false
686
+ };
687
+ setViewportAnimating(false);
688
+ root.setPointerCapture(event.pointerId);
689
+ });
690
+ root.addEventListener("pointermove", (event) => {
691
+ if (!blankPan || event.pointerId !== blankPan.pointerId) return;
692
+ const dx = event.clientX - blankPan.lastX;
693
+ const dy = event.clientY - blankPan.lastY;
694
+ const moved = blankPan.moved
695
+ || Math.abs(event.clientX - blankPan.startX) > 3
696
+ || Math.abs(event.clientY - blankPan.startY) > 3;
697
+ blankPan = { pointerId: event.pointerId, lastX: event.clientX, lastY: event.clientY, startX: blankPan.startX, startY: blankPan.startY, moved };
698
+ if (moved) root.dataset.viewportDragging = "true";
699
+ viewportCommitter.schedule(panRendererViewport(viewport, { x: dx, y: dy }, viewportSize()));
700
+ });
701
+ const endPan = (event: PointerEvent) => {
702
+ if (!blankPan || event.pointerId !== blankPan.pointerId) return;
703
+ const wasClick = !blankPan.moved;
704
+ blankPan = null;
705
+ delete root.dataset.viewportDragging;
706
+ if (root.hasPointerCapture(event.pointerId)) root.releasePointerCapture(event.pointerId);
707
+ if (!wasClick) return;
708
+ // 真·单击空白(按下到抬起没拖动):关弹层 → 退一层(聚焦态),与拖动平移互不冲突
709
+ if (shouldBlankClickCloseToolbar(toolbarPanelState)) {
710
+ closeToolbarPanel();
711
+ return;
712
+ }
713
+ if (focus) retreatFocusedView();
714
+ };
715
+ root.addEventListener("pointerup", endPan);
716
+ root.addEventListener("pointercancel", endPan);
717
+ root.addEventListener("dblclick", (event) => {
718
+ if (!isBlankViewportTarget(event.target)) return;
719
+ event.preventDefault();
720
+ resetViewState();
721
+ });
722
+ }
723
+
724
+ function bindResizeObserver(): void {
725
+ const ViewResizeObserver = root.ownerDocument.defaultView?.ResizeObserver;
726
+ if (!ViewResizeObserver) return;
727
+ lastViewportSize = viewportSize();
728
+ resizeObserver = new ViewResizeObserver(() => {
729
+ const previous = lastViewportSize;
730
+ const next = viewportSize();
731
+ if (Math.abs(previous.width - next.width) < 1 && Math.abs(previous.height - next.height) < 1) return;
732
+ lastViewportSize = next;
733
+ const anchorPoint = selectedNodeId
734
+ ? graph.nodes.find((node) => node.id === selectedNodeId)?.point ?? null
735
+ : null;
736
+ setViewportAnimating(false);
737
+ commitViewport(viewportAfterResize(viewport, previous, next, { anchorPoint }));
738
+ });
739
+ resizeObserver.observe(root);
740
+ }
741
+
742
+ function commitViewport(nextViewport: RendererViewport): void {
743
+ viewport = nextViewport;
744
+ root.dataset.viewportScale = String(round(viewport.scale));
745
+ if (dom.contentLayer) applyRendererViewportTransform(dom.contentLayer, viewport);
746
+ updateEffectiveDensity();
747
+ updateMinimapViewport();
748
+ }
749
+
750
+ function updateEffectiveDensity(): void {
751
+ const densityMode = screenEffectiveDensityMode(graph.counts.visibleNodes, viewport.scale);
752
+ root.dataset.density = densityMode;
753
+ root.dataset.effectiveDensity = densityMode;
754
+ if (densityMode === lastEffectiveDensityMode) return;
755
+ lastEffectiveDensityMode = densityMode;
756
+ for (const node of graph.nodes) {
757
+ const element = dom.nodeElements.get(node.id);
758
+ if (!element) continue;
759
+ applyNodeDisplayMode(element, nodeDisplayModeForDensity(node, densityMode));
760
+ }
761
+ }
762
+
763
+ function renderMotionOverlays(): void {
764
+ if (dom.readerElement?.dataset.state === "open") renderReader();
765
+ if (dom.selectionElement?.dataset.state === "open") renderSelectionPanel();
766
+ if (previewNodeId || previewEdgeId || dom.previewElement?.dataset.state === "open") renderHoverPreview();
767
+ }
768
+
769
+ function updateMinimapViewport(): void {
770
+ if (!dom.miniViewportElement) return;
771
+ const rect = rendererViewportToMinimapRect(viewport, viewportSize());
772
+ dom.miniViewportElement.setAttribute("x", String(round(rect.x)));
773
+ dom.miniViewportElement.setAttribute("y", String(round(rect.y)));
774
+ dom.miniViewportElement.setAttribute("width", String(round(rect.width)));
775
+ dom.miniViewportElement.setAttribute("height", String(round(rect.height)));
776
+ }
777
+
778
+ function setViewportAnimating(enabled: boolean): void {
779
+ if (viewportAnimationTimer) {
780
+ clearTimeout(viewportAnimationTimer);
781
+ viewportAnimationTimer = null;
782
+ }
783
+ root.dataset.viewportAnimating = enabled ? "true" : "false";
784
+ dom.contentLayer?.classList.toggle("is-viewport-animating", enabled);
785
+ if (enabled) {
786
+ viewportAnimationTimer = setTimeout(() => setViewportAnimating(false), 240);
787
+ }
788
+ }
789
+
790
+ function viewportSize(): { width: number; height: number } {
791
+ const rect = root.getBoundingClientRect();
792
+ return {
793
+ width: Math.max(1, rect.width || WORLD_WIDTH),
794
+ height: Math.max(1, rect.height || WORLD_HEIGHT)
795
+ };
796
+ }
797
+
798
+ async function animateDiff(diff: GraphDiff, animationOptions: { reducedMotion?: boolean; durationMs?: number }): Promise<void> {
799
+ if (destroyed) return;
800
+ const reducedMotion = animationOptions.reducedMotion ?? prefersReducedMotion(root.ownerDocument || document);
801
+ activeDiff = diff;
802
+ root.dataset.diffState = reducedMotion ? "settled" : "playing";
803
+ root.dataset.diffAddedNodes = String(diff.addedNodes.length);
804
+ root.dataset.diffAddedEdges = String(diff.addedEdges.length);
805
+ root.dataset.diffRemovedNodes = String(diff.removedNodes.length);
806
+ root.dataset.diffNewCommunities = String(diff.newCommunities.length);
807
+ markDiffElements(diff);
808
+ if (reducedMotion) {
809
+ root.dataset.diffReducedMotion = "true";
810
+ settleDiffElements();
811
+ return;
812
+ }
813
+ delete root.dataset.diffReducedMotion;
814
+ const durationMs = clamp(animationOptions.durationMs ?? animationDurationMs(diff), 420, 3000);
815
+ await wait(durationMs);
816
+ if (!destroyed) settleDiffElements();
817
+ }
818
+
819
+ function markDiffElements(diff: GraphDiff): void {
820
+ const addedNodes = new Set(diff.addedNodes);
821
+ const removedNodes = new Set(diff.removedNodes);
822
+ const recoloredNodes = new Set(diff.recoloredNodes.map((item) => item.id));
823
+ const addedEdges = new Set(diff.addedEdges);
824
+ const removedEdges = new Set(diff.removedEdges);
825
+ const newCommunities = new Set(diff.newCommunities);
826
+ for (const [id, element] of dom.nodeElements) {
827
+ element.classList.toggle("is-diff-added", addedNodes.has(id));
828
+ element.classList.toggle("is-diff-removed", removedNodes.has(id));
829
+ element.classList.toggle("is-diff-recolored", recoloredNodes.has(id));
830
+ const delay = diff.addedNodes.indexOf(id);
831
+ element.style.setProperty("--diff-delay", delay >= 0 ? `${Math.min(delay * 55, 550)}ms` : "0ms");
832
+ const anchor = addedNodes.has(id) ? semanticAnchorForNode(id) : null;
833
+ if (anchor) {
834
+ element.style.setProperty("--diff-anchor-dx", `${round(anchor.x - (graph.nodes.find((node) => node.id === id)?.point.x ?? anchor.x))}px`);
835
+ element.style.setProperty("--diff-anchor-dy", `${round(anchor.y - (graph.nodes.find((node) => node.id === id)?.point.y ?? anchor.y))}px`);
836
+ } else {
837
+ element.style.removeProperty("--diff-anchor-dx");
838
+ element.style.removeProperty("--diff-anchor-dy");
839
+ }
840
+ }
841
+ for (const [id, element] of dom.edgeElements) {
842
+ element.classList.toggle("is-diff-added", addedEdges.has(id));
843
+ element.classList.toggle("is-diff-removed", removedEdges.has(id));
844
+ if (addedEdges.has(id)) {
845
+ const length = Math.max(1, Math.ceil(typeof element.getTotalLength === "function" ? element.getTotalLength() : 180));
846
+ element.style.setProperty("--diff-edge-length", String(length));
847
+ } else {
848
+ element.style.removeProperty("--diff-edge-length");
849
+ }
850
+ }
851
+ for (const [id, element] of dom.communityWashElements) {
852
+ element.classList.toggle("is-diff-new-community", newCommunities.has(id));
853
+ }
854
+ }
855
+
856
+ function settleDiffElements(): void {
857
+ activeDiff = null;
858
+ root.dataset.diffState = "settled";
859
+ for (const element of dom.nodeElements.values()) {
860
+ element.classList.remove("is-diff-added", "is-diff-removed", "is-diff-recolored");
861
+ element.style.removeProperty("--diff-anchor-dx");
862
+ element.style.removeProperty("--diff-anchor-dy");
863
+ element.style.removeProperty("--diff-delay");
864
+ }
865
+ for (const element of dom.edgeElements.values()) {
866
+ element.classList.remove("is-diff-added", "is-diff-removed");
867
+ element.style.removeProperty("--diff-edge-length");
868
+ }
869
+ for (const element of dom.communityWashElements.values()) {
870
+ element.classList.remove("is-diff-new-community");
871
+ }
872
+ }
873
+
874
+ function semanticAnchorForNode(id: NodeId): { x: number; y: number } | null {
875
+ const node = graph.nodes.find((item) => item.id === id);
876
+ if (!node) return null;
877
+ const neighborId = graph.edges
878
+ .filter((edge) => edge.source === id || edge.target === id)
879
+ .map((edge) => edge.source === id ? edge.target : edge.source)
880
+ .find((candidate) => candidate !== id);
881
+ const neighbor = neighborId ? graph.nodes.find((item) => item.id === neighborId) : null;
882
+ if (neighbor) return neighbor.point;
883
+ return {
884
+ x: node.point.x < WORLD_WIDTH / 2 ? -80 : WORLD_WIDTH + 80,
885
+ y: clamp(node.point.y, 80, WORLD_HEIGHT - 80)
886
+ };
887
+ }
888
+
889
+ function renderReader(): void {
890
+ const reader = dom.readerElement;
891
+ if (!reader) return;
892
+ const selected = graph.selectedNodeId ? graph.nodes.find((node) => node.id === graph.selectedNodeId) : null;
893
+ const rawNode = selected ? data.nodes.find((node) => node.id === selected.id) : null;
894
+ reader.dataset.state = selected ? "open" : "closed";
895
+ reader.replaceChildren();
896
+ if (!selected || !rawNode) {
897
+ const empty = document.createElement("p");
898
+ empty.className = "graph-reader-empty";
899
+ empty.textContent = "选择一个节点查看内容";
900
+ reader.appendChild(empty);
901
+ return;
902
+ }
903
+
904
+ const header = document.createElement("div");
905
+ header.className = "graph-reader-header";
906
+ const title = document.createElement("div");
907
+ title.className = "graph-reader-title";
908
+ title.textContent = selected.label;
909
+ const payload = openPagePayloadForNode(data, selected.id);
910
+ const meta = document.createElement("div");
911
+ meta.className = "graph-reader-meta";
912
+ for (const item of graphReaderMetaItems(payload.node)) {
913
+ const tag = document.createElement("span");
914
+ tag.textContent = item;
915
+ meta.appendChild(tag);
916
+ }
917
+ const close = document.createElement("button");
918
+ close.type = "button";
919
+ close.className = "graph-reader-close";
920
+ close.setAttribute("aria-label", "关闭阅读面板");
921
+ close.textContent = "×";
922
+ close.addEventListener("click", () => clearInteractionState());
923
+ header.append(title, meta, close);
924
+
925
+ const body = document.createElement("div");
926
+ body.className = "graph-reader-body";
927
+ if (payload.node.type === "source" && payload.node.sourcePath) {
928
+ const sourceLink = document.createElement("a");
929
+ sourceLink.className = "graph-reader-source";
930
+ sourceLink.href = payload.node.sourcePath;
931
+ sourceLink.textContent = payload.node.sourcePath;
932
+ body.appendChild(sourceLink);
933
+ }
934
+ const content = String(rawNode.content || rawNode.summary || selected.label);
935
+ const rendered = renderMarkdown(content);
936
+ if (rendered) {
937
+ const article = document.createElement("article");
938
+ article.className = "graph-reader-markdown";
939
+ article.innerHTML = rendered;
940
+ body.appendChild(article);
941
+ } else {
942
+ const pre = document.createElement("pre");
943
+ pre.textContent = content;
944
+ body.appendChild(pre);
945
+ }
946
+ reader.append(header, body);
947
+ }
948
+
949
+ function renderSelectionPanel(): void {
950
+ const panel = dom.selectionElement;
951
+ if (!panel) return;
952
+ panel.replaceChildren();
953
+ panel.dataset.state = selection ? "open" : "closed";
954
+ if (!selection) {
955
+ const empty = document.createElement("p");
956
+ empty.className = "graph-selection-empty";
957
+ empty.textContent = "Shift+点击 可选择多个节点";
958
+ panel.appendChild(empty);
959
+ return;
960
+ }
961
+
962
+ const resolved = resolveSelectionForCapabilities(data, selection, { canAsk: false });
963
+ const selectedNodes = resolved.nodeIds
964
+ .map((id) => data.nodes.find((node) => node.id === id))
965
+ .filter((node): node is GraphNode => Boolean(node));
966
+
967
+ const header = document.createElement("div");
968
+ header.className = "graph-selection-header";
969
+ const title = document.createElement("div");
970
+ title.className = "graph-selection-title";
971
+ title.textContent = offlineSelectionTitle(selection, selectedNodes.length);
972
+ const close = document.createElement("button");
973
+ close.type = "button";
974
+ close.className = "graph-selection-close";
975
+ close.setAttribute("aria-label", "关闭选区面板");
976
+ close.textContent = "×";
977
+ close.addEventListener("click", () => clearInteractionState());
978
+ header.append(title, close);
979
+
980
+ const hint = document.createElement("div");
981
+ hint.className = "graph-selection-hint";
982
+ hint.textContent = "Shift+点击 增删节点";
983
+
984
+ const facts = document.createElement("div");
985
+ facts.className = "graph-selection-facts";
986
+ facts.append(
987
+ createSelectionFact("页面", resolved.facts.pageCount),
988
+ createSelectionFact("内部关联", resolved.facts.internalLinkCount),
989
+ createSelectionFact("社区", resolved.facts.communityCount),
990
+ createSelectionFact("孤立页", resolved.facts.isolatedCount)
991
+ );
992
+
993
+ const list = document.createElement("ol");
994
+ list.className = "graph-selection-pages";
995
+ for (const node of selectedNodes) {
996
+ const item = document.createElement("li");
997
+ item.className = "graph-selection-page";
998
+ const name = document.createElement("span");
999
+ name.className = "graph-selection-page-title";
1000
+ name.textContent = node.label || node.id;
1001
+ const path = document.createElement("span");
1002
+ path.className = "graph-selection-page-path";
1003
+ path.textContent = wikiPathForGraphNode(node);
1004
+ item.append(name, path);
1005
+ list.appendChild(item);
1006
+ }
1007
+
1008
+ panel.append(header, hint, facts, list);
1009
+ }
1010
+
1011
+ function scheduleHoverPreview(id: NodeId): void {
1012
+ if (previewTimer) clearTimeout(previewTimer);
1013
+ previewTimer = setTimeout(() => {
1014
+ previewTimer = null;
1015
+ previewNodeId = id;
1016
+ previewEdgeId = null;
1017
+ renderHoverPreview();
1018
+ }, 300);
1019
+ }
1020
+
1021
+ function showEdgeHoverPreview(id: string): void {
1022
+ if (previewTimer) {
1023
+ clearTimeout(previewTimer);
1024
+ previewTimer = null;
1025
+ }
1026
+ previewNodeId = null;
1027
+ previewEdgeId = id;
1028
+ renderHoverPreview();
1029
+ }
1030
+
1031
+ function clearHoverPreview(): void {
1032
+ if (previewTimer) {
1033
+ clearTimeout(previewTimer);
1034
+ previewTimer = null;
1035
+ }
1036
+ if (!previewNodeId && !previewEdgeId) return;
1037
+ previewNodeId = null;
1038
+ previewEdgeId = null;
1039
+ renderHoverPreview();
1040
+ }
1041
+
1042
+ function renderHoverPreview(): void {
1043
+ const preview = dom.previewElement;
1044
+ if (!preview) return;
1045
+ const edge = previewEdgeId ? graph.edges.find((item) => item.id === previewEdgeId) : null;
1046
+ const rawNode = previewNodeId ? data.nodes.find((node) => node.id === previewNodeId) : null;
1047
+ const renderedNode = previewNodeId ? graph.nodes.find((node) => node.id === previewNodeId) : null;
1048
+ preview.replaceChildren();
1049
+ preview.dataset.kind = edge ? "edge" : "node";
1050
+ if (edge) {
1051
+ preview.dataset.state = "open";
1052
+ preview.append(createEdgeHoverPreviewContent(edge.relationType, edge.confidence));
1053
+ positionEdgeHoverPreview(preview, edge);
1054
+ return;
1055
+ }
1056
+ preview.dataset.state = rawNode && renderedNode ? "open" : "closed";
1057
+ if (!rawNode || !renderedNode) return;
1058
+ const content = buildHoverPreview(rawNode);
1059
+ preview.append(createHoverPreviewContent(content));
1060
+ positionHoverPreview(preview, renderedNode);
1061
+ }
1062
+
1063
+ function positionHoverPreview(preview: HTMLElement, node: RenderableNode): void {
1064
+ const rootRect = root.getBoundingClientRect();
1065
+ const previewRect = preview.getBoundingClientRect();
1066
+ const margin = 12;
1067
+ const nodeX = rootRect.width * node.x / 100;
1068
+ const nodeY = rootRect.height * node.y / 100;
1069
+ const preferredLeft = nodeX + 18;
1070
+ const preferredTop = nodeY - previewRect.height - 24;
1071
+ const maxLeft = Math.max(margin, rootRect.width - previewRect.width - margin);
1072
+ const maxTop = Math.max(margin, rootRect.height - previewRect.height - margin);
1073
+ const left = clamp(preferredLeft, margin, maxLeft);
1074
+ const top = clamp(preferredTop, margin, maxTop);
1075
+ preview.style.left = `${left}px`;
1076
+ preview.style.top = `${top}px`;
1077
+ }
1078
+
1079
+ function positionEdgeHoverPreview(preview: HTMLElement, edge: RenderableGraph["edges"][number]): void {
1080
+ const rootRect = root.getBoundingClientRect();
1081
+ const previewRect = preview.getBoundingClientRect();
1082
+ const source = graph.nodes.find((node) => node.id === edge.source);
1083
+ const target = graph.nodes.find((node) => node.id === edge.target);
1084
+ const margin = 12;
1085
+ const sourceX = source ? rootRect.width * source.x / 100 : rootRect.width / 2;
1086
+ const sourceY = source ? rootRect.height * source.y / 100 : rootRect.height / 2;
1087
+ const targetX = target ? rootRect.width * target.x / 100 : rootRect.width / 2;
1088
+ const targetY = target ? rootRect.height * target.y / 100 : rootRect.height / 2;
1089
+ const midX = (sourceX + targetX) / 2;
1090
+ const midY = (sourceY + targetY) / 2;
1091
+ const maxLeft = Math.max(margin, rootRect.width - previewRect.width - margin);
1092
+ const maxTop = Math.max(margin, rootRect.height - previewRect.height - margin);
1093
+ const left = clamp(midX + 16, margin, maxLeft);
1094
+ const top = clamp(midY - previewRect.height - 16, margin, maxTop);
1095
+ preview.style.left = `${left}px`;
1096
+ preview.style.top = `${top}px`;
1097
+ }
1098
+ }
1099
+
1100
+ interface DragHandlers {
1101
+ onCommunitySelect: (id: CommunityId) => void;
1102
+ onNodeClick: (id: NodeId, additive: boolean) => void;
1103
+ onDragStart: (id: string, event: PointerEvent) => void;
1104
+ onDragMove: (id: string, event: PointerEvent) => void;
1105
+ onDragEnd: (id: string, event: PointerEvent) => void;
1106
+ onNodeDoubleClick: (id: string) => boolean;
1107
+ onNodePreviewEnter: (id: NodeId) => void;
1108
+ onEdgePreviewEnter: (id: string) => void;
1109
+ onNodePreviewLeave: () => void;
1110
+ }
1111
+
1112
+ function paint(
1113
+ root: HTMLElement,
1114
+ graph: RenderableGraph,
1115
+ theme: ThemeId,
1116
+ hasHostReader: boolean,
1117
+ dragHandlers: DragHandlers
1118
+ ): PaintedGraphDom {
1119
+ root.replaceChildren();
1120
+ root.dataset.theme = theme;
1121
+ root.dataset.baseDensity = graph.densityMode;
1122
+ const painted = emptyPaintedDom();
1123
+ const contentLayer = document.createElement("div");
1124
+ contentLayer.className = "graph-content-layer";
1125
+ contentLayer.dataset.viewportLayer = "true";
1126
+ painted.contentLayer = contentLayer;
1127
+
1128
+ const svg = document.createElementNS(SVG_NS, "svg");
1129
+ svg.setAttribute("class", "llm-wiki-graph-svg");
1130
+ svg.setAttribute("viewBox", "0 0 1000 680");
1131
+ svg.setAttribute("preserveAspectRatio", "none");
1132
+ svg.setAttribute("aria-hidden", "true");
1133
+
1134
+ const washLayer = document.createElementNS(SVG_NS, "g");
1135
+ washLayer.setAttribute("class", "community-wash-layer");
1136
+ for (const community of graph.communities) {
1137
+ if (!community.wash) continue;
1138
+ const ellipse = document.createElementNS(SVG_NS, "ellipse");
1139
+ ellipse.setAttribute("class", "community-wash");
1140
+ ellipse.setAttribute("cx", String(community.wash.cx));
1141
+ ellipse.setAttribute("cy", String(community.wash.cy));
1142
+ ellipse.setAttribute("rx", String(community.wash.rx));
1143
+ ellipse.setAttribute("ry", String(community.wash.ry));
1144
+ ellipse.setAttribute("fill", community.color);
1145
+ ellipse.setAttribute("opacity", String(community.wash.opacity));
1146
+ ellipse.dataset.communityId = community.id;
1147
+ ellipse.style.cursor = "pointer";
1148
+ ellipse.addEventListener("click", (event) => {
1149
+ event.stopPropagation();
1150
+ dragHandlers.onCommunitySelect(community.id);
1151
+ });
1152
+ washLayer.appendChild(ellipse);
1153
+ painted.communityWashElements.set(community.id, ellipse);
1154
+ }
1155
+ svg.appendChild(washLayer);
1156
+
1157
+ const edgeLayer = document.createElementNS(SVG_NS, "g");
1158
+ edgeLayer.setAttribute("class", "edge-layer");
1159
+ for (const edge of graph.edges) {
1160
+ const path = document.createElementNS(SVG_NS, "path");
1161
+ path.setAttribute("d", edge.path);
1162
+ path.setAttribute("class", `edge confidence-${edge.confidence} ${edge.relationClass}`);
1163
+ path.setAttribute("data-from", edge.source);
1164
+ path.setAttribute("data-to", edge.target);
1165
+ path.setAttribute("data-edge-id", edge.id);
1166
+ path.setAttribute("data-confidence", edge.confidence);
1167
+ path.setAttribute("data-relation-type", edge.relationType);
1168
+ path.setAttribute("aria-label", `${edge.relationType} · ${edgeConfidenceLabel(edge.confidence)}`);
1169
+ path.setAttribute("tabindex", "0");
1170
+ path.addEventListener("pointerenter", () => dragHandlers.onEdgePreviewEnter(edge.id));
1171
+ path.addEventListener("pointerleave", () => dragHandlers.onNodePreviewLeave());
1172
+ path.addEventListener("focus", () => dragHandlers.onEdgePreviewEnter(edge.id));
1173
+ path.addEventListener("blur", () => dragHandlers.onNodePreviewLeave());
1174
+ path.style.strokeWidth = String(edge.strokeWidth);
1175
+ path.style.opacity = String(edge.opacity);
1176
+ const title = document.createElementNS(SVG_NS, "title");
1177
+ title.textContent = `${edge.relationType} · ${edgeConfidenceLabel(edge.confidence)}`;
1178
+ path.appendChild(title);
1179
+ edgeLayer.appendChild(path);
1180
+ painted.edgeElements.set(edge.id, path);
1181
+ }
1182
+ svg.appendChild(edgeLayer);
1183
+ contentLayer.appendChild(svg);
1184
+
1185
+ const nodeLayer = document.createElement("div");
1186
+ nodeLayer.className = "node-layer";
1187
+ for (const node of graph.nodes) {
1188
+ const button = createNodeButton(node, dragHandlers);
1189
+ painted.nodeElements.set(node.id, button);
1190
+ painted.basePoints.set(node.id, node.point);
1191
+ nodeLayer.appendChild(button);
1192
+ }
1193
+ contentLayer.appendChild(nodeLayer);
1194
+ root.appendChild(contentLayer);
1195
+
1196
+ const preview = document.createElement("aside");
1197
+ preview.className = "graph-hover-preview";
1198
+ preview.dataset.state = "closed";
1199
+ preview.setAttribute("aria-live", "polite");
1200
+ root.appendChild(preview);
1201
+ painted.previewElement = preview;
1202
+
1203
+ const minimap = document.createElement("div");
1204
+ minimap.className = "mini-map";
1205
+ const miniSvg = document.createElementNS(SVG_NS, "svg");
1206
+ miniSvg.setAttribute("viewBox", "0 0 160 54");
1207
+ miniSvg.setAttribute("aria-hidden", "true");
1208
+ const miniPath = document.createElementNS(SVG_NS, "path");
1209
+ miniPath.setAttribute("d", graph.minimap.path);
1210
+ miniPath.setAttribute("fill", "none");
1211
+ miniPath.setAttribute("stroke", "var(--line)");
1212
+ miniPath.setAttribute("stroke-width", "1.4");
1213
+ miniSvg.appendChild(miniPath);
1214
+ const miniViewport = document.createElementNS(SVG_NS, "rect");
1215
+ miniViewport.setAttribute("class", "mini-map-viewport");
1216
+ miniViewport.setAttribute("data-mini-map-viewport", "true");
1217
+ miniViewport.setAttribute("x", "0");
1218
+ miniViewport.setAttribute("y", "0");
1219
+ miniViewport.setAttribute("width", "160");
1220
+ miniViewport.setAttribute("height", "54");
1221
+ miniSvg.appendChild(miniViewport);
1222
+ painted.miniViewportElement = miniViewport;
1223
+ for (const miniNode of graph.minimap.nodes) {
1224
+ const circle = document.createElementNS(SVG_NS, "circle");
1225
+ circle.setAttribute("cx", String(miniNode.x));
1226
+ circle.setAttribute("cy", String(miniNode.y));
1227
+ circle.setAttribute("r", String(miniNode.r));
1228
+ circle.setAttribute("fill", miniNode.fill);
1229
+ if (miniNode.selected) circle.classList.add("is-selected");
1230
+ miniSvg.appendChild(circle);
1231
+ painted.miniNodeElements.set(miniNode.id, circle);
1232
+ }
1233
+ minimap.appendChild(miniSvg);
1234
+ root.appendChild(minimap);
1235
+ if (!hasHostReader) {
1236
+ const reader = document.createElement("aside");
1237
+ reader.className = "graph-reader";
1238
+ reader.dataset.state = graph.selectedNodeId ? "open" : "closed";
1239
+ root.appendChild(reader);
1240
+ painted.readerElement = reader;
1241
+
1242
+ const selectionPanel = document.createElement("aside");
1243
+ selectionPanel.className = "graph-selection-panel";
1244
+ selectionPanel.dataset.state = "closed";
1245
+ root.appendChild(selectionPanel);
1246
+ painted.selectionElement = selectionPanel;
1247
+ }
1248
+ return painted;
1249
+ }
1250
+
1251
+ function createHoverPreviewContent(preview: GraphHoverPreview): HTMLElement {
1252
+ const article = document.createElement("article");
1253
+ article.className = "graph-hover-preview-card";
1254
+ const type = document.createElement("div");
1255
+ type.className = "graph-hover-preview-type";
1256
+ type.textContent = preview.typeLabel;
1257
+ const title = document.createElement("div");
1258
+ title.className = "graph-hover-preview-title";
1259
+ title.textContent = preview.title;
1260
+ article.append(type, title);
1261
+ if (preview.summary) {
1262
+ const summary = document.createElement("p");
1263
+ summary.className = "graph-hover-preview-summary";
1264
+ summary.textContent = preview.summary;
1265
+ article.appendChild(summary);
1266
+ }
1267
+ return article;
1268
+ }
1269
+
1270
+ function createEdgeHoverPreviewContent(relationType: string, confidence: string): HTMLElement {
1271
+ const article = document.createElement("article");
1272
+ article.className = "graph-hover-preview-card graph-edge-hover-card";
1273
+ const type = document.createElement("div");
1274
+ type.className = "graph-hover-preview-type";
1275
+ type.textContent = "关系";
1276
+ const title = document.createElement("div");
1277
+ title.className = "graph-hover-preview-title";
1278
+ title.textContent = relationType;
1279
+ const summary = document.createElement("p");
1280
+ summary.className = "graph-hover-preview-summary";
1281
+ summary.textContent = `置信度:${edgeConfidenceLabel(confidence)}`;
1282
+ article.append(type, title, summary);
1283
+ return article;
1284
+ }
1285
+
1286
+ function createSelectionFact(label: string, value: number): HTMLElement {
1287
+ const item = document.createElement("div");
1288
+ item.className = "graph-selection-fact";
1289
+ const number = document.createElement("strong");
1290
+ number.textContent = String(value);
1291
+ const text = document.createElement("span");
1292
+ text.textContent = label;
1293
+ item.append(number, text);
1294
+ return item;
1295
+ }
1296
+
1297
+ function offlineSelectionTitle(selection: SelectionInput, count: number): string {
1298
+ if (selection.kind === "community") return `社区选区 · ${count} 页`;
1299
+ if (selection.kind === "neighbors") return `相邻节点 · ${count} 页`;
1300
+ if (selection.kind === "node") return "选中页面";
1301
+ return `手动选区 · ${count} 页`;
1302
+ }
1303
+
1304
+ function createNodeButton(node: RenderableNode, dragHandlers: DragHandlers): HTMLButtonElement {
1305
+ const button = document.createElement("button");
1306
+ button.className = "node";
1307
+ if (node.unavailable) button.classList.add("is-disabled");
1308
+ applyNodeDisplayMode(button, node.displayMode);
1309
+ if (node.previewStart) button.classList.add("is-preview-start");
1310
+ if (!node.labelVisible) button.classList.add("is-label-hidden");
1311
+ button.type = "button";
1312
+ button.dataset.id = node.id;
1313
+ button.dataset.type = node.type;
1314
+ button.dataset.community = node.community;
1315
+ button.dataset.visualRole = node.visualRole;
1316
+ button.dataset.startNode = node.startNode ? "true" : "false";
1317
+ button.dataset.previewStart = node.previewStart ? "true" : "false";
1318
+ button.style.left = `${node.x}%`;
1319
+ button.style.top = `${node.y}%`;
1320
+ button.title = node.label;
1321
+ button.setAttribute("aria-pressed", node.selected ? "true" : "false");
1322
+ button.addEventListener("dblclick", (event) => {
1323
+ event.stopPropagation();
1324
+ dragHandlers.onNodeDoubleClick(node.id);
1325
+ });
1326
+ button.addEventListener("pointerenter", () => dragHandlers.onNodePreviewEnter(node.id));
1327
+ button.addEventListener("pointerleave", () => dragHandlers.onNodePreviewLeave());
1328
+ button.addEventListener("focus", () => dragHandlers.onNodePreviewEnter(node.id));
1329
+ button.addEventListener("blur", () => dragHandlers.onNodePreviewLeave());
1330
+ bindDragHandlers(button, node.id, dragHandlers);
1331
+
1332
+ const kind = document.createElement("span");
1333
+ kind.className = "node-kind";
1334
+ kind.textContent = node.kind;
1335
+ button.appendChild(kind);
1336
+
1337
+ const name = document.createElement("span");
1338
+ name.className = "node-name";
1339
+ name.textContent = node.label;
1340
+ button.appendChild(name);
1341
+
1342
+ const meta = document.createElement("span");
1343
+ meta.className = "node-meta";
1344
+ const spark = document.createElement("i");
1345
+ spark.className = "spark";
1346
+ meta.appendChild(spark);
1347
+ meta.append(node.unavailable ? "来源暂不可用" : String(Math.round(node.priority || node.weight || 0)));
1348
+ button.appendChild(meta);
1349
+
1350
+ return button;
1351
+ }
1352
+
1353
+ function applyNodeDisplayMode(button: HTMLButtonElement, displayMode: NodeDisplayMode): void {
1354
+ button.classList.toggle("is-compact", displayMode === "compact-card");
1355
+ button.classList.toggle("is-point", displayMode === "point");
1356
+ button.classList.toggle("is-overview", displayMode === "overview");
1357
+ button.dataset.densityMode = displayMode;
1358
+ }
1359
+
1360
+ function bindDragHandlers(button: HTMLButtonElement, nodeId: string, handlers: DragHandlers): void {
1361
+ let dragging = false;
1362
+ let moved = false;
1363
+ button.addEventListener("pointerdown", (event) => {
1364
+ if (event.button !== 0) return;
1365
+ dragging = true;
1366
+ moved = false;
1367
+ button.classList.add("is-dragging");
1368
+ button.setPointerCapture(event.pointerId);
1369
+ handlers.onDragStart(nodeId, event);
1370
+ });
1371
+ button.addEventListener("pointermove", (event) => {
1372
+ if (!dragging) return;
1373
+ moved = true;
1374
+ handlers.onDragMove(nodeId, event);
1375
+ });
1376
+ const end = (event: PointerEvent) => {
1377
+ if (!dragging) return;
1378
+ dragging = false;
1379
+ button.classList.remove("is-dragging");
1380
+ if (button.hasPointerCapture(event.pointerId)) button.releasePointerCapture(event.pointerId);
1381
+ handlers.onDragEnd(nodeId, event);
1382
+ };
1383
+ button.addEventListener("pointerup", end);
1384
+ button.addEventListener("pointercancel", end);
1385
+ button.addEventListener("click", (event) => {
1386
+ if (moved) {
1387
+ moved = false;
1388
+ return;
1389
+ }
1390
+ event.stopPropagation();
1391
+ handlers.onNodeClick(nodeId, event.shiftKey);
1392
+ });
1393
+ }
1394
+
1395
+ function selectedNodeIds(selection: SelectionInput | null): NodeId[] {
1396
+ if (!selection) return [];
1397
+ if (selection.kind === "node" || selection.kind === "neighbors") return [selection.id];
1398
+ if (selection.kind === "nodes") return selection.ids;
1399
+ return [];
1400
+ }
1401
+
1402
+ function shiftSelection(id: NodeId, current: NodeId[]): SelectionInput {
1403
+ const selected = new Set(current);
1404
+ if (selected.has(id)) selected.delete(id);
1405
+ else selected.add(id);
1406
+ const ids = Array.from(selected);
1407
+ if (ids.length === 1) return { kind: "node", id: ids[0] };
1408
+ return { kind: "nodes", ids };
1409
+ }
1410
+
1411
+ function openPagePayloadForNode(data: GraphData, id: NodeId): GraphOpenPagePayload {
1412
+ const node = data.nodes.find((item) => item.id === id);
1413
+ if (!node) {
1414
+ return {
1415
+ path: id,
1416
+ node: {
1417
+ id,
1418
+ title: id,
1419
+ type: "entity",
1420
+ typeLabel: "实体",
1421
+ sourcePath: id,
1422
+ community: null,
1423
+ date: null,
1424
+ source: null,
1425
+ isolated: true
1426
+ }
1427
+ };
1428
+ }
1429
+ const sourcePath = wikiPathForGraphNode(node);
1430
+ return {
1431
+ path: sourcePath,
1432
+ node: {
1433
+ id: node.id,
1434
+ title: node.label || node.id,
1435
+ type: node.type,
1436
+ typeLabel: graphNodeTypeLabel(node.type),
1437
+ sourcePath,
1438
+ community: node.community ?? null,
1439
+ date: dateForNode(node),
1440
+ source: sourceForNode(node),
1441
+ isolated: isIsolatedNode(data, node.id)
1442
+ }
1443
+ };
1444
+ }
1445
+
1446
+ function isIsolatedNode(data: GraphData, id: NodeId): boolean {
1447
+ return !data.edges.some((edge) => edge.from === id || edge.to === id);
1448
+ }
1449
+
1450
+ function dateForNode(node: GraphNode): string | null {
1451
+ const value = node.date || node.updated_at || node.updatedAt || node.created_at || node.createdAt;
1452
+ return value == null || value === "" ? null : String(value);
1453
+ }
1454
+
1455
+ function sourceForNode(node: GraphNode): string | null {
1456
+ const value = node.source_title || node.source_url || node.url || node.author || node.source_name;
1457
+ return value == null || value === "" ? null : String(value);
1458
+ }
1459
+
1460
+ function graphReaderMetaItems(node: GraphOpenPagePayload["node"]): string[] {
1461
+ const items = [node.typeLabel];
1462
+ if (node.date) items.push(node.date);
1463
+ if (node.source) items.push(node.source);
1464
+ return items;
1465
+ }
1466
+
1467
+ function edgeConfidenceLabel(confidence: string): string {
1468
+ switch (confidence) {
1469
+ case "inferred":
1470
+ return "推断";
1471
+ case "ambiguous":
1472
+ return "待确认";
1473
+ case "unverified":
1474
+ return "未验证";
1475
+ default:
1476
+ return "原文";
1477
+ }
1478
+ }
1479
+
1480
+ function eventToGraphPoint(root: HTMLElement, event: PointerEvent): { x: number; y: number } {
1481
+ const rect = root.getBoundingClientRect();
1482
+ return {
1483
+ x: clamp((event.clientX - rect.left) / Math.max(1, rect.width) * WORLD_WIDTH, 0, WORLD_WIDTH),
1484
+ y: clamp((event.clientY - rect.top) / Math.max(1, rect.height) * WORLD_HEIGHT, 0, WORLD_HEIGHT)
1485
+ };
1486
+ }
1487
+
1488
+ function isBlankViewportTarget(target: EventTarget | null): boolean {
1489
+ const element = target instanceof Element ? target : null;
1490
+ if (!element) return false;
1491
+ return !element.closest(".node, .mini-map, .graph-reader, .graph-search, .graph-toolbar, .community-legend, .community-wash");
1492
+ }
1493
+
1494
+ function isTextEditingElement(element: Element | null): boolean {
1495
+ if (!element) return false;
1496
+ const tagName = element.tagName.toLowerCase();
1497
+ if (tagName === "textarea") return true;
1498
+ if (tagName === "input") {
1499
+ const input = element as HTMLInputElement;
1500
+ const type = input.type.toLowerCase();
1501
+ return !["button", "checkbox", "radio", "range", "submit", "reset"].includes(type);
1502
+ }
1503
+ return element instanceof HTMLElement && element.isContentEditable;
1504
+ }
1505
+
1506
+ function createGraphToolbar(
1507
+ ownerDocument: Document,
1508
+ options: {
1509
+ panelState: GraphToolbarPanelState;
1510
+ typeFilters: GraphTypeFilters;
1511
+ onPanelToggle: (panel: Exclude<GraphToolbarPanelState, "closed">) => void;
1512
+ onTypeFilterToggle: (type: string, enabled: boolean) => void;
1513
+ onReset: () => void;
1514
+ }
1515
+ ): { element: HTMLElement; panel: HTMLElement; filtersPanel: HTMLElement } {
1516
+ const element = ownerDocument.createElement("nav");
1517
+ element.className = "graph-toolbar";
1518
+ element.dataset.panel = options.panelState;
1519
+ element.setAttribute("aria-label", "图谱控制");
1520
+ element.addEventListener("click", (event) => event.stopPropagation());
1521
+
1522
+ const actions = ownerDocument.createElement("div");
1523
+ actions.className = "graph-toolbar-actions";
1524
+ const filters = createToolbarButton(ownerDocument, "筛选", options.panelState === "filters");
1525
+ filters.addEventListener("click", () => options.onPanelToggle("filters"));
1526
+ const legend = createToolbarButton(ownerDocument, "图例", options.panelState === "legend");
1527
+ legend.addEventListener("click", () => options.onPanelToggle("legend"));
1528
+ const reset = createToolbarButton(ownerDocument, "回全图", false);
1529
+ reset.addEventListener("click", options.onReset);
1530
+ actions.append(filters, legend, reset);
1531
+
1532
+ const panel = ownerDocument.createElement("section");
1533
+ panel.className = "graph-toolbar-panel";
1534
+ panel.dataset.state = options.panelState;
1535
+ const filtersPanel = ownerDocument.createElement("div");
1536
+ filtersPanel.className = "graph-toolbar-section graph-toolbar-filters";
1537
+ filtersPanel.appendChild(createTypeFilterGroup(ownerDocument, options.typeFilters, options.onTypeFilterToggle));
1538
+
1539
+ const legendPanel = ownerDocument.createElement("div");
1540
+ legendPanel.className = "graph-toolbar-section graph-toolbar-legend";
1541
+ const legendTitle = ownerDocument.createElement("div");
1542
+ legendTitle.className = "graph-toolbar-section-title";
1543
+ legendTitle.textContent = "边";
1544
+ legendPanel.appendChild(legendTitle);
1545
+ legendPanel.appendChild(createEdgeLegend(ownerDocument));
1546
+
1547
+ panel.append(filtersPanel, legendPanel);
1548
+ element.append(actions, panel);
1549
+ return { element, panel, filtersPanel };
1550
+ }
1551
+
1552
+ function createEdgeLegend(ownerDocument: Document): HTMLElement {
1553
+ const legend = ownerDocument.createElement("div");
1554
+ legend.className = "graph-edge-legend";
1555
+ const relations = ownerDocument.createElement("div");
1556
+ relations.className = "graph-edge-legend-group";
1557
+ relations.appendChild(createEdgeLegendHeading(ownerDocument, "关系类型"));
1558
+ for (const item of [
1559
+ { label: "实现 / 依赖 / 衍生", className: "relation-dependency" },
1560
+ { label: "对比", className: "relation-contrast" },
1561
+ { label: "矛盾", className: "relation-conflict" }
1562
+ ]) {
1563
+ relations.appendChild(createEdgeLegendRelation(ownerDocument, item.label, item.className));
1564
+ }
1565
+
1566
+ const confidences = ownerDocument.createElement("div");
1567
+ confidences.className = "graph-edge-legend-group";
1568
+ confidences.appendChild(createEdgeLegendHeading(ownerDocument, "置信度"));
1569
+ for (const item of [
1570
+ { label: "原文", className: "confidence-extracted" },
1571
+ { label: "推断", className: "confidence-inferred" },
1572
+ { label: "待确认", className: "confidence-ambiguous" }
1573
+ ]) {
1574
+ confidences.appendChild(createEdgeLegendConfidence(ownerDocument, item.label, item.className));
1575
+ }
1576
+
1577
+ legend.append(relations, confidences);
1578
+ return legend;
1579
+ }
1580
+
1581
+ function createEdgeLegendHeading(ownerDocument: Document, text: string): HTMLElement {
1582
+ const heading = ownerDocument.createElement("div");
1583
+ heading.className = "graph-edge-legend-heading";
1584
+ heading.textContent = text;
1585
+ return heading;
1586
+ }
1587
+
1588
+ function createEdgeLegendRelation(ownerDocument: Document, label: string, className: string): HTMLElement {
1589
+ const row = ownerDocument.createElement("div");
1590
+ row.className = `graph-edge-legend-row graph-edge-legend-relation ${className}`;
1591
+ const swatch = ownerDocument.createElement("span");
1592
+ swatch.className = "graph-edge-legend-swatch";
1593
+ const text = ownerDocument.createElement("span");
1594
+ text.textContent = label;
1595
+ row.append(swatch, text);
1596
+ return row;
1597
+ }
1598
+
1599
+ function createEdgeLegendConfidence(ownerDocument: Document, label: string, className: string): HTMLElement {
1600
+ const row = ownerDocument.createElement("div");
1601
+ row.className = `graph-edge-legend-row graph-edge-legend-confidence ${className}`;
1602
+ const line = ownerDocument.createElement("span");
1603
+ line.className = "graph-edge-legend-line";
1604
+ const text = ownerDocument.createElement("span");
1605
+ text.textContent = label;
1606
+ row.append(line, text);
1607
+ return row;
1608
+ }
1609
+
1610
+ function createTypeFilterGroup(
1611
+ ownerDocument: Document,
1612
+ typeFilters: GraphTypeFilters,
1613
+ onToggle: (type: string, enabled: boolean) => void
1614
+ ): HTMLElement {
1615
+ const group = ownerDocument.createElement("fieldset");
1616
+ group.className = "graph-type-filter";
1617
+ const title = ownerDocument.createElement("legend");
1618
+ title.className = "graph-toolbar-section-title";
1619
+ title.textContent = "类型筛选";
1620
+ group.appendChild(title);
1621
+
1622
+ for (const type of orderedGraphNodeTypes(typeFilters)) {
1623
+ const label = ownerDocument.createElement("label");
1624
+ label.className = "graph-type-filter-option";
1625
+ const input = ownerDocument.createElement("input");
1626
+ input.type = "checkbox";
1627
+ input.checked = typeFilters[type] !== false;
1628
+ input.dataset.type = type;
1629
+ input.addEventListener("change", () => onToggle(type, input.checked));
1630
+ const text = ownerDocument.createElement("span");
1631
+ text.textContent = graphNodeTypeLabel(type);
1632
+ label.append(input, text);
1633
+ group.appendChild(label);
1634
+ }
1635
+
1636
+ return group;
1637
+ }
1638
+
1639
+ function orderedGraphNodeTypes(typeFilters: GraphTypeFilters): string[] {
1640
+ const preferred = ["entity", "topic", "source"];
1641
+ const seen = new Set<string>();
1642
+ const ordered: string[] = [];
1643
+ for (const type of preferred) {
1644
+ if (Object.hasOwn(typeFilters, type)) {
1645
+ ordered.push(type);
1646
+ seen.add(type);
1647
+ }
1648
+ }
1649
+ for (const type of Object.keys(typeFilters).sort()) {
1650
+ if (!seen.has(type)) ordered.push(type);
1651
+ }
1652
+ return ordered;
1653
+ }
1654
+
1655
+ function createToolbarButton(ownerDocument: Document, label: string, active: boolean): HTMLButtonElement {
1656
+ const button = ownerDocument.createElement("button");
1657
+ button.type = "button";
1658
+ button.className = "graph-toolbar-button";
1659
+ button.dataset.active = active ? "true" : "false";
1660
+ button.textContent = label;
1661
+ return button;
1662
+ }
1663
+
1664
+ function createCommunityLegend(
1665
+ ownerDocument: Document,
1666
+ options: {
1667
+ rows: CommunityLegendRow[];
1668
+ collapsed: boolean;
1669
+ onToggle: () => void;
1670
+ onHover: (id: string | null) => void;
1671
+ onSelect: (id: string) => void;
1672
+ }
1673
+ ): { element: HTMLElement; rows: Map<string, HTMLButtonElement> } {
1674
+ const element = ownerDocument.createElement("aside");
1675
+ element.className = "community-legend";
1676
+ element.dataset.state = options.collapsed ? "collapsed" : "open";
1677
+ const header = ownerDocument.createElement("button");
1678
+ header.type = "button";
1679
+ header.className = "community-legend-toggle";
1680
+ header.setAttribute("aria-expanded", options.collapsed ? "false" : "true");
1681
+ header.textContent = options.collapsed ? "社区" : "社区";
1682
+ header.addEventListener("click", (event) => {
1683
+ event.stopPropagation();
1684
+ options.onToggle();
1685
+ });
1686
+ element.appendChild(header);
1687
+
1688
+ const list = ownerDocument.createElement("div");
1689
+ list.className = "community-legend-list";
1690
+ const rowMap = new Map<string, HTMLButtonElement>();
1691
+ for (const row of options.rows) {
1692
+ const button = ownerDocument.createElement("button");
1693
+ button.type = "button";
1694
+ button.className = "community-legend-row";
1695
+ button.dataset.communityId = row.id;
1696
+ button.addEventListener("pointerenter", () => options.onHover(row.id));
1697
+ button.addEventListener("pointerleave", () => options.onHover(null));
1698
+ button.addEventListener("click", (event) => {
1699
+ event.stopPropagation();
1700
+ options.onSelect(row.id);
1701
+ });
1702
+ const swatch = ownerDocument.createElement("span");
1703
+ swatch.className = "community-legend-swatch";
1704
+ swatch.style.background = row.color;
1705
+ const label = ownerDocument.createElement("span");
1706
+ label.className = "community-legend-label";
1707
+ label.textContent = row.label;
1708
+ const count = ownerDocument.createElement("span");
1709
+ count.className = "community-legend-count";
1710
+ count.textContent = `${row.pageCount} 页`;
1711
+ button.append(swatch, label, count);
1712
+ list.appendChild(button);
1713
+ rowMap.set(row.id, button);
1714
+ }
1715
+ element.appendChild(list);
1716
+ return { element, rows: rowMap };
1717
+ }
1718
+
1719
+ function createSearchControl(
1720
+ ownerDocument: Document,
1721
+ options: {
1722
+ open: boolean;
1723
+ query: string;
1724
+ onOpen: () => void;
1725
+ onQuery: (query: string) => void;
1726
+ onNext: () => void;
1727
+ onClose: () => void;
1728
+ }
1729
+ ): { element: HTMLElement; input: HTMLInputElement; status: HTMLElement } {
1730
+ const element = ownerDocument.createElement("div");
1731
+ element.className = "graph-search";
1732
+ element.dataset.state = options.open ? "open" : "closed";
1733
+ const input = ownerDocument.createElement("input");
1734
+ input.type = "search";
1735
+ input.className = "graph-search-input";
1736
+ input.placeholder = "搜索图谱";
1737
+ input.setAttribute("aria-label", "搜索图谱");
1738
+ input.value = options.query;
1739
+ input.addEventListener("focus", options.onOpen);
1740
+ input.addEventListener("input", () => options.onQuery(input.value));
1741
+ input.addEventListener("keydown", (event) => {
1742
+ if (event.key === "Enter") {
1743
+ event.preventDefault();
1744
+ options.onNext();
1745
+ }
1746
+ if (event.key === "Escape") {
1747
+ event.preventDefault();
1748
+ event.stopPropagation();
1749
+ options.onClose();
1750
+ }
1751
+ });
1752
+ const status = ownerDocument.createElement("span");
1753
+ status.className = "graph-search-status";
1754
+ status.textContent = options.query ? "0 个结果" : "输入关键词";
1755
+ element.append(input, status);
1756
+ return { element, input, status };
1757
+ }
1758
+
1759
+ function emptyPaintedDom(): PaintedGraphDom {
1760
+ return {
1761
+ contentLayer: null,
1762
+ edgeElements: new Map(),
1763
+ communityWashElements: new Map(),
1764
+ nodeElements: new Map(),
1765
+ miniNodeElements: new Map(),
1766
+ miniViewportElement: null,
1767
+ basePoints: new Map(),
1768
+ readerElement: null,
1769
+ selectionElement: null,
1770
+ searchElement: null,
1771
+ searchInput: null,
1772
+ searchStatusElement: null,
1773
+ toolbarElement: null,
1774
+ toolbarPanelElement: null,
1775
+ legendElement: null,
1776
+ legendRows: new Map(),
1777
+ previewElement: null
1778
+ };
1779
+ }
1780
+
1781
+ const COMMUNITY_LEGEND_COLLAPSED_KEY = "llm-wiki:graph:community-legend:collapsed";
1782
+
1783
+ function readLegendCollapsed(ownerDocument: Document): boolean {
1784
+ try {
1785
+ return ownerDocument.defaultView?.localStorage?.getItem(COMMUNITY_LEGEND_COLLAPSED_KEY) === "true";
1786
+ } catch {
1787
+ return false;
1788
+ }
1789
+ }
1790
+
1791
+ function writeLegendCollapsed(ownerDocument: Document, collapsed: boolean): void {
1792
+ try {
1793
+ ownerDocument.defaultView?.localStorage?.setItem(COMMUNITY_LEGEND_COLLAPSED_KEY, collapsed ? "true" : "false");
1794
+ } catch {
1795
+ // localStorage can be unavailable in restricted file contexts.
1796
+ }
1797
+ }
1798
+
1799
+ function clamp(value: number, min: number, max: number): number {
1800
+ return Math.max(min, Math.min(max, value));
1801
+ }
1802
+
1803
+ function round(value: number): number {
1804
+ return Math.round(value * 1000) / 1000;
1805
+ }
1806
+
1807
+ function applyTheme(root: HTMLElement, theme: ThemeId): void {
1808
+ root.dataset.theme = theme;
1809
+ root.style.colorScheme = getThemeTokens(theme).colorScheme;
1810
+ const vars = themeTokensToCssVars(theme);
1811
+ for (const [key, value] of Object.entries(vars)) {
1812
+ root.style.setProperty(key, value);
1813
+ }
1814
+ }
1815
+
1816
+ function ensureStaticRendererStyles(doc: Document): void {
1817
+ if (doc.getElementById("llm-wiki-graph-engine-static-styles")) return;
1818
+ const style = doc.createElement("style");
1819
+ style.id = "llm-wiki-graph-engine-static-styles";
1820
+ style.textContent = STATIC_RENDERER_CSS;
1821
+ doc.head.appendChild(style);
1822
+ }
1823
+
1824
+ const STATIC_RENDERER_CSS = `
1825
+ .llm-wiki-graph-engine {
1826
+ position: relative;
1827
+ width: 100%;
1828
+ min-height: 520px;
1829
+ height: 100%;
1830
+ overflow: hidden;
1831
+ color: var(--ink);
1832
+ font-family: var(--font-ui);
1833
+ background:
1834
+ radial-gradient(ellipse at 28% 55%, color-mix(in srgb, var(--surface) 56%, transparent), transparent 56%),
1835
+ radial-gradient(ellipse at 70% 48%, color-mix(in srgb, var(--mist) 60%, transparent), transparent 58%),
1836
+ var(--bg);
1837
+ }
1838
+ .llm-wiki-graph-engine[data-theme="mo-ye"] {
1839
+ background:
1840
+ linear-gradient(180deg, color-mix(in srgb, var(--surface-2) 38%, transparent), transparent 34%),
1841
+ radial-gradient(ellipse at 28% 56%, color-mix(in srgb, var(--night) 13%, transparent), transparent 58%),
1842
+ radial-gradient(ellipse at 76% 38%, color-mix(in srgb, var(--cinnabar) 9%, transparent), transparent 54%),
1843
+ var(--bg);
1844
+ }
1845
+ .graph-content-layer {
1846
+ position: absolute;
1847
+ inset: 0;
1848
+ z-index: 2;
1849
+ transform-origin: 0 0;
1850
+ will-change: transform;
1851
+ }
1852
+ .graph-search {
1853
+ position: absolute;
1854
+ top: 64px;
1855
+ left: 14px;
1856
+ z-index: 7;
1857
+ display: grid;
1858
+ grid-template-columns: minmax(180px, 260px) auto;
1859
+ align-items: center;
1860
+ gap: 8px;
1861
+ opacity: 0;
1862
+ pointer-events: none;
1863
+ transform: translateY(-6px);
1864
+ transition: opacity .16s ease, transform .16s ease;
1865
+ }
1866
+ .graph-search[data-state="open"],
1867
+ .graph-search:focus-within {
1868
+ opacity: 1;
1869
+ pointer-events: auto;
1870
+ transform: translateY(0);
1871
+ }
1872
+ .graph-search-input {
1873
+ min-width: 0;
1874
+ border: 1px solid color-mix(in srgb, var(--rule) 78%, transparent);
1875
+ border-radius: 8px;
1876
+ background: color-mix(in srgb, var(--surface) 92%, transparent);
1877
+ padding: 8px 10px;
1878
+ color: var(--ink);
1879
+ font: 13px/1.3 var(--font-ui);
1880
+ outline: none;
1881
+ box-shadow: 0 12px 24px rgba(36, 24, 12, .08);
1882
+ }
1883
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .graph-search-input {
1884
+ background: color-mix(in srgb, var(--surface) 88%, transparent);
1885
+ }
1886
+ .graph-search-input:focus {
1887
+ border-color: color-mix(in srgb, var(--cinnabar) 70%, transparent);
1888
+ }
1889
+ .graph-search-status {
1890
+ border: 1px solid color-mix(in srgb, var(--rule) 68%, transparent);
1891
+ border-radius: 999px;
1892
+ background: color-mix(in srgb, var(--surface) 84%, transparent);
1893
+ padding: 5px 8px;
1894
+ color: var(--muted);
1895
+ font-size: 11px;
1896
+ white-space: nowrap;
1897
+ }
1898
+ .graph-toolbar {
1899
+ position: absolute;
1900
+ top: 14px;
1901
+ left: 14px;
1902
+ right: 14px;
1903
+ z-index: 8;
1904
+ display: grid;
1905
+ justify-items: start;
1906
+ pointer-events: none;
1907
+ }
1908
+ .graph-toolbar-actions {
1909
+ display: flex;
1910
+ align-items: center;
1911
+ gap: 6px;
1912
+ max-width: 100%;
1913
+ border: 1px solid color-mix(in srgb, var(--rule) 62%, transparent);
1914
+ border-radius: 8px;
1915
+ background: color-mix(in srgb, var(--surface) 64%, transparent);
1916
+ box-shadow: 0 14px 30px rgba(36, 24, 12, .08);
1917
+ backdrop-filter: blur(14px);
1918
+ padding: 4px;
1919
+ pointer-events: auto;
1920
+ }
1921
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .graph-toolbar-actions {
1922
+ background: color-mix(in srgb, var(--surface) 58%, transparent);
1923
+ }
1924
+ .graph-toolbar-button {
1925
+ min-height: 28px;
1926
+ border: 0;
1927
+ border-radius: 6px;
1928
+ background: transparent;
1929
+ color: var(--muted);
1930
+ font: 12px/1.2 var(--font-ui);
1931
+ padding: 0 10px;
1932
+ cursor: pointer;
1933
+ white-space: nowrap;
1934
+ }
1935
+ .graph-toolbar-button:hover,
1936
+ .graph-toolbar-button[data-active="true"] {
1937
+ background: color-mix(in srgb, var(--cinnabar) 10%, transparent);
1938
+ color: var(--ink);
1939
+ }
1940
+ .graph-toolbar-panel {
1941
+ width: min(320px, calc(100vw - 28px));
1942
+ max-height: min(58vh, 420px);
1943
+ margin-top: 8px;
1944
+ border: 1px solid color-mix(in srgb, var(--rule) 62%, transparent);
1945
+ border-radius: 8px;
1946
+ background: color-mix(in srgb, var(--surface) 70%, transparent);
1947
+ box-shadow: 0 20px 42px rgba(36, 24, 12, .12);
1948
+ backdrop-filter: blur(16px);
1949
+ overflow: auto;
1950
+ pointer-events: auto;
1951
+ }
1952
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .graph-toolbar-panel {
1953
+ background: color-mix(in srgb, var(--surface) 62%, transparent);
1954
+ }
1955
+ .graph-toolbar-panel[data-state="closed"] {
1956
+ display: none;
1957
+ }
1958
+ .graph-toolbar-section {
1959
+ display: none;
1960
+ }
1961
+ .graph-toolbar-panel[data-state="filters"] .graph-toolbar-filters,
1962
+ .graph-toolbar-panel[data-state="legend"] .graph-toolbar-legend {
1963
+ display: block;
1964
+ }
1965
+ .graph-toolbar-section-title {
1966
+ padding: 10px 12px;
1967
+ color: var(--muted);
1968
+ font: 12px/1.3 var(--font-ui);
1969
+ }
1970
+ .graph-type-filter {
1971
+ display: grid;
1972
+ grid-template-columns: repeat(3, minmax(0, 1fr));
1973
+ gap: 6px;
1974
+ margin: 0;
1975
+ border: 0;
1976
+ border-bottom: 1px solid color-mix(in srgb, var(--rule) 52%, transparent);
1977
+ padding: 0 10px 10px;
1978
+ }
1979
+ .graph-type-filter .graph-toolbar-section-title {
1980
+ grid-column: 1 / -1;
1981
+ padding: 10px 2px 2px;
1982
+ }
1983
+ .graph-type-filter-option {
1984
+ display: inline-flex;
1985
+ align-items: center;
1986
+ gap: 6px;
1987
+ min-width: 0;
1988
+ min-height: 28px;
1989
+ border: 1px solid color-mix(in srgb, var(--rule) 52%, transparent);
1990
+ border-radius: 6px;
1991
+ background: color-mix(in srgb, var(--surface) 48%, transparent);
1992
+ padding: 0 8px;
1993
+ color: var(--ink);
1994
+ font: 12px/1.2 var(--font-ui);
1995
+ cursor: pointer;
1996
+ }
1997
+ .graph-type-filter-option input {
1998
+ margin: 0;
1999
+ accent-color: var(--cinnabar);
2000
+ }
2001
+ .graph-type-filter-option span {
2002
+ min-width: 0;
2003
+ overflow: hidden;
2004
+ text-overflow: ellipsis;
2005
+ white-space: nowrap;
2006
+ }
2007
+ .graph-edge-legend {
2008
+ display: grid;
2009
+ gap: 12px;
2010
+ padding: 0 12px 12px;
2011
+ }
2012
+ .graph-edge-legend-group {
2013
+ display: grid;
2014
+ gap: 7px;
2015
+ }
2016
+ .graph-edge-legend-heading {
2017
+ color: var(--muted);
2018
+ font: 11px/1.2 var(--font-ui);
2019
+ }
2020
+ .graph-edge-legend-row {
2021
+ display: grid;
2022
+ grid-template-columns: 38px minmax(0, 1fr);
2023
+ align-items: center;
2024
+ gap: 8px;
2025
+ min-height: 24px;
2026
+ color: var(--ink);
2027
+ font: 12px/1.2 var(--font-ui);
2028
+ }
2029
+ .graph-edge-legend-swatch,
2030
+ .graph-edge-legend-line {
2031
+ display: block;
2032
+ width: 34px;
2033
+ height: 0;
2034
+ border-top: 2px solid color-mix(in srgb, var(--night) 66%, transparent);
2035
+ }
2036
+ .graph-edge-legend-relation.relation-contrast .graph-edge-legend-swatch {
2037
+ border-top-color: color-mix(in srgb, var(--amber) 82%, transparent);
2038
+ }
2039
+ .graph-edge-legend-relation.relation-conflict .graph-edge-legend-swatch {
2040
+ border-top-color: color-mix(in srgb, #d94693 78%, transparent);
2041
+ }
2042
+ .graph-edge-legend-confidence.confidence-inferred .graph-edge-legend-line {
2043
+ border-top-style: dashed;
2044
+ }
2045
+ .graph-edge-legend-confidence.confidence-ambiguous .graph-edge-legend-line {
2046
+ border-top-style: dotted;
2047
+ }
2048
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .graph-edge-legend-swatch,
2049
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .graph-edge-legend-line {
2050
+ border-top-color: color-mix(in srgb, var(--line) 70%, transparent);
2051
+ }
2052
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .graph-edge-legend-relation.relation-contrast .graph-edge-legend-swatch {
2053
+ border-top-color: color-mix(in srgb, var(--amber) 76%, transparent);
2054
+ }
2055
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .graph-edge-legend-relation.relation-conflict .graph-edge-legend-swatch {
2056
+ border-top-color: color-mix(in srgb, #f472b6 78%, transparent);
2057
+ }
2058
+ .community-legend {
2059
+ width: 100%;
2060
+ border: 0;
2061
+ border-radius: 0;
2062
+ background: transparent;
2063
+ box-shadow: none;
2064
+ overflow: hidden;
2065
+ }
2066
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .community-legend {
2067
+ background: transparent;
2068
+ }
2069
+ .community-legend-toggle {
2070
+ width: 100%;
2071
+ border: 0;
2072
+ border-bottom: 1px solid color-mix(in srgb, var(--rule) 64%, transparent);
2073
+ background: transparent;
2074
+ padding: 8px 10px;
2075
+ color: var(--ink);
2076
+ font: 12px/1.3 var(--font-ui);
2077
+ text-align: left;
2078
+ cursor: pointer;
2079
+ }
2080
+ .community-legend[data-state="collapsed"] .community-legend-toggle {
2081
+ border-bottom: 0;
2082
+ }
2083
+ .community-legend-list {
2084
+ display: grid;
2085
+ }
2086
+ .community-legend[data-state="collapsed"] .community-legend-list {
2087
+ display: none;
2088
+ }
2089
+ .community-legend-row {
2090
+ display: grid;
2091
+ grid-template-columns: 12px minmax(0, 1fr) auto;
2092
+ align-items: center;
2093
+ gap: 8px;
2094
+ border: 0;
2095
+ border-top: 1px solid color-mix(in srgb, var(--rule) 48%, transparent);
2096
+ background: transparent;
2097
+ padding: 8px 10px;
2098
+ color: var(--ink);
2099
+ font: 12px/1.3 var(--font-ui);
2100
+ cursor: pointer;
2101
+ text-align: left;
2102
+ }
2103
+ .community-legend-row:first-child {
2104
+ border-top: 0;
2105
+ }
2106
+ .community-legend-row:hover,
2107
+ .community-legend-row[data-community-state="active"] {
2108
+ background: color-mix(in srgb, var(--cinnabar) 8%, transparent);
2109
+ }
2110
+ .community-legend-row[data-community-state="faded"] {
2111
+ opacity: .42;
2112
+ }
2113
+ .community-legend-swatch {
2114
+ width: 12px;
2115
+ height: 12px;
2116
+ border-radius: 999px;
2117
+ box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12);
2118
+ }
2119
+ .community-legend-label {
2120
+ overflow: hidden;
2121
+ text-overflow: ellipsis;
2122
+ white-space: nowrap;
2123
+ }
2124
+ .community-legend-count {
2125
+ color: var(--muted);
2126
+ font-size: 11px;
2127
+ white-space: nowrap;
2128
+ }
2129
+ .graph-selection-panel {
2130
+ position: absolute;
2131
+ right: 16px;
2132
+ bottom: 16px;
2133
+ z-index: 7;
2134
+ display: grid;
2135
+ gap: 12px;
2136
+ width: min(360px, calc(100% - 32px));
2137
+ max-height: min(520px, calc(100% - 32px));
2138
+ overflow: auto;
2139
+ border: 1px solid color-mix(in srgb, var(--rule) 72%, transparent);
2140
+ border-radius: 8px;
2141
+ background: color-mix(in srgb, var(--surface) 92%, transparent);
2142
+ box-shadow: 0 18px 36px rgba(36, 24, 12, .14);
2143
+ padding: 14px;
2144
+ opacity: 0;
2145
+ pointer-events: none;
2146
+ transform: translateY(8px);
2147
+ transition: opacity .16s ease, transform .16s ease;
2148
+ }
2149
+ .graph-selection-panel[data-state="open"] {
2150
+ opacity: 1;
2151
+ pointer-events: auto;
2152
+ transform: translateY(0);
2153
+ }
2154
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .graph-selection-panel {
2155
+ background: color-mix(in srgb, var(--surface) 88%, transparent);
2156
+ }
2157
+ .graph-selection-header {
2158
+ display: grid;
2159
+ grid-template-columns: minmax(0, 1fr) 28px;
2160
+ align-items: center;
2161
+ gap: 8px;
2162
+ }
2163
+ .graph-selection-title {
2164
+ overflow: hidden;
2165
+ color: var(--ink);
2166
+ font: 600 14px/1.35 var(--font-ui);
2167
+ text-overflow: ellipsis;
2168
+ white-space: nowrap;
2169
+ }
2170
+ .graph-selection-close {
2171
+ width: 28px;
2172
+ height: 28px;
2173
+ border: 1px solid color-mix(in srgb, var(--rule) 72%, transparent);
2174
+ border-radius: 999px;
2175
+ background: transparent;
2176
+ color: var(--ink);
2177
+ cursor: pointer;
2178
+ font-size: 17px;
2179
+ line-height: 1;
2180
+ }
2181
+ .graph-selection-hint,
2182
+ .graph-selection-empty {
2183
+ margin: 0;
2184
+ color: var(--muted);
2185
+ font-size: 12px;
2186
+ line-height: 1.45;
2187
+ }
2188
+ .graph-selection-facts {
2189
+ display: grid;
2190
+ grid-template-columns: repeat(4, minmax(0, 1fr));
2191
+ gap: 6px;
2192
+ }
2193
+ .graph-selection-fact {
2194
+ min-width: 0;
2195
+ border: 1px solid color-mix(in srgb, var(--rule) 58%, transparent);
2196
+ border-radius: 8px;
2197
+ background: color-mix(in srgb, var(--mist) 52%, transparent);
2198
+ padding: 8px 6px;
2199
+ }
2200
+ .graph-selection-fact strong,
2201
+ .graph-selection-fact span {
2202
+ display: block;
2203
+ overflow: hidden;
2204
+ text-align: center;
2205
+ text-overflow: ellipsis;
2206
+ white-space: nowrap;
2207
+ }
2208
+ .graph-selection-fact strong {
2209
+ color: var(--ink);
2210
+ font-size: 15px;
2211
+ }
2212
+ .graph-selection-fact span {
2213
+ margin-top: 2px;
2214
+ color: var(--muted);
2215
+ font-size: 11px;
2216
+ }
2217
+ .graph-selection-pages {
2218
+ display: grid;
2219
+ gap: 6px;
2220
+ margin: 0;
2221
+ padding: 0;
2222
+ list-style: none;
2223
+ }
2224
+ .graph-selection-page {
2225
+ min-width: 0;
2226
+ border-top: 1px solid color-mix(in srgb, var(--rule) 46%, transparent);
2227
+ padding-top: 7px;
2228
+ }
2229
+ .graph-selection-page:first-child {
2230
+ border-top: 0;
2231
+ padding-top: 0;
2232
+ }
2233
+ .graph-selection-page-title,
2234
+ .graph-selection-page-path {
2235
+ display: block;
2236
+ overflow: hidden;
2237
+ text-overflow: ellipsis;
2238
+ white-space: nowrap;
2239
+ }
2240
+ .graph-selection-page-title {
2241
+ color: var(--ink);
2242
+ font-size: 13px;
2243
+ }
2244
+ .graph-selection-page-path {
2245
+ margin-top: 2px;
2246
+ color: var(--muted);
2247
+ font-size: 11px;
2248
+ }
2249
+ .graph-content-layer.is-viewport-animating {
2250
+ transition: transform .2s ease-out;
2251
+ }
2252
+ .llm-wiki-graph-svg {
2253
+ position: absolute;
2254
+ inset: 0;
2255
+ width: 100%;
2256
+ height: 100%;
2257
+ overflow: visible;
2258
+ }
2259
+ .edge {
2260
+ fill: none;
2261
+ stroke-linecap: round;
2262
+ opacity: .74;
2263
+ pointer-events: stroke;
2264
+ }
2265
+ .edge.is-diff-added {
2266
+ stroke-dasharray: var(--diff-edge-length, 180);
2267
+ stroke-dashoffset: var(--diff-edge-length, 180);
2268
+ animation: llm-wiki-edge-draw 1.15s ease forwards;
2269
+ }
2270
+ .edge.is-diff-removed {
2271
+ animation: llm-wiki-fade-out .72s ease forwards;
2272
+ }
2273
+ .edge.relation-implementation,
2274
+ .edge.relation-dependency,
2275
+ .edge.relation-derivation {
2276
+ stroke: color-mix(in srgb, var(--night) 66%, transparent);
2277
+ }
2278
+ .edge.relation-contrast {
2279
+ stroke: color-mix(in srgb, var(--amber) 82%, transparent);
2280
+ }
2281
+ .edge.relation-conflict {
2282
+ stroke: color-mix(in srgb, #d94693 78%, transparent);
2283
+ }
2284
+ .edge.confidence-inferred { stroke-dasharray: 6 8; }
2285
+ .edge.confidence-ambiguous { stroke-dasharray: 2 7; }
2286
+ .edge.confidence-unverified { stroke-dasharray: 1 8; }
2287
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .edge {
2288
+ opacity: .82;
2289
+ }
2290
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .edge.relation-implementation,
2291
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .edge.relation-dependency,
2292
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .edge.relation-derivation {
2293
+ stroke: color-mix(in srgb, var(--line) 70%, transparent);
2294
+ }
2295
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .edge.relation-contrast {
2296
+ stroke: color-mix(in srgb, var(--amber) 76%, transparent);
2297
+ }
2298
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .edge.relation-conflict {
2299
+ stroke: color-mix(in srgb, #f472b6 78%, transparent);
2300
+ }
2301
+ .community-wash {
2302
+ transition: opacity .16s ease, cx .24s ease, cy .24s ease, rx .24s ease, ry .24s ease;
2303
+ }
2304
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .community-wash {
2305
+ mix-blend-mode: screen;
2306
+ filter: saturate(.9);
2307
+ }
2308
+ .community-wash.is-diff-new-community {
2309
+ animation: llm-wiki-community-emerge .85s ease both;
2310
+ }
2311
+ .llm-wiki-graph-engine[data-dragging] .community-wash {
2312
+ opacity: .035;
2313
+ }
2314
+ .node-layer {
2315
+ position: absolute;
2316
+ inset: 0;
2317
+ z-index: 3;
2318
+ pointer-events: none;
2319
+ }
2320
+ .graph-hover-preview {
2321
+ position: absolute;
2322
+ z-index: 9;
2323
+ width: min(300px, calc(100% - 32px));
2324
+ pointer-events: none;
2325
+ opacity: 0;
2326
+ transition: opacity .14s ease;
2327
+ }
2328
+ .graph-hover-preview[data-state="open"] {
2329
+ opacity: 1;
2330
+ }
2331
+ .graph-hover-preview-card {
2332
+ border: 1px solid color-mix(in srgb, var(--rule) 74%, transparent);
2333
+ border-radius: 8px;
2334
+ background: color-mix(in srgb, var(--surface) 94%, transparent);
2335
+ box-shadow: 0 18px 34px rgba(36, 31, 26, .16);
2336
+ padding: 11px 12px;
2337
+ }
2338
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .graph-hover-preview-card {
2339
+ border-color: color-mix(in srgb, var(--line) 38%, transparent);
2340
+ background: color-mix(in srgb, var(--surface) 90%, transparent);
2341
+ box-shadow: 0 18px 36px rgba(0, 0, 0, .38);
2342
+ }
2343
+ .graph-hover-preview-type {
2344
+ color: var(--muted);
2345
+ font-size: 11px;
2346
+ line-height: 1.2;
2347
+ }
2348
+ .graph-hover-preview-title {
2349
+ margin-top: 3px;
2350
+ overflow: hidden;
2351
+ color: var(--ink);
2352
+ font-family: var(--font-serif);
2353
+ font-size: 15px;
2354
+ font-weight: 700;
2355
+ line-height: 1.25;
2356
+ text-overflow: ellipsis;
2357
+ white-space: nowrap;
2358
+ }
2359
+ .graph-hover-preview-summary {
2360
+ display: -webkit-box;
2361
+ margin: 7px 0 0;
2362
+ overflow: hidden;
2363
+ color: var(--muted);
2364
+ font-size: 12px;
2365
+ line-height: 1.45;
2366
+ -webkit-box-orient: vertical;
2367
+ -webkit-line-clamp: 3;
2368
+ }
2369
+ .node {
2370
+ position: absolute;
2371
+ z-index: 3;
2372
+ pointer-events: auto;
2373
+ min-height: 46px;
2374
+ max-width: 178px;
2375
+ padding: 8px 11px;
2376
+ border-radius: 12px;
2377
+ border: 1px solid color-mix(in srgb, var(--rule) 98%, transparent);
2378
+ background: color-mix(in srgb, var(--surface) 88%, transparent);
2379
+ box-shadow: 0 12px 22px rgba(36, 31, 26, .09), inset 0 0 0 1px rgba(255, 255, 255, .32);
2380
+ translate: -50% -50%;
2381
+ text-align: left;
2382
+ color: var(--ink);
2383
+ transition:
2384
+ opacity .16s ease,
2385
+ width .16s ease,
2386
+ height .16s ease,
2387
+ min-width .16s ease,
2388
+ min-height .16s ease,
2389
+ max-width .16s ease,
2390
+ padding .16s ease,
2391
+ border-radius .16s ease,
2392
+ border-color .16s ease,
2393
+ background-color .16s ease,
2394
+ box-shadow .16s ease;
2395
+ }
2396
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .node {
2397
+ border-color: color-mix(in srgb, var(--rule) 84%, transparent);
2398
+ background: color-mix(in srgb, var(--surface) 86%, transparent);
2399
+ box-shadow: 0 16px 30px rgba(0, 0, 0, .34), inset 0 0 0 1px rgba(245, 240, 230, .07);
2400
+ }
2401
+ .node::before {
2402
+ content: "";
2403
+ position: absolute;
2404
+ inset: -7px;
2405
+ border-radius: 17px;
2406
+ background: radial-gradient(circle, color-mix(in srgb, var(--night) 18%, transparent), transparent 66%);
2407
+ z-index: -1;
2408
+ opacity: .46;
2409
+ }
2410
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .node::before {
2411
+ background: radial-gradient(circle, color-mix(in srgb, var(--night) 24%, transparent), transparent 68%);
2412
+ opacity: .4;
2413
+ }
2414
+ .node[data-type="topic"] { border-left: 5px solid var(--cinnabar); }
2415
+ .node[data-type="entity"] { border-left: 5px solid var(--night); }
2416
+ .node[data-type="source"] { border-left: 5px solid var(--jade); }
2417
+ .node[aria-pressed="true"] {
2418
+ border-color: color-mix(in srgb, var(--cinnabar) 74%, transparent);
2419
+ box-shadow: 0 16px 28px color-mix(in srgb, var(--cinnabar) 16%, transparent), 0 0 0 4px color-mix(in srgb, var(--cinnabar) 10%, transparent);
2420
+ transform: translateY(-2px);
2421
+ }
2422
+ .node[data-search-state="match"] {
2423
+ border-color: color-mix(in srgb, var(--cinnabar) 78%, transparent);
2424
+ box-shadow: 0 16px 28px color-mix(in srgb, var(--cinnabar) 15%, transparent), 0 0 0 4px color-mix(in srgb, var(--cinnabar) 9%, transparent);
2425
+ }
2426
+ .node[data-search-focus="true"] {
2427
+ outline: 3px solid color-mix(in srgb, var(--cinnabar) 68%, transparent);
2428
+ outline-offset: 4px;
2429
+ }
2430
+ .node[data-search-state="faded"] {
2431
+ opacity: .28;
2432
+ }
2433
+ .node[data-community-state="faded"] {
2434
+ opacity: .24;
2435
+ }
2436
+ .edge[data-community-state="faded"],
2437
+ .community-wash[data-community-state="faded"] {
2438
+ opacity: .12 !important;
2439
+ }
2440
+ .community-wash[data-community-state="active"] {
2441
+ opacity: .2;
2442
+ }
2443
+ .node.is-dragging {
2444
+ cursor: grabbing;
2445
+ z-index: 8;
2446
+ box-shadow: 0 18px 34px color-mix(in srgb, var(--cinnabar) 18%, transparent), 0 0 0 4px color-mix(in srgb, var(--cinnabar) 10%, transparent);
2447
+ }
2448
+ .node.is-pinned::after {
2449
+ content: "";
2450
+ position: absolute;
2451
+ right: -5px;
2452
+ top: -5px;
2453
+ width: 10px;
2454
+ height: 10px;
2455
+ border: 2px solid color-mix(in srgb, var(--surface) 92%, transparent);
2456
+ border-radius: 99px;
2457
+ background: var(--cinnabar);
2458
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--cinnabar) 13%, transparent);
2459
+ }
2460
+ .node-kind {
2461
+ display: none;
2462
+ color: var(--muted);
2463
+ font-family: var(--font-mono);
2464
+ font-size: 10px;
2465
+ letter-spacing: .04em;
2466
+ text-transform: uppercase;
2467
+ }
2468
+ .node-name {
2469
+ display: block;
2470
+ max-width: 146px;
2471
+ margin-top: 0;
2472
+ overflow: hidden;
2473
+ text-overflow: ellipsis;
2474
+ white-space: nowrap;
2475
+ font-family: var(--font-serif);
2476
+ font-size: 14px;
2477
+ font-weight: 700;
2478
+ line-height: 1.25;
2479
+ }
2480
+ .node-meta {
2481
+ display: none;
2482
+ align-items: center;
2483
+ gap: 6px;
2484
+ margin-top: 6px;
2485
+ color: var(--faint);
2486
+ font-size: 11px;
2487
+ }
2488
+ .node:hover .node-kind,
2489
+ .node[aria-pressed="true"] .node-kind {
2490
+ display: block;
2491
+ }
2492
+ .node:hover .node-name,
2493
+ .node[aria-pressed="true"] .node-name {
2494
+ margin-top: 3px;
2495
+ }
2496
+ .node:hover .node-meta,
2497
+ .node[aria-pressed="true"] .node-meta {
2498
+ display: flex;
2499
+ }
2500
+ .spark {
2501
+ width: 5px;
2502
+ height: 5px;
2503
+ border-radius: 99px;
2504
+ background: var(--night);
2505
+ box-shadow: 0 0 10px color-mix(in srgb, var(--night) 70%, transparent);
2506
+ }
2507
+ .node.is-compact {
2508
+ min-height: 34px;
2509
+ max-width: 130px;
2510
+ padding: 6px 9px;
2511
+ border-radius: 10px;
2512
+ }
2513
+ .node.is-compact .node-kind,
2514
+ .node.is-compact .node-meta { display: none; }
2515
+ .node.is-compact .node-name {
2516
+ max-width: 104px;
2517
+ font-size: 12px;
2518
+ }
2519
+ .node.is-point,
2520
+ .node.is-overview,
2521
+ .node[data-visual-role="map-pin"] {
2522
+ width: 14px;
2523
+ height: 14px;
2524
+ min-width: 14px;
2525
+ min-height: 14px;
2526
+ max-width: 14px;
2527
+ padding: 0;
2528
+ border: 0;
2529
+ border-radius: 999px;
2530
+ background: var(--night);
2531
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--night) 14%, transparent);
2532
+ }
2533
+ .node.is-point[data-type="topic"],
2534
+ .node.is-overview[data-type="topic"] { background: var(--cinnabar); }
2535
+ .node.is-point[data-type="source"],
2536
+ .node.is-overview[data-type="source"] { background: var(--jade); }
2537
+ .node.is-point .node-kind,
2538
+ .node.is-point .node-name,
2539
+ .node.is-point .node-meta,
2540
+ .node.is-overview .node-kind,
2541
+ .node.is-overview .node-name,
2542
+ .node.is-overview .node-meta { display: none; }
2543
+ .node.is-label-hidden .node-name { display: none; }
2544
+ .node[data-visual-role="landmark"] {
2545
+ min-height: 30px;
2546
+ max-width: 150px;
2547
+ padding: 5px 10px 5px 24px;
2548
+ border: 1px solid color-mix(in srgb, var(--rule) 78%, transparent);
2549
+ border-radius: 999px 8px 8px 999px;
2550
+ background: color-mix(in srgb, var(--surface) 70%, transparent);
2551
+ box-shadow: 0 8px 16px rgba(36, 31, 26, .06);
2552
+ }
2553
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .node[data-visual-role="landmark"] {
2554
+ border-color: color-mix(in srgb, var(--line) 38%, transparent);
2555
+ background: color-mix(in srgb, var(--surface) 64%, transparent);
2556
+ box-shadow: 0 10px 20px rgba(0, 0, 0, .22);
2557
+ }
2558
+ .node[data-visual-role="landmark"]::before {
2559
+ inset: auto auto auto 9px;
2560
+ top: 50%;
2561
+ width: 8px;
2562
+ height: 8px;
2563
+ border-radius: 999px;
2564
+ background: var(--night);
2565
+ opacity: .78;
2566
+ translate: 0 -50%;
2567
+ }
2568
+ .node[data-visual-role="landmark"][data-type="topic"]::before { background: var(--cinnabar); }
2569
+ .node[data-visual-role="landmark"][data-type="source"]::before { background: var(--jade); }
2570
+ .node[data-visual-role="landmark"] .node-kind,
2571
+ .node[data-visual-role="landmark"] .node-meta { display: none; }
2572
+ .node[data-visual-role="landmark"] .node-name {
2573
+ max-width: 116px;
2574
+ margin-top: 0;
2575
+ font-size: 12px;
2576
+ line-height: 1.2;
2577
+ }
2578
+ .node[data-visual-role="index-slip"],
2579
+ .node[data-visual-role="cinnabar-note"] {
2580
+ min-height: 42px;
2581
+ max-width: 182px;
2582
+ padding: 8px 11px 8px 13px;
2583
+ border-radius: 8px 12px 12px 8px;
2584
+ background: color-mix(in srgb, var(--surface) 92%, transparent);
2585
+ box-shadow: 0 13px 24px rgba(36, 31, 26, .1), inset 0 0 0 1px rgba(255, 255, 255, .32);
2586
+ }
2587
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .node[data-visual-role="index-slip"],
2588
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .node[data-visual-role="cinnabar-note"] {
2589
+ background: color-mix(in srgb, var(--surface-2) 88%, transparent);
2590
+ box-shadow: 0 16px 30px rgba(0, 0, 0, .38), inset 0 0 0 1px rgba(245, 240, 230, .09);
2591
+ }
2592
+ .node[data-visual-role="cinnabar-note"] {
2593
+ border-color: color-mix(in srgb, var(--cinnabar) 78%, transparent);
2594
+ box-shadow: 0 17px 30px color-mix(in srgb, var(--cinnabar) 18%, transparent), 0 0 0 4px color-mix(in srgb, var(--cinnabar) 11%, transparent);
2595
+ }
2596
+ .node.is-disabled { opacity: .72; }
2597
+ .node.is-diff-added {
2598
+ animation: llm-wiki-node-grow .96s cubic-bezier(.18,.82,.22,1) both;
2599
+ animation-delay: var(--diff-delay, 0ms);
2600
+ }
2601
+ .node.is-diff-removed {
2602
+ animation: llm-wiki-fade-out .72s ease forwards;
2603
+ }
2604
+ .node.is-diff-recolored {
2605
+ animation: llm-wiki-node-recolor .92s ease both;
2606
+ }
2607
+ .mini-map {
2608
+ position: absolute;
2609
+ right: 16px;
2610
+ bottom: 16px;
2611
+ z-index: 4;
2612
+ width: 160px;
2613
+ height: 54px;
2614
+ border: 1px solid color-mix(in srgb, var(--rule) 86%, transparent);
2615
+ border-radius: var(--radius);
2616
+ background: color-mix(in srgb, var(--surface) 74%, transparent);
2617
+ box-shadow: var(--soft-shadow);
2618
+ }
2619
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .mini-map,
2620
+ .llm-wiki-graph-engine[data-theme="mo-ye"] .graph-reader {
2621
+ border-color: color-mix(in srgb, var(--line) 34%, transparent);
2622
+ background: color-mix(in srgb, var(--surface) 88%, transparent);
2623
+ box-shadow: var(--soft-shadow), inset 0 0 0 1px rgba(245, 240, 230, .05);
2624
+ }
2625
+ .mini-map svg {
2626
+ width: 100%;
2627
+ height: 100%;
2628
+ display: block;
2629
+ }
2630
+ .mini-map .is-selected {
2631
+ stroke: var(--cinnabar);
2632
+ stroke-width: 1.5;
2633
+ }
2634
+ .mini-map-viewport {
2635
+ fill: color-mix(in srgb, var(--cinnabar) 7%, transparent);
2636
+ stroke: color-mix(in srgb, var(--cinnabar) 78%, transparent);
2637
+ stroke-width: 1.2;
2638
+ rx: 3;
2639
+ pointer-events: none;
2640
+ }
2641
+ .graph-reader {
2642
+ position: absolute;
2643
+ top: 16px;
2644
+ right: 16px;
2645
+ z-index: 6;
2646
+ display: flex;
2647
+ flex-direction: column;
2648
+ width: min(360px, calc(100% - 32px));
2649
+ max-height: calc(100% - 100px);
2650
+ border: 1px solid color-mix(in srgb, var(--rule) 82%, transparent);
2651
+ border-radius: var(--radius);
2652
+ background: color-mix(in srgb, var(--surface) 92%, transparent);
2653
+ box-shadow: var(--soft-shadow);
2654
+ opacity: 0;
2655
+ pointer-events: none;
2656
+ transform: translateY(-4px);
2657
+ transition: opacity .18s ease, transform .18s ease;
2658
+ }
2659
+ .graph-reader[data-state="open"] {
2660
+ opacity: 1;
2661
+ pointer-events: auto;
2662
+ transform: translateY(0);
2663
+ }
2664
+ .graph-reader-header {
2665
+ position: relative;
2666
+ padding: 14px 42px 10px 14px;
2667
+ border-bottom: 1px solid color-mix(in srgb, var(--rule) 72%, transparent);
2668
+ }
2669
+ .graph-reader-title {
2670
+ overflow: hidden;
2671
+ text-overflow: ellipsis;
2672
+ white-space: nowrap;
2673
+ font-family: var(--font-serif);
2674
+ font-size: 16px;
2675
+ font-weight: 700;
2676
+ }
2677
+ .graph-reader-meta {
2678
+ margin-top: 4px;
2679
+ display: flex;
2680
+ flex-wrap: wrap;
2681
+ gap: 4px 8px;
2682
+ overflow: hidden;
2683
+ color: var(--muted);
2684
+ font-size: 11px;
2685
+ }
2686
+ .graph-reader-meta span {
2687
+ max-width: 100%;
2688
+ overflow: hidden;
2689
+ text-overflow: ellipsis;
2690
+ white-space: nowrap;
2691
+ }
2692
+ .graph-reader-close {
2693
+ position: absolute;
2694
+ top: 9px;
2695
+ right: 10px;
2696
+ width: 26px;
2697
+ height: 26px;
2698
+ border: 1px solid color-mix(in srgb, var(--rule) 78%, transparent);
2699
+ border-radius: 999px;
2700
+ background: color-mix(in srgb, var(--bg) 72%, transparent);
2701
+ color: var(--ink);
2702
+ }
2703
+ .graph-reader-body {
2704
+ min-height: 0;
2705
+ overflow: auto;
2706
+ padding: 12px 14px 14px;
2707
+ }
2708
+ .graph-reader-source {
2709
+ display: inline-block;
2710
+ max-width: 100%;
2711
+ margin-bottom: 10px;
2712
+ overflow: hidden;
2713
+ text-overflow: ellipsis;
2714
+ vertical-align: top;
2715
+ white-space: nowrap;
2716
+ color: var(--cinnabar);
2717
+ font-size: 12px;
2718
+ }
2719
+ .graph-reader-body pre {
2720
+ margin: 0;
2721
+ white-space: pre-wrap;
2722
+ overflow-wrap: anywhere;
2723
+ font-family: var(--font-serif);
2724
+ font-size: 13px;
2725
+ line-height: 1.65;
2726
+ }
2727
+ .graph-reader-empty {
2728
+ margin: 0;
2729
+ padding: 12px 14px;
2730
+ color: var(--muted);
2731
+ font-size: 13px;
2732
+ }
2733
+ @keyframes llm-wiki-node-grow {
2734
+ 0% {
2735
+ opacity: 0;
2736
+ translate: calc(-50% + var(--diff-anchor-dx, 0px)) calc(-50% + var(--diff-anchor-dy, 0px));
2737
+ transform: scale(.68);
2738
+ }
2739
+ 100% {
2740
+ opacity: 1;
2741
+ translate: -50% -50%;
2742
+ transform: scale(1);
2743
+ }
2744
+ }
2745
+ @keyframes llm-wiki-edge-draw {
2746
+ to { stroke-dashoffset: 0; }
2747
+ }
2748
+ @keyframes llm-wiki-fade-out {
2749
+ to { opacity: 0; transform: scale(.82); }
2750
+ }
2751
+ @keyframes llm-wiki-node-recolor {
2752
+ 0% { filter: saturate(.55) brightness(1.18); }
2753
+ 100% { filter: saturate(1) brightness(1); }
2754
+ }
2755
+ @keyframes llm-wiki-community-emerge {
2756
+ 0% { opacity: 0; transform: scale(.82); }
2757
+ 100% { transform: scale(1); }
2758
+ }
2759
+ `;
2760
+
2761
+ function animationDurationMs(diff: GraphDiff): number {
2762
+ const stagger = Math.min(diff.addedNodes.length * 55, 550);
2763
+ const complexity = (diff.addedEdges.length + diff.removedEdges.length + diff.recoloredNodes.length + diff.newCommunities.length) * 24;
2764
+ return Math.min(3000, 1120 + stagger + complexity);
2765
+ }
2766
+
2767
+ function prefersReducedMotion(doc: Document): boolean {
2768
+ return doc.defaultView?.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true;
2769
+ }
2770
+
2771
+ function wait(ms: number): Promise<void> {
2772
+ return new Promise((resolve) => setTimeout(resolve, ms));
2773
+ }
2774
+
2775
+ function renderMarkdown(markdown: string): string | null {
2776
+ const runtime = globalThis as unknown as {
2777
+ marked?: { parse?: (input: string, options?: Record<string, unknown>) => string };
2778
+ DOMPurify?: { sanitize?: (input: string, options?: Record<string, unknown>) => string };
2779
+ };
2780
+ if (typeof runtime.marked?.parse !== "function" || typeof runtime.DOMPurify?.sanitize !== "function") return null;
2781
+ const html = runtime.marked.parse(markdown, { breaks: false, gfm: true });
2782
+ return runtime.DOMPurify.sanitize(html, { ADD_ATTR: ["target", "data-target", "tabindex"] });
2783
+ }