next-ai-editor 0.2.2 → 0.2.4

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.
Files changed (32) hide show
  1. package/dist/{AIEditorProvider-IjMydA1Y.cjs → AIEditorProvider-CLgf1Vwa.cjs} +185 -153
  2. package/dist/{AIEditorProvider-IjMydA1Y.cjs.map → AIEditorProvider-CLgf1Vwa.cjs.map} +1 -1
  3. package/dist/{AIEditorProvider-633ZBZul.js → AIEditorProvider-DWId5Qmv.js} +185 -153
  4. package/dist/{AIEditorProvider-633ZBZul.js.map → AIEditorProvider-DWId5Qmv.js.map} +1 -1
  5. package/dist/client/AIEditorProvider.d.ts +9 -1
  6. package/dist/client/AIEditorProvider.d.ts.map +1 -1
  7. package/dist/client.cjs +1 -1
  8. package/dist/client.js +1 -1
  9. package/dist/{comments-Daur80r4.cjs → comments-2HX-AAwu.cjs} +545 -1051
  10. package/dist/comments-2HX-AAwu.cjs.map +1 -0
  11. package/dist/{comments-D3m0RsOO.js → comments-BYFEhf6K.js} +563 -1069
  12. package/dist/comments-BYFEhf6K.js.map +1 -0
  13. package/dist/index.cjs +2 -4
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.js +20 -22
  16. package/dist/next-ai-editor.css +1 -2
  17. package/dist/server/handlers/config.d.ts +7 -0
  18. package/dist/server/handlers/config.d.ts.map +1 -0
  19. package/dist/server/handlers/index.d.ts +0 -2
  20. package/dist/server/handlers/index.d.ts.map +1 -1
  21. package/dist/server/index.d.ts +0 -2
  22. package/dist/server/index.d.ts.map +1 -1
  23. package/dist/server.cjs +1 -3
  24. package/dist/server.cjs.map +1 -1
  25. package/dist/server.js +19 -21
  26. package/package.json +2 -5
  27. package/dist/comments-D3m0RsOO.js.map +0 -1
  28. package/dist/comments-Daur80r4.cjs.map +0 -1
  29. package/dist/server/handlers/edit.d.ts +0 -3
  30. package/dist/server/handlers/edit.d.ts.map +0 -1
  31. package/dist/server/handlers/suggestions.d.ts +0 -3
  32. package/dist/server/handlers/suggestions.d.ts.map +0 -1
@@ -1,13 +1,11 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import fs, { readFile, writeFile, mkdir } from "fs/promises";
3
- import { ChatAnthropic } from "@langchain/anthropic";
4
- import { SystemMessage, HumanMessage } from "@langchain/core/messages";
5
3
  import path, { join } from "path";
4
+ import { SourceMapConsumer } from "@jridgewell/source-map";
5
+ import { c as cleanPath, s as shouldSkipPath, n as normalizeSourcePath } from "./path-utils-Bai2xKx9.js";
6
6
  import * as parser from "@babel/parser";
7
7
  import traverse from "@babel/traverse";
8
8
  import * as t from "@babel/types";
9
- import { SourceMapConsumer } from "@jridgewell/source-map";
10
- import { c as cleanPath, s as shouldSkipPath, n as normalizeSourcePath } from "./path-utils-Bai2xKx9.js";
11
9
  import crypto from "crypto";
12
10
  import { unstable_v2_resumeSession, unstable_v2_createSession } from "@anthropic-ai/claude-agent-sdk";
13
11
  import { existsSync } from "fs";
@@ -50,913 +48,609 @@ async function fileExists$1(filePath) {
50
48
  return false;
51
49
  }
52
50
  }
53
- function parseFile(content) {
54
- try {
55
- return parser.parse(content, {
56
- sourceType: "module",
57
- plugins: [
58
- "jsx",
59
- "typescript",
60
- "decorators-legacy",
61
- "classProperties",
62
- "optionalChaining",
63
- "nullishCoalescingOperator"
64
- ]
65
- });
66
- } catch (e) {
67
- console.error("Parse error:", e);
68
- return null;
69
- }
70
- }
71
- function extractComponentName(ast) {
72
- let componentName = null;
73
- traverse(ast, {
74
- ExportDefaultDeclaration(path2) {
75
- var _a;
76
- if (t.isFunctionDeclaration(path2.node.declaration)) {
77
- componentName = ((_a = path2.node.declaration.id) == null ? void 0 : _a.name) || null;
78
- } else if (t.isArrowFunctionExpression(path2.node.declaration)) {
79
- componentName = "default";
80
- } else if (t.isIdentifier(path2.node.declaration)) {
81
- componentName = path2.node.declaration.name;
51
+ function parseDebugStack(stack) {
52
+ if (!stack) return null;
53
+ const stackStr = typeof stack === "string" ? stack : stack.stack || String(stack);
54
+ const frames = stackStr.split("\n");
55
+ const skipPatterns = [
56
+ "node_modules",
57
+ "SegmentViewNode",
58
+ "LayoutRouter",
59
+ "ErrorBoundary",
60
+ "fakeJSXCallSite"
61
+ ];
62
+ for (const frame of frames) {
63
+ if (skipPatterns.some((p) => frame.includes(p))) continue;
64
+ const match = frame.match(/at\s+(\w+)\s+\((.+?):(\d+):(\d+)\)?$/);
65
+ if (match) {
66
+ let filePath = match[2];
67
+ const line = parseInt(match[3], 10);
68
+ const column = parseInt(match[4], 10);
69
+ let chunkId;
70
+ const chunkMatch = filePath.match(/\?([^:]+)$/);
71
+ if (chunkMatch) {
72
+ chunkId = chunkMatch[1];
73
+ filePath = filePath.replace(/\?[^:]*$/, "");
82
74
  }
83
- },
84
- ExportNamedDeclaration(path2) {
85
- var _a;
86
- if (t.isFunctionDeclaration(path2.node.declaration)) {
87
- componentName = ((_a = path2.node.declaration.id) == null ? void 0 : _a.name) || null;
88
- } else if (t.isVariableDeclaration(path2.node.declaration)) {
89
- const declarator = path2.node.declaration.declarations[0];
90
- if (t.isIdentifier(declarator.id)) {
91
- componentName = declarator.id.name;
92
- }
93
- } else if (path2.node.specifiers && path2.node.specifiers.length > 0) {
94
- const specifier = path2.node.specifiers[0];
95
- if (t.isExportSpecifier(specifier) && t.isIdentifier(specifier.exported)) {
96
- componentName = specifier.exported.name;
97
- }
75
+ filePath = cleanPath(filePath);
76
+ if (!shouldSkipPath(filePath)) {
77
+ console.log("parseDebugStack extracted:", { filePath, line, column });
78
+ return { filePath, line, column, chunkId };
98
79
  }
99
80
  }
100
- });
101
- return componentName;
81
+ }
82
+ console.log(
83
+ "parseDebugStack: no valid frame found in stack:",
84
+ stackStr.substring(0, 200)
85
+ );
86
+ return null;
102
87
  }
103
- function validateGeneratedCode(newCode, originalCode, fileContent) {
104
- try {
105
- const isFullComponent = /^(export\s+)?(default\s+)?function\s+\w+/.test(newCode.trim()) || /^(export\s+)?(default\s+)?const\s+\w+\s*=/.test(newCode.trim());
106
- if (isFullComponent) {
107
- let codeToValidate = newCode;
108
- if (fileContent) {
109
- const interfaceMatches = fileContent.match(
110
- /^(interface|type)\s+\w+[^}]*\}/gm
111
- );
112
- if (interfaceMatches) {
113
- codeToValidate = interfaceMatches.join("\n\n") + "\n\n" + newCode;
114
- }
88
+ function extractComponentNameFromStack(stack) {
89
+ if (!stack) return null;
90
+ const stackStr = typeof stack === "string" ? stack : stack.stack || String(stack);
91
+ const frames = stackStr.split("\n");
92
+ const skipPatterns = [
93
+ "node_modules",
94
+ "SegmentViewNode",
95
+ "LayoutRouter",
96
+ "ErrorBoundary",
97
+ "fakeJSXCallSite",
98
+ "react_stack_bottom_frame"
99
+ ];
100
+ for (const frame of frames) {
101
+ if (skipPatterns.some((p) => frame.includes(p))) continue;
102
+ const match = frame.match(/at\s+(\w+)\s+\(/);
103
+ if (match && match[1]) {
104
+ const componentName = match[1];
105
+ if (componentName !== "Object" && componentName !== "anonymous") {
106
+ return componentName;
115
107
  }
116
- parser.parse(codeToValidate, {
117
- sourceType: "module",
118
- plugins: ["jsx", "typescript"]
119
- });
120
- } else {
121
- const wrapped = `function _() { return (${newCode}); }`;
122
- parser.parse(wrapped, {
123
- sourceType: "module",
124
- plugins: ["jsx", "typescript"]
125
- });
126
108
  }
127
- } catch (e) {
128
- console.error("Generated code parse error:", e);
129
- return false;
130
- }
131
- const origBraces = (originalCode.match(/[{}]/g) || []).length;
132
- const newBraces = (newCode.match(/[{}]/g) || []).length;
133
- const origTags = (originalCode.match(/[<>]/g) || []).length;
134
- const newTags = (newCode.match(/[<>]/g) || []).length;
135
- if (Math.abs(origBraces - newBraces) > 4 || Math.abs(origTags - newTags) > 4) {
136
- console.warn(
137
- `Structure changed significantly: braces ${origBraces}->${newBraces}, tags ${origTags}->${newTags}`
138
- );
139
109
  }
140
- return true;
110
+ return null;
141
111
  }
142
- function findTargetElement(ast, fileContent, options) {
143
- const { componentName, lineNumber, elementContext } = options;
144
- let componentNode = null;
145
- let componentStart = 0;
146
- let componentEnd = Infinity;
147
- let fallbackExportDefault = null;
148
- traverse(ast, {
149
- FunctionDeclaration(path2) {
150
- var _a, _b, _c;
151
- if (((_a = path2.node.id) == null ? void 0 : _a.name) === componentName) {
152
- componentNode = path2.node;
153
- componentStart = ((_b = path2.node.loc) == null ? void 0 : _b.start.line) || 0;
154
- componentEnd = ((_c = path2.node.loc) == null ? void 0 : _c.end.line) || Infinity;
112
+ function parseDebugStackFrames(stack) {
113
+ if (!stack) return [];
114
+ const stackStr = typeof stack === "string" ? stack : stack.stack || String(stack);
115
+ const frames = stackStr.split("\n");
116
+ const skipPatterns = [
117
+ "node_modules",
118
+ "SegmentViewNode",
119
+ "LayoutRouter",
120
+ "ErrorBoundary",
121
+ "fakeJSXCallSite",
122
+ "react_stack_bottom_frame"
123
+ ];
124
+ const positions = [];
125
+ for (const frame of frames) {
126
+ if (skipPatterns.some((p) => frame.includes(p))) continue;
127
+ const match = frame.match(/at\s+(\w+)\s+\((.+?):(\d+):(\d+)\)?$/);
128
+ if (match) {
129
+ match[1];
130
+ let filePath = match[2];
131
+ const line = parseInt(match[3], 10);
132
+ const column = parseInt(match[4], 10);
133
+ let chunkId;
134
+ const chunkMatch = filePath.match(/\?([^:]+)$/);
135
+ if (chunkMatch) {
136
+ chunkId = chunkMatch[1];
137
+ filePath = filePath.replace(/\?[^:]*$/, "");
155
138
  }
156
- },
157
- VariableDeclarator(path2) {
158
- var _a, _b;
159
- if (t.isIdentifier(path2.node.id) && path2.node.id.name === componentName) {
160
- componentNode = path2.node;
161
- const parent = path2.parentPath.parent;
162
- componentStart = ((_a = parent == null ? void 0 : parent.loc) == null ? void 0 : _a.start.line) || 0;
163
- componentEnd = ((_b = parent == null ? void 0 : parent.loc) == null ? void 0 : _b.end.line) || Infinity;
139
+ filePath = cleanPath(filePath);
140
+ if (!shouldSkipPath(filePath)) {
141
+ positions.push({ filePath, line, column, chunkId });
142
+ if (positions.length >= 2) break;
164
143
  }
165
- },
166
- ExportDefaultDeclaration(path2) {
167
- var _a, _b, _c, _d, _e;
168
- if (t.isFunctionDeclaration(path2.node.declaration)) {
169
- const funcName = (_a = path2.node.declaration.id) == null ? void 0 : _a.name;
170
- if (funcName === componentName || !funcName) {
171
- componentNode = path2.node;
172
- componentStart = ((_b = path2.node.loc) == null ? void 0 : _b.start.line) || 0;
173
- componentEnd = ((_c = path2.node.loc) == null ? void 0 : _c.end.line) || Infinity;
174
- } else if (!componentNode) {
175
- fallbackExportDefault = {
176
- node: path2.node,
177
- start: ((_d = path2.node.loc) == null ? void 0 : _d.start.line) || 0,
178
- end: ((_e = path2.node.loc) == null ? void 0 : _e.end.line) || Infinity
179
- };
144
+ }
145
+ }
146
+ console.log(`parseDebugStackFrames extracted ${positions.length} frames:`, positions);
147
+ return positions;
148
+ }
149
+ async function resolveOriginalPosition(compiledPos, projectRoot) {
150
+ try {
151
+ console.log("resolveOriginalPosition called with:", compiledPos);
152
+ let compiledFilePath = compiledPos.filePath;
153
+ compiledFilePath = cleanPath(compiledFilePath);
154
+ console.log("After cleanPath:", compiledFilePath);
155
+ if (compiledFilePath.startsWith("http://") || compiledFilePath.startsWith("https://")) {
156
+ const url = new URL(compiledFilePath);
157
+ const pathname = url.pathname;
158
+ if (pathname.startsWith("/_next/")) {
159
+ const relativePath = pathname.substring("/_next/".length);
160
+ compiledFilePath = path.join(projectRoot, ".next", "dev", relativePath);
161
+ } else {
162
+ console.warn("Unexpected HTTP URL path:", pathname);
163
+ return null;
164
+ }
165
+ } else if (!path.isAbsolute(compiledFilePath)) {
166
+ if (compiledFilePath.startsWith(".next/")) {
167
+ compiledFilePath = path.resolve(projectRoot, compiledFilePath);
168
+ } else {
169
+ const resolved = path.resolve(projectRoot, compiledFilePath);
170
+ if (await fileExists(resolved)) {
171
+ compiledFilePath = resolved;
172
+ } else {
173
+ const possiblePaths = [
174
+ path.join(projectRoot, ".next", "dev", compiledFilePath),
175
+ path.join(projectRoot, compiledFilePath)
176
+ ];
177
+ for (const tryPath of possiblePaths) {
178
+ if (await fileExists(tryPath)) {
179
+ compiledFilePath = tryPath;
180
+ break;
181
+ }
182
+ }
180
183
  }
181
184
  }
185
+ } else {
186
+ compiledFilePath = path.normalize(compiledFilePath);
182
187
  }
183
- });
184
- if (!componentNode && fallbackExportDefault !== null) {
185
- const fallback = fallbackExportDefault;
188
+ console.log("Normalized compiled file path:", compiledFilePath);
189
+ const compiledExists = await fileExists(compiledFilePath);
186
190
  console.log(
187
- `⚠️ Component "${componentName}" not found, using export default function as fallback`
191
+ "Compiled file exists:",
192
+ compiledExists,
193
+ "at:",
194
+ compiledFilePath
188
195
  );
189
- componentNode = fallback.node;
190
- componentStart = fallback.start;
191
- componentEnd = fallback.end;
192
- }
193
- const allElementsByTag = /* @__PURE__ */ new Map();
194
- const elementsAtLine = [];
195
- traverse(ast, {
196
- JSXElement(path2) {
197
- const loc = path2.node.loc;
198
- if (!loc) return;
199
- const startLine = loc.start.line;
200
- const endLine = loc.end.line;
201
- if (startLine < componentStart || endLine > componentEnd) return;
202
- const opening = path2.node.openingElement;
203
- if (t.isJSXIdentifier(opening.name)) {
204
- const tagName = opening.name.name;
205
- if (!allElementsByTag.has(tagName)) {
206
- allElementsByTag.set(tagName, []);
196
+ if (!compiledExists) {
197
+ console.error(
198
+ `Compiled file not found: ${compiledFilePath} (from parsed: ${compiledPos.filePath})`
199
+ );
200
+ }
201
+ const sourceMapPath = compiledFilePath + ".map";
202
+ console.log("Looking for source map at:", sourceMapPath);
203
+ const sourceMapExists = await fileExists(sourceMapPath);
204
+ console.log("Source map exists:", sourceMapExists);
205
+ if (!sourceMapExists) {
206
+ console.error(
207
+ `Source map not found: ${sourceMapPath} (from compiled: ${compiledPos.filePath}, normalized: ${compiledFilePath})`
208
+ );
209
+ const altPaths = [
210
+ compiledFilePath.replace(/\.js$/, ".map"),
211
+ path.join(
212
+ path.dirname(compiledFilePath),
213
+ path.basename(compiledFilePath) + ".map"
214
+ )
215
+ ];
216
+ console.log("Trying alternative paths:", altPaths);
217
+ for (const altPath of altPaths) {
218
+ if (await fileExists(altPath)) {
219
+ console.log("Found source map at alternative path:", altPath);
220
+ return await resolveFromSourceMap(altPath, compiledPos, projectRoot);
207
221
  }
208
- allElementsByTag.get(tagName).push({ node: path2.node, startLine, endLine, score: 0 });
209
- }
210
- if (startLine === lineNumber) {
211
- elementsAtLine.push({ node: path2.node, startLine, endLine, score: 0 });
212
222
  }
223
+ return null;
213
224
  }
214
- });
215
- if (elementsAtLine.length > 0) {
216
- if (elementsAtLine.length === 1) {
217
- const target = elementsAtLine[0];
218
- return {
219
- startLine: target.startLine,
220
- endLine: target.endLine,
221
- componentStart,
222
- componentEnd
223
- };
225
+ return await resolveFromSourceMap(sourceMapPath, compiledPos, projectRoot);
226
+ } catch (error) {
227
+ console.error("Error resolving source map:", error);
228
+ return null;
229
+ }
230
+ }
231
+ async function resolveFromSourceMap(sourceMapPath, compiledPos, projectRoot) {
232
+ try {
233
+ const sourceMapContent = await fs.readFile(sourceMapPath, "utf-8");
234
+ const sourceMap = JSON.parse(sourceMapContent);
235
+ if ((!sourceMap.sources || sourceMap.sources.length === 0) && (!sourceMap.sections || sourceMap.sections.length === 0)) {
236
+ console.warn(
237
+ "Empty source map detected, cannot resolve position:",
238
+ sourceMapPath
239
+ );
240
+ return null;
224
241
  }
225
- if (elementContext) {
226
- for (const elem of elementsAtLine) {
227
- if (t.isJSXElement(elem.node)) {
228
- const score = scoreElementMatch(elem.node, elementContext, fileContent);
229
- elem.score = score;
242
+ if (sourceMap.sections) {
243
+ let matchingSection = null;
244
+ for (let i = sourceMap.sections.length - 1; i >= 0; i--) {
245
+ const section = sourceMap.sections[i];
246
+ const offset = section.offset;
247
+ const sectionStartLine1Indexed = offset.line + 1;
248
+ if (compiledPos.line > sectionStartLine1Indexed || compiledPos.line === sectionStartLine1Indexed && compiledPos.column >= offset.column) {
249
+ matchingSection = section;
250
+ break;
230
251
  }
231
252
  }
232
- elementsAtLine.sort((a, b) => b.score - a.score);
233
- if (elementsAtLine[0].score > 0) {
234
- return {
235
- startLine: elementsAtLine[0].startLine,
236
- endLine: elementsAtLine[0].endLine,
237
- componentStart,
238
- componentEnd
239
- };
240
- }
241
- }
242
- return {
243
- startLine: elementsAtLine[0].startLine,
244
- endLine: elementsAtLine[0].endLine,
245
- componentStart,
246
- componentEnd
247
- };
248
- }
249
- if (elementContext == null ? void 0 : elementContext.tagName) {
250
- const allOfTag = allElementsByTag.get(elementContext.tagName);
251
- if (allOfTag && allOfTag.length > 0) {
252
- if (elementContext.textContent || elementContext.className) {
253
- for (const elem of allOfTag) {
254
- if (t.isJSXElement(elem.node)) {
255
- elem.score = scoreElementMatch(elem.node, elementContext, fileContent);
256
- }
253
+ if (matchingSection && matchingSection.map) {
254
+ const sectionMap = matchingSection.map;
255
+ const offset = matchingSection.offset;
256
+ let consumer2;
257
+ try {
258
+ consumer2 = await new SourceMapConsumer(sectionMap);
259
+ } catch (error) {
260
+ console.error("Error creating SourceMapConsumer:", error);
261
+ return null;
257
262
  }
258
- allOfTag.sort((a, b) => b.score - a.score);
259
- if (allOfTag[0].score > 50) {
260
- return {
261
- startLine: allOfTag[0].startLine,
262
- endLine: allOfTag[0].endLine,
263
- componentStart,
264
- componentEnd
265
- };
263
+ try {
264
+ const adjustedLine = compiledPos.line - offset.line;
265
+ const adjustedColumn = compiledPos.line === offset.line + 1 ? compiledPos.column - offset.column : compiledPos.column;
266
+ const originalPos = consumer2.originalPositionFor({
267
+ line: adjustedLine,
268
+ column: adjustedColumn
269
+ });
270
+ if (originalPos.source && originalPos.line !== null) {
271
+ const source = normalizeSourcePath(
272
+ originalPos.source || "",
273
+ projectRoot
274
+ );
275
+ return {
276
+ source,
277
+ line: originalPos.line,
278
+ column: originalPos.column ?? 0
279
+ };
280
+ }
281
+ } finally {
282
+ consumer2.destroy();
266
283
  }
267
284
  }
268
- if (elementContext.nthOfType && allOfTag.length >= elementContext.nthOfType) {
269
- const target = allOfTag[elementContext.nthOfType - 1];
285
+ return null;
286
+ }
287
+ const consumer = await new SourceMapConsumer(sourceMap);
288
+ try {
289
+ const originalPos = consumer.originalPositionFor({
290
+ line: compiledPos.line,
291
+ column: compiledPos.column
292
+ });
293
+ if (originalPos.source && originalPos.line !== null) {
294
+ const source = normalizeSourcePath(
295
+ originalPos.source || "",
296
+ projectRoot
297
+ );
270
298
  return {
271
- startLine: target.startLine,
272
- endLine: target.endLine,
273
- componentStart,
274
- componentEnd
299
+ source,
300
+ line: originalPos.line,
301
+ column: originalPos.column ?? 0
275
302
  };
276
303
  }
304
+ } finally {
305
+ consumer.destroy();
277
306
  }
307
+ return null;
308
+ } catch (error) {
309
+ console.error("Error resolving source map:", error);
310
+ return null;
278
311
  }
279
- const nearbyElements = [];
312
+ }
313
+ async function fileExists(filePath) {
314
+ try {
315
+ await fs.access(filePath);
316
+ return true;
317
+ } catch {
318
+ return false;
319
+ }
320
+ }
321
+ async function getOriginalPositionFromDebugStack(debugStack, projectRoot) {
322
+ const compiledPos = parseDebugStack(debugStack);
323
+ if (!compiledPos) return null;
324
+ return await resolveOriginalPosition(compiledPos, projectRoot);
325
+ }
326
+ function parseFile(content) {
327
+ try {
328
+ return parser.parse(content, {
329
+ sourceType: "module",
330
+ plugins: [
331
+ "jsx",
332
+ "typescript",
333
+ "decorators-legacy",
334
+ "classProperties",
335
+ "optionalChaining",
336
+ "nullishCoalescingOperator"
337
+ ]
338
+ });
339
+ } catch (e) {
340
+ console.error("Parse error:", e);
341
+ return null;
342
+ }
343
+ }
344
+ function extractComponentName(ast) {
345
+ let componentName = null;
280
346
  traverse(ast, {
281
- JSXElement(path2) {
282
- const loc = path2.node.loc;
283
- if (!loc) return;
284
- const startLine = loc.start.line;
285
- const endLine = loc.end.line;
286
- if (startLine < componentStart || endLine > componentEnd) return;
287
- if (Math.abs(startLine - lineNumber) <= 5) {
288
- const score = elementContext ? scoreElementMatch(path2.node, elementContext, fileContent) : 100 - Math.abs(startLine - lineNumber);
289
- nearbyElements.push({ node: path2.node, startLine, endLine, score });
347
+ ExportDefaultDeclaration(path2) {
348
+ var _a;
349
+ if (t.isFunctionDeclaration(path2.node.declaration)) {
350
+ componentName = ((_a = path2.node.declaration.id) == null ? void 0 : _a.name) || null;
351
+ } else if (t.isArrowFunctionExpression(path2.node.declaration)) {
352
+ componentName = "default";
353
+ } else if (t.isIdentifier(path2.node.declaration)) {
354
+ componentName = path2.node.declaration.name;
355
+ }
356
+ },
357
+ ExportNamedDeclaration(path2) {
358
+ var _a;
359
+ if (t.isFunctionDeclaration(path2.node.declaration)) {
360
+ componentName = ((_a = path2.node.declaration.id) == null ? void 0 : _a.name) || null;
361
+ } else if (t.isVariableDeclaration(path2.node.declaration)) {
362
+ const declarator = path2.node.declaration.declarations[0];
363
+ if (t.isIdentifier(declarator.id)) {
364
+ componentName = declarator.id.name;
365
+ }
366
+ } else if (path2.node.specifiers && path2.node.specifiers.length > 0) {
367
+ const specifier = path2.node.specifiers[0];
368
+ if (t.isExportSpecifier(specifier) && t.isIdentifier(specifier.exported)) {
369
+ componentName = specifier.exported.name;
370
+ }
290
371
  }
291
372
  }
292
373
  });
293
- if (nearbyElements.length > 0) {
294
- nearbyElements.sort((a, b) => b.score - a.score);
295
- return {
296
- startLine: nearbyElements[0].startLine,
297
- endLine: nearbyElements[0].endLine,
298
- componentStart,
299
- componentEnd
300
- };
301
- }
302
- if (componentNode && componentStart > 0) {
303
- return {
304
- startLine: componentStart,
305
- endLine: componentEnd,
306
- componentStart,
307
- componentEnd
308
- };
309
- }
310
- return null;
374
+ return componentName;
311
375
  }
312
- function scoreElementMatch(node, context, fileContent) {
313
- let score = 0;
314
- const opening = node.openingElement;
315
- if (t.isJSXIdentifier(opening.name)) {
316
- if (opening.name.name === context.tagName) {
317
- score += 50;
318
- } else {
319
- return 0;
320
- }
321
- } else if (t.isJSXMemberExpression(opening.name)) {
322
- const fullName = getJSXMemberName(opening.name);
323
- if (fullName === context.tagName || fullName.endsWith(`.${context.tagName}`)) {
324
- score += 50;
325
- } else {
326
- return 0;
327
- }
328
- }
329
- if (context.className) {
330
- const classAttr = opening.attributes.find(
331
- (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === "className"
332
- );
333
- if (classAttr && t.isJSXAttribute(classAttr)) {
334
- const classValue = getAttributeValue(classAttr);
335
- if (classValue && context.className.split(/\s+/).some((c) => classValue.includes(c))) {
336
- score += 20;
337
- }
338
- }
339
- }
340
- if (context.textContent && node.loc) {
341
- const elementCode = fileContent.split("\n").slice(node.loc.start.line - 1, node.loc.end.line).join("\n");
342
- const normalizedContent = context.textContent.toLowerCase().trim();
343
- const normalizedElement = elementCode.toLowerCase();
344
- if (normalizedElement.includes(normalizedContent)) {
345
- score += 30;
346
- }
347
- }
348
- if (context.props) {
349
- for (const [key, value] of Object.entries(context.props)) {
350
- if (key.startsWith("_")) continue;
351
- const attr = opening.attributes.find(
352
- (a) => t.isJSXAttribute(a) && t.isJSXIdentifier(a.name) && a.name.name === key
353
- );
354
- if (attr) {
355
- score += 5;
356
- if (typeof value === "string" && t.isJSXAttribute(attr)) {
357
- const attrValue = getAttributeValue(attr);
358
- if (attrValue === value) score += 10;
376
+ function validateGeneratedCode(newCode, originalCode, fileContent) {
377
+ try {
378
+ const isFullComponent = /^(export\s+)?(default\s+)?function\s+\w+/.test(newCode.trim()) || /^(export\s+)?(default\s+)?const\s+\w+\s*=/.test(newCode.trim());
379
+ if (isFullComponent) {
380
+ let codeToValidate = newCode;
381
+ if (fileContent) {
382
+ const interfaceMatches = fileContent.match(
383
+ /^(interface|type)\s+\w+[^}]*\}/gm
384
+ );
385
+ if (interfaceMatches) {
386
+ codeToValidate = interfaceMatches.join("\n\n") + "\n\n" + newCode;
359
387
  }
360
388
  }
389
+ parser.parse(codeToValidate, {
390
+ sourceType: "module",
391
+ plugins: ["jsx", "typescript"]
392
+ });
393
+ } else {
394
+ const wrapped = `function _() { return (${newCode}); }`;
395
+ parser.parse(wrapped, {
396
+ sourceType: "module",
397
+ plugins: ["jsx", "typescript"]
398
+ });
361
399
  }
400
+ } catch (e) {
401
+ console.error("Generated code parse error:", e);
402
+ return false;
362
403
  }
363
- return score;
364
- }
365
- function getJSXMemberName(node) {
366
- if (t.isJSXIdentifier(node.object)) {
367
- return `${node.object.name}.${node.property.name}`;
368
- }
369
- if (t.isJSXMemberExpression(node.object)) {
370
- return `${getJSXMemberName(node.object)}.${node.property.name}`;
371
- }
372
- return node.property.name;
373
- }
374
- function getAttributeValue(attr) {
375
- if (!attr.value) return null;
376
- if (t.isStringLiteral(attr.value)) return attr.value.value;
377
- if (t.isJSXExpressionContainer(attr.value) && t.isStringLiteral(attr.value.expression)) {
378
- return attr.value.expression.value;
404
+ const origBraces = (originalCode.match(/[{}]/g) || []).length;
405
+ const newBraces = (newCode.match(/[{}]/g) || []).length;
406
+ const origTags = (originalCode.match(/[<>]/g) || []).length;
407
+ const newTags = (newCode.match(/[<>]/g) || []).length;
408
+ if (Math.abs(origBraces - newBraces) > 4 || Math.abs(origTags - newTags) > 4) {
409
+ console.warn(
410
+ `Structure changed significantly: braces ${origBraces}->${newBraces}, tags ${origTags}->${newTags}`
411
+ );
379
412
  }
380
- return null;
413
+ return true;
381
414
  }
382
- async function handleEdit(req) {
383
- var _a;
384
- const devModeError = validateDevMode();
385
- if (devModeError) return devModeError;
386
- try {
387
- const body = await req.json();
388
- const {
389
- filePath,
390
- lineNumber,
391
- componentName,
392
- suggestion,
393
- elementContext,
394
- editHistory,
395
- parentInstance
396
- } = body;
397
- const projectRoot = process.cwd();
398
- const normalizedPath = normalizePath(filePath);
399
- const absolutePath = await resolveFilePath(projectRoot, normalizedPath);
400
- if (!absolutePath) {
401
- return NextResponse.json(
402
- { success: false, error: `File not found: ${normalizedPath}` },
403
- { status: 404 }
404
- );
405
- }
406
- const fileContent = await fs.readFile(absolutePath, "utf-8");
407
- const ast = parseFile(fileContent);
408
- if (!ast) {
409
- return NextResponse.json(
410
- { success: false, error: "Failed to parse file" },
411
- { status: 400 }
412
- );
413
- }
414
- const target = findTargetElement(ast, fileContent, {
415
- componentName,
416
- lineNumber,
417
- elementContext
418
- });
419
- if (!target) {
420
- return NextResponse.json(
421
- { success: false, error: "Could not locate target element" },
422
- { status: 400 }
423
- );
424
- }
425
- const lines = fileContent.split("\n");
426
- const targetCode = lines.slice(target.startLine - 1, target.endLine).join("\n");
427
- const baseIndentation = ((_a = lines[target.startLine - 1].match(/^(\s*)/)) == null ? void 0 : _a[1]) || "";
428
- if (target.componentStart <= 0 || target.componentEnd === Infinity) {
429
- return NextResponse.json({
430
- success: false,
431
- error: `Could not determine component bounds. Component: ${target.componentStart}-${target.componentEnd}`
432
- });
433
- }
434
- const componentLines = lines.slice(
435
- target.componentStart - 1,
436
- target.componentEnd
437
- );
438
- const annotatedLines = componentLines.map((line, idx) => {
439
- const lineNum = target.componentStart + idx;
440
- const isTargetStart = lineNum === target.startLine;
441
- const isTargetEnd = lineNum === target.endLine;
442
- const isWithinTarget = lineNum >= target.startLine && lineNum <= target.endLine;
443
- let annotation = "";
444
- if (isTargetStart && isTargetEnd) {
445
- annotation = " // ← CLICKED ELEMENT (single line)";
446
- } else if (isTargetStart) {
447
- annotation = " // ← CLICKED ELEMENT STARTS";
448
- } else if (isTargetEnd) {
449
- annotation = " // ← CLICKED ELEMENT ENDS";
450
- } else if (isWithinTarget) {
451
- annotation = " // ← (clicked element)";
415
+ function findTargetElement(ast, fileContent, options) {
416
+ const { componentName, lineNumber, elementContext } = options;
417
+ let componentNode = null;
418
+ let componentStart = 0;
419
+ let componentEnd = Infinity;
420
+ let fallbackExportDefault = null;
421
+ traverse(ast, {
422
+ FunctionDeclaration(path2) {
423
+ var _a, _b, _c;
424
+ if (((_a = path2.node.id) == null ? void 0 : _a.name) === componentName) {
425
+ componentNode = path2.node;
426
+ componentStart = ((_b = path2.node.loc) == null ? void 0 : _b.start.line) || 0;
427
+ componentEnd = ((_c = path2.node.loc) == null ? void 0 : _c.end.line) || Infinity;
452
428
  }
453
- return line + annotation;
454
- });
455
- const fullComponentCode = annotatedLines.join("\n");
456
- const newCode = await generateEdit({
457
- targetCode,
458
- fullComponentCode,
459
- suggestion,
460
- elementContext,
461
- baseIndentation,
462
- targetLine: target.startLine,
463
- targetEndLine: target.endLine,
464
- componentStart: target.componentStart,
465
- componentEnd: target.componentEnd,
466
- editHistory: editHistory || [],
467
- parentInstance
468
- });
469
- if (!newCode) {
470
- return NextResponse.json({
471
- success: false,
472
- error: "AI failed to generate valid edit"
473
- });
474
- }
475
- const parentInstanceMatch = newCode.match(/\/\/ EDIT_PARENT_INSTANCE\s*\n([\s\S]+)/);
476
- if (parentInstanceMatch && parentInstance) {
477
- const parentCode = parentInstanceMatch[1].trim();
478
- const parentNormalizedPath = normalizePath(parentInstance.filePath);
479
- const parentAbsolutePath = await resolveFilePath(projectRoot, parentNormalizedPath);
480
- if (!parentAbsolutePath) {
481
- return NextResponse.json({
482
- success: false,
483
- error: `Parent file not found: ${parentNormalizedPath}`
484
- });
429
+ },
430
+ VariableDeclarator(path2) {
431
+ var _a, _b;
432
+ if (t.isIdentifier(path2.node.id) && path2.node.id.name === componentName) {
433
+ componentNode = path2.node;
434
+ const parent = path2.parentPath.parent;
435
+ componentStart = ((_a = parent == null ? void 0 : parent.loc) == null ? void 0 : _a.start.line) || 0;
436
+ componentEnd = ((_b = parent == null ? void 0 : parent.loc) == null ? void 0 : _b.end.line) || Infinity;
437
+ }
438
+ },
439
+ ExportDefaultDeclaration(path2) {
440
+ var _a, _b, _c, _d, _e;
441
+ if (t.isFunctionDeclaration(path2.node.declaration)) {
442
+ const funcName = (_a = path2.node.declaration.id) == null ? void 0 : _a.name;
443
+ if (funcName === componentName || !funcName) {
444
+ componentNode = path2.node;
445
+ componentStart = ((_b = path2.node.loc) == null ? void 0 : _b.start.line) || 0;
446
+ componentEnd = ((_c = path2.node.loc) == null ? void 0 : _c.end.line) || Infinity;
447
+ } else if (!componentNode) {
448
+ fallbackExportDefault = {
449
+ node: path2.node,
450
+ start: ((_d = path2.node.loc) == null ? void 0 : _d.start.line) || 0,
451
+ end: ((_e = path2.node.loc) == null ? void 0 : _e.end.line) || Infinity
452
+ };
453
+ }
485
454
  }
486
- const parentFileContent = await fs.readFile(parentAbsolutePath, "utf-8");
487
- const parentLines = parentFileContent.split("\n");
488
- const newParentLines = [...parentLines];
489
- newParentLines.splice(
490
- parentInstance.lineStart - 1,
491
- parentInstance.lineEnd - parentInstance.lineStart + 1,
492
- ...parentCode.split("\n")
493
- );
494
- await fs.writeFile(parentAbsolutePath, newParentLines.join("\n"), "utf-8");
495
- return NextResponse.json({
496
- success: true,
497
- fileSnapshot: parentFileContent,
498
- generatedCode: parentCode,
499
- modifiedLines: {
500
- start: parentInstance.lineStart,
501
- end: parentInstance.lineEnd
502
- },
503
- editedFile: parentInstance.filePath
504
- // Indicate which file was edited
505
- });
506
- }
507
- const fullComponentMatch = newCode.match(/\/\/ FULL_COMPONENT\s*\n([\s\S]+)/);
508
- let codeToApply = newCode;
509
- let startLineToReplace = target.startLine;
510
- let endLineToReplace = target.endLine;
511
- const isFullComponentDeclaration = /^(export\s+)?(default\s+)?function\s+\w+/.test(newCode.trim()) || /^(export\s+)?const\s+\w+\s*=/.test(newCode.trim());
512
- if (fullComponentMatch) {
513
- codeToApply = fullComponentMatch[1].trim();
514
- startLineToReplace = target.componentStart;
515
- endLineToReplace = target.componentEnd;
516
- } else if (isFullComponentDeclaration && target.startLine !== target.componentStart) {
517
- codeToApply = newCode;
518
- startLineToReplace = target.componentStart;
519
- endLineToReplace = target.componentEnd;
520
- }
521
- if (!validateGeneratedCode(codeToApply, targetCode, fileContent)) {
522
- return NextResponse.json({
523
- success: false,
524
- error: "Generated code is invalid"
525
- });
526
455
  }
527
- const newLines = [...lines];
528
- newLines.splice(
529
- startLineToReplace - 1,
530
- endLineToReplace - startLineToReplace + 1,
531
- ...codeToApply.split("\n")
532
- );
533
- await fs.writeFile(absolutePath, newLines.join("\n"), "utf-8");
534
- return NextResponse.json({
535
- success: true,
536
- fileSnapshot: fileContent,
537
- // Original file content for undo
538
- generatedCode: codeToApply,
539
- // AI-generated code
540
- modifiedLines: {
541
- start: startLineToReplace,
542
- end: endLineToReplace
543
- },
544
- editedFile: filePath
545
- // Indicate which file was edited
546
- });
547
- } catch (error) {
548
- return NextResponse.json(
549
- { success: false, error: String(error) },
550
- { status: 500 }
551
- );
552
- }
553
- }
554
- async function generateEdit(options) {
555
- const {
556
- fullComponentCode,
557
- suggestion,
558
- baseIndentation,
559
- editHistory,
560
- parentInstance
561
- } = options;
562
- const apiKey = process.env.ANTHROPIC_API_KEY;
563
- if (!apiKey) throw new Error("ANTHROPIC_API_KEY not set");
564
- const model = new ChatAnthropic({
565
- apiKey,
566
- modelName: "claude-sonnet-4-5-20250929",
567
- maxTokens: 4096,
568
- temperature: 0
569
456
  });
570
- const systemPrompt = `You are a precise code editor for React/JSX components.
571
-
572
- WHAT YOU'LL SEE:
573
- - Component Definition: Full code of the clicked component with line annotations
574
- * The clicked element is marked with "// ← CLICKED ELEMENT STARTS" and "// ← CLICKED ELEMENT ENDS"
575
- * These are just annotations - NOT part of the actual code
576
- ${parentInstance ? `- Parent Instance: Where this component is used, with "// ← COMPONENT USAGE" marking the usage line` : ""}
577
-
578
- YOUR DECISION - Choose ONE approach based on the user's request:
579
-
580
- 1. EDIT COMPONENT DEFINITION (most common):
581
- - Modify styling, layout, or behavior that should apply to ALL instances
582
- - Example requests: "make the button blue", "add padding", "change font size"
583
- - Output: Just the modified element OR "// FULL_COMPONENT\\n" + complete modified component
584
-
585
- ${parentInstance ? `2. EDIT PARENT INSTANCE (when user wants to modify THIS specific usage):
586
- - Change props, text, or configuration for THIS specific instance only
587
- - Example requests: "change this title to...", "remove this card", "add another button here"
588
- - Output: "// EDIT_PARENT_INSTANCE\\n" + complete modified parent component` : ""}
589
-
590
- RULES:
591
- - Output ONLY code, no explanations
592
- - No markdown fences
593
- - Do NOT include annotation comments in your output
594
- - Preserve indentation
595
- - Make minimal changes`;
596
- let userPrompt = "";
597
- if (editHistory.length > 0) {
598
- userPrompt += "Previous edits made to this element:\n";
599
- editHistory.forEach((item, idx) => {
600
- userPrompt += `${idx + 1}. ${item.suggestion} ${item.success ? "(✓ applied)" : "(✗ failed)"}
601
- `;
602
- });
603
- userPrompt += "\n";
604
- }
605
- userPrompt += `COMPONENT DEFINITION (clicked element is annotated):
606
-
607
- \`\`\`jsx
608
- ${fullComponentCode}
609
- \`\`\`
610
- `;
611
- if (parentInstance) {
612
- const parentLines = parentInstance.content.split("\n");
613
- const annotatedParentLines = parentLines.map((line, idx) => {
614
- const lineNum = parentInstance.lineStart + idx;
615
- const isUsageLine = lineNum >= parentInstance.usageLineStart && lineNum <= parentInstance.usageLineEnd;
616
- return line + (isUsageLine ? " // ← COMPONENT USAGE" : "");
617
- });
618
- userPrompt += `
619
- PARENT INSTANCE (where component is used - in ${parentInstance.filePath}):
620
-
621
- \`\`\`jsx
622
- ${annotatedParentLines.join("\n")}
623
- \`\`\`
624
- `;
625
- }
626
- userPrompt += `
627
- User request: "${suggestion}"
628
- ${editHistory.length > 0 ? "(Build upon previous changes)" : ""}
629
-
630
- ${parentInstance ? `Decide whether to:
631
- 1. Edit the component definition (for changes that affect ALL instances)
632
- 2. Edit the parent instance (for changes specific to THIS usage)
633
-
634
- Then output the appropriate code with the correct marker.` : `Modify the annotated element to fulfill this request. Remember: do NOT include the annotation comments in your output.`}`;
635
- try {
636
- const response = await model.invoke([
637
- new SystemMessage(systemPrompt),
638
- new HumanMessage(userPrompt)
639
- ]);
640
- let code = typeof response.content === "string" ? response.content : String(response.content);
641
- code = cleanGeneratedCode(code, baseIndentation);
642
- return code || null;
643
- } catch (e) {
644
- return null;
645
- }
646
- }
647
- function cleanGeneratedCode(code, baseIndentation) {
648
- var _a, _b, _c;
649
- const isParentEdit = code.trim().startsWith("// EDIT_PARENT_INSTANCE");
650
- if (isParentEdit) {
651
- const marker2 = "// EDIT_PARENT_INSTANCE\n";
652
- code = code.replace(/^\/\/ EDIT_PARENT_INSTANCE\n?/, "");
653
- code = code.replace(/^```[\w]*\n?/gm, "").replace(/\n?```$/gm, "").replace(/\s*\/\/ ← COMPONENT USAGE/g, "").trim();
654
- return marker2 + code;
655
- }
656
- const isFullComponent = code.trim().startsWith("// FULL_COMPONENT");
657
- let marker = "";
658
- if (isFullComponent) {
659
- marker = "// FULL_COMPONENT\n";
660
- code = code.replace(/^\/\/ FULL_COMPONENT\n?/, "");
457
+ if (!componentNode && fallbackExportDefault !== null) {
458
+ const fallback = fallbackExportDefault;
459
+ console.log(
460
+ `⚠️ Component "${componentName}" not found, using export default function as fallback`
461
+ );
462
+ componentNode = fallback.node;
463
+ componentStart = fallback.start;
464
+ componentEnd = fallback.end;
661
465
  }
662
- code = code.replace(/^```[\w]*\n?/gm, "").replace(/\n?```$/gm, "").trim();
663
- code = code.replace(/^(Here'?s?|Here is|Modified|Output|Result)[:\s]*/i, "").replace(/\s*(Done|Complete|That'?s? it)\.?$/i, "").trim();
664
- code = code.replace(/\s*\/\/ ← CLICKED ELEMENT STARTS/g, "").replace(/\s*\/\/ ← CLICKED ELEMENT ENDS/g, "").replace(/\s*\/\/ ← CLICKED ELEMENT \(single line\)/g, "").replace(/\s*\/\/ ← \(clicked element\)/g, "").trim();
665
- if (!code) return "";
666
- if (!isFullComponent) {
667
- const lines = code.split("\n");
668
- const firstLineIndent = ((_b = (_a = lines[0]) == null ? void 0 : _a.match(/^(\s*)/)) == null ? void 0 : _b[1]) || "";
669
- const indentDiff = baseIndentation.length - firstLineIndent.length;
670
- if (indentDiff !== 0) {
671
- for (let i = 0; i < lines.length; i++) {
672
- if (lines[i].trim()) {
673
- const currentIndent = ((_c = lines[i].match(/^(\s*)/)) == null ? void 0 : _c[1]) || "";
674
- const newIndentLength = Math.max(
675
- 0,
676
- currentIndent.length + indentDiff
677
- );
678
- lines[i] = " ".repeat(newIndentLength) + lines[i].trimStart();
466
+ const allElementsByTag = /* @__PURE__ */ new Map();
467
+ const elementsAtLine = [];
468
+ traverse(ast, {
469
+ JSXElement(path2) {
470
+ const loc = path2.node.loc;
471
+ if (!loc) return;
472
+ const startLine = loc.start.line;
473
+ const endLine = loc.end.line;
474
+ if (startLine < componentStart || endLine > componentEnd) return;
475
+ const opening = path2.node.openingElement;
476
+ if (t.isJSXIdentifier(opening.name)) {
477
+ const tagName = opening.name.name;
478
+ if (!allElementsByTag.has(tagName)) {
479
+ allElementsByTag.set(tagName, []);
679
480
  }
481
+ allElementsByTag.get(tagName).push({ node: path2.node, startLine, endLine, score: 0 });
482
+ }
483
+ if (startLine === lineNumber) {
484
+ elementsAtLine.push({ node: path2.node, startLine, endLine, score: 0 });
680
485
  }
681
- code = lines.join("\n");
682
486
  }
683
- }
684
- return marker + code;
685
- }
686
- function parseDebugStack(stack) {
687
- if (!stack) return null;
688
- const stackStr = typeof stack === "string" ? stack : stack.stack || String(stack);
689
- const frames = stackStr.split("\n");
690
- const skipPatterns = [
691
- "node_modules",
692
- "SegmentViewNode",
693
- "LayoutRouter",
694
- "ErrorBoundary",
695
- "fakeJSXCallSite"
696
- ];
697
- for (const frame of frames) {
698
- if (skipPatterns.some((p) => frame.includes(p))) continue;
699
- const match = frame.match(/at\s+(\w+)\s+\((.+?):(\d+):(\d+)\)?$/);
700
- if (match) {
701
- let filePath = match[2];
702
- const line = parseInt(match[3], 10);
703
- const column = parseInt(match[4], 10);
704
- let chunkId;
705
- const chunkMatch = filePath.match(/\?([^:]+)$/);
706
- if (chunkMatch) {
707
- chunkId = chunkMatch[1];
708
- filePath = filePath.replace(/\?[^:]*$/, "");
487
+ });
488
+ if (elementsAtLine.length > 0) {
489
+ if (elementsAtLine.length === 1) {
490
+ const target = elementsAtLine[0];
491
+ return {
492
+ startLine: target.startLine,
493
+ endLine: target.endLine,
494
+ componentStart,
495
+ componentEnd
496
+ };
497
+ }
498
+ if (elementContext) {
499
+ for (const elem of elementsAtLine) {
500
+ if (t.isJSXElement(elem.node)) {
501
+ const score = scoreElementMatch(elem.node, elementContext, fileContent);
502
+ elem.score = score;
503
+ }
709
504
  }
710
- filePath = cleanPath(filePath);
711
- if (!shouldSkipPath(filePath)) {
712
- console.log("parseDebugStack extracted:", { filePath, line, column });
713
- return { filePath, line, column, chunkId };
505
+ elementsAtLine.sort((a, b) => b.score - a.score);
506
+ if (elementsAtLine[0].score > 0) {
507
+ return {
508
+ startLine: elementsAtLine[0].startLine,
509
+ endLine: elementsAtLine[0].endLine,
510
+ componentStart,
511
+ componentEnd
512
+ };
714
513
  }
715
514
  }
515
+ return {
516
+ startLine: elementsAtLine[0].startLine,
517
+ endLine: elementsAtLine[0].endLine,
518
+ componentStart,
519
+ componentEnd
520
+ };
716
521
  }
717
- console.log(
718
- "parseDebugStack: no valid frame found in stack:",
719
- stackStr.substring(0, 200)
720
- );
721
- return null;
722
- }
723
- function extractComponentNameFromStack(stack) {
724
- if (!stack) return null;
725
- const stackStr = typeof stack === "string" ? stack : stack.stack || String(stack);
726
- const frames = stackStr.split("\n");
727
- const skipPatterns = [
728
- "node_modules",
729
- "SegmentViewNode",
730
- "LayoutRouter",
731
- "ErrorBoundary",
732
- "fakeJSXCallSite",
733
- "react_stack_bottom_frame"
734
- ];
735
- for (const frame of frames) {
736
- if (skipPatterns.some((p) => frame.includes(p))) continue;
737
- const match = frame.match(/at\s+(\w+)\s+\(/);
738
- if (match && match[1]) {
739
- const componentName = match[1];
740
- if (componentName !== "Object" && componentName !== "anonymous") {
741
- return componentName;
522
+ if (elementContext == null ? void 0 : elementContext.tagName) {
523
+ const allOfTag = allElementsByTag.get(elementContext.tagName);
524
+ if (allOfTag && allOfTag.length > 0) {
525
+ if (elementContext.textContent || elementContext.className) {
526
+ for (const elem of allOfTag) {
527
+ if (t.isJSXElement(elem.node)) {
528
+ elem.score = scoreElementMatch(elem.node, elementContext, fileContent);
529
+ }
530
+ }
531
+ allOfTag.sort((a, b) => b.score - a.score);
532
+ if (allOfTag[0].score > 50) {
533
+ return {
534
+ startLine: allOfTag[0].startLine,
535
+ endLine: allOfTag[0].endLine,
536
+ componentStart,
537
+ componentEnd
538
+ };
539
+ }
540
+ }
541
+ if (elementContext.nthOfType && allOfTag.length >= elementContext.nthOfType) {
542
+ const target = allOfTag[elementContext.nthOfType - 1];
543
+ return {
544
+ startLine: target.startLine,
545
+ endLine: target.endLine,
546
+ componentStart,
547
+ componentEnd
548
+ };
742
549
  }
743
550
  }
744
551
  }
745
- return null;
746
- }
747
- function parseDebugStackFrames(stack) {
748
- if (!stack) return [];
749
- const stackStr = typeof stack === "string" ? stack : stack.stack || String(stack);
750
- const frames = stackStr.split("\n");
751
- const skipPatterns = [
752
- "node_modules",
753
- "SegmentViewNode",
754
- "LayoutRouter",
755
- "ErrorBoundary",
756
- "fakeJSXCallSite",
757
- "react_stack_bottom_frame"
758
- ];
759
- const positions = [];
760
- for (const frame of frames) {
761
- if (skipPatterns.some((p) => frame.includes(p))) continue;
762
- const match = frame.match(/at\s+(\w+)\s+\((.+?):(\d+):(\d+)\)?$/);
763
- if (match) {
764
- match[1];
765
- let filePath = match[2];
766
- const line = parseInt(match[3], 10);
767
- const column = parseInt(match[4], 10);
768
- let chunkId;
769
- const chunkMatch = filePath.match(/\?([^:]+)$/);
770
- if (chunkMatch) {
771
- chunkId = chunkMatch[1];
772
- filePath = filePath.replace(/\?[^:]*$/, "");
773
- }
774
- filePath = cleanPath(filePath);
775
- if (!shouldSkipPath(filePath)) {
776
- positions.push({ filePath, line, column, chunkId });
777
- if (positions.length >= 2) break;
552
+ const nearbyElements = [];
553
+ traverse(ast, {
554
+ JSXElement(path2) {
555
+ const loc = path2.node.loc;
556
+ if (!loc) return;
557
+ const startLine = loc.start.line;
558
+ const endLine = loc.end.line;
559
+ if (startLine < componentStart || endLine > componentEnd) return;
560
+ if (Math.abs(startLine - lineNumber) <= 5) {
561
+ const score = elementContext ? scoreElementMatch(path2.node, elementContext, fileContent) : 100 - Math.abs(startLine - lineNumber);
562
+ nearbyElements.push({ node: path2.node, startLine, endLine, score });
778
563
  }
779
564
  }
565
+ });
566
+ if (nearbyElements.length > 0) {
567
+ nearbyElements.sort((a, b) => b.score - a.score);
568
+ return {
569
+ startLine: nearbyElements[0].startLine,
570
+ endLine: nearbyElements[0].endLine,
571
+ componentStart,
572
+ componentEnd
573
+ };
780
574
  }
781
- console.log(`parseDebugStackFrames extracted ${positions.length} frames:`, positions);
782
- return positions;
783
- }
784
- async function resolveOriginalPosition(compiledPos, projectRoot) {
785
- try {
786
- console.log("resolveOriginalPosition called with:", compiledPos);
787
- let compiledFilePath = compiledPos.filePath;
788
- compiledFilePath = cleanPath(compiledFilePath);
789
- console.log("After cleanPath:", compiledFilePath);
790
- if (compiledFilePath.startsWith("http://") || compiledFilePath.startsWith("https://")) {
791
- const url = new URL(compiledFilePath);
792
- const pathname = url.pathname;
793
- if (pathname.startsWith("/_next/")) {
794
- const relativePath = pathname.substring("/_next/".length);
795
- compiledFilePath = path.join(projectRoot, ".next", "dev", relativePath);
796
- } else {
797
- console.warn("Unexpected HTTP URL path:", pathname);
798
- return null;
799
- }
800
- } else if (!path.isAbsolute(compiledFilePath)) {
801
- if (compiledFilePath.startsWith(".next/")) {
802
- compiledFilePath = path.resolve(projectRoot, compiledFilePath);
803
- } else {
804
- const resolved = path.resolve(projectRoot, compiledFilePath);
805
- if (await fileExists(resolved)) {
806
- compiledFilePath = resolved;
807
- } else {
808
- const possiblePaths = [
809
- path.join(projectRoot, ".next", "dev", compiledFilePath),
810
- path.join(projectRoot, compiledFilePath)
811
- ];
812
- for (const tryPath of possiblePaths) {
813
- if (await fileExists(tryPath)) {
814
- compiledFilePath = tryPath;
815
- break;
816
- }
817
- }
818
- }
819
- }
575
+ if (componentNode && componentStart > 0) {
576
+ return {
577
+ startLine: componentStart,
578
+ endLine: componentEnd,
579
+ componentStart,
580
+ componentEnd
581
+ };
582
+ }
583
+ return null;
584
+ }
585
+ function scoreElementMatch(node, context, fileContent) {
586
+ let score = 0;
587
+ const opening = node.openingElement;
588
+ if (t.isJSXIdentifier(opening.name)) {
589
+ if (opening.name.name === context.tagName) {
590
+ score += 50;
820
591
  } else {
821
- compiledFilePath = path.normalize(compiledFilePath);
592
+ return 0;
822
593
  }
823
- console.log("Normalized compiled file path:", compiledFilePath);
824
- const compiledExists = await fileExists(compiledFilePath);
825
- console.log(
826
- "Compiled file exists:",
827
- compiledExists,
828
- "at:",
829
- compiledFilePath
830
- );
831
- if (!compiledExists) {
832
- console.error(
833
- `Compiled file not found: ${compiledFilePath} (from parsed: ${compiledPos.filePath})`
834
- );
594
+ } else if (t.isJSXMemberExpression(opening.name)) {
595
+ const fullName = getJSXMemberName(opening.name);
596
+ if (fullName === context.tagName || fullName.endsWith(`.${context.tagName}`)) {
597
+ score += 50;
598
+ } else {
599
+ return 0;
835
600
  }
836
- const sourceMapPath = compiledFilePath + ".map";
837
- console.log("Looking for source map at:", sourceMapPath);
838
- const sourceMapExists = await fileExists(sourceMapPath);
839
- console.log("Source map exists:", sourceMapExists);
840
- if (!sourceMapExists) {
841
- console.error(
842
- `Source map not found: ${sourceMapPath} (from compiled: ${compiledPos.filePath}, normalized: ${compiledFilePath})`
843
- );
844
- const altPaths = [
845
- compiledFilePath.replace(/\.js$/, ".map"),
846
- path.join(
847
- path.dirname(compiledFilePath),
848
- path.basename(compiledFilePath) + ".map"
849
- )
850
- ];
851
- console.log("Trying alternative paths:", altPaths);
852
- for (const altPath of altPaths) {
853
- if (await fileExists(altPath)) {
854
- console.log("Found source map at alternative path:", altPath);
855
- return await resolveFromSourceMap(altPath, compiledPos, projectRoot);
856
- }
601
+ }
602
+ if (context.className) {
603
+ const classAttr = opening.attributes.find(
604
+ (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === "className"
605
+ );
606
+ if (classAttr && t.isJSXAttribute(classAttr)) {
607
+ const classValue = getAttributeValue(classAttr);
608
+ if (classValue && context.className.split(/\s+/).some((c) => classValue.includes(c))) {
609
+ score += 20;
857
610
  }
858
- return null;
859
611
  }
860
- return await resolveFromSourceMap(sourceMapPath, compiledPos, projectRoot);
861
- } catch (error) {
862
- console.error("Error resolving source map:", error);
863
- return null;
864
612
  }
865
- }
866
- async function resolveFromSourceMap(sourceMapPath, compiledPos, projectRoot) {
867
- try {
868
- const sourceMapContent = await fs.readFile(sourceMapPath, "utf-8");
869
- const sourceMap = JSON.parse(sourceMapContent);
870
- if ((!sourceMap.sources || sourceMap.sources.length === 0) && (!sourceMap.sections || sourceMap.sections.length === 0)) {
871
- console.warn(
872
- "Empty source map detected, cannot resolve position:",
873
- sourceMapPath
874
- );
875
- return null;
613
+ if (context.textContent && node.loc) {
614
+ const elementCode = fileContent.split("\n").slice(node.loc.start.line - 1, node.loc.end.line).join("\n");
615
+ const normalizedContent = context.textContent.toLowerCase().trim();
616
+ const normalizedElement = elementCode.toLowerCase();
617
+ if (normalizedElement.includes(normalizedContent)) {
618
+ score += 30;
876
619
  }
877
- if (sourceMap.sections) {
878
- let matchingSection = null;
879
- for (let i = sourceMap.sections.length - 1; i >= 0; i--) {
880
- const section = sourceMap.sections[i];
881
- const offset = section.offset;
882
- const sectionStartLine1Indexed = offset.line + 1;
883
- if (compiledPos.line > sectionStartLine1Indexed || compiledPos.line === sectionStartLine1Indexed && compiledPos.column >= offset.column) {
884
- matchingSection = section;
885
- break;
886
- }
887
- }
888
- if (matchingSection && matchingSection.map) {
889
- const sectionMap = matchingSection.map;
890
- const offset = matchingSection.offset;
891
- let consumer2;
892
- try {
893
- consumer2 = await new SourceMapConsumer(sectionMap);
894
- } catch (error) {
895
- console.error("Error creating SourceMapConsumer:", error);
896
- return null;
897
- }
898
- try {
899
- const adjustedLine = compiledPos.line - offset.line;
900
- const adjustedColumn = compiledPos.line === offset.line + 1 ? compiledPos.column - offset.column : compiledPos.column;
901
- const originalPos = consumer2.originalPositionFor({
902
- line: adjustedLine,
903
- column: adjustedColumn
904
- });
905
- if (originalPos.source && originalPos.line !== null) {
906
- const source = normalizeSourcePath(
907
- originalPos.source || "",
908
- projectRoot
909
- );
910
- return {
911
- source,
912
- line: originalPos.line,
913
- column: originalPos.column ?? 0
914
- };
915
- }
916
- } finally {
917
- consumer2.destroy();
620
+ }
621
+ if (context.props) {
622
+ for (const [key, value] of Object.entries(context.props)) {
623
+ if (key.startsWith("_")) continue;
624
+ const attr = opening.attributes.find(
625
+ (a) => t.isJSXAttribute(a) && t.isJSXIdentifier(a.name) && a.name.name === key
626
+ );
627
+ if (attr) {
628
+ score += 5;
629
+ if (typeof value === "string" && t.isJSXAttribute(attr)) {
630
+ const attrValue = getAttributeValue(attr);
631
+ if (attrValue === value) score += 10;
918
632
  }
919
633
  }
920
- return null;
921
- }
922
- const consumer = await new SourceMapConsumer(sourceMap);
923
- try {
924
- const originalPos = consumer.originalPositionFor({
925
- line: compiledPos.line,
926
- column: compiledPos.column
927
- });
928
- if (originalPos.source && originalPos.line !== null) {
929
- const source = normalizeSourcePath(
930
- originalPos.source || "",
931
- projectRoot
932
- );
933
- return {
934
- source,
935
- line: originalPos.line,
936
- column: originalPos.column ?? 0
937
- };
938
- }
939
- } finally {
940
- consumer.destroy();
941
634
  }
942
- return null;
943
- } catch (error) {
944
- console.error("Error resolving source map:", error);
945
- return null;
946
635
  }
636
+ return score;
947
637
  }
948
- async function fileExists(filePath) {
949
- try {
950
- await fs.access(filePath);
951
- return true;
952
- } catch {
953
- return false;
638
+ function getJSXMemberName(node) {
639
+ if (t.isJSXIdentifier(node.object)) {
640
+ return `${node.object.name}.${node.property.name}`;
641
+ }
642
+ if (t.isJSXMemberExpression(node.object)) {
643
+ return `${getJSXMemberName(node.object)}.${node.property.name}`;
954
644
  }
645
+ return node.property.name;
955
646
  }
956
- async function getOriginalPositionFromDebugStack(debugStack, projectRoot) {
957
- const compiledPos = parseDebugStack(debugStack);
958
- if (!compiledPos) return null;
959
- return await resolveOriginalPosition(compiledPos, projectRoot);
647
+ function getAttributeValue(attr) {
648
+ if (!attr.value) return null;
649
+ if (t.isStringLiteral(attr.value)) return attr.value.value;
650
+ if (t.isJSXExpressionContainer(attr.value) && t.isStringLiteral(attr.value.expression)) {
651
+ return attr.value.expression.value;
652
+ }
653
+ return null;
960
654
  }
961
655
  async function handleRead(req) {
962
656
  const devModeError = validateDevMode();
@@ -1313,209 +1007,6 @@ async function handleValidateSession(req) {
1313
1007
  );
1314
1008
  }
1315
1009
  }
1316
- const suggestionCache = /* @__PURE__ */ new Map();
1317
- const CACHE_TTL = 3e4;
1318
- function getCacheKey(params) {
1319
- return `${params.componentName}:${params.elementTag || "div"}:${params.lastSuggestion || ""}`;
1320
- }
1321
- function getCachedSuggestions(key) {
1322
- const cached = suggestionCache.get(key);
1323
- if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
1324
- return cached.suggestions;
1325
- }
1326
- suggestionCache.delete(key);
1327
- return null;
1328
- }
1329
- function cacheSuggestions(key, suggestions) {
1330
- suggestionCache.set(key, { suggestions, timestamp: Date.now() });
1331
- if (suggestionCache.size > 100) {
1332
- const oldestKeys = Array.from(suggestionCache.entries()).sort((a, b) => a[1].timestamp - b[1].timestamp).slice(0, 20).map(([key2]) => key2);
1333
- oldestKeys.forEach((key2) => suggestionCache.delete(key2));
1334
- }
1335
- }
1336
- const DEFAULT_SUGGESTIONS = [
1337
- "Add padding",
1338
- "Change color",
1339
- "Make larger",
1340
- "Add shadow",
1341
- "Round corners",
1342
- "Center content",
1343
- "Add hover effect",
1344
- "Adjust spacing"
1345
- ];
1346
- async function handleSuggestions(req) {
1347
- const devModeError = validateDevMode();
1348
- if (devModeError) return devModeError;
1349
- try {
1350
- const { searchParams } = new URL(req.url);
1351
- const componentName = searchParams.get("componentName") || "Component";
1352
- const elementTag = searchParams.get("elementTag") || void 0;
1353
- const className = searchParams.get("className") || void 0;
1354
- const textContent = searchParams.get("textContent") || void 0;
1355
- const lastSuggestion = searchParams.get("lastSuggestion") || void 0;
1356
- const editHistoryStr = searchParams.get("editHistory") || void 0;
1357
- const excludedSuggestionsStr = searchParams.get("excludedSuggestions") || void 0;
1358
- let editHistory = [];
1359
- if (editHistoryStr) {
1360
- try {
1361
- editHistory = JSON.parse(editHistoryStr);
1362
- } catch (e) {
1363
- }
1364
- }
1365
- let excludedSuggestions = [];
1366
- if (excludedSuggestionsStr) {
1367
- try {
1368
- excludedSuggestions = JSON.parse(excludedSuggestionsStr);
1369
- } catch (e) {
1370
- }
1371
- }
1372
- const cacheKey = getCacheKey({ componentName, elementTag, lastSuggestion });
1373
- const cached = excludedSuggestions.length === 0 ? getCachedSuggestions(cacheKey) : null;
1374
- if (cached) {
1375
- return NextResponse.json({
1376
- success: true,
1377
- suggestions: cached
1378
- });
1379
- }
1380
- const suggestions = await generateSuggestions({
1381
- componentName,
1382
- elementTag,
1383
- className,
1384
- textContent,
1385
- editHistory,
1386
- lastSuggestion,
1387
- excludedSuggestions
1388
- });
1389
- if (suggestions && suggestions.length > 0) {
1390
- cacheSuggestions(cacheKey, suggestions);
1391
- return NextResponse.json({
1392
- success: true,
1393
- suggestions
1394
- });
1395
- }
1396
- return NextResponse.json({
1397
- success: true,
1398
- suggestions: DEFAULT_SUGGESTIONS
1399
- });
1400
- } catch (error) {
1401
- return NextResponse.json(
1402
- {
1403
- success: false,
1404
- error: String(error),
1405
- suggestions: DEFAULT_SUGGESTIONS
1406
- // Fallback
1407
- },
1408
- { status: 500 }
1409
- );
1410
- }
1411
- }
1412
- async function generateSuggestions(options) {
1413
- const {
1414
- componentName,
1415
- elementTag,
1416
- className,
1417
- textContent,
1418
- editHistory,
1419
- lastSuggestion,
1420
- excludedSuggestions = []
1421
- } = options;
1422
- const apiKey = process.env.ANTHROPIC_API_KEY;
1423
- if (!apiKey) {
1424
- return null;
1425
- }
1426
- const model = new ChatAnthropic({
1427
- apiKey,
1428
- modelName: "claude-haiku-4-5-20251001",
1429
- maxTokens: 1024,
1430
- temperature: 0.3
1431
- // Slightly creative for variety
1432
- });
1433
- const systemPrompt = `You are a UI/UX expert suggesting quick edits for React components.
1434
-
1435
- Generate 6-8 concise, actionable suggestions that a developer might want to make next.
1436
-
1437
- RULES:
1438
- - Each suggestion must be 2-6 words (e.g., "Add padding", "Make text larger")
1439
- - Focus on common UI improvements: spacing, colors, sizing, layout, shadows, borders, typography
1440
- - Consider the element type (${elementTag || "element"})
1441
- - Output ONLY a JSON array of strings, no explanations, no markdown fences
1442
- ${excludedSuggestions.length > 0 ? `- DO NOT suggest any of these (user wants different options): ${excludedSuggestions.join(
1443
- ", "
1444
- )}` : ""}
1445
-
1446
- ${lastSuggestion ? `IMPORTANT - LAST EDIT CONTEXT:
1447
- The user just applied: "${lastSuggestion}"
1448
- Your suggestions MUST be direct follow-ups/refinements of this last change:
1449
- - If it was "Add padding" → suggest "More padding", "Less padding", "Add vertical padding only"
1450
- - If it was "Make it blue" → suggest "Darker blue", "Lighter blue", "Change to navy blue"
1451
- - If it was "Increase font size" → suggest "Decrease font size", "Make even larger", "Adjust line height"
1452
- - 4-6 suggestions should be variations/refinements of the last edit
1453
- - 2-4 suggestions can be other related improvements
1454
-
1455
- Generate follow-up suggestions that let the user iteratively refine what they just did.` : `Generate varied initial suggestions covering different aspects: layout, colors, spacing, shadows, typography, etc.`}
1456
-
1457
- Example output format:
1458
- ["Add hover effect", "Increase padding", "Make corners rounder", "Change to flex row", "Add drop shadow", "Adjust font size"]`;
1459
- let userPrompt = `Element: <${elementTag || "div"}>`;
1460
- if (className) {
1461
- userPrompt += `
1462
- Classes: ${className}`;
1463
- }
1464
- if (textContent) {
1465
- userPrompt += `
1466
- Text: "${textContent.slice(0, 50)}"`;
1467
- }
1468
- userPrompt += `
1469
- Component: ${componentName}`;
1470
- if (editHistory && editHistory.length > 0) {
1471
- userPrompt += `
1472
-
1473
- Recent edits:
1474
- `;
1475
- editHistory.slice(-3).forEach((item, idx) => {
1476
- userPrompt += `${idx + 1}. ${item.suggestion} ${item.success ? "✓" : "✗"}
1477
- `;
1478
- });
1479
- } else {
1480
- userPrompt += `
1481
-
1482
- No previous edits.`;
1483
- }
1484
- if (lastSuggestion) {
1485
- userPrompt += `
1486
-
1487
- **LAST EDIT APPLIED:** "${lastSuggestion}"`;
1488
- userPrompt += `
1489
-
1490
- Generate 6-8 follow-up suggestions (mostly variations of the last edit):`;
1491
- } else {
1492
- userPrompt += `
1493
-
1494
- Generate 6-8 initial suggestions:`;
1495
- }
1496
- try {
1497
- const response = await model.invoke([
1498
- new SystemMessage(systemPrompt),
1499
- new HumanMessage(userPrompt)
1500
- ]);
1501
- let content = typeof response.content === "string" ? response.content : String(response.content);
1502
- content = content.trim();
1503
- content = content.replace(/^```json?\s*/gm, "").replace(/\s*```$/gm, "");
1504
- const suggestions = JSON.parse(content);
1505
- if (Array.isArray(suggestions)) {
1506
- const validSuggestions = suggestions.filter((s) => typeof s === "string").map((s) => s.trim()).filter((s) => {
1507
- const words = s.split(/\s+/).length;
1508
- return words >= 1 && words <= 15;
1509
- }).slice(0, 8);
1510
- if (validSuggestions.length >= 4) {
1511
- return validSuggestions;
1512
- }
1513
- }
1514
- return null;
1515
- } catch (e) {
1516
- return null;
1517
- }
1518
- }
1519
1010
  class AIEditorAgent {
1520
1011
  constructor(session, projectRoot) {
1521
1012
  this.session = session;
@@ -1871,14 +1362,19 @@ async function handleChat(req) {
1871
1362
  );
1872
1363
  }
1873
1364
  }
1365
+ async function handleConfig() {
1366
+ const enabled = process.env.NEXT_PUBLIC_AI_EDITOR_ENABLED === "true" || process.env.VITE_AI_EDITOR_ENABLED === "true" || process.env.REACT_APP_AI_EDITOR_ENABLED === "true";
1367
+ return NextResponse.json({
1368
+ enabled,
1369
+ // Include NODE_ENV for debugging
1370
+ nodeEnv: process.env.NODE_ENV
1371
+ });
1372
+ }
1874
1373
  async function handleAIEditorRequest(req, context) {
1875
1374
  const { path: path2 } = await context.params;
1876
1375
  const endpoint = path2[0];
1877
1376
  const method = req.method;
1878
1377
  switch (endpoint) {
1879
- case "edit":
1880
- if (method === "POST") return handleEdit(req);
1881
- break;
1882
1378
  case "read":
1883
1379
  if (method === "GET") return handleRead(req);
1884
1380
  break;
@@ -1894,12 +1390,12 @@ async function handleAIEditorRequest(req, context) {
1894
1390
  case "validate-session":
1895
1391
  if (method === "POST") return handleValidateSession(req);
1896
1392
  break;
1897
- case "suggestions":
1898
- if (method === "GET") return handleSuggestions(req);
1899
- break;
1900
1393
  case "chat":
1901
1394
  if (method === "POST") return handleChat(req);
1902
1395
  break;
1396
+ case "config":
1397
+ if (method === "GET") return handleConfig();
1398
+ break;
1903
1399
  }
1904
1400
  return NextResponse.json(
1905
1401
  { error: `Unknown endpoint: ${endpoint}` },
@@ -1971,31 +1467,29 @@ async function handleCommentsRequest(request) {
1971
1467
  }
1972
1468
  }
1973
1469
  export {
1974
- handleEdit as a,
1975
- handleRead as b,
1976
- handleUndo as c,
1977
- handleResolve as d,
1978
- handleAbsolutePath as e,
1979
- handleValidateSession as f,
1980
- handleSuggestions as g,
1470
+ handleRead as a,
1471
+ handleUndo as b,
1472
+ handleResolve as c,
1473
+ handleAbsolutePath as d,
1474
+ handleValidateSession as e,
1475
+ handleCommentsRequest as f,
1476
+ fileExists$1 as g,
1981
1477
  handleAIEditorRequest as h,
1982
- handleCommentsRequest as i,
1983
- isPathSecure as j,
1984
- fileExists$1 as k,
1985
- extractComponentName as l,
1986
- validateGeneratedCode as m,
1478
+ isPathSecure as i,
1479
+ extractComponentName as j,
1480
+ validateGeneratedCode as k,
1481
+ findTargetElement as l,
1482
+ getJSXMemberName as m,
1987
1483
  normalizePath as n,
1988
- findTargetElement as o,
1484
+ getAttributeValue as o,
1989
1485
  parseFile as p,
1990
- getJSXMemberName as q,
1486
+ parseDebugStack as q,
1991
1487
  resolveFilePath as r,
1992
1488
  scoreElementMatch as s,
1993
- getAttributeValue as t,
1994
- parseDebugStack as u,
1489
+ extractComponentNameFromStack as t,
1490
+ parseDebugStackFrames as u,
1995
1491
  validateDevMode as v,
1996
- extractComponentNameFromStack as w,
1997
- parseDebugStackFrames as x,
1998
- resolveOriginalPosition as y,
1999
- getOriginalPositionFromDebugStack as z
1492
+ resolveOriginalPosition as w,
1493
+ getOriginalPositionFromDebugStack as x
2000
1494
  };
2001
- //# sourceMappingURL=comments-D3m0RsOO.js.map
1495
+ //# sourceMappingURL=comments-BYFEhf6K.js.map