@luna-editor/engine 0.1.0 → 0.2.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/dist/Player.js +28 -13
- package/dist/contexts/AudioContext.d.ts +13 -0
- package/dist/contexts/AudioContext.js +13 -0
- package/dist/contexts/DataContext.d.ts +4 -1
- package/dist/contexts/DataContext.js +14 -3
- package/dist/hooks/useVoice.js +4 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/plugin/PluginManager.js +3 -0
- package/dist/sdk.d.ts +6 -1
- package/dist/types.d.ts +15 -0
- package/package.json +1 -1
package/dist/Player.js
CHANGED
|
@@ -17,6 +17,7 @@ import { EndScreen } from "./components/EndScreen";
|
|
|
17
17
|
import { GameScreen } from "./components/GameScreen";
|
|
18
18
|
import { OverlayUI } from "./components/OverlayUI";
|
|
19
19
|
import { PluginComponentProvider } from "./components/PluginComponentProvider";
|
|
20
|
+
import { AudioProvider } from "./contexts/AudioContext";
|
|
20
21
|
import { DataProvider } from "./contexts/DataContext";
|
|
21
22
|
import { useBacklog } from "./hooks/useBacklog";
|
|
22
23
|
import { usePlayerLogic } from "./hooks/usePlayerLogic";
|
|
@@ -26,8 +27,16 @@ import { usePreloadImages } from "./hooks/usePreloadImages";
|
|
|
26
27
|
import { useTypewriter } from "./hooks/useTypewriter";
|
|
27
28
|
import { PluginManager } from "./plugin/PluginManager";
|
|
28
29
|
import { ComponentType } from "./sdk";
|
|
29
|
-
export const Player = ({ scenario, settings, plugins = [], onEnd, onScenarioEnd, onScenarioStart, onScenarioCancelled, className, autoplay = false, }) => {
|
|
30
|
+
export const Player = ({ scenario, settings, plugins = [], onEnd, onScenarioEnd, onScenarioStart, onScenarioCancelled, onSettingsChange, className, autoplay = false, }) => {
|
|
30
31
|
var _a;
|
|
32
|
+
// デフォルト値とマージ
|
|
33
|
+
const mergedSettings = Object.assign({ textSpeed: 80, autoPlaySpeed: 3, bgmVolume: 1.0, seVolume: 1.0, voiceVolume: 1.0, effectVolume: 1.0, textSoundVolume: 1.0 }, settings);
|
|
34
|
+
// プラグインからの設定更新ハンドラ
|
|
35
|
+
const handleSettingsUpdate = useCallback((updatedSettings) => {
|
|
36
|
+
var _a, _b;
|
|
37
|
+
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);
|
|
38
|
+
onSettingsChange === null || onSettingsChange === void 0 ? void 0 : onSettingsChange(newSettings);
|
|
39
|
+
}, [settings, onSettingsChange]);
|
|
31
40
|
const pluginManagerRef = useRef(new PluginManager());
|
|
32
41
|
// グローバルUIAPIを初期化(プラグインコンポーネントから使用可能にする)
|
|
33
42
|
useEffect(() => {
|
|
@@ -127,7 +136,7 @@ export const Player = ({ scenario, settings, plugins = [], onEnd, onScenarioEnd,
|
|
|
127
136
|
}
|
|
128
137
|
}, [isFirstRenderComplete]);
|
|
129
138
|
const { displayText, isTyping, skipTyping, startTyping } = useTypewriter({
|
|
130
|
-
speed:
|
|
139
|
+
speed: mergedSettings.textSpeed,
|
|
131
140
|
});
|
|
132
141
|
// 現在の表示可能なブロックを取得
|
|
133
142
|
const currentBlock = displayableBlockIndices[state.currentBlockIndex] !== undefined
|
|
@@ -265,13 +274,13 @@ export const Player = ({ scenario, settings, plugins = [], onEnd, onScenarioEnd,
|
|
|
265
274
|
clearLogs: backlog.clearLogs,
|
|
266
275
|
},
|
|
267
276
|
settings: {
|
|
268
|
-
aspectRatio: (_a =
|
|
269
|
-
bgObjectFit: (_b =
|
|
270
|
-
textSpeed:
|
|
271
|
-
autoPlaySpeed:
|
|
272
|
-
bgmVolume:
|
|
273
|
-
seVolume:
|
|
274
|
-
voiceVolume:
|
|
277
|
+
aspectRatio: (_a = mergedSettings.aspectRatio) !== null && _a !== void 0 ? _a : "16:9",
|
|
278
|
+
bgObjectFit: (_b = mergedSettings.bgObjectFit) !== null && _b !== void 0 ? _b : "contain",
|
|
279
|
+
textSpeed: mergedSettings.textSpeed,
|
|
280
|
+
autoPlaySpeed: mergedSettings.autoPlaySpeed,
|
|
281
|
+
bgmVolume: mergedSettings.bgmVolume,
|
|
282
|
+
seVolume: mergedSettings.seVolume,
|
|
283
|
+
voiceVolume: mergedSettings.voiceVolume,
|
|
275
284
|
skipMode: "unread",
|
|
276
285
|
},
|
|
277
286
|
pluginAssets: {
|
|
@@ -329,8 +338,14 @@ export const Player = ({ scenario, settings, plugins = [], onEnd, onScenarioEnd,
|
|
|
329
338
|
touchAction: "none",
|
|
330
339
|
userSelect: "none",
|
|
331
340
|
WebkitUserSelect: "none",
|
|
332
|
-
}, children: _jsx("div", { className: "relative bg-white flex flex-col w-full overflow-hidden h-full", style: { aspectRatio: getAspectRatio() }, children:
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
341
|
+
}, children: _jsx("div", { className: "relative bg-white flex flex-col w-full overflow-hidden h-full", style: { aspectRatio: getAspectRatio() }, children: _jsx(DataProvider, { data: dataContext, onSettingsUpdate: handleSettingsUpdate, children: _jsxs(AudioProvider, { settings: {
|
|
342
|
+
bgmVolume: mergedSettings.bgmVolume,
|
|
343
|
+
seVolume: mergedSettings.seVolume,
|
|
344
|
+
voiceVolume: mergedSettings.voiceVolume,
|
|
345
|
+
effectVolume: mergedSettings.effectVolume,
|
|
346
|
+
textSoundVolume: mergedSettings.textSoundVolume,
|
|
347
|
+
}, children: [_jsx("div", { className: "h-full", children: _jsx(GameScreen, { scenario: scenario, currentBlock: currentBlock, displayedCharacters: displayedCharacters }) }), _jsx(OverlayUI, { children: _jsxs("div", { className: "h-full w-full relative", children: [_jsx(PluginComponentProvider, { type: ComponentType.DialogueBox, pluginManager: pluginManagerRef.current, fallback: DialogueBox }), pluginManagerRef.current
|
|
348
|
+
.getRegisteredComponents()
|
|
349
|
+
.filter((type) => type !== ComponentType.DialogueBox) // DialogueBoxは既に上でレンダリング済み
|
|
350
|
+
.map((componentType) => (_jsx(PluginComponentProvider, { type: componentType, pluginManager: pluginManagerRef.current }, componentType)))] }) })] }) }) }) })] }))] }));
|
|
336
351
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export interface AudioSettings {
|
|
3
|
+
bgmVolume: number;
|
|
4
|
+
seVolume: number;
|
|
5
|
+
voiceVolume: number;
|
|
6
|
+
effectVolume: number;
|
|
7
|
+
textSoundVolume: number;
|
|
8
|
+
}
|
|
9
|
+
export declare const AudioProvider: ({ children, settings, }: {
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
settings: AudioSettings;
|
|
12
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export declare const useAudioSettings: () => AudioSettings;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
const AudioContext = createContext({
|
|
4
|
+
bgmVolume: 1.0,
|
|
5
|
+
seVolume: 1.0,
|
|
6
|
+
voiceVolume: 1.0,
|
|
7
|
+
effectVolume: 1.0,
|
|
8
|
+
textSoundVolume: 1.0,
|
|
9
|
+
});
|
|
10
|
+
export const AudioProvider = ({ children, settings, }) => {
|
|
11
|
+
return (_jsx(AudioContext.Provider, { value: settings, children: children }));
|
|
12
|
+
};
|
|
13
|
+
export const useAudioSettings = () => useContext(AudioContext);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import type { DataAPI, DataContext } from "../sdk";
|
|
2
|
+
import type { DataAPI, DataContext, PlayerSettingsData } from "../sdk";
|
|
3
|
+
type SettingsUpdater = (settings: Partial<PlayerSettingsData>) => void;
|
|
3
4
|
/**
|
|
4
5
|
* データプロバイダー
|
|
5
6
|
* プラグインがシナリオ情報にリアクティブにアクセスするためのProvider
|
|
@@ -15,6 +16,7 @@ import type { DataAPI, DataContext } from "../sdk";
|
|
|
15
16
|
*/
|
|
16
17
|
export declare const DataProvider: React.FC<{
|
|
17
18
|
data: DataContext;
|
|
19
|
+
onSettingsUpdate?: SettingsUpdater;
|
|
18
20
|
children: ReactNode;
|
|
19
21
|
}>;
|
|
20
22
|
/**
|
|
@@ -22,3 +24,4 @@ export declare const DataProvider: React.FC<{
|
|
|
22
24
|
* プラグインから呼び出される
|
|
23
25
|
*/
|
|
24
26
|
export declare function useDataAPI(): DataAPI;
|
|
27
|
+
export {};
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, } from "react";
|
|
3
3
|
const DataContextInstance = createContext(null);
|
|
4
4
|
const SubscribersContext = createContext(null);
|
|
5
|
+
const SettingsUpdaterContext = createContext(null);
|
|
5
6
|
/**
|
|
6
7
|
* データプロバイダー
|
|
7
8
|
* プラグインがシナリオ情報にリアクティブにアクセスするためのProvider
|
|
@@ -15,7 +16,7 @@ const SubscribersContext = createContext(null);
|
|
|
15
16
|
* </DataProvider>
|
|
16
17
|
* ```
|
|
17
18
|
*/
|
|
18
|
-
export const DataProvider = ({ data, children }) => {
|
|
19
|
+
export const DataProvider = ({ data, onSettingsUpdate, children }) => {
|
|
19
20
|
const subscribers = useMemo(() => new Map(), // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
20
21
|
[]);
|
|
21
22
|
const previousDataRef = useRef(data);
|
|
@@ -48,7 +49,7 @@ export const DataProvider = ({ data, children }) => {
|
|
|
48
49
|
}
|
|
49
50
|
previousDataRef.current = data;
|
|
50
51
|
}, [data, subscribers]);
|
|
51
|
-
return (_jsx(SubscribersContext.Provider, { value: subscribers, children: _jsx(DataContextInstance.Provider, { value: data, children: children }) }));
|
|
52
|
+
return (_jsx(SubscribersContext.Provider, { value: subscribers, children: _jsx(SettingsUpdaterContext.Provider, { value: onSettingsUpdate !== null && onSettingsUpdate !== void 0 ? onSettingsUpdate : null, children: _jsx(DataContextInstance.Provider, { value: data, children: children }) }) }));
|
|
52
53
|
};
|
|
53
54
|
/**
|
|
54
55
|
* DataAPI実装を提供するフック
|
|
@@ -57,6 +58,7 @@ export const DataProvider = ({ data, children }) => {
|
|
|
57
58
|
export function useDataAPI() {
|
|
58
59
|
const data = useContext(DataContextInstance);
|
|
59
60
|
const subscribers = useContext(SubscribersContext);
|
|
61
|
+
const settingsUpdater = useContext(SettingsUpdaterContext);
|
|
60
62
|
if (!data || !subscribers) {
|
|
61
63
|
throw new Error("useDataAPI must be used within DataProvider");
|
|
62
64
|
}
|
|
@@ -93,9 +95,18 @@ export function useDataAPI() {
|
|
|
93
95
|
(_a = subscribers.get(subscriberKey)) === null || _a === void 0 ? void 0 : _a.delete(callback);
|
|
94
96
|
};
|
|
95
97
|
}, [subscribers]);
|
|
98
|
+
const updateSettings = useCallback((settings) => {
|
|
99
|
+
if (settingsUpdater) {
|
|
100
|
+
settingsUpdater(settings);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
console.warn("updateSettings called but no settings updater provided");
|
|
104
|
+
}
|
|
105
|
+
}, [settingsUpdater]);
|
|
96
106
|
return useMemo(() => ({
|
|
97
107
|
get,
|
|
98
108
|
subscribe,
|
|
99
109
|
watch,
|
|
100
|
-
|
|
110
|
+
updateSettings,
|
|
111
|
+
}), [get, subscribe, watch, updateSettings]);
|
|
101
112
|
}
|
package/dist/hooks/useVoice.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { useCallback, useRef } from "react";
|
|
2
|
+
import { useAudioSettings } from "../contexts/AudioContext";
|
|
2
3
|
export const useVoice = () => {
|
|
3
4
|
const audioRef = useRef(null);
|
|
5
|
+
const audioSettings = useAudioSettings();
|
|
4
6
|
const playVoice = useCallback((voiceUrl) => {
|
|
5
7
|
if (!audioRef.current) {
|
|
6
8
|
audioRef.current = new Audio();
|
|
7
9
|
}
|
|
8
10
|
audioRef.current.src = voiceUrl;
|
|
11
|
+
audioRef.current.volume = audioSettings.voiceVolume;
|
|
9
12
|
audioRef.current.play().catch(console.error);
|
|
10
|
-
}, []);
|
|
13
|
+
}, [audioSettings.voiceVolume]);
|
|
11
14
|
const stopVoice = useCallback(() => {
|
|
12
15
|
if (audioRef.current) {
|
|
13
16
|
audioRef.current.pause();
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { useScreenSizeAtom } from "./atoms/screen-size";
|
|
2
2
|
export { OverlayUI } from "./components/OverlayUI";
|
|
3
3
|
export { aspectRatio, BasisHeight, BasisWidth } from "./constants/screen-size";
|
|
4
|
+
export type { AudioSettings } from "./contexts/AudioContext";
|
|
5
|
+
export { useAudioSettings } from "./contexts/AudioContext";
|
|
4
6
|
export { useDataAPI } from "./contexts/DataContext";
|
|
5
7
|
export { setGlobalUIAPI, usePluginAPI, useUIVisibility, } from "./hooks/usePluginAPI";
|
|
6
8
|
export { useScreenScale, useScreenSize, useToPixel, } from "./hooks/useScreenSize";
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
export { useScreenSizeAtom } from "./atoms/screen-size";
|
|
3
3
|
export { OverlayUI } from "./components/OverlayUI";
|
|
4
4
|
export { aspectRatio, BasisHeight, BasisWidth } from "./constants/screen-size";
|
|
5
|
+
export { useAudioSettings } from "./contexts/AudioContext";
|
|
5
6
|
export { useDataAPI } from "./contexts/DataContext";
|
|
6
7
|
export { setGlobalUIAPI, usePluginAPI, useUIVisibility, } from "./hooks/usePluginAPI";
|
|
7
8
|
export { useScreenScale, useScreenSize, useToPixel, } from "./hooks/useScreenSize";
|
|
@@ -621,6 +621,9 @@ export class PluginManager {
|
|
|
621
621
|
watch: () => {
|
|
622
622
|
throw new Error("DataAPI.watch() can only be called from within DataProvider context");
|
|
623
623
|
},
|
|
624
|
+
updateSettings: () => {
|
|
625
|
+
throw new Error("DataAPI.updateSettings() can only be called from within DataProvider context");
|
|
626
|
+
},
|
|
624
627
|
},
|
|
625
628
|
};
|
|
626
629
|
}
|
package/dist/sdk.d.ts
CHANGED
|
@@ -48,7 +48,7 @@ export interface BacklogData {
|
|
|
48
48
|
* プレイヤー設定データ
|
|
49
49
|
*/
|
|
50
50
|
export interface PlayerSettingsData extends PlayerSettings {
|
|
51
|
-
/** テキスト速度 (
|
|
51
|
+
/** テキスト速度 (ミリ秒/文字) */
|
|
52
52
|
textSpeed: number;
|
|
53
53
|
/** 自動再生速度 (秒) */
|
|
54
54
|
autoPlaySpeed: number;
|
|
@@ -168,6 +168,11 @@ export interface DataAPI {
|
|
|
168
168
|
* @returns unsubscribe関数
|
|
169
169
|
*/
|
|
170
170
|
watch<K extends keyof DataContext, P extends keyof DataContext[K]>(key: K, property: P, callback: DataSubscriber<DataContext[K][P]>): () => void;
|
|
171
|
+
/**
|
|
172
|
+
* プレイヤー設定を更新
|
|
173
|
+
* @param settings - 更新する設定値(部分更新可能)
|
|
174
|
+
*/
|
|
175
|
+
updateSettings(settings: Partial<PlayerSettingsData>): void;
|
|
171
176
|
}
|
|
172
177
|
export interface PluginAPI {
|
|
173
178
|
style: StyleAPI;
|
package/dist/types.d.ts
CHANGED
|
@@ -153,6 +153,20 @@ export interface PublishedScenario {
|
|
|
153
153
|
export interface PlayerSettings {
|
|
154
154
|
aspectRatio: string;
|
|
155
155
|
bgObjectFit: "contain" | "cover";
|
|
156
|
+
/** 文字送り速度 (ミリ秒/文字, デフォルト: 80) */
|
|
157
|
+
textSpeed?: number;
|
|
158
|
+
/** 自動再生速度 (秒, デフォルト: 3) */
|
|
159
|
+
autoPlaySpeed?: number;
|
|
160
|
+
/** BGM音量 (0-1, デフォルト: 1) */
|
|
161
|
+
bgmVolume?: number;
|
|
162
|
+
/** SE音量 (0-1, デフォルト: 1) */
|
|
163
|
+
seVolume?: number;
|
|
164
|
+
/** パートボイス音量 (0-1, デフォルト: 1) */
|
|
165
|
+
voiceVolume?: number;
|
|
166
|
+
/** エフェクト音音量 (0-1, デフォルト: 1) */
|
|
167
|
+
effectVolume?: number;
|
|
168
|
+
/** テキスト音音量 (0-1, デフォルト: 1) */
|
|
169
|
+
textSoundVolume?: number;
|
|
156
170
|
}
|
|
157
171
|
export interface PluginConfig {
|
|
158
172
|
packageName: string;
|
|
@@ -167,6 +181,7 @@ export interface PlayerProps {
|
|
|
167
181
|
onScenarioEnd?: () => void;
|
|
168
182
|
onScenarioStart?: () => void;
|
|
169
183
|
onScenarioCancelled?: () => void;
|
|
184
|
+
onSettingsChange?: (settings: PlayerSettings) => void;
|
|
170
185
|
className?: string;
|
|
171
186
|
autoplay?: boolean;
|
|
172
187
|
}
|