mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-01 19:42:03 +00:00
feat(studio): auto-reveal source when selecting elements in Code tab
When the Code tab is active and a user clicks an element in the preview, the code editor now auto-scrolls to the corresponding HTML source. This removes the need for Alt+click — the existing click-to-source mechanism fires automatically when the Code tab is already open. - Add getTab() to LeftSidebarHandle for reading the active tab - Add getSidebarTab getter to useDomEditSession - Add effect that calls openSourceForSelection on selection change when Code tab is active
This commit is contained in:
@@ -295,6 +295,15 @@ export function StudioApp() {
|
||||
handleCut,
|
||||
});
|
||||
|
||||
const selectSidebarTabStable = useCallback(
|
||||
(tab: SidebarTab) => leftSidebarRef.current?.selectTab(tab),
|
||||
[],
|
||||
);
|
||||
const getSidebarTabStable = useCallback(
|
||||
() => leftSidebarRef.current?.getTab() ?? "compositions",
|
||||
[],
|
||||
);
|
||||
|
||||
const domEditSession = useDomEditSession({
|
||||
projectId,
|
||||
activeCompPath,
|
||||
@@ -327,7 +336,8 @@ export function StudioApp() {
|
||||
reloadPreview,
|
||||
setRefreshKey,
|
||||
openSourceForSelection: fileManager.openSourceForSelection,
|
||||
selectSidebarTab: (tab: SidebarTab) => leftSidebarRef.current?.selectTab(tab),
|
||||
selectSidebarTab: selectSidebarTabStable,
|
||||
getSidebarTab: getSidebarTabStable,
|
||||
});
|
||||
|
||||
domEditSelectionBridgeRef.current = domEditSession.domEditSelection;
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
useState,
|
||||
useCallback,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
forwardRef,
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
@@ -17,6 +18,7 @@ export type SidebarTab = "compositions" | "assets" | "code" | "blocks";
|
||||
|
||||
export interface LeftSidebarHandle {
|
||||
selectTab: (tab: SidebarTab) => void;
|
||||
getTab: () => SidebarTab;
|
||||
}
|
||||
|
||||
const STORAGE_KEY = "hf-studio-sidebar-tab";
|
||||
@@ -87,6 +89,8 @@ export const LeftSidebar = memo(
|
||||
ref,
|
||||
) {
|
||||
const [tab, setTab] = useState<SidebarTab>(getPersistedTab);
|
||||
const tabRef = useRef(tab);
|
||||
tabRef.current = tab;
|
||||
|
||||
const selectTab = useCallback((t: SidebarTab) => {
|
||||
setTab(t);
|
||||
@@ -94,7 +98,9 @@ export const LeftSidebar = memo(
|
||||
trackStudioEvent("tab_switch", { panel: "left_sidebar", tab: t });
|
||||
}, []);
|
||||
|
||||
useImperativeHandle(ref, () => ({ selectTab }), [selectTab]);
|
||||
const getTab = useCallback(() => tabRef.current, []);
|
||||
|
||||
useImperativeHandle(ref, () => ({ selectTab, getTab }), [selectTab, getTab]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useEffect } from "react";
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
import type { TimelineElement } from "../player";
|
||||
import { STUDIO_INSPECTOR_PANELS_ENABLED } from "../components/editor/manualEditingAvailability";
|
||||
import { findElementForSelection, type DomEditSelection } from "../components/editor/domEditing";
|
||||
@@ -56,6 +56,7 @@ export interface UseDomEditSessionParams {
|
||||
setRefreshKey: React.Dispatch<React.SetStateAction<number>>;
|
||||
openSourceForSelection?: (sourceFile: string, target: PatchTarget) => void;
|
||||
selectSidebarTab?: (tab: SidebarTab) => void;
|
||||
getSidebarTab?: () => SidebarTab;
|
||||
}
|
||||
|
||||
// ── Hook ──
|
||||
@@ -93,6 +94,7 @@ export function useDomEditSession({
|
||||
setRefreshKey: _setRefreshKey,
|
||||
openSourceForSelection,
|
||||
selectSidebarTab,
|
||||
getSidebarTab,
|
||||
}: UseDomEditSessionParams) {
|
||||
void _setRefreshKey;
|
||||
|
||||
@@ -281,6 +283,22 @@ export function useDomEditSession({
|
||||
applyStudioManualEditsToPreviewRef,
|
||||
]);
|
||||
|
||||
// Auto-reveal source when an element is selected while the Code tab is active.
|
||||
// Use a ref for the callback so the effect only fires on selection changes,
|
||||
// not when openSourceForSelection is recreated due to editingFile content updates.
|
||||
const openSourceRef = useRef(openSourceForSelection);
|
||||
openSourceRef.current = openSourceForSelection;
|
||||
useEffect(() => {
|
||||
if (!domEditSelection || !openSourceRef.current || !getSidebarTab) return;
|
||||
if (!domEditSelection.sourceFile) return;
|
||||
if (getSidebarTab() !== "code") return;
|
||||
openSourceRef.current(domEditSelection.sourceFile, {
|
||||
id: domEditSelection.id,
|
||||
selector: domEditSelection.selector,
|
||||
selectorIndex: domEditSelection.selectorIndex,
|
||||
});
|
||||
}, [domEditSelection, getSidebarTab]);
|
||||
|
||||
return {
|
||||
// State
|
||||
domEditSelection,
|
||||
|
||||
@@ -122,6 +122,9 @@ export function useFileManager({
|
||||
const handleFileSelect = useCallback((path: string) => {
|
||||
const pid = projectIdRef.current;
|
||||
if (!pid) return;
|
||||
revealAbortRef.current?.abort();
|
||||
revealAbortRef.current = null;
|
||||
revealRequestIdRef.current++;
|
||||
// Skip fetching binary content for media files — just set the path for preview
|
||||
if (isMediaFile(path)) {
|
||||
setEditingFile({ path, content: null });
|
||||
|
||||
Reference in New Issue
Block a user