@vishu1301/script-writing 1.2.2 → 1.2.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.d.cts CHANGED
@@ -72,7 +72,7 @@ interface ScriptBreakdown {
72
72
  scene_number: string;
73
73
  content: string;
74
74
  }
75
- type ElementCategory = "CAST" | "PROP" | "COSTUME" | "VEHICLE" | "SET_PROP" | "EXTRA" | "LOCATION" | "OTHER";
75
+ type ElementCategory = "CAST" | "PROP" | "COSTUME" | "VEHICLE" | "SET_PROP" | "EXTRA" | "LOCATION" | "SUBLOCATION" | "OTHER";
76
76
  interface Tag {
77
77
  id?: string;
78
78
  scene_id?: string | number;
@@ -91,7 +91,7 @@ declare const CATEGORIES: {
91
91
  hex: string;
92
92
  }[];
93
93
 
94
- declare function ScriptBreakdownSceneView({ blocks, characters, isLoading, sceneNumber, tags, selectionMenu, handleMouseUp, addTag, removeTag, clearSelection, menuPlacement, menuRef, subLocations, addSubLocation, removeSubLocation, sceneBrief, setSceneBrief, onSummarize, isSummarizing, }: {
94
+ declare function ScriptBreakdownSceneView({ blocks, characters, isLoading, sceneNumber, tags, selectionMenu, handleMouseUp, addTag, updateTag, removeTag, clearSelection, menuPlacement, menuRef, sceneBrief, setSceneBrief, onSummarize, isSummarizing, }: {
95
95
  blocks: Block[];
96
96
  characters: string[];
97
97
  isLoading: boolean;
@@ -107,13 +107,11 @@ declare function ScriptBreakdownSceneView({ blocks, characters, isLoading, scene
107
107
  } | null;
108
108
  handleMouseUp: () => void;
109
109
  addTag: (c: ElementCategory) => void;
110
+ updateTag?: (id: string, categoryId: ElementCategory) => void;
110
111
  removeTag: (e: React__default.MouseEvent, id?: string) => void;
111
112
  clearSelection: () => void;
112
113
  menuPlacement: "top" | "bottom";
113
114
  menuRef: React__default.RefObject<HTMLDivElement | null>;
114
- subLocations: string[];
115
- addSubLocation: (loc: string) => void;
116
- removeSubLocation: (loc: string) => void;
117
115
  sceneBrief: string;
118
116
  setSceneBrief: (brief: string) => void;
119
117
  onSummarize?: () => void;
@@ -125,7 +123,9 @@ interface UseScriptBreakdownSceneOptions {
125
123
  fetchOptions?: RequestInit;
126
124
  onAISummarize?: (scene: any) => void;
127
125
  onTagAdded?: (tag: Tag) => void;
126
+ onTagsBulkAdded?: (tags: Tag[], summary?: string) => Promise<void>;
128
127
  onTagRemoved?: (tagId: string) => void;
128
+ onTagUpdated?: (tagId: string, categoryId: ElementCategory) => void;
129
129
  preLoadedTags?: Tag[];
130
130
  }
131
131
  declare function useScriptBreakdownScene(options: UseScriptBreakdownSceneOptions): {
@@ -145,13 +145,11 @@ declare function useScriptBreakdownScene(options: UseScriptBreakdownSceneOptions
145
145
  } | null;
146
146
  handleMouseUp: () => void;
147
147
  addTag: (categoryId: ElementCategory) => Promise<void>;
148
+ updateTag: (id: string, categoryId: ElementCategory) => Promise<void>;
148
149
  removeTag: (e: React.MouseEvent, id: string) => Promise<void>;
149
150
  clearSelection: () => void;
150
151
  menuPlacement: "bottom" | "top";
151
152
  menuRef: React$1.RefObject<HTMLDivElement | null>;
152
- subLocations: string[];
153
- addSubLocation: (subLocation: string) => void;
154
- removeSubLocation: (subLocation: string) => void;
155
153
  sceneBrief: string;
156
154
  setSceneBrief: React$1.Dispatch<React$1.SetStateAction<string>>;
157
155
  handleAISummarize: () => Promise<any>;
package/dist/index.d.ts CHANGED
@@ -72,7 +72,7 @@ interface ScriptBreakdown {
72
72
  scene_number: string;
73
73
  content: string;
74
74
  }
75
- type ElementCategory = "CAST" | "PROP" | "COSTUME" | "VEHICLE" | "SET_PROP" | "EXTRA" | "LOCATION" | "OTHER";
75
+ type ElementCategory = "CAST" | "PROP" | "COSTUME" | "VEHICLE" | "SET_PROP" | "EXTRA" | "LOCATION" | "SUBLOCATION" | "OTHER";
76
76
  interface Tag {
77
77
  id?: string;
78
78
  scene_id?: string | number;
@@ -91,7 +91,7 @@ declare const CATEGORIES: {
91
91
  hex: string;
92
92
  }[];
93
93
 
94
- declare function ScriptBreakdownSceneView({ blocks, characters, isLoading, sceneNumber, tags, selectionMenu, handleMouseUp, addTag, removeTag, clearSelection, menuPlacement, menuRef, subLocations, addSubLocation, removeSubLocation, sceneBrief, setSceneBrief, onSummarize, isSummarizing, }: {
94
+ declare function ScriptBreakdownSceneView({ blocks, characters, isLoading, sceneNumber, tags, selectionMenu, handleMouseUp, addTag, updateTag, removeTag, clearSelection, menuPlacement, menuRef, sceneBrief, setSceneBrief, onSummarize, isSummarizing, }: {
95
95
  blocks: Block[];
96
96
  characters: string[];
97
97
  isLoading: boolean;
@@ -107,13 +107,11 @@ declare function ScriptBreakdownSceneView({ blocks, characters, isLoading, scene
107
107
  } | null;
108
108
  handleMouseUp: () => void;
109
109
  addTag: (c: ElementCategory) => void;
110
+ updateTag?: (id: string, categoryId: ElementCategory) => void;
110
111
  removeTag: (e: React__default.MouseEvent, id?: string) => void;
111
112
  clearSelection: () => void;
112
113
  menuPlacement: "top" | "bottom";
113
114
  menuRef: React__default.RefObject<HTMLDivElement | null>;
114
- subLocations: string[];
115
- addSubLocation: (loc: string) => void;
116
- removeSubLocation: (loc: string) => void;
117
115
  sceneBrief: string;
118
116
  setSceneBrief: (brief: string) => void;
119
117
  onSummarize?: () => void;
@@ -125,7 +123,9 @@ interface UseScriptBreakdownSceneOptions {
125
123
  fetchOptions?: RequestInit;
126
124
  onAISummarize?: (scene: any) => void;
127
125
  onTagAdded?: (tag: Tag) => void;
126
+ onTagsBulkAdded?: (tags: Tag[], summary?: string) => Promise<void>;
128
127
  onTagRemoved?: (tagId: string) => void;
128
+ onTagUpdated?: (tagId: string, categoryId: ElementCategory) => void;
129
129
  preLoadedTags?: Tag[];
130
130
  }
131
131
  declare function useScriptBreakdownScene(options: UseScriptBreakdownSceneOptions): {
@@ -145,13 +145,11 @@ declare function useScriptBreakdownScene(options: UseScriptBreakdownSceneOptions
145
145
  } | null;
146
146
  handleMouseUp: () => void;
147
147
  addTag: (categoryId: ElementCategory) => Promise<void>;
148
+ updateTag: (id: string, categoryId: ElementCategory) => Promise<void>;
148
149
  removeTag: (e: React.MouseEvent, id: string) => Promise<void>;
149
150
  clearSelection: () => void;
150
151
  menuPlacement: "bottom" | "top";
151
152
  menuRef: React$1.RefObject<HTMLDivElement | null>;
152
- subLocations: string[];
153
- addSubLocation: (subLocation: string) => void;
154
- removeSubLocation: (subLocation: string) => void;
155
153
  sceneBrief: string;
156
154
  setSceneBrief: React$1.Dispatch<React$1.SetStateAction<string>>;
157
155
  handleAISummarize: () => Promise<any>;
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, Lock, Unlock, 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 } 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';
@@ -1728,6 +1728,7 @@ var CATEGORIES = [
1728
1728
  { id: "SET_PROP", label: "Set Prop", color: "#00C853", hex: "#69F0AE" },
1729
1729
  { id: "EXTRA", label: "Extra", color: "#00B8D4", hex: "#62EFFF" },
1730
1730
  { id: "LOCATION", label: "Location", color: "#FFB300", hex: "#FFE082" },
1731
+ { id: "SUBLOCATION", label: "Sublocation", color: "#004CFF", hex: "#004CFF" },
1731
1732
  { id: "OTHER", label: "Other", color: "#9E9E9E", hex: "#E0E0E0" }
1732
1733
  ];
1733
1734
  var PopcornIcon = ({ isSummarizing }) => /* @__PURE__ */ jsxs(
@@ -1915,31 +1916,17 @@ function ScriptBreakdownSceneView({
1915
1916
  selectionMenu,
1916
1917
  handleMouseUp,
1917
1918
  addTag,
1919
+ updateTag,
1918
1920
  removeTag,
1919
1921
  clearSelection,
1920
1922
  menuPlacement,
1921
1923
  menuRef,
1922
- subLocations,
1923
- addSubLocation,
1924
- removeSubLocation,
1925
1924
  sceneBrief,
1926
1925
  setSceneBrief,
1927
1926
  onSummarize,
1928
1927
  isSummarizing
1929
1928
  }) {
1930
1929
  const COURIER_STACK = "'Courier Prime', 'Courier', monospace";
1931
- const [isSubLocOpen, setIsSubLocOpen] = useState(false);
1932
- const [subLocInput, setSubLocInput] = useState("");
1933
- const subLocPopoverRef = useRef(null);
1934
- useEffect(() => {
1935
- const handleClickOutside = (e) => {
1936
- if (isSubLocOpen && subLocPopoverRef.current && !subLocPopoverRef.current.contains(e.target)) {
1937
- setIsSubLocOpen(false);
1938
- }
1939
- };
1940
- document.addEventListener("mousedown", handleClickOutside);
1941
- return () => document.removeEventListener("mousedown", handleClickOutside);
1942
- }, [isSubLocOpen]);
1943
1930
  useEffect(() => {
1944
1931
  const fontId = "google-font-courier-prime";
1945
1932
  const styleId = "screenplay-editor-force-v4";
@@ -2083,11 +2070,20 @@ function ScriptBreakdownSceneView({
2083
2070
  ] }),
2084
2071
  /* @__PURE__ */ jsxs("div", { className: "relative z-10 flex flex-col gap-1", children: [
2085
2072
  CATEGORIES.filter(
2086
- (cat) => !(cat.id === "LOCATION" && hasLocationTag)
2073
+ (cat) => !(cat.id === "LOCATION" && hasLocationTag) && !(cat.id === "SUBLOCATION" && !hasLocationTag)
2087
2074
  ).map((cat) => /* @__PURE__ */ jsxs(
2088
2075
  "button",
2089
2076
  {
2090
- onClick: () => addTag(cat.id),
2077
+ onClick: () => {
2078
+ const existingTag = tags.find(
2079
+ (t) => t.block_id === block.id && t.start_index === selectionMenu.startIndex && t.end_index === selectionMenu.endIndex
2080
+ );
2081
+ if (existingTag && existingTag.id) {
2082
+ updateTag == null ? void 0 : updateTag(existingTag.id, cat.id);
2083
+ } else {
2084
+ addTag(cat.id);
2085
+ }
2086
+ },
2091
2087
  className: "group w-full text-[12px] font-bold px-3 py-2 rounded-xl transition-all duration-300 text-left flex items-center justify-between hover:bg-white/80 hover:shadow-[0_2px_10px_rgb(0,0,0,0.02)] active:scale-[0.98]",
2092
2088
  style: { color: cat.color },
2093
2089
  children: [
@@ -2174,6 +2170,7 @@ function ScriptBreakdownSceneView({
2174
2170
  /* @__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 })
2175
2171
  ] }),
2176
2172
  tags.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-8", children: CATEGORIES.map((cat) => {
2173
+ if (cat.id === "SUBLOCATION" && !hasLocationTag) return null;
2177
2174
  const catTags = Array.from(
2178
2175
  new Map(
2179
2176
  tags.filter((t) => t.category_id === cat.id).map((tag) => [tag.name.toLowerCase(), tag])
@@ -2191,88 +2188,15 @@ function ScriptBreakdownSceneView({
2191
2188
  ),
2192
2189
  cat.label
2193
2190
  ] }),
2194
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
2195
- catTags.map((tag, index) => /* @__PURE__ */ jsx(
2196
- "span",
2197
- {
2198
- 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",
2199
- style: { color: cat.color },
2200
- children: tag.name
2201
- },
2202
- index
2203
- )),
2204
- cat.id === "LOCATION" && /* @__PURE__ */ jsxs(
2205
- "div",
2206
- {
2207
- className: "relative flex items-center",
2208
- ref: subLocPopoverRef,
2209
- children: [
2210
- /* @__PURE__ */ jsx(
2211
- "button",
2212
- {
2213
- onClick: () => setIsSubLocOpen(!isSubLocOpen),
2214
- className: "flex items-center justify-center w-5 h-5 rounded-full hover:bg-slate-200 transition-colors",
2215
- title: "Add Sub Location",
2216
- children: /* @__PURE__ */ jsx(Plus, { className: "w-3 h-3 text-slate-500" })
2217
- }
2218
- ),
2219
- 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: [
2220
- /* @__PURE__ */ jsx("p", { className: "text-[9px] font-extrabold tracking-[0.2em] text-slate-400 uppercase mb-2 px-1", children: "Add Sub Location" }),
2221
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2 items-center", children: [
2222
- /* @__PURE__ */ jsx(
2223
- "input",
2224
- {
2225
- type: "text",
2226
- value: subLocInput,
2227
- onChange: (e) => setSubLocInput(e.target.value),
2228
- onKeyDown: (e) => {
2229
- if (e.key === "Enter") {
2230
- addSubLocation(subLocInput);
2231
- setSubLocInput("");
2232
- setIsSubLocOpen(false);
2233
- }
2234
- },
2235
- 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",
2236
- placeholder: "Sub location...",
2237
- autoFocus: true
2238
- }
2239
- ),
2240
- /* @__PURE__ */ jsx(
2241
- "button",
2242
- {
2243
- onClick: () => {
2244
- addSubLocation(subLocInput);
2245
- setSubLocInput("");
2246
- setIsSubLocOpen(false);
2247
- },
2248
- 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",
2249
- children: "Add"
2250
- }
2251
- )
2252
- ] })
2253
- ] })
2254
- ]
2255
- }
2256
- ),
2257
- cat.id === "LOCATION" && subLocations.map((subLoc) => /* @__PURE__ */ jsxs(
2258
- "span",
2259
- {
2260
- 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",
2261
- children: [
2262
- subLoc,
2263
- /* @__PURE__ */ jsx(
2264
- "button",
2265
- {
2266
- onClick: () => removeSubLocation(subLoc),
2267
- 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",
2268
- children: /* @__PURE__ */ jsx(X, { className: "w-2.5 h-2.5" })
2269
- }
2270
- )
2271
- ]
2272
- },
2273
- `sub-${subLoc}`
2274
- ))
2275
- ] })
2191
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: catTags.map((tag, index) => /* @__PURE__ */ jsx(
2192
+ "span",
2193
+ {
2194
+ 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",
2195
+ style: { color: cat.color },
2196
+ children: tag.name
2197
+ },
2198
+ index
2199
+ )) })
2276
2200
  ] }, cat.id);
2277
2201
  }) }) : /* @__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." })
2278
2202
  ] })
@@ -2285,7 +2209,6 @@ function useScriptBreakdownScene(options) {
2285
2209
  const autoTaggedSceneRef = useRef(null);
2286
2210
  const [scene, setScene] = useState(null);
2287
2211
  const [menuPlacement, setMenuPlacement] = useState("top");
2288
- const [subLocations, setSubLocations] = useState([]);
2289
2212
  const [sceneBrief, setSceneBrief] = useState("");
2290
2213
  const [isSummarizing, setIsSummarizing] = useState(false);
2291
2214
  const [isLoading, setIsLoading] = useState(true);
@@ -2385,9 +2308,122 @@ function useScriptBreakdownScene(options) {
2385
2308
  end_index: aiTag.end_index
2386
2309
  });
2387
2310
  });
2311
+ if (newTags.length > 0) {
2312
+ const originalTags = tags;
2313
+ setTags((prev) => {
2314
+ const merged = [...prev];
2315
+ newTags.forEach((newTag) => {
2316
+ const isOverlapping = merged.some(
2317
+ (t) => t.block_id === newTag.block_id && newTag.end_index > t.start_index && newTag.start_index < t.end_index
2318
+ );
2319
+ if (!isOverlapping) {
2320
+ merged.push(newTag);
2321
+ }
2322
+ });
2323
+ return merged;
2324
+ });
2325
+ try {
2326
+ if (options.onTagsBulkAdded) {
2327
+ await options.onTagsBulkAdded(newTags, parsedSummaryData.summarise);
2328
+ }
2329
+ } catch (error2) {
2330
+ console.error("Failed to bulk add AI-generated tags:", error2);
2331
+ setTags(originalTags);
2332
+ }
2333
+ }
2334
+ return data;
2335
+ } else {
2336
+ setIsSummarizing(false);
2337
+ console.error("Failed to summarize scene:", res);
2338
+ }
2339
+ };
2340
+ const bulkCreateTags = useCallback(async () => {
2341
+ if (blocks.length === 0) return;
2342
+ const newTags = [];
2343
+ const seenCharacters = /* @__PURE__ */ new Set();
2344
+ const timeOfDays = ["DAY", "NIGHT"];
2345
+ const isTimeOfDay = (str) => timeOfDays.includes(str.toUpperCase());
2346
+ blocks.forEach((block) => {
2347
+ if (block.type === "CHARACTER") {
2348
+ const text = block.text.trim();
2349
+ const parenIndex = text.indexOf("(");
2350
+ const charName = parenIndex > -1 ? text.substring(0, parenIndex).trim() : text;
2351
+ if (charName && !seenCharacters.has(charName.toUpperCase())) {
2352
+ seenCharacters.add(charName.toUpperCase());
2353
+ const startIndex = text.indexOf(charName);
2354
+ if (startIndex !== -1) {
2355
+ newTags.push({
2356
+ id: uuid(),
2357
+ block_id: block.id,
2358
+ category_id: "CAST",
2359
+ name: charName,
2360
+ start_index: startIndex,
2361
+ end_index: startIndex + charName.length
2362
+ });
2363
+ }
2364
+ }
2365
+ } else if (block.type === "SCENE_HEADING") {
2366
+ const text = block.text.trim();
2367
+ const typeMatch = text.match(
2368
+ /^(INT\/EXT|INT\.?\/EXT\.?|INT\.EXT\.?|INT|EXT|I\/E)\.?\s+/i
2369
+ );
2370
+ let remainingText = text;
2371
+ let offset = 0;
2372
+ if (typeMatch) {
2373
+ offset = typeMatch[0].length;
2374
+ remainingText = text.substring(offset);
2375
+ }
2376
+ const parts = remainingText.split(/\s+-\s+/);
2377
+ if (parts.length > 0) {
2378
+ const locationName = parts[0].trim();
2379
+ const locStart = text.indexOf(locationName, offset);
2380
+ if (locStart !== -1 && locationName) {
2381
+ newTags.push({
2382
+ id: uuid(),
2383
+ block_id: block.id,
2384
+ category_id: "LOCATION",
2385
+ name: locationName,
2386
+ start_index: locStart,
2387
+ end_index: locStart + locationName.length
2388
+ });
2389
+ }
2390
+ if (parts.length > 1) {
2391
+ const secondPart = parts[1].trim();
2392
+ const isLast = parts.length === 2;
2393
+ if (!isLast || !isTimeOfDay(secondPart)) {
2394
+ const subLocStart = text.indexOf(
2395
+ secondPart,
2396
+ locStart + locationName.length
2397
+ );
2398
+ if (subLocStart !== -1 && secondPart) {
2399
+ newTags.push({
2400
+ id: uuid(),
2401
+ block_id: block.id,
2402
+ category_id: "SUBLOCATION",
2403
+ name: secondPart,
2404
+ start_index: subLocStart,
2405
+ end_index: subLocStart + secondPart.length
2406
+ });
2407
+ }
2408
+ }
2409
+ }
2410
+ }
2411
+ }
2412
+ });
2413
+ if (newTags.length > 0) {
2414
+ const originalTags = tags;
2388
2415
  setTags((prev) => {
2389
2416
  const merged = [...prev];
2417
+ const existingChars = new Set(
2418
+ merged.filter(
2419
+ (t) => t.category_id === "CHARACTER" || t.category_id === "CAST"
2420
+ ).map((t) => t.name.toUpperCase())
2421
+ );
2390
2422
  newTags.forEach((newTag) => {
2423
+ if (newTag.category_id === "CHARACTER") {
2424
+ if (existingChars.has(newTag.name.toUpperCase())) return;
2425
+ existingChars.add(newTag.name.toUpperCase());
2426
+ }
2391
2427
  const isOverlapping = merged.some(
2392
2428
  (t) => t.block_id === newTag.block_id && newTag.end_index > t.start_index && newTag.start_index < t.end_index
2393
2429
  );
@@ -2397,26 +2433,17 @@ function useScriptBreakdownScene(options) {
2397
2433
  });
2398
2434
  return merged;
2399
2435
  });
2400
- return data;
2401
- } else {
2402
- setIsSummarizing(false);
2403
- console.error("Failed to summarize scene:", res);
2404
- }
2405
- };
2406
- const addSubLocation = useCallback(
2407
- (subLocation) => {
2408
- const trimmed = subLocation.trim();
2409
- if (trimmed && !subLocations.includes(trimmed)) {
2410
- setSubLocations((prev) => [...prev, trimmed]);
2436
+ try {
2437
+ if (options.onTagsBulkAdded) {
2438
+ await options.onTagsBulkAdded(newTags);
2439
+ }
2440
+ } catch (error2) {
2441
+ console.error("Failed to bulk create tags:", error2);
2442
+ setTags(originalTags);
2411
2443
  }
2412
- },
2413
- [subLocations]
2414
- );
2415
- const removeSubLocation = useCallback((subLocation) => {
2416
- setSubLocations((prev) => prev.filter((loc) => loc !== subLocation));
2417
- }, []);
2444
+ }
2445
+ }, [blocks, tags, options.onTagsBulkAdded]);
2418
2446
  useEffect(() => {
2419
- setSubLocations([]);
2420
2447
  setSceneBrief("");
2421
2448
  autoTaggedSceneRef.current = null;
2422
2449
  }, [options.scene_url]);
@@ -2430,6 +2457,18 @@ function useScriptBreakdownScene(options) {
2430
2457
  });
2431
2458
  }
2432
2459
  }, [options.preLoadedTags]);
2460
+ useEffect(() => {
2461
+ const doBulkCreate = async () => {
2462
+ if (blocks.length > 0 && !autoTaggedSceneRef.current) {
2463
+ const hasPreloadedTags = options.preLoadedTags && options.preLoadedTags.length > 0;
2464
+ if (!hasPreloadedTags) {
2465
+ autoTaggedSceneRef.current = options.scene_url;
2466
+ await bulkCreateTags();
2467
+ }
2468
+ }
2469
+ };
2470
+ doBulkCreate();
2471
+ }, [blocks, options.scene_url, options.preLoadedTags, bulkCreateTags]);
2433
2472
  const clearSelection = useCallback(() => {
2434
2473
  var _a;
2435
2474
  setSelectionMenu(null);
@@ -2556,6 +2595,25 @@ function useScriptBreakdownScene(options) {
2556
2595
  setTags((prev) => [...prev, tagToRemove]);
2557
2596
  }
2558
2597
  };
2598
+ const updateTag = async (id, categoryId) => {
2599
+ var _a;
2600
+ const tagToUpdate = tags.find((t) => t.id === id);
2601
+ if (!tagToUpdate) return;
2602
+ setTags(
2603
+ (prev) => prev.map((t) => t.id === id ? __spreadProps(__spreadValues({}, t), { category_id: categoryId }) : t)
2604
+ );
2605
+ clearSelection();
2606
+ try {
2607
+ await ((_a = options.onTagUpdated) == null ? void 0 : _a.call(options, id, categoryId));
2608
+ } catch (error2) {
2609
+ console.error("Failed to update tag:", error2);
2610
+ setTags(
2611
+ (prev) => prev.map(
2612
+ (t) => t.id === id ? __spreadProps(__spreadValues({}, t), { category_id: tagToUpdate.category_id }) : t
2613
+ )
2614
+ );
2615
+ }
2616
+ };
2559
2617
  return {
2560
2618
  scene,
2561
2619
  blocks,
@@ -2566,13 +2624,11 @@ function useScriptBreakdownScene(options) {
2566
2624
  selectionMenu,
2567
2625
  handleMouseUp,
2568
2626
  addTag,
2627
+ updateTag,
2569
2628
  removeTag,
2570
2629
  clearSelection,
2571
2630
  menuPlacement,
2572
2631
  menuRef,
2573
- subLocations,
2574
- addSubLocation,
2575
- removeSubLocation,
2576
2632
  sceneBrief,
2577
2633
  setSceneBrief,
2578
2634
  handleAISummarize,