@lamemind/loom-deck 0.21.1 → 0.21.2
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 +29 -15
- package/dist/cli.js +19 -13
- package/dist/config.js +8 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -88,21 +88,35 @@ Assegnazioni correnti:
|
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
91
|
+
Il footer è **due righe con due nature diverse**:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
⏎/^K/^P/^R spawn · ^F cerca · C nuova · E edit · S sort · F filtri · w salva
|
|
95
|
+
t 💻 · c 🤖 · 1 📝 codium · 2 ☕ idea
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
- **tasti** — cosa puoi fare *qui e ora*. Solo le voci attive: quelle contestuali
|
|
99
|
+
compaiono quando il pane a fuoco le rende possibili e altrimenti spariscono,
|
|
100
|
+
invece di mostrarsi inerti. Fuori navigazione (`↑↓` `←→`), uscita (`q`) e
|
|
101
|
+
indicatore `focus:` — le prime due sono universali in qualunque TUI, il pane a
|
|
102
|
+
fuoco si vede già dall'evidenziazione.
|
|
103
|
+
- **surface** — *dove* puoi aprire qualcosa: prima le due built-in (`t`/`c`), poi
|
|
104
|
+
le voci `launch` del progetto. Stanno insieme perché hanno la stessa natura —
|
|
105
|
+
fire-once, `cwd` = project root, nessuno stato — e differiscono solo per essere
|
|
106
|
+
universali invece che custom.
|
|
107
|
+
|
|
108
|
+
L'indice da solo è opaco (le `launch` sono custom per-progetto, senza una lettera
|
|
109
|
+
fissa per app), quindi la riga espone la mappa e non il conteggio. Se non entrano
|
|
110
|
+
in larghezza, si ferma a voci intere e mostra il contatore di quelle fuori riga —
|
|
111
|
+
mai un troncamento silenzioso; le celle delle due built-in sono riservate a monte,
|
|
112
|
+
o le voci sfonderebbero il box di quel tanto. Il cap a `9` è imposto dai
|
|
113
|
+
tasti-cifra, non dallo schema: un progetto può dichiarare più di 9 voci, quelle
|
|
114
|
+
oltre la nona sono configurate ma non raggiungibili (e la riga lo dice).
|
|
115
|
+
|
|
116
|
+
Le emoji sono quelle del menu compass. Per il terminale compass usa 🖥️, che nel
|
|
117
|
+
frame Ink non sopravvive: `sanitize` lo sostituisce perché VTE lo disegna largo 1
|
|
118
|
+
mentre `string-width` ne conta 2 (invariante ① di `src/width.ts`) — 💻 è il
|
|
119
|
+
gemello concorde.
|
|
106
120
|
|
|
107
121
|
`t` e `c` sono gemelle: entrambe aprono una surface del cappello nella stessa
|
|
108
122
|
finestra Ptyxis, senza passare da un modale. `c` (minuscola, azione) e `C`
|
package/dist/cli.js
CHANGED
|
@@ -13,7 +13,7 @@ import { discoverProjectSessions } from './sessions.js';
|
|
|
13
13
|
import { buildRows, firstRowKey, moveRowSelection, rowIndexOfKey, searchSessions, selectedRow, DEFAULT_OPTIONS, MIN_QUERY, } from './search.js';
|
|
14
14
|
import { appendNote, appendPin, appendSessionRecord, appendTaskBinding, loadSessionIndex, } from './task-index.js';
|
|
15
15
|
import { assembleSessionList, firstSelectableId, moveSelection, rowIndexOf, rowLabel, selectedSession, stripProjectCore, } from './session-list.js';
|
|
16
|
-
import { launchLegend, loadIdentity, loadLaunch } from './config.js';
|
|
16
|
+
import { cellWidth, launchLegend, loadIdentity, loadLaunch } from './config.js';
|
|
17
17
|
import { isCompact, layoutBudget, readerCapacity, searchListCapacity, searchPreviewCapacity, windowRange, } from './viewport.js';
|
|
18
18
|
import { caretWindow, cut, sanitize, termWidth, wrapLines, wrapWithOffsets, } from './width.js';
|
|
19
19
|
import { applyView, cycleSort, describeSort, priName, progName, toggleHidden, PRI_ENTRIES, PROG_ENTRIES, } from './view.js';
|
|
@@ -1355,9 +1355,21 @@ function Deck({ cwd, tasksPath, tasksDir }) {
|
|
|
1355
1355
|
// T50 — il pin agisce su qualunque riga selezionata (anche stale, per
|
|
1356
1356
|
// spinnarla); basta il focus sul pane e una selezione.
|
|
1357
1357
|
const canPin = focus === 'sessions' && selSessionId !== null;
|
|
1358
|
+
// Le due surface built-in del cappello stanno in testa alla riga launch, non
|
|
1359
|
+
// fra i tasti: hanno la stessa natura delle voci `launch` — fire-once, cwd =
|
|
1360
|
+
// project root, nessuno stato — e la differenza è solo che sono universali
|
|
1361
|
+
// (nessun progetto le dichiara) invece che custom. Emoji del menu compass:
|
|
1362
|
+
// 🤖 = nuova sessione claude. Per il terminale compass usa 🖥️, che nel frame
|
|
1363
|
+
// Ink NON passa — `sanitize` lo sostituisce (VTE lo disegna largo 1,
|
|
1364
|
+
// string-width dice 2: discordante, invariante ① di width.ts) e resterebbe un
|
|
1365
|
+
// `·` muto. 💻 è il gemello concorde; il `sanitize` qui rende il vincolo
|
|
1366
|
+
// automatico invece che da ricordare.
|
|
1367
|
+
const surfaceLegend = sanitize('t 💻 · c 🤖');
|
|
1358
1368
|
// Larghezza dal medesimo hook che dà l'altezza: dopo un resize la legenda si
|
|
1359
|
-
// ricalcola con lo stesso re-render che ridimensiona i pane.
|
|
1360
|
-
|
|
1369
|
+
// ricalcola con lo stesso re-render che ridimensiona i pane. Le celle delle
|
|
1370
|
+
// surface (più il ` · ` che le separa dalle voci) sono già spese sulla riga →
|
|
1371
|
+
// vanno riservate, o le voci launch la sfonderebbero di quel tanto.
|
|
1372
|
+
const legend = launchLegend(launch, columns, cellWidth(surfaceLegend) + 3);
|
|
1361
1373
|
// Legenda della modalità normale. Elenca SOLO i tasti che fanno qualcosa qui e
|
|
1362
1374
|
// ora: le voci contestuali compaiono quando il pane a fuoco le rende possibili
|
|
1363
1375
|
// e altrimenti spariscono, invece di annunciarsi inerti con un `—`.
|
|
@@ -1374,14 +1386,6 @@ function Deck({ cwd, tasksPath, tasksDir }) {
|
|
|
1374
1386
|
'S sort',
|
|
1375
1387
|
'F filtri',
|
|
1376
1388
|
'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
1389
|
].join(' · '));
|
|
1386
1390
|
// ── T52 · schermate sostitutive ─────────────────────────────────────────
|
|
1387
1391
|
// Ricerca e reader sono gli unici modali che NON stanno in flusso sopra i
|
|
@@ -1433,7 +1437,9 @@ function Deck({ cwd, tasksPath, tasksDir }) {
|
|
|
1433
1437
|
// Ptyxis significa un frame intero versato nello scrollback per ogni tick del
|
|
1434
1438
|
// poll. Tutto ciò che varia in altezza (le due liste e la descrizione del
|
|
1435
1439
|
// dettaglio) riceve qui la propria capienza.
|
|
1436
|
-
|
|
1440
|
+
// Le surface built-in `t`/`c` la rendono sempre presente in modalità normale:
|
|
1441
|
+
// non dipende più da quante voci `launch` il progetto dichiara.
|
|
1442
|
+
const launchLine = mode === 'normal';
|
|
1437
1443
|
const detailParts = detail ? detailMetaOf(detail) : null;
|
|
1438
1444
|
const budget = layoutBudget({
|
|
1439
1445
|
rows,
|
|
@@ -1465,7 +1471,7 @@ function Deck({ cwd, tasksPath, tasksDir }) {
|
|
|
1465
1471
|
if (budget.compact) {
|
|
1466
1472
|
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"] })] }));
|
|
1467
1473
|
}
|
|
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'
|
|
1474
|
+
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' ? (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: [surfaceLegend, legend.shown ? ` · ${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] }));
|
|
1469
1475
|
}
|
|
1470
1476
|
const SORT_UI = { pri: 'pri', prog: 'stato', id: 'id' };
|
|
1471
1477
|
// Modali resi IN FLUSSO (come l'input box di create), non in overlay assoluto:
|
package/dist/config.js
CHANGED
|
@@ -66,14 +66,17 @@ export function cellWidth(s) {
|
|
|
66
66
|
// custom per-progetto, non hanno una lettera fissa per app), quindi la resa deve
|
|
67
67
|
// esporre la mappa, non il conteggio. Degradazione mai silenziosa: ciò che non
|
|
68
68
|
// entra in larghezza finisce in un contatore esplicito, non troncato a metà.
|
|
69
|
-
|
|
69
|
+
// `reserved` = celle già occupate sulla stessa riga da chi la renderizza (le
|
|
70
|
+
// surface built-in `t`/`c`, che precedono le voci). Parametro e non aritmetica
|
|
71
|
+
// sul chiamante: `columns` significa "colonne del terminale" e passargliene di
|
|
72
|
+
// meno per fargli credere a uno schermo più stretto renderebbe il calcolo
|
|
73
|
+
// illeggibile al primo che lo rilegge.
|
|
74
|
+
export function launchLegend(entries, columns, reserved = 0) {
|
|
70
75
|
const reachable = entries.slice(0, LAUNCH_MAX);
|
|
71
76
|
const unreachable = entries.length - reachable.length;
|
|
72
77
|
// bordo + padding del box ≈ 4 celle, più 2 di margine; pavimento a 24 per
|
|
73
|
-
// non degenerare a legenda vuota su terminali strettissimi.
|
|
74
|
-
|
|
75
|
-
// label`), quindi le 7 celle che occupava tornano alle voci.
|
|
76
|
-
const budget = Math.max(24, columns - 6);
|
|
78
|
+
// non degenerare a legenda vuota su terminali strettissimi.
|
|
79
|
+
const budget = Math.max(24, columns - 6 - reserved);
|
|
77
80
|
// I fallback di parseLaunch (`▸` per emoji, command per label) sono già
|
|
78
81
|
// applicati a monte: qui non si re-implementano né si assumono campi popolati.
|
|
79
82
|
const parts = reachable.map((e, i) => `${i + 1} ${e.emoji} ${e.label}`);
|
package/package.json
CHANGED