@hyperframes/studio 0.8.104 → 0.8.106
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-DAoaajuM.js → hyperframes-player-dl6K6ABp.js} +1 -1
- package/dist/assets/index-CMiYVGv5.js +399 -0
- package/dist/assets/{index-WV9LroAD.js → index-DKT5NF1Y.js} +1 -1
- package/dist/assets/index-lrT-gfHa.css +1 -0
- package/dist/assets/{index-BDK4rTMb.js → index-ooaZZhKf.js} +1 -1
- package/dist/{chunk-62DDWOSH.js → chunk-EEXSYY5F.js} +19 -18
- package/dist/chunk-EEXSYY5F.js.map +1 -0
- package/dist/{chunk-HIGHVWYD.js → chunk-ICTZJPQQ.js} +3 -2
- package/dist/chunk-ICTZJPQQ.js.map +1 -0
- package/dist/{chunk-6GAO75IU.js → chunk-NCUBAVGK.js} +10 -8
- package/dist/chunk-NCUBAVGK.js.map +1 -0
- package/dist/index.css +6 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +15 -4
- package/dist/index.html +11 -2
- package/dist/index.js +10391 -9836
- package/dist/index.js.map +1 -1
- package/dist/player.js +2 -2
- package/dist/styles/tailwind-preset.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/ui.js +2 -2
- package/package.json +9 -9
- package/src/captions/components/CaptionAnimationPanel.tsx +2 -2
- package/src/captions/components/CaptionPropertyPanel.tsx +2 -2
- package/src/captions/components/CaptionTimeline.tsx +3 -3
- package/src/captions/keyboard.test.ts +6 -0
- package/src/captions/keyboard.ts +2 -1
- package/src/components/AskAgentModal.tsx +1 -1
- package/src/components/LintModal.tsx +35 -35
- package/src/components/StudioErrorBoundary.tsx +1 -1
- package/src/components/StudioHeader.dom.test.tsx +35 -13
- package/src/components/StudioHeader.tsx +6 -2
- package/src/components/StudioToast.tsx +7 -16
- package/src/components/ThemeToggle.tsx +99 -0
- package/src/components/TimelineToolPicker.tsx +1 -1
- package/src/components/TimelineToolbar.tsx +11 -11
- package/src/components/dock/Dock.tsx +1 -1
- package/src/components/dock/dock.css +9 -1
- package/src/components/editor/AnimationCard.tsx +5 -5
- package/src/components/editor/AnimationCardParts.tsx +1 -1
- package/src/components/editor/BlockParamsPanel.tsx +3 -3
- package/src/components/editor/BorderRadiusEditor.tsx +8 -15
- package/src/components/editor/CanvasContextMenu.tsx +1 -1
- package/src/components/editor/ComputedTweenNotice.tsx +1 -1
- package/src/components/editor/DomEditOverlay.tsx +37 -5
- package/src/components/editor/DomEditRotateHandle.tsx +1 -1
- package/src/components/editor/EaseCurveSection.tsx +14 -19
- package/src/components/editor/EaseModeControls.tsx +2 -2
- package/src/components/editor/EaseParamFields.tsx +8 -8
- package/src/components/editor/FileTree.tsx +1 -1
- package/src/components/editor/FileTreeIcons.tsx +47 -26
- package/src/components/editor/FileTreeNodes.tsx +11 -8
- package/src/components/editor/GestureRecordControl.tsx +3 -3
- package/src/components/editor/GsapAddAnimationControl.tsx +1 -1
- package/src/components/editor/GsapAnimationSection.tsx +2 -2
- package/src/components/editor/InlineTextToolbar.tsx +4 -4
- package/src/components/editor/InspectorHeaderActions.tsx +1 -1
- package/src/components/editor/KeyframeDiamond.tsx +6 -4
- package/src/components/editor/KeyframeNavigation.tsx +4 -4
- package/src/components/editor/LayersPanel.tsx +3 -3
- package/src/components/editor/PromotableControl.tsx +2 -2
- package/src/components/editor/PropertyPanelEmptyState.tsx +4 -4
- package/src/components/editor/PropertyPanelFlatFooter.tsx +2 -2
- package/src/components/editor/PropertyPanelFlatHeader.test.tsx +1 -1
- package/src/components/editor/PropertyPanelFlatHeader.tsx +2 -2
- package/src/components/editor/SnapToolbar.tsx +5 -5
- package/src/components/editor/SourceEditor.tsx +2 -6
- package/src/components/editor/TimelineFxPopover.tsx +4 -4
- package/src/components/editor/Transform3DCube.tsx +1 -1
- package/src/components/editor/anchoredResizeReleaseShift.test.ts +0 -84
- package/src/components/editor/domEditOverlayStartGesture.ts +127 -115
- package/src/components/editor/easeCurveSvg.tsx +1 -1
- package/src/components/editor/gestureHoldsPreviewReload.test.tsx +275 -0
- package/src/components/editor/manualEditsDom.ts +36 -3
- package/src/components/editor/manualEditsDomPatches.test.ts +2 -86
- package/src/components/editor/manualEditsDomPatches.ts +0 -30
- package/src/components/editor/manualEditsSnapshot.ts +1 -0
- package/src/components/editor/marqueeCommit.test.tsx +69 -1
- package/src/components/editor/marqueeCommit.ts +6 -1
- package/src/components/editor/overlayFrameLoop.test.ts +27 -0
- package/src/components/editor/overlayFrameLoop.ts +21 -0
- package/src/components/editor/persistSeam.integration.test.ts +0 -18
- package/src/components/editor/plainTranslate.ts +34 -3
- package/src/components/editor/propertyPanelColorCurveGraph.tsx +2 -2
- package/src/components/editor/propertyPanelColorGradingControls.tsx +1 -1
- package/src/components/editor/propertyPanelColorGradingSection.tsx +8 -8
- package/src/components/editor/propertyPanelColorGradingSlider.tsx +1 -1
- package/src/components/editor/propertyPanelColorSecondary.tsx +4 -4
- package/src/components/editor/propertyPanelFill.tsx +6 -6
- package/src/components/editor/propertyPanelFlatColorGradingAccessory.tsx +3 -3
- package/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx +1 -1
- package/src/components/editor/propertyPanelFlatColorGradingSection.tsx +6 -9
- package/src/components/editor/propertyPanelFlatEffectsSection.tsx +4 -4
- package/src/components/editor/propertyPanelFlatMediaSection.tsx +2 -2
- package/src/components/editor/propertyPanelFlatMotionSection.tsx +2 -2
- package/src/components/editor/propertyPanelFlatOverlaysSection.tsx +2 -2
- package/src/components/editor/propertyPanelFlatPrimitives.test.tsx +4 -3
- package/src/components/editor/propertyPanelFlatPrimitives.tsx +1 -1
- package/src/components/editor/propertyPanelFlatSliderReadout.tsx +1 -1
- package/src/components/editor/propertyPanelFont.test.tsx +80 -3
- package/src/components/editor/propertyPanelFont.tsx +71 -48
- package/src/components/editor/propertyPanelFxCarveModule.tsx +1 -1
- package/src/components/editor/propertyPanelFxControls.tsx +2 -2
- package/src/components/editor/propertyPanelFxEqModule.tsx +2 -2
- package/src/components/editor/propertyPanelFxNodeRow.tsx +1 -1
- package/src/components/editor/propertyPanelFxPresetRun.tsx +1 -1
- package/src/components/editor/propertyPanelHelpers.ts +1 -2
- package/src/components/editor/propertyPanelMediaSection.tsx +1 -1
- package/src/components/editor/propertyPanelPrimitives.tsx +1 -1
- package/src/components/editor/propertyPanelSections.tsx +2 -2
- package/src/components/editor/propertyPanelValueTier.test.ts +1 -1
- package/src/components/editor/propertyPanelValueTier.ts +1 -1
- package/src/components/editor/studioCodeTheme.test.ts +20 -0
- package/src/components/editor/studioCodeTheme.ts +99 -0
- package/src/components/editor/useDomEditNudge.test.tsx +31 -0
- package/src/components/editor/useDomEditNudge.ts +2 -2
- package/src/components/editor/useInlineTextEditing.tsx +1 -1
- package/src/components/editor/useMotionPathData.test.tsx +64 -0
- package/src/components/editor/useMotionPathData.ts +5 -1
- package/src/components/feedback/StudioFeedbackCard.tsx +11 -23
- package/src/components/nle/AssetPreviewOverlay.tsx +1 -1
- package/src/components/nle/AudioMeterStrip.tsx +2 -2
- package/src/components/nle/NLEPreview.test.ts +8 -4
- package/src/components/nle/PreviewOverlays.tsx +1 -1
- package/src/components/nle/PreviewPane.tsx +3 -3
- package/src/components/nle/PreviewPoster.tsx +29 -1
- package/src/components/nle/PreviewZoomOverlay.tsx +1 -1
- package/src/components/panels/SlideshowPanel.tsx +4 -4
- package/src/components/panels/SlideshowSubPanels.tsx +19 -21
- package/src/components/panels/VariablesBindElement.tsx +1 -1
- package/src/components/panels/VariablesDeclarationForm.tsx +1 -1
- package/src/components/panels/VariablesPanel.tsx +4 -6
- package/src/components/panels/VariablesRowAction.tsx +1 -1
- package/src/components/renders/FfmpegRequiredNotice.tsx +4 -4
- package/src/components/renders/RenderQueue.tsx +2 -2
- package/src/components/renders/RenderQueueItem.tsx +3 -3
- package/src/components/sidebar/AssetCard.tsx +3 -3
- package/src/components/sidebar/AssetContextMenu.tsx +7 -7
- package/src/components/sidebar/AssetsTab.tsx +6 -6
- package/src/components/sidebar/AudioRow.tsx +5 -3
- package/src/components/sidebar/BlocksTab.tsx +6 -6
- package/src/components/sidebar/CompositionsTab.drag.test.tsx +3 -69
- package/src/components/sidebar/CompositionsTab.rootBadge.test.tsx +4 -50
- package/src/components/sidebar/CompositionsTab.thumbnails.test.tsx +256 -0
- package/src/components/sidebar/CompositionsTab.tsx +65 -12
- package/src/components/sidebar/PromptPreviewModal.tsx +3 -3
- package/src/components/sidebar/SidebarLintButton.tsx +3 -3
- package/src/components/sidebar/compositionsTabTestUtils.tsx +39 -0
- package/src/components/timelineToolbarStyles.ts +3 -3
- package/src/components/ui/Button.tsx +4 -4
- package/src/components/ui/HyperframesLogo.tsx +19 -18
- package/src/components/ui/Input.tsx +1 -1
- package/src/components/ui/Menu.tsx +4 -2
- package/src/hooks/appHotkeysDispatch.ts +2 -1
- package/src/hooks/elementOffsetStager.ts +8 -7
- package/src/hooks/previewPromotionSelection.test.tsx +180 -0
- package/src/hooks/useAppHotkeys.test.ts +32 -0
- package/src/hooks/useAppHotkeys.ts +1 -2
- package/src/hooks/useClipboard.source.test.ts +141 -23
- package/src/hooks/useClipboard.ts +154 -123
- package/src/hooks/useDomEditCommits.ts +0 -3
- package/src/hooks/useDomEditSession.test.tsx +76 -72
- package/src/hooks/useDomEditSession.ts +7 -43
- package/src/hooks/useDomGeometryCommit.test.tsx +4 -1
- package/src/hooks/useDomGeometryCommit.ts +0 -1
- package/src/hooks/useDomGeometryCommits.test.tsx +177 -32
- package/src/hooks/useDomGeometryCommits.ts +4 -49
- package/src/hooks/useGsapTweenCache.ts +1 -2
- package/src/hooks/useInlineTextEdit.ts +16 -2
- package/src/hooks/useLivePreviewIframe.ts +18 -0
- package/src/hooks/usePreviewPersistence.history.test.tsx +47 -0
- package/src/hooks/usePreviewPersistence.ts +2 -5
- package/src/hooks/useRenderClipContent.ts +3 -3
- package/src/index.ts +2 -0
- package/src/livePreviewIframeExport.test.tsx +33 -0
- package/src/main.tsx +4 -1
- package/src/player/components/AudioWaveform.test.tsx +36 -15
- package/src/player/components/AudioWaveform.tsx +24 -7
- package/src/player/components/ClipContextMenu.tsx +1 -1
- package/src/player/components/CompositionThumbnail.tsx +23 -11
- package/src/player/components/EditModal.tsx +6 -6
- package/src/player/components/KeyframeDiamondContextMenu.tsx +2 -2
- package/src/player/components/PlayerControls.tsx +3 -3
- package/src/player/components/ShortcutsPanel.tsx +8 -20
- package/src/player/components/TimelineCanvas.tsx +1 -1
- package/src/player/components/TimelineClip.test.tsx +38 -0
- package/src/player/components/TimelineClip.tsx +16 -8
- package/src/player/components/TimelineClipFades.test.tsx +376 -74
- package/src/player/components/TimelineClipFades.tsx +368 -137
- package/src/player/components/TimelineEmptyState.tsx +2 -2
- package/src/player/components/TimelineLanes.tsx +0 -1
- package/src/player/components/TrackClipCount.tsx +1 -1
- package/src/player/components/VolumeControl.tsx +1 -1
- package/src/player/components/timelineClipChildren.tsx +1 -1
- package/src/player/components/timelineMotionStyles.test.ts +6 -22
- package/src/player/components/trackHeaderLabelRows.tsx +16 -16
- package/src/player/components/useTimelineKeyboardActor.ts +1 -1
- package/src/player/hooks/timelinePlayerTestHarness.ts +27 -0
- package/src/player/hooks/useShadowPreviewReload.ts +72 -5
- package/src/player/lib/automationStoreSync.test.ts +18 -1
- package/src/player/lib/automationStoreSync.ts +29 -17
- package/src/player/lib/thumbnailScheduler.ts +4 -5
- package/src/player/lib/timelineViewportBudgets.test.ts +1 -1
- package/src/player/lib/timelineViewportBudgets.ts +1 -1
- package/src/player/sceneSwap.ts +18 -0
- package/src/styles/components.css +26 -0
- package/src/styles/hex-baseline.json +7 -19
- package/src/styles/studio.css +4 -7
- package/src/styles/tailwind-preset.shared.js +2 -1
- package/src/styles/theme.css +214 -147
- package/src/styles/theme.test.ts +13 -9
- package/src/utils/blockCategories.ts +41 -9
- package/src/utils/clipboardPayload.ts +12 -1
- package/src/utils/studioPendingEdits.ts +21 -0
- package/src/utils/studioTheme.test.tsx +141 -0
- package/src/utils/studioTheme.ts +34 -0
- package/src/utils/studioUiPreferences.ts +3 -0
- package/src/utils/typingTarget.test.ts +8 -1
- package/src/utils/typingTarget.ts +5 -0
- package/dist/assets/index-CQN3M56N.css +0 -1
- package/dist/assets/index-De2aO4XM.js +0 -399
- package/dist/chunk-62DDWOSH.js.map +0 -1
- package/dist/chunk-6GAO75IU.js.map +0 -1
- package/dist/chunk-HIGHVWYD.js.map +0 -1
- package/src/hooks/gsapTargetCache.ts +0 -62
|
@@ -3,7 +3,7 @@ import { ADD_METHODS, ADD_METHOD_LABELS, METHOD_TOOLTIPS } from "./gsapAnimation
|
|
|
3
3
|
const STYLES = {
|
|
4
4
|
classic: {
|
|
5
5
|
method:
|
|
6
|
-
"rounded-lg border border-neutral-700 bg-neutral-900 px-2.5 py-1.5 text-[11px] font-medium text-neutral-300 transition-colors hover:border-neutral-600 hover:text-
|
|
6
|
+
"rounded-lg border border-neutral-700 bg-neutral-900 px-2.5 py-1.5 text-[11px] font-medium text-neutral-300 transition-colors hover:border-neutral-600 hover:text-text-0",
|
|
7
7
|
cancel: "px-1.5 text-[11px] text-neutral-500 hover:text-neutral-300",
|
|
8
8
|
trigger: "text-[11px] font-medium text-neutral-400 transition-colors hover:text-neutral-200",
|
|
9
9
|
},
|
|
@@ -24,13 +24,13 @@ export const GsapAnimationSection = memo(function GsapAnimationSection({
|
|
|
24
24
|
return (
|
|
25
25
|
<Section title="Animation" icon={<Film size={15} />}>
|
|
26
26
|
{multipleTimelines && (
|
|
27
|
-
<p className="mb-2 rounded-lg bg-amber-500/10 px-3 py-2 text-[11px] leading-relaxed text-
|
|
27
|
+
<p className="mb-2 rounded-lg bg-amber-500/10 px-3 py-2 text-[11px] leading-relaxed text-warning-ink">
|
|
28
28
|
This file has multiple GSAP timelines. Animation editing is disabled to prevent data loss
|
|
29
29
|
— consolidate into a single timeline to enable editing.
|
|
30
30
|
</p>
|
|
31
31
|
)}
|
|
32
32
|
{unsupportedTimelinePattern && (
|
|
33
|
-
<p className="mb-2 rounded-lg bg-amber-500/10 px-3 py-2 text-[11px] leading-relaxed text-
|
|
33
|
+
<p className="mb-2 rounded-lg bg-amber-500/10 px-3 py-2 text-[11px] leading-relaxed text-warning-ink">
|
|
34
34
|
This timeline uses a computed key (window.__timelines[variable]) the editor can't
|
|
35
35
|
resolve statically. Use a string-literal key (window.__timelines["id"]) or a
|
|
36
36
|
variable declaration (const tl = gsap.timeline()) to enable editing.
|
|
@@ -77,7 +77,7 @@ export function InlineTextToolbar({
|
|
|
77
77
|
data-inline-text-toolbar="true"
|
|
78
78
|
role="toolbar"
|
|
79
79
|
aria-label="Text formatting"
|
|
80
|
-
className="pointer-events-auto fixed z-200 flex items-center gap-1 rounded-lg border border-
|
|
80
|
+
className="pointer-events-auto fixed z-200 flex items-center gap-1 rounded-lg border border-border bg-raised p-1 shadow-[0_8px_24px_rgba(0,0,0,0.55)]"
|
|
81
81
|
style={{
|
|
82
82
|
left: placement.left,
|
|
83
83
|
top: placement.top,
|
|
@@ -98,12 +98,12 @@ export function InlineTextToolbar({
|
|
|
98
98
|
onClick={(event) => event.stopPropagation()}
|
|
99
99
|
>
|
|
100
100
|
<label
|
|
101
|
-
className="group relative flex h-6 w-6 cursor-pointer items-center justify-center rounded-md hover:bg-
|
|
101
|
+
className="group relative flex h-6 w-6 cursor-pointer items-center justify-center rounded-md hover:bg-hover"
|
|
102
102
|
title="Text colour"
|
|
103
103
|
>
|
|
104
104
|
<span
|
|
105
105
|
aria-hidden="true"
|
|
106
|
-
className="h-4 w-4 rounded-full border-2 border-
|
|
106
|
+
className="h-4 w-4 rounded-full border-2 border-border-strong transition-transform duration-150 group-hover:scale-110 group-active:scale-95"
|
|
107
107
|
// `background` maps a gradient to the PADDING box and then repeats it
|
|
108
108
|
// to fill the border box, so the 1px border shows the strip either
|
|
109
109
|
// side of the tile: the end colour on the left, the start colour on
|
|
@@ -202,7 +202,7 @@ function ToolbarToggle({
|
|
|
202
202
|
aria-label={label}
|
|
203
203
|
aria-pressed={on}
|
|
204
204
|
className={`flex h-6 w-6 items-center justify-center rounded-md text-xs ${
|
|
205
|
-
on ? "bg-studio-accent/20 text-
|
|
205
|
+
on ? "bg-studio-accent/20 text-accent-ink" : "text-text-2 hover:bg-hover"
|
|
206
206
|
}`}
|
|
207
207
|
style={{
|
|
208
208
|
fontWeight: bold ? 700 : 400,
|
|
@@ -67,7 +67,7 @@ export function InspectorHeaderActions({
|
|
|
67
67
|
}}
|
|
68
68
|
className={`flex h-6 w-6 items-center justify-center rounded transition-colors ${
|
|
69
69
|
copied
|
|
70
|
-
? "text-
|
|
70
|
+
? "text-accent-ink"
|
|
71
71
|
: "text-neutral-500 hover:bg-neutral-800 hover:text-neutral-300"
|
|
72
72
|
}`}
|
|
73
73
|
title={copied ? "Copied!" : "Copy element info to clipboard"}
|
|
@@ -19,8 +19,11 @@ export const KeyframeDiamond = memo(function KeyframeDiamond({
|
|
|
19
19
|
isHold = false,
|
|
20
20
|
}: KeyframeDiamondProps) {
|
|
21
21
|
const isFilled = state === "active";
|
|
22
|
-
const
|
|
23
|
-
|
|
22
|
+
const color = {
|
|
23
|
+
active: "text-accent",
|
|
24
|
+
inactive: "text-text-2 hover:text-text-0",
|
|
25
|
+
ghost: "text-text-off hover:text-text-0",
|
|
26
|
+
}[state];
|
|
24
27
|
|
|
25
28
|
return (
|
|
26
29
|
<button
|
|
@@ -29,8 +32,7 @@ export const KeyframeDiamond = memo(function KeyframeDiamond({
|
|
|
29
32
|
e.stopPropagation();
|
|
30
33
|
onClick();
|
|
31
34
|
}}
|
|
32
|
-
className=
|
|
33
|
-
style={{ color, opacity }}
|
|
35
|
+
className={`relative shrink-0 p-0.5 transition-colors before:absolute before:-inset-1.5 before:content-[''] ${color}`}
|
|
34
36
|
title={title}
|
|
35
37
|
aria-label={title}
|
|
36
38
|
aria-pressed={state === "active"}
|
|
@@ -82,11 +82,11 @@ function ArrowLeft({ disabled }: { disabled: boolean }) {
|
|
|
82
82
|
height="10"
|
|
83
83
|
viewBox="0 0 6 10"
|
|
84
84
|
fill="none"
|
|
85
|
-
|
|
85
|
+
className={disabled ? "text-text-off" : "text-text-2"}
|
|
86
86
|
>
|
|
87
87
|
<path
|
|
88
88
|
d="M5 1L1 5L5 9"
|
|
89
|
-
stroke="
|
|
89
|
+
stroke="currentColor"
|
|
90
90
|
strokeWidth="1.4"
|
|
91
91
|
strokeLinecap="round"
|
|
92
92
|
strokeLinejoin="round"
|
|
@@ -102,11 +102,11 @@ function ArrowRight({ disabled }: { disabled: boolean }) {
|
|
|
102
102
|
height="10"
|
|
103
103
|
viewBox="0 0 6 10"
|
|
104
104
|
fill="none"
|
|
105
|
-
|
|
105
|
+
className={disabled ? "text-text-off" : "text-text-2"}
|
|
106
106
|
>
|
|
107
107
|
<path
|
|
108
108
|
d="M1 1L5 5L1 9"
|
|
109
|
-
stroke="
|
|
109
|
+
stroke="currentColor"
|
|
110
110
|
strokeWidth="1.4"
|
|
111
111
|
strokeLinecap="round"
|
|
112
112
|
strokeLinejoin="round"
|
|
@@ -458,7 +458,7 @@ export const LayersPanel = memo(function LayersPanel() {
|
|
|
458
458
|
isDragged
|
|
459
459
|
? "opacity-40"
|
|
460
460
|
: selected
|
|
461
|
-
? "bg-panel-accent/14 text-
|
|
461
|
+
? "bg-panel-accent/14 text-accent-ink"
|
|
462
462
|
: "text-panel-text-2 hover:bg-panel-hover/40 hover:text-panel-text-1"
|
|
463
463
|
} ${dragKey ? "cursor-grabbing" : "cursor-pointer"}`}
|
|
464
464
|
style={{ paddingLeft: 8 + layer.depth * 16 }}
|
|
@@ -494,9 +494,9 @@ export const LayersPanel = memo(function LayersPanel() {
|
|
|
494
494
|
<span
|
|
495
495
|
className={`flex h-5 w-5 shrink-0 items-center justify-center rounded text-[8px] font-bold uppercase ${
|
|
496
496
|
selected
|
|
497
|
-
? "bg-panel-accent/18 text-
|
|
497
|
+
? "bg-panel-accent/18 text-accent-ink"
|
|
498
498
|
: isCompHost
|
|
499
|
-
? "bg-
|
|
499
|
+
? "bg-on text-accent-ink"
|
|
500
500
|
: "bg-panel-hover text-panel-text-4"
|
|
501
501
|
}`}
|
|
502
502
|
>
|
|
@@ -83,7 +83,7 @@ export function PromotableControl({
|
|
|
83
83
|
<span
|
|
84
84
|
// Sits above the row (not on top of top-0) so it clears a value that
|
|
85
85
|
// renders flush to the row's right edge, e.g. flat Font/Color rows.
|
|
86
|
-
className="pointer-events-none absolute -top-2 right-1.5 z-10 inline-flex max-w-[60%] items-center gap-1 truncate rounded-sm bg-studio-accent/20 px-1 py-px font-mono text-[8px] font-medium text-
|
|
86
|
+
className="pointer-events-none absolute -top-2 right-1.5 z-10 inline-flex max-w-[60%] items-center gap-1 truncate rounded-sm bg-studio-accent/20 px-1 py-px font-mono text-[8px] font-medium text-accent-ink"
|
|
87
87
|
title={`Bound to variable "${promote.boundId}"`}
|
|
88
88
|
>
|
|
89
89
|
◆ {promote.boundId}
|
|
@@ -97,7 +97,7 @@ export function PromotableControl({
|
|
|
97
97
|
e.stopPropagation();
|
|
98
98
|
promote.promote();
|
|
99
99
|
}}
|
|
100
|
-
className="absolute -top-2 right-1.5 z-10 inline-flex items-center gap-1 rounded-sm bg-
|
|
100
|
+
className="absolute -top-2 right-1.5 z-10 inline-flex items-center gap-1 rounded-sm bg-raised/80 px-1 py-px font-mono text-[8px] font-medium text-neutral-400 opacity-70 transition-colors hover:bg-studio-accent/20 hover:text-accent-ink hover:opacity-100"
|
|
101
101
|
>
|
|
102
102
|
◇ var
|
|
103
103
|
</button>
|
|
@@ -24,7 +24,7 @@ function FlatEmptyState() {
|
|
|
24
24
|
<div className="mt-2 flex w-full flex-col gap-1.5">
|
|
25
25
|
<span className="flex items-center justify-between rounded-lg border border-panel-border bg-panel-bg px-3 py-2">
|
|
26
26
|
<span className="flex items-center gap-2 text-[11px] text-panel-text-2">
|
|
27
|
-
<span className="text-
|
|
27
|
+
<span className="text-danger-ink">●</span>
|
|
28
28
|
Record a gesture
|
|
29
29
|
</span>
|
|
30
30
|
<span className="rounded-sm border border-panel-border-input px-[5px] py-px font-mono text-[9px] text-panel-text-5">
|
|
@@ -33,7 +33,7 @@ function FlatEmptyState() {
|
|
|
33
33
|
</span>
|
|
34
34
|
<span className="flex items-center justify-between rounded-lg border border-panel-border bg-panel-bg px-3 py-2">
|
|
35
35
|
<span className="flex items-center gap-2 text-[11px] text-panel-text-2">
|
|
36
|
-
<span className="text-
|
|
36
|
+
<span className="text-accent-ink">✦</span>
|
|
37
37
|
Describe a change to the agent
|
|
38
38
|
</span>
|
|
39
39
|
<span className="rounded-sm border border-panel-border-input px-[5px] py-px font-mono text-[9px] text-panel-text-5">
|
|
@@ -50,7 +50,7 @@ function elementKindGlyph(element: DomEditSelection): { glyph: string; className
|
|
|
50
50
|
return { glyph: "◆", className: "bg-panel-media/10 text-panel-media" };
|
|
51
51
|
}
|
|
52
52
|
if (element.textFields?.length > 0) {
|
|
53
|
-
return { glyph: "T", className: "bg-panel-accent/10 text-
|
|
53
|
+
return { glyph: "T", className: "bg-panel-accent/10 text-accent-ink" };
|
|
54
54
|
}
|
|
55
55
|
return { glyph: "▦", className: "bg-panel-container/10 text-panel-container" };
|
|
56
56
|
}
|
|
@@ -74,7 +74,7 @@ function FlatMultiSelectState({
|
|
|
74
74
|
return (
|
|
75
75
|
<div className="flex flex-col gap-3 px-4 py-3">
|
|
76
76
|
<div className="flex items-center gap-3 rounded-xl border border-panel-border bg-panel-surface p-3">
|
|
77
|
-
<span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-panel-accent/10 text-
|
|
77
|
+
<span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-panel-accent/10 text-accent-ink">
|
|
78
78
|
<Layers size={16} />
|
|
79
79
|
</span>
|
|
80
80
|
<div className="min-w-0 flex-1">
|
|
@@ -40,7 +40,7 @@ export function PropertyPanelFlatFooter({
|
|
|
40
40
|
height="13"
|
|
41
41
|
viewBox="0 0 16 16"
|
|
42
42
|
fill="currentColor"
|
|
43
|
-
className="text-
|
|
43
|
+
className="text-accent-ink"
|
|
44
44
|
>
|
|
45
45
|
<path d="M8 1l1.4 4.6L14 7l-4.6 1.4L8 13l-1.4-4.6L2 7l4.6-1.4z" />
|
|
46
46
|
</svg>
|
|
@@ -57,7 +57,7 @@ export function PropertyPanelFlatFooter({
|
|
|
57
57
|
track("button", "Gesture recording");
|
|
58
58
|
onToggleRecording();
|
|
59
59
|
}}
|
|
60
|
-
className={recording ? "text-
|
|
60
|
+
className={recording ? "text-danger-ink animate-pulse" : "text-danger-ink"}
|
|
61
61
|
>
|
|
62
62
|
<svg width="14" height="14" viewBox="0 0 14 14" fill="currentColor">
|
|
63
63
|
{recording ? (
|
|
@@ -38,7 +38,7 @@ describe("PropertyPanelFlatHeader", () => {
|
|
|
38
38
|
expect(host.textContent).toContain("Mono Label");
|
|
39
39
|
expect(host.textContent).toContain(".mono-label · div");
|
|
40
40
|
const icon = host.querySelector('[data-flat-header-icon="true"]');
|
|
41
|
-
expect(icon?.className).toContain("text-
|
|
41
|
+
expect(icon?.className).toContain("text-accent-ink");
|
|
42
42
|
act(() => root.unmount());
|
|
43
43
|
});
|
|
44
44
|
|
|
@@ -4,7 +4,7 @@ import { ClipboardList, Film, Square, Type, X } from "../../icons/SystemIcons";
|
|
|
4
4
|
|
|
5
5
|
const ICON_BY_KIND = { text: Type, media: Film, other: Square } as const;
|
|
6
6
|
const ICON_COLOR_BY_KIND = {
|
|
7
|
-
text: "text-
|
|
7
|
+
text: "text-accent-ink",
|
|
8
8
|
media: "text-panel-media",
|
|
9
9
|
other: "text-panel-container",
|
|
10
10
|
} as const;
|
|
@@ -92,7 +92,7 @@ export function PropertyPanelFlatHeader({
|
|
|
92
92
|
track("button", "Copy element info");
|
|
93
93
|
onCopy();
|
|
94
94
|
}}
|
|
95
|
-
className={copied ? "text-
|
|
95
|
+
className={copied ? "text-accent-ink" : undefined}
|
|
96
96
|
>
|
|
97
97
|
<ClipboardList size={13} />
|
|
98
98
|
</button>
|
|
@@ -73,7 +73,7 @@ export const SnapToolbar = memo(function SnapToolbar() {
|
|
|
73
73
|
type="button"
|
|
74
74
|
className={`rounded-md p-1.5 transition-colors active:scale-[0.95] ${
|
|
75
75
|
motionPathArmed
|
|
76
|
-
? "bg-studio-accent/20 text-
|
|
76
|
+
? "bg-studio-accent/20 text-accent-ink"
|
|
77
77
|
: "bg-black/40 text-white/60 hover:bg-black/60 hover:text-white/80"
|
|
78
78
|
}`}
|
|
79
79
|
onClick={() => setMotionPathArmed(!motionPathArmed)}
|
|
@@ -99,7 +99,7 @@ export const SnapToolbar = memo(function SnapToolbar() {
|
|
|
99
99
|
type="button"
|
|
100
100
|
className={`rounded-md p-1.5 transition-colors active:scale-[0.95] ${
|
|
101
101
|
visible
|
|
102
|
-
? "bg-studio-accent/20 text-
|
|
102
|
+
? "bg-studio-accent/20 text-accent-ink"
|
|
103
103
|
: "bg-black/40 text-white/60 hover:bg-black/60 hover:text-white/80"
|
|
104
104
|
}`}
|
|
105
105
|
onClick={toggle}
|
|
@@ -115,7 +115,7 @@ export const SnapToolbar = memo(function SnapToolbar() {
|
|
|
115
115
|
type="button"
|
|
116
116
|
className={`rounded-md p-1.5 transition-colors active:scale-[0.95] ${
|
|
117
117
|
prefs.snapEnabled
|
|
118
|
-
? "bg-studio-accent/20 text-
|
|
118
|
+
? "bg-studio-accent/20 text-accent-ink"
|
|
119
119
|
: "bg-black/40 text-white/60 hover:bg-black/60 hover:text-white/80"
|
|
120
120
|
}`}
|
|
121
121
|
onClick={toggleSnap}
|
|
@@ -131,7 +131,7 @@ export const SnapToolbar = memo(function SnapToolbar() {
|
|
|
131
131
|
type="button"
|
|
132
132
|
className={`rounded-md p-1.5 transition-colors active:scale-[0.95] ${
|
|
133
133
|
prefs.gridVisible
|
|
134
|
-
? "bg-studio-accent/20 text-
|
|
134
|
+
? "bg-studio-accent/20 text-accent-ink"
|
|
135
135
|
: "bg-black/40 text-white/60 hover:bg-black/60 hover:text-white/80"
|
|
136
136
|
}`}
|
|
137
137
|
onClick={toggleGrid}
|
|
@@ -164,7 +164,7 @@ export const SnapToolbar = memo(function SnapToolbar() {
|
|
|
164
164
|
{gridPopoverOpen && (
|
|
165
165
|
<div
|
|
166
166
|
ref={popoverRef}
|
|
167
|
-
className="absolute right-0 top-full mt-1 rounded-lg bg-
|
|
167
|
+
className="absolute right-0 top-full mt-1 rounded-lg bg-raised border border-border p-3 shadow-xl min-w-[180px]"
|
|
168
168
|
>
|
|
169
169
|
<label className="flex items-center justify-between text-xs text-white/80 mb-2">
|
|
170
170
|
<span>Grid spacing</span>
|
|
@@ -11,12 +11,12 @@ import { defaultKeymap, history, historyKeymap } from "@codemirror/commands";
|
|
|
11
11
|
import { bracketMatching, foldGutter, indentOnInput } from "@codemirror/language";
|
|
12
12
|
import { closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete";
|
|
13
13
|
import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
|
|
14
|
-
import { oneDark } from "@codemirror/theme-one-dark";
|
|
15
14
|
import type { Extension } from "@codemirror/state";
|
|
16
15
|
import { html } from "@codemirror/lang-html";
|
|
17
16
|
import { css } from "@codemirror/lang-css";
|
|
18
17
|
import { javascript } from "@codemirror/lang-javascript";
|
|
19
18
|
import { markdown } from "@codemirror/lang-markdown";
|
|
19
|
+
import { studioCodeTheme } from "./studioCodeTheme";
|
|
20
20
|
|
|
21
21
|
// Marks a programmatic doc sync (external content push — e.g. a manual-edit
|
|
22
22
|
// commit writing the source) so the update listener doesn't mistake it for a
|
|
@@ -114,13 +114,9 @@ export const SourceEditor = memo(function SourceEditor({
|
|
|
114
114
|
highlightSelectionMatches(),
|
|
115
115
|
keymap.of([...closeBracketsKeymap, ...defaultKeymap, ...searchKeymap, ...historyKeymap]),
|
|
116
116
|
getLanguageExtension(lang),
|
|
117
|
-
|
|
117
|
+
studioCodeTheme,
|
|
118
118
|
updateListener,
|
|
119
119
|
EditorState.readOnly.of(readOnly),
|
|
120
|
-
EditorView.theme({
|
|
121
|
-
"&": { height: "100%" },
|
|
122
|
-
".cm-scroller": { overflow: "auto" },
|
|
123
|
-
}),
|
|
124
120
|
],
|
|
125
121
|
});
|
|
126
122
|
|
|
@@ -139,7 +139,7 @@ export function TimelineFxPopover({
|
|
|
139
139
|
ref={rootRef}
|
|
140
140
|
role="dialog"
|
|
141
141
|
aria-label="Effects"
|
|
142
|
-
className="z-200 flex flex-col overflow-hidden rounded-md border border-
|
|
142
|
+
className="z-200 flex flex-col overflow-hidden rounded-md border border-border bg-raised p-2 shadow-xl"
|
|
143
143
|
style={clampedStyle(anchorRect)}
|
|
144
144
|
onKeyDown={onKeyDown}
|
|
145
145
|
onPointerDown={(event) => event.stopPropagation()}
|
|
@@ -165,10 +165,10 @@ export function TimelineFxPopover({
|
|
|
165
165
|
onAuditionTracked={(id) => trackPresetAuditioned(id, { trackKind })}
|
|
166
166
|
/>
|
|
167
167
|
</div>
|
|
168
|
-
<div className="mt-2 flex shrink-0 items-center justify-between border-t border-
|
|
168
|
+
<div className="mt-2 flex shrink-0 items-center justify-between border-t border-border pt-2 text-[10px] text-text-muted">
|
|
169
169
|
<button
|
|
170
170
|
type="button"
|
|
171
|
-
className="hover:text-
|
|
171
|
+
className="hover:text-text-0"
|
|
172
172
|
onClick={() => {
|
|
173
173
|
onClose();
|
|
174
174
|
onOpenRack();
|
|
@@ -178,7 +178,7 @@ export function TimelineFxPopover({
|
|
|
178
178
|
</button>
|
|
179
179
|
<button
|
|
180
180
|
type="button"
|
|
181
|
-
className="hover:text-
|
|
181
|
+
className="hover:text-text-0"
|
|
182
182
|
onClick={() => {
|
|
183
183
|
onClose();
|
|
184
184
|
onOpenRack();
|
|
@@ -314,7 +314,7 @@ export function Transform3DCube({
|
|
|
314
314
|
aria-label="Keyframe 3D transform"
|
|
315
315
|
aria-pressed={keyframed}
|
|
316
316
|
className={`absolute left-1.5 top-1.5 rounded p-0.5 hover:bg-neutral-800 ${
|
|
317
|
-
keyframed ? "text-
|
|
317
|
+
keyframed ? "text-accent-ink" : "text-neutral-500 hover:text-neutral-200"
|
|
318
318
|
}`}
|
|
319
319
|
>
|
|
320
320
|
<svg
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
// @vitest-environment jsdom
|
|
2
2
|
import { afterEach, describe, expect, it } from "vitest";
|
|
3
|
-
import { applyStudioBoxSize, applyStudioPathOffset, readStudioBoxSize } from "./manualEditsDom";
|
|
4
|
-
import { reapplyPositionEditsAfterSeek } from "./manualEditsSeekReapply";
|
|
5
|
-
import { buildBoxSizePatches, buildPathOffsetPatches } from "./manualEditsDomPatches";
|
|
6
|
-
import { createManualOffsetDragMember, applyManualOffsetDragCommit } from "./manualOffsetDrag";
|
|
7
3
|
import { computeNextResizeAnchor } from "./domEditResizeLocal";
|
|
8
|
-
import type { PatchOperation } from "../../utils/sourcePatcher";
|
|
9
|
-
import { splitTopLevelWhitespace } from "./manualEditsStyleHelpers";
|
|
10
4
|
|
|
11
5
|
/**
|
|
12
6
|
* Center-anchored corner resize (CapCut model): the element scales about its
|
|
@@ -38,40 +32,6 @@ afterEach(() => {
|
|
|
38
32
|
document.body.innerHTML = "";
|
|
39
33
|
});
|
|
40
34
|
|
|
41
|
-
/** Apply a built PatchOperation[] to a live element, mirroring sourcePatcher's
|
|
42
|
-
* inline-style / attribute application — i.e. what the persisted source carries
|
|
43
|
-
* when it is re-parsed into the DOM on the next preview load. */
|
|
44
|
-
function applyPatchesToElement(el: HTMLElement, ops: PatchOperation[]): void {
|
|
45
|
-
for (const op of ops) {
|
|
46
|
-
if (op.type === "inline-style") {
|
|
47
|
-
if (op.value === null) el.style.removeProperty(op.property);
|
|
48
|
-
else el.style.setProperty(op.property, op.value);
|
|
49
|
-
} else if (op.type === "attribute") {
|
|
50
|
-
if (op.value === null) el.removeAttribute(op.property);
|
|
51
|
-
else el.setAttribute(op.property, op.value);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** Net translate applied to an element, resolving the studio offset var()
|
|
57
|
-
* expression to its px value so we compare the actually-rendered translation. */
|
|
58
|
-
function resolvedTranslatePx(el: HTMLElement): { x: number; y: number } {
|
|
59
|
-
const raw = el.style.getPropertyValue("translate").trim();
|
|
60
|
-
if (!raw || raw === "none") return { x: 0, y: 0 };
|
|
61
|
-
const vx = Number.parseFloat(el.style.getPropertyValue("--hf-studio-offset-x")) || 0;
|
|
62
|
-
const vy = Number.parseFloat(el.style.getPropertyValue("--hf-studio-offset-y")) || 0;
|
|
63
|
-
const parts = splitTopLevelWhitespace(raw);
|
|
64
|
-
const parseAxis = (part: string, varVal: number): number => {
|
|
65
|
-
if (part && part.includes("--hf-studio-offset")) return varVal;
|
|
66
|
-
const n = Number.parseFloat(part);
|
|
67
|
-
return Number.isFinite(n) ? n : 0;
|
|
68
|
-
};
|
|
69
|
-
return {
|
|
70
|
-
x: parseAxis(parts[0] ?? "", vx),
|
|
71
|
-
y: parseAxis(parts[1] ?? "", vy),
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
35
|
describe("center-anchored corner resize — no shift after release", () => {
|
|
76
36
|
it("the per-frame center anchor converges (does NOT oscillate) — the release-shift root cause", () => {
|
|
77
37
|
// Model the pointermove anchor loop that pins the element's CENTER. The physical
|
|
@@ -157,48 +117,4 @@ describe("center-anchored corner resize — no shift after release", () => {
|
|
|
157
117
|
}
|
|
158
118
|
}
|
|
159
119
|
});
|
|
160
|
-
|
|
161
|
-
it("net translate after persist+reload equals the committed anchor offset (non-GSAP)", () => {
|
|
162
|
-
// The committed offset flows through the real apply → persist → reload chain
|
|
163
|
-
// unchanged (this hop was proved clean; the shift is upstream in the anchor
|
|
164
|
-
// loop above, not in persistence).
|
|
165
|
-
const el = document.createElement("div");
|
|
166
|
-
el.style.setProperty("width", "200px");
|
|
167
|
-
el.style.setProperty("height", "100px");
|
|
168
|
-
document.body.appendChild(el);
|
|
169
|
-
|
|
170
|
-
const anchorDx = -30;
|
|
171
|
-
const anchorDy = -18;
|
|
172
|
-
const finalSize = { width: 240, height: 130 };
|
|
173
|
-
|
|
174
|
-
applyStudioBoxSize(el, finalSize);
|
|
175
|
-
const memberResult = createManualOffsetDragMember({
|
|
176
|
-
key: "k",
|
|
177
|
-
selection: { element: el } as never,
|
|
178
|
-
element: el,
|
|
179
|
-
rect: { left: 0, top: 0, width: 240, height: 130, editScaleX: 1, editScaleY: 1 },
|
|
180
|
-
});
|
|
181
|
-
expect(memberResult.ok).toBe(true);
|
|
182
|
-
if (!memberResult.ok) return;
|
|
183
|
-
|
|
184
|
-
const finalOffset = applyManualOffsetDragCommit(memberResult.member, anchorDx, anchorDy);
|
|
185
|
-
|
|
186
|
-
applyStudioBoxSize(el, finalSize);
|
|
187
|
-
const patches = buildBoxSizePatches(el);
|
|
188
|
-
applyStudioPathOffset(el, finalOffset);
|
|
189
|
-
patches.push(...buildPathOffsetPatches(el));
|
|
190
|
-
|
|
191
|
-
expect(resolvedTranslatePx(el)).toEqual({ x: anchorDx, y: anchorDy });
|
|
192
|
-
|
|
193
|
-
// Persist → fresh element re-parsed from source → reload re-stamp.
|
|
194
|
-
const reloaded = document.createElement("div");
|
|
195
|
-
reloaded.style.setProperty("width", "200px");
|
|
196
|
-
reloaded.style.setProperty("height", "100px");
|
|
197
|
-
document.body.appendChild(reloaded);
|
|
198
|
-
applyPatchesToElement(reloaded, patches);
|
|
199
|
-
reapplyPositionEditsAfterSeek(reloaded.ownerDocument);
|
|
200
|
-
|
|
201
|
-
expect(resolvedTranslatePx(reloaded)).toEqual({ x: anchorDx, y: anchorDy });
|
|
202
|
-
expect(readStudioBoxSize(reloaded)).toEqual(finalSize);
|
|
203
|
-
});
|
|
204
120
|
});
|