mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
Run `fallow fix --auto-fixable` to remove `export` keywords from symbols fallow's reachability analysis identifies as unused. Keeps only the cases where the symbol is still referenced internally in its own file (so removing `export` doesn't surface a new oxlint `no-unused-vars` error). Result: fallow dead-code findings drop from 276 → 208 (68 fewer unused exports), with no behavior change — each symbol is still defined and used exactly the same way within its file. Reverted ~20 files where fallow's auto-fix would have created cascading "declared but never used" lint errors — those are cases where the symbol isn't used at all, and properly cleaning them up means deleting the declaration, not just dropping `export`. Better to land that as a separate, narrower PR rather than mixing it into a mechanical de-export. Also reverted four false positives where fallow missed real consumers: - `captureCost.ts` (renderOrchestrator has two separate import blocks from the same module; fallow only saw the first) - `propertyPanelHelpers.ts`, `domEditingLayers.ts` (real internal uses fallow's reachability missed) - `render.ts` (functions imported via `await import()` dynamic import, which fallow's static analysis doesn't follow) Test plan: bun run --filter '*' typecheck (clean), oxlint + oxfmt clean, cli/core/studio/engine vitest suites pass (335 + 917 + 576 + 605 tests).
136 lines
4.6 KiB
TypeScript
136 lines
4.6 KiB
TypeScript
import type { RightPanelTab } from "./studioHelpers";
|
|
import { buildProjectHash, parseProjectHashRoute } from "./projectRouting";
|
|
import {
|
|
STUDIO_INSPECTOR_PANELS_ENABLED,
|
|
STUDIO_MOTION_PANEL_ENABLED,
|
|
} from "../components/editor/manualEditingAvailability";
|
|
|
|
export interface StudioUrlSelectionState {
|
|
sourceFile?: string;
|
|
id?: string;
|
|
selector?: string;
|
|
selectorIndex?: number;
|
|
}
|
|
|
|
export interface StudioUrlState {
|
|
activeCompPath: string | null;
|
|
currentTime: number | null;
|
|
rightPanelTab: RightPanelTab | null;
|
|
rightCollapsed: boolean | null;
|
|
timelineVisible: boolean | null;
|
|
selection: StudioUrlSelectionState | null;
|
|
}
|
|
|
|
const VALID_TABS: RightPanelTab[] = ["layers", "design", "motion", "renders"];
|
|
|
|
export function normalizeStudioUrlPanelTab(
|
|
tab: RightPanelTab | null,
|
|
options: {
|
|
inspectorPanelsEnabled?: boolean;
|
|
motionPanelEnabled?: boolean;
|
|
} = {},
|
|
): RightPanelTab | null {
|
|
if (!tab) return null;
|
|
if (!VALID_TABS.includes(tab)) return null;
|
|
const inspectorPanelsEnabled = options.inspectorPanelsEnabled ?? STUDIO_INSPECTOR_PANELS_ENABLED;
|
|
const motionPanelEnabled = options.motionPanelEnabled ?? STUDIO_MOTION_PANEL_ENABLED;
|
|
|
|
if (!inspectorPanelsEnabled && tab !== "renders") return "renders";
|
|
if (tab === "motion" && !motionPanelEnabled) return "design";
|
|
return tab;
|
|
}
|
|
|
|
export function normalizeStudioCompositionPath(
|
|
activeCompPath: string | null,
|
|
fileTree: string[],
|
|
): string | null {
|
|
if (!activeCompPath || activeCompPath === "index.html") return null;
|
|
return fileTree.includes(activeCompPath) ? activeCompPath : null;
|
|
}
|
|
|
|
function parseBoolean(value: string | null): boolean | null {
|
|
if (value === "1") return true;
|
|
if (value === "0") return false;
|
|
return null;
|
|
}
|
|
|
|
function parseNumber(value: string | null): number | null {
|
|
if (value == null || value === "") return null;
|
|
const parsed = Number(value);
|
|
return Number.isFinite(parsed) ? parsed : null;
|
|
}
|
|
|
|
function parseTab(value: string | null): RightPanelTab | null {
|
|
return VALID_TABS.includes(value as RightPanelTab) ? (value as RightPanelTab) : null;
|
|
}
|
|
|
|
function normalizeSelection(params: URLSearchParams): StudioUrlSelectionState | null {
|
|
const sourceFile = params.get("selFile") || undefined;
|
|
const id = params.get("selId") || undefined;
|
|
const selector = params.get("selSelector") || undefined;
|
|
const selectorIndex = parseNumber(params.get("selIndex"));
|
|
|
|
if (!sourceFile && !id && !selector) return null;
|
|
|
|
return {
|
|
sourceFile,
|
|
id,
|
|
selector,
|
|
selectorIndex: selectorIndex != null ? Math.max(0, Math.floor(selectorIndex)) : undefined,
|
|
};
|
|
}
|
|
|
|
function defaultStudioUrlState(): StudioUrlState {
|
|
return {
|
|
activeCompPath: null,
|
|
currentTime: null,
|
|
rightPanelTab: null,
|
|
rightCollapsed: null,
|
|
timelineVisible: null,
|
|
selection: null,
|
|
};
|
|
}
|
|
|
|
export function parseStudioUrlStateFromHash(hash: string): StudioUrlState {
|
|
const route = parseProjectHashRoute(hash);
|
|
if (!route) return defaultStudioUrlState();
|
|
|
|
const { params } = route;
|
|
return {
|
|
activeCompPath: params.get("comp") || null,
|
|
currentTime: parseNumber(params.get("t")),
|
|
rightPanelTab: normalizeStudioUrlPanelTab(parseTab(params.get("tab"))),
|
|
rightCollapsed: parseBoolean(params.get("rc")),
|
|
timelineVisible: parseBoolean(params.get("tv")),
|
|
selection: normalizeSelection(params),
|
|
};
|
|
}
|
|
|
|
export function readStudioUrlStateFromWindow(): StudioUrlState {
|
|
if (typeof window === "undefined") return defaultStudioUrlState();
|
|
return parseStudioUrlStateFromHash(window.location.hash);
|
|
}
|
|
|
|
export function buildStudioHash(projectId: string, state: StudioUrlState): string {
|
|
const params = new URLSearchParams();
|
|
|
|
params.set("v", "1");
|
|
if (state.activeCompPath) params.set("comp", state.activeCompPath);
|
|
if (state.currentTime != null && Number.isFinite(state.currentTime)) {
|
|
params.set("t", String(Math.max(0, Math.round(state.currentTime * 1000) / 1000)));
|
|
}
|
|
if (state.rightPanelTab) params.set("tab", state.rightPanelTab);
|
|
if (state.rightCollapsed != null) params.set("rc", state.rightCollapsed ? "1" : "0");
|
|
if (state.timelineVisible != null) params.set("tv", state.timelineVisible ? "1" : "0");
|
|
if (state.selection) {
|
|
if (state.selection.sourceFile) params.set("selFile", state.selection.sourceFile);
|
|
if (state.selection.id) params.set("selId", state.selection.id);
|
|
if (state.selection.selector) params.set("selSelector", state.selection.selector);
|
|
if (typeof state.selection.selectorIndex === "number") {
|
|
params.set("selIndex", String(Math.max(0, Math.floor(state.selection.selectorIndex))));
|
|
}
|
|
}
|
|
|
|
return buildProjectHash(projectId, params);
|
|
}
|