@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.
- package/dist/deck-model.js +7 -0
- package/dist/hooks.js +43 -18
- package/package.json +1 -1
package/dist/deck-model.js
CHANGED
|
@@ -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({
|
|
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
|
-
|
|
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
|
-
|
|
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