@dailies-studio/shot-editor 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/dist/assets/index-B7notge7.css +1 -0
- package/dist/assets/index-BBE6Ta9L.js +52 -0
- package/dist/index.html +13 -0
- package/package.json +45 -0
- package/src/api.ts +289 -0
- package/src/app/App.tsx +394 -0
- package/src/audio/index.tsx +74 -0
- package/src/audio/preview.ts +364 -0
- package/src/bridge.ts +45 -0
- package/src/bridgeTypes.ts +582 -0
- package/src/channels.ts +40 -0
- package/src/main.tsx +15 -0
- package/src/panels/ResizeHandle.tsx +113 -0
- package/src/panels/agent/AgentPanel.tsx +478 -0
- package/src/panels/agent/agent.css +322 -0
- package/src/panels/export/ExportDialog.tsx +284 -0
- package/src/panels/files/FilePanel.tsx +564 -0
- package/src/panels/files/McpSection.tsx +66 -0
- package/src/panels/files/icons.tsx +118 -0
- package/src/panels/header/DocSwitcher.tsx +85 -0
- package/src/panels/header/Header.tsx +132 -0
- package/src/panels/header/HistoryControls.tsx +20 -0
- package/src/panels/header/StatusBadges.tsx +49 -0
- package/src/panels/header/TransportBar.tsx +86 -0
- package/src/panels/header/ZoomControls.tsx +38 -0
- package/src/panels/help/HelpSection.tsx +190 -0
- package/src/panels/inspector/FramingRows.tsx +69 -0
- package/src/panels/inspector/InspectorPanel.tsx +178 -0
- package/src/panels/inspector/MarkerSection.tsx +63 -0
- package/src/panels/inspector/ParamRow.tsx +71 -0
- package/src/panels/inspector/animate/BlockTiming.tsx +341 -0
- package/src/panels/inspector/animate/DuringBlocks.tsx +85 -0
- package/src/panels/inspector/animate/InOutPresets.tsx +57 -0
- package/src/panels/inspector/animate/ResolvedChannels.tsx +150 -0
- package/src/panels/inspector/animate/ShotSection.tsx +126 -0
- package/src/panels/inspector/animate/useBlocks.ts +43 -0
- package/src/panels/inspector/design/AlignRow.tsx +63 -0
- package/src/panels/inspector/design/AppearanceSection.tsx +49 -0
- package/src/panels/inspector/design/ClonerSection.tsx +142 -0
- package/src/panels/inspector/design/ComponentSection.tsx +94 -0
- package/src/panels/inspector/design/CornerSection.tsx +52 -0
- package/src/panels/inspector/design/FillSection.tsx +89 -0
- package/src/panels/inspector/design/HtmlSection.tsx +50 -0
- package/src/panels/inspector/design/InstanceSection.tsx +302 -0
- package/src/panels/inspector/design/MediaSection.tsx +251 -0
- package/src/panels/inspector/design/MeshSection.tsx +172 -0
- package/src/panels/inspector/design/ShadowSection.tsx +73 -0
- package/src/panels/inspector/design/StrokeSection.tsx +85 -0
- package/src/panels/inspector/design/TransformSection.tsx +91 -0
- package/src/panels/inspector/design/TypographySection.tsx +170 -0
- package/src/panels/inspector/scene/CameraSection.tsx +59 -0
- package/src/panels/inspector/scene/EffectsSection.tsx +123 -0
- package/src/panels/inspector/scene/SceneSection.tsx +71 -0
- package/src/panels/inspector/scene/TransitionSection.tsx +136 -0
- package/src/panels/inspector/useDocWrite.ts +268 -0
- package/src/panels/material/MaterialPicker.tsx +172 -0
- package/src/panels/stage/StageMenu.tsx +38 -0
- package/src/panels/stage/StagePanel.tsx +131 -0
- package/src/panels/timeline/AudioLane.tsx +340 -0
- package/src/panels/timeline/BlockRow.tsx +242 -0
- package/src/panels/timeline/ChannelRow.tsx +266 -0
- package/src/panels/timeline/LifespanRow.tsx +165 -0
- package/src/panels/timeline/MarkerLane.tsx +146 -0
- package/src/panels/timeline/SceneStrip.tsx +145 -0
- package/src/panels/timeline/TimelineDock.tsx +488 -0
- package/src/panels/timeline/model.ts +641 -0
- package/src/panels/timeline/snap.ts +191 -0
- package/src/panels/toolbar/AddMenu.tsx +230 -0
- package/src/panels/toolbar/CameraViewBar.tsx +70 -0
- package/src/panels/toolbar/ShadingBar.tsx +42 -0
- package/src/panels/toolbar/ToolDockBar.tsx +110 -0
- package/src/panels/toolbar/ViewModeBar.tsx +49 -0
- package/src/shotTokens.css +131 -0
- package/src/stage/FrustumGizmo.tsx +184 -0
- package/src/stage/GridPlane.tsx +274 -0
- package/src/stage/MoveGizmo.tsx +411 -0
- package/src/stage/PerfHud.tsx +206 -0
- package/src/stage/SplitRenderer.tsx +511 -0
- package/src/stage/StageApp.tsx +3423 -0
- package/src/stage/StageTools.tsx +581 -0
- package/src/stage/bridgeClient.ts +278 -0
- package/src/stage/feed.ts +243 -0
- package/src/stage/frustumGeometry.ts +172 -0
- package/src/stage/gizmoMath.ts +494 -0
- package/src/stage/handles.ts +151 -0
- package/src/stage/instanceModules.ts +152 -0
- package/src/stage/interactions/move.ts +172 -0
- package/src/stage/interactions/panZoom.ts +60 -0
- package/src/stage/isolation.ts +155 -0
- package/src/stage/main.tsx +110 -0
- package/src/stage/modalState.ts +427 -0
- package/src/stage/overlays/Overlays.tsx +1046 -0
- package/src/stage/panes.ts +121 -0
- package/src/stage/penState.ts +112 -0
- package/src/stage/project.ts +402 -0
- package/src/stage/shading.ts +55 -0
- package/src/stage/tokenColor.ts +26 -0
- package/src/stage/view.ts +182 -0
- package/src/stage/workingView.ts +636 -0
- package/src/state/agentActions.ts +203 -0
- package/src/state/clientId.ts +25 -0
- package/src/state/context.tsx +33 -0
- package/src/state/insert.ts +507 -0
- package/src/state/keymap.ts +560 -0
- package/src/state/menu.ts +141 -0
- package/src/state/panelSize.ts +26 -0
- package/src/state/renderEvents.ts +100 -0
- package/src/state/store.ts +4094 -0
- package/src/state/triangles.ts +70 -0
- package/src/state/types.ts +384 -0
- package/src/styles.css +2948 -0
- package/src/tools.ts +181 -0
package/dist/index.html
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Dailies shot</title>
|
|
7
|
+
<script type="module" crossorigin src="/shot/assets/index-BBE6Ta9L.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/shot/assets/index-B7notge7.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<div id="root"></div>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dailies-studio/shot-editor",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The shot editor chrome: a prebuilt React app served static at /shot by the merged studio server, so user tooling can never break it. The 3D stage lives in src/stage and is served ONLY through the project's Vite (pinned three stack, project assets) via /shot-stage.html — never bundled into dist.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
"./package.json": "./package.json",
|
|
8
|
+
"./stage": {
|
|
9
|
+
"types": "./src/stage/main.tsx",
|
|
10
|
+
"default": "./src/stage/main.tsx"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"src"
|
|
16
|
+
],
|
|
17
|
+
"sideEffects": false,
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"react": "^19.0.0",
|
|
20
|
+
"react-dom": "^19.0.0",
|
|
21
|
+
"@dailies-studio/contract": "0.1.0",
|
|
22
|
+
"@dailies-studio/engine": "0.1.0",
|
|
23
|
+
"@dailies-studio/shot": "0.1.0",
|
|
24
|
+
"@dailies-studio/runtime": "0.1.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@react-three/fiber": "^9.6.1",
|
|
28
|
+
"@types/react": "^19.0.0",
|
|
29
|
+
"@types/react-dom": "^19.0.0",
|
|
30
|
+
"@types/three": "^0.185.1",
|
|
31
|
+
"@vitejs/plugin-react": "^4.5.0",
|
|
32
|
+
"jsdom": "^26.1.0",
|
|
33
|
+
"three": "^0.185.1",
|
|
34
|
+
"vite": "^6.3.5",
|
|
35
|
+
"@dailies-studio/agent-client": "0.1.0",
|
|
36
|
+
"@dailies-studio/transitions": "0.1.0",
|
|
37
|
+
"@dailies-studio/timeline": "0.1.0",
|
|
38
|
+
"@dailies-studio/ui": "0.1.0"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"dev": "vite",
|
|
42
|
+
"build": "vite build",
|
|
43
|
+
"typecheck": "tsc --noEmit"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/api.ts
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
// The chrome's HTTP surface: /api/shots, plus the render verb.
|
|
2
|
+
//
|
|
3
|
+
// Every write carries the client id header, so the server's change event comes
|
|
4
|
+
// back labelled and this chrome can tell its own echo from someone else's write
|
|
5
|
+
// (see state/clientId.ts). Every write also carries `baseRev`, so a lost race
|
|
6
|
+
// is a 409 the author sees rather than a silent clobber.
|
|
7
|
+
import type { Doc3D, ExportFormat, ExportQualityId, RenderStats } from "@dailies-studio/shot/doc";
|
|
8
|
+
import type { Contract } from "@dailies-studio/contract";
|
|
9
|
+
import { createAgentClient } from "@dailies-studio/agent-client";
|
|
10
|
+
import type { AgentClient, AgentTransport } from "@dailies-studio/agent-client";
|
|
11
|
+
import { CLIENT_ID, CLIENT_ID_HEADER } from "./state/clientId.js";
|
|
12
|
+
|
|
13
|
+
export interface ShotListing {
|
|
14
|
+
name: string;
|
|
15
|
+
mtimeMs: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface LoadedShot {
|
|
19
|
+
name: string;
|
|
20
|
+
doc: Doc3D;
|
|
21
|
+
rev: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SaveResult {
|
|
25
|
+
saved: true;
|
|
26
|
+
rev: string;
|
|
27
|
+
generated: string | null;
|
|
28
|
+
warning?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** A refused write, carrying enough for the chrome to say WHY. `conflict` is
|
|
32
|
+
* the 409 case specifically, because that one has a recovery ("reload and
|
|
33
|
+
* re-apply") the others do not. */
|
|
34
|
+
export class ApiError extends Error {
|
|
35
|
+
constructor(
|
|
36
|
+
readonly status: number,
|
|
37
|
+
message: string,
|
|
38
|
+
readonly reason?: string,
|
|
39
|
+
) {
|
|
40
|
+
super(message);
|
|
41
|
+
this.name = "ApiError";
|
|
42
|
+
}
|
|
43
|
+
get conflict(): boolean {
|
|
44
|
+
return this.status === 409;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function request<T>(path: string, init?: RequestInit): Promise<T> {
|
|
49
|
+
const res = await fetch(path, {
|
|
50
|
+
...init,
|
|
51
|
+
headers: {
|
|
52
|
+
...(init?.body !== undefined ? { "content-type": "application/json" } : {}),
|
|
53
|
+
[CLIENT_ID_HEADER]: CLIENT_ID,
|
|
54
|
+
...init?.headers,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
const text = await res.text();
|
|
58
|
+
let body: unknown = null;
|
|
59
|
+
if (text.length > 0) {
|
|
60
|
+
try {
|
|
61
|
+
body = JSON.parse(text);
|
|
62
|
+
} catch {
|
|
63
|
+
// A non-JSON body from a route that owes JSON is itself the error.
|
|
64
|
+
throw new ApiError(res.status, text.slice(0, 400));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (!res.ok) {
|
|
68
|
+
const b = body as { error?: unknown; reason?: unknown } | null;
|
|
69
|
+
throw new ApiError(
|
|
70
|
+
res.status,
|
|
71
|
+
typeof b?.error === "string" ? b.error : `${init?.method ?? "GET"} ${path} failed (${res.status})`,
|
|
72
|
+
typeof b?.reason === "string" ? b.reason : undefined,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
return body as T;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function listShots(): Promise<{ shots: ShotListing[] }> {
|
|
79
|
+
return request("/api/shots");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function loadShot(name: string): Promise<LoadedShot> {
|
|
83
|
+
return request(`/api/shots/${encodeURIComponent(name)}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** `baseRev` is REQUIRED at this seam even though the route allows it to be
|
|
87
|
+
* absent: an editor that omits it is an editor that can silently overwrite
|
|
88
|
+
* another surface's work, and `null` already means "I am claiming a create". */
|
|
89
|
+
export function saveShot(name: string, doc: Doc3D, baseRev: string | null): Promise<SaveResult> {
|
|
90
|
+
return request(`/api/shots/${encodeURIComponent(name)}`, {
|
|
91
|
+
method: "PUT",
|
|
92
|
+
body: JSON.stringify({ doc, baseRev }),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function deleteShot(name: string): Promise<{ deleted: true }> {
|
|
97
|
+
return request(`/api/shots/${encodeURIComponent(name)}`, { method: "DELETE" });
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function renameShot(name: string, to: string): Promise<{ renamed: true; generated: string | null }> {
|
|
101
|
+
return request(`/api/shots/${encodeURIComponent(name)}/rename`, {
|
|
102
|
+
method: "POST",
|
|
103
|
+
body: JSON.stringify({ to }),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function duplicateShot(name: string, to: string): Promise<{ duplicated: true }> {
|
|
108
|
+
return request(`/api/shots/${encodeURIComponent(name)}/duplicate`, {
|
|
109
|
+
method: "POST",
|
|
110
|
+
body: JSON.stringify({ to }),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ---- the component registry (instance layers) --------------------------------
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* One row of `GET /api/components` — the live registry the ADD menu lists and
|
|
118
|
+
* the instance inspector judges an instance against.
|
|
119
|
+
*
|
|
120
|
+
* Declared here rather than imported from another chrome: only these fields are
|
|
121
|
+
* load-bearing for this lane, and the shot chrome depends on no other chrome.
|
|
122
|
+
*
|
|
123
|
+
* `host` is the instance gate (only "three" components can be instanced — a
|
|
124
|
+
* "dom" one under the three reconciler is a hard crash, and the SERVER's
|
|
125
|
+
* `classifyHost` is what decided the value). `contractHash` is the drift
|
|
126
|
+
* baseline recorded on the layer. `contractAvailability` is the server's own
|
|
127
|
+
* verdict on whether generated code can STATICALLY IMPORT the contract or
|
|
128
|
+
* whether the document must carry a snapshot — consumed verbatim, never
|
|
129
|
+
* re-derived, because re-deriving it chrome-side got builtins wrong in the 2D
|
|
130
|
+
* lane and emitted imports that cannot resolve.
|
|
131
|
+
*/
|
|
132
|
+
export interface ComponentRow {
|
|
133
|
+
id: string;
|
|
134
|
+
name: string;
|
|
135
|
+
rel: string;
|
|
136
|
+
exportName: string;
|
|
137
|
+
contractHash: string;
|
|
138
|
+
contractSource: "declared" | "inferred";
|
|
139
|
+
contractAvailability?: "sibling" | "colocated" | "inferred";
|
|
140
|
+
host: "dom" | "three" | "dom-embedded-gl";
|
|
141
|
+
builtin?: boolean;
|
|
142
|
+
/**
|
|
143
|
+
* Present only on catalog EFFECTS, which is what makes it the filter for the
|
|
144
|
+
* material picker: a TSL generator is exactly a builtin that carries this.
|
|
145
|
+
*
|
|
146
|
+
* `thumb` is a pre-rendered PNG path under the server's own thumbs route —
|
|
147
|
+
* the reason the picker can show real renders without a single line of three
|
|
148
|
+
* in the chrome. `softwareRaster` badges a generator measured NOT to finish
|
|
149
|
+
* under `--gl swangle`, so an author learns before they pick rather than
|
|
150
|
+
* after a render has stalled.
|
|
151
|
+
*/
|
|
152
|
+
effect?: {
|
|
153
|
+
category: string;
|
|
154
|
+
tags: string[];
|
|
155
|
+
thumb?: string;
|
|
156
|
+
softwareRaster?: "unfinished";
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface ComponentDetail extends ComponentRow {
|
|
161
|
+
contract: Contract;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** A bare ARRAY, not an envelope — the shape that route has always answered. */
|
|
165
|
+
export function listComponents(): Promise<ComponentRow[]> {
|
|
166
|
+
return request("/api/components");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function componentDetail(id: string): Promise<ComponentDetail> {
|
|
170
|
+
return request(`/api/components/${encodeURIComponent(id)}`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ---- the asset store ---------------------------------------------------------
|
|
174
|
+
|
|
175
|
+
/** What `POST /api/assets` answers with. `path` is the BARE content-addressed
|
|
176
|
+
* filename; identical bytes always land on it, so a re-import is idempotent. */
|
|
177
|
+
export interface UploadedAsset {
|
|
178
|
+
path: string;
|
|
179
|
+
hash: string;
|
|
180
|
+
/** Absent for an extension the store does not recognise. */
|
|
181
|
+
kind?: string;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Upload one file into the shared asset store (the F2-C route every chrome
|
|
186
|
+
* uses; the shot lane is served by the same server, so it is the same route).
|
|
187
|
+
*
|
|
188
|
+
* The filename rides a URI-ENCODED header because HTTP headers are latin-1 and
|
|
189
|
+
* user filenames are not, and the BODY is the raw bytes — this is the one write
|
|
190
|
+
* in this module that is not JSON, so it overrides `request`'s content-type
|
|
191
|
+
* rather than letting it claim the bytes are JSON.
|
|
192
|
+
*/
|
|
193
|
+
export function uploadAsset(file: File): Promise<UploadedAsset> {
|
|
194
|
+
return request("/api/assets", {
|
|
195
|
+
method: "POST",
|
|
196
|
+
body: file,
|
|
197
|
+
headers: {
|
|
198
|
+
"content-type": "application/octet-stream",
|
|
199
|
+
"x-studio-filename": encodeURIComponent(file.name),
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* The stored bytes of an asset.
|
|
206
|
+
*
|
|
207
|
+
* Raw, and deliberately not through `request` (which owes JSON). The audio lane
|
|
208
|
+
* reads the STORED WAV rather than the upload: the server transcodes every
|
|
209
|
+
* audio import to canonical 48 kHz PCM, so the original container's duration
|
|
210
|
+
* and the stored one's can differ by a partial frame, and the clip has to be
|
|
211
|
+
* timed against what will actually be mixed.
|
|
212
|
+
*/
|
|
213
|
+
export async function fetchAssetBytes(path: string): Promise<ArrayBuffer> {
|
|
214
|
+
const url = `/studio/assets/${encodeURIComponent(path)}`;
|
|
215
|
+
const res = await fetch(url);
|
|
216
|
+
if (!res.ok) throw new ApiError(res.status, `GET ${url} -> ${res.status}`);
|
|
217
|
+
return res.arrayBuffer();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ---- the export button -------------------------------------------------------
|
|
221
|
+
|
|
222
|
+
export interface RenderJob {
|
|
223
|
+
id: string;
|
|
224
|
+
name: string;
|
|
225
|
+
phase: "running" | "done" | "failed" | "cancelled";
|
|
226
|
+
done: number;
|
|
227
|
+
total: number;
|
|
228
|
+
out?: string;
|
|
229
|
+
error?: string;
|
|
230
|
+
elapsedMs: number;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export interface RenderRequest {
|
|
234
|
+
format: ExportFormat;
|
|
235
|
+
quality: ExportQualityId;
|
|
236
|
+
/** Absent = the whole timeline. */
|
|
237
|
+
sceneId?: string;
|
|
238
|
+
/** `false` renders a SILENT file even though the document has clips. Absent
|
|
239
|
+
* = the server's default, which is to mux whatever `doc.audio` carries. */
|
|
240
|
+
audio?: boolean;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export interface RenderState {
|
|
244
|
+
job: RenderJob | null;
|
|
245
|
+
recent: RenderJob[];
|
|
246
|
+
stats: RenderStats;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Start a render. Resolves as soon as the job EXISTS, not when it finishes —
|
|
251
|
+
* progress arrives on the SSE bus.
|
|
252
|
+
*
|
|
253
|
+
* A 409 means one is already running, and its `job` rides the error rather than
|
|
254
|
+
* being thrown away, so the dialog can show that instead of an error the author
|
|
255
|
+
* cannot act on.
|
|
256
|
+
*/
|
|
257
|
+
export async function startRender(name: string, spec: RenderRequest): Promise<RenderJob> {
|
|
258
|
+
const body = await request<{ job: RenderJob }>(`/api/shots/${encodeURIComponent(name)}/render`, {
|
|
259
|
+
method: "POST",
|
|
260
|
+
body: JSON.stringify(spec),
|
|
261
|
+
});
|
|
262
|
+
return body.job;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function renderState(name: string): Promise<RenderState> {
|
|
266
|
+
return request(`/api/shots/${encodeURIComponent(name)}/render`);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function cancelRender(name: string): Promise<{ cancelled: boolean }> {
|
|
270
|
+
return request(`/api/shots/${encodeURIComponent(name)}/render`, { method: "DELETE" });
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ---- the agent lane ----------------------------------------------------------
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* The agent transport, shared with every other chrome.
|
|
277
|
+
*
|
|
278
|
+
* The shot chrome is ALWAYS served by the server it calls (there is no
|
|
279
|
+
* standalone shot lane and no hosted pairing), so this is plain same-origin
|
|
280
|
+
* fetch over this module's own error discipline. `createAgentClient` owns the
|
|
281
|
+
* routes, the SSE drain and the event vocabulary, so the four chromes cannot
|
|
282
|
+
* interpret one stream four different ways.
|
|
283
|
+
*/
|
|
284
|
+
const agentTransport: AgentTransport = {
|
|
285
|
+
json: (path, init) => request(path, init),
|
|
286
|
+
raw: (path) => fetch(path),
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
export const agent: AgentClient = createAgentClient(agentTransport);
|