@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 +122 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +123 -36
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
142
|
+
if (file.name.toLowerCase().endsWith(".sbx")) {
|
|
143
|
+
let text = await file.text();
|
|
144
|
+
if (text.includes("<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
|
-
|
|
160
|
-
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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) => {
|