@hypen-space/web 0.4.36 → 0.4.38

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 (149) hide show
  1. package/README.md +7 -6
  2. package/dist/canvas/accessibility.js +174 -174
  3. package/dist/canvas/accessibility.js.map +1 -10
  4. package/dist/canvas/events.js +264 -360
  5. package/dist/canvas/events.js.map +1 -11
  6. package/dist/canvas/index.js +11 -47
  7. package/dist/canvas/index.js.map +1 -9
  8. package/dist/canvas/input.js +178 -289
  9. package/dist/canvas/input.js.map +1 -11
  10. package/dist/canvas/layout.js +349 -547
  11. package/dist/canvas/layout.js.map +1 -12
  12. package/dist/canvas/paint.js +1122 -1146
  13. package/dist/canvas/paint.js.map +1 -12
  14. package/dist/canvas/renderer.js +352 -2259
  15. package/dist/canvas/renderer.js.map +1 -17
  16. package/dist/canvas/text.js +129 -264
  17. package/dist/canvas/text.js.map +1 -11
  18. package/dist/canvas/types.js +7 -2
  19. package/dist/canvas/types.js.map +1 -9
  20. package/dist/canvas/utils.js +158 -161
  21. package/dist/canvas/utils.js.map +1 -10
  22. package/dist/dom/applicators/advanced-layout.js +105 -138
  23. package/dist/dom/applicators/advanced-layout.js.map +1 -10
  24. package/dist/dom/applicators/background.js +36 -76
  25. package/dist/dom/applicators/background.js.map +1 -10
  26. package/dist/dom/applicators/border.js +82 -106
  27. package/dist/dom/applicators/border.js.map +1 -10
  28. package/dist/dom/applicators/color.js +17 -58
  29. package/dist/dom/applicators/color.js.map +1 -10
  30. package/dist/dom/applicators/display.js +39 -79
  31. package/dist/dom/applicators/display.js.map +1 -10
  32. package/dist/dom/applicators/effects.js +121 -147
  33. package/dist/dom/applicators/effects.js.map +1 -10
  34. package/dist/dom/applicators/events.js +413 -450
  35. package/dist/dom/applicators/events.js.map +1 -11
  36. package/dist/dom/applicators/font.js +119 -140
  37. package/dist/dom/applicators/font.js.map +1 -10
  38. package/dist/dom/applicators/index.js +386 -1770
  39. package/dist/dom/applicators/index.js.map +1 -26
  40. package/dist/dom/applicators/layout.js +143 -149
  41. package/dist/dom/applicators/layout.js.map +1 -10
  42. package/dist/dom/applicators/margin.js +64 -105
  43. package/dist/dom/applicators/margin.js.map +1 -10
  44. package/dist/dom/applicators/padding.js +64 -105
  45. package/dist/dom/applicators/padding.js.map +1 -10
  46. package/dist/dom/applicators/size.js +169 -172
  47. package/dist/dom/applicators/size.js.map +1 -10
  48. package/dist/dom/applicators/transform.js +73 -114
  49. package/dist/dom/applicators/transform.js.map +1 -10
  50. package/dist/dom/applicators/transition.js +47 -88
  51. package/dist/dom/applicators/transition.js.map +1 -10
  52. package/dist/dom/applicators/types.js +7 -2
  53. package/dist/dom/applicators/types.js.map +1 -9
  54. package/dist/dom/applicators/typography.js +68 -109
  55. package/dist/dom/applicators/typography.js.map +1 -10
  56. package/dist/dom/canvas/index.js +49 -83
  57. package/dist/dom/canvas/index.js.map +1 -10
  58. package/dist/dom/components/app.js +16 -63
  59. package/dist/dom/components/app.js.map +1 -10
  60. package/dist/dom/components/audio.js +31 -76
  61. package/dist/dom/components/audio.js.map +1 -10
  62. package/dist/dom/components/avatar.js +41 -87
  63. package/dist/dom/components/avatar.js.map +1 -10
  64. package/dist/dom/components/badge.js +35 -83
  65. package/dist/dom/components/badge.js.map +1 -10
  66. package/dist/dom/components/button.js +26 -69
  67. package/dist/dom/components/button.js.map +1 -10
  68. package/dist/dom/components/card.js +13 -63
  69. package/dist/dom/components/card.js.map +1 -10
  70. package/dist/dom/components/center.js +21 -65
  71. package/dist/dom/components/center.js.map +1 -10
  72. package/dist/dom/components/checkbox.js +37 -82
  73. package/dist/dom/components/checkbox.js.map +1 -10
  74. package/dist/dom/components/column.js +16 -62
  75. package/dist/dom/components/column.js.map +1 -10
  76. package/dist/dom/components/container.js +11 -59
  77. package/dist/dom/components/container.js.map +1 -10
  78. package/dist/dom/components/divider.js +27 -73
  79. package/dist/dom/components/divider.js.map +1 -10
  80. package/dist/dom/components/grid.js +41 -80
  81. package/dist/dom/components/grid.js.map +1 -10
  82. package/dist/dom/components/heading.js +28 -74
  83. package/dist/dom/components/heading.js.map +1 -10
  84. package/dist/dom/components/hypenapp.js +291 -288
  85. package/dist/dom/components/hypenapp.js.map +1 -10
  86. package/dist/dom/components/image.js +18 -67
  87. package/dist/dom/components/image.js.map +1 -10
  88. package/dist/dom/components/index.js +109 -1427
  89. package/dist/dom/components/index.js.map +1 -42
  90. package/dist/dom/components/input.js +19 -69
  91. package/dist/dom/components/input.js.map +1 -10
  92. package/dist/dom/components/link.js +23 -70
  93. package/dist/dom/components/link.js.map +1 -10
  94. package/dist/dom/components/list.js +26 -72
  95. package/dist/dom/components/list.js.map +1 -10
  96. package/dist/dom/components/paragraph.js +14 -63
  97. package/dist/dom/components/paragraph.js.map +1 -10
  98. package/dist/dom/components/progressbar.js +38 -85
  99. package/dist/dom/components/progressbar.js.map +1 -10
  100. package/dist/dom/components/route.js +27 -73
  101. package/dist/dom/components/route.js.map +1 -10
  102. package/dist/dom/components/router.js +15 -62
  103. package/dist/dom/components/router.js.map +1 -10
  104. package/dist/dom/components/row.js +25 -70
  105. package/dist/dom/components/row.js.map +1 -10
  106. package/dist/dom/components/select.js +42 -85
  107. package/dist/dom/components/select.js.map +1 -10
  108. package/dist/dom/components/slider.js +31 -76
  109. package/dist/dom/components/slider.js.map +1 -10
  110. package/dist/dom/components/spacer.js +10 -60
  111. package/dist/dom/components/spacer.js.map +1 -10
  112. package/dist/dom/components/spinner.js +41 -88
  113. package/dist/dom/components/spinner.js.map +1 -10
  114. package/dist/dom/components/stack.js +19 -67
  115. package/dist/dom/components/stack.js.map +1 -10
  116. package/dist/dom/components/switch.js +50 -93
  117. package/dist/dom/components/switch.js.map +1 -10
  118. package/dist/dom/components/text.js +24 -69
  119. package/dist/dom/components/text.js.map +1 -10
  120. package/dist/dom/components/textarea.js +35 -79
  121. package/dist/dom/components/textarea.js.map +1 -10
  122. package/dist/dom/components/video.js +35 -79
  123. package/dist/dom/components/video.js.map +1 -10
  124. package/dist/dom/debug.js +159 -160
  125. package/dist/dom/debug.js.map +1 -10
  126. package/dist/dom/element-data.js +181 -143
  127. package/dist/dom/element-data.js.map +1 -10
  128. package/dist/dom/events.js +133 -142
  129. package/dist/dom/events.js.map +1 -10
  130. package/dist/dom/index.js +11 -48
  131. package/dist/dom/index.js.map +1 -9
  132. package/dist/dom/renderer.js +311 -3596
  133. package/dist/dom/renderer.js.map +1 -62
  134. package/dist/index.d.ts +2 -2
  135. package/dist/index.js +36 -53
  136. package/dist/index.js.map +1 -9
  137. package/package.json +2 -16
  138. package/src/canvas/QUICKSTART.md +4 -3
  139. package/src/canvas/README.md +3 -2
  140. package/src/dom/README.md +4 -3
  141. package/src/index.ts +2 -2
  142. package/dist/client.d.ts +0 -19
  143. package/dist/client.js +0 -45
  144. package/dist/client.js.map +0 -9
  145. package/dist/hypen.d.ts +0 -140
  146. package/dist/hypen.js +0 -3909
  147. package/dist/hypen.js.map +0 -63
  148. package/src/client.ts +0 -20
  149. package/src/hypen.ts +0 -556
package/src/client.ts DELETED
@@ -1,20 +0,0 @@
1
- /**
2
- * @hypen-space/web/client - Client-side rendering with WASM engine
3
- *
4
- * This entry point includes the Hypen orchestrator which connects the
5
- * BrowserEngine (WASM) with the DOM renderer. Use this when you want
6
- * to run the engine client-side in the browser.
7
- *
8
- * For remote UI (server-side engine, no WASM in browser), use the
9
- * default import from "@hypen-space/web" instead.
10
- *
11
- * @example
12
- * ```typescript
13
- * import { render } from "@hypen-space/web/client";
14
- *
15
- * await render("Counter", "#app");
16
- * ```
17
- */
18
-
19
- export { Hypen, render, renderWithComponents } from "./hypen.js";
20
- export type { HypenConfig } from "./hypen.js";
package/src/hypen.ts DELETED
@@ -1,556 +0,0 @@
1
- /**
2
- * Hypen - High-Level API for Web Applications
3
- *
4
- * Simple API for rendering Hypen applications (like ReactDOM.render)
5
- */
6
-
7
- import { Engine } from "@hypen-space/core/engine/browser";
8
- import { HypenModuleInstance } from "@hypen-space/core/app";
9
- import type { HypenModuleDefinition } from "@hypen-space/core/app";
10
- import { HypenRouter } from "@hypen-space/core/router";
11
- import { HypenGlobalContext } from "@hypen-space/core/context";
12
- import { componentLoader } from "@hypen-space/core/loader";
13
- import { Router, Route, Link } from "@hypen-space/core/components";
14
- import { frameworkLoggers, setDebugMode } from "@hypen-space/core/logger";
15
- import { DOMRenderer } from "./dom/renderer.js";
16
- import type { DebugConfig } from "./dom/debug.js";
17
-
18
- const log = frameworkLoggers.hypen;
19
-
20
- export interface HypenConfig {
21
- /** Base directory for components (default: "./src/components") */
22
- componentsDir?: string;
23
- /** Enable debug logging */
24
- debug?: boolean;
25
- /** Custom WASM URL */
26
- wasmUrl?: string;
27
- /** Custom WASM JS glue URL */
28
- jsUrl?: string;
29
- /** Enable re-render heatmap debugging */
30
- debugHeatmap?: boolean;
31
- /** Heatmap increment per re-render (default: 5%) */
32
- heatmapIncrement?: number;
33
- /** Heatmap fade out duration in ms (default: 2000) */
34
- heatmapFadeOut?: number;
35
- }
36
-
37
- export class Hypen {
38
- private engine: Engine | null = null;
39
- private renderer: DOMRenderer | null = null;
40
- private moduleInstance: HypenModuleInstance<any> | null = null;
41
- private container: HTMLElement | null = null;
42
- private config: HypenConfig;
43
- private router: HypenRouter;
44
- private globalContext: HypenGlobalContext;
45
- private moduleInstances = new Map<string, HypenModuleInstance<any>>();
46
-
47
- constructor(config: HypenConfig = {}) {
48
- this.config = {
49
- componentsDir: "./src/components",
50
- debug: false,
51
- ...config,
52
- };
53
-
54
- // Enable debug mode if configured
55
- if (this.config.debug) {
56
- setDebugMode(true);
57
- }
58
-
59
- // Initialize router and global context
60
- this.router = new HypenRouter();
61
- this.globalContext = new HypenGlobalContext();
62
-
63
- // Register built-in components
64
- componentLoader.register("Router", Router, "");
65
- componentLoader.register("Route", Route, "");
66
- componentLoader.register("Link", Link, "");
67
-
68
- // Store hypen engine instance in global context for built-in component access
69
- (this.globalContext as any).__hypenEngine = this;
70
- }
71
-
72
- /**
73
- * Initialize the Hypen runtime
74
- * Must be called before render()
75
- */
76
- async init(): Promise<void> {
77
- log.debug("Initializing...");
78
-
79
- // Initialize engine
80
- this.engine = new Engine();
81
- await this.engine.init({
82
- wasmUrl: this.config.wasmUrl,
83
- jsUrl: this.config.jsUrl,
84
- });
85
-
86
- log.debug("Engine initialized");
87
- }
88
-
89
- /**
90
- * Load all components from the components directory
91
- */
92
- async loadComponents(componentsDir?: string): Promise<void> {
93
- const dir = componentsDir || this.config.componentsDir!;
94
-
95
- log.debug(`Loading components from ${dir}...`);
96
-
97
- await componentLoader.loadFromComponentsDir(dir);
98
-
99
- log.debug(`Loaded ${componentLoader.getNames().length} components`);
100
- }
101
-
102
- /**
103
- * Render a component to a DOM container
104
- *
105
- * @param componentName - Name of the component to render (e.g., "HomePage")
106
- * @param containerSelector - CSS selector or HTMLElement for the mount point
107
- */
108
- async render(
109
- componentName: string,
110
- containerSelector: string | HTMLElement
111
- ): Promise<void> {
112
- if (!this.engine) {
113
- throw new Error("[Hypen] Engine not initialized. Call init() first.");
114
- }
115
-
116
- // Get the container element
117
- if (typeof containerSelector === "string") {
118
- const element = document.querySelector(containerSelector);
119
- if (!element) {
120
- throw new Error(`[Hypen] Container not found: ${containerSelector}`);
121
- }
122
- this.container = element as HTMLElement;
123
- } else {
124
- this.container = containerSelector;
125
- }
126
-
127
- // Get the component definition
128
- const component = componentLoader.get(componentName);
129
- if (!component) {
130
- throw new Error(
131
- `[Hypen] Component "${componentName}" not found. Available: ${componentLoader.getNames().join(", ")}`
132
- );
133
- }
134
-
135
- log.debug(`Rendering ${componentName} to`, this.container);
136
-
137
- // Create renderer with debug config
138
- this.renderer = new DOMRenderer(this.container, this.engine, {
139
- enabled: this.config.debugHeatmap || false,
140
- showHeatmap: this.config.debugHeatmap || false,
141
- heatmapIncrement: this.config.heatmapIncrement || 5,
142
- fadeOutDuration: this.config.heatmapFadeOut || 2000,
143
- maxOpacity: 0.8,
144
- });
145
-
146
- // Set render callback
147
- this.engine.setRenderCallback((patches) => {
148
- log.debug(`Applying ${patches.length} patches`);
149
- this.renderer!.applyPatches(patches);
150
- });
151
-
152
- // Set context on renderer for component composition
153
- this.renderer.setContext(this.router, this.globalContext);
154
-
155
- // Extract module ID from component name or .id() applicator
156
- const moduleId = this.extractModuleId(componentName, component.template);
157
-
158
- // Create module instance with router and global context
159
- this.moduleInstance = new HypenModuleInstance(
160
- this.engine,
161
- component.module,
162
- this.router,
163
- this.globalContext
164
- );
165
-
166
- // Register module in global context
167
- this.globalContext.registerModule(moduleId, this.moduleInstance);
168
- this.moduleInstances.set(moduleId, this.moduleInstance);
169
-
170
- // Connect module state changes to renderer
171
- this.moduleInstance.onStateChange(() => {
172
- const mergedState = this.getMergedState();
173
- log.debug(`State changed, merged state:`, mergedState);
174
- this.renderer!.updateState(mergedState);
175
- });
176
-
177
- // Set up component resolver for dynamic component composition
178
- this.setupComponentResolver();
179
-
180
- // Create module instances for ALL components that have state
181
- this.createNestedModuleInstances();
182
-
183
- // Render the UI template
184
- this.engine.renderSource(component.template);
185
-
186
- // Update renderer with initial state
187
- this.renderer!.updateState(this.getMergedState());
188
-
189
- log.debug(`${componentName} rendered successfully`);
190
- }
191
-
192
- /**
193
- * Unmount and cleanup
194
- */
195
- async unmount(): Promise<void> {
196
- log.debug("Unmounting...");
197
-
198
- if (this.moduleInstance) {
199
- await this.moduleInstance.destroy();
200
- this.moduleInstance = null;
201
- }
202
-
203
- if (this.container) {
204
- this.container.innerHTML = "";
205
- this.container = null;
206
- }
207
-
208
- this.renderer = null;
209
-
210
- log.debug("Unmounted");
211
- }
212
-
213
- /**
214
- * Get the current module instance
215
- */
216
- getModuleInstance(): HypenModuleInstance<any> | null {
217
- return this.moduleInstance;
218
- }
219
-
220
- /**
221
- * Get the current state
222
- */
223
- getState(): any {
224
- return this.moduleInstance?.getState() ?? null;
225
- }
226
-
227
- /**
228
- * Get the router instance
229
- */
230
- getRouter(): HypenRouter {
231
- return this.router;
232
- }
233
-
234
- /**
235
- * Get the global context
236
- */
237
- getGlobalContext(): HypenGlobalContext {
238
- return this.globalContext;
239
- }
240
-
241
- /**
242
- * Enable or disable debug heatmap mode
243
- */
244
- setDebugHeatmap(enabled: boolean): void {
245
- if (this.renderer) {
246
- this.renderer.setDebugConfig({ enabled, showHeatmap: enabled });
247
- log.debug(`Debug heatmap ${enabled ? "enabled" : "disabled"}`);
248
- }
249
- }
250
-
251
- /**
252
- * Reset debug tracking for all elements
253
- */
254
- resetDebugTracking(): void {
255
- if (this.renderer) {
256
- this.renderer.resetDebugTracking();
257
- log.debug("Debug tracking reset");
258
- }
259
- }
260
-
261
- /**
262
- * Get debug statistics
263
- */
264
- getDebugStats(): {
265
- totalRerenders: number;
266
- elementCount: number;
267
- avgRerenders: number;
268
- } | null {
269
- return this.renderer?.getDebugStats() || null;
270
- }
271
-
272
- /**
273
- * Render a lazy route component into a specific route element
274
- * This is called by the Router when a route becomes active
275
- */
276
- async renderLazyRoute(
277
- routePath: string,
278
- componentName: string,
279
- routeElement: HTMLElement
280
- ): Promise<void> {
281
- if (!this.engine || !this.renderer) {
282
- throw new Error("Engine not initialized");
283
- }
284
-
285
- // Get the component from the loader
286
- const component = componentLoader.get(componentName);
287
- if (!component) {
288
- throw new Error(`Component ${componentName} not found`);
289
- }
290
-
291
- // Get the component template
292
- const template = component.template;
293
- if (!template) {
294
- throw new Error(`Component ${componentName} has no template`);
295
- }
296
-
297
- // Create module instance for this component if it has state
298
- if (component.module && !this.moduleInstances.has(componentName)) {
299
- const moduleId = this.extractModuleId(componentName, template);
300
-
301
- const moduleInstance = new HypenModuleInstance(
302
- this.engine,
303
- component.module,
304
- this.router,
305
- this.globalContext
306
- );
307
-
308
- this.globalContext.registerModule(moduleId, moduleInstance);
309
- this.moduleInstances.set(componentName, moduleInstance);
310
-
311
- // Listen to state changes
312
- moduleInstance.onStateChange(() => {
313
- const mergedState = this.getMergedState();
314
- log.debug(`Lazy component ${componentName} state changed:`, mergedState);
315
- this.renderer!.updateState(mergedState);
316
- });
317
- }
318
-
319
- // Wait a tick for module instance to initialize and fetch data
320
- await new Promise((resolve) => setTimeout(resolve, 50));
321
-
322
- // Get the route element's node ID from the renderer
323
- const routeNodeId = routeElement.dataset.hypenId;
324
- if (!routeNodeId) {
325
- throw new Error(`Route element is missing data-hypen-id attribute`);
326
- }
327
-
328
- // Get the current merged state
329
- const mergedState = this.getMergedState();
330
-
331
- // Render into the Route element
332
- this.engine.renderInto(template, routeNodeId, mergedState);
333
-
334
- // Ensure freshly created text nodes are interpolated with current state
335
- this.renderer!.updateState(mergedState);
336
- }
337
-
338
- /**
339
- * Extract module ID from component name or .id() applicator
340
- */
341
- private extractModuleId(componentName: string, template: string): string {
342
- // Look for .id("CustomName") or .id('CustomName') in the template
343
- const idMatch = template.match(/\.id\(["']([^"']+)["']\)/);
344
- const matchedId = idMatch?.[1];
345
- if (matchedId) {
346
- return matchedId;
347
- }
348
-
349
- // Default to component name
350
- return componentName;
351
- }
352
-
353
- /**
354
- * Create module instances for all components that have state
355
- */
356
- private createNestedModuleInstances(): void {
357
- // Create built-in Router module instance
358
- if (Router && !this.moduleInstances.has("Router")) {
359
- const routerInstance = new HypenModuleInstance(
360
- this.engine!,
361
- Router,
362
- this.router,
363
- this.globalContext
364
- );
365
- this.globalContext.registerModule("Router", routerInstance);
366
- this.moduleInstances.set("Router", routerInstance);
367
- routerInstance.onStateChange(() => {
368
- const mergedState = this.getMergedState();
369
- log.debug("Router state changed:", mergedState);
370
- this.renderer!.updateState(mergedState);
371
- });
372
- }
373
-
374
- // Get all registered components
375
- const componentNames = componentLoader.getNames();
376
-
377
- for (const name of componentNames) {
378
- // Skip if already created
379
- if (this.moduleInstances.has(name)) {
380
- continue;
381
- }
382
-
383
- const comp = componentLoader.get(name);
384
- if (!comp || !comp.module) {
385
- continue;
386
- }
387
-
388
- // Create module instance
389
- log.debug(`Creating nested module instance for: ${name}`);
390
-
391
- const moduleInstance = new HypenModuleInstance(
392
- this.engine!,
393
- comp.module,
394
- this.router,
395
- this.globalContext
396
- );
397
-
398
- this.globalContext.registerModule(name, moduleInstance);
399
- this.moduleInstances.set(name, moduleInstance);
400
-
401
- // Connect state changes to renderer
402
- moduleInstance.onStateChange(() => {
403
- const mergedState = this.getMergedState();
404
- log.debug(`Nested component ${name} state changed:`, mergedState);
405
- this.renderer!.updateState(mergedState);
406
- });
407
- }
408
- }
409
-
410
- /**
411
- * Get merged state from all module instances
412
- */
413
- private getMergedState(): Record<string, any> {
414
- const merged: Record<string, any> = {};
415
-
416
- // Include main module state
417
- if (this.moduleInstance) {
418
- const mainState = this.moduleInstance.getState();
419
- Object.assign(merged, mainState);
420
- }
421
-
422
- // Include all nested component states
423
- for (const [name, instance] of this.moduleInstances.entries()) {
424
- const nestedState = instance.getState();
425
- Object.assign(merged, nestedState);
426
- }
427
-
428
- return merged;
429
- }
430
-
431
- /**
432
- * Set up component resolver for the engine
433
- */
434
- private setupComponentResolver(): void {
435
- if (!this.engine) return;
436
-
437
- // List of built-in DOM elements that should NOT be resolved
438
- const builtInElements = new Set([
439
- "Column",
440
- "Row",
441
- "Text",
442
- "Button",
443
- "Image",
444
- "Input",
445
- "Container",
446
- "Box",
447
- "Center",
448
- "List",
449
- "Canvas",
450
- "Spacer",
451
- "Divider",
452
- "ScrollView",
453
- ]);
454
-
455
- this.engine.setComponentResolver(
456
- (componentName: string, contextPath: string | null) => {
457
- // Don't try to resolve built-in DOM elements
458
- if (builtInElements.has(componentName)) {
459
- return null;
460
- }
461
-
462
- log.debug(`Resolving component: ${componentName} (context: ${contextPath})`);
463
-
464
- // Check if component is registered
465
- const componentDef = componentLoader.get(componentName);
466
- if (!componentDef) {
467
- log.debug(`Component not found: ${componentName}`);
468
- return null;
469
- }
470
-
471
- // Router is passthrough, Route is lazy
472
- const isPassthrough = componentName === "Router";
473
- const isLazy = componentName === "Route";
474
-
475
- const resolved = {
476
- source: componentDef.template,
477
- path: componentDef.path || componentName,
478
- passthrough: isPassthrough,
479
- lazy: isLazy,
480
- };
481
-
482
- const flags = [];
483
- if (isPassthrough) flags.push("passthrough");
484
- if (isLazy) flags.push("lazy");
485
- const flagStr = flags.length > 0 ? ` (${flags.join(", ")})` : "";
486
- log.debug(`Resolved ${componentName} -> ${resolved.path}${flagStr}`);
487
-
488
- return resolved;
489
- }
490
- );
491
- }
492
- }
493
-
494
- /**
495
- * Quick render function (like ReactDOM.render)
496
- *
497
- * @example
498
- * ```typescript
499
- * import { render } from "@hypen-space/web/client";
500
- *
501
- * await render("HomePage", "#app");
502
- * ```
503
- */
504
- export async function render(
505
- componentName: string,
506
- containerSelector: string | HTMLElement,
507
- config?: HypenConfig
508
- ): Promise<Hypen> {
509
- const hypen = new Hypen(config);
510
- await hypen.init();
511
- await hypen.loadComponents();
512
- await hypen.render(componentName, containerSelector);
513
- return hypen;
514
- }
515
-
516
- /**
517
- * Render with explicit component loading
518
- *
519
- * @example
520
- * ```typescript
521
- * import { renderWithComponents } from "@hypen-space/web/client";
522
- * import HomePage from "./components/HomePage/component";
523
- * import homePageTemplate from "./components/HomePage/component.hypen";
524
- *
525
- * await renderWithComponents(
526
- * { HomePage: { module: HomePage, template: homePageTemplate } },
527
- * "HomePage",
528
- * "#app"
529
- * );
530
- * ```
531
- */
532
- export async function renderWithComponents(
533
- components: Record<string, { module: HypenModuleDefinition; template: string }>,
534
- componentName: string,
535
- containerSelector: string | HTMLElement,
536
- config?: HypenConfig
537
- ): Promise<Hypen> {
538
- const hypen = new Hypen(config);
539
- await hypen.init();
540
-
541
- // Register components manually
542
- for (const [name, { module, template }] of Object.entries(components)) {
543
- let processedTemplate = template;
544
-
545
- // If template starts with "module ComponentName {", extract just the children
546
- const moduleMatch = template.match(/^\s*module\s+\w+\s*\{([\s\S]*)\}\s*$/);
547
- if (moduleMatch && moduleMatch[1]) {
548
- processedTemplate = moduleMatch[1].trim();
549
- }
550
-
551
- componentLoader.register(name, module, processedTemplate);
552
- }
553
-
554
- await hypen.render(componentName, containerSelector);
555
- return hypen;
556
- }