@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,353 @@
1
+ import { effect, getActiveScope, onCleanup, runInScope, Scope, untrack } from "../signals";
2
+ import { type Child, insertChild, type ReactiveValue, resolveValue } from "./render";
3
+
4
+ export interface ShowProps<T> {
5
+ when: ReactiveValue<T | false | null | undefined>;
6
+ fallback?: Child;
7
+ children: Child | ((item: () => T) => Child);
8
+ }
9
+
10
+ /**
11
+ * Conditionally renders JSX children when the `when` condition evaluates to truthy,
12
+ * with fine-grained reactive scope lifecycle management and automatic fallback rendering.
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * const loggedIn = signal(false);
17
+ *
18
+ * <Show when={loggedIn} fallback={<div>Please log in</div>}>
19
+ * <Dashboard />
20
+ * </Show>
21
+ * ```
22
+ */
23
+ export function Show<T>(props: ShowProps<T>): any {
24
+ if (typeof document === "undefined") {
25
+ const condition = resolveValue(props.when);
26
+ if (condition) {
27
+ return typeof props.children === "function" && props.children.length > 0
28
+ ? (props.children as (item: () => T) => Child)(() => resolveValue(props.when) as T)
29
+ : typeof props.children === "function"
30
+ ? (props.children as () => Child)()
31
+ : props.children;
32
+ }
33
+ return props.fallback ?? null;
34
+ }
35
+
36
+ const marker = document.createTextNode("");
37
+ const initialFrag = document.createDocumentFragment();
38
+ let currentScope: Scope | null = null;
39
+ let currentContent: Node[] = [];
40
+ let isMounted = false;
41
+
42
+ effect(() => {
43
+ const condition = resolveValue(props.when);
44
+
45
+ if (currentScope) {
46
+ currentScope.dispose();
47
+ currentScope = null;
48
+ }
49
+
50
+ // Clean up old nodes from DOM
51
+ for (const node of currentContent) {
52
+ if (node.parentNode) {
53
+ node.parentNode.removeChild(node);
54
+ }
55
+ }
56
+ currentContent = [];
57
+
58
+ const frag = document.createDocumentFragment();
59
+
60
+ if (condition) {
61
+ currentScope = new Scope(getActiveScope());
62
+ untrack(() => {
63
+ runInScope(currentScope, () => {
64
+ const childRes =
65
+ typeof props.children === "function" && props.children.length > 0
66
+ ? (props.children as (item: () => T) => Child)(() => resolveValue(props.when) as T)
67
+ : typeof props.children === "function"
68
+ ? (props.children as () => Child)()
69
+ : props.children;
70
+
71
+ insertChild(frag, childRes);
72
+ });
73
+ });
74
+ } else if (props.fallback !== undefined) {
75
+ currentScope = new Scope(getActiveScope());
76
+ untrack(() => {
77
+ runInScope(currentScope, () => {
78
+ insertChild(frag, props.fallback);
79
+ });
80
+ });
81
+ }
82
+
83
+ currentContent = Array.from(frag.childNodes);
84
+
85
+ if (marker.parentNode) {
86
+ marker.parentNode.insertBefore(frag, marker);
87
+ } else if (!isMounted) {
88
+ initialFrag.appendChild(frag);
89
+ }
90
+ });
91
+
92
+ isMounted = true;
93
+ initialFrag.appendChild(marker);
94
+
95
+ onCleanup(() => {
96
+ if (currentScope) {
97
+ currentScope.dispose();
98
+ }
99
+ });
100
+
101
+ return initialFrag;
102
+ }
103
+
104
+ export interface ForProps<T> {
105
+ each: ReactiveValue<readonly T[] | undefined | null>;
106
+ fallback?: Child;
107
+ children: (item: T, index: () => number) => Child;
108
+ }
109
+
110
+ interface ItemRecord<T> {
111
+ item: T;
112
+ scope: Scope;
113
+ nodes: Node[];
114
+ indexSignal: (val?: number) => number;
115
+ }
116
+
117
+ /**
118
+ * Keyed / referential list renderer with fine-grained item reconciliation.
119
+ *
120
+ * Items in the list are reused across array mutations without re-rendering existing items
121
+ * or tearing down their active reactive scopes.
122
+ *
123
+ * @example
124
+ * ```tsx
125
+ * const items = signal(["Apple", "Banana", "Cherry"]);
126
+ *
127
+ * <For each={items} fallback={<div>No items found</div>}>
128
+ * {(item, index) => (
129
+ * <li>
130
+ * {index() + 1}. {item}
131
+ * </li>
132
+ * )}
133
+ * </For>
134
+ * ```
135
+ */
136
+ export function For<T>(props: ForProps<T>): any {
137
+ if (typeof document === "undefined") {
138
+ const list = resolveValue(props.each) ?? [];
139
+ if (list.length === 0) {
140
+ return props.fallback ?? null;
141
+ }
142
+ return list.map((item, idx) => props.children(item, () => idx));
143
+ }
144
+
145
+ const marker = document.createTextNode("");
146
+ const initialFrag = document.createDocumentFragment();
147
+ let items: ItemRecord<T>[] = [];
148
+ let fallbackScope: Scope | null = null;
149
+ let fallbackNodes: Node[] = [];
150
+ let isMounted = false;
151
+
152
+ effect(() => {
153
+ const list = resolveValue(props.each) ?? [];
154
+ const parent = marker.parentNode ?? (!isMounted ? initialFrag : null);
155
+
156
+ if (list.length === 0) {
157
+ // Clear items
158
+ for (const rec of items) {
159
+ rec.scope.dispose();
160
+ for (const node of rec.nodes) {
161
+ if (node.parentNode) node.parentNode.removeChild(node);
162
+ }
163
+ }
164
+ items = [];
165
+
166
+ // Render fallback
167
+ if (props.fallback !== undefined && fallbackNodes.length === 0 && parent) {
168
+ fallbackScope = new Scope(getActiveScope());
169
+ untrack(() => {
170
+ runInScope(fallbackScope, () => {
171
+ const frag = document.createDocumentFragment();
172
+ insertChild(frag, props.fallback);
173
+ fallbackNodes = Array.from(frag.childNodes);
174
+ if (marker.parentNode) {
175
+ marker.parentNode.insertBefore(frag, marker);
176
+ } else if (!isMounted) {
177
+ initialFrag.appendChild(frag);
178
+ }
179
+ });
180
+ });
181
+ }
182
+ return;
183
+ }
184
+
185
+ // Clean up fallback if present
186
+ if (fallbackNodes.length > 0) {
187
+ if (fallbackScope) {
188
+ fallbackScope.dispose();
189
+ fallbackScope = null;
190
+ }
191
+ for (const node of fallbackNodes) {
192
+ if (node.parentNode) node.parentNode.removeChild(node);
193
+ }
194
+ fallbackNodes = [];
195
+ }
196
+
197
+ if (!parent) return;
198
+
199
+ const newItems: ItemRecord<T>[] = [];
200
+ const prevMap = new Map<T, ItemRecord<T>>();
201
+
202
+ for (const rec of items) {
203
+ prevMap.set(rec.item, rec);
204
+ }
205
+
206
+ // Fast-path: map existing or create new item scopes
207
+ for (let i = 0; i < list.length; i++) {
208
+ const item = list[i]!;
209
+ const existing = prevMap.get(item);
210
+
211
+ if (existing) {
212
+ existing.indexSignal(i);
213
+ newItems.push(existing);
214
+ prevMap.delete(item);
215
+ } else {
216
+ let indexVal = i;
217
+ const indexSubscribers = new Set<() => void>();
218
+ const indexSignal = (...args: [number?]): number => {
219
+ if (args.length > 0 && args[0] !== undefined && args[0] !== indexVal) {
220
+ indexVal = args[0];
221
+ for (const sub of Array.from(indexSubscribers)) sub();
222
+ }
223
+ return indexVal;
224
+ };
225
+
226
+ const itemScope = new Scope(getActiveScope());
227
+ let itemNodes: Node[] = [];
228
+
229
+ untrack(() => {
230
+ runInScope(itemScope, () => {
231
+ const frag = document.createDocumentFragment();
232
+ const child = props.children(item, indexSignal);
233
+ insertChild(frag, child);
234
+ itemNodes = Array.from(frag.childNodes);
235
+ });
236
+ });
237
+
238
+ newItems.push({
239
+ item,
240
+ scope: itemScope,
241
+ nodes: itemNodes,
242
+ indexSignal
243
+ });
244
+ }
245
+ }
246
+
247
+ // Clean up removed items
248
+ for (const [, removed] of prevMap) {
249
+ removed.scope.dispose();
250
+ for (const node of removed.nodes) {
251
+ if (node.parentNode) node.parentNode.removeChild(node);
252
+ }
253
+ }
254
+
255
+ // Reorder / insert DOM nodes
256
+ if (marker.parentNode) {
257
+ let currentMarker: Node = marker;
258
+ for (let i = newItems.length - 1; i >= 0; i--) {
259
+ const rec = newItems[i]!;
260
+ for (let j = rec.nodes.length - 1; j >= 0; j--) {
261
+ const node = rec.nodes[j]!;
262
+ if (node.nextSibling !== currentMarker) {
263
+ marker.parentNode.insertBefore(node, currentMarker);
264
+ }
265
+ currentMarker = node;
266
+ }
267
+ }
268
+ } else if (!isMounted) {
269
+ for (const rec of newItems) {
270
+ for (const node of rec.nodes) {
271
+ initialFrag.appendChild(node);
272
+ }
273
+ }
274
+ }
275
+
276
+ items = newItems;
277
+ });
278
+
279
+ isMounted = true;
280
+ initialFrag.appendChild(marker);
281
+
282
+ onCleanup(() => {
283
+ for (const rec of items) {
284
+ rec.scope.dispose();
285
+ }
286
+ if (fallbackScope) {
287
+ fallbackScope.dispose();
288
+ }
289
+ });
290
+
291
+ return initialFrag;
292
+ }
293
+
294
+ export interface PortalProps {
295
+ mount?: HTMLElement | null;
296
+ children: Child | (() => Child);
297
+ }
298
+
299
+ /**
300
+ * Portal renders children into a different DOM container while preserving reactive context
301
+ */
302
+ export function Portal(props: PortalProps): any {
303
+ if (typeof document === "undefined") {
304
+ return null;
305
+ }
306
+
307
+ const marker = document.createTextNode("");
308
+ let currentScope: Scope | null = null;
309
+ let nodes: Node[] = [];
310
+
311
+ effect(() => {
312
+ const target = props.mount ?? (typeof document !== "undefined" ? document.body : null);
313
+ if (!target) return;
314
+
315
+ if (currentScope) {
316
+ currentScope.dispose();
317
+ currentScope = null;
318
+ }
319
+ for (const node of nodes) {
320
+ if (node.parentNode) {
321
+ node.parentNode.removeChild(node);
322
+ }
323
+ }
324
+ nodes = [];
325
+
326
+ currentScope = new Scope(getActiveScope());
327
+ untrack(() => {
328
+ runInScope(currentScope, () => {
329
+ const childRes =
330
+ typeof props.children === "function" ? (props.children as () => Child)() : props.children;
331
+ const frag = document.createDocumentFragment();
332
+ insertChild(frag, childRes);
333
+ nodes = Array.from(frag.childNodes);
334
+ target.appendChild(frag);
335
+ });
336
+ });
337
+ });
338
+
339
+ onCleanup(() => {
340
+ if (currentScope) {
341
+ currentScope.dispose();
342
+ currentScope = null;
343
+ }
344
+ for (const node of nodes) {
345
+ if (node.parentNode) {
346
+ node.parentNode.removeChild(node);
347
+ }
348
+ }
349
+ nodes = [];
350
+ });
351
+
352
+ return marker;
353
+ }
@@ -0,0 +1,32 @@
1
+ export {
2
+ type Component,
3
+ createResource,
4
+ ErrorBoundary,
5
+ type ErrorBoundaryProps,
6
+ type LazyComponent,
7
+ lazy,
8
+ type Resource,
9
+ type ResourceActions,
10
+ Suspense,
11
+ SuspenseContext,
12
+ type SuspenseContextValue,
13
+ type SuspenseProps
14
+ } from "./async";
15
+
16
+ export {
17
+ For,
18
+ type ForProps,
19
+ Portal,
20
+ type PortalProps,
21
+ Show,
22
+ type ShowProps
23
+ } from "./control-flow";
24
+ export {
25
+ type Child,
26
+ insertChild,
27
+ isReactive,
28
+ type ReactiveValue,
29
+ render,
30
+ resolveValue,
31
+ setAttribute
32
+ } from "./render";
@@ -0,0 +1,82 @@
1
+ import { useContext } from "../signals/context";
2
+ import type { Child } from "./render";
3
+ import { createResource, SuspenseContext } from "./resource";
4
+
5
+ export type Component<P = any> = (props: P) => Child;
6
+
7
+ export type LazyComponent<T extends Component<any>> = T & {
8
+ preload(): Promise<T>;
9
+ };
10
+
11
+ /**
12
+ * Lazy load an asynchronous component with automatic `<Suspense>` boundary coordination and preloading.
13
+ *
14
+ * Dynamically imports the component module upon first render or when `.preload()` is invoked.
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * // Dynamic code-split component
19
+ * const HeavyChart = lazy(() => import("./HeavyChart"));
20
+ *
21
+ * // Optional early prefetching
22
+ * HeavyChart.preload();
23
+ *
24
+ * // Render inside Suspense
25
+ * <Suspense fallback={<div>Loading chart...</div>}>
26
+ * <HeavyChart data={chartData()} />
27
+ * </Suspense>
28
+ * ```
29
+ *
30
+ * @param importer - Dynamic import function returning a component or `{ default: Component }`
31
+ * @returns A lazy component with a `.preload()` method
32
+ */
33
+ export function lazy<T extends Component<any>>(
34
+ importer: () => Promise<{ default: T } | T>
35
+ ): LazyComponent<T> {
36
+ let loadedComponent: T | null = null;
37
+ let loadingPromise: Promise<T> | null = null;
38
+
39
+ const preload = (): Promise<T> => {
40
+ if (loadedComponent) {
41
+ return Promise.resolve(loadedComponent);
42
+ }
43
+ if (!loadingPromise) {
44
+ loadingPromise = importer()
45
+ .then((mod) => {
46
+ const comp =
47
+ mod && typeof mod === "object" && "default" in mod
48
+ ? (mod as { default: T }).default
49
+ : (mod as T);
50
+ loadedComponent = comp;
51
+ return comp;
52
+ })
53
+ .catch((err) => {
54
+ loadingPromise = null;
55
+ throw err;
56
+ });
57
+ }
58
+ return loadingPromise;
59
+ };
60
+
61
+ const LazyWrapper = ((props: any) => {
62
+ if (loadedComponent) {
63
+ return loadedComponent(props);
64
+ }
65
+
66
+ const promise = preload();
67
+ const suspense = useContext(SuspenseContext);
68
+ if (suspense) {
69
+ suspense.registerPromise(promise);
70
+ }
71
+
72
+ const res = createResource(() => promise);
73
+ return () => {
74
+ const Comp = res();
75
+ return Comp ? Comp(props) : null;
76
+ };
77
+ }) as LazyComponent<T>;
78
+
79
+ LazyWrapper.preload = preload;
80
+
81
+ return LazyWrapper;
82
+ }