@opentui/solid 0.1.8 → 0.1.9

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/README.md CHANGED
@@ -35,4 +35,4 @@ import { render } from "@opentui/solid"
35
35
  render(() => <text>Hello, World!</text>)
36
36
  ```
37
37
 
38
- 4. Run with `bun --conditions=browser index.tsx`.
38
+ 4. Run with `bun index.tsx`.
package/index.js CHANGED
@@ -108,16 +108,19 @@ var elements = {
108
108
  import {
109
109
  InputRenderable as InputRenderable2,
110
110
  InputRenderableEvents,
111
- Renderable,
111
+ Renderable as Renderable2,
112
112
  SelectRenderable as SelectRenderable2,
113
113
  SelectRenderableEvents,
114
114
  StyledText,
115
115
  TabSelectRenderable as TabSelectRenderable2,
116
116
  TabSelectRenderableEvents,
117
- TextRenderable as TextRenderable2
117
+ TextRenderable as TextRenderable3
118
118
  } from "@opentui/core";
119
119
  import { createRenderer } from "solid-js/universal";
120
120
 
121
+ // src/elements/text-node.ts
122
+ import { Renderable, TextRenderable as TextRenderable2 } from "@opentui/core";
123
+
121
124
  // src/utils/id-counter.ts
122
125
  var idCounter = new Map;
123
126
  function getNextId(elementType) {
@@ -129,8 +132,14 @@ function getNextId(elementType) {
129
132
  return `${elementType}-${value}`;
130
133
  }
131
134
 
132
- // src/reconciler.ts
135
+ // src/utils/log.ts
136
+ var log = (...args) => {
137
+ console.log("[Reconciler]", ...args);
138
+ };
139
+
140
+ // src/elements/text-node.ts
133
141
  var GHOST_NODE_TAG = "text-ghost";
142
+ var ChunkToTextNodeMap = new WeakMap;
134
143
 
135
144
  class TextNode {
136
145
  id;
@@ -140,91 +149,112 @@ class TextNode {
140
149
  constructor(chunk) {
141
150
  this.id = getNextId("text-node");
142
151
  this.chunk = chunk;
152
+ ChunkToTextNodeMap.set(chunk, this);
143
153
  }
144
- }
145
- var ChunkToTextNodeMap = new WeakMap;
146
- var log = (...args) => {
147
- console.log("[Reconciler]", ...args);
148
- };
149
- function getOrCreateTextGhostNode(parent, anchor) {
150
- if (anchor instanceof TextNode && anchor.textParent) {
151
- return anchor.textParent;
152
- }
153
- const children = parent.getChildren();
154
- if (anchor instanceof Renderable) {
155
- const anchorIndex = children.findIndex((el) => el.id === anchor.id);
156
- const beforeAnchor = children[anchorIndex - 1];
157
- if (beforeAnchor instanceof TextRenderable2 && beforeAnchor.id.startsWith(GHOST_NODE_TAG)) {
158
- return beforeAnchor;
154
+ replaceText(newChunk) {
155
+ const textParent = this.textParent;
156
+ if (!textParent) {
157
+ log("No parent found for text node:", this.id);
158
+ return;
159
159
  }
160
+ textParent.content = textParent.content.replace(newChunk, this.chunk);
161
+ this.chunk = newChunk;
162
+ ChunkToTextNodeMap.set(newChunk, this);
160
163
  }
161
- const lastChild = children.at(-1);
162
- if (lastChild instanceof TextRenderable2 && lastChild.id.startsWith(GHOST_NODE_TAG)) {
163
- return lastChild;
164
- }
165
- const ghostNode = new TextRenderable2(getNextId(GHOST_NODE_TAG), {});
166
- _insertNode(parent, ghostNode, anchor);
167
- return ghostNode;
168
- }
169
- function insertTextNode(parent, node, anchor) {
170
- if (!(parent instanceof Renderable)) {
171
- console.warn("Attaching text node to parent text node, impossible");
172
- return;
173
- }
174
- log("Inserting text node:", node.id, "into parent:", parent.id, "with anchor:", anchor?.id);
175
- let textParent;
176
- if (!(parent instanceof TextRenderable2)) {
177
- textParent = getOrCreateTextGhostNode(parent, anchor);
178
- } else {
179
- textParent = parent;
164
+ static getTextNodeFromChunk(chunk) {
165
+ return ChunkToTextNodeMap.get(chunk);
180
166
  }
181
- node.textParent = textParent;
182
- let styledText = textParent.content;
183
- if (anchor && anchor instanceof TextNode) {
184
- const anchorIndex = styledText.chunks.indexOf(anchor.chunk);
185
- if (anchorIndex == -1) {
186
- console.log("anchor not found");
167
+ insert(parent, anchor) {
168
+ if (!(parent instanceof Renderable)) {
169
+ log("Attaching text node to parent text node, impossible");
187
170
  return;
188
171
  }
189
- styledText = styledText.insert(node.chunk, anchorIndex);
190
- } else {
191
- const firstChunk = textParent.content.chunks[0];
192
- if (firstChunk && !ChunkToTextNodeMap.has(firstChunk)) {
193
- styledText = styledText.replace(node.chunk, firstChunk);
172
+ let textParent;
173
+ if (!(parent instanceof TextRenderable2)) {
174
+ textParent = this.getOrCreateTextGhostNode(parent, anchor);
175
+ } else {
176
+ textParent = parent;
177
+ }
178
+ this.textParent = textParent;
179
+ let styledText = textParent.content;
180
+ if (anchor && anchor instanceof TextNode) {
181
+ const anchorIndex = styledText.chunks.indexOf(anchor.chunk);
182
+ if (anchorIndex === -1) {
183
+ log("anchor not found");
184
+ return;
185
+ }
186
+ styledText = styledText.insert(this.chunk, anchorIndex);
194
187
  } else {
195
- styledText = styledText.insert(node.chunk);
188
+ const firstChunk = textParent.content.chunks[0];
189
+ if (firstChunk && !ChunkToTextNodeMap.has(firstChunk)) {
190
+ styledText = styledText.replace(this.chunk, firstChunk);
191
+ } else {
192
+ styledText = styledText.insert(this.chunk);
193
+ }
196
194
  }
195
+ textParent.content = styledText;
196
+ this.parent = parent;
197
197
  }
198
- textParent.content = styledText;
199
- node.parent = parent;
200
- return;
201
- }
202
- function removeTextNode(parent, node) {
203
- if (!(parent instanceof Renderable)) {
204
- ChunkToTextNodeMap.delete(node.chunk);
205
- return;
198
+ remove(parent) {
199
+ if (!(parent instanceof Renderable)) {
200
+ ChunkToTextNodeMap.delete(this.chunk);
201
+ return;
202
+ }
203
+ if (parent === this.textParent && parent instanceof TextRenderable2) {
204
+ ChunkToTextNodeMap.delete(this.chunk);
205
+ parent.content = parent.content.remove(this.chunk);
206
+ return;
207
+ }
208
+ if (this.textParent) {
209
+ ChunkToTextNodeMap.delete(this.chunk);
210
+ let styledText = this.textParent.content;
211
+ styledText = styledText.remove(this.chunk);
212
+ if (styledText.chunks.length > 0) {
213
+ this.textParent.content = styledText;
214
+ } else {
215
+ this.parent?.remove(this.textParent.id);
216
+ this.textParent.destroyRecursively();
217
+ }
218
+ }
206
219
  }
207
- if (parent === node.textParent && parent instanceof TextRenderable2) {
208
- ChunkToTextNodeMap.delete(node.chunk);
209
- parent.content = parent.content.remove(node.chunk);
210
- } else if (node.textParent) {
211
- ChunkToTextNodeMap.delete(node.chunk);
212
- let styledText = node.textParent.content;
213
- styledText = styledText.remove(node.chunk);
214
- if (styledText.chunks.length > 0) {
215
- node.textParent.content = styledText;
216
- } else {
217
- node.parent?.remove(node.textParent.id);
218
- node.textParent.destroyRecursively();
220
+ getOrCreateTextGhostNode(parent, anchor) {
221
+ if (anchor instanceof TextNode && anchor.textParent) {
222
+ return anchor.textParent;
219
223
  }
224
+ const children = parent.getChildren();
225
+ if (anchor instanceof Renderable) {
226
+ const anchorIndex = children.findIndex((el) => el.id === anchor.id);
227
+ const beforeAnchor = children[anchorIndex - 1];
228
+ if (beforeAnchor instanceof GhostTextRenderable) {
229
+ return beforeAnchor;
230
+ }
231
+ }
232
+ const lastChild = children.at(-1);
233
+ if (lastChild instanceof GhostTextRenderable) {
234
+ return lastChild;
235
+ }
236
+ const ghostNode = new GhostTextRenderable(getNextId(GHOST_NODE_TAG), {});
237
+ insertNode(parent, ghostNode, anchor);
238
+ return ghostNode;
239
+ }
240
+ }
241
+
242
+ class GhostTextRenderable extends TextRenderable2 {
243
+ constructor(id, options) {
244
+ super(id, options);
245
+ }
246
+ static isGhostNode(node) {
247
+ return node instanceof GhostTextRenderable;
220
248
  }
221
249
  }
250
+
251
+ // src/reconciler.ts
222
252
  function _insertNode(parent, node, anchor) {
223
253
  log("Inserting node:", node.id, "into parent:", parent.id, "with anchor:", anchor?.id);
224
254
  if (node instanceof TextNode) {
225
- return insertTextNode(parent, node, anchor);
255
+ return node.insert(parent, anchor);
226
256
  }
227
- if (!(parent instanceof Renderable)) {
257
+ if (!(parent instanceof Renderable2)) {
228
258
  return;
229
259
  }
230
260
  if (anchor) {
@@ -242,9 +272,9 @@ function _insertNode(parent, node, anchor) {
242
272
  function _removeNode(parent, node) {
243
273
  log("Removing node:", node.id, "from parent:", parent.id);
244
274
  if (node instanceof TextNode) {
245
- return removeTextNode(parent, node);
275
+ return node.remove(parent);
246
276
  }
247
- if (parent instanceof Renderable && node instanceof Renderable) {
277
+ if (parent instanceof Renderable2 && node instanceof Renderable2) {
248
278
  parent.remove(node.id);
249
279
  node.destroyRecursively();
250
280
  }
@@ -278,30 +308,18 @@ var {
278
308
  plainText: `${value}`
279
309
  };
280
310
  const textNode = new TextNode(chunk);
281
- ChunkToTextNodeMap.set(chunk, textNode);
282
311
  return textNode;
283
312
  },
284
313
  replaceText(textNode, value) {
285
314
  log("Replacing text:", value, "in node:", textNode.id);
286
- if (textNode instanceof Renderable)
315
+ if (textNode instanceof Renderable2)
287
316
  return;
288
317
  const newChunk = {
289
318
  __isChunk: true,
290
319
  text: new TextEncoder().encode(value),
291
320
  plainText: value
292
321
  };
293
- const textParent = textNode.textParent;
294
- if (!textParent) {
295
- log("No parent found for text node:", textNode.id);
296
- return;
297
- }
298
- if (textParent instanceof TextRenderable2) {
299
- const styledText = textParent.content;
300
- styledText.replace(newChunk, textNode.chunk);
301
- textParent.content = styledText;
302
- textNode.chunk = newChunk;
303
- ChunkToTextNodeMap.set(newChunk, textNode);
304
- }
322
+ textNode.replaceText(newChunk);
305
323
  },
306
324
  setProperty(node, name, value, prev) {
307
325
  if (node instanceof TextNode) {
@@ -414,10 +432,10 @@ var {
414
432
  },
415
433
  getFirstChild(node) {
416
434
  log("Getting first child of node:", node.id);
417
- if (node instanceof TextRenderable2) {
435
+ if (node instanceof TextRenderable3) {
418
436
  const chunk = node.content.chunks[0];
419
437
  if (chunk) {
420
- return ChunkToTextNodeMap.get(chunk);
438
+ return TextNode.getTextNodeFromChunk(chunk);
421
439
  } else {
422
440
  return;
423
441
  }
@@ -441,7 +459,7 @@ var {
441
459
  return;
442
460
  }
443
461
  if (node instanceof TextNode) {
444
- if (parent instanceof TextRenderable2) {
462
+ if (parent instanceof TextRenderable3) {
445
463
  const siblings2 = parent.content.chunks;
446
464
  const index2 = siblings2.indexOf(node.chunk);
447
465
  if (index2 === -1 || index2 === siblings2.length - 1) {
@@ -453,7 +471,7 @@ var {
453
471
  log("Next sibling is null for node:", node.id);
454
472
  return;
455
473
  }
456
- return ChunkToTextNodeMap.get(nextSibling2);
474
+ return TextNode.getTextNodeFromChunk(nextSibling2);
457
475
  }
458
476
  console.warn("Text parent is not a text node:", node.id);
459
477
  return;
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.8",
7
+ "version": "0.1.9",
8
8
  "description": "SolidJS renderer for OpenTUI",
9
9
  "license": "MIT",
10
10
  "repository": {
@@ -30,7 +30,7 @@
30
30
  "./jsx-dev-runtime": "./jsx-runtime.d.ts"
31
31
  },
32
32
  "dependencies": {
33
- "@opentui/core": "0.1.8"
33
+ "@opentui/core": "0.1.9"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/babel__core": "7.20.5",
@@ -8,26 +8,17 @@ import { type BunPlugin } from "bun"
8
8
  const solidTransformPlugin: BunPlugin = {
9
9
  name: "bun-plugin-solid",
10
10
  setup: (build) => {
11
- // build.onLoad({ filter: /\.(js|ts)$/ }, async (args) => {
12
- // const { readFile } = await import("node:fs/promises");
13
- // const code = await readFile(args.path, "utf8");
14
- // const transforms = await transformAsync(code, {
15
- // filename: args.path,
16
- // // env: {
17
- // // development: {
18
- // // plugins: ["solid-refresh/babel"],
19
- // // },
20
- // // },
21
- // presets: [[ts, {}]],
22
- // });
23
- // return {
24
- // contents: transforms.code,
25
- // loader: "js",
26
- // };
27
- // });
11
+ build.onLoad({ filter: /\/node_modules\/solid-js\/dist\/server\.js$/ }, async (args) => {
12
+ const { readFile } = await import("node:fs/promises")
13
+ const path = args.path.replace("server.js", "solid.js")
14
+ const file = Bun.file(path)
15
+ const code = await file.text()
16
+ return { contents: code, loader: "js" }
17
+ })
28
18
  build.onLoad({ filter: /\.(js|ts)x$/ }, async (args) => {
29
19
  const { readFile } = await import("node:fs/promises")
30
- const code = await readFile(args.path, "utf8")
20
+ const file = Bun.file(args.path)
21
+ const code = await file.text()
31
22
  const transforms = await transformAsync(code, {
32
23
  filename: args.path,
33
24
  // env: {
@@ -17,12 +17,12 @@ type ElementProps<T extends RenderableOptions, K extends Renderable = Renderable
17
17
  style?: Omit<T, NonStyleKeys | RenderableNonStyleKeys>;
18
18
  ref?: Ref<K>;
19
19
  } & T;
20
- type ContianerProps = {
20
+ type ContainerProps = {
21
21
  children?: JSX.Element;
22
22
  };
23
- export type BoxElementProps = ElementProps<BoxOptions, BoxRenderable, "title"> & ContianerProps;
23
+ export type BoxElementProps = ElementProps<BoxOptions, BoxRenderable, "title"> & ContainerProps;
24
24
  export type BoxStyle = BoxElementProps["style"];
25
- export type GroupElementProps = ElementProps<RenderableOptions, GroupRenderable> & ContianerProps;
25
+ export type GroupElementProps = ElementProps<RenderableOptions, GroupRenderable> & ContainerProps;
26
26
  export type GroupStyle = GroupElementProps["style"];
27
27
  export type InputElementProps = ElementProps<InputRenderableOptions, InputRenderable, "value" | "maxLength" | "placeholder"> & {
28
28
  onInput?: (value: string) => void;
@@ -0,0 +1,47 @@
1
+ import { Renderable, TextRenderable, type TextChunk, type TextOptions } from "@opentui/core";
2
+ import { type DomNode } from "../reconciler";
3
+ /**
4
+ * Represents a text node in the SolidJS reconciler.
5
+ */
6
+ export declare class TextNode {
7
+ id: string;
8
+ chunk: TextChunk;
9
+ parent?: Renderable;
10
+ textParent?: TextRenderable | GhostTextRenderable;
11
+ constructor(chunk: TextChunk);
12
+ /**
13
+ * Replaces the current text chunk with a new one.
14
+ * @param newChunk The new text chunk to replace with.
15
+ */
16
+ replaceText(newChunk: TextChunk): void;
17
+ /**
18
+ * Retrieves the TextNode associated with a given TextChunk.
19
+ * @param chunk The text chunk to look up.
20
+ * @returns The associated TextNode or undefined if not found.
21
+ */
22
+ static getTextNodeFromChunk(chunk: TextChunk): TextNode | undefined;
23
+ /**
24
+ * Inserts this text node into the DOM structure.
25
+ * @param parent The parent DOM node.
26
+ * @param anchor The anchor node for positioning.
27
+ */
28
+ insert(parent: DomNode, anchor?: DomNode): void;
29
+ /**
30
+ * Removes this text node from the DOM structure.
31
+ * @param parent The parent DOM node.
32
+ */
33
+ remove(parent: DomNode): void;
34
+ /**
35
+ * Gets or creates a ghost text node for rendering text content.
36
+ * @param parent The parent renderable.
37
+ * @param anchor The anchor node for positioning.
38
+ * @returns The text renderable ghost node.
39
+ * @private
40
+ */
41
+ private getOrCreateTextGhostNode;
42
+ }
43
+ declare class GhostTextRenderable extends TextRenderable {
44
+ constructor(id: string, options: TextOptions);
45
+ static isGhostNode(node: DomNode): node is GhostTextRenderable;
46
+ }
47
+ export {};
@@ -1,12 +1,5 @@
1
- import { Renderable, TextRenderable, type TextChunk } from "@opentui/core";
2
- declare class TextNode {
3
- id: string;
4
- chunk: TextChunk;
5
- parent?: Renderable;
6
- textParent?: TextRenderable;
7
- constructor(chunk: TextChunk);
8
- }
9
- type DomNode = Renderable | TextNode;
1
+ import { Renderable } from "@opentui/core";
2
+ import { TextNode } from "./elements/text-node";
3
+ export type DomNode = Renderable | TextNode;
10
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, solidUniversalInsert: <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;
11
5
  export declare const insert: typeof solidUniversalInsert;
12
- export {};
package/src/reconciler.js CHANGED
@@ -3,13 +3,13 @@
3
3
  import {
4
4
  InputRenderable as InputRenderable2,
5
5
  InputRenderableEvents,
6
- Renderable,
6
+ Renderable as Renderable2,
7
7
  SelectRenderable as SelectRenderable2,
8
8
  SelectRenderableEvents,
9
9
  StyledText,
10
10
  TabSelectRenderable as TabSelectRenderable2,
11
11
  TabSelectRenderableEvents,
12
- TextRenderable as TextRenderable2
12
+ TextRenderable as TextRenderable3
13
13
  } from "@opentui/core";
14
14
  import { createRenderer } from "solid-js/universal";
15
15
 
@@ -43,6 +43,9 @@ var elements = {
43
43
  text: TextRenderable
44
44
  };
45
45
 
46
+ // src/elements/text-node.ts
47
+ import { Renderable, TextRenderable as TextRenderable2 } from "@opentui/core";
48
+
46
49
  // src/utils/id-counter.ts
47
50
  var idCounter = new Map;
48
51
  function getNextId(elementType) {
@@ -54,8 +57,14 @@ function getNextId(elementType) {
54
57
  return `${elementType}-${value}`;
55
58
  }
56
59
 
57
- // src/reconciler.ts
60
+ // src/utils/log.ts
61
+ var log = (...args) => {
62
+ console.log("[Reconciler]", ...args);
63
+ };
64
+
65
+ // src/elements/text-node.ts
58
66
  var GHOST_NODE_TAG = "text-ghost";
67
+ var ChunkToTextNodeMap = new WeakMap;
59
68
 
60
69
  class TextNode {
61
70
  id;
@@ -65,91 +74,112 @@ class TextNode {
65
74
  constructor(chunk) {
66
75
  this.id = getNextId("text-node");
67
76
  this.chunk = chunk;
77
+ ChunkToTextNodeMap.set(chunk, this);
68
78
  }
69
- }
70
- var ChunkToTextNodeMap = new WeakMap;
71
- var log = (...args) => {
72
- console.log("[Reconciler]", ...args);
73
- };
74
- function getOrCreateTextGhostNode(parent, anchor) {
75
- if (anchor instanceof TextNode && anchor.textParent) {
76
- return anchor.textParent;
77
- }
78
- const children = parent.getChildren();
79
- if (anchor instanceof Renderable) {
80
- const anchorIndex = children.findIndex((el) => el.id === anchor.id);
81
- const beforeAnchor = children[anchorIndex - 1];
82
- if (beforeAnchor instanceof TextRenderable2 && beforeAnchor.id.startsWith(GHOST_NODE_TAG)) {
83
- return beforeAnchor;
79
+ replaceText(newChunk) {
80
+ const textParent = this.textParent;
81
+ if (!textParent) {
82
+ log("No parent found for text node:", this.id);
83
+ return;
84
84
  }
85
+ textParent.content = textParent.content.replace(newChunk, this.chunk);
86
+ this.chunk = newChunk;
87
+ ChunkToTextNodeMap.set(newChunk, this);
85
88
  }
86
- const lastChild = children.at(-1);
87
- if (lastChild instanceof TextRenderable2 && lastChild.id.startsWith(GHOST_NODE_TAG)) {
88
- return lastChild;
89
- }
90
- const ghostNode = new TextRenderable2(getNextId(GHOST_NODE_TAG), {});
91
- _insertNode(parent, ghostNode, anchor);
92
- return ghostNode;
93
- }
94
- function insertTextNode(parent, node, anchor) {
95
- if (!(parent instanceof Renderable)) {
96
- console.warn("Attaching text node to parent text node, impossible");
97
- return;
98
- }
99
- log("Inserting text node:", node.id, "into parent:", parent.id, "with anchor:", anchor?.id);
100
- let textParent;
101
- if (!(parent instanceof TextRenderable2)) {
102
- textParent = getOrCreateTextGhostNode(parent, anchor);
103
- } else {
104
- textParent = parent;
89
+ static getTextNodeFromChunk(chunk) {
90
+ return ChunkToTextNodeMap.get(chunk);
105
91
  }
106
- node.textParent = textParent;
107
- let styledText = textParent.content;
108
- if (anchor && anchor instanceof TextNode) {
109
- const anchorIndex = styledText.chunks.indexOf(anchor.chunk);
110
- if (anchorIndex == -1) {
111
- console.log("anchor not found");
92
+ insert(parent, anchor) {
93
+ if (!(parent instanceof Renderable)) {
94
+ log("Attaching text node to parent text node, impossible");
112
95
  return;
113
96
  }
114
- styledText = styledText.insert(node.chunk, anchorIndex);
115
- } else {
116
- const firstChunk = textParent.content.chunks[0];
117
- if (firstChunk && !ChunkToTextNodeMap.has(firstChunk)) {
118
- styledText = styledText.replace(node.chunk, firstChunk);
97
+ let textParent;
98
+ if (!(parent instanceof TextRenderable2)) {
99
+ textParent = this.getOrCreateTextGhostNode(parent, anchor);
100
+ } else {
101
+ textParent = parent;
102
+ }
103
+ this.textParent = textParent;
104
+ let styledText = textParent.content;
105
+ if (anchor && anchor instanceof TextNode) {
106
+ const anchorIndex = styledText.chunks.indexOf(anchor.chunk);
107
+ if (anchorIndex === -1) {
108
+ log("anchor not found");
109
+ return;
110
+ }
111
+ styledText = styledText.insert(this.chunk, anchorIndex);
119
112
  } else {
120
- styledText = styledText.insert(node.chunk);
113
+ const firstChunk = textParent.content.chunks[0];
114
+ if (firstChunk && !ChunkToTextNodeMap.has(firstChunk)) {
115
+ styledText = styledText.replace(this.chunk, firstChunk);
116
+ } else {
117
+ styledText = styledText.insert(this.chunk);
118
+ }
121
119
  }
120
+ textParent.content = styledText;
121
+ this.parent = parent;
122
122
  }
123
- textParent.content = styledText;
124
- node.parent = parent;
125
- return;
126
- }
127
- function removeTextNode(parent, node) {
128
- if (!(parent instanceof Renderable)) {
129
- ChunkToTextNodeMap.delete(node.chunk);
130
- return;
123
+ remove(parent) {
124
+ if (!(parent instanceof Renderable)) {
125
+ ChunkToTextNodeMap.delete(this.chunk);
126
+ return;
127
+ }
128
+ if (parent === this.textParent && parent instanceof TextRenderable2) {
129
+ ChunkToTextNodeMap.delete(this.chunk);
130
+ parent.content = parent.content.remove(this.chunk);
131
+ return;
132
+ }
133
+ if (this.textParent) {
134
+ ChunkToTextNodeMap.delete(this.chunk);
135
+ let styledText = this.textParent.content;
136
+ styledText = styledText.remove(this.chunk);
137
+ if (styledText.chunks.length > 0) {
138
+ this.textParent.content = styledText;
139
+ } else {
140
+ this.parent?.remove(this.textParent.id);
141
+ this.textParent.destroyRecursively();
142
+ }
143
+ }
131
144
  }
132
- if (parent === node.textParent && parent instanceof TextRenderable2) {
133
- ChunkToTextNodeMap.delete(node.chunk);
134
- parent.content = parent.content.remove(node.chunk);
135
- } else if (node.textParent) {
136
- ChunkToTextNodeMap.delete(node.chunk);
137
- let styledText = node.textParent.content;
138
- styledText = styledText.remove(node.chunk);
139
- if (styledText.chunks.length > 0) {
140
- node.textParent.content = styledText;
141
- } else {
142
- node.parent?.remove(node.textParent.id);
143
- node.textParent.destroyRecursively();
145
+ getOrCreateTextGhostNode(parent, anchor) {
146
+ if (anchor instanceof TextNode && anchor.textParent) {
147
+ return anchor.textParent;
144
148
  }
149
+ const children = parent.getChildren();
150
+ if (anchor instanceof Renderable) {
151
+ const anchorIndex = children.findIndex((el) => el.id === anchor.id);
152
+ const beforeAnchor = children[anchorIndex - 1];
153
+ if (beforeAnchor instanceof GhostTextRenderable) {
154
+ return beforeAnchor;
155
+ }
156
+ }
157
+ const lastChild = children.at(-1);
158
+ if (lastChild instanceof GhostTextRenderable) {
159
+ return lastChild;
160
+ }
161
+ const ghostNode = new GhostTextRenderable(getNextId(GHOST_NODE_TAG), {});
162
+ insertNode(parent, ghostNode, anchor);
163
+ return ghostNode;
164
+ }
165
+ }
166
+
167
+ class GhostTextRenderable extends TextRenderable2 {
168
+ constructor(id, options) {
169
+ super(id, options);
170
+ }
171
+ static isGhostNode(node) {
172
+ return node instanceof GhostTextRenderable;
145
173
  }
146
174
  }
175
+
176
+ // src/reconciler.ts
147
177
  function _insertNode(parent, node, anchor) {
148
178
  log("Inserting node:", node.id, "into parent:", parent.id, "with anchor:", anchor?.id);
149
179
  if (node instanceof TextNode) {
150
- return insertTextNode(parent, node, anchor);
180
+ return node.insert(parent, anchor);
151
181
  }
152
- if (!(parent instanceof Renderable)) {
182
+ if (!(parent instanceof Renderable2)) {
153
183
  return;
154
184
  }
155
185
  if (anchor) {
@@ -167,9 +197,9 @@ function _insertNode(parent, node, anchor) {
167
197
  function _removeNode(parent, node) {
168
198
  log("Removing node:", node.id, "from parent:", parent.id);
169
199
  if (node instanceof TextNode) {
170
- return removeTextNode(parent, node);
200
+ return node.remove(parent);
171
201
  }
172
- if (parent instanceof Renderable && node instanceof Renderable) {
202
+ if (parent instanceof Renderable2 && node instanceof Renderable2) {
173
203
  parent.remove(node.id);
174
204
  node.destroyRecursively();
175
205
  }
@@ -203,30 +233,18 @@ var {
203
233
  plainText: `${value}`
204
234
  };
205
235
  const textNode = new TextNode(chunk);
206
- ChunkToTextNodeMap.set(chunk, textNode);
207
236
  return textNode;
208
237
  },
209
238
  replaceText(textNode, value) {
210
239
  log("Replacing text:", value, "in node:", textNode.id);
211
- if (textNode instanceof Renderable)
240
+ if (textNode instanceof Renderable2)
212
241
  return;
213
242
  const newChunk = {
214
243
  __isChunk: true,
215
244
  text: new TextEncoder().encode(value),
216
245
  plainText: value
217
246
  };
218
- const textParent = textNode.textParent;
219
- if (!textParent) {
220
- log("No parent found for text node:", textNode.id);
221
- return;
222
- }
223
- if (textParent instanceof TextRenderable2) {
224
- const styledText = textParent.content;
225
- styledText.replace(newChunk, textNode.chunk);
226
- textParent.content = styledText;
227
- textNode.chunk = newChunk;
228
- ChunkToTextNodeMap.set(newChunk, textNode);
229
- }
247
+ textNode.replaceText(newChunk);
230
248
  },
231
249
  setProperty(node, name, value, prev) {
232
250
  if (node instanceof TextNode) {
@@ -339,10 +357,10 @@ var {
339
357
  },
340
358
  getFirstChild(node) {
341
359
  log("Getting first child of node:", node.id);
342
- if (node instanceof TextRenderable2) {
360
+ if (node instanceof TextRenderable3) {
343
361
  const chunk = node.content.chunks[0];
344
362
  if (chunk) {
345
- return ChunkToTextNodeMap.get(chunk);
363
+ return TextNode.getTextNodeFromChunk(chunk);
346
364
  } else {
347
365
  return;
348
366
  }
@@ -366,7 +384,7 @@ var {
366
384
  return;
367
385
  }
368
386
  if (node instanceof TextNode) {
369
- if (parent instanceof TextRenderable2) {
387
+ if (parent instanceof TextRenderable3) {
370
388
  const siblings2 = parent.content.chunks;
371
389
  const index2 = siblings2.indexOf(node.chunk);
372
390
  if (index2 === -1 || index2 === siblings2.length - 1) {
@@ -378,7 +396,7 @@ var {
378
396
  log("Next sibling is null for node:", node.id);
379
397
  return;
380
398
  }
381
- return ChunkToTextNodeMap.get(nextSibling2);
399
+ return TextNode.getTextNodeFromChunk(nextSibling2);
382
400
  }
383
401
  console.warn("Text parent is not a text node:", node.id);
384
402
  return;
@@ -405,13 +423,13 @@ var insertStyledText = (parent, value, current, marker) => {
405
423
  return current;
406
424
  if (current) {
407
425
  if (typeof current === "object" && "__isChunk" in current) {
408
- const node = ChunkToTextNodeMap.get(current);
426
+ const node = TextNode.getTextNodeFromChunk(current);
409
427
  if (node) {
410
428
  _removeNode(parent, node);
411
429
  }
412
430
  } else if (current instanceof StyledText) {
413
431
  for (const chunk of current.chunks) {
414
- const chunkNode = ChunkToTextNodeMap.get(chunk);
432
+ const chunkNode = TextNode.getTextNodeFromChunk(chunk);
415
433
  if (!chunkNode)
416
434
  continue;
417
435
  _removeNode(parent, chunkNode);
@@ -0,0 +1 @@
1
+ export declare const log: (...args: any[]) => void;