@termwright/probe-ink 0.2.0 → 0.3.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.
@@ -0,0 +1,649 @@
1
+ import {
2
+ INK_VERSION,
3
+ instrumentationSentinel
4
+ } from "./chunk-SLKX554P.js";
5
+
6
+ // src/react-commit-bridge.ts
7
+ var BRIDGE = /* @__PURE__ */ Symbol.for("@termwright/probe-ink/react-commit-bridge.v1");
8
+ var ReactCommitBridge = class {
9
+ #renderers = /* @__PURE__ */ new Map();
10
+ #roots = /* @__PURE__ */ new Map();
11
+ #listeners = /* @__PURE__ */ new Set();
12
+ #nextRendererId = 1;
13
+ register(renderer, delegatedId) {
14
+ const rendererId = delegatedId === void 0 ? this.#nextRendererId++ : delegatedId;
15
+ if (typeof rendererId === "number" && Number.isInteger(rendererId)) {
16
+ this.#nextRendererId = Math.max(this.#nextRendererId, rendererId + 1);
17
+ }
18
+ if (renderer.rendererPackageName === "ink") {
19
+ this.#renderers.set(rendererId, {
20
+ rendererId,
21
+ packageName: "ink",
22
+ ...typeof renderer.rendererVersion === "string" ? { version: renderer.rendererVersion } : {}
23
+ });
24
+ }
25
+ return rendererId;
26
+ }
27
+ commit(rendererId, fiberRoot) {
28
+ const renderer = this.#renderers.get(rendererId);
29
+ if (renderer === void 0) return;
30
+ const containerInfo = fiberRoot.containerInfo;
31
+ if (!isInkRoot(containerInfo)) {
32
+ this.#emit({ type: "invalid-root", renderer, fiberRoot, containerInfo });
33
+ return;
34
+ }
35
+ this.#roots.set(fiberRoot, containerInfo);
36
+ this.#emit({ type: "commit", renderer, fiberRoot, root: containerInfo });
37
+ }
38
+ unmount(rendererId, fiber) {
39
+ const renderer = this.#renderers.get(rendererId);
40
+ if (renderer !== void 0) this.#emit({ type: "unmount", renderer, fiber });
41
+ }
42
+ subscribe(listener) {
43
+ this.#listeners.add(listener);
44
+ return () => this.#listeners.delete(listener);
45
+ }
46
+ roots() {
47
+ return [...this.#roots.values()];
48
+ }
49
+ hasInkRenderer() {
50
+ return this.#renderers.size > 0;
51
+ }
52
+ #emit(event) {
53
+ for (const listener of this.#listeners) {
54
+ try {
55
+ listener(event);
56
+ } catch {
57
+ }
58
+ }
59
+ }
60
+ };
61
+ function installReactCommitBridge(target = globalThis) {
62
+ const holder = target;
63
+ const existing = holder.__REACT_DEVTOOLS_GLOBAL_HOOK__;
64
+ const installed = existing?.[BRIDGE];
65
+ if (installed !== void 0) return installed;
66
+ const bridge = new ReactCommitBridge();
67
+ const hook = Object.create(existing ?? null);
68
+ Object.defineProperties(hook, {
69
+ supportsFiber: { value: true, enumerable: true, configurable: true },
70
+ inject: {
71
+ configurable: true,
72
+ value(renderer) {
73
+ const delegatedId = existing?.inject?.call(existing, renderer);
74
+ return bridge.register(renderer, delegatedId);
75
+ }
76
+ },
77
+ onCommitFiberRoot: {
78
+ configurable: true,
79
+ value(rendererId, root, ...rest) {
80
+ try {
81
+ existing?.onCommitFiberRoot?.call(existing, rendererId, root, ...rest);
82
+ } finally {
83
+ bridge.commit(rendererId, root);
84
+ }
85
+ }
86
+ },
87
+ onCommitFiberUnmount: {
88
+ configurable: true,
89
+ value(rendererId, fiber) {
90
+ try {
91
+ existing?.onCommitFiberUnmount?.call(existing, rendererId, fiber);
92
+ } finally {
93
+ bridge.unmount(rendererId, fiber);
94
+ }
95
+ }
96
+ },
97
+ [BRIDGE]: { value: bridge }
98
+ });
99
+ try {
100
+ const descriptor = Object.getOwnPropertyDescriptor(holder, "__REACT_DEVTOOLS_GLOBAL_HOOK__");
101
+ if (descriptor?.configurable === true && ("writable" in descriptor && descriptor.writable === false || !("writable" in descriptor) && descriptor.set === void 0)) {
102
+ Object.defineProperty(holder, "__REACT_DEVTOOLS_GLOBAL_HOOK__", {
103
+ value: hook,
104
+ writable: true,
105
+ enumerable: descriptor.enumerable ?? false,
106
+ configurable: true
107
+ });
108
+ } else {
109
+ holder.__REACT_DEVTOOLS_GLOBAL_HOOK__ = hook;
110
+ }
111
+ } catch (cause) {
112
+ throw new Error(
113
+ "Ink semantic probe unavailable: the existing React renderer instrumentation hook cannot be composed.",
114
+ { cause }
115
+ );
116
+ }
117
+ return bridge;
118
+ }
119
+ var bridgeLeases = /* @__PURE__ */ new WeakMap();
120
+ function acquireReactCommitBridge(target = globalThis) {
121
+ const holder = target;
122
+ const currentRecord = bridgeLeases.get(target);
123
+ if (currentRecord !== void 0 && holder.__REACT_DEVTOOLS_GLOBAL_HOOK__ === currentRecord.hook) {
124
+ currentRecord.references += 1;
125
+ return leaseFor(target, currentRecord);
126
+ }
127
+ const existingBridge = holder.__REACT_DEVTOOLS_GLOBAL_HOOK__?.[BRIDGE];
128
+ const priorDescriptor = Object.getOwnPropertyDescriptor(holder, "__REACT_DEVTOOLS_GLOBAL_HOOK__");
129
+ const bridge = installReactCommitBridge(target);
130
+ const hook = holder.__REACT_DEVTOOLS_GLOBAL_HOOK__;
131
+ if (hook === void 0) {
132
+ throw new Error(
133
+ "Ink semantic probe unavailable: React renderer instrumentation hook installation disappeared."
134
+ );
135
+ }
136
+ if (existingBridge !== void 0) return { bridge, release() {
137
+ } };
138
+ const record = {
139
+ bridge,
140
+ hook,
141
+ ...priorDescriptor === void 0 ? {} : { priorDescriptor },
142
+ references: 1
143
+ };
144
+ bridgeLeases.set(target, record);
145
+ return leaseFor(target, record);
146
+ }
147
+ function leaseFor(target, record) {
148
+ let released = false;
149
+ return {
150
+ bridge: record.bridge,
151
+ release() {
152
+ if (released) return;
153
+ released = true;
154
+ record.references -= 1;
155
+ if (record.references > 0) return;
156
+ bridgeLeases.delete(target);
157
+ const holder = target;
158
+ if (holder.__REACT_DEVTOOLS_GLOBAL_HOOK__ !== record.hook) return;
159
+ if (record.priorDescriptor === void 0) {
160
+ delete holder.__REACT_DEVTOOLS_GLOBAL_HOOK__;
161
+ } else {
162
+ Object.defineProperty(holder, "__REACT_DEVTOOLS_GLOBAL_HOOK__", record.priorDescriptor);
163
+ }
164
+ }
165
+ };
166
+ }
167
+ var activatedReconcilers = /* @__PURE__ */ new WeakMap();
168
+ function activateInkRendererObservation(reconciler, target = globalThis) {
169
+ const bridge = installReactCommitBridge(target);
170
+ let bridges = activatedReconcilers.get(reconciler);
171
+ if (bridges === void 0) {
172
+ bridges = /* @__PURE__ */ new WeakSet();
173
+ activatedReconcilers.set(reconciler, bridges);
174
+ }
175
+ if (!bridges.has(bridge)) {
176
+ reconciler.injectIntoDevTools();
177
+ if (!bridge.hasInkRenderer())
178
+ throw new Error(
179
+ "Ink semantic probe unavailable: React renderer instrumentation did not register Ink."
180
+ );
181
+ bridges.add(bridge);
182
+ }
183
+ return bridge;
184
+ }
185
+ function requireCommittedInkRoot(event) {
186
+ if (event.type !== "commit") {
187
+ throw new Error(
188
+ "Ink semantic probe unavailable: React renderer instrumentation did not expose expected committed Ink root."
189
+ );
190
+ }
191
+ return event.root;
192
+ }
193
+ function isInkRoot(value) {
194
+ if (typeof value !== "object" || value === null) return false;
195
+ const candidate = value;
196
+ return candidate.nodeName === "ink-root" && Array.isArray(candidate.childNodes);
197
+ }
198
+
199
+ // src/annotations.ts
200
+ import { validateProbeAnnotations } from "@termwright/protocol";
201
+ var REGISTRY = /* @__PURE__ */ Symbol.for("termwright.annotation.ink.v1");
202
+ function channel() {
203
+ const scope = globalThis;
204
+ const present = scope[REGISTRY];
205
+ if (present?.entries instanceof WeakMap && present.listeners instanceof Set) {
206
+ return present;
207
+ }
208
+ const created = {
209
+ entries: /* @__PURE__ */ new WeakMap(),
210
+ listeners: /* @__PURE__ */ new Set()
211
+ };
212
+ Object.defineProperty(scope, REGISTRY, { configurable: true, value: created });
213
+ return created;
214
+ }
215
+ function onInkAnnotationChange(handler) {
216
+ const listeners = channel().listeners;
217
+ listeners.add(handler);
218
+ return () => listeners.delete(handler);
219
+ }
220
+ function strings(refs, idFor, maxTargets) {
221
+ if (refs === void 0) return void 0;
222
+ if (!Array.isArray(refs)) return null;
223
+ const length = Object.getOwnPropertyDescriptor(refs, "length")?.value;
224
+ if (!Number.isSafeInteger(length) || length < 0 || length > maxTargets) return null;
225
+ const ids = [];
226
+ for (let index = 0; index < length; index += 1) {
227
+ try {
228
+ const descriptor = Object.getOwnPropertyDescriptor(refs, String(index));
229
+ if (descriptor === void 0 || !("value" in descriptor)) return null;
230
+ const ref = descriptor.value;
231
+ if (!(ref instanceof WeakRef)) return null;
232
+ const target = WeakRef.prototype.deref.call(ref);
233
+ if (target !== void 0) ids.push(idFor(target));
234
+ } catch {
235
+ return null;
236
+ }
237
+ }
238
+ return ids.length === 0 ? void 0 : ids;
239
+ }
240
+ function ownData(value, key) {
241
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
242
+ return descriptor !== void 0 && "value" in descriptor ? descriptor.value : void 0;
243
+ }
244
+ function annotationForInkNode(node, idFor, limits) {
245
+ try {
246
+ const slot = channel().entries.get(node);
247
+ const value = slot?.current;
248
+ if (value === void 0) return void 0;
249
+ const role = ownData(value, "role");
250
+ const name = ownData(value, "name");
251
+ const description = ownData(value, "description");
252
+ const testId = ownData(value, "testId");
253
+ const extended = ownData(value, "extended");
254
+ const actions = ownData(value, "actions");
255
+ const labelledBy = strings(ownData(value, "labelledBy"), idFor, limits.maxRelationTargets);
256
+ const describedBy = strings(ownData(value, "describedBy"), idFor, limits.maxRelationTargets);
257
+ const candidate = {
258
+ ...role === void 0 ? {} : { role },
259
+ ...name === void 0 ? {} : { name },
260
+ ...description === void 0 ? {} : { description },
261
+ ...testId === void 0 ? {} : { testId },
262
+ ...extended === void 0 ? {} : { extended },
263
+ ...actions === void 0 ? {} : { actions },
264
+ ...labelledBy === void 0 ? {} : { labelledBy },
265
+ ...describedBy === void 0 ? {} : { describedBy }
266
+ };
267
+ if (Object.keys(candidate).length === 0) return void 0;
268
+ const validated = validateProbeAnnotations(candidate, limits);
269
+ return validated.ok ? validated.annotations : void 0;
270
+ } catch {
271
+ return void 0;
272
+ }
273
+ }
274
+
275
+ // src/observe.ts
276
+ var isElement = (node) => node.nodeName !== "#text";
277
+ function observeInkTree(root, options) {
278
+ const objects = [];
279
+ const ids = identityStore(root);
280
+ let truncated = false;
281
+ const geometryRegions = /* @__PURE__ */ new Map();
282
+ const visited = /* @__PURE__ */ new Set();
283
+ const visit = (node, parent, depth, ancestorHidden) => {
284
+ if (node === options.excluded || visited.has(node)) return;
285
+ visited.add(node);
286
+ if (depth > options.limits.maxDepth || objects.length >= options.limits.maxNodes) {
287
+ truncated = true;
288
+ return;
289
+ }
290
+ const hidden = ancestorHidden || node.style?.display === "none";
291
+ const state = observedState(node, !hidden);
292
+ const annotations = annotationForInkNode(
293
+ node,
294
+ (target) => ids.idFor(target),
295
+ options.limits
296
+ );
297
+ const accessibility = observedAccessibility(node);
298
+ const geometry = geometryOf(node, options);
299
+ const identity = ids.idFor(node);
300
+ const region = options.geometry?.get(node)?.region;
301
+ if (region !== void 0) geometryRegions.set(identity, region);
302
+ const children = options.retainedChildren?.get(node) ?? node.childNodes;
303
+ const text = isTextHost(node) ? textOf(children, options.limits.maxStringBytes) : void 0;
304
+ const unobservable = unobservableFor(
305
+ node,
306
+ geometry?.intendedRect !== void 0,
307
+ text !== void 0
308
+ );
309
+ objects.push({
310
+ identity: { kind: "stable", value: identity },
311
+ frameworkType: node.nodeName,
312
+ ...parent === void 0 ? {} : { parent: ids.idFor(parent) },
313
+ ...geometry === void 0 ? {} : { geometry },
314
+ ...state === void 0 ? {} : { state },
315
+ ...text === void 0 ? {} : { text },
316
+ ...accessibility === void 0 ? {} : { accessibility },
317
+ ...annotations === void 0 ? {} : { annotations },
318
+ unobservable
319
+ });
320
+ for (const child of children) {
321
+ if (isElement(child)) visit(child, node, depth + 1, hidden);
322
+ }
323
+ };
324
+ visit(root, void 0, 0, false);
325
+ if (root.staticNode !== void 0) visit(root.staticNode, root, 1, false);
326
+ for (const retained of options.retainedRoots ?? []) visit(retained, root, 1, false);
327
+ return { frame: { frame: options.frame, objects }, truncated, geometryRegions };
328
+ }
329
+ var stores = /* @__PURE__ */ new WeakMap();
330
+ function identityStore(root) {
331
+ let store = stores.get(root);
332
+ if (store !== void 0) return store;
333
+ const ids = /* @__PURE__ */ new WeakMap();
334
+ let nextId = 0;
335
+ store = {
336
+ idFor(node) {
337
+ const existing = ids.get(node);
338
+ if (existing !== void 0) return existing;
339
+ nextId += 1;
340
+ const id = String(nextId);
341
+ ids.set(node, id);
342
+ return id;
343
+ }
344
+ };
345
+ stores.set(root, store);
346
+ return store;
347
+ }
348
+ function isTextHost(node) {
349
+ return node.nodeName === "ink-text" || node.nodeName === "ink-virtual-text";
350
+ }
351
+ function observedAccessibility(node) {
352
+ const role = node.internal_accessibility?.role;
353
+ return role === void 0 ? void 0 : { role };
354
+ }
355
+ function observedState(node, displayed) {
356
+ const accessibility = node.internal_accessibility?.state;
357
+ const state = {
358
+ displayed,
359
+ ...accessibility?.checked === void 0 ? {} : { checked: accessibility.checked },
360
+ ...accessibility?.disabled === void 0 ? {} : { disabled: accessibility.disabled },
361
+ ...accessibility?.expanded === void 0 ? {} : { expanded: accessibility.expanded },
362
+ ...accessibility?.readonly === void 0 ? {} : { readonly: accessibility.readonly },
363
+ ...accessibility?.selected === void 0 ? {} : { selected: accessibility.selected },
364
+ ...accessibility?.busy === void 0 ? {} : { busy: accessibility.busy },
365
+ ...accessibility?.multiline === void 0 ? {} : { multiline: accessibility.multiline },
366
+ ...accessibility?.required === void 0 ? {} : { required: accessibility.required },
367
+ ...accessibility?.multiselectable === void 0 ? {} : { multiselectable: accessibility.multiselectable }
368
+ };
369
+ return state;
370
+ }
371
+ function geometryOf(node, options) {
372
+ const geometry = options.geometry?.get(node);
373
+ return geometry === void 0 ? void 0 : { intendedRect: geometry.intended, visibleRect: geometry.visible };
374
+ }
375
+ function textOf(children, maxBytes) {
376
+ const parts = [];
377
+ let bytes = 0;
378
+ const append = (value) => {
379
+ for (const codePoint of value) {
380
+ const size = Buffer.byteLength(codePoint, "utf8");
381
+ if (bytes + size > maxBytes) return;
382
+ parts.push(codePoint);
383
+ bytes += size;
384
+ }
385
+ };
386
+ for (const child of children) {
387
+ if (bytes >= maxBytes) break;
388
+ if (!isElement(child)) append(child.nodeValue);
389
+ }
390
+ const text = parts.join("").replace(/\s+/gu, " ").trim();
391
+ return text.length === 0 ? void 0 : text;
392
+ }
393
+ function unobservableFor(node, hasGeometry, hasText) {
394
+ const result = [
395
+ "focused",
396
+ "value",
397
+ "selectedIndex",
398
+ "textSelection",
399
+ "scroll",
400
+ "scrollExtent",
401
+ "paintOrder"
402
+ ];
403
+ const state = node.internal_accessibility?.state;
404
+ if (state?.disabled === void 0) result.push("disabled");
405
+ if (state?.checked === void 0) result.push("checked");
406
+ if (state?.expanded === void 0) result.push("expanded");
407
+ if (state?.readonly === void 0) result.push("readonly");
408
+ if (state?.selected === void 0) result.push("selected");
409
+ if (state?.busy === void 0) result.push("busy");
410
+ if (state?.multiline === void 0) result.push("multiline");
411
+ if (!hasGeometry) result.push("intendedRect", "visibleRect");
412
+ if (!hasText && isTextHost(node)) result.push("text");
413
+ return result;
414
+ }
415
+
416
+ // src/version.ts
417
+ var PACKAGE_VERSION = "0.3.1";
418
+
419
+ // src/probe-info.ts
420
+ function probeInfo(frameworkVersion = instrumentationSentinel()?.frameworkVersion ?? INK_VERSION) {
421
+ return {
422
+ framework: "ink",
423
+ frameworkVersion,
424
+ probeVersion: PACKAGE_VERSION,
425
+ identityKind: "stable",
426
+ capabilities: ["stable-identity", "intended-rect", "visible-rect", "annotations"],
427
+ instrumentation: {
428
+ highestTier: "T3",
429
+ semanticClass: "A",
430
+ degradedCapabilities: []
431
+ }
432
+ };
433
+ }
434
+
435
+ // src/session.ts
436
+ import { writeWindowsConsoleMarker } from "@termwright/pty";
437
+ import { recognize } from "@termwright/recognizers";
438
+ function createInkSession(options) {
439
+ let revision = 0;
440
+ let frames = 0;
441
+ let latestFrame = 0;
442
+ let stopped = false;
443
+ let queue = Promise.resolve();
444
+ const publicationWaiters = [];
445
+ const fail = (error) => {
446
+ if (stopped) return;
447
+ stopped = true;
448
+ const failure = error instanceof Error ? error : new Error(String(error));
449
+ for (const waiter of publicationWaiters.splice(0)) waiter.reject(failure);
450
+ options.onGuaranteeViolation?.(failure);
451
+ options.channel.close();
452
+ };
453
+ const stop = () => {
454
+ if (stopped) return;
455
+ stopped = true;
456
+ const failure = new Error("Ink probe stopped");
457
+ for (const waiter of publicationWaiters.splice(0)) waiter.reject(failure);
458
+ options.channel.close();
459
+ };
460
+ const resolvePublications = (frame, publishedRevision) => {
461
+ for (let index = publicationWaiters.length - 1; index >= 0; index -= 1) {
462
+ const waiter = publicationWaiters[index];
463
+ if (waiter === void 0 || waiter.targetFrame > frame) continue;
464
+ publicationWaiters.splice(index, 1);
465
+ waiter.resolve(publishedRevision);
466
+ }
467
+ };
468
+ const publish = async (frozen) => {
469
+ await nextMacrotask();
470
+ await options.waitForRenderFlush();
471
+ await options.tracker.drain();
472
+ if (stopped) return null;
473
+ if (!options.channel.isOpen) {
474
+ fail(new Error("Ink semantic channel closed before publication"));
475
+ return null;
476
+ }
477
+ if (frozen.number !== latestFrame) {
478
+ options.channel.recordCoalescedEvent();
479
+ return null;
480
+ }
481
+ const context = frozen.capture.context;
482
+ if (context === void 0) throw new Error("certified Ink frame context is unavailable");
483
+ if (frozen.capture.screenReader) {
484
+ throw new Error("Ink screen-reader output has no authoritative per-node cell geometry");
485
+ }
486
+ const position = options.tracker.position();
487
+ if ((context.alternateScreen ? "alternate" : "normal") !== position.buffer) {
488
+ throw new Error("Ink render mode and committed VT buffer disagree");
489
+ }
490
+ const columns = options.stdout.columns ?? 80;
491
+ const rows = options.stdout.rows ?? 24;
492
+ const qualified = qualifyFrame(frozen, position, columns, rows);
493
+ revision += 1;
494
+ const snapshot = recognize(qualified, {
495
+ sessionId: options.channel.session.sessionId,
496
+ revision,
497
+ columns,
498
+ rows,
499
+ framework: "ink",
500
+ paintOrderKnown: false,
501
+ maxStringBytes: options.channel.session.limits.maxStringBytes
502
+ });
503
+ const marker = options.channel.publish(snapshot, {
504
+ probeEvents: qualified.objects.length + (qualified.operations?.length ?? 0)
505
+ });
506
+ if (marker === void 0) throw new Error("Ink semantic publication was refused");
507
+ await options.writeMarker(marker);
508
+ resolvePublications(frozen.number, revision);
509
+ return revision;
510
+ };
511
+ return {
512
+ get revision() {
513
+ return revision;
514
+ },
515
+ get frames() {
516
+ return frames;
517
+ },
518
+ notifyRender(notifyOptions = {}) {
519
+ if (stopped) return Promise.resolve(null);
520
+ try {
521
+ const root = options.resolveRoot();
522
+ if (root === null) throw new Error("Ink committed frame has no retained root");
523
+ const capture = options.resolveCapture(root);
524
+ if (capture === void 0 || capture.root !== root) {
525
+ throw new Error("Ink committed frame has no matching certified renderer capture");
526
+ }
527
+ const excluded = options.resolveExcluded?.();
528
+ const observation = observeInkTree(root, {
529
+ frame: frames,
530
+ limits: options.channel.session.limits,
531
+ ...excluded === void 0 ? {} : { excluded },
532
+ ...capture.staticRoots.length === 0 ? {} : { retainedRoots: capture.staticRoots },
533
+ ...capture.staticChildren.size === 0 ? {} : { retainedChildren: capture.staticChildren },
534
+ geometry: capture.geometry
535
+ });
536
+ if (hasDisplayedNodeWithoutGeometry(observation.frame)) {
537
+ if (notifyOptions.allowUnsettled === true) return Promise.resolve(null);
538
+ throw new Error(
539
+ "certified Ink renderer capture is missing geometry for a displayed host node"
540
+ );
541
+ }
542
+ frames += 1;
543
+ latestFrame = frames;
544
+ const frozen = { number: frames, capture, observation };
545
+ const boundary = notifyOptions.awaitPublication === true ? new Promise((resolve, reject) => {
546
+ publicationWaiters.push({ targetFrame: frozen.number, resolve, reject });
547
+ }) : null;
548
+ const publication = queue.then(() => publish(frozen)).catch((error) => {
549
+ fail(error);
550
+ return null;
551
+ });
552
+ queue = publication.then(() => void 0);
553
+ return boundary ?? publication;
554
+ } catch (error) {
555
+ fail(error);
556
+ return Promise.resolve(null);
557
+ }
558
+ },
559
+ async flush() {
560
+ await queue.catch(() => void 0);
561
+ },
562
+ stop
563
+ };
564
+ }
565
+ function hasDisplayedNodeWithoutGeometry(frame) {
566
+ return frame.objects.some(
567
+ (object) => object.state?.displayed !== false && object.geometry?.intendedRect === void 0
568
+ );
569
+ }
570
+ function qualifyFrame(frozen, position, columns, rows) {
571
+ const { capture, observation } = frozen;
572
+ const context = capture.context;
573
+ const fullscreen = context.stdoutIsTTY && capture.liveRows >= context.rows;
574
+ const liveOrigin = context.alternateScreen ? 0 : !context.interactive ? position.row : context.debug || fullscreen ? position.row - Math.max(0, capture.liveRows - 1) : position.row - capture.liveRows;
575
+ const staticOrigin = liveOrigin - capture.staticRows;
576
+ return {
577
+ ...observation.frame,
578
+ objects: observation.frame.objects.map((object) => {
579
+ const region = observation.geometryRegions.get(object.identity.value);
580
+ const geometry = object.geometry;
581
+ if (geometry?.intendedRect === void 0 || geometry.visibleRect === void 0 || region === void 0)
582
+ return object;
583
+ const origin = region === "live" ? liveOrigin : staticOrigin;
584
+ const intendedRect = shift(geometry.intendedRect, origin);
585
+ const visibleRect = context.interactive || region === "static" || context.debug ? viewportIntersection(shift(geometry.visibleRect, origin), columns, rows) : { row: Math.min(Math.max(origin, 0), rows), column: 0, width: 0, height: 0 };
586
+ return { ...object, geometry: { intendedRect, visibleRect } };
587
+ })
588
+ };
589
+ }
590
+ function shift(rect, rows) {
591
+ return { ...rect, row: rect.row + rows };
592
+ }
593
+ function viewportIntersection(rect, columns, rows) {
594
+ const column = Math.max(0, rect.column);
595
+ const row = Math.max(0, rect.row);
596
+ const right = Math.max(column, Math.min(columns, rect.column + rect.width));
597
+ const bottom = Math.max(row, Math.min(rows, rect.row + rect.height));
598
+ return { row, column, width: right - column, height: bottom - row };
599
+ }
600
+ function nextMacrotask() {
601
+ return new Promise((resolve) => setImmediate(resolve));
602
+ }
603
+ function createInkMarkerWriter(stream, options) {
604
+ const platform = options.platform ?? process.platform;
605
+ if (!options.certifiedHarness && platform === "win32" && stream.isTTY === true) {
606
+ const fd = stream.fd;
607
+ if (typeof fd !== "number" || !Number.isInteger(fd) || fd < 0) {
608
+ return () => Promise.reject(new Error("Ink stdout has no certifiable Windows console handle"));
609
+ }
610
+ const writeNative = options.writeWindowsMarker ?? writeWindowsConsoleMarker;
611
+ return (marker) => {
612
+ try {
613
+ writeNative(fd, marker);
614
+ return Promise.resolve();
615
+ } catch (error) {
616
+ return Promise.reject(error instanceof Error ? error : new Error(String(error)));
617
+ }
618
+ };
619
+ }
620
+ return (marker) => new Promise((resolve, reject) => {
621
+ if (stream.writableEnded || stream.destroyed) {
622
+ reject(new Error("Ink stdout closed before the semantic render marker could be written"));
623
+ return;
624
+ }
625
+ try {
626
+ stream.write(marker, (error) => {
627
+ if (error instanceof Error) reject(error);
628
+ else resolve();
629
+ });
630
+ } catch (error) {
631
+ reject(error instanceof Error ? error : new Error(String(error)));
632
+ }
633
+ });
634
+ }
635
+
636
+ export {
637
+ ReactCommitBridge,
638
+ installReactCommitBridge,
639
+ acquireReactCommitBridge,
640
+ activateInkRendererObservation,
641
+ requireCommittedInkRoot,
642
+ onInkAnnotationChange,
643
+ observeInkTree,
644
+ PACKAGE_VERSION,
645
+ probeInfo,
646
+ createInkSession,
647
+ createInkMarkerWriter
648
+ };
649
+ //# sourceMappingURL=chunk-Q75BSILO.js.map