@vishu1301/script-writing 0.4.4 → 0.4.6
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 +208 -360
- 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 -361
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -242,9 +242,6 @@ function PdfImporter({ onScriptImported, children }) {
|
|
|
242
242
|
}
|
|
243
243
|
function ScreenplayEditorView({
|
|
244
244
|
blocks,
|
|
245
|
-
pages,
|
|
246
|
-
isPageSplitEnabled,
|
|
247
|
-
togglePageSplit,
|
|
248
245
|
refs,
|
|
249
246
|
focusedBlockId,
|
|
250
247
|
showSuggestions,
|
|
@@ -252,7 +249,6 @@ function ScreenplayEditorView({
|
|
|
252
249
|
characterExtensions,
|
|
253
250
|
locations,
|
|
254
251
|
characters,
|
|
255
|
-
sceneNumbers,
|
|
256
252
|
handleBlockTextChange,
|
|
257
253
|
handleSceneTypeChange,
|
|
258
254
|
handleTimeOfDayChange,
|
|
@@ -307,131 +303,56 @@ function ScreenplayEditorView({
|
|
|
307
303
|
type
|
|
308
304
|
);
|
|
309
305
|
}) }),
|
|
310
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-12 w-full items-center pb-24", children:
|
|
306
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-12 w-full items-center pb-24", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
311
307
|
"div",
|
|
312
308
|
{
|
|
313
309
|
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",
|
|
314
310
|
style: {
|
|
315
311
|
fontFamily: "var(--font-courier-prime, 'Courier New', Courier, monospace)"
|
|
316
312
|
},
|
|
317
|
-
children:
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
/* @__PURE__ */ jsxRuntime.
|
|
327
|
-
|
|
328
|
-
"input",
|
|
329
|
-
{
|
|
330
|
-
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",
|
|
331
|
-
spellCheck: false,
|
|
332
|
-
value: block.sceneNumber || "",
|
|
333
|
-
onChange: (e) => handleSceneNumberChange(
|
|
334
|
-
block.id,
|
|
335
|
-
e.target.value.toUpperCase()
|
|
336
|
-
),
|
|
337
|
-
onFocus: () => handleFocus(block.id),
|
|
338
|
-
onBlur: () => handleBlur(block.id),
|
|
339
|
-
onKeyDown: (e) => {
|
|
340
|
-
if (e.key === "Enter" || e.key === "Backspace") {
|
|
341
|
-
e.stopPropagation();
|
|
342
|
-
}
|
|
343
|
-
},
|
|
344
|
-
"aria-label": "Scene Number"
|
|
345
|
-
}
|
|
346
|
-
),
|
|
347
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
348
|
-
"select",
|
|
349
|
-
{
|
|
350
|
-
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",
|
|
351
|
-
"aria-label": "Scene Type",
|
|
352
|
-
value: (_a = block.sceneType) != null ? _a : "INT.",
|
|
353
|
-
onChange: (e) => handleSceneTypeChange(block.id, e.target.value),
|
|
354
|
-
children: [
|
|
355
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "INT." }),
|
|
356
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "EXT." }),
|
|
357
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "INT/EXT." })
|
|
358
|
-
]
|
|
359
|
-
}
|
|
360
|
-
),
|
|
361
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
362
|
-
"div",
|
|
363
|
-
{
|
|
364
|
-
ref: (el) => {
|
|
365
|
-
if (!el) return;
|
|
366
|
-
refs.current[block.id] = el;
|
|
367
|
-
},
|
|
368
|
-
contentEditable: true,
|
|
369
|
-
suppressContentEditableWarning: true,
|
|
370
|
-
"aria-label": `Scene Heading: ${block.text}`,
|
|
371
|
-
"aria-haspopup": "listbox",
|
|
372
|
-
"aria-expanded": focusedBlockId === block.id && showSuggestions && locations.length > 0,
|
|
373
|
-
spellCheck: false,
|
|
374
|
-
className: "min-w-[3rem] py-1 outline-none text-base font-bold uppercase tracking-widest break-all bg-transparent",
|
|
375
|
-
onInput: (e) => handleBlockTextChange(
|
|
376
|
-
block.id,
|
|
377
|
-
e.target.innerText
|
|
378
|
-
),
|
|
379
|
-
onKeyDown: (e) => handleKeyDown(e, block.id, block.text),
|
|
380
|
-
onFocus: () => handleFocus(block.id),
|
|
381
|
-
onBlur: () => handleBlur(block.id)
|
|
382
|
-
}
|
|
383
|
-
),
|
|
384
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-zinc-400/80 font-bold", children: "-" }),
|
|
385
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
386
|
-
"select",
|
|
387
|
-
{
|
|
388
|
-
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",
|
|
389
|
-
"aria-label": "Time of Day",
|
|
390
|
-
value: (_b = block.timeOfDay) != null ? _b : "DAY",
|
|
391
|
-
onChange: (e) => handleTimeOfDayChange(block.id, e.target.value),
|
|
392
|
-
children: timeOfDayOptions.map((t) => /* @__PURE__ */ jsxRuntime.jsx("option", { children: t }, t))
|
|
393
|
-
}
|
|
394
|
-
)
|
|
395
|
-
] }),
|
|
396
|
-
focusedBlockId === block.id && showSuggestions && locations.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
397
|
-
"div",
|
|
313
|
+
children: blocks.map((block) => {
|
|
314
|
+
var _a, _b;
|
|
315
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
316
|
+
"div",
|
|
317
|
+
{
|
|
318
|
+
"data-block-id": block.id,
|
|
319
|
+
className: `relative rounded-sm transition-all duration-200 outline-none ${focusedBlockId === block.id ? "bg-zinc-100/50" : "bg-transparent"}`,
|
|
320
|
+
children: block.type === "SCENE_HEADING" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
321
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 px-4 py-1 bg-transparent", children: [
|
|
322
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
323
|
+
"input",
|
|
398
324
|
{
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
handleBlockTextChange(block.id, loc);
|
|
415
|
-
element.focus();
|
|
416
|
-
const range = document.createRange();
|
|
417
|
-
const sel = window.getSelection();
|
|
418
|
-
range.selectNodeContents(element);
|
|
419
|
-
range.collapse(false);
|
|
420
|
-
sel == null ? void 0 : sel.removeAllRanges();
|
|
421
|
-
sel == null ? void 0 : sel.addRange(range);
|
|
422
|
-
}
|
|
423
|
-
handleBlur(block.id);
|
|
424
|
-
},
|
|
425
|
-
children: [
|
|
426
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[12px] font-semibold tracking-wide text-slate-600 uppercase line-clamp-1", children: loc }),
|
|
427
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.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" })
|
|
428
|
-
]
|
|
429
|
-
},
|
|
430
|
-
loc
|
|
431
|
-
)) })
|
|
325
|
+
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",
|
|
326
|
+
spellCheck: false,
|
|
327
|
+
value: block.sceneNumber || "",
|
|
328
|
+
onChange: (e) => handleSceneNumberChange(
|
|
329
|
+
block.id,
|
|
330
|
+
e.target.value.toUpperCase()
|
|
331
|
+
),
|
|
332
|
+
onFocus: () => handleFocus(block.id),
|
|
333
|
+
onBlur: () => handleBlur(block.id),
|
|
334
|
+
onKeyDown: (e) => {
|
|
335
|
+
if (e.key === "Enter" || e.key === "Backspace") {
|
|
336
|
+
e.stopPropagation();
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
"aria-label": "Scene Number"
|
|
432
340
|
}
|
|
433
|
-
)
|
|
434
|
-
|
|
341
|
+
),
|
|
342
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
343
|
+
"select",
|
|
344
|
+
{
|
|
345
|
+
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",
|
|
346
|
+
"aria-label": "Scene Type",
|
|
347
|
+
value: (_a = block.sceneType) != null ? _a : "INT.",
|
|
348
|
+
onChange: (e) => handleSceneTypeChange(block.id, e.target.value),
|
|
349
|
+
children: [
|
|
350
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "INT." }),
|
|
351
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "EXT." }),
|
|
352
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "INT/EXT." })
|
|
353
|
+
]
|
|
354
|
+
}
|
|
355
|
+
),
|
|
435
356
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
436
357
|
"div",
|
|
437
358
|
{
|
|
@@ -441,100 +362,168 @@ function ScreenplayEditorView({
|
|
|
441
362
|
},
|
|
442
363
|
contentEditable: true,
|
|
443
364
|
suppressContentEditableWarning: true,
|
|
444
|
-
"aria-label":
|
|
445
|
-
"aria-
|
|
365
|
+
"aria-label": `Scene Heading: ${block.text}`,
|
|
366
|
+
"aria-haspopup": "listbox",
|
|
367
|
+
"aria-expanded": focusedBlockId === block.id && showSuggestions && locations.length > 0,
|
|
446
368
|
spellCheck: false,
|
|
447
|
-
className:
|
|
369
|
+
className: "min-w-[3rem] py-1 outline-none text-base font-bold uppercase tracking-widest break-all bg-transparent",
|
|
448
370
|
onInput: (e) => handleBlockTextChange(
|
|
449
371
|
block.id,
|
|
450
372
|
e.target.innerText
|
|
451
373
|
),
|
|
452
374
|
onKeyDown: (e) => handleKeyDown(e, block.id, block.text),
|
|
453
375
|
onFocus: () => handleFocus(block.id),
|
|
454
|
-
onBlur: () => handleBlur(block.id)
|
|
455
|
-
style: blockStyles[block.type].inputStyle
|
|
376
|
+
onBlur: () => handleBlur(block.id)
|
|
456
377
|
}
|
|
457
378
|
),
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
role: "listbox",
|
|
462
|
-
id: `suggestions-${block.id}`,
|
|
463
|
-
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",
|
|
464
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-56 overflow-y-auto custom-scrollbar", children: characters.filter(
|
|
465
|
-
(char) => char.startsWith(block.text.toUpperCase()) && char !== block.text.toUpperCase()
|
|
466
|
-
).map((char) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
467
|
-
"div",
|
|
468
|
-
{
|
|
469
|
-
role: "option",
|
|
470
|
-
className: "group flex items-center px-4 py-2.5 cursor-pointer transition-colors duration-150 hover:bg-slate-50 active:bg-slate-100",
|
|
471
|
-
onMouseDown: (e) => {
|
|
472
|
-
e.preventDefault();
|
|
473
|
-
const element = refs.current[block.id];
|
|
474
|
-
if (element) {
|
|
475
|
-
element.innerText = char;
|
|
476
|
-
handleBlockTextChange(block.id, char);
|
|
477
|
-
element.focus();
|
|
478
|
-
const range = document.createRange();
|
|
479
|
-
const sel = window.getSelection();
|
|
480
|
-
range.selectNodeContents(element);
|
|
481
|
-
range.collapse(false);
|
|
482
|
-
sel == null ? void 0 : sel.removeAllRanges();
|
|
483
|
-
sel == null ? void 0 : sel.addRange(range);
|
|
484
|
-
}
|
|
485
|
-
handleBlur(block.id);
|
|
486
|
-
},
|
|
487
|
-
children: [
|
|
488
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, { className: "w-3.5 h-3.5 text-slate-300 group-hover:text-sky-500 transition-colors mr-3" }),
|
|
489
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 text-[11px] font-bold tracking-[0.1em] text-slate-600 uppercase text-left", children: char }),
|
|
490
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.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" })
|
|
491
|
-
]
|
|
492
|
-
},
|
|
493
|
-
char
|
|
494
|
-
)) })
|
|
495
|
-
}
|
|
496
|
-
),
|
|
497
|
-
focusedBlockId === block.id && block.type === "CHARACTER" && showExtensionSuggestions && characterExtensions && /* @__PURE__ */ jsxRuntime.jsx(
|
|
498
|
-
"div",
|
|
379
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-zinc-400/80 font-bold", children: "-" }),
|
|
380
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
381
|
+
"select",
|
|
499
382
|
{
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
const query = openParenIndex > -1 ? block.text.substring(openParenIndex + 1).toUpperCase() : "";
|
|
506
|
-
return ext.toUpperCase().includes(query);
|
|
507
|
-
}).map((ext) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
508
|
-
"div",
|
|
509
|
-
{
|
|
510
|
-
role: "option",
|
|
511
|
-
className: "group flex items-center px-4 py-2.5 cursor-pointer transition-colors duration-150 hover:bg-slate-50 active:bg-slate-100",
|
|
512
|
-
onMouseDown: (e) => {
|
|
513
|
-
e.preventDefault();
|
|
514
|
-
handleSelectCharacterExtension(ext);
|
|
515
|
-
},
|
|
516
|
-
children: [
|
|
517
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 text-[11px] font-bold tracking-[0.1em] text-slate-600 uppercase text-left", children: ext }),
|
|
518
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.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" })
|
|
519
|
-
]
|
|
520
|
-
},
|
|
521
|
-
ext
|
|
522
|
-
)) })
|
|
383
|
+
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",
|
|
384
|
+
"aria-label": "Time of Day",
|
|
385
|
+
value: (_b = block.timeOfDay) != null ? _b : "DAY",
|
|
386
|
+
onChange: (e) => handleTimeOfDayChange(block.id, e.target.value),
|
|
387
|
+
children: timeOfDayOptions.map((t) => /* @__PURE__ */ jsxRuntime.jsx("option", { children: t }, t))
|
|
523
388
|
}
|
|
524
389
|
)
|
|
525
|
-
] })
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
390
|
+
] }),
|
|
391
|
+
focusedBlockId === block.id && showSuggestions && locations.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
392
|
+
"div",
|
|
393
|
+
{
|
|
394
|
+
role: "listbox",
|
|
395
|
+
id: `suggestions-${block.id}`,
|
|
396
|
+
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",
|
|
397
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-60 overflow-y-auto custom-scrollbar", children: locations.filter(
|
|
398
|
+
(loc) => loc.startsWith(block.text.toUpperCase()) && loc !== block.text.toUpperCase()
|
|
399
|
+
).map((loc) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
400
|
+
"div",
|
|
401
|
+
{
|
|
402
|
+
role: "option",
|
|
403
|
+
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",
|
|
404
|
+
onMouseDown: (e) => {
|
|
405
|
+
e.preventDefault();
|
|
406
|
+
const element = refs.current[block.id];
|
|
407
|
+
if (element) {
|
|
408
|
+
element.innerText = loc;
|
|
409
|
+
handleBlockTextChange(block.id, loc);
|
|
410
|
+
element.focus();
|
|
411
|
+
const range = document.createRange();
|
|
412
|
+
const sel = window.getSelection();
|
|
413
|
+
range.selectNodeContents(element);
|
|
414
|
+
range.collapse(false);
|
|
415
|
+
sel == null ? void 0 : sel.removeAllRanges();
|
|
416
|
+
sel == null ? void 0 : sel.addRange(range);
|
|
417
|
+
}
|
|
418
|
+
handleBlur(block.id);
|
|
419
|
+
},
|
|
420
|
+
children: [
|
|
421
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[12px] font-semibold tracking-wide text-slate-600 uppercase line-clamp-1", children: loc }),
|
|
422
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.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" })
|
|
423
|
+
]
|
|
424
|
+
},
|
|
425
|
+
loc
|
|
426
|
+
)) })
|
|
427
|
+
}
|
|
428
|
+
)
|
|
429
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
430
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
431
|
+
"div",
|
|
432
|
+
{
|
|
433
|
+
ref: (el) => {
|
|
434
|
+
if (!el) return;
|
|
435
|
+
refs.current[block.id] = el;
|
|
436
|
+
},
|
|
437
|
+
contentEditable: true,
|
|
438
|
+
suppressContentEditableWarning: true,
|
|
439
|
+
"aria-label": `${blockStyles[block.type].label} text`,
|
|
440
|
+
"aria-multiline": block.type === "ACTION" || block.type === "DIALOGUE",
|
|
441
|
+
spellCheck: false,
|
|
442
|
+
className: `block outline-none w-full min-h-[2.5rem] px-4 py-2 break-words ${blockStyles[block.type].className}`,
|
|
443
|
+
onInput: (e) => handleBlockTextChange(
|
|
444
|
+
block.id,
|
|
445
|
+
e.target.innerText
|
|
446
|
+
),
|
|
447
|
+
onKeyDown: (e) => handleKeyDown(e, block.id, block.text),
|
|
448
|
+
onFocus: () => handleFocus(block.id),
|
|
449
|
+
onBlur: () => handleBlur(block.id),
|
|
450
|
+
style: blockStyles[block.type].inputStyle
|
|
451
|
+
}
|
|
452
|
+
),
|
|
453
|
+
focusedBlockId === block.id && block.type === "CHARACTER" && showSuggestions && characters.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
454
|
+
"div",
|
|
455
|
+
{
|
|
456
|
+
role: "listbox",
|
|
457
|
+
id: `suggestions-${block.id}`,
|
|
458
|
+
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",
|
|
459
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-56 overflow-y-auto custom-scrollbar", children: characters.filter(
|
|
460
|
+
(char) => char.startsWith(block.text.toUpperCase()) && char !== block.text.toUpperCase()
|
|
461
|
+
).map((char) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
462
|
+
"div",
|
|
463
|
+
{
|
|
464
|
+
role: "option",
|
|
465
|
+
className: "group flex items-center px-4 py-2.5 cursor-pointer transition-colors duration-150 hover:bg-slate-50 active:bg-slate-100",
|
|
466
|
+
onMouseDown: (e) => {
|
|
467
|
+
e.preventDefault();
|
|
468
|
+
const element = refs.current[block.id];
|
|
469
|
+
if (element) {
|
|
470
|
+
element.innerText = char;
|
|
471
|
+
handleBlockTextChange(block.id, char);
|
|
472
|
+
element.focus();
|
|
473
|
+
const range = document.createRange();
|
|
474
|
+
const sel = window.getSelection();
|
|
475
|
+
range.selectNodeContents(element);
|
|
476
|
+
range.collapse(false);
|
|
477
|
+
sel == null ? void 0 : sel.removeAllRanges();
|
|
478
|
+
sel == null ? void 0 : sel.addRange(range);
|
|
479
|
+
}
|
|
480
|
+
handleBlur(block.id);
|
|
481
|
+
},
|
|
482
|
+
children: [
|
|
483
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, { className: "w-3.5 h-3.5 text-slate-300 group-hover:text-sky-500 transition-colors mr-3" }),
|
|
484
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 text-[11px] font-bold tracking-[0.1em] text-slate-600 uppercase text-left", children: char }),
|
|
485
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.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" })
|
|
486
|
+
]
|
|
487
|
+
},
|
|
488
|
+
char
|
|
489
|
+
)) })
|
|
490
|
+
}
|
|
491
|
+
),
|
|
492
|
+
focusedBlockId === block.id && block.type === "CHARACTER" && showExtensionSuggestions && characterExtensions && /* @__PURE__ */ jsxRuntime.jsx(
|
|
493
|
+
"div",
|
|
494
|
+
{
|
|
495
|
+
role: "listbox",
|
|
496
|
+
id: `extension-suggestions-${block.id}`,
|
|
497
|
+
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",
|
|
498
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-56 overflow-y-auto custom-scrollbar", children: characterExtensions.filter((ext) => {
|
|
499
|
+
const openParenIndex = block.text.lastIndexOf("(");
|
|
500
|
+
const query = openParenIndex > -1 ? block.text.substring(openParenIndex + 1).toUpperCase() : "";
|
|
501
|
+
return ext.toUpperCase().includes(query);
|
|
502
|
+
}).map((ext) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
503
|
+
"div",
|
|
504
|
+
{
|
|
505
|
+
role: "option",
|
|
506
|
+
className: "group flex items-center px-4 py-2.5 cursor-pointer transition-colors duration-150 hover:bg-slate-50 active:bg-slate-100",
|
|
507
|
+
onMouseDown: (e) => {
|
|
508
|
+
e.preventDefault();
|
|
509
|
+
handleSelectCharacterExtension(ext);
|
|
510
|
+
},
|
|
511
|
+
children: [
|
|
512
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 text-[11px] font-bold tracking-[0.1em] text-slate-600 uppercase text-left", children: ext }),
|
|
513
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.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" })
|
|
514
|
+
]
|
|
515
|
+
},
|
|
516
|
+
ext
|
|
517
|
+
)) })
|
|
518
|
+
}
|
|
519
|
+
)
|
|
520
|
+
] })
|
|
521
|
+
},
|
|
522
|
+
block.id + "-" + block.type
|
|
523
|
+
);
|
|
524
|
+
})
|
|
525
|
+
}
|
|
526
|
+
) }),
|
|
538
527
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed bottom-6 right-6 flex flex-col items-end gap-4 z-50", children: [
|
|
539
528
|
/* @__PURE__ */ jsxRuntime.jsx(PdfImporter, { onScriptImported: handleScriptImport, children: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
540
529
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { className: "w-5 h-5" }),
|
|
@@ -566,46 +555,24 @@ function ScreenplayEditorView({
|
|
|
566
555
|
),
|
|
567
556
|
isRulesOpen && /* @__PURE__ */ jsxRuntime.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: [
|
|
568
557
|
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-bold text-zinc-800 mb-3 text-sm", children: "Settings & Shortcuts" }),
|
|
569
|
-
/* @__PURE__ */ jsxRuntime.
|
|
570
|
-
/* @__PURE__ */ jsxRuntime.jsxs("
|
|
571
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", {
|
|
572
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
573
|
-
"button",
|
|
574
|
-
{
|
|
575
|
-
type: "button",
|
|
576
|
-
role: "switch",
|
|
577
|
-
"aria-checked": isPageSplitEnabled,
|
|
578
|
-
onClick: togglePageSplit,
|
|
579
|
-
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`,
|
|
580
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
581
|
-
"span",
|
|
582
|
-
{
|
|
583
|
-
"aria-hidden": "true",
|
|
584
|
-
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`
|
|
585
|
-
}
|
|
586
|
-
)
|
|
587
|
-
}
|
|
588
|
-
)
|
|
558
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1.5", children: /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "space-y-1.5", children: [
|
|
559
|
+
/* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-center justify-between gap-6", children: [
|
|
560
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "New Block" }),
|
|
561
|
+
/* @__PURE__ */ jsxRuntime.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" })
|
|
589
562
|
] }),
|
|
590
|
-
/* @__PURE__ */ jsxRuntime.
|
|
591
|
-
/* @__PURE__ */ jsxRuntime.
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
/* @__PURE__ */ jsxRuntime.
|
|
596
|
-
|
|
597
|
-
/* @__PURE__ */ jsxRuntime.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: "
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Change Type" }),
|
|
601
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
602
|
-
/* @__PURE__ */ jsxRuntime.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" }),
|
|
603
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "+" }),
|
|
604
|
-
/* @__PURE__ */ jsxRuntime.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" })
|
|
605
|
-
] })
|
|
563
|
+
/* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-center justify-between gap-6", children: [
|
|
564
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Delete Block" }),
|
|
565
|
+
/* @__PURE__ */ jsxRuntime.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" })
|
|
566
|
+
] }),
|
|
567
|
+
/* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-center justify-between gap-6", children: [
|
|
568
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Change Type" }),
|
|
569
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
570
|
+
/* @__PURE__ */ jsxRuntime.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" }),
|
|
571
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "+" }),
|
|
572
|
+
/* @__PURE__ */ jsxRuntime.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" })
|
|
606
573
|
] })
|
|
607
|
-
] })
|
|
608
|
-
] })
|
|
574
|
+
] })
|
|
575
|
+
] }) }) })
|
|
609
576
|
] }),
|
|
610
577
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
611
578
|
"button",
|
|
@@ -893,19 +860,6 @@ function useScreenplayEditor() {
|
|
|
893
860
|
const [showSuggestions, setShowSuggestions] = react.useState(false);
|
|
894
861
|
const [showExtensionSuggestions, setShowExtensionSuggestions] = react.useState(false);
|
|
895
862
|
const blurTimeout = react.useRef(null);
|
|
896
|
-
const [isPageSplitEnabled, setIsPageSplitEnabled] = react.useState(false);
|
|
897
|
-
const [pageBreaks, setPageBreaks] = react.useState([]);
|
|
898
|
-
const focusStateRef = react.useRef(null);
|
|
899
|
-
const togglePageSplit = react.useCallback(() => {
|
|
900
|
-
if (focusedBlockId && document.activeElement && document.activeElement.hasAttribute("contenteditable")) {
|
|
901
|
-
const el = refs.current[focusedBlockId];
|
|
902
|
-
if (el) {
|
|
903
|
-
const offset = getCaretCharacterOffsetWithin(el);
|
|
904
|
-
focusStateRef.current = { id: focusedBlockId, offset };
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
setIsPageSplitEnabled((prev) => !prev);
|
|
908
|
-
}, [focusedBlockId]);
|
|
909
863
|
const characterExtensions = react.useMemo(
|
|
910
864
|
() => ["(V.O.)", "(O.S.)", "(O.C.)", "(SUBTITLE)", "(CONT'D)"],
|
|
911
865
|
[]
|
|
@@ -952,17 +906,6 @@ function useScreenplayEditor() {
|
|
|
952
906
|
});
|
|
953
907
|
return map;
|
|
954
908
|
}, [blocks]);
|
|
955
|
-
react.useCallback(() => {
|
|
956
|
-
let count = 1;
|
|
957
|
-
setBlocks(
|
|
958
|
-
(prev) => prev.map((b) => {
|
|
959
|
-
if (b.type === "SCENE_HEADING") {
|
|
960
|
-
return __spreadProps(__spreadValues({}, b), { sceneNumber: String(count++) });
|
|
961
|
-
}
|
|
962
|
-
return b;
|
|
963
|
-
})
|
|
964
|
-
);
|
|
965
|
-
}, []);
|
|
966
909
|
react.useEffect(() => {
|
|
967
910
|
if (newBlockId && refs.current[newBlockId]) {
|
|
968
911
|
const block = blocks.find((b) => b.id === newBlockId);
|
|
@@ -1007,98 +950,6 @@ function useScreenplayEditor() {
|
|
|
1007
950
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
1008
951
|
};
|
|
1009
952
|
}, []);
|
|
1010
|
-
react.useEffect(() => {
|
|
1011
|
-
if (!isPageSplitEnabled) {
|
|
1012
|
-
setPageBreaks([]);
|
|
1013
|
-
return;
|
|
1014
|
-
}
|
|
1015
|
-
const saveFocus = () => {
|
|
1016
|
-
if (focusedBlockId && document.activeElement && document.activeElement.hasAttribute("contenteditable")) {
|
|
1017
|
-
const el = refs.current[focusedBlockId];
|
|
1018
|
-
if (el) {
|
|
1019
|
-
const offset = getCaretCharacterOffsetWithin(el);
|
|
1020
|
-
focusStateRef.current = { id: focusedBlockId, offset };
|
|
1021
|
-
}
|
|
1022
|
-
} else {
|
|
1023
|
-
focusStateRef.current = null;
|
|
1024
|
-
}
|
|
1025
|
-
};
|
|
1026
|
-
const timeoutId = setTimeout(() => {
|
|
1027
|
-
let currentHeight = 0;
|
|
1028
|
-
let hasSceneOnCurrentPage = false;
|
|
1029
|
-
const A4_HEIGHT = 960;
|
|
1030
|
-
const newPageBreaks = [];
|
|
1031
|
-
blocks.forEach((block) => {
|
|
1032
|
-
const el = refs.current[block.id];
|
|
1033
|
-
if (el) {
|
|
1034
|
-
const measureEl = document.querySelector(`[data-block-id="${block.id}"]`) || el;
|
|
1035
|
-
const style = window.getComputedStyle(measureEl);
|
|
1036
|
-
const marginTop = parseFloat(style.marginTop) || 0;
|
|
1037
|
-
const marginBottom = parseFloat(style.marginBottom) || 0;
|
|
1038
|
-
const height = measureEl.getBoundingClientRect().height + marginTop + marginBottom;
|
|
1039
|
-
let breakPage = false;
|
|
1040
|
-
if (currentHeight + height > A4_HEIGHT) {
|
|
1041
|
-
breakPage = true;
|
|
1042
|
-
} else if (block.type === "SCENE_HEADING") {
|
|
1043
|
-
if (hasSceneOnCurrentPage) {
|
|
1044
|
-
breakPage = true;
|
|
1045
|
-
} else if (currentHeight > A4_HEIGHT - 120) {
|
|
1046
|
-
breakPage = true;
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
if (breakPage && currentHeight > 0) {
|
|
1050
|
-
newPageBreaks.push(block.id);
|
|
1051
|
-
currentHeight = height;
|
|
1052
|
-
hasSceneOnCurrentPage = block.type === "SCENE_HEADING";
|
|
1053
|
-
} else {
|
|
1054
|
-
currentHeight += height;
|
|
1055
|
-
if (block.type === "SCENE_HEADING") {
|
|
1056
|
-
hasSceneOnCurrentPage = true;
|
|
1057
|
-
}
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
});
|
|
1061
|
-
setPageBreaks((prev) => {
|
|
1062
|
-
if (prev.length !== newPageBreaks.length) {
|
|
1063
|
-
saveFocus();
|
|
1064
|
-
return newPageBreaks;
|
|
1065
|
-
}
|
|
1066
|
-
for (let i = 0; i < prev.length; i++) {
|
|
1067
|
-
if (prev[i] !== newPageBreaks[i]) {
|
|
1068
|
-
saveFocus();
|
|
1069
|
-
return newPageBreaks;
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
return prev;
|
|
1073
|
-
});
|
|
1074
|
-
}, 300);
|
|
1075
|
-
return () => clearTimeout(timeoutId);
|
|
1076
|
-
}, [blocks, isPageSplitEnabled, focusedBlockId]);
|
|
1077
|
-
const pages = react.useMemo(() => {
|
|
1078
|
-
if (!isPageSplitEnabled || pageBreaks.length === 0) return [blocks];
|
|
1079
|
-
const result = [];
|
|
1080
|
-
let currentPage = [];
|
|
1081
|
-
for (const block of blocks) {
|
|
1082
|
-
if (pageBreaks.includes(block.id) && currentPage.length > 0) {
|
|
1083
|
-
result.push(currentPage);
|
|
1084
|
-
currentPage = [];
|
|
1085
|
-
}
|
|
1086
|
-
currentPage.push(block);
|
|
1087
|
-
}
|
|
1088
|
-
if (currentPage.length > 0) result.push(currentPage);
|
|
1089
|
-
return result;
|
|
1090
|
-
}, [blocks, isPageSplitEnabled, pageBreaks]);
|
|
1091
|
-
react.useEffect(() => {
|
|
1092
|
-
if (focusStateRef.current) {
|
|
1093
|
-
const { id, offset } = focusStateRef.current;
|
|
1094
|
-
const el = refs.current[id];
|
|
1095
|
-
if (el && document.activeElement !== el) {
|
|
1096
|
-
el.focus();
|
|
1097
|
-
setCaretPosition(el, offset);
|
|
1098
|
-
}
|
|
1099
|
-
focusStateRef.current = null;
|
|
1100
|
-
}
|
|
1101
|
-
}, [pages]);
|
|
1102
953
|
const handleBlockTextChange = react.useCallback(
|
|
1103
954
|
(id, text) => {
|
|
1104
955
|
const block = blocks.find((b) => b.id === id);
|
|
@@ -1428,9 +1279,6 @@ function useScreenplayEditor() {
|
|
|
1428
1279
|
}, []);
|
|
1429
1280
|
return {
|
|
1430
1281
|
blocks,
|
|
1431
|
-
pages,
|
|
1432
|
-
isPageSplitEnabled,
|
|
1433
|
-
togglePageSplit,
|
|
1434
1282
|
refs,
|
|
1435
1283
|
focusedBlockId,
|
|
1436
1284
|
showSuggestions,
|