@vishu1301/script-writing 1.4.2 → 1.4.4

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.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { useState, useRef, useEffect, useMemo, useCallback } from 'react';
2
- import { Users, Box, Shirt, Car, Armchair, UserPlus, MapPin, Map as Map$1, ArrowRightLeft, MessageCircle, Brackets, UserRound, Sparkles, Clapperboard, Upload, Lock, Unlock, Save, FileDown, RefreshCcw, Cog, ArrowRight, User, ChevronRight, Loader2, AlignLeft, Tags, ChevronDown, X, Video, Settings2, Eye, Pencil, Info, Frame, BookText, Check, AsteriskIcon, Target, Activity, MonitorPlay, Crosshair, Clock, Camera, Aperture, SlidersHorizontal, Sun, Wand2, Volume2 } from 'lucide-react';
2
+ import { Users, Box, Shirt, Car, Armchair, UserPlus, MapPin, Map as Map$1, CornerDownRight, MessageSquareText, Parentheses, User, AlignLeft, Type, Loader2, Upload, Lock, Unlock, Save, FileDown, RefreshCcw, Cog, ArrowRight, ChevronRight, Sparkles, Check, Tags, ChevronDown, X, Video, Settings2, Eye, Pencil, Info, Frame, BookText, AsteriskIcon, Target, Activity, MonitorPlay, Crosshair, Clock, Camera, Aperture, SlidersHorizontal, Sun, Wand2, Volume2 } from 'lucide-react';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import * as pdfjs from 'pdfjs-dist';
5
5
  import jsPDF from 'jspdf';
@@ -48,12 +48,12 @@ var blockTypes = [
48
48
  ];
49
49
  var uuid = () => Math.random().toString(36).slice(2, 9);
50
50
  var icons = {
51
- SCENE_HEADING: /* @__PURE__ */ jsx(Clapperboard, { className: "w-5 h-5" }),
52
- ACTION: /* @__PURE__ */ jsx(Sparkles, { className: "w-5 h-5" }),
53
- CHARACTER: /* @__PURE__ */ jsx(UserRound, { className: "w-5 h-5" }),
54
- PARENTHETICAL: /* @__PURE__ */ jsx(Brackets, { className: "w-5 h-5" }),
55
- DIALOGUE: /* @__PURE__ */ jsx(MessageCircle, { className: "w-5 h-5" }),
56
- TRANSITION: /* @__PURE__ */ jsx(ArrowRightLeft, { className: "w-5 h-5" })
51
+ SCENE_HEADING: /* @__PURE__ */ jsx(Type, { className: "w-5 h-5" }),
52
+ ACTION: /* @__PURE__ */ jsx(AlignLeft, { className: "w-5 h-5" }),
53
+ CHARACTER: /* @__PURE__ */ jsx(User, { className: "w-5 h-5" }),
54
+ PARENTHETICAL: /* @__PURE__ */ jsx(Parentheses, { className: "w-5 h-5" }),
55
+ DIALOGUE: /* @__PURE__ */ jsx(MessageSquareText, { className: "w-5 h-5" }),
56
+ TRANSITION: /* @__PURE__ */ jsx(CornerDownRight, { className: "w-5 h-5" })
57
57
  };
58
58
  var blockStyles = {
59
59
  SCENE_HEADING: {
@@ -320,9 +320,18 @@ function ScreenplayEditorView({
320
320
  handleScriptImport,
321
321
  onSave,
322
322
  onSaveAsPdf,
323
- onSaveAsSbx,
324
323
  onSyncWithCloud,
325
- handleSceneNumberChange
324
+ handleSceneNumberChange,
325
+ handleEnhance,
326
+ handleApproveEnhance,
327
+ handleRejectEnhance,
328
+ enhancingBlockId,
329
+ enhancementSuggestion,
330
+ isEnhancing,
331
+ showUnsavedPopover,
332
+ syncScreenplay,
333
+ ignoreChanges,
334
+ isLoading = false
326
335
  }) {
327
336
  const [isRulesOpen, setIsRulesOpen] = useState(false);
328
337
  const rulesRef = useRef(null);
@@ -366,9 +375,15 @@ function ScreenplayEditorView({
366
375
  document.head.appendChild(style);
367
376
  }
368
377
  }, [COURIER_STACK]);
378
+ if (isLoading) {
379
+ return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-[100] flex items-center justify-center bg-white/80 transition-opacity", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3", children: [
380
+ /* @__PURE__ */ jsx(Loader2, { className: "w-8 h-8 text-zinc-400 animate-spin" }),
381
+ /* @__PURE__ */ jsx("p", { className: "text-zinc-500 text-sm font-medium", children: "Loading script..." })
382
+ ] }) });
383
+ }
369
384
  return /* @__PURE__ */ jsxs(Fragment, { children: [
370
- /* @__PURE__ */ jsxs("div", { className: "sticky top-2 sm:top-6 z-50 mx-auto w-full max-w-5xl bg-white/80 backdrop-blur-2xl border border-white/60 shadow-[0_8px_30px_rgb(0,0,0,0.06)] rounded-[1.5rem] sm:rounded-[2rem] flex items-center justify-between p-1.5 sm:p-2 mb-6 sm:mb-12 select-none transition-all", children: [
371
- /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center gap-1 overflow-x-auto custom-scrollbar pr-2 flex-1 [mask-image:linear-gradient(to_right,white_90%,transparent_100%)] sm:[mask-image:none] no-scrollbar", children: blockTypes.map((type) => {
385
+ /* @__PURE__ */ jsxs("div", { className: "sticky top-2 sm:top-6 z-50 mx-auto w-full max-w-full rounded-[2.5rem] sm:rounded-[2rem] bg-gradient-to-b from-white/90 to-white/70 border border-white/60 shadow-[0_14px_34px_rgba(16,37,54,0.06),0_2px_8px_rgba(16,37,54,0.03)] backdrop-blur-xl flex items-center justify-between px-2 py-1.5 sm:px-3 sm:py-2 mb-6 sm:mb-12 select-none transition-all", children: [
386
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 overflow-x-auto pr-2 flex-1 no-scrollbar rounded-full p-1 bg-gradient-to-b from-white/80 to-blumine-50 border border-blumine-200 shadow-inner", children: blockTypes.map((type) => {
372
387
  var _a;
373
388
  const selected = ((_a = blocks.find((b) => b.id === focusedBlockId)) == null ? void 0 : _a.type) === type;
374
389
  return /* @__PURE__ */ jsxs(
@@ -376,67 +391,37 @@ function ScreenplayEditorView({
376
391
  {
377
392
  type: "button",
378
393
  disabled: isLocked,
379
- className: `group flex shrink-0 items-center gap-2 sm:gap-2.5 px-3 py-1.5 sm:px-4 sm:py-2 rounded-full font-semibold text-xs sm:text-sm transition-all duration-300 ease-out active:scale-95 ${selected ? "bg-zinc-900 text-white shadow-md shadow-zinc-900/20" : "text-zinc-500 hover:bg-zinc-100 hover:text-zinc-900"} ${isLocked ? "opacity-50 cursor-not-allowed" : ""}`,
380
394
  onClick: () => handleBlockTypeChange(type),
395
+ className: `group flex shrink-0 items-center gap-2 px-3 sm:px-4 h-[40px] sm:h-[42px] rounded-full font-medium text-xs sm:text-sm transition-all duration-200 ease-out active:scale-95 whitespace-nowrap ${selected ? "bg-gradient-to-b from-white to-blumine-100 text-blumine-500 shadow-[0_6px_14px_rgba(24,88,122,0.15)] border border-blumine-200" : "text-blumine-500 hover:bg-white"} ${isLocked ? "opacity-50 cursor-not-allowed" : ""}`,
381
396
  children: [
382
397
  /* @__PURE__ */ jsx(
383
- "input",
398
+ "div",
384
399
  {
385
- type: "radio",
386
- name: "blockType",
387
- id: `block-type-${type}`,
388
- className: "sr-only",
389
- "aria-label": blockStyles[type].label,
390
- checked: selected,
391
- readOnly: true
400
+ className: `flex items-center justify-center transition-opacity duration-200 ${selected ? "opacity-100" : "opacity-70 group-hover:opacity-100"}`,
401
+ children: icons[type]
392
402
  }
393
403
  ),
394
- /* @__PURE__ */ jsxs(
395
- "label",
396
- {
397
- htmlFor: `block-type-${type}`,
398
- className: "flex items-center gap-2 cursor-pointer pointer-events-none",
399
- children: [
400
- /* @__PURE__ */ jsx(
401
- "div",
402
- {
403
- className: `${selected ? "opacity-100" : "opacity-70 group-hover:opacity-100"} transition-opacity duration-200 flex items-center justify-center`,
404
- children: icons[type]
405
- }
406
- ),
407
- /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap hidden lg:inline tracking-wide", children: blockStyles[type].label })
408
- ]
409
- }
410
- )
404
+ /* @__PURE__ */ jsx("span", { className: "hidden lg:inline tracking-wide", children: blockStyles[type].label })
411
405
  ]
412
406
  },
413
407
  type
414
408
  );
415
409
  }) }),
416
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 sm:gap-1 shrink-0 relative px-1 sm:px-2 ml-2 sm:ml-0 border-l border-zinc-200/80 sm:border-none pl-2 sm:pl-1", children: [
417
- /* @__PURE__ */ jsx("div", { className: "w-[1px] h-6 bg-zinc-200/80 mx-1 sm:mx-2 hidden sm:block rounded-full" }),
410
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 shrink-0 ml-2 sm:ml-0 pl-2 sm:pl-1 border-l sm:border-none border-blumine-200/70", children: [
411
+ /* @__PURE__ */ jsx("div", { className: "hidden sm:block w-[1px] h-6 bg-blumine-200 rounded-full mx-1" }),
418
412
  showPdfImport && !isLocked && /* @__PURE__ */ jsx(
419
413
  PdfImporter,
420
414
  {
421
415
  disabled: isLocked,
422
416
  onScriptImported: handleScriptImport,
423
- children: /* @__PURE__ */ jsx(
424
- "div",
425
- {
426
- title: "Import Script",
427
- className: "flex items-center justify-center",
428
- children: /* @__PURE__ */ jsx(Upload, { className: "w-4 h-4 sm:w-[18px] sm:h-[18px]" })
429
- }
430
- )
417
+ children: /* @__PURE__ */ jsx("div", { className: "w-9 h-9 sm:w-10 sm:h-10 flex items-center justify-center rounded-full text-blumine-500 hover:text-blumine-900 hover:bg-white/70 transition active:scale-95", children: /* @__PURE__ */ jsx(Upload, { className: "w-4 h-4 sm:w-[18px] sm:h-[18px]" }) })
431
418
  }
432
419
  ),
433
420
  onToggleLock && /* @__PURE__ */ jsx(
434
421
  "button",
435
422
  {
436
423
  onClick: onToggleLock,
437
- className: `flex items-center justify-center w-8 h-8 sm:w-10 sm:h-10 rounded-full transition-all duration-200 active:scale-95 ${isLocked ? "text-rose-500 hover:bg-rose-50" : "text-zinc-500 hover:bg-zinc-100 hover:text-zinc-900"}`,
438
- title: isLocked ? "Unlock Script" : "Lock Script",
439
- "aria-label": isLocked ? "Unlock Script" : "Lock Script",
424
+ className: `w-9 h-9 sm:w-10 sm:h-10 flex items-center justify-center rounded-full transition-all duration-200 active:scale-95 ${isLocked ? "text-rose-500 bg-rose-50/60" : "text-blumine-500 hover:text-blumine-900 hover:bg-white/70"}`,
440
425
  children: isLocked ? /* @__PURE__ */ jsx(Lock, { className: "w-4 h-4 sm:w-[18px] sm:h-[18px]" }) : /* @__PURE__ */ jsx(Unlock, { className: "w-4 h-4 sm:w-[18px] sm:h-[18px]" })
441
426
  }
442
427
  ),
@@ -444,9 +429,7 @@ function ScreenplayEditorView({
444
429
  "button",
445
430
  {
446
431
  onClick: onSave,
447
- className: "flex items-center justify-center w-8 h-8 sm:w-10 sm:h-10 rounded-full text-zinc-500 hover:bg-zinc-100 hover:text-zinc-900 transition-all duration-200 active:scale-95",
448
- title: "Save Script",
449
- "aria-label": "Save Script",
432
+ className: "w-9 h-9 sm:w-10 sm:h-10 flex items-center justify-center rounded-full text-blumine-500 hover:text-blumine-900 hover:bg-white/70 transition active:scale-95",
450
433
  children: /* @__PURE__ */ jsx(Save, { className: "w-4 h-4 sm:w-[18px] sm:h-[18px]" })
451
434
  }
452
435
  ),
@@ -454,9 +437,7 @@ function ScreenplayEditorView({
454
437
  "button",
455
438
  {
456
439
  onClick: onSaveAsPdf,
457
- className: "flex items-center justify-center w-8 h-8 sm:w-10 sm:h-10 rounded-full text-zinc-500 hover:bg-zinc-100 hover:text-zinc-900 transition-all duration-200 active:scale-95",
458
- title: "Save as PDF",
459
- "aria-label": "Save Script as PDF",
440
+ className: "w-9 h-9 sm:w-10 sm:h-10 flex items-center justify-center rounded-full text-blumine-500 hover:text-blumine-900 hover:bg-white/70 transition active:scale-95",
460
441
  children: /* @__PURE__ */ jsx(FileDown, { className: "w-4 h-4 sm:w-[18px] sm:h-[18px]" })
461
442
  }
462
443
  ),
@@ -464,9 +445,7 @@ function ScreenplayEditorView({
464
445
  "button",
465
446
  {
466
447
  onClick: onSyncWithCloud,
467
- className: "flex items-center justify-center w-8 h-8 sm:w-10 sm:h-10 rounded-full text-zinc-500 hover:bg-zinc-100 hover:text-zinc-900 transition-all duration-200 active:scale-95",
468
- title: "Sync with Cloud",
469
- "aria-label": "Sync with Cloud",
448
+ className: "w-9 h-9 sm:w-10 sm:h-10 flex items-center justify-center rounded-full text-blumine-500 hover:text-blumine-900 hover:bg-white/70 transition active:scale-95",
470
449
  children: /* @__PURE__ */ jsx(RefreshCcw, { className: "w-4 h-4 sm:w-[18px] sm:h-[18px]" })
471
450
  }
472
451
  ),
@@ -475,35 +454,65 @@ function ScreenplayEditorView({
475
454
  "button",
476
455
  {
477
456
  onClick: () => setIsRulesOpen(!isRulesOpen),
478
- className: `flex items-center justify-center w-8 h-8 sm:w-10 sm:h-10 rounded-full transition-all duration-200 active:scale-95 ${isRulesOpen ? "bg-zinc-900 text-white shadow-md shadow-zinc-900/20" : "text-zinc-500 hover:bg-zinc-100 hover:text-zinc-900"}`,
479
- title: "Settings & Shortcuts",
480
- "aria-label": "Toggle Settings",
457
+ className: `w-9 h-9 sm:w-10 sm:h-10 flex items-center justify-center rounded-full transition-all duration-200 active:scale-95 ${isRulesOpen ? "bg-gradient-to-b from-white to-blumine-100 text-blumine-500 shadow-md border border-blumine-100" : "text-blumine-500 hover:text-blumine-900 hover:bg-white/70"}`,
481
458
  children: /* @__PURE__ */ jsx(Cog, { className: "w-4 h-4 sm:w-[18px] sm:h-[18px]" })
482
459
  }
483
460
  ),
484
- isRulesOpen && /* @__PURE__ */ jsxs("div", { className: "absolute top-full mt-2 right-0 sm:-right-2 bg-white/95 backdrop-blur-3xl rounded-[1.5rem] shadow-[0_20px_60px_-15px_rgba(0,0,0,0.1)] p-4 sm:p-5 text-sm text-zinc-700 select-none font-sans overflow-hidden transition-all duration-300 w-[calc(100vw-2rem)] sm:w-72 max-w-[18rem] origin-top-right animate-in fade-in zoom-in-95 z-50", children: [
485
- /* @__PURE__ */ jsxs("h4", { className: "font-bold text-zinc-900 mb-4 text-sm flex items-center gap-2", children: [
486
- /* @__PURE__ */ jsx(Cog, { className: "w-4 h-4 text-zinc-400" }),
487
- "Settings & Shortcuts"
488
- ] }),
489
- /* @__PURE__ */ jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsxs("ul", { className: "space-y-2.5", children: [
490
- /* @__PURE__ */ jsxs("li", { className: "flex items-center justify-between gap-6", children: [
491
- /* @__PURE__ */ jsx("span", { className: "font-medium text-zinc-600", children: "New Block" }),
492
- /* @__PURE__ */ jsx("kbd", { className: "px-2 py-1 text-[11px] font-bold text-zinc-700 bg-white border border-zinc-200/80 shadow-[0_2px_4px_rgb(0,0,0,0.02)] rounded-md", children: "Enter" })
493
- ] }),
494
- /* @__PURE__ */ jsxs("li", { className: "flex items-center justify-between gap-6", children: [
495
- /* @__PURE__ */ jsx("span", { className: "font-medium text-zinc-600", children: "Delete Block" }),
496
- /* @__PURE__ */ jsx("kbd", { className: "px-2 py-1 text-[11px] font-bold text-zinc-700 bg-white border border-zinc-200/80 shadow-[0_2px_4px_rgb(0,0,0,0.02)] rounded-md", children: "Backspace" })
497
- ] }),
498
- /* @__PURE__ */ jsxs("li", { className: "flex items-center justify-between gap-6", children: [
499
- /* @__PURE__ */ jsx("span", { className: "font-medium text-zinc-600", children: "Change Type" }),
500
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
501
- /* @__PURE__ */ jsx("kbd", { className: "px-2 py-1 text-[11px] font-bold text-zinc-700 bg-white border border-zinc-200/80 shadow-[0_2px_4px_rgb(0,0,0,0.02)] rounded-md", children: "Ctrl" }),
502
- /* @__PURE__ */ jsx("span", { className: "text-zinc-400 font-medium", children: "+" }),
503
- /* @__PURE__ */ jsx("kbd", { className: "px-2 py-1 text-[11px] font-bold text-zinc-700 bg-white border border-zinc-200/80 shadow-[0_2px_4px_rgb(0,0,0,0.02)] rounded-md", children: "\u2191/\u2193" })
504
- ] })
461
+ isRulesOpen && /* @__PURE__ */ jsxs("div", { className: "absolute -right-2 top-[calc(100%+14px)] w-[332px] max-w-[90vw] p-3 rounded-[24px] bg-white border border-blumine-200/70 shadow-[0_18px_40px_rgba(16,37,54,0.08),0_3px_10px_rgba(16,37,54,0.04)] backdrop-blur-xl origin-top-right animate-in fade-in zoom-in-95 z-50", children: [
462
+ /* @__PURE__ */ jsx("div", { className: "absolute -top-2 right-5 w-4 h-4 rotate-45 bg-white border-l border-t border-blumine-200/70" }),
463
+ /* @__PURE__ */ jsx("div", { className: "flex items-start justify-between mb-3 px-1", children: /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
464
+ /* @__PURE__ */ jsx("div", { className: "w-10 h-10 flex items-center justify-center rounded-xl bg-gradient-to-b from-white to-blumine-50 text-blumine-500 border border-blumine-200/70 shadow-inner", children: /* @__PURE__ */ jsx(Cog, { className: "w-4 h-4" }) }),
465
+ /* @__PURE__ */ jsxs("div", { children: [
466
+ /* @__PURE__ */ jsx("h4", { className: "text-[15px] font-semibold text-blumine-900 leading-tight", children: "Settings & Shortcuts" }),
467
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-blumine-500 mt-1", children: "Writing flow & editor controls" })
505
468
  ] })
506
- ] }) })
469
+ ] }) }),
470
+ /* @__PURE__ */ jsx("div", { className: "space-y-2", children: [
471
+ {
472
+ title: "New Block",
473
+ desc: "Insert the next section",
474
+ key: ["Enter"]
475
+ },
476
+ {
477
+ title: "Delete Block",
478
+ desc: "Remove selected section",
479
+ key: ["Backspace \u{1F860}"]
480
+ },
481
+ {
482
+ title: "Change Type",
483
+ desc: "Cycle block styles",
484
+ key: ["Ctrl", "+", "\u2191/\u2193"]
485
+ }
486
+ ].map((item) => /* @__PURE__ */ jsxs(
487
+ "div",
488
+ {
489
+ className: "flex items-center justify-between gap-4 p-3 rounded-[18px] bg-gradient-to-b from-white to-blumine-50/50 border border-blumine-200/60 shadow-[inset_0_1px_0_rgba(255,255,255,0.8)]",
490
+ children: [
491
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col min-w-0", children: [
492
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-blumine-800", children: item.title }),
493
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] text-blumine-500 truncate", children: item.desc })
494
+ ] }),
495
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 shrink-0", children: item.key.map(
496
+ (k, i) => k === "+" ? /* @__PURE__ */ jsx(
497
+ "span",
498
+ {
499
+ className: "text-blumine-400 text-xs font-semibold",
500
+ children: "+"
501
+ },
502
+ i
503
+ ) : /* @__PURE__ */ jsx(
504
+ "kbd",
505
+ {
506
+ className: "min-w-[28px] px-2 py-1 text-[11px] font-bold text-primary bg-gradient-to-b from-white to-blumine-100 border border-blumine-200/70 rounded-full text-center shadow-[inset_0_1px_0_rgba(255,255,255,0.9)]",
507
+ children: k
508
+ },
509
+ i
510
+ )
511
+ ) })
512
+ ]
513
+ },
514
+ item.title
515
+ )) })
507
516
  ] })
508
517
  ] })
509
518
  ] })
@@ -703,7 +712,7 @@ function ScreenplayEditorView({
703
712
  handleBlur(block.id);
704
713
  },
705
714
  children: [
706
- /* @__PURE__ */ jsx(User, { className: "w-3.5 h-3.5 text-slate-300 group-hover:text-sky-500 transition-colors mr-3" }),
715
+ /* @__PURE__ */ jsx(User, { className: "w-3.5 h-3.5 text-slate-300 group-hover:text-blumine-500 transition-colors mr-3" }),
707
716
  /* @__PURE__ */ jsx("span", { className: "flex-1 text-[11px] font-bold tracking-[0.1em] text-slate-600 uppercase text-left", children: char }),
708
717
  /* @__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" })
709
718
  ]
@@ -739,6 +748,64 @@ function ScreenplayEditorView({
739
748
  ext
740
749
  )) })
741
750
  }
751
+ ),
752
+ focusedBlockId === block.id && !enhancingBlockId && (block.type === "ACTION" || block.type === "DIALOGUE") && /* @__PURE__ */ jsx(
753
+ "button",
754
+ {
755
+ onClick: () => handleEnhance(block),
756
+ className: "absolute -right-12 top-1/2 -translate-y-1/2 flex items-center justify-center w-8 h-8 rounded-xl border border-blumine-200/50 bg-white text-blumine-500 hover:border-blumine-400/60 hover:bg-blumine-50/90 hover:ring-2 hover:ring-blumine-100 hover:scale-105 active:scale-95 transition-all duration-200 group/enhance animate-in fade-in slide-in-from-left-2",
757
+ title: "Enhance with AI",
758
+ children: /* @__PURE__ */ jsx(Sparkles, { className: "w-4 h-4 group-hover/enhance:animate-pulse transition-colors" })
759
+ }
760
+ ),
761
+ enhancingBlockId === block.id && (enhancementSuggestion || isEnhancing) && /* @__PURE__ */ jsxs(
762
+ "div",
763
+ {
764
+ className: "absolute left-0 right-0 -top-4 -translate-y-full z-[30] animate-in fade-in zoom-in-95 slide-in-from-bottom-4 duration-300",
765
+ style: { width: "calc(100% + 2rem)", left: "-1rem" },
766
+ children: [
767
+ /* @__PURE__ */ jsx("div", { className: "mx-auto w-full rounded-[2rem] bg-white border border-white/60 shadow-[0_14px_34px_rgba(16,37,54,0.1),0_2px_8px_rgba(16,37,54,0.05)] backdrop-blur-xl p-2 select-none transition-all", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
768
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pl-1", children: [
769
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
770
+ /* @__PURE__ */ jsx("div", { className: "w-9 h-9 flex items-center justify-center rounded-full bg-blumine-50 text-blumine-500 border border-blumine-100 shadow-inner", children: /* @__PURE__ */ jsx(Sparkles, { className: "w-4 h-4" }) }),
771
+ /* @__PURE__ */ jsxs("div", { children: [
772
+ /* @__PURE__ */ jsx("p", { className: "text-[11px] font-bold text-zinc-900 uppercase tracking-wider leading-none", children: "AI Suggestion" }),
773
+ !isEnhancing && /* @__PURE__ */ jsx("p", { className: "text-[10px] text-zinc-500 mt-1 font-medium", children: "Refining your screenplay dialogue" })
774
+ ] })
775
+ ] }),
776
+ !isEnhancing && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
777
+ /* @__PURE__ */ jsx(
778
+ "button",
779
+ {
780
+ onClick: handleRejectEnhance,
781
+ className: "px-3 py-2 rounded-full text-xs font-bold text-zinc-500 hover:bg-zinc-100 hover:text-zinc-900 transition-all active:scale-95",
782
+ children: "Discard"
783
+ }
784
+ ),
785
+ /* @__PURE__ */ jsxs(
786
+ "button",
787
+ {
788
+ onClick: handleApproveEnhance,
789
+ className: "px-4 py-2 rounded-full text-xs font-bold bg-blumine-500 text-white hover:bg-blumine-600 shadow-lg shadow-blumine-500/20 transition-all active:scale-95 flex items-center gap-1.5",
790
+ children: [
791
+ /* @__PURE__ */ jsx(Check, { className: "w-3.5 h-3.5" }),
792
+ "Apply Changes"
793
+ ]
794
+ }
795
+ )
796
+ ] })
797
+ ] }),
798
+ /* @__PURE__ */ jsx("div", { className: "relative overflow-hidden rounded-[1.25rem] bg-zinc-50/50 border border-zinc-100 p-4 min-h-[4rem]", children: isEnhancing ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2.5 py-1", children: [
799
+ /* @__PURE__ */ jsx("div", { className: "h-3.5 w-3/4 bg-zinc-200/60 animate-pulse rounded-full" }),
800
+ /* @__PURE__ */ jsx("div", { className: "h-3.5 w-1/2 bg-zinc-200/60 animate-pulse rounded-full" })
801
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
802
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-zinc-400 line-through decoration-zinc-300 italic", children: block.text }),
803
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-zinc-800 font-medium leading-relaxed", children: enhancementSuggestion })
804
+ ] }) })
805
+ ] }) }),
806
+ /* @__PURE__ */ jsx("div", { className: "absolute -bottom-1.5 left-1/2 -translate-x-1/2 w-3.5 h-3.5 bg-white border-r border-b border-zinc-200/50 rotate-45" })
807
+ ]
808
+ }
742
809
  )
743
810
  ] })
744
811
  },
@@ -746,7 +813,43 @@ function ScreenplayEditorView({
746
813
  );
747
814
  })
748
815
  }
749
- ) })
816
+ ) }),
817
+ /* @__PURE__ */ jsx(
818
+ "div",
819
+ {
820
+ className: `sticky bottom-0 left-1/2 -translate-x-1/2 z-[100] transition-all duration-500 ease-in-out ${showUnsavedPopover ? "translate-y-0 opacity-100 scale-100" : "translate-y-full opacity-0 scale-95 pointer-events-none"}`,
821
+ children: /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-full rounded-[2.5rem] bg-white border-white/60 shadow-[0_14px_34px_rgba(16,37,54,0.06),0_2px_8px_rgba(16,37,54,0.03)] backdrop-blur-xl flex items-center justify-between px-2 py-1.5 sm:px-3 sm:py-2 mb-6 sm:mb-12 select-none transition-all", children: [
822
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
823
+ /* @__PURE__ */ jsx("div", { className: "w-10 h-10 flex items-center justify-center rounded-[2.5rem] bg-blumine-50 text-blumine-500 border border-blumine-100 shadow-inner", children: /* @__PURE__ */ jsx(Save, { className: "w-5 h-5" }) }),
824
+ /* @__PURE__ */ jsxs("div", { className: "mr-5", children: [
825
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-bold text-zinc-900 leading-none", children: "Unsaved Changes" }),
826
+ /* @__PURE__ */ jsx("p", { className: "text-[11px] text-zinc-500 mt-1 font-medium", children: "You have changes that haven't been saved yet." })
827
+ ] })
828
+ ] }),
829
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 w-full sm:w-auto", children: [
830
+ /* @__PURE__ */ jsx(
831
+ "button",
832
+ {
833
+ onClick: ignoreChanges,
834
+ className: "flex-1 sm:flex-none px-4 py-2 rounded-[2.5rem] text-xs font-bold text-zinc-500 hover:bg-zinc-100 hover:text-zinc-900 transition-all active:scale-95 whitespace-nowrap",
835
+ children: "Ignore"
836
+ }
837
+ ),
838
+ /* @__PURE__ */ jsxs(
839
+ "button",
840
+ {
841
+ onClick: syncScreenplay,
842
+ className: "flex-1 sm:flex-none px-5 py-2 rounded-[2.5rem] text-xs font-bold bg-blumine-500 text-white hover:bg-blumine-600 shadow-lg shadow-blumine-500/25 transition-all active:scale-95 flex items-center justify-center gap-2 whitespace-nowrap",
843
+ children: [
844
+ /* @__PURE__ */ jsx(RefreshCcw, { className: "w-4 h-4" }),
845
+ "Sync to Cloud"
846
+ ]
847
+ }
848
+ )
849
+ ] })
850
+ ] })
851
+ }
852
+ )
750
853
  ] });
751
854
  }
752
855
 
@@ -954,6 +1057,31 @@ function parseScreenplayText(content) {
954
1057
  }
955
1058
  return blocks.length > 0 ? blocks : [createNewBlock("SCENE_HEADING")];
956
1059
  }
1060
+ function serializeToSbx(blocks) {
1061
+ const typeToDivClass = {
1062
+ SCENE_HEADING: "divtype0",
1063
+ ACTION: "divtype2",
1064
+ CHARACTER: "divtype3",
1065
+ PARENTHETICAL: "divtype4",
1066
+ DIALOGUE: "divtype5",
1067
+ TRANSITION: "divtype6",
1068
+ GENERAL: "divtype2"
1069
+ };
1070
+ return blocks.map((block) => {
1071
+ const divClass = typeToDivClass[block.type] || "divtype2";
1072
+ let text = block.text || "";
1073
+ let extraAttributes = "";
1074
+ if (block.type === "SCENE_HEADING") {
1075
+ text = `${block.sceneType || "INT."} ${text} - ${block.timeOfDay || "DAY"}`.toUpperCase();
1076
+ if (block.sceneNumber) {
1077
+ extraAttributes = ` data-scene="${block.sceneNumber}"`;
1078
+ }
1079
+ } else if (block.type === "CHARACTER" || block.type === "TRANSITION") {
1080
+ text = text.toUpperCase();
1081
+ }
1082
+ return `<div class="${divClass}" id="par${block.id}"${extraAttributes}>${text}</div>`;
1083
+ }).join("");
1084
+ }
957
1085
 
958
1086
  // app/hook/use-screenplay-editor.ts
959
1087
  var initialBlocks = [
@@ -1022,13 +1150,23 @@ function useScreenplayEditor(options) {
1022
1150
  const [newBlockId, setNewBlockId] = useState(null);
1023
1151
  const [showSuggestions, setShowSuggestions] = useState(false);
1024
1152
  const [showExtensionSuggestions, setShowExtensionSuggestions] = useState(false);
1153
+ const [isLoading, setIsLoading] = useState(!!(options == null ? void 0 : options.initialUrl));
1154
+ const [enhancingBlockId, setEnhancingBlockId] = useState(null);
1155
+ const [enhancementSuggestion, setEnhancementSuggestion] = useState(null);
1156
+ const [isEnhancing, setIsEnhancing] = useState(false);
1157
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
1158
+ const [showUnsavedPopover, setShowUnsavedPopover] = useState(false);
1159
+ const popoverTimeoutRef = useRef(null);
1025
1160
  const blurTimeout = useRef(null);
1161
+ const isInitialLoadRef = useRef(true);
1026
1162
  const loadedUrlRef = useRef(null);
1027
1163
  const lastSavedContent = useRef(null);
1028
1164
  const onSaveRef = useRef(options == null ? void 0 : options.onSave);
1165
+ const onSyncWithCloudRef = useRef(options == null ? void 0 : options.onSyncWithCloud);
1029
1166
  useEffect(() => {
1030
1167
  onSaveRef.current = options == null ? void 0 : options.onSave;
1031
- }, [options == null ? void 0 : options.onSave]);
1168
+ onSyncWithCloudRef.current = options == null ? void 0 : options.onSyncWithCloud;
1169
+ }, [options == null ? void 0 : options.onSave, options == null ? void 0 : options.onSyncWithCloud]);
1032
1170
  const characterExtensions = useMemo(
1033
1171
  () => ["(V.O.)", "(O.S.)", "(O.C.)", "(SUBTITLE)", "(CONT'D)"],
1034
1172
  []
@@ -1415,29 +1553,7 @@ function useScreenplayEditor(options) {
1415
1553
  });
1416
1554
  setBlocks(finalizedBlocks);
1417
1555
  if (onSaveRef.current) {
1418
- const typeToDivClass = {
1419
- SCENE_HEADING: "divtype0",
1420
- ACTION: "divtype2",
1421
- CHARACTER: "divtype3",
1422
- PARENTHETICAL: "divtype4",
1423
- DIALOGUE: "divtype5",
1424
- TRANSITION: "divtype6",
1425
- GENERAL: "divtype2"
1426
- };
1427
- const sbxData = finalizedBlocks.map((block) => {
1428
- const divClass = typeToDivClass[block.type] || "divtype2";
1429
- let text = block.text || "";
1430
- let extraAttributes = "";
1431
- if (block.type === "SCENE_HEADING") {
1432
- text = `${block.sceneType || "INT."} ${text} - ${block.timeOfDay || "DAY"}`.toUpperCase();
1433
- if (block.sceneNumber) {
1434
- extraAttributes = ` data-scene="${block.sceneNumber}"`;
1435
- }
1436
- } else if (block.type === "CHARACTER" || block.type === "TRANSITION") {
1437
- text = text.toUpperCase();
1438
- }
1439
- return `<div class="${divClass}" id="par${block.id}"${extraAttributes}>${text}</div>`;
1440
- }).join("");
1556
+ const sbxData = serializeToSbx(finalizedBlocks);
1441
1557
  if (sbxData !== lastSavedContent.current) {
1442
1558
  lastSavedContent.current = sbxData;
1443
1559
  if (!isInitialLoad) {
@@ -1490,9 +1606,22 @@ function useScreenplayEditor(options) {
1490
1606
  setShowExtensionSuggestions(false);
1491
1607
  }, 200);
1492
1608
  }, []);
1609
+ const syncScreenplay = useCallback(() => {
1610
+ if (!onSyncWithCloudRef.current) return;
1611
+ onSyncWithCloudRef.current(blocks, sceneNumbers);
1612
+ lastSavedContent.current = serializeToSbx(blocks);
1613
+ setHasUnsavedChanges(false);
1614
+ setShowUnsavedPopover(false);
1615
+ if (popoverTimeoutRef.current) clearTimeout(popoverTimeoutRef.current);
1616
+ }, [blocks, sceneNumbers]);
1617
+ const ignoreChanges = useCallback(() => {
1618
+ setShowUnsavedPopover(false);
1619
+ if (popoverTimeoutRef.current) clearTimeout(popoverTimeoutRef.current);
1620
+ }, []);
1493
1621
  const loadFromUrl = useCallback(
1494
1622
  async (url, fetchOptions = {}, isInitialLoad) => {
1495
1623
  var _a;
1624
+ setIsLoading(true);
1496
1625
  try {
1497
1626
  const response = await fetch(url, fetchOptions);
1498
1627
  if (!response.ok) {
@@ -1569,16 +1698,42 @@ function useScreenplayEditor(options) {
1569
1698
  preParsedBlocks,
1570
1699
  isInitialLoad
1571
1700
  );
1572
- } catch (error) {
1573
- console.error(
1574
- "[useScreenplayEditor] Error loading script from URL:",
1575
- error
1576
- );
1577
- throw error;
1701
+ } finally {
1702
+ setIsLoading(false);
1578
1703
  }
1579
1704
  },
1580
1705
  [handleScriptImport]
1581
1706
  );
1707
+ useEffect(() => {
1708
+ if (isLoading) {
1709
+ isInitialLoadRef.current = true;
1710
+ return;
1711
+ }
1712
+ const currentSbx = serializeToSbx(blocks);
1713
+ if (isInitialLoadRef.current) {
1714
+ if (lastSavedContent.current === null) {
1715
+ lastSavedContent.current = currentSbx;
1716
+ }
1717
+ isInitialLoadRef.current = false;
1718
+ return;
1719
+ }
1720
+ const hasActualChanges = lastSavedContent.current !== null && currentSbx !== lastSavedContent.current;
1721
+ if (hasActualChanges) {
1722
+ setHasUnsavedChanges(true);
1723
+ if (popoverTimeoutRef.current) clearTimeout(popoverTimeoutRef.current);
1724
+ setShowUnsavedPopover(false);
1725
+ popoverTimeoutRef.current = setTimeout(() => {
1726
+ setShowUnsavedPopover(true);
1727
+ }, 1e3);
1728
+ } else {
1729
+ setHasUnsavedChanges(false);
1730
+ setShowUnsavedPopover(false);
1731
+ if (popoverTimeoutRef.current) clearTimeout(popoverTimeoutRef.current);
1732
+ }
1733
+ return () => {
1734
+ if (popoverTimeoutRef.current) clearTimeout(popoverTimeoutRef.current);
1735
+ };
1736
+ }, [blocks, isLoading]);
1582
1737
  useEffect(() => {
1583
1738
  if ((options == null ? void 0 : options.initialUrl) && options.initialUrl !== loadedUrlRef.current) {
1584
1739
  loadedUrlRef.current = options.initialUrl;
@@ -1595,6 +1750,7 @@ function useScreenplayEditor(options) {
1595
1750
  locations,
1596
1751
  characters,
1597
1752
  sceneNumbers,
1753
+ isLoading,
1598
1754
  handleBlockTextChange,
1599
1755
  handleSceneTypeChange,
1600
1756
  handleTimeOfDayChange,
@@ -1605,6 +1761,61 @@ function useScreenplayEditor(options) {
1605
1761
  handleSceneNumberChange,
1606
1762
  handleFocus,
1607
1763
  handleBlur,
1764
+ handleEnhance: async (block) => {
1765
+ if (isEnhancing || enhancingBlockId || !(options == null ? void 0 : options.enhanceContentUrl))
1766
+ return;
1767
+ setEnhancingBlockId(block.id);
1768
+ setIsEnhancing(true);
1769
+ setEnhancementSuggestion(null);
1770
+ try {
1771
+ const res = await fetch(options == null ? void 0 : options.enhanceContentUrl, {
1772
+ method: "POST",
1773
+ headers: {
1774
+ "Content-Type": "application/json"
1775
+ },
1776
+ body: JSON.stringify({
1777
+ mode: block.type.toLowerCase(),
1778
+ script: block.text,
1779
+ language: "English"
1780
+ })
1781
+ });
1782
+ const data = await res.json();
1783
+ const content = data.data;
1784
+ setEnhancementSuggestion(content);
1785
+ } catch (error) {
1786
+ console.error("Failed to enhance block:", error);
1787
+ if (error) {
1788
+ setIsEnhancing(false);
1789
+ }
1790
+ } finally {
1791
+ setIsEnhancing(false);
1792
+ }
1793
+ },
1794
+ handleApproveEnhance: () => {
1795
+ if (enhancingBlockId && enhancementSuggestion) {
1796
+ setBlocks(
1797
+ (bs) => updateBlock(
1798
+ bs,
1799
+ enhancingBlockId,
1800
+ "text",
1801
+ enhancementSuggestion
1802
+ )
1803
+ );
1804
+ }
1805
+ setEnhancingBlockId(null);
1806
+ setEnhancementSuggestion(null);
1807
+ },
1808
+ handleRejectEnhance: () => {
1809
+ setEnhancingBlockId(null);
1810
+ setEnhancementSuggestion(null);
1811
+ },
1812
+ enhancingBlockId,
1813
+ enhancementSuggestion,
1814
+ isEnhancing,
1815
+ hasUnsavedChanges,
1816
+ showUnsavedPopover,
1817
+ syncScreenplay,
1818
+ ignoreChanges,
1608
1819
  loadFromUrl
1609
1820
  };
1610
1821
  }
@@ -4295,7 +4506,7 @@ function ShotBreakdownView({
4295
4506
  /* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-full bg-slate-50 border border-slate-100 mb-3", children: /* @__PURE__ */ jsx(Video, { className: "h-4 w-4 text-slate-300" }) }),
4296
4507
  /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-slate-700", children: "No shots created" }),
4297
4508
  /* @__PURE__ */ jsx("p", { className: "text-[11px] text-slate-500 mt-1 max-w-[160px] leading-relaxed", children: "Highlight text in the screenplay to add your first shot." })
4298
- ] }) : /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2.5 overflow-y-auto overflow-x-hidden pb-4 pr-2 custom-scrollbar", children: shots.map((shot) => {
4509
+ ] }) : /* @__PURE__ */ jsx("div", { className: "flex h-full max-h-[calc(100vh-200px)] flex-col gap-2.5 overflow-y-auto overflow-x-hidden pb-4 pr-2 custom-scrollbar", children: shots.map((shot) => {
4299
4510
  const isActive = toggledShotId === shot.id;
4300
4511
  return /* @__PURE__ */ jsxs(
4301
4512
  "div",
@@ -4304,13 +4515,13 @@ function ShotBreakdownView({
4304
4515
  var _a2;
4305
4516
  return setToggledShotId(isActive ? null : (_a2 = shot.id) != null ? _a2 : null);
4306
4517
  },
4307
- className: `group relative cursor-pointer rounded-xl border transition-all duration-300 overflow-hidden ${isActive ? "border-slate-900 bg-slate-50 shadow-[0_6px_20px_rgba(0,0,0,0.06)]" : "border-slate-200 bg-white hover:border-slate-300 hover:shadow-[0_4px_14px_rgba(0,0,0,0.05)]"}`,
4518
+ className: `group relative flex-shrink-0 cursor-pointer rounded-xl border transition-all duration-300 overflow-hidden ${isActive ? "border-slate-900 bg-slate-50 shadow-[0_6px_20px_rgba(0,0,0,0.06)]" : "border-slate-200 bg-white hover:border-slate-300 hover:shadow-[0_4px_14px_rgba(0,0,0,0.05)]"}`,
4308
4519
  children: [
4309
4520
  /* @__PURE__ */ jsx(
4310
4521
  "div",
4311
4522
  {
4312
4523
  className: `absolute left-0 top-0 h-full w-[3px] transition-all duration-300
4313
- ${isActive ? "bg-slate-900" : "bg-transparent group-hover:bg-slate-300"}`
4524
+ ${isActive ? "bg-slate-900" : "bg-transparent group-hover:bg-slate-300"}`
4314
4525
  }
4315
4526
  ),
4316
4527
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 px-4 py-3.5", children: [
@@ -4320,7 +4531,7 @@ function ShotBreakdownView({
4320
4531
  "span",
4321
4532
  {
4322
4533
  className: `text-[11px] font-semibold tracking-wide uppercase
4323
- ${isActive ? "text-slate-900" : "text-slate-500"}`,
4534
+ ${isActive ? "text-slate-900" : "text-slate-500"}`,
4324
4535
  children: [
4325
4536
  "Shot ",
4326
4537
  shot.shot_number
@@ -4332,7 +4543,7 @@ function ShotBreakdownView({
4332
4543
  /* @__PURE__ */ jsx(
4333
4544
  "span",
4334
4545
  {
4335
- className: `rounded-md px-2 py-0.5 text-[9px] font-bold uppercase tracking-wider ${isActive ? "bg-slate-900 text-white" : "bg-slate-100 text-slate-500 group-hover:bg-slate-200"}`,
4546
+ className: `rounded-md px-2 py-0.5 text-[8px] font-bold uppercase tracking-wider ${isActive ? "bg-slate-900 text-white" : "bg-slate-100 text-slate-500 group-hover:bg-slate-200"}`,
4336
4547
  children: shot.shot_type
4337
4548
  }
4338
4549
  )
@@ -4344,8 +4555,8 @@ function ShotBreakdownView({
4344
4555
  /* @__PURE__ */ jsx(
4345
4556
  "div",
4346
4557
  {
4347
- className: `grid transition-all duration-300 ease-in-out ${isActive ? "grid-rows-[2fr] mt-2" : "grid-rows-[0fr]"}`,
4348
- children: /* @__PURE__ */ jsx("div", { className: "overflow-hidden", children: /* @__PURE__ */ jsxs("div", { className: "py-3 pb-4 border-t border-slate-200/70 flex gap-2", children: [
4558
+ className: `grid transition-all duration-300 ease-in-out ${isActive ? "grid-rows-[1fr] opacity-100 mt-2" : "grid-rows-[0fr] opacity-0"}`,
4559
+ children: /* @__PURE__ */ jsx("div", { className: "overflow-hidden", children: /* @__PURE__ */ jsxs("div", { className: "py-3 pb-1 border-t border-slate-200/70 flex gap-2", children: [
4349
4560
  /* @__PURE__ */ jsxs(
4350
4561
  "button",
4351
4562
  {
@@ -4704,9 +4915,8 @@ function useShotBreakdownScene(options) {
4704
4915
  if (res && res.ok) {
4705
4916
  const data = await res.json();
4706
4917
  setIsSummarizing(false);
4707
- console.log(data);
4708
4918
  const newShots = [];
4709
- data.data.forEach((aiShot) => {
4919
+ data.data[0].forEach((aiShot) => {
4710
4920
  var _a2;
4711
4921
  const newShot = {
4712
4922
  id: aiShot.id || uuid(),
@@ -4744,7 +4954,7 @@ function useShotBreakdownScene(options) {
4744
4954
  if (newShots.length > 0) {
4745
4955
  setShots((prev) => [...prev, ...newShots]);
4746
4956
  if (options.onShotsBulkAdded) {
4747
- await options.onShotsBulkAdded(newShots);
4957
+ await options.onShotsBulkAdded(newShots, data.data[1]);
4748
4958
  }
4749
4959
  }
4750
4960
  } else {