@lamemind/loom-deck 0.57.2 → 0.57.3

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.
@@ -361,6 +361,13 @@ export function useDeckModel({ cwd, tasksPath, tasksDir, setNote, }) {
361
361
  const next = rightPane === 'inbox' ? 'sessions' : 'inbox';
362
362
  setRightPane(next);
363
363
  setFocus((f) => (f === 'tasks' ? f : next));
364
+ // Montare il pane RIMISURA la coda. Il periodico resta, come rete per il
365
+ // deck lasciato aperto sul pane inbox, ma non è più l'unico canale: senza
366
+ // questa chiamata chi apre il pane guarda l'ultima misura, che può essere
367
+ // vecchia quanto l'intero intervallo. Non serve il verso opposto — tornare
368
+ // alle sessioni non guarda la coda, e uno scan lì si pagherebbe per nessuno.
369
+ if (next === 'inbox')
370
+ inbox.scan();
364
371
  setNote(next === 'inbox' ? '^B → pane inbox' : '^B → pane sessioni');
365
372
  }
366
373
  /** `esc` in vista normale: torna alle sessioni. Solo in quel verso — `esc`
package/dist/hooks.js CHANGED
@@ -219,28 +219,53 @@ export function useDirtyFolders(idsSig, tasksDir, projectRoot) {
219
219
  return dirty;
220
220
  }
221
221
  export function useInboxScan(projectRoot, docsRoot) {
222
- const [state, setState] = useState({ files: [], ok: true, scanned: false });
222
+ const [state, setState] = useState({
223
+ files: [],
224
+ ok: true,
225
+ scanned: false,
226
+ });
227
+ const [epoch, setEpoch] = useState(0);
228
+ // Il flag di corsa sta in un ref e non in stato, come in `useWrapScan`: due
229
+ // aperture ravvicinate del pane girano nello stesso tick di React, e un
230
+ // valore di stato arriverebbe al render dopo — cioè troppo tardi per
231
+ // impedire il secondo spawn.
232
+ const busy = useRef(false);
233
+ // `alive` vive fuori dall'effect perché `run` sopravvive al suo giro: la
234
+ // richiesta on-demand è scatenata da un gesto, non dall'effect che la ospita.
235
+ const alive = useRef(true);
223
236
  useEffect(() => {
224
- let alive = true;
225
- const scan = () => {
226
- scanInbox(projectRoot, docsRoot).then((res) => {
227
- if (!alive)
228
- return;
229
- // Un tentativo fallito NON butta via l'esito dell'ultimo riuscito: la
230
- // lista precedente resta vera e ancora apribile, e il guasto si dice
231
- // col glifo di allerta accanto ai contatori. È la stessa regola già
232
- // scritta per il project status (`finish` non riscrive la cache).
233
- setState((prev) => res.ok ? { files: res.files, ok: true, scanned: true } : { ...prev, ok: false, scanned: true });
234
- });
235
- };
236
- scan();
237
- const id = setInterval(scan, INBOX_SCAN_INTERVAL_MS);
237
+ alive.current = true;
238
238
  return () => {
239
- alive = false;
240
- clearInterval(id);
239
+ alive.current = false;
241
240
  };
241
+ }, []);
242
+ const run = useCallback(() => {
243
+ if (busy.current)
244
+ return;
245
+ busy.current = true;
246
+ scanInbox(projectRoot, docsRoot).then((res) => {
247
+ busy.current = false;
248
+ if (!alive.current)
249
+ return;
250
+ // Un tentativo fallito NON butta via l'esito dell'ultimo riuscito: la
251
+ // lista precedente resta vera e ancora apribile, e il guasto si dice
252
+ // col glifo di allerta accanto ai contatori. È la stessa regola già
253
+ // scritta per il project status (`finish` non riscrive la cache).
254
+ setState((prev) => res.ok ? { files: res.files, ok: true, scanned: true } : { ...prev, ok: false, scanned: true });
255
+ });
242
256
  }, [projectRoot, docsRoot]);
243
- return state;
257
+ useEffect(() => {
258
+ run();
259
+ const id = setInterval(run, INBOX_SCAN_INTERVAL_MS);
260
+ return () => clearInterval(id);
261
+ }, [run, epoch]);
262
+ // Lo scan a richiesta RESETTA il timer, come quello del wrap: senza,
263
+ // l'intervallo continuerebbe a scorrere dall'ultimo tick automatico e potrebbe
264
+ // rimisurare pochi istanti dopo un dato appena misurato.
265
+ const scan = useCallback(() => {
266
+ setEpoch((e) => e + 1);
267
+ }, []);
268
+ return { ...state, scan };
244
269
  }
245
270
  export function useWrapScan(projectRoot) {
246
271
  const path = useMemo(() => wrapCacheFile(projectRoot), [projectRoot]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lamemind/loom-deck",
3
- "version": "0.57.2",
3
+ "version": "0.57.3",
4
4
  "description": "Deck TUI Ink per-progetto della famiglia loom: legge tasks.md e spawna sessioni Claude Code bound via LOOM_TASK",
5
5
  "type": "module",
6
6
  "bin": {