@kokoa/clotho-editor 0.2.0 → 0.4.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.
@@ -1096,6 +1096,232 @@ body.studio-player-popout #clotho-player-preview {
1096
1096
  color: var(--color-accent);
1097
1097
  background: linear-gradient(90deg, transparent, rgba(99, 102, 241, 0.15));
1098
1098
  }
1099
+ /* Canvas overlay for the camera. Drawn into the editing SVG, which keeps the
1100
+ full-canvas viewBox, so the frame is the part a reader will actually see. */
1101
+ .studio-camera-frame-scrim {
1102
+ /* Fixed rather than themed: this dims the *document's* canvas, which keeps its
1103
+ own background whatever the editor chrome is doing. A themed value goes
1104
+ invisible exactly when the editor is dark and the canvas is white. */
1105
+ fill: rgba(15, 23, 42, 0.32);
1106
+ }
1107
+ .studio-camera-frame-rect {
1108
+ fill: none;
1109
+ stroke: #7c3aed;
1110
+ stroke-width: 2;
1111
+ stroke-dasharray: 6 4;
1112
+ }
1113
+ .studio-camera-frame.is-selected .studio-camera-frame-rect {
1114
+ stroke-dasharray: none;
1115
+ stroke-width: 2.5;
1116
+ }
1117
+ .studio-camera-frame-rect[data-camera-frame] {
1118
+ cursor: pointer;
1119
+ }
1120
+ /* Transparent, but it takes the pan drag — so it needs a fill to be hit. */
1121
+ .studio-camera-frame-body {
1122
+ fill: transparent;
1123
+ cursor: grab;
1124
+ }
1125
+ .studio-camera-frame-body:active {
1126
+ cursor: grabbing;
1127
+ }
1128
+ .studio-camera-frame-handle {
1129
+ fill: var(--color-surface-elevated, #fff);
1130
+ stroke: #7c3aed;
1131
+ stroke-width: 1.5;
1132
+ cursor: nwse-resize;
1133
+ }
1134
+ .studio-camera-frame-handle:hover {
1135
+ fill: #7c3aed;
1136
+ }
1137
+ .studio-camera-frame-label {
1138
+ fill: #7c3aed;
1139
+ font-family: var(--font-mono);
1140
+ font-size: 12px;
1141
+ }
1142
+ .studio-camera-frame-warning {
1143
+ fill: #ef4444;
1144
+ font-family: var(--font-mono);
1145
+ font-size: 11px;
1146
+ }
1147
+ .studio-spotlight-preview-label {
1148
+ fill: #f59e0b;
1149
+ font-family: var(--font-mono);
1150
+ font-size: 12px;
1151
+ }
1152
+ .studio-camera-readout {
1153
+ padding: 0.4rem 0.55rem;
1154
+ margin-bottom: 0.4rem;
1155
+ border-radius: 6px;
1156
+ background-color: var(--color-surface);
1157
+ font-family: var(--font-mono);
1158
+ font-size: 0.7rem;
1159
+ color: var(--color-fg-muted);
1160
+ }
1161
+ .studio-camera-issue {
1162
+ color: #ef4444;
1163
+ }
1164
+ .studio-camera-focus > summary {
1165
+ cursor: pointer;
1166
+ padding: 0.3rem 0;
1167
+ font-family: var(--font-mono);
1168
+ font-size: 0.7rem;
1169
+ }
1170
+ .studio-camera-track {
1171
+ padding: 0.3rem 0;
1172
+ border-top: 1px solid var(--color-border);
1173
+ }
1174
+ .studio-camera-track-head {
1175
+ display: flex;
1176
+ align-items: center;
1177
+ gap: 0.4rem;
1178
+ font-family: var(--font-mono);
1179
+ font-size: 0.72rem;
1180
+ }
1181
+ .studio-camera-track-now {
1182
+ margin-left: auto;
1183
+ color: var(--color-fg-muted);
1184
+ font-size: 0.68rem;
1185
+ }
1186
+ .studio-camera-track .studio-props-empty {
1187
+ margin: 0.2rem 0 0;
1188
+ padding: 0;
1189
+ text-align: left;
1190
+ font-size: 0.68rem;
1191
+ }
1192
+ .studio-camera-control {
1193
+ color: #7c3aed;
1194
+ }
1195
+ .studio-camera-field-label {
1196
+ margin: 0.5rem 0 0.25rem;
1197
+ font-size: 0.72rem;
1198
+ color: var(--color-fg-muted);
1199
+ }
1200
+ .studio-camera-range {
1201
+ margin: 0.45rem 0;
1202
+ }
1203
+ .studio-camera-range-head {
1204
+ display: flex;
1205
+ justify-content: space-between;
1206
+ align-items: baseline;
1207
+ font-size: 0.72rem;
1208
+ color: var(--color-fg-muted);
1209
+ }
1210
+ .studio-camera-range-value {
1211
+ font-family: var(--font-mono);
1212
+ color: var(--color-fg);
1213
+ }
1214
+ .studio-camera-range-row {
1215
+ display: flex;
1216
+ align-items: center;
1217
+ gap: 0.4rem;
1218
+ }
1219
+ .studio-camera-range-row input[type="range"] {
1220
+ flex: 1;
1221
+ min-width: 0;
1222
+ }
1223
+ .studio-camera-range-row input[type="number"] {
1224
+ width: 5.2rem;
1225
+ flex: 0 0 auto;
1226
+ }
1227
+ .studio-camera-hint {
1228
+ margin: 0.2rem 0 0;
1229
+ font-size: 0.66rem;
1230
+ color: var(--color-fg-muted);
1231
+ }
1232
+ .studio-camera-chips {
1233
+ display: flex;
1234
+ flex-wrap: wrap;
1235
+ gap: 0.25rem;
1236
+ margin: 0.25rem 0;
1237
+ }
1238
+ .studio-chip {
1239
+ padding: 0.1rem 0.45rem;
1240
+ border: 1px solid var(--color-border);
1241
+ border-radius: 999px;
1242
+ background: none;
1243
+ color: var(--color-fg-muted);
1244
+ font: inherit;
1245
+ font-size: 0.68rem;
1246
+ cursor: pointer;
1247
+ }
1248
+ .studio-chip.is-on {
1249
+ border-color: #7c3aed;
1250
+ background-color: color-mix(in srgb, #7c3aed 18%, transparent);
1251
+ color: var(--color-fg);
1252
+ }
1253
+ .studio-camera-focus-actions {
1254
+ display: flex;
1255
+ flex-wrap: wrap;
1256
+ gap: 0.3rem;
1257
+ margin: 0.3rem 0;
1258
+ }
1259
+ /* The target list scrolls: a document with forty elements must not push the
1260
+ padding and maxZoom controls off the panel. */
1261
+ .studio-camera-targets {
1262
+ max-height: 11rem;
1263
+ overflow-y: auto;
1264
+ border: 1px solid var(--color-border);
1265
+ border-radius: 6px;
1266
+ padding: 0.2rem;
1267
+ }
1268
+ .studio-camera-target {
1269
+ display: flex;
1270
+ align-items: center;
1271
+ gap: 0.35rem;
1272
+ padding: 0.15rem 0.3rem;
1273
+ border-radius: 4px;
1274
+ font-size: 0.72rem;
1275
+ cursor: pointer;
1276
+ }
1277
+ .studio-camera-target:hover {
1278
+ background-color: color-mix(in srgb, var(--color-accent) 10%, transparent);
1279
+ }
1280
+ .studio-camera-target.is-on {
1281
+ background-color: color-mix(in srgb, #7c3aed 16%, transparent);
1282
+ }
1283
+ .studio-camera-target-name {
1284
+ flex: 1;
1285
+ overflow: hidden;
1286
+ text-overflow: ellipsis;
1287
+ white-space: nowrap;
1288
+ }
1289
+ .studio-camera-target-id,
1290
+ .studio-camera-target-type {
1291
+ font-family: var(--font-mono);
1292
+ font-size: 0.64rem;
1293
+ color: var(--color-fg-muted);
1294
+ }
1295
+ .studio-camera-kf-list {
1296
+ list-style: none;
1297
+ margin: 0.3rem 0 0;
1298
+ padding: 0;
1299
+ display: flex;
1300
+ flex-wrap: wrap;
1301
+ gap: 0.3rem;
1302
+ }
1303
+ .studio-camera-kf-list li {
1304
+ display: flex;
1305
+ align-items: center;
1306
+ gap: 0.25rem;
1307
+ padding: 0.1rem 0.3rem;
1308
+ border: 1px solid var(--color-border);
1309
+ border-radius: 999px;
1310
+ font-family: var(--font-mono);
1311
+ font-size: 0.68rem;
1312
+ }
1313
+ .studio-camera-kf,
1314
+ .studio-camera-kf-del {
1315
+ border: 0;
1316
+ background: none;
1317
+ color: inherit;
1318
+ font: inherit;
1319
+ cursor: pointer;
1320
+ padding: 0;
1321
+ }
1322
+ .studio-camera-kf-del {
1323
+ color: #ef4444;
1324
+ }
1099
1325
  .studio-tl-total {
1100
1326
  margin-top: 0.3rem;
1101
1327
  padding: 0.2rem 0.4rem;
@@ -1885,6 +2111,81 @@ body.studio-player-popout #clotho-player-preview {
1885
2111
  color: #fff;
1886
2112
  border-color: #b91c1c;
1887
2113
  }
2114
+ /* Camera rows: the same shape as an element row, tinted so a document-level
2115
+ track is not mistaken for one belonging to whatever element is above it. */
2116
+ .studio-tl-camera-row {
2117
+ border-bottom: 1px solid var(--color-border);
2118
+ min-height: 30px;
2119
+ }
2120
+ .studio-tl-camera-row.is-selected .studio-tl-gutter,
2121
+ .studio-tl-camera-row.is-selected .studio-tl-body {
2122
+ background-color: color-mix(
2123
+ in srgb,
2124
+ var(--color-accent) 10%,
2125
+ var(--color-surface-elevated)
2126
+ );
2127
+ }
2128
+ .studio-tl-camera-gutter {
2129
+ gap: 0.35rem;
2130
+ color: #7c3aed;
2131
+ font-weight: 700;
2132
+ cursor: pointer;
2133
+ }
2134
+ .studio-tl-camera-gutter.is-sub {
2135
+ color: var(--color-fg-muted);
2136
+ font-weight: 400;
2137
+ padding-left: 1.4rem;
2138
+ }
2139
+ .studio-tl-camera-add {
2140
+ margin-left: auto;
2141
+ border: 1px solid var(--color-border);
2142
+ border-radius: 4px;
2143
+ background: none;
2144
+ color: inherit;
2145
+ font: inherit;
2146
+ line-height: 1;
2147
+ padding: 0 0.3rem;
2148
+ cursor: pointer;
2149
+ }
2150
+ .studio-tl-camera-track {
2151
+ position: relative;
2152
+ height: 100%;
2153
+ min-height: 30px;
2154
+ }
2155
+ .studio-tl-camera-empty {
2156
+ position: absolute;
2157
+ left: 0.5rem;
2158
+ top: 50%;
2159
+ transform: translateY(-50%);
2160
+ font-size: 0.68rem;
2161
+ color: var(--color-fg-muted);
2162
+ }
2163
+ .studio-tl-camera-focus {
2164
+ position: absolute;
2165
+ top: 5px;
2166
+ bottom: 5px;
2167
+ border-radius: 4px;
2168
+ border: 1px solid #7c3aed;
2169
+ background-color: color-mix(in srgb, #7c3aed 22%, transparent);
2170
+ cursor: grab;
2171
+ overflow: hidden;
2172
+ display: flex;
2173
+ align-items: center;
2174
+ }
2175
+ .studio-tl-camera-focus.is-selected {
2176
+ border-color: #ef4444;
2177
+ background-color: color-mix(in srgb, #ef4444 26%, transparent);
2178
+ }
2179
+ .studio-tl-camera-focus-label {
2180
+ padding: 0 0.35rem;
2181
+ font-size: 0.66rem;
2182
+ font-family: var(--font-mono);
2183
+ white-space: nowrap;
2184
+ pointer-events: none;
2185
+ }
2186
+ .studio-tl-camera-keyframe {
2187
+ color: #7c3aed;
2188
+ }
1888
2189
  .studio-tl-playhead {
1889
2190
  position: absolute;
1890
2191
  top: 0;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AnimationDocument, SnapshotMap, Appearance, AnimationElement, TrackKeyframe, Chapter, AnimationEffect, DataValue, LayoutMode, Checkpoint, StoryManifest, StoryEdge, AnimationTemplate, GroupElement, ParameterSchema } from '@kokoa/clotho';
1
+ import { AnimationDocument, SnapshotMap, Appearance, AnimationElement, TrackKeyframe, Chapter, AnimationEffect, CameraFocus, Camera, CameraProperty, DataValue, LayoutMode, Checkpoint, StoryManifest, StoryEdge, AnimationTemplate, GroupElement, ParameterSchema } from '@kokoa/clotho';
2
2
 
3
3
  interface StudioProps {
4
4
  initial: AnimationDocument;
@@ -50,8 +50,11 @@ type Selection = {
50
50
  } | {
51
51
  kind: "effect";
52
52
  effectId: string;
53
+ } | {
54
+ kind: "camera";
55
+ focusIndex?: number;
53
56
  };
54
- type HistoryKind = "meta" | "canvas" | "settings" | "add" | "delete" | "move" | "style" | "rotate" | "resize" | "reorder" | "track" | "appearance" | "chapter" | "effect" | "group" | "layout" | "checkpoint" | "asset" | "other";
57
+ type HistoryKind = "meta" | "canvas" | "settings" | "add" | "delete" | "move" | "style" | "rotate" | "resize" | "reorder" | "track" | "appearance" | "chapter" | "effect" | "camera" | "group" | "layout" | "checkpoint" | "asset" | "other";
55
58
  interface HistoryEntry {
56
59
  snap: string;
57
60
  label: string;
@@ -111,9 +114,65 @@ declare function updateChapter(id: string, patch: Partial<Chapter>): void;
111
114
  declare function deleteChapter(id: string): void;
112
115
  declare function addEffect(eff: AnimationEffect): void;
113
116
  declare function updateEffect(id: string, patch: Partial<AnimationEffect>): void;
117
+ /**
118
+ * Swap one effect for another with the same id.
119
+ *
120
+ * Changing an effect's type is a replacement, not a patch: `spotlight` carries
121
+ * `elementIds` where the others carry `elementId`, so merging the new fields over
122
+ * the old ones would leave a shape the schema rejects — and `mutateDef` drops a
123
+ * rejected edit without saying so.
124
+ */
125
+ declare function replaceEffect(id: string, next: AnimationEffect): void;
114
126
  declare function deleteEffect(id: string): void;
115
127
  declare function updateDuration(ms: number): void;
116
128
 
129
+ declare function getCamera(): Camera;
130
+ /** Whether the document actually carries a camera, as opposed to defaults. */
131
+ declare function hasCamera(): boolean;
132
+ /**
133
+ * Which mechanism decides the view at `time`.
134
+ *
135
+ * The renderer's rule is that a focus entry that has already started wins, and the
136
+ * tracks supply the value otherwise. Direct manipulation on the canvas has to know
137
+ * which one it is editing — dragging the frame when a focus is in control would
138
+ * write track keyframes that the focus immediately overrides, and a control that
139
+ * silently does nothing is worse than one that is disabled.
140
+ */
141
+ type CameraControl = {
142
+ kind: "none";
143
+ } | {
144
+ kind: "tracks";
145
+ } | {
146
+ kind: "focus";
147
+ index: number;
148
+ };
149
+ declare function cameraControlAt(time: number): CameraControl;
150
+ declare function setCameraStrokeScaling(value: Camera["strokeScaling"]): void;
151
+ /** Add or replace the keyframe at `time` on one camera property. */
152
+ declare function setCameraKeyframe(property: CameraProperty, time: number, value: number, ease?: CameraKeyframeEase): void;
153
+ type CameraKeyframeEase = NonNullable<Camera["tracks"][number]["keyframes"][number]["ease"]>;
154
+ /**
155
+ * Move a keyframe in time, keeping its value.
156
+ *
157
+ * Separate from `setCameraKeyframe` because a timeline drag has to remove the old
158
+ * time as well; doing it as remove-then-set would push two history entries for one
159
+ * gesture.
160
+ */
161
+ declare function moveCameraKeyframe(property: CameraProperty, from: number, to: number): void;
162
+ declare function removeCameraKeyframe(property: CameraProperty, time: number): void;
163
+ declare function removeCameraTrack(property: CameraProperty): void;
164
+ declare function addCameraFocus(focus: CameraFocus): void;
165
+ /**
166
+ * Patch one focus entry.
167
+ *
168
+ * Sorting can move it, so the selection is re-pointed at the same entry rather than
169
+ * at whatever ends up at the old index.
170
+ */
171
+ declare function updateCameraFocus(index: number, patch: Partial<CameraFocus>): void;
172
+ declare function deleteCameraFocus(index: number): void;
173
+ /** Remove the camera entirely, so the document renders as it did without one. */
174
+ declare function clearCamera(): void;
175
+
117
176
  declare function updateMeta(patch: Partial<Pick<AnimationDocument, "title" | "description">>): void;
118
177
  /** Update the locales offered to localized text elements in this document. */
119
178
  declare function updateLocales(locales: string[]): void;
@@ -304,4 +363,4 @@ declare function createPerformanceProfilerPlugin(): EditorPluginDefinition;
304
363
 
305
364
  declare function createLinterPlugin(): EditorPluginDefinition;
306
365
 
307
- export { type AnimationRepository, type AnimationSummary, type DocumentImporter, type EditorPluginContext, type EditorPluginDefinition, type EditorPluginHostState, type EditorPluginManifestLike, type EditorPluginMountResult, type EditorPluginPermissionResolver, type EditorPluginPermissions, type EditorPluginSlot, type EditorPluginView, type HistoryEntry, type HistoryKind, type HostOptions, type ImageUploadResolver, type LocalStorageRepositoryOptions, RESPONSIVE_VIEWPORTS, type SceneProfile, type Selection, type StoryEditorOptions, Studio, StudioMount, type StudioMountProps, type StudioProps, addAppearance, addChapter, addCheckpoint, addDefaultResponsiveVariants, addEffect, addElement, animationDocumentFileName, animationDocumentToJson, apiBaseUrl, appendStoryEdge, beginTransient, canRedo, canUndo, childIdsOf, configureAnimationRepository, configureApi, configureHost, createEditorPluginContext, createLayout, createLinterPlugin, createLocalStorageRepository, createPerformanceProfilerPlugin, createResponsiveInspectorPlugin, createStoryEditorPlugin, createTemplateEditorPlugin, createVisualRegressionPlugin, deleteChapter, deleteCheckpoint, deleteEffect, deleteElement, detachFromLayout, downloadAnimationJson, endTransient, findLayoutCollisions, garbageCollectAnimationAssets, getCurrentSnapshot, getCurrentTime, getDef, getHistory, getSelectedElementIds, getSelection, groupElements, initialParameterValue, isDirty, isDraft, isElementSelected, isGroup, jumpBack, jumpForward, layoutIdsFor, markClean, mountEditorPlugins, moveElementToEnd, moveElementToFront, parameterInputValue, placeholderImageUrl, profileAnimation, promoteDraftToSaved, redo, registerDataUriAsset, registerExternalAsset, registerInlineAsset, removeAppearance, removeTrack, removeTrackKeyframe, reorderElement, replaceStoryNodeDocument, resetHistory, setCurrentTime, setDef, setDraft, setElementValueAtTime, setSelection, setTrackKeyframe, subscribe, toggleSelectionFor, undo, ungroupElement, uniqueChapterId, uniqueCheckpointId, uniqueEffectId, uniqueElementId, updateAppearance, updateCanvas, updateChapter, updateCheckpoint, updateData, updateDuration, updateEffect, updateElementBase, updateLocales, updateMeta, updateResponsive, updateSettings, validateEditorPlugin };
366
+ export { type AnimationRepository, type AnimationSummary, type CameraControl, type DocumentImporter, type EditorPluginContext, type EditorPluginDefinition, type EditorPluginHostState, type EditorPluginManifestLike, type EditorPluginMountResult, type EditorPluginPermissionResolver, type EditorPluginPermissions, type EditorPluginSlot, type EditorPluginView, type HistoryEntry, type HistoryKind, type HostOptions, type ImageUploadResolver, type LocalStorageRepositoryOptions, RESPONSIVE_VIEWPORTS, type SceneProfile, type Selection, type StoryEditorOptions, Studio, StudioMount, type StudioMountProps, type StudioProps, addAppearance, addCameraFocus, addChapter, addCheckpoint, addDefaultResponsiveVariants, addEffect, addElement, animationDocumentFileName, animationDocumentToJson, apiBaseUrl, appendStoryEdge, beginTransient, cameraControlAt, canRedo, canUndo, childIdsOf, clearCamera, configureAnimationRepository, configureApi, configureHost, createEditorPluginContext, createLayout, createLinterPlugin, createLocalStorageRepository, createPerformanceProfilerPlugin, createResponsiveInspectorPlugin, createStoryEditorPlugin, createTemplateEditorPlugin, createVisualRegressionPlugin, deleteCameraFocus, deleteChapter, deleteCheckpoint, deleteEffect, deleteElement, detachFromLayout, downloadAnimationJson, endTransient, findLayoutCollisions, garbageCollectAnimationAssets, getCamera, getCurrentSnapshot, getCurrentTime, getDef, getHistory, getSelectedElementIds, getSelection, groupElements, hasCamera, initialParameterValue, isDirty, isDraft, isElementSelected, isGroup, jumpBack, jumpForward, layoutIdsFor, markClean, mountEditorPlugins, moveCameraKeyframe, moveElementToEnd, moveElementToFront, parameterInputValue, placeholderImageUrl, profileAnimation, promoteDraftToSaved, redo, registerDataUriAsset, registerExternalAsset, registerInlineAsset, removeAppearance, removeCameraKeyframe, removeCameraTrack, removeTrack, removeTrackKeyframe, reorderElement, replaceEffect, replaceStoryNodeDocument, resetHistory, setCameraKeyframe, setCameraStrokeScaling, setCurrentTime, setDef, setDraft, setElementValueAtTime, setSelection, setTrackKeyframe, subscribe, toggleSelectionFor, undo, ungroupElement, uniqueChapterId, uniqueCheckpointId, uniqueEffectId, uniqueElementId, updateAppearance, updateCameraFocus, updateCanvas, updateChapter, updateCheckpoint, updateData, updateDuration, updateEffect, updateElementBase, updateLocales, updateMeta, updateResponsive, updateSettings, validateEditorPlugin };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { downloadAnimationJson, configureAnimationRepository } from './chunk-FAI2G3XK.js';
2
- export { addAppearance, addChapter, addCheckpoint, addEffect, addElement, animationDocumentFileName, animationDocumentToJson, apiBaseUrl, beginTransient, canRedo, canUndo, childIdsOf, configureAnimationRepository, configureApi, configureHost, createEditorPluginContext, createLayout, deleteChapter, deleteCheckpoint, deleteEffect, deleteElement, detachFromLayout, downloadAnimationJson, endTransient, findLayoutCollisions, garbageCollectAnimationAssets, getCurrentSnapshot, getCurrentTime, getDef, getHistory, getSelectedElementIds, getSelection, groupElements, isDirty, isDraft, isElementSelected, isGroup, jumpBack, jumpForward, layoutIdsFor, markClean, mountEditorPlugins, moveElementToEnd, moveElementToFront, placeholderImageUrl, promoteDraftToSaved, redo, registerDataUriAsset, registerExternalAsset, registerInlineAsset, removeAppearance, removeTrack, removeTrackKeyframe, reorderElement, resetHistory, setCurrentTime, setDef, setDraft, setElementValueAtTime, setSelection, setTrackKeyframe, subscribe, toggleSelectionFor, undo, ungroupElement, uniqueChapterId, uniqueCheckpointId, uniqueEffectId, uniqueElementId, updateAppearance, updateCanvas, updateChapter, updateCheckpoint, updateData, updateDuration, updateEffect, updateElementBase, updateLocales, updateMeta, updateResponsive, updateSettings, validateEditorPlugin } from './chunk-FAI2G3XK.js';
1
+ import { downloadAnimationJson, configureAnimationRepository } from './chunk-WOP52KFW.js';
2
+ export { addAppearance, addCameraFocus, addChapter, addCheckpoint, addEffect, addElement, animationDocumentFileName, animationDocumentToJson, apiBaseUrl, beginTransient, cameraControlAt, canRedo, canUndo, childIdsOf, clearCamera, configureAnimationRepository, configureApi, configureHost, createEditorPluginContext, createLayout, deleteCameraFocus, deleteChapter, deleteCheckpoint, deleteEffect, deleteElement, detachFromLayout, downloadAnimationJson, endTransient, findLayoutCollisions, garbageCollectAnimationAssets, getCamera, getCurrentSnapshot, getCurrentTime, getDef, getHistory, getSelectedElementIds, getSelection, groupElements, hasCamera, isDirty, isDraft, isElementSelected, isGroup, jumpBack, jumpForward, layoutIdsFor, markClean, mountEditorPlugins, moveCameraKeyframe, moveElementToEnd, moveElementToFront, placeholderImageUrl, promoteDraftToSaved, redo, registerDataUriAsset, registerExternalAsset, registerInlineAsset, removeAppearance, removeCameraKeyframe, removeCameraTrack, removeTrack, removeTrackKeyframe, reorderElement, replaceEffect, resetHistory, setCameraKeyframe, setCameraStrokeScaling, setCurrentTime, setDef, setDraft, setElementValueAtTime, setSelection, setTrackKeyframe, subscribe, toggleSelectionFor, undo, ungroupElement, uniqueChapterId, uniqueCheckpointId, uniqueEffectId, uniqueElementId, updateAppearance, updateCameraFocus, updateCanvas, updateChapter, updateCheckpoint, updateData, updateDuration, updateEffect, updateElementBase, updateLocales, updateMeta, updateResponsive, updateSettings, validateEditorPlugin } from './chunk-WOP52KFW.js';
3
3
  import { useState, useEffect, useRef, useMemo } from 'react';
4
4
  import { usePlayer, AnimationStage } from '@kokoa/clotho/react';
5
5
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -491,6 +491,7 @@ var SKELETON = `
491
491
  <button type="button" id="studio-undo" class="studio-btn studio-btn-icon" aria-label="\uC2E4\uD589 \uCDE8\uC18C" title="\uC2E4\uD589 \uCDE8\uC18C (\u2318Z)" disabled>\u21B6</button>
492
492
  <button type="button" id="studio-redo" class="studio-btn studio-btn-icon" aria-label="\uB2E4\uC2DC \uC2E4\uD589" title="\uB2E4\uC2DC \uC2E4\uD589 (\u2318\u21E7Z / \u2318Y)" disabled>\u21B7</button>
493
493
  <button type="button" id="studio-grid-toggle" class="studio-btn studio-btn-grid" aria-label="\uACA9\uC790 + \uC2A4\uB0C5" title="\uACA9\uC790 + \uC2A4\uB0C5 (G)" aria-pressed="false"><span class="studio-btn-grid-icon">\u229E</span><span class="studio-btn-grid-label" id="studio-grid-label">\uACA9\uC790 \uB054</span></button>
494
+ <button type="button" id="studio-camera-frame-toggle" class="studio-btn studio-btn-grid" aria-label="\uCE74\uBA54\uB77C \uC601\uC5ED \uD45C\uC2DC" title="\uCE94\uBC84\uC2A4\uC5D0 \uCE74\uBA54\uB77C\uAC00 \uBCF4\uC5EC\uC8FC\uB294 \uC601\uC5ED\uC744 \uD45C\uC2DC\uD569\uB2C8\uB2E4" aria-pressed="true"><span class="studio-btn-grid-icon">\u{1F3A5}</span><span class="studio-btn-grid-label" id="studio-camera-frame-label">\uCE74\uBA54\uB77C \uC601\uC5ED</span></button>
494
495
  <input type="text" id="studio-title" class="studio-title-input" placeholder="\uC81C\uBAA9" aria-label="\uC560\uB2C8\uBA54\uC774\uC158 \uC81C\uBAA9" disabled />
495
496
  <span id="studio-id-display" class="studio-id-display"></span>
496
497
  <span id="studio-status" class="studio-status" aria-live="polite">\uB300\uAE30 \uC911</span>
@@ -702,7 +703,7 @@ function StudioMount({
702
703
  if (repository) configureAnimationRepository(repository);
703
704
  let disposed = false;
704
705
  let cleanup;
705
- void import('./main-SKM3P4X7.js').then(({ initStudio }) => {
706
+ void import('./main-HDE5SDKM.js').then(({ initStudio }) => {
706
707
  if (disposed) return;
707
708
  cleanup = initStudio({
708
709
  initialId,