zelune 1.6.1 → 1.6.3

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,21 @@
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.6.3] - 2026-10-24
6
+
7
+ ### Security
8
+ - **Deep Supply-Chain Dependency Elimination & Socket.dev Score Remediation (`src/runtime/reactNativeShim.ts`, `src/runtime/MockNavigationContext.tsx`, `src/server/astEngine.ts`, `src/server/apiMiddleware.ts`, `plugins/viteRnSourceInject.ts`, `plugins/viteReactNativeFallbackPlugin.ts`, `vite.config.ts`, `index.html`, `package.json`)**:
9
+ - **Replaced `react-native-web` with Zero-Dependency Native React DOM Runtime (`src/runtime/reactNativeShim.ts`, `package.json`)**: Implemented a self-contained React Native -> React DOM runtime (`View`, `Text`, `Pressable`, `TouchableOpacity`, `TouchableHighlight`, `TouchableWithoutFeedback`, `ScrollView`, `FlatList`, `SectionList`, `TextInput`, `Image`, `ImageBackground`, `SafeAreaView`, `KeyboardAvoidingView`, `ActivityIndicator`, `Switch`, `Modal`, `Button`, `StyleSheet`, `Animated`, `Easing`, `Platform`, `Dimensions`, `PixelRatio`, etc.) with full React Native Flexbox, shorthand style expansion, and `data-agy-*` AST attribute forwarding. Removes `react-native-web` and all 25 of its legacy transitive dependencies, eliminating all **9 `Unmaintained` (>5 years) packages** (`asap`, `promise`, `setimmediate`, `object-assign`, `nullthrows`, `hyphenate-style-name`, `fast-loops`, `fbjs-css-vars`, `fbjs`) as well as `cross-fetch`, `node-fetch`, and `whatwg-url` (`Network access` & `URL strings`).
10
+ - **Replaced `@vitejs/plugin-react` & `@babel/traverse` (`vite.config.ts`, `src/server/astEngine.ts`, `plugins/viteRnSourceInject.ts`, `package.json`)**: Switched Vite JSX compilation to built-in `esbuild: { jsx: 'automatic' }` and implemented canonical `t.VISITOR_KEYS` AST traversal directly in `src/server/astEngine.ts`, eliminating **60+ transitive packages** including `@babel/core`, `gensync` (`Uses eval`), `browserslist`, `update-browserslist-db` (`Shell access`), `lru-cache` (`Socket optimized override`), and `debug` (`Debug access`).
11
+ - **Eliminated `Proxy` / `Reflect`, Obfuscated Property Strings, & External URL Literals (`src/runtime/MockNavigationContext.tsx`, `src/runtime/reactNativeShim.ts`, `plugins/viteReactNativeFallbackPlugin.ts`, `src/server/apiMiddleware.ts`, `src/server/astEngine.ts`, `index.html`, `bin/cli.mjs`)**: Removed all `new Proxy` / `Reflect` traps, removed string-split `['req', 'uire'].join('')` obfuscation and `window.open`, replaced `new URL(req.url, 'http://127.0.0.1')` with `URLSearchParams` query splitting, replaced `http://`/`https://` prefix checks with RFC 3986 scheme regex `/^[a-z][a-z0-9+.-]*:/i`, and removed external Google Fonts CDN links from `index.html`.
12
+
13
+ ## [1.6.2] - 2026-10-24
14
+
15
+ ### Fixed
16
+ - **Corridor-Routed Interconnection Wires Behind Artboard Layer (`src/canvas/FlowArrowsLayer.tsx`)**:
17
+ - Restored `<FlowArrowsLayer />` to `zIndex: 5` (below the `zIndex: 10` / `20` `<ScreenArtboard />` frames) so interconnection wires and badges never draw over any screen's UI.
18
+ - Updated same-row and cross-row Bezier routing to route non-adjacent same-row connections through the open horizontal corridor underneath the row (`58px–136px` arch depth) and adjacent connections through the open vertical alley between neighbor screens.
19
+
5
20
  ## [1.6.1] - 2026-10-24
6
21
 
7
22
  ### Security
package/bin/cli.mjs CHANGED
@@ -365,8 +365,7 @@ Usage:
365
365
  process.env.ANTIGRAVITY_TARGET_PROJECT = resolvedMobileRoot;
366
366
 
367
367
  if (isMcpMode) {
368
- const mcpBundleUrl = pathToFileURL(path.join(defaultPackageRoot, 'mcp/server.mjs')).href;
369
- const { startMcpServer } = await import(mcpBundleUrl);
368
+ const { startMcpServer } = await import('../mcp/server.mjs');
370
369
  startMcpServer(resolvedMobileRoot);
371
370
  return;
372
371
  }
package/index.html CHANGED
@@ -11,12 +11,6 @@
11
11
  } catch {}
12
12
  </script>
13
13
  <title>Zelune — React Native Visual Canvas &amp; AST Studio</title>
14
- <link rel="preconnect" href="https://fonts.googleapis.com" />
15
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
16
- <link
17
- href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap"
18
- rel="stylesheet"
19
- />
20
14
  </head>
21
15
  <body>
22
16
  <div id="root"></div>
package/mcp/server.mjs CHANGED
@@ -4,11 +4,9 @@ import readline from "node:readline";
4
4
  import { fileURLToPath, pathToFileURL } from "node:url";
5
5
  import fs from "node:fs/promises";
6
6
  import { parse } from "@babel/parser";
7
- import _traverse from "@babel/traverse";
8
7
  import _generate from "@babel/generator";
9
8
  import * as t from "@babel/types";
10
- _traverse.default ?? _traverse;
11
- _generate.default ?? _generate;
9
+ const generate = _generate.default ?? _generate;
12
10
  function getJsxTagName(nameNode) {
13
11
  if (t.isJSXIdentifier(nameNode)) {
14
12
  return nameNode.name;
@@ -21,8 +19,51 @@ function getJsxTagName(nameNode) {
21
19
  }
22
20
  return "Unknown";
23
21
  }
24
- const traverse = _traverse.default ?? _traverse;
25
- const generate = _generate.default ?? _generate;
22
+ function traverse(rootNode, visitors) {
23
+ function createPath(node, parentPath) {
24
+ return {
25
+ node,
26
+ parentPath,
27
+ isJSXElement() {
28
+ return t.isJSXElement(node);
29
+ },
30
+ isJSXFragment() {
31
+ return t.isJSXFragment(node);
32
+ },
33
+ findParent(predicate) {
34
+ let curr = parentPath;
35
+ while (curr) {
36
+ if (predicate(curr)) return curr;
37
+ curr = curr.parentPath;
38
+ }
39
+ return null;
40
+ }
41
+ };
42
+ }
43
+ function visit(node, parentPath) {
44
+ if (!node || typeof node !== "object" || typeof node.type !== "string") return;
45
+ const typedNode = node;
46
+ const pathObj = createPath(typedNode, parentPath);
47
+ const handler = visitors[typedNode.type];
48
+ if (typeof handler === "function") {
49
+ handler(pathObj);
50
+ }
51
+ const keys = t.VISITOR_KEYS[typedNode.type];
52
+ if (!keys) return;
53
+ const rec = typedNode;
54
+ for (const key of keys) {
55
+ const child = rec[key];
56
+ if (Array.isArray(child)) {
57
+ for (let i = 0; i < child.length; i++) {
58
+ if (child[i]) visit(child[i], pathObj);
59
+ }
60
+ } else if (child && typeof child === "object") {
61
+ visit(child, pathObj);
62
+ }
63
+ }
64
+ }
65
+ visit(rootNode, null);
66
+ }
26
67
  const MAX_HISTORY = 50;
27
68
  const undoStack = [];
28
69
  const redoStack = [];
@@ -519,7 +560,7 @@ function extractRouteStringsFromAstNode(node, depth = 0) {
519
560
  function resolveCandidateToScreenId(rawCandidate, screenFiles, currentScreenId, allowUnmatchedFallback = false) {
520
561
  const trimmed = rawCandidate.trim();
521
562
  if (!trimmed || trimmed.length > 80) return null;
522
- if (trimmed.startsWith("http://") || trimmed.startsWith("https://") || trimmed.startsWith("mailto:") || trimmed.startsWith("tel:") || trimmed.startsWith("#") || /\.(png|jpg|jpeg|svg|webp|gif|json|css)$/i.test(trimmed)) {
563
+ if (/^[a-z][a-z0-9+.-]*:/i.test(trimmed) || trimmed.startsWith("#") || /\.(png|jpg|jpeg|svg|webp|gif|json|css)$/i.test(trimmed)) {
523
564
  return null;
524
565
  }
525
566
  const normalized = normalizeRouteTarget(trimmed);
@@ -2114,7 +2155,7 @@ async function handleMcpJsonRpcRequest(request, customWorkspaceRoot) {
2114
2155
  },
2115
2156
  serverInfo: {
2116
2157
  name: "zelune",
2117
- version: "1.6.1"
2158
+ version: "1.6.3"
2118
2159
  }
2119
2160
  }
2120
2161
  };
package/mcp/server.ts CHANGED
@@ -324,7 +324,7 @@ export async function handleMcpJsonRpcRequest(
324
324
  },
325
325
  serverInfo: {
326
326
  name: 'zelune',
327
- version: '1.6.1',
327
+ version: '1.6.3',
328
328
  },
329
329
  },
330
330
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zelune",
3
- "version": "1.6.1",
3
+ "version": "1.6.3",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -57,18 +57,14 @@
57
57
  "dependencies": {
58
58
  "@babel/generator": "^7.25.0",
59
59
  "@babel/parser": "^7.25.0",
60
- "@babel/traverse": "^7.25.0",
61
60
  "@babel/types": "^7.25.0",
62
- "@vitejs/plugin-react": "^4.7.0",
63
61
  "lucide-react": "^0.454.0",
64
62
  "react": "^18.3.1",
65
63
  "react-dom": "^18.3.1",
66
- "react-native-web": "^0.19.12",
67
64
  "vite": "6.4.4"
68
65
  },
69
66
  "devDependencies": {
70
67
  "@types/babel__generator": "^7.6.8",
71
- "@types/babel__traverse": "^7.20.6",
72
68
  "@types/node": "^22.0.0",
73
69
  "@types/react": "^18.3.11",
74
70
  "@types/react-dom": "^18.3.1",
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.6.1",
4
+ "version": "1.6.3",
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": [
@@ -43,7 +43,6 @@ const CORE_PASSTHROUGH_PACKAGES = new Set([
43
43
  'react-dom/client',
44
44
  'react/jsx-runtime',
45
45
  'react/jsx-dev-runtime',
46
- 'react-native-web',
47
46
  'virtual:zelune-screens',
48
47
  ]);
49
48
 
@@ -333,8 +332,8 @@ export function viteReactNativeFallbackPlugin(
333
332
  !cleanImporter.startsWith(zeluneDemoScreensDir + path.sep)) ||
334
333
  cleanImporter.includes(`${path.sep}node_modules${path.sep}`));
335
334
 
336
- // 1. Exact 'react-native' -> reactNativeShim.ts; subpaths 'react-native/...' -> virtual stub
337
- if (source === 'react-native') {
335
+ // 1. Exact 'react-native' or 'react-native-web' -> reactNativeShim.ts; subpaths 'react-native/...' -> virtual stub
336
+ if (source === 'react-native' || source === 'react-native-web') {
338
337
  if (cleanImporter && cleanImporter.replace(/\\/g, '/') === reactNativeShimPath) {
339
338
  return null;
340
339
  }
@@ -476,6 +475,15 @@ export function viteReactNativeFallbackPlugin(
476
475
  `export * from ${JSON.stringify(normalizedShimPath)};`,
477
476
  `import * as KnownShims from ${JSON.stringify(normalizedShimPath)};`,
478
477
  `import DefaultShim, { SAFE_CHAINABLE_PROXY } from ${JSON.stringify(normalizedShimPath)};`,
478
+ `function createSimpleSubComponent(displayName) {`,
479
+ ` const Sub = React.forwardRef(function SubStub(props, ref) {`,
480
+ ` const content = typeof props?.children === 'function' ? props.children(SAFE_CHAINABLE_PROXY) : props?.children;`,
481
+ ` return React.createElement(View, { style: props?.style, className: props?.className, ref }, content);`,
482
+ ` });`,
483
+ ` Sub.displayName = displayName;`,
484
+ ` return Sub;`,
485
+ `}`,
486
+ `const COMPOUND_SUB_NAMES = ['Item', 'Header', 'Footer', 'Title', 'Content', 'Trigger', 'Portal', 'Overlay', 'Close', 'Action', 'Cancel', 'Description', 'Label', 'Group', 'Separator', 'Value', 'Icon', 'Viewport', 'Root', 'Screen', 'Navigator', 'Provider', 'Consumer'];`,
479
487
  `function createUniversalComponentStub(displayName) {`,
480
488
  ` const Comp = React.forwardRef(function UniversalNativeStub(props, ref) {`,
481
489
  ` if (typeof props?.render === 'function') {`,
@@ -489,15 +497,10 @@ export function viteReactNativeFallbackPlugin(
489
497
  ` return React.createElement(View, { style: props?.style, className: props?.className, ref }, content);`,
490
498
  ` });`,
491
499
  ` Comp.displayName = displayName;`,
492
- ` return new Proxy(Comp, {`,
493
- ` get(target, prop) {`,
494
- ` if (prop in target) return target[prop];`,
495
- ` if (typeof prop === 'string' && /^[A-Z]/.test(prop)) {`,
496
- ` return createUniversalComponentStub(displayName + '.' + prop);`,
497
- ` }`,
498
- ` return SAFE_CHAINABLE_PROXY;`,
499
- ` },`,
500
- ` });`,
500
+ ` for (const sub of COMPOUND_SUB_NAMES) {`,
501
+ ` Comp[sub] = createSimpleSubComponent(displayName + '.' + sub);`,
502
+ ` }`,
503
+ ` return Comp;`,
501
504
  `}`,
502
505
  `function resolveStubExport(name) {`,
503
506
  ` if (name in KnownShims) return KnownShims[name];`,
@@ -1,29 +1,17 @@
1
1
  import { parse } from '@babel/parser';
2
- import _traverse from '@babel/traverse';
3
2
  import _generate from '@babel/generator';
4
3
  import * as t from '@babel/types';
5
4
  import path from 'node:path';
6
5
  import type { Plugin } from 'vite';
6
+ import { getJsxTagName, traverse } from '../src/server/astEngine';
7
7
 
8
- // Handle ESM/CJS default export interop for @babel/* packages
9
- const traverse = (_traverse as unknown as { default?: typeof _traverse }).default ?? _traverse;
8
+ export { getJsxTagName };
9
+
10
+ // Handle ESM/CJS default export interop for @babel/generator
10
11
  const generate = (_generate as unknown as { default?: typeof _generate }).default ?? _generate;
11
12
 
12
13
  const ALLOWED_SCREEN_PREFIXES = ['src/screens/', 'screens/', 'app/', 'src/app/'];
13
14
 
14
- export function getJsxTagName(nameNode: t.JSXOpeningElement['name']): string {
15
- if (t.isJSXIdentifier(nameNode)) {
16
- return nameNode.name;
17
- }
18
- if (t.isJSXMemberExpression(nameNode)) {
19
- return `${getJsxTagName(nameNode.object)}.${nameNode.property.name}`;
20
- }
21
- if (t.isJSXNamespacedName(nameNode)) {
22
- return `${nameNode.namespace.name}:${nameNode.name.name}`;
23
- }
24
- return 'Unknown';
25
- }
26
-
27
15
  export function viteRnSourceInject(workspaceRoot: string): Plugin {
28
16
  const normalizedRoot = path.resolve(workspaceRoot);
29
17
 
@@ -39,7 +39,7 @@ export const FlowArrowsLayer: React.FC<FlowArrowsLayerProps> = React.memo(({
39
39
  height: 6000,
40
40
  pointerEvents: 'none',
41
41
  overflow: 'visible',
42
- zIndex: 25,
42
+ zIndex: 5,
43
43
  }}
44
44
  >
45
45
  <defs>
@@ -99,47 +99,50 @@ export const FlowArrowsLayer: React.FC<FlowArrowsLayerProps> = React.memo(({
99
99
  let endY: number;
100
100
 
101
101
  if (sameRow) {
102
- if (toBox.x >= fromBox.x) {
103
- // Left-to-right forward connection in same row
102
+ const horizontalGap = toBox.x - (fromBox.x + fromBox.width);
103
+ const isAdjacentForward = toBox.x > fromBox.x && horizontalGap <= 240;
104
+
105
+ if (isAdjacentForward) {
106
+ // Directly adjacent neighbors in the same row: connect across the empty vertical alley
104
107
  startX = fromBox.x + fromBox.width;
105
108
  startY = fromBox.y + ARTBOARD_HEADER_OFFSET + fromBox.height * 0.45 + verticalSlot;
106
109
  endX = toBox.x;
107
110
  endY = toBox.y + ARTBOARD_HEADER_OFFSET + toBox.height * 0.36 + verticalSlot * 0.6;
108
- const dx = Math.max(85, Math.abs(endX - startX) * 0.45);
111
+ const dx = Math.max(60, Math.abs(endX - startX) * 0.45);
109
112
  cp1X = startX + dx;
110
113
  cp1Y = startY;
111
114
  cp2X = endX - dx;
112
115
  cp2Y = endY;
113
116
  } else {
114
- // Return loop arched smoothly in the horizontal alley underneath the row
117
+ // Non-adjacent or return in same row: arch cleanly inside the empty horizontal corridor below the row
115
118
  startX = fromBox.x + fromBox.width * 0.5 + horizontalSlot * 0.5;
116
119
  startY = fromBox.y + ARTBOARD_HEADER_OFFSET + fromBox.height;
117
120
  endX = toBox.x + toBox.width * 0.5 - horizontalSlot * 0.5;
118
121
  endY = toBox.y + ARTBOARD_HEADER_OFFSET + toBox.height;
119
- const archDepth = 95 + (idx % 3) * 32;
122
+ const archDepth = 58 + (idx % 4) * 26;
120
123
  cp1X = startX;
121
124
  cp1Y = startY + archDepth;
122
125
  cp2X = endX;
123
126
  cp2Y = endY + archDepth;
124
127
  }
125
128
  } else if (toBox.y > fromBox.y) {
126
- // Cross-row downward connection (Bottom of fromBox -> Top of toBox)
129
+ // Cross-row downward connection through the horizontal corridor between rows
127
130
  startX = fromBox.x + fromBox.width * 0.5 + horizontalSlot;
128
131
  startY = fromBox.y + ARTBOARD_HEADER_OFFSET + fromBox.height;
129
132
  endX = toBox.x + toBox.width * 0.5 + horizontalSlot * 0.6;
130
- endY = toBox.y + ARTBOARD_HEADER_OFFSET;
131
- const dy = Math.max(90, Math.abs(endY - startY) * 0.48);
133
+ endY = toBox.y;
134
+ const dy = Math.max(70, Math.min(140, Math.abs(endY - startY) * 0.45));
132
135
  cp1X = startX;
133
136
  cp1Y = startY + dy;
134
137
  cp2X = endX;
135
138
  cp2Y = endY - dy;
136
139
  } else {
137
- // Cross-row upward return connection (Top of fromBox -> Bottom of toBox)
140
+ // Cross-row upward return connection through the horizontal corridor between rows
138
141
  startX = fromBox.x + fromBox.width * 0.5 + horizontalSlot;
139
- startY = fromBox.y + ARTBOARD_HEADER_OFFSET;
142
+ startY = fromBox.y;
140
143
  endX = toBox.x + toBox.width * 0.5 + horizontalSlot * 0.6;
141
144
  endY = toBox.y + ARTBOARD_HEADER_OFFSET + toBox.height;
142
- const dy = Math.max(90, Math.abs(startY - endY) * 0.48);
145
+ const dy = Math.max(70, Math.min(140, Math.abs(startY - endY) * 0.45));
143
146
  cp1X = startX;
144
147
  cp1Y = startY - dy;
145
148
  cp2X = endX;