@hugpy/video-intelligence-ui 0.1.0
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 +41 -0
- package/assets/sampels/images/bycycle/bd8603bad35d4f179f4d1134d8faf2ee.mp4 +0 -0
- package/assets/sampels/images/bycycle/frame_00000.png +0 -0
- package/assets/sampels/images/bycycle/frame_00001.png +0 -0
- package/assets/sampels/images/bycycle/frame_00002.png +0 -0
- package/assets/sampels/images/bycycle/frame_00003.png +0 -0
- package/assets/sampels/images/bycycle/frame_00004.png +0 -0
- package/assets/sampels/images/bycycle/frame_00005.png +0 -0
- package/assets/sampels/images/forrest/ff23090611b844a6b85c224f388bd542.mp4 +0 -0
- package/assets/sampels/images/forrest/frame_00000.png +0 -0
- package/assets/sampels/images/forrest/frame_00001.png +0 -0
- package/assets/sampels/images/forrest/frame_00002.png +0 -0
- package/assets/sampels/images/forrest/frame_00003.png +0 -0
- package/assets/sampels/images/forrest/frame_00004.png +0 -0
- package/assets/sampels/images/forrest/frame_00005.png +0 -0
- package/assets/sampels/images/shenzo/e1a4fcd623084ab4a986d11c8cd00db8.mp4 +0 -0
- package/assets/sampels/images/shenzo/frame_00000.png +0 -0
- package/assets/sampels/images/shenzo/frame_00001.png +0 -0
- package/assets/sampels/images/shenzo/frame_00002.png +0 -0
- package/assets/sampels/images/shenzo/frame_00003.png +0 -0
- package/assets/sampels/images/shenzo/frame_00004.png +0 -0
- package/assets/sampels/images/shenzo/frame_00005.png +0 -0
- package/assets/sampels/scenes/man.mp4 +0 -0
- package/assets/sampels/scenes/woman.mp4 +0 -0
- package/assets/sampels/studio_movies/caverns/_assembly/contrib_00.mp4 +0 -0
- package/assets/sampels/studio_movies/caverns/movie.json +67 -0
- package/assets/sampels/studio_movies/caverns/movie.mp4 +0 -0
- package/assets/sampels/studio_movies/caverns/segment_00/5eea7770a554fef0c3bdc38f8f38b925dcf75f13c015d423e8eadec47d41bf6b/clip.mp4 +0 -0
- package/assets/sampels/studio_movies/caverns/segment_00/5eea7770a554fef0c3bdc38f8f38b925dcf75f13c015d423e8eadec47d41bf6b/manifest.json +87 -0
- package/assets/sampels/studio_movies/caverns/segment_00/5eea7770a554fef0c3bdc38f8f38b925dcf75f13c015d423e8eadec47d41bf6b/provenance.json +7 -0
- package/assets/sampels/studio_movies/caverns/segment_01/branch.png +0 -0
- package/assets/sampels/studio_movies/keeper-matrix/_assembly/concat_list.txt +2 -0
- package/assets/sampels/studio_movies/keeper-matrix/_assembly/contrib_00.mp4 +0 -0
- package/assets/sampels/studio_movies/keeper-matrix/_assembly/contrib_01.mp4 +0 -0
- package/assets/sampels/studio_movies/keeper-matrix/movie.json +81 -0
- package/assets/sampels/studio_movies/keeper-matrix/movie.mp4 +0 -0
- package/assets/sampels/studio_movies/keeper-matrix/segment_00/64854c6a21d47ce10b23e884e12de448b25bfb0a375d33238dd0bec8056c076d/clip.mp4 +0 -0
- package/assets/sampels/studio_movies/keeper-matrix/segment_00/64854c6a21d47ce10b23e884e12de448b25bfb0a375d33238dd0bec8056c076d/manifest.json +87 -0
- package/assets/sampels/studio_movies/keeper-matrix/segment_00/64854c6a21d47ce10b23e884e12de448b25bfb0a375d33238dd0bec8056c076d/provenance.json +7 -0
- package/assets/sampels/studio_movies/keeper-matrix/segment_01/branch.png +0 -0
- package/assets/sampels/studio_movies/keeper-matrix/segment_01/cc926b44a42b90d7d291a4e50e3859458306c207a17c718086a3466e4049d6c6/clip.mp4 +0 -0
- package/assets/sampels/studio_movies/keeper-matrix/segment_01/cc926b44a42b90d7d291a4e50e3859458306c207a17c718086a3466e4049d6c6/manifest.json +87 -0
- package/assets/sampels/studio_movies/keeper-matrix/segment_01/cc926b44a42b90d7d291a4e50e3859458306c207a17c718086a3466e4049d6c6/provenance.json +7 -0
- package/assets/sampels/videos/a_red_paper_boat_drifting_on_a_calm_pond__gentle_ripples__mo_a62d6c28.mp4 +0 -0
- package/assets/sampels/videos/aurora.mp4 +0 -0
- package/assets/sampels/videos/dark_night.mp4 +0 -0
- package/assets/sampels/videos/fire_and_soul.mp4 +0 -0
- package/assets/sampels/videos/harsh_seas.mp4 +0 -0
- package/entry.tsx +74 -0
- package/index.html +53 -0
- package/package.json +42 -0
- package/src/assets/hugpy-mark.png +0 -0
- package/src/config.ts +676 -0
- package/src/demo/DemoBanner.tsx +54 -0
- package/src/demo/assets/demo-audio.mp3 +0 -0
- package/src/demo/assets/demo-clip.mp4 +0 -0
- package/src/demo/assets/demo-crop.mp4 +0 -0
- package/src/demo/assets/demo-poster.jpg +0 -0
- package/src/demo/assets/frame-1.jpg +0 -0
- package/src/demo/assets/frame-2.jpg +0 -0
- package/src/demo/assets/frame-3.jpg +0 -0
- package/src/demo/assets/frame-4.jpg +0 -0
- package/src/demo/assets/frame-5.jpg +0 -0
- package/src/demo/assets/frame-6.jpg +0 -0
- package/src/demo/assets/gen-1.jpg +0 -0
- package/src/demo/assets/gen-2.jpg +0 -0
- package/src/demo/assets.d.ts +14 -0
- package/src/demo/demo.css +64 -0
- package/src/demo/demoFetch.ts +305 -0
- package/src/demo/fixtures.ts +814 -0
- package/src/demo/index.ts +43 -0
- package/src/demo/mode.ts +81 -0
- package/src/demo/promptAssist.ts +181 -0
- package/src/demo/seed.ts +97 -0
- package/src/nav/BrandMark.css +66 -0
- package/src/nav/BrandMark.tsx +40 -0
- package/src/nav/Navbar.css +13 -0
- package/src/nav/Navbar.tsx +68 -0
- package/src/regions/SpatialRegionEditor.tsx +375 -0
- package/src/regions/TemporalRegionEditor.tsx +485 -0
- package/src/regions/types.ts +85 -0
- package/src/session.ts +85 -0
- package/src/share.ts +77 -0
- package/src/stations/ActivePanel.tsx +55 -0
- package/src/stations/ActiveProcessesStation.tsx +272 -0
- package/src/stations/ActiveProcessesStation.tsx.bak-sessions-20260812 +228 -0
- package/src/stations/AddFileBar.tsx +59 -0
- package/src/stations/AudioCropStation.tsx +157 -0
- package/src/stations/ComfyPanel.tsx +210 -0
- package/src/stations/ComfyStation.tsx +196 -0
- package/src/stations/CondensedKnobStrip.tsx +245 -0
- package/src/stations/ConsolePanel.tsx +82 -0
- package/src/stations/FrameExtractStation.tsx +148 -0
- package/src/stations/GenIdentityBar.tsx +412 -0
- package/src/stations/GenerateStation.tsx +5603 -0
- package/src/stations/GenerateStation.tsx.bak-tester-removal +5170 -0
- package/src/stations/GoalComposer.tsx +139 -0
- package/src/stations/ImageCropStation.tsx +153 -0
- package/src/stations/KnobInfo.tsx +64 -0
- package/src/stations/PlaceholderStation.tsx +26 -0
- package/src/stations/SectionTabs.tsx +276 -0
- package/src/stations/SharePanel.tsx +298 -0
- package/src/stations/SidebarRegistry.tsx +105 -0
- package/src/stations/StationShell.tsx +96 -0
- package/src/stations/StudioClipsStation.tsx +28 -0
- package/src/stations/WorkbenchDrawer.tsx +66 -0
- package/src/stations/WorkbenchSidebar.tsx +1224 -0
- package/src/stations/WorkbenchStation.tsx +91 -0
- package/src/stations/comfyEmbed.ts +79 -0
- package/src/stations/drawerShell.tsx +419 -0
- package/src/stations/ops/AudioCropCore.tsx +218 -0
- package/src/stations/ops/CharacterGroupsPanel.tsx +716 -0
- package/src/stations/ops/FrameExtractCore.tsx +695 -0
- package/src/stations/ops/ImageCropCore.tsx +164 -0
- package/src/stations/ops/VideoCropCore.tsx +193 -0
- package/src/stations/ops/useVideoCropJobs.ts +224 -0
- package/src/stations/pollCaps.ts +87 -0
- package/src/stations/registry.ts +191 -0
- package/src/stations/studio/AngleRingEditor.tsx +159 -0
- package/src/stations/studio/IdentitiesStation.tsx +2455 -0
- package/src/stations/studio/IdentityProfileControls.tsx +262 -0
- package/src/stations/studio/StudioActiveProcesses.tsx +224 -0
- package/src/stations/studio/StudioAssistLogPanel.tsx +220 -0
- package/src/stations/studio/StudioGenerateMode.tsx +29 -0
- package/src/stations/studio/StudioGenerateTab.tsx +2688 -0
- package/src/stations/studio/StudioItemCard.tsx +334 -0
- package/src/stations/studio/StudioLibraryTab.tsx +506 -0
- package/src/stations/studio/StudioMovieComposer.tsx +2875 -0
- package/src/stations/studio/StudioMovieComposer.tsx.bak-sessions-20260812 +2729 -0
- package/src/stations/studio/StudioMovieSessions.tsx +553 -0
- package/src/stations/studio/StudioMovieSessions.tsx.bak-sessions-20260812 +417 -0
- package/src/stations/studio/StudioPlane.tsx +302 -0
- package/src/stations/studio/StudioSplicedRow.tsx +476 -0
- package/src/stations/studio/StudioViewer.tsx +339 -0
- package/src/stations/studio/TurntableViewer.tsx +220 -0
- package/src/stations/studio/movieTimeline.ts +198 -0
- package/src/stations/studio/promptCard.tsx +498 -0
- package/src/stations/studio/studioItem.ts +87 -0
- package/src/stations/studio/studioShared.tsx +821 -0
- package/src/stations/studio/useIdentityProfiles.ts +575 -0
- package/src/stations/studio/useReconstruction.ts +283 -0
- package/src/stations/types.ts +43 -0
- package/src/stations/useAudioCropJobs.ts +202 -0
- package/src/stations/useAudioExtractJob.ts +144 -0
- package/src/stations/useCropJobs.ts +206 -0
- package/src/stations/useFrameJobs.ts +140 -0
- package/src/stations/useGenerateJob.ts +151 -0
- package/src/stations/useGenerateMovieJob.ts +177 -0
- package/src/stations/useGenerateRowJobs.ts +184 -0
- package/src/stations/useGenerateSceneJob.ts +156 -0
- package/src/stations/useGenerateSceneRowJobs.ts +282 -0
- package/src/style/app.css +4974 -0
- package/src/transport/client.ts +262 -0
- package/src/transport/telemetry.ts +97 -0
- package/src/video/DropReceptacle.tsx +122 -0
- package/src/video/PromptAssistButtons.tsx +118 -0
- package/src/video/composerBridge.ts +66 -0
- package/src/video/contract.ts +599 -0
- package/src/video/jobTracker.ts +702 -0
- package/src/video/mediaLibrary.ts +328 -0
- package/src/video/renderCompat.ts +512 -0
- package/src/video/sessionForm.ts +189 -0
- package/src/video/spreadAssist.ts +392 -0
- package/src/video/studioBridge.ts +82 -0
- package/src/video/useMediaJobs.ts +279 -0
- package/src/video/useModels.ts +413 -0
- package/src/video/useMoviePresets.ts +97 -0
- package/src/video/useMovieSessions.ts +292 -0
- package/src/video/usePresets.ts +167 -0
- package/src/video/useProjects.ts +87 -0
- package/src/video/usePromptAssist.ts +394 -0
- package/src/video/useRenderPresets.ts +221 -0
- package/src/video/useStudioAssistLog.ts +266 -0
- package/src/video/useStudioPresets.ts +128 -0
- package/src/video/useWorkerCalls.ts +165 -0
- package/tsconfig.json +17 -0
- package/vite.config.ts +33 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
// AudioCropCore — the reusable body of the Audio Crop station (Studio slice 2a).
|
|
2
|
+
// The station's "Components view" MINUS the upload bar: the video→audio extract
|
|
3
|
+
// step (useAudioExtractJob) + the TemporalRegionEditor + the useAudioCropJobs
|
|
4
|
+
// enqueue hook + the result grid, parameterised by a `source` ref (audio OR video).
|
|
5
|
+
//
|
|
6
|
+
// The video→audio extract PATH IS PRESERVED verbatim: when the `source` is a video
|
|
7
|
+
// the Core pulls its audio track out first and the editor works over the extracted
|
|
8
|
+
// audio; an audio source is edited directly. Behaviour is IDENTICAL to the
|
|
9
|
+
// pre-slice-2a AudioCropStation body: same `vi-station-components` shell, same
|
|
10
|
+
// extract progress affordance, same TemporalRegionEditor props, same run footer,
|
|
11
|
+
// same `vi-result-grid` + AudioJobCard. `onProduced` is an OPTIONAL slice-2b sink;
|
|
12
|
+
// when OMITTED (the station case) finished clips still land in the library via the
|
|
13
|
+
// tracker exactly as before.
|
|
14
|
+
//
|
|
15
|
+
// Source lifecycle: the station owned `extract.reset()` + `resetCrops()` + clearing
|
|
16
|
+
// the queued windows inside onPick/clearSource, and kicked `extract.run()` for a
|
|
17
|
+
// video. The Core now owns those, so it runs them whenever the `source` REFERENCE
|
|
18
|
+
// changes (a new pick or a clear). The initial mount is skipped (prevSource seed) so
|
|
19
|
+
// a remount after a sub-tab switch does NOT re-run the extract or wipe the durable
|
|
20
|
+
// tracker's finished clips.
|
|
21
|
+
import { useEffect, useRef, useState } from "react";
|
|
22
|
+
import { TemporalRegionEditor } from "../../regions/TemporalRegionEditor";
|
|
23
|
+
import type { TemporalRegion } from "../../regions/types";
|
|
24
|
+
import { useAudioExtractJob } from "../useAudioExtractJob";
|
|
25
|
+
import { useAudioCropJobs, type TrackedAudioJob } from "../useAudioCropJobs";
|
|
26
|
+
import { STILL_RUNNING_MESSAGE } from "../pollCaps";
|
|
27
|
+
import { mediaBytesUrl } from "../../config";
|
|
28
|
+
import type { MediaRef } from "../../video/contract";
|
|
29
|
+
|
|
30
|
+
export interface AudioCropCoreProps {
|
|
31
|
+
/** Source audio or video, or null before one is loaded. */
|
|
32
|
+
source: MediaRef | null;
|
|
33
|
+
/** Multi-clip queue (station) vs single clip (inline-on-item, slice 2b). */
|
|
34
|
+
multiRegion?: boolean;
|
|
35
|
+
/** Additive (slice 2b): notified with each finished clip's ref + its window.
|
|
36
|
+
Omitted by the station — the clip still lands in the library via the tracker. */
|
|
37
|
+
onProduced?: (
|
|
38
|
+
ref: MediaRef,
|
|
39
|
+
meta: { spatial?: null; temporal?: TemporalRegion | null },
|
|
40
|
+
) => void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function AudioCropCore({
|
|
44
|
+
source,
|
|
45
|
+
multiRegion = true,
|
|
46
|
+
onProduced,
|
|
47
|
+
}: AudioCropCoreProps) {
|
|
48
|
+
const [queued, setQueued] = useState<TemporalRegion[]>([]);
|
|
49
|
+
|
|
50
|
+
// Video sources are routed through the audio_extract chain first.
|
|
51
|
+
const extract = useAudioExtractJob();
|
|
52
|
+
|
|
53
|
+
// The audio ref the editor works over: an audio source directly, or the audio
|
|
54
|
+
// track extracted from a video source once that job finishes.
|
|
55
|
+
const audioSource: MediaRef | null =
|
|
56
|
+
source?.kind === "audio"
|
|
57
|
+
? source
|
|
58
|
+
: source?.kind === "video"
|
|
59
|
+
? extract.output
|
|
60
|
+
: null;
|
|
61
|
+
|
|
62
|
+
const { jobs, runCrops, resume, reset: resetCrops } = useAudioCropJobs(audioSource);
|
|
63
|
+
|
|
64
|
+
const duration = audioSource?.duration_s ?? 0;
|
|
65
|
+
|
|
66
|
+
// Reset the queue + extract + this station's tracker rows when a NEW source is
|
|
67
|
+
// picked (or cleared), and kick the audio_extract chain for a video — keyed on the
|
|
68
|
+
// source reference so it fires exactly on a pick/clear. The mount is skipped
|
|
69
|
+
// (prevSource seed) so a remount after a tab switch neither re-runs the extract
|
|
70
|
+
// (no duplicate job) nor wipes the durable tracker's finished clips.
|
|
71
|
+
const prevSource = useRef<MediaRef | null>(source);
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (prevSource.current === source) return;
|
|
74
|
+
prevSource.current = source;
|
|
75
|
+
setQueued([]);
|
|
76
|
+
extract.reset();
|
|
77
|
+
resetCrops();
|
|
78
|
+
// A video needs its audio track pulled out before the editor can open.
|
|
79
|
+
if (source?.kind === "video") extract.run(source);
|
|
80
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
81
|
+
}, [source]);
|
|
82
|
+
|
|
83
|
+
// Additive slice-2b sink: fire once per finished clip. Inert when onProduced is
|
|
84
|
+
// omitted (the station case) — the library push is owned by the tracker regardless.
|
|
85
|
+
const notified = useRef<Set<string>>(new Set());
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (!onProduced) return;
|
|
88
|
+
for (const j of jobs) {
|
|
89
|
+
if (j.status === "done" && j.output && !notified.current.has(j.key)) {
|
|
90
|
+
notified.current.add(j.key);
|
|
91
|
+
onProduced(j.output, { spatial: null, temporal: j.region });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}, [jobs, onProduced]);
|
|
95
|
+
|
|
96
|
+
const isVideo = source?.kind === "video";
|
|
97
|
+
const extracting = isVideo && !extract.output && extract.status !== "failed";
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<div className="vi-station-components">
|
|
101
|
+
{/* Video → audio extract progress + still-running affordance. */}
|
|
102
|
+
{isVideo && extracting && (
|
|
103
|
+
<p className="vi-crop-hint">
|
|
104
|
+
{extract.capped
|
|
105
|
+
? STILL_RUNNING_MESSAGE
|
|
106
|
+
: `Extracting the audio track… ${extract.status ?? "queued"}`}
|
|
107
|
+
{extract.capped && (
|
|
108
|
+
<button
|
|
109
|
+
type="button"
|
|
110
|
+
className="vi-btn vi-btn-sm"
|
|
111
|
+
onClick={extract.resume}
|
|
112
|
+
style={{ marginLeft: "0.6rem" }}
|
|
113
|
+
>
|
|
114
|
+
Check again
|
|
115
|
+
</button>
|
|
116
|
+
)}
|
|
117
|
+
</p>
|
|
118
|
+
)}
|
|
119
|
+
{isVideo && extract.error && (
|
|
120
|
+
<p className="vi-error" role="alert">{extract.error}</p>
|
|
121
|
+
)}
|
|
122
|
+
|
|
123
|
+
{/* Audio view + crop/manipulation options (waveform + numeric
|
|
124
|
+
windows) — ALWAYS rendered. With no audio loaded the editor shows
|
|
125
|
+
its full chrome over a blank (flat) waveform; loading audio only
|
|
126
|
+
fills the waveform. */}
|
|
127
|
+
<TemporalRegionEditor
|
|
128
|
+
source={audioSource}
|
|
129
|
+
duration={duration}
|
|
130
|
+
values={queued}
|
|
131
|
+
onChange={(next) => setQueued(Array.isArray(next) ? next : [next])}
|
|
132
|
+
multiRegion={multiRegion}
|
|
133
|
+
numericInputs
|
|
134
|
+
/>
|
|
135
|
+
|
|
136
|
+
{/* Run */}
|
|
137
|
+
<div className="vi-station-footer">
|
|
138
|
+
<div className="vi-station-footer-main">
|
|
139
|
+
<span className="vi-crop-hint">
|
|
140
|
+
Each queued window enqueues one temporal crop; clips land in the
|
|
141
|
+
media library.
|
|
142
|
+
</span>
|
|
143
|
+
</div>
|
|
144
|
+
<div className="vi-station-footer-run">
|
|
145
|
+
<button
|
|
146
|
+
type="button"
|
|
147
|
+
className="vi-btn vi-btn-accent"
|
|
148
|
+
disabled={!audioSource || queued.length === 0}
|
|
149
|
+
onClick={() => runCrops(queued)}
|
|
150
|
+
>
|
|
151
|
+
Run crop{queued.length === 1 ? "" : "s"}
|
|
152
|
+
{queued.length > 0 ? ` (${queued.length})` : ""}
|
|
153
|
+
</button>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
{jobs.length > 0 && (
|
|
158
|
+
<div className="vi-result-grid">
|
|
159
|
+
{jobs.map((j) => (
|
|
160
|
+
<AudioJobCard key={j.key} job={j} onResume={() => resume(j.key)} />
|
|
161
|
+
))}
|
|
162
|
+
</div>
|
|
163
|
+
)}
|
|
164
|
+
</div>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const STATUS_TEXT: Record<string, string> = {
|
|
169
|
+
queued: "queued",
|
|
170
|
+
claimed: "claimed",
|
|
171
|
+
running: "running",
|
|
172
|
+
done: "done",
|
|
173
|
+
failed: "failed",
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
function AudioJobCard({
|
|
177
|
+
job,
|
|
178
|
+
onResume,
|
|
179
|
+
}: {
|
|
180
|
+
job: TrackedAudioJob;
|
|
181
|
+
onResume: () => void;
|
|
182
|
+
}) {
|
|
183
|
+
const statusKey = job.status ?? "queued";
|
|
184
|
+
const done = job.status === "done";
|
|
185
|
+
const failed = job.status === "failed";
|
|
186
|
+
return (
|
|
187
|
+
<figure className="vi-result-card">
|
|
188
|
+
<div className="vi-result-head">
|
|
189
|
+
<span className="vi-region-idx">#{job.index}</span>
|
|
190
|
+
<span className={`vi-status vi-status-${statusKey}`}>{STATUS_TEXT[statusKey]}</span>
|
|
191
|
+
<code className="vi-region-dims">
|
|
192
|
+
{job.region.start_s.toFixed(2)}–{job.region.end_s.toFixed(2)}s
|
|
193
|
+
</code>
|
|
194
|
+
</div>
|
|
195
|
+
<div className="vi-result-body">
|
|
196
|
+
{done && job.output ? (
|
|
197
|
+
<div className="vi-audio-clip">
|
|
198
|
+
<audio controls className="vi-audio-player" src={mediaBytesUrl(job.output.uri)} />
|
|
199
|
+
<span className="vi-gen-note">in library</span>
|
|
200
|
+
</div>
|
|
201
|
+
) : failed ? (
|
|
202
|
+
<p className="vi-error">{job.error ?? "Audio-crop job failed."}</p>
|
|
203
|
+
) : job.capped ? (
|
|
204
|
+
<div className="vi-result-pending">
|
|
205
|
+
<p className="vi-crop-hint">{STILL_RUNNING_MESSAGE}</p>
|
|
206
|
+
<button type="button" className="vi-btn vi-btn-sm" onClick={onResume}>
|
|
207
|
+
Check again
|
|
208
|
+
</button>
|
|
209
|
+
</div>
|
|
210
|
+
) : (
|
|
211
|
+
<div className="vi-result-pending" aria-hidden>
|
|
212
|
+
{STATUS_TEXT[statusKey]}…
|
|
213
|
+
</div>
|
|
214
|
+
)}
|
|
215
|
+
</div>
|
|
216
|
+
</figure>
|
|
217
|
+
);
|
|
218
|
+
}
|