@lyric_dev/data-loom 0.7.0 → 0.8.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/package.json +1 -1
- package/public/app.js +73 -1
- package/public/style.css +102 -0
package/package.json
CHANGED
package/public/app.js
CHANGED
|
@@ -180,8 +180,12 @@ function renderReview() {
|
|
|
180
180
|
reviewEl.appendChild(el("span", "review-badge", String(n)));
|
|
181
181
|
const txt = el("span", "review-text");
|
|
182
182
|
const subject = n > 1 ? "proposals need" : "proposal needs";
|
|
183
|
-
txt.innerHTML = `${n} ${subject} dependency review —
|
|
183
|
+
txt.innerHTML = `${n} ${subject} dependency review — copy the <b>weave</b> command to run it in Claude Code.`;
|
|
184
184
|
reviewEl.appendChild(txt);
|
|
185
|
+
const btn = el("button", "banner-action", "Weave");
|
|
186
|
+
btn.title = "Copy /loom:weave";
|
|
187
|
+
btn.addEventListener("click", () => copyCommand("/loom:weave"));
|
|
188
|
+
reviewEl.appendChild(btn);
|
|
185
189
|
}
|
|
186
190
|
|
|
187
191
|
function clearBoard() {
|
|
@@ -272,6 +276,16 @@ function renderCard(c) {
|
|
|
272
276
|
const text = [capsParts.join(" "), tasks].filter(Boolean).join(" · ");
|
|
273
277
|
body.appendChild(el("div", "gcard-caps", text));
|
|
274
278
|
}
|
|
279
|
+
const action = changeAction(c);
|
|
280
|
+
if (action) {
|
|
281
|
+
const btn = el("button", "card-action " + action.kind, action.label);
|
|
282
|
+
btn.title = "Copy " + action.command;
|
|
283
|
+
btn.addEventListener("click", (ev) => {
|
|
284
|
+
ev.stopPropagation(); // copy only — don't also select the card / open detail
|
|
285
|
+
copyCommand(action.command);
|
|
286
|
+
});
|
|
287
|
+
body.appendChild(btn);
|
|
288
|
+
}
|
|
275
289
|
card.appendChild(body);
|
|
276
290
|
|
|
277
291
|
if (nextUpNames.has(c.name)) card.appendChild(el("div", "gcard-next", "NEXT UP"));
|
|
@@ -524,6 +538,13 @@ function renderChangeDetail(c) {
|
|
|
524
538
|
pills.appendChild(rp);
|
|
525
539
|
inner.appendChild(pills);
|
|
526
540
|
|
|
541
|
+
const action = changeAction(c);
|
|
542
|
+
if (action) {
|
|
543
|
+
const btn = el("button", "detail-action " + action.kind, "Copy " + action.command);
|
|
544
|
+
btn.addEventListener("click", () => copyCommand(action.command));
|
|
545
|
+
inner.appendChild(btn);
|
|
546
|
+
}
|
|
547
|
+
|
|
527
548
|
if (c.totalTasks > 0) {
|
|
528
549
|
const pct = Math.round((c.completedTasks / c.totalTasks) * 100);
|
|
529
550
|
const tasks = el("div", "detail-tasks");
|
|
@@ -607,6 +628,57 @@ function itemList(items, cls, prefix) {
|
|
|
607
628
|
return list;
|
|
608
629
|
}
|
|
609
630
|
|
|
631
|
+
// ═══════════════ command handoff (clipboard + toast) ═══════════════
|
|
632
|
+
|
|
633
|
+
// A single reused toast node, auto-dismissed. DataLoom prepares a command; the
|
|
634
|
+
// user runs it in their own Claude Code session — so a copy is confirmed here,
|
|
635
|
+
// never executed.
|
|
636
|
+
let toastEl = null;
|
|
637
|
+
let toastTimer = null;
|
|
638
|
+
function showToast(message, tone) {
|
|
639
|
+
if (!toastEl) {
|
|
640
|
+
toastEl = el("div", "toast");
|
|
641
|
+
document.body.appendChild(toastEl);
|
|
642
|
+
}
|
|
643
|
+
toastEl.textContent = message;
|
|
644
|
+
toastEl.className = "toast" + (tone ? " " + tone : "");
|
|
645
|
+
// force reflow so re-triggering the transition restarts it
|
|
646
|
+
void toastEl.offsetWidth;
|
|
647
|
+
toastEl.classList.add("show");
|
|
648
|
+
clearTimeout(toastTimer);
|
|
649
|
+
toastTimer = setTimeout(() => toastEl.classList.remove("show"), 3200);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// Copy a Claude Code command to the clipboard and confirm via a toast. Purely
|
|
653
|
+
// client-side: no network call, no daemon/agent activity. On localhost the
|
|
654
|
+
// Clipboard API is available (secure context); a rejection or its absence falls
|
|
655
|
+
// back to showing the command for manual copy.
|
|
656
|
+
async function copyCommand(command) {
|
|
657
|
+
try {
|
|
658
|
+
await navigator.clipboard.writeText(command);
|
|
659
|
+
showToast(`Copied ${command} — paste into Claude Code`);
|
|
660
|
+
} catch {
|
|
661
|
+
showToast(`Couldn't copy — run this in Claude Code: ${command}`, "warn");
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// The command action a change offers, derived from state the model already
|
|
666
|
+
// carries. Kept in one place so a card and the detail panel always agree:
|
|
667
|
+
// archived → nothing
|
|
668
|
+
// tasks all complete → archive
|
|
669
|
+
// ready (incomplete) → apply
|
|
670
|
+
// otherwise → nothing
|
|
671
|
+
function changeAction(c) {
|
|
672
|
+
if (c.archived) return null;
|
|
673
|
+
if (c.totalTasks > 0 && c.completedTasks === c.totalTasks) {
|
|
674
|
+
return { kind: "archive", label: "Archive", command: "/opsx:archive " + c.name };
|
|
675
|
+
}
|
|
676
|
+
if (c.readiness === "ready") {
|
|
677
|
+
return { kind: "apply", label: "Apply", command: "/opsx:apply " + c.name };
|
|
678
|
+
}
|
|
679
|
+
return null;
|
|
680
|
+
}
|
|
681
|
+
|
|
610
682
|
// ═══════════════ helpers ═══════════════
|
|
611
683
|
|
|
612
684
|
function statusColor(status) {
|
package/public/style.css
CHANGED
|
@@ -540,6 +540,108 @@ main {
|
|
|
540
540
|
color: var(--text);
|
|
541
541
|
font-family: var(--prose);
|
|
542
542
|
}
|
|
543
|
+
.banner-action {
|
|
544
|
+
flex: none;
|
|
545
|
+
margin-left: auto;
|
|
546
|
+
font-family: var(--prose);
|
|
547
|
+
font-weight: 600;
|
|
548
|
+
font-size: 12px;
|
|
549
|
+
padding: 6px 15px;
|
|
550
|
+
border-radius: 8px;
|
|
551
|
+
border: 1px solid var(--accent-bd);
|
|
552
|
+
background: var(--panel);
|
|
553
|
+
color: var(--accent);
|
|
554
|
+
cursor: pointer;
|
|
555
|
+
transition: background 0.15s, color 0.15s;
|
|
556
|
+
}
|
|
557
|
+
.banner-action:hover {
|
|
558
|
+
background: var(--accent);
|
|
559
|
+
color: var(--on-accent);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/* ===== Command handoff: card/detail actions + toast ===== */
|
|
563
|
+
.card-action {
|
|
564
|
+
align-self: flex-start;
|
|
565
|
+
margin-top: 10px;
|
|
566
|
+
font-family: var(--prose);
|
|
567
|
+
font-weight: 600;
|
|
568
|
+
font-size: 11px;
|
|
569
|
+
padding: 4px 11px;
|
|
570
|
+
border-radius: 7px;
|
|
571
|
+
border: 1px solid var(--accent-bd);
|
|
572
|
+
background: var(--accent-soft);
|
|
573
|
+
color: var(--accent);
|
|
574
|
+
cursor: pointer;
|
|
575
|
+
transition: background 0.15s, color 0.15s;
|
|
576
|
+
}
|
|
577
|
+
.card-action:hover {
|
|
578
|
+
background: var(--accent);
|
|
579
|
+
color: var(--on-accent);
|
|
580
|
+
}
|
|
581
|
+
.card-action.archive {
|
|
582
|
+
border-color: var(--c-done);
|
|
583
|
+
color: var(--c-done);
|
|
584
|
+
background: transparent;
|
|
585
|
+
}
|
|
586
|
+
.card-action.archive:hover {
|
|
587
|
+
background: var(--c-done);
|
|
588
|
+
color: var(--on-accent);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.detail-action {
|
|
592
|
+
margin-top: 16px;
|
|
593
|
+
width: 100%;
|
|
594
|
+
font-family: var(--prose);
|
|
595
|
+
font-weight: 600;
|
|
596
|
+
font-size: 12.5px;
|
|
597
|
+
padding: 9px;
|
|
598
|
+
border-radius: 9px;
|
|
599
|
+
border: 1px solid var(--accent-bd);
|
|
600
|
+
background: var(--accent-soft);
|
|
601
|
+
color: var(--accent);
|
|
602
|
+
cursor: pointer;
|
|
603
|
+
transition: background 0.15s, color 0.15s;
|
|
604
|
+
}
|
|
605
|
+
.detail-action:hover {
|
|
606
|
+
background: var(--accent);
|
|
607
|
+
color: var(--on-accent);
|
|
608
|
+
}
|
|
609
|
+
.detail-action.archive {
|
|
610
|
+
border-color: var(--c-done);
|
|
611
|
+
color: var(--c-done);
|
|
612
|
+
}
|
|
613
|
+
.detail-action.archive:hover {
|
|
614
|
+
background: var(--c-done);
|
|
615
|
+
color: var(--on-accent);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
.toast {
|
|
619
|
+
position: fixed;
|
|
620
|
+
left: 50%;
|
|
621
|
+
bottom: 26px;
|
|
622
|
+
transform: translate(-50%, 12px);
|
|
623
|
+
max-width: min(70vw, 480px);
|
|
624
|
+
padding: 11px 16px;
|
|
625
|
+
border-radius: 10px;
|
|
626
|
+
background: var(--panel);
|
|
627
|
+
border: 1px solid var(--accent-bd);
|
|
628
|
+
color: var(--text);
|
|
629
|
+
font-family: var(--mono);
|
|
630
|
+
font-size: 12px;
|
|
631
|
+
box-shadow: var(--shadow);
|
|
632
|
+
opacity: 0;
|
|
633
|
+
pointer-events: none;
|
|
634
|
+
z-index: 40;
|
|
635
|
+
transition: opacity 0.18s ease, transform 0.18s ease;
|
|
636
|
+
}
|
|
637
|
+
.toast.show {
|
|
638
|
+
opacity: 1;
|
|
639
|
+
transform: translate(-50%, 0);
|
|
640
|
+
}
|
|
641
|
+
.toast.warn {
|
|
642
|
+
border-color: var(--c-warn);
|
|
643
|
+
color: var(--c-warn);
|
|
644
|
+
}
|
|
543
645
|
|
|
544
646
|
/* ===== MCP Topology: circuit board ===== */
|
|
545
647
|
.topo-controls {
|