shadok-ai 0.12.9 → 0.12.10
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 +80 -31
- package/public/tour-steps.js +8 -10
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -275,7 +275,8 @@
|
|
|
275
275
|
.led.busy { background: var(--amber); box-shadow: 0 0 8px var(--amber); animation: breathe 1.2s ease-in-out infinite; }
|
|
276
276
|
.led.off { background: var(--err); }
|
|
277
277
|
@keyframes breathe { 50% { opacity: 0.35; } }
|
|
278
|
-
@
|
|
278
|
+
@keyframes sheet-up { from { transform: translateY(100%); } to { transform: translateY(0); } }
|
|
279
|
+
@media (prefers-reduced-motion: reduce) { .led.busy { animation: none; } body.picker-open #tabbar { animation: none; } }
|
|
279
280
|
header .spacer { flex: 1; }
|
|
280
281
|
/* Quota dials (5h / 7d subscription usage).
|
|
281
282
|
A 240° speedometer whose CENTRE is the ideal pace and whose RIGHT END is
|
|
@@ -1178,17 +1179,43 @@
|
|
|
1178
1179
|
#chanBar .gauge .label { font-size: 10px; }
|
|
1179
1180
|
#chanBar #cwdView { display: inline-block; max-width: 200px; overflow: hidden; text-overflow: ellipsis; vertical-align: bottom; }
|
|
1180
1181
|
/* Channel selector: hidden on desktop (the sidebar is the navigation). */
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1182
|
+
/* Mobile agents trigger: hidden on desktop (the sidebar IS the navigation),
|
|
1183
|
+
shown as a compact "● name ▾" pill on a phone. */
|
|
1184
|
+
#chanPicker {
|
|
1185
|
+
display: none; align-items: center; gap: 6px;
|
|
1186
|
+
font-family: var(--mono); font-size: 13px; cursor: pointer;
|
|
1184
1187
|
background: var(--bg-inset); color: var(--text);
|
|
1185
1188
|
border: 1px solid var(--line); border-radius: 6px; padding: 5px 8px;
|
|
1186
1189
|
max-width: 55vw;
|
|
1187
1190
|
}
|
|
1191
|
+
#chanPicker #pickerName { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1192
|
+
#chanPicker .pk-caret { color: var(--text-dim); font-size: 11px; flex: none; transition: transform 0.15s; }
|
|
1193
|
+
body.picker-open #chanPicker { border-color: var(--amber); }
|
|
1194
|
+
body.picker-open #chanPicker .pk-caret { transform: rotate(180deg); }
|
|
1188
1195
|
/* ── Mobile: sidebar hidden, navigation via the channel header's selector ── */
|
|
1189
1196
|
@media (max-width: 640px) {
|
|
1190
1197
|
#tabbar { display: none; }
|
|
1191
|
-
#
|
|
1198
|
+
#chanPicker { display: inline-flex; min-width: 0; }
|
|
1199
|
+
/* The agents column, on demand, as a bottom sheet — the SAME rich tabs, not a
|
|
1200
|
+
flattened list. Slides up over the chat; a backdrop dims and closes it. */
|
|
1201
|
+
body.picker-open #tabbar {
|
|
1202
|
+
display: flex;
|
|
1203
|
+
position: fixed; left: 0; right: 0; bottom: 0; top: auto;
|
|
1204
|
+
width: auto; max-height: 78vh; z-index: 55;
|
|
1205
|
+
border-right: none; border-top: 1px solid var(--line);
|
|
1206
|
+
border-radius: 14px 14px 0 0;
|
|
1207
|
+
box-shadow: 0 -12px 40px rgba(0, 0, 0, 0.5);
|
|
1208
|
+
padding: 8px 8px calc(12px + env(safe-area-inset-bottom, 0px));
|
|
1209
|
+
animation: sheet-up 0.18s ease-out;
|
|
1210
|
+
}
|
|
1211
|
+
/* A grab handle, so the panel reads as a sheet you can dismiss. */
|
|
1212
|
+
body.picker-open #tabbar::before {
|
|
1213
|
+
content: ""; align-self: center; flex: none;
|
|
1214
|
+
width: 36px; height: 4px; margin: 2px 0 8px;
|
|
1215
|
+
border-radius: 2px; background: var(--line);
|
|
1216
|
+
}
|
|
1217
|
+
#pickerBackdrop { position: fixed; inset: 0; z-index: 54; background: rgba(8, 9, 13, 0.5); }
|
|
1218
|
+
#pickerBackdrop[hidden] { display: none; }
|
|
1192
1219
|
#chanBar #chanBarName { display: none; }
|
|
1193
1220
|
/* Condensed channel bar: keep Status + Session, hide the rest. */
|
|
1194
1221
|
#chanBar #dirGauge, #chanBar #branchGauge { display: none !important; }
|
|
@@ -1241,7 +1268,7 @@
|
|
|
1241
1268
|
@media (pointer: coarse) {
|
|
1242
1269
|
textarea, select,
|
|
1243
1270
|
input[type="text"], input[type="password"], input[type="number"], input[type="search"],
|
|
1244
|
-
#composer textarea
|
|
1271
|
+
#composer textarea { font-size: 16px; }
|
|
1245
1272
|
}
|
|
1246
1273
|
#xtermHost { flex: 1; min-height: 0; overflow: auto; padding: 8px 10px; }
|
|
1247
1274
|
#xtermHost .xterm { height: 100%; }
|
|
@@ -2022,6 +2049,8 @@
|
|
|
2022
2049
|
</button>
|
|
2023
2050
|
</div>
|
|
2024
2051
|
</nav>
|
|
2052
|
+
<!-- Dims the chat behind the mobile agents sheet; tap to close. -->
|
|
2053
|
+
<div id="pickerBackdrop" hidden></div>
|
|
2025
2054
|
|
|
2026
2055
|
<section class="chat-pane">
|
|
2027
2056
|
|
|
@@ -2030,8 +2059,12 @@
|
|
|
2030
2059
|
separate place, so its actions have no reason to appear with it. -->
|
|
2031
2060
|
<div id="chanBar" hidden>
|
|
2032
2061
|
<span class="label" id="chanBarName"></span>
|
|
2033
|
-
<!-- Mobile: sidebar hidden → this
|
|
2034
|
-
|
|
2062
|
+
<!-- Mobile: the sidebar is hidden → this button opens the very same rich
|
|
2063
|
+
agent tabs (LED, state, background-work hatch, context bar) as a bottom
|
|
2064
|
+
sheet, instead of flattening them into a native <select>. -->
|
|
2065
|
+
<button id="chanPicker" type="button" aria-haspopup="true" aria-expanded="false" title="Agents">
|
|
2066
|
+
<span id="pickerLed" class="led"></span><span id="pickerName">—</span><span class="pk-caret">▾</span>
|
|
2067
|
+
</button>
|
|
2035
2068
|
<div class="gauge">
|
|
2036
2069
|
<span class="label">Status</span>
|
|
2037
2070
|
<span class="value"><span id="led" class="led"></span><span id="state">—</span></span>
|
|
@@ -2787,7 +2820,7 @@
|
|
|
2787
2820
|
document.addEventListener("click", () => {
|
|
2788
2821
|
if (Date.now() - tabMenuOpenedAt > 200) closeTabMenu();
|
|
2789
2822
|
});
|
|
2790
|
-
document.addEventListener("keydown", (e) => { if (e.key === "Escape") closeTabMenu(); });
|
|
2823
|
+
document.addEventListener("keydown", (e) => { if (e.key === "Escape") { closeTabMenu(); closePicker(); } });
|
|
2791
2824
|
window.addEventListener("blur", () => closeTabMenu());
|
|
2792
2825
|
|
|
2793
2826
|
/** Right-click menu on a tab: the home for per-channel actions. */
|
|
@@ -3460,6 +3493,8 @@
|
|
|
3460
3493
|
promptInput.value = tab.draft;
|
|
3461
3494
|
autosizePrompt();
|
|
3462
3495
|
refreshChrome();
|
|
3496
|
+
// Picking an agent from the mobile sheet dismisses it.
|
|
3497
|
+
closePicker();
|
|
3463
3498
|
// Experimental terminal follows the active channel (re-seeds on switch).
|
|
3464
3499
|
if (typeof syncTerm === "function") syncTerm();
|
|
3465
3500
|
if (tab.status !== "setup") {
|
|
@@ -3866,29 +3901,39 @@
|
|
|
3866
3901
|
* central panel shows the invitation.
|
|
3867
3902
|
*/
|
|
3868
3903
|
/** (Re)builds the mobile selector: one option per channel + "+ New agent". */
|
|
3904
|
+
// The mobile trigger just MIRRORS the active agent (its name + LED). The sheet
|
|
3905
|
+
// it opens is the real sidebar (#tabbar), so there is no flattened list to
|
|
3906
|
+
// rebuild here — every rich tab renders itself. Name kept for its callers.
|
|
3869
3907
|
function rebuildChanSelect() {
|
|
3870
|
-
const
|
|
3871
|
-
if (!
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
}
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
}
|
|
3908
|
+
const led = $("pickerLed"), name = $("pickerName");
|
|
3909
|
+
if (!led || !name) return;
|
|
3910
|
+
led.className = active && active.ledEl ? active.ledEl.className : "led";
|
|
3911
|
+
name.textContent = active ? (active.nameEl ? active.nameEl.textContent : "agent " + active.id) : "—";
|
|
3912
|
+
}
|
|
3913
|
+
// ── Mobile agents sheet ──────────────────────────────────────────────────
|
|
3914
|
+
// On a phone the sidebar is hidden; this opens it as a bottom sheet so the
|
|
3915
|
+
// channels keep their full rendering (status, background-work hatch, context)
|
|
3916
|
+
// instead of collapsing into a native <select>.
|
|
3917
|
+
function isMobileWidth() { return matchMedia("(max-width: 640px)").matches; }
|
|
3918
|
+
function openPicker() {
|
|
3919
|
+
if (!isMobileWidth()) return;
|
|
3920
|
+
document.body.classList.add("picker-open");
|
|
3921
|
+
$("pickerBackdrop").hidden = false;
|
|
3922
|
+
$("chanPicker").setAttribute("aria-expanded", "true");
|
|
3923
|
+
}
|
|
3924
|
+
function closePicker() {
|
|
3925
|
+
if (!document.body.classList.contains("picker-open")) return;
|
|
3926
|
+
document.body.classList.remove("picker-open");
|
|
3927
|
+
$("pickerBackdrop").hidden = true;
|
|
3928
|
+
$("chanPicker").setAttribute("aria-expanded", "false");
|
|
3929
|
+
}
|
|
3930
|
+
function togglePicker() {
|
|
3931
|
+
document.body.classList.contains("picker-open") ? closePicker() : openPicker();
|
|
3932
|
+
}
|
|
3933
|
+
$("chanPicker").addEventListener("click", togglePicker);
|
|
3934
|
+
$("pickerBackdrop").addEventListener("click", closePicker);
|
|
3935
|
+
// Leaving phone width (rotation, resized window) must not strand an open sheet.
|
|
3936
|
+
addEventListener("resize", () => { if (!isMobileWidth()) closePicker(); });
|
|
3892
3937
|
|
|
3893
3938
|
function refreshChrome() {
|
|
3894
3939
|
rebuildChanSelect(); // keeps the mobile selector in step (list + active)
|
|
@@ -4774,6 +4819,9 @@
|
|
|
4774
4819
|
* of the conversation you were watching.
|
|
4775
4820
|
*/
|
|
4776
4821
|
function openSetup() {
|
|
4822
|
+
// Close the mobile agents sheet first: the setup overlay sits BELOW it in
|
|
4823
|
+
// the stack, so leaving the sheet open would hide the form behind it.
|
|
4824
|
+
closePicker();
|
|
4777
4825
|
// PREDICTABLE defaults on every opening. The browser restores form-field
|
|
4778
4826
|
// state (a "continue"/"resume" mode chosen once, an unticked worktree box, a
|
|
4779
4827
|
// worktree cwd) — which is what silently made "create an agent" fall back
|
|
@@ -7252,6 +7300,7 @@
|
|
|
7252
7300
|
|
|
7253
7301
|
tweakCta.addEventListener("click", async () => {
|
|
7254
7302
|
if (tweakBusy) return;
|
|
7303
|
+
closePicker(); // dismiss the mobile sheet; the tweak agent takes over the view
|
|
7255
7304
|
// Belt and braces: the CTA is hidden as soon as a tweak channel exists, but
|
|
7256
7305
|
// should it reappear we activate the existing one rather than open a second.
|
|
7257
7306
|
const already = tweakTab();
|
package/public/tour-steps.js
CHANGED
|
@@ -38,16 +38,14 @@ export const TOUR_STEPS = [
|
|
|
38
38
|
"Every agent gets its own git worktree and branch, so several never collide. " +
|
|
39
39
|
"Pick one here to follow it, and “+ New agent” starts another. One of them is this " +
|
|
40
40
|
"cockpit's home agent: the only one that cannot be closed.",
|
|
41
|
-
// The column on desktop, the
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
// same failure as a spotlight on empty space
|
|
48
|
-
|
|
49
|
-
// not in the tour.)
|
|
50
|
-
target: ["#tabbar", "#chanSelect"],
|
|
41
|
+
// The column on desktop, the agents-picker button on a phone — never both at
|
|
42
|
+
// once. The phone's `#chanPicker` opens the SAME column as a bottom sheet, so
|
|
43
|
+
// "+ New agent" and Tweak are reachable there too now — but the spotlight
|
|
44
|
+
// lands on the CLOSED button, whose contents aren't on screen yet. So this
|
|
45
|
+
// body still says neither "at the top" nor "at the bottom" and still doesn't
|
|
46
|
+
// point at "Tweak Shadok-AI": naming a control the reader cannot see at the
|
|
47
|
+
// moment of the spotlight is the same failure as a spotlight on empty space.
|
|
48
|
+
target: ["#tabbar", "#chanPicker"],
|
|
51
49
|
},
|
|
52
50
|
{
|
|
53
51
|
id: "tab",
|