shadok-ai 0.2.17 → 0.2.18
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/README.md +7 -5
- package/package.json +1 -1
- package/public/index.html +64 -17
package/README.md
CHANGED
|
@@ -63,15 +63,17 @@ Enter to skip; you can add it later from the web UI).
|
|
|
63
63
|
the chat can't express. With tmux there is also an **experimental real
|
|
64
64
|
terminal** (xterm.js over the pane's byte stream) when a snapshot isn't enough.
|
|
65
65
|
- **Diff panel** — what an agent actually changed, against its base.
|
|
66
|
-
- **Tweak Shadok-AI** — a card at the bottom of the agents column. One
|
|
67
|
-
clones shadok-ai's own source into `~/.shadok-ai/self/shadok-ai
|
|
68
|
-
agent on it in its own worktree
|
|
66
|
+
- **Tweak Shadok-AI** — a card pinned at the bottom of the agents column. One
|
|
67
|
+
click clones shadok-ai's own source into `~/.shadok-ai/self/shadok-ai` and
|
|
68
|
+
starts an agent on it in its own worktree; that agent delivers its change as a
|
|
69
69
|
**pull request** — a fork under your GitHub account, since you need no rights
|
|
70
70
|
on the repo. Nothing to configure and no token to paste: the clone is
|
|
71
71
|
anonymous, so you describe an idea, watch it work and read the diff first, and
|
|
72
72
|
it only asks for GitHub (via `gh auth login`, device code relayed in the chat)
|
|
73
|
-
when there is something worth pushing.
|
|
74
|
-
|
|
73
|
+
when there is something worth pushing. It is **one channel, not a launcher**:
|
|
74
|
+
the card becomes that channel's tab, with the usual menu — mute, reload,
|
|
75
|
+
rename, change profile, mirror to Telegram, close — and closing it brings the
|
|
76
|
+
card back. Desktop only: the agents column is hidden on phones.
|
|
75
77
|
- **Self-update** — polls npm and can update and reload itself in place.
|
|
76
78
|
|
|
77
79
|
### Telegram (optional)
|
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -374,10 +374,27 @@
|
|
|
374
374
|
bordure et les + des boutons fantômes, donc une carte pleine se lit comme
|
|
375
375
|
une action et non comme une session. Épinglée en bas (margin-top:auto +
|
|
376
376
|
sticky) pour rester atteignable quand la liste des agents défile. */
|
|
377
|
-
|
|
377
|
+
/* L'emplacement réservé au canal « tweak », épinglé en bas de la colonne. Il
|
|
378
|
+
porte tantôt le CTA (aucune session), tantôt l'ONGLET du canal lui-même —
|
|
379
|
+
qui est un `.tab` ordinaire déplacé ici, donc états, menu contextuel et
|
|
380
|
+
rattachement Telegram marchent sans code en double. */
|
|
381
|
+
#tweakSlot {
|
|
378
382
|
margin-top: auto;
|
|
379
383
|
position: sticky;
|
|
380
384
|
bottom: 0;
|
|
385
|
+
padding-top: 8px;
|
|
386
|
+
background: var(--bg-raised);
|
|
387
|
+
/* Sépare de la liste qui défile dessous, sans ajouter de filet. */
|
|
388
|
+
box-shadow: 0 -8px 10px -8px rgba(0, 0, 0, 0.6);
|
|
389
|
+
}
|
|
390
|
+
/* L'onglet du canal tweak garde la teinte de son emplacement, pour qu'on le
|
|
391
|
+
reconnaisse au même endroit qu'avant de l'avoir démarré. */
|
|
392
|
+
#tweakSlot .tab {
|
|
393
|
+
border: 1px solid var(--amber);
|
|
394
|
+
background: linear-gradient(var(--amber-soft), var(--amber-soft)), var(--bg-raised);
|
|
395
|
+
}
|
|
396
|
+
#tweakSlot .tab .name { color: var(--amber); }
|
|
397
|
+
#tweakCta {
|
|
381
398
|
display: flex;
|
|
382
399
|
flex-direction: column;
|
|
383
400
|
gap: 2px;
|
|
@@ -385,13 +402,8 @@
|
|
|
385
402
|
padding: 8px;
|
|
386
403
|
border: 1px solid var(--amber);
|
|
387
404
|
border-radius: 6px;
|
|
388
|
-
|
|
389
|
-
est sticky, et un fond translucide laisserait les onglets défiler au
|
|
390
|
-
travers. */
|
|
405
|
+
width: 100%;
|
|
391
406
|
background: linear-gradient(var(--amber-soft), var(--amber-soft)), var(--bg-raised);
|
|
392
|
-
/* Ombre douce vers le haut : sépare la carte de la liste qui passe dessous,
|
|
393
|
-
sans ajouter de filet qui doublerait la bordure. */
|
|
394
|
-
box-shadow: 0 -8px 10px -8px rgba(0, 0, 0, 0.6);
|
|
395
407
|
}
|
|
396
408
|
#tweakCta .tweak-title {
|
|
397
409
|
font-family: var(--mono);
|
|
@@ -404,10 +416,7 @@
|
|
|
404
416
|
/* Aucune animation permanente : un élément qui clignote ici entrerait en
|
|
405
417
|
concurrence avec les vrais signaux (onglet en attente, jauges) et
|
|
406
418
|
deviendrait du bruit. */
|
|
407
|
-
#tweakCta:hover {
|
|
408
|
-
border-color: var(--amber);
|
|
409
|
-
box-shadow: 0 -8px 10px -8px rgba(0, 0, 0, 0.6), 0 0 12px rgba(240, 168, 72, 0.18);
|
|
410
|
-
}
|
|
419
|
+
#tweakCta:hover { border-color: var(--amber); box-shadow: 0 0 12px rgba(240, 168, 72, 0.18); }
|
|
411
420
|
#tweakCta:hover .tweak-arrow { transform: translateX(2px); }
|
|
412
421
|
#tweakCta.busy { opacity: 0.7; cursor: default; }
|
|
413
422
|
#tweakCta.failed { border-color: var(--err); background: transparent; }
|
|
@@ -1411,11 +1420,13 @@
|
|
|
1411
1420
|
<div id="groups"></div>
|
|
1412
1421
|
<button id="newTab" title="New agent">+ new agent</button>
|
|
1413
1422
|
<button id="newGroup" title="New agent group">+ new group</button>
|
|
1414
|
-
<
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1423
|
+
<div id="tweakSlot">
|
|
1424
|
+
<button id="tweakCta" type="button"
|
|
1425
|
+
title="Change shadok-ai itself — the agent works on a branch and opens a pull request">
|
|
1426
|
+
<span class="tweak-title">🛠 Tweak Shadok-AI <span class="tweak-arrow">›</span></span>
|
|
1427
|
+
<span class="tweak-sub">change the cockpit itself</span>
|
|
1428
|
+
</button>
|
|
1429
|
+
</div>
|
|
1419
1430
|
</nav>
|
|
1420
1431
|
|
|
1421
1432
|
<section class="chat-pane">
|
|
@@ -2209,7 +2220,9 @@
|
|
|
2209
2220
|
e.dataTransfer.dropEffect = "move";
|
|
2210
2221
|
if (dragTabEl) {
|
|
2211
2222
|
const overTab = e.target.closest(".tab");
|
|
2212
|
-
|
|
2223
|
+
// L'emplacement du bas n'accueille QUE le canal tweak : sans ce garde, un
|
|
2224
|
+
// onglet traîné au-dessus s'y insérerait et se retrouverait épinglé là.
|
|
2225
|
+
if (overTab && overTab !== dragTabEl && !overTab.classList.contains("tweak")) {
|
|
2213
2226
|
const r = overTab.getBoundingClientRect();
|
|
2214
2227
|
const after = e.clientY > r.top + r.height / 2;
|
|
2215
2228
|
overTab.parentNode.insertBefore(dragTabEl, after ? overTab.nextSibling : overTab);
|
|
@@ -2330,6 +2343,9 @@
|
|
|
2330
2343
|
if (tab.reconnectTimer) clearTimeout(tab.reconnectTimer);
|
|
2331
2344
|
if (tab.ws && tab.ws.readyState === WebSocket.OPEN) tab.ws.close();
|
|
2332
2345
|
dropDraft(tab);
|
|
2346
|
+
// Le canal tweak fermé, son emplacement redevient le CTA — sinon le bas de
|
|
2347
|
+
// la colonne resterait vide et la fonctionnalité disparaîtrait sans retour.
|
|
2348
|
+
if (tab.isTweak) document.getElementById("tweakCta").hidden = false;
|
|
2333
2349
|
tab.btnEl.remove();
|
|
2334
2350
|
tab.transcriptEl.remove();
|
|
2335
2351
|
tabs = tabs.filter((t) => t !== tab);
|
|
@@ -2383,6 +2399,9 @@
|
|
|
2383
2399
|
nt.draft = savedDrafts[c.sessionId] || "";
|
|
2384
2400
|
if (c.branch) nt.branch = c.branch;
|
|
2385
2401
|
nt.profile = c.profile || null;
|
|
2402
|
+
// Un canal tweak né ailleurs (autre appareil, Telegram) reprend sa place
|
|
2403
|
+
// en bas plutôt que d'apparaître comme un agent ordinaire de la liste.
|
|
2404
|
+
if (nt.profile === "Shadok-Tweak" && !tweakTab()) markTweakTab(nt);
|
|
2386
2405
|
setCrons(nt, c.crons);
|
|
2387
2406
|
if (c.muted) setMuted(nt, true, false);
|
|
2388
2407
|
if (typeof c.mirror === "boolean" ? c.mirror : !!c.telegram) setMirror(nt, true, false);
|
|
@@ -4598,6 +4617,13 @@
|
|
|
4598
4617
|
// Un clic démarre un agent sur le code source de shadok-ai lui-même : le
|
|
4599
4618
|
// dossier, le worktree et le rôle sont tous déjà décidés, donc un formulaire
|
|
4600
4619
|
// ne ferait que demander d'approuver des choix qu'on ne peut pas juger.
|
|
4620
|
+
//
|
|
4621
|
+
// Le canal tweak est UNIQUE et vit dans son emplacement en bas de colonne : le
|
|
4622
|
+
// CTA n'est visible que tant qu'il n'existe pas. Une fois lancé, son onglet
|
|
4623
|
+
// prend la place du CTA — un `.tab` ordinaire simplement déplacé là, donc il
|
|
4624
|
+
// hérite sans code en double des états, du menu contextuel, du rattachement
|
|
4625
|
+
// Telegram, du renommage et de la fermeture.
|
|
4626
|
+
const tweakSlot = $("tweakSlot");
|
|
4601
4627
|
const tweakCta = $("tweakCta");
|
|
4602
4628
|
const tweakTitle = tweakCta.querySelector(".tweak-title");
|
|
4603
4629
|
const tweakSub = tweakCta.querySelector(".tweak-sub");
|
|
@@ -4619,8 +4645,26 @@
|
|
|
4619
4645
|
}
|
|
4620
4646
|
}
|
|
4621
4647
|
|
|
4648
|
+
/** L'onglet du canal tweak, s'il existe. */
|
|
4649
|
+
function tweakTab() { return tabs.find((t) => t.isTweak); }
|
|
4650
|
+
|
|
4651
|
+
/** Déplace l'onglet dans l'emplacement du bas et masque le CTA : à partir de
|
|
4652
|
+
* là, c'est LUI qu'on voit et qu'on clique. `appendChild` déplace le nœud,
|
|
4653
|
+
* donc l'onglet quitte la liste de gauche sans être recréé. */
|
|
4654
|
+
function markTweakTab(t) {
|
|
4655
|
+
t.isTweak = true;
|
|
4656
|
+
t.btnEl.classList.add("tweak");
|
|
4657
|
+
t.btnEl.draggable = false; // il n'a pas à finir dans un groupe
|
|
4658
|
+
tweakSlot.appendChild(t.btnEl);
|
|
4659
|
+
tweakCta.hidden = true;
|
|
4660
|
+
}
|
|
4661
|
+
|
|
4622
4662
|
tweakCta.addEventListener("click", async () => {
|
|
4623
4663
|
if (tweakBusy) return;
|
|
4664
|
+
// Ceinture : le CTA est masqué dès qu'un canal tweak existe, mais s'il
|
|
4665
|
+
// réapparaissait on active l'existant plutôt que d'en ouvrir un second.
|
|
4666
|
+
const already = tweakTab();
|
|
4667
|
+
if (already) return activate(already);
|
|
4624
4668
|
tweakBusy = true;
|
|
4625
4669
|
setTweakCta("busy");
|
|
4626
4670
|
let data;
|
|
@@ -4637,6 +4681,7 @@
|
|
|
4637
4681
|
t.customName = true; // launchTab ne nomme qu'un onglet qui n'a pas de nom
|
|
4638
4682
|
t.nameEl.textContent = "tweak";
|
|
4639
4683
|
t.profile = "Shadok-Tweak";
|
|
4684
|
+
markTweakTab(t);
|
|
4640
4685
|
launchTab(t, { cwd: data.cwd, worktree: true, profile: "Shadok-Tweak" });
|
|
4641
4686
|
});
|
|
4642
4687
|
newGroupBtn.addEventListener("click", () => {
|
|
@@ -4775,6 +4820,8 @@
|
|
|
4775
4820
|
t.draft = savedDrafts[s.sessionId] || "";
|
|
4776
4821
|
if (s.branch) t.branch = s.branch;
|
|
4777
4822
|
t.profile = s.profile || null;
|
|
4823
|
+
// Au rechargement, le canal tweak retrouve son emplacement du bas.
|
|
4824
|
+
if (t.profile === "Shadok-Tweak" && !tweakTab()) markTweakTab(t);
|
|
4778
4825
|
setCrons(t, s.crons);
|
|
4779
4826
|
if (s.muted) setMuted(t, true, false); // restoring: don't push back
|
|
4780
4827
|
// The mirror TOO, otherwise the first persist pushes `mirror:false`
|