@vishu1301/script-writing 1.1.4 → 1.1.6

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.d.cts CHANGED
@@ -43,11 +43,11 @@ declare function useScreenplayEditor(options?: UseScreenplayEditorOptions): {
43
43
  handleBlockTypeChange: (newType: BlockType) => void;
44
44
  handleSelectCharacterExtension: (extension: string) => void;
45
45
  handleKeyDown: (e: KeyboardEvent<HTMLDivElement>, id: string, text: string) => void;
46
- handleScriptImport: (title: string, content: string, preParsedBlocks?: Partial<Block>[]) => void;
46
+ handleScriptImport: (title: string, content: string, preParsedBlocks?: Partial<Block>[], isInitialLoad?: boolean) => void;
47
47
  handleSceneNumberChange: (id: string, newNumber: string) => void;
48
48
  handleFocus: (id: string) => void;
49
49
  handleBlur: (id: string) => void;
50
- loadFromUrl: (url: string, fetchOptions?: RequestInit) => Promise<void>;
50
+ loadFromUrl: (url: string, fetchOptions?: RequestInit, isInitialLoad?: boolean) => Promise<void>;
51
51
  };
52
52
 
53
53
  type ScreenplayEditorViewProps = ReturnType<typeof useScreenplayEditor> & {
@@ -120,12 +120,12 @@ declare function ScriptBreakdownSceneView({ blocks, characters, isLoading, scene
120
120
  isSummarizing?: boolean;
121
121
  }): react_jsx_runtime.JSX.Element;
122
122
 
123
- declare function useScriptBreakdownScene(sceneNumber: string): {
124
- scene: ScriptBreakdown | undefined;
123
+ declare function useScriptBreakdownScene(scene_url: string, fetchOptions?: RequestInit, onAISummarize?: (scene: any) => void, onTagAdded?: (tag: Tag) => void, onTagRemoved?: (tagId: string) => void): {
124
+ scene: any;
125
125
  blocks: Block[];
126
126
  characters: string[];
127
127
  isLoading: boolean;
128
- error: Error | null;
128
+ error: boolean;
129
129
  tags: Tag[];
130
130
  selectionMenu: {
131
131
  blockId: string;
@@ -136,10 +136,10 @@ declare function useScriptBreakdownScene(sceneNumber: string): {
136
136
  left: number;
137
137
  } | null;
138
138
  handleMouseUp: () => void;
139
- addTag: (categoryId: ElementCategory) => void;
140
- removeTag: (e: React.MouseEvent, id: string) => void;
139
+ addTag: (categoryId: ElementCategory) => Promise<void>;
140
+ removeTag: (e: React.MouseEvent, id: string) => Promise<void>;
141
141
  clearSelection: () => void;
142
- menuPlacement: "top" | "bottom";
142
+ menuPlacement: "bottom" | "top";
143
143
  menuRef: React$1.RefObject<HTMLDivElement | null>;
144
144
  subLocations: string[];
145
145
  addSubLocation: (subLocation: string) => void;
package/dist/index.d.ts CHANGED
@@ -43,11 +43,11 @@ declare function useScreenplayEditor(options?: UseScreenplayEditorOptions): {
43
43
  handleBlockTypeChange: (newType: BlockType) => void;
44
44
  handleSelectCharacterExtension: (extension: string) => void;
45
45
  handleKeyDown: (e: KeyboardEvent<HTMLDivElement>, id: string, text: string) => void;
46
- handleScriptImport: (title: string, content: string, preParsedBlocks?: Partial<Block>[]) => void;
46
+ handleScriptImport: (title: string, content: string, preParsedBlocks?: Partial<Block>[], isInitialLoad?: boolean) => void;
47
47
  handleSceneNumberChange: (id: string, newNumber: string) => void;
48
48
  handleFocus: (id: string) => void;
49
49
  handleBlur: (id: string) => void;
50
- loadFromUrl: (url: string, fetchOptions?: RequestInit) => Promise<void>;
50
+ loadFromUrl: (url: string, fetchOptions?: RequestInit, isInitialLoad?: boolean) => Promise<void>;
51
51
  };
52
52
 
53
53
  type ScreenplayEditorViewProps = ReturnType<typeof useScreenplayEditor> & {
@@ -120,12 +120,12 @@ declare function ScriptBreakdownSceneView({ blocks, characters, isLoading, scene
120
120
  isSummarizing?: boolean;
121
121
  }): react_jsx_runtime.JSX.Element;
122
122
 
123
- declare function useScriptBreakdownScene(sceneNumber: string): {
124
- scene: ScriptBreakdown | undefined;
123
+ declare function useScriptBreakdownScene(scene_url: string, fetchOptions?: RequestInit, onAISummarize?: (scene: any) => void, onTagAdded?: (tag: Tag) => void, onTagRemoved?: (tagId: string) => void): {
124
+ scene: any;
125
125
  blocks: Block[];
126
126
  characters: string[];
127
127
  isLoading: boolean;
128
- error: Error | null;
128
+ error: boolean;
129
129
  tags: Tag[];
130
130
  selectionMenu: {
131
131
  blockId: string;
@@ -136,10 +136,10 @@ declare function useScriptBreakdownScene(sceneNumber: string): {
136
136
  left: number;
137
137
  } | null;
138
138
  handleMouseUp: () => void;
139
- addTag: (categoryId: ElementCategory) => void;
140
- removeTag: (e: React.MouseEvent, id: string) => void;
139
+ addTag: (categoryId: ElementCategory) => Promise<void>;
140
+ removeTag: (e: React.MouseEvent, id: string) => Promise<void>;
141
141
  clearSelection: () => void;
142
- menuPlacement: "top" | "bottom";
142
+ menuPlacement: "bottom" | "top";
143
143
  menuRef: React$1.RefObject<HTMLDivElement | null>;
144
144
  subLocations: string[];
145
145
  addSubLocation: (subLocation: string) => void;
package/dist/index.js CHANGED
@@ -1365,7 +1365,7 @@ function useScreenplayEditor(options) {
1365
1365
  [blocks, handleBlockTextChange]
1366
1366
  );
1367
1367
  const handleScriptImport = useCallback(
1368
- (title, content, preParsedBlocks) => {
1368
+ (title, content, preParsedBlocks, isInitialLoad) => {
1369
1369
  let parsedBlocks = [];
1370
1370
  if (preParsedBlocks && preParsedBlocks.length > 0) {
1371
1371
  parsedBlocks = preParsedBlocks.map((b) => ({
@@ -1421,8 +1421,10 @@ function useScreenplayEditor(options) {
1421
1421
  }).join("");
1422
1422
  if (sbxData !== lastSavedContent.current) {
1423
1423
  lastSavedContent.current = sbxData;
1424
- const blob = new Blob([sbxData], { type: "text/plain" });
1425
- onSaveRef.current(blob);
1424
+ if (!isInitialLoad) {
1425
+ const blob = new Blob([sbxData], { type: "text/plain" });
1426
+ onSaveRef.current(blob);
1427
+ }
1426
1428
  }
1427
1429
  }
1428
1430
  setTimeout(() => {
@@ -1470,7 +1472,7 @@ function useScreenplayEditor(options) {
1470
1472
  }, 200);
1471
1473
  }, []);
1472
1474
  const loadFromUrl = useCallback(
1473
- async (url, fetchOptions = {}) => {
1475
+ async (url, fetchOptions = {}, isInitialLoad) => {
1474
1476
  var _a;
1475
1477
  try {
1476
1478
  const response = await fetch(url, fetchOptions);
@@ -1542,7 +1544,7 @@ function useScreenplayEditor(options) {
1542
1544
  }
1543
1545
  }
1544
1546
  const filename = ((_a = url.split("/").pop()) == null ? void 0 : _a.replace(/\.sbx$/i, "")) || "Imported from URL";
1545
- handleScriptImport(filename, scriptContent, preParsedBlocks);
1547
+ handleScriptImport(filename, scriptContent, preParsedBlocks, isInitialLoad);
1546
1548
  } catch (error) {
1547
1549
  console.error(
1548
1550
  "[useScreenplayEditor] Error loading script from URL:",
@@ -1556,7 +1558,7 @@ function useScreenplayEditor(options) {
1556
1558
  useEffect(() => {
1557
1559
  if ((options == null ? void 0 : options.initialUrl) && options.initialUrl !== loadedUrlRef.current) {
1558
1560
  loadedUrlRef.current = options.initialUrl;
1559
- loadFromUrl(options.initialUrl, options.fetchOptions);
1561
+ loadFromUrl(options.initialUrl, options.fetchOptions, true);
1560
1562
  }
1561
1563
  }, [options == null ? void 0 : options.initialUrl, options == null ? void 0 : options.fetchOptions, loadFromUrl]);
1562
1564
  return {
@@ -1728,6 +1730,166 @@ var CATEGORIES = [
1728
1730
  { id: "LOCATION", label: "Location", color: "#FFB300", hex: "#FFE082" },
1729
1731
  { id: "OTHER", label: "Other", color: "#9E9E9E", hex: "#E0E0E0" }
1730
1732
  ];
1733
+ var PopcornIcon = ({ isSummarizing }) => /* @__PURE__ */ jsxs(
1734
+ "svg",
1735
+ {
1736
+ xmlns: "http://www.w3.org/2000/svg",
1737
+ width: "24",
1738
+ height: "24",
1739
+ viewBox: "0 0 24 24",
1740
+ fill: "none",
1741
+ strokeLinecap: "round",
1742
+ strokeLinejoin: "round",
1743
+ className: `w-6 h-6 transition-all duration-500 ${isSummarizing ? "animate-cook" : "group-hover:scale-110 group-hover:-rotate-12"}`,
1744
+ children: [
1745
+ /* @__PURE__ */ jsx(
1746
+ "path",
1747
+ {
1748
+ d: "M18 8a2 2 0 0 0 0-4 2 2 0 0 0-4 0 2 2 0 0 0-4 0 2 2 0 0 0-4 0 2 2 0 0 0 0 4",
1749
+ className: "stroke-yellow-400",
1750
+ strokeWidth: "2"
1751
+ }
1752
+ ),
1753
+ /* @__PURE__ */ jsxs("g", { className: "stroke-red-500", strokeWidth: "2", children: [
1754
+ /* @__PURE__ */ jsx("path", { d: "M20 8c.5 0 .9.4.8 1l-2.6 12c-.1.5-.7 1-1.2 1H7c-.6 0-1.1-.4-1.2-1L3.2 9c-.1-.6.3-1 .8-1Z" }),
1755
+ /* @__PURE__ */ jsx("path", { d: "M10 22 9 8", className: "stroke-red-500", strokeWidth: "1" }),
1756
+ /* @__PURE__ */ jsx("path", { d: "m14 22 1-14", className: "stroke-red-500", strokeWidth: "1" })
1757
+ ] })
1758
+ ]
1759
+ }
1760
+ );
1761
+ var SummarizeButton = ({
1762
+ onSummarize,
1763
+ isSummarizing
1764
+ }) => {
1765
+ return /* @__PURE__ */ jsxs(
1766
+ "button",
1767
+ {
1768
+ onClick: onSummarize,
1769
+ disabled: isSummarizing,
1770
+ className: `group relative w-full py-4 px-8 rounded-[3rem] transition-all duration-500 ease-[cubic-bezier(0.22,1,0.36,1)] border ${isSummarizing ? "bg-zinc-100 border-zinc-300 opacity-100 cursor-wait shadow-inner" : "bg-gradient-to-b from-white via-zinc-50 to-zinc-100 border-white/70 shadow-[0_10px_30px_-10px_rgba(0,0,0,0.12),inset_0_1px_0_rgba(255,255,255,0.8)] hover:shadow-[0_20px_50px_-15px_rgba(139,92,246,0.3)] hover:-translate-y-[2px] active:scale-[0.98]"}`,
1771
+ children: [
1772
+ /* @__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_0deg_at_50%_50%,#c084fc,transparent_60%,#fb7185,transparent_90%)] blur-3xl opacity-20 animate-[spin_10s_linear_infinite]" }) }),
1773
+ /* @__PURE__ */ jsx(
1774
+ "span",
1775
+ {
1776
+ className: `absolute inset-0 transition-opacity duration-700 bg-[radial-gradient(circle_at_50%_50%,rgba(139,92,246,0.1),transparent_70%)] ${isSummarizing ? "opacity-100" : "opacity-0"}`
1777
+ }
1778
+ ),
1779
+ /* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center gap-4", children: [
1780
+ /* @__PURE__ */ jsx("style", { children: `
1781
+ @keyframes kernel-pop {
1782
+ 0% { transform: translate(0, 0) scale(0) rotate(0deg); opacity: 0; }
1783
+ 10% { opacity: 1; transform: translate(var(--tx1), var(--ty1)) scale(1.3) rotate(45deg); }
1784
+ 40% { transform: translate(var(--tx2), var(--ty2)) scale(1) rotate(180deg); opacity: 1; }
1785
+ 100% { transform: translate(var(--tx3), var(--ty3)) scale(0.4) rotate(360deg); opacity: 0; }
1786
+ }
1787
+ @keyframes icon-cook {
1788
+ 0%, 100% { transform: scale(1.1) translateY(0); }
1789
+ 50% { transform: scale(0.9, 1.1) translateY(-3px) rotate(3deg); }
1790
+ }
1791
+ .animate-kernel {
1792
+ animation: kernel-pop var(--dur) cubic-bezier(0.34, 1.56, 0.64, 1) infinite;
1793
+ animation-delay: var(--delay);
1794
+ filter: drop-shadow(0 4px 6px rgba(0,0,0,0.15)); /* Added shadow for visibility */
1795
+ }
1796
+ .animate-cook { animation: icon-cook 0.3s ease-in-out infinite; }
1797
+ ` }),
1798
+ /* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center", children: [
1799
+ /* @__PURE__ */ jsx(PopcornIcon, { isSummarizing }),
1800
+ isSummarizing && /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 pointer-events-none", children: [
1801
+ /* @__PURE__ */ jsx(
1802
+ "svg",
1803
+ {
1804
+ className: "animate-kernel absolute w-4 h-4",
1805
+ style: {
1806
+ "--dur": "1.2s",
1807
+ "--delay": "0s",
1808
+ "--tx1": "-18px",
1809
+ "--ty1": "-22px",
1810
+ "--tx2": "-28px",
1811
+ "--ty2": "-28px",
1812
+ "--tx3": "-38px",
1813
+ "--ty3": "12px"
1814
+ },
1815
+ viewBox: "0 0 24 24",
1816
+ children: /* @__PURE__ */ jsx(
1817
+ "path",
1818
+ {
1819
+ d: "M12 2a4 4 0 0 0-4 4 4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0 4-4 4 4 0 0 0-4-4 4 4 0 0 0-4-4z",
1820
+ fill: "#FBBF24",
1821
+ stroke: "#F59E0B",
1822
+ strokeWidth: "0.5"
1823
+ }
1824
+ )
1825
+ }
1826
+ ),
1827
+ /* @__PURE__ */ jsx(
1828
+ "svg",
1829
+ {
1830
+ className: "animate-kernel absolute w-3.5 h-3.5",
1831
+ style: {
1832
+ "--dur": "1.5s",
1833
+ "--delay": "0.3s",
1834
+ "--tx1": "18px",
1835
+ "--ty1": "-28px",
1836
+ "--tx2": "28px",
1837
+ "--ty2": "-38px",
1838
+ "--tx3": "38px",
1839
+ "--ty3": "18px"
1840
+ },
1841
+ viewBox: "0 0 24 24",
1842
+ children: /* @__PURE__ */ jsx(
1843
+ "path",
1844
+ {
1845
+ d: "M12 3c-2 0-3 1-4 2-1-1-3-1-4 1-1 2 0 4 1 5-1 1-1 3 1 4 2 1 4 0 6-1 2 1 4 2 6 1 2-1 2-3 1-4 1-1 2-3 1-5-1-2-3-2-4-1-1-1-2-2-4-2z",
1846
+ fill: "#FDE68A",
1847
+ stroke: "#FBBF24",
1848
+ strokeWidth: "0.5"
1849
+ }
1850
+ )
1851
+ }
1852
+ ),
1853
+ /* @__PURE__ */ jsx(
1854
+ "svg",
1855
+ {
1856
+ className: "animate-kernel absolute w-4 h-4",
1857
+ style: {
1858
+ "--dur": "1.8s",
1859
+ "--delay": "0.7s",
1860
+ "--tx1": "-2px",
1861
+ "--ty1": "-28px",
1862
+ "--tx2": "5px",
1863
+ "--ty2": "-48px",
1864
+ "--tx3": "10px",
1865
+ "--ty3": "18px"
1866
+ },
1867
+ viewBox: "0 0 24 24",
1868
+ children: /* @__PURE__ */ jsx(
1869
+ "path",
1870
+ {
1871
+ d: "M12 2a4 4 0 0 0-4 4 4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0 4-4 4 4 0 0 0-4-4 4 4 0 0 0-4-4z",
1872
+ fill: "#FEF3C7"
1873
+ }
1874
+ )
1875
+ }
1876
+ )
1877
+ ] })
1878
+ ] }),
1879
+ /* @__PURE__ */ jsx(
1880
+ "span",
1881
+ {
1882
+ className: `text-[12px] font-bold tracking-[0.25em] uppercase transition-all duration-300 ${isSummarizing ? "text-violet-700" : "text-zinc-500 group-hover:text-zinc-900"}`,
1883
+ children: isSummarizing ? "Summarizing..." : "Summarize Scene"
1884
+ }
1885
+ )
1886
+ ] }),
1887
+ /* @__PURE__ */ jsx("span", { className: "absolute bottom-0 left-0 right-0 h-[1px] bg-gradient-to-r from-transparent via-white/40 to-transparent" })
1888
+ ]
1889
+ }
1890
+ );
1891
+ };
1892
+ var summarize_button_default = SummarizeButton;
1731
1893
  function ScriptBreakdownSceneView({
1732
1894
  blocks,
1733
1895
  characters,
@@ -1973,7 +2135,7 @@ function ScriptBreakdownSceneView({
1973
2135
  value: sceneBrief,
1974
2136
  onChange: (e) => setSceneBrief(e.target.value),
1975
2137
  placeholder: "Write a brief description or notes for this scene...",
1976
- className: "w-full min-h-[120px] bg-transparent outline-none resize-y text-zinc-700 placeholder:text-zinc-400 text-sm md:text-base custom-scrollbar font-sans",
2138
+ className: "w-full min-h-[120px] bg-transparent outline-none resize-y text-zinc-700 placeholder:text-zinc-400 text-sm md:text-base custom-scrollbar font-sans select-none",
1977
2139
  style: {
1978
2140
  lineHeight: "1.6"
1979
2141
  }
@@ -1981,27 +2143,15 @@ function ScriptBreakdownSceneView({
1981
2143
  ) })
1982
2144
  ] })
1983
2145
  ] }),
1984
- /* @__PURE__ */ jsxs("div", { className: "w-full lg:w-80 flex-shrink-0 flex flex-col gap-6 sticky top-6", children: [
1985
- /* @__PURE__ */ jsxs(
1986
- "button",
2146
+ /* @__PURE__ */ jsxs("div", { className: "w-full flex flex-col gap-6 sticky top-6", children: [
2147
+ /* @__PURE__ */ jsx(
2148
+ summarize_button_default,
1987
2149
  {
1988
- onClick: onSummarize,
1989
- disabled: isSummarizing,
1990
- 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]"}`,
1991
- children: [
1992
- /* @__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]" }) }),
1993
- /* @__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%)]" }),
1994
- /* @__PURE__ */ jsx("span", { className: "absolute inset-[1px] rounded-[inherit] bg-gradient-to-b from-white/90 to-transparent pointer-events-none" }),
1995
- /* @__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]" }) }),
1996
- /* @__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" }),
1997
- /* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center gap-3", children: [
1998
- 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" }),
1999
- /* @__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" })
2000
- ] })
2001
- ]
2150
+ isSummarizing,
2151
+ onSummarize
2002
2152
  }
2003
2153
  ),
2004
- /* @__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: [
2154
+ /* @__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 w-full", children: [
2005
2155
  /* @__PURE__ */ jsxs("h3", { className: "text-xs font-extrabold text-slate-800 uppercase tracking-[0.25em] mb-8 flex items-center gap-3", children: [
2006
2156
  /* @__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" }) }),
2007
2157
  "Tags",
@@ -2113,96 +2263,39 @@ function ScriptBreakdownSceneView({
2113
2263
  ] })
2114
2264
  ] }) });
2115
2265
  }
2116
- function useScriptBreakdown({
2117
- scenes
2118
- }) {
2119
- const [parsedScenes, setParsedScenes] = useState(scenes || []);
2120
- const [isLoading, setIsLoading] = useState(false);
2121
- const [error, setError] = useState(null);
2122
- const [hasFetchedFallback, setHasFetchedFallback] = useState(false);
2123
- useEffect(() => {
2124
- if (scenes && scenes.length > 0) {
2125
- setParsedScenes(scenes);
2126
- return;
2127
- }
2128
- if (hasFetchedFallback) return;
2129
- const fetchFallback = async () => {
2130
- setIsLoading(true);
2131
- setHasFetchedFallback(true);
2132
- try {
2133
- const response = await fetch(
2134
- "https://pub-4c2073ce6f434c4e92ed33f8e1c7f9ea.r2.dev/screenplay%20(1).sbx"
2135
- );
2136
- if (!response.ok) {
2137
- throw new Error(
2138
- `Failed to fetch fallback script: ${response.status}`
2139
- );
2140
- }
2141
- let text = await response.text();
2142
- if (text.includes("&lt;div")) {
2143
- const textarea = document.createElement("textarea");
2144
- textarea.innerHTML = text;
2145
- text = textarea.value;
2146
- }
2147
- const parser = new DOMParser();
2148
- const doc = parser.parseFromString(text, "text/html");
2149
- const divs = Array.from(doc.querySelectorAll("div"));
2150
- const extractedScenes = [];
2151
- let currentSceneNumber = "1";
2152
- let currentContent = "";
2153
- let hasSeenFirstSceneHeading = false;
2154
- divs.forEach((div) => {
2155
- var _a;
2156
- const isSceneHeading = div.classList.contains("divtype0");
2157
- const divText = ((_a = div.textContent) == null ? void 0 : _a.trim()) || "";
2158
- if (!divText) return;
2159
- if (isSceneHeading) {
2160
- if (hasSeenFirstSceneHeading) {
2161
- extractedScenes.push({
2162
- scene_number: currentSceneNumber,
2163
- content: currentContent.trim()
2164
- });
2165
- currentContent = "";
2166
- }
2167
- hasSeenFirstSceneHeading = true;
2168
- currentSceneNumber = div.getAttribute("data-scene") || String(extractedScenes.length + 1);
2169
- }
2170
- currentContent += div.outerHTML + "\n";
2171
- });
2172
- if (currentContent.trim()) {
2173
- extractedScenes.push({
2174
- scene_number: currentSceneNumber,
2175
- content: currentContent.trim()
2176
- });
2177
- }
2178
- setParsedScenes(extractedScenes);
2179
- } catch (err) {
2180
- console.error("Error fetching fallback script:", err);
2181
- setError(err instanceof Error ? err : new Error("Unknown error"));
2182
- } finally {
2183
- setIsLoading(false);
2184
- }
2185
- };
2186
- fetchFallback();
2187
- }, [scenes, hasFetchedFallback]);
2188
- return { scenes: parsedScenes, isLoading, error };
2189
- }
2190
- var use_script_breakdown_default = useScriptBreakdown;
2191
-
2192
- // app/hook/use-script-breakdown-scene.ts
2193
- function useScriptBreakdownScene(sceneNumber) {
2194
- const { scenes, isLoading, error } = use_script_breakdown_default({ scenes: [] });
2266
+ function useScriptBreakdownScene(scene_url, fetchOptions, onAISummarize, onTagAdded, onTagRemoved) {
2195
2267
  const [tags, setTags] = useState([]);
2196
2268
  const [selectionMenu, setSelectionMenu] = useState(null);
2197
2269
  const autoTaggedSceneRef = useRef(null);
2270
+ const [scene, setScene] = useState(null);
2198
2271
  const [menuPlacement, setMenuPlacement] = useState("top");
2199
2272
  const [subLocations, setSubLocations] = useState([]);
2200
2273
  const [sceneBrief, setSceneBrief] = useState("");
2201
2274
  const [isSummarizing, setIsSummarizing] = useState(false);
2275
+ const [isLoading, setIsLoading] = useState(true);
2276
+ const [error, setError] = useState(false);
2202
2277
  const menuRef = useRef(null);
2203
- const scene = useMemo(() => {
2204
- return scenes.find((s) => s.scene_number === sceneNumber);
2205
- }, [scenes, sceneNumber]);
2278
+ useEffect(() => {
2279
+ setIsLoading(true);
2280
+ const fetchScene = async () => {
2281
+ try {
2282
+ const response = await fetch(scene_url, fetchOptions);
2283
+ if (response.ok) {
2284
+ const text = await response.text();
2285
+ setScene({ content: text });
2286
+ } else {
2287
+ console.error("Failed to fetch scene:", response);
2288
+ setError(true);
2289
+ }
2290
+ setIsLoading(false);
2291
+ } catch (error2) {
2292
+ setError(true);
2293
+ setIsLoading(false);
2294
+ console.error("Error fetching scene:", error2);
2295
+ }
2296
+ };
2297
+ fetchScene();
2298
+ }, []);
2206
2299
  const blocks = useMemo(() => {
2207
2300
  if (!scene || !scene.content) return [];
2208
2301
  const parser = new DOMParser();
@@ -2244,24 +2337,15 @@ function useScriptBreakdownScene(sceneNumber) {
2244
2337
  }, [blocks]);
2245
2338
  const handleAISummarize = async () => {
2246
2339
  setIsSummarizing(true);
2247
- const res = await fetch(
2248
- "https://nonfibrous-extrafloral-verlene.ngrok-free.dev/extract",
2249
- {
2250
- method: "POST",
2251
- headers: {
2252
- "Content-Type": "application/json",
2253
- "ngrok-skip-browser-warning": "true"
2254
- },
2255
- body: JSON.stringify({
2256
- script: (scene == null ? void 0 : scene.content) || ""
2257
- })
2258
- }
2259
- );
2340
+ const res = await (onAISummarize == null ? void 0 : onAISummarize(scene.content));
2260
2341
  if (res.ok) {
2261
2342
  const data = await res.json();
2262
2343
  setIsSummarizing(false);
2263
- const parsedData = JSON.parse(data.data) || [];
2264
- console.log("AI Tags:", parsedData);
2344
+ const normalData = JSON.parse(data.data);
2345
+ const parsedData = Array.isArray(normalData) ? normalData[0] : [];
2346
+ const summary = Array.isArray(normalData) ? normalData[1] : "";
2347
+ const parsedSummaryData = summary[0];
2348
+ setSceneBrief(parsedSummaryData.summarise);
2265
2349
  const newTags = [];
2266
2350
  parsedData.forEach((aiTag) => {
2267
2351
  if (!aiTag.block_id || !aiTag.category_id || typeof aiTag.start_index !== "number" || typeof aiTag.end_index !== "number") {
@@ -2310,9 +2394,9 @@ function useScriptBreakdownScene(sceneNumber) {
2310
2394
  setSubLocations([]);
2311
2395
  setSceneBrief("");
2312
2396
  autoTaggedSceneRef.current = null;
2313
- }, [sceneNumber]);
2397
+ }, [scene_url]);
2314
2398
  useEffect(() => {
2315
- if (blocks.length > 0 && characters.length > 0 && autoTaggedSceneRef.current !== sceneNumber) {
2399
+ if (blocks.length > 0 && characters.length > 0 && autoTaggedSceneRef.current !== scene) {
2316
2400
  const autoTags = [];
2317
2401
  const escapeRegExp = (string) => string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2318
2402
  const sortedChars = [...characters].sort((a, b) => b.length - a.length);
@@ -2342,9 +2426,9 @@ function useScriptBreakdownScene(sceneNumber) {
2342
2426
  });
2343
2427
  });
2344
2428
  setTags(autoTags);
2345
- autoTaggedSceneRef.current = sceneNumber;
2429
+ autoTaggedSceneRef.current = scene;
2346
2430
  }
2347
- }, [blocks, characters, sceneNumber]);
2431
+ }, [blocks, characters, scene]);
2348
2432
  const clearSelection = useCallback(() => {
2349
2433
  var _a;
2350
2434
  setSelectionMenu(null);
@@ -2431,7 +2515,7 @@ function useScriptBreakdownScene(sceneNumber) {
2431
2515
  });
2432
2516
  }
2433
2517
  };
2434
- const addTag = (categoryId) => {
2518
+ const addTag = async (categoryId) => {
2435
2519
  if (!selectionMenu) return;
2436
2520
  const newTag = {
2437
2521
  id: uuid(),
@@ -2448,12 +2532,26 @@ function useScriptBreakdownScene(sceneNumber) {
2448
2532
  return [...filtered, newTag];
2449
2533
  });
2450
2534
  clearSelection();
2535
+ try {
2536
+ await (onTagAdded == null ? void 0 : onTagAdded(newTag));
2537
+ } catch (error2) {
2538
+ console.error("Failed to add tag:", error2);
2539
+ setTags((prev) => prev.filter((t) => t.id !== newTag.id));
2540
+ }
2451
2541
  };
2452
- const removeTag = (e, id) => {
2542
+ const removeTag = async (e, id) => {
2453
2543
  e.stopPropagation();
2454
2544
  e.preventDefault();
2545
+ const tagToRemove = tags.find((t) => t.id === id);
2546
+ if (!tagToRemove) return;
2455
2547
  setTags((prev) => prev.filter((t) => t.id !== id));
2456
2548
  clearSelection();
2549
+ try {
2550
+ await (onTagRemoved == null ? void 0 : onTagRemoved(id));
2551
+ } catch (error2) {
2552
+ console.error("Failed to remove tag:", error2);
2553
+ setTags((prev) => [...prev, tagToRemove]);
2554
+ }
2457
2555
  };
2458
2556
  return {
2459
2557
  scene,