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
|
@@ -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,118 @@ 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
|
+
const cached = !(excludedSuggestions == null ? void 0 : excludedSuggestions.length) ? suggestionsCache.get(cacheKey) : null;
|
|
896
|
+
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
|
897
|
+
setSuggestions(cached.suggestions);
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
setSuggestionsLoading(true);
|
|
901
|
+
try {
|
|
902
|
+
const params = new URLSearchParams({
|
|
903
|
+
componentName: source.componentName
|
|
904
|
+
});
|
|
905
|
+
if ((_b = source.elementContext) == null ? void 0 : _b.tagName) {
|
|
906
|
+
params.append("elementTag", source.elementContext.tagName);
|
|
907
|
+
}
|
|
908
|
+
if ((_c = source.elementContext) == null ? void 0 : _c.className) {
|
|
909
|
+
params.append("className", source.elementContext.className);
|
|
910
|
+
}
|
|
911
|
+
if ((_d = source.elementContext) == null ? void 0 : _d.textContent) {
|
|
912
|
+
params.append("textContent", source.elementContext.textContent);
|
|
913
|
+
}
|
|
914
|
+
if (lastEdit) {
|
|
915
|
+
params.append("lastSuggestion", lastEdit);
|
|
916
|
+
}
|
|
917
|
+
if (history.length > 0) {
|
|
918
|
+
params.append(
|
|
919
|
+
"editHistory",
|
|
920
|
+
JSON.stringify(
|
|
921
|
+
history.slice(-3).map((h) => ({ suggestion: h.suggestion, success: h.success }))
|
|
922
|
+
)
|
|
923
|
+
);
|
|
924
|
+
}
|
|
925
|
+
if (excludedSuggestions && excludedSuggestions.length > 0) {
|
|
926
|
+
params.append("excludedSuggestions", JSON.stringify(excludedSuggestions));
|
|
927
|
+
}
|
|
928
|
+
const response = await fetch(`/api/ai-editor/suggestions?${params}`);
|
|
929
|
+
const data = await response.json();
|
|
930
|
+
if (data.success) {
|
|
931
|
+
setSuggestions(data.suggestions);
|
|
932
|
+
setSuggestionsCache((prev) => {
|
|
933
|
+
const newCache = new Map(prev);
|
|
934
|
+
newCache.set(cacheKey, {
|
|
935
|
+
suggestions: data.suggestions,
|
|
936
|
+
timestamp: Date.now()
|
|
937
|
+
});
|
|
938
|
+
return newCache;
|
|
939
|
+
});
|
|
940
|
+
} else {
|
|
941
|
+
setSuggestions([]);
|
|
942
|
+
}
|
|
943
|
+
} catch (error) {
|
|
944
|
+
setSuggestions([]);
|
|
945
|
+
} finally {
|
|
946
|
+
setSuggestionsLoading(false);
|
|
947
|
+
}
|
|
948
|
+
},
|
|
949
|
+
[suggestionsCache, setSuggestions, setSuggestionsLoading, setSuggestionsCache]
|
|
665
950
|
);
|
|
666
951
|
const handleSelect = useCallback(
|
|
667
952
|
(element, source) => {
|
|
668
953
|
setSelectedDOMElement(element);
|
|
669
954
|
setSelectedSource(source);
|
|
670
955
|
setEditHistory([]);
|
|
956
|
+
setParentInstance(null);
|
|
671
957
|
resolveSourceLocation(source).then((resolved) => {
|
|
672
958
|
if (resolved) {
|
|
673
959
|
setSelectedSource((prev) => prev === source ? resolved : prev);
|
|
674
960
|
}
|
|
675
961
|
});
|
|
676
962
|
},
|
|
677
|
-
[setSelectedDOMElement, setSelectedSource]
|
|
963
|
+
[setSelectedDOMElement, setSelectedSource, setParentInstance]
|
|
678
964
|
);
|
|
679
965
|
if (process.env.NODE_ENV !== "development") {
|
|
680
966
|
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
@@ -692,7 +978,12 @@ function AIEditorProvider({
|
|
|
692
978
|
setIsLoading,
|
|
693
979
|
editHistory,
|
|
694
980
|
setEditHistory,
|
|
695
|
-
submitEdit
|
|
981
|
+
submitEdit,
|
|
982
|
+
suggestions,
|
|
983
|
+
suggestionsLoading,
|
|
984
|
+
lastAppliedSuggestion,
|
|
985
|
+
setLastAppliedSuggestion,
|
|
986
|
+
fetchSuggestions
|
|
696
987
|
},
|
|
697
988
|
children: [
|
|
698
989
|
children,
|
|
@@ -708,7 +999,9 @@ function AIEditorProvider({
|
|
|
708
999
|
setStaleSession,
|
|
709
1000
|
setHasActiveSession,
|
|
710
1001
|
restoreSession,
|
|
711
|
-
handleClearSession
|
|
1002
|
+
handleClearSession,
|
|
1003
|
+
parentInstance,
|
|
1004
|
+
setParentInstance
|
|
712
1005
|
}
|
|
713
1006
|
)
|
|
714
1007
|
]
|
|
@@ -725,7 +1018,9 @@ function EditorOverlay({
|
|
|
725
1018
|
setStaleSession,
|
|
726
1019
|
setHasActiveSession,
|
|
727
1020
|
restoreSession,
|
|
728
|
-
handleClearSession
|
|
1021
|
+
handleClearSession,
|
|
1022
|
+
parentInstance,
|
|
1023
|
+
setParentInstance
|
|
729
1024
|
}) {
|
|
730
1025
|
var _a;
|
|
731
1026
|
const {
|
|
@@ -737,7 +1032,12 @@ function EditorOverlay({
|
|
|
737
1032
|
setEnabled,
|
|
738
1033
|
selectedDOMElement,
|
|
739
1034
|
editHistory,
|
|
740
|
-
setEditHistory
|
|
1035
|
+
setEditHistory,
|
|
1036
|
+
suggestions,
|
|
1037
|
+
suggestionsLoading,
|
|
1038
|
+
lastAppliedSuggestion,
|
|
1039
|
+
setLastAppliedSuggestion,
|
|
1040
|
+
fetchSuggestions
|
|
741
1041
|
} = useAIEditor();
|
|
742
1042
|
const [hoveredSource, setHoveredSource] = useState(
|
|
743
1043
|
null
|
|
@@ -756,43 +1056,34 @@ function EditorOverlay({
|
|
|
756
1056
|
const [parsedComponentName, setParsedComponentName] = useState(
|
|
757
1057
|
null
|
|
758
1058
|
);
|
|
1059
|
+
const [isSourcePreviewExpanded, setIsSourcePreviewExpanded] = useState(false);
|
|
1060
|
+
const [isParentPreviewExpanded, setIsParentPreviewExpanded] = useState(false);
|
|
1061
|
+
const [isEditHistoryExpanded, setIsEditHistoryExpanded] = useState(true);
|
|
759
1062
|
const isDark = theme === "dark";
|
|
1063
|
+
const refreshCodePreview = useCallback(async (source, options = {}) => {
|
|
1064
|
+
const params = buildReadQueryParams(source, options.includeParent ?? true);
|
|
1065
|
+
try {
|
|
1066
|
+
const url = `/api/ai-editor/read?` + new URLSearchParams(params);
|
|
1067
|
+
const response = await fetch(url);
|
|
1068
|
+
const data = await response.json();
|
|
1069
|
+
if (data.success) {
|
|
1070
|
+
setCode(data.content);
|
|
1071
|
+
setCodeLineStart(data.lineStart || 1);
|
|
1072
|
+
setTargetStartLine(data.targetStartLine || null);
|
|
1073
|
+
setTargetEndLine(data.targetEndLine || null);
|
|
1074
|
+
setParentInstance(data.parentInstance || null);
|
|
1075
|
+
if (data.componentName) {
|
|
1076
|
+
setParsedComponentName(data.componentName);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
} catch (error) {
|
|
1080
|
+
console.error("Error refreshing code preview:", error);
|
|
1081
|
+
}
|
|
1082
|
+
}, []);
|
|
760
1083
|
useEffect(() => {
|
|
761
|
-
var _a2, _b, _c, _d;
|
|
762
1084
|
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
|
-
});
|
|
1085
|
+
refreshCodePreview(selectedSource);
|
|
1086
|
+
fetchSuggestions(selectedSource, []);
|
|
796
1087
|
fetch(
|
|
797
1088
|
`/api/ai-editor/absolute-path?` + new URLSearchParams({ path: selectedSource.filePath })
|
|
798
1089
|
).then((r) => r.json()).then((d) => d.success && setAbsolutePath(d.absolutePath)).catch(() => setAbsolutePath(null));
|
|
@@ -800,7 +1091,7 @@ function EditorOverlay({
|
|
|
800
1091
|
setAbsolutePath(null);
|
|
801
1092
|
setParsedComponentName(null);
|
|
802
1093
|
}
|
|
803
|
-
}, [selectedSource]);
|
|
1094
|
+
}, [selectedSource, refreshCodePreview]);
|
|
804
1095
|
useEffect(() => {
|
|
805
1096
|
let lastEl = null;
|
|
806
1097
|
let raf;
|
|
@@ -871,54 +1162,38 @@ function EditorOverlay({
|
|
|
871
1162
|
};
|
|
872
1163
|
}, [hoveredSource, hoveredElement, selectedSource, onSelect]);
|
|
873
1164
|
const handleSubmit = async () => {
|
|
874
|
-
var _a2, _b, _c, _d;
|
|
875
1165
|
if (!suggestion.trim()) return;
|
|
1166
|
+
const appliedSuggestion = suggestion;
|
|
876
1167
|
const res = await submitEdit(suggestion);
|
|
877
1168
|
setResult(res);
|
|
878
1169
|
if (res.success) {
|
|
879
1170
|
setSuggestion("");
|
|
1171
|
+
setLastAppliedSuggestion(appliedSuggestion);
|
|
880
1172
|
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);
|
|
1173
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1174
|
+
await refreshCodePreview(selectedSource);
|
|
1175
|
+
console.log("[AI Editor] Fetching suggestions after edit:", appliedSuggestion);
|
|
1176
|
+
fetchSuggestions(selectedSource, editHistory, appliedSuggestion);
|
|
903
1177
|
}
|
|
904
1178
|
setTimeout(() => setResult(null), 3e3);
|
|
905
1179
|
}
|
|
906
1180
|
};
|
|
907
1181
|
const handleUndo = async () => {
|
|
908
|
-
var _a2, _b, _c, _d;
|
|
909
1182
|
if (editHistory.length === 0 || !selectedSource) return;
|
|
910
1183
|
const lastSuccessfulEdit = [...editHistory].reverse().find((edit) => edit.success && edit.fileSnapshot);
|
|
911
1184
|
if (!lastSuccessfulEdit) {
|
|
912
1185
|
console.warn("No successful edit with snapshot found to undo");
|
|
913
1186
|
return;
|
|
914
1187
|
}
|
|
1188
|
+
const fileToRestore = lastSuccessfulEdit.editedFilePath || selectedSource.filePath;
|
|
1189
|
+
console.log(`[Undo] Restoring file: ${fileToRestore}`);
|
|
915
1190
|
setIsLoading(true);
|
|
916
1191
|
try {
|
|
917
1192
|
const response = await fetch("/api/ai-editor/undo", {
|
|
918
1193
|
method: "POST",
|
|
919
1194
|
headers: { "Content-Type": "application/json" },
|
|
920
1195
|
body: JSON.stringify({
|
|
921
|
-
filePath:
|
|
1196
|
+
filePath: fileToRestore,
|
|
922
1197
|
content: lastSuccessfulEdit.fileSnapshot
|
|
923
1198
|
})
|
|
924
1199
|
});
|
|
@@ -930,28 +1205,7 @@ function EditorOverlay({
|
|
|
930
1205
|
const lastIndex = prev.lastIndexOf(lastSuccessfulEdit);
|
|
931
1206
|
return prev.filter((_, idx) => idx !== lastIndex);
|
|
932
1207
|
});
|
|
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);
|
|
1208
|
+
await refreshCodePreview(selectedSource);
|
|
955
1209
|
setResult({ success: true, error: void 0 });
|
|
956
1210
|
setTimeout(() => setResult(null), 3e3);
|
|
957
1211
|
} catch (error) {
|
|
@@ -962,50 +1216,30 @@ function EditorOverlay({
|
|
|
962
1216
|
}
|
|
963
1217
|
};
|
|
964
1218
|
const handleRetry = async (editIndex) => {
|
|
965
|
-
var _a2, _b, _c, _d;
|
|
966
1219
|
const editToRetry = editHistory[editIndex];
|
|
967
1220
|
if (!editToRetry) return;
|
|
968
1221
|
const res = await submitEdit(editToRetry.suggestion);
|
|
969
1222
|
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);
|
|
1223
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1224
|
+
await refreshCodePreview(selectedSource, { includeParent: false });
|
|
992
1225
|
}
|
|
993
1226
|
};
|
|
994
1227
|
const handleUndoAndRetry = async () => {
|
|
995
|
-
var _a2, _b, _c, _d;
|
|
996
1228
|
if (editHistory.length === 0 || !selectedSource) return;
|
|
997
1229
|
const lastSuccessfulEdit = [...editHistory].reverse().find((edit) => edit.success && edit.fileSnapshot);
|
|
998
1230
|
if (!lastSuccessfulEdit) {
|
|
999
1231
|
console.warn("No successful edit with snapshot found to undo and retry");
|
|
1000
1232
|
return;
|
|
1001
1233
|
}
|
|
1234
|
+
const fileToRestore = lastSuccessfulEdit.editedFilePath || selectedSource.filePath;
|
|
1235
|
+
console.log(`[Undo & Retry] Restoring file: ${fileToRestore}`);
|
|
1002
1236
|
setIsLoading(true);
|
|
1003
1237
|
try {
|
|
1004
1238
|
const undoResponse = await fetch("/api/ai-editor/undo", {
|
|
1005
1239
|
method: "POST",
|
|
1006
1240
|
headers: { "Content-Type": "application/json" },
|
|
1007
1241
|
body: JSON.stringify({
|
|
1008
|
-
filePath:
|
|
1242
|
+
filePath: fileToRestore,
|
|
1009
1243
|
content: lastSuccessfulEdit.fileSnapshot
|
|
1010
1244
|
})
|
|
1011
1245
|
});
|
|
@@ -1017,42 +1251,12 @@ function EditorOverlay({
|
|
|
1017
1251
|
const lastIndex = prev.lastIndexOf(lastSuccessfulEdit);
|
|
1018
1252
|
return prev.filter((_, idx) => idx !== lastIndex);
|
|
1019
1253
|
});
|
|
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);
|
|
1254
|
+
await refreshCodePreview(selectedSource);
|
|
1042
1255
|
setIsLoading(true);
|
|
1043
1256
|
const retryResult = await submitEdit(lastSuccessfulEdit.suggestion);
|
|
1044
1257
|
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);
|
|
1258
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1259
|
+
await refreshCodePreview(selectedSource);
|
|
1056
1260
|
}
|
|
1057
1261
|
if (retryResult.success) {
|
|
1058
1262
|
setResult({ success: true, error: void 0 });
|
|
@@ -1087,6 +1291,12 @@ function EditorOverlay({
|
|
|
1087
1291
|
className: "ai-editor-ui",
|
|
1088
1292
|
style: { fontFamily: "system-ui, sans-serif" },
|
|
1089
1293
|
children: [
|
|
1294
|
+
/* @__PURE__ */ jsx("style", { children: `
|
|
1295
|
+
@keyframes ai-editor-spin {
|
|
1296
|
+
from { transform: rotate(0deg); }
|
|
1297
|
+
to { transform: rotate(360deg); }
|
|
1298
|
+
}
|
|
1299
|
+
` }),
|
|
1090
1300
|
hoveredRect && !selectedSource && /* @__PURE__ */ jsx(
|
|
1091
1301
|
"div",
|
|
1092
1302
|
{
|
|
@@ -1292,20 +1502,31 @@ function EditorOverlay({
|
|
|
1292
1502
|
}
|
|
1293
1503
|
),
|
|
1294
1504
|
code && /* @__PURE__ */ jsxs("div", { style: { marginBottom: 20 }, children: [
|
|
1295
|
-
/* @__PURE__ */
|
|
1296
|
-
"
|
|
1505
|
+
/* @__PURE__ */ jsxs(
|
|
1506
|
+
"button",
|
|
1297
1507
|
{
|
|
1508
|
+
onClick: () => setIsSourcePreviewExpanded(!isSourcePreviewExpanded),
|
|
1298
1509
|
style: {
|
|
1299
1510
|
fontSize: 11,
|
|
1300
1511
|
fontWeight: 600,
|
|
1301
1512
|
marginBottom: 8,
|
|
1302
1513
|
color: c.muted,
|
|
1303
|
-
textTransform: "uppercase"
|
|
1514
|
+
textTransform: "uppercase",
|
|
1515
|
+
background: "transparent",
|
|
1516
|
+
border: "none",
|
|
1517
|
+
padding: 0,
|
|
1518
|
+
cursor: "pointer",
|
|
1519
|
+
display: "flex",
|
|
1520
|
+
alignItems: "center",
|
|
1521
|
+
gap: 6
|
|
1304
1522
|
},
|
|
1305
|
-
children:
|
|
1523
|
+
children: [
|
|
1524
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 10 }, children: isSourcePreviewExpanded ? "▼" : "▶" }),
|
|
1525
|
+
"Source Preview"
|
|
1526
|
+
]
|
|
1306
1527
|
}
|
|
1307
1528
|
),
|
|
1308
|
-
/* @__PURE__ */ jsx(
|
|
1529
|
+
isSourcePreviewExpanded && /* @__PURE__ */ jsx(
|
|
1309
1530
|
Prism,
|
|
1310
1531
|
{
|
|
1311
1532
|
language: "tsx",
|
|
@@ -1341,25 +1562,99 @@ function EditorOverlay({
|
|
|
1341
1562
|
}
|
|
1342
1563
|
)
|
|
1343
1564
|
] }),
|
|
1565
|
+
parentInstance && /* @__PURE__ */ jsxs("div", { style: { marginBottom: 20 }, children: [
|
|
1566
|
+
/* @__PURE__ */ jsxs(
|
|
1567
|
+
"button",
|
|
1568
|
+
{
|
|
1569
|
+
onClick: () => setIsParentPreviewExpanded(!isParentPreviewExpanded),
|
|
1570
|
+
style: {
|
|
1571
|
+
fontSize: 11,
|
|
1572
|
+
fontWeight: 600,
|
|
1573
|
+
marginBottom: 8,
|
|
1574
|
+
color: c.muted,
|
|
1575
|
+
textTransform: "uppercase",
|
|
1576
|
+
background: "transparent",
|
|
1577
|
+
border: "none",
|
|
1578
|
+
padding: 0,
|
|
1579
|
+
cursor: "pointer",
|
|
1580
|
+
display: "flex",
|
|
1581
|
+
alignItems: "center",
|
|
1582
|
+
gap: 6
|
|
1583
|
+
},
|
|
1584
|
+
children: [
|
|
1585
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 10 }, children: isParentPreviewExpanded ? "▼" : "▶" }),
|
|
1586
|
+
"Used In: ",
|
|
1587
|
+
parentInstance.componentName,
|
|
1588
|
+
" (",
|
|
1589
|
+
parentInstance.filePath,
|
|
1590
|
+
")"
|
|
1591
|
+
]
|
|
1592
|
+
}
|
|
1593
|
+
),
|
|
1594
|
+
isParentPreviewExpanded && /* @__PURE__ */ jsx(
|
|
1595
|
+
Prism,
|
|
1596
|
+
{
|
|
1597
|
+
language: "tsx",
|
|
1598
|
+
style: isDark ? vscDarkPlus : vs,
|
|
1599
|
+
showLineNumbers: true,
|
|
1600
|
+
startingLineNumber: parentInstance.lineStart,
|
|
1601
|
+
customStyle: {
|
|
1602
|
+
background: c.bgAlt,
|
|
1603
|
+
borderRadius: 10,
|
|
1604
|
+
fontSize: 14,
|
|
1605
|
+
margin: 0,
|
|
1606
|
+
maxHeight: 180,
|
|
1607
|
+
overflowX: "auto",
|
|
1608
|
+
border: `1px solid ${c.border}`
|
|
1609
|
+
},
|
|
1610
|
+
lineNumberStyle: {
|
|
1611
|
+
minWidth: "2.5em",
|
|
1612
|
+
paddingRight: "1em",
|
|
1613
|
+
color: c.muted,
|
|
1614
|
+
textAlign: "right",
|
|
1615
|
+
userSelect: "none"
|
|
1616
|
+
},
|
|
1617
|
+
wrapLines: true,
|
|
1618
|
+
lineProps: (lineNumber) => {
|
|
1619
|
+
const isUsageLine = lineNumber >= parentInstance.usageLineStart && lineNumber <= parentInstance.usageLineEnd;
|
|
1620
|
+
return {
|
|
1621
|
+
style: {
|
|
1622
|
+
backgroundColor: isUsageLine ? isDark ? "rgba(255, 165, 0, 0.15)" : "rgba(255, 165, 0, 0.2)" : "transparent"
|
|
1623
|
+
}
|
|
1624
|
+
};
|
|
1625
|
+
},
|
|
1626
|
+
children: parentInstance.content
|
|
1627
|
+
}
|
|
1628
|
+
)
|
|
1629
|
+
] }),
|
|
1344
1630
|
editHistory.length > 0 && /* @__PURE__ */ jsxs("div", { style: { marginBottom: 20 }, children: [
|
|
1345
1631
|
/* @__PURE__ */ jsxs(
|
|
1346
|
-
"
|
|
1632
|
+
"button",
|
|
1347
1633
|
{
|
|
1634
|
+
onClick: () => setIsEditHistoryExpanded(!isEditHistoryExpanded),
|
|
1348
1635
|
style: {
|
|
1349
1636
|
fontSize: 11,
|
|
1350
1637
|
fontWeight: 600,
|
|
1351
1638
|
marginBottom: 8,
|
|
1352
1639
|
color: c.muted,
|
|
1353
|
-
textTransform: "uppercase"
|
|
1640
|
+
textTransform: "uppercase",
|
|
1641
|
+
background: "transparent",
|
|
1642
|
+
border: "none",
|
|
1643
|
+
padding: 0,
|
|
1644
|
+
cursor: "pointer",
|
|
1645
|
+
display: "flex",
|
|
1646
|
+
alignItems: "center",
|
|
1647
|
+
gap: 6
|
|
1354
1648
|
},
|
|
1355
1649
|
children: [
|
|
1650
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 10 }, children: isEditHistoryExpanded ? "▼" : "▶" }),
|
|
1356
1651
|
"Edit History (",
|
|
1357
1652
|
editHistory.length,
|
|
1358
1653
|
")"
|
|
1359
1654
|
]
|
|
1360
1655
|
}
|
|
1361
1656
|
),
|
|
1362
|
-
/* @__PURE__ */ jsx(
|
|
1657
|
+
isEditHistoryExpanded && /* @__PURE__ */ jsx(
|
|
1363
1658
|
"div",
|
|
1364
1659
|
{
|
|
1365
1660
|
style: {
|
|
@@ -1399,7 +1694,19 @@ function EditorOverlay({
|
|
|
1399
1694
|
marginBottom: 4
|
|
1400
1695
|
},
|
|
1401
1696
|
children: [
|
|
1402
|
-
/* @__PURE__ */ jsx(
|
|
1697
|
+
item.pending ? /* @__PURE__ */ jsx(
|
|
1698
|
+
"div",
|
|
1699
|
+
{
|
|
1700
|
+
style: {
|
|
1701
|
+
width: 16,
|
|
1702
|
+
height: 16,
|
|
1703
|
+
border: "2px solid transparent",
|
|
1704
|
+
borderTopColor: c.accent,
|
|
1705
|
+
borderRadius: "50%",
|
|
1706
|
+
animation: "ai-editor-spin 0.8s linear infinite"
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
) : /* @__PURE__ */ jsx(
|
|
1403
1710
|
"span",
|
|
1404
1711
|
{
|
|
1405
1712
|
style: {
|
|
@@ -1450,7 +1757,7 @@ function EditorOverlay({
|
|
|
1450
1757
|
)
|
|
1451
1758
|
] }),
|
|
1452
1759
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 6, flexWrap: "wrap" }, children: [
|
|
1453
|
-
isLastEdit && hasSnapshot && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1760
|
+
isLastEdit && hasSnapshot && !item.pending && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1454
1761
|
/* @__PURE__ */ jsx(
|
|
1455
1762
|
"button",
|
|
1456
1763
|
{
|
|
@@ -1512,25 +1819,25 @@ function EditorOverlay({
|
|
|
1512
1819
|
"button",
|
|
1513
1820
|
{
|
|
1514
1821
|
onClick: () => handleRetry(idx),
|
|
1515
|
-
disabled: isLoading,
|
|
1822
|
+
disabled: isLoading || item.pending,
|
|
1516
1823
|
style: {
|
|
1517
1824
|
padding: "4px 10px",
|
|
1518
1825
|
fontSize: 11,
|
|
1519
1826
|
borderRadius: 6,
|
|
1520
1827
|
border: "none",
|
|
1521
|
-
background: isLoading ? c.muted : c.accent,
|
|
1828
|
+
background: isLoading || item.pending ? c.muted : c.accent,
|
|
1522
1829
|
color: "#fff",
|
|
1523
1830
|
fontWeight: 600,
|
|
1524
|
-
cursor: isLoading ? "wait" : "pointer",
|
|
1831
|
+
cursor: isLoading || item.pending ? "wait" : "pointer",
|
|
1525
1832
|
whiteSpace: "nowrap"
|
|
1526
1833
|
},
|
|
1527
1834
|
title: "Retry this edit",
|
|
1528
1835
|
onMouseEnter: (e) => {
|
|
1529
|
-
if (!isLoading)
|
|
1836
|
+
if (!isLoading && !item.pending)
|
|
1530
1837
|
e.currentTarget.style.background = "#6366f1";
|
|
1531
1838
|
},
|
|
1532
1839
|
onMouseLeave: (e) => {
|
|
1533
|
-
if (!isLoading)
|
|
1840
|
+
if (!isLoading && !item.pending)
|
|
1534
1841
|
e.currentTarget.style.background = c.accent;
|
|
1535
1842
|
},
|
|
1536
1843
|
children: "↻ Retry"
|
|
@@ -1590,6 +1897,144 @@ function EditorOverlay({
|
|
|
1590
1897
|
}
|
|
1591
1898
|
)
|
|
1592
1899
|
] }),
|
|
1900
|
+
(suggestions.length > 0 || suggestionsLoading) && /* @__PURE__ */ jsxs("div", { style: { marginBottom: 20 }, children: [
|
|
1901
|
+
/* @__PURE__ */ jsxs(
|
|
1902
|
+
"div",
|
|
1903
|
+
{
|
|
1904
|
+
style: {
|
|
1905
|
+
fontSize: 11,
|
|
1906
|
+
fontWeight: 600,
|
|
1907
|
+
marginBottom: 8,
|
|
1908
|
+
color: c.muted,
|
|
1909
|
+
textTransform: "uppercase",
|
|
1910
|
+
display: "flex",
|
|
1911
|
+
alignItems: "center",
|
|
1912
|
+
gap: 8
|
|
1913
|
+
},
|
|
1914
|
+
children: [
|
|
1915
|
+
"Quick Suggestions",
|
|
1916
|
+
suggestionsLoading ? /* @__PURE__ */ jsx(
|
|
1917
|
+
"div",
|
|
1918
|
+
{
|
|
1919
|
+
style: {
|
|
1920
|
+
width: 12,
|
|
1921
|
+
height: 12,
|
|
1922
|
+
border: "2px solid transparent",
|
|
1923
|
+
borderTopColor: c.accent,
|
|
1924
|
+
borderRadius: "50%",
|
|
1925
|
+
animation: "ai-editor-spin 0.8s linear infinite"
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
) : suggestions.length > 0 && selectedSource && /* @__PURE__ */ jsx(
|
|
1929
|
+
"button",
|
|
1930
|
+
{
|
|
1931
|
+
onClick: () => {
|
|
1932
|
+
console.log("[AI Editor] Refreshing suggestions, excluding:", suggestions);
|
|
1933
|
+
fetchSuggestions(selectedSource, editHistory, lastAppliedSuggestion, suggestions);
|
|
1934
|
+
},
|
|
1935
|
+
disabled: isLoading,
|
|
1936
|
+
style: {
|
|
1937
|
+
background: "transparent",
|
|
1938
|
+
border: "none",
|
|
1939
|
+
padding: 4,
|
|
1940
|
+
cursor: isLoading ? "wait" : "pointer",
|
|
1941
|
+
color: c.muted,
|
|
1942
|
+
fontSize: 14,
|
|
1943
|
+
display: "flex",
|
|
1944
|
+
alignItems: "center",
|
|
1945
|
+
transition: "all 0.15s ease"
|
|
1946
|
+
},
|
|
1947
|
+
onMouseEnter: (e) => {
|
|
1948
|
+
if (!isLoading) {
|
|
1949
|
+
e.currentTarget.style.color = c.accent;
|
|
1950
|
+
e.currentTarget.style.transform = "rotate(180deg)";
|
|
1951
|
+
}
|
|
1952
|
+
},
|
|
1953
|
+
onMouseLeave: (e) => {
|
|
1954
|
+
if (!isLoading) {
|
|
1955
|
+
e.currentTarget.style.color = c.muted;
|
|
1956
|
+
e.currentTarget.style.transform = "rotate(0deg)";
|
|
1957
|
+
}
|
|
1958
|
+
},
|
|
1959
|
+
title: "Get different suggestions",
|
|
1960
|
+
children: "↻"
|
|
1961
|
+
}
|
|
1962
|
+
)
|
|
1963
|
+
]
|
|
1964
|
+
}
|
|
1965
|
+
),
|
|
1966
|
+
/* @__PURE__ */ jsx(
|
|
1967
|
+
"div",
|
|
1968
|
+
{
|
|
1969
|
+
style: {
|
|
1970
|
+
display: "flex",
|
|
1971
|
+
flexWrap: "wrap",
|
|
1972
|
+
gap: 8
|
|
1973
|
+
},
|
|
1974
|
+
children: suggestionsLoading ? (
|
|
1975
|
+
// Loading skeletons
|
|
1976
|
+
Array.from({ length: 6 }).map((_, idx) => /* @__PURE__ */ jsx(
|
|
1977
|
+
"div",
|
|
1978
|
+
{
|
|
1979
|
+
style: {
|
|
1980
|
+
padding: "8px 14px",
|
|
1981
|
+
borderRadius: 8,
|
|
1982
|
+
background: c.bgAlt,
|
|
1983
|
+
border: `1px solid ${c.border}`,
|
|
1984
|
+
fontSize: 13,
|
|
1985
|
+
height: 34,
|
|
1986
|
+
width: Math.random() * 60 + 80,
|
|
1987
|
+
// Variable width
|
|
1988
|
+
opacity: 0.5
|
|
1989
|
+
}
|
|
1990
|
+
},
|
|
1991
|
+
idx
|
|
1992
|
+
))
|
|
1993
|
+
) : suggestions.map((suggestionText, idx) => /* @__PURE__ */ jsx(
|
|
1994
|
+
"button",
|
|
1995
|
+
{
|
|
1996
|
+
onClick: (e) => {
|
|
1997
|
+
if (e.metaKey || e.ctrlKey) {
|
|
1998
|
+
setSuggestion(suggestionText);
|
|
1999
|
+
setTimeout(() => handleSubmit(), 50);
|
|
2000
|
+
} else {
|
|
2001
|
+
setSuggestion(suggestionText);
|
|
2002
|
+
}
|
|
2003
|
+
},
|
|
2004
|
+
disabled: isLoading,
|
|
2005
|
+
style: {
|
|
2006
|
+
padding: "8px 14px",
|
|
2007
|
+
borderRadius: 8,
|
|
2008
|
+
border: `1px solid ${c.border}`,
|
|
2009
|
+
background: c.bgAlt,
|
|
2010
|
+
color: c.text,
|
|
2011
|
+
fontSize: 13,
|
|
2012
|
+
cursor: isLoading ? "wait" : "pointer",
|
|
2013
|
+
transition: "all 0.15s ease",
|
|
2014
|
+
whiteSpace: "nowrap"
|
|
2015
|
+
},
|
|
2016
|
+
onMouseEnter: (e) => {
|
|
2017
|
+
if (!isLoading) {
|
|
2018
|
+
e.currentTarget.style.background = c.accent + "20";
|
|
2019
|
+
e.currentTarget.style.borderColor = c.accent;
|
|
2020
|
+
e.currentTarget.style.transform = "translateY(-1px)";
|
|
2021
|
+
}
|
|
2022
|
+
},
|
|
2023
|
+
onMouseLeave: (e) => {
|
|
2024
|
+
if (!isLoading) {
|
|
2025
|
+
e.currentTarget.style.background = c.bgAlt;
|
|
2026
|
+
e.currentTarget.style.borderColor = c.border;
|
|
2027
|
+
e.currentTarget.style.transform = "translateY(0)";
|
|
2028
|
+
}
|
|
2029
|
+
},
|
|
2030
|
+
title: "Click to use • Cmd+Click to apply immediately",
|
|
2031
|
+
children: suggestionText
|
|
2032
|
+
},
|
|
2033
|
+
idx
|
|
2034
|
+
))
|
|
2035
|
+
}
|
|
2036
|
+
)
|
|
2037
|
+
] }),
|
|
1593
2038
|
result && /* @__PURE__ */ jsx(
|
|
1594
2039
|
"div",
|
|
1595
2040
|
{
|
|
@@ -1720,4 +2165,4 @@ function EditorOverlay({
|
|
|
1720
2165
|
export {
|
|
1721
2166
|
AIEditorProvider as A
|
|
1722
2167
|
};
|
|
1723
|
-
//# sourceMappingURL=AIEditorProvider-
|
|
2168
|
+
//# sourceMappingURL=AIEditorProvider-CxdGjdLL.js.map
|