@flowtty/core 1.0.0-alpha.1 → 1.0.0-alpha.11

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.
@@ -1,357 +1,383 @@
1
- import { wrapText, Buffer, BORDER_CHARS } from '../chunk-D6HW2BNM.js';
2
- export { BORDER_CHARS } from '../chunk-D6HW2BNM.js';
3
- import { loadYoga, FlexDirection, PositionType, Edge, Gutter, Display, Wrap, Justify, Align, MeasureMode } from 'yoga-layout/load';
4
-
5
- var yogaPromise = null;
1
+ import { a as Buffer, n as BORDER_CHARS, t as wrapText } from "../wrap-D5f0P1iA.js";
2
+ import { Align, Display, Edge, FlexDirection, Gutter, Justify, MeasureMode, PositionType, Wrap, loadYoga } from "yoga-layout/load";
3
+ //#region src/host/yoga.ts
4
+ let yogaPromise = null;
6
5
  function getYoga() {
7
- yogaPromise ??= loadYoga();
8
- return yogaPromise;
6
+ yogaPromise ??= loadYoga();
7
+ return yogaPromise;
9
8
  }
10
-
11
- // src/host/host.ts
9
+ //#endregion
10
+ //#region src/host/host.ts
12
11
  function createInstance(type, props, Yoga) {
13
- const node = Yoga.Node.create();
14
- const inst = { type: "box", props, yogaNode: node, children: [] };
15
- applyProps(inst, props);
16
- return inst;
12
+ const inst = {
13
+ type: "box",
14
+ props,
15
+ yogaNode: Yoga.Node.create(),
16
+ children: []
17
+ };
18
+ applyProps(inst, props, Yoga);
19
+ return inst;
17
20
  }
18
21
  function createTextInstance(text, _Yoga) {
19
- return { type: "text", text };
22
+ return {
23
+ type: "text",
24
+ text
25
+ };
20
26
  }
21
27
  function applyProps(inst, props, _Yoga) {
22
- inst.props = props;
23
- const n = inst.yogaNode;
24
- if (typeof props.width === "number") n.setWidth(props.width);
25
- else if (typeof props.width === "string" && props.width.endsWith("%")) {
26
- n.setWidthPercent(parseFloat(props.width));
27
- } else n.setWidthAuto();
28
- if (typeof props.height === "number") n.setHeight(props.height);
29
- else if (typeof props.height === "string" && props.height.endsWith("%")) {
30
- n.setHeightPercent(parseFloat(props.height));
31
- } else n.setHeightAuto();
32
- n.setFlexDirection(
33
- props.flexDirection === "row" ? FlexDirection.Row : FlexDirection.Column
34
- );
35
- n.setPositionType(props.position === "absolute" ? PositionType.Absolute : PositionType.Static);
36
- if (props.top !== void 0) n.setPosition(Edge.Top, props.top);
37
- if (props.left !== void 0) n.setPosition(Edge.Left, props.left);
38
- if (props.right !== void 0) n.setPosition(Edge.Right, props.right);
39
- if (props.bottom !== void 0) n.setPosition(Edge.Bottom, props.bottom);
40
- const borderWidth = props.border ? 1 : 0;
41
- n.setBorder(Edge.Top, borderWidth);
42
- n.setBorder(Edge.Right, borderWidth);
43
- n.setBorder(Edge.Bottom, borderWidth);
44
- n.setBorder(Edge.Left, borderWidth);
45
- const padTop = props.paddingTop ?? props.paddingY ?? props.padding ?? 0;
46
- const padRight = props.paddingRight ?? props.paddingX ?? props.padding ?? 0;
47
- const padBottom = props.paddingBottom ?? props.paddingY ?? props.padding ?? 0;
48
- const padLeft = props.paddingLeft ?? props.paddingX ?? props.padding ?? 0;
49
- n.setPadding(Edge.Top, padTop);
50
- n.setPadding(Edge.Right, padRight);
51
- n.setPadding(Edge.Bottom, padBottom);
52
- n.setPadding(Edge.Left, padLeft);
53
- const marTop = props.marginTop ?? props.marginY ?? props.margin ?? 0;
54
- const marRight = props.marginRight ?? props.marginX ?? props.margin ?? 0;
55
- const marBottom = props.marginBottom ?? props.marginY ?? props.margin ?? 0;
56
- const marLeft = props.marginLeft ?? props.marginX ?? props.margin ?? 0;
57
- n.setMargin(Edge.Top, marTop);
58
- n.setMargin(Edge.Right, marRight);
59
- n.setMargin(Edge.Bottom, marBottom);
60
- n.setMargin(Edge.Left, marLeft);
61
- const rGap = props.rowGap ?? props.gap ?? 0;
62
- const cGap = props.columnGap ?? props.gap ?? 0;
63
- n.setGap(Gutter.Row, rGap);
64
- n.setGap(Gutter.Column, cGap);
65
- n.setFlexGrow(props.flexGrow ?? 0);
66
- n.setFlexShrink(props.flexShrink ?? 0);
67
- if (typeof props.flexBasis === "number") {
68
- n.setFlexBasis(props.flexBasis);
69
- } else if (typeof props.flexBasis === "string" && props.flexBasis.endsWith("%")) {
70
- n.setFlexBasisPercent(parseFloat(props.flexBasis));
71
- } else {
72
- n.setFlexBasisAuto();
73
- }
74
- n.setMinWidth(props.minWidth);
75
- n.setMaxWidth(props.maxWidth);
76
- n.setMinHeight(props.minHeight);
77
- n.setMaxHeight(props.maxHeight);
78
- n.setAspectRatio(props.aspectRatio);
79
- n.setDisplay(props.display === "none" ? Display.None : Display.Flex);
80
- n.setFlexWrap(wrapMap(props.flexWrap));
81
- n.setAlignContent(acMap(props.alignContent));
82
- n.setJustifyContent(jcMap(props.justifyContent));
83
- n.setAlignItems(aiMap(props.alignItems));
28
+ inst.props = props;
29
+ const n = inst.yogaNode;
30
+ if (typeof props.width === "number") n.setWidth(props.width);
31
+ else if (typeof props.width === "string" && props.width.endsWith("%")) n.setWidthPercent(parseFloat(props.width));
32
+ else n.setWidthAuto();
33
+ if (typeof props.height === "number") n.setHeight(props.height);
34
+ else if (typeof props.height === "string" && props.height.endsWith("%")) n.setHeightPercent(parseFloat(props.height));
35
+ else n.setHeightAuto();
36
+ n.setFlexDirection(props.flexDirection === "row" ? FlexDirection.Row : FlexDirection.Column);
37
+ const isViewport = props.scrollTop !== void 0 || props.scrollBottom !== void 0;
38
+ n.setPositionType(props.position === "absolute" ? PositionType.Absolute : isViewport ? PositionType.Relative : PositionType.Static);
39
+ if (props.top !== void 0) n.setPosition(Edge.Top, props.top);
40
+ if (props.left !== void 0) n.setPosition(Edge.Left, props.left);
41
+ if (props.right !== void 0) n.setPosition(Edge.Right, props.right);
42
+ if (props.bottom !== void 0) n.setPosition(Edge.Bottom, props.bottom);
43
+ const borderWidth = props.border ? 1 : 0;
44
+ n.setBorder(Edge.Top, borderWidth);
45
+ n.setBorder(Edge.Right, borderWidth);
46
+ n.setBorder(Edge.Bottom, borderWidth);
47
+ n.setBorder(Edge.Left, borderWidth);
48
+ const padTop = props.paddingTop ?? props.paddingY ?? props.padding ?? 0;
49
+ const padRight = props.paddingRight ?? props.paddingX ?? props.padding ?? 0;
50
+ const padBottom = props.paddingBottom ?? props.paddingY ?? props.padding ?? 0;
51
+ const padLeft = props.paddingLeft ?? props.paddingX ?? props.padding ?? 0;
52
+ n.setPadding(Edge.Top, padTop);
53
+ n.setPadding(Edge.Right, padRight);
54
+ n.setPadding(Edge.Bottom, padBottom);
55
+ n.setPadding(Edge.Left, padLeft);
56
+ const marTop = props.marginTop ?? props.marginY ?? props.margin ?? 0;
57
+ const marRight = props.marginRight ?? props.marginX ?? props.margin ?? 0;
58
+ const marBottom = props.marginBottom ?? props.marginY ?? props.margin ?? 0;
59
+ const marLeft = props.marginLeft ?? props.marginX ?? props.margin ?? 0;
60
+ n.setMargin(Edge.Top, marTop);
61
+ n.setMargin(Edge.Right, marRight);
62
+ n.setMargin(Edge.Bottom, marBottom);
63
+ n.setMargin(Edge.Left, marLeft);
64
+ const rGap = props.rowGap ?? props.gap ?? 0;
65
+ const cGap = props.columnGap ?? props.gap ?? 0;
66
+ n.setGap(Gutter.Row, rGap);
67
+ n.setGap(Gutter.Column, cGap);
68
+ n.setFlexGrow(props.flexGrow ?? 0);
69
+ n.setFlexShrink(props.flexShrink ?? 0);
70
+ if (typeof props.flexBasis === "number") n.setFlexBasis(props.flexBasis);
71
+ else if (typeof props.flexBasis === "string" && props.flexBasis.endsWith("%")) n.setFlexBasisPercent(parseFloat(props.flexBasis));
72
+ else n.setFlexBasisAuto();
73
+ n.setMinWidth(props.minWidth);
74
+ n.setMaxWidth(props.maxWidth);
75
+ n.setMinHeight(props.minHeight);
76
+ n.setMaxHeight(props.maxHeight);
77
+ n.setAspectRatio(props.aspectRatio);
78
+ n.setDisplay(props.display === "none" ? Display.None : Display.Flex);
79
+ n.setFlexWrap(wrapMap(props.flexWrap));
80
+ n.setAlignContent(acMap(props.alignContent));
81
+ n.setJustifyContent(jcMap(props.justifyContent));
82
+ n.setAlignItems(aiMap(props.alignItems));
84
83
  }
85
84
  function wrapMap(v) {
86
- switch (v) {
87
- case "wrap":
88
- return Wrap.Wrap;
89
- case "wrap-reverse":
90
- return Wrap.WrapReverse;
91
- default:
92
- return Wrap.NoWrap;
93
- }
85
+ switch (v) {
86
+ case "wrap": return Wrap.Wrap;
87
+ case "wrap-reverse": return Wrap.WrapReverse;
88
+ default: return Wrap.NoWrap;
89
+ }
94
90
  }
95
91
  function jcMap(v) {
96
- switch (v) {
97
- case "center":
98
- return Justify.Center;
99
- case "flex-end":
100
- return Justify.FlexEnd;
101
- case "space-between":
102
- return Justify.SpaceBetween;
103
- case "space-around":
104
- return Justify.SpaceAround;
105
- case "space-evenly":
106
- return Justify.SpaceEvenly;
107
- default:
108
- return Justify.FlexStart;
109
- }
92
+ switch (v) {
93
+ case "center": return Justify.Center;
94
+ case "flex-end": return Justify.FlexEnd;
95
+ case "space-between": return Justify.SpaceBetween;
96
+ case "space-around": return Justify.SpaceAround;
97
+ case "space-evenly": return Justify.SpaceEvenly;
98
+ default: return Justify.FlexStart;
99
+ }
110
100
  }
111
101
  function aiMap(v) {
112
- switch (v) {
113
- case "center":
114
- return Align.Center;
115
- case "flex-end":
116
- return Align.FlexEnd;
117
- case "flex-start":
118
- return Align.FlexStart;
119
- // Default 'stretch' (matches React Native; deviates from CSS flex-start).
120
- // Rationale: most TUI use cases want children to fill cross-axis (e.g. a
121
- // column dialog wants its TextInput/buttons to span the dialog width); CSS's
122
- // flex-start default forces content-sizing which causes percentage children
123
- // to collapse and onLayout-driven scroll to misreport viewport widths.
124
- default:
125
- return Align.Stretch;
126
- }
102
+ switch (v) {
103
+ case "center": return Align.Center;
104
+ case "flex-end": return Align.FlexEnd;
105
+ case "flex-start": return Align.FlexStart;
106
+ default: return Align.Stretch;
107
+ }
127
108
  }
128
109
  function acMap(v) {
129
- switch (v) {
130
- case "flex-end":
131
- return Align.FlexEnd;
132
- case "center":
133
- return Align.Center;
134
- case "space-between":
135
- return Align.SpaceBetween;
136
- case "space-around":
137
- return Align.SpaceAround;
138
- case "space-evenly":
139
- return Align.SpaceEvenly;
140
- case "stretch":
141
- return Align.Stretch;
142
- default:
143
- return Align.FlexStart;
144
- }
110
+ switch (v) {
111
+ case "flex-end": return Align.FlexEnd;
112
+ case "center": return Align.Center;
113
+ case "space-between": return Align.SpaceBetween;
114
+ case "space-around": return Align.SpaceAround;
115
+ case "space-evenly": return Align.SpaceEvenly;
116
+ case "stretch": return Align.Stretch;
117
+ default: return Align.FlexStart;
118
+ }
145
119
  }
146
120
  function measureText(text) {
147
- const lines = text.split("\n");
148
- const width = lines.reduce((m, l) => Math.max(m, [...l].length), 0);
149
- return { width, height: lines.length };
121
+ const lines = text.split("\n");
122
+ return {
123
+ width: lines.reduce((m, l) => Math.max(m, [...l].length), 0),
124
+ height: lines.length
125
+ };
150
126
  }
151
127
  function ownText(inst) {
152
- return inst.children.filter((c) => c.type === "text").map((c) => c.text).join("");
128
+ return inst.children.filter((c) => c.type === "text").map((c) => c.text).join("");
153
129
  }
154
130
  function refreshMeasure(inst, _Yoga) {
155
- const hasText = inst.children.some((c) => c.type === "text");
156
- const hasBox = inst.children.some((c) => c.type === "box");
157
- if (hasText && !hasBox) {
158
- const text = ownText(inst);
159
- const mode = inst.props.wrap ?? "none";
160
- inst.yogaNode.setMeasureFunc((width, widthMode) => {
161
- if (mode !== "none" && (widthMode === MeasureMode.Exactly || widthMode === MeasureMode.AtMost) && Number.isFinite(width)) {
162
- const cap = Math.max(0, Math.floor(width));
163
- const lines = wrapText(text, cap, mode);
164
- const longest = lines.reduce((m, l) => Math.max(m, [...l].length), 0);
165
- return { width: longest, height: lines.length };
166
- }
167
- return measureText(text);
168
- });
169
- inst.yogaNode.markDirty();
170
- } else {
171
- inst.yogaNode.setMeasureFunc(null);
172
- }
131
+ const hasText = inst.children.some((c) => c.type === "text");
132
+ const hasBox = inst.children.some((c) => c.type === "box");
133
+ if (hasText && !hasBox) {
134
+ const text = ownText(inst);
135
+ const mode = inst.props.wrap ?? "none";
136
+ inst.yogaNode.setMeasureFunc((width, widthMode) => {
137
+ if (mode !== "none" && (widthMode === MeasureMode.Exactly || widthMode === MeasureMode.AtMost) && Number.isFinite(width)) {
138
+ const lines = wrapText(text, Math.max(0, Math.floor(width)), mode);
139
+ return {
140
+ width: lines.reduce((m, l) => Math.max(m, [...l].length), 0),
141
+ height: lines.length
142
+ };
143
+ }
144
+ return measureText(text);
145
+ });
146
+ inst.yogaNode.markDirty();
147
+ } else inst.yogaNode.setMeasureFunc(null);
148
+ }
149
+ function detachForMove(parent, child) {
150
+ const i = parent.children.indexOf(child);
151
+ if (i < 0) return;
152
+ parent.children.splice(i, 1);
153
+ if (child.type === "box") parent.yogaNode.removeChild(child.yogaNode);
173
154
  }
174
155
  function appendChild(parent, child, Yoga) {
175
- parent.children.push(child);
176
- if (child.type === "box") {
177
- parent.yogaNode.setMeasureFunc(null);
178
- parent.yogaNode.insertChild(child.yogaNode, parent.yogaNode.getChildCount());
179
- } else {
180
- child.parent = parent;
181
- }
182
- refreshMeasure(parent);
156
+ detachForMove(parent, child);
157
+ parent.children.push(child);
158
+ if (child.type === "box") {
159
+ parent.yogaNode.setMeasureFunc(null);
160
+ parent.yogaNode.insertChild(child.yogaNode, parent.yogaNode.getChildCount());
161
+ } else child.parent = parent;
162
+ refreshMeasure(parent, Yoga);
183
163
  }
184
164
  function removeChild(parent, child, Yoga) {
185
- const i = parent.children.indexOf(child);
186
- if (i >= 0) parent.children.splice(i, 1);
187
- if (child.type === "box") {
188
- parent.yogaNode.removeChild(child.yogaNode);
189
- child.yogaNode.freeRecursive();
190
- } else {
191
- child.parent = void 0;
192
- }
193
- refreshMeasure(parent);
165
+ const i = parent.children.indexOf(child);
166
+ if (i >= 0) parent.children.splice(i, 1);
167
+ if (child.type === "box") {
168
+ parent.yogaNode.removeChild(child.yogaNode);
169
+ child.yogaNode.freeRecursive();
170
+ } else child.parent = void 0;
171
+ refreshMeasure(parent, Yoga);
194
172
  }
195
173
  function insertBefore(parent, child, before, Yoga) {
196
- const i = parent.children.indexOf(before);
197
- parent.children.splice(i < 0 ? parent.children.length : i, 0, child);
198
- if (child.type === "box") {
199
- const boxIndex = parent.children.filter((c) => c.type === "box").indexOf(child);
200
- parent.yogaNode.setMeasureFunc(null);
201
- parent.yogaNode.insertChild(child.yogaNode, boxIndex);
202
- } else {
203
- child.parent = parent;
204
- }
205
- refreshMeasure(parent);
174
+ detachForMove(parent, child);
175
+ const i = parent.children.indexOf(before);
176
+ parent.children.splice(i < 0 ? parent.children.length : i, 0, child);
177
+ if (child.type === "box") {
178
+ const boxIndex = parent.children.filter((c) => c.type === "box").indexOf(child);
179
+ parent.yogaNode.setMeasureFunc(null);
180
+ parent.yogaNode.insertChild(child.yogaNode, boxIndex);
181
+ } else child.parent = parent;
182
+ refreshMeasure(parent, Yoga);
206
183
  }
207
-
208
- // src/host/layout.ts
184
+ //#endregion
185
+ //#region src/host/layout.ts
209
186
  function computeLayout(container, width, height) {
210
- for (const root of container.children) {
211
- root.yogaNode.calculateLayout(width, height);
212
- }
187
+ for (const root of container.children) root.yogaNode.calculateLayout(width, height);
213
188
  }
214
189
  function layoutOf(inst, offsetX = 0, offsetY = 0) {
215
- const n = inst.yogaNode;
216
- return {
217
- left: offsetX + n.getComputedLeft(),
218
- top: offsetY + n.getComputedTop(),
219
- width: n.getComputedWidth(),
220
- height: n.getComputedHeight()
221
- };
190
+ const n = inst.yogaNode;
191
+ return {
192
+ left: offsetX + n.getComputedLeft(),
193
+ top: offsetY + n.getComputedTop(),
194
+ width: n.getComputedWidth(),
195
+ height: n.getComputedHeight()
196
+ };
222
197
  }
223
-
224
- // src/host/paint.ts
198
+ //#endregion
199
+ //#region src/host/paint.ts
225
200
  function paint(container, width, height) {
226
- const buffer = new Buffer(width, height);
227
- for (const root of container.children) paintInstance(root, buffer, 0, 0);
228
- return buffer;
201
+ const buffer = new Buffer(width, height);
202
+ for (const root of container.children) paintInstance(root, buffer, 0, 0);
203
+ return buffer;
229
204
  }
230
205
  function textStyleOf(inst) {
231
- const p = inst.props;
232
- const style = {};
233
- if (p.color !== void 0) style.fg = p.color;
234
- if (p.bold) style.bold = true;
235
- if (p.dim) style.dim = true;
236
- if (p.underline) style.underline = true;
237
- if (p.inverse) style.inverse = true;
238
- if (p.strikethrough) style.strikethrough = true;
239
- if (p.link !== void 0) style.link = p.link;
240
- if (p.backgroundColor !== void 0) style.bg = p.backgroundColor;
241
- return style;
206
+ const p = inst.props;
207
+ const style = {};
208
+ if (p.color !== void 0) style.fg = p.color;
209
+ if (p.bold) style.bold = true;
210
+ if (p.dim) style.dim = true;
211
+ if (p.underline) style.underline = true;
212
+ if (p.inverse) style.inverse = true;
213
+ if (p.strikethrough) style.strikethrough = true;
214
+ if (p.link !== void 0) style.link = p.link;
215
+ if (p.backgroundColor !== void 0) style.bg = p.backgroundColor;
216
+ return style;
242
217
  }
243
218
  function setClipped(buffer, x, y, char, style, clip) {
244
- if (clip !== null) {
245
- if (x < clip.left || y < clip.top || x >= clip.left + clip.width || y >= clip.top + clip.height) return;
246
- }
247
- buffer.set(x, y, char, style);
219
+ if (clip !== null) {
220
+ if (x < clip.left || y < clip.top || x >= clip.left + clip.width || y >= clip.top + clip.height) return;
221
+ }
222
+ buffer.set(x, y, char, style);
248
223
  }
249
224
  function intersectRects(a, b) {
250
- if (a === null) return b;
251
- const left = Math.max(a.left, b.left);
252
- const top = Math.max(a.top, b.top);
253
- const right = Math.min(a.left + a.width, b.left + b.width);
254
- const bottom = Math.min(a.top + a.height, b.top + b.height);
255
- if (right <= left || bottom <= top) return { left, top, width: 0, height: 0 };
256
- return { left, top, width: right - left, height: bottom - top };
225
+ if (a === null) return b;
226
+ const left = Math.max(a.left, b.left);
227
+ const top = Math.max(a.top, b.top);
228
+ const right = Math.min(a.left + a.width, b.left + b.width);
229
+ const bottom = Math.min(a.top + a.height, b.top + b.height);
230
+ if (right <= left || bottom <= top) return {
231
+ left,
232
+ top,
233
+ width: 0,
234
+ height: 0
235
+ };
236
+ return {
237
+ left,
238
+ top,
239
+ width: right - left,
240
+ height: bottom - top
241
+ };
257
242
  }
258
- function paintBorder(inst, buffer, box, clip) {
259
- const style = inst.props.border;
260
- if (!style) return;
261
- if (box.width < 2 || box.height < 2) return;
262
- const chars = BORDER_CHARS[style];
263
- const cellStyle = {};
264
- if (inst.props.borderColor !== void 0) cellStyle.fg = inst.props.borderColor;
265
- const x0 = box.left;
266
- const y0 = box.top;
267
- const x1 = box.left + box.width - 1;
268
- const y1 = box.top + box.height - 1;
269
- setClipped(buffer, x0, y0, chars.tl, cellStyle, clip);
270
- setClipped(buffer, x1, y0, chars.tr, cellStyle, clip);
271
- setClipped(buffer, x0, y1, chars.bl, cellStyle, clip);
272
- setClipped(buffer, x1, y1, chars.br, cellStyle, clip);
273
- for (let x = x0 + 1; x < x1; x++) {
274
- setClipped(buffer, x, y0, chars.h, cellStyle, clip);
275
- setClipped(buffer, x, y1, chars.h, cellStyle, clip);
276
- }
277
- for (let y = y0 + 1; y < y1; y++) {
278
- setClipped(buffer, x0, y, chars.v, cellStyle, clip);
279
- setClipped(buffer, x1, y, chars.v, cellStyle, clip);
280
- }
281
- const title = inst.props.borderTitle;
282
- if (title && title !== "") {
283
- const avail = box.width - 4;
284
- if (avail >= 1) {
285
- const raw = ` ${title} `;
286
- const titleChars = [...raw];
287
- const drawN = Math.min(titleChars.length, avail);
288
- for (let i = 0; i < drawN; i++) {
289
- const ch = i === drawN - 1 && titleChars.length > avail ? "\u2026" : titleChars[i];
290
- setClipped(buffer, x0 + 2 + i, y0, ch, cellStyle, clip);
291
- }
292
- }
293
- }
243
+ function paintBorder(inst, buffer, box, clip, effectiveBg) {
244
+ const style = inst.props.border;
245
+ if (!style) return;
246
+ if (box.width < 2 || box.height < 2) return;
247
+ const chars = BORDER_CHARS[style];
248
+ const cellStyle = {};
249
+ if (inst.props.borderColor !== void 0) cellStyle.fg = inst.props.borderColor;
250
+ const bg = inst.props.borderBackgroundColor ?? effectiveBg;
251
+ if (bg !== void 0 && bg !== "default") cellStyle.bg = bg;
252
+ const x0 = box.left;
253
+ const y0 = box.top;
254
+ const x1 = box.left + box.width - 1;
255
+ const y1 = box.top + box.height - 1;
256
+ setClipped(buffer, x0, y0, chars.tl, cellStyle, clip);
257
+ setClipped(buffer, x1, y0, chars.tr, cellStyle, clip);
258
+ setClipped(buffer, x0, y1, chars.bl, cellStyle, clip);
259
+ setClipped(buffer, x1, y1, chars.br, cellStyle, clip);
260
+ for (let x = x0 + 1; x < x1; x++) {
261
+ setClipped(buffer, x, y0, chars.h, cellStyle, clip);
262
+ setClipped(buffer, x, y1, chars.h, cellStyle, clip);
263
+ }
264
+ for (let y = y0 + 1; y < y1; y++) {
265
+ setClipped(buffer, x0, y, chars.v, cellStyle, clip);
266
+ setClipped(buffer, x1, y, chars.v, cellStyle, clip);
267
+ }
268
+ const title = inst.props.borderTitle;
269
+ if (title && title !== "") {
270
+ const avail = box.width - 4;
271
+ if (avail >= 1) {
272
+ const titleChars = [...` ${title} `];
273
+ const drawN = Math.min(titleChars.length, avail);
274
+ for (let i = 0; i < drawN; i++) {
275
+ const ch = i === drawN - 1 && titleChars.length > avail ? "…" : titleChars[i];
276
+ setClipped(buffer, x0 + 2 + i, y0, ch, cellStyle, clip);
277
+ }
278
+ }
279
+ }
294
280
  }
295
281
  function contentRectOf(inst, box) {
296
- const n = inst.yogaNode;
297
- const padT = n.getComputedPadding(Edge.Top) + n.getComputedBorder(Edge.Top);
298
- const padR = n.getComputedPadding(Edge.Right) + n.getComputedBorder(Edge.Right);
299
- const padB = n.getComputedPadding(Edge.Bottom) + n.getComputedBorder(Edge.Bottom);
300
- const padL = n.getComputedPadding(Edge.Left) + n.getComputedBorder(Edge.Left);
301
- return {
302
- left: box.left + padL,
303
- top: box.top + padT,
304
- width: Math.max(0, box.width - padL - padR),
305
- height: Math.max(0, box.height - padT - padB)
306
- };
282
+ const n = inst.yogaNode;
283
+ const padT = n.getComputedPadding(Edge.Top) + n.getComputedBorder(Edge.Top);
284
+ const padR = n.getComputedPadding(Edge.Right) + n.getComputedBorder(Edge.Right);
285
+ const padB = n.getComputedPadding(Edge.Bottom) + n.getComputedBorder(Edge.Bottom);
286
+ const padL = n.getComputedPadding(Edge.Left) + n.getComputedBorder(Edge.Left);
287
+ return {
288
+ left: box.left + padL,
289
+ top: box.top + padT,
290
+ width: Math.max(0, box.width - padL - padR),
291
+ height: Math.max(0, box.height - padT - padB)
292
+ };
293
+ }
294
+ function paddingRectOf(inst, box) {
295
+ const n = inst.yogaNode;
296
+ const t = n.getComputedBorder(Edge.Top);
297
+ const r = n.getComputedBorder(Edge.Right);
298
+ const b = n.getComputedBorder(Edge.Bottom);
299
+ const l = n.getComputedBorder(Edge.Left);
300
+ return {
301
+ left: box.left + l,
302
+ top: box.top + t,
303
+ width: Math.max(0, box.width - l - r),
304
+ height: Math.max(0, box.height - t - b)
305
+ };
307
306
  }
308
307
  function paintInstance(inst, buffer, offsetX, offsetY, inheritedBg = void 0, clip = null) {
309
- if (inst.props.display === "none") return;
310
- const box = layoutOf(inst, offsetX, offsetY);
311
- inst.props.onLayout?.(box);
312
- const ownBg = inst.props.backgroundColor;
313
- const effectiveBg = ownBg ?? inheritedBg;
314
- if (ownBg !== void 0) {
315
- const fillStyle = ownBg === "default" ? {} : { bg: ownBg };
316
- for (let y = box.top; y < box.top + box.height; y++) {
317
- for (let x = box.left; x < box.left + box.width; x++) {
318
- setClipped(buffer, x, y, " ", fillStyle, clip);
319
- }
320
- }
321
- }
322
- paintBorder(inst, buffer, box, clip);
323
- const text = ownText(inst);
324
- if (text) {
325
- const content = contentRectOf(inst, box);
326
- const mode = inst.props.wrap ?? "none";
327
- const lines = mode === "none" ? text.split("\n") : wrapText(text, content.width, mode);
328
- const textStyle = textStyleOf(inst);
329
- if (textStyle.bg === void 0 && effectiveBg !== void 0) {
330
- textStyle.bg = effectiveBg;
331
- }
332
- for (let row = 0; row < lines.length; row++) {
333
- if (row >= content.height) break;
334
- const chars = [...lines[row] ?? ""];
335
- for (let col = 0; col < chars.length; col++) {
336
- if (col >= content.width) break;
337
- const ch = chars[col];
338
- const safe = ch.charCodeAt(0) < 32 ? " " : ch;
339
- setClipped(buffer, content.left + col, content.top + row, safe, textStyle, clip);
340
- }
341
- }
342
- }
343
- const childClip = inst.props.overflow === "hidden" ? intersectRects(clip, contentRectOf(inst, box)) : clip;
344
- const stackFlow = [];
345
- const absolutes = [];
346
- for (const child of inst.children) {
347
- if (child.type !== "box") continue;
348
- (child.props.position === "absolute" ? absolutes : stackFlow).push(child);
349
- }
350
- const byZ = (a, b) => (a.props.zIndex ?? 0) - (b.props.zIndex ?? 0);
351
- stackFlow.sort(byZ);
352
- absolutes.sort(byZ);
353
- for (const child of stackFlow) paintInstance(child, buffer, box.left, box.top, effectiveBg, childClip);
354
- for (const child of absolutes) paintInstance(child, buffer, box.left, box.top, effectiveBg, childClip);
308
+ if (inst.props.display === "none") return;
309
+ const box = layoutOf(inst, offsetX, offsetY);
310
+ inst.props.onLayout?.(box);
311
+ const ownBg = inst.props.backgroundColor;
312
+ const effectiveBg = ownBg ?? inheritedBg;
313
+ const offscreen = clip !== null && (box.left >= clip.left + clip.width || box.left + box.width <= clip.left || box.top >= clip.top + clip.height || box.top + box.height <= clip.top);
314
+ if (!offscreen && inst.props.backdrop === "dim") for (let y = box.top; y < box.top + box.height; y++) for (let x = box.left; x < box.left + box.width; x++) {
315
+ if (x < 0 || y < 0 || x >= buffer.width || y >= buffer.height) continue;
316
+ const under = buffer.get(x, y);
317
+ if (under.style.dim && !under.style.bold) continue;
318
+ const { bold: _bold, ...rest } = under.style;
319
+ setClipped(buffer, x, y, under.char, {
320
+ ...rest,
321
+ dim: true
322
+ }, clip);
323
+ }
324
+ if (!offscreen && ownBg !== void 0) {
325
+ const fillStyle = ownBg === "default" ? {} : { bg: ownBg };
326
+ for (let y = box.top; y < box.top + box.height; y++) for (let x = box.left; x < box.left + box.width; x++) setClipped(buffer, x, y, " ", fillStyle, clip);
327
+ }
328
+ if (!offscreen) paintBorder(inst, buffer, box, clip, effectiveBg);
329
+ const text = offscreen ? "" : ownText(inst);
330
+ if (text) {
331
+ const content = contentRectOf(inst, box);
332
+ const mode = inst.props.wrap ?? "none";
333
+ const lines = mode === "none" ? text.split("\n") : wrapText(text, content.width, mode);
334
+ const textStyle = textStyleOf(inst);
335
+ if (textStyle.bg === void 0 && effectiveBg !== void 0) textStyle.bg = effectiveBg;
336
+ for (let row = 0; row < lines.length; row++) {
337
+ if (row >= content.height) break;
338
+ const chars = [...lines[row] ?? ""];
339
+ for (let col = 0; col < chars.length; col++) {
340
+ if (col >= content.width) break;
341
+ const ch = chars[col];
342
+ const safe = ch.charCodeAt(0) < 32 ? " " : ch;
343
+ setClipped(buffer, content.left + col, content.top + row, safe, textStyle, clip);
344
+ }
345
+ }
346
+ }
347
+ const scrolls = inst.props.scrollTop !== void 0 || inst.props.scrollBottom !== void 0;
348
+ let scrollTop = 0;
349
+ if (scrolls || inst.props.onScrollMetrics) {
350
+ const viewport = contentRectOf(inst, box);
351
+ let contentBottom = 0;
352
+ for (const child of inst.children) {
353
+ if (child.type !== "box" || child.props.position === "absolute" || child.props.display === "none") continue;
354
+ const n = child.yogaNode;
355
+ contentBottom = Math.max(contentBottom, n.getComputedTop() + n.getComputedHeight() + n.getComputedMargin(Edge.Bottom));
356
+ }
357
+ const contentHeight = Math.max(0, contentBottom - (viewport.top - box.top));
358
+ const maxScrollTop = Math.max(0, contentHeight - viewport.height);
359
+ const wanted = inst.props.scrollBottom !== void 0 ? maxScrollTop - inst.props.scrollBottom : inst.props.scrollTop ?? 0;
360
+ scrollTop = Math.max(0, Math.min(maxScrollTop, Math.round(wanted)));
361
+ inst.props.onScrollMetrics?.({
362
+ contentHeight,
363
+ viewportHeight: viewport.height,
364
+ scrollTop,
365
+ maxScrollTop
366
+ });
367
+ }
368
+ const childClip = inst.props.overflow === "hidden" || scrolls ? intersectRects(clip, contentRectOf(inst, box)) : clip;
369
+ const stackFlow = [];
370
+ const absolutes = [];
371
+ for (const child of inst.children) {
372
+ if (child.type !== "box") continue;
373
+ (child.props.position === "absolute" ? absolutes : stackFlow).push(child);
374
+ }
375
+ const byZ = (a, b) => (a.props.zIndex ?? 0) - (b.props.zIndex ?? 0);
376
+ stackFlow.sort(byZ);
377
+ absolutes.sort(byZ);
378
+ for (const child of stackFlow) paintInstance(child, buffer, box.left, box.top - scrollTop, effectiveBg, childClip);
379
+ const overlayClip = scrolls ? intersectRects(clip, paddingRectOf(inst, box)) : childClip;
380
+ for (const child of absolutes) paintInstance(child, buffer, box.left, box.top, effectiveBg, overlayClip);
355
381
  }
356
-
357
- export { appendChild, applyProps, computeLayout, createInstance, createTextInstance, getYoga, insertBefore, layoutOf, measureText, ownText, paint, refreshMeasure, removeChild };
382
+ //#endregion
383
+ export { BORDER_CHARS, appendChild, applyProps, computeLayout, createInstance, createTextInstance, getYoga, insertBefore, layoutOf, measureText, ownText, paint, refreshMeasure, removeChild };