@vishu1301/script-writing 0.5.0 → 0.5.2
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 +101 -3
- 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 +102 -4
- 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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useRef, useMemo, useCallback
|
|
1
|
+
import { useState, useEffect, useRef, useMemo, useCallback } from 'react';
|
|
2
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';
|
|
@@ -306,6 +306,16 @@ function ScreenplayEditorView({
|
|
|
306
306
|
handleSceneNumberChange
|
|
307
307
|
}) {
|
|
308
308
|
const [isRulesOpen, setIsRulesOpen] = useState(false);
|
|
309
|
+
useEffect(() => {
|
|
310
|
+
const fontId = "google-font-courier-prime";
|
|
311
|
+
if (!document.getElementById(fontId)) {
|
|
312
|
+
const link = document.createElement("link");
|
|
313
|
+
link.id = fontId;
|
|
314
|
+
link.rel = "stylesheet";
|
|
315
|
+
link.href = "https://fonts.googleapis.com/css2?family=Courier+Prime:ital,wght@0,400;0,700;1,400;1,700&display=swap";
|
|
316
|
+
document.head.appendChild(link);
|
|
317
|
+
}
|
|
318
|
+
}, []);
|
|
309
319
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
310
320
|
/* @__PURE__ */ jsx("div", { className: "sticky top-6 z-50 bg-white backdrop-blur-xl border border-white/10 rounded-full shadow-2xl flex gap-1 max-w-fit p-1.5 mb-12 select-none overflow-x-auto custom-scrollbar", children: blockTypes.map((type) => {
|
|
311
321
|
var _a;
|
|
@@ -350,7 +360,7 @@ function ScreenplayEditorView({
|
|
|
350
360
|
{
|
|
351
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",
|
|
352
362
|
style: {
|
|
353
|
-
fontFamily: "'Courier Prime', 'Courier New', Courier, monospace",
|
|
363
|
+
fontFamily: "var(--font-courier-prime, 'Courier Prime', 'Courier New', Courier, monospace)",
|
|
354
364
|
paddingLeft: "1.5in",
|
|
355
365
|
paddingRight: "1in",
|
|
356
366
|
paddingTop: "1in",
|
|
@@ -914,7 +924,7 @@ function setCaretPosition(element, offset) {
|
|
|
914
924
|
sel.removeAllRanges();
|
|
915
925
|
sel.addRange(range);
|
|
916
926
|
}
|
|
917
|
-
function useScreenplayEditor() {
|
|
927
|
+
function useScreenplayEditor(options) {
|
|
918
928
|
const [blocks, setBlocks] = useState(initialBlocks);
|
|
919
929
|
const refs = useRef({});
|
|
920
930
|
const [focusedBlockId, setFocusedBlockId] = useState(
|
|
@@ -924,6 +934,7 @@ function useScreenplayEditor() {
|
|
|
924
934
|
const [showSuggestions, setShowSuggestions] = useState(false);
|
|
925
935
|
const [showExtensionSuggestions, setShowExtensionSuggestions] = useState(false);
|
|
926
936
|
const blurTimeout = useRef(null);
|
|
937
|
+
const loadedUrlRef = useRef(null);
|
|
927
938
|
const characterExtensions = useMemo(
|
|
928
939
|
() => ["(V.O.)", "(O.S.)", "(O.C.)", "(SUBTITLE)", "(CONT'D)"],
|
|
929
940
|
[]
|
|
@@ -1353,6 +1364,92 @@ function useScreenplayEditor() {
|
|
|
1353
1364
|
setShowExtensionSuggestions(false);
|
|
1354
1365
|
}, 200);
|
|
1355
1366
|
}, []);
|
|
1367
|
+
const loadFromUrl = useCallback(
|
|
1368
|
+
async (url, fetchOptions = {}) => {
|
|
1369
|
+
var _a;
|
|
1370
|
+
try {
|
|
1371
|
+
const response = await fetch(url, fetchOptions);
|
|
1372
|
+
if (!response.ok) {
|
|
1373
|
+
throw new Error(
|
|
1374
|
+
`[useScreenplayEditor] Failed to fetch script. HTTP Status: ${response.status}`
|
|
1375
|
+
);
|
|
1376
|
+
}
|
|
1377
|
+
let text = await response.text();
|
|
1378
|
+
let preParsedBlocks = void 0;
|
|
1379
|
+
let scriptContent = text;
|
|
1380
|
+
if (text.includes('class="divtype') || url.toLowerCase().includes(".sbx")) {
|
|
1381
|
+
if (text.includes("<div")) {
|
|
1382
|
+
const textarea = document.createElement("textarea");
|
|
1383
|
+
textarea.innerHTML = text;
|
|
1384
|
+
text = textarea.value;
|
|
1385
|
+
}
|
|
1386
|
+
const parser = new DOMParser();
|
|
1387
|
+
const doc = parser.parseFromString(text, "text/html");
|
|
1388
|
+
const divs = Array.from(doc.querySelectorAll("div"));
|
|
1389
|
+
const parsed = [];
|
|
1390
|
+
const typeMap = {
|
|
1391
|
+
divtype0: "SCENE_HEADING",
|
|
1392
|
+
divtype2: "ACTION",
|
|
1393
|
+
divtype3: "CHARACTER",
|
|
1394
|
+
divtype4: "PARENTHETICAL",
|
|
1395
|
+
divtype5: "DIALOGUE",
|
|
1396
|
+
divtype6: "TRANSITION"
|
|
1397
|
+
};
|
|
1398
|
+
divs.forEach((div) => {
|
|
1399
|
+
var _a2;
|
|
1400
|
+
let divText = ((_a2 = div.textContent) == null ? void 0 : _a2.trim()) || "";
|
|
1401
|
+
if (!divText) return;
|
|
1402
|
+
let type = "ACTION";
|
|
1403
|
+
for (const className of Array.from(div.classList)) {
|
|
1404
|
+
if (typeMap[className]) {
|
|
1405
|
+
type = typeMap[className];
|
|
1406
|
+
break;
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
const block = { type, text: divText };
|
|
1410
|
+
if (type === "SCENE_HEADING") {
|
|
1411
|
+
const sceneNum = div.getAttribute("data-scene");
|
|
1412
|
+
if (sceneNum) block.sceneNumber = sceneNum;
|
|
1413
|
+
let parsedText = divText;
|
|
1414
|
+
const typeMatch = parsedText.match(/^(INT\/EXT|INT|EXT)\.?\s+/i);
|
|
1415
|
+
if (typeMatch) {
|
|
1416
|
+
let sType = typeMatch[1].toUpperCase();
|
|
1417
|
+
if (!sType.endsWith(".")) sType += ".";
|
|
1418
|
+
block.sceneType = sType;
|
|
1419
|
+
parsedText = parsedText.substring(typeMatch[0].length).trim();
|
|
1420
|
+
}
|
|
1421
|
+
const timeMatch = parsedText.match(/\s+-\s+([^-]+)$/);
|
|
1422
|
+
if (timeMatch) {
|
|
1423
|
+
block.timeOfDay = timeMatch[1].trim().toUpperCase();
|
|
1424
|
+
parsedText = parsedText.substring(0, timeMatch.index).trim();
|
|
1425
|
+
}
|
|
1426
|
+
block.text = parsedText;
|
|
1427
|
+
}
|
|
1428
|
+
parsed.push(block);
|
|
1429
|
+
});
|
|
1430
|
+
if (parsed.length > 0) {
|
|
1431
|
+
preParsedBlocks = parsed;
|
|
1432
|
+
scriptContent = "";
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
const filename = ((_a = url.split("/").pop()) == null ? void 0 : _a.replace(/\.sbx$/i, "")) || "Imported from URL";
|
|
1436
|
+
handleScriptImport(filename, scriptContent, preParsedBlocks);
|
|
1437
|
+
} catch (error) {
|
|
1438
|
+
console.error(
|
|
1439
|
+
"[useScreenplayEditor] Error loading script from URL:",
|
|
1440
|
+
error
|
|
1441
|
+
);
|
|
1442
|
+
throw error;
|
|
1443
|
+
}
|
|
1444
|
+
},
|
|
1445
|
+
[handleScriptImport]
|
|
1446
|
+
);
|
|
1447
|
+
useEffect(() => {
|
|
1448
|
+
if ((options == null ? void 0 : options.initialUrl) && options.initialUrl !== loadedUrlRef.current) {
|
|
1449
|
+
loadedUrlRef.current = options.initialUrl;
|
|
1450
|
+
loadFromUrl(options.initialUrl, options.fetchOptions);
|
|
1451
|
+
}
|
|
1452
|
+
}, [options == null ? void 0 : options.initialUrl, options == null ? void 0 : options.fetchOptions, loadFromUrl]);
|
|
1356
1453
|
return {
|
|
1357
1454
|
blocks,
|
|
1358
1455
|
refs,
|
|
@@ -1372,7 +1469,8 @@ function useScreenplayEditor() {
|
|
|
1372
1469
|
handleScriptImport,
|
|
1373
1470
|
handleSceneNumberChange,
|
|
1374
1471
|
handleFocus,
|
|
1375
|
-
handleBlur
|
|
1472
|
+
handleBlur,
|
|
1473
|
+
loadFromUrl
|
|
1376
1474
|
};
|
|
1377
1475
|
}
|
|
1378
1476
|
var handleSaveAsPdf = (blocks, sceneNumbers) => {
|