@lingjingai/script-editor 0.1.19 → 0.1.21
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/INTEGRATION.md +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +434 -105
- package/dist/index.js.map +1 -1
- package/dist/style.css +25 -4
- package/package.json +6 -2
package/INTEGRATION.md
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
```ts
|
|
21
21
|
import "@lingjingai/script-editor/style.css";
|
|
22
22
|
```
|
|
23
|
-
-
|
|
23
|
+
- **构建产物**:发布 / 外部消费走 `dist`(`tsup` 编译好的 JS + d.ts + style.css + paper-fibers.png),无需编译包内 TS。本仓 `apps/web` dev 通过 Vite 的 `conditions:["development"]` 走 `src/index.ts` + `src/styles.css`,这样改包内源码能 HMR 生效;外部没有该 condition 时自动落到 `dist`。
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
package/dist/index.d.ts
CHANGED
|
@@ -163,7 +163,7 @@ type StudioSelectionFrom = {
|
|
|
163
163
|
*
|
|
164
164
|
* 不打 origin 时按 'sidebar' 等价处理(保守滚动)。
|
|
165
165
|
*/
|
|
166
|
-
type StudioSelectionOrigin = "sidebar" | "scroll" | "reference" | "diff" | "chat" | "init" | "episode-toggle";
|
|
166
|
+
type StudioSelectionOrigin = "sidebar" | "scroll" | "reference" | "diff" | "search" | "chat" | "init" | "episode-toggle";
|
|
167
167
|
declare function buildSelectionFrom(kind: StudioSelectionFrom["kind"], id: string): StudioSelectionFrom | undefined;
|
|
168
168
|
declare function fromToSelection(from: StudioSelectionFrom): StudioSelection;
|
|
169
169
|
/** 公共字段:所有 selection variant 都可携带(origin 防双向联动死循环;from 资产回链)。 */
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createContext, memo, useState, useMemo, useCallback, Fragment, useRef, useContext, useEffect, useLayoutEffect, useDeferredValue } from 'react';
|
|
2
|
-
import { Plus, Trash2, X, Check,
|
|
2
|
+
import { Plus, Trash2, X, Check, ArrowUp, ArrowDown, GripVertical, MessageCircle, Film, Brain, BookOpen, Eraser, Scissors, ArrowUpToLine, BookText, ChevronRight, FileText, Users, MapPin, Package, Undo2, Redo2, MessageSquareQuote, ArrowLeft, ChevronLeft, Search, Loader2, AlertCircle } from 'lucide-react';
|
|
3
3
|
import { jsxs, jsx, Fragment as Fragment$1 } from 'react/jsx-runtime';
|
|
4
4
|
import { Virtuoso } from 'react-virtuoso';
|
|
5
5
|
import { createPortal } from 'react-dom';
|
|
@@ -596,6 +596,30 @@ function EntityGroup({
|
|
|
596
596
|
)) : null
|
|
597
597
|
] });
|
|
598
598
|
}
|
|
599
|
+
var HighlightContext = createContext("");
|
|
600
|
+
function HighlightedText({ text }) {
|
|
601
|
+
const highlight = useContext(HighlightContext);
|
|
602
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children: renderHighlightedText(text, highlight) });
|
|
603
|
+
}
|
|
604
|
+
function renderHighlightedText(value, query) {
|
|
605
|
+
const needle = query.trim();
|
|
606
|
+
if (!needle) return value;
|
|
607
|
+
const lowerValue = value.toLocaleLowerCase();
|
|
608
|
+
const lowerNeedle = needle.toLocaleLowerCase();
|
|
609
|
+
const out = [];
|
|
610
|
+
let from = 0;
|
|
611
|
+
let hit = lowerValue.indexOf(lowerNeedle);
|
|
612
|
+
while (hit >= 0) {
|
|
613
|
+
if (hit > from) out.push(value.slice(from, hit));
|
|
614
|
+
out.push(
|
|
615
|
+
/* @__PURE__ */ jsx("mark", { className: "lj-se-search-hit", children: value.slice(hit, hit + needle.length) }, `${hit}-${out.length}`)
|
|
616
|
+
);
|
|
617
|
+
from = hit + needle.length;
|
|
618
|
+
hit = lowerValue.indexOf(lowerNeedle, from);
|
|
619
|
+
}
|
|
620
|
+
if (from < value.length) out.push(value.slice(from));
|
|
621
|
+
return out.length > 0 ? out : value;
|
|
622
|
+
}
|
|
599
623
|
var EditingSignalContext = createContext(null);
|
|
600
624
|
var LIVE_COMMIT_MS = 400;
|
|
601
625
|
function caretOffsetFromPoint(x, y) {
|
|
@@ -627,6 +651,7 @@ function EditableText({
|
|
|
627
651
|
const inputRef = useRef(null);
|
|
628
652
|
const committedRef = useRef(value);
|
|
629
653
|
const editingSignal = useContext(EditingSignalContext);
|
|
654
|
+
const highlight = useContext(HighlightContext);
|
|
630
655
|
const liveTimerRef = useRef(null);
|
|
631
656
|
const composingRef = useRef(false);
|
|
632
657
|
const draftRef = useRef(value);
|
|
@@ -710,13 +735,14 @@ function EditableText({
|
|
|
710
735
|
onEditEnd?.();
|
|
711
736
|
}
|
|
712
737
|
if (!editing) {
|
|
738
|
+
const content = value ? renderHighlightedText(value, highlight) : /* @__PURE__ */ jsx("span", { className: "lj-se-ph", children: placeholder ?? "" });
|
|
713
739
|
return /* @__PURE__ */ jsx(
|
|
714
740
|
"span",
|
|
715
741
|
{
|
|
716
742
|
...spanProps,
|
|
717
743
|
className: "lj-se-editable" + (className ? " " + className : "") + (disabled ? " lj-se-editable--ro" : ""),
|
|
718
744
|
onClick: beginEdit,
|
|
719
|
-
children:
|
|
745
|
+
children: content
|
|
720
746
|
}
|
|
721
747
|
);
|
|
722
748
|
}
|
|
@@ -1746,6 +1772,9 @@ function SelectField({
|
|
|
1746
1772
|
style,
|
|
1747
1773
|
emptyOptionLabel
|
|
1748
1774
|
}) {
|
|
1775
|
+
const highlight = useContext(HighlightContext).trim();
|
|
1776
|
+
const selectedLabel = options.find((o) => o.value === value)?.label ?? (value ? value : placeholder ?? emptyOptionLabel ?? "\u2014");
|
|
1777
|
+
const highlighted = !!highlight && selectedLabel.toLocaleLowerCase().includes(highlight.toLocaleLowerCase());
|
|
1749
1778
|
return /* @__PURE__ */ jsxs(
|
|
1750
1779
|
"select",
|
|
1751
1780
|
{
|
|
@@ -1754,7 +1783,7 @@ function SelectField({
|
|
|
1754
1783
|
title,
|
|
1755
1784
|
onChange: (e) => onChange(e.target.value),
|
|
1756
1785
|
style,
|
|
1757
|
-
className: cn("lj-se-select", mono && "lj-se-mono", className),
|
|
1786
|
+
className: cn("lj-se-select", mono && "lj-se-mono", highlighted && "is-search-hit", className),
|
|
1758
1787
|
children: [
|
|
1759
1788
|
!value || !options.some((o) => o.value === value) ? /* @__PURE__ */ jsx("option", { value, children: placeholder ?? emptyOptionLabel ?? "\u2014" }) : null,
|
|
1760
1789
|
options.map((o) => /* @__PURE__ */ jsx("option", { value: o.value, children: o.label }, o.value))
|
|
@@ -1778,7 +1807,7 @@ function AssetStateChip({
|
|
|
1778
1807
|
const opts = stateOptions(states2);
|
|
1779
1808
|
const trimmed = (currentStateId ?? "").trim();
|
|
1780
1809
|
return /* @__PURE__ */ jsxs("span", { className: cn("lj-se-chip", orphan && "is-orphan"), title: orphan ? `${name} \u4E0D\u5B58\u5728` : void 0, children: [
|
|
1781
|
-
onNavigate ? /* @__PURE__ */ jsx("button", { type: "button", className: "lj-se-chip-name lj-se-chip-name--link", title: `\u8DF3\u5230 ${name}`, onClick: onNavigate, children: name }) : /* @__PURE__ */ jsx("span", { className: "lj-se-chip-name", children: name }),
|
|
1810
|
+
onNavigate ? /* @__PURE__ */ jsx("button", { type: "button", className: "lj-se-chip-name lj-se-chip-name--link", title: `\u8DF3\u5230 ${name}`, onClick: onNavigate, children: /* @__PURE__ */ jsx(HighlightedText, { text: name }) }) : /* @__PURE__ */ jsx("span", { className: "lj-se-chip-name", children: /* @__PURE__ */ jsx(HighlightedText, { text: name }) }),
|
|
1782
1811
|
/* @__PURE__ */ jsx("span", { className: "lj-se-chip-sep", children: "\xB7" }),
|
|
1783
1812
|
/* @__PURE__ */ jsx(
|
|
1784
1813
|
SelectField,
|
|
@@ -2267,7 +2296,7 @@ function SceneItemRowImpl({
|
|
|
2267
2296
|
className: "lj-se-cue lj-se-cue--structured",
|
|
2268
2297
|
style: resolvedActor ? { color: getActorCueVar(resolvedActorId, tone) } : void 0,
|
|
2269
2298
|
children: [
|
|
2270
|
-
getDialogueSpeakerLabel(item, actorMap, speakerMap) || "\u672A\u77E5\u53D1\u58F0\u6E90",
|
|
2299
|
+
/* @__PURE__ */ jsx(HighlightedText, { text: getDialogueSpeakerLabel(item, actorMap, speakerMap) || "\u672A\u77E5\u53D1\u58F0\u6E90" }),
|
|
2271
2300
|
getDialogueDeliveryLabel(item.delivery) ? /* @__PURE__ */ jsxs("span", { className: "lj-se-cue-delivery", children: [
|
|
2272
2301
|
"\uFF08",
|
|
2273
2302
|
getDialogueDeliveryLabel(item.delivery),
|
|
@@ -2359,7 +2388,7 @@ function SceneItemRowImpl({
|
|
|
2359
2388
|
const tone = isInner ? "inner" : "dialogue";
|
|
2360
2389
|
const tag = isInner ? emotion ? `OS\xB7${emotion}` : "OS" : emotion;
|
|
2361
2390
|
body = /* @__PURE__ */ jsxs("p", { className: cn("lj-se-asian-cueline", isInner && "is-inner"), children: [
|
|
2362
|
-
multiSpeaker ? /* @__PURE__ */ jsx("strong", { className: "lj-se-asian-speaker", style: resolvedActor ? { color: getActorCueVar(resolvedActorId, tone) } : void 0, children: getDialogueSpeakerLabel(item, actorMap, speakerMap) }) : /* @__PURE__ */ jsx(
|
|
2391
|
+
multiSpeaker ? /* @__PURE__ */ jsx("strong", { className: "lj-se-asian-speaker", style: resolvedActor ? { color: getActorCueVar(resolvedActorId, tone) } : void 0, children: /* @__PURE__ */ jsx(HighlightedText, { text: getDialogueSpeakerLabel(item, actorMap, speakerMap) }) }) : /* @__PURE__ */ jsx(
|
|
2363
2392
|
SelectField,
|
|
2364
2393
|
{
|
|
2365
2394
|
value: resolvedActorId,
|
|
@@ -2753,56 +2782,6 @@ function removeEntityState(data, kind, id, stateId) {
|
|
|
2753
2782
|
states: (e.states ?? []).filter((s) => (s.state_id ?? "").trim() !== stateId)
|
|
2754
2783
|
}));
|
|
2755
2784
|
}
|
|
2756
|
-
var CONFIRM_TTL_MS = 2500;
|
|
2757
|
-
function DeleteConfirmButton({
|
|
2758
|
-
onConfirm,
|
|
2759
|
-
disabled,
|
|
2760
|
-
targetScope,
|
|
2761
|
-
title = "\u5220\u9664\uFF1F"
|
|
2762
|
-
}) {
|
|
2763
|
-
const [confirming, setConfirming] = useState(false);
|
|
2764
|
-
const timerRef = useRef(null);
|
|
2765
|
-
useEffect(
|
|
2766
|
-
() => () => {
|
|
2767
|
-
if (timerRef.current) clearTimeout(timerRef.current);
|
|
2768
|
-
},
|
|
2769
|
-
[]
|
|
2770
|
-
);
|
|
2771
|
-
const cancel = () => {
|
|
2772
|
-
if (timerRef.current) {
|
|
2773
|
-
clearTimeout(timerRef.current);
|
|
2774
|
-
timerRef.current = null;
|
|
2775
|
-
}
|
|
2776
|
-
setConfirming(false);
|
|
2777
|
-
};
|
|
2778
|
-
const handleClick = () => {
|
|
2779
|
-
if (confirming) {
|
|
2780
|
-
cancel();
|
|
2781
|
-
void onConfirm();
|
|
2782
|
-
return;
|
|
2783
|
-
}
|
|
2784
|
-
setConfirming(true);
|
|
2785
|
-
if (timerRef.current) clearTimeout(timerRef.current);
|
|
2786
|
-
timerRef.current = setTimeout(() => {
|
|
2787
|
-
setConfirming(false);
|
|
2788
|
-
timerRef.current = null;
|
|
2789
|
-
}, CONFIRM_TTL_MS);
|
|
2790
|
-
};
|
|
2791
|
-
return /* @__PURE__ */ jsx(
|
|
2792
|
-
"button",
|
|
2793
|
-
{
|
|
2794
|
-
type: "button",
|
|
2795
|
-
disabled,
|
|
2796
|
-
"data-delete-target": targetScope,
|
|
2797
|
-
"data-confirming": confirming || void 0,
|
|
2798
|
-
onClick: handleClick,
|
|
2799
|
-
"aria-label": confirming ? `\u518D\u70B9\u4E00\u6B21\u786E\u8BA4\uFF1A${title}` : title,
|
|
2800
|
-
title: confirming ? "\u518D\u70B9\u4E00\u6B21\u786E\u8BA4\uFF082.5 \u79D2\u540E\u81EA\u52A8\u53D6\u6D88\uFF09" : title,
|
|
2801
|
-
className: cn("lj-se-del", confirming && "is-confirming"),
|
|
2802
|
-
children: confirming ? /* @__PURE__ */ jsx(Check, { className: "lj-se-icon", strokeWidth: 2.2 }) : /* @__PURE__ */ jsx(Eraser, { className: "lj-se-icon", strokeWidth: 1.5 })
|
|
2803
|
-
}
|
|
2804
|
-
);
|
|
2805
|
-
}
|
|
2806
2785
|
var SPACE_ZH = { interior: "\u5185", exterior: "\u5916" };
|
|
2807
2786
|
var TIME_ZH = { day: "\u65E5", night: "\u591C", dawn: "\u6668", dusk: "\u66AE" };
|
|
2808
2787
|
function resolveCueId(item, actorMap, speakerMap) {
|
|
@@ -3044,6 +3023,38 @@ var META = {
|
|
|
3044
3023
|
function newStateId(id) {
|
|
3045
3024
|
return `st_${id}_${Date.now().toString(36)}`;
|
|
3046
3025
|
}
|
|
3026
|
+
function StateDeleteButton({ stateName, onConfirm }) {
|
|
3027
|
+
const [open, setOpen] = useState(false);
|
|
3028
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
3029
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "lj-se-assetcard-state-del", title: "\u5220\u9664\u8BE5\u72B6\u6001", "aria-label": "\u5220\u9664\u8BE5\u72B6\u6001", onClick: () => setOpen(true), children: /* @__PURE__ */ jsx(X, { className: "lj-se-ic-3", strokeWidth: 1.8 }) }),
|
|
3030
|
+
open ? /* @__PURE__ */ jsx("div", { className: "lj-se-modal-backdrop", role: "presentation", onMouseDown: () => setOpen(false), children: /* @__PURE__ */ jsxs("div", { className: "lj-se-modal", role: "dialog", "aria-modal": "true", "aria-labelledby": "lj-se-delete-state-title", onMouseDown: (e) => e.stopPropagation(), children: [
|
|
3031
|
+
/* @__PURE__ */ jsxs("div", { className: "lj-se-modal-head", children: [
|
|
3032
|
+
/* @__PURE__ */ jsx("h2", { id: "lj-se-delete-state-title", className: "lj-se-modal-title", children: "\u5220\u9664\u72B6\u6001" }),
|
|
3033
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "lj-se-modal-x", "aria-label": "\u5173\u95ED", onClick: () => setOpen(false), children: /* @__PURE__ */ jsx(X, { className: "lj-se-ic-3", strokeWidth: 1.8 }) })
|
|
3034
|
+
] }),
|
|
3035
|
+
/* @__PURE__ */ jsxs("p", { className: "lj-se-modal-body", children: [
|
|
3036
|
+
"\u786E\u8BA4\u5220\u9664\u300C",
|
|
3037
|
+
stateName || "\u672A\u547D\u540D\u72B6\u6001",
|
|
3038
|
+
"\u300D\uFF1F"
|
|
3039
|
+
] }),
|
|
3040
|
+
/* @__PURE__ */ jsxs("div", { className: "lj-se-modal-actions", children: [
|
|
3041
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "lj-se-modal-btn", onClick: () => setOpen(false), children: "\u53D6\u6D88" }),
|
|
3042
|
+
/* @__PURE__ */ jsx(
|
|
3043
|
+
"button",
|
|
3044
|
+
{
|
|
3045
|
+
type: "button",
|
|
3046
|
+
className: "lj-se-modal-btn lj-se-modal-btn-danger",
|
|
3047
|
+
onClick: () => {
|
|
3048
|
+
setOpen(false);
|
|
3049
|
+
onConfirm();
|
|
3050
|
+
},
|
|
3051
|
+
children: "\u5220\u9664"
|
|
3052
|
+
}
|
|
3053
|
+
)
|
|
3054
|
+
] })
|
|
3055
|
+
] }) }) : null
|
|
3056
|
+
] });
|
|
3057
|
+
}
|
|
3047
3058
|
function AssetCardImpl({
|
|
3048
3059
|
data,
|
|
3049
3060
|
group,
|
|
@@ -3075,7 +3086,7 @@ function AssetCardImpl({
|
|
|
3075
3086
|
const description = rawDescription.trim();
|
|
3076
3087
|
const states2 = entity.states ?? [];
|
|
3077
3088
|
const fromRef = buildSelectionFrom(kind, assetId);
|
|
3078
|
-
return /* @__PURE__ */ jsx("div", { className: "lj-se-assetcard-row", children: /* @__PURE__ */ jsxs("article", { className: "lj-se-assetcard", "data-no-quote-selection": true, children: [
|
|
3089
|
+
return /* @__PURE__ */ jsx("div", { className: "lj-se-assetcard-row", children: /* @__PURE__ */ jsx(HighlightContext.Provider, { value: "", children: /* @__PURE__ */ jsxs("article", { className: "lj-se-assetcard", "data-no-quote-selection": true, children: [
|
|
3079
3090
|
/* @__PURE__ */ jsxs("div", { className: "lj-se-assetcard-head", children: [
|
|
3080
3091
|
/* @__PURE__ */ jsx("span", { className: "lj-se-assetcard-name", children: /* @__PURE__ */ jsx(
|
|
3081
3092
|
EditableText,
|
|
@@ -3150,7 +3161,13 @@ function AssetCardImpl({
|
|
|
3150
3161
|
onSave: (v) => onEdit((p) => updateEntityStateField(p, kind, assetId, sid, "state_name", v))
|
|
3151
3162
|
}
|
|
3152
3163
|
),
|
|
3153
|
-
!disabled && (state.state_id ?? "").trim() ? /* @__PURE__ */ jsx(
|
|
3164
|
+
!disabled && (state.state_id ?? "").trim() ? /* @__PURE__ */ jsx(
|
|
3165
|
+
StateDeleteButton,
|
|
3166
|
+
{
|
|
3167
|
+
stateName: state.state_name ?? state.name ?? "",
|
|
3168
|
+
onConfirm: () => onEdit((p) => removeEntityState(p, kind, assetId, sid))
|
|
3169
|
+
}
|
|
3170
|
+
) : null
|
|
3154
3171
|
] }),
|
|
3155
3172
|
/* @__PURE__ */ jsx(
|
|
3156
3173
|
EditableText,
|
|
@@ -3181,7 +3198,7 @@ function AssetCardImpl({
|
|
|
3181
3198
|
}
|
|
3182
3199
|
)
|
|
3183
3200
|
) : null
|
|
3184
|
-
] }) });
|
|
3201
|
+
] }) }) });
|
|
3185
3202
|
}
|
|
3186
3203
|
function areEqual(prev, next) {
|
|
3187
3204
|
if (prev.group !== next.group || prev.assetId !== next.assetId || prev.disabled !== next.disabled) return false;
|
|
@@ -3884,6 +3901,250 @@ function QuoteLayer({
|
|
|
3884
3901
|
}
|
|
3885
3902
|
);
|
|
3886
3903
|
}
|
|
3904
|
+
function ScriptSearchBox({
|
|
3905
|
+
query,
|
|
3906
|
+
current,
|
|
3907
|
+
total,
|
|
3908
|
+
inputRef,
|
|
3909
|
+
onQueryChange,
|
|
3910
|
+
onPrev,
|
|
3911
|
+
onNext,
|
|
3912
|
+
onClose
|
|
3913
|
+
}) {
|
|
3914
|
+
return /* @__PURE__ */ jsxs("div", { className: "lj-se-search", children: [
|
|
3915
|
+
/* @__PURE__ */ jsx(Search, { className: "lj-se-ic-3", strokeWidth: 2 }),
|
|
3916
|
+
/* @__PURE__ */ jsx(
|
|
3917
|
+
"input",
|
|
3918
|
+
{
|
|
3919
|
+
ref: inputRef,
|
|
3920
|
+
className: "lj-se-search-input",
|
|
3921
|
+
value: query,
|
|
3922
|
+
placeholder: "\u641C\u7D22",
|
|
3923
|
+
"aria-label": "\u641C\u7D22\u5267\u672C",
|
|
3924
|
+
onChange: (e) => onQueryChange(e.target.value),
|
|
3925
|
+
onKeyDown: (e) => {
|
|
3926
|
+
if (e.key === "Enter") {
|
|
3927
|
+
e.preventDefault();
|
|
3928
|
+
if (e.shiftKey) onPrev();
|
|
3929
|
+
else onNext();
|
|
3930
|
+
} else if (e.key === "Escape") {
|
|
3931
|
+
e.preventDefault();
|
|
3932
|
+
onClose();
|
|
3933
|
+
}
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
),
|
|
3937
|
+
/* @__PURE__ */ jsxs("span", { className: "lj-se-search-count", children: [
|
|
3938
|
+
total > 0 ? current + 1 : 0,
|
|
3939
|
+
"/",
|
|
3940
|
+
total
|
|
3941
|
+
] }),
|
|
3942
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "lj-se-search-btn", title: "\u4E0A\u4E00\u5904", disabled: total === 0, onClick: onPrev, children: /* @__PURE__ */ jsx(ChevronLeft, { className: "lj-se-ic-3", strokeWidth: 2.25 }) }),
|
|
3943
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "lj-se-search-btn", title: "\u4E0B\u4E00\u5904", disabled: total === 0, onClick: onNext, children: /* @__PURE__ */ jsx(ChevronRight, { className: "lj-se-ic-3", strokeWidth: 2.25 }) }),
|
|
3944
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "lj-se-search-btn", title: "\u5173\u95ED\u641C\u7D22", onClick: onClose, children: /* @__PURE__ */ jsx(X, { className: "lj-se-ic-3", strokeWidth: 2.25 }) })
|
|
3945
|
+
] });
|
|
3946
|
+
}
|
|
3947
|
+
|
|
3948
|
+
// src/search/search-index.ts
|
|
3949
|
+
var SPACE_LABEL = {
|
|
3950
|
+
interior: "INT. \u5185",
|
|
3951
|
+
exterior: "EXT. \u5916"
|
|
3952
|
+
};
|
|
3953
|
+
var TIME_LABEL = {
|
|
3954
|
+
day: "DAY \u65E5",
|
|
3955
|
+
night: "NIGHT \u591C",
|
|
3956
|
+
dawn: "DAWN \u6668",
|
|
3957
|
+
dusk: "DUSK \u66AE"
|
|
3958
|
+
};
|
|
3959
|
+
function add(parts, value) {
|
|
3960
|
+
const text = value?.trim();
|
|
3961
|
+
if (text) parts.push(text);
|
|
3962
|
+
}
|
|
3963
|
+
function findStateName(states2, stateId) {
|
|
3964
|
+
const id = stateId?.trim();
|
|
3965
|
+
if (!id) return "";
|
|
3966
|
+
return getStateDisplayName(states2?.find((state) => state.state_id?.trim() === id));
|
|
3967
|
+
}
|
|
3968
|
+
function normalize(value) {
|
|
3969
|
+
return value.trim().toLocaleLowerCase();
|
|
3970
|
+
}
|
|
3971
|
+
function sceneSearchText(scene, sceneIdx, maps) {
|
|
3972
|
+
const parts = [];
|
|
3973
|
+
const context = getSceneContext(scene);
|
|
3974
|
+
const primaryLocationId = getPrimarySceneLocationId(scene);
|
|
3975
|
+
const location = primaryLocationId ? maps.locationMap.get(primaryLocationId) : void 0;
|
|
3976
|
+
add(parts, String(getSceneNumber(scene, sceneIdx)));
|
|
3977
|
+
add(parts, SPACE_LABEL[scene.environment?.space ?? ""] ?? scene.environment?.space);
|
|
3978
|
+
add(parts, location ? getLocationDisplayName(location) : primaryLocationId);
|
|
3979
|
+
add(parts, TIME_LABEL[scene.environment?.time ?? ""] ?? scene.environment?.time);
|
|
3980
|
+
for (const ref of context.actors ?? []) {
|
|
3981
|
+
const id = ref.actor_id?.trim();
|
|
3982
|
+
if (!id) continue;
|
|
3983
|
+
const actor = maps.actorMap.get(id);
|
|
3984
|
+
add(parts, actor ? getActorDisplayName(actor) : id);
|
|
3985
|
+
add(parts, findStateName(actor?.states, ref.state_id));
|
|
3986
|
+
}
|
|
3987
|
+
for (const ref of context.props ?? []) {
|
|
3988
|
+
const id = ref.prop_id?.trim();
|
|
3989
|
+
if (!id) continue;
|
|
3990
|
+
const prop = maps.propMap.get(id);
|
|
3991
|
+
add(parts, prop ? getPropDisplayName(prop) : id);
|
|
3992
|
+
add(parts, findStateName(prop?.states, ref.state_id));
|
|
3993
|
+
}
|
|
3994
|
+
for (const item of scene.actions ?? []) {
|
|
3995
|
+
addItemSearchText(parts, item, maps.actorMap, maps.speakerMap);
|
|
3996
|
+
}
|
|
3997
|
+
return parts.join("\n");
|
|
3998
|
+
}
|
|
3999
|
+
function addItemSearchText(parts, item, actorMap, speakerMap) {
|
|
4000
|
+
add(parts, item.content);
|
|
4001
|
+
add(parts, translateEmotionLabel(item.emotion));
|
|
4002
|
+
if (isActorBearingContentType(item.type)) {
|
|
4003
|
+
add(parts, getDialogueSpeakerLabel(item, actorMap, speakerMap));
|
|
4004
|
+
}
|
|
4005
|
+
for (const line of item.lines ?? []) {
|
|
4006
|
+
add(parts, getDialogueSpeakerLabel({ speaker_id: line.speaker_id }, actorMap, speakerMap));
|
|
4007
|
+
add(parts, line.content);
|
|
4008
|
+
}
|
|
4009
|
+
}
|
|
4010
|
+
function buildScriptSearchEntries(value) {
|
|
4011
|
+
const entries = [];
|
|
4012
|
+
const maps = {
|
|
4013
|
+
actorMap: getActorMap(value.actors ?? []),
|
|
4014
|
+
speakerMap: getSpeakerMap(value.speakers ?? []),
|
|
4015
|
+
locationMap: getLocationMap(value.locations ?? []),
|
|
4016
|
+
propMap: getPropMap(value.props ?? [])
|
|
4017
|
+
};
|
|
4018
|
+
const title = value.title?.trim();
|
|
4019
|
+
if (title && (value.episodes?.length ?? 0) > 0) {
|
|
4020
|
+
entries.push({
|
|
4021
|
+
key: "title",
|
|
4022
|
+
text: title,
|
|
4023
|
+
selection: { kind: "episode", episodeIdx: 0 }
|
|
4024
|
+
});
|
|
4025
|
+
}
|
|
4026
|
+
(value.episodes ?? []).forEach((episode, episodeIdx) => {
|
|
4027
|
+
entries.push({
|
|
4028
|
+
key: `episode:${episodeIdx}`,
|
|
4029
|
+
text: getEpisodeTitle(episode, episodeIdx),
|
|
4030
|
+
selection: { kind: "episode", episodeIdx }
|
|
4031
|
+
});
|
|
4032
|
+
(episode.scenes ?? []).forEach((scene, sceneIdx) => {
|
|
4033
|
+
entries.push({
|
|
4034
|
+
key: `scene:${episodeIdx}:${sceneIdx}`,
|
|
4035
|
+
text: sceneSearchText(scene, sceneIdx, maps),
|
|
4036
|
+
selection: { kind: "scene", episodeIdx, sceneIdx }
|
|
4037
|
+
});
|
|
4038
|
+
});
|
|
4039
|
+
});
|
|
4040
|
+
return entries;
|
|
4041
|
+
}
|
|
4042
|
+
function findScriptSearchMatches(entries, query) {
|
|
4043
|
+
const needle = normalize(query);
|
|
4044
|
+
if (!needle) return [];
|
|
4045
|
+
const matches = [];
|
|
4046
|
+
for (const entry of entries) {
|
|
4047
|
+
if (normalize(entry.text).includes(needle)) {
|
|
4048
|
+
matches.push({ entry, index: matches.length });
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
4051
|
+
return matches;
|
|
4052
|
+
}
|
|
4053
|
+
function ScriptSearchLayer({
|
|
4054
|
+
value,
|
|
4055
|
+
disabled,
|
|
4056
|
+
rootRef,
|
|
4057
|
+
selection,
|
|
4058
|
+
onSelectionChange,
|
|
4059
|
+
children
|
|
4060
|
+
}) {
|
|
4061
|
+
const editorActiveRef = useRef(false);
|
|
4062
|
+
const inputRef = useRef(null);
|
|
4063
|
+
const [open, setOpen] = useState(false);
|
|
4064
|
+
const [query, setQuery] = useState("");
|
|
4065
|
+
const [cursor, setCursor] = useState(0);
|
|
4066
|
+
const entries = useMemo(() => buildScriptSearchEntries(value), [value]);
|
|
4067
|
+
const matches = useMemo(() => findScriptSearchMatches(entries, query), [entries, query]);
|
|
4068
|
+
const total = matches.length;
|
|
4069
|
+
const current = total > 0 ? Math.min(cursor, total - 1) : 0;
|
|
4070
|
+
const selectMatch = useCallback(
|
|
4071
|
+
(index) => {
|
|
4072
|
+
const match = matches[index];
|
|
4073
|
+
if (!match) return;
|
|
4074
|
+
const next = withOrigin(match.entry.selection, "search");
|
|
4075
|
+
setCursor(index);
|
|
4076
|
+
if (!isSameSelectionTarget(next, selection)) onSelectionChange(next);
|
|
4077
|
+
},
|
|
4078
|
+
[matches, onSelectionChange, selection]
|
|
4079
|
+
);
|
|
4080
|
+
const openSearch = useCallback(() => {
|
|
4081
|
+
setOpen(true);
|
|
4082
|
+
requestAnimationFrame(() => {
|
|
4083
|
+
inputRef.current?.focus();
|
|
4084
|
+
inputRef.current?.select();
|
|
4085
|
+
});
|
|
4086
|
+
}, []);
|
|
4087
|
+
const closeSearch = useCallback(() => setOpen(false), []);
|
|
4088
|
+
const updateQuery = useCallback((next) => {
|
|
4089
|
+
setQuery(next);
|
|
4090
|
+
setCursor(0);
|
|
4091
|
+
}, []);
|
|
4092
|
+
const nextMatch = useCallback(() => {
|
|
4093
|
+
if (total > 0) selectMatch((current + 1) % total);
|
|
4094
|
+
}, [current, selectMatch, total]);
|
|
4095
|
+
const prevMatch = useCallback(() => {
|
|
4096
|
+
if (total > 0) selectMatch((current - 1 + total) % total);
|
|
4097
|
+
}, [current, selectMatch, total]);
|
|
4098
|
+
useEffect(() => {
|
|
4099
|
+
if (open && total > 0) selectMatch(current);
|
|
4100
|
+
}, [current, open, selectMatch, total]);
|
|
4101
|
+
useEffect(() => {
|
|
4102
|
+
if (!disabled && open) openSearch();
|
|
4103
|
+
}, [disabled, open, openSearch]);
|
|
4104
|
+
useEffect(() => {
|
|
4105
|
+
if (disabled) return;
|
|
4106
|
+
const rememberActive = (target) => {
|
|
4107
|
+
editorActiveRef.current = target instanceof Node && !!rootRef.current?.contains(target);
|
|
4108
|
+
};
|
|
4109
|
+
const onPointerDown = (e) => rememberActive(e.target);
|
|
4110
|
+
const onFocusIn = (e) => rememberActive(e.target);
|
|
4111
|
+
const onKeyDown = (e) => {
|
|
4112
|
+
if (!(e.metaKey || e.ctrlKey) || e.key.toLowerCase() !== "f") return;
|
|
4113
|
+
const target = e.target;
|
|
4114
|
+
const targetInside = target instanceof Node && !!rootRef.current?.contains(target);
|
|
4115
|
+
if (!targetInside) {
|
|
4116
|
+
if (target instanceof HTMLElement && target !== document.body) return;
|
|
4117
|
+
if (!editorActiveRef.current) return;
|
|
4118
|
+
}
|
|
4119
|
+
e.preventDefault();
|
|
4120
|
+
openSearch();
|
|
4121
|
+
};
|
|
4122
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
4123
|
+
document.addEventListener("focusin", onFocusIn, true);
|
|
4124
|
+
document.addEventListener("keydown", onKeyDown, true);
|
|
4125
|
+
return () => {
|
|
4126
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
4127
|
+
document.removeEventListener("focusin", onFocusIn, true);
|
|
4128
|
+
document.removeEventListener("keydown", onKeyDown, true);
|
|
4129
|
+
};
|
|
4130
|
+
}, [disabled, openSearch, rootRef]);
|
|
4131
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
4132
|
+
open ? /* @__PURE__ */ jsx(
|
|
4133
|
+
ScriptSearchBox,
|
|
4134
|
+
{
|
|
4135
|
+
query,
|
|
4136
|
+
current,
|
|
4137
|
+
total,
|
|
4138
|
+
inputRef,
|
|
4139
|
+
onQueryChange: updateQuery,
|
|
4140
|
+
onPrev: prevMatch,
|
|
4141
|
+
onNext: nextMatch,
|
|
4142
|
+
onClose: closeSearch
|
|
4143
|
+
}
|
|
4144
|
+
) : null,
|
|
4145
|
+
/* @__PURE__ */ jsx(HighlightContext.Provider, { value: open ? query : "", children })
|
|
4146
|
+
] });
|
|
4147
|
+
}
|
|
3887
4148
|
function ScriptEditor({
|
|
3888
4149
|
value,
|
|
3889
4150
|
onEdit,
|
|
@@ -3968,6 +4229,7 @@ function ScriptEditor({
|
|
|
3968
4229
|
},
|
|
3969
4230
|
[onFormatChange]
|
|
3970
4231
|
);
|
|
4232
|
+
const rootRef = useRef(null);
|
|
3971
4233
|
const sceneKindByKey = useMemo(() => {
|
|
3972
4234
|
const m = /* @__PURE__ */ new Map();
|
|
3973
4235
|
for (const s of diff?.scenes ?? []) m.set(sceneChangeKey(s.episodeIdx, s.sceneId), s.kind);
|
|
@@ -4057,54 +4319,71 @@ function ScriptEditor({
|
|
|
4057
4319
|
onRequestAssetDelete
|
|
4058
4320
|
}
|
|
4059
4321
|
);
|
|
4060
|
-
return /* @__PURE__ */ jsx(EditingSignalContext.Provider, { value: editingSignal, children: /* @__PURE__ */ jsxs(
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
}
|
|
4069
|
-
) : null,
|
|
4070
|
-
/* @__PURE__ */ jsxs("div", { className: "lj-se-main", children: [
|
|
4071
|
-
/* @__PURE__ */ jsx(
|
|
4072
|
-
CanvasTopBar,
|
|
4073
|
-
{
|
|
4074
|
-
title: value.title,
|
|
4075
|
-
onTitleChange: disabled ? void 0 : onTitleChange,
|
|
4076
|
-
titleDisabled: disabled,
|
|
4077
|
-
contextLabel,
|
|
4078
|
-
format,
|
|
4079
|
-
onFormatChange: setFormat,
|
|
4080
|
-
canUndo,
|
|
4081
|
-
canRedo,
|
|
4082
|
-
onUndo,
|
|
4083
|
-
onRedo,
|
|
4084
|
-
actions: topBarActions
|
|
4085
|
-
}
|
|
4086
|
-
),
|
|
4087
|
-
/* @__PURE__ */ jsxs("div", { className: "lj-se-canvas", ref: canvasRef, children: [
|
|
4088
|
-
center,
|
|
4089
|
-
onAddChatReferences ? /* @__PURE__ */ jsx(QuoteLayer, { containerRef: canvasRef, onAddChatReferences }) : null,
|
|
4090
|
-
/* @__PURE__ */ jsx(
|
|
4091
|
-
SaveCapsule,
|
|
4322
|
+
return /* @__PURE__ */ jsx(EditingSignalContext.Provider, { value: editingSignal, children: /* @__PURE__ */ jsxs(
|
|
4323
|
+
"div",
|
|
4324
|
+
{
|
|
4325
|
+
className: rootClass,
|
|
4326
|
+
ref: rootRef,
|
|
4327
|
+
children: [
|
|
4328
|
+
toc === "sidebar" ? /* @__PURE__ */ jsx(
|
|
4329
|
+
StudioToc,
|
|
4092
4330
|
{
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
onDiffPrev: changedSelections.length > 0 ? () => navDiff(-1) : void 0,
|
|
4098
|
-
onDiffNext: changedSelections.length > 0 ? () => navDiff(1) : void 0,
|
|
4099
|
-
onAcceptAll: diff?.onAcceptAll,
|
|
4100
|
-
onRejectAll: diff?.onRejectAll,
|
|
4101
|
-
returnLabel: returnTarget?.label,
|
|
4102
|
-
onReturn: returnTarget ? () => setSelection(returnTarget.selection) : void 0
|
|
4331
|
+
data: value,
|
|
4332
|
+
selection,
|
|
4333
|
+
onSelect: setSelection,
|
|
4334
|
+
onAddEpisode: disabled ? void 0 : () => onEdit((p) => addEpisode(p))
|
|
4103
4335
|
}
|
|
4104
|
-
)
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4336
|
+
) : null,
|
|
4337
|
+
/* @__PURE__ */ jsxs("div", { className: "lj-se-main", children: [
|
|
4338
|
+
/* @__PURE__ */ jsx(
|
|
4339
|
+
CanvasTopBar,
|
|
4340
|
+
{
|
|
4341
|
+
title: value.title,
|
|
4342
|
+
onTitleChange: disabled ? void 0 : onTitleChange,
|
|
4343
|
+
titleDisabled: disabled,
|
|
4344
|
+
contextLabel,
|
|
4345
|
+
format,
|
|
4346
|
+
onFormatChange: setFormat,
|
|
4347
|
+
canUndo,
|
|
4348
|
+
canRedo,
|
|
4349
|
+
onUndo,
|
|
4350
|
+
onRedo,
|
|
4351
|
+
actions: topBarActions
|
|
4352
|
+
}
|
|
4353
|
+
),
|
|
4354
|
+
/* @__PURE__ */ jsxs("div", { className: "lj-se-canvas", ref: canvasRef, children: [
|
|
4355
|
+
/* @__PURE__ */ jsx(
|
|
4356
|
+
ScriptSearchLayer,
|
|
4357
|
+
{
|
|
4358
|
+
value,
|
|
4359
|
+
disabled,
|
|
4360
|
+
rootRef,
|
|
4361
|
+
selection,
|
|
4362
|
+
onSelectionChange: setSelection,
|
|
4363
|
+
children: center
|
|
4364
|
+
}
|
|
4365
|
+
),
|
|
4366
|
+
onAddChatReferences ? /* @__PURE__ */ jsx(QuoteLayer, { containerRef: canvasRef, onAddChatReferences }) : null,
|
|
4367
|
+
/* @__PURE__ */ jsx(
|
|
4368
|
+
SaveCapsule,
|
|
4369
|
+
{
|
|
4370
|
+
saveStatus,
|
|
4371
|
+
diffCount,
|
|
4372
|
+
baselineStatus: diff?.baselineStatus,
|
|
4373
|
+
canApplyDiff: !!(diff?.onAcceptAll || diff?.onRejectAll),
|
|
4374
|
+
onDiffPrev: changedSelections.length > 0 ? () => navDiff(-1) : void 0,
|
|
4375
|
+
onDiffNext: changedSelections.length > 0 ? () => navDiff(1) : void 0,
|
|
4376
|
+
onAcceptAll: diff?.onAcceptAll,
|
|
4377
|
+
onRejectAll: diff?.onRejectAll,
|
|
4378
|
+
returnLabel: returnTarget?.label,
|
|
4379
|
+
onReturn: returnTarget ? () => setSelection(returnTarget.selection) : void 0
|
|
4380
|
+
}
|
|
4381
|
+
)
|
|
4382
|
+
] })
|
|
4383
|
+
] })
|
|
4384
|
+
]
|
|
4385
|
+
}
|
|
4386
|
+
) });
|
|
4108
4387
|
}
|
|
4109
4388
|
function buildContextLabel(value, selection) {
|
|
4110
4389
|
if (selection.kind === "worldview") {
|
|
@@ -4251,6 +4530,56 @@ __export(mutations_exports, {
|
|
|
4251
4530
|
updateSceneLocationState: () => updateSceneLocationState,
|
|
4252
4531
|
updateScenePropState: () => updateScenePropState
|
|
4253
4532
|
});
|
|
4533
|
+
var CONFIRM_TTL_MS = 2500;
|
|
4534
|
+
function DeleteConfirmButton({
|
|
4535
|
+
onConfirm,
|
|
4536
|
+
disabled,
|
|
4537
|
+
targetScope,
|
|
4538
|
+
title = "\u5220\u9664\uFF1F"
|
|
4539
|
+
}) {
|
|
4540
|
+
const [confirming, setConfirming] = useState(false);
|
|
4541
|
+
const timerRef = useRef(null);
|
|
4542
|
+
useEffect(
|
|
4543
|
+
() => () => {
|
|
4544
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
4545
|
+
},
|
|
4546
|
+
[]
|
|
4547
|
+
);
|
|
4548
|
+
const cancel = () => {
|
|
4549
|
+
if (timerRef.current) {
|
|
4550
|
+
clearTimeout(timerRef.current);
|
|
4551
|
+
timerRef.current = null;
|
|
4552
|
+
}
|
|
4553
|
+
setConfirming(false);
|
|
4554
|
+
};
|
|
4555
|
+
const handleClick = () => {
|
|
4556
|
+
if (confirming) {
|
|
4557
|
+
cancel();
|
|
4558
|
+
void onConfirm();
|
|
4559
|
+
return;
|
|
4560
|
+
}
|
|
4561
|
+
setConfirming(true);
|
|
4562
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
4563
|
+
timerRef.current = setTimeout(() => {
|
|
4564
|
+
setConfirming(false);
|
|
4565
|
+
timerRef.current = null;
|
|
4566
|
+
}, CONFIRM_TTL_MS);
|
|
4567
|
+
};
|
|
4568
|
+
return /* @__PURE__ */ jsx(
|
|
4569
|
+
"button",
|
|
4570
|
+
{
|
|
4571
|
+
type: "button",
|
|
4572
|
+
disabled,
|
|
4573
|
+
"data-delete-target": targetScope,
|
|
4574
|
+
"data-confirming": confirming || void 0,
|
|
4575
|
+
onClick: handleClick,
|
|
4576
|
+
"aria-label": confirming ? `\u518D\u70B9\u4E00\u6B21\u786E\u8BA4\uFF1A${title}` : title,
|
|
4577
|
+
title: confirming ? "\u518D\u70B9\u4E00\u6B21\u786E\u8BA4\uFF082.5 \u79D2\u540E\u81EA\u52A8\u53D6\u6D88\uFF09" : title,
|
|
4578
|
+
className: cn("lj-se-del", confirming && "is-confirming"),
|
|
4579
|
+
children: confirming ? /* @__PURE__ */ jsx(Check, { className: "lj-se-icon", strokeWidth: 2.2 }) : /* @__PURE__ */ jsx(Eraser, { className: "lj-se-icon", strokeWidth: 1.5 })
|
|
4580
|
+
}
|
|
4581
|
+
);
|
|
4582
|
+
}
|
|
4254
4583
|
|
|
4255
4584
|
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 };
|
|
4256
4585
|
//# sourceMappingURL=index.js.map
|