@alchemy.run/sigil 0.0.0-alpha.5 → 0.0.0-alpha.6
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/{Text-D5HUf3Fj.d.ts → Text-BobFKi74.d.ts} +2 -2
- package/dist/{index-Bmc2tRPk.d.ts → index-DZ88EXJv.d.ts} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +23 -15
- package/dist/router.d.ts +1 -1
- package/dist/router.js +1 -1
- package/dist/{screen-BReKIheE.d.ts → screen-BOSLQ8fF.d.ts} +11 -2
- package/dist/{screen-BOh__-65.js → screen-CiPytswf.js} +30 -12
- package/dist/screen.d.ts +2 -2
- package/dist/screen.js +1 -1
- package/dist/{session-BJmzX2NJ.js → session-aZr9O8h3.js} +3 -2
- package/dist/terminal.d.ts +1 -1
- package/dist/terminal.js +1 -1
- package/dist/{use-focus-C2sciZOo.js → use-focus-BzqAJi0n.js} +3 -1
- package/package.json +1 -1
- package/src/components/Text.tsx +1 -1
- package/src/dom.ts +6 -0
- package/src/paint-tree.ts +7 -3
- package/src/screen/canvas.ts +49 -26
- package/src/screen/screen.ts +45 -12
- package/src/structured-text.ts +4 -0
- package/src/styles.ts +7 -1
- package/src/terminal/screen-presenter.ts +4 -4
- package/src/wrap-text.ts +4 -0
|
@@ -100,7 +100,7 @@ type BoxGlyphs = {
|
|
|
100
100
|
//#endregion
|
|
101
101
|
//#region src/styles.d.ts
|
|
102
102
|
type Styles = {
|
|
103
|
-
readonly textWrap?: "wrap" | "hard" | "truncate-end" | "truncate" | "truncate-middle" | "truncate-start";
|
|
103
|
+
readonly textWrap?: "wrap" | "hard" | "truncate-end" | "truncate" | "truncate-middle" | "truncate-start" | "none";
|
|
104
104
|
/**
|
|
105
105
|
Controls how the element is positioned.
|
|
106
106
|
|
|
@@ -439,7 +439,7 @@ type Props = {
|
|
|
439
439
|
*/
|
|
440
440
|
readonly inverse?: boolean;
|
|
441
441
|
/**
|
|
442
|
-
This property tells Ink to wrap or truncate text if its width is larger than the container. If `wrap` is passed (the default), Ink will wrap text and split it into multiple lines. If `hard` is passed, Ink will fill each line to the full column width, breaking words as necessary. If `truncate-*` is passed, Ink will truncate text instead, resulting in one line of text with the rest cut off.
|
|
442
|
+
This property tells Ink to wrap or truncate text if its width is larger than the container. If `wrap` is passed (the default), Ink will wrap text and split it into multiple lines. If `hard` is passed, Ink will fill each line to the full column width, breaking words as necessary. If `truncate-*` is passed, Ink will truncate text instead, resulting in one line of text with the rest cut off. If `none` is passed, the text is neither wrapped nor truncated — it overflows the container (and the screen's nominal width), which is intended for log lines committed to scrollback (e.g. inside `<Static>`), not for live regions.
|
|
443
443
|
*/
|
|
444
444
|
readonly wrap?: Styles["textWrap"];
|
|
445
445
|
readonly children?: ReactNode;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as StyledChar } from "./tokenize-Dx1y_l5H.js";
|
|
2
2
|
import { i as Cell, t as ColorProfile } from "./color-profile-u0Nhe9Nv.js";
|
|
3
|
-
import { n as Screen, t as Line } from "./screen-
|
|
3
|
+
import { n as Screen, t as Line } from "./screen-BOSLQ8fF.js";
|
|
4
4
|
//#region src/screen/ansi.d.ts
|
|
5
5
|
/** Converts the renderer's canonical ANSI state into backend-neutral cells. */
|
|
6
6
|
declare function cellsFromAnsi(text: string): Cell[];
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { a as getTerminalQuery, i as applyTerminalQuery, n as TerminalQueryOptio
|
|
|
4
4
|
import { i as OutputStream, n as capabilities, r as getCapabilities, t as CapabilitiesStore } from "./store-CgrG9K4y.js";
|
|
5
5
|
import { s as CellStyle, t as ColorProfile } from "./color-profile-u0Nhe9Nv.js";
|
|
6
6
|
import { s as Paint } from "./paint-C19minOS.js";
|
|
7
|
-
import { n as Text, r as Styles, t as Props$8 } from "./Text-
|
|
7
|
+
import { n as Text, r as Styles, t as Props$8 } from "./Text-BobFKi74.js";
|
|
8
8
|
import { i as Node } from "./index-DDVME65c.js";
|
|
9
9
|
import { t as CursorPosition } from "./cursor-position-D2LAkRG0.js";
|
|
10
10
|
import { Writable } from "node:stream";
|
package/dist/index.js
CHANGED
|
@@ -2,17 +2,17 @@ import { C as bsu, S as ansiEscapes, V as esu, W as link, _ as CSI } from "./sgr
|
|
|
2
2
|
import { h as tokenizeAnsi, p as graphemes } from "./tokenize-AjqbvtiT.js";
|
|
3
3
|
import { t as stringWidth } from "./string-width-CijQwpIk.js";
|
|
4
4
|
import { n as sliceAnsi, r as wrapAnsi } from "./truncate-D31fhU6i.js";
|
|
5
|
-
import { _ as squashTextNodes, a as reconciler, c as kittyModifiers, d as FocusContext, f as Text, g as transformAnsiLine, h as emitLayoutListeners, i as useStdinContext, l as resolveFlags, m as createNode, n as useInput, o as detectKittySupport, p as addLayoutListener, r as useStdin, s as kittyFlags, t as useFocus, u as StdinContext, v as accessibilityContext } from "./use-focus-
|
|
5
|
+
import { _ as squashTextNodes, a as reconciler, c as kittyModifiers, d as FocusContext, f as Text, g as transformAnsiLine, h as emitLayoutListeners, i as useStdinContext, l as resolveFlags, m as createNode, n as useInput, o as detectKittySupport, p as addLayoutListener, r as useStdin, s as kittyFlags, t as useFocus, u as StdinContext, v as accessibilityContext } from "./use-focus-BzqAJi0n.js";
|
|
6
6
|
import { a as isTty, i as isSigilDev, o as isWindows, r as isScreenReader, t as isInCi } from "./env-YVw64yZS.js";
|
|
7
7
|
import { a as detectTerminal, c as signalExit, i as detectHyperlinkSupport, n as detectCapabilities, o as detectUnicodeSupport, r as detectColorLevel, s as terminalSize, t as createSupportsColor } from "./detect-BuTXtY6e.js";
|
|
8
8
|
import { u as cliCursor } from "./osc-CCH7xDoS.js";
|
|
9
9
|
import { a as registerTerminalIntegration, c as ensureTerminalQuery, d as queryTerminal, f as refreshTerminalQuery, i as getCapabilities, l as getTerminalQuery, o as getRawModeStream, r as capabilities, s as applyTerminalQuery, u as getTerminalQueryPromise } from "./color-policy-SVj1pYTA.js";
|
|
10
|
-
import { a as buildCursorSuffix, c as hideCursorEscape, i as buildCursorOnlySequence, o as buildReturnToBottomPrefix, s as cursorPositionChanged, t as TerminalSession } from "./session-
|
|
10
|
+
import { a as buildCursorSuffix, c as hideCursorEscape, i as buildCursorOnlySequence, o as buildReturnToBottomPrefix, s as cursorPositionChanged, t as TerminalSession } from "./session-aZr9O8h3.js";
|
|
11
11
|
import { t as Yoga } from "./yoga-5jKhYCJC.js";
|
|
12
12
|
import { r as createCell, t as cellAttributes } from "./cell-_ZVhbfl0.js";
|
|
13
13
|
import { i as semanticTextStyleToCellStyle, n as mergeSemanticTextStyles, t as emptySemanticTextStyle } from "./semantic-text-style-DIMzC7xt.js";
|
|
14
14
|
import { a as samplePaint } from "./sample-Cqw1bjUL.js";
|
|
15
|
-
import { n as cellFromStyledChar, r as cellsFromAnsi, t as Screen } from "./screen-
|
|
15
|
+
import { n as cellFromStyledChar, r as cellsFromAnsi, t as Screen } from "./screen-CiPytswf.js";
|
|
16
16
|
import { t as colorProfileFromLevel } from "./color-profile-DHhQHY55.js";
|
|
17
17
|
import { n as serializeScreen, t as serializeLine } from "./serialize-BTkAZgw1.js";
|
|
18
18
|
import { t as stripAnsi } from "./strip-BvU4toXG.js";
|
|
@@ -1115,6 +1115,7 @@ function structuredTextBaseStyle(node) {
|
|
|
1115
1115
|
}
|
|
1116
1116
|
/** Wraps or truncates semantic cells without converting them through ANSI. */
|
|
1117
1117
|
function wrapStructuredText(input, maxWidth, wrap, baseStyle) {
|
|
1118
|
+
if (wrap === "none") return input.map((line) => [...line]);
|
|
1118
1119
|
if (maxWidth < 1) return [[]];
|
|
1119
1120
|
return input.flatMap((line) => {
|
|
1120
1121
|
if (lineWidth(line) <= maxWidth) return [[...line]];
|
|
@@ -1301,7 +1302,9 @@ const paintTree = (node, output, options) => {
|
|
|
1301
1302
|
const firstChildYoga = node.childNodes[0]?.yogaNode;
|
|
1302
1303
|
const paddingX = firstChildYoga?.getComputedLeft() ?? 0;
|
|
1303
1304
|
const paddingY = firstChildYoga?.getComputedTop() ?? 0;
|
|
1304
|
-
const
|
|
1305
|
+
const textWrap = node.style.textWrap ?? "wrap";
|
|
1306
|
+
const overflow = textWrap === "none";
|
|
1307
|
+
const lines = wrapStructuredText(structuredTextLines(node), maxWidth, textWrap, structuredTextBaseStyle(node));
|
|
1305
1308
|
const paintBounds = {
|
|
1306
1309
|
x: x + paddingX,
|
|
1307
1310
|
y: y + paddingY,
|
|
@@ -1314,8 +1317,11 @@ const paintTree = (node, output, options) => {
|
|
|
1314
1317
|
output.writeAnsi(paintBounds.x, paintBounds.y, sampled.map((line) => serializeLine(line, {
|
|
1315
1318
|
colorProfile: output.paintContext.profile ?? "truecolor",
|
|
1316
1319
|
trimEnd: false
|
|
1317
|
-
})).join("\n"), {
|
|
1318
|
-
|
|
1320
|
+
})).join("\n"), {
|
|
1321
|
+
transformers: textTransformers,
|
|
1322
|
+
overflow
|
|
1323
|
+
});
|
|
1324
|
+
} else output.writeCells(paintBounds.x, paintBounds.y, sampled, { overflow });
|
|
1319
1325
|
}
|
|
1320
1326
|
return;
|
|
1321
1327
|
}
|
|
@@ -1375,15 +1381,16 @@ var Canvas = class {
|
|
|
1375
1381
|
};
|
|
1376
1382
|
this.#screen = new Screen(this.width, this.height);
|
|
1377
1383
|
}
|
|
1378
|
-
writeCells(x, y, lines) {
|
|
1384
|
+
writeCells(x, y, lines, options) {
|
|
1379
1385
|
const clip = this.#clips.at(-1);
|
|
1386
|
+
const overflow = options?.overflow === true;
|
|
1380
1387
|
for (const [rowOffset, line] of lines.entries()) {
|
|
1381
1388
|
const currentY = y + rowOffset;
|
|
1382
1389
|
if (!this.#insideY(currentY, clip)) continue;
|
|
1383
1390
|
let currentX = x;
|
|
1384
1391
|
for (const cell of line) {
|
|
1385
1392
|
const endX = currentX + cell.width;
|
|
1386
|
-
if (!(clip?.x1 !== void 0 && currentX < clip.x1 || clip?.x2 !== void 0 && endX > clip.x2)) this.#composeNativeCell(currentX, currentY, cell);
|
|
1393
|
+
if (!(clip?.x1 !== void 0 && currentX < clip.x1 || clip?.x2 !== void 0 && endX > clip.x2)) this.#composeNativeCell(currentX, currentY, cell, overflow);
|
|
1387
1394
|
currentX = endX;
|
|
1388
1395
|
}
|
|
1389
1396
|
}
|
|
@@ -1392,6 +1399,7 @@ var Canvas = class {
|
|
|
1392
1399
|
if (!text) return;
|
|
1393
1400
|
let lines = text.split("\n");
|
|
1394
1401
|
const clip = this.#clips.at(-1);
|
|
1402
|
+
const overflow = options.overflow === true;
|
|
1395
1403
|
if (clip?.y1 !== void 0 && y < clip.y1) {
|
|
1396
1404
|
lines = lines.slice(clip.y1 - y);
|
|
1397
1405
|
y = clip.y1;
|
|
@@ -1410,7 +1418,7 @@ var Canvas = class {
|
|
|
1410
1418
|
for (const character of transformAnsiLine(line, lineIndex, options.transformers)) {
|
|
1411
1419
|
const width = Math.max(1, stringWidth(character.value));
|
|
1412
1420
|
if (clip?.x2 !== void 0 && currentX + width > clip.x2) break;
|
|
1413
|
-
this.#writeCompatibilityCell(currentX, currentY, character, width);
|
|
1421
|
+
this.#writeCompatibilityCell(currentX, currentY, character, width, overflow);
|
|
1414
1422
|
currentX += width;
|
|
1415
1423
|
}
|
|
1416
1424
|
}
|
|
@@ -1427,8 +1435,8 @@ var Canvas = class {
|
|
|
1427
1435
|
#insideY(y, clip) {
|
|
1428
1436
|
return y >= 0 && y < this.#screen.height && (clip?.y1 === void 0 || y >= clip.y1) && (clip?.y2 === void 0 || y < clip.y2);
|
|
1429
1437
|
}
|
|
1430
|
-
#composeNativeCell(x, y, cell) {
|
|
1431
|
-
if (x >= this.#screen.width) return;
|
|
1438
|
+
#composeNativeCell(x, y, cell, overflow = false) {
|
|
1439
|
+
if (!overflow && x >= this.#screen.width) return;
|
|
1432
1440
|
this.#screen.composeCell(x, y, {
|
|
1433
1441
|
content: {
|
|
1434
1442
|
grapheme: cell.grapheme,
|
|
@@ -1440,10 +1448,10 @@ var Canvas = class {
|
|
|
1440
1448
|
underline: cell.style.underline,
|
|
1441
1449
|
attributes: cell.style.attributes,
|
|
1442
1450
|
hyperlink: cell.hyperlink ?? null
|
|
1443
|
-
});
|
|
1451
|
+
}, { overflow });
|
|
1444
1452
|
}
|
|
1445
|
-
#writeCompatibilityCell(x, y, character, width) {
|
|
1446
|
-
if (x >= this.#screen.width) return;
|
|
1453
|
+
#writeCompatibilityCell(x, y, character, width, overflow = false) {
|
|
1454
|
+
if (!overflow && x >= this.#screen.width) return;
|
|
1447
1455
|
const cell = cellFromStyledChar(character, width);
|
|
1448
1456
|
this.#screen.composeCell(x, y, {
|
|
1449
1457
|
content: {
|
|
@@ -1456,7 +1464,7 @@ var Canvas = class {
|
|
|
1456
1464
|
underline: cell.style.underline,
|
|
1457
1465
|
attributes: cell.style.attributes,
|
|
1458
1466
|
hyperlink: cell.hyperlink ?? null
|
|
1459
|
-
});
|
|
1467
|
+
}, { overflow });
|
|
1460
1468
|
}
|
|
1461
1469
|
};
|
|
1462
1470
|
//#endregion
|
package/dist/router.d.ts
CHANGED
package/dist/router.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { f as Text, n as useInput, t as useFocus } from "./use-focus-
|
|
1
|
+
import { f as Text, n as useInput, t as useFocus } from "./use-focus-BzqAJi0n.js";
|
|
2
2
|
import { Children, Fragment, createContext, isValidElement, startTransition, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
|
|
4
4
|
//#region src/router/history.ts
|
|
@@ -16,17 +16,26 @@ declare class Screen {
|
|
|
16
16
|
#private;
|
|
17
17
|
constructor(width: number, height: number);
|
|
18
18
|
get width(): number;
|
|
19
|
+
/**
|
|
20
|
+
The populated length of one row: the nominal width, or more when overflow
|
|
21
|
+
writes (`textWrap: "none"` content) extended it past the screen's width.
|
|
22
|
+
*/
|
|
23
|
+
rowLength(y: number): number;
|
|
19
24
|
get height(): number;
|
|
20
25
|
bounds(): Rect;
|
|
21
26
|
cellAt(x: number, y: number): Cell | undefined;
|
|
22
27
|
/** Whether a drawable explicitly touched this position. */
|
|
23
28
|
isPainted(x: number, y: number): boolean;
|
|
24
|
-
setCell(x: number, y: number, cell: Cell
|
|
29
|
+
setCell(x: number, y: number, cell: Cell, options?: {
|
|
30
|
+
overflow?: boolean;
|
|
31
|
+
}): void;
|
|
25
32
|
/**
|
|
26
33
|
Composes independent cell channels over the existing cell. An undefined
|
|
27
34
|
patch is transparent; an explicit space in `content` paints a blank.
|
|
28
35
|
*/
|
|
29
|
-
composeCell(x: number, y: number, patch: CellPatch | undefined
|
|
36
|
+
composeCell(x: number, y: number, patch: CellPatch | undefined, options?: {
|
|
37
|
+
overflow?: boolean;
|
|
38
|
+
}): void;
|
|
30
39
|
/** Composes a patch throughout a rectangle, clipped to this screen. */
|
|
31
40
|
fill(bounds: Rect, patch: CellPatch | undefined): void;
|
|
32
41
|
/** Resizes while preserving complete graphemes in the shared top-left area. */
|
|
@@ -155,6 +155,13 @@ var Screen = class {
|
|
|
155
155
|
get width() {
|
|
156
156
|
return this.#width;
|
|
157
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
The populated length of one row: the nominal width, or more when overflow
|
|
160
|
+
writes (`textWrap: "none"` content) extended it past the screen's width.
|
|
161
|
+
*/
|
|
162
|
+
rowLength(y) {
|
|
163
|
+
return Math.max(this.#width, this.#rows[y]?.length ?? 0);
|
|
164
|
+
}
|
|
158
165
|
get height() {
|
|
159
166
|
return this.#height;
|
|
160
167
|
}
|
|
@@ -173,17 +180,20 @@ var Screen = class {
|
|
|
173
180
|
isPainted(x, y) {
|
|
174
181
|
return this.#painted[y]?.[x] ?? false;
|
|
175
182
|
}
|
|
176
|
-
setCell(x, y, cell) {
|
|
183
|
+
setCell(x, y, cell, options) {
|
|
177
184
|
const row = this.#rows[y];
|
|
178
|
-
|
|
185
|
+
const overflow = options?.overflow === true;
|
|
186
|
+
if (!row || x < 0 || !overflow && x >= this.width) return;
|
|
179
187
|
if (!Number.isInteger(cell.width) || cell.width < 1) throw new Error("Only leading cells with a positive width may be written");
|
|
180
188
|
const finalColumn = x + cell.width;
|
|
181
|
-
|
|
182
|
-
const
|
|
189
|
+
if (overflow && finalColumn > row.length) this.#growRow(row, y, finalColumn);
|
|
190
|
+
const capacity = overflow ? row.length : this.width;
|
|
191
|
+
const clipped = finalColumn > capacity;
|
|
192
|
+
const overwriteEnd = Math.min(finalColumn, capacity);
|
|
183
193
|
for (let column = x; column < overwriteEnd; column++) this.#clearGraphemeAt(row, y, column);
|
|
184
194
|
if (clipped) {
|
|
185
|
-
for (let column = x; column <
|
|
186
|
-
this.#markDirty(y, x,
|
|
195
|
+
for (let column = x; column < capacity; column++) row[column] = emptyCell;
|
|
196
|
+
this.#markDirty(y, x, capacity);
|
|
187
197
|
return;
|
|
188
198
|
}
|
|
189
199
|
row[x] = cell;
|
|
@@ -203,10 +213,10 @@ var Screen = class {
|
|
|
203
213
|
Composes independent cell channels over the existing cell. An undefined
|
|
204
214
|
patch is transparent; an explicit space in `content` paints a blank.
|
|
205
215
|
*/
|
|
206
|
-
composeCell(x, y, patch) {
|
|
216
|
+
composeCell(x, y, patch, options) {
|
|
207
217
|
if (!patch) return;
|
|
208
218
|
const row = this.#rows[y];
|
|
209
|
-
if (!row || x < 0 || x >= this.width) return;
|
|
219
|
+
if (!row || x < 0 || options?.overflow !== true && x >= this.width) return;
|
|
210
220
|
let leadingColumn = x;
|
|
211
221
|
while (leadingColumn > 0 && row[leadingColumn]?.width === 0) leadingColumn--;
|
|
212
222
|
const destination = row[leadingColumn] ?? emptyCell;
|
|
@@ -220,7 +230,7 @@ var Screen = class {
|
|
|
220
230
|
};
|
|
221
231
|
const content = patch.content ?? destination;
|
|
222
232
|
const hyperlink = patch.hyperlink === void 0 ? destination.hyperlink : patch.hyperlink ?? void 0;
|
|
223
|
-
this.setCell(writeColumn, y, createCell(content.grapheme, content.width, style, hyperlink));
|
|
233
|
+
this.setCell(writeColumn, y, createCell(content.grapheme, content.width, style, hyperlink), options);
|
|
224
234
|
}
|
|
225
235
|
/** Composes a patch throughout a rectangle, clipped to this screen. */
|
|
226
236
|
fill(bounds, patch) {
|
|
@@ -265,7 +275,7 @@ var Screen = class {
|
|
|
265
275
|
for (const [y, row] of this.#rows.entries()) {
|
|
266
276
|
row.fill(emptyCell);
|
|
267
277
|
this.#painted[y].fill(true);
|
|
268
|
-
this.#markDirty(y, 0, this.
|
|
278
|
+
this.#markDirty(y, 0, this.rowLength(y));
|
|
269
279
|
}
|
|
270
280
|
}
|
|
271
281
|
toRows() {
|
|
@@ -282,6 +292,14 @@ var Screen = class {
|
|
|
282
292
|
this.#dirty.fill(void 0);
|
|
283
293
|
return spans;
|
|
284
294
|
}
|
|
295
|
+
/** Extends a row (and its paint mask) so overflow writes land past `width`. */
|
|
296
|
+
#growRow(row, y, length) {
|
|
297
|
+
const painted = this.#painted[y];
|
|
298
|
+
while (row.length < length) {
|
|
299
|
+
row.push(emptyCell);
|
|
300
|
+
painted.push(false);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
285
303
|
#clearGraphemeAt(row, y, x) {
|
|
286
304
|
const existing = row[x];
|
|
287
305
|
if (!existing || existing === emptyCell) return;
|
|
@@ -289,7 +307,7 @@ var Screen = class {
|
|
|
289
307
|
while (leadingColumn > 0 && row[leadingColumn]?.width === 0) leadingColumn--;
|
|
290
308
|
const leadingCell = row[leadingColumn];
|
|
291
309
|
const width = Math.max(1, leadingCell?.width ?? 1);
|
|
292
|
-
for (let column = leadingColumn; column < Math.min(leadingColumn + width,
|
|
310
|
+
for (let column = leadingColumn; column < Math.min(leadingColumn + width, row.length); column++) {
|
|
293
311
|
row[column] = emptyCell;
|
|
294
312
|
this.#painted[y][column] = true;
|
|
295
313
|
}
|
|
@@ -306,7 +324,7 @@ var Screen = class {
|
|
|
306
324
|
#markDirty(y, start, end) {
|
|
307
325
|
if (y < 0 || y >= this.height || start >= end) return;
|
|
308
326
|
const clippedStart = Math.max(0, start);
|
|
309
|
-
const clippedEnd = Math.min(this.
|
|
327
|
+
const clippedEnd = Math.min(this.rowLength(y), end);
|
|
310
328
|
const current = this.#dirty[y];
|
|
311
329
|
this.#dirty[y] = current ? {
|
|
312
330
|
start: Math.min(current.start, clippedStart),
|
package/dist/screen.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { _ as emptyCellStyle, a as CellContent, c as Color, d as RgbColor, f as UnderlineStyle, g as emptyCell, h as createCell, i as Cell, l as Hyperlink, m as cellsEqual, n as colorProfileFromLevel, o as CellPatch, p as cellAttributes, r as quantizeColor, s as CellStyle, t as ColorProfile, u as IndexedColor } from "./color-profile-u0Nhe9Nv.js";
|
|
2
2
|
import { n as Rect, t as Point } from "./geometry-BxXOzJgo.js";
|
|
3
|
-
import { a as cellFromStyledChar, i as styledCharFromCell, n as serializeLine, o as cellsFromAnsi, r as serializeScreen, t as SerializeOptions } from "./index-
|
|
4
|
-
import { n as Screen, t as Line } from "./screen-
|
|
3
|
+
import { a as cellFromStyledChar, i as styledCharFromCell, n as serializeLine, o as cellsFromAnsi, r as serializeScreen, t as SerializeOptions } from "./index-DZ88EXJv.js";
|
|
4
|
+
import { n as Screen, t as Line } from "./screen-BOSLQ8fF.js";
|
|
5
5
|
export { Cell, CellContent, CellPatch, CellStyle, Color, ColorProfile, Hyperlink, IndexedColor, type Line, Point, Rect, RgbColor, Screen, SerializeOptions, UnderlineStyle, cellAttributes, cellFromStyledChar, cellsEqual, cellsFromAnsi, colorProfileFromLevel, createCell, emptyCell, emptyCellStyle, quantizeColor, serializeLine, serializeScreen, styledCharFromCell };
|
package/dist/screen.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as emptyCellStyle, i as emptyCell, n as cellsEqual, r as createCell, t as cellAttributes } from "./cell-_ZVhbfl0.js";
|
|
2
|
-
import { n as cellFromStyledChar, r as cellsFromAnsi, t as Screen } from "./screen-
|
|
2
|
+
import { n as cellFromStyledChar, r as cellsFromAnsi, t as Screen } from "./screen-CiPytswf.js";
|
|
3
3
|
import { n as quantizeColor, t as colorProfileFromLevel } from "./color-profile-DHhQHY55.js";
|
|
4
4
|
import { n as serializeScreen, r as styledCharFromCell, t as serializeLine } from "./serialize-BTkAZgw1.js";
|
|
5
5
|
export { Screen, cellAttributes, cellFromStyledChar, cellsEqual, cellsFromAnsi, colorProfileFromLevel, createCell, emptyCell, emptyCellStyle, quantizeColor, serializeLine, serializeScreen, styledCharFromCell };
|
|
@@ -358,12 +358,13 @@ function findFirstChangedRow(previous, next) {
|
|
|
358
358
|
for (let y = 0; y < height; y++) if (!rowsEqual(previous, next, y)) return y;
|
|
359
359
|
}
|
|
360
360
|
function rowsEqual(left, right, y) {
|
|
361
|
-
|
|
361
|
+
const length = Math.max(left.rowLength(y), right.rowLength(y));
|
|
362
|
+
for (let x = 0; x < length; x++) if (!cellsEqual(left.cellAt(x, y), right.cellAt(x, y))) return false;
|
|
362
363
|
return true;
|
|
363
364
|
}
|
|
364
365
|
function screensEqual(left, right) {
|
|
365
366
|
if (left.width !== right.width || left.height !== right.height) return false;
|
|
366
|
-
for (let y = 0; y < left.height; y++)
|
|
367
|
+
for (let y = 0; y < left.height; y++) if (!rowsEqual(left, right, y)) return false;
|
|
367
368
|
return true;
|
|
368
369
|
}
|
|
369
370
|
//#endregion
|
package/dist/terminal.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { n as TerminalProgressState, t as ClipboardSelection } from "./osc-Cn0fw
|
|
|
3
3
|
import { i as OutputStream, t as CapabilitiesStore } from "./store-CgrG9K4y.js";
|
|
4
4
|
import { t as ColorProfile } from "./color-profile-u0Nhe9Nv.js";
|
|
5
5
|
import { t as ColorPolicy } from "./color-policy-BMzMwV7Q.js";
|
|
6
|
-
import { n as Screen } from "./screen-
|
|
6
|
+
import { n as Screen } from "./screen-BOSLQ8fF.js";
|
|
7
7
|
import { t as CursorPosition } from "./cursor-position-D2LAkRG0.js";
|
|
8
8
|
//#region src/input-parser.d.ts
|
|
9
9
|
type InputEvent = string | {
|
package/dist/terminal.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as TerminalInput, r as parseMouseEvent, t as TerminalSession } from "./session-
|
|
1
|
+
import { n as TerminalInput, r as parseMouseEvent, t as TerminalSession } from "./session-aZr9O8h3.js";
|
|
2
2
|
export { TerminalInput, TerminalSession, parseMouseEvent };
|
|
@@ -278,6 +278,7 @@ function transformAnsiLine(serializedSubtree, lineIndex, transformers) {
|
|
|
278
278
|
//#region src/wrap-text.ts
|
|
279
279
|
const wrapTextCache = new QuickLru({ maxSize: 4096 });
|
|
280
280
|
const wrapText = (text, maxWidth, wrapType) => {
|
|
281
|
+
if (wrapType === "none") return text;
|
|
281
282
|
const cacheKey = text + String(maxWidth) + String(wrapType);
|
|
282
283
|
const cachedText = wrapTextCache.get(cacheKey);
|
|
283
284
|
if (cachedText !== void 0) return cachedText;
|
|
@@ -386,6 +387,7 @@ const measureTextNode = function(node, width) {
|
|
|
386
387
|
if (dimensions.width <= width) return dimensions;
|
|
387
388
|
if (dimensions.width >= 1 && width > 0 && width < 1) return dimensions;
|
|
388
389
|
const textWrap = node.style?.textWrap ?? "wrap";
|
|
390
|
+
if (textWrap === "none") return dimensions;
|
|
389
391
|
const wrappedText = wrapText(text, width, textWrap);
|
|
390
392
|
return measureText(wrappedText);
|
|
391
393
|
};
|
|
@@ -562,7 +564,7 @@ const detectKittySupport = (stdin, stdout, onSupported) => {
|
|
|
562
564
|
//#endregion
|
|
563
565
|
//#region package.json
|
|
564
566
|
var name = "@alchemy.run/sigil";
|
|
565
|
-
var version = "0.0.0-alpha.
|
|
567
|
+
var version = "0.0.0-alpha.6";
|
|
566
568
|
//#endregion
|
|
567
569
|
//#region src/reconciler.ts
|
|
568
570
|
if (isSigilDev) await import("./devtools-DbthxoD1.js").catch(() => {});
|
package/package.json
CHANGED
package/src/components/Text.tsx
CHANGED
|
@@ -59,7 +59,7 @@ export type Props = {
|
|
|
59
59
|
readonly inverse?: boolean;
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
|
-
This property tells Ink to wrap or truncate text if its width is larger than the container. If `wrap` is passed (the default), Ink will wrap text and split it into multiple lines. If `hard` is passed, Ink will fill each line to the full column width, breaking words as necessary. If `truncate-*` is passed, Ink will truncate text instead, resulting in one line of text with the rest cut off.
|
|
62
|
+
This property tells Ink to wrap or truncate text if its width is larger than the container. If `wrap` is passed (the default), Ink will wrap text and split it into multiple lines. If `hard` is passed, Ink will fill each line to the full column width, breaking words as necessary. If `truncate-*` is passed, Ink will truncate text instead, resulting in one line of text with the rest cut off. If `none` is passed, the text is neither wrapped nor truncated — it overflows the container (and the screen's nominal width), which is intended for log lines committed to scrollback (e.g. inside `<Static>`), not for live regions.
|
|
63
63
|
*/
|
|
64
64
|
readonly wrap?: Styles["textWrap"];
|
|
65
65
|
|
package/src/dom.ts
CHANGED
|
@@ -243,6 +243,12 @@ const measureTextNode = function (node: DOMNode, width: number): { width: number
|
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
const textWrap = node.style?.textWrap ?? "wrap";
|
|
246
|
+
|
|
247
|
+
// `none` keeps the intrinsic dimensions and overflows the container.
|
|
248
|
+
if (textWrap === "none") {
|
|
249
|
+
return dimensions;
|
|
250
|
+
}
|
|
251
|
+
|
|
246
252
|
const wrappedText = wrapText(text, width, textWrap);
|
|
247
253
|
|
|
248
254
|
return measureText(wrappedText);
|
package/src/paint-tree.ts
CHANGED
|
@@ -117,10 +117,14 @@ export const paintTree = (
|
|
|
117
117
|
const firstChildYoga = node.childNodes[0]?.yogaNode;
|
|
118
118
|
const paddingX = firstChildYoga?.getComputedLeft() ?? 0;
|
|
119
119
|
const paddingY = firstChildYoga?.getComputedTop() ?? 0;
|
|
120
|
+
const textWrap = node.style.textWrap ?? "wrap";
|
|
121
|
+
// `none` text may paint past the screen's nominal width; the screen
|
|
122
|
+
// grows the touched rows on demand. Clip rects still apply.
|
|
123
|
+
const overflow = textWrap === "none";
|
|
120
124
|
const lines = wrapStructuredText(
|
|
121
125
|
structuredTextLines(node),
|
|
122
126
|
maxWidth,
|
|
123
|
-
|
|
127
|
+
textWrap,
|
|
124
128
|
structuredTextBaseStyle(node),
|
|
125
129
|
);
|
|
126
130
|
const paintBounds = {
|
|
@@ -147,10 +151,10 @@ export const paintTree = (
|
|
|
147
151
|
}),
|
|
148
152
|
)
|
|
149
153
|
.join("\n"),
|
|
150
|
-
{ transformers: textTransformers },
|
|
154
|
+
{ transformers: textTransformers, overflow },
|
|
151
155
|
);
|
|
152
156
|
} else {
|
|
153
|
-
output.writeCells(paintBounds.x, paintBounds.y, sampled);
|
|
157
|
+
output.writeCells(paintBounds.x, paintBounds.y, sampled, { overflow });
|
|
154
158
|
}
|
|
155
159
|
}
|
|
156
160
|
|
package/src/screen/canvas.ts
CHANGED
|
@@ -34,8 +34,14 @@ export class Canvas {
|
|
|
34
34
|
this.#screen = new Screen(this.width, this.height);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
writeCells(
|
|
37
|
+
writeCells(
|
|
38
|
+
x: number,
|
|
39
|
+
y: number,
|
|
40
|
+
lines: readonly (readonly Cell[])[],
|
|
41
|
+
options?: { overflow?: boolean },
|
|
42
|
+
): void {
|
|
38
43
|
const clip = this.#clips.at(-1);
|
|
44
|
+
const overflow = options?.overflow === true;
|
|
39
45
|
for (const [rowOffset, line] of lines.entries()) {
|
|
40
46
|
const currentY = y + rowOffset;
|
|
41
47
|
if (!this.#insideY(currentY, clip)) continue;
|
|
@@ -45,7 +51,7 @@ export class Canvas {
|
|
|
45
51
|
const clipped =
|
|
46
52
|
(clip?.x1 !== undefined && currentX < clip.x1) ||
|
|
47
53
|
(clip?.x2 !== undefined && endX > clip.x2);
|
|
48
|
-
if (!clipped) this.#composeNativeCell(currentX, currentY, cell);
|
|
54
|
+
if (!clipped) this.#composeNativeCell(currentX, currentY, cell, overflow);
|
|
49
55
|
currentX = endX;
|
|
50
56
|
}
|
|
51
57
|
}
|
|
@@ -55,11 +61,12 @@ export class Canvas {
|
|
|
55
61
|
x: number,
|
|
56
62
|
y: number,
|
|
57
63
|
text: string,
|
|
58
|
-
options: { transformers: AnsiTransformer[] },
|
|
64
|
+
options: { transformers: AnsiTransformer[]; overflow?: boolean },
|
|
59
65
|
): void {
|
|
60
66
|
if (!text) return;
|
|
61
67
|
let lines = text.split("\n");
|
|
62
68
|
const clip = this.#clips.at(-1);
|
|
69
|
+
const overflow = options.overflow === true;
|
|
63
70
|
if (clip?.y1 !== undefined && y < clip.y1) {
|
|
64
71
|
lines = lines.slice(clip.y1 - y);
|
|
65
72
|
y = clip.y1;
|
|
@@ -78,7 +85,7 @@ export class Canvas {
|
|
|
78
85
|
for (const character of transformAnsiLine(line, lineIndex, options.transformers)) {
|
|
79
86
|
const width = Math.max(1, stringWidth(character.value));
|
|
80
87
|
if (clip?.x2 !== undefined && currentX + width > clip.x2) break;
|
|
81
|
-
this.#writeCompatibilityCell(currentX, currentY, character, width);
|
|
88
|
+
this.#writeCompatibilityCell(currentX, currentY, character, width, overflow);
|
|
82
89
|
currentX += width;
|
|
83
90
|
}
|
|
84
91
|
}
|
|
@@ -104,34 +111,50 @@ export class Canvas {
|
|
|
104
111
|
);
|
|
105
112
|
}
|
|
106
113
|
|
|
107
|
-
#composeNativeCell(x: number, y: number, cell: Cell): void {
|
|
108
|
-
if (x >= this.#screen.width) {
|
|
114
|
+
#composeNativeCell(x: number, y: number, cell: Cell, overflow = false): void {
|
|
115
|
+
if (!overflow && x >= this.#screen.width) {
|
|
109
116
|
return;
|
|
110
117
|
}
|
|
111
|
-
this.#screen.composeCell(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
this.#screen.composeCell(
|
|
119
|
+
x,
|
|
120
|
+
y,
|
|
121
|
+
{
|
|
122
|
+
content: { grapheme: cell.grapheme, width: cell.width },
|
|
123
|
+
foreground: cell.reset?.foreground ? null : (cell.style.foreground ?? null),
|
|
124
|
+
background: cell.reset?.background ? null : cell.style.background,
|
|
125
|
+
underlineColor: cell.style.underlineColor ?? null,
|
|
126
|
+
underline: cell.style.underline,
|
|
127
|
+
attributes: cell.style.attributes,
|
|
128
|
+
hyperlink: cell.hyperlink ?? null,
|
|
129
|
+
},
|
|
130
|
+
{ overflow },
|
|
131
|
+
);
|
|
120
132
|
}
|
|
121
133
|
|
|
122
|
-
#writeCompatibilityCell(
|
|
123
|
-
|
|
134
|
+
#writeCompatibilityCell(
|
|
135
|
+
x: number,
|
|
136
|
+
y: number,
|
|
137
|
+
character: StyledChar,
|
|
138
|
+
width: number,
|
|
139
|
+
overflow = false,
|
|
140
|
+
): void {
|
|
141
|
+
if (!overflow && x >= this.#screen.width) {
|
|
124
142
|
return;
|
|
125
143
|
}
|
|
126
144
|
const cell = cellFromStyledChar(character, width);
|
|
127
|
-
this.#screen.composeCell(
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
145
|
+
this.#screen.composeCell(
|
|
146
|
+
x,
|
|
147
|
+
y,
|
|
148
|
+
{
|
|
149
|
+
content: { grapheme: cell.grapheme, width: cell.width },
|
|
150
|
+
foreground: cell.style.foreground ?? null,
|
|
151
|
+
background: cell.style.background,
|
|
152
|
+
underlineColor: cell.style.underlineColor ?? null,
|
|
153
|
+
underline: cell.style.underline,
|
|
154
|
+
attributes: cell.style.attributes,
|
|
155
|
+
hyperlink: cell.hyperlink ?? null,
|
|
156
|
+
},
|
|
157
|
+
{ overflow },
|
|
158
|
+
);
|
|
136
159
|
}
|
|
137
160
|
}
|
package/src/screen/screen.ts
CHANGED
|
@@ -39,6 +39,14 @@ export class Screen {
|
|
|
39
39
|
return this.#width;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
The populated length of one row: the nominal width, or more when overflow
|
|
44
|
+
writes (`textWrap: "none"` content) extended it past the screen's width.
|
|
45
|
+
*/
|
|
46
|
+
rowLength(y: number): number {
|
|
47
|
+
return Math.max(this.#width, this.#rows[y]?.length ?? 0);
|
|
48
|
+
}
|
|
49
|
+
|
|
42
50
|
get height(): number {
|
|
43
51
|
return this.#height;
|
|
44
52
|
}
|
|
@@ -56,9 +64,10 @@ export class Screen {
|
|
|
56
64
|
return this.#painted[y]?.[x] ?? false;
|
|
57
65
|
}
|
|
58
66
|
|
|
59
|
-
setCell(x: number, y: number, cell: Cell): void {
|
|
67
|
+
setCell(x: number, y: number, cell: Cell, options?: { overflow?: boolean }): void {
|
|
60
68
|
const row = this.#rows[y];
|
|
61
|
-
|
|
69
|
+
const overflow = options?.overflow === true;
|
|
70
|
+
if (!row || x < 0 || (!overflow && x >= this.width)) {
|
|
62
71
|
return;
|
|
63
72
|
}
|
|
64
73
|
|
|
@@ -67,18 +76,23 @@ export class Screen {
|
|
|
67
76
|
}
|
|
68
77
|
|
|
69
78
|
const finalColumn = x + cell.width;
|
|
70
|
-
|
|
71
|
-
|
|
79
|
+
if (overflow && finalColumn > row.length) {
|
|
80
|
+
this.#growRow(row, y, finalColumn);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const capacity = overflow ? row.length : this.width;
|
|
84
|
+
const clipped = finalColumn > capacity;
|
|
85
|
+
const overwriteEnd = Math.min(finalColumn, capacity);
|
|
72
86
|
|
|
73
87
|
for (let column = x; column < overwriteEnd; column++) {
|
|
74
88
|
this.#clearGraphemeAt(row, y, column);
|
|
75
89
|
}
|
|
76
90
|
|
|
77
91
|
if (clipped) {
|
|
78
|
-
for (let column = x; column <
|
|
92
|
+
for (let column = x; column < capacity; column++) {
|
|
79
93
|
row[column] = emptyCell;
|
|
80
94
|
}
|
|
81
|
-
this.#markDirty(y, x,
|
|
95
|
+
this.#markDirty(y, x, capacity);
|
|
82
96
|
|
|
83
97
|
return;
|
|
84
98
|
}
|
|
@@ -102,13 +116,18 @@ export class Screen {
|
|
|
102
116
|
Composes independent cell channels over the existing cell. An undefined
|
|
103
117
|
patch is transparent; an explicit space in `content` paints a blank.
|
|
104
118
|
*/
|
|
105
|
-
composeCell(
|
|
119
|
+
composeCell(
|
|
120
|
+
x: number,
|
|
121
|
+
y: number,
|
|
122
|
+
patch: CellPatch | undefined,
|
|
123
|
+
options?: { overflow?: boolean },
|
|
124
|
+
): void {
|
|
106
125
|
if (!patch) {
|
|
107
126
|
return;
|
|
108
127
|
}
|
|
109
128
|
|
|
110
129
|
const row = this.#rows[y];
|
|
111
|
-
if (!row || x < 0 || x >= this.width) {
|
|
130
|
+
if (!row || x < 0 || (options?.overflow !== true && x >= this.width)) {
|
|
112
131
|
return;
|
|
113
132
|
}
|
|
114
133
|
|
|
@@ -130,7 +149,12 @@ export class Screen {
|
|
|
130
149
|
const hyperlink =
|
|
131
150
|
patch.hyperlink === undefined ? destination.hyperlink : (patch.hyperlink ?? undefined);
|
|
132
151
|
|
|
133
|
-
this.setCell(
|
|
152
|
+
this.setCell(
|
|
153
|
+
writeColumn,
|
|
154
|
+
y,
|
|
155
|
+
createCell(content.grapheme, content.width, style, hyperlink),
|
|
156
|
+
options,
|
|
157
|
+
);
|
|
134
158
|
}
|
|
135
159
|
|
|
136
160
|
/** Composes a patch throughout a rectangle, clipped to this screen. */
|
|
@@ -191,7 +215,7 @@ export class Screen {
|
|
|
191
215
|
for (const [y, row] of this.#rows.entries()) {
|
|
192
216
|
row.fill(emptyCell);
|
|
193
217
|
this.#painted[y]!.fill(true);
|
|
194
|
-
this.#markDirty(y, 0, this.
|
|
218
|
+
this.#markDirty(y, 0, this.rowLength(y));
|
|
195
219
|
}
|
|
196
220
|
}
|
|
197
221
|
|
|
@@ -209,6 +233,15 @@ export class Screen {
|
|
|
209
233
|
return spans;
|
|
210
234
|
}
|
|
211
235
|
|
|
236
|
+
/** Extends a row (and its paint mask) so overflow writes land past `width`. */
|
|
237
|
+
#growRow(row: Cell[], y: number, length: number): void {
|
|
238
|
+
const painted = this.#painted[y]!;
|
|
239
|
+
while (row.length < length) {
|
|
240
|
+
row.push(emptyCell);
|
|
241
|
+
painted.push(false);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
212
245
|
#clearGraphemeAt(row: Cell[], y: number, x: number): void {
|
|
213
246
|
const existing = row[x];
|
|
214
247
|
if (!existing || existing === emptyCell) {
|
|
@@ -224,7 +257,7 @@ export class Screen {
|
|
|
224
257
|
const width = Math.max(1, leadingCell?.width ?? 1);
|
|
225
258
|
for (
|
|
226
259
|
let column = leadingColumn;
|
|
227
|
-
column < Math.min(leadingColumn + width,
|
|
260
|
+
column < Math.min(leadingColumn + width, row.length);
|
|
228
261
|
column++
|
|
229
262
|
) {
|
|
230
263
|
row[column] = emptyCell;
|
|
@@ -257,7 +290,7 @@ export class Screen {
|
|
|
257
290
|
}
|
|
258
291
|
|
|
259
292
|
const clippedStart = Math.max(0, start);
|
|
260
|
-
const clippedEnd = Math.min(this.
|
|
293
|
+
const clippedEnd = Math.min(this.rowLength(y), end);
|
|
261
294
|
const current = this.#dirty[y];
|
|
262
295
|
this.#dirty[y] = current
|
|
263
296
|
? { start: Math.min(current.start, clippedStart), end: Math.max(current.end, clippedEnd) }
|
package/src/structured-text.ts
CHANGED
package/src/styles.ts
CHANGED
|
@@ -5,6 +5,11 @@ import { Yoga, type Node as YogaNode, type PositionType } from "#/yoga/index.ts"
|
|
|
5
5
|
export type Styles = {
|
|
6
6
|
/*
|
|
7
7
|
We keep this as a single enum so overflow is one complete choice and invalid combinations like wrap + truncate-middle are unrepresentable. In hindsight, `normal` would have been a clearer default value than `wrap`, since it describes the standard behavior instead of repeating the prop name.
|
|
8
|
+
|
|
9
|
+
`none` neither wraps nor truncates: the text overflows the container (and
|
|
10
|
+
the screen's nominal width). Meant for scrollback content (`<Static>`),
|
|
11
|
+
not live regions, where a physically soft-wrapped line breaks the
|
|
12
|
+
presenter's line accounting.
|
|
8
13
|
*/
|
|
9
14
|
readonly textWrap?:
|
|
10
15
|
| "wrap"
|
|
@@ -12,7 +17,8 @@ export type Styles = {
|
|
|
12
17
|
| "truncate-end"
|
|
13
18
|
| "truncate"
|
|
14
19
|
| "truncate-middle"
|
|
15
|
-
| "truncate-start"
|
|
20
|
+
| "truncate-start"
|
|
21
|
+
| "none";
|
|
16
22
|
|
|
17
23
|
/**
|
|
18
24
|
Controls how the element is positioned.
|
|
@@ -171,7 +171,9 @@ function findFirstChangedRow(previous: Screen, next: Screen): number | undefined
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
function rowsEqual(left: Screen, right: Screen, y: number): boolean {
|
|
174
|
-
|
|
174
|
+
// Rows can extend past the nominal width when overflow content painted.
|
|
175
|
+
const length = Math.max(left.rowLength(y), right.rowLength(y));
|
|
176
|
+
for (let x = 0; x < length; x++) {
|
|
175
177
|
if (!cellsEqual(left.cellAt(x, y), right.cellAt(x, y))) return false;
|
|
176
178
|
}
|
|
177
179
|
return true;
|
|
@@ -180,9 +182,7 @@ function rowsEqual(left: Screen, right: Screen, y: number): boolean {
|
|
|
180
182
|
function screensEqual(left: Screen, right: Screen): boolean {
|
|
181
183
|
if (left.width !== right.width || left.height !== right.height) return false;
|
|
182
184
|
for (let y = 0; y < left.height; y++) {
|
|
183
|
-
|
|
184
|
-
if (!cellsEqual(left.cellAt(x, y), right.cellAt(x, y))) return false;
|
|
185
|
-
}
|
|
185
|
+
if (!rowsEqual(left, right, y)) return false;
|
|
186
186
|
}
|
|
187
187
|
return true;
|
|
188
188
|
}
|
package/src/wrap-text.ts
CHANGED
|
@@ -6,6 +6,10 @@ import { type Styles } from "#/styles.ts";
|
|
|
6
6
|
export const wrapTextCache = new QuickLRU<string, string>({ maxSize: 4096 });
|
|
7
7
|
|
|
8
8
|
export const wrapText = (text: string, maxWidth: number, wrapType: Styles["textWrap"]): string => {
|
|
9
|
+
if (wrapType === "none") {
|
|
10
|
+
return text;
|
|
11
|
+
}
|
|
12
|
+
|
|
9
13
|
const cacheKey = text + String(maxWidth) + String(wrapType);
|
|
10
14
|
const cachedText = wrapTextCache.get(cacheKey);
|
|
11
15
|
|