@opentui/solid 0.1.8 → 0.1.10

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,16 @@ 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
+ if (process.env.DEBUG) {
138
+ console.log("[Reconciler]", ...args);
139
+ }
140
+ };
141
+
142
+ // src/elements/text-node.ts
133
143
  var GHOST_NODE_TAG = "text-ghost";
144
+ var ChunkToTextNodeMap = new WeakMap;
134
145
 
135
146
  class TextNode {
136
147
  id;
@@ -140,91 +151,112 @@ class TextNode {
140
151
  constructor(chunk) {
141
152
  this.id = getNextId("text-node");
142
153
  this.chunk = chunk;
154
+ ChunkToTextNodeMap.set(chunk, this);
143
155
  }
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;
156
+ replaceText(newChunk) {
157
+ const textParent = this.textParent;
158
+ if (!textParent) {
159
+ log("No parent found for text node:", this.id);
160
+ return;
159
161
  }
162
+ textParent.content = textParent.content.replace(newChunk, this.chunk);
163
+ this.chunk = newChunk;
164
+ ChunkToTextNodeMap.set(newChunk, this);
160
165
  }
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;
166
+ static getTextNodeFromChunk(chunk) {
167
+ return ChunkToTextNodeMap.get(chunk);
180
168
  }
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");
169
+ insert(parent, anchor) {
170
+ if (!(parent instanceof Renderable)) {
171
+ log("Attaching text node to parent text node, impossible");
187
172
  return;
188
173
  }
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);
174
+ let textParent;
175
+ if (!(parent instanceof TextRenderable2)) {
176
+ textParent = this.getOrCreateTextGhostNode(parent, anchor);
194
177
  } else {
195
- styledText = styledText.insert(node.chunk);
178
+ textParent = parent;
196
179
  }
180
+ this.textParent = textParent;
181
+ let styledText = textParent.content;
182
+ if (anchor && anchor instanceof TextNode) {
183
+ const anchorIndex = styledText.chunks.indexOf(anchor.chunk);
184
+ if (anchorIndex === -1) {
185
+ log("anchor not found");
186
+ return;
187
+ }
188
+ styledText = styledText.insert(this.chunk, anchorIndex);
189
+ } else {
190
+ const firstChunk = textParent.content.chunks[0];
191
+ if (firstChunk && !ChunkToTextNodeMap.has(firstChunk)) {
192
+ styledText = styledText.replace(this.chunk, firstChunk);
193
+ } else {
194
+ styledText = styledText.insert(this.chunk);
195
+ }
196
+ }
197
+ textParent.content = styledText;
198
+ this.parent = parent;
197
199
  }
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;
200
+ remove(parent) {
201
+ if (!(parent instanceof Renderable)) {
202
+ ChunkToTextNodeMap.delete(this.chunk);
203
+ return;
204
+ }
205
+ if (parent === this.textParent && parent instanceof TextRenderable2) {
206
+ ChunkToTextNodeMap.delete(this.chunk);
207
+ parent.content = parent.content.remove(this.chunk);
208
+ return;
209
+ }
210
+ if (this.textParent) {
211
+ ChunkToTextNodeMap.delete(this.chunk);
212
+ let styledText = this.textParent.content;
213
+ styledText = styledText.remove(this.chunk);
214
+ if (styledText.chunks.length > 0) {
215
+ this.textParent.content = styledText;
216
+ } else {
217
+ this.parent?.remove(this.textParent.id);
218
+ this.textParent.destroyRecursively();
219
+ }
220
+ }
206
221
  }
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();
222
+ getOrCreateTextGhostNode(parent, anchor) {
223
+ if (anchor instanceof TextNode && anchor.textParent) {
224
+ return anchor.textParent;
225
+ }
226
+ const children = parent.getChildren();
227
+ if (anchor instanceof Renderable) {
228
+ const anchorIndex = children.findIndex((el) => el.id === anchor.id);
229
+ const beforeAnchor = children[anchorIndex - 1];
230
+ if (beforeAnchor instanceof GhostTextRenderable) {
231
+ return beforeAnchor;
232
+ }
233
+ }
234
+ const lastChild = children.at(-1);
235
+ if (lastChild instanceof GhostTextRenderable) {
236
+ return lastChild;
219
237
  }
238
+ const ghostNode = new GhostTextRenderable(getNextId(GHOST_NODE_TAG), {});
239
+ insertNode(parent, ghostNode, anchor);
240
+ return ghostNode;
241
+ }
242
+ }
243
+
244
+ class GhostTextRenderable extends TextRenderable2 {
245
+ constructor(id, options) {
246
+ super(id, options);
247
+ }
248
+ static isGhostNode(node) {
249
+ return node instanceof GhostTextRenderable;
220
250
  }
221
251
  }
252
+
253
+ // src/reconciler.ts
222
254
  function _insertNode(parent, node, anchor) {
223
255
  log("Inserting node:", node.id, "into parent:", parent.id, "with anchor:", anchor?.id);
224
256
  if (node instanceof TextNode) {
225
- return insertTextNode(parent, node, anchor);
257
+ return node.insert(parent, anchor);
226
258
  }
227
- if (!(parent instanceof Renderable)) {
259
+ if (!(parent instanceof Renderable2)) {
228
260
  return;
229
261
  }
230
262
  if (anchor) {
@@ -242,9 +274,9 @@ function _insertNode(parent, node, anchor) {
242
274
  function _removeNode(parent, node) {
243
275
  log("Removing node:", node.id, "from parent:", parent.id);
244
276
  if (node instanceof TextNode) {
245
- return removeTextNode(parent, node);
277
+ return node.remove(parent);
246
278
  }
247
- if (parent instanceof Renderable && node instanceof Renderable) {
279
+ if (parent instanceof Renderable2 && node instanceof Renderable2) {
248
280
  parent.remove(node.id);
249
281
  node.destroyRecursively();
250
282
  }
@@ -278,30 +310,18 @@ var {
278
310
  plainText: `${value}`
279
311
  };
280
312
  const textNode = new TextNode(chunk);
281
- ChunkToTextNodeMap.set(chunk, textNode);
282
313
  return textNode;
283
314
  },
284
315
  replaceText(textNode, value) {
285
316
  log("Replacing text:", value, "in node:", textNode.id);
286
- if (textNode instanceof Renderable)
317
+ if (textNode instanceof Renderable2)
287
318
  return;
288
319
  const newChunk = {
289
320
  __isChunk: true,
290
321
  text: new TextEncoder().encode(value),
291
322
  plainText: value
292
323
  };
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
- }
324
+ textNode.replaceText(newChunk);
305
325
  },
306
326
  setProperty(node, name, value, prev) {
307
327
  if (node instanceof TextNode) {
@@ -414,10 +434,10 @@ var {
414
434
  },
415
435
  getFirstChild(node) {
416
436
  log("Getting first child of node:", node.id);
417
- if (node instanceof TextRenderable2) {
437
+ if (node instanceof TextRenderable3) {
418
438
  const chunk = node.content.chunks[0];
419
439
  if (chunk) {
420
- return ChunkToTextNodeMap.get(chunk);
440
+ return TextNode.getTextNodeFromChunk(chunk);
421
441
  } else {
422
442
  return;
423
443
  }
@@ -441,7 +461,7 @@ var {
441
461
  return;
442
462
  }
443
463
  if (node instanceof TextNode) {
444
- if (parent instanceof TextRenderable2) {
464
+ if (parent instanceof TextRenderable3) {
445
465
  const siblings2 = parent.content.chunks;
446
466
  const index2 = siblings2.indexOf(node.chunk);
447
467
  if (index2 === -1 || index2 === siblings2.length - 1) {
@@ -453,7 +473,7 @@ var {
453
473
  log("Next sibling is null for node:", node.id);
454
474
  return;
455
475
  }
456
- return ChunkToTextNodeMap.get(nextSibling2);
476
+ return TextNode.getTextNodeFromChunk(nextSibling2);
457
477
  }
458
478
  console.warn("Text parent is not a text node:", node.id);
459
479
  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.10",
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.10"
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,16 @@ 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
+ if (process.env.DEBUG) {
63
+ console.log("[Reconciler]", ...args);
64
+ }
65
+ };
66
+
67
+ // src/elements/text-node.ts
58
68
  var GHOST_NODE_TAG = "text-ghost";
69
+ var ChunkToTextNodeMap = new WeakMap;
59
70
 
60
71
  class TextNode {
61
72
  id;
@@ -65,91 +76,112 @@ class TextNode {
65
76
  constructor(chunk) {
66
77
  this.id = getNextId("text-node");
67
78
  this.chunk = chunk;
79
+ ChunkToTextNodeMap.set(chunk, this);
68
80
  }
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;
81
+ replaceText(newChunk) {
82
+ const textParent = this.textParent;
83
+ if (!textParent) {
84
+ log("No parent found for text node:", this.id);
85
+ return;
84
86
  }
87
+ textParent.content = textParent.content.replace(newChunk, this.chunk);
88
+ this.chunk = newChunk;
89
+ ChunkToTextNodeMap.set(newChunk, this);
85
90
  }
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;
91
+ static getTextNodeFromChunk(chunk) {
92
+ return ChunkToTextNodeMap.get(chunk);
105
93
  }
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");
94
+ insert(parent, anchor) {
95
+ if (!(parent instanceof Renderable)) {
96
+ log("Attaching text node to parent text node, impossible");
112
97
  return;
113
98
  }
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);
99
+ let textParent;
100
+ if (!(parent instanceof TextRenderable2)) {
101
+ textParent = this.getOrCreateTextGhostNode(parent, anchor);
119
102
  } else {
120
- styledText = styledText.insert(node.chunk);
103
+ textParent = parent;
121
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;
122
124
  }
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;
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
+ }
131
146
  }
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();
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;
144
162
  }
163
+ const ghostNode = new GhostTextRenderable(getNextId(GHOST_NODE_TAG), {});
164
+ insertNode(parent, ghostNode, anchor);
165
+ return ghostNode;
166
+ }
167
+ }
168
+
169
+ class GhostTextRenderable extends TextRenderable2 {
170
+ constructor(id, options) {
171
+ super(id, options);
172
+ }
173
+ static isGhostNode(node) {
174
+ return node instanceof GhostTextRenderable;
145
175
  }
146
176
  }
177
+
178
+ // src/reconciler.ts
147
179
  function _insertNode(parent, node, anchor) {
148
180
  log("Inserting node:", node.id, "into parent:", parent.id, "with anchor:", anchor?.id);
149
181
  if (node instanceof TextNode) {
150
- return insertTextNode(parent, node, anchor);
182
+ return node.insert(parent, anchor);
151
183
  }
152
- if (!(parent instanceof Renderable)) {
184
+ if (!(parent instanceof Renderable2)) {
153
185
  return;
154
186
  }
155
187
  if (anchor) {
@@ -167,9 +199,9 @@ function _insertNode(parent, node, anchor) {
167
199
  function _removeNode(parent, node) {
168
200
  log("Removing node:", node.id, "from parent:", parent.id);
169
201
  if (node instanceof TextNode) {
170
- return removeTextNode(parent, node);
202
+ return node.remove(parent);
171
203
  }
172
- if (parent instanceof Renderable && node instanceof Renderable) {
204
+ if (parent instanceof Renderable2 && node instanceof Renderable2) {
173
205
  parent.remove(node.id);
174
206
  node.destroyRecursively();
175
207
  }
@@ -203,30 +235,18 @@ var {
203
235
  plainText: `${value}`
204
236
  };
205
237
  const textNode = new TextNode(chunk);
206
- ChunkToTextNodeMap.set(chunk, textNode);
207
238
  return textNode;
208
239
  },
209
240
  replaceText(textNode, value) {
210
241
  log("Replacing text:", value, "in node:", textNode.id);
211
- if (textNode instanceof Renderable)
242
+ if (textNode instanceof Renderable2)
212
243
  return;
213
244
  const newChunk = {
214
245
  __isChunk: true,
215
246
  text: new TextEncoder().encode(value),
216
247
  plainText: value
217
248
  };
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
- }
249
+ textNode.replaceText(newChunk);
230
250
  },
231
251
  setProperty(node, name, value, prev) {
232
252
  if (node instanceof TextNode) {
@@ -339,10 +359,10 @@ var {
339
359
  },
340
360
  getFirstChild(node) {
341
361
  log("Getting first child of node:", node.id);
342
- if (node instanceof TextRenderable2) {
362
+ if (node instanceof TextRenderable3) {
343
363
  const chunk = node.content.chunks[0];
344
364
  if (chunk) {
345
- return ChunkToTextNodeMap.get(chunk);
365
+ return TextNode.getTextNodeFromChunk(chunk);
346
366
  } else {
347
367
  return;
348
368
  }
@@ -366,7 +386,7 @@ var {
366
386
  return;
367
387
  }
368
388
  if (node instanceof TextNode) {
369
- if (parent instanceof TextRenderable2) {
389
+ if (parent instanceof TextRenderable3) {
370
390
  const siblings2 = parent.content.chunks;
371
391
  const index2 = siblings2.indexOf(node.chunk);
372
392
  if (index2 === -1 || index2 === siblings2.length - 1) {
@@ -378,7 +398,7 @@ var {
378
398
  log("Next sibling is null for node:", node.id);
379
399
  return;
380
400
  }
381
- return ChunkToTextNodeMap.get(nextSibling2);
401
+ return TextNode.getTextNodeFromChunk(nextSibling2);
382
402
  }
383
403
  console.warn("Text parent is not a text node:", node.id);
384
404
  return;
@@ -405,13 +425,13 @@ var insertStyledText = (parent, value, current, marker) => {
405
425
  return current;
406
426
  if (current) {
407
427
  if (typeof current === "object" && "__isChunk" in current) {
408
- const node = ChunkToTextNodeMap.get(current);
428
+ const node = TextNode.getTextNodeFromChunk(current);
409
429
  if (node) {
410
430
  _removeNode(parent, node);
411
431
  }
412
432
  } else if (current instanceof StyledText) {
413
433
  for (const chunk of current.chunks) {
414
- const chunkNode = ChunkToTextNodeMap.get(chunk);
434
+ const chunkNode = TextNode.getTextNodeFromChunk(chunk);
415
435
  if (!chunkNode)
416
436
  continue;
417
437
  _removeNode(parent, chunkNode);
@@ -0,0 +1 @@
1
+ export declare const log: (...args: any[]) => void;