@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.
Files changed (195) hide show
  1. package/dist/chunk-2s02mkzs.js +32 -0
  2. package/dist/chunk-2s02mkzs.js.map +9 -0
  3. package/dist/src/canvas/accessibility.js +152 -0
  4. package/dist/src/canvas/accessibility.js.map +10 -0
  5. package/dist/src/canvas/events.js +198 -0
  6. package/dist/src/canvas/events.js.map +10 -0
  7. package/dist/src/canvas/index.js +28 -0
  8. package/dist/src/canvas/index.js.map +9 -0
  9. package/dist/src/canvas/input.js +132 -0
  10. package/dist/src/canvas/input.js.map +10 -0
  11. package/dist/src/canvas/layout.js +309 -0
  12. package/dist/src/canvas/layout.js.map +10 -0
  13. package/dist/src/canvas/paint.js +878 -0
  14. package/dist/src/canvas/paint.js.map +10 -0
  15. package/dist/src/canvas/renderer.js +276 -0
  16. package/dist/src/canvas/renderer.js.map +10 -0
  17. package/dist/src/canvas/text.js +118 -0
  18. package/dist/src/canvas/text.js.map +10 -0
  19. package/dist/src/canvas/types.js +2 -0
  20. package/dist/src/canvas/types.js.map +9 -0
  21. package/dist/src/canvas/utils.js +139 -0
  22. package/dist/src/canvas/utils.js.map +10 -0
  23. package/dist/src/dom/applicators/advanced-layout.js +111 -0
  24. package/dist/src/dom/applicators/advanced-layout.js.map +10 -0
  25. package/dist/src/dom/applicators/background.js +54 -0
  26. package/dist/src/dom/applicators/background.js.map +10 -0
  27. package/dist/src/dom/applicators/border.js +33 -0
  28. package/dist/src/dom/applicators/border.js.map +10 -0
  29. package/dist/src/dom/applicators/color.js +36 -0
  30. package/dist/src/dom/applicators/color.js.map +10 -0
  31. package/dist/src/dom/applicators/display.js +57 -0
  32. package/dist/src/dom/applicators/display.js.map +10 -0
  33. package/dist/src/dom/applicators/effects.js +89 -0
  34. package/dist/src/dom/applicators/effects.js.map +10 -0
  35. package/dist/src/dom/applicators/events.js +518 -0
  36. package/dist/src/dom/applicators/events.js.map +10 -0
  37. package/dist/src/dom/applicators/font.js +39 -0
  38. package/dist/src/dom/applicators/font.js.map +10 -0
  39. package/dist/src/dom/applicators/index.js +296 -0
  40. package/dist/src/dom/applicators/index.js.map +10 -0
  41. package/dist/src/dom/applicators/layout.js +86 -0
  42. package/dist/src/dom/applicators/layout.js.map +10 -0
  43. package/dist/src/dom/applicators/margin.js +32 -0
  44. package/dist/src/dom/applicators/margin.js.map +10 -0
  45. package/dist/src/dom/applicators/padding.js +35 -0
  46. package/dist/src/dom/applicators/padding.js.map +10 -0
  47. package/dist/src/dom/applicators/size.js +42 -0
  48. package/dist/src/dom/applicators/size.js.map +10 -0
  49. package/dist/src/dom/applicators/transform.js +92 -0
  50. package/dist/src/dom/applicators/transform.js.map +10 -0
  51. package/dist/src/dom/applicators/transition.js +66 -0
  52. package/dist/src/dom/applicators/transition.js.map +10 -0
  53. package/dist/src/dom/applicators/typography.js +87 -0
  54. package/dist/src/dom/applicators/typography.js.map +10 -0
  55. package/dist/src/dom/canvas/index.js +50 -0
  56. package/dist/src/dom/canvas/index.js.map +10 -0
  57. package/dist/src/dom/components/audio.js +48 -0
  58. package/dist/src/dom/components/audio.js.map +10 -0
  59. package/dist/src/dom/components/avatar.js +58 -0
  60. package/dist/src/dom/components/avatar.js.map +10 -0
  61. package/dist/src/dom/components/badge.js +55 -0
  62. package/dist/src/dom/components/badge.js.map +10 -0
  63. package/dist/src/dom/components/button.js +29 -0
  64. package/dist/src/dom/components/button.js.map +10 -0
  65. package/dist/src/dom/components/card.js +33 -0
  66. package/dist/src/dom/components/card.js.map +10 -0
  67. package/dist/src/dom/components/center.js +32 -0
  68. package/dist/src/dom/components/center.js.map +10 -0
  69. package/dist/src/dom/components/checkbox.js +54 -0
  70. package/dist/src/dom/components/checkbox.js.map +10 -0
  71. package/dist/src/dom/components/column.js +31 -0
  72. package/dist/src/dom/components/column.js.map +10 -0
  73. package/dist/src/dom/components/container.js +29 -0
  74. package/dist/src/dom/components/container.js.map +10 -0
  75. package/dist/src/dom/components/divider.js +45 -0
  76. package/dist/src/dom/components/divider.js.map +10 -0
  77. package/dist/src/dom/components/grid.js +44 -0
  78. package/dist/src/dom/components/grid.js.map +10 -0
  79. package/dist/src/dom/components/heading.js +47 -0
  80. package/dist/src/dom/components/heading.js.map +10 -0
  81. package/dist/src/dom/components/image.js +39 -0
  82. package/dist/src/dom/components/image.js.map +10 -0
  83. package/dist/src/dom/components/index.js +217 -0
  84. package/dist/src/dom/components/index.js.map +10 -0
  85. package/dist/src/dom/components/input.js +41 -0
  86. package/dist/src/dom/components/input.js.map +10 -0
  87. package/dist/src/dom/components/link.js +42 -0
  88. package/dist/src/dom/components/link.js.map +10 -0
  89. package/dist/src/dom/components/list.js +42 -0
  90. package/dist/src/dom/components/list.js.map +10 -0
  91. package/dist/src/dom/components/paragraph.js +35 -0
  92. package/dist/src/dom/components/paragraph.js.map +10 -0
  93. package/dist/src/dom/components/progressbar.js +57 -0
  94. package/dist/src/dom/components/progressbar.js.map +10 -0
  95. package/dist/src/dom/components/route.js +44 -0
  96. package/dist/src/dom/components/route.js.map +10 -0
  97. package/dist/src/dom/components/router.js +33 -0
  98. package/dist/src/dom/components/router.js.map +10 -0
  99. package/dist/src/dom/components/row.js +31 -0
  100. package/dist/src/dom/components/row.js.map +10 -0
  101. package/dist/src/dom/components/select.js +57 -0
  102. package/dist/src/dom/components/select.js.map +10 -0
  103. package/dist/src/dom/components/slider.js +48 -0
  104. package/dist/src/dom/components/slider.js.map +10 -0
  105. package/dist/src/dom/components/spacer.js +30 -0
  106. package/dist/src/dom/components/spacer.js.map +10 -0
  107. package/dist/src/dom/components/spinner.js +65 -0
  108. package/dist/src/dom/components/spinner.js.map +10 -0
  109. package/dist/src/dom/components/stack.js +45 -0
  110. package/dist/src/dom/components/stack.js.map +10 -0
  111. package/dist/src/dom/components/switch.js +83 -0
  112. package/dist/src/dom/components/switch.js.map +10 -0
  113. package/dist/src/dom/components/text.js +37 -0
  114. package/dist/src/dom/components/text.js.map +10 -0
  115. package/dist/src/dom/components/textarea.js +51 -0
  116. package/dist/src/dom/components/textarea.js.map +10 -0
  117. package/dist/src/dom/components/video.js +51 -0
  118. package/dist/src/dom/components/video.js.map +10 -0
  119. package/dist/src/dom/debug.js +170 -0
  120. package/dist/src/dom/debug.js.map +10 -0
  121. package/dist/src/dom/events.js +112 -0
  122. package/dist/src/dom/events.js.map +10 -0
  123. package/dist/src/dom/index.js +73 -0
  124. package/dist/src/dom/index.js.map +9 -0
  125. package/dist/src/dom/renderer.js +277 -0
  126. package/dist/src/dom/renderer.js.map +10 -0
  127. package/dist/src/index.js +89 -0
  128. package/dist/src/index.js.map +9 -0
  129. package/package.json +84 -0
  130. package/src/canvas/QUICKSTART.md +421 -0
  131. package/src/canvas/README.md +376 -0
  132. package/src/canvas/accessibility.ts +218 -0
  133. package/src/canvas/events.ts +307 -0
  134. package/src/canvas/index.ts +35 -0
  135. package/src/canvas/input.ts +210 -0
  136. package/src/canvas/layout.ts +401 -0
  137. package/src/canvas/paint.ts +1321 -0
  138. package/src/canvas/renderer.ts +422 -0
  139. package/src/canvas/text.ts +182 -0
  140. package/src/canvas/types.ts +137 -0
  141. package/src/canvas/utils.ts +218 -0
  142. package/src/dom/README.md +265 -0
  143. package/src/dom/applicators/advanced-layout.ts +128 -0
  144. package/src/dom/applicators/background.ts +50 -0
  145. package/src/dom/applicators/border.ts +19 -0
  146. package/src/dom/applicators/color.ts +23 -0
  147. package/src/dom/applicators/display.ts +54 -0
  148. package/src/dom/applicators/effects.ts +97 -0
  149. package/src/dom/applicators/events.ts +689 -0
  150. package/src/dom/applicators/font.ts +27 -0
  151. package/src/dom/applicators/index.ts +354 -0
  152. package/src/dom/applicators/layout.ts +92 -0
  153. package/src/dom/applicators/margin.ts +18 -0
  154. package/src/dom/applicators/padding.ts +18 -0
  155. package/src/dom/applicators/size.ts +31 -0
  156. package/src/dom/applicators/transform.ts +93 -0
  157. package/src/dom/applicators/transition.ts +65 -0
  158. package/src/dom/applicators/typography.ts +91 -0
  159. package/src/dom/canvas/index.ts +60 -0
  160. package/src/dom/components/audio.ts +45 -0
  161. package/src/dom/components/avatar.ts +49 -0
  162. package/src/dom/components/badge.ts +45 -0
  163. package/src/dom/components/button.ts +13 -0
  164. package/src/dom/components/card.ts +19 -0
  165. package/src/dom/components/center.ts +16 -0
  166. package/src/dom/components/checkbox.ts +54 -0
  167. package/src/dom/components/column.ts +15 -0
  168. package/src/dom/components/container.ts +13 -0
  169. package/src/dom/components/divider.ts +37 -0
  170. package/src/dom/components/grid.ts +40 -0
  171. package/src/dom/components/heading.ts +41 -0
  172. package/src/dom/components/image.ts +27 -0
  173. package/src/dom/components/index.ts +115 -0
  174. package/src/dom/components/input.ts +29 -0
  175. package/src/dom/components/link.ts +35 -0
  176. package/src/dom/components/list.ts +30 -0
  177. package/src/dom/components/paragraph.ts +23 -0
  178. package/src/dom/components/progressbar.ts +51 -0
  179. package/src/dom/components/route.ts +37 -0
  180. package/src/dom/components/router.ts +22 -0
  181. package/src/dom/components/row.ts +15 -0
  182. package/src/dom/components/select.ts +56 -0
  183. package/src/dom/components/slider.ts +45 -0
  184. package/src/dom/components/spacer.ts +16 -0
  185. package/src/dom/components/spinner.ts +60 -0
  186. package/src/dom/components/stack.ts +34 -0
  187. package/src/dom/components/switch.ts +86 -0
  188. package/src/dom/components/text.ts +24 -0
  189. package/src/dom/components/textarea.ts +50 -0
  190. package/src/dom/components/video.ts +50 -0
  191. package/src/dom/debug.ts +247 -0
  192. package/src/dom/events.ts +168 -0
  193. package/src/dom/index.ts +11 -0
  194. package/src/dom/renderer.ts +327 -0
  195. package/src/index.ts +56 -0
@@ -0,0 +1,878 @@
1
+ import {
2
+ renderText
3
+ } from "./text.js";
4
+ import"./utils.js";
5
+ import"../../chunk-2s02mkzs.js";
6
+
7
+ // src/canvas/paint.ts
8
+ var customPainters = new Map;
9
+ function registerPainter(type, painter) {
10
+ customPainters.set(type.toLowerCase(), painter);
11
+ }
12
+ function paintNode(ctx, node) {
13
+ if (!node.visible || !node.layout)
14
+ return;
15
+ ctx.save();
16
+ applyTransforms(ctx, node);
17
+ if (node.opacity < 1) {
18
+ ctx.globalAlpha = node.opacity;
19
+ }
20
+ const customPainter = customPainters.get(node.type.toLowerCase());
21
+ if (customPainter) {
22
+ customPainter(ctx, node);
23
+ ctx.restore();
24
+ return;
25
+ }
26
+ switch (node.type.toLowerCase()) {
27
+ case "column":
28
+ case "row":
29
+ case "stack":
30
+ paintContainer(ctx, node);
31
+ break;
32
+ case "text":
33
+ paintText(ctx, node);
34
+ break;
35
+ case "button":
36
+ paintButton(ctx, node);
37
+ break;
38
+ case "input":
39
+ paintInput(ctx, node);
40
+ break;
41
+ case "image":
42
+ paintImage(ctx, node);
43
+ break;
44
+ case "spacer":
45
+ break;
46
+ case "divider":
47
+ case "separator":
48
+ paintDivider(ctx, node);
49
+ break;
50
+ case "checkbox":
51
+ paintCheckbox(ctx, node);
52
+ break;
53
+ case "radio":
54
+ paintRadio(ctx, node);
55
+ break;
56
+ case "switch":
57
+ case "toggle":
58
+ paintSwitch(ctx, node);
59
+ break;
60
+ case "slider":
61
+ paintSlider(ctx, node);
62
+ break;
63
+ case "progress":
64
+ case "progressbar":
65
+ paintProgress(ctx, node);
66
+ break;
67
+ case "spinner":
68
+ case "loading":
69
+ paintSpinner(ctx, node);
70
+ break;
71
+ case "card":
72
+ paintCard(ctx, node);
73
+ break;
74
+ case "badge":
75
+ paintBadge(ctx, node);
76
+ break;
77
+ case "avatar":
78
+ paintAvatar(ctx, node);
79
+ break;
80
+ case "icon":
81
+ paintIcon(ctx, node);
82
+ break;
83
+ case "link":
84
+ paintLink(ctx, node);
85
+ break;
86
+ case "container":
87
+ case "box":
88
+ paintContainer(ctx, node);
89
+ break;
90
+ default:
91
+ paintContainer(ctx, node);
92
+ }
93
+ ctx.restore();
94
+ for (const child of node.children) {
95
+ paintNode(ctx, child);
96
+ }
97
+ if (node._needsRestore) {
98
+ ctx.restore();
99
+ delete node._needsRestore;
100
+ }
101
+ }
102
+ function paintContainer(ctx, node) {
103
+ const layout = node.layout;
104
+ const props = node.props;
105
+ const x = layout.x;
106
+ const y = layout.y;
107
+ const width = layout.width;
108
+ const height = layout.height;
109
+ const radius = layout.border.radius;
110
+ const shadow = props.shadow || props.boxShadow;
111
+ if (shadow) {
112
+ applyShadow(ctx, shadow);
113
+ }
114
+ const backgroundColor = props.backgroundColor || props.background;
115
+ if (backgroundColor) {
116
+ if (typeof backgroundColor === "string" && backgroundColor.includes("gradient")) {
117
+ ctx.fillStyle = parseGradient(ctx, backgroundColor, x, y, width, height);
118
+ } else {
119
+ ctx.fillStyle = backgroundColor;
120
+ }
121
+ if (radius > 0) {
122
+ drawRoundedRect(ctx, x, y, width, height, radius);
123
+ ctx.fill();
124
+ } else {
125
+ ctx.fillRect(x, y, width, height);
126
+ }
127
+ }
128
+ if (shadow) {
129
+ ctx.shadowColor = "transparent";
130
+ ctx.shadowBlur = 0;
131
+ ctx.shadowOffsetX = 0;
132
+ ctx.shadowOffsetY = 0;
133
+ }
134
+ if (layout.border.width > 0 && layout.border.color !== "transparent") {
135
+ ctx.strokeStyle = layout.border.color;
136
+ ctx.lineWidth = layout.border.width;
137
+ if (radius > 0) {
138
+ drawRoundedRect(ctx, x, y, width, height, radius);
139
+ ctx.stroke();
140
+ } else {
141
+ ctx.strokeRect(x, y, width, height);
142
+ }
143
+ }
144
+ const overflow = props.overflow || "visible";
145
+ if (overflow === "hidden" || overflow === "scroll" || overflow === "auto") {
146
+ ctx.save();
147
+ ctx.beginPath();
148
+ if (radius > 0) {
149
+ drawRoundedRect(ctx, x, y, width, height, radius);
150
+ } else {
151
+ ctx.rect(x, y, width, height);
152
+ }
153
+ ctx.clip();
154
+ node._needsRestore = true;
155
+ }
156
+ }
157
+ function paintText(ctx, node) {
158
+ const layout = node.layout;
159
+ const props = node.props;
160
+ let text = String(props[0] || props.text || "");
161
+ const color = props.color || "#000000";
162
+ const fontSize = parseFloat(props.fontSize) || 16;
163
+ const fontWeight = props.fontWeight || "normal";
164
+ const fontFamily = props.fontFamily || "system-ui, sans-serif";
165
+ const textAlign = props.textAlign || "left";
166
+ const lineHeight = parseFloat(props.lineHeight) || fontSize * 1.2;
167
+ const textDecoration = props.textDecoration || "none";
168
+ const textTransform = props.textTransform || "none";
169
+ const letterSpacing = parseFloat(props.letterSpacing) || 0;
170
+ if (textTransform === "uppercase") {
171
+ text = text.toUpperCase();
172
+ } else if (textTransform === "lowercase") {
173
+ text = text.toLowerCase();
174
+ } else if (textTransform === "capitalize") {
175
+ text = text.replace(/\b\w/g, (char) => char.toUpperCase());
176
+ }
177
+ const textShadow = props.textShadow || props.shadow;
178
+ if (textShadow) {
179
+ applyShadow(ctx, textShadow);
180
+ }
181
+ const x = layout.x + layout.contentX;
182
+ const y = layout.y + layout.contentY;
183
+ if (letterSpacing !== 0) {
184
+ ctx.fillStyle = color;
185
+ ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
186
+ ctx.textAlign = "left";
187
+ ctx.textBaseline = "top";
188
+ let currentX = x;
189
+ for (let i = 0;i < text.length; i++) {
190
+ ctx.fillText(text[i], currentX, y);
191
+ currentX += ctx.measureText(text[i]).width;
192
+ if (i < text.length - 1) {
193
+ currentX += letterSpacing;
194
+ }
195
+ }
196
+ if (textDecoration !== "none") {
197
+ applyTextDecoration(ctx, textDecoration, color, x, y, currentX - x, fontSize);
198
+ }
199
+ } else {
200
+ renderText(ctx, text, x, y, layout.contentWidth, layout.contentHeight, {
201
+ color,
202
+ fontSize,
203
+ fontWeight,
204
+ fontFamily,
205
+ textAlign,
206
+ verticalAlign: "top",
207
+ lineHeight
208
+ });
209
+ if (textDecoration !== "none") {
210
+ ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
211
+ const textWidth = ctx.measureText(text).width;
212
+ applyTextDecoration(ctx, textDecoration, color, x, y, textWidth, fontSize);
213
+ }
214
+ }
215
+ if (textShadow) {
216
+ ctx.shadowColor = "transparent";
217
+ ctx.shadowBlur = 0;
218
+ ctx.shadowOffsetX = 0;
219
+ ctx.shadowOffsetY = 0;
220
+ }
221
+ }
222
+ function paintButton(ctx, node) {
223
+ const layout = node.layout;
224
+ const props = node.props;
225
+ const x = layout.x;
226
+ const y = layout.y;
227
+ const width = layout.width;
228
+ const height = layout.height;
229
+ const radius = layout.border.radius || 4;
230
+ const shadow = props.shadow || props.boxShadow;
231
+ if (shadow) {
232
+ applyShadow(ctx, shadow);
233
+ }
234
+ let backgroundColor = props.backgroundColor || "#007bff";
235
+ if (node.hovered) {
236
+ backgroundColor = props.hoverColor || "#0056b3";
237
+ }
238
+ if (node.focused) {
239
+ backgroundColor = props.focusColor || "#004085";
240
+ }
241
+ if (typeof backgroundColor === "string" && backgroundColor.includes("gradient")) {
242
+ ctx.fillStyle = parseGradient(ctx, backgroundColor, x, y, width, height);
243
+ } else {
244
+ ctx.fillStyle = backgroundColor;
245
+ }
246
+ drawRoundedRect(ctx, x, y, width, height, radius);
247
+ ctx.fill();
248
+ if (shadow) {
249
+ ctx.shadowColor = "transparent";
250
+ ctx.shadowBlur = 0;
251
+ ctx.shadowOffsetX = 0;
252
+ ctx.shadowOffsetY = 0;
253
+ }
254
+ if (layout.border.width > 0) {
255
+ ctx.strokeStyle = layout.border.color;
256
+ ctx.lineWidth = layout.border.width;
257
+ drawRoundedRect(ctx, x, y, width, height, radius);
258
+ ctx.stroke();
259
+ }
260
+ for (const child of node.children) {
261
+ paintNode(ctx, child);
262
+ }
263
+ }
264
+ function paintInput(ctx, node) {
265
+ const layout = node.layout;
266
+ const props = node.props;
267
+ const x = layout.x;
268
+ const y = layout.y;
269
+ const width = layout.width;
270
+ const height = layout.height;
271
+ const radius = layout.border.radius || 4;
272
+ ctx.fillStyle = props.backgroundColor || "#ffffff";
273
+ drawRoundedRect(ctx, x, y, width, height, radius);
274
+ ctx.fill();
275
+ const borderColor = node.focused ? "#007bff" : layout.border.color || "#cccccc";
276
+ const borderWidth = node.focused ? 2 : layout.border.width || 1;
277
+ ctx.strokeStyle = borderColor;
278
+ ctx.lineWidth = borderWidth;
279
+ drawRoundedRect(ctx, x, y, width, height, radius);
280
+ ctx.stroke();
281
+ const value = props.value || "";
282
+ const placeholder = props.placeholder || "";
283
+ const text = value || placeholder;
284
+ const textColor = value ? props.color || "#000000" : "#999999";
285
+ if (text) {
286
+ const fontSize = parseFloat(props.fontSize) || 16;
287
+ const fontWeight = props.fontWeight || "normal";
288
+ const fontFamily = props.fontFamily || "system-ui, sans-serif";
289
+ const lineHeight = parseFloat(props.lineHeight) || fontSize * 1.2;
290
+ renderText(ctx, text, layout.x + layout.contentX, layout.y + layout.contentY, layout.contentWidth, layout.contentHeight, {
291
+ color: textColor,
292
+ fontSize,
293
+ fontWeight,
294
+ fontFamily,
295
+ textAlign: "left",
296
+ verticalAlign: "middle",
297
+ lineHeight
298
+ });
299
+ }
300
+ }
301
+ function paintImage(ctx, node) {
302
+ const layout = node.layout;
303
+ const props = node.props;
304
+ const src = props.src || props[0];
305
+ if (!src)
306
+ return;
307
+ const x = layout.x;
308
+ const y = layout.y;
309
+ const width = layout.width;
310
+ const height = layout.height;
311
+ ctx.fillStyle = "#e0e0e0";
312
+ ctx.fillRect(x, y, width, height);
313
+ ctx.strokeStyle = "#999999";
314
+ ctx.lineWidth = 1;
315
+ ctx.strokeRect(x, y, width, height);
316
+ ctx.fillStyle = "#666666";
317
+ ctx.font = "14px sans-serif";
318
+ ctx.textAlign = "center";
319
+ ctx.textBaseline = "middle";
320
+ ctx.fillText("IMG", x + width / 2, y + height / 2);
321
+ }
322
+ function drawRoundedRect(ctx, x, y, width, height, radius) {
323
+ if (radius <= 0) {
324
+ ctx.rect(x, y, width, height);
325
+ return;
326
+ }
327
+ ctx.beginPath();
328
+ ctx.moveTo(x + radius, y);
329
+ ctx.lineTo(x + width - radius, y);
330
+ ctx.arcTo(x + width, y, x + width, y + radius, radius);
331
+ ctx.lineTo(x + width, y + height - radius);
332
+ ctx.arcTo(x + width, y + height, x + width - radius, y + height, radius);
333
+ ctx.lineTo(x + radius, y + height);
334
+ ctx.arcTo(x, y + height, x, y + height - radius, radius);
335
+ ctx.lineTo(x, y + radius);
336
+ ctx.arcTo(x, y, x + radius, y, radius);
337
+ ctx.closePath();
338
+ }
339
+ function applyShadow(ctx, shadow) {
340
+ if (typeof shadow === "string") {
341
+ const parts = shadow.trim().split(/\s+/);
342
+ if (parts.length >= 3) {
343
+ const offsetX = parseFloat(parts[0]);
344
+ const offsetY = parseFloat(parts[1]);
345
+ const blur = parseFloat(parts[2]);
346
+ const color = parts.slice(3).join(" ") || "rgba(0,0,0,0.3)";
347
+ ctx.shadowOffsetX = offsetX;
348
+ ctx.shadowOffsetY = offsetY;
349
+ ctx.shadowBlur = blur;
350
+ ctx.shadowColor = color;
351
+ }
352
+ } else if (typeof shadow === "object") {
353
+ ctx.shadowOffsetX = shadow.offsetX || 0;
354
+ ctx.shadowOffsetY = shadow.offsetY || 0;
355
+ ctx.shadowBlur = shadow.blur || 0;
356
+ ctx.shadowColor = shadow.color || "rgba(0,0,0,0.3)";
357
+ }
358
+ }
359
+ function parseGradient(ctx, gradientStr, x, y, width, height) {
360
+ if (gradientStr.startsWith("linear-gradient")) {
361
+ const match = gradientStr.match(/linear-gradient\((.*)\)/);
362
+ if (!match)
363
+ return gradientStr;
364
+ const parts = match[1].split(",").map((s) => s.trim());
365
+ let x0 = x, y0 = y, x1 = x, y1 = y + height;
366
+ let colorStart = 0;
367
+ if (parts[0].includes("deg") || parts[0].includes("to ")) {
368
+ colorStart = 1;
369
+ const direction = parts[0];
370
+ if (direction.includes("to right") || direction === "90deg") {
371
+ x1 = x + width;
372
+ y1 = y;
373
+ } else if (direction.includes("to left") || direction === "270deg") {
374
+ x0 = x + width;
375
+ x1 = x;
376
+ y0 = y;
377
+ y1 = y;
378
+ } else if (direction.includes("to top") || direction === "0deg") {
379
+ y0 = y + height;
380
+ y1 = y;
381
+ }
382
+ }
383
+ const gradient = ctx.createLinearGradient(x0, y0, x1, y1);
384
+ const colors = parts.slice(colorStart);
385
+ colors.forEach((color, i) => {
386
+ const stop = i / (colors.length - 1);
387
+ gradient.addColorStop(stop, color.trim());
388
+ });
389
+ return gradient;
390
+ } else if (gradientStr.startsWith("radial-gradient")) {
391
+ const match = gradientStr.match(/radial-gradient\((.*)\)/);
392
+ if (!match)
393
+ return gradientStr;
394
+ const parts = match[1].split(",").map((s) => s.trim());
395
+ const centerX = x + width / 2;
396
+ const centerY = y + height / 2;
397
+ const radius = Math.max(width, height) / 2;
398
+ const gradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, radius);
399
+ parts.forEach((color, i) => {
400
+ const stop = i / (parts.length - 1);
401
+ gradient.addColorStop(stop, color.trim());
402
+ });
403
+ return gradient;
404
+ }
405
+ return gradientStr;
406
+ }
407
+ function applyTransforms(ctx, node) {
408
+ const props = node.props;
409
+ const layout = node.layout;
410
+ const originX = parseFloat(props.transformOriginX) || 0.5;
411
+ const originY = parseFloat(props.transformOriginY) || 0.5;
412
+ const centerX = layout.x + layout.width * originX;
413
+ const centerY = layout.y + layout.height * originY;
414
+ const translateX = parseFloat(props.translateX) || 0;
415
+ const translateY = parseFloat(props.translateY) || 0;
416
+ const rotate = parseFloat(props.rotate) || 0;
417
+ const scaleX = parseFloat(props.scaleX) || parseFloat(props.scale) || 1;
418
+ const scaleY = parseFloat(props.scaleY) || parseFloat(props.scale) || 1;
419
+ const skewX = parseFloat(props.skewX) || parseFloat(props.skew) || 0;
420
+ const skewY = parseFloat(props.skewY) || 0;
421
+ if (translateX !== 0 || translateY !== 0 || rotate !== 0 || scaleX !== 1 || scaleY !== 1 || skewX !== 0 || skewY !== 0) {
422
+ ctx.translate(centerX, centerY);
423
+ if (scaleX !== 1 || scaleY !== 1) {
424
+ ctx.scale(scaleX, scaleY);
425
+ }
426
+ if (rotate !== 0) {
427
+ ctx.rotate(rotate * Math.PI / 180);
428
+ }
429
+ if (skewX !== 0 || skewY !== 0) {
430
+ const skewXRad = skewX * Math.PI / 180;
431
+ const skewYRad = skewY * Math.PI / 180;
432
+ ctx.transform(1, Math.tan(skewYRad), Math.tan(skewXRad), 1, 0, 0);
433
+ }
434
+ ctx.translate(-centerX + translateX, -centerY + translateY);
435
+ }
436
+ if (props.transform && typeof props.transform === "string") {
437
+ parseTransformString(ctx, props.transform, centerX, centerY);
438
+ }
439
+ }
440
+ function parseTransformString(ctx, transformStr, originX, originY) {
441
+ const transforms = transformStr.match(/(\w+)\(([^)]+)\)/g);
442
+ if (!transforms)
443
+ return;
444
+ ctx.translate(originX, originY);
445
+ for (const transform of transforms) {
446
+ const match = transform.match(/(\w+)\(([^)]+)\)/);
447
+ if (!match)
448
+ continue;
449
+ const [, func, args] = match;
450
+ const values = args.split(",").map((v) => parseFloat(v.trim()));
451
+ switch (func.toLowerCase()) {
452
+ case "translate":
453
+ ctx.translate(values[0] || 0, values[1] || 0);
454
+ break;
455
+ case "rotate":
456
+ ctx.rotate(values[0] * Math.PI / 180);
457
+ break;
458
+ case "scale":
459
+ ctx.scale(values[0] || 1, values[1] || values[0] || 1);
460
+ break;
461
+ }
462
+ }
463
+ ctx.translate(-originX, -originY);
464
+ }
465
+ function paintDivider(ctx, node) {
466
+ const layout = node.layout;
467
+ const props = node.props;
468
+ const orientation = props.orientation || "horizontal";
469
+ const color = props.color || props.backgroundColor || "#e0e0e0";
470
+ const thickness = parseFloat(props.thickness) || 1;
471
+ ctx.strokeStyle = color;
472
+ ctx.lineWidth = thickness;
473
+ ctx.beginPath();
474
+ if (orientation === "vertical") {
475
+ const x = layout.x + layout.width / 2;
476
+ ctx.moveTo(x, layout.y);
477
+ ctx.lineTo(x, layout.y + layout.height);
478
+ } else {
479
+ const y = layout.y + layout.height / 2;
480
+ ctx.moveTo(layout.x, y);
481
+ ctx.lineTo(layout.x + layout.width, y);
482
+ }
483
+ ctx.stroke();
484
+ }
485
+ function paintCheckbox(ctx, node) {
486
+ const layout = node.layout;
487
+ const props = node.props;
488
+ const size = Math.min(layout.width, layout.height);
489
+ const x = layout.x;
490
+ const y = layout.y;
491
+ const checkedValue = props.checked !== undefined ? props.checked : props.value;
492
+ const checked = checkedValue === true || checkedValue === "true" || checkedValue !== false && checkedValue !== "false" && !!checkedValue;
493
+ const radius = parseFloat(props.borderRadius) || 2;
494
+ const bgColor = checked ? props.checkedColor || "#007bff" : props.backgroundColor || "#ffffff";
495
+ ctx.fillStyle = bgColor;
496
+ drawRoundedRect(ctx, x, y, size, size, radius);
497
+ ctx.fill();
498
+ const borderColor = checked ? props.checkedColor || "#007bff" : props.borderColor || "#cccccc";
499
+ ctx.strokeStyle = borderColor;
500
+ ctx.lineWidth = node.focused ? 2 : 1;
501
+ drawRoundedRect(ctx, x, y, size, size, radius);
502
+ ctx.stroke();
503
+ if (checked) {
504
+ ctx.strokeStyle = props.checkColor || "#ffffff";
505
+ ctx.lineWidth = 2;
506
+ ctx.lineCap = "round";
507
+ ctx.lineJoin = "round";
508
+ const padding = size * 0.25;
509
+ ctx.beginPath();
510
+ ctx.moveTo(x + padding, y + size / 2);
511
+ ctx.lineTo(x + size * 0.4, y + size - padding);
512
+ ctx.lineTo(x + size - padding, y + padding);
513
+ ctx.stroke();
514
+ }
515
+ }
516
+ function paintRadio(ctx, node) {
517
+ const layout = node.layout;
518
+ const props = node.props;
519
+ const size = Math.min(layout.width, layout.height);
520
+ const centerX = layout.x + size / 2;
521
+ const centerY = layout.y + size / 2;
522
+ const radius = size / 2;
523
+ const checkedValue = props.checked !== undefined ? props.checked : props.value;
524
+ const checked = checkedValue === true || checkedValue === "true" || checkedValue !== false && checkedValue !== "false" && !!checkedValue;
525
+ const bgColor = props.backgroundColor || "#ffffff";
526
+ ctx.fillStyle = bgColor;
527
+ ctx.beginPath();
528
+ ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
529
+ ctx.fill();
530
+ const borderColor = checked ? props.checkedColor || "#007bff" : props.borderColor || "#cccccc";
531
+ ctx.strokeStyle = borderColor;
532
+ ctx.lineWidth = node.focused ? 2 : 1;
533
+ ctx.beginPath();
534
+ ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
535
+ ctx.stroke();
536
+ if (checked) {
537
+ ctx.fillStyle = props.checkedColor || "#007bff";
538
+ ctx.beginPath();
539
+ ctx.arc(centerX, centerY, radius * 0.5, 0, Math.PI * 2);
540
+ ctx.fill();
541
+ }
542
+ }
543
+ function paintSwitch(ctx, node) {
544
+ const layout = node.layout;
545
+ const props = node.props;
546
+ const width = layout.width;
547
+ const height = layout.height;
548
+ const x = layout.x;
549
+ const y = layout.y;
550
+ const checkedValue = props.checked !== undefined ? props.checked : props.value;
551
+ const checked = checkedValue === true || checkedValue === "true" || checkedValue !== false && checkedValue !== "false" && !!checkedValue;
552
+ const radius = height / 2;
553
+ const trackColor = checked ? props.checkedColor || "#4caf50" : props.backgroundColor || "#cccccc";
554
+ ctx.fillStyle = trackColor;
555
+ drawRoundedRect(ctx, x, y, width, height, radius);
556
+ ctx.fill();
557
+ const thumbRadius = radius * 0.8;
558
+ const thumbX = checked ? x + width - radius : x + radius;
559
+ const thumbY = y + radius;
560
+ ctx.fillStyle = props.thumbColor || "#ffffff";
561
+ ctx.beginPath();
562
+ ctx.arc(thumbX, thumbY, thumbRadius, 0, Math.PI * 2);
563
+ ctx.fill();
564
+ if (props.shadow !== false) {
565
+ ctx.shadowColor = "rgba(0,0,0,0.2)";
566
+ ctx.shadowBlur = 2;
567
+ ctx.shadowOffsetY = 1;
568
+ ctx.beginPath();
569
+ ctx.arc(thumbX, thumbY, thumbRadius, 0, Math.PI * 2);
570
+ ctx.fill();
571
+ ctx.shadowColor = "transparent";
572
+ ctx.shadowBlur = 0;
573
+ ctx.shadowOffsetY = 0;
574
+ }
575
+ }
576
+ function paintSlider(ctx, node) {
577
+ const layout = node.layout;
578
+ const props = node.props;
579
+ const width = layout.width;
580
+ const height = layout.height;
581
+ const x = layout.x;
582
+ const y = layout.y;
583
+ const min = parseFloat(props.min) || 0;
584
+ const max = parseFloat(props.max) || 100;
585
+ const value = parseFloat(props.value) || min;
586
+ const percentage = (value - min) / (max - min);
587
+ const trackHeight = parseFloat(props.trackHeight) || 4;
588
+ const thumbSize = parseFloat(props.thumbSize) || 16;
589
+ const trackY = y + (height - trackHeight) / 2;
590
+ ctx.fillStyle = props.trackColor || "#e0e0e0";
591
+ drawRoundedRect(ctx, x, trackY, width, trackHeight, trackHeight / 2);
592
+ ctx.fill();
593
+ const fillWidth = width * percentage;
594
+ const fillColor = props.fillColor || props.color || "#007bff";
595
+ if (typeof fillColor === "string" && fillColor.includes("gradient")) {
596
+ ctx.fillStyle = parseGradient(ctx, fillColor, x, trackY, fillWidth, trackHeight);
597
+ } else {
598
+ ctx.fillStyle = fillColor;
599
+ }
600
+ drawRoundedRect(ctx, x, trackY, fillWidth, trackHeight, trackHeight / 2);
601
+ ctx.fill();
602
+ const thumbX = x + fillWidth;
603
+ const thumbY = y + height / 2;
604
+ ctx.fillStyle = props.thumbColor || "#007bff";
605
+ ctx.beginPath();
606
+ ctx.arc(thumbX, thumbY, thumbSize / 2, 0, Math.PI * 2);
607
+ ctx.fill();
608
+ ctx.strokeStyle = "#ffffff";
609
+ ctx.lineWidth = 2;
610
+ ctx.beginPath();
611
+ ctx.arc(thumbX, thumbY, thumbSize / 2, 0, Math.PI * 2);
612
+ ctx.stroke();
613
+ }
614
+ function paintProgress(ctx, node) {
615
+ const layout = node.layout;
616
+ const props = node.props;
617
+ const width = layout.width;
618
+ const height = layout.height;
619
+ const x = layout.x;
620
+ const y = layout.y;
621
+ const min = parseFloat(props.min) || 0;
622
+ const max = parseFloat(props.max) || 100;
623
+ const value = parseFloat(props.value) || 0;
624
+ const percentage = Math.min(Math.max((value - min) / (max - min), 0), 1);
625
+ const radius = layout.border.radius || height / 2;
626
+ ctx.fillStyle = props.backgroundColor || "#e0e0e0";
627
+ drawRoundedRect(ctx, x, y, width, height, radius);
628
+ ctx.fill();
629
+ if (percentage > 0) {
630
+ const fillWidth = width * percentage;
631
+ const fillColor = props.fillColor || props.color || "#007bff";
632
+ if (typeof fillColor === "string" && fillColor.includes("gradient")) {
633
+ ctx.fillStyle = parseGradient(ctx, fillColor, x, y, fillWidth, height);
634
+ } else {
635
+ ctx.fillStyle = fillColor;
636
+ }
637
+ drawRoundedRect(ctx, x, y, fillWidth, height, radius);
638
+ ctx.fill();
639
+ }
640
+ if (props.showLabel) {
641
+ const label = props.label || `${Math.round(percentage * 100)}%`;
642
+ ctx.fillStyle = props.labelColor || "#ffffff";
643
+ ctx.font = `${props.fontSize || 12}px ${props.fontFamily || "sans-serif"}`;
644
+ ctx.textAlign = "center";
645
+ ctx.textBaseline = "middle";
646
+ ctx.fillText(label, x + width / 2, y + height / 2);
647
+ }
648
+ }
649
+ function paintSpinner(ctx, node) {
650
+ const layout = node.layout;
651
+ const props = node.props;
652
+ const size = Math.min(layout.width, layout.height);
653
+ const centerX = layout.x + size / 2;
654
+ const centerY = layout.y + size / 2;
655
+ const radius = size / 2 - 4;
656
+ const thickness = parseFloat(props.thickness) || 4;
657
+ const color = props.color || "#007bff";
658
+ const rotation = Date.now() / 1000 * Math.PI;
659
+ ctx.strokeStyle = color;
660
+ ctx.lineWidth = thickness;
661
+ ctx.lineCap = "round";
662
+ ctx.beginPath();
663
+ ctx.arc(centerX, centerY, radius, rotation, rotation + Math.PI * 1.5);
664
+ ctx.stroke();
665
+ ctx.globalAlpha = 0.3;
666
+ ctx.beginPath();
667
+ ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
668
+ ctx.stroke();
669
+ ctx.globalAlpha = 1;
670
+ }
671
+ function paintCard(ctx, node) {
672
+ const layout = node.layout;
673
+ const props = node.props;
674
+ const x = layout.x;
675
+ const y = layout.y;
676
+ const width = layout.width;
677
+ const height = layout.height;
678
+ const radius = layout.border.radius || 8;
679
+ const shadow = props.shadow || props.boxShadow || "0 2 8 rgba(0,0,0,0.1)";
680
+ applyShadow(ctx, shadow);
681
+ const backgroundColor = props.backgroundColor || "#ffffff";
682
+ if (typeof backgroundColor === "string" && backgroundColor.includes("gradient")) {
683
+ ctx.fillStyle = parseGradient(ctx, backgroundColor, x, y, width, height);
684
+ } else {
685
+ ctx.fillStyle = backgroundColor;
686
+ }
687
+ drawRoundedRect(ctx, x, y, width, height, radius);
688
+ ctx.fill();
689
+ ctx.shadowColor = "transparent";
690
+ ctx.shadowBlur = 0;
691
+ ctx.shadowOffsetX = 0;
692
+ ctx.shadowOffsetY = 0;
693
+ if (layout.border.width > 0) {
694
+ ctx.strokeStyle = layout.border.color;
695
+ ctx.lineWidth = layout.border.width;
696
+ drawRoundedRect(ctx, x, y, width, height, radius);
697
+ ctx.stroke();
698
+ }
699
+ for (const child of node.children) {
700
+ paintNode(ctx, child);
701
+ }
702
+ }
703
+ function paintBadge(ctx, node) {
704
+ const layout = node.layout;
705
+ const props = node.props;
706
+ const x = layout.x;
707
+ const y = layout.y;
708
+ const width = layout.width;
709
+ const height = layout.height;
710
+ const radius = layout.border.radius || height / 2;
711
+ const backgroundColor = props.backgroundColor || "#dc3545";
712
+ ctx.fillStyle = backgroundColor;
713
+ drawRoundedRect(ctx, x, y, width, height, radius);
714
+ ctx.fill();
715
+ const text = String(props[0] || props.text || "");
716
+ if (text) {
717
+ ctx.fillStyle = props.color || "#ffffff";
718
+ ctx.font = `${props.fontWeight || "bold"} ${props.fontSize || 10}px ${props.fontFamily || "sans-serif"}`;
719
+ ctx.textAlign = "center";
720
+ ctx.textBaseline = "middle";
721
+ ctx.fillText(text, x + width / 2, y + height / 2);
722
+ }
723
+ }
724
+ function paintAvatar(ctx, node) {
725
+ const layout = node.layout;
726
+ const props = node.props;
727
+ const size = Math.min(layout.width, layout.height);
728
+ const centerX = layout.x + size / 2;
729
+ const centerY = layout.y + size / 2;
730
+ const radius = size / 2;
731
+ ctx.save();
732
+ ctx.beginPath();
733
+ ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
734
+ ctx.clip();
735
+ const backgroundColor = props.backgroundColor || "#cccccc";
736
+ ctx.fillStyle = backgroundColor;
737
+ ctx.beginPath();
738
+ ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
739
+ ctx.fill();
740
+ const text = String(props[0] || props.text || props.initials || "");
741
+ if (text) {
742
+ ctx.fillStyle = props.color || "#ffffff";
743
+ ctx.font = `${props.fontWeight || "bold"} ${props.fontSize || size / 2.5}px ${props.fontFamily || "sans-serif"}`;
744
+ ctx.textAlign = "center";
745
+ ctx.textBaseline = "middle";
746
+ ctx.fillText(text, centerX, centerY);
747
+ }
748
+ ctx.restore();
749
+ if (layout.border.width > 0) {
750
+ ctx.strokeStyle = layout.border.color;
751
+ ctx.lineWidth = layout.border.width;
752
+ ctx.beginPath();
753
+ ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
754
+ ctx.stroke();
755
+ }
756
+ }
757
+ function paintIcon(ctx, node) {
758
+ const layout = node.layout;
759
+ const props = node.props;
760
+ const size = Math.min(layout.width, layout.height);
761
+ const x = layout.x;
762
+ const y = layout.y;
763
+ const color = props.color || "#000000";
764
+ const iconName = props.icon || props.name || "circle";
765
+ ctx.fillStyle = color;
766
+ ctx.strokeStyle = color;
767
+ ctx.lineWidth = 2;
768
+ switch (iconName.toLowerCase()) {
769
+ case "circle":
770
+ ctx.beginPath();
771
+ ctx.arc(x + size / 2, y + size / 2, size / 3, 0, Math.PI * 2);
772
+ ctx.fill();
773
+ break;
774
+ case "square":
775
+ const padding = size * 0.2;
776
+ ctx.fillRect(x + padding, y + padding, size - padding * 2, size - padding * 2);
777
+ break;
778
+ case "star":
779
+ drawStar(ctx, x + size / 2, y + size / 2, 5, size / 3, size / 6);
780
+ ctx.fill();
781
+ break;
782
+ case "check":
783
+ case "checkmark":
784
+ const p = size * 0.2;
785
+ ctx.beginPath();
786
+ ctx.moveTo(x + p, y + size / 2);
787
+ ctx.lineTo(x + size * 0.4, y + size - p);
788
+ ctx.lineTo(x + size - p, y + p);
789
+ ctx.stroke();
790
+ break;
791
+ case "x":
792
+ case "close":
793
+ const pd = size * 0.2;
794
+ ctx.beginPath();
795
+ ctx.moveTo(x + pd, y + pd);
796
+ ctx.lineTo(x + size - pd, y + size - pd);
797
+ ctx.moveTo(x + size - pd, y + pd);
798
+ ctx.lineTo(x + pd, y + size - pd);
799
+ ctx.stroke();
800
+ break;
801
+ default:
802
+ ctx.beginPath();
803
+ ctx.arc(x + size / 2, y + size / 2, size / 3, 0, Math.PI * 2);
804
+ ctx.fill();
805
+ }
806
+ }
807
+ function paintLink(ctx, node) {
808
+ const layout = node.layout;
809
+ const props = node.props;
810
+ const text = String(props[0] || props.text || "");
811
+ const color = node.hovered ? props.hoverColor || "#0056b3" : props.color || "#007bff";
812
+ const fontSize = parseFloat(props.fontSize) || 16;
813
+ const fontWeight = props.fontWeight || "normal";
814
+ const fontFamily = props.fontFamily || "system-ui, sans-serif";
815
+ const textDecoration = props.textDecoration !== undefined ? props.textDecoration : "underline";
816
+ ctx.fillStyle = color;
817
+ ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
818
+ ctx.textAlign = "left";
819
+ ctx.textBaseline = "top";
820
+ const x = layout.x + layout.contentX;
821
+ const y = layout.y + layout.contentY;
822
+ ctx.fillText(text, x, y);
823
+ if (textDecoration === "underline") {
824
+ const textWidth = ctx.measureText(text).width;
825
+ ctx.strokeStyle = color;
826
+ ctx.lineWidth = 1;
827
+ ctx.beginPath();
828
+ ctx.moveTo(x, y + fontSize + 2);
829
+ ctx.lineTo(x + textWidth, y + fontSize + 2);
830
+ ctx.stroke();
831
+ }
832
+ }
833
+ function applyTextDecoration(ctx, decoration, color, x, y, width, fontSize) {
834
+ ctx.strokeStyle = color;
835
+ ctx.lineWidth = Math.max(1, fontSize / 16);
836
+ ctx.beginPath();
837
+ if (decoration === "underline") {
838
+ const underlineY = y + fontSize + 2;
839
+ ctx.moveTo(x, underlineY);
840
+ ctx.lineTo(x + width, underlineY);
841
+ } else if (decoration === "line-through" || decoration === "strikethrough") {
842
+ const lineThroughY = y + fontSize / 2;
843
+ ctx.moveTo(x, lineThroughY);
844
+ ctx.lineTo(x + width, lineThroughY);
845
+ } else if (decoration === "overline") {
846
+ ctx.moveTo(x, y);
847
+ ctx.lineTo(x + width, y);
848
+ }
849
+ ctx.stroke();
850
+ }
851
+ function drawStar(ctx, cx, cy, spikes, outerRadius, innerRadius) {
852
+ let rot = Math.PI / 2 * 3;
853
+ let x = cx;
854
+ let y = cy;
855
+ const step = Math.PI / spikes;
856
+ ctx.beginPath();
857
+ ctx.moveTo(cx, cy - outerRadius);
858
+ for (let i = 0;i < spikes; i++) {
859
+ x = cx + Math.cos(rot) * outerRadius;
860
+ y = cy + Math.sin(rot) * outerRadius;
861
+ ctx.lineTo(x, y);
862
+ rot += step;
863
+ x = cx + Math.cos(rot) * innerRadius;
864
+ y = cy + Math.sin(rot) * innerRadius;
865
+ ctx.lineTo(x, y);
866
+ rot += step;
867
+ }
868
+ ctx.lineTo(cx, cy - outerRadius);
869
+ ctx.closePath();
870
+ }
871
+ export {
872
+ registerPainter,
873
+ paintNode
874
+ };
875
+
876
+ export { registerPainter, paintNode };
877
+
878
+ //# debugId=0E042DAE3A9F443064756E2164756E21