@lingjingai/script-editor 0.1.21 → 0.1.22

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
@@ -618,9 +618,15 @@ declare function decrementSceneId(sceneId: string | undefined, fallbackEpisodeIn
618
618
  declare function createSceneFromDraft(draft: SceneDraft): Scene;
619
619
  declare function createItemFromDraft(draft: ItemDraft): ScriptItem;
620
620
  declare function updateSceneField(data: ScriptProject, episodeIndex: number, sceneIndex: number, field: "scene_id" | "space" | "time" | "location" | "actors" | "props", value: string): ScriptProject;
621
+ type SceneAssetStateBindingKind = "actor" | "location" | "prop";
622
+ interface SceneAssetStateBindingTarget {
623
+ episodeIndex: number;
624
+ sceneIndex: number;
625
+ }
621
626
  declare function updateSceneActorState(data: ScriptProject, episodeIndex: number, sceneIndex: number, actorId: string, stateId: string | null): ScriptProject;
622
627
  declare function updateSceneLocationState(data: ScriptProject, episodeIndex: number, sceneIndex: number, locationId: string, stateId: string | null): ScriptProject;
623
628
  declare function updateScenePropState(data: ScriptProject, episodeIndex: number, sceneIndex: number, propId: string, stateId: string | null): ScriptProject;
629
+ declare function updateSceneAssetStateBindings(data: ScriptProject, kind: SceneAssetStateBindingKind, assetId: string, targets: SceneAssetStateBindingTarget[], stateId: string | null): ScriptProject;
624
630
  declare function updateSceneItemContent(data: ScriptProject, episodeIndex: number, sceneIndex: number, itemIndex: number, content: string): ScriptProject;
625
631
  declare function updateSceneItemActor(data: ScriptProject, episodeIndex: number, sceneIndex: number, itemIndex: number, actorId: string): ScriptProject;
626
632
  declare function updateSceneItemEmotion(data: ScriptProject, episodeIndex: number, sceneIndex: number, itemIndex: number, emotion: string): ScriptProject;
@@ -705,6 +711,8 @@ declare function removeEntityState(data: ScriptProject, kind: EntityKind, id: st
705
711
  type index_EntityKind = EntityKind;
706
712
  type index_ItemDraft = ItemDraft;
707
713
  type index_ItemLocation = ItemLocation;
714
+ type index_SceneAssetStateBindingKind = SceneAssetStateBindingKind;
715
+ type index_SceneAssetStateBindingTarget = SceneAssetStateBindingTarget;
708
716
  type index_SceneDraft = SceneDraft;
709
717
  declare const index_addBlankScene: typeof addBlankScene;
710
718
  declare const index_addEntityState: typeof addEntityState;
@@ -735,6 +743,7 @@ declare const index_updateEntityDescription: typeof updateEntityDescription;
735
743
  declare const index_updateEntityName: typeof updateEntityName;
736
744
  declare const index_updateEntityStateField: typeof updateEntityStateField;
737
745
  declare const index_updateSceneActorState: typeof updateSceneActorState;
746
+ declare const index_updateSceneAssetStateBindings: typeof updateSceneAssetStateBindings;
738
747
  declare const index_updateSceneField: typeof updateSceneField;
739
748
  declare const index_updateSceneItemActor: typeof updateSceneItemActor;
740
749
  declare const index_updateSceneItemContent: typeof updateSceneItemContent;
@@ -743,7 +752,7 @@ declare const index_updateSceneItemType: typeof updateSceneItemType;
743
752
  declare const index_updateSceneLocationState: typeof updateSceneLocationState;
744
753
  declare const index_updateScenePropState: typeof updateScenePropState;
745
754
  declare namespace index {
746
- export { type index_EntityKind as EntityKind, type index_ItemDraft as ItemDraft, type index_ItemLocation as ItemLocation, 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_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 };
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 };
747
756
  }
748
757
 
749
758
  /**
package/dist/index.js CHANGED
@@ -1087,33 +1087,108 @@ function updateSceneField(data, episodeIndex, sceneIndex, field, value) {
1087
1087
  });
1088
1088
  });
1089
1089
  }
1090
- function updateSceneAssetRefState(data, episodeIndex, sceneIndex, field, idKey, targetId, stateId) {
1090
+ function updateSceneActorRefState(scene, actorId, stateId) {
1091
+ const context = getSceneContext(scene);
1092
+ let matched = false;
1093
+ let changed = false;
1094
+ const actors = (context.actors ?? []).map((item) => {
1095
+ if ((item.actor_id ?? "").trim() !== actorId) return item;
1096
+ matched = true;
1097
+ if ((item.state_id ?? null) === stateId) return item;
1098
+ changed = true;
1099
+ return { ...item, state_id: stateId };
1100
+ });
1101
+ if (!matched) {
1102
+ changed = true;
1103
+ actors.push({ actor_id: actorId, state_id: stateId });
1104
+ }
1105
+ return changed ? setSceneContext(scene, { actors }) : scene;
1106
+ }
1107
+ function updateSceneLocationRefState(scene, locationId, stateId) {
1108
+ const context = getSceneContext(scene);
1109
+ let matched = false;
1110
+ let changed = false;
1111
+ const locations = (context.locations ?? []).map((item) => {
1112
+ if ((item.location_id ?? "").trim() !== locationId) return item;
1113
+ matched = true;
1114
+ if ((item.state_id ?? null) === stateId) return item;
1115
+ changed = true;
1116
+ return { ...item, state_id: stateId };
1117
+ });
1118
+ if (!matched) {
1119
+ changed = true;
1120
+ locations.push({ location_id: locationId, state_id: stateId });
1121
+ }
1122
+ return changed ? setSceneContext(scene, { locations }) : scene;
1123
+ }
1124
+ function updateScenePropRefState(scene, propId, stateId) {
1125
+ const context = getSceneContext(scene);
1126
+ let matched = false;
1127
+ let changed = false;
1128
+ const props = (context.props ?? []).map((item) => {
1129
+ if ((item.prop_id ?? "").trim() !== propId) return item;
1130
+ matched = true;
1131
+ if ((item.state_id ?? null) === stateId) return item;
1132
+ changed = true;
1133
+ return { ...item, state_id: stateId };
1134
+ });
1135
+ if (!matched) {
1136
+ changed = true;
1137
+ props.push({ prop_id: propId, state_id: stateId });
1138
+ }
1139
+ return changed ? setSceneContext(scene, { props }) : scene;
1140
+ }
1141
+ function updateSceneAssetRefState(data, episodeIndex, sceneIndex, kind, targetId, stateId) {
1091
1142
  const normalizedTarget = targetId.trim();
1092
1143
  const normalizedState = stateId?.trim() || null;
1093
1144
  if (!normalizedTarget) return data;
1094
1145
  return withSceneUpdated(data, episodeIndex, sceneIndex, (scene) => {
1095
- const context = getSceneContext(scene);
1096
- const list = context[field] || [];
1097
- let matched = false;
1098
- const next = list.map((item) => {
1099
- if ((item[idKey] || "").toString().trim() !== normalizedTarget) return item;
1100
- matched = true;
1101
- return { ...item, state_id: normalizedState };
1102
- });
1103
- if (!matched) {
1104
- next.push({ [idKey]: normalizedTarget, state_id: normalizedState });
1105
- }
1106
- return setSceneContext(scene, { [field]: next });
1146
+ if (kind === "actor") return updateSceneActorRefState(scene, normalizedTarget, normalizedState);
1147
+ if (kind === "location") return updateSceneLocationRefState(scene, normalizedTarget, normalizedState);
1148
+ return updateScenePropRefState(scene, normalizedTarget, normalizedState);
1107
1149
  });
1108
1150
  }
1109
1151
  function updateSceneActorState(data, episodeIndex, sceneIndex, actorId, stateId) {
1110
- return updateSceneAssetRefState(data, episodeIndex, sceneIndex, "actors", "actor_id", actorId, stateId);
1152
+ return updateSceneAssetRefState(data, episodeIndex, sceneIndex, "actor", actorId, stateId);
1111
1153
  }
1112
1154
  function updateSceneLocationState(data, episodeIndex, sceneIndex, locationId, stateId) {
1113
- return updateSceneAssetRefState(data, episodeIndex, sceneIndex, "locations", "location_id", locationId, stateId);
1155
+ return updateSceneAssetRefState(data, episodeIndex, sceneIndex, "location", locationId, stateId);
1114
1156
  }
1115
1157
  function updateScenePropState(data, episodeIndex, sceneIndex, propId, stateId) {
1116
- return updateSceneAssetRefState(data, episodeIndex, sceneIndex, "props", "prop_id", propId, stateId);
1158
+ return updateSceneAssetRefState(data, episodeIndex, sceneIndex, "prop", propId, stateId);
1159
+ }
1160
+ function updateSceneAssetStateBindings(data, kind, assetId, targets, stateId) {
1161
+ const normalizedAssetId = assetId.trim();
1162
+ if (!normalizedAssetId || targets.length === 0) return data;
1163
+ const normalizedState = stateId?.trim() || null;
1164
+ const byEpisode = /* @__PURE__ */ new Map();
1165
+ for (const target of targets) {
1166
+ if (target.episodeIndex < 0 || target.sceneIndex < 0) continue;
1167
+ let scenes = byEpisode.get(target.episodeIndex);
1168
+ if (!scenes) {
1169
+ scenes = /* @__PURE__ */ new Set();
1170
+ byEpisode.set(target.episodeIndex, scenes);
1171
+ }
1172
+ scenes.add(target.sceneIndex);
1173
+ }
1174
+ if (byEpisode.size === 0) return data;
1175
+ let changed = false;
1176
+ const episodes = (data.episodes ?? []).map((episode, episodeIndex) => {
1177
+ const sceneIndexes = byEpisode.get(episodeIndex);
1178
+ if (!sceneIndexes) return episode;
1179
+ let episodeChanged = false;
1180
+ const scenes = episode.scenes?.map((scene, sceneIndex) => {
1181
+ if (!sceneIndexes.has(sceneIndex)) return scene;
1182
+ const nextScene = kind === "actor" ? updateSceneActorRefState(scene, normalizedAssetId, normalizedState) : kind === "location" ? updateSceneLocationRefState(scene, normalizedAssetId, normalizedState) : updateScenePropRefState(scene, normalizedAssetId, normalizedState);
1183
+ if (nextScene !== scene) {
1184
+ episodeChanged = true;
1185
+ changed = true;
1186
+ }
1187
+ return nextScene;
1188
+ });
1189
+ return scenes && episodeChanged ? { ...episode, scenes } : episode;
1190
+ });
1191
+ return changed ? { ...data, episodes } : data;
1117
1192
  }
1118
1193
  function updateSceneItemContent(data, episodeIndex, sceneIndex, itemIndex, content) {
1119
1194
  return withSceneUpdated(data, episodeIndex, sceneIndex, (scene) => {
@@ -2893,6 +2968,7 @@ function PreviewLine({
2893
2968
  ] });
2894
2969
  }
2895
2970
  var UNASSIGNED = "__unassigned__";
2971
+ var DRAG_THRESHOLD = 4;
2896
2972
  var CUE_COUNT = 8;
2897
2973
  var COL = 32;
2898
2974
  var MIN_EP_WIDTH = 48;
@@ -2902,9 +2978,20 @@ function AssetStateTimeline({
2902
2978
  assetKind,
2903
2979
  refs,
2904
2980
  states: states2,
2981
+ disabled = false,
2982
+ onBind,
2905
2983
  onJump
2906
2984
  }) {
2907
2985
  const [hover, setHover] = useState(null);
2986
+ const [selectedKeys, setSelectedKeys] = useState(() => /* @__PURE__ */ new Set());
2987
+ const [draggingKeys, setDraggingKeys] = useState(() => /* @__PURE__ */ new Set());
2988
+ const [dropLaneId, setDropLaneId] = useState(null);
2989
+ const [box, setBox] = useState(null);
2990
+ const [dragGhost, setDragGhost] = useState(null);
2991
+ const rootRef = useRef(null);
2992
+ const dragRef = useRef(null);
2993
+ const boxRef = useRef(null);
2994
+ const suppressClickRef = useRef(false);
2908
2995
  const closeTimer = useRef(null);
2909
2996
  const cancelClose = () => {
2910
2997
  if (closeTimer.current) {
@@ -2916,6 +3003,7 @@ function AssetStateTimeline({
2916
3003
  cancelClose();
2917
3004
  closeTimer.current = setTimeout(() => setHover(null), 140);
2918
3005
  };
3006
+ useEffect(() => () => cancelClose(), []);
2919
3007
  const cols = [];
2920
3008
  for (const group of refs) for (const item of group.items) cols.push(item);
2921
3009
  const n = cols.length;
@@ -2926,10 +3014,11 @@ function AssetStateTimeline({
2926
3014
  const sid = (c.stateId ?? "").trim();
2927
3015
  return !sid || !definedIds.includes(sid);
2928
3016
  };
2929
- if (cols.some(isUnassigned)) {
3017
+ if (cols.some(isUnassigned) || !disabled && lanes.length > 0) {
2930
3018
  lanes.push({ id: UNASSIGNED, name: lanes.length > 0 ? "\u672A\u6807\u6CE8" : "\u51FA\u73B0", cueIndex: 0 });
2931
3019
  }
2932
3020
  const colMetas = [];
3021
+ const itemByKey = /* @__PURE__ */ new Map();
2933
3022
  const epHeads = [];
2934
3023
  let x = 0;
2935
3024
  for (const group of refs) {
@@ -2938,82 +3027,251 @@ function AssetStateTimeline({
2938
3027
  epHeads.push({ episodeIndex: group.episodeIndex, episodeNumber: group.episodeNumber, width: count * w });
2939
3028
  for (const item of group.items) {
2940
3029
  colMetas.push({ item, left: x, width: w });
3030
+ itemByKey.set(item.key, item);
2941
3031
  x += w;
2942
3032
  }
2943
3033
  }
2944
3034
  const trackWidth = x;
3035
+ useEffect(() => {
3036
+ setSelectedKeys((prev) => {
3037
+ const next = new Set(Array.from(prev).filter((key) => itemByKey.has(key)));
3038
+ return next.size === prev.size ? prev : next;
3039
+ });
3040
+ }, [refs]);
2945
3041
  const center = (m) => m.left + m.width / 2;
2946
3042
  const cueVar = (cueIndex) => cueIndex === 0 ? "var(--lj-se-muted-fg)" : `var(--lj-se-actor-cue-${cueIndex})`;
2947
3043
  const laneMetas = (laneId) => colMetas.filter((m) => laneId === UNASSIGNED ? isUnassigned(m.item) : (m.item.stateId ?? "").trim() === laneId);
2948
- return /* @__PURE__ */ jsxs("div", { className: "lj-se-sttl", children: [
2949
- /* @__PURE__ */ jsxs("div", { className: "lj-se-sttl-axis", children: [
2950
- /* @__PURE__ */ jsx("span", { className: "lj-se-sttl-axis-lab", children: "\u72B6\u6001 \uFF3C \u573A" }),
2951
- /* @__PURE__ */ jsx("span", { className: "lj-se-sttl-axis-eps", style: { width: trackWidth }, children: epHeads.map((g) => /* @__PURE__ */ jsxs("span", { className: "lj-se-sttl-ep", style: { width: g.width }, title: `\u7B2C ${g.episodeNumber} \u96C6`, children: [
2952
- "\u7B2C",
2953
- String(g.episodeNumber).padStart(2, "0"),
2954
- "\u96C6"
2955
- ] }, g.episodeIndex)) })
2956
- ] }),
2957
- lanes.map((lane) => {
2958
- const mine = laneMetas(lane.id);
2959
- return /* @__PURE__ */ jsxs("div", { className: "lj-se-sttl-lane", children: [
2960
- /* @__PURE__ */ jsxs("span", { className: "lj-se-sttl-lane-head", title: lane.name, children: [
2961
- /* @__PURE__ */ jsx("span", { className: "lj-se-sttl-dot", style: { background: cueVar(lane.cueIndex) } }),
2962
- /* @__PURE__ */ jsx("span", { className: "lj-se-sttl-lane-name", children: lane.name }),
2963
- /* @__PURE__ */ jsx("span", { className: "lj-se-sttl-lane-count", children: mine.length })
3044
+ const laneIds = new Set(lanes.map((lane) => lane.id));
3045
+ const stateIdForLane = (laneId) => laneId === UNASSIGNED ? null : laneId;
3046
+ const targetsForKeys = (keys) => Array.from(keys).map((key) => itemByKey.get(key)).filter((item) => !!item).map((item) => ({ episodeIndex: item.episodeIndex, sceneIndex: item.sceneIndex }));
3047
+ const findLaneIdAt = (clientX, clientY) => {
3048
+ const lane = document.elementFromPoint(clientX, clientY)?.closest(".lj-se-sttl-lane");
3049
+ const laneId = lane?.dataset.sttlLaneId;
3050
+ return laneId && laneIds.has(laneId) ? laneId : null;
3051
+ };
3052
+ const selectKeysInBox = (nextBox) => {
3053
+ const left = Math.min(nextBox.startX, nextBox.currentX);
3054
+ const right = Math.max(nextBox.startX, nextBox.currentX);
3055
+ const top = Math.min(nextBox.startY, nextBox.currentY);
3056
+ const bottom = Math.max(nextBox.startY, nextBox.currentY);
3057
+ const next = /* @__PURE__ */ new Set();
3058
+ rootRef.current?.querySelectorAll("[data-sttl-node-key]").forEach((node) => {
3059
+ const r = node.getBoundingClientRect();
3060
+ const x2 = r.left + r.width / 2;
3061
+ const y = r.top + r.height / 2;
3062
+ if (x2 >= left && x2 <= right && y >= top && y <= bottom) {
3063
+ const key = node.dataset.sttlNodeKey;
3064
+ if (key) next.add(key);
3065
+ }
3066
+ });
3067
+ setSelectedKeys(next);
3068
+ };
3069
+ const boxStyle = () => {
3070
+ if (!box || !rootRef.current) return void 0;
3071
+ const r = rootRef.current.getBoundingClientRect();
3072
+ return {
3073
+ left: Math.min(box.startX, box.currentX) - r.left + rootRef.current.scrollLeft,
3074
+ top: Math.min(box.startY, box.currentY) - r.top + rootRef.current.scrollTop,
3075
+ width: Math.abs(box.currentX - box.startX),
3076
+ height: Math.abs(box.currentY - box.startY)
3077
+ };
3078
+ };
3079
+ const clearPointerState = (pointerId) => {
3080
+ if (rootRef.current?.hasPointerCapture(pointerId)) rootRef.current.releasePointerCapture(pointerId);
3081
+ dragRef.current = null;
3082
+ boxRef.current = null;
3083
+ setDraggingKeys(/* @__PURE__ */ new Set());
3084
+ setDropLaneId(null);
3085
+ setBox(null);
3086
+ setDragGhost(null);
3087
+ };
3088
+ const handleNodePointerDown = (e, key) => {
3089
+ if (disabled || e.button !== 0) return;
3090
+ e.stopPropagation();
3091
+ cancelClose();
3092
+ setHover(null);
3093
+ const keys = selectedKeys.has(key) ? new Set(selectedKeys) : /* @__PURE__ */ new Set([key]);
3094
+ setSelectedKeys(keys);
3095
+ dragRef.current = {
3096
+ pointerId: e.pointerId,
3097
+ startX: e.clientX,
3098
+ startY: e.clientY,
3099
+ keys,
3100
+ dragging: false
3101
+ };
3102
+ rootRef.current?.setPointerCapture(e.pointerId);
3103
+ };
3104
+ const dragLabel = (keys) => {
3105
+ if (keys.size !== 1) return `${keys.size} \u573A`;
3106
+ const item = itemByKey.get(Array.from(keys)[0]);
3107
+ return item ? String(item.sceneNumber) : "1 \u573A";
3108
+ };
3109
+ const handleTrackPointerDown = (e) => {
3110
+ if (disabled || e.button !== 0 || e.target instanceof HTMLElement && e.target.closest("[data-sttl-node-key]")) return;
3111
+ const next = {
3112
+ pointerId: e.pointerId,
3113
+ startX: e.clientX,
3114
+ startY: e.clientY,
3115
+ currentX: e.clientX,
3116
+ currentY: e.clientY,
3117
+ moved: false
3118
+ };
3119
+ setSelectedKeys(/* @__PURE__ */ new Set());
3120
+ boxRef.current = next;
3121
+ setBox(next);
3122
+ rootRef.current?.setPointerCapture(e.pointerId);
3123
+ };
3124
+ const handlePointerMove = (e) => {
3125
+ const drag = dragRef.current;
3126
+ if (drag?.pointerId === e.pointerId) {
3127
+ const moved2 = Math.hypot(e.clientX - drag.startX, e.clientY - drag.startY) >= DRAG_THRESHOLD;
3128
+ if (!drag.dragging && moved2) {
3129
+ drag.dragging = true;
3130
+ setDraggingKeys(new Set(drag.keys));
3131
+ }
3132
+ if (drag.dragging) {
3133
+ e.preventDefault();
3134
+ setDragGhost({ x: e.clientX, y: e.clientY, label: dragLabel(drag.keys) });
3135
+ setDropLaneId(findLaneIdAt(e.clientX, e.clientY));
3136
+ }
3137
+ return;
3138
+ }
3139
+ const nextBox = boxRef.current;
3140
+ if (nextBox?.pointerId !== e.pointerId) return;
3141
+ const moved = nextBox.moved || Math.hypot(e.clientX - nextBox.startX, e.clientY - nextBox.startY) >= DRAG_THRESHOLD;
3142
+ const updated = { ...nextBox, currentX: e.clientX, currentY: e.clientY, moved };
3143
+ boxRef.current = updated;
3144
+ setBox(updated);
3145
+ if (updated.moved) selectKeysInBox(updated);
3146
+ };
3147
+ const handlePointerUp = (e) => {
3148
+ const drag = dragRef.current;
3149
+ if (drag?.pointerId === e.pointerId) {
3150
+ if (drag.dragging) {
3151
+ const laneId = dropLaneId ?? findLaneIdAt(e.clientX, e.clientY);
3152
+ if (laneId) {
3153
+ const targets = targetsForKeys(drag.keys);
3154
+ if (targets.length > 0) onBind(targets, stateIdForLane(laneId));
3155
+ }
3156
+ suppressClickRef.current = true;
3157
+ window.setTimeout(() => {
3158
+ suppressClickRef.current = false;
3159
+ }, 0);
3160
+ e.preventDefault();
3161
+ }
3162
+ clearPointerState(e.pointerId);
3163
+ return;
3164
+ }
3165
+ const nextBox = boxRef.current;
3166
+ if (nextBox?.pointerId !== e.pointerId) return;
3167
+ if (nextBox.moved) selectKeysInBox(nextBox);
3168
+ else setSelectedKeys(/* @__PURE__ */ new Set());
3169
+ clearPointerState(e.pointerId);
3170
+ };
3171
+ return /* @__PURE__ */ jsxs(
3172
+ "div",
3173
+ {
3174
+ ref: rootRef,
3175
+ className: cn("lj-se-sttl", !disabled && "is-editable", draggingKeys.size > 0 && "is-dragging"),
3176
+ onPointerMove: handlePointerMove,
3177
+ onPointerUp: handlePointerUp,
3178
+ onPointerCancel: (e) => clearPointerState(e.pointerId),
3179
+ children: [
3180
+ /* @__PURE__ */ jsxs("div", { className: "lj-se-sttl-axis", children: [
3181
+ /* @__PURE__ */ jsx("span", { className: "lj-se-sttl-axis-lab", children: "\u72B6\u6001 \uFF3C \u573A" }),
3182
+ /* @__PURE__ */ jsx("span", { className: "lj-se-sttl-axis-eps", style: { width: trackWidth }, children: epHeads.map((g) => /* @__PURE__ */ jsxs("span", { className: "lj-se-sttl-ep", style: { width: g.width }, title: `\u7B2C ${g.episodeNumber} \u96C6`, children: [
3183
+ "\u7B2C",
3184
+ String(g.episodeNumber).padStart(2, "0"),
3185
+ "\u96C6"
3186
+ ] }, g.episodeIndex)) })
2964
3187
  ] }),
2965
- /* @__PURE__ */ jsxs("span", { className: "lj-se-sttl-track", style: { width: trackWidth, "--cue": cueVar(lane.cueIndex) }, children: [
2966
- colMetas.map((m, i) => /* @__PURE__ */ jsx("span", { className: "lj-se-sttl-col", style: { left: m.left } }, `col-${i}`)),
2967
- mine.map((m, k) => {
2968
- const next = mine[k + 1];
2969
- if (!next) return null;
2970
- return /* @__PURE__ */ jsx(
3188
+ lanes.map((lane) => {
3189
+ const mine = laneMetas(lane.id);
3190
+ return /* @__PURE__ */ jsxs("div", { className: cn("lj-se-sttl-lane", dropLaneId === lane.id && "is-drop"), "data-sttl-lane-id": lane.id, children: [
3191
+ /* @__PURE__ */ jsxs("span", { className: "lj-se-sttl-lane-head", title: lane.name, children: [
3192
+ /* @__PURE__ */ jsx("span", { className: "lj-se-sttl-dot", style: { background: cueVar(lane.cueIndex) } }),
3193
+ /* @__PURE__ */ jsx("span", { className: "lj-se-sttl-lane-name", children: lane.name }),
3194
+ /* @__PURE__ */ jsx("span", { className: "lj-se-sttl-lane-count", children: mine.length })
3195
+ ] }),
3196
+ /* @__PURE__ */ jsxs(
2971
3197
  "span",
2972
3198
  {
2973
- className: "lj-se-sttl-flow",
2974
- style: { left: center(m), width: center(next) - center(m) }
2975
- },
2976
- `flow-${m.item.key}`
2977
- );
2978
- }),
2979
- mine.map((m) => /* @__PURE__ */ jsx(
2980
- "button",
2981
- {
2982
- type: "button",
2983
- className: "lj-se-sttl-node",
2984
- style: { left: center(m) },
2985
- title: m.item.label,
2986
- onClick: () => onJump(m.item.episodeIndex, m.item.sceneIndex),
2987
- onMouseEnter: (e) => {
2988
- cancelClose();
2989
- const r = e.currentTarget.getBoundingClientRect();
2990
- setHover({
2991
- episodeIndex: m.item.episodeIndex,
2992
- sceneIndex: m.item.sceneIndex,
2993
- rect: { top: r.top, bottom: r.bottom, left: r.left, width: r.width },
2994
- host: e.currentTarget.closest(".lj-se-root")
2995
- });
2996
- },
2997
- onMouseLeave: scheduleClose,
2998
- children: m.item.sceneNumber
2999
- },
3000
- m.item.key
3001
- ))
3002
- ] })
3003
- ] }, lane.id);
3004
- }),
3005
- hover ? /* @__PURE__ */ jsx(
3006
- ScenePreviewCard,
3007
- {
3008
- data,
3009
- anchor: hover,
3010
- highlightId: assetId,
3011
- highlightKind: assetKind,
3012
- onEnter: cancelClose,
3013
- onLeave: scheduleClose
3014
- }
3015
- ) : null
3016
- ] });
3199
+ className: "lj-se-sttl-track",
3200
+ style: { width: trackWidth, "--cue": cueVar(lane.cueIndex) },
3201
+ onPointerDown: handleTrackPointerDown,
3202
+ children: [
3203
+ colMetas.map((m, i) => /* @__PURE__ */ jsx("span", { className: "lj-se-sttl-col", style: { left: m.left } }, `col-${i}`)),
3204
+ mine.map((m, k) => {
3205
+ const next = mine[k + 1];
3206
+ if (!next) return null;
3207
+ return /* @__PURE__ */ jsx(
3208
+ "span",
3209
+ {
3210
+ className: "lj-se-sttl-flow",
3211
+ style: { left: center(m), width: center(next) - center(m) }
3212
+ },
3213
+ `flow-${m.item.key}`
3214
+ );
3215
+ }),
3216
+ mine.map((m) => /* @__PURE__ */ jsx(
3217
+ "button",
3218
+ {
3219
+ type: "button",
3220
+ className: cn(
3221
+ "lj-se-sttl-node",
3222
+ selectedKeys.has(m.item.key) && "is-selected",
3223
+ draggingKeys.has(m.item.key) && "is-dragging"
3224
+ ),
3225
+ "data-sttl-node-key": m.item.key,
3226
+ "aria-pressed": selectedKeys.has(m.item.key),
3227
+ style: { left: center(m) },
3228
+ title: m.item.label,
3229
+ onPointerDown: (e) => handleNodePointerDown(e, m.item.key),
3230
+ onClick: (e) => {
3231
+ if (suppressClickRef.current) {
3232
+ suppressClickRef.current = false;
3233
+ e.preventDefault();
3234
+ return;
3235
+ }
3236
+ onJump(m.item.episodeIndex, m.item.sceneIndex);
3237
+ },
3238
+ onMouseEnter: (e) => {
3239
+ if (dragRef.current?.dragging) return;
3240
+ cancelClose();
3241
+ const r = e.currentTarget.getBoundingClientRect();
3242
+ setHover({
3243
+ episodeIndex: m.item.episodeIndex,
3244
+ sceneIndex: m.item.sceneIndex,
3245
+ rect: { top: r.top, bottom: r.bottom, left: r.left, width: r.width },
3246
+ host: e.currentTarget.closest(".lj-se-root")
3247
+ });
3248
+ },
3249
+ onMouseLeave: scheduleClose,
3250
+ children: m.item.sceneNumber
3251
+ },
3252
+ m.item.key
3253
+ ))
3254
+ ]
3255
+ }
3256
+ )
3257
+ ] }, lane.id);
3258
+ }),
3259
+ box ? /* @__PURE__ */ jsx("span", { className: "lj-se-sttl-select-box", style: boxStyle() }) : null,
3260
+ dragGhost ? /* @__PURE__ */ jsx("span", { className: "lj-se-sttl-drag-ghost", style: { left: dragGhost.x, top: dragGhost.y }, children: dragGhost.label }) : null,
3261
+ hover ? /* @__PURE__ */ jsx(
3262
+ ScenePreviewCard,
3263
+ {
3264
+ data,
3265
+ anchor: hover,
3266
+ highlightId: assetId,
3267
+ highlightKind: assetKind,
3268
+ onEnter: cancelClose,
3269
+ onLeave: scheduleClose
3270
+ }
3271
+ ) : null
3272
+ ]
3273
+ }
3274
+ );
3017
3275
  }
3018
3276
  var META = {
3019
3277
  actors: { kind: "actor" },
@@ -3194,6 +3452,8 @@ function AssetCardImpl({
3194
3452
  assetKind: kind,
3195
3453
  refs,
3196
3454
  states: states2,
3455
+ disabled,
3456
+ onBind: (targets, stateId) => onEdit((p) => updateSceneAssetStateBindings(p, kind, assetId, targets, stateId)),
3197
3457
  onJump: (episodeIdx, sceneIdx) => onSelect({ kind: "scene", episodeIdx, sceneIdx, from: fromRef, origin: "sidebar" })
3198
3458
  }
3199
3459
  )
@@ -4522,6 +4782,7 @@ __export(mutations_exports, {
4522
4782
  updateEntityName: () => updateEntityName,
4523
4783
  updateEntityStateField: () => updateEntityStateField,
4524
4784
  updateSceneActorState: () => updateSceneActorState,
4785
+ updateSceneAssetStateBindings: () => updateSceneAssetStateBindings,
4525
4786
  updateSceneField: () => updateSceneField,
4526
4787
  updateSceneItemActor: () => updateSceneItemActor,
4527
4788
  updateSceneItemContent: () => updateSceneItemContent,