@vishu1301/script-writing 0.3.9 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -69,7 +69,7 @@ var blockStyles = {
69
69
  },
70
70
  CHARACTER: {
71
71
  label: "Character",
72
- className: "uppercase font-bold text-zinc-900 tracking-widest",
72
+ className: "uppercase text-zinc-900 tracking-widest",
73
73
  inputStyle: {
74
74
  textTransform: "uppercase",
75
75
  textAlign: "left",
@@ -117,7 +117,6 @@ var blockStyles = {
117
117
  label: "Transition",
118
118
  className: "uppercase font-bold text-right text-zinc-900",
119
119
  inputStyle: {
120
- marginLeft: "4.0in",
121
120
  textTransform: "uppercase",
122
121
  fontWeight: 600,
123
122
  textAlign: "right",
@@ -136,6 +135,8 @@ function ScreenplayEditorView({
136
135
  refs,
137
136
  focusedBlockId,
138
137
  showSuggestions,
138
+ showExtensionSuggestions,
139
+ characterExtensions,
139
140
  locations,
140
141
  characters,
141
142
  sceneNumbers,
@@ -143,6 +144,7 @@ function ScreenplayEditorView({
143
144
  handleSceneTypeChange,
144
145
  handleTimeOfDayChange,
145
146
  handleBlockTypeChange,
147
+ handleSelectCharacterExtension,
146
148
  handleKeyDown,
147
149
  handleFocus,
148
150
  handleBlur,
@@ -357,6 +359,34 @@ function ScreenplayEditorView({
357
359
  char
358
360
  )) })
359
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
+ }
360
390
  )
361
391
  ] })
362
392
  },
@@ -581,6 +611,7 @@ function useScreenplayEditor() {
581
611
  );
582
612
  const [newBlockId, setNewBlockId] = react.useState(null);
583
613
  const [showSuggestions, setShowSuggestions] = react.useState(false);
614
+ const [showExtensionSuggestions, setShowExtensionSuggestions] = react.useState(false);
584
615
  const blurTimeout = react.useRef(null);
585
616
  const [isPageSplitEnabled, setIsPageSplitEnabled] = react.useState(false);
586
617
  const [pageBreaks, setPageBreaks] = react.useState([]);
@@ -595,12 +626,23 @@ function useScreenplayEditor() {
595
626
  }
596
627
  setIsPageSplitEnabled((prev) => !prev);
597
628
  }, [focusedBlockId]);
629
+ const characterExtensions = react.useMemo(
630
+ () => ["(V.O.)", "(O.S.)", "(O.C.)", "(SUBTITLE)", "(CONT'D)"],
631
+ []
632
+ );
598
633
  const locations = react.useMemo(() => {
599
634
  const locs = blocks.filter((b) => b.type === "SCENE_HEADING" && b.text.trim() !== "").map((b) => b.text.trim().toUpperCase());
600
635
  return [...new Set(locs)];
601
636
  }, [blocks]);
602
637
  const characters = react.useMemo(() => {
603
- const chars = blocks.filter((b) => b.type === "CHARACTER" && b.text.trim() !== "").map((b) => b.text.trim().toUpperCase());
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);
604
646
  return [...new Set(chars)];
605
647
  }, [blocks]);
606
648
  const sceneNumbers = react.useMemo(() => {
@@ -648,6 +690,7 @@ function useScreenplayEditor() {
648
690
  if (!isInsideBlock && !isInsideToolbar && !isInsideSuggestions) {
649
691
  setFocusedBlockId("");
650
692
  setShowSuggestions(false);
693
+ setShowExtensionSuggestions(false);
651
694
  }
652
695
  };
653
696
  document.addEventListener("mousedown", handleClickOutside);
@@ -751,6 +794,20 @@ function useScreenplayEditor() {
751
794
  (id, text) => {
752
795
  const block = blocks.find((b) => b.id === id);
753
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
+ }
754
811
  let processedText = text;
755
812
  if (block.type === "PARENTHETICAL") {
756
813
  const clean = text.replace(/[()]/g, "");
@@ -770,7 +827,7 @@ function useScreenplayEditor() {
770
827
  }
771
828
  }
772
829
  },
773
- [blocks]
830
+ [blocks, showExtensionSuggestions]
774
831
  );
775
832
  const handleSceneTypeChange = react.useCallback(
776
833
  (id, sceneType) => {
@@ -815,6 +872,35 @@ function useScreenplayEditor() {
815
872
  },
816
873
  [focusedBlockId]
817
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
+ );
818
904
  const focusBlock = (id, position = "start") => {
819
905
  const el = refs.current[id];
820
906
  if (!el) return;
@@ -977,11 +1063,29 @@ function useScreenplayEditor() {
977
1063
  clearTimeout(blurTimeout.current);
978
1064
  }
979
1065
  setFocusedBlockId(id);
980
- setShowSuggestions(true);
981
- }, []);
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]);
982
1083
  const handleBlur = react.useCallback((id) => {
983
1084
  if (document.activeElement === refs.current[id]) return;
984
- blurTimeout.current = setTimeout(() => setShowSuggestions(false), 200);
1085
+ blurTimeout.current = setTimeout(() => {
1086
+ setShowSuggestions(false);
1087
+ setShowExtensionSuggestions(false);
1088
+ }, 200);
985
1089
  }, []);
986
1090
  return {
987
1091
  blocks,
@@ -991,6 +1095,8 @@ function useScreenplayEditor() {
991
1095
  refs,
992
1096
  focusedBlockId,
993
1097
  showSuggestions,
1098
+ showExtensionSuggestions,
1099
+ characterExtensions,
994
1100
  locations,
995
1101
  characters,
996
1102
  sceneNumbers,
@@ -998,6 +1104,7 @@ function useScreenplayEditor() {
998
1104
  handleSceneTypeChange,
999
1105
  handleTimeOfDayChange,
1000
1106
  handleBlockTypeChange,
1107
+ handleSelectCharacterExtension,
1001
1108
  handleKeyDown,
1002
1109
  handleFocus,
1003
1110
  handleBlur