@hypen-space/web 0.3.9 → 0.3.11

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 (174) hide show
  1. package/dist/{src/canvas → canvas}/index.js +10 -6
  2. package/dist/{src/canvas → canvas}/index.js.map +4 -4
  3. package/dist/{src/canvas → canvas}/layout.js +4 -2
  4. package/dist/{src/canvas → canvas}/layout.js.map +3 -3
  5. package/dist/{src/canvas → canvas}/paint.js +4 -2
  6. package/dist/{src/canvas → canvas}/paint.js.map +3 -3
  7. package/dist/{src/canvas → canvas}/renderer.js +10 -6
  8. package/dist/{src/canvas → canvas}/renderer.js.map +4 -4
  9. package/dist/{src/canvas → canvas}/text.js +4 -2
  10. package/dist/{src/canvas → canvas}/text.js.map +3 -3
  11. package/dist/{src/dom → dom}/applicators/advanced-layout.js +16 -3
  12. package/dist/dom/applicators/advanced-layout.js.map +11 -0
  13. package/dist/{src/dom → dom}/applicators/events.js +6 -4
  14. package/dist/dom/applicators/events.js.map +11 -0
  15. package/dist/{src/dom → dom}/applicators/index.js +153 -24
  16. package/dist/dom/applicators/index.js.map +26 -0
  17. package/dist/{src/dom → dom}/applicators/layout.js +16 -3
  18. package/dist/dom/applicators/layout.js.map +10 -0
  19. package/dist/dom/applicators/margin.js +104 -0
  20. package/dist/dom/applicators/margin.js.map +10 -0
  21. package/dist/dom/applicators/padding.js +104 -0
  22. package/dist/dom/applicators/padding.js.map +10 -0
  23. package/dist/{src/dom → dom}/applicators/size.js +11 -3
  24. package/dist/{src/dom → dom}/applicators/size.js.map +3 -3
  25. package/dist/dom/components/button.js.map +10 -0
  26. package/dist/{src/dom → dom}/components/center.js +4 -1
  27. package/dist/dom/components/center.js.map +10 -0
  28. package/dist/dom/components/column.js.map +10 -0
  29. package/dist/{src/dom → dom}/components/container.js +1 -4
  30. package/dist/{src/dom → dom}/components/container.js.map +3 -3
  31. package/dist/{src/dom → dom}/components/grid.js +18 -2
  32. package/dist/dom/components/grid.js.map +10 -0
  33. package/dist/{src/dom → dom}/components/hypenapp.js +15 -13
  34. package/dist/dom/components/hypenapp.js.map +10 -0
  35. package/dist/{src/dom → dom}/components/index.js +64 -20
  36. package/dist/dom/components/index.js.map +41 -0
  37. package/dist/{src/dom → dom}/components/route.js +5 -3
  38. package/dist/dom/components/route.js.map +10 -0
  39. package/dist/{src/dom → dom}/components/row.js +18 -2
  40. package/dist/dom/components/row.js.map +10 -0
  41. package/dist/{src/dom → dom}/components/stack.js +5 -1
  42. package/dist/dom/components/stack.js.map +10 -0
  43. package/dist/{src/dom → dom}/components/text.js +5 -1
  44. package/dist/dom/components/text.js.map +10 -0
  45. package/dist/{src/dom → dom}/debug.js +5 -3
  46. package/dist/dom/debug.js.map +10 -0
  47. package/dist/{src/dom → dom}/events.js +15 -12
  48. package/dist/dom/events.js.map +10 -0
  49. package/dist/{src/dom/renderer.js → dom/index.js} +345 -55
  50. package/dist/dom/index.js.map +62 -0
  51. package/dist/{src/dom/index.js → dom/renderer.js} +233 -164
  52. package/dist/dom/renderer.js.map +61 -0
  53. package/dist/{src/hypen.js → hypen.js} +265 -118
  54. package/dist/hypen.js.map +62 -0
  55. package/dist/{src/index.js → index.js} +288 -134
  56. package/dist/index.js.map +72 -0
  57. package/package.json +22 -22
  58. package/src/canvas/renderer.ts +7 -4
  59. package/src/canvas/text.ts +4 -1
  60. package/src/dom/applicators/events.ts +6 -12
  61. package/src/dom/applicators/layout.ts +4 -0
  62. package/src/dom/applicators/size.ts +7 -2
  63. package/src/dom/components/center.ts +9 -0
  64. package/src/dom/components/column.ts +0 -4
  65. package/src/dom/components/grid.ts +22 -0
  66. package/src/dom/components/hypenapp.ts +14 -11
  67. package/src/dom/components/route.ts +4 -1
  68. package/src/dom/components/row.ts +20 -0
  69. package/src/dom/debug.ts +6 -2
  70. package/src/dom/events.ts +16 -12
  71. package/src/dom/renderer.ts +12 -9
  72. package/src/hypen.ts +32 -73
  73. package/dist/src/dom/applicators/advanced-layout.js.map +0 -11
  74. package/dist/src/dom/applicators/events.js.map +0 -11
  75. package/dist/src/dom/applicators/index.js.map +0 -26
  76. package/dist/src/dom/applicators/layout.js.map +0 -10
  77. package/dist/src/dom/applicators/margin.js +0 -51
  78. package/dist/src/dom/applicators/margin.js.map +0 -10
  79. package/dist/src/dom/applicators/padding.js +0 -51
  80. package/dist/src/dom/applicators/padding.js.map +0 -10
  81. package/dist/src/dom/components/button.js.map +0 -10
  82. package/dist/src/dom/components/center.js.map +0 -10
  83. package/dist/src/dom/components/column.js.map +0 -10
  84. package/dist/src/dom/components/grid.js.map +0 -10
  85. package/dist/src/dom/components/hypenapp.js.map +0 -10
  86. package/dist/src/dom/components/index.js.map +0 -41
  87. package/dist/src/dom/components/route.js.map +0 -10
  88. package/dist/src/dom/components/row.js.map +0 -10
  89. package/dist/src/dom/components/stack.js.map +0 -10
  90. package/dist/src/dom/components/text.js.map +0 -10
  91. package/dist/src/dom/debug.js.map +0 -10
  92. package/dist/src/dom/events.js.map +0 -10
  93. package/dist/src/dom/index.js.map +0 -62
  94. package/dist/src/dom/renderer.js.map +0 -61
  95. package/dist/src/hypen.js.map +0 -62
  96. package/dist/src/index.js.map +0 -72
  97. /package/dist/{src/canvas → canvas}/accessibility.js +0 -0
  98. /package/dist/{src/canvas → canvas}/accessibility.js.map +0 -0
  99. /package/dist/{src/canvas → canvas}/events.js +0 -0
  100. /package/dist/{src/canvas → canvas}/events.js.map +0 -0
  101. /package/dist/{src/canvas → canvas}/input.js +0 -0
  102. /package/dist/{src/canvas → canvas}/input.js.map +0 -0
  103. /package/dist/{src/canvas → canvas}/types.js +0 -0
  104. /package/dist/{src/canvas → canvas}/types.js.map +0 -0
  105. /package/dist/{src/canvas → canvas}/utils.js +0 -0
  106. /package/dist/{src/canvas → canvas}/utils.js.map +0 -0
  107. /package/dist/{src/dom → dom}/applicators/background.js +0 -0
  108. /package/dist/{src/dom → dom}/applicators/background.js.map +0 -0
  109. /package/dist/{src/dom → dom}/applicators/border.js +0 -0
  110. /package/dist/{src/dom → dom}/applicators/border.js.map +0 -0
  111. /package/dist/{src/dom → dom}/applicators/color.js +0 -0
  112. /package/dist/{src/dom → dom}/applicators/color.js.map +0 -0
  113. /package/dist/{src/dom → dom}/applicators/display.js +0 -0
  114. /package/dist/{src/dom → dom}/applicators/display.js.map +0 -0
  115. /package/dist/{src/dom → dom}/applicators/effects.js +0 -0
  116. /package/dist/{src/dom → dom}/applicators/effects.js.map +0 -0
  117. /package/dist/{src/dom → dom}/applicators/font.js +0 -0
  118. /package/dist/{src/dom → dom}/applicators/font.js.map +0 -0
  119. /package/dist/{src/dom → dom}/applicators/transform.js +0 -0
  120. /package/dist/{src/dom → dom}/applicators/transform.js.map +0 -0
  121. /package/dist/{src/dom → dom}/applicators/transition.js +0 -0
  122. /package/dist/{src/dom → dom}/applicators/transition.js.map +0 -0
  123. /package/dist/{src/dom → dom}/applicators/types.js +0 -0
  124. /package/dist/{src/dom → dom}/applicators/types.js.map +0 -0
  125. /package/dist/{src/dom → dom}/applicators/typography.js +0 -0
  126. /package/dist/{src/dom → dom}/applicators/typography.js.map +0 -0
  127. /package/dist/{src/dom → dom}/canvas/index.js +0 -0
  128. /package/dist/{src/dom → dom}/canvas/index.js.map +0 -0
  129. /package/dist/{src/dom → dom}/components/audio.js +0 -0
  130. /package/dist/{src/dom → dom}/components/audio.js.map +0 -0
  131. /package/dist/{src/dom → dom}/components/avatar.js +0 -0
  132. /package/dist/{src/dom → dom}/components/avatar.js.map +0 -0
  133. /package/dist/{src/dom → dom}/components/badge.js +0 -0
  134. /package/dist/{src/dom → dom}/components/badge.js.map +0 -0
  135. /package/dist/{src/dom → dom}/components/button.js +0 -0
  136. /package/dist/{src/dom → dom}/components/card.js +0 -0
  137. /package/dist/{src/dom → dom}/components/card.js.map +0 -0
  138. /package/dist/{src/dom → dom}/components/checkbox.js +0 -0
  139. /package/dist/{src/dom → dom}/components/checkbox.js.map +0 -0
  140. /package/dist/{src/dom → dom}/components/column.js +0 -0
  141. /package/dist/{src/dom → dom}/components/divider.js +0 -0
  142. /package/dist/{src/dom → dom}/components/divider.js.map +0 -0
  143. /package/dist/{src/dom → dom}/components/heading.js +0 -0
  144. /package/dist/{src/dom → dom}/components/heading.js.map +0 -0
  145. /package/dist/{src/dom → dom}/components/image.js +0 -0
  146. /package/dist/{src/dom → dom}/components/image.js.map +0 -0
  147. /package/dist/{src/dom → dom}/components/input.js +0 -0
  148. /package/dist/{src/dom → dom}/components/input.js.map +0 -0
  149. /package/dist/{src/dom → dom}/components/link.js +0 -0
  150. /package/dist/{src/dom → dom}/components/link.js.map +0 -0
  151. /package/dist/{src/dom → dom}/components/list.js +0 -0
  152. /package/dist/{src/dom → dom}/components/list.js.map +0 -0
  153. /package/dist/{src/dom → dom}/components/paragraph.js +0 -0
  154. /package/dist/{src/dom → dom}/components/paragraph.js.map +0 -0
  155. /package/dist/{src/dom → dom}/components/progressbar.js +0 -0
  156. /package/dist/{src/dom → dom}/components/progressbar.js.map +0 -0
  157. /package/dist/{src/dom → dom}/components/router.js +0 -0
  158. /package/dist/{src/dom → dom}/components/router.js.map +0 -0
  159. /package/dist/{src/dom → dom}/components/select.js +0 -0
  160. /package/dist/{src/dom → dom}/components/select.js.map +0 -0
  161. /package/dist/{src/dom → dom}/components/slider.js +0 -0
  162. /package/dist/{src/dom → dom}/components/slider.js.map +0 -0
  163. /package/dist/{src/dom → dom}/components/spacer.js +0 -0
  164. /package/dist/{src/dom → dom}/components/spacer.js.map +0 -0
  165. /package/dist/{src/dom → dom}/components/spinner.js +0 -0
  166. /package/dist/{src/dom → dom}/components/spinner.js.map +0 -0
  167. /package/dist/{src/dom → dom}/components/switch.js +0 -0
  168. /package/dist/{src/dom → dom}/components/switch.js.map +0 -0
  169. /package/dist/{src/dom → dom}/components/textarea.js +0 -0
  170. /package/dist/{src/dom → dom}/components/textarea.js.map +0 -0
  171. /package/dist/{src/dom → dom}/components/video.js +0 -0
  172. /package/dist/{src/dom → dom}/components/video.js.map +0 -0
  173. /package/dist/{src/dom → dom}/element-data.js +0 -0
  174. /package/dist/{src/dom → dom}/element-data.js.map +0 -0
package/package.json CHANGED
@@ -1,40 +1,40 @@
1
1
  {
2
2
  "name": "@hypen-space/web",
3
- "version": "0.3.9",
3
+ "version": "0.3.11",
4
4
  "description": "Hypen web renderers - DOM and Canvas rendering for browsers",
5
5
  "type": "module",
6
- "main": "./dist/src/index.js",
7
- "module": "./dist/src/index.js",
8
- "types": "./dist/src/index.d.ts",
9
- "browser": "./dist/src/index.js",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "browser": "./dist/index.js",
10
10
  "exports": {
11
11
  ".": {
12
- "types": "./dist/src/index.d.ts",
13
- "browser": "./dist/src/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "browser": "./dist/index.js",
14
14
  "bun": "./src/index.ts",
15
- "import": "./dist/src/index.js",
16
- "default": "./dist/src/index.js"
15
+ "import": "./dist/index.js",
16
+ "default": "./dist/index.js"
17
17
  },
18
18
  "./dom": {
19
- "types": "./dist/src/dom/index.d.ts",
20
- "browser": "./dist/src/dom/index.js",
19
+ "types": "./dist/dom/index.d.ts",
20
+ "browser": "./dist/dom/index.js",
21
21
  "bun": "./src/dom/index.ts",
22
- "import": "./dist/src/dom/index.js",
23
- "default": "./dist/src/dom/index.js"
22
+ "import": "./dist/dom/index.js",
23
+ "default": "./dist/dom/index.js"
24
24
  },
25
25
  "./canvas": {
26
- "types": "./dist/src/canvas/index.d.ts",
27
- "browser": "./dist/src/canvas/index.js",
26
+ "types": "./dist/canvas/index.d.ts",
27
+ "browser": "./dist/canvas/index.js",
28
28
  "bun": "./src/canvas/index.ts",
29
- "import": "./dist/src/canvas/index.js",
30
- "default": "./dist/src/canvas/index.js"
29
+ "import": "./dist/canvas/index.js",
30
+ "default": "./dist/canvas/index.js"
31
31
  },
32
32
  "./hypen": {
33
- "types": "./dist/src/hypen.d.ts",
34
- "browser": "./dist/src/hypen.js",
33
+ "types": "./dist/hypen.d.ts",
34
+ "browser": "./dist/hypen.js",
35
35
  "bun": "./src/hypen.ts",
36
- "import": "./dist/src/hypen.js",
37
- "default": "./dist/src/hypen.js"
36
+ "import": "./dist/hypen.js",
37
+ "default": "./dist/hypen.js"
38
38
  }
39
39
  },
40
40
  "files": [
@@ -48,7 +48,7 @@
48
48
  "clean": "rm -rf dist"
49
49
  },
50
50
  "dependencies": {
51
- "@hypen-space/core": "^0.3.9"
51
+ "@hypen-space/core": "^0.3.10"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/bun": "latest",
@@ -5,6 +5,9 @@
5
5
  */
6
6
 
7
7
  import type { Renderer, Patch } from "@hypen-space/core";
8
+ import { frameworkLoggers } from "@hypen-space/core";
9
+
10
+ const log = frameworkLoggers.canvas;
8
11
 
9
12
  // Interface for the engine that canvas renderer needs
10
13
  interface IEngine {
@@ -130,7 +133,7 @@ export class CanvasRenderer implements Renderer {
130
133
  private applyPatch(patch: Patch): void {
131
134
  switch (patch.type) {
132
135
  case "create":
133
- this.onCreate(patch.id!, (patch as any).element_type!, patch.props || {});
136
+ this.onCreate(patch.id!, patch.elementType!, patch.props || {});
134
137
  break;
135
138
 
136
139
  case "setProp":
@@ -142,11 +145,11 @@ export class CanvasRenderer implements Renderer {
142
145
  break;
143
146
 
144
147
  case "insert":
145
- this.onInsert((patch as any).parent_id!, patch.id!, (patch as any).before_id);
148
+ this.onInsert(patch.parentId!, patch.id!, patch.beforeId);
146
149
  break;
147
150
 
148
151
  case "move":
149
- this.onMove((patch as any).parent_id!, patch.id!, (patch as any).before_id);
152
+ this.onMove(patch.parentId!, patch.id!, patch.beforeId);
150
153
  break;
151
154
 
152
155
  case "remove":
@@ -352,7 +355,7 @@ export class CanvasRenderer implements Renderer {
352
355
  const elapsed = performance.now() - startTime;
353
356
  this.frameCount++;
354
357
  if (performance.now() - this.lastFrameTime > 1000) {
355
- console.log(`Canvas FPS: ${this.frameCount}, Last frame: ${elapsed.toFixed(2)}ms`);
358
+ log.debug(`Canvas FPS: ${this.frameCount}, Last frame: ${elapsed.toFixed(2)}ms`);
356
359
  this.frameCount = 0;
357
360
  this.lastFrameTime = performance.now();
358
361
  }
@@ -6,6 +6,9 @@
6
6
 
7
7
  import type { FontStyle, TextMetrics, TextStyle } from "./types.js";
8
8
  import { createFontString } from "./utils.js";
9
+ import { frameworkLoggers } from "@hypen-space/core";
10
+
11
+ const log = frameworkLoggers.canvas;
9
12
 
10
13
  /**
11
14
  * Text metrics cache
@@ -166,7 +169,7 @@ export async function loadFont(fontFamily: string, fontWeight: string | number =
166
169
  try {
167
170
  await document.fonts.load(font);
168
171
  } catch (error) {
169
- console.warn(`Failed to load font: ${font}`, error);
172
+ log.warn(`Failed to load font: ${font}`, error);
170
173
  }
171
174
  }
172
175
 
@@ -12,6 +12,7 @@ import {
12
12
  disposableTimeout,
13
13
  type Disposable,
14
14
  } from "@hypen-space/core/disposable";
15
+ import { frameworkLoggers } from "@hypen-space/core";
15
16
  import {
16
17
  type IEngine,
17
18
  getEngine,
@@ -22,6 +23,8 @@ import {
22
23
  setKeyTarget,
23
24
  } from "../element-data.js";
24
25
 
26
+ const log = frameworkLoggers.events;
27
+
25
28
  // ============================================================================
26
29
  // Types
27
30
  // ============================================================================
@@ -198,10 +201,7 @@ function createEventHandler(
198
201
  const { actionName, payload: customPayload } = extractActionDetails(value);
199
202
 
200
203
  if (!actionName) {
201
- console.warn(
202
- `[EventApplicator] ${eventType} requires an action reference starting with @, got:`,
203
- value
204
- );
204
+ log.warn(`${eventType} requires an action reference starting with @, got:`, value);
205
205
  return;
206
206
  }
207
207
 
@@ -278,10 +278,7 @@ function createKeyHandler(defaultKey: string = "Enter"): ApplicatorHandler {
278
278
  const { actionName, payload: customPayload } = extractActionDetails(value);
279
279
 
280
280
  if (!actionName) {
281
- console.warn(
282
- `[EventApplicator] onKey requires an action reference starting with @, got:`,
283
- value
284
- );
281
+ log.warn(`onKey requires an action reference starting with @, got:`, value);
285
282
  return;
286
283
  }
287
284
 
@@ -343,10 +340,7 @@ function createLongClickHandler(thresholdMs: number = 500): ApplicatorHandler {
343
340
  const { actionName, payload: customPayload } = extractActionDetails(value);
344
341
 
345
342
  if (!actionName) {
346
- console.warn(
347
- `[EventApplicator] onLongClick requires an action reference starting with @, got:`,
348
- value
349
- );
343
+ log.warn(`onLongClick requires an action reference starting with @, got:`, value);
350
344
  return;
351
345
  }
352
346
 
@@ -104,11 +104,15 @@ export const layoutHandlers: Record<string, ApplicatorHandler> = {
104
104
  // Use .weight(1) to make element take remaining space in Row/Column
105
105
  weight: (el, value) => {
106
106
  el.style.flex = String(value);
107
+ // Mark element as having flex for CSS parent detection
108
+ el.dataset.hypenFlex = "true";
107
109
  },
108
110
 
109
111
  // flex: CSS flex shorthand (kept for CSS compatibility)
110
112
  flex: (el, value) => {
111
113
  el.style.flex = String(value);
114
+ // Mark element as having flex for CSS parent detection
115
+ el.dataset.hypenFlex = "true";
112
116
  },
113
117
 
114
118
  flexGrow: (el, value) => {
@@ -136,12 +136,13 @@ export const sizeHandlers: Record<string, ApplicatorHandler> = {
136
136
  },
137
137
 
138
138
  // Fill max width - stretch to fill parent width
139
+ // Note: This only stretches within parent's current width.
140
+ // For full-width behavior, parent Columns also need fillMaxWidth(true).
139
141
  fillMaxWidth: (el, value) => {
140
142
  if (value === false) return;
141
143
  const fraction = typeof value === "number" ? value : 1;
142
144
  if (fraction === 1) {
143
- // For 100% width: use align-self stretch in Column (cross-axis)
144
- // and width 100% as fallback for Row and other contexts
145
+ // Use align-self stretch to fill cross-axis in flex containers
145
146
  el.style.alignSelf = "stretch";
146
147
  el.style.width = "100%";
147
148
  el.style.minWidth = "0"; // Prevent flex item from overflowing
@@ -168,5 +169,9 @@ export const sizeHandlers: Record<string, ApplicatorHandler> = {
168
169
  const fraction = typeof value === "number" ? value : 1;
169
170
  el.style.width = `${fraction * 100}%`;
170
171
  el.style.height = `${fraction * 100}%`;
172
+ // Use align-self stretch to fill cross-axis in flex containers
173
+ // This is needed because parent might have alignItems: flex-start (wrap behavior)
174
+ el.style.alignSelf = "stretch";
175
+ el.style.minWidth = "0"; // Prevent flex item from overflowing
171
176
  },
172
177
  };
@@ -1,5 +1,9 @@
1
1
  /**
2
2
  * Center Component - Centers content
3
+ *
4
+ * Center expands to fill available space by default (matching iOS/Android behavior).
5
+ * This is because a Center that wraps to content can't meaningfully center anything.
6
+ * The expansion is constrained by parent's layout rules.
3
7
  */
4
8
 
5
9
  import type { ComponentHandler } from "./index.js";
@@ -10,6 +14,11 @@ export const centerHandler: ComponentHandler = {
10
14
  el.style.display = "flex";
11
15
  el.style.alignItems = "center";
12
16
  el.style.justifyContent = "center";
17
+ // Center expands to fill available space by default
18
+ // This matches iOS which has .frame(maxWidth: .infinity, maxHeight: .infinity)
19
+ el.style.width = "100%";
20
+ el.style.height = "100%";
21
+ el.style.alignSelf = "stretch"; // Needed for flex containers with alignItems: flex-start
13
22
  el.dataset.hypenType = "center";
14
23
  return el;
15
24
  },
@@ -2,7 +2,6 @@
2
2
  * Column Component - Vertical Stack
3
3
  *
4
4
  * Children wrap to content by default (matching Android/iOS behavior).
5
- * Column stretches to fill parent's width so children with fillMaxWidth work.
6
5
  * Use .fillMaxWidth() on children or .horizontalAlignment("stretch") to stretch.
7
6
  */
8
7
 
@@ -15,9 +14,6 @@ export const columnHandler: ComponentHandler = {
15
14
  el.style.flexDirection = "column";
16
15
  // Wrap children to content by default (match iOS/Android behavior)
17
16
  el.style.alignItems = "flex-start";
18
- // Stretch to fill parent's width so children with fillMaxWidth can reference it
19
- // This matches iOS/Android where Column expands to accommodate children that want to stretch
20
- el.style.alignSelf = "stretch";
21
17
  el.dataset.hypenType = "column";
22
18
  return el;
23
19
  },
@@ -1,11 +1,33 @@
1
1
  /**
2
2
  * Grid Component - CSS Grid Layout
3
+ *
4
+ * Grid items stretch to fill their cells by default (matching Android/iOS behavior).
3
5
  */
4
6
 
5
7
  import type { ComponentHandler } from "./index.js";
6
8
 
9
+ // Inject global styles for grid children
10
+ let gridStylesInjected = false;
11
+ function ensureGridStyles(): void {
12
+ if (gridStylesInjected) return;
13
+ gridStylesInjected = true;
14
+
15
+ const style = document.createElement("style");
16
+ style.id = "hypen-grid-styles";
17
+ style.textContent = `
18
+ /* Grid children stretch to fill cells by default (matches Android behavior) */
19
+ [data-hypen-type="grid"] > * {
20
+ justify-self: stretch;
21
+ align-self: stretch;
22
+ }
23
+ `;
24
+ document.head.appendChild(style);
25
+ }
26
+
7
27
  export const gridHandler: ComponentHandler = {
8
28
  create(): HTMLElement {
29
+ ensureGridStyles();
30
+
9
31
  const el = document.createElement("div");
10
32
  el.style.display = "grid";
11
33
  el.dataset.hypenType = "grid";
@@ -15,6 +15,9 @@
15
15
  import type { ComponentHandler } from "./index.js";
16
16
  import { RemoteEngine } from "@hypen-space/core/remote/client";
17
17
  import type { Patch } from "@hypen-space/core/remote";
18
+ import { frameworkLoggers } from "@hypen-space/core";
19
+
20
+ const log = frameworkLoggers.remote;
18
21
 
19
22
  // Store active HypenApp instances for cleanup
20
23
  const activeInstances = new WeakMap<
@@ -38,7 +41,7 @@ export const hypenAppHandler: ComponentHandler = {
38
41
  const url = props["0"] || props.url;
39
42
 
40
43
  if (!url || typeof url !== "string") {
41
- console.error("[HypenApp] URL is required");
44
+ log.error("HypenApp: URL is required");
42
45
  element.innerHTML = '<div style="color: red;">HypenApp: URL required</div>';
43
46
  return;
44
47
  }
@@ -80,20 +83,20 @@ export const hypenAppHandler: ComponentHandler = {
80
83
  .then(() => {
81
84
  // Clear loading state - patches will populate content
82
85
  element.innerHTML = "";
83
- console.log(`[HypenApp] Connected to ${url}`);
86
+ log.debug(`HypenApp connected to ${url}`);
84
87
  })
85
88
  .catch((error) => {
86
89
  element.innerHTML = `<div style="color: red;">HypenApp: Connection failed - ${error.message}</div>`;
87
- console.error("[HypenApp] Connection failed:", error);
90
+ log.error("HypenApp connection failed:", error);
88
91
  });
89
92
 
90
93
  // Handle disconnection
91
94
  engine.onDisconnect(() => {
92
- console.log("[HypenApp] Disconnected");
95
+ log.debug("HypenApp disconnected");
93
96
  });
94
97
 
95
98
  engine.onError((error) => {
96
- console.error("[HypenApp] Error:", error);
99
+ log.error("HypenApp error:", error);
97
100
  });
98
101
 
99
102
  // Cleanup on element removal
@@ -104,7 +107,7 @@ export const hypenAppHandler: ComponentHandler = {
104
107
  engine.disconnect();
105
108
  activeInstances.delete(element);
106
109
  observer.disconnect();
107
- console.log("[HypenApp] Cleaned up");
110
+ log.debug("HypenApp cleaned up");
108
111
  return;
109
112
  }
110
113
  }
@@ -131,7 +134,7 @@ function applyPatches(
131
134
  for (const patch of patches) {
132
135
  switch (patch.type) {
133
136
  case "create": {
134
- const el = createElement((patch as any).element_type, patch.props || {});
137
+ const el = createElement(patch.elementType!, patch.props || {});
135
138
  el.dataset.hypenId = patch.id!;
136
139
  (el as any).__hypenEngine = engine;
137
140
  nodes.set(patch.id!, el);
@@ -155,10 +158,10 @@ function applyPatches(
155
158
  }
156
159
 
157
160
  case "insert": {
158
- const parentId = (patch as any).parent_id;
161
+ const parentId = patch.parentId;
159
162
  const parent = parentId === "root" ? container : nodes.get(parentId);
160
163
  const child = nodes.get(patch.id!);
161
- const beforeId = (patch as any).before_id;
164
+ const beforeId = patch.beforeId;
162
165
 
163
166
  if (parent && child) {
164
167
  if (parentId === "root") {
@@ -180,10 +183,10 @@ function applyPatches(
180
183
  }
181
184
 
182
185
  case "move": {
183
- const parentId = (patch as any).parent_id;
186
+ const parentId = patch.parentId;
184
187
  const parent = parentId === "root" ? container : nodes.get(parentId);
185
188
  const child = nodes.get(patch.id!);
186
- const beforeId = (patch as any).before_id;
189
+ const beforeId = patch.beforeId;
187
190
 
188
191
  if (parent && child) {
189
192
  if (beforeId) {
@@ -4,6 +4,9 @@
4
4
  */
5
5
 
6
6
  import type { ComponentHandler } from "./index.js";
7
+ import { frameworkLoggers } from "@hypen-space/core";
8
+
9
+ const log = frameworkLoggers.router;
7
10
 
8
11
  export const routeHandler: ComponentHandler = {
9
12
  create(): HTMLElement {
@@ -31,7 +34,7 @@ export const routeHandler: ComponentHandler = {
31
34
  el.dataset.routeComponent = String(componentName);
32
35
  }
33
36
 
34
- console.log(`🛣️ Route created: path="${path}", lazy=${isLazy}, component="${el.dataset.routeComponent || 'none'}"`);
37
+ log.debug(`Route created: path="${path}", lazy=${isLazy}, component="${el.dataset.routeComponent || 'none'}"`);
35
38
  },
36
39
  };
37
40
 
@@ -6,8 +6,28 @@
6
6
 
7
7
  import type { ComponentHandler } from "./index.js";
8
8
 
9
+ // Inject global styles for Row with flex/weighted children
10
+ let rowStylesInjected = false;
11
+ function ensureRowStyles(): void {
12
+ if (rowStylesInjected) return;
13
+ rowStylesInjected = true;
14
+
15
+ const style = document.createElement("style");
16
+ style.id = "hypen-row-styles";
17
+ style.textContent = `
18
+ /* Row expands to fill width when it has children with flex/weight */
19
+ /* This matches iOS/Android behavior where weighted children cause parent to expand */
20
+ [data-hypen-type="row"]:has(> [data-hypen-flex]) {
21
+ width: 100%;
22
+ }
23
+ `;
24
+ document.head.appendChild(style);
25
+ }
26
+
9
27
  export const rowHandler: ComponentHandler = {
10
28
  create(): HTMLElement {
29
+ ensureRowStyles();
30
+
11
31
  const el = document.createElement("div");
12
32
  el.style.display = "flex";
13
33
  el.style.flexDirection = "row";
package/src/dom/debug.ts CHANGED
@@ -4,6 +4,10 @@
4
4
  * Provides heatmap overlays to visualize re-renders
5
5
  */
6
6
 
7
+ import { frameworkLoggers } from "@hypen-space/core";
8
+
9
+ const log = frameworkLoggers.debug;
10
+
7
11
  export interface DebugConfig {
8
12
  /** Enable debug mode */
9
13
  enabled: boolean;
@@ -57,7 +61,7 @@ export class RerenderTracker {
57
61
  return;
58
62
  }
59
63
 
60
- console.log(`🔥 [Debug] Tracking re-render: ${id} - ${patchType}`);
64
+ log.debug(`Tracking re-render: ${id} - ${patchType}`);
61
65
 
62
66
  // Increment render count
63
67
  const currentCount = this.renderCounts.get(id) || 0;
@@ -78,7 +82,7 @@ export class RerenderTracker {
78
82
  this.config.maxOpacity
79
83
  );
80
84
 
81
- console.log(`🔥 [Debug] Updating heatmap for ${id}, count: ${renderCount}, opacity: ${opacity}`);
85
+ log.debug(`Updating heatmap for ${id}, count: ${renderCount}, opacity: ${opacity}`);
82
86
 
83
87
  // For inline elements or text, use a simpler approach: background color + outline
84
88
  const isInline = window.getComputedStyle(element).display.includes('inline');
package/src/dom/events.ts CHANGED
@@ -4,6 +4,10 @@
4
4
  * Handles DOM event binding and action dispatching
5
5
  */
6
6
 
7
+ import { frameworkLoggers } from "@hypen-space/core";
8
+
9
+ const log = frameworkLoggers.events;
10
+
7
11
  // Interface for the engine that EventManager needs
8
12
  interface IEngine {
9
13
  dispatchAction(name: string, payload?: any): void;
@@ -29,13 +33,13 @@ export class EventManager {
29
33
  attach(elementId: string, element: HTMLElement, eventName: string, actionName: string): void {
30
34
  // Convert onClick to click for DOM events
31
35
  const domEventName = eventName === "onClick" ? "click" : eventName;
32
- console.log(`[EventManager] Attaching ${eventName} (DOM: ${domEventName}) to element ${elementId}, action: ${actionName}`);
36
+ log.debug(`Attaching ${eventName} (DOM: ${domEventName}) to element ${elementId}, action: ${actionName}`);
33
37
 
34
38
  // Create event listener that dispatches action
35
39
  const listener = (event: Event) => {
36
- console.log(`🔥 [EventManager] Event fired: ${eventName} on ${elementId}, dispatching action: ${actionName}`);
37
- console.log(`🔥 [EventManager] Event object:`, event);
38
- console.log(`🔥 [EventManager] Element:`, element);
40
+ log.debug(`Event fired: ${eventName} on ${elementId}, dispatching action: ${actionName}`);
41
+ log.debug(`Event object:`, event);
42
+ log.debug(`Element:`, element);
39
43
 
40
44
  // Prevent default for certain events
41
45
  if (eventName === "submit" || (eventName === "click" && element.tagName === "A")) {
@@ -44,15 +48,15 @@ export class EventManager {
44
48
 
45
49
  // Extract event data
46
50
  const payload = this.extractEventData(event, element);
47
- console.log(`🔥 [EventManager] Event payload:`, payload);
51
+ log.debug(`Event payload:`, payload);
48
52
 
49
53
  // Dispatch action to engine
50
- console.log(`🔥 [EventManager] Calling engine.dispatchAction(${actionName})`);
54
+ log.debug(`Calling engine.dispatchAction(${actionName})`);
51
55
  try {
52
56
  this.engine.dispatchAction(actionName, payload);
53
- console.log(`🔥 [EventManager] ✅ dispatchAction succeeded`);
57
+ log.debug(`dispatchAction succeeded`);
54
58
  } catch (error) {
55
- console.error(`🔥 [EventManager] ❌ dispatchAction failed:`, error);
59
+ log.error(`dispatchAction failed:`, error);
56
60
  }
57
61
  };
58
62
 
@@ -66,18 +70,18 @@ export class EventManager {
66
70
 
67
71
  // Attach to DOM
68
72
  element.addEventListener(domEventName, listener);
69
- console.log(`[EventManager] Listener attached to DOM for ${domEventName}`);
70
- console.log(`[EventManager] Element details:`, {
73
+ log.debug(`Listener attached to DOM for ${domEventName}`);
74
+ log.debug(`Element details:`, {
71
75
  tagName: element.tagName,
72
76
  id: element.id,
73
77
  dataset: element.dataset,
74
78
  textContent: element.textContent?.substring(0, 50)
75
79
  });
76
-
80
+
77
81
  // Test: Add a simple click listener to verify DOM events work
78
82
  if (domEventName === "click") {
79
83
  element.addEventListener("click", (e) => {
80
- console.log(`🧪 [TEST] Raw DOM click detected on ${element.tagName}`, e);
84
+ log.debug(`Raw DOM click detected on ${element.tagName}`, e);
81
85
  });
82
86
  }
83
87
  }
@@ -8,6 +8,9 @@ import type { Patch } from "@hypen-space/core";
8
8
  import type { HypenModuleInstance, RouterContext } from "@hypen-space/core";
9
9
  import type { HypenRouter } from "@hypen-space/core";
10
10
  import type { HypenGlobalContext } from "@hypen-space/core";
11
+ import { frameworkLoggers } from "@hypen-space/core";
12
+
13
+ const log = frameworkLoggers.renderer;
11
14
  import { ComponentRegistry } from "./components/index.js";
12
15
  import { ApplicatorRegistry } from "./applicators/index.js";
13
16
  import { canvasHandler, canvasApplicators } from "./canvas/index.js";
@@ -67,7 +70,7 @@ export class DOMRenderer {
67
70
  * Update state and interpolate text content
68
71
  */
69
72
  updateState(state: Record<string, any>): void {
70
- console.log(`[Renderer] Updating state:`, state);
73
+ log.debug("Updating state:", state);
71
74
  this.currentState = state;
72
75
  this.interpolateAllText();
73
76
  }
@@ -77,7 +80,7 @@ export class DOMRenderer {
77
80
  */
78
81
  private mergeComponentState(componentState: Record<string, any>): void {
79
82
  this.currentState = { ...this.currentState, ...componentState };
80
- console.log(`[Renderer] Merged state:`, this.currentState);
83
+ log.debug("Merged state:", this.currentState);
81
84
  this.interpolateAllText();
82
85
  }
83
86
 
@@ -124,7 +127,7 @@ export class DOMRenderer {
124
127
  private applyPatch(patch: Patch): void {
125
128
  switch (patch.type) {
126
129
  case "create":
127
- this.onCreate(patch.id!, (patch as any).element_type!, patch.props || {});
130
+ this.onCreate(patch.id!, patch.elementType!, patch.props || {});
128
131
  break;
129
132
  case "setProp":
130
133
  this.onSetProp(patch.id!, patch.name!, patch.value);
@@ -133,10 +136,10 @@ export class DOMRenderer {
133
136
  this.onSetText(patch.id!, patch.text!);
134
137
  break;
135
138
  case "insert":
136
- this.onInsert((patch as any).parent_id!, patch.id!, (patch as any).before_id);
139
+ this.onInsert(patch.parentId!, patch.id!, patch.beforeId);
137
140
  break;
138
141
  case "move":
139
- this.onMove((patch as any).parent_id!, patch.id!, (patch as any).before_id);
142
+ this.onMove(patch.parentId!, patch.id!, patch.beforeId);
140
143
  break;
141
144
  case "remove":
142
145
  this.onRemove(patch.id!);
@@ -160,7 +163,7 @@ export class DOMRenderer {
160
163
  fallback.dataset.hypenType = elementType.toLowerCase();
161
164
  fallback.style.display = "contents"; // Make container transparent in layout
162
165
  element = fallback;
163
- console.log(`[Renderer] Unknown component "${elementType}" - using transparent container`);
166
+ log.debug(`Unknown component "${elementType}" - using transparent container`);
164
167
  }
165
168
 
166
169
  element.dataset.hypenType = elementType.toLowerCase();
@@ -194,7 +197,7 @@ export class DOMRenderer {
194
197
  if (elementType === "input") {
195
198
  const inputEl = element as HTMLInputElement;
196
199
  inputEl.value = String(value);
197
- console.log(`[Renderer] Updated input value: "${value}"`);
200
+ log.debug(`Updated input value: "${value}"`);
198
201
  return;
199
202
  }
200
203
 
@@ -217,7 +220,7 @@ export class DOMRenderer {
217
220
  // If current template isn't a template, keep it in sync.
218
221
  element.dataset.textTemplate = nextText;
219
222
  }
220
- console.log(`[Renderer] Updated text content: "${value}"`);
223
+ log.debug(`Updated text content: "${value}"`);
221
224
  return;
222
225
  }
223
226
 
@@ -242,7 +245,7 @@ export class DOMRenderer {
242
245
  const parent = parentId === "root" ? this.container : this.nodes.get(parentId);
243
246
  const child = this.nodes.get(id);
244
247
 
245
- console.log(`[Renderer] Inserting ${id} into ${parentId}`, {
248
+ log.debug(`Inserting ${id} into ${parentId}`, {
246
249
  parent: parent ? `${parent.tagName}#${parent.id || 'no-id'}` : 'null',
247
250
  child: child ? `${child.tagName}#${child.id || 'no-id'}` : 'null',
248
251
  childText: child?.textContent?.substring(0, 20)