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