@lamemind/loom-deck 0.21.0 β†’ 0.21.1

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
@@ -83,19 +83,27 @@ Assegnazioni correnti:
83
83
  | immediata | `^P` | spawna la task selezionata sul **preflight** |
84
84
  | immediata | `^R` | spawna la task selezionata in **esecuzione** |
85
85
  | immediata | `f` | **forka** la sessione selezionata (solo col focus sul pane Sessions) |
86
- | immediata | `t` | terminale @project-root (surface standard launch) |
87
- | immediata | `c` | sessione Claude **nuda**: nessuna task, nessun prompt iniziale |
86
+ | immediata | `t` πŸ’» | terminale @project-root (surface standard launch) |
87
+ | immediata | `c` πŸ€– | sessione Claude **nuda**: nessuna task, nessun prompt iniziale |
88
88
  | immediata | `w` | salva la vista corrente su disco |
89
89
  | launch | `1`…`9` | esegue il `command` della voce, con `cwd` = project root |
90
90
 
91
91
  Le voci `launch` sono elencate in una **riga di legenda** sotto il footer
92
- (`launch 1 πŸ“ codium Β· 2 β˜• idea`): l'indice da solo Γ¨ opaco, perchΓ© le voci sono
92
+ (`1 πŸ“ codium Β· 2 β˜• idea`): l'indice da solo Γ¨ opaco, perchΓ© le voci sono
93
93
  custom per-progetto e non hanno una lettera fissa per app. Se non entrano in
94
94
  larghezza, la legenda si ferma a voci intere e mostra il contatore di quelle
95
95
  fuori riga β€” mai un troncamento silenzioso. Il cap a `9` Γ¨ imposto dai tasti-cifra,
96
96
  non dallo schema: un progetto puΓ² dichiarare piΓΉ di 9 voci, quelle oltre la nona
97
97
  sono configurate ma non raggiungibili (e la legenda lo dice).
98
98
 
99
+ In legenda le due surface del cappello portano l'emoji del menu compass (`t πŸ’»`,
100
+ `c πŸ€–`) invece della parola. Per il terminale compass usa πŸ–₯️, che nel frame Ink
101
+ non sopravvive: `sanitize` lo sostituisce perchΓ© VTE lo disegna largo 1 mentre
102
+ `string-width` ne conta 2 (invariante β‘  di `src/width.ts`) β€” πŸ’» Γ¨ il gemello
103
+ concorde. La legenda elenca solo i tasti attivi in quel momento: navigazione,
104
+ uscita e indicatore di focus non ci sono, e le voci contestuali spariscono
105
+ invece di mostrarsi inerti.
106
+
99
107
  `t` e `c` sono gemelle: entrambe aprono una surface del cappello nella stessa
100
108
  finestra Ptyxis, senza passare da un modale. `c` (minuscola, azione) e `C`
101
109
  (maiuscola, modale create-task) restano distinte per la regola sopra β€” cosΓ¬ come
package/dist/cli.js CHANGED
@@ -1358,6 +1358,31 @@ function Deck({ cwd, tasksPath, tasksDir }) {
1358
1358
  // Larghezza dal medesimo hook che dΓ  l'altezza: dopo un resize la legenda si
1359
1359
  // ricalcola con lo stesso re-render che ridimensiona i pane.
1360
1360
  const legend = launchLegend(launch, columns);
1361
+ // Legenda della modalitΓ  normale. Elenca SOLO i tasti che fanno qualcosa qui e
1362
+ // ora: le voci contestuali compaiono quando il pane a fuoco le rende possibili
1363
+ // e altrimenti spariscono, invece di annunciarsi inerti con un `β€”`.
1364
+ // Fuori: navigazione (`↑↓` `←→`) e uscita (`q`), universali in qualunque TUI, e
1365
+ // l'indicatore `focus:` β€” il pane a fuoco si vede giΓ  dall'evidenziazione, e
1366
+ // ridirlo a parole costava colonne su una riga che tronca in silenzio.
1367
+ const keyLegend = sanitize([
1368
+ ...(canSpawn ? ['⏎/^K/^P/^R spawn'] : canResume ? ['⏎ resume'] : []),
1369
+ ...(canResume ? ['f fork'] : []),
1370
+ ...(canPin ? ['p pin', 'N nota'] : []),
1371
+ '^F cerca',
1372
+ 'C nuova',
1373
+ 'E edit',
1374
+ 'S sort',
1375
+ 'F filtri',
1376
+ 'w salva',
1377
+ // Emoji delle surface, le stesse del menu compass: πŸ€– = nuova sessione
1378
+ // claude. Per il terminale compass usa πŸ–₯️, che nel frame Ink NON passa β€”
1379
+ // `sanitize` lo sostituisce (VTE lo disegna largo 1, string-width dice 2:
1380
+ // discordante, invariante β‘  di width.ts) e resterebbe un `Β·` muto. πŸ’» Γ¨ il
1381
+ // gemello concorde piΓΉ vicino. Il `sanitize` attorno all'array Γ¨ la rete
1382
+ // che rende quel vincolo automatico, non una cosa da ricordarsi.
1383
+ 't πŸ’»',
1384
+ 'c πŸ€–',
1385
+ ].join(' Β· '));
1361
1386
  // ── T52 Β· schermate sostitutive ─────────────────────────────────────────
1362
1387
  // Ricerca e reader sono gli unici modali che NON stanno in flusso sopra i
1363
1388
  // pane: una lista di occorrenze non entra in un box da 4 righe. Prendono
@@ -1440,7 +1465,7 @@ function Deck({ cwd, tasksPath, tasksDir }) {
1440
1465
  if (budget.compact) {
1441
1466
  return (_jsxs(Text, { wrap: "truncate-end", children: [_jsx(Text, { bold: true, color: "cyan", children: "loom-deck" }), _jsxs(Text, { dimColor: true, children: [' ', "\u00B7 ", viewTasks.length, " task \u00B7 sel ", selectedTaskId ?? 'spot', " \u00B7 terminale ", rows, "\u00D7", columns, ": troppo basso, allarga"] })] }));
1442
1467
  }
1443
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "loom-deck" }), mode === 'create' ? (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ["nuova task \u00B7 ", _jsx(Text, { color: "yellow", children: "\u23CE" }), " crea \u00B7 ", _jsx(Text, { color: "yellow", children: "esc" }), " annulla"] })) : mode === 'sort' ? (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ["sort \u00B7 ", _jsx(Text, { color: "yellow", children: "p" }), " pri ", _jsx(Text, { color: "yellow", children: "s" }), " stato", ' ', _jsx(Text, { color: "yellow", children: "i" }), " id (asc\u2192desc\u2192off) \u00B7 ", _jsx(Text, { color: "yellow", children: "\u23CE" }), " ok \u00B7", ' ', _jsx(Text, { color: "yellow", children: "esc" }), " annulla"] })) : mode === 'filter' ? (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ["filtri \u00B7 ", _jsx(Text, { color: "yellow", children: "\u2191\u2193\u2190\u2192" }), " naviga \u00B7 ", _jsx(Text, { color: "yellow", children: "spazio" }), ' ', "mostra/nascondi \u00B7 ", _jsx(Text, { color: "yellow", children: "\u23CE" }), " ok \u00B7 ", _jsx(Text, { color: "yellow", children: "esc" }), " annulla"] })) : mode === 'note' ? (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ["nota conversazione \u00B7 ", _jsx(Text, { color: "yellow", children: "^U" }), " svuota \u00B7", ' ', _jsx(Text, { color: "yellow", children: "\u23CE" }), " salva (vuoto = rimuove) \u00B7", ' ', _jsx(Text, { color: "yellow", children: "esc" }), " annulla"] })) : mode === 'edit' ? (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ["edit \u00B7 ", _jsx(Text, { color: "yellow", children: "\u2191\u2193" }), " campo \u00B7 ", _jsx(Text, { color: "yellow", children: "\u2190\u2192" }), " valore, o cursore sul testo \u00B7 ", _jsx(Text, { color: "yellow", children: "^A/^E" }), " inizio/fine \u00B7", ' ', _jsx(Text, { color: "yellow", children: "^D" }), " canc \u00B7 ", _jsx(Text, { color: "yellow", children: "\u23CE" }), " salva+commit \u00B7", ' ', _jsx(Text, { color: "yellow", children: "esc" }), " annulla"] })) : (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ["\u2191\u2193 naviga \u00B7 \u2190\u2192 pane \u00B7", ' ', canSpawn ? '⏎/^K/^P/^R spawn' : canResume ? '⏎ resume' : '⏎ β€”', canResume ? ' Β· f fork' : '', canPin ? ' Β· p pin Β· N nota' : '', " \u00B7 ^F cerca \u00B7 C nuova \u00B7 E edit \u00B7 S sort \u00B7 F filtri \u00B7 w salva \u00B7 t term \u00B7 c claude \u00B7 q esci \u00B7 focus:", ' ', _jsx(Text, { color: "cyan", children: focus })] })), mode === 'normal' && launch.length > 0 ? (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ["launch ", legend.shown, legend.overflow > 0 ? (_jsxs(Text, { color: "yellow", children: [" \u00B7 +", legend.overflow, " fuori riga"] })) : null, legend.unreachable > 0 ? (_jsxs(Text, { color: "yellow", children: [" \u00B7 ", legend.unreachable, " oltre la 9\u00AA (non raggiungibili)"] })) : null] })) : null, mode === 'create' ? (_jsxs(Box, { borderStyle: "round", borderColor: "yellow", paddingX: 1, marginTop: 1, children: [_jsx(Text, { color: "yellow", children: "C \u203A " }), _jsx(Text, { children: draft }), _jsx(Text, { inverse: true, children: " " })] })) : null, mode === 'note' ? (_jsxs(Box, { borderStyle: "round", borderColor: "yellow", paddingX: 1, marginTop: 1, children: [_jsx(Text, { color: "yellow", children: "\u270E \u203A " }), _jsx(Text, { children: noteDraft }), _jsx(Text, { inverse: true, children: " " })] })) : null, mode === 'sort' ? _jsx(SortModal, { sort: view.sort }) : null, mode === 'filter' ? _jsx(FilterModal, { view: view, cursor: filterCursor }) : null, mode === 'edit' && edit && selTask ? (_jsx(EditModal, { id: selTask.id, draft: edit, row: editRow, columns: columns })) : null, _jsxs(Box, { flexDirection: "row", marginTop: 1, children: [_jsx(TasksPane, { tasks: windowTasks, filtered: viewTasks.length, total: tasks.length, hidden: hiddenTasks, view: view, selected: selIndex, spotCount: spotCount, childCount: childCount, focused: focus === 'tasks', loadError: loadError, detail: detail, windowStart: taskWin.start, above: taskWin.start, below: viewTasks.length - taskWin.end, detailLines: budget.detailLines, columns: columns }), _jsx(SessionsPane, { parentLabel: parentLabel, isSpot: isSpot, rows: windowRows, total: assembled.pinnedCount + assembled.contextTotal, pinnedCount: assembled.pinnedCount, hidden: assembled.contextHidden, selectedId: selSessionId ?? undefined, focused: focus === 'sessions', above: sessionWin.start, below: sessionRows.length - sessionWin.end, detail: budget.sessionDetail ? selSessionObj : null, firstLines: budget.sessionFirstLines, lastLines: budget.sessionLastLines, columns: columns, forkOf: forkOf, sessionNotes: sessionNotes, projectCore: projectCore })] }), note ? _jsx(Text, { color: "green", wrap: "truncate-end", children: sanitize(note) }) : null] }));
1468
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "loom-deck" }), mode === 'create' ? (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ["nuova task \u00B7 ", _jsx(Text, { color: "yellow", children: "\u23CE" }), " crea \u00B7 ", _jsx(Text, { color: "yellow", children: "esc" }), " annulla"] })) : mode === 'sort' ? (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ["sort \u00B7 ", _jsx(Text, { color: "yellow", children: "p" }), " pri ", _jsx(Text, { color: "yellow", children: "s" }), " stato", ' ', _jsx(Text, { color: "yellow", children: "i" }), " id (asc\u2192desc\u2192off) \u00B7 ", _jsx(Text, { color: "yellow", children: "\u23CE" }), " ok \u00B7", ' ', _jsx(Text, { color: "yellow", children: "esc" }), " annulla"] })) : mode === 'filter' ? (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ["filtri \u00B7 ", _jsx(Text, { color: "yellow", children: "\u2191\u2193\u2190\u2192" }), " naviga \u00B7 ", _jsx(Text, { color: "yellow", children: "spazio" }), ' ', "mostra/nascondi \u00B7 ", _jsx(Text, { color: "yellow", children: "\u23CE" }), " ok \u00B7 ", _jsx(Text, { color: "yellow", children: "esc" }), " annulla"] })) : mode === 'note' ? (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ["nota conversazione \u00B7 ", _jsx(Text, { color: "yellow", children: "^U" }), " svuota \u00B7", ' ', _jsx(Text, { color: "yellow", children: "\u23CE" }), " salva (vuoto = rimuove) \u00B7", ' ', _jsx(Text, { color: "yellow", children: "esc" }), " annulla"] })) : mode === 'edit' ? (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ["edit \u00B7 ", _jsx(Text, { color: "yellow", children: "\u2191\u2193" }), " campo \u00B7 ", _jsx(Text, { color: "yellow", children: "\u2190\u2192" }), " valore, o cursore sul testo \u00B7 ", _jsx(Text, { color: "yellow", children: "^A/^E" }), " inizio/fine \u00B7", ' ', _jsx(Text, { color: "yellow", children: "^D" }), " canc \u00B7 ", _jsx(Text, { color: "yellow", children: "\u23CE" }), " salva+commit \u00B7", ' ', _jsx(Text, { color: "yellow", children: "esc" }), " annulla"] })) : (_jsx(Text, { dimColor: true, wrap: "truncate-end", children: keyLegend })), mode === 'normal' && launch.length > 0 ? (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: [legend.shown, legend.overflow > 0 ? (_jsxs(Text, { color: "yellow", children: [" \u00B7 +", legend.overflow, " fuori riga"] })) : null, legend.unreachable > 0 ? (_jsxs(Text, { color: "yellow", children: [" \u00B7 ", legend.unreachable, " oltre la 9\u00AA (non raggiungibili)"] })) : null] })) : null, mode === 'create' ? (_jsxs(Box, { borderStyle: "round", borderColor: "yellow", paddingX: 1, marginTop: 1, children: [_jsx(Text, { color: "yellow", children: "C \u203A " }), _jsx(Text, { children: draft }), _jsx(Text, { inverse: true, children: " " })] })) : null, mode === 'note' ? (_jsxs(Box, { borderStyle: "round", borderColor: "yellow", paddingX: 1, marginTop: 1, children: [_jsx(Text, { color: "yellow", children: "\u270E \u203A " }), _jsx(Text, { children: noteDraft }), _jsx(Text, { inverse: true, children: " " })] })) : null, mode === 'sort' ? _jsx(SortModal, { sort: view.sort }) : null, mode === 'filter' ? _jsx(FilterModal, { view: view, cursor: filterCursor }) : null, mode === 'edit' && edit && selTask ? (_jsx(EditModal, { id: selTask.id, draft: edit, row: editRow, columns: columns })) : null, _jsxs(Box, { flexDirection: "row", marginTop: 1, children: [_jsx(TasksPane, { tasks: windowTasks, filtered: viewTasks.length, total: tasks.length, hidden: hiddenTasks, view: view, selected: selIndex, spotCount: spotCount, childCount: childCount, focused: focus === 'tasks', loadError: loadError, detail: detail, windowStart: taskWin.start, above: taskWin.start, below: viewTasks.length - taskWin.end, detailLines: budget.detailLines, columns: columns }), _jsx(SessionsPane, { parentLabel: parentLabel, isSpot: isSpot, rows: windowRows, total: assembled.pinnedCount + assembled.contextTotal, pinnedCount: assembled.pinnedCount, hidden: assembled.contextHidden, selectedId: selSessionId ?? undefined, focused: focus === 'sessions', above: sessionWin.start, below: sessionRows.length - sessionWin.end, detail: budget.sessionDetail ? selSessionObj : null, firstLines: budget.sessionFirstLines, lastLines: budget.sessionLastLines, columns: columns, forkOf: forkOf, sessionNotes: sessionNotes, projectCore: projectCore })] }), note ? _jsx(Text, { color: "green", wrap: "truncate-end", children: sanitize(note) }) : null] }));
1444
1469
  }
1445
1470
  const SORT_UI = { pri: 'pri', prog: 'stato', id: 'id' };
1446
1471
  // Modali resi IN FLUSSO (come l'input box di create), non in overlay assoluto:
package/dist/config.js CHANGED
@@ -69,9 +69,11 @@ export function cellWidth(s) {
69
69
  export function launchLegend(entries, columns) {
70
70
  const reachable = entries.slice(0, LAUNCH_MAX);
71
71
  const unreachable = entries.length - reachable.length;
72
- // bordo + padding del box + prefisso "launch " β‰ˆ 12 celle; pavimento a 24 per
73
- // non degenerare a legenda vuota su terminali strettissimi.
74
- const budget = Math.max(24, columns - 12);
72
+ // bordo + padding del box β‰ˆ 4 celle, piΓΉ 2 di margine; pavimento a 24 per
73
+ // non degenerare a legenda vuota su terminali strettissimi. Il prefisso
74
+ // "launch " non c'Γ¨ piΓΉ (la riga si riconosce dalla forma `indice emoji
75
+ // label`), quindi le 7 celle che occupava tornano alle voci.
76
+ const budget = Math.max(24, columns - 6);
75
77
  // I fallback di parseLaunch (`β–Έ` per emoji, command per label) sono giΓ 
76
78
  // applicati a monte: qui non si re-implementano nΓ© si assumono campi popolati.
77
79
  const parts = reachable.map((e, i) => `${i + 1} ${e.emoji} ${e.label}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lamemind/loom-deck",
3
- "version": "0.21.0",
3
+ "version": "0.21.1",
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": {