@continuum-dev/react 0.1.1 → 0.1.3

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/README.md CHANGED
@@ -311,6 +311,8 @@ Tracks non-data state (focus, expansion, scroll, zoom, offsets) inside the sessi
311
311
  const [viewport, setViewport] = useContinuumViewport('table');
312
312
  ```
313
313
 
314
+ If this hook is called from inside a collection-item scope, Continuum logs a development warning because viewport state is not currently scoped per collection item.
315
+
314
316
  #### `useContinuumSession()`
315
317
 
316
318
  Returns the active session for full session API access.
@@ -327,6 +329,8 @@ Subscribes to the full current `ContinuitySnapshot`.
327
329
  const snapshot = useContinuumSnapshot();
328
330
  ```
329
331
 
332
+ Snapshots are delivered as immutable top-level copies so consumer code cannot accidentally mutate session internals.
333
+
330
334
  #### `useContinuumHydrated()`
331
335
 
332
336
  Indicates whether provider initialization came from persisted storage.
@@ -343,6 +347,36 @@ Scans current snapshot values for suggestions and provides accept-all / reject-a
343
347
  const { hasSuggestions, acceptAll, rejectAll } = useContinuumSuggestions();
344
348
  ```
345
349
 
350
+ #### `useContinuumAction(intentId)`
351
+
352
+ Handles action dispatch with built-in loading and result state.
353
+
354
+ ```ts
355
+ const { dispatch, isDispatching, lastResult } = useContinuumAction('submit_form');
356
+ ```
357
+
358
+ When multiple dispatches overlap, `isDispatching` and `lastResult` reflect the latest in-flight dispatch.
359
+
360
+ Example action component:
361
+
362
+ ```tsx
363
+ function SubmitButton({ definition }: ContinuumNodeProps) {
364
+ const intentId = definition.intentId ?? '';
365
+ const { dispatch, isDispatching, lastResult } = useContinuumAction(intentId);
366
+
367
+ return (
368
+ <div>
369
+ <button disabled={isDispatching} onClick={() => dispatch(definition.id)}>
370
+ {isDispatching ? 'Working...' : definition.label}
371
+ </button>
372
+ {lastResult && (
373
+ <span>{lastResult.success ? 'Done' : 'Failed'}</span>
374
+ )}
375
+ </div>
376
+ );
377
+ }
378
+ ```
379
+
346
380
  ---
347
381
 
348
382
  ## The node contract
@@ -443,14 +477,15 @@ function UndoButton() {
443
477
  - scoped item state storage
444
478
  - default template values
445
479
  - canonical nested ids for collection children
480
+ - headless control wiring through your own collection components
446
481
 
447
- Rendered collection controls include attributes like:
482
+ Collection controls are now passed as props to your mapped components:
448
483
 
449
- - `data-continuum-collection-add`
450
- - `data-continuum-collection-remove`
451
- - `data-continuum-collection-item`
484
+ - collection root components receive `onAdd`, `canAdd`, `onRemove`, and `canRemove`
485
+ - template root components receive `itemIndex`, `onRemove`, and `canRemove`
486
+ - no renderer-owned wrapper elements or `data-continuum-*` control attributes are injected
452
487
 
453
- You get practical collection behavior out of the box while still controlling the visual wrapper component.
488
+ This keeps collection behavior built in while letting your design system fully own the markup and styles.
454
489
 
455
490
  ---
456
491
 
@@ -477,6 +512,7 @@ The fallback renders:
477
512
  Every rendered node is wrapped in `NodeErrorBoundary`.
478
513
 
479
514
  If one component crashes while rendering a dynamic node, sibling regions can keep working.
515
+ When a later rerender provides recoverable children, the boundary resets and the node can render again.
480
516
 
481
517
  ---
482
518
 
package/lib/context.d.ts CHANGED
@@ -2,22 +2,48 @@ import type { Session } from '@continuum-dev/session';
2
2
  import type { ContinuitySnapshot, NodeValue, ViewportState } from '@continuum-dev/contract';
3
3
  import type { ContinuumNodeMap, ContinuumProviderProps } from './types.js';
4
4
  type Listener = () => void;
5
+ /**
6
+ * Subscription-oriented store facade over Continuum session state.
7
+ */
5
8
  export interface ContinuumStore {
9
+ /** Returns the latest continuity snapshot. */
6
10
  getSnapshot(): ContinuitySnapshot | null;
11
+ /** Subscribes to snapshot updates. */
7
12
  subscribeSnapshot(listener: Listener): () => void;
13
+ /** Subscribes to diagnostics-related updates. */
8
14
  subscribeDiagnostics(listener: Listener): () => void;
15
+ /** Returns a node value by canonical id. */
9
16
  getNodeValue(nodeId: string): NodeValue | undefined;
17
+ /** Returns viewport state by canonical node id. */
10
18
  getNodeViewport(nodeId: string): ViewportState | undefined;
19
+ /** Subscribes to updates for a specific node id. */
11
20
  subscribeNode(nodeId: string, listener: Listener): () => void;
21
+ /** Releases store subscriptions and listeners. */
12
22
  destroy(): void;
13
23
  }
24
+ /**
25
+ * Value shape exposed through `ContinuumContext`.
26
+ */
14
27
  export interface ContinuumContextValue {
28
+ /** Backing Continuum session instance. */
15
29
  session: Session;
30
+ /** Subscription-friendly store facade over session state. */
16
31
  store: ContinuumStore;
32
+ /** Resolved node type to component map. */
17
33
  componentMap: ContinuumNodeMap;
34
+ /** True when provider loaded from existing persisted state. */
18
35
  wasHydrated: boolean;
19
36
  }
37
+ /**
38
+ * React context backing all `@continuum-dev/react` hooks and renderer behavior.
39
+ */
20
40
  export declare const ContinuumContext: import("react").Context<ContinuumContextValue | null>;
41
+ /**
42
+ * Initializes and provides Continuum session context to the React subtree.
43
+ *
44
+ * Creates (or hydrates) a session once, wires optional persistence, and
45
+ * provides a reactive store used by hooks and renderer components.
46
+ */
21
47
  export declare function ContinuumProvider({ components, persist, storageKey, maxPersistBytes, onPersistError, sessionOptions, children, }: ContinuumProviderProps): import("react/jsx-runtime").JSX.Element;
22
48
  export {};
23
49
  //# sourceMappingURL=context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../../packages/react/src/lib/context.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAE3E,KAAK,QAAQ,GAAG,MAAM,IAAI,CAAC;AA+C3B,MAAM,WAAW,cAAc;IAC7B,WAAW,IAAI,kBAAkB,GAAG,IAAI,CAAC;IACzC,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI,CAAC;IAClD,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI,CAAC;IACrD,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;IACpD,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAC;IAC3D,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI,CAAC;IAC9D,OAAO,IAAI,IAAI,CAAC;CACjB;AAoFD,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,cAAc,CAAC;IACtB,YAAY,EAAE,gBAAgB,CAAC;IAC/B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,gBAAgB,uDAAoD,CAAC;AAuClF,wBAAgB,iBAAiB,CAAC,EAChC,UAAU,EACV,OAAe,EACf,UAAgC,EAChC,eAAe,EACf,cAAc,EACd,cAAc,EACd,QAAQ,GACT,EAAE,sBAAsB,2CAsDxB"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../../packages/react/src/lib/context.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAE3E,KAAK,QAAQ,GAAG,MAAM,IAAI,CAAC;AA+C3B;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,8CAA8C;IAC9C,WAAW,IAAI,kBAAkB,GAAG,IAAI,CAAC;IACzC,sCAAsC;IACtC,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI,CAAC;IAClD,iDAAiD;IACjD,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI,CAAC;IACrD,4CAA4C;IAC5C,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;IACpD,mDAAmD;IACnD,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAC;IAC3D,oDAAoD;IACpD,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI,CAAC;IAC9D,kDAAkD;IAClD,OAAO,IAAI,IAAI,CAAC;CACjB;AAoFD;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,0CAA0C;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,6DAA6D;IAC7D,KAAK,EAAE,cAAc,CAAC;IACtB,2CAA2C;IAC3C,YAAY,EAAE,gBAAgB,CAAC;IAC/B,+DAA+D;IAC/D,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,uDAAoD,CAAC;AAuClF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,UAAU,EACV,OAAe,EACf,UAAgC,EAChC,eAAe,EACf,cAAc,EACd,cAAc,EACd,QAAQ,GACT,EAAE,sBAAsB,2CAsDxB"}
package/lib/context.js CHANGED
@@ -112,6 +112,9 @@ function createContinuumStore(session) {
112
112
  },
113
113
  };
114
114
  }
115
+ /**
116
+ * React context backing all `@continuum-dev/react` hooks and renderer behavior.
117
+ */
115
118
  export const ContinuumContext = createContext(null);
116
119
  const DEFAULT_STORAGE_KEY = 'continuum_session';
117
120
  function resolveStorage(persist) {
@@ -143,6 +146,12 @@ function useStableMap(map) {
143
146
  ref.current = map;
144
147
  return map;
145
148
  }
149
+ /**
150
+ * Initializes and provides Continuum session context to the React subtree.
151
+ *
152
+ * Creates (or hydrates) a session once, wires optional persistence, and
153
+ * provides a reactive store used by hooks and renderer components.
154
+ */
146
155
  export function ContinuumProvider({ components, persist = false, storageKey = DEFAULT_STORAGE_KEY, maxPersistBytes, onPersistError, sessionOptions, children, }) {
147
156
  const storage = resolveStorage(persist);
148
157
  const stableComponents = useStableMap(components);
@@ -7,9 +7,13 @@ interface NodeErrorBoundaryState {
7
7
  hasError: boolean;
8
8
  message: string;
9
9
  }
10
+ /**
11
+ * Per-node error boundary used by the renderer to isolate component failures.
12
+ */
10
13
  export declare class NodeErrorBoundary extends Component<NodeErrorBoundaryProps, NodeErrorBoundaryState> {
11
14
  state: NodeErrorBoundaryState;
12
15
  static getDerivedStateFromError(error: unknown): NodeErrorBoundaryState;
16
+ componentDidUpdate(prevProps: NodeErrorBoundaryProps): void;
13
17
  render(): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null | undefined;
14
18
  }
15
19
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"error-boundary.d.ts","sourceRoot":"","sources":["../../../../packages/react/src/lib/error-boundary.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAElD,UAAU,sBAAsB;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,UAAU,sBAAsB;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,iBAAkB,SAAQ,SAAS,CAC9C,sBAAsB,EACtB,sBAAsB,CACvB;IACU,KAAK,EAAE,sBAAsB,CAGpC;IAEF,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,sBAAsB;IAO9D,MAAM;CAUhB"}
1
+ {"version":3,"file":"error-boundary.d.ts","sourceRoot":"","sources":["../../../../packages/react/src/lib/error-boundary.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAElD,UAAU,sBAAsB;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,UAAU,sBAAsB;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,SAAS,CAC9C,sBAAsB,EACtB,sBAAsB,CACvB;IACU,KAAK,EAAE,sBAAsB,CAGpC;IAEF,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,sBAAsB;IAO9D,kBAAkB,CAAC,SAAS,EAAE,sBAAsB;IASpD,MAAM;CAUhB"}
@@ -1,5 +1,8 @@
1
1
  import { jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Component } from 'react';
3
+ /**
4
+ * Per-node error boundary used by the renderer to isolate component failures.
5
+ */
3
6
  export class NodeErrorBoundary extends Component {
4
7
  state = {
5
8
  hasError: false,
@@ -11,6 +14,14 @@ export class NodeErrorBoundary extends Component {
11
14
  message: error instanceof Error ? error.message : String(error),
12
15
  };
13
16
  }
17
+ componentDidUpdate(prevProps) {
18
+ if (this.state.hasError && prevProps.children !== this.props.children) {
19
+ this.setState({
20
+ hasError: false,
21
+ message: '',
22
+ });
23
+ }
24
+ }
14
25
  render() {
15
26
  if (this.state.hasError) {
16
27
  return (_jsxs("div", { "data-continuum-render-error": this.props.nodeId, children: ["Node render failed: ", this.props.nodeId, " (", this.state.message, ")"] }));
package/lib/fallback.d.ts CHANGED
@@ -1,3 +1,6 @@
1
1
  import type { ContinuumNodeProps } from './types.js';
2
+ /**
3
+ * Default renderer used when no component exists for a node type.
4
+ */
2
5
  export declare function FallbackComponent({ value, onChange, definition, }: ContinuumNodeProps): import("react/jsx-runtime").JSX.Element;
3
6
  //# sourceMappingURL=fallback.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../../../../packages/react/src/lib/fallback.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,QAAQ,EACR,UAAU,GACX,EAAE,kBAAkB,2CAoCpB"}
1
+ {"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../../../../packages/react/src/lib/fallback.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,QAAQ,EACR,UAAU,GACX,EAAE,kBAAkB,2CAoCpB"}
package/lib/fallback.js CHANGED
@@ -1,4 +1,7 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Default renderer used when no component exists for a node type.
4
+ */
2
5
  export function FallbackComponent({ value, onChange, definition, }) {
3
6
  const raw = value;
4
7
  const textValue = typeof raw?.['value'] === 'string' || typeof raw?.['value'] === 'number'
package/lib/hooks.d.ts CHANGED
@@ -1,32 +1,76 @@
1
1
  import type { Session } from '@continuum-dev/session';
2
- import type { ContinuitySnapshot, NodeValue, ViewportState, ProposedValue } from '@continuum-dev/contract';
2
+ import type { ContinuitySnapshot, NodeValue, ViewportState, ProposedValue, ActionResult } from '@continuum-dev/contract';
3
3
  interface NodeStateScope {
4
4
  subscribeNode: (nodeId: string, listener: () => void) => () => void;
5
5
  getNodeValue: (nodeId: string) => NodeValue | undefined;
6
6
  setNodeValue: (nodeId: string, value: NodeValue) => void;
7
7
  }
8
+ /**
9
+ * Internal scope context used by collection item renderers to map local node ids
10
+ * onto collection-backed values.
11
+ */
8
12
  export declare const NodeStateScopeContext: import("react").Context<NodeStateScope | null>;
13
+ /**
14
+ * Returns the active Continuum session from provider context.
15
+ */
9
16
  export declare function useContinuumSession(): Session;
17
+ /**
18
+ * Subscribes to and updates a specific node value by canonical node id.
19
+ *
20
+ * @param nodeId Canonical node id.
21
+ */
10
22
  export declare function useContinuumState(nodeId: string): [NodeValue | undefined, (value: NodeValue) => void];
23
+ /**
24
+ * Subscribes to the full continuity snapshot.
25
+ */
11
26
  export declare function useContinuumSnapshot(): ContinuitySnapshot | null;
27
+ /**
28
+ * Subscribes to and updates viewport state for a specific node.
29
+ *
30
+ * @param nodeId Canonical node id.
31
+ */
12
32
  export declare function useContinuumViewport(nodeId: string): [ViewportState | undefined, (state: ViewportState) => void];
33
+ /**
34
+ * Subscribes to session diagnostics (`issues`, `diffs`, `resolutions`, checkpoints).
35
+ */
13
36
  export declare function useContinuumDiagnostics(): {
14
37
  issues: ReturnType<Session["getIssues"]>;
15
38
  diffs: ReturnType<Session["getDiffs"]>;
16
39
  resolutions: ReturnType<Session["getResolutions"]>;
17
40
  checkpoints: ReturnType<Session["getCheckpoints"]>;
18
41
  };
42
+ /**
43
+ * Indicates whether the provider session was restored from persistence.
44
+ */
19
45
  export declare function useContinuumHydrated(): boolean;
46
+ /**
47
+ * Returns conflict state and resolution actions for one node.
48
+ *
49
+ * @param nodeId Canonical node id.
50
+ */
20
51
  export declare function useContinuumConflict(nodeId: string): {
21
52
  hasConflict: boolean;
22
53
  proposal: ProposedValue | null;
23
54
  accept: () => void;
24
55
  reject: () => void;
25
56
  };
57
+ /**
58
+ * Aggregates suggestion state and exposes bulk accept/reject operations.
59
+ */
26
60
  export declare function useContinuumSuggestions(): {
27
61
  hasSuggestions: boolean;
28
62
  acceptAll: () => void;
29
63
  rejectAll: () => void;
30
64
  };
65
+ /**
66
+ * Returns an action dispatcher bound to an intent id with dispatch state.
67
+ *
68
+ * @param intentId Registered action intent id to dispatch.
69
+ */
70
+ export declare function useContinuumAction(intentId: string): {
71
+ dispatch: (nodeId: string) => Promise<ActionResult>;
72
+ isDispatching: boolean;
73
+ lastResult: ActionResult | null;
74
+ };
31
75
  export {};
32
76
  //# sourceMappingURL=hooks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../../../packages/react/src/lib/hooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAwDvG,UAAU,cAAc;IACtB,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;IACpE,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,SAAS,GAAG,SAAS,CAAC;IACxD,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CAC1D;AAED,eAAO,MAAM,qBAAqB,gDAA6C,CAAC;AAEhF,wBAAgB,mBAAmB,IAAI,OAAO,CAQ7C;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,GACb,CAAC,SAAS,GAAG,SAAS,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC,CAiDrD;AAED,wBAAgB,oBAAoB,IAAI,kBAAkB,GAAG,IAAI,CAiDhE;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,GACb,CAAC,aAAa,GAAG,SAAS,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC,CAkC7D;AAED,wBAAgB,uBAAuB;YAS3B,UAAU,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;WACjC,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;iBACzB,UAAU,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;iBACrC,UAAU,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;EAmCrD;AAED,wBAAgB,oBAAoB,IAAI,OAAO,CAQ9C;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG;IACpD,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAC/B,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB,CAsCA;AAED,wBAAgB,uBAAuB,IAAI;IACzC,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,CAgEA"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../../../packages/react/src/lib/hooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAwDrH,UAAU,cAAc;IACtB,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;IACpE,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,SAAS,GAAG,SAAS,CAAC;IACxD,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CAC1D;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,gDAA6C,CAAC;AAEhF;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAQ7C;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,GACb,CAAC,SAAS,GAAG,SAAS,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC,CAiDrD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,kBAAkB,GAAG,IAAI,CAiDhE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,GACb,CAAC,aAAa,GAAG,SAAS,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC,CA4C7D;AAED;;GAEG;AACH,wBAAgB,uBAAuB;YAS3B,UAAU,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;WACjC,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;iBACzB,UAAU,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;iBACrC,UAAU,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;EAmCrD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAQ9C;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG;IACpD,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAC/B,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB,CAsCA;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI;IACzC,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,CAgEA;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG;IACpD,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IACpD,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,EAAE,YAAY,GAAG,IAAI,CAAC;CACjC,CAgCA"}
package/lib/hooks.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createContext, useContext, useCallback, useRef, useSyncExternalStore } from 'react';
1
+ import { createContext, useContext, useCallback, useRef, useState, useSyncExternalStore } from 'react';
2
2
  import { ContinuumContext } from './context.js';
3
3
  function shallowArrayEqual(left, right) {
4
4
  if (left.length !== right.length) {
@@ -38,7 +38,14 @@ function shallowViewportEqual(left, right) {
38
38
  left.isExpanded === right.isExpanded &&
39
39
  left.isFocused === right.isFocused);
40
40
  }
41
+ /**
42
+ * Internal scope context used by collection item renderers to map local node ids
43
+ * onto collection-backed values.
44
+ */
41
45
  export const NodeStateScopeContext = createContext(null);
46
+ /**
47
+ * Returns the active Continuum session from provider context.
48
+ */
42
49
  export function useContinuumSession() {
43
50
  const ctx = useContext(ContinuumContext);
44
51
  if (!ctx) {
@@ -46,6 +53,11 @@ export function useContinuumSession() {
46
53
  }
47
54
  return ctx.session;
48
55
  }
56
+ /**
57
+ * Subscribes to and updates a specific node value by canonical node id.
58
+ *
59
+ * @param nodeId Canonical node id.
60
+ */
49
61
  export function useContinuumState(nodeId) {
50
62
  const ctx = useContext(ContinuumContext);
51
63
  const scope = useContext(NodeStateScopeContext);
@@ -70,7 +82,7 @@ export function useContinuumState(nodeId) {
70
82
  }
71
83
  valueCacheRef.current = nextValue;
72
84
  return nextValue;
73
- }, [store, nodeId]);
85
+ }, [scope, store, nodeId]);
74
86
  const value = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
75
87
  const setValue = useCallback((next) => {
76
88
  if (scope) {
@@ -81,6 +93,9 @@ export function useContinuumState(nodeId) {
81
93
  }, [scope, session, nodeId]);
82
94
  return [value, setValue];
83
95
  }
96
+ /**
97
+ * Subscribes to the full continuity snapshot.
98
+ */
84
99
  export function useContinuumSnapshot() {
85
100
  const ctx = useContext(ContinuumContext);
86
101
  if (!ctx) {
@@ -114,11 +129,22 @@ export function useContinuumSnapshot() {
114
129
  }, [store]);
115
130
  return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
116
131
  }
132
+ /**
133
+ * Subscribes to and updates viewport state for a specific node.
134
+ *
135
+ * @param nodeId Canonical node id.
136
+ */
117
137
  export function useContinuumViewport(nodeId) {
118
138
  const ctx = useContext(ContinuumContext);
139
+ const scope = useContext(NodeStateScopeContext);
119
140
  if (!ctx) {
120
141
  throw new Error('useContinuumViewport must be used within a <ContinuumProvider>');
121
142
  }
143
+ if (scope
144
+ && typeof process !== 'undefined'
145
+ && process.env.NODE_ENV !== 'production') {
146
+ console.warn(`useContinuumViewport("${nodeId}") called inside a collection scope. Viewport state is not supported for collection item nodes.`);
147
+ }
122
148
  const { session, store } = ctx;
123
149
  const viewportCacheRef = useRef(undefined);
124
150
  const subscribe = useCallback((onStoreChange) => store.subscribeNode(nodeId, onStoreChange), [store, nodeId]);
@@ -137,6 +163,9 @@ export function useContinuumViewport(nodeId) {
137
163
  }, [session, nodeId]);
138
164
  return [viewport, setViewport];
139
165
  }
166
+ /**
167
+ * Subscribes to session diagnostics (`issues`, `diffs`, `resolutions`, checkpoints).
168
+ */
140
169
  export function useContinuumDiagnostics() {
141
170
  const ctx = useContext(ContinuumContext);
142
171
  if (!ctx) {
@@ -165,6 +194,9 @@ export function useContinuumDiagnostics() {
165
194
  const subscribe = useCallback((onStoreChange) => store.subscribeDiagnostics(onStoreChange), [store]);
166
195
  return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
167
196
  }
197
+ /**
198
+ * Indicates whether the provider session was restored from persistence.
199
+ */
168
200
  export function useContinuumHydrated() {
169
201
  const ctx = useContext(ContinuumContext);
170
202
  if (!ctx) {
@@ -172,6 +204,11 @@ export function useContinuumHydrated() {
172
204
  }
173
205
  return ctx.wasHydrated;
174
206
  }
207
+ /**
208
+ * Returns conflict state and resolution actions for one node.
209
+ *
210
+ * @param nodeId Canonical node id.
211
+ */
175
212
  export function useContinuumConflict(nodeId) {
176
213
  const ctx = useContext(ContinuumContext);
177
214
  if (!ctx) {
@@ -203,6 +240,9 @@ export function useContinuumConflict(nodeId) {
203
240
  reject,
204
241
  };
205
242
  }
243
+ /**
244
+ * Aggregates suggestion state and exposes bulk accept/reject operations.
245
+ */
206
246
  export function useContinuumSuggestions() {
207
247
  const ctx = useContext(ContinuumContext);
208
248
  if (!ctx) {
@@ -260,3 +300,35 @@ export function useContinuumSuggestions() {
260
300
  rejectAll,
261
301
  };
262
302
  }
303
+ /**
304
+ * Returns an action dispatcher bound to an intent id with dispatch state.
305
+ *
306
+ * @param intentId Registered action intent id to dispatch.
307
+ */
308
+ export function useContinuumAction(intentId) {
309
+ const ctx = useContext(ContinuumContext);
310
+ if (!ctx) {
311
+ throw new Error('useContinuumAction must be used within a <ContinuumProvider>');
312
+ }
313
+ const { session } = ctx;
314
+ const [isDispatching, setIsDispatching] = useState(false);
315
+ const [lastResult, setLastResult] = useState(null);
316
+ const dispatchIdRef = useRef(0);
317
+ const dispatch = useCallback(async (nodeId) => {
318
+ const dispatchId = ++dispatchIdRef.current;
319
+ setIsDispatching(true);
320
+ try {
321
+ const result = await session.dispatchAction(intentId, nodeId);
322
+ if (dispatchIdRef.current === dispatchId) {
323
+ setLastResult(result);
324
+ }
325
+ return result;
326
+ }
327
+ finally {
328
+ if (dispatchIdRef.current === dispatchId) {
329
+ setIsDispatching(false);
330
+ }
331
+ }
332
+ }, [session, intentId]);
333
+ return { dispatch, isDispatching, lastResult };
334
+ }
package/lib/renderer.d.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  import type { ViewDefinition } from '@continuum-dev/contract';
2
+ /**
3
+ * Renders a `ViewDefinition` tree using components registered in `ContinuumProvider`.
4
+ */
2
5
  export declare function ContinuumRenderer({ view }: {
3
6
  view: ViewDefinition;
4
7
  }): import("react/jsx-runtime").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../../packages/react/src/lib/renderer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAIV,cAAc,EAEf,MAAM,qBAAqB,CAAC;AA+W7B,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,2CAQnE"}
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../../packages/react/src/lib/renderer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAIV,cAAc,EAEf,MAAM,qBAAqB,CAAC;AAkZ7B;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,2CAQnE"}
package/lib/renderer.js CHANGED
@@ -1,5 +1,5 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { memo, useContext, useMemo } from 'react';
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { memo, useCallback, useContext, useMemo } from 'react';
3
3
  import { getChildNodes } from '@continuum-dev/contract';
4
4
  import { ContinuumContext } from './context.js';
5
5
  import { NodeStateScopeContext, useContinuumState } from './hooks.js';
@@ -83,68 +83,90 @@ function collectTemplateDefaults(node, parentPath = '') {
83
83
  }
84
84
  return values;
85
85
  }
86
- const StatefulNodeRenderer = memo(function StatefulNodeRenderer({ definition, parentPath }) {
86
+ const StatefulNodeRenderer = memo(function StatefulNodeRenderer({ definition, parentPath, mappedProps, }) {
87
87
  const Component = useResolvedComponent(definition);
88
88
  const canonicalId = toCanonicalId(definition.id, parentPath);
89
89
  const [value, setValue] = useContinuumState(canonicalId);
90
90
  if (definition.hidden) {
91
91
  return null;
92
92
  }
93
- return (_jsx("div", { "data-continuum-id": definition.id, children: _jsx(NodeErrorBoundary, { nodeId: definition.id, children: _jsx(Component, { value: value, onChange: setValue, definition: definition, nodeId: canonicalId }) }) }));
93
+ return (_jsx(_Fragment, { children: _jsx(NodeErrorBoundary, { nodeId: definition.id, children: _jsx(Component, { value: value, onChange: setValue, definition: definition, nodeId: canonicalId, ...mappedProps }) }) }));
94
94
  });
95
- const ContainerNodeRenderer = memo(function ContainerNodeRenderer({ definition, parentPath }) {
95
+ const ContainerNodeRenderer = memo(function ContainerNodeRenderer({ definition, parentPath, mappedProps, }) {
96
96
  const Component = useResolvedComponent(definition);
97
97
  if (definition.hidden) {
98
98
  return null;
99
99
  }
100
100
  const canonicalId = toCanonicalId(definition.id, parentPath);
101
- const childNodes = getChildNodes(definition).map((child) => (_jsx(NodeRenderer, { definition: child, parentPath: canonicalId }, child.id)));
102
- return (_jsx("div", { "data-continuum-id": definition.id, children: _jsx(NodeErrorBoundary, { nodeId: definition.id, children: _jsx(Component, { value: undefined, onChange: noopOnChange, definition: definition, nodeId: canonicalId, children: childNodes }) }) }));
101
+ const childNodes = getChildNodes(definition).map((child) => (_jsx(NodeRenderer, { definition: child, parentPath: canonicalId, mappedProps: mappedProps }, child.id)));
102
+ return (_jsx(_Fragment, { children: _jsx(NodeErrorBoundary, { nodeId: definition.id, children: _jsx(Component, { value: undefined, onChange: noopOnChange, definition: definition, nodeId: canonicalId, ...mappedProps, children: childNodes }) }) }));
103
103
  });
104
104
  const CollectionItemRenderer = memo(function CollectionItemRenderer({ collectionCanonicalId, itemIndex, template, templateDefaults, canRemove, onRemove, }) {
105
105
  const ctx = useContext(ContinuumContext);
106
+ const parentScope = useContext(NodeStateScopeContext);
106
107
  if (!ctx) {
107
108
  throw new Error('ContinuumRenderer must be used within a <ContinuumProvider>');
108
109
  }
109
110
  const { session, store } = ctx;
110
- const scope = useMemo(() => ({
111
- subscribeNode: (_nodeId, listener) => store.subscribeNode(collectionCanonicalId, listener),
112
- getNodeValue: (nodeId) => {
113
- const relativeId = toRelativeNodeId(collectionCanonicalId, nodeId);
114
- if (!relativeId) {
115
- return undefined;
116
- }
117
- const collectionValue = normalizeCollectionNodeValue(store.getNodeValue(collectionCanonicalId));
118
- return (collectionValue.value.items[itemIndex]?.values?.[relativeId] ??
119
- templateDefaults[relativeId]);
120
- },
121
- setNodeValue: (nodeId, nextValue) => {
122
- const relativeId = toRelativeNodeId(collectionCanonicalId, nodeId);
123
- if (!relativeId) {
124
- return;
125
- }
126
- const collectionValue = normalizeCollectionNodeValue(store.getNodeValue(collectionCanonicalId));
127
- const items = collectionValue.value.items.map((item) => ({
128
- values: { ...item.values },
129
- }));
130
- while (items.length <= itemIndex) {
131
- items.push({ values: {} });
132
- }
133
- items[itemIndex] = {
134
- values: {
135
- ...items[itemIndex].values,
136
- [relativeId]: nextValue,
137
- },
138
- };
139
- session.updateState(collectionCanonicalId, {
140
- ...collectionValue,
141
- value: { items },
142
- });
143
- },
144
- }), [collectionCanonicalId, itemIndex, session, store, templateDefaults]);
145
- return (_jsx(NodeStateScopeContext.Provider, { value: scope, children: _jsxs("div", { "data-continuum-collection-item": `${collectionCanonicalId}:${itemIndex}`, className: "continuum-collection-item", children: [_jsx(NodeRenderer, { definition: template, parentPath: collectionCanonicalId }), canRemove ? (_jsx("div", { className: "continuum-collection-item-actions", children: _jsx("button", { type: "button", "data-continuum-collection-remove": `${collectionCanonicalId}:${itemIndex}`, onClick: () => onRemove(itemIndex), className: "continuum-collection-remove", children: "\u00D7" }) })) : null] }) }));
111
+ const scope = useMemo(() => {
112
+ const readCollectionValue = parentScope
113
+ ? () => normalizeCollectionNodeValue(parentScope.getNodeValue(collectionCanonicalId))
114
+ : () => normalizeCollectionNodeValue(store.getNodeValue(collectionCanonicalId));
115
+ const writeCollectionValue = parentScope
116
+ ? (next) => parentScope.setNodeValue(collectionCanonicalId, next)
117
+ : (next) => session.updateState(collectionCanonicalId, next);
118
+ const subscribeToCollection = parentScope
119
+ ? (listener) => parentScope.subscribeNode(collectionCanonicalId, listener)
120
+ : (listener) => store.subscribeNode(collectionCanonicalId, listener);
121
+ return {
122
+ subscribeNode: (_nodeId, listener) => subscribeToCollection(listener),
123
+ getNodeValue: (nodeId) => {
124
+ const relativeId = toRelativeNodeId(collectionCanonicalId, nodeId);
125
+ if (!relativeId) {
126
+ return undefined;
127
+ }
128
+ const collectionValue = readCollectionValue();
129
+ return (collectionValue.value.items[itemIndex]?.values?.[relativeId] ??
130
+ templateDefaults[relativeId]);
131
+ },
132
+ setNodeValue: (nodeId, nextValue) => {
133
+ const relativeId = toRelativeNodeId(collectionCanonicalId, nodeId);
134
+ if (!relativeId) {
135
+ return;
136
+ }
137
+ const collectionValue = readCollectionValue();
138
+ const items = collectionValue.value.items.map((item) => ({
139
+ values: { ...item.values },
140
+ }));
141
+ while (items.length <= itemIndex) {
142
+ items.push({ values: {} });
143
+ }
144
+ items[itemIndex] = {
145
+ values: {
146
+ ...items[itemIndex].values,
147
+ [relativeId]: nextValue,
148
+ },
149
+ };
150
+ writeCollectionValue({
151
+ ...collectionValue,
152
+ value: { items },
153
+ });
154
+ },
155
+ };
156
+ }, [collectionCanonicalId, itemIndex, session, store, templateDefaults, parentScope]);
157
+ return (_jsx(NodeStateScopeContext.Provider, { value: scope, children: _jsx(NodeRenderer, { definition: template, parentPath: collectionCanonicalId, mappedProps: {
158
+ itemIndex,
159
+ canRemove,
160
+ onRemove: () => onRemove(itemIndex),
161
+ } }) }));
146
162
  });
147
163
  const CollectionNodeRenderer = memo(function CollectionNodeRenderer({ definition, parentPath, }) {
164
+ const ctx = useContext(ContinuumContext);
165
+ const parentScope = useContext(NodeStateScopeContext);
166
+ if (!ctx) {
167
+ throw new Error('ContinuumRenderer must be used within a <ContinuumProvider>');
168
+ }
169
+ const { store } = ctx;
148
170
  const Component = useResolvedComponent(definition);
149
171
  const canonicalId = toCanonicalId(definition.id, parentPath);
150
172
  const [collectionValue, setCollectionValue] = useContinuumState(canonicalId);
@@ -158,49 +180,57 @@ const CollectionNodeRenderer = memo(function CollectionNodeRenderer({ definition
158
180
  if (definition.hidden) {
159
181
  return null;
160
182
  }
161
- const addItem = () => {
162
- if (!canAdd) {
183
+ const readCollectionValue = useCallback(() => normalizeCollectionNodeValue(parentScope
184
+ ? parentScope.getNodeValue(canonicalId)
185
+ : store.getNodeValue(canonicalId)), [canonicalId, parentScope, store]);
186
+ const addItem = useCallback(() => {
187
+ const current = readCollectionValue();
188
+ if (maxItems !== undefined && current.value.items.length >= maxItems) {
163
189
  return;
164
190
  }
165
191
  const items = [
166
- ...normalizedCollection.value.items.map((item) => ({
192
+ ...current.value.items.map((item) => ({
167
193
  values: { ...item.values },
168
194
  })),
169
195
  { values: { ...templateDefaults } },
170
196
  ];
171
197
  setCollectionValue({
172
- ...normalizedCollection,
198
+ ...current,
173
199
  value: { items },
174
200
  });
175
- };
176
- const removeItem = (index) => {
177
- if (!canRemove) {
201
+ }, [maxItems, readCollectionValue, setCollectionValue, templateDefaults]);
202
+ const removeItem = useCallback((index) => {
203
+ const current = readCollectionValue();
204
+ if (current.value.items.length <= minItems) {
178
205
  return;
179
206
  }
180
- const items = normalizedCollection.value.items
207
+ const items = current.value.items
181
208
  .map((item) => ({ values: { ...item.values } }))
182
209
  .filter((_, itemIndex) => itemIndex !== index);
183
210
  if (items.length < minItems) {
184
211
  return;
185
212
  }
186
213
  setCollectionValue({
187
- ...normalizedCollection,
214
+ ...current,
188
215
  value: { items },
189
216
  });
190
- };
217
+ }, [minItems, readCollectionValue, setCollectionValue]);
191
218
  const renderedItems = normalizedCollection.value.items.map((_, index) => (_jsx(CollectionItemRenderer, { collectionCanonicalId: canonicalId, itemIndex: index, template: definition.template, templateDefaults: templateDefaults, canRemove: canRemove, onRemove: removeItem }, index)));
192
- return (_jsx("div", { "data-continuum-id": definition.id, children: _jsx(NodeErrorBoundary, { nodeId: definition.id, children: _jsxs(Component, { value: collectionValue, onChange: setCollectionValue, definition: definition, nodeId: canonicalId, children: [renderedItems, _jsx("div", { className: "continuum-collection-add-container", children: _jsx("button", { type: "button", "data-continuum-collection-add": canonicalId, onClick: addItem, disabled: !canAdd, className: "continuum-collection-add", children: "+ Add item" }) })] }) }) }));
219
+ return (_jsx(_Fragment, { children: _jsx(NodeErrorBoundary, { nodeId: definition.id, children: _jsx(Component, { value: collectionValue, onChange: setCollectionValue, definition: definition, nodeId: canonicalId, canAdd: canAdd, canRemove: canRemove, onAdd: addItem, onRemove: removeItem, children: renderedItems }) }) }));
193
220
  });
194
- const NodeRenderer = memo(function NodeRenderer({ definition, parentPath }) {
221
+ const NodeRenderer = memo(function NodeRenderer({ definition, parentPath, mappedProps, }) {
195
222
  if (definition.type === 'collection') {
196
223
  return _jsx(CollectionNodeRenderer, { definition: definition, parentPath: parentPath });
197
224
  }
198
225
  const childNodes = getChildNodes(definition);
199
226
  if (childNodes.length > 0) {
200
- return _jsx(ContainerNodeRenderer, { definition: definition, parentPath: parentPath });
227
+ return _jsx(ContainerNodeRenderer, { definition: definition, parentPath: parentPath, mappedProps: mappedProps });
201
228
  }
202
- return _jsx(StatefulNodeRenderer, { definition: definition, parentPath: parentPath });
229
+ return _jsx(StatefulNodeRenderer, { definition: definition, parentPath: parentPath, mappedProps: mappedProps });
203
230
  });
231
+ /**
232
+ * Renders a `ViewDefinition` tree using components registered in `ContinuumProvider`.
233
+ */
204
234
  export function ContinuumRenderer({ view }) {
205
- return (_jsx("div", { "data-continuum-view": view.viewId, children: (view.nodes ?? []).map((node) => (_jsx(NodeRenderer, { definition: node, parentPath: "" }, node.id))) }));
235
+ return (_jsx(_Fragment, { children: (view.nodes ?? []).map((node) => (_jsx(NodeRenderer, { definition: node, parentPath: "" }, node.id))) }));
206
236
  }
package/lib/types.d.ts CHANGED
@@ -1,31 +1,71 @@
1
1
  import type { ViewNode, NodeValue } from '@continuum-dev/contract';
2
2
  import type { SessionOptions } from '@continuum-dev/session';
3
3
  import type { ComponentType } from 'react';
4
+ /**
5
+ * Props passed to node renderer components in the Continuum map.
6
+ *
7
+ * @template T Node value shape consumed by the component.
8
+ */
4
9
  export interface ContinuumNodeProps<T = NodeValue> {
10
+ /** Current node value from session state. */
5
11
  value: T | undefined;
12
+ /** Writes a new node value into session state. */
6
13
  onChange: (value: T) => void;
14
+ /** Raw node definition from the active view. */
7
15
  definition: ViewNode;
16
+ /** Canonical node id, including parent path for nested nodes. */
8
17
  nodeId?: string;
18
+ /** Rendered children for container-like nodes. */
9
19
  children?: React.ReactNode;
20
+ /** Additional mapped props provided by renderers/integrations. */
10
21
  [prop: string]: unknown;
11
22
  }
23
+ /**
24
+ * Component registry keyed by Continuum node `type`.
25
+ */
12
26
  export type ContinuumNodeMap = Record<string, ComponentType<ContinuumNodeProps<any>>>;
27
+ /**
28
+ * Backward-compatible alias for `ContinuumNodeProps`.
29
+ *
30
+ * @template T Node value shape consumed by the component.
31
+ */
13
32
  export type ContinuumComponentProps<T = NodeValue> = ContinuumNodeProps<T>;
33
+ /**
34
+ * Backward-compatible alias for `ContinuumNodeMap`.
35
+ */
14
36
  export type ContinuumComponentMap = ContinuumNodeMap;
37
+ /**
38
+ * Error metadata emitted when persistence fails in the provider.
39
+ */
15
40
  export interface ContinuumPersistError {
41
+ /** Persistence failure category. */
16
42
  reason: 'size_limit' | 'storage_error';
43
+ /** Storage key used for persistence. */
17
44
  key: string;
45
+ /** Serialized payload size in bytes. */
18
46
  attemptedBytes?: number;
47
+ /** Configured max byte limit for persistence. */
19
48
  maxBytes?: number;
49
+ /** Original error/cause when available. */
20
50
  cause?: unknown;
21
51
  }
52
+ /**
53
+ * Props for `ContinuumProvider`.
54
+ */
22
55
  export interface ContinuumProviderProps {
56
+ /** Node type to component map used by `ContinuumRenderer`. */
23
57
  components: ContinuumNodeMap;
58
+ /** Optional browser persistence mode. */
24
59
  persist?: 'sessionStorage' | 'localStorage' | false;
60
+ /** Storage key for persisted session data. */
25
61
  storageKey?: string;
62
+ /** Maximum serialized bytes allowed for persisted payloads. */
26
63
  maxPersistBytes?: number;
64
+ /** Callback invoked when persistence errors occur. */
27
65
  onPersistError?: (error: ContinuumPersistError) => void;
66
+ /** Options forwarded to `@continuum-dev/session`. */
28
67
  sessionOptions?: SessionOptions;
68
+ /** React subtree that consumes Continuum context. */
29
69
  children: React.ReactNode;
30
70
  }
31
71
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../packages/react/src/lib/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,MAAM,WAAW,kBAAkB,CAAC,CAAC,GAAG,SAAS;IAC/C,KAAK,EAAE,CAAC,GAAG,SAAS,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC7B,UAAU,EAAE,QAAQ,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CACzB;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,CACnC,MAAM,EACN,aAAa,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CACvC,CAAC;AAEF,MAAM,MAAM,uBAAuB,CAAC,CAAC,GAAG,SAAS,IAAI,kBAAkB,CAAC,CAAC,CAAC,CAAC;AAC3E,MAAM,MAAM,qBAAqB,GAAG,gBAAgB,CAAC;AAErD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,YAAY,GAAG,eAAe,CAAC;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,gBAAgB,CAAC;IAC7B,OAAO,CAAC,EAAE,gBAAgB,GAAG,cAAc,GAAG,KAAK,CAAC;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;IACxD,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../packages/react/src/lib/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C;;;;GAIG;AACH,MAAM,WAAW,kBAAkB,CAAC,CAAC,GAAG,SAAS;IAC/C,6CAA6C;IAC7C,KAAK,EAAE,CAAC,GAAG,SAAS,CAAC;IACrB,kDAAkD;IAClD,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC7B,gDAAgD;IAChD,UAAU,EAAE,QAAQ,CAAC;IACrB,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,kEAAkE;IAClE,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,CACnC,MAAM,EACN,aAAa,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CACvC,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,CAAC,CAAC,GAAG,SAAS,IAAI,kBAAkB,CAAC,CAAC,CAAC,CAAC;AAC3E;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,gBAAgB,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,oCAAoC;IACpC,MAAM,EAAE,YAAY,GAAG,eAAe,CAAC;IACvC,wCAAwC;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,wCAAwC;IACxC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,8DAA8D;IAC9D,UAAU,EAAE,gBAAgB,CAAC;IAC7B,yCAAyC;IACzC,OAAO,CAAC,EAAE,gBAAgB,GAAG,cAAc,GAAG,KAAK,CAAC;IACpD,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;IACxD,iDAAiD;IACjD,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,qDAAqD;IACrD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@continuum-dev/react",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -44,7 +44,7 @@
44
44
  "react": ">=18"
45
45
  },
46
46
  "dependencies": {
47
- "@continuum-dev/contract": "^0.1.1",
48
- "@continuum-dev/session": "^0.1.1"
47
+ "@continuum-dev/contract": "^0.1.3",
48
+ "@continuum-dev/session": "^0.1.3"
49
49
  }
50
50
  }