@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.cjs
CHANGED
|
@@ -331,6 +331,16 @@ function ScreenplayEditorView({
|
|
|
331
331
|
handleSceneNumberChange
|
|
332
332
|
}) {
|
|
333
333
|
const [isRulesOpen, setIsRulesOpen] = react.useState(false);
|
|
334
|
+
react.useEffect(() => {
|
|
335
|
+
const fontId = "google-font-courier-prime";
|
|
336
|
+
if (!document.getElementById(fontId)) {
|
|
337
|
+
const link = document.createElement("link");
|
|
338
|
+
link.id = fontId;
|
|
339
|
+
link.rel = "stylesheet";
|
|
340
|
+
link.href = "https://fonts.googleapis.com/css2?family=Courier+Prime:ital,wght@0,400;0,700;1,400;1,700&display=swap";
|
|
341
|
+
document.head.appendChild(link);
|
|
342
|
+
}
|
|
343
|
+
}, []);
|
|
334
344
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
335
345
|
/* @__PURE__ */ jsxRuntime.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) => {
|
|
336
346
|
var _a;
|
|
@@ -375,7 +385,7 @@ function ScreenplayEditorView({
|
|
|
375
385
|
{
|
|
376
386
|
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",
|
|
377
387
|
style: {
|
|
378
|
-
fontFamily: "'Courier Prime', 'Courier New', Courier, monospace",
|
|
388
|
+
fontFamily: "var(--font-courier-prime, 'Courier Prime', 'Courier New', Courier, monospace)",
|
|
379
389
|
paddingLeft: "1.5in",
|
|
380
390
|
paddingRight: "1in",
|
|
381
391
|
paddingTop: "1in",
|
|
@@ -939,7 +949,7 @@ function setCaretPosition(element, offset) {
|
|
|
939
949
|
sel.removeAllRanges();
|
|
940
950
|
sel.addRange(range);
|
|
941
951
|
}
|
|
942
|
-
function useScreenplayEditor() {
|
|
952
|
+
function useScreenplayEditor(options) {
|
|
943
953
|
const [blocks, setBlocks] = react.useState(initialBlocks);
|
|
944
954
|
const refs = react.useRef({});
|
|
945
955
|
const [focusedBlockId, setFocusedBlockId] = react.useState(
|
|
@@ -949,6 +959,7 @@ function useScreenplayEditor() {
|
|
|
949
959
|
const [showSuggestions, setShowSuggestions] = react.useState(false);
|
|
950
960
|
const [showExtensionSuggestions, setShowExtensionSuggestions] = react.useState(false);
|
|
951
961
|
const blurTimeout = react.useRef(null);
|
|
962
|
+
const loadedUrlRef = react.useRef(null);
|
|
952
963
|
const characterExtensions = react.useMemo(
|
|
953
964
|
() => ["(V.O.)", "(O.S.)", "(O.C.)", "(SUBTITLE)", "(CONT'D)"],
|
|
954
965
|
[]
|
|
@@ -1378,6 +1389,92 @@ function useScreenplayEditor() {
|
|
|
1378
1389
|
setShowExtensionSuggestions(false);
|
|
1379
1390
|
}, 200);
|
|
1380
1391
|
}, []);
|
|
1392
|
+
const loadFromUrl = react.useCallback(
|
|
1393
|
+
async (url, fetchOptions = {}) => {
|
|
1394
|
+
var _a;
|
|
1395
|
+
try {
|
|
1396
|
+
const response = await fetch(url, fetchOptions);
|
|
1397
|
+
if (!response.ok) {
|
|
1398
|
+
throw new Error(
|
|
1399
|
+
`[useScreenplayEditor] Failed to fetch script. HTTP Status: ${response.status}`
|
|
1400
|
+
);
|
|
1401
|
+
}
|
|
1402
|
+
let text = await response.text();
|
|
1403
|
+
let preParsedBlocks = void 0;
|
|
1404
|
+
let scriptContent = text;
|
|
1405
|
+
if (text.includes('class="divtype') || url.toLowerCase().includes(".sbx")) {
|
|
1406
|
+
if (text.includes("<div")) {
|
|
1407
|
+
const textarea = document.createElement("textarea");
|
|
1408
|
+
textarea.innerHTML = text;
|
|
1409
|
+
text = textarea.value;
|
|
1410
|
+
}
|
|
1411
|
+
const parser = new DOMParser();
|
|
1412
|
+
const doc = parser.parseFromString(text, "text/html");
|
|
1413
|
+
const divs = Array.from(doc.querySelectorAll("div"));
|
|
1414
|
+
const parsed = [];
|
|
1415
|
+
const typeMap = {
|
|
1416
|
+
divtype0: "SCENE_HEADING",
|
|
1417
|
+
divtype2: "ACTION",
|
|
1418
|
+
divtype3: "CHARACTER",
|
|
1419
|
+
divtype4: "PARENTHETICAL",
|
|
1420
|
+
divtype5: "DIALOGUE",
|
|
1421
|
+
divtype6: "TRANSITION"
|
|
1422
|
+
};
|
|
1423
|
+
divs.forEach((div) => {
|
|
1424
|
+
var _a2;
|
|
1425
|
+
let divText = ((_a2 = div.textContent) == null ? void 0 : _a2.trim()) || "";
|
|
1426
|
+
if (!divText) return;
|
|
1427
|
+
let type = "ACTION";
|
|
1428
|
+
for (const className of Array.from(div.classList)) {
|
|
1429
|
+
if (typeMap[className]) {
|
|
1430
|
+
type = typeMap[className];
|
|
1431
|
+
break;
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
const block = { type, text: divText };
|
|
1435
|
+
if (type === "SCENE_HEADING") {
|
|
1436
|
+
const sceneNum = div.getAttribute("data-scene");
|
|
1437
|
+
if (sceneNum) block.sceneNumber = sceneNum;
|
|
1438
|
+
let parsedText = divText;
|
|
1439
|
+
const typeMatch = parsedText.match(/^(INT\/EXT|INT|EXT)\.?\s+/i);
|
|
1440
|
+
if (typeMatch) {
|
|
1441
|
+
let sType = typeMatch[1].toUpperCase();
|
|
1442
|
+
if (!sType.endsWith(".")) sType += ".";
|
|
1443
|
+
block.sceneType = sType;
|
|
1444
|
+
parsedText = parsedText.substring(typeMatch[0].length).trim();
|
|
1445
|
+
}
|
|
1446
|
+
const timeMatch = parsedText.match(/\s+-\s+([^-]+)$/);
|
|
1447
|
+
if (timeMatch) {
|
|
1448
|
+
block.timeOfDay = timeMatch[1].trim().toUpperCase();
|
|
1449
|
+
parsedText = parsedText.substring(0, timeMatch.index).trim();
|
|
1450
|
+
}
|
|
1451
|
+
block.text = parsedText;
|
|
1452
|
+
}
|
|
1453
|
+
parsed.push(block);
|
|
1454
|
+
});
|
|
1455
|
+
if (parsed.length > 0) {
|
|
1456
|
+
preParsedBlocks = parsed;
|
|
1457
|
+
scriptContent = "";
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
const filename = ((_a = url.split("/").pop()) == null ? void 0 : _a.replace(/\.sbx$/i, "")) || "Imported from URL";
|
|
1461
|
+
handleScriptImport(filename, scriptContent, preParsedBlocks);
|
|
1462
|
+
} catch (error) {
|
|
1463
|
+
console.error(
|
|
1464
|
+
"[useScreenplayEditor] Error loading script from URL:",
|
|
1465
|
+
error
|
|
1466
|
+
);
|
|
1467
|
+
throw error;
|
|
1468
|
+
}
|
|
1469
|
+
},
|
|
1470
|
+
[handleScriptImport]
|
|
1471
|
+
);
|
|
1472
|
+
react.useEffect(() => {
|
|
1473
|
+
if ((options == null ? void 0 : options.initialUrl) && options.initialUrl !== loadedUrlRef.current) {
|
|
1474
|
+
loadedUrlRef.current = options.initialUrl;
|
|
1475
|
+
loadFromUrl(options.initialUrl, options.fetchOptions);
|
|
1476
|
+
}
|
|
1477
|
+
}, [options == null ? void 0 : options.initialUrl, options == null ? void 0 : options.fetchOptions, loadFromUrl]);
|
|
1381
1478
|
return {
|
|
1382
1479
|
blocks,
|
|
1383
1480
|
refs,
|
|
@@ -1397,7 +1494,8 @@ function useScreenplayEditor() {
|
|
|
1397
1494
|
handleScriptImport,
|
|
1398
1495
|
handleSceneNumberChange,
|
|
1399
1496
|
handleFocus,
|
|
1400
|
-
handleBlur
|
|
1497
|
+
handleBlur,
|
|
1498
|
+
loadFromUrl
|
|
1401
1499
|
};
|
|
1402
1500
|
}
|
|
1403
1501
|
var handleSaveAsPdf = (blocks, sceneNumbers) => {
|