@gridland/web 0.2.33 → 0.2.34
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/index.d.ts +3 -2
- package/dist/index.js +1750 -8254
- package/dist/index.js.map +4 -4
- package/dist/{next-Cwun29WQ.d.ts → next-Cdtr1evW.d.ts} +4 -90
- package/dist/next-plugin.cjs +12 -35
- package/dist/next-plugin.cjs.map +1 -1
- package/dist/next-plugin.d.cts +2 -7
- package/dist/next-plugin.d.ts +2 -7
- package/dist/next-plugin.js +12 -35
- package/dist/next-plugin.js.map +1 -1
- package/dist/next.d.ts +2 -1
- package/dist/next.js +654 -7623
- package/dist/next.js.map +4 -4
- package/dist/vite-plugin.d.ts +4 -9
- package/dist/vite-plugin.js +20 -74
- package/dist/vite-plugin.js.map +1 -1
- package/package.json +3 -5
- package/src/shims/native-stub.ts +6 -0
- package/src/shims/slider-deps.ts +2 -2
- package/dist/core-shims.js +0 -46163
- package/dist/core-shims.js.map +0 -7
- package/src/browser-buffer.ts +0 -715
- package/src/core-shims/index.ts +0 -269
- package/src/core-shims/renderable-types.ts +0 -4
- package/src/core-shims/rgba.ts +0 -195
- package/src/core-shims/types.ts +0 -132
- package/src/shims/console-stub.ts +0 -13
- package/src/shims/console.ts +0 -3
- package/src/shims/filters-stub.ts +0 -4
- package/src/shims/native-span-feed-stub.ts +0 -7
- package/src/shims/node-buffer.ts +0 -39
- package/src/shims/node-fs.ts +0 -20
- package/src/shims/node-os.ts +0 -6
- package/src/shims/node-path.ts +0 -35
- package/src/shims/node-stream.ts +0 -10
- package/src/shims/node-url.ts +0 -8
- package/src/shims/node-util.ts +0 -33
- package/src/shims/renderer-stub.ts +0 -21
- package/src/shims/timeline-stub.ts +0 -43
package/src/browser-buffer.ts
DELETED
|
@@ -1,715 +0,0 @@
|
|
|
1
|
-
import type { RGBA } from "./core-shims/rgba"
|
|
2
|
-
import type { CapturedLine, CapturedSpan, CursorStyle } from "./core-shims/types"
|
|
3
|
-
import { attributesWithLink } from "./core-shims/index"
|
|
4
|
-
|
|
5
|
-
// Attribute flags matching TextAttributes from opentui core
|
|
6
|
-
const CONTINUATION = 0xc0000000
|
|
7
|
-
|
|
8
|
-
interface ScissorRect {
|
|
9
|
-
x: number
|
|
10
|
-
y: number
|
|
11
|
-
width: number
|
|
12
|
-
height: number
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export type WidthMethod = "wcwidth" | "unicode"
|
|
16
|
-
|
|
17
|
-
export interface BorderDrawOptions {
|
|
18
|
-
x: number
|
|
19
|
-
y: number
|
|
20
|
-
width: number
|
|
21
|
-
height: number
|
|
22
|
-
borderStyle?: string
|
|
23
|
-
customBorderChars?: Uint32Array
|
|
24
|
-
border: boolean | string[]
|
|
25
|
-
borderColor: RGBA
|
|
26
|
-
backgroundColor: RGBA
|
|
27
|
-
shouldFill?: boolean
|
|
28
|
-
title?: string
|
|
29
|
-
titleAlignment?: "left" | "center" | "right"
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export class BrowserBuffer {
|
|
33
|
-
public id: string
|
|
34
|
-
public respectAlpha: boolean
|
|
35
|
-
|
|
36
|
-
private _width: number
|
|
37
|
-
private _height: number
|
|
38
|
-
private _widthMethod: WidthMethod
|
|
39
|
-
|
|
40
|
-
// Cell data - same layout as native OptimizedBuffer
|
|
41
|
-
public char: Uint32Array
|
|
42
|
-
public fg: Float32Array
|
|
43
|
-
public bg: Float32Array
|
|
44
|
-
public attributes: Uint32Array
|
|
45
|
-
|
|
46
|
-
private scissorStack: ScissorRect[] = []
|
|
47
|
-
private opacityStack: number[] = []
|
|
48
|
-
|
|
49
|
-
// Link registry for clickable links
|
|
50
|
-
public linkRegistry: Map<number, string> = new Map()
|
|
51
|
-
private nextLinkId: number = 1
|
|
52
|
-
/** Cursor rendering config -- set by renderer before pipeline, read by drawEditorView */
|
|
53
|
-
public cursorColor: RGBA | null = null
|
|
54
|
-
public cursorStyleType: CursorStyle = "block"
|
|
55
|
-
/** Line cursor position -- set by drawEditorView during pipeline, read by renderer after */
|
|
56
|
-
public lineCursorPosition: { x: number; y: number } | null = null
|
|
57
|
-
|
|
58
|
-
constructor(
|
|
59
|
-
width: number,
|
|
60
|
-
height: number,
|
|
61
|
-
options: { respectAlpha?: boolean; id?: string; widthMethod?: WidthMethod } = {},
|
|
62
|
-
) {
|
|
63
|
-
this._width = width
|
|
64
|
-
this._height = height
|
|
65
|
-
this._widthMethod = options.widthMethod ?? "wcwidth"
|
|
66
|
-
this.respectAlpha = options.respectAlpha ?? false
|
|
67
|
-
this.id = options.id ?? `browser-buffer-${Math.random().toString(36).slice(2, 8)}`
|
|
68
|
-
|
|
69
|
-
const size = width * height
|
|
70
|
-
this.char = new Uint32Array(size)
|
|
71
|
-
this.fg = new Float32Array(size * 4)
|
|
72
|
-
this.bg = new Float32Array(size * 4)
|
|
73
|
-
this.attributes = new Uint32Array(size)
|
|
74
|
-
|
|
75
|
-
// Fill with spaces
|
|
76
|
-
this.char.fill(0x20) // space
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
static create(
|
|
80
|
-
width: number,
|
|
81
|
-
height: number,
|
|
82
|
-
widthMethod: WidthMethod,
|
|
83
|
-
options?: { respectAlpha?: boolean; id?: string },
|
|
84
|
-
): BrowserBuffer {
|
|
85
|
-
return new BrowserBuffer(width, height, { ...options, widthMethod })
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
get width(): number {
|
|
89
|
-
return this._width
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
get height(): number {
|
|
93
|
-
return this._height
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
get widthMethod(): WidthMethod {
|
|
97
|
-
return this._widthMethod
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
get ptr(): number {
|
|
101
|
-
return 0
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
get buffers() {
|
|
105
|
-
return {
|
|
106
|
-
char: this.char,
|
|
107
|
-
fg: this.fg,
|
|
108
|
-
bg: this.bg,
|
|
109
|
-
attributes: this.attributes,
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
setRespectAlpha(respectAlpha: boolean): void {
|
|
114
|
-
this.respectAlpha = respectAlpha
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
getNativeId(): string {
|
|
118
|
-
return this.id
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
registerLink(url: string): number {
|
|
122
|
-
const id = this.nextLinkId++
|
|
123
|
-
this.linkRegistry.set(id, url)
|
|
124
|
-
return id
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
getLinkUrl(linkId: number): string | undefined {
|
|
128
|
-
return this.linkRegistry.get(linkId)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
private isInScissor(x: number, y: number): boolean {
|
|
132
|
-
if (this.scissorStack.length === 0) return true
|
|
133
|
-
const rect = this.scissorStack[this.scissorStack.length - 1]
|
|
134
|
-
return x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
private getCurrentOpacityMultiplier(): number {
|
|
138
|
-
if (this.opacityStack.length === 0) return 1
|
|
139
|
-
return this.opacityStack[this.opacityStack.length - 1]
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
private applyOpacity(color: RGBA): RGBA {
|
|
143
|
-
const multiplier = this.getCurrentOpacityMultiplier()
|
|
144
|
-
if (multiplier >= 1) return color
|
|
145
|
-
return {
|
|
146
|
-
r: color.r,
|
|
147
|
-
g: color.g,
|
|
148
|
-
b: color.b,
|
|
149
|
-
a: color.a * multiplier,
|
|
150
|
-
buffer: new Float32Array([color.r, color.g, color.b, color.a * multiplier]),
|
|
151
|
-
toInts: color.toInts,
|
|
152
|
-
equals: color.equals,
|
|
153
|
-
map: color.map,
|
|
154
|
-
toString: color.toString,
|
|
155
|
-
} as RGBA
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
clear(bg?: RGBA): void {
|
|
159
|
-
const size = this._width * this._height
|
|
160
|
-
this.char.fill(0x20) // space
|
|
161
|
-
this.attributes.fill(0)
|
|
162
|
-
this.linkRegistry.clear()
|
|
163
|
-
this.nextLinkId = 1
|
|
164
|
-
|
|
165
|
-
if (bg) {
|
|
166
|
-
for (let i = 0; i < size; i++) {
|
|
167
|
-
const offset = i * 4
|
|
168
|
-
this.bg[offset] = bg.r
|
|
169
|
-
this.bg[offset + 1] = bg.g
|
|
170
|
-
this.bg[offset + 2] = bg.b
|
|
171
|
-
this.bg[offset + 3] = bg.a
|
|
172
|
-
// Clear fg
|
|
173
|
-
this.fg[offset] = 0
|
|
174
|
-
this.fg[offset + 1] = 0
|
|
175
|
-
this.fg[offset + 2] = 0
|
|
176
|
-
this.fg[offset + 3] = 0
|
|
177
|
-
}
|
|
178
|
-
} else {
|
|
179
|
-
this.fg.fill(0)
|
|
180
|
-
this.bg.fill(0)
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
setCell(x: number, y: number, char: string, fgColor: RGBA, bgColor: RGBA, attr: number = 0): void {
|
|
185
|
-
if (x < 0 || x >= this._width || y < 0 || y >= this._height) return
|
|
186
|
-
if (!this.isInScissor(x, y)) return
|
|
187
|
-
|
|
188
|
-
const idx = y * this._width + x
|
|
189
|
-
const offset = idx * 4
|
|
190
|
-
|
|
191
|
-
const effectiveBg = this.applyOpacity(bgColor)
|
|
192
|
-
const effectiveFg = this.applyOpacity(fgColor)
|
|
193
|
-
|
|
194
|
-
this.char[idx] = char.codePointAt(0) ?? 0x20
|
|
195
|
-
this.attributes[idx] = attr
|
|
196
|
-
|
|
197
|
-
this.fg[offset] = effectiveFg.r
|
|
198
|
-
this.fg[offset + 1] = effectiveFg.g
|
|
199
|
-
this.fg[offset + 2] = effectiveFg.b
|
|
200
|
-
this.fg[offset + 3] = effectiveFg.a
|
|
201
|
-
|
|
202
|
-
this.bg[offset] = effectiveBg.r
|
|
203
|
-
this.bg[offset + 1] = effectiveBg.g
|
|
204
|
-
this.bg[offset + 2] = effectiveBg.b
|
|
205
|
-
this.bg[offset + 3] = effectiveBg.a
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
setCellWithAlphaBlending(
|
|
209
|
-
x: number,
|
|
210
|
-
y: number,
|
|
211
|
-
char: string,
|
|
212
|
-
fgColor: RGBA,
|
|
213
|
-
bgColor: RGBA,
|
|
214
|
-
attr: number = 0,
|
|
215
|
-
): void {
|
|
216
|
-
// For the PoC, same as setCell
|
|
217
|
-
this.setCell(x, y, char, fgColor, bgColor, attr)
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
drawChar(charCode: number, x: number, y: number, fgColor: RGBA, bgColor: RGBA, attr: number = 0): void {
|
|
221
|
-
if (x < 0 || x >= this._width || y < 0 || y >= this._height) return
|
|
222
|
-
if (!this.isInScissor(x, y)) return
|
|
223
|
-
|
|
224
|
-
const idx = y * this._width + x
|
|
225
|
-
const offset = idx * 4
|
|
226
|
-
|
|
227
|
-
const effectiveBg = this.applyOpacity(bgColor)
|
|
228
|
-
const effectiveFg = this.applyOpacity(fgColor)
|
|
229
|
-
|
|
230
|
-
this.char[idx] = charCode
|
|
231
|
-
this.attributes[idx] = attr
|
|
232
|
-
|
|
233
|
-
this.fg[offset] = effectiveFg.r
|
|
234
|
-
this.fg[offset + 1] = effectiveFg.g
|
|
235
|
-
this.fg[offset + 2] = effectiveFg.b
|
|
236
|
-
this.fg[offset + 3] = effectiveFg.a
|
|
237
|
-
|
|
238
|
-
this.bg[offset] = effectiveBg.r
|
|
239
|
-
this.bg[offset + 1] = effectiveBg.g
|
|
240
|
-
this.bg[offset + 2] = effectiveBg.b
|
|
241
|
-
this.bg[offset + 3] = effectiveBg.a
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
drawText(
|
|
245
|
-
text: string,
|
|
246
|
-
x: number,
|
|
247
|
-
y: number,
|
|
248
|
-
fgColor: RGBA,
|
|
249
|
-
bgColor?: RGBA,
|
|
250
|
-
attr: number = 0,
|
|
251
|
-
_selection?: { start: number; end: number; bgColor?: RGBA; fgColor?: RGBA } | null,
|
|
252
|
-
): void {
|
|
253
|
-
const transparentBg: RGBA = {
|
|
254
|
-
r: 0, g: 0, b: 0, a: 0,
|
|
255
|
-
buffer: new Float32Array([0, 0, 0, 0]),
|
|
256
|
-
} as RGBA
|
|
257
|
-
const bg = bgColor ?? transparentBg
|
|
258
|
-
|
|
259
|
-
let curX = x
|
|
260
|
-
for (const ch of text) {
|
|
261
|
-
if (curX >= this._width) break
|
|
262
|
-
if (curX >= 0) {
|
|
263
|
-
this.setCell(curX, y, ch, fgColor, bg, attr)
|
|
264
|
-
}
|
|
265
|
-
curX++
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
fillRect(x: number, y: number, width: number, height: number, bgColor: RGBA): void {
|
|
270
|
-
for (let row = y; row < y + height && row < this._height; row++) {
|
|
271
|
-
for (let col = x; col < x + width && col < this._width; col++) {
|
|
272
|
-
if (col < 0 || row < 0) continue
|
|
273
|
-
if (!this.isInScissor(col, row)) continue
|
|
274
|
-
|
|
275
|
-
const idx = row * this._width + col
|
|
276
|
-
const offset = idx * 4
|
|
277
|
-
const effectiveBg = this.applyOpacity(bgColor)
|
|
278
|
-
|
|
279
|
-
this.char[idx] = 0x20
|
|
280
|
-
this.bg[offset] = effectiveBg.r
|
|
281
|
-
this.bg[offset + 1] = effectiveBg.g
|
|
282
|
-
this.bg[offset + 2] = effectiveBg.b
|
|
283
|
-
this.bg[offset + 3] = effectiveBg.a
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
drawBox(options: BorderDrawOptions): void {
|
|
289
|
-
const {
|
|
290
|
-
x,
|
|
291
|
-
y,
|
|
292
|
-
width,
|
|
293
|
-
height,
|
|
294
|
-
border,
|
|
295
|
-
borderColor,
|
|
296
|
-
backgroundColor,
|
|
297
|
-
shouldFill = true,
|
|
298
|
-
title,
|
|
299
|
-
titleAlignment = "left",
|
|
300
|
-
} = options
|
|
301
|
-
|
|
302
|
-
if (width <= 0 || height <= 0) return
|
|
303
|
-
|
|
304
|
-
// Parse border sides
|
|
305
|
-
const sides = {
|
|
306
|
-
top: border === true || (Array.isArray(border) && border.includes("top")),
|
|
307
|
-
right: border === true || (Array.isArray(border) && border.includes("right")),
|
|
308
|
-
bottom: border === true || (Array.isArray(border) && border.includes("bottom")),
|
|
309
|
-
left: border === true || (Array.isArray(border) && border.includes("left")),
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// Get border chars (use customBorderChars or default rounded)
|
|
313
|
-
const borderChars = options.customBorderChars ?? this.getDefaultBorderChars(options.borderStyle)
|
|
314
|
-
|
|
315
|
-
// Fill background
|
|
316
|
-
if (shouldFill) {
|
|
317
|
-
const fillStartX = x + (sides.left ? 1 : 0)
|
|
318
|
-
const fillStartY = y + (sides.top ? 1 : 0)
|
|
319
|
-
const fillWidth = width - (sides.left ? 1 : 0) - (sides.right ? 1 : 0)
|
|
320
|
-
const fillHeight = height - (sides.top ? 1 : 0) - (sides.bottom ? 1 : 0)
|
|
321
|
-
if (fillWidth > 0 && fillHeight > 0) {
|
|
322
|
-
this.fillRect(fillStartX, fillStartY, fillWidth, fillHeight, backgroundColor)
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
if (!border) return
|
|
327
|
-
|
|
328
|
-
const transparent: RGBA = { r: 0, g: 0, b: 0, a: 0, buffer: new Float32Array([0, 0, 0, 0]) } as RGBA
|
|
329
|
-
|
|
330
|
-
// Draw borders
|
|
331
|
-
// borderChars layout: [topLeft, topRight, bottomLeft, bottomRight, horizontal, vertical, topT, bottomT, leftT, rightT, cross]
|
|
332
|
-
const topLeft = borderChars[0]
|
|
333
|
-
const topRight = borderChars[1]
|
|
334
|
-
const bottomLeft = borderChars[2]
|
|
335
|
-
const bottomRight = borderChars[3]
|
|
336
|
-
const horizontal = borderChars[4]
|
|
337
|
-
const vertical = borderChars[5]
|
|
338
|
-
|
|
339
|
-
// Top border
|
|
340
|
-
if (sides.top) {
|
|
341
|
-
if (sides.left) this.drawChar(topLeft, x, y, borderColor, transparent)
|
|
342
|
-
for (let col = 1; col < width - 1; col++) {
|
|
343
|
-
this.drawChar(horizontal, x + col, y, borderColor, transparent)
|
|
344
|
-
}
|
|
345
|
-
if (sides.right && width > 1) this.drawChar(topRight, x + width - 1, y, borderColor, transparent)
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// Bottom border
|
|
349
|
-
if (sides.bottom && height > 1) {
|
|
350
|
-
if (sides.left) this.drawChar(bottomLeft, x, y + height - 1, borderColor, transparent)
|
|
351
|
-
for (let col = 1; col < width - 1; col++) {
|
|
352
|
-
this.drawChar(horizontal, x + col, y + height - 1, borderColor, transparent)
|
|
353
|
-
}
|
|
354
|
-
if (sides.right && width > 1)
|
|
355
|
-
this.drawChar(bottomRight, x + width - 1, y + height - 1, borderColor, transparent)
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// Left border
|
|
359
|
-
if (sides.left) {
|
|
360
|
-
for (let row = 1; row < height - 1; row++) {
|
|
361
|
-
this.drawChar(vertical, x, y + row, borderColor, transparent)
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// Right border
|
|
366
|
-
if (sides.right && width > 1) {
|
|
367
|
-
for (let row = 1; row < height - 1; row++) {
|
|
368
|
-
this.drawChar(vertical, x + width - 1, y + row, borderColor, transparent)
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// Draw title on top border
|
|
373
|
-
if (title && sides.top && width > 4) {
|
|
374
|
-
const maxTitleLen = width - 4
|
|
375
|
-
const truncatedTitle = title.length > maxTitleLen ? title.slice(0, maxTitleLen) : title
|
|
376
|
-
let titleX: number
|
|
377
|
-
if (titleAlignment === "center") {
|
|
378
|
-
titleX = x + Math.floor((width - truncatedTitle.length) / 2)
|
|
379
|
-
} else if (titleAlignment === "right") {
|
|
380
|
-
titleX = x + width - truncatedTitle.length - 2
|
|
381
|
-
} else {
|
|
382
|
-
titleX = x + 2
|
|
383
|
-
}
|
|
384
|
-
this.drawText(truncatedTitle, titleX, y, borderColor, transparent)
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
private getDefaultBorderChars(borderStyle?: string): Uint32Array {
|
|
389
|
-
// Rounded border chars by default
|
|
390
|
-
const styles: Record<string, number[]> = {
|
|
391
|
-
rounded: [0x256d, 0x256e, 0x2570, 0x256f, 0x2500, 0x2502, 0x252c, 0x2534, 0x251c, 0x2524, 0x253c],
|
|
392
|
-
single: [0x250c, 0x2510, 0x2514, 0x2518, 0x2500, 0x2502, 0x252c, 0x2534, 0x251c, 0x2524, 0x253c],
|
|
393
|
-
double: [0x2554, 0x2557, 0x255a, 0x255d, 0x2550, 0x2551, 0x2566, 0x2569, 0x2560, 0x2563, 0x256c],
|
|
394
|
-
heavy: [0x250f, 0x2513, 0x2517, 0x251b, 0x2501, 0x2503, 0x2533, 0x253b, 0x2523, 0x252b, 0x254b],
|
|
395
|
-
}
|
|
396
|
-
const chars = styles[borderStyle ?? "rounded"] ?? styles.rounded
|
|
397
|
-
return new Uint32Array(chars)
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
pushScissorRect(x: number, y: number, width: number, height: number): void {
|
|
401
|
-
if (this.scissorStack.length > 0) {
|
|
402
|
-
// Intersect with current scissor
|
|
403
|
-
const current = this.scissorStack[this.scissorStack.length - 1]
|
|
404
|
-
const nx = Math.max(x, current.x)
|
|
405
|
-
const ny = Math.max(y, current.y)
|
|
406
|
-
const nw = Math.min(x + width, current.x + current.width) - nx
|
|
407
|
-
const nh = Math.min(y + height, current.y + current.height) - ny
|
|
408
|
-
this.scissorStack.push({ x: nx, y: ny, width: Math.max(0, nw), height: Math.max(0, nh) })
|
|
409
|
-
} else {
|
|
410
|
-
this.scissorStack.push({ x, y, width, height })
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
popScissorRect(): void {
|
|
415
|
-
this.scissorStack.pop()
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
clearScissorRects(): void {
|
|
419
|
-
this.scissorStack = []
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
pushOpacity(opacity: number): void {
|
|
423
|
-
const current = this.getCurrentOpacityMultiplier()
|
|
424
|
-
this.opacityStack.push(current * opacity)
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
popOpacity(): void {
|
|
428
|
-
this.opacityStack.pop()
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
getCurrentOpacity(): number {
|
|
432
|
-
return this.getCurrentOpacityMultiplier()
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
clearOpacity(): void {
|
|
436
|
-
this.opacityStack = []
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
resize(width: number, height: number): void {
|
|
440
|
-
this._width = width
|
|
441
|
-
this._height = height
|
|
442
|
-
const size = width * height
|
|
443
|
-
this.char = new Uint32Array(size)
|
|
444
|
-
this.fg = new Float32Array(size * 4)
|
|
445
|
-
this.bg = new Float32Array(size * 4)
|
|
446
|
-
this.attributes = new Uint32Array(size)
|
|
447
|
-
this.char.fill(0x20)
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// Read buffer into CapturedLine[] for testing
|
|
451
|
-
getSpanLines(): CapturedLine[] {
|
|
452
|
-
const lines: CapturedLine[] = []
|
|
453
|
-
|
|
454
|
-
for (let row = 0; row < this._height; row++) {
|
|
455
|
-
const spans: CapturedSpan[] = []
|
|
456
|
-
let currentSpan: CapturedSpan | null = null
|
|
457
|
-
|
|
458
|
-
for (let col = 0; col < this._width; col++) {
|
|
459
|
-
const idx = row * this._width + col
|
|
460
|
-
const offset = idx * 4
|
|
461
|
-
|
|
462
|
-
// Skip continuation chars
|
|
463
|
-
if (this.attributes[idx] & CONTINUATION) continue
|
|
464
|
-
|
|
465
|
-
const charCode = this.char[idx]
|
|
466
|
-
const ch = charCode === 0 ? " " : String.fromCodePoint(charCode)
|
|
467
|
-
const fgR = this.fg[offset]
|
|
468
|
-
const fgG = this.fg[offset + 1]
|
|
469
|
-
const fgB = this.fg[offset + 2]
|
|
470
|
-
const fgA = this.fg[offset + 3]
|
|
471
|
-
const bgR = this.bg[offset]
|
|
472
|
-
const bgG = this.bg[offset + 1]
|
|
473
|
-
const bgB = this.bg[offset + 2]
|
|
474
|
-
const bgA = this.bg[offset + 3]
|
|
475
|
-
const attr = this.attributes[idx] & 0xff
|
|
476
|
-
|
|
477
|
-
const fg: RGBA = {
|
|
478
|
-
r: fgR, g: fgG, b: fgB, a: fgA,
|
|
479
|
-
buffer: new Float32Array([fgR, fgG, fgB, fgA]),
|
|
480
|
-
} as RGBA
|
|
481
|
-
const bg: RGBA = {
|
|
482
|
-
r: bgR, g: bgG, b: bgB, a: bgA,
|
|
483
|
-
buffer: new Float32Array([bgR, bgG, bgB, bgA]),
|
|
484
|
-
} as RGBA
|
|
485
|
-
|
|
486
|
-
if (
|
|
487
|
-
currentSpan &&
|
|
488
|
-
currentSpan.fg.r === fgR &&
|
|
489
|
-
currentSpan.fg.g === fgG &&
|
|
490
|
-
currentSpan.fg.b === fgB &&
|
|
491
|
-
currentSpan.fg.a === fgA &&
|
|
492
|
-
currentSpan.bg.r === bgR &&
|
|
493
|
-
currentSpan.bg.g === bgG &&
|
|
494
|
-
currentSpan.bg.b === bgB &&
|
|
495
|
-
currentSpan.bg.a === bgA &&
|
|
496
|
-
currentSpan.attributes === attr
|
|
497
|
-
) {
|
|
498
|
-
currentSpan.text += ch
|
|
499
|
-
currentSpan.width += 1
|
|
500
|
-
} else {
|
|
501
|
-
if (currentSpan) spans.push(currentSpan)
|
|
502
|
-
currentSpan = { text: ch, fg, bg, attributes: attr, width: 1 }
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
if (currentSpan) spans.push(currentSpan)
|
|
507
|
-
lines.push({ spans })
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
return lines
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
// Draw a text buffer view into the buffer
|
|
514
|
-
drawTextBufferView(view: any, x: number, y: number): void {
|
|
515
|
-
if (!view || !view.getVisibleLines) return
|
|
516
|
-
|
|
517
|
-
const lines = view.getVisibleLines()
|
|
518
|
-
if (!lines) return
|
|
519
|
-
|
|
520
|
-
const textAlign = view.textAlign as string | undefined
|
|
521
|
-
const viewWidth = view._viewportWidth as number | undefined
|
|
522
|
-
|
|
523
|
-
for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
|
|
524
|
-
const line = lines[lineIdx]
|
|
525
|
-
if (!line) continue
|
|
526
|
-
|
|
527
|
-
let curX = x
|
|
528
|
-
if (textAlign && textAlign !== "left" && viewWidth) {
|
|
529
|
-
const lineWidth = line.chunks.reduce((sum: number, c: any) => sum + c.text.length, 0)
|
|
530
|
-
if (textAlign === "center") {
|
|
531
|
-
curX = x + Math.floor((viewWidth - lineWidth) / 2)
|
|
532
|
-
} else if (textAlign === "right") {
|
|
533
|
-
curX = x + viewWidth - lineWidth
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
for (const chunk of line.chunks) {
|
|
537
|
-
const text = chunk.text
|
|
538
|
-
const fgColor = chunk.fg
|
|
539
|
-
const bgColor = chunk.bg
|
|
540
|
-
let attr = chunk.attributes ?? 0
|
|
541
|
-
|
|
542
|
-
// Encode link ID into attributes if chunk has a link
|
|
543
|
-
if (chunk.link && chunk.link.url) {
|
|
544
|
-
const linkId = this.registerLink(chunk.link.url)
|
|
545
|
-
attr = attributesWithLink(attr, linkId)
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
for (const ch of text) {
|
|
549
|
-
if (curX >= this._width) break
|
|
550
|
-
if (curX >= 0 && y + lineIdx >= 0 && y + lineIdx < this._height) {
|
|
551
|
-
this.setCell(curX, y + lineIdx, ch, fgColor, bgColor, attr)
|
|
552
|
-
}
|
|
553
|
-
curX++
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
drawTextBuffer(textBufferView: any, x: number, y: number): void {
|
|
560
|
-
this.drawTextBufferView(textBufferView, x, y)
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
drawFrameBuffer(
|
|
564
|
-
destX: number,
|
|
565
|
-
destY: number,
|
|
566
|
-
frameBuffer: BrowserBuffer,
|
|
567
|
-
sourceX: number = 0,
|
|
568
|
-
sourceY: number = 0,
|
|
569
|
-
sourceWidth?: number,
|
|
570
|
-
sourceHeight?: number,
|
|
571
|
-
): void {
|
|
572
|
-
const sw = sourceWidth ?? frameBuffer.width
|
|
573
|
-
const sh = sourceHeight ?? frameBuffer.height
|
|
574
|
-
const srcChar = frameBuffer.char
|
|
575
|
-
const srcFg = frameBuffer.fg
|
|
576
|
-
const srcBg = frameBuffer.bg
|
|
577
|
-
const srcAttr = frameBuffer.attributes
|
|
578
|
-
const srcCols = frameBuffer.width
|
|
579
|
-
|
|
580
|
-
for (let row = 0; row < sh; row++) {
|
|
581
|
-
const srcRow = sourceY + row
|
|
582
|
-
const dstRow = destY + row
|
|
583
|
-
if (srcRow < 0 || srcRow >= frameBuffer.height) continue
|
|
584
|
-
if (dstRow < 0 || dstRow >= this._height) continue
|
|
585
|
-
|
|
586
|
-
for (let col = 0; col < sw; col++) {
|
|
587
|
-
const srcCol = sourceX + col
|
|
588
|
-
const dstCol = destX + col
|
|
589
|
-
if (srcCol < 0 || srcCol >= frameBuffer.width) continue
|
|
590
|
-
if (dstCol < 0 || dstCol >= this._width) continue
|
|
591
|
-
if (!this.isInScissor(dstCol, dstRow)) continue
|
|
592
|
-
|
|
593
|
-
const srcIdx = srcRow * srcCols + srcCol
|
|
594
|
-
const dstIdx = dstRow * this._width + dstCol
|
|
595
|
-
const srcOffset = srcIdx * 4
|
|
596
|
-
const dstOffset = dstIdx * 4
|
|
597
|
-
|
|
598
|
-
this.char[dstIdx] = srcChar[srcIdx]
|
|
599
|
-
this.attributes[dstIdx] = srcAttr[srcIdx]
|
|
600
|
-
|
|
601
|
-
// Apply opacity to fg
|
|
602
|
-
const fgA = srcFg[srcOffset + 3]
|
|
603
|
-
const opacityMul = this.getCurrentOpacityMultiplier()
|
|
604
|
-
this.fg[dstOffset] = srcFg[srcOffset]
|
|
605
|
-
this.fg[dstOffset + 1] = srcFg[srcOffset + 1]
|
|
606
|
-
this.fg[dstOffset + 2] = srcFg[srcOffset + 2]
|
|
607
|
-
this.fg[dstOffset + 3] = fgA * opacityMul
|
|
608
|
-
|
|
609
|
-
// Apply opacity to bg
|
|
610
|
-
const bgA = srcBg[srcOffset + 3]
|
|
611
|
-
this.bg[dstOffset] = srcBg[srcOffset]
|
|
612
|
-
this.bg[dstOffset + 1] = srcBg[srcOffset + 1]
|
|
613
|
-
this.bg[dstOffset + 2] = srcBg[srcOffset + 2]
|
|
614
|
-
this.bg[dstOffset + 3] = bgA * opacityMul
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
drawEditorView(editorView: any, x: number, y: number): void {
|
|
619
|
-
if (!editorView) return
|
|
620
|
-
|
|
621
|
-
const viewport = editorView.getViewport()
|
|
622
|
-
const text = editorView.getText()
|
|
623
|
-
const lines = text.split("\n")
|
|
624
|
-
|
|
625
|
-
// Default colors
|
|
626
|
-
const dfg = editorView.editBuffer?._defaultFg ?? {
|
|
627
|
-
r: 1, g: 1, b: 1, a: 1,
|
|
628
|
-
buffer: new Float32Array([1, 1, 1, 1]),
|
|
629
|
-
} as RGBA
|
|
630
|
-
const dbg = editorView.editBuffer?._defaultBg ?? {
|
|
631
|
-
r: 0, g: 0, b: 0, a: 0,
|
|
632
|
-
buffer: new Float32Array([0, 0, 0, 0]),
|
|
633
|
-
} as RGBA
|
|
634
|
-
|
|
635
|
-
const visibleRows = viewport.height > 0 ? viewport.height : this._height - y
|
|
636
|
-
|
|
637
|
-
if (text === "" && editorView._placeholderChunks && editorView._placeholderChunks.length > 0) {
|
|
638
|
-
// Draw placeholder text; offset by 1 cell for line cursor so it doesn't overlap
|
|
639
|
-
let curX = this.cursorStyleType === "line" ? x + 1 : x
|
|
640
|
-
for (const chunk of editorView._placeholderChunks) {
|
|
641
|
-
const chunkFg = chunk.fg ?? dfg
|
|
642
|
-
const chunkBg = chunk.bg ?? dbg
|
|
643
|
-
const attr = (chunk.attributes ?? 0) | 2 // DIM attribute = 1 << 1
|
|
644
|
-
for (const ch of chunk.text) {
|
|
645
|
-
if (curX >= this._width) break
|
|
646
|
-
if (curX >= 0 && y >= 0 && y < this._height) {
|
|
647
|
-
this.setCell(curX, y, ch, chunkFg, chunkBg, attr)
|
|
648
|
-
}
|
|
649
|
-
curX++
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
} else {
|
|
653
|
-
// Draw text lines
|
|
654
|
-
for (let row = 0; row < visibleRows; row++) {
|
|
655
|
-
const lineIdx = viewport.offsetY + row
|
|
656
|
-
if (lineIdx < 0 || lineIdx >= lines.length) continue
|
|
657
|
-
const dstRow = y + row
|
|
658
|
-
if (dstRow < 0 || dstRow >= this._height) continue
|
|
659
|
-
|
|
660
|
-
const line = lines[lineIdx]
|
|
661
|
-
for (let col = 0; col < line.length; col++) {
|
|
662
|
-
const srcCol = viewport.offsetX + col
|
|
663
|
-
if (srcCol < 0 || srcCol >= line.length) continue
|
|
664
|
-
const dstCol = x + col
|
|
665
|
-
if (dstCol < 0 || dstCol >= this._width) break
|
|
666
|
-
|
|
667
|
-
this.setCell(dstCol, dstRow, line[srcCol], dfg, dbg, 0)
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
// Draw cursor
|
|
673
|
-
const cursor = editorView.getVisualCursor()
|
|
674
|
-
if (cursor) {
|
|
675
|
-
const cursorX = x + cursor.visualCol
|
|
676
|
-
const cursorY = y + cursor.visualRow
|
|
677
|
-
if (cursorX >= 0 && cursorX < this._width && cursorY >= 0 && cursorY < this._height) {
|
|
678
|
-
const cursorFg = this.cursorColor ?? dfg
|
|
679
|
-
if (this.cursorStyleType === "line") {
|
|
680
|
-
// Line cursor: store position for renderer to build cursor overlay
|
|
681
|
-
this.lineCursorPosition = { x: cursorX, y: cursorY }
|
|
682
|
-
} else {
|
|
683
|
-
// Block cursor: overwrite cell with INVERSE
|
|
684
|
-
const idx = cursorY * this._width + cursorX
|
|
685
|
-
const charCode = this.char[idx]
|
|
686
|
-
const ch = charCode === 0 || charCode === 0x20 ? " " : String.fromCodePoint(charCode)
|
|
687
|
-
const offset = idx * 4
|
|
688
|
-
const cellBg = {
|
|
689
|
-
r: this.bg[offset],
|
|
690
|
-
g: this.bg[offset + 1],
|
|
691
|
-
b: this.bg[offset + 2],
|
|
692
|
-
a: this.bg[offset + 3] || 1,
|
|
693
|
-
} as RGBA
|
|
694
|
-
this.setCell(cursorX, cursorY, ch, cursorFg, cellBg, 32) // INVERSE = 1 << 5 = 32
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
drawSuperSampleBuffer(): void {}
|
|
700
|
-
drawPackedBuffer(): void {}
|
|
701
|
-
drawGrayscaleBuffer(): void {}
|
|
702
|
-
drawGrayscaleBufferSupersampled(): void {}
|
|
703
|
-
drawGrid(): void {}
|
|
704
|
-
encodeUnicode(_text: string): null {
|
|
705
|
-
return null
|
|
706
|
-
}
|
|
707
|
-
freeUnicode(): void {}
|
|
708
|
-
getRealCharBytes(): Uint8Array {
|
|
709
|
-
return new Uint8Array(0)
|
|
710
|
-
}
|
|
711
|
-
destroy(): void {}
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
// Export as OptimizedBuffer so opentui source code imports work directly
|
|
715
|
-
export { BrowserBuffer as OptimizedBuffer }
|