@opentui/solid 0.1.12 → 0.1.13

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/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { type CliRendererConfig } from "@opentui/core";
2
2
  import type { JSX } from "./jsx-runtime";
3
- export * from "./src/elements";
4
3
  export declare const render: (node: () => JSX.Element, renderConfig?: CliRendererConfig) => Promise<void>;
4
+ export * from "./src/reconciler";
5
+ export * from "./src/elements";
5
6
  export { type JSX };
package/index.js CHANGED
@@ -6,7 +6,6 @@ import { createCliRenderer } from "@opentui/core";
6
6
  import {
7
7
  ASCIIFontRenderable,
8
8
  BoxRenderable,
9
- GroupRenderable,
10
9
  InputRenderable,
11
10
  SelectRenderable,
12
11
  TabSelectRenderable,
@@ -97,7 +96,6 @@ var useTimeline = (timeline, initialValue, targetValue, options, startTime = 0)
97
96
  var elements = {
98
97
  ascii_font: ASCIIFontRenderable,
99
98
  box: BoxRenderable,
100
- group: GroupRenderable,
101
99
  input: InputRenderable,
102
100
  select: SelectRenderable,
103
101
  tab_select: TabSelectRenderable,
@@ -500,6 +498,45 @@ var {
500
498
  return nextSibling;
501
499
  }
502
500
  });
501
+ var insertStyledText = (parent, value, current, marker) => {
502
+ while (typeof current === "function")
503
+ current = current();
504
+ if (value === current)
505
+ return current;
506
+ if (current) {
507
+ if (typeof current === "object" && "__isChunk" in current) {
508
+ const node = TextNode.getTextNodeFromChunk(current);
509
+ if (node) {
510
+ _removeNode(parent, node);
511
+ }
512
+ } else if (current instanceof StyledText) {
513
+ for (const chunk of current.chunks) {
514
+ const chunkNode = TextNode.getTextNodeFromChunk(chunk);
515
+ if (!chunkNode)
516
+ continue;
517
+ _removeNode(parent, chunkNode);
518
+ }
519
+ }
520
+ }
521
+ if (value instanceof StyledText) {
522
+ log("Inserting styled text:", value.toString());
523
+ for (const chunk of value.chunks) {
524
+ insertNode(parent, createTextNode(chunk), marker);
525
+ }
526
+ return value;
527
+ } else if (value && typeof value === "object" && "__isChunk" in value) {
528
+ insertNode(parent, createTextNode(value), marker);
529
+ return value;
530
+ }
531
+ return solidUniversalInsert(parent, value, marker, current);
532
+ };
533
+ var insert = (parent, accessor, marker, initial) => {
534
+ if (marker !== undefined && !initial)
535
+ initial = [];
536
+ if (typeof accessor !== "function")
537
+ return insertStyledText(parent, accessor, initial, marker);
538
+ effect((current) => insertStyledText(parent, accessor(), current, marker), initial);
539
+ };
503
540
 
504
541
  // index.ts
505
542
  var render = async (node, renderConfig = {}) => {
@@ -519,9 +556,22 @@ export {
519
556
  useSelectionHandler,
520
557
  useRenderer,
521
558
  useKeyHandler,
559
+ use,
560
+ spread,
561
+ solidUniversalInsert,
562
+ setProp,
522
563
  render,
523
564
  onResize,
565
+ mergeProps,
566
+ memo,
567
+ insertNode,
568
+ insert,
524
569
  elements,
570
+ effect,
571
+ createTextNode,
572
+ createElement,
525
573
  createComponentTimeline,
574
+ createComponent,
575
+ _render,
526
576
  RendererContext
527
577
  };
package/jsx-runtime.d.ts CHANGED
@@ -2,7 +2,6 @@ import { Renderable } from "@opentui/core"
2
2
  import {
3
3
  ASCIIFontElementProps,
4
4
  BoxElementProps,
5
- GroupElementProps,
6
5
  InputElementProps,
7
6
  SelectElementProps,
8
7
  TabSelectElementProps,
@@ -18,7 +17,6 @@ declare namespace JSX {
18
17
  interface IntrinsicElements {
19
18
  ascii_font: ASCIIFontElementProps
20
19
  box: BoxElementProps
21
- group: GroupElementProps
22
20
  input: InputElementProps
23
21
  select: SelectElementProps
24
22
  tab_select: TabSelectElementProps
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.1.12",
7
+ "version": "0.1.13",
8
8
  "description": "SolidJS renderer for OpenTUI",
9
9
  "license": "MIT",
10
10
  "repository": {
@@ -18,11 +18,6 @@
18
18
  "import": "./index.js",
19
19
  "require": "./index.js"
20
20
  },
21
- "./reconciler": {
22
- "types": "./src/reconciler.d.ts",
23
- "import": "./src/reconciler.js",
24
- "require": "./src/reconciler.js"
25
- },
26
21
  "./preload": {
27
22
  "import": "./scripts/preload.ts"
28
23
  },
@@ -30,7 +25,7 @@
30
25
  "./jsx-dev-runtime": "./jsx-runtime.d.ts"
31
26
  },
32
27
  "dependencies": {
33
- "@opentui/core": "0.1.12",
28
+ "@opentui/core": "0.1.13",
34
29
  "babel-plugin-module-resolver": "5.0.2",
35
30
  "@babel/core": "7.28.0",
36
31
  "@babel/preset-typescript": "7.27.1",
@@ -29,7 +29,7 @@ const solidTransformPlugin: BunPlugin = {
29
29
  [
30
30
  solid,
31
31
  {
32
- moduleName: "@opentui/solid/reconciler",
32
+ moduleName: "@opentui/solid",
33
33
  generate: "universal",
34
34
  },
35
35
  ],
@@ -1,11 +1,10 @@
1
1
  import type { ASCIIFontOptions, BoxOptions, InputRenderableOptions, Renderable, RenderableOptions, SelectOption, SelectRenderableOptions, StyledText, TabSelectOption, TabSelectRenderableOptions, TextChunk, TextOptions } from "@opentui/core";
2
- import { ASCIIFontRenderable, BoxRenderable, GroupRenderable, InputRenderable, SelectRenderable, TabSelectRenderable, TextRenderable } from "@opentui/core";
2
+ import { ASCIIFontRenderable, BoxRenderable, InputRenderable, SelectRenderable, TabSelectRenderable, TextRenderable } from "@opentui/core";
3
3
  import type { JSX, Ref } from "solid-js";
4
4
  export * from "./hooks";
5
5
  export declare const elements: {
6
6
  ascii_font: typeof ASCIIFontRenderable;
7
7
  box: typeof BoxRenderable;
8
- group: typeof GroupRenderable;
9
8
  input: typeof InputRenderable;
10
9
  select: typeof SelectRenderable;
11
10
  tab_select: typeof TabSelectRenderable;
@@ -22,8 +21,6 @@ type ContainerProps = {
22
21
  };
23
22
  export type BoxElementProps = ElementProps<BoxOptions, BoxRenderable, "title"> & ContainerProps;
24
23
  export type BoxStyle = BoxElementProps["style"];
25
- export type GroupElementProps = ElementProps<RenderableOptions, GroupRenderable> & ContainerProps;
26
- export type GroupStyle = GroupElementProps["style"];
27
24
  export type InputElementProps = ElementProps<InputRenderableOptions, InputRenderable, "value" | "maxLength" | "placeholder"> & {
28
25
  onInput?: (value: string) => void;
29
26
  onSubmit?: (value: string) => void;
package/src/reconciler.js DELETED
@@ -1,481 +0,0 @@
1
- // @bun
2
- // src/reconciler.ts
3
- import {
4
- InputRenderable as InputRenderable2,
5
- InputRenderableEvents,
6
- Renderable as Renderable2,
7
- SelectRenderable as SelectRenderable2,
8
- SelectRenderableEvents,
9
- StyledText,
10
- TabSelectRenderable as TabSelectRenderable2,
11
- TabSelectRenderableEvents,
12
- TextRenderable as TextRenderable3
13
- } from "@opentui/core";
14
- import { createRenderer } from "solid-js/universal";
15
-
16
- // src/elements/index.ts
17
- import {
18
- ASCIIFontRenderable,
19
- BoxRenderable,
20
- GroupRenderable,
21
- InputRenderable,
22
- SelectRenderable,
23
- TabSelectRenderable,
24
- TextRenderable
25
- } from "@opentui/core";
26
-
27
- // src/elements/hooks.ts
28
- import {
29
- getKeyHandler,
30
- Timeline
31
- } from "@opentui/core";
32
- import { createContext, createSignal, onCleanup, onMount, useContext } from "solid-js";
33
- var RendererContext = createContext();
34
-
35
- // src/elements/index.ts
36
- var elements = {
37
- ascii_font: ASCIIFontRenderable,
38
- box: BoxRenderable,
39
- group: GroupRenderable,
40
- input: InputRenderable,
41
- select: SelectRenderable,
42
- tab_select: TabSelectRenderable,
43
- text: TextRenderable
44
- };
45
-
46
- // src/elements/text-node.ts
47
- import { Renderable, TextRenderable as TextRenderable2 } from "@opentui/core";
48
-
49
- // src/utils/id-counter.ts
50
- var idCounter = new Map;
51
- function getNextId(elementType) {
52
- if (!idCounter.has(elementType)) {
53
- idCounter.set(elementType, 0);
54
- }
55
- const value = idCounter.get(elementType) + 1;
56
- idCounter.set(elementType, value);
57
- return `${elementType}-${value}`;
58
- }
59
-
60
- // src/utils/log.ts
61
- var log = (...args) => {
62
- if (process.env.DEBUG) {
63
- console.log("[Reconciler]", ...args);
64
- }
65
- };
66
-
67
- // src/elements/text-node.ts
68
- var GHOST_NODE_TAG = "text-ghost";
69
- var ChunkToTextNodeMap = new WeakMap;
70
-
71
- class TextNode {
72
- id;
73
- chunk;
74
- parent;
75
- textParent;
76
- constructor(chunk) {
77
- this.id = getNextId("text-node");
78
- this.chunk = chunk;
79
- ChunkToTextNodeMap.set(chunk, this);
80
- }
81
- replaceText(newChunk) {
82
- const textParent = this.textParent;
83
- if (!textParent) {
84
- log("No parent found for text node:", this.id);
85
- return;
86
- }
87
- textParent.content = textParent.content.replace(newChunk, this.chunk);
88
- this.chunk = newChunk;
89
- ChunkToTextNodeMap.set(newChunk, this);
90
- }
91
- static getTextNodeFromChunk(chunk) {
92
- return ChunkToTextNodeMap.get(chunk);
93
- }
94
- insert(parent, anchor) {
95
- if (!(parent instanceof Renderable)) {
96
- log("Attaching text node to parent text node, impossible");
97
- return;
98
- }
99
- let textParent;
100
- if (!(parent instanceof TextRenderable2)) {
101
- textParent = this.getOrCreateTextGhostNode(parent, anchor);
102
- } else {
103
- textParent = parent;
104
- }
105
- this.textParent = textParent;
106
- let styledText = textParent.content;
107
- if (anchor && anchor instanceof TextNode) {
108
- const anchorIndex = styledText.chunks.indexOf(anchor.chunk);
109
- if (anchorIndex === -1) {
110
- log("anchor not found");
111
- return;
112
- }
113
- styledText = styledText.insert(this.chunk, anchorIndex);
114
- } else {
115
- const firstChunk = textParent.content.chunks[0];
116
- if (firstChunk && !ChunkToTextNodeMap.has(firstChunk)) {
117
- styledText = styledText.replace(this.chunk, firstChunk);
118
- } else {
119
- styledText = styledText.insert(this.chunk);
120
- }
121
- }
122
- textParent.content = styledText;
123
- this.parent = parent;
124
- }
125
- remove(parent) {
126
- if (!(parent instanceof Renderable)) {
127
- ChunkToTextNodeMap.delete(this.chunk);
128
- return;
129
- }
130
- if (parent === this.textParent && parent instanceof TextRenderable2) {
131
- ChunkToTextNodeMap.delete(this.chunk);
132
- parent.content = parent.content.remove(this.chunk);
133
- return;
134
- }
135
- if (this.textParent) {
136
- ChunkToTextNodeMap.delete(this.chunk);
137
- let styledText = this.textParent.content;
138
- styledText = styledText.remove(this.chunk);
139
- if (styledText.chunks.length > 0) {
140
- this.textParent.content = styledText;
141
- } else {
142
- this.parent?.remove(this.textParent.id);
143
- this.textParent.destroyRecursively();
144
- }
145
- }
146
- }
147
- getOrCreateTextGhostNode(parent, anchor) {
148
- if (anchor instanceof TextNode && anchor.textParent) {
149
- return anchor.textParent;
150
- }
151
- const children = parent.getChildren();
152
- if (anchor instanceof Renderable) {
153
- const anchorIndex = children.findIndex((el) => el.id === anchor.id);
154
- const beforeAnchor = children[anchorIndex - 1];
155
- if (beforeAnchor instanceof GhostTextRenderable) {
156
- return beforeAnchor;
157
- }
158
- }
159
- const lastChild = children.at(-1);
160
- if (lastChild instanceof GhostTextRenderable) {
161
- return lastChild;
162
- }
163
- const ghostNode = new GhostTextRenderable(parent.ctx, {
164
- id: getNextId(GHOST_NODE_TAG)
165
- });
166
- insertNode(parent, ghostNode, anchor);
167
- return ghostNode;
168
- }
169
- }
170
-
171
- class GhostTextRenderable extends TextRenderable2 {
172
- constructor(ctx, options) {
173
- super(ctx, options);
174
- }
175
- static isGhostNode(node) {
176
- return node instanceof GhostTextRenderable;
177
- }
178
- }
179
-
180
- // src/reconciler.ts
181
- import { useContext as useContext2 } from "solid-js";
182
- function _insertNode(parent, node, anchor) {
183
- log("Inserting node:", node.id, "into parent:", parent.id, "with anchor:", anchor?.id);
184
- if (node instanceof TextNode) {
185
- return node.insert(parent, anchor);
186
- }
187
- if (!(parent instanceof Renderable2)) {
188
- return;
189
- }
190
- if (anchor) {
191
- const anchorIndex = parent.getChildren().findIndex((el) => {
192
- if (anchor instanceof TextNode) {
193
- return el.id === anchor.textParent?.id;
194
- }
195
- return el.id === anchor.id;
196
- });
197
- parent.add(node, anchorIndex);
198
- } else {
199
- parent.add(node);
200
- }
201
- }
202
- function _removeNode(parent, node) {
203
- log("Removing node:", node.id, "from parent:", parent.id);
204
- if (node instanceof TextNode) {
205
- return node.remove(parent);
206
- }
207
- if (parent instanceof Renderable2 && node instanceof Renderable2) {
208
- parent.remove(node.id);
209
- node.destroyRecursively();
210
- }
211
- }
212
- var {
213
- render: _render,
214
- effect,
215
- memo,
216
- createComponent,
217
- createElement,
218
- createTextNode,
219
- insertNode,
220
- insert: solidUniversalInsert,
221
- spread,
222
- setProp,
223
- mergeProps,
224
- use
225
- } = createRenderer({
226
- createElement(tagName) {
227
- log("Creating element:", tagName);
228
- const id = getNextId(tagName);
229
- const solidRenderer = useContext2(RendererContext);
230
- if (!solidRenderer) {
231
- throw new Error("No renderer found");
232
- }
233
- const element = new elements[tagName](solidRenderer, { id });
234
- log("Element created with id:", id);
235
- return element;
236
- },
237
- createTextNode(value) {
238
- log("Creating text node:", value);
239
- const chunk = typeof value === "object" && "__isChunk" in value ? value : {
240
- __isChunk: true,
241
- text: new TextEncoder().encode(`${value}`),
242
- plainText: `${value}`
243
- };
244
- const textNode = new TextNode(chunk);
245
- return textNode;
246
- },
247
- replaceText(textNode, value) {
248
- log("Replacing text:", value, "in node:", textNode.id);
249
- if (textNode instanceof Renderable2)
250
- return;
251
- const newChunk = {
252
- __isChunk: true,
253
- text: new TextEncoder().encode(value),
254
- plainText: value
255
- };
256
- textNode.replaceText(newChunk);
257
- },
258
- setProperty(node, name, value, prev) {
259
- if (node instanceof TextNode) {
260
- console.warn("Cannot set property on text node:", node.id);
261
- return;
262
- }
263
- if (name.startsWith("on:")) {
264
- const eventName = name.slice(3);
265
- if (value) {
266
- node.on(eventName, value);
267
- }
268
- if (prev) {
269
- node.off(eventName, prev);
270
- }
271
- return;
272
- }
273
- switch (name) {
274
- case "focused":
275
- if (value) {
276
- node.focus();
277
- } else {
278
- node.blur();
279
- }
280
- break;
281
- case "onChange":
282
- let event = undefined;
283
- if (node instanceof SelectRenderable2) {
284
- event = SelectRenderableEvents.SELECTION_CHANGED;
285
- } else if (node instanceof TabSelectRenderable2) {
286
- event = TabSelectRenderableEvents.SELECTION_CHANGED;
287
- } else if (node instanceof InputRenderable2) {
288
- event = InputRenderableEvents.CHANGE;
289
- }
290
- if (!event)
291
- break;
292
- if (value) {
293
- node.on(event, value);
294
- }
295
- if (prev) {
296
- node.off(event, prev);
297
- }
298
- break;
299
- case "onInput":
300
- if (node instanceof InputRenderable2) {
301
- if (value) {
302
- node.on(InputRenderableEvents.INPUT, value);
303
- }
304
- if (prev) {
305
- node.off(InputRenderableEvents.INPUT, prev);
306
- }
307
- }
308
- break;
309
- case "onSubmit":
310
- if (node instanceof InputRenderable2) {
311
- if (value) {
312
- node.on(InputRenderableEvents.ENTER, value);
313
- }
314
- if (prev) {
315
- node.off(InputRenderableEvents.ENTER, prev);
316
- }
317
- }
318
- break;
319
- case "onSelect":
320
- if (node instanceof SelectRenderable2) {
321
- if (value) {
322
- node.on(SelectRenderableEvents.ITEM_SELECTED, value);
323
- }
324
- if (prev) {
325
- node.off(SelectRenderableEvents.ITEM_SELECTED, prev);
326
- }
327
- } else if (node instanceof TabSelectRenderable2) {
328
- if (value) {
329
- node.on(TabSelectRenderableEvents.ITEM_SELECTED, value);
330
- }
331
- if (prev) {
332
- node.off(TabSelectRenderableEvents.ITEM_SELECTED, prev);
333
- }
334
- }
335
- break;
336
- case "style":
337
- for (const prop in value) {
338
- const propVal = value[prop];
339
- if (prev !== undefined && propVal === prev[prop])
340
- continue;
341
- node[prop] = propVal;
342
- }
343
- break;
344
- case "text":
345
- case "content":
346
- node[name] = typeof value === "string" ? value : Array.isArray(value) ? value.join("") : `${value}`;
347
- break;
348
- default:
349
- node[name] = value;
350
- }
351
- },
352
- isTextNode(node) {
353
- return node instanceof TextNode;
354
- },
355
- insertNode: _insertNode,
356
- removeNode: _removeNode,
357
- getParentNode(node) {
358
- log("Getting parent of node:", node.id);
359
- const parent = node.parent;
360
- if (!parent) {
361
- log("No parent found for node:", node.id);
362
- return;
363
- }
364
- log("Parent found:", parent.id, "for node:", node.id);
365
- return parent;
366
- },
367
- getFirstChild(node) {
368
- log("Getting first child of node:", node.id);
369
- if (node instanceof TextRenderable3) {
370
- const chunk = node.content.chunks[0];
371
- if (chunk) {
372
- return TextNode.getTextNodeFromChunk(chunk);
373
- } else {
374
- return;
375
- }
376
- }
377
- if (node instanceof TextNode) {
378
- return;
379
- }
380
- const firstChild = node.getChildren()[0];
381
- if (!firstChild) {
382
- log("No first child found for node:", node.id);
383
- return;
384
- }
385
- log("First child found:", firstChild.id, "for node:", node.id);
386
- return firstChild;
387
- },
388
- getNextSibling(node) {
389
- log("Getting next sibling of node:", node.id);
390
- const parent = node.parent;
391
- if (!parent) {
392
- log("No parent found for node:", node.id);
393
- return;
394
- }
395
- if (node instanceof TextNode) {
396
- if (parent instanceof TextRenderable3) {
397
- const siblings2 = parent.content.chunks;
398
- const index2 = siblings2.indexOf(node.chunk);
399
- if (index2 === -1 || index2 === siblings2.length - 1) {
400
- log("No next sibling found for node:", node.id);
401
- return;
402
- }
403
- const nextSibling2 = siblings2[index2 + 1];
404
- if (!nextSibling2) {
405
- log("Next sibling is null for node:", node.id);
406
- return;
407
- }
408
- return TextNode.getTextNodeFromChunk(nextSibling2);
409
- }
410
- console.warn("Text parent is not a text node:", node.id);
411
- return;
412
- }
413
- const siblings = parent.getChildren();
414
- const index = siblings.indexOf(node);
415
- if (index === -1 || index === siblings.length - 1) {
416
- log("No next sibling found for node:", node.id);
417
- return;
418
- }
419
- const nextSibling = siblings[index + 1];
420
- if (!nextSibling) {
421
- log("Next sibling is null for node:", node.id);
422
- return;
423
- }
424
- log("Next sibling found:", nextSibling.id, "for node:", node.id);
425
- return nextSibling;
426
- }
427
- });
428
- var insertStyledText = (parent, value, current, marker) => {
429
- while (typeof current === "function")
430
- current = current();
431
- if (value === current)
432
- return current;
433
- if (current) {
434
- if (typeof current === "object" && "__isChunk" in current) {
435
- const node = TextNode.getTextNodeFromChunk(current);
436
- if (node) {
437
- _removeNode(parent, node);
438
- }
439
- } else if (current instanceof StyledText) {
440
- for (const chunk of current.chunks) {
441
- const chunkNode = TextNode.getTextNodeFromChunk(chunk);
442
- if (!chunkNode)
443
- continue;
444
- _removeNode(parent, chunkNode);
445
- }
446
- }
447
- }
448
- if (value instanceof StyledText) {
449
- log("Inserting styled text:", value.toString());
450
- for (const chunk of value.chunks) {
451
- insertNode(parent, createTextNode(chunk), marker);
452
- }
453
- return value;
454
- } else if (value && typeof value === "object" && "__isChunk" in value) {
455
- insertNode(parent, createTextNode(value), marker);
456
- return value;
457
- }
458
- return solidUniversalInsert(parent, value, marker, current);
459
- };
460
- var insert = (parent, accessor, marker, initial) => {
461
- if (marker !== undefined && !initial)
462
- initial = [];
463
- if (typeof accessor !== "function")
464
- return insertStyledText(parent, accessor, initial, marker);
465
- effect((current) => insertStyledText(parent, accessor(), current, marker), initial);
466
- };
467
- export {
468
- use,
469
- spread,
470
- solidUniversalInsert,
471
- setProp,
472
- mergeProps,
473
- memo,
474
- insertNode,
475
- insert,
476
- effect,
477
- createTextNode,
478
- createElement,
479
- createComponent,
480
- _render
481
- };