gentle-shell-bridge 0.3.0 → 0.3.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/LICENSE +202 -0
- package/README.md +91 -722
- package/docs/contract.md +92 -35
- package/package.json +13 -1
- package/src/api.ts +1 -1
- package/.codegraph/.gitignore +0 -5
- package/PRD.md +0 -144
- package/examples/bash-card.ts +0 -1694
- package/gentle-shell-bridge-0.3.0.tgz +0 -0
- package/odd/tasks/palette-sections.md +0 -30
- package/odd/tasks/plugin-keymap.md +0 -32
- package/odd/tasks/plugin-surface-contract.md +0 -55
- package/test/api.test.ts +0 -395
- package/test/apply-on-move.test.ts +0 -477
- package/test/arm.test.ts +0 -220
- package/test/bridge-ready.test.ts +0 -309
- package/test/bus.test.ts +0 -165
- package/test/button-rows.test.ts +0 -409
- package/test/capture.test.ts +0 -356
- package/test/card-style-rows.test.ts +0 -510
- package/test/card-style-transform.test.ts +0 -379
- package/test/card-style-wiring.test.ts +0 -258
- package/test/card-styles-e2e.test.ts +0 -491
- package/test/card-styles.test.ts +0 -324
- package/test/card-transcript-path.test.ts +0 -178
- package/test/config.test.ts +0 -93
- package/test/e2e.test.ts +0 -443
- package/test/example-bash-identity.test.ts +0 -372
- package/test/example-bash-matrix.test.ts +0 -856
- package/test/example-designs.test.ts +0 -734
- package/test/example-neon-transform.test.ts +0 -283
- package/test/example-plain-style.test.ts +0 -1268
- package/test/extension.test.ts +0 -160
- package/test/fake-events.ts +0 -58
- package/test/fake-host.ts +0 -211
- package/test/fake-sidebar.ts +0 -516
- package/test/fake-transcript.ts +0 -486
- package/test/frame-telemetry.test.ts +0 -417
- package/test/hook.test.ts +0 -196
- package/test/manifest.test.ts +0 -105
- package/test/new-category.test.ts +0 -979
- package/test/notices.test.ts +0 -328
- package/test/plugin-integration-lines.test.ts +0 -979
- package/test/plugin-surface.test.ts +0 -152
- package/test/presence.test.ts +0 -459
- package/test/rail-surfaces.test.ts +0 -457
- package/test/rail-width.test.ts +0 -423
- package/test/regression.test.ts +0 -483
- package/test/repaint-api.test.ts +0 -200
- package/test/reserved-categories.test.ts +0 -361
- package/test/row-labels.test.ts +0 -284
- package/test/row-names.test.ts +0 -473
- package/test/row-origin.test.ts +0 -395
- package/test/section-interaction.test.ts +0 -364
- package/test/section-panel.test.ts +0 -403
- package/test/sections-rows.test.ts +0 -291
- package/test/sidebar-backend.test.ts +0 -471
- package/test/sidebar-e2e.test.ts +0 -331
- package/test/sidebar-hover.test.ts +0 -276
- package/test/sidebar-independent.test.ts +0 -349
- package/test/sidebar-render.test.ts +0 -281
- package/test/sidebar-sections.test.ts +0 -245
- package/test/status-card-styles.test.ts +0 -201
- package/test/status-section-frame.test.ts +0 -181
- package/test/status-sections.test.ts +0 -243
- package/test/status.test.ts +0 -209
- package/test/store.test.ts +0 -192
- package/test/surface-repaint-cost.test.ts +0 -520
- package/test/tool-design-chain.test.ts +0 -483
- package/test/tool-design-cost.test.ts +0 -473
- package/test/tool-design-rows.test.ts +0 -359
- package/test/tool-designs.test.ts +0 -745
- package/test/transform-pipeline.test.ts +0 -465
- package/test/transforms-bus.test.ts +0 -233
- package/test/verifier-width-regressions.test.ts +0 -164
- package/tsconfig.json +0 -18
package/test/row-origin.test.ts
DELETED
|
@@ -1,395 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Row origin (T1–T3 of `odd/tasks/bash-card-identity.md`): WHICH host child drew each drawn row.
|
|
3
|
-
*
|
|
4
|
-
* The host heads a bash card with the inferred activity (`quiet-tools.ts:672`), never with the
|
|
5
|
-
* literal `bash` the example scans for, so identity has to come from the component that produced
|
|
6
|
-
* the row — the way `pi-cinlodev-cute` reads `child.toolName === "bash"`
|
|
7
|
-
* (`src/transcript/transcript-dispatcher.ts:30-40`).
|
|
8
|
-
*
|
|
9
|
-
* What is real here and what is modelled: pi-tui is not a dependency of this package, so
|
|
10
|
-
* `FakeContainer` copies `Container.render` (`pi-tui/dist/tui.js:136-149`) — render each child,
|
|
11
|
-
* record `{ component, height }`, publish `mouseLayout`, concatenate the rows. Everything else is
|
|
12
|
-
* the bridge's own code: `src/row-origin.ts` reads that published layout, `./card-style-wiring.ts`
|
|
13
|
-
* builds the map after the host's render, and `./transform-pipeline.ts` hands it to the transform.
|
|
14
|
-
* The rows are asserted through the public surfaces (the transform's `ctx`, the drawn frame), never
|
|
15
|
-
* through an internal field.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import assert from "node:assert/strict";
|
|
19
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
20
|
-
import { tmpdir } from "node:os";
|
|
21
|
-
import { join } from "node:path";
|
|
22
|
-
import test from "node:test";
|
|
23
|
-
|
|
24
|
-
import { registerTransform, resetBusForTests } from "../src/bus.ts";
|
|
25
|
-
import { resetCardStyleTransformForTests } from "../src/card-style-transform.ts";
|
|
26
|
-
import {
|
|
27
|
-
disableCardStyleTransform,
|
|
28
|
-
installCardStyleTransform,
|
|
29
|
-
installRailCardStyleTransform,
|
|
30
|
-
resetCardStyleWiringForTests,
|
|
31
|
-
} from "../src/card-style-wiring.ts";
|
|
32
|
-
import { setCardStyleSelection } from "../src/card-styles.ts";
|
|
33
|
-
import { resetNoticesForTests } from "../src/notices.ts";
|
|
34
|
-
import { rowOrigins } from "../src/row-origin.ts";
|
|
35
|
-
import { resetStoreStateForTests } from "../src/store.ts";
|
|
36
|
-
import type { RowOrigin } from "../src/types.ts";
|
|
37
|
-
import { createRailTui } from "./fake-sidebar.ts";
|
|
38
|
-
|
|
39
|
-
let configHome: string;
|
|
40
|
-
let previousConfigHome: string | undefined;
|
|
41
|
-
|
|
42
|
-
test.before(() => {
|
|
43
|
-
configHome = mkdtempSync(join(tmpdir(), "gsb-row-origin-"));
|
|
44
|
-
previousConfigHome = process.env.GENTLE_PI_CONFIG_HOME;
|
|
45
|
-
process.env.GENTLE_PI_CONFIG_HOME = configHome;
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test.after(() => {
|
|
49
|
-
if (previousConfigHome === undefined) delete process.env.GENTLE_PI_CONFIG_HOME;
|
|
50
|
-
else process.env.GENTLE_PI_CONFIG_HOME = previousConfigHome;
|
|
51
|
-
rmSync(configHome, { recursive: true, force: true });
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test.beforeEach(() => {
|
|
55
|
-
rmSync(join(configHome, "gentle-shell-bridge"), { recursive: true, force: true });
|
|
56
|
-
resetBusForTests();
|
|
57
|
-
resetCardStyleTransformForTests();
|
|
58
|
-
resetCardStyleWiringForTests();
|
|
59
|
-
resetNoticesForTests();
|
|
60
|
-
resetStoreStateForTests();
|
|
61
|
-
disableCardStyleTransform();
|
|
62
|
-
setCardStyleSelection("native");
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// ---------------------------------------------------------------------------------------------
|
|
66
|
-
// The fixture: the shapes the host's own render pass leaves behind
|
|
67
|
-
// ---------------------------------------------------------------------------------------------
|
|
68
|
-
|
|
69
|
-
const WIDTH = 40;
|
|
70
|
-
|
|
71
|
-
/** A component, in Pi's sense: `render(width)` returns one row per line. */
|
|
72
|
-
interface FakeComponent {
|
|
73
|
-
render(width: number): string[];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** A bash tool row: the `toolName` the host's own component carries (`pi-cinlodev-cute` reads it). */
|
|
77
|
-
class BashRow {
|
|
78
|
-
readonly toolName = "bash";
|
|
79
|
-
private readonly height: number;
|
|
80
|
-
|
|
81
|
-
constructor(height: number) {
|
|
82
|
-
this.height = height;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
render(): string[] {
|
|
86
|
-
return Array.from({ length: this.height }, (_, row) => `$ npm run check ${String(row)}`);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** A read tool row: a different tool on the same host shape. */
|
|
91
|
-
class ReadRow {
|
|
92
|
-
readonly toolName = "read";
|
|
93
|
-
private readonly height: number;
|
|
94
|
-
|
|
95
|
-
constructor(height: number) {
|
|
96
|
-
this.height = height;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
render(): string[] {
|
|
100
|
-
return Array.from({ length: this.height }, (_, row) => `≡ config.ts ${String(row)}`);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/** A drawn block that is not a tool at all: prose, a banner, a spacer. It carries no `toolName`. */
|
|
105
|
-
class ProseBlock {
|
|
106
|
-
private readonly rows: readonly string[];
|
|
107
|
-
|
|
108
|
-
constructor(rows: readonly string[]) {
|
|
109
|
-
this.rows = rows;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
render(): string[] {
|
|
113
|
-
return [...this.rows];
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** A `toolName` that is not a string is not a tool name: only a string is one (S1). */
|
|
118
|
-
class OddRow {
|
|
119
|
-
readonly toolName = 42;
|
|
120
|
-
|
|
121
|
-
render(): string[] {
|
|
122
|
-
return ["odd row"];
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
interface MouseChild {
|
|
127
|
-
component: FakeComponent;
|
|
128
|
-
height: number;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
interface MouseLayout {
|
|
132
|
-
width: number;
|
|
133
|
-
children: MouseChild[];
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* `pi-tui`'s `Container`: the host's conversation component is one, so its `render(width)` publishes
|
|
138
|
-
* `mouseLayout` — `{ width, children: [{ component, height }] }` — on every pass
|
|
139
|
-
* (`pi-tui/dist/tui.js:136-149`). Copied rather than imported: `pi-tui` is not a dependency.
|
|
140
|
-
*/
|
|
141
|
-
class FakeContainer {
|
|
142
|
-
readonly children: FakeComponent[] = [];
|
|
143
|
-
mouseLayout?: MouseLayout;
|
|
144
|
-
|
|
145
|
-
addChild(child: FakeComponent): void {
|
|
146
|
-
this.children.push(child);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
render(width: number): string[] {
|
|
150
|
-
const lines: string[] = [];
|
|
151
|
-
const mouseChildren: MouseChild[] = [];
|
|
152
|
-
for (const child of this.children) {
|
|
153
|
-
const childLines = child.render(width);
|
|
154
|
-
mouseChildren.push({ component: child, height: childLines.length });
|
|
155
|
-
lines.push(...childLines);
|
|
156
|
-
}
|
|
157
|
-
this.mouseLayout = { width, children: mouseChildren };
|
|
158
|
-
return lines;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/** Prose(1) · bash(3) · read(2) · a non-string tool name(1) · prose(1) = 8 rows. */
|
|
163
|
-
function conversationFixture(): FakeContainer {
|
|
164
|
-
const conversation = new FakeContainer();
|
|
165
|
-
conversation.addChild(new ProseBlock(["I will check the config first."]));
|
|
166
|
-
conversation.addChild(new BashRow(3));
|
|
167
|
-
conversation.addChild(new ReadRow(2));
|
|
168
|
-
conversation.addChild(new OddRow());
|
|
169
|
-
conversation.addChild(new ProseBlock([""]));
|
|
170
|
-
return conversation;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const ORIGINS = {
|
|
174
|
-
toolName: [undefined, "bash", "bash", "bash", "read", "read", undefined, undefined],
|
|
175
|
-
component: [
|
|
176
|
-
"ProseBlock",
|
|
177
|
-
"BashRow",
|
|
178
|
-
"BashRow",
|
|
179
|
-
"BashRow",
|
|
180
|
-
"ReadRow",
|
|
181
|
-
"ReadRow",
|
|
182
|
-
"OddRow",
|
|
183
|
-
"ProseBlock",
|
|
184
|
-
],
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
/** The `tui` path `./card-style-wiring.ts` walks: `children[0]` → document → `children[2]`. */
|
|
188
|
-
function tuiFor(conversation: FakeComponent): Record<string, unknown> {
|
|
189
|
-
return {
|
|
190
|
-
terminal: {},
|
|
191
|
-
children: [
|
|
192
|
-
{
|
|
193
|
-
children: [
|
|
194
|
-
{ render: () => ["header"] },
|
|
195
|
-
{ render: () => ["resources"] },
|
|
196
|
-
conversation,
|
|
197
|
-
],
|
|
198
|
-
},
|
|
199
|
-
],
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// ---------------------------------------------------------------------------------------------
|
|
204
|
-
// T1 — the map
|
|
205
|
-
// ---------------------------------------------------------------------------------------------
|
|
206
|
-
|
|
207
|
-
test("T1: every drawn row names the child that produced it, with its tool name and constructor", () => {
|
|
208
|
-
const conversation = conversationFixture();
|
|
209
|
-
const lines = conversation.render(WIDTH);
|
|
210
|
-
|
|
211
|
-
const map = rowOrigins(conversation, WIDTH, lines.length);
|
|
212
|
-
|
|
213
|
-
assert.equal(map.reason, undefined, "a layout consistent with the frame is a complete map");
|
|
214
|
-
assert.equal(map.rows.length, lines.length, "one entry per drawn row");
|
|
215
|
-
assert.deepEqual(
|
|
216
|
-
map.rows.map((row) => row.index),
|
|
217
|
-
lines.map((_, index) => index),
|
|
218
|
-
"the entry says which row it is about",
|
|
219
|
-
);
|
|
220
|
-
assert.deepEqual(map.rows.map((row) => row.toolName), ORIGINS.toolName);
|
|
221
|
-
assert.deepEqual(
|
|
222
|
-
map.rows.map((row) => row.component),
|
|
223
|
-
ORIGINS.component,
|
|
224
|
-
"a row whose child carries no tool name still names its constructor",
|
|
225
|
-
);
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
test("T1: the map is derived from the host's own render pass, with no second render", () => {
|
|
229
|
-
const conversation = conversationFixture();
|
|
230
|
-
let renders = 0;
|
|
231
|
-
const counting = new Proxy(conversation, {
|
|
232
|
-
get(target, key, receiver): unknown {
|
|
233
|
-
if (key === "render") {
|
|
234
|
-
return (width: number): string[] => {
|
|
235
|
-
renders += 1;
|
|
236
|
-
return target.render(width);
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
return Reflect.get(target, key, receiver);
|
|
240
|
-
},
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
const lines = counting.render(WIDTH);
|
|
244
|
-
assert.equal(renders, 1);
|
|
245
|
-
|
|
246
|
-
const map = rowOrigins(counting, WIDTH, lines.length);
|
|
247
|
-
|
|
248
|
-
assert.equal(renders, 1, "reading the layout never renders a child again");
|
|
249
|
-
assert.equal(map.rows.length, lines.length);
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
// ---------------------------------------------------------------------------------------------
|
|
253
|
-
// T1 — every absence is an empty map with a reason, never a guess and never a throw
|
|
254
|
-
// ---------------------------------------------------------------------------------------------
|
|
255
|
-
|
|
256
|
-
test("T1: a component that published no layout yields an empty map and a reason", () => {
|
|
257
|
-
const map = rowOrigins({ render: () => ["one row"] }, WIDTH, 1);
|
|
258
|
-
|
|
259
|
-
assert.deepEqual(map.rows, []);
|
|
260
|
-
assert.equal(map.reason, "no-layout");
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
test("T1: a layout measured at another width yields an empty map and a reason", () => {
|
|
264
|
-
const conversation = conversationFixture();
|
|
265
|
-
const lines = conversation.render(WIDTH);
|
|
266
|
-
|
|
267
|
-
const map = rowOrigins(conversation, WIDTH + 20, lines.length);
|
|
268
|
-
|
|
269
|
-
assert.deepEqual(map.rows, []);
|
|
270
|
-
assert.equal(map.reason, "width-mismatch");
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
test("T1: a layout whose heights do not add up to the frame yields an empty map, never a partial one", () => {
|
|
274
|
-
const conversation = conversationFixture();
|
|
275
|
-
const lines = conversation.render(WIDTH);
|
|
276
|
-
|
|
277
|
-
const map = rowOrigins(conversation, WIDTH, lines.length + 1);
|
|
278
|
-
|
|
279
|
-
assert.deepEqual(map.rows, [], "a partial map would attribute rows to the wrong child");
|
|
280
|
-
assert.equal(map.reason, "height-mismatch");
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
test("T1: a malformed layout entry yields an empty map and a reason", () => {
|
|
284
|
-
const broken = {
|
|
285
|
-
mouseLayout: { width: WIDTH, children: [{ component: {}, height: -2 }] },
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
const map = rowOrigins(broken, WIDTH, -2);
|
|
289
|
-
|
|
290
|
-
assert.deepEqual(map.rows, []);
|
|
291
|
-
assert.equal(map.reason, "bad-layout");
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
test("T1: nothing it can be handed makes it throw", () => {
|
|
295
|
-
for (const value of [null, undefined, 42, "container", {}, { mouseLayout: "nope" }]) {
|
|
296
|
-
const map = rowOrigins(value, WIDTH, 3);
|
|
297
|
-
assert.deepEqual(map.rows, []);
|
|
298
|
-
assert.notEqual(map.reason, undefined);
|
|
299
|
-
}
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
// ---------------------------------------------------------------------------------------------
|
|
303
|
-
// T3 — the wrapper builds the map after the host render and hands it over
|
|
304
|
-
// ---------------------------------------------------------------------------------------------
|
|
305
|
-
|
|
306
|
-
test("T3: the transform receives one origin per row, built after the host's own render", () => {
|
|
307
|
-
const conversation = conversationFixture();
|
|
308
|
-
assert.deepEqual(installCardStyleTransform(tuiFor(conversation)), {
|
|
309
|
-
installed: true,
|
|
310
|
-
outcome: "wrapped",
|
|
311
|
-
component: "chat",
|
|
312
|
-
});
|
|
313
|
-
const frames: Array<readonly RowOrigin[] | undefined> = [];
|
|
314
|
-
registerTransform({
|
|
315
|
-
id: "probe.rows",
|
|
316
|
-
transform: (lines, ctx) => {
|
|
317
|
-
frames.push(ctx.rows);
|
|
318
|
-
return lines;
|
|
319
|
-
},
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
const drawn = conversation.render(WIDTH);
|
|
323
|
-
|
|
324
|
-
assert.equal(frames.length, 1, "the transform ran once per frame, inside the wrapper");
|
|
325
|
-
const rows = frames[0] ?? [];
|
|
326
|
-
assert.equal(rows.length, drawn.length, "one origin entry per drawn row");
|
|
327
|
-
assert.deepEqual(rows.map((row) => row.toolName), ORIGINS.toolName);
|
|
328
|
-
assert.deepEqual(rows.map((row) => row.component), ORIGINS.component);
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
test("T3: a host render that publishes no layout hands the transform an empty map, never an error", () => {
|
|
332
|
-
const conversation: FakeComponent = { render: () => ["plain row", "second row"] };
|
|
333
|
-
installCardStyleTransform(tuiFor(conversation));
|
|
334
|
-
const frames: Array<readonly RowOrigin[] | undefined> = [];
|
|
335
|
-
registerTransform({
|
|
336
|
-
id: "probe.no-layout",
|
|
337
|
-
transform: (lines, ctx) => {
|
|
338
|
-
frames.push(ctx.rows);
|
|
339
|
-
return lines;
|
|
340
|
-
},
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
const drawn = conversation.render(WIDTH);
|
|
344
|
-
|
|
345
|
-
assert.deepEqual(drawn, ["plain row", "second row"], "the frame is drawn untouched");
|
|
346
|
-
assert.deepEqual(frames, [[]], "an unknown origin is an empty array, not a guess");
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
test("T3: the rail runs no transcript transform, so no origin map is ever needed there", () => {
|
|
350
|
-
const railLines = ["rail row one", "rail row two"];
|
|
351
|
-
const tui = createRailTui({ rail: { render: () => [...railLines] }, contentWidth: WIDTH });
|
|
352
|
-
assert.deepEqual(installRailCardStyleTransform(tui), {
|
|
353
|
-
installed: true,
|
|
354
|
-
outcome: "wrapped",
|
|
355
|
-
component: "rail",
|
|
356
|
-
});
|
|
357
|
-
let calls = 0;
|
|
358
|
-
registerTransform({
|
|
359
|
-
id: "probe.rail",
|
|
360
|
-
transform: (lines) => {
|
|
361
|
-
calls += 1;
|
|
362
|
-
return lines;
|
|
363
|
-
},
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
const drawn = tui.renderRail();
|
|
367
|
-
|
|
368
|
-
assert.deepEqual(drawn, railLines, "the rail's own rows are drawn untouched");
|
|
369
|
-
assert.equal(calls, 0, "a plugin's conversation transform never sees the rail");
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
// ---------------------------------------------------------------------------------------------
|
|
373
|
-
// T2 — additive: a transform that ignores `rows` is byte-identical
|
|
374
|
-
// ---------------------------------------------------------------------------------------------
|
|
375
|
-
|
|
376
|
-
test("T2: a transform that ignores ctx.rows leaves the frame byte-identical", () => {
|
|
377
|
-
const conversation = conversationFixture();
|
|
378
|
-
const hostFrame = conversation.render(WIDTH);
|
|
379
|
-
installCardStyleTransform(tuiFor(conversation));
|
|
380
|
-
const seen: Array<readonly RowOrigin[] | undefined> = [];
|
|
381
|
-
registerTransform({
|
|
382
|
-
id: "probe.identity",
|
|
383
|
-
transform: (lines, ctx) => {
|
|
384
|
-
seen.push(ctx.rows);
|
|
385
|
-
return lines;
|
|
386
|
-
},
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
const drawn = conversation.render(WIDTH);
|
|
390
|
-
|
|
391
|
-
assert.equal(seen.length, 1);
|
|
392
|
-
assert.ok((seen[0] ?? []).length > 0, "the new field was delivered, so identity is not vacuous");
|
|
393
|
-
assert.deepEqual(drawn, hostFrame);
|
|
394
|
-
assert.equal(drawn.join("\n"), hostFrame.join("\n"), "the same bytes, row for row");
|
|
395
|
-
});
|