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