@biela.dev/core 1.7.12 → 1.7.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +42 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -509
- package/dist/index.d.ts +27 -509
- package/dist/index.js +43 -7
- package/dist/index.js.map +1 -1
- package/dist/lite.cjs +24 -5
- package/dist/lite.cjs.map +1 -1
- package/dist/lite.d.cts +518 -4
- package/dist/lite.d.ts +518 -4
- package/dist/lite.js +24 -5
- package/dist/lite.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,518 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
declare function ptsToPx(pts: number, dpr: number): number;
|
|
11
|
-
/** Convert physical pixels to logical points */
|
|
12
|
-
declare function pxToPts(px: number, dpr: number): number;
|
|
13
|
-
/** Convert points to percentage of a total dimension */
|
|
14
|
-
declare function ptsToPercent(pts: number, total: number): number;
|
|
15
|
-
/** Apply uniform scale factor to any dimension value */
|
|
16
|
-
declare function scaleValue(value: number, scaleFactor: number): number;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Scale Engine Math — mirrors Xcode Simulator "fit to screen" behavior.
|
|
20
|
-
*
|
|
21
|
-
* Rules:
|
|
22
|
-
* 1. Inner content ALWAYS at 1:1 logical point size
|
|
23
|
-
* 2. Scale derived from available container space, never hardcoded
|
|
24
|
-
* 3. SVG frame chrome scales WITH content as one atomic unit
|
|
25
|
-
* 4. Host layout reserves SCALED dimensions
|
|
26
|
-
* 5. Maximum scale always 1.0 — never larger than the real device
|
|
27
|
-
*/
|
|
28
|
-
/** Discrete scale steps matching Xcode Simulator presets */
|
|
29
|
-
declare const SCALE_STEPS: readonly [0.25, 0.33, 0.5, 0.75, 1];
|
|
30
|
-
/**
|
|
31
|
-
* Compute the adaptive scale factor for a device in a given container.
|
|
32
|
-
* Mirrors Xcode "fit to screen" exactly.
|
|
33
|
-
*
|
|
34
|
-
* @param deviceWidth - Logical points width of the device screen
|
|
35
|
-
* @param deviceHeight - Logical points height of the device screen
|
|
36
|
-
* @param containerWidth - Available CSS px width of the host container
|
|
37
|
-
* @param containerHeight - Available CSS px height of the host container
|
|
38
|
-
* @param padding - Breathing room in px (subtracted from each side)
|
|
39
|
-
* @param maxScale - Upper cap — never exceed 1.0 (real device size)
|
|
40
|
-
* @param minScale - Lower floor — prevent invisibly small render
|
|
41
|
-
*/
|
|
42
|
-
declare function computeAdaptiveScale(deviceWidth: number, deviceHeight: number, containerWidth: number, containerHeight: number, padding?: number, maxScale?: number, minScale?: number): number;
|
|
43
|
-
/**
|
|
44
|
-
* Snap a raw scale to the nearest discrete step that does not exceed it.
|
|
45
|
-
* Fixed version: filters steps to only those <= raw, takes max.
|
|
46
|
-
* Falls back to raw if no step qualifies.
|
|
47
|
-
*/
|
|
48
|
-
declare function snapToStep(raw: number): number;
|
|
49
|
-
/**
|
|
50
|
-
* Derive host container size from device dimensions + scale.
|
|
51
|
-
* The host div occupies these dimensions in normal document flow.
|
|
52
|
-
*/
|
|
53
|
-
declare function computeHostSize(deviceWidth: number, deviceHeight: number, scale: number): {
|
|
54
|
-
width: number;
|
|
55
|
-
height: number;
|
|
56
|
-
};
|
|
57
|
-
/** Result returned by the scale computation */
|
|
58
|
-
interface AdaptiveScaleResult {
|
|
59
|
-
/** Computed scale factor e.g. 0.735 */
|
|
60
|
-
scale: number;
|
|
61
|
-
/** Host container width = deviceWidth * scale */
|
|
62
|
-
scaledWidth: number;
|
|
63
|
-
/** Host container height = deviceHeight * scale */
|
|
64
|
-
scaledHeight: number;
|
|
65
|
-
/** Raw logical pts — scaler div width */
|
|
66
|
-
deviceWidth: number;
|
|
67
|
-
/** Raw logical pts — scaler div height */
|
|
68
|
-
deviceHeight: number;
|
|
69
|
-
/** True when scale === maxScale (showing at real size) */
|
|
70
|
-
isAtMaxScale: boolean;
|
|
71
|
-
/** True when scale < maxScale (container is limiting) */
|
|
72
|
-
isConstrained: boolean;
|
|
73
|
-
/** Display string e.g. "73%" */
|
|
74
|
-
scalePercent: string;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Full scale computation returning all derived values.
|
|
78
|
-
*/
|
|
79
|
-
declare function computeFullScale(deviceWidth: number, deviceHeight: number, containerWidth: number, containerHeight: number, options?: {
|
|
80
|
-
padding?: number;
|
|
81
|
-
maxScale?: number;
|
|
82
|
-
minScale?: number;
|
|
83
|
-
snapToSteps?: boolean;
|
|
84
|
-
}): AdaptiveScaleResult;
|
|
85
|
-
|
|
86
|
-
interface ContainerSize {
|
|
87
|
-
width: number;
|
|
88
|
-
height: number;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Observes an element's content box dimensions via ResizeObserver.
|
|
92
|
-
* Foundation of adaptive scaling — same mechanism as Xcode's window resize detection.
|
|
93
|
-
*
|
|
94
|
-
* Safety features:
|
|
95
|
-
* 1. Clamps reported size to the visual viewport so a mis-sized parent
|
|
96
|
-
* can never cause the device frame to render larger than the screen.
|
|
97
|
-
* 2. When the element has 0 height (common AI-generated code mistake:
|
|
98
|
-
* height: auto or height: 100% without explicit ancestor height),
|
|
99
|
-
* uses a viewport-based fallback so the device still renders.
|
|
100
|
-
* 3. Listens for window resize to stay in sync.
|
|
101
|
-
*/
|
|
102
|
-
declare function useContainerSize(ref: RefObject<HTMLElement | null>): ContainerSize;
|
|
103
|
-
|
|
104
|
-
interface UseAdaptiveScaleOptions {
|
|
105
|
-
/** Device metadata (screen dimensions) */
|
|
106
|
-
device: DeviceMeta;
|
|
107
|
-
/** Container width from useContainerSize */
|
|
108
|
-
containerWidth: number;
|
|
109
|
-
/** Container height from useContainerSize */
|
|
110
|
-
containerHeight: number;
|
|
111
|
-
/** Breathing room in px (default: 24) */
|
|
112
|
-
padding?: number;
|
|
113
|
-
/** Upper cap — never exceed 1.0 (default: 1.0) */
|
|
114
|
-
maxScale?: number;
|
|
115
|
-
/** Lower floor (default: 0.1) */
|
|
116
|
-
minScale?: number;
|
|
117
|
-
/** Snap to discrete 25/33/50/75/100% steps (default: false) */
|
|
118
|
-
snapToSteps?: boolean;
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* Full adaptive scale hook — mirrors Xcode "fit to screen".
|
|
122
|
-
* Recalculates live as container resizes via ResizeObserver.
|
|
123
|
-
*/
|
|
124
|
-
declare function useAdaptiveScale(options: UseAdaptiveScaleOptions): AdaptiveScaleResult;
|
|
125
|
-
|
|
126
|
-
interface UseDeviceContractResult {
|
|
127
|
-
contract: DeviceLayoutContract;
|
|
128
|
-
cssVariables: DeviceLayoutContract["cssVariables"];
|
|
129
|
-
contentZone: DeviceLayoutContract["contentZone"]["portrait"];
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Hook to access the Device Layout Contract for a given device.
|
|
133
|
-
* Returns the contract, CSS variables, and content zone for the current orientation.
|
|
134
|
-
*/
|
|
135
|
-
declare function useDeviceContract(deviceId: string, orientation?: "portrait" | "landscape"): UseDeviceContractResult;
|
|
136
|
-
|
|
137
|
-
interface VolumeState {
|
|
138
|
-
/** Current volume level 0–1 (16 steps, each = 1/16) */
|
|
139
|
-
level: number;
|
|
140
|
-
/** Is audio muted? */
|
|
141
|
-
muted: boolean;
|
|
142
|
-
/** Should the HUD be visible? */
|
|
143
|
-
hudVisible: boolean;
|
|
144
|
-
/** Increase volume by one step */
|
|
145
|
-
volumeUp: () => void;
|
|
146
|
-
/** Decrease volume by one step */
|
|
147
|
-
volumeDown: () => void;
|
|
148
|
-
/** Toggle mute */
|
|
149
|
-
toggleMute: () => void;
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Volume control hook — 16-step volume with auto-hiding HUD.
|
|
153
|
-
* Applies volume to all <audio> and <video> elements inside `.bielaframe-content`.
|
|
154
|
-
*/
|
|
155
|
-
declare function useVolumeControl(initialVolume?: number): VolumeState;
|
|
156
|
-
|
|
157
|
-
interface ScreenPowerState {
|
|
158
|
-
/** Is the screen off? */
|
|
159
|
-
isOff: boolean;
|
|
160
|
-
/** Toggle screen on/off */
|
|
161
|
-
toggle: () => void;
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Screen power hook — manages on/off state for the power button.
|
|
165
|
-
*/
|
|
166
|
-
declare function useScreenPower(): ScreenPowerState;
|
|
167
|
-
|
|
168
|
-
interface UseOrientationResult {
|
|
169
|
-
/** Current orientation */
|
|
170
|
-
orientation: "portrait" | "landscape";
|
|
1
|
+
export { AdaptiveScaleResult, ButtonName, CustomSVGStore, DeviceCompare, DeviceCompareProps, DeviceErrorBoundary, DeviceFrame, DeviceFrameProps, DynamicStatusBar, HardwareButtons, SCALE_STEPS, SVGCropArea, SVGOverrideEntry, SVGScreenRect, SafeAreaOverlay, SafeAreaView, ScaleBar, ScreenPowerState, StatusBarIndicators, UseAdaptiveScaleOptions, UseOrientationResult, VolumeHUD, VolumeState, computeAdaptiveScale, computeFullScale, computeHostSize, getCustomSVGStore, ptsToPercent, ptsToPx, pxToPts, registerCustomDeviceSVG, registerDeviceSVG, scaleValue, snapToStep, useAdaptiveScale, useContainerSize, useDeviceContract, useOrientation, useScreenPower, useVolumeControl } from './lite.cjs';
|
|
2
|
+
import 'react';
|
|
3
|
+
import '@biela.dev/devices';
|
|
4
|
+
import 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
type FoldState = "folded" | "open";
|
|
7
|
+
interface UseFoldStateResult {
|
|
8
|
+
/** Current fold state */
|
|
9
|
+
foldState: FoldState;
|
|
171
10
|
/** Convenience boolean */
|
|
172
|
-
|
|
173
|
-
/**
|
|
11
|
+
isOpen: boolean;
|
|
12
|
+
/** The device ID to pass to DeviceFrame (switches between folded/open variant) */
|
|
13
|
+
deviceId: string;
|
|
14
|
+
/** Toggle between folded and open */
|
|
174
15
|
toggle: () => void;
|
|
175
|
-
/** Set
|
|
176
|
-
|
|
16
|
+
/** Set fold state directly */
|
|
17
|
+
setFoldState: (state: FoldState) => void;
|
|
177
18
|
}
|
|
178
19
|
/**
|
|
179
|
-
* Hook for managing device
|
|
20
|
+
* Hook for managing foldable device fold/unfold state.
|
|
180
21
|
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
* <DeviceFrame deviceId="iphone-17-pro" orientation={orientation} />
|
|
185
|
-
* ```
|
|
186
|
-
*/
|
|
187
|
-
declare function useOrientation(initial?: "portrait" | "landscape"): UseOrientationResult;
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Resolve device SVG component by ID.
|
|
191
|
-
* This is a lookup registry — devices register their SVG components here.
|
|
192
|
-
*/
|
|
193
|
-
type DeviceSVGComponent = React.ComponentType<{
|
|
194
|
-
colorScheme?: "light" | "dark";
|
|
195
|
-
style?: React.CSSProperties;
|
|
196
|
-
}>;
|
|
197
|
-
interface FrameInfo {
|
|
198
|
-
bezelTop: number;
|
|
199
|
-
bezelBottom: number;
|
|
200
|
-
bezelLeft: number;
|
|
201
|
-
bezelRight: number;
|
|
202
|
-
totalWidth: number;
|
|
203
|
-
totalHeight: number;
|
|
204
|
-
screenWidth: number;
|
|
205
|
-
screenHeight: number;
|
|
206
|
-
screenRadius: number;
|
|
207
|
-
}
|
|
208
|
-
/** Register a device SVG component for use with DeviceFrame */
|
|
209
|
-
declare function registerDeviceSVG(deviceId: string, component: DeviceSVGComponent, frame: FrameInfo): void;
|
|
210
|
-
/** Optional crop area to rewrite the SVG viewBox (e.g., to exclude side buttons) */
|
|
211
|
-
interface SVGCropArea {
|
|
212
|
-
x: number;
|
|
213
|
-
y: number;
|
|
214
|
-
width: number;
|
|
215
|
-
height: number;
|
|
216
|
-
}
|
|
217
|
-
/** Screen rectangle in SVG native coordinates — used to punch a transparent hole */
|
|
218
|
-
interface SVGScreenRect {
|
|
219
|
-
x: number;
|
|
220
|
-
y: number;
|
|
221
|
-
width: number;
|
|
222
|
-
height: number;
|
|
223
|
-
rx?: number;
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* Register a custom device SVG from a raw SVG string.
|
|
227
|
-
*
|
|
228
|
-
* Creates a React component that renders the SVG with scoped IDs
|
|
229
|
-
* and registers it in the SVG_REGISTRY.
|
|
230
|
-
*
|
|
231
|
-
* The inner <svg> element is rewritten to use width="100%" height="100%"
|
|
232
|
-
* so it fills the parent container (which is sized by the frame info).
|
|
233
|
-
*
|
|
234
|
-
* If `cropViewBox` is provided, the SVG viewBox is set to that area,
|
|
235
|
-
* cropping out external elements like side buttons.
|
|
236
|
-
*
|
|
237
|
-
* If `screenRect` is provided, a mask is injected to punch a transparent
|
|
238
|
-
* hole where the screen is, so content underneath shows through.
|
|
239
|
-
*/
|
|
240
|
-
declare function registerCustomDeviceSVG(deviceId: string, svgString: string, frame: FrameInfo, cropViewBox?: SVGCropArea, screenRect?: SVGScreenRect): void;
|
|
241
|
-
interface DeviceFrameProps {
|
|
242
|
-
/** Device ID e.g. "iphone-16-pro" */
|
|
243
|
-
device?: string;
|
|
244
|
-
/** Alias for device — accepts either prop name */
|
|
245
|
-
deviceId?: string;
|
|
246
|
-
/** Orientation */
|
|
247
|
-
orientation?: "portrait" | "landscape";
|
|
248
|
-
/** Scale mode: "fit" auto-scales, "manual" uses manualScale value */
|
|
249
|
-
scaleMode?: "fit" | "manual" | "steps";
|
|
250
|
-
/** Manual scale override (0.1–1.0), only when scaleMode="manual" */
|
|
251
|
-
manualScale?: number;
|
|
252
|
-
/** Show safe zone overlay (dev tool) */
|
|
253
|
-
showSafeAreaOverlay?: boolean;
|
|
254
|
-
/** Show DLC JSON panel */
|
|
255
|
-
showDLCPanel?: boolean;
|
|
256
|
-
/** Show scale bar (default: true) */
|
|
257
|
-
showScaleBar?: boolean;
|
|
258
|
-
/** Frame color scheme */
|
|
259
|
-
colorScheme?: "light" | "dark";
|
|
260
|
-
/** Callback when DLC is ready */
|
|
261
|
-
onContractReady?: (dlc: DeviceLayoutContract) => void;
|
|
262
|
-
/** Callback when scale changes */
|
|
263
|
-
onScaleChange?: (scale: number) => void;
|
|
264
|
-
/** Content rendered inside the device screen at real resolution */
|
|
265
|
-
children?: ReactNode;
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* DeviceFrame — the main BielaFrame component.
|
|
22
|
+
* Returns the appropriate device ID for the current fold state.
|
|
23
|
+
* When the returned deviceId changes, DeviceFrame's CSS transitions
|
|
24
|
+
* animate the container resize automatically (same timing as orientation).
|
|
269
25
|
*
|
|
270
|
-
*
|
|
271
|
-
* - Inner content renders at 1:1 device resolution (e.g. 402×874px)
|
|
272
|
-
* - Visual presentation scales via CSS transform: scale()
|
|
273
|
-
* - The AI component always thinks it's on a real device
|
|
26
|
+
* Convention: the open variant uses `${baseDeviceId}-open`.
|
|
274
27
|
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
* │ └── children (AI-generated component)
|
|
281
|
-
* ├── SVG frame overlay (pointer-events: none)
|
|
282
|
-
* └── SafeAreaOverlay (optional dev tool)
|
|
283
|
-
* └── ScaleBar (outside scaler, in normal flow)
|
|
284
|
-
*/
|
|
285
|
-
declare function DeviceFrame({ device: deviceProp, deviceId, orientation, scaleMode, manualScale, showSafeAreaOverlay, showScaleBar, colorScheme, onContractReady, onScaleChange, children, }: DeviceFrameProps): react_jsx_runtime.JSX.Element;
|
|
286
|
-
|
|
287
|
-
interface DeviceCompareProps {
|
|
288
|
-
/** First device ID */
|
|
289
|
-
deviceA: string;
|
|
290
|
-
/** Second device ID */
|
|
291
|
-
deviceB: string;
|
|
292
|
-
/** Orientation for both devices */
|
|
293
|
-
orientation?: "portrait" | "landscape";
|
|
294
|
-
/** Frame color scheme */
|
|
295
|
-
colorScheme?: "light" | "dark";
|
|
296
|
-
/** Show safe area overlays */
|
|
297
|
-
showSafeAreaOverlay?: boolean;
|
|
298
|
-
/** Show scale bars */
|
|
299
|
-
showScaleBar?: boolean;
|
|
300
|
-
/** Layout direction — "auto" picks row for portrait, column for landscape */
|
|
301
|
-
layout?: "horizontal" | "vertical" | "auto";
|
|
302
|
-
/** Gap between the two frames in px (default: 24) */
|
|
303
|
-
gap?: number;
|
|
304
|
-
/** Content rendered inside BOTH device frames */
|
|
305
|
-
children?: ReactNode;
|
|
306
|
-
/** Content for device A only (overrides children for A) */
|
|
307
|
-
childrenA?: ReactNode;
|
|
308
|
-
/** Content for device B only (overrides children for B) */
|
|
309
|
-
childrenB?: ReactNode;
|
|
310
|
-
/** Callback when device A contract is ready */
|
|
311
|
-
onContractReadyA?: (dlc: DeviceLayoutContract) => void;
|
|
312
|
-
/** Callback when device B contract is ready */
|
|
313
|
-
onContractReadyB?: (dlc: DeviceLayoutContract) => void;
|
|
314
|
-
}
|
|
315
|
-
/**
|
|
316
|
-
* DeviceCompare — renders two device frames side-by-side for comparison.
|
|
317
|
-
*
|
|
318
|
-
* Layout defaults to "auto": horizontal (row) in portrait, vertical (column) in landscape.
|
|
319
|
-
* Each frame auto-scales independently to fit its half of the container.
|
|
320
|
-
*
|
|
321
|
-
* Must be placed inside a container with explicit width and height.
|
|
322
|
-
*/
|
|
323
|
-
declare function DeviceCompare({ deviceA, deviceB, orientation, colorScheme, showSafeAreaOverlay, showScaleBar, layout, gap, children, childrenA, childrenB, onContractReadyA, onContractReadyB, }: DeviceCompareProps): react_jsx_runtime.JSX.Element;
|
|
324
|
-
|
|
325
|
-
interface Props {
|
|
326
|
-
children: ReactNode;
|
|
327
|
-
fallback?: ReactNode;
|
|
328
|
-
}
|
|
329
|
-
interface State {
|
|
330
|
-
hasError: boolean;
|
|
331
|
-
error: Error | null;
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* Error Boundary wrapping the children slot in DeviceFrame.
|
|
335
|
-
* AI-generated components will crash — this catches them gracefully
|
|
336
|
-
* and renders a fallback inside the device screen area.
|
|
337
|
-
*/
|
|
338
|
-
declare class DeviceErrorBoundary extends Component<Props, State> {
|
|
339
|
-
constructor(props: Props);
|
|
340
|
-
static getDerivedStateFromError(error: Error): State;
|
|
341
|
-
componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
|
|
342
|
-
render(): ReactNode;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
interface SafeAreaViewProps {
|
|
346
|
-
/** Which edges to apply safe area padding to (default: all) */
|
|
347
|
-
edges?: Array<"top" | "bottom" | "left" | "right">;
|
|
348
|
-
children: ReactNode;
|
|
349
|
-
style?: CSSProperties;
|
|
350
|
-
}
|
|
351
|
-
/**
|
|
352
|
-
* Convenience wrapper that applies safe area padding via CSS variables.
|
|
353
|
-
* Uses var(--safe-top), var(--safe-bottom), etc. — injected by DeviceFrame.
|
|
354
|
-
*
|
|
355
|
-
* Usage inside a generated app:
|
|
356
|
-
* <SafeAreaView edges={["top", "bottom"]}>
|
|
357
|
-
* {content that never overlaps hardware elements}
|
|
358
|
-
* </SafeAreaView>
|
|
359
|
-
*/
|
|
360
|
-
declare function SafeAreaView({ edges, children, style }: SafeAreaViewProps): react_jsx_runtime.JSX.Element;
|
|
361
|
-
|
|
362
|
-
interface SafeAreaOverlayProps {
|
|
363
|
-
contract: DeviceLayoutContract;
|
|
364
|
-
orientation: "portrait" | "landscape";
|
|
365
|
-
}
|
|
366
|
-
/**
|
|
367
|
-
* Dev overlay showing safe zones when showSafeAreaOverlay={true}.
|
|
368
|
-
* Does NOT affect layout — position: absolute, pointerEvents: none.
|
|
369
|
-
*
|
|
370
|
-
* Colors:
|
|
371
|
-
* - Red: status bar zone + home indicator zone (restricted)
|
|
372
|
-
* - Orange: hardware overlay (Dynamic Island / punch-hole)
|
|
373
|
-
* - Green: content zone (usable area)
|
|
374
|
-
* - White dimension labels at each edge
|
|
375
|
-
*/
|
|
376
|
-
declare function SafeAreaOverlay({ contract, orientation }: SafeAreaOverlayProps): react_jsx_runtime.JSX.Element;
|
|
377
|
-
|
|
378
|
-
interface ScaleBarProps {
|
|
379
|
-
deviceName: string;
|
|
380
|
-
deviceWidth: number;
|
|
381
|
-
deviceHeight: number;
|
|
382
|
-
scale: number;
|
|
383
|
-
scalePercent: string;
|
|
384
|
-
isAtMaxScale: boolean;
|
|
385
|
-
isConstrained: boolean;
|
|
386
|
-
onScaleChange?: (scale: number) => void;
|
|
387
|
-
onFit?: () => void;
|
|
388
|
-
onRealSize?: () => void;
|
|
389
|
-
}
|
|
390
|
-
/**
|
|
391
|
-
* Persistent footer below device showing scale state:
|
|
392
|
-
* [ iPhone 16 Pro · 402×874pt | ━━━●━━━━━ 66% | Fit ↗ | 1:1 ]
|
|
393
|
-
*
|
|
394
|
-
* Accessibility:
|
|
395
|
-
* - role="slider" with aria-valuenow on scrubber
|
|
396
|
-
* - aria-label on the component
|
|
397
|
-
* - aria-live="polite" on scale announcements
|
|
398
|
-
*/
|
|
399
|
-
declare function ScaleBar({ deviceName, deviceWidth, deviceHeight, scale, scalePercent, isAtMaxScale, isConstrained, onScaleChange, onFit, onRealSize, }: ScaleBarProps): react_jsx_runtime.JSX.Element;
|
|
400
|
-
|
|
401
|
-
interface VolumeHUDProps {
|
|
402
|
-
level: number;
|
|
403
|
-
muted: boolean;
|
|
404
|
-
visible: boolean;
|
|
405
|
-
platform: "ios" | "android";
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* Volume HUD overlay — iOS-style vertical pill or Android-style horizontal bar.
|
|
409
|
-
*/
|
|
410
|
-
declare function VolumeHUD({ level, muted, visible, platform }: VolumeHUDProps): react_jsx_runtime.JSX.Element;
|
|
411
|
-
|
|
412
|
-
type ButtonName = "volumeUp" | "volumeDown" | "power" | "actionButton" | "cameraControl";
|
|
413
|
-
interface HardwareButtonsProps {
|
|
414
|
-
/** The container element that holds the SVG frame overlay */
|
|
415
|
-
frameContainerRef: React.RefObject<HTMLDivElement | null>;
|
|
416
|
-
/** Callbacks for button actions */
|
|
417
|
-
onButtonPress?: (button: ButtonName) => void;
|
|
418
|
-
/** Whether interactive buttons are enabled */
|
|
419
|
-
enabled?: boolean;
|
|
420
|
-
/** Current orientation — triggers button position re-discovery */
|
|
421
|
-
orientation?: "portrait" | "landscape";
|
|
422
|
-
}
|
|
423
|
-
/**
|
|
424
|
-
* HardwareButtons — creates invisible click-target overlays positioned
|
|
425
|
-
* over the SVG button elements.
|
|
426
|
-
*
|
|
427
|
-
* Because the SVG frame overlay has pointer-events: none (so content
|
|
428
|
-
* underneath remains interactive), we can't attach event listeners directly
|
|
429
|
-
* to SVG elements. Instead, we:
|
|
430
|
-
* 1. Find all [data-button] elements in the SVG
|
|
431
|
-
* 2. Read their bounding rects relative to the scaler container
|
|
432
|
-
* 3. Render transparent absolutely-positioned divs at those positions
|
|
433
|
-
* 4. Handle mouse/touch events on those divs
|
|
434
|
-
* 5. Apply press animation to the original SVG elements
|
|
435
|
-
*/
|
|
436
|
-
declare function HardwareButtons({ frameContainerRef, onButtonPress, enabled, orientation, }: HardwareButtonsProps): ReactElement | null;
|
|
437
|
-
|
|
438
|
-
interface DynamicStatusBarProps {
|
|
439
|
-
contract: DeviceLayoutContract;
|
|
440
|
-
orientation: "portrait" | "landscape";
|
|
441
|
-
colorScheme: "light" | "dark";
|
|
442
|
-
/** Show a live updating clock (default: true) */
|
|
443
|
-
showLiveClock?: boolean;
|
|
444
|
-
/** Fixed time string override (e.g. "9:41") — disables live clock */
|
|
445
|
-
fixedTime?: string;
|
|
446
|
-
}
|
|
447
|
-
/**
|
|
448
|
-
* DynamicStatusBar — renders ONLY a live clock overlay.
|
|
449
|
-
*
|
|
450
|
-
* The SVG frame already provides all decorative status bar elements
|
|
451
|
-
* (signal bars, wifi, battery). This component only replaces the
|
|
452
|
-
* static time text (e.g. "9:41") with a live-updating clock.
|
|
453
|
-
*
|
|
454
|
-
* It renders a small opaque patch behind the clock text so the
|
|
455
|
-
* SVG's baked-in static time is fully covered, then draws the
|
|
456
|
-
* live time on top.
|
|
457
|
-
*
|
|
458
|
-
* Platform-specific clock positions:
|
|
459
|
-
* - iOS Dynamic Island: left side, paddingLeft ~20
|
|
460
|
-
* - iOS Notch: left side, paddingLeft ~20
|
|
461
|
-
* - iOS SE: centered
|
|
462
|
-
* - Android: left side, paddingLeft ~16
|
|
463
|
-
*/
|
|
464
|
-
declare function DynamicStatusBar({ contract, orientation, colorScheme, showLiveClock, fixedTime, }: DynamicStatusBarProps): react_jsx_runtime.JSX.Element | null;
|
|
465
|
-
|
|
466
|
-
interface StatusBarIndicatorsProps {
|
|
467
|
-
platform: "ios" | "android";
|
|
468
|
-
colorScheme: "light" | "dark";
|
|
469
|
-
}
|
|
470
|
-
/**
|
|
471
|
-
* Static decorative status bar indicators — signal, wifi, battery.
|
|
472
|
-
* Rendered as small inline SVGs, platform-specific styling.
|
|
473
|
-
*/
|
|
474
|
-
declare function StatusBarIndicators({ platform, colorScheme }: StatusBarIndicatorsProps): react_jsx_runtime.JSX.Element;
|
|
475
|
-
|
|
476
|
-
interface SVGOverrideEntry {
|
|
477
|
-
deviceId: string;
|
|
478
|
-
svgString: string;
|
|
479
|
-
bezelTop: number;
|
|
480
|
-
bezelBottom: number;
|
|
481
|
-
bezelLeft: number;
|
|
482
|
-
bezelRight: number;
|
|
483
|
-
screenRect?: SVGScreenRect;
|
|
484
|
-
updatedAt: string;
|
|
485
|
-
}
|
|
486
|
-
/**
|
|
487
|
-
* Persistent storage for custom SVG overrides.
|
|
488
|
-
*
|
|
489
|
-
* When a user uploads a custom SVG frame for a device, it's stored in
|
|
490
|
-
* localStorage and automatically applied on subsequent imports.
|
|
491
|
-
*
|
|
492
|
-
* SSR-safe: falls back to no-op storage when localStorage is unavailable.
|
|
28
|
+
* ```tsx
|
|
29
|
+
* const { deviceId, toggle, isOpen } = useFoldState("galaxy-z-fold-7");
|
|
30
|
+
* <button onClick={toggle}>{isOpen ? "Fold" : "Unfold"}</button>
|
|
31
|
+
* <DeviceFrame deviceId={deviceId} />
|
|
32
|
+
* ```
|
|
493
33
|
*/
|
|
494
|
-
declare
|
|
495
|
-
private storage;
|
|
496
|
-
constructor(storage?: Storage | null);
|
|
497
|
-
/** Load all stored overrides */
|
|
498
|
-
getAll(): Record<string, SVGOverrideEntry>;
|
|
499
|
-
/** Save an override and register it in the SVG registry */
|
|
500
|
-
save(entry: SVGOverrideEntry): void;
|
|
501
|
-
/** Remove an override (revert to built-in) */
|
|
502
|
-
remove(deviceId: string): void;
|
|
503
|
-
/** Check if a device has a custom override */
|
|
504
|
-
has(deviceId: string): boolean;
|
|
505
|
-
/** Get a single override by device ID */
|
|
506
|
-
get(deviceId: string): SVGOverrideEntry | undefined;
|
|
507
|
-
/**
|
|
508
|
-
* Apply all stored overrides to the SVG registry.
|
|
509
|
-
* Called during auto-registration to restore user customizations.
|
|
510
|
-
*/
|
|
511
|
-
applyAll(): void;
|
|
512
|
-
private applyEntry;
|
|
513
|
-
private persist;
|
|
514
|
-
}
|
|
515
|
-
/** Get the default CustomSVGStore instance (singleton, uses localStorage) */
|
|
516
|
-
declare function getCustomSVGStore(): CustomSVGStore;
|
|
34
|
+
declare function useFoldState(baseDeviceId: string, initial?: FoldState): UseFoldStateResult;
|
|
517
35
|
|
|
518
|
-
export { type
|
|
36
|
+
export { type FoldState, type UseFoldStateResult, useFoldState };
|