@hypen-space/web 0.2.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.
- package/dist/chunk-2s02mkzs.js +32 -0
- package/dist/chunk-2s02mkzs.js.map +9 -0
- package/dist/src/canvas/accessibility.js +152 -0
- package/dist/src/canvas/accessibility.js.map +10 -0
- package/dist/src/canvas/events.js +198 -0
- package/dist/src/canvas/events.js.map +10 -0
- package/dist/src/canvas/index.js +28 -0
- package/dist/src/canvas/index.js.map +9 -0
- package/dist/src/canvas/input.js +132 -0
- package/dist/src/canvas/input.js.map +10 -0
- package/dist/src/canvas/layout.js +309 -0
- package/dist/src/canvas/layout.js.map +10 -0
- package/dist/src/canvas/paint.js +878 -0
- package/dist/src/canvas/paint.js.map +10 -0
- package/dist/src/canvas/renderer.js +276 -0
- package/dist/src/canvas/renderer.js.map +10 -0
- package/dist/src/canvas/text.js +118 -0
- package/dist/src/canvas/text.js.map +10 -0
- package/dist/src/canvas/types.js +2 -0
- package/dist/src/canvas/types.js.map +9 -0
- package/dist/src/canvas/utils.js +139 -0
- package/dist/src/canvas/utils.js.map +10 -0
- package/dist/src/dom/applicators/advanced-layout.js +111 -0
- package/dist/src/dom/applicators/advanced-layout.js.map +10 -0
- package/dist/src/dom/applicators/background.js +54 -0
- package/dist/src/dom/applicators/background.js.map +10 -0
- package/dist/src/dom/applicators/border.js +33 -0
- package/dist/src/dom/applicators/border.js.map +10 -0
- package/dist/src/dom/applicators/color.js +36 -0
- package/dist/src/dom/applicators/color.js.map +10 -0
- package/dist/src/dom/applicators/display.js +57 -0
- package/dist/src/dom/applicators/display.js.map +10 -0
- package/dist/src/dom/applicators/effects.js +89 -0
- package/dist/src/dom/applicators/effects.js.map +10 -0
- package/dist/src/dom/applicators/events.js +518 -0
- package/dist/src/dom/applicators/events.js.map +10 -0
- package/dist/src/dom/applicators/font.js +39 -0
- package/dist/src/dom/applicators/font.js.map +10 -0
- package/dist/src/dom/applicators/index.js +296 -0
- package/dist/src/dom/applicators/index.js.map +10 -0
- package/dist/src/dom/applicators/layout.js +86 -0
- package/dist/src/dom/applicators/layout.js.map +10 -0
- package/dist/src/dom/applicators/margin.js +32 -0
- package/dist/src/dom/applicators/margin.js.map +10 -0
- package/dist/src/dom/applicators/padding.js +35 -0
- package/dist/src/dom/applicators/padding.js.map +10 -0
- package/dist/src/dom/applicators/size.js +42 -0
- package/dist/src/dom/applicators/size.js.map +10 -0
- package/dist/src/dom/applicators/transform.js +92 -0
- package/dist/src/dom/applicators/transform.js.map +10 -0
- package/dist/src/dom/applicators/transition.js +66 -0
- package/dist/src/dom/applicators/transition.js.map +10 -0
- package/dist/src/dom/applicators/typography.js +87 -0
- package/dist/src/dom/applicators/typography.js.map +10 -0
- package/dist/src/dom/canvas/index.js +50 -0
- package/dist/src/dom/canvas/index.js.map +10 -0
- package/dist/src/dom/components/audio.js +48 -0
- package/dist/src/dom/components/audio.js.map +10 -0
- package/dist/src/dom/components/avatar.js +58 -0
- package/dist/src/dom/components/avatar.js.map +10 -0
- package/dist/src/dom/components/badge.js +55 -0
- package/dist/src/dom/components/badge.js.map +10 -0
- package/dist/src/dom/components/button.js +29 -0
- package/dist/src/dom/components/button.js.map +10 -0
- package/dist/src/dom/components/card.js +33 -0
- package/dist/src/dom/components/card.js.map +10 -0
- package/dist/src/dom/components/center.js +32 -0
- package/dist/src/dom/components/center.js.map +10 -0
- package/dist/src/dom/components/checkbox.js +54 -0
- package/dist/src/dom/components/checkbox.js.map +10 -0
- package/dist/src/dom/components/column.js +31 -0
- package/dist/src/dom/components/column.js.map +10 -0
- package/dist/src/dom/components/container.js +29 -0
- package/dist/src/dom/components/container.js.map +10 -0
- package/dist/src/dom/components/divider.js +45 -0
- package/dist/src/dom/components/divider.js.map +10 -0
- package/dist/src/dom/components/grid.js +44 -0
- package/dist/src/dom/components/grid.js.map +10 -0
- package/dist/src/dom/components/heading.js +47 -0
- package/dist/src/dom/components/heading.js.map +10 -0
- package/dist/src/dom/components/image.js +39 -0
- package/dist/src/dom/components/image.js.map +10 -0
- package/dist/src/dom/components/index.js +217 -0
- package/dist/src/dom/components/index.js.map +10 -0
- package/dist/src/dom/components/input.js +41 -0
- package/dist/src/dom/components/input.js.map +10 -0
- package/dist/src/dom/components/link.js +42 -0
- package/dist/src/dom/components/link.js.map +10 -0
- package/dist/src/dom/components/list.js +42 -0
- package/dist/src/dom/components/list.js.map +10 -0
- package/dist/src/dom/components/paragraph.js +35 -0
- package/dist/src/dom/components/paragraph.js.map +10 -0
- package/dist/src/dom/components/progressbar.js +57 -0
- package/dist/src/dom/components/progressbar.js.map +10 -0
- package/dist/src/dom/components/route.js +44 -0
- package/dist/src/dom/components/route.js.map +10 -0
- package/dist/src/dom/components/router.js +33 -0
- package/dist/src/dom/components/router.js.map +10 -0
- package/dist/src/dom/components/row.js +31 -0
- package/dist/src/dom/components/row.js.map +10 -0
- package/dist/src/dom/components/select.js +57 -0
- package/dist/src/dom/components/select.js.map +10 -0
- package/dist/src/dom/components/slider.js +48 -0
- package/dist/src/dom/components/slider.js.map +10 -0
- package/dist/src/dom/components/spacer.js +30 -0
- package/dist/src/dom/components/spacer.js.map +10 -0
- package/dist/src/dom/components/spinner.js +65 -0
- package/dist/src/dom/components/spinner.js.map +10 -0
- package/dist/src/dom/components/stack.js +45 -0
- package/dist/src/dom/components/stack.js.map +10 -0
- package/dist/src/dom/components/switch.js +83 -0
- package/dist/src/dom/components/switch.js.map +10 -0
- package/dist/src/dom/components/text.js +37 -0
- package/dist/src/dom/components/text.js.map +10 -0
- package/dist/src/dom/components/textarea.js +51 -0
- package/dist/src/dom/components/textarea.js.map +10 -0
- package/dist/src/dom/components/video.js +51 -0
- package/dist/src/dom/components/video.js.map +10 -0
- package/dist/src/dom/debug.js +170 -0
- package/dist/src/dom/debug.js.map +10 -0
- package/dist/src/dom/events.js +112 -0
- package/dist/src/dom/events.js.map +10 -0
- package/dist/src/dom/index.js +73 -0
- package/dist/src/dom/index.js.map +9 -0
- package/dist/src/dom/renderer.js +277 -0
- package/dist/src/dom/renderer.js.map +10 -0
- package/dist/src/index.js +89 -0
- package/dist/src/index.js.map +9 -0
- package/package.json +84 -0
- package/src/canvas/QUICKSTART.md +421 -0
- package/src/canvas/README.md +376 -0
- package/src/canvas/accessibility.ts +218 -0
- package/src/canvas/events.ts +307 -0
- package/src/canvas/index.ts +35 -0
- package/src/canvas/input.ts +210 -0
- package/src/canvas/layout.ts +401 -0
- package/src/canvas/paint.ts +1321 -0
- package/src/canvas/renderer.ts +422 -0
- package/src/canvas/text.ts +182 -0
- package/src/canvas/types.ts +137 -0
- package/src/canvas/utils.ts +218 -0
- package/src/dom/README.md +265 -0
- package/src/dom/applicators/advanced-layout.ts +128 -0
- package/src/dom/applicators/background.ts +50 -0
- package/src/dom/applicators/border.ts +19 -0
- package/src/dom/applicators/color.ts +23 -0
- package/src/dom/applicators/display.ts +54 -0
- package/src/dom/applicators/effects.ts +97 -0
- package/src/dom/applicators/events.ts +689 -0
- package/src/dom/applicators/font.ts +27 -0
- package/src/dom/applicators/index.ts +354 -0
- package/src/dom/applicators/layout.ts +92 -0
- package/src/dom/applicators/margin.ts +18 -0
- package/src/dom/applicators/padding.ts +18 -0
- package/src/dom/applicators/size.ts +31 -0
- package/src/dom/applicators/transform.ts +93 -0
- package/src/dom/applicators/transition.ts +65 -0
- package/src/dom/applicators/typography.ts +91 -0
- package/src/dom/canvas/index.ts +60 -0
- package/src/dom/components/audio.ts +45 -0
- package/src/dom/components/avatar.ts +49 -0
- package/src/dom/components/badge.ts +45 -0
- package/src/dom/components/button.ts +13 -0
- package/src/dom/components/card.ts +19 -0
- package/src/dom/components/center.ts +16 -0
- package/src/dom/components/checkbox.ts +54 -0
- package/src/dom/components/column.ts +15 -0
- package/src/dom/components/container.ts +13 -0
- package/src/dom/components/divider.ts +37 -0
- package/src/dom/components/grid.ts +40 -0
- package/src/dom/components/heading.ts +41 -0
- package/src/dom/components/image.ts +27 -0
- package/src/dom/components/index.ts +115 -0
- package/src/dom/components/input.ts +29 -0
- package/src/dom/components/link.ts +35 -0
- package/src/dom/components/list.ts +30 -0
- package/src/dom/components/paragraph.ts +23 -0
- package/src/dom/components/progressbar.ts +51 -0
- package/src/dom/components/route.ts +37 -0
- package/src/dom/components/router.ts +22 -0
- package/src/dom/components/row.ts +15 -0
- package/src/dom/components/select.ts +56 -0
- package/src/dom/components/slider.ts +45 -0
- package/src/dom/components/spacer.ts +16 -0
- package/src/dom/components/spinner.ts +60 -0
- package/src/dom/components/stack.ts +34 -0
- package/src/dom/components/switch.ts +86 -0
- package/src/dom/components/text.ts +24 -0
- package/src/dom/components/textarea.ts +50 -0
- package/src/dom/components/video.ts +50 -0
- package/src/dom/debug.ts +247 -0
- package/src/dom/events.ts +168 -0
- package/src/dom/index.ts +11 -0
- package/src/dom/renderer.ts +327 -0
- package/src/index.ts +56 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM Renderer
|
|
3
|
+
*
|
|
4
|
+
* Renders Hypen patches to the DOM
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Patch } from "@hypen/core";
|
|
8
|
+
import type { HypenModuleInstance, RouterContext } from "@hypen/core";
|
|
9
|
+
import type { HypenRouter } from "@hypen/core";
|
|
10
|
+
import type { HypenGlobalContext } from "@hypen/core";
|
|
11
|
+
import { ComponentRegistry } from "./components/index.js";
|
|
12
|
+
import { ApplicatorRegistry } from "./applicators/index.js";
|
|
13
|
+
import { canvasHandler, canvasApplicators } from "./canvas/index.js";
|
|
14
|
+
import { RerenderTracker, type DebugConfig, defaultDebugConfig } from "./debug.js";
|
|
15
|
+
|
|
16
|
+
// Interface for the engine that renderer needs
|
|
17
|
+
interface IEngine {
|
|
18
|
+
dispatchAction(name: string, payload?: any): void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class DOMRenderer {
|
|
22
|
+
private container: HTMLElement;
|
|
23
|
+
private nodes: Map<string, HTMLElement> = new Map();
|
|
24
|
+
private rootId: string | null = null;
|
|
25
|
+
private components: ComponentRegistry;
|
|
26
|
+
private applicators: ApplicatorRegistry;
|
|
27
|
+
private engine: IEngine;
|
|
28
|
+
private currentState: Record<string, any> = {};
|
|
29
|
+
private routerContext: RouterContext | null = null;
|
|
30
|
+
private globalContext: HypenGlobalContext | null = null;
|
|
31
|
+
private componentInstances = new Map<string, HypenModuleInstance>();
|
|
32
|
+
private debugTracker: RerenderTracker;
|
|
33
|
+
|
|
34
|
+
constructor(container: HTMLElement, engine: IEngine, debugConfig?: Partial<DebugConfig>) {
|
|
35
|
+
this.container = container;
|
|
36
|
+
this.engine = engine;
|
|
37
|
+
this.components = new ComponentRegistry();
|
|
38
|
+
this.applicators = new ApplicatorRegistry();
|
|
39
|
+
this.debugTracker = new RerenderTracker({ ...defaultDebugConfig, ...debugConfig });
|
|
40
|
+
|
|
41
|
+
// Register canvas component and applicators
|
|
42
|
+
this.components.register("canvas", canvasHandler);
|
|
43
|
+
for (const [name, handler] of Object.entries(canvasApplicators)) {
|
|
44
|
+
this.applicators.register(name, handler);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Set router and global context for component composition
|
|
50
|
+
*/
|
|
51
|
+
setContext(routerContext: RouterContext, globalContext: HypenGlobalContext): void {
|
|
52
|
+
this.routerContext = routerContext;
|
|
53
|
+
this.globalContext = globalContext;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Apply a batch of patches to the DOM
|
|
58
|
+
*/
|
|
59
|
+
applyPatches(patches: Patch[]): void {
|
|
60
|
+
for (const patch of patches) {
|
|
61
|
+
this.applyPatch(patch);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Update state and interpolate text content
|
|
67
|
+
*/
|
|
68
|
+
updateState(state: Record<string, any>): void {
|
|
69
|
+
console.log(`[Renderer] Updating state:`, state);
|
|
70
|
+
this.currentState = state;
|
|
71
|
+
this.interpolateAllText();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Merge component state into current state and re-interpolate
|
|
76
|
+
*/
|
|
77
|
+
private mergeComponentState(componentState: Record<string, any>): void {
|
|
78
|
+
this.currentState = { ...this.currentState, ...componentState };
|
|
79
|
+
console.log(`[Renderer] Merged state:`, this.currentState);
|
|
80
|
+
this.interpolateAllText();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Interpolate state values in all text elements
|
|
85
|
+
*/
|
|
86
|
+
private interpolateAllText(): void {
|
|
87
|
+
for (const [id, element] of this.nodes.entries()) {
|
|
88
|
+
if (element.dataset.hypenType === "text" && element.dataset.textTemplate) {
|
|
89
|
+
const template = element.dataset.textTemplate;
|
|
90
|
+
const interpolated = this.interpolateText(template, this.currentState);
|
|
91
|
+
|
|
92
|
+
// Track re-render if text actually changed
|
|
93
|
+
const currentText = element.textContent;
|
|
94
|
+
if (currentText !== interpolated) {
|
|
95
|
+
this.debugTracker.trackRerender(id, element, "interpolate");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
element.textContent = interpolated;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Interpolate state values in text template
|
|
105
|
+
*/
|
|
106
|
+
private interpolateText(template: string, state: Record<string, any>): string {
|
|
107
|
+
return template.replace(/\$\{([^}]+)\}/g, (match, path) => {
|
|
108
|
+
try {
|
|
109
|
+
const value = path.split('.').reduce((obj: any, key: string) => {
|
|
110
|
+
if (key === 'state') return state;
|
|
111
|
+
return obj?.[key];
|
|
112
|
+
}, state);
|
|
113
|
+
return value !== undefined ? String(value) : match;
|
|
114
|
+
} catch {
|
|
115
|
+
return match;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Apply a single patch
|
|
122
|
+
*/
|
|
123
|
+
private applyPatch(patch: Patch): void {
|
|
124
|
+
switch (patch.type) {
|
|
125
|
+
case "create":
|
|
126
|
+
this.onCreate(patch.id!, (patch as any).element_type!, patch.props || {});
|
|
127
|
+
break;
|
|
128
|
+
case "setProp":
|
|
129
|
+
this.onSetProp(patch.id!, patch.name!, patch.value);
|
|
130
|
+
break;
|
|
131
|
+
case "setText":
|
|
132
|
+
this.onSetText(patch.id!, patch.text!);
|
|
133
|
+
break;
|
|
134
|
+
case "insert":
|
|
135
|
+
this.onInsert((patch as any).parent_id!, patch.id!, (patch as any).before_id);
|
|
136
|
+
break;
|
|
137
|
+
case "move":
|
|
138
|
+
this.onMove((patch as any).parent_id!, patch.id!, (patch as any).before_id);
|
|
139
|
+
break;
|
|
140
|
+
case "remove":
|
|
141
|
+
this.onRemove(patch.id!);
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Create a new element
|
|
148
|
+
*/
|
|
149
|
+
private onCreate(id: string, elementType: string, props: Record<string, any> | Map<string, any>): void {
|
|
150
|
+
const propsObj = props instanceof Map ? Object.fromEntries(props) : props;
|
|
151
|
+
|
|
152
|
+
const element = this.components.createElement(elementType, propsObj);
|
|
153
|
+
|
|
154
|
+
if (!element) {
|
|
155
|
+
const fallback = document.createElement("div");
|
|
156
|
+
fallback.dataset.hypenType = elementType;
|
|
157
|
+
fallback.textContent = `Unknown component: ${elementType}`;
|
|
158
|
+
this.nodes.set(id, fallback);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
element.dataset.hypenType = elementType.toLowerCase();
|
|
163
|
+
element.dataset.hypenId = id;
|
|
164
|
+
(element as any).__hypenEngine = this.engine;
|
|
165
|
+
|
|
166
|
+
this.applicators.applyAll(element, propsObj);
|
|
167
|
+
this.nodes.set(id, element);
|
|
168
|
+
this.debugTracker.trackRerender(id, element, `create:${elementType}`);
|
|
169
|
+
|
|
170
|
+
if (!this.rootId) {
|
|
171
|
+
this.rootId = id;
|
|
172
|
+
if (!this.container.contains(element)) {
|
|
173
|
+
this.container.appendChild(element);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Set a property on an element
|
|
180
|
+
*/
|
|
181
|
+
private onSetProp(id: string, name: string, value: any): void {
|
|
182
|
+
const element = this.nodes.get(id);
|
|
183
|
+
if (!element) return;
|
|
184
|
+
|
|
185
|
+
this.debugTracker.trackRerender(id, element, `setProp:${name}`);
|
|
186
|
+
|
|
187
|
+
if (name === "0" || name === "text") {
|
|
188
|
+
const elementType = element.dataset.hypenType;
|
|
189
|
+
|
|
190
|
+
if (elementType === "input") {
|
|
191
|
+
const inputEl = element as HTMLInputElement;
|
|
192
|
+
inputEl.value = String(value);
|
|
193
|
+
console.log(`[Renderer] Updated input value: "${value}"`);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
element.textContent = String(value);
|
|
198
|
+
if (element.dataset.textTemplate !== undefined) {
|
|
199
|
+
element.dataset.textTemplate = String(value);
|
|
200
|
+
}
|
|
201
|
+
console.log(`[Renderer] Updated text content: "${value}"`);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
this.applicators.apply(element, name, value);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Set text content
|
|
210
|
+
*/
|
|
211
|
+
private onSetText(id: string, text: string): void {
|
|
212
|
+
const element = this.nodes.get(id);
|
|
213
|
+
if (!element) return;
|
|
214
|
+
|
|
215
|
+
this.debugTracker.trackRerender(id, element, "setText");
|
|
216
|
+
element.textContent = text;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Insert an element into the tree
|
|
221
|
+
*/
|
|
222
|
+
private onInsert(parentId: string, id: string, beforeId?: string): void {
|
|
223
|
+
const parent = parentId === "root" ? this.container : this.nodes.get(parentId);
|
|
224
|
+
const child = this.nodes.get(id);
|
|
225
|
+
|
|
226
|
+
console.log(`[Renderer] Inserting ${id} into ${parentId}`, {
|
|
227
|
+
parent: parent ? `${parent.tagName}#${parent.id || 'no-id'}` : 'null',
|
|
228
|
+
child: child ? `${child.tagName}#${child.id || 'no-id'}` : 'null',
|
|
229
|
+
childText: child?.textContent?.substring(0, 20)
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
if (!parent || !child) return;
|
|
233
|
+
|
|
234
|
+
if (parentId === "root") {
|
|
235
|
+
this.rootId = id;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (beforeId) {
|
|
239
|
+
const before = this.nodes.get(beforeId);
|
|
240
|
+
if (before && before.parentNode === parent) {
|
|
241
|
+
parent.insertBefore(child, before);
|
|
242
|
+
} else if (!parent.contains(child)) {
|
|
243
|
+
parent.appendChild(child);
|
|
244
|
+
}
|
|
245
|
+
} else {
|
|
246
|
+
if (!parent.contains(child)) {
|
|
247
|
+
parent.appendChild(child);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Move an element within the tree
|
|
254
|
+
*/
|
|
255
|
+
private onMove(parentId: string, id: string, beforeId?: string): void {
|
|
256
|
+
this.onInsert(parentId, id, beforeId);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Remove an element from the tree
|
|
261
|
+
*/
|
|
262
|
+
private onRemove(id: string): void {
|
|
263
|
+
const element = this.nodes.get(id);
|
|
264
|
+
if (!element) return;
|
|
265
|
+
|
|
266
|
+
if (element.parentNode) {
|
|
267
|
+
element.parentNode.removeChild(element);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
this.nodes.delete(id);
|
|
271
|
+
|
|
272
|
+
if (this.rootId === id) {
|
|
273
|
+
this.rootId = null;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Get an element by ID
|
|
279
|
+
*/
|
|
280
|
+
getNode(id: string): HTMLElement | undefined {
|
|
281
|
+
return this.nodes.get(id);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Clear all nodes
|
|
286
|
+
*/
|
|
287
|
+
clear(): void {
|
|
288
|
+
this.container.innerHTML = "";
|
|
289
|
+
this.nodes.clear();
|
|
290
|
+
this.rootId = null;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Get the component registry (for registering custom components)
|
|
295
|
+
*/
|
|
296
|
+
getComponentRegistry(): ComponentRegistry {
|
|
297
|
+
return this.components;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Get the applicator registry (for registering custom applicators)
|
|
302
|
+
*/
|
|
303
|
+
getApplicatorRegistry(): ApplicatorRegistry {
|
|
304
|
+
return this.applicators;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Enable or configure debug mode
|
|
309
|
+
*/
|
|
310
|
+
setDebugConfig(config: Partial<DebugConfig>): void {
|
|
311
|
+
this.debugTracker.setConfig(config);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Reset debug tracking for all elements
|
|
316
|
+
*/
|
|
317
|
+
resetDebugTracking(): void {
|
|
318
|
+
this.debugTracker.resetAll();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Get debug statistics
|
|
323
|
+
*/
|
|
324
|
+
getDebugStats(): { totalRerenders: number; elementCount: number; avgRerenders: number } {
|
|
325
|
+
return this.debugTracker.getStats();
|
|
326
|
+
}
|
|
327
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hypen/web - Hypen Web Renderers
|
|
3
|
+
*
|
|
4
|
+
* Browser-only package providing DOM and Canvas rendering for Hypen applications.
|
|
5
|
+
* Requires @hypen/core for the engine and state management.
|
|
6
|
+
*
|
|
7
|
+
* ## Quick Start
|
|
8
|
+
*
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { Engine, app } from "@hypen/core";
|
|
11
|
+
* import { DOMRenderer } from "@hypen/web";
|
|
12
|
+
*
|
|
13
|
+
* // Initialize engine
|
|
14
|
+
* const engine = new Engine();
|
|
15
|
+
* await engine.init({ wasmPath: "/hypen_engine_bg.wasm" });
|
|
16
|
+
*
|
|
17
|
+
* // Create renderer
|
|
18
|
+
* const container = document.getElementById("app")!;
|
|
19
|
+
* const renderer = new DOMRenderer(container, engine);
|
|
20
|
+
*
|
|
21
|
+
* // Set up patch callback
|
|
22
|
+
* engine.setRenderCallback((patches) => {
|
|
23
|
+
* renderer.applyPatches(patches);
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* // Render your app
|
|
27
|
+
* engine.renderSource(`Column { Text("Hello Hypen!") }`);
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// DOM RENDERER
|
|
33
|
+
// ============================================================================
|
|
34
|
+
|
|
35
|
+
export { DOMRenderer } from "./dom/renderer.js";
|
|
36
|
+
export { ComponentRegistry } from "./dom/components/index.js";
|
|
37
|
+
export { ApplicatorRegistry } from "./dom/applicators/index.js";
|
|
38
|
+
export { EventManager } from "./dom/events.js";
|
|
39
|
+
export { RerenderTracker, type DebugConfig, defaultDebugConfig } from "./dom/debug.js";
|
|
40
|
+
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// CANVAS RENDERER
|
|
43
|
+
// ============================================================================
|
|
44
|
+
|
|
45
|
+
export { CanvasRenderer } from "./canvas/renderer.js";
|
|
46
|
+
export { canvasHandler, canvasApplicators } from "./dom/canvas/index.js";
|
|
47
|
+
|
|
48
|
+
// Re-export core types that web users commonly need
|
|
49
|
+
export type {
|
|
50
|
+
Patch,
|
|
51
|
+
Action,
|
|
52
|
+
Renderer,
|
|
53
|
+
RouterContext,
|
|
54
|
+
HypenModuleInstance,
|
|
55
|
+
HypenGlobalContext,
|
|
56
|
+
} from "@hypen/core";
|