import type { StatusBarConfig, StatusBarLayout, StatusBarDeviceType } from './status-bar.js'; import type { BrowserBarConfig } from './browser-bar.js'; export type DeviceCategory = 'phone' | 'tablet' | 'laptop' | 'browser'; export type DeviceFrameId = 'iphone-16-pro' | (string & {}); export interface DeviceFrameDefinition { id: string; name: string; category: DeviceCategory; viewport: { width: number; height: number; }; } export type DeviceOrientation = 'portrait' | 'landscape'; export interface OrientationConfigData { screen: { logicalWidth: number; logicalHeight: number; scale: number; cornerRadius: number; }; viewport: { width: number; height: number; }; safeArea?: { top: number; bottom: number; left?: number; right?: number; }; statusBar?: { asset?: string; height: number; width: number; type?: StatusBarDeviceType; layout?: StatusBarLayout; }; homeIndicator?: { width: number; height: number; cornerRadius: number; bottomOffset: number; }; frame: { type: 'png' | 'svg'; asset: string; width: number; height: number; screenRect: { x: number; y: number; width: number; height: number; }; }; /** Rotation angle (degrees CW) applied to the frame image */ frameRotation?: number; /** When true, the frame is rendered behind the screen content */ frameBehindContent?: boolean; frameUrl?: string; /** Window border for frameless browser mockups */ windowBorder?: { color: string; width: number; radius: number; }; /** Dark-mode frame URL (browser category) */ frameDarkUrl?: string; /** Configurable zones for browser bar dynamic content */ browserBarZones?: { favicon?: { x: number; y: number; width: number; height: number; }; pageTitle?: { x: number; y: number; width: number; height: number; fontSize?: number; fontWeight?: number | string; textAlign?: 'left' | 'center' | 'right'; }; url?: { x: number; y: number; width: number; height: number; fontSize?: number; fontWeight?: number | string; textAlign?: 'left' | 'center' | 'right'; }; }; /** Which coded browser chrome style to render. Defaults to 'chrome'. */ browserStyle?: 'chrome' | 'safari'; adminShowStatusBar?: boolean; adminForcedSafeAreaColors?: { top?: string; bottom?: string; left?: string; right?: string; }; } export interface DeviceConfig { id: string; name: string; category: DeviceCategory; platform: string; frameOrientation?: 'portrait' | 'landscape'; supportedOrientations?: ('portrait' | 'landscape')[]; orientations?: { portrait?: OrientationConfigData; landscape?: OrientationConfigData; }; /** Row-level frame_url (shared fallback for all orientations) */ _rowFrameUrl?: string; screen: { logicalWidth: number; logicalHeight: number; scale: number; cornerRadius: number; }; viewport: { width: number; height: number; }; safeArea?: { top: number; bottom: number; left?: number; right?: number; }; statusBar?: { asset: string; height: number; width: number; type?: StatusBarDeviceType; layout?: StatusBarLayout; }; homeIndicator?: { width: number; height: number; cornerRadius: number; bottomOffset: number; }; frame: { type: 'png' | 'svg'; asset: string; width: number; height: number; screenRect: { x: number; y: number; width: number; height: number; }; }; frameRotation?: number; frameBehindContent?: boolean; windowBorder?: OrientationConfigData['windowBorder']; frameDarkUrl?: string; browserBarZones?: OrientationConfigData['browserBarZones']; browserStyle?: OrientationConfigData['browserStyle']; adminShowStatusBar?: OrientationConfigData['adminShowStatusBar']; adminForcedSafeAreaColors?: OrientationConfigData['adminForcedSafeAreaColors']; } export type MockupOrientation = 'portrait' | 'landscape'; export interface MockupOptions { orientation?: MockupOrientation; outputScale?: number; showStatusBar?: boolean; showSafeAreaTop?: boolean; showSafeAreaBottom?: boolean; showSafeAreaLeft?: boolean; showSafeAreaRight?: boolean; showHomeIndicator?: boolean; safeAreaTopColor?: string; safeAreaBottomColor?: string; safeAreaLeftColor?: string; safeAreaRightColor?: string; statusBar?: StatusBarConfig; browserBar?: BrowserBarConfig; /** Window border for frameless mockups (browser category) */ windowBorder?: { color: string; width: number; radius: number; }; /** Explicit color scheme override — bypasses auto-detection from edge colors */ colorScheme?: 'light' | 'dark'; /** * Viewport the screenshot was captured at (logical CSS px). For frameless * browser mockups this drives the frame geometry so the content area matches * the capture aspect ratio — without it, captures taken at a custom preset * viewport get stretched by the fit:'fill' resize into the device config's * logical dimensions. Ignored for devices with a frame asset (their screen * rect is fixed by the asset). */ viewport?: { width: number; height: number; }; } /** * Status bar default: shown only on phone mockups. Tablet, laptop, and browser * categories default to off (browser is additionally excluded at render time). * An explicit user choice (`mockupOptions.showStatusBar`) always overrides this. */ export declare function defaultShowStatusBar(category: DeviceCategory): boolean; /** * Resolve the two per-variant frame decisions shared by both render paths (CLI direct-upload * framing and the cloud legacy-multipart route): a variant's own `mockupOptions` wins, falling * back to the viewport-inferred orientation and the deprecated program-level `applyStatusBar`. * Pure + exported so the precedence is tested once instead of in two duplicated call sites. * * `showStatusBar` is left `undefined` when neither the variant nor the legacy fallback set it, * so `applyDeviceFrame` can apply the category-aware default (phones on, everything else off). */ export declare function resolveVariantFrameOptions(mockupOptions: MockupOptions | undefined, fallback: { orientation?: MockupOrientation; showStatusBar?: boolean; }): { orientation?: MockupOrientation; showStatusBar?: boolean; }; export interface ResolvedDeviceFrameDescriptor { id: string; name: string; category: DeviceCategory; orientation: MockupOrientation; /** Pre-computed viewport from the device config (content area) */ viewport?: { width: number; height: number; }; screen: { logicalWidth: number; logicalHeight: number; scale: number; cornerRadius: number; }; safeArea: { top: number; bottom: number; left?: number; right?: number; }; statusBar?: { asset?: string; height: number; width: number; type?: StatusBarDeviceType; layout?: StatusBarLayout; }; homeIndicator?: { width: number; height: number; cornerRadius: number; bottomOffset: number; }; frame: { type: 'png' | 'svg'; asset: string; width: number; height: number; }; frameUrl?: string; frameWidth: number; frameHeight: number; screenRect: { x: number; y: number; width: number; height: number; }; frameRotation: number; frameBehindContent: boolean; browserStyle?: OrientationConfigData['browserStyle']; disableOverlays: boolean; } export declare function invalidateDeviceConfigCache(): void; export declare function seedDeviceConfigs(configs: Record | null | undefined): void; export declare function resolveDeviceFrameDescriptor(id: DeviceFrameId, options?: { orientation?: MockupOrientation; }): Promise; export declare function rasterizeDeviceFrame(descriptor: ResolvedDeviceFrameDescriptor, outputScale: number): Promise; export declare function getDeviceFrames(): Promise; export declare function getDeviceFrame(id: DeviceFrameId): Promise; export declare function applyDeviceFrame(screenshot: Buffer, deviceId: DeviceFrameId, options?: MockupOptions): Promise;