@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
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-GeD1mo16.js"></script>
|
|
53
|
+
<link rel="stylesheet" crossorigin href="/video/assets/index-DMoT8G1q.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
|
@@ -6,13 +6,12 @@ import { createRoot } from "react-dom/client";
|
|
|
6
6
|
import { BrowserRouter } from "react-router-dom";
|
|
7
7
|
import { StationShell } from "./src/stations/StationShell";
|
|
8
8
|
import { hugpyConfig } from "./src/config";
|
|
9
|
-
import { startSessionMetering } from "./src/session";
|
|
10
9
|
import { initShare } from "./src/share";
|
|
11
10
|
import { installVideoDemo } from "./src/demo";
|
|
12
11
|
// Sitewide floating Help button. ONE shared framework-free DOM module mounted
|
|
13
12
|
// identically by all four surfaces (main SPA + media/video/fleet arms) — see
|
|
14
13
|
// ../ui_shared/help/helpWidget.js for why it isn't a copied React component.
|
|
15
|
-
import { mountHelpWidget } from "
|
|
14
|
+
import { mountHelpWidget } from "@hugpy/ui-shared/help/helpWidget.js";
|
|
16
15
|
import "./src/style/app.css";
|
|
17
16
|
|
|
18
17
|
// k9: lift a `?share=<key>` share-link credential out of the URL (stash + strip)
|
|
@@ -24,31 +23,11 @@ initShare();
|
|
|
24
23
|
// anything below can touch the network. No-op outside demo mode.
|
|
25
24
|
installVideoDemo();
|
|
26
25
|
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
//
|
|
32
|
-
// /version reports an integer `api` contract version. Fire-and-forget on boot —
|
|
33
|
-
// never block or delay rendering, and swallow any fetch/parse error.
|
|
34
|
-
const EXPECTED_API_CONTRACT = 1;
|
|
35
|
-
void (async () => {
|
|
36
|
-
try {
|
|
37
|
-
const res = await fetch(`${hugpyConfig.apiBase}/version`, {
|
|
38
|
-
credentials: hugpyConfig.withCredentials ? "include" : "same-origin",
|
|
39
|
-
});
|
|
40
|
-
if (!res.ok) return;
|
|
41
|
-
const body = (await res.json()) as { api?: unknown };
|
|
42
|
-
const api = body?.api;
|
|
43
|
-
if (api !== EXPECTED_API_CONTRACT) {
|
|
44
|
-
console.warn(
|
|
45
|
-
`hugpy API contract mismatch (got ${String(api)}, expected ${EXPECTED_API_CONTRACT})`,
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
} catch {
|
|
49
|
-
// swallow: never let the contract check disrupt boot.
|
|
50
|
-
}
|
|
51
|
-
})();
|
|
26
|
+
// Session ping (/session/ping) and the boot /version contract probe were dropped
|
|
27
|
+
// (2026-10-05): neither route has a backend in this build, so both only produced
|
|
28
|
+
// a repeating 404 and a one-shot 404. The page is action-driven now — it contacts
|
|
29
|
+
// the server on a real read (a seed) or a pushed event, never on a timer or a boot
|
|
30
|
+
// liveness poke.
|
|
52
31
|
|
|
53
32
|
const el = document.getElementById("root");
|
|
54
33
|
if (!el) throw new Error("video-intelligence: #root not found");
|
package/index.html
CHANGED
|
@@ -19,12 +19,12 @@
|
|
|
19
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
20
|
/>
|
|
21
21
|
<meta property="og:url" content="https://hugpy.ai/video/" />
|
|
22
|
-
<meta property="og:image" content="https://hugpy.ai/demo-media/og/video-thumb.png" />
|
|
23
|
-
<meta property="og:image:width" content="
|
|
24
|
-
<meta property="og:image:height" content="
|
|
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
25
|
<meta name="twitter:card" content="summary_large_image" />
|
|
26
26
|
<meta name="twitter:title" content="Hugpy Video Intelligence Studio" />
|
|
27
|
-
<meta name="twitter:image" content="https://hugpy.ai/demo-media/og/video-thumb.png" />
|
|
27
|
+
<meta name="twitter:image" content="https://hugpy.ai/demo-media/og/video-thumb-luigi.png" />
|
|
28
28
|
|
|
29
29
|
<!-- Same type system as the media arm: Newsreader serif + IBM Plex Mono. -->
|
|
30
30
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
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.4",
|
|
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",
|
|
@@ -14,7 +39,8 @@
|
|
|
14
39
|
"react-dom": "^18.3.1",
|
|
15
40
|
"react-router-dom": "^7.16.0",
|
|
16
41
|
"three": "^0.185.1",
|
|
17
|
-
"zod": "^3.23.8"
|
|
42
|
+
"zod": "^3.23.8",
|
|
43
|
+
"@hugpy/ui-shared": "^0.3.1"
|
|
18
44
|
},
|
|
19
45
|
"devDependencies": {
|
|
20
46
|
"@tailwindcss/vite": "^4.0.0",
|
|
@@ -25,6 +51,7 @@
|
|
|
25
51
|
"vite": "^5.4.21"
|
|
26
52
|
},
|
|
27
53
|
"files": [
|
|
54
|
+
"dist",
|
|
28
55
|
"src",
|
|
29
56
|
"entry.tsx",
|
|
30
57
|
"index.html",
|
|
@@ -32,8 +59,20 @@
|
|
|
32
59
|
"tsconfig.json",
|
|
33
60
|
"LICENSE"
|
|
34
61
|
],
|
|
35
|
-
"author": "putkoff (hugpy.ai)",
|
|
36
62
|
"publishConfig": {
|
|
37
63
|
"access": "public"
|
|
64
|
+
},
|
|
65
|
+
"hugpy": {
|
|
66
|
+
"genre": "video",
|
|
67
|
+
"python": [
|
|
68
|
+
{
|
|
69
|
+
"name": "hugpy-video",
|
|
70
|
+
"url": "https://pypi.org/project/hugpy-video/"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "hugpy-oracle",
|
|
74
|
+
"url": "https://pypi.org/project/hugpy-oracle/"
|
|
75
|
+
}
|
|
76
|
+
]
|
|
38
77
|
}
|
|
39
|
-
}
|
|
78
|
+
}
|
|
File without changes
|
package/src/config.ts
CHANGED
|
@@ -149,6 +149,8 @@ export const hugpyConfig = {
|
|
|
149
149
|
*/
|
|
150
150
|
studioI2VEnqueueUrl:
|
|
151
151
|
read("VITE_HUGPY_STUDIO_I2V_URL") ?? `${apiBase}/video/studio/i2v`,
|
|
152
|
+
studioPlanUrl:
|
|
153
|
+
read("VITE_HUGPY_STUDIO_PLAN_URL") ?? `${apiBase}/video/studio/plan`,
|
|
152
154
|
/**
|
|
153
155
|
* Enqueue a STUDIO MOVIE (Studio Movie composer, slice B2/B3) — POST an ordered
|
|
154
156
|
* goal timeline {resolution:{width,height,fps}, seed, vram_budget_gb, negative_prompt,
|
|
@@ -202,13 +204,19 @@ export const hugpyConfig = {
|
|
|
202
204
|
* (?all=1 appends recent terminal rows; ?limit=N, default 50) → { jobs: [{ job_id,
|
|
203
205
|
* name, status, created, updated, principal, progress:{…|phase:"awaiting_capacity",
|
|
204
206
|
* reason, held_since, overtaken}|null, placement?:{source:"reservation"|"template",
|
|
205
|
-
* host, worker_id, gpu, process, reserved_bytes} }] }.
|
|
206
|
-
*
|
|
207
|
-
* media CATALOG projection and carries PLACEMENT — WHERE each in-flight render
|
|
207
|
+
* host, worker_id, gpu, process, reserved_bytes} }] }. This is the media
|
|
208
|
+
* CATALOG projection and carries PLACEMENT — WHERE each in-flight render
|
|
208
209
|
* physically executes (ae · cuda:0 · P-studio). Declared here so the Active
|
|
209
210
|
* Processes surface never derives the path inline.
|
|
210
211
|
*/
|
|
211
212
|
mediaJobsUrl: read("VITE_HUGPY_MEDIA_JOBS_URL") ?? `${apiBase}/video/jobs`,
|
|
213
|
+
/**
|
|
214
|
+
* SSE: every media-bus job row the moment a worker reports a transition — the
|
|
215
|
+
* push that replaced the /video/jobs poll loops. The page seeds ONCE from
|
|
216
|
+
* mediaJobsUrl, then listens here; see video/jobStream.ts. 404-tolerant: if the
|
|
217
|
+
* stream is absent the page keeps the one-time seed and never falls back to a poll.
|
|
218
|
+
*/
|
|
219
|
+
jobsStreamUrl: read("VITE_HUGPY_JOBS_STREAM_URL") ?? `${apiBase}/video/jobs/stream`,
|
|
212
220
|
/**
|
|
213
221
|
* LIVE tok/s telemetry for the generate workbench's Toks panel. Two endpoints,
|
|
214
222
|
* both same-origin `/api`, polled together every 2s by `useToks`:
|
|
@@ -362,19 +370,6 @@ export const hugpyConfig = {
|
|
|
362
370
|
modelsUrl: read("VITE_HUGPY_MODELS_URL") ?? `${apiBase}/v1/models`,
|
|
363
371
|
/** Task defaults — GET → { defaults: { "text-to-image": <id>, ... }, tasks:[...] }. */
|
|
364
372
|
promptTasksUrl: read("VITE_HUGPY_PROMPT_TASKS_URL") ?? `${apiBase}/prompt/tasks`,
|
|
365
|
-
/**
|
|
366
|
-
* The AUTHORITATIVE unified feed of EVERY in-flight worker call across ALL
|
|
367
|
-
* transports (web / v1 / discord / cli / media) — GET → { jobs: [{ id, kind,
|
|
368
|
-
* status, transport, model, model_name, worker, principal, elapsed, tokens,
|
|
369
|
-
* error, ... }] }. Unlike the per-session jobTracker (which only knows jobs THIS
|
|
370
|
-
* browser enqueued) this shows fleet-wide work — identity reconstructions
|
|
371
|
-
* (kind:"identity_reconstruction", transport:"media"), studio renders, and any
|
|
372
|
-
* call from another station/transport. The Active Processes panel polls it,
|
|
373
|
-
* keeps the in-flight rows (pending/processing/streaming, minus `download`
|
|
374
|
-
* provisioning), and merges them with the tracker rows. Declared here so the
|
|
375
|
-
* panel never derives the path inline.
|
|
376
|
-
*/
|
|
377
|
-
llmJobsUrl: read("VITE_HUGPY_LLM_JOBS_URL") ?? `${apiBase}/llm/jobs`,
|
|
378
373
|
/**
|
|
379
374
|
* k9 VIDEO-SHARE links — GET → { keys:[{id,label,prefix,created_at,expires_at,
|
|
380
375
|
* expired,revoked,last_used}] } (OPERATOR-ONLY; a share/anon session gets 401,
|
|
@@ -464,17 +459,6 @@ export function llmChatCancelUrl(id: string): string {
|
|
|
464
459
|
return `${apiBase}/llm/chat/cancel/${encodeURIComponent(id)}`;
|
|
465
460
|
}
|
|
466
461
|
|
|
467
|
-
/**
|
|
468
|
-
* HARD-CANCEL a job by id on the unified feed: POST → {cancelled, status}. Unlike
|
|
469
|
-
* the transport-specific routes above, this is the ONE cancel the fleet-wide
|
|
470
|
-
* `/llm/jobs` Active-Processes rows hit regardless of which transport launched the
|
|
471
|
-
* job — the backend fans it out to the right bus. Per-id like `jobCancelUrl` —
|
|
472
|
-
* derived from apiBase, the one module permitted to hold URL literals.
|
|
473
|
-
*/
|
|
474
|
-
export function llmJobCancelUrl(id: string): string {
|
|
475
|
-
return `${apiBase}/llm/jobs/${encodeURIComponent(id)}/cancel`;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
462
|
/**
|
|
479
463
|
* Apply (pre-warm) a curated Generate-station preset by id: POST → on success
|
|
480
464
|
* {ok:true, worker:{name,id}, model_key, mode, defaults{...}, warming}; on failure
|
package/src/demo/DemoBanner.tsx
CHANGED
|
File without changes
|
package/src/demo/demo.css
CHANGED
|
File without changes
|
package/src/demo/demoFetch.ts
CHANGED
|
@@ -199,15 +199,6 @@ async function route(
|
|
|
199
199
|
if (path === "/v1/models") return jsonResponse(fx.V1_MODELS);
|
|
200
200
|
if (path === "/prompt/tasks") return jsonResponse(fx.PROMPT_TASKS);
|
|
201
201
|
|
|
202
|
-
// Unified in-flight worker feed (Active Processes poller, useWorkerCalls.ts,
|
|
203
|
-
// ~2s cadence). The brochure runs nothing, so the honest envelope is an empty
|
|
204
|
-
// in-flight list — { jobs: [] } is exactly the shape parseJobs() projects, and
|
|
205
|
-
// an empty list keeps the panel truthful (no fabricated running render). Routed
|
|
206
|
-
// (not gated) so the poll simply never reaches the wire in canned mode.
|
|
207
|
-
if (path === "/llm/jobs" && method === "GET") {
|
|
208
|
-
return jsonResponse({ jobs: [] });
|
|
209
|
-
}
|
|
210
|
-
|
|
211
202
|
// Bus-wide MEDIA JOBS listing (Active Processes media poller, useMediaJobs.ts,
|
|
212
203
|
// ~2s cadence, GET bare /video/jobs — no id, so the job-poll regex below never
|
|
213
204
|
// matches it). Same honest empty in-flight list as /llm/jobs. Must sit ABOVE
|
package/src/demo/fixtures.ts
CHANGED
|
File without changes
|
|
File without changes
|
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,14 +19,16 @@
|
|
|
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 "@hugpy/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
|
|
26
28
|
// drift from the rest of the site. Rendering stays local (runtime siteBase,
|
|
27
29
|
// current-marking). Plain .js, imported via allowJs with no .d.ts.
|
|
28
|
-
import { buildNavItems } from "
|
|
29
|
-
import "
|
|
30
|
+
import { buildNavItems } from "@hugpy/ui-shared/navbar/links.js";
|
|
31
|
+
import "@hugpy/ui-shared/navbar/navbar.css";
|
|
30
32
|
import "./Navbar.css";
|
|
31
33
|
|
|
32
34
|
// Origin-relative on a hugpy origin; the public hugpy site (siteUrl) otherwise.
|
|
@@ -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
|
@@ -7,11 +7,7 @@
|
|
|
7
7
|
// wipe them immediately. A server-side 1h sweep is the safety net for missed
|
|
8
8
|
// beacons. Disabled entirely in canned demo mode (no backend is contacted, and
|
|
9
9
|
// sendBeacon bypasses the demo fetch shim).
|
|
10
|
-
import { hugpyConfig } from "./config";
|
|
11
|
-
import { isCanned } from "./demo/mode";
|
|
12
|
-
|
|
13
10
|
const SID_KEY = "hugpy.video.sid";
|
|
14
|
-
const HEARTBEAT_MS = 60_000;
|
|
15
11
|
|
|
16
12
|
function makeSid(): string {
|
|
17
13
|
try {
|
|
@@ -42,44 +38,13 @@ export function getSessionId(): string {
|
|
|
42
38
|
}
|
|
43
39
|
}
|
|
44
40
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Session metering was removed (2026-10-05). It heartbeat /session/ping every 60s
|
|
43
|
+
* and beaconed /session/end on close, but neither route has a backend in this
|
|
44
|
+
* build (the ping 404'd on a timer). The per-tab session id below still tags
|
|
45
|
+
* uploads; the server's own sweep is the upload-lifecycle backstop. Kept as an
|
|
46
|
+
* exported no-op so any caller/import stays valid without reintroducing a poll.
|
|
47
|
+
*/
|
|
48
48
|
export function startSessionMetering(): void {
|
|
49
|
-
|
|
50
|
-
if (isCanned()) return;
|
|
51
|
-
started = true;
|
|
52
|
-
|
|
53
|
-
const sid = getSessionId();
|
|
54
|
-
const base = hugpyConfig.apiBase.replace(/\/$/, "");
|
|
55
|
-
const q = `sid=${encodeURIComponent(sid)}`;
|
|
56
|
-
|
|
57
|
-
// Heartbeat — a "simple" POST (query param, no custom header / JSON) so it never
|
|
58
|
-
// triggers a CORS preflight in cross-origin deploys. Failures are ignored.
|
|
59
|
-
const ping = () => {
|
|
60
|
-
try {
|
|
61
|
-
void fetch(`${base}/session/ping?${q}`, {
|
|
62
|
-
method: "POST",
|
|
63
|
-
credentials: hugpyConfig.withCredentials ? "include" : "same-origin",
|
|
64
|
-
keepalive: true,
|
|
65
|
-
}).catch(() => {});
|
|
66
|
-
} catch {
|
|
67
|
-
/* ignore */
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
ping(); // announce on load
|
|
72
|
-
window.setInterval(ping, HEARTBEAT_MS);
|
|
73
|
-
|
|
74
|
-
// Tab close → wipe now (best-effort). Skip bfcache navigations (persisted),
|
|
75
|
-
// which may be restored; the heartbeat resumes if so, and the 1h sweep is the
|
|
76
|
-
// backstop either way.
|
|
77
|
-
window.addEventListener("pagehide", (e) => {
|
|
78
|
-
if ((e as PageTransitionEvent).persisted) return;
|
|
79
|
-
try {
|
|
80
|
-
navigator.sendBeacon?.(`${base}/session/end?${q}`);
|
|
81
|
-
} catch {
|
|
82
|
-
/* ignore */
|
|
83
|
-
}
|
|
84
|
-
});
|
|
49
|
+
/* intentionally empty — no session ping, no tab-close beacon (no backend) */
|
|
85
50
|
}
|
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
|
|
@@ -1456,10 +1456,10 @@ export function GenerateStation({ spec }: { spec: StationSpec }) {
|
|
|
1456
1456
|
// This part's OWN assist model (falls back to the shared pick / fleet default).
|
|
1457
1457
|
const model = p.assistModel ?? assist.assistModel ?? null;
|
|
1458
1458
|
const context = opts.media ? { media: opts.media } : undefined;
|
|
1459
|
-
let newText =
|
|
1459
|
+
let newText = "";
|
|
1460
1460
|
await assist.runAssist(
|
|
1461
1461
|
mode,
|
|
1462
|
-
p.text,
|
|
1462
|
+
mode === "generate" ? "" : p.text,
|
|
1463
1463
|
(t) => {
|
|
1464
1464
|
newText = t;
|
|
1465
1465
|
editText(p.key, t);
|
|
@@ -3793,7 +3793,7 @@ export function GenerateStation({ spec }: { spec: StationSpec }) {
|
|
|
3793
3793
|
// apply (replace the primary text part). On ANY failure the hook surfaces .error
|
|
3794
3794
|
// inline and never calls apply, so the existing prompt stays intact.
|
|
3795
3795
|
function runAssist(assistMode: "detail" | "generate"): void {
|
|
3796
|
-
void assist.runAssist(assistMode, assembledPrompt, applyAssistedPrompt);
|
|
3796
|
+
void assist.runAssist(assistMode, assistMode === "generate" ? "" : assembledPrompt, applyAssistedPrompt);
|
|
3797
3797
|
}
|
|
3798
3798
|
|
|
3799
3799
|
useEffect(() => {
|
|
@@ -4010,7 +4010,10 @@ export function GenerateStation({ spec }: { spec: StationSpec }) {
|
|
|
4010
4010
|
SectionTabs + Options-rail + Run-row layout. The surface is LOCKED to the
|
|
4011
4011
|
matching sub-mode: Clip → "clip", Cinema → "movie" (the StudioMovieComposer). */}
|
|
4012
4012
|
{isStudioSurface && (
|
|
4013
|
-
<StudioGenerateMode
|
|
4013
|
+
<StudioGenerateMode
|
|
4014
|
+
lockedSurfaceMode={mode === "cinema" ? "movie" : "clip"}
|
|
4015
|
+
onSelectSurfaceMode={(next) => setMode(next === "movie" ? "cinema" : "clip")}
|
|
4016
|
+
/>
|
|
4014
4017
|
)}
|
|
4015
4018
|
|
|
4016
4019
|
{!isStudioSurface && (
|
|
@@ -5270,7 +5273,7 @@ export function GenerateStation({ spec }: { spec: StationSpec }) {
|
|
|
5270
5273
|
onGenerate={() =>
|
|
5271
5274
|
void (async () => {
|
|
5272
5275
|
let fresh = "";
|
|
5273
|
-
await assist.runAssist("generate",
|
|
5276
|
+
await assist.runAssist("generate", "", (p) => {
|
|
5274
5277
|
fresh = p;
|
|
5275
5278
|
editGoal(g.id, "prompt", p);
|
|
5276
5279
|
});
|
|
@@ -5489,7 +5492,8 @@ export function GenerateStation({ spec }: { spec: StationSpec }) {
|
|
|
5489
5492
|
{assist.assistModels.map((m) => (
|
|
5490
5493
|
<option key={m.model} value={m.model}>
|
|
5491
5494
|
{m.model}
|
|
5492
|
-
{m.
|
|
5495
|
+
{m.state === "serving" || m.state === "loaded" ? ""
|
|
5496
|
+
: m.state === "hot" ? " · hot (loads)" : " · cold (downloads)"}
|
|
5493
5497
|
</option>
|
|
5494
5498
|
))}
|
|
5495
5499
|
</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
|
|
@@ -80,10 +80,10 @@ export function SharePanel() {
|
|
|
80
80
|
}
|
|
81
81
|
}, []);
|
|
82
82
|
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
// The mount probe of GET /keys/video-share was dropped (2026-10-05): that route
|
|
84
|
+
// has no backend in this build, so the probe only 404'd. `authed` therefore
|
|
85
|
+
// stays null and the Share affordance renders nothing — the same outcome the
|
|
86
|
+
// 401/403 branch produced, with no request. refresh() still backs mint/revoke.
|
|
87
87
|
|
|
88
88
|
const mint = useCallback(async () => {
|
|
89
89
|
setMinting(true);
|
|
File without changes
|
|
File without changes
|
|
File without changes
|