@gridland/web 0.2.15 → 0.2.17
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 +2 -2
- package/dist/index.js +70 -13
- package/dist/index.js.map +2 -2
- package/dist/{next-CxYMg6AG.d.ts → next-BWTklBmN.d.ts} +22 -3
- package/dist/next.d.ts +1 -1
- package/dist/next.js +70 -13
- package/dist/next.js.map +2 -2
- package/dist/vite-plugin.js +73 -38
- package/dist/vite-plugin.js.map +1 -1
- package/package.json +7 -2
- package/src/browser-buffer.ts +715 -0
- package/src/core-shims/index.ts +269 -0
- package/src/core-shims/renderable-types.ts +4 -0
- package/src/core-shims/rgba.ts +195 -0
- package/src/core-shims/types.ts +132 -0
- package/src/shims/bun-ffi-structs.ts +20 -0
- package/src/shims/bun-ffi.ts +28 -0
- package/src/shims/console-stub.ts +13 -0
- package/src/shims/console.ts +3 -0
- package/src/shims/devtools-polyfill-stub.ts +3 -0
- package/src/shims/edit-buffer-stub.ts +475 -0
- package/src/shims/editor-view-stub.ts +388 -0
- package/src/shims/events-shim.ts +81 -0
- package/src/shims/filters-stub.ts +4 -0
- package/src/shims/hast-stub.ts +8 -0
- package/src/shims/native-span-feed-stub.ts +7 -0
- package/src/shims/node-buffer.ts +39 -0
- package/src/shims/node-fs.ts +20 -0
- package/src/shims/node-os.ts +6 -0
- package/src/shims/node-path.ts +35 -0
- package/src/shims/node-stream.ts +10 -0
- package/src/shims/node-url.ts +8 -0
- package/src/shims/node-util.ts +33 -0
- package/src/shims/renderer-stub.ts +21 -0
- package/src/shims/slider-deps.ts +8 -0
- package/src/shims/syntax-style-shim.ts +23 -0
- package/src/shims/text-buffer-shim.ts +3 -0
- package/src/shims/text-buffer-view-shim.ts +2 -0
- package/src/shims/timeline-stub.ts +43 -0
- package/src/shims/tree-sitter-stub.ts +47 -0
- package/src/shims/tree-sitter-styled-text-stub.ts +8 -0
- package/src/shims/zig-stub.ts +20 -0
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
// Pure-JS EditorView implementation for browser environment.
|
|
2
|
+
// Wraps an EditBuffer and provides viewport/visual cursor mapping.
|
|
3
|
+
|
|
4
|
+
import type { EditBuffer, LogicalCursor } from "./edit-buffer-stub"
|
|
5
|
+
|
|
6
|
+
export interface Viewport {
|
|
7
|
+
offsetY: number
|
|
8
|
+
offsetX: number
|
|
9
|
+
height: number
|
|
10
|
+
width: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface VisualCursor {
|
|
14
|
+
visualRow: number
|
|
15
|
+
visualCol: number
|
|
16
|
+
logicalRow: number
|
|
17
|
+
logicalCol: number
|
|
18
|
+
offset: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface LineInfo {
|
|
22
|
+
lineIndex: number
|
|
23
|
+
lineCount: number
|
|
24
|
+
colIndex: number
|
|
25
|
+
colCount: number
|
|
26
|
+
byteIndex: number
|
|
27
|
+
byteCount: number
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class EditorView {
|
|
31
|
+
public readonly ptr: number = 0 // dummy pointer
|
|
32
|
+
|
|
33
|
+
private editBuffer: EditBuffer
|
|
34
|
+
private viewportWidth: number
|
|
35
|
+
private viewportHeight: number
|
|
36
|
+
private viewportOffsetX: number = 0
|
|
37
|
+
private viewportOffsetY: number = 0
|
|
38
|
+
private _destroyed: boolean = false
|
|
39
|
+
private _wrapMode: "none" | "char" | "word" = "word"
|
|
40
|
+
private _scrollMargin: number = 0
|
|
41
|
+
private _placeholderChunks: { text: string; fg?: any; bg?: any; attributes?: number }[] = []
|
|
42
|
+
|
|
43
|
+
// Selection state
|
|
44
|
+
private _selectionStart: number | null = null
|
|
45
|
+
private _selectionEnd: number | null = null
|
|
46
|
+
|
|
47
|
+
private _extmarksController: any = null
|
|
48
|
+
|
|
49
|
+
constructor(editBuffer: EditBuffer, width: number, height: number) {
|
|
50
|
+
this.editBuffer = editBuffer
|
|
51
|
+
this.viewportWidth = width
|
|
52
|
+
this.viewportHeight = height
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static create(editBuffer: EditBuffer, viewportWidth: number, viewportHeight: number): EditorView {
|
|
56
|
+
return new EditorView(editBuffer, viewportWidth, viewportHeight)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// --- Viewport ---
|
|
60
|
+
|
|
61
|
+
setViewportSize(width: number, height: number): void {
|
|
62
|
+
this.viewportWidth = width
|
|
63
|
+
this.viewportHeight = height
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
setViewport(x: number, y: number, width: number, height: number, _moveCursor: boolean = true): void {
|
|
67
|
+
this.viewportOffsetX = x
|
|
68
|
+
this.viewportOffsetY = y
|
|
69
|
+
this.viewportWidth = width
|
|
70
|
+
this.viewportHeight = height
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
getViewport(): Viewport {
|
|
74
|
+
return {
|
|
75
|
+
offsetX: this.viewportOffsetX,
|
|
76
|
+
offsetY: this.viewportOffsetY,
|
|
77
|
+
width: this.viewportWidth,
|
|
78
|
+
height: this.viewportHeight,
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
setScrollMargin(margin: number): void {
|
|
83
|
+
this._scrollMargin = margin
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
setWrapMode(mode: "none" | "char" | "word"): void {
|
|
87
|
+
this._wrapMode = mode
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// --- Line counts ---
|
|
91
|
+
|
|
92
|
+
getVirtualLineCount(): number {
|
|
93
|
+
// For no-wrap mode, virtual lines = logical lines
|
|
94
|
+
if (this._wrapMode === "none") {
|
|
95
|
+
return this.editBuffer.getLineCount()
|
|
96
|
+
}
|
|
97
|
+
// For wrap modes, calculate wrapped lines
|
|
98
|
+
return this.getTotalVirtualLineCount()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
getTotalVirtualLineCount(): number {
|
|
102
|
+
if (this._wrapMode === "none" || this.viewportWidth <= 0) {
|
|
103
|
+
return this.editBuffer.getLineCount()
|
|
104
|
+
}
|
|
105
|
+
const text = this.editBuffer.getText()
|
|
106
|
+
const lines = text.split("\n")
|
|
107
|
+
let total = 0
|
|
108
|
+
for (const line of lines) {
|
|
109
|
+
total += Math.max(1, Math.ceil(line.length / this.viewportWidth))
|
|
110
|
+
}
|
|
111
|
+
return total
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// --- Cursor ---
|
|
115
|
+
|
|
116
|
+
getCursor(): { row: number; col: number } {
|
|
117
|
+
const pos = this.editBuffer.getCursorPosition()
|
|
118
|
+
return { row: pos.row, col: pos.col }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
getVisualCursor(): VisualCursor {
|
|
122
|
+
const pos = this.editBuffer.getCursorPosition()
|
|
123
|
+
// For simple single-line / no-wrap, visual = logical minus viewport offset
|
|
124
|
+
const visualRow = pos.row - this.viewportOffsetY
|
|
125
|
+
const visualCol = pos.col - this.viewportOffsetX
|
|
126
|
+
return {
|
|
127
|
+
visualRow: Math.max(0, visualRow),
|
|
128
|
+
visualCol: Math.max(0, visualCol),
|
|
129
|
+
logicalRow: pos.row,
|
|
130
|
+
logicalCol: pos.col,
|
|
131
|
+
offset: pos.offset,
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
setCursorByOffset(offset: number): void {
|
|
136
|
+
this.editBuffer.setCursorByOffset(offset)
|
|
137
|
+
this.ensureCursorVisible()
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// --- Visual navigation ---
|
|
141
|
+
|
|
142
|
+
moveUpVisual(): void {
|
|
143
|
+
this.editBuffer.moveCursorUp()
|
|
144
|
+
this.ensureCursorVisible()
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
moveDownVisual(): void {
|
|
148
|
+
this.editBuffer.moveCursorDown()
|
|
149
|
+
this.ensureCursorVisible()
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
getVisualSOL(): VisualCursor {
|
|
153
|
+
const pos = this.editBuffer.getCursorPosition()
|
|
154
|
+
const offset = this.editBuffer.getLineStartOffset(pos.row)
|
|
155
|
+
return {
|
|
156
|
+
visualRow: pos.row - this.viewportOffsetY,
|
|
157
|
+
visualCol: 0,
|
|
158
|
+
logicalRow: pos.row,
|
|
159
|
+
logicalCol: 0,
|
|
160
|
+
offset,
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
getVisualEOL(): VisualCursor {
|
|
165
|
+
const eol = this.editBuffer.getEOL()
|
|
166
|
+
return {
|
|
167
|
+
visualRow: eol.row - this.viewportOffsetY,
|
|
168
|
+
visualCol: eol.col - this.viewportOffsetX,
|
|
169
|
+
logicalRow: eol.row,
|
|
170
|
+
logicalCol: eol.col,
|
|
171
|
+
offset: eol.offset,
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
getNextWordBoundary(): VisualCursor {
|
|
176
|
+
const lc = this.editBuffer.getNextWordBoundary()
|
|
177
|
+
return this.logicalToVisual(lc)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
getPrevWordBoundary(): VisualCursor {
|
|
181
|
+
const lc = this.editBuffer.getPrevWordBoundary()
|
|
182
|
+
return this.logicalToVisual(lc)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
getEOL(): VisualCursor {
|
|
186
|
+
const eol = this.editBuffer.getEOL()
|
|
187
|
+
return this.logicalToVisual(eol)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// --- Line info ---
|
|
191
|
+
|
|
192
|
+
getLineInfo(): LineInfo {
|
|
193
|
+
return this.getLogicalLineInfo()
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
getLogicalLineInfo(): LineInfo {
|
|
197
|
+
const pos = this.editBuffer.getCursorPosition()
|
|
198
|
+
const text = this.editBuffer.getText()
|
|
199
|
+
const lines = text.split("\n")
|
|
200
|
+
const line = lines[pos.row] || ""
|
|
201
|
+
return {
|
|
202
|
+
lineIndex: pos.row,
|
|
203
|
+
lineCount: lines.length,
|
|
204
|
+
colIndex: pos.col,
|
|
205
|
+
colCount: line.length,
|
|
206
|
+
byteIndex: pos.offset,
|
|
207
|
+
byteCount: new TextEncoder().encode(text).length,
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// --- Selection ---
|
|
212
|
+
|
|
213
|
+
setSelection(start: number, end: number, _bgColor?: any, _fgColor?: any): void {
|
|
214
|
+
this._selectionStart = start
|
|
215
|
+
this._selectionEnd = end
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
updateSelection(end: number, _bgColor?: any, _fgColor?: any): void {
|
|
219
|
+
this._selectionEnd = end
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
resetSelection(): void {
|
|
223
|
+
this._selectionStart = null
|
|
224
|
+
this._selectionEnd = null
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
getSelection(): { start: number; end: number } | null {
|
|
228
|
+
if (this._selectionStart === null || this._selectionEnd === null) return null
|
|
229
|
+
return { start: this._selectionStart, end: this._selectionEnd }
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
hasSelection(): boolean {
|
|
233
|
+
return this._selectionStart !== null && this._selectionEnd !== null
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
setLocalSelection(
|
|
237
|
+
_anchorX: number,
|
|
238
|
+
_anchorY: number,
|
|
239
|
+
_focusX: number,
|
|
240
|
+
_focusY: number,
|
|
241
|
+
_bgColor?: any,
|
|
242
|
+
_fgColor?: any,
|
|
243
|
+
_updateCursor?: boolean,
|
|
244
|
+
_followCursor?: boolean,
|
|
245
|
+
): boolean {
|
|
246
|
+
// Convert visual coords to offsets (simplified)
|
|
247
|
+
this._selectionStart = 0
|
|
248
|
+
this._selectionEnd = 0
|
|
249
|
+
return true
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
updateLocalSelection(
|
|
253
|
+
_anchorX: number,
|
|
254
|
+
_anchorY: number,
|
|
255
|
+
_focusX: number,
|
|
256
|
+
_focusY: number,
|
|
257
|
+
_bgColor?: any,
|
|
258
|
+
_fgColor?: any,
|
|
259
|
+
_updateCursor?: boolean,
|
|
260
|
+
_followCursor?: boolean,
|
|
261
|
+
): boolean {
|
|
262
|
+
return true
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
resetLocalSelection(): void {
|
|
266
|
+
this.resetSelection()
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
getSelectedText(): string {
|
|
270
|
+
const sel = this.getSelection()
|
|
271
|
+
if (!sel) return ""
|
|
272
|
+
const text = this.editBuffer.getText()
|
|
273
|
+
const start = Math.min(sel.start, sel.end)
|
|
274
|
+
const end = Math.max(sel.start, sel.end)
|
|
275
|
+
return text.substring(start, end)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
deleteSelectedText(): void {
|
|
279
|
+
const sel = this.getSelection()
|
|
280
|
+
if (!sel) return
|
|
281
|
+
const text = this.editBuffer.getText()
|
|
282
|
+
const start = Math.min(sel.start, sel.end)
|
|
283
|
+
const end = Math.max(sel.start, sel.end)
|
|
284
|
+
const newText = text.substring(0, start) + text.substring(end)
|
|
285
|
+
this.editBuffer.setText(newText)
|
|
286
|
+
this.editBuffer.setCursorByOffset(start)
|
|
287
|
+
this.resetSelection()
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// --- Text ---
|
|
291
|
+
|
|
292
|
+
getText(): string {
|
|
293
|
+
return this.editBuffer.getText()
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// --- Placeholder ---
|
|
297
|
+
|
|
298
|
+
setPlaceholderStyledText(chunks: { text: string; fg?: any; bg?: any; attributes?: number }[]): void {
|
|
299
|
+
this._placeholderChunks = chunks
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// --- Tab ---
|
|
303
|
+
|
|
304
|
+
setTabIndicator(_indicator: string | number): void {}
|
|
305
|
+
setTabIndicatorColor(_color: any): void {}
|
|
306
|
+
|
|
307
|
+
// --- Measurement ---
|
|
308
|
+
|
|
309
|
+
measureForDimensions(width: number, _height: number): { lineCount: number; maxWidth: number } | null {
|
|
310
|
+
const text = this.editBuffer.getText()
|
|
311
|
+
const lines = text.split("\n")
|
|
312
|
+
|
|
313
|
+
if (this._wrapMode === "none" || width <= 0) {
|
|
314
|
+
let maxWidth = 0
|
|
315
|
+
for (const line of lines) {
|
|
316
|
+
maxWidth = Math.max(maxWidth, line.length)
|
|
317
|
+
}
|
|
318
|
+
// If text is empty but we have placeholder, use that for measurement
|
|
319
|
+
if (text === "" && this._placeholderChunks.length > 0) {
|
|
320
|
+
let placeholderLen = 0
|
|
321
|
+
for (const chunk of this._placeholderChunks) {
|
|
322
|
+
placeholderLen += chunk.text.length
|
|
323
|
+
}
|
|
324
|
+
maxWidth = Math.max(maxWidth, placeholderLen)
|
|
325
|
+
}
|
|
326
|
+
return { lineCount: lines.length, maxWidth }
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
let totalLines = 0
|
|
330
|
+
let maxWidth = 0
|
|
331
|
+
for (const line of lines) {
|
|
332
|
+
const wrappedLines = Math.max(1, Math.ceil(line.length / width))
|
|
333
|
+
totalLines += wrappedLines
|
|
334
|
+
maxWidth = Math.max(maxWidth, Math.min(line.length, width))
|
|
335
|
+
}
|
|
336
|
+
return { lineCount: totalLines, maxWidth }
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// --- Extmarks ---
|
|
340
|
+
|
|
341
|
+
get extmarks(): any {
|
|
342
|
+
if (!this._extmarksController) {
|
|
343
|
+
this._extmarksController = {
|
|
344
|
+
destroy() {},
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return this._extmarksController
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// --- Cleanup ---
|
|
351
|
+
|
|
352
|
+
destroy(): void {
|
|
353
|
+
if (this._destroyed) return
|
|
354
|
+
this._destroyed = true
|
|
355
|
+
if (this._extmarksController && this._extmarksController.destroy) {
|
|
356
|
+
this._extmarksController.destroy()
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// --- Helpers ---
|
|
361
|
+
|
|
362
|
+
private logicalToVisual(lc: LogicalCursor): VisualCursor {
|
|
363
|
+
return {
|
|
364
|
+
visualRow: lc.row - this.viewportOffsetY,
|
|
365
|
+
visualCol: lc.col - this.viewportOffsetX,
|
|
366
|
+
logicalRow: lc.row,
|
|
367
|
+
logicalCol: lc.col,
|
|
368
|
+
offset: lc.offset,
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
private ensureCursorVisible(): void {
|
|
373
|
+
const pos = this.editBuffer.getCursorPosition()
|
|
374
|
+
// Auto-scroll viewport to keep cursor visible
|
|
375
|
+
if (pos.row < this.viewportOffsetY) {
|
|
376
|
+
this.viewportOffsetY = pos.row
|
|
377
|
+
} else if (pos.row >= this.viewportOffsetY + this.viewportHeight) {
|
|
378
|
+
this.viewportOffsetY = pos.row - this.viewportHeight + 1
|
|
379
|
+
}
|
|
380
|
+
if (this._wrapMode === "none") {
|
|
381
|
+
if (pos.col < this.viewportOffsetX) {
|
|
382
|
+
this.viewportOffsetX = pos.col
|
|
383
|
+
} else if (pos.col >= this.viewportOffsetX + this.viewportWidth) {
|
|
384
|
+
this.viewportOffsetX = pos.col - this.viewportWidth + 1
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Browser-compatible EventEmitter shim.
|
|
2
|
+
// Replaces the 'events' npm package to avoid CJS/ESM issues in Vite.
|
|
3
|
+
|
|
4
|
+
type Listener = (...args: any[]) => void
|
|
5
|
+
|
|
6
|
+
export class EventEmitter {
|
|
7
|
+
private _listeners = new Map<string | symbol, Listener[]>()
|
|
8
|
+
|
|
9
|
+
on(event: string | symbol, listener: Listener): this {
|
|
10
|
+
const list = this._listeners.get(event) ?? []
|
|
11
|
+
list.push(listener)
|
|
12
|
+
this._listeners.set(event, list)
|
|
13
|
+
return this
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
addListener(event: string | symbol, listener: Listener): this {
|
|
17
|
+
return this.on(event, listener)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
off(event: string | symbol, listener: Listener): this {
|
|
21
|
+
return this.removeListener(event, listener)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
removeListener(event: string | symbol, listener: Listener): this {
|
|
25
|
+
const list = this._listeners.get(event)
|
|
26
|
+
if (list) {
|
|
27
|
+
const idx = list.indexOf(listener)
|
|
28
|
+
if (idx !== -1) list.splice(idx, 1)
|
|
29
|
+
if (list.length === 0) this._listeners.delete(event)
|
|
30
|
+
}
|
|
31
|
+
return this
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
removeAllListeners(event?: string | symbol): this {
|
|
35
|
+
if (event) {
|
|
36
|
+
this._listeners.delete(event)
|
|
37
|
+
} else {
|
|
38
|
+
this._listeners.clear()
|
|
39
|
+
}
|
|
40
|
+
return this
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
emit(event: string | symbol, ...args: any[]): boolean {
|
|
44
|
+
const list = this._listeners.get(event)
|
|
45
|
+
if (!list || list.length === 0) return false
|
|
46
|
+
for (const listener of [...list]) {
|
|
47
|
+
listener(...args)
|
|
48
|
+
}
|
|
49
|
+
return true
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
once(event: string | symbol, listener: Listener): this {
|
|
53
|
+
const wrapper = (...args: any[]) => {
|
|
54
|
+
this.removeListener(event, wrapper)
|
|
55
|
+
listener(...args)
|
|
56
|
+
}
|
|
57
|
+
return this.on(event, wrapper)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
listenerCount(event: string | symbol): number {
|
|
61
|
+
return this._listeners.get(event)?.length ?? 0
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
listeners(event: string | symbol): Listener[] {
|
|
65
|
+
return [...(this._listeners.get(event) ?? [])]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
eventNames(): (string | symbol)[] {
|
|
69
|
+
return [...this._listeners.keys()]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
setMaxListeners(_n: number): this {
|
|
73
|
+
return this // no-op in browser
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
getMaxListeners(): number {
|
|
77
|
+
return Infinity
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export default EventEmitter
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Browser shim for node:buffer
|
|
2
|
+
// TextEncoder/TextDecoder are available in browsers
|
|
3
|
+
const BrowserBuffer = {
|
|
4
|
+
from(data: string | ArrayBuffer | Uint8Array, encoding?: string): Uint8Array {
|
|
5
|
+
if (typeof data === "string") {
|
|
6
|
+
const encoder = new TextEncoder()
|
|
7
|
+
return encoder.encode(data)
|
|
8
|
+
}
|
|
9
|
+
if (data instanceof ArrayBuffer) {
|
|
10
|
+
return new Uint8Array(data)
|
|
11
|
+
}
|
|
12
|
+
if (data instanceof Uint8Array) {
|
|
13
|
+
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
|
|
14
|
+
}
|
|
15
|
+
return new Uint8Array(0)
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
alloc(size: number): Uint8Array {
|
|
19
|
+
return new Uint8Array(size)
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
isBuffer(obj: any): boolean {
|
|
23
|
+
return obj instanceof Uint8Array
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
concat(list: Uint8Array[], totalLength?: number): Uint8Array {
|
|
27
|
+
const length = totalLength ?? list.reduce((acc, buf) => acc + buf.byteLength, 0)
|
|
28
|
+
const result = new Uint8Array(length)
|
|
29
|
+
let offset = 0
|
|
30
|
+
for (const buf of list) {
|
|
31
|
+
result.set(buf, offset)
|
|
32
|
+
offset += buf.byteLength
|
|
33
|
+
}
|
|
34
|
+
return result
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const Buffer = BrowserBuffer
|
|
39
|
+
export default { Buffer: BrowserBuffer }
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Browser stub for node:fs and fs/promises
|
|
2
|
+
export function existsSync(_path: string): boolean { return false }
|
|
3
|
+
export function readFileSync(_path: string, _encoding?: string): string { return "" }
|
|
4
|
+
export function writeFileSync(): void {}
|
|
5
|
+
export function mkdirSync(): void {}
|
|
6
|
+
export function readdirSync(): string[] { return [] }
|
|
7
|
+
export function statSync(): any { return { isDirectory: () => false, isFile: () => false } }
|
|
8
|
+
export function unlinkSync(): void {}
|
|
9
|
+
|
|
10
|
+
// fs/promises
|
|
11
|
+
export async function readFile(): Promise<string> { return "" }
|
|
12
|
+
export async function writeFile(): Promise<void> {}
|
|
13
|
+
export async function mkdir(): Promise<void> {}
|
|
14
|
+
export async function readdir(): Promise<string[]> { return [] }
|
|
15
|
+
export async function stat(): Promise<any> { return { isDirectory: () => false, isFile: () => false } }
|
|
16
|
+
|
|
17
|
+
export default {
|
|
18
|
+
existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, statSync, unlinkSync,
|
|
19
|
+
promises: { readFile, writeFile, mkdir, readdir, stat },
|
|
20
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Browser stub for node:os
|
|
2
|
+
export function homedir(): string { return "/home/user" }
|
|
3
|
+
export function tmpdir(): string { return "/tmp" }
|
|
4
|
+
export function platform(): string { return "browser" }
|
|
5
|
+
export function arch(): string { return "wasm" }
|
|
6
|
+
export default { homedir, tmpdir, platform, arch }
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Browser shim for node:path
|
|
2
|
+
export function join(...parts: string[]): string {
|
|
3
|
+
return parts.join("/").replace(/\/+/g, "/")
|
|
4
|
+
}
|
|
5
|
+
export function resolve(...parts: string[]): string {
|
|
6
|
+
return join(...parts)
|
|
7
|
+
}
|
|
8
|
+
export function dirname(p: string): string {
|
|
9
|
+
return p.split("/").slice(0, -1).join("/") || "/"
|
|
10
|
+
}
|
|
11
|
+
export function basename(p: string, ext?: string): string {
|
|
12
|
+
const base = p.split("/").pop() || ""
|
|
13
|
+
if (ext && base.endsWith(ext)) return base.slice(0, -ext.length)
|
|
14
|
+
return base
|
|
15
|
+
}
|
|
16
|
+
export function extname(p: string): string {
|
|
17
|
+
const base = basename(p)
|
|
18
|
+
const idx = base.lastIndexOf(".")
|
|
19
|
+
return idx >= 0 ? base.slice(idx) : ""
|
|
20
|
+
}
|
|
21
|
+
export function parse(p: string): { root: string; dir: string; base: string; ext: string; name: string } {
|
|
22
|
+
const dir = dirname(p)
|
|
23
|
+
const base = basename(p)
|
|
24
|
+
const ext = extname(p)
|
|
25
|
+
const name = ext ? base.slice(0, -ext.length) : base
|
|
26
|
+
return { root: p.startsWith("/") ? "/" : "", dir, base, ext, name }
|
|
27
|
+
}
|
|
28
|
+
export function isAbsolute(p: string): boolean {
|
|
29
|
+
return p.startsWith("/")
|
|
30
|
+
}
|
|
31
|
+
export function relative(from: string, to: string): string {
|
|
32
|
+
return to.replace(from, "").replace(/^\//, "")
|
|
33
|
+
}
|
|
34
|
+
export const sep = "/"
|
|
35
|
+
export default { join, resolve, dirname, basename, extname, parse, isAbsolute, relative, sep }
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Browser stub for node:stream
|
|
2
|
+
export class Writable {
|
|
3
|
+
write(_chunk: any): boolean { return true }
|
|
4
|
+
end(): void {}
|
|
5
|
+
}
|
|
6
|
+
export class Readable {
|
|
7
|
+
read(): any { return null }
|
|
8
|
+
}
|
|
9
|
+
export class Transform extends Writable {}
|
|
10
|
+
export default { Writable, Readable, Transform }
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Browser stub for node:util
|
|
2
|
+
export function inspect(obj: any, _options?: any): string {
|
|
3
|
+
try {
|
|
4
|
+
return JSON.stringify(obj, null, 2)
|
|
5
|
+
} catch {
|
|
6
|
+
return String(obj)
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function format(fmt: string, ...args: any[]): string {
|
|
11
|
+
let i = 0
|
|
12
|
+
return fmt.replace(/%[sdjifoO%]/g, (match) => {
|
|
13
|
+
if (match === "%%") return "%"
|
|
14
|
+
if (i >= args.length) return match
|
|
15
|
+
return String(args[i++])
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function promisify(fn: Function): Function {
|
|
20
|
+
return (...args: any[]) =>
|
|
21
|
+
new Promise((resolve, reject) => {
|
|
22
|
+
fn(...args, (err: any, result: any) => {
|
|
23
|
+
if (err) reject(err)
|
|
24
|
+
else resolve(result)
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function isDeepStrictEqual(a: any, b: any): boolean {
|
|
30
|
+
return JSON.stringify(a) === JSON.stringify(b)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default { inspect, format, promisify, isDeepStrictEqual }
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Stub for opentui/packages/core/src/renderer.ts
|
|
2
|
+
import { EventEmitter } from "events"
|
|
3
|
+
|
|
4
|
+
export enum CliRenderEvents {
|
|
5
|
+
DESTROY = "destroy",
|
|
6
|
+
DEBUG_OVERLAY_TOGGLE = "debug_overlay_toggle",
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class CliRenderer extends EventEmitter {
|
|
10
|
+
root: any = null
|
|
11
|
+
keyInput: any = new EventEmitter()
|
|
12
|
+
destroy(): void {
|
|
13
|
+
this.emit(CliRenderEvents.DESTROY)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type MouseEvent = any
|
|
18
|
+
|
|
19
|
+
export function createCliRenderer(): Promise<CliRenderer> {
|
|
20
|
+
return Promise.resolve(new CliRenderer())
|
|
21
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Minimal barrel for Slider.ts that avoids the circular dependency.
|
|
2
|
+
// Slider.ts imports { OptimizedBuffer, parseColor, Renderable, RGBA, RenderableOptions, RenderContext } from "../index"
|
|
3
|
+
// We provide these directly from the source files, bypassing any barrel that re-exports renderables.
|
|
4
|
+
|
|
5
|
+
export { Renderable, type RenderableOptions } from "../../../../opentui/packages/core/src/Renderable"
|
|
6
|
+
export type { RenderContext } from "../core-shims/types"
|
|
7
|
+
export { BrowserBuffer as OptimizedBuffer } from "../browser-buffer"
|
|
8
|
+
export { RGBA, parseColor, type ColorInput } from "../core-shims/rgba"
|