@hyperframes/studio 0.8.50 → 0.8.53-desktop.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{hyperframes-player-CxxDxHTW.js → hyperframes-player-PTEnAHhg.js} +3 -3
- package/dist/assets/{index-DUAIWxkI.js → index--myXp8ui.js} +1 -1
- package/dist/assets/{index-Dt3qBI-9.js → index-BjslOS9g.js} +1 -1
- package/dist/assets/index-BnXZuIJD.css +1 -0
- package/dist/assets/index-CNyviK_l.js +390 -0
- package/dist/index.css +29 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +825 -24
- package/dist/index.html +2 -2
- package/dist/index.js +8911 -9853
- package/dist/index.js.map +1 -1
- package/dist/styles/tailwind-preset.js.map +1 -1
- package/package.json +13 -12
- package/src/App.tsx +145 -163
- package/src/captions/components/CaptionAnimationPanel.tsx +4 -4
- package/src/captions/components/CaptionOverlay.sender.test.tsx +52 -0
- package/src/captions/components/CaptionOverlay.tsx +8 -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.selectionSync.test.tsx +9 -4
- package/src/components/EditorShell.tsx +59 -62
- package/src/components/ExternalFileConflictBanner.tsx +16 -16
- package/src/components/LintModal.tsx +6 -16
- package/src/components/MediaPreview.tsx +2 -2
- package/src/components/RightPanelTabs.test.tsx +151 -0
- package/src/components/RightPanelTabs.tsx +59 -0
- package/src/components/SaveQueuePausedBanner.tsx +2 -2
- package/src/components/StudioHeader.dom.test.tsx +181 -0
- package/src/components/StudioHeader.test.ts +0 -12
- package/src/components/StudioHeader.tsx +118 -221
- package/src/components/StudioLeftPanels.tsx +202 -0
- package/src/components/StudioOverlays.tsx +1 -1
- package/src/components/{StudioRightPanel.tsx → StudioRightPanels.tsx} +57 -194
- package/src/components/{StudioRightPanel.types.ts → StudioRightPanels.types.ts} +4 -3
- package/src/components/StudioToast.tsx +2 -2
- package/src/components/TimelineHistoryButtons.dom.test.tsx +117 -0
- package/src/components/TimelineHistoryButtons.tsx +54 -0
- package/src/components/TimelineToolbar.test.tsx +8 -0
- package/src/components/TimelineToolbar.tsx +8 -12
- package/src/components/dock/Dock.test.tsx +274 -0
- package/src/components/dock/Dock.tsx +291 -0
- package/src/components/dock/dock.css +28 -0
- package/src/components/dock/dockAccessibility.test.ts +166 -0
- package/src/components/dock/dockAccessibility.ts +98 -0
- package/src/components/dock/dockLayout.test.ts +134 -0
- package/src/components/dock/dockLayout.ts +105 -0
- package/src/components/dock/dockLayoutSchema.ts +66 -0
- package/src/components/dock/dockLayoutStore.test.ts +28 -0
- package/src/components/dock/dockLayoutStore.ts +108 -0
- package/src/components/dock/dockTestHarness.ts +11 -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.test.ts +97 -1
- package/src/components/editor/LayersPanel.tsx +7 -5
- package/src/components/editor/MarqueeOverlay.tsx +1 -1
- package/src/components/editor/OffCanvasIndicators.tsx +1 -1
- package/src/components/editor/PreviewGuides.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.test.tsx +0 -3
- package/src/components/editor/SnapToolbar.tsx +2 -2
- package/src/components/editor/TimelineFxPopover.tsx +1 -1
- package/src/components/editor/TopologyLens.test.tsx +28 -0
- package/src/components/editor/TopologyLens.tsx +7 -5
- 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/editor/useColorGradingController.test.ts +38 -0
- package/src/components/editor/useColorGradingController.ts +9 -1
- package/src/components/editor/useColorGradingPreviews.test.ts +76 -0
- package/src/components/editor/useColorGradingPreviews.ts +11 -1
- package/src/components/editor/usePreviewCompositionRect.test.tsx +95 -0
- package/src/components/editor/usePreviewCompositionRect.ts +21 -0
- 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/NLEContext.tsx +0 -43
- package/src/components/nle/NLEPreview.tsx +3 -3
- package/src/components/nle/PreviewOverlays.tsx +6 -6
- package/src/components/nle/PreviewPane.tsx +3 -3
- package/src/components/nle/TimelinePane.tsx +53 -67
- package/src/components/panels/SlideshowPanel.tsx +3 -3
- 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 +4 -4
- package/src/components/panels/VariablesValueControls.tsx +2 -2
- package/src/components/pointerTargetSize.test.tsx +8 -0
- package/src/components/renders/FfmpegRequiredNotice.tsx +5 -5
- package/src/components/renders/RenderQueue.test.tsx +7 -0
- package/src/components/renders/RenderQueue.tsx +7 -7
- package/src/components/renders/RenderQueueItem.tsx +6 -6
- package/src/components/renders/RenderQueuePanel.tsx +1 -1
- 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/{LeftSidebar.masterComposition.test.tsx → CompositionsPanel.masterComposition.test.tsx} +6 -9
- package/src/components/sidebar/CompositionsPanel.tsx +15 -0
- package/src/components/sidebar/CompositionsTab.tsx +5 -5
- package/src/components/sidebar/GlobalAssetsView.tsx +1 -1
- package/src/components/sidebar/PromptPreviewModal.tsx +3 -3
- package/src/components/sidebar/SidebarLintButton.test.tsx +53 -0
- package/src/components/sidebar/SidebarLintButton.tsx +50 -0
- package/src/components/timelineToolbarStyles.ts +5 -0
- package/src/components/ui/Button.test.tsx +252 -0
- package/src/components/ui/Button.tsx +89 -126
- package/src/components/ui/IconButton.tsx +38 -0
- package/src/components/ui/Input.tsx +115 -0
- package/src/components/ui/Menu.test.tsx +374 -0
- package/src/components/ui/Menu.tsx +213 -0
- package/src/components/ui/NumberField.tsx +122 -0
- package/src/components/ui/Popover.tsx +74 -0
- package/src/components/ui/SearchInput.tsx +2 -2
- package/src/components/ui/Select.tsx +100 -0
- package/src/components/ui/Slider.tsx +153 -0
- package/src/components/ui/Tabs.test.tsx +98 -0
- package/src/components/ui/Tabs.tsx +71 -0
- package/src/components/ui/Toggle.tsx +63 -0
- package/src/components/ui/Tooltip.test.tsx +110 -0
- package/src/components/ui/Tooltip.tsx +38 -112
- package/src/components/ui/cn.test.ts +30 -0
- package/src/components/ui/cn.ts +25 -0
- package/src/components/ui/index.ts +29 -2
- package/src/components/ui/mountHost.testHelpers.ts +24 -0
- package/src/components/ui/valueControls.test.tsx +524 -0
- package/src/contexts/PanelLayoutContext.tsx +3 -57
- package/src/hooks/appHotkeysDispatch.ts +3 -4
- package/src/hooks/timelineEditPermission.ts +31 -0
- package/src/hooks/useAppHotkeys.previewForwarding.test.tsx +0 -2
- package/src/hooks/useAppHotkeys.test.ts +0 -1
- package/src/hooks/useAppHotkeys.textEditing.test.tsx +0 -3
- package/src/hooks/useAppHotkeys.ts +21 -84
- package/src/hooks/useCaptionDetection.sender.test.tsx +46 -0
- package/src/hooks/useCaptionDetection.ts +3 -1
- package/src/hooks/useDomEditPreviewSync.ts +4 -6
- package/src/hooks/useDomEditSession.ts +0 -7
- package/src/hooks/useDomEditWiring.ts +4 -9
- package/src/hooks/useEditHistoryActions.test.tsx +90 -0
- package/src/hooks/useEditHistoryActions.ts +106 -0
- package/src/hooks/useFileManager.ts +21 -123
- package/src/hooks/useLintModal.test.tsx +65 -0
- package/src/hooks/useLintModal.ts +3 -0
- package/src/hooks/usePanelLayout.test.ts +108 -296
- package/src/hooks/usePanelLayout.ts +50 -255
- package/src/hooks/usePersistentEditHistory.ts +1 -1
- package/src/hooks/useProjectFileWriter.ts +150 -0
- package/src/hooks/useRightPanelIntents.test.ts +181 -0
- package/src/hooks/useRightPanelIntents.ts +63 -0
- package/src/hooks/useSdkSession.lifecycle.test.tsx +49 -4
- package/src/hooks/useSdkSession.ts +43 -10
- package/src/hooks/useServerConnection.hashProject.test.tsx +94 -0
- package/src/hooks/useServerConnection.ts +43 -10
- package/src/hooks/useSlideshowTabState.test.ts +9 -31
- package/src/hooks/useSlideshowTabState.ts +6 -19
- package/src/hooks/useStudioContextValue.test.ts +6 -18
- package/src/hooks/useStudioContextValue.ts +5 -26
- package/src/hooks/useTimelineAssetDropOps.test.tsx +41 -1
- package/src/hooks/useTimelineAssetDropOps.ts +52 -11
- package/src/hooks/useTimelineDeleteOps.test.tsx +111 -0
- package/src/hooks/useTimelineEditing.test.tsx +322 -3
- package/src/hooks/useTimelineEditing.ts +103 -16
- package/src/hooks/useTimelineEditingTypes.ts +3 -0
- package/src/hooks/useTrackPendingTimelineEdit.test.tsx +62 -0
- package/src/hooks/useTrackPendingTimelineEdit.ts +22 -0
- package/src/hostMount.test.tsx +222 -0
- package/src/index.ts +87 -0
- package/src/main.tsx +9 -3
- package/src/player/components/AudioWaveform.tsx +1 -1
- package/src/player/components/AutomationSelectionMenu.tsx +3 -3
- 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 +3 -2
- package/src/player/components/TimelineCanvas.tsx +21 -14
- package/src/player/components/TimelineClip.tsx +0 -1
- 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.test.tsx +26 -5
- package/src/player/components/TimelineTrackHeader.tsx +2 -2
- 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/timelineTheme.test.ts +4 -4
- package/src/player/components/timelineTheme.ts +30 -30
- 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/player/lib/playbackShortcuts.ts +2 -0
- package/src/player/lib/timelinePerformanceDiagnostics.test.ts +3 -2
- package/src/player/lib/timelinePerformanceDiagnostics.ts +1 -1
- package/src/player/store/previewIframeStore.notify.test.tsx +75 -0
- package/src/player/store/previewIframeStore.test.tsx +29 -0
- package/src/player/store/previewIframeStore.ts +25 -0
- package/src/renderQueueExports.test.tsx +58 -0
- package/src/styles/classCandidates.test.ts +105 -0
- package/src/styles/classCandidates.ts +281 -0
- package/src/styles/hex-baseline.json +84 -0
- package/src/styles/hexRatchet.test.ts +201 -0
- 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/styleSources.test.ts +20 -0
- package/src/styles/styleSources.ts +47 -0
- package/src/styles/tailwind-preset.shared.js +3 -0
- package/src/styles/theme.css +270 -0
- package/src/styles/theme.test.ts +183 -0
- package/src/styles/tokenGate.test.ts +109 -0
- package/src/utils/sourcePatcher.ts +1 -1
- package/src/utils/studioHelpers.test.ts +18 -0
- package/src/utils/studioHelpers.ts +11 -8
- package/src/utils/studioUiPreferences.test.ts +10 -16
- package/src/utils/studioUiPreferences.ts +7 -16
- package/src/utils/timelineDropTrackInsert.test.ts +134 -0
- package/src/utils/timelineDropTrackInsert.ts +83 -0
- package/src/utils/typingTarget.ts +4 -0
- package/dist/assets/index-n2ml4tUn.js +0 -442
- package/dist/assets/index-pO6p0H27.css +0 -1
- package/src/components/PanelTabButton.tsx +0 -31
- package/src/components/StudioLeftSidebar.tsx +0 -204
- package/src/components/nle/TimelineResizeDivider.tsx +0 -101
- package/src/components/sidebar/LeftSidebar.storage.test.ts +0 -28
- package/src/components/sidebar/LeftSidebar.tsx +0 -334
- 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/useInspectorSplitResize.ts +0 -51
- package/src/hooks/useProjectSignaturePoll.test.tsx +0 -135
- package/src/hooks/useProjectSignaturePoll.ts +0 -68
- package/src/hooks/useStoryboard.ts +0 -99
- package/src/utils/fitPanels.test.ts +0 -164
- package/src/utils/fitPanels.ts +0 -151
|
@@ -1,12 +1,55 @@
|
|
|
1
1
|
// @vitest-environment happy-dom
|
|
2
2
|
|
|
3
|
+
import { createElement, act } from "react";
|
|
4
|
+
import { createRoot, type Root } from "react-dom/client";
|
|
3
5
|
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
|
|
4
6
|
import { Window } from "happy-dom";
|
|
7
|
+
import { usePreviewIframeStore } from "../../player/store/previewIframeStore";
|
|
5
8
|
import type { DomEditLayerItem } from "./domEditingTypes";
|
|
6
|
-
import { createRafThrottle, sortLayersByZIndex } from "./LayersPanel";
|
|
9
|
+
import { createRafThrottle, LayersPanel, sortLayersByZIndex } from "./LayersPanel";
|
|
7
10
|
import { isLayerDraggable } from "./useLayerDrag";
|
|
8
11
|
import { liveTime } from "../../player";
|
|
9
12
|
|
|
13
|
+
(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
|
14
|
+
|
|
15
|
+
const mocks = vi.hoisted(() => ({
|
|
16
|
+
previewIframeRef: { current: null } as { current: unknown },
|
|
17
|
+
collect: vi.fn(),
|
|
18
|
+
setActiveGroupElement: vi.fn(),
|
|
19
|
+
showToast: vi.fn(),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
vi.mock("../../contexts/StudioContext", () => ({
|
|
23
|
+
useStudioShellContext: () => ({
|
|
24
|
+
previewIframeRef: mocks.previewIframeRef,
|
|
25
|
+
activeCompPath: "index.html",
|
|
26
|
+
showToast: mocks.showToast,
|
|
27
|
+
}),
|
|
28
|
+
useStudioPlaybackContext: () => ({
|
|
29
|
+
refreshKey: 0,
|
|
30
|
+
compositionLoading: true,
|
|
31
|
+
timelineElements: [],
|
|
32
|
+
isPlaying: false,
|
|
33
|
+
}),
|
|
34
|
+
}));
|
|
35
|
+
vi.mock("../../contexts/DomEditContext", () => ({
|
|
36
|
+
useDomEditContext: () => ({
|
|
37
|
+
domEditSelection: null,
|
|
38
|
+
activeGroupElement: null,
|
|
39
|
+
applyDomSelection: vi.fn(),
|
|
40
|
+
updateDomEditHoverSelection: vi.fn(),
|
|
41
|
+
handleDomZIndexReorderCommit: vi.fn(),
|
|
42
|
+
setActiveGroupElement: mocks.setActiveGroupElement,
|
|
43
|
+
}),
|
|
44
|
+
}));
|
|
45
|
+
vi.mock("../nle/useCanvasZOrderTimelineMirror", () => ({
|
|
46
|
+
useLayerReorderTimelineMirror: () => vi.fn(),
|
|
47
|
+
}));
|
|
48
|
+
vi.mock("./domEditing", async (importOriginal) => ({
|
|
49
|
+
...(await importOriginal<typeof import("./domEditing")>()),
|
|
50
|
+
collectDomEditLayerItems: mocks.collect,
|
|
51
|
+
}));
|
|
52
|
+
|
|
10
53
|
function makeLayer(
|
|
11
54
|
overrides: Partial<DomEditLayerItem> & { zIndex?: string; locked?: boolean },
|
|
12
55
|
): DomEditLayerItem {
|
|
@@ -197,3 +240,56 @@ describe("liveTime subscribe / unsubscribe (LayersPanel scrub contract)", () =>
|
|
|
197
240
|
unsubscribe();
|
|
198
241
|
});
|
|
199
242
|
});
|
|
243
|
+
|
|
244
|
+
describe("LayersPanel preview promotion", () => {
|
|
245
|
+
let root: Root;
|
|
246
|
+
let host: HTMLDivElement;
|
|
247
|
+
|
|
248
|
+
function makeIframe(compId: string): HTMLIFrameElement {
|
|
249
|
+
const iframe = document.createElement("iframe");
|
|
250
|
+
const doc = new Window().document;
|
|
251
|
+
doc.body.innerHTML = `<div data-composition-id="${compId}"></div>`;
|
|
252
|
+
Object.defineProperty(iframe, "contentDocument", { value: doc });
|
|
253
|
+
return iframe;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const collectedRootIds = () =>
|
|
257
|
+
mocks.collect.mock.calls.map((call) => (call[0] as HTMLElement).dataset.compositionId);
|
|
258
|
+
|
|
259
|
+
beforeEach(() => {
|
|
260
|
+
mocks.collect.mockReset().mockReturnValue([]);
|
|
261
|
+
host = document.createElement("div");
|
|
262
|
+
document.body.append(host);
|
|
263
|
+
root = createRoot(host);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
afterEach(() => {
|
|
267
|
+
act(() => root.unmount());
|
|
268
|
+
host.remove();
|
|
269
|
+
usePreviewIframeStore.setState({ iframe: null });
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it("recollects layers from the promoted document's load, not the replaced one", () => {
|
|
273
|
+
const a = makeIframe("a");
|
|
274
|
+
const b = makeIframe("b");
|
|
275
|
+
mocks.previewIframeRef.current = a;
|
|
276
|
+
usePreviewIframeStore.setState({ iframe: a });
|
|
277
|
+
act(() => root.render(createElement(LayersPanel)));
|
|
278
|
+
|
|
279
|
+
act(() => {
|
|
280
|
+
mocks.previewIframeRef.current = b;
|
|
281
|
+
usePreviewIframeStore.getState().setIframe(b);
|
|
282
|
+
});
|
|
283
|
+
mocks.collect.mockClear();
|
|
284
|
+
|
|
285
|
+
act(() => {
|
|
286
|
+
a.dispatchEvent(new Event("load"));
|
|
287
|
+
});
|
|
288
|
+
expect(mocks.collect).not.toHaveBeenCalled();
|
|
289
|
+
|
|
290
|
+
act(() => {
|
|
291
|
+
b.dispatchEvent(new Event("load"));
|
|
292
|
+
});
|
|
293
|
+
expect(collectedRootIds()).toEqual(["b"]);
|
|
294
|
+
});
|
|
295
|
+
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useLivePreviewIframe } from "../../player/store/previewIframeStore";
|
|
1
2
|
import { memo, useState, useCallback, useEffect, useRef } from "react";
|
|
2
3
|
import {
|
|
3
4
|
collectDomEditLayerItems,
|
|
@@ -122,6 +123,7 @@ export const LayersPanel = memo(function LayersPanel() {
|
|
|
122
123
|
|
|
123
124
|
const isMasterView = !activeCompPath || activeCompPath === "index.html";
|
|
124
125
|
|
|
126
|
+
const livePreviewIframe = useLivePreviewIframe();
|
|
125
127
|
const collectLayers = useCallback(() => {
|
|
126
128
|
const iframe = previewIframeRef.current;
|
|
127
129
|
if (!iframe) return;
|
|
@@ -152,7 +154,7 @@ export const LayersPanel = memo(function LayersPanel() {
|
|
|
152
154
|
|
|
153
155
|
useEffect(() => {
|
|
154
156
|
collectLayers();
|
|
155
|
-
}, [collectLayers, refreshKey, zEditVersion]);
|
|
157
|
+
}, [collectLayers, refreshKey, zEditVersion, livePreviewIframe]);
|
|
156
158
|
|
|
157
159
|
useEffect(() => {
|
|
158
160
|
const iframe = previewIframeRef.current;
|
|
@@ -163,7 +165,7 @@ export const LayersPanel = memo(function LayersPanel() {
|
|
|
163
165
|
};
|
|
164
166
|
iframe.addEventListener("load", handleLoad);
|
|
165
167
|
return () => iframe.removeEventListener("load", handleLoad);
|
|
166
|
-
}, [previewIframeRef, collectLayers]);
|
|
168
|
+
}, [previewIframeRef, livePreviewIframe, collectLayers]);
|
|
167
169
|
|
|
168
170
|
useEffect(() => {
|
|
169
171
|
if (!compositionLoading) {
|
|
@@ -485,7 +487,7 @@ export const LayersPanel = memo(function LayersPanel() {
|
|
|
485
487
|
onClick={(e) => toggleCollapse(layer.key, e)}
|
|
486
488
|
aria-expanded={!isCollapsed}
|
|
487
489
|
aria-label={isCollapsed ? "Expand children" : "Collapse children"}
|
|
488
|
-
className="relative flex h-4 w-4
|
|
490
|
+
className="relative flex h-4 w-4 shrink-0 items-center justify-center rounded-sm text-neutral-500 hover:text-neutral-300 before:absolute before:-inset-1.5 before:content-['']"
|
|
489
491
|
>
|
|
490
492
|
<svg
|
|
491
493
|
width="8"
|
|
@@ -498,10 +500,10 @@ export const LayersPanel = memo(function LayersPanel() {
|
|
|
498
500
|
</svg>
|
|
499
501
|
</button>
|
|
500
502
|
) : (
|
|
501
|
-
<span className="w-4
|
|
503
|
+
<span className="w-4 shrink-0" />
|
|
502
504
|
)}
|
|
503
505
|
<span
|
|
504
|
-
className={`flex h-5 w-5
|
|
506
|
+
className={`flex h-5 w-5 shrink-0 items-center justify-center rounded text-[8px] font-bold uppercase ${
|
|
505
507
|
selected
|
|
506
508
|
? "bg-panel-accent/18 text-panel-accent"
|
|
507
509
|
: isCompHost
|
|
@@ -19,7 +19,7 @@ export function MarqueeOverlay({ candidateRects, marqueeRect }: MarqueeOverlayPr
|
|
|
19
19
|
<div
|
|
20
20
|
key={i}
|
|
21
21
|
aria-hidden="true"
|
|
22
|
-
className="pointer-events-none absolute rounded-
|
|
22
|
+
className="pointer-events-none absolute rounded-xs border border-studio-accent bg-studio-accent/5"
|
|
23
23
|
style={{ left: r.left, top: r.top, width: r.width, height: r.height }}
|
|
24
24
|
/>
|
|
25
25
|
))}
|
|
@@ -167,7 +167,7 @@ export function ChildRectOutlines({
|
|
|
167
167
|
{rects.map((rect, index) => (
|
|
168
168
|
<div
|
|
169
169
|
key={index}
|
|
170
|
-
className="pointer-events-none absolute border border-dashed border-white/20 rounded-
|
|
170
|
+
className="pointer-events-none absolute border border-dashed border-white/20 rounded-xs"
|
|
171
171
|
style={{
|
|
172
172
|
left: rect.left,
|
|
173
173
|
top: rect.top,
|
|
@@ -8,7 +8,7 @@ interface PreviewGuidesProps {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
const TICKS = Array.from({ length: 11 }, (_, i) => i * 10);
|
|
11
|
-
const INK = "
|
|
11
|
+
const INK = "color-mix(in srgb, white 70%, transparent)";
|
|
12
12
|
|
|
13
13
|
/** Ruler and safe-margin boxes drawn over the preview pane, never inside the composition. */
|
|
14
14
|
export const PreviewGuides = memo(function PreviewGuides({ iframeRef }: PreviewGuidesProps) {
|
|
@@ -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 bg-studio-accent/20 px-1 py-px font-mono text-[8px] font-medium text-studio-accent"
|
|
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-studio-accent"
|
|
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 bg-neutral-800/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-studio-accent hover:opacity-100"
|
|
100
|
+
className="absolute -top-2 right-1.5 z-10 inline-flex items-center gap-1 rounded-sm bg-neutral-800/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-studio-accent hover:opacity-100"
|
|
101
101
|
>
|
|
102
102
|
◇ var
|
|
103
103
|
</button>
|
|
@@ -18,7 +18,7 @@ function FlatEmptyState() {
|
|
|
18
18
|
</svg>
|
|
19
19
|
</span>
|
|
20
20
|
<div className="text-[13px] font-semibold text-panel-text-0">Nothing selected</div>
|
|
21
|
-
<div className="max-w-[250px] text-[11px] leading-
|
|
21
|
+
<div className="max-w-[250px] text-[11px] leading-normal text-panel-text-3">
|
|
22
22
|
Click any element on the canvas to edit it, or drag to select several.
|
|
23
23
|
</div>
|
|
24
24
|
<div className="mt-2 flex w-full flex-col gap-1.5">
|
|
@@ -27,7 +27,7 @@ function FlatEmptyState() {
|
|
|
27
27
|
<span className="text-panel-danger">●</span>
|
|
28
28
|
Record a gesture
|
|
29
29
|
</span>
|
|
30
|
-
<span className="rounded border border-panel-border-input px-[5px] py-px font-mono text-[9px] text-panel-text-5">
|
|
30
|
+
<span className="rounded-sm border border-panel-border-input px-[5px] py-px font-mono text-[9px] text-panel-text-5">
|
|
31
31
|
R
|
|
32
32
|
</span>
|
|
33
33
|
</span>
|
|
@@ -36,7 +36,7 @@ function FlatEmptyState() {
|
|
|
36
36
|
<span className="text-panel-accent">✦</span>
|
|
37
37
|
Describe a change to the agent
|
|
38
38
|
</span>
|
|
39
|
-
<span className="rounded border border-panel-border-input px-[5px] py-px font-mono text-[9px] text-panel-text-5">
|
|
39
|
+
<span className="rounded-sm border border-panel-border-input px-[5px] py-px font-mono text-[9px] text-panel-text-5">
|
|
40
40
|
⌘K
|
|
41
41
|
</span>
|
|
42
42
|
</span>
|
|
@@ -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
|
|
77
|
+
<span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-panel-accent/10 text-panel-accent">
|
|
78
78
|
<Layers size={16} />
|
|
79
79
|
</span>
|
|
80
80
|
<div className="min-w-0 flex-1">
|
|
@@ -90,7 +90,7 @@ function FlatMultiSelectState({
|
|
|
90
90
|
data-flat-multiselect-clear="true"
|
|
91
91
|
aria-label="Clear selection"
|
|
92
92
|
onClick={onClearSelection}
|
|
93
|
-
className="flex h-[26px] w-[26px]
|
|
93
|
+
className="flex h-[26px] w-[26px] shrink-0 items-center justify-center text-panel-text-3"
|
|
94
94
|
>
|
|
95
95
|
<svg
|
|
96
96
|
width="13"
|
|
@@ -113,14 +113,14 @@ function FlatMultiSelectState({
|
|
|
113
113
|
className="flex items-center gap-2 rounded-lg border border-panel-border bg-panel-bg px-2.5 py-[7px]"
|
|
114
114
|
>
|
|
115
115
|
<span
|
|
116
|
-
className={`flex h-[18px] w-[18px]
|
|
116
|
+
className={`flex h-[18px] w-[18px] shrink-0 items-center justify-center rounded-sm text-[9px] font-bold ${className}`}
|
|
117
117
|
>
|
|
118
118
|
{glyph}
|
|
119
119
|
</span>
|
|
120
120
|
<span className="min-w-0 flex-1 truncate text-[11px] text-panel-text-1">
|
|
121
121
|
{element.label}
|
|
122
122
|
</span>
|
|
123
|
-
<span className="
|
|
123
|
+
<span className="shrink-0 font-mono text-[9px] text-panel-text-4">
|
|
124
124
|
{element.id ? `#${element.id}` : element.selector}
|
|
125
125
|
</span>
|
|
126
126
|
</span>
|
|
@@ -41,13 +41,13 @@ export function PropertyPanelFlatHeader({
|
|
|
41
41
|
<Icon
|
|
42
42
|
size={15}
|
|
43
43
|
data-flat-header-icon="true"
|
|
44
|
-
className={`
|
|
44
|
+
className={`shrink-0 ${ICON_COLOR_BY_KIND[elementKind]}`}
|
|
45
45
|
/>
|
|
46
46
|
<div className="flex min-w-0 flex-1 items-baseline gap-2">
|
|
47
47
|
<span className="truncate text-[13px] font-semibold text-panel-text-0">{name}</span>
|
|
48
48
|
<span className="truncate font-mono text-[10px] text-panel-text-4">{meta}</span>
|
|
49
49
|
</div>
|
|
50
|
-
<div className="flex
|
|
50
|
+
<div className="flex shrink-0 items-center gap-2.5 text-panel-text-3">
|
|
51
51
|
{showUngroup && (
|
|
52
52
|
<button
|
|
53
53
|
type="button"
|
|
@@ -5,7 +5,6 @@ import { createRoot } from "react-dom/client";
|
|
|
5
5
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
6
6
|
import { useAppHotkeys } from "../../hooks/useAppHotkeys";
|
|
7
7
|
import { usePlayerStore } from "../../player/store/playerStore";
|
|
8
|
-
import type { LeftSidebarHandle } from "../sidebar/LeftSidebar";
|
|
9
8
|
import type { DomEditSelection } from "./domEditing";
|
|
10
9
|
import { SnapToolbar } from "./SnapToolbar";
|
|
11
10
|
import { usePreviewGuidesStore } from "./previewGuidesStore";
|
|
@@ -31,7 +30,6 @@ function renderToolbar(onSnapChange = vi.fn()) {
|
|
|
31
30
|
function AppHotkeyHarness() {
|
|
32
31
|
const domEditSelectionRef = useRef<DomEditSelection | null>(null);
|
|
33
32
|
const clearDomSelectionRef = useRef<() => void>(() => undefined);
|
|
34
|
-
const leftSidebarRef = useRef<LeftSidebarHandle | null>(null);
|
|
35
33
|
|
|
36
34
|
useAppHotkeys({
|
|
37
35
|
handleTimelineElementsDelete: vi.fn(async () => {}),
|
|
@@ -50,7 +48,6 @@ function AppHotkeyHarness() {
|
|
|
50
48
|
showToast: vi.fn(),
|
|
51
49
|
syncHistoryPreviewAfterApply: vi.fn(async () => undefined),
|
|
52
50
|
waitForPendingDomEditSaves: vi.fn(async () => undefined),
|
|
53
|
-
leftSidebarRef,
|
|
54
51
|
handleCopy: vi.fn(() => false),
|
|
55
52
|
handlePaste: vi.fn(async () => undefined),
|
|
56
53
|
handleCut: vi.fn(async () => false),
|
|
@@ -179,7 +179,7 @@ export const SnapToolbar = memo(function SnapToolbar({ onSnapChange }: SnapToolb
|
|
|
179
179
|
</button>
|
|
180
180
|
<button
|
|
181
181
|
type="button"
|
|
182
|
-
className="absolute -right-0.5 -bottom-0.5 rounded p-0.5 text-white/50 hover:text-white/90 bg-black/50"
|
|
182
|
+
className="absolute -right-0.5 -bottom-0.5 rounded-sm p-0.5 text-white/50 hover:text-white/90 bg-black/50"
|
|
183
183
|
onClick={() => setGridPopoverOpen((v) => !v)}
|
|
184
184
|
title="Grid options"
|
|
185
185
|
aria-label="Grid options"
|
|
@@ -209,7 +209,7 @@ export const SnapToolbar = memo(function SnapToolbar({ onSnapChange }: SnapToolb
|
|
|
209
209
|
updatePrefs({ gridSpacing: val });
|
|
210
210
|
}
|
|
211
211
|
}}
|
|
212
|
-
className="w-16 rounded bg-neutral-900 border border-neutral-600 px-1.5 py-0.5 text-xs text-white text-right tabular-nums outline-
|
|
212
|
+
className="w-16 rounded-sm bg-neutral-900 border border-neutral-600 px-1.5 py-0.5 text-xs text-white text-right tabular-nums outline-hidden focus:border-studio-accent"
|
|
213
213
|
/>
|
|
214
214
|
</label>
|
|
215
215
|
<label className="flex items-center gap-2 text-xs text-white/80 cursor-pointer">
|
|
@@ -139,7 +139,7 @@ export function TimelineFxPopover({
|
|
|
139
139
|
ref={rootRef}
|
|
140
140
|
role="dialog"
|
|
141
141
|
aria-label="Effects"
|
|
142
|
-
className="z-
|
|
142
|
+
className="z-200 flex flex-col overflow-hidden rounded-md border border-white/10 bg-[#1b1b1f] p-2 shadow-xl"
|
|
143
143
|
style={clampedStyle(anchorRect)}
|
|
144
144
|
onKeyDown={onKeyDown}
|
|
145
145
|
onPointerDown={(event) => event.stopPropagation()}
|
|
@@ -4,6 +4,7 @@ import React, { act } from "react";
|
|
|
4
4
|
import { createRoot, type Root } from "react-dom/client";
|
|
5
5
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
6
|
import { studioEditLifecycle, type StudioWriteResult } from "../../webmcp/writeCoordinator";
|
|
7
|
+
import { usePreviewIframeStore } from "../../player/store/previewIframeStore";
|
|
7
8
|
import { TopologyLens } from "./TopologyLens";
|
|
8
9
|
|
|
9
10
|
(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
|
@@ -291,4 +292,31 @@ describe("TopologyLens", () => {
|
|
|
291
292
|
expect(vi.getTimerCount()).toBe(0);
|
|
292
293
|
expect(studioEditLifecycle.getSnapshot()).toEqual({ phase: "idle" });
|
|
293
294
|
});
|
|
295
|
+
|
|
296
|
+
it("dismisses on the promoted iframe's load, not the replaced one", () => {
|
|
297
|
+
const a = document.createElement("iframe");
|
|
298
|
+
const b = document.createElement("iframe");
|
|
299
|
+
document.body.append(a, b);
|
|
300
|
+
const ref = { current: a as HTMLIFrameElement | null };
|
|
301
|
+
usePreviewIframeStore.setState({ iframe: a });
|
|
302
|
+
window.matchMedia = matchMedia(false);
|
|
303
|
+
host = document.createElement("div");
|
|
304
|
+
document.body.append(host);
|
|
305
|
+
root = createRoot(host);
|
|
306
|
+
act(() => {
|
|
307
|
+
root?.render(<TopologyLens iframeRef={ref} activeCompositionPath="index.html" />);
|
|
308
|
+
});
|
|
309
|
+
begin();
|
|
310
|
+
|
|
311
|
+
act(() => {
|
|
312
|
+
ref.current = b;
|
|
313
|
+
usePreviewIframeStore.getState().setIframe(b);
|
|
314
|
+
});
|
|
315
|
+
act(() => a.dispatchEvent(new Event("load")));
|
|
316
|
+
expect(host.querySelector('[data-topology-lens="hidden"]')).toBeNull();
|
|
317
|
+
|
|
318
|
+
act(() => b.dispatchEvent(new Event("load")));
|
|
319
|
+
expect(host.querySelector('[data-topology-lens="hidden"]')).not.toBeNull();
|
|
320
|
+
usePreviewIframeStore.setState({ iframe: null });
|
|
321
|
+
});
|
|
294
322
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useLivePreviewIframe } from "../../player/store/previewIframeStore";
|
|
1
2
|
import {
|
|
2
3
|
useEffect,
|
|
3
4
|
useLayoutEffect,
|
|
@@ -113,13 +114,14 @@ export function TopologyLens({ iframeRef, activeCompositionPath }: TopologyLensP
|
|
|
113
114
|
setMeasured(geometry ? { callId, geometry } : null);
|
|
114
115
|
}, [activeCompositionPath, callId, handle, iframeRef, phase]);
|
|
115
116
|
|
|
117
|
+
const livePreviewIframe = useLivePreviewIframe();
|
|
116
118
|
useEffect(() => {
|
|
117
119
|
const iframe = iframeRef.current;
|
|
118
120
|
if (!iframe || !callId) return;
|
|
119
121
|
const dismiss = () => studioEditLifecycle.dismiss(callId);
|
|
120
122
|
iframe.addEventListener("load", dismiss);
|
|
121
123
|
return () => iframe.removeEventListener("load", dismiss);
|
|
122
|
-
}, [callId, iframeRef]);
|
|
124
|
+
}, [callId, iframeRef, livePreviewIframe]);
|
|
123
125
|
|
|
124
126
|
useEffect(() => {
|
|
125
127
|
if (pendingUnmountDismissRef.current !== null) {
|
|
@@ -143,7 +145,7 @@ export function TopologyLens({ iframeRef, activeCompositionPath }: TopologyLensP
|
|
|
143
145
|
ref={overlayRef}
|
|
144
146
|
aria-hidden="true"
|
|
145
147
|
data-topology-lens={visible ? state.phase : "hidden"}
|
|
146
|
-
className="pointer-events-none absolute inset-0 z-
|
|
148
|
+
className="pointer-events-none absolute inset-0 z-58 overflow-hidden"
|
|
147
149
|
>
|
|
148
150
|
{visible && (
|
|
149
151
|
<>
|
|
@@ -171,7 +173,7 @@ export function TopologyLens({ iframeRef, activeCompositionPath }: TopologyLensP
|
|
|
171
173
|
key={index}
|
|
172
174
|
data-topology-contour="true"
|
|
173
175
|
data-topology-node={label}
|
|
174
|
-
className="hf-topology-contour pointer-events-none absolute z-
|
|
176
|
+
className="hf-topology-contour pointer-events-none absolute z-1 rounded-sm"
|
|
175
177
|
style={{
|
|
176
178
|
...rectStyle(rect),
|
|
177
179
|
animationDelay: `${Math.min(index, 5) * 28}ms`,
|
|
@@ -181,7 +183,7 @@ export function TopologyLens({ iframeRef, activeCompositionPath }: TopologyLensP
|
|
|
181
183
|
{!reducedMotion && (
|
|
182
184
|
<div
|
|
183
185
|
data-topology-scan="true"
|
|
184
|
-
className="pointer-events-none absolute z-
|
|
186
|
+
className="pointer-events-none absolute z-2 overflow-hidden rounded-sm"
|
|
185
187
|
style={rectStyle(geometry.field.rect)}
|
|
186
188
|
>
|
|
187
189
|
<div className="hf-topology-scan absolute inset-y-0 left-0 w-1/4" />
|
|
@@ -196,7 +198,7 @@ export function TopologyLens({ iframeRef, activeCompositionPath }: TopologyLensP
|
|
|
196
198
|
data-topology-terminal={
|
|
197
199
|
state.phase === "localizing" ? (state.terminal ?? undefined) : undefined
|
|
198
200
|
}
|
|
199
|
-
className="hf-topology-target pointer-events-none absolute z-
|
|
201
|
+
className="hf-topology-target pointer-events-none absolute z-3 rounded-md"
|
|
200
202
|
style={rectStyle(geometry.target.rect)}
|
|
201
203
|
/>
|
|
202
204
|
</>
|
|
@@ -183,7 +183,7 @@ export function Transform3DCube({
|
|
|
183
183
|
};
|
|
184
184
|
|
|
185
185
|
return (
|
|
186
|
-
<div className="relative overflow-hidden rounded-lg border border-neutral-800 bg-
|
|
186
|
+
<div className="relative overflow-hidden rounded-lg border border-neutral-800 bg-linear-to-b from-neutral-900 to-neutral-950">
|
|
187
187
|
<svg
|
|
188
188
|
ref={svgRef}
|
|
189
189
|
viewBox={`0 0 ${VIEW_W} ${VIEW_H}`}
|
|
@@ -294,7 +294,7 @@ export function Transform3DCube({
|
|
|
294
294
|
onClick={onRecenter}
|
|
295
295
|
title="Reset 3D orientation"
|
|
296
296
|
aria-label="Reset 3D orientation"
|
|
297
|
-
className="absolute right-1.5 top-1.5 rounded p-0.5 text-neutral-500 hover:bg-neutral-800 hover:text-neutral-200"
|
|
297
|
+
className="absolute right-1.5 top-1.5 rounded-sm p-0.5 text-neutral-500 hover:bg-neutral-800 hover:text-neutral-200"
|
|
298
298
|
>
|
|
299
299
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
300
300
|
<circle cx="12" cy="12" r="9" strokeWidth="2" />
|
|
@@ -37,6 +37,7 @@ export function audioFxRevealTarget(
|
|
|
37
37
|
const parsed = parseAutomationTarget(target);
|
|
38
38
|
if (!parsed) return null;
|
|
39
39
|
if (parsed.kind === "volume") return { kind: "volume" };
|
|
40
|
+
if (parsed.kind === "rate") return null;
|
|
40
41
|
if (parsed.kind === "preset") {
|
|
41
42
|
// A preset-level lane names the preset, not a node inside it: find its run
|
|
42
43
|
// by the first node that belongs to it, which is how `runKey` is built.
|
|
@@ -1103,7 +1103,7 @@ describe("patch builders and prompt builder", () => {
|
|
|
1103
1103
|
expect(prompt).toContain("Computed styles (browser-resolved):");
|
|
1104
1104
|
expect(prompt).toContain("Target HTML:");
|
|
1105
1105
|
expect(prompt).toContain("Guardrails:");
|
|
1106
|
-
expect(prompt).toContain("Do not modify other elements' data-* attributes or positioning
|
|
1106
|
+
expect(prompt).toContain("Do not modify other elements' data-* attributes or positioning");
|
|
1107
1107
|
});
|
|
1108
1108
|
|
|
1109
1109
|
it("uses an absolute source path in copied agent prompts when provided", () => {
|
|
@@ -78,9 +78,10 @@ describe("buildElementAgentPrompt", () => {
|
|
|
78
78
|
"Text: Hello world",
|
|
79
79
|
"",
|
|
80
80
|
"Guardrails:",
|
|
81
|
-
"- Make a targeted change to this element only.",
|
|
82
|
-
"- Preserve the rest of the composition and its timing.",
|
|
83
|
-
"- Do not modify other elements' data-* attributes or positioning.",
|
|
81
|
+
"- Make a targeted change to this element only, unless the request is a timeline edit.",
|
|
82
|
+
"- Preserve the rest of the composition and its timing, except what a timeline edit changes.",
|
|
83
|
+
"- Do not modify other elements' data-* attributes or positioning, except where the requested timeline edit requires it (split, retime, reorder, copy a group, swap media).",
|
|
84
|
+
"- For timeline edits (trim, split, speed, volume, copy, swap), follow the creator-editing-recipes reference of the hyperframes-core skill and use its exact attribute forms.",
|
|
84
85
|
"- Prefer existing inline styles or existing CSS rules for this element over adding unrelated selectors.",
|
|
85
86
|
].join("\n"),
|
|
86
87
|
);
|
|
@@ -160,9 +161,10 @@ describe("buildPickerAgentPrompt", () => {
|
|
|
160
161
|
// Both end on the identical guardrails block, byte for byte.
|
|
161
162
|
const guardrails = [
|
|
162
163
|
"Guardrails:",
|
|
163
|
-
"- Make a targeted change to this element only.",
|
|
164
|
-
"- Preserve the rest of the composition and its timing.",
|
|
165
|
-
"- Do not modify other elements' data-* attributes or positioning.",
|
|
164
|
+
"- Make a targeted change to this element only, unless the request is a timeline edit.",
|
|
165
|
+
"- Preserve the rest of the composition and its timing, except what a timeline edit changes.",
|
|
166
|
+
"- Do not modify other elements' data-* attributes or positioning, except where the requested timeline edit requires it (split, retime, reorder, copy a group, swap media).",
|
|
167
|
+
"- For timeline edits (trim, split, speed, volume, copy, swap), follow the creator-editing-recipes reference of the hyperframes-core skill and use its exact attribute forms.",
|
|
166
168
|
"- Prefer existing inline styles or existing CSS rules for this element over adding unrelated selectors.",
|
|
167
169
|
].join("\n");
|
|
168
170
|
expect(domPrompt.endsWith(guardrails)).toBe(true);
|
|
@@ -28,9 +28,10 @@ export interface AgentPromptElementInfo {
|
|
|
28
28
|
|
|
29
29
|
const GUARDRAIL_LINES = [
|
|
30
30
|
"Guardrails:",
|
|
31
|
-
"- Make a targeted change to this element only.",
|
|
32
|
-
"- Preserve the rest of the composition and its timing.",
|
|
33
|
-
"- Do not modify other elements' data-* attributes or positioning.",
|
|
31
|
+
"- Make a targeted change to this element only, unless the request is a timeline edit.",
|
|
32
|
+
"- Preserve the rest of the composition and its timing, except what a timeline edit changes.",
|
|
33
|
+
"- Do not modify other elements' data-* attributes or positioning, except where the requested timeline edit requires it (split, retime, reorder, copy a group, swap media).",
|
|
34
|
+
"- For timeline edits (trim, split, speed, volume, copy, swap), follow the creator-editing-recipes reference of the hyperframes-core skill and use its exact attribute forms.",
|
|
34
35
|
"- Prefer existing inline styles or existing CSS rules for this element over adding unrelated selectors.",
|
|
35
36
|
];
|
|
36
37
|
|
|
@@ -79,7 +79,7 @@ function ColorSlider({
|
|
|
79
79
|
aria-valuemax={max}
|
|
80
80
|
aria-valuenow={value}
|
|
81
81
|
aria-disabled={disabled}
|
|
82
|
-
className={`relative h-4 rounded-full border border-neutral-700 shadow-[inset_0_1px_2px_rgba(0,0,0,0.55)] outline-
|
|
82
|
+
className={`relative h-4 rounded-full border border-neutral-700 shadow-[inset_0_1px_2px_rgba(0,0,0,0.55)] outline-hidden focus:border-panel-accent focus:ring-2 focus:ring-panel-accent/40 ${
|
|
83
83
|
disabled ? "cursor-not-allowed opacity-50" : "cursor-ew-resize"
|
|
84
84
|
}`}
|
|
85
85
|
style={{ background }}
|
|
@@ -328,7 +328,7 @@ export function ColorField({
|
|
|
328
328
|
role="dialog"
|
|
329
329
|
aria-label={`${label} color picker`}
|
|
330
330
|
tabIndex={-1}
|
|
331
|
-
className="fixed z-
|
|
331
|
+
className="fixed z-9999 w-[292px] overflow-hidden rounded-2xl border border-neutral-700 bg-neutral-950 shadow-2xl shadow-black/50 outline-hidden"
|
|
332
332
|
style={{
|
|
333
333
|
left: panelPosition?.left ?? -9999,
|
|
334
334
|
top: panelPosition?.top ?? -9999,
|
|
@@ -367,8 +367,8 @@ export function ColorField({
|
|
|
367
367
|
onPointerUp={settleColorGesture}
|
|
368
368
|
onPointerCancel={cancelColorGesture}
|
|
369
369
|
>
|
|
370
|
-
<div className="absolute inset-0 bg-
|
|
371
|
-
<div className="absolute inset-0 bg-
|
|
370
|
+
<div className="absolute inset-0 bg-linear-to-r from-white to-transparent" />
|
|
371
|
+
<div className="absolute inset-0 bg-linear-to-t from-black to-transparent" />
|
|
372
372
|
<div
|
|
373
373
|
className="pointer-events-none absolute top-0 h-full w-px -translate-x-1/2 bg-white/70 shadow-[0_0_0_1px_rgba(0,0,0,0.45)] mix-blend-difference"
|
|
374
374
|
style={{ left: `${hsv.saturation * 100}%` }}
|
|
@@ -389,7 +389,7 @@ export function ColorField({
|
|
|
389
389
|
|
|
390
390
|
<div className="flex min-w-0 items-center gap-3">
|
|
391
391
|
<div
|
|
392
|
-
className="h-9 w-9
|
|
392
|
+
className="h-9 w-9 shrink-0 rounded-xl border border-neutral-600 shadow-[inset_0_1px_0_rgba(255,255,255,0.08)]"
|
|
393
393
|
style={{ backgroundColor: currentColor }}
|
|
394
394
|
/>
|
|
395
395
|
<div className="min-w-0 flex-1">
|
|
@@ -442,7 +442,7 @@ export function ColorField({
|
|
|
442
442
|
value={hexDraft}
|
|
443
443
|
onChange={(event) => handleHexChange(event.target.value)}
|
|
444
444
|
onBlur={settleColorGesture}
|
|
445
|
-
className={`${FIELD} h-10 w-full text-[11px] font-medium outline-
|
|
445
|
+
className={`${FIELD} h-10 w-full text-[11px] font-medium outline-hidden`}
|
|
446
446
|
spellCheck={false}
|
|
447
447
|
/>
|
|
448
448
|
</label>
|
|
@@ -475,7 +475,7 @@ export function ColorField({
|
|
|
475
475
|
className="flex items-center gap-2 disabled:cursor-not-allowed"
|
|
476
476
|
>
|
|
477
477
|
<span
|
|
478
|
-
className="h-4 w-4
|
|
478
|
+
className="h-4 w-4 shrink-0 rounded-[4px]"
|
|
479
479
|
style={{ backgroundColor: open ? currentColor : value || "transparent" }}
|
|
480
480
|
/>
|
|
481
481
|
<span className="font-mono text-[11px] text-panel-text-0">
|
|
@@ -484,7 +484,7 @@ export function ColorField({
|
|
|
484
484
|
{mixed && (
|
|
485
485
|
<span
|
|
486
486
|
data-color-mixed-indicator="true"
|
|
487
|
-
className="rounded bg-panel-hover px-1.5 py-0.5 text-[9px] font-medium text-panel-text-4"
|
|
487
|
+
className="rounded-sm bg-panel-hover px-1.5 py-0.5 text-[9px] font-medium text-panel-text-4"
|
|
488
488
|
>
|
|
489
489
|
Mixed
|
|
490
490
|
</span>
|
|
@@ -504,7 +504,7 @@ export function ColorField({
|
|
|
504
504
|
type="button"
|
|
505
505
|
disabled={disabled}
|
|
506
506
|
onClick={onReset}
|
|
507
|
-
className="rounded bg-panel-hover px-1.5 py-0.5 text-[9px] font-medium text-panel-text-4 transition-colors hover:text-panel-text-0 disabled:cursor-not-allowed disabled:opacity-40"
|
|
507
|
+
className="rounded-sm bg-panel-hover px-1.5 py-0.5 text-[9px] font-medium text-panel-text-4 transition-colors hover:text-panel-text-0 disabled:cursor-not-allowed disabled:opacity-40"
|
|
508
508
|
>
|
|
509
509
|
Reset
|
|
510
510
|
</button>
|
|
@@ -519,7 +519,7 @@ export function ColorField({
|
|
|
519
519
|
className={`${FIELD} flex items-center gap-3 text-left hover:border-neutral-700 disabled:cursor-not-allowed ${open ? "border-neutral-600" : ""}`}
|
|
520
520
|
>
|
|
521
521
|
<div
|
|
522
|
-
className="relative h-7 w-7
|
|
522
|
+
className="relative h-7 w-7 shrink-0 overflow-hidden rounded-lg border border-neutral-700 bg-neutral-950 shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]"
|
|
523
523
|
style={{ backgroundColor: value || "transparent" }}
|
|
524
524
|
/>
|
|
525
525
|
<span className="min-w-0 flex-1 truncate text-[11px] font-medium text-neutral-100">
|
|
@@ -528,7 +528,7 @@ export function ColorField({
|
|
|
528
528
|
{mixed && (
|
|
529
529
|
<span
|
|
530
530
|
data-color-mixed-indicator="true"
|
|
531
|
-
className="rounded bg-panel-hover px-1.5 py-0.5 text-[9px] font-medium text-panel-text-4"
|
|
531
|
+
className="rounded-sm bg-panel-hover px-1.5 py-0.5 text-[9px] font-medium text-panel-text-4"
|
|
532
532
|
>
|
|
533
533
|
Mixed
|
|
534
534
|
</span>
|
|
@@ -477,7 +477,7 @@ export function CurveGraph({
|
|
|
477
477
|
onSettle();
|
|
478
478
|
}
|
|
479
479
|
}}
|
|
480
|
-
className="mx-auto aspect-square w-full max-w-[200px] touch-none rounded border border-panel-border-input bg-black/20 outline-
|
|
480
|
+
className="mx-auto aspect-square w-full max-w-[200px] touch-none rounded-sm border border-panel-border-input bg-black/20 outline-hidden focus:ring-1 focus:ring-panel-accent"
|
|
481
481
|
>
|
|
482
482
|
<defs>
|
|
483
483
|
<linearGradient id={`hf-hue-axis-${tab.key}`}>
|