@sigx/terminal 0.1.4 → 0.1.5

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
@@ -250,6 +250,29 @@ function registerComponentPlugin(plugin) {
250
250
  function getComponentPlugins() {
251
251
  return plugins$1;
252
252
  }
253
+ const contextExtensions = [];
254
+ /**
255
+ * Register a function that will be called to extend every component context.
256
+ * Extensions are called in order of registration.
257
+ *
258
+ * @example
259
+ * ```ts
260
+ * // In @sigx/server-renderer/client
261
+ * registerContextExtension((ctx) => {
262
+ * ctx.ssr = { load: () => {} };
263
+ * });
264
+ * ```
265
+ */
266
+ function registerContextExtension(extension) {
267
+ contextExtensions.push(extension);
268
+ }
269
+ /**
270
+ * Apply all registered context extensions to a context object.
271
+ * Called internally by the renderer when creating component contexts.
272
+ */
273
+ function applyContextExtensions(ctx) {
274
+ for (const extension of contextExtensions) extension(ctx);
275
+ }
253
276
 
254
277
  //#endregion
255
278
  //#region ../runtime-core/src/app.ts
@@ -825,6 +848,195 @@ function guid$1() {
825
848
  });
826
849
  }
827
850
 
851
+ //#endregion
852
+ //#region ../runtime-core/src/utils/props-accessor.ts
853
+ /**
854
+ * Creates a props accessor that can be called with defaults or accessed directly.
855
+ * After calling with defaults, direct property access uses those defaults.
856
+ *
857
+ * @example
858
+ * ```ts
859
+ * // In component setup:
860
+ * const props = createPropsAccessor(reactiveProps);
861
+ *
862
+ * // Set defaults
863
+ * props({ count: 0, label: 'Default' });
864
+ *
865
+ * // Access props (falls back to defaults if not provided)
866
+ * const count = props.count;
867
+ * ```
868
+ */
869
+ function createPropsAccessor(reactiveProps) {
870
+ let defaults = {};
871
+ const proxy = new Proxy(function propsAccessor() {}, {
872
+ get(_, key) {
873
+ if (typeof key === "symbol") return void 0;
874
+ const value = reactiveProps[key];
875
+ return value != null ? value : defaults[key];
876
+ },
877
+ apply(_, __, args) {
878
+ if (args[0] && typeof args[0] === "object") defaults = {
879
+ ...defaults,
880
+ ...args[0]
881
+ };
882
+ return proxy;
883
+ },
884
+ has(_, key) {
885
+ if (typeof key === "symbol") return false;
886
+ return key in reactiveProps || key in defaults;
887
+ },
888
+ ownKeys() {
889
+ return [...new Set([...Object.keys(reactiveProps), ...Object.keys(defaults)])];
890
+ },
891
+ getOwnPropertyDescriptor(_, key) {
892
+ if (typeof key === "symbol") return void 0;
893
+ if (key in reactiveProps || key in defaults) return {
894
+ enumerable: true,
895
+ configurable: true,
896
+ writable: false
897
+ };
898
+ }
899
+ });
900
+ return proxy;
901
+ }
902
+
903
+ //#endregion
904
+ //#region ../runtime-core/src/utils/slots.ts
905
+ /**
906
+ * Slots system for component children.
907
+ * Supports default and named slots with reactivity.
908
+ */
909
+ /**
910
+ * Create slots object from children and slots prop.
911
+ * Uses a version signal to trigger re-renders when children change.
912
+ *
913
+ * Supports named slots via:
914
+ * - `slots` prop object (e.g., `slots={{ header: () => <div>...</div> }}`)
915
+ * - `slot` prop on children (e.g., `<div slot="header">...</div>`)
916
+ *
917
+ * @example
918
+ * ```tsx
919
+ * // Parent component
920
+ * <Card slots={{ header: () => <h1>Title</h1> }}>
921
+ * <p>Default content</p>
922
+ * <span slot="footer">Footer text</span>
923
+ * </Card>
924
+ *
925
+ * // Card component setup
926
+ * const slots = createSlots(children, slotsFromProps);
927
+ * return () => (
928
+ * <div>
929
+ * {slots.header()}
930
+ * {slots.default()}
931
+ * {slots.footer()}
932
+ * </div>
933
+ * );
934
+ * ```
935
+ */
936
+ function createSlots(children, slotsFromProps) {
937
+ const versionSignal = signal({ v: 0 });
938
+ function extractNamedSlotsFromChildren(c) {
939
+ const defaultChildren = [];
940
+ const namedSlots = {};
941
+ if (c == null) return {
942
+ defaultChildren,
943
+ namedSlots
944
+ };
945
+ const items = Array.isArray(c) ? c : [c];
946
+ for (const child of items) if (child && typeof child === "object" && child.props && child.props.slot) {
947
+ const slotName = child.props.slot;
948
+ if (!namedSlots[slotName]) namedSlots[slotName] = [];
949
+ namedSlots[slotName].push(child);
950
+ } else defaultChildren.push(child);
951
+ return {
952
+ defaultChildren,
953
+ namedSlots
954
+ };
955
+ }
956
+ const slotsObj = {
957
+ _children: children,
958
+ _slotsFromProps: slotsFromProps || {},
959
+ _version: versionSignal,
960
+ _isPatching: false,
961
+ default: function() {
962
+ this._version.v;
963
+ const c = this._children;
964
+ const { defaultChildren } = extractNamedSlotsFromChildren(c);
965
+ return defaultChildren.filter((child) => child != null && child !== false && child !== true);
966
+ }
967
+ };
968
+ return new Proxy(slotsObj, { get(target, prop) {
969
+ if (prop in target) return target[prop];
970
+ if (typeof prop === "string") return function(scopedProps) {
971
+ target._version.v;
972
+ if (target._slotsFromProps && typeof target._slotsFromProps[prop] === "function") {
973
+ const result = target._slotsFromProps[prop](scopedProps);
974
+ if (result == null) return [];
975
+ return Array.isArray(result) ? result : [result];
976
+ }
977
+ const { namedSlots } = extractNamedSlotsFromChildren(target._children);
978
+ return namedSlots[prop] || [];
979
+ };
980
+ } });
981
+ }
982
+
983
+ //#endregion
984
+ //#region ../runtime-core/src/utils/normalize.ts
985
+ /**
986
+ * VNode normalization utilities.
987
+ * Converts render results into proper VNode structures.
988
+ */
989
+ /**
990
+ * Normalize render result to a VNode (wrapping arrays in Fragment).
991
+ * Handles null, undefined, false, true by returning an empty Text node.
992
+ *
993
+ * This is used to normalize the return value of component render functions
994
+ * into a consistent VNode structure for the renderer to process.
995
+ *
996
+ * @example
997
+ * ```ts
998
+ * // Conditional rendering returns null/false
999
+ * normalizeSubTree(null) // → empty Text node
1000
+ * normalizeSubTree(false) // → empty Text node
1001
+ *
1002
+ * // Arrays become Fragments
1003
+ * normalizeSubTree([<A/>, <B/>]) // → Fragment with children
1004
+ *
1005
+ * // Primitives become Text nodes
1006
+ * normalizeSubTree("hello") // → Text node
1007
+ * normalizeSubTree(42) // → Text node
1008
+ *
1009
+ * // VNodes pass through
1010
+ * normalizeSubTree(<div/>) // → same VNode
1011
+ * ```
1012
+ */
1013
+ function normalizeSubTree(result) {
1014
+ if (result == null || result === false || result === true) return {
1015
+ type: Text,
1016
+ props: {},
1017
+ key: null,
1018
+ children: [],
1019
+ dom: null,
1020
+ text: ""
1021
+ };
1022
+ if (Array.isArray(result)) return {
1023
+ type: Fragment,
1024
+ props: {},
1025
+ key: null,
1026
+ children: result,
1027
+ dom: null
1028
+ };
1029
+ if (typeof result === "string" || typeof result === "number") return {
1030
+ type: Text,
1031
+ props: {},
1032
+ key: null,
1033
+ children: [],
1034
+ dom: null,
1035
+ text: result
1036
+ };
1037
+ return result;
1038
+ }
1039
+
828
1040
  //#endregion
829
1041
  //#region ../runtime-core/src/models/index.ts
830
1042
  const guid = guid$1;
@@ -1358,43 +1570,6 @@ function createRenderer(options) {
1358
1570
  for (let i = beginIdx; i <= endIdx; i++) if (children[i] && isSameVNode(children[i], newChild)) return i;
1359
1571
  return null;
1360
1572
  }
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
- }
1398
1573
  function mountComponent(vnode, container, before, setup) {
1399
1574
  const anchor = hostCreateComment("");
1400
1575
  vnode.dom = anchor;
@@ -1435,6 +1610,7 @@ function createRenderer(options) {
1435
1610
  renderFn: null,
1436
1611
  update: () => {}
1437
1612
  };
1613
+ applyContextExtensions(ctx);
1438
1614
  ctx.__name = componentName;
1439
1615
  if (currentAppContext) ctx._appContext = currentAppContext;
1440
1616
  const componentInstance = {
@@ -1445,7 +1621,9 @@ function createRenderer(options) {
1445
1621
  const prev = setCurrentInstance(ctx);
1446
1622
  let renderFn;
1447
1623
  try {
1448
- renderFn = setup(ctx);
1624
+ const setupResult = setup(ctx);
1625
+ if (setupResult && typeof setupResult.then === "function") throw new Error(`Async setup in component "${componentName}" is only supported during SSR. On the client, use pre-loaded data from hydration or fetch in onMount.`);
1626
+ renderFn = setupResult;
1449
1627
  notifyComponentCreated(currentAppContext, componentInstance);
1450
1628
  } catch (err) {
1451
1629
  if (!handleComponentError(currentAppContext, err, componentInstance, "setup")) throw err;
@@ -1490,84 +1668,12 @@ function createRenderer(options) {
1490
1668
  cleanupHooks.forEach((hook) => hook(mountCtx));
1491
1669
  };
1492
1670
  }
1493
- /**
1494
- * Create slots object from children and slots prop.
1495
- * Uses a version signal to trigger re-renders when children change.
1496
- * Supports named slots via:
1497
- * - `slots` prop object (e.g., slots={{ header: () => <div>...</div> }})
1498
- * - `slot` prop on children (e.g., <div slot="header">...</div>)
1499
- */
1500
- function createSlots(children, slotsFromProps) {
1501
- const versionSignal = signal({ v: 0 });
1502
- function extractNamedSlotsFromChildren(c) {
1503
- const defaultChildren = [];
1504
- const namedSlots = {};
1505
- if (c == null) return {
1506
- defaultChildren,
1507
- namedSlots
1508
- };
1509
- const items = Array.isArray(c) ? c : [c];
1510
- for (const child of items) if (child && typeof child === "object" && child.props && child.props.slot) {
1511
- const slotName = child.props.slot;
1512
- if (!namedSlots[slotName]) namedSlots[slotName] = [];
1513
- namedSlots[slotName].push(child);
1514
- } else defaultChildren.push(child);
1515
- return {
1516
- defaultChildren,
1517
- namedSlots
1518
- };
1519
- }
1520
- const slotsObj = {
1521
- _children: children,
1522
- _slotsFromProps: slotsFromProps || {},
1523
- _version: versionSignal,
1524
- _isPatching: false,
1525
- default: function() {
1526
- this._version.v;
1527
- const c = this._children;
1528
- const { defaultChildren } = extractNamedSlotsFromChildren(c);
1529
- return defaultChildren.filter((child) => child != null && child !== false && child !== true);
1530
- }
1531
- };
1532
- return new Proxy(slotsObj, { get(target, prop) {
1533
- if (prop in target) return target[prop];
1534
- if (typeof prop === "string") return function(scopedProps) {
1535
- target._version.v;
1536
- if (target._slotsFromProps && typeof target._slotsFromProps[prop] === "function") {
1537
- const result = target._slotsFromProps[prop](scopedProps);
1538
- if (result == null) return [];
1539
- return Array.isArray(result) ? result : [result];
1540
- }
1541
- const { namedSlots } = extractNamedSlotsFromChildren(target._children);
1542
- return namedSlots[prop] || [];
1543
- };
1544
- } });
1545
- }
1546
- /**
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.
1550
- */
1551
- function normalizeSubTree(result) {
1552
- if (Array.isArray(result)) return {
1553
- type: Fragment,
1554
- props: {},
1555
- key: null,
1556
- children: result,
1557
- dom: null
1558
- };
1559
- if (typeof result === "string" || typeof result === "number") return {
1560
- type: Text,
1561
- props: {},
1562
- key: null,
1563
- children: [],
1564
- dom: null,
1565
- text: result
1566
- };
1567
- return result;
1568
- }
1569
1671
  return {
1570
1672
  render: render$1,
1673
+ patch,
1674
+ mount,
1675
+ unmount,
1676
+ mountComponent,
1571
1677
  createApp: (rootComponent) => {
1572
1678
  return { mount(selectorOrContainer) {
1573
1679
  let container = null;
@@ -2440,5 +2546,5 @@ const terminalMount = (component, options, appContext) => {
2440
2546
  setDefaultMount(terminalMount);
2441
2547
 
2442
2548
  //#endregion
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 };
2549
+ export { AppContextKey, Button, Checkbox, Fragment, Input, InstanceLifetimes, ProgressBar, Select, SubscriptionHandler, Suspense, Text, Utils, applyContextExtensions, batch, createApp, createPropsAccessor, createPropsProxy, createRenderer, createSlots, 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, normalizeSubTree, notifyComponentCreated, notifyComponentMounted, notifyComponentUnmounted, notifyComponentUpdated, onCleanup, onKey, onMount, provide, registerComponentPlugin, registerContextExtension, registerFocusable, registerPendingPromise, render, renderNodeToLines, renderTerminal, setCurrentInstance, setDefaultMount, setPlatformSyncProcessor, signal, terminalMount, toSubscriber, unregisterFocusable, untrack, valueOf, watch };
2444
2550
  //# sourceMappingURL=index.js.map