shadok-ai 0.12.6 → 0.12.8
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/index.html +56 -1
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -504,7 +504,19 @@
|
|
|
504
504
|
border-radius: 0 4px 4px 0;
|
|
505
505
|
color: var(--text);
|
|
506
506
|
}
|
|
507
|
-
|
|
507
|
+
/* Background work still running (shells and/or monitors the agent left going,
|
|
508
|
+
the ⏳/👁 on the row): a green diagonal HATCH that breathes, the way
|
|
509
|
+
.working breathes amber — so a tab with something ticking in the background
|
|
510
|
+
reads at a glance, distinct from the solid fill of an active turn. Layered as
|
|
511
|
+
a background-IMAGE, not the `background` shorthand, so a tab that is ALSO
|
|
512
|
+
working / needs-answer / unread keeps its state colour underneath the hatch. */
|
|
513
|
+
.tab.has-bg {
|
|
514
|
+
background-image: repeating-linear-gradient(45deg,
|
|
515
|
+
color-mix(in srgb, var(--ok) 22%, transparent) 0 5px,
|
|
516
|
+
transparent 5px 10px);
|
|
517
|
+
animation: breathe 1.8s ease-in-out infinite;
|
|
518
|
+
}
|
|
519
|
+
@media (prefers-reduced-motion: reduce) { .tab.working, .tab.has-bg { animation: none; } }
|
|
508
520
|
#newTab, #newGroup {
|
|
509
521
|
margin-top: 8px;
|
|
510
522
|
border: 1px dashed var(--line);
|
|
@@ -897,6 +909,22 @@
|
|
|
897
909
|
overflow-x: auto;
|
|
898
910
|
}
|
|
899
911
|
.bubble.md pre code { background: none; padding: 0; }
|
|
912
|
+
/* Copy on a fenced code block. The button sits on a WRAPPER around the <pre>,
|
|
913
|
+
not inside it: the <pre> scrolls sideways, and a button within would scroll
|
|
914
|
+
away. Always visible (a phone has no hover), small, top-right — the <pre>'s
|
|
915
|
+
own top padding keeps the first line clear of it. */
|
|
916
|
+
.bubble.md .md-code { position: relative; }
|
|
917
|
+
.bubble.md .md-code-copy {
|
|
918
|
+
position: absolute; top: 6px; right: 6px;
|
|
919
|
+
padding: 2px 7px; font-family: var(--mono); font-size: 10px; line-height: 1.4;
|
|
920
|
+
cursor: pointer; background: var(--bg-inset); border: 1px solid var(--line);
|
|
921
|
+
border-radius: 6px; color: var(--text-dim); opacity: 0.75;
|
|
922
|
+
transition: color 0.12s, border-color 0.12s, opacity 0.12s;
|
|
923
|
+
}
|
|
924
|
+
.bubble.md .md-code-copy:hover, .bubble.md .md-code-copy:focus-visible {
|
|
925
|
+
opacity: 1; color: var(--amber); border-color: var(--amber);
|
|
926
|
+
}
|
|
927
|
+
.bubble.md .md-code-copy.done { opacity: 1; color: var(--ok, #7ac77a); border-color: currentColor; }
|
|
900
928
|
/* The horizontal scroll for a wide table lives on this wrapper (added in JS
|
|
901
929
|
after sanitising), NOT on the <table>: `display:block` on a table breaks its
|
|
902
930
|
own layout — thead and tbody stop sharing column widths, so the header row
|
|
@@ -2630,6 +2658,9 @@
|
|
|
2630
2658
|
paintCount(tab.monitorsEl, Number(monitors) || 0, "monitor");
|
|
2631
2659
|
tab.shells = Number(shells) || 0;
|
|
2632
2660
|
tab.monitors = Number(monitors) || 0;
|
|
2661
|
+
// The green hatched breathe on the whole tab: on as soon as anything is left
|
|
2662
|
+
// running in the background, off when the last one stops.
|
|
2663
|
+
tab.btnEl.classList.toggle("has-bg", tab.shells > 0 || tab.monitors > 0);
|
|
2633
2664
|
}
|
|
2634
2665
|
function paintCount(el, n, noun) {
|
|
2635
2666
|
if (!el) return;
|
|
@@ -4144,6 +4175,30 @@
|
|
|
4144
4175
|
t.replaceWith(wrap);
|
|
4145
4176
|
wrap.appendChild(t);
|
|
4146
4177
|
}
|
|
4178
|
+
// A copy button on every fenced code block — the thing you most often want
|
|
4179
|
+
// to lift out of an answer. It goes on a wrapper AROUND the <pre> (the
|
|
4180
|
+
// <pre> scrolls; a button inside would scroll off), and copies the code's
|
|
4181
|
+
// own text, not the whole message.
|
|
4182
|
+
for (const pre of bubble.querySelectorAll("pre")) {
|
|
4183
|
+
const code = pre.querySelector("code") || pre;
|
|
4184
|
+
const wrap = document.createElement("div");
|
|
4185
|
+
wrap.className = "md-code";
|
|
4186
|
+
pre.replaceWith(wrap);
|
|
4187
|
+
wrap.appendChild(pre);
|
|
4188
|
+
const btn = document.createElement("button");
|
|
4189
|
+
btn.type = "button";
|
|
4190
|
+
btn.className = "md-code-copy";
|
|
4191
|
+
btn.textContent = "⧉ copy";
|
|
4192
|
+
btn.title = "Copy this code";
|
|
4193
|
+
btn.addEventListener("click", async (e) => {
|
|
4194
|
+
e.stopPropagation();
|
|
4195
|
+
const ok = await copyText(code.textContent.replace(/\n$/, ""));
|
|
4196
|
+
btn.textContent = ok ? "✓ copied" : "⚠ failed";
|
|
4197
|
+
btn.classList.toggle("done", ok);
|
|
4198
|
+
setTimeout(() => { btn.textContent = "⧉ copy"; btn.classList.remove("done"); }, 1500);
|
|
4199
|
+
});
|
|
4200
|
+
wrap.appendChild(btn);
|
|
4201
|
+
}
|
|
4147
4202
|
} else {
|
|
4148
4203
|
bubble.textContent = text;
|
|
4149
4204
|
}
|