@lingjingai/script-editor 0.1.19 → 0.1.20

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 CHANGED
@@ -20,7 +20,7 @@
20
20
  ```ts
21
21
  import "@lingjingai/script-editor/style.css";
22
22
  ```
23
- - **构建产物**:消费方一律走 `dist`(`tsup` 编译好的 JS + d.ts + style.css + paper-fibers.png),无需编译包内 TS。`exports` 不带 `development` 条件(早期版本带过,会让 Vite **dev 模式**去找未发布的 `src/` 而报 `Failed to resolve import .../style.css` —— `0.1.1+` 已修)。
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, Eraser, ArrowUp, ArrowDown, GripVertical, MessageCircle, Film, Brain, BookOpen, Scissors, ArrowUpToLine, BookText, ChevronRight, FileText, Users, MapPin, Package, Undo2, Redo2, MessageSquareQuote, ArrowLeft, ChevronLeft, Loader2, AlertCircle } from 'lucide-react';
2
+ import { Plus, Trash2, X, Check, Eraser, ArrowUp, ArrowDown, GripVertical, MessageCircle, Film, Brain, BookOpen, 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: value || /* @__PURE__ */ jsx("span", { className: "lj-se-ph", children: placeholder ?? "" })
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,
@@ -3075,7 +3104,7 @@ function AssetCardImpl({
3075
3104
  const description = rawDescription.trim();
3076
3105
  const states2 = entity.states ?? [];
3077
3106
  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: [
3107
+ 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
3108
  /* @__PURE__ */ jsxs("div", { className: "lj-se-assetcard-head", children: [
3080
3109
  /* @__PURE__ */ jsx("span", { className: "lj-se-assetcard-name", children: /* @__PURE__ */ jsx(
3081
3110
  EditableText,
@@ -3181,7 +3210,7 @@ function AssetCardImpl({
3181
3210
  }
3182
3211
  )
3183
3212
  ) : null
3184
- ] }) });
3213
+ ] }) }) });
3185
3214
  }
3186
3215
  function areEqual(prev, next) {
3187
3216
  if (prev.group !== next.group || prev.assetId !== next.assetId || prev.disabled !== next.disabled) return false;
@@ -3884,6 +3913,250 @@ function QuoteLayer({
3884
3913
  }
3885
3914
  );
3886
3915
  }
3916
+ function ScriptSearchBox({
3917
+ query,
3918
+ current,
3919
+ total,
3920
+ inputRef,
3921
+ onQueryChange,
3922
+ onPrev,
3923
+ onNext,
3924
+ onClose
3925
+ }) {
3926
+ return /* @__PURE__ */ jsxs("div", { className: "lj-se-search", children: [
3927
+ /* @__PURE__ */ jsx(Search, { className: "lj-se-ic-3", strokeWidth: 2 }),
3928
+ /* @__PURE__ */ jsx(
3929
+ "input",
3930
+ {
3931
+ ref: inputRef,
3932
+ className: "lj-se-search-input",
3933
+ value: query,
3934
+ placeholder: "\u641C\u7D22",
3935
+ "aria-label": "\u641C\u7D22\u5267\u672C",
3936
+ onChange: (e) => onQueryChange(e.target.value),
3937
+ onKeyDown: (e) => {
3938
+ if (e.key === "Enter") {
3939
+ e.preventDefault();
3940
+ if (e.shiftKey) onPrev();
3941
+ else onNext();
3942
+ } else if (e.key === "Escape") {
3943
+ e.preventDefault();
3944
+ onClose();
3945
+ }
3946
+ }
3947
+ }
3948
+ ),
3949
+ /* @__PURE__ */ jsxs("span", { className: "lj-se-search-count", children: [
3950
+ total > 0 ? current + 1 : 0,
3951
+ "/",
3952
+ total
3953
+ ] }),
3954
+ /* @__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 }) }),
3955
+ /* @__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 }) }),
3956
+ /* @__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 }) })
3957
+ ] });
3958
+ }
3959
+
3960
+ // src/search/search-index.ts
3961
+ var SPACE_LABEL = {
3962
+ interior: "INT. \u5185",
3963
+ exterior: "EXT. \u5916"
3964
+ };
3965
+ var TIME_LABEL = {
3966
+ day: "DAY \u65E5",
3967
+ night: "NIGHT \u591C",
3968
+ dawn: "DAWN \u6668",
3969
+ dusk: "DUSK \u66AE"
3970
+ };
3971
+ function add(parts, value) {
3972
+ const text = value?.trim();
3973
+ if (text) parts.push(text);
3974
+ }
3975
+ function findStateName(states2, stateId) {
3976
+ const id = stateId?.trim();
3977
+ if (!id) return "";
3978
+ return getStateDisplayName(states2?.find((state) => state.state_id?.trim() === id));
3979
+ }
3980
+ function normalize(value) {
3981
+ return value.trim().toLocaleLowerCase();
3982
+ }
3983
+ function sceneSearchText(scene, sceneIdx, maps) {
3984
+ const parts = [];
3985
+ const context = getSceneContext(scene);
3986
+ const primaryLocationId = getPrimarySceneLocationId(scene);
3987
+ const location = primaryLocationId ? maps.locationMap.get(primaryLocationId) : void 0;
3988
+ add(parts, String(getSceneNumber(scene, sceneIdx)));
3989
+ add(parts, SPACE_LABEL[scene.environment?.space ?? ""] ?? scene.environment?.space);
3990
+ add(parts, location ? getLocationDisplayName(location) : primaryLocationId);
3991
+ add(parts, TIME_LABEL[scene.environment?.time ?? ""] ?? scene.environment?.time);
3992
+ for (const ref of context.actors ?? []) {
3993
+ const id = ref.actor_id?.trim();
3994
+ if (!id) continue;
3995
+ const actor = maps.actorMap.get(id);
3996
+ add(parts, actor ? getActorDisplayName(actor) : id);
3997
+ add(parts, findStateName(actor?.states, ref.state_id));
3998
+ }
3999
+ for (const ref of context.props ?? []) {
4000
+ const id = ref.prop_id?.trim();
4001
+ if (!id) continue;
4002
+ const prop = maps.propMap.get(id);
4003
+ add(parts, prop ? getPropDisplayName(prop) : id);
4004
+ add(parts, findStateName(prop?.states, ref.state_id));
4005
+ }
4006
+ for (const item of scene.actions ?? []) {
4007
+ addItemSearchText(parts, item, maps.actorMap, maps.speakerMap);
4008
+ }
4009
+ return parts.join("\n");
4010
+ }
4011
+ function addItemSearchText(parts, item, actorMap, speakerMap) {
4012
+ add(parts, item.content);
4013
+ add(parts, translateEmotionLabel(item.emotion));
4014
+ if (isActorBearingContentType(item.type)) {
4015
+ add(parts, getDialogueSpeakerLabel(item, actorMap, speakerMap));
4016
+ }
4017
+ for (const line of item.lines ?? []) {
4018
+ add(parts, getDialogueSpeakerLabel({ speaker_id: line.speaker_id }, actorMap, speakerMap));
4019
+ add(parts, line.content);
4020
+ }
4021
+ }
4022
+ function buildScriptSearchEntries(value) {
4023
+ const entries = [];
4024
+ const maps = {
4025
+ actorMap: getActorMap(value.actors ?? []),
4026
+ speakerMap: getSpeakerMap(value.speakers ?? []),
4027
+ locationMap: getLocationMap(value.locations ?? []),
4028
+ propMap: getPropMap(value.props ?? [])
4029
+ };
4030
+ const title = value.title?.trim();
4031
+ if (title && (value.episodes?.length ?? 0) > 0) {
4032
+ entries.push({
4033
+ key: "title",
4034
+ text: title,
4035
+ selection: { kind: "episode", episodeIdx: 0 }
4036
+ });
4037
+ }
4038
+ (value.episodes ?? []).forEach((episode, episodeIdx) => {
4039
+ entries.push({
4040
+ key: `episode:${episodeIdx}`,
4041
+ text: getEpisodeTitle(episode, episodeIdx),
4042
+ selection: { kind: "episode", episodeIdx }
4043
+ });
4044
+ (episode.scenes ?? []).forEach((scene, sceneIdx) => {
4045
+ entries.push({
4046
+ key: `scene:${episodeIdx}:${sceneIdx}`,
4047
+ text: sceneSearchText(scene, sceneIdx, maps),
4048
+ selection: { kind: "scene", episodeIdx, sceneIdx }
4049
+ });
4050
+ });
4051
+ });
4052
+ return entries;
4053
+ }
4054
+ function findScriptSearchMatches(entries, query) {
4055
+ const needle = normalize(query);
4056
+ if (!needle) return [];
4057
+ const matches = [];
4058
+ for (const entry of entries) {
4059
+ if (normalize(entry.text).includes(needle)) {
4060
+ matches.push({ entry, index: matches.length });
4061
+ }
4062
+ }
4063
+ return matches;
4064
+ }
4065
+ function ScriptSearchLayer({
4066
+ value,
4067
+ disabled,
4068
+ rootRef,
4069
+ selection,
4070
+ onSelectionChange,
4071
+ children
4072
+ }) {
4073
+ const editorActiveRef = useRef(false);
4074
+ const inputRef = useRef(null);
4075
+ const [open, setOpen] = useState(false);
4076
+ const [query, setQuery] = useState("");
4077
+ const [cursor, setCursor] = useState(0);
4078
+ const entries = useMemo(() => buildScriptSearchEntries(value), [value]);
4079
+ const matches = useMemo(() => findScriptSearchMatches(entries, query), [entries, query]);
4080
+ const total = matches.length;
4081
+ const current = total > 0 ? Math.min(cursor, total - 1) : 0;
4082
+ const selectMatch = useCallback(
4083
+ (index) => {
4084
+ const match = matches[index];
4085
+ if (!match) return;
4086
+ const next = withOrigin(match.entry.selection, "search");
4087
+ setCursor(index);
4088
+ if (!isSameSelectionTarget(next, selection)) onSelectionChange(next);
4089
+ },
4090
+ [matches, onSelectionChange, selection]
4091
+ );
4092
+ const openSearch = useCallback(() => {
4093
+ setOpen(true);
4094
+ requestAnimationFrame(() => {
4095
+ inputRef.current?.focus();
4096
+ inputRef.current?.select();
4097
+ });
4098
+ }, []);
4099
+ const closeSearch = useCallback(() => setOpen(false), []);
4100
+ const updateQuery = useCallback((next) => {
4101
+ setQuery(next);
4102
+ setCursor(0);
4103
+ }, []);
4104
+ const nextMatch = useCallback(() => {
4105
+ if (total > 0) selectMatch((current + 1) % total);
4106
+ }, [current, selectMatch, total]);
4107
+ const prevMatch = useCallback(() => {
4108
+ if (total > 0) selectMatch((current - 1 + total) % total);
4109
+ }, [current, selectMatch, total]);
4110
+ useEffect(() => {
4111
+ if (open && total > 0) selectMatch(current);
4112
+ }, [current, open, selectMatch, total]);
4113
+ useEffect(() => {
4114
+ if (!disabled && open) openSearch();
4115
+ }, [disabled, open, openSearch]);
4116
+ useEffect(() => {
4117
+ if (disabled) return;
4118
+ const rememberActive = (target) => {
4119
+ editorActiveRef.current = target instanceof Node && !!rootRef.current?.contains(target);
4120
+ };
4121
+ const onPointerDown = (e) => rememberActive(e.target);
4122
+ const onFocusIn = (e) => rememberActive(e.target);
4123
+ const onKeyDown = (e) => {
4124
+ if (!(e.metaKey || e.ctrlKey) || e.key.toLowerCase() !== "f") return;
4125
+ const target = e.target;
4126
+ const targetInside = target instanceof Node && !!rootRef.current?.contains(target);
4127
+ if (!targetInside) {
4128
+ if (target instanceof HTMLElement && target !== document.body) return;
4129
+ if (!editorActiveRef.current) return;
4130
+ }
4131
+ e.preventDefault();
4132
+ openSearch();
4133
+ };
4134
+ document.addEventListener("pointerdown", onPointerDown, true);
4135
+ document.addEventListener("focusin", onFocusIn, true);
4136
+ document.addEventListener("keydown", onKeyDown, true);
4137
+ return () => {
4138
+ document.removeEventListener("pointerdown", onPointerDown, true);
4139
+ document.removeEventListener("focusin", onFocusIn, true);
4140
+ document.removeEventListener("keydown", onKeyDown, true);
4141
+ };
4142
+ }, [disabled, openSearch, rootRef]);
4143
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
4144
+ open ? /* @__PURE__ */ jsx(
4145
+ ScriptSearchBox,
4146
+ {
4147
+ query,
4148
+ current,
4149
+ total,
4150
+ inputRef,
4151
+ onQueryChange: updateQuery,
4152
+ onPrev: prevMatch,
4153
+ onNext: nextMatch,
4154
+ onClose: closeSearch
4155
+ }
4156
+ ) : null,
4157
+ /* @__PURE__ */ jsx(HighlightContext.Provider, { value: open ? query : "", children })
4158
+ ] });
4159
+ }
3887
4160
  function ScriptEditor({
3888
4161
  value,
3889
4162
  onEdit,
@@ -3968,6 +4241,7 @@ function ScriptEditor({
3968
4241
  },
3969
4242
  [onFormatChange]
3970
4243
  );
4244
+ const rootRef = useRef(null);
3971
4245
  const sceneKindByKey = useMemo(() => {
3972
4246
  const m = /* @__PURE__ */ new Map();
3973
4247
  for (const s of diff?.scenes ?? []) m.set(sceneChangeKey(s.episodeIdx, s.sceneId), s.kind);
@@ -4057,54 +4331,71 @@ function ScriptEditor({
4057
4331
  onRequestAssetDelete
4058
4332
  }
4059
4333
  );
4060
- return /* @__PURE__ */ jsx(EditingSignalContext.Provider, { value: editingSignal, children: /* @__PURE__ */ jsxs("div", { className: rootClass, children: [
4061
- toc === "sidebar" ? /* @__PURE__ */ jsx(
4062
- StudioToc,
4063
- {
4064
- data: value,
4065
- selection,
4066
- onSelect: setSelection,
4067
- onAddEpisode: disabled ? void 0 : () => onEdit((p) => addEpisode(p))
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,
4334
+ return /* @__PURE__ */ jsx(EditingSignalContext.Provider, { value: editingSignal, children: /* @__PURE__ */ jsxs(
4335
+ "div",
4336
+ {
4337
+ className: rootClass,
4338
+ ref: rootRef,
4339
+ children: [
4340
+ toc === "sidebar" ? /* @__PURE__ */ jsx(
4341
+ StudioToc,
4092
4342
  {
4093
- saveStatus,
4094
- diffCount,
4095
- baselineStatus: diff?.baselineStatus,
4096
- canApplyDiff: !!(diff?.onAcceptAll || diff?.onRejectAll),
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
4343
+ data: value,
4344
+ selection,
4345
+ onSelect: setSelection,
4346
+ onAddEpisode: disabled ? void 0 : () => onEdit((p) => addEpisode(p))
4103
4347
  }
4104
- )
4105
- ] })
4106
- ] })
4107
- ] }) });
4348
+ ) : null,
4349
+ /* @__PURE__ */ jsxs("div", { className: "lj-se-main", children: [
4350
+ /* @__PURE__ */ jsx(
4351
+ CanvasTopBar,
4352
+ {
4353
+ title: value.title,
4354
+ onTitleChange: disabled ? void 0 : onTitleChange,
4355
+ titleDisabled: disabled,
4356
+ contextLabel,
4357
+ format,
4358
+ onFormatChange: setFormat,
4359
+ canUndo,
4360
+ canRedo,
4361
+ onUndo,
4362
+ onRedo,
4363
+ actions: topBarActions
4364
+ }
4365
+ ),
4366
+ /* @__PURE__ */ jsxs("div", { className: "lj-se-canvas", ref: canvasRef, children: [
4367
+ /* @__PURE__ */ jsx(
4368
+ ScriptSearchLayer,
4369
+ {
4370
+ value,
4371
+ disabled,
4372
+ rootRef,
4373
+ selection,
4374
+ onSelectionChange: setSelection,
4375
+ children: center
4376
+ }
4377
+ ),
4378
+ onAddChatReferences ? /* @__PURE__ */ jsx(QuoteLayer, { containerRef: canvasRef, onAddChatReferences }) : null,
4379
+ /* @__PURE__ */ jsx(
4380
+ SaveCapsule,
4381
+ {
4382
+ saveStatus,
4383
+ diffCount,
4384
+ baselineStatus: diff?.baselineStatus,
4385
+ canApplyDiff: !!(diff?.onAcceptAll || diff?.onRejectAll),
4386
+ onDiffPrev: changedSelections.length > 0 ? () => navDiff(-1) : void 0,
4387
+ onDiffNext: changedSelections.length > 0 ? () => navDiff(1) : void 0,
4388
+ onAcceptAll: diff?.onAcceptAll,
4389
+ onRejectAll: diff?.onRejectAll,
4390
+ returnLabel: returnTarget?.label,
4391
+ onReturn: returnTarget ? () => setSelection(returnTarget.selection) : void 0
4392
+ }
4393
+ )
4394
+ ] })
4395
+ ] })
4396
+ ]
4397
+ }
4398
+ ) });
4108
4399
  }
4109
4400
  function buildContextLabel(value, selection) {
4110
4401
  if (selection.kind === "worldview") {