@xrift/world-components 0.2.5 → 0.4.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/README.md CHANGED
@@ -48,6 +48,59 @@ function MyWorld() {
48
48
  }
49
49
  ```
50
50
 
51
+ ### useInstanceState フック
52
+
53
+ インスタンス全体で同期される状態を管理するフックです。React の `useState` と同じAPIを提供します。
54
+
55
+ ```tsx
56
+ import { useInstanceState } from '@xrift/world-components'
57
+
58
+ function MyWorld() {
59
+ // インスタンス全体で同期される状態
60
+ const [buttonState, setButtonState] = useInstanceState('button-1', { enabled: false })
61
+
62
+ return (
63
+ <Interactable
64
+ id="button-1"
65
+ onInteract={() => {
66
+ // 状態を更新(全てのクライアントで同期される)
67
+ setButtonState({ enabled: !buttonState.enabled })
68
+ }}
69
+ >
70
+ <mesh>
71
+ <meshStandardMaterial color={buttonState.enabled ? 'green' : 'red'} />
72
+ </mesh>
73
+ </Interactable>
74
+ )
75
+ }
76
+ ```
77
+
78
+ #### 使用方法
79
+
80
+ ```tsx
81
+ const [state, setState] = useInstanceState<T>(stateId, initialState)
82
+ ```
83
+
84
+ - `stateId`: 状態の一意識別子(インスタンス内で一意である必要があります)
85
+ - `initialState`: 初期状態
86
+ - `setState`: 状態を更新する関数(直接値 or 関数型アップデートをサポート)
87
+
88
+ #### 関数型アップデート
89
+
90
+ ```tsx
91
+ // 直接値を設定
92
+ setState({ enabled: true })
93
+
94
+ // 前の状態を基に更新
95
+ setState(prev => ({ enabled: !prev.enabled }))
96
+ ```
97
+
98
+ #### 注意事項
99
+
100
+ - Context未設定時はローカル `useState` として動作します
101
+ - プラットフォーム側(xrift-frontend)がWebSocket実装を注入することで、インスタンス全体での同期が有効になります
102
+ - 状態はシリアライズ可能な値(JSON)である必要があります
103
+
51
104
  ### Interactable コンポーネント
52
105
 
53
106
  3Dオブジェクトをインタラクション可能にするラッパーコンポーネントです。
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Interactable/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAsE,KAAK,EAAE,EAAE,MAAM,OAAO,CAAA;AAGnG,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAIpC,eAAO,MAAM,YAAY,EAAE,EAAE,CAAC,KAAK,CA0GlC,CAAA;AAED,YAAY,EAAE,KAAK,IAAI,iBAAiB,EAAE,MAAM,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Interactable/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAsE,KAAK,EAAE,EAAE,MAAM,OAAO,CAAA;AAGnG,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAIpC,eAAO,MAAM,YAAY,EAAE,EAAE,CAAC,KAAK,CAgHlC,CAAA;AAED,YAAY,EAAE,KAAK,IAAI,iBAAiB,EAAE,MAAM,SAAS,CAAA"}
@@ -4,9 +4,9 @@ import { Children, cloneElement, isValidElement, useEffect, useMemo, useRef } fr
4
4
  import { useXRift } from '../../contexts/XRiftContext';
5
5
  const INTERACTABLE_LAYER = 10;
6
6
  export const Interactable = ({ id, type = 'button', onInteract, interactionText, enabled = true, children, }) => {
7
- const { currentTarget } = useXRift();
7
+ const { currentTarget, registerInteractable, unregisterInteractable } = useXRift();
8
8
  const groupRef = useRef(null);
9
- // userDataにインタラクション情報を設定 & レイヤー設定
9
+ // userDataにインタラクション情報を設定 & レイヤー設定 & オブジェクト登録
10
10
  useEffect(() => {
11
11
  const object = groupRef.current;
12
12
  if (!object)
@@ -24,8 +24,12 @@ export const Interactable = ({ id, type = 'button', onInteract, interactionText,
24
24
  object.traverse((child) => {
25
25
  child.layers.enable(INTERACTABLE_LAYER);
26
26
  });
27
+ // インタラクト可能オブジェクトとして登録
28
+ registerInteractable(object);
27
29
  // クリーンアップ: userDataからインタラクション情報を削除
28
30
  return () => {
31
+ // 登録解除
32
+ unregisterInteractable(object);
29
33
  if (object.userData) {
30
34
  delete object.userData.id;
31
35
  delete object.userData.type;
@@ -38,7 +42,7 @@ export const Interactable = ({ id, type = 'button', onInteract, interactionText,
38
42
  child.layers.disable(INTERACTABLE_LAYER);
39
43
  });
40
44
  };
41
- }, [id, type, onInteract, interactionText, enabled]);
45
+ }, [id, type, onInteract, interactionText, enabled, registerInteractable, unregisterInteractable]);
42
46
  // 現在のターゲットかどうかで視覚的フィードバックを提供
43
47
  const isTargeted = currentTarget !== null && currentTarget.uuid === groupRef.current?.uuid;
44
48
  // 再帰的に子要素を探索して、すべての<mesh>に<Outlines>を追加する関数
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/Interactable/index.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/Interactable/index.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import { type ReactNode } from 'react';
2
+ /**
3
+ * インスタンス状態を管理するためのインターフェース
4
+ * プラットフォーム側(xrift-frontend)がWebSocket実装を注入する
5
+ */
6
+ export interface InstanceStateContextValue {
7
+ /**
8
+ * 指定されたIDの状態を取得する
9
+ * @param stateId 状態の一意識別子
10
+ * @param initialState 初期状態
11
+ * @returns [現在の状態, 状態更新関数]
12
+ */
13
+ getState: <T>(stateId: string, initialState: T) => [T, (state: T | ((prevState: T) => T)) => void];
14
+ }
15
+ /**
16
+ * インスタンス状態を管理するContext
17
+ * ワールド作成者はこのContextを通じてインスタンス全体で同期される状態にアクセスする
18
+ */
19
+ export declare const InstanceStateContext: import("react").Context<InstanceStateContextValue>;
20
+ interface Props {
21
+ /**
22
+ * プラットフォーム側が提供する実装(WebSocket同期など)
23
+ * 未指定の場合はデフォルト実装(ローカルstate)が使用される
24
+ */
25
+ implementation?: InstanceStateContextValue;
26
+ children: ReactNode;
27
+ }
28
+ /**
29
+ * インスタンス状態を提供するContextProvider
30
+ * プラットフォーム側(xrift-frontend)がWebSocket実装を注入するために使用
31
+ *
32
+ * @example
33
+ * // プラットフォーム側での使用例
34
+ * <InstanceStateProvider implementation={webSocketImplementation}>
35
+ * <WorldComponent />
36
+ * </InstanceStateProvider>
37
+ */
38
+ export declare const InstanceStateProvider: ({ implementation, children }: Props) => import("react/jsx-runtime").JSX.Element;
39
+ /**
40
+ * インスタンス状態のContextを取得するhook
41
+ * 通常、ワールド作成者は直接このhookを使用せず、useInstanceStateを使用する
42
+ */
43
+ export declare const useInstanceStateContext: () => InstanceStateContextValue;
44
+ export {};
45
+ //# sourceMappingURL=InstanceStateContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InstanceStateContext.d.ts","sourceRoot":"","sources":["../../src/contexts/InstanceStateContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAwB,MAAM,OAAO,CAAA;AAE3E;;;GAGG;AACH,MAAM,WAAW,yBAAyB;IACxC;;;;;OAKG;IACH,QAAQ,EAAE,CAAC,CAAC,EACV,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,CAAC,KACZ,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,CAAA;CACrD;AA2BD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,oDAEhC,CAAA;AAED,UAAU,KAAK;IACb;;;OAGG;IACH,cAAc,CAAC,EAAE,yBAAyB,CAAA;IAC1C,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,GAAI,8BAA8B,KAAK,4CASxE,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,QAAO,yBAE1C,CAAA"}
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext, useState } from 'react';
3
+ /**
4
+ * デフォルト実装: Context未設定時はローカルuseStateとして動作
5
+ * 開発時やテスト時に使用される
6
+ */
7
+ const createDefaultImplementation = () => {
8
+ const stateMap = new Map();
9
+ const setterMap = new Map();
10
+ return {
11
+ getState: (stateId, initialState) => {
12
+ // このstateIdで既に状態が存在する場合は既存の状態を返す
13
+ if (stateMap.has(stateId)) {
14
+ return [stateMap.get(stateId), setterMap.get(stateId)];
15
+ }
16
+ // 新しい状態を作成
17
+ const [state, setState] = useState(initialState);
18
+ stateMap.set(stateId, state);
19
+ setterMap.set(stateId, setState);
20
+ return [state, setState];
21
+ },
22
+ };
23
+ };
24
+ /**
25
+ * インスタンス状態を管理するContext
26
+ * ワールド作成者はこのContextを通じてインスタンス全体で同期される状態にアクセスする
27
+ */
28
+ export const InstanceStateContext = createContext(createDefaultImplementation());
29
+ /**
30
+ * インスタンス状態を提供するContextProvider
31
+ * プラットフォーム側(xrift-frontend)がWebSocket実装を注入するために使用
32
+ *
33
+ * @example
34
+ * // プラットフォーム側での使用例
35
+ * <InstanceStateProvider implementation={webSocketImplementation}>
36
+ * <WorldComponent />
37
+ * </InstanceStateProvider>
38
+ */
39
+ export const InstanceStateProvider = ({ implementation, children }) => {
40
+ const defaultImplementation = createDefaultImplementation();
41
+ const value = implementation || defaultImplementation;
42
+ return (_jsx(InstanceStateContext.Provider, { value: value, children: children }));
43
+ };
44
+ /**
45
+ * インスタンス状態のContextを取得するhook
46
+ * 通常、ワールド作成者は直接このhookを使用せず、useInstanceStateを使用する
47
+ */
48
+ export const useInstanceStateContext = () => {
49
+ return useContext(InstanceStateContext);
50
+ };
51
+ //# sourceMappingURL=InstanceStateContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InstanceStateContext.js","sourceRoot":"","sources":["../../src/contexts/InstanceStateContext.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAkB,UAAU,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAmB3E;;;GAGG;AACH,MAAM,2BAA2B,GAAG,GAA8B,EAAE;IAClE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAe,CAAA;IACvC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAgC,CAAA;IAEzD,OAAO;QACL,QAAQ,EAAE,CAAK,OAAe,EAAE,YAAe,EAAE,EAAE;YACjD,iCAAiC;YACjC,IAAI,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC1B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,OAAO,CAAE,CAAC,CAAA;YACzD,CAAC;YAED,WAAW;YACX,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAI,YAAY,CAAC,CAAA;YACnD,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;YAC5B,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;YAEhC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;QAC1B,CAAC;KACF,CAAA;AACH,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,aAAa,CAC/C,2BAA2B,EAAE,CAC9B,CAAA;AAWD;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,EAAE,cAAc,EAAE,QAAQ,EAAS,EAAE,EAAE;IAC3E,MAAM,qBAAqB,GAAG,2BAA2B,EAAE,CAAA;IAC3D,MAAM,KAAK,GAAG,cAAc,IAAI,qBAAqB,CAAA;IAErD,OAAO,CACL,KAAC,oBAAoB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YACxC,QAAQ,GACqB,CACjC,CAAA;AACH,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAA8B,EAAE;IACrE,OAAO,UAAU,CAAC,oBAAoB,CAAC,CAAA;AACzC,CAAC,CAAA"}
@@ -1,5 +1,6 @@
1
1
  import { type ReactNode } from 'react';
2
2
  import type { Object3D } from 'three';
3
+ import { type InstanceStateContextValue } from './InstanceStateContext';
3
4
  export interface XRiftContextValue {
4
5
  /**
5
6
  * ワールドのベースURL(CDNのディレクトリパス)
@@ -11,6 +12,19 @@ export interface XRiftContextValue {
11
12
  * xrift-frontend側のRaycastDetectorが設定する
12
13
  */
13
14
  currentTarget: Object3D | null;
15
+ /**
16
+ * インタラクト可能なオブジェクトのセット
17
+ * レイキャストのパフォーマンス最適化のために使用
18
+ */
19
+ interactableObjects: Set<Object3D>;
20
+ /**
21
+ * インタラクト可能なオブジェクトを登録
22
+ */
23
+ registerInteractable: (object: Object3D) => void;
24
+ /**
25
+ * インタラクト可能なオブジェクトの登録を解除
26
+ */
27
+ unregisterInteractable: (object: Object3D) => void;
14
28
  }
15
29
  /**
16
30
  * XRift ワールドの情報を提供するContext
@@ -20,6 +34,11 @@ export declare const XRiftContext: import("react").Context<XRiftContextValue | n
20
34
  interface Props {
21
35
  baseUrl: string;
22
36
  currentTarget?: Object3D | null;
37
+ /**
38
+ * インスタンス状態管理の実装(オプション)
39
+ * 指定しない場合はデフォルト実装(ローカルstate)が使用される
40
+ */
41
+ instanceStateImplementation?: InstanceStateContextValue;
23
42
  children: ReactNode;
24
43
  }
25
44
  /**
@@ -27,7 +46,7 @@ interface Props {
27
46
  * Module Federationで動的にロードされたワールドコンポーネントに
28
47
  * 必要な情報を注入するために使用
29
48
  */
30
- export declare const XRiftProvider: ({ baseUrl, currentTarget, children }: Props) => import("react/jsx-runtime").JSX.Element;
49
+ export declare const XRiftProvider: ({ baseUrl, currentTarget, instanceStateImplementation, children }: Props) => import("react/jsx-runtime").JSX.Element;
31
50
  /**
32
51
  * XRift ワールドの情報を取得するhook
33
52
  * ワールドプロジェクト側でアセットの相対パスを絶対パスに変換する際に使用
@@ -1 +1 @@
1
- {"version":3,"file":"XRiftContext.d.ts","sourceRoot":"","sources":["../../src/contexts/XRiftContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAc,MAAM,OAAO,CAAA;AACjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAErC,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,aAAa,EAAE,QAAQ,GAAG,IAAI,CAAA;CAK/B;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,mDAAgD,CAAA;AAEzE,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAA;IACf,aAAa,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAA;IAC/B,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,sCAA6C,KAAK,4CAW/E,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,QAAQ,QAAO,iBAQ3B,CAAA"}
1
+ {"version":3,"file":"XRiftContext.d.ts","sourceRoot":"","sources":["../../src/contexts/XRiftContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAqC,MAAM,OAAO,CAAA;AACxF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACrC,OAAO,EAAyB,KAAK,yBAAyB,EAAE,MAAM,wBAAwB,CAAA;AAE9F,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,aAAa,EAAE,QAAQ,GAAG,IAAI,CAAA;IAC9B;;;OAGG;IACH,mBAAmB,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAA;IAClC;;OAEG;IACH,oBAAoB,EAAE,CAAC,MAAM,EAAE,QAAQ,KAAK,IAAI,CAAA;IAChD;;OAEG;IACH,sBAAsB,EAAE,CAAC,MAAM,EAAE,QAAQ,KAAK,IAAI,CAAA;CAKnD;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,mDAAgD,CAAA;AAEzE,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAA;IACf,aAAa,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAA;IAC/B;;;OAGG;IACH,2BAA2B,CAAC,EAAE,yBAAyB,CAAA;IACvD,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,mEAK3B,KAAK,4CA6BP,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,QAAQ,QAAO,iBAQ3B,CAAA"}
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { createContext, useContext } from 'react';
2
+ import { createContext, useCallback, useContext, useState } from 'react';
3
+ import { InstanceStateProvider } from './InstanceStateContext';
3
4
  /**
4
5
  * XRift ワールドの情報を提供するContext
5
6
  * ワールド側でこのContextを直接参照して情報を取得できる
@@ -10,11 +11,24 @@ export const XRiftContext = createContext(null);
10
11
  * Module Federationで動的にロードされたワールドコンポーネントに
11
12
  * 必要な情報を注入するために使用
12
13
  */
13
- export const XRiftProvider = ({ baseUrl, currentTarget = null, children }) => {
14
+ export const XRiftProvider = ({ baseUrl, currentTarget = null, instanceStateImplementation, children }) => {
15
+ // インタラクト可能なオブジェクトの管理
16
+ const [interactableObjects] = useState(() => new Set());
17
+ // オブジェクトの登録
18
+ const registerInteractable = useCallback((object) => {
19
+ interactableObjects.add(object);
20
+ }, [interactableObjects]);
21
+ // オブジェクトの登録解除
22
+ const unregisterInteractable = useCallback((object) => {
23
+ interactableObjects.delete(object);
24
+ }, [interactableObjects]);
14
25
  return (_jsx(XRiftContext.Provider, { value: {
15
26
  baseUrl,
16
27
  currentTarget,
17
- }, children: children }));
28
+ interactableObjects,
29
+ registerInteractable,
30
+ unregisterInteractable,
31
+ }, children: _jsx(InstanceStateProvider, { implementation: instanceStateImplementation, children: children }) }));
18
32
  };
19
33
  /**
20
34
  * XRift ワールドの情報を取得するhook
@@ -1 +1 @@
1
- {"version":3,"file":"XRiftContext.js","sourceRoot":"","sources":["../../src/contexts/XRiftContext.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAkB,UAAU,EAAE,MAAM,OAAO,CAAA;AAoBjE;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CAA2B,IAAI,CAAC,CAAA;AAQzE;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI,EAAE,QAAQ,EAAS,EAAE,EAAE;IAClF,OAAO,CACL,KAAC,YAAY,CAAC,QAAQ,IACpB,KAAK,EAAE;YACL,OAAO;YACP,aAAa;SACd,YAEA,QAAQ,GACa,CACzB,CAAA;AACH,CAAC,CAAA;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAsB,EAAE;IAC9C,MAAM,OAAO,GAAG,UAAU,CAAC,YAAY,CAAC,CAAA;IAExC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAA;IAC/D,CAAC;IAED,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA"}
1
+ {"version":3,"file":"XRiftContext.js","sourceRoot":"","sources":["../../src/contexts/XRiftContext.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAkB,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAExF,OAAO,EAAE,qBAAqB,EAAkC,MAAM,wBAAwB,CAAA;AAgC9F;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CAA2B,IAAI,CAAC,CAAA;AAazE;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,EAC5B,OAAO,EACP,aAAa,GAAG,IAAI,EACpB,2BAA2B,EAC3B,QAAQ,EACF,EAAE,EAAE;IACV,qBAAqB;IACrB,MAAM,CAAC,mBAAmB,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,GAAG,EAAY,CAAC,CAAA;IAEjE,YAAY;IACZ,MAAM,oBAAoB,GAAG,WAAW,CAAC,CAAC,MAAgB,EAAE,EAAE;QAC5D,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACjC,CAAC,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAA;IAEzB,cAAc;IACd,MAAM,sBAAsB,GAAG,WAAW,CAAC,CAAC,MAAgB,EAAE,EAAE;QAC9D,mBAAmB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACpC,CAAC,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAA;IAEzB,OAAO,CACL,KAAC,YAAY,CAAC,QAAQ,IACpB,KAAK,EAAE;YACL,OAAO;YACP,aAAa;YACb,mBAAmB;YACnB,oBAAoB;YACpB,sBAAsB;SACvB,YAED,KAAC,qBAAqB,IAAC,cAAc,EAAE,2BAA2B,YAC/D,QAAQ,GACa,GACF,CACzB,CAAA;AACH,CAAC,CAAA;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAsB,EAAE;IAC9C,MAAM,OAAO,GAAG,UAAU,CAAC,YAAY,CAAC,CAAA;IAExC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAA;IAC/D,CAAC;IAED,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * インスタンス全体で同期される状態を管理するhook
3
+ *
4
+ * React の useState と同じAPIを提供します。
5
+ * Context未設定時はローカルstateとして動作し、
6
+ * プラットフォーム側が実装を注入することでWebSocket同期などが可能になります。
7
+ *
8
+ * @param stateId 状態の一意識別子(インスタンス内で一意である必要があります)
9
+ * @param initialState 初期状態
10
+ * @returns [現在の状態, 状態更新関数]
11
+ *
12
+ * @example
13
+ * // ボタンの有効/無効状態を管理
14
+ * const [buttonState, setButtonState] = useInstanceState('button-1', { enabled: false })
15
+ *
16
+ * // 直接値を設定
17
+ * setButtonState({ enabled: true })
18
+ *
19
+ * // 関数型アップデート
20
+ * setButtonState(prev => ({ enabled: !prev.enabled }))
21
+ *
22
+ * @example
23
+ * // より複雑な状態の管理
24
+ * interface DoorState {
25
+ * isOpen: boolean
26
+ * openedBy: string | null
27
+ * }
28
+ *
29
+ * const [doorState, setDoorState] = useInstanceState<DoorState>('main-door', {
30
+ * isOpen: false,
31
+ * openedBy: null
32
+ * })
33
+ *
34
+ * // ドアを開く
35
+ * setDoorState({ isOpen: true, openedBy: 'player-123' })
36
+ */
37
+ export declare function useInstanceState<T>(stateId: string, initialState: T): [T, (state: T | ((prevState: T) => T)) => void];
38
+ //# sourceMappingURL=useInstanceState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useInstanceState.d.ts","sourceRoot":"","sources":["../../src/hooks/useInstanceState.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,CAAC,GACd,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,CAGjD"}
@@ -0,0 +1,42 @@
1
+ import { useInstanceStateContext } from '../contexts/InstanceStateContext';
2
+ /**
3
+ * インスタンス全体で同期される状態を管理するhook
4
+ *
5
+ * React の useState と同じAPIを提供します。
6
+ * Context未設定時はローカルstateとして動作し、
7
+ * プラットフォーム側が実装を注入することでWebSocket同期などが可能になります。
8
+ *
9
+ * @param stateId 状態の一意識別子(インスタンス内で一意である必要があります)
10
+ * @param initialState 初期状態
11
+ * @returns [現在の状態, 状態更新関数]
12
+ *
13
+ * @example
14
+ * // ボタンの有効/無効状態を管理
15
+ * const [buttonState, setButtonState] = useInstanceState('button-1', { enabled: false })
16
+ *
17
+ * // 直接値を設定
18
+ * setButtonState({ enabled: true })
19
+ *
20
+ * // 関数型アップデート
21
+ * setButtonState(prev => ({ enabled: !prev.enabled }))
22
+ *
23
+ * @example
24
+ * // より複雑な状態の管理
25
+ * interface DoorState {
26
+ * isOpen: boolean
27
+ * openedBy: string | null
28
+ * }
29
+ *
30
+ * const [doorState, setDoorState] = useInstanceState<DoorState>('main-door', {
31
+ * isOpen: false,
32
+ * openedBy: null
33
+ * })
34
+ *
35
+ * // ドアを開く
36
+ * setDoorState({ isOpen: true, openedBy: 'player-123' })
37
+ */
38
+ export function useInstanceState(stateId, initialState) {
39
+ const context = useInstanceStateContext();
40
+ return context.getState(stateId, initialState);
41
+ }
42
+ //# sourceMappingURL=useInstanceState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useInstanceState.js","sourceRoot":"","sources":["../../src/hooks/useInstanceState.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAA;AAE1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAe,EACf,YAAe;IAEf,MAAM,OAAO,GAAG,uBAAuB,EAAE,CAAA;IACzC,OAAO,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC,CAAA;AAChD,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export { XRiftContext, XRiftProvider, useXRift, type XRiftContextValue, } from './contexts/XRiftContext';
2
+ export { InstanceStateContext, useInstanceStateContext, type InstanceStateContextValue, } from './contexts/InstanceStateContext';
2
3
  export { Interactable, type InteractableProps, } from './components/Interactable';
4
+ export { useInstanceState } from './hooks/useInstanceState';
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,YAAY,EACZ,aAAa,EACb,QAAQ,EACR,KAAK,iBAAiB,GACvB,MAAM,yBAAyB,CAAA;AAGhC,OAAO,EACL,YAAY,EACZ,KAAK,iBAAiB,GACvB,MAAM,2BAA2B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,YAAY,EACZ,aAAa,EACb,QAAQ,EACR,KAAK,iBAAiB,GACvB,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,KAAK,yBAAyB,GAC/B,MAAM,iCAAiC,CAAA;AAGxC,OAAO,EACL,YAAY,EACZ,KAAK,iBAAiB,GACvB,MAAM,2BAA2B,CAAA;AAGlC,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA"}
package/dist/index.js CHANGED
@@ -1,5 +1,8 @@
1
1
  // Contexts
2
2
  export { XRiftContext, XRiftProvider, useXRift, } from './contexts/XRiftContext';
3
+ export { InstanceStateContext, useInstanceStateContext, } from './contexts/InstanceStateContext';
3
4
  // Components
4
5
  export { Interactable, } from './components/Interactable';
6
+ // Hooks
7
+ export { useInstanceState } from './hooks/useInstanceState';
5
8
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,WAAW;AACX,OAAO,EACL,YAAY,EACZ,aAAa,EACb,QAAQ,GAET,MAAM,yBAAyB,CAAA;AAEhC,aAAa;AACb,OAAO,EACL,YAAY,GAEb,MAAM,2BAA2B,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,WAAW;AACX,OAAO,EACL,YAAY,EACZ,aAAa,EACb,QAAQ,GAET,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EACL,oBAAoB,EACpB,uBAAuB,GAExB,MAAM,iCAAiC,CAAA;AAExC,aAAa;AACb,OAAO,EACL,YAAY,GAEb,MAAM,2BAA2B,CAAA;AAElC,QAAQ;AACR,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xrift/world-components",
3
- "version": "0.2.5",
3
+ "version": "0.4.0",
4
4
  "description": "Shared components and utilities for Xrift worlds",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",