zelune 1.5.7 → 1.6.2
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 +34 -0
- package/bin/cli.mjs +22 -42
- package/mcp/server.mjs +2228 -0
- package/mcp/server.ts +367 -117
- package/package.json +3 -5
- package/plugin.json +1 -1
- package/plugins/viteReactNativeFallbackPlugin.ts +2 -11
- package/src/canvas/CanvasApp.tsx +174 -3
- package/src/canvas/FlowArrowsLayer.tsx +86 -44
- package/src/canvas/InspectorPanel.tsx +529 -23
- package/src/runtime/MockNavigationContext.tsx +59 -0
- package/src/server/apiMiddleware.ts +48 -0
- package/src/server/astEngine.ts +1291 -33
- package/vite.config.ts +177 -115
package/mcp/server.ts
CHANGED
|
@@ -1,134 +1,198 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
1
2
|
import path from 'node:path';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
|
-
import {
|
|
6
|
-
CallToolRequestSchema,
|
|
7
|
-
ListToolsRequestSchema,
|
|
8
|
-
} from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
import readline from 'node:readline';
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
9
5
|
import {
|
|
10
6
|
discoverScreens,
|
|
11
7
|
inspectElement,
|
|
12
8
|
patchElement,
|
|
13
9
|
reorderElement,
|
|
10
|
+
insertElement,
|
|
11
|
+
duplicateElement,
|
|
12
|
+
deleteElement,
|
|
13
|
+
type InsertElementPreset,
|
|
14
14
|
type StyleValue,
|
|
15
15
|
} from '../src/server/astEngine.js';
|
|
16
16
|
|
|
17
17
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
18
|
-
const
|
|
18
|
+
const DEFAULT_WORKSPACE_ROOT = path.resolve(__dirname, '..');
|
|
19
|
+
|
|
20
|
+
export interface JsonRpcRequest {
|
|
21
|
+
jsonrpc?: string;
|
|
22
|
+
id?: string | number | null;
|
|
23
|
+
method?: string;
|
|
24
|
+
params?: Record<string, unknown>;
|
|
25
|
+
}
|
|
19
26
|
|
|
20
|
-
|
|
27
|
+
export interface JsonRpcResponse {
|
|
28
|
+
jsonrpc: '2.0';
|
|
29
|
+
id: string | number | null;
|
|
30
|
+
result?: unknown;
|
|
31
|
+
error?: {
|
|
32
|
+
code: number;
|
|
33
|
+
message: string;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const MCP_TOOLS = [
|
|
21
38
|
{
|
|
22
|
-
name: '
|
|
23
|
-
|
|
39
|
+
name: 'list_canvas_screens',
|
|
40
|
+
description:
|
|
41
|
+
'Lists all discovered React Native screens on the visual canvas along with their element counts and outgoing navigation flow edges.',
|
|
42
|
+
inputSchema: {
|
|
43
|
+
type: 'object',
|
|
44
|
+
properties: {},
|
|
45
|
+
},
|
|
24
46
|
},
|
|
25
47
|
{
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
'
|
|
39
|
-
inputSchema: {
|
|
40
|
-
type: 'object',
|
|
41
|
-
properties: {},
|
|
48
|
+
name: 'inspect_screen_element',
|
|
49
|
+
description:
|
|
50
|
+
'Inspects a specific JSX element inside a React Native screen by its relative file path and AST index path.',
|
|
51
|
+
inputSchema: {
|
|
52
|
+
type: 'object',
|
|
53
|
+
properties: {
|
|
54
|
+
file: {
|
|
55
|
+
type: 'string',
|
|
56
|
+
description: 'Relative path to the screen file (e.g. src/screens/HomeScreen.tsx)',
|
|
57
|
+
},
|
|
58
|
+
astPath: {
|
|
59
|
+
type: 'string',
|
|
60
|
+
description: 'Preorder AST index of the JSX element (e.g. "0", "4")',
|
|
42
61
|
},
|
|
43
62
|
},
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
63
|
+
required: ['file', 'astPath'],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'patch_screen_element',
|
|
68
|
+
description:
|
|
69
|
+
'Surgically patches the text copy or StyleSheet properties of a React Native JSX element.',
|
|
70
|
+
inputSchema: {
|
|
71
|
+
type: 'object',
|
|
72
|
+
properties: {
|
|
73
|
+
file: {
|
|
74
|
+
type: 'string',
|
|
75
|
+
description: 'Relative path to the screen file (e.g. src/screens/HomeScreen.tsx)',
|
|
76
|
+
},
|
|
77
|
+
astPath: {
|
|
78
|
+
type: 'string',
|
|
79
|
+
description: 'Preorder AST index of the JSX element',
|
|
80
|
+
},
|
|
81
|
+
text: {
|
|
82
|
+
type: 'string',
|
|
83
|
+
description: 'Optional new static text content for <Text> elements',
|
|
84
|
+
},
|
|
85
|
+
styles: {
|
|
49
86
|
type: 'object',
|
|
50
|
-
|
|
51
|
-
file: {
|
|
52
|
-
type: 'string',
|
|
53
|
-
description: 'Relative path to the screen file (e.g. src/screens/HomeScreen.tsx)',
|
|
54
|
-
},
|
|
55
|
-
astPath: {
|
|
56
|
-
type: 'string',
|
|
57
|
-
description: 'Preorder AST index of the JSX element (e.g. "0", "4")',
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
required: ['file', 'astPath'],
|
|
87
|
+
description: 'Optional map of React Native style properties to update',
|
|
61
88
|
},
|
|
62
89
|
},
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
required: ['file', 'astPath'],
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'reorder_screen_element',
|
|
95
|
+
description:
|
|
96
|
+
'Surgically reorders a React Native JSX element among its Flexbox siblings in the .tsx AST.',
|
|
97
|
+
inputSchema: {
|
|
98
|
+
type: 'object',
|
|
99
|
+
properties: {
|
|
100
|
+
file: {
|
|
101
|
+
type: 'string',
|
|
102
|
+
description: 'Relative path to the screen file (e.g. src/screens/HomeScreen.tsx)',
|
|
103
|
+
},
|
|
104
|
+
astPath: {
|
|
105
|
+
type: 'string',
|
|
106
|
+
description: 'Preorder AST index of the JSX element to move',
|
|
107
|
+
},
|
|
108
|
+
direction: {
|
|
109
|
+
type: 'string',
|
|
110
|
+
enum: ['up', 'down'],
|
|
111
|
+
description: 'Optional relative direction ("up" or "down") among siblings',
|
|
112
|
+
},
|
|
113
|
+
targetAstPath: {
|
|
114
|
+
type: 'string',
|
|
115
|
+
description: 'Optional AST index of the sibling to place before/after',
|
|
116
|
+
},
|
|
117
|
+
placement: {
|
|
118
|
+
type: 'string',
|
|
119
|
+
enum: ['before', 'after'],
|
|
120
|
+
description: 'Whether to insert before or after targetAstPath',
|
|
88
121
|
},
|
|
89
122
|
},
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
123
|
+
required: ['file', 'astPath'],
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: 'insert_screen_element',
|
|
128
|
+
description:
|
|
129
|
+
'Surgically inserts a new React Native component/section preset (button, section, row, heading, text, input, badge, divider) into a screen .tsx AST and auto-injects required react-native imports.',
|
|
130
|
+
inputSchema: {
|
|
131
|
+
type: 'object',
|
|
132
|
+
properties: {
|
|
133
|
+
file: {
|
|
134
|
+
type: 'string',
|
|
135
|
+
description: 'Relative path to the screen file (e.g. src/screens/HomeScreen.tsx)',
|
|
136
|
+
},
|
|
137
|
+
astPath: {
|
|
138
|
+
type: 'string',
|
|
139
|
+
description: 'Optional AST index of the reference JSX element',
|
|
140
|
+
},
|
|
141
|
+
placement: {
|
|
142
|
+
type: 'string',
|
|
143
|
+
enum: ['inside', 'after', 'before'],
|
|
144
|
+
description: 'Placement relative to astPath (defaults to "after" or "inside")',
|
|
145
|
+
},
|
|
146
|
+
preset: {
|
|
147
|
+
type: 'string',
|
|
148
|
+
enum: ['button', 'section', 'row', 'heading', 'text', 'input', 'badge', 'divider'],
|
|
149
|
+
description: 'Component preset to insert',
|
|
150
|
+
},
|
|
151
|
+
label: {
|
|
152
|
+
type: 'string',
|
|
153
|
+
description: 'Optional custom label/text/placeholder for the inserted component',
|
|
154
|
+
},
|
|
155
|
+
navigateTo: {
|
|
156
|
+
type: 'string',
|
|
157
|
+
description: 'Optional target screen ID if inserting a navigation button',
|
|
121
158
|
},
|
|
122
159
|
},
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
160
|
+
required: ['file', 'preset'],
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
name: 'duplicate_screen_element',
|
|
165
|
+
description: 'Duplicates a JSX element right after itself in the screen .tsx AST.',
|
|
166
|
+
inputSchema: {
|
|
167
|
+
type: 'object',
|
|
168
|
+
properties: {
|
|
169
|
+
file: { type: 'string' },
|
|
170
|
+
astPath: { type: 'string' },
|
|
171
|
+
},
|
|
172
|
+
required: ['file', 'astPath'],
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: 'delete_screen_element',
|
|
177
|
+
description: 'Deletes a non-root JSX element from the screen .tsx AST.',
|
|
178
|
+
inputSchema: {
|
|
179
|
+
type: 'object',
|
|
180
|
+
properties: {
|
|
181
|
+
file: { type: 'string' },
|
|
182
|
+
astPath: { type: 'string' },
|
|
183
|
+
},
|
|
184
|
+
required: ['file', 'astPath'],
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
];
|
|
129
188
|
|
|
189
|
+
async function executeMcpTool(
|
|
190
|
+
workspaceRoot: string,
|
|
191
|
+
name: string,
|
|
192
|
+
args: Record<string, unknown> | undefined
|
|
193
|
+
): Promise<{ content: Array<{ type: 'text'; text: string }> }> {
|
|
130
194
|
if (name === 'list_canvas_screens') {
|
|
131
|
-
const screens = await discoverScreens(
|
|
195
|
+
const screens = await discoverScreens(workspaceRoot);
|
|
132
196
|
return {
|
|
133
197
|
content: [{ type: 'text', text: JSON.stringify(screens, null, 2) }],
|
|
134
198
|
};
|
|
@@ -137,7 +201,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
137
201
|
if (name === 'inspect_screen_element') {
|
|
138
202
|
const file = String(args?.file ?? '');
|
|
139
203
|
const astPath = String(args?.astPath ?? '0');
|
|
140
|
-
const element = await inspectElement(
|
|
204
|
+
const element = await inspectElement(workspaceRoot, { file, astPath });
|
|
141
205
|
return {
|
|
142
206
|
content: [{ type: 'text', text: JSON.stringify(element, null, 2) }],
|
|
143
207
|
};
|
|
@@ -149,7 +213,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
149
213
|
const text = typeof args?.text === 'string' ? args.text : undefined;
|
|
150
214
|
const styles = (args?.styles as Record<string, StyleValue> | undefined) ?? undefined;
|
|
151
215
|
|
|
152
|
-
const result = await patchElement(
|
|
216
|
+
const result = await patchElement(workspaceRoot, {
|
|
153
217
|
file,
|
|
154
218
|
astPath,
|
|
155
219
|
text,
|
|
@@ -170,7 +234,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
170
234
|
const placement =
|
|
171
235
|
args?.placement === 'before' || args?.placement === 'after' ? args.placement : undefined;
|
|
172
236
|
|
|
173
|
-
const result = await reorderElement(
|
|
237
|
+
const result = await reorderElement(workspaceRoot, {
|
|
174
238
|
file,
|
|
175
239
|
astPath,
|
|
176
240
|
direction,
|
|
@@ -182,15 +246,201 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
182
246
|
};
|
|
183
247
|
}
|
|
184
248
|
|
|
249
|
+
if (name === 'insert_screen_element') {
|
|
250
|
+
const file = String(args?.file ?? '');
|
|
251
|
+
const preset = String(args?.preset ?? 'button') as InsertElementPreset;
|
|
252
|
+
const astPath = typeof args?.astPath === 'string' ? args.astPath : undefined;
|
|
253
|
+
const placement =
|
|
254
|
+
args?.placement === 'inside' || args?.placement === 'after' || args?.placement === 'before'
|
|
255
|
+
? args.placement
|
|
256
|
+
: undefined;
|
|
257
|
+
const label = typeof args?.label === 'string' ? args.label : undefined;
|
|
258
|
+
const navigateTo = typeof args?.navigateTo === 'string' ? args.navigateTo : undefined;
|
|
259
|
+
|
|
260
|
+
const result = await insertElement(workspaceRoot, {
|
|
261
|
+
file,
|
|
262
|
+
astPath,
|
|
263
|
+
placement,
|
|
264
|
+
preset,
|
|
265
|
+
label,
|
|
266
|
+
navigateTo,
|
|
267
|
+
});
|
|
268
|
+
return {
|
|
269
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (name === 'duplicate_screen_element') {
|
|
274
|
+
const file = String(args?.file ?? '');
|
|
275
|
+
const astPath = String(args?.astPath ?? '1');
|
|
276
|
+
const result = await duplicateElement(workspaceRoot, { file, astPath });
|
|
277
|
+
return {
|
|
278
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (name === 'delete_screen_element') {
|
|
283
|
+
const file = String(args?.file ?? '');
|
|
284
|
+
const astPath = String(args?.astPath ?? '1');
|
|
285
|
+
const result = await deleteElement(workspaceRoot, { file, astPath });
|
|
286
|
+
return {
|
|
287
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
185
291
|
throw new Error(`Unknown MCP tool: ${name}`);
|
|
186
|
-
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export async function handleMcpJsonRpcRequest(
|
|
295
|
+
request: JsonRpcRequest,
|
|
296
|
+
customWorkspaceRoot?: string
|
|
297
|
+
): Promise<JsonRpcResponse | null> {
|
|
298
|
+
if (request.id === undefined) {
|
|
299
|
+
// Notification (e.g. notifications/initialized)
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const workspaceRoot =
|
|
304
|
+
customWorkspaceRoot ||
|
|
305
|
+
process.env.ANTIGRAVITY_TARGET_PROJECT ||
|
|
306
|
+
process.env.ANTIGRAVITY_WORKSPACE_ROOT ||
|
|
307
|
+
DEFAULT_WORKSPACE_ROOT;
|
|
308
|
+
|
|
309
|
+
const method = request.method ?? '';
|
|
187
310
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
311
|
+
try {
|
|
312
|
+
if (method === 'initialize') {
|
|
313
|
+
const requestedProtocol =
|
|
314
|
+
typeof request.params?.protocolVersion === 'string'
|
|
315
|
+
? request.params.protocolVersion
|
|
316
|
+
: '2024-11-05';
|
|
317
|
+
return {
|
|
318
|
+
jsonrpc: '2.0',
|
|
319
|
+
id: request.id,
|
|
320
|
+
result: {
|
|
321
|
+
protocolVersion: requestedProtocol,
|
|
322
|
+
capabilities: {
|
|
323
|
+
tools: {},
|
|
324
|
+
},
|
|
325
|
+
serverInfo: {
|
|
326
|
+
name: 'zelune',
|
|
327
|
+
version: '1.6.1',
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (method === 'ping') {
|
|
334
|
+
return {
|
|
335
|
+
jsonrpc: '2.0',
|
|
336
|
+
id: request.id,
|
|
337
|
+
result: {},
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (method === 'tools/list') {
|
|
342
|
+
return {
|
|
343
|
+
jsonrpc: '2.0',
|
|
344
|
+
id: request.id,
|
|
345
|
+
result: {
|
|
346
|
+
tools: MCP_TOOLS,
|
|
347
|
+
},
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (method === 'resources/list') {
|
|
352
|
+
return {
|
|
353
|
+
jsonrpc: '2.0',
|
|
354
|
+
id: request.id,
|
|
355
|
+
result: {
|
|
356
|
+
resources: [],
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (method === 'prompts/list') {
|
|
362
|
+
return {
|
|
363
|
+
jsonrpc: '2.0',
|
|
364
|
+
id: request.id,
|
|
365
|
+
result: {
|
|
366
|
+
prompts: [],
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (method === 'tools/call') {
|
|
372
|
+
const toolName = String(request.params?.name ?? '');
|
|
373
|
+
const toolArgs =
|
|
374
|
+
(request.params?.arguments as Record<string, unknown> | undefined) ?? undefined;
|
|
375
|
+
const toolResult = await executeMcpTool(workspaceRoot, toolName, toolArgs);
|
|
376
|
+
return {
|
|
377
|
+
jsonrpc: '2.0',
|
|
378
|
+
id: request.id,
|
|
379
|
+
result: toolResult,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return {
|
|
384
|
+
jsonrpc: '2.0',
|
|
385
|
+
id: request.id,
|
|
386
|
+
error: {
|
|
387
|
+
code: -32601,
|
|
388
|
+
message: `Method not found: ${method}`,
|
|
389
|
+
},
|
|
390
|
+
};
|
|
391
|
+
} catch (err) {
|
|
392
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
393
|
+
return {
|
|
394
|
+
jsonrpc: '2.0',
|
|
395
|
+
id: request.id,
|
|
396
|
+
error: {
|
|
397
|
+
code: -32603,
|
|
398
|
+
message,
|
|
399
|
+
},
|
|
400
|
+
};
|
|
401
|
+
}
|
|
191
402
|
}
|
|
192
403
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
404
|
+
export function startMcpServer(customWorkspaceRoot?: string): void {
|
|
405
|
+
const rl = readline.createInterface({
|
|
406
|
+
input: process.stdin,
|
|
407
|
+
terminal: false,
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
rl.on('line', async (line) => {
|
|
411
|
+
const trimmed = line.trim();
|
|
412
|
+
if (!trimmed) return;
|
|
413
|
+
|
|
414
|
+
let parsed: JsonRpcRequest;
|
|
415
|
+
try {
|
|
416
|
+
parsed = JSON.parse(trimmed) as JsonRpcRequest;
|
|
417
|
+
} catch {
|
|
418
|
+
const parseErr: JsonRpcResponse = {
|
|
419
|
+
jsonrpc: '2.0',
|
|
420
|
+
id: null,
|
|
421
|
+
error: { code: -32700, message: 'Parse error' },
|
|
422
|
+
};
|
|
423
|
+
process.stdout.write(`${JSON.stringify(parseErr)}\n`);
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const response = await handleMcpJsonRpcRequest(parsed, customWorkspaceRoot);
|
|
428
|
+
if (response) {
|
|
429
|
+
process.stdout.write(`${JSON.stringify(response)}\n`);
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const isDirectExecution =
|
|
435
|
+
Boolean(process.argv[1]) &&
|
|
436
|
+
(() => {
|
|
437
|
+
try {
|
|
438
|
+
return import.meta.url === pathToFileURL(fs.realpathSync(process.argv[1])).href;
|
|
439
|
+
} catch {
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
})();
|
|
443
|
+
|
|
444
|
+
if (isDirectExecution) {
|
|
445
|
+
startMcpServer();
|
|
446
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zelune",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -59,14 +59,12 @@
|
|
|
59
59
|
"@babel/parser": "^7.25.0",
|
|
60
60
|
"@babel/traverse": "^7.25.0",
|
|
61
61
|
"@babel/types": "^7.25.0",
|
|
62
|
-
"@
|
|
63
|
-
"@vitejs/plugin-react": "^4.3.2",
|
|
62
|
+
"@vitejs/plugin-react": "^4.7.0",
|
|
64
63
|
"lucide-react": "^0.454.0",
|
|
65
64
|
"react": "^18.3.1",
|
|
66
65
|
"react-dom": "^18.3.1",
|
|
67
66
|
"react-native-web": "^0.19.12",
|
|
68
|
-
"
|
|
69
|
-
"vite": "^5.4.8"
|
|
67
|
+
"vite": "6.4.4"
|
|
70
68
|
},
|
|
71
69
|
"devDependencies": {
|
|
72
70
|
"@types/babel__generator": "^7.6.8",
|
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.
|
|
4
|
+
"version": "1.6.2",
|
|
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": [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { parse } from '@babel/parser';
|
|
2
|
+
import * as LucideIcons from 'lucide-react';
|
|
2
3
|
import fs from 'node:fs';
|
|
3
|
-
import { createRequire } from 'node:module';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import type { Plugin } from 'vite';
|
|
6
6
|
|
|
@@ -279,16 +279,7 @@ export function viteReactNativeFallbackPlugin(
|
|
|
279
279
|
const zeluneDemoScreensDir = path.resolve(zelunePackageRoot, 'src/screens');
|
|
280
280
|
|
|
281
281
|
const knownShimExports = extractModuleNamedExports(mockNavShimPath);
|
|
282
|
-
const
|
|
283
|
-
const lucideExportKeys = new Set<string>();
|
|
284
|
-
try {
|
|
285
|
-
const lucideMod = reqFromPlugin('lucide-react') as Record<string, unknown>;
|
|
286
|
-
for (const k of Object.keys(lucideMod)) {
|
|
287
|
-
lucideExportKeys.add(k);
|
|
288
|
-
}
|
|
289
|
-
} catch {
|
|
290
|
-
// Ignore if lucide-react cannot be eagerly inspected
|
|
291
|
-
}
|
|
282
|
+
const lucideExportKeys = new Set<string>(Object.keys(LucideIcons));
|
|
292
283
|
|
|
293
284
|
const uniqueRoots = Array.from(
|
|
294
285
|
new Set([path.resolve(targetProjectRoot), path.resolve(workspaceRoot)])
|