@lingjingai/script-editor 0.1.28 → 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/index.d.ts CHANGED
@@ -11,9 +11,19 @@ interface SceneEnvironment {
11
11
  space?: "exterior" | "interior" | string;
12
12
  time?: "day" | "night" | "dawn" | "dusk" | string;
13
13
  }
14
+ /** v3 inline dialogue speaker: an actor ref, OR a non-actor source (system / broadcast / …). */
15
+ type V3Speaker = {
16
+ actor_id?: string;
17
+ state_id?: string | null;
18
+ } | {
19
+ kind?: "system" | "broadcast" | "offscreen" | "group" | string;
20
+ label?: string;
21
+ };
14
22
  interface ScriptItem {
15
23
  type?: ScriptContentType | string;
16
24
  content?: string;
25
+ /** v3: inline speaker object (actor ref or non-actor kind). */
26
+ speaker?: V3Speaker;
17
27
  actor_id?: string;
18
28
  speaker_id?: string;
19
29
  speakers?: Array<{
@@ -35,6 +45,8 @@ interface AssetState {
35
45
  description?: string;
36
46
  }
37
47
  type ScriptTargetKind = "actor" | "location" | "prop";
48
+ /** v3 asset tier: featured = 主角/配角 (下游出资产 + 跟踪状态); background = 龙套/背景. */
49
+ type Importance = "featured" | "background";
38
50
  interface StateChange {
39
51
  target_kind?: ScriptTargetKind | string;
40
52
  target_id?: string;
@@ -65,6 +77,11 @@ interface SceneContext {
65
77
  interface Scene {
66
78
  scene_id?: string;
67
79
  environment?: SceneEnvironment;
80
+ /** v3: single scene location (object), replaces the legacy location_ids[]. */
81
+ location?: {
82
+ location_id?: string;
83
+ state_id?: string | null;
84
+ } | null;
68
85
  location_ids?: string[];
69
86
  context?: SceneContext;
70
87
  actors?: Array<{
@@ -86,6 +103,7 @@ interface ScriptActor {
86
103
  actor_name?: string;
87
104
  name?: string;
88
105
  description?: string;
106
+ importance?: Importance;
89
107
  states?: AssetState[];
90
108
  }
91
109
  interface ScriptLocation {
@@ -93,6 +111,7 @@ interface ScriptLocation {
93
111
  location_name?: string;
94
112
  name?: string;
95
113
  description?: string;
114
+ importance?: Importance;
96
115
  states?: AssetState[];
97
116
  }
98
117
  interface ScriptProp {
@@ -100,6 +119,7 @@ interface ScriptProp {
100
119
  prop_name?: string;
101
120
  name?: string;
102
121
  description?: string;
122
+ importance?: Importance;
103
123
  states?: AssetState[];
104
124
  }
105
125
  interface ScriptEpisode {
@@ -703,6 +723,8 @@ declare function pickInheritedActorId(scene: Scene, beforeIndex: number): string
703
723
 
704
724
  type EntityKind = "actor" | "location" | "prop";
705
725
  declare function updateEntityName(data: ScriptProject, kind: EntityKind, id: string, name: string): ScriptProject;
726
+ /** Flip an asset between featured (主要) and background (龙套/背景). */
727
+ declare function setEntityImportance(data: ScriptProject, kind: EntityKind, id: string, importance: Importance): ScriptProject;
706
728
  declare function updateEntityDescription(data: ScriptProject, kind: EntityKind, id: string, description: string): ScriptProject;
707
729
  declare function updateEntityStateField(data: ScriptProject, kind: EntityKind, id: string, stateId: string, field: "state_name" | "description", value: string): ScriptProject;
708
730
  declare function addEntityState(data: ScriptProject, kind: EntityKind, id: string, state: AssetState): ScriptProject;
@@ -738,6 +760,7 @@ declare const index_nextEpisodeSceneId: typeof nextEpisodeSceneId;
738
760
  declare const index_pickInheritedActorId: typeof pickInheritedActorId;
739
761
  declare const index_removeEntityState: typeof removeEntityState;
740
762
  declare const index_replaceSceneItem: typeof replaceSceneItem;
763
+ declare const index_setEntityImportance: typeof setEntityImportance;
741
764
  declare const index_splitScene: typeof splitScene;
742
765
  declare const index_updateEntityDescription: typeof updateEntityDescription;
743
766
  declare const index_updateEntityName: typeof updateEntityName;
@@ -752,7 +775,7 @@ declare const index_updateSceneItemType: typeof updateSceneItemType;
752
775
  declare const index_updateSceneLocationState: typeof updateSceneLocationState;
753
776
  declare const index_updateScenePropState: typeof updateScenePropState;
754
777
  declare namespace index {
755
- export { type index_EntityKind as EntityKind, type index_ItemDraft as ItemDraft, type index_ItemLocation as ItemLocation, type index_SceneAssetStateBindingKind as SceneAssetStateBindingKind, type index_SceneAssetStateBindingTarget as SceneAssetStateBindingTarget, type index_SceneDraft as SceneDraft, index_addBlankScene as addBlankScene, index_addEntityState as addEntityState, index_addEpisode as addEpisode, index_buildNewScriptItem as buildNewScriptItem, index_buildSceneDraft as buildSceneDraft, index_createItemFromDraft as createItemFromDraft, index_createSceneFromDraft as createSceneFromDraft, index_decrementSceneId as decrementSceneId, index_deleteEpisode as deleteEpisode, index_deleteScene as deleteScene, index_deleteSceneItem as deleteSceneItem, index_getPrimarySceneLocationId as getPrimarySceneLocationId, index_getSceneActorsText as getSceneActorsText, index_incrementSceneId as incrementSceneId, index_insertScene as insertScene, index_insertSceneItem as insertSceneItem, index_mergeSceneIntoPrev as mergeSceneIntoPrev, index_moveSceneItem as moveSceneItem, index_moveScriptItem as moveScriptItem, index_nextAvailableSceneId as nextAvailableSceneId, index_nextEpisodeSceneId as nextEpisodeSceneId, index_pickInheritedActorId as pickInheritedActorId, index_removeEntityState as removeEntityState, index_replaceSceneItem as replaceSceneItem, index_splitScene as splitScene, index_updateEntityDescription as updateEntityDescription, index_updateEntityName as updateEntityName, index_updateEntityStateField as updateEntityStateField, index_updateSceneActorState as updateSceneActorState, index_updateSceneAssetStateBindings as updateSceneAssetStateBindings, index_updateSceneField as updateSceneField, index_updateSceneItemActor as updateSceneItemActor, index_updateSceneItemContent as updateSceneItemContent, index_updateSceneItemEmotion as updateSceneItemEmotion, index_updateSceneItemType as updateSceneItemType, index_updateSceneLocationState as updateSceneLocationState, index_updateScenePropState as updateScenePropState };
778
+ export { type index_EntityKind as EntityKind, type index_ItemDraft as ItemDraft, type index_ItemLocation as ItemLocation, type index_SceneAssetStateBindingKind as SceneAssetStateBindingKind, type index_SceneAssetStateBindingTarget as SceneAssetStateBindingTarget, type index_SceneDraft as SceneDraft, index_addBlankScene as addBlankScene, index_addEntityState as addEntityState, index_addEpisode as addEpisode, index_buildNewScriptItem as buildNewScriptItem, index_buildSceneDraft as buildSceneDraft, index_createItemFromDraft as createItemFromDraft, index_createSceneFromDraft as createSceneFromDraft, index_decrementSceneId as decrementSceneId, index_deleteEpisode as deleteEpisode, index_deleteScene as deleteScene, index_deleteSceneItem as deleteSceneItem, index_getPrimarySceneLocationId as getPrimarySceneLocationId, index_getSceneActorsText as getSceneActorsText, index_incrementSceneId as incrementSceneId, index_insertScene as insertScene, index_insertSceneItem as insertSceneItem, index_mergeSceneIntoPrev as mergeSceneIntoPrev, index_moveSceneItem as moveSceneItem, index_moveScriptItem as moveScriptItem, index_nextAvailableSceneId as nextAvailableSceneId, index_nextEpisodeSceneId as nextEpisodeSceneId, index_pickInheritedActorId as pickInheritedActorId, index_removeEntityState as removeEntityState, index_replaceSceneItem as replaceSceneItem, index_setEntityImportance as setEntityImportance, index_splitScene as splitScene, index_updateEntityDescription as updateEntityDescription, index_updateEntityName as updateEntityName, index_updateEntityStateField as updateEntityStateField, index_updateSceneActorState as updateSceneActorState, index_updateSceneAssetStateBindings as updateSceneAssetStateBindings, index_updateSceneField as updateSceneField, index_updateSceneItemActor as updateSceneItemActor, index_updateSceneItemContent as updateSceneItemContent, index_updateSceneItemEmotion as updateSceneItemEmotion, index_updateSceneItemType as updateSceneItemType, index_updateSceneLocationState as updateSceneLocationState, index_updateScenePropState as updateScenePropState };
756
779
  }
757
780
 
758
781
  /**
@@ -807,4 +830,4 @@ declare function DeleteConfirmButton({ onConfirm, disabled, targetScope, title,
807
830
  description?: string;
808
831
  }): react.JSX.Element;
809
832
 
810
- export { type AssetAdapter, type AssetDeleteRequest, type AssetState, type ChangeSet, type ChatReference, DeleteConfirmButton, type DiffActionMark, type DiffAdapter, type DiffSceneMark, EMPTY_SELECTION, EditableText, type EpisodeWorkspaceView, type FieldChange, Popover, type SaveStatus, type Scene, type SceneActionDiff, type SceneChangeKind, type SceneContext, type SceneEnvironment, type ScriptActor, ScriptContentType, type ScriptData, ScriptEditor, type ScriptEditorAdapters, type ScriptEditorProps, type ScriptEpisode, type ScriptFormat, type ScriptItem, type ScriptLocation, type ScriptNav, type ScriptNavEntity, type ScriptNavEpisode, type ScriptNavScene, type ScriptProject, type ScriptProp, type ScriptReferenceFocusRequest, type ScriptReferenceLocator, type ScriptSpeaker, type ScriptTargetKind, SelectField, type StateChange, type StudioSelection, type StudioSelectionFrom, type StudioSelectionOrigin, type TransitionPrompt, acceptActionIntoBaseline, acceptSceneIntoBaseline, applyActionAccept, applyActionReject, buildActionDiff, buildDiffFromBaseline, buildScriptNav, buildSelectionFrom, changeSetToDiffScenes, computeChangeSet, computeSceneActionDiff, findBaselineScene, fromToSelection, getActorCueVar, getActorMap, getEpisodeTitle, getLocationMap, getPropMap, getSceneContext, getSelectionEpisodeIdx, getSpeakerMap, isClipSelected, isEpisodeOpen, isSameSelectionTarget, isSceneSelected, index as mutations, rejectActionToBaseline, rejectSceneToBaseline, sceneChangeKey, selectionFromScriptReference, selectionTargetKey, withOrigin };
833
+ export { type AssetAdapter, type AssetDeleteRequest, type AssetState, type ChangeSet, type ChatReference, DeleteConfirmButton, type DiffActionMark, type DiffAdapter, type DiffSceneMark, EMPTY_SELECTION, EditableText, type EpisodeWorkspaceView, type FieldChange, type Importance, Popover, type SaveStatus, type Scene, type SceneActionDiff, type SceneChangeKind, type SceneContext, type SceneEnvironment, type ScriptActor, ScriptContentType, type ScriptData, ScriptEditor, type ScriptEditorAdapters, type ScriptEditorProps, type ScriptEpisode, type ScriptFormat, type ScriptItem, type ScriptLocation, type ScriptNav, type ScriptNavEntity, type ScriptNavEpisode, type ScriptNavScene, type ScriptProject, type ScriptProp, type ScriptReferenceFocusRequest, type ScriptReferenceLocator, type ScriptSpeaker, type ScriptTargetKind, SelectField, type StateChange, type StudioSelection, type StudioSelectionFrom, type StudioSelectionOrigin, type TransitionPrompt, type V3Speaker, acceptActionIntoBaseline, acceptSceneIntoBaseline, applyActionAccept, applyActionReject, buildActionDiff, buildDiffFromBaseline, buildScriptNav, buildSelectionFrom, changeSetToDiffScenes, computeChangeSet, computeSceneActionDiff, findBaselineScene, fromToSelection, getActorCueVar, getActorMap, getEpisodeTitle, getLocationMap, getPropMap, getSceneContext, getSelectionEpisodeIdx, getSpeakerMap, isClipSelected, isEpisodeOpen, isSameSelectionTarget, isSceneSelected, index as mutations, rejectActionToBaseline, rejectSceneToBaseline, sceneChangeKey, selectionFromScriptReference, selectionTargetKey, withOrigin };
package/dist/index.js CHANGED
@@ -280,12 +280,31 @@ function getStateDisplayName(state) {
280
280
  return state?.state_name?.trim() || state?.name?.trim() || state?.state_id?.trim() || "";
281
281
  }
282
282
  function getSceneContext(scene) {
283
+ const v3Loc = scene.location?.location_id?.trim() ? [{ location_id: scene.location.location_id, state_id: scene.location.state_id ?? null }] : null;
283
284
  return {
284
285
  actors: (scene.context?.actors?.length ? scene.context.actors : scene.actors) ?? [],
285
- locations: (scene.context?.locations?.length ? scene.context.locations : scene.locations) ?? [],
286
+ locations: (scene.context?.locations?.length ? scene.context.locations : scene.locations?.length ? scene.locations : v3Loc) ?? [],
286
287
  props: (scene.context?.props?.length ? scene.context.props : scene.props) ?? []
287
288
  };
288
289
  }
290
+ function countAssetAppearances(data) {
291
+ const actors = /* @__PURE__ */ new Map();
292
+ const locations = /* @__PURE__ */ new Map();
293
+ const props = /* @__PURE__ */ new Map();
294
+ const bump = (m, id) => {
295
+ const k = id?.trim();
296
+ if (k) m.set(k, (m.get(k) ?? 0) + 1);
297
+ };
298
+ for (const ep of data.episodes ?? []) {
299
+ for (const scene of ep.scenes ?? []) {
300
+ const ctx = getSceneContext(scene);
301
+ for (const a of ctx.actors) bump(actors, a.actor_id);
302
+ for (const l of ctx.locations) bump(locations, l.location_id);
303
+ for (const p of ctx.props) bump(props, p.prop_id);
304
+ }
305
+ }
306
+ return { actors, locations, props };
307
+ }
289
308
  function findState(states2, stateId) {
290
309
  const id = stateId?.trim();
291
310
  if (!id || !states2) return void 0;
@@ -411,7 +430,28 @@ function getSpeakerDisplayName(speakerId, speakerMap = /* @__PURE__ */ new Map()
411
430
  if (actorId && actorMap.has(actorId)) return getActorDisplayName(actorMap.get(actorId));
412
431
  return id;
413
432
  }
433
+ var V3_SPEAKER_KIND_ZH = {
434
+ system: "\u7CFB\u7EDF",
435
+ broadcast: "\u5E7F\u64AD",
436
+ offscreen: "\u753B\u5916\u97F3",
437
+ group: "\u7FA4\u4F53"
438
+ };
439
+ function getV3Speaker(item) {
440
+ const s = item.speaker;
441
+ if (!s || typeof s !== "object") return null;
442
+ if ("actor_id" in s && s.actor_id?.trim()) return { actorId: s.actor_id.trim() };
443
+ if ("kind" in s && s.kind) {
444
+ const label = s.label?.trim();
445
+ return { label: label || V3_SPEAKER_KIND_ZH[s.kind] || s.kind };
446
+ }
447
+ return null;
448
+ }
449
+ function getItemActorId(item) {
450
+ return item.actor_id?.trim() || getV3Speaker(item)?.actorId || "";
451
+ }
414
452
  function getDialogueSpeakerLabel(item, actorMap, speakerMap = /* @__PURE__ */ new Map()) {
453
+ const v3 = getV3Speaker(item);
454
+ if (v3) return v3.actorId ? getActorDisplayName(actorMap.get(v3.actorId)) || v3.actorId : v3.label ?? "";
415
455
  if (item.delivery === "overlap" && Array.isArray(item.lines) && item.lines.length > 0) {
416
456
  return item.lines.map((line) => getSpeakerDisplayName(line.speaker_id, speakerMap, actorMap)).filter(Boolean).join(" / ");
417
457
  }
@@ -478,6 +518,7 @@ function StudioToc({
478
518
  });
479
519
  const pick = (sel) => onSelect(withOrigin(sel, "sidebar"));
480
520
  const epHighlighted = selection.kind === "episode" || selection.kind === "scene" || selection.kind === "clip";
521
+ const counts = useMemo(() => countAssetAppearances(data), [data.episodes]);
481
522
  return /* @__PURE__ */ jsxs("nav", { className: "lj-se-toc", children: [
482
523
  /* @__PURE__ */ jsx(
483
524
  GroupHeader,
@@ -536,7 +577,10 @@ function StudioToc({
536
577
  icon: Users,
537
578
  label: "\u89D2\u8272",
538
579
  emptyText: "\u5C1A\u65E0\u89D2\u8272",
539
- items: (data.actors ?? []).map((a) => ({ id: (a.actor_id ?? "").trim(), name: getActorDisplayName(a) })),
580
+ items: (data.actors ?? []).map((a) => {
581
+ const id = (a.actor_id ?? "").trim();
582
+ return { id, name: getActorDisplayName(a), importance: a.importance, count: counts.actors.get(id) ?? 0 };
583
+ }),
540
584
  highlighted: selection.kind === "actor",
541
585
  isActive: (id) => selection.kind === "actor" && selection.actorId === id,
542
586
  onPick: (id) => pick({ kind: "actor", actorId: id })
@@ -548,7 +592,10 @@ function StudioToc({
548
592
  icon: MapPin,
549
593
  label: "\u573A\u666F",
550
594
  emptyText: "\u5C1A\u65E0\u573A\u666F",
551
- items: (data.locations ?? []).map((l) => ({ id: (l.location_id ?? "").trim(), name: getLocationDisplayName(l) })),
595
+ items: (data.locations ?? []).map((l) => {
596
+ const id = (l.location_id ?? "").trim();
597
+ return { id, name: getLocationDisplayName(l), importance: l.importance, count: counts.locations.get(id) ?? 0 };
598
+ }),
552
599
  highlighted: selection.kind === "location",
553
600
  isActive: (id) => selection.kind === "location" && selection.locationId === id,
554
601
  onPick: (id) => pick({ kind: "location", locationId: id })
@@ -560,7 +607,10 @@ function StudioToc({
560
607
  icon: Package,
561
608
  label: "\u9053\u5177",
562
609
  emptyText: "\u5C1A\u65E0\u9053\u5177",
563
- items: (data.props ?? []).map((p) => ({ id: (p.prop_id ?? "").trim(), name: getPropDisplayName(p) })),
610
+ items: (data.props ?? []).map((p) => {
611
+ const id = (p.prop_id ?? "").trim();
612
+ return { id, name: getPropDisplayName(p), importance: p.importance, count: counts.props.get(id) ?? 0 };
613
+ }),
564
614
  highlighted: selection.kind === "prop",
565
615
  isActive: (id) => selection.kind === "prop" && selection.propId === id,
566
616
  onPick: (id) => pick({ kind: "prop", propId: id })
@@ -578,22 +628,42 @@ function EntityGroup({
578
628
  onPick
579
629
  }) {
580
630
  const [open, setOpen] = useState(true);
631
+ const [bgOpen, setBgOpen] = useState(false);
632
+ const byCount = (a, b) => b.count - a.count || a.name.localeCompare(b.name, "zh");
581
633
  const valid = items.filter((i) => i.id);
634
+ const featured = valid.filter((i) => i.importance !== "background").sort(byCount);
635
+ const background = valid.filter((i) => i.importance === "background").sort(byCount);
636
+ const row = (i) => /* @__PURE__ */ jsxs(
637
+ "button",
638
+ {
639
+ type: "button",
640
+ className: cn("lj-se-tocitem lj-se-toc-l1", isActive(i.id) && "is-active"),
641
+ onClick: () => onPick(i.id),
642
+ children: [
643
+ /* @__PURE__ */ jsx(Icon, { className: "lj-se-tocitem-icon lj-se-ic-3", strokeWidth: 1.5 }),
644
+ /* @__PURE__ */ jsx("span", { className: "lj-se-tocitem-label", children: i.name }),
645
+ i.count > 0 ? /* @__PURE__ */ jsx("span", { className: "lj-se-tocitem-meta", children: i.count }) : null
646
+ ]
647
+ },
648
+ i.id
649
+ );
582
650
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [
583
651
  /* @__PURE__ */ jsx(GroupHeader, { icon: Icon, label, count: valid.length, open, highlighted, onToggle: () => setOpen((v) => !v) }),
584
- open ? valid.length === 0 ? /* @__PURE__ */ jsx("div", { className: "lj-se-toc-empty", children: emptyText }) : valid.map((i) => /* @__PURE__ */ jsxs(
585
- "button",
586
- {
587
- type: "button",
588
- className: cn("lj-se-tocitem lj-se-toc-l1", isActive(i.id) && "is-active"),
589
- onClick: () => onPick(i.id),
590
- children: [
591
- /* @__PURE__ */ jsx(Icon, { className: "lj-se-tocitem-icon lj-se-ic-3", strokeWidth: 1.5 }),
592
- /* @__PURE__ */ jsx("span", { className: "lj-se-tocitem-label", children: i.name })
593
- ]
594
- },
595
- i.id
596
- )) : null
652
+ open ? valid.length === 0 ? /* @__PURE__ */ jsx("div", { className: "lj-se-toc-empty", children: emptyText }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
653
+ featured.map(row),
654
+ background.length > 0 ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
655
+ /* @__PURE__ */ jsxs("button", { type: "button", className: cn("lj-se-toc-fold", bgOpen && "is-open"), onClick: () => setBgOpen((v) => !v), children: [
656
+ /* @__PURE__ */ jsx(ChevronRight, { className: "lj-se-toc-fold-chev lj-se-ic-3", strokeWidth: 2 }),
657
+ /* @__PURE__ */ jsxs("span", { children: [
658
+ "\u80CC\u666F",
659
+ label,
660
+ " \xB7 ",
661
+ background.length
662
+ ] })
663
+ ] }),
664
+ bgOpen ? background.map(row) : null
665
+ ] }) : null
666
+ ] }) : null
597
667
  ] });
598
668
  }
599
669
  var HighlightContext = createContext("");
@@ -928,18 +998,20 @@ function normalizeCommaList(value) {
928
998
  }
929
999
  function setSceneContext(scene, patch) {
930
1000
  const current = getSceneContext(scene);
931
- const context = {
932
- actors: patch.actors ?? current.actors,
933
- locations: patch.locations ?? current.locations,
934
- props: patch.props ?? current.props
935
- };
936
- return {
1001
+ const actors = patch.actors ?? current.actors;
1002
+ const props = patch.props ?? current.props;
1003
+ const locations = patch.locations ?? current.locations;
1004
+ const loc = locations[0];
1005
+ const next = {
937
1006
  ...scene,
938
- context,
939
- actors: context.actors,
940
- locations: context.locations,
941
- props: context.props
1007
+ actors,
1008
+ props,
1009
+ location: loc?.location_id?.trim() ? { location_id: loc.location_id, state_id: loc.state_id ?? null } : null
942
1010
  };
1011
+ delete next.context;
1012
+ delete next.locations;
1013
+ delete next.location_ids;
1014
+ return next;
943
1015
  }
944
1016
  function getSceneActorsText(scene) {
945
1017
  return (getSceneContext(scene).actors || []).map((item) => item.actor_id?.trim() || "").filter(Boolean).join("\uFF0C");
@@ -1028,17 +1100,18 @@ function createSceneFromDraft(draft) {
1028
1100
  space: draft.space.trim() || "interior",
1029
1101
  time: draft.time.trim() || "day"
1030
1102
  },
1031
- location_ids: location ? [location] : [],
1032
- locations: location ? [{ location_id: location, state_id: null }] : [],
1103
+ location: location ? { location_id: location, state_id: null } : null,
1033
1104
  actors: actors.map((actorId) => ({ actor_id: actorId, state_id: null })),
1105
+ props: [],
1034
1106
  actions: []
1035
1107
  };
1036
1108
  }
1037
1109
  function createItemFromDraft(draft) {
1038
1110
  if (draft.kind === "dialogue") {
1111
+ const actorId = draft.actorName.trim();
1039
1112
  return {
1040
1113
  type: "dialogue" /* Dialogue */,
1041
- actor_id: draft.actorName.trim(),
1114
+ ...actorId ? { speaker: { actor_id: actorId } } : {},
1042
1115
  emotion: draft.emotion.trim(),
1043
1116
  content: draft.content.trim()
1044
1117
  };
@@ -1064,10 +1137,7 @@ function updateSceneField(data, episodeIndex, sceneIndex, field, value) {
1064
1137
  }
1065
1138
  if (field === "location") {
1066
1139
  const location = value.trim();
1067
- return setSceneContext({
1068
- ...scene,
1069
- location_ids: location ? [location] : []
1070
- }, { locations: location ? [{ location_id: location, state_id: null }] : [] });
1140
+ return setSceneContext(scene, { locations: location ? [{ location_id: location, state_id: null }] : [] });
1071
1141
  }
1072
1142
  if (field === "props") {
1073
1143
  const propIds = normalizeCommaList(value);
@@ -1210,7 +1280,8 @@ function updateSceneItemActor(data, episodeIndex, sceneIndex, itemIndex, actorId
1210
1280
  const actions = [...scene.actions || []];
1211
1281
  const item = actions[itemIndex];
1212
1282
  if (!item) return scene;
1213
- const next = { ...item, actor_id: actorId.trim() };
1283
+ const next = { ...item, speaker: { actor_id: actorId.trim() } };
1284
+ delete next.actor_id;
1214
1285
  delete next.speaker_id;
1215
1286
  delete next.speakers;
1216
1287
  delete next.lines;
@@ -1437,7 +1508,7 @@ function buildActorDistribution(data, actorId) {
1437
1508
  (episode.scenes ?? []).forEach((scene, si) => {
1438
1509
  const ctx = getSceneContext(scene);
1439
1510
  const ref = (ctx.actors ?? []).find((i) => (i.actor_id ?? "").trim() === actorId);
1440
- const inActions = !ref && (scene.actions ?? []).some((i) => (i.actor_id ?? "").trim() === actorId);
1511
+ const inActions = !ref && (scene.actions ?? []).some((i) => getItemActorId(i) === actorId);
1441
1512
  if (ref || inActions) {
1442
1513
  items.push({
1443
1514
  key: `actor-${actorId}-${ei}-${si}`,
@@ -1808,11 +1879,17 @@ function updateSceneItemType(data, episodeIndex, sceneIndex, itemIndex, type) {
1808
1879
  const current = actions[itemIndex];
1809
1880
  if (!current) return scene;
1810
1881
  const next = { ...current, type };
1882
+ delete next.actor_id;
1883
+ delete next.speaker_id;
1884
+ delete next.speakers;
1885
+ delete next.lines;
1811
1886
  if (!isActorBearingContentType(type)) {
1812
- delete next.actor_id;
1887
+ delete next.speaker;
1813
1888
  delete next.emotion;
1814
1889
  } else {
1815
- next.actor_id = next.actor_id?.trim() || pickInheritedActorId(scene, itemIndex);
1890
+ const actorId = getItemActorId(current) || pickInheritedActorId(scene, itemIndex);
1891
+ if (actorId) next.speaker = { actor_id: actorId };
1892
+ else delete next.speaker;
1816
1893
  if (type !== "dialogue" /* Dialogue */) {
1817
1894
  delete next.emotion;
1818
1895
  }
@@ -1823,9 +1900,10 @@ function updateSceneItemType(data, episodeIndex, sceneIndex, itemIndex, type) {
1823
1900
  }
1824
1901
  function buildNewScriptItem(type, inheritedActorId) {
1825
1902
  if (isActorBearingContentType(type)) {
1903
+ const actorId = inheritedActorId?.trim();
1826
1904
  return {
1827
1905
  type,
1828
- actor_id: inheritedActorId?.trim() || "",
1906
+ ...actorId ? { speaker: { actor_id: actorId } } : {},
1829
1907
  content: ""
1830
1908
  };
1831
1909
  }
@@ -1835,8 +1913,9 @@ function pickInheritedActorId(scene, beforeIndex) {
1835
1913
  const actions = scene.actions || [];
1836
1914
  for (let i = beforeIndex - 1; i >= 0; i -= 1) {
1837
1915
  const item = actions[i];
1838
- if (item && isActorBearingContentType(item.type) && item.actor_id?.trim()) {
1839
- return item.actor_id.trim();
1916
+ if (item && isActorBearingContentType(item.type)) {
1917
+ const actorId = getItemActorId(item);
1918
+ if (actorId) return actorId;
1840
1919
  }
1841
1920
  }
1842
1921
  return "";
@@ -2346,8 +2425,9 @@ function SceneItemRowImpl({
2346
2425
  const actorId = item.actor_id?.trim() || "";
2347
2426
  const emotion = translateEmotionLabel(item.emotion);
2348
2427
  const stateLabels = formatStateChangeLabels(item, actorMap, locationMap, propMap);
2349
- const multiSpeaker = type === "dialogue" /* Dialogue */ && (item.delivery === "overlap" || (item.lines?.length ?? 0) > 0 || (item.speakers?.length ?? 0) > 1);
2350
- const resolvedActorId = actorId || (() => {
2428
+ const v3Speaker = getV3Speaker(item);
2429
+ const multiSpeaker = type === "dialogue" /* Dialogue */ && (item.delivery === "overlap" || (item.lines?.length ?? 0) > 0 || (item.speakers?.length ?? 0) > 1 || !!v3Speaker?.label);
2430
+ const resolvedActorId = actorId || v3Speaker?.actorId || (() => {
2351
2431
  const spkId = item.speaker_id?.trim() || item.speakers?.[0]?.speaker_id?.trim() || "";
2352
2432
  if (!spkId) return "";
2353
2433
  const spk = speakerMap.get(spkId);
@@ -2869,6 +2949,9 @@ function mapEntity(data, kind, id, fn) {
2869
2949
  function updateEntityName(data, kind, id, name) {
2870
2950
  return mapEntity(data, kind, id, (e) => ({ ...e, [NAME_FIELD[kind]]: name }));
2871
2951
  }
2952
+ function setEntityImportance(data, kind, id, importance) {
2953
+ return mapEntity(data, kind, id, (e) => ({ ...e, importance }));
2954
+ }
2872
2955
  function updateEntityDescription(data, kind, id, description) {
2873
2956
  return mapEntity(data, kind, id, (e) => ({ ...e, description }));
2874
2957
  }
@@ -2894,6 +2977,8 @@ var TIME_ZH = { day: "\u65E5", night: "\u591C", dawn: "\u6668", dusk: "\u66AE" }
2894
2977
  function resolveCueId(item, actorMap, speakerMap) {
2895
2978
  const actorId = item.actor_id?.trim();
2896
2979
  if (actorId) return actorId;
2980
+ const v3 = getV3Speaker(item);
2981
+ if (v3) return v3.actorId ?? "";
2897
2982
  const spkId = item.speaker_id?.trim() || item.speakers?.[0]?.speaker_id?.trim() || "";
2898
2983
  if (!spkId) return "";
2899
2984
  const spk = speakerMap.get(spkId);
@@ -3393,6 +3478,20 @@ function AssetCardImpl({
3393
3478
  /* @__PURE__ */ jsx("span", { className: "lj-se-assetcard-id", children: assetId }),
3394
3479
  /* @__PURE__ */ jsxs("span", { className: "lj-se-assetcard-actions", children: [
3395
3480
  /* @__PURE__ */ jsx("span", { className: "lj-se-assetcard-badge", children: assetGroupLabel(group) }),
3481
+ (() => {
3482
+ const isBg = entity.importance === "background";
3483
+ return /* @__PURE__ */ jsx(
3484
+ "button",
3485
+ {
3486
+ type: "button",
3487
+ className: cn("lj-se-assetcard-imp", isBg && "is-bg"),
3488
+ disabled,
3489
+ title: isBg ? "\u80CC\u666F\uFF08\u9F99\u5957\uFF09\xB7 \u70B9\u51FB\u5347\u4E3A\u4E3B\u8981" : "\u4E3B\u8981 \xB7 \u70B9\u51FB\u964D\u4E3A\u80CC\u666F",
3490
+ onClick: () => onEdit((p) => setEntityImportance(p, kind, assetId, isBg ? "featured" : "background")),
3491
+ children: isBg ? "\u80CC\u666F" : "\u4E3B\u8981"
3492
+ }
3493
+ );
3494
+ })(),
3396
3495
  /* @__PURE__ */ jsxs("span", { className: "lj-se-assetcard-badge", children: [
3397
3496
  sceneCount,
3398
3497
  " \u573A \xB7 ",
@@ -4812,6 +4911,7 @@ __export(mutations_exports, {
4812
4911
  pickInheritedActorId: () => pickInheritedActorId,
4813
4912
  removeEntityState: () => removeEntityState,
4814
4913
  replaceSceneItem: () => replaceSceneItem,
4914
+ setEntityImportance: () => setEntityImportance,
4815
4915
  splitScene: () => splitScene,
4816
4916
  updateEntityDescription: () => updateEntityDescription,
4817
4917
  updateEntityName: () => updateEntityName,