@luna-editor/engine 0.5.10 → 0.5.12

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.
@@ -1,4 +1,45 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
+ /**
3
+ * speakerIdからキャラクター画像URLとクロップ情報を解決する
4
+ * 優先順位: speakerState > baseBodyState > characters[].entityState
5
+ */
6
+ function resolveSpeakerImage(block, allBlocks) {
7
+ var _a, _b, _c, _d, _e, _f, _g, _h;
8
+ // 1. speakerState.imageUrl(単一画像キャラ)
9
+ if ((_a = block.speakerState) === null || _a === void 0 ? void 0 : _a.imageUrl) {
10
+ return {
11
+ imageUrl: block.speakerState.imageUrl,
12
+ cropArea: (_b = block.speakerState.cropArea) !== null && _b !== void 0 ? _b : undefined,
13
+ };
14
+ }
15
+ // 2. speakerState.layers[0].imageUrl(レイヤー機能キャラの表情レイヤー)
16
+ if ((_e = (_d = (_c = block.speakerState) === null || _c === void 0 ? void 0 : _c.layers) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.imageUrl) {
17
+ return {
18
+ imageUrl: block.speakerState.layers[0].imageUrl,
19
+ cropArea: (_f = block.speakerState.cropArea) !== null && _f !== void 0 ? _f : undefined,
20
+ };
21
+ }
22
+ if (!block.speakerId)
23
+ return {};
24
+ // 2. character_entranceブロックからbaseBodyState or entityStateを探す
25
+ for (const b of allBlocks) {
26
+ if (b.characters) {
27
+ const char = b.characters.find((c) => c.object.id === block.speakerId);
28
+ if (char) {
29
+ if ((_g = char.baseBodyState) === null || _g === void 0 ? void 0 : _g.imageUrl) {
30
+ return { imageUrl: char.baseBodyState.imageUrl };
31
+ }
32
+ if (char.entityState.imageUrl) {
33
+ return {
34
+ imageUrl: char.entityState.imageUrl,
35
+ cropArea: (_h = char.entityState.cropArea) !== null && _h !== void 0 ? _h : undefined,
36
+ };
37
+ }
38
+ }
39
+ }
40
+ }
41
+ return {};
42
+ }
2
43
  export function useBacklog({ scenario, currentBlockIndex, currentBlock, }) {
3
44
  const [logs, setLogs] = useState([]);
4
45
  const hasBuiltInitialHistory = useRef(false);
@@ -26,8 +67,9 @@ export function useBacklog({ scenario, currentBlockIndex, currentBlock, }) {
26
67
  .map((block, index) => ({ block, realIndex: index }))
27
68
  .filter(({ block }) => block.blockType === "dialogue" || block.blockType === "narration");
28
69
  conversationBlocks.forEach(({ block, realIndex }) => {
29
- var _a, _b, _c, _d, _e;
70
+ var _a, _b, _c;
30
71
  if (!processedBlocks.current.has(realIndex)) {
72
+ const speakerImage = resolveSpeakerImage(block, scenario.blocks);
31
73
  const logEntry = {
32
74
  id: `${block.id}_init`,
33
75
  timestamp: Date.now(),
@@ -37,7 +79,8 @@ export function useBacklog({ scenario, currentBlockIndex, currentBlock, }) {
37
79
  speakerName: (_a = block.speaker) === null || _a === void 0 ? void 0 : _a.name,
38
80
  speakerState: (_b = block.speakerState) === null || _b === void 0 ? void 0 : _b.name,
39
81
  speakerId: (_c = block.speakerId) !== null && _c !== void 0 ? _c : undefined,
40
- speakerImageUrl: (_e = (_d = block.speakerState) === null || _d === void 0 ? void 0 : _d.imageUrl) !== null && _e !== void 0 ? _e : undefined,
82
+ speakerImageUrl: speakerImage.imageUrl,
83
+ speakerCropArea: speakerImage.cropArea,
41
84
  options: block.options,
42
85
  };
43
86
  addLogEntry(logEntry);
@@ -48,7 +91,7 @@ export function useBacklog({ scenario, currentBlockIndex, currentBlock, }) {
48
91
  }, [scenario.blocks, addLogEntry]);
49
92
  // 新しいブロックを処理
50
93
  useEffect(() => {
51
- var _a, _b, _c, _d, _e;
94
+ var _a, _b, _c;
52
95
  if (!currentBlock || !hasBuiltInitialHistory.current) {
53
96
  return;
54
97
  }
@@ -56,7 +99,7 @@ export function useBacklog({ scenario, currentBlockIndex, currentBlock, }) {
56
99
  if (currentBlock.blockType === "dialogue" ||
57
100
  currentBlock.blockType === "narration") {
58
101
  if (!processedBlocks.current.has(currentBlockIndex)) {
59
- console.log("Adding new conversation block to log:", currentBlock);
102
+ const speakerImage = resolveSpeakerImage(currentBlock, scenario.blocks);
60
103
  const logEntry = {
61
104
  id: `${currentBlock.id}_${Date.now()}`,
62
105
  timestamp: Date.now(),
@@ -66,7 +109,8 @@ export function useBacklog({ scenario, currentBlockIndex, currentBlock, }) {
66
109
  speakerName: (_a = currentBlock.speaker) === null || _a === void 0 ? void 0 : _a.name,
67
110
  speakerState: (_b = currentBlock.speakerState) === null || _b === void 0 ? void 0 : _b.name,
68
111
  speakerId: (_c = currentBlock.speakerId) !== null && _c !== void 0 ? _c : undefined,
69
- speakerImageUrl: (_e = (_d = currentBlock.speakerState) === null || _d === void 0 ? void 0 : _d.imageUrl) !== null && _e !== void 0 ? _e : undefined,
112
+ speakerImageUrl: speakerImage.imageUrl,
113
+ speakerCropArea: speakerImage.cropArea,
70
114
  options: currentBlock.options,
71
115
  };
72
116
  addLogEntry(logEntry);
package/dist/types.d.ts CHANGED
@@ -392,8 +392,10 @@ export interface BacklogEntry {
392
392
  speakerState?: string;
393
393
  /** 話者のオブジェクトID */
394
394
  speakerId?: string;
395
- /** 話者の画像URL(そのブロック時点の状態の画像) */
395
+ /** 話者の画像URL(そのブロック時点の状態の画像、またはレイヤーキャラの素体画像) */
396
396
  speakerImageUrl?: string;
397
+ /** 話者の画像クロップ情報(JSON: {x, y, width, height}) */
398
+ speakerCropArea?: string;
397
399
  /** Block options (for plugin-defined options like bubble style) */
398
400
  options?: BlockOptions | null;
399
401
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luna-editor/engine",
3
- "version": "0.5.10",
3
+ "version": "0.5.12",
4
4
  "description": "Luna Editor scenario playback engine",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",