sdtk-wiki-kit 0.2.1 → 0.3.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.
@@ -359,6 +359,7 @@ body.nav-open .page-nav-toggle{display:none}
359
359
  #atlas-ask-model-select{flex:0 0 136px;width:136px}
360
360
  #atlas-ask-mode-select{flex:1 1 140px;width:140px}
361
361
  #atlas-ask-info-toggle{flex:0 0 36px}
362
+ #atlas-ask-path-filter{padding:0 12px;background-image:none;flex:1 1 120px}
362
363
  .atlas-ask-send{display:inline-flex;align-items:center;justify-content:center;flex:0 0 42px;width:42px;height:42px;border-radius:999px;border:0;background:linear-gradient(135deg,#4f78ff 0%,#3156c9 100%);color:#ffffff;cursor:pointer;font-size:20px;font-weight:900;box-shadow:0 14px 24px rgba(49,86,201,0.26)}
363
364
  .atlas-ask-send:hover{filter:brightness(1.03)}
364
365
  .atlas-ask-send:disabled{opacity:.5;cursor:not-allowed}
@@ -407,6 +408,47 @@ body.nav-open .page-nav-toggle{display:none}
407
408
  .atlas-ask-history-item strong{font-size:12px;color:#24344d}
408
409
  .atlas-ask-history-meta{font-size:11px;color:#6d7d95;line-height:1.4}
409
410
  .atlas-ask-history-preview{font-size:12px;color:#52606f;line-height:1.45}
411
+ .ask-scope-wrap{position:relative;flex:1 1 150px;min-width:0}
412
+ .ask-scope-btn{display:flex;align-items:center;gap:8px;width:100%;height:36px;padding:0 12px;border-radius:999px;border:1px solid #d9cfbf;background:#fff;color:#24344d;font-size:12px;font-weight:700;cursor:pointer;box-shadow:0 8px 18px rgba(15,23,42,0.04);text-align:left}
413
+ .ask-scope-btn:hover{border-color:#aebfe7;background:#f6f8ff}
414
+ .ask-scope-btn .scope-ico{flex:0 0 auto}
415
+ .ask-scope-btn .scope-label{flex:1 1 auto;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
416
+ .ask-scope-btn .scope-caret{flex:0 0 auto;color:#7d8fa8;font-size:9px}
417
+ .ask-scope-menu{display:none;position:absolute;left:0;bottom:calc(100% + 8px);z-index:5;flex-direction:column;gap:2px;padding:8px;min-width:248px;max-width:320px;background:rgba(255,255,255,0.99);border:1px solid #d9cfbf;border-radius:18px;box-shadow:0 18px 30px rgba(94,83,63,0.18);max-height:340px;overflow:auto}
418
+ .ask-scope-wrap.open .ask-scope-menu{display:flex}
419
+ .ask-scope-opt{display:flex;align-items:center;gap:10px;padding:9px 10px;border-radius:12px;cursor:pointer;color:#24344d}
420
+ .ask-scope-opt:hover{background:#f7f9ff}
421
+ .ask-scope-opt.active{background:#eef3ff;border:1px solid #cad7ff;padding:8px 9px}
422
+ .ask-scope-opt.disabled{opacity:.45;cursor:not-allowed;pointer-events:none}
423
+ .ask-scope-opt .opt-ico{flex:0 0 18px;text-align:center}
424
+ .ask-scope-opt .opt-copy{display:flex;align-items:center;min-width:0;flex:1}
425
+ .ask-scope-opt .opt-copy strong{font-size:12.5px;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
426
+ .ask-scope-opt .opt-check{flex:0 0 auto;color:#3156c9;font-weight:900;visibility:hidden}
427
+ .ask-scope-opt.active .opt-check{visibility:visible}
428
+ .ask-scope-sep{height:1px;background:#ece4d8;margin:4px 2px}
429
+ .ask-folder-block{display:none;flex-direction:column;gap:6px;padding:4px 4px 6px;margin:2px 0 0;border-top:1px dashed #e6ded1}
430
+ .ask-folder-block.open{display:flex}
431
+ .ask-folder-filter{height:32px;border-radius:999px;border:1px solid #d9cfbf;background:#fff;padding:0 12px;font:inherit;font-size:12px;color:#24344d}
432
+ .ask-folder-filter:focus{outline:none;border-color:#5f89ff;box-shadow:0 0 0 3px rgba(95,137,255,0.12)}
433
+ .ask-folder-list{display:flex;flex-direction:column;gap:1px;max-height:170px;overflow:auto}
434
+ .ask-folder-item{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:7px 10px;border-radius:10px;cursor:pointer;font-size:12px}
435
+ .ask-folder-item:hover{background:#f7f9ff}
436
+ .ask-folder-item.active{background:#eef3ff}
437
+ .ask-folder-item .f-path{font-family:'SF Mono',Consolas,monospace;color:#354153;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
438
+ .ask-folder-item .f-count{flex:0 0 auto;font-size:11px;font-weight:800;color:#6d7d95;background:#f3efe7;border:1px solid #e6ded1;border-radius:999px;padding:1px 8px}
439
+ .ask-sources{margin-top:2px;border:1px solid #e6ded1;border-radius:14px;background:rgba(255,255,255,0.9);overflow:hidden}
440
+ .ask-sources-head{display:flex;align-items:center;gap:8px;width:100%;padding:10px 12px;background:none;border:0;cursor:pointer;color:#52606f;font-size:12px;font-weight:700;text-align:left}
441
+ .ask-sources-head:hover{background:#f7f9ff}
442
+ .ask-sources-caret{transition:transform .15s ease;color:#7d8fa8;font-size:10px}
443
+ .ask-sources.open .ask-sources-caret{transform:rotate(90deg)}
444
+ .ask-sources-badge{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:18px;padding:0 6px;border-radius:999px;background:#3156c9;color:#fff;font-size:11px;font-weight:800}
445
+ .ask-sources-body{display:none;flex-direction:column;gap:6px;padding:4px 12px 12px}
446
+ .ask-sources.open .ask-sources-body{display:flex}
447
+ .ask-source-row{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:7px 10px;border:1px solid #ece4d8;border-radius:10px;background:#fff}
448
+ .ask-source-row .s-path{font-family:'SF Mono',Consolas,monospace;font-size:11.5px;color:#354153;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
449
+ .s-chip{flex:0 0 auto;font-size:10px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;border-radius:999px;padding:2px 8px}
450
+ .s-chip.primary{color:#2f8f5b;background:#e7f6ee;border:1px solid #bfe6cf}
451
+ .s-chip.related{color:#3156c9;background:#eef3ff;border:1px solid #cad7ff}
410
452
  @media (max-width: 1180px){
411
453
  .atlas-ask-dock{position:relative;left:auto;top:auto;bottom:auto;width:100%;max-width:none;margin-top:14px}
412
454
  .atlas-ask-dock:not(.open){width:100%}
@@ -482,6 +524,47 @@ body.nav-open .page-nav-toggle{display:none}
482
524
  .graph-toolbar-overlay,.graph-toolbar-overlay.compact,.graph-toolbar-overlay.minimized{position:relative;top:auto;left:auto;width:100%;max-width:none;margin-bottom:14px}
483
525
  .graph-focus-overlay,.graph-focus-overlay.minimized{position:relative;top:auto;right:auto;bottom:auto;width:100%;height:auto;max-height:none;pointer-events:auto}
484
526
  }
527
+ /* BK-272 Agent Kaban board */
528
+ .kaban-header{display:flex;align-items:flex-start;flex-wrap:wrap;gap:8px;padding:8px 2px}
529
+ .kaban-feature-info{flex:1;min-width:0}
530
+ .kaban-feature-name{font-size:14px;font-weight:700;color:var(--graph-text)}
531
+ .kaban-meta{font-size:11px;color:var(--graph-muted);margin-top:2px}
532
+ .kaban-liveness{font-size:11px;color:#b84c00;font-weight:700;padding:2px 8px;border:1px solid rgba(184,76,0,.35);border-radius:4px;white-space:nowrap;background:rgba(240,136,62,.07)}
533
+ .kaban-freshness{font-size:10px;color:var(--graph-muted)}
534
+ .kaban-notice{margin:0 0 6px;padding:5px 10px;border-radius:5px;font-size:11px;background:rgba(184,76,0,.06);color:#b84c00;border:1px solid rgba(184,76,0,.18)}
535
+ .kaban-tabs{display:flex;border-bottom:1px solid var(--graph-border);margin-bottom:6px}
536
+ .kaban-tab{background:none;border:none;border-bottom:2px solid transparent;padding:5px 14px;font-size:11px;font-weight:600;color:var(--graph-muted);cursor:pointer;margin-bottom:-1px}
537
+ .kaban-tab:hover{color:var(--graph-text)}
538
+ .kaban-tab.active{color:var(--graph-text);border-bottom-color:#5f89ff}
539
+ .kaban-board-wrap{overflow-x:auto;padding-bottom:8px}
540
+ .kaban-board{display:grid;grid-template-columns:80px repeat(4,minmax(110px,1fr));min-width:540px;column-gap:5px;row-gap:0}
541
+ .kaban-col-header{font-size:9px;text-transform:uppercase;letter-spacing:.06em;font-weight:700;padding:3px 4px 6px;text-align:center;color:var(--graph-muted)}
542
+ .kaban-col-header.kaban-col-inprog{color:#3156c9}
543
+ .kaban-col-header.kaban-col-pending{color:#b84c00}
544
+ .kaban-col-header.kaban-col-done{color:#1a7f37}
545
+ .kaban-swimlane-header{font-size:8px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--graph-muted);padding:7px 4px 3px;text-align:right;border-top:1px solid var(--graph-border)}
546
+ .kaban-cell{min-height:28px;padding:3px 2px;border-top:1px solid var(--graph-border);display:flex;flex-direction:column;gap:3px}
547
+ .kaban-cell.kaban-cell-inprog .kaban-card{border-left:3px solid #3156c9}
548
+ .kaban-cell.kaban-cell-pending .kaban-card{border-left:3px solid #b84c00}
549
+ .kaban-cell.kaban-cell-done .kaban-card{border-left:3px solid #1a7f37}
550
+ .kaban-cell.kaban-cell-todo .kaban-card{border-left:3px solid var(--border)}
551
+ .kaban-card{background:var(--surface);border:1px solid var(--border);border-radius:4px;padding:5px 7px;font-size:11px;color:var(--text);cursor:pointer;line-height:1.4;transition:background .12s}
552
+ .kaban-card:hover{background:var(--surface2)}
553
+ .kaban-card-title{font-weight:600}
554
+ .kaban-card-badges{display:flex;gap:3px;flex-wrap:wrap;margin-top:2px}
555
+ .kaban-badge{font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;padding:1px 4px;border-radius:3px;display:inline-block}
556
+ .kaban-badge-attention{background:rgba(184,76,0,.14);color:#b84c00;border:1px solid rgba(184,76,0,.28)}
557
+ .kaban-badge-gate{background:rgba(100,60,200,.08);color:#6438c8;border:1px solid rgba(100,60,200,.2)}
558
+ .kaban-card-detail{position:fixed;top:10%;left:10%;right:10%;max-height:76vh;z-index:200;background:var(--surface);border:1px solid var(--border);border-radius:10px;display:flex;flex-direction:column;box-shadow:0 16px 48px rgba(0,0,0,.7)}
559
+ .kaban-detail-header{display:flex;align-items:flex-start;gap:10px;padding:12px 14px 8px;border-bottom:1px solid var(--border)}
560
+ .kaban-detail-phase{font-size:9px;text-transform:uppercase;letter-spacing:.07em;color:var(--text2);font-weight:700}
561
+ .kaban-detail-title{font-size:14px;font-weight:700;color:var(--text);margin-top:3px}
562
+ .kaban-detail-close{background:none;border:none;color:var(--text2);font-size:18px;cursor:pointer;padding:0 4px;line-height:1;border-radius:3px;flex-shrink:0}
563
+ .kaban-detail-close:hover{color:var(--text);background:var(--surface2)}
564
+ .kaban-detail-body{padding:12px 14px;overflow-y:auto;flex:1;font-size:12px;line-height:1.6}
565
+ .kaban-detail-row{display:flex;gap:8px;margin-bottom:5px}
566
+ .kaban-detail-label{font-size:10px;text-transform:uppercase;letter-spacing:.05em;color:var(--text2);font-weight:600;min-width:68px;flex-shrink:0}
567
+ .kaban-detail-value{color:var(--text);flex:1;word-break:break-word}
485
568
 
486
569
  </style>
487
570
  </head>
@@ -503,25 +586,33 @@ body.nav-open .page-nav-toggle{display:none}
503
586
  <p>Project-local wiki and knowledge graph - generated __ATLAS_GENERATED__</p>
504
587
  </div>
505
588
  <div class="screen-scroll">
506
- <div class="dash-grid">
507
- <div class="dash-card" style="border-color:#58a6ff">
508
- <span class="num" style="color:#58a6ff" id="stat-total">-</span>
509
- <span class="label">Total Docs</span>
589
+ <div class="kaban-header" id="kaban-header">
590
+ <div class="kaban-feature-info">
591
+ <div class="kaban-feature-name" id="kaban-feature-name">Agent Kaban Board</div>
592
+ <div class="kaban-meta" id="kaban-meta">Loading pipeline data...</div>
510
593
  </div>
511
- <div class="dash-card" style="border-color:#3fb950">
512
- <span class="num" style="color:#3fb950" id="stat-families">-</span>
513
- <span class="label">Families</span>
514
- </div>
515
- <div class="dash-card" style="border-color:#f0883e">
516
- <span class="num" style="color:#f0883e" id="stat-edges">-</span>
517
- <span class="label">Graph Edges</span>
518
- </div>
519
- <div class="dash-card" style="border-color:#d2a8ff">
520
- <span class="num" style="color:#d2a8ff" id="stat-issues">-</span>
521
- <span class="label">BK Issues</span>
594
+ <div class="kaban-liveness" id="kaban-liveness" style="display:none">&#9888; <span id="kaban-liveness-text"></span></div>
595
+ <div class="kaban-freshness" id="kaban-freshness"></div>
596
+ </div>
597
+ <div class="kaban-notice" id="kaban-notice" style="display:none"></div>
598
+ <div class="kaban-tabs">
599
+ <button class="kaban-tab active" id="kaban-tab-pipeline" onclick="switchKabanTab('pipeline')">Pipeline</button>
600
+ <button class="kaban-tab" id="kaban-tab-quality" onclick="switchKabanTab('quality')">Quality Gates</button>
601
+ </div>
602
+ <div class="kaban-board-wrap">
603
+ <div id="kaban-board-pipeline"></div>
604
+ <div id="kaban-board-quality" style="display:none"></div>
605
+ </div>
606
+ </div>
607
+ <div class="kaban-card-detail" id="kaban-card-detail" style="display:none">
608
+ <div class="kaban-detail-header">
609
+ <div style="flex:1;min-width:0">
610
+ <div class="kaban-detail-phase" id="kd-phase"></div>
611
+ <div class="kaban-detail-title" id="kd-title"></div>
522
612
  </div>
613
+ <button class="kaban-detail-close" onclick="closeKabanDetail()" aria-label="Close card detail" title="Close card detail">&times;</button>
523
614
  </div>
524
- <div id="dash-family-rows"></div>
615
+ <div class="kaban-detail-body" id="kd-body"></div>
525
616
  </div>
526
617
  </div>
527
618
  </div>
@@ -614,7 +705,7 @@ body.nav-open .page-nav-toggle{display:none}
614
705
  <button class="graph-focus-toggle atlas-ask-collapse" id="atlas-ask-toggle" title="Hide Ask SDTK-WIKI" onclick="toggleWikiAsk(false)">&#9662;</button>
615
706
  <div class="atlas-ask-attachments" id="atlas-ask-attachments"></div>
616
707
  <div class="atlas-ask-input-wrap">
617
- <textarea class="atlas-ask-input" id="atlas-ask-input" placeholder="Ask about the visible graph group, current focus, or attached docs..."></textarea>
708
+ <textarea class="atlas-ask-input" id="atlas-ask-input" placeholder="Ask something about your docs..."></textarea>
618
709
  <div class="atlas-ask-mention-results" id="atlas-ask-mention-results"></div>
619
710
  </div>
620
711
  <div class="atlas-ask-source-summary" id="atlas-ask-source-summary"></div>
@@ -622,11 +713,40 @@ body.nav-open .page-nav-toggle{display:none}
622
713
  <div class="atlas-ask-controls">
623
714
  <select class="atlas-ask-mode-select" id="atlas-ask-runtime-select" onchange="setWikiAskRuntimeAgent(this.value)" aria-label="Select runtime agent"></select>
624
715
  <select class="atlas-ask-mode-select" id="atlas-ask-model-select" onchange="setWikiAskModel(this.value)" aria-label="Select model"></select>
625
- <select class="atlas-ask-mode-select" id="atlas-ask-mode-select" onchange="setWikiAskMode(this.value)" aria-label="Select grounding mode">
626
- <option value="visible-group">Visible Group</option>
627
- <option value="current-focus">Current Focus</option>
628
- <option value="selected-docs">Selected Docs</option>
629
- </select>
716
+ <div class="ask-scope-wrap" id="ask-scope-wrap">
717
+ <button class="ask-scope-btn" id="ask-scope-btn" onclick="toggleWikiAskScopeMenu(event)" aria-haspopup="listbox" aria-expanded="false" title="Select Ask scope">
718
+ <span class="scope-ico" id="ask-scope-ico">&#128218;</span>
719
+ <span class="scope-label" id="ask-scope-label">All Docs</span>
720
+ <span class="scope-caret">&#9650;</span>
721
+ </button>
722
+ <div class="ask-scope-menu" id="ask-scope-menu" role="listbox">
723
+ <div class="ask-scope-opt active" data-scope="all" role="option" tabindex="0" onclick="pickWikiAskScope('all')" title="Hỏi trên toàn bộ tài liệu đã index (mặc định).">
724
+ <span class="opt-ico">&#128218;</span>
725
+ <span class="opt-copy"><strong>All Docs</strong></span>
726
+ <span class="opt-check">&#10003;</span>
727
+ </div>
728
+ <div class="ask-scope-opt" id="ask-scope-opt-folder" data-scope="folder" role="option" tabindex="0" onclick="toggleWikiAskFolderBlock(event)" title="Chọn một thư mục có thật để giới hạn phạm vi hỏi.">
729
+ <span class="opt-ico">&#128193;</span>
730
+ <span class="opt-copy"><strong>By folder</strong></span>
731
+ <span class="opt-check">&#10003;</span>
732
+ </div>
733
+ <div class="ask-folder-block" id="ask-folder-block">
734
+ <input class="ask-folder-filter" id="ask-folder-filter" type="text" placeholder="Filter folders&#x2026;" oninput="filterWikiAskFolders(this.value)" aria-label="Filter folders by name">
735
+ <div class="ask-folder-list" id="ask-folder-list"></div>
736
+ </div>
737
+ <div class="ask-scope-sep"></div>
738
+ <div class="ask-scope-opt" id="ask-scope-opt-focus" data-scope="focus" role="option" tabindex="0" onclick="pickWikiAskScope('focus')" title="T&#xE0;i li&#x1EC7;u &#x111;ang focus + c&#xE1;c t&#xE0;i li&#x1EC7;u li&#xEA;n quan c&#xF9;ng BK / knowledge / skill.">
739
+ <span class="opt-ico">&#127919;</span>
740
+ <span class="opt-copy"><strong>This doc + related</strong></span>
741
+ <span class="opt-check">&#10003;</span>
742
+ </div>
743
+ <div class="ask-scope-opt" id="ask-scope-opt-selected" data-scope="selected" role="option" tabindex="0" onclick="pickWikiAskScope('selected')" title="Ch&#x1EC9; h&#x1ECF;i tr&#xEA;n c&#xE1;c t&#xE0;i li&#x1EC7;u b&#x1EA1;n &#x111;&#xE3; &#x111;&#xED;nh k&#xE8;m.">
744
+ <span class="opt-ico">&#10003;</span>
745
+ <span class="opt-copy"><strong id="ask-scope-selected-label">Selected docs</strong></span>
746
+ <span class="opt-check">&#10003;</span>
747
+ </div>
748
+ </div>
749
+ </div>
630
750
  <button class="atlas-ask-icon-btn" id="atlas-ask-info-toggle" title="Show grounding info" onclick="toggleWikiAskAdvanced()">i</button>
631
751
  </div>
632
752
  <button class="atlas-ask-send" id="atlas-ask-submit" onclick="submitWikiAsk()" aria-label="Submit SDTK-WIKI Ask" title="Submit SDTK-WIKI Ask">&#8593;</button>
@@ -740,26 +860,179 @@ const FAMILY_GLYPHS = {
740
860
  };
741
861
  const GRAPH_EDGE_COLORS = { references_path: 'rgba(95,137,255,0.38)', references_wiki_link: 'rgba(46,181,125,0.44)' };
742
862
  const GRAPH_FRAME_MS = 40;
743
- (function initDash() {
744
- const docs = INDEX.documents;
745
- const families = new Set(docs.map(d => d.family));
746
- const issues = new Set(docs.flatMap(d => d.issues));
747
- const edges = GRAPH.edges.filter(e => e.type !== 'same_family');
748
- document.getElementById('stat-total').textContent = docs.length;
749
- document.getElementById('stat-families').textContent = families.size;
750
- document.getElementById('stat-edges').textContent = edges.length;
751
- document.getElementById('stat-issues').textContent = issues.size;
752
- const famCounts = {};
753
- docs.forEach(d => { famCounts[d.family] = (famCounts[d.family] || 0) + 1; });
754
- const rows = document.getElementById('dash-family-rows');
755
- Object.entries(famCounts).sort((a,b)=>b[1]-a[1]).forEach(([fam,cnt]) => {
756
- const color = FAMILY_COLORS[fam] || '#8b949e';
757
- const row = document.createElement('div');
758
- row.className = 'stat-row';
759
- row.innerHTML = `<span class="label"><span class="dot" style="background:${color}"></span> ${fam}</span><span class="val" style="color:${color}">${cnt}</span>`;
760
- rows.appendChild(row);
761
- });
762
- })();
863
+ // BK-272: Agent Kaban board — state, render, and poll
864
+ var kabanState = { data: null, activeTab: 'pipeline', pollTimer: null, lastFetch: null };
865
+ const KABAN_POLL_MS = 3000;
866
+ const KABAN_LIVENESS_MIN_JS = 15;
867
+ const KABAN_COLS = ['todo', 'in_progress', 'pending', 'done'];
868
+ const KABAN_COL_LABELS = { todo: 'Todo', in_progress: 'In Progress', pending: 'Pending', done: 'Done' };
869
+ const KABAN_COL_CSS = { todo: '', in_progress: 'kaban-col-inprog', pending: 'kaban-col-pending', done: 'kaban-col-done' };
870
+ const KABAN_CELL_CSS = { todo: 'kaban-cell-todo', in_progress: 'kaban-cell-inprog', pending: 'kaban-cell-pending', done: 'kaban-cell-done' };
871
+
872
+ function safeKabanHtml(s) {
873
+ return String(s || '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
874
+ }
875
+ function formatKabanFreshness(dt) {
876
+ if (!dt) return '';
877
+ const d = Math.floor((new Date() - dt) / 1000);
878
+ if (d < 5) return 'just now';
879
+ if (d < 60) return d + 's ago';
880
+ if (d < 3600) return Math.floor(d / 60) + 'm ago';
881
+ return Math.floor(d / 3600) + 'h ago';
882
+ }
883
+ function renderKabanCard(card) {
884
+ const badges = [];
885
+ if (card.attention) badges.push('<span class="kaban-badge kaban-badge-attention">&#9888; Pending</span>');
886
+ if (card.isGate) badges.push('<span class="kaban-badge kaban-badge-gate">GATE</span>');
887
+ const id = safeKabanHtml(card.id);
888
+ const tab = safeKabanHtml(kabanState.activeTab);
889
+ return '<div class="kaban-card" onclick="openKabanDetail(\'' + id + '\',\'' + tab + '\')" role="button" tabindex="0">' +
890
+ '<div class="kaban-card-title">' + safeKabanHtml(card.title || card.id) + '</div>' +
891
+ (badges.length ? '<div class="kaban-card-badges">' + badges.join('') + '</div>' : '') +
892
+ '</div>';
893
+ }
894
+ function renderKabanBoard(containerId, cards, agents) {
895
+ const container = document.getElementById(containerId);
896
+ if (!container) return;
897
+ if (!cards || cards.length === 0) {
898
+ container.innerHTML = '<div style="padding:16px;text-align:center;font-size:11px;color:var(--text2)">No data — SHARED_PLANNING.md or QUALITY_CHECKLIST.md not found in project root.</div>';
899
+ return;
900
+ }
901
+ let html = '<div class="kaban-board">';
902
+ html += '<div class="kaban-col-header"></div>';
903
+ for (const col of KABAN_COLS) {
904
+ html += '<div class="kaban-col-header ' + (KABAN_COL_CSS[col] || '') + '">' + KABAN_COL_LABELS[col] + '</div>';
905
+ }
906
+ for (const agent of agents) {
907
+ html += '<div class="kaban-swimlane-header">' + safeKabanHtml(agent) + '</div>';
908
+ for (const col of KABAN_COLS) {
909
+ const cellCards = cards.filter(function(c){ return c.agent === agent && c.column === col; });
910
+ html += '<div class="kaban-cell ' + KABAN_CELL_CSS[col] + '">';
911
+ for (const c of cellCards) html += renderKabanCard(c);
912
+ html += '</div>';
913
+ }
914
+ }
915
+ const otherCards = cards.filter(function(c){ return !agents.includes(c.agent); });
916
+ if (otherCards.length > 0) {
917
+ html += '<div class="kaban-swimlane-header">OTHER</div>';
918
+ for (const col of KABAN_COLS) {
919
+ const cellCards = otherCards.filter(function(c){ return c.column === col; });
920
+ html += '<div class="kaban-cell ' + KABAN_CELL_CSS[col] + '">';
921
+ for (const c of cellCards) html += renderKabanCard(c);
922
+ html += '</div>';
923
+ }
924
+ }
925
+ html += '</div>';
926
+ container.innerHTML = html;
927
+ }
928
+ function renderKaban(data) {
929
+ if (!data) return;
930
+ const meta = data.meta || {};
931
+ const agents = data.agents || ['PM','BA','ARCH','DEV','QA'];
932
+ const nameEl = document.getElementById('kaban-feature-name');
933
+ if (nameEl) nameEl.textContent = meta.featureName || meta.featureKey || 'Agent Kaban Board';
934
+ const metaEl = document.getElementById('kaban-meta');
935
+ if (metaEl) {
936
+ const parts = [];
937
+ if (meta.featureKey) parts.push(meta.featureKey);
938
+ if (meta.lastUpdated) parts.push('Updated: ' + meta.lastUpdated);
939
+ metaEl.textContent = parts.join(' · ') || 'No pipeline data';
940
+ }
941
+ const freshEl = document.getElementById('kaban-freshness');
942
+ if (freshEl && kabanState.lastFetch) freshEl.textContent = 'Fetched ' + formatKabanFreshness(kabanState.lastFetch);
943
+ const livenessEl = document.getElementById('kaban-liveness');
944
+ const livenessTextEl = document.getElementById('kaban-liveness-text');
945
+ if (livenessEl && meta.lastUpdated) {
946
+ try {
947
+ const lu = new Date(meta.lastUpdated.replace(' ','T'));
948
+ const diffMin = Math.floor((new Date() - lu) / 60000);
949
+ if (diffMin > KABAN_LIVENESS_MIN_JS && !isNaN(diffMin)) {
950
+ if (livenessTextEl) livenessTextEl.textContent = 'No movement for ' + diffMin + ' min';
951
+ livenessEl.style.display = '';
952
+ } else { livenessEl.style.display = 'none'; }
953
+ } catch(_) { livenessEl.style.display = 'none'; }
954
+ } else if (livenessEl) { livenessEl.style.display = 'none'; }
955
+ const noticeEl = document.getElementById('kaban-notice');
956
+ if (noticeEl) {
957
+ const notices = [];
958
+ if (!meta.pipelinePresent) notices.push('SHARED_PLANNING.md not found');
959
+ if (!meta.qualityPresent) notices.push('QUALITY_CHECKLIST.md not found');
960
+ (meta.errors || []).forEach(function(e){ notices.push(e); });
961
+ if (notices.length > 0) { noticeEl.textContent = notices.join(' · '); noticeEl.style.display = ''; }
962
+ else { noticeEl.style.display = 'none'; }
963
+ }
964
+ renderKabanBoard('kaban-board-pipeline', (data.pipeline || {}).cards || [], agents);
965
+ renderKabanBoard('kaban-board-quality', (data.quality || {}).cards || [], agents);
966
+ }
967
+ function openKabanDetail(cardId, tab) {
968
+ if (!kabanState.data) return;
969
+ const src = tab === 'quality' ? (kabanState.data.quality || {}) : (kabanState.data.pipeline || {});
970
+ const card = (src.cards || []).find(function(c){ return c.id === cardId; });
971
+ if (!card) return;
972
+ const phaseEl = document.getElementById('kd-phase');
973
+ const titleEl = document.getElementById('kd-title');
974
+ const bodyEl = document.getElementById('kd-body');
975
+ const detailEl = document.getElementById('kaban-card-detail');
976
+ if (!detailEl) return;
977
+ if (phaseEl) phaseEl.textContent = (card.agent || '') + (card.phase ? ' · ' + card.phase : '');
978
+ if (titleEl) titleEl.textContent = card.title || card.id;
979
+ if (bodyEl) {
980
+ const rows = [];
981
+ function addRow(label, value) {
982
+ if (!value) return;
983
+ rows.push('<div class="kaban-detail-row"><span class="kaban-detail-label">' + safeKabanHtml(label) + '</span><span class="kaban-detail-value">' + safeKabanHtml(value) + '</span></div>');
984
+ }
985
+ addRow('Column', KABAN_COL_LABELS[card.column] || card.column);
986
+ addRow('Status', card.rawStatus);
987
+ addRow('Owner', card.owner);
988
+ addRow('Artifact', card.artifact);
989
+ addRow('Notes', card.notes);
990
+ addRow('Issue', card.issue);
991
+ addRow('Last update', card.lastUpdate);
992
+ addRow('Handoff →', card.handoffNext);
993
+ if (card.isGate) rows.push('<div style="margin-top:8px"><span class="kaban-badge kaban-badge-gate">GATE CHECKPOINT</span></div>');
994
+ if (card.attention) rows.push('<div style="margin-top:4px"><span class="kaban-badge kaban-badge-attention">&#9888; Needs attention</span></div>');
995
+ bodyEl.innerHTML = rows.join('') || '<span style="color:var(--text2)">No additional detail.</span>';
996
+ }
997
+ detailEl.style.display = 'flex';
998
+ }
999
+ function closeKabanDetail() {
1000
+ const el = document.getElementById('kaban-card-detail');
1001
+ if (el) el.style.display = 'none';
1002
+ }
1003
+ function switchKabanTab(tab) {
1004
+ kabanState.activeTab = tab;
1005
+ const ptab = document.getElementById('kaban-tab-pipeline');
1006
+ const qtab = document.getElementById('kaban-tab-quality');
1007
+ const pboard = document.getElementById('kaban-board-pipeline');
1008
+ const qboard = document.getElementById('kaban-board-quality');
1009
+ if (ptab) ptab.classList.toggle('active', tab === 'pipeline');
1010
+ if (qtab) qtab.classList.toggle('active', tab === 'quality');
1011
+ if (pboard) pboard.style.display = tab === 'pipeline' ? '' : 'none';
1012
+ if (qboard) qboard.style.display = tab === 'quality' ? '' : 'none';
1013
+ closeKabanDetail();
1014
+ }
1015
+ async function fetchKabanData() {
1016
+ try {
1017
+ const resp = await fetch('/api/kaban');
1018
+ if (!resp.ok) throw new Error('HTTP ' + resp.status);
1019
+ const data = await resp.json();
1020
+ kabanState.data = data;
1021
+ kabanState.lastFetch = new Date();
1022
+ renderKaban(data);
1023
+ } catch(e) {
1024
+ const noticeEl = document.getElementById('kaban-notice');
1025
+ if (noticeEl) { noticeEl.textContent = 'Could not load kaban data: ' + e.message; noticeEl.style.display = ''; }
1026
+ }
1027
+ }
1028
+ function startKabanPoll() {
1029
+ stopKabanPoll();
1030
+ fetchKabanData();
1031
+ kabanState.pollTimer = setInterval(fetchKabanData, KABAN_POLL_MS);
1032
+ }
1033
+ function stopKabanPoll() {
1034
+ if (kabanState.pollTimer) { clearInterval(kabanState.pollTimer); kabanState.pollTimer = null; }
1035
+ }
763
1036
 
764
1037
 
765
1038
  function applySidebarState(collapsed) {
@@ -805,6 +1078,7 @@ function showPanel(name, btn) {
805
1078
  } else {
806
1079
  stopGraphAnimation();
807
1080
  }
1081
+ if (name === 'dash') { startKabanPoll(); } else { stopKabanPoll(); }
808
1082
  }
809
1083
 
810
1084
  let activeFamily = null;
@@ -879,7 +1153,8 @@ const graphState = {
879
1153
  };
880
1154
  const wikiAskState = {
881
1155
  open: false,
882
- mode: 'visible-group',
1156
+ mode: 'all-docs',
1157
+ docPathFilter: '',
883
1158
  selectedDocIds: [],
884
1159
  status: 'idle',
885
1160
  answer: null,
@@ -1433,8 +1708,165 @@ function openGraphToolbarPeek(event) {
1433
1708
  toggleGraphToolbarMinimized(false);
1434
1709
  }
1435
1710
  function getWikiAskModeLabel(mode) {
1436
- return ({ 'current-focus': 'Current Focus', 'visible-group': 'Visible Group', 'selected-docs': 'Selected Docs' }[mode]) || 'Ask SDTK-WIKI';
1711
+ return ({ 'all-docs': 'All Docs', 'current-focus': 'This doc + related', 'visible-group': 'All Docs', 'selected-docs': 'Selected docs' }[mode]) || 'Ask SDTK-WIKI';
1712
+ }
1713
+ function getWikiAskAllDocIds(pathFilter) {
1714
+ const filter = (pathFilter || '').trim().toLowerCase();
1715
+ return docs.filter(d => {
1716
+ if (!filter) return true;
1717
+ const docPath = (d.sourcePath || d.path || d.id || '').toLowerCase();
1718
+ return docPath.startsWith(filter);
1719
+ }).map(d => d.id);
1720
+ }
1721
+ function setWikiAskDocPathFilter(value) {
1722
+ wikiAskState.docPathFilter = value || '';
1723
+ renderWikiAsk();
1724
+ }
1725
+ function getWikiAskFolderList() {
1726
+ var counts = {};
1727
+ for (var i = 0; i < docs.length; i++) {
1728
+ var d = docs[i];
1729
+ var sp = (d.sourcePath || d.path || d.id || '').replace(/\\/g, '/');
1730
+ if (!sp) continue;
1731
+ var parts = sp.split('/');
1732
+ if (parts.length >= 2) {
1733
+ var prefix = parts.slice(0, 2).join('/') + '/';
1734
+ counts[prefix] = (counts[prefix] || 0) + 1;
1735
+ }
1736
+ }
1737
+ return Object.entries(counts)
1738
+ .sort(function(a, b) { return b[1] - a[1] || a[0].localeCompare(b[0]); })
1739
+ .slice(0, 8)
1740
+ .map(function(e) { return { path: e[0], count: e[1] }; });
1741
+ }
1742
+ function renderWikiAskFolderList() {
1743
+ var list = document.getElementById('ask-folder-list');
1744
+ if (!list) return;
1745
+ var folders = getWikiAskFolderList();
1746
+ if (!folders.length) {
1747
+ list.innerHTML = '<div style="padding:8px 10px;font-size:12px;color:#6d7d95">No folders found.</div>';
1748
+ return;
1749
+ }
1750
+ list.innerHTML = folders.map(function(f) {
1751
+ var active = wikiAskState.docPathFilter === f.path ? ' active' : '';
1752
+ return '<div class="ask-folder-item' + active + '" data-path="' + escapeHtml(f.path) + '" onclick="pickWikiAskFolder(' + JSON.stringify(f.path) + ')"><span class="f-path">' + escapeHtml(f.path) + '</span><span class="f-count">' + f.count + '</span></div>';
1753
+ }).join('');
1437
1754
  }
1755
+ function toggleWikiAskScopeMenu(event) {
1756
+ if (event) event.stopPropagation();
1757
+ var wrap = document.getElementById('ask-scope-wrap');
1758
+ if (!wrap) return;
1759
+ var isOpen = wrap.classList.contains('open');
1760
+ if (!isOpen) {
1761
+ renderWikiAskFolderList();
1762
+ syncWikiAskScopeMenu();
1763
+ }
1764
+ wrap.classList.toggle('open', !isOpen);
1765
+ var btn = document.getElementById('ask-scope-btn');
1766
+ if (btn) btn.setAttribute('aria-expanded', String(!isOpen));
1767
+ }
1768
+ function closeWikiAskScopeMenu() {
1769
+ var wrap = document.getElementById('ask-scope-wrap');
1770
+ if (wrap) {
1771
+ wrap.classList.remove('open');
1772
+ var btn = document.getElementById('ask-scope-btn');
1773
+ if (btn) btn.setAttribute('aria-expanded', 'false');
1774
+ }
1775
+ }
1776
+ function syncWikiAskScopeMenu() {
1777
+ var menu = document.getElementById('ask-scope-menu');
1778
+ if (!menu) return;
1779
+ menu.querySelectorAll('.ask-scope-opt').forEach(function(opt) { opt.classList.remove('active'); });
1780
+ var scope = wikiAskState.mode === 'current-focus' ? 'focus'
1781
+ : wikiAskState.mode === 'selected-docs' ? 'selected'
1782
+ : wikiAskState.docPathFilter ? 'folder'
1783
+ : 'all';
1784
+ var target = menu.querySelector('[data-scope="' + scope + '"]');
1785
+ if (target) target.classList.add('active');
1786
+ var focusOpt = document.getElementById('ask-scope-opt-focus');
1787
+ var focusId = typeof getGraphFocusId !== 'undefined' ? getGraphFocusId() : null;
1788
+ if (focusOpt) {
1789
+ if (focusId) { focusOpt.classList.remove('disabled'); focusOpt.removeAttribute('aria-disabled'); }
1790
+ else { focusOpt.classList.add('disabled'); focusOpt.setAttribute('aria-disabled', 'true'); }
1791
+ }
1792
+ var selectedOpt = document.getElementById('ask-scope-opt-selected');
1793
+ if (selectedOpt) {
1794
+ var selLabel = document.getElementById('ask-scope-selected-label');
1795
+ if (wikiAskState.selectedDocIds.length > 0) {
1796
+ selectedOpt.style.display = '';
1797
+ if (selLabel) selLabel.textContent = 'Selected docs (' + wikiAskState.selectedDocIds.length + ')';
1798
+ } else { selectedOpt.style.display = 'none'; }
1799
+ }
1800
+ if (scope === 'folder') {
1801
+ var folderBlock = document.getElementById('ask-folder-block');
1802
+ if (folderBlock) folderBlock.classList.add('open');
1803
+ document.querySelectorAll('#ask-folder-list .ask-folder-item').forEach(function(item) {
1804
+ item.classList.toggle('active', item.dataset.path === wikiAskState.docPathFilter);
1805
+ });
1806
+ }
1807
+ }
1808
+ function pickWikiAskScope(scope) {
1809
+ if (scope === 'focus') {
1810
+ var focusId = typeof getGraphFocusId !== 'undefined' ? getGraphFocusId() : null;
1811
+ if (!focusId) return;
1812
+ wikiAskState.mode = 'current-focus';
1813
+ wikiAskState.docPathFilter = '';
1814
+ } else if (scope === 'selected') {
1815
+ wikiAskState.mode = 'selected-docs';
1816
+ wikiAskState.docPathFilter = '';
1817
+ } else {
1818
+ wikiAskState.mode = 'all-docs';
1819
+ wikiAskState.docPathFilter = '';
1820
+ }
1821
+ closeWikiAskScopeMenu();
1822
+ renderWikiAsk();
1823
+ }
1824
+ function toggleWikiAskFolderBlock(event) {
1825
+ if (event) event.stopPropagation();
1826
+ var block = document.getElementById('ask-folder-block');
1827
+ if (!block) return;
1828
+ var isOpen = block.classList.contains('open');
1829
+ block.classList.toggle('open', !isOpen);
1830
+ if (!isOpen) { var filter = document.getElementById('ask-folder-filter'); if (filter) filter.focus(); }
1831
+ }
1832
+ function pickWikiAskFolder(path) {
1833
+ wikiAskState.mode = 'all-docs';
1834
+ wikiAskState.docPathFilter = path;
1835
+ document.querySelectorAll('#ask-folder-list .ask-folder-item').forEach(function(item) {
1836
+ item.classList.toggle('active', item.dataset.path === path);
1837
+ });
1838
+ closeWikiAskScopeMenu();
1839
+ renderWikiAsk();
1840
+ }
1841
+ function filterWikiAskFolders(q) {
1842
+ var query = (q || '').trim().toLowerCase();
1843
+ document.querySelectorAll('#ask-folder-list .ask-folder-item').forEach(function(item) {
1844
+ var p = item.dataset.path.toLowerCase();
1845
+ item.style.display = (!query || p.indexOf(query) !== -1) ? '' : 'none';
1846
+ });
1847
+ }
1848
+ function renderWikiAskSourcesPanel(context) {
1849
+ if (!context) return '';
1850
+ var sourcePaths = Array.isArray(context.source_paths) ? context.source_paths : [];
1851
+ var relationMap = context.truncated && typeof context.truncated === 'object' && context.truncated.relation ? context.truncated.relation : {};
1852
+ var total = sourcePaths.length;
1853
+ if (!total) return '';
1854
+ var rows = sourcePaths.map(function(p) {
1855
+ var rel = relationMap[p];
1856
+ var chipHtml;
1857
+ if (!rel || rel === 'primary') {
1858
+ chipHtml = '<span class="s-chip primary">primary</span>';
1859
+ } else {
1860
+ var facetsRaw = rel.replace(/^shares\s+/, '');
1861
+ var bkMatch = facetsRaw.match(/issue:([^,\s]+)/);
1862
+ var relLabel = bkMatch ? 'related \u00b7 ' + bkMatch[1] : 'related';
1863
+ chipHtml = '<span class="s-chip related">' + escapeHtml(relLabel) + '</span>';
1864
+ }
1865
+ return '<div class="ask-source-row"><span class="s-path">' + escapeHtml(p) + '</span>' + chipHtml + '</div>';
1866
+ }).join('');
1867
+ return '<div class="ask-sources"><button class="ask-sources-head" onclick="this.parentElement.classList.toggle(\'open\')" title="Toggle sources used panel"><span class="ask-sources-caret">&#9658;</span><span>Sources used</span><span class="ask-sources-badge">' + total + '</span></button><div class="ask-sources-body">' + rows + '</div></div>';
1868
+ }
1869
+
1438
1870
  function getWikiAskVisibleDocIds() {
1439
1871
  return getVisibleDocs().map(doc => doc.id);
1440
1872
  }
@@ -1569,7 +2001,8 @@ function loadWikiAskHistoryItem(id) {
1569
2001
  wikiAskState.question = '';
1570
2002
  wikiAskState.pendingQuestion = '';
1571
2003
  wikiAskState.lastQuestion = item.question || '';
1572
- wikiAskState.mode = item.mode || 'visible-group';
2004
+ wikiAskState.mode = (item.mode === 'visible-group' ? 'all-docs' : (item.mode || 'all-docs'));
2005
+ wikiAskState.docPathFilter = item.doc_path_filter || '';
1573
2006
  wikiAskState.selectedDocIds = Array.isArray(item.selected_doc_ids) ? [...item.selected_doc_ids] : [];
1574
2007
  wikiAskState.answer = {
1575
2008
  question: item.question || '',
@@ -1696,20 +2129,23 @@ function clearWikiAskSources() {
1696
2129
  renderWikiAsk();
1697
2130
  }
1698
2131
  function getWikiAskSourceSummaryHtml() {
1699
- const visibleCount = getWikiAskVisibleDocIds().length;
1700
2132
  const selectedCount = wikiAskState.selectedDocIds.length;
1701
2133
  const focusId = getGraphFocusId();
1702
- const focusLabel = focusId ? ((docById[focusId] && (docById[focusId].title || focusId)) || focusId) : 'None';
2134
+ const focusLabel = focusId ? ((docById[focusId] && (docById[focusId].title || focusId)) || focusId) : null;
2135
+ if (wikiAskState.mode === 'all-docs') {
2136
+ const filteredIds = getWikiAskAllDocIds(wikiAskState.docPathFilter);
2137
+ if (wikiAskState.docPathFilter) {
2138
+ return `<strong>Scope:</strong> By folder <code>${escapeHtml(wikiAskState.docPathFilter)}</code> — ${filteredIds.length} of ${docs.length} docs.`;
2139
+ }
2140
+ return `<strong>Scope:</strong> All Docs — ${filteredIds.length} docs indexed.`;
2141
+ }
1703
2142
  if (wikiAskState.mode === 'current-focus') {
1704
2143
  if (focusId) {
1705
- return `<strong>Grounding:</strong> Current focus source. <strong>Focus:</strong> ${escapeHtml(focusLabel)}. <strong>Visible group:</strong> ${visibleCount} docs.`;
2144
+ return `<strong>Scope:</strong> This doc + related — <strong>${escapeHtml(focusLabel)}</strong> and its related siblings.`;
1706
2145
  }
1707
- return `<strong>Grounding:</strong> Current focus source. Select a graph note to anchor this ask. <strong>Visible group:</strong> ${visibleCount} docs.`;
1708
- }
1709
- if (wikiAskState.mode === 'visible-group') {
1710
- return `<strong>Grounding:</strong> Visible graph group. <strong>Visible docs:</strong> ${visibleCount}.${selectedCount ? ` <strong>Explicit docs:</strong> ${selectedCount}.` : ''}`;
2146
+ return `<strong>Scope:</strong> This doc + related <em>focus a graph node first.</em>`;
1711
2147
  }
1712
- return `<strong>Grounding:</strong> Explicit source pack. <strong>Selected docs:</strong> ${selectedCount}.${selectedCount ? '' : ' Use graph search results or @mention docs before asking.'}`;
2148
+ return `<strong>Scope:</strong> Selected docs ${selectedCount} doc${selectedCount !== 1 ? 's' : ''} attached.${selectedCount ? '' : ' Attach docs via @mention or graph search.'}`;
1713
2149
  }
1714
2150
  function getWikiAskAdvancedSummaryText() {
1715
2151
  const bits = [];
@@ -1837,7 +2273,6 @@ function renderWikiAsk() {
1837
2273
  );
1838
2274
  const runtimeSelect = document.getElementById('atlas-ask-runtime-select');
1839
2275
  const modelSelect = document.getElementById('atlas-ask-model-select');
1840
- const modeSelect = document.getElementById('atlas-ask-mode-select');
1841
2276
  const input = document.getElementById('atlas-ask-input');
1842
2277
  const attachments = document.getElementById('atlas-ask-attachments');
1843
2278
  const sourceSummary = document.getElementById('atlas-ask-source-summary');
@@ -1857,7 +2292,24 @@ function renderWikiAsk() {
1857
2292
  modelSelect.innerHTML = modelOptions.map(model => `<option value="${model}">${escapeHtml(getWikiAskModelLabel(model))}</option>`).join('');
1858
2293
  if (modelSelect.value !== wikiAskState.model) modelSelect.value = wikiAskState.model;
1859
2294
  }
1860
- if (modeSelect && modeSelect.value !== wikiAskState.mode) modeSelect.value = wikiAskState.mode;
2295
+ const scopeIco = document.getElementById('ask-scope-ico');
2296
+ const scopeLbl = document.getElementById('ask-scope-label');
2297
+ if (scopeIco && scopeLbl) {
2298
+ if (wikiAskState.mode === 'current-focus') {
2299
+ scopeIco.textContent = '\u{1F3AF}';
2300
+ scopeLbl.textContent = 'This doc + related';
2301
+ } else if (wikiAskState.mode === 'selected-docs') {
2302
+ scopeIco.textContent = '\u2713';
2303
+ const selCnt = wikiAskState.selectedDocIds.length;
2304
+ scopeLbl.textContent = selCnt > 0 ? `Selected docs (${selCnt})` : 'Selected docs';
2305
+ } else if (wikiAskState.docPathFilter) {
2306
+ scopeIco.textContent = '\u{1F4C1}';
2307
+ scopeLbl.textContent = wikiAskState.docPathFilter;
2308
+ } else {
2309
+ scopeIco.textContent = '\u{1F4DA}';
2310
+ scopeLbl.textContent = 'All Docs';
2311
+ }
2312
+ }
1861
2313
  if (sourceSummary) sourceSummary.innerHTML = getWikiAskSourceSummaryHtml();
1862
2314
  if (mentionResults) renderWikiAskMentions();
1863
2315
  if (attachments) {
@@ -1903,7 +2355,8 @@ function renderWikiAsk() {
1903
2355
  ? `<ul>${turn.citations.map(item => `<li><button class="wiki-link" onclick="focusGraphNode('${item.path}')">${escapeHtml(item.title || item.path)}</button> &middot; ${escapeHtml(item.reason || 'Grounded source')}</li>`).join('')}</ul>`
1904
2356
  : '<p class="muted">No citations were returned.</p>';
1905
2357
  blocks.push(`<div class="atlas-ask-message user"><div class="atlas-ask-message-label">You</div><div class="atlas-ask-message-bubble">${escapeHtml(turn.question || '')}</div></div>`);
1906
- blocks.push(`<div class="atlas-ask-message assistant"><div class="atlas-ask-message-label">SDTK-WIKI Ask</div><div class="atlas-ask-answer-card"><div class="graph-note-prose">${renderMarkdownFragment(turn.answer_markdown || '')}</div>${turn.disclaimer ? `<span class="muted">${escapeHtml(turn.disclaimer)}</span>` : ''}</div><div class="atlas-ask-answer-card"><h3>Citations</h3>${citationHtml}</div></div>`);
2358
+ const sourcesHtml = renderWikiAskSourcesPanel(turn.context || null);
2359
+ blocks.push(`<div class="atlas-ask-message assistant"><div class="atlas-ask-message-label">SDTK-WIKI Ask</div><div class="atlas-ask-answer-card"><div class="graph-note-prose">${renderMarkdownFragment(turn.answer_markdown || '')}</div>${turn.disclaimer ? `<span class="muted">${escapeHtml(turn.disclaimer)}</span>` : ''}</div><div class="atlas-ask-answer-card"><h3>Citations</h3>${citationHtml}</div>${sourcesHtml}</div>`);
1907
2360
  });
1908
2361
 
1909
2362
  if (wikiAskState.pendingQuestion) {
@@ -1912,7 +2365,7 @@ function renderWikiAsk() {
1912
2365
  }
1913
2366
 
1914
2367
  if (!blocks.length) {
1915
- answer.innerHTML = '<div class="atlas-ask-empty">Ask SDTK-WIKI from the visible graph group by default, or narrow the source pack with a selected node or attached docs.</div>';
2368
+ answer.innerHTML = '<div class="atlas-ask-empty">Use <strong>Ask within</strong> to choose your scope: All Docs for full coverage, By folder to narrow by path, This doc + related to focus on the current graph node, or Selected docs for pinned sources.</div>';
1916
2369
  return;
1917
2370
  }
1918
2371
 
@@ -1930,14 +2383,18 @@ async function submitWikiAsk() {
1930
2383
  renderWikiAsk();
1931
2384
  return;
1932
2385
  }
2386
+ const allDocIds = wikiAskState.mode === 'all-docs'
2387
+ ? getWikiAskAllDocIds(wikiAskState.docPathFilter)
2388
+ : getWikiAskVisibleDocIds();
1933
2389
  const payload = {
1934
2390
  question: draftQuestion,
1935
2391
  mode: wikiAskState.mode,
1936
2392
  runtime_agent: wikiAskState.runtimeAgent,
1937
2393
  model: wikiAskState.model,
1938
2394
  focus_doc_id: getGraphFocusId() || '',
1939
- visible_doc_ids: getWikiAskVisibleDocIds(),
2395
+ visible_doc_ids: allDocIds,
1940
2396
  selected_doc_ids: [...wikiAskState.selectedDocIds],
2397
+ doc_path_filter: wikiAskState.mode === 'all-docs' ? (wikiAskState.docPathFilter || '') : '',
1941
2398
  };
1942
2399
  wikiAskState.pendingQuestion = draftQuestion;
1943
2400
  wikiAskState.question = '';
@@ -2416,6 +2873,10 @@ document.addEventListener('mousedown', event => {
2416
2873
  graphState.settingsOpen = false;
2417
2874
  syncGraphSettings();
2418
2875
  }
2876
+ const scopeWrap = document.getElementById('ask-scope-wrap');
2877
+ if (scopeWrap && scopeWrap.classList.contains('open') && !scopeWrap.contains(event.target)) {
2878
+ closeWikiAskScopeMenu();
2879
+ }
2419
2880
  });
2420
2881
  window.addEventListener('mousemove', dragWikiAsk);
2421
2882
  window.addEventListener('mouseup', stopWikiAskDrag);