@lingjingai/script-editor 0.2.1 → 0.3.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
@@ -37,6 +37,11 @@ interface ScriptItem {
37
37
  emotion?: string;
38
38
  state_changes?: StateChange[];
39
39
  transition_prompt?: TransitionPrompt;
40
+ /** v3 free-form namespace; `extend.timestamp` is the in-episode "M:SS.mmm". */
41
+ extend?: {
42
+ timestamp?: string;
43
+ [key: string]: unknown;
44
+ };
40
45
  }
41
46
  interface AssetState {
42
47
  state_id?: string;
@@ -500,7 +505,9 @@ interface ScriptEditorAdapters {
500
505
 
501
506
  interface ScriptEditorProps extends ScriptEditorAdapters {
502
507
  value: ScriptProject;
503
- onEdit: (patch: (prev: ScriptProject) => ScriptProject) => void;
508
+ /** the (prev)=>next patch sink. Optional — omit for a pure read-only view
509
+ * (pair with `disabled`); edits become no-ops. */
510
+ onEdit?: (patch: (prev: ScriptProject) => ScriptProject) => void;
504
511
  selection?: StudioSelection;
505
512
  onSelectionChange?: (next: StudioSelection) => void;
506
513
  format?: ScriptFormat;
@@ -537,8 +544,56 @@ interface ScriptEditorProps extends ScriptEditorAdapters {
537
544
  /** stable per-scope key (e.g. projectGroupNo) — remembers & restores the
538
545
  * scroll position across editor unmount/remount (tab switch / project nav). */
539
546
  scrollScopeKey?: string;
547
+ /** render the asset-card groups (角色/场景/道具) below the script. Default true. */
548
+ showAssets?: boolean;
549
+ /** render the top bar's script title. Default true. */
550
+ showTitle?: boolean;
551
+ /** render the 标准/亚洲 format toggle button. Default true (format stays controlled). */
552
+ showFormatToggle?: boolean;
553
+ /** parchment paper surface. Default true; false → flat plain segments. */
554
+ paper?: boolean;
555
+ /** render a MM:SS gutter from each action's extend.timestamp. Default false. */
556
+ showTimestamp?: boolean;
557
+ /** host range highlights (per action), rendered on top of the row. */
558
+ highlights?: ScriptHighlight[];
559
+ /** content column width (script segments + asset cards). Number → px; string
560
+ * passes through as-is (e.g. "100%" to fill, "60rem"). Default 820px. Sets
561
+ * the --lj-se-content-width CSS var; hosts can also override that var in CSS. */
562
+ contentWidth?: number | string;
563
+ /** the floating bottom-center capsule (save status + AI-diff + 返回 breadcrumb).
564
+ * Default true. Set false for a pure display surface with no floating chrome. */
565
+ showSaveCapsule?: boolean;
566
+ }
567
+ /** A host range highlight targeting one action (same coords as DiffActionMark). */
568
+ interface ScriptHighlight {
569
+ target: {
570
+ episodeIdx: number;
571
+ sceneId: string;
572
+ actionIndex: number;
573
+ };
574
+ className?: string;
575
+ style?: React.CSSProperties;
540
576
  }
541
- declare function ScriptEditor({ value, onEdit, selection: controlledSelection, onSelectionChange, format: controlledFormat, onFormatChange, disabled, className, dark, toc, onTitleChange, topBarActions, saveStatus, canUndo, canRedo, onUndo, onRedo, onEditingChange, referenceFocusRequest, scrollScopeKey, diff, asset, onAddChatReferences, onRequestAssetDelete, }: ScriptEditorProps): react.JSX.Element;
577
+ 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, diff, asset, onAddChatReferences, onRequestAssetDelete, }: ScriptEditorProps): react.JSX.Element;
578
+
579
+ /**
580
+ * Action timestamps live at `action.extend.timestamp` as an in-episode
581
+ * "M:SS.mmm" string (e.g. "00:03.333", "01:12.000"). The editor shows the
582
+ * "MM:SS" part prominently and the ".mmm" fraction de-emphasized.
583
+ *
584
+ * Parsing is lenient: anything that isn't a well-formed timestamp returns null
585
+ * so the caller simply renders no time gutter for that row (never a placeholder).
586
+ */
587
+ interface ParsedTimestamp {
588
+ /** minutes:seconds, zero-padded — the prominent part, e.g. "01:07". */
589
+ mmss: string;
590
+ /** the fractional ".mmm" suffix incl. the dot, or "" when there is none. */
591
+ ms: string;
592
+ /** full normalized "MM:SS.mmm" (or "MM:SS" when no fraction) for title/hover. */
593
+ full: string;
594
+ }
595
+ /** Parse "M:SS.mmm" → { mmss, ms, full }, or null when malformed / empty. */
596
+ declare function formatTimestamp(raw: unknown): ParsedTimestamp | null;
542
597
 
543
598
  /**
544
599
  * Headless navigation model — surfaces the script's TOC tree (the same
@@ -830,4 +885,4 @@ declare function DeleteConfirmButton({ onConfirm, disabled, targetScope, title,
830
885
  description?: string;
831
886
  }): react.JSX.Element;
832
887
 
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 };
888
+ 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 };
package/dist/index.js CHANGED
@@ -633,11 +633,11 @@ function EntityGroup({
633
633
  const valid = items.filter((i) => i.id);
634
634
  const featured = valid.filter((i) => i.importance !== "background").sort(byCount);
635
635
  const background = valid.filter((i) => i.importance === "background").sort(byCount);
636
- const row = (i) => /* @__PURE__ */ jsxs(
636
+ const row = (i, bg = false) => /* @__PURE__ */ jsxs(
637
637
  "button",
638
638
  {
639
639
  type: "button",
640
- className: cn("lj-se-tocitem lj-se-toc-l1", isActive(i.id) && "is-active"),
640
+ className: cn("lj-se-tocitem", bg ? "lj-se-toc-bg" : "lj-se-toc-l1", isActive(i.id) && "is-active"),
641
641
  onClick: () => onPick(i.id),
642
642
  children: [
643
643
  /* @__PURE__ */ jsx(Icon, { className: "lj-se-tocitem-icon lj-se-ic-3", strokeWidth: 1.5 }),
@@ -648,9 +648,9 @@ function EntityGroup({
648
648
  i.id
649
649
  );
650
650
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [
651
- /* @__PURE__ */ jsx(GroupHeader, { icon: Icon, label, count: valid.length, open, highlighted, onToggle: () => setOpen((v) => !v) }),
651
+ /* @__PURE__ */ jsx(GroupHeader, { icon: Icon, label, count: featured.length, open, highlighted, onToggle: () => setOpen((v) => !v) }),
652
652
  open ? valid.length === 0 ? /* @__PURE__ */ jsx("div", { className: "lj-se-toc-empty", children: emptyText }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
653
- featured.map(row),
653
+ featured.map((i) => row(i)),
654
654
  background.length > 0 ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
655
655
  /* @__PURE__ */ jsxs("button", { type: "button", className: cn("lj-se-toc-fold", bgOpen && "is-open"), onClick: () => setBgOpen((v) => !v), children: [
656
656
  /* @__PURE__ */ jsx(ChevronRight, { className: "lj-se-toc-fold-chev lj-se-ic-3", strokeWidth: 2 }),
@@ -661,7 +661,7 @@ function EntityGroup({
661
661
  background.length
662
662
  ] })
663
663
  ] }),
664
- bgOpen ? background.map(row) : null
664
+ bgOpen ? background.map((i) => row(i, true)) : null
665
665
  ] }) : null
666
666
  ] }) : null
667
667
  ] });
@@ -887,6 +887,7 @@ function CanvasTopBar({
887
887
  contextLabel,
888
888
  format,
889
889
  onFormatChange,
890
+ showFormatToggle = true,
890
891
  canUndo,
891
892
  canRedo,
892
893
  onUndo,
@@ -912,7 +913,7 @@ function CanvasTopBar({
912
913
  ] }) : null
913
914
  ] }),
914
915
  /* @__PURE__ */ jsxs("div", { className: "lj-se-topbar-right", children: [
915
- /* @__PURE__ */ jsx(FormatToggle, { value: format, onChange: onFormatChange }),
916
+ showFormatToggle ? /* @__PURE__ */ jsx(FormatToggle, { value: format, onChange: onFormatChange }) : null,
916
917
  onUndo || onRedo ? /* @__PURE__ */ jsxs("div", { className: "lj-se-topbar-undo", children: [
917
918
  /* @__PURE__ */ jsx("button", { type: "button", className: "lj-se-rowbtn", disabled: !canUndo, title: "\u64A4\u9500", onClick: onUndo, children: /* @__PURE__ */ jsx(Undo2, { className: "lj-se-ic-4", strokeWidth: 1.75 }) }),
918
919
  /* @__PURE__ */ jsx("button", { type: "button", className: "lj-se-rowbtn", disabled: !canRedo, title: "\u91CD\u505A", onClick: onRedo, children: /* @__PURE__ */ jsx(Redo2, { className: "lj-se-ic-4", strokeWidth: 1.75 }) })
@@ -933,7 +934,17 @@ function SaveCapsule({
933
934
  returnLabel,
934
935
  onReturn
935
936
  }) {
936
- const showSave = saveStatus !== "idle";
937
+ const [savedExpired, setSavedExpired] = useState(false);
938
+ useEffect(() => {
939
+ if (saveStatus !== "saved") {
940
+ setSavedExpired(false);
941
+ return;
942
+ }
943
+ setSavedExpired(false);
944
+ const t = setTimeout(() => setSavedExpired(true), 1600);
945
+ return () => clearTimeout(t);
946
+ }, [saveStatus]);
947
+ const showSave = saveStatus !== "idle" && !(saveStatus === "saved" && savedExpired);
937
948
  const hasDiff = diffCount > 0;
938
949
  const hasReturn = !!(returnLabel && onReturn);
939
950
  if (!showSave && !hasDiff && !hasReturn) return null;
@@ -1861,6 +1872,21 @@ function useDragReorder() {
1861
1872
  return useContext(DragReorderContext);
1862
1873
  }
1863
1874
 
1875
+ // src/timestamp.ts
1876
+ var TS_RE = /^\s*(\d+):([0-5]?\d)(?:\.(\d{1,3}))?\s*$/;
1877
+ function formatTimestamp(raw) {
1878
+ if (typeof raw !== "string") return null;
1879
+ const m = TS_RE.exec(raw);
1880
+ if (!m) return null;
1881
+ const min = Number(m[1]);
1882
+ const sec = Number(m[2]);
1883
+ if (!Number.isFinite(min) || !Number.isFinite(sec)) return null;
1884
+ const mmss = `${String(min).padStart(2, "0")}:${String(sec).padStart(2, "0")}`;
1885
+ const frac = m[3] ? m[3].padEnd(3, "0") : "";
1886
+ const ms = frac ? `.${frac}` : "";
1887
+ return { mmss, ms, full: `${mmss}${ms}` };
1888
+ }
1889
+
1864
1890
  // src/mutations/item-mutations.ts
1865
1891
  function withSceneUpdated2(data, episodeIndex, sceneIndex, updater) {
1866
1892
  const episodes = [...data.episodes || []];
@@ -2622,6 +2648,8 @@ function SceneEditorImpl({
2622
2648
  disabled,
2623
2649
  focused = false,
2624
2650
  actionDiff,
2651
+ showTimestamp = false,
2652
+ highlights,
2625
2653
  onEdit,
2626
2654
  onNavigateAsset,
2627
2655
  onRequestAssetDelete
@@ -2720,9 +2748,11 @@ function SceneEditorImpl({
2720
2748
  onNavigateAsset
2721
2749
  }
2722
2750
  ),
2723
- /* @__PURE__ */ jsxs("div", { className: "lj-se-actions", children: [
2751
+ /* @__PURE__ */ jsxs("div", { className: cn("lj-se-actions", showTimestamp && "has-ts"), children: [
2724
2752
  actions.map((item, idx) => {
2725
2753
  const mark = actionDiff?.byIndex.get(idx);
2754
+ const hl = highlights?.get(idx);
2755
+ const ts = showTimestamp ? formatTimestamp(item.extend?.timestamp) : null;
2726
2756
  return /* @__PURE__ */ jsxs(Fragment, { children: [
2727
2757
  renderGhosts(idx),
2728
2758
  /* @__PURE__ */ jsxs(
@@ -2732,11 +2762,15 @@ function SceneEditorImpl({
2732
2762
  "lj-se-action-slot",
2733
2763
  dragIndex === idx && "is-dragging",
2734
2764
  dropSlot === idx && "drop-before",
2735
- mark && (mark.kind === "added" ? "is-diff-added" : "is-diff-modified")
2765
+ mark && (mark.kind === "added" ? "is-diff-added" : "is-diff-modified"),
2766
+ hl && "lj-se-hl",
2767
+ hl?.className
2736
2768
  ),
2769
+ style: hl?.style,
2737
2770
  onDragOver: (e) => onSlotDragOver(e, idx),
2738
2771
  onDrop: onSlotDrop,
2739
2772
  children: [
2773
+ ts ? /* @__PURE__ */ jsx("span", { className: "lj-se-ts", title: ts.full, children: ts.mmss }) : null,
2740
2774
  mark ? /* @__PURE__ */ jsx(
2741
2775
  DiffActionControls,
2742
2776
  {
@@ -2817,7 +2851,8 @@ function safeId(raw, fallback) {
2817
2851
  }
2818
2852
  var GROUP_LABEL = { actors: "\u89D2\u8272", locations: "\u573A\u666F", props: "\u9053\u5177" };
2819
2853
  var assetGroupLabel = (g) => GROUP_LABEL[g];
2820
- function buildFlatStream(data) {
2854
+ function buildFlatStream(data, opts = {}) {
2855
+ const includeAssets = opts.includeAssets ?? true;
2821
2856
  const items = [];
2822
2857
  const indexByKey = /* @__PURE__ */ new Map();
2823
2858
  const sceneIndexLookup = /* @__PURE__ */ new Map();
@@ -2865,14 +2900,16 @@ function buildFlatStream(data) {
2865
2900
  }
2866
2901
  });
2867
2902
  push({ key: "add-episode", kind: "add-episode" });
2868
- const specs = [
2869
- { group: "actors", ids: (data.actors ?? []).map((a, i) => safeId(a.actor_id, `__idx:${i}`)) },
2870
- { group: "locations", ids: (data.locations ?? []).map((l, i) => safeId(l.location_id, `__idx:${i}`)) },
2871
- { group: "props", ids: (data.props ?? []).map((p, i) => safeId(p.prop_id, `__idx:${i}`)) }
2872
- ];
2873
- for (const { group, ids } of specs) {
2874
- push({ key: `asset-group:${group}`, kind: "asset-group-header", group });
2875
- ids.forEach((assetId) => push({ key: `asset:${group}:${assetId}`, kind: "asset-card", group, assetId }));
2903
+ if (includeAssets) {
2904
+ const specs = [
2905
+ { group: "actors", ids: (data.actors ?? []).map((a, i) => safeId(a.actor_id, `__idx:${i}`)) },
2906
+ { group: "locations", ids: (data.locations ?? []).map((l, i) => safeId(l.location_id, `__idx:${i}`)) },
2907
+ { group: "props", ids: (data.props ?? []).map((p, i) => safeId(p.prop_id, `__idx:${i}`)) }
2908
+ ];
2909
+ for (const { group, ids } of specs) {
2910
+ push({ key: `asset-group:${group}`, kind: "asset-group-header", group });
2911
+ ids.forEach((assetId) => push({ key: `asset:${group}:${assetId}`, kind: "asset-card", group, assetId }));
2912
+ }
2876
2913
  }
2877
2914
  return { items, indexByKey, sceneIndexLookup, sceneIdLookup, assetIndexLookup, paperStartKey, paperEndKey };
2878
2915
  }
@@ -3553,6 +3590,7 @@ function AssetCardImpl({
3553
3590
  onSave: (v) => onEdit((p) => updateEntityStateField(p, kind, assetId, sid, "state_name", v))
3554
3591
  }
3555
3592
  ),
3593
+ (state.state_id ?? "").trim() ? /* @__PURE__ */ jsx("span", { className: "lj-se-assetcard-state-id", title: "\u72B6\u6001 id", children: (state.state_id ?? "").trim() }) : null,
3556
3594
  !disabled && (state.state_id ?? "").trim() ? /* @__PURE__ */ jsx(
3557
3595
  StateDeleteButton,
3558
3596
  {
@@ -3614,11 +3652,28 @@ function ScriptStream({
3614
3652
  onAcceptAction,
3615
3653
  onRejectAction,
3616
3654
  scrollScopeKey,
3655
+ includeAssets = true,
3656
+ paper = true,
3657
+ showTimestamp = false,
3658
+ highlights,
3617
3659
  onEdit,
3618
3660
  onSelectionChange,
3619
3661
  onRequestAssetDelete
3620
3662
  }) {
3621
- const stream = useMemo(() => buildFlatStream(value), [value]);
3663
+ const stream = useMemo(() => buildFlatStream(value, { includeAssets }), [value, includeAssets]);
3664
+ const highlightsByScene = useMemo(() => {
3665
+ const m = /* @__PURE__ */ new Map();
3666
+ for (const h of highlights ?? []) {
3667
+ const key = `${h.episodeIdx}:${h.sceneId}`;
3668
+ let inner = m.get(key);
3669
+ if (!inner) {
3670
+ inner = /* @__PURE__ */ new Map();
3671
+ m.set(key, inner);
3672
+ }
3673
+ inner.set(h.actionIndex, { className: h.className, style: h.style });
3674
+ }
3675
+ return m;
3676
+ }, [highlights]);
3622
3677
  const maps = useMemo(
3623
3678
  () => ({
3624
3679
  actorMap: getActorMap(value.actors ?? []),
@@ -3686,6 +3741,7 @@ function ScriptStream({
3686
3741
  const end = key === stream.paperEndKey;
3687
3742
  return start && end ? "is-sole" : start ? "is-start" : end ? "is-end" : "is-mid";
3688
3743
  };
3744
+ const segClass = (key, extra) => cn(paper ? "lj-se-paper-seg" : "lj-se-plain-seg", paperEdge(key), extra);
3689
3745
  useEffect(() => {
3690
3746
  const key = selectionTargetKey(selection);
3691
3747
  if (selection.origin === "scroll") {
@@ -3723,7 +3779,7 @@ function ScriptStream({
3723
3779
  case "episode-header": {
3724
3780
  const episode = value.episodes?.[item.episodeIdx];
3725
3781
  if (!episode) return null;
3726
- return /* @__PURE__ */ jsx("div", { className: "lj-se-stream-row", children: /* @__PURE__ */ jsx("div", { className: cn("lj-se-paper-seg", paperEdge(item.key), "lj-se-ep-seg"), children: /* @__PURE__ */ jsxs("div", { className: "lj-se-epbanner-wrap", children: [
3782
+ return /* @__PURE__ */ jsx("div", { className: "lj-se-stream-row", children: /* @__PURE__ */ jsx("div", { className: segClass(item.key, "lj-se-ep-seg"), children: /* @__PURE__ */ jsxs("div", { className: "lj-se-epbanner-wrap", children: [
3727
3783
  !disabled ? /* @__PURE__ */ jsx(EpisodeMenu, { episodeIdx: item.episodeIdx, onEdit }) : null,
3728
3784
  /* @__PURE__ */ jsx(
3729
3785
  EpisodeBanner,
@@ -3739,7 +3795,7 @@ function ScriptStream({
3739
3795
  }
3740
3796
  case "episode-empty":
3741
3797
  if (disabled) return null;
3742
- return /* @__PURE__ */ jsx("div", { className: "lj-se-stream-row", children: /* @__PURE__ */ jsx("div", { className: cn("lj-se-paper-seg", paperEdge(item.key)), children: /* @__PURE__ */ jsxs(
3798
+ return /* @__PURE__ */ jsx("div", { className: "lj-se-stream-row", children: /* @__PURE__ */ jsx("div", { className: segClass(item.key), children: /* @__PURE__ */ jsxs(
3743
3799
  "button",
3744
3800
  {
3745
3801
  type: "button",
@@ -3765,7 +3821,7 @@ function ScriptStream({
3765
3821
  onAccept: onAcceptAction,
3766
3822
  onReject: onRejectAction
3767
3823
  } : void 0;
3768
- return /* @__PURE__ */ jsx("div", { className: "lj-se-stream-row", "data-quote-source": true, children: /* @__PURE__ */ jsxs("div", { className: cn("lj-se-paper-seg", paperEdge(item.key), wholeKind && "is-changed"), children: [
3824
+ return /* @__PURE__ */ jsx("div", { className: "lj-se-stream-row", "data-quote-source": true, children: /* @__PURE__ */ jsxs("div", { className: segClass(item.key, wholeKind ? "is-changed" : void 0), children: [
3769
3825
  sceneDiff && inDiff ? /* @__PURE__ */ jsxs("div", { className: "lj-se-scenediff", children: [
3770
3826
  /* @__PURE__ */ jsx("span", { className: cn("lj-se-scenediff-tag", wholeKind === "added" && "is-added"), children: wholeKind === "added" ? "AI \u65B0\u589E" : "AI \u6539\u52A8" }),
3771
3827
  /* @__PURE__ */ jsx("span", { className: "lj-se-scenediff-spacer" }),
@@ -3799,6 +3855,8 @@ function ScriptStream({
3799
3855
  format,
3800
3856
  disabled,
3801
3857
  focused,
3858
+ showTimestamp,
3859
+ highlights: highlightsByScene.get(diffKey),
3802
3860
  onEdit,
3803
3861
  onNavigateAsset: select,
3804
3862
  onRequestAssetDelete
@@ -4539,9 +4597,11 @@ function ScriptSearchLayer({
4539
4597
  /* @__PURE__ */ jsx(HighlightContext.Provider, { value: open ? query : "", children })
4540
4598
  ] });
4541
4599
  }
4600
+ var NOOP_EDIT = (patch) => {
4601
+ };
4542
4602
  function ScriptEditor({
4543
4603
  value,
4544
- onEdit,
4604
+ onEdit: onEditProp,
4545
4605
  selection: controlledSelection,
4546
4606
  onSelectionChange,
4547
4607
  format: controlledFormat,
@@ -4560,11 +4620,20 @@ function ScriptEditor({
4560
4620
  onEditingChange,
4561
4621
  referenceFocusRequest,
4562
4622
  scrollScopeKey,
4623
+ showAssets = true,
4624
+ showTitle = true,
4625
+ showFormatToggle = true,
4626
+ paper = true,
4627
+ showTimestamp = false,
4628
+ highlights,
4629
+ contentWidth,
4630
+ showSaveCapsule = true,
4563
4631
  diff,
4564
4632
  asset,
4565
4633
  onAddChatReferences,
4566
4634
  onRequestAssetDelete
4567
4635
  }) {
4636
+ const onEdit = onEditProp ?? NOOP_EDIT;
4568
4637
  const editingCount = useRef(0);
4569
4638
  const onEditingChangeRef = useRef(onEditingChange);
4570
4639
  onEditingChangeRef.current = onEditingChange;
@@ -4695,6 +4764,17 @@ function ScriptEditor({
4695
4764
  const contextLabel = buildContextLabel(value, selection);
4696
4765
  const returnTarget = resolveReturnTarget(value, selection);
4697
4766
  const rootClass = ["lj-se-root", dark ? "dark" : "", className].filter(Boolean).join(" ");
4767
+ const rootStyle = contentWidth != null ? { "--lj-se-content-width": typeof contentWidth === "number" ? `${contentWidth}px` : contentWidth } : void 0;
4768
+ const streamHighlights = useMemo(
4769
+ () => highlights?.map((h) => ({
4770
+ episodeIdx: h.target.episodeIdx,
4771
+ sceneId: h.target.sceneId,
4772
+ actionIndex: h.target.actionIndex,
4773
+ className: h.className,
4774
+ style: h.style
4775
+ })),
4776
+ [highlights]
4777
+ );
4698
4778
  const center = /* @__PURE__ */ jsx(
4699
4779
  ScriptStream,
4700
4780
  {
@@ -4708,6 +4788,10 @@ function ScriptEditor({
4708
4788
  onAcceptAction: diff?.onAcceptAction,
4709
4789
  onRejectAction: diff?.onRejectAction,
4710
4790
  scrollScopeKey,
4791
+ includeAssets: showAssets,
4792
+ paper,
4793
+ showTimestamp,
4794
+ highlights: streamHighlights,
4711
4795
  onEdit,
4712
4796
  onSelectionChange: setSelection,
4713
4797
  onRequestAssetDelete
@@ -4717,6 +4801,7 @@ function ScriptEditor({
4717
4801
  "div",
4718
4802
  {
4719
4803
  className: rootClass,
4804
+ style: rootStyle,
4720
4805
  ref: rootRef,
4721
4806
  children: [
4722
4807
  toc === "sidebar" ? /* @__PURE__ */ jsx(
@@ -4732,12 +4817,13 @@ function ScriptEditor({
4732
4817
  /* @__PURE__ */ jsx(
4733
4818
  CanvasTopBar,
4734
4819
  {
4735
- title: value.title,
4736
- onTitleChange: disabled ? void 0 : onTitleChange,
4820
+ title: showTitle ? value.title : void 0,
4821
+ onTitleChange: disabled || !showTitle ? void 0 : onTitleChange,
4737
4822
  titleDisabled: disabled,
4738
4823
  contextLabel,
4739
4824
  format,
4740
4825
  onFormatChange: setFormat,
4826
+ showFormatToggle,
4741
4827
  canUndo,
4742
4828
  canRedo,
4743
4829
  onUndo,
@@ -4758,7 +4844,7 @@ function ScriptEditor({
4758
4844
  }
4759
4845
  ),
4760
4846
  onAddChatReferences ? /* @__PURE__ */ jsx(QuoteLayer, { containerRef: canvasRef, onAddChatReferences }) : null,
4761
- /* @__PURE__ */ jsx(
4847
+ showSaveCapsule ? /* @__PURE__ */ jsx(
4762
4848
  SaveCapsule,
4763
4849
  {
4764
4850
  saveStatus,
@@ -4772,7 +4858,7 @@ function ScriptEditor({
4772
4858
  returnLabel: returnTarget?.label,
4773
4859
  onReturn: returnTarget ? () => setSelection(returnTarget.selection) : void 0
4774
4860
  }
4775
- )
4861
+ ) : null
4776
4862
  ] })
4777
4863
  ] })
4778
4864
  ]
@@ -4977,6 +5063,6 @@ function DeleteConfirmButton({
4977
5063
  );
4978
5064
  }
4979
5065
 
4980
- export { DeleteConfirmButton, EMPTY_SELECTION, EditableText, Popover, ScriptContentType, ScriptEditor, SelectField, 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, mutations_exports as mutations, rejectActionToBaseline, rejectSceneToBaseline, sceneChangeKey, selectionFromScriptReference, selectionTargetKey, withOrigin };
5066
+ 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 };
4981
5067
  //# sourceMappingURL=index.js.map
4982
5068
  //# sourceMappingURL=index.js.map