@hugpy/video-intelligence-ui 0.3.2 → 0.3.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.
Files changed (152) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +9 -0
  3. package/dist/assets/hugpy-mark-BXktFcOQ.png +0 -0
  4. package/dist/assets/index-DMoT8G1q.css +1 -0
  5. package/dist/assets/index-GeD1mo16.js +4190 -0
  6. package/dist/index.html +71 -0
  7. package/entry.tsx +6 -27
  8. package/index.html +4 -4
  9. package/package.json +44 -5
  10. package/src/assets/hugpy-mark.png +0 -0
  11. package/src/config.ts +11 -27
  12. package/src/demo/DemoBanner.tsx +0 -0
  13. package/src/demo/demo.css +0 -0
  14. package/src/demo/demoFetch.ts +0 -9
  15. package/src/demo/fixtures.ts +0 -0
  16. package/src/demo/identityFixture.ts +0 -0
  17. package/src/demo/index.ts +0 -0
  18. package/src/demo/mediaBase.ts +0 -0
  19. package/src/demo/mode.ts +0 -0
  20. package/src/demo/promptAssist.ts +0 -0
  21. package/src/demo/sampleData.ts +0 -0
  22. package/src/demo/sampleSets.ts +0 -0
  23. package/src/demo/seed.ts +0 -0
  24. package/src/nav/BrandMark.css +0 -0
  25. package/src/nav/BrandMark.tsx +0 -0
  26. package/src/nav/Navbar.css +0 -0
  27. package/src/nav/Navbar.tsx +8 -3
  28. package/src/regions/SpatialRegionEditor.tsx +0 -0
  29. package/src/regions/TemporalRegionEditor.tsx +0 -0
  30. package/src/regions/types.ts +0 -0
  31. package/src/session.ts +8 -43
  32. package/src/share.ts +0 -0
  33. package/src/stations/ActivePanel.tsx +0 -0
  34. package/src/stations/ActiveProcessesStation.tsx +0 -0
  35. package/src/stations/AddFileBar.tsx +0 -0
  36. package/src/stations/AudioCropStation.tsx +0 -0
  37. package/src/stations/ComfyPanel.tsx +0 -0
  38. package/src/stations/ComfyStation.tsx +0 -0
  39. package/src/stations/CondensedKnobStrip.tsx +0 -0
  40. package/src/stations/ConsolePanel.tsx +0 -0
  41. package/src/stations/FrameExtractStation.tsx +0 -0
  42. package/src/stations/GenIdentityBar.tsx +0 -0
  43. package/src/stations/GenerateStation.tsx +10 -6
  44. package/src/stations/GoalComposer.tsx +0 -0
  45. package/src/stations/ImageCropStation.tsx +0 -0
  46. package/src/stations/KnobInfo.tsx +0 -0
  47. package/src/stations/PlaceholderStation.tsx +0 -0
  48. package/src/stations/ScriptFirstStation.tsx +4 -0
  49. package/src/stations/SectionTabs.tsx +0 -0
  50. package/src/stations/SharePanel.tsx +4 -4
  51. package/src/stations/SidebarRegistry.tsx +0 -0
  52. package/src/stations/StationShell.tsx +0 -0
  53. package/src/stations/StudioClipsStation.tsx +0 -0
  54. package/src/stations/TemplateStepsPanel.tsx +190 -0
  55. package/src/stations/ToksPanel.tsx +1 -2
  56. package/src/stations/WorkbenchDrawer.tsx +0 -0
  57. package/src/stations/WorkbenchSidebar.tsx +8 -261
  58. package/src/stations/WorkbenchStation.tsx +0 -0
  59. package/src/stations/comfyEmbed.ts +0 -0
  60. package/src/stations/drawerShell.tsx +0 -0
  61. package/src/stations/ops/AudioCropCore.tsx +0 -0
  62. package/src/stations/ops/CharacterGroupsPanel.tsx +75 -83
  63. package/src/stations/ops/FrameExtractCore.tsx +0 -0
  64. package/src/stations/ops/ImageCropCore.tsx +0 -0
  65. package/src/stations/ops/VideoCropCore.tsx +0 -0
  66. package/src/stations/ops/useVideoCropJobs.ts +0 -0
  67. package/src/stations/oracle/DagPanel.tsx +0 -0
  68. package/src/stations/oracle/OracleStation.tsx +2 -0
  69. package/src/stations/oracle/PerformanceComposer.tsx +119 -0
  70. package/src/stations/oracle/SpatialTab.tsx +0 -0
  71. package/src/stations/oracle/StewardPanel.tsx +0 -0
  72. package/src/stations/oracle/oracle.css +33 -0
  73. package/src/stations/oracle/oracleShared.tsx +0 -0
  74. package/src/stations/oracle/oracleTypes.ts +0 -0
  75. package/src/stations/oracle/useOracle.ts +0 -0
  76. package/src/stations/pollCaps.ts +0 -0
  77. package/src/stations/registry.ts +0 -0
  78. package/src/stations/scriptFirst.css +23 -0
  79. package/src/stations/studio/AngleRingEditor.tsx +0 -0
  80. package/src/stations/studio/IdentitiesStation.tsx +86 -76
  81. package/src/stations/studio/IdentityCreatePanel.tsx +0 -0
  82. package/src/stations/studio/IdentityProfileControls.tsx +0 -0
  83. package/src/stations/studio/StudioActiveProcesses.tsx +0 -0
  84. package/src/stations/studio/StudioAssistLogPanel.tsx +0 -0
  85. package/src/stations/studio/StudioGenerateMode.tsx +3 -2
  86. package/src/stations/studio/StudioGenerateTab.tsx +278 -190
  87. package/src/stations/studio/StudioItemCard.tsx +0 -0
  88. package/src/stations/studio/StudioLibraryTab.tsx +0 -0
  89. package/src/stations/studio/StudioMovieComposer.tsx +321 -46
  90. package/src/stations/studio/StudioMovieSessions.tsx +12 -19
  91. package/src/stations/studio/StudioPlane.tsx +28 -5
  92. package/src/stations/studio/StudioSplicedRow.tsx +0 -0
  93. package/src/stations/studio/StudioViewer.tsx +6 -11
  94. package/src/stations/studio/TurntableViewer.tsx +0 -0
  95. package/src/stations/studio/coordinationBadge.tsx +0 -0
  96. package/src/stations/studio/identityUpload.ts +0 -0
  97. package/src/stations/studio/movieTimeline.ts +0 -0
  98. package/src/stations/studio/promptCard.tsx +0 -0
  99. package/src/stations/studio/studioItem.ts +0 -0
  100. package/src/stations/studio/studioShared.tsx +14 -2
  101. package/src/stations/studio/useIdentityCreateJob.ts +26 -21
  102. package/src/stations/studio/useIdentityProfiles.ts +0 -0
  103. package/src/stations/studio/useReconstruction.ts +11 -5
  104. package/src/stations/types.ts +0 -0
  105. package/src/stations/useAudioCropJobs.ts +0 -0
  106. package/src/stations/useAudioExtractJob.ts +0 -0
  107. package/src/stations/useCropJobs.ts +0 -0
  108. package/src/stations/useFrameJobs.ts +0 -0
  109. package/src/stations/useGenerateJob.ts +0 -0
  110. package/src/stations/useGenerateMovieJob.ts +0 -0
  111. package/src/stations/useGenerateRowJobs.ts +0 -0
  112. package/src/stations/useGenerateSceneJob.ts +0 -0
  113. package/src/stations/useGenerateSceneRowJobs.ts +0 -0
  114. package/src/stations/useScriptFirstRun.ts +0 -0
  115. package/src/style/app.css +49 -0
  116. package/src/transport/client.ts +0 -0
  117. package/src/transport/telemetry.ts +0 -0
  118. package/src/video/DropReceptacle.tsx +0 -0
  119. package/src/video/LengthRow.tsx +0 -0
  120. package/src/video/LibraryGroups.tsx +0 -0
  121. package/src/video/PromptAssistButtons.tsx +5 -4
  122. package/src/video/UnnamedWarn.tsx +0 -0
  123. package/src/video/composerBridge.ts +0 -0
  124. package/src/video/contract.ts +0 -0
  125. package/src/video/coordinationReport.ts +0 -0
  126. package/src/video/jobClock.ts +0 -0
  127. package/src/video/jobStream.ts +119 -0
  128. package/src/video/jobTracker.ts +67 -76
  129. package/src/video/mediaLibrary.ts +0 -0
  130. package/src/video/modelsFeed.ts +105 -0
  131. package/src/video/renderCompat.ts +0 -0
  132. package/src/video/sessionForm.ts +0 -0
  133. package/src/video/spreadAssist.ts +0 -0
  134. package/src/video/studioBridge.ts +0 -0
  135. package/src/video/studioOracle.ts +48 -0
  136. package/src/video/useMediaJobs.ts +17 -3
  137. package/src/video/useModels.ts +12 -53
  138. package/src/video/useMoviePresets.ts +0 -0
  139. package/src/video/useMovieSessions.ts +15 -6
  140. package/src/video/useOperatorHistory.ts +11 -101
  141. package/src/video/usePresets.ts +0 -0
  142. package/src/video/useProjects.ts +0 -0
  143. package/src/video/usePromptAssist.ts +70 -25
  144. package/src/video/useRenderPresets.ts +0 -0
  145. package/src/video/useStudioAssistLog.ts +0 -0
  146. package/src/video/useStudioPresets.ts +0 -0
  147. package/src/video/useToks.ts +14 -6
  148. package/src/video/watchVideoJob.ts +43 -0
  149. package/tsconfig.json +0 -0
  150. package/vite.config.ts +0 -0
  151. package/src/stations/studio/StudioMovieComposer.tsx.bak-declutter-20260813 +0 -3031
  152. package/src/video/useWorkerCalls.ts +0 -165
@@ -34,7 +34,8 @@ import * as THREE from "three";
34
34
  import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
35
35
  import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
36
36
  import { request, okValue, errorOf, describeAppError } from "../../transport/client";
37
- import { mediaBytesUrl, identityGenerateUrl, identityMeshStatusUrl, videoJobUrl } from "../../config";
37
+ import { hugpyConfig, mediaBytesUrl, identityGenerateUrl, identityMeshStatusUrl } from "../../config";
38
+ import { watchVideoJob } from "../../video/watchVideoJob";
38
39
  import { useMediaLibrary } from "../../video/mediaLibrary";
39
40
  import type { LibraryItem } from "../../video/mediaLibrary";
40
41
  import type { MediaRef, IdentityReconstruction, IdentityAngleView } from "../../video/contract";
@@ -529,12 +530,12 @@ export function ExtractFromVideoPanel({
529
530
  const [pickError, setPickError] = useState<string | null>(null);
530
531
 
531
532
  const active = useRef(false);
532
- const timer = useRef<number | null>(null);
533
+ const jobStop = useRef<(() => void) | null>(null);
533
534
 
534
535
  useEffect(() => {
535
536
  return () => {
536
537
  active.current = false;
537
- if (timer.current != null) window.clearTimeout(timer.current);
538
+ jobStop.current?.();
538
539
  };
539
540
  }, []);
540
541
 
@@ -575,47 +576,34 @@ export function ExtractFromVideoPanel({
575
576
  }
576
577
 
577
578
  const jobId = enq.jobId;
578
- const deadline = Date.now() + 20 * 60 * 1000; // ~20 min bound, mirrors the /generate poll
579
- const pollOnce = async () => {
580
- if (!active.current) return;
581
- if (Date.now() > deadline) {
579
+ jobStop.current?.();
580
+ jobStop.current = watchVideoJob(jobId, (row) => {
581
+ if (!active.current) return true;
582
+ const job = row as {
583
+ progress?: number | null;
584
+ result?: { ok?: boolean; error?: { code?: string; message?: string; retryable?: boolean } } | null;
585
+ stage?: string | null;
586
+ message?: string | null;
587
+ };
588
+ const result = job.result;
589
+ if (result && typeof result.ok === "boolean") {
582
590
  active.current = false;
583
591
  setBusy(false);
584
- setStatus("Timed out waiting for the video extraction.");
585
- return;
586
- }
587
- const res = await request<unknown>(videoJobUrl(jobId), {
588
- meta: { specKey: "studio", operation: "identity.video_extract.status" },
589
- });
590
- if (!active.current) return;
591
- if (res.ok) {
592
- const job = okValue(res) as {
593
- progress?: number | null;
594
- result?: { ok?: boolean; error?: { code?: string; message?: string; retryable?: boolean } } | null;
595
- stage?: string | null;
596
- message?: string | null;
597
- };
598
- const result = job.result;
599
- if (result && typeof result.ok === "boolean") {
600
- active.current = false;
601
- setBusy(false);
602
- if (result.ok) {
603
- setStatus("done");
604
- setVideo(null);
605
- reload();
606
- } else {
607
- setStatus((result.error && result.error.message) || "Extraction failed.");
608
- }
609
- return;
610
- }
611
- const pct = typeof job.progress === "number" ? ` (${Math.round(job.progress * 100)}%)` : "";
612
- const stage = job.stage || job.message;
613
- setStatus(`Extracting…${pct}${stage ? ` — ${stage}` : ""}`);
592
+ if (result.ok) {
593
+ setStatus("done");
594
+ setVideo(null);
595
+ reload();
596
+ } else setStatus((result.error && result.error.message) || "Extraction failed.");
597
+ return true;
614
598
  }
615
- // queued/running/transient-poll-failure → keep polling.
616
- timer.current = window.setTimeout(() => void pollOnce(), 5000);
617
- };
618
- timer.current = window.setTimeout(() => void pollOnce(), 5000);
599
+ const pct = typeof job.progress === "number" ? ` (${Math.round(job.progress * 100)}%)` : "";
600
+ const stage = job.stage || job.message;
601
+ setStatus(`Extracting…${pct}${stage ? ` — ${stage}` : ""}`);
602
+ }, () => {
603
+ active.current = false;
604
+ setBusy(false);
605
+ setStatus("Timed out waiting for the video extraction.");
606
+ }, 20 * 60 * 1000);
619
607
  }
620
608
 
621
609
  const canSubmit = video != null && !busy && uploadPhase === "idle";
@@ -1523,13 +1511,13 @@ function IdentityDetail({
1523
1511
  // SUPERSEDED by the one-click mesh path for the identity gen flow — kept behind
1524
1512
  // the "⚙ Settings & advanced" disclosure (see the render) so the tab stays to
1525
1513
  // the point.
1526
- const genTimer = useRef<number | null>(null);
1514
+ const genStop = useRef<(() => void) | null>(null);
1527
1515
  const genActive = useRef(false);
1528
1516
  // Stop the poll loop + free the timer when this card unmounts mid-build.
1529
1517
  useEffect(() => {
1530
1518
  return () => {
1531
1519
  genActive.current = false;
1532
- if (genTimer.current != null) window.clearTimeout(genTimer.current);
1520
+ genStop.current?.();
1533
1521
  };
1534
1522
  }, []);
1535
1523
 
@@ -1770,43 +1758,46 @@ function IdentityDetail({
1770
1758
  return;
1771
1759
  }
1772
1760
 
1773
- const deadline = Date.now() + 20 * 60 * 1000; // ~20 min bound
1774
- const pollOnce = async () => {
1775
- if (!genActive.current) return;
1776
- if (Date.now() > deadline) {
1777
- genActive.current = false;
1778
- setGenBusy(false);
1779
- setGenStatus("Timed out waiting for the 3D identity build.");
1780
- reload();
1781
- return;
1782
- }
1783
- const sres = await request<unknown>(identityMeshStatusUrl(profile.slug, reconId), {
1784
- meta: { specKey: "studio", operation: "identity.mesh.status" },
1785
- });
1786
- if (!genActive.current) return;
1787
- if (sres.ok) {
1788
- const ms = okValue(sres) as { status?: string; error?: string | null };
1789
- const st = ms.status ?? "queued";
1790
- if (st === "done") {
1791
- genActive.current = false;
1792
- setGenBusy(false);
1793
- setGenStatus("done");
1794
- reload();
1795
- return;
1796
- }
1797
- if (st === "error" || st === "cancelled") {
1761
+ if (!enq.job_id) {
1762
+ genActive.current = false;
1763
+ setGenBusy(false);
1764
+ setGenStatus("Generate response had no job_id.");
1765
+ return;
1766
+ }
1767
+ genStop.current?.();
1768
+ genStop.current = watchVideoJob(enq.job_id, (row) => {
1769
+ if (!genActive.current) return true;
1770
+ const result = row.result as { ok?: boolean; error?: { message?: string } } | null;
1771
+ if (result && typeof result.ok === "boolean") {
1772
+ if (!result.ok) {
1798
1773
  genActive.current = false;
1799
1774
  setGenBusy(false);
1800
- setGenStatus(ms.error || `Build ${st}.`);
1775
+ setGenStatus(result.error?.message || "3D identity build failed.");
1801
1776
  reload();
1802
- return;
1777
+ } else {
1778
+ void request<unknown>(identityMeshStatusUrl(profile.slug, reconId), {
1779
+ meta: { specKey: "studio", operation: "identity.mesh.final" },
1780
+ }).then((sres) => {
1781
+ if (!genActive.current) return;
1782
+ genActive.current = false;
1783
+ setGenBusy(false);
1784
+ if (!sres.ok) setGenStatus(describeAppError(errorOf(sres)));
1785
+ else {
1786
+ const ms = okValue(sres) as { status?: string; error?: string | null };
1787
+ setGenStatus(ms.status === "done" ? "done" : ms.error || `Build ${ms.status || "unknown"}.`);
1788
+ }
1789
+ reload();
1790
+ });
1803
1791
  }
1804
- setGenStatus(`Generating… (${st})`);
1792
+ return true;
1805
1793
  }
1806
- // queued/running/transient-poll-failure → keep polling.
1807
- genTimer.current = window.setTimeout(() => void pollOnce(), 5000);
1808
- };
1809
- genTimer.current = window.setTimeout(() => void pollOnce(), 5000);
1794
+ setGenStatus(`Generating… (${String(row.current_stage || row.status || "running")})`);
1795
+ }, () => {
1796
+ genActive.current = false;
1797
+ setGenBusy(false);
1798
+ setGenStatus("Timed out waiting for the 3D identity build.");
1799
+ reload();
1800
+ }, 20 * 60 * 1000);
1810
1801
  }
1811
1802
 
1812
1803
  const { short: createdShort, full: createdFull } = fmtCreated(profile.created_at);
@@ -2149,6 +2140,17 @@ type IdTab = "active" | "session" | "settings";
2149
2140
 
2150
2141
  export function IdentitiesStation({ spec }: { spec: StationSpec }) {
2151
2142
  void spec;
2143
+ const [serviceProbe, setServiceProbe] = useState<{ configured: boolean; reachable: boolean; reason?: string; version?: string; capabilities?: Record<string, unknown> } | null>(null);
2144
+ useEffect(() => {
2145
+ let active = true;
2146
+ void (async () => {
2147
+ const result = await request<typeof serviceProbe>(`${hugpyConfig.apiBase}/video/identity-render/probe`, {
2148
+ meta: { specKey: "identities", operation: "identity.render.probe" },
2149
+ });
2150
+ if (active && result.ok) setServiceProbe(okValue(result));
2151
+ })();
2152
+ return () => { active = false; };
2153
+ }, []);
2152
2154
  const {
2153
2155
  profiles,
2154
2156
  loading,
@@ -2225,6 +2227,14 @@ export function IdentitiesStation({ spec }: { spec: StationSpec }) {
2225
2227
  return (
2226
2228
  <section className="station-wide vi-identities vi-identities-md" aria-label="Identities workspace"
2227
2229
  style={{ display: "grid", gridTemplateColumns: "300px minmax(0, 1fr)", gap: "1rem", alignItems: "start" }}>
2230
+ <div role="status" style={{ gridColumn: "1 / -1", padding: "0.55rem 0.75rem", border: "1px solid var(--vi-border)", borderRadius: "0.5rem", fontSize: "0.8rem" }}>
2231
+ <strong>Identity render service:</strong>{" "}
2232
+ {!serviceProbe ? "checking…" : serviceProbe.reachable
2233
+ ? `online${serviceProbe.version ? ` · ${serviceProbe.version}` : ""}`
2234
+ : serviceProbe.reason || "unavailable"}
2235
+ {serviceProbe?.reachable && serviceProbe.capabilities ?
2236
+ <span> · {Object.entries(serviceProbe.capabilities).filter(([, value]) => Boolean(value)).map(([name]) => name).join(", ")}</span> : null}
2237
+ </div>
2228
2238
  {/* ── SIDEBAR: tab filter + master list (wireframe b/c/d/f/e) ────────── */}
2229
2239
  <aside className="vi-id-sidebar" aria-label="Identity list"
2230
2240
  style={{ border: "1px solid var(--vi-border)", borderRadius: "0.5rem", padding: "0.5rem", position: "sticky", top: "0.5rem" }}>
@@ -2379,4 +2389,4 @@ export function IdentitiesStation({ spec }: { spec: StationSpec }) {
2379
2389
  </div>
2380
2390
  </section>
2381
2391
  );
2382
- }
2392
+ }
File without changes
File without changes
File without changes
File without changes
@@ -16,13 +16,14 @@ export interface StudioGenerateModeProps {
16
16
  /** Set by the top-level Generate tab (Clip → "clip", Cinema → "movie") to lock the
17
17
  * studio surface to one sub-mode and hide its internal Clip|Cinema switcher. */
18
18
  lockedSurfaceMode?: "clip" | "movie";
19
+ onSelectSurfaceMode?: (mode: "clip" | "movie") => void;
19
20
  }
20
21
 
21
- export function StudioGenerateMode({ lockedSurfaceMode }: StudioGenerateModeProps = {}) {
22
+ export function StudioGenerateMode({ lockedSurfaceMode, onSelectSurfaceMode }: StudioGenerateModeProps = {}) {
22
23
  return (
23
24
  <div className="vi-section-tabs vi-section-tabs--stacked">
24
25
  <section className="vi-section-region vi-section-region--work" aria-label="Studio">
25
- <StudioPlane lockedSurfaceMode={lockedSurfaceMode} />
26
+ <StudioPlane lockedSurfaceMode={lockedSurfaceMode} onSelectSurfaceMode={onSelectSurfaceMode} />
26
27
  </section>
27
28
  </div>
28
29
  );