@hugpy/video-intelligence-ui 0.3.4 → 0.3.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.html CHANGED
@@ -49,7 +49,7 @@
49
49
  overflow-x: hidden;
50
50
  }
51
51
  </style>
52
- <script type="module" crossorigin src="/video/assets/index-GeD1mo16.js"></script>
52
+ <script type="module" crossorigin src="/video/assets/index--is_kWXv.js"></script>
53
53
  <link rel="stylesheet" crossorigin href="/video/assets/index-DMoT8G1q.css">
54
54
  </head>
55
55
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hugpy/video-intelligence-ui",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "license": "SEE LICENSE IN LICENSE",
5
5
  "type": "module",
6
6
  "description": "Hugpy video-intelligence console: image crop, audio crop, frames/models and generate stations over the hugpy-video job bus; a standalone Vite + Tailwind v4 arm served at /video",
@@ -728,6 +728,18 @@ export function StudioMovieComposer({ settingsHost }: StudioMovieComposerProps =
728
728
  // the warning is worth the one extra click. Session-persisted like every other authored
729
729
  // field here.
730
730
  const [movieTitle, setMovieTitle] = useSessionState("studio.movie.title", "");
731
+ const autoTitleRef = useSessionRef<string>("studio.movie.autoTitle", "");
732
+ const applyGeneratedTitle = useCallback((prompt: string, title?: string) => {
733
+ const derived = title?.trim() || prompt.split(/[.,;\n]/)[0].trim()
734
+ .split(/\s+/).slice(0, 7).join(" ").slice(0, 48);
735
+ if (!derived) return;
736
+ const previousAutoTitle = autoTitleRef.current;
737
+ setMovieTitle((current) => {
738
+ if (current.trim() && current.trim() !== previousAutoTitle) return current;
739
+ autoTitleRef.current = derived;
740
+ return derived;
741
+ });
742
+ }, [setMovieTitle, autoTitleRef]);
731
743
  // Movie-level MODEL PIN + sampler overrides (operator ask 2026-07-13: the Settings tab
732
744
  // wants "the model selection" + a wider range of options). All BLANK-first ⇒ router
733
745
  // default: modelId "" = the capability router picks by budget/resolution (an unroutable
@@ -1306,13 +1318,6 @@ export function StudioMovieComposer({ settingsHost }: StudioMovieComposerProps =
1306
1318
  );
1307
1319
  const [producing, setProducing] = useState(false);
1308
1320
  const [produceError, setProduceError] = useState<string | null>(null);
1309
- // k120 slice 2 — render-time continuity: the runner rewrites each next segment
1310
- // from the previous segment's ACTUAL closing frame. Auto-enabled by a
1311
- // successful Produce (a scripted movie wants it); always operator-untickable.
1312
- const [continuityRefresh, setContinuityRefresh] = useSessionState<boolean>(
1313
- "studio.movie.continuityRefresh",
1314
- false,
1315
- );
1316
1321
  const produceMovie = useCallback(async () => {
1317
1322
  const p = premise.trim() || movieQuery.trim();
1318
1323
  if (!p || producing) return;
@@ -1368,10 +1373,10 @@ export function StudioMovieComposer({ settingsHost }: StudioMovieComposerProps =
1368
1373
  if (first.height != null && !manualSettings.current.includes("height")) setHeight(first.height);
1369
1374
  if (first.fps != null && !manualSettings.current.includes("fps")) setFps(first.fps);
1370
1375
  }
1376
+ applyGeneratedTitle(plan.segments[0]?.prompt || p, plan.title);
1371
1377
  setProducerNote([plan.title, plan.logline].filter(Boolean).join(" — "));
1372
- setContinuityRefresh(false);
1373
1378
  }, [premise, movieQuery, producing, goals, fps, assist.assistModel, startImage,
1374
- setWidth, setHeight, setFps, setContinuityRefresh]);
1379
+ setWidth, setHeight, setFps, applyGeneratedTitle]);
1375
1380
 
1376
1381
  const setSegmentCount = useCallback(
1377
1382
  (n: number) => {
@@ -1485,7 +1490,6 @@ export function StudioMovieComposer({ settingsHost }: StudioMovieComposerProps =
1485
1490
  // The last AUTO-derived movie title. A name the operator typed is sacred; a
1486
1491
  // name this ref minted may be replaced by the next Generate (operator
1487
1492
  // 2026-08-13: "purely a product of the same generation method").
1488
- const autoTitleRef = useRef("");
1489
1493
  const planGoalPrompt = useCallback(async (
1490
1494
  i: number, text: string,
1491
1495
  coordinated?: { seed?: number; frames?: number; joint_mode?: string },
@@ -1548,29 +1552,14 @@ export function StudioMovieComposer({ settingsHost }: StudioMovieComposerProps =
1548
1552
  setGoalField(i, { negative: cur ? `${cur}, ${neg}` : neg });
1549
1553
  },
1550
1554
  );
1551
- // NAME RIDES ALONG: derive a title from the generated prompt when the
1552
- // movie has none — or when the current one was itself auto-derived.
1553
- const t = movieTitle.trim();
1554
- if (t === "" || t === autoTitleRef.current) {
1555
- const derived = fresh
1556
- .split(/[.,;\n]/)[0]
1557
- .trim()
1558
- .split(/\s+/)
1559
- .slice(0, 7)
1560
- .join(" ")
1561
- .slice(0, 48);
1562
- if (derived) {
1563
- autoTitleRef.current = derived;
1564
- setMovieTitle(derived);
1565
- }
1566
- }
1555
+ applyGeneratedTitle(fresh);
1567
1556
  })();
1568
1557
  // The text is about to change, so the old classification no longer describes it.
1569
1558
  intentText.current[g.key] = "";
1570
1559
  setIntents((prev) => ({ ...prev, [g.key]: null }));
1571
1560
  },
1572
1561
  [assist, goals, rowContext, setGoalField, movieTitle, setMovieTitle,
1573
- settingsMode, planGoalPrompt],
1562
+ settingsMode, planGoalPrompt, applyGeneratedTitle],
1574
1563
  );
1575
1564
 
1576
1565
  // ── negative generation (§1b) — an exclusion list for THIS shot ──
@@ -1606,7 +1595,7 @@ export function StudioMovieComposer({ settingsHost }: StudioMovieComposerProps =
1606
1595
  });
1607
1596
  if (!body) return;
1608
1597
  setSpreadNotice(null);
1609
- const res = await assist.runSpread(body);
1598
+ const res = await assist.runSpread({ ...body, fps });
1610
1599
  if (!res) return;
1611
1600
 
1612
1601
  const bySeg = new Map(res.segments.map((s) => [s.segment_id, s]));
@@ -1643,6 +1632,8 @@ export function StudioMovieComposer({ settingsHost }: StudioMovieComposerProps =
1643
1632
  };
1644
1633
  }),
1645
1634
  );
1635
+ const firstGenerated = applied.length ? bySeg.get(applied[0]) : undefined;
1636
+ if (firstGenerated) applyGeneratedTitle(firstGenerated.prompt);
1646
1637
  let oracleNotice: string | null = null;
1647
1638
  if (settingsMode === "oracle") {
1648
1639
  for (const key of applied) {
@@ -1693,6 +1684,8 @@ export function StudioMovieComposer({ settingsHost }: StudioMovieComposerProps =
1693
1684
  steeringSeed,
1694
1685
  settingsMode,
1695
1686
  planGoalPrompt,
1687
+ applyGeneratedTitle,
1688
+ fps,
1696
1689
  ]);
1697
1690
 
1698
1691
  // ── GROUP ACTION: negatives for the selected rows ──
@@ -1909,13 +1902,9 @@ export function StudioMovieComposer({ settingsHost }: StudioMovieComposerProps =
1909
1902
  reference_images:
1910
1903
  !selectedProfile && references.length ? references.map((r) => r.uri) : undefined,
1911
1904
  goals: goalBodies,
1912
- // k120 slice 2 — PRODUCER CONTINUITY REFRESH: the runner rewrites each
1913
- // non-root segment's prompt from a vision read of the frame the previous
1914
- // segment actually ended on. Off drops the key (schema default False).
1915
- continuity_refresh: continuityRefresh || undefined,
1916
1905
  };
1917
1906
  return { body };
1918
- }, [goals, width, height, fps, seed, vramBudget, modelId, steps, cfg, negative, project, movieTitle, startImage, references, selectedProfile, continuityRefresh, settingsMode]);
1907
+ }, [goals, width, height, fps, seed, vramBudget, modelId, steps, cfg, negative, project, movieTitle, startImage, references, selectedProfile, settingsMode]);
1919
1908
 
1920
1909
  // NAME NUDGE (k91) — set when a submit was held back only because the movie has no
1921
1910
  // name. It is a warning, not a gate: the run row then offers "Generate without a
@@ -71,6 +71,7 @@ export interface AssistContextWire {
71
71
  /** The `mode:"spread"` request body (§1a), minus `model` (the hook adds that). */
72
72
  export interface SpreadRequestWire {
73
73
  mode: "spread";
74
+ fps?: number;
74
75
  movie_query?: string;
75
76
  style_bible?: Record<string, string>;
76
77
  fixed_segments: SegmentRefWire[];