radui 1.0.0__py3-none-any.whl

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 (111) hide show
  1. genesis/__init__.py +563 -0
  2. genesis/__main__.py +9 -0
  3. genesis/ai/__init__.py +21 -0
  4. genesis/ai/a2ui.py +82 -0
  5. genesis/ai/ag_ui.py +84 -0
  6. genesis/ai/mcp.py +46 -0
  7. genesis/ai/persistence.py +157 -0
  8. genesis/ai/streaming.py +95 -0
  9. genesis/benchmarks/__init__.py +5 -0
  10. genesis/benchmarks/suite.py +336 -0
  11. genesis/cli.py +1225 -0
  12. genesis/components/__init__.py +297 -0
  13. genesis/components/model_forms.py +149 -0
  14. genesis/components/nodes.py +2014 -0
  15. genesis/core/__init__.py +15 -0
  16. genesis/core/app.py +1441 -0
  17. genesis/core/auth.py +53 -0
  18. genesis/core/idempotency.py +1042 -0
  19. genesis/core/ir.py +48 -0
  20. genesis/core/outbox.py +475 -0
  21. genesis/core/registry.py +321 -0
  22. genesis/core/security.py +66 -0
  23. genesis/core/theme.py +96 -0
  24. genesis/data/__init__.py +23 -0
  25. genesis/data/browser.py +25 -0
  26. genesis/data/query.py +268 -0
  27. genesis/i18n/__init__.py +21 -0
  28. genesis/i18n/formatters.py +121 -0
  29. genesis/inspector/__init__.py +14 -0
  30. genesis/inspector/collector.py +131 -0
  31. genesis/inspector/graph.py +175 -0
  32. genesis/inspector/otlp.py +132 -0
  33. genesis/jobs/__init__.py +26 -0
  34. genesis/jobs/contracts.py +67 -0
  35. genesis/jobs/redis.py +118 -0
  36. genesis/jobs/runtime.py +585 -0
  37. genesis/jobs/sqlite.py +515 -0
  38. genesis/motion/__init__.py +198 -0
  39. genesis/motion/springs.py +75 -0
  40. genesis/motion/types.py +114 -0
  41. genesis/plugins/__init__.py +5 -0
  42. genesis/plugins/base.py +103 -0
  43. genesis/renderers/__init__.py +3 -0
  44. genesis/renderers/html.py +3808 -0
  45. genesis/state/__init__.py +45 -0
  46. genesis/state/backend.py +311 -0
  47. genesis/state/dependencies.py +218 -0
  48. genesis/state/middleware.py +38 -0
  49. genesis/state/refs.py +269 -0
  50. genesis/state/runtime.py +596 -0
  51. genesis/testing.py +132 -0
  52. genesis/transport/__init__.py +21 -0
  53. genesis/transport/events.py +717 -0
  54. radui/__init__.py +553 -0
  55. radui/__main__.py +9 -0
  56. radui/ai/__init__.py +21 -0
  57. radui/ai/a2ui.py +82 -0
  58. radui/ai/ag_ui.py +84 -0
  59. radui/ai/mcp.py +46 -0
  60. radui/ai/persistence.py +157 -0
  61. radui/ai/streaming.py +95 -0
  62. radui/benchmarks/__init__.py +5 -0
  63. radui/benchmarks/suite.py +336 -0
  64. radui/cli.py +1235 -0
  65. radui/components/__init__.py +297 -0
  66. radui/components/model_forms.py +149 -0
  67. radui/components/nodes.py +2028 -0
  68. radui/core/__init__.py +15 -0
  69. radui/core/app.py +1441 -0
  70. radui/core/auth.py +53 -0
  71. radui/core/idempotency.py +1042 -0
  72. radui/core/ir.py +48 -0
  73. radui/core/outbox.py +475 -0
  74. radui/core/registry.py +321 -0
  75. radui/core/security.py +66 -0
  76. radui/core/theme.py +96 -0
  77. radui/data/__init__.py +23 -0
  78. radui/data/browser.py +25 -0
  79. radui/data/query.py +268 -0
  80. radui/i18n/__init__.py +21 -0
  81. radui/i18n/formatters.py +121 -0
  82. radui/inspector/__init__.py +14 -0
  83. radui/inspector/collector.py +131 -0
  84. radui/inspector/graph.py +175 -0
  85. radui/inspector/otlp.py +132 -0
  86. radui/jobs/__init__.py +26 -0
  87. radui/jobs/contracts.py +67 -0
  88. radui/jobs/redis.py +118 -0
  89. radui/jobs/runtime.py +585 -0
  90. radui/jobs/sqlite.py +515 -0
  91. radui/motion/__init__.py +198 -0
  92. radui/motion/springs.py +75 -0
  93. radui/motion/types.py +114 -0
  94. radui/plugins/__init__.py +5 -0
  95. radui/plugins/base.py +103 -0
  96. radui/renderers/__init__.py +3 -0
  97. radui/renderers/html.py +4093 -0
  98. radui/state/__init__.py +45 -0
  99. radui/state/backend.py +311 -0
  100. radui/state/dependencies.py +218 -0
  101. radui/state/middleware.py +38 -0
  102. radui/state/refs.py +269 -0
  103. radui/state/runtime.py +596 -0
  104. radui/testing.py +132 -0
  105. radui/transport/__init__.py +21 -0
  106. radui/transport/events.py +717 -0
  107. radui-1.0.0.dist-info/METADATA +763 -0
  108. radui-1.0.0.dist-info/RECORD +111 -0
  109. radui-1.0.0.dist-info/WHEEL +4 -0
  110. radui-1.0.0.dist-info/entry_points.txt +3 -0
  111. radui-1.0.0.dist-info/licenses/LICENSE +202 -0
genesis/__init__.py ADDED
@@ -0,0 +1,563 @@
1
+ """RADUI Core 1.0.0: Python-first frontend application framework for AI, analytics, and full-stack software."""
2
+
3
+ import warnings
4
+
5
+ warnings.warn(
6
+ "The 'genesis' package has been rebranded to 'radui' (Reactive Application Development User Interface). "
7
+ "Please update your imports to 'import radui as ui'. "
8
+ "'genesis' will remain as a backward-compatible alias until v2.0.",
9
+ DeprecationWarning,
10
+ stacklevel=2,
11
+ )
12
+
13
+ from radui.components import (
14
+ Accordion,
15
+ AccordionItem,
16
+ ActionCard,
17
+ Avatar,
18
+ AvatarGroup,
19
+ Badge,
20
+ Breadcrumb,
21
+ BreadcrumbItem,
22
+ Button,
23
+ Callout,
24
+ Card,
25
+ ChatInput,
26
+ ChatMessage,
27
+ ChatView,
28
+ Checkbox,
29
+ CodeBlock,
30
+ Column,
31
+ Component,
32
+ Container,
33
+ DataGrid,
34
+ DatePicker,
35
+ Diagram,
36
+ DiagramEdge,
37
+ DiagramNode,
38
+ Divider,
39
+ Drawer,
40
+ DropdownMenu,
41
+ DropdownMenuItem,
42
+ DynamicCard,
43
+ Element,
44
+ EmptyState,
45
+ ErrorBoundary,
46
+ FileUpload,
47
+ Form,
48
+ GenerativeSlot,
49
+ Grid,
50
+ Heading,
51
+ Input,
52
+ JobPanel,
53
+ Kbd,
54
+ Link,
55
+ MetricCard,
56
+ Modal,
57
+ Pagination,
58
+ Plot,
59
+ Popover,
60
+ ProgressBar,
61
+ ProgressCircle,
62
+ RadioGroup,
63
+ Row,
64
+ Select,
65
+ Skeleton,
66
+ Slider,
67
+ Spacer,
68
+ SplitPanel,
69
+ Stack,
70
+ StepTimeline,
71
+ StreamingText,
72
+ Switch,
73
+ TabItem,
74
+ Table,
75
+ Tabs,
76
+ Tag,
77
+ Text,
78
+ Textarea,
79
+ ThemeToggle,
80
+ TimePicker,
81
+ Toast,
82
+ ToastContainer,
83
+ Tooltip,
84
+ TreeNode,
85
+ TreeView,
86
+ VirtualList,
87
+ accordion,
88
+ accordion_item,
89
+ action_card,
90
+ avatar,
91
+ avatar_group,
92
+ badge,
93
+ breadcrumb,
94
+ breadcrumb_item,
95
+ button,
96
+ callout,
97
+ card,
98
+ chat_input,
99
+ chat_message,
100
+ chat_view,
101
+ checkbox,
102
+ code_block,
103
+ column,
104
+ container,
105
+ data_grid,
106
+ date_picker,
107
+ diagram,
108
+ diagram_edge,
109
+ diagram_node,
110
+ divider,
111
+ drawer,
112
+ dropdown_menu,
113
+ dropdown_menu_item,
114
+ dynamic_card,
115
+ empty_state,
116
+ error_boundary,
117
+ file_upload,
118
+ form,
119
+ form_from_model,
120
+ generative_slot,
121
+ grid,
122
+ heading,
123
+ input,
124
+ job_panel,
125
+ kbd,
126
+ link,
127
+ metric_card,
128
+ modal,
129
+ pagination,
130
+ plot,
131
+ popover,
132
+ progress_bar,
133
+ progress_circle,
134
+ radio_group,
135
+ row,
136
+ select,
137
+ skeleton,
138
+ slider,
139
+ spacer,
140
+ split_panel,
141
+ stack,
142
+ step_timeline,
143
+ streaming_text,
144
+ switch,
145
+ tab_item,
146
+ table,
147
+ tabs,
148
+ tag,
149
+ text,
150
+ textarea,
151
+ theme_toggle,
152
+ time_picker,
153
+ toast,
154
+ toast_container,
155
+ tooltip,
156
+ tree_node,
157
+ tree_view,
158
+ virtual_list,
159
+ )
160
+ from radui.core import (
161
+ App,
162
+ ApplicationIR,
163
+ CommandContext,
164
+ CommandSpec,
165
+ ComponentRegistry,
166
+ DuplicateComponentRegistrationError,
167
+ JobSpec,
168
+ UnregisteredComponentError,
169
+ notify,
170
+ )
171
+ from radui.core.auth import RBACPolicy, User
172
+ from radui.core.theme import Theme
173
+ from radui.testing import TestHarness
174
+ from radui.inspector import (
175
+ OTLPExporter,
176
+ TraceCategory,
177
+ TraceCollector,
178
+ TraceRecord,
179
+ extract_dag_structure,
180
+ generate_dag_svg,
181
+ )
182
+ from radui.ai import (
183
+ AGUIEvent,
184
+ AGUIEventJournal,
185
+ AGUIEventType,
186
+ ChatMessageRecord,
187
+ MCPToolDescriptor,
188
+ MCPToolRegistry,
189
+ SQLiteChatRepository,
190
+ StreamingFenceParser,
191
+ TokenStream,
192
+ a2ui_to_component,
193
+ )
194
+ from radui.state import (
195
+ BatchContext,
196
+ ComputedRef,
197
+ CyclicDependencyError,
198
+ InMemoryStateBackend,
199
+ Invalidation,
200
+ PostgreSQLStateBackend,
201
+ RedisStateBackend,
202
+ SQLiteStateBackend,
203
+ StateBackend,
204
+ StateBackendUnavailableError,
205
+ StateChange,
206
+ StateContext,
207
+ StateMiddleware,
208
+ StateRef,
209
+ StateRuntime,
210
+ StateScope,
211
+ TenantRequiredError,
212
+ TransactionContext,
213
+ UserRequiredError,
214
+ computed,
215
+ state,
216
+ )
217
+ from radui.transport import (
218
+ Event,
219
+ EventBackend,
220
+ EventBroker,
221
+ EventRecord,
222
+ InMemoryEventBackend,
223
+ PostgreSQLEventBackend,
224
+ RedisEventBackend,
225
+ SQLiteEventBackend,
226
+ )
227
+ from radui.core.idempotency import (
228
+ IdempotencyBackend,
229
+ IdempotencyBackendUnavailableError,
230
+ IdempotencyConflictError,
231
+ IdempotencyError,
232
+ IdempotencyRecord,
233
+ IdempotencyResult,
234
+ InMemoryIdempotencyBackend,
235
+ PostgreSQLIdempotencyBackend,
236
+ RedisIdempotencyBackend,
237
+ SQLiteIdempotencyBackend,
238
+ StaleLeaseError,
239
+ compute_payload_fingerprint,
240
+ )
241
+ from radui.core.outbox import (
242
+ OutboxRecord,
243
+ OutboxStatus,
244
+ StaleOutboxLeaseError,
245
+ TransactionalOutbox,
246
+ )
247
+ from radui.jobs import (
248
+ JobBackend,
249
+ JobCancelledError,
250
+ JobCapacityError,
251
+ JobContext,
252
+ JobDefinition,
253
+ JobError,
254
+ JobNotFoundError,
255
+ JobSnapshot,
256
+ JobStatus,
257
+ LocalJobBackend,
258
+ RedisJobBackend,
259
+ SQLiteJobBackend,
260
+ )
261
+ from radui.data import (
262
+ QueryCache,
263
+ QueryEntry,
264
+ aquery,
265
+ cached,
266
+ download_button,
267
+ invalidate_query,
268
+ paginate,
269
+ query,
270
+ )
271
+ from radui.i18n import (
272
+ Translator,
273
+ format_currency,
274
+ format_date,
275
+ format_number,
276
+ get_locale,
277
+ set_locale,
278
+ t,
279
+ )
280
+ from radui.plugins import Plugin, PluginManager
281
+ from radui.core.security import RateLimiter, RateLimitMiddleware
282
+ from radui.benchmarks import BenchmarkResult, BenchmarkSuite
283
+ from radui import motion
284
+
285
+ __version__ = "1.0.0"
286
+
287
+ __all__ = [
288
+ # Core
289
+ "App",
290
+ "ApplicationIR",
291
+ "ComponentRegistry",
292
+ "DuplicateComponentRegistrationError",
293
+ "UnregisteredComponentError",
294
+ "CommandContext",
295
+ "CommandSpec",
296
+ "JobSpec",
297
+ "notify",
298
+ "__version__",
299
+ "User",
300
+ "RBACPolicy",
301
+ "Theme",
302
+ "TestHarness",
303
+ "StateBackend",
304
+ "InMemoryStateBackend",
305
+ "SQLiteStateBackend",
306
+ "RedisStateBackend",
307
+ "PostgreSQLStateBackend",
308
+ "StateBackendUnavailableError",
309
+
310
+ # Motion & Animation
311
+ "motion",
312
+
313
+ # Data Engine
314
+ "query",
315
+ "aquery",
316
+ "invalidate_query",
317
+ "cached",
318
+ "paginate",
319
+ "download_button",
320
+ "QueryCache",
321
+ "QueryEntry",
322
+
323
+ # Components & Factories
324
+ "Accordion",
325
+ "AccordionItem",
326
+ "ActionCard",
327
+ "Avatar",
328
+ "AvatarGroup",
329
+ "Badge",
330
+ "Breadcrumb",
331
+ "BreadcrumbItem",
332
+ "Button",
333
+ "Callout",
334
+ "Card",
335
+ "ChatInput",
336
+ "ChatMessage",
337
+ "ChatView",
338
+ "Checkbox",
339
+ "CodeBlock",
340
+ "Column",
341
+ "Component",
342
+ "Container",
343
+ "DataGrid",
344
+ "DatePicker",
345
+ "Diagram",
346
+ "DiagramEdge",
347
+ "DiagramNode",
348
+ "Divider",
349
+ "Drawer",
350
+ "DropdownMenu",
351
+ "DropdownMenuItem",
352
+ "DynamicCard",
353
+ "EmptyState",
354
+ "ErrorBoundary",
355
+ "FileUpload",
356
+ "Form",
357
+ "GenerativeSlot",
358
+ "Grid",
359
+ "Heading",
360
+ "Input",
361
+ "JobPanel",
362
+ "Kbd",
363
+ "Link",
364
+ "MetricCard",
365
+ "Modal",
366
+ "Pagination",
367
+ "Plot",
368
+ "Popover",
369
+ "ProgressBar",
370
+ "ProgressCircle",
371
+ "RadioGroup",
372
+ "Row",
373
+ "Select",
374
+ "Skeleton",
375
+ "Slider",
376
+ "Spacer",
377
+ "SplitPanel",
378
+ "Stack",
379
+ "StepTimeline",
380
+ "StreamingText",
381
+ "Switch",
382
+ "TabItem",
383
+ "Table",
384
+ "Tabs",
385
+ "Tag",
386
+ "Text",
387
+ "Textarea",
388
+ "ThemeToggle",
389
+ "TimePicker",
390
+ "Toast",
391
+ "ToastContainer",
392
+ "Tooltip",
393
+ "TreeNode",
394
+ "TreeView",
395
+ "VirtualList",
396
+ "accordion",
397
+ "accordion_item",
398
+ "action_card",
399
+ "avatar",
400
+ "avatar_group",
401
+ "badge",
402
+ "breadcrumb",
403
+ "breadcrumb_item",
404
+ "button",
405
+ "callout",
406
+ "card",
407
+ "chat_input",
408
+ "chat_message",
409
+ "chat_view",
410
+ "checkbox",
411
+ "code_block",
412
+ "column",
413
+ "container",
414
+ "data_grid",
415
+ "date_picker",
416
+ "diagram",
417
+ "diagram_edge",
418
+ "diagram_node",
419
+ "divider",
420
+ "drawer",
421
+ "dropdown_menu",
422
+ "dropdown_menu_item",
423
+ "dynamic_card",
424
+ "empty_state",
425
+ "error_boundary",
426
+ "file_upload",
427
+ "form",
428
+ "form_from_model",
429
+ "generative_slot",
430
+ "grid",
431
+ "heading",
432
+ "input",
433
+ "job_panel",
434
+ "kbd",
435
+ "link",
436
+ "metric_card",
437
+ "modal",
438
+ "pagination",
439
+ "plot",
440
+ "popover",
441
+ "progress_bar",
442
+ "progress_circle",
443
+ "radio_group",
444
+ "row",
445
+ "select",
446
+ "skeleton",
447
+ "slider",
448
+ "spacer",
449
+ "split_panel",
450
+ "stack",
451
+ "step_timeline",
452
+ "streaming_text",
453
+ "switch",
454
+ "tab_item",
455
+ "tabs",
456
+ "table",
457
+ "tag",
458
+ "text",
459
+ "textarea",
460
+ "theme_toggle",
461
+ "time_picker",
462
+ "toast",
463
+ "toast_container",
464
+ "tooltip",
465
+ "tree_node",
466
+ "tree_view",
467
+ "virtual_list",
468
+
469
+ # AI & Agent protocols
470
+ "a2ui_to_component",
471
+ "AGUIEvent",
472
+ "AGUIEventJournal",
473
+ "AGUIEventType",
474
+ "ChatMessageRecord",
475
+ "MCPToolDescriptor",
476
+ "MCPToolRegistry",
477
+ "SQLiteChatRepository",
478
+ "StreamingFenceParser",
479
+ "TokenStream",
480
+
481
+ # Internationalization (i18n)
482
+ "Translator",
483
+ "t",
484
+ "set_locale",
485
+ "get_locale",
486
+ "format_number",
487
+ "format_currency",
488
+ "format_date",
489
+
490
+ # Plugins & Extensions
491
+ "Plugin",
492
+ "PluginManager",
493
+
494
+ # Security
495
+ "RateLimiter",
496
+ "RateLimitMiddleware",
497
+
498
+ # Inspector & Telemetry
499
+ "BenchmarkResult",
500
+ "BenchmarkSuite",
501
+ "OTLPExporter",
502
+ "TraceCategory",
503
+ "TraceCollector",
504
+ "TraceRecord",
505
+ "extract_dag_structure",
506
+ "generate_dag_svg",
507
+
508
+ # Events & Jobs
509
+ "Event",
510
+ "EventBackend",
511
+ "EventBroker",
512
+ "EventRecord",
513
+ "InMemoryEventBackend",
514
+ "SQLiteEventBackend",
515
+ "RedisEventBackend",
516
+ "PostgreSQLEventBackend",
517
+ "JobBackend",
518
+ "JobCancelledError",
519
+ "JobCapacityError",
520
+ "JobContext",
521
+ "JobDefinition",
522
+ "JobError",
523
+ "JobNotFoundError",
524
+ "JobSnapshot",
525
+ "JobStatus",
526
+ "LocalJobBackend",
527
+ "RedisJobBackend",
528
+ "SQLiteJobBackend",
529
+
530
+ # Idempotency Engine
531
+ "IdempotencyBackend",
532
+ "IdempotencyRecord",
533
+ "IdempotencyResult",
534
+ "IdempotencyError",
535
+ "IdempotencyConflictError",
536
+ "IdempotencyBackendUnavailableError",
537
+ "InMemoryIdempotencyBackend",
538
+ "SQLiteIdempotencyBackend",
539
+ "RedisIdempotencyBackend",
540
+ "PostgreSQLIdempotencyBackend",
541
+ "StaleLeaseError",
542
+ "compute_payload_fingerprint",
543
+
544
+ # Transactional Outbox
545
+ "OutboxRecord",
546
+ "OutboxStatus",
547
+ "StaleOutboxLeaseError",
548
+ "TransactionalOutbox",
549
+
550
+ # Reactive State
551
+ "BatchContext",
552
+ "ComputedRef",
553
+ "CyclicDependencyError",
554
+ "StateChange",
555
+ "StateContext",
556
+ "StateMiddleware",
557
+ "StateRef",
558
+ "StateRuntime",
559
+ "StateScope",
560
+ "TransactionContext",
561
+ "computed",
562
+ "state",
563
+ ]
genesis/__main__.py ADDED
@@ -0,0 +1,9 @@
1
+ """Main entry point for running RADUI as a module: python -m radui."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from radui.cli import main
7
+
8
+ if __name__ == "__main__":
9
+ sys.exit(main())
genesis/ai/__init__.py ADDED
@@ -0,0 +1,21 @@
1
+ """RADUI AI & Agent-Native subsystem: A2UI, AG-UI, MCP, and streaming parsers."""
2
+
3
+ from .a2ui import a2ui_to_component
4
+ from .ag_ui import AGUIEvent, AGUIEventJournal, AGUIEventType
5
+ from .mcp import MCPToolDescriptor, MCPToolRegistry
6
+ from .persistence import ChatMessageRecord, SQLiteChatRepository
7
+ from .streaming import StreamingFenceParser, TokenStream
8
+
9
+ __all__ = [
10
+ "a2ui_to_component",
11
+ "AGUIEvent",
12
+ "AGUIEventJournal",
13
+ "AGUIEventType",
14
+ "ChatMessageRecord",
15
+ "MCPToolDescriptor",
16
+ "MCPToolRegistry",
17
+ "SQLiteChatRepository",
18
+ "StreamingFenceParser",
19
+ "TokenStream",
20
+ ]
21
+
genesis/ai/a2ui.py ADDED
@@ -0,0 +1,82 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+ import radui.components.nodes as nodes
5
+
6
+
7
+ def a2ui_to_component(spec: dict[str, Any]) -> nodes.Component:
8
+ """
9
+ Maps an A2UI 1.0 component specification into a native RADUI Component node.
10
+ Enforces safe property mapping and type safety.
11
+ """
12
+ comp_type = spec.get("type", "").lower()
13
+ cid = spec.get("id")
14
+ props = spec.get("props", {})
15
+
16
+ if comp_type == "text":
17
+ return nodes.text(str(props.get("text", "")), id=cid)
18
+
19
+ if comp_type == "heading":
20
+ return nodes.heading(
21
+ str(props.get("text", "")),
22
+ level=int(props.get("level", 1)),
23
+ id=cid,
24
+ )
25
+
26
+ if comp_type in {"column", "vertical_layout"}:
27
+ children = [a2ui_to_component(c) for c in spec.get("children", []) if isinstance(c, dict)]
28
+ return nodes.column(*children, gap=int(props.get("gap", 16)), id=cid)
29
+
30
+ if comp_type in {"row", "horizontal_layout"}:
31
+ children = [a2ui_to_component(c) for c in spec.get("children", []) if isinstance(c, dict)]
32
+ return nodes.row(*children, gap=int(props.get("gap", 12)), id=cid)
33
+
34
+ if comp_type == "card":
35
+ children = [a2ui_to_component(c) for c in spec.get("children", []) if isinstance(c, dict)]
36
+ return nodes.card(*children, id=cid)
37
+
38
+ if comp_type == "badge":
39
+ return nodes.badge(
40
+ str(props.get("label", "")),
41
+ variant=props.get("variant", "neutral"),
42
+ id=cid,
43
+ )
44
+
45
+ if comp_type == "callout":
46
+ return nodes.callout(
47
+ str(props.get("message", "")),
48
+ title=str(props.get("title", "")),
49
+ variant=props.get("variant", "info"),
50
+ id=cid,
51
+ )
52
+
53
+ if comp_type in {"metric", "stat"}:
54
+ delta_val = props.get("delta") or props.get("change")
55
+ trend_val = props.get("delta_type") or props.get("trend", "neutral")
56
+ return nodes.metric_card(
57
+ title=str(props.get("title", "")),
58
+ value=str(props.get("value", "")),
59
+ delta=str(delta_val) if delta_val is not None else None,
60
+ delta_type=trend_val,
61
+ subtitle=str(props.get("subtitle", "")),
62
+ id=cid,
63
+ )
64
+
65
+ if comp_type == "button":
66
+ return nodes.button(str(props.get("label", "Click")), id=cid)
67
+
68
+ if comp_type == "table":
69
+ headers = props.get("headers") or props.get("columns") or []
70
+ return nodes.table(
71
+ headers=headers,
72
+ rows=props.get("rows", []),
73
+ id=cid,
74
+ )
75
+
76
+ # Fallback for dynamic/unmapped components: wrap in DynamicCard
77
+ return nodes.dynamic_card(
78
+ title=str(props.get("title", comp_type)),
79
+ block_type=comp_type,
80
+ payload=spec,
81
+ id=cid,
82
+ )