next-ai-editor 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/AIEditorProvider-CKA2K_g2.js +1428 -0
  2. package/dist/AIEditorProvider-CKA2K_g2.js.map +1 -0
  3. package/dist/AIEditorProvider-C_zRSAuV.cjs +1427 -0
  4. package/dist/AIEditorProvider-C_zRSAuV.cjs.map +1 -0
  5. package/dist/client/AIEditorProvider.d.ts +1 -33
  6. package/dist/client/AIEditorProvider.d.ts.map +1 -1
  7. package/dist/client/components/ChatPanel.d.ts +18 -0
  8. package/dist/client/components/ChatPanel.d.ts.map +1 -0
  9. package/dist/client/components/ControlPill.d.ts +8 -0
  10. package/dist/client/components/ControlPill.d.ts.map +1 -0
  11. package/dist/client/components/MessageItem.d.ts +7 -0
  12. package/dist/client/components/MessageItem.d.ts.map +1 -0
  13. package/dist/client/components/MessageList.d.ts +7 -0
  14. package/dist/client/components/MessageList.d.ts.map +1 -0
  15. package/dist/client/components/TaskHistoryPanel.d.ts +10 -0
  16. package/dist/client/components/TaskHistoryPanel.d.ts.map +1 -0
  17. package/dist/client/components/index.d.ts +11 -0
  18. package/dist/client/components/index.d.ts.map +1 -0
  19. package/dist/client/hooks/index.d.ts +3 -0
  20. package/dist/client/hooks/index.d.ts.map +1 -0
  21. package/dist/client/hooks/useChatStream.d.ts +66 -0
  22. package/dist/client/hooks/useChatStream.d.ts.map +1 -0
  23. package/dist/client/hooks/useHotReload.d.ts +10 -0
  24. package/dist/client/hooks/useHotReload.d.ts.map +1 -0
  25. package/dist/client/index.d.ts +3 -0
  26. package/dist/client/index.d.ts.map +1 -1
  27. package/dist/client.cjs +7 -1
  28. package/dist/client.cjs.map +1 -1
  29. package/dist/client.js +8 -2
  30. package/dist/client.js.map +1 -1
  31. package/dist/{index-3OMXRwpD.js → comments-D3m0RsOO.js} +505 -26
  32. package/dist/comments-D3m0RsOO.js.map +1 -0
  33. package/dist/{index-9QODCOgD.cjs → comments-Daur80r4.cjs} +492 -13
  34. package/dist/comments-Daur80r4.cjs.map +1 -0
  35. package/dist/index.cjs +34 -26
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.js +22 -14
  38. package/dist/index.js.map +1 -1
  39. package/dist/next-ai-editor.css +880 -0
  40. package/dist/server/agent/sdk-client.d.ts +54 -0
  41. package/dist/server/agent/sdk-client.d.ts.map +1 -0
  42. package/dist/server/agent/session-store.d.ts +101 -0
  43. package/dist/server/agent/session-store.d.ts.map +1 -0
  44. package/dist/server/handlers/chat.d.ts +6 -0
  45. package/dist/server/handlers/chat.d.ts.map +1 -0
  46. package/dist/server/handlers/comments.d.ts +10 -0
  47. package/dist/server/handlers/comments.d.ts.map +1 -0
  48. package/dist/server/handlers/index.d.ts +2 -1
  49. package/dist/server/handlers/index.d.ts.map +1 -1
  50. package/dist/server/handlers/read.d.ts.map +1 -1
  51. package/dist/server/index.d.ts +1 -0
  52. package/dist/server/index.d.ts.map +1 -1
  53. package/dist/server/utils/ast.d.ts.map +1 -1
  54. package/dist/server/utils/source-map.d.ts +5 -0
  55. package/dist/server/utils/source-map.d.ts.map +1 -1
  56. package/dist/server.cjs +27 -25
  57. package/dist/server.cjs.map +1 -1
  58. package/dist/server.js +14 -12
  59. package/dist/shared/comment-types.d.ts +140 -0
  60. package/dist/shared/comment-types.d.ts.map +1 -0
  61. package/package.json +13 -4
  62. package/dist/AIEditorProvider-CFFnEtEB.js +0 -2170
  63. package/dist/AIEditorProvider-CFFnEtEB.js.map +0 -1
  64. package/dist/AIEditorProvider-CmiACRfw.cjs +0 -2169
  65. package/dist/AIEditorProvider-CmiACRfw.cjs.map +0 -1
  66. package/dist/index-3OMXRwpD.js.map +0 -1
  67. package/dist/index-9QODCOgD.cjs.map +0 -1
@@ -1,2169 +0,0 @@
1
- "use client";
2
- "use strict";
3
- const jsxRuntime = require("react/jsx-runtime");
4
- const react = require("react");
5
- const reactSyntaxHighlighter = require("react-syntax-highlighter");
6
- const prism = require("react-syntax-highlighter/dist/esm/styles/prism");
7
- const pathUtils = require("./path-utils-DYzEWUGy.cjs");
8
- const _AIEditorStorage = class _AIEditorStorage {
9
- // 5MB
10
- /**
11
- * Generate storage key from file path and component name
12
- * Format: "ai-editor:session:components/Header.tsx#Header"
13
- */
14
- static getSessionKey(filePath, componentName) {
15
- const normalizedPath = filePath.replace(/\\/g, "/");
16
- return `${this.STORAGE_PREFIX}${normalizedPath}#${componentName}`;
17
- }
18
- /**
19
- * Save session to localStorage
20
- */
21
- static saveSession(session) {
22
- try {
23
- const key = this.getSessionKey(
24
- session.sourceLocation.filePath,
25
- session.sourceLocation.componentName
26
- );
27
- if (this.getStorageSize() > this.MAX_STORAGE_SIZE) {
28
- console.warn("Storage size exceeded, pruning old sessions...");
29
- this.pruneOldSessions(7 * 24 * 60 * 60 * 1e3);
30
- }
31
- const serialized = JSON.stringify(session);
32
- localStorage.setItem(key, serialized);
33
- return true;
34
- } catch (e) {
35
- if (e instanceof Error && e.name === "QuotaExceededError") {
36
- console.error("localStorage quota exceeded");
37
- this.pruneOldSessions(7 * 24 * 60 * 60 * 1e3);
38
- try {
39
- const key = this.getSessionKey(
40
- session.sourceLocation.filePath,
41
- session.sourceLocation.componentName
42
- );
43
- const serialized = JSON.stringify(session);
44
- localStorage.setItem(key, serialized);
45
- return true;
46
- } catch {
47
- return false;
48
- }
49
- }
50
- console.error("Failed to save session:", e);
51
- return false;
52
- }
53
- }
54
- /**
55
- * Load session by filePath + componentName
56
- */
57
- static loadSession(filePath, componentName) {
58
- try {
59
- const key = this.getSessionKey(filePath, componentName);
60
- const stored = localStorage.getItem(key);
61
- if (!stored) return null;
62
- const session = JSON.parse(stored);
63
- if (!session.version || !session.sourceLocation || !session.editHistory) {
64
- console.warn("Invalid session schema, removing corrupted data");
65
- localStorage.removeItem(key);
66
- return null;
67
- }
68
- return session;
69
- } catch (e) {
70
- console.error("Failed to load session:", e);
71
- const key = this.getSessionKey(filePath, componentName);
72
- localStorage.removeItem(key);
73
- return null;
74
- }
75
- }
76
- /**
77
- * Delete specific session
78
- */
79
- static deleteSession(filePath, componentName) {
80
- try {
81
- const key = this.getSessionKey(filePath, componentName);
82
- localStorage.removeItem(key);
83
- } catch (e) {
84
- console.error("Failed to delete session:", e);
85
- }
86
- }
87
- /**
88
- * Get all session keys
89
- */
90
- static getAllSessionKeys() {
91
- const keys = [];
92
- try {
93
- for (let i = 0; i < localStorage.length; i++) {
94
- const key = localStorage.key(i);
95
- if (key && key.startsWith(this.STORAGE_PREFIX)) {
96
- keys.push(key);
97
- }
98
- }
99
- } catch (e) {
100
- console.error("Failed to get session keys:", e);
101
- }
102
- return keys;
103
- }
104
- /**
105
- * Clear all sessions
106
- */
107
- static clearAllSessions() {
108
- try {
109
- const keys = this.getAllSessionKeys();
110
- keys.forEach((key) => localStorage.removeItem(key));
111
- console.log(`Cleared ${keys.length} session(s)`);
112
- } catch (e) {
113
- console.error("Failed to clear sessions:", e);
114
- }
115
- }
116
- /**
117
- * Get total storage usage in bytes
118
- */
119
- static getStorageSize() {
120
- let total = 0;
121
- try {
122
- for (let i = 0; i < localStorage.length; i++) {
123
- const key = localStorage.key(i);
124
- if (key && key.startsWith(this.STORAGE_PREFIX)) {
125
- const value = localStorage.getItem(key);
126
- if (value) {
127
- total += (key.length + value.length) * 2;
128
- }
129
- }
130
- }
131
- } catch (e) {
132
- console.error("Failed to calculate storage size:", e);
133
- }
134
- return total;
135
- }
136
- /**
137
- * Remove sessions older than maxAge milliseconds
138
- */
139
- static pruneOldSessions(maxAge) {
140
- try {
141
- const now = Date.now();
142
- const keys = this.getAllSessionKeys();
143
- keys.forEach((key) => {
144
- const value = localStorage.getItem(key);
145
- if (!value) return;
146
- try {
147
- const session = JSON.parse(value);
148
- const age = now - session.lastModified;
149
- if (age > maxAge) {
150
- console.log(`Pruning old session: ${key} (age: ${Math.round(age / (24 * 60 * 60 * 1e3))} days)`);
151
- localStorage.removeItem(key);
152
- }
153
- } catch (e) {
154
- console.warn(`Removing corrupted session: ${key}`);
155
- localStorage.removeItem(key);
156
- }
157
- });
158
- } catch (e) {
159
- console.error("Failed to prune sessions:", e);
160
- }
161
- }
162
- /**
163
- * Compute SHA-256 hash of file content using Web Crypto API
164
- */
165
- static async hashFileContent(content) {
166
- try {
167
- const encoder = new TextEncoder();
168
- const data = encoder.encode(content);
169
- const hashBuffer = await crypto.subtle.digest("SHA-256", data);
170
- const hashArray = Array.from(new Uint8Array(hashBuffer));
171
- const hashHex = hashArray.map((byte) => byte.toString(16).padStart(2, "0")).join("");
172
- return hashHex;
173
- } catch (e) {
174
- console.error("Failed to hash content:", e);
175
- return this.simpleHash(content);
176
- }
177
- }
178
- /**
179
- * Simple hash fallback (for environments without Web Crypto API)
180
- */
181
- static simpleHash(str) {
182
- let hash = 0;
183
- for (let i = 0; i < str.length; i++) {
184
- const char = str.charCodeAt(i);
185
- hash = (hash << 5) - hash + char;
186
- hash = hash & hash;
187
- }
188
- return hash.toString(16);
189
- }
190
- /**
191
- * Get most recently modified session
192
- */
193
- static getLastActiveSession() {
194
- try {
195
- const keys = this.getAllSessionKeys();
196
- if (keys.length === 0) return null;
197
- let latestSession = null;
198
- let latestTime = 0;
199
- keys.forEach((key) => {
200
- const value = localStorage.getItem(key);
201
- if (!value) return;
202
- try {
203
- const session = JSON.parse(value);
204
- if (!session.version || !session.sourceLocation || !session.editHistory) {
205
- console.warn(`Skipping invalid session: ${key}`);
206
- return;
207
- }
208
- if (session.lastModified > latestTime) {
209
- latestTime = session.lastModified;
210
- latestSession = session;
211
- }
212
- } catch (e) {
213
- console.warn(`Skipping corrupted session: ${key}`);
214
- }
215
- });
216
- return latestSession;
217
- } catch (e) {
218
- console.error("Failed to get last active session:", e);
219
- return null;
220
- }
221
- }
222
- };
223
- _AIEditorStorage.STORAGE_PREFIX = "ai-editor:session:";
224
- _AIEditorStorage.VERSION = "1.0.0";
225
- _AIEditorStorage.MAX_STORAGE_SIZE = 5 * 1024 * 1024;
226
- let AIEditorStorage = _AIEditorStorage;
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) || ""
236
- };
237
- if (selectedSource.debugStack) {
238
- params.debugStack = selectedSource.debugStack;
239
- }
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;
249
- }
250
- }
251
- return params;
252
- }
253
- function getFiberFromElement(element) {
254
- const key = Object.keys(element).find(
255
- (k) => k.startsWith("__reactFiber$") || k.startsWith("__reactInternalInstance$")
256
- );
257
- return key ? element[key] : null;
258
- }
259
- function getComponentName(fiber) {
260
- var _a, _b;
261
- if (!fiber) return "Unknown";
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
- }
268
- if (typeof type === "string") return type;
269
- if (typeof type === "function")
270
- return type.displayName || type.name || "Anonymous";
271
- if (type == null ? void 0 : type.displayName) return type.displayName;
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
- }
280
- return "Unknown";
281
- }
282
- function isUserComponent(fiber) {
283
- if (fiber.tag === 5 || fiber.tag === 6 || fiber.tag === 3) return false;
284
- const type = fiber.type;
285
- if (!type || typeof type === "string") return false;
286
- return true;
287
- }
288
- function countNthOfType(element, tagName) {
289
- let boundary = element.parentElement;
290
- while (boundary) {
291
- const fiber = getFiberFromElement(boundary);
292
- if (fiber && isUserComponent(fiber)) break;
293
- boundary = boundary.parentElement;
294
- }
295
- if (!boundary) boundary = element.parentElement;
296
- if (!boundary) return 1;
297
- const sameTagElements = [];
298
- if (boundary.tagName.toLowerCase() === tagName.toLowerCase()) {
299
- sameTagElements.push(boundary);
300
- }
301
- sameTagElements.push(...Array.from(boundary.querySelectorAll(tagName)));
302
- let count = 1;
303
- for (const el of sameTagElements) {
304
- if (el === element) break;
305
- count++;
306
- }
307
- return count;
308
- }
309
- function extractProps(fiber) {
310
- if (!fiber) return void 0;
311
- const fiberProps = fiber.memoizedProps || fiber.pendingProps;
312
- if (!fiberProps || typeof fiberProps !== "object") return void 0;
313
- const props = {};
314
- const identifyingKeys = [
315
- "id",
316
- "name",
317
- "type",
318
- "href",
319
- "src",
320
- "alt",
321
- "role",
322
- "aria-label",
323
- "data-testid"
324
- ];
325
- for (const key of identifyingKeys) {
326
- if (key in fiberProps && typeof fiberProps[key] === "string") {
327
- props[key] = fiberProps[key];
328
- }
329
- }
330
- if (fiberProps.style && typeof fiberProps.style === "object") {
331
- props._styleKeys = Object.keys(fiberProps.style).slice(0, 5);
332
- }
333
- return Object.keys(props).length > 0 ? props : void 0;
334
- }
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);
346
- }
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 };
352
- }
353
- function extractFromDebugStack(fiber) {
354
- const stack = fiber._debugStack;
355
- if (!stack) return null;
356
- const stackStr = stack.stack || String(stack);
357
- const frames = stackStr.split("\n");
358
- const skipPatterns = [
359
- "node_modules",
360
- "SegmentViewNode",
361
- "LayoutRouter",
362
- "ErrorBoundary",
363
- "fakeJSXCallSite"
364
- ];
365
- for (const frame of frames) {
366
- if (skipPatterns.some((p) => frame.includes(p))) continue;
367
- const match = frame.match(/at\s+(\w+)\s+\((.+?):(\d+):(\d+)\)?$/);
368
- if (match) {
369
- const fileName = pathUtils.cleanPath(match[2].replace(/\?[^:]*$/, ""));
370
- if (!pathUtils.shouldSkipPath(fileName, ["ai-editor-provider"])) {
371
- return {
372
- fileName,
373
- lineNumber: parseInt(match[3], 10),
374
- columnNumber: parseInt(match[4], 10)
375
- };
376
- }
377
- }
378
- }
379
- return null;
380
- }
381
- function extractFromDebugOwner(fiber) {
382
- var _a, _b;
383
- const owner = fiber._debugOwner;
384
- if (!owner) return null;
385
- if ((_a = owner._debugSource) == null ? void 0 : _a.fileName) {
386
- return owner._debugSource;
387
- }
388
- const stack = owner.stack;
389
- if (Array.isArray(stack) && ((_b = stack[0]) == null ? void 0 : _b.fileName)) {
390
- return stack[0];
391
- }
392
- return null;
393
- }
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
- }
402
- }
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
- }
455
- }
456
- current = current.return || current._debugOwner || null;
457
- }
458
- return null;
459
- }
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;
617
- }
618
- function getSourceFromElement(element) {
619
- var _a;
620
- let current = element;
621
- let elementWithSource = null;
622
- let fiberWithSource = null;
623
- while (current && current !== document.body) {
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
- }
632
- current = current.parentElement;
633
- }
634
- if (!fiberWithSource || !elementWithSource) return null;
635
- const source = findSourceFromFiber(fiberWithSource);
636
- if (!source) return null;
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
- };
647
- }
648
- if (typeof window !== "undefined") {
649
- window.__getSource = getSourceFromElement;
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
- }
766
- function AIEditorProvider({
767
- children,
768
- theme = "dark"
769
- }) {
770
- const [isEnabled, setEnabled] = react.useState(false);
771
- const [selectedSource, setSelectedSource] = react.useState(
772
- null
773
- );
774
- const [selectedDOMElement, setSelectedDOMElement] = react.useState(
775
- null
776
- );
777
- const [isLoading, setIsLoading] = react.useState(false);
778
- const [editHistory, setEditHistory] = react.useState([]);
779
- const [showStaleWarning, setShowStaleWarning] = react.useState(false);
780
- const [staleSession, setStaleSession] = react.useState(null);
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());
787
- react.useEffect(() => {
788
- const handleKey = (e) => {
789
- if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key.toLowerCase() === "e") {
790
- e.preventDefault();
791
- setEnabled((p) => !p);
792
- }
793
- };
794
- window.addEventListener("keydown", handleKey);
795
- return () => window.removeEventListener("keydown", handleKey);
796
- }, []);
797
- react.useEffect(() => {
798
- return;
799
- }, []);
800
- const restoreSession = react.useCallback(async (session) => {
801
- const sourceLocation = session.sourceLocation;
802
- setSelectedSource(sourceLocation);
803
- setEditHistory(session.editHistory);
804
- if (sourceLocation.debugStack) {
805
- const resolved = await resolveSourceLocation(sourceLocation);
806
- if (resolved) {
807
- setSelectedSource(resolved);
808
- }
809
- }
810
- }, []);
811
- const handleClearSession = react.useCallback(() => {
812
- if (selectedSource) {
813
- AIEditorStorage.deleteSession(
814
- selectedSource.filePath,
815
- selectedSource.componentName
816
- );
817
- setHasActiveSession(false);
818
- setSelectedSource(null);
819
- setEditHistory([]);
820
- }
821
- }, [selectedSource]);
822
- const submitEdit = react.useCallback(
823
- async (suggestion) => {
824
- if (!selectedSource)
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]);
835
- setIsLoading(true);
836
- try {
837
- const res = await fetch("/api/ai-editor/edit", {
838
- method: "POST",
839
- headers: { "Content-Type": "application/json" },
840
- body: JSON.stringify({
841
- filePath: selectedSource.filePath,
842
- lineNumber: selectedSource.lineNumber,
843
- componentName: selectedSource.componentName,
844
- suggestion,
845
- // NEW: Pass element context for precise matching
846
- elementContext: selectedSource.elementContext,
847
- // Pass debugStack for server-side source map resolution
848
- debugStack: selectedSource.debugStack,
849
- // Pass edit history for context
850
- editHistory: editHistory.map((item) => ({
851
- suggestion: item.suggestion,
852
- success: item.success
853
- })),
854
- // Pass parent instance if available
855
- parentInstance
856
- })
857
- });
858
- const result = await res.json();
859
- const completedEdit = {
860
- id: editId,
861
- suggestion,
862
- success: result.success,
863
- pending: false,
864
- error: result.error,
865
- timestamp: Date.now(),
866
- fileSnapshot: result.fileSnapshot,
867
- editedFilePath: result.editedFile,
868
- // Track which file was actually edited
869
- generatedCode: result.generatedCode,
870
- modifiedLines: result.modifiedLines
871
- };
872
- setEditHistory(
873
- (prev) => prev.map((item) => item.id === editId ? completedEdit : item)
874
- );
875
- if (ENABLE_SESSION_PERSISTENCE && selectedSource && result.fileSnapshot) ;
876
- return result;
877
- } catch (err) {
878
- const error = String(err);
879
- setEditHistory(
880
- (prev) => prev.map(
881
- (item) => item.id === editId ? { ...item, success: false, pending: false, error } : item
882
- )
883
- );
884
- return { success: false, error };
885
- } finally {
886
- setIsLoading(false);
887
- }
888
- },
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]
954
- );
955
- const handleSelect = react.useCallback(
956
- (element, source) => {
957
- setSelectedDOMElement(element);
958
- setSelectedSource(source);
959
- setEditHistory([]);
960
- setParentInstance(null);
961
- resolveSourceLocation(source).then((resolved) => {
962
- if (resolved) {
963
- setSelectedSource((prev) => prev === source ? resolved : prev);
964
- }
965
- });
966
- },
967
- [setSelectedDOMElement, setSelectedSource, setParentInstance]
968
- );
969
- if (process.env.NODE_ENV !== "development") {
970
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
971
- }
972
- return /* @__PURE__ */ jsxRuntime.jsxs(
973
- EditorContext.Provider,
974
- {
975
- value: {
976
- isEnabled,
977
- setEnabled,
978
- selectedSource,
979
- setSelectedSource,
980
- selectedDOMElement,
981
- isLoading,
982
- setIsLoading,
983
- editHistory,
984
- setEditHistory,
985
- submitEdit,
986
- suggestions,
987
- suggestionsLoading,
988
- lastAppliedSuggestion,
989
- setLastAppliedSuggestion,
990
- fetchSuggestions
991
- },
992
- children: [
993
- children,
994
- isEnabled && /* @__PURE__ */ jsxRuntime.jsx(
995
- EditorOverlay,
996
- {
997
- theme,
998
- onSelect: handleSelect,
999
- showStaleWarning,
1000
- staleSession,
1001
- hasActiveSession,
1002
- setShowStaleWarning,
1003
- setStaleSession,
1004
- setHasActiveSession,
1005
- restoreSession,
1006
- handleClearSession,
1007
- parentInstance,
1008
- setParentInstance
1009
- }
1010
- )
1011
- ]
1012
- }
1013
- );
1014
- }
1015
- function EditorOverlay({
1016
- theme,
1017
- onSelect,
1018
- showStaleWarning,
1019
- staleSession,
1020
- hasActiveSession,
1021
- setShowStaleWarning,
1022
- setStaleSession,
1023
- setHasActiveSession,
1024
- restoreSession,
1025
- handleClearSession,
1026
- parentInstance,
1027
- setParentInstance
1028
- }) {
1029
- var _a;
1030
- const {
1031
- selectedSource,
1032
- setSelectedSource,
1033
- submitEdit,
1034
- isLoading,
1035
- setIsLoading,
1036
- setEnabled,
1037
- selectedDOMElement,
1038
- editHistory,
1039
- setEditHistory,
1040
- suggestions,
1041
- suggestionsLoading,
1042
- lastAppliedSuggestion,
1043
- setLastAppliedSuggestion,
1044
- fetchSuggestions
1045
- } = useAIEditor();
1046
- const [hoveredSource, setHoveredSource] = react.useState(
1047
- null
1048
- );
1049
- const [hoveredElement, setHoveredElement] = react.useState(null);
1050
- const [hoveredRect, setHoveredRect] = react.useState(null);
1051
- const [mousePos, setMousePos] = react.useState({ x: 0, y: 0 });
1052
- const [isResolvingSource, setIsResolvingSource] = react.useState(false);
1053
- const [suggestion, setSuggestion] = react.useState("");
1054
- const [result, setResult] = react.useState(null);
1055
- const [code, setCode] = react.useState("");
1056
- const [codeLineStart, setCodeLineStart] = react.useState(1);
1057
- const [targetStartLine, setTargetStartLine] = react.useState(null);
1058
- const [targetEndLine, setTargetEndLine] = react.useState(null);
1059
- const [absolutePath, setAbsolutePath] = react.useState(null);
1060
- const [parsedComponentName, setParsedComponentName] = react.useState(
1061
- null
1062
- );
1063
- const [isSourcePreviewExpanded, setIsSourcePreviewExpanded] = react.useState(false);
1064
- const [isParentPreviewExpanded, setIsParentPreviewExpanded] = react.useState(false);
1065
- const [isEditHistoryExpanded, setIsEditHistoryExpanded] = react.useState(true);
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
- }, []);
1086
- react.useEffect(() => {
1087
- if (selectedSource) {
1088
- refreshCodePreview(selectedSource);
1089
- fetchSuggestions(selectedSource, []);
1090
- fetch(
1091
- `/api/ai-editor/absolute-path?` + new URLSearchParams({ path: selectedSource.filePath })
1092
- ).then((r) => r.json()).then((d) => d.success && setAbsolutePath(d.absolutePath)).catch(() => setAbsolutePath(null));
1093
- } else {
1094
- setAbsolutePath(null);
1095
- setParsedComponentName(null);
1096
- }
1097
- }, [selectedSource, refreshCodePreview]);
1098
- react.useEffect(() => {
1099
- let lastEl = null;
1100
- let raf;
1101
- const onMove = (e) => {
1102
- setMousePos({ x: e.clientX, y: e.clientY });
1103
- if (selectedSource) return;
1104
- cancelAnimationFrame(raf);
1105
- raf = requestAnimationFrame(() => {
1106
- const el = document.elementFromPoint(e.clientX, e.clientY);
1107
- if (!el || el.closest(".ai-editor-ui") || el === lastEl) return;
1108
- lastEl = el;
1109
- const source = getSourceFromElement(el);
1110
- if (source) {
1111
- setHoveredElement(el);
1112
- setHoveredRect(el.getBoundingClientRect());
1113
- const isCompiledPath = source.filePath.includes(".next/") || source.filePath.includes("/chunks/") || source.filePath.startsWith("file://") || source.filePath.endsWith(".js") && (source.filePath.includes("/server/") || source.filePath.includes("/static/"));
1114
- if (isCompiledPath && source.debugStack) {
1115
- setIsResolvingSource(true);
1116
- resolveSourceLocation(source).then((resolved) => {
1117
- setIsResolvingSource(false);
1118
- if (resolved) {
1119
- console.log("Resolved source location:", resolved);
1120
- setHoveredSource(resolved);
1121
- } else {
1122
- console.warn(
1123
- "Failed to resolve source location for:",
1124
- source
1125
- );
1126
- setHoveredSource(source);
1127
- }
1128
- }).catch((err) => {
1129
- console.error("Error resolving source location:", err);
1130
- setIsResolvingSource(false);
1131
- setHoveredSource(source);
1132
- });
1133
- } else if (isCompiledPath && !source.debugStack) {
1134
- console.warn("Compiled path but no debugStack:", source);
1135
- setIsResolvingSource(false);
1136
- setHoveredSource(source);
1137
- } else {
1138
- setIsResolvingSource(false);
1139
- setHoveredSource(source);
1140
- }
1141
- } else {
1142
- setHoveredSource(null);
1143
- setHoveredElement(null);
1144
- setHoveredRect(null);
1145
- setIsResolvingSource(false);
1146
- }
1147
- });
1148
- };
1149
- const onClick = (e) => {
1150
- if (e.target.closest(".ai-editor-ui")) return;
1151
- if (hoveredSource && hoveredElement) {
1152
- e.preventDefault();
1153
- e.stopPropagation();
1154
- onSelect(hoveredElement, hoveredSource);
1155
- setSuggestion("");
1156
- setResult(null);
1157
- }
1158
- };
1159
- document.addEventListener("mousemove", onMove, { passive: true });
1160
- document.addEventListener("click", onClick, true);
1161
- return () => {
1162
- document.removeEventListener("mousemove", onMove);
1163
- document.removeEventListener("click", onClick, true);
1164
- cancelAnimationFrame(raf);
1165
- };
1166
- }, [hoveredSource, hoveredElement, selectedSource, onSelect]);
1167
- const handleSubmit = async () => {
1168
- if (!suggestion.trim()) return;
1169
- const appliedSuggestion = suggestion;
1170
- const res = await submitEdit(suggestion);
1171
- setResult(res);
1172
- if (res.success) {
1173
- setSuggestion("");
1174
- setLastAppliedSuggestion(appliedSuggestion);
1175
- if (selectedSource) {
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);
1180
- }
1181
- setTimeout(() => setResult(null), 3e3);
1182
- }
1183
- };
1184
- const handleUndo = async () => {
1185
- if (editHistory.length === 0 || !selectedSource) return;
1186
- const lastSuccessfulEdit = [...editHistory].reverse().find((edit) => edit.success && edit.fileSnapshot);
1187
- if (!lastSuccessfulEdit) {
1188
- console.warn("No successful edit with snapshot found to undo");
1189
- return;
1190
- }
1191
- const fileToRestore = lastSuccessfulEdit.editedFilePath || selectedSource.filePath;
1192
- console.log(`[Undo] Restoring file: ${fileToRestore}`);
1193
- setIsLoading(true);
1194
- try {
1195
- const response = await fetch("/api/ai-editor/undo", {
1196
- method: "POST",
1197
- headers: { "Content-Type": "application/json" },
1198
- body: JSON.stringify({
1199
- filePath: fileToRestore,
1200
- content: lastSuccessfulEdit.fileSnapshot
1201
- })
1202
- });
1203
- const result2 = await response.json();
1204
- if (!result2.success) {
1205
- throw new Error(result2.error || "Undo failed");
1206
- }
1207
- setEditHistory((prev) => {
1208
- const lastIndex = prev.lastIndexOf(lastSuccessfulEdit);
1209
- return prev.filter((_, idx) => idx !== lastIndex);
1210
- });
1211
- await refreshCodePreview(selectedSource);
1212
- setResult({ success: true, error: void 0 });
1213
- setTimeout(() => setResult(null), 3e3);
1214
- } catch (error) {
1215
- console.error("Undo error:", error);
1216
- setResult({ success: false, error: String(error) });
1217
- } finally {
1218
- setIsLoading(false);
1219
- }
1220
- };
1221
- const handleRetry = async (editIndex) => {
1222
- const editToRetry = editHistory[editIndex];
1223
- if (!editToRetry) return;
1224
- const res = await submitEdit(editToRetry.suggestion);
1225
- if (res.success && selectedSource) {
1226
- await new Promise((resolve) => setTimeout(resolve, 100));
1227
- await refreshCodePreview(selectedSource, { includeParent: false });
1228
- }
1229
- };
1230
- const handleUndoAndRetry = async () => {
1231
- if (editHistory.length === 0 || !selectedSource) return;
1232
- const lastSuccessfulEdit = [...editHistory].reverse().find((edit) => edit.success && edit.fileSnapshot);
1233
- if (!lastSuccessfulEdit) {
1234
- console.warn("No successful edit with snapshot found to undo and retry");
1235
- return;
1236
- }
1237
- const fileToRestore = lastSuccessfulEdit.editedFilePath || selectedSource.filePath;
1238
- console.log(`[Undo & Retry] Restoring file: ${fileToRestore}`);
1239
- setIsLoading(true);
1240
- try {
1241
- const undoResponse = await fetch("/api/ai-editor/undo", {
1242
- method: "POST",
1243
- headers: { "Content-Type": "application/json" },
1244
- body: JSON.stringify({
1245
- filePath: fileToRestore,
1246
- content: lastSuccessfulEdit.fileSnapshot
1247
- })
1248
- });
1249
- const undoResult = await undoResponse.json();
1250
- if (!undoResult.success) {
1251
- throw new Error(undoResult.error || "Undo failed");
1252
- }
1253
- setEditHistory((prev) => {
1254
- const lastIndex = prev.lastIndexOf(lastSuccessfulEdit);
1255
- return prev.filter((_, idx) => idx !== lastIndex);
1256
- });
1257
- await refreshCodePreview(selectedSource);
1258
- setIsLoading(true);
1259
- const retryResult = await submitEdit(lastSuccessfulEdit.suggestion);
1260
- if (retryResult.success && selectedSource) {
1261
- await new Promise((resolve) => setTimeout(resolve, 100));
1262
- await refreshCodePreview(selectedSource);
1263
- }
1264
- if (retryResult.success) {
1265
- setResult({ success: true, error: void 0 });
1266
- } else {
1267
- setResult({ success: false, error: retryResult.error });
1268
- }
1269
- setTimeout(() => setResult(null), 3e3);
1270
- } catch (error) {
1271
- console.error("Undo and retry error:", error);
1272
- setResult({ success: false, error: String(error) });
1273
- } finally {
1274
- setIsLoading(false);
1275
- }
1276
- };
1277
- const handleDone = () => {
1278
- window.location.reload();
1279
- };
1280
- const c = {
1281
- bg: isDark ? "#0d0d14" : "#fff",
1282
- bgAlt: isDark ? "#16162a" : "#f5f5f5",
1283
- text: isDark ? "#e4e4e7" : "#18181b",
1284
- muted: isDark ? "#71717a" : "#a1a1aa",
1285
- accent: "#818cf8",
1286
- border: isDark ? "#27273f" : "#e4e4e7",
1287
- success: "#34d399",
1288
- error: "#f87171"
1289
- };
1290
- const elCtx = selectedSource == null ? void 0 : selectedSource.elementContext;
1291
- return /* @__PURE__ */ jsxRuntime.jsxs(
1292
- "div",
1293
- {
1294
- className: "ai-editor-ui",
1295
- style: { fontFamily: "system-ui, sans-serif" },
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
- ` }),
1303
- hoveredRect && !selectedSource && /* @__PURE__ */ jsxRuntime.jsx(
1304
- "div",
1305
- {
1306
- style: {
1307
- position: "fixed",
1308
- left: hoveredRect.left - 2,
1309
- top: hoveredRect.top - 2,
1310
- width: hoveredRect.width + 4,
1311
- height: hoveredRect.height + 4,
1312
- border: `2px solid ${c.accent}`,
1313
- borderRadius: 6,
1314
- background: `${c.accent}15`,
1315
- pointerEvents: "none",
1316
- zIndex: 99998
1317
- }
1318
- }
1319
- ),
1320
- hoveredSource && !selectedSource && !isResolvingSource && /* @__PURE__ */ jsxRuntime.jsxs(
1321
- "div",
1322
- {
1323
- style: {
1324
- position: "fixed",
1325
- left: Math.min(mousePos.x + 14, window.innerWidth - 340),
1326
- top: mousePos.y + 14,
1327
- background: c.bg,
1328
- color: c.text,
1329
- border: `1px solid ${c.border}`,
1330
- borderRadius: 10,
1331
- padding: "12px 16px",
1332
- fontSize: 12,
1333
- fontFamily: "ui-monospace, monospace",
1334
- zIndex: 99999,
1335
- boxShadow: `0 8px 30px rgba(0,0,0,${isDark ? 0.5 : 0.15})`,
1336
- maxWidth: 320,
1337
- pointerEvents: "none"
1338
- },
1339
- children: [
1340
- /* @__PURE__ */ jsxRuntime.jsxs(
1341
- "div",
1342
- {
1343
- style: {
1344
- fontWeight: 700,
1345
- color: c.accent,
1346
- marginBottom: 4,
1347
- fontSize: 14
1348
- },
1349
- children: [
1350
- "<",
1351
- hoveredSource.componentName,
1352
- " />"
1353
- ]
1354
- }
1355
- ),
1356
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { color: c.muted, fontSize: 11 }, children: [
1357
- hoveredSource.filePath,
1358
- ":",
1359
- hoveredSource.lineNumber
1360
- ] }),
1361
- ((_a = hoveredSource.elementContext) == null ? void 0 : _a.textContent) && /* @__PURE__ */ jsxRuntime.jsxs(
1362
- "div",
1363
- {
1364
- style: {
1365
- color: c.muted,
1366
- fontSize: 10,
1367
- marginTop: 4,
1368
- fontStyle: "italic"
1369
- },
1370
- children: [
1371
- '"',
1372
- hoveredSource.elementContext.textContent,
1373
- '"'
1374
- ]
1375
- }
1376
- )
1377
- ]
1378
- }
1379
- ),
1380
- ENABLE_SESSION_PERSISTENCE,
1381
- selectedSource && /* @__PURE__ */ jsxRuntime.jsxs(
1382
- "div",
1383
- {
1384
- style: {
1385
- position: "fixed",
1386
- bottom: 20,
1387
- right: 20,
1388
- background: c.bg,
1389
- color: c.text,
1390
- borderRadius: 16,
1391
- padding: 24,
1392
- width: 560,
1393
- maxWidth: "calc(100vw - 40px)",
1394
- maxHeight: "calc(100vh - 40px)",
1395
- overflowY: "auto",
1396
- overflowX: "hidden",
1397
- zIndex: 1e5,
1398
- boxShadow: `0 24px 80px rgba(0,0,0,${isDark ? 0.6 : 0.25})`,
1399
- border: `1px solid ${c.border}`
1400
- },
1401
- children: [
1402
- /* @__PURE__ */ jsxRuntime.jsxs(
1403
- "div",
1404
- {
1405
- style: {
1406
- display: "flex",
1407
- justifyContent: "space-between",
1408
- marginBottom: 20
1409
- },
1410
- children: [
1411
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1412
- /* @__PURE__ */ jsxRuntime.jsx(
1413
- "div",
1414
- {
1415
- style: {
1416
- fontSize: 11,
1417
- textTransform: "uppercase",
1418
- letterSpacing: 1,
1419
- color: c.muted
1420
- },
1421
- children: "Editing"
1422
- }
1423
- ),
1424
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 20, fontWeight: 700, color: c.accent }, children: [
1425
- "<",
1426
- parsedComponentName || selectedSource.componentName,
1427
- " />"
1428
- ] }),
1429
- /* @__PURE__ */ jsxRuntime.jsx(
1430
- "div",
1431
- {
1432
- style: {
1433
- fontSize: 12,
1434
- color: c.muted,
1435
- fontFamily: "monospace",
1436
- marginTop: 4
1437
- },
1438
- children: /* @__PURE__ */ jsxRuntime.jsxs(
1439
- "a",
1440
- {
1441
- href: absolutePath ? `cursor://file/${absolutePath}:${selectedSource.lineNumber}` : `cursor://file/${selectedSource.filePath}:${selectedSource.lineNumber}`,
1442
- onClick: (e) => {
1443
- e.preventDefault();
1444
- e.stopPropagation();
1445
- const path = absolutePath || selectedSource.filePath;
1446
- const cursorPath = path.startsWith("/") ? path : `/${path}`;
1447
- window.location.href = `cursor://file${cursorPath}:${selectedSource.lineNumber}`;
1448
- },
1449
- style: {
1450
- color: c.accent,
1451
- textDecoration: "none",
1452
- cursor: "pointer",
1453
- borderBottom: `1px solid ${c.accent}40`
1454
- },
1455
- onMouseEnter: (e) => {
1456
- e.currentTarget.style.borderBottomColor = c.accent;
1457
- },
1458
- onMouseLeave: (e) => {
1459
- e.currentTarget.style.borderBottomColor = `${c.accent}40`;
1460
- },
1461
- children: [
1462
- selectedSource.filePath,
1463
- ":",
1464
- selectedSource.lineNumber
1465
- ]
1466
- }
1467
- )
1468
- }
1469
- ),
1470
- (elCtx == null ? void 0 : elCtx.textContent) && /* @__PURE__ */ jsxRuntime.jsxs(
1471
- "div",
1472
- {
1473
- style: {
1474
- fontSize: 11,
1475
- color: c.muted,
1476
- marginTop: 4,
1477
- fontStyle: "italic"
1478
- },
1479
- children: [
1480
- 'Element text: "',
1481
- elCtx.textContent,
1482
- '"'
1483
- ]
1484
- }
1485
- )
1486
- ] }),
1487
- /* @__PURE__ */ jsxRuntime.jsx(
1488
- "button",
1489
- {
1490
- onClick: () => setSelectedSource(null),
1491
- style: {
1492
- width: 36,
1493
- height: 36,
1494
- borderRadius: 10,
1495
- border: "none",
1496
- background: c.bgAlt,
1497
- color: c.muted,
1498
- fontSize: 20,
1499
- cursor: "pointer"
1500
- },
1501
- children: "×"
1502
- }
1503
- )
1504
- ]
1505
- }
1506
- ),
1507
- code && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 20 }, children: [
1508
- /* @__PURE__ */ jsxRuntime.jsxs(
1509
- "button",
1510
- {
1511
- onClick: () => setIsSourcePreviewExpanded(!isSourcePreviewExpanded),
1512
- style: {
1513
- fontSize: 11,
1514
- fontWeight: 600,
1515
- marginBottom: 8,
1516
- color: c.muted,
1517
- textTransform: "uppercase",
1518
- background: "transparent",
1519
- border: "none",
1520
- padding: 0,
1521
- cursor: "pointer",
1522
- display: "flex",
1523
- alignItems: "center",
1524
- gap: 6
1525
- },
1526
- children: [
1527
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 10 }, children: isSourcePreviewExpanded ? "▼" : "▶" }),
1528
- "Source Preview"
1529
- ]
1530
- }
1531
- ),
1532
- isSourcePreviewExpanded && /* @__PURE__ */ jsxRuntime.jsx(
1533
- reactSyntaxHighlighter.Prism,
1534
- {
1535
- language: "tsx",
1536
- style: isDark ? prism.vscDarkPlus : prism.vs,
1537
- showLineNumbers: true,
1538
- startingLineNumber: codeLineStart,
1539
- customStyle: {
1540
- background: c.bgAlt,
1541
- borderRadius: 10,
1542
- fontSize: 14,
1543
- margin: 0,
1544
- maxHeight: 180,
1545
- overflowX: "auto",
1546
- border: `1px solid ${c.border}`
1547
- },
1548
- lineNumberStyle: {
1549
- minWidth: "2.5em",
1550
- paddingRight: "1em",
1551
- color: c.muted,
1552
- textAlign: "right",
1553
- userSelect: "none"
1554
- },
1555
- wrapLines: true,
1556
- lineProps: (lineNumber) => {
1557
- const isHighlighted = targetStartLine !== null && targetEndLine !== null && lineNumber >= targetStartLine && lineNumber <= targetEndLine;
1558
- return {
1559
- style: {
1560
- backgroundColor: isHighlighted ? isDark ? "rgba(102, 126, 234, 0.15)" : "rgba(147, 197, 253, 0.3)" : "transparent"
1561
- }
1562
- };
1563
- },
1564
- children: code
1565
- }
1566
- )
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
- ] }),
1633
- editHistory.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 20 }, children: [
1634
- /* @__PURE__ */ jsxRuntime.jsxs(
1635
- "button",
1636
- {
1637
- onClick: () => setIsEditHistoryExpanded(!isEditHistoryExpanded),
1638
- style: {
1639
- fontSize: 11,
1640
- fontWeight: 600,
1641
- marginBottom: 8,
1642
- color: c.muted,
1643
- textTransform: "uppercase",
1644
- background: "transparent",
1645
- border: "none",
1646
- padding: 0,
1647
- cursor: "pointer",
1648
- display: "flex",
1649
- alignItems: "center",
1650
- gap: 6
1651
- },
1652
- children: [
1653
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 10 }, children: isEditHistoryExpanded ? "▼" : "▶" }),
1654
- "Edit History (",
1655
- editHistory.length,
1656
- ")"
1657
- ]
1658
- }
1659
- ),
1660
- isEditHistoryExpanded && /* @__PURE__ */ jsxRuntime.jsx(
1661
- "div",
1662
- {
1663
- style: {
1664
- background: c.bgAlt,
1665
- borderRadius: 10,
1666
- border: `1px solid ${c.border}`,
1667
- maxHeight: 200,
1668
- overflow: "auto"
1669
- },
1670
- children: editHistory.map((item, idx) => {
1671
- const isLastEdit = idx === editHistory.length - 1;
1672
- const hasSnapshot = item.success && item.fileSnapshot;
1673
- return /* @__PURE__ */ jsxRuntime.jsx(
1674
- "div",
1675
- {
1676
- style: {
1677
- padding: "10px 14px",
1678
- borderBottom: idx < editHistory.length - 1 ? `1px solid ${c.border}` : "none"
1679
- },
1680
- children: /* @__PURE__ */ jsxRuntime.jsxs(
1681
- "div",
1682
- {
1683
- style: {
1684
- display: "flex",
1685
- alignItems: "flex-start",
1686
- gap: 8
1687
- },
1688
- children: [
1689
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1 }, children: [
1690
- /* @__PURE__ */ jsxRuntime.jsxs(
1691
- "div",
1692
- {
1693
- style: {
1694
- display: "flex",
1695
- alignItems: "center",
1696
- gap: 8,
1697
- marginBottom: 4
1698
- },
1699
- children: [
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(
1713
- "span",
1714
- {
1715
- style: {
1716
- color: item.success ? c.success : c.error,
1717
- fontSize: 16
1718
- },
1719
- children: item.success ? "✓" : "✗"
1720
- }
1721
- ),
1722
- /* @__PURE__ */ jsxRuntime.jsxs(
1723
- "span",
1724
- {
1725
- style: {
1726
- fontSize: 11,
1727
- color: c.muted
1728
- },
1729
- children: [
1730
- "Edit ",
1731
- idx + 1
1732
- ]
1733
- }
1734
- )
1735
- ]
1736
- }
1737
- ),
1738
- /* @__PURE__ */ jsxRuntime.jsx(
1739
- "div",
1740
- {
1741
- style: {
1742
- fontSize: 13,
1743
- color: c.text,
1744
- marginLeft: 24
1745
- },
1746
- children: item.suggestion
1747
- }
1748
- ),
1749
- item.error && /* @__PURE__ */ jsxRuntime.jsx(
1750
- "div",
1751
- {
1752
- style: {
1753
- fontSize: 11,
1754
- color: c.error,
1755
- marginLeft: 24,
1756
- marginTop: 4
1757
- },
1758
- children: item.error
1759
- }
1760
- )
1761
- ] }),
1762
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 6, flexWrap: "wrap" }, children: [
1763
- isLastEdit && hasSnapshot && !item.pending && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1764
- /* @__PURE__ */ jsxRuntime.jsx(
1765
- "button",
1766
- {
1767
- onClick: handleUndo,
1768
- disabled: isLoading,
1769
- style: {
1770
- padding: "4px 10px",
1771
- fontSize: 11,
1772
- borderRadius: 6,
1773
- border: "none",
1774
- background: isLoading ? c.muted : "#f59e0b",
1775
- color: "#fff",
1776
- fontWeight: 600,
1777
- cursor: isLoading ? "wait" : "pointer",
1778
- whiteSpace: "nowrap"
1779
- },
1780
- title: "Undo this edit",
1781
- onMouseEnter: (e) => {
1782
- if (!isLoading)
1783
- e.currentTarget.style.background = "#d97706";
1784
- },
1785
- onMouseLeave: (e) => {
1786
- if (!isLoading)
1787
- e.currentTarget.style.background = "#f59e0b";
1788
- },
1789
- children: "↶ Undo"
1790
- }
1791
- ),
1792
- /* @__PURE__ */ jsxRuntime.jsx(
1793
- "button",
1794
- {
1795
- onClick: handleUndoAndRetry,
1796
- disabled: isLoading,
1797
- style: {
1798
- padding: "4px 10px",
1799
- fontSize: 11,
1800
- borderRadius: 6,
1801
- border: "none",
1802
- background: isLoading ? c.muted : "#8b5cf6",
1803
- color: "#fff",
1804
- fontWeight: 600,
1805
- cursor: isLoading ? "wait" : "pointer",
1806
- whiteSpace: "nowrap"
1807
- },
1808
- title: "Undo and retry with AI",
1809
- onMouseEnter: (e) => {
1810
- if (!isLoading)
1811
- e.currentTarget.style.background = "#7c3aed";
1812
- },
1813
- onMouseLeave: (e) => {
1814
- if (!isLoading)
1815
- e.currentTarget.style.background = "#8b5cf6";
1816
- },
1817
- children: "↶↻ Undo & Retry"
1818
- }
1819
- )
1820
- ] }),
1821
- /* @__PURE__ */ jsxRuntime.jsx(
1822
- "button",
1823
- {
1824
- onClick: () => handleRetry(idx),
1825
- disabled: isLoading || item.pending,
1826
- style: {
1827
- padding: "4px 10px",
1828
- fontSize: 11,
1829
- borderRadius: 6,
1830
- border: "none",
1831
- background: isLoading || item.pending ? c.muted : c.accent,
1832
- color: "#fff",
1833
- fontWeight: 600,
1834
- cursor: isLoading || item.pending ? "wait" : "pointer",
1835
- whiteSpace: "nowrap"
1836
- },
1837
- title: "Retry this edit",
1838
- onMouseEnter: (e) => {
1839
- if (!isLoading && !item.pending)
1840
- e.currentTarget.style.background = "#6366f1";
1841
- },
1842
- onMouseLeave: (e) => {
1843
- if (!isLoading && !item.pending)
1844
- e.currentTarget.style.background = c.accent;
1845
- },
1846
- children: "↻ Retry"
1847
- }
1848
- )
1849
- ] })
1850
- ]
1851
- }
1852
- )
1853
- },
1854
- idx
1855
- );
1856
- })
1857
- }
1858
- )
1859
- ] }),
1860
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 20 }, children: [
1861
- /* @__PURE__ */ jsxRuntime.jsx(
1862
- "div",
1863
- {
1864
- style: {
1865
- fontSize: 11,
1866
- fontWeight: 600,
1867
- marginBottom: 8,
1868
- color: c.muted,
1869
- textTransform: "uppercase"
1870
- },
1871
- children: "Describe Changes"
1872
- }
1873
- ),
1874
- /* @__PURE__ */ jsxRuntime.jsx(
1875
- "textarea",
1876
- {
1877
- value: suggestion,
1878
- onChange: (e) => setSuggestion(e.target.value),
1879
- placeholder: `e.g., Make this ${(elCtx == null ? void 0 : elCtx.tagName) || "element"} blue with rounded corners...`,
1880
- autoFocus: true,
1881
- style: {
1882
- width: "100%",
1883
- height: 100,
1884
- padding: 14,
1885
- borderRadius: 10,
1886
- border: `1px solid ${c.border}`,
1887
- background: c.bgAlt,
1888
- color: c.text,
1889
- fontSize: 14,
1890
- resize: "vertical",
1891
- fontFamily: "inherit",
1892
- boxSizing: "border-box"
1893
- },
1894
- onKeyDown: (e) => {
1895
- if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
1896
- e.preventDefault();
1897
- handleSubmit();
1898
- }
1899
- }
1900
- }
1901
- )
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
- ] }),
2041
- result && /* @__PURE__ */ jsxRuntime.jsx(
2042
- "div",
2043
- {
2044
- style: {
2045
- marginBottom: 20,
2046
- padding: 14,
2047
- borderRadius: 10,
2048
- background: result.success ? `${c.success}15` : `${c.error}15`,
2049
- color: result.success ? c.success : c.error,
2050
- fontWeight: 500
2051
- },
2052
- children: result.success ? "✓ Applied!" : `✗ ${result.error}`
2053
- }
2054
- ),
2055
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 12, flexWrap: "wrap" }, children: [
2056
- /* @__PURE__ */ jsxRuntime.jsx(
2057
- "button",
2058
- {
2059
- onClick: () => setSelectedSource(null),
2060
- style: {
2061
- flex: "1 1 auto",
2062
- padding: "12px 20px",
2063
- borderRadius: 10,
2064
- border: `1px solid ${c.border}`,
2065
- background: "transparent",
2066
- color: c.text,
2067
- cursor: "pointer"
2068
- },
2069
- children: "Cancel"
2070
- }
2071
- ),
2072
- ENABLE_SESSION_PERSISTENCE,
2073
- editHistory.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
2074
- "button",
2075
- {
2076
- onClick: handleDone,
2077
- style: {
2078
- flex: "1 1 auto",
2079
- padding: "12px 20px",
2080
- borderRadius: 10,
2081
- border: `1px solid ${c.success}`,
2082
- background: "transparent",
2083
- color: c.success,
2084
- fontWeight: 600,
2085
- cursor: "pointer"
2086
- },
2087
- children: "Finish & Reload"
2088
- }
2089
- ),
2090
- /* @__PURE__ */ jsxRuntime.jsx(
2091
- "button",
2092
- {
2093
- onClick: handleSubmit,
2094
- disabled: isLoading || !suggestion.trim(),
2095
- style: {
2096
- flex: "2 1 auto",
2097
- padding: "12px 20px",
2098
- borderRadius: 10,
2099
- border: "none",
2100
- background: isLoading ? c.muted : c.accent,
2101
- color: "#fff",
2102
- fontWeight: 600,
2103
- cursor: isLoading ? "wait" : "pointer",
2104
- opacity: !suggestion.trim() ? 0.5 : 1
2105
- },
2106
- children: isLoading ? "Applying..." : "Apply Changes ⌘↵"
2107
- }
2108
- )
2109
- ] })
2110
- ]
2111
- }
2112
- ),
2113
- !selectedSource && /* @__PURE__ */ jsxRuntime.jsxs(
2114
- "div",
2115
- {
2116
- style: {
2117
- position: "fixed",
2118
- bottom: 20,
2119
- right: 20,
2120
- background: c.bg,
2121
- color: c.success,
2122
- padding: "10px 16px",
2123
- borderRadius: 20,
2124
- fontSize: 13,
2125
- fontWeight: 600,
2126
- zIndex: 99997,
2127
- display: "flex",
2128
- alignItems: "center",
2129
- gap: 8,
2130
- border: `1px solid ${c.border}`,
2131
- boxShadow: `0 4px 20px rgba(0,0,0,${isDark ? 0.4 : 0.1})`
2132
- },
2133
- children: [
2134
- /* @__PURE__ */ jsxRuntime.jsx(
2135
- "span",
2136
- {
2137
- style: {
2138
- width: 8,
2139
- height: 8,
2140
- borderRadius: "50%",
2141
- background: c.success
2142
- }
2143
- }
2144
- ),
2145
- "AI Editor",
2146
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: c.muted, fontSize: 11 }, children: "⌘⇧E" }),
2147
- ENABLE_SESSION_PERSISTENCE,
2148
- /* @__PURE__ */ jsxRuntime.jsx(
2149
- "button",
2150
- {
2151
- onClick: () => setEnabled(false),
2152
- style: {
2153
- background: "none",
2154
- border: "none",
2155
- color: c.muted,
2156
- cursor: "pointer"
2157
- },
2158
- children: "×"
2159
- }
2160
- )
2161
- ]
2162
- }
2163
- )
2164
- ]
2165
- }
2166
- );
2167
- }
2168
- exports.AIEditorProvider = AIEditorProvider;
2169
- //# sourceMappingURL=AIEditorProvider-CmiACRfw.cjs.map