shadok-ai 0.2.10 → 0.2.11

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/public/index.html +45 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shadok-ai",
3
- "version": "0.2.10",
3
+ "version": "0.2.11",
4
4
  "main": "dist/session.js",
5
5
  "scripts": {
6
6
  "test": "node --import tsx --test test/*.ts .claude/skills/shadok-ai-agents/test/*.test.mjs",
package/public/index.html CHANGED
@@ -1605,15 +1605,37 @@
1605
1605
 
1606
1606
  // Composer drafts, one per channel, remembered by session id.
1607
1607
  const savedDrafts = JSON.parse(localStorage.getItem("cp.drafts") || "{}");
1608
+ /** Écrit MAINTENANT le brouillon de cet onglet, sans attendre le débounce. */
1609
+ function writeDraft(tab) {
1610
+ if (!tab.sessionId) return;
1611
+ clearTimeout(tab.draftTimer);
1612
+ if (tab.draft) savedDrafts[tab.sessionId] = tab.draft;
1613
+ else delete savedDrafts[tab.sessionId];
1614
+ localStorage.setItem("cp.drafts", JSON.stringify(savedDrafts));
1615
+ }
1608
1616
  function persistDraft(tab) {
1609
1617
  if (!tab.sessionId) return;
1610
1618
  clearTimeout(tab.draftTimer);
1611
- tab.draftTimer = setTimeout(() => {
1612
- if (tab.draft) savedDrafts[tab.sessionId] = tab.draft;
1613
- else delete savedDrafts[tab.sessionId];
1614
- localStorage.setItem("cp.drafts", JSON.stringify(savedDrafts));
1615
- }, 300);
1619
+ tab.draftTimer = setTimeout(() => writeDraft(tab), 300);
1616
1620
  }
1621
+ /**
1622
+ * Vide les écritures en attente de TOUS les onglets.
1623
+ *
1624
+ * Le débounce se réarme à chaque frappe : tant qu'on écrit sans marquer 300 ms
1625
+ * de pause, rien n'était jamais écrit — un rechargement en plein élan (typique
1626
+ * de l'auto-update, qui tombe quand il tombe) emportait tout le message. On
1627
+ * force donc l'écriture au moment où la page s'en va.
1628
+ */
1629
+ function flushDrafts() {
1630
+ for (const t of tabs) writeDraft(t);
1631
+ }
1632
+ // `pagehide` couvre le rechargement, la navigation et la mise en bfcache ;
1633
+ // `visibilitychange` rattrape le mobile, où l'onglet peut être tué sans que
1634
+ // `pagehide` ne parte. localStorage étant synchrone, l'écriture aboutit.
1635
+ window.addEventListener("pagehide", flushDrafts);
1636
+ document.addEventListener("visibilitychange", () => {
1637
+ if (document.visibilityState === "hidden") flushDrafts();
1638
+ });
1617
1639
  function dropDraft(tab) {
1618
1640
  clearTimeout(tab.draftTimer);
1619
1641
  tab.draft = "";
@@ -2156,6 +2178,7 @@
2156
2178
  } else if (!dismissedChannels.has(c.sessionId)) {
2157
2179
  const nt = createTab(c.group);
2158
2180
  nt.sessionId = c.sessionId;
2181
+ nt.draft = savedDrafts[c.sessionId] || "";
2159
2182
  if (c.branch) nt.branch = c.branch;
2160
2183
  nt.profile = c.profile || null;
2161
2184
  setCrons(nt, c.crons);
@@ -3115,7 +3138,14 @@
3115
3138
 
3116
3139
  function handleVersion(msg) {
3117
3140
  if (bootVersion === null) bootVersion = msg.current;
3118
- else if (msg.current && msg.current !== bootVersion) { location.reload(); return; }
3141
+ else if (msg.current && msg.current !== bootVersion) {
3142
+ // Mise à jour : la page part maintenant. On écrit d'abord — c'est LE cas
3143
+ // où l'on perdait un message en cours de frappe, puisque le rechargement
3144
+ // n'est pas déclenché par l'utilisateur.
3145
+ flushDrafts();
3146
+ location.reload();
3147
+ return;
3148
+ }
3119
3149
  lastVersion = msg;
3120
3150
  if (!verView) return;
3121
3151
  const ahead = msg.latest && verIsNewer(msg.latest, msg.current);
@@ -3238,6 +3268,10 @@
3238
3268
  promptInput.value = t.draft;
3239
3269
  autosizePrompt();
3240
3270
  }
3271
+ } else if (t.draft) {
3272
+ // Tapé AVANT que la session ne soit connue : `persistDraft` refuse
3273
+ // d'écrire sans id, ce texte n'était donc dans aucun stockage.
3274
+ writeDraft(t);
3241
3275
  }
3242
3276
  }
3243
3277
  // launchTab reuses the tab object, so a bubble armed before the session
@@ -4394,6 +4428,11 @@
4394
4428
  // Know the session id from the start, so a slow or failed reattach never
4395
4429
  // drops the channel from a persist (which eroded the list to one).
4396
4430
  t.sessionId = s.sessionId;
4431
+ // Le brouillon revient DÈS la création de l'onglet, sans attendre le
4432
+ // `ready` : après une mise à jour le serveur redémarre, la reconnexion
4433
+ // prend plusieurs secondes, et l'utilisateur retrouvait un champ vide —
4434
+ // de quoi croire que son message était perdu et le retaper.
4435
+ t.draft = savedDrafts[s.sessionId] || "";
4397
4436
  if (s.branch) t.branch = s.branch;
4398
4437
  t.profile = s.profile || null;
4399
4438
  setCrons(t, s.crons);