shadok-ai 0.2.7 → 0.2.9
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 +58 -2
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -869,6 +869,25 @@
|
|
|
869
869
|
#chanBar .gauge { flex-direction: row; align-items: baseline; gap: 4px; }
|
|
870
870
|
#chanBar .gauge .label { font-size: 10px; }
|
|
871
871
|
#chanBar #cwdView { display: inline-block; max-width: 200px; overflow: hidden; text-overflow: ellipsis; vertical-align: bottom; }
|
|
872
|
+
/* Sélecteur de canal : masqué sur desktop (la sidebar est la navigation). */
|
|
873
|
+
#chanSelect {
|
|
874
|
+
display: none;
|
|
875
|
+
font-family: var(--mono); font-size: 13px;
|
|
876
|
+
background: var(--bg-inset); color: var(--text);
|
|
877
|
+
border: 1px solid var(--line); border-radius: 6px; padding: 5px 8px;
|
|
878
|
+
max-width: 55vw;
|
|
879
|
+
}
|
|
880
|
+
/* ── Mobile : sidebar cachée, navigation par le sélecteur du header canal ── */
|
|
881
|
+
@media (max-width: 640px) {
|
|
882
|
+
#tabbar { display: none; }
|
|
883
|
+
#chanSelect { display: inline-block; }
|
|
884
|
+
#chanBar #chanBarName { display: none; }
|
|
885
|
+
/* Bandeau canal condensé : on garde Status + Session, on cache le reste. */
|
|
886
|
+
#chanBar #dirGauge, #chanBar #branchGauge { display: none !important; }
|
|
887
|
+
#chanBar .gauge .label { display: none; } /* valeurs seules, plus compact */
|
|
888
|
+
#chanBar { gap: 6px; padding: 6px 10px; }
|
|
889
|
+
header { flex-wrap: wrap; row-gap: 4px; }
|
|
890
|
+
}
|
|
872
891
|
#xtermHost { flex: 1; min-height: 0; overflow: auto; padding: 8px 10px; }
|
|
873
892
|
#xtermHost .xterm { height: 100%; }
|
|
874
893
|
/* Aucun agent ouvert. Le panneau central n'affiche plus un formulaire à demi
|
|
@@ -1320,11 +1339,13 @@
|
|
|
1320
1339
|
séparé, donc ses actions n'ont pas de raison d'apparaître avec lui. -->
|
|
1321
1340
|
<div id="chanBar" hidden>
|
|
1322
1341
|
<span class="label" id="chanBarName"></span>
|
|
1342
|
+
<!-- Mobile: sidebar cachée → ce sélecteur change de canal / en crée un. -->
|
|
1343
|
+
<select id="chanSelect" title="Canal"></select>
|
|
1323
1344
|
<div class="gauge">
|
|
1324
1345
|
<span class="label">Status</span>
|
|
1325
1346
|
<span class="value"><span id="led" class="led"></span><span id="state">—</span></span>
|
|
1326
1347
|
</div>
|
|
1327
|
-
<div class="gauge">
|
|
1348
|
+
<div class="gauge" id="dirGauge">
|
|
1328
1349
|
<span class="label">Directory</span>
|
|
1329
1350
|
<span class="value" id="cwdView">—</span>
|
|
1330
1351
|
</div>
|
|
@@ -1646,6 +1667,7 @@
|
|
|
1646
1667
|
tab.nameEl.textContent = v;
|
|
1647
1668
|
saveName(tab, v);
|
|
1648
1669
|
persistChannels();
|
|
1670
|
+
rebuildChanSelect(); // le nom change → mettre à jour le sélecteur mobile
|
|
1649
1671
|
});
|
|
1650
1672
|
}
|
|
1651
1673
|
|
|
@@ -2329,7 +2351,33 @@
|
|
|
2329
2351
|
* un formulaire à la place. Tout ce qui décrit un canal retombe alors sur « — »
|
|
2330
2352
|
* et le panneau central montre l'invitation.
|
|
2331
2353
|
*/
|
|
2354
|
+
/** (Re)construit le sélecteur mobile : une option par canal + « + New agent ». */
|
|
2355
|
+
function rebuildChanSelect() {
|
|
2356
|
+
const sel = $("chanSelect");
|
|
2357
|
+
if (!sel) return;
|
|
2358
|
+
const cur = active ? String(active.id) : "";
|
|
2359
|
+
sel.innerHTML = "";
|
|
2360
|
+
for (const t of tabs) {
|
|
2361
|
+
const o = document.createElement("option");
|
|
2362
|
+
o.value = String(t.id);
|
|
2363
|
+
o.textContent = t.nameEl ? t.nameEl.textContent : "agent " + t.id;
|
|
2364
|
+
sel.appendChild(o);
|
|
2365
|
+
}
|
|
2366
|
+
const nw = document.createElement("option");
|
|
2367
|
+
nw.value = "__new__";
|
|
2368
|
+
nw.textContent = "+ New agent";
|
|
2369
|
+
sel.appendChild(nw);
|
|
2370
|
+
sel.value = cur;
|
|
2371
|
+
}
|
|
2372
|
+
$("chanSelect").addEventListener("change", () => {
|
|
2373
|
+
const v = $("chanSelect").value;
|
|
2374
|
+
if (v === "__new__") { createTab(); return; }
|
|
2375
|
+
const t = tabs.find((x) => String(x.id) === v);
|
|
2376
|
+
if (t) activate(t);
|
|
2377
|
+
});
|
|
2378
|
+
|
|
2332
2379
|
function refreshChrome() {
|
|
2380
|
+
rebuildChanSelect(); // garde le sélecteur mobile en phase (liste + actif)
|
|
2333
2381
|
const t = active;
|
|
2334
2382
|
if (!t) {
|
|
2335
2383
|
$("led").className = "led ";
|
|
@@ -3596,6 +3644,14 @@
|
|
|
3596
3644
|
for (let i = 0; i < bin.length; i++) a[i] = bin.charCodeAt(i);
|
|
3597
3645
|
return a;
|
|
3598
3646
|
}
|
|
3647
|
+
// Keystroke → base64, UTF-8 first. btoa() alone treats the string as Latin1,
|
|
3648
|
+
// so "é" (U+00E9) went out as the single byte 0xE9 instead of its UTF-8 pair
|
|
3649
|
+
// 0xC3 0xA9 → mojibake in the pane; anything above U+00FF (emoji…) threw.
|
|
3650
|
+
function strToB64Utf8(s) {
|
|
3651
|
+
let bin = "";
|
|
3652
|
+
for (const b of new TextEncoder().encode(s)) bin += String.fromCharCode(b);
|
|
3653
|
+
return btoa(bin);
|
|
3654
|
+
}
|
|
3599
3655
|
function updateTermBtn() {
|
|
3600
3656
|
const on = !!(active && active.termOn);
|
|
3601
3657
|
const b = $("chanTerm");
|
|
@@ -3621,7 +3677,7 @@
|
|
|
3621
3677
|
term.open($("xtermHost"));
|
|
3622
3678
|
term.onData((d) => {
|
|
3623
3679
|
if (term && termSession && termSession.ws)
|
|
3624
|
-
termSession.ws.send(JSON.stringify({ type: "term-input", data:
|
|
3680
|
+
termSession.ws.send(JSON.stringify({ type: "term-input", data: strToB64Utf8(d) }));
|
|
3625
3681
|
});
|
|
3626
3682
|
}
|
|
3627
3683
|
if (termSession !== active) {
|