@hyperframes/studio 0.6.0-alpha.8 → 0.6.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/{hyperframes-player-DjsVzYFP.js → hyperframes-player-DOFETgjy.js} +1 -1
- package/dist/assets/index-D1JDq7Gg.css +1 -0
- package/dist/assets/index-DUqUmaoH.js +117 -0
- package/dist/favicon.svg +14 -0
- package/dist/index.html +3 -2
- package/package.json +9 -9
- package/src/App.tsx +428 -4299
- package/src/components/AskAgentModal.tsx +120 -0
- package/src/components/StudioHeader.tsx +133 -0
- package/src/components/StudioLeftSidebar.tsx +125 -0
- package/src/components/StudioPreviewArea.tsx +163 -0
- package/src/components/StudioRightPanel.tsx +198 -0
- package/src/components/TimelineToolbar.tsx +89 -0
- package/src/components/editor/DomEditOverlay.tsx +15 -1
- package/src/components/editor/PropertyPanel.test.ts +0 -49
- package/src/components/editor/PropertyPanel.tsx +132 -2763
- package/src/components/editor/domEditing.ts +38 -5
- package/src/components/editor/manualEditingAvailability.test.ts +2 -2
- package/src/components/editor/manualEditingAvailability.ts +1 -1
- package/src/components/editor/manualEdits.ts +32 -0
- package/src/components/editor/propertyPanelColor.tsx +371 -0
- package/src/components/editor/propertyPanelFill.tsx +421 -0
- package/src/components/editor/propertyPanelFont.tsx +455 -0
- package/src/components/editor/propertyPanelHelpers.ts +401 -0
- package/src/components/editor/propertyPanelPrimitives.tsx +357 -0
- package/src/components/editor/propertyPanelSections.tsx +453 -0
- package/src/components/editor/propertyPanelStyleSections.tsx +411 -0
- package/src/components/nle/NLELayout.tsx +8 -11
- package/src/components/nle/NLEPreview.tsx +3 -0
- package/src/components/renders/RenderQueue.tsx +102 -31
- package/src/components/renders/useRenderQueue.ts +8 -2
- package/src/components/sidebar/LeftSidebar.tsx +186 -186
- package/src/contexts/DomEditContext.tsx +137 -0
- package/src/contexts/FileManagerContext.tsx +110 -0
- package/src/contexts/PanelLayoutContext.tsx +68 -0
- package/src/contexts/StudioContext.tsx +135 -0
- package/src/hooks/useAppHotkeys.ts +326 -0
- package/src/hooks/useAskAgentModal.ts +162 -0
- package/src/hooks/useCaptionDetection.ts +132 -0
- package/src/hooks/useCompositionDimensions.ts +25 -0
- package/src/hooks/useConsoleErrorCapture.ts +60 -0
- package/src/hooks/useDomEditCommits.ts +437 -0
- package/src/hooks/useDomEditSession.ts +342 -0
- package/src/hooks/useDomEditTextCommits.ts +330 -0
- package/src/hooks/useDomSelection.ts +398 -0
- package/src/hooks/useFileManager.ts +431 -0
- package/src/hooks/useFrameCapture.ts +77 -0
- package/src/hooks/useLintModal.ts +35 -0
- package/src/hooks/useManifestPersistence.ts +492 -0
- package/src/hooks/usePanelLayout.ts +68 -0
- package/src/hooks/usePreviewInteraction.ts +153 -0
- package/src/hooks/useRenderClipContent.ts +124 -0
- package/src/hooks/useTimelineEditing.ts +472 -0
- package/src/player/components/Player.tsx +35 -4
- package/src/player/components/Timeline.test.ts +0 -8
- package/src/player/components/Timeline.tsx +10 -103
- package/src/player/components/TimelineClip.tsx +9 -244
- package/src/player/hooks/useTimelinePlayer.ts +140 -103
- package/src/utils/domEditHelpers.ts +50 -0
- package/src/utils/studioFontHelpers.ts +83 -0
- package/src/utils/studioHelpers.ts +214 -0
- package/src/utils/studioPreviewHelpers.ts +185 -0
- package/src/utils/timelineDiscovery.ts +1 -1
- package/dist/assets/index-14zH9lqh.css +0 -1
- package/dist/assets/index-ClYcrksa.js +0 -108
- package/src/player/components/TimelineClip.test.ts +0 -92
|
@@ -14,8 +14,14 @@ export interface RenderJob {
|
|
|
14
14
|
// Mirrors `CanvasResolution` from @hyperframes/core. Kept local because
|
|
15
15
|
// studio's tsconfig doesn't include node types, and the core barrel
|
|
16
16
|
// transitively pulls in modules with `node:fs` imports. Drift risk is
|
|
17
|
-
// low (
|
|
18
|
-
export type ResolutionPreset =
|
|
17
|
+
// low (6 string literals kept in sync manually with CANVAS_DIMENSIONS).
|
|
18
|
+
export type ResolutionPreset =
|
|
19
|
+
| "landscape"
|
|
20
|
+
| "portrait"
|
|
21
|
+
| "landscape-4k"
|
|
22
|
+
| "portrait-4k"
|
|
23
|
+
| "square"
|
|
24
|
+
| "square-4k";
|
|
19
25
|
|
|
20
26
|
export interface StartRenderOptions {
|
|
21
27
|
fps?: number;
|
|
@@ -1,10 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
memo,
|
|
3
|
+
useState,
|
|
4
|
+
useCallback,
|
|
5
|
+
useImperativeHandle,
|
|
6
|
+
forwardRef,
|
|
7
|
+
type ReactNode,
|
|
8
|
+
} from "react";
|
|
3
9
|
import { CompositionsTab } from "./CompositionsTab";
|
|
4
10
|
import { AssetsTab } from "./AssetsTab";
|
|
5
11
|
import { FileTree } from "../editor/FileTree";
|
|
6
12
|
|
|
7
|
-
type SidebarTab = "compositions" | "assets" | "code";
|
|
13
|
+
export type SidebarTab = "compositions" | "assets" | "code";
|
|
14
|
+
|
|
15
|
+
export interface LeftSidebarHandle {
|
|
16
|
+
selectTab: (tab: SidebarTab) => void;
|
|
17
|
+
}
|
|
8
18
|
|
|
9
19
|
const STORAGE_KEY = "hf-studio-sidebar-tab";
|
|
10
20
|
|
|
@@ -39,201 +49,191 @@ interface LeftSidebarProps {
|
|
|
39
49
|
takeoverContent?: ReactNode;
|
|
40
50
|
}
|
|
41
51
|
|
|
42
|
-
export const LeftSidebar = memo(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
52
|
+
export const LeftSidebar = memo(
|
|
53
|
+
forwardRef<LeftSidebarHandle, LeftSidebarProps>(function LeftSidebar(
|
|
54
|
+
{
|
|
55
|
+
width = 240,
|
|
56
|
+
projectId,
|
|
57
|
+
compositions,
|
|
58
|
+
assets,
|
|
59
|
+
activeComposition,
|
|
60
|
+
onSelectComposition,
|
|
61
|
+
onImportFiles,
|
|
62
|
+
fileTree: fileProp,
|
|
63
|
+
editingFile,
|
|
64
|
+
onSelectFile,
|
|
65
|
+
onCreateFile,
|
|
66
|
+
onCreateFolder,
|
|
67
|
+
onDeleteFile,
|
|
68
|
+
onRenameFile,
|
|
69
|
+
onDuplicateFile,
|
|
70
|
+
onMoveFile,
|
|
71
|
+
codeChildren,
|
|
72
|
+
onLint,
|
|
73
|
+
linting,
|
|
74
|
+
onToggleCollapse,
|
|
75
|
+
takeoverContent,
|
|
76
|
+
},
|
|
77
|
+
ref,
|
|
78
|
+
) {
|
|
79
|
+
const [tab, setTab] = useState<SidebarTab>(getPersistedTab);
|
|
66
80
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
81
|
+
const selectTab = useCallback((t: SidebarTab) => {
|
|
82
|
+
setTab(t);
|
|
83
|
+
localStorage.setItem(STORAGE_KEY, t);
|
|
84
|
+
}, []);
|
|
71
85
|
|
|
72
|
-
|
|
73
|
-
useMountEffect(() => {
|
|
74
|
-
const handler = (e: KeyboardEvent) => {
|
|
75
|
-
if (!e.metaKey && !e.ctrlKey) return;
|
|
76
|
-
if (e.key === "1") {
|
|
77
|
-
e.preventDefault();
|
|
78
|
-
selectTab("compositions");
|
|
79
|
-
}
|
|
80
|
-
if (e.key === "2") {
|
|
81
|
-
e.preventDefault();
|
|
82
|
-
selectTab("assets");
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
window.addEventListener("keydown", handler);
|
|
86
|
-
return () => window.removeEventListener("keydown", handler);
|
|
87
|
-
});
|
|
86
|
+
useImperativeHandle(ref, () => ({ selectTab }), [selectTab]);
|
|
88
87
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
>
|
|
105
|
-
<button
|
|
106
|
-
type="button"
|
|
107
|
-
onClick={() => selectTab("code")}
|
|
108
|
-
className={`rounded-[14px] px-2.5 py-2 text-[10px] font-semibold transition-all ${
|
|
109
|
-
tab === "code"
|
|
110
|
-
? "bg-neutral-800 text-white"
|
|
111
|
-
: "text-neutral-500 hover:text-neutral-200"
|
|
112
|
-
}`}
|
|
113
|
-
>
|
|
114
|
-
Code
|
|
115
|
-
</button>
|
|
116
|
-
<button
|
|
117
|
-
type="button"
|
|
118
|
-
onClick={() => selectTab("compositions")}
|
|
119
|
-
className={`rounded-[14px] px-2.5 py-2 text-[10px] font-semibold transition-all ${
|
|
120
|
-
tab === "compositions"
|
|
121
|
-
? "bg-neutral-800 text-white"
|
|
122
|
-
: "text-neutral-500 hover:text-neutral-200"
|
|
123
|
-
}`}
|
|
124
|
-
>
|
|
125
|
-
Compositions
|
|
126
|
-
</button>
|
|
127
|
-
<button
|
|
128
|
-
type="button"
|
|
129
|
-
onClick={() => selectTab("assets")}
|
|
130
|
-
className={`rounded-[14px] px-2.5 py-2 text-[10px] font-semibold transition-all ${
|
|
131
|
-
tab === "assets"
|
|
132
|
-
? "bg-neutral-800 text-white"
|
|
133
|
-
: "text-neutral-500 hover:text-neutral-200"
|
|
134
|
-
}`}
|
|
88
|
+
return (
|
|
89
|
+
<div
|
|
90
|
+
className="flex flex-col h-full bg-neutral-950 border-r border-neutral-800/50"
|
|
91
|
+
style={{ width }}
|
|
92
|
+
>
|
|
93
|
+
{takeoverContent ? (
|
|
94
|
+
<div className="flex min-h-0 flex-1">{takeoverContent}</div>
|
|
95
|
+
) : (
|
|
96
|
+
<>
|
|
97
|
+
{/* Tabs — Code first */}
|
|
98
|
+
<div className="border-b border-neutral-800/50 px-3 py-3 flex-shrink-0">
|
|
99
|
+
<div className="flex items-center gap-2">
|
|
100
|
+
<div
|
|
101
|
+
className="grid min-w-0 flex-1 gap-0.5 rounded-[18px] bg-neutral-900 p-1 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]"
|
|
102
|
+
style={{ gridTemplateColumns: "1fr 1fr 1fr" }}
|
|
135
103
|
>
|
|
136
|
-
|
|
137
|
-
|
|
104
|
+
<button
|
|
105
|
+
type="button"
|
|
106
|
+
onClick={() => selectTab("code")}
|
|
107
|
+
className={`rounded-[14px] px-1.5 py-2 text-[10px] font-semibold truncate transition-all ${
|
|
108
|
+
tab === "code"
|
|
109
|
+
? "bg-neutral-800 text-white"
|
|
110
|
+
: "text-neutral-500 hover:text-neutral-200"
|
|
111
|
+
}`}
|
|
112
|
+
>
|
|
113
|
+
Code
|
|
114
|
+
</button>
|
|
115
|
+
<button
|
|
116
|
+
type="button"
|
|
117
|
+
onClick={() => selectTab("compositions")}
|
|
118
|
+
className={`rounded-[14px] px-1.5 py-2 text-[10px] font-semibold truncate transition-all ${
|
|
119
|
+
tab === "compositions"
|
|
120
|
+
? "bg-neutral-800 text-white"
|
|
121
|
+
: "text-neutral-500 hover:text-neutral-200"
|
|
122
|
+
}`}
|
|
123
|
+
>
|
|
124
|
+
Comps
|
|
125
|
+
</button>
|
|
126
|
+
<button
|
|
127
|
+
type="button"
|
|
128
|
+
onClick={() => selectTab("assets")}
|
|
129
|
+
className={`rounded-[14px] px-1.5 py-2 text-[10px] font-semibold truncate transition-all ${
|
|
130
|
+
tab === "assets"
|
|
131
|
+
? "bg-neutral-800 text-white"
|
|
132
|
+
: "text-neutral-500 hover:text-neutral-200"
|
|
133
|
+
}`}
|
|
134
|
+
>
|
|
135
|
+
Assets
|
|
136
|
+
</button>
|
|
137
|
+
</div>
|
|
138
|
+
{onToggleCollapse && (
|
|
139
|
+
<button
|
|
140
|
+
type="button"
|
|
141
|
+
onClick={onToggleCollapse}
|
|
142
|
+
className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-md border border-transparent text-neutral-500 transition-colors hover:border-neutral-800 hover:bg-neutral-900 hover:text-neutral-300"
|
|
143
|
+
title="Hide sidebar"
|
|
144
|
+
aria-label="Hide sidebar"
|
|
145
|
+
>
|
|
146
|
+
<svg
|
|
147
|
+
width="14"
|
|
148
|
+
height="14"
|
|
149
|
+
viewBox="0 0 24 24"
|
|
150
|
+
fill="none"
|
|
151
|
+
stroke="currentColor"
|
|
152
|
+
strokeWidth="1.5"
|
|
153
|
+
strokeLinecap="round"
|
|
154
|
+
strokeLinejoin="round"
|
|
155
|
+
aria-hidden="true"
|
|
156
|
+
>
|
|
157
|
+
<path d="m14 7-5 5 5 5" />
|
|
158
|
+
<path d="M19 4v16" />
|
|
159
|
+
</svg>
|
|
160
|
+
</button>
|
|
161
|
+
)}
|
|
138
162
|
</div>
|
|
139
|
-
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
{/* Tab content */}
|
|
166
|
+
{tab === "compositions" && (
|
|
167
|
+
<CompositionsTab
|
|
168
|
+
projectId={projectId}
|
|
169
|
+
compositions={compositions}
|
|
170
|
+
activeComposition={activeComposition}
|
|
171
|
+
onSelect={onSelectComposition}
|
|
172
|
+
/>
|
|
173
|
+
)}
|
|
174
|
+
{tab === "assets" && (
|
|
175
|
+
<AssetsTab
|
|
176
|
+
projectId={projectId}
|
|
177
|
+
assets={assets}
|
|
178
|
+
onImport={onImportFiles}
|
|
179
|
+
onDelete={onDeleteFile}
|
|
180
|
+
onRename={onRenameFile}
|
|
181
|
+
/>
|
|
182
|
+
)}
|
|
183
|
+
{tab === "code" && (
|
|
184
|
+
<div className="flex flex-1 min-h-0">
|
|
185
|
+
{(fileProp?.length ?? 0) > 0 && (
|
|
186
|
+
<div className="w-[160px] flex-shrink-0 border-r border-neutral-800 overflow-y-auto">
|
|
187
|
+
<FileTree
|
|
188
|
+
files={fileProp ?? []}
|
|
189
|
+
activeFile={editingFile?.path ?? null}
|
|
190
|
+
onSelectFile={onSelectFile ?? (() => {})}
|
|
191
|
+
onCreateFile={onCreateFile}
|
|
192
|
+
onCreateFolder={onCreateFolder}
|
|
193
|
+
onDeleteFile={onDeleteFile}
|
|
194
|
+
onRenameFile={onRenameFile}
|
|
195
|
+
onDuplicateFile={onDuplicateFile}
|
|
196
|
+
onMoveFile={onMoveFile}
|
|
197
|
+
onImportFiles={onImportFiles}
|
|
198
|
+
/>
|
|
199
|
+
</div>
|
|
200
|
+
)}
|
|
201
|
+
<div className="flex-1 overflow-hidden min-w-0">
|
|
202
|
+
{codeChildren ?? (
|
|
203
|
+
<div className="flex items-center justify-center h-full text-neutral-600 text-sm">
|
|
204
|
+
Select a file to edit
|
|
205
|
+
</div>
|
|
206
|
+
)}
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
)}
|
|
210
|
+
|
|
211
|
+
{/* Lint button pinned at the bottom */}
|
|
212
|
+
{onLint && (
|
|
213
|
+
<div className="border-t border-neutral-800 p-2 flex-shrink-0">
|
|
140
214
|
<button
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
className="
|
|
144
|
-
title="Hide sidebar"
|
|
145
|
-
aria-label="Hide sidebar"
|
|
215
|
+
onClick={onLint}
|
|
216
|
+
disabled={linting}
|
|
217
|
+
className="w-full flex items-center justify-center gap-1.5 px-2 py-1.5 rounded-md text-[11px] font-medium text-neutral-500 hover:text-amber-300 hover:bg-neutral-800 transition-colors disabled:opacity-40"
|
|
146
218
|
>
|
|
147
219
|
<svg
|
|
148
|
-
width="
|
|
149
|
-
height="
|
|
220
|
+
width="12"
|
|
221
|
+
height="12"
|
|
150
222
|
viewBox="0 0 24 24"
|
|
151
223
|
fill="none"
|
|
152
224
|
stroke="currentColor"
|
|
153
|
-
strokeWidth="
|
|
154
|
-
strokeLinecap="round"
|
|
155
|
-
strokeLinejoin="round"
|
|
156
|
-
aria-hidden="true"
|
|
225
|
+
strokeWidth="2"
|
|
157
226
|
>
|
|
158
|
-
<path d="
|
|
159
|
-
<path d="
|
|
227
|
+
<path d="M9 11l3 3L22 4" />
|
|
228
|
+
<path d="M21 12v7a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h11" />
|
|
160
229
|
</svg>
|
|
230
|
+
{linting ? "Linting…" : "Lint"}
|
|
161
231
|
</button>
|
|
162
|
-
)}
|
|
163
|
-
</div>
|
|
164
|
-
</div>
|
|
165
|
-
|
|
166
|
-
{/* Tab content */}
|
|
167
|
-
{tab === "compositions" && (
|
|
168
|
-
<CompositionsTab
|
|
169
|
-
projectId={projectId}
|
|
170
|
-
compositions={compositions}
|
|
171
|
-
activeComposition={activeComposition}
|
|
172
|
-
onSelect={onSelectComposition}
|
|
173
|
-
/>
|
|
174
|
-
)}
|
|
175
|
-
{tab === "assets" && (
|
|
176
|
-
<AssetsTab
|
|
177
|
-
projectId={projectId}
|
|
178
|
-
assets={assets}
|
|
179
|
-
onImport={onImportFiles}
|
|
180
|
-
onDelete={onDeleteFile}
|
|
181
|
-
onRename={onRenameFile}
|
|
182
|
-
/>
|
|
183
|
-
)}
|
|
184
|
-
{tab === "code" && (
|
|
185
|
-
<div className="flex flex-1 min-h-0">
|
|
186
|
-
{(fileProp?.length ?? 0) > 0 && (
|
|
187
|
-
<div className="w-[160px] flex-shrink-0 border-r border-neutral-800 overflow-y-auto">
|
|
188
|
-
<FileTree
|
|
189
|
-
files={fileProp ?? []}
|
|
190
|
-
activeFile={editingFile?.path ?? null}
|
|
191
|
-
onSelectFile={onSelectFile ?? (() => {})}
|
|
192
|
-
onCreateFile={onCreateFile}
|
|
193
|
-
onCreateFolder={onCreateFolder}
|
|
194
|
-
onDeleteFile={onDeleteFile}
|
|
195
|
-
onRenameFile={onRenameFile}
|
|
196
|
-
onDuplicateFile={onDuplicateFile}
|
|
197
|
-
onMoveFile={onMoveFile}
|
|
198
|
-
onImportFiles={onImportFiles}
|
|
199
|
-
/>
|
|
200
|
-
</div>
|
|
201
|
-
)}
|
|
202
|
-
<div className="flex-1 overflow-hidden min-w-0">
|
|
203
|
-
{codeChildren ?? (
|
|
204
|
-
<div className="flex items-center justify-center h-full text-neutral-600 text-sm">
|
|
205
|
-
Select a file to edit
|
|
206
|
-
</div>
|
|
207
|
-
)}
|
|
208
232
|
</div>
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
onClick={onLint}
|
|
217
|
-
disabled={linting}
|
|
218
|
-
className="w-full flex items-center justify-center gap-1.5 px-2 py-1.5 rounded-md text-[11px] font-medium text-neutral-500 hover:text-amber-300 hover:bg-neutral-800 transition-colors disabled:opacity-40"
|
|
219
|
-
>
|
|
220
|
-
<svg
|
|
221
|
-
width="12"
|
|
222
|
-
height="12"
|
|
223
|
-
viewBox="0 0 24 24"
|
|
224
|
-
fill="none"
|
|
225
|
-
stroke="currentColor"
|
|
226
|
-
strokeWidth="2"
|
|
227
|
-
>
|
|
228
|
-
<path d="M9 11l3 3L22 4" />
|
|
229
|
-
<path d="M21 12v7a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h11" />
|
|
230
|
-
</svg>
|
|
231
|
-
{linting ? "Linting…" : "Lint"}
|
|
232
|
-
</button>
|
|
233
|
-
</div>
|
|
234
|
-
)}
|
|
235
|
-
</>
|
|
236
|
-
)}
|
|
237
|
-
</div>
|
|
238
|
-
);
|
|
239
|
-
});
|
|
233
|
+
)}
|
|
234
|
+
</>
|
|
235
|
+
)}
|
|
236
|
+
</div>
|
|
237
|
+
);
|
|
238
|
+
}),
|
|
239
|
+
);
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { createContext, useContext, useMemo, type ReactNode } from "react";
|
|
2
|
+
import type { useDomEditSession } from "../hooks/useDomEditSession";
|
|
3
|
+
|
|
4
|
+
type DomEditValue = ReturnType<typeof useDomEditSession>;
|
|
5
|
+
|
|
6
|
+
const DomEditContext = createContext<DomEditValue | null>(null);
|
|
7
|
+
|
|
8
|
+
export function useDomEditContext(): DomEditValue {
|
|
9
|
+
const ctx = useContext(DomEditContext);
|
|
10
|
+
if (!ctx) throw new Error("useDomEditContext must be used within DomEditProvider");
|
|
11
|
+
return ctx;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function DomEditProvider({
|
|
15
|
+
value: {
|
|
16
|
+
domEditSelection,
|
|
17
|
+
domEditGroupSelections,
|
|
18
|
+
domEditHoverSelection,
|
|
19
|
+
agentModalOpen,
|
|
20
|
+
agentModalAnchorPoint,
|
|
21
|
+
copiedAgentPrompt,
|
|
22
|
+
agentPromptSelectionContext,
|
|
23
|
+
domEditSelectionRef,
|
|
24
|
+
handleTimelineElementSelect,
|
|
25
|
+
handlePreviewCanvasMouseDown,
|
|
26
|
+
handlePreviewCanvasPointerMove,
|
|
27
|
+
handlePreviewCanvasPointerLeave,
|
|
28
|
+
applyDomSelection,
|
|
29
|
+
clearDomSelection,
|
|
30
|
+
handleDomStyleCommit,
|
|
31
|
+
handleDomPathOffsetCommit,
|
|
32
|
+
handleDomGroupPathOffsetCommit,
|
|
33
|
+
handleDomBoxSizeCommit,
|
|
34
|
+
handleDomRotationCommit,
|
|
35
|
+
handleDomManualEditsReset,
|
|
36
|
+
handleDomMotionCommit,
|
|
37
|
+
handleDomMotionClear,
|
|
38
|
+
handleDomTextCommit,
|
|
39
|
+
handleDomTextFieldStyleCommit,
|
|
40
|
+
handleDomAddTextField,
|
|
41
|
+
handleDomRemoveTextField,
|
|
42
|
+
handleAskAgent,
|
|
43
|
+
handleAgentModalSubmit,
|
|
44
|
+
handleBlockedDomMove,
|
|
45
|
+
handleDomManualDragStart,
|
|
46
|
+
handleDomEditElementDelete,
|
|
47
|
+
buildDomSelectionForTimelineElement,
|
|
48
|
+
resolveImportedFontAsset,
|
|
49
|
+
setAgentModalOpen,
|
|
50
|
+
setAgentPromptSelectionContext,
|
|
51
|
+
setAgentModalAnchorPoint,
|
|
52
|
+
},
|
|
53
|
+
children,
|
|
54
|
+
}: {
|
|
55
|
+
value: DomEditValue;
|
|
56
|
+
children: ReactNode;
|
|
57
|
+
}) {
|
|
58
|
+
const stable = useMemo<DomEditValue>(
|
|
59
|
+
() => ({
|
|
60
|
+
domEditSelection,
|
|
61
|
+
domEditGroupSelections,
|
|
62
|
+
domEditHoverSelection,
|
|
63
|
+
agentModalOpen,
|
|
64
|
+
agentModalAnchorPoint,
|
|
65
|
+
copiedAgentPrompt,
|
|
66
|
+
agentPromptSelectionContext,
|
|
67
|
+
domEditSelectionRef,
|
|
68
|
+
handleTimelineElementSelect,
|
|
69
|
+
handlePreviewCanvasMouseDown,
|
|
70
|
+
handlePreviewCanvasPointerMove,
|
|
71
|
+
handlePreviewCanvasPointerLeave,
|
|
72
|
+
applyDomSelection,
|
|
73
|
+
clearDomSelection,
|
|
74
|
+
handleDomStyleCommit,
|
|
75
|
+
handleDomPathOffsetCommit,
|
|
76
|
+
handleDomGroupPathOffsetCommit,
|
|
77
|
+
handleDomBoxSizeCommit,
|
|
78
|
+
handleDomRotationCommit,
|
|
79
|
+
handleDomManualEditsReset,
|
|
80
|
+
handleDomMotionCommit,
|
|
81
|
+
handleDomMotionClear,
|
|
82
|
+
handleDomTextCommit,
|
|
83
|
+
handleDomTextFieldStyleCommit,
|
|
84
|
+
handleDomAddTextField,
|
|
85
|
+
handleDomRemoveTextField,
|
|
86
|
+
handleAskAgent,
|
|
87
|
+
handleAgentModalSubmit,
|
|
88
|
+
handleBlockedDomMove,
|
|
89
|
+
handleDomManualDragStart,
|
|
90
|
+
handleDomEditElementDelete,
|
|
91
|
+
buildDomSelectionForTimelineElement,
|
|
92
|
+
resolveImportedFontAsset,
|
|
93
|
+
setAgentModalOpen,
|
|
94
|
+
setAgentPromptSelectionContext,
|
|
95
|
+
setAgentModalAnchorPoint,
|
|
96
|
+
}),
|
|
97
|
+
[
|
|
98
|
+
domEditSelection,
|
|
99
|
+
domEditGroupSelections,
|
|
100
|
+
domEditHoverSelection,
|
|
101
|
+
agentModalOpen,
|
|
102
|
+
agentModalAnchorPoint,
|
|
103
|
+
copiedAgentPrompt,
|
|
104
|
+
agentPromptSelectionContext,
|
|
105
|
+
domEditSelectionRef,
|
|
106
|
+
handleTimelineElementSelect,
|
|
107
|
+
handlePreviewCanvasMouseDown,
|
|
108
|
+
handlePreviewCanvasPointerMove,
|
|
109
|
+
handlePreviewCanvasPointerLeave,
|
|
110
|
+
applyDomSelection,
|
|
111
|
+
clearDomSelection,
|
|
112
|
+
handleDomStyleCommit,
|
|
113
|
+
handleDomPathOffsetCommit,
|
|
114
|
+
handleDomGroupPathOffsetCommit,
|
|
115
|
+
handleDomBoxSizeCommit,
|
|
116
|
+
handleDomRotationCommit,
|
|
117
|
+
handleDomManualEditsReset,
|
|
118
|
+
handleDomMotionCommit,
|
|
119
|
+
handleDomMotionClear,
|
|
120
|
+
handleDomTextCommit,
|
|
121
|
+
handleDomTextFieldStyleCommit,
|
|
122
|
+
handleDomAddTextField,
|
|
123
|
+
handleDomRemoveTextField,
|
|
124
|
+
handleAskAgent,
|
|
125
|
+
handleAgentModalSubmit,
|
|
126
|
+
handleBlockedDomMove,
|
|
127
|
+
handleDomManualDragStart,
|
|
128
|
+
handleDomEditElementDelete,
|
|
129
|
+
buildDomSelectionForTimelineElement,
|
|
130
|
+
resolveImportedFontAsset,
|
|
131
|
+
setAgentModalOpen,
|
|
132
|
+
setAgentPromptSelectionContext,
|
|
133
|
+
setAgentModalAnchorPoint,
|
|
134
|
+
],
|
|
135
|
+
);
|
|
136
|
+
return <DomEditContext value={stable}>{children}</DomEditContext>;
|
|
137
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { createContext, useContext, useMemo, type ReactNode } from "react";
|
|
2
|
+
import type { useFileManager } from "../hooks/useFileManager";
|
|
3
|
+
|
|
4
|
+
type FileManagerValue = ReturnType<typeof useFileManager>;
|
|
5
|
+
|
|
6
|
+
const FileManagerContext = createContext<FileManagerValue | null>(null);
|
|
7
|
+
|
|
8
|
+
export function useFileManagerContext(): FileManagerValue {
|
|
9
|
+
const ctx = useContext(FileManagerContext);
|
|
10
|
+
if (!ctx) throw new Error("useFileManagerContext must be used within FileManagerProvider");
|
|
11
|
+
return ctx;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function FileManagerProvider({
|
|
15
|
+
value: {
|
|
16
|
+
editingFile,
|
|
17
|
+
setEditingFile,
|
|
18
|
+
projectDir,
|
|
19
|
+
fileTree,
|
|
20
|
+
setFileTree,
|
|
21
|
+
editingPathRef,
|
|
22
|
+
projectIdRef,
|
|
23
|
+
saveTimerRef,
|
|
24
|
+
importedFontAssetsRef,
|
|
25
|
+
readProjectFile,
|
|
26
|
+
writeProjectFile,
|
|
27
|
+
readOptionalProjectFile,
|
|
28
|
+
handleFileSelect,
|
|
29
|
+
handleContentChange,
|
|
30
|
+
refreshFileTree,
|
|
31
|
+
uploadProjectFiles,
|
|
32
|
+
handleCreateFile,
|
|
33
|
+
handleCreateFolder,
|
|
34
|
+
handleDeleteFile,
|
|
35
|
+
handleRenameFile,
|
|
36
|
+
handleDuplicateFile,
|
|
37
|
+
handleMoveFile,
|
|
38
|
+
handleImportFiles,
|
|
39
|
+
handleImportFonts,
|
|
40
|
+
compositions,
|
|
41
|
+
assets,
|
|
42
|
+
fontAssets,
|
|
43
|
+
},
|
|
44
|
+
children,
|
|
45
|
+
}: {
|
|
46
|
+
value: FileManagerValue;
|
|
47
|
+
children: ReactNode;
|
|
48
|
+
}) {
|
|
49
|
+
const stable = useMemo<FileManagerValue>(
|
|
50
|
+
() => ({
|
|
51
|
+
editingFile,
|
|
52
|
+
setEditingFile,
|
|
53
|
+
projectDir,
|
|
54
|
+
fileTree,
|
|
55
|
+
setFileTree,
|
|
56
|
+
editingPathRef,
|
|
57
|
+
projectIdRef,
|
|
58
|
+
saveTimerRef,
|
|
59
|
+
importedFontAssetsRef,
|
|
60
|
+
readProjectFile,
|
|
61
|
+
writeProjectFile,
|
|
62
|
+
readOptionalProjectFile,
|
|
63
|
+
handleFileSelect,
|
|
64
|
+
handleContentChange,
|
|
65
|
+
refreshFileTree,
|
|
66
|
+
uploadProjectFiles,
|
|
67
|
+
handleCreateFile,
|
|
68
|
+
handleCreateFolder,
|
|
69
|
+
handleDeleteFile,
|
|
70
|
+
handleRenameFile,
|
|
71
|
+
handleDuplicateFile,
|
|
72
|
+
handleMoveFile,
|
|
73
|
+
handleImportFiles,
|
|
74
|
+
handleImportFonts,
|
|
75
|
+
compositions,
|
|
76
|
+
assets,
|
|
77
|
+
fontAssets,
|
|
78
|
+
}),
|
|
79
|
+
[
|
|
80
|
+
editingFile,
|
|
81
|
+
setEditingFile,
|
|
82
|
+
projectDir,
|
|
83
|
+
fileTree,
|
|
84
|
+
setFileTree,
|
|
85
|
+
editingPathRef,
|
|
86
|
+
projectIdRef,
|
|
87
|
+
saveTimerRef,
|
|
88
|
+
importedFontAssetsRef,
|
|
89
|
+
readProjectFile,
|
|
90
|
+
writeProjectFile,
|
|
91
|
+
readOptionalProjectFile,
|
|
92
|
+
handleFileSelect,
|
|
93
|
+
handleContentChange,
|
|
94
|
+
refreshFileTree,
|
|
95
|
+
uploadProjectFiles,
|
|
96
|
+
handleCreateFile,
|
|
97
|
+
handleCreateFolder,
|
|
98
|
+
handleDeleteFile,
|
|
99
|
+
handleRenameFile,
|
|
100
|
+
handleDuplicateFile,
|
|
101
|
+
handleMoveFile,
|
|
102
|
+
handleImportFiles,
|
|
103
|
+
handleImportFonts,
|
|
104
|
+
compositions,
|
|
105
|
+
assets,
|
|
106
|
+
fontAssets,
|
|
107
|
+
],
|
|
108
|
+
);
|
|
109
|
+
return <FileManagerContext value={stable}>{children}</FileManagerContext>;
|
|
110
|
+
}
|