spectoflow 0.13.4 → 0.13.5
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spectoflow",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.5",
|
|
4
4
|
"description": "Agent-agnostic spec-driven development framework + real-time local control plane. Markdown artifacts, intent router, workflow-by-scope.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"spec-driven-development",
|
|
@@ -328,6 +328,7 @@ function renderBoard(){
|
|
|
328
328
|
board.append(renderPhase(ph,pl.file,filtered));
|
|
329
329
|
}));
|
|
330
330
|
if(!shown) board.append(noMatchState());
|
|
331
|
+
updatePhaseToggleAll();
|
|
331
332
|
}
|
|
332
333
|
// Kanban view — one column per status, filtered by the text search (columns already are the statuses).
|
|
333
334
|
function renderKanban(board, tasks){
|
|
@@ -435,16 +436,26 @@ function backlogRow(r){
|
|
|
435
436
|
return tr;
|
|
436
437
|
}
|
|
437
438
|
|
|
438
|
-
// ----
|
|
439
|
-
|
|
440
|
-
|
|
439
|
+
// ---- phase expand state: we track which phases are EXPANDED (default = none), so on a big
|
|
440
|
+
// project the board opens compact — just phase headers with progress — and the user opens what
|
|
441
|
+
// they need. Persisted per phase title (guarded for private mode). ----
|
|
442
|
+
function loadExpanded(){
|
|
443
|
+
try{ const raw=localStorage.getItem('spf-expanded'); const arr=raw?JSON.parse(raw):[]; return new Set(Array.isArray(arr)?arr:[]); }
|
|
441
444
|
catch{ return new Set(); }
|
|
442
445
|
}
|
|
443
|
-
function
|
|
444
|
-
let
|
|
446
|
+
function saveExpanded(set){ try{ localStorage.setItem('spf-expanded', JSON.stringify([...set])); }catch{} }
|
|
447
|
+
let expandedPhases=loadExpanded();
|
|
448
|
+
function allPhaseTitles(){ const t=new Set(); (P.plans||[]).forEach(pl=> pl.phases.forEach(ph=> t.add(ph.title))); return [...t]; }
|
|
449
|
+
function updatePhaseToggleAll(){
|
|
450
|
+
const btn=$('#phaseToggleAll'); if(!btn) return;
|
|
451
|
+
const titles=allPhaseTitles();
|
|
452
|
+
const allOpen = titles.length>0 && titles.every(t=> expandedPhases.has(t));
|
|
453
|
+
btn.textContent = allOpen ? 'Collapse all' : 'Expand all';
|
|
454
|
+
btn.dataset.state = allOpen ? 'open' : 'closed';
|
|
455
|
+
}
|
|
445
456
|
|
|
446
457
|
function renderPhase(ph,file,filteredTasks){
|
|
447
|
-
const isCollapsed
|
|
458
|
+
const isCollapsed=!expandedPhases.has(ph.title);
|
|
448
459
|
const sec=el('section','phase'+(isCollapsed?' is-collapsed':''));
|
|
449
460
|
const head=el('div','phase-head'); head.tabIndex=0; head.setAttribute('role','button'); head.setAttribute('aria-expanded',String(!isCollapsed));
|
|
450
461
|
head.append(el('span','chevron'));
|
|
@@ -458,8 +469,8 @@ function renderPhase(ph,file,filteredTasks){
|
|
|
458
469
|
const toggle=()=>{
|
|
459
470
|
const now=sec.classList.toggle('is-collapsed');
|
|
460
471
|
head.setAttribute('aria-expanded',String(!now));
|
|
461
|
-
if(now)
|
|
462
|
-
|
|
472
|
+
if(now) expandedPhases.delete(ph.title); else expandedPhases.add(ph.title);
|
|
473
|
+
saveExpanded(expandedPhases); updatePhaseToggleAll();
|
|
463
474
|
};
|
|
464
475
|
head.addEventListener('click',toggle);
|
|
465
476
|
head.addEventListener('keydown',e=>{ if(e.key==='Enter'||e.key===' '){ e.preventDefault(); toggle(); } });
|
|
@@ -953,6 +964,14 @@ $$('#statusChips .fchip').forEach(b=> b.addEventListener('click', ()=>{ filter.s
|
|
|
953
964
|
$('#search').addEventListener('input', e=>{ filter.q=e.target.value; renderBoard(); });
|
|
954
965
|
// board view switch — List (phase-grouped) vs Kanban (columns by status), persisted per viewer
|
|
955
966
|
$$('#boardViewToggle .vt-btn').forEach(b=> b.addEventListener('click', ()=>{ boardView=b.dataset.view; try{ localStorage.setItem('spf-board-view',boardView); }catch{} renderBoard(); }));
|
|
967
|
+
// expand / collapse all phases (List view) — keeps a big board compact by default
|
|
968
|
+
const phaseToggleAllBtn=$('#phaseToggleAll');
|
|
969
|
+
if(phaseToggleAllBtn) phaseToggleAllBtn.addEventListener('click', ()=>{
|
|
970
|
+
const titles=allPhaseTitles();
|
|
971
|
+
const allOpen = titles.length>0 && titles.every(t=> expandedPhases.has(t));
|
|
972
|
+
if(allOpen) expandedPhases.clear(); else titles.forEach(t=> expandedPhases.add(t));
|
|
973
|
+
saveExpanded(expandedPhases); renderBoard();
|
|
974
|
+
});
|
|
956
975
|
// backlog: independent filters + sortable column headers — client-side only (reset to page 1 on change)
|
|
957
976
|
$$('#backlogStatusChips .fchip').forEach(b=> b.addEventListener('click', ()=>{ backlogFilter.status=b.dataset.status; backlogPage=1; renderBacklog(); }));
|
|
958
977
|
$('#backlogSearch').addEventListener('input', e=>{ backlogFilter.q=e.target.value; backlogPage=1; renderBacklog(); });
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
<button class="vt-btn active" data-view="list" title="Grouped by phase"><span class="tab-ico" data-icon="backlog"></span><span>List</span></button>
|
|
66
66
|
<button class="vt-btn" data-view="kanban" title="Columns by status"><span class="tab-ico" data-icon="board"></span><span>Kanban</span></button>
|
|
67
67
|
</div>
|
|
68
|
+
<button class="mini-btn" id="phaseToggleAll" title="Expand or collapse all phases">Expand all</button>
|
|
68
69
|
<input type="search" id="search" class="search" placeholder="Filter tasks…" autocomplete="off" />
|
|
69
70
|
</div>
|
|
70
71
|
<div class="board" id="board"></div>
|
|
@@ -815,3 +815,9 @@ body.booting .wf-step2 { opacity:0; animation:rise .4s cubic-bezier(.2,.8,.2,1)
|
|
|
815
815
|
.kanban-col-body { display:flex; flex-direction:column; gap:8px; padding:10px; }
|
|
816
816
|
.kanban-empty { color:var(--faint); font-size:12px; text-align:center; padding:10px 0; }
|
|
817
817
|
.kanban .task { width:auto; }
|
|
818
|
+
|
|
819
|
+
/* ---- board compactness: expand/collapse-all button + kanban column scroll (v0.13.5) ---- */
|
|
820
|
+
.mini-btn { font:inherit; font-size:11.5px; padding:5px 12px; border:1px solid var(--line); border-radius:999px; background:var(--surface); color:var(--muted); cursor:pointer; flex-shrink:0; }
|
|
821
|
+
.mini-btn:hover { color:var(--ink); border-color:var(--cool); }
|
|
822
|
+
.main:has(.board.is-kanban) #phaseToggleAll { display:none; } /* phases are a List concept */
|
|
823
|
+
.kanban-col-body { max-height:64vh; overflow-y:auto; } /* keep columns compact on a big project */
|