@hyperframes/studio 0.8.50 → 0.8.51
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-CxxDxHTW.js → hyperframes-player-Cn_PpHqP.js} +3 -3
- package/dist/assets/index-C05SNrvY.js +360 -0
- package/dist/assets/{index-DUAIWxkI.js → index-CPS3Cvj8.js} +1 -1
- package/dist/assets/{index-Dt3qBI-9.js → index-Clrb11lj.js} +1 -1
- package/dist/assets/index-DUR9qzQM.css +1 -0
- package/dist/index.d.ts +18 -13
- package/dist/index.html +2 -2
- package/dist/index.js +1867 -3335
- package/dist/index.js.map +1 -1
- package/dist/styles/tailwind-preset.js.map +1 -1
- package/package.json +11 -12
- package/src/App.tsx +128 -141
- package/src/captions/components/CaptionAnimationPanel.tsx +4 -4
- package/src/captions/components/CaptionOverlayUtils.ts +2 -3
- package/src/captions/components/CaptionPropertyPanel.tsx +3 -3
- package/src/captions/components/CaptionTimeline.tsx +1 -1
- package/src/captions/components/shared.tsx +2 -2
- package/src/components/AskAgentModal.tsx +5 -5
- package/src/components/EditorShell.tsx +4 -7
- package/src/components/ExternalFileConflictBanner.tsx +16 -16
- package/src/components/LintModal.tsx +6 -16
- package/src/components/MediaPreview.tsx +2 -2
- package/src/components/SaveQueuePausedBanner.tsx +2 -2
- package/src/components/StudioHeader.tsx +2 -62
- package/src/components/StudioLeftSidebar.tsx +7 -4
- package/src/components/StudioOverlays.tsx +1 -1
- package/src/components/StudioRightPanel.tsx +4 -4
- package/src/components/StudioToast.tsx +2 -2
- package/src/components/TimelineToolbar.tsx +7 -7
- package/src/components/dock/Dock.test.tsx +192 -0
- package/src/components/dock/Dock.tsx +264 -0
- package/src/components/dock/dock.css +23 -0
- package/src/components/dock/dockLayout.ts +58 -0
- package/src/components/dock/dockLayoutSchema.ts +66 -0
- package/src/components/dock/dockLayoutStore.ts +91 -0
- package/src/components/dock/panelRegistry.ts +67 -0
- package/src/components/editor/AnimationCard.tsx +4 -4
- package/src/components/editor/AnimationCardParts.tsx +6 -6
- package/src/components/editor/BlockParamsPanel.tsx +4 -4
- package/src/components/editor/BorderRadiusEditor.tsx +1 -1
- package/src/components/editor/CanvasContextMenu.tsx +1 -1
- package/src/components/editor/ComputedTweenNotice.tsx +1 -1
- package/src/components/editor/DomEditOverlay.tsx +1 -1
- package/src/components/editor/EaseCurveSection.tsx +1 -1
- package/src/components/editor/EaseParamFields.tsx +5 -5
- package/src/components/editor/FileTree.tsx +4 -4
- package/src/components/editor/FileTreeIcons.tsx +1 -1
- package/src/components/editor/FileTreeNodes.tsx +13 -13
- package/src/components/editor/InlineTextToolbar.tsx +1 -1
- package/src/components/editor/InspectorHeaderActions.tsx +3 -3
- package/src/components/editor/KeyframeDiamond.tsx +1 -1
- package/src/components/editor/KeyframeEaseList.tsx +1 -1
- package/src/components/editor/LayersPanel.tsx +3 -3
- package/src/components/editor/MarqueeOverlay.tsx +1 -1
- package/src/components/editor/OffCanvasIndicators.tsx +1 -1
- package/src/components/editor/PromotableControl.tsx +2 -2
- package/src/components/editor/PropertyPanelEmptyState.tsx +7 -7
- package/src/components/editor/PropertyPanelFlatHeader.tsx +2 -2
- package/src/components/editor/SnapToolbar.tsx +2 -2
- package/src/components/editor/TimelineFxPopover.tsx +1 -1
- package/src/components/editor/TopologyLens.tsx +4 -4
- package/src/components/editor/Transform3DCube.tsx +2 -2
- package/src/components/editor/audioFxRevealTarget.ts +1 -0
- package/src/components/editor/domEditing.test.ts +1 -1
- package/src/components/editor/domEditingAgentPrompt.test.ts +8 -6
- package/src/components/editor/domEditingAgentPrompt.ts +4 -3
- package/src/components/editor/propertyPanelColor.tsx +11 -11
- package/src/components/editor/propertyPanelColorCurveGraph.tsx +1 -1
- package/src/components/editor/propertyPanelColorGradingControls.tsx +7 -9
- package/src/components/editor/propertyPanelColorGradingSection.tsx +6 -6
- package/src/components/editor/propertyPanelColorGradingSlider.tsx +5 -5
- package/src/components/editor/propertyPanelColorWheels.tsx +1 -1
- package/src/components/editor/propertyPanelCommitField.tsx +1 -1
- package/src/components/editor/propertyPanelFill.tsx +4 -4
- package/src/components/editor/propertyPanelFlatColorGradingAccessory.tsx +3 -3
- package/src/components/editor/propertyPanelFlatColorGradingSection.tsx +7 -7
- package/src/components/editor/propertyPanelFlatEffectsSection.tsx +4 -4
- package/src/components/editor/propertyPanelFlatMediaSection.tsx +3 -3
- package/src/components/editor/propertyPanelFlatOverlaysSection.tsx +1 -1
- package/src/components/editor/propertyPanelFlatPrimitives.tsx +10 -10
- package/src/components/editor/propertyPanelFlatSelectRow.tsx +3 -3
- package/src/components/editor/propertyPanelFlatTextSection.test.tsx +8 -9
- package/src/components/editor/propertyPanelFlatTextSection.tsx +5 -5
- package/src/components/editor/propertyPanelFlatToggle.tsx +1 -1
- package/src/components/editor/propertyPanelFont.tsx +5 -5
- package/src/components/editor/propertyPanelFxCarveModule.tsx +1 -1
- package/src/components/editor/propertyPanelFxControls.test.tsx +1 -1
- package/src/components/editor/propertyPanelFxControls.tsx +6 -6
- package/src/components/editor/propertyPanelFxEqModule.tsx +2 -2
- package/src/components/editor/propertyPanelFxPresetStyle.ts +2 -2
- package/src/components/editor/propertyPanelGradingNumberField.tsx +1 -1
- package/src/components/editor/propertyPanelMediaSection.tsx +1 -1
- package/src/components/editor/propertyPanelPrimitives.tsx +7 -7
- package/src/components/editor/propertyPanelSections.tsx +8 -8
- package/src/components/feedback/StudioFeedbackCard.tsx +7 -7
- package/src/components/feedback/feedbackTrigger.ts +0 -1
- package/src/components/nle/AssetPreviewOverlay.tsx +6 -2
- package/src/components/nle/CompositionBreadcrumb.tsx +3 -3
- package/src/components/nle/NLEPreview.tsx +3 -3
- package/src/components/nle/PreviewOverlays.tsx +6 -6
- package/src/components/nle/PreviewPane.tsx +2 -2
- package/src/components/nle/TimelinePane.tsx +3 -3
- package/src/components/nle/TimelineResizeDivider.tsx +2 -2
- package/src/components/panels/SlideshowPanel.tsx +2 -2
- package/src/components/panels/SlideshowSubPanels.tsx +20 -17
- package/src/components/panels/VariablesBindElement.tsx +2 -2
- package/src/components/panels/VariablesDeclarationForm.tsx +2 -2
- package/src/components/panels/VariablesOtherCompositions.tsx +1 -1
- package/src/components/panels/VariablesPanel.tsx +3 -3
- package/src/components/panels/VariablesValueControls.tsx +2 -2
- package/src/components/renders/FfmpegRequiredNotice.tsx +5 -5
- package/src/components/renders/RenderQueue.test.tsx +7 -0
- package/src/components/renders/RenderQueue.tsx +6 -6
- package/src/components/renders/RenderQueueItem.tsx +6 -6
- package/src/components/sidebar/AssetCard.tsx +8 -8
- package/src/components/sidebar/AssetContextMenu.tsx +8 -8
- package/src/components/sidebar/AssetsTab.tsx +2 -2
- package/src/components/sidebar/AudioRow.tsx +6 -6
- package/src/components/sidebar/BlocksTab.tsx +6 -6
- package/src/components/sidebar/CompositionsTab.tsx +5 -5
- package/src/components/sidebar/GlobalAssetsView.tsx +1 -1
- package/src/components/sidebar/LeftSidebar.lintBadge.test.tsx +58 -0
- package/src/components/sidebar/LeftSidebar.tsx +19 -5
- package/src/components/sidebar/PromptPreviewModal.tsx +3 -3
- package/src/components/ui/Button.test.tsx +60 -0
- package/src/components/ui/Button.tsx +4 -4
- package/src/components/ui/SearchInput.tsx +2 -2
- package/src/components/ui/Tooltip.tsx +1 -1
- package/src/hooks/useAppHotkeys.ts +1 -1
- package/src/hooks/useLintModal.test.tsx +65 -0
- package/src/hooks/useLintModal.ts +3 -0
- package/src/hooks/useSdkSession.lifecycle.test.tsx +49 -4
- package/src/hooks/useSdkSession.ts +43 -10
- package/src/hooks/useTimelineAssetDropOps.test.tsx +41 -1
- package/src/hooks/useTimelineAssetDropOps.ts +52 -11
- package/src/main.tsx +9 -3
- package/src/player/components/AudioWaveform.tsx +1 -1
- package/src/player/components/AutomationSelectionMenu.tsx +2 -2
- package/src/player/components/ClipContextMenu.tsx +3 -3
- package/src/player/components/CompositionThumbnail.test.ts +0 -3
- package/src/player/components/CompositionThumbnail.tsx +3 -4
- package/src/player/components/EditModal.tsx +1 -1
- package/src/player/components/ImageThumbnail.tsx +1 -1
- package/src/player/components/KeyframeDiamondContextMenu.tsx +4 -4
- package/src/player/components/LayerDisclosureRow.tsx +2 -2
- package/src/player/components/PlayerControls.tsx +1 -1
- package/src/player/components/ShortcutsPanel.tsx +9 -9
- package/src/player/components/SpeedMenu.tsx +3 -5
- package/src/player/components/Timeline.tsx +2 -1
- package/src/player/components/TimelineCanvas.tsx +17 -10
- package/src/player/components/TimelineDiamondConnectors.tsx +1 -1
- package/src/player/components/TimelineEmptyState.tsx +5 -5
- package/src/player/components/TimelineFxButton.tsx +6 -6
- package/src/player/components/TimelineGroupHeader.tsx +5 -5
- package/src/player/components/TimelineGroupLaneLabels.tsx +1 -1
- package/src/player/components/TimelineGroupRow.tsx +1 -1
- package/src/player/components/TimelineLanes.test.tsx +32 -0
- package/src/player/components/TimelineLanes.tsx +2 -5
- package/src/player/components/TimelineLayerGroupHeader.tsx +2 -2
- package/src/player/components/TimelineRuler.tsx +1 -1
- package/src/player/components/TimelineShortcutHint.tsx +1 -1
- package/src/player/components/TimelineTrackHeader.tsx +1 -1
- package/src/player/components/TimelineTrackPlainHeader.tsx +3 -3
- package/src/player/components/TimelineTypes.ts +2 -1
- package/src/player/components/TrackGapContextMenu.tsx +1 -1
- package/src/player/components/VideoThumbnail.tsx +4 -2
- package/src/player/components/VolumeControl.tsx +2 -2
- package/src/player/components/timelineCallbacks.ts +8 -8
- package/src/player/components/timelineClipDragPreview.ts +51 -27
- package/src/player/components/timelineClipDragTypes.ts +3 -0
- package/src/player/components/timelineDragDrop.test.tsx +61 -1
- package/src/player/components/timelineDragDrop.ts +57 -15
- package/src/player/components/timelineLaneProps.ts +3 -0
- package/src/player/components/timelineSnapping.test.ts +27 -0
- package/src/player/components/timelineSnapping.ts +14 -1
- package/src/player/components/trackHeaderLabelRows.tsx +4 -4
- package/src/player/components/useTimelineClipDrag.resize.test.tsx +136 -2
- package/src/player/components/useTimelineClipDrag.ts +30 -3
- package/src/player/lib/describeClips.ts +3 -54
- package/src/styles/iconTokens.ts +20 -0
- package/src/styles/palette-reset.css +6 -0
- package/src/styles/studio.css +24 -21
- package/src/styles/tailwind-preset.shared.js +3 -0
- package/src/styles/theme.css +213 -0
- package/src/styles/theme.test.ts +196 -0
- package/src/utils/studioUiPreferences.ts +7 -0
- package/src/utils/timelineDropTrackInsert.test.ts +134 -0
- package/src/utils/timelineDropTrackInsert.ts +83 -0
- package/dist/assets/index-n2ml4tUn.js +0 -442
- package/dist/assets/index-pO6p0H27.css +0 -1
- package/src/components/storyboard/AgentChatMessageButton.test.tsx +0 -47
- package/src/components/storyboard/AgentChatMessageButton.tsx +0 -45
- package/src/components/storyboard/FramePoster.test.tsx +0 -61
- package/src/components/storyboard/FramePoster.tsx +0 -86
- package/src/components/storyboard/StoryboardDirection.tsx +0 -45
- package/src/components/storyboard/StoryboardFrameFocus.tsx +0 -412
- package/src/components/storyboard/StoryboardFrameTile.tsx +0 -117
- package/src/components/storyboard/StoryboardGrid.tsx +0 -55
- package/src/components/storyboard/StoryboardLoaded.tsx +0 -307
- package/src/components/storyboard/StoryboardReviewGuide.tsx +0 -300
- package/src/components/storyboard/StoryboardScriptPanel.tsx +0 -26
- package/src/components/storyboard/StoryboardSourceEditor.tsx +0 -231
- package/src/components/storyboard/StoryboardStatusLegend.tsx +0 -21
- package/src/components/storyboard/StoryboardView.tsx +0 -169
- package/src/components/storyboard/StoryboardViewModeGuard.test.tsx +0 -187
- package/src/components/storyboard/frameComments.test.ts +0 -95
- package/src/components/storyboard/frameComments.ts +0 -115
- package/src/components/storyboard/frameStatus.ts +0 -36
- package/src/components/storyboard/storyboardReviewStage.test.ts +0 -40
- package/src/components/storyboard/storyboardReviewStage.ts +0 -45
- package/src/components/storyboard/useFrameComments.ts +0 -98
- package/src/contexts/ViewModeContext.tsx +0 -142
- package/src/hooks/useProjectSignaturePoll.test.tsx +0 -135
- package/src/hooks/useProjectSignaturePoll.ts +0 -68
- package/src/hooks/useStoryboard.ts +0 -99
|
@@ -32,7 +32,7 @@ function ConflictReview({
|
|
|
32
32
|
|
|
33
33
|
return (
|
|
34
34
|
<div
|
|
35
|
-
className="fixed inset-0 z-
|
|
35
|
+
className="fixed inset-0 z-110 flex items-center justify-center bg-black/70 px-5 backdrop-blur-xs"
|
|
36
36
|
onClick={onClose}
|
|
37
37
|
>
|
|
38
38
|
<div
|
|
@@ -41,7 +41,7 @@ function ConflictReview({
|
|
|
41
41
|
aria-modal="true"
|
|
42
42
|
aria-labelledby="external-conflict-title"
|
|
43
43
|
tabIndex={-1}
|
|
44
|
-
className="max-h-[88vh] w-full max-w-5xl overflow-auto rounded-xl border border-amber-400/30 bg-neutral-950 p-5 text-neutral-100 shadow-2xl outline-
|
|
44
|
+
className="max-h-[88vh] w-full max-w-5xl overflow-auto rounded-xl border border-amber-400/30 bg-neutral-950 p-5 text-neutral-100 shadow-2xl outline-hidden"
|
|
45
45
|
onClick={(event) => event.stopPropagation()}
|
|
46
46
|
>
|
|
47
47
|
<div className="flex items-start justify-between gap-4">
|
|
@@ -53,7 +53,7 @@ function ConflictReview({
|
|
|
53
53
|
Reviewing or exporting does not change either version.
|
|
54
54
|
</p>
|
|
55
55
|
</div>
|
|
56
|
-
<button type="button" onClick={onClose} className="rounded px-2 py-1 text-neutral-400">
|
|
56
|
+
<button type="button" onClick={onClose} className="rounded-sm px-2 py-1 text-neutral-400">
|
|
57
57
|
Close
|
|
58
58
|
</button>
|
|
59
59
|
</div>
|
|
@@ -68,14 +68,14 @@ function ConflictReview({
|
|
|
68
68
|
<div className="flex gap-2">
|
|
69
69
|
<button
|
|
70
70
|
type="button"
|
|
71
|
-
className="rounded border border-neutral-700 px-2 py-1 text-[11px]"
|
|
71
|
+
className="rounded-sm border border-neutral-700 px-2 py-1 text-[11px]"
|
|
72
72
|
onClick={() => void navigator.clipboard.writeText(side.content)}
|
|
73
73
|
>
|
|
74
74
|
Copy
|
|
75
75
|
</button>
|
|
76
76
|
<button
|
|
77
77
|
type="button"
|
|
78
|
-
className="rounded border border-neutral-700 px-2 py-1 text-[11px]"
|
|
78
|
+
className="rounded-sm border border-neutral-700 px-2 py-1 text-[11px]"
|
|
79
79
|
onClick={() =>
|
|
80
80
|
downloadText(`${conflict.error.filePath}.${side.suffix}.html`, side.content)
|
|
81
81
|
}
|
|
@@ -87,7 +87,7 @@ function ConflictReview({
|
|
|
87
87
|
<textarea
|
|
88
88
|
readOnly
|
|
89
89
|
value={side.content}
|
|
90
|
-
className="h-80 w-full resize-y rounded border border-neutral-800 bg-neutral-900 p-3 font-mono text-[11px] leading-relaxed text-neutral-300"
|
|
90
|
+
className="h-80 w-full resize-y rounded-sm border border-neutral-800 bg-neutral-900 p-3 font-mono text-[11px] leading-relaxed text-neutral-300"
|
|
91
91
|
/>
|
|
92
92
|
</section>
|
|
93
93
|
))}
|
|
@@ -110,7 +110,7 @@ function FailedDraftReview({
|
|
|
110
110
|
useDialogBehavior({ open: true, onClose, containerRef });
|
|
111
111
|
return (
|
|
112
112
|
<div
|
|
113
|
-
className="fixed inset-0 z-
|
|
113
|
+
className="fixed inset-0 z-110 flex items-center justify-center bg-black/70 px-5 backdrop-blur-xs"
|
|
114
114
|
onClick={onClose}
|
|
115
115
|
>
|
|
116
116
|
<div
|
|
@@ -119,7 +119,7 @@ function FailedDraftReview({
|
|
|
119
119
|
aria-modal="true"
|
|
120
120
|
aria-labelledby="failed-draft-title"
|
|
121
121
|
tabIndex={-1}
|
|
122
|
-
className="max-h-[88vh] w-full max-w-3xl overflow-auto rounded-xl border border-amber-400/30 bg-neutral-950 p-5 text-neutral-100 shadow-2xl outline-
|
|
122
|
+
className="max-h-[88vh] w-full max-w-3xl overflow-auto rounded-xl border border-amber-400/30 bg-neutral-950 p-5 text-neutral-100 shadow-2xl outline-hidden"
|
|
123
123
|
onClick={(event) => event.stopPropagation()}
|
|
124
124
|
>
|
|
125
125
|
<div className="flex items-start justify-between gap-4">
|
|
@@ -131,21 +131,21 @@ function FailedDraftReview({
|
|
|
131
131
|
Copy or download this draft before choosing to discard it.
|
|
132
132
|
</p>
|
|
133
133
|
</div>
|
|
134
|
-
<button type="button" onClick={onClose} className="rounded px-2 py-1 text-neutral-400">
|
|
134
|
+
<button type="button" onClick={onClose} className="rounded-sm px-2 py-1 text-neutral-400">
|
|
135
135
|
Close
|
|
136
136
|
</button>
|
|
137
137
|
</div>
|
|
138
138
|
<div className="mt-4 flex justify-end gap-2">
|
|
139
139
|
<button
|
|
140
140
|
type="button"
|
|
141
|
-
className="rounded border border-neutral-700 px-2 py-1 text-[11px]"
|
|
141
|
+
className="rounded-sm border border-neutral-700 px-2 py-1 text-[11px]"
|
|
142
142
|
onClick={() => void navigator.clipboard.writeText(content)}
|
|
143
143
|
>
|
|
144
144
|
Copy
|
|
145
145
|
</button>
|
|
146
146
|
<button
|
|
147
147
|
type="button"
|
|
148
|
-
className="rounded border border-neutral-700 px-2 py-1 text-[11px]"
|
|
148
|
+
className="rounded-sm border border-neutral-700 px-2 py-1 text-[11px]"
|
|
149
149
|
onClick={() => downloadText(`${path}.studio.html`, content)}
|
|
150
150
|
>
|
|
151
151
|
Download
|
|
@@ -154,7 +154,7 @@ function FailedDraftReview({
|
|
|
154
154
|
<textarea
|
|
155
155
|
readOnly
|
|
156
156
|
value={content}
|
|
157
|
-
className="mt-2 h-80 w-full resize-y rounded border border-neutral-800 bg-neutral-900 p-3 font-mono text-[11px] leading-relaxed text-neutral-300"
|
|
157
|
+
className="mt-2 h-80 w-full resize-y rounded-sm border border-neutral-800 bg-neutral-900 p-3 font-mono text-[11px] leading-relaxed text-neutral-300"
|
|
158
158
|
/>
|
|
159
159
|
</div>
|
|
160
160
|
</div>
|
|
@@ -176,7 +176,7 @@ export function ExternalFileConflictBanner({
|
|
|
176
176
|
<>
|
|
177
177
|
<div
|
|
178
178
|
role="alert"
|
|
179
|
-
className="absolute left-1/2 top-14 z-
|
|
179
|
+
className="absolute left-1/2 top-14 z-94 flex max-w-[calc(100vw-32px)] -translate-x-1/2 flex-wrap items-center gap-3 rounded-md border border-amber-400/30 bg-amber-950/95 px-4 py-2 text-[12px] font-medium text-amber-50 shadow-lg"
|
|
180
180
|
>
|
|
181
181
|
<span>
|
|
182
182
|
{conflict
|
|
@@ -201,7 +201,7 @@ export function ExternalFileConflictBanner({
|
|
|
201
201
|
<button
|
|
202
202
|
type="button"
|
|
203
203
|
onClick={() => void coordinator.useExternalFile()}
|
|
204
|
-
className="rounded border border-amber-200/30 px-2 py-1"
|
|
204
|
+
className="rounded-sm border border-amber-200/30 px-2 py-1"
|
|
205
205
|
>
|
|
206
206
|
Discard Studio edits and reload file
|
|
207
207
|
</button>
|
|
@@ -217,7 +217,7 @@ export function ExternalFileConflictBanner({
|
|
|
217
217
|
void coordinator.keepStudioFile();
|
|
218
218
|
}
|
|
219
219
|
}}
|
|
220
|
-
className="rounded border border-red-300/40 px-2 py-1 text-red-100"
|
|
220
|
+
className="rounded-sm border border-red-300/40 px-2 py-1 text-red-100"
|
|
221
221
|
>
|
|
222
222
|
Overwrite file with Studio version
|
|
223
223
|
</button>
|
|
@@ -234,7 +234,7 @@ export function ExternalFileConflictBanner({
|
|
|
234
234
|
void coordinator.keepStudioFile();
|
|
235
235
|
}
|
|
236
236
|
}}
|
|
237
|
-
className="rounded border border-red-300/40 px-2 py-1 text-red-100"
|
|
237
|
+
className="rounded-sm border border-red-300/40 px-2 py-1 text-red-100"
|
|
238
238
|
>
|
|
239
239
|
Overwrite file with recovered Studio draft
|
|
240
240
|
</button>
|
|
@@ -58,7 +58,7 @@ export function LintModal({
|
|
|
58
58
|
|
|
59
59
|
return (
|
|
60
60
|
<div
|
|
61
|
-
className="hf-backdrop-in fixed inset-0 z-
|
|
61
|
+
className="hf-backdrop-in fixed inset-0 z-100 flex items-center justify-center bg-black/60 backdrop-blur-xs"
|
|
62
62
|
onClick={requestClose}
|
|
63
63
|
>
|
|
64
64
|
<div
|
|
@@ -67,7 +67,7 @@ export function LintModal({
|
|
|
67
67
|
aria-modal="true"
|
|
68
68
|
aria-label={title}
|
|
69
69
|
tabIndex={-1}
|
|
70
|
-
className="bg-neutral-950 border border-neutral-800 rounded-xl shadow-2xl w-full max-w-xl max-h-[80vh] flex flex-col overflow-hidden outline-
|
|
70
|
+
className="bg-neutral-950 border border-neutral-800 rounded-xl shadow-2xl w-full max-w-xl max-h-[80vh] flex flex-col overflow-hidden outline-hidden"
|
|
71
71
|
onClick={(e) => e.stopPropagation()}
|
|
72
72
|
>
|
|
73
73
|
{/* Header */}
|
|
@@ -130,20 +130,13 @@ export function LintModal({
|
|
|
130
130
|
{errors.map((f, i) => (
|
|
131
131
|
<div key={`e-${i}`} className="py-3 border-b border-neutral-800/50 last:border-0">
|
|
132
132
|
<div className="flex items-start gap-2">
|
|
133
|
-
<WarningIcon
|
|
134
|
-
size={14}
|
|
135
|
-
className="text-red-400 flex-shrink-0 mt-0.5"
|
|
136
|
-
weight="fill"
|
|
137
|
-
/>
|
|
133
|
+
<WarningIcon size={14} className="text-red-400 shrink-0 mt-0.5" weight="fill" />
|
|
138
134
|
<div className="min-w-0">
|
|
139
135
|
<p className="text-sm text-neutral-200">{f.message}</p>
|
|
140
136
|
{f.file && <p className="text-xs text-neutral-600 font-mono mt-0.5">{f.file}</p>}
|
|
141
137
|
{f.fixHint && (
|
|
142
138
|
<div className="flex items-start gap-1 mt-1.5">
|
|
143
|
-
<CaretRightIcon
|
|
144
|
-
size={10}
|
|
145
|
-
className="text-studio-accent flex-shrink-0 mt-0.5"
|
|
146
|
-
/>
|
|
139
|
+
<CaretRightIcon size={10} className="text-studio-accent shrink-0 mt-0.5" />
|
|
147
140
|
<p className="text-xs text-studio-accent">{f.fixHint}</p>
|
|
148
141
|
</div>
|
|
149
142
|
)}
|
|
@@ -154,16 +147,13 @@ export function LintModal({
|
|
|
154
147
|
{warnings.map((f, i) => (
|
|
155
148
|
<div key={`w-${i}`} className="py-3 border-b border-neutral-800/50 last:border-0">
|
|
156
149
|
<div className="flex items-start gap-2">
|
|
157
|
-
<WarningIcon size={14} className="text-amber-400
|
|
150
|
+
<WarningIcon size={14} className="text-amber-400 shrink-0 mt-0.5" />
|
|
158
151
|
<div className="min-w-0">
|
|
159
152
|
<p className="text-sm text-neutral-300">{f.message}</p>
|
|
160
153
|
{f.file && <p className="text-xs text-neutral-600 font-mono mt-0.5">{f.file}</p>}
|
|
161
154
|
{f.fixHint && (
|
|
162
155
|
<div className="flex items-start gap-1 mt-1.5">
|
|
163
|
-
<CaretRightIcon
|
|
164
|
-
size={10}
|
|
165
|
-
className="text-studio-accent flex-shrink-0 mt-0.5"
|
|
166
|
-
/>
|
|
156
|
+
<CaretRightIcon size={10} className="text-studio-accent shrink-0 mt-0.5" />
|
|
167
157
|
<p className="text-xs text-studio-accent">{f.fixHint}</p>
|
|
168
158
|
</div>
|
|
169
159
|
)}
|
|
@@ -45,7 +45,7 @@ export function MediaPreview({ projectId, filePath }: { projectId: string; fileP
|
|
|
45
45
|
src={serveUrl}
|
|
46
46
|
alt={name}
|
|
47
47
|
onError={setFailed}
|
|
48
|
-
className="max-w-full max-h-[70%] object-contain rounded border border-neutral-800"
|
|
48
|
+
className="max-w-full max-h-[70%] object-contain rounded-sm border border-neutral-800"
|
|
49
49
|
/>
|
|
50
50
|
<span className="mt-3 text-[11px] text-neutral-500 font-mono">{filePath}</span>
|
|
51
51
|
</div>
|
|
@@ -59,7 +59,7 @@ export function MediaPreview({ projectId, filePath }: { projectId: string; fileP
|
|
|
59
59
|
src={serveUrl}
|
|
60
60
|
controls
|
|
61
61
|
onError={setFailed}
|
|
62
|
-
className="max-w-full max-h-[70%] rounded border border-neutral-800"
|
|
62
|
+
className="max-w-full max-h-[70%] rounded-sm border border-neutral-800"
|
|
63
63
|
/>
|
|
64
64
|
<span className="mt-3 text-[11px] text-neutral-500 font-mono">{filePath}</span>
|
|
65
65
|
</div>
|
|
@@ -8,14 +8,14 @@ interface SaveQueuePausedBannerProps {
|
|
|
8
8
|
export function SaveQueuePausedBanner({ message, onRetry }: SaveQueuePausedBannerProps) {
|
|
9
9
|
return (
|
|
10
10
|
<div
|
|
11
|
-
className="hf-backdrop-in absolute left-1/2 top-14 z-
|
|
11
|
+
className="hf-backdrop-in absolute left-1/2 top-14 z-92 flex max-w-[calc(100vw-32px)] -translate-x-1/2 items-center gap-3 rounded-md border border-red-500/30 bg-red-950/85 px-4 py-2 text-[12px] font-medium text-red-100 shadow-lg shadow-black/30"
|
|
12
12
|
role="alert"
|
|
13
13
|
>
|
|
14
14
|
<span>{message}</span>
|
|
15
15
|
<button
|
|
16
16
|
type="button"
|
|
17
17
|
onClick={onRetry}
|
|
18
|
-
className="rounded border border-red-300/20 px-2 py-1 text-[11px] text-red-100 transition-colors hover:bg-red-400/10 active:scale-[0.98]"
|
|
18
|
+
className="rounded-sm border border-red-300/20 px-2 py-1 text-[11px] text-red-100 transition-colors hover:bg-red-400/10 active:scale-[0.98]"
|
|
19
19
|
>
|
|
20
20
|
Retry saving
|
|
21
21
|
</button>
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { MouseEvent } from "react";
|
|
2
2
|
import { RotateCcw, RotateCw, Camera } from "../icons/SystemIcons";
|
|
3
3
|
import { getHistoryShortcutLabel } from "../utils/studioHelpers";
|
|
4
4
|
import { useStudioShellContext } from "../contexts/StudioContext";
|
|
5
5
|
import { usePanelLayoutContext } from "../contexts/PanelLayoutContext";
|
|
6
|
-
import { useViewMode, type StudioViewMode } from "../contexts/ViewModeContext";
|
|
7
6
|
import { trackStudioEvent } from "../utils/studioTelemetry";
|
|
8
7
|
import { Tooltip } from "./ui";
|
|
9
8
|
|
|
@@ -139,63 +138,6 @@ function HyperframesLogo() {
|
|
|
139
138
|
);
|
|
140
139
|
}
|
|
141
140
|
|
|
142
|
-
const VIEW_MODE_OPTIONS: Array<{ mode: StudioViewMode; label: string }> = [
|
|
143
|
-
{ mode: "storyboard", label: "Storyboard" },
|
|
144
|
-
{ mode: "timeline", label: "Preview" },
|
|
145
|
-
];
|
|
146
|
-
|
|
147
|
-
/** Segmented control switching the main stage between storyboard and preview. */
|
|
148
|
-
export function ViewModeToggle() {
|
|
149
|
-
const { viewMode, setViewMode } = useViewMode();
|
|
150
|
-
const tabRefs = useRef<Array<HTMLButtonElement | null>>([]);
|
|
151
|
-
|
|
152
|
-
const selectMode = (mode: StudioViewMode) => {
|
|
153
|
-
if (mode === viewMode) return;
|
|
154
|
-
if (setViewMode(mode)) trackStudioEvent("view_mode_toggle", { mode });
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
// Complete APG tabs pattern: roving tabIndex + arrow-key navigation.
|
|
158
|
-
const handleKeyDown = (e: React.KeyboardEvent, index: number) => {
|
|
159
|
-
if (e.key !== "ArrowLeft" && e.key !== "ArrowRight") return;
|
|
160
|
-
e.preventDefault();
|
|
161
|
-
const dir = e.key === "ArrowLeft" ? -1 : 1;
|
|
162
|
-
const next = (index + dir + VIEW_MODE_OPTIONS.length) % VIEW_MODE_OPTIONS.length;
|
|
163
|
-
tabRefs.current[next]?.focus();
|
|
164
|
-
selectMode(VIEW_MODE_OPTIONS[next].mode);
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
return (
|
|
168
|
-
<div
|
|
169
|
-
className="flex items-center gap-0.5 rounded-md bg-neutral-800 p-0.5"
|
|
170
|
-
role="tablist"
|
|
171
|
-
aria-label="Studio view"
|
|
172
|
-
>
|
|
173
|
-
{VIEW_MODE_OPTIONS.map(({ mode, label }, index) => {
|
|
174
|
-
const active = viewMode === mode;
|
|
175
|
-
return (
|
|
176
|
-
<button
|
|
177
|
-
key={mode}
|
|
178
|
-
ref={(el) => {
|
|
179
|
-
tabRefs.current[index] = el;
|
|
180
|
-
}}
|
|
181
|
-
type="button"
|
|
182
|
-
role="tab"
|
|
183
|
-
aria-selected={active}
|
|
184
|
-
tabIndex={active ? 0 : -1}
|
|
185
|
-
onClick={() => selectMode(mode)}
|
|
186
|
-
onKeyDown={(e) => handleKeyDown(e, index)}
|
|
187
|
-
className={`rounded px-3 py-1 text-[11px] font-medium transition-colors active:scale-[0.98] outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-studio-accent ${
|
|
188
|
-
active ? "bg-neutral-200 text-neutral-900" : "text-neutral-400 hover:text-neutral-200"
|
|
189
|
-
}`}
|
|
190
|
-
>
|
|
191
|
-
{label}
|
|
192
|
-
</button>
|
|
193
|
-
);
|
|
194
|
-
})}
|
|
195
|
-
</div>
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
141
|
/**
|
|
200
142
|
* Does the header's Inspector button open the panel, or close it?
|
|
201
143
|
*
|
|
@@ -232,7 +174,7 @@ export function StudioHeader({
|
|
|
232
174
|
const ffmpegMissing = renderQueue.ffmpegMissing;
|
|
233
175
|
|
|
234
176
|
return (
|
|
235
|
-
<div className="flex items-center justify-between h-10 px-3 bg-neutral-900 border-b border-neutral-800
|
|
177
|
+
<div className="flex items-center justify-between h-10 px-3 bg-neutral-900 border-b border-neutral-800 shrink-0">
|
|
236
178
|
{/* Left: logo + project name */}
|
|
237
179
|
<div className="flex items-center gap-3">
|
|
238
180
|
<HyperframesLogo />
|
|
@@ -241,8 +183,6 @@ export function StudioHeader({
|
|
|
241
183
|
</span>
|
|
242
184
|
<span className="text-[11px] font-medium text-neutral-300">{projectId}</span>
|
|
243
185
|
</div>
|
|
244
|
-
{/* Center: storyboard / preview toggle */}
|
|
245
|
-
<ViewModeToggle />
|
|
246
186
|
{/* Right: toolbar buttons */}
|
|
247
187
|
<div className="flex items-center gap-1.5">
|
|
248
188
|
<Tooltip
|
|
@@ -17,6 +17,7 @@ export interface StudioLeftSidebarProps {
|
|
|
17
17
|
onLint: () => void;
|
|
18
18
|
linting: boolean;
|
|
19
19
|
lintFindingCount?: number;
|
|
20
|
+
lintHasError?: boolean;
|
|
20
21
|
lintFindingsByFile?: Map<string, { count: number; messages: string[] }>;
|
|
21
22
|
onAddAssetToTimeline?: (path: string) => void;
|
|
22
23
|
onAddCompositionToTimeline?: (path: string) => void;
|
|
@@ -31,6 +32,7 @@ export function StudioLeftSidebar({
|
|
|
31
32
|
onLint,
|
|
32
33
|
linting,
|
|
33
34
|
lintFindingCount,
|
|
35
|
+
lintHasError,
|
|
34
36
|
lintFindingsByFile,
|
|
35
37
|
onAddAssetToTimeline,
|
|
36
38
|
onAddCompositionToTimeline,
|
|
@@ -86,7 +88,7 @@ export function StudioLeftSidebar({
|
|
|
86
88
|
|
|
87
89
|
if (effectiveLeftCollapsed) {
|
|
88
90
|
return (
|
|
89
|
-
<div className="mr-0.5 flex w-10
|
|
91
|
+
<div className="mr-0.5 flex w-10 shrink-0 flex-col items-center rounded-lg border border-neutral-800/50 bg-neutral-950 pt-1">
|
|
90
92
|
<button
|
|
91
93
|
type="button"
|
|
92
94
|
onClick={toggleLeftSidebar}
|
|
@@ -160,6 +162,7 @@ export function StudioLeftSidebar({
|
|
|
160
162
|
onLint={onLint}
|
|
161
163
|
linting={linting}
|
|
162
164
|
lintFindingCount={lintFindingCount}
|
|
165
|
+
lintHasError={lintHasError}
|
|
163
166
|
lintFindingsByFile={lintFindingsByFile}
|
|
164
167
|
onToggleCollapse={toggleLeftSidebar}
|
|
165
168
|
onAddBlock={onAddBlock}
|
|
@@ -169,14 +172,14 @@ export function StudioLeftSidebar({
|
|
|
169
172
|
/>
|
|
170
173
|
{/* Vertical resize divider: 3px visible seam, 13px pointer-capture zone via
|
|
171
174
|
the absolutely-positioned inner hit area. The outer element is w-[3px] so
|
|
172
|
-
it contributes only 3px of gap in the flex row; the inner
|
|
175
|
+
it contributes only 3px of gap in the flex row; the inner left-[-2px]
|
|
173
176
|
element widens the hit area without affecting layout. */}
|
|
174
177
|
<div
|
|
175
178
|
role="separator"
|
|
176
179
|
aria-label="Resize sidebar"
|
|
177
180
|
aria-orientation="vertical"
|
|
178
181
|
tabIndex={0}
|
|
179
|
-
className="group relative w-[3px]
|
|
182
|
+
className="group relative w-[3px] shrink-0 cursor-col-resize outline-hidden focus-visible:bg-studio-accent/20"
|
|
180
183
|
style={{ touchAction: "none" }}
|
|
181
184
|
onPointerDown={(e) => handlePanelResizeStart("left", e)}
|
|
182
185
|
onPointerMove={handlePanelResizeMove}
|
|
@@ -195,7 +198,7 @@ export function StudioLeftSidebar({
|
|
|
195
198
|
24px WCAG 2.2 (2.5.8) target because the next pixel on either side is
|
|
196
199
|
live: the sidebar's scrolling tab content on the left, the preview
|
|
197
200
|
stage on the right. Silently stealing their clicks is the worse bug. */}
|
|
198
|
-
<div className="absolute inset-y-0
|
|
201
|
+
<div className="absolute inset-y-0 left-[-2px] w-[13px]" />
|
|
199
202
|
{/* Visible hairline */}
|
|
200
203
|
<div className="absolute top-1/2 left-0 h-[52px] w-[3px] -translate-y-1/2 bg-white/12 transition-colors group-hover:bg-white/18 group-active:bg-white/24" />
|
|
201
204
|
</div>
|
|
@@ -77,7 +77,7 @@ export function StudioOverlays({
|
|
|
77
77
|
)}
|
|
78
78
|
{/* One bottom-right stack so the feedback card and toasts queue instead
|
|
79
79
|
of covering each other. Empty when nothing is showing. */}
|
|
80
|
-
<div className="absolute bottom-6 right-6 z-
|
|
80
|
+
<div className="absolute bottom-6 right-6 z-91 flex flex-col items-end gap-2">
|
|
81
81
|
{toasts.map((toast) => (
|
|
82
82
|
<StudioToast
|
|
83
83
|
key={toast.id}
|
|
@@ -347,7 +347,7 @@ export function StudioRightPanel({
|
|
|
347
347
|
aria-label="Resize inspector panel"
|
|
348
348
|
aria-orientation="vertical"
|
|
349
349
|
tabIndex={0}
|
|
350
|
-
className="group relative w-[3px]
|
|
350
|
+
className="group relative w-[3px] shrink-0 cursor-col-resize outline-hidden focus-visible:bg-studio-accent/20"
|
|
351
351
|
style={{ touchAction: "none" }}
|
|
352
352
|
onPointerDown={(e) => handlePanelResizeStart("right", e)}
|
|
353
353
|
onPointerMove={handlePanelResizeMove}
|
|
@@ -364,12 +364,12 @@ export function StudioRightPanel({
|
|
|
364
364
|
{/* Asymmetric hit zone: 8px into the preview's p-2 gutter (the only dead
|
|
365
365
|
space), the 3px seam, 2px into the card. Stops short of the 24px WCAG
|
|
366
366
|
2.5.8 target because the next pixel each way is live. */}
|
|
367
|
-
<div className="absolute inset-y-0
|
|
367
|
+
<div className="absolute inset-y-0 left-[-8px] w-[13px]" />
|
|
368
368
|
{/* Visible hairline */}
|
|
369
369
|
<div className="absolute top-1/2 left-0 h-[52px] w-[3px] -translate-y-1/2 bg-white/12 transition-colors group-hover:bg-white/18 group-active:bg-white/24" />
|
|
370
370
|
</div>
|
|
371
371
|
<div
|
|
372
|
-
className="flex min-w-0
|
|
372
|
+
className="flex min-w-0 shrink-0 flex-col overflow-hidden rounded-lg border border-neutral-800 bg-neutral-950"
|
|
373
373
|
style={{ width: rightWidth }}
|
|
374
374
|
>
|
|
375
375
|
{captionEditMode ? (
|
|
@@ -444,7 +444,7 @@ export function StudioRightPanel({
|
|
|
444
444
|
role="separator"
|
|
445
445
|
aria-label="Resize Layers and Design panes"
|
|
446
446
|
aria-orientation="horizontal"
|
|
447
|
-
className="group flex h-2
|
|
447
|
+
className="group flex h-2 shrink-0 cursor-row-resize items-center justify-center border-y border-neutral-800 bg-neutral-900"
|
|
448
448
|
style={{ touchAction: "none" }}
|
|
449
449
|
onPointerDown={handleInspectorSplitResizeStart}
|
|
450
450
|
onPointerMove={handleInspectorSplitResizeMove}
|
|
@@ -30,7 +30,7 @@ export function StudioToast({ message, tone, leaving, onDismiss }: StudioToastPr
|
|
|
30
30
|
}}
|
|
31
31
|
>
|
|
32
32
|
<span
|
|
33
|
-
className={`min-w-0 break-
|
|
33
|
+
className={`min-w-0 wrap-break-word leading-5 ${isError ? "text-red-200" : "text-neutral-200"}`}
|
|
34
34
|
>
|
|
35
35
|
{message}
|
|
36
36
|
</span>
|
|
@@ -38,7 +38,7 @@ export function StudioToast({ message, tone, leaving, onDismiss }: StudioToastPr
|
|
|
38
38
|
<button
|
|
39
39
|
type="button"
|
|
40
40
|
onClick={onDismiss}
|
|
41
|
-
className="flex h-5 w-5
|
|
41
|
+
className="flex h-5 w-5 shrink-0 items-center justify-center rounded-md text-neutral-500 transition-colors hover:bg-white/10 hover:text-neutral-300"
|
|
42
42
|
aria-label="Dismiss"
|
|
43
43
|
>
|
|
44
44
|
<svg
|
|
@@ -189,8 +189,8 @@ export function TimelineToolbar({ domEditSession, onSplitElement }: TimelineTool
|
|
|
189
189
|
// CapCut-flat icon buttons: no per-button border/box chrome — a transparent
|
|
190
190
|
// 28px hit area with a subtle rounded hover wash, consistent 16px glyphs.
|
|
191
191
|
const flatBtn = "flex h-7 w-7 items-center justify-center rounded-md transition-colors";
|
|
192
|
-
const flatIdle = `${flatBtn} text-neutral-400 hover:bg-white/
|
|
193
|
-
const flatActive = `${flatBtn} bg-white/
|
|
192
|
+
const flatIdle = `${flatBtn} text-neutral-400 hover:bg-white/6 hover:text-neutral-200 active:scale-[0.98]`;
|
|
193
|
+
const flatActive = `${flatBtn} bg-white/8 text-neutral-100 active:scale-[0.98]`;
|
|
194
194
|
const flatDisabled = `${flatBtn} text-neutral-700 cursor-not-allowed`;
|
|
195
195
|
|
|
196
196
|
return (
|
|
@@ -297,7 +297,7 @@ export function TimelineToolbar({ domEditSession, onSplitElement }: TimelineTool
|
|
|
297
297
|
className={
|
|
298
298
|
!onToggleKeyframe
|
|
299
299
|
? flatDisabled
|
|
300
|
-
: `${flatBtn} active:scale-[0.98] hover:bg-white/
|
|
300
|
+
: `${flatBtn} active:scale-[0.98] hover:bg-white/6 ${
|
|
301
301
|
keyframeState === "active"
|
|
302
302
|
? "text-studio-accent"
|
|
303
303
|
: keyframeState === "inactive"
|
|
@@ -332,7 +332,7 @@ export function TimelineToolbar({ domEditSession, onSplitElement }: TimelineTool
|
|
|
332
332
|
onClick={() => setAutoKeyframeEnabled(!autoKeyframeEnabled)}
|
|
333
333
|
aria-label="Auto-record manual edits as keyframes"
|
|
334
334
|
aria-pressed={autoKeyframeEnabled}
|
|
335
|
-
className={`${flatBtn} active:scale-[0.98] hover:bg-white/
|
|
335
|
+
className={`${flatBtn} active:scale-[0.98] hover:bg-white/6 ${
|
|
336
336
|
autoKeyframeEnabled
|
|
337
337
|
? "text-red-400 hover:text-red-300"
|
|
338
338
|
: "text-neutral-600 hover:text-neutral-400"
|
|
@@ -429,7 +429,7 @@ export function TimelineToolbar({ domEditSession, onSplitElement }: TimelineTool
|
|
|
429
429
|
}}
|
|
430
430
|
className={
|
|
431
431
|
canAdd
|
|
432
|
-
? `${flatBtn} text-neutral-400 hover:bg-white/
|
|
432
|
+
? `${flatBtn} text-neutral-400 hover:bg-white/6 hover:text-[#22c55e] active:scale-[0.98]`
|
|
433
433
|
: flatDisabled
|
|
434
434
|
}
|
|
435
435
|
>
|
|
@@ -467,7 +467,7 @@ export function TimelineToolbar({ domEditSession, onSplitElement }: TimelineTool
|
|
|
467
467
|
className={`h-7 px-2 rounded-md text-[11px] font-medium transition-colors ${
|
|
468
468
|
thumbnailsVisible
|
|
469
469
|
? "bg-studio-accent/10 text-studio-accent"
|
|
470
|
-
: "text-neutral-400 hover:bg-white/
|
|
470
|
+
: "text-neutral-400 hover:bg-white/6 hover:text-neutral-200"
|
|
471
471
|
}`}
|
|
472
472
|
>
|
|
473
473
|
<Image size={16} aria-hidden="true" />
|
|
@@ -480,7 +480,7 @@ export function TimelineToolbar({ domEditSession, onSplitElement }: TimelineTool
|
|
|
480
480
|
className={`h-7 px-2 rounded-md text-[11px] font-medium transition-colors ${
|
|
481
481
|
zoomMode === "fit"
|
|
482
482
|
? "bg-studio-accent/10 text-studio-accent"
|
|
483
|
-
: "text-neutral-400 hover:bg-white/
|
|
483
|
+
: "text-neutral-400 hover:bg-white/6 hover:text-neutral-200"
|
|
484
484
|
}`}
|
|
485
485
|
>
|
|
486
486
|
Fit
|