@lamemind/loom-deck 0.33.0 → 0.34.0

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 CHANGED
@@ -45,8 +45,8 @@ Regola unica, senza eccezioni — pensata per reggere l'aggiunta di nuove azioni
45
45
  | Tasto | Semantica | Note |
46
46
  |---|---|---|
47
47
  | `↑` `↓` | naviga nella lista | |
48
- | `tab` | cambia pane | l'unico tasto che sposta il focus |
49
- | `←` `→` | cambia **vista** del pane a fuoco | ciclico; il catalogo è l'header stesso |
48
+ | `←` `→` | cambia pane | assoluto, non toggle: `←` = Tasks, `→` = Sessions — la direzione nomina il pane, che è affiancato |
49
+ | `tab` | cambia **vista** del pane a fuoco | ciclico (`shift+tab` indietro); il catalogo è l'header stesso |
50
50
  | `⏎` | azione primaria del pane | Tasks → apre il **detail** della task selezionata · Sessions → riprende (`claude --resume`) la sessione selezionata |
51
51
  | **MAIUSCOLA** | **apre un modale** | cattura tutti i tasti; `esc` annulla, non esce |
52
52
  | minuscola | azione immediata, one-shot | |
@@ -122,7 +122,7 @@ Le azioni non sono un catalogo nuovo: chiamano lo **stesso** percorso di spawn d
122
122
 
123
123
  Sono bottoni affiancati e non voci di un menu verticale perché è la forma che sopravvive all'arrivo del mouse: un rettangolo ha già coordinate e area cliccabile, una lista di voci andrebbe rifatta.
124
124
 
125
- Dentro il detail `←→` cicla i bottoni invece di cambiare la vista del pane — il
125
+ Dentro il detail `←→` cicla i bottoni invece di cambiare pane, e `tab` non cambia la vista — il
126
126
  ramo `useInput` della modalità chiude prima di arrivare a quello di default. Ink non ha focus-trap: la cattura **è** l'ordine dei rami, e cambiare cosa fa un tasto fuori dal detail non tocca cosa fa dentro.
127
127
 
128
128
  Il catalogo dei prompt vive in `scripts/deck-run`, non nella TUI: è il primitive UI-agnostico, il deck gli passa un **simbolo** (`--prompt-kind`) e non una stringa. Così il testo sta in un posto solo, e con lui il suo vincolo di quoting — il prompt viaggia dentro apici singoli in `bash -lc`, quindi non può contenerne.
@@ -209,7 +209,7 @@ e al riavvio viene ripristinata. File assente o corrotto → default puliti.
209
209
  ## L'header è un selettore di vista
210
210
 
211
211
  Ogni segmento dell'header nomina un sottoinsieme che il deck sa già calcolare.
212
- `←`/`→` lo rendono **raggiungibile**: la voce attiva si evidenzia in video
212
+ `tab` lo rende **raggiungibile**: la voce attiva si evidenzia in video
213
213
  inverso e la lista sotto mostra quel sottoinsieme.
214
214
 
215
215
  ```
package/dist/cli.js CHANGED
@@ -628,7 +628,7 @@ function Deck({ cwd, tasksPath, tasksDir }) {
628
628
  // modale: la lista si aggiorna dal vivo, quindi `esc` deve poter ripristinare.
629
629
  const [view, setView] = useState(() => loadView(cwd));
630
630
  const [viewBackup, setViewBackup] = useState(null);
631
- // T100 — vista attiva di ciascun pane, navigata con ←/→. VOLATILE per
631
+ // T100 — vista attiva di ciascun pane, navigata con `tab`. VOLATILE per
632
632
  // decisione (D3 create): non entra in `deck-view.json`, il deck riapre sempre
633
633
  // su `Tasks` e su `{parent}`. Il criterio è il rischio di leggere una lista
634
634
  // parziale credendola completa — un filtro salvato lo si è scelto, una vista
@@ -990,7 +990,7 @@ function Deck({ cwd, tasksPath, tasksDir }) {
990
990
  // messaggi a mano. `verb` è l'unica cosa che cambia fra i due usi.
991
991
  function selectedTaskOr(keyLabel, verb) {
992
992
  if (focus !== 'tasks') {
993
- setNote(`${keyLabel} → ${verb}: seleziona una task (tab per il pane)`);
993
+ setNote(`${keyLabel} → ${verb}: seleziona una task ( per il pane)`);
994
994
  return null;
995
995
  }
996
996
  // T59 — la guardia è "non è una task", non "è spot": le righe meta sono due
@@ -1241,7 +1241,7 @@ function Deck({ cwd, tasksPath, tasksDir }) {
1241
1241
  else
1242
1242
  setSel(paneTasks[next - META_ROWS]?.id ?? SPOT);
1243
1243
  }
1244
- // T100 — ←/→ navigano il catalogo viste del pane in focus. Il reset della
1244
+ // T100 — `tab` naviga il catalogo viste del pane in focus. Il reset della
1245
1245
  // selezione è la regola letterale «prima riga in alto», senza eccezioni: sul
1246
1246
  // pane task è `ROW_ALL` (D2 preflight — le righe meta non si saltano, e il
1247
1247
  // parent delle sessioni che torna a `tutte` è un effetto accettato); sul pane
@@ -1669,7 +1669,10 @@ function Deck({ cwd, tasksPath, tasksDir }) {
1669
1669
  // selezione per ognuna. Sta PRIMA del ramo di default per la stessa ragione
1670
1670
  // di reader e ricerca — Ink non ha focus-trap, quindi la cattura È l'ordine
1671
1671
  // dei rami — e il `return` in fondo è ciò che impedisce a `←→` di cambiare
1672
- // pane mentre muove fra le azioni: qui quel binding non esiste più.
1672
+ // pane mentre muove fra le azioni: qui quel binding non esiste più. Cade lì
1673
+ // anche `tab`, cioè il cambio vista: dentro il detail nessuno dei due tasti
1674
+ // di navigazione del pane risponde, e non è un'eccezione da ricordare per
1675
+ // ognuno — è il `return` che chiude tutto il ramo di default in una volta.
1673
1676
  // È anche prima del ramo CTRL, quindi `^K`/`^P`/`^R` restano acceleratori
1674
1677
  // della sola lista: chi è già nel detail ha i bottoni.
1675
1678
  if (mode === 'detail') {
@@ -1794,16 +1797,22 @@ function Deck({ cwd, tasksPath, tasksDir }) {
1794
1797
  return;
1795
1798
  }
1796
1799
  if (key.tab) {
1797
- // T100 — `tab` resta l'unico tasto che sposta il focus fra i due pane. Le
1798
- // frecce orizzontali facevano lo stesso lavoro (due tasti per un'azione)
1799
- // mentre la navigazione DENTRO un pane non ne aveva nessuno: ora sono il
1800
- // selettore di vista dell'header. Il ramo `mode === 'detail'`, anteposto e
1801
- // chiuso da `return`, non è più l'eccezione a «cambia pane» ma a «cambia
1802
- // vista» l'ordine dei rami non cambia, cambia cosa cattura.
1803
- setFocus((f) => (f === 'tasks' ? 'sessions' : 'tasks'));
1800
+ // `tab` cicla la VISTA del pane a fuoco, `←→` spostano il focus fra i due
1801
+ // pane: il criterio dell'assegnazione è spaziale. Una freccia orizzontale
1802
+ // porta con una direzione e i pane sono affiancati (task a sinistra,
1803
+ // sessioni a destra), quindi il tasto NOMINA il pane invece di limitarsi
1804
+ // a scambiarlo; un catalogo ciclico non ha un verso da rispettare, e un
1805
+ // tasto solo gli basta. `shift+tab` (backtab, `[Z`) scorre a rovescio — il
1806
+ // verso che un tasto singolo non esprime sta nel modificatore, e su cinque
1807
+ // voci risparmia quattro pressioni.
1808
+ cycleView(key.shift ? -1 : 1);
1804
1809
  }
1805
1810
  else if (key.leftArrow || key.rightArrow) {
1806
- cycleView(key.leftArrow ? -1 : 1);
1811
+ // Binding ASSOLUTO, non toggle: `←` porta sempre sui task e `→` sempre
1812
+ // sulle sessioni, quindi ripremere lo stesso tasto non riporta indietro.
1813
+ // È ciò che lo rende spaziale — la direzione indica una destinazione, e
1814
+ // con due soli pane un toggle sarebbe indistinguibile solo per caso.
1815
+ setFocus(key.leftArrow ? 'tasks' : 'sessions');
1807
1816
  }
1808
1817
  else if (key.upArrow) {
1809
1818
  if (focus === 'tasks')
@@ -1880,7 +1889,7 @@ function Deck({ cwd, tasksPath, tasksDir }) {
1880
1889
  // Vive solo sul pane sessioni: il fork ha per oggetto una conversazione,
1881
1890
  // e senza focus lì non ce n'è una selezionata su cui agire.
1882
1891
  if (focus !== 'sessions') {
1883
- setNote('f → fork: seleziona una sessione (tab per il pane)');
1892
+ setNote('f → fork: seleziona una sessione ( per il pane)');
1884
1893
  }
1885
1894
  else {
1886
1895
  const s = selSessionObj;
@@ -1912,7 +1921,7 @@ function Deck({ cwd, tasksPath, tasksDir }) {
1912
1921
  // pinnata STALE (l'unico modo di spinnarla). Scrive il sidecar e ricarica
1913
1922
  // subito, senza attendere il tick del poll.
1914
1923
  if (focus !== 'sessions') {
1915
- setNote('p → pin: seleziona una sessione (tab per il pane)');
1924
+ setNote('p → pin: seleziona una sessione ( per il pane)');
1916
1925
  }
1917
1926
  else if (!selSessionId) {
1918
1927
  setNote('p → nessuna sessione da pinnare');
@@ -1940,7 +1949,7 @@ function Deck({ cwd, tasksPath, tasksDir }) {
1940
1949
  // su una pinnata STALE, perché annotare «questa non c'è più, era X» è
1941
1950
  // proprio il caso in cui una nota serve.
1942
1951
  if (focus !== 'sessions') {
1943
- setNote('N → nota: seleziona una sessione (tab per il pane)');
1952
+ setNote('N → nota: seleziona una sessione ( per il pane)');
1944
1953
  }
1945
1954
  else if (!selSessionId) {
1946
1955
  setNote('N → nessuna sessione da annotare');
@@ -1958,7 +1967,7 @@ function Deck({ cwd, tasksPath, tasksDir }) {
1958
1967
  // il binding è nostro, il transcript è di CC — riassegnare una
1959
1968
  // conversazione il cui transcript non c'è più resta legittimo.
1960
1969
  if (focus !== 'sessions') {
1961
- setNote('A → assegna: seleziona una sessione (tab per il pane)');
1970
+ setNote('A → assegna: seleziona una sessione ( per il pane)');
1962
1971
  }
1963
1972
  else if (!selSessionId) {
1964
1973
  setNote('A → nessuna sessione da assegnare');
@@ -2513,7 +2522,7 @@ function AssignScreen({ sessionId, label, current, filter, rows, selected, match
2513
2522
  * segmento resta il primo a cedere il posto (`↑↓` in coda alle voci navigabili).
2514
2523
  *
2515
2524
  * T100 — la riga non è più informativa: le voci del catalogo sono SELEZIONABILI
2516
- * con ←/→, e l'attiva si distingue in video inverso (D5 — costa 0 colonne e non
2525
+ * con `tab`, e l'attiva si distingue in video inverso (D5 — costa 0 colonne e non
2517
2526
  * entra in gara con la semantica di colore già occupata). Le voci ci sono tutte
2518
2527
  * anche a 0 (D1): un catalogo che si accorcia sposta le voci sotto le dita.
2519
2528
  * L'ordine è vincolato — le navigabili PRIMA di `↑N`/`↓N`, che cadono per primi
@@ -1,7 +1,7 @@
1
1
  // T100 — Catalogo delle viste dei due pane. L'header smette di essere una riga
2
2
  // di contatori e diventa un SELETTORE: ogni segmento che nomina un sottoinsieme
3
3
  // calcolabile (`nascoste`, `archiviabili`, `vive`, `📌`, `+più vecchie`) è una
4
- // vista raggiungibile con ←/→.
4
+ // vista raggiungibile con `tab`.
5
5
  //
6
6
  // Modulo PURO: nessun import da ink/react, nessun I/O. Il calcolo resta dove
7
7
  // stava — `view.ts` per i filtri task, `session-list.ts` per l'assemblaggio a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lamemind/loom-deck",
3
- "version": "0.33.0",
3
+ "version": "0.34.0",
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": {