mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-04 07:19:52 +00:00
refactor(studio): code quality — 22 findings, dead code removal, App.tsx split (#144)
## Summary Full code quality review of the studio package, fixing 22 of 25 findings. Removes dead code, extracts modules from App.tsx, fixes accessibility and performance issues. ## Critical fixes (3) - **`aria-valuenow`** on seek bar now updates imperatively via `liveTime.subscribe` — screen readers previously always reported position 0 - **Speed menu** closes on outside click (was permanently stuck open) - **RenderQueue auto-scroll** moved from render phase to `useEffect` (was violating React render purity via `queueMicrotask` during render) ## Dead code removed (-331 lines) | File | Lines | Why dead | |---|---|---| | `PreviewPanel.tsx` | 180 | Replaced by NLELayout + NLEPreview | | `useCodeEditor.ts` | 80 | Exported but never imported | | `formatTick` alias | 2 | Deprecated, unused | | `onClipChange` prop | 5 | Declared, never used | | `trackH` prop | 5 | Declared, never used | | `editRange*` + updaters in store | 60 | Never read or written | ## App.tsx extraction | Extracted to | Lines | What | |---|---|---| | `components/LintModal.tsx` | 130 | Lint results modal + LintFinding type | | `components/MediaPreview.tsx` | 75 | Image/video/audio/font file previewer | | `utils/mediaTypes.ts` | 15 | Shared regex constants (App.tsx and AssetsTab.tsx had diverged copies) | ## Performance fixes - `useMemo` for `compositions`/`assets` derivation from `fileTree` - `useMemo` for `buildTree(files)` in FileTree - Debounced `handleContentChange` PUT (600ms — was firing on every keystroke) - CompositionsTab iframe hover debounced (300ms — was mounting immediately) - `VideoFrameThumbnail` re-extracts frame when `src` prop changes ## Not addressed (3 — low priority) - #6: SystemIcons consolidation (large refactor across many files) - #16-17: Overlay dismiss pattern standardization - #18: Inline SVG → Phosphor replacement (gradual, per-PR) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
@@ -1,88 +0,0 @@
|
||||
import { useState, useCallback } from "react";
|
||||
|
||||
export interface OpenFile {
|
||||
path: string;
|
||||
content: string;
|
||||
savedContent: string;
|
||||
isDirty: boolean;
|
||||
}
|
||||
|
||||
export interface UseCodeEditorReturn {
|
||||
openFiles: OpenFile[];
|
||||
activeFilePath: string | null;
|
||||
activeFile: OpenFile | null;
|
||||
openFile: (path: string, content: string) => void;
|
||||
closeFile: (path: string) => void;
|
||||
setActiveFile: (path: string) => void;
|
||||
updateContent: (content: string) => void;
|
||||
markSaved: (path: string) => void;
|
||||
/** External update — updates saved content, shows reload indicator */
|
||||
externalUpdate: (path: string, content: string) => void;
|
||||
}
|
||||
|
||||
export function useCodeEditor(): UseCodeEditorReturn {
|
||||
const [openFiles, setOpenFiles] = useState<OpenFile[]>([]);
|
||||
const [activeFilePath, setActiveFilePath] = useState<string | null>(null);
|
||||
|
||||
const activeFile = openFiles.find((f) => f.path === activeFilePath) ?? null;
|
||||
|
||||
const openFile = useCallback((path: string, content: string) => {
|
||||
setOpenFiles((prev) => {
|
||||
const existing = prev.find((f) => f.path === path);
|
||||
if (existing) return prev;
|
||||
return [...prev, { path, content, savedContent: content, isDirty: false }];
|
||||
});
|
||||
setActiveFilePath(path);
|
||||
}, []);
|
||||
|
||||
const closeFile = useCallback(
|
||||
(path: string) => {
|
||||
setOpenFiles((prev) => prev.filter((f) => f.path !== path));
|
||||
setActiveFilePath((prev) => {
|
||||
if (prev === path) {
|
||||
const remaining = openFiles.filter((f) => f.path !== path);
|
||||
return remaining.length > 0 ? remaining[remaining.length - 1].path : null;
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
},
|
||||
[openFiles],
|
||||
);
|
||||
|
||||
const updateContent = useCallback(
|
||||
(content: string) => {
|
||||
setOpenFiles((prev) =>
|
||||
prev.map((f) =>
|
||||
f.path === activeFilePath ? { ...f, content, isDirty: content !== f.savedContent } : f,
|
||||
),
|
||||
);
|
||||
},
|
||||
[activeFilePath],
|
||||
);
|
||||
|
||||
const markSaved = useCallback((path: string) => {
|
||||
setOpenFiles((prev) =>
|
||||
prev.map((f) => (f.path === path ? { ...f, savedContent: f.content, isDirty: false } : f)),
|
||||
);
|
||||
}, []);
|
||||
|
||||
const externalUpdate = useCallback((path: string, content: string) => {
|
||||
setOpenFiles((prev) =>
|
||||
prev.map((f) =>
|
||||
f.path === path ? { ...f, savedContent: content, content, isDirty: false } : f,
|
||||
),
|
||||
);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
openFiles,
|
||||
activeFilePath,
|
||||
activeFile,
|
||||
openFile,
|
||||
closeFile,
|
||||
setActiveFile: setActiveFilePath,
|
||||
updateContent,
|
||||
markSaved,
|
||||
externalUpdate,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user