@north-light/crouter 0.3.294 → 0.3.296
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/clients/attach/render/crtr-output.js +57 -27
- package/dist/clients/attach/viewer.js +524 -524
- package/package.json +1 -1
- package/runtime.lock.json +5 -5
|
@@ -251,42 +251,72 @@ function surfaceEntryText(entry, theme) {
|
|
|
251
251
|
constraints.push(`gate ${predicateValueText(entry.gate)}`);
|
|
252
252
|
return `${theme.fg('text', constraints.join(' '))} ${theme.fg('dim', 'at')} ${theme.fg(rungTone(entry.at), entry.at)}`;
|
|
253
253
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
const indent = ' ';
|
|
262
|
-
const lines = [];
|
|
263
|
-
const placement = [];
|
|
254
|
+
const ROUTING_INDENT = ' ';
|
|
255
|
+
/** The routing block under the folded call's header line: one line per field,
|
|
256
|
+
* in the order the substrate resolves them. A fact the invocation does not
|
|
257
|
+
* state is left out rather than shown as empty. */
|
|
258
|
+
function memoryRoutingFields(routing, theme) {
|
|
259
|
+
const label = (text) => theme.fg('dim', text);
|
|
260
|
+
const fields = [];
|
|
264
261
|
if (routing.scope !== undefined)
|
|
265
|
-
|
|
262
|
+
fields.push({ label: label('scope'), values: [theme.fg('text', routing.scope)] });
|
|
266
263
|
if (routing.gate !== undefined)
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
264
|
+
fields.push({ label: label('gate'), values: [theme.fg('text', routing.gate)] });
|
|
265
|
+
fields.push({
|
|
266
|
+
label: label('surface'),
|
|
267
|
+
values: routing.surfaces.length === 0
|
|
268
|
+
? [theme.fg('muted', 'none')]
|
|
269
|
+
: routing.surfaces.map((entry) => surfaceEntryText(entry, theme)),
|
|
270
|
+
});
|
|
274
271
|
if (routing.whenAndWhyToRead !== undefined) {
|
|
275
|
-
|
|
272
|
+
fields.push({ label: label('when-and-why-to-read'), values: [theme.fg('text', routing.whenAndWhyToRead)] });
|
|
273
|
+
}
|
|
274
|
+
return fields;
|
|
275
|
+
}
|
|
276
|
+
/** One field's rows: the label, then its values in a column of their own. Each
|
|
277
|
+
* value starts at that column — so a second surface entry reads as a sibling of
|
|
278
|
+
* the first — and a value too long for the pane wraps under it. A field that
|
|
279
|
+
* states SEVERAL values wraps one notch deeper still, which is what keeps a
|
|
280
|
+
* wrapped surface entry from reading as the next entry. */
|
|
281
|
+
function routingFieldLines(field, width) {
|
|
282
|
+
const head = `${ROUTING_INDENT}${field.label} `;
|
|
283
|
+
const column = visibleWidth(head);
|
|
284
|
+
const valueIndent = ' '.repeat(column);
|
|
285
|
+
const wrapIndent = field.values.length > 1 ? `${valueIndent} ` : valueIndent;
|
|
286
|
+
const lines = [];
|
|
287
|
+
for (const value of field.values) {
|
|
288
|
+
const wrapped = wrapTextWithAnsi(value, Math.max(1, width - visibleWidth(wrapIndent)));
|
|
289
|
+
wrapped.forEach((line, index) => {
|
|
290
|
+
const prefix = index > 0 ? wrapIndent : lines.length === 0 ? head : valueIndent;
|
|
291
|
+
lines.push(`${prefix}${line}`);
|
|
292
|
+
});
|
|
276
293
|
}
|
|
277
294
|
return lines;
|
|
278
295
|
}
|
|
279
|
-
/** A folded call
|
|
280
|
-
*
|
|
281
|
-
*
|
|
296
|
+
/** A folded memory call: its header line, then its routing fields. Values WRAP
|
|
297
|
+
* rather than clip — the row states the document's whole reach, so a value cut
|
|
298
|
+
* off at the pane edge is routing the human cannot read. Wrapping keeps the
|
|
299
|
+
* folded call painting exactly the rows it reports, because the viewer
|
|
300
|
+
* measures a block by rendering it at the width it paints at and caches that
|
|
301
|
+
* measurement per width (MeasuredContainer). What must hold at every width is
|
|
302
|
+
* that no row overhangs the pane, or the terminal wraps a row nobody counted. */
|
|
282
303
|
class FoldedCrtrCallComponent {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
304
|
+
header;
|
|
305
|
+
fields;
|
|
306
|
+
constructor(header, fields) {
|
|
307
|
+
this.header = header;
|
|
308
|
+
this.fields = fields;
|
|
286
309
|
}
|
|
287
310
|
invalidate() { }
|
|
288
311
|
render(width) {
|
|
289
|
-
|
|
312
|
+
const lines = wrapTextWithAnsi(this.header, Math.max(1, width));
|
|
313
|
+
for (const field of this.fields)
|
|
314
|
+
lines.push(...routingFieldLines(field, width));
|
|
315
|
+
// Final backstop: a pane too narrow to leave the value column any room to
|
|
316
|
+
// wrap into (24 cells for when-and-why-to-read, 12 for a wrapped surface
|
|
317
|
+
// entry) is the only way a row can overhang. It erases the value it clips
|
|
318
|
+
// rather than trimming it — at that width the label alone fills the pane.
|
|
319
|
+
return lines.map((line) => truncateToWidth(line, width, ''));
|
|
290
320
|
}
|
|
291
321
|
}
|
|
292
322
|
/** Syntax-color flags and their values while leaving the command path and
|
|
@@ -860,7 +890,7 @@ export function createCrtrBashToolDefinition(args, fold = new FoldedToolCallCont
|
|
|
860
890
|
if (routing !== undefined) {
|
|
861
891
|
const named = routing.name === '' ? '' : ` ${theme.fg('toolTitle', theme.bold(routing.name))}`;
|
|
862
892
|
const header = `${styledPrefix} ${theme.fg('toolTitle', theme.bold(routing.leaf))}${named}`;
|
|
863
|
-
return fold.capture(new FoldedCrtrCallComponent(
|
|
893
|
+
return fold.capture(new FoldedCrtrCallComponent(header, memoryRoutingFields(routing, theme)));
|
|
864
894
|
}
|
|
865
895
|
const styledPath = path
|
|
866
896
|
? ` ${fold.folded ? styleFoldedCrtrTokens(pathTokens, theme) : theme.fg('toolTitle', theme.bold(path))}`
|