@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
@@ -0,0 +1,135 @@
1
+ import type { RightPanelTab } from "./studioHelpers";
2
+ import { buildProjectHash, parseProjectHashRoute } from "./projectRouting";
3
+ import {
4
+ STUDIO_INSPECTOR_PANELS_ENABLED,
5
+ STUDIO_MOTION_PANEL_ENABLED,
6
+ } from "../components/editor/manualEditingAvailability";
7
+
8
+ export interface StudioUrlSelectionState {
9
+ sourceFile?: string;
10
+ id?: string;
11
+ selector?: string;
12
+ selectorIndex?: number;
13
+ }
14
+
15
+ export interface StudioUrlState {
16
+ activeCompPath: string | null;
17
+ currentTime: number | null;
18
+ rightPanelTab: RightPanelTab | null;
19
+ rightCollapsed: boolean | null;
20
+ timelineVisible: boolean | null;
21
+ selection: StudioUrlSelectionState | null;
22
+ }
23
+
24
+ const VALID_TABS: RightPanelTab[] = ["layers", "design", "motion", "renders"];
25
+
26
+ export function normalizeStudioUrlPanelTab(
27
+ tab: RightPanelTab | null,
28
+ options: {
29
+ inspectorPanelsEnabled?: boolean;
30
+ motionPanelEnabled?: boolean;
31
+ } = {},
32
+ ): RightPanelTab | null {
33
+ if (!tab) return null;
34
+ if (!VALID_TABS.includes(tab)) return null;
35
+ const inspectorPanelsEnabled = options.inspectorPanelsEnabled ?? STUDIO_INSPECTOR_PANELS_ENABLED;
36
+ const motionPanelEnabled = options.motionPanelEnabled ?? STUDIO_MOTION_PANEL_ENABLED;
37
+
38
+ if (!inspectorPanelsEnabled && tab !== "renders") return "renders";
39
+ if (tab === "motion" && !motionPanelEnabled) return "design";
40
+ return tab;
41
+ }
42
+
43
+ export function normalizeStudioCompositionPath(
44
+ activeCompPath: string | null,
45
+ fileTree: string[],
46
+ ): string | null {
47
+ if (!activeCompPath || activeCompPath === "index.html") return null;
48
+ return fileTree.includes(activeCompPath) ? activeCompPath : null;
49
+ }
50
+
51
+ function parseBoolean(value: string | null): boolean | null {
52
+ if (value === "1") return true;
53
+ if (value === "0") return false;
54
+ return null;
55
+ }
56
+
57
+ function parseNumber(value: string | null): number | null {
58
+ if (value == null || value === "") return null;
59
+ const parsed = Number(value);
60
+ return Number.isFinite(parsed) ? parsed : null;
61
+ }
62
+
63
+ function parseTab(value: string | null): RightPanelTab | null {
64
+ return VALID_TABS.includes(value as RightPanelTab) ? (value as RightPanelTab) : null;
65
+ }
66
+
67
+ function normalizeSelection(params: URLSearchParams): StudioUrlSelectionState | null {
68
+ const sourceFile = params.get("selFile") || undefined;
69
+ const id = params.get("selId") || undefined;
70
+ const selector = params.get("selSelector") || undefined;
71
+ const selectorIndex = parseNumber(params.get("selIndex"));
72
+
73
+ if (!sourceFile && !id && !selector) return null;
74
+
75
+ return {
76
+ sourceFile,
77
+ id,
78
+ selector,
79
+ selectorIndex: selectorIndex != null ? Math.max(0, Math.floor(selectorIndex)) : undefined,
80
+ };
81
+ }
82
+
83
+ export function defaultStudioUrlState(): StudioUrlState {
84
+ return {
85
+ activeCompPath: null,
86
+ currentTime: null,
87
+ rightPanelTab: null,
88
+ rightCollapsed: null,
89
+ timelineVisible: null,
90
+ selection: null,
91
+ };
92
+ }
93
+
94
+ export function parseStudioUrlStateFromHash(hash: string): StudioUrlState {
95
+ const route = parseProjectHashRoute(hash);
96
+ if (!route) return defaultStudioUrlState();
97
+
98
+ const { params } = route;
99
+ return {
100
+ activeCompPath: params.get("comp") || null,
101
+ currentTime: parseNumber(params.get("t")),
102
+ rightPanelTab: normalizeStudioUrlPanelTab(parseTab(params.get("tab"))),
103
+ rightCollapsed: parseBoolean(params.get("rc")),
104
+ timelineVisible: parseBoolean(params.get("tv")),
105
+ selection: normalizeSelection(params),
106
+ };
107
+ }
108
+
109
+ export function readStudioUrlStateFromWindow(): StudioUrlState {
110
+ if (typeof window === "undefined") return defaultStudioUrlState();
111
+ return parseStudioUrlStateFromHash(window.location.hash);
112
+ }
113
+
114
+ export function buildStudioHash(projectId: string, state: StudioUrlState): string {
115
+ const params = new URLSearchParams();
116
+
117
+ params.set("v", "1");
118
+ if (state.activeCompPath) params.set("comp", state.activeCompPath);
119
+ if (state.currentTime != null && Number.isFinite(state.currentTime)) {
120
+ params.set("t", String(Math.max(0, Math.round(state.currentTime * 1000) / 1000)));
121
+ }
122
+ if (state.rightPanelTab) params.set("tab", state.rightPanelTab);
123
+ if (state.rightCollapsed != null) params.set("rc", state.rightCollapsed ? "1" : "0");
124
+ if (state.timelineVisible != null) params.set("tv", state.timelineVisible ? "1" : "0");
125
+ if (state.selection) {
126
+ if (state.selection.sourceFile) params.set("selFile", state.selection.sourceFile);
127
+ if (state.selection.id) params.set("selId", state.selection.id);
128
+ if (state.selection.selector) params.set("selSelector", state.selection.selector);
129
+ if (typeof state.selection.selectorIndex === "number") {
130
+ params.set("selIndex", String(Math.max(0, Math.floor(state.selection.selectorIndex))));
131
+ }
132
+ }
133
+
134
+ return buildProjectHash(projectId, params);
135
+ }