oh-my-knowledge 0.44.0 → 0.46.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 +1 -1
- package/README.zh.md +1 -1
- package/dist/artifact-graph/doctor.js +1 -1
- package/dist/artifact-graph/eval.js +120 -0
- package/dist/assets/agent-skills/omk/SKILL.md +3 -3
- package/dist/assets/agent-skills/omk/references/commands.md +13 -6
- package/dist/cli/commands/doctor.d.ts +3 -2
- package/dist/cli/commands/doctor.js +66 -63
- package/dist/cli/lib/cmd-flags.d.ts +3 -2
- package/dist/cli/lib/i18n-dict/common.d.ts +1 -1
- package/dist/cli/lib/i18n-dict/common.js +0 -4
- package/dist/doctor/endpoint-rule.js +1 -1
- package/dist/doctor/health/composer.js +162 -50
- package/dist/doctor/health/consensus.d.ts +43 -0
- package/dist/doctor/health/consensus.js +224 -0
- package/dist/doctor/health/dimension-spec.d.ts +6 -0
- package/dist/doctor/health/register.d.ts +0 -1
- package/dist/doctor/health/register.js +0 -1
- package/dist/doctor/index.js +26 -3
- package/dist/doctor/messages.d.ts +1 -1
- package/dist/doctor/messages.js +8 -0
- package/dist/doctor/rules.d.ts +4 -2
- package/dist/doctor/rules.js +4 -2
- package/dist/eval-core/evaluation-reporting.js +1 -0
- package/dist/executors/claude-cli.js +1 -1
- package/dist/inputs/load-samples.js +27 -0
- package/dist/observability/skill-chain-advisories.js +2 -2
- package/dist/renderer/doctor-detail-renderer.js +30 -2
- package/dist/renderer/report-shell.d.ts +1 -1
- package/dist/renderer/report-shell.js +5 -0
- package/dist/renderer/skill-detail-renderer.js +908 -94
- package/dist/renderer/skill-list-renderer.js +1 -1
- package/dist/server/skill-index.js +58 -1
- package/dist/shared/llm-prompts/registry.js +2 -1
- package/dist/shared/llm-prompts/skill-health-merge.d.ts +22 -0
- package/dist/shared/llm-prompts/skill-health-merge.js +79 -0
- package/dist/shared/llm-prompts/skill-health.js +1 -1
- package/dist/types/artifact-graph.d.ts +1 -1
- package/dist/types/doctor.d.ts +32 -8
- package/dist/types/eval.d.ts +10 -0
- package/dist/types/report.d.ts +3 -1
- package/dist/types/skill-index.d.ts +14 -0
- package/package.json +6 -5
|
@@ -400,11 +400,21 @@ details.si-pass-card[open] > summary > .si-pass-row1::before { content:'▾ ' }
|
|
|
400
400
|
/* Skill Map:以 SKILL.md 为根的轻量知识图,不暴露完整 graph JSON 拓扑 */
|
|
401
401
|
.sm-sect { border-left-color:#4f46e5 }
|
|
402
402
|
.sm-body { display:flex;flex-direction:column;gap:10px }
|
|
403
|
+
.sm-evidence-strip { display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px }
|
|
404
|
+
.sm-evidence-item { min-height:44px;padding:8px 10px;border:1px solid #e4e8f1;border-radius:8px;background:#fff;display:flex;flex-direction:column;justify-content:center;gap:2px }
|
|
405
|
+
.sm-evidence-item--risk { border-color:rgba(220,38,38,.34);background:rgba(220,38,38,.055) }
|
|
406
|
+
.sm-evidence-item--action { border-color:rgba(245,158,11,.42);background:rgba(245,158,11,.075) }
|
|
407
|
+
.sm-evidence-k { font-size:10px;color:#8792a4;font-weight:700;line-height:1;text-transform:uppercase;font-family:"SF Mono",Menlo,monospace }
|
|
408
|
+
.sm-evidence-v { font-size:12.5px;color:#182033;font-weight:700;line-height:1.35;word-break:break-word }
|
|
403
409
|
.sm-toolbar { display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;padding:8px 10px;border:1px solid #e4e8f1;border-radius:8px;background:#fff }
|
|
404
410
|
.sm-tools,.sm-toggles { display:flex;align-items:center;gap:6px;flex-wrap:wrap }
|
|
405
411
|
.sm-tool { min-width:30px;height:28px;padding:0 9px;border:1px solid #dbe2ee;border-radius:6px;background:#fff;color:#384255;font-size:13px;font-weight:700;cursor:pointer }
|
|
406
412
|
.sm-tool:hover { background:#f7f8ff;border-color:#c7d2fe }
|
|
407
413
|
.sm-zoom-label { min-width:44px;text-align:center;font-size:11.5px;color:#637083;font-variant-numeric:tabular-nums }
|
|
414
|
+
.sm-view-switch { display:inline-flex;align-items:center;gap:3px;padding:3px;border:1px solid #dbe2ee;border-radius:8px;background:#f8fafc }
|
|
415
|
+
.sm-view { height:26px;padding:0 10px;border:0;border-radius:6px;background:transparent;color:#637083;font-size:11.5px;font-weight:700;cursor:pointer }
|
|
416
|
+
.sm-view:hover { color:#384255;background:#fff }
|
|
417
|
+
.sm-view.is-active { color:#182033;background:#fff;box-shadow:0 1px 4px rgba(31,41,55,.08) }
|
|
408
418
|
.sm-toggle { display:inline-flex;align-items:center;gap:5px;height:28px;padding:0 8px;border:1px solid #dbe2ee;border-radius:14px;background:#fff;color:#384255;font-size:11.5px;cursor:pointer;user-select:none }
|
|
409
419
|
.sm-toggle input { margin:0;accent-color:#4f46e5 }
|
|
410
420
|
.sm-canvas { position:relative;height:min(68vh,620px);min-height:520px;padding:0;background:
|
|
@@ -412,6 +422,13 @@ details.si-pass-card[open] > summary > .si-pass-row1::before { content:'▾ ' }
|
|
|
412
422
|
linear-gradient(rgba(148,163,184,.075) 1px,transparent 1px),
|
|
413
423
|
linear-gradient(90deg,rgba(148,163,184,.06) 1px,transparent 1px),
|
|
414
424
|
linear-gradient(#fff,#fbfcff);background-size:auto,28px 28px,28px 28px,auto;background-position:0 0;border:1px solid #e4e8f1;border-radius:8px;overflow:auto;overscroll-behavior:contain }
|
|
425
|
+
.sm-body[data-sm-view="boundary"] .sm-canvas { background:
|
|
426
|
+
linear-gradient(90deg,rgba(31,157,99,.052) 0%,rgba(31,157,99,.025) 42%,transparent 42%,transparent 100%),
|
|
427
|
+
linear-gradient(rgba(148,163,184,.07) 1px,transparent 1px),
|
|
428
|
+
linear-gradient(90deg,rgba(148,163,184,.055) 1px,transparent 1px),
|
|
429
|
+
linear-gradient(#fff,#fbfcff);background-size:auto,28px 28px,28px 28px,auto;background-position:0 0 }
|
|
430
|
+
.sm-body[data-sm-view="boundary"] [data-sm-layer="measurement"],
|
|
431
|
+
.sm-body[data-sm-view="boundary"] [data-sm-evidence-edge="1"] { display:none !important }
|
|
415
432
|
.sm-graph-stage { position:relative;width:1100px;height:560px }
|
|
416
433
|
.sm-graph { position:absolute;left:0;top:0;width:1100px;height:560px;transform-origin:0 0;transition:transform .12s ease }
|
|
417
434
|
.sm-graph [hidden] { display:none !important }
|
|
@@ -419,6 +436,11 @@ details.si-pass-card[open] > summary > .si-pass-row1::before { content:'▾ ' }
|
|
|
419
436
|
.sm-edge { fill:none;stroke:#d8e0ec;stroke-width:1.45;stroke-linecap:round;opacity:.86;vector-effect:non-scaling-stroke }
|
|
420
437
|
.sm-edge--definition { stroke:#a9cfc4 }
|
|
421
438
|
.sm-edge--measurement { stroke:#bbc7ff }
|
|
439
|
+
.sm-edge--coverage { stroke:#f59e0b;stroke-width:2;stroke-dasharray:5 5;opacity:.9 }
|
|
440
|
+
.sm-edge--coverage-failed { stroke:#dc2626;stroke-width:2.2;opacity:.92 }
|
|
441
|
+
.sm-zone-label { position:absolute;top:16px;z-index:1;font-size:10.5px;font-weight:800;letter-spacing:0;text-transform:uppercase;color:#8792a4;font-family:"SF Mono",Menlo,monospace;background:rgba(255,255,255,.82);border:1px solid #e4e8f1;border-radius:12px;padding:4px 9px;pointer-events:none }
|
|
442
|
+
.sm-zone-label--definition { left:36px;color:#2f7d68;border-color:rgba(47,125,104,.22);background:rgba(244,251,248,.86) }
|
|
443
|
+
.sm-zone-label--measurement { right:36px;color:#4f46e5;border-color:rgba(79,70,229,.22);background:rgba(247,248,255,.86) }
|
|
422
444
|
.sm-node { position:absolute;z-index:1;box-sizing:border-box;transform:translate(-50%,-50%);width:148px;min-height:54px;padding:8px 10px;border:1px solid #d8deea;border-radius:7px;background:rgba(255,255,255,.96);box-shadow:0 8px 20px rgba(31,41,55,6%);display:flex;flex-direction:column;justify-content:center;gap:4px;text-align:center;backdrop-filter:blur(4px) }
|
|
423
445
|
.sm-node[data-sm-draggable="1"] { cursor:grab;touch-action:none;user-select:none }
|
|
424
446
|
.sm-node[data-sm-draggable="1"].is-dragging { cursor:grabbing;z-index:3;box-shadow:0 16px 34px rgba(31,41,55,14%) }
|
|
@@ -426,18 +448,36 @@ details.si-pass-card[open] > summary > .si-pass-row1::before { content:'▾ ' }
|
|
|
426
448
|
.sm-node[data-sm-draggable="1"][data-sm-more-toggle] { cursor:grab }
|
|
427
449
|
.sm-node[data-sm-draggable="1"][data-sm-more-toggle].is-dragging { cursor:grabbing }
|
|
428
450
|
.sm-node[data-sm-more-toggle]:hover,.sm-node[data-sm-more-toggle]:focus-visible { border-color:#9fbfb5;background:#f1fbf7;outline:none }
|
|
451
|
+
.sm-node:hover,.sm-node:focus-visible,.sm-node.is-selected { border-color:#4f46e5;outline:3px solid rgba(79,70,229,.12);outline-offset:2px }
|
|
452
|
+
.sm-node.is-selected { z-index:2;box-shadow:0 14px 30px rgba(79,70,229,13%) }
|
|
453
|
+
.sm-node.is-related { z-index:2;outline:3px solid rgba(245,158,11,.14);outline-offset:2px;box-shadow:0 12px 26px rgba(245,158,11,.12) }
|
|
429
454
|
.sm-node--skill { width:172px;min-height:86px;border-color:#4f46e5;background:linear-gradient(180deg,#fff 0%,#f7f8ff 100%);box-shadow:0 16px 34px rgba(79,70,229,14%);outline:4px solid rgba(79,70,229,.07) }
|
|
455
|
+
.sm-node--skill.is-selected { outline-color:rgba(79,70,229,.15) }
|
|
430
456
|
.sm-node--definition { border-color:#b7d8cf;background:#f4fbf8 }
|
|
431
457
|
.sm-node--measurement { border-color:#c7d2fe;background:#f7f8ff }
|
|
432
458
|
.sm-node--ok { border-color:rgba(31,157,99,.38);background:rgba(31,157,99,.06) }
|
|
433
459
|
.sm-node--warning { border-color:rgba(217,119,6,.38);background:rgba(217,119,6,.07) }
|
|
434
460
|
.sm-node--failed { border-color:rgba(220,38,38,.38);background:rgba(220,38,38,.06) }
|
|
435
461
|
.sm-node--pending,.sm-node--more { border-style:dashed;background:rgba(248,249,251,.94);color:#637083 }
|
|
462
|
+
.sm-node--coverage-declared { border-color:rgba(31,157,99,.42);box-shadow:0 10px 24px rgba(31,157,99,.08) }
|
|
463
|
+
.sm-node--coverage-undeclared { border-style:dashed;background:rgba(248,249,251,.94);color:#637083 }
|
|
436
464
|
.sm-node-title { font-size:12.2px;font-weight:700;color:#182033;line-height:1.28;word-break:break-word;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden }
|
|
437
465
|
.sm-node--skill .sm-node-title { font-size:15px;line-height:1.2 }
|
|
438
466
|
.sm-node-meta { font-size:10.5px;color:#637083;line-height:1.35;word-break:break-word }
|
|
439
467
|
.sm-node--skill .sm-node-meta { font-size:11.5px }
|
|
440
468
|
.sm-node-kind { font-size:9.5px;text-transform:uppercase;letter-spacing:0;color:#8792a4;font-family:"SF Mono",Menlo,monospace;line-height:1 }
|
|
469
|
+
.sm-node--group { width:164px;min-height:66px;border-color:rgba(47,125,104,.42);background:linear-gradient(180deg,#fff 0%,#f4fbf8 100%);box-shadow:0 12px 28px rgba(47,125,104,.1) }
|
|
470
|
+
.sm-node--group .sm-node-title { font-size:13px }
|
|
471
|
+
.sm-node-toggle-icon { position:absolute;right:7px;top:7px;width:17px;height:17px;border:1px solid rgba(47,125,104,.24);border-radius:50%;background:rgba(255,255,255,.72);box-shadow:0 4px 10px rgba(47,125,104,.06);color:#2f7d68;display:flex;align-items:center;justify-content:center;transition:background .14s ease,border-color .14s ease,box-shadow .14s ease }
|
|
472
|
+
.sm-node-toggle-icon::before { content:'';width:5px;height:5px;border-right:1.8px solid currentColor;border-bottom:1.8px solid currentColor;transform:translateY(-1px) rotate(45deg);transition:transform .14s ease }
|
|
473
|
+
.sm-node[data-sm-more-toggle]:hover .sm-node-toggle-icon,.sm-node[data-sm-more-toggle]:focus-visible .sm-node-toggle-icon { background:#fff;border-color:rgba(47,125,104,.42);box-shadow:0 5px 12px rgba(47,125,104,.11) }
|
|
474
|
+
.sm-node[data-sm-more-toggle][aria-expanded="true"] .sm-node-toggle-icon { background:rgba(47,125,104,.1);border-color:rgba(47,125,104,.34);box-shadow:0 4px 10px rgba(47,125,104,.08) }
|
|
475
|
+
.sm-node[data-sm-more-toggle][aria-expanded="true"] .sm-node-toggle-icon::before { transform:translateY(1px) rotate(225deg) }
|
|
476
|
+
.sm-node-signal { align-self:center;max-width:100%;padding:1px 6px;border-radius:9px;font-size:9.5px;font-weight:700;line-height:1.25;color:#637083;background:#eef2f7;white-space:nowrap;overflow:hidden;text-overflow:ellipsis }
|
|
477
|
+
.sm-node-signal--failed { color:#dc2626;background:rgba(220,38,38,.12) }
|
|
478
|
+
.sm-node-signal--warning { color:#d97706;background:rgba(217,119,6,.13) }
|
|
479
|
+
.sm-node-signal--declared { color:#1f9d63;background:rgba(31,157,99,.13) }
|
|
480
|
+
.sm-node-signal--undeclared { color:#637083;background:#eef2f7;border:1px dashed #cbd5e1 }
|
|
441
481
|
.sm-stage-rail { display:grid;grid-template-columns:1fr auto 1fr auto 1fr;align-items:center;gap:8px;padding:10px 12px;border:1px solid #e4e8f1;border-radius:8px;background:#fff }
|
|
442
482
|
.sm-stage-card { min-height:50px;padding:9px 11px;border:1px solid #dbe2ee;border-radius:8px;background:#fbfcff;text-align:center;display:flex;flex-direction:column;justify-content:center;gap:3px }
|
|
443
483
|
.sm-stage-card--ok { border-color:rgba(31,157,99,.38);background:rgba(31,157,99,.06) }
|
|
@@ -447,6 +487,16 @@ details.si-pass-card[open] > summary > .si-pass-row1::before { content:'▾ ' }
|
|
|
447
487
|
.sm-stage-k { font-size:10px;text-transform:uppercase;letter-spacing:0;color:#7b8798;font-family:"SF Mono",Menlo,monospace;line-height:1 }
|
|
448
488
|
.sm-stage-v { font-size:13px;font-weight:700;color:#182033;line-height:1.35 }
|
|
449
489
|
.sm-stage-arrow { color:#b8c5d8;font-size:18px;line-height:1;text-align:center }
|
|
490
|
+
.sm-detail { display:grid;grid-template-columns:minmax(180px,1.1fr) minmax(260px,2fr);gap:12px;align-items:start;padding:12px;border:1px solid #e4e8f1;border-radius:8px;background:linear-gradient(180deg,#fff 0%,#fbfcff 100%) }
|
|
491
|
+
.sm-detail-head { min-width:0 }
|
|
492
|
+
.sm-detail-kind { display:inline-flex;align-items:center;min-height:20px;padding:0 7px;border-radius:10px;background:#eef2ff;color:#4f46e5;font-size:10.5px;font-weight:700;line-height:1 }
|
|
493
|
+
.sm-detail-title { margin-top:8px;font-size:15px;font-weight:700;line-height:1.35;color:#182033;word-break:break-word }
|
|
494
|
+
.sm-detail-subtitle { margin-top:3px;font-size:12px;line-height:1.45;color:#637083;word-break:break-word }
|
|
495
|
+
.sm-detail-list { margin:0;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px }
|
|
496
|
+
.sm-detail-row { min-width:0;padding:8px 10px;border:1px solid #eef2f7;border-radius:7px;background:#fff }
|
|
497
|
+
.sm-detail-row[hidden] { display:none !important }
|
|
498
|
+
.sm-detail-row dt { margin:0 0 3px;font-size:10.5px;color:#8792a4;font-weight:600;line-height:1.2 }
|
|
499
|
+
.sm-detail-row dd { margin:0;font-size:12px;color:#384255;line-height:1.45;word-break:break-word }
|
|
450
500
|
.sm-card { border:1px solid #e4e8f1;border-radius:8px;background:#fff;overflow:hidden }
|
|
451
501
|
.sm-card > summary { cursor:pointer;list-style:none;padding:10px 12px;font-size:12.5px;font-weight:600;color:#4f46e5;user-select:none }
|
|
452
502
|
.sm-card > summary::-webkit-details-marker { display:none }
|
|
@@ -455,9 +505,12 @@ details.si-pass-card[open] > summary > .si-pass-row1::before { content:'▾ ' }
|
|
|
455
505
|
.sm-card textarea { width:100%;min-height:260px;border:0;border-top:1px solid #e4e8f1;background:#fbfcff;padding:12px;font-family:"SF Mono",Menlo,monospace;font-size:11.5px;line-height:1.55;color:#182033;resize:vertical;box-sizing:border-box }
|
|
456
506
|
@media(max-width:900px){
|
|
457
507
|
.sm-toolbar { align-items:flex-start }
|
|
508
|
+
.sm-evidence-strip { grid-template-columns:1fr }
|
|
458
509
|
.sm-stage-rail { grid-template-columns:1fr;gap:6px }
|
|
459
510
|
.sm-stage-arrow { transform:rotate(90deg);font-size:16px }
|
|
460
511
|
.sm-canvas { height:620px;min-height:620px }
|
|
512
|
+
.sm-detail { grid-template-columns:1fr }
|
|
513
|
+
.sm-detail-list { grid-template-columns:1fr }
|
|
461
514
|
.sm-node { width:142px }
|
|
462
515
|
.sm-node--skill { width:168px }
|
|
463
516
|
}
|
|
@@ -844,6 +897,7 @@ const SKILL_MAP_INIT_SCRIPT = `
|
|
|
844
897
|
var stage = map.querySelector('[data-sm-graph-stage]');
|
|
845
898
|
var graph = map.querySelector('[data-sm-graph]');
|
|
846
899
|
var label = map.querySelector('[data-sm-zoom-label]');
|
|
900
|
+
var detail = map.querySelector('[data-sm-detail]');
|
|
847
901
|
if (!viewport || !stage || !graph) return;
|
|
848
902
|
var baseWidth = Number(map.getAttribute('data-sm-base-width')) || 1100;
|
|
849
903
|
var baseHeight = Number(map.getAttribute('data-sm-base-height')) || 560;
|
|
@@ -852,6 +906,8 @@ const SKILL_MAP_INIT_SCRIPT = `
|
|
|
852
906
|
var zoom = 1;
|
|
853
907
|
var dragState = null;
|
|
854
908
|
var suppressMoreClickUntil = 0;
|
|
909
|
+
var selectedNodeId = '';
|
|
910
|
+
var currentView = map.getAttribute('data-sm-view') || 'boundary';
|
|
855
911
|
|
|
856
912
|
function edgePoint(fromX, fromY, toX, toY, box){
|
|
857
913
|
var dx = toX - fromX;
|
|
@@ -900,6 +956,92 @@ const SKILL_MAP_INIT_SCRIPT = `
|
|
|
900
956
|
node.style.top = y + 'px';
|
|
901
957
|
}
|
|
902
958
|
|
|
959
|
+
function nodeViewPosition(node, view){
|
|
960
|
+
var prefix = view === 'evidence' ? 'data-sm-evidence' : 'data-sm-boundary';
|
|
961
|
+
var x = Number(node.getAttribute(prefix + '-x'));
|
|
962
|
+
var y = Number(node.getAttribute(prefix + '-y'));
|
|
963
|
+
if (Number.isFinite(x) && Number.isFinite(y)) return { x: x, y: y };
|
|
964
|
+
return {
|
|
965
|
+
x: Number(node.getAttribute('data-sm-origin-x')) || Number(node.getAttribute('data-sm-x')) || parseFloat(node.style.left) || 0,
|
|
966
|
+
y: Number(node.getAttribute('data-sm-origin-y')) || Number(node.getAttribute('data-sm-y')) || parseFloat(node.style.top) || 0
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
function hideFilteredNode(node){
|
|
971
|
+
node.hidden = true;
|
|
972
|
+
node.toggleAttribute('hidden', true);
|
|
973
|
+
node.setAttribute('aria-hidden', 'true');
|
|
974
|
+
node.style.display = 'none';
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
function revealNode(node, transient){
|
|
978
|
+
node.hidden = false;
|
|
979
|
+
node.toggleAttribute('hidden', false);
|
|
980
|
+
node.setAttribute('aria-hidden', 'false');
|
|
981
|
+
node.style.display = '';
|
|
982
|
+
node.setAttribute('data-sm-filter-hidden', '0');
|
|
983
|
+
if (transient) node.setAttribute('data-sm-evidence-revealed', '1');
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
function detailText(node, attr, fallback){
|
|
987
|
+
var value = node ? node.getAttribute(attr) : '';
|
|
988
|
+
return value && value.trim() ? value : (fallback || '');
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
function setDetailValue(key, value, required){
|
|
992
|
+
if (!detail) return;
|
|
993
|
+
var row = detail.querySelector('[data-sm-detail-row="' + key + '"]');
|
|
994
|
+
var valueEl = detail.querySelector('[data-sm-detail-value="' + key + '"]');
|
|
995
|
+
var hasValue = !!(value && value.trim());
|
|
996
|
+
if (valueEl) valueEl.textContent = hasValue ? value : '—';
|
|
997
|
+
if (row) {
|
|
998
|
+
var hidden = !hasValue && !required;
|
|
999
|
+
row.hidden = hidden;
|
|
1000
|
+
row.toggleAttribute('hidden', hidden);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
function selectedMoreStatus(node){
|
|
1005
|
+
if (!node || !node.hasAttribute('data-sm-more-toggle')) return '';
|
|
1006
|
+
var attr = node.getAttribute('aria-expanded') === 'true'
|
|
1007
|
+
? 'data-sm-detail-expanded-status'
|
|
1008
|
+
: 'data-sm-detail-collapsed-status';
|
|
1009
|
+
return node.getAttribute(attr) || '';
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
function setSelectedNode(node){
|
|
1013
|
+
if (!node || node.hidden) return;
|
|
1014
|
+
var nodeId = node.getAttribute('data-sm-node-id') || '';
|
|
1015
|
+
selectedNodeId = nodeId;
|
|
1016
|
+
graph.querySelectorAll('.sm-node.is-selected').forEach(function(item){
|
|
1017
|
+
item.classList.remove('is-selected');
|
|
1018
|
+
item.setAttribute('aria-pressed', 'false');
|
|
1019
|
+
});
|
|
1020
|
+
node.classList.add('is-selected');
|
|
1021
|
+
node.setAttribute('aria-pressed', 'true');
|
|
1022
|
+
if (!detail) return;
|
|
1023
|
+
var title = detailText(node, 'data-sm-detail-title', node.textContent || '');
|
|
1024
|
+
var subtitle = detailText(node, 'data-sm-detail-subtitle', '');
|
|
1025
|
+
var kind = detailText(node, 'data-sm-detail-kind', '');
|
|
1026
|
+
var kindEl = detail.querySelector('[data-sm-detail-kind-label]');
|
|
1027
|
+
var titleEl = detail.querySelector('[data-sm-detail-title]');
|
|
1028
|
+
var subtitleEl = detail.querySelector('[data-sm-detail-subtitle]');
|
|
1029
|
+
if (kindEl) kindEl.textContent = kind;
|
|
1030
|
+
if (titleEl) titleEl.textContent = title;
|
|
1031
|
+
if (subtitleEl) {
|
|
1032
|
+
subtitleEl.textContent = subtitle;
|
|
1033
|
+
subtitleEl.hidden = !subtitle;
|
|
1034
|
+
subtitleEl.toggleAttribute('hidden', !subtitle);
|
|
1035
|
+
}
|
|
1036
|
+
setDetailValue('scope', detailText(node, 'data-sm-detail-scope', ''), true);
|
|
1037
|
+
setDetailValue('source', detailText(node, 'data-sm-detail-source', ''), false);
|
|
1038
|
+
setDetailValue('coverage', detailText(node, 'data-sm-detail-coverage', ''), false);
|
|
1039
|
+
setDetailValue('status', selectedMoreStatus(node) || detailText(node, 'data-sm-detail-status', ''), false);
|
|
1040
|
+
setDetailValue('evidence', detailText(node, 'data-sm-detail-evidence', ''), false);
|
|
1041
|
+
setDetailValue('action', detailText(node, 'data-sm-detail-action', ''), false);
|
|
1042
|
+
updateEvidenceFocus();
|
|
1043
|
+
}
|
|
1044
|
+
|
|
903
1045
|
function updateEdgesForNode(nodeId, x, y){
|
|
904
1046
|
map.querySelectorAll('[data-sm-edge-to]').forEach(function(edge){
|
|
905
1047
|
if (edge.getAttribute('data-sm-edge-to') !== nodeId) return;
|
|
@@ -949,13 +1091,16 @@ const SKILL_MAP_INIT_SCRIPT = `
|
|
|
949
1091
|
}
|
|
950
1092
|
|
|
951
1093
|
function resetNodePositions(){
|
|
1094
|
+
applyViewPositions(currentView);
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
function applyViewPositions(view){
|
|
952
1098
|
graph.querySelectorAll('[data-sm-draggable="1"]').forEach(function(node){
|
|
953
1099
|
var nodeId = node.getAttribute('data-sm-node-id');
|
|
954
|
-
var
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
if (nodeId)
|
|
958
|
-
if (nodeId) updateEdgesFromNode(nodeId, originX, originY);
|
|
1100
|
+
var next = nodeViewPosition(node, view);
|
|
1101
|
+
setNodePosition(node, next.x, next.y);
|
|
1102
|
+
if (nodeId) updateEdgesForNode(nodeId, next.x, next.y);
|
|
1103
|
+
if (nodeId) updateEdgesFromNode(nodeId, next.x, next.y);
|
|
959
1104
|
});
|
|
960
1105
|
}
|
|
961
1106
|
|
|
@@ -987,35 +1132,103 @@ const SKILL_MAP_INIT_SCRIPT = `
|
|
|
987
1132
|
viewport.scrollTop = Math.max(0, scaledRootY - viewport.clientHeight / 2);
|
|
988
1133
|
}
|
|
989
1134
|
|
|
990
|
-
function layerEnabled(layer){
|
|
991
|
-
var input = map.querySelector('[data-sm-toggle-layer="' + layer + '"]');
|
|
992
|
-
return !input || input.checked;
|
|
993
|
-
}
|
|
994
|
-
|
|
995
1135
|
function overflowGroupExpanded(group){
|
|
996
1136
|
if (!group) return true;
|
|
997
1137
|
var toggle = map.querySelector('[data-sm-more-toggle="' + group + '"]');
|
|
998
1138
|
return !!toggle && toggle.getAttribute('aria-expanded') === 'true';
|
|
999
1139
|
}
|
|
1000
1140
|
|
|
1141
|
+
function updateViewButtons(){
|
|
1142
|
+
map.setAttribute('data-sm-view', currentView);
|
|
1143
|
+
map.querySelectorAll('[data-sm-view-button]').forEach(function(button){
|
|
1144
|
+
var active = button.getAttribute('data-sm-view-button') === currentView;
|
|
1145
|
+
button.classList.toggle('is-active', active);
|
|
1146
|
+
button.setAttribute('aria-pressed', active ? 'true' : 'false');
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
function updateEvidenceFocus(){
|
|
1151
|
+
if (!graph) return;
|
|
1152
|
+
graph.querySelectorAll('[data-sm-evidence-revealed="1"]').forEach(function(node){
|
|
1153
|
+
node.removeAttribute('data-sm-evidence-revealed');
|
|
1154
|
+
if (node.getAttribute('data-sm-layer') === 'definition' && currentView === 'evidence') hideFilteredNode(node);
|
|
1155
|
+
});
|
|
1156
|
+
graph.querySelectorAll('.sm-node.is-related').forEach(function(node){
|
|
1157
|
+
node.classList.remove('is-related');
|
|
1158
|
+
});
|
|
1159
|
+
map.querySelectorAll('[data-sm-evidence-edge="1"]').forEach(function(edge){
|
|
1160
|
+
var toId = edge.getAttribute('data-sm-edge-to');
|
|
1161
|
+
var fromId = edge.getAttribute('data-sm-edge-from-node');
|
|
1162
|
+
var toNode = toId ? graph.querySelector('[data-sm-node-id="' + toId + '"]') : null;
|
|
1163
|
+
var fromNode = fromId ? graph.querySelector('[data-sm-node-id="' + fromId + '"]') : null;
|
|
1164
|
+
var focused = currentView === 'evidence' && !!selectedNodeId && (toId === selectedNodeId || fromId === selectedNodeId);
|
|
1165
|
+
if (focused) {
|
|
1166
|
+
if (toNode) revealNode(toNode, true);
|
|
1167
|
+
if (fromNode) revealNode(fromNode, true);
|
|
1168
|
+
}
|
|
1169
|
+
var endpointHidden = (toNode && toNode.hidden) || (fromNode && fromNode.hidden);
|
|
1170
|
+
var shouldHide = !focused || endpointHidden;
|
|
1171
|
+
edge.hidden = shouldHide;
|
|
1172
|
+
edge.toggleAttribute('hidden', shouldHide);
|
|
1173
|
+
edge.setAttribute('aria-hidden', shouldHide ? 'true' : 'false');
|
|
1174
|
+
edge.style.display = shouldHide ? 'none' : '';
|
|
1175
|
+
if (!shouldHide) {
|
|
1176
|
+
if (toNode && toId !== selectedNodeId) toNode.classList.add('is-related');
|
|
1177
|
+
if (fromNode && fromId !== selectedNodeId) fromNode.classList.add('is-related');
|
|
1178
|
+
}
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1001
1182
|
function updateVisibility(){
|
|
1002
1183
|
var showLeaves = true;
|
|
1003
1184
|
var leafToggle = map.querySelector('[data-sm-toggle-leaves]');
|
|
1004
1185
|
if (leafToggle) showLeaves = leafToggle.checked;
|
|
1005
1186
|
var layers = {
|
|
1006
|
-
definition:
|
|
1007
|
-
measurement:
|
|
1187
|
+
definition: currentView === 'boundary',
|
|
1188
|
+
measurement: currentView === 'evidence'
|
|
1008
1189
|
};
|
|
1009
1190
|
map.querySelectorAll('[data-sm-layer]').forEach(function(el){
|
|
1010
1191
|
var layer = el.getAttribute('data-sm-layer');
|
|
1011
1192
|
var isLeaf = el.getAttribute('data-sm-leaf') === '1';
|
|
1012
1193
|
var overflowGroup = el.getAttribute('data-sm-overflow-group');
|
|
1013
1194
|
var shouldHide = !layers[layer] || (isLeaf && !showLeaves) || !overflowGroupExpanded(overflowGroup);
|
|
1195
|
+
el.setAttribute('data-sm-filter-hidden', shouldHide ? '1' : '0');
|
|
1014
1196
|
el.hidden = shouldHide;
|
|
1015
1197
|
el.toggleAttribute('hidden', shouldHide);
|
|
1016
1198
|
el.setAttribute('aria-hidden', shouldHide ? 'true' : 'false');
|
|
1017
1199
|
el.style.display = shouldHide ? 'none' : '';
|
|
1018
1200
|
});
|
|
1201
|
+
map.querySelectorAll('[data-sm-edge-to]').forEach(function(edge){
|
|
1202
|
+
if (edge.getAttribute('data-sm-evidence-edge') === '1') return;
|
|
1203
|
+
var toId = edge.getAttribute('data-sm-edge-to');
|
|
1204
|
+
var fromId = edge.getAttribute('data-sm-edge-from-node');
|
|
1205
|
+
var toNode = toId ? graph.querySelector('[data-sm-node-id="' + toId + '"]') : null;
|
|
1206
|
+
var fromNode = fromId ? graph.querySelector('[data-sm-node-id="' + fromId + '"]') : null;
|
|
1207
|
+
var filterHidden = edge.getAttribute('data-sm-filter-hidden') === '1';
|
|
1208
|
+
var endpointHidden = (toNode && toNode.hidden) || (fromNode && fromNode.hidden);
|
|
1209
|
+
var shouldHideEdge = filterHidden || endpointHidden;
|
|
1210
|
+
edge.hidden = shouldHideEdge;
|
|
1211
|
+
edge.toggleAttribute('hidden', shouldHideEdge);
|
|
1212
|
+
edge.setAttribute('aria-hidden', shouldHideEdge ? 'true' : 'false');
|
|
1213
|
+
edge.style.display = shouldHideEdge ? 'none' : '';
|
|
1214
|
+
});
|
|
1215
|
+
if (selectedNodeId) {
|
|
1216
|
+
var selectedNode = graph.querySelector('[data-sm-node-id="' + selectedNodeId + '"]');
|
|
1217
|
+
if (selectedNode && selectedNode.hidden) {
|
|
1218
|
+
var rootNode = graph.querySelector('[data-sm-root]');
|
|
1219
|
+
if (rootNode) setSelectedNode(rootNode);
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
updateEvidenceFocus();
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
function setView(view){
|
|
1226
|
+
currentView = view === 'evidence' ? 'evidence' : 'boundary';
|
|
1227
|
+
applyViewPositions(currentView);
|
|
1228
|
+
updateViewButtons();
|
|
1229
|
+
updateVisibility();
|
|
1230
|
+
refreshEdges();
|
|
1231
|
+
centerRoot();
|
|
1019
1232
|
}
|
|
1020
1233
|
|
|
1021
1234
|
function setMoreExpanded(toggle, expanded){
|
|
@@ -1023,11 +1236,11 @@ const SKILL_MAP_INIT_SCRIPT = `
|
|
|
1023
1236
|
var nextLabel = expanded
|
|
1024
1237
|
? toggle.getAttribute('data-sm-expanded-label')
|
|
1025
1238
|
: toggle.getAttribute('data-sm-collapsed-label');
|
|
1026
|
-
var title = toggle.querySelector('.sm-node-title');
|
|
1027
|
-
if (title && nextLabel) title.textContent = nextLabel;
|
|
1028
1239
|
if (nextLabel) toggle.setAttribute('title', nextLabel);
|
|
1240
|
+
if (nextLabel) toggle.setAttribute('aria-label', nextLabel);
|
|
1029
1241
|
updateVisibility();
|
|
1030
1242
|
refreshEdges();
|
|
1243
|
+
if (selectedNodeId && toggle.getAttribute('data-sm-node-id') === selectedNodeId) setSelectedNode(toggle);
|
|
1031
1244
|
}
|
|
1032
1245
|
|
|
1033
1246
|
function collapseMoreGroups(){
|
|
@@ -1051,7 +1264,13 @@ const SKILL_MAP_INIT_SCRIPT = `
|
|
|
1051
1264
|
});
|
|
1052
1265
|
});
|
|
1053
1266
|
|
|
1054
|
-
map.querySelectorAll('[data-sm-
|
|
1267
|
+
map.querySelectorAll('[data-sm-view-button]').forEach(function(button){
|
|
1268
|
+
button.addEventListener('click', function(){
|
|
1269
|
+
setView(button.getAttribute('data-sm-view-button') || 'boundary');
|
|
1270
|
+
});
|
|
1271
|
+
});
|
|
1272
|
+
|
|
1273
|
+
map.querySelectorAll('[data-sm-toggle-leaves]').forEach(function(input){
|
|
1055
1274
|
input.addEventListener('change', updateVisibility);
|
|
1056
1275
|
});
|
|
1057
1276
|
|
|
@@ -1062,11 +1281,13 @@ const SKILL_MAP_INIT_SCRIPT = `
|
|
|
1062
1281
|
return;
|
|
1063
1282
|
}
|
|
1064
1283
|
ev.preventDefault();
|
|
1284
|
+
setSelectedNode(toggle);
|
|
1065
1285
|
setMoreExpanded(toggle, toggle.getAttribute('aria-expanded') !== 'true');
|
|
1066
1286
|
});
|
|
1067
1287
|
toggle.addEventListener('keydown', function(ev){
|
|
1068
1288
|
if (ev.key !== 'Enter' && ev.key !== ' ') return;
|
|
1069
1289
|
ev.preventDefault();
|
|
1290
|
+
setSelectedNode(toggle);
|
|
1070
1291
|
setMoreExpanded(toggle, toggle.getAttribute('aria-expanded') !== 'true');
|
|
1071
1292
|
});
|
|
1072
1293
|
});
|
|
@@ -1078,6 +1299,7 @@ const SKILL_MAP_INIT_SCRIPT = `
|
|
|
1078
1299
|
var nodeId = node.getAttribute('data-sm-node-id');
|
|
1079
1300
|
if (!nodeId) return;
|
|
1080
1301
|
ev.preventDefault();
|
|
1302
|
+
setSelectedNode(node);
|
|
1081
1303
|
dragState = {
|
|
1082
1304
|
node: node,
|
|
1083
1305
|
nodeId: nodeId,
|
|
@@ -1126,6 +1348,19 @@ const SKILL_MAP_INIT_SCRIPT = `
|
|
|
1126
1348
|
node.addEventListener('pointercancel', finishDrag);
|
|
1127
1349
|
});
|
|
1128
1350
|
|
|
1351
|
+
graph.querySelectorAll('[data-sm-node-id]').forEach(function(node){
|
|
1352
|
+
node.addEventListener('click', function(){
|
|
1353
|
+
if (node.hasAttribute('data-sm-more-toggle') && Date.now() < suppressMoreClickUntil) return;
|
|
1354
|
+
setSelectedNode(node);
|
|
1355
|
+
});
|
|
1356
|
+
node.addEventListener('keydown', function(ev){
|
|
1357
|
+
if (ev.key !== 'Enter' && ev.key !== ' ') return;
|
|
1358
|
+
if (node.hasAttribute('data-sm-more-toggle')) return;
|
|
1359
|
+
ev.preventDefault();
|
|
1360
|
+
setSelectedNode(node);
|
|
1361
|
+
});
|
|
1362
|
+
});
|
|
1363
|
+
|
|
1129
1364
|
viewport.addEventListener('wheel', function(ev){
|
|
1130
1365
|
if (!ev.ctrlKey && !ev.metaKey) return;
|
|
1131
1366
|
ev.preventDefault();
|
|
@@ -1134,8 +1369,11 @@ const SKILL_MAP_INIT_SCRIPT = `
|
|
|
1134
1369
|
setZoom(zoom + delta, ev.clientX - rect.left, ev.clientY - rect.top);
|
|
1135
1370
|
}, { passive: false });
|
|
1136
1371
|
|
|
1372
|
+
updateViewButtons();
|
|
1137
1373
|
applyZoom();
|
|
1138
1374
|
updateVisibility();
|
|
1375
|
+
var initialRoot = graph.querySelector('[data-sm-root]');
|
|
1376
|
+
if (initialRoot) setSelectedNode(initialRoot);
|
|
1139
1377
|
requestAnimationFrame(function(){
|
|
1140
1378
|
refreshEdges();
|
|
1141
1379
|
centerRoot();
|
|
@@ -1585,6 +1823,7 @@ function nodeKindLabel(kind, lang) {
|
|
|
1585
1823
|
eval_result: ['eval', 'eval'],
|
|
1586
1824
|
trace_session: ['observe', 'observe'],
|
|
1587
1825
|
more: ['more', 'more'],
|
|
1826
|
+
knowledge_group: ['group', 'group'],
|
|
1588
1827
|
};
|
|
1589
1828
|
const pair = labels[kind] ?? [kind, kind];
|
|
1590
1829
|
return zh ? pair[0] : pair[1];
|
|
@@ -1628,47 +1867,392 @@ function previewNodes(nodes, allowedKinds, limit) {
|
|
|
1628
1867
|
const SKILL_MAP_WIDTH = 1100;
|
|
1629
1868
|
const SKILL_MAP_HEIGHT = 560;
|
|
1630
1869
|
const SKILL_MAP_ROOT_ID = 'skill-root';
|
|
1631
|
-
const
|
|
1870
|
+
const SKILL_MAP_BOUNDARY_ROOT = { x: 560, y: 285 };
|
|
1871
|
+
const SKILL_MAP_EVIDENCE_ROOT = { x: 470, y: 260 };
|
|
1632
1872
|
const SKILL_MAP_ROOT_SIZE = { width: 172, height: 86 };
|
|
1633
1873
|
const SKILL_MAP_NODE_SIZE = { width: 148, height: 54 };
|
|
1634
|
-
const
|
|
1635
|
-
{
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1874
|
+
const SKILL_MAP_DEFINITION_GROUPS = [
|
|
1875
|
+
{
|
|
1876
|
+
key: 'references',
|
|
1877
|
+
labelZh: '引用材料',
|
|
1878
|
+
labelEn: 'References',
|
|
1879
|
+
descriptionZh: 'Skill 显式依赖的参考文档和知识来源。',
|
|
1880
|
+
descriptionEn: 'Reference docs and knowledge sources the Skill depends on.',
|
|
1881
|
+
nodeKinds: ['reference'],
|
|
1882
|
+
boundaryPosition: { x: 270, y: 225 },
|
|
1883
|
+
evidencePosition: { x: 140, y: 145 },
|
|
1884
|
+
childBoundaryPositions: [
|
|
1885
|
+
{ x: 135, y: 125 },
|
|
1886
|
+
{ x: 130, y: 325 },
|
|
1887
|
+
{ x: 270, y: 435 },
|
|
1888
|
+
{ x: 410, y: 125 },
|
|
1889
|
+
{ x: 405, y: 325 },
|
|
1890
|
+
],
|
|
1891
|
+
},
|
|
1892
|
+
{
|
|
1893
|
+
key: 'workflow',
|
|
1894
|
+
labelZh: '执行流程',
|
|
1895
|
+
labelEn: 'Workflow',
|
|
1896
|
+
descriptionZh: 'Skill 期望代理遵循的流程与步骤。',
|
|
1897
|
+
descriptionEn: 'Workflows and steps the agent is expected to follow.',
|
|
1898
|
+
nodeKinds: ['workflow', 'workflow_node'],
|
|
1899
|
+
boundaryPosition: { x: 850, y: 225 },
|
|
1900
|
+
evidencePosition: { x: 960, y: 145 },
|
|
1901
|
+
childBoundaryPositions: [
|
|
1902
|
+
{ x: 990, y: 125 },
|
|
1903
|
+
{ x: 990, y: 325 },
|
|
1904
|
+
{ x: 850, y: 435 },
|
|
1905
|
+
{ x: 710, y: 125 },
|
|
1906
|
+
{ x: 715, y: 325 },
|
|
1907
|
+
],
|
|
1908
|
+
},
|
|
1909
|
+
{
|
|
1910
|
+
key: 'rules',
|
|
1911
|
+
labelZh: '约束规则',
|
|
1912
|
+
labelEn: 'Rules',
|
|
1913
|
+
descriptionZh: 'Skill 中不可违反的硬规则和输出约束。',
|
|
1914
|
+
descriptionEn: 'Hard rules and output constraints that must not be violated.',
|
|
1915
|
+
nodeKinds: ['hard_rule'],
|
|
1916
|
+
boundaryPosition: { x: 560, y: 105 },
|
|
1917
|
+
evidencePosition: { x: 140, y: 285 },
|
|
1918
|
+
childBoundaryPositions: [
|
|
1919
|
+
{ x: 360, y: 80 },
|
|
1920
|
+
{ x: 760, y: 80 },
|
|
1921
|
+
{ x: 560, y: 30 },
|
|
1922
|
+
{ x: 360, y: 160 },
|
|
1923
|
+
{ x: 760, y: 160 },
|
|
1924
|
+
],
|
|
1925
|
+
},
|
|
1642
1926
|
];
|
|
1643
|
-
const
|
|
1644
|
-
{ x:
|
|
1645
|
-
{ x:
|
|
1646
|
-
{ x:
|
|
1647
|
-
{ x:
|
|
1648
|
-
{ x:
|
|
1649
|
-
|
|
1927
|
+
const SKILL_MAP_BOUNDARY_ASSET_COORDS = [
|
|
1928
|
+
{ x: 380, y: 455 },
|
|
1929
|
+
{ x: 740, y: 455 },
|
|
1930
|
+
{ x: 205, y: 455 },
|
|
1931
|
+
{ x: 915, y: 455 },
|
|
1932
|
+
{ x: 560, y: 510 },
|
|
1933
|
+
];
|
|
1934
|
+
const SKILL_MAP_EVIDENCE_DEFINITION_COORDS = [
|
|
1935
|
+
{ x: 145, y: 145 },
|
|
1936
|
+
{ x: 145, y: 285 },
|
|
1937
|
+
{ x: 145, y: 425 },
|
|
1938
|
+
{ x: 955, y: 145 },
|
|
1939
|
+
{ x: 955, y: 285 },
|
|
1940
|
+
{ x: 955, y: 425 },
|
|
1941
|
+
{ x: 560, y: 485 },
|
|
1942
|
+
];
|
|
1943
|
+
const SKILL_MAP_EVIDENCE_MEASUREMENT_COORDS = [
|
|
1944
|
+
{ x: 320, y: 140 },
|
|
1945
|
+
{ x: 560, y: 115 },
|
|
1946
|
+
{ x: 800, y: 150 },
|
|
1947
|
+
{ x: 345, y: 360 },
|
|
1948
|
+
{ x: 775, y: 365 },
|
|
1949
|
+
{ x: 560, y: 455 },
|
|
1650
1950
|
];
|
|
1651
1951
|
const SKILL_MAP_NODE_MARGIN = { x: 92, y: 48 };
|
|
1652
1952
|
const SKILL_MAP_STATUS_CLASSES = new Set(['ok', 'warning', 'failed', 'skipped', 'unknown', 'not_measured']);
|
|
1653
1953
|
function graphNodeStatusClass(node) {
|
|
1654
1954
|
return node.status && SKILL_MAP_STATUS_CLASSES.has(node.status) ? ` sm-node--${node.status}` : '';
|
|
1655
1955
|
}
|
|
1956
|
+
function graphNodeCoverageClass(node) {
|
|
1957
|
+
if (node.coverage === 'declared')
|
|
1958
|
+
return ' sm-node--coverage-declared';
|
|
1959
|
+
if (node.coverage === 'undeclared')
|
|
1960
|
+
return ' sm-node--coverage-undeclared';
|
|
1961
|
+
return '';
|
|
1962
|
+
}
|
|
1963
|
+
function graphNodeCoverageLabel(node, lang) {
|
|
1964
|
+
const groupSummary = skillMapGroupSummary(node);
|
|
1965
|
+
if (groupSummary) {
|
|
1966
|
+
return lang === 'zh'
|
|
1967
|
+
? `${groupSummary.declared}/${groupSummary.total} 个节点已被评测用例声明`
|
|
1968
|
+
: `${groupSummary.declared}/${groupSummary.total} nodes declared by eval samples`;
|
|
1969
|
+
}
|
|
1970
|
+
if (node.coverage === 'declared')
|
|
1971
|
+
return lang === 'zh' ? '已由评测用例声明' : 'declared by eval samples';
|
|
1972
|
+
if (node.coverage === 'undeclared')
|
|
1973
|
+
return lang === 'zh' ? '尚未由评测用例声明' : 'not declared by eval samples';
|
|
1974
|
+
return '';
|
|
1975
|
+
}
|
|
1976
|
+
function graphNodeEvidenceLabel(node, lang) {
|
|
1977
|
+
const zh = lang === 'zh';
|
|
1978
|
+
const groupSummary = skillMapGroupSummary(node);
|
|
1979
|
+
if (groupSummary) {
|
|
1980
|
+
return zh
|
|
1981
|
+
? `包含 ${groupSummary.total} 个结构节点,其中 ${groupSummary.declared} 个已有评测用例声明。`
|
|
1982
|
+
: `Contains ${groupSummary.total} structure nodes; ${groupSummary.declared} are declared by eval samples.`;
|
|
1983
|
+
}
|
|
1984
|
+
if (node.coveredBySamples?.length) {
|
|
1985
|
+
const samples = node.coveredBySamples.slice(0, 3).join(zh ? '、' : ', ');
|
|
1986
|
+
const more = node.coveredBySamples.length > 3 ? (zh ? ` 等 ${node.coveredBySamples.length} 条用例` : ` and ${node.coveredBySamples.length} samples`) : '';
|
|
1987
|
+
return zh ? `由 ${samples}${more} 声明覆盖` : `Declared by ${samples}${more}`;
|
|
1988
|
+
}
|
|
1989
|
+
if (node.coverage === 'undeclared')
|
|
1990
|
+
return zh ? '暂无评测用例显式声明覆盖' : 'No eval sample explicitly declares this node yet';
|
|
1991
|
+
if (node.status === 'failed')
|
|
1992
|
+
return zh ? '当前测量证据失败' : 'Current measurement evidence failed';
|
|
1993
|
+
if (node.status === 'warning')
|
|
1994
|
+
return zh ? '当前测量证据有告警' : 'Current measurement evidence has warnings';
|
|
1995
|
+
if (node.nodeKind === 'sample')
|
|
1996
|
+
return zh ? '这条用例用于测量当前 Skill' : 'This sample measures the current Skill';
|
|
1997
|
+
if (node.nodeKind === 'assertion')
|
|
1998
|
+
return zh ? '断言用于把用例输出转成可比较证据' : 'Assertion turns sample output into comparable evidence';
|
|
1999
|
+
return '';
|
|
2000
|
+
}
|
|
2001
|
+
function graphNodeActionLabel(node, lang) {
|
|
2002
|
+
const zh = lang === 'zh';
|
|
2003
|
+
if (node.nodeKind === 'knowledge_group') {
|
|
2004
|
+
return zh
|
|
2005
|
+
? '点击分组展开叶子节点;优先检查未声明或失败证据集中的节点。'
|
|
2006
|
+
: 'Open the group to inspect leaves; prioritize undeclared nodes or nodes linked to failures.';
|
|
2007
|
+
}
|
|
2008
|
+
if (node.status === 'failed' && node.nodeKind === 'sample') {
|
|
2009
|
+
return zh ? '优先查看这条失败用例的输出,确认是 Skill 缺知识、流程缺口还是断言过严。' : 'Inspect this failed sample first and decide whether the Skill, workflow, or assertion needs work.';
|
|
2010
|
+
}
|
|
2011
|
+
if (node.status === 'failed' && node.nodeKind === 'assertion') {
|
|
2012
|
+
return zh ? '回到对应用例,检查失败断言期望是否与 Skill 边界一致。' : 'Go back to the sample and verify whether the failed assertion matches the Skill boundary.';
|
|
2013
|
+
}
|
|
2014
|
+
if (node.coverage === 'undeclared') {
|
|
2015
|
+
return zh ? '如果这是关键知识边界,给代表性评测用例补 `sample.covers`;否则可保持未声明。' : 'If this is a key boundary, add sample.covers to a representative eval sample; otherwise leave it undeclared.';
|
|
2016
|
+
}
|
|
2017
|
+
if (node.coveredBySamples?.length) {
|
|
2018
|
+
return zh ? '优先看覆盖它的用例是否失败;失败集中时,回到这条知识边界修 Skill。' : 'Check whether samples covering this node fail; clustered failures point back to this Skill boundary.';
|
|
2019
|
+
}
|
|
2020
|
+
return '';
|
|
2021
|
+
}
|
|
2022
|
+
function graphNodeDetailKindLabel(nodeKind, lang) {
|
|
2023
|
+
const zh = lang === 'zh';
|
|
2024
|
+
const labels = {
|
|
2025
|
+
skill: ['Skill 根节点', 'Skill root'],
|
|
2026
|
+
skill_file: ['Skill 文件', 'Skill file'],
|
|
2027
|
+
frontmatter: ['Frontmatter', 'Frontmatter'],
|
|
2028
|
+
reference: ['引用文档', 'Reference'],
|
|
2029
|
+
script: ['脚本', 'Script'],
|
|
2030
|
+
tool: ['工具', 'Tool'],
|
|
2031
|
+
hard_rule: ['硬规则', 'Hard rule'],
|
|
2032
|
+
workflow: ['流程', 'Workflow'],
|
|
2033
|
+
workflow_node: ['流程节点', 'Workflow node'],
|
|
2034
|
+
sample: ['评测用例', 'Eval sample'],
|
|
2035
|
+
assertion: ['断言', 'Assertion'],
|
|
2036
|
+
eval_result: ['评测结果', 'Eval result'],
|
|
2037
|
+
diagnostic: ['诊断', 'Diagnostic'],
|
|
2038
|
+
more: ['折叠分组', 'Collapsed group'],
|
|
2039
|
+
knowledge_group: ['知识分组', 'Knowledge group'],
|
|
2040
|
+
};
|
|
2041
|
+
const pair = labels[nodeKind] ?? [nodeKind, nodeKind];
|
|
2042
|
+
return zh ? pair[0] : pair[1];
|
|
2043
|
+
}
|
|
2044
|
+
function graphNodeStatusLabel(status, lang) {
|
|
2045
|
+
if (!status)
|
|
2046
|
+
return '';
|
|
2047
|
+
const zh = lang === 'zh';
|
|
2048
|
+
const labels = {
|
|
2049
|
+
ok: ['正常', 'ok'],
|
|
2050
|
+
warning: ['告警', 'warning'],
|
|
2051
|
+
failed: ['失败', 'failed'],
|
|
2052
|
+
skipped: ['已跳过', 'skipped'],
|
|
2053
|
+
unknown: ['未知', 'unknown'],
|
|
2054
|
+
not_measured: ['未测量', 'not measured'],
|
|
2055
|
+
};
|
|
2056
|
+
const pair = labels[status];
|
|
2057
|
+
if (!pair)
|
|
2058
|
+
return '';
|
|
2059
|
+
return zh ? pair[0] : pair[1];
|
|
2060
|
+
}
|
|
2061
|
+
function skillMapLayerLabel(layer, lang) {
|
|
2062
|
+
if (layer === 'definition')
|
|
2063
|
+
return lang === 'zh' ? '结构定义' : 'Definition';
|
|
2064
|
+
return lang === 'zh' ? '评测测量' : 'Measurement';
|
|
2065
|
+
}
|
|
2066
|
+
function skillMapGroupSummary(node) {
|
|
2067
|
+
return node.groupSummary;
|
|
2068
|
+
}
|
|
2069
|
+
function skillMapGroupDescription(node) {
|
|
2070
|
+
return node.groupDescription ?? '';
|
|
2071
|
+
}
|
|
2072
|
+
function graphNodeDetailSource(node, lang) {
|
|
2073
|
+
const zh = lang === 'zh';
|
|
2074
|
+
switch (node.nodeKind) {
|
|
2075
|
+
case 'knowledge_group':
|
|
2076
|
+
return skillMapGroupDescription(node);
|
|
2077
|
+
case 'reference':
|
|
2078
|
+
case 'script':
|
|
2079
|
+
return node.label;
|
|
2080
|
+
case 'workflow':
|
|
2081
|
+
return `${zh ? 'workflow id' : 'workflow id'}: ${node.label}`;
|
|
2082
|
+
case 'workflow_node':
|
|
2083
|
+
return `${zh ? 'workflow node' : 'workflow node'}: ${node.label}`;
|
|
2084
|
+
case 'hard_rule':
|
|
2085
|
+
return `${zh ? 'rule id' : 'rule id'}: ${node.label}`;
|
|
2086
|
+
case 'sample':
|
|
2087
|
+
return `${zh ? 'sample_id' : 'sample_id'}: ${node.label}`;
|
|
2088
|
+
case 'assertion':
|
|
2089
|
+
case 'diagnostic':
|
|
2090
|
+
case 'eval_result':
|
|
2091
|
+
case 'tool':
|
|
2092
|
+
case 'frontmatter':
|
|
2093
|
+
return node.label;
|
|
2094
|
+
default:
|
|
2095
|
+
return '';
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
function attr(name, value) {
|
|
2099
|
+
return value ? ` ${name}="${e(value)}"` : '';
|
|
2100
|
+
}
|
|
2101
|
+
function renderSkillMapDetailAttrs(item, lang) {
|
|
2102
|
+
const coverageLabel = graphNodeCoverageLabel(item.node, lang);
|
|
2103
|
+
const isMore = item.node.nodeKind === 'more';
|
|
2104
|
+
const isGroup = item.node.nodeKind === 'knowledge_group';
|
|
2105
|
+
const groupSummary = skillMapGroupSummary(item.node);
|
|
2106
|
+
const scope = skillMapLayerLabel(item.layer, lang);
|
|
2107
|
+
const detailTitle = item.presentation?.detailTitle ?? item.presentation?.title;
|
|
2108
|
+
const status = isMore
|
|
2109
|
+
? (lang === 'zh' ? '已收起' : 'collapsed')
|
|
2110
|
+
: isGroup && groupSummary
|
|
2111
|
+
? (lang === 'zh' ? `${groupSummary.total} 个节点` : `${groupSummary.total} nodes`)
|
|
2112
|
+
: graphNodeStatusLabel(item.node.status, lang);
|
|
2113
|
+
return [
|
|
2114
|
+
attr('data-sm-detail-kind', graphNodeDetailKindLabel(item.node.nodeKind, lang)),
|
|
2115
|
+
attr('data-sm-detail-title', isMore ? item.node.label : detailTitle ?? graphNodeDisplayLabel(item.node, lang)),
|
|
2116
|
+
attr('data-sm-detail-subtitle', isMore ? scope : graphNodeDetailSource(item.node, lang)),
|
|
2117
|
+
attr('data-sm-detail-scope', scope),
|
|
2118
|
+
attr('data-sm-detail-source', graphNodeDetailSource(item.node, lang)),
|
|
2119
|
+
attr('data-sm-detail-coverage', coverageLabel),
|
|
2120
|
+
attr('data-sm-detail-status', status),
|
|
2121
|
+
attr('data-sm-detail-evidence', graphNodeEvidenceLabel(item.node, lang)),
|
|
2122
|
+
attr('data-sm-detail-action', graphNodeActionLabel(item.node, lang)),
|
|
2123
|
+
...(item.moreToggle ? [
|
|
2124
|
+
attr('data-sm-detail-collapsed-status', lang === 'zh' ? '已收起' : 'collapsed'),
|
|
2125
|
+
attr('data-sm-detail-expanded-status', lang === 'zh' ? '已展开' : 'expanded'),
|
|
2126
|
+
] : []),
|
|
2127
|
+
].join('');
|
|
2128
|
+
}
|
|
2129
|
+
function graphNodeSignalLabel(node, lang) {
|
|
2130
|
+
const zh = lang === 'zh';
|
|
2131
|
+
const groupSummary = skillMapGroupSummary(node);
|
|
2132
|
+
if (groupSummary)
|
|
2133
|
+
return zh ? `${groupSummary.declared}/${groupSummary.total} 已声明` : `${groupSummary.declared}/${groupSummary.total} declared`;
|
|
2134
|
+
if (node.status === 'failed')
|
|
2135
|
+
return zh ? '失败证据' : 'failed';
|
|
2136
|
+
if (node.status === 'warning')
|
|
2137
|
+
return zh ? '告警' : 'warning';
|
|
2138
|
+
if (node.coverage === 'declared')
|
|
2139
|
+
return zh ? '已声明覆盖' : 'declared';
|
|
2140
|
+
if (node.coverage === 'undeclared')
|
|
2141
|
+
return zh ? '未声明' : 'undeclared';
|
|
2142
|
+
return '';
|
|
2143
|
+
}
|
|
2144
|
+
function graphNodeSignalClass(node) {
|
|
2145
|
+
const groupSummary = skillMapGroupSummary(node);
|
|
2146
|
+
if (groupSummary)
|
|
2147
|
+
return groupSummary.declared === groupSummary.total ? ' sm-node-signal--declared' : ' sm-node-signal--undeclared';
|
|
2148
|
+
if (node.status === 'failed')
|
|
2149
|
+
return ' sm-node-signal--failed';
|
|
2150
|
+
if (node.status === 'warning')
|
|
2151
|
+
return ' sm-node-signal--warning';
|
|
2152
|
+
if (node.coverage === 'declared')
|
|
2153
|
+
return ' sm-node-signal--declared';
|
|
2154
|
+
if (node.coverage === 'undeclared')
|
|
2155
|
+
return ' sm-node-signal--undeclared';
|
|
2156
|
+
return '';
|
|
2157
|
+
}
|
|
2158
|
+
function skillMapRootDetail(entry, coverageSummary, lang) {
|
|
2159
|
+
const zh = lang === 'zh';
|
|
2160
|
+
const failedSamples = entry.eval?.failCount ?? 0;
|
|
2161
|
+
return {
|
|
2162
|
+
kindLabel: graphNodeDetailKindLabel('skill', lang),
|
|
2163
|
+
title: 'SKILL.md',
|
|
2164
|
+
subtitle: entry.skillName,
|
|
2165
|
+
scope: zh ? '中心节点' : 'Root',
|
|
2166
|
+
source: 'SKILL.md',
|
|
2167
|
+
coverage: coverageSummary ?? '',
|
|
2168
|
+
status: [
|
|
2169
|
+
`doctor ${stageText(entry, 'doctor', lang)}`,
|
|
2170
|
+
`eval ${stageText(entry, 'eval', lang)}`,
|
|
2171
|
+
`observe ${stageText(entry, 'observe', lang)}`,
|
|
2172
|
+
].join(' · '),
|
|
2173
|
+
evidence: coverageSummary ?? (zh ? '尚未形成声明锚点' : 'No declared anchors yet'),
|
|
2174
|
+
action: failedSamples > 0
|
|
2175
|
+
? (zh ? `先处理 ${failedSamples} 条失败评测用例,再回看其覆盖的知识边界。` : `Start with ${failedSamples} failed eval samples, then inspect the covered Skill boundaries.`)
|
|
2176
|
+
: (zh ? '继续补充关键场景的 sample.covers,让图谱从结构图变成证据图。' : 'Keep adding sample.covers for key scenarios so the map becomes evidence-oriented.'),
|
|
2177
|
+
};
|
|
2178
|
+
}
|
|
2179
|
+
function renderSkillMapRootDetailAttrs(entry, coverageSummary, lang) {
|
|
2180
|
+
const detail = skillMapRootDetail(entry, coverageSummary, lang);
|
|
2181
|
+
return [
|
|
2182
|
+
attr('data-sm-detail-kind', detail.kindLabel),
|
|
2183
|
+
attr('data-sm-detail-title', detail.title),
|
|
2184
|
+
attr('data-sm-detail-subtitle', detail.subtitle),
|
|
2185
|
+
attr('data-sm-detail-scope', detail.scope),
|
|
2186
|
+
attr('data-sm-detail-source', detail.source),
|
|
2187
|
+
attr('data-sm-detail-coverage', detail.coverage),
|
|
2188
|
+
attr('data-sm-detail-status', detail.status),
|
|
2189
|
+
attr('data-sm-detail-evidence', detail.evidence),
|
|
2190
|
+
attr('data-sm-detail-action', detail.action),
|
|
2191
|
+
].join('');
|
|
2192
|
+
}
|
|
2193
|
+
function renderSkillMapInspector(entry, coverageSummary, lang) {
|
|
2194
|
+
const zh = lang === 'zh';
|
|
2195
|
+
const detail = skillMapRootDetail(entry, coverageSummary, lang);
|
|
2196
|
+
const row = (key, label, value, required = false) => {
|
|
2197
|
+
const hidden = !value && !required ? ' hidden' : '';
|
|
2198
|
+
return `<div class="sm-detail-row" data-sm-detail-row="${e(key)}"${hidden}>
|
|
2199
|
+
<dt>${e(label)}</dt>
|
|
2200
|
+
<dd data-sm-detail-value="${e(key)}">${e(value || '—')}</dd>
|
|
2201
|
+
</div>`;
|
|
2202
|
+
};
|
|
2203
|
+
return `<div class="sm-detail" data-sm-detail>
|
|
2204
|
+
<div class="sm-detail-head">
|
|
2205
|
+
<div class="sm-detail-kind" data-sm-detail-kind-label>${e(detail.kindLabel)}</div>
|
|
2206
|
+
<div class="sm-detail-title" data-sm-detail-title>${e(detail.title)}</div>
|
|
2207
|
+
<div class="sm-detail-subtitle" data-sm-detail-subtitle>${e(detail.subtitle)}</div>
|
|
2208
|
+
</div>
|
|
2209
|
+
<dl class="sm-detail-list">
|
|
2210
|
+
${row('scope', zh ? '范围' : 'Scope', detail.scope, true)}
|
|
2211
|
+
${row('source', zh ? '来源' : 'Source', detail.source)}
|
|
2212
|
+
${row('coverage', zh ? '声明' : 'Declaration', detail.coverage)}
|
|
2213
|
+
${row('status', zh ? '状态' : 'Status', detail.status)}
|
|
2214
|
+
${row('evidence', zh ? '证据' : 'Evidence', detail.evidence)}
|
|
2215
|
+
${row('action', zh ? '下一步' : 'Next', detail.action)}
|
|
2216
|
+
</dl>
|
|
2217
|
+
</div>`;
|
|
2218
|
+
}
|
|
1656
2219
|
function positionedNodeStyle(node) {
|
|
1657
2220
|
return `left:${node.x}px;top:${node.y}px`;
|
|
1658
2221
|
}
|
|
2222
|
+
function renderSkillMapViewPositionAttrs(item) {
|
|
2223
|
+
const attrs = [];
|
|
2224
|
+
if (item.boundaryPosition) {
|
|
2225
|
+
attrs.push(` data-sm-boundary-x="${item.boundaryPosition.x}" data-sm-boundary-y="${item.boundaryPosition.y}"`);
|
|
2226
|
+
}
|
|
2227
|
+
if (item.evidencePosition) {
|
|
2228
|
+
attrs.push(` data-sm-evidence-x="${item.evidencePosition.x}" data-sm-evidence-y="${item.evidencePosition.y}"`);
|
|
2229
|
+
}
|
|
2230
|
+
return attrs.join('');
|
|
2231
|
+
}
|
|
1659
2232
|
function renderSkillMapNode(item, lang) {
|
|
1660
|
-
const title = graphNodeMapLabel(item.node);
|
|
1661
|
-
const
|
|
1662
|
-
const
|
|
1663
|
-
|
|
2233
|
+
const title = item.presentation?.title ?? graphNodeMapLabel(item.node);
|
|
2234
|
+
const coverageLabel = graphNodeCoverageLabel(item.node, lang);
|
|
2235
|
+
const fullTitle = [
|
|
2236
|
+
item.presentation?.title,
|
|
2237
|
+
graphNodeDisplayLabel(item.node, lang),
|
|
2238
|
+
coverageLabel,
|
|
2239
|
+
].filter((value, index, values) => Boolean(value) && values.indexOf(value) === index).join(' · ');
|
|
2240
|
+
const signal = graphNodeSignalLabel(item.node, lang);
|
|
2241
|
+
const kindLabel = item.presentation?.hideKind || item.node.nodeKind === 'knowledge_group' ? '' : nodeKindLabel(item.node.nodeKind, lang);
|
|
2242
|
+
const kindClass = item.node.nodeKind === 'more' ? ' sm-node--more' : item.node.nodeKind === 'knowledge_group' ? ' sm-node--group' : '';
|
|
2243
|
+
const isLeaf = item.isLeaf !== false;
|
|
2244
|
+
const leaf = isLeaf ? ' data-sm-leaf="1"' : '';
|
|
1664
2245
|
const draggable = item.layer === 'definition' || item.layer === 'measurement' ? ' data-sm-draggable="1"' : '';
|
|
1665
2246
|
const overflow = item.overflowGroup ? ` data-sm-overflow-group="${item.overflowGroup}" hidden aria-hidden="true"` : '';
|
|
2247
|
+
const toggleIcon = item.moreToggle ? '<span class="sm-node-toggle-icon" aria-hidden="true"></span>' : '';
|
|
1666
2248
|
const moreToggle = item.moreToggle
|
|
1667
2249
|
? ` data-sm-more-toggle="${item.moreToggle.group}" data-sm-collapsed-label="${e(item.moreToggle.collapsedLabel)}" data-sm-expanded-label="${e(item.moreToggle.expandedLabel)}" role="button" tabindex="0" aria-expanded="false"`
|
|
1668
|
-
: '';
|
|
1669
|
-
return `<div class="sm-node sm-node--${item.layer}${kindClass}${graphNodeStatusClass(item.node)}" data-sm-node-id="${e(item.id)}" data-sm-layer="${item.layer}" data-sm-x="${item.x}" data-sm-y="${item.y}" data-sm-origin-x="${item.x}" data-sm-origin-y="${item.y}"${leaf}${draggable}${overflow}${moreToggle} style="${positionedNodeStyle(item)}" title="${e(fullTitle)}">
|
|
1670
|
-
|
|
2250
|
+
: ' role="button" tabindex="0"';
|
|
2251
|
+
return `<div class="sm-node sm-node--${item.layer}${kindClass}${graphNodeStatusClass(item.node)}${graphNodeCoverageClass(item.node)}" data-sm-node-id="${e(item.id)}" data-sm-layer="${item.layer}" data-sm-x="${item.x}" data-sm-y="${item.y}" data-sm-origin-x="${item.x}" data-sm-origin-y="${item.y}"${renderSkillMapViewPositionAttrs(item)}${leaf}${draggable}${overflow}${moreToggle}${renderSkillMapDetailAttrs(item, lang)} style="${positionedNodeStyle(item)}" title="${e(fullTitle)}" aria-label="${e(fullTitle)}">
|
|
2252
|
+
${toggleIcon}
|
|
2253
|
+
${kindLabel ? `<div class="sm-node-kind">${e(kindLabel)}</div>` : ''}
|
|
1671
2254
|
<div class="sm-node-title">${e(title)}</div>
|
|
2255
|
+
${signal ? `<div class="sm-node-signal${graphNodeSignalClass(item.node)}">${e(signal)}</div>` : ''}
|
|
1672
2256
|
</div>`;
|
|
1673
2257
|
}
|
|
1674
2258
|
function edgePoint(from, to, box) {
|
|
@@ -1687,33 +2271,212 @@ function skillMapPath(from, to, fromBox, toBox) {
|
|
|
1687
2271
|
const c2x = end.x - (end.x >= start.x ? dx * 0.45 : -dx * 0.45);
|
|
1688
2272
|
return `M ${start.x} ${start.y} C ${c1x} ${start.y}, ${c2x} ${end.y}, ${end.x} ${end.y}`;
|
|
1689
2273
|
}
|
|
1690
|
-
function renderSkillMapEdges(nodes, dimensions) {
|
|
2274
|
+
function renderSkillMapEdges(nodes, dimensions, evidenceLinks) {
|
|
1691
2275
|
const rootEdges = nodes
|
|
1692
2276
|
.map((node) => {
|
|
1693
|
-
const from = node.edgeFrom ??
|
|
2277
|
+
const from = node.edgeFrom ?? SKILL_MAP_BOUNDARY_ROOT;
|
|
1694
2278
|
const fromBox = node.edgeFrom ? SKILL_MAP_NODE_SIZE : SKILL_MAP_ROOT_SIZE;
|
|
1695
2279
|
const overflow = node.overflowGroup ? ` data-sm-overflow-group="${node.overflowGroup}" hidden aria-hidden="true"` : '';
|
|
1696
|
-
const fromNodeId = node.overflowGroup ? `${node.overflowGroup}-more` : SKILL_MAP_ROOT_ID;
|
|
1697
|
-
|
|
2280
|
+
const fromNodeId = node.edgeFromId ?? (node.overflowGroup ? `${node.overflowGroup}-more` : SKILL_MAP_ROOT_ID);
|
|
2281
|
+
const leaf = node.isLeaf === false ? '' : ' data-sm-leaf="1"';
|
|
2282
|
+
return `<path class="sm-edge sm-edge--${node.layer}" data-sm-layer="${node.layer}"${leaf} data-sm-edge-to="${e(node.id)}" data-sm-edge-from-node="${e(fromNodeId)}" data-sm-edge-from-x="${from.x}" data-sm-edge-from-y="${from.y}"${overflow} d="${skillMapPath(from, node, fromBox, SKILL_MAP_NODE_SIZE)}"></path>`;
|
|
1698
2283
|
})
|
|
1699
2284
|
.join('');
|
|
1700
|
-
|
|
2285
|
+
const evidenceEdges = evidenceLinks
|
|
2286
|
+
.map((link, index) => `<path class="sm-edge sm-edge--coverage${link.failed ? ' sm-edge--coverage-failed' : ''}" data-sm-layer="measurement" data-sm-leaf="1" data-sm-evidence-edge="1" data-sm-edge-to="${e(link.toId)}" data-sm-edge-from-node="${e(link.fromId)}" data-sm-edge-from-x="${link.from.x}" data-sm-edge-from-y="${link.from.y}" d="${skillMapPath(link.from, link.to, SKILL_MAP_NODE_SIZE, SKILL_MAP_NODE_SIZE)}" data-sm-edge-index="${index}" hidden aria-hidden="true"></path>`)
|
|
2287
|
+
.join('');
|
|
2288
|
+
return `<svg class="sm-svg" viewBox="0 0 ${dimensions.width} ${dimensions.height}" aria-hidden="true">${rootEdges}${evidenceEdges}</svg>`;
|
|
2289
|
+
}
|
|
2290
|
+
function coverageSamplesByStableKey(evalGraph, lang) {
|
|
2291
|
+
const zh = lang === 'zh';
|
|
2292
|
+
const out = new Map();
|
|
2293
|
+
for (const edge of evalGraph?.declaredCoverageEdges ?? []) {
|
|
2294
|
+
const samples = out.get(edge.targetStableKey) ?? [];
|
|
2295
|
+
const sampleLabel = edge.sampleStatus === 'failed'
|
|
2296
|
+
? (zh ? `${edge.sampleLabel}(失败)` : `${edge.sampleLabel} (failed)`)
|
|
2297
|
+
: edge.sampleLabel;
|
|
2298
|
+
if (!samples.includes(sampleLabel))
|
|
2299
|
+
samples.push(sampleLabel);
|
|
2300
|
+
out.set(edge.targetStableKey, samples);
|
|
2301
|
+
}
|
|
2302
|
+
return out;
|
|
2303
|
+
}
|
|
2304
|
+
function skillMapEvidenceLinks(nodes, evalGraph) {
|
|
2305
|
+
if (!evalGraph?.declaredCoverageEdges.length)
|
|
2306
|
+
return [];
|
|
2307
|
+
const targetByStableKey = new Map();
|
|
2308
|
+
const sampleByLabel = new Map();
|
|
2309
|
+
for (const node of nodes) {
|
|
2310
|
+
if (node.node.stableKey)
|
|
2311
|
+
targetByStableKey.set(node.node.stableKey, node);
|
|
2312
|
+
if (node.node.nodeKind === 'sample')
|
|
2313
|
+
sampleByLabel.set(node.node.label, node);
|
|
2314
|
+
}
|
|
2315
|
+
return evalGraph.declaredCoverageEdges
|
|
2316
|
+
.map((edge) => {
|
|
2317
|
+
const from = sampleByLabel.get(edge.sampleLabel);
|
|
2318
|
+
const to = targetByStableKey.get(edge.targetStableKey);
|
|
2319
|
+
if (!from || !to)
|
|
2320
|
+
return null;
|
|
2321
|
+
return {
|
|
2322
|
+
fromId: from.id,
|
|
2323
|
+
toId: to.id,
|
|
2324
|
+
from: { x: from.x, y: from.y },
|
|
2325
|
+
to: { x: to.x, y: to.y },
|
|
2326
|
+
...(edge.sampleStatus === 'failed' ? { failed: true } : {}),
|
|
2327
|
+
};
|
|
2328
|
+
})
|
|
2329
|
+
.filter((link) => Boolean(link));
|
|
1701
2330
|
}
|
|
1702
2331
|
function moreNode(label) {
|
|
1703
2332
|
return { nodeKind: 'more', label };
|
|
1704
2333
|
}
|
|
1705
|
-
function overflowNodePosition(layer, index) {
|
|
1706
|
-
const columns = layer === 'definition'
|
|
1707
|
-
|
|
2334
|
+
function overflowNodePosition(layer, index, view) {
|
|
2335
|
+
const columns = layer === 'definition'
|
|
2336
|
+
? view === 'boundary' ? [205, 382, 560, 738, 915] : [120, 290, 810, 980]
|
|
2337
|
+
: [250, 430, 610, 790, 970];
|
|
2338
|
+
const startY = layer === 'definition'
|
|
2339
|
+
? view === 'boundary' ? 570 : 535
|
|
2340
|
+
: 555;
|
|
1708
2341
|
return {
|
|
1709
2342
|
x: columns[index % columns.length],
|
|
1710
2343
|
y: startY + Math.floor(index / columns.length) * 98,
|
|
1711
2344
|
};
|
|
1712
2345
|
}
|
|
2346
|
+
function definitionGroupSpecForNode(node) {
|
|
2347
|
+
return SKILL_MAP_DEFINITION_GROUPS.find((group) => group.nodeKinds.includes(node.nodeKind));
|
|
2348
|
+
}
|
|
2349
|
+
function definitionGroupLabel(group, lang) {
|
|
2350
|
+
return lang === 'zh' ? group.labelZh : group.labelEn;
|
|
2351
|
+
}
|
|
2352
|
+
function definitionGroupDescription(group, lang) {
|
|
2353
|
+
return lang === 'zh' ? group.descriptionZh : group.descriptionEn;
|
|
2354
|
+
}
|
|
2355
|
+
function knowledgeGroupNode(group, children, lang) {
|
|
2356
|
+
const declared = children.filter((node) => node.coverage === 'declared').length;
|
|
2357
|
+
return {
|
|
2358
|
+
nodeKind: 'knowledge_group',
|
|
2359
|
+
label: definitionGroupLabel(group, lang),
|
|
2360
|
+
groupDescription: definitionGroupDescription(group, lang),
|
|
2361
|
+
groupSummary: {
|
|
2362
|
+
total: children.length,
|
|
2363
|
+
declared,
|
|
2364
|
+
},
|
|
2365
|
+
};
|
|
2366
|
+
}
|
|
2367
|
+
function definitionGroupChildPosition(group, index) {
|
|
2368
|
+
const position = group.childBoundaryPositions[index];
|
|
2369
|
+
if (position)
|
|
2370
|
+
return position;
|
|
2371
|
+
const columns = group.key === 'references'
|
|
2372
|
+
? [120, 290, 460]
|
|
2373
|
+
: group.key === 'workflow'
|
|
2374
|
+
? [660, 830, 1000]
|
|
2375
|
+
: [250, 405, 560, 715, 870];
|
|
2376
|
+
const startY = group.key === 'rules' ? 190 : 500;
|
|
2377
|
+
return {
|
|
2378
|
+
x: columns[index % columns.length],
|
|
2379
|
+
y: startY + Math.floor(index / columns.length) * 92,
|
|
2380
|
+
};
|
|
2381
|
+
}
|
|
2382
|
+
function standaloneAssetPosition(index) {
|
|
2383
|
+
const position = SKILL_MAP_BOUNDARY_ASSET_COORDS[index];
|
|
2384
|
+
if (position)
|
|
2385
|
+
return position;
|
|
2386
|
+
const columns = [205, 380, 560, 740, 915];
|
|
2387
|
+
return {
|
|
2388
|
+
x: columns[index % columns.length],
|
|
2389
|
+
y: 610 + Math.floor(index / columns.length) * 92,
|
|
2390
|
+
};
|
|
2391
|
+
}
|
|
2392
|
+
function standaloneAssetPresentation(node, lang) {
|
|
2393
|
+
const zh = lang === 'zh';
|
|
2394
|
+
if (node.nodeKind === 'script') {
|
|
2395
|
+
return {
|
|
2396
|
+
title: zh ? '执行脚本' : 'Script',
|
|
2397
|
+
detailTitle: zh ? '执行脚本' : 'Script',
|
|
2398
|
+
hideKind: true,
|
|
2399
|
+
};
|
|
2400
|
+
}
|
|
2401
|
+
if (node.nodeKind === 'frontmatter') {
|
|
2402
|
+
return {
|
|
2403
|
+
title: zh ? '元信息' : 'Metadata',
|
|
2404
|
+
detailTitle: zh ? '元信息' : 'Metadata',
|
|
2405
|
+
hideKind: true,
|
|
2406
|
+
};
|
|
2407
|
+
}
|
|
2408
|
+
return undefined;
|
|
2409
|
+
}
|
|
2410
|
+
function buildDefinitionMapNodes(candidates, lang) {
|
|
2411
|
+
let evidenceIndex = 0;
|
|
2412
|
+
const groupNodes = SKILL_MAP_DEFINITION_GROUPS.flatMap((group) => {
|
|
2413
|
+
const children = candidates.filter((node) => definitionGroupSpecForNode(node)?.key === group.key);
|
|
2414
|
+
if (children.length === 0)
|
|
2415
|
+
return [];
|
|
2416
|
+
const groupId = `definition-group-${group.key}`;
|
|
2417
|
+
const label = definitionGroupLabel(group, lang);
|
|
2418
|
+
const groupNode = {
|
|
2419
|
+
id: groupId,
|
|
2420
|
+
node: knowledgeGroupNode(group, children, lang),
|
|
2421
|
+
layer: 'definition',
|
|
2422
|
+
x: group.boundaryPosition.x,
|
|
2423
|
+
y: group.boundaryPosition.y,
|
|
2424
|
+
boundaryPosition: group.boundaryPosition,
|
|
2425
|
+
evidencePosition: group.evidencePosition,
|
|
2426
|
+
isLeaf: false,
|
|
2427
|
+
moreToggle: {
|
|
2428
|
+
group: groupId,
|
|
2429
|
+
collapsedLabel: lang === 'zh' ? `展开${label}` : `Expand ${label}`,
|
|
2430
|
+
expandedLabel: lang === 'zh' ? `收起${label}` : `Collapse ${label}`,
|
|
2431
|
+
},
|
|
2432
|
+
};
|
|
2433
|
+
const childNodes = children.map((node, index) => {
|
|
2434
|
+
const boundaryPosition = definitionGroupChildPosition(group, index);
|
|
2435
|
+
const evidencePosition = SKILL_MAP_EVIDENCE_DEFINITION_COORDS[evidenceIndex] ?? overflowNodePosition('definition', evidenceIndex - SKILL_MAP_EVIDENCE_DEFINITION_COORDS.length, 'evidence');
|
|
2436
|
+
evidenceIndex += 1;
|
|
2437
|
+
return {
|
|
2438
|
+
id: `${groupId}-node-${index}`,
|
|
2439
|
+
node,
|
|
2440
|
+
layer: 'definition',
|
|
2441
|
+
edgeFrom: group.boundaryPosition,
|
|
2442
|
+
edgeFromId: groupId,
|
|
2443
|
+
overflowGroup: groupId,
|
|
2444
|
+
x: boundaryPosition.x,
|
|
2445
|
+
y: boundaryPosition.y,
|
|
2446
|
+
boundaryPosition,
|
|
2447
|
+
evidencePosition,
|
|
2448
|
+
};
|
|
2449
|
+
});
|
|
2450
|
+
return [groupNode, ...childNodes];
|
|
2451
|
+
});
|
|
2452
|
+
const standaloneAssets = candidates
|
|
2453
|
+
.filter((node) => node.nodeKind === 'script' || node.nodeKind === 'frontmatter')
|
|
2454
|
+
.map((node, index) => {
|
|
2455
|
+
const boundaryPosition = standaloneAssetPosition(index);
|
|
2456
|
+
const evidencePosition = SKILL_MAP_EVIDENCE_DEFINITION_COORDS[evidenceIndex] ?? overflowNodePosition('definition', evidenceIndex - SKILL_MAP_EVIDENCE_DEFINITION_COORDS.length, 'evidence');
|
|
2457
|
+
evidenceIndex += 1;
|
|
2458
|
+
return {
|
|
2459
|
+
id: `definition-asset-${index}`,
|
|
2460
|
+
node,
|
|
2461
|
+
layer: 'definition',
|
|
2462
|
+
x: boundaryPosition.x,
|
|
2463
|
+
y: boundaryPosition.y,
|
|
2464
|
+
boundaryPosition,
|
|
2465
|
+
evidencePosition,
|
|
2466
|
+
presentation: standaloneAssetPresentation(node, lang),
|
|
2467
|
+
};
|
|
2468
|
+
});
|
|
2469
|
+
return [...groupNodes, ...standaloneAssets];
|
|
2470
|
+
}
|
|
1713
2471
|
function skillMapDimensions(nodes) {
|
|
2472
|
+
const allPositions = nodes.flatMap((node) => [
|
|
2473
|
+
{ x: node.x, y: node.y },
|
|
2474
|
+
...(node.boundaryPosition ? [node.boundaryPosition] : []),
|
|
2475
|
+
...(node.evidencePosition ? [node.evidencePosition] : []),
|
|
2476
|
+
]);
|
|
1714
2477
|
return {
|
|
1715
|
-
width: Math.max(SKILL_MAP_WIDTH, ...
|
|
1716
|
-
height: Math.max(SKILL_MAP_HEIGHT, ...
|
|
2478
|
+
width: Math.max(SKILL_MAP_WIDTH, ...allPositions.map((node) => node.x + SKILL_MAP_NODE_MARGIN.x)),
|
|
2479
|
+
height: Math.max(SKILL_MAP_HEIGHT, ...allPositions.map((node) => node.y + SKILL_MAP_NODE_MARGIN.y)),
|
|
1717
2480
|
};
|
|
1718
2481
|
}
|
|
1719
2482
|
function renderSkillStageRail(entry, lang) {
|
|
@@ -1732,6 +2495,24 @@ function renderSkillStageRail(entry, lang) {
|
|
|
1732
2495
|
${card('observe', stageText(entry, 'observe', lang), observeStatus)}
|
|
1733
2496
|
</div>`;
|
|
1734
2497
|
}
|
|
2498
|
+
function renderSkillMapEvidenceStrip(entry, doctor, evalGraph, lang) {
|
|
2499
|
+
const zh = lang === 'zh';
|
|
2500
|
+
const failedSamples = entry.eval?.failCount ?? 0;
|
|
2501
|
+
const totalSamples = entry.eval?.totalSamples ?? evalGraph?.samples ?? 0;
|
|
2502
|
+
const declaredKeys = new Set(evalGraph?.declaredCoverageStableKeys ?? []);
|
|
2503
|
+
const coverable = doctor?.definitionNodes.filter((node) => node.stableKey && DEFINITION_PREVIEW_KINDS.has(node.nodeKind)) ?? [];
|
|
2504
|
+
const declaredCount = coverable.filter((node) => node.stableKey && declaredKeys.has(node.stableKey)).length;
|
|
2505
|
+
const undeclaredCount = coverable.filter((node) => node.stableKey && !declaredKeys.has(node.stableKey)).length;
|
|
2506
|
+
const item = (key, value, tone = '') => `<div class="sm-evidence-item${tone ? ` sm-evidence-item--${tone}` : ''}">
|
|
2507
|
+
<div class="sm-evidence-k">${e(key)}</div>
|
|
2508
|
+
<div class="sm-evidence-v">${e(value)}</div>
|
|
2509
|
+
</div>`;
|
|
2510
|
+
return `<div class="sm-evidence-strip" aria-label="${zh ? '图谱诊断摘要' : 'Map diagnosis summary'}">
|
|
2511
|
+
${item(zh ? '边界覆盖' : 'Boundary coverage', coverable.length > 0 ? `${declaredCount}/${coverable.length} ${zh ? '个结构节点' : 'structure nodes'}` : (zh ? '暂无结构节点' : 'no structure nodes'))}
|
|
2512
|
+
${item(zh ? '未声明' : 'Undeclared', coverable.length > 0 ? `${undeclaredCount} ${zh ? '个结构节点' : 'structure nodes'}` : (zh ? '暂无' : 'none'), undeclaredCount > 0 ? 'action' : '')}
|
|
2513
|
+
${item(zh ? '失败证据' : 'Failed evidence', totalSamples > 0 ? `${failedSamples}/${totalSamples} ${zh ? '条评测用例' : 'eval samples'}` : (zh ? '暂无评测用例' : 'no eval samples'), failedSamples > 0 ? 'risk' : '')}
|
|
2514
|
+
</div>`;
|
|
2515
|
+
}
|
|
1735
2516
|
function orderedPreviewNodes(nodes, allowedKinds, priority) {
|
|
1736
2517
|
return (nodes ?? [])
|
|
1737
2518
|
.map((node, index) => ({ node, index }))
|
|
@@ -1739,6 +2520,50 @@ function orderedPreviewNodes(nodes, allowedKinds, priority) {
|
|
|
1739
2520
|
.sort((a, b) => (priority[a.node.nodeKind] ?? 99) - (priority[b.node.nodeKind] ?? 99) || a.index - b.index)
|
|
1740
2521
|
.map(({ node }) => node);
|
|
1741
2522
|
}
|
|
2523
|
+
function measurementStatusPriority(node) {
|
|
2524
|
+
if (node.status === 'failed')
|
|
2525
|
+
return 0;
|
|
2526
|
+
if (node.status === 'warning')
|
|
2527
|
+
return 1;
|
|
2528
|
+
if (node.nodeKind === 'sample')
|
|
2529
|
+
return 2;
|
|
2530
|
+
return 3;
|
|
2531
|
+
}
|
|
2532
|
+
function applyDefinitionCoverageDeclarations(nodes, declaredStableKeys, coveredByStableKey) {
|
|
2533
|
+
if (!nodes)
|
|
2534
|
+
return undefined;
|
|
2535
|
+
if ((!declaredStableKeys || declaredStableKeys.length === 0) && (!coveredByStableKey || coveredByStableKey.size === 0))
|
|
2536
|
+
return nodes;
|
|
2537
|
+
const declared = new Set(declaredStableKeys);
|
|
2538
|
+
return nodes.map((node) => {
|
|
2539
|
+
if (!node.stableKey || !DEFINITION_PREVIEW_KINDS.has(node.nodeKind))
|
|
2540
|
+
return node;
|
|
2541
|
+
const coveredBySamples = coveredByStableKey?.get(node.stableKey) ?? [];
|
|
2542
|
+
return {
|
|
2543
|
+
...node,
|
|
2544
|
+
coverage: declared.has(node.stableKey) ? 'declared' : 'undeclared',
|
|
2545
|
+
...(coveredBySamples.length > 0 ? { coveredBySamples } : {}),
|
|
2546
|
+
};
|
|
2547
|
+
});
|
|
2548
|
+
}
|
|
2549
|
+
function skillMapCoverageDeclarationSummary(doctor, evalGraph, lang) {
|
|
2550
|
+
const declaredKeys = new Set(evalGraph?.declaredCoverageStableKeys ?? []);
|
|
2551
|
+
if (!doctor || declaredKeys.size === 0)
|
|
2552
|
+
return null;
|
|
2553
|
+
const coverable = doctor.definitionNodes.filter((node) => node.stableKey && DEFINITION_PREVIEW_KINDS.has(node.nodeKind));
|
|
2554
|
+
if (coverable.length === 0)
|
|
2555
|
+
return null;
|
|
2556
|
+
const declaredCount = coverable.filter((node) => node.stableKey && declaredKeys.has(node.stableKey)).length;
|
|
2557
|
+
const samples = evalGraph?.samples ?? 0;
|
|
2558
|
+
return lang === 'zh'
|
|
2559
|
+
? `${samples} 条评测用例声明了 ${declaredCount}/${coverable.length} 个结构节点`
|
|
2560
|
+
: `${samples} eval samples declared ${declaredCount}/${coverable.length} structure nodes`;
|
|
2561
|
+
}
|
|
2562
|
+
function skillMapCoverageDeclarationHint(lang) {
|
|
2563
|
+
return lang === 'zh'
|
|
2564
|
+
? '这些关系来自 sample.covers,表示评测用例显式标注的主要触达节点;尚未声明只表示还没有被评测用例显式标注,不应直接视为测试缺口。'
|
|
2565
|
+
: 'These relationships come from sample.covers and show the main nodes explicitly annotated by eval samples; not declared only means not explicitly annotated yet, not necessarily a test gap.';
|
|
2566
|
+
}
|
|
1742
2567
|
function renderSkillEvidenceMarkdown(entry, lang) {
|
|
1743
2568
|
const zh = lang === 'zh';
|
|
1744
2569
|
const graph = entry.graph;
|
|
@@ -1758,7 +2583,8 @@ function renderSkillEvidenceMarkdown(entry, lang) {
|
|
|
1758
2583
|
!entry.eval ? `- ${zh ? '测量效果' : 'Measure impact'}${stepSeparator} \`omk eval --control baseline --treatment ${sourceArg}\`` : '',
|
|
1759
2584
|
!entry.observe ? `- ${zh ? '接入观察' : 'Add observation'}${stepSeparator} \`omk observe ingest <trace-dir>\`` : '',
|
|
1760
2585
|
].filter(Boolean);
|
|
1761
|
-
const
|
|
2586
|
+
const coverageSummary = skillMapCoverageDeclarationSummary(doctor, evalGraph, lang);
|
|
2587
|
+
const definitionPreview = previewNodes(applyDefinitionCoverageDeclarations(doctor?.definitionNodes, evalGraph?.declaredCoverageStableKeys, coverageSamplesByStableKey(evalGraph, lang)), DEFINITION_PREVIEW_KINDS, 8);
|
|
1762
2588
|
const measurementPreview = previewNodes(evalGraph?.measurementNodes, MEASUREMENT_PREVIEW_KINDS, 8);
|
|
1763
2589
|
const previewMermaidLines = [
|
|
1764
2590
|
...definitionPreview.visible.flatMap((node, index) => [
|
|
@@ -1771,7 +2597,7 @@ function renderSkillEvidenceMarkdown(entry, lang) {
|
|
|
1771
2597
|
]),
|
|
1772
2598
|
];
|
|
1773
2599
|
return [
|
|
1774
|
-
`## Skill
|
|
2600
|
+
`## ${zh ? '知识图谱摘要' : 'Skill Map Summary'}${zh ? ':' : ': '}${entry.skillName}`,
|
|
1775
2601
|
'',
|
|
1776
2602
|
summary,
|
|
1777
2603
|
'',
|
|
@@ -1793,6 +2619,7 @@ function renderSkillEvidenceMarkdown(entry, lang) {
|
|
|
1793
2619
|
`| doctor | ${doctorStatus} | ${doctor ? `${doctor.nodeCount} nodes / ${doctor.edgeCount} edges` : (zh ? '无 graph' : 'no graph')} |`,
|
|
1794
2620
|
`| eval | ${evalStatus} | ${evalGraph ? `${zh ? 'variant 子图' : 'variant subgraph'}: ${evalGraph.nodeCount} nodes / ${evalGraph.edgeCount} edges` : (zh ? '无 graph' : 'no graph')} |`,
|
|
1795
2621
|
`| observe | ${observeStatus} | ${zh ? '未接 production graph' : 'production graph not connected'} |`,
|
|
2622
|
+
...(coverageSummary ? ['', zh ? '### 声明锚点' : '### Declared Anchors', '', coverageSummary, '', skillMapCoverageDeclarationHint(lang)] : []),
|
|
1796
2623
|
'',
|
|
1797
2624
|
zh ? '### 关键计数' : '### Key Counts',
|
|
1798
2625
|
'',
|
|
@@ -1820,7 +2647,10 @@ function renderSkillMapSection(entry, lang) {
|
|
|
1820
2647
|
const evalGraph = graph.eval;
|
|
1821
2648
|
const band = skillMapBand(entry);
|
|
1822
2649
|
const markdown = renderSkillEvidenceMarkdown(entry, lang);
|
|
1823
|
-
const
|
|
2650
|
+
const coverageSamples = coverageSamplesByStableKey(evalGraph, lang);
|
|
2651
|
+
const definitionNodesWithCoverageDeclarations = applyDefinitionCoverageDeclarations(doctor?.definitionNodes, evalGraph?.declaredCoverageStableKeys, coverageSamples);
|
|
2652
|
+
const coverageSummary = skillMapCoverageDeclarationSummary(doctor, evalGraph, lang);
|
|
2653
|
+
const definitionCandidates = orderedPreviewNodes(definitionNodesWithCoverageDeclarations, DEFINITION_PREVIEW_KINDS, {
|
|
1824
2654
|
reference: 1,
|
|
1825
2655
|
script: 2,
|
|
1826
2656
|
workflow: 3,
|
|
@@ -1832,53 +2662,27 @@ function renderSkillMapSection(entry, lang) {
|
|
|
1832
2662
|
sample: 1,
|
|
1833
2663
|
diagnostic: 2,
|
|
1834
2664
|
assertion: 3,
|
|
1835
|
-
});
|
|
1836
|
-
const
|
|
1837
|
-
const measurementLimit = SKILL_MAP_MEASUREMENT_COORDS.length - 1;
|
|
1838
|
-
const definitionVisible = definitionCandidates.slice(0, definitionLimit);
|
|
1839
|
-
const definitionOverflow = definitionCandidates.slice(definitionLimit);
|
|
2665
|
+
}).sort((a, b) => measurementStatusPriority(a) - measurementStatusPriority(b));
|
|
2666
|
+
const measurementLimit = SKILL_MAP_EVIDENCE_MEASUREMENT_COORDS.length - 1;
|
|
1840
2667
|
const measurementVisible = measurementCandidates.slice(0, measurementLimit);
|
|
1841
2668
|
const measurementOverflow = measurementCandidates.slice(measurementLimit);
|
|
1842
|
-
const
|
|
1843
|
-
const
|
|
1844
|
-
const definitionNodes = [
|
|
1845
|
-
...definitionVisible.map((node, index) => ({
|
|
1846
|
-
id: `definition-${index}`,
|
|
1847
|
-
node,
|
|
1848
|
-
layer: 'definition',
|
|
1849
|
-
...SKILL_MAP_DEFINITION_COORDS[index],
|
|
1850
|
-
})),
|
|
1851
|
-
...(definitionOverflow.length > 0 ? [{
|
|
1852
|
-
id: 'definition-more',
|
|
1853
|
-
node: moreNode(`+ ${definitionOverflow.length} ${zh ? '更多结构节点' : 'more structure nodes'}`),
|
|
1854
|
-
layer: 'definition',
|
|
1855
|
-
moreToggle: {
|
|
1856
|
-
group: 'definition',
|
|
1857
|
-
collapsedLabel: `+ ${definitionOverflow.length} ${zh ? '更多结构节点' : 'more structure nodes'}`,
|
|
1858
|
-
expandedLabel: zh ? '收起结构节点' : 'collapse structure nodes',
|
|
1859
|
-
},
|
|
1860
|
-
...definitionMorePosition,
|
|
1861
|
-
}] : []),
|
|
1862
|
-
...definitionOverflow.map((node, index) => ({
|
|
1863
|
-
id: `definition-overflow-${index}`,
|
|
1864
|
-
node,
|
|
1865
|
-
layer: 'definition',
|
|
1866
|
-
edgeFrom: definitionMorePosition,
|
|
1867
|
-
overflowGroup: 'definition',
|
|
1868
|
-
...overflowNodePosition('definition', index),
|
|
1869
|
-
})),
|
|
1870
|
-
];
|
|
2669
|
+
const measurementMorePosition = SKILL_MAP_EVIDENCE_MEASUREMENT_COORDS[SKILL_MAP_EVIDENCE_MEASUREMENT_COORDS.length - 1];
|
|
2670
|
+
const definitionNodes = buildDefinitionMapNodes(definitionCandidates, lang);
|
|
1871
2671
|
const measurementNodes = [
|
|
1872
2672
|
...measurementVisible.map((node, index) => ({
|
|
1873
2673
|
id: `measurement-${index}`,
|
|
1874
2674
|
node,
|
|
1875
2675
|
layer: 'measurement',
|
|
1876
|
-
...
|
|
2676
|
+
...SKILL_MAP_EVIDENCE_MEASUREMENT_COORDS[index],
|
|
2677
|
+
boundaryPosition: SKILL_MAP_EVIDENCE_MEASUREMENT_COORDS[index],
|
|
2678
|
+
evidencePosition: SKILL_MAP_EVIDENCE_MEASUREMENT_COORDS[index],
|
|
1877
2679
|
})),
|
|
1878
2680
|
...(measurementOverflow.length > 0 ? [{
|
|
1879
2681
|
id: 'measurement-more',
|
|
1880
2682
|
node: moreNode(`+ ${measurementOverflow.length} ${zh ? '更多测量节点' : 'more measurement nodes'}`),
|
|
1881
2683
|
layer: 'measurement',
|
|
2684
|
+
boundaryPosition: measurementMorePosition,
|
|
2685
|
+
evidencePosition: measurementMorePosition,
|
|
1882
2686
|
moreToggle: {
|
|
1883
2687
|
group: 'measurement',
|
|
1884
2688
|
collapsedLabel: `+ ${measurementOverflow.length} ${zh ? '更多测量节点' : 'more measurement nodes'}`,
|
|
@@ -1892,18 +2696,23 @@ function renderSkillMapSection(entry, lang) {
|
|
|
1892
2696
|
layer: 'measurement',
|
|
1893
2697
|
edgeFrom: measurementMorePosition,
|
|
1894
2698
|
overflowGroup: 'measurement',
|
|
1895
|
-
...overflowNodePosition('measurement', index),
|
|
2699
|
+
...overflowNodePosition('measurement', index, 'evidence'),
|
|
2700
|
+
boundaryPosition: overflowNodePosition('measurement', index, 'evidence'),
|
|
2701
|
+
evidencePosition: overflowNodePosition('measurement', index, 'evidence'),
|
|
1896
2702
|
})),
|
|
1897
2703
|
];
|
|
1898
2704
|
const mapNodes = [...definitionNodes, ...measurementNodes];
|
|
2705
|
+
const evidenceLinks = skillMapEvidenceLinks(mapNodes, evalGraph);
|
|
1899
2706
|
const mapDimensions = skillMapDimensions(mapNodes);
|
|
2707
|
+
const topMeta = '';
|
|
1900
2708
|
return `<section id="section-skill-map" class="si-sect si-sect--${band} sm-sect">
|
|
1901
2709
|
<div class="si-sect-h">
|
|
1902
2710
|
<span class="si-sect-title">🗺 ${zh ? 'Skill Map' : 'Skill Map'}</span>
|
|
1903
|
-
|
|
2711
|
+
${topMeta ? `<span class="si-sect-meta">${e(topMeta)}</span>` : ''}
|
|
1904
2712
|
</div>
|
|
1905
|
-
<div class="sm-body" data-sm-map data-sm-base-width="${mapDimensions.width}" data-sm-base-height="${mapDimensions.height}" data-sm-root-x="${
|
|
2713
|
+
<div class="sm-body" data-sm-map data-sm-view="boundary" data-sm-base-width="${mapDimensions.width}" data-sm-base-height="${mapDimensions.height}" data-sm-root-x="${SKILL_MAP_BOUNDARY_ROOT.x}" data-sm-root-y="${SKILL_MAP_BOUNDARY_ROOT.y}">
|
|
1906
2714
|
${renderSkillStageRail(entry, lang)}
|
|
2715
|
+
${renderSkillMapEvidenceStrip(entry, doctor, evalGraph, lang)}
|
|
1907
2716
|
<div class="sm-toolbar" aria-label="${zh ? 'Skill Map 控制' : 'Skill Map controls'}">
|
|
1908
2717
|
<div class="sm-tools">
|
|
1909
2718
|
<button class="sm-tool" type="button" data-sm-action="zoom-out" title="${zh ? '缩小' : 'Zoom out'}">−</button>
|
|
@@ -1912,16 +2721,20 @@ function renderSkillMapSection(entry, lang) {
|
|
|
1912
2721
|
<button class="sm-tool" type="button" data-sm-action="reset" title="${zh ? '重置视图' : 'Reset view'}">${zh ? '重置' : 'Reset'}</button>
|
|
1913
2722
|
</div>
|
|
1914
2723
|
<div class="sm-toggles">
|
|
1915
|
-
<
|
|
1916
|
-
|
|
2724
|
+
<div class="sm-view-switch" role="group" aria-label="${zh ? '图谱视图' : 'Map view'}">
|
|
2725
|
+
<button class="sm-view is-active" type="button" data-sm-view-button="boundary" aria-pressed="true">${zh ? '边界图' : 'Boundary'}</button>
|
|
2726
|
+
<button class="sm-view" type="button" data-sm-view-button="evidence" aria-pressed="false">${zh ? '证据视图' : 'Evidence'}</button>
|
|
2727
|
+
</div>
|
|
1917
2728
|
<label class="sm-toggle"><input type="checkbox" data-sm-toggle-leaves checked> ${zh ? '叶子节点' : 'Leaf nodes'}</label>
|
|
1918
2729
|
</div>
|
|
1919
2730
|
</div>
|
|
1920
2731
|
<div class="sm-canvas" data-sm-viewport aria-label="Skill Map">
|
|
1921
2732
|
<div class="sm-graph-stage" data-sm-graph-stage style="width:${mapDimensions.width}px;height:${mapDimensions.height}px">
|
|
1922
2733
|
<div class="sm-graph" data-sm-graph style="width:${mapDimensions.width}px;height:${mapDimensions.height}px">
|
|
1923
|
-
|
|
1924
|
-
<div class="sm-
|
|
2734
|
+
<div class="sm-zone-label sm-zone-label--definition" data-sm-layer="definition">${zh ? '知识边界' : 'Knowledge'}</div>
|
|
2735
|
+
<div class="sm-zone-label sm-zone-label--measurement" data-sm-layer="measurement">${zh ? '评测证据' : 'Evidence'}</div>
|
|
2736
|
+
${renderSkillMapEdges(mapNodes, mapDimensions, evidenceLinks)}
|
|
2737
|
+
<div class="sm-node sm-node--skill" data-sm-root data-sm-node-id="${SKILL_MAP_ROOT_ID}" data-sm-x="${SKILL_MAP_BOUNDARY_ROOT.x}" data-sm-y="${SKILL_MAP_BOUNDARY_ROOT.y}" data-sm-origin-x="${SKILL_MAP_BOUNDARY_ROOT.x}" data-sm-origin-y="${SKILL_MAP_BOUNDARY_ROOT.y}" data-sm-boundary-x="${SKILL_MAP_BOUNDARY_ROOT.x}" data-sm-boundary-y="${SKILL_MAP_BOUNDARY_ROOT.y}" data-sm-evidence-x="${SKILL_MAP_EVIDENCE_ROOT.x}" data-sm-evidence-y="${SKILL_MAP_EVIDENCE_ROOT.y}" data-sm-draggable="1"${renderSkillMapRootDetailAttrs(entry, coverageSummary, lang)} role="button" tabindex="0" aria-label="SKILL.md ${e(entry.skillName)}" style="${positionedNodeStyle(SKILL_MAP_BOUNDARY_ROOT)}">
|
|
1925
2738
|
<div class="sm-node-title">SKILL.md</div>
|
|
1926
2739
|
<div class="sm-node-meta">${e(entry.skillName)}</div>
|
|
1927
2740
|
</div>
|
|
@@ -1929,8 +2742,9 @@ function renderSkillMapSection(entry, lang) {
|
|
|
1929
2742
|
</div>
|
|
1930
2743
|
</div>
|
|
1931
2744
|
</div>
|
|
2745
|
+
${renderSkillMapInspector(entry, coverageSummary, lang)}
|
|
1932
2746
|
<details class="sm-card">
|
|
1933
|
-
<summary>${zh ? '
|
|
2747
|
+
<summary>${zh ? '复制图谱摘要' : 'Copy Skill Map Summary'}</summary>
|
|
1934
2748
|
<textarea readonly>${e(markdown)}</textarea>
|
|
1935
2749
|
</details>
|
|
1936
2750
|
</div>
|