@luna-editor/engine 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Player.js +191 -37
- package/dist/components/BackgroundLayer.js +7 -5
- package/dist/components/GameScreen.d.ts +8 -2
- package/dist/components/GameScreen.js +41 -7
- package/dist/components/OverlayUI.d.ts +2 -3
- package/dist/components/OverlayUI.js +3 -14
- package/dist/components/PluginComponentProvider.d.ts +1 -1
- package/dist/components/PluginComponentProvider.js +20 -2
- package/dist/contexts/DataContext.d.ts +3 -1
- package/dist/contexts/DataContext.js +38 -9
- package/dist/contexts/PlaybackTextContext.d.ts +32 -0
- package/dist/contexts/PlaybackTextContext.js +29 -0
- package/dist/data-api-types.d.ts +251 -0
- package/dist/data-api-types.js +6 -0
- package/dist/emotion-effect-types.d.ts +86 -0
- package/dist/emotion-effect-types.js +6 -0
- package/dist/hooks/useBacklog.js +0 -1
- package/dist/hooks/useFontLoader.d.ts +9 -2
- package/dist/hooks/useFontLoader.js +126 -87
- package/dist/hooks/useImagePreloader.d.ts +5 -0
- package/dist/hooks/useImagePreloader.js +53 -0
- package/dist/hooks/usePluginAPI.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/plugin/PluginManager.d.ts +21 -4
- package/dist/plugin/PluginManager.js +132 -58
- package/dist/plugin/luna-react.d.ts +41 -0
- package/dist/plugin/luna-react.js +99 -0
- package/dist/sdk.d.ts +31 -217
- package/dist/sdk.js +12 -2
- package/dist/types.d.ts +13 -1
- package/package.json +6 -6
package/dist/Player.js
CHANGED
|
@@ -24,6 +24,7 @@ import { PluginComponentProvider } from "./components/PluginComponentProvider";
|
|
|
24
24
|
import { TimeWaitIndicator } from "./components/TimeWaitIndicator";
|
|
25
25
|
import { AudioProvider } from "./contexts/AudioContext";
|
|
26
26
|
import { DataProvider } from "./contexts/DataContext";
|
|
27
|
+
import { PlaybackTextProvider } from "./contexts/PlaybackTextContext";
|
|
27
28
|
import { useBacklog } from "./hooks/useBacklog";
|
|
28
29
|
import { useConversationBranch } from "./hooks/useConversationBranch";
|
|
29
30
|
import { useFontLoader } from "./hooks/useFontLoader";
|
|
@@ -39,20 +40,26 @@ import { convertBranchBlockToScenarioBlock } from "./utils/branchBlockConverter"
|
|
|
39
40
|
import { BranchNavigator } from "./utils/branchNavigator";
|
|
40
41
|
import { VariableManager } from "./utils/variableManager";
|
|
41
42
|
export const Player = ({ scenario: scenarioProp, settings, plugins = [], sounds = [], onEnd, onScenarioEnd, onScenarioStart, onScenarioCancelled, onSettingsChange, className, autoplay = false, preventDefaultScroll = true, screenSize: screenSizeProp, disableKeyboardNavigation = false, }) => {
|
|
42
|
-
var _a, _b, _c, _d
|
|
43
|
+
var _a, _b, _c, _d;
|
|
43
44
|
// scenario.blocks が存在しない場合は空の配列を使用
|
|
44
45
|
const scenario = useMemo(() => {
|
|
45
46
|
var _a;
|
|
46
47
|
return (Object.assign(Object.assign({}, scenarioProp), { blocks: (_a = scenarioProp.blocks) !== null && _a !== void 0 ? _a : [] }));
|
|
47
48
|
}, [scenarioProp]);
|
|
48
49
|
// デフォルト値とマージ
|
|
49
|
-
const mergedSettings = Object.assign({ textSpeed: 80, autoPlaySpeed: 3, bgmVolume: 0.5, seVolume: 1.0, voiceVolume: 1.0, effectVolume: 1.0, textSoundVolume: 1.0, defaultBackgroundFadeDuration: 0 }, settings);
|
|
50
|
+
const mergedSettings = Object.assign({ textSpeed: 80, autoPlaySpeed: 3, bgmVolume: 0.5, seVolume: 1.0, voiceVolume: 1.0, effectVolume: 1.0, textSoundVolume: 1.0, defaultBackgroundFadeDuration: 0, inactiveCharacterBrightness: 0.8 }, settings);
|
|
50
51
|
// プラグインからの設定更新ハンドラ
|
|
51
52
|
const handleSettingsUpdate = useCallback((updatedSettings) => {
|
|
52
53
|
var _a, _b;
|
|
53
54
|
const newSettings = Object.assign(Object.assign({ aspectRatio: (_a = settings === null || settings === void 0 ? void 0 : settings.aspectRatio) !== null && _a !== void 0 ? _a : "16:9", bgObjectFit: (_b = settings === null || settings === void 0 ? void 0 : settings.bgObjectFit) !== null && _b !== void 0 ? _b : "cover" }, settings), updatedSettings);
|
|
54
55
|
onSettingsChange === null || onSettingsChange === void 0 ? void 0 : onSettingsChange(newSettings);
|
|
55
56
|
}, [settings, onSettingsChange]);
|
|
57
|
+
// 感情エフェクト状態管理
|
|
58
|
+
const [emotionEffectState, setEmotionEffectState] = useState(null);
|
|
59
|
+
// プラグインからの感情エフェクト更新ハンドラ
|
|
60
|
+
const handleEmotionEffectUpdate = useCallback((state) => {
|
|
61
|
+
setEmotionEffectState(state);
|
|
62
|
+
}, []);
|
|
56
63
|
// 遅延初期化でPluginManagerを作成(毎レンダリングでnew PluginManager()が評価されるのを防ぐ)
|
|
57
64
|
const pluginManagerRef = useRef(undefined);
|
|
58
65
|
if (!pluginManagerRef.current) {
|
|
@@ -66,6 +73,16 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = [], sounds
|
|
|
66
73
|
const uiAPI = pluginManager.getUIAPI();
|
|
67
74
|
setGlobalUIAPI(uiAPI, pluginManager);
|
|
68
75
|
}, [pluginManager]);
|
|
76
|
+
// 感情エフェクト更新コールバックを登録
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
pluginManager.setEmotionEffectUpdaterCallback(handleEmotionEffectUpdate);
|
|
79
|
+
}, [pluginManager, handleEmotionEffectUpdate]);
|
|
80
|
+
// DataContextへの参照を保持(プラグインがdata.get()を呼び出せるようにする)
|
|
81
|
+
const dataContextRef = useRef(null);
|
|
82
|
+
// DataContext getterを登録
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
pluginManager.setDataContextGetter(() => dataContextRef.current);
|
|
85
|
+
}, [pluginManager]);
|
|
69
86
|
// プラグインのミュート状態を設定
|
|
70
87
|
useEffect(() => {
|
|
71
88
|
var _a;
|
|
@@ -73,6 +90,7 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = [], sounds
|
|
|
73
90
|
}, [pluginManager, mergedSettings.muteAudio]);
|
|
74
91
|
// 画面サイズの初期化
|
|
75
92
|
const [, setScreenSize] = useScreenSizeAtom();
|
|
93
|
+
const aspectRatioContainerRef = useRef(null);
|
|
76
94
|
useEffect(() => {
|
|
77
95
|
// screenSizeが明示的に指定されている場合はそれを使用(プレビュー用)
|
|
78
96
|
if (screenSizeProp) {
|
|
@@ -82,16 +100,40 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = [], sounds
|
|
|
82
100
|
// クライアントサイドでのみ実行
|
|
83
101
|
if (typeof window === "undefined")
|
|
84
102
|
return;
|
|
103
|
+
// 初期値として一時的にウィンドウサイズを設定
|
|
85
104
|
setScreenSize({ width: window.innerWidth, height: window.innerHeight });
|
|
86
|
-
|
|
87
|
-
|
|
105
|
+
const container = aspectRatioContainerRef.current;
|
|
106
|
+
if (!container)
|
|
107
|
+
return;
|
|
108
|
+
// アスペクト比コンテナのサイズを監視
|
|
109
|
+
const updateScreenSize = () => {
|
|
110
|
+
const rect = container.getBoundingClientRect();
|
|
111
|
+
// サイズが0の場合はスキップ(まだレンダリングされていない)
|
|
112
|
+
if (rect.width === 0 || rect.height === 0)
|
|
113
|
+
return;
|
|
114
|
+
console.log("[Player] screenSize更新:", {
|
|
115
|
+
width: rect.width,
|
|
116
|
+
height: rect.height,
|
|
117
|
+
windowWidth: window.innerWidth,
|
|
118
|
+
windowHeight: window.innerHeight,
|
|
119
|
+
});
|
|
88
120
|
setScreenSize({
|
|
89
|
-
width:
|
|
90
|
-
height:
|
|
121
|
+
width: rect.width,
|
|
122
|
+
height: rect.height,
|
|
91
123
|
});
|
|
92
124
|
};
|
|
93
|
-
|
|
94
|
-
|
|
125
|
+
// 初期サイズを設定(次のフレームで実行してレンダリング完了を待つ)
|
|
126
|
+
requestAnimationFrame(() => {
|
|
127
|
+
updateScreenSize();
|
|
128
|
+
});
|
|
129
|
+
// ResizeObserverでコンテナのサイズ変更を監視
|
|
130
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
131
|
+
updateScreenSize();
|
|
132
|
+
});
|
|
133
|
+
resizeObserver.observe(container);
|
|
134
|
+
return () => {
|
|
135
|
+
resizeObserver.disconnect();
|
|
136
|
+
};
|
|
95
137
|
}, [setScreenSize, screenSizeProp]);
|
|
96
138
|
// 表示可能なブロックのインデックスを事前計算
|
|
97
139
|
const displayableBlockIndices = useMemo(() => {
|
|
@@ -215,21 +257,34 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = [], sounds
|
|
|
215
257
|
getMediaType,
|
|
216
258
|
mergedSettings.defaultBackgroundFadeDuration,
|
|
217
259
|
]);
|
|
218
|
-
const [state, setState] = useState({
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
260
|
+
const [state, setState] = useState(() => {
|
|
261
|
+
var _a;
|
|
262
|
+
return {
|
|
263
|
+
currentBlockIndex: 0,
|
|
264
|
+
isPlaying: autoplay,
|
|
265
|
+
isEnded: false,
|
|
266
|
+
variables: (_a = variableManagerRef.current) === null || _a === void 0 ? void 0 : _a.getVariablesMap(),
|
|
267
|
+
};
|
|
223
268
|
});
|
|
224
269
|
const [currentBranchBlock, setCurrentBranchBlock] = useState(null);
|
|
225
270
|
// 遷移の種類を追跡(クリック、自動、時間待ちなど)
|
|
226
271
|
const [transitionSource, setTransitionSource] = useState("click");
|
|
272
|
+
// シナリオ開始コールバック(後でcurrentBlockが定義された後に実行)
|
|
273
|
+
const [hasStarted, setHasStarted] = useState(false);
|
|
227
274
|
// シナリオIDを追跡して、変更時に状態をリセット(プレビュー用)
|
|
228
275
|
const previousScenarioIdRef = useRef(scenario.id);
|
|
229
276
|
// シナリオが変更されたときに状態をリセット(keyを変更せずに対応)
|
|
230
277
|
useEffect(() => {
|
|
231
278
|
var _a;
|
|
232
279
|
if (previousScenarioIdRef.current !== scenario.id) {
|
|
280
|
+
const previousId = previousScenarioIdRef.current;
|
|
281
|
+
// 前のシナリオが開始されていた場合、onScenarioEndを呼び出す
|
|
282
|
+
if (hasStarted) {
|
|
283
|
+
pluginManager.callHook("onScenarioEnd", {
|
|
284
|
+
scenarioId: previousId,
|
|
285
|
+
scenarioName: "scenario",
|
|
286
|
+
});
|
|
287
|
+
}
|
|
233
288
|
previousScenarioIdRef.current = scenario.id;
|
|
234
289
|
// 状態をリセット
|
|
235
290
|
setState({
|
|
@@ -239,12 +294,33 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = [], sounds
|
|
|
239
294
|
variables: (_a = variableManagerRef.current) === null || _a === void 0 ? void 0 : _a.getVariablesMap(),
|
|
240
295
|
});
|
|
241
296
|
setCurrentBranchBlock(null);
|
|
297
|
+
setHasStarted(false); // これにより次の useEffect で onScenarioStart が呼ばれる
|
|
242
298
|
}
|
|
243
|
-
}, [scenario.id, autoplay]);
|
|
299
|
+
}, [scenario.id, autoplay, hasStarted, pluginManager]);
|
|
244
300
|
// 画像を事前読み込み
|
|
245
301
|
const imagesLoaded = usePreloadImages(scenario);
|
|
246
|
-
//
|
|
247
|
-
|
|
302
|
+
// シナリオ内の全テキストを抽出(フォントのプリロード用)
|
|
303
|
+
// サブセットフォントの分割ファイルを事前に読み込むことで、
|
|
304
|
+
// 文字送り中のフォント切り替わりを防止
|
|
305
|
+
const allScenarioText = useMemo(() => {
|
|
306
|
+
var _a;
|
|
307
|
+
const texts = [];
|
|
308
|
+
for (const block of scenario.blocks) {
|
|
309
|
+
// dialogue, narration, fullscreen_textのcontent
|
|
310
|
+
if (block.content) {
|
|
311
|
+
texts.push(block.content);
|
|
312
|
+
}
|
|
313
|
+
// speaker name
|
|
314
|
+
if ((_a = block.speaker) === null || _a === void 0 ? void 0 : _a.name) {
|
|
315
|
+
texts.push(block.speaker.name);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
// 重複を除去してユニークな文字のみを残す
|
|
319
|
+
const uniqueChars = [...new Set(texts.join(""))].join("");
|
|
320
|
+
return uniqueChars;
|
|
321
|
+
}, [scenario.blocks]);
|
|
322
|
+
// フォントを読み込み(シナリオ内の全テキストでプリロード)
|
|
323
|
+
const { isLoaded: fontsLoaded } = useFontLoader(scenario.fonts, allScenarioText);
|
|
248
324
|
// プラグインの読み込み状態
|
|
249
325
|
const [pluginsLoaded, setPluginsLoaded] = useState(false);
|
|
250
326
|
// 読み込み済みプラグインのパッケージ名を追跡
|
|
@@ -266,7 +342,7 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = [], sounds
|
|
|
266
342
|
for (const plugin of newPlugins) {
|
|
267
343
|
if (isCancelled)
|
|
268
344
|
return;
|
|
269
|
-
yield pluginManager.loadPlugin(plugin.packageName, plugin.bundleUrl, plugin.config);
|
|
345
|
+
yield pluginManager.loadPlugin(plugin.packageName, plugin.bundleUrl, plugin.config, plugin.assets);
|
|
270
346
|
loadedPluginNamesRef.current.add(plugin.packageName);
|
|
271
347
|
}
|
|
272
348
|
if (isCancelled)
|
|
@@ -287,8 +363,6 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = [], sounds
|
|
|
287
363
|
}, [plugins, sounds]);
|
|
288
364
|
// 初回レンダリング完了フラグ
|
|
289
365
|
const [isFirstRenderComplete, setIsFirstRenderComplete] = useState(false);
|
|
290
|
-
// シナリオ開始コールバック(後でcurrentBlockが定義された後に実行)
|
|
291
|
-
const [hasStarted, setHasStarted] = useState(false);
|
|
292
366
|
// スタイル要素の登録
|
|
293
367
|
useEffect(() => {
|
|
294
368
|
if (isFirstRenderComplete) {
|
|
@@ -347,7 +421,7 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = [], sounds
|
|
|
347
421
|
return prevIdx !== undefined ? scenario.blocks[prevIdx] : null;
|
|
348
422
|
}, [scenario.blocks, displayableBlockIndices, state.currentBlockIndex]);
|
|
349
423
|
// usePlayerLogicに渡す実際のブロックインデックス
|
|
350
|
-
const actualBlockIndex = (
|
|
424
|
+
const actualBlockIndex = (_a = displayableBlockIndices[state.currentBlockIndex]) !== null && _a !== void 0 ? _a : 0;
|
|
351
425
|
// バックログ機能
|
|
352
426
|
const backlog = useBacklog({
|
|
353
427
|
scenario,
|
|
@@ -514,21 +588,40 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = [], sounds
|
|
|
514
588
|
});
|
|
515
589
|
}
|
|
516
590
|
}, [imagesLoaded, fontsLoaded, currentBlock, isFirstRenderComplete]);
|
|
517
|
-
// シナリオ開始コールバック(currentBlock
|
|
591
|
+
// シナリオ開始コールバック(currentBlock定義後、プラグインロード完了後)
|
|
518
592
|
useEffect(() => {
|
|
519
|
-
if (isFirstRenderComplete && !hasStarted && currentBlock) {
|
|
593
|
+
if (isFirstRenderComplete && !hasStarted && currentBlock && pluginsLoaded) {
|
|
520
594
|
setHasStarted(true);
|
|
521
595
|
onScenarioStart === null || onScenarioStart === void 0 ? void 0 : onScenarioStart();
|
|
596
|
+
// プラグインのonScenarioStartフックを呼び出し
|
|
597
|
+
pluginManager.callHook("onScenarioStart", {
|
|
598
|
+
scenarioId: scenario.id,
|
|
599
|
+
scenarioName: scenario.name || "scenario",
|
|
600
|
+
});
|
|
522
601
|
}
|
|
523
|
-
}, [
|
|
602
|
+
}, [
|
|
603
|
+
isFirstRenderComplete,
|
|
604
|
+
hasStarted,
|
|
605
|
+
currentBlock,
|
|
606
|
+
pluginsLoaded,
|
|
607
|
+
onScenarioStart,
|
|
608
|
+
pluginManager,
|
|
609
|
+
scenario.id,
|
|
610
|
+
scenario.name,
|
|
611
|
+
]);
|
|
524
612
|
// restartをusePlayerLogicの前に定義(分岐状態もリセット + キャンセルコールバック)
|
|
525
613
|
const restart = useCallback(() => {
|
|
526
614
|
// リスタート時はシナリオがキャンセルされたとみなす
|
|
527
615
|
if (hasStarted && !state.isEnded) {
|
|
528
616
|
onScenarioCancelled === null || onScenarioCancelled === void 0 ? void 0 : onScenarioCancelled();
|
|
617
|
+
// プラグインのonScenarioEndフックを呼び出し(シナリオ中断時)
|
|
618
|
+
pluginManager.callHook("onScenarioEnd", {
|
|
619
|
+
scenarioId: scenario.id,
|
|
620
|
+
scenarioName: scenario.name || "scenario",
|
|
621
|
+
});
|
|
529
622
|
}
|
|
530
623
|
setIsFirstRenderComplete(false);
|
|
531
|
-
setHasStarted(false);
|
|
624
|
+
setHasStarted(false); // これにより次の useEffect で onScenarioStart が呼ばれる
|
|
532
625
|
setCurrentBranchBlock(null);
|
|
533
626
|
branchNavigatorRef.current.reset();
|
|
534
627
|
resetAccumulated(); // 蓄積テキストをクリア
|
|
@@ -543,6 +636,9 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = [], sounds
|
|
|
543
636
|
state.isEnded,
|
|
544
637
|
onScenarioCancelled,
|
|
545
638
|
resetAccumulated,
|
|
639
|
+
pluginManager,
|
|
640
|
+
scenario.id,
|
|
641
|
+
scenario.name,
|
|
546
642
|
]);
|
|
547
643
|
const { handleNext: handleNextInternal, handlePrevious: handlePreviousInternal, togglePlay: _togglePlay, restart: _restartInternal, displayedCharacters, } = usePlayerLogic({
|
|
548
644
|
state: Object.assign(Object.assign({}, state), { currentBlockIndex: actualBlockIndex }),
|
|
@@ -591,6 +687,29 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = [], sounds
|
|
|
591
687
|
pluginsLoaded,
|
|
592
688
|
transitionSource,
|
|
593
689
|
});
|
|
690
|
+
// ActionNode実行処理
|
|
691
|
+
useEffect(() => {
|
|
692
|
+
var _a;
|
|
693
|
+
if (currentBlock && currentBlock.blockType === "action_node") {
|
|
694
|
+
const attrs = {};
|
|
695
|
+
// attributeValuesから属性値を収集
|
|
696
|
+
if (currentBlock.attributeValues) {
|
|
697
|
+
for (const attrValue of currentBlock.attributeValues) {
|
|
698
|
+
attrs[attrValue.attribute.name] = attrValue.value;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
// ActionNode実行(actionNode.typeがundefinedでないことを確認)
|
|
702
|
+
if ((_a = currentBlock.actionNode) === null || _a === void 0 ? void 0 : _a.type) {
|
|
703
|
+
pluginManager.executeActionNode(currentBlock.actionNode.type, {
|
|
704
|
+
attributes: attrs,
|
|
705
|
+
currentBlock,
|
|
706
|
+
currentSpeaker: undefined, // TODO: 現在の発話キャラクターを取得
|
|
707
|
+
displayedCharacters: displayedCharacters,
|
|
708
|
+
api: pluginManager,
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}, [currentBlock, displayedCharacters, pluginManager]);
|
|
594
713
|
// 初期履歴構築(シナリオ開始時)
|
|
595
714
|
useEffect(() => {
|
|
596
715
|
if (isFirstRenderComplete && actualBlockIndex >= 0) {
|
|
@@ -627,6 +746,11 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = [], sounds
|
|
|
627
746
|
}
|
|
628
747
|
else {
|
|
629
748
|
setState((prev) => (Object.assign(Object.assign({}, prev), { isEnded: true, isPlaying: false })));
|
|
749
|
+
// プラグインのonScenarioEndフックを呼び出し
|
|
750
|
+
pluginManager.callHook("onScenarioEnd", {
|
|
751
|
+
scenarioId: scenario.id,
|
|
752
|
+
scenarioName: scenario.name || "scenario",
|
|
753
|
+
});
|
|
630
754
|
onEnd === null || onEnd === void 0 ? void 0 : onEnd();
|
|
631
755
|
onScenarioEnd === null || onScenarioEnd === void 0 ? void 0 : onScenarioEnd();
|
|
632
756
|
}
|
|
@@ -637,7 +761,10 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = [], sounds
|
|
|
637
761
|
displayableBlockIndices.length,
|
|
638
762
|
handleNextInternal,
|
|
639
763
|
onEnd,
|
|
640
|
-
onScenarioEnd,
|
|
764
|
+
onScenarioEnd, // プラグインのonScenarioEndフックを呼び出し
|
|
765
|
+
pluginManager,
|
|
766
|
+
scenario.id,
|
|
767
|
+
scenario.name,
|
|
641
768
|
]);
|
|
642
769
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
643
770
|
const _handlePrevious = useCallback(() => {
|
|
@@ -647,7 +774,16 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = [], sounds
|
|
|
647
774
|
}, [state.currentBlockIndex, handlePreviousInternal]);
|
|
648
775
|
// 現在の背景を計算
|
|
649
776
|
const currentBackground = useMemo(() => calculateCurrentBackground(actualBlockIndex), [calculateCurrentBackground, actualBlockIndex]);
|
|
650
|
-
//
|
|
777
|
+
// displayText を JSX に変換(PlaybackTextProvider 用)
|
|
778
|
+
const displayTextElement = useMemo(() => {
|
|
779
|
+
if (displayText.includes("\n")) {
|
|
780
|
+
return displayText.split("\n").map((line, index, array) => (_jsxs(React.Fragment, { children: [line, index < array.length - 1 && _jsx("br", {})] }, index)));
|
|
781
|
+
}
|
|
782
|
+
return displayText;
|
|
783
|
+
}, [displayText]);
|
|
784
|
+
// DataContext の構築
|
|
785
|
+
// DataRefContext で安定した参照を提供することで、
|
|
786
|
+
// useDataAPI() を使うコンポーネントの再レンダリングを防ぐ
|
|
651
787
|
const dataContext = useMemo(() => {
|
|
652
788
|
var _a, _b, _c;
|
|
653
789
|
return ({
|
|
@@ -657,9 +793,7 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = [], sounds
|
|
|
657
793
|
scenarioId: scenario.id,
|
|
658
794
|
scenarioName: scenario.name,
|
|
659
795
|
currentBlock: currentBlock || null,
|
|
660
|
-
displayText:
|
|
661
|
-
? displayText.split("\n").map((line, index, array) => (_jsxs(React.Fragment, { children: [line, index < array.length - 1 && _jsx("br", {})] }, index)))
|
|
662
|
-
: displayText,
|
|
796
|
+
displayText: displayTextElement,
|
|
663
797
|
isTyping,
|
|
664
798
|
displayedCharacters,
|
|
665
799
|
},
|
|
@@ -678,6 +812,8 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = [], sounds
|
|
|
678
812
|
seVolume: mergedSettings.seVolume,
|
|
679
813
|
voiceVolume: mergedSettings.voiceVolume,
|
|
680
814
|
skipMode: "unread",
|
|
815
|
+
selectedFontFamily: mergedSettings.selectedFontFamily,
|
|
816
|
+
selectedUIFontFamily: mergedSettings.selectedUIFontFamily,
|
|
681
817
|
},
|
|
682
818
|
pluginAssets: {
|
|
683
819
|
getAssetUrl: (pluginName, filename) => {
|
|
@@ -691,30 +827,38 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = [], sounds
|
|
|
691
827
|
fonts: {
|
|
692
828
|
fonts: (_c = scenario.fonts) !== null && _c !== void 0 ? _c : [],
|
|
693
829
|
selectedFontFamily: mergedSettings.selectedFontFamily,
|
|
830
|
+
selectedUIFontFamily: mergedSettings.selectedUIFontFamily,
|
|
694
831
|
isLoaded: fontsLoaded,
|
|
695
832
|
},
|
|
833
|
+
emotionEffect: emotionEffectState,
|
|
696
834
|
});
|
|
697
835
|
}, [
|
|
698
836
|
pluginManager,
|
|
699
837
|
actualBlockIndex,
|
|
700
838
|
scenario,
|
|
701
839
|
currentBlock,
|
|
702
|
-
|
|
840
|
+
displayTextElement,
|
|
703
841
|
isTyping,
|
|
704
842
|
displayedCharacters,
|
|
705
843
|
backlog,
|
|
706
844
|
conversationBranch.branchState,
|
|
707
845
|
mergedSettings.aspectRatio,
|
|
708
846
|
mergedSettings.autoPlaySpeed,
|
|
847
|
+
emotionEffectState,
|
|
709
848
|
mergedSettings.bgObjectFit,
|
|
710
849
|
mergedSettings.bgmVolume,
|
|
711
850
|
mergedSettings.seVolume,
|
|
712
851
|
mergedSettings.textSpeed,
|
|
713
852
|
mergedSettings.voiceVolume,
|
|
714
853
|
mergedSettings.selectedFontFamily,
|
|
854
|
+
mergedSettings.selectedUIFontFamily,
|
|
715
855
|
currentBackground,
|
|
716
856
|
fontsLoaded,
|
|
717
857
|
]);
|
|
858
|
+
// DataContextの参照を更新(プラグインがapi.data.get()を呼び出せるようにする)
|
|
859
|
+
useEffect(() => {
|
|
860
|
+
dataContextRef.current = dataContext;
|
|
861
|
+
}, [dataContext]);
|
|
718
862
|
// マウスホイールとタッチジェスチャーを無効化(preventDefaultScrollがtrueの場合のみ)
|
|
719
863
|
useEffect(() => {
|
|
720
864
|
if (!preventDefaultScroll)
|
|
@@ -747,6 +891,12 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = [], sounds
|
|
|
747
891
|
const [width, height] = settings.aspectRatio.split(":").map(Number);
|
|
748
892
|
return `${width}/${height}`;
|
|
749
893
|
};
|
|
894
|
+
const getAspectRatioValue = () => {
|
|
895
|
+
if (!(settings === null || settings === void 0 ? void 0 : settings.aspectRatio))
|
|
896
|
+
return 16 / 9;
|
|
897
|
+
const [width, height] = settings.aspectRatio.split(":").map(Number);
|
|
898
|
+
return width / height;
|
|
899
|
+
};
|
|
750
900
|
// 条件付きレンダリングを JSX で処理(フックの後、early return なし)
|
|
751
901
|
return (_jsxs(_Fragment, { children: [!currentBlock && !state.isEnded && (_jsx("div", { className: clsx("flex items-center justify-center p-8", className), children: _jsx("p", { className: "text-gray-500", children: "\u30B7\u30CA\u30EA\u30AA\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093" }) })), state.isEnded && (_jsx(EndScreen, { scenarioName: scenario.name, onRestart: restart, className: className })), currentBlock && !state.isEnded && (_jsxs(_Fragment, { children: [(!imagesLoaded ||
|
|
752
902
|
!fontsLoaded ||
|
|
@@ -763,16 +913,20 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = [], sounds
|
|
|
763
913
|
touchAction: "none",
|
|
764
914
|
userSelect: "none",
|
|
765
915
|
WebkitUserSelect: "none",
|
|
766
|
-
}, children: _jsx("div", { className: "relative bg-white flex flex-col
|
|
916
|
+
}, children: _jsx("div", { ref: aspectRatioContainerRef, className: "relative bg-white flex flex-col overflow-hidden", style: {
|
|
917
|
+
aspectRatio: getAspectRatio(),
|
|
918
|
+
width: `min(100vw, calc(100vh * ${getAspectRatioValue()}))`,
|
|
919
|
+
height: `min(100vh, calc(100vw / ${getAspectRatioValue()}))`,
|
|
920
|
+
}, children: _jsx(DataProvider, { data: dataContext, onSettingsUpdate: handleSettingsUpdate, onEmotionEffectUpdate: handleEmotionEffectUpdate, children: _jsx(AudioProvider, { settings: {
|
|
767
921
|
bgmVolume: mergedSettings.bgmVolume,
|
|
768
922
|
seVolume: mergedSettings.seVolume,
|
|
769
923
|
voiceVolume: mergedSettings.voiceVolume,
|
|
770
924
|
effectVolume: mergedSettings.effectVolume,
|
|
771
925
|
textSoundVolume: mergedSettings.textSoundVolume,
|
|
772
|
-
muteAudio: (
|
|
773
|
-
}, children: [_jsxs("div", { className: "h-full relative", children: [_jsx(PluginComponentProvider, { type: ComponentType.Background, pluginManager: pluginManager, fallback: BackgroundLayer }, "background"), _jsx(GameScreen, { scenario: scenario, currentBlock: currentBlock, previousBlock: previousBlock, displayedCharacters: displayedCharacters }, "game-screen")] }), _jsx(OverlayUI, { children: _jsxs("div", { className: "h-full w-full relative", children: [_jsx(PluginComponentProvider, { type: ComponentType.DialogueBox, pluginManager: pluginManager, fallback: DialogueBox }, "dialogue-box"), (currentBlock === null || currentBlock === void 0 ? void 0 : currentBlock.blockType) === "conversation_branch" && (_jsx(PluginComponentProvider, { type: ComponentType.ConversationBranch, pluginManager: pluginManager, fallback: ConversationBranchBox }, "conversation-branch")), (currentBlock === null || currentBlock === void 0 ? void 0 : currentBlock.blockType) === "fullscreen_text" && (_jsx(FullscreenTextBox, { onComplete: () => handleNext("click") }, "fullscreen-text")), (currentBlock === null || currentBlock === void 0 ? void 0 : currentBlock.blockType) === "click_wait" && (_jsx(ClickWaitIndicator, {}, "click-wait")), (currentBlock === null || currentBlock === void 0 ? void 0 : currentBlock.blockType) === "time_wait" && (_jsx(TimeWaitIndicator, { duration: (
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
926
|
+
muteAudio: (_b = mergedSettings.muteAudio) !== null && _b !== void 0 ? _b : false,
|
|
927
|
+
}, children: _jsxs(PlaybackTextProvider, { displayText: displayTextElement, isTyping: isTyping, children: [_jsxs("div", { className: "h-full relative", children: [_jsx(PluginComponentProvider, { type: ComponentType.Background, pluginManager: pluginManager, fallback: BackgroundLayer }, "background"), _jsx(GameScreen, { scenario: scenario, currentBlock: currentBlock, previousBlock: previousBlock, displayedCharacters: displayedCharacters }, "game-screen")] }), _jsx(OverlayUI, { children: _jsxs("div", { className: "h-full w-full relative", children: [_jsx(PluginComponentProvider, { type: ComponentType.DialogueBox, pluginManager: pluginManager, fallback: DialogueBox }, "dialogue-box"), (currentBlock === null || currentBlock === void 0 ? void 0 : currentBlock.blockType) === "conversation_branch" && (_jsx(PluginComponentProvider, { type: ComponentType.ConversationBranch, pluginManager: pluginManager, fallback: ConversationBranchBox }, "conversation-branch")), (currentBlock === null || currentBlock === void 0 ? void 0 : currentBlock.blockType) === "fullscreen_text" && (_jsx(FullscreenTextBox, { onComplete: () => handleNext("click") }, "fullscreen-text")), (currentBlock === null || currentBlock === void 0 ? void 0 : currentBlock.blockType) === "click_wait" && (_jsx(ClickWaitIndicator, {}, "click-wait")), (currentBlock === null || currentBlock === void 0 ? void 0 : currentBlock.blockType) === "time_wait" && (_jsx(TimeWaitIndicator, { duration: (_d = (_c = currentBlock.options) === null || _c === void 0 ? void 0 : _c.duration) !== null && _d !== void 0 ? _d : 1, onComplete: () => handleNext("time_wait") }, "time-wait")), pluginManager
|
|
928
|
+
.getRegisteredComponents()
|
|
929
|
+
.filter((type) => type !== ComponentType.DialogueBox &&
|
|
930
|
+
type !== ComponentType.ConversationBranch)
|
|
931
|
+
.map((componentType) => (_jsx(PluginComponentProvider, { type: componentType, pluginManager: pluginManager }, componentType)))] }) })] }) }) }) }) })] }))] }));
|
|
778
932
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { memo, useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { useDataAPI } from "../contexts/DataContext";
|
|
4
4
|
/**
|
|
5
5
|
* iOS/macOSかどうかを判定
|
|
@@ -56,8 +56,10 @@ function getMediaType(url) {
|
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
58
|
* 背景メディアコンポーネント
|
|
59
|
+
* React.memo でラップして、props が変わらない限り再レンダリングを防ぐ
|
|
60
|
+
* これにより displayText の更新による不要な再描画を防止
|
|
59
61
|
*/
|
|
60
|
-
const BackgroundMedia = ({ background, opacity, zIndex, useAppleFormat })
|
|
62
|
+
const BackgroundMedia = memo(function BackgroundMedia({ background, opacity, zIndex, useAppleFormat }) {
|
|
61
63
|
var _a, _b;
|
|
62
64
|
// OS判定に基づいて使用するURLを決定
|
|
63
65
|
// iOS/macOS: mp4 (imageUrl)
|
|
@@ -84,7 +86,7 @@ const BackgroundMedia = ({ background, opacity, zIndex, useAppleFormat }) => {
|
|
|
84
86
|
transition: "none", // requestAnimationFrameでアニメーション
|
|
85
87
|
pointerEvents: isHighLayer ? "none" : undefined,
|
|
86
88
|
}, "data-background-object-id": background.objectId, "data-background-state-id": background.stateId, "data-background-layer": layer, children: mediaType === "video" ? (_jsx("video", { src: videoUrl, className: `w-full h-full ${objectFitClass}`, autoPlay: true, muted: true, playsInline: true, loop: background.loop }, videoUrl)) : (_jsx("img", { src: background.imageUrl, alt: background.stateName, className: `w-full h-full ${objectFitClass}` }, background.imageUrl)) }));
|
|
87
|
-
};
|
|
89
|
+
});
|
|
88
90
|
/**
|
|
89
91
|
* 背景の配列が同じかどうかを比較
|
|
90
92
|
*/
|
|
@@ -107,7 +109,7 @@ function areBackgroundsEqual(a, b) {
|
|
|
107
109
|
* プラグインによって置き換え可能
|
|
108
110
|
* フェードイン・フェードアウト対応
|
|
109
111
|
*/
|
|
110
|
-
export const BackgroundLayer = ({ className
|
|
112
|
+
export const BackgroundLayer = memo(({ className }) => {
|
|
111
113
|
const dataAPI = useDataAPI();
|
|
112
114
|
// Apple環境かどうかを判定(iOS/macOSではmp4を使用)
|
|
113
115
|
const useAppleFormat = useMemo(() => isApplePlatform(), []);
|
|
@@ -214,5 +216,5 @@ export const BackgroundLayer = ({ className, }) => {
|
|
|
214
216
|
const zIndex = getLayerZIndex(layer, index);
|
|
215
217
|
return (_jsx(BackgroundMedia, { background: bg, opacity: fadeState.isFading ? fadeState.fadeProgress : 1, zIndex: zIndex, useAppleFormat: useAppleFormat }, `curr-${bg.objectId}-${bg.stateId}-${index}`));
|
|
216
218
|
})] }));
|
|
217
|
-
};
|
|
219
|
+
});
|
|
218
220
|
export { getMediaType };
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import type React from "react";
|
|
2
1
|
import type { DisplayedCharacter, PublishedScenario, ScenarioBlock } from "../types";
|
|
3
2
|
interface GameScreenProps {
|
|
4
3
|
scenario: PublishedScenario;
|
|
5
4
|
currentBlock: ScenarioBlock;
|
|
6
5
|
previousBlock?: ScenarioBlock | null;
|
|
7
6
|
displayedCharacters: DisplayedCharacter[];
|
|
7
|
+
inactiveCharacterBrightness?: number;
|
|
8
|
+
characterSpacing?: number;
|
|
8
9
|
}
|
|
9
|
-
|
|
10
|
+
/**
|
|
11
|
+
* ゲームスクリーンコンポーネント
|
|
12
|
+
* React.memo でラップして、props が変わらない限り再レンダリングを防ぐ
|
|
13
|
+
* これにより displayText の更新による不要な再描画を防止
|
|
14
|
+
*/
|
|
15
|
+
export declare const GameScreen: import("react").NamedExoticComponent<GameScreenProps>;
|
|
10
16
|
export {};
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
|
-
|
|
2
|
+
import { memo, useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* ゲームスクリーンコンポーネント
|
|
5
|
+
* React.memo でラップして、props が変わらない限り再レンダリングを防ぐ
|
|
6
|
+
* これにより displayText の更新による不要な再描画を防止
|
|
7
|
+
*/
|
|
8
|
+
export const GameScreen = memo(function GameScreen({ scenario, currentBlock, previousBlock, displayedCharacters, inactiveCharacterBrightness = 0.8, characterSpacing = 0.1, }) {
|
|
4
9
|
var _a;
|
|
5
10
|
// キャラクターごとのフェード状態を管理
|
|
6
11
|
const [fadeStates, setFadeStates] = useState(new Map());
|
|
@@ -222,6 +227,28 @@ export const GameScreen = ({ scenario, currentBlock, previousBlock, displayedCha
|
|
|
222
227
|
const currentSpeaker = (currentBlock === null || currentBlock === void 0 ? void 0 : currentBlock.speakerId)
|
|
223
228
|
? displayedCharacters.find((char) => char.objectId === currentBlock.speakerId)
|
|
224
229
|
: null;
|
|
230
|
+
// 簡易モード用の自動配置計算
|
|
231
|
+
const calculateAutoLayout = (characters, spacing) => {
|
|
232
|
+
const total = characters.length;
|
|
233
|
+
const positions = new Map();
|
|
234
|
+
if (total === 0)
|
|
235
|
+
return positions;
|
|
236
|
+
if (total === 1) {
|
|
237
|
+
// 1人の場合は中央
|
|
238
|
+
positions.set(characters[0].objectId, { x: 0, y: 0 });
|
|
239
|
+
return positions;
|
|
240
|
+
}
|
|
241
|
+
// 複数の場合は均等配置
|
|
242
|
+
// spacing: 画面幅に対する割合(例: 0.1 = 10%)
|
|
243
|
+
// 座標系: -1(左端)〜 1(右端)
|
|
244
|
+
const totalWidth = (total - 1) * spacing * 2;
|
|
245
|
+
const startX = -totalWidth / 2;
|
|
246
|
+
characters.forEach((char, index) => {
|
|
247
|
+
const x = startX + index * spacing * 2;
|
|
248
|
+
positions.set(char.objectId, { x, y: 0 });
|
|
249
|
+
});
|
|
250
|
+
return positions;
|
|
251
|
+
};
|
|
225
252
|
// キャラクター描画用のヘルパー関数
|
|
226
253
|
const renderCharacter = (image, displayedChar, isCurrentSpeaker, keyPrefix) => {
|
|
227
254
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
@@ -257,7 +284,7 @@ export const GameScreen = ({ scenario, currentBlock, previousBlock, displayedCha
|
|
|
257
284
|
// 複数キャラクター表示で、現在の話者でない場合
|
|
258
285
|
if (currentSpeaker &&
|
|
259
286
|
currentSpeaker.objectId !== displayedChar.objectId) {
|
|
260
|
-
brightness =
|
|
287
|
+
brightness = inactiveCharacterBrightness;
|
|
261
288
|
}
|
|
262
289
|
}
|
|
263
290
|
// z-indexを決定
|
|
@@ -270,9 +297,9 @@ export const GameScreen = ({ scenario, currentBlock, previousBlock, displayedCha
|
|
|
270
297
|
: ((_d = image.scale) !== null && _d !== void 0 ? _d : 1);
|
|
271
298
|
// 位置を決定(カスタム位置またはデフォルト位置)
|
|
272
299
|
// 新座標系: x: -1=左見切れ, 0=中央, 1=右見切れ / y: -1=上見切れ, 0=中央, 1=下見切れ
|
|
273
|
-
let finalPosition = { x: 0, y:
|
|
300
|
+
let finalPosition = { x: 0, y: 0 };
|
|
274
301
|
if (displayedChar) {
|
|
275
|
-
//
|
|
302
|
+
// カスタム位置が設定されている場合(詳細モード)
|
|
276
303
|
if (displayedChar.positionX !== null &&
|
|
277
304
|
displayedChar.positionY !== null) {
|
|
278
305
|
finalPosition = {
|
|
@@ -280,7 +307,14 @@ export const GameScreen = ({ scenario, currentBlock, previousBlock, displayedCha
|
|
|
280
307
|
y: (_f = displayedChar.positionY) !== null && _f !== void 0 ? _f : 0,
|
|
281
308
|
};
|
|
282
309
|
}
|
|
283
|
-
|
|
310
|
+
else {
|
|
311
|
+
// 簡易モード: 自動配置
|
|
312
|
+
const autoPositions = calculateAutoLayout(displayedCharacters, characterSpacing !== null && characterSpacing !== void 0 ? characterSpacing : 0.25);
|
|
313
|
+
const autoPos = autoPositions.get(displayedChar.objectId);
|
|
314
|
+
if (autoPos) {
|
|
315
|
+
finalPosition = autoPos;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
284
318
|
}
|
|
285
319
|
// コンテナ相対の位置計算(パーセンテージ使用)
|
|
286
320
|
// positionX: -1.0 = 完全に左に見切れ, 0.0 = 中央, 1.0 = 完全に右に見切れ
|
|
@@ -390,4 +424,4 @@ export const GameScreen = ({ scenario, currentBlock, previousBlock, displayedCha
|
|
|
390
424
|
return null;
|
|
391
425
|
return renderCharacter(image, null, false, "fadeout");
|
|
392
426
|
})()] }))] }) }));
|
|
393
|
-
};
|
|
427
|
+
});
|
|
@@ -5,9 +5,8 @@ interface OverlayUIProps {
|
|
|
5
5
|
/**
|
|
6
6
|
* OverlayUI コンポーネント
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* - タブレット(幅 ≥ 1000px): 70%に縮小して中央配置
|
|
8
|
+
* アスペクト比コンテナ全体に配置されるUIオーバーレイ
|
|
9
|
+
* 親コンテナ(アスペクト比コンテナ)のサイズに合わせて自動調整される
|
|
11
10
|
*/
|
|
12
11
|
export declare const OverlayUI: React.FC<OverlayUIProps>;
|
|
13
12
|
export {};
|
|
@@ -1,21 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useScreenSizeAtom } from "../atoms/screen-size";
|
|
3
2
|
/**
|
|
4
3
|
* OverlayUI コンポーネント
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* - タブレット(幅 ≥ 1000px): 70%に縮小して中央配置
|
|
5
|
+
* アスペクト比コンテナ全体に配置されるUIオーバーレイ
|
|
6
|
+
* 親コンテナ(アスペクト比コンテナ)のサイズに合わせて自動調整される
|
|
9
7
|
*/
|
|
10
8
|
export const OverlayUI = ({ children }) => {
|
|
11
|
-
|
|
12
|
-
let [{ height: screenHeight }] = useScreenSizeAtom();
|
|
13
|
-
let screenWidth = (screenHeight * 16) / 9;
|
|
14
|
-
// タブレット(幅1000px以上)では70%に縮小
|
|
15
|
-
const [{ width: checkWidth }] = useScreenSizeAtom();
|
|
16
|
-
if (checkWidth >= 1000) {
|
|
17
|
-
screenHeight = (screenHeight * 7) / 10;
|
|
18
|
-
screenWidth = (screenHeight * 16) / 9;
|
|
19
|
-
}
|
|
20
|
-
return (_jsx("div", { className: "absolute inset-0 pointer-events-none z-[9999] flex justify-center items-center", children: _jsx("div", { style: { width: screenWidth, height: screenHeight }, children: children }) }));
|
|
9
|
+
return (_jsx("div", { className: "absolute inset-0 pointer-events-none z-[9999]", children: _jsx("div", { className: "w-full h-full", children: children }) }));
|
|
21
10
|
};
|
|
@@ -8,7 +8,7 @@ interface PluginComponentProviderProps {
|
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
10
|
* Wrapper component that renders a plugin-registered component or a fallback
|
|
11
|
-
*
|
|
11
|
+
* DataAPI is provided via props to avoid React context issues across different React instances
|
|
12
12
|
*/
|
|
13
13
|
export declare function PluginComponentProvider({ type, pluginManager, fallback: FallbackComponent, }: PluginComponentProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
14
14
|
export {};
|