zelune 1.5.6 → 1.5.7

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 CHANGED
@@ -2,6 +2,13 @@
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.7] - 2026-10-24
6
+
7
+ ### Added
8
+ - **Universal NativeWind v2/v4 (`styled`, `cssInterop`, `remapProps`), `twrnc` (`tw`, `createTw`), & TypeScript `tailwind.config.ts` Support (`src/runtime/nativewindShim.ts`, `src/runtime/MockNavigationContext.tsx`, `vite.config.ts`)**:
9
+ - Added `styled(Component, baseClassName)` (NativeWind v2), `cssInterop` / `remapProps` (NativeWind v4), and `tw` / `createTw` (`twrnc` tagged templates and `.style()` helper) to `MockNavigationContext.tsx` so any React Native project worldwide using NativeWind v2, NativeWind v4, or `twrnc` works out-of-the-box.
10
+ - Added dynamic `fontFamily` token resolution from `window.__ZELUNE_TAILWIND_THEME__.fontFamily` in `src/runtime/nativewindShim.ts` and TypeScript type-stripping (`const config: Config =`, `satisfies Config`, `as const`) in `loadProjectTailwindTheme` (`vite.config.ts`).
11
+
5
12
  ## [1.5.6] - 2026-10-24
6
13
 
7
14
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zelune",
3
- "version": "1.5.6",
3
+ "version": "1.5.7",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -11,7 +11,7 @@ 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
15
 
16
16
  export interface NavigationController {
17
17
  navigate: (targetRoute: string, params?: Record<string, unknown>) => void;
@@ -1221,6 +1221,53 @@ export const clsx = (...args: unknown[]) =>
1221
1221
  export const cn = clsx;
1222
1222
  export const twMerge = clsx;
1223
1223
 
1224
+ // NativeWind (v2 / v4) & twrnc Universal Shims
1225
+ export function styled<P extends Record<string, unknown>>(
1226
+ Component: React.ComponentType<P>,
1227
+ baseClassName?: string
1228
+ ): React.ComponentType<P> {
1229
+ const Wrapped = withNativeWind(Component as unknown as React.ComponentType<Record<string, unknown>>);
1230
+ if (!baseClassName) return Wrapped as unknown as React.ComponentType<P>;
1231
+ return React.forwardRef<unknown, P>((props, ref) => {
1232
+ const combinedClass = [baseClassName, props?.className].filter(Boolean).join(' ');
1233
+ return React.createElement(Wrapped, { ...props, className: combinedClass, ref });
1234
+ }) as unknown as React.ComponentType<P>;
1235
+ }
1236
+
1237
+ export function cssInterop<P extends Record<string, unknown>>(
1238
+ Component: React.ComponentType<P>
1239
+ ): React.ComponentType<P> {
1240
+ return withNativeWind(Component as unknown as React.ComponentType<Record<string, unknown>>) as unknown as React.ComponentType<P>;
1241
+ }
1242
+
1243
+ export const remapProps = cssInterop;
1244
+
1245
+ function twFn(stringsOrClass: TemplateStringsArray | string, ...values: unknown[]): Record<string, unknown> {
1246
+ if (typeof stringsOrClass === 'string') {
1247
+ return resolveTailwindClassNameToStyle(stringsOrClass) ?? {};
1248
+ }
1249
+ if (Array.isArray(stringsOrClass)) {
1250
+ const built = stringsOrClass.reduce(
1251
+ (acc, str, idx) => acc + str + (values[idx] !== undefined ? String(values[idx]) : ''),
1252
+ ''
1253
+ );
1254
+ return resolveTailwindClassNameToStyle(built) ?? {};
1255
+ }
1256
+ return {};
1257
+ }
1258
+
1259
+ twFn.style = (...args: unknown[]) => {
1260
+ const mergedClass = args.filter((a): a is string => typeof a === 'string' && a.length > 0).join(' ');
1261
+ return resolveTailwindClassNameToStyle(mergedClass) ?? {};
1262
+ };
1263
+ twFn.color = (token: string) => {
1264
+ const s = resolveTailwindClassNameToStyle(`text-${token}`);
1265
+ return (s?.color as string | undefined) ?? '#0F172A';
1266
+ };
1267
+
1268
+ export const tw = twFn;
1269
+ export const createTw = () => twFn;
1270
+
1224
1271
  // React Hook Form Shims (react-hook-form)
1225
1272
  export function useForm(config?: { defaultValues?: Record<string, unknown> }) {
1226
1273
  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') {
package/vite.config.ts CHANGED
@@ -112,6 +112,9 @@ function loadProjectTailwindTheme(roots: string[]): Record<string, unknown> {
112
112
  const raw = fs.readFileSync(fullPath, 'utf-8');
113
113
  const cleaned = raw
114
114
  .replace(/^import\s+.*$/gm, '')
115
+ .replace(/\b(const|let|var)\s+([A-Za-z0-9_$]+)\s*:\s*[^=]+=/g, '$1 $2 =')
116
+ .replace(/\bsatisfies\s+[A-Za-z0-9_$.<>]+/g, '')
117
+ .replace(/\bas\s+const\b/g, '')
115
118
  .replace(/export\s+default\s+/, 'module.exports = ');
116
119
  const mod = { exports: {} as Record<string, unknown> };
117
120
  const fakeRequire = () => ({});