@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 +93 -40
- package/package.json +2 -2
- package/src/reconciler.d.ts +2 -1
- package/src/reconciler.js +93 -40
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
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
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
|
-
|
|
176
|
-
|
|
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) =>
|
|
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 (
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
|
243
|
-
if (!
|
|
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 (
|
|
248
|
-
const styledText =
|
|
300
|
+
if (textParent instanceof TextRenderable2) {
|
|
301
|
+
const styledText = textParent.content;
|
|
249
302
|
styledText.replace(newChunk, textNode.chunk);
|
|
250
|
-
|
|
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.
|
|
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.
|
|
33
|
+
"@opentui/core": "0.1.7"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/babel__core": "^7.20.5",
|
package/src/reconciler.d.ts
CHANGED
|
@@ -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
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
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
|
-
|
|
101
|
-
|
|
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) =>
|
|
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 (
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
|
168
|
-
if (!
|
|
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 (
|
|
173
|
-
const styledText =
|
|
225
|
+
if (textParent instanceof TextRenderable2) {
|
|
226
|
+
const styledText = textParent.content;
|
|
174
227
|
styledText.replace(newChunk, textNode.chunk);
|
|
175
|
-
|
|
228
|
+
textParent.content = styledText;
|
|
176
229
|
textNode.chunk = newChunk;
|
|
177
230
|
ChunkToTextNodeMap.set(newChunk, textNode);
|
|
178
231
|
}
|