@joekytc/dsh-swarm 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/README.zh-CN.md +3 -3
- package/client/BoardCard.tsx +24 -2
- package/client/ConfigSection.tsx +71 -0
- package/client/ConfigSelect.tsx +69 -0
- package/client/KanbanBoard.tsx +3 -0
- package/client/KanbanTab.tsx +4 -3
- package/client/TaskDrawer.tsx +2 -26
- package/client/WorkflowRail.tsx +3 -1
- package/client/config-store.ts +51 -0
- package/client/config.css +110 -0
- package/client/index.ts +39 -4
- package/client/kanban.css +19 -2
- package/client/session-bridge.ts +39 -0
- package/client/timeline-model.ts +10 -1
- package/client/workflow-model.ts +3 -1
- package/lib/client.js +358 -53
- package/lib/config.d.ts +8 -1
- package/lib/config.js +6 -1
- package/lib/dispatcher/agent-runner.d.ts +28 -6
- package/lib/dispatcher/agent-runner.js +175 -62
- package/lib/dispatcher/chain-auditor.js +4 -1
- package/lib/dispatcher/dispatcher.d.ts +62 -3
- package/lib/dispatcher/dispatcher.js +243 -20
- package/lib/dispatcher/session-events.d.ts +11 -15
- package/lib/dispatcher/session-events.js +17 -5
- package/lib/dispatcher/v-orchestrator.d.ts +42 -9
- package/lib/dispatcher/v-orchestrator.js +236 -28
- package/lib/domain/config-override.d.ts +38 -0
- package/lib/domain/config-override.js +101 -0
- package/lib/domain/delivery-contract.d.ts +3 -2
- package/lib/domain/delivery-contract.js +28 -16
- package/lib/domain/gate-policy.d.ts +26 -0
- package/lib/domain/gate-policy.js +44 -0
- package/lib/domain/kanban-service.d.ts +16 -3
- package/lib/domain/kanban-service.js +41 -6
- package/lib/domain/permissions.js +1 -1
- package/lib/domain/planning-checklist.d.ts +5 -0
- package/lib/domain/planning-checklist.js +80 -6
- package/lib/domain/projection.js +2 -1
- package/lib/domain/review-evidence.js +1 -1
- package/lib/domain/state-machine.js +2 -1
- package/lib/domain/types.d.ts +2 -2
- package/lib/index.js +19 -21
- package/lib/roles/clean-fs-tools.d.ts +22 -0
- package/lib/roles/clean-fs-tools.js +118 -0
- package/lib/roles/skill-installer.d.ts +6 -0
- package/lib/roles/skill-installer.js +32 -0
- package/lib/roles/toolsets.d.ts +13 -1
- package/lib/roles/toolsets.js +163 -31
- package/lib/roles/wiki-worker.d.ts +1 -0
- package/lib/roles/wiki-worker.js +10 -2
- package/lib/routes/kanban-http.d.ts +5 -2
- package/lib/routes/kanban-http.js +36 -3
- package/lib/routes/planning-driver.d.ts +3 -1
- package/lib/routes/planning-driver.js +10 -6
- package/lib/routes/prefix-router.d.ts +16 -0
- package/lib/routes/prefix-router.js +37 -2
- package/lib/services/config-provider.d.ts +42 -0
- package/lib/services/config-provider.js +94 -0
- package/lib/services/gate-runner.d.ts +24 -0
- package/lib/services/gate-runner.js +87 -0
- package/lib/services/kanban-provider.d.ts +5 -1
- package/lib/services/kanban-provider.js +40 -2
- package/lib/services/llm-catalog.d.ts +35 -0
- package/lib/services/llm-catalog.js +19 -0
- package/lib/tools/kanban-tools.js +1 -0
- package/lib/tools/main-session-tools.d.ts +17 -2
- package/lib/tools/main-session-tools.js +88 -21
- package/lib/tools/planning-tools.d.ts +4 -0
- package/lib/tools/planning-tools.js +39 -12
- package/lib/tools/prefetch-tools.js +1 -0
- package/lib/tools/spec-card-tools.js +1 -0
- package/lib/tools/wiki-tools.js +5 -4
- package/lib/wiki/kb-linkage.d.ts +9 -2
- package/lib/wiki/kb-linkage.js +3 -3
- package/lib/wiki/local-kb-client.d.ts +14 -0
- package/lib/wiki/local-kb-client.js +101 -0
- package/lib/wiki/local-kb.d.ts +6 -0
- package/lib/wiki/local-kb.js +25 -0
- package/lib/wiki/memory-recall.d.ts +14 -6
- package/lib/wiki/memory-recall.js +17 -13
- package/lib/wiki/page-path.d.ts +9 -3
- package/lib/wiki/page-path.js +29 -11
- package/lib/wiki/wiki-vault-client.d.ts +3 -3
- package/lib/wiki/wiki-vault-client.js +6 -6
- package/package.json +16 -11
- package/personas/kanban-d/preset.yml +1 -1
- package/personas/kanban-dt/agent.cordis.yml +2 -2
- package/personas/kanban-dt/preset.yml +1 -1
- package/personas/kanban-p/agent.cordis.yml +16 -0
- package/personas/kanban-p/preset.yml +1 -1
- package/personas/kanban-pt/agent.cordis.yml +38 -8
- package/personas/kanban-pt/preset.yml +1 -1
- package/personas/kanban-w/agent.cordis.yml +35 -16
- package/personas/kanban-w/preset.yml +1 -1
- package/personas/persona-dt.md +1 -1
- package/personas/persona-p.md +4 -1
- package/personas/persona-pt.md +13 -6
- package/personas/persona-w.md +11 -9
- package/skills/grill-me/SKILL.md +71 -0
- package/skills/llm-wiki-skill/.mise.toml +2 -0
- package/skills/llm-wiki-skill/.nvmrc +1 -0
- package/skills/llm-wiki-skill/AGENTS.md +173 -0
- package/skills/llm-wiki-skill/CHANGELOG.md +548 -0
- package/skills/llm-wiki-skill/CLAUDE.md +152 -0
- package/skills/llm-wiki-skill/HERMES.md +44 -0
- package/skills/llm-wiki-skill/README.en.md +261 -0
- package/skills/llm-wiki-skill/README.md +291 -0
- package/skills/llm-wiki-skill/SKILL.md +1133 -0
- package/skills/llm-wiki-skill/assets/graph-demo.gif +0 -0
- package/skills/llm-wiki-skill/assets/graph-preview.png +0 -0
- package/skills/llm-wiki-skill/deps/LICENSE-d3.txt +13 -0
- package/skills/llm-wiki-skill/deps/LICENSE-marked.txt +44 -0
- package/skills/llm-wiki-skill/deps/LICENSE-purify.txt +568 -0
- package/skills/llm-wiki-skill/deps/LICENSE-roughjs.txt +21 -0
- package/skills/llm-wiki-skill/deps/baoyu-url-to-markdown/SKILL.md +261 -0
- package/skills/llm-wiki-skill/deps/baoyu-url-to-markdown/references/config/first-time-setup.md +106 -0
- package/skills/llm-wiki-skill/deps/baoyu-url-to-markdown/scripts/cdp.ts +179 -0
- package/skills/llm-wiki-skill/deps/baoyu-url-to-markdown/scripts/constants.ts +13 -0
- package/skills/llm-wiki-skill/deps/baoyu-url-to-markdown/scripts/defuddle-converter.ts +58 -0
- package/skills/llm-wiki-skill/deps/baoyu-url-to-markdown/scripts/html-to-markdown.ts +135 -0
- package/skills/llm-wiki-skill/deps/baoyu-url-to-markdown/scripts/legacy-converter.ts +629 -0
- package/skills/llm-wiki-skill/deps/baoyu-url-to-markdown/scripts/main.ts +314 -0
- package/skills/llm-wiki-skill/deps/baoyu-url-to-markdown/scripts/markdown-conversion-shared.ts +305 -0
- package/skills/llm-wiki-skill/deps/baoyu-url-to-markdown/scripts/media-localizer.ts +317 -0
- package/skills/llm-wiki-skill/deps/baoyu-url-to-markdown/scripts/package.json +14 -0
- package/skills/llm-wiki-skill/deps/baoyu-url-to-markdown/scripts/paths.ts +29 -0
- package/skills/llm-wiki-skill/deps/baoyu-url-to-markdown/scripts/vendor/baoyu-chrome-cdp/package.json +9 -0
- package/skills/llm-wiki-skill/deps/baoyu-url-to-markdown/scripts/vendor/baoyu-chrome-cdp/src/index.test.ts +307 -0
- package/skills/llm-wiki-skill/deps/baoyu-url-to-markdown/scripts/vendor/baoyu-chrome-cdp/src/index.ts +523 -0
- package/skills/llm-wiki-skill/deps/d3.min.js +2 -0
- package/skills/llm-wiki-skill/deps/marked.min.js +6 -0
- package/skills/llm-wiki-skill/deps/purify.min.js +3 -0
- package/skills/llm-wiki-skill/deps/rough.min.js +7 -0
- package/skills/llm-wiki-skill/deps/youtube-transcript/SKILL.md +47 -0
- package/skills/llm-wiki-skill/deps/youtube-transcript/scripts/get_transcript.py +72 -0
- package/skills/llm-wiki-skill/designs/graph-edge-legibility/graph-edge-treatments.html +480 -0
- package/skills/llm-wiki-skill/docs/design/oriental-atlas/DESIGN.md +183 -0
- package/skills/llm-wiki-skill/docs/design/oriental-atlas/README.md +10 -0
- package/skills/llm-wiki-skill/docs/design/oriental-atlas/design-brief.md +170 -0
- package/skills/llm-wiki-skill/docs/design/oriental-atlas/oriental-editorial-atlas.html +1908 -0
- package/skills/llm-wiki-skill/docs/design/oriental-atlas/reviews/result-review.md +68 -0
- package/skills/llm-wiki-skill/docs/graph/2026-06-16-interaction-risk-audit.md +88 -0
- package/skills/llm-wiki-skill/docs/graph/2026-06-16-interaction-test-coverage-map.md +212 -0
- package/skills/llm-wiki-skill/docs/graph/performance/2026-06-18-phase-0-2-baseline.md +86 -0
- package/skills/llm-wiki-skill/docs/graph/performance/2026-06-18-phase-1-3-dom-svg-large-baseline.md +85 -0
- package/skills/llm-wiki-skill/docs/graph/performance/2026-06-18-phase-1-4-disposable-validation-gate.md +97 -0
- package/skills/llm-wiki-skill/docs/graph/performance/2026-06-18-phase-2-4-global-renderer-trial-matrix.md +168 -0
- package/skills/llm-wiki-skill/docs/graph/performance/2026-06-19-phase-6-1-sigma-graphology-trial.md +71 -0
- package/skills/llm-wiki-skill/docs/graph/performance/2026-06-19-phase-6-2-vis-network-trial.md +78 -0
- package/skills/llm-wiki-skill/docs/graph/performance/2026-06-19-phase-6-3-aggregation-fallback-trial.md +72 -0
- package/skills/llm-wiki-skill/docs/graph/performance/2026-06-19-phase-6-4-global-renderer-route-decision.md +94 -0
- package/skills/llm-wiki-skill/docs/graph/performance/2026-06-19-sigma-global-production-integration-result.md +102 -0
- package/skills/llm-wiki-skill/docs/graph/performance/artifacts/2026-06-18-phase-0-2/stage-4.5-offline-dense-wheel.json +9 -0
- package/skills/llm-wiki-skill/docs/graph/performance/artifacts/2026-06-18-phase-0-2/stage-4.5-offline-dense-wheel.png +0 -0
- package/skills/llm-wiki-skill/docs/graph/performance/artifacts/2026-06-18-phase-0-2/stage-4.5-offline-navigation.png +0 -0
- package/skills/llm-wiki-skill/docs/spark/2026-06-15-omp-tool-status-events-design.md +174 -0
- package/skills/llm-wiki-skill/docs/spark/2026-06-16-graph-interaction-geometry-design.md +832 -0
- package/skills/llm-wiki-skill/docs/spark/2026-06-16-graph-six-layer-architecture-design.md +577 -0
- package/skills/llm-wiki-skill/docs/spark/2026-06-17-static-renderer-coordination-split-design.md +172 -0
- package/skills/llm-wiki-skill/docs/spark/2026-06-18-large-graph-global-community-design.md +909 -0
- package/skills/llm-wiki-skill/docs/spark/2026-06-19-sigma-global-graph-renderer-design.md +360 -0
- package/skills/llm-wiki-skill/docs/spark/2026-06-20-paper-ui-port-design.md +162 -0
- package/skills/llm-wiki-skill/docs/spark/2026-06-20-paper-ui-v2-shell-alignment-design.md +384 -0
- package/skills/llm-wiki-skill/docs/spark/2026-06-21-graph-experience-completion-design.md +534 -0
- package/skills/llm-wiki-skill/docs/spark/2026-06-22-chat-auto-scroll-design.md +185 -0
- package/skills/llm-wiki-skill/docs/spark/2026-06-22-graph-community-node-visual-design.md +266 -0
- package/skills/llm-wiki-skill/docs/spark/2026-06-25-sigma-overlay-refactor-perf-design.md +170 -0
- package/skills/llm-wiki-skill/docs/spark/2026-06-26-global-sigma-community-spotlight-design.md +393 -0
- package/skills/llm-wiki-skill/docs/spark/mockups/2026-06-18-large-graph-global-community-flow.html +746 -0
- package/skills/llm-wiki-skill/install.ps1 +87 -0
- package/skills/llm-wiki-skill/install.sh +722 -0
- package/skills/llm-wiki-skill/package-lock.json +11171 -0
- package/skills/llm-wiki-skill/package.json +24 -0
- package/skills/llm-wiki-skill/packages/graph-engine/package.json +37 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/anim/.gitkeep +1 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/anim/index.ts +179 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/architecture.ts +79 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/diff.ts +121 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/facade.ts +1406 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/graph-node.ts +37 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/index.ts +25 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/layout/edge-geometry.ts +17 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/layout/index.ts +16 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/layout/spatial-index.ts +509 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/model/atlas.ts +18 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/model/index.ts +10 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/model/labels.ts +11 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/model/layout.ts +81 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/model/learning.ts +8 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/model/legacy-helpers.ts +1379 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/model/queue.ts +7 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/model/storage.ts +4 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/model/visibility.ts +9 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/.gitkeep +1 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/adapter.ts +443 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/aggregation-containers.ts +64 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/community-cloud-geometry.ts +178 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/community-wash.ts +238 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/community-washes.ts +19 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/compat-contract.ts +14 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/controller.ts +705 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/controls.ts +288 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/dom-svg-renderer.ts +180 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/edges.ts +51 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/geometry.ts +380 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/gestures.ts +673 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/graph-renderer-root.ts +387 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/hit-testing.ts +56 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/host-dom.ts +14 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/hover-card.ts +37 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/index.ts +213 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/keyboard.ts +48 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/legend.ts +25 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/minimap.ts +45 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/model.ts +1339 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/node-drag-lifecycle.ts +94 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/nodes.ts +103 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/offline-reader.ts +209 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/overlays-presenter.ts +192 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/overlays.ts +73 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/pin-position.ts +51 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/preview.ts +65 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/relation-focus.ts +91 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/render-context.ts +85 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/render-pipeline.ts +871 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/render-styles.ts +1405 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/renderer-surface.ts +75 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/search.ts +57 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/sigma-coordinates.ts +44 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/sigma-global-drag.ts +246 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/sigma-global-renderer.ts +1425 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/sigma-overlay-svg.ts +121 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/simulation-bridge.ts +59 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/state.ts +262 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/toolbar.ts +40 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/render/viewport.ts +336 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/select/.gitkeep +1 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/select/index.ts +189 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/sim/index.ts +372 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/sim/pins.ts +84 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/summary/index.ts +447 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/themes/.gitkeep +1 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/themes/index.ts +8 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/themes/tokens.ts +117 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/types/d3-quadtree.d.ts +22 -0
- package/skills/llm-wiki-skill/packages/graph-engine/src/types.ts +531 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/aggregation-fallback-trial-adapter.test.ts +115 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/aggregation-fallback-trial-adapter.ts +222 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/anim.test.ts +121 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/architecture.test.ts +63 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/community-cloud-geometry.test.ts +86 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/community-wash.test.ts +97 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/diff.test.ts +133 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/facade.test.ts +1369 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/geometry.test.ts +147 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/gestures.test.ts +768 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/graph-layout.test.ts +110 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/helpers.test.ts +287 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/interaction-contract.test.ts +106 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/keyboard.test.ts +59 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/large-fixtures.test.ts +47 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/large-graph-fixtures.ts +420 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/learning.test.ts +259 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/node-drag-lifecycle.test.ts +166 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/overlays.test.ts +115 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/preview.test.ts +56 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/queue.test.ts +114 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/relation-focus.test.ts +115 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/render-model.test.ts +1030 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/renderer-adapter-contract.test.ts +271 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/renderer-boundary.test.ts +618 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/renderer-lifecycle.test.ts +1711 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/route-continuity.test.ts +284 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/runtime-state.test.ts +281 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/search-and-legend.test.ts +124 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/select.test.ts +168 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/sigma-coordinates.test.ts +50 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/sigma-global-renderer.test.ts +2210 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/sigma-trial-adapter.test.ts +82 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/sigma-trial-adapter.ts +271 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/sim.test.ts +207 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/simulation-bridge.test.ts +96 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/spatial-index.test.ts +222 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/state.test.ts +189 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/summary-contract.test.ts +422 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/themes.test.ts +62 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/toolbar.test.ts +64 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/viewport.test.ts +213 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/vis-network-trial-adapter.test.ts +82 -0
- package/skills/llm-wiki-skill/packages/graph-engine/test/vis-network-trial-adapter.ts +186 -0
- package/skills/llm-wiki-skill/packages/graph-engine/tsconfig.json +17 -0
- package/skills/llm-wiki-skill/packages/graph-engine/vite.config.ts +15 -0
- package/skills/llm-wiki-skill/platforms/claude/CLAUDE.md +37 -0
- package/skills/llm-wiki-skill/platforms/claude/companions/llm-wiki-upgrade/SKILL.md +95 -0
- package/skills/llm-wiki-skill/platforms/codex/AGENTS.md +23 -0
- package/skills/llm-wiki-skill/platforms/hermes/README.md +31 -0
- package/skills/llm-wiki-skill/platforms/openclaw/README.md +31 -0
- package/skills/llm-wiki-skill/scripts/adapter-state.sh +424 -0
- package/skills/llm-wiki-skill/scripts/build-graph-data.sh +395 -0
- package/skills/llm-wiki-skill/scripts/build-graph-html.sh +459 -0
- package/skills/llm-wiki-skill/scripts/cache.sh +352 -0
- package/skills/llm-wiki-skill/scripts/create-source-page.sh +100 -0
- package/skills/llm-wiki-skill/scripts/delete-helper.sh +68 -0
- package/skills/llm-wiki-skill/scripts/graph-analysis.js +732 -0
- package/skills/llm-wiki-skill/scripts/hook-session-start.sh +46 -0
- package/skills/llm-wiki-skill/scripts/init-wiki.sh +95 -0
- package/skills/llm-wiki-skill/scripts/lib/source-signal-eligibility.js +158 -0
- package/skills/llm-wiki-skill/scripts/lint-fix.sh +114 -0
- package/skills/llm-wiki-skill/scripts/lint-runner.sh +217 -0
- package/skills/llm-wiki-skill/scripts/runtime-context.sh +77 -0
- package/skills/llm-wiki-skill/scripts/shared-config.sh +83 -0
- package/skills/llm-wiki-skill/scripts/source-record-contract.tsv +10 -0
- package/skills/llm-wiki-skill/scripts/source-registry.sh +349 -0
- package/skills/llm-wiki-skill/scripts/source-registry.tsv +10 -0
- package/skills/llm-wiki-skill/scripts/source-signal-coverage.js +83 -0
- package/skills/llm-wiki-skill/scripts/validate-step1.sh +144 -0
- package/skills/llm-wiki-skill/scripts/wiki-compat.sh +267 -0
- package/skills/llm-wiki-skill/setup.sh +8 -0
- package/skills/llm-wiki-skill/templates/entity-template.md +30 -0
- package/skills/llm-wiki-skill/templates/graph-styles/wash/footer.html +27 -0
- package/skills/llm-wiki-skill/templates/graph-styles/wash/graph-wash-helpers.js +1326 -0
- package/skills/llm-wiki-skill/templates/graph-styles/wash/graph-wash.js +1009 -0
- package/skills/llm-wiki-skill/templates/graph-styles/wash/header.html +2002 -0
- package/skills/llm-wiki-skill/templates/index-en-template.md +51 -0
- package/skills/llm-wiki-skill/templates/index-template.md +51 -0
- package/skills/llm-wiki-skill/templates/log-en-template.md +11 -0
- package/skills/llm-wiki-skill/templates/log-template.md +11 -0
- package/skills/llm-wiki-skill/templates/overview-en-template.md +41 -0
- package/skills/llm-wiki-skill/templates/overview-template.md +41 -0
- package/skills/llm-wiki-skill/templates/purpose-en-template.md +16 -0
- package/skills/llm-wiki-skill/templates/purpose-template.md +16 -0
- package/skills/llm-wiki-skill/templates/query-template.md +19 -0
- package/skills/llm-wiki-skill/templates/schema-template.md +185 -0
- package/skills/llm-wiki-skill/templates/source-template.md +51 -0
- package/skills/llm-wiki-skill/templates/synthesis-template.md +25 -0
- package/skills/llm-wiki-skill/templates/topic-template.md +35 -0
- package/skills/llm-wiki-skill/tests/adapter-state.sh +338 -0
- package/skills/llm-wiki-skill/tests/browser/graph-aggregation-fallback-trial.ts +737 -0
- package/skills/llm-wiki-skill/tests/browser/graph-community-node-map.mjs +255 -0
- package/skills/llm-wiki-skill/tests/browser/graph-community-wash-interactions.mjs +170 -0
- package/skills/llm-wiki-skill/tests/browser/graph-default-behavior.mjs +630 -0
- package/skills/llm-wiki-skill/tests/browser/graph-density-preview.mjs +53 -0
- package/skills/llm-wiki-skill/tests/browser/graph-html-insights.mjs +41 -0
- package/skills/llm-wiki-skill/tests/browser/graph-large-performance.ts +689 -0
- package/skills/llm-wiki-skill/tests/browser/graph-node-slim.mjs +75 -0
- package/skills/llm-wiki-skill/tests/browser/graph-offline-phase-6.mjs +660 -0
- package/skills/llm-wiki-skill/tests/browser/graph-renderer-trial-shared.ts +291 -0
- package/skills/llm-wiki-skill/tests/browser/graph-sigma-global-production.ts +1437 -0
- package/skills/llm-wiki-skill/tests/browser/graph-sigma-graphology-trial.ts +949 -0
- package/skills/llm-wiki-skill/tests/browser/graph-stage-4-5.mjs +815 -0
- package/skills/llm-wiki-skill/tests/browser/graph-vis-network-trial.ts +762 -0
- package/skills/llm-wiki-skill/tests/browser/graph-workbench-interactions.mjs +730 -0
- package/skills/llm-wiki-skill/tests/browser/validate-graph-trial-result.mjs +149 -0
- package/skills/llm-wiki-skill/tests/expected/graph-data-empty.json +60 -0
- package/skills/llm-wiki-skill/tests/expected/graph-data-sample.json +396 -0
- package/skills/llm-wiki-skill/tests/expected/graph-interactive-basic.html +1736 -0
- package/skills/llm-wiki-skill/tests/expected/lint-output.txt +44 -0
- package/skills/llm-wiki-skill/tests/fixtures/coverage-sample-wiki/index.md +1 -0
- package/skills/llm-wiki-skill/tests/fixtures/coverage-sample-wiki/wiki/entities/Alpha.md +10 -0
- package/skills/llm-wiki-skill/tests/fixtures/coverage-sample-wiki/wiki/entities/Beta.md +7 -0
- package/skills/llm-wiki-skill/tests/fixtures/coverage-sample-wiki/wiki/entities/Delta.md +8 -0
- package/skills/llm-wiki-skill/tests/fixtures/coverage-sample-wiki/wiki/entities/Gamma.md +8 -0
- package/skills/llm-wiki-skill/tests/fixtures/coverage-sample-wiki/wiki/queries/20260422-abc.md +10 -0
- package/skills/llm-wiki-skill/tests/fixtures/coverage-sample-wiki/wiki/synthesis/Crystal.md +8 -0
- package/skills/llm-wiki-skill/tests/fixtures/coverage-sample-wiki/wiki/topics/Overview.md +9 -0
- package/skills/llm-wiki-skill/tests/fixtures/graph-data-empty-wiki/wiki/entities/.gitkeep +0 -0
- package/skills/llm-wiki-skill/tests/fixtures/graph-data-sample-wiki/purpose.md +3 -0
- package/skills/llm-wiki-skill/tests/fixtures/graph-data-sample-wiki/wiki/entities/Attention.md +7 -0
- package/skills/llm-wiki-skill/tests/fixtures/graph-data-sample-wiki/wiki/entities/Decoder.md +7 -0
- package/skills/llm-wiki-skill/tests/fixtures/graph-data-sample-wiki/wiki/entities/Encoder.md +7 -0
- package/skills/llm-wiki-skill/tests/fixtures/graph-data-sample-wiki/wiki/entities/GPT.md +7 -0
- package/skills/llm-wiki-skill/tests/fixtures/graph-data-sample-wiki/wiki/entities/Transformer.md +13 -0
- package/skills/llm-wiki-skill/tests/fixtures/graph-data-sample-wiki/wiki/sources/paper.md +9 -0
- package/skills/llm-wiki-skill/tests/fixtures/graph-data-sample-wiki/wiki/topics/arch.md +12 -0
- package/skills/llm-wiki-skill/tests/fixtures/graph-data-sample-wiki/wiki/topics/finetune.md +9 -0
- package/skills/llm-wiki-skill/tests/fixtures/graph-interactive-basic/wiki/graph-data.json +40 -0
- package/skills/llm-wiki-skill/tests/fixtures/graph-interactive-dense/wiki/graph-data.json +3965 -0
- package/skills/llm-wiki-skill/tests/fixtures/graph-interactive-multicomm/wiki/graph-data.json +212 -0
- package/skills/llm-wiki-skill/tests/fixtures/lint-sample-wiki/index.md +14 -0
- package/skills/llm-wiki-skill/tests/fixtures/lint-sample-wiki/wiki/entities/C++.md +6 -0
- package/skills/llm-wiki-skill/tests/fixtures/lint-sample-wiki/wiki/entities/Lonely.md +4 -0
- package/skills/llm-wiki-skill/tests/fixtures/lint-sample-wiki/wiki/entities/Other.md +8 -0
- package/skills/llm-wiki-skill/tests/fixtures/lint-sample-wiki/wiki/entities/Real.md +7 -0
- package/skills/llm-wiki-skill/tests/fixtures/lint-sample-wiki/wiki/sources/2026-01-01-test-source.md +15 -0
- package/skills/llm-wiki-skill/tests/fixtures/lint-sample-wiki/wiki/sources/2026-02-01-inline-img.md +12 -0
- package/skills/llm-wiki-skill/tests/fixtures/lint-sample-wiki/wiki/topics/AIbasic.md +3 -0
- package/skills/llm-wiki-skill/tests/graph-aggregation-fallback-trial.regression-1.sh +46 -0
- package/skills/llm-wiki-skill/tests/graph-analysis-helper.regression-1.sh +237 -0
- package/skills/llm-wiki-skill/tests/graph-browser-large-performance.regression-1.sh +39 -0
- package/skills/llm-wiki-skill/tests/graph-browser-stage-4-5.regression-1.sh +163 -0
- package/skills/llm-wiki-skill/tests/graph-build-failures.regression-1.sh +91 -0
- package/skills/llm-wiki-skill/tests/graph-community-node-map.regression-1.sh +107 -0
- package/skills/llm-wiki-skill/tests/graph-community-wash-interactions.regression-1.sh +35 -0
- package/skills/llm-wiki-skill/tests/graph-data-confidence-merge.regression-1.sh +309 -0
- package/skills/llm-wiki-skill/tests/graph-data-source-paths.regression-1.sh +49 -0
- package/skills/llm-wiki-skill/tests/graph-default-behavior.regression-1.sh +35 -0
- package/skills/llm-wiki-skill/tests/graph-html-a11y.regression-1.sh +50 -0
- package/skills/llm-wiki-skill/tests/graph-html-brand-link.regression-1.sh +47 -0
- package/skills/llm-wiki-skill/tests/graph-html-density.regression-1.sh +228 -0
- package/skills/llm-wiki-skill/tests/graph-html-drawer-neighbors.regression-1.sh +47 -0
- package/skills/llm-wiki-skill/tests/graph-html-insights.regression-1.sh +80 -0
- package/skills/llm-wiki-skill/tests/graph-html-long-label.regression-1.sh +92 -0
- package/skills/llm-wiki-skill/tests/graph-html-minimap.regression-1.sh +51 -0
- package/skills/llm-wiki-skill/tests/graph-html-mobile.regression-1.sh +48 -0
- package/skills/llm-wiki-skill/tests/graph-html-oriental-atlas-contract.regression-1.sh +94 -0
- package/skills/llm-wiki-skill/tests/graph-html-oriental-design-contract.regression-1.sh +82 -0
- package/skills/llm-wiki-skill/tests/graph-html-search.regression-1.sh +52 -0
- package/skills/llm-wiki-skill/tests/graph-html-styles.regression-1.sh +66 -0
- package/skills/llm-wiki-skill/tests/graph-html-toolbar.regression-1.sh +62 -0
- package/skills/llm-wiki-skill/tests/graph-offline-phase-6.regression-1.sh +51 -0
- package/skills/llm-wiki-skill/tests/graph-sigma-global-production.regression-1.sh +64 -0
- package/skills/llm-wiki-skill/tests/graph-sigma-graphology-trial.regression-1.sh +46 -0
- package/skills/llm-wiki-skill/tests/graph-vis-network-trial.regression-1.sh +46 -0
- package/skills/llm-wiki-skill/tests/graph-workbench-interactions.regression-1.sh +151 -0
- package/skills/llm-wiki-skill/tests/js/graph-wash-bootstrap.test.js +110 -0
- package/skills/llm-wiki-skill/tests/js/graph-wash-helpers.test.js +296 -0
- package/skills/llm-wiki-skill/tests/js/graph-wash-learning.test.js +260 -0
- package/skills/llm-wiki-skill/tests/js/graph-wash-queue.test.js +115 -0
- package/skills/llm-wiki-skill/tests/js/graph-wash-runtime-state.test.js +282 -0
- package/skills/llm-wiki-skill/tests/js/source-signal-coverage.test.js +48 -0
- package/skills/llm-wiki-skill/tests/js/source-signal-eligibility.test.js +202 -0
- package/skills/llm-wiki-skill/tests/lib/graph-html-engine-helpers.sh +110 -0
- package/skills/llm-wiki-skill/tests/lint-output.regression-1.sh +32 -0
- package/skills/llm-wiki-skill/tests/regression.sh +1754 -0
- package/skills/llm-wiki-skill/workbench/AGENTS.md +110 -0
- package/skills/llm-wiki-skill/workbench/CLAUDE.md +55 -0
- package/skills/llm-wiki-skill/workbench/LICENSE +21 -0
- package/skills/llm-wiki-skill/workbench/PRODUCT.md +1353 -0
- package/skills/llm-wiki-skill/workbench/README.md +30 -0
- package/skills/llm-wiki-skill/workbench/docs/current-kb-retrieval-design.md +587 -0
- package/skills/llm-wiki-skill/workbench/docs/graph-evolution-1-design.md +210 -0
- package/skills/llm-wiki-skill/workbench/docs/stage-2-design.md +1139 -0
- package/skills/llm-wiki-skill/workbench/docs/stage-3-design.md +1269 -0
- package/skills/llm-wiki-skill/workbench/docs/stage-3.5-design.md +1164 -0
- package/skills/llm-wiki-skill/workbench/docs/stage-4-design.md +504 -0
- package/skills/llm-wiki-skill/workbench/docs/stage-4.5-design.md +195 -0
- package/skills/llm-wiki-skill/workbench/docs/superpowers/specs/2026-05-28-resizable-preview-layout-design.md +165 -0
- package/skills/llm-wiki-skill/workbench/docs/superpowers/specs/2026-05-28-settings-panel-scroll-fix.md +50 -0
- package/skills/llm-wiki-skill/workbench/server/package.json +31 -0
- package/skills/llm-wiki-skill/workbench/server/src/agent.ts +457 -0
- package/skills/llm-wiki-skill/workbench/server/src/artifacts.ts +248 -0
- package/skills/llm-wiki-skill/workbench/server/src/auth.ts +164 -0
- package/skills/llm-wiki-skill/workbench/server/src/config.ts +130 -0
- package/skills/llm-wiki-skill/workbench/server/src/conversations.test.ts +159 -0
- package/skills/llm-wiki-skill/workbench/server/src/conversations.ts +270 -0
- package/skills/llm-wiki-skill/workbench/server/src/digest/batch.ts +189 -0
- package/skills/llm-wiki-skill/workbench/server/src/digest/concurrency.test.ts +36 -0
- package/skills/llm-wiki-skill/workbench/server/src/digest/concurrency.ts +31 -0
- package/skills/llm-wiki-skill/workbench/server/src/digest/subagent.ts +91 -0
- package/skills/llm-wiki-skill/workbench/server/src/extensions/artifacts.ts +81 -0
- package/skills/llm-wiki-skill/workbench/server/src/extensions/knowledge-base.ts +263 -0
- package/skills/llm-wiki-skill/workbench/server/src/extensions/new-wiki.ts +42 -0
- package/skills/llm-wiki-skill/workbench/server/src/extensions/synthesis.ts +172 -0
- package/skills/llm-wiki-skill/workbench/server/src/graph-watcher.test.ts +196 -0
- package/skills/llm-wiki-skill/workbench/server/src/graph.test.ts +113 -0
- package/skills/llm-wiki-skill/workbench/server/src/graph.ts +351 -0
- package/skills/llm-wiki-skill/workbench/server/src/index.ts +1148 -0
- package/skills/llm-wiki-skill/workbench/server/src/knowledge-bases.test.ts +30 -0
- package/skills/llm-wiki-skill/workbench/server/src/knowledge-bases.ts +321 -0
- package/skills/llm-wiki-skill/workbench/server/src/pages.test.ts +41 -0
- package/skills/llm-wiki-skill/workbench/server/src/pages.ts +160 -0
- package/skills/llm-wiki-skill/workbench/server/src/retrieval.test.ts +151 -0
- package/skills/llm-wiki-skill/workbench/server/src/retrieval.ts +427 -0
- package/skills/llm-wiki-skill/workbench/server/src/tool-status-events.test.ts +363 -0
- package/skills/llm-wiki-skill/workbench/server/src/tool-status-events.ts +616 -0
- package/skills/llm-wiki-skill/workbench/server/src/wiki-init.test.ts +19 -0
- package/skills/llm-wiki-skill/workbench/server/src/wiki-init.ts +188 -0
- package/skills/llm-wiki-skill/workbench/server/tsconfig.json +10 -0
- package/skills/llm-wiki-skill/workbench/tsconfig.json +20 -0
- package/skills/llm-wiki-skill/workbench/web/README.md +73 -0
- package/skills/llm-wiki-skill/workbench/web/components.json +21 -0
- package/skills/llm-wiki-skill/workbench/web/eslint.config.js +22 -0
- package/skills/llm-wiki-skill/workbench/web/index.html +16 -0
- package/skills/llm-wiki-skill/workbench/web/package.json +54 -0
- package/skills/llm-wiki-skill/workbench/web/src/App.tsx +1195 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/AddExternalDialog.tsx +375 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/AppearancePanel.tsx +158 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/ArtifactView.tsx +15 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/BatchDigestPanel.tsx +117 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/ChatPanel.tsx +890 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/CommandMenu.tsx +75 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/ExportButtons.tsx +58 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/GraphPanel.tsx +800 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/GraphReader.tsx +52 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/GraphSelection.tsx +100 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/GraphSummaryDrawer.tsx +269 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/MainViewTabs.tsx +69 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/MarkdownView.tsx +106 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/NewWikiDialog.tsx +103 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/RefMenu.tsx +44 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/RightDrawer.tsx +298 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/SearchPanel.tsx +165 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/SettingsPanel.tsx +320 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/Sidebar.tsx +354 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/ToolHistorySummary.tsx +111 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/ToolStatusRunway.tsx +70 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/TopBar.tsx +348 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/renderers/DownloadOnlyRenderer.tsx +88 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/renderers/HtmlRenderer.tsx +92 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/ui/button.tsx +52 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/ui/command.tsx +50 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/ui/dialog.tsx +156 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/ui/input.tsx +21 -0
- package/skills/llm-wiki-skill/workbench/web/src/components/ui/tooltip.tsx +57 -0
- package/skills/llm-wiki-skill/workbench/web/src/index.css +4204 -0
- package/skills/llm-wiki-skill/workbench/web/src/lib/api.ts +648 -0
- package/skills/llm-wiki-skill/workbench/web/src/lib/appearance.ts +114 -0
- package/skills/llm-wiki-skill/workbench/web/src/lib/drawer-layout.ts +41 -0
- package/skills/llm-wiki-skill/workbench/web/src/lib/drawer-state.ts +168 -0
- package/skills/llm-wiki-skill/workbench/web/src/lib/graph-reader.ts +25 -0
- package/skills/llm-wiki-skill/workbench/web/src/lib/graph-selection-drawer.ts +35 -0
- package/skills/llm-wiki-skill/workbench/web/src/lib/graph-selection.ts +96 -0
- package/skills/llm-wiki-skill/workbench/web/src/lib/graph-summary-actions.ts +184 -0
- package/skills/llm-wiki-skill/workbench/web/src/lib/legacy-tool-status.ts +113 -0
- package/skills/llm-wiki-skill/workbench/web/src/lib/model-roles.ts +20 -0
- package/skills/llm-wiki-skill/workbench/web/src/lib/search-filter.ts +104 -0
- package/skills/llm-wiki-skill/workbench/web/src/lib/sse.ts +72 -0
- package/skills/llm-wiki-skill/workbench/web/src/lib/tool-status-format.ts +170 -0
- package/skills/llm-wiki-skill/workbench/web/src/lib/tool-status-model.ts +392 -0
- package/skills/llm-wiki-skill/workbench/web/src/lib/utils.ts +10 -0
- package/skills/llm-wiki-skill/workbench/web/src/lib/view-status.ts +16 -0
- package/skills/llm-wiki-skill/workbench/web/src/lib/wiki-links.ts +32 -0
- package/skills/llm-wiki-skill/workbench/web/src/main.tsx +10 -0
- package/skills/llm-wiki-skill/workbench/web/test/api.test.ts +61 -0
- package/skills/llm-wiki-skill/workbench/web/test/app-shell-drawer-layout.test.tsx +48 -0
- package/skills/llm-wiki-skill/workbench/web/test/appearance-panel.test.tsx +121 -0
- package/skills/llm-wiki-skill/workbench/web/test/appearance.test.ts +262 -0
- package/skills/llm-wiki-skill/workbench/web/test/chat-host-source.test.tsx +22 -0
- package/skills/llm-wiki-skill/workbench/web/test/chat-panel-auto-scroll.test.tsx +311 -0
- package/skills/llm-wiki-skill/workbench/web/test/chat-panel-bubbles.test.tsx +48 -0
- package/skills/llm-wiki-skill/workbench/web/test/chat-panel-composer.test.tsx +86 -0
- package/skills/llm-wiki-skill/workbench/web/test/chat-panel-tool-status.test.tsx +26 -0
- package/skills/llm-wiki-skill/workbench/web/test/export-batch-paper.test.tsx +101 -0
- package/skills/llm-wiki-skill/workbench/web/test/graph-drawer-state.test.ts +257 -0
- package/skills/llm-wiki-skill/workbench/web/test/graph-panel-paper.test.tsx +200 -0
- package/skills/llm-wiki-skill/workbench/web/test/graph-reader-request.test.ts +32 -0
- package/skills/llm-wiki-skill/workbench/web/test/graph-reader.test.ts +62 -0
- package/skills/llm-wiki-skill/workbench/web/test/graph-selection-drawer.test.ts +56 -0
- package/skills/llm-wiki-skill/workbench/web/test/graph-selection.test.ts +83 -0
- package/skills/llm-wiki-skill/workbench/web/test/graph-summary-actions.test.ts +188 -0
- package/skills/llm-wiki-skill/workbench/web/test/main-view-tabs.test.tsx +34 -0
- package/skills/llm-wiki-skill/workbench/web/test/markdown-view.test.tsx +34 -0
- package/skills/llm-wiki-skill/workbench/web/test/popup-menus.test.tsx +85 -0
- package/skills/llm-wiki-skill/workbench/web/test/render.test.tsx +30 -0
- package/skills/llm-wiki-skill/workbench/web/test/render.tsx +38 -0
- package/skills/llm-wiki-skill/workbench/web/test/right-drawer-graph-summary.test.tsx +287 -0
- package/skills/llm-wiki-skill/workbench/web/test/right-drawer-interactions.test.tsx +182 -0
- package/skills/llm-wiki-skill/workbench/web/test/search-filter.test.ts +65 -0
- package/skills/llm-wiki-skill/workbench/web/test/search-panel.test.tsx +155 -0
- package/skills/llm-wiki-skill/workbench/web/test/setup-dom.ts +66 -0
- package/skills/llm-wiki-skill/workbench/web/test/sidebar-v2-source.test.ts +17 -0
- package/skills/llm-wiki-skill/workbench/web/test/sidebar.test.tsx +110 -0
- package/skills/llm-wiki-skill/workbench/web/test/tool-history-summary.test.tsx +150 -0
- package/skills/llm-wiki-skill/workbench/web/test/tool-status-format.test.ts +107 -0
- package/skills/llm-wiki-skill/workbench/web/test/tool-status-model.test.ts +324 -0
- package/skills/llm-wiki-skill/workbench/web/test/tool-status-runway.test.ts +159 -0
- package/skills/llm-wiki-skill/workbench/web/test/topbar.test.tsx +248 -0
- package/skills/llm-wiki-skill/workbench/web/test/visual/paper-ui.ts +898 -0
- package/skills/llm-wiki-skill/workbench/web/test/wiki-links.test.ts +23 -0
- package/skills/llm-wiki-skill/workbench/web/test-results/paper-ui/actual/.gitkeep +1 -0
- package/skills/llm-wiki-skill/workbench/web/test-results/paper-ui/baseline/.gitkeep +1 -0
- package/skills/llm-wiki-skill/workbench/web/tsconfig.app.json +30 -0
- package/skills/llm-wiki-skill/workbench/web/tsconfig.json +7 -0
- package/skills/llm-wiki-skill/workbench/web/tsconfig.node.json +24 -0
- package/skills/llm-wiki-skill/workbench/web/vite.config.ts +28 -0
|
@@ -0,0 +1,730 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const { chromium } = require("playwright");
|
|
8
|
+
|
|
9
|
+
const workbenchUrl = process.env.GRAPH_WORKBENCH_URL || "";
|
|
10
|
+
const artifactDir = process.env.GRAPH_WORKBENCH_ARTIFACT_DIR || "";
|
|
11
|
+
const executablePath = process.env.GRAPH_WORKBENCH_CHROME_EXECUTABLE || "";
|
|
12
|
+
const GLOBAL_NODE_IDS = ["A", "B", "C", "D", "E", "F", "G"];
|
|
13
|
+
const T1_NODE_IDS = ["A", "B", "D", "E", "F", "G"];
|
|
14
|
+
|
|
15
|
+
assert.notEqual(workbenchUrl, "", "GRAPH_WORKBENCH_URL must point at the workbench dev server");
|
|
16
|
+
|
|
17
|
+
const browser = await chromium.launch(executablePath ? { executablePath } : {});
|
|
18
|
+
try {
|
|
19
|
+
const desktop = await runPhaseOneChecks(browser, { width: 1440, height: 960 }, "dark");
|
|
20
|
+
const narrow = await runNarrowChecks(browser);
|
|
21
|
+
const evidence = { desktop, narrow };
|
|
22
|
+
if (artifactDir) {
|
|
23
|
+
await fs.mkdir(artifactDir, { recursive: true });
|
|
24
|
+
await fs.writeFile(path.join(artifactDir, "phase-1-graph-route-regression.json"), `${JSON.stringify(evidence, null, 2)}\n`);
|
|
25
|
+
}
|
|
26
|
+
console.log(JSON.stringify(evidence, null, 2));
|
|
27
|
+
} finally {
|
|
28
|
+
await closeBrowserForRegression(browser);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function runPhaseOneChecks(browser, viewport, theme) {
|
|
32
|
+
const page = await openWorkbenchGraphPage(browser, viewport, theme);
|
|
33
|
+
const evidence = {
|
|
34
|
+
viewport: `${viewport.width}x${viewport.height}`,
|
|
35
|
+
initial: await sigmaGlobalSnapshot(page),
|
|
36
|
+
communitySummary: null,
|
|
37
|
+
focusedCommunity: null,
|
|
38
|
+
returnedGlobal: null,
|
|
39
|
+
statePreservation: null,
|
|
40
|
+
labelClick: null,
|
|
41
|
+
routeCycles: null
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
assertSigmaGlobal(evidence.initial, "initial graph view");
|
|
45
|
+
assert.equal(evidence.initial.oldDomGlobalNodeCount, 0, "initial Sigma global should not render old DOM global nodes");
|
|
46
|
+
assert.equal(evidence.initial.communityButtonCount, 0, "initial Sigma global should not render circular community buttons");
|
|
47
|
+
assert.ok(evidence.initial.communityRegionCount >= 1, "initial Sigma global should keep passive community regions");
|
|
48
|
+
assert.ok(evidence.initial.communityLabelCount >= 1 && evidence.initial.communityLabelCount <= 8, "initial Sigma global should show 1-8 passive community labels");
|
|
49
|
+
assert.ok(evidence.initial.edgeCount >= 1, "initial Sigma global should render relationship skeleton");
|
|
50
|
+
|
|
51
|
+
evidence.communitySummary = await openCommunitySummaryFromRegion(page, "t1");
|
|
52
|
+
assert.equal(evidence.communitySummary.route.renderer, "sigma-global", "community region selection should stay in Sigma global");
|
|
53
|
+
assert.equal(evidence.communitySummary.drawerTestId, "graph-community-summary", "community region selection should open community summary");
|
|
54
|
+
|
|
55
|
+
evidence.labelClick = await clickPassiveCommunityLabel(page);
|
|
56
|
+
assert.equal(evidence.labelClick.drawerTestId, "graph-community-summary", "passive community label area should delegate to map hit handling");
|
|
57
|
+
assert.equal(evidence.labelClick.route.renderer, "sigma-global", "passive label click should not enter a DOM control route");
|
|
58
|
+
|
|
59
|
+
await page.locator('[data-testid="graph-community-summary"] button', { hasText: "进入社区" }).click();
|
|
60
|
+
await waitForDomCommunity(page, "t1");
|
|
61
|
+
evidence.focusedCommunity = await domCommunitySnapshot(page);
|
|
62
|
+
assert.deepEqual(evidence.focusedCommunity.visibleNodes, T1_NODE_IDS, "enter community should use DOM/SVG community reading");
|
|
63
|
+
assert.equal(evidence.focusedCommunity.sigmaRendererCount, 0, "community reading route should not keep Sigma global mounted");
|
|
64
|
+
|
|
65
|
+
await clickReturnGlobal(page);
|
|
66
|
+
await waitForSigmaGlobal(page);
|
|
67
|
+
evidence.returnedGlobal = await sigmaGlobalSnapshot(page);
|
|
68
|
+
assertSigmaGlobal(evidence.returnedGlobal, "return global");
|
|
69
|
+
assert.equal(evidence.returnedGlobal.oldDomGlobalNodeCount, 0, "return global should not show old DOM full graph");
|
|
70
|
+
assert.equal(evidence.returnedGlobal.communityButtonCount, 0, "return global should not reintroduce circular community controls");
|
|
71
|
+
|
|
72
|
+
evidence.statePreservation = await runStatePreservationCheck(page);
|
|
73
|
+
evidence.routeCycles = await runRouteCycleAccumulationCheck(page, "t1", 3);
|
|
74
|
+
|
|
75
|
+
if (artifactDir) {
|
|
76
|
+
await page.screenshot({ path: path.join(artifactDir, "phase-1-graph-route-desktop.png"), fullPage: true });
|
|
77
|
+
}
|
|
78
|
+
await page.close();
|
|
79
|
+
return evidence;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function runNarrowChecks(browser) {
|
|
83
|
+
const page = await openWorkbenchGraphPage(browser, { width: 390, height: 844 }, "light");
|
|
84
|
+
const initial = await sigmaGlobalSnapshot(page);
|
|
85
|
+
assertSigmaGlobal(initial, "narrow initial graph view");
|
|
86
|
+
assert.ok(initial.communityLabelCount >= 1 && initial.communityLabelCount <= 8, "narrow Sigma global should keep passive map labels");
|
|
87
|
+
if (artifactDir) {
|
|
88
|
+
await page.screenshot({ path: path.join(artifactDir, "phase-1-graph-route-narrow.png"), fullPage: true });
|
|
89
|
+
}
|
|
90
|
+
await page.close();
|
|
91
|
+
return { viewport: "390x844", initial };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function openWorkbenchGraphPage(browser, viewport, theme) {
|
|
95
|
+
const page = await browser.newPage({ viewport });
|
|
96
|
+
await page.addInitScript(({ theme }) => {
|
|
97
|
+
window.localStorage.setItem("llm-wiki-agent-main-view", "graph");
|
|
98
|
+
window.localStorage.setItem("llm-wiki-agent-theme", theme);
|
|
99
|
+
}, { theme });
|
|
100
|
+
await page.goto(workbenchUrl);
|
|
101
|
+
await page.waitForSelector(".app-shell");
|
|
102
|
+
const kbButton = page.getByRole("button", { name: /Phase 6 Workbench Test|phase-6-workbench/ });
|
|
103
|
+
if (await kbButton.count() && await kbButton.first().isVisible()) await kbButton.first().click();
|
|
104
|
+
const graphButton = page.getByRole("button", { name: /图谱/ });
|
|
105
|
+
if (await graphButton.count() && await graphButton.first().isVisible() && await graphButton.first().isEnabled()) {
|
|
106
|
+
await graphButton.first().click();
|
|
107
|
+
}
|
|
108
|
+
await page.waitForSelector(".graph-host");
|
|
109
|
+
await waitForSigmaGlobal(page);
|
|
110
|
+
const expectedGraphTheme = theme === "dark" ? "mo-ye" : "shan-shui";
|
|
111
|
+
await page.waitForFunction((expectedGraphTheme) => {
|
|
112
|
+
return document.querySelector(".graph-screen")?.dataset.graphTheme === expectedGraphTheme
|
|
113
|
+
&& document.querySelector(".sigma-global-renderer")?.dataset.theme === expectedGraphTheme;
|
|
114
|
+
}, expectedGraphTheme);
|
|
115
|
+
return page;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function waitForSigmaGlobal(page) {
|
|
119
|
+
await page.waitForSelector(".sigma-global-route[data-route='sigma-global']");
|
|
120
|
+
await page.waitForSelector(".sigma-global-renderer[data-renderer='sigma-global']");
|
|
121
|
+
await page.waitForSelector(".sigma-global-renderer canvas");
|
|
122
|
+
await page.waitForSelector(".sigma-global-community-region");
|
|
123
|
+
await page.waitForSelector(".sigma-global-community-label");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function waitForDomCommunity(page, communityId) {
|
|
127
|
+
await page.waitForSelector("[data-llm-wiki-graph-root='true']");
|
|
128
|
+
await page.waitForFunction((communityId) => {
|
|
129
|
+
return document.querySelector(".graph-host")?.dataset.llmWikiGraphFocus === `community:${communityId}`
|
|
130
|
+
&& document.querySelectorAll(".node").length > 0
|
|
131
|
+
&& !document.querySelector(".sigma-global-renderer");
|
|
132
|
+
}, communityId);
|
|
133
|
+
await waitForVisibleDomNodeIds(page, T1_NODE_IDS);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function sigmaGlobalSnapshot(page) {
|
|
137
|
+
return page.evaluate(() => {
|
|
138
|
+
const sigma = document.querySelector(".sigma-global-renderer");
|
|
139
|
+
const canvas = sigma?.querySelector("canvas");
|
|
140
|
+
const graph = document.querySelector(".sigma-global-route");
|
|
141
|
+
const labels = [...document.querySelectorAll(".sigma-global-community-label")];
|
|
142
|
+
const regions = [...document.querySelectorAll(".sigma-global-community-region")];
|
|
143
|
+
const nodeIds = [...document.querySelectorAll(".sigma-global-node-hit-target")]
|
|
144
|
+
.map((node) => node.getAttribute("data-node-id") || "")
|
|
145
|
+
.filter(Boolean)
|
|
146
|
+
.sort();
|
|
147
|
+
const regionIds = regions.map((region) => region.getAttribute("data-community-id") || "").filter(Boolean).sort();
|
|
148
|
+
const labelIds = labels.map((label) => label.getAttribute("data-community-id") || "").filter(Boolean).sort();
|
|
149
|
+
return {
|
|
150
|
+
route: graph?.getAttribute("data-route") || "",
|
|
151
|
+
renderer: sigma?.getAttribute("data-renderer") || "",
|
|
152
|
+
canvasCount: sigma?.querySelectorAll("canvas").length || 0,
|
|
153
|
+
canvasBox: canvas ? (() => {
|
|
154
|
+
const rect = canvas.getBoundingClientRect();
|
|
155
|
+
const round = (value) => Math.round(value * 1000) / 1000;
|
|
156
|
+
return {
|
|
157
|
+
left: round(rect.left),
|
|
158
|
+
top: round(rect.top),
|
|
159
|
+
width: round(rect.width),
|
|
160
|
+
height: round(rect.height)
|
|
161
|
+
};
|
|
162
|
+
})() : null,
|
|
163
|
+
nodeHitTargetCount: document.querySelectorAll(".sigma-global-node-hit-target").length,
|
|
164
|
+
nodeHitTargetIds: nodeIds,
|
|
165
|
+
sigmaRouteCount: document.querySelectorAll(".sigma-global-route[data-route='sigma-global']").length,
|
|
166
|
+
sigmaRendererCount: document.querySelectorAll(".sigma-global-renderer[data-renderer='sigma-global']").length,
|
|
167
|
+
edgeCount: document.querySelectorAll("canvas").length,
|
|
168
|
+
communityRegionCount: regions.length,
|
|
169
|
+
communityRegionIds: regionIds,
|
|
170
|
+
communityLabelCount: labels.length,
|
|
171
|
+
communityLabelIds: labelIds,
|
|
172
|
+
communityButtonCount: document.querySelectorAll(".sigma-global-community-wash, button.sigma-global-community-label, [role='button'].sigma-global-community-label").length,
|
|
173
|
+
aggregationButtonCount: document.querySelectorAll(".sigma-global-aggregation-container").length,
|
|
174
|
+
oldDomGlobalNodeCount: document.querySelectorAll(".node").length,
|
|
175
|
+
labels: labels.map((label) => ({
|
|
176
|
+
communityId: label.getAttribute("data-community-id") || "",
|
|
177
|
+
ariaHidden: label.getAttribute("aria-hidden") || "",
|
|
178
|
+
tabIndex: label instanceof HTMLElement ? label.tabIndex : null,
|
|
179
|
+
pointerEvents: label instanceof HTMLElement ? getComputedStyle(label).pointerEvents : "",
|
|
180
|
+
text: label.textContent || ""
|
|
181
|
+
}))
|
|
182
|
+
};
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async function domCommunitySnapshot(page) {
|
|
187
|
+
return page.evaluate(() => ({
|
|
188
|
+
focus: document.querySelector(".graph-host")?.dataset.llmWikiGraphFocus || "",
|
|
189
|
+
sigmaRendererCount: document.querySelectorAll(".sigma-global-renderer").length,
|
|
190
|
+
visibleNodes: [...document.querySelectorAll(".node")]
|
|
191
|
+
.filter((node) => node.getAttribute("data-filter-state") !== "hidden")
|
|
192
|
+
.map((node) => node.getAttribute("data-id"))
|
|
193
|
+
.filter(Boolean)
|
|
194
|
+
.sort(),
|
|
195
|
+
toolbarReturnCount: [...document.querySelectorAll("button")].filter((button) => button.textContent?.includes("回全图")).length
|
|
196
|
+
}));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function runRouteCycleAccumulationCheck(page, communityId, cycles) {
|
|
200
|
+
await closeDrawerIfOpen(page);
|
|
201
|
+
await waitForSigmaGlobal(page);
|
|
202
|
+
await openSearch(page);
|
|
203
|
+
await setGraphSearchQuery(page, "");
|
|
204
|
+
await page.waitForFunction(() => document.querySelectorAll(".sigma-global-node-hit-target").length > 1);
|
|
205
|
+
const baseline = await sigmaGlobalSnapshot(page);
|
|
206
|
+
const snapshots = [];
|
|
207
|
+
|
|
208
|
+
for (let index = 0; index < cycles; index += 1) {
|
|
209
|
+
const summary = await openCommunitySummaryFromRegion(page, communityId);
|
|
210
|
+
assert.equal(summary.route.sigmaRouteCount, 1, `cycle ${index + 1}: should have one Sigma route before entering community`);
|
|
211
|
+
await page.locator('[data-testid="graph-community-summary"] button', { hasText: "进入社区" }).click();
|
|
212
|
+
await waitForDomCommunity(page, communityId);
|
|
213
|
+
const focused = await domCommunitySnapshot(page);
|
|
214
|
+
assert.equal(focused.sigmaRendererCount, 0, `cycle ${index + 1}: community route should not retain Sigma renderer`);
|
|
215
|
+
assert.equal(focused.toolbarReturnCount, 1, `cycle ${index + 1}: community route should expose one return-global control`);
|
|
216
|
+
|
|
217
|
+
await clickReturnGlobal(page);
|
|
218
|
+
await waitForSigmaGlobal(page);
|
|
219
|
+
const returned = await sigmaGlobalSnapshot(page);
|
|
220
|
+
assertSigmaGlobal(returned, `cycle ${index + 1} return global`);
|
|
221
|
+
assert.equal(returned.sigmaRouteCount, 1, `cycle ${index + 1}: should have one Sigma route after return`);
|
|
222
|
+
assert.equal(returned.sigmaRendererCount, 1, `cycle ${index + 1}: should have one Sigma renderer after return`);
|
|
223
|
+
assert.equal(returned.oldDomGlobalNodeCount, 0, `cycle ${index + 1}: should not accumulate old DOM global nodes`);
|
|
224
|
+
assert.equal(returned.communityButtonCount, 0, `cycle ${index + 1}: should not reintroduce circular community controls`);
|
|
225
|
+
assert.equal(returned.aggregationButtonCount, 0, `cycle ${index + 1}: should not show aggregation controls`);
|
|
226
|
+
assert.deepEqual(returned.nodeHitTargetIds, baseline.nodeHitTargetIds, `cycle ${index + 1}: node hit targets should match the baseline set`);
|
|
227
|
+
assert.deepEqual(returned.communityRegionIds, baseline.communityRegionIds, `cycle ${index + 1}: community regions should match the baseline set`);
|
|
228
|
+
assert.deepEqual(returned.communityLabelIds, baseline.communityLabelIds, `cycle ${index + 1}: community labels should match the baseline set`);
|
|
229
|
+
snapshots.push({ summary: summary.route, focused, returned });
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return { cycles, baseline, snapshots };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function openCommunitySummaryFromRegion(page, communityId) {
|
|
236
|
+
await closeDrawerIfOpen(page);
|
|
237
|
+
const point = await clickCommunityRegion(page, communityId);
|
|
238
|
+
try {
|
|
239
|
+
await page.waitForSelector('[data-testid="graph-community-summary"]');
|
|
240
|
+
} catch (err) {
|
|
241
|
+
const diagnostics = await communityClickDiagnostics(page, point, communityId);
|
|
242
|
+
throw new assert.AssertionError({
|
|
243
|
+
message: `community region click should open community summary: ${JSON.stringify(diagnostics)}`,
|
|
244
|
+
actual: err,
|
|
245
|
+
expected: "graph-community-summary",
|
|
246
|
+
operator: "strictEqual"
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
route: await sigmaGlobalSnapshot(page),
|
|
251
|
+
drawerTestId: await drawerTestId(page)
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async function clickPassiveCommunityLabel(page) {
|
|
256
|
+
const point = await page.locator(".sigma-global-community-label").first().evaluate((label) => {
|
|
257
|
+
const rect = label.getBoundingClientRect();
|
|
258
|
+
return { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 };
|
|
259
|
+
});
|
|
260
|
+
await page.mouse.click(point.x, point.y);
|
|
261
|
+
await page.waitForSelector('[data-testid="graph-community-summary"]');
|
|
262
|
+
return {
|
|
263
|
+
point: { x: round(point.x), y: round(point.y) },
|
|
264
|
+
route: await sigmaGlobalSnapshot(page),
|
|
265
|
+
drawerTestId: await drawerTestId(page)
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function clickCommunityRegion(page, communityId) {
|
|
270
|
+
const point = await findCommunityRegionPoint(page, communityId);
|
|
271
|
+
await page.evaluate(() => {
|
|
272
|
+
window.__llmWikiRegionClickProbe = [];
|
|
273
|
+
for (const region of document.querySelectorAll(".sigma-global-community-region")) {
|
|
274
|
+
region.addEventListener("click", (event) => {
|
|
275
|
+
window.__llmWikiRegionClickProbe.push({
|
|
276
|
+
communityId: region.getAttribute("data-community-id") || "",
|
|
277
|
+
clientX: event.clientX,
|
|
278
|
+
clientY: event.clientY
|
|
279
|
+
});
|
|
280
|
+
}, { once: true });
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
await page.mouse.click(point.x, point.y);
|
|
284
|
+
return point;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async function findCommunityRegionPoint(page, communityId) {
|
|
288
|
+
return page.evaluate((communityId) => {
|
|
289
|
+
const region = document.querySelector(`.sigma-global-community-region[data-community-id="${CSS.escape(communityId)}"]`)
|
|
290
|
+
|| document.querySelector(".sigma-global-community-region");
|
|
291
|
+
if (!region) throw new Error(`Missing Sigma community region ${communityId}`);
|
|
292
|
+
const rect = region.getBoundingClientRect();
|
|
293
|
+
const candidates = [
|
|
294
|
+
[0.5, 0.5],
|
|
295
|
+
[0.5, 0.32],
|
|
296
|
+
[0.32, 0.5],
|
|
297
|
+
[0.68, 0.5],
|
|
298
|
+
[0.5, 0.68]
|
|
299
|
+
];
|
|
300
|
+
for (const [rx, ry] of candidates) {
|
|
301
|
+
const x = rect.left + rect.width * rx;
|
|
302
|
+
const y = rect.top + rect.height * ry;
|
|
303
|
+
const hit = document.elementFromPoint(x, y);
|
|
304
|
+
if (!hit?.closest?.(".sigma-global-node-hit-target")) return { x, y };
|
|
305
|
+
}
|
|
306
|
+
return { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 };
|
|
307
|
+
}, communityId);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async function communityClickDiagnostics(page, point, communityId) {
|
|
311
|
+
return page.evaluate(({ point, communityId }) => {
|
|
312
|
+
const hit = document.elementFromPoint(point.x, point.y);
|
|
313
|
+
const regions = [...document.querySelectorAll(".sigma-global-community-region")].map((region) => {
|
|
314
|
+
const rect = region.getBoundingClientRect();
|
|
315
|
+
return {
|
|
316
|
+
communityId: region.getAttribute("data-community-id") || "",
|
|
317
|
+
selected: region.getAttribute("data-selected") || "",
|
|
318
|
+
pointerEvents: region instanceof HTMLElement ? getComputedStyle(region).pointerEvents : "",
|
|
319
|
+
inlinePointerEvents: region instanceof HTMLElement ? region.style.pointerEvents : "",
|
|
320
|
+
rect: {
|
|
321
|
+
left: Math.round(rect.left * 1000) / 1000,
|
|
322
|
+
top: Math.round(rect.top * 1000) / 1000,
|
|
323
|
+
width: Math.round(rect.width * 1000) / 1000,
|
|
324
|
+
height: Math.round(rect.height * 1000) / 1000
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
});
|
|
328
|
+
return {
|
|
329
|
+
expectedCommunityId: communityId,
|
|
330
|
+
point,
|
|
331
|
+
hitTag: hit?.tagName || "",
|
|
332
|
+
hitClass: hit instanceof Element ? hit.className : "",
|
|
333
|
+
hitCommunityId: hit instanceof Element ? hit.closest(".sigma-global-community-region")?.getAttribute("data-community-id") || "" : "",
|
|
334
|
+
probe: window.__llmWikiRegionClickProbe || [],
|
|
335
|
+
drawerTestId: document.querySelector(".drawer-panel-open [data-testid]")?.getAttribute("data-testid") || "",
|
|
336
|
+
route: document.querySelector(".sigma-global-route")?.getAttribute("data-route") || "",
|
|
337
|
+
renderer: document.querySelector(".sigma-global-renderer")?.getAttribute("data-renderer") || "",
|
|
338
|
+
regions
|
|
339
|
+
};
|
|
340
|
+
}, { point, communityId });
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
async function runStatePreservationCheck(page) {
|
|
344
|
+
await closeDrawerIfOpen(page);
|
|
345
|
+
await waitForSigmaGlobal(page);
|
|
346
|
+
|
|
347
|
+
await openSearch(page);
|
|
348
|
+
await setGraphSearchQuery(page, "节点A");
|
|
349
|
+
try {
|
|
350
|
+
await page.waitForFunction(() => document.querySelector(".graph-search-status")?.textContent?.includes("1 个结果"));
|
|
351
|
+
} catch (err) {
|
|
352
|
+
const diagnostics = await searchDiagnostics(page);
|
|
353
|
+
throw new assert.AssertionError({
|
|
354
|
+
message: `graph search should find node A: ${JSON.stringify(diagnostics)}`,
|
|
355
|
+
actual: err,
|
|
356
|
+
expected: "1 个结果",
|
|
357
|
+
operator: "strictEqual"
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
await page.waitForSelector(".sigma-global-node-hit-target[data-node-id='A'][data-search-hit='true']");
|
|
361
|
+
await clickNodeHitTarget(page, "A");
|
|
362
|
+
await page.waitForSelector('[data-testid="graph-node-summary"]');
|
|
363
|
+
const beforeDrag = await sigmaStateSnapshot(page);
|
|
364
|
+
const drag = await dragSigmaGlobalNode(page, "A", { dx: 56, dy: 32 });
|
|
365
|
+
try {
|
|
366
|
+
await page.waitForFunction(() => document.querySelector(".sigma-global-node-hit-target[data-node-id='A']")?.getAttribute("data-pinned") === "true");
|
|
367
|
+
} catch (err) {
|
|
368
|
+
const diagnostics = await dragDiagnostics(page, "A", drag);
|
|
369
|
+
throw new assert.AssertionError({
|
|
370
|
+
message: `dragging Sigma global node should pin node A: ${JSON.stringify(diagnostics)}`,
|
|
371
|
+
actual: err,
|
|
372
|
+
expected: "data-pinned=true",
|
|
373
|
+
operator: "strictEqual"
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
const afterDrag = await sigmaStateSnapshot(page);
|
|
377
|
+
assertPointShifted(afterDrag.nodeABox, beforeDrag.nodeABox, "drag should move node A before it is treated as fixed");
|
|
378
|
+
await waitForPersistedGraphPin(page, "wiki/entities/A.md");
|
|
379
|
+
|
|
380
|
+
await page.locator('[data-testid="graph-node-summary"] button', { hasText: "打开详情" }).click();
|
|
381
|
+
await page.waitForSelector(".graph-reader-drawer");
|
|
382
|
+
await waitForDomCommunity(page, "t1");
|
|
383
|
+
const focused = await domCommunitySnapshot(page);
|
|
384
|
+
|
|
385
|
+
await clickReturnGlobal(page);
|
|
386
|
+
await waitForSigmaGlobal(page);
|
|
387
|
+
await page.waitForSelector('[data-testid="graph-node-summary"]');
|
|
388
|
+
await page.waitForFunction(() => document.querySelector(".graph-search-input")?.value === "节点A");
|
|
389
|
+
await page.waitForFunction(() => document.querySelector(".sigma-global-node-hit-target[data-node-id='A']")?.getAttribute("data-pinned") === "true");
|
|
390
|
+
await page.waitForFunction(() => document.querySelector(".sigma-global-node-hit-target[data-node-id='A']")?.getAttribute("data-selected") === "true");
|
|
391
|
+
const returned = await sigmaStateSnapshot(page);
|
|
392
|
+
|
|
393
|
+
assert.equal(returned.drawerTestId, "graph-node-summary", "node summary should survive community return");
|
|
394
|
+
assert.equal(returned.searchQuery, "节点A", "search query should survive community return");
|
|
395
|
+
assert.equal(returned.nodeASelected, "true", "selected node should survive community return");
|
|
396
|
+
assert.equal(returned.nodeAPinned, "true", "fixed node should survive community return");
|
|
397
|
+
assertPointStable(returned.nodeABox, afterDrag.nodeABox, "dragged fixed node should remain at the released global position after community return");
|
|
398
|
+
assert.equal(returned.oldDomGlobalNodeCount, 0, "state-preserving return should still use Sigma, not old DOM global");
|
|
399
|
+
|
|
400
|
+
await page.reload();
|
|
401
|
+
await waitForSigmaGlobal(page);
|
|
402
|
+
const reloadedGlobal = await sigmaGlobalSnapshot(page);
|
|
403
|
+
assertSigmaGlobal(reloadedGlobal, "reloaded graph view");
|
|
404
|
+
assert.equal(reloadedGlobal.communityButtonCount, 0, "reload should not reintroduce circular community controls");
|
|
405
|
+
assert.equal(reloadedGlobal.aggregationButtonCount, 0, "reload should not show aggregation UI");
|
|
406
|
+
assert.equal(reloadedGlobal.oldDomGlobalNodeCount, 0, "reload should stay on Sigma, not old DOM global");
|
|
407
|
+
await openSearch(page);
|
|
408
|
+
await setGraphSearchQuery(page, "节点A");
|
|
409
|
+
try {
|
|
410
|
+
await page.waitForFunction(() => document.querySelector(".sigma-global-node-hit-target[data-node-id='A']")?.getAttribute("data-pinned") === "true");
|
|
411
|
+
} catch (err) {
|
|
412
|
+
const diagnostics = await reloadPinDiagnostics(page, "A");
|
|
413
|
+
throw new assert.AssertionError({
|
|
414
|
+
message: `fixed node should survive reload: ${JSON.stringify(diagnostics)}`,
|
|
415
|
+
actual: err,
|
|
416
|
+
expected: "data-pinned=true",
|
|
417
|
+
operator: "strictEqual"
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
const reloaded = await sigmaStateSnapshot(page);
|
|
421
|
+
assert.equal(reloaded.nodeAPinned, "true", "fixed node should survive a page reload");
|
|
422
|
+
assertPointShifted(reloaded.nodeABox, beforeDrag.nodeABox, "reloaded fixed node should not fall back to its pre-drag position");
|
|
423
|
+
|
|
424
|
+
return { drag, beforeDrag, afterDrag, focused, returned, reloadedGlobal, reloaded };
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
async function sigmaStateSnapshot(page) {
|
|
428
|
+
return page.evaluate(() => {
|
|
429
|
+
const nodeA = document.querySelector(".sigma-global-node-hit-target[data-node-id='A']");
|
|
430
|
+
const nodeABox = nodeA ? (() => {
|
|
431
|
+
const rect = nodeA.getBoundingClientRect();
|
|
432
|
+
return {
|
|
433
|
+
left: Math.round(rect.left * 1000) / 1000,
|
|
434
|
+
top: Math.round(rect.top * 1000) / 1000,
|
|
435
|
+
width: Math.round(rect.width * 1000) / 1000,
|
|
436
|
+
height: Math.round(rect.height * 1000) / 1000,
|
|
437
|
+
centerX: Math.round((rect.left + rect.width / 2) * 1000) / 1000,
|
|
438
|
+
centerY: Math.round((rect.top + rect.height / 2) * 1000) / 1000
|
|
439
|
+
};
|
|
440
|
+
})() : null;
|
|
441
|
+
return {
|
|
442
|
+
route: document.querySelector(".sigma-global-route")?.getAttribute("data-route") || "",
|
|
443
|
+
renderer: document.querySelector(".sigma-global-renderer")?.getAttribute("data-renderer") || "",
|
|
444
|
+
drawerTestId: document.querySelector(".drawer-panel-open [data-testid]")?.getAttribute("data-testid") || "",
|
|
445
|
+
searchQuery: document.querySelector(".graph-search-input")?.value || "",
|
|
446
|
+
nodeASelected: nodeA?.getAttribute("data-selected") || "",
|
|
447
|
+
nodeAPinned: nodeA?.getAttribute("data-pinned") || "",
|
|
448
|
+
nodeABox,
|
|
449
|
+
oldDomGlobalNodeCount: document.querySelectorAll(".node").length
|
|
450
|
+
};
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
async function searchDiagnostics(page) {
|
|
455
|
+
return page.evaluate(() => ({
|
|
456
|
+
searchState: document.querySelector(".graph-search")?.getAttribute("data-state") || "",
|
|
457
|
+
searchOpen: document.querySelector(".sigma-global-route")?.getAttribute("data-search-open") || "",
|
|
458
|
+
inputValue: document.querySelector(".graph-search-input")?.value || "",
|
|
459
|
+
statusText: document.querySelector(".graph-search-status")?.textContent || "",
|
|
460
|
+
nodeTargets: [...document.querySelectorAll(".sigma-global-node-hit-target")].map((target) => ({
|
|
461
|
+
nodeId: target.getAttribute("data-node-id") || "",
|
|
462
|
+
searchHit: target.getAttribute("data-search-hit") || "",
|
|
463
|
+
selected: target.getAttribute("data-selected") || "",
|
|
464
|
+
pinned: target.getAttribute("data-pinned") || ""
|
|
465
|
+
}))
|
|
466
|
+
}));
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
async function clickNodeHitTarget(page, nodeId) {
|
|
470
|
+
const locator = page.locator(`.sigma-global-node-hit-target[data-node-id="${cssString(nodeId)}"]`);
|
|
471
|
+
await locator.waitFor();
|
|
472
|
+
await locator.click({ force: true });
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
async function dragSigmaGlobalNode(page, nodeId, delta) {
|
|
476
|
+
const locator = page.locator(`.sigma-global-node-hit-target[data-node-id="${cssString(nodeId)}"]`);
|
|
477
|
+
await locator.waitFor();
|
|
478
|
+
await waitForStableNodeHitTarget(page, nodeId);
|
|
479
|
+
const start = await locator.evaluate((node) => {
|
|
480
|
+
const rect = node.getBoundingClientRect();
|
|
481
|
+
return {
|
|
482
|
+
x: rect.left + rect.width / 2,
|
|
483
|
+
y: rect.top + rect.height / 2
|
|
484
|
+
};
|
|
485
|
+
});
|
|
486
|
+
const end = { x: start.x + delta.dx, y: start.y + delta.dy };
|
|
487
|
+
await page.mouse.move(start.x, start.y);
|
|
488
|
+
await page.mouse.down();
|
|
489
|
+
await page.mouse.move(start.x + delta.dx / 2, start.y + delta.dy / 2, { steps: 6 });
|
|
490
|
+
await page.mouse.move(end.x, end.y, { steps: 6 });
|
|
491
|
+
await page.mouse.up();
|
|
492
|
+
return {
|
|
493
|
+
start: { x: round(start.x), y: round(start.y) },
|
|
494
|
+
end: { x: round(end.x), y: round(end.y) },
|
|
495
|
+
delta
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
async function waitForStableNodeHitTarget(page, nodeId) {
|
|
500
|
+
await page.waitForFunction(async (nodeId) => {
|
|
501
|
+
const target = document.querySelector(`.sigma-global-node-hit-target[data-node-id="${CSS.escape(nodeId)}"]`);
|
|
502
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
503
|
+
const snapshot = () => {
|
|
504
|
+
const rect = target.getBoundingClientRect();
|
|
505
|
+
return {
|
|
506
|
+
centerX: Math.round((rect.left + rect.width / 2) * 1000) / 1000,
|
|
507
|
+
centerY: Math.round((rect.top + rect.height / 2) * 1000) / 1000
|
|
508
|
+
};
|
|
509
|
+
};
|
|
510
|
+
const first = snapshot();
|
|
511
|
+
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
|
|
512
|
+
const second = snapshot();
|
|
513
|
+
const hit = document.elementFromPoint(second.centerX, second.centerY);
|
|
514
|
+
const hitNodeId = hit instanceof Element
|
|
515
|
+
? hit.closest(".sigma-global-node-hit-target")?.getAttribute("data-node-id") || ""
|
|
516
|
+
: "";
|
|
517
|
+
return hitNodeId === nodeId &&
|
|
518
|
+
Math.abs(first.centerX - second.centerX) < 1 &&
|
|
519
|
+
Math.abs(first.centerY - second.centerY) < 1;
|
|
520
|
+
}, nodeId, { timeout: 5000 });
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
async function dragDiagnostics(page, nodeId, drag) {
|
|
524
|
+
return page.evaluate(({ nodeId, drag }) => {
|
|
525
|
+
const roundInPage = (value) => Math.round(value * 1000) / 1000;
|
|
526
|
+
const target = document.querySelector(`.sigma-global-node-hit-target[data-node-id="${CSS.escape(nodeId)}"]`);
|
|
527
|
+
const hit = document.elementFromPoint(drag.start.x, drag.start.y);
|
|
528
|
+
return {
|
|
529
|
+
drag,
|
|
530
|
+
hitTag: hit?.tagName || "",
|
|
531
|
+
hitClass: hit instanceof Element ? hit.className : "",
|
|
532
|
+
hitNodeId: hit instanceof Element ? hit.closest(".sigma-global-node-hit-target")?.getAttribute("data-node-id") || "" : "",
|
|
533
|
+
targetPinned: target?.getAttribute("data-pinned") || "",
|
|
534
|
+
targetSelected: target?.getAttribute("data-selected") || "",
|
|
535
|
+
targetSearchHit: target?.getAttribute("data-search-hit") || "",
|
|
536
|
+
targetBox: target instanceof HTMLElement ? (() => {
|
|
537
|
+
const rect = target.getBoundingClientRect();
|
|
538
|
+
return {
|
|
539
|
+
left: roundInPage(rect.left),
|
|
540
|
+
top: roundInPage(rect.top),
|
|
541
|
+
width: roundInPage(rect.width),
|
|
542
|
+
height: roundInPage(rect.height),
|
|
543
|
+
centerX: roundInPage(rect.left + rect.width / 2),
|
|
544
|
+
centerY: roundInPage(rect.top + rect.height / 2),
|
|
545
|
+
pointerEvents: getComputedStyle(target).pointerEvents,
|
|
546
|
+
touchAction: getComputedStyle(target).touchAction,
|
|
547
|
+
userSelect: getComputedStyle(target).userSelect
|
|
548
|
+
};
|
|
549
|
+
})() : null,
|
|
550
|
+
route: document.querySelector(".sigma-global-route")?.getAttribute("data-route") || "",
|
|
551
|
+
renderer: document.querySelector(".sigma-global-renderer")?.getAttribute("data-renderer") || "",
|
|
552
|
+
draggingNodeId: document.querySelector(".sigma-global-renderer")?.getAttribute("data-dragging-node-id") || ""
|
|
553
|
+
};
|
|
554
|
+
}, { nodeId, drag });
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
async function reloadPinDiagnostics(page, nodeId) {
|
|
558
|
+
return page.evaluate(async (nodeId) => {
|
|
559
|
+
const currentGraphLayoutUrl = async () => {
|
|
560
|
+
const response = await fetch("/api/knowledge-base");
|
|
561
|
+
const payload = await response.json();
|
|
562
|
+
const kbPath = payload?.active?.kb?.path || "";
|
|
563
|
+
return kbPath ? `/api/graph/layout?kb=${encodeURIComponent(kbPath)}` : "/api/graph/layout";
|
|
564
|
+
};
|
|
565
|
+
const target = document.querySelector(`.sigma-global-node-hit-target[data-node-id="${CSS.escape(nodeId)}"]`);
|
|
566
|
+
const layoutUrl = await currentGraphLayoutUrl();
|
|
567
|
+
const layoutResponse = await fetch(layoutUrl);
|
|
568
|
+
const layoutPayload = await layoutResponse.json().catch(() => null);
|
|
569
|
+
return {
|
|
570
|
+
nodeId,
|
|
571
|
+
targetExists: Boolean(target),
|
|
572
|
+
targetPinned: target?.getAttribute("data-pinned") || "",
|
|
573
|
+
targetSelected: target?.getAttribute("data-selected") || "",
|
|
574
|
+
targetSearchHit: target?.getAttribute("data-search-hit") || "",
|
|
575
|
+
searchQuery: document.querySelector(".graph-search-input")?.value || "",
|
|
576
|
+
statusText: document.querySelector(".graph-search-status")?.textContent || "",
|
|
577
|
+
route: document.querySelector(".sigma-global-route")?.getAttribute("data-route") || "",
|
|
578
|
+
renderer: document.querySelector(".sigma-global-renderer")?.getAttribute("data-renderer") || "",
|
|
579
|
+
layoutUrl,
|
|
580
|
+
layoutPins: layoutPayload?.layout?.pins || null,
|
|
581
|
+
allTargets: [...document.querySelectorAll(".sigma-global-node-hit-target")].map((node) => ({
|
|
582
|
+
id: node.getAttribute("data-node-id") || "",
|
|
583
|
+
pinned: node.getAttribute("data-pinned") || "",
|
|
584
|
+
selected: node.getAttribute("data-selected") || "",
|
|
585
|
+
searchHit: node.getAttribute("data-search-hit") || ""
|
|
586
|
+
}))
|
|
587
|
+
};
|
|
588
|
+
}, nodeId);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
async function waitForPersistedGraphPin(page, pinKey) {
|
|
592
|
+
try {
|
|
593
|
+
await page.waitForFunction(async (pinKey) => {
|
|
594
|
+
const currentGraphLayoutUrl = async () => {
|
|
595
|
+
const response = await fetch("/api/knowledge-base");
|
|
596
|
+
const payload = await response.json();
|
|
597
|
+
const kbPath = payload?.active?.kb?.path || "";
|
|
598
|
+
return kbPath ? `/api/graph/layout?kb=${encodeURIComponent(kbPath)}` : "/api/graph/layout";
|
|
599
|
+
};
|
|
600
|
+
const layoutUrl = await currentGraphLayoutUrl();
|
|
601
|
+
const response = await fetch(layoutUrl);
|
|
602
|
+
if (!response.ok) return false;
|
|
603
|
+
const payload = await response.json();
|
|
604
|
+
return Boolean(payload?.layout?.pins?.[pinKey]);
|
|
605
|
+
}, pinKey, { timeout: 5000 });
|
|
606
|
+
} catch (err) {
|
|
607
|
+
const diagnostics = await page.evaluate(async (pinKey) => {
|
|
608
|
+
const currentGraphLayoutUrl = async () => {
|
|
609
|
+
const response = await fetch("/api/knowledge-base");
|
|
610
|
+
const payload = await response.json();
|
|
611
|
+
const kbPath = payload?.active?.kb?.path || "";
|
|
612
|
+
return kbPath ? `/api/graph/layout?kb=${encodeURIComponent(kbPath)}` : "/api/graph/layout";
|
|
613
|
+
};
|
|
614
|
+
const layoutUrl = await currentGraphLayoutUrl();
|
|
615
|
+
const response = await fetch(layoutUrl);
|
|
616
|
+
const text = await response.text();
|
|
617
|
+
return {
|
|
618
|
+
pinKey,
|
|
619
|
+
layoutUrl,
|
|
620
|
+
ok: response.ok,
|
|
621
|
+
status: response.status,
|
|
622
|
+
body: text.slice(0, 500)
|
|
623
|
+
};
|
|
624
|
+
}, pinKey);
|
|
625
|
+
throw new assert.AssertionError({
|
|
626
|
+
message: `graph pin should be persisted before reload: ${JSON.stringify(diagnostics)}`,
|
|
627
|
+
actual: err,
|
|
628
|
+
expected: `layout.pins[${pinKey}]`,
|
|
629
|
+
operator: "strictEqual"
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
async function clickReturnGlobal(page) {
|
|
635
|
+
await page.getByRole("button", { name: "回全图" }).click();
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
async function openSearch(page) {
|
|
639
|
+
await page.locator(".graph-search-input").focus();
|
|
640
|
+
await page.waitForFunction(() => document.querySelector(".graph-search")?.getAttribute("data-state") === "open");
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
async function setGraphSearchQuery(page, query) {
|
|
644
|
+
await page.evaluate((query) => {
|
|
645
|
+
const input = document.querySelector(".graph-search[data-state='open'] .graph-search-input");
|
|
646
|
+
if (!(input instanceof HTMLInputElement)) throw new Error("Graph search input is not open");
|
|
647
|
+
input.value = query;
|
|
648
|
+
input.dispatchEvent(new InputEvent("input", {
|
|
649
|
+
bubbles: true,
|
|
650
|
+
inputType: "insertText",
|
|
651
|
+
data: query
|
|
652
|
+
}));
|
|
653
|
+
}, query);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
async function drawerTestId(page) {
|
|
657
|
+
return page.evaluate(() => document.querySelector(".drawer-panel-open [data-testid]")?.getAttribute("data-testid") || "");
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
async function closeDrawerIfOpen(page) {
|
|
661
|
+
const button = page.locator(".drawer-header button[aria-label='关闭']");
|
|
662
|
+
if (await button.count()) {
|
|
663
|
+
await button.first().click({ force: true });
|
|
664
|
+
await page.waitForSelector(".drawer-panel-open", { state: "detached", timeout: 3000 }).catch(() => undefined);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
async function waitForVisibleDomNodeIds(page, expected) {
|
|
669
|
+
await page.waitForFunction((expected) => {
|
|
670
|
+
const actual = [...document.querySelectorAll(".node")]
|
|
671
|
+
.filter((node) => node.getAttribute("data-filter-state") !== "hidden")
|
|
672
|
+
.map((node) => node.getAttribute("data-id"))
|
|
673
|
+
.filter(Boolean)
|
|
674
|
+
.sort();
|
|
675
|
+
return actual.length === expected.length && actual.every((id, index) => id === expected[index]);
|
|
676
|
+
}, expected, { timeout: 5000 });
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
function assertSigmaGlobal(snapshot, label) {
|
|
680
|
+
assert.equal(snapshot.route, "sigma-global", `${label}: route should be Sigma global`);
|
|
681
|
+
assert.equal(snapshot.renderer, "sigma-global", `${label}: renderer should be Sigma global`);
|
|
682
|
+
assert.ok(snapshot.canvasCount >= 1, `${label}: Sigma canvas should exist`);
|
|
683
|
+
assert.ok(snapshot.canvasBox?.width > 120 && snapshot.canvasBox?.height > 120, `${label}: Sigma canvas should be visible`);
|
|
684
|
+
assert.equal(snapshot.aggregationButtonCount, 0, `${label}: no visible aggregation controls`);
|
|
685
|
+
for (const labelInfo of snapshot.labels) {
|
|
686
|
+
assert.equal(labelInfo.ariaHidden, "true", `${label}: community labels should be aria-hidden`);
|
|
687
|
+
assert.equal(labelInfo.tabIndex, -1, `${label}: community labels should not be tab-focusable`);
|
|
688
|
+
assert.equal(labelInfo.pointerEvents, "none", `${label}: community labels should be passive`);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
function assertPointStable(actual, expected, message) {
|
|
693
|
+
assert.ok(actual, `${message}: missing actual node box`);
|
|
694
|
+
assert.ok(expected, `${message}: missing expected node box`);
|
|
695
|
+
assert.ok(Math.abs(actual.centerX - expected.centerX) <= 3, `${message}: x drifted from ${expected.centerX} to ${actual.centerX}`);
|
|
696
|
+
assert.ok(Math.abs(actual.centerY - expected.centerY) <= 3, `${message}: y drifted from ${expected.centerY} to ${actual.centerY}`);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
function assertPointShifted(actual, expected, message) {
|
|
700
|
+
assert.ok(actual, `${message}: missing actual node box`);
|
|
701
|
+
assert.ok(expected, `${message}: missing expected node box`);
|
|
702
|
+
const dx = Math.abs(actual.centerX - expected.centerX);
|
|
703
|
+
const dy = Math.abs(actual.centerY - expected.centerY);
|
|
704
|
+
assert.ok(dx >= 20 || dy >= 20, `${message}: expected a visible move, got dx=${dx}, dy=${dy}`);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
async function closeBrowserForRegression(browser) {
|
|
708
|
+
let closed = false;
|
|
709
|
+
const closePromise = browser.close()
|
|
710
|
+
.then(() => {
|
|
711
|
+
closed = true;
|
|
712
|
+
})
|
|
713
|
+
.catch(() => {
|
|
714
|
+
closed = true;
|
|
715
|
+
});
|
|
716
|
+
await Promise.race([
|
|
717
|
+
closePromise,
|
|
718
|
+
new Promise((resolve) => setTimeout(resolve, 5000))
|
|
719
|
+
]);
|
|
720
|
+
const browserProcess = typeof browser.process === "function" ? browser.process() : null;
|
|
721
|
+
if (!closed) browserProcess?.kill("SIGKILL");
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
function round(value) {
|
|
725
|
+
return Math.round(value * 1000) / 1000;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
function cssString(value) {
|
|
729
|
+
return String(value).replaceAll("\\", "\\\\").replaceAll('"', '\\"');
|
|
730
|
+
}
|