@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.cjs +1390 -934
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -4
- package/dist/index.d.ts +28 -4
- package/dist/index.js +1680 -1227
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var React4 = require('react');
|
|
4
4
|
var lucideReact = require('lucide-react');
|
|
5
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
6
|
var pdfjs = require('pdfjs-dist');
|
|
7
7
|
var jsPDF = require('jspdf');
|
|
8
8
|
var html2canvas = require('html2canvas-pro');
|
|
9
|
-
var Yup = require('yup');
|
|
10
9
|
var formik = require('formik');
|
|
10
|
+
var Yup = require('yup');
|
|
11
11
|
|
|
12
12
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
13
|
|
|
@@ -29,6 +29,7 @@ function _interopNamespace(e) {
|
|
|
29
29
|
return Object.freeze(n);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
var React4__default = /*#__PURE__*/_interopDefault(React4);
|
|
32
33
|
var pdfjs__namespace = /*#__PURE__*/_interopNamespace(pdfjs);
|
|
33
34
|
var jsPDF__default = /*#__PURE__*/_interopDefault(jsPDF);
|
|
34
35
|
var html2canvas__default = /*#__PURE__*/_interopDefault(html2canvas);
|
|
@@ -817,9 +818,9 @@ function PhoneticSuggestions({
|
|
|
817
818
|
}
|
|
818
819
|
pdfjs__namespace.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs__namespace.version}/build/pdf.worker.min.mjs`;
|
|
819
820
|
function PdfImporter({ onScriptImported, disabled, children }) {
|
|
820
|
-
const [isProcessing, setIsProcessing] =
|
|
821
|
-
const [error, setError] =
|
|
822
|
-
const fileInputRef =
|
|
821
|
+
const [isProcessing, setIsProcessing] = React4.useState(false);
|
|
822
|
+
const [error, setError] = React4.useState(null);
|
|
823
|
+
const fileInputRef = React4.useRef(null);
|
|
823
824
|
const handleFileChange = async (event) => {
|
|
824
825
|
var _a;
|
|
825
826
|
const file = (_a = event.target.files) == null ? void 0 : _a[0];
|
|
@@ -975,12 +976,12 @@ function PhoneticGuide({
|
|
|
975
976
|
isOpen,
|
|
976
977
|
onClose
|
|
977
978
|
}) {
|
|
978
|
-
const [activeTab, setActiveTab] =
|
|
979
|
-
const [searchQuery, setSearchQuery] =
|
|
979
|
+
const [activeTab, setActiveTab] = React4.useState("vowels");
|
|
980
|
+
const [searchQuery, setSearchQuery] = React4.useState("");
|
|
980
981
|
const script = language.includes("Hindi") ? "Hindi" : "Gujarati";
|
|
981
982
|
const vowelsData = VOWEL_FORMS[script] || {};
|
|
982
983
|
const config = LANGUAGE_CONFIGS[language];
|
|
983
|
-
const consonants =
|
|
984
|
+
const consonants = React4.useMemo(() => {
|
|
984
985
|
if (!config || language === "English") return [];
|
|
985
986
|
return Object.entries(config.mapping).filter(([_, val]) => {
|
|
986
987
|
const isVowelMatra = Object.values(vowelsData).some(
|
|
@@ -994,7 +995,7 @@ function PhoneticGuide({
|
|
|
994
995
|
shiftChar: val.shift
|
|
995
996
|
}));
|
|
996
997
|
}, [language, vowelsData, config]);
|
|
997
|
-
const symbols =
|
|
998
|
+
const symbols = React4.useMemo(() => {
|
|
998
999
|
if (!config || language === "English") return [];
|
|
999
1000
|
return Object.entries(config.mapping).filter(([_, val]) => /[.,/;:'"\[\]{}\\=`\-_]/.test(val.default)).map(([key, val]) => ({
|
|
1000
1001
|
key: key.replace("Key", "").toLowerCase(),
|
|
@@ -1286,13 +1287,13 @@ function ScreenplayEditorView({
|
|
|
1286
1287
|
phoneticSuggestions,
|
|
1287
1288
|
handleSelectPhoneticSuggestion
|
|
1288
1289
|
}) {
|
|
1289
|
-
const [isRulesOpen, setIsRulesOpen] =
|
|
1290
|
-
const [isLanguageOpen, setIsLanguageOpen] =
|
|
1291
|
-
const [isPhoneticGuideOpen, setIsPhoneticGuideOpen] =
|
|
1292
|
-
const rulesRef =
|
|
1293
|
-
const languageRef =
|
|
1290
|
+
const [isRulesOpen, setIsRulesOpen] = React4.useState(false);
|
|
1291
|
+
const [isLanguageOpen, setIsLanguageOpen] = React4.useState(false);
|
|
1292
|
+
const [isPhoneticGuideOpen, setIsPhoneticGuideOpen] = React4.useState(false);
|
|
1293
|
+
const rulesRef = React4.useRef(null);
|
|
1294
|
+
const languageRef = React4.useRef(null);
|
|
1294
1295
|
const COURIER_STACK = "'Courier Prime', 'Courier', monospace";
|
|
1295
|
-
|
|
1296
|
+
React4.useEffect(() => {
|
|
1296
1297
|
const handleClickOutside = (event) => {
|
|
1297
1298
|
if (rulesRef.current && !rulesRef.current.contains(event.target)) {
|
|
1298
1299
|
setIsRulesOpen(false);
|
|
@@ -1308,7 +1309,7 @@ function ScreenplayEditorView({
|
|
|
1308
1309
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
1309
1310
|
};
|
|
1310
1311
|
}, [isRulesOpen, isLanguageOpen]);
|
|
1311
|
-
|
|
1312
|
+
React4.useEffect(() => {
|
|
1312
1313
|
const fontId = "google-font-courier-prime";
|
|
1313
1314
|
const styleId = "screenplay-editor-force-v4";
|
|
1314
1315
|
if (!document.getElementById(fontId)) {
|
|
@@ -2223,49 +2224,49 @@ function createRangeFromGlobalOffset(element, startOffset, endOffset) {
|
|
|
2223
2224
|
return null;
|
|
2224
2225
|
}
|
|
2225
2226
|
function useScreenplayEditor(options) {
|
|
2226
|
-
const [blocks, setBlocks] =
|
|
2227
|
-
const refs =
|
|
2228
|
-
const [focusedBlockId, setFocusedBlockId] =
|
|
2227
|
+
const [blocks, setBlocks] = React4.useState(initialBlocks);
|
|
2228
|
+
const refs = React4.useRef({});
|
|
2229
|
+
const [focusedBlockId, setFocusedBlockId] = React4.useState(
|
|
2229
2230
|
initialBlocks[0].id
|
|
2230
2231
|
);
|
|
2231
|
-
const [newBlockId, setNewBlockId] =
|
|
2232
|
-
const [showSuggestions, setShowSuggestions] =
|
|
2233
|
-
const [showExtensionSuggestions, setShowExtensionSuggestions] =
|
|
2234
|
-
const [isLoading, setIsLoading] =
|
|
2235
|
-
const [enhancingBlockId, setEnhancingBlockId] =
|
|
2236
|
-
const [enhancementSuggestion, setEnhancementSuggestion] =
|
|
2237
|
-
const [isEnhancing, setIsEnhancing] =
|
|
2238
|
-
const [hasUnsavedChanges, setHasUnsavedChanges] =
|
|
2239
|
-
const [showUnsavedPopover, setShowUnsavedPopover] =
|
|
2240
|
-
const popoverTimeoutRef =
|
|
2241
|
-
const blurTimeout =
|
|
2242
|
-
const isInitialLoadRef =
|
|
2243
|
-
const loadedUrlRef =
|
|
2244
|
-
const lastSavedContent =
|
|
2245
|
-
const onSaveRef =
|
|
2246
|
-
const onSyncWithCloudRef =
|
|
2247
|
-
const [currentLanguage, setCurrentLanguage] =
|
|
2248
|
-
const [lastPhoneticAction, setLastPhoneticAction] =
|
|
2249
|
-
const [phoneticSuggestions, setPhoneticSuggestions] =
|
|
2250
|
-
|
|
2232
|
+
const [newBlockId, setNewBlockId] = React4.useState(null);
|
|
2233
|
+
const [showSuggestions, setShowSuggestions] = React4.useState(false);
|
|
2234
|
+
const [showExtensionSuggestions, setShowExtensionSuggestions] = React4.useState(false);
|
|
2235
|
+
const [isLoading, setIsLoading] = React4.useState(!!(options == null ? void 0 : options.initialUrl));
|
|
2236
|
+
const [enhancingBlockId, setEnhancingBlockId] = React4.useState(null);
|
|
2237
|
+
const [enhancementSuggestion, setEnhancementSuggestion] = React4.useState(null);
|
|
2238
|
+
const [isEnhancing, setIsEnhancing] = React4.useState(false);
|
|
2239
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = React4.useState(false);
|
|
2240
|
+
const [showUnsavedPopover, setShowUnsavedPopover] = React4.useState(false);
|
|
2241
|
+
const popoverTimeoutRef = React4.useRef(null);
|
|
2242
|
+
const blurTimeout = React4.useRef(null);
|
|
2243
|
+
const isInitialLoadRef = React4.useRef(true);
|
|
2244
|
+
const loadedUrlRef = React4.useRef(null);
|
|
2245
|
+
const lastSavedContent = React4.useRef(null);
|
|
2246
|
+
const onSaveRef = React4.useRef(options == null ? void 0 : options.onSave);
|
|
2247
|
+
const onSyncWithCloudRef = React4.useRef(options == null ? void 0 : options.onSyncWithCloud);
|
|
2248
|
+
const [currentLanguage, setCurrentLanguage] = React4.useState("English");
|
|
2249
|
+
const [lastPhoneticAction, setLastPhoneticAction] = React4.useState(null);
|
|
2250
|
+
const [phoneticSuggestions, setPhoneticSuggestions] = React4.useState([]);
|
|
2251
|
+
React4.useEffect(() => {
|
|
2251
2252
|
setLastPhoneticAction(null);
|
|
2252
2253
|
setPhoneticSuggestions([]);
|
|
2253
2254
|
}, [currentLanguage]);
|
|
2254
|
-
const phoneticEngine =
|
|
2255
|
+
const phoneticEngine = React4.useMemo(() => {
|
|
2255
2256
|
if (currentLanguage.includes("Phonetic")) {
|
|
2256
2257
|
return new PhoneticEngine(currentLanguage);
|
|
2257
2258
|
}
|
|
2258
2259
|
return null;
|
|
2259
2260
|
}, [currentLanguage]);
|
|
2260
|
-
|
|
2261
|
+
React4.useEffect(() => {
|
|
2261
2262
|
onSaveRef.current = options == null ? void 0 : options.onSave;
|
|
2262
2263
|
onSyncWithCloudRef.current = options == null ? void 0 : options.onSyncWithCloud;
|
|
2263
2264
|
}, [options == null ? void 0 : options.onSave, options == null ? void 0 : options.onSyncWithCloud]);
|
|
2264
|
-
const characterExtensions =
|
|
2265
|
+
const characterExtensions = React4.useMemo(
|
|
2265
2266
|
() => ["(V.O.)", "(O.S.)", "(O.C.)", "(SUBTITLE)", "(CONT'D)"],
|
|
2266
2267
|
[]
|
|
2267
2268
|
);
|
|
2268
|
-
const handleSceneNumberChange =
|
|
2269
|
+
const handleSceneNumberChange = React4.useCallback(
|
|
2269
2270
|
(id, newNumber) => {
|
|
2270
2271
|
setBlocks(
|
|
2271
2272
|
(prevBlocks) => prevBlocks.map(
|
|
@@ -2275,11 +2276,11 @@ function useScreenplayEditor(options) {
|
|
|
2275
2276
|
},
|
|
2276
2277
|
[]
|
|
2277
2278
|
);
|
|
2278
|
-
const locations =
|
|
2279
|
+
const locations = React4.useMemo(() => {
|
|
2279
2280
|
const locs = blocks.filter((b) => b.type === "SCENE_HEADING" && b.text.trim() !== "").map((b) => b.text.trim().toUpperCase());
|
|
2280
2281
|
return [...new Set(locs)];
|
|
2281
2282
|
}, [blocks]);
|
|
2282
|
-
const characters =
|
|
2283
|
+
const characters = React4.useMemo(() => {
|
|
2283
2284
|
const chars = blocks.filter((b) => b.type === "CHARACTER" && b.text.trim() !== "").map((b) => {
|
|
2284
2285
|
const text = b.text.trim().toUpperCase();
|
|
2285
2286
|
const parenIndex = text.indexOf("(");
|
|
@@ -2290,7 +2291,7 @@ function useScreenplayEditor(options) {
|
|
|
2290
2291
|
}).filter(Boolean);
|
|
2291
2292
|
return [...new Set(chars)];
|
|
2292
2293
|
}, [blocks]);
|
|
2293
|
-
const sceneNumbers =
|
|
2294
|
+
const sceneNumbers = React4.useMemo(() => {
|
|
2294
2295
|
const map = {};
|
|
2295
2296
|
let fallbackCount = 0;
|
|
2296
2297
|
blocks.forEach((block) => {
|
|
@@ -2307,7 +2308,7 @@ function useScreenplayEditor(options) {
|
|
|
2307
2308
|
});
|
|
2308
2309
|
return map;
|
|
2309
2310
|
}, [blocks]);
|
|
2310
|
-
|
|
2311
|
+
React4.useEffect(() => {
|
|
2311
2312
|
if (newBlockId && refs.current[newBlockId]) {
|
|
2312
2313
|
const block = blocks.find((b) => b.id === newBlockId);
|
|
2313
2314
|
const el = refs.current[newBlockId];
|
|
@@ -2324,7 +2325,7 @@ function useScreenplayEditor(options) {
|
|
|
2324
2325
|
setNewBlockId(null);
|
|
2325
2326
|
}
|
|
2326
2327
|
}, [newBlockId, blocks]);
|
|
2327
|
-
|
|
2328
|
+
React4.useEffect(() => {
|
|
2328
2329
|
blocks.forEach((block) => {
|
|
2329
2330
|
const element = refs.current[block.id];
|
|
2330
2331
|
if (element) {
|
|
@@ -2334,7 +2335,7 @@ function useScreenplayEditor(options) {
|
|
|
2334
2335
|
}
|
|
2335
2336
|
});
|
|
2336
2337
|
}, [blocks]);
|
|
2337
|
-
|
|
2338
|
+
React4.useEffect(() => {
|
|
2338
2339
|
const handleClickOutside = (e) => {
|
|
2339
2340
|
const target = e.target;
|
|
2340
2341
|
const isInsideBlock = target.closest("[data-block-id]");
|
|
@@ -2352,7 +2353,7 @@ function useScreenplayEditor(options) {
|
|
|
2352
2353
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
2353
2354
|
};
|
|
2354
2355
|
}, []);
|
|
2355
|
-
const handleBlockTextChange =
|
|
2356
|
+
const handleBlockTextChange = React4.useCallback(
|
|
2356
2357
|
(id, text) => {
|
|
2357
2358
|
const block = blocks.find((b) => b.id === id);
|
|
2358
2359
|
if (!block) return;
|
|
@@ -2395,7 +2396,7 @@ function useScreenplayEditor(options) {
|
|
|
2395
2396
|
},
|
|
2396
2397
|
[blocks, showExtensionSuggestions]
|
|
2397
2398
|
);
|
|
2398
|
-
const handleSceneTypeChange =
|
|
2399
|
+
const handleSceneTypeChange = React4.useCallback(
|
|
2399
2400
|
(id, sceneType) => {
|
|
2400
2401
|
setBlocks(
|
|
2401
2402
|
(bs) => updateBlock(bs, id, "sceneType", sceneType)
|
|
@@ -2408,7 +2409,7 @@ function useScreenplayEditor(options) {
|
|
|
2408
2409
|
},
|
|
2409
2410
|
[]
|
|
2410
2411
|
);
|
|
2411
|
-
const handleTimeOfDayChange =
|
|
2412
|
+
const handleTimeOfDayChange = React4.useCallback((id, time) => {
|
|
2412
2413
|
setBlocks((bs) => updateBlock(bs, id, "timeOfDay", time));
|
|
2413
2414
|
setTimeout(() => {
|
|
2414
2415
|
var _a;
|
|
@@ -2416,7 +2417,7 @@ function useScreenplayEditor(options) {
|
|
|
2416
2417
|
setFocusedBlockId(id);
|
|
2417
2418
|
}, 10);
|
|
2418
2419
|
}, []);
|
|
2419
|
-
const handleBlockTypeChange =
|
|
2420
|
+
const handleBlockTypeChange = React4.useCallback(
|
|
2420
2421
|
(newType) => {
|
|
2421
2422
|
if (!focusedBlockId) return;
|
|
2422
2423
|
setBlocks(
|
|
@@ -2442,7 +2443,7 @@ function useScreenplayEditor(options) {
|
|
|
2442
2443
|
},
|
|
2443
2444
|
[focusedBlockId, blocks]
|
|
2444
2445
|
);
|
|
2445
|
-
const handleSelectCharacterExtension =
|
|
2446
|
+
const handleSelectCharacterExtension = React4.useCallback(
|
|
2446
2447
|
(extension) => {
|
|
2447
2448
|
if (!focusedBlockId) return;
|
|
2448
2449
|
setBlocks((currentBlocks) => {
|
|
@@ -2511,7 +2512,7 @@ function useScreenplayEditor(options) {
|
|
|
2511
2512
|
}
|
|
2512
2513
|
}, 10);
|
|
2513
2514
|
};
|
|
2514
|
-
const handleKeyDown =
|
|
2515
|
+
const handleKeyDown = React4.useCallback(
|
|
2515
2516
|
(e, id, text) => {
|
|
2516
2517
|
var _a;
|
|
2517
2518
|
if (currentLanguage !== "English") {
|
|
@@ -2720,7 +2721,7 @@ function useScreenplayEditor(options) {
|
|
|
2720
2721
|
lastPhoneticAction
|
|
2721
2722
|
]
|
|
2722
2723
|
);
|
|
2723
|
-
const handleScriptImport =
|
|
2724
|
+
const handleScriptImport = React4.useCallback(
|
|
2724
2725
|
(title, content, preParsedBlocks, isInitialLoad) => {
|
|
2725
2726
|
let parsedBlocks = [];
|
|
2726
2727
|
if (preParsedBlocks && preParsedBlocks.length > 0) {
|
|
@@ -2773,7 +2774,7 @@ function useScreenplayEditor(options) {
|
|
|
2773
2774
|
},
|
|
2774
2775
|
[refs]
|
|
2775
2776
|
);
|
|
2776
|
-
const handleSelectPhoneticSuggestion =
|
|
2777
|
+
const handleSelectPhoneticSuggestion = React4.useCallback(
|
|
2777
2778
|
(suggestion) => {
|
|
2778
2779
|
if (!focusedBlockId) return;
|
|
2779
2780
|
const el = refs.current[focusedBlockId];
|
|
@@ -2793,7 +2794,7 @@ function useScreenplayEditor(options) {
|
|
|
2793
2794
|
},
|
|
2794
2795
|
[focusedBlockId, handleBlockTextChange]
|
|
2795
2796
|
);
|
|
2796
|
-
const handleFocus =
|
|
2797
|
+
const handleFocus = React4.useCallback(
|
|
2797
2798
|
(id) => {
|
|
2798
2799
|
if (blurTimeout.current) {
|
|
2799
2800
|
clearTimeout(blurTimeout.current);
|
|
@@ -2818,14 +2819,14 @@ function useScreenplayEditor(options) {
|
|
|
2818
2819
|
},
|
|
2819
2820
|
[blocks]
|
|
2820
2821
|
);
|
|
2821
|
-
const handleBlur =
|
|
2822
|
+
const handleBlur = React4.useCallback((id) => {
|
|
2822
2823
|
if (document.activeElement === refs.current[id]) return;
|
|
2823
2824
|
blurTimeout.current = setTimeout(() => {
|
|
2824
2825
|
setShowSuggestions(false);
|
|
2825
2826
|
setShowExtensionSuggestions(false);
|
|
2826
2827
|
}, 200);
|
|
2827
2828
|
}, []);
|
|
2828
|
-
const syncScreenplay =
|
|
2829
|
+
const syncScreenplay = React4.useCallback(() => {
|
|
2829
2830
|
if (!onSyncWithCloudRef.current) return;
|
|
2830
2831
|
onSyncWithCloudRef.current(blocks, sceneNumbers);
|
|
2831
2832
|
lastSavedContent.current = serializeToSbx(blocks);
|
|
@@ -2833,11 +2834,11 @@ function useScreenplayEditor(options) {
|
|
|
2833
2834
|
setShowUnsavedPopover(false);
|
|
2834
2835
|
if (popoverTimeoutRef.current) clearTimeout(popoverTimeoutRef.current);
|
|
2835
2836
|
}, [blocks, sceneNumbers]);
|
|
2836
|
-
const ignoreChanges =
|
|
2837
|
+
const ignoreChanges = React4.useCallback(() => {
|
|
2837
2838
|
setShowUnsavedPopover(false);
|
|
2838
2839
|
if (popoverTimeoutRef.current) clearTimeout(popoverTimeoutRef.current);
|
|
2839
2840
|
}, []);
|
|
2840
|
-
const loadFromUrl =
|
|
2841
|
+
const loadFromUrl = React4.useCallback(
|
|
2841
2842
|
async (url, fetchOptions = {}, isInitialLoad) => {
|
|
2842
2843
|
var _a;
|
|
2843
2844
|
setIsLoading(true);
|
|
@@ -2923,7 +2924,7 @@ function useScreenplayEditor(options) {
|
|
|
2923
2924
|
},
|
|
2924
2925
|
[handleScriptImport]
|
|
2925
2926
|
);
|
|
2926
|
-
|
|
2927
|
+
React4.useEffect(() => {
|
|
2927
2928
|
if (isLoading) {
|
|
2928
2929
|
isInitialLoadRef.current = true;
|
|
2929
2930
|
return;
|
|
@@ -2953,7 +2954,7 @@ function useScreenplayEditor(options) {
|
|
|
2953
2954
|
if (popoverTimeoutRef.current) clearTimeout(popoverTimeoutRef.current);
|
|
2954
2955
|
};
|
|
2955
2956
|
}, [blocks, isLoading]);
|
|
2956
|
-
|
|
2957
|
+
React4.useEffect(() => {
|
|
2957
2958
|
if ((options == null ? void 0 : options.initialUrl) && options.initialUrl !== loadedUrlRef.current) {
|
|
2958
2959
|
loadedUrlRef.current = options.initialUrl;
|
|
2959
2960
|
loadFromUrl(options.initialUrl, options.fetchOptions, true);
|
|
@@ -3325,161 +3326,523 @@ var SummarizeButton = ({
|
|
|
3325
3326
|
);
|
|
3326
3327
|
};
|
|
3327
3328
|
var summarize_button_default = SummarizeButton;
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
}
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
link.rel = "stylesheet";
|
|
3365
|
-
link.href = "https://fonts.googleapis.com/css2?family=Courier+Prime:ital,wght@0,400;0,700;1,400;1,700&display=swap";
|
|
3366
|
-
document.head.appendChild(link);
|
|
3367
|
-
}
|
|
3368
|
-
if (!document.getElementById(styleId)) {
|
|
3369
|
-
const style = document.createElement("style");
|
|
3370
|
-
style.id = styleId;
|
|
3371
|
-
style.textContent = `
|
|
3372
|
-
/* We target by the data-attribute to ensure the highest specificity possible */
|
|
3373
|
-
[data-screenplay-editor] *,
|
|
3374
|
-
[data-screenplay-editor] div,
|
|
3375
|
-
[data-screenplay-editor] span,
|
|
3376
|
-
[data-screenplay-editor] [contenteditable="true"] {
|
|
3377
|
-
font-family: ${COURIER_STACK} !important;
|
|
3378
|
-
-webkit-font-smoothing: antialiased;
|
|
3379
|
-
}
|
|
3380
|
-
`;
|
|
3381
|
-
document.head.appendChild(style);
|
|
3382
|
-
}
|
|
3383
|
-
}, [COURIER_STACK]);
|
|
3384
|
-
if (isLoading) {
|
|
3385
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-32 gap-4", children: [
|
|
3386
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "w-8 h-8 animate-spin text-zinc-400" }),
|
|
3387
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-zinc-500 animate-pulse", children: "Loading scene details..." })
|
|
3388
|
-
] });
|
|
3389
|
-
}
|
|
3390
|
-
const hasLocationTag = tags.some((t) => t.category_id === "LOCATION");
|
|
3391
|
-
const renderBlockText = (block) => {
|
|
3392
|
-
const blockTags = tags.filter((t) => t.block_id === block.id).sort((a, b) => a.start_index - b.start_index);
|
|
3393
|
-
if (blockTags.length === 0) return block.text;
|
|
3394
|
-
const nodes = [];
|
|
3395
|
-
let currentIndex = 0;
|
|
3396
|
-
blockTags.forEach((tag) => {
|
|
3397
|
-
const actualStart = Math.max(tag.start_index, currentIndex);
|
|
3398
|
-
if (actualStart > currentIndex) {
|
|
3399
|
-
nodes.push(
|
|
3400
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: block.text.slice(currentIndex, actualStart) }, `text-${currentIndex}`)
|
|
3401
|
-
);
|
|
3329
|
+
|
|
3330
|
+
// data/crowd-data.ts
|
|
3331
|
+
var crowdAgeOption = [
|
|
3332
|
+
{ name: "Adult" },
|
|
3333
|
+
{ name: "Old" },
|
|
3334
|
+
{ name: "Teenage" },
|
|
3335
|
+
{ name: "Kid" }
|
|
3336
|
+
];
|
|
3337
|
+
var crowdTypeOption = [{ name: "Male" }, { name: "Female" }];
|
|
3338
|
+
var FormikSelect = ({
|
|
3339
|
+
label,
|
|
3340
|
+
name,
|
|
3341
|
+
selectedOption,
|
|
3342
|
+
optionData,
|
|
3343
|
+
value,
|
|
3344
|
+
disable,
|
|
3345
|
+
divClasses,
|
|
3346
|
+
valueProperty = "name",
|
|
3347
|
+
labelProperty = "name",
|
|
3348
|
+
label2propery,
|
|
3349
|
+
brackets,
|
|
3350
|
+
className,
|
|
3351
|
+
enableRedAsterick = false,
|
|
3352
|
+
disableOptionProperty = "disable",
|
|
3353
|
+
disabledOptionText = "",
|
|
3354
|
+
onChange
|
|
3355
|
+
}) => {
|
|
3356
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${divClasses}`, children: [
|
|
3357
|
+
label && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3358
|
+
"label",
|
|
3359
|
+
{
|
|
3360
|
+
className: `mb-1.5 text-sm font-medium text-slate-700 capitalize flex`,
|
|
3361
|
+
children: [
|
|
3362
|
+
label,
|
|
3363
|
+
enableRedAsterick && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AsteriskIcon, { className: "size-3 text-red-500" })
|
|
3364
|
+
]
|
|
3402
3365
|
}
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3366
|
+
),
|
|
3367
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3368
|
+
formik.Field,
|
|
3369
|
+
{
|
|
3370
|
+
name,
|
|
3371
|
+
as: "select",
|
|
3372
|
+
value,
|
|
3373
|
+
disabled: disable,
|
|
3374
|
+
onChange,
|
|
3375
|
+
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`,
|
|
3376
|
+
children: [
|
|
3377
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { selected: true, value: "", disabled: true, className: "capitalize", children: selectedOption }),
|
|
3378
|
+
optionData == null ? void 0 : optionData.map((option, index) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3379
|
+
"option",
|
|
3408
3380
|
{
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
range.selectNodeContents(textNode);
|
|
3418
|
-
} else {
|
|
3419
|
-
range.selectNodeContents(e.currentTarget);
|
|
3420
|
-
}
|
|
3421
|
-
selection.removeAllRanges();
|
|
3422
|
-
selection.addRange(range);
|
|
3423
|
-
setTimeout(() => handleMouseUp(), 0);
|
|
3424
|
-
},
|
|
3425
|
-
className: "cursor-pointer font-bold transition-all hover:opacity-80 rounded-[3px]",
|
|
3426
|
-
style: {
|
|
3427
|
-
color: category == null ? void 0 : category.color,
|
|
3428
|
-
padding: "0.125rem 0.25rem",
|
|
3429
|
-
margin: "0 -0.125rem"
|
|
3430
|
-
},
|
|
3431
|
-
children: block.text.slice(actualStart, tag.end_index)
|
|
3381
|
+
value: option[valueProperty],
|
|
3382
|
+
disabled: option[disableOptionProperty],
|
|
3383
|
+
children: [
|
|
3384
|
+
option[labelProperty],
|
|
3385
|
+
label2propery ? brackets ? ` (${option[label2propery]})` : option[label2propery] : "",
|
|
3386
|
+
" ",
|
|
3387
|
+
option[disableOptionProperty] && `(${disabledOptionText})`
|
|
3388
|
+
]
|
|
3432
3389
|
},
|
|
3433
|
-
|
|
3434
|
-
)
|
|
3435
|
-
|
|
3390
|
+
index
|
|
3391
|
+
))
|
|
3392
|
+
]
|
|
3436
3393
|
}
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3394
|
+
),
|
|
3395
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3396
|
+
formik.ErrorMessage,
|
|
3397
|
+
{
|
|
3398
|
+
name,
|
|
3399
|
+
className: "text-red-500 text-sm mt-1",
|
|
3400
|
+
component: "div"
|
|
3401
|
+
}
|
|
3402
|
+
)
|
|
3403
|
+
] });
|
|
3404
|
+
};
|
|
3405
|
+
var FormikInput = (_a) => {
|
|
3406
|
+
var _b = _a, {
|
|
3407
|
+
label,
|
|
3408
|
+
name,
|
|
3409
|
+
type,
|
|
3410
|
+
placeholder,
|
|
3411
|
+
className,
|
|
3412
|
+
labelClassName,
|
|
3413
|
+
readOnly,
|
|
3414
|
+
divClasses,
|
|
3415
|
+
length,
|
|
3416
|
+
disable,
|
|
3417
|
+
min = 0,
|
|
3418
|
+
max,
|
|
3419
|
+
convertToText = false,
|
|
3420
|
+
enableRedAsterick = false,
|
|
3421
|
+
case_sensitivity = "normal"
|
|
3422
|
+
} = _b, props = __objRest(_b, [
|
|
3423
|
+
"label",
|
|
3424
|
+
"name",
|
|
3425
|
+
"type",
|
|
3426
|
+
"placeholder",
|
|
3427
|
+
"className",
|
|
3428
|
+
"labelClassName",
|
|
3429
|
+
"readOnly",
|
|
3430
|
+
"divClasses",
|
|
3431
|
+
"length",
|
|
3432
|
+
"disable",
|
|
3433
|
+
"min",
|
|
3434
|
+
"max",
|
|
3435
|
+
"convertToText",
|
|
3436
|
+
"enableRedAsterick",
|
|
3437
|
+
"case_sensitivity"
|
|
3438
|
+
]);
|
|
3439
|
+
const { setFieldValue } = formik.useFormikContext();
|
|
3440
|
+
const handleCaseChange = (e) => {
|
|
3441
|
+
let value = e.target.value;
|
|
3442
|
+
if (type === "number" && min !== void 0) {
|
|
3443
|
+
if (value !== "" && Number(value) < min) return;
|
|
3443
3444
|
}
|
|
3444
|
-
|
|
3445
|
+
switch (case_sensitivity) {
|
|
3446
|
+
case "lowercase":
|
|
3447
|
+
value = value.toLowerCase();
|
|
3448
|
+
break;
|
|
3449
|
+
case "uppercase":
|
|
3450
|
+
value = value.toUpperCase();
|
|
3451
|
+
break;
|
|
3452
|
+
}
|
|
3453
|
+
setFieldValue(name, value);
|
|
3445
3454
|
};
|
|
3446
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className:
|
|
3447
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3455
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${divClasses}`, children: [
|
|
3456
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3457
|
+
"label",
|
|
3458
|
+
{
|
|
3459
|
+
className: `${labelClassName} mb-1.5 text-sm font-medium text-slate-700 capitalize flex`,
|
|
3460
|
+
children: [
|
|
3461
|
+
label,
|
|
3462
|
+
enableRedAsterick && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AsteriskIcon, { className: "size-3 text-red-500" })
|
|
3463
|
+
]
|
|
3464
|
+
}
|
|
3465
|
+
),
|
|
3466
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3467
|
+
formik.Field,
|
|
3468
|
+
__spreadProps(__spreadValues({
|
|
3469
|
+
name,
|
|
3470
|
+
placeholder,
|
|
3471
|
+
readOnly,
|
|
3472
|
+
type,
|
|
3473
|
+
maxLength: length,
|
|
3474
|
+
disabled: disable,
|
|
3475
|
+
max,
|
|
3476
|
+
min,
|
|
3477
|
+
onChange: handleCaseChange
|
|
3478
|
+
}, props), {
|
|
3479
|
+
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`
|
|
3480
|
+
})
|
|
3481
|
+
),
|
|
3482
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3483
|
+
formik.ErrorMessage,
|
|
3484
|
+
{
|
|
3485
|
+
name,
|
|
3486
|
+
className: "text-red-500 text-sm mt-1",
|
|
3487
|
+
component: "div"
|
|
3488
|
+
}
|
|
3489
|
+
)
|
|
3490
|
+
] });
|
|
3491
|
+
};
|
|
3492
|
+
var FormikTextarea = ({
|
|
3493
|
+
label,
|
|
3494
|
+
name,
|
|
3495
|
+
placeholder,
|
|
3496
|
+
readOnly,
|
|
3497
|
+
divClasses = "",
|
|
3498
|
+
className = "",
|
|
3499
|
+
length,
|
|
3500
|
+
disable,
|
|
3501
|
+
min,
|
|
3502
|
+
max,
|
|
3503
|
+
enableRedAsterick = false
|
|
3504
|
+
}) => {
|
|
3505
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${divClasses}`, children: [
|
|
3506
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3507
|
+
"label",
|
|
3508
|
+
{
|
|
3509
|
+
className: `mb-1.5 text-sm font-medium text-slate-700 capitalize flex`,
|
|
3510
|
+
children: [
|
|
3511
|
+
label,
|
|
3512
|
+
enableRedAsterick && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AsteriskIcon, { className: "size-3 text-red-500" })
|
|
3513
|
+
]
|
|
3514
|
+
}
|
|
3515
|
+
),
|
|
3516
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3517
|
+
formik.Field,
|
|
3518
|
+
{
|
|
3519
|
+
as: "textarea",
|
|
3520
|
+
name,
|
|
3521
|
+
placeholder,
|
|
3522
|
+
readOnly,
|
|
3523
|
+
maxLength: length,
|
|
3524
|
+
disabled: disable,
|
|
3525
|
+
max,
|
|
3526
|
+
min,
|
|
3527
|
+
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`
|
|
3528
|
+
}
|
|
3529
|
+
),
|
|
3530
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3531
|
+
formik.ErrorMessage,
|
|
3532
|
+
{
|
|
3533
|
+
name,
|
|
3534
|
+
className: "text-red-500 text-sm mt-1",
|
|
3535
|
+
component: "div"
|
|
3536
|
+
}
|
|
3537
|
+
)
|
|
3538
|
+
] });
|
|
3539
|
+
};
|
|
3540
|
+
var Switch = ({
|
|
3541
|
+
isOn,
|
|
3542
|
+
handleToggle,
|
|
3543
|
+
activeColor = "bg-green-500",
|
|
3544
|
+
inactiveColor = "bg-gray-300",
|
|
3545
|
+
size = "medium",
|
|
3546
|
+
onLabel,
|
|
3547
|
+
offLabel,
|
|
3548
|
+
label,
|
|
3549
|
+
divClasses,
|
|
3550
|
+
labelClasses,
|
|
3551
|
+
disable = false
|
|
3552
|
+
}) => {
|
|
3553
|
+
const sizeClasses = {
|
|
3554
|
+
small: { width: 28, height: 18, handle: 12, labelSize: 12, gap: 4 },
|
|
3555
|
+
medium: { width: 48, height: 28, handle: 20, labelSize: 16, gap: 6 },
|
|
3556
|
+
large: { width: 64, height: 36, handle: 28, labelSize: 20, gap: 10 }
|
|
3557
|
+
};
|
|
3558
|
+
const selectedSize = sizeClasses[size] || sizeClasses.medium;
|
|
3559
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${divClasses}`, children: [
|
|
3560
|
+
label && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3561
|
+
"label",
|
|
3562
|
+
{
|
|
3563
|
+
className: `block mb-2 text-sm text-gray-600 ${disable && "text-gray-400!"} ${labelClasses}`,
|
|
3564
|
+
children: label
|
|
3565
|
+
}
|
|
3566
|
+
),
|
|
3567
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { gap: selectedSize.gap }, className: "flex items-center", children: [
|
|
3568
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3569
|
+
"div",
|
|
3570
|
+
{
|
|
3571
|
+
className: `flex items-center rounded-full cursor-pointer relative ${isOn ? activeColor : inactiveColor} ${disable && "opacity-50 cursor-not-allowed"}`,
|
|
3572
|
+
onClick: disable ? () => {
|
|
3573
|
+
} : handleToggle,
|
|
3574
|
+
role: "button",
|
|
3575
|
+
"aria-pressed": isOn,
|
|
3576
|
+
style: {
|
|
3577
|
+
width: selectedSize.width,
|
|
3578
|
+
height: selectedSize.height
|
|
3579
|
+
},
|
|
3580
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3581
|
+
"div",
|
|
3582
|
+
{
|
|
3583
|
+
className: "bg-white rounded-full shadow-sm ease-in duration-200",
|
|
3584
|
+
style: {
|
|
3585
|
+
width: selectedSize.handle,
|
|
3586
|
+
height: selectedSize.handle,
|
|
3587
|
+
transform: `translateX(${isOn ? selectedSize.width - selectedSize.handle - 3 : 3}px)`,
|
|
3588
|
+
transition: "transform 0.2s ease-in-out"
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3591
|
+
)
|
|
3592
|
+
}
|
|
3593
|
+
),
|
|
3594
|
+
onLabel && offLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: selectedSize.labelSize }, children: isOn ? onLabel : offLabel })
|
|
3595
|
+
] })
|
|
3596
|
+
] });
|
|
3597
|
+
};
|
|
3598
|
+
var MultiSelect = ({
|
|
3599
|
+
label,
|
|
3600
|
+
options,
|
|
3601
|
+
value,
|
|
3602
|
+
onChange,
|
|
3603
|
+
placeholder = "Select options...",
|
|
3604
|
+
valueProperty = "name",
|
|
3605
|
+
labelProperty = "name",
|
|
3606
|
+
divClasses = "",
|
|
3607
|
+
returnObjects = false
|
|
3608
|
+
}) => {
|
|
3609
|
+
const [isOpen, setIsOpen] = React4__default.default.useState(false);
|
|
3610
|
+
const containerRef = React4__default.default.useRef(null);
|
|
3611
|
+
const isSelected = (optionValue) => {
|
|
3612
|
+
return value.some(
|
|
3613
|
+
(v) => typeof v === "object" && v !== null ? v[valueProperty] === optionValue : v === optionValue
|
|
3614
|
+
);
|
|
3615
|
+
};
|
|
3616
|
+
const toggleOption = (optionValue) => {
|
|
3617
|
+
let newValues;
|
|
3618
|
+
if (isSelected(optionValue)) {
|
|
3619
|
+
newValues = value.filter(
|
|
3620
|
+
(v) => typeof v === "object" && v !== null ? v[valueProperty] !== optionValue : v !== optionValue
|
|
3621
|
+
);
|
|
3622
|
+
} else {
|
|
3623
|
+
const newValue = returnObjects ? options.find((opt) => opt[valueProperty] === optionValue) : optionValue;
|
|
3624
|
+
newValues = [...value, newValue];
|
|
3625
|
+
}
|
|
3626
|
+
onChange(newValues);
|
|
3627
|
+
};
|
|
3628
|
+
const removeOption = (e, optionValue) => {
|
|
3629
|
+
e.stopPropagation();
|
|
3630
|
+
const newValues = value.filter(
|
|
3631
|
+
(v) => typeof v === "object" && v !== null ? v[valueProperty] !== optionValue : v !== optionValue
|
|
3632
|
+
);
|
|
3633
|
+
onChange(newValues);
|
|
3634
|
+
};
|
|
3635
|
+
React4__default.default.useEffect(() => {
|
|
3636
|
+
const handleClickOutside = (event) => {
|
|
3637
|
+
if (containerRef.current && !containerRef.current.contains(event.target)) {
|
|
3638
|
+
setIsOpen(false);
|
|
3639
|
+
}
|
|
3640
|
+
};
|
|
3641
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
3642
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
3643
|
+
}, []);
|
|
3644
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full relative ${divClasses}`, ref: containerRef, children: [
|
|
3645
|
+
label && /* @__PURE__ */ jsxRuntime.jsx("label", { className: "mb-1.5 text-xs font-bold text-slate-600 ml-1 uppercase tracking-wider", children: label }),
|
|
3646
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3647
|
+
"div",
|
|
3648
|
+
{
|
|
3649
|
+
onClick: () => setIsOpen(!isOpen),
|
|
3650
|
+
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"}`,
|
|
3651
|
+
children: value.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-400 text-[13px] ml-1", children: placeholder }) : value.map((val) => {
|
|
3652
|
+
const actualValue = typeof val === "object" && val !== null ? val[valueProperty] : val;
|
|
3653
|
+
const option = options.find(
|
|
3654
|
+
(opt) => opt[valueProperty] === actualValue
|
|
3655
|
+
);
|
|
3656
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3657
|
+
"div",
|
|
3658
|
+
{
|
|
3659
|
+
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",
|
|
3660
|
+
children: [
|
|
3661
|
+
option ? option[labelProperty] : actualValue,
|
|
3662
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3663
|
+
"button",
|
|
3664
|
+
{
|
|
3665
|
+
type: "button",
|
|
3666
|
+
onClick: (e) => removeOption(e, actualValue),
|
|
3667
|
+
className: "hover:text-red-500 transition-colors ml-0.5",
|
|
3668
|
+
children: "\xD7"
|
|
3669
|
+
}
|
|
3670
|
+
)
|
|
3671
|
+
]
|
|
3672
|
+
},
|
|
3673
|
+
actualValue
|
|
3674
|
+
);
|
|
3675
|
+
})
|
|
3676
|
+
}
|
|
3677
|
+
),
|
|
3678
|
+
isOpen && /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx("div", { className: "p-1", children: options.filter((opt) => !isSelected(opt[valueProperty])).length > 0 ? options.filter((opt) => !isSelected(opt[valueProperty])).map((option, index) => {
|
|
3679
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3680
|
+
"div",
|
|
3681
|
+
{
|
|
3682
|
+
onClick: () => toggleOption(option[valueProperty]),
|
|
3683
|
+
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",
|
|
3684
|
+
children: option[labelProperty]
|
|
3685
|
+
},
|
|
3686
|
+
index
|
|
3687
|
+
);
|
|
3688
|
+
}) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-3 text-[10px] text-slate-400 text-center font-bold uppercase tracking-wider", children: "All selected" }) }) })
|
|
3689
|
+
] });
|
|
3690
|
+
};
|
|
3691
|
+
function ScriptBreakdownSceneView({
|
|
3692
|
+
blocks,
|
|
3693
|
+
characters,
|
|
3694
|
+
isLoading,
|
|
3695
|
+
sceneNumber,
|
|
3696
|
+
tags,
|
|
3697
|
+
selectionMenu,
|
|
3698
|
+
handleMouseUp,
|
|
3699
|
+
addTag,
|
|
3700
|
+
updateTag,
|
|
3701
|
+
removeTag,
|
|
3702
|
+
clearSelection,
|
|
3703
|
+
menuPlacement,
|
|
3704
|
+
menuRef,
|
|
3705
|
+
sceneBrief,
|
|
3706
|
+
setSceneBrief,
|
|
3707
|
+
onSummarize,
|
|
3708
|
+
isSummarizing,
|
|
3709
|
+
aiSummarized = false,
|
|
3710
|
+
onUpdateBrief
|
|
3711
|
+
}) {
|
|
3712
|
+
const [expandedCategories, setExpandedCategories] = React4.useState({});
|
|
3713
|
+
const [editingTagData, setEditingTagData] = React4.useState(null);
|
|
3714
|
+
const [tagForm, setTagForm] = React4.useState({ quantity: 1, look: "", age: "", age_range: [], crowd_type: [] });
|
|
3715
|
+
const [popupPlacement, setPopupPlacement] = React4.useState({
|
|
3716
|
+
alignRight: false,
|
|
3717
|
+
alignBottom: false
|
|
3718
|
+
});
|
|
3719
|
+
const [isSidebarOpen, setIsSidebarOpen] = React4.useState(false);
|
|
3720
|
+
const COURIER_STACK = "'Courier Prime', 'Courier', monospace";
|
|
3721
|
+
React4.useEffect(() => {
|
|
3722
|
+
const fontId = "google-font-courier-prime";
|
|
3723
|
+
const styleId = "screenplay-editor-force-v4";
|
|
3724
|
+
if (!document.getElementById(fontId)) {
|
|
3725
|
+
const link = document.createElement("link");
|
|
3726
|
+
link.id = fontId;
|
|
3727
|
+
link.rel = "stylesheet";
|
|
3728
|
+
link.href = "https://fonts.googleapis.com/css2?family=Courier+Prime:ital,wght@0,400;0,700;1,400;1,700&display=swap";
|
|
3729
|
+
document.head.appendChild(link);
|
|
3730
|
+
}
|
|
3731
|
+
if (!document.getElementById(styleId)) {
|
|
3732
|
+
const style = document.createElement("style");
|
|
3733
|
+
style.id = styleId;
|
|
3734
|
+
style.textContent = `
|
|
3735
|
+
/* We target by the data-attribute to ensure the highest specificity possible */
|
|
3736
|
+
[data-screenplay-editor] *,
|
|
3737
|
+
[data-screenplay-editor] div,
|
|
3738
|
+
[data-screenplay-editor] span,
|
|
3739
|
+
[data-screenplay-editor] [contenteditable="true"] {
|
|
3740
|
+
font-family: ${COURIER_STACK} !important;
|
|
3741
|
+
-webkit-font-smoothing: antialiased;
|
|
3742
|
+
}
|
|
3743
|
+
`;
|
|
3744
|
+
document.head.appendChild(style);
|
|
3745
|
+
}
|
|
3746
|
+
}, [COURIER_STACK]);
|
|
3747
|
+
if (isLoading) {
|
|
3748
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-32 gap-4", children: [
|
|
3749
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "w-8 h-8 animate-spin text-zinc-400" }),
|
|
3750
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-zinc-500 animate-pulse", children: "Loading scene details..." })
|
|
3751
|
+
] });
|
|
3752
|
+
}
|
|
3753
|
+
const hasLocationTag = tags.some((t) => t.category_id === "LOCATION");
|
|
3754
|
+
const renderBlockText = (block) => {
|
|
3755
|
+
const blockTags = tags.filter((t) => t.block_id === block.id).sort((a, b) => a.start_index - b.start_index);
|
|
3756
|
+
if (blockTags.length === 0) return block.text;
|
|
3757
|
+
const nodes = [];
|
|
3758
|
+
let currentIndex = 0;
|
|
3759
|
+
blockTags.forEach((tag) => {
|
|
3760
|
+
const actualStart = Math.max(tag.start_index, currentIndex);
|
|
3761
|
+
if (actualStart > currentIndex) {
|
|
3762
|
+
nodes.push(
|
|
3763
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: block.text.slice(currentIndex, actualStart) }, `text-${currentIndex}`)
|
|
3764
|
+
);
|
|
3765
|
+
}
|
|
3766
|
+
const category = CATEGORIES.find((c) => c.id === tag.category_id);
|
|
3767
|
+
if (actualStart < tag.end_index) {
|
|
3768
|
+
nodes.push(
|
|
3769
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3770
|
+
"span",
|
|
3771
|
+
{
|
|
3772
|
+
title: `${category == null ? void 0 : category.label} (Click to edit)`,
|
|
3773
|
+
onClick: (e) => {
|
|
3774
|
+
e.stopPropagation();
|
|
3775
|
+
const selection = window.getSelection();
|
|
3776
|
+
if (!selection) return;
|
|
3777
|
+
const range = document.createRange();
|
|
3778
|
+
const textNode = e.currentTarget.firstChild;
|
|
3779
|
+
if (textNode && textNode.nodeType === Node.TEXT_NODE) {
|
|
3780
|
+
range.selectNodeContents(textNode);
|
|
3781
|
+
} else {
|
|
3782
|
+
range.selectNodeContents(e.currentTarget);
|
|
3783
|
+
}
|
|
3784
|
+
selection.removeAllRanges();
|
|
3785
|
+
selection.addRange(range);
|
|
3786
|
+
setTimeout(() => handleMouseUp(), 0);
|
|
3787
|
+
},
|
|
3788
|
+
className: "cursor-pointer font-bold transition-all hover:opacity-80 rounded-[3px]",
|
|
3789
|
+
style: {
|
|
3790
|
+
color: category == null ? void 0 : category.color,
|
|
3791
|
+
padding: "0.125rem 0.25rem",
|
|
3792
|
+
margin: "0 -0.125rem"
|
|
3793
|
+
},
|
|
3794
|
+
children: block.text.slice(actualStart, tag.end_index)
|
|
3795
|
+
},
|
|
3796
|
+
tag.id || `tag-${actualStart}-${tag.end_index}`
|
|
3797
|
+
)
|
|
3798
|
+
);
|
|
3799
|
+
}
|
|
3800
|
+
currentIndex = Math.max(currentIndex, tag.end_index);
|
|
3801
|
+
});
|
|
3802
|
+
if (currentIndex < block.text.length) {
|
|
3803
|
+
nodes.push(
|
|
3804
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: block.text.slice(currentIndex) }, `text-${currentIndex}`)
|
|
3805
|
+
);
|
|
3806
|
+
}
|
|
3807
|
+
return nodes;
|
|
3808
|
+
};
|
|
3809
|
+
return /* @__PURE__ */ jsxRuntime.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: [
|
|
3810
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center max-w-6xl xl:max-w-full", children: [
|
|
3811
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 min-h-screen", children: [
|
|
3812
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3813
|
+
"div",
|
|
3814
|
+
{
|
|
3815
|
+
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",
|
|
3816
|
+
style: {
|
|
3817
|
+
fontFamily: COURIER_STACK,
|
|
3818
|
+
paddingLeft: "1.5in",
|
|
3819
|
+
paddingRight: "1in",
|
|
3820
|
+
paddingTop: "1in",
|
|
3821
|
+
paddingBottom: "1in",
|
|
3822
|
+
lineHeight: "1.2"
|
|
3823
|
+
},
|
|
3824
|
+
"data-screenplay-editor": "true",
|
|
3825
|
+
onMouseUp: handleMouseUp,
|
|
3826
|
+
children: blocks.map((block) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3827
|
+
"div",
|
|
3828
|
+
{
|
|
3829
|
+
"data-block-id": block.id,
|
|
3830
|
+
className: `relative break-words w-full px-4 py-2 ${blockStyles[block.type].className}`,
|
|
3831
|
+
style: __spreadProps(__spreadValues({}, blockStyles[block.type].inputStyle), {
|
|
3832
|
+
minHeight: "2.5rem"
|
|
3833
|
+
}),
|
|
3834
|
+
children: [
|
|
3835
|
+
renderBlockText(block),
|
|
3836
|
+
(selectionMenu == null ? void 0 : selectionMenu.blockId) === block.id && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3837
|
+
"div",
|
|
3838
|
+
{
|
|
3839
|
+
ref: menuRef,
|
|
3840
|
+
"data-screenplay-editor": "false",
|
|
3841
|
+
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"}`,
|
|
3842
|
+
style: {
|
|
3843
|
+
top: selectionMenu.top,
|
|
3844
|
+
left: selectionMenu.left,
|
|
3845
|
+
transform: menuPlacement === "top" ? "translate(-50%, calc(-100% - 12px))" : "translate(-50%, 32px)"
|
|
3483
3846
|
},
|
|
3484
3847
|
children: [
|
|
3485
3848
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative z-10 px-3 py-2.5 border-b border-white/60 mb-1.5", children: [
|
|
@@ -3709,7 +4072,7 @@ function ScriptBreakdownSceneView({
|
|
|
3709
4072
|
title: tag.name,
|
|
3710
4073
|
onClick: (e) => {
|
|
3711
4074
|
e.stopPropagation();
|
|
3712
|
-
if (cat.id !== "PROP" && cat.id !== "SET_PROP" && cat.id !== "CAST")
|
|
4075
|
+
if (cat.id !== "PROP" && cat.id !== "SET_PROP" && cat.id !== "CAST" && cat.id !== "EXTRA")
|
|
3713
4076
|
return;
|
|
3714
4077
|
if ((editingTagData == null ? void 0 : editingTagData.tag.id) === tag.id) {
|
|
3715
4078
|
setEditingTagData(null);
|
|
@@ -3722,7 +4085,9 @@ function ScriptBreakdownSceneView({
|
|
|
3722
4085
|
setTagForm({
|
|
3723
4086
|
quantity: tag.quantity || 1,
|
|
3724
4087
|
look: tag.look || "",
|
|
3725
|
-
age: tag.age || ""
|
|
4088
|
+
age: tag.age || "",
|
|
4089
|
+
age_range: tag.age_range || [],
|
|
4090
|
+
crowd_type: tag.crowd_type || []
|
|
3726
4091
|
});
|
|
3727
4092
|
setEditingTagData({
|
|
3728
4093
|
tag,
|
|
@@ -3732,7 +4097,7 @@ function ScriptBreakdownSceneView({
|
|
|
3732
4097
|
});
|
|
3733
4098
|
}
|
|
3734
4099
|
},
|
|
3735
|
-
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" : ""}`,
|
|
4100
|
+
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" : ""}`,
|
|
3736
4101
|
style: {
|
|
3737
4102
|
color: cat.color,
|
|
3738
4103
|
background: `linear-gradient(145deg, ${cat.color}18, rgba(255,255,255,0.88))`,
|
|
@@ -3856,13 +4221,38 @@ function ScriptBreakdownSceneView({
|
|
|
3856
4221
|
)
|
|
3857
4222
|
] })
|
|
3858
4223
|
] }),
|
|
4224
|
+
editingTagData.catId === "EXTRA" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
4225
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4226
|
+
MultiSelect,
|
|
4227
|
+
{
|
|
4228
|
+
label: "Age Range",
|
|
4229
|
+
options: crowdAgeOption,
|
|
4230
|
+
value: tagForm.age_range,
|
|
4231
|
+
returnObjects: true,
|
|
4232
|
+
onChange: (val) => setTagForm((prev) => __spreadProps(__spreadValues({}, prev), { age_range: val }))
|
|
4233
|
+
}
|
|
4234
|
+
),
|
|
4235
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4236
|
+
MultiSelect,
|
|
4237
|
+
{
|
|
4238
|
+
label: "Crowd Type",
|
|
4239
|
+
options: crowdTypeOption,
|
|
4240
|
+
value: tagForm.crowd_type,
|
|
4241
|
+
returnObjects: true,
|
|
4242
|
+
onChange: (val) => setTagForm((prev) => __spreadProps(__spreadValues({}, prev), { crowd_type: val }))
|
|
4243
|
+
}
|
|
4244
|
+
)
|
|
4245
|
+
] }),
|
|
3859
4246
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3860
4247
|
"button",
|
|
3861
4248
|
{
|
|
3862
4249
|
onClick: (e) => {
|
|
3863
4250
|
e.stopPropagation();
|
|
3864
4251
|
if (editingTagData.tag.id) {
|
|
3865
|
-
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 } : {})
|
|
4252
|
+
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" ? {
|
|
4253
|
+
age_range: tagForm.age_range,
|
|
4254
|
+
crowd_type: tagForm.crowd_type
|
|
4255
|
+
} : {}));
|
|
3866
4256
|
}
|
|
3867
4257
|
setEditingTagData(null);
|
|
3868
4258
|
},
|
|
@@ -3877,17 +4267,17 @@ function ScriptBreakdownSceneView({
|
|
|
3877
4267
|
] });
|
|
3878
4268
|
}
|
|
3879
4269
|
function useScriptBreakdownScene(options) {
|
|
3880
|
-
const [tags, setTags] =
|
|
3881
|
-
const [selectionMenu, setSelectionMenu] =
|
|
3882
|
-
const autoTaggedSceneRef =
|
|
3883
|
-
const [scene, setScene] =
|
|
3884
|
-
const [menuPlacement, setMenuPlacement] =
|
|
3885
|
-
const [sceneBrief, setSceneBrief] =
|
|
3886
|
-
const [isSummarizing, setIsSummarizing] =
|
|
3887
|
-
const [isLoading, setIsLoading] =
|
|
3888
|
-
const [error, setError] =
|
|
3889
|
-
const menuRef =
|
|
3890
|
-
|
|
4270
|
+
const [tags, setTags] = React4.useState(options.preLoadedTags || []);
|
|
4271
|
+
const [selectionMenu, setSelectionMenu] = React4.useState(null);
|
|
4272
|
+
const autoTaggedSceneRef = React4.useRef(null);
|
|
4273
|
+
const [scene, setScene] = React4.useState(null);
|
|
4274
|
+
const [menuPlacement, setMenuPlacement] = React4.useState("top");
|
|
4275
|
+
const [sceneBrief, setSceneBrief] = React4.useState("");
|
|
4276
|
+
const [isSummarizing, setIsSummarizing] = React4.useState(false);
|
|
4277
|
+
const [isLoading, setIsLoading] = React4.useState(true);
|
|
4278
|
+
const [error, setError] = React4.useState(false);
|
|
4279
|
+
const menuRef = React4.useRef(null);
|
|
4280
|
+
React4.useEffect(() => {
|
|
3891
4281
|
setIsLoading(true);
|
|
3892
4282
|
const fetchScene = async () => {
|
|
3893
4283
|
try {
|
|
@@ -3897,702 +4287,512 @@ function useScriptBreakdownScene(options) {
|
|
|
3897
4287
|
setScene({ content: text });
|
|
3898
4288
|
} else {
|
|
3899
4289
|
console.error("Failed to fetch scene:", response);
|
|
3900
|
-
setError(true);
|
|
3901
|
-
}
|
|
3902
|
-
setIsLoading(false);
|
|
3903
|
-
} catch (error2) {
|
|
3904
|
-
setError(true);
|
|
3905
|
-
setIsLoading(false);
|
|
3906
|
-
console.error("Error fetching scene:", error2);
|
|
3907
|
-
}
|
|
3908
|
-
};
|
|
3909
|
-
fetchScene();
|
|
3910
|
-
}, []);
|
|
3911
|
-
const blocks = react.useMemo(() => {
|
|
3912
|
-
if (!scene || !scene.content) return [];
|
|
3913
|
-
const parser = new DOMParser();
|
|
3914
|
-
const doc = parser.parseFromString(scene.content, "text/html");
|
|
3915
|
-
const divs = Array.from(doc.querySelectorAll("div"));
|
|
3916
|
-
const parsedBlocks = [];
|
|
3917
|
-
const typeMap = {
|
|
3918
|
-
divtype0: "SCENE_HEADING",
|
|
3919
|
-
divtype2: "ACTION",
|
|
3920
|
-
divtype3: "CHARACTER",
|
|
3921
|
-
divtype4: "PARENTHETICAL",
|
|
3922
|
-
divtype5: "DIALOGUE",
|
|
3923
|
-
divtype6: "TRANSITION"
|
|
3924
|
-
};
|
|
3925
|
-
divs.forEach((div) => {
|
|
3926
|
-
var _a;
|
|
3927
|
-
const divText = ((_a = div.textContent) == null ? void 0 : _a.trim()) || "";
|
|
3928
|
-
if (!divText) return;
|
|
3929
|
-
let type = "ACTION";
|
|
3930
|
-
for (const className of Array.from(div.classList)) {
|
|
3931
|
-
if (typeMap[className]) {
|
|
3932
|
-
type = typeMap[className];
|
|
3933
|
-
break;
|
|
3934
|
-
}
|
|
3935
|
-
}
|
|
3936
|
-
const idAttr = div.getAttribute("id");
|
|
3937
|
-
const blockId = idAttr && idAttr.startsWith("par") ? idAttr.substring(3) : idAttr || uuid();
|
|
3938
|
-
parsedBlocks.push({ id: blockId, type, text: divText });
|
|
3939
|
-
});
|
|
3940
|
-
return parsedBlocks;
|
|
3941
|
-
}, [scene]);
|
|
3942
|
-
const characters = react.useMemo(() => {
|
|
3943
|
-
const chars = blocks.filter((b) => b.type === "CHARACTER").map((b) => {
|
|
3944
|
-
const text = b.text.trim().toUpperCase();
|
|
3945
|
-
const parenIndex = text.indexOf("(");
|
|
3946
|
-
return parenIndex > -1 ? text.substring(0, parenIndex).trim() : text;
|
|
3947
|
-
}).filter(Boolean);
|
|
3948
|
-
return [...new Set(chars)];
|
|
3949
|
-
}, [blocks]);
|
|
3950
|
-
const handleAISummarize = async () => {
|
|
3951
|
-
var _a;
|
|
3952
|
-
setIsSummarizing(true);
|
|
3953
|
-
const res = await ((_a = options.onAISummarize) == null ? void 0 : _a.call(options, scene.content));
|
|
3954
|
-
if (res.ok) {
|
|
3955
|
-
const data = await res.json();
|
|
3956
|
-
setIsSummarizing(false);
|
|
3957
|
-
let parsedData = [];
|
|
3958
|
-
let parsedSummaryData = {};
|
|
3959
|
-
try {
|
|
3960
|
-
const normalData = JSON.parse(data.data);
|
|
3961
|
-
if (Array.isArray(normalData)) {
|
|
3962
|
-
parsedData = Array.isArray(normalData[0]) ? normalData[0] : [];
|
|
3963
|
-
const summary = Array.isArray(normalData[1]) ? normalData[1] : [];
|
|
3964
|
-
parsedSummaryData = summary[0] || {};
|
|
3965
|
-
}
|
|
3966
|
-
} catch (error2) {
|
|
3967
|
-
console.error("Error parsing AI summary data:", error2);
|
|
3968
|
-
}
|
|
3969
|
-
setSceneBrief(parsedSummaryData.summarise || "");
|
|
3970
|
-
const newTags = [];
|
|
3971
|
-
parsedData.forEach((aiTag) => {
|
|
3972
|
-
if (!aiTag.block_id || !aiTag.category_id || typeof aiTag.start_index !== "number" || typeof aiTag.end_index !== "number") {
|
|
3973
|
-
return;
|
|
3974
|
-
}
|
|
3975
|
-
const newTag = {
|
|
3976
|
-
id: aiTag.id || uuid(),
|
|
3977
|
-
block_id: String(aiTag.block_id).startsWith("par") ? String(aiTag.block_id).substring(3) : String(aiTag.block_id),
|
|
3978
|
-
category_id: aiTag.category_id,
|
|
3979
|
-
name: aiTag.name,
|
|
3980
|
-
start_index: aiTag.start_index,
|
|
3981
|
-
end_index: aiTag.end_index
|
|
3982
|
-
};
|
|
3983
|
-
if (aiTag.category_id === "PROP" || aiTag.category_id === "SET_PROP") {
|
|
3984
|
-
newTag.quantity = 1;
|
|
3985
|
-
} else if (aiTag.category_id === "CAST") {
|
|
3986
|
-
newTag.look = "";
|
|
3987
|
-
newTag.age = "";
|
|
3988
|
-
}
|
|
3989
|
-
newTags.push(newTag);
|
|
3990
|
-
});
|
|
3991
|
-
if (newTags.length > 0) {
|
|
3992
|
-
const originalTags = tags;
|
|
3993
|
-
setTags((prev) => {
|
|
3994
|
-
const merged = [...prev];
|
|
3995
|
-
newTags.forEach((newTag) => {
|
|
3996
|
-
const isOverlapping = merged.some(
|
|
3997
|
-
(t) => t.block_id === newTag.block_id && newTag.end_index > t.start_index && newTag.start_index < t.end_index
|
|
3998
|
-
);
|
|
3999
|
-
if (!isOverlapping) {
|
|
4000
|
-
merged.push(newTag);
|
|
4001
|
-
}
|
|
4002
|
-
});
|
|
4003
|
-
return merged;
|
|
4004
|
-
});
|
|
4005
|
-
try {
|
|
4006
|
-
if (options.onTagsBulkAdded) {
|
|
4007
|
-
await options.onTagsBulkAdded(newTags, parsedSummaryData.summarise);
|
|
4008
|
-
}
|
|
4009
|
-
} catch (error2) {
|
|
4010
|
-
console.error("Failed to bulk add AI-generated tags:", error2);
|
|
4011
|
-
setTags(originalTags);
|
|
4012
|
-
}
|
|
4013
|
-
}
|
|
4014
|
-
return data;
|
|
4015
|
-
} else {
|
|
4016
|
-
setIsSummarizing(false);
|
|
4017
|
-
console.error("Failed to summarize scene:", res);
|
|
4018
|
-
}
|
|
4019
|
-
};
|
|
4020
|
-
const bulkCreateTags = react.useCallback(async () => {
|
|
4021
|
-
if (blocks.length === 0) return;
|
|
4022
|
-
const newTags = [];
|
|
4023
|
-
const seenCharacters = /* @__PURE__ */ new Set();
|
|
4024
|
-
const timeOfDays = ["DAY", "NIGHT"];
|
|
4025
|
-
const isTimeOfDay = (str) => timeOfDays.includes(str.toUpperCase());
|
|
4026
|
-
blocks.forEach((block) => {
|
|
4027
|
-
if (block.type === "CHARACTER") {
|
|
4028
|
-
const text = block.text.trim();
|
|
4029
|
-
const parenIndex = text.indexOf("(");
|
|
4030
|
-
const charName = parenIndex > -1 ? text.substring(0, parenIndex).trim() : text;
|
|
4031
|
-
if (charName && !seenCharacters.has(charName.toUpperCase())) {
|
|
4032
|
-
seenCharacters.add(charName.toUpperCase());
|
|
4033
|
-
const startIndex = text.indexOf(charName);
|
|
4034
|
-
if (startIndex !== -1) {
|
|
4035
|
-
newTags.push({
|
|
4036
|
-
id: uuid(),
|
|
4037
|
-
block_id: block.id,
|
|
4038
|
-
category_id: "CAST",
|
|
4039
|
-
name: charName,
|
|
4040
|
-
start_index: startIndex,
|
|
4041
|
-
end_index: startIndex + charName.length,
|
|
4042
|
-
look: "",
|
|
4043
|
-
age: ""
|
|
4044
|
-
});
|
|
4045
|
-
}
|
|
4046
|
-
}
|
|
4047
|
-
} else if (block.type === "SCENE_HEADING") {
|
|
4048
|
-
const text = block.text.trim();
|
|
4049
|
-
const typeMatch = text.match(
|
|
4050
|
-
/^(INT\/EXT|INT\.?\/EXT\.?|INT\.EXT\.?|INT|EXT|I\/E)\.?\s+/i
|
|
4051
|
-
);
|
|
4052
|
-
let remainingText = text;
|
|
4053
|
-
let offset = 0;
|
|
4054
|
-
if (typeMatch) {
|
|
4055
|
-
offset = typeMatch[0].length;
|
|
4056
|
-
remainingText = text.substring(offset);
|
|
4057
|
-
}
|
|
4058
|
-
const parts = remainingText.split(/\s+-\s+/);
|
|
4059
|
-
if (parts.length > 0) {
|
|
4060
|
-
const locationName = parts[0].trim();
|
|
4061
|
-
const locStart = text.indexOf(locationName, offset);
|
|
4062
|
-
if (locStart !== -1 && locationName) {
|
|
4063
|
-
newTags.push({
|
|
4064
|
-
id: uuid(),
|
|
4065
|
-
block_id: block.id,
|
|
4066
|
-
category_id: "LOCATION",
|
|
4067
|
-
name: locationName,
|
|
4068
|
-
start_index: locStart,
|
|
4069
|
-
end_index: locStart + locationName.length
|
|
4070
|
-
});
|
|
4071
|
-
}
|
|
4072
|
-
if (parts.length > 1) {
|
|
4073
|
-
const secondPart = parts[1].trim();
|
|
4074
|
-
const isLast = parts.length === 2;
|
|
4075
|
-
if (!isLast || !isTimeOfDay(secondPart)) {
|
|
4076
|
-
const subLocStart = text.indexOf(
|
|
4077
|
-
secondPart,
|
|
4078
|
-
locStart + locationName.length
|
|
4079
|
-
);
|
|
4080
|
-
if (subLocStart !== -1 && secondPart) {
|
|
4081
|
-
newTags.push({
|
|
4082
|
-
id: uuid(),
|
|
4083
|
-
block_id: block.id,
|
|
4084
|
-
category_id: "SUBLOCATION",
|
|
4085
|
-
name: secondPart,
|
|
4086
|
-
start_index: subLocStart,
|
|
4087
|
-
end_index: subLocStart + secondPart.length
|
|
4088
|
-
});
|
|
4089
|
-
}
|
|
4090
|
-
}
|
|
4091
|
-
}
|
|
4092
|
-
}
|
|
4093
|
-
}
|
|
4094
|
-
});
|
|
4095
|
-
if (newTags.length > 0) {
|
|
4096
|
-
const originalTags = tags;
|
|
4097
|
-
setTags((prev) => {
|
|
4098
|
-
const merged = [...prev];
|
|
4099
|
-
const existingChars = new Set(
|
|
4100
|
-
merged.filter(
|
|
4101
|
-
(t) => t.category_id === "CHARACTER" || t.category_id === "CAST"
|
|
4102
|
-
).map((t) => t.name.toUpperCase())
|
|
4103
|
-
);
|
|
4104
|
-
newTags.forEach((newTag) => {
|
|
4105
|
-
if (newTag.category_id === "CHARACTER") {
|
|
4106
|
-
if (existingChars.has(newTag.name.toUpperCase())) return;
|
|
4107
|
-
existingChars.add(newTag.name.toUpperCase());
|
|
4108
|
-
}
|
|
4109
|
-
const isOverlapping = merged.some(
|
|
4110
|
-
(t) => t.block_id === newTag.block_id && newTag.end_index > t.start_index && newTag.start_index < t.end_index
|
|
4111
|
-
);
|
|
4112
|
-
if (!isOverlapping) {
|
|
4113
|
-
merged.push(newTag);
|
|
4114
|
-
}
|
|
4115
|
-
});
|
|
4116
|
-
return merged;
|
|
4117
|
-
});
|
|
4118
|
-
try {
|
|
4119
|
-
if (options.onTagsBulkAdded) {
|
|
4120
|
-
await options.onTagsBulkAdded(newTags);
|
|
4121
|
-
}
|
|
4122
|
-
} catch (error2) {
|
|
4123
|
-
console.error("Failed to bulk create tags:", error2);
|
|
4124
|
-
setTags(originalTags);
|
|
4125
|
-
}
|
|
4126
|
-
}
|
|
4127
|
-
}, [blocks, tags, options.onTagsBulkAdded]);
|
|
4128
|
-
react.useEffect(() => {
|
|
4129
|
-
setSceneBrief("");
|
|
4130
|
-
autoTaggedSceneRef.current = null;
|
|
4131
|
-
}, [options.scene_url]);
|
|
4132
|
-
react.useEffect(() => {
|
|
4133
|
-
if (options.preLoadedTags && options.preLoadedTags.length > 0) {
|
|
4134
|
-
setTags((prev) => {
|
|
4135
|
-
if (JSON.stringify(prev) === JSON.stringify(options.preLoadedTags)) {
|
|
4136
|
-
return prev;
|
|
4290
|
+
setError(true);
|
|
4137
4291
|
}
|
|
4138
|
-
|
|
4139
|
-
})
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
if (blocks.length === 0) return;
|
|
4145
|
-
if (options.preLoadedTagsLoading) return;
|
|
4146
|
-
if (autoTaggedSceneRef.current === options.scene_url) return;
|
|
4147
|
-
const hasPreloadedTags = options.preLoadedTags && options.preLoadedTags.length > 0;
|
|
4148
|
-
autoTaggedSceneRef.current = options.scene_url;
|
|
4149
|
-
if (hasPreloadedTags) return;
|
|
4150
|
-
await bulkCreateTags();
|
|
4292
|
+
setIsLoading(false);
|
|
4293
|
+
} catch (error2) {
|
|
4294
|
+
setError(true);
|
|
4295
|
+
setIsLoading(false);
|
|
4296
|
+
console.error("Error fetching scene:", error2);
|
|
4297
|
+
}
|
|
4151
4298
|
};
|
|
4152
|
-
|
|
4153
|
-
}, [
|
|
4154
|
-
blocks,
|
|
4155
|
-
options.scene_url,
|
|
4156
|
-
options.preLoadedTags,
|
|
4157
|
-
options.preLoadedTagsLoading
|
|
4158
|
-
]);
|
|
4159
|
-
const clearSelection = react.useCallback(() => {
|
|
4160
|
-
var _a;
|
|
4161
|
-
setSelectionMenu(null);
|
|
4162
|
-
(_a = window.getSelection()) == null ? void 0 : _a.removeAllRanges();
|
|
4299
|
+
fetchScene();
|
|
4163
4300
|
}, []);
|
|
4164
|
-
|
|
4165
|
-
if (!
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
}
|
|
4178
|
-
}, [selectionMenu, menuPlacement]);
|
|
4179
|
-
react.useEffect(() => {
|
|
4180
|
-
const handleClickOutside = (e) => {
|
|
4181
|
-
if (selectionMenu && !e.target.closest(".tag-menu")) {
|
|
4182
|
-
clearSelection();
|
|
4183
|
-
}
|
|
4301
|
+
const blocks = React4.useMemo(() => {
|
|
4302
|
+
if (!scene || !scene.content) return [];
|
|
4303
|
+
const parser = new DOMParser();
|
|
4304
|
+
const doc = parser.parseFromString(scene.content, "text/html");
|
|
4305
|
+
const divs = Array.from(doc.querySelectorAll("div"));
|
|
4306
|
+
const parsedBlocks = [];
|
|
4307
|
+
const typeMap = {
|
|
4308
|
+
divtype0: "SCENE_HEADING",
|
|
4309
|
+
divtype2: "ACTION",
|
|
4310
|
+
divtype3: "CHARACTER",
|
|
4311
|
+
divtype4: "PARENTHETICAL",
|
|
4312
|
+
divtype5: "DIALOGUE",
|
|
4313
|
+
divtype6: "TRANSITION"
|
|
4184
4314
|
};
|
|
4185
|
-
|
|
4186
|
-
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
4187
|
-
}, [selectionMenu, clearSelection]);
|
|
4188
|
-
const getAbsoluteOffset = (container, targetNode, targetOffset) => {
|
|
4189
|
-
let absoluteOffset = 0;
|
|
4190
|
-
let found = false;
|
|
4191
|
-
const traverse = (node) => {
|
|
4315
|
+
divs.forEach((div) => {
|
|
4192
4316
|
var _a;
|
|
4193
|
-
|
|
4194
|
-
if (
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
absoluteOffset += ((_a = node.nodeValue) == null ? void 0 : _a.length) || 0;
|
|
4201
|
-
} else {
|
|
4202
|
-
for (let i = 0; i < node.childNodes.length; i++) {
|
|
4203
|
-
traverse(node.childNodes[i]);
|
|
4204
|
-
if (found) return;
|
|
4317
|
+
const divText = ((_a = div.textContent) == null ? void 0 : _a.trim()) || "";
|
|
4318
|
+
if (!divText) return;
|
|
4319
|
+
let type = "ACTION";
|
|
4320
|
+
for (const className of Array.from(div.classList)) {
|
|
4321
|
+
if (typeMap[className]) {
|
|
4322
|
+
type = typeMap[className];
|
|
4323
|
+
break;
|
|
4205
4324
|
}
|
|
4206
4325
|
}
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
};
|
|
4211
|
-
const handleMouseUp = () => {
|
|
4212
|
-
const selection = window.getSelection();
|
|
4213
|
-
if (!selection || selection.isCollapsed || !selection.toString().trim())
|
|
4214
|
-
return;
|
|
4215
|
-
const range = selection.getRangeAt(0);
|
|
4216
|
-
let container = range.commonAncestorContainer;
|
|
4217
|
-
if (container.nodeType === Node.TEXT_NODE)
|
|
4218
|
-
container = container.parentElement;
|
|
4219
|
-
const blockElem = container.closest("[data-block-id]");
|
|
4220
|
-
if (!blockElem) return;
|
|
4221
|
-
const blockId = blockElem.getAttribute("data-block-id");
|
|
4222
|
-
const startOffset = getAbsoluteOffset(
|
|
4223
|
-
blockElem,
|
|
4224
|
-
range.startContainer,
|
|
4225
|
-
range.startOffset
|
|
4226
|
-
);
|
|
4227
|
-
const endOffset = getAbsoluteOffset(
|
|
4228
|
-
blockElem,
|
|
4229
|
-
range.endContainer,
|
|
4230
|
-
range.endOffset
|
|
4231
|
-
);
|
|
4232
|
-
if (startOffset !== null && endOffset !== null) {
|
|
4233
|
-
const rect = range.getBoundingClientRect();
|
|
4234
|
-
const blockRect = blockElem.getBoundingClientRect();
|
|
4235
|
-
setSelectionMenu({
|
|
4236
|
-
blockId,
|
|
4237
|
-
startIndex: Math.min(startOffset, endOffset),
|
|
4238
|
-
endIndex: Math.max(startOffset, endOffset),
|
|
4239
|
-
text: selection.toString().trim(),
|
|
4240
|
-
top: rect.top - blockRect.top,
|
|
4241
|
-
left: rect.left - blockRect.left + rect.width / 2
|
|
4242
|
-
});
|
|
4243
|
-
}
|
|
4244
|
-
};
|
|
4245
|
-
const addTag = async (categoryId) => {
|
|
4246
|
-
var _a;
|
|
4247
|
-
if (!selectionMenu) return;
|
|
4248
|
-
const newTag = {
|
|
4249
|
-
id: uuid(),
|
|
4250
|
-
block_id: selectionMenu.blockId,
|
|
4251
|
-
category_id: categoryId,
|
|
4252
|
-
name: selectionMenu.text,
|
|
4253
|
-
start_index: selectionMenu.startIndex,
|
|
4254
|
-
end_index: selectionMenu.endIndex
|
|
4255
|
-
};
|
|
4256
|
-
if (categoryId === "PROP" || categoryId === "SET_PROP") {
|
|
4257
|
-
newTag.quantity = 1;
|
|
4258
|
-
} else if (categoryId === "CAST") {
|
|
4259
|
-
newTag.look = "";
|
|
4260
|
-
newTag.age = "";
|
|
4261
|
-
}
|
|
4262
|
-
setTags((prev) => {
|
|
4263
|
-
const filtered = prev.filter(
|
|
4264
|
-
(t) => t.block_id !== newTag.block_id || !(newTag.end_index > t.start_index && newTag.start_index < t.end_index)
|
|
4265
|
-
);
|
|
4266
|
-
return [...filtered, newTag];
|
|
4326
|
+
const idAttr = div.getAttribute("id");
|
|
4327
|
+
const blockId = idAttr && idAttr.startsWith("par") ? idAttr.substring(3) : idAttr || uuid();
|
|
4328
|
+
parsedBlocks.push({ id: blockId, type, text: divText });
|
|
4267
4329
|
});
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4330
|
+
return parsedBlocks;
|
|
4331
|
+
}, [scene]);
|
|
4332
|
+
const characters = React4.useMemo(() => {
|
|
4333
|
+
const chars = blocks.filter((b) => b.type === "CHARACTER").map((b) => {
|
|
4334
|
+
const text = b.text.trim().toUpperCase();
|
|
4335
|
+
const parenIndex = text.indexOf("(");
|
|
4336
|
+
return parenIndex > -1 ? text.substring(0, parenIndex).trim() : text;
|
|
4337
|
+
}).filter(Boolean);
|
|
4338
|
+
return [...new Set(chars)];
|
|
4339
|
+
}, [blocks]);
|
|
4340
|
+
const handleAISummarize = async () => {
|
|
4277
4341
|
var _a;
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
if (!tagToUpdate) return;
|
|
4295
|
-
const updatedFields = __spreadValues({}, details);
|
|
4296
|
-
if (categoryId) {
|
|
4297
|
-
updatedFields.category_id = categoryId;
|
|
4298
|
-
if (categoryId === "PROP" || categoryId === "SET_PROP") {
|
|
4299
|
-
updatedFields.quantity = (_a = tagToUpdate.quantity) != null ? _a : 1;
|
|
4300
|
-
} else if (categoryId === "CAST") {
|
|
4301
|
-
updatedFields.look = (_b = tagToUpdate.look) != null ? _b : "";
|
|
4302
|
-
updatedFields.age = (_c = tagToUpdate.age) != null ? _c : "";
|
|
4342
|
+
setIsSummarizing(true);
|
|
4343
|
+
const res = await ((_a = options.onAISummarize) == null ? void 0 : _a.call(options, scene.content));
|
|
4344
|
+
if (res.ok) {
|
|
4345
|
+
const data = await res.json();
|
|
4346
|
+
setIsSummarizing(false);
|
|
4347
|
+
let parsedData = [];
|
|
4348
|
+
let parsedSummaryData = {};
|
|
4349
|
+
try {
|
|
4350
|
+
const normalData = JSON.parse(data.data);
|
|
4351
|
+
if (Array.isArray(normalData)) {
|
|
4352
|
+
parsedData = Array.isArray(normalData[0]) ? normalData[0] : [];
|
|
4353
|
+
const summary = Array.isArray(normalData[1]) ? normalData[1] : [];
|
|
4354
|
+
parsedSummaryData = summary[0] || {};
|
|
4355
|
+
}
|
|
4356
|
+
} catch (error2) {
|
|
4357
|
+
console.error("Error parsing AI summary data:", error2);
|
|
4303
4358
|
}
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
if (
|
|
4308
|
-
|
|
4309
|
-
|
|
4359
|
+
setSceneBrief(parsedSummaryData.summarise || "");
|
|
4360
|
+
const newTags = [];
|
|
4361
|
+
parsedData.forEach((aiTag) => {
|
|
4362
|
+
if (!aiTag.block_id || !aiTag.category_id || typeof aiTag.start_index !== "number" || typeof aiTag.end_index !== "number") {
|
|
4363
|
+
return;
|
|
4364
|
+
}
|
|
4365
|
+
const newTag = {
|
|
4366
|
+
id: aiTag.id || uuid(),
|
|
4367
|
+
block_id: String(aiTag.block_id).startsWith("par") ? String(aiTag.block_id).substring(3) : String(aiTag.block_id),
|
|
4368
|
+
category_id: aiTag.category_id,
|
|
4369
|
+
name: aiTag.name,
|
|
4370
|
+
start_index: aiTag.start_index,
|
|
4371
|
+
end_index: aiTag.end_index
|
|
4372
|
+
};
|
|
4373
|
+
if (aiTag.category_id === "PROP" || aiTag.category_id === "SET_PROP") {
|
|
4374
|
+
newTag.quantity = 1;
|
|
4375
|
+
} else if (aiTag.category_id === "CAST") {
|
|
4376
|
+
newTag.look = "";
|
|
4377
|
+
newTag.age = "";
|
|
4378
|
+
}
|
|
4379
|
+
newTags.push(newTag);
|
|
4380
|
+
});
|
|
4381
|
+
if (newTags.length > 0) {
|
|
4382
|
+
const originalTags = tags;
|
|
4383
|
+
setTags((prev) => {
|
|
4384
|
+
const merged = [...prev];
|
|
4385
|
+
newTags.forEach((newTag) => {
|
|
4386
|
+
const isOverlapping = merged.some(
|
|
4387
|
+
(t) => t.block_id === newTag.block_id && newTag.end_index > t.start_index && newTag.start_index < t.end_index
|
|
4388
|
+
);
|
|
4389
|
+
if (!isOverlapping) {
|
|
4390
|
+
merged.push(newTag);
|
|
4391
|
+
}
|
|
4392
|
+
});
|
|
4393
|
+
return merged;
|
|
4394
|
+
});
|
|
4395
|
+
try {
|
|
4396
|
+
if (options.onTagsBulkAdded) {
|
|
4397
|
+
await options.onTagsBulkAdded(newTags, parsedSummaryData.summarise);
|
|
4398
|
+
}
|
|
4399
|
+
} catch (error2) {
|
|
4400
|
+
console.error("Failed to bulk add AI-generated tags:", error2);
|
|
4401
|
+
setTags(originalTags);
|
|
4310
4402
|
}
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
await ((_d = options.onTagUpdated) == null ? void 0 : _d.call(options, id, categoryId || null, details));
|
|
4317
|
-
} catch (error2) {
|
|
4318
|
-
console.error("Failed to update tag:", error2);
|
|
4319
|
-
setTags((prev) => prev.map((t) => t.id === id ? tagToUpdate : t));
|
|
4403
|
+
}
|
|
4404
|
+
return data;
|
|
4405
|
+
} else {
|
|
4406
|
+
setIsSummarizing(false);
|
|
4407
|
+
console.error("Failed to summarize scene:", res);
|
|
4320
4408
|
}
|
|
4321
4409
|
};
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
maxWidth = "max-w-4xl",
|
|
4348
|
-
menuRef
|
|
4349
|
-
}) {
|
|
4350
|
-
react.useEffect(() => {
|
|
4351
|
-
document.body.style.overflow = "hidden";
|
|
4352
|
-
return () => {
|
|
4353
|
-
document.body.style.overflow = "";
|
|
4354
|
-
};
|
|
4355
|
-
}, []);
|
|
4356
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4357
|
-
"div",
|
|
4358
|
-
{
|
|
4359
|
-
ref: menuRef,
|
|
4360
|
-
onClick: onClose,
|
|
4361
|
-
className: "fixed inset-0 z-50 overflow-y-auto bg-black/40 backdrop-blur-xs",
|
|
4362
|
-
"data-lenis-prevent": "true",
|
|
4363
|
-
children: [
|
|
4364
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-full items-center justify-center p-4 sm:p-6", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4365
|
-
"div",
|
|
4366
|
-
{
|
|
4367
|
-
onClick: (e) => e.stopPropagation(),
|
|
4368
|
-
className: `relative w-full ${maxWidth} bg-white rounded-3xl shadow-2xl transform transition-all duration-300 scale-100 opacity-100 animate-modal`,
|
|
4369
|
-
children: [
|
|
4370
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-b border-gray-200 pb-3 pt-6 px-6", children: [
|
|
4371
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl sm:text-2xl font-medium text-blumine-600 line-clamp-1", children: title }),
|
|
4372
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4373
|
-
"button",
|
|
4374
|
-
{
|
|
4375
|
-
onClick: onClose,
|
|
4376
|
-
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",
|
|
4377
|
-
"aria-label": "Close",
|
|
4378
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 20 })
|
|
4379
|
-
}
|
|
4380
|
-
)
|
|
4381
|
-
] }),
|
|
4382
|
-
children
|
|
4383
|
-
]
|
|
4410
|
+
const bulkCreateTags = React4.useCallback(async () => {
|
|
4411
|
+
if (blocks.length === 0) return;
|
|
4412
|
+
const newTags = [];
|
|
4413
|
+
const seenCharacters = /* @__PURE__ */ new Set();
|
|
4414
|
+
const timeOfDays = ["DAY", "NIGHT"];
|
|
4415
|
+
const isTimeOfDay = (str) => timeOfDays.includes(str.toUpperCase());
|
|
4416
|
+
blocks.forEach((block) => {
|
|
4417
|
+
if (block.type === "CHARACTER") {
|
|
4418
|
+
const text = block.text.trim();
|
|
4419
|
+
const parenIndex = text.indexOf("(");
|
|
4420
|
+
const charName = parenIndex > -1 ? text.substring(0, parenIndex).trim() : text;
|
|
4421
|
+
if (charName && !seenCharacters.has(charName.toUpperCase())) {
|
|
4422
|
+
seenCharacters.add(charName.toUpperCase());
|
|
4423
|
+
const startIndex = text.indexOf(charName);
|
|
4424
|
+
if (startIndex !== -1) {
|
|
4425
|
+
newTags.push({
|
|
4426
|
+
id: uuid(),
|
|
4427
|
+
block_id: block.id,
|
|
4428
|
+
category_id: "CAST",
|
|
4429
|
+
name: charName,
|
|
4430
|
+
start_index: startIndex,
|
|
4431
|
+
end_index: startIndex + charName.length,
|
|
4432
|
+
look: "",
|
|
4433
|
+
age: ""
|
|
4434
|
+
});
|
|
4384
4435
|
}
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4436
|
+
}
|
|
4437
|
+
} else if (block.type === "SCENE_HEADING") {
|
|
4438
|
+
const text = block.text.trim();
|
|
4439
|
+
const typeMatch = text.match(
|
|
4440
|
+
/^(INT\/EXT|INT\.?\/EXT\.?|INT\.EXT\.?|INT|EXT|I\/E)\.?\s+/i
|
|
4441
|
+
);
|
|
4442
|
+
let remainingText = text;
|
|
4443
|
+
let offset = 0;
|
|
4444
|
+
if (typeMatch) {
|
|
4445
|
+
offset = typeMatch[0].length;
|
|
4446
|
+
remainingText = text.substring(offset);
|
|
4447
|
+
}
|
|
4448
|
+
const parts = remainingText.split(/\s+-\s+/);
|
|
4449
|
+
if (parts.length > 0) {
|
|
4450
|
+
const locationName = parts[0].trim();
|
|
4451
|
+
const locStart = text.indexOf(locationName, offset);
|
|
4452
|
+
if (locStart !== -1 && locationName) {
|
|
4453
|
+
newTags.push({
|
|
4454
|
+
id: uuid(),
|
|
4455
|
+
block_id: block.id,
|
|
4456
|
+
category_id: "LOCATION",
|
|
4457
|
+
name: locationName,
|
|
4458
|
+
start_index: locStart,
|
|
4459
|
+
end_index: locStart + locationName.length
|
|
4460
|
+
});
|
|
4396
4461
|
}
|
|
4397
|
-
.
|
|
4398
|
-
|
|
4462
|
+
if (parts.length > 1) {
|
|
4463
|
+
const secondPart = parts[1].trim();
|
|
4464
|
+
const isLast = parts.length === 2;
|
|
4465
|
+
if (!isLast || !isTimeOfDay(secondPart)) {
|
|
4466
|
+
const subLocStart = text.indexOf(
|
|
4467
|
+
secondPart,
|
|
4468
|
+
locStart + locationName.length
|
|
4469
|
+
);
|
|
4470
|
+
if (subLocStart !== -1 && secondPart) {
|
|
4471
|
+
newTags.push({
|
|
4472
|
+
id: uuid(),
|
|
4473
|
+
block_id: block.id,
|
|
4474
|
+
category_id: "SUBLOCATION",
|
|
4475
|
+
name: secondPart,
|
|
4476
|
+
start_index: subLocStart,
|
|
4477
|
+
end_index: subLocStart + secondPart.length
|
|
4478
|
+
});
|
|
4479
|
+
}
|
|
4480
|
+
}
|
|
4399
4481
|
}
|
|
4400
|
-
|
|
4401
|
-
]
|
|
4402
|
-
}
|
|
4403
|
-
);
|
|
4404
|
-
}
|
|
4405
|
-
var modal_layout_default = ModalLayout;
|
|
4406
|
-
var FormikSelect = ({
|
|
4407
|
-
label,
|
|
4408
|
-
name,
|
|
4409
|
-
selectedOption,
|
|
4410
|
-
optionData,
|
|
4411
|
-
value,
|
|
4412
|
-
disable,
|
|
4413
|
-
divClasses,
|
|
4414
|
-
valueProperty = "name",
|
|
4415
|
-
labelProperty = "name",
|
|
4416
|
-
label2propery,
|
|
4417
|
-
brackets,
|
|
4418
|
-
className,
|
|
4419
|
-
enableRedAsterick = false,
|
|
4420
|
-
disableOptionProperty = "disable",
|
|
4421
|
-
disabledOptionText = "",
|
|
4422
|
-
onChange
|
|
4423
|
-
}) => {
|
|
4424
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${divClasses}`, children: [
|
|
4425
|
-
label && /* @__PURE__ */ jsxRuntime.jsxs("label", { className: `mb-1.5 text-sm font-medium text-slate-700 capitalize flex`, children: [
|
|
4426
|
-
label,
|
|
4427
|
-
enableRedAsterick && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AsteriskIcon, { className: "size-3 text-red-500" })
|
|
4428
|
-
] }),
|
|
4429
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4430
|
-
formik.Field,
|
|
4431
|
-
{
|
|
4432
|
-
name,
|
|
4433
|
-
as: "select",
|
|
4434
|
-
value,
|
|
4435
|
-
disabled: disable,
|
|
4436
|
-
onChange,
|
|
4437
|
-
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`,
|
|
4438
|
-
children: [
|
|
4439
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { selected: true, value: "", disabled: true, className: "capitalize", children: selectedOption }),
|
|
4440
|
-
optionData == null ? void 0 : optionData.map((option, index) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4441
|
-
"option",
|
|
4442
|
-
{
|
|
4443
|
-
value: option[valueProperty],
|
|
4444
|
-
disabled: option[disableOptionProperty],
|
|
4445
|
-
children: [
|
|
4446
|
-
option[labelProperty],
|
|
4447
|
-
label2propery ? brackets ? ` (${option[label2propery]})` : option[label2propery] : "",
|
|
4448
|
-
" ",
|
|
4449
|
-
option[disableOptionProperty] && `(${disabledOptionText})`
|
|
4450
|
-
]
|
|
4451
|
-
},
|
|
4452
|
-
index
|
|
4453
|
-
))
|
|
4454
|
-
]
|
|
4455
|
-
}
|
|
4456
|
-
),
|
|
4457
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4458
|
-
formik.ErrorMessage,
|
|
4459
|
-
{
|
|
4460
|
-
name,
|
|
4461
|
-
className: "text-red-500 text-sm mt-1",
|
|
4462
|
-
component: "div"
|
|
4482
|
+
}
|
|
4463
4483
|
}
|
|
4464
|
-
)
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
"
|
|
4484
|
+
});
|
|
4485
|
+
if (newTags.length > 0) {
|
|
4486
|
+
const originalTags = tags;
|
|
4487
|
+
setTags((prev) => {
|
|
4488
|
+
const merged = [...prev];
|
|
4489
|
+
const existingChars = new Set(
|
|
4490
|
+
merged.filter(
|
|
4491
|
+
(t) => t.category_id === "CHARACTER" || t.category_id === "CAST"
|
|
4492
|
+
).map((t) => t.name.toUpperCase())
|
|
4493
|
+
);
|
|
4494
|
+
newTags.forEach((newTag) => {
|
|
4495
|
+
if (newTag.category_id === "CHARACTER") {
|
|
4496
|
+
if (existingChars.has(newTag.name.toUpperCase())) return;
|
|
4497
|
+
existingChars.add(newTag.name.toUpperCase());
|
|
4498
|
+
}
|
|
4499
|
+
const isOverlapping = merged.some(
|
|
4500
|
+
(t) => t.block_id === newTag.block_id && newTag.end_index > t.start_index && newTag.start_index < t.end_index
|
|
4501
|
+
);
|
|
4502
|
+
if (!isOverlapping) {
|
|
4503
|
+
merged.push(newTag);
|
|
4504
|
+
}
|
|
4505
|
+
});
|
|
4506
|
+
return merged;
|
|
4507
|
+
});
|
|
4508
|
+
try {
|
|
4509
|
+
if (options.onTagsBulkAdded) {
|
|
4510
|
+
await options.onTagsBulkAdded(newTags);
|
|
4511
|
+
}
|
|
4512
|
+
} catch (error2) {
|
|
4513
|
+
console.error("Failed to bulk create tags:", error2);
|
|
4514
|
+
setTags(originalTags);
|
|
4515
|
+
}
|
|
4516
|
+
}
|
|
4517
|
+
}, [blocks, tags, options.onTagsBulkAdded]);
|
|
4518
|
+
React4.useEffect(() => {
|
|
4519
|
+
setSceneBrief("");
|
|
4520
|
+
autoTaggedSceneRef.current = null;
|
|
4521
|
+
}, [options.scene_url]);
|
|
4522
|
+
React4.useEffect(() => {
|
|
4523
|
+
if (options.preLoadedTags && options.preLoadedTags.length > 0) {
|
|
4524
|
+
setTags((prev) => {
|
|
4525
|
+
if (JSON.stringify(prev) === JSON.stringify(options.preLoadedTags)) {
|
|
4526
|
+
return prev;
|
|
4527
|
+
}
|
|
4528
|
+
return options.preLoadedTags || [];
|
|
4529
|
+
});
|
|
4530
|
+
}
|
|
4531
|
+
}, [options.preLoadedTags]);
|
|
4532
|
+
React4.useEffect(() => {
|
|
4533
|
+
const doBulkCreate = async () => {
|
|
4534
|
+
if (blocks.length === 0) return;
|
|
4535
|
+
if (options.preLoadedTagsLoading) return;
|
|
4536
|
+
if (autoTaggedSceneRef.current === options.scene_url) return;
|
|
4537
|
+
const hasPreloadedTags = options.preLoadedTags && options.preLoadedTags.length > 0;
|
|
4538
|
+
autoTaggedSceneRef.current = options.scene_url;
|
|
4539
|
+
if (hasPreloadedTags) return;
|
|
4540
|
+
await bulkCreateTags();
|
|
4541
|
+
};
|
|
4542
|
+
doBulkCreate();
|
|
4543
|
+
}, [
|
|
4544
|
+
blocks,
|
|
4545
|
+
options.scene_url,
|
|
4546
|
+
options.preLoadedTags,
|
|
4547
|
+
options.preLoadedTagsLoading
|
|
4500
4548
|
]);
|
|
4501
|
-
const
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4549
|
+
const clearSelection = React4.useCallback(() => {
|
|
4550
|
+
var _a;
|
|
4551
|
+
setSelectionMenu(null);
|
|
4552
|
+
(_a = window.getSelection()) == null ? void 0 : _a.removeAllRanges();
|
|
4553
|
+
}, []);
|
|
4554
|
+
React4.useEffect(() => {
|
|
4555
|
+
if (!selectionMenu) {
|
|
4556
|
+
setMenuPlacement("top");
|
|
4506
4557
|
}
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4558
|
+
}, [selectionMenu]);
|
|
4559
|
+
React4.useEffect(() => {
|
|
4560
|
+
if (selectionMenu && menuRef.current) {
|
|
4561
|
+
const rect = menuRef.current.getBoundingClientRect();
|
|
4562
|
+
if (menuPlacement === "top" && rect.top < 100) {
|
|
4563
|
+
setMenuPlacement("bottom");
|
|
4564
|
+
} else if (menuPlacement === "bottom" && rect.bottom > window.innerHeight - 40 && rect.top > 300) {
|
|
4565
|
+
setMenuPlacement("top");
|
|
4566
|
+
}
|
|
4514
4567
|
}
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
{
|
|
4521
|
-
className: `${labelClassName} mb-1.5 text-sm font-medium text-slate-700 capitalize flex`,
|
|
4522
|
-
children: [
|
|
4523
|
-
label,
|
|
4524
|
-
enableRedAsterick && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AsteriskIcon, { className: "size-3 text-red-500" })
|
|
4525
|
-
]
|
|
4568
|
+
}, [selectionMenu, menuPlacement]);
|
|
4569
|
+
React4.useEffect(() => {
|
|
4570
|
+
const handleClickOutside = (e) => {
|
|
4571
|
+
if (selectionMenu && !e.target.closest(".tag-menu")) {
|
|
4572
|
+
clearSelection();
|
|
4526
4573
|
}
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
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`
|
|
4542
|
-
})
|
|
4543
|
-
),
|
|
4544
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4545
|
-
formik.ErrorMessage,
|
|
4546
|
-
{
|
|
4547
|
-
name,
|
|
4548
|
-
className: "text-red-500 text-sm mt-1",
|
|
4549
|
-
component: "div"
|
|
4574
|
+
};
|
|
4575
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
4576
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
4577
|
+
}, [selectionMenu, clearSelection]);
|
|
4578
|
+
const getAbsoluteOffset = (container, targetNode, targetOffset) => {
|
|
4579
|
+
let absoluteOffset = 0;
|
|
4580
|
+
let found = false;
|
|
4581
|
+
const traverse = (node) => {
|
|
4582
|
+
var _a;
|
|
4583
|
+
if (found) return;
|
|
4584
|
+
if (node === targetNode) {
|
|
4585
|
+
absoluteOffset += targetOffset;
|
|
4586
|
+
found = true;
|
|
4587
|
+
return;
|
|
4550
4588
|
}
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
}
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
readOnly,
|
|
4559
|
-
divClasses = "",
|
|
4560
|
-
className = "",
|
|
4561
|
-
length,
|
|
4562
|
-
disable,
|
|
4563
|
-
min,
|
|
4564
|
-
max,
|
|
4565
|
-
enableRedAsterick = false
|
|
4566
|
-
}) => {
|
|
4567
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${divClasses}`, children: [
|
|
4568
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: `mb-1.5 text-sm font-medium text-slate-700 capitalize flex`, children: [
|
|
4569
|
-
label,
|
|
4570
|
-
enableRedAsterick && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AsteriskIcon, { className: "size-3 text-red-500" })
|
|
4571
|
-
] }),
|
|
4572
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4573
|
-
formik.Field,
|
|
4574
|
-
{
|
|
4575
|
-
as: "textarea",
|
|
4576
|
-
name,
|
|
4577
|
-
placeholder,
|
|
4578
|
-
readOnly,
|
|
4579
|
-
maxLength: length,
|
|
4580
|
-
disabled: disable,
|
|
4581
|
-
max,
|
|
4582
|
-
min,
|
|
4583
|
-
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`
|
|
4589
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
4590
|
+
absoluteOffset += ((_a = node.nodeValue) == null ? void 0 : _a.length) || 0;
|
|
4591
|
+
} else {
|
|
4592
|
+
for (let i = 0; i < node.childNodes.length; i++) {
|
|
4593
|
+
traverse(node.childNodes[i]);
|
|
4594
|
+
if (found) return;
|
|
4595
|
+
}
|
|
4584
4596
|
}
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4597
|
+
};
|
|
4598
|
+
traverse(container);
|
|
4599
|
+
return found ? absoluteOffset : null;
|
|
4600
|
+
};
|
|
4601
|
+
const handleMouseUp = () => {
|
|
4602
|
+
const selection = window.getSelection();
|
|
4603
|
+
if (!selection || selection.isCollapsed || !selection.toString().trim())
|
|
4604
|
+
return;
|
|
4605
|
+
const range = selection.getRangeAt(0);
|
|
4606
|
+
let container = range.commonAncestorContainer;
|
|
4607
|
+
if (container.nodeType === Node.TEXT_NODE)
|
|
4608
|
+
container = container.parentElement;
|
|
4609
|
+
const blockElem = container.closest("[data-block-id]");
|
|
4610
|
+
if (!blockElem) return;
|
|
4611
|
+
const blockId = blockElem.getAttribute("data-block-id");
|
|
4612
|
+
const startOffset = getAbsoluteOffset(
|
|
4613
|
+
blockElem,
|
|
4614
|
+
range.startContainer,
|
|
4615
|
+
range.startOffset
|
|
4616
|
+
);
|
|
4617
|
+
const endOffset = getAbsoluteOffset(
|
|
4618
|
+
blockElem,
|
|
4619
|
+
range.endContainer,
|
|
4620
|
+
range.endOffset
|
|
4621
|
+
);
|
|
4622
|
+
if (startOffset !== null && endOffset !== null) {
|
|
4623
|
+
const rect = range.getBoundingClientRect();
|
|
4624
|
+
const blockRect = blockElem.getBoundingClientRect();
|
|
4625
|
+
setSelectionMenu({
|
|
4626
|
+
blockId,
|
|
4627
|
+
startIndex: Math.min(startOffset, endOffset),
|
|
4628
|
+
endIndex: Math.max(startOffset, endOffset),
|
|
4629
|
+
text: selection.toString().trim(),
|
|
4630
|
+
top: rect.top - blockRect.top,
|
|
4631
|
+
left: rect.left - blockRect.left + rect.width / 2
|
|
4632
|
+
});
|
|
4633
|
+
}
|
|
4634
|
+
};
|
|
4635
|
+
const addTag = async (categoryId) => {
|
|
4636
|
+
var _a;
|
|
4637
|
+
if (!selectionMenu) return;
|
|
4638
|
+
const newTag = {
|
|
4639
|
+
id: uuid(),
|
|
4640
|
+
block_id: selectionMenu.blockId,
|
|
4641
|
+
category_id: categoryId,
|
|
4642
|
+
name: selectionMenu.text,
|
|
4643
|
+
start_index: selectionMenu.startIndex,
|
|
4644
|
+
end_index: selectionMenu.endIndex
|
|
4645
|
+
};
|
|
4646
|
+
if (categoryId === "PROP" || categoryId === "SET_PROP") {
|
|
4647
|
+
newTag.quantity = 1;
|
|
4648
|
+
} else if (categoryId === "CAST") {
|
|
4649
|
+
newTag.look = "";
|
|
4650
|
+
newTag.age = "";
|
|
4651
|
+
}
|
|
4652
|
+
setTags((prev) => {
|
|
4653
|
+
const filtered = prev.filter(
|
|
4654
|
+
(t) => t.block_id !== newTag.block_id || !(newTag.end_index > t.start_index && newTag.start_index < t.end_index)
|
|
4655
|
+
);
|
|
4656
|
+
return [...filtered, newTag];
|
|
4657
|
+
});
|
|
4658
|
+
clearSelection();
|
|
4659
|
+
try {
|
|
4660
|
+
await ((_a = options.onTagAdded) == null ? void 0 : _a.call(options, newTag));
|
|
4661
|
+
} catch (error2) {
|
|
4662
|
+
console.error("Failed to add tag:", error2);
|
|
4663
|
+
setTags((prev) => prev.filter((t) => t.id !== newTag.id));
|
|
4664
|
+
}
|
|
4665
|
+
};
|
|
4666
|
+
const removeTag = async (e, id) => {
|
|
4667
|
+
var _a;
|
|
4668
|
+
e.stopPropagation();
|
|
4669
|
+
e.preventDefault();
|
|
4670
|
+
const tagToRemove = tags.find((t) => t.id === id);
|
|
4671
|
+
if (!tagToRemove) return;
|
|
4672
|
+
setTags((prev) => prev.filter((t) => t.id !== id));
|
|
4673
|
+
clearSelection();
|
|
4674
|
+
try {
|
|
4675
|
+
await ((_a = options.onTagRemoved) == null ? void 0 : _a.call(options, id));
|
|
4676
|
+
} catch (error2) {
|
|
4677
|
+
console.error("Failed to remove tag:", error2);
|
|
4678
|
+
setTags((prev) => [...prev, tagToRemove]);
|
|
4679
|
+
}
|
|
4680
|
+
};
|
|
4681
|
+
const updateTag = async (id, categoryId, details) => {
|
|
4682
|
+
var _a, _b, _c, _d;
|
|
4683
|
+
const tagToUpdate = tags.find((t) => t.id === id);
|
|
4684
|
+
if (!tagToUpdate) return;
|
|
4685
|
+
const updatedFields = __spreadValues({}, details);
|
|
4686
|
+
if (categoryId) {
|
|
4687
|
+
updatedFields.category_id = categoryId;
|
|
4688
|
+
if (categoryId === "PROP" || categoryId === "SET_PROP") {
|
|
4689
|
+
updatedFields.quantity = (_a = tagToUpdate.quantity) != null ? _a : 1;
|
|
4690
|
+
} else if (categoryId === "CAST") {
|
|
4691
|
+
updatedFields.look = (_b = tagToUpdate.look) != null ? _b : "";
|
|
4692
|
+
updatedFields.age = (_c = tagToUpdate.age) != null ? _c : "";
|
|
4592
4693
|
}
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4694
|
+
}
|
|
4695
|
+
setTags(
|
|
4696
|
+
(prev) => prev.map((t) => {
|
|
4697
|
+
if (t.id === id) return __spreadValues(__spreadValues({}, t), updatedFields);
|
|
4698
|
+
if (details && t.name === tagToUpdate.name && t.category_id === tagToUpdate.category_id) {
|
|
4699
|
+
return __spreadValues(__spreadValues({}, t), details);
|
|
4700
|
+
}
|
|
4701
|
+
return t;
|
|
4702
|
+
})
|
|
4703
|
+
);
|
|
4704
|
+
if (categoryId) clearSelection();
|
|
4705
|
+
try {
|
|
4706
|
+
await ((_d = options.onTagUpdated) == null ? void 0 : _d.call(options, id, categoryId || null, details));
|
|
4707
|
+
} catch (error2) {
|
|
4708
|
+
console.error("Failed to update tag:", error2);
|
|
4709
|
+
setTags((prev) => prev.map((t) => t.id === id ? tagToUpdate : t));
|
|
4710
|
+
}
|
|
4711
|
+
};
|
|
4712
|
+
return {
|
|
4713
|
+
scene,
|
|
4714
|
+
blocks,
|
|
4715
|
+
characters,
|
|
4716
|
+
isLoading,
|
|
4717
|
+
error,
|
|
4718
|
+
tags,
|
|
4719
|
+
selectionMenu,
|
|
4720
|
+
handleMouseUp,
|
|
4721
|
+
addTag,
|
|
4722
|
+
updateTag,
|
|
4723
|
+
removeTag,
|
|
4724
|
+
clearSelection,
|
|
4725
|
+
menuPlacement,
|
|
4726
|
+
menuRef,
|
|
4727
|
+
sceneBrief,
|
|
4728
|
+
setSceneBrief,
|
|
4729
|
+
handleAISummarize,
|
|
4730
|
+
isSummarizing
|
|
4731
|
+
};
|
|
4732
|
+
}
|
|
4733
|
+
function ModalLayout({
|
|
4734
|
+
children,
|
|
4735
|
+
onClose,
|
|
4736
|
+
title,
|
|
4737
|
+
maxWidth = "max-w-4xl",
|
|
4738
|
+
menuRef
|
|
4739
|
+
}) {
|
|
4740
|
+
React4.useEffect(() => {
|
|
4741
|
+
document.body.style.overflow = "hidden";
|
|
4742
|
+
return () => {
|
|
4743
|
+
document.body.style.overflow = "";
|
|
4744
|
+
};
|
|
4745
|
+
}, []);
|
|
4746
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4747
|
+
"div",
|
|
4748
|
+
{
|
|
4749
|
+
ref: menuRef,
|
|
4750
|
+
onClick: onClose,
|
|
4751
|
+
className: "fixed inset-0 z-50 overflow-y-auto bg-black/40 backdrop-blur-xs",
|
|
4752
|
+
"data-lenis-prevent": "true",
|
|
4753
|
+
children: [
|
|
4754
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-full items-center justify-center p-4 sm:p-6", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4755
|
+
"div",
|
|
4756
|
+
{
|
|
4757
|
+
onClick: (e) => e.stopPropagation(),
|
|
4758
|
+
className: `relative w-full ${maxWidth} bg-white rounded-3xl shadow-2xl transform transition-all duration-300 scale-100 opacity-100 animate-modal`,
|
|
4759
|
+
children: [
|
|
4760
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-b border-gray-200 pb-3 pt-6 px-6", children: [
|
|
4761
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl sm:text-2xl font-medium text-blumine-600 line-clamp-1", children: title }),
|
|
4762
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4763
|
+
"button",
|
|
4764
|
+
{
|
|
4765
|
+
onClick: onClose,
|
|
4766
|
+
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",
|
|
4767
|
+
"aria-label": "Close",
|
|
4768
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 20 })
|
|
4769
|
+
}
|
|
4770
|
+
)
|
|
4771
|
+
] }),
|
|
4772
|
+
children
|
|
4773
|
+
]
|
|
4774
|
+
}
|
|
4775
|
+
) }),
|
|
4776
|
+
/* @__PURE__ */ jsxRuntime.jsx("style", { children: `
|
|
4777
|
+
@keyframes modal {
|
|
4778
|
+
0% {
|
|
4779
|
+
opacity: 0;
|
|
4780
|
+
transform: translateY(20px) scale(0.95);
|
|
4781
|
+
}
|
|
4782
|
+
100% {
|
|
4783
|
+
opacity: 1;
|
|
4784
|
+
transform: translateY(0) scale(1);
|
|
4785
|
+
}
|
|
4786
|
+
}
|
|
4787
|
+
.animate-modal {
|
|
4788
|
+
animation: modal 0.3s ease-out forwards;
|
|
4789
|
+
}
|
|
4790
|
+
` })
|
|
4791
|
+
]
|
|
4792
|
+
}
|
|
4793
|
+
);
|
|
4794
|
+
}
|
|
4795
|
+
var modal_layout_default = ModalLayout;
|
|
4596
4796
|
|
|
4597
4797
|
// data/shot-data.ts
|
|
4598
4798
|
var shot_types = [
|
|
@@ -4863,8 +5063,8 @@ var AddShotForm = ({
|
|
|
4863
5063
|
duration_seconds: Yup__namespace.number().min(0).required("Required"),
|
|
4864
5064
|
camera_name: Yup__namespace.string().required("Required")
|
|
4865
5065
|
});
|
|
4866
|
-
const [step, setStep] =
|
|
4867
|
-
const [highestStep, setHighestStep] =
|
|
5066
|
+
const [step, setStep] = React4.useState(1);
|
|
5067
|
+
const [highestStep, setHighestStep] = React4.useState(1);
|
|
4868
5068
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4869
5069
|
formik.Formik,
|
|
4870
5070
|
{
|
|
@@ -5353,22 +5553,32 @@ var ProductionSetupModal = ({
|
|
|
5353
5553
|
initialValues
|
|
5354
5554
|
}) => {
|
|
5355
5555
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-[80vh] flex items-center justify-center px-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full max-w-lg", children: [
|
|
5356
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "
|
|
5556
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "my-8 text-center", children: [
|
|
5357
5557
|
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl md:text-3xl font-semibold text-slate-900 tracking-tight", children: "Production Setup for this Scene" }),
|
|
5358
5558
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-slate-500 mt-2", children: "Configure your scene and camera setup to get started" })
|
|
5359
5559
|
] }),
|
|
5360
5560
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 md:p-8", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5361
5561
|
formik.Formik,
|
|
5362
5562
|
{
|
|
5363
|
-
initialValues: initialValues || {
|
|
5563
|
+
initialValues: initialValues || {
|
|
5564
|
+
numCameras: 1,
|
|
5565
|
+
scene_type: "",
|
|
5566
|
+
dance_choreographer_required: false,
|
|
5567
|
+
action_sequence_required: false
|
|
5568
|
+
},
|
|
5364
5569
|
validationSchema: Yup__namespace.object({
|
|
5365
5570
|
numCameras: Yup__namespace.number().min(1, "Must be at least 1").max(20, "Cannot add more than 20 cameras").required("Number of cameras is required"),
|
|
5366
5571
|
scene_type: Yup__namespace.string().required("Scene type is required")
|
|
5367
5572
|
}),
|
|
5368
5573
|
onSubmit: async (values) => {
|
|
5369
|
-
await initializeProduction(
|
|
5574
|
+
await initializeProduction(
|
|
5575
|
+
values.numCameras,
|
|
5576
|
+
values.scene_type,
|
|
5577
|
+
values.dance_choreographer_required,
|
|
5578
|
+
values.action_sequence_required
|
|
5579
|
+
);
|
|
5370
5580
|
},
|
|
5371
|
-
children: ({ isSubmitting, setFieldValue }) => /* @__PURE__ */ jsxRuntime.jsxs(formik.Form, { className: "flex flex-col gap-6", children: [
|
|
5581
|
+
children: ({ isSubmitting, setFieldValue, values }) => /* @__PURE__ */ jsxRuntime.jsxs(formik.Form, { className: "flex flex-col gap-6", children: [
|
|
5372
5582
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-5", children: [
|
|
5373
5583
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5374
5584
|
FormikSelect,
|
|
@@ -5391,7 +5601,65 @@ var ProductionSetupModal = ({
|
|
|
5391
5601
|
max: 20,
|
|
5392
5602
|
enableRedAsterick: true
|
|
5393
5603
|
}
|
|
5394
|
-
)
|
|
5604
|
+
),
|
|
5605
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
5606
|
+
/* @__PURE__ */ jsxRuntime.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.dance_choreographer_required ? "bg-blumine-50/30 border-blumine-200 shadow-sm" : "bg-slate-50/50 border-slate-100 hover:border-slate-200"}`,
|
|
5610
|
+
onClick: () => setFieldValue(
|
|
5611
|
+
"dance_choreographer_required",
|
|
5612
|
+
!values.dance_choreographer_required
|
|
5613
|
+
),
|
|
5614
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between w-full", children: [
|
|
5615
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
5616
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[15px] font-bold text-slate-800", children: "Dance Sequence" }),
|
|
5617
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] text-slate-500 font-medium mt-0.5", children: "Choreography needed" })
|
|
5618
|
+
] }),
|
|
5619
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5620
|
+
Switch,
|
|
5621
|
+
{
|
|
5622
|
+
isOn: values.dance_choreographer_required || false,
|
|
5623
|
+
handleToggle: () => setFieldValue(
|
|
5624
|
+
"dance_choreographer_required",
|
|
5625
|
+
!values.dance_choreographer_required
|
|
5626
|
+
),
|
|
5627
|
+
activeColor: "bg-blumine-600",
|
|
5628
|
+
divClasses: "w-fit!"
|
|
5629
|
+
}
|
|
5630
|
+
)
|
|
5631
|
+
] })
|
|
5632
|
+
}
|
|
5633
|
+
),
|
|
5634
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5635
|
+
"div",
|
|
5636
|
+
{
|
|
5637
|
+
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"}`,
|
|
5638
|
+
onClick: () => setFieldValue(
|
|
5639
|
+
"action_sequence_required",
|
|
5640
|
+
!values.action_sequence_required
|
|
5641
|
+
),
|
|
5642
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between w-full", children: [
|
|
5643
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
5644
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[15px] font-bold text-slate-800", children: "Action Sequence" }),
|
|
5645
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] text-slate-500 font-medium mt-0.5", children: "Stunt & combat logic" })
|
|
5646
|
+
] }),
|
|
5647
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5648
|
+
Switch,
|
|
5649
|
+
{
|
|
5650
|
+
isOn: values.action_sequence_required || false,
|
|
5651
|
+
handleToggle: () => setFieldValue(
|
|
5652
|
+
"action_sequence_required",
|
|
5653
|
+
!values.action_sequence_required
|
|
5654
|
+
),
|
|
5655
|
+
activeColor: "bg-rose-500",
|
|
5656
|
+
divClasses: "w-fit!"
|
|
5657
|
+
}
|
|
5658
|
+
)
|
|
5659
|
+
] })
|
|
5660
|
+
}
|
|
5661
|
+
)
|
|
5662
|
+
] })
|
|
5395
5663
|
] }),
|
|
5396
5664
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 bg-slate-50 border border-slate-100 p-4 rounded-xl", children: [
|
|
5397
5665
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Info, { className: "w-5 h-5 text-slate-400 shrink-0 mt-0.5" }),
|
|
@@ -5625,18 +5893,18 @@ function ShotBreakdownView({
|
|
|
5625
5893
|
}) {
|
|
5626
5894
|
var _a, _b, _c;
|
|
5627
5895
|
const COURIER_STACK = "'Courier Prime', 'Courier', monospace";
|
|
5628
|
-
const containerRef =
|
|
5629
|
-
const [isSidebarOpen, setIsSidebarOpen] =
|
|
5630
|
-
const [toggledShotId, setToggledShotId] =
|
|
5896
|
+
const containerRef = React4.useRef(null);
|
|
5897
|
+
const [isSidebarOpen, setIsSidebarOpen] = React4.useState(false);
|
|
5898
|
+
const [toggledShotId, setToggledShotId] = React4.useState(
|
|
5631
5899
|
null
|
|
5632
5900
|
);
|
|
5633
|
-
const [viewingShotId, setViewingShotId] =
|
|
5901
|
+
const [viewingShotId, setViewingShotId] = React4.useState(
|
|
5634
5902
|
null
|
|
5635
5903
|
);
|
|
5636
|
-
const [updatingShotId, setUpdatingShotId] =
|
|
5904
|
+
const [updatingShotId, setUpdatingShotId] = React4.useState(
|
|
5637
5905
|
null
|
|
5638
5906
|
);
|
|
5639
|
-
const [isInitModalOpen, setIsInitModalOpen] =
|
|
5907
|
+
const [isInitModalOpen, setIsInitModalOpen] = React4.useState(false);
|
|
5640
5908
|
const handleCloseModal = () => {
|
|
5641
5909
|
clearSelection();
|
|
5642
5910
|
};
|
|
@@ -5687,7 +5955,7 @@ function ShotBreakdownView({
|
|
|
5687
5955
|
}
|
|
5688
5956
|
return nodes;
|
|
5689
5957
|
};
|
|
5690
|
-
|
|
5958
|
+
React4.useEffect(() => {
|
|
5691
5959
|
const fontId = "google-font-courier-prime";
|
|
5692
5960
|
const styleId = "screenplay-editor-force-v4";
|
|
5693
5961
|
if (!document.getElementById(fontId)) {
|
|
@@ -5933,14 +6201,21 @@ function ShotBreakdownView({
|
|
|
5933
6201
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5934
6202
|
production_setup_modal_default,
|
|
5935
6203
|
{
|
|
5936
|
-
initializeProduction: (count, type) => {
|
|
5937
|
-
initializeProduction(
|
|
6204
|
+
initializeProduction: (count, type, dance_choreographer_required, action_sequence_required) => {
|
|
6205
|
+
initializeProduction(
|
|
6206
|
+
count,
|
|
6207
|
+
type,
|
|
6208
|
+
dance_choreographer_required,
|
|
6209
|
+
action_sequence_required
|
|
6210
|
+
).then(() => {
|
|
5938
6211
|
setIsInitModalOpen(false);
|
|
5939
6212
|
});
|
|
5940
6213
|
},
|
|
5941
6214
|
initialValues: {
|
|
5942
6215
|
numCameras: cameras.length || 1,
|
|
5943
|
-
scene_type: sceneType
|
|
6216
|
+
scene_type: sceneType,
|
|
6217
|
+
dance_choreographer_required: false,
|
|
6218
|
+
action_sequence_required: false
|
|
5944
6219
|
}
|
|
5945
6220
|
}
|
|
5946
6221
|
)
|
|
@@ -5983,20 +6258,20 @@ function ShotBreakdownView({
|
|
|
5983
6258
|
);
|
|
5984
6259
|
}
|
|
5985
6260
|
function useShotBreakdownScene(options) {
|
|
5986
|
-
const [shots, setShots] =
|
|
5987
|
-
const [cameras, setCameras] =
|
|
6261
|
+
const [shots, setShots] = React4.useState(options.preloadedShots || []);
|
|
6262
|
+
const [cameras, setCameras] = React4.useState(
|
|
5988
6263
|
options.preloadedCameras || []
|
|
5989
6264
|
);
|
|
5990
|
-
const [sceneType, setSceneType] =
|
|
6265
|
+
const [sceneType, setSceneType] = React4.useState(
|
|
5991
6266
|
options.preloadedSceneType || ""
|
|
5992
6267
|
);
|
|
5993
|
-
const [scene, setScene] =
|
|
5994
|
-
const [isLoading, setIsLoading] =
|
|
5995
|
-
const [isSummarizing, setIsSummarizing] =
|
|
5996
|
-
const [error, setError] =
|
|
5997
|
-
const [selectionMenu, setSelectionMenu] =
|
|
5998
|
-
const menuRef =
|
|
5999
|
-
|
|
6268
|
+
const [scene, setScene] = React4.useState(null);
|
|
6269
|
+
const [isLoading, setIsLoading] = React4.useState(true);
|
|
6270
|
+
const [isSummarizing, setIsSummarizing] = React4.useState(false);
|
|
6271
|
+
const [error, setError] = React4.useState(false);
|
|
6272
|
+
const [selectionMenu, setSelectionMenu] = React4.useState(null);
|
|
6273
|
+
const menuRef = React4.useRef(null);
|
|
6274
|
+
React4.useEffect(() => {
|
|
6000
6275
|
if (options.preloadedShots && options.preloadedShots.length > 0) {
|
|
6001
6276
|
setShots((prev) => {
|
|
6002
6277
|
if (JSON.stringify(prev) === JSON.stringify(options.preloadedShots)) {
|
|
@@ -6006,7 +6281,7 @@ function useShotBreakdownScene(options) {
|
|
|
6006
6281
|
});
|
|
6007
6282
|
}
|
|
6008
6283
|
}, [options.preloadedShots]);
|
|
6009
|
-
|
|
6284
|
+
React4.useEffect(() => {
|
|
6010
6285
|
if (options.preloadedCameras && options.preloadedCameras.length > 0) {
|
|
6011
6286
|
setCameras((prev) => {
|
|
6012
6287
|
if (JSON.stringify(prev) === JSON.stringify(options.preloadedCameras)) {
|
|
@@ -6016,7 +6291,7 @@ function useShotBreakdownScene(options) {
|
|
|
6016
6291
|
});
|
|
6017
6292
|
}
|
|
6018
6293
|
}, [options.preloadedCameras]);
|
|
6019
|
-
|
|
6294
|
+
React4.useEffect(() => {
|
|
6020
6295
|
if (options.preloadedSceneType) {
|
|
6021
6296
|
setSceneType((prev) => {
|
|
6022
6297
|
if (prev === options.preloadedSceneType) return prev;
|
|
@@ -6024,7 +6299,7 @@ function useShotBreakdownScene(options) {
|
|
|
6024
6299
|
});
|
|
6025
6300
|
}
|
|
6026
6301
|
}, [options.preloadedSceneType]);
|
|
6027
|
-
|
|
6302
|
+
React4.useEffect(() => {
|
|
6028
6303
|
setIsLoading(true);
|
|
6029
6304
|
const fetchScene = async () => {
|
|
6030
6305
|
try {
|
|
@@ -6045,7 +6320,7 @@ function useShotBreakdownScene(options) {
|
|
|
6045
6320
|
};
|
|
6046
6321
|
fetchScene();
|
|
6047
6322
|
}, []);
|
|
6048
|
-
const blocks =
|
|
6323
|
+
const blocks = React4.useMemo(() => {
|
|
6049
6324
|
if (!scene || !scene.content) return [];
|
|
6050
6325
|
const parser = new DOMParser();
|
|
6051
6326
|
const doc = parser.parseFromString(scene.content, "text/html");
|
|
@@ -6081,7 +6356,7 @@ function useShotBreakdownScene(options) {
|
|
|
6081
6356
|
setSelectionMenu(null);
|
|
6082
6357
|
(_a = window.getSelection()) == null ? void 0 : _a.removeAllRanges();
|
|
6083
6358
|
};
|
|
6084
|
-
|
|
6359
|
+
React4.useEffect(() => {
|
|
6085
6360
|
const handleClickOutside = (e) => {
|
|
6086
6361
|
if (menuRef.current && !menuRef.current.contains(e.target)) {
|
|
6087
6362
|
clearSelection();
|
|
@@ -6156,13 +6431,19 @@ function useShotBreakdownScene(options) {
|
|
|
6156
6431
|
});
|
|
6157
6432
|
}
|
|
6158
6433
|
};
|
|
6159
|
-
const initializeProduction = async (count, type) => {
|
|
6434
|
+
const initializeProduction = async (count, type, dance_choreographer_required, action_sequence_required) => {
|
|
6160
6435
|
var _a;
|
|
6161
6436
|
const newCameras = [];
|
|
6162
6437
|
for (let i = 1; i <= count; i++) {
|
|
6163
6438
|
newCameras.push({ name: `Camera ${String.fromCharCode(64 + i)}` });
|
|
6164
6439
|
}
|
|
6165
|
-
const result = (_a = options.onProductionInitialized) == null ? void 0 : _a.call(
|
|
6440
|
+
const result = (_a = options.onProductionInitialized) == null ? void 0 : _a.call(
|
|
6441
|
+
options,
|
|
6442
|
+
newCameras,
|
|
6443
|
+
type,
|
|
6444
|
+
dance_choreographer_required,
|
|
6445
|
+
action_sequence_required
|
|
6446
|
+
);
|
|
6166
6447
|
if (result instanceof Promise) {
|
|
6167
6448
|
setCameras(newCameras);
|
|
6168
6449
|
setSceneType(type);
|
|
@@ -6286,8 +6567,182 @@ function useShotBreakdownScene(options) {
|
|
|
6286
6567
|
menuRef
|
|
6287
6568
|
};
|
|
6288
6569
|
}
|
|
6570
|
+
function SceneScriptView({
|
|
6571
|
+
blocks,
|
|
6572
|
+
isLoading = false,
|
|
6573
|
+
sceneNumber,
|
|
6574
|
+
title
|
|
6575
|
+
}) {
|
|
6576
|
+
const COURIER_STACK = "'Courier Prime', 'Courier', monospace";
|
|
6577
|
+
React4.useEffect(() => {
|
|
6578
|
+
const fontId = "google-font-courier-prime";
|
|
6579
|
+
const styleId = "screenplay-editor-force-v4";
|
|
6580
|
+
if (!document.getElementById(fontId)) {
|
|
6581
|
+
const link = document.createElement("link");
|
|
6582
|
+
link.id = fontId;
|
|
6583
|
+
link.rel = "stylesheet";
|
|
6584
|
+
link.href = "https://fonts.googleapis.com/css2?family=Courier+Prime:ital,wght@0,400;0,700;1,400;1,700&display=swap";
|
|
6585
|
+
document.head.appendChild(link);
|
|
6586
|
+
}
|
|
6587
|
+
if (!document.getElementById(styleId)) {
|
|
6588
|
+
const style = document.createElement("style");
|
|
6589
|
+
style.id = styleId;
|
|
6590
|
+
style.textContent = `
|
|
6591
|
+
[data-scene-script-view] *,
|
|
6592
|
+
[data-scene-script-view] div,
|
|
6593
|
+
[data-scene-script-view] span {
|
|
6594
|
+
font-family: ${COURIER_STACK} !important;
|
|
6595
|
+
-webkit-font-smoothing: antialiased;
|
|
6596
|
+
}
|
|
6597
|
+
`;
|
|
6598
|
+
document.head.appendChild(style);
|
|
6599
|
+
}
|
|
6600
|
+
}, [COURIER_STACK]);
|
|
6601
|
+
if (isLoading) {
|
|
6602
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-32 gap-4", children: [
|
|
6603
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "w-8 h-8 animate-spin text-zinc-400" }),
|
|
6604
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-zinc-500 animate-pulse", children: "Loading script..." })
|
|
6605
|
+
] });
|
|
6606
|
+
}
|
|
6607
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6608
|
+
"div",
|
|
6609
|
+
{
|
|
6610
|
+
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",
|
|
6611
|
+
style: {
|
|
6612
|
+
fontFamily: COURIER_STACK,
|
|
6613
|
+
paddingLeft: "1.5in",
|
|
6614
|
+
paddingRight: "1in",
|
|
6615
|
+
paddingTop: "1in",
|
|
6616
|
+
paddingBottom: "1in",
|
|
6617
|
+
lineHeight: "1.2"
|
|
6618
|
+
},
|
|
6619
|
+
"data-scene-script-view": "true",
|
|
6620
|
+
children: blocks.map((block) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
6621
|
+
"div",
|
|
6622
|
+
{
|
|
6623
|
+
className: `relative break-words w-full px-4 py-2 ${blockStyles[block.type].className}`,
|
|
6624
|
+
style: __spreadProps(__spreadValues({}, blockStyles[block.type].inputStyle), {
|
|
6625
|
+
minHeight: "1.5rem"
|
|
6626
|
+
}),
|
|
6627
|
+
children: block.type === "SCENE_HEADING" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
6628
|
+
block.sceneNumber && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -left-16 text-zinc-400 font-bold select-none", children: block.sceneNumber }),
|
|
6629
|
+
block.sceneType && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-bold", children: block.sceneType }),
|
|
6630
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-bold", children: block.text }),
|
|
6631
|
+
block.timeOfDay && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6632
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-zinc-400", children: "-" }),
|
|
6633
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-bold", children: block.timeOfDay })
|
|
6634
|
+
] })
|
|
6635
|
+
] }) : block.text
|
|
6636
|
+
},
|
|
6637
|
+
block.id
|
|
6638
|
+
))
|
|
6639
|
+
}
|
|
6640
|
+
);
|
|
6641
|
+
}
|
|
6642
|
+
function useSceneScript(options) {
|
|
6643
|
+
const [sceneContent, setSceneContent] = React4.useState(null);
|
|
6644
|
+
const [isLoading, setIsLoading] = React4.useState(true);
|
|
6645
|
+
const [error, setError] = React4.useState(null);
|
|
6646
|
+
React4.useEffect(() => {
|
|
6647
|
+
let isMounted = true;
|
|
6648
|
+
setIsLoading(true);
|
|
6649
|
+
setError(null);
|
|
6650
|
+
const fetchScene = async () => {
|
|
6651
|
+
try {
|
|
6652
|
+
const response = await fetch(options.scene_url, options.fetchOptions);
|
|
6653
|
+
if (response.ok) {
|
|
6654
|
+
const text = await response.text();
|
|
6655
|
+
if (isMounted) {
|
|
6656
|
+
setSceneContent(text);
|
|
6657
|
+
}
|
|
6658
|
+
} else {
|
|
6659
|
+
if (isMounted) {
|
|
6660
|
+
setError(`Failed to fetch scene: ${response.statusText}`);
|
|
6661
|
+
}
|
|
6662
|
+
}
|
|
6663
|
+
} catch (err) {
|
|
6664
|
+
if (isMounted) {
|
|
6665
|
+
setError(err instanceof Error ? err.message : "An unknown error occurred");
|
|
6666
|
+
}
|
|
6667
|
+
} finally {
|
|
6668
|
+
if (isMounted) {
|
|
6669
|
+
setIsLoading(false);
|
|
6670
|
+
}
|
|
6671
|
+
}
|
|
6672
|
+
};
|
|
6673
|
+
fetchScene();
|
|
6674
|
+
return () => {
|
|
6675
|
+
isMounted = false;
|
|
6676
|
+
};
|
|
6677
|
+
}, [options.scene_url]);
|
|
6678
|
+
const blocks = React4.useMemo(() => {
|
|
6679
|
+
if (!sceneContent) return [];
|
|
6680
|
+
const parser = new DOMParser();
|
|
6681
|
+
const doc = parser.parseFromString(sceneContent, "text/html");
|
|
6682
|
+
const divs = Array.from(doc.querySelectorAll("div"));
|
|
6683
|
+
const parsedBlocks = [];
|
|
6684
|
+
const typeMap = {
|
|
6685
|
+
divtype0: "SCENE_HEADING",
|
|
6686
|
+
divtype2: "ACTION",
|
|
6687
|
+
divtype3: "CHARACTER",
|
|
6688
|
+
divtype4: "PARENTHETICAL",
|
|
6689
|
+
divtype5: "DIALOGUE",
|
|
6690
|
+
divtype6: "TRANSITION"
|
|
6691
|
+
};
|
|
6692
|
+
divs.forEach((div) => {
|
|
6693
|
+
var _a;
|
|
6694
|
+
const divText = ((_a = div.textContent) == null ? void 0 : _a.trim()) || "";
|
|
6695
|
+
if (!divText && !div.classList.contains("divtype0")) return;
|
|
6696
|
+
let type = "ACTION";
|
|
6697
|
+
for (const className of Array.from(div.classList)) {
|
|
6698
|
+
if (typeMap[className]) {
|
|
6699
|
+
type = typeMap[className];
|
|
6700
|
+
break;
|
|
6701
|
+
}
|
|
6702
|
+
}
|
|
6703
|
+
const idAttr = div.getAttribute("id");
|
|
6704
|
+
const blockId = idAttr && idAttr.startsWith("par") ? idAttr.substring(3) : idAttr || uuid();
|
|
6705
|
+
let sceneNumber;
|
|
6706
|
+
let sceneType;
|
|
6707
|
+
let timeOfDay;
|
|
6708
|
+
let blockText = divText;
|
|
6709
|
+
if (type === "SCENE_HEADING") {
|
|
6710
|
+
const typeMatch = blockText.match(/^(INT\.?\/EXT\.?|INT\.?|EXT\.?|I\/E)\s+/i);
|
|
6711
|
+
if (typeMatch) {
|
|
6712
|
+
const matchedType = typeMatch[1].toUpperCase();
|
|
6713
|
+
if (matchedType.includes("INT") && matchedType.includes("EXT")) sceneType = "INT/EXT.";
|
|
6714
|
+
else if (matchedType.includes("EXT")) sceneType = "EXT.";
|
|
6715
|
+
else sceneType = "INT.";
|
|
6716
|
+
blockText = blockText.substring(typeMatch[0].length).trim();
|
|
6717
|
+
}
|
|
6718
|
+
const timeMatch = blockText.match(/\s+-\s+(DAY|NIGHT|MORNING|EVENING|AFTERNOON|LATER|MOMENTS LATER|CONTINUOUS)$/i);
|
|
6719
|
+
if (timeMatch) {
|
|
6720
|
+
timeOfDay = timeMatch[1].toUpperCase();
|
|
6721
|
+
blockText = blockText.substring(0, timeMatch.index).trim();
|
|
6722
|
+
}
|
|
6723
|
+
blockText = blockText.replace(/^-+\s*|\s*-+$/g, "").trim();
|
|
6724
|
+
}
|
|
6725
|
+
parsedBlocks.push({
|
|
6726
|
+
id: blockId,
|
|
6727
|
+
type,
|
|
6728
|
+
text: blockText,
|
|
6729
|
+
sceneNumber,
|
|
6730
|
+
sceneType,
|
|
6731
|
+
timeOfDay
|
|
6732
|
+
});
|
|
6733
|
+
});
|
|
6734
|
+
return parsedBlocks;
|
|
6735
|
+
}, [sceneContent]);
|
|
6736
|
+
return {
|
|
6737
|
+
blocks,
|
|
6738
|
+
isLoading,
|
|
6739
|
+
error,
|
|
6740
|
+
sceneContent
|
|
6741
|
+
};
|
|
6742
|
+
}
|
|
6289
6743
|
|
|
6290
6744
|
exports.CATEGORIES = CATEGORIES;
|
|
6745
|
+
exports.SceneScriptView = SceneScriptView;
|
|
6291
6746
|
exports.ScreenplayEditorView = ScreenplayEditorView;
|
|
6292
6747
|
exports.ScriptBreakdownSceneView = ScriptBreakdownSceneView;
|
|
6293
6748
|
exports.ShotBreakdownView = ShotBreakdownView;
|
|
@@ -6306,6 +6761,7 @@ exports.lens_options = lens_options;
|
|
|
6306
6761
|
exports.scene_types = scene_types;
|
|
6307
6762
|
exports.shot_types = shot_types;
|
|
6308
6763
|
exports.timeOfDayOptions = timeOfDayOptions;
|
|
6764
|
+
exports.useSceneScript = useSceneScript;
|
|
6309
6765
|
exports.useScreenplayEditor = useScreenplayEditor;
|
|
6310
6766
|
exports.useScriptBreakdownScene = useScriptBreakdownScene;
|
|
6311
6767
|
exports.useShotBreakdownScene = useShotBreakdownScene;
|