elementdrawing 1.0.0

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.
Files changed (78) hide show
  1. package/LICENSE +21 -0
  2. package/dist/elementdrawing.min.js +3 -0
  3. package/dist/elementdrawing.min.js.LICENSE.txt +8 -0
  4. package/dist/elementdrawing.min.js.map +1 -0
  5. package/dist/index.html +1 -0
  6. package/package.json +127 -0
  7. package/src/core/bridge.h +855 -0
  8. package/src/core/diff.c +900 -0
  9. package/src/core/element.c +1078 -0
  10. package/src/core/event.c +813 -0
  11. package/src/core/fiber.c +1027 -0
  12. package/src/core/hooks.c +919 -0
  13. package/src/core/renderer.c +963 -0
  14. package/src/core/scheduler.c +702 -0
  15. package/src/core/state.c +803 -0
  16. package/src/css/animations.css +779 -0
  17. package/src/css/base.css +615 -0
  18. package/src/css/components.css +1311 -0
  19. package/src/css/tailwind.css +370 -0
  20. package/src/css/themes.css +517 -0
  21. package/src/css/utilities.css +475 -0
  22. package/src/index.js +746 -0
  23. package/src/js/animation.js +655 -0
  24. package/src/js/dom.js +665 -0
  25. package/src/js/events.js +585 -0
  26. package/src/js/http.js +446 -0
  27. package/src/js/index.js +26 -0
  28. package/src/js/router.js +483 -0
  29. package/src/js/store.js +539 -0
  30. package/src/js/utils.js +593 -0
  31. package/src/js/validator.js +529 -0
  32. package/src/jsx/components/Accordion.jsx +210 -0
  33. package/src/jsx/components/Alert.jsx +169 -0
  34. package/src/jsx/components/Avatar.jsx +214 -0
  35. package/src/jsx/components/Badge.jsx +136 -0
  36. package/src/jsx/components/Breadcrumb.jsx +200 -0
  37. package/src/jsx/components/Button.jsx +188 -0
  38. package/src/jsx/components/Card.jsx +192 -0
  39. package/src/jsx/components/Carousel.jsx +278 -0
  40. package/src/jsx/components/Checkbox.jsx +215 -0
  41. package/src/jsx/components/Dialog.jsx +242 -0
  42. package/src/jsx/components/Drawer.jsx +190 -0
  43. package/src/jsx/components/Dropdown.jsx +268 -0
  44. package/src/jsx/components/Form.jsx +274 -0
  45. package/src/jsx/components/Input.jsx +285 -0
  46. package/src/jsx/components/Menu.jsx +276 -0
  47. package/src/jsx/components/Modal.jsx +274 -0
  48. package/src/jsx/components/Navbar.jsx +292 -0
  49. package/src/jsx/components/Pagination.jsx +268 -0
  50. package/src/jsx/components/Progress.jsx +252 -0
  51. package/src/jsx/components/Radio.jsx +208 -0
  52. package/src/jsx/components/Select.jsx +397 -0
  53. package/src/jsx/components/Sidebar.jsx +250 -0
  54. package/src/jsx/components/Slider.jsx +310 -0
  55. package/src/jsx/components/Spinner.jsx +198 -0
  56. package/src/jsx/components/Switch.jsx +201 -0
  57. package/src/jsx/components/Table.jsx +332 -0
  58. package/src/jsx/components/Tabs.jsx +227 -0
  59. package/src/jsx/components/Textarea.jsx +212 -0
  60. package/src/jsx/components/Toast.jsx +270 -0
  61. package/src/jsx/components/Tooltip.jsx +178 -0
  62. package/src/jsx/components/Typography.jsx +299 -0
  63. package/src/jsx/components/index.jsx +70 -0
  64. package/src/jsx/core/element.js +3 -0
  65. package/src/jsx/hooks/index.js +356 -0
  66. package/src/jsx/hooks/useCallback.js +472 -0
  67. package/src/jsx/hooks/useContext.js +586 -0
  68. package/src/jsx/hooks/useEffect.js +704 -0
  69. package/src/jsx/hooks/useLayoutEffect.js +508 -0
  70. package/src/jsx/hooks/useMemo.js +689 -0
  71. package/src/jsx/hooks/useReducer.js +729 -0
  72. package/src/jsx/hooks/useRef.js +542 -0
  73. package/src/jsx/hooks/useState.js +854 -0
  74. package/src/jsx/runtime/commit.js +903 -0
  75. package/src/jsx/runtime/createElement.js +860 -0
  76. package/src/jsx/runtime/index.js +356 -0
  77. package/src/jsx/runtime/reconcile.js +687 -0
  78. package/src/jsx/runtime/render.js +914 -0
@@ -0,0 +1,900 @@
1
+ /*
2
+ * diff.c - ElementDrawing Core: Diff Algorithm and Patch System
3
+ *
4
+ * Implements tree diffing with key-based reconciliation, an O(n)
5
+ * child diffing algorithm, prop diffing, diff result management,
6
+ * patch queue operations, and patch application to the DOM.
7
+ */
8
+
9
+ #include "bridge.h"
10
+
11
+ /* ================================================================
12
+ * Diff Operation Management
13
+ * ================================================================ */
14
+
15
+ EDDiffOperation* ed_diff_operation_create(EDDiffOpType type, uint32_t index)
16
+ {
17
+ EDDiffOperation* op = (EDDiffOperation*)calloc(1, sizeof(EDDiffOperation));
18
+ if (!op) return NULL;
19
+
20
+ op->type = type;
21
+ op->index = index;
22
+ op->new_index = 0;
23
+ op->node = NULL;
24
+ op->new_node = NULL;
25
+ op->prop_changes = NULL;
26
+ op->prop_change_count = 0;
27
+ op->next = NULL;
28
+
29
+ return op;
30
+ }
31
+
32
+ void ed_diff_operation_destroy(EDDiffOperation* op)
33
+ {
34
+ if (!op) return;
35
+ /* Destroy prop changes */
36
+ EDVirtualProp* prop = op->prop_changes;
37
+ while (prop) {
38
+ EDVirtualProp* next = prop->next;
39
+ free(prop);
40
+ prop = next;
41
+ }
42
+ free(op);
43
+ }
44
+
45
+ /* ================================================================
46
+ * Diff Result Management
47
+ * ================================================================ */
48
+
49
+ static EDDiffResult* ed_diff_result_create(void)
50
+ {
51
+ EDDiffResult* result = (EDDiffResult*)calloc(1, sizeof(EDDiffResult));
52
+ if (!result) return NULL;
53
+
54
+ result->operations = NULL;
55
+ result->operation_count = 0;
56
+ result->insert_count = 0;
57
+ result->delete_count = 0;
58
+ result->update_count = 0;
59
+ result->move_count = 0;
60
+ result->reorder_count = 0;
61
+ result->has_changes = false;
62
+
63
+ return result;
64
+ }
65
+
66
+ void ed_diff_result_destroy(EDDiffResult* result)
67
+ {
68
+ if (!result) return;
69
+
70
+ EDDiffOperation* op = result->operations;
71
+ while (op) {
72
+ EDDiffOperation* next = op->next;
73
+ ed_diff_operation_destroy(op);
74
+ op = next;
75
+ }
76
+
77
+ free(result);
78
+ }
79
+
80
+ uint32_t ed_diff_count_operations(EDDiffResult* result)
81
+ {
82
+ return result ? result->operation_count : 0;
83
+ }
84
+
85
+ bool ed_diff_has_changes(EDDiffResult* result)
86
+ {
87
+ return result ? result->has_changes : false;
88
+ }
89
+
90
+ /* ================================================================
91
+ * Internal: Append Operation to Result
92
+ * ================================================================ */
93
+
94
+ static void ed_diff_result_append(EDDiffResult* result, EDDiffOperation* op)
95
+ {
96
+ if (!result || !op) return;
97
+
98
+ if (!result->operations) {
99
+ result->operations = op;
100
+ } else {
101
+ /* Append to end */
102
+ EDDiffOperation* tail = result->operations;
103
+ while (tail->next) {
104
+ tail = tail->next;
105
+ }
106
+ tail->next = op;
107
+ }
108
+
109
+ result->operation_count++;
110
+ result->has_changes = true;
111
+
112
+ switch (op->type) {
113
+ case ED_DIFF_INSERT: result->insert_count++; break;
114
+ case ED_DIFF_DELETE: result->delete_count++; break;
115
+ case ED_DIFF_UPDATE: result->update_count++; break;
116
+ case ED_DIFF_MOVE: result->move_count++; break;
117
+ case ED_DIFF_REORDER: result->reorder_count++; break;
118
+ default: break;
119
+ }
120
+ }
121
+
122
+ /* ================================================================
123
+ * Prop Diffing
124
+ * ================================================================ */
125
+
126
+ static EDVirtualProp* ed_diff_props(EDVirtualNode* old_node, EDVirtualNode* new_node,
127
+ uint32_t* change_count)
128
+ {
129
+ if (change_count) *change_count = 0;
130
+
131
+ EDVirtualProp* changes = NULL;
132
+ EDVirtualProp** tail = &changes;
133
+
134
+ /* Find added and updated props */
135
+ EDVirtualProp* new_prop = new_node ? new_node->props : NULL;
136
+ while (new_prop) {
137
+ EDVirtualProp* old_prop = old_node ? ed_vnode_get_prop(old_node, new_prop->key) : NULL;
138
+
139
+ if (!old_prop) {
140
+ /* Prop added */
141
+ EDVirtualProp* change = (EDVirtualProp*)calloc(1, sizeof(EDVirtualProp));
142
+ if (change) {
143
+ strncpy(change->key, new_prop->key, ED_MAX_PROP_KEY_LEN - 1);
144
+ strncpy(change->value, new_prop->value, ED_MAX_PROP_VALUE_LEN - 1);
145
+ change->type = new_prop->type;
146
+ change->hash = new_prop->hash;
147
+ change->is_event = new_prop->is_event;
148
+ change->is_style = new_prop->is_style;
149
+ change->is_class = new_prop->is_class;
150
+ change->is_ref = new_prop->is_ref;
151
+ *tail = change;
152
+ tail = &change->next;
153
+ if (change_count) (*change_count)++;
154
+ }
155
+ } else if (strcmp(old_prop->value, new_prop->value) != 0 || old_prop->type != new_prop->type) {
156
+ /* Prop updated */
157
+ EDVirtualProp* change = (EDVirtualProp*)calloc(1, sizeof(EDVirtualProp));
158
+ if (change) {
159
+ strncpy(change->key, new_prop->key, ED_MAX_PROP_KEY_LEN - 1);
160
+ strncpy(change->value, new_prop->value, ED_MAX_PROP_VALUE_LEN - 1);
161
+ change->type = new_prop->type;
162
+ change->hash = new_prop->hash;
163
+ *tail = change;
164
+ tail = &change->next;
165
+ if (change_count) (*change_count)++;
166
+ }
167
+ }
168
+
169
+ new_prop = new_prop->next;
170
+ }
171
+
172
+ /* Find removed props */
173
+ EDVirtualProp* old_prop = old_node ? old_node->props : NULL;
174
+ while (old_prop) {
175
+ EDVirtualProp* found = new_node ? ed_vnode_get_prop(new_node, old_prop->key) : NULL;
176
+ if (!found) {
177
+ /* Prop removed - mark with empty value */
178
+ EDVirtualProp* change = (EDVirtualProp*)calloc(1, sizeof(EDVirtualProp));
179
+ if (change) {
180
+ strncpy(change->key, old_prop->key, ED_MAX_PROP_KEY_LEN - 1);
181
+ change->value[0] = '\0'; /* Empty = removed */
182
+ change->type = old_prop->type;
183
+ change->hash = old_prop->hash;
184
+ *tail = change;
185
+ tail = &change->next;
186
+ if (change_count) (*change_count)++;
187
+ }
188
+ }
189
+ old_prop = old_prop->next;
190
+ }
191
+
192
+ return changes;
193
+ }
194
+
195
+ /* ================================================================
196
+ * Key-based Child Diffing: O(n) Algorithm
197
+ *
198
+ * This implements the "flag and sweep" approach:
199
+ * 1. Build a map of old children by key
200
+ * 2. Walk new children, matching keys to old
201
+ * 3. Mark matched old children, delete unmarked
202
+ * 4. Track moves via last placed index
203
+ * ================================================================ */
204
+
205
+ /* Key-to-index map for fast lookups */
206
+ typedef struct EDChildKeyEntry {
207
+ char key[ED_MAX_KEY_LEN];
208
+ uint32_t hash;
209
+ uint32_t old_index;
210
+ EDVirtualNode* old_node;
211
+ struct EDChildKeyEntry* next;
212
+ } EDChildKeyEntry;
213
+
214
+ typedef struct EDChildKeyMap {
215
+ EDChildKeyEntry* buckets[ED_DIFF_KEY_CACHE_SIZE];
216
+ uint32_t count;
217
+ } EDChildKeyMap;
218
+
219
+ static void ed_child_key_map_init(EDChildKeyMap* map)
220
+ {
221
+ if (!map) return;
222
+ memset(map, 0, sizeof(EDChildKeyMap));
223
+ }
224
+
225
+ static void ed_child_key_map_insert(EDChildKeyMap* map, const char* key, uint32_t hash,
226
+ uint32_t old_index, EDVirtualNode* node)
227
+ {
228
+ if (!map || !key) return;
229
+ EDChildKeyEntry* entry = (EDChildKeyEntry*)calloc(1, sizeof(EDChildKeyEntry));
230
+ if (!entry) return;
231
+
232
+ strncpy(entry->key, key, ED_MAX_KEY_LEN - 1);
233
+ entry->key[ED_MAX_KEY_LEN - 1] = '\0';
234
+ entry->hash = hash;
235
+ entry->old_index = old_index;
236
+ entry->old_node = node;
237
+
238
+ uint32_t bucket = hash % ED_DIFF_KEY_CACHE_SIZE;
239
+ entry->next = map->buckets[bucket];
240
+ map->buckets[bucket] = entry;
241
+ map->count++;
242
+ }
243
+
244
+ static EDChildKeyEntry* ed_child_key_map_find(EDChildKeyMap* map, const char* key,
245
+ uint32_t hash)
246
+ {
247
+ if (!map || !key) return NULL;
248
+ uint32_t bucket = hash % ED_DIFF_KEY_CACHE_SIZE;
249
+ EDChildKeyEntry* entry = map->buckets[bucket];
250
+ while (entry) {
251
+ if (entry->hash == hash && strcmp(entry->key, key) == 0) {
252
+ return entry;
253
+ }
254
+ entry = entry->next;
255
+ }
256
+ return NULL;
257
+ }
258
+
259
+ static void ed_child_key_map_destroy(EDChildKeyMap* map)
260
+ {
261
+ if (!map) return;
262
+ for (uint32_t i = 0; i < ED_DIFF_KEY_CACHE_SIZE; i++) {
263
+ EDChildKeyEntry* entry = map->buckets[i];
264
+ while (entry) {
265
+ EDChildKeyEntry* next = entry->next;
266
+ free(entry);
267
+ entry = next;
268
+ }
269
+ map->buckets[i] = NULL;
270
+ }
271
+ map->count = 0;
272
+ }
273
+
274
+ static void ed_diff_children_keyed(EDVirtualNode* old_parent, EDVirtualNode* new_parent,
275
+ EDDiffResult* result)
276
+ {
277
+ EDChildKeyMap key_map;
278
+ ed_child_key_map_init(&key_map);
279
+
280
+ /* Build key map from old children */
281
+ for (uint32_t i = 0; i < old_parent->child_count; i++) {
282
+ EDVirtualNode* child = old_parent->children[i];
283
+ if (child && child->key[0] != '\0') {
284
+ uint32_t h = ed_hash_fnv1a(child->key);
285
+ ed_child_key_map_insert(&key_map, child->key, h, i, child);
286
+ }
287
+ }
288
+
289
+ /* Track which old children have been matched */
290
+ bool* matched = NULL;
291
+ if (old_parent->child_count > 0) {
292
+ matched = (bool*)calloc(old_parent->child_count, sizeof(bool));
293
+ }
294
+
295
+ uint32_t last_placed_index = 0;
296
+
297
+ /* Walk new children */
298
+ for (uint32_t i = 0; i < new_parent->child_count; i++) {
299
+ EDVirtualNode* new_child = new_parent->children[i];
300
+ if (!new_child) continue;
301
+
302
+ if (new_child->key[0] != '\0') {
303
+ /* Keyed child: lookup in map */
304
+ uint32_t h = ed_hash_fnv1a(new_child->key);
305
+ EDChildKeyEntry* entry = ed_child_key_map_find(&key_map, new_child->key, h);
306
+
307
+ if (entry) {
308
+ EDVirtualNode* old_child = entry->old_node;
309
+ matched[entry->old_index] = true;
310
+
311
+ if (old_child->type == new_child->type &&
312
+ strcmp(old_child->tag, new_child->tag) == 0) {
313
+ /* Same type and key - check for updates */
314
+ uint32_t prop_changes = 0;
315
+ EDVirtualProp* changes = ed_diff_props(old_child, new_child, &prop_changes);
316
+
317
+ if (prop_changes > 0 || strcmp(old_child->text, new_child->text) != 0) {
318
+ EDDiffOperation* op = ed_diff_operation_create(ED_DIFF_UPDATE, i);
319
+ op->new_index = i;
320
+ op->node = old_child;
321
+ op->new_node = new_child;
322
+ op->prop_changes = changes;
323
+ op->prop_change_count = prop_changes;
324
+ ed_diff_result_append(result, op);
325
+ }
326
+
327
+ /* Check if moved */
328
+ if (entry->old_index < last_placed_index) {
329
+ EDDiffOperation* op = ed_diff_operation_create(ED_DIFF_MOVE, entry->old_index);
330
+ op->new_index = i;
331
+ op->node = old_child;
332
+ op->new_node = new_child;
333
+ ed_diff_result_append(result, op);
334
+ } else {
335
+ last_placed_index = entry->old_index;
336
+ }
337
+
338
+ /* Recurse into children */
339
+ if (old_child->child_count > 0 || new_child->child_count > 0) {
340
+ EDDiffResult* child_diff = ed_diff_compute(old_child, new_child);
341
+ if (child_diff && child_diff->has_changes) {
342
+ /* Merge child operations */
343
+ EDDiffOperation* cop = child_diff->operations;
344
+ while (cop) {
345
+ EDDiffOperation* next = cop->next;
346
+ cop->next = NULL;
347
+ ed_diff_result_append(result, cop);
348
+ cop = next;
349
+ }
350
+ child_diff->operations = NULL;
351
+ }
352
+ ed_diff_result_destroy(child_diff);
353
+ }
354
+ } else {
355
+ /* Type changed with same key - replace */
356
+ EDDiffOperation* op = ed_diff_operation_create(ED_DIFF_REPLACE, entry->old_index);
357
+ op->new_index = i;
358
+ op->node = old_child;
359
+ op->new_node = new_child;
360
+ ed_diff_result_append(result, op);
361
+ }
362
+ } else {
363
+ /* New keyed child - insert */
364
+ EDDiffOperation* op = ed_diff_operation_create(ED_DIFF_INSERT, i);
365
+ op->new_index = i;
366
+ op->new_node = new_child;
367
+ ed_diff_result_append(result, op);
368
+ }
369
+ } else {
370
+ /* Unkeyed child: positional matching */
371
+ if (i < old_parent->child_count) {
372
+ EDVirtualNode* old_child = old_parent->children[i];
373
+ if (old_child && old_child->key[0] == '\0') {
374
+ if (matched) matched[i] = true;
375
+
376
+ if (old_child->type == new_child->type &&
377
+ strcmp(old_child->tag, new_child->tag) == 0) {
378
+ /* Same type - check for updates */
379
+ uint32_t prop_changes = 0;
380
+ EDVirtualProp* changes = ed_diff_props(old_child, new_child, &prop_changes);
381
+
382
+ if (prop_changes > 0 || strcmp(old_child->text, new_child->text) != 0) {
383
+ EDDiffOperation* op = ed_diff_operation_create(ED_DIFF_UPDATE, i);
384
+ op->new_index = i;
385
+ op->node = old_child;
386
+ op->new_node = new_child;
387
+ op->prop_changes = changes;
388
+ op->prop_change_count = prop_changes;
389
+ ed_diff_result_append(result, op);
390
+ }
391
+
392
+ /* Recurse */
393
+ if (old_child->child_count > 0 || new_child->child_count > 0) {
394
+ EDDiffResult* child_diff = ed_diff_compute(old_child, new_child);
395
+ if (child_diff && child_diff->has_changes) {
396
+ EDDiffOperation* cop = child_diff->operations;
397
+ while (cop) {
398
+ EDDiffOperation* next = cop->next;
399
+ cop->next = NULL;
400
+ ed_diff_result_append(result, cop);
401
+ cop = next;
402
+ }
403
+ child_diff->operations = NULL;
404
+ }
405
+ ed_diff_result_destroy(child_diff);
406
+ }
407
+ } else {
408
+ /* Different type - replace */
409
+ EDDiffOperation* op = ed_diff_operation_create(ED_DIFF_REPLACE, i);
410
+ op->new_index = i;
411
+ op->node = old_child;
412
+ op->new_node = new_child;
413
+ ed_diff_result_append(result, op);
414
+ }
415
+ } else {
416
+ /* Old child has key, new doesn't - insert new, old stays for deletion */
417
+ EDDiffOperation* op = ed_diff_operation_create(ED_DIFF_INSERT, i);
418
+ op->new_index = i;
419
+ op->new_node = new_child;
420
+ ed_diff_result_append(result, op);
421
+ }
422
+ } else {
423
+ /* Extra new child - insert */
424
+ EDDiffOperation* op = ed_diff_operation_create(ED_DIFF_INSERT, i);
425
+ op->new_index = i;
426
+ op->new_node = new_child;
427
+ ed_diff_result_append(result, op);
428
+ }
429
+ }
430
+ }
431
+
432
+ /* Delete unmatched old children */
433
+ if (matched) {
434
+ for (uint32_t i = 0; i < old_parent->child_count; i++) {
435
+ if (!matched[i]) {
436
+ EDDiffOperation* op = ed_diff_operation_create(ED_DIFF_DELETE, i);
437
+ op->node = old_parent->children[i];
438
+ ed_diff_result_append(result, op);
439
+ }
440
+ }
441
+ free(matched);
442
+ }
443
+
444
+ ed_child_key_map_destroy(&key_map);
445
+ }
446
+
447
+ /* ================================================================
448
+ * Main Diff Computation
449
+ * ================================================================ */
450
+
451
+ EDDiffResult* ed_diff_compute(EDVirtualNode* old_tree, EDVirtualNode* new_tree)
452
+ {
453
+ EDDiffResult* result = ed_diff_result_create();
454
+ if (!result) return NULL;
455
+
456
+ /* Both NULL - no changes */
457
+ if (!old_tree && !new_tree) {
458
+ result->has_changes = false;
459
+ return result;
460
+ }
461
+
462
+ /* Old is NULL - everything is new */
463
+ if (!old_tree) {
464
+ EDDiffOperation* op = ed_diff_operation_create(ED_DIFF_INSERT, 0);
465
+ op->new_node = new_tree;
466
+ ed_diff_result_append(result, op);
467
+
468
+ /* Recursively mark all new children as inserts */
469
+ for (uint32_t i = 0; i < new_tree->child_count; i++) {
470
+ EDDiffOperation* child_op = ed_diff_operation_create(ED_DIFF_INSERT, i);
471
+ child_op->new_node = new_tree->children[i];
472
+ child_op->new_index = i;
473
+ ed_diff_result_append(result, child_op);
474
+ }
475
+ return result;
476
+ }
477
+
478
+ /* New is NULL - everything is deleted */
479
+ if (!new_tree) {
480
+ EDDiffOperation* op = ed_diff_operation_create(ED_DIFF_DELETE, 0);
481
+ op->node = old_tree;
482
+ ed_diff_result_append(result, op);
483
+
484
+ for (uint32_t i = 0; i < old_tree->child_count; i++) {
485
+ EDDiffOperation* child_op = ed_diff_operation_create(ED_DIFF_DELETE, i);
486
+ child_op->node = old_tree->children[i];
487
+ ed_diff_result_append(result, child_op);
488
+ }
489
+ return result;
490
+ }
491
+
492
+ /* Both exist - compare types */
493
+ if (old_tree->type != new_tree->type ||
494
+ (old_tree->type == ED_NODE_ELEMENT && strcmp(old_tree->tag, new_tree->tag) != 0) ||
495
+ (old_tree->type == ED_NODE_TEXT && strcmp(old_tree->text, new_tree->text) != 0)) {
496
+ /* Different type or tag - replace */
497
+ EDDiffOperation* op = ed_diff_operation_create(ED_DIFF_REPLACE, 0);
498
+ op->node = old_tree;
499
+ op->new_node = new_tree;
500
+ ed_diff_result_append(result, op);
501
+ return result;
502
+ }
503
+
504
+ /* Same type - diff props */
505
+ uint32_t prop_change_count = 0;
506
+ EDVirtualProp* prop_changes = ed_diff_props(old_tree, new_tree, &prop_change_count);
507
+
508
+ if (prop_change_count > 0) {
509
+ EDDiffOperation* op = ed_diff_operation_create(ED_DIFF_PROP_UPDATE, 0);
510
+ op->node = old_tree;
511
+ op->new_node = new_tree;
512
+ op->prop_changes = prop_changes;
513
+ op->prop_change_count = prop_change_count;
514
+ ed_diff_result_append(result, op);
515
+ } else {
516
+ /* Free unused prop changes */
517
+ while (prop_changes) {
518
+ EDVirtualProp* next = prop_changes->next;
519
+ free(prop_changes);
520
+ prop_changes = next;
521
+ }
522
+ }
523
+
524
+ /* Diff text content for text nodes */
525
+ if (old_tree->type == ED_NODE_TEXT && strcmp(old_tree->text, new_tree->text) != 0) {
526
+ EDDiffOperation* op = ed_diff_operation_create(ED_DIFF_TEXT_UPDATE, 0);
527
+ op->node = old_tree;
528
+ op->new_node = new_tree;
529
+ ed_diff_result_append(result, op);
530
+ }
531
+
532
+ /* Diff children */
533
+ if (old_tree->child_count > 0 || new_tree->child_count > 0) {
534
+ ed_diff_children_keyed(old_tree, new_tree, result);
535
+ }
536
+
537
+ return result;
538
+ }
539
+
540
+ /* ================================================================
541
+ * Patch Queue Implementation
542
+ * ================================================================ */
543
+
544
+ EDPatchQueue* ed_patch_queue_create(uint32_t capacity)
545
+ {
546
+ EDPatchQueue* queue = (EDPatchQueue*)calloc(1, sizeof(EDPatchQueue));
547
+ if (!queue) return NULL;
548
+
549
+ queue->head = NULL;
550
+ queue->tail = NULL;
551
+ queue->count = 0;
552
+ queue->capacity = (capacity > 0) ? capacity : ED_PATCH_QUEUE_SIZE;
553
+
554
+ return queue;
555
+ }
556
+
557
+ void ed_patch_queue_destroy(EDPatchQueue* queue)
558
+ {
559
+ if (!queue) return;
560
+ ed_patch_queue_flush(queue);
561
+ free(queue);
562
+ }
563
+
564
+ void ed_patch_queue_push(EDPatchQueue* queue, EDPatchOperation* op)
565
+ {
566
+ if (!queue || !op) return;
567
+
568
+ if (queue->count >= queue->capacity) {
569
+ ed_log_warn("Patch queue full (%u/%u), auto-flushing",
570
+ queue->count, queue->capacity);
571
+ ed_patch_queue_flush(queue);
572
+ }
573
+
574
+ op->next = NULL;
575
+
576
+ if (!queue->tail) {
577
+ queue->head = op;
578
+ queue->tail = op;
579
+ } else {
580
+ queue->tail->next = op;
581
+ queue->tail = op;
582
+ }
583
+
584
+ queue->count++;
585
+ }
586
+
587
+ EDPatchOperation* ed_patch_queue_pop(EDPatchQueue* queue)
588
+ {
589
+ if (!queue || !queue->head) return NULL;
590
+
591
+ EDPatchOperation* op = queue->head;
592
+ queue->head = op->next;
593
+ if (!queue->head) {
594
+ queue->tail = NULL;
595
+ }
596
+ op->next = NULL;
597
+ queue->count--;
598
+
599
+ return op;
600
+ }
601
+
602
+ void ed_patch_queue_flush(EDPatchQueue* queue)
603
+ {
604
+ if (!queue) return;
605
+
606
+ EDPatchOperation* op = queue->head;
607
+ while (op) {
608
+ EDPatchOperation* next = op->next;
609
+ free(op);
610
+ op = next;
611
+ }
612
+
613
+ queue->head = NULL;
614
+ queue->tail = NULL;
615
+ queue->count = 0;
616
+ }
617
+
618
+ uint32_t ed_patch_queue_count(EDPatchQueue* queue)
619
+ {
620
+ return queue ? queue->count : 0;
621
+ }
622
+
623
+ bool ed_patch_queue_is_empty(EDPatchQueue* queue)
624
+ {
625
+ return queue ? (queue->count == 0) : true;
626
+ }
627
+
628
+ /* ================================================================
629
+ * Patch Creation Helpers
630
+ * ================================================================ */
631
+
632
+ static EDPatchOperation* ed_patch_create(EDPatchOpType type, void* target)
633
+ {
634
+ EDPatchOperation* op = (EDPatchOperation*)calloc(1, sizeof(EDPatchOperation));
635
+ if (!op) return NULL;
636
+
637
+ op->type = type;
638
+ op->target_node = target;
639
+ op->key[0] = '\0';
640
+ op->value[0] = '\0';
641
+ op->index = 0;
642
+ op->data = NULL;
643
+ op->next = NULL;
644
+
645
+ return op;
646
+ }
647
+
648
+ /* ================================================================
649
+ * Patch Application
650
+ * ================================================================ */
651
+
652
+ void ed_patch_apply(EDPatchOperation* op)
653
+ {
654
+ if (!op) return;
655
+
656
+ switch (op->type) {
657
+ case ED_PATCH_SET_ATTRIBUTE:
658
+ /* Set DOM attribute */
659
+ ed_log_debug("PATCH: set attribute '%s'='%s'", op->key, op->value);
660
+ break;
661
+
662
+ case ED_PATCH_REMOVE_ATTRIBUTE:
663
+ ed_log_debug("PATCH: remove attribute '%s'", op->key);
664
+ break;
665
+
666
+ case ED_PATCH_SET_STYLE:
667
+ ed_log_debug("PATCH: set style '%s'='%s'", op->key, op->value);
668
+ break;
669
+
670
+ case ED_PATCH_REMOVE_STYLE:
671
+ ed_log_debug("PATCH: remove style '%s'", op->key);
672
+ break;
673
+
674
+ case ED_PATCH_ADD_CLASS:
675
+ ed_log_debug("PATCH: add class '%s'", op->value);
676
+ break;
677
+
678
+ case ED_PATCH_REMOVE_CLASS:
679
+ ed_log_debug("PATCH: remove class '%s'", op->value);
680
+ break;
681
+
682
+ case ED_PATCH_INSERT_CHILD:
683
+ ed_log_debug("PATCH: insert child at index %u", op->index);
684
+ break;
685
+
686
+ case ED_PATCH_REMOVE_CHILD:
687
+ ed_log_debug("PATCH: remove child at index %u", op->index);
688
+ break;
689
+
690
+ case ED_PATCH_REPLACE_CHILD:
691
+ ed_log_debug("PATCH: replace child at index %u", op->index);
692
+ break;
693
+
694
+ case ED_PATCH_MOVE_CHILD:
695
+ ed_log_debug("PATCH: move child from %u to %u", op->index, (uint32_t)(uintptr_t)op->data);
696
+ break;
697
+
698
+ case ED_PATCH_SET_TEXT:
699
+ ed_log_debug("PATCH: set text '%s'", op->value);
700
+ break;
701
+
702
+ case ED_PATCH_SET_EVENT:
703
+ ed_log_debug("PATCH: set event '%s'", op->key);
704
+ break;
705
+
706
+ case ED_PATCH_REMOVE_EVENT:
707
+ ed_log_debug("PATCH: remove event '%s'", op->key);
708
+ break;
709
+
710
+ case ED_PATCH_SET_REF:
711
+ ed_log_debug("PATCH: set ref");
712
+ break;
713
+
714
+ case ED_PATCH_REMOVE_REF:
715
+ ed_log_debug("PATCH: remove ref");
716
+ break;
717
+
718
+ default:
719
+ ed_log_warn("PATCH: unknown operation type %d", op->type);
720
+ break;
721
+ }
722
+ }
723
+
724
+ void ed_patch_apply_all(EDPatchQueue* queue)
725
+ {
726
+ if (!queue) return;
727
+
728
+ uint32_t applied = 0;
729
+ EDPatchOperation* op = queue->head;
730
+
731
+ while (op) {
732
+ ed_patch_apply(op);
733
+ applied++;
734
+ op = op->next;
735
+ }
736
+
737
+ ed_log_debug("Applied %u patches", applied);
738
+ ed_patch_queue_flush(queue);
739
+ }
740
+
741
+ /* ================================================================
742
+ * Diff-to-Patch Conversion
743
+ * ================================================================ */
744
+
745
+ static EDPatchQueue* ed_diff_to_patches(EDDiffResult* diff, void* root_node)
746
+ {
747
+ if (!diff) return NULL;
748
+
749
+ EDPatchQueue* queue = ed_patch_queue_create(ED_PATCH_QUEUE_SIZE);
750
+ if (!queue) return NULL;
751
+
752
+ EDDiffOperation* op = diff->operations;
753
+ while (op) {
754
+ switch (op->type) {
755
+ case ED_DIFF_INSERT: {
756
+ if (op->new_node) {
757
+ EDPatchOperation* patch = ed_patch_create(ED_PATCH_INSERT_CHILD, root_node);
758
+ patch->data = op->new_node;
759
+ patch->index = op->index;
760
+ ed_patch_queue_push(queue, patch);
761
+
762
+ /* Generate patches for props on the new node */
763
+ EDVirtualProp* prop = op->new_node->props;
764
+ while (prop) {
765
+ EDPatchOperation* prop_patch = ed_patch_create(
766
+ ED_PATCH_SET_ATTRIBUTE, op->new_node);
767
+ strncpy(prop_patch->key, prop->key, ED_MAX_PROP_KEY_LEN - 1);
768
+ strncpy(prop_patch->value, prop->value, ED_MAX_PROP_VALUE_LEN - 1);
769
+ ed_patch_queue_push(queue, prop_patch);
770
+ prop = prop->next;
771
+ }
772
+ }
773
+ break;
774
+ }
775
+
776
+ case ED_DIFF_DELETE: {
777
+ EDPatchOperation* patch = ed_patch_create(ED_PATCH_REMOVE_CHILD, root_node);
778
+ patch->data = op->node;
779
+ patch->index = op->index;
780
+ ed_patch_queue_push(queue, patch);
781
+ break;
782
+ }
783
+
784
+ case ED_DIFF_REPLACE: {
785
+ EDPatchOperation* patch = ed_patch_create(ED_PATCH_REPLACE_CHILD, root_node);
786
+ patch->data = op->new_node;
787
+ patch->index = op->index;
788
+ ed_patch_queue_push(queue, patch);
789
+ break;
790
+ }
791
+
792
+ case ED_DIFF_MOVE: {
793
+ EDPatchOperation* patch = ed_patch_create(ED_PATCH_MOVE_CHILD, root_node);
794
+ patch->index = op->index;
795
+ patch->data = (void*)(uintptr_t)op->new_index;
796
+ ed_patch_queue_push(queue, patch);
797
+ break;
798
+ }
799
+
800
+ case ED_DIFF_UPDATE: {
801
+ /* Convert prop changes to patches */
802
+ EDVirtualProp* change = op->prop_changes;
803
+ while (change) {
804
+ if (strlen(change->value) == 0) {
805
+ /* Removed prop */
806
+ EDPatchOperation* prop_patch = ed_patch_create(
807
+ ED_PATCH_REMOVE_ATTRIBUTE, op->node);
808
+ strncpy(prop_patch->key, change->key, ED_MAX_PROP_KEY_LEN - 1);
809
+ ed_patch_queue_push(queue, prop_patch);
810
+ } else {
811
+ /* Updated/added prop */
812
+ EDPatchOpType patch_type = ED_PATCH_SET_ATTRIBUTE;
813
+ if (change->is_style) patch_type = ED_PATCH_SET_STYLE;
814
+ else if (change->is_class) patch_type = ED_PATCH_ADD_CLASS;
815
+ else if (change->is_event) patch_type = ED_PATCH_SET_EVENT;
816
+ else if (change->is_ref) patch_type = ED_PATCH_SET_REF;
817
+
818
+ EDPatchOperation* prop_patch = ed_patch_create(patch_type, op->node);
819
+ strncpy(prop_patch->key, change->key, ED_MAX_PROP_KEY_LEN - 1);
820
+ strncpy(prop_patch->value, change->value, ED_MAX_PROP_VALUE_LEN - 1);
821
+ ed_patch_queue_push(queue, prop_patch);
822
+ }
823
+ change = change->next;
824
+ }
825
+ break;
826
+ }
827
+
828
+ case ED_DIFF_PROP_UPDATE: {
829
+ EDVirtualProp* change = op->prop_changes;
830
+ while (change) {
831
+ if (strlen(change->value) == 0) {
832
+ EDPatchOperation* prop_patch = ed_patch_create(
833
+ ED_PATCH_REMOVE_ATTRIBUTE, op->node);
834
+ strncpy(prop_patch->key, change->key, ED_MAX_PROP_KEY_LEN - 1);
835
+ ed_patch_queue_push(queue, prop_patch);
836
+ } else {
837
+ EDPatchOperation* prop_patch = ed_patch_create(
838
+ ED_PATCH_SET_ATTRIBUTE, op->node);
839
+ strncpy(prop_patch->key, change->key, ED_MAX_PROP_KEY_LEN - 1);
840
+ strncpy(prop_patch->value, change->value, ED_MAX_PROP_VALUE_LEN - 1);
841
+ ed_patch_queue_push(queue, prop_patch);
842
+ }
843
+ change = change->next;
844
+ }
845
+ break;
846
+ }
847
+
848
+ case ED_DIFF_TEXT_UPDATE: {
849
+ EDPatchOperation* patch = ed_patch_create(ED_PATCH_SET_TEXT, op->node);
850
+ if (op->new_node) {
851
+ strncpy(patch->value, op->new_node->text, ED_MAX_PROP_VALUE_LEN - 1);
852
+ }
853
+ ed_patch_queue_push(queue, patch);
854
+ break;
855
+ }
856
+
857
+ default:
858
+ break;
859
+ }
860
+
861
+ op = op->next;
862
+ }
863
+
864
+ return queue;
865
+ }
866
+
867
+ /* ================================================================
868
+ * Optimized Diff: Quick Equality Check
869
+ * ================================================================ */
870
+
871
+ static bool ed_diff_quick_check(EDVirtualNode* old_tree, EDVirtualNode* new_tree)
872
+ {
873
+ if (!old_tree || !new_tree) return false;
874
+ if (old_tree->hash != 0 && new_tree->hash != 0 && old_tree->hash == new_tree->hash) {
875
+ return true; /* Same hash - likely identical */
876
+ }
877
+ if (old_tree == new_tree) return true; /* Same pointer */
878
+ return false;
879
+ }
880
+
881
+ /* ================================================================
882
+ * Diff Statistics
883
+ * ================================================================ */
884
+
885
+ typedef struct EDDiffStats {
886
+ uint32_t total_diffs;
887
+ uint32_t total_operations;
888
+ uint64_t total_diff_time_ns;
889
+ uint32_t cache_hits;
890
+ uint32_t cache_misses;
891
+ } EDDiffStats;
892
+
893
+ static EDDiffStats g_diff_stats = {0};
894
+
895
+ static void ed_diff_record_stats(EDDiffResult* result, uint64_t elapsed_ms)
896
+ {
897
+ g_diff_stats.total_diffs++;
898
+ g_diff_stats.total_operations += result ? result->operation_count : 0;
899
+ (void)elapsed_ms;
900
+ }