@lingjingai/script-editor 0.3.1 → 0.4.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
@@ -265,6 +265,15 @@ declare function getSceneContext(scene: Scene): {
265
265
  state_id?: string | null;
266
266
  }[];
267
267
  };
268
+ /**
269
+ * Count how many scenes each asset appears in (its scene-cast / location / prop
270
+ * refs), in one pass. Drives the TOC's "出现次数" sort. v3-aware via getSceneContext.
271
+ */
272
+ declare function countAssetAppearances(data: ScriptProject): {
273
+ actors: Map<string, number>;
274
+ locations: Map<string, number>;
275
+ props: Map<string, number>;
276
+ };
268
277
  declare function getEpisodeTitle(episode: ScriptEpisode, index: number): string;
269
278
 
270
279
  interface SelectOption {
@@ -289,6 +298,27 @@ type ScriptFormat = "standard" | "asian";
289
298
 
290
299
  type SaveStatus = "idle" | "saving" | "saved" | "error";
291
300
 
301
+ /**
302
+ * Flattens a ScriptProject into ONE continuous, stable-keyed item list for the
303
+ * virtualized stream — mirroring agent-frontend's single global scroll:
304
+ * each episode (episode-header → scene → scene → …)
305
+ * → asset groups (角色 / 场景 / 道具: group-header → asset-card × N)
306
+ * Plus selection ↔ flatIndex lookups (two-way) for scroll/scrollspy sync.
307
+ * (世界观 & 风格 is edited in a dialog now, not inline — see ScriptEditor.)
308
+ */
309
+
310
+ type AssetGroup = "actors" | "locations" | "props";
311
+ /** An asset as seen by `assetSort` — enough to reproduce any TOC ordering
312
+ * (by name / importance / appearance count) host-side. */
313
+ interface AssetSortItem {
314
+ group: AssetGroup;
315
+ id: string;
316
+ name: string;
317
+ importance?: string;
318
+ /** appearance count (scenes referencing this asset). */
319
+ count: number;
320
+ }
321
+
292
322
  /**
293
323
  * Reference focus — the INVERSE of "quote to chat". The host pushes a
294
324
  * ScriptReferenceFocusRequest (e.g. when a chat message / comment that quotes
@@ -576,6 +606,10 @@ interface ScriptEditorProps extends ScriptEditorAdapters {
576
606
  sceneId: string;
577
607
  actionIndex: number;
578
608
  }) => void;
609
+ /** order assets within each group in the preview (角色/场景/道具). Feed the SAME
610
+ * comparator your TOC uses so the left nav and right preview stay in lockstep.
611
+ * Omit → script.json array order. */
612
+ assetSort?: (a: AssetSortItem, b: AssetSortItem) => number;
579
613
  }
580
614
  /** A host range highlight targeting one action (same coords as DiffActionMark). */
581
615
  interface ScriptHighlight {
@@ -587,7 +621,7 @@ interface ScriptHighlight {
587
621
  className?: string;
588
622
  style?: React.CSSProperties;
589
623
  }
590
- declare function ScriptEditor({ value, onEdit: onEditProp, selection: controlledSelection, onSelectionChange, format: controlledFormat, onFormatChange, disabled, className, dark, toc, onTitleChange, topBarActions, saveStatus, canUndo, canRedo, onUndo, onRedo, onEditingChange, referenceFocusRequest, scrollScopeKey, showAssets, showTitle, showFormatToggle, paper, showTimestamp, highlights, contentWidth, showSaveCapsule, onActionHover, onActionClick, diff, asset, onAddChatReferences, onRequestAssetDelete, }: ScriptEditorProps): react.JSX.Element;
624
+ declare function ScriptEditor({ value, onEdit: onEditProp, selection: controlledSelection, onSelectionChange, format: controlledFormat, onFormatChange, disabled, className, dark, toc, onTitleChange, topBarActions, saveStatus, canUndo, canRedo, onUndo, onRedo, onEditingChange, referenceFocusRequest, scrollScopeKey, showAssets, showTitle, showFormatToggle, paper, showTimestamp, highlights, contentWidth, showSaveCapsule, onActionHover, onActionClick, assetSort, diff, asset, onAddChatReferences, onRequestAssetDelete, }: ScriptEditorProps): react.JSX.Element;
591
625
 
592
626
  /**
593
627
  * Action timestamps live at `action.extend.timestamp` as an in-episode
@@ -898,4 +932,4 @@ declare function DeleteConfirmButton({ onConfirm, disabled, targetScope, title,
898
932
  description?: string;
899
933
  }): react.JSX.Element;
900
934
 
901
- 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, type ParsedTimestamp, 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 ScriptHighlight, 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, formatTimestamp, fromToSelection, getActorCueVar, getActorMap, getEpisodeTitle, getLocationMap, getPropMap, getSceneContext, getSelectionEpisodeIdx, getSpeakerMap, isClipSelected, isEpisodeOpen, isSameSelectionTarget, isSceneSelected, index as mutations, rejectActionToBaseline, rejectSceneToBaseline, sceneChangeKey, selectionFromScriptReference, selectionTargetKey, withOrigin };
935
+ export { type AssetAdapter, type AssetDeleteRequest, type AssetSortItem, type AssetState, type ChangeSet, type ChatReference, DeleteConfirmButton, type DiffActionMark, type DiffAdapter, type DiffSceneMark, EMPTY_SELECTION, EditableText, type EpisodeWorkspaceView, type FieldChange, type Importance, type ParsedTimestamp, 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 ScriptHighlight, 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, countAssetAppearances, findBaselineScene, formatTimestamp, 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
@@ -618,6 +618,29 @@ function StudioToc({
618
618
  )
619
619
  ] });
620
620
  }
621
+ function TocLabel({ text, className }) {
622
+ const ref = useRef(null);
623
+ const [tip, setTip] = useState(null);
624
+ const onEnter = () => {
625
+ const el = ref.current;
626
+ if (!el || el.scrollWidth <= el.clientWidth) return;
627
+ const r = el.getBoundingClientRect();
628
+ setTip({ x: r.right, y: r.top + r.height / 2 });
629
+ };
630
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
631
+ /* @__PURE__ */ jsx(
632
+ "span",
633
+ {
634
+ ref,
635
+ className,
636
+ onMouseEnter: onEnter,
637
+ onMouseLeave: () => setTip(null),
638
+ children: text
639
+ }
640
+ ),
641
+ tip ? /* @__PURE__ */ jsx("span", { className: "lj-se-toc-tip", style: { left: tip.x, top: tip.y }, role: "tooltip", children: text }) : null
642
+ ] });
643
+ }
621
644
  function EntityGroup({
622
645
  icon: Icon,
623
646
  label,
@@ -641,7 +664,7 @@ function EntityGroup({
641
664
  onClick: () => onPick(i.id),
642
665
  children: [
643
666
  /* @__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 }),
667
+ /* @__PURE__ */ jsx(TocLabel, { className: "lj-se-tocitem-label", text: i.name }),
645
668
  i.count > 0 ? /* @__PURE__ */ jsx("span", { className: "lj-se-tocitem-meta", children: i.count }) : null
646
669
  ]
647
670
  },
@@ -2912,6 +2935,20 @@ function buildFlatStream(data, opts = {}) {
2912
2935
  { group: "locations", ids: (data.locations ?? []).map((l, i) => safeId(l.location_id, `__idx:${i}`)) },
2913
2936
  { group: "props", ids: (data.props ?? []).map((p, i) => safeId(p.prop_id, `__idx:${i}`)) }
2914
2937
  ];
2938
+ if (opts.assetSort) {
2939
+ const counts = countAssetAppearances(data);
2940
+ const actorMap = getActorMap(data.actors ?? []);
2941
+ const locationMap = getLocationMap(data.locations ?? []);
2942
+ const propMap = getPropMap(data.props ?? []);
2943
+ const enrich = (group, id) => {
2944
+ if (group === "actors") return { group, id, name: getActorDisplayName(actorMap.get(id)) || id, importance: actorMap.get(id)?.importance, count: counts.actors.get(id) ?? 0 };
2945
+ if (group === "locations") return { group, id, name: getLocationDisplayName(locationMap.get(id)) || id, importance: locationMap.get(id)?.importance, count: counts.locations.get(id) ?? 0 };
2946
+ return { group, id, name: getPropDisplayName(propMap.get(id)) || id, importance: propMap.get(id)?.importance, count: counts.props.get(id) ?? 0 };
2947
+ };
2948
+ for (const spec of specs) {
2949
+ spec.ids = spec.ids.map((id) => enrich(spec.group, id)).sort(opts.assetSort).map((it) => it.id);
2950
+ }
2951
+ }
2915
2952
  for (const { group, ids } of specs) {
2916
2953
  push({ key: `asset-group:${group}`, kind: "asset-group-header", group });
2917
2954
  ids.forEach((assetId) => push({ key: `asset:${group}:${assetId}`, kind: "asset-card", group, assetId }));
@@ -3659,6 +3696,7 @@ function ScriptStream({
3659
3696
  onRejectAction,
3660
3697
  scrollScopeKey,
3661
3698
  includeAssets = true,
3699
+ assetSort,
3662
3700
  paper = true,
3663
3701
  showTimestamp = false,
3664
3702
  highlights,
@@ -3668,7 +3706,7 @@ function ScriptStream({
3668
3706
  onSelectionChange,
3669
3707
  onRequestAssetDelete
3670
3708
  }) {
3671
- const stream = useMemo(() => buildFlatStream(value, { includeAssets }), [value, includeAssets]);
3709
+ const stream = useMemo(() => buildFlatStream(value, { includeAssets, assetSort }), [value, includeAssets, assetSort]);
3672
3710
  const highlightsByScene = useMemo(() => {
3673
3711
  const m = /* @__PURE__ */ new Map();
3674
3712
  for (const h of highlights ?? []) {
@@ -4640,6 +4678,7 @@ function ScriptEditor({
4640
4678
  showSaveCapsule = true,
4641
4679
  onActionHover,
4642
4680
  onActionClick,
4681
+ assetSort,
4643
4682
  diff,
4644
4683
  asset,
4645
4684
  onAddChatReferences,
@@ -4801,6 +4840,7 @@ function ScriptEditor({
4801
4840
  onRejectAction: diff?.onRejectAction,
4802
4841
  scrollScopeKey,
4803
4842
  includeAssets: showAssets,
4843
+ assetSort,
4804
4844
  paper,
4805
4845
  showTimestamp,
4806
4846
  highlights: streamHighlights,
@@ -5077,6 +5117,6 @@ function DeleteConfirmButton({
5077
5117
  );
5078
5118
  }
5079
5119
 
5080
- export { DeleteConfirmButton, EMPTY_SELECTION, EditableText, Popover, ScriptContentType, ScriptEditor, SelectField, acceptActionIntoBaseline, acceptSceneIntoBaseline, applyActionAccept, applyActionReject, buildActionDiff, buildDiffFromBaseline, buildScriptNav, buildSelectionFrom, changeSetToDiffScenes, computeChangeSet, computeSceneActionDiff, findBaselineScene, formatTimestamp, fromToSelection, getActorCueVar, getActorMap, getEpisodeTitle, getLocationMap, getPropMap, getSceneContext, getSelectionEpisodeIdx, getSpeakerMap, isClipSelected, isEpisodeOpen, isSameSelectionTarget, isSceneSelected, mutations_exports as mutations, rejectActionToBaseline, rejectSceneToBaseline, sceneChangeKey, selectionFromScriptReference, selectionTargetKey, withOrigin };
5120
+ export { DeleteConfirmButton, EMPTY_SELECTION, EditableText, Popover, ScriptContentType, ScriptEditor, SelectField, acceptActionIntoBaseline, acceptSceneIntoBaseline, applyActionAccept, applyActionReject, buildActionDiff, buildDiffFromBaseline, buildScriptNav, buildSelectionFrom, changeSetToDiffScenes, computeChangeSet, computeSceneActionDiff, countAssetAppearances, findBaselineScene, formatTimestamp, fromToSelection, getActorCueVar, getActorMap, getEpisodeTitle, getLocationMap, getPropMap, getSceneContext, getSelectionEpisodeIdx, getSpeakerMap, isClipSelected, isEpisodeOpen, isSameSelectionTarget, isSceneSelected, mutations_exports as mutations, rejectActionToBaseline, rejectSceneToBaseline, sceneChangeKey, selectionFromScriptReference, selectionTargetKey, withOrigin };
5081
5121
  //# sourceMappingURL=index.js.map
5082
5122
  //# sourceMappingURL=index.js.map