@vishu1301/script-writing 1.1.3 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +291 -186
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -11
- package/dist/index.d.ts +20 -11
- package/dist/index.js +292 -187
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useState, useRef, useEffect, useMemo, useCallback } from 'react';
|
|
2
|
-
import { ArrowRightLeft, MessageCircle, Brackets, UserRound, Sparkles, Clapperboard, Upload, Save, FileDown, RefreshCcw, Cog, ArrowRight, User, ChevronRight, Loader2, AlignLeft, Tags, Plus, X } from 'lucide-react';
|
|
2
|
+
import { ArrowRightLeft, MessageCircle, Brackets, UserRound, Sparkles, Clapperboard, Upload, Lock, Unlock, Save, FileDown, RefreshCcw, Cog, ArrowRight, User, ChevronRight, Loader2, AlignLeft, Tags, Plus, X } from 'lucide-react';
|
|
3
3
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
import * as pdfjs from 'pdfjs-dist';
|
|
5
5
|
import jsPDF from 'jspdf';
|
|
@@ -127,7 +127,7 @@ var blockStyles = {
|
|
|
127
127
|
}
|
|
128
128
|
};
|
|
129
129
|
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;
|
|
130
|
-
function PdfImporter({ onScriptImported, children }) {
|
|
130
|
+
function PdfImporter({ onScriptImported, disabled, children }) {
|
|
131
131
|
const [isProcessing, setIsProcessing] = useState(false);
|
|
132
132
|
const [error, setError] = useState(null);
|
|
133
133
|
const fileInputRef = useRef(null);
|
|
@@ -260,7 +260,7 @@ function PdfImporter({ onScriptImported, children }) {
|
|
|
260
260
|
type: "file",
|
|
261
261
|
accept: ".pdf,application/pdf",
|
|
262
262
|
onChange: handleFileChange,
|
|
263
|
-
disabled: isProcessing,
|
|
263
|
+
disabled: isProcessing || disabled,
|
|
264
264
|
className: "hidden",
|
|
265
265
|
id: "pdf-importer-input"
|
|
266
266
|
}
|
|
@@ -269,8 +269,8 @@ function PdfImporter({ onScriptImported, children }) {
|
|
|
269
269
|
"button",
|
|
270
270
|
{
|
|
271
271
|
onClick: handleClick,
|
|
272
|
-
disabled: isProcessing,
|
|
273
|
-
className: "flex items-center justify-center gap-2 w-auto px-4 h-12 rounded-full bg-zinc-950 text-white shadow-xl shadow-zinc-900/20 border border-white/10 hover:bg-zinc-800 hover:scale-105 active:scale-95 transition-all duration-300",
|
|
272
|
+
disabled: isProcessing || disabled,
|
|
273
|
+
className: "flex items-center justify-center gap-2 w-auto px-4 h-12 rounded-full bg-zinc-950 text-white shadow-xl shadow-zinc-900/20 border border-white/10 hover:bg-zinc-800 hover:scale-105 active:scale-95 transition-all duration-300 disabled:cursor-not-allowed disabled:bg-zinc-700 disabled:shadow-none disabled:border-zinc-600/50",
|
|
274
274
|
"aria-label": "Import Script",
|
|
275
275
|
children: isProcessing ? /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: "Processing..." }) : children
|
|
276
276
|
}
|
|
@@ -293,6 +293,8 @@ function ScreenplayEditorView({
|
|
|
293
293
|
showPdfImport = false,
|
|
294
294
|
showSaveButton = false,
|
|
295
295
|
showSyncButton = false,
|
|
296
|
+
isLocked = false,
|
|
297
|
+
onToggleLock,
|
|
296
298
|
handleBlockTextChange,
|
|
297
299
|
handleSceneTypeChange,
|
|
298
300
|
handleTimeOfDayChange,
|
|
@@ -366,7 +368,8 @@ function ScreenplayEditorView({
|
|
|
366
368
|
"button",
|
|
367
369
|
{
|
|
368
370
|
type: "button",
|
|
369
|
-
|
|
371
|
+
disabled: isLocked,
|
|
372
|
+
className: `group flex items-center gap-2.5 px-4 py-2 rounded-full font-semibold text-sm transition-all duration-300 ease-out active:scale-95 ${selected ? "bg-zinc-900 text-white shadow-md shadow-zinc-900/20" : "text-zinc-500 hover:bg-zinc-100 hover:text-zinc-900"} ${isLocked ? "opacity-50 cursor-not-allowed" : ""}`,
|
|
370
373
|
onClick: () => handleBlockTypeChange(type),
|
|
371
374
|
children: [
|
|
372
375
|
/* @__PURE__ */ jsx(
|
|
@@ -405,8 +408,18 @@ function ScreenplayEditorView({
|
|
|
405
408
|
}) }),
|
|
406
409
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 shrink-0 relative px-1", children: [
|
|
407
410
|
/* @__PURE__ */ jsx("div", { className: "w-[1px] h-6 bg-zinc-200/80 mx-2 hidden sm:block rounded-full" }),
|
|
408
|
-
showPdfImport && /* @__PURE__ */ jsx(PdfImporter, { onScriptImported: handleScriptImport, children: /* @__PURE__ */ jsx("div", { title: "Import Script", children: /* @__PURE__ */ jsx(Upload, { className: "w-[18px] h-[18px]" }) }) }),
|
|
409
|
-
|
|
411
|
+
showPdfImport && /* @__PURE__ */ jsx(PdfImporter, { disabled: isLocked, onScriptImported: handleScriptImport, children: /* @__PURE__ */ jsx("div", { title: "Import Script", children: /* @__PURE__ */ jsx(Upload, { className: "w-[18px] h-[18px]" }) }) }),
|
|
412
|
+
onToggleLock && /* @__PURE__ */ jsx(
|
|
413
|
+
"button",
|
|
414
|
+
{
|
|
415
|
+
onClick: onToggleLock,
|
|
416
|
+
className: `flex items-center justify-center w-10 h-10 rounded-full transition-all duration-200 active:scale-95 ${isLocked ? "text-rose-500 hover:bg-rose-50" : "text-zinc-500 hover:bg-zinc-100 hover:text-zinc-900"}`,
|
|
417
|
+
title: isLocked ? "Unlock Script" : "Lock Script",
|
|
418
|
+
"aria-label": isLocked ? "Unlock Script" : "Lock Script",
|
|
419
|
+
children: isLocked ? /* @__PURE__ */ jsx(Lock, { className: "w-[18px] h-[18px]" }) : /* @__PURE__ */ jsx(Unlock, { className: "w-[18px] h-[18px]" })
|
|
420
|
+
}
|
|
421
|
+
),
|
|
422
|
+
onSave && showSaveButton && !isLocked && /* @__PURE__ */ jsx(
|
|
410
423
|
"button",
|
|
411
424
|
{
|
|
412
425
|
onClick: onSave,
|
|
@@ -426,7 +439,7 @@ function ScreenplayEditorView({
|
|
|
426
439
|
children: /* @__PURE__ */ jsx(FileDown, { className: "w-[18px] h-[18px]" })
|
|
427
440
|
}
|
|
428
441
|
),
|
|
429
|
-
onSyncWithCloud && showSyncButton && /* @__PURE__ */ jsx(
|
|
442
|
+
onSyncWithCloud && showSyncButton && !isLocked && /* @__PURE__ */ jsx(
|
|
430
443
|
"button",
|
|
431
444
|
{
|
|
432
445
|
onClick: onSyncWithCloud,
|
|
@@ -503,6 +516,7 @@ function ScreenplayEditorView({
|
|
|
503
516
|
{
|
|
504
517
|
className: "absolute -left-16 top-2 w-12 text-right text-zinc-400 font-semibold select-none bg-transparent outline-none focus:ring-1 focus:ring-blue-400 rounded-sm",
|
|
505
518
|
spellCheck: false,
|
|
519
|
+
disabled: isLocked,
|
|
506
520
|
value: block.sceneNumber || "",
|
|
507
521
|
onChange: (e) => handleSceneNumberChange(
|
|
508
522
|
block.id,
|
|
@@ -523,6 +537,7 @@ function ScreenplayEditorView({
|
|
|
523
537
|
{
|
|
524
538
|
className: "rounded-md text-zinc-800 font-bold px-1.5 py-1 appearance-none bg-transparent hover:bg-zinc-200/50 outline-none cursor-pointer w-fit transition-colors",
|
|
525
539
|
"aria-label": "Scene Type",
|
|
540
|
+
disabled: isLocked,
|
|
526
541
|
value: (_a = block.sceneType) != null ? _a : "INT.",
|
|
527
542
|
onChange: (e) => handleSceneTypeChange(block.id, e.target.value),
|
|
528
543
|
style: {
|
|
@@ -542,7 +557,7 @@ function ScreenplayEditorView({
|
|
|
542
557
|
if (!el) return;
|
|
543
558
|
refs.current[block.id] = el;
|
|
544
559
|
},
|
|
545
|
-
contentEditable:
|
|
560
|
+
contentEditable: !isLocked,
|
|
546
561
|
suppressContentEditableWarning: true,
|
|
547
562
|
"aria-label": `Scene Heading: ${block.text}`,
|
|
548
563
|
"aria-haspopup": "listbox",
|
|
@@ -567,6 +582,7 @@ function ScreenplayEditorView({
|
|
|
567
582
|
{
|
|
568
583
|
className: "rounded-md text-zinc-800 font-bold px-1.5 py-1 appearance-none bg-transparent hover:bg-zinc-200/50 outline-none cursor-pointer transition-colors",
|
|
569
584
|
"aria-label": "Time of Day",
|
|
585
|
+
disabled: isLocked,
|
|
570
586
|
value: (_b = block.timeOfDay) != null ? _b : "DAY",
|
|
571
587
|
style: {
|
|
572
588
|
appearance: "none"
|
|
@@ -622,7 +638,7 @@ function ScreenplayEditorView({
|
|
|
622
638
|
if (!el) return;
|
|
623
639
|
refs.current[block.id] = el;
|
|
624
640
|
},
|
|
625
|
-
contentEditable:
|
|
641
|
+
contentEditable: !isLocked,
|
|
626
642
|
suppressContentEditableWarning: true,
|
|
627
643
|
"aria-label": `${blockStyles[block.type].label} text`,
|
|
628
644
|
"aria-multiline": block.type === "ACTION" || block.type === "DIALOGUE",
|
|
@@ -1349,7 +1365,7 @@ function useScreenplayEditor(options) {
|
|
|
1349
1365
|
[blocks, handleBlockTextChange]
|
|
1350
1366
|
);
|
|
1351
1367
|
const handleScriptImport = useCallback(
|
|
1352
|
-
(title, content, preParsedBlocks) => {
|
|
1368
|
+
(title, content, preParsedBlocks, isInitialLoad) => {
|
|
1353
1369
|
let parsedBlocks = [];
|
|
1354
1370
|
if (preParsedBlocks && preParsedBlocks.length > 0) {
|
|
1355
1371
|
parsedBlocks = preParsedBlocks.map((b) => ({
|
|
@@ -1405,8 +1421,10 @@ function useScreenplayEditor(options) {
|
|
|
1405
1421
|
}).join("");
|
|
1406
1422
|
if (sbxData !== lastSavedContent.current) {
|
|
1407
1423
|
lastSavedContent.current = sbxData;
|
|
1408
|
-
|
|
1409
|
-
|
|
1424
|
+
if (!isInitialLoad) {
|
|
1425
|
+
const blob = new Blob([sbxData], { type: "text/plain" });
|
|
1426
|
+
onSaveRef.current(blob);
|
|
1427
|
+
}
|
|
1410
1428
|
}
|
|
1411
1429
|
}
|
|
1412
1430
|
setTimeout(() => {
|
|
@@ -1454,7 +1472,7 @@ function useScreenplayEditor(options) {
|
|
|
1454
1472
|
}, 200);
|
|
1455
1473
|
}, []);
|
|
1456
1474
|
const loadFromUrl = useCallback(
|
|
1457
|
-
async (url, fetchOptions = {}) => {
|
|
1475
|
+
async (url, fetchOptions = {}, isInitialLoad) => {
|
|
1458
1476
|
var _a;
|
|
1459
1477
|
try {
|
|
1460
1478
|
const response = await fetch(url, fetchOptions);
|
|
@@ -1526,7 +1544,7 @@ function useScreenplayEditor(options) {
|
|
|
1526
1544
|
}
|
|
1527
1545
|
}
|
|
1528
1546
|
const filename = ((_a = url.split("/").pop()) == null ? void 0 : _a.replace(/\.sbx$/i, "")) || "Imported from URL";
|
|
1529
|
-
handleScriptImport(filename, scriptContent, preParsedBlocks);
|
|
1547
|
+
handleScriptImport(filename, scriptContent, preParsedBlocks, isInitialLoad);
|
|
1530
1548
|
} catch (error) {
|
|
1531
1549
|
console.error(
|
|
1532
1550
|
"[useScreenplayEditor] Error loading script from URL:",
|
|
@@ -1540,7 +1558,7 @@ function useScreenplayEditor(options) {
|
|
|
1540
1558
|
useEffect(() => {
|
|
1541
1559
|
if ((options == null ? void 0 : options.initialUrl) && options.initialUrl !== loadedUrlRef.current) {
|
|
1542
1560
|
loadedUrlRef.current = options.initialUrl;
|
|
1543
|
-
loadFromUrl(options.initialUrl, options.fetchOptions);
|
|
1561
|
+
loadFromUrl(options.initialUrl, options.fetchOptions, true);
|
|
1544
1562
|
}
|
|
1545
1563
|
}, [options == null ? void 0 : options.initialUrl, options == null ? void 0 : options.fetchOptions, loadFromUrl]);
|
|
1546
1564
|
return {
|
|
@@ -1703,13 +1721,14 @@ var handleSyncWithCloud = (blocks, sceneNumbers, onSaveAsSbx, project_name) => {
|
|
|
1703
1721
|
|
|
1704
1722
|
// app/types/script-breakdown.types.tsx
|
|
1705
1723
|
var CATEGORIES = [
|
|
1706
|
-
{ id: "CAST", label: "Cast", color: "#
|
|
1707
|
-
{ id: "PROP", label: "Prop", color: "#
|
|
1708
|
-
{ id: "COSTUME", label: "Costume", color: "#
|
|
1709
|
-
{ id: "VEHICLE", label: "Vehicle", color: "#
|
|
1710
|
-
{ id: "SET_PROP", label: "Set Prop", color: "#
|
|
1711
|
-
{ id: "EXTRA", label: "Extra", color: "#
|
|
1712
|
-
{ id: "LOCATION", label: "Location", color: "#
|
|
1724
|
+
{ id: "CAST", label: "Cast", color: "#7C3AED", hex: "#A78BFA" },
|
|
1725
|
+
{ id: "PROP", label: "Prop", color: "#FF6A00", hex: "#FFB86B" },
|
|
1726
|
+
{ id: "COSTUME", label: "Costume", color: "#FF2E93", hex: "#FF8AC2" },
|
|
1727
|
+
{ id: "VEHICLE", label: "Vehicle", color: "#00A6FF", hex: "#6ED6FF" },
|
|
1728
|
+
{ id: "SET_PROP", label: "Set Prop", color: "#00C853", hex: "#69F0AE" },
|
|
1729
|
+
{ id: "EXTRA", label: "Extra", color: "#00B8D4", hex: "#62EFFF" },
|
|
1730
|
+
{ id: "LOCATION", label: "Location", color: "#FFB300", hex: "#FFE082" },
|
|
1731
|
+
{ id: "OTHER", label: "Other", color: "#9E9E9E", hex: "#E0E0E0" }
|
|
1713
1732
|
];
|
|
1714
1733
|
function ScriptBreakdownSceneView({
|
|
1715
1734
|
blocks,
|
|
@@ -1728,7 +1747,9 @@ function ScriptBreakdownSceneView({
|
|
|
1728
1747
|
addSubLocation,
|
|
1729
1748
|
removeSubLocation,
|
|
1730
1749
|
sceneBrief,
|
|
1731
|
-
setSceneBrief
|
|
1750
|
+
setSceneBrief,
|
|
1751
|
+
onSummarize,
|
|
1752
|
+
isSummarizing
|
|
1732
1753
|
}) {
|
|
1733
1754
|
const COURIER_STACK = "'Courier Prime', 'Courier', monospace";
|
|
1734
1755
|
const [isSubLocOpen, setIsSubLocOpen] = useState(false);
|
|
@@ -1775,47 +1796,54 @@ function ScriptBreakdownSceneView({
|
|
|
1775
1796
|
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-zinc-500 animate-pulse", children: "Loading scene details..." })
|
|
1776
1797
|
] });
|
|
1777
1798
|
}
|
|
1778
|
-
const hasLocationTag = tags.some((t) => t.
|
|
1799
|
+
const hasLocationTag = tags.some((t) => t.category_id === "LOCATION");
|
|
1779
1800
|
const renderBlockText = (block) => {
|
|
1780
|
-
const blockTags = tags.filter((t) => t.
|
|
1801
|
+
const blockTags = tags.filter((t) => t.block_id === block.id).sort((a, b) => a.start_index - b.start_index);
|
|
1781
1802
|
if (blockTags.length === 0) return block.text;
|
|
1782
1803
|
const nodes = [];
|
|
1783
1804
|
let currentIndex = 0;
|
|
1784
1805
|
blockTags.forEach((tag) => {
|
|
1785
|
-
|
|
1806
|
+
const actualStart = Math.max(tag.start_index, currentIndex);
|
|
1807
|
+
if (actualStart > currentIndex) {
|
|
1786
1808
|
nodes.push(
|
|
1787
|
-
/* @__PURE__ */ jsx("span", { children: block.text.slice(currentIndex,
|
|
1809
|
+
/* @__PURE__ */ jsx("span", { children: block.text.slice(currentIndex, actualStart) }, `text-${currentIndex}`)
|
|
1788
1810
|
);
|
|
1789
1811
|
}
|
|
1790
|
-
const category = CATEGORIES.find((c) => c.id === tag.
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
e
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1812
|
+
const category = CATEGORIES.find((c) => c.id === tag.category_id);
|
|
1813
|
+
if (actualStart < tag.end_index) {
|
|
1814
|
+
nodes.push(
|
|
1815
|
+
/* @__PURE__ */ jsx(
|
|
1816
|
+
"span",
|
|
1817
|
+
{
|
|
1818
|
+
title: `${category == null ? void 0 : category.label} (Click to edit)`,
|
|
1819
|
+
onClick: (e) => {
|
|
1820
|
+
e.stopPropagation();
|
|
1821
|
+
const selection = window.getSelection();
|
|
1822
|
+
if (!selection) return;
|
|
1823
|
+
const range = document.createRange();
|
|
1824
|
+
const textNode = e.currentTarget.firstChild;
|
|
1825
|
+
if (textNode && textNode.nodeType === Node.TEXT_NODE) {
|
|
1826
|
+
range.selectNodeContents(textNode);
|
|
1827
|
+
} else {
|
|
1828
|
+
range.selectNodeContents(e.currentTarget);
|
|
1829
|
+
}
|
|
1830
|
+
selection.removeAllRanges();
|
|
1831
|
+
selection.addRange(range);
|
|
1832
|
+
setTimeout(() => handleMouseUp(), 0);
|
|
1833
|
+
},
|
|
1834
|
+
className: "cursor-pointer font-bold transition-all hover:opacity-80 rounded-[3px]",
|
|
1835
|
+
style: {
|
|
1836
|
+
color: category == null ? void 0 : category.color,
|
|
1837
|
+
padding: "0.125rem 0.25rem",
|
|
1838
|
+
margin: "0 -0.125rem"
|
|
1839
|
+
},
|
|
1840
|
+
children: block.text.slice(actualStart, tag.end_index)
|
|
1810
1841
|
},
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
)
|
|
1817
|
-
);
|
|
1818
|
-
currentIndex = tag.endIndex;
|
|
1842
|
+
tag.id || `tag-${actualStart}-${tag.end_index}`
|
|
1843
|
+
)
|
|
1844
|
+
);
|
|
1845
|
+
}
|
|
1846
|
+
currentIndex = Math.max(currentIndex, tag.end_index);
|
|
1819
1847
|
});
|
|
1820
1848
|
if (currentIndex < block.text.length) {
|
|
1821
1849
|
nodes.push(
|
|
@@ -1903,16 +1931,16 @@ function ScriptBreakdownSceneView({
|
|
|
1903
1931
|
cat.id
|
|
1904
1932
|
)),
|
|
1905
1933
|
tags.some(
|
|
1906
|
-
(t) => t.
|
|
1934
|
+
(t) => t.block_id === block.id && t.start_index === selectionMenu.startIndex && t.end_index === selectionMenu.endIndex
|
|
1907
1935
|
) && /* @__PURE__ */ jsx("div", { className: "mt-1 pt-1 border-t border-white/60", children: /* @__PURE__ */ jsxs(
|
|
1908
1936
|
"button",
|
|
1909
1937
|
{
|
|
1910
1938
|
onClick: (e) => {
|
|
1911
1939
|
const tagToRemove = tags.find(
|
|
1912
|
-
(t) => t.
|
|
1940
|
+
(t) => t.block_id === block.id && t.start_index === selectionMenu.startIndex && t.end_index === selectionMenu.endIndex
|
|
1913
1941
|
);
|
|
1914
|
-
if (tagToRemove) {
|
|
1915
|
-
removeTag(e, tagToRemove.id);
|
|
1942
|
+
if (tagToRemove && tagToRemove.id) {
|
|
1943
|
+
removeTag(e, tagToRemove == null ? void 0 : tagToRemove.id);
|
|
1916
1944
|
clearSelection();
|
|
1917
1945
|
}
|
|
1918
1946
|
},
|
|
@@ -1955,115 +1983,136 @@ function ScriptBreakdownSceneView({
|
|
|
1955
1983
|
) })
|
|
1956
1984
|
] })
|
|
1957
1985
|
] }),
|
|
1958
|
-
/* @__PURE__ */
|
|
1959
|
-
/* @__PURE__ */ jsxs(
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
1986
|
+
/* @__PURE__ */ jsxs("div", { className: "w-full lg:w-80 flex-shrink-0 flex flex-col gap-6 sticky top-6", children: [
|
|
1987
|
+
/* @__PURE__ */ jsxs(
|
|
1988
|
+
"button",
|
|
1989
|
+
{
|
|
1990
|
+
onClick: onSummarize,
|
|
1991
|
+
disabled: isSummarizing,
|
|
1992
|
+
className: `group relative w-full py-4 px-6 rounded-[3rem] overflow-hidden bg-gradient-to-b from-white via-zinc-50 to-zinc-100 border border-white/70 shadow-[0_10px_30px_-10px_rgba(0,0,0,0.15),inset_0_1px_0_rgba(255,255,255,0.9)] transition-all duration-500 ease-[cubic-bezier(0.22,1,0.36,1)] ${isSummarizing ? "opacity-70 cursor-wait" : "hover:shadow-[0_20px_50px_-15px_rgba(139,92,246,0.35)] hover:-translate-y-[2px] active:scale-[0.97]"}`,
|
|
1993
|
+
children: [
|
|
1994
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inset-0 opacity-0 group-hover:opacity-100 transition duration-700", children: /* @__PURE__ */ jsx("span", { className: "absolute inset-0 bg-[conic-gradient(from_180deg_at_50%_50%,#8b5cf6,transparent_40%,#d946ef,transparent_70%)] blur-2xl opacity-30 animate-[spin_6s_linear_infinite]" }) }),
|
|
1995
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inset-0 opacity-0 group-hover:opacity-100 transition duration-500 bg-[radial-gradient(circle_at_50%_40%,rgba(139,92,246,0.18),transparent_70%)]" }),
|
|
1996
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inset-[1px] rounded-[inherit] bg-gradient-to-b from-white/90 to-transparent pointer-events-none" }),
|
|
1997
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inset-0 overflow-hidden", children: /* @__PURE__ */ jsx("span", { className: "absolute -left-[100%] top-0 h-full w-[60%] bg-gradient-to-r from-transparent via-white/60 to-transparent skew-x-[-20deg] group-hover:left-[120%] transition-all duration-[1200ms]" }) }),
|
|
1998
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inset-[2px] rounded-[inherit] opacity-0 group-hover:opacity-100 bg-gradient-to-r from-violet-200/40 via-white/40 to-fuchsia-200/40 blur-md transition duration-500" }),
|
|
1999
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center gap-3", children: [
|
|
2000
|
+
isSummarizing ? /* @__PURE__ */ jsx(Loader2, { className: "w-4 h-4 text-violet-500 animate-spin" }) : /* @__PURE__ */ jsx(Sparkles, { className: "w-4 h-4 text-violet-500 transition-all duration-500 group-hover:-rotate-12 group-hover:scale-125" }),
|
|
2001
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] font-semibold tracking-[0.32em] uppercase text-zinc-800 group-hover:text-zinc-950 transition-all duration-300", children: isSummarizing ? "Summarizing..." : "Summarize Scene" })
|
|
2002
|
+
] })
|
|
2003
|
+
]
|
|
2004
|
+
}
|
|
2005
|
+
),
|
|
2006
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-white/50 backdrop-blur-2xl border border-white shadow-[0_8px_30px_rgb(0,0,0,0.04)] rounded-[2.5rem] p-8", children: [
|
|
2007
|
+
/* @__PURE__ */ jsxs("h3", { className: "text-xs font-extrabold text-slate-800 uppercase tracking-[0.25em] mb-8 flex items-center gap-3", children: [
|
|
2008
|
+
/* @__PURE__ */ jsx("span", { className: "flex items-center justify-center w-8 h-8 rounded-full bg-white/80 shadow-[0_4px_15px_rgb(0,0,0,0.04)] border border-white", children: /* @__PURE__ */ jsx(Tags, { className: "w-3.5 h-3.5 text-slate-500" }) }),
|
|
2009
|
+
"Tags",
|
|
2010
|
+
/* @__PURE__ */ jsx("span", { className: "ml-auto bg-slate-100/80 text-slate-500 px-2.5 py-1 rounded-lg text-[10px] font-bold tracking-widest border border-slate-200/50 shadow-inner", children: tags.length })
|
|
2011
|
+
] }),
|
|
2012
|
+
tags.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-8", children: CATEGORIES.map((cat) => {
|
|
2013
|
+
const catTags = Array.from(
|
|
2014
|
+
new Map(
|
|
2015
|
+
tags.filter((t) => t.category_id === cat.id).map((tag) => [tag.name.toLowerCase(), tag])
|
|
2016
|
+
).values()
|
|
2017
|
+
);
|
|
2018
|
+
if (catTags.length === 0) return null;
|
|
2019
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
2020
|
+
/* @__PURE__ */ jsxs("h4", { className: "text-[10px] font-bold text-slate-400 uppercase tracking-[0.2em] flex items-center gap-2 border-b border-white/60 pb-2", children: [
|
|
2021
|
+
/* @__PURE__ */ jsx(
|
|
2022
|
+
"div",
|
|
2023
|
+
{
|
|
2024
|
+
className: "w-2 h-2 rounded-full shadow-sm",
|
|
2025
|
+
style: { backgroundColor: cat.hex }
|
|
2026
|
+
}
|
|
2027
|
+
),
|
|
2028
|
+
cat.label
|
|
2029
|
+
] }),
|
|
2030
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
2031
|
+
catTags.map((tag, index) => /* @__PURE__ */ jsx(
|
|
2032
|
+
"span",
|
|
2033
|
+
{
|
|
2034
|
+
className: "text-[11px] font-bold px-3 py-1.5 rounded-xl bg-white/80 backdrop-blur-md border border-white shadow-[0_4px_15px_rgb(0,0,0,0.03)] hover:shadow-[0_4px_20px_rgb(0,0,0,0.06)] hover:-translate-y-0.5 transition-all duration-300 cursor-default",
|
|
2035
|
+
style: { color: cat.color },
|
|
2036
|
+
children: tag.name
|
|
2037
|
+
},
|
|
2038
|
+
index
|
|
2039
|
+
)),
|
|
2040
|
+
cat.id === "LOCATION" && /* @__PURE__ */ jsxs(
|
|
2041
|
+
"div",
|
|
2042
|
+
{
|
|
2043
|
+
className: "relative flex items-center",
|
|
2044
|
+
ref: subLocPopoverRef,
|
|
2045
|
+
children: [
|
|
2046
|
+
/* @__PURE__ */ jsx(
|
|
2047
|
+
"button",
|
|
2048
|
+
{
|
|
2049
|
+
onClick: () => setIsSubLocOpen(!isSubLocOpen),
|
|
2050
|
+
className: "flex items-center justify-center w-5 h-5 rounded-full hover:bg-slate-200 transition-colors",
|
|
2051
|
+
title: "Add Sub Location",
|
|
2052
|
+
children: /* @__PURE__ */ jsx(Plus, { className: "w-3 h-3 text-slate-500" })
|
|
2053
|
+
}
|
|
2054
|
+
),
|
|
2055
|
+
isSubLocOpen && /* @__PURE__ */ jsxs("div", { className: "absolute left-0 top-full mt-2 w-56 bg-white backdrop-blur-2xl shadow-[0_10px_40px_rgb(0,0,0,0.06)] border border-white rounded-[1.5rem] p-3 z-50 animate-in fade-in zoom-in-95", children: [
|
|
2056
|
+
/* @__PURE__ */ jsx("p", { className: "text-[9px] font-extrabold tracking-[0.2em] text-slate-400 uppercase mb-2 px-1", children: "Add Sub Location" }),
|
|
2057
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2 items-center", children: [
|
|
2058
|
+
/* @__PURE__ */ jsx(
|
|
2059
|
+
"input",
|
|
2060
|
+
{
|
|
2061
|
+
type: "text",
|
|
2062
|
+
value: subLocInput,
|
|
2063
|
+
onChange: (e) => setSubLocInput(e.target.value),
|
|
2064
|
+
onKeyDown: (e) => {
|
|
2065
|
+
if (e.key === "Enter") {
|
|
2066
|
+
addSubLocation(subLocInput);
|
|
2067
|
+
setSubLocInput("");
|
|
2068
|
+
setIsSubLocOpen(false);
|
|
2069
|
+
}
|
|
2070
|
+
},
|
|
2071
|
+
className: "w-full text-xs px-3 py-2 bg-white/50 border border-white/60 rounded-xl outline-none focus:bg-white/80 focus:border-white transition-all text-slate-700 font-bold shadow-[0_2px_10px_rgb(0,0,0,0.02)] placeholder:font-medium placeholder:text-slate-400",
|
|
2072
|
+
placeholder: "Sub location...",
|
|
2073
|
+
autoFocus: true
|
|
2074
|
+
}
|
|
2075
|
+
),
|
|
2076
|
+
/* @__PURE__ */ jsx(
|
|
2077
|
+
"button",
|
|
2078
|
+
{
|
|
2079
|
+
onClick: () => {
|
|
2007
2080
|
addSubLocation(subLocInput);
|
|
2008
2081
|
setSubLocInput("");
|
|
2009
2082
|
setIsSubLocOpen(false);
|
|
2010
|
-
}
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
),
|
|
2017
|
-
/* @__PURE__ */ jsx(
|
|
2018
|
-
"button",
|
|
2019
|
-
{
|
|
2020
|
-
onClick: () => {
|
|
2021
|
-
addSubLocation(subLocInput);
|
|
2022
|
-
setSubLocInput("");
|
|
2023
|
-
setIsSubLocOpen(false);
|
|
2024
|
-
},
|
|
2025
|
-
className: "flex items-center justify-center shrink-0 bg-slate-800 text-white px-3.5 py-2 rounded-xl text-[11px] font-bold hover:bg-slate-700 hover:shadow-md transition-all active:scale-95",
|
|
2026
|
-
children: "Add"
|
|
2027
|
-
}
|
|
2028
|
-
)
|
|
2083
|
+
},
|
|
2084
|
+
className: "flex items-center justify-center shrink-0 bg-slate-800 text-white px-3.5 py-2 rounded-xl text-[11px] font-bold hover:bg-slate-700 hover:shadow-md transition-all active:scale-95",
|
|
2085
|
+
children: "Add"
|
|
2086
|
+
}
|
|
2087
|
+
)
|
|
2088
|
+
] })
|
|
2029
2089
|
] })
|
|
2030
|
-
]
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
children: /* @__PURE__ */ jsx(X, { className: "w-2.5 h-2.5" })
|
|
2057
|
-
}
|
|
2058
|
-
)
|
|
2059
|
-
]
|
|
2060
|
-
},
|
|
2061
|
-
`sub-${subLoc}`
|
|
2062
|
-
))
|
|
2063
|
-
] })
|
|
2064
|
-
] }, cat.id);
|
|
2065
|
-
}) }) : /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-slate-400 italic bg-white/40 p-6 rounded-[2rem] border border-white border-dashed text-center shadow-[0_4px_20px_rgb(0,0,0,0.02)]", children: "Highlight text to tag elements." })
|
|
2066
|
-
] }) })
|
|
2090
|
+
]
|
|
2091
|
+
}
|
|
2092
|
+
),
|
|
2093
|
+
cat.id === "LOCATION" && subLocations.map((subLoc) => /* @__PURE__ */ jsxs(
|
|
2094
|
+
"span",
|
|
2095
|
+
{
|
|
2096
|
+
className: "group flex items-center gap-1.5 text-[11px] font-bold px-3 py-1.5 rounded-xl bg-white backdrop-blur-md border border-slate-200/50 shadow-[0_4px_15px_rgb(0,0,0,0.03)] transition-all duration-300 cursor-default text-slate-500",
|
|
2097
|
+
children: [
|
|
2098
|
+
subLoc,
|
|
2099
|
+
/* @__PURE__ */ jsx(
|
|
2100
|
+
"button",
|
|
2101
|
+
{
|
|
2102
|
+
onClick: () => removeSubLocation(subLoc),
|
|
2103
|
+
className: "w-3.5 h-3.5 rounded-full hover:bg-slate-300/50 flex items-center justify-center transition-colors opacity-0 group-hover:opacity-100",
|
|
2104
|
+
children: /* @__PURE__ */ jsx(X, { className: "w-2.5 h-2.5" })
|
|
2105
|
+
}
|
|
2106
|
+
)
|
|
2107
|
+
]
|
|
2108
|
+
},
|
|
2109
|
+
`sub-${subLoc}`
|
|
2110
|
+
))
|
|
2111
|
+
] })
|
|
2112
|
+
] }, cat.id);
|
|
2113
|
+
}) }) : /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-slate-400 italic bg-white/40 p-6 rounded-[2rem] border border-white border-dashed text-center shadow-[0_4px_20px_rgb(0,0,0,0.02)]", children: "Highlight text to tag elements." })
|
|
2114
|
+
] })
|
|
2115
|
+
] })
|
|
2067
2116
|
] }) });
|
|
2068
2117
|
}
|
|
2069
2118
|
function useScriptBreakdown({
|
|
@@ -2145,6 +2194,14 @@ var use_script_breakdown_default = useScriptBreakdown;
|
|
|
2145
2194
|
// app/hook/use-script-breakdown-scene.ts
|
|
2146
2195
|
function useScriptBreakdownScene(sceneNumber) {
|
|
2147
2196
|
const { scenes, isLoading, error } = use_script_breakdown_default({ scenes: [] });
|
|
2197
|
+
const [tags, setTags] = useState([]);
|
|
2198
|
+
const [selectionMenu, setSelectionMenu] = useState(null);
|
|
2199
|
+
const autoTaggedSceneRef = useRef(null);
|
|
2200
|
+
const [menuPlacement, setMenuPlacement] = useState("top");
|
|
2201
|
+
const [subLocations, setSubLocations] = useState([]);
|
|
2202
|
+
const [sceneBrief, setSceneBrief] = useState("");
|
|
2203
|
+
const [isSummarizing, setIsSummarizing] = useState(false);
|
|
2204
|
+
const menuRef = useRef(null);
|
|
2148
2205
|
const scene = useMemo(() => {
|
|
2149
2206
|
return scenes.find((s) => s.scene_number === sceneNumber);
|
|
2150
2207
|
}, [scenes, sceneNumber]);
|
|
@@ -2173,7 +2230,9 @@ function useScriptBreakdownScene(sceneNumber) {
|
|
|
2173
2230
|
break;
|
|
2174
2231
|
}
|
|
2175
2232
|
}
|
|
2176
|
-
|
|
2233
|
+
const idAttr = div.getAttribute("id");
|
|
2234
|
+
const blockId = idAttr && idAttr.startsWith("par") ? idAttr.substring(3) : idAttr || uuid();
|
|
2235
|
+
parsedBlocks.push({ id: blockId, type, text: divText });
|
|
2177
2236
|
});
|
|
2178
2237
|
return parsedBlocks;
|
|
2179
2238
|
}, [scene]);
|
|
@@ -2185,12 +2244,57 @@ function useScriptBreakdownScene(sceneNumber) {
|
|
|
2185
2244
|
}).filter(Boolean);
|
|
2186
2245
|
return [...new Set(chars)];
|
|
2187
2246
|
}, [blocks]);
|
|
2188
|
-
const
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2247
|
+
const handleAISummarize = async () => {
|
|
2248
|
+
setIsSummarizing(true);
|
|
2249
|
+
const res = await fetch(
|
|
2250
|
+
"https://nonfibrous-extrafloral-verlene.ngrok-free.dev/extract",
|
|
2251
|
+
{
|
|
2252
|
+
method: "POST",
|
|
2253
|
+
headers: {
|
|
2254
|
+
"Content-Type": "application/json",
|
|
2255
|
+
"ngrok-skip-browser-warning": "true"
|
|
2256
|
+
},
|
|
2257
|
+
body: JSON.stringify({
|
|
2258
|
+
script: (scene == null ? void 0 : scene.content) || ""
|
|
2259
|
+
})
|
|
2260
|
+
}
|
|
2261
|
+
);
|
|
2262
|
+
if (res.ok) {
|
|
2263
|
+
const data = await res.json();
|
|
2264
|
+
setIsSummarizing(false);
|
|
2265
|
+
const parsedData = JSON.parse(data.data) || [];
|
|
2266
|
+
console.log("AI Tags:", parsedData);
|
|
2267
|
+
const newTags = [];
|
|
2268
|
+
parsedData.forEach((aiTag) => {
|
|
2269
|
+
if (!aiTag.block_id || !aiTag.category_id || typeof aiTag.start_index !== "number" || typeof aiTag.end_index !== "number") {
|
|
2270
|
+
return;
|
|
2271
|
+
}
|
|
2272
|
+
newTags.push({
|
|
2273
|
+
block_id: String(aiTag.block_id).startsWith("par") ? String(aiTag.block_id).substring(3) : String(aiTag.block_id),
|
|
2274
|
+
category_id: aiTag.category_id,
|
|
2275
|
+
name: aiTag.name,
|
|
2276
|
+
start_index: aiTag.start_index,
|
|
2277
|
+
end_index: aiTag.end_index
|
|
2278
|
+
});
|
|
2279
|
+
});
|
|
2280
|
+
setTags((prev) => {
|
|
2281
|
+
const merged = [...prev];
|
|
2282
|
+
newTags.forEach((newTag) => {
|
|
2283
|
+
const isOverlapping = merged.some(
|
|
2284
|
+
(t) => t.block_id === newTag.block_id && newTag.end_index > t.start_index && newTag.start_index < t.end_index
|
|
2285
|
+
);
|
|
2286
|
+
if (!isOverlapping) {
|
|
2287
|
+
merged.push(newTag);
|
|
2288
|
+
}
|
|
2289
|
+
});
|
|
2290
|
+
return merged;
|
|
2291
|
+
});
|
|
2292
|
+
return data;
|
|
2293
|
+
} else {
|
|
2294
|
+
setIsSummarizing(false);
|
|
2295
|
+
console.error("Failed to summarize scene:", res);
|
|
2296
|
+
}
|
|
2297
|
+
};
|
|
2194
2298
|
const addSubLocation = useCallback(
|
|
2195
2299
|
(subLocation) => {
|
|
2196
2300
|
const trimmed = subLocation.trim();
|
|
@@ -2203,7 +2307,6 @@ function useScriptBreakdownScene(sceneNumber) {
|
|
|
2203
2307
|
const removeSubLocation = useCallback((subLocation) => {
|
|
2204
2308
|
setSubLocations((prev) => prev.filter((loc) => loc !== subLocation));
|
|
2205
2309
|
}, []);
|
|
2206
|
-
const [sceneBrief, setSceneBrief] = useState("");
|
|
2207
2310
|
useEffect(() => {
|
|
2208
2311
|
setTags([]);
|
|
2209
2312
|
setSubLocations([]);
|
|
@@ -2222,19 +2325,19 @@ function useScriptBreakdownScene(sceneNumber) {
|
|
|
2222
2325
|
let match;
|
|
2223
2326
|
while ((match = regex.exec(block.text)) !== null) {
|
|
2224
2327
|
const isOverlapping = autoTags.some(
|
|
2225
|
-
(t) => t.
|
|
2328
|
+
(t) => t.block_id === block.id && match.index + char.length > t.start_index && match.index < t.end_index
|
|
2226
2329
|
);
|
|
2227
2330
|
if (!isOverlapping) {
|
|
2228
2331
|
autoTags.push({
|
|
2229
2332
|
id: uuid(),
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2333
|
+
block_id: block.id,
|
|
2334
|
+
category_id: "CAST",
|
|
2335
|
+
name: block.text.substring(
|
|
2233
2336
|
match.index,
|
|
2234
2337
|
match.index + char.length
|
|
2235
2338
|
),
|
|
2236
|
-
|
|
2237
|
-
|
|
2339
|
+
start_index: match.index,
|
|
2340
|
+
end_index: match.index + char.length
|
|
2238
2341
|
});
|
|
2239
2342
|
}
|
|
2240
2343
|
}
|
|
@@ -2334,15 +2437,15 @@ function useScriptBreakdownScene(sceneNumber) {
|
|
|
2334
2437
|
if (!selectionMenu) return;
|
|
2335
2438
|
const newTag = {
|
|
2336
2439
|
id: uuid(),
|
|
2337
|
-
|
|
2338
|
-
categoryId,
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2440
|
+
block_id: selectionMenu.blockId,
|
|
2441
|
+
category_id: categoryId,
|
|
2442
|
+
name: selectionMenu.text,
|
|
2443
|
+
start_index: selectionMenu.startIndex,
|
|
2444
|
+
end_index: selectionMenu.endIndex
|
|
2342
2445
|
};
|
|
2343
2446
|
setTags((prev) => {
|
|
2344
2447
|
const filtered = prev.filter(
|
|
2345
|
-
(t) => t.
|
|
2448
|
+
(t) => t.block_id !== newTag.block_id || !(newTag.end_index > t.start_index && newTag.start_index < t.end_index)
|
|
2346
2449
|
);
|
|
2347
2450
|
return [...filtered, newTag];
|
|
2348
2451
|
});
|
|
@@ -2372,7 +2475,9 @@ function useScriptBreakdownScene(sceneNumber) {
|
|
|
2372
2475
|
addSubLocation,
|
|
2373
2476
|
removeSubLocation,
|
|
2374
2477
|
sceneBrief,
|
|
2375
|
-
setSceneBrief
|
|
2478
|
+
setSceneBrief,
|
|
2479
|
+
handleAISummarize,
|
|
2480
|
+
isSummarizing
|
|
2376
2481
|
};
|
|
2377
2482
|
}
|
|
2378
2483
|
|