@vishu1301/script-writing 0.4.4 → 0.4.5
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 +208 -348
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -4
- package/dist/index.d.ts +1 -4
- package/dist/index.js +209 -349
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useRef,
|
|
1
|
+
import { useState, useRef, useMemo, useCallback, useEffect } from 'react';
|
|
2
2
|
import { ArrowRightLeft, MessageCircle, Brackets, UserRound, Sparkles, Clapperboard, ArrowRight, User, ChevronRight, Upload, Save, FileDown, Cog } from 'lucide-react';
|
|
3
3
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
import * as pdfjs from 'pdfjs-dist';
|
|
@@ -220,9 +220,6 @@ function PdfImporter({ onScriptImported, children }) {
|
|
|
220
220
|
}
|
|
221
221
|
function ScreenplayEditorView({
|
|
222
222
|
blocks,
|
|
223
|
-
pages,
|
|
224
|
-
isPageSplitEnabled,
|
|
225
|
-
togglePageSplit,
|
|
226
223
|
refs,
|
|
227
224
|
focusedBlockId,
|
|
228
225
|
showSuggestions,
|
|
@@ -285,131 +282,56 @@ function ScreenplayEditorView({
|
|
|
285
282
|
type
|
|
286
283
|
);
|
|
287
284
|
}) }),
|
|
288
|
-
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-12 w-full items-center pb-24", children:
|
|
285
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-12 w-full items-center pb-24", children: /* @__PURE__ */ jsx(
|
|
289
286
|
"div",
|
|
290
287
|
{
|
|
291
288
|
className: "relative bg-[#fdfdfc] shadow-2xl shadow-zinc-300/60 ring-1 ring-zinc-200/50 rounded-sm md:rounded-md pl-[1.5in] py-[1in] pr-[1in] flex flex-col w-[210mm] min-h-[297mm] shrink-0",
|
|
292
289
|
style: {
|
|
293
290
|
fontFamily: "var(--font-courier-prime, 'Courier New', Courier, monospace)"
|
|
294
291
|
},
|
|
295
|
-
children:
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
/* @__PURE__ */
|
|
305
|
-
|
|
306
|
-
"input",
|
|
307
|
-
{
|
|
308
|
-
className: "absolute -left-16 top-2 w-12 text-right text-zinc-400 font-semibold select-none bg-transparent outline-none focus:ring-1 focus:ring-blue-400 rounded-sm",
|
|
309
|
-
spellCheck: false,
|
|
310
|
-
value: block.sceneNumber || "",
|
|
311
|
-
onChange: (e) => handleSceneNumberChange(
|
|
312
|
-
block.id,
|
|
313
|
-
e.target.value.toUpperCase()
|
|
314
|
-
),
|
|
315
|
-
onFocus: () => handleFocus(block.id),
|
|
316
|
-
onBlur: () => handleBlur(block.id),
|
|
317
|
-
onKeyDown: (e) => {
|
|
318
|
-
if (e.key === "Enter" || e.key === "Backspace") {
|
|
319
|
-
e.stopPropagation();
|
|
320
|
-
}
|
|
321
|
-
},
|
|
322
|
-
"aria-label": "Scene Number"
|
|
323
|
-
}
|
|
324
|
-
),
|
|
325
|
-
/* @__PURE__ */ jsxs(
|
|
326
|
-
"select",
|
|
327
|
-
{
|
|
328
|
-
className: "rounded-md text-zinc-800 font-bold px-1.5 py-1 appearance-none bg-transparent hover:bg-zinc-200/50 outline-none cursor-pointer w-fit transition-colors",
|
|
329
|
-
"aria-label": "Scene Type",
|
|
330
|
-
value: (_a = block.sceneType) != null ? _a : "INT.",
|
|
331
|
-
onChange: (e) => handleSceneTypeChange(block.id, e.target.value),
|
|
332
|
-
children: [
|
|
333
|
-
/* @__PURE__ */ jsx("option", { children: "INT." }),
|
|
334
|
-
/* @__PURE__ */ jsx("option", { children: "EXT." }),
|
|
335
|
-
/* @__PURE__ */ jsx("option", { children: "INT/EXT." })
|
|
336
|
-
]
|
|
337
|
-
}
|
|
338
|
-
),
|
|
339
|
-
/* @__PURE__ */ jsx(
|
|
340
|
-
"div",
|
|
341
|
-
{
|
|
342
|
-
ref: (el) => {
|
|
343
|
-
if (!el) return;
|
|
344
|
-
refs.current[block.id] = el;
|
|
345
|
-
},
|
|
346
|
-
contentEditable: true,
|
|
347
|
-
suppressContentEditableWarning: true,
|
|
348
|
-
"aria-label": `Scene Heading: ${block.text}`,
|
|
349
|
-
"aria-haspopup": "listbox",
|
|
350
|
-
"aria-expanded": focusedBlockId === block.id && showSuggestions && locations.length > 0,
|
|
351
|
-
spellCheck: false,
|
|
352
|
-
className: "min-w-[3rem] py-1 outline-none text-base font-bold uppercase tracking-widest break-all bg-transparent",
|
|
353
|
-
onInput: (e) => handleBlockTextChange(
|
|
354
|
-
block.id,
|
|
355
|
-
e.target.innerText
|
|
356
|
-
),
|
|
357
|
-
onKeyDown: (e) => handleKeyDown(e, block.id, block.text),
|
|
358
|
-
onFocus: () => handleFocus(block.id),
|
|
359
|
-
onBlur: () => handleBlur(block.id)
|
|
360
|
-
}
|
|
361
|
-
),
|
|
362
|
-
/* @__PURE__ */ jsx("span", { className: "text-zinc-400/80 font-bold", children: "-" }),
|
|
363
|
-
/* @__PURE__ */ jsx(
|
|
364
|
-
"select",
|
|
365
|
-
{
|
|
366
|
-
className: "rounded-md text-zinc-800 font-bold px-1.5 py-1 appearance-none bg-transparent hover:bg-zinc-200/50 outline-none cursor-pointer transition-colors",
|
|
367
|
-
"aria-label": "Time of Day",
|
|
368
|
-
value: (_b = block.timeOfDay) != null ? _b : "DAY",
|
|
369
|
-
onChange: (e) => handleTimeOfDayChange(block.id, e.target.value),
|
|
370
|
-
children: timeOfDayOptions.map((t) => /* @__PURE__ */ jsx("option", { children: t }, t))
|
|
371
|
-
}
|
|
372
|
-
)
|
|
373
|
-
] }),
|
|
374
|
-
focusedBlockId === block.id && showSuggestions && locations.length > 0 && /* @__PURE__ */ jsx(
|
|
375
|
-
"div",
|
|
292
|
+
children: blocks.map((block) => {
|
|
293
|
+
var _a, _b;
|
|
294
|
+
return /* @__PURE__ */ jsx(
|
|
295
|
+
"div",
|
|
296
|
+
{
|
|
297
|
+
"data-block-id": block.id,
|
|
298
|
+
className: `relative rounded-sm transition-all duration-200 outline-none ${focusedBlockId === block.id ? "bg-zinc-100/50" : "bg-transparent"}`,
|
|
299
|
+
children: block.type === "SCENE_HEADING" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
300
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-4 py-1 bg-transparent", children: [
|
|
301
|
+
/* @__PURE__ */ jsx(
|
|
302
|
+
"input",
|
|
376
303
|
{
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
handleBlockTextChange(block.id, loc);
|
|
393
|
-
element.focus();
|
|
394
|
-
const range = document.createRange();
|
|
395
|
-
const sel = window.getSelection();
|
|
396
|
-
range.selectNodeContents(element);
|
|
397
|
-
range.collapse(false);
|
|
398
|
-
sel == null ? void 0 : sel.removeAllRanges();
|
|
399
|
-
sel == null ? void 0 : sel.addRange(range);
|
|
400
|
-
}
|
|
401
|
-
handleBlur(block.id);
|
|
402
|
-
},
|
|
403
|
-
children: [
|
|
404
|
-
/* @__PURE__ */ jsx("span", { className: "text-[12px] font-semibold tracking-wide text-slate-600 uppercase line-clamp-1", children: loc }),
|
|
405
|
-
/* @__PURE__ */ jsx(ArrowRight, { className: "w-3.5 h-3.5 text-slate-300 opacity-0 -translate-x-2 group-hover:opacity-100 group-hover:translate-x-0 transition-all duration-200" })
|
|
406
|
-
]
|
|
407
|
-
},
|
|
408
|
-
loc
|
|
409
|
-
)) })
|
|
304
|
+
className: "absolute -left-16 top-2 w-12 text-right text-zinc-400 font-semibold select-none bg-transparent outline-none focus:ring-1 focus:ring-blue-400 rounded-sm",
|
|
305
|
+
spellCheck: false,
|
|
306
|
+
value: block.sceneNumber || "",
|
|
307
|
+
onChange: (e) => handleSceneNumberChange(
|
|
308
|
+
block.id,
|
|
309
|
+
e.target.value.toUpperCase()
|
|
310
|
+
),
|
|
311
|
+
onFocus: () => handleFocus(block.id),
|
|
312
|
+
onBlur: () => handleBlur(block.id),
|
|
313
|
+
onKeyDown: (e) => {
|
|
314
|
+
if (e.key === "Enter" || e.key === "Backspace") {
|
|
315
|
+
e.stopPropagation();
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
"aria-label": "Scene Number"
|
|
410
319
|
}
|
|
411
|
-
)
|
|
412
|
-
|
|
320
|
+
),
|
|
321
|
+
/* @__PURE__ */ jsxs(
|
|
322
|
+
"select",
|
|
323
|
+
{
|
|
324
|
+
className: "rounded-md text-zinc-800 font-bold px-1.5 py-1 appearance-none bg-transparent hover:bg-zinc-200/50 outline-none cursor-pointer w-fit transition-colors",
|
|
325
|
+
"aria-label": "Scene Type",
|
|
326
|
+
value: (_a = block.sceneType) != null ? _a : "INT.",
|
|
327
|
+
onChange: (e) => handleSceneTypeChange(block.id, e.target.value),
|
|
328
|
+
children: [
|
|
329
|
+
/* @__PURE__ */ jsx("option", { children: "INT." }),
|
|
330
|
+
/* @__PURE__ */ jsx("option", { children: "EXT." }),
|
|
331
|
+
/* @__PURE__ */ jsx("option", { children: "INT/EXT." })
|
|
332
|
+
]
|
|
333
|
+
}
|
|
334
|
+
),
|
|
413
335
|
/* @__PURE__ */ jsx(
|
|
414
336
|
"div",
|
|
415
337
|
{
|
|
@@ -419,100 +341,168 @@ function ScreenplayEditorView({
|
|
|
419
341
|
},
|
|
420
342
|
contentEditable: true,
|
|
421
343
|
suppressContentEditableWarning: true,
|
|
422
|
-
"aria-label":
|
|
423
|
-
"aria-
|
|
344
|
+
"aria-label": `Scene Heading: ${block.text}`,
|
|
345
|
+
"aria-haspopup": "listbox",
|
|
346
|
+
"aria-expanded": focusedBlockId === block.id && showSuggestions && locations.length > 0,
|
|
424
347
|
spellCheck: false,
|
|
425
|
-
className:
|
|
348
|
+
className: "min-w-[3rem] py-1 outline-none text-base font-bold uppercase tracking-widest break-all bg-transparent",
|
|
426
349
|
onInput: (e) => handleBlockTextChange(
|
|
427
350
|
block.id,
|
|
428
351
|
e.target.innerText
|
|
429
352
|
),
|
|
430
353
|
onKeyDown: (e) => handleKeyDown(e, block.id, block.text),
|
|
431
354
|
onFocus: () => handleFocus(block.id),
|
|
432
|
-
onBlur: () => handleBlur(block.id)
|
|
433
|
-
style: blockStyles[block.type].inputStyle
|
|
355
|
+
onBlur: () => handleBlur(block.id)
|
|
434
356
|
}
|
|
435
357
|
),
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
role: "listbox",
|
|
440
|
-
id: `suggestions-${block.id}`,
|
|
441
|
-
className: "absolute top-[calc(100%+8px)] left-1/2 -translate-x-1/2 w-72 z-50 bg-white border border-slate-200 shadow-2xl shadow-slate-200/60 rounded-xl py-2 overflow-hidden animate-in fade-in zoom-in-95 duration-200",
|
|
442
|
-
children: /* @__PURE__ */ jsx("div", { className: "max-h-56 overflow-y-auto custom-scrollbar", children: characters.filter(
|
|
443
|
-
(char) => char.startsWith(block.text.toUpperCase()) && char !== block.text.toUpperCase()
|
|
444
|
-
).map((char) => /* @__PURE__ */ jsxs(
|
|
445
|
-
"div",
|
|
446
|
-
{
|
|
447
|
-
role: "option",
|
|
448
|
-
className: "group flex items-center px-4 py-2.5 cursor-pointer transition-colors duration-150 hover:bg-slate-50 active:bg-slate-100",
|
|
449
|
-
onMouseDown: (e) => {
|
|
450
|
-
e.preventDefault();
|
|
451
|
-
const element = refs.current[block.id];
|
|
452
|
-
if (element) {
|
|
453
|
-
element.innerText = char;
|
|
454
|
-
handleBlockTextChange(block.id, char);
|
|
455
|
-
element.focus();
|
|
456
|
-
const range = document.createRange();
|
|
457
|
-
const sel = window.getSelection();
|
|
458
|
-
range.selectNodeContents(element);
|
|
459
|
-
range.collapse(false);
|
|
460
|
-
sel == null ? void 0 : sel.removeAllRanges();
|
|
461
|
-
sel == null ? void 0 : sel.addRange(range);
|
|
462
|
-
}
|
|
463
|
-
handleBlur(block.id);
|
|
464
|
-
},
|
|
465
|
-
children: [
|
|
466
|
-
/* @__PURE__ */ jsx(User, { className: "w-3.5 h-3.5 text-slate-300 group-hover:text-sky-500 transition-colors mr-3" }),
|
|
467
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 text-[11px] font-bold tracking-[0.1em] text-slate-600 uppercase text-left", children: char }),
|
|
468
|
-
/* @__PURE__ */ jsx(ChevronRight, { className: "w-3 h-3 text-slate-200 opacity-0 group-hover:opacity-100 transition-all -translate-x-1 group-hover:translate-x-0" })
|
|
469
|
-
]
|
|
470
|
-
},
|
|
471
|
-
char
|
|
472
|
-
)) })
|
|
473
|
-
}
|
|
474
|
-
),
|
|
475
|
-
focusedBlockId === block.id && block.type === "CHARACTER" && showExtensionSuggestions && characterExtensions && /* @__PURE__ */ jsx(
|
|
476
|
-
"div",
|
|
358
|
+
/* @__PURE__ */ jsx("span", { className: "text-zinc-400/80 font-bold", children: "-" }),
|
|
359
|
+
/* @__PURE__ */ jsx(
|
|
360
|
+
"select",
|
|
477
361
|
{
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
const query = openParenIndex > -1 ? block.text.substring(openParenIndex + 1).toUpperCase() : "";
|
|
484
|
-
return ext.toUpperCase().includes(query);
|
|
485
|
-
}).map((ext) => /* @__PURE__ */ jsxs(
|
|
486
|
-
"div",
|
|
487
|
-
{
|
|
488
|
-
role: "option",
|
|
489
|
-
className: "group flex items-center px-4 py-2.5 cursor-pointer transition-colors duration-150 hover:bg-slate-50 active:bg-slate-100",
|
|
490
|
-
onMouseDown: (e) => {
|
|
491
|
-
e.preventDefault();
|
|
492
|
-
handleSelectCharacterExtension(ext);
|
|
493
|
-
},
|
|
494
|
-
children: [
|
|
495
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 text-[11px] font-bold tracking-[0.1em] text-slate-600 uppercase text-left", children: ext }),
|
|
496
|
-
/* @__PURE__ */ jsx(ChevronRight, { className: "w-3 h-3 text-slate-200 opacity-0 group-hover:opacity-100 transition-all -translate-x-1 group-hover:translate-x-0" })
|
|
497
|
-
]
|
|
498
|
-
},
|
|
499
|
-
ext
|
|
500
|
-
)) })
|
|
362
|
+
className: "rounded-md text-zinc-800 font-bold px-1.5 py-1 appearance-none bg-transparent hover:bg-zinc-200/50 outline-none cursor-pointer transition-colors",
|
|
363
|
+
"aria-label": "Time of Day",
|
|
364
|
+
value: (_b = block.timeOfDay) != null ? _b : "DAY",
|
|
365
|
+
onChange: (e) => handleTimeOfDayChange(block.id, e.target.value),
|
|
366
|
+
children: timeOfDayOptions.map((t) => /* @__PURE__ */ jsx("option", { children: t }, t))
|
|
501
367
|
}
|
|
502
368
|
)
|
|
503
|
-
] })
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
369
|
+
] }),
|
|
370
|
+
focusedBlockId === block.id && showSuggestions && locations.length > 0 && /* @__PURE__ */ jsx(
|
|
371
|
+
"div",
|
|
372
|
+
{
|
|
373
|
+
role: "listbox",
|
|
374
|
+
id: `suggestions-${block.id}`,
|
|
375
|
+
className: "absolute top-[calc(100%+6px)] left-0 min-w-[240px] z-50 bg-white border border-slate-200/80 shadow-xl shadow-slate-200/40 rounded-xl py-1 overflow-hidden animate-in fade-in zoom-in-95 duration-150",
|
|
376
|
+
children: /* @__PURE__ */ jsx("div", { className: "max-h-60 overflow-y-auto custom-scrollbar", children: locations.filter(
|
|
377
|
+
(loc) => loc.startsWith(block.text.toUpperCase()) && loc !== block.text.toUpperCase()
|
|
378
|
+
).map((loc) => /* @__PURE__ */ jsxs(
|
|
379
|
+
"div",
|
|
380
|
+
{
|
|
381
|
+
role: "option",
|
|
382
|
+
className: "group flex items-center justify-between px-4 py-2.5 cursor-pointer transition-all duration-150 hover:bg-slate-50 active:bg-slate-100",
|
|
383
|
+
onMouseDown: (e) => {
|
|
384
|
+
e.preventDefault();
|
|
385
|
+
const element = refs.current[block.id];
|
|
386
|
+
if (element) {
|
|
387
|
+
element.innerText = loc;
|
|
388
|
+
handleBlockTextChange(block.id, loc);
|
|
389
|
+
element.focus();
|
|
390
|
+
const range = document.createRange();
|
|
391
|
+
const sel = window.getSelection();
|
|
392
|
+
range.selectNodeContents(element);
|
|
393
|
+
range.collapse(false);
|
|
394
|
+
sel == null ? void 0 : sel.removeAllRanges();
|
|
395
|
+
sel == null ? void 0 : sel.addRange(range);
|
|
396
|
+
}
|
|
397
|
+
handleBlur(block.id);
|
|
398
|
+
},
|
|
399
|
+
children: [
|
|
400
|
+
/* @__PURE__ */ jsx("span", { className: "text-[12px] font-semibold tracking-wide text-slate-600 uppercase line-clamp-1", children: loc }),
|
|
401
|
+
/* @__PURE__ */ jsx(ArrowRight, { className: "w-3.5 h-3.5 text-slate-300 opacity-0 -translate-x-2 group-hover:opacity-100 group-hover:translate-x-0 transition-all duration-200" })
|
|
402
|
+
]
|
|
403
|
+
},
|
|
404
|
+
loc
|
|
405
|
+
)) })
|
|
406
|
+
}
|
|
407
|
+
)
|
|
408
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
409
|
+
/* @__PURE__ */ jsx(
|
|
410
|
+
"div",
|
|
411
|
+
{
|
|
412
|
+
ref: (el) => {
|
|
413
|
+
if (!el) return;
|
|
414
|
+
refs.current[block.id] = el;
|
|
415
|
+
},
|
|
416
|
+
contentEditable: true,
|
|
417
|
+
suppressContentEditableWarning: true,
|
|
418
|
+
"aria-label": `${blockStyles[block.type].label} text`,
|
|
419
|
+
"aria-multiline": block.type === "ACTION" || block.type === "DIALOGUE",
|
|
420
|
+
spellCheck: false,
|
|
421
|
+
className: `block outline-none w-full min-h-[2.5rem] px-4 py-2 break-words ${blockStyles[block.type].className}`,
|
|
422
|
+
onInput: (e) => handleBlockTextChange(
|
|
423
|
+
block.id,
|
|
424
|
+
e.target.innerText
|
|
425
|
+
),
|
|
426
|
+
onKeyDown: (e) => handleKeyDown(e, block.id, block.text),
|
|
427
|
+
onFocus: () => handleFocus(block.id),
|
|
428
|
+
onBlur: () => handleBlur(block.id),
|
|
429
|
+
style: blockStyles[block.type].inputStyle
|
|
430
|
+
}
|
|
431
|
+
),
|
|
432
|
+
focusedBlockId === block.id && block.type === "CHARACTER" && showSuggestions && characters.length > 0 && /* @__PURE__ */ jsx(
|
|
433
|
+
"div",
|
|
434
|
+
{
|
|
435
|
+
role: "listbox",
|
|
436
|
+
id: `suggestions-${block.id}`,
|
|
437
|
+
className: "absolute top-[calc(100%+8px)] left-1/2 -translate-x-1/2 w-72 z-50 bg-white border border-slate-200 shadow-2xl shadow-slate-200/60 rounded-xl py-2 overflow-hidden animate-in fade-in zoom-in-95 duration-200",
|
|
438
|
+
children: /* @__PURE__ */ jsx("div", { className: "max-h-56 overflow-y-auto custom-scrollbar", children: characters.filter(
|
|
439
|
+
(char) => char.startsWith(block.text.toUpperCase()) && char !== block.text.toUpperCase()
|
|
440
|
+
).map((char) => /* @__PURE__ */ jsxs(
|
|
441
|
+
"div",
|
|
442
|
+
{
|
|
443
|
+
role: "option",
|
|
444
|
+
className: "group flex items-center px-4 py-2.5 cursor-pointer transition-colors duration-150 hover:bg-slate-50 active:bg-slate-100",
|
|
445
|
+
onMouseDown: (e) => {
|
|
446
|
+
e.preventDefault();
|
|
447
|
+
const element = refs.current[block.id];
|
|
448
|
+
if (element) {
|
|
449
|
+
element.innerText = char;
|
|
450
|
+
handleBlockTextChange(block.id, char);
|
|
451
|
+
element.focus();
|
|
452
|
+
const range = document.createRange();
|
|
453
|
+
const sel = window.getSelection();
|
|
454
|
+
range.selectNodeContents(element);
|
|
455
|
+
range.collapse(false);
|
|
456
|
+
sel == null ? void 0 : sel.removeAllRanges();
|
|
457
|
+
sel == null ? void 0 : sel.addRange(range);
|
|
458
|
+
}
|
|
459
|
+
handleBlur(block.id);
|
|
460
|
+
},
|
|
461
|
+
children: [
|
|
462
|
+
/* @__PURE__ */ jsx(User, { className: "w-3.5 h-3.5 text-slate-300 group-hover:text-sky-500 transition-colors mr-3" }),
|
|
463
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 text-[11px] font-bold tracking-[0.1em] text-slate-600 uppercase text-left", children: char }),
|
|
464
|
+
/* @__PURE__ */ jsx(ChevronRight, { className: "w-3 h-3 text-slate-200 opacity-0 group-hover:opacity-100 transition-all -translate-x-1 group-hover:translate-x-0" })
|
|
465
|
+
]
|
|
466
|
+
},
|
|
467
|
+
char
|
|
468
|
+
)) })
|
|
469
|
+
}
|
|
470
|
+
),
|
|
471
|
+
focusedBlockId === block.id && block.type === "CHARACTER" && showExtensionSuggestions && characterExtensions && /* @__PURE__ */ jsx(
|
|
472
|
+
"div",
|
|
473
|
+
{
|
|
474
|
+
role: "listbox",
|
|
475
|
+
id: `extension-suggestions-${block.id}`,
|
|
476
|
+
className: "absolute top-[calc(100%+8px)] left-1/2 -translate-x-1/2 w-72 z-50 bg-white border border-slate-200 shadow-2xl shadow-slate-200/60 rounded-xl py-2 overflow-hidden animate-in fade-in zoom-in-95 duration-200",
|
|
477
|
+
children: /* @__PURE__ */ jsx("div", { className: "max-h-56 overflow-y-auto custom-scrollbar", children: characterExtensions.filter((ext) => {
|
|
478
|
+
const openParenIndex = block.text.lastIndexOf("(");
|
|
479
|
+
const query = openParenIndex > -1 ? block.text.substring(openParenIndex + 1).toUpperCase() : "";
|
|
480
|
+
return ext.toUpperCase().includes(query);
|
|
481
|
+
}).map((ext) => /* @__PURE__ */ jsxs(
|
|
482
|
+
"div",
|
|
483
|
+
{
|
|
484
|
+
role: "option",
|
|
485
|
+
className: "group flex items-center px-4 py-2.5 cursor-pointer transition-colors duration-150 hover:bg-slate-50 active:bg-slate-100",
|
|
486
|
+
onMouseDown: (e) => {
|
|
487
|
+
e.preventDefault();
|
|
488
|
+
handleSelectCharacterExtension(ext);
|
|
489
|
+
},
|
|
490
|
+
children: [
|
|
491
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 text-[11px] font-bold tracking-[0.1em] text-slate-600 uppercase text-left", children: ext }),
|
|
492
|
+
/* @__PURE__ */ jsx(ChevronRight, { className: "w-3 h-3 text-slate-200 opacity-0 group-hover:opacity-100 transition-all -translate-x-1 group-hover:translate-x-0" })
|
|
493
|
+
]
|
|
494
|
+
},
|
|
495
|
+
ext
|
|
496
|
+
)) })
|
|
497
|
+
}
|
|
498
|
+
)
|
|
499
|
+
] })
|
|
500
|
+
},
|
|
501
|
+
block.id + "-" + block.type
|
|
502
|
+
);
|
|
503
|
+
})
|
|
504
|
+
}
|
|
505
|
+
) }),
|
|
516
506
|
/* @__PURE__ */ jsxs("div", { className: "fixed bottom-6 right-6 flex flex-col items-end gap-4 z-50", children: [
|
|
517
507
|
/* @__PURE__ */ jsx(PdfImporter, { onScriptImported: handleScriptImport, children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
518
508
|
/* @__PURE__ */ jsx(Upload, { className: "w-5 h-5" }),
|
|
@@ -544,46 +534,24 @@ function ScreenplayEditorView({
|
|
|
544
534
|
),
|
|
545
535
|
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: [
|
|
546
536
|
/* @__PURE__ */ jsx("h4", { className: "font-bold text-zinc-800 mb-3 text-sm", children: "Settings & Shortcuts" }),
|
|
547
|
-
/* @__PURE__ */
|
|
548
|
-
/* @__PURE__ */ jsxs("
|
|
549
|
-
/* @__PURE__ */ jsx("span", {
|
|
550
|
-
/* @__PURE__ */ jsx(
|
|
551
|
-
"button",
|
|
552
|
-
{
|
|
553
|
-
type: "button",
|
|
554
|
-
role: "switch",
|
|
555
|
-
"aria-checked": isPageSplitEnabled,
|
|
556
|
-
onClick: togglePageSplit,
|
|
557
|
-
className: `${isPageSplitEnabled ? "bg-zinc-900" : "bg-zinc-300"} relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none`,
|
|
558
|
-
children: /* @__PURE__ */ jsx(
|
|
559
|
-
"span",
|
|
560
|
-
{
|
|
561
|
-
"aria-hidden": "true",
|
|
562
|
-
className: `${isPageSplitEnabled ? "translate-x-4" : "translate-x-0"} pointer-events-none inline-block h-4 w-4 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out`
|
|
563
|
-
}
|
|
564
|
-
)
|
|
565
|
-
}
|
|
566
|
-
)
|
|
537
|
+
/* @__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: [
|
|
538
|
+
/* @__PURE__ */ jsxs("li", { className: "flex items-center justify-between gap-6", children: [
|
|
539
|
+
/* @__PURE__ */ jsx("span", { children: "New Block" }),
|
|
540
|
+
/* @__PURE__ */ jsx("kbd", { className: "px-2 py-1 text-xs font-semibold text-zinc-800 bg-zinc-200/70 border border-zinc-300/70 rounded-md", children: "Enter" })
|
|
567
541
|
] }),
|
|
568
|
-
/* @__PURE__ */
|
|
569
|
-
/* @__PURE__ */
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
/* @__PURE__ */
|
|
574
|
-
|
|
575
|
-
/* @__PURE__ */ jsx("kbd", { className: "px-2 py-1 text-xs font-semibold text-zinc-800 bg-zinc-200/70 border border-zinc-300/70 rounded-md", children: "
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
/* @__PURE__ */ jsx("span", { children: "Change Type" }),
|
|
579
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
580
|
-
/* @__PURE__ */ jsx("kbd", { className: "px-2 py-1 text-xs font-semibold text-zinc-800 bg-zinc-200/70 border border-zinc-300/70 rounded-md", children: "Ctrl" }),
|
|
581
|
-
/* @__PURE__ */ jsx("span", { children: "+" }),
|
|
582
|
-
/* @__PURE__ */ jsx("kbd", { className: "px-2 py-1 text-xs font-semibold text-zinc-800 bg-zinc-200/70 border border-zinc-300/70 rounded-md", children: "\u2191/\u2193" })
|
|
583
|
-
] })
|
|
542
|
+
/* @__PURE__ */ jsxs("li", { className: "flex items-center justify-between gap-6", children: [
|
|
543
|
+
/* @__PURE__ */ jsx("span", { children: "Delete Block" }),
|
|
544
|
+
/* @__PURE__ */ jsx("kbd", { className: "px-2 py-1 text-xs font-semibold text-zinc-800 bg-zinc-200/70 border border-zinc-300/70 rounded-md", children: "Backspace" })
|
|
545
|
+
] }),
|
|
546
|
+
/* @__PURE__ */ jsxs("li", { className: "flex items-center justify-between gap-6", children: [
|
|
547
|
+
/* @__PURE__ */ jsx("span", { children: "Change Type" }),
|
|
548
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
549
|
+
/* @__PURE__ */ jsx("kbd", { className: "px-2 py-1 text-xs font-semibold text-zinc-800 bg-zinc-200/70 border border-zinc-300/70 rounded-md", children: "Ctrl" }),
|
|
550
|
+
/* @__PURE__ */ jsx("span", { children: "+" }),
|
|
551
|
+
/* @__PURE__ */ jsx("kbd", { className: "px-2 py-1 text-xs font-semibold text-zinc-800 bg-zinc-200/70 border border-zinc-300/70 rounded-md", children: "\u2191/\u2193" })
|
|
584
552
|
] })
|
|
585
|
-
] })
|
|
586
|
-
] })
|
|
553
|
+
] })
|
|
554
|
+
] }) }) })
|
|
587
555
|
] }),
|
|
588
556
|
/* @__PURE__ */ jsx(
|
|
589
557
|
"button",
|
|
@@ -871,19 +839,6 @@ function useScreenplayEditor() {
|
|
|
871
839
|
const [showSuggestions, setShowSuggestions] = useState(false);
|
|
872
840
|
const [showExtensionSuggestions, setShowExtensionSuggestions] = useState(false);
|
|
873
841
|
const blurTimeout = useRef(null);
|
|
874
|
-
const [isPageSplitEnabled, setIsPageSplitEnabled] = useState(false);
|
|
875
|
-
const [pageBreaks, setPageBreaks] = useState([]);
|
|
876
|
-
const focusStateRef = useRef(null);
|
|
877
|
-
const togglePageSplit = useCallback(() => {
|
|
878
|
-
if (focusedBlockId && document.activeElement && document.activeElement.hasAttribute("contenteditable")) {
|
|
879
|
-
const el = refs.current[focusedBlockId];
|
|
880
|
-
if (el) {
|
|
881
|
-
const offset = getCaretCharacterOffsetWithin(el);
|
|
882
|
-
focusStateRef.current = { id: focusedBlockId, offset };
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
setIsPageSplitEnabled((prev) => !prev);
|
|
886
|
-
}, [focusedBlockId]);
|
|
887
842
|
const characterExtensions = useMemo(
|
|
888
843
|
() => ["(V.O.)", "(O.S.)", "(O.C.)", "(SUBTITLE)", "(CONT'D)"],
|
|
889
844
|
[]
|
|
@@ -985,98 +940,6 @@ function useScreenplayEditor() {
|
|
|
985
940
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
986
941
|
};
|
|
987
942
|
}, []);
|
|
988
|
-
useEffect(() => {
|
|
989
|
-
if (!isPageSplitEnabled) {
|
|
990
|
-
setPageBreaks([]);
|
|
991
|
-
return;
|
|
992
|
-
}
|
|
993
|
-
const saveFocus = () => {
|
|
994
|
-
if (focusedBlockId && document.activeElement && document.activeElement.hasAttribute("contenteditable")) {
|
|
995
|
-
const el = refs.current[focusedBlockId];
|
|
996
|
-
if (el) {
|
|
997
|
-
const offset = getCaretCharacterOffsetWithin(el);
|
|
998
|
-
focusStateRef.current = { id: focusedBlockId, offset };
|
|
999
|
-
}
|
|
1000
|
-
} else {
|
|
1001
|
-
focusStateRef.current = null;
|
|
1002
|
-
}
|
|
1003
|
-
};
|
|
1004
|
-
const timeoutId = setTimeout(() => {
|
|
1005
|
-
let currentHeight = 0;
|
|
1006
|
-
let hasSceneOnCurrentPage = false;
|
|
1007
|
-
const A4_HEIGHT = 960;
|
|
1008
|
-
const newPageBreaks = [];
|
|
1009
|
-
blocks.forEach((block) => {
|
|
1010
|
-
const el = refs.current[block.id];
|
|
1011
|
-
if (el) {
|
|
1012
|
-
const measureEl = document.querySelector(`[data-block-id="${block.id}"]`) || el;
|
|
1013
|
-
const style = window.getComputedStyle(measureEl);
|
|
1014
|
-
const marginTop = parseFloat(style.marginTop) || 0;
|
|
1015
|
-
const marginBottom = parseFloat(style.marginBottom) || 0;
|
|
1016
|
-
const height = measureEl.getBoundingClientRect().height + marginTop + marginBottom;
|
|
1017
|
-
let breakPage = false;
|
|
1018
|
-
if (currentHeight + height > A4_HEIGHT) {
|
|
1019
|
-
breakPage = true;
|
|
1020
|
-
} else if (block.type === "SCENE_HEADING") {
|
|
1021
|
-
if (hasSceneOnCurrentPage) {
|
|
1022
|
-
breakPage = true;
|
|
1023
|
-
} else if (currentHeight > A4_HEIGHT - 120) {
|
|
1024
|
-
breakPage = true;
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
if (breakPage && currentHeight > 0) {
|
|
1028
|
-
newPageBreaks.push(block.id);
|
|
1029
|
-
currentHeight = height;
|
|
1030
|
-
hasSceneOnCurrentPage = block.type === "SCENE_HEADING";
|
|
1031
|
-
} else {
|
|
1032
|
-
currentHeight += height;
|
|
1033
|
-
if (block.type === "SCENE_HEADING") {
|
|
1034
|
-
hasSceneOnCurrentPage = true;
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
});
|
|
1039
|
-
setPageBreaks((prev) => {
|
|
1040
|
-
if (prev.length !== newPageBreaks.length) {
|
|
1041
|
-
saveFocus();
|
|
1042
|
-
return newPageBreaks;
|
|
1043
|
-
}
|
|
1044
|
-
for (let i = 0; i < prev.length; i++) {
|
|
1045
|
-
if (prev[i] !== newPageBreaks[i]) {
|
|
1046
|
-
saveFocus();
|
|
1047
|
-
return newPageBreaks;
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
return prev;
|
|
1051
|
-
});
|
|
1052
|
-
}, 300);
|
|
1053
|
-
return () => clearTimeout(timeoutId);
|
|
1054
|
-
}, [blocks, isPageSplitEnabled, focusedBlockId]);
|
|
1055
|
-
const pages = useMemo(() => {
|
|
1056
|
-
if (!isPageSplitEnabled || pageBreaks.length === 0) return [blocks];
|
|
1057
|
-
const result = [];
|
|
1058
|
-
let currentPage = [];
|
|
1059
|
-
for (const block of blocks) {
|
|
1060
|
-
if (pageBreaks.includes(block.id) && currentPage.length > 0) {
|
|
1061
|
-
result.push(currentPage);
|
|
1062
|
-
currentPage = [];
|
|
1063
|
-
}
|
|
1064
|
-
currentPage.push(block);
|
|
1065
|
-
}
|
|
1066
|
-
if (currentPage.length > 0) result.push(currentPage);
|
|
1067
|
-
return result;
|
|
1068
|
-
}, [blocks, isPageSplitEnabled, pageBreaks]);
|
|
1069
|
-
useEffect(() => {
|
|
1070
|
-
if (focusStateRef.current) {
|
|
1071
|
-
const { id, offset } = focusStateRef.current;
|
|
1072
|
-
const el = refs.current[id];
|
|
1073
|
-
if (el && document.activeElement !== el) {
|
|
1074
|
-
el.focus();
|
|
1075
|
-
setCaretPosition(el, offset);
|
|
1076
|
-
}
|
|
1077
|
-
focusStateRef.current = null;
|
|
1078
|
-
}
|
|
1079
|
-
}, [pages]);
|
|
1080
943
|
const handleBlockTextChange = useCallback(
|
|
1081
944
|
(id, text) => {
|
|
1082
945
|
const block = blocks.find((b) => b.id === id);
|
|
@@ -1406,9 +1269,6 @@ function useScreenplayEditor() {
|
|
|
1406
1269
|
}, []);
|
|
1407
1270
|
return {
|
|
1408
1271
|
blocks,
|
|
1409
|
-
pages,
|
|
1410
|
-
isPageSplitEnabled,
|
|
1411
|
-
togglePageSplit,
|
|
1412
1272
|
refs,
|
|
1413
1273
|
focusedBlockId,
|
|
1414
1274
|
showSuggestions,
|