@vishu1301/script-writing 1.1.5 → 1.1.7

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 CHANGED
@@ -1755,6 +1755,166 @@ var CATEGORIES = [
1755
1755
  { id: "LOCATION", label: "Location", color: "#FFB300", hex: "#FFE082" },
1756
1756
  { id: "OTHER", label: "Other", color: "#9E9E9E", hex: "#E0E0E0" }
1757
1757
  ];
1758
+ var PopcornIcon = ({ isSummarizing }) => /* @__PURE__ */ jsxRuntime.jsxs(
1759
+ "svg",
1760
+ {
1761
+ xmlns: "http://www.w3.org/2000/svg",
1762
+ width: "24",
1763
+ height: "24",
1764
+ viewBox: "0 0 24 24",
1765
+ fill: "none",
1766
+ strokeLinecap: "round",
1767
+ strokeLinejoin: "round",
1768
+ className: `w-6 h-6 transition-all duration-500 ${isSummarizing ? "animate-cook" : "group-hover:scale-110 group-hover:-rotate-12"}`,
1769
+ children: [
1770
+ /* @__PURE__ */ jsxRuntime.jsx(
1771
+ "path",
1772
+ {
1773
+ 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",
1774
+ className: "stroke-yellow-400",
1775
+ strokeWidth: "2"
1776
+ }
1777
+ ),
1778
+ /* @__PURE__ */ jsxRuntime.jsxs("g", { className: "stroke-red-500", strokeWidth: "2", children: [
1779
+ /* @__PURE__ */ jsxRuntime.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" }),
1780
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M10 22 9 8", className: "stroke-red-500", strokeWidth: "1" }),
1781
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m14 22 1-14", className: "stroke-red-500", strokeWidth: "1" })
1782
+ ] })
1783
+ ]
1784
+ }
1785
+ );
1786
+ var SummarizeButton = ({
1787
+ onSummarize,
1788
+ isSummarizing
1789
+ }) => {
1790
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1791
+ "button",
1792
+ {
1793
+ onClick: onSummarize,
1794
+ disabled: isSummarizing,
1795
+ 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]"}`,
1796
+ children: [
1797
+ /* @__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_0deg_at_50%_50%,#c084fc,transparent_60%,#fb7185,transparent_90%)] blur-3xl opacity-20 animate-[spin_10s_linear_infinite]" }) }),
1798
+ /* @__PURE__ */ jsxRuntime.jsx(
1799
+ "span",
1800
+ {
1801
+ 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"}`
1802
+ }
1803
+ ),
1804
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex items-center justify-center gap-4", children: [
1805
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
1806
+ @keyframes kernel-pop {
1807
+ 0% { transform: translate(0, 0) scale(0) rotate(0deg); opacity: 0; }
1808
+ 10% { opacity: 1; transform: translate(var(--tx1), var(--ty1)) scale(1.3) rotate(45deg); }
1809
+ 40% { transform: translate(var(--tx2), var(--ty2)) scale(1) rotate(180deg); opacity: 1; }
1810
+ 100% { transform: translate(var(--tx3), var(--ty3)) scale(0.4) rotate(360deg); opacity: 0; }
1811
+ }
1812
+ @keyframes icon-cook {
1813
+ 0%, 100% { transform: scale(1.1) translateY(0); }
1814
+ 50% { transform: scale(0.9, 1.1) translateY(-3px) rotate(3deg); }
1815
+ }
1816
+ .animate-kernel {
1817
+ animation: kernel-pop var(--dur) cubic-bezier(0.34, 1.56, 0.64, 1) infinite;
1818
+ animation-delay: var(--delay);
1819
+ filter: drop-shadow(0 4px 6px rgba(0,0,0,0.15)); /* Added shadow for visibility */
1820
+ }
1821
+ .animate-cook { animation: icon-cook 0.3s ease-in-out infinite; }
1822
+ ` }),
1823
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex items-center justify-center", children: [
1824
+ /* @__PURE__ */ jsxRuntime.jsx(PopcornIcon, { isSummarizing }),
1825
+ isSummarizing && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute inset-0 pointer-events-none", children: [
1826
+ /* @__PURE__ */ jsxRuntime.jsx(
1827
+ "svg",
1828
+ {
1829
+ className: "animate-kernel absolute w-4 h-4",
1830
+ style: {
1831
+ "--dur": "1.2s",
1832
+ "--delay": "0s",
1833
+ "--tx1": "-18px",
1834
+ "--ty1": "-22px",
1835
+ "--tx2": "-28px",
1836
+ "--ty2": "-28px",
1837
+ "--tx3": "-38px",
1838
+ "--ty3": "12px"
1839
+ },
1840
+ viewBox: "0 0 24 24",
1841
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1842
+ "path",
1843
+ {
1844
+ 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",
1845
+ fill: "#FBBF24",
1846
+ stroke: "#F59E0B",
1847
+ strokeWidth: "0.5"
1848
+ }
1849
+ )
1850
+ }
1851
+ ),
1852
+ /* @__PURE__ */ jsxRuntime.jsx(
1853
+ "svg",
1854
+ {
1855
+ className: "animate-kernel absolute w-3.5 h-3.5",
1856
+ style: {
1857
+ "--dur": "1.5s",
1858
+ "--delay": "0.3s",
1859
+ "--tx1": "18px",
1860
+ "--ty1": "-28px",
1861
+ "--tx2": "28px",
1862
+ "--ty2": "-38px",
1863
+ "--tx3": "38px",
1864
+ "--ty3": "18px"
1865
+ },
1866
+ viewBox: "0 0 24 24",
1867
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1868
+ "path",
1869
+ {
1870
+ 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",
1871
+ fill: "#FDE68A",
1872
+ stroke: "#FBBF24",
1873
+ strokeWidth: "0.5"
1874
+ }
1875
+ )
1876
+ }
1877
+ ),
1878
+ /* @__PURE__ */ jsxRuntime.jsx(
1879
+ "svg",
1880
+ {
1881
+ className: "animate-kernel absolute w-4 h-4",
1882
+ style: {
1883
+ "--dur": "1.8s",
1884
+ "--delay": "0.7s",
1885
+ "--tx1": "-2px",
1886
+ "--ty1": "-28px",
1887
+ "--tx2": "5px",
1888
+ "--ty2": "-48px",
1889
+ "--tx3": "10px",
1890
+ "--ty3": "18px"
1891
+ },
1892
+ viewBox: "0 0 24 24",
1893
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1894
+ "path",
1895
+ {
1896
+ 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",
1897
+ fill: "#FEF3C7"
1898
+ }
1899
+ )
1900
+ }
1901
+ )
1902
+ ] })
1903
+ ] }),
1904
+ /* @__PURE__ */ jsxRuntime.jsx(
1905
+ "span",
1906
+ {
1907
+ 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"}`,
1908
+ children: isSummarizing ? "Summarizing..." : "Summarize Scene"
1909
+ }
1910
+ )
1911
+ ] }),
1912
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute bottom-0 left-0 right-0 h-[1px] bg-gradient-to-r from-transparent via-white/40 to-transparent" })
1913
+ ]
1914
+ }
1915
+ );
1916
+ };
1917
+ var summarize_button_default = SummarizeButton;
1758
1918
  function ScriptBreakdownSceneView({
1759
1919
  blocks,
1760
1920
  characters,
@@ -2000,7 +2160,7 @@ function ScriptBreakdownSceneView({
2000
2160
  value: sceneBrief,
2001
2161
  onChange: (e) => setSceneBrief(e.target.value),
2002
2162
  placeholder: "Write a brief description or notes for this scene...",
2003
- 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",
2163
+ 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",
2004
2164
  style: {
2005
2165
  lineHeight: "1.6"
2006
2166
  }
@@ -2008,27 +2168,15 @@ function ScriptBreakdownSceneView({
2008
2168
  ) })
2009
2169
  ] })
2010
2170
  ] }),
2011
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full lg:w-80 flex-shrink-0 flex flex-col gap-6 sticky top-6", children: [
2012
- /* @__PURE__ */ jsxRuntime.jsxs(
2013
- "button",
2171
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full flex flex-col gap-6 sticky top-6", children: [
2172
+ /* @__PURE__ */ jsxRuntime.jsx(
2173
+ summarize_button_default,
2014
2174
  {
2015
- onClick: onSummarize,
2016
- disabled: isSummarizing,
2017
- 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]"}`,
2018
- children: [
2019
- /* @__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]" }) }),
2020
- /* @__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%)]" }),
2021
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inset-[1px] rounded-[inherit] bg-gradient-to-b from-white/90 to-transparent pointer-events-none" }),
2022
- /* @__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]" }) }),
2023
- /* @__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" }),
2024
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex items-center justify-center gap-3", children: [
2025
- 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" }),
2026
- /* @__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" })
2027
- ] })
2028
- ]
2175
+ isSummarizing,
2176
+ onSummarize
2029
2177
  }
2030
2178
  ),
2031
- /* @__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: [
2179
+ /* @__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 w-full", children: [
2032
2180
  /* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "text-xs font-extrabold text-slate-800 uppercase tracking-[0.25em] mb-8 flex items-center gap-3", children: [
2033
2181
  /* @__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" }) }),
2034
2182
  "Tags",
@@ -2140,96 +2288,39 @@ function ScriptBreakdownSceneView({
2140
2288
  ] })
2141
2289
  ] }) });
2142
2290
  }
2143
- function useScriptBreakdown({
2144
- scenes
2145
- }) {
2146
- const [parsedScenes, setParsedScenes] = react.useState(scenes || []);
2147
- const [isLoading, setIsLoading] = react.useState(false);
2148
- const [error, setError] = react.useState(null);
2149
- const [hasFetchedFallback, setHasFetchedFallback] = react.useState(false);
2150
- react.useEffect(() => {
2151
- if (scenes && scenes.length > 0) {
2152
- setParsedScenes(scenes);
2153
- return;
2154
- }
2155
- if (hasFetchedFallback) return;
2156
- const fetchFallback = async () => {
2157
- setIsLoading(true);
2158
- setHasFetchedFallback(true);
2159
- try {
2160
- const response = await fetch(
2161
- "https://pub-4c2073ce6f434c4e92ed33f8e1c7f9ea.r2.dev/screenplay%20(1).sbx"
2162
- );
2163
- if (!response.ok) {
2164
- throw new Error(
2165
- `Failed to fetch fallback script: ${response.status}`
2166
- );
2167
- }
2168
- let text = await response.text();
2169
- if (text.includes("<div")) {
2170
- const textarea = document.createElement("textarea");
2171
- textarea.innerHTML = text;
2172
- text = textarea.value;
2173
- }
2174
- const parser = new DOMParser();
2175
- const doc = parser.parseFromString(text, "text/html");
2176
- const divs = Array.from(doc.querySelectorAll("div"));
2177
- const extractedScenes = [];
2178
- let currentSceneNumber = "1";
2179
- let currentContent = "";
2180
- let hasSeenFirstSceneHeading = false;
2181
- divs.forEach((div) => {
2182
- var _a;
2183
- const isSceneHeading = div.classList.contains("divtype0");
2184
- const divText = ((_a = div.textContent) == null ? void 0 : _a.trim()) || "";
2185
- if (!divText) return;
2186
- if (isSceneHeading) {
2187
- if (hasSeenFirstSceneHeading) {
2188
- extractedScenes.push({
2189
- scene_number: currentSceneNumber,
2190
- content: currentContent.trim()
2191
- });
2192
- currentContent = "";
2193
- }
2194
- hasSeenFirstSceneHeading = true;
2195
- currentSceneNumber = div.getAttribute("data-scene") || String(extractedScenes.length + 1);
2196
- }
2197
- currentContent += div.outerHTML + "\n";
2198
- });
2199
- if (currentContent.trim()) {
2200
- extractedScenes.push({
2201
- scene_number: currentSceneNumber,
2202
- content: currentContent.trim()
2203
- });
2204
- }
2205
- setParsedScenes(extractedScenes);
2206
- } catch (err) {
2207
- console.error("Error fetching fallback script:", err);
2208
- setError(err instanceof Error ? err : new Error("Unknown error"));
2209
- } finally {
2210
- setIsLoading(false);
2211
- }
2212
- };
2213
- fetchFallback();
2214
- }, [scenes, hasFetchedFallback]);
2215
- return { scenes: parsedScenes, isLoading, error };
2216
- }
2217
- var use_script_breakdown_default = useScriptBreakdown;
2218
-
2219
- // app/hook/use-script-breakdown-scene.ts
2220
- function useScriptBreakdownScene(sceneNumber) {
2221
- const { scenes, isLoading, error } = use_script_breakdown_default({ scenes: [] });
2222
- const [tags, setTags] = react.useState([]);
2291
+ function useScriptBreakdownScene(scene_url, fetchOptions, onAISummarize, onTagAdded, onTagRemoved, preLoadedTags) {
2292
+ const [tags, setTags] = react.useState(preLoadedTags || []);
2223
2293
  const [selectionMenu, setSelectionMenu] = react.useState(null);
2224
2294
  const autoTaggedSceneRef = react.useRef(null);
2295
+ const [scene, setScene] = react.useState(null);
2225
2296
  const [menuPlacement, setMenuPlacement] = react.useState("top");
2226
2297
  const [subLocations, setSubLocations] = react.useState([]);
2227
2298
  const [sceneBrief, setSceneBrief] = react.useState("");
2228
2299
  const [isSummarizing, setIsSummarizing] = react.useState(false);
2300
+ const [isLoading, setIsLoading] = react.useState(true);
2301
+ const [error, setError] = react.useState(false);
2229
2302
  const menuRef = react.useRef(null);
2230
- const scene = react.useMemo(() => {
2231
- return scenes.find((s) => s.scene_number === sceneNumber);
2232
- }, [scenes, sceneNumber]);
2303
+ react.useEffect(() => {
2304
+ setIsLoading(true);
2305
+ const fetchScene = async () => {
2306
+ try {
2307
+ const response = await fetch(scene_url, fetchOptions);
2308
+ if (response.ok) {
2309
+ const text = await response.text();
2310
+ setScene({ content: text });
2311
+ } else {
2312
+ console.error("Failed to fetch scene:", response);
2313
+ setError(true);
2314
+ }
2315
+ setIsLoading(false);
2316
+ } catch (error2) {
2317
+ setError(true);
2318
+ setIsLoading(false);
2319
+ console.error("Error fetching scene:", error2);
2320
+ }
2321
+ };
2322
+ fetchScene();
2323
+ }, []);
2233
2324
  const blocks = react.useMemo(() => {
2234
2325
  if (!scene || !scene.content) return [];
2235
2326
  const parser = new DOMParser();
@@ -2271,24 +2362,15 @@ function useScriptBreakdownScene(sceneNumber) {
2271
2362
  }, [blocks]);
2272
2363
  const handleAISummarize = async () => {
2273
2364
  setIsSummarizing(true);
2274
- const res = await fetch(
2275
- "https://nonfibrous-extrafloral-verlene.ngrok-free.dev/extract",
2276
- {
2277
- method: "POST",
2278
- headers: {
2279
- "Content-Type": "application/json",
2280
- "ngrok-skip-browser-warning": "true"
2281
- },
2282
- body: JSON.stringify({
2283
- script: (scene == null ? void 0 : scene.content) || ""
2284
- })
2285
- }
2286
- );
2365
+ const res = await (onAISummarize == null ? void 0 : onAISummarize(scene.content));
2287
2366
  if (res.ok) {
2288
2367
  const data = await res.json();
2289
2368
  setIsSummarizing(false);
2290
- const parsedData = JSON.parse(data.data) || [];
2291
- console.log("AI Tags:", parsedData);
2369
+ const normalData = JSON.parse(data.data);
2370
+ const parsedData = Array.isArray(normalData) ? normalData[0] : [];
2371
+ const summary = Array.isArray(normalData) ? normalData[1] : "";
2372
+ const parsedSummaryData = summary[0];
2373
+ setSceneBrief(parsedSummaryData.summarise);
2292
2374
  const newTags = [];
2293
2375
  parsedData.forEach((aiTag) => {
2294
2376
  if (!aiTag.block_id || !aiTag.category_id || typeof aiTag.start_index !== "number" || typeof aiTag.end_index !== "number") {
@@ -2337,9 +2419,9 @@ function useScriptBreakdownScene(sceneNumber) {
2337
2419
  setSubLocations([]);
2338
2420
  setSceneBrief("");
2339
2421
  autoTaggedSceneRef.current = null;
2340
- }, [sceneNumber]);
2422
+ }, [scene_url]);
2341
2423
  react.useEffect(() => {
2342
- if (blocks.length > 0 && characters.length > 0 && autoTaggedSceneRef.current !== sceneNumber) {
2424
+ if (blocks.length > 0 && characters.length > 0 && autoTaggedSceneRef.current !== scene) {
2343
2425
  const autoTags = [];
2344
2426
  const escapeRegExp = (string) => string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2345
2427
  const sortedChars = [...characters].sort((a, b) => b.length - a.length);
@@ -2369,9 +2451,9 @@ function useScriptBreakdownScene(sceneNumber) {
2369
2451
  });
2370
2452
  });
2371
2453
  setTags(autoTags);
2372
- autoTaggedSceneRef.current = sceneNumber;
2454
+ autoTaggedSceneRef.current = scene;
2373
2455
  }
2374
- }, [blocks, characters, sceneNumber]);
2456
+ }, [blocks, characters, scene]);
2375
2457
  const clearSelection = react.useCallback(() => {
2376
2458
  var _a;
2377
2459
  setSelectionMenu(null);
@@ -2458,7 +2540,7 @@ function useScriptBreakdownScene(sceneNumber) {
2458
2540
  });
2459
2541
  }
2460
2542
  };
2461
- const addTag = (categoryId) => {
2543
+ const addTag = async (categoryId) => {
2462
2544
  if (!selectionMenu) return;
2463
2545
  const newTag = {
2464
2546
  id: uuid(),
@@ -2475,12 +2557,26 @@ function useScriptBreakdownScene(sceneNumber) {
2475
2557
  return [...filtered, newTag];
2476
2558
  });
2477
2559
  clearSelection();
2560
+ try {
2561
+ await (onTagAdded == null ? void 0 : onTagAdded(newTag));
2562
+ } catch (error2) {
2563
+ console.error("Failed to add tag:", error2);
2564
+ setTags((prev) => prev.filter((t) => t.id !== newTag.id));
2565
+ }
2478
2566
  };
2479
- const removeTag = (e, id) => {
2567
+ const removeTag = async (e, id) => {
2480
2568
  e.stopPropagation();
2481
2569
  e.preventDefault();
2570
+ const tagToRemove = tags.find((t) => t.id === id);
2571
+ if (!tagToRemove) return;
2482
2572
  setTags((prev) => prev.filter((t) => t.id !== id));
2483
2573
  clearSelection();
2574
+ try {
2575
+ await (onTagRemoved == null ? void 0 : onTagRemoved(id));
2576
+ } catch (error2) {
2577
+ console.error("Failed to remove tag:", error2);
2578
+ setTags((prev) => [...prev, tagToRemove]);
2579
+ }
2484
2580
  };
2485
2581
  return {
2486
2582
  scene,