@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/README.md +9 -7
- package/dist/index.cjs +122 -47
- 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 -48
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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,
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
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
|
),
|
|
@@ -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
|
-
|
|
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) => {
|