@hyperframes/studio 0.6.6 → 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.
- package/dist/assets/{hyperframes-player-T-ME1rqL.js → hyperframes-player-D0Yi3xMP.js} +2 -2
- package/dist/assets/{index-Bne9FFeo.css → index-Ckqo37Co.css} +1 -1
- package/dist/assets/index-Yvtxngdi.js +116 -0
- package/dist/index.html +2 -2
- package/package.json +4 -4
- package/src/App.tsx +54 -31
- package/src/components/StudioGlobalDragOverlay.tsx +26 -0
- package/src/components/StudioRightPanel.tsx +0 -2
- package/src/components/editor/DomEditOverlay.test.ts +1 -0
- package/src/components/editor/DomEditOverlay.tsx +2 -1
- package/src/components/editor/PropertyPanel.tsx +27 -36
- package/src/components/editor/domEditingElement.ts +1 -0
- package/src/components/editor/manualEdits.test.ts +39 -466
- package/src/components/editor/manualEdits.ts +6 -168
- package/src/components/editor/manualEditsDom.ts +361 -1
- package/src/components/editor/manualEditsParsing.ts +2 -240
- package/src/components/editor/manualEditsTypes.ts +1 -40
- package/src/components/editor/useDomEditOverlayGestures.ts +25 -8
- package/src/components/nle/NLEPreview.tsx +1 -1
- package/src/components/sidebar/CompositionsTab.tsx +9 -3
- package/src/contexts/DomEditContext.tsx +3 -0
- package/src/contexts/FileManagerContext.tsx +3 -0
- package/src/hooks/useAppHotkeys.ts +1 -4
- package/src/hooks/useDomEditCommits.ts +82 -77
- package/src/hooks/useDomEditSession.ts +4 -16
- package/src/hooks/useFileManager.ts +10 -1
- package/src/hooks/useManifestPersistence.ts +51 -187
- package/src/hooks/usePanelLayout.ts +10 -3
- package/src/hooks/usePreviewInteraction.ts +0 -1
- package/src/hooks/useStudioUrlState.ts +188 -0
- package/src/player/components/Player.tsx +15 -1
- package/src/player/components/PlayerControls.test.ts +17 -0
- package/src/player/components/PlayerControls.tsx +61 -0
- package/src/player/hooks/usePlaybackKeyboard.test.ts +174 -0
- package/src/player/hooks/usePlaybackKeyboard.ts +18 -15
- package/src/player/hooks/useTimelinePlayer.seek.test.ts +329 -0
- package/src/player/hooks/useTimelinePlayer.ts +76 -18
- package/src/player/hooks/useTimelineSyncCallbacks.ts +10 -4
- package/src/player/lib/playbackAdapter.test.ts +50 -0
- package/src/player/lib/playbackAdapter.ts +2 -2
- package/src/player/lib/playbackTypes.ts +1 -1
- package/src/player/lib/timelineDOM.ts +4 -2
- package/src/player/lib/timelineIframeHelpers.ts +63 -7
- package/src/player/store/playerStore.test.ts +105 -1
- package/src/player/store/playerStore.ts +12 -1
- package/src/utils/projectRouting.test.ts +15 -0
- package/src/utils/projectRouting.ts +46 -9
- package/src/utils/sourcePatcher.ts +50 -14
- package/src/utils/studioPreviewHelpers.test.ts +56 -0
- package/src/utils/studioPreviewHelpers.ts +51 -13
- package/src/utils/studioUiPreferences.test.ts +3 -0
- package/src/utils/studioUiPreferences.ts +4 -0
- package/src/utils/studioUrlState.test.ts +249 -0
- package/src/utils/studioUrlState.ts +135 -0
- package/dist/assets/index-DYqqzECY.js +0 -117
package/dist/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
7
7
|
<title>HyperFrames Studio</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-Yvtxngdi.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Ckqo37Co.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/studio",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"@phosphor-icons/react": "^2.1.10",
|
|
33
33
|
"codemirror": "^6.0.1",
|
|
34
34
|
"motion": "^12.38.0",
|
|
35
|
-
"@hyperframes/core": "0.6.
|
|
36
|
-
"@hyperframes/player": "0.6.
|
|
35
|
+
"@hyperframes/core": "0.6.7",
|
|
36
|
+
"@hyperframes/player": "0.6.7"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/react": "19",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"vite": "^6.4.2",
|
|
48
48
|
"vitest": "^3.2.4",
|
|
49
49
|
"zustand": "^5.0.0",
|
|
50
|
-
"@hyperframes/producer": "0.6.
|
|
50
|
+
"@hyperframes/producer": "0.6.7"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"react": "19",
|
package/src/App.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useCallback, useRef, useMemo } from "react";
|
|
1
|
+
import { useState, useCallback, useRef, useMemo, useEffect } from "react";
|
|
2
2
|
import type { LeftSidebarHandle } from "./components/sidebar/LeftSidebar";
|
|
3
3
|
import { useRenderQueue } from "./components/renders/useRenderQueue";
|
|
4
4
|
import { usePlayerStore } from "./player";
|
|
@@ -20,6 +20,7 @@ import { useFrameCapture } from "./hooks/useFrameCapture";
|
|
|
20
20
|
import { useLintModal } from "./hooks/useLintModal";
|
|
21
21
|
import { useCompositionDimensions } from "./hooks/useCompositionDimensions";
|
|
22
22
|
import { useToast } from "./hooks/useToast";
|
|
23
|
+
import { useStudioUrlState } from "./hooks/useStudioUrlState";
|
|
23
24
|
import {
|
|
24
25
|
STUDIO_INSPECTOR_PANELS_ENABLED,
|
|
25
26
|
STUDIO_MOTION_PANEL_ENABLED,
|
|
@@ -27,6 +28,7 @@ import {
|
|
|
27
28
|
import { getStudioMotionForSelection } from "./components/editor/studioMotion";
|
|
28
29
|
import type { DomEditSelection } from "./components/editor/domEditing";
|
|
29
30
|
import { AskAgentModal } from "./components/AskAgentModal";
|
|
31
|
+
import { StudioGlobalDragOverlay } from "./components/StudioGlobalDragOverlay";
|
|
30
32
|
import { StudioHeader } from "./components/StudioHeader";
|
|
31
33
|
import { StudioLeftSidebar } from "./components/StudioLeftSidebar";
|
|
32
34
|
import { StudioPreviewArea } from "./components/StudioPreviewArea";
|
|
@@ -38,11 +40,19 @@ import { FileManagerProvider } from "./contexts/FileManagerContext";
|
|
|
38
40
|
import { DomEditProvider } from "./contexts/DomEditContext";
|
|
39
41
|
import { StudioSplash } from "./components/StudioSplash";
|
|
40
42
|
import { useServerConnection } from "./hooks/useServerConnection";
|
|
43
|
+
import {
|
|
44
|
+
normalizeStudioCompositionPath,
|
|
45
|
+
readStudioUrlStateFromWindow,
|
|
46
|
+
} from "./utils/studioUrlState";
|
|
41
47
|
|
|
42
48
|
export function StudioApp() {
|
|
43
49
|
const { projectId, resolving, waitingForServer } = useServerConnection();
|
|
50
|
+
const initialUrlStateRef = useRef(readStudioUrlStateFromWindow());
|
|
44
51
|
|
|
45
52
|
const [activeCompPath, setActiveCompPath] = useState<string | null>(null);
|
|
53
|
+
const [activeCompPathHydrated, setActiveCompPathHydrated] = useState(
|
|
54
|
+
() => initialUrlStateRef.current.activeCompPath == null,
|
|
55
|
+
);
|
|
46
56
|
const [compIdToSrc, setCompIdToSrc] = useState<Map<string, string>>(new Map());
|
|
47
57
|
const [previewIframe, setPreviewIframe] = useState<HTMLIFrameElement | null>(null);
|
|
48
58
|
const [compositionLoading, setCompositionLoading] = useState(true);
|
|
@@ -80,7 +90,10 @@ export function StudioApp() {
|
|
|
80
90
|
}, []);
|
|
81
91
|
|
|
82
92
|
const [timelineVisible, setTimelineVisible] = useState(
|
|
83
|
-
() =>
|
|
93
|
+
() =>
|
|
94
|
+
initialUrlStateRef.current.timelineVisible ??
|
|
95
|
+
readStudioUiPreferences().timelineVisible ??
|
|
96
|
+
true,
|
|
84
97
|
);
|
|
85
98
|
const toggleTimelineVisibility = useCallback(() => {
|
|
86
99
|
setTimelineVisible((v) => {
|
|
@@ -89,7 +102,10 @@ export function StudioApp() {
|
|
|
89
102
|
});
|
|
90
103
|
}, []);
|
|
91
104
|
const { appToast, showToast } = useToast();
|
|
92
|
-
const panelLayout = usePanelLayout(
|
|
105
|
+
const panelLayout = usePanelLayout({
|
|
106
|
+
rightCollapsed: initialUrlStateRef.current.rightCollapsed,
|
|
107
|
+
rightPanelTab: initialUrlStateRef.current.rightPanelTab,
|
|
108
|
+
});
|
|
93
109
|
const editHistory = usePersistentEditHistory({ projectId });
|
|
94
110
|
const domEditSaveTimestampRef = useRef(0);
|
|
95
111
|
const reloadPreview = useCallback(() => {
|
|
@@ -108,6 +124,18 @@ export function StudioApp() {
|
|
|
108
124
|
setRefreshKey,
|
|
109
125
|
});
|
|
110
126
|
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (activeCompPathHydrated) return;
|
|
129
|
+
if (!fileManager.fileTreeLoaded) return;
|
|
130
|
+
|
|
131
|
+
const nextCompPath = normalizeStudioCompositionPath(
|
|
132
|
+
initialUrlStateRef.current.activeCompPath,
|
|
133
|
+
fileManager.fileTree,
|
|
134
|
+
);
|
|
135
|
+
setActiveCompPath((current) => (current === nextCompPath ? current : nextCompPath));
|
|
136
|
+
setActiveCompPathHydrated(true);
|
|
137
|
+
}, [activeCompPathHydrated, fileManager.fileTree, fileManager.fileTreeLoaded]);
|
|
138
|
+
|
|
111
139
|
const manifestPersistence = useManifestPersistence({
|
|
112
140
|
projectId,
|
|
113
141
|
showToast,
|
|
@@ -116,6 +144,8 @@ export function StudioApp() {
|
|
|
116
144
|
recordEdit: editHistory.recordEdit,
|
|
117
145
|
previewIframeRef,
|
|
118
146
|
activeCompPathRef,
|
|
147
|
+
domEditSaveTimestampRef,
|
|
148
|
+
reloadPreview: () => setRefreshKey((k) => k + 1),
|
|
119
149
|
});
|
|
120
150
|
|
|
121
151
|
const timelineEditing = useTimelineEditing({
|
|
@@ -169,12 +199,9 @@ export function StudioApp() {
|
|
|
169
199
|
setRightPanelTab: panelLayout.setRightPanelTab,
|
|
170
200
|
showToast,
|
|
171
201
|
refreshPreviewDocumentVersion,
|
|
172
|
-
|
|
173
|
-
manifestPersistence.commitStudioManualEditManifestOptimistically,
|
|
202
|
+
queueDomEditSave: manifestPersistence.queueDomEditSave,
|
|
174
203
|
commitStudioMotionManifestOptimistically:
|
|
175
204
|
manifestPersistence.commitStudioMotionManifestOptimistically,
|
|
176
|
-
applyCurrentStudioManualEditsToPreview:
|
|
177
|
-
manifestPersistence.applyCurrentStudioManualEditsToPreview,
|
|
178
205
|
applyCurrentStudioMotionToPreview: manifestPersistence.applyCurrentStudioMotionToPreview,
|
|
179
206
|
readProjectFile: fileManager.readProjectFile,
|
|
180
207
|
writeProjectFile: fileManager.writeProjectFile,
|
|
@@ -284,6 +311,25 @@ export function StudioApp() {
|
|
|
284
311
|
const inspectorButtonActive =
|
|
285
312
|
STUDIO_INSPECTOR_PANELS_ENABLED && !panelLayout.rightCollapsed && inspectorPanelActive;
|
|
286
313
|
|
|
314
|
+
useStudioUrlState({
|
|
315
|
+
projectId,
|
|
316
|
+
activeCompPath,
|
|
317
|
+
currentTime,
|
|
318
|
+
duration: effectiveTimelineDuration,
|
|
319
|
+
isPlaying,
|
|
320
|
+
compositionLoading,
|
|
321
|
+
refreshKey,
|
|
322
|
+
previewIframeRef,
|
|
323
|
+
rightPanelTab: panelLayout.rightPanelTab,
|
|
324
|
+
rightCollapsed: panelLayout.rightCollapsed,
|
|
325
|
+
timelineVisible,
|
|
326
|
+
activeCompPathHydrated,
|
|
327
|
+
domEditSelection: domEditSession.domEditSelection,
|
|
328
|
+
buildDomSelectionFromTarget: domEditSession.buildDomSelectionFromTarget,
|
|
329
|
+
applyDomSelection: domEditSession.applyDomSelection,
|
|
330
|
+
initialState: initialUrlStateRef.current,
|
|
331
|
+
});
|
|
332
|
+
|
|
287
333
|
// StudioProvider performs its own useMemo — no need for a second memo here.
|
|
288
334
|
const studioCtxValue: StudioContextValue = {
|
|
289
335
|
projectId: projectId!,
|
|
@@ -420,30 +466,7 @@ export function StudioApp() {
|
|
|
420
466
|
/>
|
|
421
467
|
)}
|
|
422
468
|
|
|
423
|
-
{globalDragOver &&
|
|
424
|
-
<div className="absolute inset-0 z-[90] flex items-center justify-center bg-black/50 backdrop-blur-sm pointer-events-none">
|
|
425
|
-
<div className="flex flex-col items-center gap-3 px-8 py-6 rounded-xl border-2 border-dashed border-studio-accent/60 bg-studio-accent/[0.06]">
|
|
426
|
-
<svg
|
|
427
|
-
width="32"
|
|
428
|
-
height="32"
|
|
429
|
-
viewBox="0 0 24 24"
|
|
430
|
-
fill="none"
|
|
431
|
-
stroke="currentColor"
|
|
432
|
-
strokeWidth="1.5"
|
|
433
|
-
strokeLinecap="round"
|
|
434
|
-
strokeLinejoin="round"
|
|
435
|
-
className="text-studio-accent"
|
|
436
|
-
>
|
|
437
|
-
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
|
|
438
|
-
<polyline points="7 10 12 15 17 10" />
|
|
439
|
-
<line x1="12" y1="15" x2="12" y2="3" />
|
|
440
|
-
</svg>
|
|
441
|
-
<span className="text-sm font-medium text-studio-accent">
|
|
442
|
-
Drop files to import into project
|
|
443
|
-
</span>
|
|
444
|
-
</div>
|
|
445
|
-
</div>
|
|
446
|
-
)}
|
|
469
|
+
{globalDragOver && <StudioGlobalDragOverlay />}
|
|
447
470
|
|
|
448
471
|
{appToast && (
|
|
449
472
|
<div
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function StudioGlobalDragOverlay() {
|
|
2
|
+
return (
|
|
3
|
+
<div className="absolute inset-0 z-[90] flex items-center justify-center bg-black/50 backdrop-blur-sm pointer-events-none">
|
|
4
|
+
<div className="flex flex-col items-center gap-3 px-8 py-6 rounded-xl border-2 border-dashed border-studio-accent/60 bg-studio-accent/[0.06]">
|
|
5
|
+
<svg
|
|
6
|
+
width="32"
|
|
7
|
+
height="32"
|
|
8
|
+
viewBox="0 0 24 24"
|
|
9
|
+
fill="none"
|
|
10
|
+
stroke="currentColor"
|
|
11
|
+
strokeWidth="1.5"
|
|
12
|
+
strokeLinecap="round"
|
|
13
|
+
strokeLinejoin="round"
|
|
14
|
+
className="text-studio-accent"
|
|
15
|
+
>
|
|
16
|
+
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
|
|
17
|
+
<polyline points="7 10 12 15 17 10" />
|
|
18
|
+
<line x1="12" y1="15" x2="12" y2="3" />
|
|
19
|
+
</svg>
|
|
20
|
+
<span className="text-sm font-medium text-studio-accent">
|
|
21
|
+
Drop files to import into project
|
|
22
|
+
</span>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -59,7 +59,6 @@ export function StudioRightPanel({
|
|
|
59
59
|
handleDomTextFieldStyleCommit,
|
|
60
60
|
handleDomAddTextField,
|
|
61
61
|
handleDomRemoveTextField,
|
|
62
|
-
handleDomManualEditsReset,
|
|
63
62
|
handleAskAgent,
|
|
64
63
|
handleDomMotionCommit,
|
|
65
64
|
handleDomMotionClear,
|
|
@@ -173,7 +172,6 @@ export function StudioRightPanel({
|
|
|
173
172
|
onSetTextFieldStyle={handleDomTextFieldStyleCommit}
|
|
174
173
|
onAddTextField={handleDomAddTextField}
|
|
175
174
|
onRemoveTextField={handleDomRemoveTextField}
|
|
176
|
-
onResetManualEdits={handleDomManualEditsReset}
|
|
177
175
|
onAskAgent={handleAskAgent}
|
|
178
176
|
onImportAssets={handleImportFiles}
|
|
179
177
|
fontAssets={fontAssets}
|
|
@@ -138,6 +138,7 @@ export const DomEditOverlay = memo(function DomEditOverlay({
|
|
|
138
138
|
|
|
139
139
|
const gestures = createDomEditOverlayGestureHandlers({
|
|
140
140
|
overlayRef,
|
|
141
|
+
iframeRef,
|
|
141
142
|
boxRef,
|
|
142
143
|
selectionRef,
|
|
143
144
|
overlayRectRef,
|
|
@@ -307,7 +308,7 @@ export const DomEditOverlay = memo(function DomEditOverlay({
|
|
|
307
308
|
cursor: allowCanvasMovement && groupCanMove ? "move" : "default",
|
|
308
309
|
}}
|
|
309
310
|
onPointerDown={(e) => {
|
|
310
|
-
if (!allowCanvasMovement || e.shiftKey) return;
|
|
311
|
+
if (!allowCanvasMovement || !groupCanMove || e.shiftKey) return;
|
|
311
312
|
gestures.startGroupDrag(e);
|
|
312
313
|
}}
|
|
313
314
|
onMouseDown={suppressBoxMouseDown}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { memo } from "react";
|
|
2
|
-
import { Eye, Layers, MessageSquare, Move,
|
|
2
|
+
import { Eye, Layers, MessageSquare, Move, X } from "../../icons/SystemIcons";
|
|
3
3
|
import {
|
|
4
4
|
collectDomEditLayerItems,
|
|
5
5
|
getDomEditLayerKey,
|
|
@@ -46,7 +46,6 @@ interface PropertyPanelProps {
|
|
|
46
46
|
onSetTextFieldStyle: (fieldKey: string, property: string, value: string) => void;
|
|
47
47
|
onAddTextField: (afterFieldKey?: string) => string | Promise<string | null> | null;
|
|
48
48
|
onRemoveTextField: (fieldKey: string) => void;
|
|
49
|
-
onResetManualEdits: (element: DomEditSelection) => void;
|
|
50
49
|
onAskAgent: () => void;
|
|
51
50
|
onImportAssets?: (files: FileList) => Promise<string[]>;
|
|
52
51
|
fontAssets?: ImportedFontAsset[];
|
|
@@ -134,7 +133,6 @@ export const PropertyPanel = memo(function PropertyPanel({
|
|
|
134
133
|
onSetTextFieldStyle,
|
|
135
134
|
onAddTextField,
|
|
136
135
|
onRemoveTextField,
|
|
137
|
-
onResetManualEdits,
|
|
138
136
|
onAskAgent,
|
|
139
137
|
onImportAssets,
|
|
140
138
|
fontAssets = [],
|
|
@@ -146,30 +144,32 @@ export const PropertyPanel = memo(function PropertyPanel({
|
|
|
146
144
|
|
|
147
145
|
if (!element) {
|
|
148
146
|
return (
|
|
149
|
-
<div className="flex h-full flex-col
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
147
|
+
<div className="flex h-full flex-col bg-neutral-900">
|
|
148
|
+
<div className="flex flex-1 flex-col items-center justify-center px-6 text-center">
|
|
149
|
+
{multiSelectCount > 1 ? (
|
|
150
|
+
<>
|
|
151
|
+
<Layers size={18} className="mb-3 text-neutral-600" />
|
|
152
|
+
<p className="text-sm font-medium text-neutral-200">
|
|
153
|
+
{multiSelectCount} elements selected
|
|
154
|
+
</p>
|
|
155
|
+
<p className="mt-2 max-w-[260px] text-xs leading-5 text-neutral-500">
|
|
156
|
+
Select a single element to edit its properties. Click an element in the preview or
|
|
157
|
+
use the timeline layer panel.
|
|
158
|
+
</p>
|
|
159
|
+
</>
|
|
160
|
+
) : (
|
|
161
|
+
<>
|
|
162
|
+
<Eye size={18} className="mb-3 text-neutral-600" />
|
|
163
|
+
<p className="text-sm font-medium text-neutral-200">
|
|
164
|
+
Select an element in the preview.
|
|
165
|
+
</p>
|
|
166
|
+
<p className="mt-2 max-w-[260px] text-xs leading-5 text-neutral-500">
|
|
167
|
+
The inspector is tuned for element edits with safer geometry controls, color
|
|
168
|
+
picking, and cleaner grouped layer controls.
|
|
169
|
+
</p>
|
|
170
|
+
</>
|
|
171
|
+
)}
|
|
172
|
+
</div>
|
|
173
173
|
</div>
|
|
174
174
|
);
|
|
175
175
|
}
|
|
@@ -253,15 +253,6 @@ export const PropertyPanel = memo(function PropertyPanel({
|
|
|
253
253
|
<MessageSquare size={15} />
|
|
254
254
|
<span>{copiedAgentPrompt ? "Prompt copied" : "Ask agent"}</span>
|
|
255
255
|
</button>
|
|
256
|
-
<button
|
|
257
|
-
type="button"
|
|
258
|
-
onClick={() => onResetManualEdits(element)}
|
|
259
|
-
title="Reset move, size, and rotation edits"
|
|
260
|
-
className="inline-flex h-8 items-center justify-center gap-2 rounded-xl border border-neutral-700 bg-neutral-950 px-3.5 text-[11px] font-medium text-neutral-100 transition-colors hover:border-neutral-500 hover:text-white"
|
|
261
|
-
>
|
|
262
|
-
<RotateCcw size={14} />
|
|
263
|
-
<span>Reset edits</span>
|
|
264
|
-
</button>
|
|
265
256
|
</div>
|
|
266
257
|
</div>
|
|
267
258
|
|
|
@@ -118,6 +118,7 @@ export function getDomLayerPatchTarget(
|
|
|
118
118
|
activeCompositionPath: string | null,
|
|
119
119
|
): Pick<DomEditSelection, "id" | "selector" | "selectorIndex" | "sourceFile"> | null {
|
|
120
120
|
if (!isInspectableLayerElement(el)) return null;
|
|
121
|
+
if (el.hasAttribute("data-composition-id")) return null;
|
|
121
122
|
|
|
122
123
|
const selector = buildStableSelector(el);
|
|
123
124
|
if (!selector) return null;
|