@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
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// What a scene's geometry costs, and when to say something about it.
|
|
2
|
+
//
|
|
3
|
+
// ── WHY A BUDGET AT ALL ─────────────────────────────────────────────────
|
|
4
|
+
//
|
|
5
|
+
// A model's triangle count is the one authoring decision in this lane that can
|
|
6
|
+
// make the editor unusable, and it is the one an author has no way to see. A
|
|
7
|
+
// GLB is a file with a name; nothing on screen distinguishes a 4k-triangle prop
|
|
8
|
+
// from a 900k-triangle scan until the pane stops keeping up, and by then the
|
|
9
|
+
// document is built around it.
|
|
10
|
+
//
|
|
11
|
+
// ── AND WHY THE TWO NUMBERS ARE WHAT THEY ARE ───────────────────────────
|
|
12
|
+
//
|
|
13
|
+
// The split viewport draws the scene ONCE PER PANE, so an authored count is
|
|
14
|
+
// doubled before it reaches the GPU, and every one of those frames is a
|
|
15
|
+
// re-draw the author is watching happen under their hands.
|
|
16
|
+
//
|
|
17
|
+
// SOFT 500k (1M drawn) is where a per-commit redraw stops being instant on
|
|
18
|
+
// integrated graphics with this lane's unlit single-pipeline materials. It
|
|
19
|
+
// WARNS: the outliner tints the badges and the title says why. A hard block
|
|
20
|
+
// here would refuse work that renders perfectly well on a workstation.
|
|
21
|
+
//
|
|
22
|
+
// HARD 2M (4M drawn) is where even discrete hardware drops below a usable
|
|
23
|
+
// frame rate for a drag. It refuses the INSERT — the moment a model is added
|
|
24
|
+
// is the only moment refusing costs nothing, because nothing has been built
|
|
25
|
+
// on it yet. An already-heavy document still opens and still edits: a cap
|
|
26
|
+
// that locked an author out of their own file would be the worse failure.
|
|
27
|
+
//
|
|
28
|
+
// Both are reasoned from how the lane draws rather than measured on target
|
|
29
|
+
// hardware, and the HUD's `tris` readout is what will eventually replace the
|
|
30
|
+
// reasoning with numbers.
|
|
31
|
+
export const TRIANGLE_SOFT_BUDGET = 500_000;
|
|
32
|
+
export const TRIANGLE_HARD_CAP = 2_000_000;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The scene's total.
|
|
36
|
+
*
|
|
37
|
+
* A SUM OVER LOADED MODELS, and it under-counts on purpose in one place: a
|
|
38
|
+
* cloner draws its subtree N times and this counts it once. Multiplying would
|
|
39
|
+
* need the clone count at this level, which is a document walk for a number
|
|
40
|
+
* whose job is to be a rough warning.
|
|
41
|
+
*
|
|
42
|
+
* ponytail: multiply by clone count when someone ships a cloned model heavy
|
|
43
|
+
* enough that the badge disagrees with the frame rate.
|
|
44
|
+
*/
|
|
45
|
+
export function sumTriangles(byLayer: Readonly<Record<string, number>>): number {
|
|
46
|
+
let total = 0;
|
|
47
|
+
for (const n of Object.values(byLayer)) total += n;
|
|
48
|
+
return total;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** `12.4k`, `1.2M`. Short enough for a row that already carries an icon, a
|
|
52
|
+
* name and two toggles. */
|
|
53
|
+
export function formatTriangles(count: number): string {
|
|
54
|
+
if (count >= 1_000_000) return `${(count / 1_000_000).toFixed(1)}M`;
|
|
55
|
+
if (count >= 1_000) return `${(count / 1_000).toFixed(count >= 10_000 ? 0 : 1)}k`;
|
|
56
|
+
return String(count);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** The soft-budget message, on the badge's own title. */
|
|
60
|
+
export function sceneBudgetWarning(total: number): string {
|
|
61
|
+
return (
|
|
62
|
+
`THIS SCENE CARRIES ${total.toLocaleString()} TRIANGLES. ` +
|
|
63
|
+
`ABOVE ${TRIANGLE_SOFT_BUDGET.toLocaleString()} THE WORKING PANE STOPS FEELING INSTANT.`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** The insert refusal, verbatim. */
|
|
68
|
+
export const TRIANGLE_CAP_REFUSAL =
|
|
69
|
+
`A SCENE HOLDS AT MOST ${TRIANGLE_HARD_CAP.toLocaleString()} TRIANGLES. ` +
|
|
70
|
+
`REMOVE OR SIMPLIFY A MODEL BEFORE ADDING ANOTHER.`;
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
// The shot chrome's state shape. ONE document, flat fields.
|
|
2
|
+
//
|
|
3
|
+
// ── WHY THERE ARE NO BOARDS ───────────────────────────────────────────────
|
|
4
|
+
//
|
|
5
|
+
// The canvas lane keeps N documents open on an infinite pasteboard because a
|
|
6
|
+
// Doc2D is a COMPONENT and a project holds dozens. A shot document is a
|
|
7
|
+
// DELIVERABLE VIDEO; Raylight's "many things at once" is scenes INSIDE one
|
|
8
|
+
// document, which is the timeline's macro lane, not a pasteboard.
|
|
9
|
+
//
|
|
10
|
+
// The non-aesthetic reason is `GL_CONTEXT_BUDGET = 12` (cli/src/pageCount.ts):
|
|
11
|
+
// a measured per-origin browser ceiling on live WebGL contexts whose failure
|
|
12
|
+
// mode is a SILENT WEDGE, not an error. Every mounted shot owns a `<Canvas>`.
|
|
13
|
+
// The canvas lane never had that ceiling because its boards are DOM.
|
|
14
|
+
//
|
|
15
|
+
// So: one document, flat fields, and roughly 1700 lines of board machinery
|
|
16
|
+
// simply not written. Autosave debounce, the sha256 rev and the generation
|
|
17
|
+
// fence all still apply — they were never board-shaped, only board-keyed.
|
|
18
|
+
import { DOCK_HEIGHT, RAIL_WIDTH } from "./panelSize.js";
|
|
19
|
+
import type { Doc3D } from "@dailies-studio/shot/doc";
|
|
20
|
+
import type { AgentCredentialsInfo, AgentTranscriptItem } from "@dailies-studio/agent-client";
|
|
21
|
+
import type { Contract } from "@dailies-studio/contract";
|
|
22
|
+
import type { ComponentRow, RenderJob, ShotListing } from "../api.js";
|
|
23
|
+
import type { ShadingMode, ShotStageInfo, ShotView, StageLayout, StagePane, WorkingView } from "../bridgeTypes.js";
|
|
24
|
+
import { INITIAL_WORKING_VIEW } from "../stage/workingView.js";
|
|
25
|
+
import type { DrawPoint } from "./insert.js";
|
|
26
|
+
import type { ShotTool } from "../tools.js";
|
|
27
|
+
|
|
28
|
+
export type SaveState = "idle" | "saving" | "saved" | "error";
|
|
29
|
+
|
|
30
|
+
export interface ShotEditorState {
|
|
31
|
+
/** Everything in `.studio/shots/`, for the switcher. */
|
|
32
|
+
shots: readonly ShotListing[];
|
|
33
|
+
/** The open document's store name, or null at the empty state. */
|
|
34
|
+
name: string | null;
|
|
35
|
+
doc: Doc3D | null;
|
|
36
|
+
/** The rev the open document was READ at, and the baseline every save
|
|
37
|
+
* claims. null = nothing open, or a create that has not landed yet. */
|
|
38
|
+
rev: string | null;
|
|
39
|
+
|
|
40
|
+
/** MASTER frame. Integer, always. */
|
|
41
|
+
frame: number;
|
|
42
|
+
playing: boolean;
|
|
43
|
+
|
|
44
|
+
/** Which scene the micro timeline and the inspector are looking at. This is
|
|
45
|
+
* the SINGLE bit that decides macro vs micro, and it is the same bit the URL
|
|
46
|
+
* mirrors — one two-level model, not two. */
|
|
47
|
+
sceneId: string | null;
|
|
48
|
+
/** Layer ids, in document order, deduped. */
|
|
49
|
+
selection: readonly string[];
|
|
50
|
+
|
|
51
|
+
/** Which tool is armed. Decides what a canvas press does, and which plane a
|
|
52
|
+
* move unprojects onto. */
|
|
53
|
+
tool: ShotTool;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* TWO MODE BITS, and collapsing them would be a data-corrupting surprise.
|
|
57
|
+
*
|
|
58
|
+
* `inspectorMode` is a VIEW choice, set by clicking the inspector's tabs.
|
|
59
|
+
* `animateMode` is a BEHAVIOUR choice, set ONLY by ⇧A: with it on, a canvas
|
|
60
|
+
* drag authors a BLOCK ending at the playhead instead of writing the base.
|
|
61
|
+
*
|
|
62
|
+
* One bit would mean that clicking "Animate" to LOOK at the blocks silently
|
|
63
|
+
* arms block authoring on the next drag.
|
|
64
|
+
*/
|
|
65
|
+
inspectorMode: "design" | "animate";
|
|
66
|
+
animateMode: boolean;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The block the dock has focused, or null.
|
|
70
|
+
*
|
|
71
|
+
* Addressed by (scene, layer, block) even though a block id is unique across
|
|
72
|
+
* the document. The layer is what tells a row which lane it is in without a
|
|
73
|
+
* lookup; the SCENE is what stopped being derivable when the dock became one
|
|
74
|
+
* master strip. `state.sceneId` follows the playhead, so a block clicked in
|
|
75
|
+
* scene 3 while the playhead sits in scene 1 would have sent Delete and Split
|
|
76
|
+
* at scene 1 — where the id does not exist, so both would have silently done
|
|
77
|
+
* nothing.
|
|
78
|
+
*/
|
|
79
|
+
selectedBlock: { sceneId: string; layerId: string; blockId: string } | null;
|
|
80
|
+
/**
|
|
81
|
+
* The marker the strip has selected, or null.
|
|
82
|
+
*
|
|
83
|
+
* A BARE ID, unlike `selectedBlock`: a marker belongs to the document rather
|
|
84
|
+
* than to a scene (`MarkerRef.frame` is a master frame), so there is nothing
|
|
85
|
+
* to address it relative to and nothing that can go stale under a retime.
|
|
86
|
+
*
|
|
87
|
+
* It is the FIRST subject Delete and the inspector consult — see
|
|
88
|
+
* `deleteSelection`. Any layer selection clears it, so "click a layer, press
|
|
89
|
+
* Delete" can never remove a marker the author stopped looking at.
|
|
90
|
+
*/
|
|
91
|
+
selectedMarker: string | null;
|
|
92
|
+
/**
|
|
93
|
+
* The box a drawn IMAGE layer is waiting to land in, or null.
|
|
94
|
+
*
|
|
95
|
+
* The image tool draws its box first and asks for a file second, and a file
|
|
96
|
+
* dialog can only be opened from a real click on a real `<input>` — which the
|
|
97
|
+
* store is not. So the geometry parks here and the toolbar's hidden input
|
|
98
|
+
* picks it up; `completeImageDraw` is the other half. Never persisted, never
|
|
99
|
+
* a document edit: nothing exists yet.
|
|
100
|
+
*/
|
|
101
|
+
imagePick: readonly DrawPoint[] | null;
|
|
102
|
+
/**
|
|
103
|
+
* The stage's last right press, or null. A MAILBOX, not a menu.
|
|
104
|
+
*
|
|
105
|
+
* Fresh identity per event, so `StageMenu`'s effect fires again when the
|
|
106
|
+
* author right-clicks a second time at the same point on the same layer —
|
|
107
|
+
* value equality here would silently swallow the second menu.
|
|
108
|
+
*
|
|
109
|
+
* Cleared by `onActivePane`, which is the seam EVERY stage press and wheel
|
|
110
|
+
* already crosses. It has to be cleared from the stage side: a pointerdown
|
|
111
|
+
* inside the iframe is a different document's event and never reaches the
|
|
112
|
+
* chrome window's outside-click listener.
|
|
113
|
+
*/
|
|
114
|
+
stageMenu: { at: { x: number; y: number }; hits: readonly string[]; pane: StagePane } | null;
|
|
115
|
+
/**
|
|
116
|
+
* The mesh layers a material pick would be applied to, or null when the
|
|
117
|
+
* picker is closed. Editor state: nothing exists in the document until a
|
|
118
|
+
* card is chosen.
|
|
119
|
+
*/
|
|
120
|
+
materialPicker: { layerIds: readonly string[] } | null;
|
|
121
|
+
/**
|
|
122
|
+
* Triangle counts by mesh layer id, pushed up from the stage as models load.
|
|
123
|
+
*
|
|
124
|
+
* The chrome is three-free by test, so it cannot count them itself. Held here
|
|
125
|
+
* rather than derived because the count is a property of the loaded GLB, not
|
|
126
|
+
* of the document: two layers on one asset report the same number, and a
|
|
127
|
+
* layer whose model has not arrived yet reports nothing at all.
|
|
128
|
+
*/
|
|
129
|
+
meshTriangles: Readonly<Record<string, number>>;
|
|
130
|
+
/**
|
|
131
|
+
* The subtree being edited in isolation, or null for the whole scene.
|
|
132
|
+
*
|
|
133
|
+
* EDITOR STATE, never a document field. It scopes three things at once — the
|
|
134
|
+
* working pane's camera, what is pickable, and what is dimmed — and all three
|
|
135
|
+
* derive from this ONE id rather than from copies, so they cannot disagree
|
|
136
|
+
* about what the author is inside.
|
|
137
|
+
*
|
|
138
|
+
* Validated on every document write (`onHistoryChange`): a delete, an undo or
|
|
139
|
+
* an agent edit can take the layer away, and an isolation pointing at nothing
|
|
140
|
+
* is a pane framing nothing with everything else unpickable.
|
|
141
|
+
*/
|
|
142
|
+
isolation: string | null;
|
|
143
|
+
/**
|
|
144
|
+
* Inside isolation, the AUTHOR owns the working camera.
|
|
145
|
+
*
|
|
146
|
+
* False is the entry state: the derived orthographic head-on rig draws the
|
|
147
|
+
* pane, which is the only camera that can frame a rolled or face-down card.
|
|
148
|
+
* The first orbit, pan or dolly flips this and the ordinary perspective orbit
|
|
149
|
+
* rig takes over — Blender's local view scopes what you SEE and leaves the
|
|
150
|
+
* camera free, and this is that.
|
|
151
|
+
*
|
|
152
|
+
* Home and numpad-dot clear it, so head-on is the RESET rather than a cage.
|
|
153
|
+
* Entering another subtree clears it too (`setIsolation` on the stage does
|
|
154
|
+
* the same on its own side), so a second card always opens head-on.
|
|
155
|
+
*
|
|
156
|
+
* WORKSPACE STATE. It is meaningless while `isolation` is null, and
|
|
157
|
+
* `writeFramingFromView` reads the pair to decide whether the pose it is
|
|
158
|
+
* about to project onto the shot camera is one anybody can see.
|
|
159
|
+
*/
|
|
160
|
+
isolationOrbit: boolean;
|
|
161
|
+
/** The shading the working pane was in before the current one, so ⇧Z can
|
|
162
|
+
* toggle wireframe and come BACK rather than stranding the author in it. */
|
|
163
|
+
previousShading: ShadingMode;
|
|
164
|
+
/**
|
|
165
|
+
* The component definition the OUTLINE is scoped to, or null for the scene.
|
|
166
|
+
*
|
|
167
|
+
* Separate from `isolation` on purpose. Isolation is a SPATIAL move — the
|
|
168
|
+
* working pane frames a subtree that is on screen — and a definition's layers
|
|
169
|
+
* are in no scene and have no camera at all. Conflating them would make
|
|
170
|
+
* "what am I looking at" and "what am I editing" one field that has to answer
|
|
171
|
+
* both, and the answer for a definition would be a camera pointing at
|
|
172
|
+
* nothing.
|
|
173
|
+
*
|
|
174
|
+
* Cleared with the other selection bits on document close, and validated on
|
|
175
|
+
* every write beside `isolation` — a definition can be deleted too.
|
|
176
|
+
*/
|
|
177
|
+
editingDef: string | null;
|
|
178
|
+
/** The export dialog is open. A store bit rather than local state, because
|
|
179
|
+
* the keymap opens it (⌘E) and the header's button toggles it, and two
|
|
180
|
+
* places holding "is it open" is one place for them to disagree. */
|
|
181
|
+
exportOpen: boolean;
|
|
182
|
+
/**
|
|
183
|
+
* The live (or most recent) render, mirrored off the SSE bus.
|
|
184
|
+
*
|
|
185
|
+
* Held in the STORE and not in the dialog, so closing the dialog does not
|
|
186
|
+
* lose the render: the header keeps showing progress and the author can walk
|
|
187
|
+
* away from the modal without cancelling anything.
|
|
188
|
+
*/
|
|
189
|
+
renderJob: RenderJob | null;
|
|
190
|
+
// ── the agent drawer ─────────────────────────────────────────────────────
|
|
191
|
+
//
|
|
192
|
+
// Held in the STORE and not in the panel, for the reason the render job is:
|
|
193
|
+
// closing the drawer must not cancel a running turn or lose its transcript.
|
|
194
|
+
agentOpen: boolean;
|
|
195
|
+
agentItems: readonly AgentTranscriptItem[];
|
|
196
|
+
agentRunning: boolean;
|
|
197
|
+
agentError: string | null;
|
|
198
|
+
/** The session the CURRENT turn is streaming. Apply targets the session
|
|
199
|
+
* that MINTED a card, which is carried on the card itself — this is only
|
|
200
|
+
* for cancel. */
|
|
201
|
+
agentSessionId: string | null;
|
|
202
|
+
/** A per-chrome model override, or null for the server's default. */
|
|
203
|
+
agentModel: string | null;
|
|
204
|
+
agentCostUsd: number | null;
|
|
205
|
+
agentCredentials: AgentCredentialsInfo | null;
|
|
206
|
+
agentCredentialsError: string | null;
|
|
207
|
+
/** Hidden, not unmounted — see TimelineDock. */
|
|
208
|
+
dockCollapsed: boolean;
|
|
209
|
+
/** The two side rails, hidden so the stage can be seen. Unlike the dock
|
|
210
|
+
* these ARE unmounted (`display: none`): they hold no live geometry that a
|
|
211
|
+
* remount would lose, and the widths they occupy are variables the control
|
|
212
|
+
* strip and the dock position themselves against. */
|
|
213
|
+
railCollapsed: boolean;
|
|
214
|
+
inspectorCollapsed: boolean;
|
|
215
|
+
/**
|
|
216
|
+
* What entering the working view auto-collapsed, per panel: the collapse
|
|
217
|
+
* value to restore on leaving, or null when there is nothing pending —
|
|
218
|
+
* either the mode is not on, or the author toggled that panel themselves
|
|
219
|
+
* inside it and took ownership (the collapse verbs null the slot).
|
|
220
|
+
*/
|
|
221
|
+
focusRestoreInspector: boolean | null;
|
|
222
|
+
focusRestoreDock: boolean | null;
|
|
223
|
+
/** Dragged sizes, in PIXELS — see state/panelSize.ts for why not `vh`. */
|
|
224
|
+
railWidth: number;
|
|
225
|
+
dockHeight: number;
|
|
226
|
+
/** Pixels per frame in the dock. The unit @dailies-studio/timeline's geometry
|
|
227
|
+
* and `useAnchoredZoom` both speak, so there is no second zoom scale. */
|
|
228
|
+
dockPpf: number;
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* PREVIEW audio, and only preview: the monitoring level in this browser tab.
|
|
232
|
+
*
|
|
233
|
+
* Never the document, and the distinction is load-bearing. `clip.gain` and
|
|
234
|
+
* `clip.muted` are the mix and they are exported; these two are the author
|
|
235
|
+
* turning their speakers down, and an export that got quieter because
|
|
236
|
+
* somebody muted their monitor would be the worst kind of silent corruption.
|
|
237
|
+
* They ride `workspacePrefs` with the rest of "where the author is standing".
|
|
238
|
+
*/
|
|
239
|
+
previewMuted: boolean;
|
|
240
|
+
/** Linear, 0..1. */
|
|
241
|
+
previewVolume: number;
|
|
242
|
+
|
|
243
|
+
/** The stage's working-area view. Owned by the stage, mirrored here so the
|
|
244
|
+
* zoom readout has something to read and "fit" has something to write. */
|
|
245
|
+
view: ShotView;
|
|
246
|
+
/**
|
|
247
|
+
* How many views the stage is showing, and how the width is divided.
|
|
248
|
+
*
|
|
249
|
+
* A WORKSPACE PREF, listed in `workspacePrefs`: it survives a document switch
|
|
250
|
+
* and never dirties the document. The split is where the author is standing,
|
|
251
|
+
* not something about the shot — and a layout that reset on every open would
|
|
252
|
+
* be a setting nobody could keep.
|
|
253
|
+
*/
|
|
254
|
+
stageLayout: StageLayout;
|
|
255
|
+
/** The working pane's orbit camera. A workspace pref for the same reason, and
|
|
256
|
+
* additionally: it is EDITOR STATE that must never reach an export. */
|
|
257
|
+
workingView: WorkingView;
|
|
258
|
+
/** Which pane the author last acted in. The zoom controls and the fit
|
|
259
|
+
* shortcut point at it, so it follows the POINTER and not the selection. */
|
|
260
|
+
activePane: StagePane;
|
|
261
|
+
/**
|
|
262
|
+
* "Camera follows this view": orbiting the working pane writes the shot
|
|
263
|
+
* camera's framing continuously.
|
|
264
|
+
*
|
|
265
|
+
* A WORKSPACE PREF like the other two, and deliberately NOT called a lock.
|
|
266
|
+
* The word says "nothing can move" when what is true is the opposite — the
|
|
267
|
+
* camera is being moved by everything you do. It is armed only by its own
|
|
268
|
+
* control, never as a side effect of entering a mode, for the reason
|
|
269
|
+
* `animateMode` is: a bit that silently makes drags write the document must
|
|
270
|
+
* be turned on on purpose.
|
|
271
|
+
*/
|
|
272
|
+
cameraFollowsView: boolean;
|
|
273
|
+
/**
|
|
274
|
+
* The WORKING pane's shading (solid / wireframe / material / rendered).
|
|
275
|
+
*
|
|
276
|
+
* A workspace pref like the layout: how the author looks at the scene, never
|
|
277
|
+
* something about the shot. The camera pane is not addressable — it is
|
|
278
|
+
* always the graded render, which is what makes "preview === export" a
|
|
279
|
+
* sentence with no footnotes.
|
|
280
|
+
*/
|
|
281
|
+
shadingMode: ShadingMode;
|
|
282
|
+
/** What the stage reported on ready, or null before it did. */
|
|
283
|
+
stageInfo: ShotStageInfo | null;
|
|
284
|
+
/** A stage connect or runtime failure, for the overlay strip. */
|
|
285
|
+
stageError: string | null;
|
|
286
|
+
|
|
287
|
+
// ── the component registry, for `instance` layers ────────────────────────
|
|
288
|
+
/** Every registry row, whatever its host: the ADD menu filters to "three",
|
|
289
|
+
* and the inspector needs the others in order to SAY why a layer's target
|
|
290
|
+
* no longer qualifies rather than reporting it as missing. */
|
|
291
|
+
library: readonly ComponentRow[];
|
|
292
|
+
/** Has `GET /api/components` ever ANSWERED? "loading finished" is not the
|
|
293
|
+
* same thing — a FAILED fetch also ends with an empty list, and judging an
|
|
294
|
+
* instance against that would report every one of them as an unknown
|
|
295
|
+
* component, which is a claim nobody established. */
|
|
296
|
+
libraryLoaded: boolean;
|
|
297
|
+
libraryError: string | null;
|
|
298
|
+
/** Lazily fetched contracts by component id — the inspector's row source. */
|
|
299
|
+
contracts: Readonly<Record<string, Contract>>;
|
|
300
|
+
|
|
301
|
+
saveState: SaveState;
|
|
302
|
+
/** Unsaved edits exist. Separate from `saveState` because a save that is
|
|
303
|
+
* in flight while a new edit lands is BOTH "saving" and dirty. */
|
|
304
|
+
dirty: boolean;
|
|
305
|
+
/** The last API failure, in words the author can act on. */
|
|
306
|
+
error: string | null;
|
|
307
|
+
/** Codegen refused to overwrite a hand-edited generated file. Sticky until
|
|
308
|
+
* the next clean save, because it means the .tsx on disk no longer matches
|
|
309
|
+
* the document — which is exactly the thing an author must not miss. */
|
|
310
|
+
emitWarning: string | null;
|
|
311
|
+
|
|
312
|
+
canUndo: boolean;
|
|
313
|
+
canRedo: boolean;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export const INITIAL_VIEW: ShotView = { zoom: 1, panX: 0, panY: 0 };
|
|
317
|
+
|
|
318
|
+
/** Camera only, because that is the pane that IS the export: an editor that
|
|
319
|
+
* opened onto a working pane would open onto a view of the shot that is not
|
|
320
|
+
* the shot. */
|
|
321
|
+
export const INITIAL_STAGE_LAYOUT: StageLayout = { mode: "camera", splitRatio: 0.5 };
|
|
322
|
+
|
|
323
|
+
export const INITIAL_STATE: ShotEditorState = {
|
|
324
|
+
shots: [],
|
|
325
|
+
name: null,
|
|
326
|
+
doc: null,
|
|
327
|
+
rev: null,
|
|
328
|
+
frame: 0,
|
|
329
|
+
playing: false,
|
|
330
|
+
sceneId: null,
|
|
331
|
+
selection: [],
|
|
332
|
+
isolation: null,
|
|
333
|
+
isolationOrbit: false,
|
|
334
|
+
previousShading: "solid",
|
|
335
|
+
editingDef: null,
|
|
336
|
+
tool: "select",
|
|
337
|
+
inspectorMode: "design",
|
|
338
|
+
animateMode: false,
|
|
339
|
+
selectedBlock: null,
|
|
340
|
+
selectedMarker: null,
|
|
341
|
+
imagePick: null,
|
|
342
|
+
stageMenu: null,
|
|
343
|
+
materialPicker: null,
|
|
344
|
+
meshTriangles: {},
|
|
345
|
+
exportOpen: false,
|
|
346
|
+
renderJob: null,
|
|
347
|
+
agentOpen: false,
|
|
348
|
+
agentItems: [],
|
|
349
|
+
agentRunning: false,
|
|
350
|
+
agentError: null,
|
|
351
|
+
agentSessionId: null,
|
|
352
|
+
agentModel: null,
|
|
353
|
+
agentCostUsd: null,
|
|
354
|
+
agentCredentials: null,
|
|
355
|
+
agentCredentialsError: null,
|
|
356
|
+
dockCollapsed: false,
|
|
357
|
+
railCollapsed: false,
|
|
358
|
+
inspectorCollapsed: false,
|
|
359
|
+
focusRestoreInspector: null,
|
|
360
|
+
focusRestoreDock: null,
|
|
361
|
+
railWidth: RAIL_WIDTH.default,
|
|
362
|
+
dockHeight: DOCK_HEIGHT.default,
|
|
363
|
+
dockPpf: 6,
|
|
364
|
+
previewMuted: false,
|
|
365
|
+
previewVolume: 1,
|
|
366
|
+
view: INITIAL_VIEW,
|
|
367
|
+
stageLayout: INITIAL_STAGE_LAYOUT,
|
|
368
|
+
workingView: INITIAL_WORKING_VIEW,
|
|
369
|
+
activePane: "camera",
|
|
370
|
+
cameraFollowsView: false,
|
|
371
|
+
shadingMode: "solid",
|
|
372
|
+
stageInfo: null,
|
|
373
|
+
stageError: null,
|
|
374
|
+
library: [],
|
|
375
|
+
libraryLoaded: false,
|
|
376
|
+
libraryError: null,
|
|
377
|
+
contracts: {},
|
|
378
|
+
saveState: "idle",
|
|
379
|
+
dirty: false,
|
|
380
|
+
error: null,
|
|
381
|
+
emitWarning: null,
|
|
382
|
+
canUndo: false,
|
|
383
|
+
canRedo: false,
|
|
384
|
+
};
|