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,1027 @@
1
+ /*
2
+ * fiber.c - ElementDrawing Core: Fiber Architecture
3
+ *
4
+ * Implements fiber node creation/destruction, fiber pool management,
5
+ * fiber tree manipulation (add child/sibling), property and state
6
+ * management, effect tracking, flag operations, and the complete
7
+ * reconciliation algorithm for the ElementDrawing engine.
8
+ */
9
+
10
+ #include "bridge.h"
11
+
12
+ /* ================================================================
13
+ * Fiber Node Creation and Destruction
14
+ * ================================================================ */
15
+
16
+ EDFiberNode* ed_fiber_create(EDFiberTag tag, EDFiberFlags flags)
17
+ {
18
+ EDFiberNode* fiber = (EDFiberNode*)calloc(1, sizeof(EDFiberNode));
19
+ if (!fiber) {
20
+ ed_log_error("Failed to allocate fiber node (tag=%d)", tag);
21
+ return NULL;
22
+ }
23
+
24
+ fiber->tag = tag;
25
+ fiber->flags = flags;
26
+ fiber->priority = ED_PRIORITY_NORMAL;
27
+
28
+ /* Tree structure initialized to NULL by calloc */
29
+ fiber->parent = NULL;
30
+ fiber->child = NULL;
31
+ fiber->sibling = NULL;
32
+ fiber->alternate = NULL;
33
+
34
+ /* Work */
35
+ fiber->pending_props = NULL;
36
+ fiber->memoized_props = NULL;
37
+ fiber->memoized_state = NULL;
38
+ fiber->memoized_hooks = NULL;
39
+
40
+ /* DOM reference */
41
+ fiber->state_node = NULL;
42
+ fiber->ref_value = NULL;
43
+
44
+ /* Key */
45
+ fiber->key[0] = '\0';
46
+ fiber->key_hash = 0;
47
+
48
+ /* Effect lists */
49
+ fiber->first_effect = NULL;
50
+ fiber->last_effect = NULL;
51
+ fiber->next_effect = NULL;
52
+
53
+ /* Dependency tracking */
54
+ fiber->dependencies = NULL;
55
+ fiber->lanes = 0;
56
+ fiber->child_lanes = 0;
57
+
58
+ /* Update queue */
59
+ fiber->update_queue = NULL;
60
+ fiber->update_count = 0;
61
+
62
+ /* Return node */
63
+ fiber->return_node = NULL;
64
+
65
+ /* Index */
66
+ fiber->index = 0;
67
+
68
+ /* Scheduling */
69
+ fiber->expiration_time = 0;
70
+ fiber->start_time = 0;
71
+ fiber->finish_time = 0;
72
+
73
+ /* Error boundary */
74
+ fiber->error_boundary = NULL;
75
+ fiber->error_boundary_info = NULL;
76
+
77
+ /* Offscreen */
78
+ fiber->is_hidden = false;
79
+ fiber->is_offscreen = false;
80
+
81
+ /* Pool */
82
+ fiber->is_active = true;
83
+ fiber->next_free = NULL;
84
+
85
+ return fiber;
86
+ }
87
+
88
+ void ed_fiber_destroy(EDFiberNode* fiber)
89
+ {
90
+ if (!fiber) return;
91
+
92
+ /* Destroy memoized hooks chain */
93
+ EDHookNode* hook = fiber->memoized_hooks;
94
+ while (hook) {
95
+ EDHookNode* next = hook->next;
96
+ ed_hook_destroy(hook);
97
+ hook = next;
98
+ }
99
+
100
+ /* Don't recursively destroy children - that's the pool's job
101
+ * or the tree traversal that initiates destruction */
102
+
103
+ /* Clear references (don't free what we don't own) */
104
+ fiber->pending_props = NULL;
105
+ fiber->memoized_props = NULL;
106
+ fiber->memoized_state = NULL;
107
+ fiber->state_node = NULL;
108
+ fiber->ref_value = NULL;
109
+ fiber->update_queue = NULL;
110
+ fiber->error_boundary_info = NULL;
111
+ fiber->dependencies = NULL;
112
+
113
+ /* Clear effect list references */
114
+ fiber->first_effect = NULL;
115
+ fiber->last_effect = NULL;
116
+ fiber->next_effect = NULL;
117
+
118
+ /* Mark as inactive for pool recycling */
119
+ fiber->is_active = false;
120
+
121
+ free(fiber);
122
+ }
123
+
124
+ /* ================================================================
125
+ * Fiber Pool
126
+ * ================================================================ */
127
+
128
+ EDFiberPool* ed_fiber_pool_create(uint32_t capacity)
129
+ {
130
+ if (capacity == 0) capacity = ED_FIBER_POOL_SIZE;
131
+
132
+ EDFiberPool* pool = (EDFiberPool*)calloc(1, sizeof(EDFiberPool));
133
+ if (!pool) return NULL;
134
+
135
+ pool->nodes = (EDFiberNode*)calloc(capacity, sizeof(EDFiberNode));
136
+ if (!pool->nodes) {
137
+ free(pool);
138
+ return NULL;
139
+ }
140
+
141
+ pool->capacity = capacity;
142
+ pool->used_count = 0;
143
+ pool->peak_usage = 0;
144
+ pool->alloc_count = 0;
145
+ pool->free_count = 0;
146
+ pool->reuse_count = 0;
147
+
148
+ /* Initialize all nodes as inactive and build free list */
149
+ pool->free_list = NULL;
150
+ for (uint32_t i = capacity; i > 0; i--) {
151
+ EDFiberNode* node = &pool->nodes[i - 1];
152
+ node->is_active = false;
153
+ node->next_free = pool->free_list;
154
+ pool->free_list = node;
155
+ }
156
+
157
+ ed_log_debug("Fiber pool created: capacity=%u", capacity);
158
+ return pool;
159
+ }
160
+
161
+ void ed_fiber_pool_destroy(EDFiberPool* pool)
162
+ {
163
+ if (!pool) return;
164
+
165
+ /* Destroy any active fiber hooks */
166
+ for (uint32_t i = 0; i < pool->capacity; i++) {
167
+ EDFiberNode* node = &pool->nodes[i];
168
+ if (node->is_active && node->memoized_hooks) {
169
+ EDHookNode* hook = node->memoized_hooks;
170
+ while (hook) {
171
+ EDHookNode* next = hook->next;
172
+ ed_hook_destroy(hook);
173
+ hook = next;
174
+ }
175
+ }
176
+ }
177
+
178
+ free(pool->nodes);
179
+ pool->nodes = NULL;
180
+ pool->free_list = NULL;
181
+ free(pool);
182
+ }
183
+
184
+ EDFiberNode* ed_fiber_pool_alloc(EDFiberPool* pool)
185
+ {
186
+ if (!pool) return ed_fiber_create(ED_FIBER_HOST_COMPONENT, ED_FIBER_FLAG_NONE);
187
+
188
+ /* Try to get from free list */
189
+ if (pool->free_list) {
190
+ EDFiberNode* fiber = pool->free_list;
191
+ pool->free_list = fiber->next_free;
192
+ fiber->next_free = NULL;
193
+
194
+ /* Reset the fiber to a clean state */
195
+ memset(fiber, 0, sizeof(EDFiberNode));
196
+ fiber->is_active = true;
197
+ fiber->tag = ED_FIBER_HOST_COMPONENT;
198
+ fiber->priority = ED_PRIORITY_NORMAL;
199
+
200
+ pool->used_count++;
201
+ pool->alloc_count++;
202
+ pool->reuse_count++;
203
+
204
+ if (pool->used_count > pool->peak_usage) {
205
+ pool->peak_usage = pool->used_count;
206
+ }
207
+
208
+ return fiber;
209
+ }
210
+
211
+ /* Pool exhausted - allocate a new standalone fiber */
212
+ ed_log_warn("Fiber pool exhausted (capacity=%u), allocating standalone", pool->capacity);
213
+ EDFiberNode* fiber = ed_fiber_create(ED_FIBER_HOST_COMPONENT, ED_FIBER_FLAG_NONE);
214
+ pool->alloc_count++;
215
+ return fiber;
216
+ }
217
+
218
+ void ed_fiber_pool_free(EDFiberPool* pool, EDFiberNode* fiber)
219
+ {
220
+ if (!pool || !fiber) return;
221
+
222
+ /* Check if fiber belongs to pool */
223
+ if (fiber >= pool->nodes && fiber < pool->nodes + pool->capacity) {
224
+ /* Reset the fiber */
225
+ EDHookNode* hook = fiber->memoized_hooks;
226
+ while (hook) {
227
+ EDHookNode* next = hook->next;
228
+ ed_hook_destroy(hook);
229
+ hook = next;
230
+ }
231
+
232
+ memset(fiber, 0, sizeof(EDFiberNode));
233
+ fiber->is_active = false;
234
+ fiber->next_free = pool->free_list;
235
+ pool->free_list = fiber;
236
+
237
+ pool->used_count--;
238
+ pool->free_count++;
239
+ } else {
240
+ /* Standalone fiber - just destroy it */
241
+ ed_fiber_destroy(fiber);
242
+ pool->free_count++;
243
+ }
244
+ }
245
+
246
+ /* ================================================================
247
+ * Fiber Clone
248
+ * ================================================================ */
249
+
250
+ EDFiberNode* ed_fiber_clone(EDFiberNode* fiber)
251
+ {
252
+ if (!fiber) return NULL;
253
+
254
+ EDFiberNode* clone = ed_fiber_create(fiber->tag, fiber->flags);
255
+ if (!clone) return NULL;
256
+
257
+ /* Copy scalar fields */
258
+ clone->priority = fiber->priority;
259
+ clone->lanes = fiber->lanes;
260
+ clone->child_lanes = fiber->child_lanes;
261
+ clone->index = fiber->index;
262
+ clone->is_hidden = fiber->is_hidden;
263
+ clone->is_offscreen = fiber->is_offscreen;
264
+
265
+ /* Copy key */
266
+ if (fiber->key[0] != '\0') {
267
+ strncpy(clone->key, fiber->key, ED_MAX_KEY_LEN - 1);
268
+ clone->key[ED_MAX_KEY_LEN - 1] = '\0';
269
+ }
270
+ clone->key_hash = fiber->key_hash;
271
+
272
+ /* Set up alternate relationship */
273
+ clone->alternate = fiber;
274
+ fiber->alternate = clone;
275
+
276
+ /* Copy props/state references (shared, not deep-copied) */
277
+ clone->pending_props = fiber->pending_props;
278
+ clone->memoized_props = fiber->memoized_props;
279
+ clone->memoized_state = fiber->memoized_state;
280
+ clone->memoized_hooks = fiber->memoized_hooks;
281
+
282
+ /* Copy DOM reference */
283
+ clone->state_node = fiber->state_node;
284
+ clone->ref_value = fiber->ref_value;
285
+
286
+ /* Copy return pointer */
287
+ clone->return_node = fiber->return_node;
288
+
289
+ /* Copy scheduling info */
290
+ clone->expiration_time = fiber->expiration_time;
291
+
292
+ /* Copy error boundary */
293
+ clone->error_boundary = fiber->error_boundary;
294
+
295
+ /* Set WIP flag */
296
+ ed_fiber_set_flag(clone, ED_FIBER_FLAG_WORK_IN_PROGRESS);
297
+ ed_fiber_set_flag(clone, ED_FIBER_FLAG_CLONED);
298
+
299
+ return clone;
300
+ }
301
+
302
+ /* ================================================================
303
+ * Fiber Tree Manipulation
304
+ * ================================================================ */
305
+
306
+ void ed_fiber_add_child(EDFiberNode* parent, EDFiberNode* child)
307
+ {
308
+ if (!parent || !child) return;
309
+
310
+ child->return_node = parent;
311
+
312
+ if (!parent->child) {
313
+ parent->child = child;
314
+ } else {
315
+ /* Add as last sibling */
316
+ EDFiberNode* sibling = parent->child;
317
+ while (sibling->sibling) {
318
+ sibling = sibling->sibling;
319
+ }
320
+ sibling->sibling = child;
321
+ }
322
+
323
+ /* Update child lanes on parent */
324
+ parent->child_lanes |= child->lanes;
325
+ }
326
+
327
+ void ed_fiber_add_sibling(EDFiberNode* a, EDFiberNode* b)
328
+ {
329
+ if (!a || !b) return;
330
+
331
+ EDFiberNode* sibling = a;
332
+ while (sibling->sibling) {
333
+ sibling = sibling->sibling;
334
+ }
335
+ sibling->sibling = b;
336
+ b->return_node = a->return_node;
337
+ }
338
+
339
+ void ed_fiber_set_props(EDFiberNode* fiber, EDVirtualNode* props)
340
+ {
341
+ if (!fiber) return;
342
+ fiber->pending_props = props;
343
+ ed_fiber_set_flag(fiber, ED_FIBER_FLAG_NEEDS_UPDATE);
344
+ }
345
+
346
+ void ed_fiber_set_state(EDFiberNode* fiber, EDVirtualNode* state)
347
+ {
348
+ if (!fiber) return;
349
+ fiber->memoized_state = state;
350
+ ed_fiber_set_flag(fiber, ED_FIBER_FLAG_UPDATE);
351
+ }
352
+
353
+ void ed_fiber_add_effect(EDFiberNode* fiber, EDFiberNode* effect)
354
+ {
355
+ if (!fiber || !effect) return;
356
+
357
+ if (!fiber->last_effect) {
358
+ fiber->first_effect = effect;
359
+ fiber->last_effect = effect;
360
+ } else {
361
+ fiber->last_effect->next_effect = effect;
362
+ fiber->last_effect = effect;
363
+ }
364
+
365
+ ed_fiber_set_flag(fiber, ED_FIBER_FLAG_HAS_EFFECT);
366
+ }
367
+
368
+ void ed_fiber_add_dependency(EDFiberNode* fiber, EDFiberNode* dep)
369
+ {
370
+ if (!fiber || !dep) return;
371
+
372
+ /* Simple linked list for dependencies */
373
+ dep->dependencies = fiber->dependencies;
374
+ fiber->dependencies = dep;
375
+ }
376
+
377
+ void ed_fiber_set_priority(EDFiberNode* fiber, EDSchedulerPriority priority)
378
+ {
379
+ if (!fiber) return;
380
+ fiber->priority = priority;
381
+
382
+ /* Update expiration time based on priority */
383
+ uint64_t now = ed_get_timestamp_ms();
384
+ switch (priority) {
385
+ case ED_PRIORITY_IMMEDIATE:
386
+ fiber->expiration_time = now;
387
+ break;
388
+ case ED_PRIORITY_USER_BLOCKING:
389
+ fiber->expiration_time = now + 100;
390
+ break;
391
+ case ED_PRIORITY_NORMAL:
392
+ fiber->expiration_time = now + ED_FIBER_WORK_EXPIRATION;
393
+ break;
394
+ case ED_PRIORITY_LOW:
395
+ fiber->expiration_time = now + 10000;
396
+ break;
397
+ case ED_PRIORITY_IDLE:
398
+ fiber->expiration_time = UINT64_MAX;
399
+ break;
400
+ }
401
+ }
402
+
403
+ /* ================================================================
404
+ * Fiber Traversal and Counting
405
+ * ================================================================ */
406
+
407
+ uint32_t ed_fiber_count_descendants(EDFiberNode* fiber)
408
+ {
409
+ if (!fiber) return 0;
410
+
411
+ uint32_t count = 0;
412
+ EDFiberNode* child = fiber->child;
413
+ while (child) {
414
+ count += 1 + ed_fiber_count_descendants(child);
415
+ child = child->sibling;
416
+ }
417
+ return count;
418
+ }
419
+
420
+ uint32_t ed_fiber_count_effects(EDFiberNode* fiber)
421
+ {
422
+ if (!fiber) return 0;
423
+
424
+ uint32_t count = 0;
425
+ EDFiberNode* effect = fiber->first_effect;
426
+ while (effect) {
427
+ count++;
428
+ effect = effect->next_effect;
429
+ }
430
+ return count;
431
+ }
432
+
433
+ /* ================================================================
434
+ * Fiber Flag Operations
435
+ * ================================================================ */
436
+
437
+ bool ed_fiber_has_effect(EDFiberNode* fiber, EDFiberFlags flag)
438
+ {
439
+ if (!fiber) return false;
440
+ return (fiber->flags & flag) != 0;
441
+ }
442
+
443
+ void ed_fiber_set_flag(EDFiberNode* fiber, EDFiberFlags flag)
444
+ {
445
+ if (!fiber) return;
446
+ fiber->flags |= flag;
447
+ }
448
+
449
+ void ed_fiber_clear_flag(EDFiberNode* fiber, EDFiberFlags flag)
450
+ {
451
+ if (!fiber) return;
452
+ fiber->flags &= ~flag;
453
+ }
454
+
455
+ /* ================================================================
456
+ * Reconciliation: Single Element
457
+ * ================================================================ */
458
+
459
+ static bool ed_fiber_tags_match(EDFiberNode* current, EDVirtualNode* element)
460
+ {
461
+ if (!current || !element) return false;
462
+
463
+ switch (element->type) {
464
+ case ED_NODE_ELEMENT:
465
+ return current->tag == ED_FIBER_HOST_COMPONENT &&
466
+ strcmp(current->memoized_props ? current->memoized_props->tag : "",
467
+ element->tag) == 0;
468
+ case ED_NODE_TEXT:
469
+ return current->tag == ED_FIBER_HOST_TEXT;
470
+ case ED_NODE_FRAGMENT:
471
+ return current->tag == ED_FIBER_FRAGMENT;
472
+ case ED_NODE_CONTEXT_PROVIDER:
473
+ return current->tag == ED_FIBER_CONTEXT_PROVIDER;
474
+ case ED_NODE_CONTEXT_CONSUMER:
475
+ return current->tag == ED_FIBER_CONTEXT_CONSUMER;
476
+ default:
477
+ return false;
478
+ }
479
+ }
480
+
481
+ EDFiberNode* ed_reconcile_single_element(EDFiberNode* current,
482
+ EDFiberNode* work_in_progress,
483
+ EDVirtualNode* element)
484
+ {
485
+ if (!element) return NULL;
486
+
487
+ const char* key = element->key[0] != '\0' ? element->key : NULL;
488
+
489
+ if (current !== NULL) {
490
+ /* Check if we can reuse the existing fiber */
491
+ if (ed_fiber_tags_match(current, element)) {
492
+ /* Check keys match */
493
+ if ((key == NULL && current->key[0] == '\0') ||
494
+ (key != NULL && strcmp(current->key, key) == 0)) {
495
+ /* Reuse the current fiber */
496
+ EDFiberNode* existing = ed_use_fiber(current, element);
497
+ ed_fiber_clear_flag(existing, ED_FIBER_FLAG_DELETION);
498
+ ed_fiber_set_flag(existing, ED_FIBER_FLAG_UPDATE);
499
+ return existing;
500
+ }
501
+ }
502
+
503
+ /* Cannot reuse - delete the current fiber */
504
+ ed_fiber_set_flag(current, ED_FIBER_FLAG_DELETION);
505
+ }
506
+
507
+ /* Create a new fiber */
508
+ EDFiberNode* fiber = ed_create_child(element, 0);
509
+ if (key && key[0] != '\0') {
510
+ strncpy(fiber->key, key, ED_MAX_KEY_LEN - 1);
511
+ fiber->key[ED_MAX_KEY_LEN - 1] = '\0';
512
+ fiber->key_hash = ed_hash_fnv1a(key);
513
+ }
514
+
515
+ ed_fiber_set_flag(fiber, ED_FIBER_FLAG_PLACEMENT);
516
+ return fiber;
517
+ }
518
+
519
+ EDFiberNode* ed_reconcile_single_text(EDFiberNode* current,
520
+ EDFiberNode* work_in_progress,
521
+ EDVirtualNode* text)
522
+ {
523
+ if (!text) return NULL;
524
+
525
+ if (current !== NULL && current->tag == ED_FIBER_HOST_TEXT) {
526
+ /* Reuse existing text fiber */
527
+ EDFiberNode* existing = ed_use_fiber(current, text);
528
+ ed_fiber_clear_flag(existing, ED_FIBER_FLAG_DELETION);
529
+
530
+ /* Check if text content changed */
531
+ if (current->memoized_props &&
532
+ strcmp(current->memoized_props->text, text->text) != 0) {
533
+ ed_fiber_set_flag(existing, ED_FIBER_FLAG_UPDATE);
534
+ }
535
+ return existing;
536
+ }
537
+
538
+ /* Create new text fiber */
539
+ EDFiberNode* fiber = ed_fiber_create(ED_FIBER_HOST_TEXT, ED_FIBER_FLAG_NONE);
540
+ if (!fiber) return NULL;
541
+
542
+ fiber->pending_props = text;
543
+ ed_fiber_set_flag(fiber, ED_FIBER_FLAG_PLACEMENT);
544
+ return fiber;
545
+ }
546
+
547
+ /* ================================================================
548
+ * Reconciliation: Children Array (O(n) algorithm with keys)
549
+ * ================================================================ */
550
+
551
+ /* Key-to-fiber mapping for efficient lookups */
552
+ typedef struct EDKeyMapEntry {
553
+ char key[ED_MAX_KEY_LEN];
554
+ uint32_t key_hash;
555
+ EDFiberNode* fiber;
556
+ uint32_t index;
557
+ struct EDKeyMapEntry* next;
558
+ } EDKeyMapEntry;
559
+
560
+ typedef struct EDKeyMap {
561
+ EDKeyMapEntry* buckets[ED_DIFF_KEY_CACHE_SIZE];
562
+ uint32_t count;
563
+ } EDKeyMap;
564
+
565
+ static void ed_key_map_init(EDKeyMap* map)
566
+ {
567
+ if (!map) return;
568
+ memset(map, 0, sizeof(EDKeyMap));
569
+ }
570
+
571
+ static void ed_key_map_insert(EDKeyMap* map, const char* key, uint32_t key_hash,
572
+ EDFiberNode* fiber, uint32_t index)
573
+ {
574
+ if (!map || !key) return;
575
+
576
+ EDKeyMapEntry* entry = (EDKeyMapEntry*)calloc(1, sizeof(EDKeyMapEntry));
577
+ if (!entry) return;
578
+
579
+ strncpy(entry->key, key, ED_MAX_KEY_LEN - 1);
580
+ entry->key[ED_MAX_KEY_LEN - 1] = '\0';
581
+ entry->key_hash = key_hash;
582
+ entry->fiber = fiber;
583
+ entry->index = index;
584
+
585
+ uint32_t bucket = key_hash % ED_DIFF_KEY_CACHE_SIZE;
586
+ entry->next = map->buckets[bucket];
587
+ map->buckets[bucket] = entry;
588
+ map->count++;
589
+ }
590
+
591
+ static EDFiberNode* ed_key_map_lookup(EDKeyMap* map, const char* key, uint32_t key_hash,
592
+ uint32_t* out_index)
593
+ {
594
+ if (!map || !key) return NULL;
595
+
596
+ uint32_t bucket = key_hash % ED_DIFF_KEY_CACHE_SIZE;
597
+ EDKeyMapEntry* entry = map->buckets[bucket];
598
+
599
+ while (entry) {
600
+ if (entry->key_hash == key_hash && strcmp(entry->key, key) == 0) {
601
+ if (out_index) *out_index = entry->index;
602
+ return entry->fiber;
603
+ }
604
+ entry = entry->next;
605
+ }
606
+ return NULL;
607
+ }
608
+
609
+ static void ed_key_map_destroy(EDKeyMap* map)
610
+ {
611
+ if (!map) return;
612
+ for (uint32_t i = 0; i < ED_DIFF_KEY_CACHE_SIZE; i++) {
613
+ EDKeyMapEntry* entry = map->buckets[i];
614
+ while (entry) {
615
+ EDKeyMapEntry* next = entry->next;
616
+ free(entry);
617
+ entry = next;
618
+ }
619
+ map->buckets[i] = NULL;
620
+ }
621
+ map->count = 0;
622
+ }
623
+
624
+ EDFiberNode* ed_reconcile_children_array(EDFiberNode* current,
625
+ EDFiberNode* work_in_progress,
626
+ EDVirtualNode** next_children,
627
+ uint32_t count)
628
+ {
629
+ if (!work_in_progress || !next_children || count == 0) return NULL;
630
+
631
+ /* Build key map from current children */
632
+ EDKeyMap existing_map;
633
+ ed_key_map_init(&existing_map);
634
+
635
+ EDFiberNode* existing_child = current ? current->child : NULL;
636
+ uint32_t existing_idx = 0;
637
+ while (existing_child) {
638
+ if (existing_child->key[0] != '\0') {
639
+ ed_key_map_insert(&existing_map, existing_child->key,
640
+ existing_child->key_hash, existing_child, existing_idx);
641
+ }
642
+ existing_child = existing_child->sibling;
643
+ existing_idx++;
644
+ }
645
+
646
+ /* Reconcile each new child */
647
+ EDFiberNode* first_new_child = NULL;
648
+ EDFiberNode* prev_new_child = NULL;
649
+ uint32_t last_placed_index = 0;
650
+
651
+ for (uint32_t i = 0; i < count; i++) {
652
+ EDVirtualNode* element = next_children[i];
653
+ if (!element) continue;
654
+
655
+ EDFiberNode* new_fiber = NULL;
656
+ const char* key = element->key[0] != '\0' ? element->key : NULL;
657
+ uint32_t key_hash = key ? ed_hash_fnv1a(key) : 0;
658
+
659
+ if (key) {
660
+ /* Key-based matching */
661
+ uint32_t matched_index = 0;
662
+ EDFiberNode* matched = ed_key_map_lookup(&existing_map, key, key_hash,
663
+ &matched_index);
664
+ if (matched) {
665
+ /* Found a match - reuse the fiber */
666
+ if (ed_fiber_tags_match(matched, element)) {
667
+ new_fiber = ed_use_fiber(matched, element);
668
+ ed_fiber_clear_flag(new_fiber, ED_FIBER_FLAG_DELETION);
669
+ ed_fiber_set_flag(new_fiber, ED_FIBER_FLAG_UPDATE);
670
+
671
+ /* Check if moved (out of order) */
672
+ if (matched_index < last_placed_index) {
673
+ ed_fiber_set_flag(new_fiber, ED_FIBER_FLAG_PLACEMENT);
674
+ }
675
+ last_placed_index = (matched_index > last_placed_index) ?
676
+ matched_index : last_placed_index;
677
+ } else {
678
+ /* Type changed - delete old, create new */
679
+ ed_fiber_set_flag(matched, ED_FIBER_FLAG_DELETION);
680
+ new_fiber = ed_create_child(element, i);
681
+ ed_fiber_set_flag(new_fiber, ED_FIBER_FLAG_PLACEMENT);
682
+ }
683
+ } else {
684
+ /* No match - create new */
685
+ new_fiber = ed_create_child(element, i);
686
+ ed_fiber_set_flag(new_fiber, ED_FIBER_FLAG_PLACEMENT);
687
+ }
688
+ } else {
689
+ /* No key - positional matching */
690
+ if (current) {
691
+ EDFiberNode* old_child = current->child;
692
+ uint32_t j = 0;
693
+ while (old_child && j < i) {
694
+ old_child = old_child->sibling;
695
+ j++;
696
+ }
697
+ if (old_child && ed_fiber_tags_match(old_child, element) &&
698
+ old_child->key[0] == '\0') {
699
+ new_fiber = ed_use_fiber(old_child, element);
700
+ ed_fiber_clear_flag(new_fiber, ED_FIBER_FLAG_DELETION);
701
+ ed_fiber_set_flag(new_fiber, ED_FIBER_FLAG_UPDATE);
702
+ } else {
703
+ new_fiber = ed_create_child(element, i);
704
+ ed_fiber_set_flag(new_fiber, ED_FIBER_FLAG_PLACEMENT);
705
+ }
706
+ } else {
707
+ new_fiber = ed_create_child(element, i);
708
+ ed_fiber_set_flag(new_fiber, ED_FIBER_FLAG_PLACEMENT);
709
+ }
710
+ }
711
+
712
+ if (new_fiber) {
713
+ new_fiber->index = i;
714
+ new_fiber->return_node = work_in_progress;
715
+
716
+ if (!first_new_child) {
717
+ first_new_child = new_fiber;
718
+ } else if (prev_new_child) {
719
+ prev_new_child->sibling = new_fiber;
720
+ }
721
+ prev_new_child = new_fiber;
722
+ }
723
+ }
724
+
725
+ ed_key_map_destroy(&existing_map);
726
+
727
+ /* Mark remaining old children for deletion */
728
+ if (current) {
729
+ EDFiberNode* old_child = current->child;
730
+ while (old_child) {
731
+ if (!ed_fiber_has_effect(old_child, ED_FIBER_FLAG_UPDATE) &&
732
+ !ed_fiber_has_effect(old_child, ED_FIBER_FLAG_PLACEMENT)) {
733
+ ed_fiber_set_flag(old_child, ED_FIBER_FLAG_DELETION);
734
+ }
735
+ old_child = old_child->sibling;
736
+ }
737
+ }
738
+
739
+ return first_new_child;
740
+ }
741
+
742
+ /* ================================================================
743
+ * Reconciliation: Top-Level Children
744
+ * ================================================================ */
745
+
746
+ EDFiberNode* ed_reconcile_children(EDFiberNode* current, EDFiberNode* work_in_progress)
747
+ {
748
+ if (!work_in_progress) return NULL;
749
+
750
+ EDVirtualNode* next_children = work_in_progress->pending_props;
751
+
752
+ if (current == NULL) {
753
+ /* Mount: no current tree, just create new fibers */
754
+ if (next_children) {
755
+ EDFiberNode* child = NULL;
756
+ if (next_children->type == ED_NODE_TEXT) {
757
+ child = ed_reconcile_single_text(NULL, work_in_progress, next_children);
758
+ } else {
759
+ child = ed_reconcile_single_element(NULL, work_in_progress, next_children);
760
+ }
761
+ work_in_progress->child = child;
762
+ return child;
763
+ }
764
+ return NULL;
765
+ }
766
+
767
+ /* Update: reconcile current with new */
768
+ if (next_children) {
769
+ if (next_children->child_count > 0) {
770
+ EDFiberNode* first_child = ed_reconcile_children_array(
771
+ current, work_in_progress,
772
+ next_children->children,
773
+ next_children->child_count);
774
+ work_in_progress->child = first_child;
775
+ return first_child;
776
+ } else if (next_children->type == ED_NODE_TEXT) {
777
+ EDFiberNode* child = ed_reconcile_single_text(
778
+ current->child, work_in_progress, next_children);
779
+ work_in_progress->child = child;
780
+ return child;
781
+ } else {
782
+ EDFiberNode* child = ed_reconcile_single_element(
783
+ current->child, work_in_progress, next_children);
784
+ work_in_progress->child = child;
785
+ return child;
786
+ }
787
+ }
788
+
789
+ /* No children - delete existing */
790
+ ed_delete_remaining_children(current);
791
+ work_in_progress->child = NULL;
792
+ return NULL;
793
+ }
794
+
795
+ void ed_reconcile_child_fibers(EDFiberNode* current, EDFiberNode* work_in_progress,
796
+ EDVirtualNode* next_children)
797
+ {
798
+ if (!work_in_progress) return;
799
+
800
+ if (next_children) {
801
+ work_in_progress->pending_props = next_children;
802
+ }
803
+
804
+ ed_reconcile_children(current, work_in_progress);
805
+ }
806
+
807
+ /* ================================================================
808
+ * Reconciliation Helpers
809
+ * ================================================================ */
810
+
811
+ void ed_place_child(EDFiberNode* new_fiber, EDFiberFlags flags, uint32_t index)
812
+ {
813
+ if (!new_fiber) return;
814
+
815
+ ed_fiber_set_flag(new_fiber, flags);
816
+ if (flags & ED_FIBER_FLAG_PLACEMENT) {
817
+ ed_fiber_set_flag(new_fiber, ED_FIBER_FLAG_PLACEMENT);
818
+ }
819
+ new_fiber->index = index;
820
+ }
821
+
822
+ EDFiberNode* ed_update_slot(EDFiberNode* current, uint32_t index, EDVirtualNode* element)
823
+ {
824
+ if (!element) return NULL;
825
+
826
+ /* Return existing fiber at slot if compatible, otherwise create new */
827
+ if (current) {
828
+ EDFiberNode* child = current->child;
829
+ uint32_t i = 0;
830
+ while (child && i < index) {
831
+ child = child->sibling;
832
+ i++;
833
+ }
834
+ if (child && ed_fiber_tags_match(child, element)) {
835
+ return ed_use_fiber(child, element);
836
+ }
837
+ }
838
+
839
+ return ed_create_child(element, index);
840
+ }
841
+
842
+ EDFiberNode* ed_create_child(EDVirtualNode* element, uint32_t index)
843
+ {
844
+ if (!element) return NULL;
845
+
846
+ EDFiberTag tag;
847
+ switch (element->type) {
848
+ case ED_NODE_TEXT:
849
+ tag = ED_FIBER_HOST_TEXT;
850
+ break;
851
+ case ED_NODE_FRAGMENT:
852
+ tag = ED_FIBER_FRAGMENT;
853
+ break;
854
+ case ED_NODE_ELEMENT:
855
+ tag = ED_FIBER_HOST_COMPONENT;
856
+ break;
857
+ case ED_NODE_CONTEXT_PROVIDER:
858
+ tag = ED_FIBER_CONTEXT_PROVIDER;
859
+ break;
860
+ case ED_NODE_CONTEXT_CONSUMER:
861
+ tag = ED_FIBER_CONTEXT_CONSUMER;
862
+ break;
863
+ default:
864
+ tag = ED_FIBER_HOST_COMPONENT;
865
+ break;
866
+ }
867
+
868
+ EDFiberNode* fiber = ed_fiber_create(tag, ED_FIBER_FLAG_NONE);
869
+ if (!fiber) return NULL;
870
+
871
+ fiber->pending_props = element;
872
+ fiber->index = index;
873
+
874
+ if (element->key[0] != '\0') {
875
+ strncpy(fiber->key, element->key, ED_MAX_KEY_LEN - 1);
876
+ fiber->key[ED_MAX_KEY_LEN - 1] = '\0';
877
+ fiber->key_hash = ed_hash_fnv1a(element->key);
878
+ }
879
+
880
+ ed_fiber_set_flag(fiber, ED_FIBER_FLAG_PLACEMENT);
881
+ return fiber;
882
+ }
883
+
884
+ EDFiberNode* ed_use_fiber(EDFiberNode* current, EDVirtualNode* pending_props)
885
+ {
886
+ if (!current) return ed_create_child(pending_props, 0);
887
+
888
+ EDFiberNode* clone = ed_fiber_clone(current);
889
+ if (!clone) return current; /* Fallback */
890
+
891
+ clone->pending_props = pending_props;
892
+ clone->index = current->index;
893
+ return clone;
894
+ }
895
+
896
+ uint32_t ed_delete_remaining_children(EDFiberNode* current)
897
+ {
898
+ if (!current) return 0;
899
+
900
+ uint32_t deleted = 0;
901
+ EDFiberNode* child = current->child;
902
+
903
+ while (child) {
904
+ ed_fiber_set_flag(child, ED_FIBER_FLAG_DELETION);
905
+ deleted++;
906
+ deleted += ed_delete_remaining_children(child);
907
+ child = child->sibling;
908
+ }
909
+
910
+ return deleted;
911
+ }
912
+
913
+ /* ================================================================
914
+ * Fiber Effect Bubble-Up
915
+ * ================================================================ */
916
+
917
+ static void ed_fiber_bubble_effects(EDFiberNode* completed_work)
918
+ {
919
+ if (!completed_work) return;
920
+
921
+ EDFiberFlags flags = completed_work->flags;
922
+
923
+ if (flags & (ED_FIBER_FLAG_PLACEMENT | ED_FIBER_FLAG_UPDATE |
924
+ ED_FIBER_FLAG_DELETION | ED_FIBER_FLAG_REF)) {
925
+ /* This fiber has work to do, add to parent's effect list */
926
+ /* In the full implementation, this would traverse up to root
927
+ * building the effect list */
928
+ }
929
+ }
930
+
931
+ /* ================================================================
932
+ * Fiber Tree Utilities
933
+ * ================================================================ */
934
+
935
+ static EDFiberNode* ed_fiber_find_deepest_child(EDFiberNode* fiber)
936
+ {
937
+ if (!fiber) return NULL;
938
+ while (fiber->child) {
939
+ fiber = fiber->child;
940
+ }
941
+ return fiber;
942
+ }
943
+
944
+ static EDFiberNode* ed_fiber_next_work(EDFiberNode* fiber)
945
+ {
946
+ if (!fiber) return NULL;
947
+
948
+ /* Child first */
949
+ if (fiber->child) {
950
+ return fiber->child;
951
+ }
952
+
953
+ /* Then sibling */
954
+ if (fiber->sibling) {
955
+ return fiber->sibling;
956
+ }
957
+
958
+ /* Then parent's sibling */
959
+ EDFiberNode* parent = fiber->return_node;
960
+ while (parent) {
961
+ if (parent->sibling) {
962
+ return parent->sibling;
963
+ }
964
+ parent = parent->return_node;
965
+ }
966
+
967
+ return NULL;
968
+ }
969
+
970
+ static void ed_fiber_collect_deletions(EDFiberNode* fiber, EDFiberNode** deletion_list,
971
+ uint32_t* count, uint32_t max)
972
+ {
973
+ if (!fiber || !deletion_list || !count) return;
974
+
975
+ if (ed_fiber_has_effect(fiber, ED_FIBER_FLAG_DELETION)) {
976
+ if (*count < max) {
977
+ deletion_list[*count] = fiber;
978
+ (*count)++;
979
+ }
980
+ }
981
+
982
+ EDFiberNode* child = fiber->child;
983
+ while (child) {
984
+ ed_fiber_collect_deletions(child, deletion_list, count, max);
985
+ child = child->sibling;
986
+ }
987
+ }
988
+
989
+ /* ================================================================
990
+ * Fiber Work Loop (simplified)
991
+ * ================================================================ */
992
+
993
+ typedef enum {
994
+ ED_WORK_PHASE_RENDER = 0,
995
+ ED_WORK_PHASE_COMMIT = 1,
996
+ ED_WORK_PHASE_COMPLETE = 2
997
+ } EDWorkPhase;
998
+
999
+ static EDWorkPhase ed_fiber_perform_work(EDFiberNode* fiber)
1000
+ {
1001
+ if (!fiber) return ED_WORK_PHASE_COMPLETE;
1002
+
1003
+ ed_fiber_set_flag(fiber, ED_FIBER_FLAG_PERFORMED_WORK);
1004
+ fiber->start_time = ed_get_timestamp_ms();
1005
+
1006
+ /* Perform work based on fiber tag */
1007
+ switch (fiber->tag) {
1008
+ case ED_FIBER_HOST_ROOT:
1009
+ /* Process root */
1010
+ break;
1011
+ case ED_FIBER_HOST_COMPONENT:
1012
+ case ED_FIBER_HOST_TEXT:
1013
+ /* Host components: reconcile children */
1014
+ ed_reconcile_children(fiber->alternate, fiber);
1015
+ break;
1016
+ case ED_FIBER_FUNCTION_COMPONENT:
1017
+ case ED_FIBER_CLASS_COMPONENT:
1018
+ /* Render component and reconcile */
1019
+ ed_reconcile_children(fiber->alternate, fiber);
1020
+ break;
1021
+ default:
1022
+ break;
1023
+ }
1024
+
1025
+ fiber->finish_time = ed_get_timestamp_ms();
1026
+ return ED_WORK_PHASE_RENDER;
1027
+ }