@vishu1301/script-writing 1.1.3 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +283 -180
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -9
- package/dist/index.d.ts +18 -9
- package/dist/index.js +284 -181
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -152,7 +152,7 @@ var blockStyles = {
|
|
|
152
152
|
}
|
|
153
153
|
};
|
|
154
154
|
pdfjs__namespace.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs__namespace.version}/build/pdf.worker.min.mjs`;
|
|
155
|
-
function PdfImporter({ onScriptImported, children }) {
|
|
155
|
+
function PdfImporter({ onScriptImported, disabled, children }) {
|
|
156
156
|
const [isProcessing, setIsProcessing] = react.useState(false);
|
|
157
157
|
const [error, setError] = react.useState(null);
|
|
158
158
|
const fileInputRef = react.useRef(null);
|
|
@@ -285,7 +285,7 @@ function PdfImporter({ onScriptImported, children }) {
|
|
|
285
285
|
type: "file",
|
|
286
286
|
accept: ".pdf,application/pdf",
|
|
287
287
|
onChange: handleFileChange,
|
|
288
|
-
disabled: isProcessing,
|
|
288
|
+
disabled: isProcessing || disabled,
|
|
289
289
|
className: "hidden",
|
|
290
290
|
id: "pdf-importer-input"
|
|
291
291
|
}
|
|
@@ -294,8 +294,8 @@ function PdfImporter({ onScriptImported, children }) {
|
|
|
294
294
|
"button",
|
|
295
295
|
{
|
|
296
296
|
onClick: handleClick,
|
|
297
|
-
disabled: isProcessing,
|
|
298
|
-
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",
|
|
297
|
+
disabled: isProcessing || disabled,
|
|
298
|
+
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",
|
|
299
299
|
"aria-label": "Import Script",
|
|
300
300
|
children: isProcessing ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold", children: "Processing..." }) : children
|
|
301
301
|
}
|
|
@@ -318,6 +318,8 @@ function ScreenplayEditorView({
|
|
|
318
318
|
showPdfImport = false,
|
|
319
319
|
showSaveButton = false,
|
|
320
320
|
showSyncButton = false,
|
|
321
|
+
isLocked = false,
|
|
322
|
+
onToggleLock,
|
|
321
323
|
handleBlockTextChange,
|
|
322
324
|
handleSceneTypeChange,
|
|
323
325
|
handleTimeOfDayChange,
|
|
@@ -391,7 +393,8 @@ function ScreenplayEditorView({
|
|
|
391
393
|
"button",
|
|
392
394
|
{
|
|
393
395
|
type: "button",
|
|
394
|
-
|
|
396
|
+
disabled: isLocked,
|
|
397
|
+
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" : ""}`,
|
|
395
398
|
onClick: () => handleBlockTypeChange(type),
|
|
396
399
|
children: [
|
|
397
400
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -430,8 +433,18 @@ function ScreenplayEditorView({
|
|
|
430
433
|
}) }),
|
|
431
434
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 shrink-0 relative px-1", children: [
|
|
432
435
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-[1px] h-6 bg-zinc-200/80 mx-2 hidden sm:block rounded-full" }),
|
|
433
|
-
showPdfImport && /* @__PURE__ */ jsxRuntime.jsx(PdfImporter, { onScriptImported: handleScriptImport, children: /* @__PURE__ */ jsxRuntime.jsx("div", { title: "Import Script", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { className: "w-[18px] h-[18px]" }) }) }),
|
|
434
|
-
|
|
436
|
+
showPdfImport && /* @__PURE__ */ jsxRuntime.jsx(PdfImporter, { disabled: isLocked, onScriptImported: handleScriptImport, children: /* @__PURE__ */ jsxRuntime.jsx("div", { title: "Import Script", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { className: "w-[18px] h-[18px]" }) }) }),
|
|
437
|
+
onToggleLock && /* @__PURE__ */ jsxRuntime.jsx(
|
|
438
|
+
"button",
|
|
439
|
+
{
|
|
440
|
+
onClick: onToggleLock,
|
|
441
|
+
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"}`,
|
|
442
|
+
title: isLocked ? "Unlock Script" : "Lock Script",
|
|
443
|
+
"aria-label": isLocked ? "Unlock Script" : "Lock Script",
|
|
444
|
+
children: isLocked ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Lock, { className: "w-[18px] h-[18px]" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Unlock, { className: "w-[18px] h-[18px]" })
|
|
445
|
+
}
|
|
446
|
+
),
|
|
447
|
+
onSave && showSaveButton && !isLocked && /* @__PURE__ */ jsxRuntime.jsx(
|
|
435
448
|
"button",
|
|
436
449
|
{
|
|
437
450
|
onClick: onSave,
|
|
@@ -451,7 +464,7 @@ function ScreenplayEditorView({
|
|
|
451
464
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileDown, { className: "w-[18px] h-[18px]" })
|
|
452
465
|
}
|
|
453
466
|
),
|
|
454
|
-
onSyncWithCloud && showSyncButton && /* @__PURE__ */ jsxRuntime.jsx(
|
|
467
|
+
onSyncWithCloud && showSyncButton && !isLocked && /* @__PURE__ */ jsxRuntime.jsx(
|
|
455
468
|
"button",
|
|
456
469
|
{
|
|
457
470
|
onClick: onSyncWithCloud,
|
|
@@ -528,6 +541,7 @@ function ScreenplayEditorView({
|
|
|
528
541
|
{
|
|
529
542
|
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",
|
|
530
543
|
spellCheck: false,
|
|
544
|
+
disabled: isLocked,
|
|
531
545
|
value: block.sceneNumber || "",
|
|
532
546
|
onChange: (e) => handleSceneNumberChange(
|
|
533
547
|
block.id,
|
|
@@ -548,6 +562,7 @@ function ScreenplayEditorView({
|
|
|
548
562
|
{
|
|
549
563
|
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",
|
|
550
564
|
"aria-label": "Scene Type",
|
|
565
|
+
disabled: isLocked,
|
|
551
566
|
value: (_a = block.sceneType) != null ? _a : "INT.",
|
|
552
567
|
onChange: (e) => handleSceneTypeChange(block.id, e.target.value),
|
|
553
568
|
style: {
|
|
@@ -567,7 +582,7 @@ function ScreenplayEditorView({
|
|
|
567
582
|
if (!el) return;
|
|
568
583
|
refs.current[block.id] = el;
|
|
569
584
|
},
|
|
570
|
-
contentEditable:
|
|
585
|
+
contentEditable: !isLocked,
|
|
571
586
|
suppressContentEditableWarning: true,
|
|
572
587
|
"aria-label": `Scene Heading: ${block.text}`,
|
|
573
588
|
"aria-haspopup": "listbox",
|
|
@@ -592,6 +607,7 @@ function ScreenplayEditorView({
|
|
|
592
607
|
{
|
|
593
608
|
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",
|
|
594
609
|
"aria-label": "Time of Day",
|
|
610
|
+
disabled: isLocked,
|
|
595
611
|
value: (_b = block.timeOfDay) != null ? _b : "DAY",
|
|
596
612
|
style: {
|
|
597
613
|
appearance: "none"
|
|
@@ -647,7 +663,7 @@ function ScreenplayEditorView({
|
|
|
647
663
|
if (!el) return;
|
|
648
664
|
refs.current[block.id] = el;
|
|
649
665
|
},
|
|
650
|
-
contentEditable:
|
|
666
|
+
contentEditable: !isLocked,
|
|
651
667
|
suppressContentEditableWarning: true,
|
|
652
668
|
"aria-label": `${blockStyles[block.type].label} text`,
|
|
653
669
|
"aria-multiline": block.type === "ACTION" || block.type === "DIALOGUE",
|
|
@@ -1728,13 +1744,14 @@ var handleSyncWithCloud = (blocks, sceneNumbers, onSaveAsSbx, project_name) => {
|
|
|
1728
1744
|
|
|
1729
1745
|
// app/types/script-breakdown.types.tsx
|
|
1730
1746
|
var CATEGORIES = [
|
|
1731
|
-
{ id: "CAST", label: "Cast", color: "#
|
|
1732
|
-
{ id: "PROP", label: "Prop", color: "#
|
|
1733
|
-
{ id: "COSTUME", label: "Costume", color: "#
|
|
1734
|
-
{ id: "VEHICLE", label: "Vehicle", color: "#
|
|
1735
|
-
{ id: "SET_PROP", label: "Set Prop", color: "#
|
|
1736
|
-
{ id: "EXTRA", label: "Extra", color: "#
|
|
1737
|
-
{ id: "LOCATION", label: "Location", color: "#
|
|
1747
|
+
{ id: "CAST", label: "Cast", color: "#7C3AED", hex: "#A78BFA" },
|
|
1748
|
+
{ id: "PROP", label: "Prop", color: "#FF6A00", hex: "#FFB86B" },
|
|
1749
|
+
{ id: "COSTUME", label: "Costume", color: "#FF2E93", hex: "#FF8AC2" },
|
|
1750
|
+
{ id: "VEHICLE", label: "Vehicle", color: "#00A6FF", hex: "#6ED6FF" },
|
|
1751
|
+
{ id: "SET_PROP", label: "Set Prop", color: "#00C853", hex: "#69F0AE" },
|
|
1752
|
+
{ id: "EXTRA", label: "Extra", color: "#00B8D4", hex: "#62EFFF" },
|
|
1753
|
+
{ id: "LOCATION", label: "Location", color: "#FFB300", hex: "#FFE082" },
|
|
1754
|
+
{ id: "OTHER", label: "Other", color: "#9E9E9E", hex: "#E0E0E0" }
|
|
1738
1755
|
];
|
|
1739
1756
|
function ScriptBreakdownSceneView({
|
|
1740
1757
|
blocks,
|
|
@@ -1753,7 +1770,9 @@ function ScriptBreakdownSceneView({
|
|
|
1753
1770
|
addSubLocation,
|
|
1754
1771
|
removeSubLocation,
|
|
1755
1772
|
sceneBrief,
|
|
1756
|
-
setSceneBrief
|
|
1773
|
+
setSceneBrief,
|
|
1774
|
+
onSummarize,
|
|
1775
|
+
isSummarizing
|
|
1757
1776
|
}) {
|
|
1758
1777
|
const COURIER_STACK = "'Courier Prime', 'Courier', monospace";
|
|
1759
1778
|
const [isSubLocOpen, setIsSubLocOpen] = react.useState(false);
|
|
@@ -1800,47 +1819,54 @@ function ScriptBreakdownSceneView({
|
|
|
1800
1819
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-zinc-500 animate-pulse", children: "Loading scene details..." })
|
|
1801
1820
|
] });
|
|
1802
1821
|
}
|
|
1803
|
-
const hasLocationTag = tags.some((t) => t.
|
|
1822
|
+
const hasLocationTag = tags.some((t) => t.category_id === "LOCATION");
|
|
1804
1823
|
const renderBlockText = (block) => {
|
|
1805
|
-
const blockTags = tags.filter((t) => t.
|
|
1824
|
+
const blockTags = tags.filter((t) => t.block_id === block.id).sort((a, b) => a.start_index - b.start_index);
|
|
1806
1825
|
if (blockTags.length === 0) return block.text;
|
|
1807
1826
|
const nodes = [];
|
|
1808
1827
|
let currentIndex = 0;
|
|
1809
1828
|
blockTags.forEach((tag) => {
|
|
1810
|
-
|
|
1829
|
+
const actualStart = Math.max(tag.start_index, currentIndex);
|
|
1830
|
+
if (actualStart > currentIndex) {
|
|
1811
1831
|
nodes.push(
|
|
1812
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: block.text.slice(currentIndex,
|
|
1832
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: block.text.slice(currentIndex, actualStart) }, `text-${currentIndex}`)
|
|
1813
1833
|
);
|
|
1814
1834
|
}
|
|
1815
|
-
const category = CATEGORIES.find((c) => c.id === tag.
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
e
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
+
const category = CATEGORIES.find((c) => c.id === tag.category_id);
|
|
1836
|
+
if (actualStart < tag.end_index) {
|
|
1837
|
+
nodes.push(
|
|
1838
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1839
|
+
"span",
|
|
1840
|
+
{
|
|
1841
|
+
title: `${category == null ? void 0 : category.label} (Click to edit)`,
|
|
1842
|
+
onClick: (e) => {
|
|
1843
|
+
e.stopPropagation();
|
|
1844
|
+
const selection = window.getSelection();
|
|
1845
|
+
if (!selection) return;
|
|
1846
|
+
const range = document.createRange();
|
|
1847
|
+
const textNode = e.currentTarget.firstChild;
|
|
1848
|
+
if (textNode && textNode.nodeType === Node.TEXT_NODE) {
|
|
1849
|
+
range.selectNodeContents(textNode);
|
|
1850
|
+
} else {
|
|
1851
|
+
range.selectNodeContents(e.currentTarget);
|
|
1852
|
+
}
|
|
1853
|
+
selection.removeAllRanges();
|
|
1854
|
+
selection.addRange(range);
|
|
1855
|
+
setTimeout(() => handleMouseUp(), 0);
|
|
1856
|
+
},
|
|
1857
|
+
className: "cursor-pointer font-bold transition-all hover:opacity-80 rounded-[3px]",
|
|
1858
|
+
style: {
|
|
1859
|
+
color: category == null ? void 0 : category.color,
|
|
1860
|
+
padding: "0.125rem 0.25rem",
|
|
1861
|
+
margin: "0 -0.125rem"
|
|
1862
|
+
},
|
|
1863
|
+
children: block.text.slice(actualStart, tag.end_index)
|
|
1835
1864
|
},
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
)
|
|
1842
|
-
);
|
|
1843
|
-
currentIndex = tag.endIndex;
|
|
1865
|
+
tag.id || `tag-${actualStart}-${tag.end_index}`
|
|
1866
|
+
)
|
|
1867
|
+
);
|
|
1868
|
+
}
|
|
1869
|
+
currentIndex = Math.max(currentIndex, tag.end_index);
|
|
1844
1870
|
});
|
|
1845
1871
|
if (currentIndex < block.text.length) {
|
|
1846
1872
|
nodes.push(
|
|
@@ -1928,16 +1954,16 @@ function ScriptBreakdownSceneView({
|
|
|
1928
1954
|
cat.id
|
|
1929
1955
|
)),
|
|
1930
1956
|
tags.some(
|
|
1931
|
-
(t) => t.
|
|
1957
|
+
(t) => t.block_id === block.id && t.start_index === selectionMenu.startIndex && t.end_index === selectionMenu.endIndex
|
|
1932
1958
|
) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 pt-1 border-t border-white/60", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1933
1959
|
"button",
|
|
1934
1960
|
{
|
|
1935
1961
|
onClick: (e) => {
|
|
1936
1962
|
const tagToRemove = tags.find(
|
|
1937
|
-
(t) => t.
|
|
1963
|
+
(t) => t.block_id === block.id && t.start_index === selectionMenu.startIndex && t.end_index === selectionMenu.endIndex
|
|
1938
1964
|
);
|
|
1939
|
-
if (tagToRemove) {
|
|
1940
|
-
removeTag(e, tagToRemove.id);
|
|
1965
|
+
if (tagToRemove && tagToRemove.id) {
|
|
1966
|
+
removeTag(e, tagToRemove == null ? void 0 : tagToRemove.id);
|
|
1941
1967
|
clearSelection();
|
|
1942
1968
|
}
|
|
1943
1969
|
},
|
|
@@ -1980,115 +2006,136 @@ function ScriptBreakdownSceneView({
|
|
|
1980
2006
|
) })
|
|
1981
2007
|
] })
|
|
1982
2008
|
] }),
|
|
1983
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1984
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2009
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full lg:w-80 flex-shrink-0 flex flex-col gap-6 sticky top-6", children: [
|
|
2010
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2011
|
+
"button",
|
|
2012
|
+
{
|
|
2013
|
+
onClick: onSummarize,
|
|
2014
|
+
disabled: isSummarizing,
|
|
2015
|
+
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]"}`,
|
|
2016
|
+
children: [
|
|
2017
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inset-0 opacity-0 group-hover:opacity-100 transition duration-700", children: /* @__PURE__ */ jsxRuntime.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]" }) }),
|
|
2018
|
+
/* @__PURE__ */ jsxRuntime.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%)]" }),
|
|
2019
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inset-[1px] rounded-[inherit] bg-gradient-to-b from-white/90 to-transparent pointer-events-none" }),
|
|
2020
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inset-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.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]" }) }),
|
|
2021
|
+
/* @__PURE__ */ jsxRuntime.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" }),
|
|
2022
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex items-center justify-center gap-3", children: [
|
|
2023
|
+
isSummarizing ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "w-4 h-4 text-violet-500 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "w-4 h-4 text-violet-500 transition-all duration-500 group-hover:-rotate-12 group-hover:scale-125" }),
|
|
2024
|
+
/* @__PURE__ */ jsxRuntime.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" })
|
|
2025
|
+
] })
|
|
2026
|
+
]
|
|
2027
|
+
}
|
|
2028
|
+
),
|
|
2029
|
+
/* @__PURE__ */ jsxRuntime.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: [
|
|
2030
|
+
/* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "text-xs font-extrabold text-slate-800 uppercase tracking-[0.25em] mb-8 flex items-center gap-3", children: [
|
|
2031
|
+
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx(lucideReact.Tags, { className: "w-3.5 h-3.5 text-slate-500" }) }),
|
|
2032
|
+
"Tags",
|
|
2033
|
+
/* @__PURE__ */ jsxRuntime.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 })
|
|
2034
|
+
] }),
|
|
2035
|
+
tags.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-8", children: CATEGORIES.map((cat) => {
|
|
2036
|
+
const catTags = Array.from(
|
|
2037
|
+
new Map(
|
|
2038
|
+
tags.filter((t) => t.category_id === cat.id).map((tag) => [tag.name.toLowerCase(), tag])
|
|
2039
|
+
).values()
|
|
2040
|
+
);
|
|
2041
|
+
if (catTags.length === 0) return null;
|
|
2042
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
2043
|
+
/* @__PURE__ */ jsxRuntime.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: [
|
|
2044
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2045
|
+
"div",
|
|
2046
|
+
{
|
|
2047
|
+
className: "w-2 h-2 rounded-full shadow-sm",
|
|
2048
|
+
style: { backgroundColor: cat.hex }
|
|
2049
|
+
}
|
|
2050
|
+
),
|
|
2051
|
+
cat.label
|
|
2052
|
+
] }),
|
|
2053
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
2054
|
+
catTags.map((tag, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2055
|
+
"span",
|
|
2056
|
+
{
|
|
2057
|
+
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",
|
|
2058
|
+
style: { color: cat.color },
|
|
2059
|
+
children: tag.name
|
|
2060
|
+
},
|
|
2061
|
+
index
|
|
2062
|
+
)),
|
|
2063
|
+
cat.id === "LOCATION" && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2064
|
+
"div",
|
|
2065
|
+
{
|
|
2066
|
+
className: "relative flex items-center",
|
|
2067
|
+
ref: subLocPopoverRef,
|
|
2068
|
+
children: [
|
|
2069
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2070
|
+
"button",
|
|
2071
|
+
{
|
|
2072
|
+
onClick: () => setIsSubLocOpen(!isSubLocOpen),
|
|
2073
|
+
className: "flex items-center justify-center w-5 h-5 rounded-full hover:bg-slate-200 transition-colors",
|
|
2074
|
+
title: "Add Sub Location",
|
|
2075
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "w-3 h-3 text-slate-500" })
|
|
2076
|
+
}
|
|
2077
|
+
),
|
|
2078
|
+
isSubLocOpen && /* @__PURE__ */ jsxRuntime.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: [
|
|
2079
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[9px] font-extrabold tracking-[0.2em] text-slate-400 uppercase mb-2 px-1", children: "Add Sub Location" }),
|
|
2080
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 items-center", children: [
|
|
2081
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2082
|
+
"input",
|
|
2083
|
+
{
|
|
2084
|
+
type: "text",
|
|
2085
|
+
value: subLocInput,
|
|
2086
|
+
onChange: (e) => setSubLocInput(e.target.value),
|
|
2087
|
+
onKeyDown: (e) => {
|
|
2088
|
+
if (e.key === "Enter") {
|
|
2089
|
+
addSubLocation(subLocInput);
|
|
2090
|
+
setSubLocInput("");
|
|
2091
|
+
setIsSubLocOpen(false);
|
|
2092
|
+
}
|
|
2093
|
+
},
|
|
2094
|
+
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",
|
|
2095
|
+
placeholder: "Sub location...",
|
|
2096
|
+
autoFocus: true
|
|
2097
|
+
}
|
|
2098
|
+
),
|
|
2099
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2100
|
+
"button",
|
|
2101
|
+
{
|
|
2102
|
+
onClick: () => {
|
|
2032
2103
|
addSubLocation(subLocInput);
|
|
2033
2104
|
setSubLocInput("");
|
|
2034
2105
|
setIsSubLocOpen(false);
|
|
2035
|
-
}
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
),
|
|
2042
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2043
|
-
"button",
|
|
2044
|
-
{
|
|
2045
|
-
onClick: () => {
|
|
2046
|
-
addSubLocation(subLocInput);
|
|
2047
|
-
setSubLocInput("");
|
|
2048
|
-
setIsSubLocOpen(false);
|
|
2049
|
-
},
|
|
2050
|
-
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",
|
|
2051
|
-
children: "Add"
|
|
2052
|
-
}
|
|
2053
|
-
)
|
|
2106
|
+
},
|
|
2107
|
+
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",
|
|
2108
|
+
children: "Add"
|
|
2109
|
+
}
|
|
2110
|
+
)
|
|
2111
|
+
] })
|
|
2054
2112
|
] })
|
|
2055
|
-
]
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-2.5 h-2.5" })
|
|
2082
|
-
}
|
|
2083
|
-
)
|
|
2084
|
-
]
|
|
2085
|
-
},
|
|
2086
|
-
`sub-${subLoc}`
|
|
2087
|
-
))
|
|
2088
|
-
] })
|
|
2089
|
-
] }, cat.id);
|
|
2090
|
-
}) }) : /* @__PURE__ */ jsxRuntime.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." })
|
|
2091
|
-
] }) })
|
|
2113
|
+
]
|
|
2114
|
+
}
|
|
2115
|
+
),
|
|
2116
|
+
cat.id === "LOCATION" && subLocations.map((subLoc) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2117
|
+
"span",
|
|
2118
|
+
{
|
|
2119
|
+
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",
|
|
2120
|
+
children: [
|
|
2121
|
+
subLoc,
|
|
2122
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2123
|
+
"button",
|
|
2124
|
+
{
|
|
2125
|
+
onClick: () => removeSubLocation(subLoc),
|
|
2126
|
+
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",
|
|
2127
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-2.5 h-2.5" })
|
|
2128
|
+
}
|
|
2129
|
+
)
|
|
2130
|
+
]
|
|
2131
|
+
},
|
|
2132
|
+
`sub-${subLoc}`
|
|
2133
|
+
))
|
|
2134
|
+
] })
|
|
2135
|
+
] }, cat.id);
|
|
2136
|
+
}) }) : /* @__PURE__ */ jsxRuntime.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." })
|
|
2137
|
+
] })
|
|
2138
|
+
] })
|
|
2092
2139
|
] }) });
|
|
2093
2140
|
}
|
|
2094
2141
|
function useScriptBreakdown({
|
|
@@ -2170,6 +2217,14 @@ var use_script_breakdown_default = useScriptBreakdown;
|
|
|
2170
2217
|
// app/hook/use-script-breakdown-scene.ts
|
|
2171
2218
|
function useScriptBreakdownScene(sceneNumber) {
|
|
2172
2219
|
const { scenes, isLoading, error } = use_script_breakdown_default({ scenes: [] });
|
|
2220
|
+
const [tags, setTags] = react.useState([]);
|
|
2221
|
+
const [selectionMenu, setSelectionMenu] = react.useState(null);
|
|
2222
|
+
const autoTaggedSceneRef = react.useRef(null);
|
|
2223
|
+
const [menuPlacement, setMenuPlacement] = react.useState("top");
|
|
2224
|
+
const [subLocations, setSubLocations] = react.useState([]);
|
|
2225
|
+
const [sceneBrief, setSceneBrief] = react.useState("");
|
|
2226
|
+
const [isSummarizing, setIsSummarizing] = react.useState(false);
|
|
2227
|
+
const menuRef = react.useRef(null);
|
|
2173
2228
|
const scene = react.useMemo(() => {
|
|
2174
2229
|
return scenes.find((s) => s.scene_number === sceneNumber);
|
|
2175
2230
|
}, [scenes, sceneNumber]);
|
|
@@ -2198,7 +2253,9 @@ function useScriptBreakdownScene(sceneNumber) {
|
|
|
2198
2253
|
break;
|
|
2199
2254
|
}
|
|
2200
2255
|
}
|
|
2201
|
-
|
|
2256
|
+
const idAttr = div.getAttribute("id");
|
|
2257
|
+
const blockId = idAttr && idAttr.startsWith("par") ? idAttr.substring(3) : idAttr || uuid();
|
|
2258
|
+
parsedBlocks.push({ id: blockId, type, text: divText });
|
|
2202
2259
|
});
|
|
2203
2260
|
return parsedBlocks;
|
|
2204
2261
|
}, [scene]);
|
|
@@ -2210,12 +2267,57 @@ function useScriptBreakdownScene(sceneNumber) {
|
|
|
2210
2267
|
}).filter(Boolean);
|
|
2211
2268
|
return [...new Set(chars)];
|
|
2212
2269
|
}, [blocks]);
|
|
2213
|
-
const
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2270
|
+
const handleAISummarize = async () => {
|
|
2271
|
+
setIsSummarizing(true);
|
|
2272
|
+
const res = await fetch(
|
|
2273
|
+
"https://nonfibrous-extrafloral-verlene.ngrok-free.dev/extract",
|
|
2274
|
+
{
|
|
2275
|
+
method: "POST",
|
|
2276
|
+
headers: {
|
|
2277
|
+
"Content-Type": "application/json",
|
|
2278
|
+
"ngrok-skip-browser-warning": "true"
|
|
2279
|
+
},
|
|
2280
|
+
body: JSON.stringify({
|
|
2281
|
+
script: (scene == null ? void 0 : scene.content) || ""
|
|
2282
|
+
})
|
|
2283
|
+
}
|
|
2284
|
+
);
|
|
2285
|
+
if (res.ok) {
|
|
2286
|
+
const data = await res.json();
|
|
2287
|
+
setIsSummarizing(false);
|
|
2288
|
+
const parsedData = JSON.parse(data.data) || [];
|
|
2289
|
+
console.log("AI Tags:", parsedData);
|
|
2290
|
+
const newTags = [];
|
|
2291
|
+
parsedData.forEach((aiTag) => {
|
|
2292
|
+
if (!aiTag.block_id || !aiTag.category_id || typeof aiTag.start_index !== "number" || typeof aiTag.end_index !== "number") {
|
|
2293
|
+
return;
|
|
2294
|
+
}
|
|
2295
|
+
newTags.push({
|
|
2296
|
+
block_id: String(aiTag.block_id).startsWith("par") ? String(aiTag.block_id).substring(3) : String(aiTag.block_id),
|
|
2297
|
+
category_id: aiTag.category_id,
|
|
2298
|
+
name: aiTag.name,
|
|
2299
|
+
start_index: aiTag.start_index,
|
|
2300
|
+
end_index: aiTag.end_index
|
|
2301
|
+
});
|
|
2302
|
+
});
|
|
2303
|
+
setTags((prev) => {
|
|
2304
|
+
const merged = [...prev];
|
|
2305
|
+
newTags.forEach((newTag) => {
|
|
2306
|
+
const isOverlapping = merged.some(
|
|
2307
|
+
(t) => t.block_id === newTag.block_id && newTag.end_index > t.start_index && newTag.start_index < t.end_index
|
|
2308
|
+
);
|
|
2309
|
+
if (!isOverlapping) {
|
|
2310
|
+
merged.push(newTag);
|
|
2311
|
+
}
|
|
2312
|
+
});
|
|
2313
|
+
return merged;
|
|
2314
|
+
});
|
|
2315
|
+
return data;
|
|
2316
|
+
} else {
|
|
2317
|
+
setIsSummarizing(false);
|
|
2318
|
+
console.error("Failed to summarize scene:", res);
|
|
2319
|
+
}
|
|
2320
|
+
};
|
|
2219
2321
|
const addSubLocation = react.useCallback(
|
|
2220
2322
|
(subLocation) => {
|
|
2221
2323
|
const trimmed = subLocation.trim();
|
|
@@ -2228,7 +2330,6 @@ function useScriptBreakdownScene(sceneNumber) {
|
|
|
2228
2330
|
const removeSubLocation = react.useCallback((subLocation) => {
|
|
2229
2331
|
setSubLocations((prev) => prev.filter((loc) => loc !== subLocation));
|
|
2230
2332
|
}, []);
|
|
2231
|
-
const [sceneBrief, setSceneBrief] = react.useState("");
|
|
2232
2333
|
react.useEffect(() => {
|
|
2233
2334
|
setTags([]);
|
|
2234
2335
|
setSubLocations([]);
|
|
@@ -2247,19 +2348,19 @@ function useScriptBreakdownScene(sceneNumber) {
|
|
|
2247
2348
|
let match;
|
|
2248
2349
|
while ((match = regex.exec(block.text)) !== null) {
|
|
2249
2350
|
const isOverlapping = autoTags.some(
|
|
2250
|
-
(t) => t.
|
|
2351
|
+
(t) => t.block_id === block.id && match.index + char.length > t.start_index && match.index < t.end_index
|
|
2251
2352
|
);
|
|
2252
2353
|
if (!isOverlapping) {
|
|
2253
2354
|
autoTags.push({
|
|
2254
2355
|
id: uuid(),
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2356
|
+
block_id: block.id,
|
|
2357
|
+
category_id: "CAST",
|
|
2358
|
+
name: block.text.substring(
|
|
2258
2359
|
match.index,
|
|
2259
2360
|
match.index + char.length
|
|
2260
2361
|
),
|
|
2261
|
-
|
|
2262
|
-
|
|
2362
|
+
start_index: match.index,
|
|
2363
|
+
end_index: match.index + char.length
|
|
2263
2364
|
});
|
|
2264
2365
|
}
|
|
2265
2366
|
}
|
|
@@ -2359,15 +2460,15 @@ function useScriptBreakdownScene(sceneNumber) {
|
|
|
2359
2460
|
if (!selectionMenu) return;
|
|
2360
2461
|
const newTag = {
|
|
2361
2462
|
id: uuid(),
|
|
2362
|
-
|
|
2363
|
-
categoryId,
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2463
|
+
block_id: selectionMenu.blockId,
|
|
2464
|
+
category_id: categoryId,
|
|
2465
|
+
name: selectionMenu.text,
|
|
2466
|
+
start_index: selectionMenu.startIndex,
|
|
2467
|
+
end_index: selectionMenu.endIndex
|
|
2367
2468
|
};
|
|
2368
2469
|
setTags((prev) => {
|
|
2369
2470
|
const filtered = prev.filter(
|
|
2370
|
-
(t) => t.
|
|
2471
|
+
(t) => t.block_id !== newTag.block_id || !(newTag.end_index > t.start_index && newTag.start_index < t.end_index)
|
|
2371
2472
|
);
|
|
2372
2473
|
return [...filtered, newTag];
|
|
2373
2474
|
});
|
|
@@ -2397,7 +2498,9 @@ function useScriptBreakdownScene(sceneNumber) {
|
|
|
2397
2498
|
addSubLocation,
|
|
2398
2499
|
removeSubLocation,
|
|
2399
2500
|
sceneBrief,
|
|
2400
|
-
setSceneBrief
|
|
2501
|
+
setSceneBrief,
|
|
2502
|
+
handleAISummarize,
|
|
2503
|
+
isSummarizing
|
|
2401
2504
|
};
|
|
2402
2505
|
}
|
|
2403
2506
|
|