@vishu1301/script-writing 0.3.7 → 0.3.9

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
@@ -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 leading-relaxed",
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: 1.7
67
+ lineHeight: "12pt"
66
68
  }
67
69
  },
68
70
  CHARACTER: {
69
71
  label: "Character",
70
- className: "uppercase font-bold text-center text-zinc-900 tracking-widest",
72
+ className: "uppercase font-bold text-zinc-900 tracking-widest",
71
73
  inputStyle: {
72
74
  textTransform: "uppercase",
73
- textAlign: "center",
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,37 +85,39 @@ var blockStyles = {
81
85
  },
82
86
  PARENTHETICAL: {
83
87
  label: "Parenthetical",
84
- className: "text-center text-zinc-600",
88
+ className: "text-zinc-600",
85
89
  inputStyle: {
86
90
  fontStyle: "normal",
87
- maxWidth: "20rem",
88
- margin: "0 auto",
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: "center"
98
+ textAlign: "left"
94
99
  }
95
100
  },
96
101
  DIALOGUE: {
97
102
  label: "Dialogue",
98
- className: "text-zinc-900 leading-relaxed max-w-[30rem] mx-auto",
103
+ className: "text-zinc-900",
99
104
  inputStyle: {
100
- marginLeft: "auto",
101
- marginRight: "auto",
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: 1.7
113
+ lineHeight: "12pt"
109
114
  }
110
115
  },
111
116
  TRANSITION: {
112
117
  label: "Transition",
113
118
  className: "uppercase font-bold text-right text-zinc-900",
114
119
  inputStyle: {
120
+ marginLeft: "4.0in",
115
121
  textTransform: "uppercase",
116
122
  fontWeight: 600,
117
123
  textAlign: "right",
@@ -187,7 +193,7 @@ function ScreenplayEditorView({
187
193
  /* @__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
194
  "div",
189
195
  {
190
- className: "relative bg-[#fdfdfc] shadow-2xl shadow-zinc-300/60 ring-1 ring-zinc-200/50 rounded-sm md:rounded-md p-16 md:p-20 flex flex-col w-[210mm] min-h-[297mm] shrink-0",
196
+ 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
197
  style: {
192
198
  fontFamily: "var(--font-courier-prime, 'Courier New', Courier, monospace)"
193
199
  },
@@ -229,7 +235,7 @@ function ScreenplayEditorView({
229
235
  "aria-haspopup": "listbox",
230
236
  "aria-expanded": focusedBlockId === block.id && showSuggestions && locations.length > 0,
231
237
  spellCheck: false,
232
- className: "min-w-[5rem] py-1 outline-none text-base font-bold uppercase tracking-widest break-all bg-transparent",
238
+ className: "min-w-[3rem] py-1 outline-none text-base font-bold uppercase tracking-widest break-all bg-transparent",
233
239
  onInput: (e) => handleBlockTextChange(
234
240
  block.id,
235
241
  e.target.innerText
@@ -468,6 +474,8 @@ function createNewBlock(type) {
468
474
  if (type === "SCENE_HEADING") {
469
475
  newBlock.sceneType = "INT.";
470
476
  newBlock.timeOfDay = "DAY";
477
+ } else if (type === "PARENTHETICAL") {
478
+ newBlock.text = "()";
471
479
  }
472
480
  return newBlock;
473
481
  }
@@ -607,13 +615,22 @@ function useScreenplayEditor() {
607
615
  return map;
608
616
  }, [blocks]);
609
617
  react.useEffect(() => {
610
- var _a;
611
618
  if (newBlockId && refs.current[newBlockId]) {
612
- (_a = refs.current[newBlockId]) == null ? void 0 : _a.focus();
619
+ const block = blocks.find((b) => b.id === newBlockId);
620
+ const el = refs.current[newBlockId];
621
+ if (el && block) {
622
+ el.focus();
623
+ el.innerText = block.text;
624
+ if (block.type === "PARENTHETICAL") {
625
+ setTimeout(() => setCaretPosition(el, 1), 0);
626
+ } else {
627
+ setTimeout(() => setCaretPosition(el, block.text.length), 0);
628
+ }
629
+ }
613
630
  setFocusedBlockId(newBlockId);
614
631
  setNewBlockId(null);
615
632
  }
616
- }, [newBlockId]);
633
+ }, [newBlockId, blocks]);
617
634
  react.useEffect(() => {
618
635
  blocks.forEach((block) => {
619
636
  const element = refs.current[block.id];
@@ -622,6 +639,22 @@ function useScreenplayEditor() {
622
639
  }
623
640
  });
624
641
  }, [blocks, isPageSplitEnabled, pageBreaks]);
642
+ react.useEffect(() => {
643
+ const handleClickOutside = (e) => {
644
+ const target = e.target;
645
+ const isInsideBlock = target.closest("[data-block-id]");
646
+ const isInsideToolbar = target.closest(".sticky");
647
+ const isInsideSuggestions = target.closest('[role="listbox"]');
648
+ if (!isInsideBlock && !isInsideToolbar && !isInsideSuggestions) {
649
+ setFocusedBlockId("");
650
+ setShowSuggestions(false);
651
+ }
652
+ };
653
+ document.addEventListener("mousedown", handleClickOutside);
654
+ return () => {
655
+ document.removeEventListener("mousedown", handleClickOutside);
656
+ };
657
+ }, []);
625
658
  react.useEffect(() => {
626
659
  if (!isPageSplitEnabled) {
627
660
  setPageBreaks([]);
@@ -714,9 +747,31 @@ function useScreenplayEditor() {
714
747
  focusStateRef.current = null;
715
748
  }
716
749
  }, [pages]);
717
- const handleBlockTextChange = react.useCallback((id, text) => {
718
- setBlocks((bs) => updateBlock(bs, id, "text", text));
719
- }, []);
750
+ const handleBlockTextChange = react.useCallback(
751
+ (id, text) => {
752
+ const block = blocks.find((b) => b.id === id);
753
+ if (!block) return;
754
+ let processedText = text;
755
+ if (block.type === "PARENTHETICAL") {
756
+ const clean = text.replace(/[()]/g, "");
757
+ processedText = `(${clean})`;
758
+ }
759
+ setBlocks(
760
+ (bs) => updateBlock(bs, id, "text", processedText)
761
+ );
762
+ if (text !== processedText) {
763
+ const el = refs.current[id];
764
+ if (el) {
765
+ const offset = getCaretCharacterOffsetWithin(el);
766
+ const charsBeforeCaret = text.substring(0, offset).replace(/[()]/g, "").length;
767
+ const newOffset = 1 + charsBeforeCaret;
768
+ el.innerText = processedText;
769
+ setCaretPosition(el, newOffset);
770
+ }
771
+ }
772
+ },
773
+ [blocks]
774
+ );
720
775
  const handleSceneTypeChange = react.useCallback(
721
776
  (id, sceneType) => {
722
777
  setBlocks(
@@ -747,8 +802,14 @@ function useScreenplayEditor() {
747
802
  setTimeout(() => {
748
803
  const el = refs.current[focusedBlockId];
749
804
  if (el) {
750
- el.innerText = "";
751
805
  el.focus();
806
+ const newBlock = createNewBlock(newType);
807
+ el.innerText = newBlock.text;
808
+ if (newType === "PARENTHETICAL") {
809
+ setCaretPosition(el, 1);
810
+ } else {
811
+ setCaretPosition(el, newBlock.text.length);
812
+ }
752
813
  }
753
814
  }, 0);
754
815
  },
@@ -767,19 +828,44 @@ function useScreenplayEditor() {
767
828
  sel.addRange(range);
768
829
  };
769
830
  const cycleBlockType = (id, direction) => {
770
- setBlocks((bs) => {
771
- const block = bs.find((b) => b.id === id);
772
- if (!block) return bs;
773
- const idx = blockTypes.indexOf(block.type);
774
- let newIdx = direction === "up" ? idx - 1 : idx + 1;
775
- if (newIdx < 0) newIdx = blockTypes.length - 1;
776
- if (newIdx >= blockTypes.length) newIdx = 0;
777
- return updateBlock(bs, id, "type", blockTypes[newIdx]);
778
- });
831
+ const block = blocks.find((b) => b.id === id);
832
+ if (!block) return;
833
+ const idx = blockTypes.indexOf(block.type);
834
+ let newIdx = direction === "up" ? idx - 1 : idx + 1;
835
+ if (newIdx < 0) newIdx = blockTypes.length - 1;
836
+ if (newIdx >= blockTypes.length) newIdx = 0;
837
+ const newType = blockTypes[newIdx];
838
+ setBlocks((bs) => changeBlockType(bs, id, newType));
839
+ setTimeout(() => {
840
+ const el = refs.current[id];
841
+ if (el) {
842
+ el.focus();
843
+ const newBlock = createNewBlock(newType);
844
+ el.innerText = newBlock.text;
845
+ if (newType === "PARENTHETICAL") {
846
+ setCaretPosition(el, 1);
847
+ } else {
848
+ setCaretPosition(el, el.innerText.length);
849
+ }
850
+ }
851
+ }, 10);
779
852
  };
780
853
  const handleKeyDown = react.useCallback(
781
854
  (e, id, text) => {
782
855
  var _a;
856
+ const block = blocks.find((b) => b.id === id);
857
+ if ((block == null ? void 0 : block.type) === "PARENTHETICAL") {
858
+ const offset = getCaretCharacterOffsetWithin(e.currentTarget);
859
+ if (e.key === "Backspace" && (offset <= 1 || text === "()")) {
860
+ e.preventDefault();
861
+ cycleBlockType(id, "up");
862
+ return;
863
+ }
864
+ if (e.key === "Delete" && (offset >= text.length - 1 || text === "()")) {
865
+ e.preventDefault();
866
+ return;
867
+ }
868
+ }
783
869
  if ((e.key === "Backspace" || e.key === "Delete") && text.length <= 1) {
784
870
  e.preventDefault();
785
871
  const { newBlocks, nextFocusedId } = deleteBlock(
@@ -805,11 +891,9 @@ function useScreenplayEditor() {
805
891
  } else if (e.key === "ArrowUp" && e.ctrlKey) {
806
892
  e.preventDefault();
807
893
  cycleBlockType(id, "up");
808
- requestAnimationFrame(() => focusBlock(id));
809
894
  } else if (e.key === "ArrowDown" && e.ctrlKey) {
810
895
  e.preventDefault();
811
896
  cycleBlockType(id, "down");
812
- requestAnimationFrame(() => focusBlock(id));
813
897
  } else if (e.key === "ArrowUp" && !e.ctrlKey) {
814
898
  const selection = window.getSelection();
815
899
  if (!selection || !selection.isCollapsed || selection.rangeCount === 0) {
@@ -886,7 +970,7 @@ function useScreenplayEditor() {
886
970
  }
887
971
  }
888
972
  },
889
- [blocks]
973
+ [blocks, handleBlockTextChange]
890
974
  );
891
975
  const handleFocus = react.useCallback((id) => {
892
976
  if (blurTimeout.current) {