@hyperframes/studio 0.6.5 → 0.6.7

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.
Files changed (57) hide show
  1. package/dist/assets/{hyperframes-player-CzwFysqv.js → hyperframes-player-D0Yi3xMP.js} +2 -2
  2. package/dist/assets/index-Ckqo37Co.css +1 -0
  3. package/dist/assets/index-Yvtxngdi.js +116 -0
  4. package/dist/index.html +2 -2
  5. package/package.json +4 -4
  6. package/src/App.tsx +54 -31
  7. package/src/components/StudioGlobalDragOverlay.tsx +26 -0
  8. package/src/components/StudioHeader.tsx +128 -3
  9. package/src/components/StudioRightPanel.tsx +0 -2
  10. package/src/components/editor/DomEditOverlay.test.ts +1 -0
  11. package/src/components/editor/DomEditOverlay.tsx +2 -1
  12. package/src/components/editor/PropertyPanel.tsx +27 -36
  13. package/src/components/editor/domEditingElement.ts +1 -0
  14. package/src/components/editor/manualEdits.test.ts +39 -466
  15. package/src/components/editor/manualEdits.ts +6 -168
  16. package/src/components/editor/manualEditsDom.ts +361 -1
  17. package/src/components/editor/manualEditsParsing.ts +2 -240
  18. package/src/components/editor/manualEditsTypes.ts +1 -40
  19. package/src/components/editor/useDomEditOverlayGestures.ts +25 -8
  20. package/src/components/nle/NLEPreview.tsx +1 -1
  21. package/src/components/sidebar/CompositionsTab.tsx +9 -3
  22. package/src/contexts/DomEditContext.tsx +3 -0
  23. package/src/contexts/FileManagerContext.tsx +3 -0
  24. package/src/hooks/useAppHotkeys.ts +1 -4
  25. package/src/hooks/useDomEditCommits.ts +82 -77
  26. package/src/hooks/useDomEditSession.ts +4 -16
  27. package/src/hooks/useFileManager.ts +10 -1
  28. package/src/hooks/useManifestPersistence.ts +51 -187
  29. package/src/hooks/usePanelLayout.ts +10 -3
  30. package/src/hooks/usePreviewInteraction.ts +0 -1
  31. package/src/hooks/useStudioUrlState.ts +188 -0
  32. package/src/player/components/Player.tsx +15 -1
  33. package/src/player/components/PlayerControls.test.ts +17 -0
  34. package/src/player/components/PlayerControls.tsx +347 -56
  35. package/src/player/hooks/usePlaybackKeyboard.test.ts +174 -0
  36. package/src/player/hooks/usePlaybackKeyboard.ts +37 -10
  37. package/src/player/hooks/useTimelinePlayer.seek.test.ts +329 -0
  38. package/src/player/hooks/useTimelinePlayer.ts +97 -28
  39. package/src/player/hooks/useTimelineSyncCallbacks.ts +10 -4
  40. package/src/player/lib/playbackAdapter.test.ts +50 -0
  41. package/src/player/lib/playbackAdapter.ts +2 -2
  42. package/src/player/lib/playbackTypes.ts +1 -1
  43. package/src/player/lib/timelineDOM.ts +4 -2
  44. package/src/player/lib/timelineIframeHelpers.ts +63 -7
  45. package/src/player/store/playerStore.test.ts +105 -1
  46. package/src/player/store/playerStore.ts +39 -1
  47. package/src/utils/projectRouting.test.ts +15 -0
  48. package/src/utils/projectRouting.ts +46 -9
  49. package/src/utils/sourcePatcher.ts +50 -14
  50. package/src/utils/studioPreviewHelpers.test.ts +56 -0
  51. package/src/utils/studioPreviewHelpers.ts +51 -13
  52. package/src/utils/studioUiPreferences.test.ts +3 -0
  53. package/src/utils/studioUiPreferences.ts +4 -0
  54. package/src/utils/studioUrlState.test.ts +249 -0
  55. package/src/utils/studioUrlState.ts +135 -0
  56. package/dist/assets/index-Bs6NmE0o.js +0 -117
  57. package/dist/assets/index-Dswa2GJ2.css +0 -1
@@ -1,142 +1,10 @@
1
- import type { DomEditSelection } from "./domEditing";
2
- import type {
3
- StudioManualEdit,
4
- StudioManualEditManifest,
5
- StudioManualEditTarget,
6
- StudioPathOffsetEdit,
7
- StudioBoxSizeEdit,
8
- StudioRotationEdit,
9
- } from "./manualEditsTypes";
10
- import { STUDIO_MANUAL_EDITS_PATH } from "./manualEditsTypes";
11
-
12
1
  /* ── Helpers ──────────────────────────────────────────────────────── */
13
2
  export function finiteNumber(value: unknown): number | null {
14
3
  return typeof value === "number" && Number.isFinite(value) ? value : null;
15
4
  }
16
5
 
17
- /* ── Manifest factory ─────────────────────────────────────────────── */
18
- export function emptyStudioManualEditManifest(): StudioManualEditManifest {
19
- return { version: 1, edits: [] };
20
- }
21
-
22
- /* ── Parsing ──────────────────────────────────────────────────────── */
23
- function parsePathOffsetEdit(value: unknown): StudioPathOffsetEdit | null {
24
- if (!value || typeof value !== "object") return null;
25
- const record = value as Record<string, unknown>;
26
- if (record.kind !== "path-offset") return null;
27
- const target = record.target;
28
- if (!target || typeof target !== "object") return null;
29
- const targetRecord = target as Record<string, unknown>;
30
- const sourceFile = typeof targetRecord.sourceFile === "string" ? targetRecord.sourceFile : "";
31
- if (!sourceFile) return null;
32
-
33
- const selector = typeof targetRecord.selector === "string" ? targetRecord.selector : undefined;
34
- const id = typeof targetRecord.id === "string" ? targetRecord.id : undefined;
35
- if (!selector && !id) return null;
36
-
37
- const x = finiteNumber(record.x);
38
- const y = finiteNumber(record.y);
39
- if (x == null || y == null) return null;
40
-
41
- return {
42
- kind: "path-offset",
43
- target: {
44
- sourceFile,
45
- selector,
46
- selectorIndex: finiteNumber(targetRecord.selectorIndex) ?? undefined,
47
- id,
48
- },
49
- x,
50
- y,
51
- updatedAt: typeof record.updatedAt === "string" ? record.updatedAt : undefined,
52
- };
53
- }
54
-
55
- function parseBoxSizeEdit(value: unknown): StudioBoxSizeEdit | null {
56
- if (!value || typeof value !== "object") return null;
57
- const record = value as Record<string, unknown>;
58
- if (record.kind !== "box-size") return null;
59
- const target = record.target;
60
- if (!target || typeof target !== "object") return null;
61
- const targetRecord = target as Record<string, unknown>;
62
- const sourceFile = typeof targetRecord.sourceFile === "string" ? targetRecord.sourceFile : "";
63
- if (!sourceFile) return null;
64
-
65
- const selector = typeof targetRecord.selector === "string" ? targetRecord.selector : undefined;
66
- const id = typeof targetRecord.id === "string" ? targetRecord.id : undefined;
67
- if (!selector && !id) return null;
68
-
69
- const width = finiteNumber(record.width);
70
- const height = finiteNumber(record.height);
71
- if (width == null || height == null || width <= 0 || height <= 0) return null;
72
-
73
- return {
74
- kind: "box-size",
75
- target: {
76
- sourceFile,
77
- selector,
78
- selectorIndex: finiteNumber(targetRecord.selectorIndex) ?? undefined,
79
- id,
80
- },
81
- width,
82
- height,
83
- updatedAt: typeof record.updatedAt === "string" ? record.updatedAt : undefined,
84
- };
85
- }
86
-
87
- function parseRotationEdit(value: unknown): StudioRotationEdit | null {
88
- if (!value || typeof value !== "object") return null;
89
- const record = value as Record<string, unknown>;
90
- if (record.kind !== "rotation") return null;
91
- const target = record.target;
92
- if (!target || typeof target !== "object") return null;
93
- const targetRecord = target as Record<string, unknown>;
94
- const sourceFile = typeof targetRecord.sourceFile === "string" ? targetRecord.sourceFile : "";
95
- if (!sourceFile) return null;
96
-
97
- const selector = typeof targetRecord.selector === "string" ? targetRecord.selector : undefined;
98
- const id = typeof targetRecord.id === "string" ? targetRecord.id : undefined;
99
- if (!selector && !id) return null;
100
-
101
- const angle = finiteNumber(record.angle);
102
- if (angle == null) return null;
103
-
104
- return {
105
- kind: "rotation",
106
- target: {
107
- sourceFile,
108
- selector,
109
- selectorIndex: finiteNumber(targetRecord.selectorIndex) ?? undefined,
110
- id,
111
- },
112
- angle,
113
- updatedAt: typeof record.updatedAt === "string" ? record.updatedAt : undefined,
114
- };
115
- }
116
-
117
- function parseManualEdit(value: unknown): StudioManualEdit | null {
118
- return parsePathOffsetEdit(value) ?? parseBoxSizeEdit(value) ?? parseRotationEdit(value);
119
- }
120
-
121
- export function parseStudioManualEditManifest(content: string): StudioManualEditManifest {
122
- if (!content.trim()) return emptyStudioManualEditManifest();
123
-
124
- try {
125
- const parsed = JSON.parse(content) as unknown;
126
- if (!parsed || typeof parsed !== "object") return emptyStudioManualEditManifest();
127
- const edits = (parsed as { edits?: unknown }).edits;
128
- if (!Array.isArray(edits)) return emptyStudioManualEditManifest();
129
- return {
130
- version: 1,
131
- edits: edits.map(parseManualEdit).filter((edit): edit is StudioManualEdit => edit !== null),
132
- };
133
- } catch {
134
- return emptyStudioManualEditManifest();
135
- }
136
- }
137
-
138
- export function serializeStudioManualEditManifest(manifest: StudioManualEditManifest): string {
139
- return `${JSON.stringify(manifest, null, 2)}\n`;
6
+ export function roundRotationAngle(angle: number): number {
7
+ return Math.round(angle * 10) / 10;
140
8
  }
141
9
 
142
10
  /* ── File path utilities ──────────────────────────────────────────── */
@@ -172,109 +40,3 @@ function readStudioFileChangePathFromValue(value: unknown): string | null {
172
40
  export function readStudioFileChangePath(payload: unknown): string | null {
173
41
  return readStudioFileChangePathFromValue(payload);
174
42
  }
175
-
176
- export function isStudioManualEditManifestPath(path: string | null): boolean {
177
- if (!path) return false;
178
- const normalized = normalizeStudioFileChangePath(path);
179
- return (
180
- normalized === STUDIO_MANUAL_EDITS_PATH || normalized.endsWith(`/${STUDIO_MANUAL_EDITS_PATH}`)
181
- );
182
- }
183
-
184
- /* ── Target / upsert helpers ──────────────────────────────────────── */
185
- function selectionTarget(selection: DomEditSelection): StudioManualEditTarget {
186
- return {
187
- sourceFile: selection.sourceFile || "index.html",
188
- selector: selection.selector,
189
- selectorIndex: selection.selectorIndex,
190
- id: selection.id ?? undefined,
191
- };
192
- }
193
-
194
- function targetKey(target: StudioManualEditTarget): string {
195
- return [
196
- target.sourceFile,
197
- target.id ?? "",
198
- target.selector ?? "",
199
- target.selectorIndex ?? "",
200
- ].join("|");
201
- }
202
-
203
- export function roundRotationAngle(angle: number): number {
204
- return Math.round(angle * 10) / 10;
205
- }
206
-
207
- export function upsertStudioPathOffsetEdit(
208
- manifest: StudioManualEditManifest,
209
- selection: DomEditSelection,
210
- offset: { x: number; y: number },
211
- ): StudioManualEditManifest {
212
- const target = selectionTarget(selection);
213
- const key = targetKey(target);
214
- const nextEdit: StudioPathOffsetEdit = {
215
- kind: "path-offset",
216
- target,
217
- x: Math.round(offset.x),
218
- y: Math.round(offset.y),
219
- updatedAt: new Date().toISOString(),
220
- };
221
-
222
- const edits = manifest.edits.filter(
223
- (edit) => edit.kind !== "path-offset" || targetKey(edit.target) !== key,
224
- );
225
- edits.push(nextEdit);
226
- return { version: 1, edits };
227
- }
228
-
229
- export function upsertStudioBoxSizeEdit(
230
- manifest: StudioManualEditManifest,
231
- selection: DomEditSelection,
232
- size: { width: number; height: number },
233
- ): StudioManualEditManifest {
234
- const target = selectionTarget(selection);
235
- const key = targetKey(target);
236
- const nextEdit: StudioBoxSizeEdit = {
237
- kind: "box-size",
238
- target,
239
- width: Math.round(Math.max(1, size.width)),
240
- height: Math.round(Math.max(1, size.height)),
241
- updatedAt: new Date().toISOString(),
242
- };
243
-
244
- const edits = manifest.edits.filter(
245
- (edit) => edit.kind !== "box-size" || targetKey(edit.target) !== key,
246
- );
247
- edits.push(nextEdit);
248
- return { version: 1, edits };
249
- }
250
-
251
- export function upsertStudioRotationEdit(
252
- manifest: StudioManualEditManifest,
253
- selection: DomEditSelection,
254
- rotation: { angle: number },
255
- ): StudioManualEditManifest {
256
- const target = selectionTarget(selection);
257
- const key = targetKey(target);
258
- const nextEdit: StudioRotationEdit = {
259
- kind: "rotation",
260
- target,
261
- angle: roundRotationAngle(rotation.angle),
262
- updatedAt: new Date().toISOString(),
263
- };
264
-
265
- const edits = manifest.edits.filter(
266
- (edit) => edit.kind !== "rotation" || targetKey(edit.target) !== key,
267
- );
268
- edits.push(nextEdit);
269
- return { version: 1, edits };
270
- }
271
-
272
- export function removeStudioManualEditsForSelection(
273
- manifest: StudioManualEditManifest,
274
- selection: DomEditSelection,
275
- ): StudioManualEditManifest {
276
- const key = targetKey(selectionTarget(selection));
277
- const edits = manifest.edits.filter((edit) => targetKey(edit.target) !== key);
278
- if (edits.length === manifest.edits.length) return manifest;
279
- return { version: 1, edits };
280
- }
@@ -1,5 +1,4 @@
1
1
  /* ── Public constants ──────────────────────────────────────────────── */
2
- export const STUDIO_MANUAL_EDITS_PATH = ".hyperframes/studio-manual-edits.json";
3
2
  export const STUDIO_OFFSET_X_PROP = "--hf-studio-offset-x";
4
3
  export const STUDIO_OFFSET_Y_PROP = "--hf-studio-offset-y";
5
4
  export const STUDIO_WIDTH_PROP = "--hf-studio-width";
@@ -40,44 +39,6 @@ export const STUDIO_MANUAL_EDITS_PLAYBACK_FRAME_PROP = "__hfStudioManualEditsPla
40
39
 
41
40
  export const STUDIO_ROTATION_TRANSFORM_ORIGIN = "center center";
42
41
 
43
- /* ── Edit types ───────────────────────────────────────────────────── */
44
- export interface StudioManualEditTarget {
45
- sourceFile: string;
46
- selector?: string;
47
- selectorIndex?: number;
48
- id?: string;
49
- }
50
-
51
- export interface StudioPathOffsetEdit {
52
- kind: "path-offset";
53
- target: StudioManualEditTarget;
54
- x: number;
55
- y: number;
56
- updatedAt?: string;
57
- }
58
-
59
- export interface StudioBoxSizeEdit {
60
- kind: "box-size";
61
- target: StudioManualEditTarget;
62
- width: number;
63
- height: number;
64
- updatedAt?: string;
65
- }
66
-
67
- export interface StudioRotationEdit {
68
- kind: "rotation";
69
- target: StudioManualEditTarget;
70
- angle: number;
71
- updatedAt?: string;
72
- }
73
-
74
- export type StudioManualEdit = StudioPathOffsetEdit | StudioBoxSizeEdit | StudioRotationEdit;
75
-
76
- export interface StudioManualEditManifest {
77
- version: 1;
78
- edits: StudioManualEdit[];
79
- }
80
-
81
42
  export type StudioManualEditSeekWindow = Window & {
82
43
  __hf?: Record<string, unknown>;
83
44
  __player?: Record<string, unknown>;
@@ -87,7 +48,7 @@ export type StudioManualEditSeekWindow = Window & {
87
48
  __hfStudioManualEditsPlaybackFrame?: number | null;
88
49
  };
89
50
 
90
- /* ── Snapshot types ───────────────────────────────────────────────── */
51
+ /* ── Snapshot types (used by drag/drop restore) ───────────────────── */
91
52
  export interface StudioBoxSizeSnapshot {
92
53
  width: string;
93
54
  height: string;
@@ -23,7 +23,7 @@ import {
23
23
  restoreStudioPathOffset,
24
24
  restoreStudioRotation,
25
25
  } from "./manualEdits";
26
- import { type GroupOverlayItem, type OverlayRect } from "./domEditOverlayGeometry";
26
+ import { type GroupOverlayItem, type OverlayRect, toOverlayRect } from "./domEditOverlayGeometry";
27
27
  import {
28
28
  BLOCKED_MOVE_THRESHOLD_PX,
29
29
  type BlockedMoveState,
@@ -43,6 +43,7 @@ import {
43
43
  // Refs are stable across renders; values are read via .current.
44
44
  export type UseDomEditOverlayGesturesOptions = {
45
45
  overlayRef: RefObject<HTMLDivElement | null>;
46
+ iframeRef: RefObject<HTMLIFrameElement | null>;
46
47
  boxRef: RefObject<HTMLDivElement | null>;
47
48
  selectionRef: RefObject<DomEditSelection | null>;
48
49
  overlayRectRef: RefObject<OverlayRect | null>;
@@ -194,17 +195,31 @@ export function createDomEditOverlayGestureHandlers(opts: UseDomEditOverlayGestu
194
195
  dy,
195
196
  uniform: e.shiftKey,
196
197
  });
198
+ applyStudioBoxSizeDraft(sel.element, nextSize);
199
+
200
+ // Re-read BCR after applying dimensions. For elements with a GSAP
201
+ // scale transform and centered transform-origin the visual top-left
202
+ // drifts and the visual size diverges from the raw CSS size, so BCR
203
+ // is the only accurate source for both.
204
+ const overlayEl = opts.overlayRef.current;
205
+ const iframe = opts.iframeRef.current;
206
+ const refreshed = overlayEl && iframe ? toOverlayRect(overlayEl, iframe, sel.element) : null;
207
+ const overlayLeft = refreshed ? refreshed.left : g.originLeft;
208
+ const overlayTop = refreshed ? refreshed.top : g.originTop;
209
+ const overlayWidth = refreshed ? refreshed.width : nextSize.overlayWidth;
210
+ const overlayHeight = refreshed ? refreshed.height : nextSize.overlayHeight;
211
+ box.style.left = `${overlayLeft}px`;
212
+ box.style.top = `${overlayTop}px`;
213
+ box.style.width = `${overlayWidth}px`;
214
+ box.style.height = `${overlayHeight}px`;
197
215
  setDraftOverlayRect({
198
- left: g.originLeft,
199
- top: g.originTop,
200
- width: nextSize.overlayWidth,
201
- height: nextSize.overlayHeight,
216
+ left: overlayLeft,
217
+ top: overlayTop,
218
+ width: overlayWidth,
219
+ height: overlayHeight,
202
220
  editScaleX: g.editScaleX,
203
221
  editScaleY: g.editScaleY,
204
222
  });
205
- box.style.width = `${nextSize.overlayWidth}px`;
206
- box.style.height = `${nextSize.overlayHeight}px`;
207
- applyStudioBoxSizeDraft(sel.element, nextSize);
208
223
  }
209
224
  };
210
225
 
@@ -281,6 +296,7 @@ export function createDomEditOverlayGestureHandlers(opts: UseDomEditOverlayGestu
281
296
  box.style.height = `${g.originHeight}px`;
282
297
  }
283
298
  restoreGestureOverlayRect(g);
299
+ opts.suppressNextBoxClickRef.current = true;
284
300
  return;
285
301
  }
286
302
 
@@ -341,6 +357,7 @@ export function createDomEditOverlayGestureHandlers(opts: UseDomEditOverlayGestu
341
357
  if (g.pathOffsetMember) endManualOffsetDragMembers([g.pathOffsetMember]);
342
358
  });
343
359
  } else {
360
+ opts.suppressNextBoxClickRef.current = true;
344
361
  const finalSize = readStudioBoxSize(sel.element);
345
362
  applyStudioBoxSize(sel.element, finalSize);
346
363
  void Promise.resolve(opts.onBoxSizeCommitRef.current(sel, finalSize))
@@ -264,7 +264,7 @@ export const NLEPreview = memo(function NLEPreview({
264
264
  <div className="flex flex-col h-full min-h-0">
265
265
  <div
266
266
  ref={viewportRef}
267
- className="relative flex-1 flex items-center justify-center p-2 overflow-hidden min-h-0 outline-none focus:ring-1 focus:ring-studio-accent/40"
267
+ className="relative flex-1 flex items-center justify-center p-2 overflow-hidden min-h-0 outline-none focus:ring-1 focus:ring-studio-accent/40 bg-neutral-700"
268
268
  tabIndex={0}
269
269
  aria-label="Composition preview"
270
270
  onPointerDown={handlePointerDown}
@@ -8,6 +8,8 @@ interface CompositionsTabProps {
8
8
  }
9
9
 
10
10
  const DEFAULT_PREVIEW_STAGE = { width: 1920, height: 1080 };
11
+ const CARD_W = 80;
12
+ const CARD_H = 45;
11
13
  const THUMBNAIL_SEEK_TIME_SECONDS = 3;
12
14
  const THUMBNAIL_PLAYBACK_SYNC_ATTEMPTS = 10;
13
15
 
@@ -132,11 +134,13 @@ function CompCard({
132
134
  const name = comp.replace(/^compositions\//, "").replace(/\.html$/, "");
133
135
  const previewUrl = `/api/projects/${projectId}/preview/comp/${comp}`;
134
136
  const previewScale = resolveCompositionPreviewScale({
135
- cardWidth: 80,
136
- cardHeight: 45,
137
+ cardWidth: CARD_W,
138
+ cardHeight: CARD_H,
137
139
  stageWidth: stageSize.width,
138
140
  stageHeight: stageSize.height,
139
141
  });
142
+ const thumbnailOffsetX = (CARD_W - stageSize.width * previewScale) / 2;
143
+ const thumbnailOffsetY = (CARD_H - stageSize.height * previewScale) / 2;
140
144
 
141
145
  useEffect(() => {
142
146
  requestIframePlaybackSync(hovered);
@@ -166,11 +170,13 @@ function CompCard({
166
170
  src={previewUrl}
167
171
  sandbox="allow-scripts allow-same-origin"
168
172
  loading="lazy"
169
- className="absolute left-0 top-0 border-none pointer-events-none"
173
+ className="absolute border-none pointer-events-none"
170
174
  style={{
171
175
  transformOrigin: "0 0",
172
176
  width: stageSize.width,
173
177
  height: stageSize.height,
178
+ left: thumbnailOffsetX,
179
+ top: thumbnailOffsetY,
174
180
  transform: `scale(${previewScale})`,
175
181
  }}
176
182
  onLoad={(e) => {
@@ -44,6 +44,7 @@ export function DomEditProvider({
44
44
  handleBlockedDomMove,
45
45
  handleDomManualDragStart,
46
46
  handleDomEditElementDelete,
47
+ buildDomSelectionFromTarget,
47
48
  buildDomSelectionForTimelineElement,
48
49
  updateDomEditHoverSelection,
49
50
  resolveImportedFontAsset,
@@ -89,6 +90,7 @@ export function DomEditProvider({
89
90
  handleBlockedDomMove,
90
91
  handleDomManualDragStart,
91
92
  handleDomEditElementDelete,
93
+ buildDomSelectionFromTarget,
92
94
  buildDomSelectionForTimelineElement,
93
95
  updateDomEditHoverSelection,
94
96
  resolveImportedFontAsset,
@@ -128,6 +130,7 @@ export function DomEditProvider({
128
130
  handleBlockedDomMove,
129
131
  handleDomManualDragStart,
130
132
  handleDomEditElementDelete,
133
+ buildDomSelectionFromTarget,
131
134
  buildDomSelectionForTimelineElement,
132
135
  updateDomEditHoverSelection,
133
136
  resolveImportedFontAsset,
@@ -17,6 +17,7 @@ export function FileManagerProvider({
17
17
  setEditingFile,
18
18
  projectDir,
19
19
  fileTree,
20
+ fileTreeLoaded,
20
21
  setFileTree,
21
22
  editingPathRef,
22
23
  projectIdRef,
@@ -52,6 +53,7 @@ export function FileManagerProvider({
52
53
  setEditingFile,
53
54
  projectDir,
54
55
  fileTree,
56
+ fileTreeLoaded,
55
57
  setFileTree,
56
58
  editingPathRef,
57
59
  projectIdRef,
@@ -81,6 +83,7 @@ export function FileManagerProvider({
81
83
  setEditingFile,
82
84
  projectDir,
83
85
  fileTree,
86
+ fileTreeLoaded,
84
87
  setFileTree,
85
88
  editingPathRef,
86
89
  projectIdRef,
@@ -3,7 +3,6 @@ import { usePlayerStore } from "../player";
3
3
  import type { TimelineElement } from "../player";
4
4
  import type { DomEditSelection } from "../components/editor/domEditing";
5
5
  import type { LeftSidebarHandle } from "../components/sidebar/LeftSidebar";
6
- import { STUDIO_MANUAL_EDITS_PATH } from "../components/editor/manualEdits";
7
6
  import { STUDIO_MOTION_PATH } from "../components/editor/studioMotion";
8
7
  import { shouldHandleTimelineToggleHotkey, isEditableTarget } from "../utils/timelineDiscovery";
9
8
  import { shouldIgnoreHistoryShortcut } from "../utils/studioHelpers";
@@ -85,9 +84,7 @@ export function useAppHotkeys({
85
84
 
86
85
  const readHistoryProjectFile = useCallback(
87
86
  async (path: string): Promise<string> => {
88
- return path === STUDIO_MANUAL_EDITS_PATH || path === STUDIO_MOTION_PATH
89
- ? readOptionalProjectFile(path)
90
- : readProjectFile(path);
87
+ return path === STUDIO_MOTION_PATH ? readOptionalProjectFile(path) : readProjectFile(path);
91
88
  },
92
89
  [readOptionalProjectFile, readProjectFile],
93
90
  );