@vishu1301/script-writing 0.3.8 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +228 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +228 -37
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -48,6 +48,7 @@ var blockStyles = {
|
|
|
48
48
|
inputStyle: {
|
|
49
49
|
textTransform: "uppercase",
|
|
50
50
|
fontWeight: 700,
|
|
51
|
+
maxWidth: "6.0in",
|
|
51
52
|
outline: "none",
|
|
52
53
|
whiteSpace: "pre-wrap",
|
|
53
54
|
overflowWrap: "break-word",
|
|
@@ -56,21 +57,24 @@ var blockStyles = {
|
|
|
56
57
|
},
|
|
57
58
|
ACTION: {
|
|
58
59
|
label: "Action",
|
|
59
|
-
className: "text-zinc-800
|
|
60
|
+
className: "text-zinc-800",
|
|
60
61
|
inputStyle: {
|
|
62
|
+
maxWidth: "6.0in",
|
|
61
63
|
outline: "none",
|
|
62
64
|
whiteSpace: "pre-wrap",
|
|
63
65
|
overflowWrap: "break-word",
|
|
64
66
|
wordBreak: "break-word",
|
|
65
|
-
lineHeight:
|
|
67
|
+
lineHeight: "12pt"
|
|
66
68
|
}
|
|
67
69
|
},
|
|
68
70
|
CHARACTER: {
|
|
69
71
|
label: "Character",
|
|
70
|
-
className: "uppercase font-bold text-
|
|
72
|
+
className: "uppercase font-bold text-zinc-900 tracking-widest",
|
|
71
73
|
inputStyle: {
|
|
72
74
|
textTransform: "uppercase",
|
|
73
|
-
textAlign: "
|
|
75
|
+
textAlign: "left",
|
|
76
|
+
marginLeft: "2.0in",
|
|
77
|
+
maxWidth: "4.0in",
|
|
74
78
|
fontWeight: 700,
|
|
75
79
|
letterSpacing: "0.1em",
|
|
76
80
|
outline: "none",
|
|
@@ -81,31 +85,32 @@ var blockStyles = {
|
|
|
81
85
|
},
|
|
82
86
|
PARENTHETICAL: {
|
|
83
87
|
label: "Parenthetical",
|
|
84
|
-
className: "text-
|
|
88
|
+
className: "text-zinc-600",
|
|
85
89
|
inputStyle: {
|
|
86
90
|
fontStyle: "normal",
|
|
87
|
-
maxWidth: "
|
|
88
|
-
|
|
91
|
+
maxWidth: "3.0in",
|
|
92
|
+
marginLeft: "1.5in",
|
|
93
|
+
textTransform: "lowercase",
|
|
89
94
|
outline: "none",
|
|
90
95
|
whiteSpace: "pre-wrap",
|
|
91
96
|
overflowWrap: "break-word",
|
|
92
97
|
wordBreak: "break-word",
|
|
93
|
-
textAlign: "
|
|
98
|
+
textAlign: "left"
|
|
94
99
|
}
|
|
95
100
|
},
|
|
96
101
|
DIALOGUE: {
|
|
97
102
|
label: "Dialogue",
|
|
98
|
-
className: "text-zinc-900
|
|
103
|
+
className: "text-zinc-900",
|
|
99
104
|
inputStyle: {
|
|
100
|
-
marginLeft: "
|
|
101
|
-
|
|
105
|
+
marginLeft: "1.0in",
|
|
106
|
+
maxWidth: "3.5in",
|
|
102
107
|
outline: "none",
|
|
103
108
|
whiteSpace: "pre-wrap",
|
|
104
109
|
overflowWrap: "break-word",
|
|
105
110
|
wordBreak: "break-word",
|
|
106
111
|
fontSize: "1.05rem",
|
|
107
112
|
textAlign: "left",
|
|
108
|
-
lineHeight:
|
|
113
|
+
lineHeight: "12pt"
|
|
109
114
|
}
|
|
110
115
|
},
|
|
111
116
|
TRANSITION: {
|
|
@@ -130,6 +135,8 @@ function ScreenplayEditorView({
|
|
|
130
135
|
refs,
|
|
131
136
|
focusedBlockId,
|
|
132
137
|
showSuggestions,
|
|
138
|
+
showExtensionSuggestions,
|
|
139
|
+
characterExtensions,
|
|
133
140
|
locations,
|
|
134
141
|
characters,
|
|
135
142
|
sceneNumbers,
|
|
@@ -137,6 +144,7 @@ function ScreenplayEditorView({
|
|
|
137
144
|
handleSceneTypeChange,
|
|
138
145
|
handleTimeOfDayChange,
|
|
139
146
|
handleBlockTypeChange,
|
|
147
|
+
handleSelectCharacterExtension,
|
|
140
148
|
handleKeyDown,
|
|
141
149
|
handleFocus,
|
|
142
150
|
handleBlur,
|
|
@@ -187,7 +195,7 @@ function ScreenplayEditorView({
|
|
|
187
195
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-12 w-full items-center pb-24", children: pages.map((pageBlocks, pageIndex) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
188
196
|
"div",
|
|
189
197
|
{
|
|
190
|
-
className: "relative bg-[#fdfdfc] shadow-2xl shadow-zinc-300/60 ring-1 ring-zinc-200/50 rounded-sm md:rounded-md
|
|
198
|
+
className: "relative bg-[#fdfdfc] shadow-2xl shadow-zinc-300/60 ring-1 ring-zinc-200/50 rounded-sm md:rounded-md pl-[1.5in] py-[1in] pr-[1in] flex flex-col w-[210mm] min-h-[297mm] shrink-0",
|
|
191
199
|
style: {
|
|
192
200
|
fontFamily: "var(--font-courier-prime, 'Courier New', Courier, monospace)"
|
|
193
201
|
},
|
|
@@ -229,7 +237,7 @@ function ScreenplayEditorView({
|
|
|
229
237
|
"aria-haspopup": "listbox",
|
|
230
238
|
"aria-expanded": focusedBlockId === block.id && showSuggestions && locations.length > 0,
|
|
231
239
|
spellCheck: false,
|
|
232
|
-
className: "min-w-[
|
|
240
|
+
className: "min-w-[3rem] py-1 outline-none text-base font-bold uppercase tracking-widest break-all bg-transparent",
|
|
233
241
|
onInput: (e) => handleBlockTextChange(
|
|
234
242
|
block.id,
|
|
235
243
|
e.target.innerText
|
|
@@ -351,6 +359,34 @@ function ScreenplayEditorView({
|
|
|
351
359
|
char
|
|
352
360
|
)) })
|
|
353
361
|
}
|
|
362
|
+
),
|
|
363
|
+
focusedBlockId === block.id && block.type === "CHARACTER" && showExtensionSuggestions && characterExtensions && /* @__PURE__ */ jsxRuntime.jsx(
|
|
364
|
+
"div",
|
|
365
|
+
{
|
|
366
|
+
role: "listbox",
|
|
367
|
+
id: `extension-suggestions-${block.id}`,
|
|
368
|
+
className: "absolute top-[calc(100%+8px)] left-1/2 -translate-x-1/2 w-72 z-50 bg-white border border-slate-200 shadow-2xl shadow-slate-200/60 rounded-xl py-2 overflow-hidden animate-in fade-in zoom-in-95 duration-200",
|
|
369
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-56 overflow-y-auto custom-scrollbar", children: characterExtensions.filter((ext) => {
|
|
370
|
+
const openParenIndex = block.text.lastIndexOf("(");
|
|
371
|
+
const query = openParenIndex > -1 ? block.text.substring(openParenIndex + 1).toUpperCase() : "";
|
|
372
|
+
return ext.toUpperCase().includes(query);
|
|
373
|
+
}).map((ext) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
374
|
+
"div",
|
|
375
|
+
{
|
|
376
|
+
role: "option",
|
|
377
|
+
className: "group flex items-center px-4 py-2.5 cursor-pointer transition-colors duration-150 hover:bg-slate-50 active:bg-slate-100",
|
|
378
|
+
onMouseDown: (e) => {
|
|
379
|
+
e.preventDefault();
|
|
380
|
+
handleSelectCharacterExtension(ext);
|
|
381
|
+
},
|
|
382
|
+
children: [
|
|
383
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 text-[11px] font-bold tracking-[0.1em] text-slate-600 uppercase text-left", children: ext }),
|
|
384
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "w-3 h-3 text-slate-200 opacity-0 group-hover:opacity-100 transition-all -translate-x-1 group-hover:translate-x-0" })
|
|
385
|
+
]
|
|
386
|
+
},
|
|
387
|
+
ext
|
|
388
|
+
)) })
|
|
389
|
+
}
|
|
354
390
|
)
|
|
355
391
|
] })
|
|
356
392
|
},
|
|
@@ -468,6 +504,8 @@ function createNewBlock(type) {
|
|
|
468
504
|
if (type === "SCENE_HEADING") {
|
|
469
505
|
newBlock.sceneType = "INT.";
|
|
470
506
|
newBlock.timeOfDay = "DAY";
|
|
507
|
+
} else if (type === "PARENTHETICAL") {
|
|
508
|
+
newBlock.text = "()";
|
|
471
509
|
}
|
|
472
510
|
return newBlock;
|
|
473
511
|
}
|
|
@@ -573,6 +611,7 @@ function useScreenplayEditor() {
|
|
|
573
611
|
);
|
|
574
612
|
const [newBlockId, setNewBlockId] = react.useState(null);
|
|
575
613
|
const [showSuggestions, setShowSuggestions] = react.useState(false);
|
|
614
|
+
const [showExtensionSuggestions, setShowExtensionSuggestions] = react.useState(false);
|
|
576
615
|
const blurTimeout = react.useRef(null);
|
|
577
616
|
const [isPageSplitEnabled, setIsPageSplitEnabled] = react.useState(false);
|
|
578
617
|
const [pageBreaks, setPageBreaks] = react.useState([]);
|
|
@@ -587,12 +626,23 @@ function useScreenplayEditor() {
|
|
|
587
626
|
}
|
|
588
627
|
setIsPageSplitEnabled((prev) => !prev);
|
|
589
628
|
}, [focusedBlockId]);
|
|
629
|
+
const characterExtensions = react.useMemo(
|
|
630
|
+
() => ["(V.O.)", "(O.S.)", "(O.C.)", "(SUBTITLE)", "(CONT'D)"],
|
|
631
|
+
[]
|
|
632
|
+
);
|
|
590
633
|
const locations = react.useMemo(() => {
|
|
591
634
|
const locs = blocks.filter((b) => b.type === "SCENE_HEADING" && b.text.trim() !== "").map((b) => b.text.trim().toUpperCase());
|
|
592
635
|
return [...new Set(locs)];
|
|
593
636
|
}, [blocks]);
|
|
594
637
|
const characters = react.useMemo(() => {
|
|
595
|
-
const chars = blocks.filter((b) => b.type === "CHARACTER" && b.text.trim() !== "").map((b) =>
|
|
638
|
+
const chars = blocks.filter((b) => b.type === "CHARACTER" && b.text.trim() !== "").map((b) => {
|
|
639
|
+
const text = b.text.trim().toUpperCase();
|
|
640
|
+
const parenIndex = text.indexOf("(");
|
|
641
|
+
if (parenIndex > -1) {
|
|
642
|
+
return text.substring(0, parenIndex).trim();
|
|
643
|
+
}
|
|
644
|
+
return text;
|
|
645
|
+
}).filter(Boolean);
|
|
596
646
|
return [...new Set(chars)];
|
|
597
647
|
}, [blocks]);
|
|
598
648
|
const sceneNumbers = react.useMemo(() => {
|
|
@@ -607,13 +657,22 @@ function useScreenplayEditor() {
|
|
|
607
657
|
return map;
|
|
608
658
|
}, [blocks]);
|
|
609
659
|
react.useEffect(() => {
|
|
610
|
-
var _a;
|
|
611
660
|
if (newBlockId && refs.current[newBlockId]) {
|
|
612
|
-
|
|
661
|
+
const block = blocks.find((b) => b.id === newBlockId);
|
|
662
|
+
const el = refs.current[newBlockId];
|
|
663
|
+
if (el && block) {
|
|
664
|
+
el.focus();
|
|
665
|
+
el.innerText = block.text;
|
|
666
|
+
if (block.type === "PARENTHETICAL") {
|
|
667
|
+
setTimeout(() => setCaretPosition(el, 1), 0);
|
|
668
|
+
} else {
|
|
669
|
+
setTimeout(() => setCaretPosition(el, block.text.length), 0);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
613
672
|
setFocusedBlockId(newBlockId);
|
|
614
673
|
setNewBlockId(null);
|
|
615
674
|
}
|
|
616
|
-
}, [newBlockId]);
|
|
675
|
+
}, [newBlockId, blocks]);
|
|
617
676
|
react.useEffect(() => {
|
|
618
677
|
blocks.forEach((block) => {
|
|
619
678
|
const element = refs.current[block.id];
|
|
@@ -622,6 +681,23 @@ function useScreenplayEditor() {
|
|
|
622
681
|
}
|
|
623
682
|
});
|
|
624
683
|
}, [blocks, isPageSplitEnabled, pageBreaks]);
|
|
684
|
+
react.useEffect(() => {
|
|
685
|
+
const handleClickOutside = (e) => {
|
|
686
|
+
const target = e.target;
|
|
687
|
+
const isInsideBlock = target.closest("[data-block-id]");
|
|
688
|
+
const isInsideToolbar = target.closest(".sticky");
|
|
689
|
+
const isInsideSuggestions = target.closest('[role="listbox"]');
|
|
690
|
+
if (!isInsideBlock && !isInsideToolbar && !isInsideSuggestions) {
|
|
691
|
+
setFocusedBlockId("");
|
|
692
|
+
setShowSuggestions(false);
|
|
693
|
+
setShowExtensionSuggestions(false);
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
697
|
+
return () => {
|
|
698
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
699
|
+
};
|
|
700
|
+
}, []);
|
|
625
701
|
react.useEffect(() => {
|
|
626
702
|
if (!isPageSplitEnabled) {
|
|
627
703
|
setPageBreaks([]);
|
|
@@ -714,9 +790,45 @@ function useScreenplayEditor() {
|
|
|
714
790
|
focusStateRef.current = null;
|
|
715
791
|
}
|
|
716
792
|
}, [pages]);
|
|
717
|
-
const handleBlockTextChange = react.useCallback(
|
|
718
|
-
|
|
719
|
-
|
|
793
|
+
const handleBlockTextChange = react.useCallback(
|
|
794
|
+
(id, text) => {
|
|
795
|
+
const block = blocks.find((b) => b.id === id);
|
|
796
|
+
if (!block) return;
|
|
797
|
+
if (block.type === "CHARACTER") {
|
|
798
|
+
const trimmedText = text.trim();
|
|
799
|
+
const openParenIndex = trimmedText.lastIndexOf("(");
|
|
800
|
+
const closeParenIndex = trimmedText.lastIndexOf(")");
|
|
801
|
+
if (openParenIndex !== -1 && openParenIndex > closeParenIndex) {
|
|
802
|
+
setShowExtensionSuggestions(true);
|
|
803
|
+
setShowSuggestions(false);
|
|
804
|
+
} else {
|
|
805
|
+
setShowExtensionSuggestions(false);
|
|
806
|
+
setShowSuggestions(openParenIndex === -1);
|
|
807
|
+
}
|
|
808
|
+
} else if (showExtensionSuggestions) {
|
|
809
|
+
setShowExtensionSuggestions(false);
|
|
810
|
+
}
|
|
811
|
+
let processedText = text;
|
|
812
|
+
if (block.type === "PARENTHETICAL") {
|
|
813
|
+
const clean = text.replace(/[()]/g, "");
|
|
814
|
+
processedText = `(${clean})`;
|
|
815
|
+
}
|
|
816
|
+
setBlocks(
|
|
817
|
+
(bs) => updateBlock(bs, id, "text", processedText)
|
|
818
|
+
);
|
|
819
|
+
if (text !== processedText) {
|
|
820
|
+
const el = refs.current[id];
|
|
821
|
+
if (el) {
|
|
822
|
+
const offset = getCaretCharacterOffsetWithin(el);
|
|
823
|
+
const charsBeforeCaret = text.substring(0, offset).replace(/[()]/g, "").length;
|
|
824
|
+
const newOffset = 1 + charsBeforeCaret;
|
|
825
|
+
el.innerText = processedText;
|
|
826
|
+
setCaretPosition(el, newOffset);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
},
|
|
830
|
+
[blocks, showExtensionSuggestions]
|
|
831
|
+
);
|
|
720
832
|
const handleSceneTypeChange = react.useCallback(
|
|
721
833
|
(id, sceneType) => {
|
|
722
834
|
setBlocks(
|
|
@@ -747,13 +859,48 @@ function useScreenplayEditor() {
|
|
|
747
859
|
setTimeout(() => {
|
|
748
860
|
const el = refs.current[focusedBlockId];
|
|
749
861
|
if (el) {
|
|
750
|
-
el.innerText = "";
|
|
751
862
|
el.focus();
|
|
863
|
+
const newBlock = createNewBlock(newType);
|
|
864
|
+
el.innerText = newBlock.text;
|
|
865
|
+
if (newType === "PARENTHETICAL") {
|
|
866
|
+
setCaretPosition(el, 1);
|
|
867
|
+
} else {
|
|
868
|
+
setCaretPosition(el, newBlock.text.length);
|
|
869
|
+
}
|
|
752
870
|
}
|
|
753
871
|
}, 0);
|
|
754
872
|
},
|
|
755
873
|
[focusedBlockId]
|
|
756
874
|
);
|
|
875
|
+
const handleSelectCharacterExtension = react.useCallback(
|
|
876
|
+
(extension) => {
|
|
877
|
+
if (!focusedBlockId) return;
|
|
878
|
+
setBlocks((currentBlocks) => {
|
|
879
|
+
const block = currentBlocks.find((b) => b.id === focusedBlockId);
|
|
880
|
+
if (!block || block.type !== "CHARACTER") return currentBlocks;
|
|
881
|
+
const parenIndex = block.text.indexOf("(");
|
|
882
|
+
const baseText = (parenIndex > -1 ? block.text.substring(0, parenIndex) : block.text).trim();
|
|
883
|
+
const newText = `${baseText} ${extension}`;
|
|
884
|
+
const newBlocks = updateBlock(
|
|
885
|
+
currentBlocks,
|
|
886
|
+
focusedBlockId,
|
|
887
|
+
"text",
|
|
888
|
+
newText
|
|
889
|
+
);
|
|
890
|
+
setTimeout(() => {
|
|
891
|
+
const el = refs.current[focusedBlockId];
|
|
892
|
+
if (el) {
|
|
893
|
+
el.innerText = newText;
|
|
894
|
+
el.focus();
|
|
895
|
+
setCaretPosition(el, newText.length);
|
|
896
|
+
}
|
|
897
|
+
}, 0);
|
|
898
|
+
return newBlocks;
|
|
899
|
+
});
|
|
900
|
+
setShowExtensionSuggestions(false);
|
|
901
|
+
},
|
|
902
|
+
[focusedBlockId]
|
|
903
|
+
);
|
|
757
904
|
const focusBlock = (id, position = "start") => {
|
|
758
905
|
const el = refs.current[id];
|
|
759
906
|
if (!el) return;
|
|
@@ -767,19 +914,44 @@ function useScreenplayEditor() {
|
|
|
767
914
|
sel.addRange(range);
|
|
768
915
|
};
|
|
769
916
|
const cycleBlockType = (id, direction) => {
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
917
|
+
const block = blocks.find((b) => b.id === id);
|
|
918
|
+
if (!block) return;
|
|
919
|
+
const idx = blockTypes.indexOf(block.type);
|
|
920
|
+
let newIdx = direction === "up" ? idx - 1 : idx + 1;
|
|
921
|
+
if (newIdx < 0) newIdx = blockTypes.length - 1;
|
|
922
|
+
if (newIdx >= blockTypes.length) newIdx = 0;
|
|
923
|
+
const newType = blockTypes[newIdx];
|
|
924
|
+
setBlocks((bs) => changeBlockType(bs, id, newType));
|
|
925
|
+
setTimeout(() => {
|
|
926
|
+
const el = refs.current[id];
|
|
927
|
+
if (el) {
|
|
928
|
+
el.focus();
|
|
929
|
+
const newBlock = createNewBlock(newType);
|
|
930
|
+
el.innerText = newBlock.text;
|
|
931
|
+
if (newType === "PARENTHETICAL") {
|
|
932
|
+
setCaretPosition(el, 1);
|
|
933
|
+
} else {
|
|
934
|
+
setCaretPosition(el, el.innerText.length);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
}, 10);
|
|
779
938
|
};
|
|
780
939
|
const handleKeyDown = react.useCallback(
|
|
781
940
|
(e, id, text) => {
|
|
782
941
|
var _a;
|
|
942
|
+
const block = blocks.find((b) => b.id === id);
|
|
943
|
+
if ((block == null ? void 0 : block.type) === "PARENTHETICAL") {
|
|
944
|
+
const offset = getCaretCharacterOffsetWithin(e.currentTarget);
|
|
945
|
+
if (e.key === "Backspace" && (offset <= 1 || text === "()")) {
|
|
946
|
+
e.preventDefault();
|
|
947
|
+
cycleBlockType(id, "up");
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
if (e.key === "Delete" && (offset >= text.length - 1 || text === "()")) {
|
|
951
|
+
e.preventDefault();
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
783
955
|
if ((e.key === "Backspace" || e.key === "Delete") && text.length <= 1) {
|
|
784
956
|
e.preventDefault();
|
|
785
957
|
const { newBlocks, nextFocusedId } = deleteBlock(
|
|
@@ -805,11 +977,9 @@ function useScreenplayEditor() {
|
|
|
805
977
|
} else if (e.key === "ArrowUp" && e.ctrlKey) {
|
|
806
978
|
e.preventDefault();
|
|
807
979
|
cycleBlockType(id, "up");
|
|
808
|
-
requestAnimationFrame(() => focusBlock(id));
|
|
809
980
|
} else if (e.key === "ArrowDown" && e.ctrlKey) {
|
|
810
981
|
e.preventDefault();
|
|
811
982
|
cycleBlockType(id, "down");
|
|
812
|
-
requestAnimationFrame(() => focusBlock(id));
|
|
813
983
|
} else if (e.key === "ArrowUp" && !e.ctrlKey) {
|
|
814
984
|
const selection = window.getSelection();
|
|
815
985
|
if (!selection || !selection.isCollapsed || selection.rangeCount === 0) {
|
|
@@ -886,18 +1056,36 @@ function useScreenplayEditor() {
|
|
|
886
1056
|
}
|
|
887
1057
|
}
|
|
888
1058
|
},
|
|
889
|
-
[blocks]
|
|
1059
|
+
[blocks, handleBlockTextChange]
|
|
890
1060
|
);
|
|
891
1061
|
const handleFocus = react.useCallback((id) => {
|
|
892
1062
|
if (blurTimeout.current) {
|
|
893
1063
|
clearTimeout(blurTimeout.current);
|
|
894
1064
|
}
|
|
895
1065
|
setFocusedBlockId(id);
|
|
896
|
-
|
|
897
|
-
|
|
1066
|
+
const block = blocks.find((b) => b.id === id);
|
|
1067
|
+
if ((block == null ? void 0 : block.type) === "CHARACTER") {
|
|
1068
|
+
const trimmedText = block.text.trim();
|
|
1069
|
+
const openParenIndex = trimmedText.lastIndexOf("(");
|
|
1070
|
+
const closeParenIndex = trimmedText.lastIndexOf(")");
|
|
1071
|
+
if (openParenIndex !== -1 && openParenIndex > closeParenIndex) {
|
|
1072
|
+
setShowExtensionSuggestions(true);
|
|
1073
|
+
setShowSuggestions(false);
|
|
1074
|
+
} else {
|
|
1075
|
+
setShowExtensionSuggestions(false);
|
|
1076
|
+
setShowSuggestions(openParenIndex === -1);
|
|
1077
|
+
}
|
|
1078
|
+
} else {
|
|
1079
|
+
setShowSuggestions(true);
|
|
1080
|
+
setShowExtensionSuggestions(false);
|
|
1081
|
+
}
|
|
1082
|
+
}, [blocks]);
|
|
898
1083
|
const handleBlur = react.useCallback((id) => {
|
|
899
1084
|
if (document.activeElement === refs.current[id]) return;
|
|
900
|
-
blurTimeout.current = setTimeout(() =>
|
|
1085
|
+
blurTimeout.current = setTimeout(() => {
|
|
1086
|
+
setShowSuggestions(false);
|
|
1087
|
+
setShowExtensionSuggestions(false);
|
|
1088
|
+
}, 200);
|
|
901
1089
|
}, []);
|
|
902
1090
|
return {
|
|
903
1091
|
blocks,
|
|
@@ -907,6 +1095,8 @@ function useScreenplayEditor() {
|
|
|
907
1095
|
refs,
|
|
908
1096
|
focusedBlockId,
|
|
909
1097
|
showSuggestions,
|
|
1098
|
+
showExtensionSuggestions,
|
|
1099
|
+
characterExtensions,
|
|
910
1100
|
locations,
|
|
911
1101
|
characters,
|
|
912
1102
|
sceneNumbers,
|
|
@@ -914,6 +1104,7 @@ function useScreenplayEditor() {
|
|
|
914
1104
|
handleSceneTypeChange,
|
|
915
1105
|
handleTimeOfDayChange,
|
|
916
1106
|
handleBlockTypeChange,
|
|
1107
|
+
handleSelectCharacterExtension,
|
|
917
1108
|
handleKeyDown,
|
|
918
1109
|
handleFocus,
|
|
919
1110
|
handleBlur
|