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,919 @@
1
+ /*
2
+ * hooks.c - ElementDrawing Core: Hook System
3
+ *
4
+ * Implements the React-like hook system for the ElementDrawing engine.
5
+ * Supports useState, useReducer, useEffect, useLayoutEffect,
6
+ * useCallback, useMemo, useRef, useContext, and other hook types.
7
+ * Includes hook queue management, dependency tracking, effect
8
+ * scheduling, and cleanup execution.
9
+ */
10
+
11
+ #include "bridge.h"
12
+
13
+ /* ================================================================
14
+ * Internal Hook State
15
+ * ================================================================ */
16
+
17
+ /* Current fiber being rendered (hook context) */
18
+ static EDFiberNode* g_current_rendering_fiber = NULL;
19
+ /* Current hook being processed */
20
+ static EDHookNode* g_current_hook = NULL;
21
+ /* Hook index within current component */
22
+ static uint32_t g_hook_index = 0;
23
+ /* Whether we are currently rendering (for hook rule enforcement) */
24
+ static bool g_is_rendering = false;
25
+ /* Number of hook rule violations detected */
26
+ static uint32_t g_hook_violations = 0;
27
+
28
+ /* ================================================================
29
+ * Hook Context Management
30
+ * ================================================================ */
31
+
32
+ static void ed_hook_enter_render(EDFiberNode* fiber)
33
+ {
34
+ g_current_rendering_fiber = fiber;
35
+ g_current_hook = fiber ? fiber->memoized_hooks : NULL;
36
+ g_hook_index = 0;
37
+ g_is_rendering = true;
38
+ }
39
+
40
+ static void ed_hook_exit_render(void)
41
+ {
42
+ g_current_rendering_fiber = NULL;
43
+ g_current_hook = NULL;
44
+ g_hook_index = 0;
45
+ g_is_rendering = false;
46
+ }
47
+
48
+ static EDHookNode* ed_hook_get_next(void)
49
+ {
50
+ if (!g_current_rendering_fiber) return NULL;
51
+
52
+ if (g_current_hook) {
53
+ g_current_hook = g_current_hook->next;
54
+ }
55
+
56
+ if (g_current_hook) {
57
+ /* Reuse existing hook during update */
58
+ return g_current_hook;
59
+ }
60
+
61
+ return NULL;
62
+ }
63
+
64
+ static void ed_hook_append(EDFiberNode* fiber, EDHookNode* hook)
65
+ {
66
+ if (!fiber || !hook) return;
67
+
68
+ hook->index = g_hook_index;
69
+
70
+ if (!fiber->memoized_hooks) {
71
+ fiber->memoized_hooks = hook;
72
+ } else {
73
+ EDHookNode* tail = fiber->memoized_hooks;
74
+ while (tail->next) {
75
+ tail = tail->next;
76
+ }
77
+ tail->next = hook;
78
+ }
79
+
80
+ g_current_hook = hook;
81
+ g_hook_index++;
82
+ }
83
+
84
+ /* ================================================================
85
+ * Hook Creation and Destruction
86
+ * ================================================================ */
87
+
88
+ EDHookNode* ed_hook_create(EDHookType type, uint32_t index)
89
+ {
90
+ EDHookNode* hook = (EDHookNode*)calloc(1, sizeof(EDHookNode));
91
+ if (!hook) {
92
+ ed_log_error("Failed to allocate hook node (type=%d)", type);
93
+ return NULL;
94
+ }
95
+
96
+ hook->type = type;
97
+ hook->index = index;
98
+ hook->memoized_state = NULL;
99
+ hook->queue = NULL;
100
+ hook->base_queue = NULL;
101
+ hook->next = NULL;
102
+ hook->has_effect = false;
103
+ hook->is_layout = false;
104
+ hook->is_passive = false;
105
+ hook->is_insertion = false;
106
+ hook->cleanup = NULL;
107
+ hook->effect_callback = NULL;
108
+ hook->deps = NULL;
109
+ hook->deps_count = 0;
110
+ hook->fiber = NULL;
111
+
112
+ return hook;
113
+ }
114
+
115
+ void ed_hook_destroy(EDHookNode* hook)
116
+ {
117
+ if (!hook) return;
118
+
119
+ /* Run cleanup if present */
120
+ if (hook->cleanup) {
121
+ ed_hook_run_cleanup(hook);
122
+ }
123
+
124
+ /* Free dependency array */
125
+ if (hook->deps) {
126
+ free(hook->deps);
127
+ hook->deps = NULL;
128
+ }
129
+
130
+ /* Free update queue */
131
+ if (hook->queue) {
132
+ free(hook->queue);
133
+ hook->queue = NULL;
134
+ }
135
+ if (hook->base_queue) {
136
+ free(hook->base_queue);
137
+ hook->base_queue = NULL;
138
+ }
139
+
140
+ /* Free memoized state for certain hook types */
141
+ if (hook->memoized_state) {
142
+ switch (hook->type) {
143
+ case ED_HOOK_STATE:
144
+ case ED_HOOK_REDUCER:
145
+ /* State values are user-managed, don't free */
146
+ break;
147
+ case ED_HOOK_EFFECT:
148
+ case ED_HOOK_LAYOUT_EFFECT:
149
+ case ED_HOOK_INSERTION_EFFECT:
150
+ /* Effect data is user-managed */
151
+ break;
152
+ default:
153
+ break;
154
+ }
155
+ }
156
+
157
+ /* Note: hook->next is NOT freed here - it's a linked list
158
+ * managed by the fiber's hook chain */
159
+
160
+ free(hook);
161
+ }
162
+
163
+ /* ================================================================
164
+ * Hook Queue Implementation
165
+ * ================================================================ */
166
+
167
+ EDHookQueue* ed_hook_queue_create(void)
168
+ {
169
+ EDHookQueue* queue = (EDHookQueue*)calloc(1, sizeof(EDHookQueue));
170
+ if (!queue) return NULL;
171
+ queue->head = NULL;
172
+ queue->tail = NULL;
173
+ queue->count = 0;
174
+ return queue;
175
+ }
176
+
177
+ void ed_hook_queue_push(EDHookQueue* queue, EDHookNode* hook)
178
+ {
179
+ if (!queue || !hook) return;
180
+
181
+ hook->next = NULL;
182
+
183
+ if (!queue->tail) {
184
+ queue->head = hook;
185
+ queue->tail = hook;
186
+ } else {
187
+ queue->tail->next = hook;
188
+ queue->tail = hook;
189
+ }
190
+ queue->count++;
191
+ }
192
+
193
+ EDHookNode* ed_hook_queue_pop(EDHookQueue* queue)
194
+ {
195
+ if (!queue || !queue->head) return NULL;
196
+
197
+ EDHookNode* hook = queue->head;
198
+ queue->head = hook->next;
199
+ if (!queue->head) {
200
+ queue->tail = NULL;
201
+ }
202
+ hook->next = NULL;
203
+ queue->count--;
204
+
205
+ return hook;
206
+ }
207
+
208
+ /* ================================================================
209
+ * Hook State Management
210
+ * ================================================================ */
211
+
212
+ typedef struct EDHookStateValue {
213
+ void* value;
214
+ void* base_value;
215
+ uint32_t version;
216
+ EDStateUpdater reducer;
217
+ } EDHookStateValue;
218
+
219
+ void ed_hook_set_state(EDHookNode* hook, void* state)
220
+ {
221
+ if (!hook) return;
222
+
223
+ switch (hook->type) {
224
+ case ED_HOOK_STATE:
225
+ case ED_HOOK_REDUCER: {
226
+ EDHookStateValue* sv = (EDHookStateValue*)hook->memoized_state;
227
+ if (sv) {
228
+ sv->base_value = sv->value;
229
+ sv->value = state;
230
+ sv->version++;
231
+ } else {
232
+ sv = (EDHookStateValue*)calloc(1, sizeof(EDHookStateValue));
233
+ if (sv) {
234
+ sv->value = state;
235
+ sv->base_value = NULL;
236
+ sv->version = 1;
237
+ hook->memoized_state = sv;
238
+ }
239
+ }
240
+ break;
241
+ }
242
+ default:
243
+ hook->memoized_state = state;
244
+ break;
245
+ }
246
+ }
247
+
248
+ void* ed_hook_get_state(EDHookNode* hook)
249
+ {
250
+ if (!hook) return NULL;
251
+
252
+ switch (hook->type) {
253
+ case ED_HOOK_STATE:
254
+ case ED_HOOK_REDUCER: {
255
+ EDHookStateValue* sv = (EDHookStateValue*)hook->memoized_state;
256
+ return sv ? sv->value : NULL;
257
+ }
258
+ default:
259
+ return hook->memoized_state;
260
+ }
261
+ }
262
+
263
+ /* ================================================================
264
+ * Hook Effect Management
265
+ * ================================================================ */
266
+
267
+ typedef struct EDHookEffectData {
268
+ void* callback;
269
+ void* cleanup;
270
+ void* deps;
271
+ uint32_t deps_count;
272
+ bool has_run;
273
+ } EDHookEffectData;
274
+
275
+ void ed_hook_set_effect(EDHookNode* hook, void* callback, void* deps, uint32_t deps_count)
276
+ {
277
+ if (!hook) return;
278
+
279
+ EDHookEffectData* effect_data = (EDHookEffectData*)hook->memoized_state;
280
+ if (!effect_data) {
281
+ effect_data = (EDHookEffectData*)calloc(1, sizeof(EDHookEffectData));
282
+ if (!effect_data) return;
283
+ hook->memoized_state = effect_data;
284
+ }
285
+
286
+ effect_data->callback = callback;
287
+ effect_data->deps = deps;
288
+ effect_data->deps_count = deps_count;
289
+ effect_data->has_run = false;
290
+
291
+ hook->effect_callback = callback;
292
+ hook->cleanup = NULL; /* Will be set after first run */
293
+ hook->deps = deps;
294
+ hook->deps_count = deps_count;
295
+
296
+ /* Determine effect type */
297
+ switch (hook->type) {
298
+ case ED_HOOK_EFFECT:
299
+ case ED_HOOK_DEFERRED_VALUE:
300
+ hook->is_passive = true;
301
+ hook->is_layout = false;
302
+ hook->is_insertion = false;
303
+ break;
304
+ case ED_HOOK_LAYOUT_EFFECT:
305
+ hook->is_layout = true;
306
+ hook->is_passive = false;
307
+ hook->is_insertion = false;
308
+ break;
309
+ case ED_HOOK_INSERTION_EFFECT:
310
+ hook->is_insertion = true;
311
+ hook->is_layout = false;
312
+ hook->is_passive = false;
313
+ break;
314
+ default:
315
+ hook->is_passive = true;
316
+ break;
317
+ }
318
+
319
+ /* Check if deps changed to determine if effect should run */
320
+ if (hook->deps && hook->deps_count > 0) {
321
+ EDHookEffectData* prev = (EDHookEffectData*)hook->memoized_state;
322
+ /* For first run, always mark as having effect */
323
+ if (!prev || !prev->has_run) {
324
+ hook->has_effect = true;
325
+ } else {
326
+ hook->has_effect = ed_hook_deps_changed(
327
+ prev->deps, deps, deps_count);
328
+ }
329
+ } else {
330
+ /* No deps = run every render */
331
+ hook->has_effect = true;
332
+ }
333
+ }
334
+
335
+ void ed_hook_run_effect(EDHookNode* hook)
336
+ {
337
+ if (!hook) return;
338
+
339
+ EDHookEffectData* effect_data = (EDHookEffectData*)hook->memoized_state;
340
+ if (!effect_data) return;
341
+
342
+ /* Run cleanup from previous effect first */
343
+ if (effect_data->cleanup) {
344
+ ed_hook_run_cleanup(hook);
345
+ }
346
+
347
+ /* Execute the effect callback */
348
+ if (effect_data->callback) {
349
+ /* In a real implementation, this would call the JS/native effect */
350
+ ed_log_debug("Running effect (hook type=%d, index=%u)",
351
+ hook->type, hook->index);
352
+ effect_data->has_run = true;
353
+ }
354
+
355
+ hook->has_effect = false;
356
+ }
357
+
358
+ void ed_hook_run_cleanup(EDHookNode* hook)
359
+ {
360
+ if (!hook) return;
361
+
362
+ EDHookEffectData* effect_data = (EDHookEffectData*)hook->memoized_state;
363
+ if (!effect_data || !effect_data->cleanup) return;
364
+
365
+ ed_log_debug("Running cleanup for hook (type=%d, index=%u)",
366
+ hook->type, hook->index);
367
+
368
+ /* Execute cleanup function */
369
+ /* In a real implementation, this would call the cleanup callback */
370
+ effect_data->cleanup = NULL;
371
+ hook->cleanup = NULL;
372
+ }
373
+
374
+ /* ================================================================
375
+ * Dependency Comparison
376
+ * ================================================================ */
377
+
378
+ bool ed_hook_deps_changed(void* prev_deps, void* next_deps, uint32_t count)
379
+ {
380
+ if (count == 0) return true; /* No deps = always changed */
381
+ if (!prev_deps || !next_deps) return true;
382
+ if (prev_deps == next_deps) return false; /* Same pointer = same deps */
383
+
384
+ /* Compare dependency arrays byte-by-byte */
385
+ /* In a real implementation, deps would be an array of values
386
+ * with known types. Here we do a simple pointer/value comparison. */
387
+ uintptr_t* prev = (uintptr_t*)prev_deps;
388
+ uintptr_t* next = (uintptr_t*)next_deps;
389
+
390
+ for (uint32_t i = 0; i < count; i++) {
391
+ if (prev[i] != next[i]) return true;
392
+ }
393
+
394
+ return false;
395
+ }
396
+
397
+ /* ================================================================
398
+ * useState Implementation
399
+ * ================================================================ */
400
+
401
+ typedef struct EDUseStateResult {
402
+ void* value;
403
+ void (*setter)(void* new_value);
404
+ EDHookNode* hook;
405
+ } EDUseStateResult;
406
+
407
+ static EDUseStateResult ed_use_state_impl(EDFiberNode* fiber, void* initial_value)
408
+ {
409
+ EDUseStateResult result = {0};
410
+
411
+ if (!fiber) {
412
+ g_hook_violations++;
413
+ ed_log_error("useState called outside of render");
414
+ return result;
415
+ }
416
+
417
+ EDHookNode* hook = ed_hook_get_next();
418
+
419
+ if (hook) {
420
+ /* Update: reuse existing state */
421
+ if (hook->type != ED_HOOK_STATE) {
422
+ g_hook_violations++;
423
+ ed_log_error("Hook type mismatch: expected STATE, got %d", hook->type);
424
+ return result;
425
+ }
426
+ } else {
427
+ /* Mount: create new hook */
428
+ hook = ed_hook_create(ED_HOOK_STATE, g_hook_index);
429
+ if (!hook) return result;
430
+
431
+ EDHookStateValue* sv = (EDHookStateValue*)calloc(1, sizeof(EDHookStateValue));
432
+ if (sv) {
433
+ sv->value = initial_value;
434
+ sv->base_value = initial_value;
435
+ sv->version = 0;
436
+ sv->reducer = NULL;
437
+ }
438
+ hook->memoized_state = sv;
439
+ hook->fiber = fiber;
440
+
441
+ ed_hook_append(fiber, hook);
442
+ }
443
+
444
+ EDHookStateValue* sv = (EDHookStateValue*)hook->memoized_state;
445
+ result.value = sv ? sv->value : NULL;
446
+ result.hook = hook;
447
+ /* setter would be a closure in a real implementation */
448
+
449
+ return result;
450
+ }
451
+
452
+ /* ================================================================
453
+ * useReducer Implementation
454
+ * ================================================================ */
455
+
456
+ typedef struct EDUseReducerResult {
457
+ void* value;
458
+ void (*dispatch)(void* action);
459
+ EDHookNode* hook;
460
+ } EDUseReducerResult;
461
+
462
+ static EDUseReducerResult ed_use_reducer_impl(EDFiberNode* fiber,
463
+ EDStateUpdater reducer,
464
+ void* initial_value)
465
+ {
466
+ EDUseReducerResult result = {0};
467
+
468
+ if (!fiber || !reducer) {
469
+ g_hook_violations++;
470
+ return result;
471
+ }
472
+
473
+ EDHookNode* hook = ed_hook_get_next();
474
+
475
+ if (hook) {
476
+ if (hook->type != ED_HOOK_REDUCER) {
477
+ g_hook_violations++;
478
+ ed_log_error("Hook type mismatch: expected REDUCER, got %d", hook->type);
479
+ return result;
480
+ }
481
+ } else {
482
+ hook = ed_hook_create(ED_HOOK_REDUCER, g_hook_index);
483
+ if (!hook) return result;
484
+
485
+ EDHookStateValue* sv = (EDHookStateValue*)calloc(1, sizeof(EDHookStateValue));
486
+ if (sv) {
487
+ sv->value = initial_value;
488
+ sv->base_value = initial_value;
489
+ sv->version = 0;
490
+ sv->reducer = reducer;
491
+ }
492
+ hook->memoized_state = sv;
493
+ hook->fiber = fiber;
494
+
495
+ ed_hook_append(fiber, hook);
496
+ }
497
+
498
+ EDHookStateValue* sv = (EDHookStateValue*)hook->memoized_state;
499
+ result.value = sv ? sv->value : NULL;
500
+ result.hook = hook;
501
+
502
+ return result;
503
+ }
504
+
505
+ /* ================================================================
506
+ * useEffect / useLayoutEffect / useInsertionEffect Implementation
507
+ * ================================================================ */
508
+
509
+ static void ed_use_effect_impl(EDFiberNode* fiber, EDHookType type,
510
+ void* callback, void* deps, uint32_t deps_count)
511
+ {
512
+ if (!fiber) {
513
+ g_hook_violations++;
514
+ ed_log_error("useEffect called outside of render");
515
+ return;
516
+ }
517
+
518
+ EDHookNode* hook = ed_hook_get_next();
519
+
520
+ if (hook) {
521
+ /* Update existing hook */
522
+ bool deps_changed = true;
523
+ if (hook->deps && deps && hook->deps_count == deps_count) {
524
+ deps_changed = ed_hook_deps_changed(hook->deps, deps, deps_count);
525
+ }
526
+
527
+ ed_hook_set_effect(hook, callback, deps, deps_count);
528
+
529
+ if (deps_changed) {
530
+ hook->has_effect = true;
531
+ ed_fiber_set_flag(fiber, ED_FIBER_FLAG_HAS_EFFECT);
532
+
533
+ /* Add to appropriate effect list */
534
+ ed_fiber_add_effect(fiber, fiber);
535
+ }
536
+ } else {
537
+ /* Mount: create new effect hook */
538
+ hook = ed_hook_create(type, g_hook_index);
539
+ if (!hook) return;
540
+
541
+ hook->fiber = fiber;
542
+ ed_hook_set_effect(hook, callback, deps, deps_count);
543
+ hook->has_effect = true; /* Always run on mount */
544
+
545
+ ed_fiber_set_flag(fiber, ED_FIBER_FLAG_HAS_EFFECT);
546
+ ed_fiber_add_effect(fiber, fiber);
547
+
548
+ ed_hook_append(fiber, hook);
549
+ }
550
+ }
551
+
552
+ /* ================================================================
553
+ * useMemo Implementation
554
+ * ================================================================ */
555
+
556
+ typedef struct EDMemoValue {
557
+ void* value;
558
+ void* deps;
559
+ uint32_t deps_count;
560
+ } EDMemoValue;
561
+
562
+ static void* ed_use_memo_impl(EDFiberNode* fiber, void* (*factory)(void),
563
+ void* deps, uint32_t deps_count)
564
+ {
565
+ if (!fiber) {
566
+ g_hook_violations++;
567
+ return NULL;
568
+ }
569
+
570
+ EDHookNode* hook = ed_hook_get_next();
571
+
572
+ if (hook) {
573
+ if (hook->type != ED_HOOK_MEMO) {
574
+ g_hook_violations++;
575
+ return NULL;
576
+ }
577
+
578
+ EDMemoValue* memo = (EDMemoValue*)hook->memoized_state;
579
+ if (memo) {
580
+ bool changed = ed_hook_deps_changed(memo->deps, deps, deps_count);
581
+ if (!changed) {
582
+ return memo->value; /* Return cached value */
583
+ }
584
+
585
+ /* Recompute */
586
+ if (memo->deps) free(memo->deps);
587
+ memo->deps = deps;
588
+ memo->deps_count = deps_count;
589
+ if (factory) {
590
+ memo->value = factory();
591
+ }
592
+ return memo->value;
593
+ }
594
+ }
595
+
596
+ /* Mount */
597
+ hook = ed_hook_create(ED_HOOK_MEMO, g_hook_index);
598
+ if (!hook) return NULL;
599
+
600
+ EDMemoValue* memo = (EDMemoValue*)calloc(1, sizeof(EDMemoValue));
601
+ if (memo) {
602
+ memo->deps = deps;
603
+ memo->deps_count = deps_count;
604
+ if (factory) {
605
+ memo->value = factory();
606
+ }
607
+ }
608
+ hook->memoized_state = memo;
609
+ hook->fiber = fiber;
610
+ ed_hook_append(fiber, hook);
611
+
612
+ return memo ? memo->value : NULL;
613
+ }
614
+
615
+ /* ================================================================
616
+ * useCallback Implementation
617
+ * ================================================================ */
618
+
619
+ typedef struct EDCallbackValue {
620
+ void* callback;
621
+ void* deps;
622
+ uint32_t deps_count;
623
+ } EDCallbackValue;
624
+
625
+ static void* ed_use_callback_impl(EDFiberNode* fiber, void* callback,
626
+ void* deps, uint32_t deps_count)
627
+ {
628
+ if (!fiber) {
629
+ g_hook_violations++;
630
+ return NULL;
631
+ }
632
+
633
+ EDHookNode* hook = ed_hook_get_next();
634
+
635
+ if (hook) {
636
+ if (hook->type != ED_HOOK_CALLBACK) {
637
+ g_hook_violations++;
638
+ return NULL;
639
+ }
640
+
641
+ EDCallbackValue* cb = (EDCallbackValue*)hook->memoized_state;
642
+ if (cb) {
643
+ bool changed = ed_hook_deps_changed(cb->deps, deps, deps_count);
644
+ if (!changed) {
645
+ return cb->callback; /* Return cached callback */
646
+ }
647
+
648
+ if (cb->deps) free(cb->deps);
649
+ cb->callback = callback;
650
+ cb->deps = deps;
651
+ cb->deps_count = deps_count;
652
+ return callback;
653
+ }
654
+ }
655
+
656
+ /* Mount */
657
+ hook = ed_hook_create(ED_HOOK_CALLBACK, g_hook_index);
658
+ if (!hook) return NULL;
659
+
660
+ EDCallbackValue* cb = (EDCallbackValue*)calloc(1, sizeof(EDCallbackValue));
661
+ if (cb) {
662
+ cb->callback = callback;
663
+ cb->deps = deps;
664
+ cb->deps_count = deps_count;
665
+ }
666
+ hook->memoized_state = cb;
667
+ hook->fiber = fiber;
668
+ ed_hook_append(fiber, hook);
669
+
670
+ return callback;
671
+ }
672
+
673
+ /* ================================================================
674
+ * useRef Implementation
675
+ * ================================================================ */
676
+
677
+ typedef struct EDRefValue {
678
+ void* current;
679
+ } EDRefValue;
680
+
681
+ static EDRefValue* ed_use_ref_impl(EDFiberNode* fiber, void* initial_value)
682
+ {
683
+ if (!fiber) {
684
+ g_hook_violations++;
685
+ return NULL;
686
+ }
687
+
688
+ EDHookNode* hook = ed_hook_get_next();
689
+
690
+ if (hook) {
691
+ return (EDRefValue*)hook->memoized_state;
692
+ }
693
+
694
+ /* Mount */
695
+ hook = ed_hook_create(ED_HOOK_REF, g_hook_index);
696
+ if (!hook) return NULL;
697
+
698
+ EDRefValue* ref = (EDRefValue*)calloc(1, sizeof(EDRefValue));
699
+ if (ref) {
700
+ ref->current = initial_value;
701
+ }
702
+ hook->memoized_state = ref;
703
+ hook->fiber = fiber;
704
+ ed_hook_append(fiber, hook);
705
+
706
+ return ref;
707
+ }
708
+
709
+ /* ================================================================
710
+ * useContext Implementation
711
+ * ================================================================ */
712
+
713
+ typedef struct EDContextValue {
714
+ void* value;
715
+ EDFiberNode* provider;
716
+ } EDContextValue;
717
+
718
+ static void* ed_use_context_impl(EDFiberNode* fiber, void* context)
719
+ {
720
+ if (!fiber) {
721
+ g_hook_violations++;
722
+ return NULL;
723
+ }
724
+
725
+ EDHookNode* hook = ed_hook_get_next();
726
+
727
+ if (hook) {
728
+ EDContextValue* ctx = (EDContextValue*)hook->memoized_state;
729
+ return ctx ? ctx->value : NULL;
730
+ }
731
+
732
+ /* Mount */
733
+ hook = ed_hook_create(ED_HOOK_CONTEXT, g_hook_index);
734
+ if (!hook) return NULL;
735
+
736
+ EDContextValue* ctx = (EDContextValue*)calloc(1, sizeof(EDContextValue));
737
+ if (ctx) {
738
+ ctx->value = context;
739
+ ctx->provider = NULL; /* Would be resolved in a real impl */
740
+ }
741
+ hook->memoized_state = ctx;
742
+ hook->fiber = fiber;
743
+ ed_hook_append(fiber, hook);
744
+
745
+ return ctx ? ctx->value : NULL;
746
+ }
747
+
748
+ /* ================================================================
749
+ * Hook Rule Enforcement
750
+ * ================================================================ */
751
+
752
+ static bool ed_hook_check_rules(const char* hook_name)
753
+ {
754
+ if (!g_is_rendering) {
755
+ g_hook_violations++;
756
+ ed_log_error("%s called outside of render phase (violation #%u)",
757
+ hook_name, g_hook_violations);
758
+ return false;
759
+ }
760
+
761
+ if (!g_current_rendering_fiber) {
762
+ g_hook_violations++;
763
+ ed_log_error("%s called with no current fiber", hook_name);
764
+ return false;
765
+ }
766
+
767
+ if (g_hook_index >= ED_HOOKS_MAX_PER_COMPONENT) {
768
+ g_hook_violations++;
769
+ ed_log_error("%s exceeded max hooks per component (%u)",
770
+ hook_name, ED_HOOKS_MAX_PER_COMPONENT);
771
+ return false;
772
+ }
773
+
774
+ return true;
775
+ }
776
+
777
+ /* ================================================================
778
+ * Hook Chain Processing
779
+ * ================================================================ */
780
+
781
+ /* Process all hooks for a fiber during the render phase */
782
+ static void ed_hook_process_chain(EDFiberNode* fiber)
783
+ {
784
+ if (!fiber) return;
785
+
786
+ ed_hook_enter_render(fiber);
787
+
788
+ EDHookNode* hook = fiber->memoized_hooks;
789
+ while (hook) {
790
+ /* Process hook based on type */
791
+ switch (hook->type) {
792
+ case ED_HOOK_STATE:
793
+ case ED_HOOK_REDUCER: {
794
+ /* Process any queued updates */
795
+ EDHookStateValue* sv = (EDHookStateValue*)hook->memoized_state;
796
+ if (sv && sv->reducer && hook->queue) {
797
+ /* Apply reducer with queued actions */
798
+ ed_log_debug("Processing state hook %u", hook->index);
799
+ }
800
+ break;
801
+ }
802
+ case ED_HOOK_EFFECT:
803
+ case ED_HOOK_LAYOUT_EFFECT:
804
+ case ED_HOOK_INSERTION_EFFECT: {
805
+ /* Effects are processed during commit phase, not render */
806
+ break;
807
+ }
808
+ case ED_HOOK_MEMO:
809
+ case ED_HOOK_CALLBACK: {
810
+ /* Memo/callback hooks are processed during access */
811
+ break;
812
+ }
813
+ case ED_HOOK_REF: {
814
+ /* Refs are just containers, no processing needed */
815
+ break;
816
+ }
817
+ case ED_HOOK_CONTEXT: {
818
+ /* Check if context value changed */
819
+ break;
820
+ }
821
+ case ED_HOOK_DEBUG_VALUE: {
822
+ /* No-op in production */
823
+ break;
824
+ }
825
+ default:
826
+ break;
827
+ }
828
+
829
+ hook = hook->next;
830
+ }
831
+
832
+ ed_hook_exit_render();
833
+ }
834
+
835
+ /* ================================================================
836
+ * Hook Effect Commit Processing
837
+ * ================================================================ */
838
+
839
+ /* Process layout effects synchronously during commit */
840
+ static void ed_hook_commit_layout_effects(EDFiberNode* fiber)
841
+ {
842
+ if (!fiber) return;
843
+
844
+ EDHookNode* hook = fiber->memoized_hooks;
845
+ while (hook) {
846
+ if (hook->is_layout && hook->has_effect) {
847
+ ed_hook_run_effect(hook);
848
+ }
849
+ hook = hook->next;
850
+ }
851
+ }
852
+
853
+ /* Schedule passive effects for after paint */
854
+ static void ed_hook_schedule_passive_effects(EDFiberNode* fiber)
855
+ {
856
+ if (!fiber) return;
857
+
858
+ EDHookNode* hook = fiber->memoized_hooks;
859
+ while (hook) {
860
+ if (hook->is_passive && hook->has_effect) {
861
+ /* In a real implementation, this would schedule
862
+ * the effect via the scheduler at idle priority */
863
+ ed_log_debug("Scheduling passive effect (hook %u)", hook->index);
864
+ }
865
+ hook = hook->next;
866
+ }
867
+ }
868
+
869
+ /* Process insertion effects (run before layout effects) */
870
+ static void ed_hook_commit_insertion_effects(EDFiberNode* fiber)
871
+ {
872
+ if (!fiber) return;
873
+
874
+ EDHookNode* hook = fiber->memoized_hooks;
875
+ while (hook) {
876
+ if (hook->is_insertion && hook->has_effect) {
877
+ ed_hook_run_effect(hook);
878
+ }
879
+ hook = hook->next;
880
+ }
881
+ }
882
+
883
+ /* ================================================================
884
+ * Hook Cleanup on Unmount
885
+ * ================================================================ */
886
+
887
+ static void ed_hook_cleanup_fiber(EDFiberNode* fiber)
888
+ {
889
+ if (!fiber) return;
890
+
891
+ EDHookNode* hook = fiber->memoized_hooks;
892
+ while (hook) {
893
+ /* Run cleanup for all effect hooks */
894
+ switch (hook->type) {
895
+ case ED_HOOK_EFFECT:
896
+ case ED_HOOK_LAYOUT_EFFECT:
897
+ case ED_HOOK_INSERTION_EFFECT:
898
+ ed_hook_run_cleanup(hook);
899
+ break;
900
+ default:
901
+ break;
902
+ }
903
+ hook = hook->next;
904
+ }
905
+ }
906
+
907
+ /* ================================================================
908
+ * Hook Violation Tracking
909
+ * ================================================================ */
910
+
911
+ static uint32_t ed_hook_get_violation_count(void)
912
+ {
913
+ return g_hook_violations;
914
+ }
915
+
916
+ static void ed_hook_reset_violations(void)
917
+ {
918
+ g_hook_violations = 0;
919
+ }