@vishu1301/script-writing 0.4.6 → 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.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;
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
+ }
161
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
  ),
@@ -238,6 +300,7 @@ function ScreenplayEditorView({
238
300
  handleScriptImport,
239
301
  onSave,
240
302
  onSaveAsPdf,
303
+ onSaveAsSbx,
241
304
  onSyncWithCloud,
242
305
  handleSceneNumberChange
243
306
  }) {
@@ -531,6 +594,18 @@ function ScreenplayEditorView({
531
594
  ]
532
595
  }
533
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
+ ),
534
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: [
535
610
  /* @__PURE__ */ jsx("h4", { className: "font-bold text-zinc-800 mb-3 text-sm", children: "Settings & Shortcuts" }),
536
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: [
@@ -1193,8 +1268,20 @@ function useScreenplayEditor() {
1193
1268
  [blocks, handleBlockTextChange]
1194
1269
  );
1195
1270
  const handleScriptImport = useCallback(
1196
- (title, content) => {
1197
- 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
+ }
1198
1285
  if (parsedBlocks.length > 0) {
1199
1286
  let fallbackCount = 1;
1200
1287
  const finalizedBlocks = parsedBlocks.map((block) => {