@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.
- package/LICENSE +0 -0
- package/README.md +9 -0
- package/dist/assets/hugpy-mark-BXktFcOQ.png +0 -0
- package/dist/assets/index-DMoT8G1q.css +1 -0
- package/dist/assets/index-GeD1mo16.js +4190 -0
- package/dist/index.html +71 -0
- package/entry.tsx +6 -27
- package/index.html +4 -4
- package/package.json +44 -5
- package/src/assets/hugpy-mark.png +0 -0
- package/src/config.ts +11 -27
- package/src/demo/DemoBanner.tsx +0 -0
- package/src/demo/demo.css +0 -0
- package/src/demo/demoFetch.ts +0 -9
- package/src/demo/fixtures.ts +0 -0
- package/src/demo/identityFixture.ts +0 -0
- package/src/demo/index.ts +0 -0
- package/src/demo/mediaBase.ts +0 -0
- package/src/demo/mode.ts +0 -0
- package/src/demo/promptAssist.ts +0 -0
- package/src/demo/sampleData.ts +0 -0
- package/src/demo/sampleSets.ts +0 -0
- package/src/demo/seed.ts +0 -0
- package/src/nav/BrandMark.css +0 -0
- package/src/nav/BrandMark.tsx +0 -0
- package/src/nav/Navbar.css +0 -0
- package/src/nav/Navbar.tsx +8 -3
- package/src/regions/SpatialRegionEditor.tsx +0 -0
- package/src/regions/TemporalRegionEditor.tsx +0 -0
- package/src/regions/types.ts +0 -0
- package/src/session.ts +8 -43
- package/src/share.ts +0 -0
- package/src/stations/ActivePanel.tsx +0 -0
- package/src/stations/ActiveProcessesStation.tsx +0 -0
- package/src/stations/AddFileBar.tsx +0 -0
- package/src/stations/AudioCropStation.tsx +0 -0
- package/src/stations/ComfyPanel.tsx +0 -0
- package/src/stations/ComfyStation.tsx +0 -0
- package/src/stations/CondensedKnobStrip.tsx +0 -0
- package/src/stations/ConsolePanel.tsx +0 -0
- package/src/stations/FrameExtractStation.tsx +0 -0
- package/src/stations/GenIdentityBar.tsx +0 -0
- package/src/stations/GenerateStation.tsx +10 -6
- package/src/stations/GoalComposer.tsx +0 -0
- package/src/stations/ImageCropStation.tsx +0 -0
- package/src/stations/KnobInfo.tsx +0 -0
- package/src/stations/PlaceholderStation.tsx +0 -0
- package/src/stations/ScriptFirstStation.tsx +4 -0
- package/src/stations/SectionTabs.tsx +0 -0
- package/src/stations/SharePanel.tsx +4 -4
- package/src/stations/SidebarRegistry.tsx +0 -0
- package/src/stations/StationShell.tsx +0 -0
- package/src/stations/StudioClipsStation.tsx +0 -0
- package/src/stations/TemplateStepsPanel.tsx +190 -0
- package/src/stations/ToksPanel.tsx +1 -2
- package/src/stations/WorkbenchDrawer.tsx +0 -0
- package/src/stations/WorkbenchSidebar.tsx +8 -261
- package/src/stations/WorkbenchStation.tsx +0 -0
- package/src/stations/comfyEmbed.ts +0 -0
- package/src/stations/drawerShell.tsx +0 -0
- package/src/stations/ops/AudioCropCore.tsx +0 -0
- package/src/stations/ops/CharacterGroupsPanel.tsx +75 -83
- package/src/stations/ops/FrameExtractCore.tsx +0 -0
- package/src/stations/ops/ImageCropCore.tsx +0 -0
- package/src/stations/ops/VideoCropCore.tsx +0 -0
- package/src/stations/ops/useVideoCropJobs.ts +0 -0
- package/src/stations/oracle/DagPanel.tsx +0 -0
- package/src/stations/oracle/OracleStation.tsx +2 -0
- package/src/stations/oracle/PerformanceComposer.tsx +119 -0
- package/src/stations/oracle/SpatialTab.tsx +0 -0
- package/src/stations/oracle/StewardPanel.tsx +0 -0
- package/src/stations/oracle/oracle.css +33 -0
- package/src/stations/oracle/oracleShared.tsx +0 -0
- package/src/stations/oracle/oracleTypes.ts +0 -0
- package/src/stations/oracle/useOracle.ts +0 -0
- package/src/stations/pollCaps.ts +0 -0
- package/src/stations/registry.ts +0 -0
- package/src/stations/scriptFirst.css +23 -0
- package/src/stations/studio/AngleRingEditor.tsx +0 -0
- package/src/stations/studio/IdentitiesStation.tsx +86 -76
- package/src/stations/studio/IdentityCreatePanel.tsx +0 -0
- package/src/stations/studio/IdentityProfileControls.tsx +0 -0
- package/src/stations/studio/StudioActiveProcesses.tsx +0 -0
- package/src/stations/studio/StudioAssistLogPanel.tsx +0 -0
- package/src/stations/studio/StudioGenerateMode.tsx +3 -2
- package/src/stations/studio/StudioGenerateTab.tsx +278 -190
- package/src/stations/studio/StudioItemCard.tsx +0 -0
- package/src/stations/studio/StudioLibraryTab.tsx +0 -0
- package/src/stations/studio/StudioMovieComposer.tsx +321 -46
- package/src/stations/studio/StudioMovieSessions.tsx +12 -19
- package/src/stations/studio/StudioPlane.tsx +28 -5
- package/src/stations/studio/StudioSplicedRow.tsx +0 -0
- package/src/stations/studio/StudioViewer.tsx +6 -11
- package/src/stations/studio/TurntableViewer.tsx +0 -0
- package/src/stations/studio/coordinationBadge.tsx +0 -0
- package/src/stations/studio/identityUpload.ts +0 -0
- package/src/stations/studio/movieTimeline.ts +0 -0
- package/src/stations/studio/promptCard.tsx +0 -0
- package/src/stations/studio/studioItem.ts +0 -0
- package/src/stations/studio/studioShared.tsx +14 -2
- package/src/stations/studio/useIdentityCreateJob.ts +26 -21
- package/src/stations/studio/useIdentityProfiles.ts +0 -0
- package/src/stations/studio/useReconstruction.ts +11 -5
- package/src/stations/types.ts +0 -0
- package/src/stations/useAudioCropJobs.ts +0 -0
- package/src/stations/useAudioExtractJob.ts +0 -0
- package/src/stations/useCropJobs.ts +0 -0
- package/src/stations/useFrameJobs.ts +0 -0
- package/src/stations/useGenerateJob.ts +0 -0
- package/src/stations/useGenerateMovieJob.ts +0 -0
- package/src/stations/useGenerateRowJobs.ts +0 -0
- package/src/stations/useGenerateSceneJob.ts +0 -0
- package/src/stations/useGenerateSceneRowJobs.ts +0 -0
- package/src/stations/useScriptFirstRun.ts +0 -0
- package/src/style/app.css +49 -0
- package/src/transport/client.ts +0 -0
- package/src/transport/telemetry.ts +0 -0
- package/src/video/DropReceptacle.tsx +0 -0
- package/src/video/LengthRow.tsx +0 -0
- package/src/video/LibraryGroups.tsx +0 -0
- package/src/video/PromptAssistButtons.tsx +5 -4
- package/src/video/UnnamedWarn.tsx +0 -0
- package/src/video/composerBridge.ts +0 -0
- package/src/video/contract.ts +0 -0
- package/src/video/coordinationReport.ts +0 -0
- package/src/video/jobClock.ts +0 -0
- package/src/video/jobStream.ts +119 -0
- package/src/video/jobTracker.ts +67 -76
- package/src/video/mediaLibrary.ts +0 -0
- package/src/video/modelsFeed.ts +105 -0
- package/src/video/renderCompat.ts +0 -0
- package/src/video/sessionForm.ts +0 -0
- package/src/video/spreadAssist.ts +0 -0
- package/src/video/studioBridge.ts +0 -0
- package/src/video/studioOracle.ts +48 -0
- package/src/video/useMediaJobs.ts +17 -3
- package/src/video/useModels.ts +12 -53
- package/src/video/useMoviePresets.ts +0 -0
- package/src/video/useMovieSessions.ts +15 -6
- package/src/video/useOperatorHistory.ts +11 -101
- package/src/video/usePresets.ts +0 -0
- package/src/video/useProjects.ts +0 -0
- package/src/video/usePromptAssist.ts +70 -25
- package/src/video/useRenderPresets.ts +0 -0
- package/src/video/useStudioAssistLog.ts +0 -0
- package/src/video/useStudioPresets.ts +0 -0
- package/src/video/useToks.ts +14 -6
- package/src/video/watchVideoJob.ts +43 -0
- package/tsconfig.json +0 -0
- package/vite.config.ts +0 -0
- package/src/stations/studio/StudioMovieComposer.tsx.bak-declutter-20260813 +0 -3031
- package/src/video/useWorkerCalls.ts +0 -165
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
import { useEffect, useRef, useState } from "react";
|
|
21
21
|
import {
|
|
22
22
|
mediaBytesUrl,
|
|
23
|
-
videoJobUrl,
|
|
24
23
|
hugpyConfig,
|
|
25
24
|
identityGenerateUrl,
|
|
26
25
|
identityMeshStatusUrl,
|
|
@@ -28,6 +27,7 @@ import {
|
|
|
28
27
|
import { request, okValue, errorOf, describeAppError } from "../../transport/client";
|
|
29
28
|
import type { MediaRef } from "../../video/contract";
|
|
30
29
|
import type { IdentityProfilesState } from "../studio/useIdentityProfiles";
|
|
30
|
+
import { watchVideoJob } from "../../video/watchVideoJob";
|
|
31
31
|
import { MeshViewer } from "../studio/IdentitiesStation";
|
|
32
32
|
|
|
33
33
|
// ── S1 review manifest shapes (tolerant reads — the poll result is cast, exactly
|
|
@@ -134,18 +134,18 @@ export function CharacterGroupsPanel({
|
|
|
134
134
|
// slug can run concurrently, mirrors the group-level state being a map too).
|
|
135
135
|
const [mesh3D, setMesh3D] = useState<Record<string, Mesh3DState>>({});
|
|
136
136
|
const meshActive = useRef<Record<string, boolean>>({});
|
|
137
|
-
const
|
|
137
|
+
const meshStop = useRef<Record<string, () => void>>({});
|
|
138
138
|
|
|
139
139
|
const active = useRef(false);
|
|
140
|
-
const
|
|
140
|
+
const reviewStop = useRef<(() => void) | null>(null);
|
|
141
141
|
const idSeq = useRef(0);
|
|
142
142
|
|
|
143
143
|
useEffect(() => {
|
|
144
144
|
return () => {
|
|
145
145
|
active.current = false;
|
|
146
|
-
|
|
146
|
+
reviewStop.current?.();
|
|
147
147
|
for (const slug of Object.keys(meshActive.current)) meshActive.current[slug] = false;
|
|
148
|
-
for (const
|
|
148
|
+
for (const stop of Object.values(meshStop.current)) stop();
|
|
149
149
|
};
|
|
150
150
|
}, []);
|
|
151
151
|
|
|
@@ -194,56 +194,42 @@ export function CharacterGroupsPanel({
|
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
const jobId = enq.jobId;
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (!active.current) return;
|
|
200
|
-
|
|
197
|
+
reviewStop.current?.();
|
|
198
|
+
reviewStop.current = watchVideoJob(jobId, (row) => {
|
|
199
|
+
if (!active.current) return true;
|
|
200
|
+
const job = row as {
|
|
201
|
+
progress?: number | null;
|
|
202
|
+
result?: {
|
|
203
|
+
ok?: boolean;
|
|
204
|
+
groups?: ReviewManifest;
|
|
205
|
+
error?: { code?: string; message?: string; retryable?: boolean };
|
|
206
|
+
} | null;
|
|
207
|
+
stage?: string | null;
|
|
208
|
+
message?: string | null;
|
|
209
|
+
};
|
|
210
|
+
const result = job.result;
|
|
211
|
+
if (result && typeof result.ok === "boolean") {
|
|
201
212
|
active.current = false;
|
|
202
213
|
setBusy(false);
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (!active.current) return;
|
|
211
|
-
if (res.ok) {
|
|
212
|
-
const job = okValue(res) as {
|
|
213
|
-
progress?: number | null;
|
|
214
|
-
result?: {
|
|
215
|
-
ok?: boolean;
|
|
216
|
-
groups?: ReviewManifest;
|
|
217
|
-
error?: { code?: string; message?: string; retryable?: boolean };
|
|
218
|
-
} | null;
|
|
219
|
-
stage?: string | null;
|
|
220
|
-
message?: string | null;
|
|
221
|
-
};
|
|
222
|
-
const result = job.result;
|
|
223
|
-
if (result && typeof result.ok === "boolean") {
|
|
224
|
-
active.current = false;
|
|
225
|
-
setBusy(false);
|
|
226
|
-
if (result.ok) {
|
|
227
|
-
setStatus("done");
|
|
228
|
-
if (result.groups && Array.isArray(result.groups.groups)) {
|
|
229
|
-
loadManifest(result.groups);
|
|
230
|
-
} else {
|
|
231
|
-
setError("Review finished but returned no character groups.");
|
|
232
|
-
}
|
|
233
|
-
} else {
|
|
234
|
-
setStatus(null);
|
|
235
|
-
setError((result.error && result.error.message) || "Character review failed.");
|
|
236
|
-
}
|
|
237
|
-
return;
|
|
214
|
+
if (result.ok) {
|
|
215
|
+
setStatus("done");
|
|
216
|
+
if (result.groups && Array.isArray(result.groups.groups)) loadManifest(result.groups);
|
|
217
|
+
else setError("Review finished but returned no character groups.");
|
|
218
|
+
} else {
|
|
219
|
+
setStatus(null);
|
|
220
|
+
setError((result.error && result.error.message) || "Character review failed.");
|
|
238
221
|
}
|
|
239
|
-
|
|
240
|
-
const stage = job.stage || job.message;
|
|
241
|
-
setStatus(`Reviewing…${pct}${stage ? ` — ${stage}` : ""}`);
|
|
222
|
+
return true;
|
|
242
223
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
224
|
+
const pct = typeof job.progress === "number" ? ` (${Math.round(job.progress * 100)}%)` : "";
|
|
225
|
+
const stage = job.stage || job.message;
|
|
226
|
+
setStatus(`Reviewing…${pct}${stage ? ` — ${stage}` : ""}`);
|
|
227
|
+
}, () => {
|
|
228
|
+
active.current = false;
|
|
229
|
+
setBusy(false);
|
|
230
|
+
setStatus(null);
|
|
231
|
+
setError("Timed out waiting for the character review.");
|
|
232
|
+
}, 20 * 60 * 1000);
|
|
247
233
|
}
|
|
248
234
|
|
|
249
235
|
// ── selection helpers ─────────────────────────────────────────────────────
|
|
@@ -414,41 +400,47 @@ export function CharacterGroupsPanel({
|
|
|
414
400
|
return;
|
|
415
401
|
}
|
|
416
402
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
const
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
if (!meshActive.current[slug]) return;
|
|
429
|
-
if (sres.ok) {
|
|
430
|
-
const ms = okValue(sres) as { status?: string; error?: string | null; glb_path?: string | null };
|
|
431
|
-
const st = ms.status ?? "queued";
|
|
432
|
-
if (st === "done") {
|
|
433
|
-
meshActive.current[slug] = false;
|
|
434
|
-
if (ms.glb_path) {
|
|
435
|
-
setMesh(slug, { phase: "done", status: "done", glbUrl: mediaBytesUrl(ms.glb_path), error: null });
|
|
436
|
-
} else {
|
|
437
|
-
setMesh(slug, { phase: "error", status: null, error: "Build finished but returned no model." });
|
|
438
|
-
}
|
|
439
|
-
return;
|
|
440
|
-
}
|
|
441
|
-
if (st === "error" || st === "cancelled") {
|
|
403
|
+
if (!enq.job_id) {
|
|
404
|
+
meshActive.current[slug] = false;
|
|
405
|
+
setMesh(slug, { phase: "error", status: null, error: "Generate response had no job_id." });
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
meshStop.current[slug]?.();
|
|
409
|
+
meshStop.current[slug] = watchVideoJob(enq.job_id, (row) => {
|
|
410
|
+
if (!meshActive.current[slug]) return true;
|
|
411
|
+
const result = row.result as { ok?: boolean; error?: { message?: string } } | null;
|
|
412
|
+
if (result && typeof result.ok === "boolean") {
|
|
413
|
+
if (!result.ok) {
|
|
442
414
|
meshActive.current[slug] = false;
|
|
443
|
-
setMesh(slug, { phase: "error", status: null,
|
|
444
|
-
|
|
415
|
+
setMesh(slug, { phase: "error", status: null,
|
|
416
|
+
error: result.error?.message || "3D build failed." });
|
|
417
|
+
} else {
|
|
418
|
+
void request<unknown>(identityMeshStatusUrl(slug, reconId), {
|
|
419
|
+
meta: { specKey: "studio", operation: "identity.groups.mesh.final" },
|
|
420
|
+
}).then((sres) => {
|
|
421
|
+
if (!meshActive.current[slug]) return;
|
|
422
|
+
meshActive.current[slug] = false;
|
|
423
|
+
if (!sres.ok) {
|
|
424
|
+
setMesh(slug, { phase: "error", status: null,
|
|
425
|
+
error: describeAppError(errorOf(sres)) });
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
const ms = okValue(sres) as { status?: string; error?: string | null; glb_path?: string | null };
|
|
429
|
+
if (ms.status === "done" && ms.glb_path) {
|
|
430
|
+
setMesh(slug, { phase: "done", status: "done", glbUrl: mediaBytesUrl(ms.glb_path), error: null });
|
|
431
|
+
} else {
|
|
432
|
+
setMesh(slug, { phase: "error", status: null,
|
|
433
|
+
error: ms.error || "Build finished but returned no model." });
|
|
434
|
+
}
|
|
435
|
+
});
|
|
445
436
|
}
|
|
446
|
-
|
|
437
|
+
return true;
|
|
447
438
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
439
|
+
setMesh(slug, { status: `Generating… (${String(row.current_stage || row.status || "running")})` });
|
|
440
|
+
}, () => {
|
|
441
|
+
meshActive.current[slug] = false;
|
|
442
|
+
setMesh(slug, { phase: "error", status: null, error: "Timed out waiting for the 3D build." });
|
|
443
|
+
}, 20 * 60 * 1000);
|
|
452
444
|
}
|
|
453
445
|
|
|
454
446
|
// ── render ─────────────────────────────────────────────────────────────────
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -19,6 +19,7 @@ import { useState } from "react";
|
|
|
19
19
|
|
|
20
20
|
import type { StationSpec } from "../types";
|
|
21
21
|
import { DagPanel } from "./DagPanel";
|
|
22
|
+
import { PerformanceComposer } from "./PerformanceComposer";
|
|
22
23
|
import { SpatialTab } from "./SpatialTab";
|
|
23
24
|
import { StewardPanel } from "./StewardPanel";
|
|
24
25
|
import { GapNote, RawJson, StateChip, Tag, short, when } from "./oracleShared";
|
|
@@ -273,6 +274,7 @@ export function OracleStation({ spec }: { spec: StationSpec }) {
|
|
|
273
274
|
<p className="station-blurb">{spec.blurb}</p>
|
|
274
275
|
|
|
275
276
|
<StewardPanel api={steward} embedded={runs.manifest?.dag?.steward ?? null} />
|
|
277
|
+
<PerformanceComposer onEnqueued={(jobId) => runs.select(jobId)} />
|
|
276
278
|
<RunPicker runs={runs} />
|
|
277
279
|
<RunHeader runs={runs} />
|
|
278
280
|
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
import { hugpyConfig } from "../../config";
|
|
4
|
+
import { describeAppError, errorOf, okValue, request } from "../../transport/client";
|
|
5
|
+
|
|
6
|
+
type EnqueueResult = { job_id?: string; error?: string };
|
|
7
|
+
type PerformanceProbe = { ready: boolean; bound?: string[]; unbound?: Array<{ seam: string; requirement: string }> };
|
|
8
|
+
type Stage = "authority" | "snapshot" | "audio" | "lock" | "segments" | "keyframes" | "clips" | "assembly";
|
|
9
|
+
|
|
10
|
+
/** The small, explicit entry point for the Oracle's durable performance job. */
|
|
11
|
+
export function PerformanceComposer({ onEnqueued }: { onEnqueued: (jobId: string) => void }) {
|
|
12
|
+
const [prompt, setPrompt] = useState("");
|
|
13
|
+
const [dialogue, setDialogue] = useState("");
|
|
14
|
+
const [speaker, setSpeaker] = useState("narrator");
|
|
15
|
+
const [voiceId, setVoiceId] = useState("narrator");
|
|
16
|
+
const [rawRef, setRawRef] = useState("");
|
|
17
|
+
const [quality, setQuality] = useState("balanced");
|
|
18
|
+
const [stopAfter, setStopAfter] = useState<Stage | "">("segments");
|
|
19
|
+
const [tts, setTts] = useState(3);
|
|
20
|
+
const [keyframes, setKeyframes] = useState(3);
|
|
21
|
+
const [clips, setClips] = useState(3);
|
|
22
|
+
const [maxSeconds, setMaxSeconds] = useState("");
|
|
23
|
+
const [seed, setSeed] = useState(0);
|
|
24
|
+
const [negative, setNegative] = useState("");
|
|
25
|
+
const [busy, setBusy] = useState(false);
|
|
26
|
+
const [message, setMessage] = useState("");
|
|
27
|
+
const [probe, setProbe] = useState<PerformanceProbe | null>(null);
|
|
28
|
+
const [probeGap, setProbeGap] = useState("");
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
let active = true;
|
|
32
|
+
void (async () => {
|
|
33
|
+
const result = await request<PerformanceProbe>(`${hugpyConfig.apiBase}/video/performance/probe`, {
|
|
34
|
+
meta: { specKey: "oracle", operation: "oracle.performance.probe" },
|
|
35
|
+
});
|
|
36
|
+
if (!active) return;
|
|
37
|
+
if (result.ok) setProbe(okValue(result));
|
|
38
|
+
else setProbeGap(describeAppError(errorOf(result)));
|
|
39
|
+
})();
|
|
40
|
+
return () => { active = false; };
|
|
41
|
+
}, []);
|
|
42
|
+
|
|
43
|
+
async function enqueue() {
|
|
44
|
+
const lines = dialogue.split("\n").map((text) => text.trim()).filter(Boolean);
|
|
45
|
+
if (!prompt.trim() || !lines.length || !speaker.trim() || !voiceId.trim() || !rawRef.trim()) {
|
|
46
|
+
setMessage("Add the goal, at least one dialogue line, speaker, voice ID, and raw request reference.");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const body = {
|
|
50
|
+
goal: { objective: prompt.trim(), raw_prompt: prompt, quality, planner_mode: "local_only" },
|
|
51
|
+
dialogue: { locked: true, lines: lines.map((text, i) => ({ line_id: `line-${i + 1}`, speaker: speaker.trim(), text })) },
|
|
52
|
+
casting: [[speaker.trim(), { voice_id: voiceId.trim(), kind: "synthetic" }]],
|
|
53
|
+
raw_request_ref: rawRef.trim(),
|
|
54
|
+
tts_candidates: tts,
|
|
55
|
+
keyframe_candidates: keyframes,
|
|
56
|
+
clip_candidates: clips,
|
|
57
|
+
seed_salt: seed,
|
|
58
|
+
negative_prompt: negative.trim() || null,
|
|
59
|
+
max_seconds: maxSeconds ? Number(maxSeconds) : null,
|
|
60
|
+
stop_after: stopAfter || null,
|
|
61
|
+
};
|
|
62
|
+
setBusy(true);
|
|
63
|
+
setMessage("");
|
|
64
|
+
const result = await request<EnqueueResult>(`${hugpyConfig.apiBase}/video/jobs/performance`, {
|
|
65
|
+
method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body),
|
|
66
|
+
meta: { specKey: "oracle", operation: "oracle.performance.enqueue" },
|
|
67
|
+
});
|
|
68
|
+
setBusy(false);
|
|
69
|
+
if (!result.ok) {
|
|
70
|
+
setMessage(describeAppError(errorOf(result)));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const jobId = okValue(result).job_id;
|
|
74
|
+
if (!jobId) {
|
|
75
|
+
setMessage("The server answered without a job id.");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
setMessage(`Queued ${jobId}`);
|
|
79
|
+
onEnqueued(jobId);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return <section className="vi-oracle-panel vi-oracle-composer" aria-label="New performance">
|
|
83
|
+
<h3>New performance <span className="vi-oracle-sub">audio first · durable media job · inspect each stage below</span></h3>
|
|
84
|
+
<div className="vi-oracle-fields">
|
|
85
|
+
<label className="vi-oracle-field vi-oracle-wide">Goal / visual direction
|
|
86
|
+
<textarea value={prompt} onChange={(e) => setPrompt(e.target.value)} rows={3} placeholder="Describe the film, characters, setting and action" />
|
|
87
|
+
</label>
|
|
88
|
+
<label className="vi-oracle-field vi-oracle-wide">Locked dialogue · one line per utterance
|
|
89
|
+
<textarea value={dialogue} onChange={(e) => setDialogue(e.target.value)} rows={3} placeholder="The words to speak" />
|
|
90
|
+
</label>
|
|
91
|
+
<label className="vi-oracle-field">Speaker <input value={speaker} onChange={(e) => setSpeaker(e.target.value)} /></label>
|
|
92
|
+
<label className="vi-oracle-field">Synthetic voice ID <input value={voiceId} onChange={(e) => setVoiceId(e.target.value)} /></label>
|
|
93
|
+
<label className="vi-oracle-field vi-oracle-wide">Raw request reference
|
|
94
|
+
<input value={rawRef} onChange={(e) => setRawRef(e.target.value)} placeholder="A durable reference to the original request" />
|
|
95
|
+
</label>
|
|
96
|
+
<label className="vi-oracle-field">Quality
|
|
97
|
+
<select value={quality} onChange={(e) => setQuality(e.target.value)}><option value="preview">Preview</option><option value="balanced">Balanced</option><option value="best">Best</option></select>
|
|
98
|
+
</label>
|
|
99
|
+
<label className="vi-oracle-field">Stop after
|
|
100
|
+
<select value={stopAfter} onChange={(e) => setStopAfter(e.target.value as Stage | "")}>
|
|
101
|
+
<option value="">Full run</option>{(["authority", "snapshot", "audio", "lock", "segments", "keyframes", "clips", "assembly"] as Stage[]).map((s) => <option key={s} value={s}>{s}</option>)}
|
|
102
|
+
</select>
|
|
103
|
+
</label>
|
|
104
|
+
<label className="vi-oracle-field">TTS candidates <input type="number" min={1} max={16} value={tts} onChange={(e) => setTts(Number(e.target.value))} /></label>
|
|
105
|
+
<label className="vi-oracle-field">Keyframe candidates <input type="number" min={1} max={16} value={keyframes} onChange={(e) => setKeyframes(Number(e.target.value))} /></label>
|
|
106
|
+
<label className="vi-oracle-field">Clip candidates <input type="number" min={1} max={16} value={clips} onChange={(e) => setClips(Number(e.target.value))} /></label>
|
|
107
|
+
<label className="vi-oracle-field">Max seconds <input type="number" min={1} value={maxSeconds} onChange={(e) => setMaxSeconds(e.target.value)} placeholder="No limit" /></label>
|
|
108
|
+
<label className="vi-oracle-field">Seed salt <input type="number" min={0} value={seed} onChange={(e) => setSeed(Number(e.target.value))} /></label>
|
|
109
|
+
<label className="vi-oracle-field vi-oracle-wide">Negative prompt <input value={negative} onChange={(e) => setNegative(e.target.value)} placeholder="Optional exclusions" /></label>
|
|
110
|
+
</div>
|
|
111
|
+
<div className="vi-oracle-note" role="status">
|
|
112
|
+
{probe ? <>
|
|
113
|
+
<strong>{probe.ready ? "Full recipe wired" : "Staged runs available; full recipe has gaps"}</strong>
|
|
114
|
+
{probe.unbound?.length ? <ul className="vi-oracle-limits">{probe.unbound.map((gap) => <li key={gap.seam}><code>{gap.seam}</code>: {gap.requirement}</li>)}</ul> : null}
|
|
115
|
+
</> : probeGap ? <>Could not read performance capability probe: {probeGap}</> : "Checking performance capabilities…"}
|
|
116
|
+
</div>
|
|
117
|
+
<div className="vi-oracle-row"><button type="button" className="vi-btn vi-btn-accent" disabled={busy} onClick={() => void enqueue()}>{busy ? "Queuing…" : "Queue performance"}</button>{message && <span role="status">{message}</span>}</div>
|
|
118
|
+
</section>;
|
|
119
|
+
}
|
|
File without changes
|
|
File without changes
|
|
@@ -22,6 +22,39 @@
|
|
|
22
22
|
min-width: 0;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
.vi-oracle-fields {
|
|
26
|
+
display: grid;
|
|
27
|
+
grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
|
|
28
|
+
gap: 0.75rem;
|
|
29
|
+
margin-bottom: 0.75rem;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.vi-oracle-field {
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
gap: 0.3rem;
|
|
36
|
+
min-width: 0;
|
|
37
|
+
color: var(--vi-text-dim);
|
|
38
|
+
font-size: 0.76rem;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.vi-oracle-wide { grid-column: 1 / -1; }
|
|
42
|
+
|
|
43
|
+
.vi-oracle-field input,
|
|
44
|
+
.vi-oracle-field textarea,
|
|
45
|
+
.vi-oracle-field select {
|
|
46
|
+
width: 100%;
|
|
47
|
+
box-sizing: border-box;
|
|
48
|
+
padding: 0.48rem 0.58rem;
|
|
49
|
+
border: 1px solid var(--vi-border);
|
|
50
|
+
border-radius: 6px;
|
|
51
|
+
background: var(--vi-surface);
|
|
52
|
+
color: var(--vi-text);
|
|
53
|
+
font: inherit;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.vi-oracle-field textarea { resize: vertical; }
|
|
57
|
+
|
|
25
58
|
.vi-oracle-panel > h3 {
|
|
26
59
|
margin: 0 0 0.6rem;
|
|
27
60
|
font-family: var(--vi-mono);
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/stations/pollCaps.ts
CHANGED
|
File without changes
|
package/src/stations/registry.ts
CHANGED
|
File without changes
|
|
@@ -348,3 +348,26 @@
|
|
|
348
348
|
grid-template-columns: 1fr;
|
|
349
349
|
}
|
|
350
350
|
}
|
|
351
|
+
|
|
352
|
+
/* ---- Template steps panel (TemplateStepsPanel.tsx) ---- */
|
|
353
|
+
.vi-template-steps {
|
|
354
|
+
margin: 0.5rem 0 0;
|
|
355
|
+
padding-left: 1.5rem;
|
|
356
|
+
display: flex;
|
|
357
|
+
flex-direction: column;
|
|
358
|
+
gap: 0.6rem;
|
|
359
|
+
}
|
|
360
|
+
.vi-template-step-head {
|
|
361
|
+
display: flex;
|
|
362
|
+
align-items: center;
|
|
363
|
+
gap: 0.6rem;
|
|
364
|
+
flex-wrap: wrap;
|
|
365
|
+
}
|
|
366
|
+
.vi-template-step-head > select {
|
|
367
|
+
max-width: 24rem;
|
|
368
|
+
}
|
|
369
|
+
.vi-template-step-desc {
|
|
370
|
+
margin: 0.15rem 0 0;
|
|
371
|
+
font-size: 0.85em;
|
|
372
|
+
opacity: 0.75;
|
|
373
|
+
}
|
|
File without changes
|