zelune 1.5.4 → 1.5.5
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/CHANGELOG.md +13 -0
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/plugins/viteReactNativeFallbackPlugin.ts +13 -53
- package/src/canvas/CanvasApp.tsx +75 -22
- package/src/canvas/FlowArrowsLayer.tsx +2 -2
- package/src/canvas/ScreenArtboard.tsx +17 -3
- package/src/main.tsx +1 -0
- package/src/runtime/MockNavigationContext.tsx +488 -37
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the **Zelune — React Native Visual Canvas & MCP** project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.5.5] - 2026-10-23
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Zod, React Hook Form & Sizing Utility Shims (`src/runtime/MockNavigationContext.tsx`, `plugins/viteReactNativeFallbackPlugin.ts`)**: Added `createSafeChainableProxy()` / `SAFE_CHAINABLE_PROXY` (wrapping a callable function target in a recursive `Proxy` for unlimited method/property chains), full Zod schema shims (`z`, `string`, `number`, `boolean`, `object`, `array`, `union`, `literal`, `record`, `tuple`, `any`, `unknown`, `optional`, `nullable`, `coerce`, `zodResolver`, `yupResolver`), React Hook Form shims (`useForm`, `useFormContext`, `FormProvider`, `useWatch`, `useFieldArray`, `Controller` supporting `render={({ field, fieldState, formState }) => ...}`), and React Native responsive sizing/classname helpers (`scale`, `verticalScale`, `moderateScale`, `RFValue`, `RFPercentage`, `wp`, `hp`, `clsx`, `cn`, `twMerge`).
|
|
9
|
+
- **Universal Unmounted React Context Fallback (`src/runtime/MockNavigationContext.tsx`, `src/main.tsx`)**: Wrapped `React.createContext` and `React.useContext` (after `react-native-web` initializes its internal contexts) with `SAFE_CONTEXT_FALLBACK` (pre-populated with preview `user`, `profile`, `session`, `isAuthenticated: true`, `isLoading: false`, `colors`, `items: []`, `signIn`, `signOut`, `logout`, `refresh`, etc.) so screens rendered outside their root `app/_layout.tsx` providers never throw `useAuth must be used within an AuthProvider` (or `useTheme` / `useCart` / `useNotifications` equivalents).
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- **Eliminated Canvas Panning Vibration / Jitter (`src/canvas/CanvasApp.tsx`, `src/canvas/ScreenArtboard.tsx`, `src/canvas/FlowArrowsLayer.tsx`)**:
|
|
13
|
+
- Updated `handleCanvasWheel` and `handleCanvasPointerDown` in `CanvasApp.tsx` to mutate `worldContainerRef.current.style.transform` (`translate3d(...) scale(...)`) directly on the DOM node with device-pixel rounding (`snapToDevicePixel`) and debounce `setPan` state synchronization to 120ms after movement stops, eliminating 120Hz React re-renders of all artboards while panning.
|
|
14
|
+
- Wrapped `ScreenArtboard` and `FlowArrowsLayer` in `React.memo`, scoped `selectedElement` and `inspectedElement` props to only the active screen, and suppressed `handlePointerMoveCapture` (`computeRelativeBox` / `setHoverBox`) while the canvas is moving or pointer buttons are pressed.
|
|
15
|
+
- Stabilized `useFocusEffect`, `useSegments`, `useQuery`, `useInfiniteQuery`, `useMutation`, and `useQueryClient` in `MockNavigationContext.tsx` so screens with inline `useFocusEffect` or `useEffect` dependencies do not trigger continuous re-render loops.
|
|
16
|
+
- **Light-Themed Screen Root Background & Image Overflow (`src/canvas/ScreenArtboard.tsx`)**: Set `backgroundColor: '#FFFFFF'`, `color: '#0F172A'`, `overflowY: 'auto'`, `overflowX: 'hidden'`, `minWidth: 0`, and `minHeight: 0` on the inner `<ScreenComponent />` mount container so screens that rely on React Native's default white root window background render with proper contrast and horizontally overflowing images are clipped to the 393px device frame.
|
|
17
|
+
|
|
5
18
|
## [1.5.4] - 2026-10-23
|
|
6
19
|
|
|
7
20
|
### Added
|
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zelune",
|
|
3
3
|
"displayName": "Zelune — React Native Visual Canvas",
|
|
4
|
-
"version": "1.5.
|
|
4
|
+
"version": "1.5.5",
|
|
5
5
|
"description": "Zelune MCP & Figma-style multi-screen React Native visual canvas with live surgical AST patching and navigation flow visualization for Google Antigravity.",
|
|
6
6
|
"logo": "assets/logo.svg",
|
|
7
7
|
"suggestedPrompts": [
|
|
@@ -484,53 +484,18 @@ export function viteReactNativeFallbackPlugin(
|
|
|
484
484
|
`import { View } from 'react-native';`,
|
|
485
485
|
`export * from ${JSON.stringify(normalizedShimPath)};`,
|
|
486
486
|
`import * as KnownShims from ${JSON.stringify(normalizedShimPath)};`,
|
|
487
|
-
`import DefaultShim from ${JSON.stringify(normalizedShimPath)};`,
|
|
488
|
-
`function createSafeProxy() {`,
|
|
489
|
-
` const fnTarget = function(...args) {`,
|
|
490
|
-
` if (args.length === 1 && (typeof args[0] === 'number' || typeof args[0] === 'string')) {`,
|
|
491
|
-
` return args[0];`,
|
|
492
|
-
` }`,
|
|
493
|
-
` return proxy;`,
|
|
494
|
-
` };`,
|
|
495
|
-
` const arrTarget = [];`,
|
|
496
|
-
` const proxy = new Proxy(arrTarget, {`,
|
|
497
|
-
` get(target, prop) {`,
|
|
498
|
-
` if (prop === 'length') return 0;`,
|
|
499
|
-
` if (prop === Symbol.iterator) {`,
|
|
500
|
-
` return function* () {`,
|
|
501
|
-
` yield proxy;`,
|
|
502
|
-
` yield fnTarget;`,
|
|
503
|
-
` };`,
|
|
504
|
-
` }`,
|
|
505
|
-
` if (prop === Symbol.toPrimitive) {`,
|
|
506
|
-
` return (hint) => (hint === 'number' ? 0 : '');`,
|
|
507
|
-
` }`,
|
|
508
|
-
` if (prop === 'valueOf') return () => 0;`,
|
|
509
|
-
` if (prop === 'toString') return () => '';`,
|
|
510
|
-
` if (prop === 'then') {`,
|
|
511
|
-
` return (onFulfilled, onRejected) => Promise.resolve({ data: null, error: null }).then(onFulfilled, onRejected);`,
|
|
512
|
-
` }`,
|
|
513
|
-
` if (prop === 'catch') return () => Promise.resolve({ data: null, error: null });`,
|
|
514
|
-
` if (prop === 'finally') return (cb) => Promise.resolve({ data: null, error: null }).finally(cb);`,
|
|
515
|
-
` if (prop === 'isLoading' || prop === 'loading' || prop === 'isPending' || prop === 'isError' || prop === 'disabled') return false;`,
|
|
516
|
-
` if (prop === 'isReady' || prop === 'granted' || prop === 'isSuccess' || prop === ' isAuthenticated') return true;`,
|
|
517
|
-
` if (prop === 'error' || prop === 'user' || prop === 'session') return null;`,
|
|
518
|
-
` if (prop === 'status') return 'granted';`,
|
|
519
|
-
` if (prop === 'data' || prop === 'items' || prop === 'results' || prop === 'list') return [];`,
|
|
520
|
-
` if (prop === 'ref') return { current: null };`,
|
|
521
|
-
` if (prop === 'value') return 0;`,
|
|
522
|
-
` if (prop === 'map' || prop === 'filter' || prop === 'slice' || prop === 'flatMap') return () => [];`,
|
|
523
|
-
` if (prop === 'forEach') return () => undefined;`,
|
|
524
|
-
` if (prop === 'reduce') return (_fn, init) => init;`,
|
|
525
|
-
` if (prop in target) return Reflect.get(target, prop);`,
|
|
526
|
-
` return fnTarget;`,
|
|
527
|
-
` },`,
|
|
528
|
-
` });`,
|
|
529
|
-
` return proxy;`,
|
|
530
|
-
`}`,
|
|
487
|
+
`import DefaultShim, { SAFE_CHAINABLE_PROXY } from ${JSON.stringify(normalizedShimPath)};`,
|
|
531
488
|
`function createUniversalComponentStub(displayName) {`,
|
|
532
489
|
` const Comp = React.forwardRef(function UniversalNativeStub(props, ref) {`,
|
|
533
|
-
`
|
|
490
|
+
` if (typeof props?.render === 'function') {`,
|
|
491
|
+
` return props.render({`,
|
|
492
|
+
` field: { name: props?.name ?? '', value: props?.defaultValue ?? '', onChange: () => undefined, onBlur: () => undefined, ref: () => undefined },`,
|
|
493
|
+
` fieldState: { invalid: false, isTouched: false, isDirty: false, error: undefined },`,
|
|
494
|
+
` formState: { errors: {}, isSubmitting: false, isValid: true },`,
|
|
495
|
+
` });`,
|
|
496
|
+
` }`,
|
|
497
|
+
` const content = typeof props?.children === 'function' ? props.children(SAFE_CHAINABLE_PROXY) : props?.children;`,
|
|
498
|
+
` return React.createElement(View, { style: props?.style, ref }, content);`,
|
|
534
499
|
` });`,
|
|
535
500
|
` Comp.displayName = displayName;`,
|
|
536
501
|
` return new Proxy(Comp, {`,
|
|
@@ -539,21 +504,16 @@ export function viteReactNativeFallbackPlugin(
|
|
|
539
504
|
` if (typeof prop === 'string' && /^[A-Z]/.test(prop)) {`,
|
|
540
505
|
` return createUniversalComponentStub(displayName + '.' + prop);`,
|
|
541
506
|
` }`,
|
|
542
|
-
` return
|
|
507
|
+
` return SAFE_CHAINABLE_PROXY;`,
|
|
543
508
|
` },`,
|
|
544
509
|
` });`,
|
|
545
510
|
`}`,
|
|
546
511
|
`function resolveStubExport(name) {`,
|
|
547
512
|
` if (name in KnownShims) return KnownShims[name];`,
|
|
548
513
|
` if (DefaultShim && name in DefaultShim) return DefaultShim[name];`,
|
|
549
|
-
` if (/^use[A-Z]/.test(name)) return () =>
|
|
514
|
+
` if (/^use[A-Z]/.test(name)) return () => SAFE_CHAINABLE_PROXY;`,
|
|
550
515
|
` if (/^[A-Z]/.test(name)) return createUniversalComponentStub(name);`,
|
|
551
|
-
` return
|
|
552
|
-
` if (args.length === 1 && (typeof args[0] === 'number' || typeof args[0] === 'string')) {`,
|
|
553
|
-
` return args[0];`,
|
|
554
|
-
` }`,
|
|
555
|
-
` return createSafeProxy();`,
|
|
556
|
-
` };`,
|
|
516
|
+
` return SAFE_CHAINABLE_PROXY;`,
|
|
557
517
|
`}`,
|
|
558
518
|
`export default DefaultShim;`,
|
|
559
519
|
];
|
package/src/canvas/CanvasApp.tsx
CHANGED
|
@@ -45,6 +45,11 @@ function clampZoom(z: number): number {
|
|
|
45
45
|
return Number(Math.min(Math.max(z, MIN_ZOOM), MAX_ZOOM).toFixed(3));
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
function snapToDevicePixel(v: number): number {
|
|
49
|
+
const dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;
|
|
50
|
+
return Math.round(v * dpr) / dpr;
|
|
51
|
+
}
|
|
52
|
+
|
|
48
53
|
function buildDefaultLayout(screens: ScreenMetadata[]): ArtboardLayoutMap {
|
|
49
54
|
const map: ArtboardLayoutMap = {};
|
|
50
55
|
screens.forEach((screen, idx) => {
|
|
@@ -128,7 +133,9 @@ export const CanvasApp: React.FC = () => {
|
|
|
128
133
|
}>({ undoAvailable: 0, redoAvailable: 0 });
|
|
129
134
|
|
|
130
135
|
const canvasViewportRef = useRef<HTMLDivElement | null>(null);
|
|
136
|
+
const worldContainerRef = useRef<HTMLDivElement | null>(null);
|
|
131
137
|
const cameraTimerRef = useRef<number | null>(null);
|
|
138
|
+
const panSyncTimerRef = useRef<number | null>(null);
|
|
132
139
|
|
|
133
140
|
// Refs for synchronous access inside non-passive native wheel/gesture listeners
|
|
134
141
|
const zoomRef = useRef<number>(zoom);
|
|
@@ -140,6 +147,39 @@ export const CanvasApp: React.FC = () => {
|
|
|
140
147
|
panRef.current = pan;
|
|
141
148
|
}, [pan]);
|
|
142
149
|
|
|
150
|
+
const applyDirectPanTransform = useCallback(
|
|
151
|
+
(rawX: number, rawY: number, commitImmediately = false) => {
|
|
152
|
+
const nextPan = {
|
|
153
|
+
x: snapToDevicePixel(rawX),
|
|
154
|
+
y: snapToDevicePixel(rawY),
|
|
155
|
+
};
|
|
156
|
+
panRef.current = nextPan;
|
|
157
|
+
|
|
158
|
+
const worldEl = worldContainerRef.current;
|
|
159
|
+
if (worldEl) {
|
|
160
|
+
worldEl.style.transition = 'none';
|
|
161
|
+
worldEl.style.transform = `translate3d(${nextPan.x}px, ${nextPan.y}px, 0) scale(${zoomRef.current})`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (panSyncTimerRef.current) {
|
|
165
|
+
window.clearTimeout(panSyncTimerRef.current);
|
|
166
|
+
panSyncTimerRef.current = null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (commitImmediately) {
|
|
170
|
+
setIsCameraAnimating(false);
|
|
171
|
+
setPan(nextPan);
|
|
172
|
+
} else {
|
|
173
|
+
panSyncTimerRef.current = window.setTimeout(() => {
|
|
174
|
+
panSyncTimerRef.current = null;
|
|
175
|
+
setIsCameraAnimating(false);
|
|
176
|
+
setPan(panRef.current);
|
|
177
|
+
}, 120);
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
[]
|
|
181
|
+
);
|
|
182
|
+
|
|
143
183
|
const triggerCameraTransition = useCallback(() => {
|
|
144
184
|
if (cameraTimerRef.current) {
|
|
145
185
|
window.clearTimeout(cameraTimerRef.current);
|
|
@@ -462,13 +502,17 @@ export const CanvasApp: React.FC = () => {
|
|
|
462
502
|
const worldY = (cursorY - prevPan.y) / prevZoom;
|
|
463
503
|
|
|
464
504
|
const nextPan = {
|
|
465
|
-
x: cursorX - worldX * nextZoom,
|
|
466
|
-
y: cursorY - worldY * nextZoom,
|
|
505
|
+
x: snapToDevicePixel(cursorX - worldX * nextZoom),
|
|
506
|
+
y: snapToDevicePixel(cursorY - worldY * nextZoom),
|
|
467
507
|
};
|
|
468
508
|
|
|
469
509
|
setIsCameraAnimating(false);
|
|
470
510
|
zoomRef.current = nextZoom;
|
|
471
511
|
panRef.current = nextPan;
|
|
512
|
+
if (worldContainerRef.current) {
|
|
513
|
+
worldContainerRef.current.style.transition = 'none';
|
|
514
|
+
worldContainerRef.current.style.transform = `translate3d(${nextPan.x}px, ${nextPan.y}px, 0) scale(${nextZoom})`;
|
|
515
|
+
}
|
|
472
516
|
setZoom(nextZoom);
|
|
473
517
|
setPan(nextPan);
|
|
474
518
|
};
|
|
@@ -483,13 +527,11 @@ export const CanvasApp: React.FC = () => {
|
|
|
483
527
|
const zoomFactor = Math.exp(-e.deltaY * 0.0085);
|
|
484
528
|
applyFocalZoom(zoomRef.current * zoomFactor, e.clientX, e.clientY);
|
|
485
529
|
} else {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
panRef.current = nextPan;
|
|
492
|
-
setPan(nextPan);
|
|
530
|
+
applyDirectPanTransform(
|
|
531
|
+
panRef.current.x - e.deltaX,
|
|
532
|
+
panRef.current.y - e.deltaY,
|
|
533
|
+
false
|
|
534
|
+
);
|
|
493
535
|
}
|
|
494
536
|
};
|
|
495
537
|
|
|
@@ -537,7 +579,7 @@ export const CanvasApp: React.FC = () => {
|
|
|
537
579
|
document.removeEventListener('gesturechange', handleGestureChange);
|
|
538
580
|
document.removeEventListener('gestureend', handleGestureEnd);
|
|
539
581
|
};
|
|
540
|
-
}, []);
|
|
582
|
+
}, [applyDirectPanTransform]);
|
|
541
583
|
|
|
542
584
|
// Center viewport on a specific screen artboard using Emil Kowalski ease-out-expo camera physics
|
|
543
585
|
const focusOnScreen = useCallback(
|
|
@@ -630,7 +672,14 @@ export const CanvasApp: React.FC = () => {
|
|
|
630
672
|
[focusOnScreen]
|
|
631
673
|
);
|
|
632
674
|
|
|
633
|
-
|
|
675
|
+
const handleSelectFlow = useCallback(
|
|
676
|
+
(edge: NavigationEdge) => {
|
|
677
|
+
focusOnScreen(edge.toScreen);
|
|
678
|
+
},
|
|
679
|
+
[focusOnScreen]
|
|
680
|
+
);
|
|
681
|
+
|
|
682
|
+
// Canvas background pointer pan (0ms direct DOM manipulation override)
|
|
634
683
|
const handleCanvasPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
|
|
635
684
|
if (e.button !== 0 && e.button !== 1) return;
|
|
636
685
|
setIsCameraAnimating(false);
|
|
@@ -638,20 +687,20 @@ export const CanvasApp: React.FC = () => {
|
|
|
638
687
|
|
|
639
688
|
const startX = e.clientX;
|
|
640
689
|
const startY = e.clientY;
|
|
641
|
-
const startPanX =
|
|
642
|
-
const startPanY =
|
|
690
|
+
const startPanX = panRef.current.x;
|
|
691
|
+
const startPanY = panRef.current.y;
|
|
643
692
|
|
|
644
693
|
const handleMove = (moveEvent: PointerEvent) => {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
setPan(nextPan);
|
|
694
|
+
applyDirectPanTransform(
|
|
695
|
+
startPanX + (moveEvent.clientX - startX),
|
|
696
|
+
startPanY + (moveEvent.clientY - startY),
|
|
697
|
+
false
|
|
698
|
+
);
|
|
651
699
|
};
|
|
652
700
|
|
|
653
701
|
const handleUp = () => {
|
|
654
702
|
setIsPanning(false);
|
|
703
|
+
applyDirectPanTransform(panRef.current.x, panRef.current.y, true);
|
|
655
704
|
window.removeEventListener('pointermove', handleMove);
|
|
656
705
|
window.removeEventListener('pointerup', handleUp);
|
|
657
706
|
};
|
|
@@ -1299,6 +1348,7 @@ export const CanvasApp: React.FC = () => {
|
|
|
1299
1348
|
|
|
1300
1349
|
{/* World Transform Container: Emil Kowalski ease-out-expo on camera focus, 0ms on direct drag/pinch */}
|
|
1301
1350
|
<div
|
|
1351
|
+
ref={worldContainerRef}
|
|
1302
1352
|
style={{
|
|
1303
1353
|
position: 'absolute',
|
|
1304
1354
|
top: 0,
|
|
@@ -1306,6 +1356,7 @@ export const CanvasApp: React.FC = () => {
|
|
|
1306
1356
|
transform: `translate3d(${pan.x}px, ${pan.y}px, 0) scale(${zoom})`,
|
|
1307
1357
|
transformOrigin: '0 0',
|
|
1308
1358
|
willChange: 'transform',
|
|
1359
|
+
backfaceVisibility: 'hidden',
|
|
1309
1360
|
transition: isCameraAnimating
|
|
1310
1361
|
? 'transform var(--duration-camera) var(--ease-out-expo)'
|
|
1311
1362
|
: 'none',
|
|
@@ -1317,7 +1368,7 @@ export const CanvasApp: React.FC = () => {
|
|
|
1317
1368
|
layout={layout}
|
|
1318
1369
|
activeScreenId={activeScreenId}
|
|
1319
1370
|
highlightedScreenId={highlightedScreenId}
|
|
1320
|
-
onSelectFlow={
|
|
1371
|
+
onSelectFlow={handleSelectFlow}
|
|
1321
1372
|
/>
|
|
1322
1373
|
)}
|
|
1323
1374
|
|
|
@@ -1329,6 +1380,7 @@ export const CanvasApp: React.FC = () => {
|
|
|
1329
1380
|
height: DEFAULT_HEIGHT,
|
|
1330
1381
|
};
|
|
1331
1382
|
const ScreenComponent = SCREEN_COMPONENTS[screen.id];
|
|
1383
|
+
const isSelectedScreen = selectedElement?.screenId === screen.id;
|
|
1332
1384
|
|
|
1333
1385
|
return (
|
|
1334
1386
|
<ScreenArtboard
|
|
@@ -1340,8 +1392,9 @@ export const CanvasApp: React.FC = () => {
|
|
|
1340
1392
|
mode={mode}
|
|
1341
1393
|
isActiveScreen={activeScreenId === screen.id}
|
|
1342
1394
|
isHighlightedScreen={highlightedScreenId === screen.id}
|
|
1343
|
-
|
|
1344
|
-
|
|
1395
|
+
isCanvasMoving={isPanning}
|
|
1396
|
+
selectedElement={isSelectedScreen ? selectedElement : null}
|
|
1397
|
+
inspectedElement={isSelectedScreen ? inspectedData : null}
|
|
1345
1398
|
onSelectElement={handleSelectElement}
|
|
1346
1399
|
onSelectScreen={setActiveScreenId}
|
|
1347
1400
|
onFocusScreen={focusOnScreen}
|
|
@@ -11,7 +11,7 @@ interface FlowArrowsLayerProps {
|
|
|
11
11
|
|
|
12
12
|
const ARTBOARD_HEADER_OFFSET = 38;
|
|
13
13
|
|
|
14
|
-
export const FlowArrowsLayer: React.FC<FlowArrowsLayerProps> = ({
|
|
14
|
+
export const FlowArrowsLayer: React.FC<FlowArrowsLayerProps> = React.memo(({
|
|
15
15
|
screens,
|
|
16
16
|
layout,
|
|
17
17
|
activeScreenId,
|
|
@@ -194,4 +194,4 @@ export const FlowArrowsLayer: React.FC<FlowArrowsLayerProps> = ({
|
|
|
194
194
|
})}
|
|
195
195
|
</svg>
|
|
196
196
|
);
|
|
197
|
-
};
|
|
197
|
+
});
|
|
@@ -29,6 +29,7 @@ interface ScreenArtboardProps {
|
|
|
29
29
|
mode: CanvasMode;
|
|
30
30
|
isActiveScreen: boolean;
|
|
31
31
|
isHighlightedScreen: boolean;
|
|
32
|
+
isCanvasMoving?: boolean;
|
|
32
33
|
selectedElement: SelectedElementRef | null;
|
|
33
34
|
inspectedElement: InspectedElement | null;
|
|
34
35
|
onSelectElement: (ref: SelectedElementRef) => void;
|
|
@@ -83,7 +84,7 @@ function escapeAstPathSelector(astPath: string): string {
|
|
|
83
84
|
: astPath.replace(/["\\]/g, '\\$&');
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
export const ScreenArtboard: React.FC<ScreenArtboardProps> = ({
|
|
87
|
+
export const ScreenArtboard: React.FC<ScreenArtboardProps> = React.memo(({
|
|
87
88
|
screen,
|
|
88
89
|
ScreenComponent,
|
|
89
90
|
position,
|
|
@@ -91,6 +92,7 @@ export const ScreenArtboard: React.FC<ScreenArtboardProps> = ({
|
|
|
91
92
|
mode,
|
|
92
93
|
isActiveScreen,
|
|
93
94
|
isHighlightedScreen,
|
|
95
|
+
isCanvasMoving = false,
|
|
94
96
|
selectedElement,
|
|
95
97
|
inspectedElement,
|
|
96
98
|
onSelectElement,
|
|
@@ -732,8 +734,14 @@ export const ScreenArtboard: React.FC<ScreenArtboardProps> = ({
|
|
|
732
734
|
};
|
|
733
735
|
|
|
734
736
|
// Inspect Mode pointer handlers
|
|
737
|
+
useEffect(() => {
|
|
738
|
+
if (isCanvasMoving) {
|
|
739
|
+
setHoverBox(null);
|
|
740
|
+
}
|
|
741
|
+
}, [isCanvasMoving]);
|
|
742
|
+
|
|
735
743
|
const handlePointerMoveCapture = (e: React.PointerEvent<HTMLDivElement>) => {
|
|
736
|
-
if (mode !== 'inspect' || activeGizmoAction) return;
|
|
744
|
+
if (mode !== 'inspect' || activeGizmoAction || isCanvasMoving || e.buttons !== 0) return;
|
|
737
745
|
const targetEl = findNearestAgyElement(e.target, viewportRef.current);
|
|
738
746
|
if (!targetEl) {
|
|
739
747
|
setHoverBox(null);
|
|
@@ -992,6 +1000,12 @@ export const ScreenArtboard: React.FC<ScreenArtboardProps> = ({
|
|
|
992
1000
|
flexDirection: 'column',
|
|
993
1001
|
height: '100%',
|
|
994
1002
|
width: '100%',
|
|
1003
|
+
minHeight: 0,
|
|
1004
|
+
minWidth: 0,
|
|
1005
|
+
backgroundColor: '#FFFFFF',
|
|
1006
|
+
color: '#0F172A',
|
|
1007
|
+
overflowY: 'auto',
|
|
1008
|
+
overflowX: 'hidden',
|
|
995
1009
|
}}
|
|
996
1010
|
>
|
|
997
1011
|
<ScreenComponent />
|
|
@@ -1474,4 +1488,4 @@ export const ScreenArtboard: React.FC<ScreenArtboardProps> = ({
|
|
|
1474
1488
|
</div>
|
|
1475
1489
|
</div>
|
|
1476
1490
|
);
|
|
1477
|
-
};
|
|
1491
|
+
});
|
package/src/main.tsx
CHANGED
|
@@ -128,13 +128,17 @@ export function useRoute(): { key: string; name: string; params: Record<string,
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
export function useFocusEffect(effect: () => void | (() => void)): void {
|
|
131
|
+
const ctx = useContext(MockNavigationContext);
|
|
132
|
+
const effectRef = useRef(effect);
|
|
133
|
+
effectRef.current = effect;
|
|
131
134
|
useEffect(() => {
|
|
132
135
|
try {
|
|
133
|
-
|
|
136
|
+
const cleanup = effectRef.current();
|
|
137
|
+
return typeof cleanup === 'function' ? cleanup : undefined;
|
|
134
138
|
} catch {
|
|
135
139
|
return undefined;
|
|
136
140
|
}
|
|
137
|
-
}, [
|
|
141
|
+
}, [ctx.currentRoute]);
|
|
138
142
|
}
|
|
139
143
|
|
|
140
144
|
export function useIsFocused(): boolean {
|
|
@@ -242,7 +246,7 @@ export function usePathname(): string {
|
|
|
242
246
|
|
|
243
247
|
export function useSegments(): string[] {
|
|
244
248
|
const ctx = useContext(MockNavigationContext);
|
|
245
|
-
return [ctx.currentRoute.toLowerCase()];
|
|
249
|
+
return useMemo(() => [ctx.currentRoute.toLowerCase()], [ctx.currentRoute]);
|
|
246
250
|
}
|
|
247
251
|
|
|
248
252
|
export function useRootNavigationState() {
|
|
@@ -973,53 +977,477 @@ export const QueryClientProvider: React.FC<{ client?: unknown; children?: React.
|
|
|
973
977
|
children,
|
|
974
978
|
}) => <>{children}</>;
|
|
975
979
|
|
|
980
|
+
const singletonQueryClient = new QueryClient();
|
|
981
|
+
|
|
976
982
|
export function useQueryClient() {
|
|
977
|
-
return
|
|
983
|
+
return singletonQueryClient;
|
|
978
984
|
}
|
|
979
985
|
|
|
986
|
+
const STABLE_EMPTY_ARRAY: unknown[] = [];
|
|
987
|
+
|
|
988
|
+
const STABLE_QUERY_RESULT = {
|
|
989
|
+
data: undefined,
|
|
990
|
+
error: null,
|
|
991
|
+
isLoading: false,
|
|
992
|
+
isPending: false,
|
|
993
|
+
isFetching: false,
|
|
994
|
+
isError: false,
|
|
995
|
+
isSuccess: true,
|
|
996
|
+
status: 'success',
|
|
997
|
+
refetch: async () => ({ data: undefined, error: null }),
|
|
998
|
+
};
|
|
999
|
+
|
|
980
1000
|
export function useQuery() {
|
|
981
|
-
return
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
}
|
|
1001
|
+
return STABLE_QUERY_RESULT;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
const STABLE_INFINITE_QUERY_RESULT = {
|
|
1005
|
+
data: { pages: STABLE_EMPTY_ARRAY, pageParams: STABLE_EMPTY_ARRAY },
|
|
1006
|
+
error: null,
|
|
1007
|
+
isLoading: false,
|
|
1008
|
+
isPending: false,
|
|
1009
|
+
isFetching: false,
|
|
1010
|
+
isFetchingNextPage: false,
|
|
1011
|
+
hasNextPage: false,
|
|
1012
|
+
fetchNextPage: async () => ({}),
|
|
1013
|
+
refetch: async () => ({}),
|
|
1014
|
+
};
|
|
993
1015
|
|
|
994
1016
|
export function useInfiniteQuery() {
|
|
995
|
-
return
|
|
996
|
-
data: { pages: [], pageParams: [] },
|
|
997
|
-
error: null,
|
|
998
|
-
isLoading: false,
|
|
999
|
-
isPending: false,
|
|
1000
|
-
isFetching: false,
|
|
1001
|
-
isFetchingNextPage: false,
|
|
1002
|
-
hasNextPage: false,
|
|
1003
|
-
fetchNextPage: async () => ({}),
|
|
1004
|
-
refetch: async () => ({}),
|
|
1005
|
-
};
|
|
1017
|
+
return STABLE_INFINITE_QUERY_RESULT;
|
|
1006
1018
|
}
|
|
1007
1019
|
|
|
1020
|
+
const STABLE_MUTATION_RESULT = {
|
|
1021
|
+
mutate: () => undefined,
|
|
1022
|
+
mutateAsync: async () => ({}),
|
|
1023
|
+
reset: () => undefined,
|
|
1024
|
+
data: undefined,
|
|
1025
|
+
error: null,
|
|
1026
|
+
isLoading: false,
|
|
1027
|
+
isPending: false,
|
|
1028
|
+
isError: false,
|
|
1029
|
+
isSuccess: false,
|
|
1030
|
+
status: 'idle',
|
|
1031
|
+
};
|
|
1032
|
+
|
|
1008
1033
|
export function useMutation() {
|
|
1034
|
+
return STABLE_MUTATION_RESULT;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// ============================================================================
|
|
1038
|
+
// 11. Universal Chainable Proxy, Zod, React Hook Form & Context Fallbacks
|
|
1039
|
+
// ============================================================================
|
|
1040
|
+
export function createSafeChainableProxy(): unknown {
|
|
1041
|
+
const fnTarget = function (..._args: unknown[]) {
|
|
1042
|
+
return proxy;
|
|
1043
|
+
};
|
|
1044
|
+
const proxy: unknown = new Proxy(fnTarget, {
|
|
1045
|
+
get(_target, prop) {
|
|
1046
|
+
if (prop === Symbol.toPrimitive) {
|
|
1047
|
+
return (hint: string) => (hint === 'number' ? 0 : '');
|
|
1048
|
+
}
|
|
1049
|
+
if (prop === 'valueOf') return () => 0;
|
|
1050
|
+
if (prop === 'toString') return () => '';
|
|
1051
|
+
if (prop === Symbol.iterator) {
|
|
1052
|
+
return function* () {
|
|
1053
|
+
yield proxy;
|
|
1054
|
+
yield proxy;
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
if (prop === 'then') {
|
|
1058
|
+
return (onFulfilled?: (v: unknown) => unknown, onRejected?: (e: unknown) => unknown) =>
|
|
1059
|
+
Promise.resolve({ data: null, error: null }).then(onFulfilled, onRejected);
|
|
1060
|
+
}
|
|
1061
|
+
if (prop === 'catch') return () => Promise.resolve({ data: null, error: null });
|
|
1062
|
+
if (prop === 'finally')
|
|
1063
|
+
return (cb?: () => void) => Promise.resolve({ data: null, error: null }).finally(cb);
|
|
1064
|
+
if (prop === 'safeParse' || prop === 'spa' || prop === 'safeParseAsync') {
|
|
1065
|
+
return (val?: unknown) => ({ success: true, data: val ?? {}, error: null });
|
|
1066
|
+
}
|
|
1067
|
+
if (prop === 'parse' || prop === 'parseAsync') {
|
|
1068
|
+
return (val?: unknown) => val ?? {};
|
|
1069
|
+
}
|
|
1070
|
+
if (
|
|
1071
|
+
prop === 'isLoading' ||
|
|
1072
|
+
prop === 'loading' ||
|
|
1073
|
+
prop === 'isPending' ||
|
|
1074
|
+
prop === 'isError' ||
|
|
1075
|
+
prop === 'disabled' ||
|
|
1076
|
+
prop === 'isSubmitting'
|
|
1077
|
+
) {
|
|
1078
|
+
return false;
|
|
1079
|
+
}
|
|
1080
|
+
if (
|
|
1081
|
+
prop === 'isReady' ||
|
|
1082
|
+
prop === 'granted' ||
|
|
1083
|
+
prop === 'isSuccess' ||
|
|
1084
|
+
prop === 'isAuthenticated' ||
|
|
1085
|
+
prop === 'isValid'
|
|
1086
|
+
) {
|
|
1087
|
+
return true;
|
|
1088
|
+
}
|
|
1089
|
+
if (prop === 'error' || prop === 'errors') return null;
|
|
1090
|
+
if (prop === 'status') return 'granted';
|
|
1091
|
+
if (prop === 'data' || prop === 'items' || prop === 'results' || prop === 'list') {
|
|
1092
|
+
return STABLE_EMPTY_ARRAY;
|
|
1093
|
+
}
|
|
1094
|
+
if (prop === 'length' || prop === 'size' || prop === 'count' || prop === 'value') return 0;
|
|
1095
|
+
if (prop === 'ref') return { current: null };
|
|
1096
|
+
if (prop === 'map' || prop === 'filter' || prop === 'slice' || prop === 'flatMap') {
|
|
1097
|
+
return () => STABLE_EMPTY_ARRAY;
|
|
1098
|
+
}
|
|
1099
|
+
if (prop === 'forEach') return () => undefined;
|
|
1100
|
+
if (prop === 'reduce') return (_fn: unknown, init: unknown) => init;
|
|
1101
|
+
if (prop === 'shape' || prop === '_def') return {};
|
|
1102
|
+
return proxy;
|
|
1103
|
+
},
|
|
1104
|
+
});
|
|
1105
|
+
return proxy;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
export const SAFE_CHAINABLE_PROXY = createSafeChainableProxy();
|
|
1109
|
+
|
|
1110
|
+
// Zod Shims (import { z } from 'zod' or import * as z from 'zod')
|
|
1111
|
+
export const z = SAFE_CHAINABLE_PROXY as Record<string, unknown>;
|
|
1112
|
+
export const string = () => SAFE_CHAINABLE_PROXY;
|
|
1113
|
+
export const number = () => SAFE_CHAINABLE_PROXY;
|
|
1114
|
+
export const boolean = () => SAFE_CHAINABLE_PROXY;
|
|
1115
|
+
export const object = () => SAFE_CHAINABLE_PROXY;
|
|
1116
|
+
export const array = () => SAFE_CHAINABLE_PROXY;
|
|
1117
|
+
export const union = () => SAFE_CHAINABLE_PROXY;
|
|
1118
|
+
export const literal = () => SAFE_CHAINABLE_PROXY;
|
|
1119
|
+
export const record = () => SAFE_CHAINABLE_PROXY;
|
|
1120
|
+
export const tuple = () => SAFE_CHAINABLE_PROXY;
|
|
1121
|
+
export const any = () => SAFE_CHAINABLE_PROXY;
|
|
1122
|
+
export const unknown = () => SAFE_CHAINABLE_PROXY;
|
|
1123
|
+
export const optional = () => SAFE_CHAINABLE_PROXY;
|
|
1124
|
+
export const nullable = () => SAFE_CHAINABLE_PROXY;
|
|
1125
|
+
export const coerce = SAFE_CHAINABLE_PROXY;
|
|
1126
|
+
export const zodResolver = () => async (values: unknown) => ({
|
|
1127
|
+
values: values ?? {},
|
|
1128
|
+
errors: {},
|
|
1129
|
+
});
|
|
1130
|
+
export const yupResolver = zodResolver;
|
|
1131
|
+
|
|
1132
|
+
// Common React Native Sizing & Classname Utility Shims
|
|
1133
|
+
export const scale = (size: number) => size;
|
|
1134
|
+
export const verticalScale = (size: number) => size;
|
|
1135
|
+
export const moderateScale = (size: number) => size;
|
|
1136
|
+
export const moderateVerticalScale = (size: number) => size;
|
|
1137
|
+
export const s = scale;
|
|
1138
|
+
export const vs = verticalScale;
|
|
1139
|
+
export const ms = moderateScale;
|
|
1140
|
+
export const mvs = moderateVerticalScale;
|
|
1141
|
+
export const RFValue = (size: number) => size;
|
|
1142
|
+
export const RFPercentage = (pct: number) => Math.round(pct * 8);
|
|
1143
|
+
export const widthPercentageToDP = (pct: number | string) =>
|
|
1144
|
+
typeof pct === 'number' ? Math.round((393 * pct) / 100) : 100;
|
|
1145
|
+
export const heightPercentageToDP = (pct: number | string) =>
|
|
1146
|
+
typeof pct === 'number' ? Math.round((852 * pct) / 100) : 100;
|
|
1147
|
+
export const wp = widthPercentageToDP;
|
|
1148
|
+
export const hp = heightPercentageToDP;
|
|
1149
|
+
export const clsx = (...args: unknown[]) =>
|
|
1150
|
+
args.filter((a): a is string => typeof a === 'string' && a.length > 0).join(' ');
|
|
1151
|
+
export const cn = clsx;
|
|
1152
|
+
export const twMerge = clsx;
|
|
1153
|
+
|
|
1154
|
+
// React Hook Form Shims (react-hook-form)
|
|
1155
|
+
export function useForm(config?: { defaultValues?: Record<string, unknown> }) {
|
|
1156
|
+
const defaults = config?.defaultValues ?? {};
|
|
1009
1157
|
return {
|
|
1010
|
-
|
|
1011
|
-
|
|
1158
|
+
control: {},
|
|
1159
|
+
register: (name: string) => ({
|
|
1160
|
+
name,
|
|
1161
|
+
onChange: () => undefined,
|
|
1162
|
+
onBlur: () => undefined,
|
|
1163
|
+
ref: () => undefined,
|
|
1164
|
+
}),
|
|
1165
|
+
handleSubmit: (onValid?: (data: Record<string, unknown>) => unknown) => () =>
|
|
1166
|
+
onValid?.(defaults),
|
|
1167
|
+
watch: (name?: string) => (name ? defaults[name] ?? '' : defaults),
|
|
1168
|
+
getValues: (name?: string) => (name ? defaults[name] : defaults),
|
|
1169
|
+
setValue: () => undefined,
|
|
1012
1170
|
reset: () => undefined,
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1171
|
+
clearErrors: () => undefined,
|
|
1172
|
+
setError: () => undefined,
|
|
1173
|
+
trigger: async () => true,
|
|
1174
|
+
formState: {
|
|
1175
|
+
errors: {},
|
|
1176
|
+
isDirty: false,
|
|
1177
|
+
isLoading: false,
|
|
1178
|
+
isSubmitted: false,
|
|
1179
|
+
isSubmitting: false,
|
|
1180
|
+
isSubmitSuccessful: false,
|
|
1181
|
+
isValid: true,
|
|
1182
|
+
dirtyFields: {},
|
|
1183
|
+
touchedFields: {},
|
|
1184
|
+
},
|
|
1020
1185
|
};
|
|
1021
1186
|
}
|
|
1022
1187
|
|
|
1188
|
+
export const useFormContext = () => useForm();
|
|
1189
|
+
export const FormProvider: React.FC<{ children?: React.ReactNode }> = ({ children }) => (
|
|
1190
|
+
<>{children}</>
|
|
1191
|
+
);
|
|
1192
|
+
export const useWatch = () => '';
|
|
1193
|
+
export const useFieldArray = () => ({
|
|
1194
|
+
fields: STABLE_EMPTY_ARRAY,
|
|
1195
|
+
append: () => undefined,
|
|
1196
|
+
prepend: () => undefined,
|
|
1197
|
+
remove: () => undefined,
|
|
1198
|
+
insert: () => undefined,
|
|
1199
|
+
move: () => undefined,
|
|
1200
|
+
update: () => undefined,
|
|
1201
|
+
replace: () => undefined,
|
|
1202
|
+
});
|
|
1203
|
+
|
|
1204
|
+
export const Controller: React.FC<{
|
|
1205
|
+
name?: string;
|
|
1206
|
+
defaultValue?: unknown;
|
|
1207
|
+
render?: (params: {
|
|
1208
|
+
field: {
|
|
1209
|
+
name: string;
|
|
1210
|
+
value: unknown;
|
|
1211
|
+
onChange: (...args: unknown[]) => void;
|
|
1212
|
+
onBlur: () => void;
|
|
1213
|
+
ref: () => void;
|
|
1214
|
+
};
|
|
1215
|
+
fieldState: { invalid: boolean; isTouched: boolean; isDirty: boolean; error: undefined };
|
|
1216
|
+
formState: { errors: Record<string, unknown>; isSubmitting: boolean; isValid: boolean };
|
|
1217
|
+
}) => React.ReactNode;
|
|
1218
|
+
children?: React.ReactNode;
|
|
1219
|
+
}> = ({ name = '', defaultValue = '', render, children }) => {
|
|
1220
|
+
if (typeof render === 'function') {
|
|
1221
|
+
return (
|
|
1222
|
+
<>
|
|
1223
|
+
{render({
|
|
1224
|
+
field: {
|
|
1225
|
+
name,
|
|
1226
|
+
value: defaultValue ?? '',
|
|
1227
|
+
onChange: () => undefined,
|
|
1228
|
+
onBlur: () => undefined,
|
|
1229
|
+
ref: () => undefined,
|
|
1230
|
+
},
|
|
1231
|
+
fieldState: { invalid: false, isTouched: false, isDirty: false, error: undefined },
|
|
1232
|
+
formState: { errors: {}, isSubmitting: false, isValid: true },
|
|
1233
|
+
})}
|
|
1234
|
+
</>
|
|
1235
|
+
);
|
|
1236
|
+
}
|
|
1237
|
+
return <>{children}</>;
|
|
1238
|
+
};
|
|
1239
|
+
|
|
1240
|
+
// Universal Unmounted Context Fallback (prevents "useAuth must be used within an AuthProvider")
|
|
1241
|
+
const previewUserBase: Record<string, unknown> = {
|
|
1242
|
+
id: 'usr_preview_01',
|
|
1243
|
+
uid: 'usr_preview_01',
|
|
1244
|
+
email: 'alex.morgan@example.com',
|
|
1245
|
+
name: 'Alex Morgan',
|
|
1246
|
+
fullName: 'Alex Morgan',
|
|
1247
|
+
full_name: 'Alex Morgan',
|
|
1248
|
+
firstName: 'Alex',
|
|
1249
|
+
first_name: 'Alex',
|
|
1250
|
+
lastName: 'Morgan',
|
|
1251
|
+
last_name: 'Morgan',
|
|
1252
|
+
displayName: 'Alex Morgan',
|
|
1253
|
+
display_name: 'Alex Morgan',
|
|
1254
|
+
username: 'alexmorgan',
|
|
1255
|
+
phone: '+1 (555) 019-2834',
|
|
1256
|
+
phoneNumber: '+1 (555) 019-2834',
|
|
1257
|
+
phone_number: '+1 (555) 019-2834',
|
|
1258
|
+
role: 'buyer',
|
|
1259
|
+
accountType: 'buyer',
|
|
1260
|
+
account_type: 'buyer',
|
|
1261
|
+
location: 'San Francisco, CA',
|
|
1262
|
+
city: 'San Francisco',
|
|
1263
|
+
bio: 'Product designer & automotive enthusiast.',
|
|
1264
|
+
avatar: '',
|
|
1265
|
+
avatarUrl: '',
|
|
1266
|
+
avatar_url: '',
|
|
1267
|
+
photoURL: '',
|
|
1268
|
+
profileImage: '',
|
|
1269
|
+
profile_image: '',
|
|
1270
|
+
isVerified: true,
|
|
1271
|
+
is_verified: true,
|
|
1272
|
+
verified: true,
|
|
1273
|
+
createdAt: '2025-01-15T10:00:00Z',
|
|
1274
|
+
created_at: '2025-01-15T10:00:00Z',
|
|
1275
|
+
user_metadata: {
|
|
1276
|
+
full_name: 'Alex Morgan',
|
|
1277
|
+
name: 'Alex Morgan',
|
|
1278
|
+
email: 'alex.morgan@example.com',
|
|
1279
|
+
role: 'buyer',
|
|
1280
|
+
avatar_url: '',
|
|
1281
|
+
},
|
|
1282
|
+
app_metadata: {
|
|
1283
|
+
provider: 'email',
|
|
1284
|
+
role: 'buyer',
|
|
1285
|
+
},
|
|
1286
|
+
};
|
|
1287
|
+
|
|
1288
|
+
const previewUserProxy = new Proxy(previewUserBase, {
|
|
1289
|
+
get(target, prop) {
|
|
1290
|
+
if (prop in target) return target[prop as keyof typeof target];
|
|
1291
|
+
if (prop === Symbol.toPrimitive) return () => 'Alex Morgan';
|
|
1292
|
+
if (typeof prop === 'string') return '';
|
|
1293
|
+
return undefined;
|
|
1294
|
+
},
|
|
1295
|
+
});
|
|
1296
|
+
|
|
1297
|
+
const previewColorsBase: Record<string, string> = {
|
|
1298
|
+
primary: '#2563EB',
|
|
1299
|
+
primaryLight: '#DBEAFE',
|
|
1300
|
+
secondary: '#64748B',
|
|
1301
|
+
accent: '#4F46E5',
|
|
1302
|
+
background: '#FFFFFF',
|
|
1303
|
+
backgroundSecondary: '#F8FAFC',
|
|
1304
|
+
surface: '#F8FAFC',
|
|
1305
|
+
card: '#FFFFFF',
|
|
1306
|
+
text: '#0F172A',
|
|
1307
|
+
textPrimary: '#0F172A',
|
|
1308
|
+
textSecondary: '#475569',
|
|
1309
|
+
textMuted: '#94A3B8',
|
|
1310
|
+
muted: '#94A3B8',
|
|
1311
|
+
placeholder: '#94A3B8',
|
|
1312
|
+
border: '#E2E8F0',
|
|
1313
|
+
divider: '#E2E8F0',
|
|
1314
|
+
error: '#EF4444',
|
|
1315
|
+
danger: '#EF4444',
|
|
1316
|
+
success: '#10B981',
|
|
1317
|
+
warning: '#F59E0B',
|
|
1318
|
+
info: '#3B82F6',
|
|
1319
|
+
white: '#FFFFFF',
|
|
1320
|
+
black: '#0F172A',
|
|
1321
|
+
tint: '#2563EB',
|
|
1322
|
+
icon: '#64748B',
|
|
1323
|
+
tabIconDefault: '#94A3B8',
|
|
1324
|
+
tabIconSelected: '#2563EB',
|
|
1325
|
+
notification: '#EF4444',
|
|
1326
|
+
};
|
|
1327
|
+
|
|
1328
|
+
const previewColorsProxy = new Proxy(previewColorsBase, {
|
|
1329
|
+
get(target, prop) {
|
|
1330
|
+
if (prop in target) return target[prop as keyof typeof target];
|
|
1331
|
+
return '#64748B';
|
|
1332
|
+
},
|
|
1333
|
+
});
|
|
1334
|
+
|
|
1335
|
+
const safeContextFallbackBase: Record<string, unknown> = {
|
|
1336
|
+
user: previewUserProxy,
|
|
1337
|
+
currentUser: previewUserProxy,
|
|
1338
|
+
profile: previewUserProxy,
|
|
1339
|
+
session: {
|
|
1340
|
+
access_token: 'preview-access-token',
|
|
1341
|
+
refresh_token: 'preview-refresh-token',
|
|
1342
|
+
user: previewUserProxy,
|
|
1343
|
+
},
|
|
1344
|
+
isAuthenticated: true,
|
|
1345
|
+
isLoggedIn: true,
|
|
1346
|
+
isSignedIn: true,
|
|
1347
|
+
isLoading: false,
|
|
1348
|
+
loading: false,
|
|
1349
|
+
isPending: false,
|
|
1350
|
+
isRefreshing: false,
|
|
1351
|
+
isInitialized: true,
|
|
1352
|
+
isReady: true,
|
|
1353
|
+
error: null,
|
|
1354
|
+
theme: 'light',
|
|
1355
|
+
colorScheme: 'light',
|
|
1356
|
+
isDark: false,
|
|
1357
|
+
dark: false,
|
|
1358
|
+
colors: previewColorsProxy,
|
|
1359
|
+
items: STABLE_EMPTY_ARRAY,
|
|
1360
|
+
cart: STABLE_EMPTY_ARRAY,
|
|
1361
|
+
favorites: STABLE_EMPTY_ARRAY,
|
|
1362
|
+
wishlist: STABLE_EMPTY_ARRAY,
|
|
1363
|
+
notifications: STABLE_EMPTY_ARRAY,
|
|
1364
|
+
bookings: STABLE_EMPTY_ARRAY,
|
|
1365
|
+
listings: STABLE_EMPTY_ARRAY,
|
|
1366
|
+
vehicles: STABLE_EMPTY_ARRAY,
|
|
1367
|
+
cars: STABLE_EMPTY_ARRAY,
|
|
1368
|
+
orders: STABLE_EMPTY_ARRAY,
|
|
1369
|
+
transactions: STABLE_EMPTY_ARRAY,
|
|
1370
|
+
messages: STABLE_EMPTY_ARRAY,
|
|
1371
|
+
chats: STABLE_EMPTY_ARRAY,
|
|
1372
|
+
unreadCount: 0,
|
|
1373
|
+
count: 0,
|
|
1374
|
+
total: 0,
|
|
1375
|
+
balance: 0,
|
|
1376
|
+
signIn: async () => ({ data: { user: previewUserProxy }, error: null }),
|
|
1377
|
+
signUp: async () => ({ data: { user: previewUserProxy }, error: null }),
|
|
1378
|
+
signOut: async () => ({ error: null }),
|
|
1379
|
+
login: async () => ({ data: { user: previewUserProxy }, error: null }),
|
|
1380
|
+
logout: async () => undefined,
|
|
1381
|
+
register: async () => ({ data: { user: previewUserProxy }, error: null }),
|
|
1382
|
+
refresh: async () => undefined,
|
|
1383
|
+
refetch: async () => undefined,
|
|
1384
|
+
updateProfile: async () => ({ error: null }),
|
|
1385
|
+
toggleTheme: () => undefined,
|
|
1386
|
+
setTheme: () => undefined,
|
|
1387
|
+
};
|
|
1388
|
+
|
|
1389
|
+
export const SAFE_CONTEXT_FALLBACK: Record<string, unknown> = new Proxy(safeContextFallbackBase, {
|
|
1390
|
+
get(target, prop) {
|
|
1391
|
+
if (prop in target) return target[prop as keyof typeof target];
|
|
1392
|
+
if (prop === Symbol.iterator) {
|
|
1393
|
+
return function* () {
|
|
1394
|
+
yield SAFE_CONTEXT_FALLBACK;
|
|
1395
|
+
yield () => undefined;
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
return SAFE_CHAINABLE_PROXY;
|
|
1399
|
+
},
|
|
1400
|
+
});
|
|
1401
|
+
|
|
1402
|
+
// Install unmounted user-context fallback on React.createContext & React.useContext
|
|
1403
|
+
// (runs after react-native-web has already initialized its internal VirtualizedListContext)
|
|
1404
|
+
const origCreateContext = React.createContext;
|
|
1405
|
+
const origUseContext = React.useContext;
|
|
1406
|
+
|
|
1407
|
+
const wrappedCreateContext = function zeluneCreateContext(
|
|
1408
|
+
defaultValue: unknown,
|
|
1409
|
+
...rest: unknown[]
|
|
1410
|
+
) {
|
|
1411
|
+
const isEmptyDefault =
|
|
1412
|
+
defaultValue === undefined ||
|
|
1413
|
+
defaultValue === null ||
|
|
1414
|
+
(typeof defaultValue === 'object' &&
|
|
1415
|
+
defaultValue !== null &&
|
|
1416
|
+
Object.keys(defaultValue as object).length === 0);
|
|
1417
|
+
const effectiveDefault = isEmptyDefault ? SAFE_CONTEXT_FALLBACK : defaultValue;
|
|
1418
|
+
const ctx = (origCreateContext as (...a: unknown[]) => Record<string, unknown>).call(
|
|
1419
|
+
React,
|
|
1420
|
+
effectiveDefault,
|
|
1421
|
+
...rest
|
|
1422
|
+
);
|
|
1423
|
+
if (ctx && typeof ctx === 'object' && isEmptyDefault) {
|
|
1424
|
+
ctx.__zeluneUserContext = true;
|
|
1425
|
+
}
|
|
1426
|
+
return ctx;
|
|
1427
|
+
} as unknown as typeof React.createContext;
|
|
1428
|
+
|
|
1429
|
+
const wrappedUseContext = function zeluneUseContext(
|
|
1430
|
+
Context: React.Context<unknown>,
|
|
1431
|
+
...rest: unknown[]
|
|
1432
|
+
) {
|
|
1433
|
+
const val = (origUseContext as (...a: unknown[]) => unknown).call(React, Context, ...rest);
|
|
1434
|
+
if (
|
|
1435
|
+
val === undefined ||
|
|
1436
|
+
(val === null &&
|
|
1437
|
+
Boolean((Context as unknown as Record<string, unknown>)?.__zeluneUserContext))
|
|
1438
|
+
) {
|
|
1439
|
+
return SAFE_CONTEXT_FALLBACK;
|
|
1440
|
+
}
|
|
1441
|
+
return val;
|
|
1442
|
+
} as unknown as typeof React.useContext;
|
|
1443
|
+
|
|
1444
|
+
try {
|
|
1445
|
+
React.createContext = wrappedCreateContext;
|
|
1446
|
+
React.useContext = wrappedUseContext;
|
|
1447
|
+
} catch {
|
|
1448
|
+
// Ignore if React namespace is frozen in test environment
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1023
1451
|
function createSupabaseQueryBuilder(): Record<string, unknown> {
|
|
1024
1452
|
const resultPromise = Promise.resolve({ data: [], error: null, count: 0 });
|
|
1025
1453
|
const builder: Record<string, unknown> = new Proxy(
|
|
@@ -1146,6 +1574,29 @@ const defaultExportProperties = {
|
|
|
1146
1574
|
useInfiniteQuery,
|
|
1147
1575
|
useMutation,
|
|
1148
1576
|
createClient,
|
|
1577
|
+
z,
|
|
1578
|
+
string,
|
|
1579
|
+
number,
|
|
1580
|
+
boolean,
|
|
1581
|
+
object,
|
|
1582
|
+
array,
|
|
1583
|
+
union,
|
|
1584
|
+
literal,
|
|
1585
|
+
record,
|
|
1586
|
+
tuple,
|
|
1587
|
+
any,
|
|
1588
|
+
unknown,
|
|
1589
|
+
optional,
|
|
1590
|
+
nullable,
|
|
1591
|
+
coerce,
|
|
1592
|
+
zodResolver,
|
|
1593
|
+
yupResolver,
|
|
1594
|
+
useForm,
|
|
1595
|
+
useFormContext,
|
|
1596
|
+
FormProvider,
|
|
1597
|
+
useWatch,
|
|
1598
|
+
useFieldArray,
|
|
1599
|
+
Controller,
|
|
1149
1600
|
};
|
|
1150
1601
|
|
|
1151
1602
|
Object.assign(UniversalDefaultExportComponent, defaultExportProperties);
|
|
@@ -1161,7 +1612,7 @@ const defaultExportProxy = new Proxy(UniversalDefaultExportComponent, {
|
|
|
1161
1612
|
if (typeof prop === 'string' && /^[A-Z]/.test(prop)) {
|
|
1162
1613
|
return createVectorIconComponent(prop);
|
|
1163
1614
|
}
|
|
1164
|
-
return
|
|
1615
|
+
return SAFE_CHAINABLE_PROXY;
|
|
1165
1616
|
},
|
|
1166
1617
|
});
|
|
1167
1618
|
|