@rightkit/video-ui 0.0.0-stage → 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/CHANGELOG.md +5 -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 +24 -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 +171 -0
- package/dist/lib/shell.js +32 -0
- package/dist/styles.css +16 -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 +70 -3
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
// Folds engine events into UI progress. Event names and fields are the engine's
|
|
2
|
+
// (genright_cmd.rs, cutright-genright orchestrator/engine, project_cmd.rs `progress_line`):
|
|
3
|
+
//
|
|
4
|
+
// assemble.started {shots, narration, resumed, requests:{label:request_id}, assemble_id}
|
|
5
|
+
// genright.request {request_id, state: journaled|submitted, job_id?}
|
|
6
|
+
// genright.job {request_id, job_id, status, attempts, error}
|
|
7
|
+
// genright.job_progress {request_id?, job_id, status, stage, attempt, at, message?}
|
|
8
|
+
// genright.import {request_id, job_id, asset_id, media_path, sha256}
|
|
9
|
+
// import.done {item (label), request_id, job_id, path, blake3, needs_probe} (assemble)
|
|
10
|
+
// {request_id, job_id, asset_id, path, source_id, needs_probe} (generate)
|
|
11
|
+
// timeline.built {video_clips, added_clips, titles, total_frames, revision}
|
|
12
|
+
// render.progress {frame, out_time_ms, total_ms} (inside assemble; tagged assemble_id)
|
|
13
|
+
// progress {job, frame, out_time_ms, total_ms} (standalone render)
|
|
14
|
+
// assemble.done {result:{status, assemble_id, render?:{output, ...}}}
|
|
15
|
+
// assemble.failed {code, message}
|
|
16
|
+
//
|
|
17
|
+
// Frames arrive as `{id, tag, event}` (engine request id, caller tag, engine event).
|
|
18
|
+
export const initialProgress = {
|
|
19
|
+
tag: null,
|
|
20
|
+
frameId: null,
|
|
21
|
+
assembleId: null,
|
|
22
|
+
running: false,
|
|
23
|
+
resumed: false,
|
|
24
|
+
stages: [],
|
|
25
|
+
jobs: {},
|
|
26
|
+
imports: 0,
|
|
27
|
+
render: null,
|
|
28
|
+
timeline: null,
|
|
29
|
+
error: null,
|
|
30
|
+
cancelled: false,
|
|
31
|
+
output: null,
|
|
32
|
+
done: false,
|
|
33
|
+
log: [],
|
|
34
|
+
};
|
|
35
|
+
export const assembleRenderJob = (assembleId) => `assemble-${assembleId}-render`;
|
|
36
|
+
const str = (v) => (typeof v === "string" ? v : undefined);
|
|
37
|
+
const num = (v) => (typeof v === "number" ? v : undefined);
|
|
38
|
+
const isObj = (v) => typeof v === "object" && v !== null;
|
|
39
|
+
/** Starts tracking one engine call. */
|
|
40
|
+
export function beginProgress(tag, assembleId) {
|
|
41
|
+
return { ...initialProgress, tag, assembleId, running: true };
|
|
42
|
+
}
|
|
43
|
+
function labelOf(state, requestId) {
|
|
44
|
+
return state.jobs[requestId]?.label ?? requestId;
|
|
45
|
+
}
|
|
46
|
+
function upsertJob(state, requestId, patch) {
|
|
47
|
+
const prev = state.jobs[requestId] ?? { id: requestId, label: labelOf(state, requestId), imported: false };
|
|
48
|
+
return { ...state.jobs, [requestId]: { ...prev, ...patch } };
|
|
49
|
+
}
|
|
50
|
+
const withStage = (state, name) => state.stages.includes(name) ? state.stages : [...state.stages, name];
|
|
51
|
+
export function reduceFrame(state, frame) {
|
|
52
|
+
if (state.tag === null || frame.tag !== state.tag)
|
|
53
|
+
return state;
|
|
54
|
+
if (state.frameId !== null && frame.id !== null && frame.id !== state.frameId)
|
|
55
|
+
return state;
|
|
56
|
+
const ev = frame.event;
|
|
57
|
+
const next = {
|
|
58
|
+
...state,
|
|
59
|
+
frameId: state.frameId ?? frame.id,
|
|
60
|
+
log: [...state.log.slice(-99), ev],
|
|
61
|
+
};
|
|
62
|
+
const requestId = str(ev.request_id);
|
|
63
|
+
switch (str(ev.event)) {
|
|
64
|
+
case "assemble.started": {
|
|
65
|
+
const jobs = {};
|
|
66
|
+
if (isObj(ev.requests)) {
|
|
67
|
+
for (const [label, rid] of Object.entries(ev.requests)) {
|
|
68
|
+
if (typeof rid === "string")
|
|
69
|
+
jobs[rid] = { id: rid, label, imported: false };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const assembleId = str(ev.assemble_id) ?? state.assembleId;
|
|
73
|
+
return {
|
|
74
|
+
...next,
|
|
75
|
+
assembleId,
|
|
76
|
+
running: true,
|
|
77
|
+
resumed: ev.resumed === true,
|
|
78
|
+
jobs,
|
|
79
|
+
stages: ["assemble.started"],
|
|
80
|
+
error: null,
|
|
81
|
+
cancelled: false,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
case "genright.request":
|
|
85
|
+
if (!requestId)
|
|
86
|
+
return next;
|
|
87
|
+
next.jobs = upsertJob(state, requestId, { request: str(ev.state), jobId: str(ev.job_id) });
|
|
88
|
+
return next;
|
|
89
|
+
case "genright.job":
|
|
90
|
+
if (!requestId)
|
|
91
|
+
return next;
|
|
92
|
+
next.jobs = upsertJob(state, requestId, {
|
|
93
|
+
jobId: str(ev.job_id),
|
|
94
|
+
status: str(ev.status),
|
|
95
|
+
message: isObj(ev.error) ? str(ev.error.message) : str(ev.error),
|
|
96
|
+
});
|
|
97
|
+
return next;
|
|
98
|
+
case "genright.job_progress": {
|
|
99
|
+
// job_progress carries request_id only when the orchestrator forwards it; fall back
|
|
100
|
+
// to matching the job id.
|
|
101
|
+
const jobId = str(ev.job_id);
|
|
102
|
+
const rid = requestId ?? Object.values(state.jobs).find((j) => j.jobId === jobId)?.id;
|
|
103
|
+
if (!rid)
|
|
104
|
+
return next;
|
|
105
|
+
next.jobs = upsertJob(state, rid, {
|
|
106
|
+
jobId: jobId ?? state.jobs[rid]?.jobId,
|
|
107
|
+
status: str(ev.status) ?? state.jobs[rid]?.status,
|
|
108
|
+
stage: str(ev.stage) ?? state.jobs[rid]?.stage,
|
|
109
|
+
message: str(ev.message) ?? state.jobs[rid]?.message,
|
|
110
|
+
});
|
|
111
|
+
return next;
|
|
112
|
+
}
|
|
113
|
+
case "genright.import":
|
|
114
|
+
case "import.done": {
|
|
115
|
+
if (requestId && state.jobs[requestId])
|
|
116
|
+
next.jobs = upsertJob(state, requestId, { imported: true });
|
|
117
|
+
if (str(ev.event) === "import.done")
|
|
118
|
+
next.imports = state.imports + 1;
|
|
119
|
+
return next;
|
|
120
|
+
}
|
|
121
|
+
case "timeline.built":
|
|
122
|
+
next.stages = withStage(state, "timeline.built");
|
|
123
|
+
next.timeline = {
|
|
124
|
+
video_clips: num(ev.video_clips),
|
|
125
|
+
titles: num(ev.titles),
|
|
126
|
+
total_frames: num(ev.total_frames),
|
|
127
|
+
};
|
|
128
|
+
return next;
|
|
129
|
+
case "render.progress":
|
|
130
|
+
case "progress":
|
|
131
|
+
next.stages = withStage(state, "render");
|
|
132
|
+
next.render = {
|
|
133
|
+
job: str(ev.job) ?? state.render?.job ?? (state.assembleId ? assembleRenderJob(state.assembleId) : undefined),
|
|
134
|
+
frame: num(ev.frame),
|
|
135
|
+
out_time_ms: num(ev.out_time_ms),
|
|
136
|
+
total_ms: num(ev.total_ms) ?? state.render?.total_ms,
|
|
137
|
+
};
|
|
138
|
+
return next;
|
|
139
|
+
case "assemble.done": {
|
|
140
|
+
const result = isObj(ev.result) ? ev.result : {};
|
|
141
|
+
const render = isObj(result.render) ? result.render : {};
|
|
142
|
+
next.running = false;
|
|
143
|
+
next.done = true;
|
|
144
|
+
next.output = str(render.output) ?? state.output;
|
|
145
|
+
next.stages = withStage(state, "assemble.done");
|
|
146
|
+
return next;
|
|
147
|
+
}
|
|
148
|
+
case "assemble.failed": {
|
|
149
|
+
const code = str(ev.code);
|
|
150
|
+
next.running = false;
|
|
151
|
+
if (code === "cancelled" || code === "render_cancelled") {
|
|
152
|
+
next.cancelled = true;
|
|
153
|
+
next.error = null;
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
next.error = { code, message: str(ev.message) ?? "assembly failed" };
|
|
157
|
+
}
|
|
158
|
+
return next;
|
|
159
|
+
}
|
|
160
|
+
default:
|
|
161
|
+
return next;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/** Rebuilds what is known from an `assemble.status` result (after an app restart). */
|
|
165
|
+
export function seedFromStatus(state, status) {
|
|
166
|
+
// A stored render whose file is gone is not a finished render: the render step must run again.
|
|
167
|
+
const renderMissing = !!status.render && status.render_exists === false;
|
|
168
|
+
const jobs = {};
|
|
169
|
+
for (const r of status.requests) {
|
|
170
|
+
jobs[r.request_id] = {
|
|
171
|
+
id: r.request_id,
|
|
172
|
+
label: r.item,
|
|
173
|
+
jobId: r.job_id ?? undefined,
|
|
174
|
+
request: r.state ?? undefined,
|
|
175
|
+
imported: r.imported,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
const cancelled = status.status === "cancelled";
|
|
179
|
+
return {
|
|
180
|
+
...state,
|
|
181
|
+
assembleId: status.assemble_id,
|
|
182
|
+
jobs,
|
|
183
|
+
stages: [
|
|
184
|
+
...(status.timeline_built ? ["timeline.built"] : []),
|
|
185
|
+
...(status.status === "done" && !renderMissing ? ["assemble.done"] : []),
|
|
186
|
+
],
|
|
187
|
+
done: status.status === "done" && !renderMissing,
|
|
188
|
+
cancelled,
|
|
189
|
+
output: renderMissing ? null : (status.render?.output ?? null),
|
|
190
|
+
error: status.status === "failed" && status.error ? { code: status.error.code, message: status.error.message ?? "assembly failed" } : null,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/** Timeline ticks per second (cutright-timeline TICKS_PER_SECOND). */
|
|
2
|
+
export declare const TICKS_PER_SECOND = 705600000;
|
|
3
|
+
export declare const secondsToTicks: (seconds: number) => number;
|
|
4
|
+
export declare const ticksToSeconds: (ticks: number) => number;
|
|
5
|
+
export type SourceInfo = {
|
|
6
|
+
id: string;
|
|
7
|
+
duration: number;
|
|
8
|
+
video: {
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
} | null;
|
|
12
|
+
has_audio: boolean;
|
|
13
|
+
};
|
|
14
|
+
export type VideoClip = {
|
|
15
|
+
id: string;
|
|
16
|
+
source: string;
|
|
17
|
+
source_in: number;
|
|
18
|
+
source_out: number;
|
|
19
|
+
};
|
|
20
|
+
export type AudioClip = {
|
|
21
|
+
id: string;
|
|
22
|
+
source: string;
|
|
23
|
+
trim_in: number;
|
|
24
|
+
trim_out: number;
|
|
25
|
+
start: number;
|
|
26
|
+
/** MilliDb — thousandths of a dB. */
|
|
27
|
+
gain: number;
|
|
28
|
+
link: string | null;
|
|
29
|
+
/** UnderAudio: what happens to the video audio under this clip. */
|
|
30
|
+
under: {
|
|
31
|
+
mode: "keep";
|
|
32
|
+
} | {
|
|
33
|
+
mode: "duck";
|
|
34
|
+
amount: number;
|
|
35
|
+
} | {
|
|
36
|
+
mode: "mute";
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
/** `TitleTemplate` serde form: internally tagged by `template`, text inside the template. */
|
|
40
|
+
export type TitleTemplate = {
|
|
41
|
+
template: "lower_third";
|
|
42
|
+
title: string;
|
|
43
|
+
subtitle?: string;
|
|
44
|
+
} | {
|
|
45
|
+
template: "centered";
|
|
46
|
+
text: string;
|
|
47
|
+
} | {
|
|
48
|
+
template: "caption";
|
|
49
|
+
text: string;
|
|
50
|
+
};
|
|
51
|
+
export type OverlayClip = {
|
|
52
|
+
id: string;
|
|
53
|
+
start_frame: number;
|
|
54
|
+
frames: number;
|
|
55
|
+
template: TitleTemplate;
|
|
56
|
+
fade_in_frames?: number;
|
|
57
|
+
fade_out_frames?: number;
|
|
58
|
+
};
|
|
59
|
+
export declare const titleLabel: (t: TitleTemplate) => string;
|
|
60
|
+
export type Timeline = {
|
|
61
|
+
fps: {
|
|
62
|
+
num: number;
|
|
63
|
+
den: number;
|
|
64
|
+
};
|
|
65
|
+
sources: SourceInfo[];
|
|
66
|
+
video_tracks: {
|
|
67
|
+
id: string;
|
|
68
|
+
clips: VideoClip[];
|
|
69
|
+
}[];
|
|
70
|
+
audio_tracks: {
|
|
71
|
+
id: string;
|
|
72
|
+
role: string;
|
|
73
|
+
clips: AudioClip[];
|
|
74
|
+
}[];
|
|
75
|
+
overlay_tracks: {
|
|
76
|
+
id: string;
|
|
77
|
+
clips: OverlayClip[];
|
|
78
|
+
}[];
|
|
79
|
+
};
|
|
80
|
+
/** `project.show` result. */
|
|
81
|
+
export type ProjectShow = {
|
|
82
|
+
project: {
|
|
83
|
+
id: string;
|
|
84
|
+
name: string | null;
|
|
85
|
+
dir: string;
|
|
86
|
+
fps: {
|
|
87
|
+
num: number;
|
|
88
|
+
den: number;
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
revision: {
|
|
92
|
+
cursor: number;
|
|
93
|
+
total: number;
|
|
94
|
+
can_undo: boolean;
|
|
95
|
+
can_redo: boolean;
|
|
96
|
+
};
|
|
97
|
+
summary: {
|
|
98
|
+
total_frames: number;
|
|
99
|
+
duration_ms: number;
|
|
100
|
+
sources: number;
|
|
101
|
+
video_clips: number;
|
|
102
|
+
audio_clips: number;
|
|
103
|
+
};
|
|
104
|
+
sources: {
|
|
105
|
+
info: SourceInfo;
|
|
106
|
+
name: string | null;
|
|
107
|
+
mode: string | null;
|
|
108
|
+
}[];
|
|
109
|
+
layout: {
|
|
110
|
+
track: string;
|
|
111
|
+
clip: string;
|
|
112
|
+
start_frame: number;
|
|
113
|
+
frames: number;
|
|
114
|
+
}[];
|
|
115
|
+
timeline: Timeline;
|
|
116
|
+
/** Engine extension ids the editor may open capability gates for. */
|
|
117
|
+
extensions?: string[];
|
|
118
|
+
/** Sources carrying a validated transcript under analysis/ (word-edge evidence). */
|
|
119
|
+
transcripts?: {
|
|
120
|
+
source: string;
|
|
121
|
+
}[];
|
|
122
|
+
};
|
|
123
|
+
export declare const projectShow: (dir: string) => Promise<ProjectShow>;
|
|
124
|
+
/**
|
|
125
|
+
* Appends `source` [startMs, endMs) to the end of `track`. The range is clamped to
|
|
126
|
+
* the source's real duration: word timings can run a few ms past the media's end,
|
|
127
|
+
* and the engine rejects out-of-range indices and source ranges.
|
|
128
|
+
*/
|
|
129
|
+
export declare function appendClip(dir: string, source: string, startMs: number, endMs: number, track?: string): Promise<unknown>;
|
|
130
|
+
export declare const projectInit: (dir: string, name?: string) => Promise<unknown>;
|
|
131
|
+
export declare const fpsValue: (fps: {
|
|
132
|
+
num: number;
|
|
133
|
+
den: number;
|
|
134
|
+
}) => number;
|
|
135
|
+
export declare function formatSeconds(seconds: number): string;
|
|
136
|
+
export declare function baseName(path: string): string;
|
|
137
|
+
/** Collision-resistant clip id for new timeline clips. */
|
|
138
|
+
export declare function newClipId(): string;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { engineCall } from "./engine.js";
|
|
2
|
+
/** Timeline ticks per second (cutright-timeline TICKS_PER_SECOND). */
|
|
3
|
+
export const TICKS_PER_SECOND = 705_600_000;
|
|
4
|
+
export const secondsToTicks = (seconds) => Math.round(seconds * TICKS_PER_SECOND);
|
|
5
|
+
export const ticksToSeconds = (ticks) => ticks / TICKS_PER_SECOND;
|
|
6
|
+
export const titleLabel = (t) => (t.template === "lower_third" ? t.title : t.text);
|
|
7
|
+
export const projectShow = (dir) => engineCall("project.show", { dir });
|
|
8
|
+
/**
|
|
9
|
+
* Appends `source` [startMs, endMs) to the end of `track`. The range is clamped to
|
|
10
|
+
* the source's real duration: word timings can run a few ms past the media's end,
|
|
11
|
+
* and the engine rejects out-of-range indices and source ranges.
|
|
12
|
+
*/
|
|
13
|
+
export async function appendClip(dir, source, startMs, endMs, track = "V1") {
|
|
14
|
+
const show = await projectShow(dir);
|
|
15
|
+
const duration = show.timeline.sources.find((s) => s.id === source)?.duration ?? Infinity;
|
|
16
|
+
const index = show.timeline.video_tracks.find((t) => t.id === track)?.clips.length ?? 0;
|
|
17
|
+
return engineCall("edit", {
|
|
18
|
+
dir,
|
|
19
|
+
action: {
|
|
20
|
+
type: "insert_clip",
|
|
21
|
+
track_id: track,
|
|
22
|
+
index,
|
|
23
|
+
clip: {
|
|
24
|
+
id: newClipId(),
|
|
25
|
+
source,
|
|
26
|
+
source_in: Math.max(0, secondsToTicks(startMs / 1000)),
|
|
27
|
+
source_out: Math.min(duration, secondsToTicks(endMs / 1000)),
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export const projectInit = (dir, name) => engineCall("project.init", name ? { dir, name } : { dir });
|
|
33
|
+
export const fpsValue = (fps) => fps.num / fps.den;
|
|
34
|
+
export function formatSeconds(seconds) {
|
|
35
|
+
if (!Number.isFinite(seconds))
|
|
36
|
+
return "-";
|
|
37
|
+
const m = Math.floor(seconds / 60);
|
|
38
|
+
const s = seconds - m * 60;
|
|
39
|
+
return m > 0 ? `${m}:${s.toFixed(1).padStart(4, "0")}` : `${s.toFixed(1)}s`;
|
|
40
|
+
}
|
|
41
|
+
export function baseName(path) {
|
|
42
|
+
const parts = path.split(/[\\/]+/).filter(Boolean);
|
|
43
|
+
return parts[parts.length - 1] ?? path;
|
|
44
|
+
}
|
|
45
|
+
/** Collision-resistant clip id for new timeline clips. */
|
|
46
|
+
export function newClipId() {
|
|
47
|
+
return `clip-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`;
|
|
48
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The licence runs natively (src-tauri/src/licence.rs behind the shell's `rightkit_license_*` commands).
|
|
3
|
+
* The renderer holds no token, key or device id: it reads fixed-schema views, sends the licence key or
|
|
4
|
+
* trial e-mail once as a command argument, and hears `rightkit://license-changed`. Native code authorizes
|
|
5
|
+
* every protected operation itself; a view is a display hint.
|
|
6
|
+
*/
|
|
7
|
+
export declare const licenseClient: {
|
|
8
|
+
status: () => Promise<import("@rightkit/license").LicenseView>;
|
|
9
|
+
activate: (key: string) => Promise<import("@rightkit/license").LicenseView>;
|
|
10
|
+
refresh: () => Promise<import("@rightkit/license").RefreshView>;
|
|
11
|
+
deactivate: () => Promise<import("@rightkit/license").LicenseView>;
|
|
12
|
+
requestTrial: (email: string) => Promise<import("@rightkit/license").TrialView>;
|
|
13
|
+
onChange(callback: (view: import("@rightkit/license").LicenseView) => void, onError?: (error: import("@rightkit/license").LicenseClientError) => void): Promise<import("@rightkit/license").LicenseUnlisten>;
|
|
14
|
+
};
|
|
15
|
+
export declare const logs: import("@rightkit/logs").LogsClient;
|
|
16
|
+
export declare function setUpdateBusy(busy: boolean): void;
|
|
17
|
+
export declare function restartAfterUpdate(): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Patch releases only. An entitlement-gated "update" release needs the licence's update authorization,
|
|
20
|
+
* which exists only natively (rightkit-tauri-shell `update-license`); the renderer no longer holds the
|
|
21
|
+
* tokens that authorization used to send, so those releases are refused here until the native updater
|
|
22
|
+
* replaces this client.
|
|
23
|
+
*/
|
|
24
|
+
export declare const updates: import("@rightkit/app-update").AutoUpdateClient;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { invoke } from "@tauri-apps/api/core";
|
|
2
|
+
import { listen } from "@tauri-apps/api/event";
|
|
3
|
+
import { createAutoUpdateClient } from "@rightkit/app-update";
|
|
4
|
+
import { createLicenseClient } from "@rightkit/license";
|
|
5
|
+
import { createLogsClient } from "@rightkit/logs";
|
|
6
|
+
import { engineBusy } from "./engine.js";
|
|
7
|
+
/**
|
|
8
|
+
* The licence runs natively (src-tauri/src/licence.rs behind the shell's `rightkit_license_*` commands).
|
|
9
|
+
* The renderer holds no token, key or device id: it reads fixed-schema views, sends the licence key or
|
|
10
|
+
* trial e-mail once as a command argument, and hears `rightkit://license-changed`. Native code authorizes
|
|
11
|
+
* every protected operation itself; a view is a display hint.
|
|
12
|
+
*/
|
|
13
|
+
export const licenseClient = createLicenseClient({ invoke, listen });
|
|
14
|
+
export const logs = createLogsClient({ invoke, allowedFields: ["surface", "state", "code"] });
|
|
15
|
+
let updateBusy = false;
|
|
16
|
+
export function setUpdateBusy(busy) {
|
|
17
|
+
updateBusy = busy;
|
|
18
|
+
}
|
|
19
|
+
export async function restartAfterUpdate() {
|
|
20
|
+
const { relaunch } = await import("@tauri-apps/plugin-process");
|
|
21
|
+
await relaunch();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Patch releases only. An entitlement-gated "update" release needs the licence's update authorization,
|
|
25
|
+
* which exists only natively (rightkit-tauri-shell `update-license`); the renderer no longer holds the
|
|
26
|
+
* tokens that authorization used to send, so those releases are refused here until the native updater
|
|
27
|
+
* replaces this client.
|
|
28
|
+
*/
|
|
29
|
+
export const updates = createAutoUpdateClient({
|
|
30
|
+
check: async (options) => {
|
|
31
|
+
const { check } = await import("@tauri-apps/plugin-updater");
|
|
32
|
+
return check(options);
|
|
33
|
+
},
|
|
34
|
+
// Closing a project also stops preview; engine calls cover background requests from other views.
|
|
35
|
+
canInstall: () => !updateBusy && !engineBusy(),
|
|
36
|
+
deferReason: () => "Close your project & finish media work before installing an update.",
|
|
37
|
+
isEligible: (tier) => tier === "patch",
|
|
38
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { errorCode, errorMessage } from "./engine.js";
|
|
2
|
+
import { settingsGet, settingsSet } from "./media.js";
|
|
3
|
+
import { themeStore } from "./shell.js";
|
|
4
|
+
/**
|
|
5
|
+
* RightKit settings port over the engine's app-level settings file (the same
|
|
6
|
+
* store the engine reads for finishing defaults, project folder, the host app
|
|
7
|
+
* path). `appearance.theme` is mirrored from the app-shell theme store, which
|
|
8
|
+
* stays the single owner of the theme choice. `null` removes a key.
|
|
9
|
+
*/
|
|
10
|
+
const asError = (e) => {
|
|
11
|
+
throw e instanceof Error ? e : Object.assign(new Error(errorMessage(e)), { code: errorCode(e) });
|
|
12
|
+
};
|
|
13
|
+
export function createHostSettingsBackend() {
|
|
14
|
+
// The store's revision is a fingerprint of the file that can exceed 2^53, so the engine reports
|
|
15
|
+
// it as a string. The kit sees a number: each snapshot gets a token, remembered with the engine's
|
|
16
|
+
// revision it stands for, and a patch that names a token we did not hand out is stale.
|
|
17
|
+
let token = 0;
|
|
18
|
+
const revisions = new Map();
|
|
19
|
+
const snapshot = (values, engineRevision) => {
|
|
20
|
+
token += 1;
|
|
21
|
+
revisions.set(token, engineRevision);
|
|
22
|
+
revisions.delete(token - 32);
|
|
23
|
+
return { values: { ...values, "appearance.theme": themeStore.read() }, revision: token };
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
// The engine rejects with a {code, message} object; the settings kit shows
|
|
27
|
+
// `Error.message`, so every failure leaves here as an Error.
|
|
28
|
+
async load() {
|
|
29
|
+
const s = await settingsGet().catch(asError);
|
|
30
|
+
return snapshot(s.app, s.revision);
|
|
31
|
+
},
|
|
32
|
+
async patch(patch, expectedRevision) {
|
|
33
|
+
const rest = {};
|
|
34
|
+
for (const [k, v] of Object.entries(patch)) {
|
|
35
|
+
if (k === "appearance.theme")
|
|
36
|
+
continue; // themeStore persisted it
|
|
37
|
+
rest[k] = v;
|
|
38
|
+
}
|
|
39
|
+
let expected;
|
|
40
|
+
if (expectedRevision !== undefined) {
|
|
41
|
+
expected = revisions.get(expectedRevision);
|
|
42
|
+
if (expected === undefined) {
|
|
43
|
+
throw Object.assign(new Error("Settings changed since they were read."), { code: "revision_conflict" });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const r = await (Object.keys(rest).length
|
|
47
|
+
? settingsSet(rest, undefined, expected)
|
|
48
|
+
: settingsGet().then((s) => ({ settings: s.app, revision: s.revision }))).catch((e) => {
|
|
49
|
+
if (e && typeof e === "object" && e.code === "revision_conflict") {
|
|
50
|
+
throw Object.assign(new Error(errorMessage(e)), { code: "revision_conflict" });
|
|
51
|
+
}
|
|
52
|
+
return asError(e);
|
|
53
|
+
});
|
|
54
|
+
return snapshot(r.settings, r.revision);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { type AppShellBridge } from "@rightkit/react-shell";
|
|
2
|
+
/** Persisted light/dark/system choice (same key the pre-RightKit app used, so
|
|
3
|
+
* existing users keep their theme). */
|
|
4
|
+
export declare const themeStore: {
|
|
5
|
+
read: () => import("@rightkit/react-shell").ThemeChoice;
|
|
6
|
+
resolved: (choice?: import("@rightkit/react-shell").ThemeChoice) => import("@rightkit/react-shell").ResolvedTheme;
|
|
7
|
+
apply: (choice: import("@rightkit/react-shell").ThemeChoice) => void;
|
|
8
|
+
save: (choice: import("@rightkit/react-shell").ThemeChoice) => void;
|
|
9
|
+
subscribe: (listener: (choice: import("@rightkit/react-shell").ThemeChoice) => void) => () => void;
|
|
10
|
+
dispose: () => void;
|
|
11
|
+
};
|
|
12
|
+
export declare const shell: {
|
|
13
|
+
ready: () => Promise<{
|
|
14
|
+
background: boolean;
|
|
15
|
+
appId: string;
|
|
16
|
+
appName: string;
|
|
17
|
+
engine: {
|
|
18
|
+
state: 'starting' | 'ready' | 'error';
|
|
19
|
+
hello?: unknown;
|
|
20
|
+
code?: string;
|
|
21
|
+
message?: string;
|
|
22
|
+
} | null;
|
|
23
|
+
}>;
|
|
24
|
+
status: () => Promise<import("@rightkit/tauri-shell").ShellStatus>;
|
|
25
|
+
capabilities: () => Promise<import("@rightkit/tauri-shell").ShellCapabilities>;
|
|
26
|
+
quit: (quitOptions?: import("@rightkit/tauri-shell").QuitOptions) => Promise<import("@rightkit/tauri-shell").QuitReport>;
|
|
27
|
+
onQuitRequested: (handler: import("@rightkit/tauri-shell").QuitHandler) => Promise<import("@rightkit/tauri-shell").Unlisten>;
|
|
28
|
+
openExternal: (url: string) => Promise<void>;
|
|
29
|
+
window: {
|
|
30
|
+
do: (action: import("@rightkit/tauri-shell").WindowAction) => Promise<void>;
|
|
31
|
+
minimize: () => Promise<void>;
|
|
32
|
+
toggleMaximize: () => Promise<void>;
|
|
33
|
+
close: () => Promise<void>;
|
|
34
|
+
startDrag: () => Promise<void>;
|
|
35
|
+
open: (spec: import("@rightkit/tauri-shell").WindowSpec) => Promise<string>;
|
|
36
|
+
trafficLights: (dx: number, dy: number) => Promise<void>;
|
|
37
|
+
};
|
|
38
|
+
menu: {
|
|
39
|
+
setEnabled: (id: string, enabled: boolean) => Promise<void>;
|
|
40
|
+
setChecked: (id: string, checked: boolean) => Promise<void>;
|
|
41
|
+
setState: (updates: import("@rightkit/tauri-shell").MenuItemState[]) => Promise<void>;
|
|
42
|
+
onClick: (h: (id: string) => void) => Promise<import("@rightkit/tauri-shell").Unlisten>;
|
|
43
|
+
};
|
|
44
|
+
tray: {
|
|
45
|
+
update: (update: import("@rightkit/tauri-shell").TrayUpdate) => Promise<void>;
|
|
46
|
+
onEvent: (h: (e: {
|
|
47
|
+
kind: 'menu';
|
|
48
|
+
id: string;
|
|
49
|
+
} | {
|
|
50
|
+
kind: 'click';
|
|
51
|
+
}) => void) => Promise<import("@rightkit/tauri-shell").Unlisten>;
|
|
52
|
+
onCustom: (name: string, h: (payload: unknown) => void) => Promise<import("@rightkit/tauri-shell").Unlisten>;
|
|
53
|
+
};
|
|
54
|
+
deepLinks: {
|
|
55
|
+
on: (h: (link: import("@rightkit/tauri-shell").DeepLink) => void) => Promise<import("@rightkit/tauri-shell").Unlisten>;
|
|
56
|
+
};
|
|
57
|
+
onSecondInstance: (h: (e: {
|
|
58
|
+
argv: string[];
|
|
59
|
+
cwd: string;
|
|
60
|
+
}) => void) => Promise<import("@rightkit/tauri-shell").Unlisten>;
|
|
61
|
+
onStall: (h: (e: {
|
|
62
|
+
stalledMs: number;
|
|
63
|
+
thresholdMs: number;
|
|
64
|
+
}) => void) => Promise<import("@rightkit/tauri-shell").Unlisten>;
|
|
65
|
+
autostart: {
|
|
66
|
+
get: () => Promise<import("@rightkit/tauri-shell").AutostartStatus>;
|
|
67
|
+
set: (enabled: boolean) => Promise<import("@rightkit/tauri-shell").AutostartStatus>;
|
|
68
|
+
openLoginItemsSettings: () => Promise<void>;
|
|
69
|
+
};
|
|
70
|
+
clipboard: {
|
|
71
|
+
read: (board?: string) => Promise<import("@rightkit/tauri-shell").ClipboardContent>;
|
|
72
|
+
write: (c: import("@rightkit/tauri-shell").ClipboardContent) => Promise<void>;
|
|
73
|
+
item: () => Promise<import("@rightkit/tauri-shell").ClipboardItem>;
|
|
74
|
+
};
|
|
75
|
+
notifications: {
|
|
76
|
+
post: (request: import("@rightkit/tauri-shell").NotificationRequest) => Promise<import("@rightkit/tauri-shell").NotifyOutcome>;
|
|
77
|
+
status: () => Promise<import("@rightkit/tauri-shell").NotifyStatus>;
|
|
78
|
+
clear: (ids?: string[]) => Promise<void>;
|
|
79
|
+
setBadge: (count: number) => Promise<number>;
|
|
80
|
+
};
|
|
81
|
+
presence: {
|
|
82
|
+
state: () => Promise<import("@rightkit/tauri-shell").PresenceState>;
|
|
83
|
+
setDnd: (enabled: boolean) => Promise<import("@rightkit/tauri-shell").PresenceState>;
|
|
84
|
+
setAuto: (prefs: import("@rightkit/tauri-shell").AutoPrefs) => Promise<import("@rightkit/tauri-shell").PresenceState>;
|
|
85
|
+
probe: () => Promise<{
|
|
86
|
+
environment: import("@rightkit/tauri-shell").Environment;
|
|
87
|
+
windows: number;
|
|
88
|
+
supported: boolean;
|
|
89
|
+
}>;
|
|
90
|
+
evaluate: (monitor: import("@rightkit/tauri-shell").Rect, windows: import("@rightkit/tauri-shell").WindowObservation[], apply?: boolean) => Promise<{
|
|
91
|
+
environment: import("@rightkit/tauri-shell").Environment;
|
|
92
|
+
state: import("@rightkit/tauri-shell").PresenceState;
|
|
93
|
+
}>;
|
|
94
|
+
onChange: (h: (s: import("@rightkit/tauri-shell").PresenceState) => void) => Promise<import("@rightkit/tauri-shell").Unlisten>;
|
|
95
|
+
};
|
|
96
|
+
idle: {
|
|
97
|
+
status: () => Promise<import("@rightkit/tauri-shell").IdleStatus>;
|
|
98
|
+
note: (hidden: boolean) => Promise<void>;
|
|
99
|
+
onChange: (h: (e: {
|
|
100
|
+
state: 'hidden' | 'visible' | 'suspended' | 'resumed';
|
|
101
|
+
}) => void) => Promise<import("@rightkit/tauri-shell").Unlisten>;
|
|
102
|
+
};
|
|
103
|
+
paths: {
|
|
104
|
+
require: (path: string, write?: boolean) => Promise<string>;
|
|
105
|
+
revoke: (path: string) => Promise<void>;
|
|
106
|
+
grantRecent: (id: string) => Promise<string>;
|
|
107
|
+
regrantSaved: (path: string) => Promise<string>;
|
|
108
|
+
streamUrl: (path: string) => Promise<{
|
|
109
|
+
id: string;
|
|
110
|
+
url: string;
|
|
111
|
+
}>;
|
|
112
|
+
};
|
|
113
|
+
openPaths: {
|
|
114
|
+
ready: () => Promise<string[]>;
|
|
115
|
+
onOpen: (h: (e: {
|
|
116
|
+
paths: string[];
|
|
117
|
+
}) => void) => Promise<import("@rightkit/tauri-shell").Unlisten>;
|
|
118
|
+
};
|
|
119
|
+
associations: {
|
|
120
|
+
status: (groups?: string[]) => Promise<import("@rightkit/tauri-shell").AssociationStatus>;
|
|
121
|
+
set: (groups?: string[], timeoutMs?: number) => Promise<import("@rightkit/tauri-shell").AssociationSetResult>;
|
|
122
|
+
openDefaultApps: () => Promise<void>;
|
|
123
|
+
};
|
|
124
|
+
print: (ui?: import("@rightkit/tauri-shell").PrintUi) => Promise<void>;
|
|
125
|
+
onPrintError: (h: (message: string) => void) => Promise<import("@rightkit/tauri-shell").Unlisten>;
|
|
126
|
+
recovery: {
|
|
127
|
+
save: (snapshot: unknown) => Promise<void>;
|
|
128
|
+
load: <T = unknown>() => Promise<T | null>;
|
|
129
|
+
clear: () => Promise<void>;
|
|
130
|
+
};
|
|
131
|
+
site: {
|
|
132
|
+
grant: (document: string) => Promise<import("@rightkit/tauri-shell").SiteGrant>;
|
|
133
|
+
revoke: (token: string) => Promise<void>;
|
|
134
|
+
};
|
|
135
|
+
surface: {
|
|
136
|
+
set: (surface: string) => Promise<import("@rightkit/tauri-shell").SurfaceReport>;
|
|
137
|
+
};
|
|
138
|
+
preview: {
|
|
139
|
+
register: (kind: string, html: string) => Promise<import("@rightkit/tauri-shell").PreviewHandle>;
|
|
140
|
+
revoke: (token: string) => Promise<void>;
|
|
141
|
+
revokeAll: () => Promise<void>;
|
|
142
|
+
};
|
|
143
|
+
legal: {
|
|
144
|
+
read: () => Promise<import("@rightkit/tauri-shell").LegalReceiptRecord | null>;
|
|
145
|
+
create: (request: import("@rightkit/tauri-shell").LegalReceiptCreateRequest) => Promise<import("@rightkit/tauri-shell").LegalReceiptRecord>;
|
|
146
|
+
reset: () => Promise<void>;
|
|
147
|
+
};
|
|
148
|
+
update: {
|
|
149
|
+
check: () => Promise<import("@rightkit/tauri-shell").UpdateHostCheck>;
|
|
150
|
+
status: () => Promise<import("@rightkit/tauri-shell").UpdateHostStatus>;
|
|
151
|
+
download: (handleId: string) => Promise<import("@rightkit/tauri-shell").UpdateHostDownload>;
|
|
152
|
+
install: (handleId: string) => Promise<import("@rightkit/tauri-shell").UpdateHostInstall>;
|
|
153
|
+
cancel: (handleId: string) => Promise<{
|
|
154
|
+
cancelled: boolean;
|
|
155
|
+
}>;
|
|
156
|
+
onProgress: (h: (p: import("@rightkit/tauri-shell").UpdateHostProgress) => void) => Promise<import("@rightkit/tauri-shell").Unlisten>;
|
|
157
|
+
};
|
|
158
|
+
engine: {
|
|
159
|
+
call: <T = unknown>(method: string, params?: unknown) => Promise<T>;
|
|
160
|
+
callTagged: <T = unknown>(method: string, params?: unknown, tag?: string) => Promise<T>;
|
|
161
|
+
status: () => Promise<import("@rightkit/tauri-shell").EngineStatus | null>;
|
|
162
|
+
onEvent: (h: (e: import("@rightkit/tauri-shell").EngineEvent) => void) => Promise<import("@rightkit/tauri-shell").Unlisten>;
|
|
163
|
+
};
|
|
164
|
+
bridge: () => import("@rightkit/tauri-shell").ShellBridge;
|
|
165
|
+
};
|
|
166
|
+
/** Native side of @rightkit/react-shell: caption buttons on Windows; macOS draws
|
|
167
|
+
* its traffic lights through the overlay title bar. The caption actions reach the
|
|
168
|
+
* host's `rk_shell_window` command (rightkit-tauri-shell) through @rightkit/tauri-shell. */
|
|
169
|
+
export declare const shellBridge: AppShellBridge;
|
|
170
|
+
/** Quits the app at once through the shell host (`rk_shell_quit`): no drain handlers run. */
|
|
171
|
+
export declare const quitNow: () => Promise<import("@rightkit/tauri-shell").QuitReport>;
|