@hugpy/video-intelligence-ui 0.3.1 → 0.3.3
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-DuT1AMLn.css +1 -0
- package/dist/assets/index-_13ZiBDP.js +4191 -0
- package/dist/index.html +71 -0
- package/entry.tsx +0 -0
- package/index.html +15 -0
- package/package.json +42 -4
- package/src/assets/hugpy-mark.png +0 -0
- package/src/config.ts +0 -0
- package/src/demo/DemoBanner.tsx +0 -0
- package/src/demo/demo.css +0 -0
- package/src/demo/demoFetch.ts +5 -2
- package/src/demo/fixtures.ts +0 -0
- package/src/demo/identityFixture.ts +96 -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 +6 -1
- 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 +0 -0
- 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 +2 -1
- 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 +0 -0
- 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 +0 -0
- package/src/stations/WorkbenchDrawer.tsx +0 -0
- package/src/stations/WorkbenchSidebar.tsx +0 -0
- 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 +0 -0
- 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 +21 -2
- 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 +0 -0
- package/src/stations/studio/StudioGenerateTab.tsx +20 -2
- package/src/stations/studio/StudioItemCard.tsx +0 -0
- package/src/stations/studio/StudioLibraryTab.tsx +0 -0
- package/src/stations/studio/StudioMovieComposer.tsx +0 -0
- package/src/stations/studio/StudioMovieSessions.tsx +12 -19
- package/src/stations/studio/StudioPlane.tsx +25 -4
- package/src/stations/studio/StudioSplicedRow.tsx +0 -0
- package/src/stations/studio/StudioViewer.tsx +0 -0
- 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 +0 -0
- package/src/stations/studio/useIdentityCreateJob.ts +0 -0
- package/src/stations/studio/useIdentityProfiles.ts +0 -0
- package/src/stations/studio/useReconstruction.ts +0 -0
- 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 +6 -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/jobTracker.ts +0 -0
- package/src/video/mediaLibrary.ts +0 -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/useMediaJobs.ts +0 -0
- package/src/video/useModels.ts +0 -0
- package/src/video/useMoviePresets.ts +0 -0
- package/src/video/useMovieSessions.ts +0 -0
- package/src/video/useOperatorHistory.ts +0 -0
- package/src/video/usePresets.ts +0 -0
- package/src/video/useProjects.ts +0 -0
- package/src/video/usePromptAssist.ts +65 -23
- 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 +0 -0
- package/src/video/useWorkerCalls.ts +0 -0
- package/tsconfig.json +0 -0
- package/vite.config.ts +0 -0
- package/src/stations/studio/StudioMovieComposer.tsx.bak-declutter-20260813 +0 -3031
package/dist/index.html
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Hugpy Video Intelligence — Self-Hosted Video Crop, Frames & Generation</title>
|
|
7
|
+
<meta
|
|
8
|
+
name="description"
|
|
9
|
+
content="Hugpy Video Intelligence: a self-hosted console for spatial and temporal media cropping, frame extraction with explicit fps/quality control, and multimodal image generation — all on your own hardware."
|
|
10
|
+
/>
|
|
11
|
+
<meta name="theme-color" content="#050505" />
|
|
12
|
+
<link rel="canonical" href="https://hugpy.ai/video/" />
|
|
13
|
+
|
|
14
|
+
<meta property="og:type" content="website" />
|
|
15
|
+
<meta property="og:site_name" content="Hugpy" />
|
|
16
|
+
<meta property="og:title" content="Hugpy Video Intelligence Studio" />
|
|
17
|
+
<meta
|
|
18
|
+
property="og:description"
|
|
19
|
+
content="Self-hosted video AI on hardware you own: scene and clip workflows, frame extraction, character identities, and image generation. Try the studio in demo mode — nothing to install."
|
|
20
|
+
/>
|
|
21
|
+
<meta property="og:url" content="https://hugpy.ai/video/" />
|
|
22
|
+
<meta property="og:image" content="https://hugpy.ai/demo-media/og/video-thumb-luigi.png" />
|
|
23
|
+
<meta property="og:image:width" content="2880" />
|
|
24
|
+
<meta property="og:image:height" content="1512" />
|
|
25
|
+
<meta name="twitter:card" content="summary_large_image" />
|
|
26
|
+
<meta name="twitter:title" content="Hugpy Video Intelligence Studio" />
|
|
27
|
+
<meta name="twitter:image" content="https://hugpy.ai/demo-media/og/video-thumb-luigi.png" />
|
|
28
|
+
|
|
29
|
+
<!-- Same type system as the media arm: Newsreader serif + IBM Plex Mono. -->
|
|
30
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
31
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
32
|
+
<link
|
|
33
|
+
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&family=Newsreader:ital,wght@0,400;0,500;1,400&display=swap"
|
|
34
|
+
rel="stylesheet"
|
|
35
|
+
/>
|
|
36
|
+
<style>
|
|
37
|
+
/* Standalone shell paint — served alone at /video, so painting the page
|
|
38
|
+
chrome here cannot bleed into another site. Keeps the dark theme over
|
|
39
|
+
the entire viewport before the bundle loads. */
|
|
40
|
+
html,
|
|
41
|
+
body {
|
|
42
|
+
margin: 0;
|
|
43
|
+
background: #050505;
|
|
44
|
+
overflow-x: hidden;
|
|
45
|
+
}
|
|
46
|
+
#root {
|
|
47
|
+
min-height: 100dvh;
|
|
48
|
+
background: #050505;
|
|
49
|
+
overflow-x: hidden;
|
|
50
|
+
}
|
|
51
|
+
</style>
|
|
52
|
+
<script type="module" crossorigin src="/video/assets/index-_13ZiBDP.js"></script>
|
|
53
|
+
<link rel="stylesheet" crossorigin href="/video/assets/index-DuT1AMLn.css">
|
|
54
|
+
</head>
|
|
55
|
+
<body>
|
|
56
|
+
<div id="root"></div>
|
|
57
|
+
<noscript>
|
|
58
|
+
<main style="color:#ddd;max-width:42rem;margin:2rem auto;padding:0 1rem;font-family:system-ui,sans-serif">
|
|
59
|
+
<h1>Hugpy Video Intelligence</h1>
|
|
60
|
+
<p>
|
|
61
|
+
A self-hosted video-AI console: crop images and audio spatially/temporally,
|
|
62
|
+
extract frames with explicit fps and quality control, and generate images
|
|
63
|
+
from multimodal prompts — all on your own hardware.
|
|
64
|
+
</p>
|
|
65
|
+
<p><a style="color:#8ab4ff" href="/">Hugpy — Inference You Own</a></p>
|
|
66
|
+
</main>
|
|
67
|
+
</noscript>
|
|
68
|
+
<!-- Self-hosters/servers: replace null with your demo-media base URL string. -->
|
|
69
|
+
<script>window.__HUGPY_MEDIA_BASE__ = window.__HUGPY_MEDIA_BASE__ ?? null;</script>
|
|
70
|
+
</body>
|
|
71
|
+
</html>
|
package/entry.tsx
CHANGED
|
File without changes
|
package/index.html
CHANGED
|
@@ -11,6 +11,21 @@
|
|
|
11
11
|
<meta name="theme-color" content="#050505" />
|
|
12
12
|
<link rel="canonical" href="https://hugpy.ai/video/" />
|
|
13
13
|
|
|
14
|
+
<meta property="og:type" content="website" />
|
|
15
|
+
<meta property="og:site_name" content="Hugpy" />
|
|
16
|
+
<meta property="og:title" content="Hugpy Video Intelligence Studio" />
|
|
17
|
+
<meta
|
|
18
|
+
property="og:description"
|
|
19
|
+
content="Self-hosted video AI on hardware you own: scene and clip workflows, frame extraction, character identities, and image generation. Try the studio in demo mode — nothing to install."
|
|
20
|
+
/>
|
|
21
|
+
<meta property="og:url" content="https://hugpy.ai/video/" />
|
|
22
|
+
<meta property="og:image" content="https://hugpy.ai/demo-media/og/video-thumb-luigi.png" />
|
|
23
|
+
<meta property="og:image:width" content="2880" />
|
|
24
|
+
<meta property="og:image:height" content="1512" />
|
|
25
|
+
<meta name="twitter:card" content="summary_large_image" />
|
|
26
|
+
<meta name="twitter:title" content="Hugpy Video Intelligence Studio" />
|
|
27
|
+
<meta name="twitter:image" content="https://hugpy.ai/demo-media/og/video-thumb-luigi.png" />
|
|
28
|
+
|
|
14
29
|
<!-- Same type system as the media arm: Newsreader serif + IBM Plex Mono. -->
|
|
15
30
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
16
31
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
package/package.json
CHANGED
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hugpy/video-intelligence-ui",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"description": "
|
|
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",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"hugpy",
|
|
9
|
+
"llm",
|
|
10
|
+
"self-hosted",
|
|
11
|
+
"react",
|
|
12
|
+
"video",
|
|
13
|
+
"video-generation",
|
|
14
|
+
"console",
|
|
15
|
+
"hugpy-video",
|
|
16
|
+
"hugpy-oracle"
|
|
17
|
+
],
|
|
18
|
+
"homepage": "https://hugpy.ai",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/hugpy/hugpy.git",
|
|
22
|
+
"directory": "react/video_intelligence_ui"
|
|
23
|
+
},
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/hugpy/hugpy/issues"
|
|
26
|
+
},
|
|
27
|
+
"author": {
|
|
28
|
+
"name": "putkoff",
|
|
29
|
+
"email": "support@hugpy.ai",
|
|
30
|
+
"url": "https://hugpy.ai"
|
|
31
|
+
},
|
|
7
32
|
"scripts": {
|
|
8
33
|
"dev": "vite",
|
|
9
34
|
"build": "vite build",
|
|
@@ -25,6 +50,7 @@
|
|
|
25
50
|
"vite": "^5.4.21"
|
|
26
51
|
},
|
|
27
52
|
"files": [
|
|
53
|
+
"dist",
|
|
28
54
|
"src",
|
|
29
55
|
"entry.tsx",
|
|
30
56
|
"index.html",
|
|
@@ -32,8 +58,20 @@
|
|
|
32
58
|
"tsconfig.json",
|
|
33
59
|
"LICENSE"
|
|
34
60
|
],
|
|
35
|
-
"author": "putkoff (hugpy.ai)",
|
|
36
61
|
"publishConfig": {
|
|
37
62
|
"access": "public"
|
|
63
|
+
},
|
|
64
|
+
"hugpy": {
|
|
65
|
+
"genre": "video",
|
|
66
|
+
"python": [
|
|
67
|
+
{
|
|
68
|
+
"name": "hugpy-video",
|
|
69
|
+
"url": "https://pypi.org/project/hugpy-video/"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"name": "hugpy-oracle",
|
|
73
|
+
"url": "https://pypi.org/project/hugpy-oracle/"
|
|
74
|
+
}
|
|
75
|
+
]
|
|
38
76
|
}
|
|
39
|
-
}
|
|
77
|
+
}
|
|
File without changes
|
package/src/config.ts
CHANGED
|
File without changes
|
package/src/demo/DemoBanner.tsx
CHANGED
|
File without changes
|
package/src/demo/demo.css
CHANGED
|
File without changes
|
package/src/demo/demoFetch.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { hugpyConfig } from "../config";
|
|
|
13
13
|
import * as fx from "./fixtures";
|
|
14
14
|
import type { JobKind } from "./fixtures";
|
|
15
15
|
import { samplePromptFor, enhancePrompt } from "./promptAssist";
|
|
16
|
+
import { demoIdentityLuigi } from "./identityFixture";
|
|
16
17
|
|
|
17
18
|
// ── request introspection (same helpers as the media arm) ───────────────────
|
|
18
19
|
function pathOf(input: RequestInfo | URL): string {
|
|
@@ -245,9 +246,11 @@ async function route(
|
|
|
245
246
|
}
|
|
246
247
|
|
|
247
248
|
// IDENTITY PROFILES (Studio identity picker, useIdentityProfiles.ts, on mount)
|
|
248
|
-
// — GET → { profiles: [...] }.
|
|
249
|
+
// — GET → { profiles: [...] }. One REAL canned identity ("luigi": 12 refs →
|
|
250
|
+
// 72-view turntable → textured GLB, assets on the hosted demo-media tree)
|
|
251
|
+
// showcases the full pipeline; mutations still 401/501 honestly below.
|
|
249
252
|
if (path === "/video/identity-profiles" && method === "GET") {
|
|
250
|
-
return jsonResponse({ profiles: [] });
|
|
253
|
+
return jsonResponse({ profiles: [demoIdentityLuigi] });
|
|
251
254
|
}
|
|
252
255
|
|
|
253
256
|
// k9 VIDEO-SHARE links (SharePanel probe on mount + mint/revoke). The public
|
package/src/demo/fixtures.ts
CHANGED
|
File without changes
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// Canned IDENTITY PROFILE for the video-intelligence demo (?demo=1): the real
|
|
2
|
+
// "luigi" identity (12 refs → 72-view turntable → textured GLB), served from the
|
|
3
|
+
// HOSTED demo-media tree (canonical copy /var/www/hugpy-media/identities/luigi/).
|
|
4
|
+
//
|
|
5
|
+
// CRITICAL: every asset path here is a demoMedia() URL. config.ts's
|
|
6
|
+
// mediaBytesUrl() canned-mode bypass passes absolute http(s) URLs straight
|
|
7
|
+
// through to the browser, but any "/mnt/"-prefixed store handle would be routed
|
|
8
|
+
// back to the API — where the demo shim honestly 501s. Shape mirrors the
|
|
9
|
+
// server's _public() projection (zod: src/stations/studio/useIdentityProfiles.ts).
|
|
10
|
+
import type { IdentityProfile } from "../stations/studio/useIdentityProfiles";
|
|
11
|
+
import { demoMedia } from "./mediaBase";
|
|
12
|
+
|
|
13
|
+
const pad2 = (n: number) => String(n).padStart(2, "0");
|
|
14
|
+
|
|
15
|
+
const luigiRefs = Array.from({ length: 12 }, (_, i) =>
|
|
16
|
+
demoMedia(`identities/luigi/refs/ref_${pad2(i)}.jpeg`),
|
|
17
|
+
);
|
|
18
|
+
const luigiCanonical = Array.from({ length: 4 }, (_, i) =>
|
|
19
|
+
demoMedia(`identities/luigi/canonical/ref_${pad2(i)}.jpg`),
|
|
20
|
+
);
|
|
21
|
+
const luigiViews = Array.from({ length: 72 }, (_, i) =>
|
|
22
|
+
demoMedia(`identities/luigi/views/view_${pad2(i)}.jpg`),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export const demoIdentityLuigi: IdentityProfile = {
|
|
26
|
+
slug: "luigi",
|
|
27
|
+
name: "Luigi",
|
|
28
|
+
created_at: 1784100737.0394623,
|
|
29
|
+
notes: "",
|
|
30
|
+
reference_images: luigiRefs,
|
|
31
|
+
canonical: luigiCanonical,
|
|
32
|
+
canonical_angles: [0.0, 90.0, 180.0, 270.0],
|
|
33
|
+
// The TEXTURED version — NOT the clay base — so the station's activeRecon
|
|
34
|
+
// resolution (active_version → version.recon_id → reconstructions[]) lands on
|
|
35
|
+
// the turntable recon with the done mesh below.
|
|
36
|
+
active_version: "ver_851e3e418dc3869a",
|
|
37
|
+
versions: [
|
|
38
|
+
{
|
|
39
|
+
version_id: "ver_d9f3bf88e5fc7acd",
|
|
40
|
+
name: "base",
|
|
41
|
+
kind: "clay",
|
|
42
|
+
recon_id: "identity_ace73ab07078c529",
|
|
43
|
+
created_at: 1784101503.040569,
|
|
44
|
+
canonical: [],
|
|
45
|
+
notes: "",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
version_id: "ver_851e3e418dc3869a",
|
|
49
|
+
name: "textured-01",
|
|
50
|
+
kind: "textured",
|
|
51
|
+
recon_id: "identity_64853ed017380507",
|
|
52
|
+
created_at: 1784101750.5200672,
|
|
53
|
+
canonical: luigiCanonical,
|
|
54
|
+
notes: "",
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
reconstructions: [
|
|
58
|
+
{
|
|
59
|
+
recon_id: "identity_64853ed017380507",
|
|
60
|
+
mode: "turntable",
|
|
61
|
+
created_at: 1784101750.4968696,
|
|
62
|
+
frame_count: 72,
|
|
63
|
+
degrees_per_frame: 5.0,
|
|
64
|
+
job_id: "b31f8a1eb4e94d4faf8552cb83012290",
|
|
65
|
+
views: luigiViews,
|
|
66
|
+
mesh: {
|
|
67
|
+
status: "done",
|
|
68
|
+
error: null,
|
|
69
|
+
textured: true,
|
|
70
|
+
frame_count: 72,
|
|
71
|
+
auto_promoted: true,
|
|
72
|
+
glb_path: demoMedia("identities/luigi/identity.glb"),
|
|
73
|
+
video_path: demoMedia("identities/luigi/turntable.mp4"),
|
|
74
|
+
version_id: "ver_851e3e418dc3869a",
|
|
75
|
+
version_kind: "textured",
|
|
76
|
+
version_name: "textured-01",
|
|
77
|
+
job_id: "b31f8a1eb4e94d4faf8552cb83012290",
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
gen_settings: {
|
|
82
|
+
texture: true,
|
|
83
|
+
pose: "t-pose",
|
|
84
|
+
frame_count: 72,
|
|
85
|
+
fps: 24,
|
|
86
|
+
width: 768,
|
|
87
|
+
height: 768,
|
|
88
|
+
auto_promote: true,
|
|
89
|
+
front_ref: null,
|
|
90
|
+
remove_background: true,
|
|
91
|
+
vision_model: "Qwen2.5-VL-3B-Instruct-GGUF",
|
|
92
|
+
cleanup_prompt: "",
|
|
93
|
+
negative_prompt: "",
|
|
94
|
+
},
|
|
95
|
+
authorization: {},
|
|
96
|
+
};
|
package/src/demo/index.ts
CHANGED
|
File without changes
|
package/src/demo/mediaBase.ts
CHANGED
|
File without changes
|
package/src/demo/mode.ts
CHANGED
|
File without changes
|
package/src/demo/promptAssist.ts
CHANGED
|
File without changes
|
package/src/demo/sampleData.ts
CHANGED
|
File without changes
|
package/src/demo/sampleSets.ts
CHANGED
|
File without changes
|
package/src/demo/seed.ts
CHANGED
|
File without changes
|
package/src/nav/BrandMark.css
CHANGED
|
File without changes
|
package/src/nav/BrandMark.tsx
CHANGED
|
File without changes
|
package/src/nav/Navbar.css
CHANGED
|
File without changes
|
package/src/nav/Navbar.tsx
CHANGED
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
* demo/mode (`isEmbedded`) import — this arm has no iframe showroom, so the
|
|
20
20
|
* `target="_top"` escape hatch is unnecessary and every link is a normal nav.
|
|
21
21
|
*/
|
|
22
|
+
import React from "react";
|
|
22
23
|
import type { ReactNode } from "react";
|
|
24
|
+
import { makeUseArms } from "../../../ui_shared/arms";
|
|
23
25
|
import BrandMark from "./BrandMark";
|
|
24
26
|
import { hugpyConfig } from "../config";
|
|
25
27
|
// The link SET + order come from the ONE shared manifest so this arm can never
|
|
@@ -40,7 +42,10 @@ export const onHugpyOrigin =
|
|
|
40
42
|
/(^|\.)hugpy\.ai$/i.test(window.location.hostname);
|
|
41
43
|
export const siteBase = onHugpyOrigin ? "" : hugpyConfig.siteUrl.replace(/\/$/, "");
|
|
42
44
|
|
|
45
|
+
const useArms = makeUseArms(React); // arm links show only when their package is installed
|
|
46
|
+
|
|
43
47
|
export default function Navbar({ children }: { children?: ReactNode }) {
|
|
48
|
+
const arms = useArms(siteBase);
|
|
44
49
|
return (
|
|
45
50
|
<nav className="hugpy-navbar">
|
|
46
51
|
<span className="hugpy-navbar-brand">
|
|
@@ -51,7 +56,7 @@ export default function Navbar({ children }: { children?: ReactNode }) {
|
|
|
51
56
|
identical sitewide. Rendering is local: every destination is a plain
|
|
52
57
|
full-navigation <a> to a sibling hugpy SPA, prefixed with the runtime
|
|
53
58
|
siteBase. Video is the current surface. */}
|
|
54
|
-
{buildNavItems({ currentKey: "video", siteBase }).map((item) => (
|
|
59
|
+
{buildNavItems({ currentKey: "video", siteBase, present: arms.present }).map((item) => (
|
|
55
60
|
<a
|
|
56
61
|
key={item.key}
|
|
57
62
|
href={item.href}
|
|
File without changes
|
|
File without changes
|
package/src/regions/types.ts
CHANGED
|
File without changes
|
package/src/session.ts
CHANGED
|
File without changes
|
package/src/share.ts
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -5489,7 +5489,8 @@ export function GenerateStation({ spec }: { spec: StationSpec }) {
|
|
|
5489
5489
|
{assist.assistModels.map((m) => (
|
|
5490
5490
|
<option key={m.model} value={m.model}>
|
|
5491
5491
|
{m.model}
|
|
5492
|
-
{m.
|
|
5492
|
+
{m.state === "serving" || m.state === "loaded" ? ""
|
|
5493
|
+
: m.state === "hot" ? " · hot (loads)" : " · cold (downloads)"}
|
|
5493
5494
|
</option>
|
|
5494
5495
|
))}
|
|
5495
5496
|
</select>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
type RunState,
|
|
33
33
|
type SegmentRow,
|
|
34
34
|
} from "./useScriptFirstRun";
|
|
35
|
+
import { TemplateStepsPanel } from "./TemplateStepsPanel";
|
|
35
36
|
import "./scriptFirst.css";
|
|
36
37
|
|
|
37
38
|
const SHORT = 12;
|
|
@@ -841,6 +842,9 @@ export function ScriptFirstStation({ spec }: { spec: StationSpec }) {
|
|
|
841
842
|
)}
|
|
842
843
|
</section>
|
|
843
844
|
|
|
845
|
+
{/* ---- registry template: the ordered steps + per-step model choice ---- */}
|
|
846
|
+
<TemplateStepsPanel templateId="video-script-first" />
|
|
847
|
+
|
|
844
848
|
{(refusal || journalled) && (
|
|
845
849
|
<section className="vi-script-panel" role="alert">
|
|
846
850
|
<h3>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template steps — the ordered task outline of a registry task-template
|
|
3
|
+
* (settings NS `task_templates`), rendered so the operator can SEE every step
|
|
4
|
+
* and CHOOSE the model for each one. This is the missing cockpit: the
|
|
5
|
+
* template existed only as API data with no screen behind Generate.
|
|
6
|
+
*
|
|
7
|
+
* Reads: GET /llm/templates (open). Saves one slot at a time via
|
|
8
|
+
* POST /llm/templates/<id>/tasks/<task> {model} (operator-gated) — a 401/403
|
|
9
|
+
* or any refusal is rendered VERBATIM on the row it belongs to; the panel
|
|
10
|
+
* itself never hides. Model options come from the assist discovery endpoint,
|
|
11
|
+
* which already excludes operator-blocked and mistagged models, so a choice
|
|
12
|
+
* made here is a choice the resolver will actually serve.
|
|
13
|
+
*/
|
|
14
|
+
import { useCallback, useEffect, useState } from "react";
|
|
15
|
+
|
|
16
|
+
import { request, okValue, errorOf, describeAppError } from "../transport/client";
|
|
17
|
+
import { hugpyConfig } from "../config";
|
|
18
|
+
|
|
19
|
+
type TemplateTask = { name: string; desc: string; model: string | null };
|
|
20
|
+
type TemplateRec = {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
active: boolean;
|
|
24
|
+
groups: string[];
|
|
25
|
+
tasks: TemplateTask[];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const TEMPLATES_URL = `${hugpyConfig.apiBase}/llm/templates`;
|
|
29
|
+
|
|
30
|
+
export function TemplateStepsPanel({ templateId }: { templateId?: string }) {
|
|
31
|
+
const [templates, setTemplates] = useState<TemplateRec[]>([]);
|
|
32
|
+
const [picked, setPicked] = useState<string | null>(templateId ?? null);
|
|
33
|
+
const [models, setModels] = useState<string[]>([]);
|
|
34
|
+
const [rowBusy, setRowBusy] = useState<string | null>(null);
|
|
35
|
+
const [rowError, setRowError] = useState<Record<string, string>>({});
|
|
36
|
+
const [loadError, setLoadError] = useState<string | null>(null);
|
|
37
|
+
|
|
38
|
+
const load = useCallback(async () => {
|
|
39
|
+
const res = await request<unknown>(TEMPLATES_URL, {
|
|
40
|
+
method: "GET",
|
|
41
|
+
timeoutMs: 15_000,
|
|
42
|
+
meta: { operation: "templates.list" },
|
|
43
|
+
});
|
|
44
|
+
if (!res.ok) {
|
|
45
|
+
setLoadError(describeAppError(errorOf(res)));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const value = okValue(res) as { templates?: TemplateRec[] } | null;
|
|
49
|
+
const rows = Array.isArray(value?.templates) ? value.templates : [];
|
|
50
|
+
setTemplates(rows);
|
|
51
|
+
setLoadError(null);
|
|
52
|
+
// Keep the caller's pick when it still exists; otherwise prefer the
|
|
53
|
+
// active template, then the first one. Never invent an id.
|
|
54
|
+
setPicked((cur) =>
|
|
55
|
+
cur && rows.some((t) => t.id === cur)
|
|
56
|
+
? cur
|
|
57
|
+
: rows.find((t) => t.active)?.id ?? rows[0]?.id ?? null,
|
|
58
|
+
);
|
|
59
|
+
}, []);
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
void load();
|
|
63
|
+
}, [load]);
|
|
64
|
+
|
|
65
|
+
// Offerable generators — same feed the assist picker uses (blocked models
|
|
66
|
+
// are excluded server-side). A discovery failure leaves the dropdowns with
|
|
67
|
+
// just the current value; saving is still possible via "unset".
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
let alive = true;
|
|
70
|
+
(async () => {
|
|
71
|
+
const res = await request<unknown>(hugpyConfig.promptAssistModelsUrl, {
|
|
72
|
+
method: "GET",
|
|
73
|
+
timeoutMs: 15_000,
|
|
74
|
+
meta: { operation: "templates.models" },
|
|
75
|
+
});
|
|
76
|
+
if (!alive || !res.ok) return;
|
|
77
|
+
const value = okValue(res) as { models?: { model: string }[] } | null;
|
|
78
|
+
setModels(
|
|
79
|
+
(Array.isArray(value?.models) ? value.models : []).map((m) => m.model),
|
|
80
|
+
);
|
|
81
|
+
})();
|
|
82
|
+
return () => {
|
|
83
|
+
alive = false;
|
|
84
|
+
};
|
|
85
|
+
}, []);
|
|
86
|
+
|
|
87
|
+
const tpl = templates.find((t) => t.id === picked) ?? null;
|
|
88
|
+
|
|
89
|
+
const saveSlot = useCallback(
|
|
90
|
+
async (taskName: string, model: string | null) => {
|
|
91
|
+
if (!tpl) return;
|
|
92
|
+
setRowBusy(taskName);
|
|
93
|
+
const url = `${TEMPLATES_URL}/${encodeURIComponent(
|
|
94
|
+
tpl.id,
|
|
95
|
+
)}/tasks/${encodeURIComponent(taskName)}`;
|
|
96
|
+
const res = await request<unknown>(url, {
|
|
97
|
+
method: "POST",
|
|
98
|
+
body: JSON.stringify({ model }),
|
|
99
|
+
headers: { "Content-Type": "application/json" },
|
|
100
|
+
timeoutMs: 15_000,
|
|
101
|
+
meta: { operation: "templates.fill" },
|
|
102
|
+
});
|
|
103
|
+
setRowBusy(null);
|
|
104
|
+
if (!res.ok) {
|
|
105
|
+
setRowError((e) => ({ ...e, [taskName]: describeAppError(errorOf(res)) }));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
setRowError((e) => {
|
|
109
|
+
const { [taskName]: _gone, ...rest } = e;
|
|
110
|
+
return rest;
|
|
111
|
+
});
|
|
112
|
+
void load();
|
|
113
|
+
},
|
|
114
|
+
[tpl, load],
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<section className="vi-script-panel">
|
|
119
|
+
<h3>
|
|
120
|
+
Template steps
|
|
121
|
+
<span className="vi-script-sub">
|
|
122
|
+
the ordered plan — every step, and the model it calls
|
|
123
|
+
</span>
|
|
124
|
+
</h3>
|
|
125
|
+
|
|
126
|
+
{loadError && <p className="vi-script-note">{loadError}</p>}
|
|
127
|
+
|
|
128
|
+
<div className="vi-script-row">
|
|
129
|
+
{templates.map((t) => (
|
|
130
|
+
<button
|
|
131
|
+
key={t.id}
|
|
132
|
+
type="button"
|
|
133
|
+
className={`vi-btn vi-btn-sm${t.id === picked ? " vi-btn-on" : ""}`}
|
|
134
|
+
onClick={() => setPicked(t.id)}
|
|
135
|
+
>
|
|
136
|
+
{t.name}
|
|
137
|
+
{t.active ? " · active" : ""}
|
|
138
|
+
</button>
|
|
139
|
+
))}
|
|
140
|
+
{templates.length === 0 && !loadError && (
|
|
141
|
+
<span className="vi-script-note">No templates in the registry.</span>
|
|
142
|
+
)}
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
{tpl && (
|
|
146
|
+
<>
|
|
147
|
+
<p className="vi-script-note">
|
|
148
|
+
groups (stage order): <code>{tpl.groups.join(" → ") || "—"}</code>
|
|
149
|
+
</p>
|
|
150
|
+
<ol className="vi-template-steps">
|
|
151
|
+
{tpl.tasks.map((task) => (
|
|
152
|
+
<li key={task.name} className="vi-template-step">
|
|
153
|
+
<div className="vi-template-step-head">
|
|
154
|
+
<code>{task.name}</code>
|
|
155
|
+
<select
|
|
156
|
+
className="vi-knob-input"
|
|
157
|
+
value={task.model ?? ""}
|
|
158
|
+
disabled={rowBusy === task.name}
|
|
159
|
+
onChange={(e) =>
|
|
160
|
+
void saveSlot(task.name, e.target.value || null)
|
|
161
|
+
}
|
|
162
|
+
>
|
|
163
|
+
<option value="">unset — router picks from groups</option>
|
|
164
|
+
{/* Keep a pinned model visible even when discovery
|
|
165
|
+
doesn't offer it right now (worker offline). */}
|
|
166
|
+
{task.model && !models.includes(task.model) && (
|
|
167
|
+
<option value={task.model}>{task.model}</option>
|
|
168
|
+
)}
|
|
169
|
+
{models.map((m) => (
|
|
170
|
+
<option key={m} value={m}>
|
|
171
|
+
{m}
|
|
172
|
+
</option>
|
|
173
|
+
))}
|
|
174
|
+
</select>
|
|
175
|
+
{rowBusy === task.name && (
|
|
176
|
+
<span className="vi-script-note">saving…</span>
|
|
177
|
+
)}
|
|
178
|
+
</div>
|
|
179
|
+
<p className="vi-template-step-desc">{task.desc}</p>
|
|
180
|
+
{rowError[task.name] && (
|
|
181
|
+
<p className="vi-script-note">{rowError[task.name]}</p>
|
|
182
|
+
)}
|
|
183
|
+
</li>
|
|
184
|
+
))}
|
|
185
|
+
</ol>
|
|
186
|
+
</>
|
|
187
|
+
)}
|
|
188
|
+
</section>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|