Files
Recordly/src/utils/aspectRatioUtils.ts
webadderall 9a5ff5c0a2 feat: ui overhaul — Phosphor icons, redesigned launcher, editor layout, and component refresh
- Replace all lucide-react imports with @phosphor-icons/react across every
  component (LaunchWindow, AnnotationSettingsPanel, ExtensionManager,
  ExtensionIcon, VideoPlayback, AnnotationOverlay, CropControl, TutorialHelp,
  PlaybackControls, all shadcn ui/ primitives, etc.)
- ExtensionIcon: new Phosphor-based resolver with Lucide name alias map for
  backwards-compatible extension manifests
- LaunchWindow / SourceSelector: full redesign — new icon rail layout,
  updated backgrounds, improved source selection UX, audio/video controls,
  countdown overlay
- VideoPlayback: overhaul playback controls and preview panel layout
- Timeline items: border-radius 16px→8px, cyan→blue unification, pill height
  85% for breathing room, resize handle vertical centering
- shadcn ui/: refresh accordion, dialog, dropdown, select, slider, switch,
  tabs, toggle, popover, button, card, input, label, sonner, content-clamp,
  audio-level-meter
- App.tsx / index.css / App.css / tailwind.config: global style updates
- types.ts, captionLayout.ts, captionStyle.ts, webcamOverlay.ts: data model
  updates to support new timeline and export features
- package.json: add @phosphor-icons/react, bump dependencies
- scripts/, .eslintrc, biome, tsconfig, vite.config: tooling alignment
- extension-sources: update built-in extension bundles
2026-04-16 18:01:04 +10:00

104 lines
2.6 KiB
TypeScript

export const ASPECT_RATIOS = [
"native",
"16:9",
"9:16",
"1:1",
"4:3",
"4:5",
"16:10",
"10:16",
] as const;
type PresetAspectRatio = (typeof ASPECT_RATIOS)[number];
type CustomAspectRatio = `${number}:${number}`;
export type AspectRatio = PresetAspectRatio | CustomAspectRatio;
const CUSTOM_ASPECT_RATIO_REGEX = /^(\d+):(\d+)$/;
export function isCustomAspectRatio(aspectRatio: string): aspectRatio is CustomAspectRatio {
if ((ASPECT_RATIOS as readonly string[]).includes(aspectRatio)) {
return false;
}
const match = aspectRatio.match(CUSTOM_ASPECT_RATIO_REGEX);
if (!match) {
return false;
}
const width = Number(match[1]);
const height = Number(match[2]);
return Number.isFinite(width) && Number.isFinite(height) && width > 0 && height > 0;
}
function parseCustomAspectRatioValue(aspectRatio: string): number | null {
if (!isCustomAspectRatio(aspectRatio)) {
return null;
}
const [widthText, heightText] = aspectRatio.split(":");
const width = Number(widthText);
const height = Number(heightText);
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
return null;
}
return width / height;
}
/**
* Returns the numeric value of an aspect ratio.
* Uses exhaustive type checking to ensure all AspectRatio cases are handled.
* If TypeScript errors here, a new ratio was added to the type but not handled.
*/
export function getAspectRatioValue(aspectRatio: AspectRatio, nativeAspectRatio = 16 / 9): number {
switch (aspectRatio) {
case "native":
return nativeAspectRatio > 0 ? nativeAspectRatio : 16 / 9;
case "16:9":
return 16 / 9;
case "9:16":
return 9 / 16;
case "1:1":
return 1;
case "4:3":
return 4 / 3;
case "4:5":
return 4 / 5;
case "16:10":
return 16 / 10;
case "10:16":
return 10 / 16;
default:
return parseCustomAspectRatioValue(aspectRatio) ?? 16 / 9;
}
}
export function getAspectRatioDimensions(
aspectRatio: AspectRatio,
baseWidth: number,
nativeAspectRatio?: number,
): { width: number; height: number } {
const ratio = getAspectRatioValue(aspectRatio, nativeAspectRatio);
return {
width: baseWidth,
height: baseWidth / ratio,
};
}
export function getAspectRatioLabel(aspectRatio: AspectRatio): string {
if (aspectRatio === "native") {
return "Native";
}
if (isCustomAspectRatio(aspectRatio)) {
return `Custom ${aspectRatio}`;
}
return aspectRatio;
}
export function formatAspectRatioForCSS(
aspectRatio: AspectRatio,
nativeAspectRatio = 16 / 9,
): string {
if (aspectRatio === "native") {
return `${nativeAspectRatio > 0 ? nativeAspectRatio : 16 / 9}`;
}
return aspectRatio.replace(":", "/");
}