@xenolithengine/graph-react 0.7.0-beta.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 strelok2012
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # @xenolithengine/graph-react
2
+
3
+ [![BETA](https://img.shields.io/badge/status-BETA-FCB400?style=flat-square)](https://github.com/XenolithEngine/xenolith-graph#status)
4
+ [![MIT](https://img.shields.io/badge/license-MIT-FCB400?style=flat-square)](https://github.com/XenolithEngine/xenolith-graph/blob/main/LICENSE)
5
+
6
+ React adapter for XenolithGraph — `<XenolithGraph>` component, `useEditor` / `useNodes` / `useEdges` hooks, in-editor panels.
7
+
8
+ > **Beta** — public API in `STABLE-API.md` is the surface we plan to freeze, but it is **NOT frozen yet** — breaking changes can land at any point before v1.0. If you adopt now, pin an exact version.
9
+
10
+ Part of [XenolithGraph](https://github.com/XenolithEngine/xenolith-graph) — an AI-native, embeddable node-graph editor for the web with its own visual design language (Xen).
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pnpm add @xenolithengine/graph-react pixi.js
16
+ ```
17
+
18
+ Peer deps: `react >= 18`, `react-dom >= 18`, `pixi.js@^8.6.0`. WebGL/client-only — render the component only in the browser (the entry is marked `'use client'` for Next.js App Router).
19
+
20
+ ## Usage
21
+
22
+ ```tsx
23
+ import { XenolithGraph, XenolithControls, XenolithMiniMap } from '@xenolithengine/graph-react'
24
+
25
+ export function Editor() {
26
+ return (
27
+ <XenolithGraph
28
+ style={{ width: '100%', height: '100vh' }}
29
+ graph={savedGraph}
30
+ minimap
31
+ onNodeClick={({ nodeId }) => console.log(nodeId)}
32
+ onSelectionChange={({ nodeIds }) => console.log(nodeIds)}
33
+ onReady={(editor) => editor.fitView()}
34
+ >
35
+ <XenolithControls position="bottom-left" />
36
+ <XenolithMiniMap position="bottom-right" />
37
+ </XenolithGraph>
38
+ )
39
+ }
40
+ ```
41
+
42
+ ## What's exported
43
+
44
+ - `<XenolithGraph>` — main component (props extend `XenolithProps` + `EventCallbacks`)
45
+ - In-editor panels: `<XenolithPanel>`, `<XenolithButton>`, `<XenolithControls>`, `<XenolithMiniMap>`
46
+ - Hooks: `useEditor`, `useXenolithEditor`, `useNodes`, `useEdges`, `useSelection`, `useViewport`, `useGraphJSON`, `useEditorEvent`, `useUndoRedo`
47
+ - Lower-level: `useXenolith(hostRef, props)`, `XenolithContext`, `EVENT_PROP`
48
+ - Custom widgets: `reactWidget`, `WidgetProps`
49
+
50
+ ## Docs
51
+
52
+ - [React guide](https://xenolithengine.github.io/xenolith-graph/guides/react/)
53
+ - [API reference](https://xenolithengine.github.io/xenolith-graph/guides/api/) — every method exposed by `XenolithEditor`
54
+ - [GitHub](https://github.com/XenolithEngine/xenolith-graph)
55
+
56
+ MIT © XenolithEngine
@@ -0,0 +1,43 @@
1
+ import type { CSSProperties, ReactNode, ReactElement, ReactPortal, ButtonHTMLAttributes } from 'react';
2
+ import type { MinimapPosition, ControlsOptions } from '@xenolithengine/graph-editor';
3
+ export type PanelPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
4
+ export interface XenolithPanelProps {
5
+ position?: PanelPosition;
6
+ /** Drop the default frosted card chrome and only anchor the children. */
7
+ bare?: boolean;
8
+ className?: string;
9
+ style?: CSSProperties;
10
+ children?: ReactNode;
11
+ }
12
+ /**
13
+ * An absolutely-positioned overlay anchored inside the editor (React Flow's `<Panel>` analogue).
14
+ * Renders into the editor's screen-anchored overlay layer via a portal, so debug readouts, controls,
15
+ * legends — any React UI — live *inside* the graph. Inherits the theme's `--xeno-*` vars from the host.
16
+ */
17
+ export declare function XenolithPanel({ position, bare, className, style, children }: XenolithPanelProps): ReactPortal | null;
18
+ export interface XenolithButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
19
+ /** Render in the accent colour (filled) instead of the neutral control surface. */
20
+ active?: boolean;
21
+ }
22
+ /**
23
+ * A control button themed from the editor's `--xeno-*` vars, so it tracks the active theme (gold in
24
+ * Xen, cyan in Liquid Glass) for free. Use inside a `<XenolithPanel>` / `<XenolithControls>`.
25
+ */
26
+ export declare function XenolithButton({ active, style, children, disabled, ...rest }: XenolithButtonProps): ReactElement;
27
+ export type XenolithControlsProps = ControlsOptions;
28
+ /**
29
+ * Built-in viewport controls (zoom / fit / reset / undo·redo / save / lock). Declarative wrapper over
30
+ * the editor's vanilla `setControls` — the toolbar itself is rendered by the CORE in `overlayRoot`
31
+ * (shared by every framework, themed via `--xeno-*`), so this component renders no DOM of its own.
32
+ */
33
+ export declare function XenolithControls(props: XenolithControlsProps): null;
34
+ export interface XenolithMiniMapProps {
35
+ position?: MinimapPosition;
36
+ }
37
+ /**
38
+ * Declarative minimap toggle (React Flow's `<MiniMap>` analogue). The minimap itself is rendered in
39
+ * WebGL by the editor; mounting this component enables it and sets its anchor, and unmounting hides
40
+ * it. Renders no DOM of its own.
41
+ */
42
+ export declare function XenolithMiniMap({ position }: XenolithMiniMapProps): null;
43
+ //# sourceMappingURL=components.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../src/components.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAA;AACtG,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAGpF,MAAM,MAAM,aAAa,GACrB,UAAU,GAAG,YAAY,GAAG,WAAW,GACvC,aAAa,GAAG,eAAe,GAAG,cAAc,CAAA;AAWpD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,aAAa,CAAA;IACxB,yEAAyE;IACzE,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAAE,QAAqB,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,kBAAkB,GAAG,WAAW,GAAG,IAAI,CAuBjI;AAED,MAAM,WAAW,mBAAoB,SAAQ,oBAAoB,CAAC,iBAAiB,CAAC;IAClF,mFAAmF;IACnF,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,mBAAmB,GAAG,YAAY,CAwBhH;AAED,MAAM,MAAM,qBAAqB,GAAG,eAAe,CAAA;AAEnD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,IAAI,CAUnE;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,eAAe,CAAA;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAAE,QAAyB,EAAE,EAAE,oBAAoB,GAAG,IAAI,CASzF"}
@@ -0,0 +1,87 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useEffect } from 'react';
4
+ import { createPortal } from 'react-dom';
5
+ import { useXenolithEditor } from './context.js';
6
+ const POS = {
7
+ 'top-left': { top: 12, left: 12 },
8
+ 'top-center': { top: 12, left: '50%', transform: 'translateX(-50%)' },
9
+ 'top-right': { top: 12, right: 12 },
10
+ 'bottom-left': { bottom: 12, left: 12 },
11
+ 'bottom-center': { bottom: 12, left: '50%', transform: 'translateX(-50%)' },
12
+ 'bottom-right': { bottom: 12, right: 12 },
13
+ };
14
+ /**
15
+ * An absolutely-positioned overlay anchored inside the editor (React Flow's `<Panel>` analogue).
16
+ * Renders into the editor's screen-anchored overlay layer via a portal, so debug readouts, controls,
17
+ * legends — any React UI — live *inside* the graph. Inherits the theme's `--xeno-*` vars from the host.
18
+ */
19
+ export function XenolithPanel({ position = 'top-left', bare, className, style, children }) {
20
+ const editor = useXenolithEditor();
21
+ if (!editor)
22
+ return null;
23
+ const chrome = bare ? {} : {
24
+ background: 'var(--xeno-panel)',
25
+ border: '1px solid var(--xeno-border)',
26
+ borderRadius: 10,
27
+ padding: 10,
28
+ color: 'var(--xeno-text)',
29
+ boxShadow: '0 6px 24px rgba(0,0,0,0.35)',
30
+ fontFamily: 'system-ui, sans-serif',
31
+ fontSize: 13,
32
+ };
33
+ return createPortal(_jsx("div", { "data-xeno-panel": "", className: className, style: { position: 'absolute', pointerEvents: 'auto', ...POS[position], ...chrome, ...style }, children: children }), editor.overlayRoot);
34
+ }
35
+ /**
36
+ * A control button themed from the editor's `--xeno-*` vars, so it tracks the active theme (gold in
37
+ * Xen, cyan in Liquid Glass) for free. Use inside a `<XenolithPanel>` / `<XenolithControls>`.
38
+ */
39
+ export function XenolithButton({ active, style, children, disabled, ...rest }) {
40
+ return (_jsx("button", { type: "button", "data-xeno-button": "", disabled: disabled, style: {
41
+ font: 'inherit',
42
+ fontSize: 13,
43
+ lineHeight: 1,
44
+ padding: '7px 12px',
45
+ cursor: disabled ? 'default' : 'pointer',
46
+ opacity: disabled ? 0.4 : 1,
47
+ borderRadius: 8,
48
+ border: `1px solid ${active ? 'var(--xeno-accent)' : 'var(--xeno-border)'}`,
49
+ background: active ? 'var(--xeno-accent)' : 'var(--xeno-elevated)',
50
+ color: active ? 'var(--xeno-canvas)' : 'var(--xeno-text)',
51
+ ...style,
52
+ }, ...rest, children: children }));
53
+ }
54
+ /**
55
+ * Built-in viewport controls (zoom / fit / reset / undo·redo / save / lock). Declarative wrapper over
56
+ * the editor's vanilla `setControls` — the toolbar itself is rendered by the CORE in `overlayRoot`
57
+ * (shared by every framework, themed via `--xeno-*`), so this component renders no DOM of its own.
58
+ */
59
+ export function XenolithControls(props) {
60
+ const editor = useXenolithEditor();
61
+ const key = JSON.stringify(props);
62
+ useEffect(() => {
63
+ if (!editor)
64
+ return;
65
+ editor.setControls(props);
66
+ return () => editor.setControls(false);
67
+ // eslint-disable-next-line react-hooks/exhaustive-deps
68
+ }, [editor, key]);
69
+ return null;
70
+ }
71
+ /**
72
+ * Declarative minimap toggle (React Flow's `<MiniMap>` analogue). The minimap itself is rendered in
73
+ * WebGL by the editor; mounting this component enables it and sets its anchor, and unmounting hides
74
+ * it. Renders no DOM of its own.
75
+ */
76
+ export function XenolithMiniMap({ position = 'bottom-right' }) {
77
+ const editor = useXenolithEditor();
78
+ useEffect(() => {
79
+ if (!editor)
80
+ return;
81
+ editor.setMinimapVisible(true);
82
+ editor.setMinimapPosition(position);
83
+ return () => { editor.setMinimapVisible(false); };
84
+ }, [editor, position]);
85
+ return null;
86
+ }
87
+ //# sourceMappingURL=components.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components.js","sourceRoot":"","sources":["../src/components.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { XenolithEditor } from '@xenolithengine/graph-editor';
2
+ /** The live editor for the enclosing `<XenolithGraph>`, or `null` until it has mounted. In-editor
3
+ * components (`<XenolithPanel>`, `<XenolithControls>`, hooks) read it from here. */
4
+ export declare const XenolithContext: import("react").Context<XenolithEditor | null>;
5
+ /** The editor instance from the nearest `<XenolithGraph>` ancestor — `null` until it has mounted
6
+ * OR if the component is rendered outside any `<XenolithGraph>`. Use `useEditor()` when the
7
+ * component is GUARANTEED to live inside an editor — that variant throws on misuse rather than
8
+ * leaking nulls into your render. */
9
+ export declare function useXenolithEditor(): XenolithEditor | null;
10
+ /** Strict variant: returns the live editor, throwing if the component is rendered outside a
11
+ * `<XenolithGraph>` ancestor or before the editor has finished mounting. Prefer this when
12
+ * writing in-editor components — removes null-check noise at every call site and turns a
13
+ * misuse from a silent bug into a clear error. */
14
+ export declare function useEditor(): XenolithEditor;
15
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAElE;qFACqF;AACrF,eAAO,MAAM,eAAe,gDAA6C,CAAA;AAEzE;;;sCAGsC;AACtC,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,IAAI,CAEzD;AAED;;;mDAGmD;AACnD,wBAAgB,SAAS,IAAI,cAAc,CAI1C"}
@@ -0,0 +1,23 @@
1
+ 'use client';
2
+ import { createContext, useContext } from 'react';
3
+ /** The live editor for the enclosing `<XenolithGraph>`, or `null` until it has mounted. In-editor
4
+ * components (`<XenolithPanel>`, `<XenolithControls>`, hooks) read it from here. */
5
+ export const XenolithContext = createContext(null);
6
+ /** The editor instance from the nearest `<XenolithGraph>` ancestor — `null` until it has mounted
7
+ * OR if the component is rendered outside any `<XenolithGraph>`. Use `useEditor()` when the
8
+ * component is GUARANTEED to live inside an editor — that variant throws on misuse rather than
9
+ * leaking nulls into your render. */
10
+ export function useXenolithEditor() {
11
+ return useContext(XenolithContext);
12
+ }
13
+ /** Strict variant: returns the live editor, throwing if the component is rendered outside a
14
+ * `<XenolithGraph>` ancestor or before the editor has finished mounting. Prefer this when
15
+ * writing in-editor components — removes null-check noise at every call site and turns a
16
+ * misuse from a silent bug into a clear error. */
17
+ export function useEditor() {
18
+ const editor = useContext(XenolithContext);
19
+ if (!editor)
20
+ throw new Error('useEditor() called outside <XenolithGraph> or before the editor mounted. Use useXenolithEditor() if a null result is expected here.');
21
+ return editor;
22
+ }
23
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AACZ,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAGjD;qFACqF;AACrF,MAAM,CAAC,MAAM,eAAe,GAAG,aAAa,CAAwB,IAAI,CAAC,CAAA;AAEzE;;;sCAGsC;AACtC,MAAM,UAAU,iBAAiB;IAC/B,OAAO,UAAU,CAAC,eAAe,CAAC,CAAA;AACpC,CAAC;AAED;;;mDAGmD;AACnD,MAAM,UAAU,SAAS;IACvB,MAAM,MAAM,GAAG,UAAU,CAAC,eAAe,CAAC,CAAA;IAC1C,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,qIAAqI,CAAC,CAAA;IACnK,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,33 @@
1
+ import type { EditorEvents } from '@xenolithengine/graph-editor';
2
+ /** Maps each public editor event to its idiomatic React callback prop name. */
3
+ export declare const EVENT_PROP: {
4
+ readonly 'node:added': "onNodeAdded";
5
+ readonly 'node:removed': "onNodeRemoved";
6
+ readonly 'node:removing': "onNodeRemoving";
7
+ readonly 'node:moved': "onNodeMoved";
8
+ readonly 'node:click': "onNodeClick";
9
+ readonly 'node:clicking': "onNodeClicking";
10
+ readonly 'node:drop': "onNodeDrop";
11
+ readonly 'edge:connected': "onEdgeConnected";
12
+ readonly 'edge:disconnected': "onEdgeDisconnected";
13
+ readonly 'edge:connecting': "onEdgeConnecting";
14
+ readonly 'edge:disconnecting': "onEdgeDisconnecting";
15
+ readonly 'selection:changed': "onSelectionChange";
16
+ readonly 'viewport:changed': "onViewportChange";
17
+ readonly 'widget:changed': "onWidgetChange";
18
+ readonly 'widget:action': "onWidgetAction";
19
+ readonly 'graph:loaded': "onGraphLoad";
20
+ readonly 'history:changed': "onHistoryChange";
21
+ readonly 'dive:changed': "onDiveChange";
22
+ readonly 'sidebar:opened': "onSidebarOpen";
23
+ readonly 'sidebar:closed': "onSidebarClose";
24
+ readonly 'livemode:changed': "onLiveModeChange";
25
+ readonly 'node:contextmenu': "onNodeContextMenu";
26
+ readonly 'edge:contextmenu': "onEdgeContextMenu";
27
+ readonly 'canvas:contextmenu': "onCanvasContextMenu";
28
+ };
29
+ /** The React callback props derived from {@link EVENT_PROP} — one optional handler per event. */
30
+ export type EventCallbacks = {
31
+ [E in keyof EditorEvents as (typeof EVENT_PROP)[E]]?: (payload: EditorEvents[E]) => void;
32
+ };
33
+ //# sourceMappingURL=events-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events-map.d.ts","sourceRoot":"","sources":["../src/events-map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAEhE,+EAA+E;AAC/E,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;CAyBgC,CAAA;AAEvD,iGAAiG;AACjG,MAAM,MAAM,cAAc,GAAG;KAC1B,CAAC,IAAI,MAAM,YAAY,IAAI,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI;CACzF,CAAA"}
@@ -0,0 +1,28 @@
1
+ /** Maps each public editor event to its idiomatic React callback prop name. */
2
+ export const EVENT_PROP = {
3
+ 'node:added': 'onNodeAdded',
4
+ 'node:removed': 'onNodeRemoved',
5
+ 'node:removing': 'onNodeRemoving',
6
+ 'node:moved': 'onNodeMoved',
7
+ 'node:click': 'onNodeClick',
8
+ 'node:clicking': 'onNodeClicking',
9
+ 'node:drop': 'onNodeDrop',
10
+ 'edge:connected': 'onEdgeConnected',
11
+ 'edge:disconnected': 'onEdgeDisconnected',
12
+ 'edge:connecting': 'onEdgeConnecting',
13
+ 'edge:disconnecting': 'onEdgeDisconnecting',
14
+ 'selection:changed': 'onSelectionChange',
15
+ 'viewport:changed': 'onViewportChange',
16
+ 'widget:changed': 'onWidgetChange',
17
+ 'widget:action': 'onWidgetAction',
18
+ 'graph:loaded': 'onGraphLoad',
19
+ 'history:changed': 'onHistoryChange',
20
+ 'dive:changed': 'onDiveChange',
21
+ 'sidebar:opened': 'onSidebarOpen',
22
+ 'sidebar:closed': 'onSidebarClose',
23
+ 'livemode:changed': 'onLiveModeChange',
24
+ 'node:contextmenu': 'onNodeContextMenu',
25
+ 'edge:contextmenu': 'onEdgeContextMenu',
26
+ 'canvas:contextmenu': 'onCanvasContextMenu',
27
+ };
28
+ //# sourceMappingURL=events-map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events-map.js","sourceRoot":"","sources":["../src/events-map.ts"],"names":[],"mappings":"AAEA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,YAAY,EAAE,aAAa;IAC3B,cAAc,EAAE,eAAe;IAC/B,eAAe,EAAE,gBAAgB;IACjC,YAAY,EAAE,aAAa;IAC3B,YAAY,EAAE,aAAa;IAC3B,eAAe,EAAE,gBAAgB;IACjC,WAAW,EAAE,YAAY;IACzB,gBAAgB,EAAE,iBAAiB;IACnC,mBAAmB,EAAE,oBAAoB;IACzC,iBAAiB,EAAE,kBAAkB;IACrC,oBAAoB,EAAE,qBAAqB;IAC3C,mBAAmB,EAAE,mBAAmB;IACxC,kBAAkB,EAAE,kBAAkB;IACtC,gBAAgB,EAAE,gBAAgB;IAClC,eAAe,EAAE,gBAAgB;IACjC,cAAc,EAAE,aAAa;IAC7B,iBAAiB,EAAE,iBAAiB;IACpC,cAAc,EAAE,cAAc;IAC9B,gBAAgB,EAAE,eAAe;IACjC,gBAAgB,EAAE,gBAAgB;IAClC,kBAAkB,EAAE,kBAAkB;IACtC,kBAAkB,EAAI,mBAAmB;IACzC,kBAAkB,EAAI,mBAAmB;IACzC,oBAAoB,EAAE,qBAAqB;CACU,CAAA"}
@@ -0,0 +1,37 @@
1
+ import type { EditorEvents, ViewportState, Node, Edge, NodeId, XenolithGraphV1 } from '@xenolithengine/graph-editor';
2
+ /** Live array of nodes; re-renders on add/remove/move, load and undo/redo. */
3
+ export declare const useNodes: () => readonly Node[];
4
+ /** Live array of edges; re-renders on connect/disconnect, node removal, load and undo/redo. */
5
+ export declare const useEdges: () => readonly Edge[];
6
+ /** Live selection (node ids); re-renders on selection change. */
7
+ export declare const useSelection: () => readonly NodeId[];
8
+ /** Live viewport (`x`, `y`, `zoom`); re-renders on pan/zoom. */
9
+ export declare const useViewport: () => ViewportState;
10
+ /** Live serialized graph (xenolith.v1); recomputes on any graph mutation, load and undo/redo. */
11
+ export declare const useGraphJSON: () => XenolithGraphV1 | null;
12
+ /**
13
+ * Subscribe to a single editor event from a React component. Handler is auto-rebound when the
14
+ * editor changes; unsubscribed on unmount. Use this for events that don't have a dedicated hook —
15
+ * `node:click`, `edge:disconnected`, `sidebar:opened`, `livemode:changed`, `node:drop`, the four
16
+ * preventable `*-ing` events, etc.
17
+ *
18
+ * The handler is captured by closure each render — no deps array needed; the latest closure is
19
+ * always called. This matches `useSyncExternalStore` semantics and avoids stale-closure footguns.
20
+ *
21
+ * @example
22
+ * useEditorEvent('node:removing', (p) => { if (p.nodeId === lockedId) p.cancel() })
23
+ * useEditorEvent('widget:changed', (p) => setLocalCopy(p.value))
24
+ */
25
+ export declare function useEditorEvent<E extends keyof EditorEvents>(event: E, handler: (payload: EditorEvents[E]) => void): void;
26
+ /**
27
+ * `{ canUndo, canRedo, undo, redo }` — live undo/redo state + stable callable handles. Use for
28
+ * toolbars: `<button disabled={!canUndo} onClick={undo}>Undo</button>`. Re-renders whenever the
29
+ * editor's undo/redo log changes.
30
+ */
31
+ export declare function useUndoRedo(): {
32
+ canUndo: boolean;
33
+ canRedo: boolean;
34
+ undo: () => boolean;
35
+ redo: () => boolean;
36
+ };
37
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAkB,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAoEpI,8EAA8E;AAC9E,eAAO,MAAM,QAAQ,EAAE,MAAM,SAAS,IAAI,EAIzC,CAAA;AAED,+FAA+F;AAC/F,eAAO,MAAM,QAAQ,EAAE,MAAM,SAAS,IAAI,EAIzC,CAAA;AAED,iEAAiE;AACjE,eAAO,MAAM,YAAY,EAAE,MAAM,SAAS,MAAM,EAI/C,CAAA;AAED,gEAAgE;AAChE,eAAO,MAAM,WAAW,EAAE,MAAM,aAI/B,CAAA;AAED,iGAAiG;AACjG,eAAO,MAAM,YAAY,EAAE,MAAM,eAAe,GAAG,IAIlD,CAAA;AAID;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,YAAY,EACzD,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,GAC1C,IAAI,CAQN;AAID;;;;GAIG;AACH,wBAAgB,WAAW,IAAI;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,OAAO,CAAA;CAAE,CAa9G"}
package/dist/hooks.js ADDED
@@ -0,0 +1,112 @@
1
+ 'use client';
2
+ import { useCallback, useRef, useSyncExternalStore } from 'react';
3
+ import { useXenolithEditor } from './context.js';
4
+ // Canonical pattern: subscribe to an external (non-React) mutable store via `useSyncExternalStore`.
5
+ // Replaces the prior `useState + useEffect + subscribe` shape, which the React docs flag as the
6
+ // anti-pattern it explicitly fixes. See:
7
+ // - https://react.dev/reference/react/useSyncExternalStore
8
+ // - https://www.epicreact.dev/use-sync-external-store-demystified-for-practical-react-development-w5ac0
9
+ // Contract: `getSnapshot` MUST return the same reference if the underlying data hasn't changed —
10
+ // otherwise React tears down and re-renders forever. We cache by editor identity in a ref and
11
+ // only mutate the cache when an editor event actually fires.
12
+ function makeEditorStoreHook(events, compute, fallback) {
13
+ return function useEditorStore() {
14
+ const editor = useXenolithEditor();
15
+ const cacheRef = useRef({ editor: null, value: fallback });
16
+ const subscribe = useCallback((onChange) => {
17
+ if (!editor)
18
+ return () => { };
19
+ // Refresh the cache on subscribe so the first getSnapshot read returns up-to-date data.
20
+ cacheRef.current = { editor, value: compute(editor) };
21
+ // Coalesce a burst of events (e.g. a 1000-node transaction firing node:added 1000×) into
22
+ // ONE recompute + ONE React notification. Without this, the hook re-walks the graph on every
23
+ // event and a `+1000` button costs ~1M ops just for the snapshot, before React even renders.
24
+ let scheduled = false;
25
+ const update = () => {
26
+ if (scheduled)
27
+ return;
28
+ scheduled = true;
29
+ queueMicrotask(() => {
30
+ scheduled = false;
31
+ if (cacheRef.current.editor !== editor)
32
+ return; // unsubscribed mid-flight
33
+ cacheRef.current = { editor, value: compute(editor) };
34
+ onChange();
35
+ });
36
+ };
37
+ const offs = events.map((ev) => editor.on(ev, update));
38
+ return () => { for (const off of offs)
39
+ off(); };
40
+ }, [editor]);
41
+ const getSnapshot = useCallback(() => {
42
+ // Rebuild the cache only when the editor identity itself changes (null → instance, swap),
43
+ // OR (rare) the editor cleared its cache mid-flight. Identity-stable otherwise.
44
+ if (cacheRef.current.editor !== editor) {
45
+ cacheRef.current = { editor, value: editor ? compute(editor) : fallback };
46
+ }
47
+ return cacheRef.current.value;
48
+ }, [editor]);
49
+ return useSyncExternalStore(subscribe, getSnapshot, () => fallback);
50
+ };
51
+ }
52
+ const NODE_EVENTS = ['node:added', 'node:removed', 'node:moved', 'graph:loaded', 'history:changed'];
53
+ const EDGE_EVENTS = ['edge:connected', 'edge:disconnected', 'node:removed', 'graph:loaded', 'history:changed'];
54
+ const GRAPH_EVENTS = [
55
+ 'node:added', 'node:removed', 'node:moved', 'edge:connected', 'edge:disconnected',
56
+ 'widget:changed', 'graph:loaded', 'history:changed',
57
+ ];
58
+ const EMPTY_NODES = Object.freeze([]);
59
+ const EMPTY_EDGES = Object.freeze([]);
60
+ const EMPTY_SELECTION = Object.freeze([]);
61
+ const DEFAULT_VIEWPORT = Object.freeze({ x: 0, y: 0, zoom: 1 });
62
+ /** Live array of nodes; re-renders on add/remove/move, load and undo/redo. */
63
+ export const useNodes = makeEditorStoreHook(NODE_EVENTS, (e) => Object.freeze(Array.from(e.graph.nodes())), EMPTY_NODES);
64
+ /** Live array of edges; re-renders on connect/disconnect, node removal, load and undo/redo. */
65
+ export const useEdges = makeEditorStoreHook(EDGE_EVENTS, (e) => Object.freeze(Array.from(e.graph.edges())), EMPTY_EDGES);
66
+ /** Live selection (node ids); re-renders on selection change. */
67
+ export const useSelection = makeEditorStoreHook(['selection:changed'], (e) => Object.freeze([...e.selection.ids()]), EMPTY_SELECTION);
68
+ /** Live viewport (`x`, `y`, `zoom`); re-renders on pan/zoom. */
69
+ export const useViewport = makeEditorStoreHook(['viewport:changed'], (e) => e.viewport, DEFAULT_VIEWPORT);
70
+ /** Live serialized graph (xenolith.v1); recomputes on any graph mutation, load and undo/redo. */
71
+ export const useGraphJSON = makeEditorStoreHook(GRAPH_EVENTS, (e) => e.toJSON(), null);
72
+ import { useEffect } from 'react';
73
+ /**
74
+ * Subscribe to a single editor event from a React component. Handler is auto-rebound when the
75
+ * editor changes; unsubscribed on unmount. Use this for events that don't have a dedicated hook —
76
+ * `node:click`, `edge:disconnected`, `sidebar:opened`, `livemode:changed`, `node:drop`, the four
77
+ * preventable `*-ing` events, etc.
78
+ *
79
+ * The handler is captured by closure each render — no deps array needed; the latest closure is
80
+ * always called. This matches `useSyncExternalStore` semantics and avoids stale-closure footguns.
81
+ *
82
+ * @example
83
+ * useEditorEvent('node:removing', (p) => { if (p.nodeId === lockedId) p.cancel() })
84
+ * useEditorEvent('widget:changed', (p) => setLocalCopy(p.value))
85
+ */
86
+ export function useEditorEvent(event, handler) {
87
+ const editor = useXenolithEditor();
88
+ const handlerRef = useRef(handler);
89
+ handlerRef.current = handler;
90
+ useEffect(() => {
91
+ if (!editor)
92
+ return;
93
+ return editor.on(event, (payload) => handlerRef.current(payload));
94
+ }, [editor, event]);
95
+ }
96
+ const UNDO_REDO_EVENTS = ['history:changed'];
97
+ /**
98
+ * `{ canUndo, canRedo, undo, redo }` — live undo/redo state + stable callable handles. Use for
99
+ * toolbars: `<button disabled={!canUndo} onClick={undo}>Undo</button>`. Re-renders whenever the
100
+ * editor's undo/redo log changes.
101
+ */
102
+ export function useUndoRedo() {
103
+ const editor = useXenolithEditor();
104
+ const state = makeEditorStoreHook(UNDO_REDO_EVENTS, (e) => ({ canUndo: e.history.canUndo, canRedo: e.history.canRedo }), { canUndo: false, canRedo: false })();
105
+ return {
106
+ canUndo: state.canUndo,
107
+ canRedo: state.canRedo,
108
+ undo: useCallback(() => editor?.history.undo() ?? false, [editor]),
109
+ redo: useCallback(() => editor?.history.redo() ?? false, [editor]),
110
+ };
111
+ }
112
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { type CSSProperties, type ReactElement, type ReactNode, type Ref } from 'react';
2
+ import { type XenolithProps } from '@xenolithengine/graph-adapter-core';
3
+ import type { XenolithEditor } from '@xenolithengine/graph-editor';
4
+ import { type EventCallbacks } from './events-map.js';
5
+ export type { EventCallbacks } from './events-map.js';
6
+ export { EVENT_PROP } from './events-map.js';
7
+ export { XenolithContext, useXenolithEditor, useEditor } from './context.js';
8
+ export { XenolithPanel, XenolithButton, XenolithControls, XenolithMiniMap, type PanelPosition, type XenolithPanelProps, type XenolithButtonProps, type XenolithControlsProps, type XenolithMiniMapProps, } from './components.js';
9
+ export { useNodes, useEdges, useSelection, useViewport, useGraphJSON, useEditorEvent, useUndoRedo } from './hooks.js';
10
+ export { reactWidget, type WidgetProps } from './react-widget.js';
11
+ export interface XenolithGraphProps extends XenolithProps, EventCallbacks {
12
+ className?: string;
13
+ style?: CSSProperties;
14
+ /** Called once the editor is mounted and ready. */
15
+ onReady?: (editor: XenolithEditor) => void;
16
+ /** Forwarded to the host div, e.g. to grab the editor imperatively. */
17
+ containerRef?: Ref<HTMLDivElement>;
18
+ /** In-editor overlay UI: `<XenolithPanel>`, `<XenolithControls>`, or any component using the hooks. */
19
+ children?: ReactNode;
20
+ }
21
+ /**
22
+ * `<XenolithGraph>` — idiomatic React wrapper. Pass editor props (`theme`, `graph`, `minimap`, …)
23
+ * and `on*` event callbacks; the component mounts the editor into its div, keeps props in sync, and
24
+ * tears it down on unmount. The editor is WebGL/client-only — render it only in the browser.
25
+ */
26
+ export declare function XenolithGraph(props: XenolithGraphProps): ReactElement;
27
+ /** Lower-level hook: mount a XenolithGraph editor into `hostRef` and return the binding ref. */
28
+ export declare function useXenolith(hostRef: Ref<HTMLDivElement>, props?: XenolithProps): {
29
+ editorRef: {
30
+ current: XenolithEditor | null;
31
+ };
32
+ };
33
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAIA,OAAO,EAA+B,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,KAAK,GAAG,EAAE,MAAM,OAAO,CAAA;AACpH,OAAO,EAA2C,KAAK,aAAa,EAAE,MAAM,oCAAoC,CAAA;AAChH,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAChF,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAGjE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAC5E,OAAO,EACL,aAAa,EAAE,cAAc,EAAE,gBAAgB,EAAE,eAAe,EAChE,KAAK,aAAa,EAAE,KAAK,kBAAkB,EAAE,KAAK,mBAAmB,EACrE,KAAK,qBAAqB,EAAE,KAAK,oBAAoB,GACtD,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACrH,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAEjE,MAAM,WAAW,kBAAmB,SAAQ,aAAa,EAAE,cAAc;IACvE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,mDAAmD;IACnD,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAA;IAC1C,uEAAuE;IACvE,YAAY,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,CAAA;IAClC,uGAAuG;IACvG,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB;AAUD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,YAAY,CAyDrE;AAED,gGAAgG;AAChG,wBAAgB,WAAW,CACzB,OAAO,EAAE,GAAG,CAAC,cAAc,CAAC,EAC5B,KAAK,GAAE,aAAkB,GACxB;IAAE,SAAS,EAAE;QAAE,OAAO,EAAE,cAAc,GAAG,IAAI,CAAA;KAAE,CAAA;CAAE,CAqBnD"}
package/dist/index.js ADDED
@@ -0,0 +1,101 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ // `'use client'` is REQUIRED — Xenolith is WebGL/PIXI; cannot run as a React Server Component. Without
4
+ // this directive, Next.js App Router fails at the first import. Same applies to react-widget.tsx,
5
+ // hooks.ts, components.tsx, context.ts.
6
+ import { useEffect, useRef, useState } from 'react';
7
+ import { createEditorBinding } from '@xenolithengine/graph-adapter-core';
8
+ import { EVENT_PROP } from './events-map.js';
9
+ import { XenolithContext } from './context.js';
10
+ export { EVENT_PROP } from './events-map.js';
11
+ export { XenolithContext, useXenolithEditor, useEditor } from './context.js';
12
+ export { XenolithPanel, XenolithButton, XenolithControls, XenolithMiniMap, } from './components.js';
13
+ export { useNodes, useEdges, useSelection, useViewport, useGraphJSON, useEditorEvent, useUndoRedo } from './hooks.js';
14
+ export { reactWidget } from './react-widget.js';
15
+ const EDITOR_KEYS = ['theme', 'graph', 'zoomBounds', 'minimap', 'disableGrid', 'snap', 'resizeToWindow', 'fitOnLoad', 'isValidConnection'];
16
+ function pickEditorProps(props) {
17
+ const out = {};
18
+ for (const k of EDITOR_KEYS)
19
+ if (props[k] !== undefined)
20
+ out[k] = props[k];
21
+ return out;
22
+ }
23
+ /**
24
+ * `<XenolithGraph>` — idiomatic React wrapper. Pass editor props (`theme`, `graph`, `minimap`, …)
25
+ * and `on*` event callbacks; the component mounts the editor into its div, keeps props in sync, and
26
+ * tears it down on unmount. The editor is WebGL/client-only — render it only in the browser.
27
+ */
28
+ export function XenolithGraph(props) {
29
+ const hostRef = useRef(null);
30
+ const bindingRef = useRef(null);
31
+ // Editor lives in state so overlay children (panels/controls/hooks) render once it's ready.
32
+ const [editor, setEditor] = useState(null);
33
+ // Latest callbacks, read by the stable subscriptions so changing a handler never re-subscribes.
34
+ const cbRef = useRef(props);
35
+ cbRef.current = props;
36
+ // Mount once. Subscriptions read the live cbRef, so they survive prop changes.
37
+ useEffect(() => {
38
+ const host = hostRef.current;
39
+ if (!host)
40
+ return;
41
+ let disposed = false;
42
+ const offs = [];
43
+ void createEditorBinding(host, pickEditorProps(cbRef.current)).then((binding) => {
44
+ if (disposed) {
45
+ binding.destroy();
46
+ return;
47
+ }
48
+ bindingRef.current = binding;
49
+ cbRef.current.onReady?.(binding.editor);
50
+ setEditor(binding.editor);
51
+ for (const [event, prop] of Object.entries(EVENT_PROP)) {
52
+ offs.push(binding.on(event, (payload) => {
53
+ ;
54
+ cbRef.current[prop]?.(payload);
55
+ }));
56
+ }
57
+ });
58
+ return () => {
59
+ disposed = true;
60
+ for (const off of offs)
61
+ off();
62
+ bindingRef.current?.destroy();
63
+ bindingRef.current = null;
64
+ setEditor(null);
65
+ };
66
+ }, []);
67
+ // Sync editor props on every render (reference-diffed inside the binding).
68
+ useEffect(() => { bindingRef.current?.setProps(pickEditorProps(props)); });
69
+ return (_jsx("div", { ref: (el) => {
70
+ hostRef.current = el;
71
+ if (typeof props.containerRef === 'function')
72
+ props.containerRef(el);
73
+ else if (props.containerRef)
74
+ props.containerRef.current = el;
75
+ }, className: props.className, style: props.style, children: _jsx(XenolithContext.Provider, { value: editor, children: editor ? props.children : null }) }));
76
+ }
77
+ /** Lower-level hook: mount a XenolithGraph editor into `hostRef` and return the binding ref. */
78
+ export function useXenolith(hostRef, props = {}) {
79
+ const editorRef = useRef(null);
80
+ const propsRef = useRef(props);
81
+ propsRef.current = props;
82
+ const bindingRef = useRef(null);
83
+ useEffect(() => {
84
+ const el = hostRef.current;
85
+ if (!el)
86
+ return;
87
+ let disposed = false;
88
+ void createEditorBinding(el, propsRef.current).then((binding) => {
89
+ if (disposed) {
90
+ binding.destroy();
91
+ return;
92
+ }
93
+ bindingRef.current = binding;
94
+ editorRef.current = binding.editor;
95
+ });
96
+ return () => { disposed = true; bindingRef.current?.destroy(); bindingRef.current = null; editorRef.current = null; };
97
+ }, []);
98
+ useEffect(() => { bindingRef.current?.setProps(props); });
99
+ return { editorRef };
100
+ }
101
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { DomWidgetController } from '@xenolithengine/graph-editor';
3
+ /** Props every React-component widget receives. `value` is the widget's stored value (node.state),
4
+ * `setValue` commits a change (undoable). `accent`/`text`/`muted` come from the active theme. */
5
+ export interface WidgetProps {
6
+ value: unknown;
7
+ setValue: (v: unknown) => void;
8
+ accent: string;
9
+ text: string;
10
+ muted: string;
11
+ width: number;
12
+ height: number;
13
+ }
14
+ /** Bridge a React component into a XenolithGraph custom widget. This is the heart of "bring your
15
+ * own UI": `editor.registerWidget('name', reactWidget(MyComponent))` renders `MyComponent` as the
16
+ * node's widget, with full React state/hooks/libraries available. */
17
+ export declare function reactWidget(Component: (props: WidgetProps) => ReactNode): DomWidgetController;
18
+ //# sourceMappingURL=react-widget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-widget.d.ts","sourceRoot":"","sources":["../src/react-widget.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAA;AAEvE;kGACkG;AAClG,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;sEAEsE;AACtE,wBAAgB,WAAW,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,SAAS,GAAG,mBAAmB,CAe7F"}
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { createRoot } from 'react-dom/client';
4
+ /** Bridge a React component into a XenolithGraph custom widget. This is the heart of "bring your
5
+ * own UI": `editor.registerWidget('name', reactWidget(MyComponent))` renders `MyComponent` as the
6
+ * node's widget, with full React state/hooks/libraries available. */
7
+ export function reactWidget(Component) {
8
+ let root = null;
9
+ let setValue = () => { };
10
+ const toProps = (c) => ({ value: c.value, setValue, accent: c.accent, text: c.text, muted: c.muted, width: c.width, height: c.height });
11
+ return {
12
+ mount(el, c) {
13
+ setValue = c.setValue;
14
+ root = createRoot(el);
15
+ root.render(_jsx(Component, { ...toProps(c) }));
16
+ return () => { root?.unmount(); root = null; };
17
+ },
18
+ update(c) { root?.render(_jsx(Component, { ...toProps(c) })); },
19
+ };
20
+ }
21
+ //# sourceMappingURL=react-widget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-widget.js","sourceRoot":"","sources":["../src/react-widget.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AACZ,OAAO,EAAE,UAAU,EAAa,MAAM,kBAAkB,CAAA;AAgBxD;;sEAEsE;AACtE,MAAM,UAAU,WAAW,CAAC,SAA4C;IACtE,IAAI,IAAI,GAAgB,IAAI,CAAA;IAC5B,IAAI,QAAQ,GAAyB,GAAG,EAAE,GAAE,CAAC,CAAA;IAC7C,MAAM,OAAO,GAAG,CAAC,CAAiG,EAAe,EAAE,CACjI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAA;IAElH,OAAO;QACL,KAAK,CAAC,EAAE,EAAE,CAAC;YACT,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAA;YACrB,IAAI,GAAG,UAAU,CAAC,EAAE,CAAC,CAAA;YACrB,IAAI,CAAC,MAAM,CAAC,KAAC,SAAS,OAAK,OAAO,CAAC,CAAC,CAAC,GAAI,CAAC,CAAA;YAC1C,OAAO,GAAG,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,GAAG,IAAI,CAAA,CAAC,CAAC,CAAA;QAC/C,CAAC;QACD,MAAM,CAAC,CAAC,IAAI,IAAI,EAAE,MAAM,CAAC,KAAC,SAAS,OAAK,OAAO,CAAC,CAAC,CAAC,GAAI,CAAC,CAAA,CAAC,CAAC;KAC1D,CAAA;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@xenolithengine/graph-react",
3
+ "version": "0.7.0-beta.0",
4
+ "description": "React adapter for XenolithGraph — <XenolithGraph> component + useXenolith hook.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md"
19
+ ],
20
+ "sideEffects": false,
21
+ "dependencies": {
22
+ "@xenolithengine/graph-adapter-core": "0.7.0-beta.0",
23
+ "@xenolithengine/graph-editor": "0.7.0-beta.0"
24
+ },
25
+ "peerDependencies": {
26
+ "pixi.js": "^8.6.0",
27
+ "react": ">=18",
28
+ "react-dom": ">=18"
29
+ },
30
+ "devDependencies": {
31
+ "@testing-library/react": "^16.1.0",
32
+ "@types/react": "^19.0.0",
33
+ "@types/react-dom": "^19",
34
+ "jsdom": "^25.0.0",
35
+ "pixi.js": "^8.6.0",
36
+ "react": "^19.0.0",
37
+ "react-dom": "^19.0.0",
38
+ "typescript": "^5.7.0",
39
+ "vitest": "^3.0.0"
40
+ },
41
+ "keywords": [
42
+ "react",
43
+ "node-editor",
44
+ "node-graph",
45
+ "react-flow-alternative",
46
+ "blueprint",
47
+ "flow",
48
+ "graph-editor"
49
+ ],
50
+ "publishConfig": {
51
+ "access": "public"
52
+ },
53
+ "repository": {
54
+ "type": "git",
55
+ "url": "git+https://github.com/XenolithEngine/xenolith-graph.git",
56
+ "directory": "packages/react"
57
+ },
58
+ "homepage": "https://xenolithengine.github.io/xenolith-graph/",
59
+ "bugs": {
60
+ "url": "https://github.com/XenolithEngine/xenolith-graph/issues"
61
+ },
62
+ "author": "XenolithEngine",
63
+ "scripts": {
64
+ "build": "tsc -b",
65
+ "dev": "tsc -b --watch",
66
+ "test": "vitest run --passWithNoTests",
67
+ "lint": "eslint src",
68
+ "typecheck": "tsc --noEmit"
69
+ }
70
+ }