mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-12 07:09:59 +00:00
feat(studio): add Layers panel as new inspector tab (#784)
* feat(studio): add Layers panel as new inspector tab Adds a dedicated Layers tab alongside Design and Renders in the right panel inspector. The panel shows the full composition element tree with collapsible hierarchy — clicking a layer selects it without navigating away from the tree view. Closes #783 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(studio): add visual element previews to Layers panel Each layer row now shows a small color/content preview thumbnail: - Text elements show a snippet of their content in the actual font color - Container elements show their background color as a colored swatch - Image elements show a tiny thumbnail of the image - Media elements show an icon indicator Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(studio): add hover-to-highlight and auto-seek to Layers panel Replace tiny preview thumbnails with hover highlighting — hovering a layer row highlights the element in the preview canvas. Clicking a layer auto-seeks the playhead to that element's start time in the timeline. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(studio): layers panel tab order, autoseek, and renders toolbar overflow - Reorder inspector tabs to Design → Layers → Renders - Fix autoseek: walk DOM ancestors when selected element has no direct timeline match, so clicking a child like S2 Heading correctly seeks to the start of its parent scene - Fix Renders toolbar overflow: add flex-wrap to the export controls header so selects and the Export button wrap instead of clipping * fix(studio): seek to midpoint of element duration in layers panel autoseek * fix(studio): layers panel seek now drives adapter.seek via requestSeek signal setCurrentTime() only updated the store — adapter.seek() and liveTime.notify() were never called so the iframe never moved. Add requestedSeekTime to the player store; useTimelinePlayer subscribes and calls the real seek() path when it fires. * feat(studio): hover over a layer auto-seeks to element midpoint (300ms debounce) * feat(studio): add collapsible sections to Design panel Section component now supports collapse/expand with a chevron toggle. Text, Layout, and Fill sections stay expanded by default. Less-used sections (Flex, Radius, Stroke, Effects, Clip, Transparency) start collapsed to reduce scrolling. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(studio): remove stale selectedTimelineElement usages dropped in rebase * fix(studio): stabilize resetErrors in useConsoleErrorCapture to break render loop resetErrors was a new function object on every render. handlePreviewIframeRef had it as a dep, so it also changed every render. NLELayout's useEffect watching onIframeRef would re-fire, calling setPreviewIframe again, which re-ran useConsoleErrorCapture with the new iframe — infinite loop. --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
51f968ec87
commit
6fe970651d
@@ -50,7 +50,11 @@ export interface UseDomSelectionReturn {
|
||||
// Callbacks
|
||||
applyDomSelection: (
|
||||
selection: DomEditSelection | null,
|
||||
options?: { revealPanel?: boolean; additive?: boolean; preserveGroup?: boolean },
|
||||
options?: {
|
||||
revealPanel?: boolean;
|
||||
additive?: boolean;
|
||||
preserveGroup?: boolean;
|
||||
},
|
||||
) => void;
|
||||
clearDomSelection: () => void;
|
||||
buildDomSelectionFromTarget: (
|
||||
@@ -108,7 +112,11 @@ export function useDomSelection({
|
||||
const applyDomSelection = useCallback(
|
||||
(
|
||||
selection: DomEditSelection | null,
|
||||
options?: { revealPanel?: boolean; additive?: boolean; preserveGroup?: boolean },
|
||||
options?: {
|
||||
revealPanel?: boolean;
|
||||
additive?: boolean;
|
||||
preserveGroup?: boolean;
|
||||
},
|
||||
) => {
|
||||
if (!selection) {
|
||||
domEditSelectionRef.current = null;
|
||||
@@ -157,7 +165,9 @@ export function useDomSelection({
|
||||
if (nextSelection) {
|
||||
if (options?.revealPanel !== false) {
|
||||
setRightCollapsed(false);
|
||||
setRightPanelTab("design");
|
||||
if (rightPanelTab !== "layers") {
|
||||
setRightPanelTab("design");
|
||||
}
|
||||
}
|
||||
const nextSelectedTimelineId = findMatchingTimelineElementId(
|
||||
nextSelection,
|
||||
@@ -169,7 +179,13 @@ export function useDomSelection({
|
||||
|
||||
setSelectedTimelineElementId(null);
|
||||
},
|
||||
[setSelectedTimelineElementId, timelineElements, setRightCollapsed, setRightPanelTab],
|
||||
[
|
||||
setSelectedTimelineElementId,
|
||||
timelineElements,
|
||||
setRightCollapsed,
|
||||
setRightPanelTab,
|
||||
rightPanelTab,
|
||||
],
|
||||
);
|
||||
|
||||
const clearDomSelection = useCallback(() => {
|
||||
@@ -223,7 +239,9 @@ export function useDomSelection({
|
||||
isMasterView,
|
||||
});
|
||||
return targetElement
|
||||
? buildDomSelectionFromTarget(targetElement, { preferClipAncestor: false })
|
||||
? buildDomSelectionFromTarget(targetElement, {
|
||||
preferClipAncestor: false,
|
||||
})
|
||||
: null;
|
||||
},
|
||||
[activeCompPath, buildDomSelectionFromTarget, compIdToSrc, isMasterView, previewIframeRef],
|
||||
@@ -259,7 +277,10 @@ export function useDomSelection({
|
||||
|
||||
const nextSelection = buildDomSelectionFromTarget(element);
|
||||
if (nextSelection) {
|
||||
applyDomSelection(nextSelection, { revealPanel: false, preserveGroup: true });
|
||||
applyDomSelection(nextSelection, {
|
||||
revealPanel: false,
|
||||
preserveGroup: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
[activeCompPath, applyDomSelection, buildDomSelectionFromTarget, previewIframeRef],
|
||||
|
||||
Reference in New Issue
Block a user