@silvery/term 0.3.0
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/package.json +54 -0
- package/src/adapters/canvas-adapter.ts +356 -0
- package/src/adapters/dom-adapter.ts +452 -0
- package/src/adapters/flexily-zero-adapter.ts +368 -0
- package/src/adapters/terminal-adapter.ts +305 -0
- package/src/adapters/yoga-adapter.ts +370 -0
- package/src/ansi/ansi.ts +251 -0
- package/src/ansi/constants.ts +76 -0
- package/src/ansi/detection.ts +441 -0
- package/src/ansi/hyperlink.ts +38 -0
- package/src/ansi/index.ts +201 -0
- package/src/ansi/patch-console.ts +159 -0
- package/src/ansi/sgr-codes.ts +34 -0
- package/src/ansi/storybook.ts +209 -0
- package/src/ansi/term.ts +724 -0
- package/src/ansi/types.ts +202 -0
- package/src/ansi/underline.ts +156 -0
- package/src/ansi/utils.ts +65 -0
- package/src/ansi-sanitize.ts +509 -0
- package/src/app.ts +571 -0
- package/src/bound-term.ts +94 -0
- package/src/bracketed-paste.ts +75 -0
- package/src/browser-renderer.ts +174 -0
- package/src/buffer.ts +1984 -0
- package/src/clipboard.ts +74 -0
- package/src/cursor-query.ts +85 -0
- package/src/device-attrs.ts +228 -0
- package/src/devtools.ts +123 -0
- package/src/dom/index.ts +194 -0
- package/src/errors.ts +39 -0
- package/src/focus-reporting.ts +48 -0
- package/src/hit-registry-core.ts +228 -0
- package/src/hit-registry.ts +176 -0
- package/src/index.ts +458 -0
- package/src/input.ts +119 -0
- package/src/inspector.ts +155 -0
- package/src/kitty-detect.ts +95 -0
- package/src/kitty-manager.ts +160 -0
- package/src/layout-engine.ts +296 -0
- package/src/layout.ts +26 -0
- package/src/measurer.ts +74 -0
- package/src/mode-query.ts +106 -0
- package/src/mouse-events.ts +419 -0
- package/src/mouse.ts +83 -0
- package/src/non-tty.ts +223 -0
- package/src/osc-markers.ts +32 -0
- package/src/osc-palette.ts +169 -0
- package/src/output.ts +406 -0
- package/src/pane-manager.ts +248 -0
- package/src/pipeline/CLAUDE.md +587 -0
- package/src/pipeline/content-phase-adapter.ts +976 -0
- package/src/pipeline/content-phase.ts +1765 -0
- package/src/pipeline/helpers.ts +42 -0
- package/src/pipeline/index.ts +416 -0
- package/src/pipeline/layout-phase.ts +686 -0
- package/src/pipeline/measure-phase.ts +198 -0
- package/src/pipeline/measure-stats.ts +21 -0
- package/src/pipeline/output-phase.ts +2593 -0
- package/src/pipeline/render-box.ts +343 -0
- package/src/pipeline/render-helpers.ts +243 -0
- package/src/pipeline/render-text.ts +1255 -0
- package/src/pipeline/types.ts +161 -0
- package/src/pipeline.ts +29 -0
- package/src/pixel-size.ts +119 -0
- package/src/render-adapter.ts +179 -0
- package/src/renderer.ts +1330 -0
- package/src/runtime/create-app.tsx +1845 -0
- package/src/runtime/create-buffer.ts +18 -0
- package/src/runtime/create-runtime.ts +325 -0
- package/src/runtime/diff.ts +56 -0
- package/src/runtime/event-handlers.ts +254 -0
- package/src/runtime/index.ts +119 -0
- package/src/runtime/keys.ts +8 -0
- package/src/runtime/layout.ts +164 -0
- package/src/runtime/run.tsx +318 -0
- package/src/runtime/term-provider.ts +399 -0
- package/src/runtime/terminal-lifecycle.ts +246 -0
- package/src/runtime/tick.ts +219 -0
- package/src/runtime/types.ts +210 -0
- package/src/scheduler.ts +723 -0
- package/src/screenshot.ts +57 -0
- package/src/scroll-region.ts +69 -0
- package/src/scroll-utils.ts +97 -0
- package/src/term-def.ts +267 -0
- package/src/terminal-caps.ts +5 -0
- package/src/terminal-colors.ts +216 -0
- package/src/termtest.ts +224 -0
- package/src/text-sizing.ts +109 -0
- package/src/toolbelt/index.ts +72 -0
- package/src/unicode.ts +1763 -0
- package/src/xterm/index.ts +491 -0
- package/src/xterm/xterm-provider.ts +204 -0
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@silvery/term",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Terminal rendering target for silvery",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Bjørn Stabell <bjorn@stabell.org>",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/beorn/silvery.git",
|
|
10
|
+
"directory": "packages/term"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"src"
|
|
14
|
+
],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "src/index.ts",
|
|
17
|
+
"types": "src/index.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./src/index.ts",
|
|
21
|
+
"import": "./src/index.ts"
|
|
22
|
+
},
|
|
23
|
+
"./runtime": {
|
|
24
|
+
"types": "./src/runtime/index.ts",
|
|
25
|
+
"import": "./src/runtime/index.ts"
|
|
26
|
+
},
|
|
27
|
+
"./toolbelt": {
|
|
28
|
+
"types": "./src/toolbelt/index.ts",
|
|
29
|
+
"import": "./src/toolbelt/index.ts"
|
|
30
|
+
},
|
|
31
|
+
"./pipeline": {
|
|
32
|
+
"types": "./src/pipeline/index.ts",
|
|
33
|
+
"import": "./src/pipeline/index.ts"
|
|
34
|
+
},
|
|
35
|
+
"./ansi": {
|
|
36
|
+
"types": "./src/ansi/index.ts",
|
|
37
|
+
"import": "./src/ansi/index.ts"
|
|
38
|
+
},
|
|
39
|
+
"./*": {
|
|
40
|
+
"types": "./src/*.ts",
|
|
41
|
+
"import": "./src/*.ts"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@silvery/react": "workspace:*",
|
|
49
|
+
"@silvery/tea": "workspace:*",
|
|
50
|
+
"@silvery/test": "workspace:*",
|
|
51
|
+
"@silvery/theme": "workspace:*",
|
|
52
|
+
"flexily": "github:beorn/flexily"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canvas Render Adapter
|
|
3
|
+
*
|
|
4
|
+
* Implements the RenderAdapter interface for HTML5 Canvas output.
|
|
5
|
+
* The layout engine operates in cell units (columns x rows). This adapter
|
|
6
|
+
* converts cell coordinates to pixel coordinates when drawing to the canvas,
|
|
7
|
+
* using charWidth (fontSize * 0.6) and cellHeight (fontSize * lineHeight).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
BorderChars,
|
|
12
|
+
RenderAdapter,
|
|
13
|
+
RenderBuffer,
|
|
14
|
+
RenderStyle,
|
|
15
|
+
TextMeasureResult,
|
|
16
|
+
TextMeasureStyle,
|
|
17
|
+
TextMeasurer,
|
|
18
|
+
} from "../render-adapter"
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Configuration
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
export interface CanvasAdapterConfig {
|
|
25
|
+
/** Font size in pixels (default: 14) */
|
|
26
|
+
fontSize?: number
|
|
27
|
+
/** Font family (default: 'monospace') */
|
|
28
|
+
fontFamily?: string
|
|
29
|
+
/** Line height multiplier (default: 1.2) */
|
|
30
|
+
lineHeight?: number
|
|
31
|
+
/** Background color (default: '#1e1e1e') */
|
|
32
|
+
backgroundColor?: string
|
|
33
|
+
/** Default foreground color (default: '#d4d4d4') */
|
|
34
|
+
foregroundColor?: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const DEFAULT_CONFIG: Required<CanvasAdapterConfig> = {
|
|
38
|
+
fontSize: 14,
|
|
39
|
+
fontFamily: "monospace",
|
|
40
|
+
lineHeight: 1.2,
|
|
41
|
+
backgroundColor: "#1e1e1e",
|
|
42
|
+
foregroundColor: "#d4d4d4",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ============================================================================
|
|
46
|
+
// Border Characters (same as terminal for consistency)
|
|
47
|
+
// ============================================================================
|
|
48
|
+
|
|
49
|
+
const BORDER_CHARS: Record<string, BorderChars> = {
|
|
50
|
+
single: {
|
|
51
|
+
topLeft: "┌",
|
|
52
|
+
topRight: "┐",
|
|
53
|
+
bottomLeft: "└",
|
|
54
|
+
bottomRight: "┘",
|
|
55
|
+
horizontal: "─",
|
|
56
|
+
vertical: "│",
|
|
57
|
+
},
|
|
58
|
+
double: {
|
|
59
|
+
topLeft: "╔",
|
|
60
|
+
topRight: "╗",
|
|
61
|
+
bottomLeft: "╚",
|
|
62
|
+
bottomRight: "╝",
|
|
63
|
+
horizontal: "═",
|
|
64
|
+
vertical: "║",
|
|
65
|
+
},
|
|
66
|
+
round: {
|
|
67
|
+
topLeft: "╭",
|
|
68
|
+
topRight: "╮",
|
|
69
|
+
bottomLeft: "╰",
|
|
70
|
+
bottomRight: "╯",
|
|
71
|
+
horizontal: "─",
|
|
72
|
+
vertical: "│",
|
|
73
|
+
},
|
|
74
|
+
bold: {
|
|
75
|
+
topLeft: "┏",
|
|
76
|
+
topRight: "┓",
|
|
77
|
+
bottomLeft: "┗",
|
|
78
|
+
bottomRight: "┛",
|
|
79
|
+
horizontal: "━",
|
|
80
|
+
vertical: "┃",
|
|
81
|
+
},
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ============================================================================
|
|
85
|
+
// Canvas Measurer
|
|
86
|
+
// ============================================================================
|
|
87
|
+
|
|
88
|
+
function createCanvasMeasurer(_config: Required<CanvasAdapterConfig>): TextMeasurer {
|
|
89
|
+
// The layout engine operates in cell units (columns x rows), matching the
|
|
90
|
+
// terminal convention. For monospace fonts, text width = character count
|
|
91
|
+
// and line height = 1 row.
|
|
92
|
+
return {
|
|
93
|
+
measureText(text: string, _style?: TextMeasureStyle): TextMeasureResult {
|
|
94
|
+
// For monospace fonts, width is simply the character count (one cell per char)
|
|
95
|
+
return {
|
|
96
|
+
width: text.length,
|
|
97
|
+
height: 1,
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
getLineHeight(_style?: TextMeasureStyle): number {
|
|
102
|
+
return 1
|
|
103
|
+
},
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ============================================================================
|
|
108
|
+
// Color Conversion
|
|
109
|
+
// ============================================================================
|
|
110
|
+
|
|
111
|
+
// ANSI 256-color palette (standard 16 colors)
|
|
112
|
+
const ANSI_COLORS: Record<string, string> = {
|
|
113
|
+
black: "#000000",
|
|
114
|
+
red: "#cd0000",
|
|
115
|
+
green: "#00cd00",
|
|
116
|
+
yellow: "#cdcd00",
|
|
117
|
+
blue: "#0000ee",
|
|
118
|
+
magenta: "#cd00cd",
|
|
119
|
+
cyan: "#00cdcd",
|
|
120
|
+
white: "#e5e5e5",
|
|
121
|
+
gray: "#7f7f7f",
|
|
122
|
+
grey: "#7f7f7f",
|
|
123
|
+
brightBlack: "#7f7f7f",
|
|
124
|
+
brightRed: "#ff0000",
|
|
125
|
+
brightGreen: "#00ff00",
|
|
126
|
+
brightYellow: "#ffff00",
|
|
127
|
+
brightBlue: "#5c5cff",
|
|
128
|
+
brightMagenta: "#ff00ff",
|
|
129
|
+
brightCyan: "#00ffff",
|
|
130
|
+
brightWhite: "#ffffff",
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function resolveColor(color: string | undefined, fallback: string): string {
|
|
134
|
+
if (!color) return fallback
|
|
135
|
+
|
|
136
|
+
// Already a CSS color (hex, rgb, etc.)
|
|
137
|
+
if (color.startsWith("#") || color.startsWith("rgb")) {
|
|
138
|
+
return color
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Named ANSI color
|
|
142
|
+
const named = ANSI_COLORS[color.toLowerCase()]
|
|
143
|
+
if (named) return named
|
|
144
|
+
|
|
145
|
+
// Pass through (might be a CSS color name like 'cyan')
|
|
146
|
+
return color
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ============================================================================
|
|
150
|
+
// Canvas Render Buffer
|
|
151
|
+
// ============================================================================
|
|
152
|
+
|
|
153
|
+
export class CanvasRenderBuffer implements RenderBuffer {
|
|
154
|
+
readonly width: number
|
|
155
|
+
readonly height: number
|
|
156
|
+
readonly canvas: OffscreenCanvas | HTMLCanvasElement
|
|
157
|
+
private ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D
|
|
158
|
+
private config: Required<CanvasAdapterConfig>
|
|
159
|
+
|
|
160
|
+
// Cell-to-pixel conversion factors
|
|
161
|
+
private readonly charWidth: number
|
|
162
|
+
private readonly cellHeight: number
|
|
163
|
+
|
|
164
|
+
constructor(width: number, height: number, config: Required<CanvasAdapterConfig>) {
|
|
165
|
+
this.width = width
|
|
166
|
+
this.height = height
|
|
167
|
+
this.config = config
|
|
168
|
+
|
|
169
|
+
// Compute cell dimensions for coordinate conversion.
|
|
170
|
+
// Width/height are in cell units (cols/rows); drawing converts to pixels.
|
|
171
|
+
this.charWidth = config.fontSize * 0.6
|
|
172
|
+
this.cellHeight = config.fontSize * config.lineHeight
|
|
173
|
+
|
|
174
|
+
// Canvas pixel dimensions (convert cell units to pixels)
|
|
175
|
+
const pixelWidth = width * this.charWidth
|
|
176
|
+
const pixelHeight = height * this.cellHeight
|
|
177
|
+
|
|
178
|
+
// Use OffscreenCanvas for double buffering
|
|
179
|
+
if (typeof OffscreenCanvas !== "undefined") {
|
|
180
|
+
this.canvas = new OffscreenCanvas(pixelWidth, pixelHeight)
|
|
181
|
+
} else if (typeof document !== "undefined") {
|
|
182
|
+
this.canvas = document.createElement("canvas")
|
|
183
|
+
this.canvas.width = pixelWidth
|
|
184
|
+
this.canvas.height = pixelHeight
|
|
185
|
+
} else {
|
|
186
|
+
throw new Error("Canvas not available")
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const ctx = this.canvas.getContext("2d")
|
|
190
|
+
if (!ctx) throw new Error("Could not get 2d context")
|
|
191
|
+
this.ctx = ctx as CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D
|
|
192
|
+
|
|
193
|
+
// Initialize with background
|
|
194
|
+
this.ctx.fillStyle = config.backgroundColor
|
|
195
|
+
this.ctx.fillRect(0, 0, pixelWidth, pixelHeight)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
fillRect(x: number, y: number, width: number, height: number, style: RenderStyle): void {
|
|
199
|
+
if (style.bg) {
|
|
200
|
+
// Convert cell coordinates to pixel coordinates
|
|
201
|
+
const px = x * this.charWidth
|
|
202
|
+
const py = y * this.cellHeight
|
|
203
|
+
const pw = width * this.charWidth
|
|
204
|
+
const ph = height * this.cellHeight
|
|
205
|
+
this.ctx.fillStyle = resolveColor(style.bg, this.config.backgroundColor)
|
|
206
|
+
this.ctx.fillRect(px, py, pw, ph)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
drawText(x: number, y: number, text: string, style: RenderStyle): void {
|
|
211
|
+
// Convert cell coordinates to pixel coordinates
|
|
212
|
+
const px = x * this.charWidth
|
|
213
|
+
const py = y * this.cellHeight
|
|
214
|
+
|
|
215
|
+
const attrs = style.attrs ?? {}
|
|
216
|
+
|
|
217
|
+
// Build font string
|
|
218
|
+
const weight = attrs.bold ? "bold" : "normal"
|
|
219
|
+
const fontStyle = attrs.italic ? "italic" : "normal"
|
|
220
|
+
this.ctx.font = `${fontStyle} ${weight} ${this.config.fontSize}px ${this.config.fontFamily}`
|
|
221
|
+
|
|
222
|
+
// Set colors
|
|
223
|
+
this.ctx.fillStyle = resolveColor(style.fg, this.config.foregroundColor)
|
|
224
|
+
this.ctx.textBaseline = "top"
|
|
225
|
+
|
|
226
|
+
// Draw text
|
|
227
|
+
this.ctx.fillText(text, px, py)
|
|
228
|
+
|
|
229
|
+
// Handle underline
|
|
230
|
+
if (attrs.underline) {
|
|
231
|
+
this.drawUnderline(px, py, text, style)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Handle strikethrough
|
|
235
|
+
if (attrs.strikethrough) {
|
|
236
|
+
const metrics = this.ctx.measureText(text)
|
|
237
|
+
const textWidth = metrics.width
|
|
238
|
+
const strikeY = py + this.config.fontSize * 0.5
|
|
239
|
+
|
|
240
|
+
this.ctx.strokeStyle = resolveColor(style.fg, this.config.foregroundColor)
|
|
241
|
+
this.ctx.lineWidth = 1
|
|
242
|
+
this.ctx.beginPath()
|
|
243
|
+
this.ctx.moveTo(px, strikeY)
|
|
244
|
+
this.ctx.lineTo(px + textWidth, strikeY)
|
|
245
|
+
this.ctx.stroke()
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Draw underline decorations at pixel coordinates.
|
|
251
|
+
* Note: px, py are already in pixel coordinates.
|
|
252
|
+
*/
|
|
253
|
+
private drawUnderline(px: number, py: number, text: string, style: RenderStyle): void {
|
|
254
|
+
const attrs = style.attrs ?? {}
|
|
255
|
+
const metrics = this.ctx.measureText(text)
|
|
256
|
+
const textWidth = metrics.width
|
|
257
|
+
const underlineY = py + this.config.fontSize * 0.9
|
|
258
|
+
|
|
259
|
+
const underlineColor = resolveColor(attrs.underlineColor ?? style.fg, this.config.foregroundColor)
|
|
260
|
+
|
|
261
|
+
this.ctx.strokeStyle = underlineColor
|
|
262
|
+
this.ctx.lineWidth = 1
|
|
263
|
+
|
|
264
|
+
const underlineStyle = attrs.underlineStyle ?? "single"
|
|
265
|
+
|
|
266
|
+
switch (underlineStyle) {
|
|
267
|
+
case "double":
|
|
268
|
+
// Two parallel lines
|
|
269
|
+
this.ctx.beginPath()
|
|
270
|
+
this.ctx.moveTo(px, underlineY - 1)
|
|
271
|
+
this.ctx.lineTo(px + textWidth, underlineY - 1)
|
|
272
|
+
this.ctx.moveTo(px, underlineY + 1)
|
|
273
|
+
this.ctx.lineTo(px + textWidth, underlineY + 1)
|
|
274
|
+
this.ctx.stroke()
|
|
275
|
+
break
|
|
276
|
+
|
|
277
|
+
case "curly":
|
|
278
|
+
// Wavy line using bezier curves
|
|
279
|
+
this.ctx.beginPath()
|
|
280
|
+
this.ctx.moveTo(px, underlineY)
|
|
281
|
+
const waveLength = 4
|
|
282
|
+
const amplitude = 2
|
|
283
|
+
for (let wx = 0; wx < textWidth; wx += waveLength * 2) {
|
|
284
|
+
this.ctx.quadraticCurveTo(px + wx + waveLength / 2, underlineY - amplitude, px + wx + waveLength, underlineY)
|
|
285
|
+
this.ctx.quadraticCurveTo(
|
|
286
|
+
px + wx + (waveLength * 3) / 2,
|
|
287
|
+
underlineY + amplitude,
|
|
288
|
+
px + wx + waveLength * 2,
|
|
289
|
+
underlineY,
|
|
290
|
+
)
|
|
291
|
+
}
|
|
292
|
+
this.ctx.stroke()
|
|
293
|
+
break
|
|
294
|
+
|
|
295
|
+
case "dotted":
|
|
296
|
+
this.ctx.setLineDash([2, 2])
|
|
297
|
+
this.ctx.beginPath()
|
|
298
|
+
this.ctx.moveTo(px, underlineY)
|
|
299
|
+
this.ctx.lineTo(px + textWidth, underlineY)
|
|
300
|
+
this.ctx.stroke()
|
|
301
|
+
this.ctx.setLineDash([])
|
|
302
|
+
break
|
|
303
|
+
|
|
304
|
+
case "dashed":
|
|
305
|
+
this.ctx.setLineDash([4, 2])
|
|
306
|
+
this.ctx.beginPath()
|
|
307
|
+
this.ctx.moveTo(px, underlineY)
|
|
308
|
+
this.ctx.lineTo(px + textWidth, underlineY)
|
|
309
|
+
this.ctx.stroke()
|
|
310
|
+
this.ctx.setLineDash([])
|
|
311
|
+
break
|
|
312
|
+
|
|
313
|
+
default: // 'single'
|
|
314
|
+
this.ctx.beginPath()
|
|
315
|
+
this.ctx.moveTo(px, underlineY)
|
|
316
|
+
this.ctx.lineTo(px + textWidth, underlineY)
|
|
317
|
+
this.ctx.stroke()
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
drawChar(x: number, y: number, char: string, style: RenderStyle): void {
|
|
322
|
+
// For canvas, drawChar is essentially drawText for single chars
|
|
323
|
+
this.drawText(x, y, char, style)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
inBounds(x: number, y: number): boolean {
|
|
327
|
+
return x >= 0 && x < this.width && y >= 0 && y < this.height
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// ============================================================================
|
|
332
|
+
// Canvas Adapter Factory
|
|
333
|
+
// ============================================================================
|
|
334
|
+
|
|
335
|
+
export function createCanvasAdapter(config: CanvasAdapterConfig = {}): RenderAdapter {
|
|
336
|
+
const cfg = { ...DEFAULT_CONFIG, ...config }
|
|
337
|
+
const measurer = createCanvasMeasurer(cfg)
|
|
338
|
+
|
|
339
|
+
return {
|
|
340
|
+
name: "canvas",
|
|
341
|
+
measurer,
|
|
342
|
+
|
|
343
|
+
createBuffer(width: number, height: number): RenderBuffer {
|
|
344
|
+
return new CanvasRenderBuffer(width, height, cfg)
|
|
345
|
+
},
|
|
346
|
+
|
|
347
|
+
flush(_buffer: RenderBuffer, _prevBuffer: RenderBuffer | null): void {
|
|
348
|
+
// Canvas draws directly to the buffer during render.
|
|
349
|
+
// The caller (renderToCanvas) copies the buffer to the visible canvas.
|
|
350
|
+
},
|
|
351
|
+
|
|
352
|
+
getBorderChars(style: string): BorderChars {
|
|
353
|
+
return BORDER_CHARS[style] ?? BORDER_CHARS.single!
|
|
354
|
+
},
|
|
355
|
+
}
|
|
356
|
+
}
|