@nuvin/ink 7.0.2-alpha → 7.1.1-alpha

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.
Files changed (53) hide show
  1. package/build/components/App.d.ts +3 -1
  2. package/build/components/App.js +4 -2
  3. package/build/components/App.js.map +1 -1
  4. package/build/components/Text.d.ts +9 -1
  5. package/build/components/Text.js +2 -2
  6. package/build/components/Text.js.map +1 -1
  7. package/build/components/TextSelectionContext.d.ts +6 -0
  8. package/build/components/TextSelectionContext.js +7 -0
  9. package/build/components/TextSelectionContext.js.map +1 -0
  10. package/build/dom.d.ts +1 -0
  11. package/build/dom.js.map +1 -1
  12. package/build/hooks/use-text-selection-actions.d.ts +10 -0
  13. package/build/hooks/use-text-selection-actions.js +26 -0
  14. package/build/hooks/use-text-selection-actions.js.map +1 -0
  15. package/build/hooks/use-text-selection.d.ts +9 -0
  16. package/build/hooks/use-text-selection.js +26 -0
  17. package/build/hooks/use-text-selection.js.map +1 -0
  18. package/build/index.d.ts +4 -0
  19. package/build/index.js +2 -0
  20. package/build/index.js.map +1 -1
  21. package/build/ink.d.ts +1 -0
  22. package/build/ink.js +12 -2
  23. package/build/ink.js.map +1 -1
  24. package/build/output.d.ts +9 -1
  25. package/build/output.js +31 -4
  26. package/build/output.js.map +1 -1
  27. package/build/paint-selection.d.ts +12 -2
  28. package/build/paint-selection.js +45 -98
  29. package/build/paint-selection.js.map +1 -1
  30. package/build/reconciler.js +8 -0
  31. package/build/reconciler.js.map +1 -1
  32. package/build/render-node-to-output.js +8 -2
  33. package/build/render-node-to-output.js.map +1 -1
  34. package/build/renderer.d.ts +2 -1
  35. package/build/renderer.js +13 -2
  36. package/build/renderer.js.map +1 -1
  37. package/build/text-selection-controller.d.ts +116 -0
  38. package/build/text-selection-controller.js +340 -0
  39. package/build/text-selection-controller.js.map +1 -0
  40. package/build/text-selection.d.ts +41 -0
  41. package/build/text-selection.js +101 -0
  42. package/build/text-selection.js.map +1 -0
  43. package/package.json +1 -1
  44. package/readme.md +47 -0
  45. package/build/current-frame.d.ts +0 -27
  46. package/build/current-frame.js +0 -73
  47. package/build/current-frame.js.map +0 -1
  48. package/build/hooks/use-selection-overlay.d.ts +0 -3
  49. package/build/hooks/use-selection-overlay.js +0 -14
  50. package/build/hooks/use-selection-overlay.js.map +0 -1
  51. package/build/selection.d.ts +0 -28
  52. package/build/selection.js +0 -9
  53. package/build/selection.js.map +0 -1
@@ -0,0 +1,116 @@
1
+ import { type TextSelectionPoint, type TextSelectionSpan } from './text-selection.js';
2
+ /**
3
+ Live viewport of the selection container, in Ink output grid cells.
4
+ */
5
+ export type SelectionViewport = {
6
+ top: number;
7
+ left: number;
8
+ width: number;
9
+ height: number;
10
+ /** Vertical scroll offset in rendered rows: content row shown at `top`. */
11
+ scrollY: number;
12
+ };
13
+ export type TextSelectionSnapshot = {
14
+ /** Content coordinates (see TextSelectionPoint). */
15
+ anchor: TextSelectionPoint | null;
16
+ focus: TextSelectionPoint | null;
17
+ isDragging: boolean;
18
+ text: string;
19
+ isEmpty: boolean;
20
+ };
21
+ export type TextSelectionActions = {
22
+ /**
23
+ Points passed to start/update are 0-based SCREEN cells on the Ink output
24
+ grid; the controller converts to content space via the viewport provider.
25
+ */
26
+ start: (screenPoint: {
27
+ x: number;
28
+ y: number;
29
+ }) => void;
30
+ update: (screenPoint: {
31
+ x: number;
32
+ y: number;
33
+ }) => void;
34
+ finish: () => void;
35
+ clear: () => void;
36
+ /**
37
+ Registered by the mouse bridge. Providers stack: the most recently
38
+ registered one is active (e.g. a modal's selection area over the main
39
+ list's), and the returned unregister function restores the previous one.
40
+ A provider returning null means "bounds not measurable this frame".
41
+ */
42
+ registerViewportProvider: (provider: () => SelectionViewport | null) => () => void;
43
+ /**
44
+ Single-slot convenience over registerViewportProvider: replaces the
45
+ provider set by the previous call (null unregisters it). Prefer
46
+ registerViewportProvider where mounts can overlap.
47
+ */
48
+ setViewportProvider: (provider: (() => SelectionViewport | null) | null) => void;
49
+ };
50
+ /**
51
+ Selection actions plus an imperative snapshot read. Unlike the snapshot from
52
+ `useTextSelection()`, holding this handle does NOT subscribe the component to
53
+ selection changes — use it where selection state is only needed at event time
54
+ (mouse bridges, key handlers) so drags never re-render the host component.
55
+ */
56
+ export type TextSelectionHandle = TextSelectionActions & {
57
+ getSnapshot: () => TextSelectionSnapshot;
58
+ };
59
+ /**
60
+ Framework-agnostic selection state owned by an Ink instance. Selection points
61
+ live in content space (container-relative rows that survive scrolling); the
62
+ viewport provider converts screen ↔ content at event time and paint time.
63
+ */
64
+ export declare class TextSelectionController {
65
+ /** Wired by Ink to schedule a repaint when the highlight must change. */
66
+ onInvalidate?: () => void;
67
+ private anchor;
68
+ private focus;
69
+ private isDragging;
70
+ private text;
71
+ private readonly rowCache;
72
+ private readonly viewportProviders;
73
+ private legacyProviderUnregister;
74
+ private lastViewport;
75
+ private readonly listeners;
76
+ private snapshot;
77
+ private notifyScheduled;
78
+ subscribe: (listener: () => void) => (() => void);
79
+ getSnapshot: () => TextSelectionSnapshot;
80
+ registerViewportProvider: (provider: () => SelectionViewport | null) => (() => void);
81
+ setViewportProvider: (provider: (() => SelectionViewport | null) | null) => void;
82
+ start: (screenPoint: {
83
+ x: number;
84
+ y: number;
85
+ }) => void;
86
+ update: (screenPoint: {
87
+ x: number;
88
+ y: number;
89
+ }) => void;
90
+ finish: () => void;
91
+ clear: () => void;
92
+ /**
93
+ Called from the renderer's paint hook on every render of the main output.
94
+ Caches visible rows under content-row keys. Never invalidates synchronously
95
+ — it runs during a render pass and must not schedule another render.
96
+
97
+ While dragging, cache entries update freely (auto-scroll depends on it).
98
+ After `finish()`, the cache is frozen: existing entries are never
99
+ overwritten, so extracted text is immutable. If a visible row inside the
100
+ selected range no longer matches its frozen entry (content reflowed
101
+ underneath the selection), the selection auto-clears.
102
+ */
103
+ captureRows: (plainRows: readonly string[], maskRows?: ReadonlyArray<readonly boolean[]>) => void;
104
+ /**
105
+ Convert the selection's content spans to screen spans using the current
106
+ viewport. Rows outside the viewport or the grid are dropped (they are
107
+ still part of the extracted text).
108
+ */
109
+ getPaintSpans: (outputWidth: number, outputHeight: number) => TextSelectionSpan[];
110
+ private resolveViewport;
111
+ private toContentPoint;
112
+ /** Returns true when the extracted text changed. */
113
+ private recomputeText;
114
+ private emit;
115
+ private scheduleNotify;
116
+ }
@@ -0,0 +1,340 @@
1
+ /* eslint-disable @typescript-eslint/no-restricted-types --
2
+ The public selection API uses null (matching React ref idioms and the
3
+ mouse bridge in ink-input); keep it consistent throughout this file. */
4
+ import stringWidth from 'string-width';
5
+ import { extractTextFromRows, getSelectionSpans, } from './text-selection.js';
6
+ const fullGridViewport = {
7
+ top: 0,
8
+ left: 0,
9
+ width: Number.POSITIVE_INFINITY,
10
+ height: Number.POSITIVE_INFINITY,
11
+ scrollY: 0,
12
+ };
13
+ /**
14
+ Framework-agnostic selection state owned by an Ink instance. Selection points
15
+ live in content space (container-relative rows that survive scrolling); the
16
+ viewport provider converts screen ↔ content at event time and paint time.
17
+ */
18
+ export class TextSelectionController {
19
+ /** Wired by Ink to schedule a repaint when the highlight must change. */
20
+ onInvalidate;
21
+ anchor = null;
22
+ focus = null;
23
+ isDragging = false;
24
+ text = '';
25
+ rowCache = new Map();
26
+ // Stack of registered providers; the last entry is active. Stacking lets
27
+ // an overlay (tool detail modal) take over selection while mounted and
28
+ // hand coordinates back to the underlying list on unmount.
29
+ viewportProviders = [];
30
+ legacyProviderUnregister = null;
31
+ // Last viewport a registered provider returned. Reused for one-frame gaps
32
+ // where the provider returns null (e.g. bounds momentarily 0 mid-re-render),
33
+ // so the highlight doesn't flash to full-grid coordinates and the frozen
34
+ // row cache isn't compared against a differently-sliced viewport.
35
+ lastViewport = null;
36
+ listeners = new Set();
37
+ snapshot;
38
+ notifyScheduled = false;
39
+ subscribe = (listener) => {
40
+ this.listeners.add(listener);
41
+ return () => {
42
+ this.listeners.delete(listener);
43
+ };
44
+ };
45
+ getSnapshot = () => {
46
+ this.snapshot ??= {
47
+ anchor: this.anchor,
48
+ focus: this.focus,
49
+ isDragging: this.isDragging,
50
+ text: this.text,
51
+ isEmpty: this.text.length === 0,
52
+ };
53
+ return this.snapshot;
54
+ };
55
+ registerViewportProvider = (provider) => {
56
+ this.viewportProviders.push(provider);
57
+ // The coordinate space changed under any live selection; drop it
58
+ // rather than paint it through the wrong viewport, and forget the
59
+ // previous provider's cached viewport.
60
+ this.lastViewport = null;
61
+ if (this.anchor !== null) {
62
+ this.clear();
63
+ }
64
+ return () => {
65
+ const index = this.viewportProviders.indexOf(provider);
66
+ if (index === -1) {
67
+ return;
68
+ }
69
+ this.viewportProviders.splice(index, 1);
70
+ this.lastViewport = null;
71
+ if (this.anchor !== null) {
72
+ this.clear();
73
+ }
74
+ };
75
+ };
76
+ setViewportProvider = (provider) => {
77
+ this.legacyProviderUnregister?.();
78
+ this.legacyProviderUnregister = provider
79
+ ? this.registerViewportProvider(provider)
80
+ : null;
81
+ };
82
+ start = (screenPoint) => {
83
+ const point = this.toContentPoint(screenPoint);
84
+ this.anchor = point;
85
+ this.focus = point;
86
+ this.isDragging = true;
87
+ this.recomputeText();
88
+ this.emit();
89
+ this.onInvalidate?.();
90
+ };
91
+ update = (screenPoint) => {
92
+ if (!this.anchor) {
93
+ return;
94
+ }
95
+ const point = this.toContentPoint(screenPoint);
96
+ const focusChanged = this.focus?.x !== point.x || this.focus.y !== point.y;
97
+ this.focus = point;
98
+ const textChanged = this.recomputeText();
99
+ if (focusChanged || textChanged) {
100
+ this.emit();
101
+ this.onInvalidate?.();
102
+ }
103
+ };
104
+ finish = () => {
105
+ if (!this.isDragging) {
106
+ return;
107
+ }
108
+ this.isDragging = false;
109
+ this.emit();
110
+ };
111
+ clear = () => {
112
+ const hadSelection = this.anchor !== null || this.text.length > 0 || this.isDragging;
113
+ this.anchor = null;
114
+ this.focus = null;
115
+ this.isDragging = false;
116
+ this.text = '';
117
+ this.rowCache.clear();
118
+ if (hadSelection) {
119
+ this.emit();
120
+ this.onInvalidate?.();
121
+ }
122
+ };
123
+ /**
124
+ Called from the renderer's paint hook on every render of the main output.
125
+ Caches visible rows under content-row keys. Never invalidates synchronously
126
+ — it runs during a render pass and must not schedule another render.
127
+
128
+ While dragging, cache entries update freely (auto-scroll depends on it).
129
+ After `finish()`, the cache is frozen: existing entries are never
130
+ overwritten, so extracted text is immutable. If a visible row inside the
131
+ selected range no longer matches its frozen entry (content reflowed
132
+ underneath the selection), the selection auto-clears.
133
+ */
134
+ captureRows = (plainRows, maskRows) => {
135
+ const resolved = this.resolveViewport();
136
+ // A registered provider that returns null means the container's bounds are
137
+ // not measurable this frame (transient during a re-render). Capturing or
138
+ // comparing against the full-grid fallback would corrupt the cache and
139
+ // spuriously auto-clear a finished selection. Skip the frame instead.
140
+ if (!resolved.available && this.anchor !== null) {
141
+ return;
142
+ }
143
+ const { viewport } = resolved;
144
+ const top = Math.max(0, viewport.top);
145
+ const left = Math.max(0, viewport.left);
146
+ const bottom = Math.min(plainRows.length, viewport.top + viewport.height);
147
+ const hasSelection = this.anchor !== null && this.focus !== null;
148
+ const frozen = hasSelection && !this.isDragging;
149
+ // Without a selection there is nothing to preserve: reset the cache to
150
+ // the currently visible rows so a subsequent start() has fresh data and
151
+ // stale rows from earlier scroll positions don't accumulate.
152
+ if (!hasSelection) {
153
+ this.rowCache.clear();
154
+ }
155
+ let mismatch = false;
156
+ const range = hasSelection
157
+ ? (() => {
158
+ const [a, b] = [this.anchor, this.focus];
159
+ return a.y <= b.y ? { min: a.y, max: b.y } : { min: b.y, max: a.y };
160
+ })()
161
+ : undefined;
162
+ for (let y = top; y < bottom; y++) {
163
+ const contentY = viewport.scrollY + (y - viewport.top);
164
+ const text = sliceRow(plainRows[y] ?? '', left, viewport.width);
165
+ const mask = maskRows
166
+ ? sliceMask(maskRows[y], left, viewport.width)
167
+ : undefined;
168
+ const row = { text, mask };
169
+ const existing = this.rowCache.get(contentY);
170
+ if (frozen && existing) {
171
+ if (range &&
172
+ contentY >= range.min &&
173
+ contentY <= range.max &&
174
+ existing.text !== text) {
175
+ mismatch = true;
176
+ break;
177
+ }
178
+ continue;
179
+ }
180
+ this.rowCache.set(contentY, row);
181
+ }
182
+ if (mismatch) {
183
+ // Content reflowed underneath a finished selection: clearing is the
184
+ // only honest option. Defer the notification outside the render pass.
185
+ this.anchor = null;
186
+ this.focus = null;
187
+ this.isDragging = false;
188
+ this.text = '';
189
+ this.rowCache.clear();
190
+ this.snapshot = undefined;
191
+ this.scheduleNotify(true);
192
+ return;
193
+ }
194
+ if (hasSelection && !frozen && this.recomputeText()) {
195
+ this.scheduleNotify(false);
196
+ }
197
+ else if (hasSelection &&
198
+ frozen &&
199
+ this.text.length === 0 && // Rows may have arrived only after the gesture ended.
200
+ this.recomputeText()) {
201
+ this.scheduleNotify(false);
202
+ }
203
+ };
204
+ /**
205
+ Convert the selection's content spans to screen spans using the current
206
+ viewport. Rows outside the viewport or the grid are dropped (they are
207
+ still part of the extracted text).
208
+ */
209
+ getPaintSpans = (outputWidth, outputHeight) => {
210
+ if (!this.anchor || !this.focus) {
211
+ return [];
212
+ }
213
+ const { viewport } = this.resolveViewport();
214
+ const spans = getSelectionSpans(this.anchor, this.focus, y => this.rowCache.get(y));
215
+ const left = Math.max(0, viewport.left);
216
+ const right = Math.min(outputWidth, viewport.left + viewport.width);
217
+ const screenTop = Math.max(0, viewport.top);
218
+ const screenBottom = Math.min(outputHeight, viewport.top + viewport.height);
219
+ const result = [];
220
+ for (const span of spans) {
221
+ const screenY = viewport.top + (span.y - viewport.scrollY);
222
+ if (screenY < screenTop || screenY >= screenBottom) {
223
+ continue;
224
+ }
225
+ const x1 = Math.max(left, viewport.left + span.x1);
226
+ const x2 = Math.min(right, viewport.left + span.x2);
227
+ if (x2 <= x1) {
228
+ continue;
229
+ }
230
+ result.push({ y: screenY, x1, x2 });
231
+ }
232
+ return result;
233
+ };
234
+ resolveViewport() {
235
+ const provider = this.viewportProviders.at(-1);
236
+ if (!provider) {
237
+ // No scroll container: the whole grid is the viewport.
238
+ return { viewport: fullGridViewport, available: true };
239
+ }
240
+ const current = provider();
241
+ if (current) {
242
+ this.lastViewport = current;
243
+ return { viewport: current, available: true };
244
+ }
245
+ // Provider registered but bounds unavailable this frame. Reuse the last
246
+ // known viewport so the highlight stays put; report unavailable so
247
+ // captureRows skips the frame.
248
+ return {
249
+ viewport: this.lastViewport ?? fullGridViewport,
250
+ available: false,
251
+ };
252
+ }
253
+ toContentPoint(screenPoint) {
254
+ const { viewport } = this.resolveViewport();
255
+ const maxX = viewport.width === Number.POSITIVE_INFINITY
256
+ ? Number.POSITIVE_INFINITY
257
+ : viewport.left + viewport.width - 1;
258
+ const maxY = viewport.height === Number.POSITIVE_INFINITY
259
+ ? Number.POSITIVE_INFINITY
260
+ : viewport.top + viewport.height - 1;
261
+ const x = Math.min(Math.max(screenPoint.x, viewport.left), maxX);
262
+ const y = Math.min(Math.max(screenPoint.y, viewport.top), maxY);
263
+ return {
264
+ x: x - viewport.left,
265
+ y: y - viewport.top + viewport.scrollY,
266
+ };
267
+ }
268
+ /** Returns true when the extracted text changed. */
269
+ recomputeText() {
270
+ const next = this.anchor && this.focus
271
+ ? extractTextFromRows(this.anchor, this.focus, y => this.rowCache.get(y))
272
+ : '';
273
+ if (next === this.text) {
274
+ return false;
275
+ }
276
+ this.text = next;
277
+ return true;
278
+ }
279
+ emit() {
280
+ this.snapshot = undefined;
281
+ for (const listener of this.listeners) {
282
+ listener();
283
+ }
284
+ }
285
+ scheduleNotify(invalidate) {
286
+ this.snapshot = undefined;
287
+ if (this.notifyScheduled) {
288
+ return;
289
+ }
290
+ this.notifyScheduled = true;
291
+ queueMicrotask(() => {
292
+ this.notifyScheduled = false;
293
+ for (const listener of this.listeners) {
294
+ listener();
295
+ }
296
+ if (invalidate) {
297
+ this.onInvalidate?.();
298
+ }
299
+ });
300
+ }
301
+ }
302
+ // Slice a row by display cells, not string indices, so wide characters keep
303
+ // the text aligned with the cell-indexed mask. A wide glyph straddling the
304
+ // viewport edge is replaced by spaces for the cells that remain visible.
305
+ const sliceRow = (row, left, width) => {
306
+ if (left === 0 && width === Number.POSITIVE_INFINITY) {
307
+ return row.trimEnd();
308
+ }
309
+ const right = width === Number.POSITIVE_INFINITY
310
+ ? Number.POSITIVE_INFINITY
311
+ : left + width;
312
+ let cell = 0;
313
+ let result = '';
314
+ for (const char of row) {
315
+ const charWidth = Math.max(1, stringWidth(char));
316
+ const start = cell;
317
+ const end = cell + charWidth;
318
+ cell = end;
319
+ if (end <= left) {
320
+ continue;
321
+ }
322
+ if (start >= right) {
323
+ break;
324
+ }
325
+ result +=
326
+ start >= left && end <= right
327
+ ? char
328
+ : ' '.repeat(Math.min(end, right) - Math.max(start, left));
329
+ }
330
+ return result.trimEnd();
331
+ };
332
+ const sliceMask = (mask, left, width) => {
333
+ if (!mask) {
334
+ return undefined;
335
+ }
336
+ return width === Number.POSITIVE_INFINITY
337
+ ? mask.slice(left)
338
+ : mask.slice(left, left + width);
339
+ };
340
+ //# sourceMappingURL=text-selection-controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-selection-controller.js","sourceRoot":"","sources":["../src/text-selection-controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ A point in container content space: `x` is cells from the container's content
3
+ left edge, `y` is the content row index (survives scrolling).
4
+ */
5
+ export type TextSelectionPoint = {
6
+ x: number;
7
+ y: number;
8
+ };
9
+ export type TextSelectionSpan = {
10
+ y: number;
11
+ x1: number;
12
+ x2: number;
13
+ };
14
+ /**
15
+ A single cached row of rendered content. `mask` flags which display cells are
16
+ selectable (written by `<Text>` and not opted out); a missing mask means every
17
+ cell is selectable.
18
+ */
19
+ export type SelectionRow = {
20
+ text: string;
21
+ mask?: readonly boolean[];
22
+ };
23
+ export type RowLookup = (y: number) => SelectionRow | undefined;
24
+ export declare function normalizeSelection(anchor: TextSelectionPoint, focus: TextSelectionPoint): {
25
+ start: TextSelectionPoint;
26
+ end: TextSelectionPoint;
27
+ };
28
+ /**
29
+ One past the last printable selectable cell of the row. Trailing whitespace
30
+ and trailing chrome cells are excluded.
31
+ */
32
+ export declare function getRowEnd(row: SelectionRow): number;
33
+ export declare function getSelectionSpans(anchor: TextSelectionPoint, focus: TextSelectionPoint, getRow: RowLookup): TextSelectionSpan[];
34
+ /**
35
+ Extract the selectable text of `row` between display cells `[from, to)`.
36
+ Non-selectable runs at the edges are dropped; interior non-selectable runs
37
+ collapse to a single space. Wide glyphs are included when the range overlaps
38
+ either of their cells.
39
+ */
40
+ export declare function sliceSelectableCells(row: SelectionRow, from: number, to: number): string;
41
+ export declare function extractTextFromRows(anchor: TextSelectionPoint, focus: TextSelectionPoint, getRow: RowLookup): string;
@@ -0,0 +1,101 @@
1
+ import stringWidth from 'string-width';
2
+ export function normalizeSelection(anchor, focus) {
3
+ if (anchor.y < focus.y || (anchor.y === focus.y && anchor.x <= focus.x)) {
4
+ return { start: anchor, end: focus };
5
+ }
6
+ return { start: focus, end: anchor };
7
+ }
8
+ const isCharSelectable = (mask, charStart, charEnd) => {
9
+ if (!mask) {
10
+ return true;
11
+ }
12
+ for (let cell = charStart; cell < charEnd; cell++) {
13
+ if (mask[cell] === false) {
14
+ return false;
15
+ }
16
+ }
17
+ return true;
18
+ };
19
+ /**
20
+ One past the last printable selectable cell of the row. Trailing whitespace
21
+ and trailing chrome cells are excluded.
22
+ */
23
+ export function getRowEnd(row) {
24
+ const { text, mask } = row;
25
+ let cell = 0;
26
+ let end = 0;
27
+ for (const char of text) {
28
+ const width = Math.max(1, stringWidth(char));
29
+ const charStart = cell;
30
+ const charEnd = cell + width;
31
+ cell = charEnd;
32
+ if (/^\s*$/.test(char)) {
33
+ continue;
34
+ }
35
+ if (isCharSelectable(mask, charStart, charEnd)) {
36
+ end = charEnd;
37
+ }
38
+ }
39
+ return end;
40
+ }
41
+ export function getSelectionSpans(anchor, focus, getRow) {
42
+ const { start, end } = normalizeSelection(anchor, focus);
43
+ if (start.x === end.x && start.y === end.y) {
44
+ return [];
45
+ }
46
+ const spans = [];
47
+ for (let { y } = start; y <= end.y; y++) {
48
+ const rowEnd = getRowEnd(getRow(y) ?? { text: '' });
49
+ let x1 = y === start.y ? start.x : 0;
50
+ let x2 = y === end.y ? end.x : rowEnd;
51
+ x1 = Math.max(0, Math.min(x1, rowEnd));
52
+ x2 = Math.max(0, Math.min(x2, rowEnd));
53
+ // Emit empty spans (x1 === x2) so blank lines survive extraction.
54
+ spans.push({ y, x1, x2 });
55
+ }
56
+ return spans;
57
+ }
58
+ /**
59
+ Extract the selectable text of `row` between display cells `[from, to)`.
60
+ Non-selectable runs at the edges are dropped; interior non-selectable runs
61
+ collapse to a single space. Wide glyphs are included when the range overlaps
62
+ either of their cells.
63
+ */
64
+ export function sliceSelectableCells(row, from, to) {
65
+ if (to <= from) {
66
+ return '';
67
+ }
68
+ const { text, mask } = row;
69
+ let cell = 0;
70
+ let result = '';
71
+ let pendingGap = false;
72
+ for (const char of text) {
73
+ const width = Math.max(1, stringWidth(char));
74
+ const charStart = cell;
75
+ const charEnd = cell + width;
76
+ cell = charEnd;
77
+ if (charEnd <= from) {
78
+ continue;
79
+ }
80
+ if (charStart >= to) {
81
+ break;
82
+ }
83
+ if (isCharSelectable(mask, charStart, charEnd)) {
84
+ if (pendingGap && result.length > 0) {
85
+ result += ' ';
86
+ }
87
+ pendingGap = false;
88
+ result += char;
89
+ }
90
+ else if (result.length > 0) {
91
+ pendingGap = true;
92
+ }
93
+ }
94
+ return result;
95
+ }
96
+ export function extractTextFromRows(anchor, focus, getRow) {
97
+ return getSelectionSpans(anchor, focus, getRow)
98
+ .map(span => sliceSelectableCells(getRow(span.y) ?? { text: '' }, span.x1, span.x2))
99
+ .join('\n');
100
+ }
101
+ //# sourceMappingURL=text-selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-selection.js","sourceRoot":"","sources":["../src/text-selection.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,cAAc,CAAC;AAmBvC,MAAM,UAAU,kBAAkB,CACjC,MAA0B,EAC1B,KAAyB;IAEzB,IAAI,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACzE,OAAO,EAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAC,CAAC;IACpC,CAAC;IAED,OAAO,EAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAC,CAAC;AACpC,CAAC;AAED,MAAM,gBAAgB,GAAG,CACxB,IAAoC,EACpC,SAAiB,EACjB,OAAe,EACL,EAAE;IACZ,IAAI,CAAC,IAAI,EAAE,CAAC;QACX,OAAO,IAAI,CAAC;IACb,CAAC;IAED,KAAK,IAAI,IAAI,GAAG,SAAS,EAAE,IAAI,GAAG,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;QACnD,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC;QACd,CAAC;IACF,CAAC;IAED,OAAO,IAAI,CAAC;AACb,CAAC,CAAC;AAEF;;;EAGE;AACF,MAAM,UAAU,SAAS,CAAC,GAAiB;IAC1C,MAAM,EAAC,IAAI,EAAE,IAAI,EAAC,GAAG,GAAG,CAAC;IACzB,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,GAAG,GAAG,CAAC,CAAC;IAEZ,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;QAC7C,MAAM,SAAS,GAAG,IAAI,CAAC;QACvB,MAAM,OAAO,GAAG,IAAI,GAAG,KAAK,CAAC;QAC7B,IAAI,GAAG,OAAO,CAAC;QAEf,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,SAAS;QACV,CAAC;QAED,IAAI,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC;YAChD,GAAG,GAAG,OAAO,CAAC;QACf,CAAC;IACF,CAAC;IAED,OAAO,GAAG,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAChC,MAA0B,EAC1B,KAAyB,EACzB,MAAiB;IAEjB,MAAM,EAAC,KAAK,EAAE,GAAG,EAAC,GAAG,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACvD,IAAI,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC;QAC5C,OAAO,EAAE,CAAC;IACX,CAAC;IAED,MAAM,KAAK,GAAwB,EAAE,CAAC;IAEtC,KAAK,IAAI,EAAC,CAAC,EAAC,GAAG,KAAK,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAC,IAAI,EAAE,EAAE,EAAC,CAAC,CAAC;QAClD,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACrC,IAAI,EAAE,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QAEtC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;QACvC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;QAEvC,kEAAkE;QAClE,KAAK,CAAC,IAAI,CAAC,EAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAC,CAAC,CAAC;IACzB,CAAC;IAED,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;;EAKE;AACF,MAAM,UAAU,oBAAoB,CACnC,GAAiB,EACjB,IAAY,EACZ,EAAU;IAEV,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC;QAChB,OAAO,EAAE,CAAC;IACX,CAAC;IAED,MAAM,EAAC,IAAI,EAAE,IAAI,EAAC,GAAG,GAAG,CAAC;IACzB,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,UAAU,GAAG,KAAK,CAAC;IAEvB,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;QAC7C,MAAM,SAAS,GAAG,IAAI,CAAC;QACvB,MAAM,OAAO,GAAG,IAAI,GAAG,KAAK,CAAC;QAC7B,IAAI,GAAG,OAAO,CAAC;QAEf,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;YACrB,SAAS;QACV,CAAC;QAED,IAAI,SAAS,IAAI,EAAE,EAAE,CAAC;YACrB,MAAM;QACP,CAAC;QAED,IAAI,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC;YAChD,IAAI,UAAU,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACrC,MAAM,IAAI,GAAG,CAAC;YACf,CAAC;YAED,UAAU,GAAG,KAAK,CAAC;YACnB,MAAM,IAAI,IAAI,CAAC;QAChB,CAAC;aAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,UAAU,GAAG,IAAI,CAAC;QACnB,CAAC;IACF,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAED,MAAM,UAAU,mBAAmB,CAClC,MAA0B,EAC1B,KAAyB,EACzB,MAAiB;IAEjB,OAAO,iBAAiB,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC;SAC7C,GAAG,CAAC,IAAI,CAAC,EAAE,CACX,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAC,IAAI,EAAE,EAAE,EAAC,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC,CACpE;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;AACd,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuvin/ink",
3
- "version": "7.0.2-alpha",
3
+ "version": "7.1.1-alpha",
4
4
  "description": "React for CLI",
5
5
  "license": "MIT",
6
6
  "repository": "marschhuynh/ink",