@opentui/solid 0.1.6 → 0.1.7

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.js CHANGED
@@ -106,7 +106,6 @@ var elements = {
106
106
 
107
107
  // src/reconciler.ts
108
108
  import {
109
- GroupRenderable as GroupRenderable2,
110
109
  InputRenderable as InputRenderable2,
111
110
  InputRenderableEvents,
112
111
  Renderable,
@@ -131,10 +130,13 @@ function getNextId(elementType) {
131
130
  }
132
131
 
133
132
  // src/reconciler.ts
133
+ var GHOST_NODE_TAG = "text-ghost";
134
+
134
135
  class TextNode {
135
136
  id;
136
137
  chunk;
137
138
  parent;
139
+ textParent;
138
140
  constructor(chunk) {
139
141
  this.id = getNextId("text-node");
140
142
  this.chunk = chunk;
@@ -144,43 +146,96 @@ var ChunkToTextNodeMap = new WeakMap;
144
146
  var log = (...args) => {
145
147
  console.log("[Reconciler]", ...args);
146
148
  };
147
- function _insertNode(parent, node, anchor) {
148
- log("Inserting node:", node.id, "into parent:", parent.id, "with anchor:", anchor?.id);
149
- if (node instanceof TextNode) {
150
- if (!(parent instanceof TextRenderable2)) {
151
- console.warn(`Cannot insert text:"${node.chunk.plainText}" unless wrapped with a <text> element.`);
152
- const ghostNode = new GroupRenderable2(getNextId("ghost-group"), {});
153
- ghostNode.getLayoutNode().yogaNode.setDisplay(2);
154
- if (parent instanceof Renderable) {
155
- parent.add(ghostNode);
156
- }
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;
159
+ }
160
+ }
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;
180
+ }
181
+ node.textParent = textParent;
182
+ const 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");
157
187
  return;
158
188
  }
159
- const styledText = parent.content;
160
- if (anchor && anchor instanceof TextNode) {
161
- const anchorIndex = styledText.chunks.indexOf(anchor.chunk);
162
- if (anchorIndex == -1) {
163
- console.log("anchor not found");
164
- return;
165
- }
166
- styledText.insert(node.chunk, anchorIndex);
189
+ styledText.insert(node.chunk, anchorIndex);
190
+ } else {
191
+ const firstChunk = textParent.content.chunks[0];
192
+ if (firstChunk && !ChunkToTextNodeMap.has(firstChunk)) {
193
+ styledText.replace(node.chunk, firstChunk);
167
194
  } else {
168
- const firstChunk = parent.content.chunks[0];
169
- if (firstChunk && !ChunkToTextNodeMap.has(firstChunk)) {
170
- styledText.replace(node.chunk, firstChunk);
171
- } else {
172
- styledText.insert(node.chunk);
173
- }
195
+ styledText.insert(node.chunk);
174
196
  }
175
- parent.content = styledText;
176
- node.parent = parent;
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);
177
205
  return;
178
206
  }
207
+ if (parent === node.textParent && parent instanceof TextRenderable2) {
208
+ ChunkToTextNodeMap.delete(node.chunk);
209
+ const styledText = parent.content;
210
+ styledText.remove(node.chunk);
211
+ parent.content = styledText;
212
+ } else if (node.textParent) {
213
+ ChunkToTextNodeMap.delete(node.chunk);
214
+ const styledText = node.textParent.content;
215
+ styledText.remove(node.chunk);
216
+ if (styledText.chunks.length > 0) {
217
+ node.textParent.content = styledText;
218
+ } else {
219
+ node.parent?.remove(node.textParent.id);
220
+ node.textParent.destroyRecursively();
221
+ }
222
+ }
223
+ }
224
+ function _insertNode(parent, node, anchor) {
225
+ log("Inserting node:", node.id, "into parent:", parent.id, "with anchor:", anchor?.id);
226
+ if (node instanceof TextNode) {
227
+ return insertTextNode(parent, node, anchor);
228
+ }
179
229
  if (!(parent instanceof Renderable)) {
180
230
  return;
181
231
  }
182
232
  if (anchor) {
183
- const anchorIndex = parent.getChildren().findIndex((el) => el.id === anchor.id);
233
+ const anchorIndex = parent.getChildren().findIndex((el) => {
234
+ if (anchor instanceof TextNode) {
235
+ return el.id === anchor.textParent?.id;
236
+ }
237
+ return el.id === anchor.id;
238
+ });
184
239
  parent.add(node, anchorIndex);
185
240
  } else {
186
241
  parent.add(node);
@@ -188,14 +243,12 @@ function _insertNode(parent, node, anchor) {
188
243
  }
189
244
  function _removeNode(parent, node) {
190
245
  log("Removing node:", node.id, "from parent:", parent.id);
191
- if (parent instanceof TextRenderable2 && node instanceof TextNode) {
192
- ChunkToTextNodeMap.delete(node.chunk);
193
- const styledText = parent.content;
194
- styledText.remove(node.chunk);
195
- parent.content = styledText;
196
- } else if (parent instanceof Renderable && node instanceof Renderable) {
197
- node.destroyRecursively();
246
+ if (node instanceof TextNode) {
247
+ return removeTextNode(parent, node);
248
+ }
249
+ if (parent instanceof Renderable && node instanceof Renderable) {
198
250
  parent.remove(node.id);
251
+ node.destroyRecursively();
199
252
  }
200
253
  }
201
254
  var {
@@ -239,15 +292,15 @@ var {
239
292
  text: new TextEncoder().encode(value),
240
293
  plainText: value
241
294
  };
242
- const parent = textNode.parent;
243
- if (!parent) {
295
+ const textParent = textNode.textParent;
296
+ if (!textParent) {
244
297
  log("No parent found for text node:", textNode.id);
245
298
  return;
246
299
  }
247
- if (parent instanceof TextRenderable2) {
248
- const styledText = parent.content;
300
+ if (textParent instanceof TextRenderable2) {
301
+ const styledText = textParent.content;
249
302
  styledText.replace(newChunk, textNode.chunk);
250
- parent.content = styledText;
303
+ textParent.content = styledText;
251
304
  textNode.chunk = newChunk;
252
305
  ChunkToTextNodeMap.set(newChunk, textNode);
253
306
  }
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.6",
7
+ "version": "0.1.7",
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.6"
33
+ "@opentui/core": "0.1.7"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/babel__core": "^7.20.5",
@@ -1,8 +1,9 @@
1
- import { Renderable, type TextChunk } from "@opentui/core";
1
+ import { Renderable, TextRenderable, type TextChunk } from "@opentui/core";
2
2
  declare class TextNode {
3
3
  id: string;
4
4
  chunk: TextChunk;
5
5
  parent?: Renderable;
6
+ textParent?: TextRenderable;
6
7
  constructor(chunk: TextChunk);
7
8
  }
8
9
  type DomNode = Renderable | TextNode;
package/src/reconciler.js CHANGED
@@ -1,7 +1,6 @@
1
1
  // @bun
2
2
  // src/reconciler.ts
3
3
  import {
4
- GroupRenderable as GroupRenderable2,
5
4
  InputRenderable as InputRenderable2,
6
5
  InputRenderableEvents,
7
6
  Renderable,
@@ -56,10 +55,13 @@ function getNextId(elementType) {
56
55
  }
57
56
 
58
57
  // src/reconciler.ts
58
+ var GHOST_NODE_TAG = "text-ghost";
59
+
59
60
  class TextNode {
60
61
  id;
61
62
  chunk;
62
63
  parent;
64
+ textParent;
63
65
  constructor(chunk) {
64
66
  this.id = getNextId("text-node");
65
67
  this.chunk = chunk;
@@ -69,43 +71,96 @@ var ChunkToTextNodeMap = new WeakMap;
69
71
  var log = (...args) => {
70
72
  console.log("[Reconciler]", ...args);
71
73
  };
72
- function _insertNode(parent, node, anchor) {
73
- log("Inserting node:", node.id, "into parent:", parent.id, "with anchor:", anchor?.id);
74
- if (node instanceof TextNode) {
75
- if (!(parent instanceof TextRenderable2)) {
76
- console.warn(`Cannot insert text:"${node.chunk.plainText}" unless wrapped with a <text> element.`);
77
- const ghostNode = new GroupRenderable2(getNextId("ghost-group"), {});
78
- ghostNode.getLayoutNode().yogaNode.setDisplay(2);
79
- if (parent instanceof Renderable) {
80
- parent.add(ghostNode);
81
- }
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;
84
+ }
85
+ }
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;
105
+ }
106
+ node.textParent = textParent;
107
+ const 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");
82
112
  return;
83
113
  }
84
- const styledText = parent.content;
85
- if (anchor && anchor instanceof TextNode) {
86
- const anchorIndex = styledText.chunks.indexOf(anchor.chunk);
87
- if (anchorIndex == -1) {
88
- console.log("anchor not found");
89
- return;
90
- }
91
- styledText.insert(node.chunk, anchorIndex);
114
+ styledText.insert(node.chunk, anchorIndex);
115
+ } else {
116
+ const firstChunk = textParent.content.chunks[0];
117
+ if (firstChunk && !ChunkToTextNodeMap.has(firstChunk)) {
118
+ styledText.replace(node.chunk, firstChunk);
92
119
  } else {
93
- const firstChunk = parent.content.chunks[0];
94
- if (firstChunk && !ChunkToTextNodeMap.has(firstChunk)) {
95
- styledText.replace(node.chunk, firstChunk);
96
- } else {
97
- styledText.insert(node.chunk);
98
- }
120
+ styledText.insert(node.chunk);
99
121
  }
100
- parent.content = styledText;
101
- node.parent = parent;
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);
102
130
  return;
103
131
  }
132
+ if (parent === node.textParent && parent instanceof TextRenderable2) {
133
+ ChunkToTextNodeMap.delete(node.chunk);
134
+ const styledText = parent.content;
135
+ styledText.remove(node.chunk);
136
+ parent.content = styledText;
137
+ } else if (node.textParent) {
138
+ ChunkToTextNodeMap.delete(node.chunk);
139
+ const styledText = node.textParent.content;
140
+ styledText.remove(node.chunk);
141
+ if (styledText.chunks.length > 0) {
142
+ node.textParent.content = styledText;
143
+ } else {
144
+ node.parent?.remove(node.textParent.id);
145
+ node.textParent.destroyRecursively();
146
+ }
147
+ }
148
+ }
149
+ function _insertNode(parent, node, anchor) {
150
+ log("Inserting node:", node.id, "into parent:", parent.id, "with anchor:", anchor?.id);
151
+ if (node instanceof TextNode) {
152
+ return insertTextNode(parent, node, anchor);
153
+ }
104
154
  if (!(parent instanceof Renderable)) {
105
155
  return;
106
156
  }
107
157
  if (anchor) {
108
- const anchorIndex = parent.getChildren().findIndex((el) => el.id === anchor.id);
158
+ const anchorIndex = parent.getChildren().findIndex((el) => {
159
+ if (anchor instanceof TextNode) {
160
+ return el.id === anchor.textParent?.id;
161
+ }
162
+ return el.id === anchor.id;
163
+ });
109
164
  parent.add(node, anchorIndex);
110
165
  } else {
111
166
  parent.add(node);
@@ -113,14 +168,12 @@ function _insertNode(parent, node, anchor) {
113
168
  }
114
169
  function _removeNode(parent, node) {
115
170
  log("Removing node:", node.id, "from parent:", parent.id);
116
- if (parent instanceof TextRenderable2 && node instanceof TextNode) {
117
- ChunkToTextNodeMap.delete(node.chunk);
118
- const styledText = parent.content;
119
- styledText.remove(node.chunk);
120
- parent.content = styledText;
121
- } else if (parent instanceof Renderable && node instanceof Renderable) {
122
- node.destroyRecursively();
171
+ if (node instanceof TextNode) {
172
+ return removeTextNode(parent, node);
173
+ }
174
+ if (parent instanceof Renderable && node instanceof Renderable) {
123
175
  parent.remove(node.id);
176
+ node.destroyRecursively();
124
177
  }
125
178
  }
126
179
  var {
@@ -164,15 +217,15 @@ var {
164
217
  text: new TextEncoder().encode(value),
165
218
  plainText: value
166
219
  };
167
- const parent = textNode.parent;
168
- if (!parent) {
220
+ const textParent = textNode.textParent;
221
+ if (!textParent) {
169
222
  log("No parent found for text node:", textNode.id);
170
223
  return;
171
224
  }
172
- if (parent instanceof TextRenderable2) {
173
- const styledText = parent.content;
225
+ if (textParent instanceof TextRenderable2) {
226
+ const styledText = textParent.content;
174
227
  styledText.replace(newChunk, textNode.chunk);
175
- parent.content = styledText;
228
+ textParent.content = styledText;
176
229
  textNode.chunk = newChunk;
177
230
  ChunkToTextNodeMap.set(newChunk, textNode);
178
231
  }