next-ai-editor 0.1.1 → 0.1.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/dist/{AIEditorProvider-Bs9zUVrL.cjs → AIEditorProvider-BGHm2xyU.cjs} +823 -378
- package/dist/AIEditorProvider-BGHm2xyU.cjs.map +1 -0
- package/dist/{AIEditorProvider-D-w9-GZb.js → AIEditorProvider-CxdGjdLL.js} +847 -402
- package/dist/AIEditorProvider-CxdGjdLL.js.map +1 -0
- package/dist/client/AIEditorProvider.d.ts +8 -16
- package/dist/client/AIEditorProvider.d.ts.map +1 -1
- package/dist/client/fiber-utils.d.ts +35 -0
- package/dist/client/fiber-utils.d.ts.map +1 -0
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/query-params.d.ts +9 -0
- package/dist/client/query-params.d.ts.map +1 -0
- package/dist/client.cjs +1 -1
- package/dist/client.js +1 -1
- package/dist/{index-DnoYi4f8.cjs → index-CNJqd4EQ.cjs} +656 -225
- package/dist/index-CNJqd4EQ.cjs.map +1 -0
- package/dist/{index-BFa7H-uO.js → index-DrmEf13c.js} +662 -231
- package/dist/index-DrmEf13c.js.map +1 -0
- package/dist/index.cjs +7 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +15 -10
- package/dist/path-utils-Bai2xKx9.js +36 -0
- package/dist/path-utils-Bai2xKx9.js.map +1 -0
- package/dist/path-utils-DYzEWUGy.cjs +35 -0
- package/dist/path-utils-DYzEWUGy.cjs.map +1 -0
- package/dist/server/handlers/edit.d.ts.map +1 -1
- package/dist/server/handlers/index.d.ts +1 -0
- package/dist/server/handlers/index.d.ts.map +1 -1
- package/dist/server/handlers/read.d.ts.map +1 -1
- package/dist/server/handlers/resolve.d.ts.map +1 -1
- package/dist/server/handlers/suggestions.d.ts +3 -0
- package/dist/server/handlers/suggestions.d.ts.map +1 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/utils/ast.d.ts +10 -0
- package/dist/server/utils/ast.d.ts.map +1 -1
- package/dist/server/utils/source-map.d.ts +10 -0
- package/dist/server/utils/source-map.d.ts.map +1 -1
- package/dist/server.cjs +6 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +14 -9
- package/dist/shared/path-utils.d.ts +24 -0
- package/dist/shared/path-utils.d.ts.map +1 -0
- package/dist/shared/types.d.ts +30 -0
- package/dist/shared/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/AIEditorProvider-Bs9zUVrL.cjs.map +0 -1
- package/dist/AIEditorProvider-D-w9-GZb.js.map +0 -1
- package/dist/index-BFa7H-uO.js.map +0 -1
- package/dist/index-DnoYi4f8.cjs.map +0 -1
|
@@ -4,6 +4,7 @@ const jsxRuntime = require("react/jsx-runtime");
|
|
|
4
4
|
const react = require("react");
|
|
5
5
|
const reactSyntaxHighlighter = require("react-syntax-highlighter");
|
|
6
6
|
const prism = require("react-syntax-highlighter/dist/esm/styles/prism");
|
|
7
|
+
const pathUtils = require("./path-utils-DYzEWUGy.cjs");
|
|
7
8
|
const _AIEditorStorage = class _AIEditorStorage {
|
|
8
9
|
// 5MB
|
|
9
10
|
/**
|
|
@@ -223,120 +224,31 @@ _AIEditorStorage.STORAGE_PREFIX = "ai-editor:session:";
|
|
|
223
224
|
_AIEditorStorage.VERSION = "1.0.0";
|
|
224
225
|
_AIEditorStorage.MAX_STORAGE_SIZE = 5 * 1024 * 1024;
|
|
225
226
|
let AIEditorStorage = _AIEditorStorage;
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
if (typeof window === "undefined" || process.env.NODE_ENV !== "development") {
|
|
236
|
-
return null;
|
|
237
|
-
}
|
|
238
|
-
if (!source.debugStack) {
|
|
239
|
-
console.warn("No debugStack available for resolution:", source);
|
|
240
|
-
return null;
|
|
241
|
-
}
|
|
242
|
-
const cacheKey = source.debugStack;
|
|
243
|
-
const cached = sourceResolutionCache.get(cacheKey);
|
|
244
|
-
if (cached) {
|
|
245
|
-
const resolved2 = { ...source, ...cached };
|
|
246
|
-
if (resolved2.componentName === "Unknown" && resolved2.filePath) {
|
|
247
|
-
resolved2.componentName = inferComponentNameFromPath(resolved2.filePath);
|
|
248
|
-
}
|
|
249
|
-
return resolved2;
|
|
250
|
-
}
|
|
251
|
-
let inflight = inflightSourceResolutions.get(cacheKey);
|
|
252
|
-
if (!inflight) {
|
|
253
|
-
inflight = fetch("/api/ai-editor/resolve", {
|
|
254
|
-
method: "POST",
|
|
255
|
-
headers: { "Content-Type": "application/json" },
|
|
256
|
-
body: JSON.stringify({ debugStack: cacheKey })
|
|
257
|
-
}).then(async (res) => {
|
|
258
|
-
if (!res.ok) {
|
|
259
|
-
const errorText = await res.text();
|
|
260
|
-
console.error(
|
|
261
|
-
`Resolve API error ${res.status}:`,
|
|
262
|
-
errorText,
|
|
263
|
-
"for stack:",
|
|
264
|
-
cacheKey.substring(0, 200)
|
|
265
|
-
);
|
|
266
|
-
return inferFilePathFromComponentName(source.componentName);
|
|
267
|
-
}
|
|
268
|
-
const data = await res.json();
|
|
269
|
-
if ((data == null ? void 0 : data.success) && data.filePath && data.lineNumber) {
|
|
270
|
-
const resolved2 = {
|
|
271
|
-
filePath: data.filePath,
|
|
272
|
-
lineNumber: data.lineNumber,
|
|
273
|
-
columnNumber: typeof data.columnNumber === "number" ? data.columnNumber : source.columnNumber
|
|
274
|
-
};
|
|
275
|
-
sourceResolutionCache.set(cacheKey, resolved2);
|
|
276
|
-
return resolved2;
|
|
277
|
-
}
|
|
278
|
-
console.warn("Resolve API returned unsuccessful response:", data);
|
|
279
|
-
return inferFilePathFromComponentName(source.componentName);
|
|
280
|
-
}).catch((err) => {
|
|
281
|
-
console.error("Error calling resolve API:", err);
|
|
282
|
-
return inferFilePathFromComponentName(source.componentName);
|
|
283
|
-
}).finally(() => {
|
|
284
|
-
inflightSourceResolutions.delete(cacheKey);
|
|
285
|
-
});
|
|
286
|
-
inflightSourceResolutions.set(cacheKey, inflight);
|
|
287
|
-
}
|
|
288
|
-
const resolvedInfo = await inflight;
|
|
289
|
-
if (!resolvedInfo) return null;
|
|
290
|
-
const resolved = {
|
|
291
|
-
...source,
|
|
292
|
-
filePath: resolvedInfo.filePath,
|
|
293
|
-
lineNumber: resolvedInfo.lineNumber,
|
|
294
|
-
columnNumber: resolvedInfo.columnNumber
|
|
227
|
+
function buildReadQueryParams(selectedSource, includeParent = true) {
|
|
228
|
+
var _a, _b, _c, _d;
|
|
229
|
+
const params = {
|
|
230
|
+
path: selectedSource.filePath,
|
|
231
|
+
line: String(selectedSource.lineNumber),
|
|
232
|
+
tagName: ((_a = selectedSource.elementContext) == null ? void 0 : _a.tagName) || "",
|
|
233
|
+
nthOfType: String(((_b = selectedSource.elementContext) == null ? void 0 : _b.nthOfType) || 0),
|
|
234
|
+
textContent: ((_c = selectedSource.elementContext) == null ? void 0 : _c.textContent) || "",
|
|
235
|
+
className: ((_d = selectedSource.elementContext) == null ? void 0 : _d.className) || ""
|
|
295
236
|
};
|
|
296
|
-
if (
|
|
297
|
-
|
|
237
|
+
if (selectedSource.debugStack) {
|
|
238
|
+
params.debugStack = selectedSource.debugStack;
|
|
298
239
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
`src/components/${componentName}.tsx`,
|
|
309
|
-
`src/components/${componentName}.jsx`
|
|
310
|
-
];
|
|
311
|
-
for (const tryPath of possiblePaths) {
|
|
312
|
-
try {
|
|
313
|
-
const response = await fetch(
|
|
314
|
-
`/api/ai-editor/absolute-path?path=${encodeURIComponent(tryPath)}`
|
|
315
|
-
);
|
|
316
|
-
if (response.ok) {
|
|
317
|
-
const data = await response.json();
|
|
318
|
-
if (data.success) {
|
|
319
|
-
console.log(
|
|
320
|
-
`Inferred file path for ${componentName}: ${tryPath}`
|
|
321
|
-
);
|
|
322
|
-
return {
|
|
323
|
-
filePath: tryPath,
|
|
324
|
-
lineNumber: 1,
|
|
325
|
-
columnNumber: 0
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
} catch (err) {
|
|
240
|
+
if (includeParent && selectedSource.parentComponent) {
|
|
241
|
+
params.parentFilePath = selectedSource.parentComponent.filePath;
|
|
242
|
+
params.parentLine = String(selectedSource.parentComponent.lineNumber);
|
|
243
|
+
params.parentComponentName = selectedSource.parentComponent.componentName;
|
|
244
|
+
if (selectedSource.parentComponent.debugStack) {
|
|
245
|
+
params.parentDebugStack = selectedSource.parentComponent.debugStack;
|
|
246
|
+
}
|
|
247
|
+
if (selectedSource.parentComponent.childKey) {
|
|
248
|
+
params.childKey = selectedSource.parentComponent.childKey;
|
|
330
249
|
}
|
|
331
250
|
}
|
|
332
|
-
|
|
333
|
-
return null;
|
|
334
|
-
}
|
|
335
|
-
const EditorContext = react.createContext(null);
|
|
336
|
-
function useAIEditor() {
|
|
337
|
-
const ctx = react.useContext(EditorContext);
|
|
338
|
-
if (!ctx) throw new Error("useAIEditor must be used within AIEditorProvider");
|
|
339
|
-
return ctx;
|
|
251
|
+
return params;
|
|
340
252
|
}
|
|
341
253
|
function getFiberFromElement(element) {
|
|
342
254
|
const key = Object.keys(element).find(
|
|
@@ -345,14 +257,26 @@ function getFiberFromElement(element) {
|
|
|
345
257
|
return key ? element[key] : null;
|
|
346
258
|
}
|
|
347
259
|
function getComponentName(fiber) {
|
|
348
|
-
var _a;
|
|
260
|
+
var _a, _b;
|
|
349
261
|
if (!fiber) return "Unknown";
|
|
350
262
|
const type = fiber.type;
|
|
263
|
+
if ((_a = fiber._debugSource) == null ? void 0 : _a.fileName) {
|
|
264
|
+
const fileName = fiber._debugSource.fileName;
|
|
265
|
+
const match = fileName.match(/\/([^/]+)\.(tsx?|jsx?)$/);
|
|
266
|
+
if (match) return match[1];
|
|
267
|
+
}
|
|
351
268
|
if (typeof type === "string") return type;
|
|
352
269
|
if (typeof type === "function")
|
|
353
270
|
return type.displayName || type.name || "Anonymous";
|
|
354
271
|
if (type == null ? void 0 : type.displayName) return type.displayName;
|
|
355
|
-
if ((
|
|
272
|
+
if ((_b = type == null ? void 0 : type.render) == null ? void 0 : _b.name) return type.render.name;
|
|
273
|
+
if (fiber._debugStack) {
|
|
274
|
+
const stack = String(fiber._debugStack.stack || fiber._debugStack);
|
|
275
|
+
const match = stack.match(/at\s+([A-Z][a-zA-Z0-9]*)\s*\(/);
|
|
276
|
+
if (match && match[1] !== "Object") {
|
|
277
|
+
return match[1];
|
|
278
|
+
}
|
|
279
|
+
}
|
|
356
280
|
return "Unknown";
|
|
357
281
|
}
|
|
358
282
|
function isUserComponent(fiber) {
|
|
@@ -361,24 +285,6 @@ function isUserComponent(fiber) {
|
|
|
361
285
|
if (!type || typeof type === "string") return false;
|
|
362
286
|
return true;
|
|
363
287
|
}
|
|
364
|
-
function captureElementContext(element, fiber) {
|
|
365
|
-
const tagName = element.tagName.toLowerCase();
|
|
366
|
-
let textContent;
|
|
367
|
-
const directText = Array.from(element.childNodes).filter((n) => n.nodeType === Node.TEXT_NODE).map((n) => {
|
|
368
|
-
var _a;
|
|
369
|
-
return (_a = n.textContent) == null ? void 0 : _a.trim();
|
|
370
|
-
}).filter(Boolean).join(" ");
|
|
371
|
-
if (directText) {
|
|
372
|
-
textContent = directText.slice(0, 50);
|
|
373
|
-
} else if (element.textContent) {
|
|
374
|
-
textContent = element.textContent.trim().slice(0, 50);
|
|
375
|
-
}
|
|
376
|
-
const className = typeof element.className === "string" ? element.className.slice(0, 100) : void 0;
|
|
377
|
-
const nthOfType = countNthOfType(element, tagName);
|
|
378
|
-
const key = (fiber == null ? void 0 : fiber.key) ? String(fiber.key) : void 0;
|
|
379
|
-
const props = extractProps(fiber);
|
|
380
|
-
return { tagName, textContent, className, key, nthOfType, props };
|
|
381
|
-
}
|
|
382
288
|
function countNthOfType(element, tagName) {
|
|
383
289
|
let boundary = element.parentElement;
|
|
384
290
|
while (boundary) {
|
|
@@ -398,9 +304,6 @@ function countNthOfType(element, tagName) {
|
|
|
398
304
|
if (el === element) break;
|
|
399
305
|
count++;
|
|
400
306
|
}
|
|
401
|
-
console.log(
|
|
402
|
-
`[countNthOfType] tagName=${tagName}, found ${sameTagElements.length} elements (including boundary), this is #${count}`
|
|
403
|
-
);
|
|
404
307
|
return count;
|
|
405
308
|
}
|
|
406
309
|
function extractProps(fiber) {
|
|
@@ -429,51 +332,23 @@ function extractProps(fiber) {
|
|
|
429
332
|
}
|
|
430
333
|
return Object.keys(props).length > 0 ? props : void 0;
|
|
431
334
|
}
|
|
432
|
-
function
|
|
433
|
-
|
|
434
|
-
let
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
while (componentFiber) {
|
|
444
|
-
if (isUserComponent(componentFiber)) {
|
|
445
|
-
const componentName = getComponentName(componentFiber);
|
|
446
|
-
const isNextJSInternal = [
|
|
447
|
-
"Segment",
|
|
448
|
-
"Boundary",
|
|
449
|
-
"Router",
|
|
450
|
-
"Handler",
|
|
451
|
-
"Context",
|
|
452
|
-
"Layout",
|
|
453
|
-
"Template",
|
|
454
|
-
"Scroll",
|
|
455
|
-
"Focus",
|
|
456
|
-
"Loading",
|
|
457
|
-
"Error"
|
|
458
|
-
].some((pattern) => componentName.includes(pattern));
|
|
459
|
-
if (!isNextJSInternal) {
|
|
460
|
-
const rawDebugStack = current._debugStack ? String(current._debugStack.stack || current._debugStack) : void 0;
|
|
461
|
-
return {
|
|
462
|
-
filePath,
|
|
463
|
-
lineNumber: sourceData.lineNumber || 1,
|
|
464
|
-
columnNumber: sourceData.columnNumber || 0,
|
|
465
|
-
componentName,
|
|
466
|
-
debugStack: rawDebugStack
|
|
467
|
-
};
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
componentFiber = componentFiber.return || componentFiber._debugOwner || null;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
current = current.return || current._debugOwner || null;
|
|
335
|
+
function captureElementContext(element, fiber) {
|
|
336
|
+
const tagName = element.tagName.toLowerCase();
|
|
337
|
+
let textContent;
|
|
338
|
+
const directText = Array.from(element.childNodes).filter((n) => n.nodeType === Node.TEXT_NODE).map((n) => {
|
|
339
|
+
var _a;
|
|
340
|
+
return (_a = n.textContent) == null ? void 0 : _a.trim();
|
|
341
|
+
}).filter(Boolean).join(" ");
|
|
342
|
+
if (directText) {
|
|
343
|
+
textContent = directText.slice(0, 50);
|
|
344
|
+
} else if (element.textContent) {
|
|
345
|
+
textContent = element.textContent.trim().slice(0, 50);
|
|
475
346
|
}
|
|
476
|
-
|
|
347
|
+
const className = typeof element.className === "string" ? element.className.slice(0, 100) : void 0;
|
|
348
|
+
const nthOfType = countNthOfType(element, tagName);
|
|
349
|
+
const key = (fiber == null ? void 0 : fiber.key) ? String(fiber.key) : void 0;
|
|
350
|
+
const props = extractProps(fiber);
|
|
351
|
+
return { tagName, textContent, className, key, nthOfType, props };
|
|
477
352
|
}
|
|
478
353
|
function extractFromDebugStack(fiber) {
|
|
479
354
|
const stack = fiber._debugStack;
|
|
@@ -491,8 +366,8 @@ function extractFromDebugStack(fiber) {
|
|
|
491
366
|
if (skipPatterns.some((p) => frame.includes(p))) continue;
|
|
492
367
|
const match = frame.match(/at\s+(\w+)\s+\((.+?):(\d+):(\d+)\)?$/);
|
|
493
368
|
if (match) {
|
|
494
|
-
const fileName = cleanPath(match[2].replace(/\?[^:]*$/, ""));
|
|
495
|
-
if (!
|
|
369
|
+
const fileName = pathUtils.cleanPath(match[2].replace(/\?[^:]*$/, ""));
|
|
370
|
+
if (!pathUtils.shouldSkipPath(fileName, ["ai-editor-provider"])) {
|
|
496
371
|
return {
|
|
497
372
|
fileName,
|
|
498
373
|
lineNumber: parseInt(match[3], 10),
|
|
@@ -516,43 +391,378 @@ function extractFromDebugOwner(fiber) {
|
|
|
516
391
|
}
|
|
517
392
|
return null;
|
|
518
393
|
}
|
|
519
|
-
function
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
394
|
+
function findSourceFromFiber(fiber) {
|
|
395
|
+
if (!fiber) return null;
|
|
396
|
+
let actualComponentName = null;
|
|
397
|
+
if (fiber._debugOwner && typeof fiber._debugOwner === "object") {
|
|
398
|
+
const debugOwner = fiber._debugOwner;
|
|
399
|
+
if (debugOwner.name && typeof debugOwner.name === "string") {
|
|
400
|
+
actualComponentName = debugOwner.name;
|
|
401
|
+
}
|
|
523
402
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
403
|
+
let current = fiber;
|
|
404
|
+
const visited = /* @__PURE__ */ new Set();
|
|
405
|
+
let iterations = 0;
|
|
406
|
+
while (current && !visited.has(current) && iterations < 10) {
|
|
407
|
+
iterations++;
|
|
408
|
+
visited.add(current);
|
|
409
|
+
const sourceData = current._debugSource || extractFromDebugStack(current) || extractFromDebugOwner(current);
|
|
410
|
+
if (sourceData == null ? void 0 : sourceData.fileName) {
|
|
411
|
+
const filePath = pathUtils.cleanPath(sourceData.fileName);
|
|
412
|
+
if (!pathUtils.shouldSkipPath(filePath, ["ai-editor-provider"])) {
|
|
413
|
+
if (actualComponentName) {
|
|
414
|
+
const rawDebugStack = current._debugStack ? String(current._debugStack.stack || current._debugStack) : void 0;
|
|
415
|
+
return {
|
|
416
|
+
filePath,
|
|
417
|
+
lineNumber: sourceData.lineNumber || 1,
|
|
418
|
+
columnNumber: sourceData.columnNumber || 0,
|
|
419
|
+
componentName: actualComponentName,
|
|
420
|
+
debugStack: rawDebugStack
|
|
421
|
+
};
|
|
422
|
+
} else {
|
|
423
|
+
let componentFiber = current;
|
|
424
|
+
while (componentFiber) {
|
|
425
|
+
if (isUserComponent(componentFiber)) {
|
|
426
|
+
const componentName = getComponentName(componentFiber);
|
|
427
|
+
const isNextJSInternal = [
|
|
428
|
+
"Segment",
|
|
429
|
+
"Boundary",
|
|
430
|
+
"Router",
|
|
431
|
+
"Handler",
|
|
432
|
+
"Context",
|
|
433
|
+
"Layout",
|
|
434
|
+
"Template",
|
|
435
|
+
"Scroll",
|
|
436
|
+
"Focus",
|
|
437
|
+
"Loading",
|
|
438
|
+
"Error"
|
|
439
|
+
].some((pattern) => componentName.includes(pattern));
|
|
440
|
+
if (!isNextJSInternal) {
|
|
441
|
+
const rawDebugStack = current._debugStack ? String(current._debugStack.stack || current._debugStack) : void 0;
|
|
442
|
+
return {
|
|
443
|
+
filePath,
|
|
444
|
+
lineNumber: sourceData.lineNumber || 1,
|
|
445
|
+
columnNumber: sourceData.columnNumber || 0,
|
|
446
|
+
componentName,
|
|
447
|
+
debugStack: rawDebugStack
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
componentFiber = componentFiber.return || componentFiber._debugOwner || null;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
527
455
|
}
|
|
528
|
-
|
|
456
|
+
current = current.return || current._debugOwner || null;
|
|
529
457
|
}
|
|
530
|
-
|
|
531
|
-
return cleaned;
|
|
458
|
+
return null;
|
|
532
459
|
}
|
|
533
|
-
function
|
|
534
|
-
if (!
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
460
|
+
function findParentComponentFromFiber(childFiber, childComponentName) {
|
|
461
|
+
if (!childFiber) return null;
|
|
462
|
+
let childComponentFiber = null;
|
|
463
|
+
if (childFiber._debugOwner && typeof childFiber._debugOwner === "object") {
|
|
464
|
+
const debugOwner = childFiber._debugOwner;
|
|
465
|
+
if (debugOwner.type && typeof debugOwner.type === "function") {
|
|
466
|
+
const ownerComponentName = getComponentName(debugOwner);
|
|
467
|
+
if (ownerComponentName === childComponentName) {
|
|
468
|
+
childComponentFiber = debugOwner;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
if (!childComponentFiber) {
|
|
473
|
+
let current2 = childFiber;
|
|
474
|
+
let iterations2 = 0;
|
|
475
|
+
while (current2 && iterations2 < 20) {
|
|
476
|
+
iterations2++;
|
|
477
|
+
const componentName = getComponentName(current2);
|
|
478
|
+
current2.index;
|
|
479
|
+
if (isUserComponent(current2)) {
|
|
480
|
+
if (componentName === childComponentName) {
|
|
481
|
+
childComponentFiber = current2;
|
|
482
|
+
break;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
if (current2._debugOwner && typeof current2._debugOwner === "object") {
|
|
486
|
+
const debugOwner = current2._debugOwner;
|
|
487
|
+
if (debugOwner.name === childComponentName && !debugOwner.type) {
|
|
488
|
+
const parent = current2.return;
|
|
489
|
+
const parentDebugOwner = parent == null ? void 0 : parent._debugOwner;
|
|
490
|
+
const isRootElement = !parentDebugOwner || parentDebugOwner.name !== childComponentName;
|
|
491
|
+
if (isRootElement) {
|
|
492
|
+
childComponentFiber = current2;
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
current2 = current2.return;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
let childKey;
|
|
501
|
+
if (childComponentFiber == null ? void 0 : childComponentFiber.key) {
|
|
502
|
+
childKey = String(childComponentFiber.key);
|
|
503
|
+
} else if (childComponentFiber && typeof childComponentFiber.index === "number") {
|
|
504
|
+
childKey = String(childComponentFiber.index);
|
|
505
|
+
}
|
|
506
|
+
if (childFiber._debugOwner && typeof childFiber._debugOwner === "object") {
|
|
507
|
+
const debugOwner = childFiber._debugOwner;
|
|
508
|
+
if (debugOwner.owner && debugOwner.owner.name) {
|
|
509
|
+
const parentName = debugOwner.owner.name;
|
|
510
|
+
const parentDebugLocation = debugOwner.owner.debugLocation;
|
|
511
|
+
if (parentDebugLocation) {
|
|
512
|
+
const stack = String(parentDebugLocation.stack || parentDebugLocation);
|
|
513
|
+
return {
|
|
514
|
+
filePath: "",
|
|
515
|
+
// Will be resolved on server
|
|
516
|
+
lineNumber: 0,
|
|
517
|
+
columnNumber: 0,
|
|
518
|
+
componentName: parentName,
|
|
519
|
+
debugStack: stack,
|
|
520
|
+
childKey
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (debugOwner.type && typeof debugOwner.type === "function") {
|
|
525
|
+
const componentFiberOwner = debugOwner._debugOwner;
|
|
526
|
+
if (componentFiberOwner && typeof componentFiberOwner === "object") {
|
|
527
|
+
const parentName = componentFiberOwner.name;
|
|
528
|
+
const parentDebugLocation = componentFiberOwner.debugLocation;
|
|
529
|
+
if (parentName && typeof parentName === "string") {
|
|
530
|
+
if (parentDebugLocation) {
|
|
531
|
+
const stack = String(
|
|
532
|
+
parentDebugLocation.stack || parentDebugLocation
|
|
533
|
+
);
|
|
534
|
+
return {
|
|
535
|
+
filePath: "",
|
|
536
|
+
// Will be resolved on server
|
|
537
|
+
lineNumber: 0,
|
|
538
|
+
columnNumber: 0,
|
|
539
|
+
componentName: parentName,
|
|
540
|
+
debugStack: stack,
|
|
541
|
+
childKey
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
let current = childFiber.return;
|
|
549
|
+
const visited = /* @__PURE__ */ new Set();
|
|
550
|
+
let iterations = 0;
|
|
551
|
+
while (current && !visited.has(current) && iterations < 20) {
|
|
552
|
+
iterations++;
|
|
553
|
+
visited.add(current);
|
|
554
|
+
if (isUserComponent(current)) {
|
|
555
|
+
const componentName = getComponentName(current);
|
|
556
|
+
if (componentName === childComponentName) {
|
|
557
|
+
current = current._debugOwner || null;
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
const shouldSkipComponent = componentName.includes("AIEditorProvider") || // Skip the AI Editor wrapper itself
|
|
561
|
+
componentName.startsWith("__next") || // Skip all Next.js internal components like __next_root_layout_boundary__
|
|
562
|
+
componentName.startsWith("_") || // Skip internal components starting with underscore
|
|
563
|
+
[
|
|
564
|
+
"Segment",
|
|
565
|
+
"Boundary",
|
|
566
|
+
"Router",
|
|
567
|
+
"Handler",
|
|
568
|
+
"Context",
|
|
569
|
+
"Layout",
|
|
570
|
+
"Template",
|
|
571
|
+
"Scroll",
|
|
572
|
+
"Focus",
|
|
573
|
+
"Loading",
|
|
574
|
+
"Error",
|
|
575
|
+
"RootLayout",
|
|
576
|
+
// Skip root layout wrapper
|
|
577
|
+
"NotFound"
|
|
578
|
+
].some((pattern) => componentName.includes(pattern));
|
|
579
|
+
if (shouldSkipComponent) {
|
|
580
|
+
current = current._debugOwner || null;
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
const sourceData = current._debugSource || extractFromDebugStack(current) || extractFromDebugOwner(current);
|
|
584
|
+
if (sourceData == null ? void 0 : sourceData.fileName) {
|
|
585
|
+
const filePath = pathUtils.cleanPath(sourceData.fileName);
|
|
586
|
+
if (!pathUtils.shouldSkipPath(filePath, ["ai-editor-provider"])) {
|
|
587
|
+
const rawDebugStack = current._debugStack ? String(current._debugStack.stack || current._debugStack) : void 0;
|
|
588
|
+
return {
|
|
589
|
+
filePath,
|
|
590
|
+
lineNumber: sourceData.lineNumber || 1,
|
|
591
|
+
columnNumber: sourceData.columnNumber || 0,
|
|
592
|
+
componentName,
|
|
593
|
+
debugStack: rawDebugStack,
|
|
594
|
+
childKey
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
const nextOwner = current._debugOwner;
|
|
600
|
+
current = nextOwner || null;
|
|
601
|
+
}
|
|
602
|
+
const childDebugStack = childFiber._debugStack ? String(childFiber._debugStack.stack || childFiber._debugStack) : null;
|
|
603
|
+
if (childDebugStack) {
|
|
604
|
+
return {
|
|
605
|
+
filePath: "",
|
|
606
|
+
// Will be resolved on server from debugStack
|
|
607
|
+
lineNumber: 0,
|
|
608
|
+
columnNumber: 0,
|
|
609
|
+
componentName: "",
|
|
610
|
+
// Will be determined on server
|
|
611
|
+
debugStack: childDebugStack,
|
|
612
|
+
// Use child's debugStack for server-side resolution
|
|
613
|
+
childKey
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
return null;
|
|
538
617
|
}
|
|
539
618
|
function getSourceFromElement(element) {
|
|
619
|
+
var _a;
|
|
540
620
|
let current = element;
|
|
541
|
-
let
|
|
621
|
+
let elementWithSource = null;
|
|
622
|
+
let fiberWithSource = null;
|
|
542
623
|
while (current && current !== document.body) {
|
|
543
|
-
fiber = getFiberFromElement(current);
|
|
544
|
-
if (fiber)
|
|
624
|
+
const fiber = getFiberFromElement(current);
|
|
625
|
+
if (fiber) {
|
|
626
|
+
const hasSourceInfo = ((_a = fiber._debugSource) == null ? void 0 : _a.fileName) || fiber._debugStack || fiber._debugOwner && typeof fiber._debugOwner === "object";
|
|
627
|
+
if (hasSourceInfo && !fiberWithSource) {
|
|
628
|
+
elementWithSource = current;
|
|
629
|
+
fiberWithSource = fiber;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
545
632
|
current = current.parentElement;
|
|
546
633
|
}
|
|
547
|
-
if (!
|
|
548
|
-
const source = findSourceFromFiber(
|
|
634
|
+
if (!fiberWithSource || !elementWithSource) return null;
|
|
635
|
+
const source = findSourceFromFiber(fiberWithSource);
|
|
549
636
|
if (!source) return null;
|
|
550
|
-
const elementContext = captureElementContext(
|
|
551
|
-
|
|
637
|
+
const elementContext = captureElementContext(elementWithSource, fiberWithSource);
|
|
638
|
+
const parentComponent = findParentComponentFromFiber(
|
|
639
|
+
fiberWithSource,
|
|
640
|
+
source.componentName
|
|
641
|
+
);
|
|
642
|
+
return {
|
|
643
|
+
...source,
|
|
644
|
+
elementContext,
|
|
645
|
+
parentComponent: parentComponent || void 0
|
|
646
|
+
};
|
|
552
647
|
}
|
|
553
648
|
if (typeof window !== "undefined") {
|
|
554
649
|
window.__getSource = getSourceFromElement;
|
|
555
650
|
}
|
|
651
|
+
const ENABLE_SESSION_PERSISTENCE = false;
|
|
652
|
+
const sourceResolutionCache = /* @__PURE__ */ new Map();
|
|
653
|
+
const inflightSourceResolutions = /* @__PURE__ */ new Map();
|
|
654
|
+
function inferComponentNameFromPath(filePath) {
|
|
655
|
+
const fileName = filePath.split("/").pop() || "";
|
|
656
|
+
const nameWithoutExt = fileName.replace(/\.(tsx?|jsx?)$/, "");
|
|
657
|
+
return nameWithoutExt.charAt(0).toUpperCase() + nameWithoutExt.slice(1);
|
|
658
|
+
}
|
|
659
|
+
async function resolveSourceLocation(source) {
|
|
660
|
+
if (typeof window === "undefined" || process.env.NODE_ENV !== "development") {
|
|
661
|
+
return null;
|
|
662
|
+
}
|
|
663
|
+
if (!source.debugStack) {
|
|
664
|
+
console.warn("No debugStack available for resolution:", source);
|
|
665
|
+
return null;
|
|
666
|
+
}
|
|
667
|
+
const cacheKey = source.debugStack;
|
|
668
|
+
const cached = sourceResolutionCache.get(cacheKey);
|
|
669
|
+
if (cached) {
|
|
670
|
+
const resolved2 = { ...source, ...cached };
|
|
671
|
+
if (resolved2.componentName === "Unknown" && resolved2.filePath) {
|
|
672
|
+
resolved2.componentName = inferComponentNameFromPath(resolved2.filePath);
|
|
673
|
+
}
|
|
674
|
+
return resolved2;
|
|
675
|
+
}
|
|
676
|
+
let inflight = inflightSourceResolutions.get(cacheKey);
|
|
677
|
+
if (!inflight) {
|
|
678
|
+
inflight = fetch("/api/ai-editor/resolve", {
|
|
679
|
+
method: "POST",
|
|
680
|
+
headers: { "Content-Type": "application/json" },
|
|
681
|
+
body: JSON.stringify({ debugStack: cacheKey })
|
|
682
|
+
}).then(async (res) => {
|
|
683
|
+
if (!res.ok) {
|
|
684
|
+
const errorText = await res.text();
|
|
685
|
+
console.error(
|
|
686
|
+
`Resolve API error ${res.status}:`,
|
|
687
|
+
errorText,
|
|
688
|
+
"for stack:",
|
|
689
|
+
cacheKey.substring(0, 200)
|
|
690
|
+
);
|
|
691
|
+
return inferFilePathFromComponentName(source.componentName);
|
|
692
|
+
}
|
|
693
|
+
const data = await res.json();
|
|
694
|
+
if ((data == null ? void 0 : data.success) && data.filePath && data.lineNumber) {
|
|
695
|
+
const resolved2 = {
|
|
696
|
+
filePath: data.filePath,
|
|
697
|
+
lineNumber: data.lineNumber,
|
|
698
|
+
columnNumber: typeof data.columnNumber === "number" ? data.columnNumber : source.columnNumber
|
|
699
|
+
};
|
|
700
|
+
sourceResolutionCache.set(cacheKey, resolved2);
|
|
701
|
+
return resolved2;
|
|
702
|
+
}
|
|
703
|
+
console.warn("Resolve API returned unsuccessful response:", data);
|
|
704
|
+
return inferFilePathFromComponentName(source.componentName);
|
|
705
|
+
}).catch((err) => {
|
|
706
|
+
console.error("Error calling resolve API:", err);
|
|
707
|
+
return inferFilePathFromComponentName(source.componentName);
|
|
708
|
+
}).finally(() => {
|
|
709
|
+
inflightSourceResolutions.delete(cacheKey);
|
|
710
|
+
});
|
|
711
|
+
inflightSourceResolutions.set(cacheKey, inflight);
|
|
712
|
+
}
|
|
713
|
+
const resolvedInfo = await inflight;
|
|
714
|
+
if (!resolvedInfo) return null;
|
|
715
|
+
const resolved = {
|
|
716
|
+
...source,
|
|
717
|
+
filePath: resolvedInfo.filePath,
|
|
718
|
+
lineNumber: resolvedInfo.lineNumber,
|
|
719
|
+
columnNumber: resolvedInfo.columnNumber
|
|
720
|
+
};
|
|
721
|
+
if (resolved.componentName === "Unknown" && resolved.filePath) {
|
|
722
|
+
resolved.componentName = inferComponentNameFromPath(resolved.filePath);
|
|
723
|
+
}
|
|
724
|
+
return resolved;
|
|
725
|
+
}
|
|
726
|
+
async function inferFilePathFromComponentName(componentName) {
|
|
727
|
+
if (!componentName || componentName === "Unknown") return null;
|
|
728
|
+
const possiblePaths = [
|
|
729
|
+
`components/${componentName}.tsx`,
|
|
730
|
+
`components/${componentName}.jsx`,
|
|
731
|
+
`app/${componentName}.tsx`,
|
|
732
|
+
`app/${componentName}.jsx`,
|
|
733
|
+
`src/components/${componentName}.tsx`,
|
|
734
|
+
`src/components/${componentName}.jsx`
|
|
735
|
+
];
|
|
736
|
+
for (const tryPath of possiblePaths) {
|
|
737
|
+
try {
|
|
738
|
+
const response = await fetch(
|
|
739
|
+
`/api/ai-editor/absolute-path?path=${encodeURIComponent(tryPath)}`
|
|
740
|
+
);
|
|
741
|
+
if (response.ok) {
|
|
742
|
+
const data = await response.json();
|
|
743
|
+
if (data.success) {
|
|
744
|
+
console.log(
|
|
745
|
+
`Inferred file path for ${componentName}: ${tryPath}`
|
|
746
|
+
);
|
|
747
|
+
return {
|
|
748
|
+
filePath: tryPath,
|
|
749
|
+
lineNumber: 1,
|
|
750
|
+
columnNumber: 0
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
} catch (err) {
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
console.warn(`Could not infer file path for component: ${componentName}`);
|
|
758
|
+
return null;
|
|
759
|
+
}
|
|
760
|
+
const EditorContext = react.createContext(null);
|
|
761
|
+
function useAIEditor() {
|
|
762
|
+
const ctx = react.useContext(EditorContext);
|
|
763
|
+
if (!ctx) throw new Error("useAIEditor must be used within AIEditorProvider");
|
|
764
|
+
return ctx;
|
|
765
|
+
}
|
|
556
766
|
function AIEditorProvider({
|
|
557
767
|
children,
|
|
558
768
|
theme = "dark"
|
|
@@ -569,6 +779,11 @@ function AIEditorProvider({
|
|
|
569
779
|
const [showStaleWarning, setShowStaleWarning] = react.useState(false);
|
|
570
780
|
const [staleSession, setStaleSession] = react.useState(null);
|
|
571
781
|
const [hasActiveSession, setHasActiveSession] = react.useState(false);
|
|
782
|
+
const [parentInstance, setParentInstance] = react.useState(null);
|
|
783
|
+
const [suggestions, setSuggestions] = react.useState([]);
|
|
784
|
+
const [suggestionsLoading, setSuggestionsLoading] = react.useState(false);
|
|
785
|
+
const [lastAppliedSuggestion, setLastAppliedSuggestion] = react.useState(void 0);
|
|
786
|
+
const [suggestionsCache, setSuggestionsCache] = react.useState(/* @__PURE__ */ new Map());
|
|
572
787
|
react.useEffect(() => {
|
|
573
788
|
const handleKey = (e) => {
|
|
574
789
|
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key.toLowerCase() === "e") {
|
|
@@ -608,6 +823,15 @@ function AIEditorProvider({
|
|
|
608
823
|
async (suggestion) => {
|
|
609
824
|
if (!selectedSource)
|
|
610
825
|
return { success: false, error: "No element selected" };
|
|
826
|
+
const editId = Date.now().toString(36) + Math.random().toString(36).substring(2);
|
|
827
|
+
const pendingEdit = {
|
|
828
|
+
id: editId,
|
|
829
|
+
suggestion,
|
|
830
|
+
success: false,
|
|
831
|
+
pending: true,
|
|
832
|
+
timestamp: Date.now()
|
|
833
|
+
};
|
|
834
|
+
setEditHistory((prev) => [...prev, pendingEdit]);
|
|
611
835
|
setIsLoading(true);
|
|
612
836
|
try {
|
|
613
837
|
const res = await fetch("/api/ai-editor/edit", {
|
|
@@ -626,56 +850,118 @@ function AIEditorProvider({
|
|
|
626
850
|
editHistory: editHistory.map((item) => ({
|
|
627
851
|
suggestion: item.suggestion,
|
|
628
852
|
success: item.success
|
|
629
|
-
}))
|
|
853
|
+
})),
|
|
854
|
+
// Pass parent instance if available
|
|
855
|
+
parentInstance
|
|
630
856
|
})
|
|
631
857
|
});
|
|
632
858
|
const result = await res.json();
|
|
633
|
-
const
|
|
634
|
-
const newHistoryItem = {
|
|
859
|
+
const completedEdit = {
|
|
635
860
|
id: editId,
|
|
636
861
|
suggestion,
|
|
637
862
|
success: result.success,
|
|
863
|
+
pending: false,
|
|
638
864
|
error: result.error,
|
|
639
865
|
timestamp: Date.now(),
|
|
640
866
|
fileSnapshot: result.fileSnapshot,
|
|
867
|
+
editedFilePath: result.editedFile,
|
|
868
|
+
// Track which file was actually edited
|
|
641
869
|
generatedCode: result.generatedCode,
|
|
642
870
|
modifiedLines: result.modifiedLines
|
|
643
871
|
};
|
|
644
|
-
setEditHistory(
|
|
872
|
+
setEditHistory(
|
|
873
|
+
(prev) => prev.map((item) => item.id === editId ? completedEdit : item)
|
|
874
|
+
);
|
|
645
875
|
if (ENABLE_SESSION_PERSISTENCE && selectedSource && result.fileSnapshot) ;
|
|
646
876
|
return result;
|
|
647
877
|
} catch (err) {
|
|
648
878
|
const error = String(err);
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
suggestion,
|
|
655
|
-
success: false,
|
|
656
|
-
error,
|
|
657
|
-
timestamp: Date.now()
|
|
658
|
-
}
|
|
659
|
-
]);
|
|
879
|
+
setEditHistory(
|
|
880
|
+
(prev) => prev.map(
|
|
881
|
+
(item) => item.id === editId ? { ...item, success: false, pending: false, error } : item
|
|
882
|
+
)
|
|
883
|
+
);
|
|
660
884
|
return { success: false, error };
|
|
661
885
|
} finally {
|
|
662
886
|
setIsLoading(false);
|
|
663
887
|
}
|
|
664
888
|
},
|
|
665
|
-
[selectedSource, editHistory]
|
|
889
|
+
[selectedSource, editHistory, parentInstance]
|
|
890
|
+
);
|
|
891
|
+
const fetchSuggestions = react.useCallback(
|
|
892
|
+
async (source, history, lastEdit, excludedSuggestions) => {
|
|
893
|
+
var _a, _b, _c, _d;
|
|
894
|
+
const CACHE_TTL = 3e4;
|
|
895
|
+
const cacheKey = `${source.componentName}:${((_a = source.elementContext) == null ? void 0 : _a.tagName) || "div"}:${lastEdit || "initial"}`;
|
|
896
|
+
const cached = !(excludedSuggestions == null ? void 0 : excludedSuggestions.length) ? suggestionsCache.get(cacheKey) : null;
|
|
897
|
+
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
|
898
|
+
setSuggestions(cached.suggestions);
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
setSuggestionsLoading(true);
|
|
902
|
+
try {
|
|
903
|
+
const params = new URLSearchParams({
|
|
904
|
+
componentName: source.componentName
|
|
905
|
+
});
|
|
906
|
+
if ((_b = source.elementContext) == null ? void 0 : _b.tagName) {
|
|
907
|
+
params.append("elementTag", source.elementContext.tagName);
|
|
908
|
+
}
|
|
909
|
+
if ((_c = source.elementContext) == null ? void 0 : _c.className) {
|
|
910
|
+
params.append("className", source.elementContext.className);
|
|
911
|
+
}
|
|
912
|
+
if ((_d = source.elementContext) == null ? void 0 : _d.textContent) {
|
|
913
|
+
params.append("textContent", source.elementContext.textContent);
|
|
914
|
+
}
|
|
915
|
+
if (lastEdit) {
|
|
916
|
+
params.append("lastSuggestion", lastEdit);
|
|
917
|
+
}
|
|
918
|
+
if (history.length > 0) {
|
|
919
|
+
params.append(
|
|
920
|
+
"editHistory",
|
|
921
|
+
JSON.stringify(
|
|
922
|
+
history.slice(-3).map((h) => ({ suggestion: h.suggestion, success: h.success }))
|
|
923
|
+
)
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
if (excludedSuggestions && excludedSuggestions.length > 0) {
|
|
927
|
+
params.append("excludedSuggestions", JSON.stringify(excludedSuggestions));
|
|
928
|
+
}
|
|
929
|
+
const response = await fetch(`/api/ai-editor/suggestions?${params}`);
|
|
930
|
+
const data = await response.json();
|
|
931
|
+
if (data.success) {
|
|
932
|
+
setSuggestions(data.suggestions);
|
|
933
|
+
setSuggestionsCache((prev) => {
|
|
934
|
+
const newCache = new Map(prev);
|
|
935
|
+
newCache.set(cacheKey, {
|
|
936
|
+
suggestions: data.suggestions,
|
|
937
|
+
timestamp: Date.now()
|
|
938
|
+
});
|
|
939
|
+
return newCache;
|
|
940
|
+
});
|
|
941
|
+
} else {
|
|
942
|
+
setSuggestions([]);
|
|
943
|
+
}
|
|
944
|
+
} catch (error) {
|
|
945
|
+
setSuggestions([]);
|
|
946
|
+
} finally {
|
|
947
|
+
setSuggestionsLoading(false);
|
|
948
|
+
}
|
|
949
|
+
},
|
|
950
|
+
[suggestionsCache, setSuggestions, setSuggestionsLoading, setSuggestionsCache]
|
|
666
951
|
);
|
|
667
952
|
const handleSelect = react.useCallback(
|
|
668
953
|
(element, source) => {
|
|
669
954
|
setSelectedDOMElement(element);
|
|
670
955
|
setSelectedSource(source);
|
|
671
956
|
setEditHistory([]);
|
|
957
|
+
setParentInstance(null);
|
|
672
958
|
resolveSourceLocation(source).then((resolved) => {
|
|
673
959
|
if (resolved) {
|
|
674
960
|
setSelectedSource((prev) => prev === source ? resolved : prev);
|
|
675
961
|
}
|
|
676
962
|
});
|
|
677
963
|
},
|
|
678
|
-
[setSelectedDOMElement, setSelectedSource]
|
|
964
|
+
[setSelectedDOMElement, setSelectedSource, setParentInstance]
|
|
679
965
|
);
|
|
680
966
|
if (process.env.NODE_ENV !== "development") {
|
|
681
967
|
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
|
|
@@ -693,7 +979,12 @@ function AIEditorProvider({
|
|
|
693
979
|
setIsLoading,
|
|
694
980
|
editHistory,
|
|
695
981
|
setEditHistory,
|
|
696
|
-
submitEdit
|
|
982
|
+
submitEdit,
|
|
983
|
+
suggestions,
|
|
984
|
+
suggestionsLoading,
|
|
985
|
+
lastAppliedSuggestion,
|
|
986
|
+
setLastAppliedSuggestion,
|
|
987
|
+
fetchSuggestions
|
|
697
988
|
},
|
|
698
989
|
children: [
|
|
699
990
|
children,
|
|
@@ -709,7 +1000,9 @@ function AIEditorProvider({
|
|
|
709
1000
|
setStaleSession,
|
|
710
1001
|
setHasActiveSession,
|
|
711
1002
|
restoreSession,
|
|
712
|
-
handleClearSession
|
|
1003
|
+
handleClearSession,
|
|
1004
|
+
parentInstance,
|
|
1005
|
+
setParentInstance
|
|
713
1006
|
}
|
|
714
1007
|
)
|
|
715
1008
|
]
|
|
@@ -726,7 +1019,9 @@ function EditorOverlay({
|
|
|
726
1019
|
setStaleSession,
|
|
727
1020
|
setHasActiveSession,
|
|
728
1021
|
restoreSession,
|
|
729
|
-
handleClearSession
|
|
1022
|
+
handleClearSession,
|
|
1023
|
+
parentInstance,
|
|
1024
|
+
setParentInstance
|
|
730
1025
|
}) {
|
|
731
1026
|
var _a;
|
|
732
1027
|
const {
|
|
@@ -738,7 +1033,12 @@ function EditorOverlay({
|
|
|
738
1033
|
setEnabled,
|
|
739
1034
|
selectedDOMElement,
|
|
740
1035
|
editHistory,
|
|
741
|
-
setEditHistory
|
|
1036
|
+
setEditHistory,
|
|
1037
|
+
suggestions,
|
|
1038
|
+
suggestionsLoading,
|
|
1039
|
+
lastAppliedSuggestion,
|
|
1040
|
+
setLastAppliedSuggestion,
|
|
1041
|
+
fetchSuggestions
|
|
742
1042
|
} = useAIEditor();
|
|
743
1043
|
const [hoveredSource, setHoveredSource] = react.useState(
|
|
744
1044
|
null
|
|
@@ -757,43 +1057,34 @@ function EditorOverlay({
|
|
|
757
1057
|
const [parsedComponentName, setParsedComponentName] = react.useState(
|
|
758
1058
|
null
|
|
759
1059
|
);
|
|
1060
|
+
const [isSourcePreviewExpanded, setIsSourcePreviewExpanded] = react.useState(false);
|
|
1061
|
+
const [isParentPreviewExpanded, setIsParentPreviewExpanded] = react.useState(false);
|
|
1062
|
+
const [isEditHistoryExpanded, setIsEditHistoryExpanded] = react.useState(true);
|
|
760
1063
|
const isDark = theme === "dark";
|
|
1064
|
+
const refreshCodePreview = react.useCallback(async (source, options = {}) => {
|
|
1065
|
+
const params = buildReadQueryParams(source, options.includeParent ?? true);
|
|
1066
|
+
try {
|
|
1067
|
+
const url = `/api/ai-editor/read?` + new URLSearchParams(params);
|
|
1068
|
+
const response = await fetch(url);
|
|
1069
|
+
const data = await response.json();
|
|
1070
|
+
if (data.success) {
|
|
1071
|
+
setCode(data.content);
|
|
1072
|
+
setCodeLineStart(data.lineStart || 1);
|
|
1073
|
+
setTargetStartLine(data.targetStartLine || null);
|
|
1074
|
+
setTargetEndLine(data.targetEndLine || null);
|
|
1075
|
+
setParentInstance(data.parentInstance || null);
|
|
1076
|
+
if (data.componentName) {
|
|
1077
|
+
setParsedComponentName(data.componentName);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
} catch (error) {
|
|
1081
|
+
console.error("Error refreshing code preview:", error);
|
|
1082
|
+
}
|
|
1083
|
+
}, []);
|
|
761
1084
|
react.useEffect(() => {
|
|
762
|
-
var _a2, _b, _c, _d;
|
|
763
1085
|
if (selectedSource) {
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
line: String(selectedSource.lineNumber),
|
|
767
|
-
tagName: ((_a2 = selectedSource.elementContext) == null ? void 0 : _a2.tagName) || "",
|
|
768
|
-
nthOfType: String(((_b = selectedSource.elementContext) == null ? void 0 : _b.nthOfType) || 0),
|
|
769
|
-
textContent: ((_c = selectedSource.elementContext) == null ? void 0 : _c.textContent) || "",
|
|
770
|
-
className: ((_d = selectedSource.elementContext) == null ? void 0 : _d.className) || ""
|
|
771
|
-
};
|
|
772
|
-
if (selectedSource.debugStack) {
|
|
773
|
-
params.debugStack = selectedSource.debugStack;
|
|
774
|
-
}
|
|
775
|
-
fetch(`/api/ai-editor/read?` + new URLSearchParams(params)).then((r) => r.json()).then((d) => {
|
|
776
|
-
if (d.success) {
|
|
777
|
-
console.log("[AI Editor] Read response:", {
|
|
778
|
-
lineStart: d.lineStart,
|
|
779
|
-
targetStartLine: d.targetStartLine,
|
|
780
|
-
targetEndLine: d.targetEndLine,
|
|
781
|
-
componentName: d.componentName
|
|
782
|
-
});
|
|
783
|
-
setCode(d.content);
|
|
784
|
-
setCodeLineStart(d.lineStart || 1);
|
|
785
|
-
setTargetStartLine(d.targetStartLine || null);
|
|
786
|
-
setTargetEndLine(d.targetEndLine || null);
|
|
787
|
-
if (d.componentName) {
|
|
788
|
-
setParsedComponentName(d.componentName);
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
}).catch(() => {
|
|
792
|
-
setCode("// Could not load");
|
|
793
|
-
setCodeLineStart(1);
|
|
794
|
-
setTargetStartLine(null);
|
|
795
|
-
setTargetEndLine(null);
|
|
796
|
-
});
|
|
1086
|
+
refreshCodePreview(selectedSource);
|
|
1087
|
+
fetchSuggestions(selectedSource, []);
|
|
797
1088
|
fetch(
|
|
798
1089
|
`/api/ai-editor/absolute-path?` + new URLSearchParams({ path: selectedSource.filePath })
|
|
799
1090
|
).then((r) => r.json()).then((d) => d.success && setAbsolutePath(d.absolutePath)).catch(() => setAbsolutePath(null));
|
|
@@ -801,7 +1092,7 @@ function EditorOverlay({
|
|
|
801
1092
|
setAbsolutePath(null);
|
|
802
1093
|
setParsedComponentName(null);
|
|
803
1094
|
}
|
|
804
|
-
}, [selectedSource]);
|
|
1095
|
+
}, [selectedSource, refreshCodePreview]);
|
|
805
1096
|
react.useEffect(() => {
|
|
806
1097
|
let lastEl = null;
|
|
807
1098
|
let raf;
|
|
@@ -872,54 +1163,38 @@ function EditorOverlay({
|
|
|
872
1163
|
};
|
|
873
1164
|
}, [hoveredSource, hoveredElement, selectedSource, onSelect]);
|
|
874
1165
|
const handleSubmit = async () => {
|
|
875
|
-
var _a2, _b, _c, _d;
|
|
876
1166
|
if (!suggestion.trim()) return;
|
|
1167
|
+
const appliedSuggestion = suggestion;
|
|
877
1168
|
const res = await submitEdit(suggestion);
|
|
878
1169
|
setResult(res);
|
|
879
1170
|
if (res.success) {
|
|
880
1171
|
setSuggestion("");
|
|
1172
|
+
setLastAppliedSuggestion(appliedSuggestion);
|
|
881
1173
|
if (selectedSource) {
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
nthOfType: String(((_b = selectedSource.elementContext) == null ? void 0 : _b.nthOfType) || 0),
|
|
887
|
-
textContent: ((_c = selectedSource.elementContext) == null ? void 0 : _c.textContent) || "",
|
|
888
|
-
className: ((_d = selectedSource.elementContext) == null ? void 0 : _d.className) || ""
|
|
889
|
-
};
|
|
890
|
-
if (selectedSource.debugStack) {
|
|
891
|
-
params.debugStack = selectedSource.debugStack;
|
|
892
|
-
}
|
|
893
|
-
fetch(`/api/ai-editor/read?` + new URLSearchParams(params)).then((r) => r.json()).then((d) => {
|
|
894
|
-
if (d.success) {
|
|
895
|
-
setCode(d.content);
|
|
896
|
-
setCodeLineStart(d.lineStart || 1);
|
|
897
|
-
setTargetStartLine(d.targetStartLine || null);
|
|
898
|
-
setTargetEndLine(d.targetEndLine || null);
|
|
899
|
-
if (d.componentName) {
|
|
900
|
-
setParsedComponentName(d.componentName);
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
}).catch(console.error);
|
|
1174
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1175
|
+
await refreshCodePreview(selectedSource);
|
|
1176
|
+
console.log("[AI Editor] Fetching suggestions after edit:", appliedSuggestion);
|
|
1177
|
+
fetchSuggestions(selectedSource, editHistory, appliedSuggestion);
|
|
904
1178
|
}
|
|
905
1179
|
setTimeout(() => setResult(null), 3e3);
|
|
906
1180
|
}
|
|
907
1181
|
};
|
|
908
1182
|
const handleUndo = async () => {
|
|
909
|
-
var _a2, _b, _c, _d;
|
|
910
1183
|
if (editHistory.length === 0 || !selectedSource) return;
|
|
911
1184
|
const lastSuccessfulEdit = [...editHistory].reverse().find((edit) => edit.success && edit.fileSnapshot);
|
|
912
1185
|
if (!lastSuccessfulEdit) {
|
|
913
1186
|
console.warn("No successful edit with snapshot found to undo");
|
|
914
1187
|
return;
|
|
915
1188
|
}
|
|
1189
|
+
const fileToRestore = lastSuccessfulEdit.editedFilePath || selectedSource.filePath;
|
|
1190
|
+
console.log(`[Undo] Restoring file: ${fileToRestore}`);
|
|
916
1191
|
setIsLoading(true);
|
|
917
1192
|
try {
|
|
918
1193
|
const response = await fetch("/api/ai-editor/undo", {
|
|
919
1194
|
method: "POST",
|
|
920
1195
|
headers: { "Content-Type": "application/json" },
|
|
921
1196
|
body: JSON.stringify({
|
|
922
|
-
filePath:
|
|
1197
|
+
filePath: fileToRestore,
|
|
923
1198
|
content: lastSuccessfulEdit.fileSnapshot
|
|
924
1199
|
})
|
|
925
1200
|
});
|
|
@@ -931,28 +1206,7 @@ function EditorOverlay({
|
|
|
931
1206
|
const lastIndex = prev.lastIndexOf(lastSuccessfulEdit);
|
|
932
1207
|
return prev.filter((_, idx) => idx !== lastIndex);
|
|
933
1208
|
});
|
|
934
|
-
|
|
935
|
-
path: selectedSource.filePath,
|
|
936
|
-
line: String(selectedSource.lineNumber),
|
|
937
|
-
tagName: ((_a2 = selectedSource.elementContext) == null ? void 0 : _a2.tagName) || "",
|
|
938
|
-
nthOfType: String(((_b = selectedSource.elementContext) == null ? void 0 : _b.nthOfType) || 0),
|
|
939
|
-
textContent: ((_c = selectedSource.elementContext) == null ? void 0 : _c.textContent) || "",
|
|
940
|
-
className: ((_d = selectedSource.elementContext) == null ? void 0 : _d.className) || ""
|
|
941
|
-
};
|
|
942
|
-
if (selectedSource.debugStack) {
|
|
943
|
-
params.debugStack = selectedSource.debugStack;
|
|
944
|
-
}
|
|
945
|
-
fetch(`/api/ai-editor/read?` + new URLSearchParams(params)).then((r) => r.json()).then((d) => {
|
|
946
|
-
if (d.success) {
|
|
947
|
-
setCode(d.content);
|
|
948
|
-
setCodeLineStart(d.lineStart || 1);
|
|
949
|
-
setTargetStartLine(d.targetStartLine || null);
|
|
950
|
-
setTargetEndLine(d.targetEndLine || null);
|
|
951
|
-
if (d.componentName) {
|
|
952
|
-
setParsedComponentName(d.componentName);
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
}).catch(console.error);
|
|
1209
|
+
await refreshCodePreview(selectedSource);
|
|
956
1210
|
setResult({ success: true, error: void 0 });
|
|
957
1211
|
setTimeout(() => setResult(null), 3e3);
|
|
958
1212
|
} catch (error) {
|
|
@@ -963,50 +1217,30 @@ function EditorOverlay({
|
|
|
963
1217
|
}
|
|
964
1218
|
};
|
|
965
1219
|
const handleRetry = async (editIndex) => {
|
|
966
|
-
var _a2, _b, _c, _d;
|
|
967
1220
|
const editToRetry = editHistory[editIndex];
|
|
968
1221
|
if (!editToRetry) return;
|
|
969
1222
|
const res = await submitEdit(editToRetry.suggestion);
|
|
970
1223
|
if (res.success && selectedSource) {
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
line: String(selectedSource.lineNumber),
|
|
974
|
-
tagName: ((_a2 = selectedSource.elementContext) == null ? void 0 : _a2.tagName) || "",
|
|
975
|
-
nthOfType: String(((_b = selectedSource.elementContext) == null ? void 0 : _b.nthOfType) || 0),
|
|
976
|
-
textContent: ((_c = selectedSource.elementContext) == null ? void 0 : _c.textContent) || "",
|
|
977
|
-
className: ((_d = selectedSource.elementContext) == null ? void 0 : _d.className) || ""
|
|
978
|
-
};
|
|
979
|
-
if (selectedSource.debugStack) {
|
|
980
|
-
params.debugStack = selectedSource.debugStack;
|
|
981
|
-
}
|
|
982
|
-
fetch(`/api/ai-editor/read?` + new URLSearchParams(params)).then((r) => r.json()).then((d) => {
|
|
983
|
-
if (d.success) {
|
|
984
|
-
setCode(d.content);
|
|
985
|
-
setCodeLineStart(d.lineStart || 1);
|
|
986
|
-
setTargetStartLine(d.targetStartLine || null);
|
|
987
|
-
setTargetEndLine(d.targetEndLine || null);
|
|
988
|
-
if (d.componentName) {
|
|
989
|
-
setParsedComponentName(d.componentName);
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
}).catch(console.error);
|
|
1224
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1225
|
+
await refreshCodePreview(selectedSource, { includeParent: false });
|
|
993
1226
|
}
|
|
994
1227
|
};
|
|
995
1228
|
const handleUndoAndRetry = async () => {
|
|
996
|
-
var _a2, _b, _c, _d;
|
|
997
1229
|
if (editHistory.length === 0 || !selectedSource) return;
|
|
998
1230
|
const lastSuccessfulEdit = [...editHistory].reverse().find((edit) => edit.success && edit.fileSnapshot);
|
|
999
1231
|
if (!lastSuccessfulEdit) {
|
|
1000
1232
|
console.warn("No successful edit with snapshot found to undo and retry");
|
|
1001
1233
|
return;
|
|
1002
1234
|
}
|
|
1235
|
+
const fileToRestore = lastSuccessfulEdit.editedFilePath || selectedSource.filePath;
|
|
1236
|
+
console.log(`[Undo & Retry] Restoring file: ${fileToRestore}`);
|
|
1003
1237
|
setIsLoading(true);
|
|
1004
1238
|
try {
|
|
1005
1239
|
const undoResponse = await fetch("/api/ai-editor/undo", {
|
|
1006
1240
|
method: "POST",
|
|
1007
1241
|
headers: { "Content-Type": "application/json" },
|
|
1008
1242
|
body: JSON.stringify({
|
|
1009
|
-
filePath:
|
|
1243
|
+
filePath: fileToRestore,
|
|
1010
1244
|
content: lastSuccessfulEdit.fileSnapshot
|
|
1011
1245
|
})
|
|
1012
1246
|
});
|
|
@@ -1018,42 +1252,12 @@ function EditorOverlay({
|
|
|
1018
1252
|
const lastIndex = prev.lastIndexOf(lastSuccessfulEdit);
|
|
1019
1253
|
return prev.filter((_, idx) => idx !== lastIndex);
|
|
1020
1254
|
});
|
|
1021
|
-
|
|
1022
|
-
path: selectedSource.filePath,
|
|
1023
|
-
line: String(selectedSource.lineNumber),
|
|
1024
|
-
tagName: ((_a2 = selectedSource.elementContext) == null ? void 0 : _a2.tagName) || "",
|
|
1025
|
-
nthOfType: String(((_b = selectedSource.elementContext) == null ? void 0 : _b.nthOfType) || 0),
|
|
1026
|
-
textContent: ((_c = selectedSource.elementContext) == null ? void 0 : _c.textContent) || "",
|
|
1027
|
-
className: ((_d = selectedSource.elementContext) == null ? void 0 : _d.className) || ""
|
|
1028
|
-
};
|
|
1029
|
-
if (selectedSource.debugStack) {
|
|
1030
|
-
params.debugStack = selectedSource.debugStack;
|
|
1031
|
-
}
|
|
1032
|
-
await fetch(`/api/ai-editor/read?` + new URLSearchParams(params)).then((r) => r.json()).then((d) => {
|
|
1033
|
-
if (d.success) {
|
|
1034
|
-
setCode(d.content);
|
|
1035
|
-
setCodeLineStart(d.lineStart || 1);
|
|
1036
|
-
setTargetStartLine(d.targetStartLine || null);
|
|
1037
|
-
setTargetEndLine(d.targetEndLine || null);
|
|
1038
|
-
if (d.componentName) {
|
|
1039
|
-
setParsedComponentName(d.componentName);
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
}).catch(console.error);
|
|
1255
|
+
await refreshCodePreview(selectedSource);
|
|
1043
1256
|
setIsLoading(true);
|
|
1044
1257
|
const retryResult = await submitEdit(lastSuccessfulEdit.suggestion);
|
|
1045
1258
|
if (retryResult.success && selectedSource) {
|
|
1046
|
-
await
|
|
1047
|
-
|
|
1048
|
-
setCode(d.content);
|
|
1049
|
-
setCodeLineStart(d.lineStart || 1);
|
|
1050
|
-
setTargetStartLine(d.targetStartLine || null);
|
|
1051
|
-
setTargetEndLine(d.targetEndLine || null);
|
|
1052
|
-
if (d.componentName) {
|
|
1053
|
-
setParsedComponentName(d.componentName);
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
}).catch(console.error);
|
|
1259
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1260
|
+
await refreshCodePreview(selectedSource);
|
|
1057
1261
|
}
|
|
1058
1262
|
if (retryResult.success) {
|
|
1059
1263
|
setResult({ success: true, error: void 0 });
|
|
@@ -1088,6 +1292,12 @@ function EditorOverlay({
|
|
|
1088
1292
|
className: "ai-editor-ui",
|
|
1089
1293
|
style: { fontFamily: "system-ui, sans-serif" },
|
|
1090
1294
|
children: [
|
|
1295
|
+
/* @__PURE__ */ jsxRuntime.jsx("style", { children: `
|
|
1296
|
+
@keyframes ai-editor-spin {
|
|
1297
|
+
from { transform: rotate(0deg); }
|
|
1298
|
+
to { transform: rotate(360deg); }
|
|
1299
|
+
}
|
|
1300
|
+
` }),
|
|
1091
1301
|
hoveredRect && !selectedSource && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1092
1302
|
"div",
|
|
1093
1303
|
{
|
|
@@ -1293,20 +1503,31 @@ function EditorOverlay({
|
|
|
1293
1503
|
}
|
|
1294
1504
|
),
|
|
1295
1505
|
code && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 20 }, children: [
|
|
1296
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1297
|
-
"
|
|
1506
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1507
|
+
"button",
|
|
1298
1508
|
{
|
|
1509
|
+
onClick: () => setIsSourcePreviewExpanded(!isSourcePreviewExpanded),
|
|
1299
1510
|
style: {
|
|
1300
1511
|
fontSize: 11,
|
|
1301
1512
|
fontWeight: 600,
|
|
1302
1513
|
marginBottom: 8,
|
|
1303
1514
|
color: c.muted,
|
|
1304
|
-
textTransform: "uppercase"
|
|
1515
|
+
textTransform: "uppercase",
|
|
1516
|
+
background: "transparent",
|
|
1517
|
+
border: "none",
|
|
1518
|
+
padding: 0,
|
|
1519
|
+
cursor: "pointer",
|
|
1520
|
+
display: "flex",
|
|
1521
|
+
alignItems: "center",
|
|
1522
|
+
gap: 6
|
|
1305
1523
|
},
|
|
1306
|
-
children:
|
|
1524
|
+
children: [
|
|
1525
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 10 }, children: isSourcePreviewExpanded ? "▼" : "▶" }),
|
|
1526
|
+
"Source Preview"
|
|
1527
|
+
]
|
|
1307
1528
|
}
|
|
1308
1529
|
),
|
|
1309
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1530
|
+
isSourcePreviewExpanded && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1310
1531
|
reactSyntaxHighlighter.Prism,
|
|
1311
1532
|
{
|
|
1312
1533
|
language: "tsx",
|
|
@@ -1342,25 +1563,99 @@ function EditorOverlay({
|
|
|
1342
1563
|
}
|
|
1343
1564
|
)
|
|
1344
1565
|
] }),
|
|
1566
|
+
parentInstance && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 20 }, children: [
|
|
1567
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1568
|
+
"button",
|
|
1569
|
+
{
|
|
1570
|
+
onClick: () => setIsParentPreviewExpanded(!isParentPreviewExpanded),
|
|
1571
|
+
style: {
|
|
1572
|
+
fontSize: 11,
|
|
1573
|
+
fontWeight: 600,
|
|
1574
|
+
marginBottom: 8,
|
|
1575
|
+
color: c.muted,
|
|
1576
|
+
textTransform: "uppercase",
|
|
1577
|
+
background: "transparent",
|
|
1578
|
+
border: "none",
|
|
1579
|
+
padding: 0,
|
|
1580
|
+
cursor: "pointer",
|
|
1581
|
+
display: "flex",
|
|
1582
|
+
alignItems: "center",
|
|
1583
|
+
gap: 6
|
|
1584
|
+
},
|
|
1585
|
+
children: [
|
|
1586
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 10 }, children: isParentPreviewExpanded ? "▼" : "▶" }),
|
|
1587
|
+
"Used In: ",
|
|
1588
|
+
parentInstance.componentName,
|
|
1589
|
+
" (",
|
|
1590
|
+
parentInstance.filePath,
|
|
1591
|
+
")"
|
|
1592
|
+
]
|
|
1593
|
+
}
|
|
1594
|
+
),
|
|
1595
|
+
isParentPreviewExpanded && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1596
|
+
reactSyntaxHighlighter.Prism,
|
|
1597
|
+
{
|
|
1598
|
+
language: "tsx",
|
|
1599
|
+
style: isDark ? prism.vscDarkPlus : prism.vs,
|
|
1600
|
+
showLineNumbers: true,
|
|
1601
|
+
startingLineNumber: parentInstance.lineStart,
|
|
1602
|
+
customStyle: {
|
|
1603
|
+
background: c.bgAlt,
|
|
1604
|
+
borderRadius: 10,
|
|
1605
|
+
fontSize: 14,
|
|
1606
|
+
margin: 0,
|
|
1607
|
+
maxHeight: 180,
|
|
1608
|
+
overflowX: "auto",
|
|
1609
|
+
border: `1px solid ${c.border}`
|
|
1610
|
+
},
|
|
1611
|
+
lineNumberStyle: {
|
|
1612
|
+
minWidth: "2.5em",
|
|
1613
|
+
paddingRight: "1em",
|
|
1614
|
+
color: c.muted,
|
|
1615
|
+
textAlign: "right",
|
|
1616
|
+
userSelect: "none"
|
|
1617
|
+
},
|
|
1618
|
+
wrapLines: true,
|
|
1619
|
+
lineProps: (lineNumber) => {
|
|
1620
|
+
const isUsageLine = lineNumber >= parentInstance.usageLineStart && lineNumber <= parentInstance.usageLineEnd;
|
|
1621
|
+
return {
|
|
1622
|
+
style: {
|
|
1623
|
+
backgroundColor: isUsageLine ? isDark ? "rgba(255, 165, 0, 0.15)" : "rgba(255, 165, 0, 0.2)" : "transparent"
|
|
1624
|
+
}
|
|
1625
|
+
};
|
|
1626
|
+
},
|
|
1627
|
+
children: parentInstance.content
|
|
1628
|
+
}
|
|
1629
|
+
)
|
|
1630
|
+
] }),
|
|
1345
1631
|
editHistory.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 20 }, children: [
|
|
1346
1632
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1347
|
-
"
|
|
1633
|
+
"button",
|
|
1348
1634
|
{
|
|
1635
|
+
onClick: () => setIsEditHistoryExpanded(!isEditHistoryExpanded),
|
|
1349
1636
|
style: {
|
|
1350
1637
|
fontSize: 11,
|
|
1351
1638
|
fontWeight: 600,
|
|
1352
1639
|
marginBottom: 8,
|
|
1353
1640
|
color: c.muted,
|
|
1354
|
-
textTransform: "uppercase"
|
|
1641
|
+
textTransform: "uppercase",
|
|
1642
|
+
background: "transparent",
|
|
1643
|
+
border: "none",
|
|
1644
|
+
padding: 0,
|
|
1645
|
+
cursor: "pointer",
|
|
1646
|
+
display: "flex",
|
|
1647
|
+
alignItems: "center",
|
|
1648
|
+
gap: 6
|
|
1355
1649
|
},
|
|
1356
1650
|
children: [
|
|
1651
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 10 }, children: isEditHistoryExpanded ? "▼" : "▶" }),
|
|
1357
1652
|
"Edit History (",
|
|
1358
1653
|
editHistory.length,
|
|
1359
1654
|
")"
|
|
1360
1655
|
]
|
|
1361
1656
|
}
|
|
1362
1657
|
),
|
|
1363
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1658
|
+
isEditHistoryExpanded && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1364
1659
|
"div",
|
|
1365
1660
|
{
|
|
1366
1661
|
style: {
|
|
@@ -1400,7 +1695,19 @@ function EditorOverlay({
|
|
|
1400
1695
|
marginBottom: 4
|
|
1401
1696
|
},
|
|
1402
1697
|
children: [
|
|
1403
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1698
|
+
item.pending ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1699
|
+
"div",
|
|
1700
|
+
{
|
|
1701
|
+
style: {
|
|
1702
|
+
width: 16,
|
|
1703
|
+
height: 16,
|
|
1704
|
+
border: "2px solid transparent",
|
|
1705
|
+
borderTopColor: c.accent,
|
|
1706
|
+
borderRadius: "50%",
|
|
1707
|
+
animation: "ai-editor-spin 0.8s linear infinite"
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1404
1711
|
"span",
|
|
1405
1712
|
{
|
|
1406
1713
|
style: {
|
|
@@ -1451,7 +1758,7 @@ function EditorOverlay({
|
|
|
1451
1758
|
)
|
|
1452
1759
|
] }),
|
|
1453
1760
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 6, flexWrap: "wrap" }, children: [
|
|
1454
|
-
isLastEdit && hasSnapshot && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1761
|
+
isLastEdit && hasSnapshot && !item.pending && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1455
1762
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1456
1763
|
"button",
|
|
1457
1764
|
{
|
|
@@ -1513,25 +1820,25 @@ function EditorOverlay({
|
|
|
1513
1820
|
"button",
|
|
1514
1821
|
{
|
|
1515
1822
|
onClick: () => handleRetry(idx),
|
|
1516
|
-
disabled: isLoading,
|
|
1823
|
+
disabled: isLoading || item.pending,
|
|
1517
1824
|
style: {
|
|
1518
1825
|
padding: "4px 10px",
|
|
1519
1826
|
fontSize: 11,
|
|
1520
1827
|
borderRadius: 6,
|
|
1521
1828
|
border: "none",
|
|
1522
|
-
background: isLoading ? c.muted : c.accent,
|
|
1829
|
+
background: isLoading || item.pending ? c.muted : c.accent,
|
|
1523
1830
|
color: "#fff",
|
|
1524
1831
|
fontWeight: 600,
|
|
1525
|
-
cursor: isLoading ? "wait" : "pointer",
|
|
1832
|
+
cursor: isLoading || item.pending ? "wait" : "pointer",
|
|
1526
1833
|
whiteSpace: "nowrap"
|
|
1527
1834
|
},
|
|
1528
1835
|
title: "Retry this edit",
|
|
1529
1836
|
onMouseEnter: (e) => {
|
|
1530
|
-
if (!isLoading)
|
|
1837
|
+
if (!isLoading && !item.pending)
|
|
1531
1838
|
e.currentTarget.style.background = "#6366f1";
|
|
1532
1839
|
},
|
|
1533
1840
|
onMouseLeave: (e) => {
|
|
1534
|
-
if (!isLoading)
|
|
1841
|
+
if (!isLoading && !item.pending)
|
|
1535
1842
|
e.currentTarget.style.background = c.accent;
|
|
1536
1843
|
},
|
|
1537
1844
|
children: "↻ Retry"
|
|
@@ -1591,6 +1898,144 @@ function EditorOverlay({
|
|
|
1591
1898
|
}
|
|
1592
1899
|
)
|
|
1593
1900
|
] }),
|
|
1901
|
+
(suggestions.length > 0 || suggestionsLoading) && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 20 }, children: [
|
|
1902
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1903
|
+
"div",
|
|
1904
|
+
{
|
|
1905
|
+
style: {
|
|
1906
|
+
fontSize: 11,
|
|
1907
|
+
fontWeight: 600,
|
|
1908
|
+
marginBottom: 8,
|
|
1909
|
+
color: c.muted,
|
|
1910
|
+
textTransform: "uppercase",
|
|
1911
|
+
display: "flex",
|
|
1912
|
+
alignItems: "center",
|
|
1913
|
+
gap: 8
|
|
1914
|
+
},
|
|
1915
|
+
children: [
|
|
1916
|
+
"Quick Suggestions",
|
|
1917
|
+
suggestionsLoading ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1918
|
+
"div",
|
|
1919
|
+
{
|
|
1920
|
+
style: {
|
|
1921
|
+
width: 12,
|
|
1922
|
+
height: 12,
|
|
1923
|
+
border: "2px solid transparent",
|
|
1924
|
+
borderTopColor: c.accent,
|
|
1925
|
+
borderRadius: "50%",
|
|
1926
|
+
animation: "ai-editor-spin 0.8s linear infinite"
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
) : suggestions.length > 0 && selectedSource && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1930
|
+
"button",
|
|
1931
|
+
{
|
|
1932
|
+
onClick: () => {
|
|
1933
|
+
console.log("[AI Editor] Refreshing suggestions, excluding:", suggestions);
|
|
1934
|
+
fetchSuggestions(selectedSource, editHistory, lastAppliedSuggestion, suggestions);
|
|
1935
|
+
},
|
|
1936
|
+
disabled: isLoading,
|
|
1937
|
+
style: {
|
|
1938
|
+
background: "transparent",
|
|
1939
|
+
border: "none",
|
|
1940
|
+
padding: 4,
|
|
1941
|
+
cursor: isLoading ? "wait" : "pointer",
|
|
1942
|
+
color: c.muted,
|
|
1943
|
+
fontSize: 14,
|
|
1944
|
+
display: "flex",
|
|
1945
|
+
alignItems: "center",
|
|
1946
|
+
transition: "all 0.15s ease"
|
|
1947
|
+
},
|
|
1948
|
+
onMouseEnter: (e) => {
|
|
1949
|
+
if (!isLoading) {
|
|
1950
|
+
e.currentTarget.style.color = c.accent;
|
|
1951
|
+
e.currentTarget.style.transform = "rotate(180deg)";
|
|
1952
|
+
}
|
|
1953
|
+
},
|
|
1954
|
+
onMouseLeave: (e) => {
|
|
1955
|
+
if (!isLoading) {
|
|
1956
|
+
e.currentTarget.style.color = c.muted;
|
|
1957
|
+
e.currentTarget.style.transform = "rotate(0deg)";
|
|
1958
|
+
}
|
|
1959
|
+
},
|
|
1960
|
+
title: "Get different suggestions",
|
|
1961
|
+
children: "↻"
|
|
1962
|
+
}
|
|
1963
|
+
)
|
|
1964
|
+
]
|
|
1965
|
+
}
|
|
1966
|
+
),
|
|
1967
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1968
|
+
"div",
|
|
1969
|
+
{
|
|
1970
|
+
style: {
|
|
1971
|
+
display: "flex",
|
|
1972
|
+
flexWrap: "wrap",
|
|
1973
|
+
gap: 8
|
|
1974
|
+
},
|
|
1975
|
+
children: suggestionsLoading ? (
|
|
1976
|
+
// Loading skeletons
|
|
1977
|
+
Array.from({ length: 6 }).map((_, idx) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1978
|
+
"div",
|
|
1979
|
+
{
|
|
1980
|
+
style: {
|
|
1981
|
+
padding: "8px 14px",
|
|
1982
|
+
borderRadius: 8,
|
|
1983
|
+
background: c.bgAlt,
|
|
1984
|
+
border: `1px solid ${c.border}`,
|
|
1985
|
+
fontSize: 13,
|
|
1986
|
+
height: 34,
|
|
1987
|
+
width: Math.random() * 60 + 80,
|
|
1988
|
+
// Variable width
|
|
1989
|
+
opacity: 0.5
|
|
1990
|
+
}
|
|
1991
|
+
},
|
|
1992
|
+
idx
|
|
1993
|
+
))
|
|
1994
|
+
) : suggestions.map((suggestionText, idx) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1995
|
+
"button",
|
|
1996
|
+
{
|
|
1997
|
+
onClick: (e) => {
|
|
1998
|
+
if (e.metaKey || e.ctrlKey) {
|
|
1999
|
+
setSuggestion(suggestionText);
|
|
2000
|
+
setTimeout(() => handleSubmit(), 50);
|
|
2001
|
+
} else {
|
|
2002
|
+
setSuggestion(suggestionText);
|
|
2003
|
+
}
|
|
2004
|
+
},
|
|
2005
|
+
disabled: isLoading,
|
|
2006
|
+
style: {
|
|
2007
|
+
padding: "8px 14px",
|
|
2008
|
+
borderRadius: 8,
|
|
2009
|
+
border: `1px solid ${c.border}`,
|
|
2010
|
+
background: c.bgAlt,
|
|
2011
|
+
color: c.text,
|
|
2012
|
+
fontSize: 13,
|
|
2013
|
+
cursor: isLoading ? "wait" : "pointer",
|
|
2014
|
+
transition: "all 0.15s ease",
|
|
2015
|
+
whiteSpace: "nowrap"
|
|
2016
|
+
},
|
|
2017
|
+
onMouseEnter: (e) => {
|
|
2018
|
+
if (!isLoading) {
|
|
2019
|
+
e.currentTarget.style.background = c.accent + "20";
|
|
2020
|
+
e.currentTarget.style.borderColor = c.accent;
|
|
2021
|
+
e.currentTarget.style.transform = "translateY(-1px)";
|
|
2022
|
+
}
|
|
2023
|
+
},
|
|
2024
|
+
onMouseLeave: (e) => {
|
|
2025
|
+
if (!isLoading) {
|
|
2026
|
+
e.currentTarget.style.background = c.bgAlt;
|
|
2027
|
+
e.currentTarget.style.borderColor = c.border;
|
|
2028
|
+
e.currentTarget.style.transform = "translateY(0)";
|
|
2029
|
+
}
|
|
2030
|
+
},
|
|
2031
|
+
title: "Click to use • Cmd+Click to apply immediately",
|
|
2032
|
+
children: suggestionText
|
|
2033
|
+
},
|
|
2034
|
+
idx
|
|
2035
|
+
))
|
|
2036
|
+
}
|
|
2037
|
+
)
|
|
2038
|
+
] }),
|
|
1594
2039
|
result && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1595
2040
|
"div",
|
|
1596
2041
|
{
|
|
@@ -1719,4 +2164,4 @@ function EditorOverlay({
|
|
|
1719
2164
|
);
|
|
1720
2165
|
}
|
|
1721
2166
|
exports.AIEditorProvider = AIEditorProvider;
|
|
1722
|
-
//# sourceMappingURL=AIEditorProvider-
|
|
2167
|
+
//# sourceMappingURL=AIEditorProvider-BGHm2xyU.cjs.map
|