@vishu1301/script-writing 0.4.2 → 0.4.3

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 CHANGED
@@ -3,6 +3,27 @@
3
3
  var react = require('react');
4
4
  var lucideReact = require('lucide-react');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
+ var pdfjs = require('pdfjs-dist');
7
+
8
+ function _interopNamespace(e) {
9
+ if (e && e.__esModule) return e;
10
+ var n = Object.create(null);
11
+ if (e) {
12
+ Object.keys(e).forEach(function (k) {
13
+ if (k !== 'default') {
14
+ var d = Object.getOwnPropertyDescriptor(e, k);
15
+ Object.defineProperty(n, k, d.get ? d : {
16
+ enumerable: true,
17
+ get: function () { return e[k]; }
18
+ });
19
+ }
20
+ });
21
+ }
22
+ n.default = e;
23
+ return Object.freeze(n);
24
+ }
25
+
26
+ var pdfjs__namespace = /*#__PURE__*/_interopNamespace(pdfjs);
6
27
 
7
28
  var __defProp = Object.defineProperty;
8
29
  var __defProps = Object.defineProperties;
@@ -126,6 +147,99 @@ var blockStyles = {
126
147
  }
127
148
  }
128
149
  };
150
+ pdfjs__namespace.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs__namespace.version}/build/pdf.worker.min.mjs`;
151
+ function PdfImporter({ onScriptImported, children }) {
152
+ const [isProcessing, setIsProcessing] = react.useState(false);
153
+ const [error, setError] = react.useState(null);
154
+ const fileInputRef = react.useRef(null);
155
+ const handleFileChange = async (event) => {
156
+ var _a;
157
+ const file = (_a = event.target.files) == null ? void 0 : _a[0];
158
+ if (!file) {
159
+ return;
160
+ }
161
+ setIsProcessing(true);
162
+ setError(null);
163
+ try {
164
+ const arrayBuffer = await file.arrayBuffer();
165
+ const pdf = await pdfjs__namespace.getDocument(arrayBuffer).promise;
166
+ const processPage = async (pageNumber) => {
167
+ const page = await pdf.getPage(pageNumber);
168
+ const content = await page.getTextContent();
169
+ const items = content.items.filter((item) => "str" in item && item.str.trim().length > 0);
170
+ if (items.length === 0) return "";
171
+ const lines = [];
172
+ for (const item of items) {
173
+ let found = false;
174
+ for (const line of lines) {
175
+ if (Math.abs(line.y - item.transform[5]) < 5) {
176
+ line.items.push({ x: item.transform[4], text: item.str });
177
+ found = true;
178
+ break;
179
+ }
180
+ }
181
+ if (!found) {
182
+ lines.push({ y: item.transform[5], items: [{ x: item.transform[4], text: item.str }] });
183
+ }
184
+ }
185
+ lines.sort((a, b) => b.y - a.y);
186
+ return lines.map((line) => {
187
+ line.items.sort((a, b) => a.x - b.x);
188
+ return line.items.map((item) => item.text).join(" ");
189
+ }).join("\n");
190
+ };
191
+ let title = "";
192
+ if (pdf.numPages > 0) {
193
+ title = await processPage(1);
194
+ }
195
+ let scriptContent = "";
196
+ for (let i = 2; i <= pdf.numPages; i++) {
197
+ scriptContent += await processPage(i) + "\n\n";
198
+ }
199
+ onScriptImported(title.trim(), scriptContent);
200
+ } catch (err) {
201
+ console.error("Error processing PDF:", err);
202
+ setError(err instanceof Error ? `Error processing PDF: ${err.message}` : "An unknown error occurred.");
203
+ } finally {
204
+ setIsProcessing(false);
205
+ if (event.target) {
206
+ event.target.value = "";
207
+ }
208
+ }
209
+ };
210
+ const handleClick = () => {
211
+ var _a;
212
+ (_a = fileInputRef.current) == null ? void 0 : _a.click();
213
+ };
214
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
215
+ /* @__PURE__ */ jsxRuntime.jsx(
216
+ "input",
217
+ {
218
+ ref: fileInputRef,
219
+ type: "file",
220
+ accept: "application/pdf",
221
+ onChange: handleFileChange,
222
+ disabled: isProcessing,
223
+ className: "hidden",
224
+ id: "pdf-importer-input"
225
+ }
226
+ ),
227
+ /* @__PURE__ */ jsxRuntime.jsx(
228
+ "button",
229
+ {
230
+ onClick: handleClick,
231
+ disabled: isProcessing,
232
+ className: "flex items-center justify-center gap-2 w-auto px-4 h-12 rounded-full bg-zinc-950 text-white shadow-xl shadow-zinc-900/20 border border-white/10 hover:bg-zinc-800 hover:scale-105 active:scale-95 transition-all duration-300",
233
+ "aria-label": "Import Script from PDF",
234
+ children: isProcessing ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold", children: "Processing..." }) : children
235
+ }
236
+ ),
237
+ error && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "sr-only", children: [
238
+ "Error: ",
239
+ error
240
+ ] })
241
+ ] });
242
+ }
129
243
  function ScreenplayEditorView({
130
244
  blocks,
131
245
  pages,
@@ -147,9 +261,11 @@ function ScreenplayEditorView({
147
261
  handleKeyDown,
148
262
  handleFocus,
149
263
  handleBlur,
264
+ handleScriptImport,
150
265
  onSave,
151
266
  onSaveAsPdf,
152
- onSyncWithCloud
267
+ onSyncWithCloud,
268
+ handleSceneNumberChange
153
269
  }) {
154
270
  const [isRulesOpen, setIsRulesOpen] = react.useState(false);
155
271
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -208,7 +324,26 @@ function ScreenplayEditorView({
208
324
  className: `relative rounded-sm transition-all duration-200 outline-none ${focusedBlockId === block.id ? "bg-zinc-100/50" : "bg-transparent"}`,
209
325
  children: block.type === "SCENE_HEADING" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
210
326
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 px-4 py-1 bg-transparent", children: [
211
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -left-16 top-2 w-12 text-right text-zinc-400 font-semibold select-none", children: String(sceneNumbers[block.id] || 0) }),
327
+ /* @__PURE__ */ jsxRuntime.jsx(
328
+ "input",
329
+ {
330
+ className: "absolute -left-16 top-2 w-12 text-right text-zinc-400 font-semibold select-none bg-transparent outline-none focus:ring-1 focus:ring-blue-400 rounded-sm",
331
+ spellCheck: false,
332
+ value: block.sceneNumber || "",
333
+ onChange: (e) => handleSceneNumberChange(
334
+ block.id,
335
+ e.target.value.toUpperCase()
336
+ ),
337
+ onFocus: () => handleFocus(block.id),
338
+ onBlur: () => handleBlur(block.id),
339
+ onKeyDown: (e) => {
340
+ if (e.key === "Enter" || e.key === "Backspace") {
341
+ e.stopPropagation();
342
+ }
343
+ },
344
+ "aria-label": "Scene Number"
345
+ }
346
+ ),
212
347
  /* @__PURE__ */ jsxRuntime.jsxs(
213
348
  "select",
214
349
  {
@@ -401,6 +536,10 @@ function ScreenplayEditorView({
401
536
  pageIndex
402
537
  )) }),
403
538
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed bottom-6 right-6 flex flex-col items-end gap-4 z-50", children: [
539
+ /* @__PURE__ */ jsxRuntime.jsx(PdfImporter, { onScriptImported: handleScriptImport, children: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
540
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { className: "w-5 h-5" }),
541
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold", children: "Import" })
542
+ ] }) }),
404
543
  onSave && /* @__PURE__ */ jsxRuntime.jsxs(
405
544
  "button",
406
545
  {
@@ -503,6 +642,7 @@ function createNewBlock(type) {
503
642
  if (type === "SCENE_HEADING") {
504
643
  newBlock.sceneType = "INT.";
505
644
  newBlock.timeOfDay = "DAY";
645
+ newBlock.sceneNumber = "";
506
646
  } else if (type === "PARENTHETICAL") {
507
647
  newBlock.text = "()";
508
648
  }
@@ -510,12 +650,12 @@ function createNewBlock(type) {
510
650
  }
511
651
  function addBlockAfter(blocks, currentBlockId) {
512
652
  const currentIndex = blocks.findIndex((b) => b.id === currentBlockId);
513
- if (currentIndex === -1) {
514
- return { newBlocks: blocks, newBlockId: "" };
515
- }
516
- const currentBlock = blocks[currentIndex];
517
- const nextType = getNextBlockType(currentBlock.type);
653
+ if (currentIndex === -1) return { newBlocks: blocks, newBlockId: "" };
654
+ const nextType = getNextBlockType(blocks[currentIndex].type);
518
655
  const newBlock = createNewBlock(nextType);
656
+ if (nextType === "SCENE_HEADING") {
657
+ newBlock.sceneNumber = generateNextSceneNumber(blocks, currentIndex);
658
+ }
519
659
  const newBlocks = [
520
660
  ...blocks.slice(0, currentIndex + 1),
521
661
  newBlock,
@@ -540,10 +680,142 @@ function deleteBlock(blocks, blockIdToDelete) {
540
680
  function updateBlock(blocks, id, key, value) {
541
681
  return blocks.map((b) => b.id === id ? __spreadProps(__spreadValues({}, b), { [key]: value }) : b);
542
682
  }
683
+ var generateNextSceneNumber = (blocks, currentIndex) => {
684
+ const prevScenes = blocks.slice(0, currentIndex + 1).filter((b) => b.type === "SCENE_HEADING");
685
+ if (prevScenes.length === 0) return "1";
686
+ const lastScene = prevScenes[prevScenes.length - 1];
687
+ const lastNum = lastScene.sceneNumber || "1";
688
+ const match = lastNum.match(/^(\d+)([A-Z]*)$/);
689
+ if (!match) return "1";
690
+ const baseNumber = match[1];
691
+ const currentSuffix = match[2];
692
+ const scenesAfter = blocks.slice(currentIndex + 1).filter((b) => b.type === "SCENE_HEADING");
693
+ if (scenesAfter.length > 0) {
694
+ if (currentSuffix === "") {
695
+ return `${baseNumber}A`;
696
+ } else {
697
+ return `${baseNumber}${incrementChar(currentSuffix)}`;
698
+ }
699
+ }
700
+ return String(parseInt(baseNumber) + 1);
701
+ };
702
+ function incrementChar(text) {
703
+ if (text === "") return "A";
704
+ const lastChar = text.slice(-1);
705
+ const remaining = text.slice(0, -1);
706
+ if (lastChar === "Z") {
707
+ return incrementChar(remaining) + "A";
708
+ }
709
+ return remaining + String.fromCharCode(lastChar.charCodeAt(0) + 1);
710
+ }
543
711
  function changeBlockType(blocks, id, newType) {
712
+ const currentIndex = blocks.findIndex((b) => b.id === id);
713
+ if (currentIndex === -1) return blocks;
544
714
  const newBlock = createNewBlock(newType);
715
+ if (newType === "SCENE_HEADING") {
716
+ newBlock.sceneNumber = generateNextSceneNumber(blocks, currentIndex);
717
+ }
545
718
  return blocks.map((b) => b.id === id ? __spreadProps(__spreadValues({}, newBlock), { id: b.id }) : b);
546
719
  }
720
+ function parseScreenplayText(content) {
721
+ const lines = content.split("\n");
722
+ const blocks = [];
723
+ let lastBlock = null;
724
+ let activeSpeaker = null;
725
+ for (let i = 0; i < lines.length; i++) {
726
+ let originalLine = lines[i];
727
+ let trimmedLine = lines[i].trim().replace(/\s+/g, " ");
728
+ const leadingSpaces = originalLine.search(/\S/);
729
+ if (lines[i].trim() === "") {
730
+ activeSpeaker = null;
731
+ continue;
732
+ }
733
+ const isPageNumber = /^\d+$/.test(trimmedLine) || /^(PAGE|pg\.?)\s?\d+$/i.test(trimmedLine) || /^[\d]+\.$/.test(trimmedLine);
734
+ if (trimmedLine.length === 0 || isPageNumber) continue;
735
+ let currentBlockType = null;
736
+ const isAllUpperCase = trimmedLine === trimmedLine.toUpperCase() && /[A-Z]/.test(trimmedLine);
737
+ const sceneHeadingStartRegex = /^(?:\d+[A-Z]?\.?\s*)?(INT\.?\/EXT\.?|I\/E|INT|EXT|EST\.)\b/i;
738
+ const isTransition = isAllUpperCase && (trimmedLine.endsWith(" TO:") || ["FADE IN:", "FADE OUT.", "CUT TO BLACK."].includes(trimmedLine));
739
+ if (sceneHeadingStartRegex.test(trimmedLine)) {
740
+ currentBlockType = "SCENE_HEADING";
741
+ activeSpeaker = null;
742
+ } else if (isTransition) {
743
+ currentBlockType = "TRANSITION";
744
+ activeSpeaker = null;
745
+ } else if (isAllUpperCase && !trimmedLine.startsWith("(")) {
746
+ let nextLine = "";
747
+ for (let j = i + 1; j < lines.length; j++) {
748
+ const nextTrimmed = lines[j].trim();
749
+ const nextIsPage = /^\d+$/.test(nextTrimmed) || /^(PAGE|pg\.?)\s?\d+$/i.test(nextTrimmed);
750
+ if (nextTrimmed.length > 0 && !nextIsPage) {
751
+ nextLine = nextTrimmed;
752
+ break;
753
+ }
754
+ }
755
+ if (nextLine && (nextLine.startsWith("(") || nextLine !== nextLine.toUpperCase()) || /\s\(.*\)$/.test(trimmedLine)) {
756
+ currentBlockType = "CHARACTER";
757
+ activeSpeaker = trimmedLine;
758
+ } else {
759
+ currentBlockType = "ACTION";
760
+ activeSpeaker = null;
761
+ }
762
+ } else if (trimmedLine.startsWith("(") && trimmedLine.endsWith(")")) {
763
+ currentBlockType = "PARENTHETICAL";
764
+ } else if (activeSpeaker && ((lastBlock == null ? void 0 : lastBlock.type) === "CHARACTER" || (lastBlock == null ? void 0 : lastBlock.type) === "PARENTHETICAL" || (lastBlock == null ? void 0 : lastBlock.type) === "DIALOGUE")) {
765
+ const isLastLineComplete = /[.!?]"?$/.test(lastBlock.text.trim());
766
+ if (leadingSpaces < 15 && (lastBlock == null ? void 0 : lastBlock.type) !== "CHARACTER" && isLastLineComplete) {
767
+ currentBlockType = "ACTION";
768
+ activeSpeaker = null;
769
+ } else {
770
+ currentBlockType = "DIALOGUE";
771
+ }
772
+ } else {
773
+ currentBlockType = "ACTION";
774
+ activeSpeaker = null;
775
+ }
776
+ if (lastBlock && lastBlock.type === currentBlockType && (currentBlockType === "ACTION" || currentBlockType === "DIALOGUE") && !(currentBlockType === "DIALOGUE" && !activeSpeaker)) {
777
+ lastBlock.text += " " + trimmedLine;
778
+ } else {
779
+ const newBlock = createNewBlock(currentBlockType);
780
+ if (currentBlockType === "SCENE_HEADING") {
781
+ let workingLine = trimmedLine;
782
+ const sceneNumMatch = workingLine.match(/^(\d+[A-Z]*)\.?\s+/i);
783
+ if (sceneNumMatch) {
784
+ newBlock.sceneNumber = String(sceneNumMatch[1]).toUpperCase();
785
+ workingLine = workingLine.replace(/^(\d+[A-Z]*)\.?\s+/i, "").trim();
786
+ } else {
787
+ newBlock.sceneNumber = "";
788
+ }
789
+ const typeMatch = workingLine.match(
790
+ /^(INT\.?\/EXT\.?|I\/E|INT|EXT|EST\.)/i
791
+ );
792
+ if (typeMatch) {
793
+ let sType = typeMatch[0].toUpperCase().replace(/[^A-Z/.]/g, "");
794
+ if (!sType.endsWith(".")) sType += ".";
795
+ newBlock.sceneType = sType;
796
+ workingLine = workingLine.substring(typeMatch[0].length).trim();
797
+ }
798
+ const suffixRegex = /[.\-\s]+(DAY|NIGHT|CONTINUOUS|LATER|MORNING|EVENING|DUSK|DAWN|MORN|AFT|SUNSET|SUNRISE)(?:\s+.*)*$/i;
799
+ const suffixMatch = workingLine.match(suffixRegex);
800
+ if (suffixMatch) {
801
+ const rawTime = suffixMatch[1].toUpperCase();
802
+ const nightKeys = ["NIGHT", "EVENING", "DUSK", "SUNSET"];
803
+ newBlock.timeOfDay = nightKeys.includes(rawTime) ? "NIGHT" : "DAY";
804
+ workingLine = workingLine.substring(0, suffixMatch.index).trim();
805
+ } else {
806
+ workingLine = workingLine.replace(/\s+\d+(\s+\d+)*$/, "").trim();
807
+ newBlock.timeOfDay = "DAY";
808
+ }
809
+ newBlock.text = workingLine.replace(/^[-.\s]+/, "").replace(/[-.\s]+$/, "").toUpperCase();
810
+ } else {
811
+ newBlock.text = trimmedLine;
812
+ }
813
+ blocks.push(newBlock);
814
+ lastBlock = newBlock;
815
+ }
816
+ }
817
+ return blocks.length > 0 ? blocks : [createNewBlock("SCENE_HEADING")];
818
+ }
547
819
 
548
820
  // app/hook/use-screenplay-editor.ts
549
821
  var initialBlocks = [
@@ -551,6 +823,7 @@ var initialBlocks = [
551
823
  id: uuid(),
552
824
  type: "SCENE_HEADING",
553
825
  text: "",
826
+ sceneNumber: "1",
554
827
  sceneType: "INT.",
555
828
  timeOfDay: "DAY"
556
829
  }
@@ -629,6 +902,16 @@ function useScreenplayEditor() {
629
902
  () => ["(V.O.)", "(O.S.)", "(O.C.)", "(SUBTITLE)", "(CONT'D)"],
630
903
  []
631
904
  );
905
+ const handleSceneNumberChange = react.useCallback(
906
+ (id, newNumber) => {
907
+ setBlocks(
908
+ (prevBlocks) => prevBlocks.map(
909
+ (block) => block.id === id ? __spreadProps(__spreadValues({}, block), { sceneNumber: newNumber.toUpperCase() }) : block
910
+ )
911
+ );
912
+ },
913
+ []
914
+ );
632
915
  const locations = react.useMemo(() => {
633
916
  const locs = blocks.filter((b) => b.type === "SCENE_HEADING" && b.text.trim() !== "").map((b) => b.text.trim().toUpperCase());
634
917
  return [...new Set(locs)];
@@ -646,15 +929,32 @@ function useScreenplayEditor() {
646
929
  }, [blocks]);
647
930
  const sceneNumbers = react.useMemo(() => {
648
931
  const map = {};
649
- let count = 0;
932
+ let fallbackCount = 0;
650
933
  blocks.forEach((block) => {
651
934
  if (block.type === "SCENE_HEADING") {
652
- count++;
653
- map[block.id] = count;
935
+ if (block.sceneNumber) {
936
+ map[block.id] = block.sceneNumber;
937
+ const base = parseInt(block.sceneNumber);
938
+ if (!isNaN(base)) fallbackCount = Math.max(fallbackCount, base);
939
+ } else {
940
+ fallbackCount++;
941
+ map[block.id] = String(fallbackCount);
942
+ }
654
943
  }
655
944
  });
656
945
  return map;
657
946
  }, [blocks]);
947
+ react.useCallback(() => {
948
+ let count = 1;
949
+ setBlocks(
950
+ (prev) => prev.map((b) => {
951
+ if (b.type === "SCENE_HEADING") {
952
+ return __spreadProps(__spreadValues({}, b), { sceneNumber: String(count++) });
953
+ }
954
+ return b;
955
+ })
956
+ );
957
+ }, []);
658
958
  react.useEffect(() => {
659
959
  if (newBlockId && refs.current[newBlockId]) {
660
960
  const block = blocks.find((b) => b.id === newBlockId);
@@ -1057,28 +1357,62 @@ function useScreenplayEditor() {
1057
1357
  },
1058
1358
  [blocks, handleBlockTextChange]
1059
1359
  );
1060
- const handleFocus = react.useCallback((id) => {
1061
- if (blurTimeout.current) {
1062
- clearTimeout(blurTimeout.current);
1063
- }
1064
- setFocusedBlockId(id);
1065
- const block = blocks.find((b) => b.id === id);
1066
- if ((block == null ? void 0 : block.type) === "CHARACTER") {
1067
- const trimmedText = block.text.trim();
1068
- const openParenIndex = trimmedText.lastIndexOf("(");
1069
- const closeParenIndex = trimmedText.lastIndexOf(")");
1070
- if (openParenIndex !== -1 && openParenIndex > closeParenIndex) {
1071
- setShowExtensionSuggestions(true);
1072
- setShowSuggestions(false);
1360
+ const handleScriptImport = react.useCallback(
1361
+ (title, content) => {
1362
+ const parsedBlocks = parseScreenplayText(content);
1363
+ if (parsedBlocks.length > 0) {
1364
+ let fallbackCount = 1;
1365
+ const finalizedBlocks = parsedBlocks.map((block) => {
1366
+ if (block.type === "SCENE_HEADING") {
1367
+ if (block.sceneNumber && block.sceneNumber.trim().length > 0) {
1368
+ const isPureNumber = /^\d+$/.test(block.sceneNumber);
1369
+ if (isPureNumber) {
1370
+ fallbackCount = parseInt(block.sceneNumber) + 1;
1371
+ }
1372
+ return block;
1373
+ }
1374
+ return __spreadProps(__spreadValues({}, block), { sceneNumber: String(fallbackCount++) });
1375
+ }
1376
+ return block;
1377
+ });
1378
+ setBlocks(finalizedBlocks);
1379
+ setTimeout(() => {
1380
+ var _a;
1381
+ const firstId = parsedBlocks[0].id;
1382
+ if (firstId && refs.current[firstId]) {
1383
+ setFocusedBlockId(firstId);
1384
+ (_a = refs.current[firstId]) == null ? void 0 : _a.focus();
1385
+ }
1386
+ }, 100);
1387
+ }
1388
+ },
1389
+ [refs]
1390
+ );
1391
+ const handleFocus = react.useCallback(
1392
+ (id) => {
1393
+ if (blurTimeout.current) {
1394
+ clearTimeout(blurTimeout.current);
1395
+ }
1396
+ setFocusedBlockId(id);
1397
+ const block = blocks.find((b) => b.id === id);
1398
+ if ((block == null ? void 0 : block.type) === "CHARACTER") {
1399
+ const trimmedText = block.text.trim();
1400
+ const openParenIndex = trimmedText.lastIndexOf("(");
1401
+ const closeParenIndex = trimmedText.lastIndexOf(")");
1402
+ if (openParenIndex !== -1 && openParenIndex > closeParenIndex) {
1403
+ setShowExtensionSuggestions(true);
1404
+ setShowSuggestions(false);
1405
+ } else {
1406
+ setShowExtensionSuggestions(false);
1407
+ setShowSuggestions(openParenIndex === -1);
1408
+ }
1073
1409
  } else {
1410
+ setShowSuggestions(true);
1074
1411
  setShowExtensionSuggestions(false);
1075
- setShowSuggestions(openParenIndex === -1);
1076
1412
  }
1077
- } else {
1078
- setShowSuggestions(true);
1079
- setShowExtensionSuggestions(false);
1080
- }
1081
- }, [blocks]);
1413
+ },
1414
+ [blocks]
1415
+ );
1082
1416
  const handleBlur = react.useCallback((id) => {
1083
1417
  if (document.activeElement === refs.current[id]) return;
1084
1418
  blurTimeout.current = setTimeout(() => {
@@ -1105,6 +1439,8 @@ function useScreenplayEditor() {
1105
1439
  handleBlockTypeChange,
1106
1440
  handleSelectCharacterExtension,
1107
1441
  handleKeyDown,
1442
+ handleScriptImport,
1443
+ handleSceneNumberChange,
1108
1444
  handleFocus,
1109
1445
  handleBlur
1110
1446
  };