@effect-tui/react 0.16.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/src/codeblock.d.ts +1 -1
- package/dist/src/codeblock.d.ts.map +1 -1
- package/dist/src/codeblock.js +2 -2
- package/dist/src/codeblock.js.map +1 -1
- package/dist/src/components/Markdown.js +3 -3
- package/dist/src/components/Markdown.js.map +1 -1
- package/dist/src/components/MultilineTextInput.d.ts.map +1 -1
- package/dist/src/components/MultilineTextInput.js +133 -305
- package/dist/src/components/MultilineTextInput.js.map +1 -1
- package/dist/src/components/TextInput.d.ts.map +1 -1
- package/dist/src/components/TextInput.js +51 -98
- package/dist/src/components/TextInput.js.map +1 -1
- package/dist/src/components/text-editing.d.ts +61 -0
- package/dist/src/components/text-editing.d.ts.map +1 -1
- package/dist/src/components/text-editing.js +131 -0
- package/dist/src/components/text-editing.js.map +1 -1
- package/dist/src/hosts/base.d.ts +13 -2
- package/dist/src/hosts/base.d.ts.map +1 -1
- package/dist/src/hosts/base.js +74 -2
- package/dist/src/hosts/base.js.map +1 -1
- package/dist/src/hosts/box.d.ts +2 -2
- package/dist/src/hosts/box.d.ts.map +1 -1
- package/dist/src/hosts/box.js +29 -2
- package/dist/src/hosts/box.js.map +1 -1
- package/dist/src/hosts/canvas.d.ts +22 -2
- package/dist/src/hosts/canvas.d.ts.map +1 -1
- package/dist/src/hosts/canvas.js +99 -31
- package/dist/src/hosts/canvas.js.map +1 -1
- package/dist/src/hosts/codeblock.d.ts +8 -10
- package/dist/src/hosts/codeblock.d.ts.map +1 -1
- package/dist/src/hosts/codeblock.js +36 -33
- package/dist/src/hosts/codeblock.js.map +1 -1
- package/dist/src/hosts/flex-container.d.ts +2 -2
- package/dist/src/hosts/flex-container.d.ts.map +1 -1
- package/dist/src/hosts/flex-container.js +17 -2
- package/dist/src/hosts/flex-container.js.map +1 -1
- package/dist/src/hosts/index.d.ts +1 -1
- package/dist/src/hosts/index.d.ts.map +1 -1
- package/dist/src/hosts/index.js.map +1 -1
- package/dist/src/hosts/overlay-item.d.ts +2 -2
- package/dist/src/hosts/overlay-item.d.ts.map +1 -1
- package/dist/src/hosts/overlay-item.js +7 -2
- package/dist/src/hosts/overlay-item.js.map +1 -1
- package/dist/src/hosts/overlay.d.ts +2 -2
- package/dist/src/hosts/overlay.d.ts.map +1 -1
- package/dist/src/hosts/overlay.js +2 -2
- package/dist/src/hosts/overlay.js.map +1 -1
- package/dist/src/hosts/scroll.d.ts +7 -2
- package/dist/src/hosts/scroll.d.ts.map +1 -1
- package/dist/src/hosts/scroll.js +126 -45
- package/dist/src/hosts/scroll.js.map +1 -1
- package/dist/src/hosts/single-child.d.ts.map +1 -1
- package/dist/src/hosts/single-child.js +2 -0
- package/dist/src/hosts/single-child.js.map +1 -1
- package/dist/src/hosts/spacer.d.ts +1 -1
- package/dist/src/hosts/spacer.d.ts.map +1 -1
- package/dist/src/hosts/spacer.js +6 -1
- package/dist/src/hosts/spacer.js.map +1 -1
- package/dist/src/hosts/text.d.ts +20 -15
- package/dist/src/hosts/text.d.ts.map +1 -1
- package/dist/src/hosts/text.js +104 -71
- package/dist/src/hosts/text.js.map +1 -1
- package/dist/src/hosts/zstack.d.ts +2 -2
- package/dist/src/hosts/zstack.d.ts.map +1 -1
- package/dist/src/hosts/zstack.js +7 -2
- package/dist/src/hosts/zstack.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/internal/renderer/index.d.ts.map +1 -1
- package/dist/src/internal/renderer/index.js +41 -16
- package/dist/src/internal/renderer/index.js.map +1 -1
- package/dist/src/internal/renderer/types.d.ts +4 -0
- package/dist/src/internal/renderer/types.d.ts.map +1 -1
- package/dist/src/motion/hooks.d.ts +1 -1
- package/dist/src/motion/hooks.js +1 -1
- package/dist/src/reconciler/host-config.js +2 -2
- package/dist/src/reconciler/host-config.js.map +1 -1
- package/dist/src/reconciler/types.d.ts +5 -1
- package/dist/src/reconciler/types.d.ts.map +1 -1
- package/dist/src/utils/border.d.ts +1 -1
- package/dist/src/utils/border.d.ts.map +1 -1
- package/dist/src/utils/border.js +2 -0
- package/dist/src/utils/border.js.map +1 -1
- package/dist/src/utils/index.d.ts +2 -1
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/index.js +2 -1
- package/dist/src/utils/index.js.map +1 -1
- package/dist/src/utils/text-layout.d.ts +22 -0
- package/dist/src/utils/text-layout.d.ts.map +1 -0
- package/dist/src/utils/text-layout.js +37 -0
- package/dist/src/utils/text-layout.js.map +1 -0
- package/dist/src/utils/text-wrap.d.ts +26 -1
- package/dist/src/utils/text-wrap.d.ts.map +1 -1
- package/dist/src/utils/text-wrap.js +106 -11
- package/dist/src/utils/text-wrap.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/codeblock.tsx +2 -2
- package/src/components/Markdown.tsx +3 -3
- package/src/components/MultilineTextInput.tsx +138 -344
- package/src/components/TextInput.tsx +54 -99
- package/src/components/text-editing.ts +180 -0
- package/src/hosts/base.ts +86 -3
- package/src/hosts/box.ts +37 -2
- package/src/hosts/canvas.ts +120 -31
- package/src/hosts/codeblock.ts +46 -33
- package/src/hosts/flex-container.ts +21 -2
- package/src/hosts/index.ts +1 -1
- package/src/hosts/overlay-item.ts +8 -2
- package/src/hosts/overlay.ts +2 -2
- package/src/hosts/scroll.ts +142 -45
- package/src/hosts/single-child.ts +2 -0
- package/src/hosts/spacer.ts +6 -1
- package/src/hosts/text.ts +122 -75
- package/src/hosts/zstack.ts +7 -2
- package/src/index.ts +1 -1
- package/src/internal/renderer/index.ts +53 -20
- package/src/internal/renderer/types.ts +4 -0
- package/src/motion/hooks.ts +1 -1
- package/src/reconciler/host-config.ts +2 -2
- package/src/reconciler/types.ts +7 -1
- package/src/utils/border.ts +11 -1
- package/src/utils/index.ts +15 -1
- package/src/utils/text-layout.ts +65 -0
- package/src/utils/text-wrap.ts +135 -13
package/src/hosts/canvas.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CellBuffer, Color, Palette } from "@effect-tui/core"
|
|
2
|
-
import { Colors } from "@effect-tui/core"
|
|
2
|
+
import { Colors, displayWidth } from "@effect-tui/core"
|
|
3
3
|
import type { CommonProps, HostContext, Size } from "../reconciler/types.js"
|
|
4
|
+
import * as Prof from "../profiler.js"
|
|
4
5
|
import {
|
|
5
6
|
type BorderKind,
|
|
6
7
|
borderChars,
|
|
@@ -20,6 +21,9 @@ export interface DrawContext {
|
|
|
20
21
|
/** Canvas height in cells */
|
|
21
22
|
height: number
|
|
22
23
|
|
|
24
|
+
/** Resolve a style id for reuse across many draw calls */
|
|
25
|
+
style(opts?: { fg?: Color; bg?: Color; bold?: boolean; italic?: boolean; underline?: boolean; inverse?: boolean }): number
|
|
26
|
+
|
|
23
27
|
/** Draw text at position */
|
|
24
28
|
text(
|
|
25
29
|
x: number,
|
|
@@ -29,7 +33,7 @@ export interface DrawContext {
|
|
|
29
33
|
): void
|
|
30
34
|
|
|
31
35
|
/** Fill rectangle with character */
|
|
32
|
-
|
|
36
|
+
fillRect(
|
|
33
37
|
x: number,
|
|
34
38
|
y: number,
|
|
35
39
|
w: number,
|
|
@@ -54,6 +58,20 @@ export interface DrawContext {
|
|
|
54
58
|
|
|
55
59
|
/** Clear entire canvas */
|
|
56
60
|
clear(): void
|
|
61
|
+
|
|
62
|
+
/** Draw a single cell with a codepoint */
|
|
63
|
+
cell(x: number, y: number, cp: number, style?: number, width?: number): void
|
|
64
|
+
|
|
65
|
+
/** Draw many single-cell codepoints efficiently */
|
|
66
|
+
cells(cells: Array<CanvasCell>): void
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface CanvasCell {
|
|
70
|
+
x: number
|
|
71
|
+
y: number
|
|
72
|
+
cp: number
|
|
73
|
+
style?: number
|
|
74
|
+
width?: number
|
|
57
75
|
}
|
|
58
76
|
|
|
59
77
|
export interface CanvasProps extends CommonProps {
|
|
@@ -78,7 +96,7 @@ export class CanvasHost extends LeafHost {
|
|
|
78
96
|
this.updateProps(props as unknown as Record<string, unknown>)
|
|
79
97
|
}
|
|
80
98
|
|
|
81
|
-
|
|
99
|
+
protected measureSelf(maxW: number, maxH: number): Size {
|
|
82
100
|
const constrained = this.constrainProposal(maxW, maxH)
|
|
83
101
|
const size = {
|
|
84
102
|
w: this.fixedWidth ?? constrained.w,
|
|
@@ -103,11 +121,24 @@ export class CanvasHost extends LeafHost {
|
|
|
103
121
|
width: w,
|
|
104
122
|
height: h,
|
|
105
123
|
|
|
124
|
+
style: (opts) => {
|
|
125
|
+
const effectiveBg = opts?.bg ?? (this.inheritBg ? inheritedBgValue : undefined)
|
|
126
|
+
return styleIdFromProps(palette, {
|
|
127
|
+
fg: opts?.fg,
|
|
128
|
+
bg: effectiveBg,
|
|
129
|
+
bold: opts?.bold,
|
|
130
|
+
italic: opts?.italic,
|
|
131
|
+
underline: opts?.underline,
|
|
132
|
+
inverse: opts?.inverse,
|
|
133
|
+
})
|
|
134
|
+
},
|
|
135
|
+
|
|
106
136
|
text: (x, y, str, opts) => {
|
|
107
137
|
const px = Math.round(ox + x)
|
|
108
138
|
const py = Math.round(oy + y)
|
|
109
139
|
// Use inherited bg when inheritBg is enabled and no explicit bg provided
|
|
110
140
|
const effectiveBg = opts?.bg ?? (this.inheritBg ? inheritedBgValue : undefined)
|
|
141
|
+
const styleT = Prof.startPhase()
|
|
111
142
|
const style = styleIdFromProps(palette, {
|
|
112
143
|
fg: opts?.fg,
|
|
113
144
|
bg: effectiveBg,
|
|
@@ -116,20 +147,40 @@ export class CanvasHost extends LeafHost {
|
|
|
116
147
|
underline: opts?.underline,
|
|
117
148
|
inverse: opts?.inverse,
|
|
118
149
|
})
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
150
|
+
Prof.endPhase("canvas.text.style", styleT)
|
|
151
|
+
const widthT = Prof.startPhase()
|
|
152
|
+
let textWidth = 0
|
|
153
|
+
let asciiCp: number | null = null
|
|
154
|
+
if (str.length === 1) {
|
|
155
|
+
const code = str.charCodeAt(0)
|
|
156
|
+
if (code >= 0x20 && code <= 0x7e) {
|
|
157
|
+
textWidth = 1
|
|
158
|
+
asciiCp = code
|
|
159
|
+
} else {
|
|
160
|
+
textWidth = displayWidth(str)
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
textWidth = displayWidth(str)
|
|
164
|
+
}
|
|
165
|
+
Prof.endPhase("canvas.text.width", widthT)
|
|
166
|
+
if (textWidth > 0) {
|
|
167
|
+
const drawT = Prof.startPhase()
|
|
168
|
+
if (asciiCp !== null) {
|
|
169
|
+
buffer.drawCP(px, py, asciiCp, style, 1)
|
|
170
|
+
} else {
|
|
171
|
+
buffer.drawText(px, py, str, style, textWidth)
|
|
172
|
+
}
|
|
173
|
+
Prof.endPhase("canvas.text.draw", drawT)
|
|
124
174
|
}
|
|
125
175
|
},
|
|
126
176
|
|
|
127
|
-
|
|
177
|
+
fillRect: (x, y, fw, fh, char = " ", opts) => {
|
|
128
178
|
const px = Math.round(ox + x)
|
|
129
179
|
const py = Math.round(oy + y)
|
|
130
|
-
const cp = char.codePointAt(0)!
|
|
180
|
+
const cp = char.length > 0 ? char.codePointAt(0)! : " ".codePointAt(0)!
|
|
131
181
|
// Use inherited bg when inheritBg is enabled and no explicit bg provided
|
|
132
182
|
const effectiveBg = opts?.bg ?? (this.inheritBg ? inheritedBgValue : undefined)
|
|
183
|
+
const styleT = Prof.startPhase()
|
|
133
184
|
const style = styleIdFromProps(palette, {
|
|
134
185
|
fg: opts?.fg,
|
|
135
186
|
bg: effectiveBg,
|
|
@@ -138,65 +189,103 @@ export class CanvasHost extends LeafHost {
|
|
|
138
189
|
underline: opts?.underline,
|
|
139
190
|
inverse: opts?.inverse,
|
|
140
191
|
})
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
192
|
+
Prof.endPhase("canvas.fillRect.style", styleT)
|
|
193
|
+
const fillW = Math.ceil(fw)
|
|
194
|
+
const fillH = Math.ceil(fh)
|
|
195
|
+
const drawT = Prof.startPhase()
|
|
196
|
+
buffer.fillRect(px, py, fillW, fillH, cp, style)
|
|
197
|
+
Prof.endPhase("canvas.fillRect.draw", drawT)
|
|
148
198
|
},
|
|
149
199
|
|
|
150
200
|
box: (x, y, bw, bh, opts) => {
|
|
151
201
|
const px = Math.round(ox + x)
|
|
152
202
|
const py = Math.round(oy + y)
|
|
203
|
+
const boxW = Math.ceil(bw)
|
|
204
|
+
const boxH = Math.ceil(bh)
|
|
153
205
|
const border = opts?.border ?? "none"
|
|
206
|
+
const bgStyleT = Prof.startPhase()
|
|
154
207
|
const { value: bgValue, styleId: bgStyleId } = resolveBgStyle(palette, opts?.bg)
|
|
208
|
+
Prof.endPhase("canvas.box.bg.style", bgStyleT)
|
|
155
209
|
|
|
156
210
|
// Fill background (with clipping)
|
|
157
211
|
if (bgValue !== undefined) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const xx = px + col
|
|
162
|
-
buffer.drawCP(xx, yy, " ".codePointAt(0)!, bgStyleId)
|
|
163
|
-
}
|
|
164
|
-
}
|
|
212
|
+
const bgDrawT = Prof.startPhase()
|
|
213
|
+
buffer.fillRect(px, py, boxW, boxH, " ".codePointAt(0)!, bgStyleId)
|
|
214
|
+
Prof.endPhase("canvas.box.bg.draw", bgDrawT)
|
|
165
215
|
}
|
|
166
216
|
|
|
167
217
|
// Draw border (with clipping)
|
|
168
|
-
if (border !== "none" &&
|
|
218
|
+
if (border !== "none" && boxW >= 2 && boxH >= 2) {
|
|
219
|
+
const borderStyleT = Prof.startPhase()
|
|
169
220
|
const chars = borderChars(border)
|
|
170
221
|
const borderFg = toColorValue(opts?.borderColor) ?? toColorValue(opts?.fg) ?? Colors.ansi.gray(8)
|
|
171
222
|
const borderStyle = palette.id({ fg: borderFg })
|
|
172
|
-
|
|
223
|
+
Prof.endPhase("canvas.box.border.style", borderStyleT)
|
|
224
|
+
const borderDrawT = Prof.startPhase()
|
|
225
|
+
drawBorder(buffer, px, py, boxW, boxH, chars, borderStyle, { ox, oy, w, h })
|
|
226
|
+
Prof.endPhase("canvas.box.border.draw", borderDrawT)
|
|
173
227
|
}
|
|
174
228
|
},
|
|
175
229
|
|
|
176
230
|
clear: () => {
|
|
231
|
+
const clearT = Prof.startPhase()
|
|
177
232
|
const style = palette.id({})
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
233
|
+
buffer.fillRect(ox, oy, w, h, " ".codePointAt(0)!, style)
|
|
234
|
+
Prof.endPhase("canvas.clear", clearT)
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
cell: (x, y, cp, style = 0, width) => {
|
|
238
|
+
const px = Math.round(ox + x)
|
|
239
|
+
const py = Math.round(oy + y)
|
|
240
|
+
const drawT = Prof.startPhase()
|
|
241
|
+
buffer.drawCP(px, py, cp, style, width)
|
|
242
|
+
Prof.endPhase("canvas.cell.draw", drawT)
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
cells: (cells) => {
|
|
246
|
+
const drawT = Prof.startPhase()
|
|
247
|
+
const baseX = ox
|
|
248
|
+
const baseY = oy
|
|
249
|
+
for (const cell of cells) {
|
|
250
|
+
const px = Math.round(baseX + cell.x)
|
|
251
|
+
const py = Math.round(baseY + cell.y)
|
|
252
|
+
buffer.drawCP(px, py, cell.cp, cell.style ?? 0, cell.width)
|
|
182
253
|
}
|
|
254
|
+
Prof.endPhase("canvas.cells.draw", drawT)
|
|
183
255
|
},
|
|
184
256
|
}
|
|
185
257
|
|
|
186
258
|
buffer.withClip(ox, oy, w, h, () => {
|
|
187
259
|
// Call user's draw function within the canvas clip region
|
|
260
|
+
const drawT = Prof.startPhase()
|
|
188
261
|
this.draw(ctx)
|
|
262
|
+
Prof.endPhase("canvas.draw", drawT)
|
|
189
263
|
})
|
|
190
264
|
}
|
|
191
265
|
|
|
192
266
|
override updateProps(props: Record<string, unknown>): void {
|
|
193
267
|
super.updateProps(props)
|
|
268
|
+
const prevDraw = this.draw
|
|
269
|
+
const prevWidth = this.fixedWidth
|
|
270
|
+
const prevHeight = this.fixedHeight
|
|
271
|
+
const prevInheritBg = this.inheritBg
|
|
272
|
+
|
|
194
273
|
if (props.draw !== undefined) {
|
|
195
274
|
this.draw = props.draw as CanvasProps["draw"]
|
|
196
|
-
this.ctx.requestRender() // trigger repaint when draw function changes
|
|
197
275
|
}
|
|
198
276
|
if (props.width !== undefined) this.fixedWidth = props.width as number
|
|
199
277
|
if (props.height !== undefined) this.fixedHeight = props.height as number
|
|
200
278
|
if (props.inheritBg !== undefined) this.inheritBg = props.inheritBg as boolean
|
|
279
|
+
|
|
280
|
+
const layoutChanged = prevWidth !== this.fixedWidth || prevHeight !== this.fixedHeight
|
|
281
|
+
if (layoutChanged) {
|
|
282
|
+
this.invalidateLayout()
|
|
283
|
+
return
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const renderChanged = prevDraw !== this.draw || prevInheritBg !== this.inheritBg
|
|
287
|
+
if (renderChanged) {
|
|
288
|
+
this.invalidateRender()
|
|
289
|
+
}
|
|
201
290
|
}
|
|
202
291
|
}
|
package/src/hosts/codeblock.ts
CHANGED
|
@@ -1,33 +1,35 @@
|
|
|
1
1
|
import { type CellBuffer, type Color, Colors, displayWidth, type Palette } from "@effect-tui/core"
|
|
2
2
|
import type { HighlightLine } from "../highlight.js"
|
|
3
3
|
import type { CommonProps, HostContext, Size } from "../reconciler/types.js"
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
type Padding,
|
|
6
|
+
type PaddingInput,
|
|
7
|
+
resolveBgStyle,
|
|
8
|
+
resolvePadding,
|
|
9
|
+
spansDisplayWidth,
|
|
10
|
+
styleIdFromProps,
|
|
11
|
+
} from "../utils/index.js"
|
|
5
12
|
import { LeafHost } from "./leaf.js"
|
|
6
13
|
|
|
7
14
|
export interface CodeBlockProps extends CommonProps {
|
|
8
15
|
lines: HighlightLine[]
|
|
9
16
|
lineNumbers?: boolean
|
|
10
17
|
padding?: PaddingInput
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function lineDisplayWidth(line: HighlightLine): number {
|
|
17
|
-
return line.reduce((w, token) => w + displayWidth(token.text), 0)
|
|
18
|
+
bg?: Color
|
|
19
|
+
lineNumberFg?: Color
|
|
20
|
+
lineNumberBg?: Color
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
export class CodeBlockHost extends LeafHost {
|
|
21
24
|
lines: HighlightLine[] = [[]]
|
|
22
25
|
lineNumbers = false
|
|
23
26
|
padding: Padding = { top: 0, right: 0, bottom: 0, left: 0 }
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
bg?: Color
|
|
28
|
+
lineNumberFg?: Color
|
|
29
|
+
lineNumberBg?: Color
|
|
27
30
|
|
|
28
31
|
private cachedLineWidths: number[] = []
|
|
29
32
|
private gutterWidth = 0
|
|
30
|
-
private prepared = false
|
|
31
33
|
|
|
32
34
|
constructor(props: CodeBlockProps, ctx: HostContext) {
|
|
33
35
|
super("codeblock", props, ctx)
|
|
@@ -51,21 +53,15 @@ export class CodeBlockHost extends LeafHost {
|
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
private prepareMetrics(): void {
|
|
54
|
-
this.cachedLineWidths = this.lines.map((line) =>
|
|
56
|
+
this.cachedLineWidths = this.lines.map((line) => spansDisplayWidth(line))
|
|
55
57
|
this.gutterWidth = this.computeGutterWidth()
|
|
56
|
-
this.prepared = true
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
if (this.prepared) return
|
|
60
|
+
protected override prepareSelf(_layoutDirty: boolean, _renderDirty: boolean): void {
|
|
61
61
|
this.prepareMetrics()
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
protected
|
|
65
|
-
this.ensurePrepared()
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
measure(maxW: number, maxH: number): Size {
|
|
64
|
+
protected measureSelf(maxW: number, maxH: number): Size {
|
|
69
65
|
const constrained = this.constrainProposal(maxW, maxH)
|
|
70
66
|
this.ensurePrepared()
|
|
71
67
|
|
|
@@ -89,7 +85,7 @@ export class CodeBlockHost extends LeafHost {
|
|
|
89
85
|
const startX = x + this.padding.left + this.gutterWidth
|
|
90
86
|
const startY = y + this.padding.top
|
|
91
87
|
|
|
92
|
-
const { value: bgValue, styleId: bgStyleId } = resolveBgStyle(palette, this.
|
|
88
|
+
const { value: bgValue, styleId: bgStyleId } = resolveBgStyle(palette, this.bg)
|
|
93
89
|
if (bgValue !== undefined && w > 0 && h > 0) {
|
|
94
90
|
buffer.fillRect(x, y, w, h, " ".codePointAt(0)!, bgStyleId)
|
|
95
91
|
}
|
|
@@ -100,8 +96,8 @@ export class CodeBlockHost extends LeafHost {
|
|
|
100
96
|
|
|
101
97
|
if (this.lineNumbers) {
|
|
102
98
|
const gutterStyle = styleIdFromProps(palette, {
|
|
103
|
-
fg: this.
|
|
104
|
-
bg: this.
|
|
99
|
+
fg: this.lineNumberFg ?? Colors.ansi.gray(11),
|
|
100
|
+
bg: this.lineNumberBg ?? this.bg,
|
|
105
101
|
})
|
|
106
102
|
const digits = String(i + 1).padStart(this.gutterWidth - 1, " ")
|
|
107
103
|
buffer.drawText(x + this.padding.left, lineY, `${digits} `, gutterStyle, this.gutterWidth)
|
|
@@ -116,7 +112,7 @@ export class CodeBlockHost extends LeafHost {
|
|
|
116
112
|
const style = token.style ?? {}
|
|
117
113
|
const styleId = styleIdFromProps(palette, {
|
|
118
114
|
fg: style.fg,
|
|
119
|
-
bg: style.bg ?? this.
|
|
115
|
+
bg: style.bg ?? this.bg,
|
|
120
116
|
bold: style.bold,
|
|
121
117
|
italic: style.italic,
|
|
122
118
|
underline: style.underline,
|
|
@@ -131,19 +127,36 @@ export class CodeBlockHost extends LeafHost {
|
|
|
131
127
|
|
|
132
128
|
override updateProps(props: Record<string, unknown>): void {
|
|
133
129
|
super.updateProps(props)
|
|
134
|
-
let
|
|
130
|
+
let layoutChanged = false
|
|
131
|
+
let renderChanged = false
|
|
135
132
|
if (props.lines !== undefined) {
|
|
136
133
|
this.lines = props.lines as HighlightLine[]
|
|
137
|
-
|
|
134
|
+
layoutChanged = true
|
|
138
135
|
}
|
|
139
136
|
if (props.lineNumbers !== undefined) {
|
|
140
137
|
this.lineNumbers = !!props.lineNumbers
|
|
141
|
-
|
|
138
|
+
layoutChanged = true
|
|
139
|
+
}
|
|
140
|
+
if (props.padding !== undefined) {
|
|
141
|
+
this.padding = resolvePadding(props.padding as CodeBlockProps["padding"])
|
|
142
|
+
layoutChanged = true
|
|
143
|
+
}
|
|
144
|
+
if (props.bg !== undefined) {
|
|
145
|
+
this.bg = props.bg as Color
|
|
146
|
+
renderChanged = true
|
|
147
|
+
}
|
|
148
|
+
if (props.lineNumberFg !== undefined) {
|
|
149
|
+
this.lineNumberFg = props.lineNumberFg as Color
|
|
150
|
+
renderChanged = true
|
|
151
|
+
}
|
|
152
|
+
if (props.lineNumberBg !== undefined) {
|
|
153
|
+
this.lineNumberBg = props.lineNumberBg as Color
|
|
154
|
+
renderChanged = true
|
|
155
|
+
}
|
|
156
|
+
if (layoutChanged) {
|
|
157
|
+
this.invalidateLayout()
|
|
158
|
+
} else if (renderChanged) {
|
|
159
|
+
this.invalidateRender()
|
|
142
160
|
}
|
|
143
|
-
if (props.padding !== undefined) this.padding = resolvePadding(props.padding as CodeBlockProps["padding"])
|
|
144
|
-
if (props.background !== undefined) this.background = props.background as Color
|
|
145
|
-
if (props.lineNumberColor !== undefined) this.lineNumberColor = props.lineNumberColor as Color
|
|
146
|
-
if (props.lineNumberBackground !== undefined) this.lineNumberBackground = props.lineNumberBackground as Color
|
|
147
|
-
if (invalidate) this.prepared = false
|
|
148
161
|
}
|
|
149
162
|
}
|
|
@@ -79,7 +79,7 @@ export class FlexContainerHost<A extends FlexAxis> extends BaseHost {
|
|
|
79
79
|
* </vstack>
|
|
80
80
|
* ```
|
|
81
81
|
*/
|
|
82
|
-
|
|
82
|
+
protected measureSelf(maxW: number, maxH: number): Size {
|
|
83
83
|
// Apply frame constraints to what we propose to children
|
|
84
84
|
const constrained = this.constrainProposal(maxW, maxH)
|
|
85
85
|
const insetX = this.padding.left + this.padding.right
|
|
@@ -102,7 +102,7 @@ export class FlexContainerHost<A extends FlexAxis> extends BaseHost {
|
|
|
102
102
|
return this.constrainResult(paddedSize)
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
override
|
|
105
|
+
protected override layoutSelf(rect: Rect): void {
|
|
106
106
|
const layoutRect = this.layoutWithConstraints(rect)
|
|
107
107
|
const stretchCross = this.axis === "vertical" ? this.alignment === "left" : this.alignment === "top"
|
|
108
108
|
const insetX = this.padding.left + this.padding.right
|
|
@@ -142,6 +142,11 @@ export class FlexContainerHost<A extends FlexAxis> extends BaseHost {
|
|
|
142
142
|
|
|
143
143
|
override updateProps(props: Record<string, unknown>): void {
|
|
144
144
|
super.updateProps(props)
|
|
145
|
+
const prevSpacing = this.spacing
|
|
146
|
+
const prevAlignment = this.alignment
|
|
147
|
+
const prevPadding = this.padding
|
|
148
|
+
const prevBg = this.bg
|
|
149
|
+
|
|
145
150
|
this.spacing = (props.spacing as number | undefined) ?? 0
|
|
146
151
|
// Reset to axis-specific default when undefined
|
|
147
152
|
this.alignment =
|
|
@@ -149,5 +154,19 @@ export class FlexContainerHost<A extends FlexAxis> extends BaseHost {
|
|
|
149
154
|
((this.axis === "vertical" ? "left" : "top") as CrossAlignment<A>)
|
|
150
155
|
this.padding = resolvePadding(props.padding as FlexContainerProps<A>["padding"])
|
|
151
156
|
this.bg = props.bg as Color | undefined
|
|
157
|
+
|
|
158
|
+
const paddingChanged =
|
|
159
|
+
prevPadding.top !== this.padding.top ||
|
|
160
|
+
prevPadding.right !== this.padding.right ||
|
|
161
|
+
prevPadding.bottom !== this.padding.bottom ||
|
|
162
|
+
prevPadding.left !== this.padding.left
|
|
163
|
+
|
|
164
|
+
const layoutChanged = prevSpacing !== this.spacing || prevAlignment !== this.alignment || paddingChanged
|
|
165
|
+
|
|
166
|
+
if (layoutChanged) {
|
|
167
|
+
this.invalidateLayout()
|
|
168
|
+
} else if (prevBg !== this.bg) {
|
|
169
|
+
this.invalidateRender()
|
|
170
|
+
}
|
|
152
171
|
}
|
|
153
172
|
}
|
package/src/hosts/index.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { ZStackHost } from "./zstack.js"
|
|
|
14
14
|
|
|
15
15
|
export { BaseHost } from "./base.js"
|
|
16
16
|
export { BoxHost, type BoxProps } from "./box.js"
|
|
17
|
-
export { CanvasHost, type CanvasProps, type DrawContext } from "./canvas.js"
|
|
17
|
+
export { CanvasHost, type CanvasCell, type CanvasProps, type DrawContext } from "./canvas.js"
|
|
18
18
|
export { CodeBlockHost, type CodeBlockProps } from "./codeblock.js"
|
|
19
19
|
export { HStackHost, type HStackProps } from "./hstack.js"
|
|
20
20
|
export { OverlayHost, type OverlayProps } from "./overlay.js"
|
|
@@ -26,7 +26,7 @@ export class OverlayItemHost extends SingleChildHost {
|
|
|
26
26
|
this.updateProps(props as unknown as Record<string, unknown>)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
override
|
|
29
|
+
protected override measureSelf(maxW: number, maxH: number): Size {
|
|
30
30
|
const constrained = this.constrainProposal(maxW, maxH)
|
|
31
31
|
|
|
32
32
|
if (!this.child) {
|
|
@@ -37,7 +37,7 @@ export class OverlayItemHost extends SingleChildHost {
|
|
|
37
37
|
return this.constrainResult(childSize)
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
override
|
|
40
|
+
protected override layoutSelf(rect: Rect): void {
|
|
41
41
|
const layoutRect = this.layoutWithConstraints(rect)
|
|
42
42
|
this.child?.layout(layoutRect)
|
|
43
43
|
}
|
|
@@ -50,8 +50,14 @@ export class OverlayItemHost extends SingleChildHost {
|
|
|
50
50
|
|
|
51
51
|
override updateProps(props: Record<string, unknown>): void {
|
|
52
52
|
super.updateProps(props)
|
|
53
|
+
const prevAlignment = this.alignment
|
|
53
54
|
if (props.alignment !== undefined) {
|
|
54
55
|
this.alignment = props.alignment as { h?: HAlign; v?: VAlign }
|
|
55
56
|
}
|
|
57
|
+
const layoutChanged =
|
|
58
|
+
prevAlignment.h !== this.alignment.h || prevAlignment.v !== this.alignment.v
|
|
59
|
+
if (layoutChanged) {
|
|
60
|
+
this.invalidateLayout()
|
|
61
|
+
}
|
|
56
62
|
}
|
|
57
63
|
}
|
package/src/hosts/overlay.ts
CHANGED
|
@@ -32,7 +32,7 @@ export class OverlayHost extends BaseHost {
|
|
|
32
32
|
this.updateProps(props as unknown as Record<string, unknown>)
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
override
|
|
35
|
+
protected override measureSelf(maxW: number, maxH: number): Size {
|
|
36
36
|
// Apply frame constraints to what we propose to children
|
|
37
37
|
const constrained = this.constrainProposal(maxW, maxH)
|
|
38
38
|
|
|
@@ -56,7 +56,7 @@ export class OverlayHost extends BaseHost {
|
|
|
56
56
|
return this.constrainResult(baseSize)
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
override
|
|
59
|
+
protected override layoutSelf(rect: Rect): void {
|
|
60
60
|
const layoutRect = this.layoutWithConstraints(rect)
|
|
61
61
|
|
|
62
62
|
// Layout base child to fill our rect
|