zelune 1.5.6 → 1.6.1

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.
@@ -11,7 +11,66 @@ import {
11
11
  ScrollView as RNScrollView,
12
12
  FlatList as RNFlatList,
13
13
  } from 'react-native';
14
- import { withNativeWind } from './nativewindShim';
14
+ import { withNativeWind, resolveTailwindClassNameToStyle } from './nativewindShim';
15
+
16
+ declare global {
17
+ interface Window {
18
+ __ZELUNE_PUBLIC_ENV__?: Record<string, string>;
19
+ }
20
+ }
21
+
22
+ if (typeof window !== 'undefined') {
23
+ const baseEnv: Record<string, string> = window.__ZELUNE_PUBLIC_ENV__ ?? {
24
+ NODE_ENV: 'development',
25
+ EXPO_PUBLIC_PLATFORM: 'web',
26
+ };
27
+ const envProxy = new Proxy(baseEnv, {
28
+ get(target, prop) {
29
+ if (typeof prop !== 'string') return Reflect.get(target, prop);
30
+ if (prop in target && target[prop] !== undefined && target[prop] !== '') {
31
+ return target[prop];
32
+ }
33
+ return 'zelune-preview';
34
+ },
35
+ });
36
+
37
+ const createAssetStub = (): unknown => {
38
+ const fn = () => proxy;
39
+ const proxy: unknown = new Proxy(fn, {
40
+ get(_t, prop) {
41
+ if (prop === Symbol.toPrimitive) return (hint: string) => (hint === 'number' ? 0 : '');
42
+ if (prop === 'valueOf') return () => 0;
43
+ if (prop === 'toString') return () => '';
44
+ if (prop === 'uri' || prop === 'default') return '';
45
+ return proxy;
46
+ },
47
+ });
48
+ return proxy;
49
+ };
50
+
51
+ const win = window as unknown as Record<string, unknown>;
52
+ win.global = window;
53
+ win.__DEV__ = true;
54
+ if (!win.process || typeof win.process !== 'object') {
55
+ win.process = {
56
+ env: envProxy,
57
+ platform: 'browser',
58
+ version: '',
59
+ cwd: () => '/',
60
+ };
61
+ } else {
62
+ (win.process as Record<string, unknown>).env = envProxy;
63
+ }
64
+ const reqProp = ['req', 'uire'].join('');
65
+ if (typeof win[reqProp] !== 'function') {
66
+ win[reqProp] = (modId: unknown) => {
67
+ if (typeof modId === 'string' && /\.(png|jpe?g|gif|webp|svg|avif|bmp)$/i.test(modId)) {
68
+ return { uri: modId, default: modId };
69
+ }
70
+ return createAssetStub();
71
+ };
72
+ }
73
+ }
15
74
 
16
75
  export interface NavigationController {
17
76
  navigate: (targetRoute: string, params?: Record<string, unknown>) => void;
@@ -1221,6 +1280,53 @@ export const clsx = (...args: unknown[]) =>
1221
1280
  export const cn = clsx;
1222
1281
  export const twMerge = clsx;
1223
1282
 
1283
+ // NativeWind (v2 / v4) & twrnc Universal Shims
1284
+ export function styled<P extends Record<string, unknown>>(
1285
+ Component: React.ComponentType<P>,
1286
+ baseClassName?: string
1287
+ ): React.ComponentType<P> {
1288
+ const Wrapped = withNativeWind(Component as unknown as React.ComponentType<Record<string, unknown>>);
1289
+ if (!baseClassName) return Wrapped as unknown as React.ComponentType<P>;
1290
+ return React.forwardRef<unknown, P>((props, ref) => {
1291
+ const combinedClass = [baseClassName, props?.className].filter(Boolean).join(' ');
1292
+ return React.createElement(Wrapped, { ...props, className: combinedClass, ref });
1293
+ }) as unknown as React.ComponentType<P>;
1294
+ }
1295
+
1296
+ export function cssInterop<P extends Record<string, unknown>>(
1297
+ Component: React.ComponentType<P>
1298
+ ): React.ComponentType<P> {
1299
+ return withNativeWind(Component as unknown as React.ComponentType<Record<string, unknown>>) as unknown as React.ComponentType<P>;
1300
+ }
1301
+
1302
+ export const remapProps = cssInterop;
1303
+
1304
+ function twFn(stringsOrClass: TemplateStringsArray | string, ...values: unknown[]): Record<string, unknown> {
1305
+ if (typeof stringsOrClass === 'string') {
1306
+ return resolveTailwindClassNameToStyle(stringsOrClass) ?? {};
1307
+ }
1308
+ if (Array.isArray(stringsOrClass)) {
1309
+ const built = stringsOrClass.reduce(
1310
+ (acc, str, idx) => acc + str + (values[idx] !== undefined ? String(values[idx]) : ''),
1311
+ ''
1312
+ );
1313
+ return resolveTailwindClassNameToStyle(built) ?? {};
1314
+ }
1315
+ return {};
1316
+ }
1317
+
1318
+ twFn.style = (...args: unknown[]) => {
1319
+ const mergedClass = args.filter((a): a is string => typeof a === 'string' && a.length > 0).join(' ');
1320
+ return resolveTailwindClassNameToStyle(mergedClass) ?? {};
1321
+ };
1322
+ twFn.color = (token: string) => {
1323
+ const s = resolveTailwindClassNameToStyle(`text-${token}`);
1324
+ return (s?.color as string | undefined) ?? '#0F172A';
1325
+ };
1326
+
1327
+ export const tw = twFn;
1328
+ export const createTw = () => twFn;
1329
+
1224
1330
  // React Hook Form Shims (react-hook-form)
1225
1331
  export function useForm(config?: { defaultValues?: Record<string, unknown> }) {
1226
1332
  const defaults = config?.defaultValues ?? {};
@@ -376,6 +376,7 @@ export function resolveTailwindClassNameToStyle(className?: string): Record<stri
376
376
  (typeof window !== 'undefined' && window.__ZELUNE_TAILWIND_THEME__) || {};
377
377
  const themeColors = (projectTheme.colors ?? {}) as Record<string, unknown>;
378
378
  const themeFontSizes = (projectTheme.fontSize ?? {}) as Record<string, unknown>;
379
+ const themeFontFamilies = (projectTheme.fontFamily ?? {}) as Record<string, unknown>;
379
380
  const themeRadii = (projectTheme.borderRadius ?? {}) as Record<string, unknown>;
380
381
  const themeSpacing = (projectTheme.spacing ?? {}) as Record<string, unknown>;
381
382
 
@@ -808,6 +809,30 @@ export function resolveTailwindClassNameToStyle(className?: string): Record<stri
808
809
  style.fontFamily = 'monospace';
809
810
  continue;
810
811
  }
812
+ if (token.startsWith('font-')) {
813
+ const fontKey = token.slice(5);
814
+ const customFontEntry = themeFontFamilies[fontKey];
815
+ if (customFontEntry !== undefined) {
816
+ const primaryFont = Array.isArray(customFontEntry)
817
+ ? String(customFontEntry[0] ?? '')
818
+ : String(customFontEntry ?? '');
819
+ const lowerFont = primaryFont.toLowerCase();
820
+ if (lowerFont.includes('800') || lowerFont.includes('extrabold') || lowerFont.includes('black')) {
821
+ style.fontWeight = '800';
822
+ } else if (lowerFont.includes('700') || lowerFont.includes('bold')) {
823
+ style.fontWeight = lowerFont.includes('semibold') ? '600' : '700';
824
+ } else if (lowerFont.includes('600') || lowerFont.includes('semibold')) {
825
+ style.fontWeight = '600';
826
+ } else if (lowerFont.includes('500') || lowerFont.includes('medium')) {
827
+ style.fontWeight = '500';
828
+ } else if (lowerFont.includes('300') || lowerFont.includes('light')) {
829
+ style.fontWeight = '300';
830
+ } else {
831
+ style.fontWeight = '400';
832
+ }
833
+ continue;
834
+ }
835
+ }
811
836
 
812
837
  // Letter spacing (tracking-*)
813
838
  if (token === 'tracking-tighter') {
@@ -8,11 +8,17 @@ import {
8
8
  inspectElement,
9
9
  patchElement,
10
10
  reorderElement,
11
+ insertElement,
12
+ duplicateElement,
13
+ deleteElement,
11
14
  undoLastPatch,
12
15
  redoLastPatch,
13
16
  getHistoryStatus,
14
17
  type PatchPayload,
15
18
  type ReorderPayload,
19
+ type InsertElementPayload,
20
+ type DuplicateElementPayload,
21
+ type DeleteElementPayload,
16
22
  } from './astEngine';
17
23
 
18
24
  const MAX_BODY_BYTES = 1_000_000;
@@ -285,6 +291,48 @@ export function viteCanvasApiMiddleware(workspaceRoot: string): Plugin {
285
291
  });
286
292
  }
287
293
 
294
+ // POST /api/insert
295
+ if (req.method === 'POST' && pathname === '/api/insert') {
296
+ const body = await readJsonBody<InsertElementPayload>(req);
297
+ const result = await insertElement(workspaceRoot, body);
298
+ return sendJson(res, 200, {
299
+ ok: true,
300
+ element: result.element,
301
+ history: {
302
+ undoAvailable: result.undoAvailable,
303
+ redoAvailable: result.redoAvailable,
304
+ },
305
+ });
306
+ }
307
+
308
+ // POST /api/duplicate
309
+ if (req.method === 'POST' && pathname === '/api/duplicate') {
310
+ const body = await readJsonBody<DuplicateElementPayload>(req);
311
+ const result = await duplicateElement(workspaceRoot, body);
312
+ return sendJson(res, 200, {
313
+ ok: true,
314
+ element: result.element,
315
+ history: {
316
+ undoAvailable: result.undoAvailable,
317
+ redoAvailable: result.redoAvailable,
318
+ },
319
+ });
320
+ }
321
+
322
+ // POST /api/delete
323
+ if (req.method === 'POST' && pathname === '/api/delete') {
324
+ const body = await readJsonBody<DeleteElementPayload>(req);
325
+ const result = await deleteElement(workspaceRoot, body);
326
+ return sendJson(res, 200, {
327
+ ok: true,
328
+ deletedAstPath: result.deletedAstPath,
329
+ history: {
330
+ undoAvailable: result.undoAvailable,
331
+ redoAvailable: result.redoAvailable,
332
+ },
333
+ });
334
+ }
335
+
288
336
  // POST /api/undo
289
337
  if (req.method === 'POST' && pathname === '/api/undo') {
290
338
  const result = await undoLastPatch(workspaceRoot);