@epoch32/visage 0.0.0-stage → 0.0.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 (138) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +110 -2
  3. package/dist/bundle.dev.js +4875 -0
  4. package/dist/index.js +9633 -0
  5. package/dist/src/api/index.js +4002 -0
  6. package/dist/src/charts/index.js +2717 -0
  7. package/dist/src/devtools/index.js +4677 -0
  8. package/dist/src/dom/index.js +1550 -0
  9. package/dist/src/form/index.js +1415 -0
  10. package/dist/src/index.js +11826 -0
  11. package/dist/src/jsx-runtime/index.js +1630 -0
  12. package/dist/src/query/index.js +2422 -0
  13. package/dist/src/router/index.js +1856 -0
  14. package/dist/src/rpc/index.js +3484 -0
  15. package/dist/src/server/index.js +1173 -0
  16. package/dist/src/signals/index.js +1077 -0
  17. package/dist/src/style/index.js +2340 -0
  18. package/dist/src/template/index.js +1149 -0
  19. package/dist/src/ui/index.js +4155 -0
  20. package/dist/src/virtual/index.js +1955 -0
  21. package/dist/src/vite/index.js +984 -0
  22. package/dist/vite/index.js +707 -0
  23. package/package.json +140 -4
  24. package/src/api/client.ts +371 -0
  25. package/src/api/file-router.ts +183 -0
  26. package/src/api/handler.ts +326 -0
  27. package/src/api/index.ts +71 -0
  28. package/src/api/middleware.ts +54 -0
  29. package/src/api/pragma.ts +155 -0
  30. package/src/api/procedure.ts +172 -0
  31. package/src/api/types.ts +249 -0
  32. package/src/charts/Area.tsx +111 -0
  33. package/src/charts/Axis.tsx +126 -0
  34. package/src/charts/Bar.tsx +86 -0
  35. package/src/charts/CartesianGrid.tsx +72 -0
  36. package/src/charts/Chart.tsx +269 -0
  37. package/src/charts/Cursor.tsx +42 -0
  38. package/src/charts/Legend.tsx +56 -0
  39. package/src/charts/Line.tsx +106 -0
  40. package/src/charts/Pie.tsx +88 -0
  41. package/src/charts/Tooltip.tsx +127 -0
  42. package/src/charts/context.ts +30 -0
  43. package/src/charts/index.ts +14 -0
  44. package/src/charts/path.ts +155 -0
  45. package/src/charts/scale.ts +133 -0
  46. package/src/charts/types.ts +50 -0
  47. package/src/devtools/DomTab.tsx +134 -0
  48. package/src/devtools/GraphInspector.tsx +196 -0
  49. package/src/devtools/GraphTab.tsx +315 -0
  50. package/src/devtools/QueryTab.tsx +200 -0
  51. package/src/devtools/RpcTab.tsx +190 -0
  52. package/src/devtools/SignalsTab.tsx +129 -0
  53. package/src/devtools/component.tsx +243 -0
  54. package/src/devtools/highlighter.ts +134 -0
  55. package/src/devtools/hub.ts +175 -0
  56. package/src/devtools/index.ts +20 -0
  57. package/src/devtools/styles.ts +313 -0
  58. package/src/devtools/types.ts +45 -0
  59. package/src/dom/async.ts +232 -0
  60. package/src/dom/control-flow.ts +353 -0
  61. package/src/dom/index.ts +32 -0
  62. package/src/dom/lazy.ts +82 -0
  63. package/src/dom/render.ts +286 -0
  64. package/src/dom/resource.ts +120 -0
  65. package/src/form/form.ts +218 -0
  66. package/src/form/index.ts +17 -0
  67. package/src/form/schema.ts +299 -0
  68. package/src/index.ts +413 -0
  69. package/src/jsx-dev-runtime/index.ts +1 -0
  70. package/src/jsx-runtime/index.ts +112 -0
  71. package/src/jsx-runtime/jsx.d.ts +278 -0
  72. package/src/query/create-mutation.ts +64 -0
  73. package/src/query/create-query.ts +426 -0
  74. package/src/query/focus-manager.ts +68 -0
  75. package/src/query/index.ts +63 -0
  76. package/src/query/mutation-cache.ts +138 -0
  77. package/src/query/online-manager.ts +67 -0
  78. package/src/query/query-cache.ts +369 -0
  79. package/src/query/query-client.ts +248 -0
  80. package/src/query/types.ts +221 -0
  81. package/src/query/utils.ts +132 -0
  82. package/src/router/index.ts +19 -0
  83. package/src/router/router.tsx +352 -0
  84. package/src/rpc/client.ts +317 -0
  85. package/src/rpc/error.ts +70 -0
  86. package/src/rpc/index.ts +52 -0
  87. package/src/rpc/procedure.ts +88 -0
  88. package/src/rpc/router.ts +105 -0
  89. package/src/rpc/server.ts +511 -0
  90. package/src/rpc/types.ts +328 -0
  91. package/src/server/hydrate.ts +21 -0
  92. package/src/server/index.ts +10 -0
  93. package/src/server/render-to-stream.ts +31 -0
  94. package/src/server/render-to-string.ts +125 -0
  95. package/src/signals/computed.ts +217 -0
  96. package/src/signals/context.ts +89 -0
  97. package/src/signals/effect.ts +161 -0
  98. package/src/signals/index.ts +57 -0
  99. package/src/signals/scope.ts +132 -0
  100. package/src/signals/signal.ts +203 -0
  101. package/src/signals/store.ts +163 -0
  102. package/src/signals/system.ts +124 -0
  103. package/src/signals/types.ts +64 -0
  104. package/src/style/component.tsx +122 -0
  105. package/src/style/create-style.ts +229 -0
  106. package/src/style/cx.ts +75 -0
  107. package/src/style/hash.ts +19 -0
  108. package/src/style/index.ts +8 -0
  109. package/src/style/layout.tsx +406 -0
  110. package/src/style/manager.ts +87 -0
  111. package/src/style/parser.ts +257 -0
  112. package/src/style/scale.ts +237 -0
  113. package/src/template/index.ts +4 -0
  114. package/src/template/template.ts +126 -0
  115. package/src/ui/accordion.tsx +286 -0
  116. package/src/ui/command-dialog.tsx +49 -0
  117. package/src/ui/command.tsx +400 -0
  118. package/src/ui/data-table.tsx +322 -0
  119. package/src/ui/dialog.tsx +362 -0
  120. package/src/ui/dropdown-menu.tsx +381 -0
  121. package/src/ui/icon.tsx +69 -0
  122. package/src/ui/index.ts +138 -0
  123. package/src/ui/layout.tsx +13 -0
  124. package/src/ui/popover.tsx +249 -0
  125. package/src/ui/switch.tsx +114 -0
  126. package/src/ui/tabs.tsx +297 -0
  127. package/src/ui/toast.tsx +280 -0
  128. package/src/ui/tooltip.tsx +262 -0
  129. package/src/ui/utils.ts +110 -0
  130. package/src/virtual/index.ts +12 -0
  131. package/src/virtual/virtual-list.tsx +185 -0
  132. package/src/virtual/virtualizer.ts +274 -0
  133. package/src/vite/hmr.ts +38 -0
  134. package/src/vite/index.ts +3 -0
  135. package/src/vite/lucide.ts +124 -0
  136. package/src/vite/plugin.ts +102 -0
  137. package/src/vite/rpc-middleware.ts +82 -0
  138. package/src/vite/types.ts +65 -0
@@ -0,0 +1,406 @@
1
+ import type { Child } from "../dom";
2
+ import { jsx } from "../jsx-runtime";
3
+ import type { ClassProp, HTMLAttributes } from "../jsx-runtime/jsx";
4
+ import { resolveDimension, space } from "./scale";
5
+
6
+ const ALIGN_MAP: Record<string, string> = {
7
+ start: "flex-start",
8
+ center: "center",
9
+ end: "flex-end",
10
+ stretch: "stretch",
11
+ baseline: "baseline"
12
+ };
13
+
14
+ const JUSTIFY_MAP: Record<string, string> = {
15
+ start: "flex-start",
16
+ center: "center",
17
+ end: "flex-end",
18
+ between: "space-between",
19
+ around: "space-around",
20
+ evenly: "space-evenly",
21
+ stretch: "stretch"
22
+ };
23
+
24
+ export interface BaseLayoutProps extends HTMLAttributes<HTMLDivElement> {
25
+ as?: string;
26
+ gap?: number | string;
27
+ p?: number | string;
28
+ px?: number | string;
29
+ py?: number | string;
30
+ pt?: number | string;
31
+ pb?: number | string;
32
+ pl?: number | string;
33
+ pr?: number | string;
34
+ m?: number | string;
35
+ mx?: number | string;
36
+ my?: number | string;
37
+ mt?: number | string;
38
+ mb?: number | string;
39
+ ml?: number | string;
40
+ mr?: number | string;
41
+ w?: number | string;
42
+ h?: number | string;
43
+ minW?: number | string;
44
+ maxW?: number | string;
45
+ minH?: number | string;
46
+ maxH?: number | string;
47
+ class?: ClassProp;
48
+ className?: ClassProp;
49
+ style?: any;
50
+ children?: Child;
51
+ [key: string]: any;
52
+ }
53
+
54
+ function resolveBaseStyles(props: BaseLayoutProps): Record<string, string> {
55
+ const s: Record<string, string> = {
56
+ boxSizing: "border-box"
57
+ };
58
+
59
+ if (props.gap !== undefined) s.gap = space(props.gap);
60
+ if (props.p !== undefined) s.padding = space(props.p);
61
+ if (props.px !== undefined) {
62
+ s.paddingLeft = space(props.px);
63
+ s.paddingRight = space(props.px);
64
+ }
65
+ if (props.py !== undefined) {
66
+ s.paddingTop = space(props.py);
67
+ s.paddingBottom = space(props.py);
68
+ }
69
+ if (props.pt !== undefined) s.paddingTop = space(props.pt);
70
+ if (props.pb !== undefined) s.paddingBottom = space(props.pb);
71
+ if (props.pl !== undefined) s.paddingLeft = space(props.pl);
72
+ if (props.pr !== undefined) s.paddingRight = space(props.pr);
73
+
74
+ if (props.m !== undefined) s.margin = space(props.m);
75
+ if (props.mx !== undefined) {
76
+ s.marginLeft = space(props.mx);
77
+ s.marginRight = space(props.mx);
78
+ }
79
+ if (props.my !== undefined) {
80
+ s.marginTop = space(props.my);
81
+ s.marginBottom = space(props.my);
82
+ }
83
+ if (props.mt !== undefined) s.marginTop = space(props.mt);
84
+ if (props.mb !== undefined) s.marginBottom = space(props.mb);
85
+ if (props.ml !== undefined) s.marginLeft = space(props.ml);
86
+ if (props.mr !== undefined) s.marginRight = space(props.mr);
87
+
88
+ if (props.w !== undefined) s.width = resolveDimension(props.w);
89
+ if (props.h !== undefined) s.height = resolveDimension(props.h);
90
+ if (props.minW !== undefined) s.minWidth = resolveDimension(props.minW);
91
+ if (props.maxW !== undefined) s.maxWidth = resolveDimension(props.maxW);
92
+ if (props.minH !== undefined) s.minHeight = resolveDimension(props.minH);
93
+ if (props.maxH !== undefined) s.maxHeight = resolveDimension(props.maxH);
94
+
95
+ return s;
96
+ }
97
+
98
+ function mergeStyles(base: Record<string, string>, userStyle: any): any {
99
+ if (!userStyle) return base;
100
+ if (typeof userStyle === "string") {
101
+ const baseStr = Object.entries(base)
102
+ .map(([k, v]) => `${k.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${v}`)
103
+ .join("; ");
104
+ return `${baseStr}; ${userStyle}`;
105
+ }
106
+ if (typeof userStyle === "object") {
107
+ return { ...base, ...userStyle };
108
+ }
109
+ return base;
110
+ }
111
+
112
+ // Strip layout-specific helper props so they are not set as DOM attributes
113
+ function cleanProps(props: Record<string, any>, excludeKeys: Set<string>): Record<string, any> {
114
+ const result: Record<string, any> = {};
115
+ for (const k in props) {
116
+ if (!excludeKeys.has(k)) {
117
+ result[k] = props[k];
118
+ }
119
+ }
120
+ return result;
121
+ }
122
+
123
+ const COMMON_EXCLUDE = new Set([
124
+ "as",
125
+ "gap",
126
+ "p",
127
+ "px",
128
+ "py",
129
+ "pt",
130
+ "pb",
131
+ "pl",
132
+ "pr",
133
+ "m",
134
+ "mx",
135
+ "my",
136
+ "mt",
137
+ "mb",
138
+ "ml",
139
+ "mr",
140
+ "w",
141
+ "h",
142
+ "minW",
143
+ "maxW",
144
+ "minH",
145
+ "maxH"
146
+ ]);
147
+
148
+ export interface StackProps extends BaseLayoutProps {
149
+ align?: "start" | "center" | "end" | "stretch" | "baseline";
150
+ justify?: "start" | "center" | "end" | "between" | "around" | "evenly";
151
+ direction?: "vertical" | "horizontal";
152
+ reversed?: boolean;
153
+ wrap?: boolean | "wrap" | "nowrap" | "wrap-reverse";
154
+ inline?: boolean;
155
+ }
156
+
157
+ const STACK_EXCLUDE = new Set([
158
+ ...COMMON_EXCLUDE,
159
+ "align",
160
+ "justify",
161
+ "direction",
162
+ "reversed",
163
+ "wrap",
164
+ "inline"
165
+ ]);
166
+
167
+ /**
168
+ * <Stack> Layout Primitive
169
+ *
170
+ * Vertical (column) layout by default. Provides zero margin collapse,
171
+ * built-in spatial scale gap, and automatic flex overflow containment.
172
+ *
173
+ * @example
174
+ * <Stack gap={4} align="center">
175
+ * <Header />
176
+ * <Content />
177
+ * </Stack>
178
+ */
179
+ export function Stack(props: StackProps): Child {
180
+ const { as = "div", children, ...rest } = props;
181
+ const layoutStyles = resolveBaseStyles(props);
182
+
183
+ layoutStyles.display = props.inline ? "inline-flex" : "flex";
184
+ layoutStyles.flexDirection =
185
+ props.direction === "horizontal"
186
+ ? props.reversed
187
+ ? "row-reverse"
188
+ : "row"
189
+ : props.reversed
190
+ ? "column-reverse"
191
+ : "column";
192
+
193
+ if (props.align && ALIGN_MAP[props.align]) {
194
+ layoutStyles.alignItems = ALIGN_MAP[props.align]!;
195
+ }
196
+ if (props.justify && JUSTIFY_MAP[props.justify]) {
197
+ layoutStyles.justifyContent = JUSTIFY_MAP[props.justify]!;
198
+ }
199
+ if (props.wrap) {
200
+ layoutStyles.flexWrap = props.wrap === true ? "wrap" : props.wrap;
201
+ }
202
+
203
+ const domProps = cleanProps(rest, STACK_EXCLUDE);
204
+ domProps.style = mergeStyles(layoutStyles, props.style);
205
+ domProps.children = children;
206
+
207
+ return jsx(as, domProps);
208
+ }
209
+
210
+ export interface RowProps extends BaseLayoutProps {
211
+ align?: "start" | "center" | "end" | "stretch" | "baseline";
212
+ justify?: "start" | "center" | "end" | "between" | "around" | "evenly";
213
+ reversed?: boolean;
214
+ wrap?: boolean | "wrap" | "nowrap" | "wrap-reverse";
215
+ inline?: boolean;
216
+ }
217
+
218
+ const ROW_EXCLUDE = new Set([...COMMON_EXCLUDE, "align", "justify", "reversed", "wrap", "inline"]);
219
+
220
+ /**
221
+ * <Row> Layout Primitive
222
+ *
223
+ * Horizontal row layout with sensible vertical centering (align="center") by default.
224
+ *
225
+ * @example
226
+ * <Row gap={3} justify="between">
227
+ * <Avatar />
228
+ * <Typography>Online</Typography>
229
+ * </Row>
230
+ */
231
+ export function Row(props: RowProps): Child {
232
+ const { as = "div", children, ...rest } = props;
233
+ const layoutStyles = resolveBaseStyles(props);
234
+
235
+ layoutStyles.display = props.inline ? "inline-flex" : "flex";
236
+ layoutStyles.flexDirection = props.reversed ? "row-reverse" : "row";
237
+ layoutStyles.alignItems = props.align ? (ALIGN_MAP[props.align] ?? props.align) : "center";
238
+
239
+ if (props.justify && JUSTIFY_MAP[props.justify]) {
240
+ layoutStyles.justifyContent = JUSTIFY_MAP[props.justify]!;
241
+ }
242
+ if (props.wrap) {
243
+ layoutStyles.flexWrap = props.wrap === true ? "wrap" : props.wrap;
244
+ }
245
+
246
+ const domProps = cleanProps(rest, ROW_EXCLUDE);
247
+ domProps.style = mergeStyles(layoutStyles, props.style);
248
+ domProps.children = children;
249
+
250
+ return jsx(as, domProps);
251
+ }
252
+
253
+ export interface CenterProps extends BaseLayoutProps {
254
+ inline?: boolean;
255
+ }
256
+
257
+ const CENTER_EXCLUDE = new Set([...COMMON_EXCLUDE, "inline"]);
258
+
259
+ /**
260
+ * <Center> Layout Primitive
261
+ *
262
+ * Centers child content horizontally and vertically without CSS boilerplate.
263
+ *
264
+ * @example
265
+ * <Center h="screen">
266
+ * <Spinner />
267
+ * </Center>
268
+ */
269
+ export function Center(props: CenterProps): Child {
270
+ const { as = "div", children, ...rest } = props;
271
+ const layoutStyles = resolveBaseStyles(props);
272
+
273
+ layoutStyles.display = props.inline ? "inline-flex" : "flex";
274
+ layoutStyles.alignItems = "center";
275
+ layoutStyles.justifyContent = "center";
276
+
277
+ const domProps = cleanProps(rest, CENTER_EXCLUDE);
278
+ domProps.style = mergeStyles(layoutStyles, props.style);
279
+ domProps.children = children;
280
+
281
+ return jsx(as, domProps);
282
+ }
283
+
284
+ export interface GridProps extends BaseLayoutProps {
285
+ columns?: number | string;
286
+ minChildWidth?: number | string;
287
+ rows?: number | string;
288
+ rowGap?: number | string;
289
+ colGap?: number | string;
290
+ align?: "start" | "center" | "end" | "stretch";
291
+ justify?: "start" | "center" | "end" | "stretch";
292
+ inline?: boolean;
293
+ }
294
+
295
+ const GRID_EXCLUDE = new Set([
296
+ ...COMMON_EXCLUDE,
297
+ "columns",
298
+ "minChildWidth",
299
+ "rows",
300
+ "rowGap",
301
+ "colGap",
302
+ "align",
303
+ "justify",
304
+ "inline"
305
+ ]);
306
+
307
+ /**
308
+ * <Grid> Layout Primitive
309
+ *
310
+ * Effortless CSS Grid. Specify fixed columns or a responsive minChildWidth
311
+ * to achieve fluid wrapping grids without writing complex media queries.
312
+ *
313
+ * @example
314
+ * // Responsive auto-wrapping grid
315
+ * <Grid minChildWidth={260} gap={4}>
316
+ * <Card />
317
+ * <Card />
318
+ * </Grid>
319
+ */
320
+ export function Grid(props: GridProps): Child {
321
+ const { as = "div", children, ...rest } = props;
322
+ const layoutStyles = resolveBaseStyles(props);
323
+
324
+ layoutStyles.display = props.inline ? "inline-grid" : "grid";
325
+
326
+ if (props.minChildWidth !== undefined) {
327
+ const minW = resolveDimension(props.minChildWidth);
328
+ layoutStyles.gridTemplateColumns = `repeat(auto-fit, minmax(min(100%, ${minW}), 1fr))`;
329
+ } else if (props.columns !== undefined) {
330
+ if (typeof props.columns === "number") {
331
+ layoutStyles.gridTemplateColumns = `repeat(${props.columns}, minmax(0, 1fr))`;
332
+ } else {
333
+ layoutStyles.gridTemplateColumns = String(props.columns);
334
+ }
335
+ }
336
+
337
+ if (props.rows !== undefined) {
338
+ if (typeof props.rows === "number") {
339
+ layoutStyles.gridTemplateRows = `repeat(${props.rows}, minmax(0, 1fr))`;
340
+ } else {
341
+ layoutStyles.gridTemplateRows = String(props.rows);
342
+ }
343
+ }
344
+
345
+ if (props.rowGap !== undefined) layoutStyles.rowGap = space(props.rowGap);
346
+ if (props.colGap !== undefined) layoutStyles.columnGap = space(props.colGap);
347
+
348
+ if (props.align) {
349
+ layoutStyles.alignItems = ALIGN_MAP[props.align] ?? props.align;
350
+ }
351
+ if (props.justify) {
352
+ layoutStyles.justifyItems = props.justify;
353
+ }
354
+
355
+ const domProps = cleanProps(rest, GRID_EXCLUDE);
356
+ domProps.style = mergeStyles(layoutStyles, props.style);
357
+ domProps.children = children;
358
+
359
+ return jsx(as, domProps);
360
+ }
361
+
362
+ export interface SpacerProps extends BaseLayoutProps {
363
+ size?: number | string;
364
+ x?: number | string;
365
+ y?: number | string;
366
+ flex?: boolean | number;
367
+ }
368
+
369
+ const SPACER_EXCLUDE = new Set([...COMMON_EXCLUDE, "size", "x", "y", "flex"]);
370
+
371
+ /**
372
+ * <Spacer> Layout Primitive
373
+ *
374
+ * Creates flexible pushing space in Rows/Stacks (defaults to flex: 1)
375
+ * or creates a dedicated spatial gap block when given size.
376
+ *
377
+ * @example
378
+ * <Row>
379
+ * <Logo />
380
+ * <Spacer />
381
+ * <UserProfile />
382
+ * </Row>
383
+ */
384
+ export function Spacer(props: SpacerProps): Child {
385
+ const { as = "div", ...rest } = props;
386
+ const layoutStyles = resolveBaseStyles(props);
387
+
388
+ if (props.size !== undefined) {
389
+ const s = space(props.size);
390
+ layoutStyles.width = s;
391
+ layoutStyles.height = s;
392
+ layoutStyles.flexShrink = "0";
393
+ } else if (props.x !== undefined || props.y !== undefined) {
394
+ if (props.x !== undefined) layoutStyles.width = space(props.x);
395
+ if (props.y !== undefined) layoutStyles.height = space(props.y);
396
+ layoutStyles.flexShrink = "0";
397
+ } else {
398
+ layoutStyles.flex = typeof props.flex === "number" ? String(props.flex) : "1 1 0%";
399
+ layoutStyles.alignSelf = "stretch";
400
+ }
401
+
402
+ const domProps = cleanProps(rest, SPACER_EXCLUDE);
403
+ domProps.style = mergeStyles(layoutStyles, props.style);
404
+
405
+ return jsx(as, domProps);
406
+ }
@@ -0,0 +1,87 @@
1
+ export interface InjectedStyle {
2
+ id: string;
3
+ css: string;
4
+ count: number;
5
+ element?: HTMLStyleElement;
6
+ }
7
+
8
+ export class StyleManager {
9
+ private _styles = new Map<string, InjectedStyle>();
10
+ private _ssrStyles = new Set<string>();
11
+
12
+ /**
13
+ * Inject CSS into the document head or SSR registry
14
+ */
15
+ inject(id: string, css: string): () => void {
16
+ let entry = this._styles.get(id);
17
+
18
+ if (!entry) {
19
+ entry = { id, css, count: 0 };
20
+ this._styles.set(id, entry);
21
+
22
+ if (typeof document !== "undefined") {
23
+ const target = document.head || document.documentElement || document.body;
24
+ if (target) {
25
+ const styleEl = document.createElement("style");
26
+ styleEl.id = `visage-style-${id}`;
27
+ styleEl.setAttribute("data-visage-scope", id);
28
+ styleEl.textContent = css;
29
+ target.appendChild(styleEl);
30
+ entry.element = styleEl;
31
+ }
32
+ }
33
+ this._ssrStyles.add(css);
34
+ }
35
+
36
+ entry.count++;
37
+
38
+ // Return cleanup callback
39
+ return () => {
40
+ if (!entry) return;
41
+ entry.count--;
42
+ if (entry.count <= 0) {
43
+ if (entry.element?.parentNode) {
44
+ entry.element.parentNode.removeChild(entry.element);
45
+ }
46
+ this._styles.delete(id);
47
+ }
48
+ };
49
+ }
50
+
51
+ /**
52
+ * Check if a style scope is currently registered
53
+ */
54
+ has(id: string): boolean {
55
+ return this._styles.has(id);
56
+ }
57
+
58
+ /**
59
+ * Extract all styles collected during Server-Side Rendering (SSR)
60
+ */
61
+ extractSSRStyles(): string {
62
+ return Array.from(this._ssrStyles).join("\n");
63
+ }
64
+
65
+ /**
66
+ * Clear all registered and SSR styles
67
+ */
68
+ clear() {
69
+ for (const entry of this._styles.values()) {
70
+ if (entry.element?.parentNode) {
71
+ entry.element.parentNode.removeChild(entry.element);
72
+ }
73
+ }
74
+ this._styles.clear();
75
+ this._ssrStyles.clear();
76
+ }
77
+ }
78
+
79
+ export const styleManager = new StyleManager();
80
+
81
+ /**
82
+ * Helper to get SSR style tag HTML
83
+ */
84
+ export function extractStyleTags(): string {
85
+ const css = styleManager.extractSSRStyles();
86
+ return css ? `<style data-visage-ssr="true">${css}</style>` : "";
87
+ }