@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.cjs CHANGED
@@ -161,45 +161,107 @@ function PdfImporter({ onScriptImported, children }) {
161
161
  setIsProcessing(true);
162
162
  setError(null);
163
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;
164
+ if (file.name.toLowerCase().endsWith(".sbx")) {
165
+ let text = await file.text();
166
+ if (text.includes("&lt;div")) {
167
+ const textarea = document.createElement("textarea");
168
+ textarea.innerHTML = text;
169
+ text = textarea.value;
170
+ }
171
+ const parser = new DOMParser();
172
+ const doc = parser.parseFromString(text, "text/html");
173
+ const divs = Array.from(doc.querySelectorAll("div"));
174
+ const preParsedBlocks = [];
175
+ const typeMap = {
176
+ divtype0: "SCENE_HEADING",
177
+ divtype2: "ACTION",
178
+ divtype3: "CHARACTER",
179
+ divtype4: "PARENTHETICAL",
180
+ divtype5: "DIALOGUE",
181
+ divtype6: "TRANSITION"
182
+ };
183
+ divs.forEach((div) => {
184
+ var _a2;
185
+ let divText = ((_a2 = div.textContent) == null ? void 0 : _a2.trim()) || "";
186
+ if (!divText) return;
187
+ let type = "ACTION";
188
+ for (const className of Array.from(div.classList)) {
189
+ if (typeMap[className]) {
190
+ type = typeMap[className];
178
191
  break;
179
192
  }
180
193
  }
181
- if (!found) {
182
- lines.push({ y: item.transform[5], items: [{ x: item.transform[4], text: item.str }] });
194
+ const block = { type, text: divText };
195
+ if (type === "SCENE_HEADING") {
196
+ const sceneNum = div.getAttribute("data-scene");
197
+ if (sceneNum) block.sceneNumber = sceneNum;
198
+ let parsedText = divText;
199
+ const typeMatch = parsedText.match(/^(INT\/EXT|INT|EXT)\.?\s+/i);
200
+ if (typeMatch) {
201
+ let sType = typeMatch[1].toUpperCase();
202
+ if (!sType.endsWith(".")) sType += ".";
203
+ block.sceneType = sType;
204
+ parsedText = parsedText.substring(typeMatch[0].length).trim();
205
+ }
206
+ const timeMatch = parsedText.match(/\s+-\s+([^-]+)$/);
207
+ if (timeMatch) {
208
+ block.timeOfDay = timeMatch[1].trim().toUpperCase();
209
+ parsedText = parsedText.substring(0, timeMatch.index).trim();
210
+ }
211
+ block.text = parsedText;
212
+ }
213
+ preParsedBlocks.push(block);
214
+ });
215
+ const title = file.name.replace(/\.sbx$/i, "");
216
+ onScriptImported(title.trim(), "", preParsedBlocks);
217
+ } else {
218
+ const arrayBuffer = await file.arrayBuffer();
219
+ const pdf = await pdfjs__namespace.getDocument(arrayBuffer).promise;
220
+ const processPage = async (pageNumber) => {
221
+ const page = await pdf.getPage(pageNumber);
222
+ const content = await page.getTextContent();
223
+ const items = content.items.filter(
224
+ (item) => "str" in item && item.str.trim().length > 0
225
+ );
226
+ if (items.length === 0) return "";
227
+ const lines = [];
228
+ for (const item of items) {
229
+ let found = false;
230
+ for (const line of lines) {
231
+ if (Math.abs(line.y - item.transform[5]) < 5) {
232
+ line.items.push({ x: item.transform[4], text: item.str });
233
+ found = true;
234
+ break;
235
+ }
236
+ }
237
+ if (!found) {
238
+ lines.push({
239
+ y: item.transform[5],
240
+ items: [{ x: item.transform[4], text: item.str }]
241
+ });
242
+ }
183
243
  }
244
+ lines.sort((a, b) => b.y - a.y);
245
+ return lines.map((line) => {
246
+ line.items.sort((a, b) => a.x - b.x);
247
+ return line.items.map((item) => item.text).join(" ");
248
+ }).join("\n");
249
+ };
250
+ let title = "";
251
+ if (pdf.numPages > 0) {
252
+ title = await processPage(1);
184
253
  }
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";
254
+ let scriptContent = "";
255
+ for (let i = 2; i <= pdf.numPages; i++) {
256
+ scriptContent += await processPage(i) + "\n\n";
257
+ }
258
+ onScriptImported(title.trim(), scriptContent);
198
259
  }
199
- onScriptImported(title.trim(), scriptContent);
200
260
  } catch (err) {
201
261
  console.error("Error processing PDF:", err);
202
- setError(err instanceof Error ? `Error processing PDF: ${err.message}` : "An unknown error occurred.");
262
+ setError(
263
+ err instanceof Error ? `Error processing PDF: ${err.message}` : "An unknown error occurred."
264
+ );
203
265
  } finally {
204
266
  setIsProcessing(false);
205
267
  if (event.target) {
@@ -217,7 +279,7 @@ function PdfImporter({ onScriptImported, children }) {
217
279
  {
218
280
  ref: fileInputRef,
219
281
  type: "file",
220
- accept: "application/pdf",
282
+ accept: ".pdf,.sbx,application/pdf",
221
283
  onChange: handleFileChange,
222
284
  disabled: isProcessing,
223
285
  className: "hidden",
@@ -230,7 +292,7 @@ function PdfImporter({ onScriptImported, children }) {
230
292
  onClick: handleClick,
231
293
  disabled: isProcessing,
232
294
  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",
295
+ "aria-label": "Import Script",
234
296
  children: isProcessing ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold", children: "Processing..." }) : children
235
297
  }
236
298
  ),
@@ -260,6 +322,7 @@ function ScreenplayEditorView({
260
322
  handleScriptImport,
261
323
  onSave,
262
324
  onSaveAsPdf,
325
+ onSaveAsSbx,
263
326
  onSyncWithCloud,
264
327
  handleSceneNumberChange
265
328
  }) {
@@ -553,6 +616,18 @@ function ScreenplayEditorView({
553
616
  ]
554
617
  }
555
618
  ),
619
+ onSaveAsSbx && /* @__PURE__ */ jsxRuntime.jsxs(
620
+ "button",
621
+ {
622
+ onClick: onSaveAsSbx,
623
+ 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",
624
+ "aria-label": "Save Script as SBX",
625
+ children: [
626
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCcw, { className: "w-5 h-5" }),
627
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold", children: "Save" })
628
+ ]
629
+ }
630
+ ),
556
631
  isRulesOpen && /* @__PURE__ */ jsxRuntime.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: [
557
632
  /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-bold text-zinc-800 mb-3 text-sm", children: "Settings & Shortcuts" }),
558
633
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1.5", children: /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "space-y-1.5", children: [
@@ -1215,8 +1290,20 @@ function useScreenplayEditor() {
1215
1290
  [blocks, handleBlockTextChange]
1216
1291
  );
1217
1292
  const handleScriptImport = react.useCallback(
1218
- (title, content) => {
1219
- const parsedBlocks = parseScreenplayText(content);
1293
+ (title, content, preParsedBlocks) => {
1294
+ let parsedBlocks = [];
1295
+ if (preParsedBlocks && preParsedBlocks.length > 0) {
1296
+ parsedBlocks = preParsedBlocks.map((b) => ({
1297
+ id: uuid(),
1298
+ type: b.type || "ACTION",
1299
+ text: b.text || "",
1300
+ sceneNumber: b.sceneNumber,
1301
+ sceneType: b.sceneType,
1302
+ timeOfDay: b.timeOfDay
1303
+ }));
1304
+ } else {
1305
+ parsedBlocks = parseScreenplayText(content);
1306
+ }
1220
1307
  if (parsedBlocks.length > 0) {
1221
1308
  let fallbackCount = 1;
1222
1309
  const finalizedBlocks = parsedBlocks.map((block) => {