@lamemind/loom-deck 0.34.3 → 0.35.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/dist/glyphs.js +35 -0
- package/dist/sessions.js +24 -0
- package/dist/ui/panes.js +4 -2
- package/dist/ui/preview.js +1 -1
- package/package.json +1 -1
package/dist/glyphs.js
CHANGED
|
@@ -20,6 +20,41 @@ export const SID_CHARS = 8;
|
|
|
20
20
|
* vuota di soli spazi lascerebbe un buco che si legge come "colonna finita",
|
|
21
21
|
* e la riga tornerebbe a sembrare disallineata pur non essendolo. */
|
|
22
22
|
export const TASK_EMPTY = '·';
|
|
23
|
+
/** T110 — colonna modello della lista sessioni. Larghezza COSTANTE e non
|
|
24
|
+
* misurata come `taskW`/`ageW`: il dominio è chiuso e le short sono tutte
|
|
25
|
+
* larghe 3, quindi farla passare per `sessionCols` ricalcolerebbe a ogni
|
|
26
|
+
* render un numero già noto. */
|
|
27
|
+
export const MODEL_W = 3;
|
|
28
|
+
/** Le short sono CHIESTE, non derivate: `slice(0,3)` darebbe `opu` e `hai`, e
|
|
29
|
+
* sbaglierebbe proprio le due voci su quattro che a occhio non si controllano
|
|
30
|
+
* (`fab` e `son` coincidono col troncamento e coprono l'errore). La chiave è
|
|
31
|
+
* il token di FAMIGLIA, non l'id intero: `claude-opus-5 → ops` sarebbe un
|
|
32
|
+
* calco della generazione corrente, falso al primo bump e col sintomo del
|
|
33
|
+
* fallback «ignoto» proprio sul modello più usato. */
|
|
34
|
+
export const MODEL_SHORT = {
|
|
35
|
+
fable: 'fab',
|
|
36
|
+
opus: 'ops',
|
|
37
|
+
sonnet: 'son',
|
|
38
|
+
haiku: 'hak',
|
|
39
|
+
};
|
|
40
|
+
/** Nessun record assistant: la conversazione non ha ancora girato. Stesso
|
|
41
|
+
* glifo della cella task vuota accanto — il vocabolario non cresce. */
|
|
42
|
+
export const MODEL_EMPTY = TASK_EMPTY;
|
|
43
|
+
/** Id presente ma fuori dalle famiglie note: gira su qualcosa che la lista non
|
|
44
|
+
* sa nominare. Distinto dall'assenza, o un'estensione mancata del catalogo si
|
|
45
|
+
* legge come una riga normale e si scorre senza notarla; l'id per intero sta
|
|
46
|
+
* nel blocco preview. */
|
|
47
|
+
export const MODEL_UNKNOWN = '?';
|
|
48
|
+
export function modelShort(id) {
|
|
49
|
+
if (!id)
|
|
50
|
+
return MODEL_EMPTY;
|
|
51
|
+
const lower = id.toLowerCase();
|
|
52
|
+
for (const [family, short] of Object.entries(MODEL_SHORT)) {
|
|
53
|
+
if (lower.includes(family))
|
|
54
|
+
return short;
|
|
55
|
+
}
|
|
56
|
+
return MODEL_UNKNOWN;
|
|
57
|
+
}
|
|
23
58
|
// T62 — colonna liveness, larga 1, incollata al sessionId senza gutter proprio:
|
|
24
59
|
// il glifo qualifica QUELL'id, e uno spazio in mezzo lo farebbe leggere come una
|
|
25
60
|
// colonna a sé. Entrambi Ambiguous (EAW) → 1 colonna per il terminale e 1 cella
|
package/dist/sessions.js
CHANGED
|
@@ -54,6 +54,22 @@ const INTERRUPT_MARKER = '[Request interrupted by user';
|
|
|
54
54
|
function isInterrupt(text) {
|
|
55
55
|
return text.trimStart().startsWith(INTERRUPT_MARKER);
|
|
56
56
|
}
|
|
57
|
+
// T110 — valore che il CLI scrive nel campo modello dei record che fabbrica da
|
|
58
|
+
// sé (osservato su `{"type":"text","text":"No response requested."}`). Restano
|
|
59
|
+
// `type:assistant` a tutti gli effetti: chi si fida del campo etichetta la
|
|
60
|
+
// conversazione con un modello che non esiste. Terza istanza dello stesso
|
|
61
|
+
// pattern dei `tool_result` fra i turni e dell'interruzione fra i prompt — un
|
|
62
|
+
// record col tipo giusto che non è la cosa che quel tipo promette.
|
|
63
|
+
const SYNTHETIC_MODEL = '<synthetic>';
|
|
64
|
+
// Id del modello di UN record assistant, '' se assente o sintetico.
|
|
65
|
+
function extractModel(message) {
|
|
66
|
+
if (!message || typeof message !== 'object')
|
|
67
|
+
return '';
|
|
68
|
+
const model = message.model;
|
|
69
|
+
if (typeof model !== 'string' || !model || model === SYNTHETIC_MODEL)
|
|
70
|
+
return '';
|
|
71
|
+
return model;
|
|
72
|
+
}
|
|
57
73
|
// T52 — estrae i corpi cercabili di UN record, uno per `kind` presente.
|
|
58
74
|
//
|
|
59
75
|
// Blocchi dello stesso kind nello stesso record vengono CONCATENATI, non emessi
|
|
@@ -166,6 +182,7 @@ export function parseTranscript(content, path, mtime, sizeBytes) {
|
|
|
166
182
|
let customTitle = '';
|
|
167
183
|
let firstUserText = '';
|
|
168
184
|
let lastAssistantText = '';
|
|
185
|
+
let model = '';
|
|
169
186
|
let turns = 0;
|
|
170
187
|
const bodies = [];
|
|
171
188
|
let recordIdx = -1;
|
|
@@ -216,6 +233,12 @@ export function parseTranscript(content, path, mtime, sizeBytes) {
|
|
|
216
233
|
const t = extractText(d.message);
|
|
217
234
|
if (t)
|
|
218
235
|
lastAssistantText = t;
|
|
236
|
+
// T110 — stesso regime last-wins, ma su un asse suo: un record di solo
|
|
237
|
+
// tool_use porta comunque il modello, e va contato. Nessun costo di I/O:
|
|
238
|
+
// la riga è già parsata per turni, primo prompt e ultima risposta.
|
|
239
|
+
const m = extractModel(d.message);
|
|
240
|
+
if (m)
|
|
241
|
+
model = m;
|
|
219
242
|
}
|
|
220
243
|
}
|
|
221
244
|
if (!cwd)
|
|
@@ -240,6 +263,7 @@ export function parseTranscript(content, path, mtime, sizeBytes) {
|
|
|
240
263
|
// testo, sanificare a valle sposterebbe l'a-capo già calcolato.
|
|
241
264
|
firstPrompt: sanitize(firstUserText),
|
|
242
265
|
lastReply: sanitize(lastAssistantText),
|
|
266
|
+
model,
|
|
243
267
|
bodies,
|
|
244
268
|
};
|
|
245
269
|
}
|
package/dist/ui/panes.js
CHANGED
|
@@ -5,7 +5,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
5
5
|
import { Box, Text } from 'ink';
|
|
6
6
|
import { cut, cutParts, pad, sanitize, termWidth } from '../width.js';
|
|
7
7
|
import { isDone, paneTextWidth } from '../layout.js';
|
|
8
|
-
import { CARET, CARET_OFF, LIVE_BUSY, LIVE_IDLE, LIVE_NONE, SESSION_SEP, SID_CHARS, TASK_EMPTY, WARN, displayProg, relTime, } from '../glyphs.js';
|
|
8
|
+
import { CARET, CARET_OFF, LIVE_BUSY, LIVE_IDLE, LIVE_NONE, MODEL_W, SESSION_SEP, SID_CHARS, TASK_EMPTY, WARN, displayProg, modelShort, relTime, } from '../glyphs.js';
|
|
9
9
|
import { META_ROWS, ROW_ALL, ROW_SPOT } from '../model.js';
|
|
10
10
|
import { rowLabel, sessionTitle } from '../session-list.js';
|
|
11
11
|
import { sessionView, taskView, SESSION_VIEWS, TASK_VIEWS, } from '../pane-views.js';
|
|
@@ -199,6 +199,8 @@ export function SessionsPane({ parentLabel, isSpot, isAll, bindings, taskW, ageW
|
|
|
199
199
|
1 /* T62 · colonna liveness */ +
|
|
200
200
|
SID_CHARS +
|
|
201
201
|
1 /* gutter */ +
|
|
202
|
+
MODEL_W /* T110 · colonna modello */ +
|
|
203
|
+
1 /* gutter */ +
|
|
202
204
|
(taskW > 0 ? taskW + 1 : 0) +
|
|
203
205
|
1 /* gutter prima della data */ +
|
|
204
206
|
ageW));
|
|
@@ -215,6 +217,6 @@ export function SessionsPane({ parentLabel, isSpot, isAll, bindings, taskW, ageW
|
|
|
215
217
|
const used = (label.note ? termWidth(label.note) + 2 : 0) +
|
|
216
218
|
(label.note && label.rest ? 1 : 0) +
|
|
217
219
|
termWidth(label.rest);
|
|
218
|
-
return (_jsxs(Text, { inverse: sel && focused, bold: sel && !focused, wrap: "truncate-end", children: [sel ? CARET : CARET_OFF, isPinnedRow ? (_jsx(Text, { color: "yellow", children: pad('📌', 2) })) : linked ? (_jsx(Text, { color: "green", children: pad('🔗', 2) })) : (_jsx(Text, { dimColor: true, children: pad('○', 2) })), ' ', _jsx(Text, { color: liveEntry ? (liveEntry.status === 'busy' ? 'yellow' : 'green') : undefined, children: liveEntry ? (liveEntry.status === 'busy' ? LIVE_BUSY : LIVE_IDLE) : LIVE_NONE }), _jsx(Text, { color: liveEntry ? (liveEntry.status === 'busy' ? 'yellow' : 'green') : 'cyan', bold: Boolean(liveEntry), children: s.sessionId.slice(0, SID_CHARS) }), ' ', taskW > 0 ? (_jsxs(_Fragment, { children: [_jsx(Text, { color: bound && taskCell ? 'green' : undefined, dimColor: !bound, children: pad(taskCell, taskW) }), ' '] })) : null, forkMark ? _jsx(Text, { color: "magenta", children: forkMark }) : null, label.note ? (_jsxs(Text, { color: "yellow", bold: true, children: ["\u00AB", label.note, "\u00BB"] })) : null, label.note && label.rest ? ' ' : null, label.rest ? _jsx(Text, { dimColor: Boolean(label.note), children: label.rest }) : null, ' '.repeat(Math.max(0, inner - used)), ' ', _jsx(Text, { dimColor: true, children: pad(age, ageW, 'right') })] }, s.sessionId));
|
|
220
|
+
return (_jsxs(Text, { inverse: sel && focused, bold: sel && !focused, wrap: "truncate-end", children: [sel ? CARET : CARET_OFF, isPinnedRow ? (_jsx(Text, { color: "yellow", children: pad('📌', 2) })) : linked ? (_jsx(Text, { color: "green", children: pad('🔗', 2) })) : (_jsx(Text, { dimColor: true, children: pad('○', 2) })), ' ', _jsx(Text, { color: liveEntry ? (liveEntry.status === 'busy' ? 'yellow' : 'green') : undefined, children: liveEntry ? (liveEntry.status === 'busy' ? LIVE_BUSY : LIVE_IDLE) : LIVE_NONE }), _jsx(Text, { color: liveEntry ? (liveEntry.status === 'busy' ? 'yellow' : 'green') : 'cyan', bold: Boolean(liveEntry), children: s.sessionId.slice(0, SID_CHARS) }), ' ', _jsx(Text, { dimColor: !s.model, children: pad(modelShort(s.model), MODEL_W) }), ' ', taskW > 0 ? (_jsxs(_Fragment, { children: [_jsx(Text, { color: bound && taskCell ? 'green' : undefined, dimColor: !bound, children: pad(taskCell, taskW) }), ' '] })) : null, forkMark ? _jsx(Text, { color: "magenta", children: forkMark }) : null, label.note ? (_jsxs(Text, { color: "yellow", bold: true, children: ["\u00AB", label.note, "\u00BB"] })) : null, label.note && label.rest ? ' ' : null, label.rest ? _jsx(Text, { dimColor: Boolean(label.note), children: label.rest }) : null, ' '.repeat(Math.max(0, inner - used)), ' ', _jsx(Text, { dimColor: true, children: pad(age, ageW, 'right') })] }, s.sessionId));
|
|
219
221
|
}))] }));
|
|
220
222
|
}
|
package/dist/ui/preview.js
CHANGED
|
@@ -19,7 +19,7 @@ export function SessionPreview({ s, firstLines, lastLines, columns, origin, note
|
|
|
19
19
|
const width = previewTextWidth(columns);
|
|
20
20
|
const first = s.customTitle && firstLines > 0 ? wrapLines(s.firstPrompt, width, firstLines) : [];
|
|
21
21
|
const last = s.lastReply && lastLines > 0 ? wrapLines(s.lastReply, width, lastLines) : [];
|
|
22
|
-
return (_jsxs(_Fragment, { children: [_jsxs(Text, { bold: true, wrap: "truncate-end", children: [_jsx(Text, { color: "cyan", children: s.sessionId.slice(0, SID_CHARS) }), ' ', note ? _jsxs(Text, { color: "yellow", children: ["\u00AB", note, "\u00BB "] }) : null, _jsx(Text, { dimColor: Boolean(note), children: s.title })] }), _jsxs(Text, { dimColor: true, wrap: "truncate-end", children: [fmtSize(s.sizeBytes), " \u00B7 ", s.turns, " turni \u00B7 ", fmtDateTime(s.ts), " \u00B7 ", s.gitBranch || '-', origin ? ` · ⑂ da ${origin.slice(0, 8)}` : '', live ? (_jsx(Text, { color: live.status === 'busy' ? 'yellow' : 'green', children: ` · ${live.status === 'busy' ? LIVE_BUSY : LIVE_IDLE} viva pid ${live.pid} (${live.status})` })) : null] }), first.map((line, i) => (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: [i === 0 ? '» ' : ' ', line] }, `f${i}`))), last.map((line, i) => (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: [i === 0 ? '« ' : ' ', line] }, `l${i}`)))] }));
|
|
22
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Text, { bold: true, wrap: "truncate-end", children: [_jsx(Text, { color: "cyan", children: s.sessionId.slice(0, SID_CHARS) }), ' ', note ? _jsxs(Text, { color: "yellow", children: ["\u00AB", note, "\u00BB "] }) : null, _jsx(Text, { dimColor: Boolean(note), children: s.title })] }), _jsxs(Text, { dimColor: true, wrap: "truncate-end", children: [fmtSize(s.sizeBytes), " \u00B7 ", s.turns, " turni \u00B7 ", fmtDateTime(s.ts), " \u00B7 ", s.gitBranch || '-', s.model ? ` · ${s.model}` : '', origin ? ` · ⑂ da ${origin.slice(0, 8)}` : '', live ? (_jsx(Text, { color: live.status === 'busy' ? 'yellow' : 'green', children: ` · ${live.status === 'busy' ? LIVE_BUSY : LIVE_IDLE} viva pid ${live.pid} (${live.status})` })) : null] }), first.map((line, i) => (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: [i === 0 ? '» ' : ' ', line] }, `f${i}`))), last.map((line, i) => (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: [i === 0 ? '« ' : ' ', line] }, `l${i}`)))] }));
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* Righe non-wrappabili del dettaglio (titolo + meta + commit) e loro conteggio.
|
package/package.json
CHANGED