@vishu1301/script-writing 1.5.7 → 1.5.9

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,11 +1,11 @@
1
- import { useState, useRef, useEffect, useMemo, useCallback } from 'react';
1
+ import React4, { useState, useRef, useEffect, useMemo, useCallback } from 'react';
2
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, Languages, Check, Keyboard, ArrowRight, ChevronRight, Sparkles, Tags, ChevronDown, X, Video, Settings2, Eye, Pencil, BookOpen, Search, Zap, Info, Frame, BookText, AsteriskIcon, Target, Activity, MonitorPlay, Crosshair, Clock, Camera, Aperture, SlidersHorizontal, Sun, Wand2, Volume2, Layers } 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';
6
6
  import html2canvas from 'html2canvas-pro';
7
- import * as Yup from 'yup';
8
7
  import { Formik, Form, ErrorMessage, Field, useFormikContext } from 'formik';
8
+ import * as Yup from 'yup';
9
9
 
10
10
  var __defProp = Object.defineProperty;
11
11
  var __defProps = Object.defineProperties;
@@ -3298,1274 +3298,1473 @@ var SummarizeButton = ({
3298
3298
  );
3299
3299
  };
3300
3300
  var summarize_button_default = SummarizeButton;
3301
- function ScriptBreakdownSceneView({
3302
- blocks,
3303
- characters,
3304
- isLoading,
3305
- sceneNumber,
3306
- tags,
3307
- selectionMenu,
3308
- handleMouseUp,
3309
- addTag,
3310
- updateTag,
3311
- removeTag,
3312
- clearSelection,
3313
- menuPlacement,
3314
- menuRef,
3315
- sceneBrief,
3316
- setSceneBrief,
3317
- onSummarize,
3318
- isSummarizing,
3319
- aiSummarized = false,
3320
- onUpdateBrief
3321
- }) {
3322
- const [expandedCategories, setExpandedCategories] = useState({});
3323
- const [editingTagData, setEditingTagData] = useState(null);
3324
- const [tagForm, setTagForm] = useState({ quantity: 1, look: "", age: "" });
3325
- const [popupPlacement, setPopupPlacement] = useState({
3326
- alignRight: false,
3327
- alignBottom: false
3328
- });
3329
- const [isSidebarOpen, setIsSidebarOpen] = useState(false);
3330
- const COURIER_STACK = "'Courier Prime', 'Courier', monospace";
3331
- useEffect(() => {
3332
- const fontId = "google-font-courier-prime";
3333
- const styleId = "screenplay-editor-force-v4";
3334
- if (!document.getElementById(fontId)) {
3335
- const link = document.createElement("link");
3336
- link.id = fontId;
3337
- link.rel = "stylesheet";
3338
- link.href = "https://fonts.googleapis.com/css2?family=Courier+Prime:ital,wght@0,400;0,700;1,400;1,700&display=swap";
3339
- document.head.appendChild(link);
3340
- }
3341
- if (!document.getElementById(styleId)) {
3342
- const style = document.createElement("style");
3343
- style.id = styleId;
3344
- style.textContent = `
3345
- /* We target by the data-attribute to ensure the highest specificity possible */
3346
- [data-screenplay-editor] *,
3347
- [data-screenplay-editor] div,
3348
- [data-screenplay-editor] span,
3349
- [data-screenplay-editor] [contenteditable="true"] {
3350
- font-family: ${COURIER_STACK} !important;
3351
- -webkit-font-smoothing: antialiased;
3352
- }
3353
- `;
3354
- document.head.appendChild(style);
3355
- }
3356
- }, [COURIER_STACK]);
3357
- if (isLoading) {
3358
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-32 gap-4", children: [
3359
- /* @__PURE__ */ jsx(Loader2, { className: "w-8 h-8 animate-spin text-zinc-400" }),
3360
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-zinc-500 animate-pulse", children: "Loading scene details..." })
3361
- ] });
3362
- }
3363
- const hasLocationTag = tags.some((t) => t.category_id === "LOCATION");
3364
- const renderBlockText = (block) => {
3365
- const blockTags = tags.filter((t) => t.block_id === block.id).sort((a, b) => a.start_index - b.start_index);
3366
- if (blockTags.length === 0) return block.text;
3367
- const nodes = [];
3368
- let currentIndex = 0;
3369
- blockTags.forEach((tag) => {
3370
- const actualStart = Math.max(tag.start_index, currentIndex);
3371
- if (actualStart > currentIndex) {
3372
- nodes.push(
3373
- /* @__PURE__ */ jsx("span", { children: block.text.slice(currentIndex, actualStart) }, `text-${currentIndex}`)
3374
- );
3301
+
3302
+ // data/crowd-data.ts
3303
+ var crowdAgeOption = [
3304
+ { name: "Adult" },
3305
+ { name: "Old" },
3306
+ { name: "Teenage" },
3307
+ { name: "Kid" }
3308
+ ];
3309
+ var crowdTypeOption = [{ name: "Male" }, { name: "Female" }];
3310
+ var FormikSelect = ({
3311
+ label,
3312
+ name,
3313
+ selectedOption,
3314
+ optionData,
3315
+ value,
3316
+ disable,
3317
+ divClasses,
3318
+ valueProperty = "name",
3319
+ labelProperty = "name",
3320
+ label2propery,
3321
+ brackets,
3322
+ className,
3323
+ enableRedAsterick = false,
3324
+ disableOptionProperty = "disable",
3325
+ disabledOptionText = "",
3326
+ onChange
3327
+ }) => {
3328
+ return /* @__PURE__ */ jsxs("div", { className: `w-full ${divClasses}`, children: [
3329
+ label && /* @__PURE__ */ jsxs(
3330
+ "label",
3331
+ {
3332
+ className: `mb-1.5 text-sm font-medium text-slate-700 capitalize flex`,
3333
+ children: [
3334
+ label,
3335
+ enableRedAsterick && /* @__PURE__ */ jsx(AsteriskIcon, { className: "size-3 text-red-500" })
3336
+ ]
3375
3337
  }
3376
- const category = CATEGORIES.find((c) => c.id === tag.category_id);
3377
- if (actualStart < tag.end_index) {
3378
- nodes.push(
3379
- /* @__PURE__ */ jsx(
3380
- "span",
3338
+ ),
3339
+ /* @__PURE__ */ jsxs(
3340
+ Field,
3341
+ {
3342
+ name,
3343
+ as: "select",
3344
+ value,
3345
+ disabled: disable,
3346
+ onChange,
3347
+ className: `${className} rounded-[2.5rem] block w-full px-3 py-2.5 text-slate-700 placeholder-slate-400 placeholder:capitalize bg-white border border-slate-300/80 focus:border-blumine-500 focus:ring-2 focus:ring-blumine-500/50 focus:outline-none transition-all`,
3348
+ children: [
3349
+ /* @__PURE__ */ jsx("option", { selected: true, value: "", disabled: true, className: "capitalize", children: selectedOption }),
3350
+ optionData == null ? void 0 : optionData.map((option, index) => /* @__PURE__ */ jsxs(
3351
+ "option",
3381
3352
  {
3382
- title: `${category == null ? void 0 : category.label} (Click to edit)`,
3383
- onClick: (e) => {
3384
- e.stopPropagation();
3385
- const selection = window.getSelection();
3386
- if (!selection) return;
3387
- const range = document.createRange();
3388
- const textNode = e.currentTarget.firstChild;
3389
- if (textNode && textNode.nodeType === Node.TEXT_NODE) {
3390
- range.selectNodeContents(textNode);
3391
- } else {
3392
- range.selectNodeContents(e.currentTarget);
3393
- }
3394
- selection.removeAllRanges();
3395
- selection.addRange(range);
3396
- setTimeout(() => handleMouseUp(), 0);
3397
- },
3398
- className: "cursor-pointer font-bold transition-all hover:opacity-80 rounded-[3px]",
3399
- style: {
3400
- color: category == null ? void 0 : category.color,
3401
- padding: "0.125rem 0.25rem",
3402
- margin: "0 -0.125rem"
3403
- },
3404
- children: block.text.slice(actualStart, tag.end_index)
3353
+ value: option[valueProperty],
3354
+ disabled: option[disableOptionProperty],
3355
+ children: [
3356
+ option[labelProperty],
3357
+ label2propery ? brackets ? ` (${option[label2propery]})` : option[label2propery] : "",
3358
+ " ",
3359
+ option[disableOptionProperty] && `(${disabledOptionText})`
3360
+ ]
3405
3361
  },
3406
- tag.id || `tag-${actualStart}-${tag.end_index}`
3407
- )
3408
- );
3362
+ index
3363
+ ))
3364
+ ]
3409
3365
  }
3410
- currentIndex = Math.max(currentIndex, tag.end_index);
3411
- });
3412
- if (currentIndex < block.text.length) {
3413
- nodes.push(
3414
- /* @__PURE__ */ jsx("span", { children: block.text.slice(currentIndex) }, `text-${currentIndex}`)
3415
- );
3366
+ ),
3367
+ /* @__PURE__ */ jsx(
3368
+ ErrorMessage,
3369
+ {
3370
+ name,
3371
+ className: "text-red-500 text-sm mt-1",
3372
+ component: "div"
3373
+ }
3374
+ )
3375
+ ] });
3376
+ };
3377
+ var FormikInput = (_a) => {
3378
+ var _b = _a, {
3379
+ label,
3380
+ name,
3381
+ type,
3382
+ placeholder,
3383
+ className,
3384
+ labelClassName,
3385
+ readOnly,
3386
+ divClasses,
3387
+ length,
3388
+ disable,
3389
+ min = 0,
3390
+ max,
3391
+ convertToText = false,
3392
+ enableRedAsterick = false,
3393
+ case_sensitivity = "normal"
3394
+ } = _b, props = __objRest(_b, [
3395
+ "label",
3396
+ "name",
3397
+ "type",
3398
+ "placeholder",
3399
+ "className",
3400
+ "labelClassName",
3401
+ "readOnly",
3402
+ "divClasses",
3403
+ "length",
3404
+ "disable",
3405
+ "min",
3406
+ "max",
3407
+ "convertToText",
3408
+ "enableRedAsterick",
3409
+ "case_sensitivity"
3410
+ ]);
3411
+ const { setFieldValue } = useFormikContext();
3412
+ const handleCaseChange = (e) => {
3413
+ let value = e.target.value;
3414
+ if (type === "number" && min !== void 0) {
3415
+ if (value !== "" && Number(value) < min) return;
3416
3416
  }
3417
- return nodes;
3417
+ switch (case_sensitivity) {
3418
+ case "lowercase":
3419
+ value = value.toLowerCase();
3420
+ break;
3421
+ case "uppercase":
3422
+ value = value.toUpperCase();
3423
+ break;
3424
+ }
3425
+ setFieldValue(name, value);
3418
3426
  };
3419
- return /* @__PURE__ */ jsxs("div", { className: "relative p-8 md:p-12 mx-auto w-full min-h-screen flex flex-col gap-8 xl:pr-[20rem]", children: [
3420
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center max-w-6xl xl:max-w-full", children: [
3421
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 min-h-screen", children: [
3422
- /* @__PURE__ */ jsx(
3423
- "div",
3424
- {
3425
- className: "relative bg-[#fdfdfc] shadow-md shadow-zinc-300/60 border border-zinc-100 rounded-sm md:rounded-md pl-[1.5in] py-[1in] pr-[1in] flex flex-col w-[210mm] min-h-auto shrink-0",
3426
- style: {
3427
- fontFamily: COURIER_STACK,
3428
- paddingLeft: "1.5in",
3429
- paddingRight: "1in",
3430
- paddingTop: "1in",
3431
- paddingBottom: "1in",
3432
- lineHeight: "1.2"
3433
- },
3434
- "data-screenplay-editor": "true",
3435
- onMouseUp: handleMouseUp,
3436
- children: blocks.map((block) => /* @__PURE__ */ jsxs(
3437
- "div",
3438
- {
3439
- "data-block-id": block.id,
3440
- className: `relative break-words w-full px-4 py-2 ${blockStyles[block.type].className}`,
3441
- style: __spreadProps(__spreadValues({}, blockStyles[block.type].inputStyle), {
3442
- minHeight: "2.5rem"
3443
- }),
3444
- children: [
3445
- renderBlockText(block),
3446
- (selectionMenu == null ? void 0 : selectionMenu.blockId) === block.id && /* @__PURE__ */ jsxs(
3447
- "div",
3448
- {
3449
- ref: menuRef,
3450
- "data-screenplay-editor": "false",
3451
- className: `tag-menu absolute z-50 bg-white/70 backdrop-blur-2xl shadow-[0_10px_40px_rgb(0,0,0,0.06)] border border-white rounded-[1.5rem] p-2 flex flex-col w-56 animate-in fade-in zoom-in-95 duration-300 ease-out ${menuPlacement === "top" ? "origin-bottom" : "origin-top"}`,
3452
- style: {
3453
- top: selectionMenu.top,
3454
- left: selectionMenu.left,
3455
- transform: menuPlacement === "top" ? "translate(-50%, calc(-100% - 12px))" : "translate(-50%, 32px)"
3456
- },
3457
- children: [
3458
- /* @__PURE__ */ jsxs("div", { className: "relative z-10 px-3 py-2.5 border-b border-white/60 mb-1.5", children: [
3459
- /* @__PURE__ */ jsx("p", { className: "text-[9px] font-extrabold tracking-[0.2em] text-slate-400 uppercase mb-1", children: "Tag Element" }),
3460
- /* @__PURE__ */ jsxs(
3461
- "p",
3462
- {
3463
- className: "text-xs font-bold text-slate-700 truncate drop-shadow-sm",
3464
- title: selectionMenu.text,
3465
- children: [
3466
- '"',
3467
- selectionMenu.text,
3468
- '"'
3469
- ]
3470
- }
3471
- )
3472
- ] }),
3473
- /* @__PURE__ */ jsxs("div", { className: "relative z-10 flex flex-col gap-1", children: [
3474
- CATEGORIES.filter(
3475
- (cat) => !(cat.id === "LOCATION" && hasLocationTag) && !(cat.id === "SUBLOCATION" && !hasLocationTag)
3476
- ).map((cat) => /* @__PURE__ */ jsxs(
3477
- "button",
3478
- {
3479
- onClick: () => {
3480
- const existingTag = tags.find(
3481
- (t) => t.block_id === block.id && t.start_index === selectionMenu.startIndex && t.end_index === selectionMenu.endIndex
3482
- );
3483
- if (existingTag && existingTag.id) {
3484
- updateTag == null ? void 0 : updateTag(existingTag.id, cat.id);
3485
- } else {
3486
- addTag(cat.id);
3487
- }
3488
- },
3489
- className: "group w-full text-[12px] font-bold px-3 py-2 rounded-xl transition-all duration-300 text-left flex items-center justify-between hover:bg-white/80 hover:shadow-[0_2px_10px_rgb(0,0,0,0.02)] active:scale-[0.98]",
3490
- style: { color: cat.color },
3491
- children: [
3492
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
3493
- /* @__PURE__ */ jsx(
3494
- cat.icon,
3495
- {
3496
- className: "w-4 h-4 group-hover:scale-110 transition-transform duration-300",
3497
- style: { color: cat.color }
3498
- }
3499
- ),
3500
- cat.label
3501
- ] }),
3502
- /* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold text-slate-400 opacity-0 group-hover:opacity-100 transition-all duration-300 translate-x-1 group-hover:translate-x-0", children: "Select" })
3503
- ]
3504
- },
3505
- cat.id
3506
- )),
3507
- tags.some(
3508
- (t) => t.block_id === block.id && t.start_index === selectionMenu.startIndex && t.end_index === selectionMenu.endIndex
3509
- ) && /* @__PURE__ */ jsx("div", { className: "mt-1 pt-1 border-t border-white/60", children: /* @__PURE__ */ jsxs(
3510
- "button",
3511
- {
3512
- onClick: (e) => {
3513
- const tagToRemove = tags.find(
3514
- (t) => t.block_id === block.id && t.start_index === selectionMenu.startIndex && t.end_index === selectionMenu.endIndex
3515
- );
3516
- if (tagToRemove) {
3517
- removeTag(e, tagToRemove.id);
3518
- clearSelection();
3519
- }
3520
- },
3521
- className: "group w-full text-[12px] font-bold px-3 py-2 rounded-xl transition-all duration-300 text-left flex items-center justify-between hover:bg-rose-50 hover:text-rose-600 hover:shadow-[0_2px_10px_rgb(225,29,72,0.04)] active:scale-[0.98] text-slate-500 border border-transparent hover:border-rose-100",
3522
- children: [
3523
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
3524
- /* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full shadow-sm bg-rose-400 group-hover:scale-125 transition-transform duration-300" }),
3525
- "Remove Tag"
3526
- ] }),
3527
- /* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold text-rose-400 opacity-0 group-hover:opacity-100 transition-all duration-300 translate-x-1 group-hover:translate-x-0", children: "Remove" })
3528
- ]
3529
- }
3530
- ) })
3531
- ] })
3532
- ]
3533
- }
3534
- )
3535
- ]
3536
- },
3537
- block.id
3538
- ))
3539
- }
3540
- ),
3541
- /* @__PURE__ */ jsxs("div", { className: "relative bg-[#fdfdfc] shadow-md shadow-zinc-300/60 border border-zinc-100 rounded-sm md:rounded-md flex flex-col w-[210mm] shrink-0 p-8 md:p-12 lg:p-16", children: [
3542
- /* @__PURE__ */ jsxs("h3", { className: "text-xs font-extrabold text-slate-800 uppercase tracking-[0.25em] mb-6 flex items-center gap-3 font-sans", children: [
3543
- /* @__PURE__ */ jsx("span", { className: "flex items-center justify-center w-8 h-8 rounded-full bg-slate-100/80 shadow-inner border border-slate-200/50", children: /* @__PURE__ */ jsx(AlignLeft, { className: "w-3.5 h-3.5 text-slate-500" }) }),
3544
- "Scene Brief"
3545
- ] }),
3546
- /* @__PURE__ */ jsxs("div", { className: "relative bg-zinc-50/50 border border-zinc-200/60 rounded-xl p-4 md:p-6 shadow-inner focus-within:bg-white focus-within:border-zinc-300 focus-within:shadow-[0_8px_30px_rgb(0,0,0,0.04)] transition-all duration-300", children: [
3547
- /* @__PURE__ */ jsx(
3548
- "textarea",
3549
- {
3550
- value: sceneBrief,
3551
- onChange: (e) => setSceneBrief(e.target.value),
3552
- placeholder: "Write a brief description or notes for this scene...",
3553
- className: "w-full min-h-[120px] bg-transparent outline-none resize-y text-zinc-700 placeholder:text-zinc-400 text-sm md:text-base custom-scrollbar font-sans select-none",
3554
- style: {
3555
- lineHeight: "1.6"
3556
- }
3557
- }
3558
- ),
3559
- /* @__PURE__ */ jsx("div", { className: "mt-4 flex justify-end", children: /* @__PURE__ */ jsx(
3560
- "button",
3561
- {
3562
- onClick: onUpdateBrief,
3563
- disabled: !sceneBrief || sceneBrief.trim() === "",
3564
- className: "rounded-full bg-[#15607b] px-6 py-3 text-[13px] font-semibold tracking-wide text-white shadow-md transition-all hover:bg-[#134a61] hover:shadow-lg active:scale-95 disabled:bg-gray-200 disabled:cursor-not-allowed disabled:opacity-95 disabled:text-black disabled:shadow-none",
3565
- children: "Update"
3566
- }
3567
- ) })
3568
- ] })
3569
- ] })
3570
- ] }),
3571
- /* @__PURE__ */ jsxs(
3572
- "button",
3573
- {
3574
- onClick: () => setIsSidebarOpen(true),
3575
- className: "fixed bottom-6 right-6 z-40 flex h-14 w-14 items-center justify-center rounded-full bg-[#15607b] text-white shadow-[0_8px_30px_rgba(21,96,123,0.3)] transition-transform hover:scale-105 active:scale-95 xl:hidden",
3576
- children: [
3577
- /* @__PURE__ */ jsx(Tags, { className: "h-6 w-6" }),
3578
- tags.length > 0 && /* @__PURE__ */ jsx("span", { className: "absolute -top-1 -right-1 flex h-5 w-5 items-center justify-center rounded-full bg-rose-500 text-[10px] font-bold text-white shadow-sm border-2 border-white", children: tags.length })
3579
- ]
3580
- }
3581
- ),
3582
- isSidebarOpen && /* @__PURE__ */ jsx(
3583
- "div",
3584
- {
3585
- className: "fixed inset-0 z-40 bg-slate-900/20 backdrop-blur-sm transition-opacity xl:hidden",
3586
- onClick: () => setIsSidebarOpen(false)
3587
- }
3588
- ),
3589
- /* @__PURE__ */ jsx(
3590
- "div",
3591
- {
3592
- className: `fixed xl:absolute top-0 right-0 z-50 h-full w-72 transform transition-transform duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] xl:translate-x-0 ${isSidebarOpen ? "translate-x-0 shadow-[0_0_40px_rgba(0,0,0,0.1)]" : "translate-x-full"}`,
3593
- children: /* @__PURE__ */ jsxs("div", { className: "sticky top-0 flex h-[100dvh] max-h-screen w-full flex-col border-l border-l-[#eefafd] bg-white p-3 py-5", children: [
3594
- !aiSummarized && /* @__PURE__ */ jsx(
3595
- summarize_button_default,
3596
- {
3597
- isSummarizing,
3598
- onSummarize
3599
- }
3600
- ),
3601
- /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col gap-3 py-5 my-5 border-t border-t-[#eefafd] flex-1 overflow-hidden", children: [
3602
- /* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-between mb-6 shrink-0", children: [
3603
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
3604
- /* @__PURE__ */ jsx("div", { className: "relative flex h-8 w-8 items-center justify-center rounded-[10px] border border-[#15607b]/20 bg-white shadow-sm", children: /* @__PURE__ */ jsx(Tags, { className: "h-4 w-4 text-[#15607b]" }) }),
3605
- /* @__PURE__ */ jsx("h3", { className: "text-[12px] font-semibold uppercase tracking-[0.28em] text-[#134a61]", children: "Breakdown" })
3606
- ] }),
3607
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx("span", { className: "rounded-full border border-slate-200/60 bg-slate-50 px-2.5 py-1 text-[11px] font-semibold text-[#134a61] shadow-sm backdrop-blur-md", children: tags.length }) })
3608
- ] }),
3609
- /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-4 overflow-y-auto overflow-x-hidden pb-4 pr-1 custom-scrollbar", children: CATEGORIES.map((cat) => {
3610
- const catTags = tags.filter((t) => t.category_id === cat.id);
3611
- if (!catTags.length) return null;
3612
- const isExpanded = expandedCategories[cat.id];
3613
- const uniqueTags = Array.from(
3614
- new Map(
3615
- catTags.map((tag) => [tag.name.toLowerCase(), tag])
3616
- ).values()
3617
- );
3618
- return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
3619
- /* @__PURE__ */ jsxs(
3620
- "div",
3621
- {
3622
- onClick: () => setExpandedCategories((prev) => ({
3623
- [cat.id]: !prev[cat.id]
3624
- })),
3625
- className: "group relative flex cursor-pointer items-center gap-3 overflow-hidden rounded-[24px] border border-white/70 px-4 py-3.5 transition-all duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] hover:-translate-y-[1px]",
3626
- style: {
3627
- background: `linear-gradient(135deg, rgba(255,255,255,0.92), ${cat.hex}18)`,
3628
- boxShadow: `0 16px 36px ${cat.hex}14, inset 0 1px 0 rgba(255,255,255,0.9)`,
3629
- border: `1px solid ${cat.hex}50`
3630
- },
3631
- children: [
3632
- /* @__PURE__ */ jsx(
3633
- "div",
3634
- {
3635
- className: "absolute inset-0 opacity-0 transition-opacity duration-300 group-hover:opacity-100",
3636
- style: {
3637
- background: `radial-gradient(circle at 15% 20%, ${cat.hex}24, transparent 38%)`
3638
- }
3639
- }
3640
- ),
3641
- /* @__PURE__ */ jsx(
3642
- "div",
3643
- {
3644
- className: "relative flex h-9 w-9 shrink-0 items-center justify-center rounded-xl border border-white/70 backdrop-blur-md",
3645
- style: {
3646
- background: `linear-gradient(135deg, ${cat.color}22, rgba(255,255,255,0.75))`,
3647
- boxShadow: `0 10px 22px ${cat.color}14`
3648
- },
3649
- children: /* @__PURE__ */ jsx(cat.icon, { className: "h-4 w-4", color: cat.color })
3650
- }
3651
- ),
3652
- /* @__PURE__ */ jsx(
3653
- "span",
3654
- {
3655
- className: "relative text-[13px] font-semibold tracking-[0.01em]",
3656
- style: { color: cat.color },
3657
- children: cat.label
3658
- }
3659
- ),
3660
- /* @__PURE__ */ jsx("span", { className: "relative ml-auto rounded-full border border-white/70 bg-white/75 px-2.5 py-1 text-[11px] font-semibold text-[#456575] backdrop-blur-md", children: catTags.length }),
3661
- /* @__PURE__ */ jsx(
3662
- ChevronDown,
3663
- {
3664
- className: `relative h-4 w-4 transition-transform duration-500 ease-[cubic-bezier(0.22,1,0.36,1)] ${isExpanded ? "rotate-180" : ""}`,
3665
- style: { color: cat.color }
3666
- }
3667
- )
3668
- ]
3669
- }
3670
- ),
3671
- /* @__PURE__ */ jsx(
3672
- "div",
3673
- {
3674
- className: `grid transition-all duration-500 ease-[cubic-bezier(0.22,1,0.36,1)] ${isExpanded ? "grid-rows-[1fr] opacity-100 mt-3" : "grid-rows-[0fr] opacity-0"}`,
3675
- children: /* @__PURE__ */ jsx("div", { className: "overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap justify-start gap-2 py-2", children: uniqueTags.map((tag, index) => /* @__PURE__ */ jsx(
3676
- "div",
3677
- {
3678
- className: "relative flex flex-col items-start",
3679
- children: /* @__PURE__ */ jsxs(
3680
- "span",
3681
- {
3682
- title: tag.name,
3683
- onClick: (e) => {
3684
- e.stopPropagation();
3685
- if (cat.id !== "PROP" && cat.id !== "SET_PROP" && cat.id !== "CAST")
3686
- return;
3687
- if ((editingTagData == null ? void 0 : editingTagData.tag.id) === tag.id) {
3688
- setEditingTagData(null);
3689
- } else {
3690
- const rect = e.currentTarget.getBoundingClientRect();
3691
- setPopupPlacement({
3692
- alignRight: rect.left > window.innerWidth - 240,
3693
- alignBottom: rect.bottom > window.innerHeight - 250
3694
- });
3695
- setTagForm({
3696
- quantity: tag.quantity || 1,
3697
- look: tag.look || "",
3698
- age: tag.age || ""
3699
- });
3700
- setEditingTagData({
3701
- tag,
3702
- catId: cat.id,
3703
- catColor: cat.color,
3704
- rect
3705
- });
3706
- }
3707
- },
3708
- className: `inline-block max-w-full truncate text-[11px] font-semibold px-3 py-1.5 rounded-full border backdrop-blur-md transition-all duration-300 ${cat.id === "PROP" || cat.id === "SET_PROP" || cat.id === "CAST" ? "cursor-pointer hover:scale-105 shadow-sm" : ""}`,
3709
- style: {
3710
- color: cat.color,
3711
- background: `linear-gradient(145deg, ${cat.color}18, rgba(255,255,255,0.88))`,
3712
- borderColor: `${cat.color}28`
3713
- },
3714
- children: [
3715
- tag.name,
3716
- (cat.id === "PROP" || cat.id === "SET_PROP") && tag.quantity && tag.quantity > 1 ? /* @__PURE__ */ jsxs("span", { className: "ml-1.5 opacity-80 font-bold", children: [
3717
- "x",
3718
- tag.quantity
3719
- ] }) : null
3720
- ]
3721
- }
3722
- )
3723
- },
3724
- index
3725
- )) }) })
3726
- }
3727
- )
3728
- ] }, cat.id);
3729
- }) })
3730
- ] })
3731
- ] })
3732
- }
3733
- )
3734
- ] }),
3735
- editingTagData && /* @__PURE__ */ jsxs(Fragment, { children: [
3427
+ return /* @__PURE__ */ jsxs("div", { className: `w-full ${divClasses}`, children: [
3428
+ /* @__PURE__ */ jsxs(
3429
+ "label",
3430
+ {
3431
+ className: `${labelClassName} mb-1.5 text-sm font-medium text-slate-700 capitalize flex`,
3432
+ children: [
3433
+ label,
3434
+ enableRedAsterick && /* @__PURE__ */ jsx(AsteriskIcon, { className: "size-3 text-red-500" })
3435
+ ]
3436
+ }
3437
+ ),
3438
+ /* @__PURE__ */ jsx(
3439
+ Field,
3440
+ __spreadProps(__spreadValues({
3441
+ name,
3442
+ placeholder,
3443
+ readOnly,
3444
+ type,
3445
+ maxLength: length,
3446
+ disabled: disable,
3447
+ max,
3448
+ min,
3449
+ onChange: handleCaseChange
3450
+ }, props), {
3451
+ className: `${className} rounded-[2.5rem] block w-full px-3 py-2.5 text-slate-700 placeholder-slate-400 placeholder:capitalize bg-white border border-slate-300/80 focus:border-blumine-500 focus:ring-2 focus:ring-blumine-500/50 focus:outline-none transition-all disabled:opacity-70`
3452
+ })
3453
+ ),
3454
+ /* @__PURE__ */ jsx(
3455
+ ErrorMessage,
3456
+ {
3457
+ name,
3458
+ className: "text-red-500 text-sm mt-1",
3459
+ component: "div"
3460
+ }
3461
+ )
3462
+ ] });
3463
+ };
3464
+ var FormikTextarea = ({
3465
+ label,
3466
+ name,
3467
+ placeholder,
3468
+ readOnly,
3469
+ divClasses = "",
3470
+ className = "",
3471
+ length,
3472
+ disable,
3473
+ min,
3474
+ max,
3475
+ enableRedAsterick = false
3476
+ }) => {
3477
+ return /* @__PURE__ */ jsxs("div", { className: `w-full ${divClasses}`, children: [
3478
+ /* @__PURE__ */ jsxs(
3479
+ "label",
3480
+ {
3481
+ className: `mb-1.5 text-sm font-medium text-slate-700 capitalize flex`,
3482
+ children: [
3483
+ label,
3484
+ enableRedAsterick && /* @__PURE__ */ jsx(AsteriskIcon, { className: "size-3 text-red-500" })
3485
+ ]
3486
+ }
3487
+ ),
3488
+ /* @__PURE__ */ jsx(
3489
+ Field,
3490
+ {
3491
+ as: "textarea",
3492
+ name,
3493
+ placeholder,
3494
+ readOnly,
3495
+ maxLength: length,
3496
+ disabled: disable,
3497
+ max,
3498
+ min,
3499
+ className: `${className} block w-full px-4 py-3 text-slate-700 placeholder-slate-400 placeholder:capitalize bg-white border border-slate-300/80 rounded-2xl focus:border-blumine-500 focus:ring-2 focus:ring-blumine-500/50 focus:outline-none transition-all disabled:opacity-70`
3500
+ }
3501
+ ),
3502
+ /* @__PURE__ */ jsx(
3503
+ ErrorMessage,
3504
+ {
3505
+ name,
3506
+ className: "text-red-500 text-sm mt-1",
3507
+ component: "div"
3508
+ }
3509
+ )
3510
+ ] });
3511
+ };
3512
+ var Switch = ({
3513
+ isOn,
3514
+ handleToggle,
3515
+ activeColor = "bg-green-500",
3516
+ inactiveColor = "bg-gray-300",
3517
+ size = "medium",
3518
+ onLabel,
3519
+ offLabel,
3520
+ label,
3521
+ divClasses,
3522
+ labelClasses,
3523
+ disable = false
3524
+ }) => {
3525
+ const sizeClasses = {
3526
+ small: { width: 28, height: 18, handle: 12, labelSize: 12, gap: 4 },
3527
+ medium: { width: 48, height: 28, handle: 20, labelSize: 16, gap: 6 },
3528
+ large: { width: 64, height: 36, handle: 28, labelSize: 20, gap: 10 }
3529
+ };
3530
+ const selectedSize = sizeClasses[size] || sizeClasses.medium;
3531
+ return /* @__PURE__ */ jsxs("div", { className: `w-full ${divClasses}`, children: [
3532
+ label && /* @__PURE__ */ jsx(
3533
+ "label",
3534
+ {
3535
+ className: `block mb-2 text-sm text-gray-600 ${disable && "text-gray-400!"} ${labelClasses}`,
3536
+ children: label
3537
+ }
3538
+ ),
3539
+ /* @__PURE__ */ jsxs("div", { style: { gap: selectedSize.gap }, className: "flex items-center", children: [
3736
3540
  /* @__PURE__ */ jsx(
3737
3541
  "div",
3738
3542
  {
3739
- className: "fixed inset-0 z-[100]",
3740
- onClick: (e) => {
3741
- e.stopPropagation();
3742
- setEditingTagData(null);
3743
- }
3744
- }
3745
- ),
3746
- /* @__PURE__ */ jsxs(
3747
- "div",
3748
- {
3749
- className: `p-3.5 bg-white/90 backdrop-blur-2xl rounded-[1.25rem] shadow-[0_10px_40px_rgb(0,0,0,0.12)] border border-white/60 z-[101] w-56 animate-in fade-in zoom-in-95 duration-200 ease-out fixed`,
3543
+ className: `flex items-center rounded-full cursor-pointer relative ${isOn ? activeColor : inactiveColor} ${disable && "opacity-50 cursor-not-allowed"}`,
3544
+ onClick: disable ? () => {
3545
+ } : handleToggle,
3546
+ role: "button",
3547
+ "aria-pressed": isOn,
3750
3548
  style: {
3751
- top: popupPlacement.alignBottom ? "auto" : editingTagData.rect.bottom + 8,
3752
- bottom: popupPlacement.alignBottom ? window.innerHeight - editingTagData.rect.top + 8 : "auto",
3753
- left: popupPlacement.alignRight ? "auto" : editingTagData.rect.left,
3754
- right: popupPlacement.alignRight ? window.innerWidth - editingTagData.rect.right : "auto"
3549
+ width: selectedSize.width,
3550
+ height: selectedSize.height
3755
3551
  },
3756
- onClick: (e) => e.stopPropagation(),
3757
- children: [
3758
- /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center mb-3 border-b border-slate-100 pb-2.5", children: [
3759
- /* @__PURE__ */ jsx("span", { className: "text-[10px] font-extrabold text-slate-400 uppercase tracking-widest drop-shadow-sm", children: "Details" }),
3760
- /* @__PURE__ */ jsx(
3761
- "button",
3762
- {
3763
- onClick: () => setEditingTagData(null),
3764
- className: "hover:bg-slate-100/80 p-1.5 rounded-full transition-colors active:scale-95",
3765
- children: /* @__PURE__ */ jsx(X, { className: "w-3.5 h-3.5 text-slate-500" })
3766
- }
3767
- )
3768
- ] }),
3769
- (editingTagData.catId === "PROP" || editingTagData.catId === "SET_PROP") && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
3770
- /* @__PURE__ */ jsx("label", { className: "text-[11px] font-bold text-slate-600 ml-1", children: "Quantity" }),
3771
- /* @__PURE__ */ jsx(
3772
- "input",
3773
- {
3774
- type: "number",
3775
- min: "1",
3776
- value: tagForm.quantity,
3777
- onChange: (e) => {
3778
- const val = Math.max(1, parseInt(e.target.value) || 1);
3779
- setTagForm((prev) => __spreadProps(__spreadValues({}, prev), {
3780
- quantity: val
3781
- }));
3782
- },
3783
- className: "w-full text-[13px] px-3 py-2 rounded-xl border border-slate-200/80 outline-none focus:border-[#15607b] focus:ring-2 focus:ring-[#15607b]/20 transition-all bg-slate-50/50 focus:bg-white font-semibold text-slate-700"
3784
- }
3785
- )
3786
- ] }),
3787
- editingTagData.catId === "CAST" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
3788
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
3789
- /* @__PURE__ */ jsx("label", { className: "text-[11px] font-bold text-slate-600 ml-1", children: "Look" }),
3790
- /* @__PURE__ */ jsx(
3791
- "input",
3792
- {
3793
- type: "text",
3794
- placeholder: "e.g. Dirty, Formal",
3795
- value: tagForm.look,
3796
- onChange: (e) => {
3797
- setTagForm((prev) => __spreadProps(__spreadValues({}, prev), {
3798
- look: e.target.value
3799
- }));
3800
- },
3801
- className: "w-full text-[13px] px-3 py-2 rounded-xl border border-slate-200/80 outline-none focus:border-[#15607b] focus:ring-2 focus:ring-[#15607b]/20 transition-all bg-slate-50/50 focus:bg-white font-semibold text-slate-700 placeholder:text-slate-400 placeholder:font-normal"
3802
- }
3803
- )
3804
- ] }),
3805
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
3806
- /* @__PURE__ */ jsx("label", { className: "text-[11px] font-bold text-slate-600 ml-1", children: "Age" }),
3552
+ children: /* @__PURE__ */ jsx(
3553
+ "div",
3554
+ {
3555
+ className: "bg-white rounded-full shadow-sm ease-in duration-200",
3556
+ style: {
3557
+ width: selectedSize.handle,
3558
+ height: selectedSize.handle,
3559
+ transform: `translateX(${isOn ? selectedSize.width - selectedSize.handle - 3 : 3}px)`,
3560
+ transition: "transform 0.2s ease-in-out"
3561
+ }
3562
+ }
3563
+ )
3564
+ }
3565
+ ),
3566
+ onLabel && offLabel && /* @__PURE__ */ jsx("span", { style: { fontSize: selectedSize.labelSize }, children: isOn ? onLabel : offLabel })
3567
+ ] })
3568
+ ] });
3569
+ };
3570
+ var MultiSelect = ({
3571
+ label,
3572
+ options,
3573
+ value,
3574
+ onChange,
3575
+ placeholder = "Select options...",
3576
+ valueProperty = "name",
3577
+ labelProperty = "name",
3578
+ divClasses = "",
3579
+ returnObjects = false
3580
+ }) => {
3581
+ const [isOpen, setIsOpen] = React4.useState(false);
3582
+ const containerRef = React4.useRef(null);
3583
+ const isSelected = (optionValue) => {
3584
+ return value.some(
3585
+ (v) => typeof v === "object" && v !== null ? v[valueProperty] === optionValue : v === optionValue
3586
+ );
3587
+ };
3588
+ const toggleOption = (optionValue) => {
3589
+ let newValues;
3590
+ if (isSelected(optionValue)) {
3591
+ newValues = value.filter(
3592
+ (v) => typeof v === "object" && v !== null ? v[valueProperty] !== optionValue : v !== optionValue
3593
+ );
3594
+ } else {
3595
+ const newValue = returnObjects ? options.find((opt) => opt[valueProperty] === optionValue) : optionValue;
3596
+ newValues = [...value, newValue];
3597
+ }
3598
+ onChange(newValues);
3599
+ };
3600
+ const removeOption = (e, optionValue) => {
3601
+ e.stopPropagation();
3602
+ const newValues = value.filter(
3603
+ (v) => typeof v === "object" && v !== null ? v[valueProperty] !== optionValue : v !== optionValue
3604
+ );
3605
+ onChange(newValues);
3606
+ };
3607
+ React4.useEffect(() => {
3608
+ const handleClickOutside = (event) => {
3609
+ if (containerRef.current && !containerRef.current.contains(event.target)) {
3610
+ setIsOpen(false);
3611
+ }
3612
+ };
3613
+ document.addEventListener("mousedown", handleClickOutside);
3614
+ return () => document.removeEventListener("mousedown", handleClickOutside);
3615
+ }, []);
3616
+ return /* @__PURE__ */ jsxs("div", { className: `w-full relative ${divClasses}`, ref: containerRef, children: [
3617
+ label && /* @__PURE__ */ jsx("label", { className: "mb-1.5 text-xs font-bold text-slate-600 ml-1 uppercase tracking-wider", children: label }),
3618
+ /* @__PURE__ */ jsx(
3619
+ "div",
3620
+ {
3621
+ onClick: () => setIsOpen(!isOpen),
3622
+ className: `min-h-[40px] w-full px-3 py-1.5 bg-slate-50/50 border border-slate-200/80 rounded-xl transition-all cursor-pointer flex flex-wrap gap-1.5 items-center ${isOpen ? "border-blumine-500 bg-white shadow-sm ring-2 ring-blumine-500/10" : "hover:border-slate-300"}`,
3623
+ children: value.length === 0 ? /* @__PURE__ */ jsx("span", { className: "text-slate-400 text-[13px] ml-1", children: placeholder }) : value.map((val) => {
3624
+ const actualValue = typeof val === "object" && val !== null ? val[valueProperty] : val;
3625
+ const option = options.find(
3626
+ (opt) => opt[valueProperty] === actualValue
3627
+ );
3628
+ return /* @__PURE__ */ jsxs(
3629
+ "div",
3630
+ {
3631
+ className: "flex items-center gap-1 bg-white text-slate-700 px-2 py-0.5 rounded-lg border border-slate-200 text-[11px] font-bold shadow-sm animate-in zoom-in-95 duration-150",
3632
+ children: [
3633
+ option ? option[labelProperty] : actualValue,
3807
3634
  /* @__PURE__ */ jsx(
3808
- "input",
3635
+ "button",
3809
3636
  {
3810
- type: "number",
3811
- min: "0",
3812
- max: "100",
3813
- value: tagForm.age,
3814
- onChange: (e) => {
3815
- let val = parseInt(e.target.value);
3816
- if (isNaN(val)) {
3817
- setTagForm((prev) => __spreadProps(__spreadValues({}, prev), {
3818
- age: ""
3819
- }));
3820
- return;
3821
- }
3822
- val = Math.max(0, Math.min(100, val));
3823
- setTagForm((prev) => __spreadProps(__spreadValues({}, prev), {
3824
- age: val.toString()
3825
- }));
3826
- },
3827
- className: "w-full text-[13px] px-3 py-2 rounded-xl border border-slate-200/80 outline-none focus:border-[#15607b] focus:ring-2 focus:ring-[#15607b]/20 transition-all bg-slate-50/50 focus:bg-white font-semibold text-slate-700"
3637
+ type: "button",
3638
+ onClick: (e) => removeOption(e, actualValue),
3639
+ className: "hover:text-red-500 transition-colors ml-0.5",
3640
+ children: "\xD7"
3828
3641
  }
3829
3642
  )
3830
- ] })
3831
- ] }),
3832
- /* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(
3833
- "button",
3834
- {
3835
- onClick: (e) => {
3836
- e.stopPropagation();
3837
- if (editingTagData.tag.id) {
3838
- updateTag == null ? void 0 : updateTag(editingTagData.tag.id, editingTagData.catId, __spreadValues(__spreadValues({}, editingTagData.catId === "PROP" || editingTagData.catId === "SET_PROP" ? { quantity: tagForm.quantity } : {}), editingTagData.catId === "CAST" ? { look: tagForm.look, age: tagForm.age } : {}));
3839
- }
3840
- setEditingTagData(null);
3841
- },
3842
- className: "w-full bg-[#15607b] hover:bg-[#134a61] text-white text-[12px] font-bold py-2.5 rounded-xl transition-all shadow-[0_4px_12px_rgba(21,96,123,0.2)] hover:shadow-[0_6px_16px_rgba(21,96,123,0.3)] active:scale-[0.98]",
3843
- children: "Update Details"
3844
- }
3845
- ) })
3846
- ]
3847
- }
3848
- )
3849
- ] })
3643
+ ]
3644
+ },
3645
+ actualValue
3646
+ );
3647
+ })
3648
+ }
3649
+ ),
3650
+ isOpen && /* @__PURE__ */ jsx("div", { className: "absolute z-[110] w-full mt-1.5 bg-white border border-slate-200 rounded-xl shadow-xl max-h-48 overflow-y-auto animate-in fade-in slide-in-from-top-1 duration-200 custom-scrollbar", children: /* @__PURE__ */ jsx("div", { className: "p-1", children: options.filter((opt) => !isSelected(opt[valueProperty])).length > 0 ? options.filter((opt) => !isSelected(opt[valueProperty])).map((option, index) => {
3651
+ return /* @__PURE__ */ jsx(
3652
+ "div",
3653
+ {
3654
+ onClick: () => toggleOption(option[valueProperty]),
3655
+ className: "px-3 py-2 text-[12px] rounded-lg cursor-pointer transition-colors flex items-center justify-between text-slate-600 hover:bg-slate-50",
3656
+ children: option[labelProperty]
3657
+ },
3658
+ index
3659
+ );
3660
+ }) : /* @__PURE__ */ jsx("div", { className: "px-3 py-3 text-[10px] text-slate-400 text-center font-bold uppercase tracking-wider", children: "All selected" }) }) })
3850
3661
  ] });
3851
- }
3852
- function useScriptBreakdownScene(options) {
3853
- const [tags, setTags] = useState(options.preLoadedTags || []);
3854
- const [selectionMenu, setSelectionMenu] = useState(null);
3855
- const autoTaggedSceneRef = useRef(null);
3856
- const [scene, setScene] = useState(null);
3857
- const [menuPlacement, setMenuPlacement] = useState("top");
3858
- const [sceneBrief, setSceneBrief] = useState("");
3859
- const [isSummarizing, setIsSummarizing] = useState(false);
3860
- const [isLoading, setIsLoading] = useState(true);
3861
- const [error, setError] = useState(false);
3862
- const menuRef = useRef(null);
3662
+ };
3663
+ function ScriptBreakdownSceneView({
3664
+ blocks,
3665
+ characters,
3666
+ isLoading,
3667
+ sceneNumber,
3668
+ tags,
3669
+ selectionMenu,
3670
+ handleMouseUp,
3671
+ addTag,
3672
+ updateTag,
3673
+ removeTag,
3674
+ clearSelection,
3675
+ menuPlacement,
3676
+ menuRef,
3677
+ sceneBrief,
3678
+ setSceneBrief,
3679
+ onSummarize,
3680
+ isSummarizing,
3681
+ aiSummarized = false,
3682
+ onUpdateBrief
3683
+ }) {
3684
+ const [expandedCategories, setExpandedCategories] = useState({});
3685
+ const [editingTagData, setEditingTagData] = useState(null);
3686
+ const [tagForm, setTagForm] = useState({ quantity: 1, look: "", age: "", age_range: [], crowd_type: [] });
3687
+ const [popupPlacement, setPopupPlacement] = useState({
3688
+ alignRight: false,
3689
+ alignBottom: false
3690
+ });
3691
+ const [isSidebarOpen, setIsSidebarOpen] = useState(false);
3692
+ const COURIER_STACK = "'Courier Prime', 'Courier', monospace";
3863
3693
  useEffect(() => {
3864
- setIsLoading(true);
3865
- const fetchScene = async () => {
3866
- try {
3867
- const response = await fetch(options.scene_url, options.fetchOptions);
3868
- if (response.ok) {
3869
- const text = await response.text();
3870
- setScene({ content: text });
3871
- } else {
3872
- console.error("Failed to fetch scene:", response);
3873
- setError(true);
3694
+ const fontId = "google-font-courier-prime";
3695
+ const styleId = "screenplay-editor-force-v4";
3696
+ if (!document.getElementById(fontId)) {
3697
+ const link = document.createElement("link");
3698
+ link.id = fontId;
3699
+ link.rel = "stylesheet";
3700
+ link.href = "https://fonts.googleapis.com/css2?family=Courier+Prime:ital,wght@0,400;0,700;1,400;1,700&display=swap";
3701
+ document.head.appendChild(link);
3702
+ }
3703
+ if (!document.getElementById(styleId)) {
3704
+ const style = document.createElement("style");
3705
+ style.id = styleId;
3706
+ style.textContent = `
3707
+ /* We target by the data-attribute to ensure the highest specificity possible */
3708
+ [data-screenplay-editor] *,
3709
+ [data-screenplay-editor] div,
3710
+ [data-screenplay-editor] span,
3711
+ [data-screenplay-editor] [contenteditable="true"] {
3712
+ font-family: ${COURIER_STACK} !important;
3713
+ -webkit-font-smoothing: antialiased;
3874
3714
  }
3875
- setIsLoading(false);
3876
- } catch (error2) {
3877
- setError(true);
3878
- setIsLoading(false);
3879
- console.error("Error fetching scene:", error2);
3715
+ `;
3716
+ document.head.appendChild(style);
3717
+ }
3718
+ }, [COURIER_STACK]);
3719
+ if (isLoading) {
3720
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-32 gap-4", children: [
3721
+ /* @__PURE__ */ jsx(Loader2, { className: "w-8 h-8 animate-spin text-zinc-400" }),
3722
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-zinc-500 animate-pulse", children: "Loading scene details..." })
3723
+ ] });
3724
+ }
3725
+ const hasLocationTag = tags.some((t) => t.category_id === "LOCATION");
3726
+ const renderBlockText = (block) => {
3727
+ const blockTags = tags.filter((t) => t.block_id === block.id).sort((a, b) => a.start_index - b.start_index);
3728
+ if (blockTags.length === 0) return block.text;
3729
+ const nodes = [];
3730
+ let currentIndex = 0;
3731
+ blockTags.forEach((tag) => {
3732
+ const actualStart = Math.max(tag.start_index, currentIndex);
3733
+ if (actualStart > currentIndex) {
3734
+ nodes.push(
3735
+ /* @__PURE__ */ jsx("span", { children: block.text.slice(currentIndex, actualStart) }, `text-${currentIndex}`)
3736
+ );
3880
3737
  }
3881
- };
3882
- fetchScene();
3883
- }, []);
3884
- const blocks = useMemo(() => {
3885
- if (!scene || !scene.content) return [];
3886
- const parser = new DOMParser();
3887
- const doc = parser.parseFromString(scene.content, "text/html");
3888
- const divs = Array.from(doc.querySelectorAll("div"));
3889
- const parsedBlocks = [];
3890
- const typeMap = {
3891
- divtype0: "SCENE_HEADING",
3892
- divtype2: "ACTION",
3893
- divtype3: "CHARACTER",
3894
- divtype4: "PARENTHETICAL",
3895
- divtype5: "DIALOGUE",
3896
- divtype6: "TRANSITION"
3897
- };
3898
- divs.forEach((div) => {
3899
- var _a;
3900
- const divText = ((_a = div.textContent) == null ? void 0 : _a.trim()) || "";
3901
- if (!divText) return;
3902
- let type = "ACTION";
3903
- for (const className of Array.from(div.classList)) {
3904
- if (typeMap[className]) {
3905
- type = typeMap[className];
3906
- break;
3907
- }
3738
+ const category = CATEGORIES.find((c) => c.id === tag.category_id);
3739
+ if (actualStart < tag.end_index) {
3740
+ nodes.push(
3741
+ /* @__PURE__ */ jsx(
3742
+ "span",
3743
+ {
3744
+ title: `${category == null ? void 0 : category.label} (Click to edit)`,
3745
+ onClick: (e) => {
3746
+ e.stopPropagation();
3747
+ const selection = window.getSelection();
3748
+ if (!selection) return;
3749
+ const range = document.createRange();
3750
+ const textNode = e.currentTarget.firstChild;
3751
+ if (textNode && textNode.nodeType === Node.TEXT_NODE) {
3752
+ range.selectNodeContents(textNode);
3753
+ } else {
3754
+ range.selectNodeContents(e.currentTarget);
3755
+ }
3756
+ selection.removeAllRanges();
3757
+ selection.addRange(range);
3758
+ setTimeout(() => handleMouseUp(), 0);
3759
+ },
3760
+ className: "cursor-pointer font-bold transition-all hover:opacity-80 rounded-[3px]",
3761
+ style: {
3762
+ color: category == null ? void 0 : category.color,
3763
+ padding: "0.125rem 0.25rem",
3764
+ margin: "0 -0.125rem"
3765
+ },
3766
+ children: block.text.slice(actualStart, tag.end_index)
3767
+ },
3768
+ tag.id || `tag-${actualStart}-${tag.end_index}`
3769
+ )
3770
+ );
3908
3771
  }
3909
- const idAttr = div.getAttribute("id");
3910
- const blockId = idAttr && idAttr.startsWith("par") ? idAttr.substring(3) : idAttr || uuid();
3911
- parsedBlocks.push({ id: blockId, type, text: divText });
3772
+ currentIndex = Math.max(currentIndex, tag.end_index);
3912
3773
  });
3913
- return parsedBlocks;
3914
- }, [scene]);
3915
- const characters = useMemo(() => {
3916
- const chars = blocks.filter((b) => b.type === "CHARACTER").map((b) => {
3917
- const text = b.text.trim().toUpperCase();
3918
- const parenIndex = text.indexOf("(");
3919
- return parenIndex > -1 ? text.substring(0, parenIndex).trim() : text;
3920
- }).filter(Boolean);
3921
- return [...new Set(chars)];
3922
- }, [blocks]);
3923
- const handleAISummarize = async () => {
3924
- var _a;
3925
- setIsSummarizing(true);
3926
- const res = await ((_a = options.onAISummarize) == null ? void 0 : _a.call(options, scene.content));
3927
- if (res.ok) {
3928
- const data = await res.json();
3929
- setIsSummarizing(false);
3930
- let parsedData = [];
3931
- let parsedSummaryData = {};
3932
- try {
3933
- const normalData = JSON.parse(data.data);
3934
- if (Array.isArray(normalData)) {
3935
- parsedData = Array.isArray(normalData[0]) ? normalData[0] : [];
3936
- const summary = Array.isArray(normalData[1]) ? normalData[1] : [];
3937
- parsedSummaryData = summary[0] || {};
3938
- }
3939
- } catch (error2) {
3940
- console.error("Error parsing AI summary data:", error2);
3941
- }
3942
- setSceneBrief(parsedSummaryData.summarise || "");
3943
- const newTags = [];
3944
- parsedData.forEach((aiTag) => {
3945
- if (!aiTag.block_id || !aiTag.category_id || typeof aiTag.start_index !== "number" || typeof aiTag.end_index !== "number") {
3946
- return;
3947
- }
3948
- const newTag = {
3949
- id: aiTag.id || uuid(),
3950
- block_id: String(aiTag.block_id).startsWith("par") ? String(aiTag.block_id).substring(3) : String(aiTag.block_id),
3951
- category_id: aiTag.category_id,
3952
- name: aiTag.name,
3953
- start_index: aiTag.start_index,
3954
- end_index: aiTag.end_index
3955
- };
3956
- if (aiTag.category_id === "PROP" || aiTag.category_id === "SET_PROP") {
3957
- newTag.quantity = 1;
3958
- } else if (aiTag.category_id === "CAST") {
3959
- newTag.look = "";
3960
- newTag.age = "";
3961
- }
3962
- newTags.push(newTag);
3963
- });
3964
- if (newTags.length > 0) {
3965
- const originalTags = tags;
3966
- setTags((prev) => {
3967
- const merged = [...prev];
3968
- newTags.forEach((newTag) => {
3969
- const isOverlapping = merged.some(
3970
- (t) => t.block_id === newTag.block_id && newTag.end_index > t.start_index && newTag.start_index < t.end_index
3971
- );
3972
- if (!isOverlapping) {
3973
- merged.push(newTag);
3974
- }
3975
- });
3976
- return merged;
3977
- });
3978
- try {
3979
- if (options.onTagsBulkAdded) {
3980
- await options.onTagsBulkAdded(newTags, parsedSummaryData.summarise);
3981
- }
3982
- } catch (error2) {
3983
- console.error("Failed to bulk add AI-generated tags:", error2);
3984
- setTags(originalTags);
3985
- }
3986
- }
3987
- return data;
3988
- } else {
3989
- setIsSummarizing(false);
3990
- console.error("Failed to summarize scene:", res);
3774
+ if (currentIndex < block.text.length) {
3775
+ nodes.push(
3776
+ /* @__PURE__ */ jsx("span", { children: block.text.slice(currentIndex) }, `text-${currentIndex}`)
3777
+ );
3991
3778
  }
3779
+ return nodes;
3992
3780
  };
3993
- const bulkCreateTags = useCallback(async () => {
3994
- if (blocks.length === 0) return;
3995
- const newTags = [];
3996
- const seenCharacters = /* @__PURE__ */ new Set();
3997
- const timeOfDays = ["DAY", "NIGHT"];
3998
- const isTimeOfDay = (str) => timeOfDays.includes(str.toUpperCase());
3999
- blocks.forEach((block) => {
4000
- if (block.type === "CHARACTER") {
4001
- const text = block.text.trim();
4002
- const parenIndex = text.indexOf("(");
4003
- const charName = parenIndex > -1 ? text.substring(0, parenIndex).trim() : text;
4004
- if (charName && !seenCharacters.has(charName.toUpperCase())) {
4005
- seenCharacters.add(charName.toUpperCase());
4006
- const startIndex = text.indexOf(charName);
4007
- if (startIndex !== -1) {
4008
- newTags.push({
4009
- id: uuid(),
4010
- block_id: block.id,
4011
- category_id: "CAST",
4012
- name: charName,
4013
- start_index: startIndex,
4014
- end_index: startIndex + charName.length,
4015
- look: "",
4016
- age: ""
4017
- });
3781
+ return /* @__PURE__ */ jsxs("div", { className: "relative p-8 md:p-12 mx-auto w-full min-h-screen flex flex-col gap-8 xl:pr-[20rem]", children: [
3782
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center max-w-6xl xl:max-w-full", children: [
3783
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 min-h-screen", children: [
3784
+ /* @__PURE__ */ jsx(
3785
+ "div",
3786
+ {
3787
+ className: "relative bg-[#fdfdfc] shadow-md shadow-zinc-300/60 border border-zinc-100 rounded-sm md:rounded-md pl-[1.5in] py-[1in] pr-[1in] flex flex-col w-[210mm] min-h-auto shrink-0",
3788
+ style: {
3789
+ fontFamily: COURIER_STACK,
3790
+ paddingLeft: "1.5in",
3791
+ paddingRight: "1in",
3792
+ paddingTop: "1in",
3793
+ paddingBottom: "1in",
3794
+ lineHeight: "1.2"
3795
+ },
3796
+ "data-screenplay-editor": "true",
3797
+ onMouseUp: handleMouseUp,
3798
+ children: blocks.map((block) => /* @__PURE__ */ jsxs(
3799
+ "div",
3800
+ {
3801
+ "data-block-id": block.id,
3802
+ className: `relative break-words w-full px-4 py-2 ${blockStyles[block.type].className}`,
3803
+ style: __spreadProps(__spreadValues({}, blockStyles[block.type].inputStyle), {
3804
+ minHeight: "2.5rem"
3805
+ }),
3806
+ children: [
3807
+ renderBlockText(block),
3808
+ (selectionMenu == null ? void 0 : selectionMenu.blockId) === block.id && /* @__PURE__ */ jsxs(
3809
+ "div",
3810
+ {
3811
+ ref: menuRef,
3812
+ "data-screenplay-editor": "false",
3813
+ className: `tag-menu absolute z-50 bg-white/70 backdrop-blur-2xl shadow-[0_10px_40px_rgb(0,0,0,0.06)] border border-white rounded-[1.5rem] p-2 flex flex-col w-56 animate-in fade-in zoom-in-95 duration-300 ease-out ${menuPlacement === "top" ? "origin-bottom" : "origin-top"}`,
3814
+ style: {
3815
+ top: selectionMenu.top,
3816
+ left: selectionMenu.left,
3817
+ transform: menuPlacement === "top" ? "translate(-50%, calc(-100% - 12px))" : "translate(-50%, 32px)"
3818
+ },
3819
+ children: [
3820
+ /* @__PURE__ */ jsxs("div", { className: "relative z-10 px-3 py-2.5 border-b border-white/60 mb-1.5", children: [
3821
+ /* @__PURE__ */ jsx("p", { className: "text-[9px] font-extrabold tracking-[0.2em] text-slate-400 uppercase mb-1", children: "Tag Element" }),
3822
+ /* @__PURE__ */ jsxs(
3823
+ "p",
3824
+ {
3825
+ className: "text-xs font-bold text-slate-700 truncate drop-shadow-sm",
3826
+ title: selectionMenu.text,
3827
+ children: [
3828
+ '"',
3829
+ selectionMenu.text,
3830
+ '"'
3831
+ ]
3832
+ }
3833
+ )
3834
+ ] }),
3835
+ /* @__PURE__ */ jsxs("div", { className: "relative z-10 flex flex-col gap-1", children: [
3836
+ CATEGORIES.filter(
3837
+ (cat) => !(cat.id === "LOCATION" && hasLocationTag) && !(cat.id === "SUBLOCATION" && !hasLocationTag)
3838
+ ).map((cat) => /* @__PURE__ */ jsxs(
3839
+ "button",
3840
+ {
3841
+ onClick: () => {
3842
+ const existingTag = tags.find(
3843
+ (t) => t.block_id === block.id && t.start_index === selectionMenu.startIndex && t.end_index === selectionMenu.endIndex
3844
+ );
3845
+ if (existingTag && existingTag.id) {
3846
+ updateTag == null ? void 0 : updateTag(existingTag.id, cat.id);
3847
+ } else {
3848
+ addTag(cat.id);
3849
+ }
3850
+ },
3851
+ className: "group w-full text-[12px] font-bold px-3 py-2 rounded-xl transition-all duration-300 text-left flex items-center justify-between hover:bg-white/80 hover:shadow-[0_2px_10px_rgb(0,0,0,0.02)] active:scale-[0.98]",
3852
+ style: { color: cat.color },
3853
+ children: [
3854
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
3855
+ /* @__PURE__ */ jsx(
3856
+ cat.icon,
3857
+ {
3858
+ className: "w-4 h-4 group-hover:scale-110 transition-transform duration-300",
3859
+ style: { color: cat.color }
3860
+ }
3861
+ ),
3862
+ cat.label
3863
+ ] }),
3864
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold text-slate-400 opacity-0 group-hover:opacity-100 transition-all duration-300 translate-x-1 group-hover:translate-x-0", children: "Select" })
3865
+ ]
3866
+ },
3867
+ cat.id
3868
+ )),
3869
+ tags.some(
3870
+ (t) => t.block_id === block.id && t.start_index === selectionMenu.startIndex && t.end_index === selectionMenu.endIndex
3871
+ ) && /* @__PURE__ */ jsx("div", { className: "mt-1 pt-1 border-t border-white/60", children: /* @__PURE__ */ jsxs(
3872
+ "button",
3873
+ {
3874
+ onClick: (e) => {
3875
+ const tagToRemove = tags.find(
3876
+ (t) => t.block_id === block.id && t.start_index === selectionMenu.startIndex && t.end_index === selectionMenu.endIndex
3877
+ );
3878
+ if (tagToRemove) {
3879
+ removeTag(e, tagToRemove.id);
3880
+ clearSelection();
3881
+ }
3882
+ },
3883
+ className: "group w-full text-[12px] font-bold px-3 py-2 rounded-xl transition-all duration-300 text-left flex items-center justify-between hover:bg-rose-50 hover:text-rose-600 hover:shadow-[0_2px_10px_rgb(225,29,72,0.04)] active:scale-[0.98] text-slate-500 border border-transparent hover:border-rose-100",
3884
+ children: [
3885
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
3886
+ /* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full shadow-sm bg-rose-400 group-hover:scale-125 transition-transform duration-300" }),
3887
+ "Remove Tag"
3888
+ ] }),
3889
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold text-rose-400 opacity-0 group-hover:opacity-100 transition-all duration-300 translate-x-1 group-hover:translate-x-0", children: "Remove" })
3890
+ ]
3891
+ }
3892
+ ) })
3893
+ ] })
3894
+ ]
3895
+ }
3896
+ )
3897
+ ]
3898
+ },
3899
+ block.id
3900
+ ))
4018
3901
  }
3902
+ ),
3903
+ /* @__PURE__ */ jsxs("div", { className: "relative bg-[#fdfdfc] shadow-md shadow-zinc-300/60 border border-zinc-100 rounded-sm md:rounded-md flex flex-col w-[210mm] shrink-0 p-8 md:p-12 lg:p-16", children: [
3904
+ /* @__PURE__ */ jsxs("h3", { className: "text-xs font-extrabold text-slate-800 uppercase tracking-[0.25em] mb-6 flex items-center gap-3 font-sans", children: [
3905
+ /* @__PURE__ */ jsx("span", { className: "flex items-center justify-center w-8 h-8 rounded-full bg-slate-100/80 shadow-inner border border-slate-200/50", children: /* @__PURE__ */ jsx(AlignLeft, { className: "w-3.5 h-3.5 text-slate-500" }) }),
3906
+ "Scene Brief"
3907
+ ] }),
3908
+ /* @__PURE__ */ jsxs("div", { className: "relative bg-zinc-50/50 border border-zinc-200/60 rounded-xl p-4 md:p-6 shadow-inner focus-within:bg-white focus-within:border-zinc-300 focus-within:shadow-[0_8px_30px_rgb(0,0,0,0.04)] transition-all duration-300", children: [
3909
+ /* @__PURE__ */ jsx(
3910
+ "textarea",
3911
+ {
3912
+ value: sceneBrief,
3913
+ onChange: (e) => setSceneBrief(e.target.value),
3914
+ placeholder: "Write a brief description or notes for this scene...",
3915
+ className: "w-full min-h-[120px] bg-transparent outline-none resize-y text-zinc-700 placeholder:text-zinc-400 text-sm md:text-base custom-scrollbar font-sans select-none",
3916
+ style: {
3917
+ lineHeight: "1.6"
3918
+ }
3919
+ }
3920
+ ),
3921
+ /* @__PURE__ */ jsx("div", { className: "mt-4 flex justify-end", children: /* @__PURE__ */ jsx(
3922
+ "button",
3923
+ {
3924
+ onClick: onUpdateBrief,
3925
+ disabled: !sceneBrief || sceneBrief.trim() === "",
3926
+ className: "rounded-full bg-[#15607b] px-6 py-3 text-[13px] font-semibold tracking-wide text-white shadow-md transition-all hover:bg-[#134a61] hover:shadow-lg active:scale-95 disabled:bg-gray-200 disabled:cursor-not-allowed disabled:opacity-95 disabled:text-black disabled:shadow-none",
3927
+ children: "Update"
3928
+ }
3929
+ ) })
3930
+ ] })
3931
+ ] })
3932
+ ] }),
3933
+ /* @__PURE__ */ jsxs(
3934
+ "button",
3935
+ {
3936
+ onClick: () => setIsSidebarOpen(true),
3937
+ className: "fixed bottom-6 right-6 z-40 flex h-14 w-14 items-center justify-center rounded-full bg-[#15607b] text-white shadow-[0_8px_30px_rgba(21,96,123,0.3)] transition-transform hover:scale-105 active:scale-95 xl:hidden",
3938
+ children: [
3939
+ /* @__PURE__ */ jsx(Tags, { className: "h-6 w-6" }),
3940
+ tags.length > 0 && /* @__PURE__ */ jsx("span", { className: "absolute -top-1 -right-1 flex h-5 w-5 items-center justify-center rounded-full bg-rose-500 text-[10px] font-bold text-white shadow-sm border-2 border-white", children: tags.length })
3941
+ ]
4019
3942
  }
4020
- } else if (block.type === "SCENE_HEADING") {
4021
- const text = block.text.trim();
4022
- const typeMatch = text.match(
4023
- /^(INT\/EXT|INT\.?\/EXT\.?|INT\.EXT\.?|INT|EXT|I\/E)\.?\s+/i
4024
- );
4025
- let remainingText = text;
4026
- let offset = 0;
4027
- if (typeMatch) {
4028
- offset = typeMatch[0].length;
4029
- remainingText = text.substring(offset);
3943
+ ),
3944
+ isSidebarOpen && /* @__PURE__ */ jsx(
3945
+ "div",
3946
+ {
3947
+ className: "fixed inset-0 z-40 bg-slate-900/20 backdrop-blur-sm transition-opacity xl:hidden",
3948
+ onClick: () => setIsSidebarOpen(false)
4030
3949
  }
4031
- const parts = remainingText.split(/\s+-\s+/);
4032
- if (parts.length > 0) {
4033
- const locationName = parts[0].trim();
4034
- const locStart = text.indexOf(locationName, offset);
4035
- if (locStart !== -1 && locationName) {
4036
- newTags.push({
4037
- id: uuid(),
4038
- block_id: block.id,
4039
- category_id: "LOCATION",
4040
- name: locationName,
4041
- start_index: locStart,
4042
- end_index: locStart + locationName.length
4043
- });
4044
- }
4045
- if (parts.length > 1) {
4046
- const secondPart = parts[1].trim();
4047
- const isLast = parts.length === 2;
4048
- if (!isLast || !isTimeOfDay(secondPart)) {
4049
- const subLocStart = text.indexOf(
4050
- secondPart,
4051
- locStart + locationName.length
4052
- );
4053
- if (subLocStart !== -1 && secondPart) {
4054
- newTags.push({
4055
- id: uuid(),
4056
- block_id: block.id,
4057
- category_id: "SUBLOCATION",
4058
- name: secondPart,
4059
- start_index: subLocStart,
4060
- end_index: subLocStart + secondPart.length
4061
- });
3950
+ ),
3951
+ /* @__PURE__ */ jsx(
3952
+ "div",
3953
+ {
3954
+ className: `fixed xl:absolute top-0 right-0 z-50 h-full w-72 transform transition-transform duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] xl:translate-x-0 ${isSidebarOpen ? "translate-x-0 shadow-[0_0_40px_rgba(0,0,0,0.1)]" : "translate-x-full"}`,
3955
+ children: /* @__PURE__ */ jsxs("div", { className: "sticky top-0 flex h-[100dvh] max-h-screen w-full flex-col border-l border-l-[#eefafd] bg-white p-3 py-5", children: [
3956
+ !aiSummarized && /* @__PURE__ */ jsx(
3957
+ summarize_button_default,
3958
+ {
3959
+ isSummarizing,
3960
+ onSummarize
4062
3961
  }
4063
- }
4064
- }
3962
+ ),
3963
+ /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col gap-3 py-5 my-5 border-t border-t-[#eefafd] flex-1 overflow-hidden", children: [
3964
+ /* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-between mb-6 shrink-0", children: [
3965
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
3966
+ /* @__PURE__ */ jsx("div", { className: "relative flex h-8 w-8 items-center justify-center rounded-[10px] border border-[#15607b]/20 bg-white shadow-sm", children: /* @__PURE__ */ jsx(Tags, { className: "h-4 w-4 text-[#15607b]" }) }),
3967
+ /* @__PURE__ */ jsx("h3", { className: "text-[12px] font-semibold uppercase tracking-[0.28em] text-[#134a61]", children: "Breakdown" })
3968
+ ] }),
3969
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx("span", { className: "rounded-full border border-slate-200/60 bg-slate-50 px-2.5 py-1 text-[11px] font-semibold text-[#134a61] shadow-sm backdrop-blur-md", children: tags.length }) })
3970
+ ] }),
3971
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-4 overflow-y-auto overflow-x-hidden pb-4 pr-1 custom-scrollbar", children: CATEGORIES.map((cat) => {
3972
+ const catTags = tags.filter((t) => t.category_id === cat.id);
3973
+ if (!catTags.length) return null;
3974
+ const isExpanded = expandedCategories[cat.id];
3975
+ const uniqueTags = Array.from(
3976
+ new Map(
3977
+ catTags.map((tag) => [tag.name.toLowerCase(), tag])
3978
+ ).values()
3979
+ );
3980
+ return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
3981
+ /* @__PURE__ */ jsxs(
3982
+ "div",
3983
+ {
3984
+ onClick: () => setExpandedCategories((prev) => ({
3985
+ [cat.id]: !prev[cat.id]
3986
+ })),
3987
+ className: "group relative flex cursor-pointer items-center gap-3 overflow-hidden rounded-[24px] border border-white/70 px-4 py-3.5 transition-all duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] hover:-translate-y-[1px]",
3988
+ style: {
3989
+ background: `linear-gradient(135deg, rgba(255,255,255,0.92), ${cat.hex}18)`,
3990
+ boxShadow: `0 16px 36px ${cat.hex}14, inset 0 1px 0 rgba(255,255,255,0.9)`,
3991
+ border: `1px solid ${cat.hex}50`
3992
+ },
3993
+ children: [
3994
+ /* @__PURE__ */ jsx(
3995
+ "div",
3996
+ {
3997
+ className: "absolute inset-0 opacity-0 transition-opacity duration-300 group-hover:opacity-100",
3998
+ style: {
3999
+ background: `radial-gradient(circle at 15% 20%, ${cat.hex}24, transparent 38%)`
4000
+ }
4001
+ }
4002
+ ),
4003
+ /* @__PURE__ */ jsx(
4004
+ "div",
4005
+ {
4006
+ className: "relative flex h-9 w-9 shrink-0 items-center justify-center rounded-xl border border-white/70 backdrop-blur-md",
4007
+ style: {
4008
+ background: `linear-gradient(135deg, ${cat.color}22, rgba(255,255,255,0.75))`,
4009
+ boxShadow: `0 10px 22px ${cat.color}14`
4010
+ },
4011
+ children: /* @__PURE__ */ jsx(cat.icon, { className: "h-4 w-4", color: cat.color })
4012
+ }
4013
+ ),
4014
+ /* @__PURE__ */ jsx(
4015
+ "span",
4016
+ {
4017
+ className: "relative text-[13px] font-semibold tracking-[0.01em]",
4018
+ style: { color: cat.color },
4019
+ children: cat.label
4020
+ }
4021
+ ),
4022
+ /* @__PURE__ */ jsx("span", { className: "relative ml-auto rounded-full border border-white/70 bg-white/75 px-2.5 py-1 text-[11px] font-semibold text-[#456575] backdrop-blur-md", children: catTags.length }),
4023
+ /* @__PURE__ */ jsx(
4024
+ ChevronDown,
4025
+ {
4026
+ className: `relative h-4 w-4 transition-transform duration-500 ease-[cubic-bezier(0.22,1,0.36,1)] ${isExpanded ? "rotate-180" : ""}`,
4027
+ style: { color: cat.color }
4028
+ }
4029
+ )
4030
+ ]
4031
+ }
4032
+ ),
4033
+ /* @__PURE__ */ jsx(
4034
+ "div",
4035
+ {
4036
+ className: `grid transition-all duration-500 ease-[cubic-bezier(0.22,1,0.36,1)] ${isExpanded ? "grid-rows-[1fr] opacity-100 mt-3" : "grid-rows-[0fr] opacity-0"}`,
4037
+ children: /* @__PURE__ */ jsx("div", { className: "overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap justify-start gap-2 py-2", children: uniqueTags.map((tag, index) => /* @__PURE__ */ jsx(
4038
+ "div",
4039
+ {
4040
+ className: "relative flex flex-col items-start",
4041
+ children: /* @__PURE__ */ jsxs(
4042
+ "span",
4043
+ {
4044
+ title: tag.name,
4045
+ onClick: (e) => {
4046
+ e.stopPropagation();
4047
+ if (cat.id !== "PROP" && cat.id !== "SET_PROP" && cat.id !== "CAST" && cat.id !== "EXTRA")
4048
+ return;
4049
+ if ((editingTagData == null ? void 0 : editingTagData.tag.id) === tag.id) {
4050
+ setEditingTagData(null);
4051
+ } else {
4052
+ const rect = e.currentTarget.getBoundingClientRect();
4053
+ setPopupPlacement({
4054
+ alignRight: rect.left > window.innerWidth - 240,
4055
+ alignBottom: rect.bottom > window.innerHeight - 250
4056
+ });
4057
+ setTagForm({
4058
+ quantity: tag.quantity || 1,
4059
+ look: tag.look || "",
4060
+ age: tag.age || "",
4061
+ age_range: tag.age_range || [],
4062
+ crowd_type: tag.crowd_type || []
4063
+ });
4064
+ setEditingTagData({
4065
+ tag,
4066
+ catId: cat.id,
4067
+ catColor: cat.color,
4068
+ rect
4069
+ });
4070
+ }
4071
+ },
4072
+ className: `inline-block max-w-full truncate text-[11px] font-semibold px-3 py-1.5 rounded-full border backdrop-blur-md transition-all duration-300 cursor-pointer! ${cat.id === "PROP" || cat.id === "SET_PROP" || cat.id === "CAST" ? "cursor-pointer hover:scale-105 shadow-sm" : ""}`,
4073
+ style: {
4074
+ color: cat.color,
4075
+ background: `linear-gradient(145deg, ${cat.color}18, rgba(255,255,255,0.88))`,
4076
+ borderColor: `${cat.color}28`
4077
+ },
4078
+ children: [
4079
+ tag.name,
4080
+ (cat.id === "PROP" || cat.id === "SET_PROP") && tag.quantity && tag.quantity > 1 ? /* @__PURE__ */ jsxs("span", { className: "ml-1.5 opacity-80 font-bold", children: [
4081
+ "x",
4082
+ tag.quantity
4083
+ ] }) : null
4084
+ ]
4085
+ }
4086
+ )
4087
+ },
4088
+ index
4089
+ )) }) })
4090
+ }
4091
+ )
4092
+ ] }, cat.id);
4093
+ }) })
4094
+ ] })
4095
+ ] })
4065
4096
  }
4066
- }
4067
- });
4068
- if (newTags.length > 0) {
4069
- const originalTags = tags;
4070
- setTags((prev) => {
4071
- const merged = [...prev];
4072
- const existingChars = new Set(
4073
- merged.filter(
4074
- (t) => t.category_id === "CHARACTER" || t.category_id === "CAST"
4075
- ).map((t) => t.name.toUpperCase())
4076
- );
4077
- newTags.forEach((newTag) => {
4078
- if (newTag.category_id === "CHARACTER") {
4079
- if (existingChars.has(newTag.name.toUpperCase())) return;
4080
- existingChars.add(newTag.name.toUpperCase());
4081
- }
4082
- const isOverlapping = merged.some(
4083
- (t) => t.block_id === newTag.block_id && newTag.end_index > t.start_index && newTag.start_index < t.end_index
4084
- );
4085
- if (!isOverlapping) {
4086
- merged.push(newTag);
4097
+ )
4098
+ ] }),
4099
+ editingTagData && /* @__PURE__ */ jsxs(Fragment, { children: [
4100
+ /* @__PURE__ */ jsx(
4101
+ "div",
4102
+ {
4103
+ className: "fixed inset-0 z-[100]",
4104
+ onClick: (e) => {
4105
+ e.stopPropagation();
4106
+ setEditingTagData(null);
4087
4107
  }
4088
- });
4089
- return merged;
4090
- });
4091
- try {
4092
- if (options.onTagsBulkAdded) {
4093
- await options.onTagsBulkAdded(newTags);
4094
4108
  }
4095
- } catch (error2) {
4096
- console.error("Failed to bulk create tags:", error2);
4097
- setTags(originalTags);
4098
- }
4099
- }
4100
- }, [blocks, tags, options.onTagsBulkAdded]);
4101
- useEffect(() => {
4102
- setSceneBrief("");
4103
- autoTaggedSceneRef.current = null;
4104
- }, [options.scene_url]);
4105
- useEffect(() => {
4106
- if (options.preLoadedTags && options.preLoadedTags.length > 0) {
4107
- setTags((prev) => {
4108
- if (JSON.stringify(prev) === JSON.stringify(options.preLoadedTags)) {
4109
- return prev;
4109
+ ),
4110
+ /* @__PURE__ */ jsxs(
4111
+ "div",
4112
+ {
4113
+ className: `p-3.5 bg-white/90 backdrop-blur-2xl rounded-[1.25rem] shadow-[0_10px_40px_rgb(0,0,0,0.12)] border border-white/60 z-[101] w-56 animate-in fade-in zoom-in-95 duration-200 ease-out fixed`,
4114
+ style: {
4115
+ top: popupPlacement.alignBottom ? "auto" : editingTagData.rect.bottom + 8,
4116
+ bottom: popupPlacement.alignBottom ? window.innerHeight - editingTagData.rect.top + 8 : "auto",
4117
+ left: popupPlacement.alignRight ? "auto" : editingTagData.rect.left,
4118
+ right: popupPlacement.alignRight ? window.innerWidth - editingTagData.rect.right : "auto"
4119
+ },
4120
+ onClick: (e) => e.stopPropagation(),
4121
+ children: [
4122
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center mb-3 border-b border-slate-100 pb-2.5", children: [
4123
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-extrabold text-slate-400 uppercase tracking-widest drop-shadow-sm", children: "Details" }),
4124
+ /* @__PURE__ */ jsx(
4125
+ "button",
4126
+ {
4127
+ onClick: () => setEditingTagData(null),
4128
+ className: "hover:bg-slate-100/80 p-1.5 rounded-full transition-colors active:scale-95",
4129
+ children: /* @__PURE__ */ jsx(X, { className: "w-3.5 h-3.5 text-slate-500" })
4130
+ }
4131
+ )
4132
+ ] }),
4133
+ (editingTagData.catId === "PROP" || editingTagData.catId === "SET_PROP") && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
4134
+ /* @__PURE__ */ jsx("label", { className: "text-[11px] font-bold text-slate-600 ml-1", children: "Quantity" }),
4135
+ /* @__PURE__ */ jsx(
4136
+ "input",
4137
+ {
4138
+ type: "number",
4139
+ min: "1",
4140
+ value: tagForm.quantity,
4141
+ onChange: (e) => {
4142
+ const val = Math.max(1, parseInt(e.target.value) || 1);
4143
+ setTagForm((prev) => __spreadProps(__spreadValues({}, prev), {
4144
+ quantity: val
4145
+ }));
4146
+ },
4147
+ className: "w-full text-[13px] px-3 py-2 rounded-xl border border-slate-200/80 outline-none focus:border-[#15607b] focus:ring-2 focus:ring-[#15607b]/20 transition-all bg-slate-50/50 focus:bg-white font-semibold text-slate-700"
4148
+ }
4149
+ )
4150
+ ] }),
4151
+ editingTagData.catId === "CAST" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
4152
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
4153
+ /* @__PURE__ */ jsx("label", { className: "text-[11px] font-bold text-slate-600 ml-1", children: "Look" }),
4154
+ /* @__PURE__ */ jsx(
4155
+ "input",
4156
+ {
4157
+ type: "text",
4158
+ placeholder: "e.g. Dirty, Formal",
4159
+ value: tagForm.look,
4160
+ onChange: (e) => {
4161
+ setTagForm((prev) => __spreadProps(__spreadValues({}, prev), {
4162
+ look: e.target.value
4163
+ }));
4164
+ },
4165
+ className: "w-full text-[13px] px-3 py-2 rounded-xl border border-slate-200/80 outline-none focus:border-[#15607b] focus:ring-2 focus:ring-[#15607b]/20 transition-all bg-slate-50/50 focus:bg-white font-semibold text-slate-700 placeholder:text-slate-400 placeholder:font-normal"
4166
+ }
4167
+ )
4168
+ ] }),
4169
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
4170
+ /* @__PURE__ */ jsx("label", { className: "text-[11px] font-bold text-slate-600 ml-1", children: "Age" }),
4171
+ /* @__PURE__ */ jsx(
4172
+ "input",
4173
+ {
4174
+ type: "number",
4175
+ min: "0",
4176
+ max: "100",
4177
+ value: tagForm.age,
4178
+ onChange: (e) => {
4179
+ let val = parseInt(e.target.value);
4180
+ if (isNaN(val)) {
4181
+ setTagForm((prev) => __spreadProps(__spreadValues({}, prev), {
4182
+ age: ""
4183
+ }));
4184
+ return;
4185
+ }
4186
+ val = Math.max(0, Math.min(100, val));
4187
+ setTagForm((prev) => __spreadProps(__spreadValues({}, prev), {
4188
+ age: val.toString()
4189
+ }));
4190
+ },
4191
+ className: "w-full text-[13px] px-3 py-2 rounded-xl border border-slate-200/80 outline-none focus:border-[#15607b] focus:ring-2 focus:ring-[#15607b]/20 transition-all bg-slate-50/50 focus:bg-white font-semibold text-slate-700"
4192
+ }
4193
+ )
4194
+ ] })
4195
+ ] }),
4196
+ editingTagData.catId === "EXTRA" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
4197
+ /* @__PURE__ */ jsx(
4198
+ MultiSelect,
4199
+ {
4200
+ label: "Age Range",
4201
+ options: crowdAgeOption,
4202
+ value: tagForm.age_range,
4203
+ returnObjects: true,
4204
+ onChange: (val) => setTagForm((prev) => __spreadProps(__spreadValues({}, prev), { age_range: val }))
4205
+ }
4206
+ ),
4207
+ /* @__PURE__ */ jsx(
4208
+ MultiSelect,
4209
+ {
4210
+ label: "Crowd Type",
4211
+ options: crowdTypeOption,
4212
+ value: tagForm.crowd_type,
4213
+ returnObjects: true,
4214
+ onChange: (val) => setTagForm((prev) => __spreadProps(__spreadValues({}, prev), { crowd_type: val }))
4215
+ }
4216
+ )
4217
+ ] }),
4218
+ /* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(
4219
+ "button",
4220
+ {
4221
+ onClick: (e) => {
4222
+ e.stopPropagation();
4223
+ if (editingTagData.tag.id) {
4224
+ updateTag == null ? void 0 : updateTag(editingTagData.tag.id, editingTagData.catId, __spreadValues(__spreadValues(__spreadValues({}, editingTagData.catId === "PROP" || editingTagData.catId === "SET_PROP" ? { quantity: tagForm.quantity } : {}), editingTagData.catId === "CAST" ? { look: tagForm.look, age: tagForm.age } : {}), editingTagData.catId === "EXTRA" ? {
4225
+ age_range: tagForm.age_range,
4226
+ crowd_type: tagForm.crowd_type
4227
+ } : {}));
4228
+ }
4229
+ setEditingTagData(null);
4230
+ },
4231
+ className: "w-full bg-[#15607b] hover:bg-[#134a61] text-white text-[12px] font-bold py-2.5 rounded-xl transition-all shadow-[0_4px_12px_rgba(21,96,123,0.2)] hover:shadow-[0_6px_16px_rgba(21,96,123,0.3)] active:scale-[0.98]",
4232
+ children: "Update Details"
4233
+ }
4234
+ ) })
4235
+ ]
4110
4236
  }
4111
- return options.preLoadedTags || [];
4112
- });
4113
- }
4114
- }, [options.preLoadedTags]);
4115
- useEffect(() => {
4116
- const doBulkCreate = async () => {
4117
- if (blocks.length === 0) return;
4118
- if (options.preLoadedTagsLoading) return;
4119
- if (autoTaggedSceneRef.current === options.scene_url) return;
4120
- const hasPreloadedTags = options.preLoadedTags && options.preLoadedTags.length > 0;
4121
- autoTaggedSceneRef.current = options.scene_url;
4122
- if (hasPreloadedTags) return;
4123
- await bulkCreateTags();
4124
- };
4125
- doBulkCreate();
4126
- }, [
4127
- blocks,
4128
- options.scene_url,
4129
- options.preLoadedTags,
4130
- options.preLoadedTagsLoading
4131
- ]);
4132
- const clearSelection = useCallback(() => {
4133
- var _a;
4134
- setSelectionMenu(null);
4135
- (_a = window.getSelection()) == null ? void 0 : _a.removeAllRanges();
4136
- }, []);
4137
- useEffect(() => {
4138
- if (!selectionMenu) {
4139
- setMenuPlacement("top");
4140
- }
4141
- }, [selectionMenu]);
4142
- useEffect(() => {
4143
- if (selectionMenu && menuRef.current) {
4144
- const rect = menuRef.current.getBoundingClientRect();
4145
- if (menuPlacement === "top" && rect.top < 100) {
4146
- setMenuPlacement("bottom");
4147
- } else if (menuPlacement === "bottom" && rect.bottom > window.innerHeight - 40 && rect.top > 300) {
4148
- setMenuPlacement("top");
4149
- }
4150
- }
4151
- }, [selectionMenu, menuPlacement]);
4237
+ )
4238
+ ] })
4239
+ ] });
4240
+ }
4241
+ function useScriptBreakdownScene(options) {
4242
+ const [tags, setTags] = useState(options.preLoadedTags || []);
4243
+ const [selectionMenu, setSelectionMenu] = useState(null);
4244
+ const autoTaggedSceneRef = useRef(null);
4245
+ const [scene, setScene] = useState(null);
4246
+ const [menuPlacement, setMenuPlacement] = useState("top");
4247
+ const [sceneBrief, setSceneBrief] = useState("");
4248
+ const [isSummarizing, setIsSummarizing] = useState(false);
4249
+ const [isLoading, setIsLoading] = useState(true);
4250
+ const [error, setError] = useState(false);
4251
+ const menuRef = useRef(null);
4152
4252
  useEffect(() => {
4153
- const handleClickOutside = (e) => {
4154
- if (selectionMenu && !e.target.closest(".tag-menu")) {
4155
- clearSelection();
4253
+ setIsLoading(true);
4254
+ const fetchScene = async () => {
4255
+ try {
4256
+ const response = await fetch(options.scene_url, options.fetchOptions);
4257
+ if (response.ok) {
4258
+ const text = await response.text();
4259
+ setScene({ content: text });
4260
+ } else {
4261
+ console.error("Failed to fetch scene:", response);
4262
+ setError(true);
4263
+ }
4264
+ setIsLoading(false);
4265
+ } catch (error2) {
4266
+ setError(true);
4267
+ setIsLoading(false);
4268
+ console.error("Error fetching scene:", error2);
4156
4269
  }
4157
4270
  };
4158
- document.addEventListener("mousedown", handleClickOutside);
4159
- return () => document.removeEventListener("mousedown", handleClickOutside);
4160
- }, [selectionMenu, clearSelection]);
4161
- const getAbsoluteOffset = (container, targetNode, targetOffset) => {
4162
- let absoluteOffset = 0;
4163
- let found = false;
4164
- const traverse = (node) => {
4271
+ fetchScene();
4272
+ }, []);
4273
+ const blocks = useMemo(() => {
4274
+ if (!scene || !scene.content) return [];
4275
+ const parser = new DOMParser();
4276
+ const doc = parser.parseFromString(scene.content, "text/html");
4277
+ const divs = Array.from(doc.querySelectorAll("div"));
4278
+ const parsedBlocks = [];
4279
+ const typeMap = {
4280
+ divtype0: "SCENE_HEADING",
4281
+ divtype2: "ACTION",
4282
+ divtype3: "CHARACTER",
4283
+ divtype4: "PARENTHETICAL",
4284
+ divtype5: "DIALOGUE",
4285
+ divtype6: "TRANSITION"
4286
+ };
4287
+ divs.forEach((div) => {
4165
4288
  var _a;
4166
- if (found) return;
4167
- if (node === targetNode) {
4168
- absoluteOffset += targetOffset;
4169
- found = true;
4170
- return;
4171
- }
4172
- if (node.nodeType === Node.TEXT_NODE) {
4173
- absoluteOffset += ((_a = node.nodeValue) == null ? void 0 : _a.length) || 0;
4174
- } else {
4175
- for (let i = 0; i < node.childNodes.length; i++) {
4176
- traverse(node.childNodes[i]);
4177
- if (found) return;
4289
+ const divText = ((_a = div.textContent) == null ? void 0 : _a.trim()) || "";
4290
+ if (!divText) return;
4291
+ let type = "ACTION";
4292
+ for (const className of Array.from(div.classList)) {
4293
+ if (typeMap[className]) {
4294
+ type = typeMap[className];
4295
+ break;
4178
4296
  }
4179
4297
  }
4180
- };
4181
- traverse(container);
4182
- return found ? absoluteOffset : null;
4183
- };
4184
- const handleMouseUp = () => {
4185
- const selection = window.getSelection();
4186
- if (!selection || selection.isCollapsed || !selection.toString().trim())
4187
- return;
4188
- const range = selection.getRangeAt(0);
4189
- let container = range.commonAncestorContainer;
4190
- if (container.nodeType === Node.TEXT_NODE)
4191
- container = container.parentElement;
4192
- const blockElem = container.closest("[data-block-id]");
4193
- if (!blockElem) return;
4194
- const blockId = blockElem.getAttribute("data-block-id");
4195
- const startOffset = getAbsoluteOffset(
4196
- blockElem,
4197
- range.startContainer,
4198
- range.startOffset
4199
- );
4200
- const endOffset = getAbsoluteOffset(
4201
- blockElem,
4202
- range.endContainer,
4203
- range.endOffset
4204
- );
4205
- if (startOffset !== null && endOffset !== null) {
4206
- const rect = range.getBoundingClientRect();
4207
- const blockRect = blockElem.getBoundingClientRect();
4208
- setSelectionMenu({
4209
- blockId,
4210
- startIndex: Math.min(startOffset, endOffset),
4211
- endIndex: Math.max(startOffset, endOffset),
4212
- text: selection.toString().trim(),
4213
- top: rect.top - blockRect.top,
4214
- left: rect.left - blockRect.left + rect.width / 2
4215
- });
4216
- }
4217
- };
4218
- const addTag = async (categoryId) => {
4219
- var _a;
4220
- if (!selectionMenu) return;
4221
- const newTag = {
4222
- id: uuid(),
4223
- block_id: selectionMenu.blockId,
4224
- category_id: categoryId,
4225
- name: selectionMenu.text,
4226
- start_index: selectionMenu.startIndex,
4227
- end_index: selectionMenu.endIndex
4228
- };
4229
- if (categoryId === "PROP" || categoryId === "SET_PROP") {
4230
- newTag.quantity = 1;
4231
- } else if (categoryId === "CAST") {
4232
- newTag.look = "";
4233
- newTag.age = "";
4234
- }
4235
- setTags((prev) => {
4236
- const filtered = prev.filter(
4237
- (t) => t.block_id !== newTag.block_id || !(newTag.end_index > t.start_index && newTag.start_index < t.end_index)
4238
- );
4239
- return [...filtered, newTag];
4298
+ const idAttr = div.getAttribute("id");
4299
+ const blockId = idAttr && idAttr.startsWith("par") ? idAttr.substring(3) : idAttr || uuid();
4300
+ parsedBlocks.push({ id: blockId, type, text: divText });
4240
4301
  });
4241
- clearSelection();
4242
- try {
4243
- await ((_a = options.onTagAdded) == null ? void 0 : _a.call(options, newTag));
4244
- } catch (error2) {
4245
- console.error("Failed to add tag:", error2);
4246
- setTags((prev) => prev.filter((t) => t.id !== newTag.id));
4247
- }
4248
- };
4249
- const removeTag = async (e, id) => {
4302
+ return parsedBlocks;
4303
+ }, [scene]);
4304
+ const characters = useMemo(() => {
4305
+ const chars = blocks.filter((b) => b.type === "CHARACTER").map((b) => {
4306
+ const text = b.text.trim().toUpperCase();
4307
+ const parenIndex = text.indexOf("(");
4308
+ return parenIndex > -1 ? text.substring(0, parenIndex).trim() : text;
4309
+ }).filter(Boolean);
4310
+ return [...new Set(chars)];
4311
+ }, [blocks]);
4312
+ const handleAISummarize = async () => {
4250
4313
  var _a;
4251
- e.stopPropagation();
4252
- e.preventDefault();
4253
- const tagToRemove = tags.find((t) => t.id === id);
4254
- if (!tagToRemove) return;
4255
- setTags((prev) => prev.filter((t) => t.id !== id));
4256
- clearSelection();
4257
- try {
4258
- await ((_a = options.onTagRemoved) == null ? void 0 : _a.call(options, id));
4259
- } catch (error2) {
4260
- console.error("Failed to remove tag:", error2);
4261
- setTags((prev) => [...prev, tagToRemove]);
4262
- }
4263
- };
4264
- const updateTag = async (id, categoryId, details) => {
4265
- var _a, _b, _c, _d;
4266
- const tagToUpdate = tags.find((t) => t.id === id);
4267
- if (!tagToUpdate) return;
4268
- const updatedFields = __spreadValues({}, details);
4269
- if (categoryId) {
4270
- updatedFields.category_id = categoryId;
4271
- if (categoryId === "PROP" || categoryId === "SET_PROP") {
4272
- updatedFields.quantity = (_a = tagToUpdate.quantity) != null ? _a : 1;
4273
- } else if (categoryId === "CAST") {
4274
- updatedFields.look = (_b = tagToUpdate.look) != null ? _b : "";
4275
- updatedFields.age = (_c = tagToUpdate.age) != null ? _c : "";
4314
+ setIsSummarizing(true);
4315
+ const res = await ((_a = options.onAISummarize) == null ? void 0 : _a.call(options, scene.content));
4316
+ if (res.ok) {
4317
+ const data = await res.json();
4318
+ setIsSummarizing(false);
4319
+ let parsedData = [];
4320
+ let parsedSummaryData = {};
4321
+ try {
4322
+ const normalData = JSON.parse(data.data);
4323
+ if (Array.isArray(normalData)) {
4324
+ parsedData = Array.isArray(normalData[0]) ? normalData[0] : [];
4325
+ const summary = Array.isArray(normalData[1]) ? normalData[1] : [];
4326
+ parsedSummaryData = summary[0] || {};
4327
+ }
4328
+ } catch (error2) {
4329
+ console.error("Error parsing AI summary data:", error2);
4276
4330
  }
4277
- }
4278
- setTags(
4279
- (prev) => prev.map((t) => {
4280
- if (t.id === id) return __spreadValues(__spreadValues({}, t), updatedFields);
4281
- if (details && t.name === tagToUpdate.name && t.category_id === tagToUpdate.category_id) {
4282
- return __spreadValues(__spreadValues({}, t), details);
4331
+ setSceneBrief(parsedSummaryData.summarise || "");
4332
+ const newTags = [];
4333
+ parsedData.forEach((aiTag) => {
4334
+ if (!aiTag.block_id || !aiTag.category_id || typeof aiTag.start_index !== "number" || typeof aiTag.end_index !== "number") {
4335
+ return;
4283
4336
  }
4284
- return t;
4285
- })
4286
- );
4287
- if (categoryId) clearSelection();
4288
- try {
4289
- await ((_d = options.onTagUpdated) == null ? void 0 : _d.call(options, id, categoryId || null, details));
4290
- } catch (error2) {
4291
- console.error("Failed to update tag:", error2);
4292
- setTags((prev) => prev.map((t) => t.id === id ? tagToUpdate : t));
4337
+ const newTag = {
4338
+ id: aiTag.id || uuid(),
4339
+ block_id: String(aiTag.block_id).startsWith("par") ? String(aiTag.block_id).substring(3) : String(aiTag.block_id),
4340
+ category_id: aiTag.category_id,
4341
+ name: aiTag.name,
4342
+ start_index: aiTag.start_index,
4343
+ end_index: aiTag.end_index
4344
+ };
4345
+ if (aiTag.category_id === "PROP" || aiTag.category_id === "SET_PROP") {
4346
+ newTag.quantity = 1;
4347
+ } else if (aiTag.category_id === "CAST") {
4348
+ newTag.look = "";
4349
+ newTag.age = "";
4350
+ }
4351
+ newTags.push(newTag);
4352
+ });
4353
+ if (newTags.length > 0) {
4354
+ const originalTags = tags;
4355
+ setTags((prev) => {
4356
+ const merged = [...prev];
4357
+ newTags.forEach((newTag) => {
4358
+ const isOverlapping = merged.some(
4359
+ (t) => t.block_id === newTag.block_id && newTag.end_index > t.start_index && newTag.start_index < t.end_index
4360
+ );
4361
+ if (!isOverlapping) {
4362
+ merged.push(newTag);
4363
+ }
4364
+ });
4365
+ return merged;
4366
+ });
4367
+ try {
4368
+ if (options.onTagsBulkAdded) {
4369
+ await options.onTagsBulkAdded(newTags, parsedSummaryData.summarise);
4370
+ }
4371
+ } catch (error2) {
4372
+ console.error("Failed to bulk add AI-generated tags:", error2);
4373
+ setTags(originalTags);
4374
+ }
4375
+ }
4376
+ return data;
4377
+ } else {
4378
+ setIsSummarizing(false);
4379
+ console.error("Failed to summarize scene:", res);
4293
4380
  }
4294
4381
  };
4295
- return {
4296
- scene,
4297
- blocks,
4298
- characters,
4299
- isLoading,
4300
- error,
4301
- tags,
4302
- selectionMenu,
4303
- handleMouseUp,
4304
- addTag,
4305
- updateTag,
4306
- removeTag,
4307
- clearSelection,
4308
- menuPlacement,
4309
- menuRef,
4310
- sceneBrief,
4311
- setSceneBrief,
4312
- handleAISummarize,
4313
- isSummarizing
4314
- };
4315
- }
4316
- function ModalLayout({
4317
- children,
4318
- onClose,
4319
- title,
4320
- maxWidth = "max-w-4xl",
4321
- menuRef
4322
- }) {
4323
- useEffect(() => {
4324
- document.body.style.overflow = "hidden";
4325
- return () => {
4326
- document.body.style.overflow = "";
4327
- };
4328
- }, []);
4329
- return /* @__PURE__ */ jsxs(
4330
- "div",
4331
- {
4332
- ref: menuRef,
4333
- onClick: onClose,
4334
- className: "fixed inset-0 z-50 overflow-y-auto bg-black/40 backdrop-blur-xs",
4335
- "data-lenis-prevent": "true",
4336
- children: [
4337
- /* @__PURE__ */ jsx("div", { className: "flex min-h-full items-center justify-center p-4 sm:p-6", children: /* @__PURE__ */ jsxs(
4338
- "div",
4339
- {
4340
- onClick: (e) => e.stopPropagation(),
4341
- className: `relative w-full ${maxWidth} bg-white rounded-3xl shadow-2xl transform transition-all duration-300 scale-100 opacity-100 animate-modal`,
4342
- children: [
4343
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b border-gray-200 pb-3 pt-6 px-6", children: [
4344
- /* @__PURE__ */ jsx("h2", { className: "text-xl sm:text-2xl font-medium text-blumine-600 line-clamp-1", children: title }),
4345
- /* @__PURE__ */ jsx(
4346
- "button",
4347
- {
4348
- onClick: onClose,
4349
- className: "w-10 h-10 flex items-center justify-center rounded-full border border-blumine-600 text-blumine-600 hover:bg-blumine-600/10 hover:rotate-90 transition-all duration-300",
4350
- "aria-label": "Close",
4351
- children: /* @__PURE__ */ jsx(X, { size: 20 })
4352
- }
4353
- )
4354
- ] }),
4355
- children
4356
- ]
4382
+ const bulkCreateTags = useCallback(async () => {
4383
+ if (blocks.length === 0) return;
4384
+ const newTags = [];
4385
+ const seenCharacters = /* @__PURE__ */ new Set();
4386
+ const timeOfDays = ["DAY", "NIGHT"];
4387
+ const isTimeOfDay = (str) => timeOfDays.includes(str.toUpperCase());
4388
+ blocks.forEach((block) => {
4389
+ if (block.type === "CHARACTER") {
4390
+ const text = block.text.trim();
4391
+ const parenIndex = text.indexOf("(");
4392
+ const charName = parenIndex > -1 ? text.substring(0, parenIndex).trim() : text;
4393
+ if (charName && !seenCharacters.has(charName.toUpperCase())) {
4394
+ seenCharacters.add(charName.toUpperCase());
4395
+ const startIndex = text.indexOf(charName);
4396
+ if (startIndex !== -1) {
4397
+ newTags.push({
4398
+ id: uuid(),
4399
+ block_id: block.id,
4400
+ category_id: "CAST",
4401
+ name: charName,
4402
+ start_index: startIndex,
4403
+ end_index: startIndex + charName.length,
4404
+ look: "",
4405
+ age: ""
4406
+ });
4357
4407
  }
4358
- ) }),
4359
- /* @__PURE__ */ jsx("style", { children: `
4360
- @keyframes modal {
4361
- 0% {
4362
- opacity: 0;
4363
- transform: translateY(20px) scale(0.95);
4364
- }
4365
- 100% {
4366
- opacity: 1;
4367
- transform: translateY(0) scale(1);
4368
- }
4408
+ }
4409
+ } else if (block.type === "SCENE_HEADING") {
4410
+ const text = block.text.trim();
4411
+ const typeMatch = text.match(
4412
+ /^(INT\/EXT|INT\.?\/EXT\.?|INT\.EXT\.?|INT|EXT|I\/E)\.?\s+/i
4413
+ );
4414
+ let remainingText = text;
4415
+ let offset = 0;
4416
+ if (typeMatch) {
4417
+ offset = typeMatch[0].length;
4418
+ remainingText = text.substring(offset);
4419
+ }
4420
+ const parts = remainingText.split(/\s+-\s+/);
4421
+ if (parts.length > 0) {
4422
+ const locationName = parts[0].trim();
4423
+ const locStart = text.indexOf(locationName, offset);
4424
+ if (locStart !== -1 && locationName) {
4425
+ newTags.push({
4426
+ id: uuid(),
4427
+ block_id: block.id,
4428
+ category_id: "LOCATION",
4429
+ name: locationName,
4430
+ start_index: locStart,
4431
+ end_index: locStart + locationName.length
4432
+ });
4369
4433
  }
4370
- .animate-modal {
4371
- animation: modal 0.3s ease-out forwards;
4434
+ if (parts.length > 1) {
4435
+ const secondPart = parts[1].trim();
4436
+ const isLast = parts.length === 2;
4437
+ if (!isLast || !isTimeOfDay(secondPart)) {
4438
+ const subLocStart = text.indexOf(
4439
+ secondPart,
4440
+ locStart + locationName.length
4441
+ );
4442
+ if (subLocStart !== -1 && secondPart) {
4443
+ newTags.push({
4444
+ id: uuid(),
4445
+ block_id: block.id,
4446
+ category_id: "SUBLOCATION",
4447
+ name: secondPart,
4448
+ start_index: subLocStart,
4449
+ end_index: subLocStart + secondPart.length
4450
+ });
4451
+ }
4452
+ }
4372
4453
  }
4373
- ` })
4374
- ]
4375
- }
4376
- );
4377
- }
4378
- var modal_layout_default = ModalLayout;
4379
- var FormikSelect = ({
4380
- label,
4381
- name,
4382
- selectedOption,
4383
- optionData,
4384
- value,
4385
- disable,
4386
- divClasses,
4387
- valueProperty = "name",
4388
- labelProperty = "name",
4389
- label2propery,
4390
- brackets,
4391
- className,
4392
- enableRedAsterick = false,
4393
- disableOptionProperty = "disable",
4394
- disabledOptionText = "",
4395
- onChange
4396
- }) => {
4397
- return /* @__PURE__ */ jsxs("div", { className: `w-full ${divClasses}`, children: [
4398
- label && /* @__PURE__ */ jsxs("label", { className: `mb-1.5 text-sm font-medium text-slate-700 capitalize flex`, children: [
4399
- label,
4400
- enableRedAsterick && /* @__PURE__ */ jsx(AsteriskIcon, { className: "size-3 text-red-500" })
4401
- ] }),
4402
- /* @__PURE__ */ jsxs(
4403
- Field,
4404
- {
4405
- name,
4406
- as: "select",
4407
- value,
4408
- disabled: disable,
4409
- onChange,
4410
- className: `${className} rounded-[2.5rem] block w-full px-3 py-2.5 text-slate-700 placeholder-slate-400 placeholder:capitalize bg-white border border-slate-300/80 focus:border-blumine-500 focus:ring-2 focus:ring-blumine-500/50 focus:outline-none transition-all`,
4411
- children: [
4412
- /* @__PURE__ */ jsx("option", { selected: true, value: "", disabled: true, className: "capitalize", children: selectedOption }),
4413
- optionData == null ? void 0 : optionData.map((option, index) => /* @__PURE__ */ jsxs(
4414
- "option",
4415
- {
4416
- value: option[valueProperty],
4417
- disabled: option[disableOptionProperty],
4418
- children: [
4419
- option[labelProperty],
4420
- label2propery ? brackets ? ` (${option[label2propery]})` : option[label2propery] : "",
4421
- " ",
4422
- option[disableOptionProperty] && `(${disabledOptionText})`
4423
- ]
4424
- },
4425
- index
4426
- ))
4427
- ]
4428
- }
4429
- ),
4430
- /* @__PURE__ */ jsx(
4431
- ErrorMessage,
4432
- {
4433
- name,
4434
- className: "text-red-500 text-sm mt-1",
4435
- component: "div"
4454
+ }
4436
4455
  }
4437
- )
4438
- ] });
4439
- };
4440
- var FormikInput = (_a) => {
4441
- var _b = _a, {
4442
- label,
4443
- name,
4444
- type,
4445
- placeholder,
4446
- className,
4447
- labelClassName,
4448
- readOnly,
4449
- divClasses,
4450
- length,
4451
- disable,
4452
- min = 0,
4453
- max,
4454
- convertToText = false,
4455
- enableRedAsterick = false,
4456
- case_sensitivity = "normal"
4457
- } = _b, props = __objRest(_b, [
4458
- "label",
4459
- "name",
4460
- "type",
4461
- "placeholder",
4462
- "className",
4463
- "labelClassName",
4464
- "readOnly",
4465
- "divClasses",
4466
- "length",
4467
- "disable",
4468
- "min",
4469
- "max",
4470
- "convertToText",
4471
- "enableRedAsterick",
4472
- "case_sensitivity"
4456
+ });
4457
+ if (newTags.length > 0) {
4458
+ const originalTags = tags;
4459
+ setTags((prev) => {
4460
+ const merged = [...prev];
4461
+ const existingChars = new Set(
4462
+ merged.filter(
4463
+ (t) => t.category_id === "CHARACTER" || t.category_id === "CAST"
4464
+ ).map((t) => t.name.toUpperCase())
4465
+ );
4466
+ newTags.forEach((newTag) => {
4467
+ if (newTag.category_id === "CHARACTER") {
4468
+ if (existingChars.has(newTag.name.toUpperCase())) return;
4469
+ existingChars.add(newTag.name.toUpperCase());
4470
+ }
4471
+ const isOverlapping = merged.some(
4472
+ (t) => t.block_id === newTag.block_id && newTag.end_index > t.start_index && newTag.start_index < t.end_index
4473
+ );
4474
+ if (!isOverlapping) {
4475
+ merged.push(newTag);
4476
+ }
4477
+ });
4478
+ return merged;
4479
+ });
4480
+ try {
4481
+ if (options.onTagsBulkAdded) {
4482
+ await options.onTagsBulkAdded(newTags);
4483
+ }
4484
+ } catch (error2) {
4485
+ console.error("Failed to bulk create tags:", error2);
4486
+ setTags(originalTags);
4487
+ }
4488
+ }
4489
+ }, [blocks, tags, options.onTagsBulkAdded]);
4490
+ useEffect(() => {
4491
+ setSceneBrief("");
4492
+ autoTaggedSceneRef.current = null;
4493
+ }, [options.scene_url]);
4494
+ useEffect(() => {
4495
+ if (options.preLoadedTags && options.preLoadedTags.length > 0) {
4496
+ setTags((prev) => {
4497
+ if (JSON.stringify(prev) === JSON.stringify(options.preLoadedTags)) {
4498
+ return prev;
4499
+ }
4500
+ return options.preLoadedTags || [];
4501
+ });
4502
+ }
4503
+ }, [options.preLoadedTags]);
4504
+ useEffect(() => {
4505
+ const doBulkCreate = async () => {
4506
+ if (blocks.length === 0) return;
4507
+ if (options.preLoadedTagsLoading) return;
4508
+ if (autoTaggedSceneRef.current === options.scene_url) return;
4509
+ const hasPreloadedTags = options.preLoadedTags && options.preLoadedTags.length > 0;
4510
+ autoTaggedSceneRef.current = options.scene_url;
4511
+ if (hasPreloadedTags) return;
4512
+ await bulkCreateTags();
4513
+ };
4514
+ doBulkCreate();
4515
+ }, [
4516
+ blocks,
4517
+ options.scene_url,
4518
+ options.preLoadedTags,
4519
+ options.preLoadedTagsLoading
4473
4520
  ]);
4474
- const { setFieldValue } = useFormikContext();
4475
- const handleCaseChange = (e) => {
4476
- let value = e.target.value;
4477
- if (type === "number" && min !== void 0) {
4478
- if (value !== "" && Number(value) < min) return;
4521
+ const clearSelection = useCallback(() => {
4522
+ var _a;
4523
+ setSelectionMenu(null);
4524
+ (_a = window.getSelection()) == null ? void 0 : _a.removeAllRanges();
4525
+ }, []);
4526
+ useEffect(() => {
4527
+ if (!selectionMenu) {
4528
+ setMenuPlacement("top");
4479
4529
  }
4480
- switch (case_sensitivity) {
4481
- case "lowercase":
4482
- value = value.toLowerCase();
4483
- break;
4484
- case "uppercase":
4485
- value = value.toUpperCase();
4486
- break;
4530
+ }, [selectionMenu]);
4531
+ useEffect(() => {
4532
+ if (selectionMenu && menuRef.current) {
4533
+ const rect = menuRef.current.getBoundingClientRect();
4534
+ if (menuPlacement === "top" && rect.top < 100) {
4535
+ setMenuPlacement("bottom");
4536
+ } else if (menuPlacement === "bottom" && rect.bottom > window.innerHeight - 40 && rect.top > 300) {
4537
+ setMenuPlacement("top");
4538
+ }
4487
4539
  }
4488
- setFieldValue(name, value);
4489
- };
4490
- return /* @__PURE__ */ jsxs("div", { className: `w-full ${divClasses}`, children: [
4491
- /* @__PURE__ */ jsxs(
4492
- "label",
4493
- {
4494
- className: `${labelClassName} mb-1.5 text-sm font-medium text-slate-700 capitalize flex`,
4495
- children: [
4496
- label,
4497
- enableRedAsterick && /* @__PURE__ */ jsx(AsteriskIcon, { className: "size-3 text-red-500" })
4498
- ]
4540
+ }, [selectionMenu, menuPlacement]);
4541
+ useEffect(() => {
4542
+ const handleClickOutside = (e) => {
4543
+ if (selectionMenu && !e.target.closest(".tag-menu")) {
4544
+ clearSelection();
4499
4545
  }
4500
- ),
4501
- /* @__PURE__ */ jsx(
4502
- Field,
4503
- __spreadProps(__spreadValues({
4504
- name,
4505
- placeholder,
4506
- readOnly,
4507
- type,
4508
- maxLength: length,
4509
- disabled: disable,
4510
- max,
4511
- min,
4512
- onChange: handleCaseChange
4513
- }, props), {
4514
- className: `${className} rounded-[2.5rem] block w-full px-3 py-2.5 text-slate-700 placeholder-slate-400 placeholder:capitalize bg-white border border-slate-300/80 focus:border-blumine-500 focus:ring-2 focus:ring-blumine-500/50 focus:outline-none transition-all disabled:opacity-70`
4515
- })
4516
- ),
4517
- /* @__PURE__ */ jsx(
4518
- ErrorMessage,
4519
- {
4520
- name,
4521
- className: "text-red-500 text-sm mt-1",
4522
- component: "div"
4546
+ };
4547
+ document.addEventListener("mousedown", handleClickOutside);
4548
+ return () => document.removeEventListener("mousedown", handleClickOutside);
4549
+ }, [selectionMenu, clearSelection]);
4550
+ const getAbsoluteOffset = (container, targetNode, targetOffset) => {
4551
+ let absoluteOffset = 0;
4552
+ let found = false;
4553
+ const traverse = (node) => {
4554
+ var _a;
4555
+ if (found) return;
4556
+ if (node === targetNode) {
4557
+ absoluteOffset += targetOffset;
4558
+ found = true;
4559
+ return;
4523
4560
  }
4524
- )
4525
- ] });
4526
- };
4527
- var FormikTextarea = ({
4528
- label,
4529
- name,
4530
- placeholder,
4531
- readOnly,
4532
- divClasses = "",
4533
- className = "",
4534
- length,
4535
- disable,
4536
- min,
4537
- max,
4538
- enableRedAsterick = false
4539
- }) => {
4540
- return /* @__PURE__ */ jsxs("div", { className: `w-full ${divClasses}`, children: [
4541
- /* @__PURE__ */ jsxs("label", { className: `mb-1.5 text-sm font-medium text-slate-700 capitalize flex`, children: [
4542
- label,
4543
- enableRedAsterick && /* @__PURE__ */ jsx(AsteriskIcon, { className: "size-3 text-red-500" })
4544
- ] }),
4545
- /* @__PURE__ */ jsx(
4546
- Field,
4547
- {
4548
- as: "textarea",
4549
- name,
4550
- placeholder,
4551
- readOnly,
4552
- maxLength: length,
4553
- disabled: disable,
4554
- max,
4555
- min,
4556
- className: `${className} block w-full px-4 py-3 text-slate-700 placeholder-slate-400 placeholder:capitalize bg-white border border-slate-300/80 rounded-2xl focus:border-blumine-500 focus:ring-2 focus:ring-blumine-500/50 focus:outline-none transition-all disabled:opacity-70`
4561
+ if (node.nodeType === Node.TEXT_NODE) {
4562
+ absoluteOffset += ((_a = node.nodeValue) == null ? void 0 : _a.length) || 0;
4563
+ } else {
4564
+ for (let i = 0; i < node.childNodes.length; i++) {
4565
+ traverse(node.childNodes[i]);
4566
+ if (found) return;
4567
+ }
4557
4568
  }
4558
- ),
4559
- /* @__PURE__ */ jsx(
4560
- ErrorMessage,
4561
- {
4562
- name,
4563
- className: "text-red-500 text-sm mt-1",
4564
- component: "div"
4569
+ };
4570
+ traverse(container);
4571
+ return found ? absoluteOffset : null;
4572
+ };
4573
+ const handleMouseUp = () => {
4574
+ const selection = window.getSelection();
4575
+ if (!selection || selection.isCollapsed || !selection.toString().trim())
4576
+ return;
4577
+ const range = selection.getRangeAt(0);
4578
+ let container = range.commonAncestorContainer;
4579
+ if (container.nodeType === Node.TEXT_NODE)
4580
+ container = container.parentElement;
4581
+ const blockElem = container.closest("[data-block-id]");
4582
+ if (!blockElem) return;
4583
+ const blockId = blockElem.getAttribute("data-block-id");
4584
+ const startOffset = getAbsoluteOffset(
4585
+ blockElem,
4586
+ range.startContainer,
4587
+ range.startOffset
4588
+ );
4589
+ const endOffset = getAbsoluteOffset(
4590
+ blockElem,
4591
+ range.endContainer,
4592
+ range.endOffset
4593
+ );
4594
+ if (startOffset !== null && endOffset !== null) {
4595
+ const rect = range.getBoundingClientRect();
4596
+ const blockRect = blockElem.getBoundingClientRect();
4597
+ setSelectionMenu({
4598
+ blockId,
4599
+ startIndex: Math.min(startOffset, endOffset),
4600
+ endIndex: Math.max(startOffset, endOffset),
4601
+ text: selection.toString().trim(),
4602
+ top: rect.top - blockRect.top,
4603
+ left: rect.left - blockRect.left + rect.width / 2
4604
+ });
4605
+ }
4606
+ };
4607
+ const addTag = async (categoryId) => {
4608
+ var _a;
4609
+ if (!selectionMenu) return;
4610
+ const newTag = {
4611
+ id: uuid(),
4612
+ block_id: selectionMenu.blockId,
4613
+ category_id: categoryId,
4614
+ name: selectionMenu.text,
4615
+ start_index: selectionMenu.startIndex,
4616
+ end_index: selectionMenu.endIndex
4617
+ };
4618
+ if (categoryId === "PROP" || categoryId === "SET_PROP") {
4619
+ newTag.quantity = 1;
4620
+ } else if (categoryId === "CAST") {
4621
+ newTag.look = "";
4622
+ newTag.age = "";
4623
+ }
4624
+ setTags((prev) => {
4625
+ const filtered = prev.filter(
4626
+ (t) => t.block_id !== newTag.block_id || !(newTag.end_index > t.start_index && newTag.start_index < t.end_index)
4627
+ );
4628
+ return [...filtered, newTag];
4629
+ });
4630
+ clearSelection();
4631
+ try {
4632
+ await ((_a = options.onTagAdded) == null ? void 0 : _a.call(options, newTag));
4633
+ } catch (error2) {
4634
+ console.error("Failed to add tag:", error2);
4635
+ setTags((prev) => prev.filter((t) => t.id !== newTag.id));
4636
+ }
4637
+ };
4638
+ const removeTag = async (e, id) => {
4639
+ var _a;
4640
+ e.stopPropagation();
4641
+ e.preventDefault();
4642
+ const tagToRemove = tags.find((t) => t.id === id);
4643
+ if (!tagToRemove) return;
4644
+ setTags((prev) => prev.filter((t) => t.id !== id));
4645
+ clearSelection();
4646
+ try {
4647
+ await ((_a = options.onTagRemoved) == null ? void 0 : _a.call(options, id));
4648
+ } catch (error2) {
4649
+ console.error("Failed to remove tag:", error2);
4650
+ setTags((prev) => [...prev, tagToRemove]);
4651
+ }
4652
+ };
4653
+ const updateTag = async (id, categoryId, details) => {
4654
+ var _a, _b, _c, _d;
4655
+ const tagToUpdate = tags.find((t) => t.id === id);
4656
+ if (!tagToUpdate) return;
4657
+ const updatedFields = __spreadValues({}, details);
4658
+ if (categoryId) {
4659
+ updatedFields.category_id = categoryId;
4660
+ if (categoryId === "PROP" || categoryId === "SET_PROP") {
4661
+ updatedFields.quantity = (_a = tagToUpdate.quantity) != null ? _a : 1;
4662
+ } else if (categoryId === "CAST") {
4663
+ updatedFields.look = (_b = tagToUpdate.look) != null ? _b : "";
4664
+ updatedFields.age = (_c = tagToUpdate.age) != null ? _c : "";
4565
4665
  }
4566
- )
4567
- ] });
4568
- };
4666
+ }
4667
+ setTags(
4668
+ (prev) => prev.map((t) => {
4669
+ if (t.id === id) return __spreadValues(__spreadValues({}, t), updatedFields);
4670
+ if (details && t.name === tagToUpdate.name && t.category_id === tagToUpdate.category_id) {
4671
+ return __spreadValues(__spreadValues({}, t), details);
4672
+ }
4673
+ return t;
4674
+ })
4675
+ );
4676
+ if (categoryId) clearSelection();
4677
+ try {
4678
+ await ((_d = options.onTagUpdated) == null ? void 0 : _d.call(options, id, categoryId || null, details));
4679
+ } catch (error2) {
4680
+ console.error("Failed to update tag:", error2);
4681
+ setTags((prev) => prev.map((t) => t.id === id ? tagToUpdate : t));
4682
+ }
4683
+ };
4684
+ return {
4685
+ scene,
4686
+ blocks,
4687
+ characters,
4688
+ isLoading,
4689
+ error,
4690
+ tags,
4691
+ selectionMenu,
4692
+ handleMouseUp,
4693
+ addTag,
4694
+ updateTag,
4695
+ removeTag,
4696
+ clearSelection,
4697
+ menuPlacement,
4698
+ menuRef,
4699
+ sceneBrief,
4700
+ setSceneBrief,
4701
+ handleAISummarize,
4702
+ isSummarizing
4703
+ };
4704
+ }
4705
+ function ModalLayout({
4706
+ children,
4707
+ onClose,
4708
+ title,
4709
+ maxWidth = "max-w-4xl",
4710
+ menuRef
4711
+ }) {
4712
+ useEffect(() => {
4713
+ document.body.style.overflow = "hidden";
4714
+ return () => {
4715
+ document.body.style.overflow = "";
4716
+ };
4717
+ }, []);
4718
+ return /* @__PURE__ */ jsxs(
4719
+ "div",
4720
+ {
4721
+ ref: menuRef,
4722
+ onClick: onClose,
4723
+ className: "fixed inset-0 z-50 overflow-y-auto bg-black/40 backdrop-blur-xs",
4724
+ "data-lenis-prevent": "true",
4725
+ children: [
4726
+ /* @__PURE__ */ jsx("div", { className: "flex min-h-full items-center justify-center p-4 sm:p-6", children: /* @__PURE__ */ jsxs(
4727
+ "div",
4728
+ {
4729
+ onClick: (e) => e.stopPropagation(),
4730
+ className: `relative w-full ${maxWidth} bg-white rounded-3xl shadow-2xl transform transition-all duration-300 scale-100 opacity-100 animate-modal`,
4731
+ children: [
4732
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b border-gray-200 pb-3 pt-6 px-6", children: [
4733
+ /* @__PURE__ */ jsx("h2", { className: "text-xl sm:text-2xl font-medium text-blumine-600 line-clamp-1", children: title }),
4734
+ /* @__PURE__ */ jsx(
4735
+ "button",
4736
+ {
4737
+ onClick: onClose,
4738
+ className: "w-10 h-10 flex items-center justify-center rounded-full border border-blumine-600 text-blumine-600 hover:bg-blumine-600/10 hover:rotate-90 transition-all duration-300",
4739
+ "aria-label": "Close",
4740
+ children: /* @__PURE__ */ jsx(X, { size: 20 })
4741
+ }
4742
+ )
4743
+ ] }),
4744
+ children
4745
+ ]
4746
+ }
4747
+ ) }),
4748
+ /* @__PURE__ */ jsx("style", { children: `
4749
+ @keyframes modal {
4750
+ 0% {
4751
+ opacity: 0;
4752
+ transform: translateY(20px) scale(0.95);
4753
+ }
4754
+ 100% {
4755
+ opacity: 1;
4756
+ transform: translateY(0) scale(1);
4757
+ }
4758
+ }
4759
+ .animate-modal {
4760
+ animation: modal 0.3s ease-out forwards;
4761
+ }
4762
+ ` })
4763
+ ]
4764
+ }
4765
+ );
4766
+ }
4767
+ var modal_layout_default = ModalLayout;
4569
4768
 
4570
4769
  // data/shot-data.ts
4571
4770
  var shot_types = [
@@ -5326,22 +5525,32 @@ var ProductionSetupModal = ({
5326
5525
  initialValues
5327
5526
  }) => {
5328
5527
  return /* @__PURE__ */ jsx("div", { className: "min-h-[80vh] flex items-center justify-center px-6", children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-lg", children: [
5329
- /* @__PURE__ */ jsxs("div", { className: "mb-8 text-center", children: [
5528
+ /* @__PURE__ */ jsxs("div", { className: "my-8 text-center", children: [
5330
5529
  /* @__PURE__ */ jsx("h1", { className: "text-2xl md:text-3xl font-semibold text-slate-900 tracking-tight", children: "Production Setup for this Scene" }),
5331
5530
  /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500 mt-2", children: "Configure your scene and camera setup to get started" })
5332
5531
  ] }),
5333
5532
  /* @__PURE__ */ jsx("div", { className: "p-6 md:p-8", children: /* @__PURE__ */ jsx(
5334
5533
  Formik,
5335
5534
  {
5336
- initialValues: initialValues || { numCameras: 1, scene_type: "" },
5535
+ initialValues: initialValues || {
5536
+ numCameras: 1,
5537
+ scene_type: "",
5538
+ dance_choreographer_required: false,
5539
+ action_sequence_required: false
5540
+ },
5337
5541
  validationSchema: Yup.object({
5338
5542
  numCameras: Yup.number().min(1, "Must be at least 1").max(20, "Cannot add more than 20 cameras").required("Number of cameras is required"),
5339
5543
  scene_type: Yup.string().required("Scene type is required")
5340
5544
  }),
5341
5545
  onSubmit: async (values) => {
5342
- await initializeProduction(values.numCameras, values.scene_type);
5546
+ await initializeProduction(
5547
+ values.numCameras,
5548
+ values.scene_type,
5549
+ values.dance_choreographer_required,
5550
+ values.action_sequence_required
5551
+ );
5343
5552
  },
5344
- children: ({ isSubmitting, setFieldValue }) => /* @__PURE__ */ jsxs(Form, { className: "flex flex-col gap-6", children: [
5553
+ children: ({ isSubmitting, setFieldValue, values }) => /* @__PURE__ */ jsxs(Form, { className: "flex flex-col gap-6", children: [
5345
5554
  /* @__PURE__ */ jsxs("div", { className: "grid gap-5", children: [
5346
5555
  /* @__PURE__ */ jsx(
5347
5556
  FormikSelect,
@@ -5364,7 +5573,65 @@ var ProductionSetupModal = ({
5364
5573
  max: 20,
5365
5574
  enableRedAsterick: true
5366
5575
  }
5367
- )
5576
+ ),
5577
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
5578
+ /* @__PURE__ */ jsx(
5579
+ "div",
5580
+ {
5581
+ className: `relative p-5 rounded-3xl border transition-all duration-300 flex flex-col gap-4 group cursor-pointer ${values.dance_choreographer_required ? "bg-blumine-50/30 border-blumine-200 shadow-sm" : "bg-slate-50/50 border-slate-100 hover:border-slate-200"}`,
5582
+ onClick: () => setFieldValue(
5583
+ "dance_choreographer_required",
5584
+ !values.dance_choreographer_required
5585
+ ),
5586
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between w-full", children: [
5587
+ /* @__PURE__ */ jsxs("div", { children: [
5588
+ /* @__PURE__ */ jsx("p", { className: "text-[15px] font-bold text-slate-800", children: "Dance Sequence" }),
5589
+ /* @__PURE__ */ jsx("p", { className: "text-[11px] text-slate-500 font-medium mt-0.5", children: "Choreography needed" })
5590
+ ] }),
5591
+ /* @__PURE__ */ jsx(
5592
+ Switch,
5593
+ {
5594
+ isOn: values.dance_choreographer_required || false,
5595
+ handleToggle: () => setFieldValue(
5596
+ "dance_choreographer_required",
5597
+ !values.dance_choreographer_required
5598
+ ),
5599
+ activeColor: "bg-blumine-600",
5600
+ divClasses: "w-fit!"
5601
+ }
5602
+ )
5603
+ ] })
5604
+ }
5605
+ ),
5606
+ /* @__PURE__ */ jsx(
5607
+ "div",
5608
+ {
5609
+ className: `relative p-5 rounded-3xl border transition-all duration-300 flex flex-col gap-4 group cursor-pointer ${values.action_sequence_required ? "bg-rose-50/30 border-rose-200 shadow-sm" : "bg-slate-50/50 border-slate-100 hover:border-slate-200"}`,
5610
+ onClick: () => setFieldValue(
5611
+ "action_sequence_required",
5612
+ !values.action_sequence_required
5613
+ ),
5614
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between w-full", children: [
5615
+ /* @__PURE__ */ jsxs("div", { children: [
5616
+ /* @__PURE__ */ jsx("p", { className: "text-[15px] font-bold text-slate-800", children: "Action Sequence" }),
5617
+ /* @__PURE__ */ jsx("p", { className: "text-[11px] text-slate-500 font-medium mt-0.5", children: "Stunt & combat logic" })
5618
+ ] }),
5619
+ /* @__PURE__ */ jsx(
5620
+ Switch,
5621
+ {
5622
+ isOn: values.action_sequence_required || false,
5623
+ handleToggle: () => setFieldValue(
5624
+ "action_sequence_required",
5625
+ !values.action_sequence_required
5626
+ ),
5627
+ activeColor: "bg-rose-500",
5628
+ divClasses: "w-fit!"
5629
+ }
5630
+ )
5631
+ ] })
5632
+ }
5633
+ )
5634
+ ] })
5368
5635
  ] }),
5369
5636
  /* @__PURE__ */ jsxs("div", { className: "flex gap-3 bg-slate-50 border border-slate-100 p-4 rounded-xl", children: [
5370
5637
  /* @__PURE__ */ jsx(Info, { className: "w-5 h-5 text-slate-400 shrink-0 mt-0.5" }),
@@ -5906,14 +6173,21 @@ function ShotBreakdownView({
5906
6173
  children: /* @__PURE__ */ jsx(
5907
6174
  production_setup_modal_default,
5908
6175
  {
5909
- initializeProduction: (count, type) => {
5910
- initializeProduction(count, type).then(() => {
6176
+ initializeProduction: (count, type, dance_choreographer_required, action_sequence_required) => {
6177
+ initializeProduction(
6178
+ count,
6179
+ type,
6180
+ dance_choreographer_required,
6181
+ action_sequence_required
6182
+ ).then(() => {
5911
6183
  setIsInitModalOpen(false);
5912
6184
  });
5913
6185
  },
5914
6186
  initialValues: {
5915
6187
  numCameras: cameras.length || 1,
5916
- scene_type: sceneType
6188
+ scene_type: sceneType,
6189
+ dance_choreographer_required: false,
6190
+ action_sequence_required: false
5917
6191
  }
5918
6192
  }
5919
6193
  )
@@ -6129,13 +6403,19 @@ function useShotBreakdownScene(options) {
6129
6403
  });
6130
6404
  }
6131
6405
  };
6132
- const initializeProduction = async (count, type) => {
6406
+ const initializeProduction = async (count, type, dance_choreographer_required, action_sequence_required) => {
6133
6407
  var _a;
6134
6408
  const newCameras = [];
6135
6409
  for (let i = 1; i <= count; i++) {
6136
6410
  newCameras.push({ name: `Camera ${String.fromCharCode(64 + i)}` });
6137
6411
  }
6138
- const result = (_a = options.onProductionInitialized) == null ? void 0 : _a.call(options, newCameras, type);
6412
+ const result = (_a = options.onProductionInitialized) == null ? void 0 : _a.call(
6413
+ options,
6414
+ newCameras,
6415
+ type,
6416
+ dance_choreographer_required,
6417
+ action_sequence_required
6418
+ );
6139
6419
  if (result instanceof Promise) {
6140
6420
  setCameras(newCameras);
6141
6421
  setSceneType(type);
@@ -6259,7 +6539,180 @@ function useShotBreakdownScene(options) {
6259
6539
  menuRef
6260
6540
  };
6261
6541
  }
6542
+ function SceneScriptView({
6543
+ blocks,
6544
+ isLoading = false,
6545
+ sceneNumber,
6546
+ title
6547
+ }) {
6548
+ const COURIER_STACK = "'Courier Prime', 'Courier', monospace";
6549
+ useEffect(() => {
6550
+ const fontId = "google-font-courier-prime";
6551
+ const styleId = "screenplay-editor-force-v4";
6552
+ if (!document.getElementById(fontId)) {
6553
+ const link = document.createElement("link");
6554
+ link.id = fontId;
6555
+ link.rel = "stylesheet";
6556
+ link.href = "https://fonts.googleapis.com/css2?family=Courier+Prime:ital,wght@0,400;0,700;1,400;1,700&display=swap";
6557
+ document.head.appendChild(link);
6558
+ }
6559
+ if (!document.getElementById(styleId)) {
6560
+ const style = document.createElement("style");
6561
+ style.id = styleId;
6562
+ style.textContent = `
6563
+ [data-scene-script-view] *,
6564
+ [data-scene-script-view] div,
6565
+ [data-scene-script-view] span {
6566
+ font-family: ${COURIER_STACK} !important;
6567
+ -webkit-font-smoothing: antialiased;
6568
+ }
6569
+ `;
6570
+ document.head.appendChild(style);
6571
+ }
6572
+ }, [COURIER_STACK]);
6573
+ if (isLoading) {
6574
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-32 gap-4", children: [
6575
+ /* @__PURE__ */ jsx(Loader2, { className: "w-8 h-8 animate-spin text-zinc-400" }),
6576
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-zinc-500 animate-pulse", children: "Loading script..." })
6577
+ ] });
6578
+ }
6579
+ return /* @__PURE__ */ jsx(
6580
+ "div",
6581
+ {
6582
+ className: "relative bg-[#fdfdfc] shadow-[0_10px_30px_rgba(0,0,0,0.04),0_1px_8px_rgba(0,0,0,0.02)] border border-zinc-100 rounded-sm md:rounded-md flex flex-col w-full max-w-[210mm] min-h-auto shrink-0 pb-24",
6583
+ style: {
6584
+ fontFamily: COURIER_STACK,
6585
+ paddingLeft: "1.5in",
6586
+ paddingRight: "1in",
6587
+ paddingTop: "1in",
6588
+ paddingBottom: "1in",
6589
+ lineHeight: "1.2"
6590
+ },
6591
+ "data-scene-script-view": "true",
6592
+ children: blocks.map((block) => /* @__PURE__ */ jsx(
6593
+ "div",
6594
+ {
6595
+ className: `relative break-words w-full px-4 py-2 ${blockStyles[block.type].className}`,
6596
+ style: __spreadProps(__spreadValues({}, blockStyles[block.type].inputStyle), {
6597
+ minHeight: "1.5rem"
6598
+ }),
6599
+ children: block.type === "SCENE_HEADING" ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
6600
+ block.sceneNumber && /* @__PURE__ */ jsx("span", { className: "absolute -left-16 text-zinc-400 font-bold select-none", children: block.sceneNumber }),
6601
+ block.sceneType && /* @__PURE__ */ jsx("span", { className: "font-bold", children: block.sceneType }),
6602
+ /* @__PURE__ */ jsx("span", { className: "font-bold", children: block.text }),
6603
+ block.timeOfDay && /* @__PURE__ */ jsxs(Fragment, { children: [
6604
+ /* @__PURE__ */ jsx("span", { className: "text-zinc-400", children: "-" }),
6605
+ /* @__PURE__ */ jsx("span", { className: "font-bold", children: block.timeOfDay })
6606
+ ] })
6607
+ ] }) : block.text
6608
+ },
6609
+ block.id
6610
+ ))
6611
+ }
6612
+ );
6613
+ }
6614
+ function useSceneScript(options) {
6615
+ const [sceneContent, setSceneContent] = useState(null);
6616
+ const [isLoading, setIsLoading] = useState(true);
6617
+ const [error, setError] = useState(null);
6618
+ useEffect(() => {
6619
+ let isMounted = true;
6620
+ setIsLoading(true);
6621
+ setError(null);
6622
+ const fetchScene = async () => {
6623
+ try {
6624
+ const response = await fetch(options.scene_url, options.fetchOptions);
6625
+ if (response.ok) {
6626
+ const text = await response.text();
6627
+ if (isMounted) {
6628
+ setSceneContent(text);
6629
+ }
6630
+ } else {
6631
+ if (isMounted) {
6632
+ setError(`Failed to fetch scene: ${response.statusText}`);
6633
+ }
6634
+ }
6635
+ } catch (err) {
6636
+ if (isMounted) {
6637
+ setError(err instanceof Error ? err.message : "An unknown error occurred");
6638
+ }
6639
+ } finally {
6640
+ if (isMounted) {
6641
+ setIsLoading(false);
6642
+ }
6643
+ }
6644
+ };
6645
+ fetchScene();
6646
+ return () => {
6647
+ isMounted = false;
6648
+ };
6649
+ }, [options.scene_url]);
6650
+ const blocks = useMemo(() => {
6651
+ if (!sceneContent) return [];
6652
+ const parser = new DOMParser();
6653
+ const doc = parser.parseFromString(sceneContent, "text/html");
6654
+ const divs = Array.from(doc.querySelectorAll("div"));
6655
+ const parsedBlocks = [];
6656
+ const typeMap = {
6657
+ divtype0: "SCENE_HEADING",
6658
+ divtype2: "ACTION",
6659
+ divtype3: "CHARACTER",
6660
+ divtype4: "PARENTHETICAL",
6661
+ divtype5: "DIALOGUE",
6662
+ divtype6: "TRANSITION"
6663
+ };
6664
+ divs.forEach((div) => {
6665
+ var _a;
6666
+ const divText = ((_a = div.textContent) == null ? void 0 : _a.trim()) || "";
6667
+ if (!divText && !div.classList.contains("divtype0")) return;
6668
+ let type = "ACTION";
6669
+ for (const className of Array.from(div.classList)) {
6670
+ if (typeMap[className]) {
6671
+ type = typeMap[className];
6672
+ break;
6673
+ }
6674
+ }
6675
+ const idAttr = div.getAttribute("id");
6676
+ const blockId = idAttr && idAttr.startsWith("par") ? idAttr.substring(3) : idAttr || uuid();
6677
+ let sceneNumber;
6678
+ let sceneType;
6679
+ let timeOfDay;
6680
+ let blockText = divText;
6681
+ if (type === "SCENE_HEADING") {
6682
+ const typeMatch = blockText.match(/^(INT\.?\/EXT\.?|INT\.?|EXT\.?|I\/E)\s+/i);
6683
+ if (typeMatch) {
6684
+ const matchedType = typeMatch[1].toUpperCase();
6685
+ if (matchedType.includes("INT") && matchedType.includes("EXT")) sceneType = "INT/EXT.";
6686
+ else if (matchedType.includes("EXT")) sceneType = "EXT.";
6687
+ else sceneType = "INT.";
6688
+ blockText = blockText.substring(typeMatch[0].length).trim();
6689
+ }
6690
+ const timeMatch = blockText.match(/\s+-\s+(DAY|NIGHT|MORNING|EVENING|AFTERNOON|LATER|MOMENTS LATER|CONTINUOUS)$/i);
6691
+ if (timeMatch) {
6692
+ timeOfDay = timeMatch[1].toUpperCase();
6693
+ blockText = blockText.substring(0, timeMatch.index).trim();
6694
+ }
6695
+ blockText = blockText.replace(/^-+\s*|\s*-+$/g, "").trim();
6696
+ }
6697
+ parsedBlocks.push({
6698
+ id: blockId,
6699
+ type,
6700
+ text: blockText,
6701
+ sceneNumber,
6702
+ sceneType,
6703
+ timeOfDay
6704
+ });
6705
+ });
6706
+ return parsedBlocks;
6707
+ }, [sceneContent]);
6708
+ return {
6709
+ blocks,
6710
+ isLoading,
6711
+ error,
6712
+ sceneContent
6713
+ };
6714
+ }
6262
6715
 
6263
- export { CATEGORIES, ScreenplayEditorView, ScriptBreakdownSceneView, ShotBreakdownView, aperture_options, blockStyles, blockTypes, camera_angles, camera_movements, camera_supports, convertBlocksToSbx, fps_options, handleSaveAsPdf, handleSyncWithCloud, icons, lens_options, scene_types, shot_types, timeOfDayOptions, useScreenplayEditor, useScriptBreakdownScene, useShotBreakdownScene, uuid, vfx_types };
6716
+ export { CATEGORIES, SceneScriptView, ScreenplayEditorView, ScriptBreakdownSceneView, ShotBreakdownView, aperture_options, blockStyles, blockTypes, camera_angles, camera_movements, camera_supports, convertBlocksToSbx, fps_options, handleSaveAsPdf, handleSyncWithCloud, icons, lens_options, scene_types, shot_types, timeOfDayOptions, useSceneScript, useScreenplayEditor, useScriptBreakdownScene, useShotBreakdownScene, uuid, vfx_types };
6264
6717
  //# sourceMappingURL=index.js.map
6265
6718
  //# sourceMappingURL=index.js.map