@lingjingai/script-editor 0.2.1 → 0.3.1
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 +71 -3
- package/dist/index.js +128 -28
- package/dist/index.js.map +1 -1
- package/dist/style.css +60 -7
- package/package.json +1 -1
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
|
-
|
|
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,69 @@ 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
|
+
/** per-action hover (target on enter, null on leave) — pair with `highlights`
|
|
567
|
+
* to build a hover-to-highlight effect entirely host-side. */
|
|
568
|
+
onActionHover?: (target: {
|
|
569
|
+
episodeIdx: number;
|
|
570
|
+
sceneId: string;
|
|
571
|
+
actionIndex: number;
|
|
572
|
+
} | null) => void;
|
|
573
|
+
/** per-action click, with full address. */
|
|
574
|
+
onActionClick?: (target: {
|
|
575
|
+
episodeIdx: number;
|
|
576
|
+
sceneId: string;
|
|
577
|
+
actionIndex: number;
|
|
578
|
+
}) => void;
|
|
579
|
+
}
|
|
580
|
+
/** A host range highlight targeting one action (same coords as DiffActionMark). */
|
|
581
|
+
interface ScriptHighlight {
|
|
582
|
+
target: {
|
|
583
|
+
episodeIdx: number;
|
|
584
|
+
sceneId: string;
|
|
585
|
+
actionIndex: number;
|
|
586
|
+
};
|
|
587
|
+
className?: string;
|
|
588
|
+
style?: React.CSSProperties;
|
|
540
589
|
}
|
|
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;
|
|
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;
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Action timestamps live at `action.extend.timestamp` as an in-episode
|
|
594
|
+
* "M:SS.mmm" string (e.g. "00:03.333", "01:12.000"). The editor shows the
|
|
595
|
+
* "MM:SS" part prominently and the ".mmm" fraction de-emphasized.
|
|
596
|
+
*
|
|
597
|
+
* Parsing is lenient: anything that isn't a well-formed timestamp returns null
|
|
598
|
+
* so the caller simply renders no time gutter for that row (never a placeholder).
|
|
599
|
+
*/
|
|
600
|
+
interface ParsedTimestamp {
|
|
601
|
+
/** minutes:seconds, zero-padded — the prominent part, e.g. "01:07". */
|
|
602
|
+
mmss: string;
|
|
603
|
+
/** the fractional ".mmm" suffix incl. the dot, or "" when there is none. */
|
|
604
|
+
ms: string;
|
|
605
|
+
/** full normalized "MM:SS.mmm" (or "MM:SS" when no fraction) for title/hover. */
|
|
606
|
+
full: string;
|
|
607
|
+
}
|
|
608
|
+
/** Parse "M:SS.mmm" → { mmss, ms, full }, or null when malformed / empty. */
|
|
609
|
+
declare function formatTimestamp(raw: unknown): ParsedTimestamp | null;
|
|
542
610
|
|
|
543
611
|
/**
|
|
544
612
|
* Headless navigation model — surfaces the script's TOC tree (the same
|
|
@@ -830,4 +898,4 @@ declare function DeleteConfirmButton({ onConfirm, disabled, targetScope, title,
|
|
|
830
898
|
description?: string;
|
|
831
899
|
}): react.JSX.Element;
|
|
832
900
|
|
|
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 };
|
|
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 };
|
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:
|
|
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
|
|
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,10 @@ function SceneEditorImpl({
|
|
|
2622
2648
|
disabled,
|
|
2623
2649
|
focused = false,
|
|
2624
2650
|
actionDiff,
|
|
2651
|
+
showTimestamp = false,
|
|
2652
|
+
highlights,
|
|
2653
|
+
onActionHover,
|
|
2654
|
+
onActionClick,
|
|
2625
2655
|
onEdit,
|
|
2626
2656
|
onNavigateAsset,
|
|
2627
2657
|
onRequestAssetDelete
|
|
@@ -2629,6 +2659,7 @@ function SceneEditorImpl({
|
|
|
2629
2659
|
const [pendingEdit, setPendingEdit] = useState(null);
|
|
2630
2660
|
const { actorMap, speakerMap, locationMap, propMap } = maps;
|
|
2631
2661
|
const actions = scene.actions ?? [];
|
|
2662
|
+
const sceneId = scene.scene_id ?? "";
|
|
2632
2663
|
const actorOptions = useMemo(() => {
|
|
2633
2664
|
const out = [];
|
|
2634
2665
|
for (const a of actorMap.values()) {
|
|
@@ -2720,9 +2751,11 @@ function SceneEditorImpl({
|
|
|
2720
2751
|
onNavigateAsset
|
|
2721
2752
|
}
|
|
2722
2753
|
),
|
|
2723
|
-
/* @__PURE__ */ jsxs("div", { className: "lj-se-actions", children: [
|
|
2754
|
+
/* @__PURE__ */ jsxs("div", { className: cn("lj-se-actions", showTimestamp && "has-ts"), children: [
|
|
2724
2755
|
actions.map((item, idx) => {
|
|
2725
2756
|
const mark = actionDiff?.byIndex.get(idx);
|
|
2757
|
+
const hl = highlights?.get(idx);
|
|
2758
|
+
const ts = showTimestamp ? formatTimestamp(item.extend?.timestamp) : null;
|
|
2726
2759
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2727
2760
|
renderGhosts(idx),
|
|
2728
2761
|
/* @__PURE__ */ jsxs(
|
|
@@ -2732,11 +2765,18 @@ function SceneEditorImpl({
|
|
|
2732
2765
|
"lj-se-action-slot",
|
|
2733
2766
|
dragIndex === idx && "is-dragging",
|
|
2734
2767
|
dropSlot === idx && "drop-before",
|
|
2735
|
-
mark && (mark.kind === "added" ? "is-diff-added" : "is-diff-modified")
|
|
2768
|
+
mark && (mark.kind === "added" ? "is-diff-added" : "is-diff-modified"),
|
|
2769
|
+
hl && "lj-se-hl",
|
|
2770
|
+
hl?.className
|
|
2736
2771
|
),
|
|
2772
|
+
style: hl?.style,
|
|
2737
2773
|
onDragOver: (e) => onSlotDragOver(e, idx),
|
|
2738
2774
|
onDrop: onSlotDrop,
|
|
2775
|
+
onMouseEnter: onActionHover ? () => onActionHover({ episodeIdx: episodeIndex, sceneId, actionIndex: idx }) : void 0,
|
|
2776
|
+
onMouseLeave: onActionHover ? () => onActionHover(null) : void 0,
|
|
2777
|
+
onClick: onActionClick ? () => onActionClick({ episodeIdx: episodeIndex, sceneId, actionIndex: idx }) : void 0,
|
|
2739
2778
|
children: [
|
|
2779
|
+
ts ? /* @__PURE__ */ jsx("span", { className: "lj-se-ts", title: ts.full, children: ts.mmss }) : null,
|
|
2740
2780
|
mark ? /* @__PURE__ */ jsx(
|
|
2741
2781
|
DiffActionControls,
|
|
2742
2782
|
{
|
|
@@ -2817,7 +2857,8 @@ function safeId(raw, fallback) {
|
|
|
2817
2857
|
}
|
|
2818
2858
|
var GROUP_LABEL = { actors: "\u89D2\u8272", locations: "\u573A\u666F", props: "\u9053\u5177" };
|
|
2819
2859
|
var assetGroupLabel = (g) => GROUP_LABEL[g];
|
|
2820
|
-
function buildFlatStream(data) {
|
|
2860
|
+
function buildFlatStream(data, opts = {}) {
|
|
2861
|
+
const includeAssets = opts.includeAssets ?? true;
|
|
2821
2862
|
const items = [];
|
|
2822
2863
|
const indexByKey = /* @__PURE__ */ new Map();
|
|
2823
2864
|
const sceneIndexLookup = /* @__PURE__ */ new Map();
|
|
@@ -2865,14 +2906,16 @@ function buildFlatStream(data) {
|
|
|
2865
2906
|
}
|
|
2866
2907
|
});
|
|
2867
2908
|
push({ key: "add-episode", kind: "add-episode" });
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2909
|
+
if (includeAssets) {
|
|
2910
|
+
const specs = [
|
|
2911
|
+
{ group: "actors", ids: (data.actors ?? []).map((a, i) => safeId(a.actor_id, `__idx:${i}`)) },
|
|
2912
|
+
{ group: "locations", ids: (data.locations ?? []).map((l, i) => safeId(l.location_id, `__idx:${i}`)) },
|
|
2913
|
+
{ group: "props", ids: (data.props ?? []).map((p, i) => safeId(p.prop_id, `__idx:${i}`)) }
|
|
2914
|
+
];
|
|
2915
|
+
for (const { group, ids } of specs) {
|
|
2916
|
+
push({ key: `asset-group:${group}`, kind: "asset-group-header", group });
|
|
2917
|
+
ids.forEach((assetId) => push({ key: `asset:${group}:${assetId}`, kind: "asset-card", group, assetId }));
|
|
2918
|
+
}
|
|
2876
2919
|
}
|
|
2877
2920
|
return { items, indexByKey, sceneIndexLookup, sceneIdLookup, assetIndexLookup, paperStartKey, paperEndKey };
|
|
2878
2921
|
}
|
|
@@ -3553,6 +3596,7 @@ function AssetCardImpl({
|
|
|
3553
3596
|
onSave: (v) => onEdit((p) => updateEntityStateField(p, kind, assetId, sid, "state_name", v))
|
|
3554
3597
|
}
|
|
3555
3598
|
),
|
|
3599
|
+
(state.state_id ?? "").trim() ? /* @__PURE__ */ jsx("span", { className: "lj-se-assetcard-state-id", title: "\u72B6\u6001 id", children: (state.state_id ?? "").trim() }) : null,
|
|
3556
3600
|
!disabled && (state.state_id ?? "").trim() ? /* @__PURE__ */ jsx(
|
|
3557
3601
|
StateDeleteButton,
|
|
3558
3602
|
{
|
|
@@ -3614,11 +3658,30 @@ function ScriptStream({
|
|
|
3614
3658
|
onAcceptAction,
|
|
3615
3659
|
onRejectAction,
|
|
3616
3660
|
scrollScopeKey,
|
|
3661
|
+
includeAssets = true,
|
|
3662
|
+
paper = true,
|
|
3663
|
+
showTimestamp = false,
|
|
3664
|
+
highlights,
|
|
3665
|
+
onActionHover,
|
|
3666
|
+
onActionClick,
|
|
3617
3667
|
onEdit,
|
|
3618
3668
|
onSelectionChange,
|
|
3619
3669
|
onRequestAssetDelete
|
|
3620
3670
|
}) {
|
|
3621
|
-
const stream = useMemo(() => buildFlatStream(value), [value]);
|
|
3671
|
+
const stream = useMemo(() => buildFlatStream(value, { includeAssets }), [value, includeAssets]);
|
|
3672
|
+
const highlightsByScene = useMemo(() => {
|
|
3673
|
+
const m = /* @__PURE__ */ new Map();
|
|
3674
|
+
for (const h of highlights ?? []) {
|
|
3675
|
+
const key = `${h.episodeIdx}:${h.sceneId}`;
|
|
3676
|
+
let inner = m.get(key);
|
|
3677
|
+
if (!inner) {
|
|
3678
|
+
inner = /* @__PURE__ */ new Map();
|
|
3679
|
+
m.set(key, inner);
|
|
3680
|
+
}
|
|
3681
|
+
inner.set(h.actionIndex, { className: h.className, style: h.style });
|
|
3682
|
+
}
|
|
3683
|
+
return m;
|
|
3684
|
+
}, [highlights]);
|
|
3622
3685
|
const maps = useMemo(
|
|
3623
3686
|
() => ({
|
|
3624
3687
|
actorMap: getActorMap(value.actors ?? []),
|
|
@@ -3686,6 +3749,7 @@ function ScriptStream({
|
|
|
3686
3749
|
const end = key === stream.paperEndKey;
|
|
3687
3750
|
return start && end ? "is-sole" : start ? "is-start" : end ? "is-end" : "is-mid";
|
|
3688
3751
|
};
|
|
3752
|
+
const segClass = (key, extra) => cn(paper ? "lj-se-paper-seg" : "lj-se-plain-seg", paperEdge(key), extra);
|
|
3689
3753
|
useEffect(() => {
|
|
3690
3754
|
const key = selectionTargetKey(selection);
|
|
3691
3755
|
if (selection.origin === "scroll") {
|
|
@@ -3723,7 +3787,7 @@ function ScriptStream({
|
|
|
3723
3787
|
case "episode-header": {
|
|
3724
3788
|
const episode = value.episodes?.[item.episodeIdx];
|
|
3725
3789
|
if (!episode) return null;
|
|
3726
|
-
return /* @__PURE__ */ jsx("div", { className: "lj-se-stream-row", children: /* @__PURE__ */ jsx("div", { className:
|
|
3790
|
+
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
3791
|
!disabled ? /* @__PURE__ */ jsx(EpisodeMenu, { episodeIdx: item.episodeIdx, onEdit }) : null,
|
|
3728
3792
|
/* @__PURE__ */ jsx(
|
|
3729
3793
|
EpisodeBanner,
|
|
@@ -3739,7 +3803,7 @@ function ScriptStream({
|
|
|
3739
3803
|
}
|
|
3740
3804
|
case "episode-empty":
|
|
3741
3805
|
if (disabled) return null;
|
|
3742
|
-
return /* @__PURE__ */ jsx("div", { className: "lj-se-stream-row", children: /* @__PURE__ */ jsx("div", { className:
|
|
3806
|
+
return /* @__PURE__ */ jsx("div", { className: "lj-se-stream-row", children: /* @__PURE__ */ jsx("div", { className: segClass(item.key), children: /* @__PURE__ */ jsxs(
|
|
3743
3807
|
"button",
|
|
3744
3808
|
{
|
|
3745
3809
|
type: "button",
|
|
@@ -3765,7 +3829,7 @@ function ScriptStream({
|
|
|
3765
3829
|
onAccept: onAcceptAction,
|
|
3766
3830
|
onReject: onRejectAction
|
|
3767
3831
|
} : void 0;
|
|
3768
|
-
return /* @__PURE__ */ jsx("div", { className: "lj-se-stream-row", "data-quote-source": true, children: /* @__PURE__ */ jsxs("div", { className:
|
|
3832
|
+
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
3833
|
sceneDiff && inDiff ? /* @__PURE__ */ jsxs("div", { className: "lj-se-scenediff", children: [
|
|
3770
3834
|
/* @__PURE__ */ jsx("span", { className: cn("lj-se-scenediff-tag", wholeKind === "added" && "is-added"), children: wholeKind === "added" ? "AI \u65B0\u589E" : "AI \u6539\u52A8" }),
|
|
3771
3835
|
/* @__PURE__ */ jsx("span", { className: "lj-se-scenediff-spacer" }),
|
|
@@ -3799,6 +3863,10 @@ function ScriptStream({
|
|
|
3799
3863
|
format,
|
|
3800
3864
|
disabled,
|
|
3801
3865
|
focused,
|
|
3866
|
+
showTimestamp,
|
|
3867
|
+
highlights: highlightsByScene.get(diffKey),
|
|
3868
|
+
onActionHover,
|
|
3869
|
+
onActionClick,
|
|
3802
3870
|
onEdit,
|
|
3803
3871
|
onNavigateAsset: select,
|
|
3804
3872
|
onRequestAssetDelete
|
|
@@ -4539,9 +4607,11 @@ function ScriptSearchLayer({
|
|
|
4539
4607
|
/* @__PURE__ */ jsx(HighlightContext.Provider, { value: open ? query : "", children })
|
|
4540
4608
|
] });
|
|
4541
4609
|
}
|
|
4610
|
+
var NOOP_EDIT = (patch) => {
|
|
4611
|
+
};
|
|
4542
4612
|
function ScriptEditor({
|
|
4543
4613
|
value,
|
|
4544
|
-
onEdit,
|
|
4614
|
+
onEdit: onEditProp,
|
|
4545
4615
|
selection: controlledSelection,
|
|
4546
4616
|
onSelectionChange,
|
|
4547
4617
|
format: controlledFormat,
|
|
@@ -4560,11 +4630,22 @@ function ScriptEditor({
|
|
|
4560
4630
|
onEditingChange,
|
|
4561
4631
|
referenceFocusRequest,
|
|
4562
4632
|
scrollScopeKey,
|
|
4633
|
+
showAssets = true,
|
|
4634
|
+
showTitle = true,
|
|
4635
|
+
showFormatToggle = true,
|
|
4636
|
+
paper = true,
|
|
4637
|
+
showTimestamp = false,
|
|
4638
|
+
highlights,
|
|
4639
|
+
contentWidth,
|
|
4640
|
+
showSaveCapsule = true,
|
|
4641
|
+
onActionHover,
|
|
4642
|
+
onActionClick,
|
|
4563
4643
|
diff,
|
|
4564
4644
|
asset,
|
|
4565
4645
|
onAddChatReferences,
|
|
4566
4646
|
onRequestAssetDelete
|
|
4567
4647
|
}) {
|
|
4648
|
+
const onEdit = onEditProp ?? NOOP_EDIT;
|
|
4568
4649
|
const editingCount = useRef(0);
|
|
4569
4650
|
const onEditingChangeRef = useRef(onEditingChange);
|
|
4570
4651
|
onEditingChangeRef.current = onEditingChange;
|
|
@@ -4695,6 +4776,17 @@ function ScriptEditor({
|
|
|
4695
4776
|
const contextLabel = buildContextLabel(value, selection);
|
|
4696
4777
|
const returnTarget = resolveReturnTarget(value, selection);
|
|
4697
4778
|
const rootClass = ["lj-se-root", dark ? "dark" : "", className].filter(Boolean).join(" ");
|
|
4779
|
+
const rootStyle = contentWidth != null ? { "--lj-se-content-width": typeof contentWidth === "number" ? `${contentWidth}px` : contentWidth } : void 0;
|
|
4780
|
+
const streamHighlights = useMemo(
|
|
4781
|
+
() => highlights?.map((h) => ({
|
|
4782
|
+
episodeIdx: h.target.episodeIdx,
|
|
4783
|
+
sceneId: h.target.sceneId,
|
|
4784
|
+
actionIndex: h.target.actionIndex,
|
|
4785
|
+
className: h.className,
|
|
4786
|
+
style: h.style
|
|
4787
|
+
})),
|
|
4788
|
+
[highlights]
|
|
4789
|
+
);
|
|
4698
4790
|
const center = /* @__PURE__ */ jsx(
|
|
4699
4791
|
ScriptStream,
|
|
4700
4792
|
{
|
|
@@ -4708,6 +4800,12 @@ function ScriptEditor({
|
|
|
4708
4800
|
onAcceptAction: diff?.onAcceptAction,
|
|
4709
4801
|
onRejectAction: diff?.onRejectAction,
|
|
4710
4802
|
scrollScopeKey,
|
|
4803
|
+
includeAssets: showAssets,
|
|
4804
|
+
paper,
|
|
4805
|
+
showTimestamp,
|
|
4806
|
+
highlights: streamHighlights,
|
|
4807
|
+
onActionHover,
|
|
4808
|
+
onActionClick,
|
|
4711
4809
|
onEdit,
|
|
4712
4810
|
onSelectionChange: setSelection,
|
|
4713
4811
|
onRequestAssetDelete
|
|
@@ -4717,6 +4815,7 @@ function ScriptEditor({
|
|
|
4717
4815
|
"div",
|
|
4718
4816
|
{
|
|
4719
4817
|
className: rootClass,
|
|
4818
|
+
style: rootStyle,
|
|
4720
4819
|
ref: rootRef,
|
|
4721
4820
|
children: [
|
|
4722
4821
|
toc === "sidebar" ? /* @__PURE__ */ jsx(
|
|
@@ -4732,12 +4831,13 @@ function ScriptEditor({
|
|
|
4732
4831
|
/* @__PURE__ */ jsx(
|
|
4733
4832
|
CanvasTopBar,
|
|
4734
4833
|
{
|
|
4735
|
-
title: value.title,
|
|
4736
|
-
onTitleChange: disabled ? void 0 : onTitleChange,
|
|
4834
|
+
title: showTitle ? value.title : void 0,
|
|
4835
|
+
onTitleChange: disabled || !showTitle ? void 0 : onTitleChange,
|
|
4737
4836
|
titleDisabled: disabled,
|
|
4738
4837
|
contextLabel,
|
|
4739
4838
|
format,
|
|
4740
4839
|
onFormatChange: setFormat,
|
|
4840
|
+
showFormatToggle,
|
|
4741
4841
|
canUndo,
|
|
4742
4842
|
canRedo,
|
|
4743
4843
|
onUndo,
|
|
@@ -4758,7 +4858,7 @@ function ScriptEditor({
|
|
|
4758
4858
|
}
|
|
4759
4859
|
),
|
|
4760
4860
|
onAddChatReferences ? /* @__PURE__ */ jsx(QuoteLayer, { containerRef: canvasRef, onAddChatReferences }) : null,
|
|
4761
|
-
/* @__PURE__ */ jsx(
|
|
4861
|
+
showSaveCapsule ? /* @__PURE__ */ jsx(
|
|
4762
4862
|
SaveCapsule,
|
|
4763
4863
|
{
|
|
4764
4864
|
saveStatus,
|
|
@@ -4772,7 +4872,7 @@ function ScriptEditor({
|
|
|
4772
4872
|
returnLabel: returnTarget?.label,
|
|
4773
4873
|
onReturn: returnTarget ? () => setSelection(returnTarget.selection) : void 0
|
|
4774
4874
|
}
|
|
4775
|
-
)
|
|
4875
|
+
) : null
|
|
4776
4876
|
] })
|
|
4777
4877
|
] })
|
|
4778
4878
|
]
|
|
@@ -4977,6 +5077,6 @@ function DeleteConfirmButton({
|
|
|
4977
5077
|
);
|
|
4978
5078
|
}
|
|
4979
5079
|
|
|
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 };
|
|
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 };
|
|
4981
5081
|
//# sourceMappingURL=index.js.map
|
|
4982
5082
|
//# sourceMappingURL=index.js.map
|