@rightkit/video-ui 0.0.0-stage → 0.1.1
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/CHANGELOG.md +10 -0
- package/LICENSE-APACHE +201 -0
- package/LICENSE-MIT +21 -0
- package/README.md +47 -2
- package/dist/appName.d.ts +2 -0
- package/dist/appName.js +8 -0
- package/dist/components/DesktopWorkspace.d.ts +4 -0
- package/dist/components/DesktopWorkspace.js +527 -0
- package/dist/components/Player.d.ts +49 -0
- package/dist/components/Player.js +335 -0
- package/dist/components/RangePreview.d.ts +9 -0
- package/dist/components/RangePreview.js +22 -0
- package/dist/components/Workspace.css +15 -0
- package/dist/demo/DemoSettings.d.ts +1 -0
- package/dist/demo/DemoSettings.js +43 -0
- package/dist/demo/bindings.d.ts +121 -0
- package/dist/demo/bindings.js +57 -0
- package/dist/demo/demo.css +124 -0
- package/dist/demo/useDemo.d.ts +8 -0
- package/dist/demo/useDemo.js +37 -0
- package/dist/editor/CaptionInspector.d.ts +8 -0
- package/dist/editor/CaptionInspector.js +33 -0
- package/dist/editor/ChangesPanel.d.ts +13 -0
- package/dist/editor/ChangesPanel.js +87 -0
- package/dist/editor/ColourInspector.d.ts +5 -0
- package/dist/editor/ColourInspector.js +341 -0
- package/dist/editor/Inspector.d.ts +15 -0
- package/dist/editor/Inspector.js +202 -0
- package/dist/editor/Timeline.d.ts +39 -0
- package/dist/editor/Timeline.js +506 -0
- package/dist/editor/TimelineEditorView.d.ts +9 -0
- package/dist/editor/TimelineEditorView.js +237 -0
- package/dist/editor/TranscriptPanel.d.ts +20 -0
- package/dist/editor/TranscriptPanel.js +180 -0
- package/dist/editor/actions.d.ts +63 -0
- package/dist/editor/actions.js +124 -0
- package/dist/editor/adapter.d.ts +76 -0
- package/dist/editor/adapter.js +181 -0
- package/dist/editor/capabilities.d.ts +37 -0
- package/dist/editor/capabilities.js +33 -0
- package/dist/editor/gesture.d.ts +29 -0
- package/dist/editor/gesture.js +119 -0
- package/dist/editor/index.d.ts +10 -0
- package/dist/editor/index.js +7 -0
- package/dist/editor/keys.d.ts +15 -0
- package/dist/editor/keys.js +42 -0
- package/dist/editor/model.d.ts +88 -0
- package/dist/editor/model.js +87 -0
- package/dist/editor/transcript.d.ts +27 -0
- package/dist/editor/transcript.js +59 -0
- package/dist/editor/vendor/hyperframes/PlayheadIndicator.d.ts +9 -0
- package/dist/editor/vendor/hyperframes/PlayheadIndicator.js +38 -0
- package/dist/editor/vendor/hyperframes/audioWaveformPeaks.d.ts +7 -0
- package/dist/editor/vendor/hyperframes/audioWaveformPeaks.js +47 -0
- package/dist/editor/vendor/hyperframes/rulerGeometry.d.ts +13 -0
- package/dist/editor/vendor/hyperframes/rulerGeometry.js +133 -0
- package/dist/editor/vendor/hyperframes/useInspectorGestureTransaction.d.ts +33 -0
- package/dist/editor/vendor/hyperframes/useInspectorGestureTransaction.js +126 -0
- package/dist/env.d.ts +66 -0
- package/dist/hooks/useEngine.d.ts +10 -0
- package/dist/hooks/useEngine.js +68 -0
- package/dist/hooks/useHdrToSdr.d.ts +11 -0
- package/dist/hooks/useHdrToSdr.js +19 -0
- package/dist/hooks/useProgress.d.ts +10 -0
- package/dist/hooks/useProgress.js +32 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.js +57 -0
- package/dist/legal/legalConfig.d.ts +38 -0
- package/dist/legal/legalConfig.js +77 -0
- package/dist/lib/assemble.d.ts +100 -0
- package/dist/lib/assemble.js +72 -0
- package/dist/lib/asset.d.ts +2 -0
- package/dist/lib/asset.js +3 -0
- package/dist/lib/dialog.d.ts +4 -0
- package/dist/lib/dialog.js +44 -0
- package/dist/lib/engine.d.ts +44 -0
- package/dist/lib/engine.js +51 -0
- package/dist/lib/genright.d.ts +90 -0
- package/dist/lib/genright.js +160 -0
- package/dist/lib/media.d.ts +1045 -0
- package/dist/lib/media.js +171 -0
- package/dist/lib/mediaAdapters.d.ts +18 -0
- package/dist/lib/mediaAdapters.js +46 -0
- package/dist/lib/mic.d.ts +38 -0
- package/dist/lib/mic.js +14 -0
- package/dist/lib/models.d.ts +53 -0
- package/dist/lib/models.js +72 -0
- package/dist/lib/modelsBackend.d.ts +3 -0
- package/dist/lib/modelsBackend.js +68 -0
- package/dist/lib/progress.d.ts +77 -0
- package/dist/lib/progress.js +192 -0
- package/dist/lib/project.d.ts +138 -0
- package/dist/lib/project.js +48 -0
- package/dist/lib/services.d.ts +27 -0
- package/dist/lib/services.js +38 -0
- package/dist/lib/settingsBackend.d.ts +2 -0
- package/dist/lib/settingsBackend.js +57 -0
- package/dist/lib/shell.d.ts +172 -0
- package/dist/lib/shell.js +32 -0
- package/dist/styles.css +24 -0
- package/dist/ui/icons.d.ts +7 -0
- package/dist/ui/icons.js +62 -0
- package/dist/ui/index.d.ts +45 -0
- package/dist/ui/index.js +43 -0
- package/dist/ui/ui.css +104 -0
- package/dist/views/Assets.css +99 -0
- package/dist/views/AssetsView.d.ts +6 -0
- package/dist/views/AssetsView.js +371 -0
- package/dist/views/AutoEditView.d.ts +16 -0
- package/dist/views/AutoEditView.js +452 -0
- package/dist/views/BatchDelivery.d.ts +3 -0
- package/dist/views/BatchDelivery.js +331 -0
- package/dist/views/BundleExport.d.ts +3 -0
- package/dist/views/BundleExport.js +81 -0
- package/dist/views/Exports.css +117 -0
- package/dist/views/ExportsView.d.ts +12 -0
- package/dist/views/ExportsView.js +354 -0
- package/dist/views/FinishedEdit.css +546 -0
- package/dist/views/Home.css +97 -0
- package/dist/views/Home.d.ts +14 -0
- package/dist/views/Home.js +192 -0
- package/dist/views/Media.css +65 -0
- package/dist/views/OnboardingGate.d.ts +10 -0
- package/dist/views/OnboardingGate.js +54 -0
- package/dist/views/Phrases.css +45 -0
- package/dist/views/PhrasesView.d.ts +4 -0
- package/dist/views/PhrasesView.js +104 -0
- package/dist/views/ProgressView.d.ts +19 -0
- package/dist/views/ProgressView.js +36 -0
- package/dist/views/Prompt.css +84 -0
- package/dist/views/PromptPanel.d.ts +8 -0
- package/dist/views/PromptPanel.js +169 -0
- package/dist/views/Recipe.css +84 -0
- package/dist/views/RecipeLibrary.d.ts +7 -0
- package/dist/views/RecipeLibrary.js +104 -0
- package/dist/views/Retention.css +49 -0
- package/dist/views/RetentionView.d.ts +7 -0
- package/dist/views/RetentionView.js +145 -0
- package/dist/views/SettingsView.d.ts +9 -0
- package/dist/views/SettingsView.js +235 -0
- package/dist/views/Shorts.css +112 -0
- package/dist/views/ShortsView.d.ts +4 -0
- package/dist/views/ShortsView.js +205 -0
- package/dist/views/SourcesView.d.ts +6 -0
- package/dist/views/SourcesView.js +417 -0
- package/dist/views/TimelineView.d.ts +20 -0
- package/dist/views/TimelineView.js +12 -0
- package/package.json +68 -3
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { engineDoctor, engineStatus, errorMessage, } from "../lib/engine.js";
|
|
3
|
+
const POLL_MS = 1000;
|
|
4
|
+
/** Latest doctor report, read by views that need one readiness check without polling. */
|
|
5
|
+
let latestDoctor = null;
|
|
6
|
+
const doctorListeners = new Set();
|
|
7
|
+
export function subscribeDoctor(listener) {
|
|
8
|
+
doctorListeners.add(listener);
|
|
9
|
+
return () => {
|
|
10
|
+
doctorListeners.delete(listener);
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export const getDoctor = () => latestDoctor;
|
|
14
|
+
function publishDoctor(report) {
|
|
15
|
+
latestDoctor = report;
|
|
16
|
+
doctorListeners.forEach((listener) => listener());
|
|
17
|
+
}
|
|
18
|
+
/** Polls `engine_status` and loads the doctor report once the engine is ready. */
|
|
19
|
+
export function useEngine() {
|
|
20
|
+
const [status, setStatus] = useState(null);
|
|
21
|
+
const [doctor, setDoctor] = useState(null);
|
|
22
|
+
const [doctorError, setDoctorError] = useState(null);
|
|
23
|
+
const [statusError, setStatusError] = useState(null);
|
|
24
|
+
const doctorRequested = useRef(false);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
let cancelled = false;
|
|
27
|
+
const tick = async () => {
|
|
28
|
+
try {
|
|
29
|
+
const next = await engineStatus();
|
|
30
|
+
if (cancelled)
|
|
31
|
+
return;
|
|
32
|
+
setStatus(next);
|
|
33
|
+
setStatusError(null);
|
|
34
|
+
if (next.state !== "ready") {
|
|
35
|
+
doctorRequested.current = false;
|
|
36
|
+
}
|
|
37
|
+
else if (!doctorRequested.current) {
|
|
38
|
+
doctorRequested.current = true;
|
|
39
|
+
engineDoctor()
|
|
40
|
+
.then((report) => {
|
|
41
|
+
if (cancelled)
|
|
42
|
+
return;
|
|
43
|
+
setDoctor(report);
|
|
44
|
+
publishDoctor(report);
|
|
45
|
+
setDoctorError(null);
|
|
46
|
+
})
|
|
47
|
+
.catch((error) => {
|
|
48
|
+
if (cancelled)
|
|
49
|
+
return;
|
|
50
|
+
doctorRequested.current = false;
|
|
51
|
+
setDoctorError(errorMessage(error));
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (!cancelled)
|
|
57
|
+
setStatusError(errorMessage(error));
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
void tick();
|
|
61
|
+
const timer = window.setInterval(() => void tick(), POLL_MS);
|
|
62
|
+
return () => {
|
|
63
|
+
cancelled = true;
|
|
64
|
+
window.clearInterval(timer);
|
|
65
|
+
};
|
|
66
|
+
}, []);
|
|
67
|
+
return { status, doctor, doctorError, statusError };
|
|
68
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether this computer can finish HDR (PQ or HLG) footage to SDR, from the engine's
|
|
3
|
+
* optional `hdr_to_sdr` readiness check. `undefined` until the report loads or when the
|
|
4
|
+
* engine does not report the check; callers must treat `undefined` as "unknown" and
|
|
5
|
+
* disable nothing.
|
|
6
|
+
*/
|
|
7
|
+
export declare function useHdrToSdr(): boolean | undefined;
|
|
8
|
+
/** Presets that need HDR to SDR conversion. */
|
|
9
|
+
export declare const HDR_COLOR_PRESETS: readonly ["hdr-natural", "hdr-soft"];
|
|
10
|
+
/** The one-line notice shown when HDR to SDR conversion is unavailable. */
|
|
11
|
+
export declare const HDR_UNAVAILABLE_NOTICE = "HDR to SDR conversion is not available on this computer. HDR (PQ or HLG) footage cannot be finished here.";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useSyncExternalStore } from "react";
|
|
2
|
+
import { getDoctor, subscribeDoctor } from "./useEngine.js";
|
|
3
|
+
/**
|
|
4
|
+
* Whether this computer can finish HDR (PQ or HLG) footage to SDR, from the engine's
|
|
5
|
+
* optional `hdr_to_sdr` readiness check. `undefined` until the report loads or when the
|
|
6
|
+
* engine does not report the check; callers must treat `undefined` as "unknown" and
|
|
7
|
+
* disable nothing.
|
|
8
|
+
*/
|
|
9
|
+
export function useHdrToSdr() {
|
|
10
|
+
const doctor = useSyncExternalStore(subscribeDoctor, getDoctor);
|
|
11
|
+
const check = doctor?.readiness?.checks?.find((c) => c.id === "hdr_to_sdr");
|
|
12
|
+
if (!check)
|
|
13
|
+
return undefined;
|
|
14
|
+
return (check.status ?? "").toLowerCase() === "ok";
|
|
15
|
+
}
|
|
16
|
+
/** Presets that need HDR to SDR conversion. */
|
|
17
|
+
export const HDR_COLOR_PRESETS = ["hdr-natural", "hdr-soft"];
|
|
18
|
+
/** The one-line notice shown when HDR to SDR conversion is unavailable. */
|
|
19
|
+
export const HDR_UNAVAILABLE_NOTICE = "HDR to SDR conversion is not available on this computer. HDR (PQ or HLG) footage cannot be finished here.";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ProgressState, seedFromStatus } from "../lib/progress.js";
|
|
2
|
+
export type { ProgressState } from "../lib/progress.js";
|
|
3
|
+
/** Progress of the one engine call the UI is currently following (by tag, then frame id). */
|
|
4
|
+
export declare function useProgress(): {
|
|
5
|
+
progress: ProgressState;
|
|
6
|
+
begin: (kind: string, assembleId?: string | null) => string;
|
|
7
|
+
patch: (p: Partial<ProgressState>) => void;
|
|
8
|
+
seed: (status: Parameters<typeof seedFromStatus>[1]) => void;
|
|
9
|
+
reset: () => void;
|
|
10
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { onEngineEvent } from "../lib/engine.js";
|
|
3
|
+
import { beginProgress, initialProgress, reduceFrame, seedFromStatus } from "../lib/progress.js";
|
|
4
|
+
/** Progress of the one engine call the UI is currently following (by tag, then frame id). */
|
|
5
|
+
export function useProgress() {
|
|
6
|
+
const [progress, setProgress] = useState(initialProgress);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
let off;
|
|
9
|
+
let cancelled = false;
|
|
10
|
+
void onEngineEvent((frame) => setProgress((s) => reduceFrame(s, frame))).then((fn) => {
|
|
11
|
+
if (cancelled)
|
|
12
|
+
fn();
|
|
13
|
+
else
|
|
14
|
+
off = fn;
|
|
15
|
+
});
|
|
16
|
+
return () => {
|
|
17
|
+
cancelled = true;
|
|
18
|
+
off?.();
|
|
19
|
+
};
|
|
20
|
+
}, []);
|
|
21
|
+
const seq = useRef(0);
|
|
22
|
+
const begin = useCallback((kind, assembleId = null) => {
|
|
23
|
+
seq.current += 1;
|
|
24
|
+
const tag = `${kind}-${Date.now().toString(36)}-${seq.current}`;
|
|
25
|
+
setProgress(beginProgress(tag, assembleId));
|
|
26
|
+
return tag;
|
|
27
|
+
}, []);
|
|
28
|
+
const patch = useCallback((p) => setProgress((s) => ({ ...s, ...p })), []);
|
|
29
|
+
const seed = useCallback((status) => setProgress(seedFromStatus(initialProgress, status)), []);
|
|
30
|
+
const reset = useCallback(() => setProgress(initialProgress), []);
|
|
31
|
+
return { progress, begin, patch, seed, reset };
|
|
32
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export * from "./appName.js";
|
|
2
|
+
export * from "./components/DesktopWorkspace.js";
|
|
3
|
+
export * from "./components/Player.js";
|
|
4
|
+
export * from "./components/RangePreview.js";
|
|
5
|
+
export * from "./demo/DemoSettings.js";
|
|
6
|
+
export * from "./demo/bindings.js";
|
|
7
|
+
export * from "./demo/useDemo.js";
|
|
8
|
+
export * from "./editor/CaptionInspector.js";
|
|
9
|
+
export * from "./editor/ChangesPanel.js";
|
|
10
|
+
export * from "./editor/ColourInspector.js";
|
|
11
|
+
export * from "./editor/Inspector.js";
|
|
12
|
+
export * from "./editor/Timeline.js";
|
|
13
|
+
export * from "./editor/TranscriptPanel.js";
|
|
14
|
+
export * from "./editor/actions.js";
|
|
15
|
+
export * from "./editor/adapter.js";
|
|
16
|
+
export * from "./editor/capabilities.js";
|
|
17
|
+
export * from "./editor/gesture.js";
|
|
18
|
+
export * from "./editor/keys.js";
|
|
19
|
+
export * from "./editor/model.js";
|
|
20
|
+
export * from "./editor/vendor/hyperframes/PlayheadIndicator.js";
|
|
21
|
+
export * from "./editor/vendor/hyperframes/audioWaveformPeaks.js";
|
|
22
|
+
export * from "./editor/vendor/hyperframes/rulerGeometry.js";
|
|
23
|
+
export * from "./editor/vendor/hyperframes/useInspectorGestureTransaction.js";
|
|
24
|
+
export * from "./hooks/useEngine.js";
|
|
25
|
+
export * from "./hooks/useHdrToSdr.js";
|
|
26
|
+
export * from "./hooks/useProgress.js";
|
|
27
|
+
export * from "./legal/legalConfig.js";
|
|
28
|
+
export * from "./lib/assemble.js";
|
|
29
|
+
export * from "./lib/asset.js";
|
|
30
|
+
export * from "./lib/dialog.js";
|
|
31
|
+
export * from "./lib/genright.js";
|
|
32
|
+
export * from "./lib/mediaAdapters.js";
|
|
33
|
+
export * from "./lib/mic.js";
|
|
34
|
+
export * from "./lib/models.js";
|
|
35
|
+
export * from "./lib/modelsBackend.js";
|
|
36
|
+
export * from "./lib/services.js";
|
|
37
|
+
export * from "./lib/settingsBackend.js";
|
|
38
|
+
export * from "./lib/shell.js";
|
|
39
|
+
export * from "./ui/icons.js";
|
|
40
|
+
export * from "./views/AssetsView.js";
|
|
41
|
+
export * from "./views/AutoEditView.js";
|
|
42
|
+
export * from "./views/BatchDelivery.js";
|
|
43
|
+
export * from "./views/BundleExport.js";
|
|
44
|
+
export * from "./views/ExportsView.js";
|
|
45
|
+
export * from "./views/Home.js";
|
|
46
|
+
export * from "./views/OnboardingGate.js";
|
|
47
|
+
export * from "./views/PhrasesView.js";
|
|
48
|
+
export * from "./views/ProgressView.js";
|
|
49
|
+
export * from "./views/PromptPanel.js";
|
|
50
|
+
export * from "./views/RecipeLibrary.js";
|
|
51
|
+
export * from "./views/RetentionView.js";
|
|
52
|
+
export * from "./views/SettingsView.js";
|
|
53
|
+
export * from "./views/ShortsView.js";
|
|
54
|
+
export * from "./views/SourcesView.js";
|
|
55
|
+
export * from "./views/TimelineView.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Generated entry: every module whose exports do not collide with an earlier one.
|
|
2
|
+
// Every module is also importable directly by subpath, e.g. `<package>/lib/foo`.
|
|
3
|
+
export * from "./appName.js";
|
|
4
|
+
export * from "./components/DesktopWorkspace.js";
|
|
5
|
+
export * from "./components/Player.js";
|
|
6
|
+
export * from "./components/RangePreview.js";
|
|
7
|
+
export * from "./demo/DemoSettings.js";
|
|
8
|
+
export * from "./demo/bindings.js";
|
|
9
|
+
export * from "./demo/useDemo.js";
|
|
10
|
+
export * from "./editor/CaptionInspector.js";
|
|
11
|
+
export * from "./editor/ChangesPanel.js";
|
|
12
|
+
export * from "./editor/ColourInspector.js";
|
|
13
|
+
export * from "./editor/Inspector.js";
|
|
14
|
+
export * from "./editor/Timeline.js";
|
|
15
|
+
export * from "./editor/TranscriptPanel.js";
|
|
16
|
+
export * from "./editor/actions.js";
|
|
17
|
+
export * from "./editor/adapter.js";
|
|
18
|
+
export * from "./editor/capabilities.js";
|
|
19
|
+
export * from "./editor/gesture.js";
|
|
20
|
+
export * from "./editor/keys.js";
|
|
21
|
+
export * from "./editor/model.js";
|
|
22
|
+
export * from "./editor/vendor/hyperframes/PlayheadIndicator.js";
|
|
23
|
+
export * from "./editor/vendor/hyperframes/audioWaveformPeaks.js";
|
|
24
|
+
export * from "./editor/vendor/hyperframes/rulerGeometry.js";
|
|
25
|
+
export * from "./editor/vendor/hyperframes/useInspectorGestureTransaction.js";
|
|
26
|
+
export * from "./hooks/useEngine.js";
|
|
27
|
+
export * from "./hooks/useHdrToSdr.js";
|
|
28
|
+
export * from "./hooks/useProgress.js";
|
|
29
|
+
export * from "./legal/legalConfig.js";
|
|
30
|
+
export * from "./lib/assemble.js";
|
|
31
|
+
export * from "./lib/asset.js";
|
|
32
|
+
export * from "./lib/dialog.js";
|
|
33
|
+
export * from "./lib/genright.js";
|
|
34
|
+
export * from "./lib/mediaAdapters.js";
|
|
35
|
+
export * from "./lib/mic.js";
|
|
36
|
+
export * from "./lib/models.js";
|
|
37
|
+
export * from "./lib/modelsBackend.js";
|
|
38
|
+
export * from "./lib/services.js";
|
|
39
|
+
export * from "./lib/settingsBackend.js";
|
|
40
|
+
export * from "./lib/shell.js";
|
|
41
|
+
export * from "./ui/icons.js";
|
|
42
|
+
export * from "./views/AssetsView.js";
|
|
43
|
+
export * from "./views/AutoEditView.js";
|
|
44
|
+
export * from "./views/BatchDelivery.js";
|
|
45
|
+
export * from "./views/BundleExport.js";
|
|
46
|
+
export * from "./views/ExportsView.js";
|
|
47
|
+
export * from "./views/Home.js";
|
|
48
|
+
export * from "./views/OnboardingGate.js";
|
|
49
|
+
export * from "./views/PhrasesView.js";
|
|
50
|
+
export * from "./views/ProgressView.js";
|
|
51
|
+
export * from "./views/PromptPanel.js";
|
|
52
|
+
export * from "./views/RecipeLibrary.js";
|
|
53
|
+
export * from "./views/RetentionView.js";
|
|
54
|
+
export * from "./views/SettingsView.js";
|
|
55
|
+
export * from "./views/ShortsView.js";
|
|
56
|
+
export * from "./views/SourcesView.js";
|
|
57
|
+
export * from "./views/TimelineView.js";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { LegalStepConfig } from "@rightkit/onboarding";
|
|
2
|
+
/**
|
|
3
|
+
* the host app facts for the suite legal gate, sourced from its legal/legal-manifest.json and generated
|
|
4
|
+
* src/generated/legal-contract.json, never hardcoded, so a title, URL or document change cannot drift from the gate.
|
|
5
|
+
* A published package cannot import files of the app that installs it: the host calls configureLegal() once at
|
|
6
|
+
* startup. ES module exports are live bindings, so every importer sees the configured values.
|
|
7
|
+
*/
|
|
8
|
+
export interface LegalContract {
|
|
9
|
+
acceptanceVersion: string;
|
|
10
|
+
materialSha256: string;
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
}
|
|
13
|
+
/** Version the stored acceptance must match; a document change re-prompts. */
|
|
14
|
+
export declare let REQUIRED_LEGAL_ACCEPTANCE_VERSION: string;
|
|
15
|
+
/** Hash of the agreed (material) documents, recorded with the acceptance. */
|
|
16
|
+
export declare let LEGAL_MATERIAL_SHA256: string;
|
|
17
|
+
export interface LegalDocumentText {
|
|
18
|
+
title: string;
|
|
19
|
+
text: string;
|
|
20
|
+
}
|
|
21
|
+
/** The bundled copy of a shipped legal document (offline, exactly what was hashed). */
|
|
22
|
+
export declare function legalDocumentText(file: string): string;
|
|
23
|
+
/** Every document the user agrees to or acknowledges (not the build-provenance notices). */
|
|
24
|
+
export declare let LEGAL_GATE_DOCUMENTS: {
|
|
25
|
+
title: string;
|
|
26
|
+
url: string;
|
|
27
|
+
path: string;
|
|
28
|
+
}[];
|
|
29
|
+
/**
|
|
30
|
+
* Called once by the host at startup with its manifest, contract and the bundled legal texts keyed by file name
|
|
31
|
+
* (a Vite host: `import.meta.glob("/legal/*.md", { query: "?raw", import: "default", eager: true })`).
|
|
32
|
+
*/
|
|
33
|
+
export declare function configureLegal({ manifest, contract, texts }: {
|
|
34
|
+
manifest: unknown;
|
|
35
|
+
contract: LegalContract;
|
|
36
|
+
texts?: Record<string, string>;
|
|
37
|
+
}): void;
|
|
38
|
+
export declare function legalStepConfig(): LegalStepConfig;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { assertLegalManifest } from "@rightkit/legal/browser";
|
|
2
|
+
import { exit } from "@tauri-apps/plugin-process";
|
|
3
|
+
import { logs } from "../lib/services.js";
|
|
4
|
+
import { quitNow } from "../lib/shell.js";
|
|
5
|
+
import { hostAppName } from "../appName.js";
|
|
6
|
+
/** Version the stored acceptance must match; a document change re-prompts. */
|
|
7
|
+
export let REQUIRED_LEGAL_ACCEPTANCE_VERSION;
|
|
8
|
+
/** Hash of the agreed (material) documents, recorded with the acceptance. */
|
|
9
|
+
export let LEGAL_MATERIAL_SHA256;
|
|
10
|
+
// Suite-wide master titles are long; the gate list shows short ones.
|
|
11
|
+
const SHORT_TITLES = {
|
|
12
|
+
license: "License",
|
|
13
|
+
eula: "End User License Agreement",
|
|
14
|
+
acceptable_use: "Acceptable Use Policy",
|
|
15
|
+
};
|
|
16
|
+
let TEXTS = {};
|
|
17
|
+
/** The bundled copy of a shipped legal document (offline, exactly what was hashed). */
|
|
18
|
+
export function legalDocumentText(file) {
|
|
19
|
+
return TEXTS[file.split("/").pop() ?? file] ?? "";
|
|
20
|
+
}
|
|
21
|
+
/** Every document the user agrees to or acknowledges (not the build-provenance notices). */
|
|
22
|
+
export let LEGAL_GATE_DOCUMENTS = [];
|
|
23
|
+
/**
|
|
24
|
+
* Called once by the host at startup with its manifest, contract and the bundled legal texts keyed by file name
|
|
25
|
+
* (a Vite host: `import.meta.glob("/legal/*.md", { query: "?raw", import: "default", eager: true })`).
|
|
26
|
+
*/
|
|
27
|
+
export function configureLegal({ manifest, contract, texts = {} }) {
|
|
28
|
+
const m = assertLegalManifest(manifest);
|
|
29
|
+
if (typeof contract?.acceptanceVersion !== "string" || typeof contract?.materialSha256 !== "string") {
|
|
30
|
+
throw new TypeError("configureLegal: the contract needs acceptanceVersion and materialSha256 strings");
|
|
31
|
+
}
|
|
32
|
+
REQUIRED_LEGAL_ACCEPTANCE_VERSION = contract.acceptanceVersion;
|
|
33
|
+
LEGAL_MATERIAL_SHA256 = contract.materialSha256;
|
|
34
|
+
TEXTS = Object.fromEntries(Object.entries(texts).map(([k, v]) => [k.split("/").pop() ?? k, v]));
|
|
35
|
+
LEGAL_GATE_DOCUMENTS = m.documents
|
|
36
|
+
.filter((d) => d.role !== "third_party_notices")
|
|
37
|
+
.map((d) => ({ title: SHORT_TITLES[d.role] ?? d.title, url: d.publicUrl, path: String(d.path ?? "") }));
|
|
38
|
+
}
|
|
39
|
+
export function legalStepConfig() {
|
|
40
|
+
return {
|
|
41
|
+
appName: hostAppName(),
|
|
42
|
+
trustLine: `${hostAppName()} edits your footage on this computer. Your media is never uploaded by ${hostAppName()}.`,
|
|
43
|
+
// Each link expands the bundled copy inline in the gate: it works offline and is the exact text agreed to.
|
|
44
|
+
documents: LEGAL_GATE_DOCUMENTS.map((d) => ({
|
|
45
|
+
title: d.title,
|
|
46
|
+
url: d.url,
|
|
47
|
+
content: legalDocumentText(d.path),
|
|
48
|
+
contentFormat: "markdown",
|
|
49
|
+
})),
|
|
50
|
+
agreementStatement: `I have read and agree to the ${hostAppName()} Desktop License, End User License Agreement, Acceptable Use Policy, and Product Schedule, and I acknowledge the Privacy Notice.`,
|
|
51
|
+
// The suite gate is light-only: suite ivory base with the host app AA light accent.
|
|
52
|
+
theme: {
|
|
53
|
+
bg: "#FBF7EF",
|
|
54
|
+
surface: "#FFFCF5",
|
|
55
|
+
border: "#E6DECF",
|
|
56
|
+
text: "#231F1A",
|
|
57
|
+
muted: "#6B655C",
|
|
58
|
+
accent: "#0C9A43",
|
|
59
|
+
accentContrast: "#1A1208",
|
|
60
|
+
fontDisplay: "Tanker, 'Arial Narrow', sans-serif",
|
|
61
|
+
fontBody: "Author, -apple-system, BlinkMacSystemFont, sans-serif",
|
|
62
|
+
},
|
|
63
|
+
wordmarkFontUrl: "/fonts/Tanker-400.woff2",
|
|
64
|
+
// Declining quits the app. The gate stays in front until the process is gone, so no
|
|
65
|
+
// unaccepted UI is reachable. Both paths end in the host's RunEvent::Exit cleanup.
|
|
66
|
+
onQuit: async () => {
|
|
67
|
+
try {
|
|
68
|
+
await quitNow();
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
logs.error("legal_decline_quit_failed", { surface: "legal" });
|
|
72
|
+
await logs.flush().catch(() => undefined);
|
|
73
|
+
await exit(0);
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
export type ShotInput = {
|
|
2
|
+
prompt?: string;
|
|
3
|
+
kind: "video" | "image";
|
|
4
|
+
duration_s: number;
|
|
5
|
+
provider: string;
|
|
6
|
+
model: string;
|
|
7
|
+
params?: Record<string, unknown>;
|
|
8
|
+
};
|
|
9
|
+
export type NarrationInput = {
|
|
10
|
+
text: string;
|
|
11
|
+
provider: string;
|
|
12
|
+
model: string;
|
|
13
|
+
voice?: string;
|
|
14
|
+
};
|
|
15
|
+
/** TitleSpec: the template fields (`template` tag + its text fields) flattened beside timing. */
|
|
16
|
+
export type TitleInput = ({
|
|
17
|
+
template: "lower_third";
|
|
18
|
+
title: string;
|
|
19
|
+
subtitle?: string;
|
|
20
|
+
} | {
|
|
21
|
+
template: "centered";
|
|
22
|
+
text: string;
|
|
23
|
+
} | {
|
|
24
|
+
template: "caption";
|
|
25
|
+
text: string;
|
|
26
|
+
}) & {
|
|
27
|
+
start_s: number;
|
|
28
|
+
duration_s: number;
|
|
29
|
+
fade_in_s?: number;
|
|
30
|
+
fade_out_s?: number;
|
|
31
|
+
};
|
|
32
|
+
export type AssembleInput = {
|
|
33
|
+
prompt: string;
|
|
34
|
+
assemble_id: string;
|
|
35
|
+
shots: ShotInput[];
|
|
36
|
+
narration?: NarrationInput;
|
|
37
|
+
titles?: TitleInput[];
|
|
38
|
+
preset: "youtube" | "reels";
|
|
39
|
+
encoder?: string;
|
|
40
|
+
render: boolean;
|
|
41
|
+
out?: string;
|
|
42
|
+
};
|
|
43
|
+
/** What the form holds; titles keep one editable text field plus an optional subtitle. */
|
|
44
|
+
export type TitleDraft = {
|
|
45
|
+
template: "lower_third" | "centered" | "caption";
|
|
46
|
+
text: string;
|
|
47
|
+
subtitle: string;
|
|
48
|
+
start_s: number;
|
|
49
|
+
duration_s: number;
|
|
50
|
+
};
|
|
51
|
+
export type AssembleForm = {
|
|
52
|
+
prompt: string;
|
|
53
|
+
shots: ShotInput[];
|
|
54
|
+
narration: {
|
|
55
|
+
text: string;
|
|
56
|
+
provider: string;
|
|
57
|
+
model: string;
|
|
58
|
+
voice: string;
|
|
59
|
+
} | null;
|
|
60
|
+
titles: TitleDraft[];
|
|
61
|
+
preset: "youtube" | "reels";
|
|
62
|
+
render: boolean;
|
|
63
|
+
};
|
|
64
|
+
/** assemble_id: letters, digits, '-' or '_' (max 64). */
|
|
65
|
+
export declare function newAssembleId(): string;
|
|
66
|
+
export declare function titleInput(t: TitleDraft): TitleInput;
|
|
67
|
+
export declare function buildAssembleInput(form: AssembleForm, assembleId: string): AssembleInput;
|
|
68
|
+
/** The `assemble.status` result for one assemble id. */
|
|
69
|
+
export type AssembleStatus = {
|
|
70
|
+
assemble_id: string;
|
|
71
|
+
/** running | done | failed | cancelled */
|
|
72
|
+
status: string;
|
|
73
|
+
stage: string;
|
|
74
|
+
error: {
|
|
75
|
+
code?: string;
|
|
76
|
+
message?: string;
|
|
77
|
+
} | null;
|
|
78
|
+
timeline_built: boolean;
|
|
79
|
+
render: {
|
|
80
|
+
output?: string;
|
|
81
|
+
} | null;
|
|
82
|
+
/** false when the stored render's output file is missing or no render was stored; absent on an older engine. */
|
|
83
|
+
render_exists?: boolean;
|
|
84
|
+
requests: {
|
|
85
|
+
item: string;
|
|
86
|
+
request_id: string;
|
|
87
|
+
state: string | null;
|
|
88
|
+
job_id: string | null;
|
|
89
|
+
imported: boolean;
|
|
90
|
+
}[];
|
|
91
|
+
updated: number;
|
|
92
|
+
};
|
|
93
|
+
export type PendingAssemble = {
|
|
94
|
+
assembleId: string;
|
|
95
|
+
input: AssembleInput;
|
|
96
|
+
startedAt: number;
|
|
97
|
+
};
|
|
98
|
+
export declare function loadPending(dir: string): PendingAssemble | null;
|
|
99
|
+
export declare function savePending(dir: string, pending: PendingAssemble): void;
|
|
100
|
+
export declare function clearPending(dir: string): void;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Request/response shapes of `assemble`, exactly as `AssembleInput` deserializes
|
|
2
|
+
// (crates/cutright-engine/src/genright_cmd.rs; every struct there is deny_unknown_fields
|
|
3
|
+
// except TitleSpec, whose flattened TitleTemplate is).
|
|
4
|
+
/** assemble_id: letters, digits, '-' or '_' (max 64). */
|
|
5
|
+
export function newAssembleId() {
|
|
6
|
+
const rand = Math.random().toString(36).slice(2, 8);
|
|
7
|
+
return `a-${Date.now().toString(36)}-${rand}`;
|
|
8
|
+
}
|
|
9
|
+
export function titleInput(t) {
|
|
10
|
+
const timing = { start_s: t.start_s, duration_s: t.duration_s };
|
|
11
|
+
const text = t.text.trim();
|
|
12
|
+
if (t.template === "lower_third") {
|
|
13
|
+
const subtitle = t.subtitle.trim();
|
|
14
|
+
return { template: "lower_third", title: text, ...(subtitle ? { subtitle } : {}), ...timing };
|
|
15
|
+
}
|
|
16
|
+
return { template: t.template, text, ...timing };
|
|
17
|
+
}
|
|
18
|
+
export function buildAssembleInput(form, assembleId) {
|
|
19
|
+
const input = {
|
|
20
|
+
prompt: form.prompt.trim(),
|
|
21
|
+
assemble_id: assembleId,
|
|
22
|
+
shots: form.shots.map((s) => ({
|
|
23
|
+
...(s.prompt?.trim() ? { prompt: s.prompt.trim() } : {}),
|
|
24
|
+
kind: s.kind,
|
|
25
|
+
duration_s: s.duration_s,
|
|
26
|
+
provider: s.provider,
|
|
27
|
+
model: s.model,
|
|
28
|
+
})),
|
|
29
|
+
preset: form.preset,
|
|
30
|
+
render: form.render,
|
|
31
|
+
};
|
|
32
|
+
const n = form.narration;
|
|
33
|
+
if (n && n.text.trim()) {
|
|
34
|
+
input.narration = {
|
|
35
|
+
text: n.text.trim(),
|
|
36
|
+
provider: n.provider,
|
|
37
|
+
model: n.model,
|
|
38
|
+
...(n.voice.trim() ? { voice: n.voice.trim() } : {}),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (form.titles.length > 0)
|
|
42
|
+
input.titles = form.titles.map(titleInput);
|
|
43
|
+
return input;
|
|
44
|
+
}
|
|
45
|
+
const key = (dir) => `cutright.assemble.${dir}`;
|
|
46
|
+
export function loadPending(dir) {
|
|
47
|
+
try {
|
|
48
|
+
const v = JSON.parse(localStorage.getItem(key(dir)) ?? "null");
|
|
49
|
+
if (v && typeof v.assembleId === "string" && v.input && typeof v.input === "object")
|
|
50
|
+
return v;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
/* unavailable or corrupt */
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
export function savePending(dir, pending) {
|
|
58
|
+
try {
|
|
59
|
+
localStorage.setItem(key(dir), JSON.stringify(pending));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
/* ignore */
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export function clearPending(dir) {
|
|
66
|
+
try {
|
|
67
|
+
localStorage.removeItem(key(dir));
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
/* ignore */
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function pickFolder(title: string): Promise<string | null>;
|
|
2
|
+
export declare function pickMedia(): Promise<string[]>;
|
|
3
|
+
/** Pick one file (e.g. a retention CSV export). `extensions: null` = no filter. */
|
|
4
|
+
export declare function pickFile(title: string, extensions: string[] | null): Promise<string | null>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { pickFile as rkPickFile, pickFiles as rkPickFiles, pickFolder as rkPickFolder } from "@rightkit/tauri/dialog";
|
|
2
|
+
/**
|
|
3
|
+
* The rightkit-qa suite answers native dialogs through the in-app control
|
|
4
|
+
* server (served only when the QA launcher enables it): it sets this value and
|
|
5
|
+
* the next dialog returns it instead of opening an OS sheet. Release builds compile it out.
|
|
6
|
+
* The pickers are @rightkit/tauri/dialog; it has no QA answer source (its README: an app's QA answers
|
|
7
|
+
* pickers in its own seam), so this seam stays until RightKit ships one (see the exception ledger).
|
|
8
|
+
*/
|
|
9
|
+
function qaAnswer() {
|
|
10
|
+
if (!(typeof __RIGHTKIT_QA__ === "boolean" && __RIGHTKIT_QA__))
|
|
11
|
+
return undefined;
|
|
12
|
+
return window.__RIGHTKIT_QA_DIALOG__;
|
|
13
|
+
}
|
|
14
|
+
export async function pickFolder(title) {
|
|
15
|
+
const qa = qaAnswer();
|
|
16
|
+
if (qa !== undefined)
|
|
17
|
+
return typeof qa === "string" ? qa : null;
|
|
18
|
+
return rkPickFolder({ title });
|
|
19
|
+
}
|
|
20
|
+
export async function pickMedia() {
|
|
21
|
+
const qa = qaAnswer();
|
|
22
|
+
if (qa !== undefined)
|
|
23
|
+
return qa === null ? [] : Array.isArray(qa) ? qa : [qa];
|
|
24
|
+
const picked = await rkPickFiles({
|
|
25
|
+
title: "Add media",
|
|
26
|
+
filters: [
|
|
27
|
+
{
|
|
28
|
+
name: "Media",
|
|
29
|
+
extensions: ["mp4", "mov", "mkv", "webm", "m4v", "png", "jpg", "jpeg", "webp", "wav", "mp3", "m4a", "flac"],
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
});
|
|
33
|
+
return picked ?? [];
|
|
34
|
+
}
|
|
35
|
+
/** Pick one file (e.g. a retention CSV export). `extensions: null` = no filter. */
|
|
36
|
+
export async function pickFile(title, extensions) {
|
|
37
|
+
const qa = qaAnswer();
|
|
38
|
+
if (qa !== undefined)
|
|
39
|
+
return typeof qa === "string" ? qa : null;
|
|
40
|
+
return rkPickFile({
|
|
41
|
+
title,
|
|
42
|
+
filters: extensions && extensions.length > 0 ? [{ name: "Files", extensions }] : undefined,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { EngineEvent as ShellEngineEvent } from "@rightkit/tauri-shell";
|
|
2
|
+
import type { EngineFrame } from "./progress.js";
|
|
3
|
+
export type EngineState = "starting" | "ready" | "restarting" | "failed" | "stopped";
|
|
4
|
+
export type EngineHello = {
|
|
5
|
+
engine: string;
|
|
6
|
+
version: string;
|
|
7
|
+
contract_version: number;
|
|
8
|
+
};
|
|
9
|
+
export type EngineStatus = {
|
|
10
|
+
state: EngineState;
|
|
11
|
+
hello: EngineHello | null;
|
|
12
|
+
restarts: number;
|
|
13
|
+
last_error: string | null;
|
|
14
|
+
};
|
|
15
|
+
export type DoctorCheck = {
|
|
16
|
+
id?: string;
|
|
17
|
+
name?: string;
|
|
18
|
+
status?: string;
|
|
19
|
+
detail?: string;
|
|
20
|
+
message?: string;
|
|
21
|
+
};
|
|
22
|
+
export type DoctorReport = {
|
|
23
|
+
readiness?: {
|
|
24
|
+
status?: string;
|
|
25
|
+
checks?: DoctorCheck[];
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export declare const engineStatus: () => Promise<EngineStatus>;
|
|
29
|
+
export declare const engineDoctor: () => Promise<DoctorReport>;
|
|
30
|
+
export type EngineCallError = {
|
|
31
|
+
code: string;
|
|
32
|
+
message: string;
|
|
33
|
+
origin: "supervisor" | "engine";
|
|
34
|
+
};
|
|
35
|
+
export declare const engineBusy: () => boolean;
|
|
36
|
+
export declare function engineCall<T = unknown>(method: string, params?: Record<string, unknown>, tag?: string): Promise<T>;
|
|
37
|
+
export declare const revealPath: (path: string) => Promise<void>;
|
|
38
|
+
export type { EngineEvent, EngineFrame } from "./progress.js";
|
|
39
|
+
/** Every engine event payload (`{call, id, tag, event}`), lifecycle frames included. */
|
|
40
|
+
export declare const onEngineRaw: (handler: (payload: ShellEngineEvent) => void) => Promise<() => void>;
|
|
41
|
+
/** Frames are `{id: engine request id, tag: the caller's tag for that call, event}`. */
|
|
42
|
+
export declare function onEngineEvent(handler: (frame: EngineFrame) => void): Promise<() => void>;
|
|
43
|
+
export declare function errorCode(error: unknown): string | undefined;
|
|
44
|
+
export declare function errorMessage(error: unknown): string;
|