@opentui/solid 0.0.0-20250908-4906ddad → 0.0.0-20250912-12c969f4
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 +61 -0
- package/index.js +81 -261
- package/jsx-runtime.d.ts +7 -2
- package/package.json +2 -2
- package/scripts/solid-plugin.ts +6 -0
- package/src/elements/index.d.ts +6 -1
- package/src/reconciler.d.ts +3 -4
- package/src/types/elements.d.ts +12 -6
- package/src/elements/text-node.d.ts +0 -48
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export function useTimeline(timeline: any, initialValue: any, targetValue: any, options: any, startTime?: number): import("solid-js").Accessor<any>;
|
|
2
|
+
export function useTerminalDimensions(): import("solid-js").Accessor<{
|
|
3
|
+
width: any;
|
|
4
|
+
height: any;
|
|
5
|
+
}>;
|
|
6
|
+
export function useSelectionHandler(callback: any): void;
|
|
7
|
+
export function useRenderer(): any;
|
|
8
|
+
export function useKeyboard(callback: any): void;
|
|
9
|
+
export function useKeyHandler(callback: any): void;
|
|
10
|
+
export var use: <A, T>(fn: (element: any, arg: A) => T, element: any, arg: A) => T;
|
|
11
|
+
export var spread: <T>(node: any, accessor: (() => T) | T, skipChildren?: boolean) => void;
|
|
12
|
+
export var setProp: <T>(node: any, name: string, value: T, prev?: T | undefined) => T;
|
|
13
|
+
export function render(node: any, renderConfig?: {}): Promise<void>;
|
|
14
|
+
export function onResize(callback: any): void;
|
|
15
|
+
export var mergeProps: (...sources: unknown[]) => unknown;
|
|
16
|
+
export var memo: <T>(fn: () => T, equal: boolean) => () => T;
|
|
17
|
+
export var insertNode: (parent: any, node: any, anchor?: any) => void;
|
|
18
|
+
export var insert: <T>(parent: any, accessor: T | (() => T), marker?: any | null, initial?: any) => any;
|
|
19
|
+
export function getComponentCatalogue(): {
|
|
20
|
+
box: typeof BoxRenderable;
|
|
21
|
+
text: typeof TextRenderable;
|
|
22
|
+
input: typeof InputRenderable;
|
|
23
|
+
select: typeof SelectRenderable;
|
|
24
|
+
ascii_font: typeof ASCIIFontRenderable;
|
|
25
|
+
tab_select: typeof TabSelectRenderable;
|
|
26
|
+
scrollbox: typeof ScrollBoxRenderable;
|
|
27
|
+
span: typeof SpanRenderable;
|
|
28
|
+
};
|
|
29
|
+
export function extend(objects: any): void;
|
|
30
|
+
export var effect: <T>(fn: (prev?: T) => T, init?: T) => void;
|
|
31
|
+
export var createTextNode: (value: string) => any;
|
|
32
|
+
export var createElement: (tag: string) => any;
|
|
33
|
+
export function createComponentTimeline(options?: {}): Timeline;
|
|
34
|
+
export var createComponent: <T>(Comp: (props: T) => any, props: T) => any;
|
|
35
|
+
export namespace componentCatalogue {
|
|
36
|
+
export { BoxRenderable as box };
|
|
37
|
+
export { TextRenderable as text };
|
|
38
|
+
export { InputRenderable as input };
|
|
39
|
+
export { SelectRenderable as select };
|
|
40
|
+
export { ASCIIFontRenderable as ascii_font };
|
|
41
|
+
export { TabSelectRenderable as tab_select };
|
|
42
|
+
export { ScrollBoxRenderable as scrollbox };
|
|
43
|
+
export { SpanRenderable as span };
|
|
44
|
+
}
|
|
45
|
+
export namespace baseComponents { }
|
|
46
|
+
export var _render: (code: () => any, node: any) => () => void;
|
|
47
|
+
export var RendererContext: import("solid-js").Context<any>;
|
|
48
|
+
import { BoxRenderable } from "@opentui/core";
|
|
49
|
+
import { TextRenderable } from "@opentui/core";
|
|
50
|
+
import { InputRenderable } from "@opentui/core";
|
|
51
|
+
import { SelectRenderable } from "@opentui/core";
|
|
52
|
+
import { ASCIIFontRenderable } from "@opentui/core";
|
|
53
|
+
import { TabSelectRenderable } from "@opentui/core";
|
|
54
|
+
import { ScrollBoxRenderable } from "@opentui/core";
|
|
55
|
+
declare class SpanRenderable extends TextNodeRenderable {
|
|
56
|
+
constructor(_ctx: any, options: any);
|
|
57
|
+
_ctx: any;
|
|
58
|
+
}
|
|
59
|
+
import { Timeline } from "@opentui/core";
|
|
60
|
+
import { TextNodeRenderable } from "@opentui/core";
|
|
61
|
+
export {};
|
package/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
ScrollBoxRenderable,
|
|
11
11
|
SelectRenderable,
|
|
12
12
|
TabSelectRenderable,
|
|
13
|
+
TextNodeRenderable,
|
|
13
14
|
TextRenderable
|
|
14
15
|
} from "@opentui/core";
|
|
15
16
|
|
|
@@ -95,6 +96,13 @@ var useTimeline = (timeline, initialValue, targetValue, options, startTime = 0)
|
|
|
95
96
|
};
|
|
96
97
|
|
|
97
98
|
// src/elements/index.ts
|
|
99
|
+
class SpanRenderable extends TextNodeRenderable {
|
|
100
|
+
_ctx;
|
|
101
|
+
constructor(_ctx, options) {
|
|
102
|
+
super(options);
|
|
103
|
+
this._ctx = _ctx;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
98
106
|
var baseComponents = {
|
|
99
107
|
box: BoxRenderable,
|
|
100
108
|
text: TextRenderable,
|
|
@@ -102,7 +110,8 @@ var baseComponents = {
|
|
|
102
110
|
select: SelectRenderable,
|
|
103
111
|
ascii_font: ASCIIFontRenderable,
|
|
104
112
|
tab_select: TabSelectRenderable,
|
|
105
|
-
scrollbox: ScrollBoxRenderable
|
|
113
|
+
scrollbox: ScrollBoxRenderable,
|
|
114
|
+
span: SpanRenderable
|
|
106
115
|
};
|
|
107
116
|
var componentCatalogue = { ...baseComponents };
|
|
108
117
|
function extend(objects) {
|
|
@@ -114,22 +123,24 @@ function getComponentCatalogue() {
|
|
|
114
123
|
|
|
115
124
|
// src/reconciler.ts
|
|
116
125
|
import {
|
|
126
|
+
BaseRenderable,
|
|
127
|
+
createTextAttributes,
|
|
117
128
|
InputRenderable as InputRenderable2,
|
|
118
129
|
InputRenderableEvents,
|
|
119
|
-
|
|
130
|
+
isTextNodeRenderable,
|
|
131
|
+
parseColor,
|
|
132
|
+
Renderable,
|
|
133
|
+
RootTextNodeRenderable,
|
|
120
134
|
SelectRenderable as SelectRenderable2,
|
|
121
135
|
SelectRenderableEvents,
|
|
122
|
-
StyledText,
|
|
123
136
|
TabSelectRenderable as TabSelectRenderable2,
|
|
124
137
|
TabSelectRenderableEvents,
|
|
125
|
-
|
|
138
|
+
TextNodeRenderable as TextNodeRenderable2,
|
|
139
|
+
TextRenderable as TextRenderable2
|
|
126
140
|
} from "@opentui/core";
|
|
127
141
|
import { useContext as useContext2 } from "solid-js";
|
|
128
142
|
import { createRenderer } from "solid-js/universal";
|
|
129
143
|
|
|
130
|
-
// src/elements/text-node.ts
|
|
131
|
-
import { Renderable, TextRenderable as TextRenderable2 } from "@opentui/core";
|
|
132
|
-
|
|
133
144
|
// src/utils/id-counter.ts
|
|
134
145
|
var idCounter = new Map;
|
|
135
146
|
function getNextId(elementType) {
|
|
@@ -148,216 +159,76 @@ var log = (...args) => {
|
|
|
148
159
|
}
|
|
149
160
|
};
|
|
150
161
|
|
|
151
|
-
// src/
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
class TextNode {
|
|
159
|
-
id;
|
|
160
|
-
chunk;
|
|
161
|
-
parent;
|
|
162
|
-
textParent;
|
|
163
|
-
constructor(chunk) {
|
|
164
|
-
this.id = getNextId("text-node");
|
|
165
|
-
this.chunk = chunk;
|
|
166
|
-
ChunkToTextNodeMap.set(chunk, this);
|
|
167
|
-
}
|
|
168
|
-
replaceText(newChunk) {
|
|
169
|
-
const textParent = this.textParent;
|
|
170
|
-
if (!textParent) {
|
|
171
|
-
log("No parent found for text node:", this.id);
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
textParent.content = textParent.content.replace(newChunk, this.chunk);
|
|
175
|
-
this.chunk = newChunk;
|
|
176
|
-
ChunkToTextNodeMap.set(newChunk, this);
|
|
177
|
-
}
|
|
178
|
-
static getTextNodeFromChunk(chunk) {
|
|
179
|
-
return ChunkToTextNodeMap.get(chunk);
|
|
180
|
-
}
|
|
181
|
-
insert(parent, anchor) {
|
|
182
|
-
if (!(parent instanceof Renderable)) {
|
|
183
|
-
log("Attaching text node to parent text node, impossible");
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
let textParent;
|
|
187
|
-
if (!(parent instanceof TextRenderable2)) {
|
|
188
|
-
textParent = this.getOrCreateTextGhostNode(parent, anchor);
|
|
189
|
-
} else {
|
|
190
|
-
textParent = parent;
|
|
191
|
-
}
|
|
192
|
-
this.textParent = textParent;
|
|
193
|
-
let styledText = textParent.content;
|
|
194
|
-
if (anchor) {
|
|
195
|
-
if (anchor instanceof Renderable) {
|
|
196
|
-
console.warn("text node can't be anchored to Renderable");
|
|
197
|
-
return;
|
|
198
|
-
} else if (isTextChunk(anchor)) {
|
|
199
|
-
anchor = ChunkToTextNodeMap.get(anchor);
|
|
200
|
-
}
|
|
201
|
-
const anchorIndex = anchor ? styledText.chunks.indexOf(anchor.chunk) : -1;
|
|
202
|
-
if (anchorIndex === -1) {
|
|
203
|
-
log("anchor not found");
|
|
204
|
-
styledText = styledText.insert(this.chunk);
|
|
205
|
-
} else {
|
|
206
|
-
styledText = styledText.insert(this.chunk, anchorIndex);
|
|
207
|
-
}
|
|
208
|
-
} else {
|
|
209
|
-
const firstChunk = textParent.content.chunks[0];
|
|
210
|
-
if (firstChunk && !ChunkToTextNodeMap.has(firstChunk)) {
|
|
211
|
-
styledText = styledText.replace(this.chunk, firstChunk);
|
|
212
|
-
} else {
|
|
213
|
-
styledText = styledText.insert(this.chunk);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
textParent.content = styledText;
|
|
217
|
-
textParent.visible = textParent.textLength !== 0;
|
|
218
|
-
this.parent = parent;
|
|
219
|
-
}
|
|
220
|
-
remove(parent) {
|
|
221
|
-
if (!(parent instanceof Renderable)) {
|
|
222
|
-
ChunkToTextNodeMap.delete(this.chunk);
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
if (parent === this.textParent && parent instanceof TextRenderable2) {
|
|
226
|
-
ChunkToTextNodeMap.delete(this.chunk);
|
|
227
|
-
parent.content = parent.content.remove(this.chunk);
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
if (this.textParent) {
|
|
231
|
-
ChunkToTextNodeMap.delete(this.chunk);
|
|
232
|
-
let styledText = this.textParent.content;
|
|
233
|
-
styledText = styledText.remove(this.chunk);
|
|
234
|
-
if (styledText.chunks.length > 0) {
|
|
235
|
-
this.textParent.content = styledText;
|
|
236
|
-
} else {
|
|
237
|
-
this.parent?.remove(this.textParent.id);
|
|
238
|
-
process.nextTick(() => {
|
|
239
|
-
if (!this.textParent)
|
|
240
|
-
return;
|
|
241
|
-
if (!this.textParent.parent) {
|
|
242
|
-
this.textParent.destroyRecursively();
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
getOrCreateTextGhostNode(parent, anchor) {
|
|
249
|
-
if (anchor instanceof TextNode && anchor.textParent) {
|
|
250
|
-
return anchor.textParent;
|
|
251
|
-
}
|
|
252
|
-
const children = parent.getChildren();
|
|
253
|
-
if (anchor instanceof Renderable) {
|
|
254
|
-
const anchorIndex = children.findIndex((el) => el.id === anchor.id);
|
|
255
|
-
const beforeAnchor = children[anchorIndex - 1];
|
|
256
|
-
if (beforeAnchor instanceof GhostTextRenderable) {
|
|
257
|
-
return beforeAnchor;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
const lastChild = children.at(-1);
|
|
261
|
-
if (lastChild instanceof GhostTextRenderable) {
|
|
262
|
-
return lastChild;
|
|
263
|
-
}
|
|
264
|
-
const ghostNode = new GhostTextRenderable(parent.ctx, {
|
|
265
|
-
id: getNextId(GHOST_NODE_TAG)
|
|
266
|
-
});
|
|
267
|
-
insertNode(parent, ghostNode, anchor);
|
|
268
|
-
return ghostNode;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
class GhostTextRenderable extends TextRenderable2 {
|
|
273
|
-
constructor(ctx, options) {
|
|
274
|
-
super(ctx, options);
|
|
275
|
-
}
|
|
276
|
-
static isGhostNode(node) {
|
|
277
|
-
return node instanceof GhostTextRenderable;
|
|
162
|
+
// src/reconciler.ts
|
|
163
|
+
class TextNode extends TextNodeRenderable2 {
|
|
164
|
+
static fromString(text, options = {}) {
|
|
165
|
+
const node = new TextNode(options);
|
|
166
|
+
node.add(text);
|
|
167
|
+
return node;
|
|
278
168
|
}
|
|
279
169
|
}
|
|
280
|
-
|
|
281
|
-
// src/reconciler.ts
|
|
282
170
|
var logId = (node) => {
|
|
283
171
|
if (!node)
|
|
284
172
|
return;
|
|
285
|
-
if (isTextChunk(node)) {
|
|
286
|
-
return node.plainText;
|
|
287
|
-
}
|
|
288
173
|
return node.id;
|
|
289
174
|
};
|
|
175
|
+
var getNodeChildren = (node) => {
|
|
176
|
+
let children;
|
|
177
|
+
if (node instanceof TextRenderable2) {
|
|
178
|
+
children = node.getTextChildren();
|
|
179
|
+
} else {
|
|
180
|
+
children = node.getChildren();
|
|
181
|
+
}
|
|
182
|
+
return children;
|
|
183
|
+
};
|
|
290
184
|
function _insertNode(parent, node, anchor) {
|
|
291
185
|
log("Inserting node:", logId(node), "into parent:", logId(parent), "with anchor:", logId(anchor), node instanceof TextNode);
|
|
292
|
-
if (node
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
_insertNode(parent, _createTextNode(chunk), anchor);
|
|
186
|
+
if (isTextNodeRenderable(node)) {
|
|
187
|
+
if (!(parent instanceof TextRenderable2) && !isTextNodeRenderable(parent)) {
|
|
188
|
+
log(`Text must have a <text> as a parent: ${parent.id} above ${node.id}`);
|
|
296
189
|
return;
|
|
297
190
|
}
|
|
298
191
|
}
|
|
299
|
-
if (
|
|
300
|
-
|
|
192
|
+
if (!(parent instanceof BaseRenderable)) {
|
|
193
|
+
log("[INSERT]", "Tried to mount a non base renderable");
|
|
301
194
|
return;
|
|
302
195
|
}
|
|
303
|
-
if (
|
|
304
|
-
|
|
305
|
-
}
|
|
306
|
-
if (!(parent instanceof Renderable2)) {
|
|
196
|
+
if (!anchor) {
|
|
197
|
+
parent.add(node);
|
|
307
198
|
return;
|
|
308
199
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
const anchorIndex = parent.getChildren().findIndex((el) => {
|
|
315
|
-
if (anchor instanceof TextNode) {
|
|
316
|
-
return el.id === anchor.textParent?.id;
|
|
317
|
-
}
|
|
318
|
-
return el.id === anchor.id;
|
|
319
|
-
});
|
|
320
|
-
parent.add(node, anchorIndex);
|
|
321
|
-
} else {
|
|
322
|
-
parent.add(node);
|
|
200
|
+
const children = getNodeChildren(parent);
|
|
201
|
+
const anchorIndex = children.findIndex((el) => el.id === anchor.id);
|
|
202
|
+
if (anchorIndex === -1) {
|
|
203
|
+
log("[INSERT]", "Could not find anchor", logId(parent), logId(anchor), "[children]", ...children.map((c) => c.id));
|
|
323
204
|
}
|
|
205
|
+
parent.add(node, anchorIndex);
|
|
324
206
|
}
|
|
325
207
|
function _removeNode(parent, node) {
|
|
326
208
|
log("Removing node:", logId(node), "from parent:", logId(parent));
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
if (
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
} else if (node instanceof StyledText) {
|
|
333
|
-
for (const chunk of node.chunks) {
|
|
334
|
-
const textNode = TextNode.getTextNodeFromChunk(chunk);
|
|
335
|
-
if (!textNode)
|
|
336
|
-
continue;
|
|
337
|
-
_removeNode(parent, textNode);
|
|
209
|
+
parent.remove(node.id);
|
|
210
|
+
process.nextTick(() => {
|
|
211
|
+
if (node instanceof Renderable && !node.parent) {
|
|
212
|
+
node.destroyRecursively();
|
|
213
|
+
return;
|
|
338
214
|
}
|
|
339
|
-
}
|
|
340
|
-
if (node instanceof TextNode) {
|
|
341
|
-
return node.remove(parent);
|
|
342
|
-
}
|
|
343
|
-
if (parent instanceof Renderable2 && node instanceof Renderable2) {
|
|
344
|
-
parent.remove(node.id);
|
|
345
|
-
process.nextTick(() => {
|
|
346
|
-
if (!node.parent) {
|
|
347
|
-
node.destroyRecursively();
|
|
348
|
-
}
|
|
349
|
-
});
|
|
350
|
-
}
|
|
215
|
+
});
|
|
351
216
|
}
|
|
352
217
|
function _createTextNode(value) {
|
|
353
218
|
log("Creating text node:", value);
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
|
|
219
|
+
const id = getNextId("text-node");
|
|
220
|
+
if (typeof value === "number") {
|
|
221
|
+
value = value.toString();
|
|
222
|
+
}
|
|
223
|
+
return TextNode.fromString(value, { id });
|
|
224
|
+
}
|
|
225
|
+
function _getParentNode(childNode) {
|
|
226
|
+
log("Getting parent of node:", logId(childNode));
|
|
227
|
+
let parent = childNode.parent ?? undefined;
|
|
228
|
+
if (parent instanceof RootTextNodeRenderable) {
|
|
229
|
+
parent = parent.textParent ?? undefined;
|
|
230
|
+
}
|
|
231
|
+
return parent;
|
|
361
232
|
}
|
|
362
233
|
var {
|
|
363
234
|
render: _render,
|
|
@@ -391,24 +262,11 @@ var {
|
|
|
391
262
|
createTextNode: _createTextNode,
|
|
392
263
|
replaceText(textNode, value) {
|
|
393
264
|
log("Replacing text:", value, "in node:", logId(textNode));
|
|
394
|
-
if (textNode instanceof
|
|
265
|
+
if (!(textNode instanceof TextNode))
|
|
395
266
|
return;
|
|
396
|
-
|
|
397
|
-
console.warn("Cannot replace text on text chunk", logId(textNode));
|
|
398
|
-
return;
|
|
399
|
-
}
|
|
400
|
-
const newChunk = {
|
|
401
|
-
__isChunk: true,
|
|
402
|
-
text: new TextEncoder().encode(value),
|
|
403
|
-
plainText: value
|
|
404
|
-
};
|
|
405
|
-
textNode.replaceText(newChunk);
|
|
267
|
+
textNode.replace(value, 0);
|
|
406
268
|
},
|
|
407
269
|
setProperty(node, name, value, prev) {
|
|
408
|
-
if (node instanceof TextNode || isTextChunk(node)) {
|
|
409
|
-
console.warn("Cannot set property on text node:", logId(node));
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
270
|
if (name.startsWith("on:")) {
|
|
413
271
|
const eventName = name.slice(3);
|
|
414
272
|
if (value) {
|
|
@@ -419,8 +277,19 @@ var {
|
|
|
419
277
|
}
|
|
420
278
|
return;
|
|
421
279
|
}
|
|
280
|
+
if (isTextNodeRenderable(node)) {
|
|
281
|
+
if (name !== "style") {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
node.attributes |= createTextAttributes(value);
|
|
285
|
+
node.fg = value.fg ? parseColor(value.fg) : node.fg;
|
|
286
|
+
node.bg = value.bg ? parseColor(value.bg) : node.bg;
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
422
289
|
switch (name) {
|
|
423
290
|
case "focused":
|
|
291
|
+
if (!(node instanceof Renderable))
|
|
292
|
+
return;
|
|
424
293
|
if (value) {
|
|
425
294
|
node.focus();
|
|
426
295
|
} else {
|
|
@@ -503,37 +372,10 @@ var {
|
|
|
503
372
|
},
|
|
504
373
|
insertNode: _insertNode,
|
|
505
374
|
removeNode: _removeNode,
|
|
506
|
-
getParentNode
|
|
507
|
-
log("Getting parent of node:", logId(childNode));
|
|
508
|
-
let node = childNode;
|
|
509
|
-
if (isTextChunk(childNode)) {
|
|
510
|
-
const parentTextNode = TextNode.getTextNodeFromChunk(childNode);
|
|
511
|
-
if (!parentTextNode)
|
|
512
|
-
return;
|
|
513
|
-
node = parentTextNode;
|
|
514
|
-
}
|
|
515
|
-
const parent = node.parent;
|
|
516
|
-
if (!parent) {
|
|
517
|
-
log("No parent found for node:", logId(node));
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
520
|
-
log("Parent found:", logId(parent), "for node:", logId(node));
|
|
521
|
-
return parent;
|
|
522
|
-
},
|
|
375
|
+
getParentNode: _getParentNode,
|
|
523
376
|
getFirstChild(node) {
|
|
524
377
|
log("Getting first child of node:", logId(node));
|
|
525
|
-
|
|
526
|
-
const chunk = node.content.chunks[0];
|
|
527
|
-
if (chunk) {
|
|
528
|
-
return TextNode.getTextNodeFromChunk(chunk);
|
|
529
|
-
} else {
|
|
530
|
-
return;
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
if (node instanceof TextNode || isTextChunk(node)) {
|
|
534
|
-
return;
|
|
535
|
-
}
|
|
536
|
-
const firstChild = node.getChildren()[0];
|
|
378
|
+
const firstChild = getNodeChildren(node)[0];
|
|
537
379
|
if (!firstChild) {
|
|
538
380
|
log("No first child found for node:", logId(node));
|
|
539
381
|
return;
|
|
@@ -543,34 +385,12 @@ var {
|
|
|
543
385
|
},
|
|
544
386
|
getNextSibling(node) {
|
|
545
387
|
log("Getting next sibling of node:", logId(node));
|
|
546
|
-
|
|
547
|
-
console.warn("Cannot get next sibling of text chunk");
|
|
548
|
-
return;
|
|
549
|
-
}
|
|
550
|
-
const parent = node.parent;
|
|
388
|
+
const parent = _getParentNode(node);
|
|
551
389
|
if (!parent) {
|
|
552
390
|
log("No parent found for node:", logId(node));
|
|
553
391
|
return;
|
|
554
392
|
}
|
|
555
|
-
|
|
556
|
-
if (parent instanceof TextRenderable3) {
|
|
557
|
-
const siblings2 = parent.content.chunks;
|
|
558
|
-
const index2 = siblings2.indexOf(node.chunk);
|
|
559
|
-
if (index2 === -1 || index2 === siblings2.length - 1) {
|
|
560
|
-
log("No next sibling found for node:", logId(node));
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
const nextSibling2 = siblings2[index2 + 1];
|
|
564
|
-
if (!nextSibling2) {
|
|
565
|
-
log("Next sibling is null for node:", logId(node));
|
|
566
|
-
return;
|
|
567
|
-
}
|
|
568
|
-
return TextNode.getTextNodeFromChunk(nextSibling2);
|
|
569
|
-
}
|
|
570
|
-
console.warn("Text parent is not a text node:", logId(node));
|
|
571
|
-
return;
|
|
572
|
-
}
|
|
573
|
-
const siblings = parent.getChildren();
|
|
393
|
+
const siblings = getNodeChildren(node);
|
|
574
394
|
const index = siblings.indexOf(node);
|
|
575
395
|
if (index === -1 || index === siblings.length - 1) {
|
|
576
396
|
log("No next sibling found for node:", logId(node));
|
package/jsx-runtime.d.ts
CHANGED
|
@@ -5,24 +5,29 @@ import type {
|
|
|
5
5
|
ExtendedIntrinsicElements,
|
|
6
6
|
InputProps,
|
|
7
7
|
OpenTUIComponents,
|
|
8
|
+
ScrollBoxProps,
|
|
8
9
|
SelectProps,
|
|
10
|
+
SpanProps,
|
|
9
11
|
TabSelectProps,
|
|
10
12
|
TextProps,
|
|
11
13
|
} from "./src/types/elements"
|
|
14
|
+
import type { DomNode } from "./dist"
|
|
12
15
|
|
|
13
16
|
declare namespace JSX {
|
|
14
17
|
// Replace Node with Renderable
|
|
15
|
-
type Element =
|
|
18
|
+
type Element = DomNode | ArrayElement | string | number | boolean | null | undefined
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
type ArrayElement = Array<Element>
|
|
18
21
|
|
|
19
22
|
interface IntrinsicElements extends ExtendedIntrinsicElements<OpenTUIComponents> {
|
|
20
23
|
box: BoxProps
|
|
21
24
|
text: TextProps
|
|
25
|
+
span: SpanProps
|
|
22
26
|
input: InputProps
|
|
23
27
|
select: SelectProps
|
|
24
28
|
ascii_font: AsciiFontProps
|
|
25
29
|
tab_select: TabSelectProps
|
|
30
|
+
scrollbox: ScrollBoxProps
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
interface ElementChildrenAttribute {
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"types": "index.d.ts",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"version": "0.0.0-
|
|
7
|
+
"version": "0.0.0-20250912-12c969f4",
|
|
8
8
|
"description": "SolidJS renderer for OpenTUI",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"repository": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"./jsx-dev-runtime": "./jsx-runtime.d.ts"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@opentui/core": "0.0.0-
|
|
28
|
+
"@opentui/core": "0.0.0-20250912-12c969f4",
|
|
29
29
|
"babel-plugin-module-resolver": "5.0.2",
|
|
30
30
|
"@babel/core": "7.28.0",
|
|
31
31
|
"@babel/preset-typescript": "7.27.1",
|
package/scripts/solid-plugin.ts
CHANGED
|
@@ -14,6 +14,12 @@ const solidTransformPlugin: BunPlugin = {
|
|
|
14
14
|
const code = await file.text()
|
|
15
15
|
return { contents: code, loader: "js" }
|
|
16
16
|
})
|
|
17
|
+
build.onLoad({ filter: /\/node_modules\/solid-js\/store\/dist\/server\.js$/ }, async (args) => {
|
|
18
|
+
const path = args.path.replace("server.js", "store.js")
|
|
19
|
+
const file = Bun.file(path)
|
|
20
|
+
const code = await file.text()
|
|
21
|
+
return { contents: code, loader: "js" }
|
|
22
|
+
})
|
|
17
23
|
build.onLoad({ filter: /\.(js|ts)x$/ }, async (args) => {
|
|
18
24
|
const file = Bun.file(args.path)
|
|
19
25
|
const code = await file.text()
|
package/src/elements/index.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { ASCIIFontRenderable, BoxRenderable, InputRenderable, ScrollBoxRenderable, SelectRenderable, TabSelectRenderable, TextRenderable } from "@opentui/core";
|
|
1
|
+
import { ASCIIFontRenderable, BoxRenderable, InputRenderable, ScrollBoxRenderable, SelectRenderable, TabSelectRenderable, TextNodeRenderable, TextRenderable, type RenderContext, type TextNodeOptions } from "@opentui/core";
|
|
2
2
|
import type { RenderableConstructor } from "../types/elements";
|
|
3
3
|
export * from "./hooks";
|
|
4
|
+
declare class SpanRenderable extends TextNodeRenderable {
|
|
5
|
+
private readonly _ctx;
|
|
6
|
+
constructor(_ctx: RenderContext, options: TextNodeOptions);
|
|
7
|
+
}
|
|
4
8
|
export declare const baseComponents: {
|
|
5
9
|
box: typeof BoxRenderable;
|
|
6
10
|
text: typeof TextRenderable;
|
|
@@ -9,6 +13,7 @@ export declare const baseComponents: {
|
|
|
9
13
|
ascii_font: typeof ASCIIFontRenderable;
|
|
10
14
|
tab_select: typeof TabSelectRenderable;
|
|
11
15
|
scrollbox: typeof ScrollBoxRenderable;
|
|
16
|
+
span: typeof SpanRenderable;
|
|
12
17
|
};
|
|
13
18
|
type ComponentCatalogue = Record<string, RenderableConstructor>;
|
|
14
19
|
export declare const componentCatalogue: ComponentCatalogue;
|
package/src/reconciler.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
export declare const _render: (code: () => DomNode, node: DomNode) => () => void, effect: <T>(fn: (prev?: T) => T, init?: T) => void, memo: <T>(fn: () => T, equal: boolean) => () => T, createComponent: <T>(Comp: (props: T) => DomNode, props: T) => DomNode, createElement: (tag: string) => DomNode, createTextNode: (value: string) => DomNode, insertNode: (parent: DomNode, node: DomNode, anchor?: DomNode | undefined) => void, insert: <T>(parent: any, accessor: T | (() => T), marker?: any | null, initial?: any) => DomNode, spread: <T>(node: any, accessor: (() => T) | T, skipChildren?: boolean) => void, setProp: <T>(node: DomNode, name: string, value: T, prev?: T | undefined) => T, mergeProps: (...sources: unknown[]) => unknown, use: <A, T>(fn: (element: DomNode, arg: A) => T, element: DomNode, arg: A) => T;
|
|
1
|
+
import { BaseRenderable } from "@opentui/core";
|
|
2
|
+
export type DomNode = BaseRenderable;
|
|
3
|
+
export declare const _render: (code: () => BaseRenderable, node: BaseRenderable) => () => void, effect: <T>(fn: (prev?: T) => T, init?: T) => void, memo: <T>(fn: () => T, equal: boolean) => () => T, createComponent: <T>(Comp: (props: T) => BaseRenderable, props: T) => BaseRenderable, createElement: (tag: string) => BaseRenderable, createTextNode: (value: string) => BaseRenderable, insertNode: (parent: BaseRenderable, node: BaseRenderable, anchor?: BaseRenderable | undefined) => void, insert: <T>(parent: any, accessor: T | (() => T), marker?: any | null, initial?: any) => BaseRenderable, spread: <T>(node: any, accessor: (() => T) | T, skipChildren?: boolean) => void, setProp: <T>(node: BaseRenderable, name: string, value: T, prev?: T | undefined) => T, mergeProps: (...sources: unknown[]) => unknown, use: <A, T>(fn: (element: BaseRenderable, arg: A) => T, element: BaseRenderable, arg: A) => T;
|
package/src/types/elements.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { ASCIIFontOptions, ASCIIFontRenderable, BoxOptions, BoxRenderable, InputRenderable, InputRenderableOptions,
|
|
2
|
-
import type {
|
|
1
|
+
import type { ASCIIFontOptions, ASCIIFontRenderable, BaseRenderable, BoxOptions, BoxRenderable, InputRenderable, InputRenderableOptions, RenderableOptions, RenderContext, ScrollBoxOptions, ScrollBoxRenderable, SelectOption, SelectRenderable, SelectRenderableOptions, TabSelectOption, TabSelectRenderable, TabSelectRenderableOptions, TextNodeRenderable, TextOptions, TextRenderable } from "@opentui/core";
|
|
2
|
+
import type { Ref } from "solid-js";
|
|
3
|
+
import type { JSX } from "../../jsx-runtime";
|
|
3
4
|
/** Properties that should not be included in the style prop */
|
|
4
5
|
export type NonStyledProps = "id" | "buffered" | "live" | "enableLayout" | "selectable" | "renderAfter" | "renderBefore" | `on${string}`;
|
|
5
6
|
/** Solid-specific props for all components */
|
|
@@ -7,7 +8,7 @@ export type ElementProps<TRenderable = unknown> = {
|
|
|
7
8
|
ref?: Ref<TRenderable>;
|
|
8
9
|
};
|
|
9
10
|
/** Base type for any renderable constructor */
|
|
10
|
-
export type RenderableConstructor<TRenderable extends
|
|
11
|
+
export type RenderableConstructor<TRenderable extends BaseRenderable = BaseRenderable> = new (ctx: RenderContext, options: any) => TRenderable;
|
|
11
12
|
/** Extract the options type from a renderable constructor */
|
|
12
13
|
type ExtractRenderableOptions<TConstructor> = TConstructor extends new (ctx: RenderContext, options: infer TOptions) => any ? TOptions : never;
|
|
13
14
|
/** Extract the renderable type from a constructor */
|
|
@@ -19,13 +20,16 @@ type ContainerProps<TOptions> = TOptions & {
|
|
|
19
20
|
children?: JSX.Element;
|
|
20
21
|
};
|
|
21
22
|
/** Smart component props that automatically determine excluded properties */
|
|
22
|
-
type ComponentProps<TOptions extends RenderableOptions<TRenderable>, TRenderable extends
|
|
23
|
+
type ComponentProps<TOptions extends RenderableOptions<TRenderable>, TRenderable extends BaseRenderable> = TOptions & {
|
|
23
24
|
style?: Partial<Omit<TOptions, GetNonStyledProperties<RenderableConstructor<TRenderable>>>>;
|
|
24
25
|
} & ElementProps<TRenderable>;
|
|
25
26
|
/** Valid text content types for Text component children */
|
|
26
|
-
type TextChildren =
|
|
27
|
+
type TextChildren = string | number | boolean | null | undefined | JSX.Element;
|
|
27
28
|
export type TextProps = ComponentProps<TextOptions, TextRenderable> & {
|
|
28
|
-
children?: TextChildren |
|
|
29
|
+
children?: TextChildren | Array<TextChildren>;
|
|
30
|
+
};
|
|
31
|
+
export type SpanProps = ComponentProps<{}, TextNodeRenderable> & {
|
|
32
|
+
children?: TextChildren | Array<TextChildren>;
|
|
29
33
|
};
|
|
30
34
|
export type BoxProps = ComponentProps<ContainerProps<BoxOptions>, BoxRenderable>;
|
|
31
35
|
export type InputProps = ComponentProps<InputRenderableOptions, InputRenderable> & {
|
|
@@ -47,6 +51,8 @@ export type TabSelectProps = ComponentProps<TabSelectRenderableOptions, TabSelec
|
|
|
47
51
|
};
|
|
48
52
|
export type ScrollBoxProps = ComponentProps<ContainerProps<ScrollBoxOptions>, ScrollBoxRenderable> & {
|
|
49
53
|
focused?: boolean;
|
|
54
|
+
stickyScroll?: boolean;
|
|
55
|
+
stickyStart?: "bottom" | "top" | "left" | "right";
|
|
50
56
|
};
|
|
51
57
|
/** Convert renderable constructor to component props with proper style exclusions */
|
|
52
58
|
export type ExtendedComponentProps<TConstructor extends RenderableConstructor, TOptions = ExtractRenderableOptions<TConstructor>> = TOptions & {
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { Renderable, TextRenderable, type RenderContext, type TextChunk, type TextOptions } from "@opentui/core";
|
|
2
|
-
import { type DomNode } from "../reconciler";
|
|
3
|
-
export declare const isTextChunk: (node: any) => node is TextChunk;
|
|
4
|
-
/**
|
|
5
|
-
* Represents a text node in the SolidJS reconciler.
|
|
6
|
-
*/
|
|
7
|
-
export declare class TextNode {
|
|
8
|
-
id: string;
|
|
9
|
-
chunk: TextChunk;
|
|
10
|
-
parent?: Renderable;
|
|
11
|
-
textParent?: TextRenderable | GhostTextRenderable;
|
|
12
|
-
constructor(chunk: TextChunk);
|
|
13
|
-
/**
|
|
14
|
-
* Replaces the current text chunk with a new one.
|
|
15
|
-
* @param newChunk The new text chunk to replace with.
|
|
16
|
-
*/
|
|
17
|
-
replaceText(newChunk: TextChunk): void;
|
|
18
|
-
/**
|
|
19
|
-
* Retrieves the TextNode associated with a given TextChunk.
|
|
20
|
-
* @param chunk The text chunk to look up.
|
|
21
|
-
* @returns The associated TextNode or undefined if not found.
|
|
22
|
-
*/
|
|
23
|
-
static getTextNodeFromChunk(chunk: TextChunk): TextNode | undefined;
|
|
24
|
-
/**
|
|
25
|
-
* Inserts this text node into the DOM structure.
|
|
26
|
-
* @param parent The parent DOM node.
|
|
27
|
-
* @param anchor The anchor node for positioning.
|
|
28
|
-
*/
|
|
29
|
-
insert(parent: DomNode, anchor?: DomNode): void;
|
|
30
|
-
/**
|
|
31
|
-
* Removes this text node from the DOM structure.
|
|
32
|
-
* @param parent The parent DOM node.
|
|
33
|
-
*/
|
|
34
|
-
remove(parent: DomNode): void;
|
|
35
|
-
/**
|
|
36
|
-
* Gets or creates a ghost text node for rendering text content.
|
|
37
|
-
* @param parent The parent renderable.
|
|
38
|
-
* @param anchor The anchor node for positioning.
|
|
39
|
-
* @returns The text renderable ghost node.
|
|
40
|
-
* @private
|
|
41
|
-
*/
|
|
42
|
-
private getOrCreateTextGhostNode;
|
|
43
|
-
}
|
|
44
|
-
declare class GhostTextRenderable extends TextRenderable {
|
|
45
|
-
constructor(ctx: RenderContext, options: TextOptions);
|
|
46
|
-
static isGhostNode(node: DomNode): node is GhostTextRenderable;
|
|
47
|
-
}
|
|
48
|
-
export {};
|