@sigx/terminal 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -16,15 +16,17 @@ function batch(fn) {
16
16
  }
17
17
  }
18
18
  function runEffect(fn) {
19
- const effect$1 = function() {
20
- cleanup(effect$1);
21
- activeEffect = effect$1;
19
+ const effectFn = function() {
20
+ cleanup(effectFn);
21
+ activeEffect = effectFn;
22
22
  fn();
23
23
  activeEffect = null;
24
24
  };
25
- effect$1.deps = [];
26
- effect$1();
27
- return () => cleanup(effect$1);
25
+ effectFn.deps = [];
26
+ effectFn();
27
+ const runner = (() => effectFn());
28
+ runner.stop = () => cleanup(effectFn);
29
+ return runner;
28
30
  }
29
31
  function cleanup(effect$1) {
30
32
  if (!effect$1.deps) return;
@@ -158,8 +160,16 @@ function watch(source, cb, options) {
158
160
  let oldValue;
159
161
  let isFirst = true;
160
162
  let cleanupFn = null;
163
+ let paused = false;
164
+ let pendingValue;
165
+ let hasPending = false;
161
166
  const runner = effect(() => {
162
167
  const newValue = typeof source === "function" ? source() : source;
168
+ if (paused) {
169
+ pendingValue = newValue;
170
+ hasPending = true;
171
+ return;
172
+ }
163
173
  if (isFirst) {
164
174
  if (options?.immediate) {
165
175
  if (cleanupFn) cleanupFn();
@@ -173,14 +183,28 @@ function watch(source, cb, options) {
173
183
  oldValue = newValue;
174
184
  });
175
185
  const stop = () => {
176
- runner();
186
+ runner.stop();
177
187
  if (cleanupFn) cleanupFn();
178
188
  };
179
- const handle = stop;
180
- handle.stop = stop;
181
- handle.pause = () => {};
182
- handle.resume = () => {};
183
- return handle;
189
+ const pause = () => {
190
+ paused = true;
191
+ };
192
+ const resume = () => {
193
+ if (!paused) return;
194
+ paused = false;
195
+ if (hasPending && !Object.is(pendingValue, oldValue)) {
196
+ if (cleanupFn) cleanupFn();
197
+ cb(pendingValue, oldValue, (fn) => cleanupFn = fn);
198
+ oldValue = pendingValue;
199
+ }
200
+ hasPending = false;
201
+ pendingValue = void 0;
202
+ };
203
+ return Object.assign(stop, {
204
+ stop,
205
+ pause,
206
+ resume
207
+ });
184
208
  }
185
209
  function effectScope(detached) {
186
210
  const effects = [];
@@ -216,15 +240,15 @@ function getPlatformSyncProcessor() {
216
240
 
217
241
  //#endregion
218
242
  //#region ../runtime-core/src/plugins.ts
219
- const plugins = [];
243
+ const plugins$1 = [];
220
244
  function registerComponentPlugin(plugin) {
221
- plugins.push(plugin);
245
+ plugins$1.push(plugin);
222
246
  }
223
247
  /**
224
248
  * Get all registered plugins (internal use)
225
249
  */
226
250
  function getComponentPlugins() {
227
- return plugins;
251
+ return plugins$1;
228
252
  }
229
253
 
230
254
  //#endregion
@@ -442,12 +466,12 @@ function onCleanup(fn) {
442
466
  if (currentComponentContext) currentComponentContext.onCleanup(fn);
443
467
  else console.warn("onCleanup called outside of component setup");
444
468
  }
445
- const componentRegistry = /* @__PURE__ */ new Map();
469
+ const componentRegistry$1 = /* @__PURE__ */ new Map();
446
470
  /**
447
471
  * Get component metadata (for DevTools)
448
472
  */
449
473
  function getComponentMeta(factory) {
450
- return componentRegistry.get(factory);
474
+ return componentRegistry$1.get(factory);
451
475
  }
452
476
  /**
453
477
  * Helper to create a proxy that tracks property access
@@ -497,7 +521,7 @@ function defineComponent(setup, options) {
497
521
  factory.__events = null;
498
522
  factory.__ref = null;
499
523
  factory.__slots = null;
500
- componentRegistry.set(factory, {
524
+ componentRegistry$1.set(factory, {
501
525
  name: options?.name,
502
526
  setup
503
527
  });
@@ -603,6 +627,190 @@ function jsxs(type, props, key) {
603
627
  }
604
628
  const jsxDEV = jsx;
605
629
 
630
+ //#endregion
631
+ //#region ../runtime-core/src/lazy.tsx
632
+ /**
633
+ * Lazy loading utilities for sigx components.
634
+ *
635
+ * Provides runtime-only lazy loading with no build dependencies.
636
+ * Works with any bundler that supports dynamic import().
637
+ */
638
+ let currentSuspenseBoundary$1 = null;
639
+ /**
640
+ * Register a promise with the current Suspense boundary
641
+ * @internal
642
+ */
643
+ function registerPendingPromise(promise) {
644
+ const boundary = currentSuspenseBoundary$1;
645
+ if (boundary) {
646
+ boundary.pending.add(promise);
647
+ promise.finally(() => {
648
+ boundary.pending.delete(promise);
649
+ if (boundary.pending.size === 0) boundary.onResolve();
650
+ });
651
+ return true;
652
+ }
653
+ return false;
654
+ }
655
+ /**
656
+ * Create a lazy-loaded component wrapper.
657
+ *
658
+ * The component will be loaded on first render. Use with `<Suspense>` to show
659
+ * a fallback while loading.
660
+ *
661
+ * @param loader - Function that returns a Promise resolving to the component
662
+ * @returns A component factory that loads the real component on demand
663
+ *
664
+ * @example
665
+ * ```tsx
666
+ * import { lazy, Suspense } from 'sigx';
667
+ *
668
+ * // Component will be in a separate chunk
669
+ * const HeavyChart = lazy(() => import('./components/HeavyChart'));
670
+ *
671
+ * // Usage
672
+ * <Suspense fallback={<Spinner />}>
673
+ * <HeavyChart data={chartData} />
674
+ * </Suspense>
675
+ *
676
+ * // Preload on hover
677
+ * <button onMouseEnter={() => HeavyChart.preload()}>
678
+ * Show Chart
679
+ * </button>
680
+ * ```
681
+ */
682
+ function lazy(loader) {
683
+ let Component = null;
684
+ let promise = null;
685
+ let error = null;
686
+ let state = "pending";
687
+ const LazyWrapper = defineComponent((ctx) => {
688
+ const loadState = ctx.signal({
689
+ state,
690
+ tick: 0
691
+ });
692
+ if (!promise) promise = loader().then((mod) => {
693
+ Component = "default" in mod ? mod.default : mod;
694
+ state = "resolved";
695
+ loadState.state = "resolved";
696
+ loadState.tick++;
697
+ return Component;
698
+ }).catch((err) => {
699
+ error = err instanceof Error ? err : new Error(String(err));
700
+ state = "rejected";
701
+ loadState.state = "rejected";
702
+ loadState.tick++;
703
+ throw error;
704
+ });
705
+ if (state === "resolved" && Component) return () => {
706
+ return jsx(Component, {});
707
+ };
708
+ if (state === "rejected" && error) throw error;
709
+ if (!registerPendingPromise(promise)) promise.catch(() => {});
710
+ return () => {
711
+ const currentState = loadState.state;
712
+ loadState.tick;
713
+ if (currentState === "resolved" && Component) return jsx(Component, {});
714
+ if (currentState === "rejected" && error) throw error;
715
+ return null;
716
+ };
717
+ }, { name: "LazyComponent" });
718
+ LazyWrapper.__lazy = true;
719
+ LazyWrapper.preload = () => {
720
+ if (!promise) promise = loader().then((mod) => {
721
+ Component = "default" in mod ? mod.default : mod;
722
+ state = "resolved";
723
+ return Component;
724
+ }).catch((err) => {
725
+ error = err instanceof Error ? err : new Error(String(err));
726
+ state = "rejected";
727
+ throw error;
728
+ });
729
+ return promise;
730
+ };
731
+ LazyWrapper.isLoaded = () => {
732
+ return state === "resolved";
733
+ };
734
+ return LazyWrapper;
735
+ }
736
+ /**
737
+ * Suspense boundary component for handling async loading states.
738
+ *
739
+ * Wraps lazy-loaded components and shows a fallback while they load.
740
+ *
741
+ * @example
742
+ * ```tsx
743
+ * import { lazy, Suspense } from 'sigx';
744
+ *
745
+ * const LazyDashboard = lazy(() => import('./Dashboard'));
746
+ *
747
+ * // Basic usage
748
+ * <Suspense fallback={<div>Loading...</div>}>
749
+ * <LazyDashboard />
750
+ * </Suspense>
751
+ *
752
+ * // With spinner component
753
+ * <Suspense fallback={<Spinner size="large" />}>
754
+ * <LazyDashboard />
755
+ * <LazyCharts />
756
+ * </Suspense>
757
+ * ```
758
+ */
759
+ const Suspense = defineComponent((ctx) => {
760
+ const { props, slots } = ctx;
761
+ const state = ctx.signal({
762
+ isReady: false,
763
+ pendingCount: 0
764
+ });
765
+ const boundary = {
766
+ pending: /* @__PURE__ */ new Set(),
767
+ onResolve: () => {
768
+ state.pendingCount = boundary.pending.size;
769
+ if (boundary.pending.size === 0) state.isReady = true;
770
+ }
771
+ };
772
+ ctx.onMount(() => {
773
+ if (boundary.pending.size === 0) state.isReady = true;
774
+ });
775
+ return () => {
776
+ state.isReady;
777
+ state.pendingCount;
778
+ const prevBoundary = currentSuspenseBoundary$1;
779
+ currentSuspenseBoundary$1 = boundary;
780
+ try {
781
+ const children = slots.default();
782
+ if (boundary.pending.size > 0) {
783
+ const fallback = props.fallback;
784
+ if (typeof fallback === "function") return fallback();
785
+ return fallback ?? null;
786
+ }
787
+ if (Array.isArray(children)) {
788
+ const filtered = children.filter((c) => c != null && c !== false && c !== true);
789
+ if (filtered.length === 0) return null;
790
+ if (filtered.length === 1) return filtered[0];
791
+ return filtered;
792
+ }
793
+ return children;
794
+ } catch (err) {
795
+ if (err instanceof Promise) {
796
+ registerPendingPromise(err);
797
+ const fallback = props.fallback;
798
+ if (typeof fallback === "function") return fallback();
799
+ return fallback ?? null;
800
+ }
801
+ throw err;
802
+ } finally {
803
+ currentSuspenseBoundary$1 = prevBoundary;
804
+ }
805
+ };
806
+ }, { name: "Suspense" });
807
+ /**
808
+ * Check if a component is a lazy-loaded component
809
+ */
810
+ function isLazyComponent(component) {
811
+ return component && component.__lazy === true;
812
+ }
813
+
606
814
  //#endregion
607
815
  //#region ../runtime-core/src/utils/index.ts
608
816
  var Utils = class {
@@ -913,7 +1121,6 @@ function isComponent(type) {
913
1121
  }
914
1122
  function createRenderer(options) {
915
1123
  const { insert: hostInsert, remove: hostRemove, patchProp: hostPatchProp, createElement: hostCreateElement, createText: hostCreateText, createComment: hostCreateComment, setText: hostSetText, setElementText: hostSetElementText, parentNode: hostParentNode, nextSibling: hostNextSibling, cloneNode: hostCloneNode, insertStaticContent: hostInsertStaticContent } = options;
916
- let isPatching = false;
917
1124
  let currentAppContext = null;
918
1125
  function render$1(element, container, appContext) {
919
1126
  if (appContext) currentAppContext = appContext;
@@ -927,6 +1134,13 @@ function createRenderer(options) {
927
1134
  dom: null,
928
1135
  text: element
929
1136
  };
1137
+ else if (isComponent(element)) vnode = {
1138
+ type: element,
1139
+ props: {},
1140
+ key: null,
1141
+ children: [],
1142
+ dom: null
1143
+ };
930
1144
  else vnode = element;
931
1145
  if (vnode) {
932
1146
  if (oldVNode) patch(oldVNode, vnode, container);
@@ -938,6 +1152,7 @@ function createRenderer(options) {
938
1152
  }
939
1153
  }
940
1154
  function mount(vnode, container, before = null) {
1155
+ if (vnode == null || vnode === false || vnode === true) return;
941
1156
  if (vnode.type === Text) {
942
1157
  const node = hostCreateText(String(vnode.text));
943
1158
  vnode.dom = node;
@@ -949,7 +1164,7 @@ function createRenderer(options) {
949
1164
  const anchor = hostCreateComment("");
950
1165
  vnode.dom = anchor;
951
1166
  hostInsert(anchor, container, before);
952
- vnode.children.forEach((child) => mount(child, container, anchor));
1167
+ if (vnode.children) vnode.children.forEach((child) => mount(child, container, anchor));
953
1168
  return;
954
1169
  }
955
1170
  if (isComponent(vnode.type)) {
@@ -966,17 +1181,18 @@ function createRenderer(options) {
966
1181
  else if (typeof vnode.props.ref === "object") vnode.props.ref.current = element;
967
1182
  }
968
1183
  }
969
- vnode.children.forEach((child) => {
1184
+ if (vnode.children) vnode.children.forEach((child) => {
970
1185
  child.parent = vnode;
971
1186
  mount(child, element);
972
1187
  });
973
1188
  hostInsert(element, container, before);
974
1189
  }
975
1190
  function unmount(vnode, container) {
976
- if (vnode._effect) vnode._effect();
1191
+ const internalVNode = vnode;
1192
+ if (internalVNode._effect) internalVNode._effect.stop();
977
1193
  if (vnode.cleanup) vnode.cleanup();
978
1194
  if (isComponent(vnode.type)) {
979
- const subTree = vnode._subTree;
1195
+ const subTree = internalVNode._subTree;
980
1196
  if (subTree) unmount(subTree, container);
981
1197
  if (vnode.dom) hostRemove(vnode.dom);
982
1198
  if (vnode.props?.ref) {
@@ -986,7 +1202,7 @@ function createRenderer(options) {
986
1202
  return;
987
1203
  }
988
1204
  if (vnode.type === Fragment) {
989
- vnode.children.forEach((child) => unmount(child, container));
1205
+ if (vnode.children) vnode.children.forEach((child) => unmount(child, container));
990
1206
  if (vnode.dom) hostRemove(vnode.dom);
991
1207
  return;
992
1208
  }
@@ -1006,13 +1222,15 @@ function createRenderer(options) {
1006
1222
  mount(newVNode, parent, nextSibling);
1007
1223
  return;
1008
1224
  }
1009
- if (oldVNode._effect) {
1225
+ const oldInternal = oldVNode;
1226
+ const newInternal = newVNode;
1227
+ if (oldInternal._effect) {
1010
1228
  newVNode.dom = oldVNode.dom;
1011
- newVNode._effect = oldVNode._effect;
1012
- newVNode._subTree = oldVNode._subTree;
1013
- newVNode._slots = oldVNode._slots;
1014
- const props = oldVNode._componentProps;
1015
- newVNode._componentProps = props;
1229
+ newInternal._effect = oldInternal._effect;
1230
+ newInternal._subTree = oldInternal._subTree;
1231
+ newInternal._slots = oldInternal._slots;
1232
+ const props = oldInternal._componentProps;
1233
+ newInternal._componentProps = props;
1016
1234
  if (props) {
1017
1235
  const newProps$1 = newVNode.props || {};
1018
1236
  untrack(() => {
@@ -1022,20 +1240,20 @@ function createRenderer(options) {
1022
1240
  for (const key in props) if (!(key in newProps$1) && key !== "children" && key !== "key" && key !== "ref") delete props[key];
1023
1241
  });
1024
1242
  }
1025
- const slotsRef = oldVNode._slots;
1243
+ const slotsRef = oldInternal._slots;
1026
1244
  const newChildren = newVNode.props?.children;
1027
1245
  const newSlotsFromProps = newVNode.props?.slots;
1028
1246
  if (slotsRef) {
1029
1247
  if (newChildren !== void 0) slotsRef._children = newChildren;
1030
1248
  if (newSlotsFromProps !== void 0) slotsRef._slotsFromProps = newSlotsFromProps;
1031
- if (!isPatching) {
1032
- isPatching = true;
1249
+ if (!slotsRef._isPatching) {
1250
+ slotsRef._isPatching = true;
1033
1251
  try {
1034
1252
  untrack(() => {
1035
1253
  slotsRef._version.v++;
1036
1254
  });
1037
1255
  } finally {
1038
- isPatching = false;
1256
+ slotsRef._isPatching = false;
1039
1257
  }
1040
1258
  }
1041
1259
  }
@@ -1140,6 +1358,43 @@ function createRenderer(options) {
1140
1358
  for (let i = beginIdx; i <= endIdx; i++) if (children[i] && isSameVNode(children[i], newChild)) return i;
1141
1359
  return null;
1142
1360
  }
1361
+ /**
1362
+ * Creates a props accessor that can be called with defaults or accessed directly.
1363
+ * After calling with defaults, direct property access uses those defaults.
1364
+ */
1365
+ function createPropsAccessor(reactiveProps) {
1366
+ let defaults = {};
1367
+ const proxy = new Proxy(function propsAccessor() {}, {
1368
+ get(_, key) {
1369
+ if (typeof key === "symbol") return void 0;
1370
+ const value = reactiveProps[key];
1371
+ return value != null ? value : defaults[key];
1372
+ },
1373
+ apply(_, __, args) {
1374
+ if (args[0] && typeof args[0] === "object") defaults = {
1375
+ ...defaults,
1376
+ ...args[0]
1377
+ };
1378
+ return proxy;
1379
+ },
1380
+ has(_, key) {
1381
+ if (typeof key === "symbol") return false;
1382
+ return key in reactiveProps || key in defaults;
1383
+ },
1384
+ ownKeys() {
1385
+ return [...new Set([...Object.keys(reactiveProps), ...Object.keys(defaults)])];
1386
+ },
1387
+ getOwnPropertyDescriptor(_, key) {
1388
+ if (typeof key === "symbol") return void 0;
1389
+ if (key in reactiveProps || key in defaults) return {
1390
+ enumerable: true,
1391
+ configurable: true,
1392
+ writable: false
1393
+ };
1394
+ }
1395
+ });
1396
+ return proxy;
1397
+ }
1143
1398
  function mountComponent(vnode, container, before, setup) {
1144
1399
  const anchor = hostCreateComment("");
1145
1400
  vnode.dom = anchor;
@@ -1149,9 +1404,10 @@ function createRenderer(options) {
1149
1404
  let exposeCalled = false;
1150
1405
  const { children, slots: slotsFromProps, ...propsData } = vnode.props || {};
1151
1406
  const reactiveProps = signal(propsData);
1152
- vnode._componentProps = reactiveProps;
1407
+ const internalVNode = vnode;
1408
+ internalVNode._componentProps = reactiveProps;
1153
1409
  const slots = createSlots(children, slotsFromProps);
1154
- vnode._slots = slots;
1410
+ internalVNode._slots = slots;
1155
1411
  const mountHooks = [];
1156
1412
  const cleanupHooks = [];
1157
1413
  const parentInstance = getCurrentInstance();
@@ -1159,7 +1415,7 @@ function createRenderer(options) {
1159
1415
  const ctx = {
1160
1416
  el: container,
1161
1417
  signal,
1162
- props: reactiveProps,
1418
+ props: createPropsAccessor(reactiveProps),
1163
1419
  slots,
1164
1420
  emit: (event, ...args) => {
1165
1421
  const handler = reactiveProps[`on${event[0].toUpperCase() + event.slice(1)}`];
@@ -1175,7 +1431,9 @@ function createRenderer(options) {
1175
1431
  expose: (exposedValue) => {
1176
1432
  exposed = exposedValue;
1177
1433
  exposeCalled = true;
1178
- }
1434
+ },
1435
+ renderFn: null,
1436
+ update: () => {}
1179
1437
  };
1180
1438
  ctx.__name = componentName;
1181
1439
  if (currentAppContext) ctx._appContext = currentAppContext;
@@ -1199,24 +1457,31 @@ function createRenderer(options) {
1199
1457
  if (typeof vnode.props.ref === "function") vnode.props.ref(refValue);
1200
1458
  else if (vnode.props.ref && typeof vnode.props.ref === "object") vnode.props.ref.current = refValue;
1201
1459
  }
1202
- if (renderFn) vnode._effect = effect(() => {
1203
- const prevInstance = setCurrentInstance(ctx);
1204
- try {
1205
- const subTreeResult = renderFn();
1206
- if (subTreeResult == null) return;
1207
- const subTree = normalizeSubTree(subTreeResult);
1208
- const prevSubTree = vnode._subTree;
1209
- if (prevSubTree) {
1210
- patch(prevSubTree, subTree, container);
1211
- notifyComponentUpdated(currentAppContext, componentInstance);
1212
- } else mount(subTree, container, anchor);
1213
- vnode._subTree = subTree;
1214
- } catch (err) {
1215
- if (!handleComponentError(currentAppContext, err, componentInstance, "render")) throw err;
1216
- } finally {
1217
- setCurrentInstance(prevInstance);
1218
- }
1219
- });
1460
+ if (renderFn) {
1461
+ ctx.renderFn = renderFn;
1462
+ const componentEffect = effect(() => {
1463
+ const prevInstance = setCurrentInstance(ctx);
1464
+ try {
1465
+ const subTreeResult = ctx.renderFn();
1466
+ if (subTreeResult == null) return;
1467
+ const subTree = normalizeSubTree(subTreeResult);
1468
+ const prevSubTree = internalVNode._subTree;
1469
+ if (prevSubTree) {
1470
+ patch(prevSubTree, subTree, container);
1471
+ notifyComponentUpdated(currentAppContext, componentInstance);
1472
+ } else mount(subTree, container, anchor);
1473
+ internalVNode._subTree = subTree;
1474
+ } catch (err) {
1475
+ if (!handleComponentError(currentAppContext, err, componentInstance, "render")) throw err;
1476
+ } finally {
1477
+ setCurrentInstance(prevInstance);
1478
+ }
1479
+ });
1480
+ internalVNode._effect = componentEffect;
1481
+ ctx.update = () => {
1482
+ componentEffect();
1483
+ };
1484
+ }
1220
1485
  const mountCtx = { el: container };
1221
1486
  mountHooks.forEach((hook) => hook(mountCtx));
1222
1487
  notifyComponentMounted(currentAppContext, componentInstance);
@@ -1256,11 +1521,12 @@ function createRenderer(options) {
1256
1521
  _children: children,
1257
1522
  _slotsFromProps: slotsFromProps || {},
1258
1523
  _version: versionSignal,
1524
+ _isPatching: false,
1259
1525
  default: function() {
1260
1526
  this._version.v;
1261
1527
  const c = this._children;
1262
1528
  const { defaultChildren } = extractNamedSlotsFromChildren(c);
1263
- return defaultChildren;
1529
+ return defaultChildren.filter((child) => child != null && child !== false && child !== true);
1264
1530
  }
1265
1531
  };
1266
1532
  return new Proxy(slotsObj, { get(target, prop) {
@@ -1279,6 +1545,8 @@ function createRenderer(options) {
1279
1545
  }
1280
1546
  /**
1281
1547
  * Normalize render result to a VNode (wrapping arrays in Fragment)
1548
+ * Note: Falsy values (null, undefined, false, true) from conditional rendering
1549
+ * are handled by mount() which guards against them, so no filtering needed here.
1282
1550
  */
1283
1551
  function normalizeSubTree(result) {
1284
1552
  if (Array.isArray(result)) return {
@@ -1384,6 +1652,60 @@ let platformSyncProcessor = null;
1384
1652
  function getPlatformSyncProcessor$1() {
1385
1653
  return platformSyncProcessor;
1386
1654
  }
1655
+ const plugins = [];
1656
+ /**
1657
+ * Get all registered plugins (internal use)
1658
+ */
1659
+ function getComponentPlugins$1() {
1660
+ return plugins;
1661
+ }
1662
+ const componentRegistry = /* @__PURE__ */ new Map();
1663
+ /**
1664
+ * Define a component. Returns a JSX factory function.
1665
+ *
1666
+ * @param setup - Setup function that receives context and returns a render function
1667
+ * @param options - Optional configuration (e.g., name for DevTools)
1668
+ *
1669
+ * @example
1670
+ * ```tsx
1671
+ * type CardProps = DefineProp<"title", string> & DefineSlot<"header">;
1672
+ *
1673
+ * export const Card = defineComponent<CardProps>((ctx) => {
1674
+ * const { title } = ctx.props;
1675
+ * const { slots } = ctx;
1676
+ *
1677
+ * return () => (
1678
+ * <div class="card">
1679
+ * {slots.header?.() ?? <h2>{title}</h2>}
1680
+ * {slots.default()}
1681
+ * </div>
1682
+ * );
1683
+ * });
1684
+ * ```
1685
+ */
1686
+ function defineComponent$1(setup, options) {
1687
+ const factory = function(props) {
1688
+ return {
1689
+ type: factory,
1690
+ props: props || {},
1691
+ key: props?.key || null,
1692
+ children: [],
1693
+ dom: null
1694
+ };
1695
+ };
1696
+ factory.__setup = setup;
1697
+ factory.__name = options?.name;
1698
+ factory.__props = null;
1699
+ factory.__events = null;
1700
+ factory.__ref = null;
1701
+ factory.__slots = null;
1702
+ componentRegistry.set(factory, {
1703
+ name: options?.name,
1704
+ setup
1705
+ });
1706
+ getComponentPlugins$1().forEach((p) => p.onDefine?.(options?.name, factory, setup));
1707
+ return factory;
1708
+ }
1387
1709
  const Fragment$1 = Symbol.for("sigx.Fragment");
1388
1710
  const Text$1 = Symbol.for("sigx.Text");
1389
1711
  function normalizeChildren(children) {
@@ -1478,6 +1800,100 @@ function jsx$1(type, props, key) {
1478
1800
  function jsxs$1(type, props, key) {
1479
1801
  return jsx$1(type, props, key);
1480
1802
  }
1803
+ /**
1804
+ * Lazy loading utilities for sigx components.
1805
+ *
1806
+ * Provides runtime-only lazy loading with no build dependencies.
1807
+ * Works with any bundler that supports dynamic import().
1808
+ */
1809
+ let currentSuspenseBoundary = null;
1810
+ /**
1811
+ * Register a promise with the current Suspense boundary
1812
+ * @internal
1813
+ */
1814
+ function registerPendingPromise$1(promise) {
1815
+ const boundary = currentSuspenseBoundary;
1816
+ if (boundary) {
1817
+ boundary.pending.add(promise);
1818
+ promise.finally(() => {
1819
+ boundary.pending.delete(promise);
1820
+ if (boundary.pending.size === 0) boundary.onResolve();
1821
+ });
1822
+ return true;
1823
+ }
1824
+ return false;
1825
+ }
1826
+ /**
1827
+ * Suspense boundary component for handling async loading states.
1828
+ *
1829
+ * Wraps lazy-loaded components and shows a fallback while they load.
1830
+ *
1831
+ * @example
1832
+ * ```tsx
1833
+ * import { lazy, Suspense } from 'sigx';
1834
+ *
1835
+ * const LazyDashboard = lazy(() => import('./Dashboard'));
1836
+ *
1837
+ * // Basic usage
1838
+ * <Suspense fallback={<div>Loading...</div>}>
1839
+ * <LazyDashboard />
1840
+ * </Suspense>
1841
+ *
1842
+ * // With spinner component
1843
+ * <Suspense fallback={<Spinner size="large" />}>
1844
+ * <LazyDashboard />
1845
+ * <LazyCharts />
1846
+ * </Suspense>
1847
+ * ```
1848
+ */
1849
+ const Suspense$1 = defineComponent$1((ctx) => {
1850
+ const { props, slots } = ctx;
1851
+ const state = ctx.signal({
1852
+ isReady: false,
1853
+ pendingCount: 0
1854
+ });
1855
+ const boundary = {
1856
+ pending: /* @__PURE__ */ new Set(),
1857
+ onResolve: () => {
1858
+ state.pendingCount = boundary.pending.size;
1859
+ if (boundary.pending.size === 0) state.isReady = true;
1860
+ }
1861
+ };
1862
+ ctx.onMount(() => {
1863
+ if (boundary.pending.size === 0) state.isReady = true;
1864
+ });
1865
+ return () => {
1866
+ state.isReady;
1867
+ state.pendingCount;
1868
+ const prevBoundary = currentSuspenseBoundary;
1869
+ currentSuspenseBoundary = boundary;
1870
+ try {
1871
+ const children = slots.default();
1872
+ if (boundary.pending.size > 0) {
1873
+ const fallback = props.fallback;
1874
+ if (typeof fallback === "function") return fallback();
1875
+ return fallback ?? null;
1876
+ }
1877
+ if (Array.isArray(children)) {
1878
+ const filtered = children.filter((c) => c != null && c !== false && c !== true);
1879
+ if (filtered.length === 0) return null;
1880
+ if (filtered.length === 1) return filtered[0];
1881
+ return filtered;
1882
+ }
1883
+ return children;
1884
+ } catch (err) {
1885
+ if (err instanceof Promise) {
1886
+ registerPendingPromise$1(err);
1887
+ const fallback = props.fallback;
1888
+ if (typeof fallback === "function") return fallback();
1889
+ return fallback ?? null;
1890
+ }
1891
+ throw err;
1892
+ } finally {
1893
+ currentSuspenseBoundary = prevBoundary;
1894
+ }
1895
+ };
1896
+ }, { name: "Suspense" });
1481
1897
 
1482
1898
  //#endregion
1483
1899
  //#region ../runtime-terminal/src/components/Input.tsx
@@ -2024,5 +2440,5 @@ const terminalMount = (component, options, appContext) => {
2024
2440
  setDefaultMount(terminalMount);
2025
2441
 
2026
2442
  //#endregion
2027
- export { AppContextKey, Button, Checkbox, Fragment, Input, InstanceLifetimes, ProgressBar, Select, SubscriptionHandler, Text, Utils, batch, createApp, createPropsProxy, createRenderer, createTopic, defineApp, defineComponent, defineFactory, defineInjectable, defineProvide, defineStore, detectAccess, effect, effectScope, exitTerminal, focus, focusNext, focusPrev, focusState, getComponentMeta, getComponentPlugins, getCurrentInstance, getDefaultMount, getPlatformSyncProcessor, guid, handleComponentError, inject, injectApp, jsx, jsxDEV, jsxs, mountTerminal, notifyComponentCreated, notifyComponentMounted, notifyComponentUnmounted, notifyComponentUpdated, onCleanup, onKey, onMount, provide, registerComponentPlugin, registerFocusable, render, renderNodeToLines, renderTerminal, setCurrentInstance, setDefaultMount, setPlatformSyncProcessor, signal, terminalMount, toSubscriber, unregisterFocusable, untrack, valueOf, watch };
2443
+ export { AppContextKey, Button, Checkbox, Fragment, Input, InstanceLifetimes, ProgressBar, Select, SubscriptionHandler, Suspense, Text, Utils, batch, createApp, createPropsProxy, createRenderer, createTopic, defineApp, defineComponent, defineFactory, defineInjectable, defineProvide, defineStore, detectAccess, effect, effectScope, exitTerminal, focus, focusNext, focusPrev, focusState, getComponentMeta, getComponentPlugins, getCurrentInstance, getDefaultMount, getPlatformSyncProcessor, guid, handleComponentError, inject, injectApp, isLazyComponent, jsx, jsxDEV, jsxs, lazy, mountTerminal, notifyComponentCreated, notifyComponentMounted, notifyComponentUnmounted, notifyComponentUpdated, onCleanup, onKey, onMount, provide, registerComponentPlugin, registerFocusable, registerPendingPromise, render, renderNodeToLines, renderTerminal, setCurrentInstance, setDefaultMount, setPlatformSyncProcessor, signal, terminalMount, toSubscriber, unregisterFocusable, untrack, valueOf, watch };
2028
2444
  //# sourceMappingURL=index.js.map