@vishu1301/script-writing 0.4.5 → 0.4.7

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.d.ts CHANGED
@@ -37,7 +37,7 @@ declare function useScreenplayEditor(): {
37
37
  handleBlockTypeChange: (newType: BlockType) => void;
38
38
  handleSelectCharacterExtension: (extension: string) => void;
39
39
  handleKeyDown: (e: KeyboardEvent<HTMLDivElement>, id: string, text: string) => void;
40
- handleScriptImport: (title: string, content: string) => void;
40
+ handleScriptImport: (title: string, content: string, preParsedBlocks?: Partial<Block>[]) => void;
41
41
  handleSceneNumberChange: (id: string, newNumber: string) => void;
42
42
  handleFocus: (id: string) => void;
43
43
  handleBlur: (id: string) => void;
@@ -46,9 +46,10 @@ declare function useScreenplayEditor(): {
46
46
  type ScreenplayEditorViewProps = ReturnType<typeof useScreenplayEditor> & {
47
47
  onSave?: () => void;
48
48
  onSaveAsPdf?: () => void;
49
+ onSaveAsSbx?: () => void;
49
50
  onSyncWithCloud?: () => void;
50
51
  handleSceneNumberChange: (blockId: string, value: string) => void;
51
52
  };
52
- declare function ScreenplayEditorView({ blocks, refs, focusedBlockId, showSuggestions, showExtensionSuggestions, characterExtensions, locations, characters, sceneNumbers, handleBlockTextChange, handleSceneTypeChange, handleTimeOfDayChange, handleBlockTypeChange, handleSelectCharacterExtension, handleKeyDown, handleFocus, handleBlur, handleScriptImport, onSave, onSaveAsPdf, onSyncWithCloud, handleSceneNumberChange, }: ScreenplayEditorViewProps): react_jsx_runtime.JSX.Element;
53
+ declare function ScreenplayEditorView({ blocks, refs, focusedBlockId, showSuggestions, showExtensionSuggestions, characterExtensions, locations, characters, handleBlockTextChange, handleSceneTypeChange, handleTimeOfDayChange, handleBlockTypeChange, handleSelectCharacterExtension, handleKeyDown, handleFocus, handleBlur, handleScriptImport, onSave, onSaveAsPdf, onSaveAsSbx, onSyncWithCloud, handleSceneNumberChange, }: ScreenplayEditorViewProps): react_jsx_runtime.JSX.Element;
53
54
 
54
55
  export { type Block, type BlockType, ScreenplayEditorView, type TimeOfDay, blockStyles, blockTypes, icons, timeOfDayOptions, useScreenplayEditor, uuid };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { useState, useRef, useMemo, useCallback, useEffect } from 'react';
2
- import { ArrowRightLeft, MessageCircle, Brackets, UserRound, Sparkles, Clapperboard, ArrowRight, User, ChevronRight, Upload, Save, FileDown, Cog } from 'lucide-react';
2
+ import { ArrowRightLeft, MessageCircle, Brackets, UserRound, Sparkles, Clapperboard, ArrowRight, User, ChevronRight, Upload, Save, FileDown, RefreshCcw, Cog } from 'lucide-react';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import * as pdfjs from 'pdfjs-dist';
5
5
 
@@ -139,45 +139,107 @@ function PdfImporter({ onScriptImported, children }) {
139
139
  setIsProcessing(true);
140
140
  setError(null);
141
141
  try {
142
- const arrayBuffer = await file.arrayBuffer();
143
- const pdf = await pdfjs.getDocument(arrayBuffer).promise;
144
- const processPage = async (pageNumber) => {
145
- const page = await pdf.getPage(pageNumber);
146
- const content = await page.getTextContent();
147
- const items = content.items.filter((item) => "str" in item && item.str.trim().length > 0);
148
- if (items.length === 0) return "";
149
- const lines = [];
150
- for (const item of items) {
151
- let found = false;
152
- for (const line of lines) {
153
- if (Math.abs(line.y - item.transform[5]) < 5) {
154
- line.items.push({ x: item.transform[4], text: item.str });
155
- found = true;
142
+ if (file.name.toLowerCase().endsWith(".sbx")) {
143
+ let text = await file.text();
144
+ if (text.includes("&lt;div")) {
145
+ const textarea = document.createElement("textarea");
146
+ textarea.innerHTML = text;
147
+ text = textarea.value;
148
+ }
149
+ const parser = new DOMParser();
150
+ const doc = parser.parseFromString(text, "text/html");
151
+ const divs = Array.from(doc.querySelectorAll("div"));
152
+ const preParsedBlocks = [];
153
+ const typeMap = {
154
+ divtype0: "SCENE_HEADING",
155
+ divtype2: "ACTION",
156
+ divtype3: "CHARACTER",
157
+ divtype4: "PARENTHETICAL",
158
+ divtype5: "DIALOGUE",
159
+ divtype6: "TRANSITION"
160
+ };
161
+ divs.forEach((div) => {
162
+ var _a2;
163
+ let divText = ((_a2 = div.textContent) == null ? void 0 : _a2.trim()) || "";
164
+ if (!divText) return;
165
+ let type = "ACTION";
166
+ for (const className of Array.from(div.classList)) {
167
+ if (typeMap[className]) {
168
+ type = typeMap[className];
156
169
  break;
157
170
  }
158
171
  }
159
- if (!found) {
160
- lines.push({ y: item.transform[5], items: [{ x: item.transform[4], text: item.str }] });
172
+ const block = { type, text: divText };
173
+ if (type === "SCENE_HEADING") {
174
+ const sceneNum = div.getAttribute("data-scene");
175
+ if (sceneNum) block.sceneNumber = sceneNum;
176
+ let parsedText = divText;
177
+ const typeMatch = parsedText.match(/^(INT\/EXT|INT|EXT)\.?\s+/i);
178
+ if (typeMatch) {
179
+ let sType = typeMatch[1].toUpperCase();
180
+ if (!sType.endsWith(".")) sType += ".";
181
+ block.sceneType = sType;
182
+ parsedText = parsedText.substring(typeMatch[0].length).trim();
183
+ }
184
+ const timeMatch = parsedText.match(/\s+-\s+([^-]+)$/);
185
+ if (timeMatch) {
186
+ block.timeOfDay = timeMatch[1].trim().toUpperCase();
187
+ parsedText = parsedText.substring(0, timeMatch.index).trim();
188
+ }
189
+ block.text = parsedText;
161
190
  }
191
+ preParsedBlocks.push(block);
192
+ });
193
+ const title = file.name.replace(/\.sbx$/i, "");
194
+ onScriptImported(title.trim(), "", preParsedBlocks);
195
+ } else {
196
+ const arrayBuffer = await file.arrayBuffer();
197
+ const pdf = await pdfjs.getDocument(arrayBuffer).promise;
198
+ const processPage = async (pageNumber) => {
199
+ const page = await pdf.getPage(pageNumber);
200
+ const content = await page.getTextContent();
201
+ const items = content.items.filter(
202
+ (item) => "str" in item && item.str.trim().length > 0
203
+ );
204
+ if (items.length === 0) return "";
205
+ const lines = [];
206
+ for (const item of items) {
207
+ let found = false;
208
+ for (const line of lines) {
209
+ if (Math.abs(line.y - item.transform[5]) < 5) {
210
+ line.items.push({ x: item.transform[4], text: item.str });
211
+ found = true;
212
+ break;
213
+ }
214
+ }
215
+ if (!found) {
216
+ lines.push({
217
+ y: item.transform[5],
218
+ items: [{ x: item.transform[4], text: item.str }]
219
+ });
220
+ }
221
+ }
222
+ lines.sort((a, b) => b.y - a.y);
223
+ return lines.map((line) => {
224
+ line.items.sort((a, b) => a.x - b.x);
225
+ return line.items.map((item) => item.text).join(" ");
226
+ }).join("\n");
227
+ };
228
+ let title = "";
229
+ if (pdf.numPages > 0) {
230
+ title = await processPage(1);
162
231
  }
163
- lines.sort((a, b) => b.y - a.y);
164
- return lines.map((line) => {
165
- line.items.sort((a, b) => a.x - b.x);
166
- return line.items.map((item) => item.text).join(" ");
167
- }).join("\n");
168
- };
169
- let title = "";
170
- if (pdf.numPages > 0) {
171
- title = await processPage(1);
172
- }
173
- let scriptContent = "";
174
- for (let i = 2; i <= pdf.numPages; i++) {
175
- scriptContent += await processPage(i) + "\n\n";
232
+ let scriptContent = "";
233
+ for (let i = 2; i <= pdf.numPages; i++) {
234
+ scriptContent += await processPage(i) + "\n\n";
235
+ }
236
+ onScriptImported(title.trim(), scriptContent);
176
237
  }
177
- onScriptImported(title.trim(), scriptContent);
178
238
  } catch (err) {
179
239
  console.error("Error processing PDF:", err);
180
- setError(err instanceof Error ? `Error processing PDF: ${err.message}` : "An unknown error occurred.");
240
+ setError(
241
+ err instanceof Error ? `Error processing PDF: ${err.message}` : "An unknown error occurred."
242
+ );
181
243
  } finally {
182
244
  setIsProcessing(false);
183
245
  if (event.target) {
@@ -195,7 +257,7 @@ function PdfImporter({ onScriptImported, children }) {
195
257
  {
196
258
  ref: fileInputRef,
197
259
  type: "file",
198
- accept: "application/pdf",
260
+ accept: ".pdf,.sbx,application/pdf",
199
261
  onChange: handleFileChange,
200
262
  disabled: isProcessing,
201
263
  className: "hidden",
@@ -208,7 +270,7 @@ function PdfImporter({ onScriptImported, children }) {
208
270
  onClick: handleClick,
209
271
  disabled: isProcessing,
210
272
  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",
211
- "aria-label": "Import Script from PDF",
273
+ "aria-label": "Import Script",
212
274
  children: isProcessing ? /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: "Processing..." }) : children
213
275
  }
214
276
  ),
@@ -227,7 +289,6 @@ function ScreenplayEditorView({
227
289
  characterExtensions,
228
290
  locations,
229
291
  characters,
230
- sceneNumbers,
231
292
  handleBlockTextChange,
232
293
  handleSceneTypeChange,
233
294
  handleTimeOfDayChange,
@@ -239,6 +300,7 @@ function ScreenplayEditorView({
239
300
  handleScriptImport,
240
301
  onSave,
241
302
  onSaveAsPdf,
303
+ onSaveAsSbx,
242
304
  onSyncWithCloud,
243
305
  handleSceneNumberChange
244
306
  }) {
@@ -532,6 +594,18 @@ function ScreenplayEditorView({
532
594
  ]
533
595
  }
534
596
  ),
597
+ onSaveAsSbx && /* @__PURE__ */ jsxs(
598
+ "button",
599
+ {
600
+ onClick: onSaveAsSbx,
601
+ 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",
602
+ "aria-label": "Save Script as SBX",
603
+ children: [
604
+ /* @__PURE__ */ jsx(RefreshCcw, { className: "w-5 h-5" }),
605
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: "Save" })
606
+ ]
607
+ }
608
+ ),
535
609
  isRulesOpen && /* @__PURE__ */ jsxs("div", { className: "bg-white/80 backdrop-blur-md rounded-xl shadow-lg border border-zinc-200/50 p-4 text-xs text-zinc-700 select-none font-sans overflow-hidden transition-all duration-300 w-64 origin-bottom-right animate-in fade-in zoom-in-95", children: [
536
610
  /* @__PURE__ */ jsx("h4", { className: "font-bold text-zinc-800 mb-3 text-sm", children: "Settings & Shortcuts" }),
537
611
  /* @__PURE__ */ jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: /* @__PURE__ */ jsxs("ul", { className: "space-y-1.5", children: [
@@ -885,17 +959,6 @@ function useScreenplayEditor() {
885
959
  });
886
960
  return map;
887
961
  }, [blocks]);
888
- useCallback(() => {
889
- let count = 1;
890
- setBlocks(
891
- (prev) => prev.map((b) => {
892
- if (b.type === "SCENE_HEADING") {
893
- return __spreadProps(__spreadValues({}, b), { sceneNumber: String(count++) });
894
- }
895
- return b;
896
- })
897
- );
898
- }, []);
899
962
  useEffect(() => {
900
963
  if (newBlockId && refs.current[newBlockId]) {
901
964
  const block = blocks.find((b) => b.id === newBlockId);
@@ -1205,8 +1268,20 @@ function useScreenplayEditor() {
1205
1268
  [blocks, handleBlockTextChange]
1206
1269
  );
1207
1270
  const handleScriptImport = useCallback(
1208
- (title, content) => {
1209
- const parsedBlocks = parseScreenplayText(content);
1271
+ (title, content, preParsedBlocks) => {
1272
+ let parsedBlocks = [];
1273
+ if (preParsedBlocks && preParsedBlocks.length > 0) {
1274
+ parsedBlocks = preParsedBlocks.map((b) => ({
1275
+ id: uuid(),
1276
+ type: b.type || "ACTION",
1277
+ text: b.text || "",
1278
+ sceneNumber: b.sceneNumber,
1279
+ sceneType: b.sceneType,
1280
+ timeOfDay: b.timeOfDay
1281
+ }));
1282
+ } else {
1283
+ parsedBlocks = parseScreenplayText(content);
1284
+ }
1210
1285
  if (parsedBlocks.length > 0) {
1211
1286
  let fallbackCount = 1;
1212
1287
  const finalizedBlocks = parsedBlocks.map((block) => {