dashboard: render META INPUTS as a full tree with bulk select
Remove the depth-2 cap in walk_meta_inputs so every fetched input at every depth is surfaced, not just two levels (issue #275). The uncapped walk needs a guard: a visited-node set makes it a spanning tree — each fetched node walked once, at its shallowest path — so shared subtrees don't re-walk and a cycle can't recurse forever. A two-pass walk (claim a node's direct inputs before descending) keeps shallow inputs at a shallow path. Frontend: renderMetaInputs indents each row by its slash-path depth and shows the leaf segment (full path on hover), plus a select-all / select-none control so a long input list isn't ticked box by box.
This commit is contained in:
parent
dd3a820e57
commit
7f97acf19e
4 changed files with 102 additions and 32 deletions
|
|
@ -1520,9 +1520,22 @@
|
|||
'data-no-refresh': '',
|
||||
'data-confirm': 'update selected meta flake inputs + rebuild affected agents?',
|
||||
});
|
||||
// Bulk select — the full input tree gets long; ticking each box
|
||||
// one by one is tedious (issue #275).
|
||||
const bulk = el('div', { class: 'meta-inputs-bulk' });
|
||||
const selAll = el('button', { type: 'button', class: 'meta-bulk-btn' }, 'select all');
|
||||
const selNone = el('button', { type: 'button', class: 'meta-bulk-btn' }, 'select none');
|
||||
bulk.append('bulk: ', selAll, ' ', selNone);
|
||||
form.append(bulk);
|
||||
const ul = el('ul', { class: 'meta-inputs' });
|
||||
for (const inp of inputs) {
|
||||
// `name` is a slash-path from the meta root. Indent depth = its
|
||||
// segment count; the row label shows just the leaf segment, the
|
||||
// full path stays as the checkbox value + the label title.
|
||||
const depth = (inp.name.match(/\//g) || []).length;
|
||||
const leaf = inp.name.slice(inp.name.lastIndexOf('/') + 1);
|
||||
const li = el('li');
|
||||
if (depth > 0) li.style.marginLeft = (depth * 1.3) + 'em';
|
||||
const id = 'meta-input-' + inp.name.replace(/[^a-z0-9-]/gi, '_');
|
||||
const cb = el('input', {
|
||||
type: 'checkbox',
|
||||
|
|
@ -1531,10 +1544,11 @@
|
|||
value: inp.name,
|
||||
'data-meta-input': inp.name,
|
||||
});
|
||||
const label = el('label', { for: id });
|
||||
const label = el('label', { for: id, title: inp.name });
|
||||
label.append(cb);
|
||||
if (depth > 0) label.append(el('span', { class: 'meta-input-twig' }, '└ '));
|
||||
label.append(
|
||||
cb,
|
||||
el('span', { class: 'meta-input-name' }, inp.name), ' ',
|
||||
el('span', { class: 'meta-input-name' }, leaf), ' ',
|
||||
el('code', { class: 'meta-input-rev' }, inp.rev.slice(0, 12)), ' ',
|
||||
el('span', { class: 'meta-input-ts' }, fmtAgo(inp.last_modified)),
|
||||
);
|
||||
|
|
@ -1565,6 +1579,14 @@
|
|||
else btn.setAttribute('disabled', '');
|
||||
}
|
||||
form.addEventListener('change', refreshDisabled);
|
||||
function setAllChecked(val) {
|
||||
for (const b of form.querySelectorAll('input[data-meta-input]')) {
|
||||
b.checked = val;
|
||||
}
|
||||
refreshDisabled();
|
||||
}
|
||||
selAll.addEventListener('click', () => setAllChecked(true));
|
||||
selNone.addEventListener('click', () => setAllChecked(false));
|
||||
form.addEventListener('submit', () => {
|
||||
const selected = Array.from(form.querySelectorAll('input[data-meta-input]:checked'))
|
||||
.map((b) => b.dataset.metaInput);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue