mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-10 22:20:14 +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:
@@ -27,10 +27,6 @@ interface PlayerState {
|
||||
zoomMode: ZoomMode;
|
||||
/** Pixels per second when in manual zoom mode */
|
||||
pixelsPerSecond: number;
|
||||
/** Edit range selection */
|
||||
editRangeStart: number | null;
|
||||
editRangeEnd: number | null;
|
||||
editMode: boolean;
|
||||
|
||||
setIsPlaying: (playing: boolean) => void;
|
||||
setCurrentTime: (time: number) => void;
|
||||
@@ -39,11 +35,6 @@ interface PlayerState {
|
||||
setTimelineReady: (ready: boolean) => void;
|
||||
setElements: (elements: TimelineElement[]) => void;
|
||||
setSelectedElementId: (id: string | null) => void;
|
||||
setEditRange: (start: number | null, end: number | null) => void;
|
||||
setEditMode: (active: boolean) => void;
|
||||
updateElementStart: (elementId: string, newStart: number) => void;
|
||||
updateElementDuration: (elementId: string, newDuration: number) => void;
|
||||
updateElementTrack: (elementId: string, newTrack: number) => void;
|
||||
updateElement: (
|
||||
elementId: string,
|
||||
updates: Partial<Pick<TimelineElement, "start" | "duration" | "track">>,
|
||||
@@ -76,9 +67,6 @@ export const usePlayerStore = create<PlayerState>((set) => ({
|
||||
playbackRate: 1,
|
||||
zoomMode: "fit",
|
||||
pixelsPerSecond: 100,
|
||||
editRangeStart: null,
|
||||
editRangeEnd: null,
|
||||
editMode: false,
|
||||
|
||||
setIsPlaying: (playing) => set({ isPlaying: playing }),
|
||||
setPlaybackRate: (rate) => set({ playbackRate: rate }),
|
||||
@@ -89,26 +77,13 @@ export const usePlayerStore = create<PlayerState>((set) => ({
|
||||
setTimelineReady: (ready) => set({ timelineReady: ready }),
|
||||
setElements: (elements) => set({ elements }),
|
||||
setSelectedElementId: (id) => set({ selectedElementId: id }),
|
||||
setEditRange: (start, end) => set({ editRangeStart: start, editRangeEnd: end }),
|
||||
setEditMode: (active) => set({ editMode: active, editRangeStart: null, editRangeEnd: null }),
|
||||
updateElementStart: (elementId, newStart) =>
|
||||
set((state) => ({
|
||||
elements: state.elements.map((el) => (el.id === elementId ? { ...el, start: newStart } : el)),
|
||||
})),
|
||||
updateElementDuration: (elementId, newDuration) =>
|
||||
set((state) => ({
|
||||
elements: state.elements.map((el) =>
|
||||
el.id === elementId ? { ...el, duration: newDuration } : el,
|
||||
),
|
||||
})),
|
||||
updateElementTrack: (elementId, newTrack) =>
|
||||
set((state) => ({
|
||||
elements: state.elements.map((el) => (el.id === elementId ? { ...el, track: newTrack } : el)),
|
||||
})),
|
||||
updateElement: (elementId, updates) =>
|
||||
set((state) => ({
|
||||
elements: state.elements.map((el) => (el.id === elementId ? { ...el, ...updates } : el)),
|
||||
})),
|
||||
// Resets project-specific state when switching compositions.
|
||||
// playbackRate, zoomMode, and pixelsPerSecond are intentionally preserved
|
||||
// because they are user preferences that should survive project switches.
|
||||
reset: () =>
|
||||
set({
|
||||
isPlaying: false,
|
||||
|
||||
Reference in New Issue
Block a user