@vishu1301/script-writing 0.5.1 → 0.5.3
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 +96 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +96 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -21,7 +21,11 @@ declare const blockStyles: Record<BlockType, {
|
|
|
21
21
|
inputStyle: React.CSSProperties;
|
|
22
22
|
}>;
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
interface UseScreenplayEditorOptions {
|
|
25
|
+
initialUrl?: string;
|
|
26
|
+
fetchOptions?: RequestInit;
|
|
27
|
+
}
|
|
28
|
+
declare function useScreenplayEditor(options?: UseScreenplayEditorOptions): {
|
|
25
29
|
blocks: Block[];
|
|
26
30
|
refs: React.RefObject<Record<string, HTMLDivElement | null>>;
|
|
27
31
|
focusedBlockId: string;
|
|
@@ -41,6 +45,7 @@ declare function useScreenplayEditor(): {
|
|
|
41
45
|
handleSceneNumberChange: (id: string, newNumber: string) => void;
|
|
42
46
|
handleFocus: (id: string) => void;
|
|
43
47
|
handleBlur: (id: string) => void;
|
|
48
|
+
loadFromUrl: (url: string, fetchOptions?: RequestInit) => Promise<void>;
|
|
44
49
|
};
|
|
45
50
|
|
|
46
51
|
type ScreenplayEditorViewProps = ReturnType<typeof useScreenplayEditor> & {
|
package/dist/index.d.ts
CHANGED
|
@@ -21,7 +21,11 @@ declare const blockStyles: Record<BlockType, {
|
|
|
21
21
|
inputStyle: React.CSSProperties;
|
|
22
22
|
}>;
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
interface UseScreenplayEditorOptions {
|
|
25
|
+
initialUrl?: string;
|
|
26
|
+
fetchOptions?: RequestInit;
|
|
27
|
+
}
|
|
28
|
+
declare function useScreenplayEditor(options?: UseScreenplayEditorOptions): {
|
|
25
29
|
blocks: Block[];
|
|
26
30
|
refs: React.RefObject<Record<string, HTMLDivElement | null>>;
|
|
27
31
|
focusedBlockId: string;
|
|
@@ -41,6 +45,7 @@ declare function useScreenplayEditor(): {
|
|
|
41
45
|
handleSceneNumberChange: (id: string, newNumber: string) => void;
|
|
42
46
|
handleFocus: (id: string) => void;
|
|
43
47
|
handleBlur: (id: string) => void;
|
|
48
|
+
loadFromUrl: (url: string, fetchOptions?: RequestInit) => Promise<void>;
|
|
44
49
|
};
|
|
45
50
|
|
|
46
51
|
type ScreenplayEditorViewProps = ReturnType<typeof useScreenplayEditor> & {
|
package/dist/index.js
CHANGED
|
@@ -258,7 +258,7 @@ function PdfImporter({ onScriptImported, children }) {
|
|
|
258
258
|
{
|
|
259
259
|
ref: fileInputRef,
|
|
260
260
|
type: "file",
|
|
261
|
-
accept: ".pdf
|
|
261
|
+
accept: ".pdf,application/pdf",
|
|
262
262
|
onChange: handleFileChange,
|
|
263
263
|
disabled: isProcessing,
|
|
264
264
|
className: "hidden",
|
|
@@ -312,7 +312,7 @@ function ScreenplayEditorView({
|
|
|
312
312
|
const link = document.createElement("link");
|
|
313
313
|
link.id = fontId;
|
|
314
314
|
link.rel = "stylesheet";
|
|
315
|
-
link.href = "https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap";
|
|
315
|
+
link.href = "https://fonts.googleapis.com/css2?family=Courier+Prime:ital,wght@0,400;0,700;1,400;1,700&display=swap";
|
|
316
316
|
document.head.appendChild(link);
|
|
317
317
|
}
|
|
318
318
|
}, []);
|
|
@@ -360,7 +360,7 @@ function ScreenplayEditorView({
|
|
|
360
360
|
{
|
|
361
361
|
className: "relative bg-[#fdfdfc] shadow-2xl shadow-zinc-300/60 border border-zinc-100 rounded-sm md:rounded-md pl-[1.5in] py-[1in] pr-[1in] flex flex-col w-[210mm] min-h-[297mm] shrink-0",
|
|
362
362
|
style: {
|
|
363
|
-
fontFamily: "'Courier Prime', 'Courier New', Courier, monospace",
|
|
363
|
+
fontFamily: "var(--font-courier-prime, 'Courier Prime', 'Courier New', Courier, monospace) !important",
|
|
364
364
|
paddingLeft: "1.5in",
|
|
365
365
|
paddingRight: "1in",
|
|
366
366
|
paddingTop: "1in",
|
|
@@ -426,6 +426,9 @@ function ScreenplayEditorView({
|
|
|
426
426
|
"aria-expanded": focusedBlockId === block.id && showSuggestions && locations.length > 0,
|
|
427
427
|
spellCheck: false,
|
|
428
428
|
className: "min-w-[3rem] py-1 outline-none text-base font-bold uppercase tracking-widest break-all bg-transparent",
|
|
429
|
+
style: {
|
|
430
|
+
minWidth: "3rem"
|
|
431
|
+
},
|
|
429
432
|
onInput: (e) => handleBlockTextChange(
|
|
430
433
|
block.id,
|
|
431
434
|
e.target.innerText
|
|
@@ -924,7 +927,7 @@ function setCaretPosition(element, offset) {
|
|
|
924
927
|
sel.removeAllRanges();
|
|
925
928
|
sel.addRange(range);
|
|
926
929
|
}
|
|
927
|
-
function useScreenplayEditor() {
|
|
930
|
+
function useScreenplayEditor(options) {
|
|
928
931
|
const [blocks, setBlocks] = useState(initialBlocks);
|
|
929
932
|
const refs = useRef({});
|
|
930
933
|
const [focusedBlockId, setFocusedBlockId] = useState(
|
|
@@ -934,6 +937,7 @@ function useScreenplayEditor() {
|
|
|
934
937
|
const [showSuggestions, setShowSuggestions] = useState(false);
|
|
935
938
|
const [showExtensionSuggestions, setShowExtensionSuggestions] = useState(false);
|
|
936
939
|
const blurTimeout = useRef(null);
|
|
940
|
+
const loadedUrlRef = useRef(null);
|
|
937
941
|
const characterExtensions = useMemo(
|
|
938
942
|
() => ["(V.O.)", "(O.S.)", "(O.C.)", "(SUBTITLE)", "(CONT'D)"],
|
|
939
943
|
[]
|
|
@@ -1363,6 +1367,92 @@ function useScreenplayEditor() {
|
|
|
1363
1367
|
setShowExtensionSuggestions(false);
|
|
1364
1368
|
}, 200);
|
|
1365
1369
|
}, []);
|
|
1370
|
+
const loadFromUrl = useCallback(
|
|
1371
|
+
async (url, fetchOptions = {}) => {
|
|
1372
|
+
var _a;
|
|
1373
|
+
try {
|
|
1374
|
+
const response = await fetch(url, fetchOptions);
|
|
1375
|
+
if (!response.ok) {
|
|
1376
|
+
throw new Error(
|
|
1377
|
+
`[useScreenplayEditor] Failed to fetch script. HTTP Status: ${response.status}`
|
|
1378
|
+
);
|
|
1379
|
+
}
|
|
1380
|
+
let text = await response.text();
|
|
1381
|
+
let preParsedBlocks = void 0;
|
|
1382
|
+
let scriptContent = text;
|
|
1383
|
+
if (text.includes('class="divtype') || url.toLowerCase().includes(".sbx")) {
|
|
1384
|
+
if (text.includes("<div")) {
|
|
1385
|
+
const textarea = document.createElement("textarea");
|
|
1386
|
+
textarea.innerHTML = text;
|
|
1387
|
+
text = textarea.value;
|
|
1388
|
+
}
|
|
1389
|
+
const parser = new DOMParser();
|
|
1390
|
+
const doc = parser.parseFromString(text, "text/html");
|
|
1391
|
+
const divs = Array.from(doc.querySelectorAll("div"));
|
|
1392
|
+
const parsed = [];
|
|
1393
|
+
const typeMap = {
|
|
1394
|
+
divtype0: "SCENE_HEADING",
|
|
1395
|
+
divtype2: "ACTION",
|
|
1396
|
+
divtype3: "CHARACTER",
|
|
1397
|
+
divtype4: "PARENTHETICAL",
|
|
1398
|
+
divtype5: "DIALOGUE",
|
|
1399
|
+
divtype6: "TRANSITION"
|
|
1400
|
+
};
|
|
1401
|
+
divs.forEach((div) => {
|
|
1402
|
+
var _a2;
|
|
1403
|
+
let divText = ((_a2 = div.textContent) == null ? void 0 : _a2.trim()) || "";
|
|
1404
|
+
if (!divText) return;
|
|
1405
|
+
let type = "ACTION";
|
|
1406
|
+
for (const className of Array.from(div.classList)) {
|
|
1407
|
+
if (typeMap[className]) {
|
|
1408
|
+
type = typeMap[className];
|
|
1409
|
+
break;
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
const block = { type, text: divText };
|
|
1413
|
+
if (type === "SCENE_HEADING") {
|
|
1414
|
+
const sceneNum = div.getAttribute("data-scene");
|
|
1415
|
+
if (sceneNum) block.sceneNumber = sceneNum;
|
|
1416
|
+
let parsedText = divText;
|
|
1417
|
+
const typeMatch = parsedText.match(/^(INT\/EXT|INT|EXT)\.?\s+/i);
|
|
1418
|
+
if (typeMatch) {
|
|
1419
|
+
let sType = typeMatch[1].toUpperCase();
|
|
1420
|
+
if (!sType.endsWith(".")) sType += ".";
|
|
1421
|
+
block.sceneType = sType;
|
|
1422
|
+
parsedText = parsedText.substring(typeMatch[0].length).trim();
|
|
1423
|
+
}
|
|
1424
|
+
const timeMatch = parsedText.match(/\s+-\s+([^-]+)$/);
|
|
1425
|
+
if (timeMatch) {
|
|
1426
|
+
block.timeOfDay = timeMatch[1].trim().toUpperCase();
|
|
1427
|
+
parsedText = parsedText.substring(0, timeMatch.index).trim();
|
|
1428
|
+
}
|
|
1429
|
+
block.text = parsedText;
|
|
1430
|
+
}
|
|
1431
|
+
parsed.push(block);
|
|
1432
|
+
});
|
|
1433
|
+
if (parsed.length > 0) {
|
|
1434
|
+
preParsedBlocks = parsed;
|
|
1435
|
+
scriptContent = "";
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
const filename = ((_a = url.split("/").pop()) == null ? void 0 : _a.replace(/\.sbx$/i, "")) || "Imported from URL";
|
|
1439
|
+
handleScriptImport(filename, scriptContent, preParsedBlocks);
|
|
1440
|
+
} catch (error) {
|
|
1441
|
+
console.error(
|
|
1442
|
+
"[useScreenplayEditor] Error loading script from URL:",
|
|
1443
|
+
error
|
|
1444
|
+
);
|
|
1445
|
+
throw error;
|
|
1446
|
+
}
|
|
1447
|
+
},
|
|
1448
|
+
[handleScriptImport]
|
|
1449
|
+
);
|
|
1450
|
+
useEffect(() => {
|
|
1451
|
+
if ((options == null ? void 0 : options.initialUrl) && options.initialUrl !== loadedUrlRef.current) {
|
|
1452
|
+
loadedUrlRef.current = options.initialUrl;
|
|
1453
|
+
loadFromUrl(options.initialUrl, options.fetchOptions);
|
|
1454
|
+
}
|
|
1455
|
+
}, [options == null ? void 0 : options.initialUrl, options == null ? void 0 : options.fetchOptions, loadFromUrl]);
|
|
1366
1456
|
return {
|
|
1367
1457
|
blocks,
|
|
1368
1458
|
refs,
|
|
@@ -1382,7 +1472,8 @@ function useScreenplayEditor() {
|
|
|
1382
1472
|
handleScriptImport,
|
|
1383
1473
|
handleSceneNumberChange,
|
|
1384
1474
|
handleFocus,
|
|
1385
|
-
handleBlur
|
|
1475
|
+
handleBlur,
|
|
1476
|
+
loadFromUrl
|
|
1386
1477
|
};
|
|
1387
1478
|
}
|
|
1388
1479
|
var handleSaveAsPdf = (blocks, sceneNumbers) => {
|