next-ai-editor 0.2.6 → 1.0.1

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 (97) hide show
  1. package/README.md +2 -2
  2. package/dist/index.cjs +2115 -34
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.ts +189 -4
  5. package/dist/index.js +2115 -34
  6. package/dist/index.js.map +1 -1
  7. package/dist/next-ai-editor.css +489 -112
  8. package/package.json +4 -22
  9. package/client/package.json +0 -5
  10. package/dist/AIEditorProvider-CLgf1Vwa.cjs +0 -1488
  11. package/dist/AIEditorProvider-CLgf1Vwa.cjs.map +0 -1
  12. package/dist/AIEditorProvider-DWId5Qmv.js +0 -1489
  13. package/dist/AIEditorProvider-DWId5Qmv.js.map +0 -1
  14. package/dist/client/AIEditorProvider.d.ts +0 -21
  15. package/dist/client/AIEditorProvider.d.ts.map +0 -1
  16. package/dist/client/components/ChatPanel.d.ts +0 -18
  17. package/dist/client/components/ChatPanel.d.ts.map +0 -1
  18. package/dist/client/components/ControlPill.d.ts +0 -8
  19. package/dist/client/components/ControlPill.d.ts.map +0 -1
  20. package/dist/client/components/MessageItem.d.ts +0 -7
  21. package/dist/client/components/MessageItem.d.ts.map +0 -1
  22. package/dist/client/components/MessageList.d.ts +0 -7
  23. package/dist/client/components/MessageList.d.ts.map +0 -1
  24. package/dist/client/components/TaskHistoryPanel.d.ts +0 -10
  25. package/dist/client/components/TaskHistoryPanel.d.ts.map +0 -1
  26. package/dist/client/components/index.d.ts +0 -11
  27. package/dist/client/components/index.d.ts.map +0 -1
  28. package/dist/client/fiber-utils.d.ts +0 -35
  29. package/dist/client/fiber-utils.d.ts.map +0 -1
  30. package/dist/client/hooks/index.d.ts +0 -3
  31. package/dist/client/hooks/index.d.ts.map +0 -1
  32. package/dist/client/hooks/useChatStream.d.ts +0 -66
  33. package/dist/client/hooks/useChatStream.d.ts.map +0 -1
  34. package/dist/client/hooks/useHotReload.d.ts +0 -10
  35. package/dist/client/hooks/useHotReload.d.ts.map +0 -1
  36. package/dist/client/index.d.ts +0 -6
  37. package/dist/client/index.d.ts.map +0 -1
  38. package/dist/client/query-params.d.ts +0 -9
  39. package/dist/client/query-params.d.ts.map +0 -1
  40. package/dist/client.cjs +0 -12
  41. package/dist/client.cjs.map +0 -1
  42. package/dist/client.js +0 -12
  43. package/dist/client.js.map +0 -1
  44. package/dist/comments-BQ6AQ-eC.cjs +0 -1570
  45. package/dist/comments-BQ6AQ-eC.cjs.map +0 -1
  46. package/dist/comments-CjBQxjDP.js +0 -1553
  47. package/dist/comments-CjBQxjDP.js.map +0 -1
  48. package/dist/index.d.ts.map +0 -1
  49. package/dist/path-utils-Bai2xKx9.js +0 -36
  50. package/dist/path-utils-Bai2xKx9.js.map +0 -1
  51. package/dist/path-utils-DYzEWUGy.cjs +0 -35
  52. package/dist/path-utils-DYzEWUGy.cjs.map +0 -1
  53. package/dist/server/agent/sdk-client.d.ts +0 -54
  54. package/dist/server/agent/sdk-client.d.ts.map +0 -1
  55. package/dist/server/agent/session-store.d.ts +0 -101
  56. package/dist/server/agent/session-store.d.ts.map +0 -1
  57. package/dist/server/handlers/absolute-path.d.ts +0 -3
  58. package/dist/server/handlers/absolute-path.d.ts.map +0 -1
  59. package/dist/server/handlers/chat.d.ts +0 -6
  60. package/dist/server/handlers/chat.d.ts.map +0 -1
  61. package/dist/server/handlers/comments.d.ts +0 -10
  62. package/dist/server/handlers/comments.d.ts.map +0 -1
  63. package/dist/server/handlers/config.d.ts +0 -7
  64. package/dist/server/handlers/config.d.ts.map +0 -1
  65. package/dist/server/handlers/index.d.ts +0 -18
  66. package/dist/server/handlers/index.d.ts.map +0 -1
  67. package/dist/server/handlers/pages-router.d.ts +0 -21
  68. package/dist/server/handlers/pages-router.d.ts.map +0 -1
  69. package/dist/server/handlers/read.d.ts +0 -3
  70. package/dist/server/handlers/read.d.ts.map +0 -1
  71. package/dist/server/handlers/resolve.d.ts +0 -3
  72. package/dist/server/handlers/resolve.d.ts.map +0 -1
  73. package/dist/server/handlers/undo.d.ts +0 -3
  74. package/dist/server/handlers/undo.d.ts.map +0 -1
  75. package/dist/server/handlers/validate-session.d.ts +0 -3
  76. package/dist/server/handlers/validate-session.d.ts.map +0 -1
  77. package/dist/server/index.d.ts +0 -12
  78. package/dist/server/index.d.ts.map +0 -1
  79. package/dist/server/utils/ast.d.ts +0 -49
  80. package/dist/server/utils/ast.d.ts.map +0 -1
  81. package/dist/server/utils/file-system.d.ts +0 -24
  82. package/dist/server/utils/file-system.d.ts.map +0 -1
  83. package/dist/server/utils/source-map.d.ts +0 -39
  84. package/dist/server/utils/source-map.d.ts.map +0 -1
  85. package/dist/server.cjs +0 -29
  86. package/dist/server.cjs.map +0 -1
  87. package/dist/server.js +0 -29
  88. package/dist/server.js.map +0 -1
  89. package/dist/shared/comment-types.d.ts +0 -140
  90. package/dist/shared/comment-types.d.ts.map +0 -1
  91. package/dist/shared/path-utils.d.ts +0 -24
  92. package/dist/shared/path-utils.d.ts.map +0 -1
  93. package/dist/shared/storage.d.ts +0 -53
  94. package/dist/shared/storage.d.ts.map +0 -1
  95. package/dist/shared/types.d.ts +0 -74
  96. package/dist/shared/types.d.ts.map +0 -1
  97. package/server/package.json +0 -5
@@ -1,1489 +0,0 @@
1
- "use client";
2
- import { jsxs, jsx, Fragment } from "react/jsx-runtime";
3
- import { useRef, useEffect, useState, useCallback, createContext } from "react";
4
- import { c as cleanPath, s as shouldSkipPath } from "./path-utils-Bai2xKx9.js";
5
- import ReactMarkdown from "react-markdown";
6
- import remarkGfm from "remark-gfm";
7
- import { FileCode, Box, Type, X, Pencil } from "lucide-react";
8
- function getFiberFromElement(element) {
9
- const key = Object.keys(element).find(
10
- (k) => k.startsWith("__reactFiber$") || k.startsWith("__reactInternalInstance$")
11
- );
12
- return key ? element[key] : null;
13
- }
14
- function getComponentName(fiber) {
15
- var _a, _b;
16
- if (!fiber) return "Unknown";
17
- const type = fiber.type;
18
- if ((_a = fiber._debugSource) == null ? void 0 : _a.fileName) {
19
- const fileName = fiber._debugSource.fileName;
20
- const match = fileName.match(/\/([^/]+)\.(tsx?|jsx?)$/);
21
- if (match) return match[1];
22
- }
23
- if (typeof type === "string") return type;
24
- if (typeof type === "function")
25
- return type.displayName || type.name || "Anonymous";
26
- if (type == null ? void 0 : type.displayName) return type.displayName;
27
- if ((_b = type == null ? void 0 : type.render) == null ? void 0 : _b.name) return type.render.name;
28
- if (fiber._debugStack) {
29
- const stack = String(fiber._debugStack.stack || fiber._debugStack);
30
- const match = stack.match(/at\s+([A-Z][a-zA-Z0-9]*)\s*\(/);
31
- if (match && match[1] !== "Object") {
32
- return match[1];
33
- }
34
- }
35
- return "Unknown";
36
- }
37
- function isUserComponent(fiber) {
38
- if (fiber.tag === 5 || fiber.tag === 6 || fiber.tag === 3) return false;
39
- const type = fiber.type;
40
- if (!type || typeof type === "string") return false;
41
- return true;
42
- }
43
- function countNthOfType(element, tagName) {
44
- let boundary = element.parentElement;
45
- while (boundary) {
46
- const fiber = getFiberFromElement(boundary);
47
- if (fiber && isUserComponent(fiber)) break;
48
- boundary = boundary.parentElement;
49
- }
50
- if (!boundary) boundary = element.parentElement;
51
- if (!boundary) return 1;
52
- const sameTagElements = [];
53
- if (boundary.tagName.toLowerCase() === tagName.toLowerCase()) {
54
- sameTagElements.push(boundary);
55
- }
56
- sameTagElements.push(...Array.from(boundary.querySelectorAll(tagName)));
57
- let count = 1;
58
- for (const el of sameTagElements) {
59
- if (el === element) break;
60
- count++;
61
- }
62
- return count;
63
- }
64
- function extractProps(fiber) {
65
- if (!fiber) return void 0;
66
- const fiberProps = fiber.memoizedProps || fiber.pendingProps;
67
- if (!fiberProps || typeof fiberProps !== "object") return void 0;
68
- const props = {};
69
- const identifyingKeys = [
70
- "id",
71
- "name",
72
- "type",
73
- "href",
74
- "src",
75
- "alt",
76
- "role",
77
- "aria-label",
78
- "data-testid"
79
- ];
80
- for (const key of identifyingKeys) {
81
- if (key in fiberProps && typeof fiberProps[key] === "string") {
82
- props[key] = fiberProps[key];
83
- }
84
- }
85
- if (fiberProps.style && typeof fiberProps.style === "object") {
86
- props._styleKeys = Object.keys(fiberProps.style).slice(0, 5);
87
- }
88
- return Object.keys(props).length > 0 ? props : void 0;
89
- }
90
- function captureElementContext(element, fiber) {
91
- const tagName = element.tagName.toLowerCase();
92
- let textContent;
93
- const directText = Array.from(element.childNodes).filter((n) => n.nodeType === Node.TEXT_NODE).map((n) => {
94
- var _a;
95
- return (_a = n.textContent) == null ? void 0 : _a.trim();
96
- }).filter(Boolean).join(" ");
97
- if (directText) {
98
- textContent = directText.slice(0, 50);
99
- } else if (element.textContent) {
100
- textContent = element.textContent.trim().slice(0, 50);
101
- }
102
- const className = typeof element.className === "string" ? element.className.slice(0, 100) : void 0;
103
- const nthOfType = countNthOfType(element, tagName);
104
- const key = (fiber == null ? void 0 : fiber.key) ? String(fiber.key) : void 0;
105
- const props = extractProps(fiber);
106
- return { tagName, textContent, className, key, nthOfType, props };
107
- }
108
- function extractFromDebugStack(fiber) {
109
- const stack = fiber._debugStack;
110
- if (!stack) return null;
111
- const stackStr = stack.stack || String(stack);
112
- const frames = stackStr.split("\n");
113
- const skipPatterns = [
114
- "node_modules",
115
- "SegmentViewNode",
116
- "LayoutRouter",
117
- "ErrorBoundary",
118
- "fakeJSXCallSite"
119
- ];
120
- for (const frame of frames) {
121
- if (skipPatterns.some((p) => frame.includes(p))) continue;
122
- const match = frame.match(/at\s+(\w+)\s+\((.+?):(\d+):(\d+)\)?$/);
123
- if (match) {
124
- const fileName = cleanPath(match[2].replace(/\?[^:]*$/, ""));
125
- if (!shouldSkipPath(fileName, ["ai-editor-provider"])) {
126
- return {
127
- fileName,
128
- lineNumber: parseInt(match[3], 10),
129
- columnNumber: parseInt(match[4], 10)
130
- };
131
- }
132
- }
133
- }
134
- return null;
135
- }
136
- function extractFromDebugOwner(fiber) {
137
- var _a, _b;
138
- const owner = fiber._debugOwner;
139
- if (!owner) return null;
140
- if ((_a = owner._debugSource) == null ? void 0 : _a.fileName) {
141
- return owner._debugSource;
142
- }
143
- const stack = owner.stack;
144
- if (Array.isArray(stack) && ((_b = stack[0]) == null ? void 0 : _b.fileName)) {
145
- return stack[0];
146
- }
147
- return null;
148
- }
149
- function findSourceFromFiber(fiber) {
150
- if (!fiber) return null;
151
- let actualComponentName = null;
152
- if (fiber._debugOwner && typeof fiber._debugOwner === "object") {
153
- const debugOwner = fiber._debugOwner;
154
- if (debugOwner.name && typeof debugOwner.name === "string") {
155
- actualComponentName = debugOwner.name;
156
- }
157
- }
158
- let current = fiber;
159
- const visited = /* @__PURE__ */ new Set();
160
- let iterations = 0;
161
- while (current && !visited.has(current) && iterations < 10) {
162
- iterations++;
163
- visited.add(current);
164
- const sourceData = current._debugSource || extractFromDebugStack(current) || extractFromDebugOwner(current);
165
- if (sourceData == null ? void 0 : sourceData.fileName) {
166
- const filePath = cleanPath(sourceData.fileName);
167
- if (!shouldSkipPath(filePath, ["ai-editor-provider"])) {
168
- if (actualComponentName) {
169
- const rawDebugStack = current._debugStack ? String(current._debugStack.stack || current._debugStack) : void 0;
170
- return {
171
- filePath,
172
- lineNumber: sourceData.lineNumber || 1,
173
- columnNumber: sourceData.columnNumber || 0,
174
- componentName: actualComponentName,
175
- debugStack: rawDebugStack
176
- };
177
- } else {
178
- let componentFiber = current;
179
- while (componentFiber) {
180
- if (isUserComponent(componentFiber)) {
181
- const componentName = getComponentName(componentFiber);
182
- const isNextJSInternal = [
183
- "Segment",
184
- "Boundary",
185
- "Router",
186
- "Handler",
187
- "Context",
188
- "Layout",
189
- "Template",
190
- "Scroll",
191
- "Focus",
192
- "Loading",
193
- "Error"
194
- ].some((pattern) => componentName.includes(pattern));
195
- if (!isNextJSInternal) {
196
- const rawDebugStack = current._debugStack ? String(current._debugStack.stack || current._debugStack) : void 0;
197
- return {
198
- filePath,
199
- lineNumber: sourceData.lineNumber || 1,
200
- columnNumber: sourceData.columnNumber || 0,
201
- componentName,
202
- debugStack: rawDebugStack
203
- };
204
- }
205
- }
206
- componentFiber = componentFiber.return || componentFiber._debugOwner || null;
207
- }
208
- }
209
- }
210
- }
211
- current = current.return || current._debugOwner || null;
212
- }
213
- return null;
214
- }
215
- function findParentComponentFromFiber(childFiber, childComponentName) {
216
- if (!childFiber) return null;
217
- let childComponentFiber = null;
218
- if (childFiber._debugOwner && typeof childFiber._debugOwner === "object") {
219
- const debugOwner = childFiber._debugOwner;
220
- if (debugOwner.type && typeof debugOwner.type === "function") {
221
- const ownerComponentName = getComponentName(debugOwner);
222
- if (ownerComponentName === childComponentName) {
223
- childComponentFiber = debugOwner;
224
- }
225
- }
226
- }
227
- if (!childComponentFiber) {
228
- let current2 = childFiber;
229
- let iterations2 = 0;
230
- while (current2 && iterations2 < 20) {
231
- iterations2++;
232
- const componentName = getComponentName(current2);
233
- current2.index;
234
- if (isUserComponent(current2)) {
235
- if (componentName === childComponentName) {
236
- childComponentFiber = current2;
237
- break;
238
- }
239
- }
240
- if (current2._debugOwner && typeof current2._debugOwner === "object") {
241
- const debugOwner = current2._debugOwner;
242
- if (debugOwner.name === childComponentName && !debugOwner.type) {
243
- const parent = current2.return;
244
- const parentDebugOwner = parent == null ? void 0 : parent._debugOwner;
245
- const isRootElement = !parentDebugOwner || parentDebugOwner.name !== childComponentName;
246
- if (isRootElement) {
247
- childComponentFiber = current2;
248
- break;
249
- }
250
- }
251
- }
252
- current2 = current2.return;
253
- }
254
- }
255
- let childKey;
256
- if (childComponentFiber == null ? void 0 : childComponentFiber.key) {
257
- childKey = String(childComponentFiber.key);
258
- } else if (childComponentFiber && typeof childComponentFiber.index === "number") {
259
- childKey = String(childComponentFiber.index);
260
- }
261
- if (childFiber._debugOwner && typeof childFiber._debugOwner === "object") {
262
- const debugOwner = childFiber._debugOwner;
263
- if (debugOwner.owner && debugOwner.owner.name) {
264
- const parentName = debugOwner.owner.name;
265
- const parentDebugLocation = debugOwner.owner.debugLocation;
266
- if (parentDebugLocation) {
267
- const stack = String(parentDebugLocation.stack || parentDebugLocation);
268
- return {
269
- filePath: "",
270
- // Will be resolved on server
271
- lineNumber: 0,
272
- columnNumber: 0,
273
- componentName: parentName,
274
- debugStack: stack,
275
- childKey
276
- };
277
- }
278
- }
279
- if (debugOwner.type && typeof debugOwner.type === "function") {
280
- const componentFiberOwner = debugOwner._debugOwner;
281
- if (componentFiberOwner && typeof componentFiberOwner === "object") {
282
- const parentName = componentFiberOwner.name;
283
- const parentDebugLocation = componentFiberOwner.debugLocation;
284
- if (parentName && typeof parentName === "string") {
285
- if (parentDebugLocation) {
286
- const stack = String(
287
- parentDebugLocation.stack || parentDebugLocation
288
- );
289
- return {
290
- filePath: "",
291
- // Will be resolved on server
292
- lineNumber: 0,
293
- columnNumber: 0,
294
- componentName: parentName,
295
- debugStack: stack,
296
- childKey
297
- };
298
- }
299
- }
300
- }
301
- }
302
- }
303
- let current = childFiber.return;
304
- const visited = /* @__PURE__ */ new Set();
305
- let iterations = 0;
306
- while (current && !visited.has(current) && iterations < 20) {
307
- iterations++;
308
- visited.add(current);
309
- if (isUserComponent(current)) {
310
- const componentName = getComponentName(current);
311
- if (componentName === childComponentName) {
312
- current = current._debugOwner || null;
313
- continue;
314
- }
315
- const shouldSkipComponent = componentName.includes("AIEditorProvider") || // Skip the AI Editor wrapper itself
316
- componentName.startsWith("__next") || // Skip all Next.js internal components like __next_root_layout_boundary__
317
- componentName.startsWith("_") || // Skip internal components starting with underscore
318
- [
319
- "Segment",
320
- "Boundary",
321
- "Router",
322
- "Handler",
323
- "Context",
324
- "Layout",
325
- "Template",
326
- "Scroll",
327
- "Focus",
328
- "Loading",
329
- "Error",
330
- "RootLayout",
331
- // Skip root layout wrapper
332
- "NotFound"
333
- ].some((pattern) => componentName.includes(pattern));
334
- if (shouldSkipComponent) {
335
- current = current._debugOwner || null;
336
- continue;
337
- }
338
- const sourceData = current._debugSource || extractFromDebugStack(current) || extractFromDebugOwner(current);
339
- if (sourceData == null ? void 0 : sourceData.fileName) {
340
- const filePath = cleanPath(sourceData.fileName);
341
- if (!shouldSkipPath(filePath, ["ai-editor-provider"])) {
342
- const rawDebugStack = current._debugStack ? String(current._debugStack.stack || current._debugStack) : void 0;
343
- return {
344
- filePath,
345
- lineNumber: sourceData.lineNumber || 1,
346
- columnNumber: sourceData.columnNumber || 0,
347
- componentName,
348
- debugStack: rawDebugStack,
349
- childKey
350
- };
351
- }
352
- }
353
- }
354
- const nextOwner = current._debugOwner;
355
- current = nextOwner || null;
356
- }
357
- const childDebugStack = childFiber._debugStack ? String(childFiber._debugStack.stack || childFiber._debugStack) : null;
358
- if (childDebugStack) {
359
- return {
360
- filePath: "",
361
- // Will be resolved on server from debugStack
362
- lineNumber: 0,
363
- columnNumber: 0,
364
- componentName: "",
365
- // Will be determined on server
366
- debugStack: childDebugStack,
367
- // Use child's debugStack for server-side resolution
368
- childKey
369
- };
370
- }
371
- return null;
372
- }
373
- function getSourceFromElement(element) {
374
- var _a;
375
- let current = element;
376
- let elementWithSource = null;
377
- let fiberWithSource = null;
378
- while (current && current !== document.body) {
379
- const fiber = getFiberFromElement(current);
380
- if (fiber) {
381
- const hasSourceInfo = ((_a = fiber._debugSource) == null ? void 0 : _a.fileName) || fiber._debugStack || fiber._debugOwner && typeof fiber._debugOwner === "object";
382
- if (hasSourceInfo && !fiberWithSource) {
383
- elementWithSource = current;
384
- fiberWithSource = fiber;
385
- }
386
- }
387
- current = current.parentElement;
388
- }
389
- if (!fiberWithSource || !elementWithSource) return null;
390
- const source = findSourceFromFiber(fiberWithSource);
391
- if (!source) return null;
392
- const elementContext = captureElementContext(elementWithSource, fiberWithSource);
393
- const parentComponent = findParentComponentFromFiber(
394
- fiberWithSource,
395
- source.componentName
396
- );
397
- return {
398
- ...source,
399
- elementContext,
400
- parentComponent: parentComponent || void 0
401
- };
402
- }
403
- if (typeof window !== "undefined") {
404
- window.__getSource = getSourceFromElement;
405
- }
406
- function TaskHistoryPanel({
407
- isOpen,
408
- onClose,
409
- tasks,
410
- onTaskClick
411
- }) {
412
- if (!isOpen) {
413
- return null;
414
- }
415
- return /* @__PURE__ */ jsxs("div", { className: "task-history-panel ai-editor-ui", children: [
416
- /* @__PURE__ */ jsx("div", { className: "task-history-overlay", onClick: onClose }),
417
- /* @__PURE__ */ jsxs("div", { className: "task-history-content", children: [
418
- /* @__PURE__ */ jsxs("div", { className: "task-history-header", children: [
419
- /* @__PURE__ */ jsxs("div", { className: "header-title", children: [
420
- /* @__PURE__ */ jsx("span", { className: "header-icon", children: "📝" }),
421
- /* @__PURE__ */ jsx("h2", { children: "Task History" })
422
- ] }),
423
- /* @__PURE__ */ jsx(
424
- "button",
425
- {
426
- className: "close-btn",
427
- onClick: onClose,
428
- "aria-label": "Close",
429
- title: "Close (Esc)",
430
- children: "✕"
431
- }
432
- )
433
- ] }),
434
- /* @__PURE__ */ jsx("div", { className: "task-history-body", children: tasks.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "empty-state", children: [
435
- /* @__PURE__ */ jsx("div", { className: "empty-icon", children: "🎯" }),
436
- /* @__PURE__ */ jsx("h3", { children: "No tasks yet" }),
437
- /* @__PURE__ */ jsx("p", { children: "Create a comment and send a message to see AI actions here." })
438
- ] }) : /* @__PURE__ */ jsx("div", { className: "task-list", children: tasks.map((task) => /* @__PURE__ */ jsx(
439
- TaskItem,
440
- {
441
- task,
442
- onClick: () => onTaskClick == null ? void 0 : onTaskClick(task.id)
443
- },
444
- task.id
445
- )) }) })
446
- ] })
447
- ] });
448
- }
449
- function TaskItem({ task, onClick }) {
450
- const getStatusIcon = () => {
451
- switch (task.status) {
452
- case "pending":
453
- return "⏳";
454
- case "done":
455
- return "✅";
456
- case "failed":
457
- return "❌";
458
- default:
459
- return "⏳";
460
- }
461
- };
462
- const getStatusLabel = () => {
463
- switch (task.status) {
464
- case "pending":
465
- return "In Progress";
466
- case "done":
467
- return "Complete";
468
- case "failed":
469
- return "Failed";
470
- default:
471
- return "Unknown";
472
- }
473
- };
474
- const formatTime = (timestamp) => {
475
- const date = new Date(timestamp);
476
- const now = /* @__PURE__ */ new Date();
477
- const diffMs = now.getTime() - date.getTime();
478
- const diffMins = Math.floor(diffMs / 6e4);
479
- if (diffMins < 1) return "Just now";
480
- if (diffMins < 60) return `${diffMins}m ago`;
481
- const diffHours = Math.floor(diffMins / 60);
482
- if (diffHours < 24) return `${diffHours}h ago`;
483
- const diffDays = Math.floor(diffHours / 24);
484
- return `${diffDays}d ago`;
485
- };
486
- return /* @__PURE__ */ jsxs(
487
- "div",
488
- {
489
- className: `task-item status-${task.status}`,
490
- onClick,
491
- children: [
492
- /* @__PURE__ */ jsxs("div", { className: "task-header", children: [
493
- /* @__PURE__ */ jsxs("div", { className: "task-status", children: [
494
- /* @__PURE__ */ jsx("span", { className: "status-icon", children: getStatusIcon() }),
495
- /* @__PURE__ */ jsx("span", { className: "status-label", children: getStatusLabel() })
496
- ] }),
497
- /* @__PURE__ */ jsx("div", { className: "task-time", children: formatTime(task.createdAt) })
498
- ] }),
499
- /* @__PURE__ */ jsx("div", { className: "task-request", children: task.userRequest }),
500
- task.actions.length > 0 && /* @__PURE__ */ jsx("div", { className: "task-actions", children: task.actions.map((action, idx) => /* @__PURE__ */ jsxs("div", { className: "action-item", children: [
501
- action.type === "tool_use" && /* @__PURE__ */ jsxs("span", { className: "action-text", children: [
502
- "🔧 ",
503
- action.tool
504
- ] }),
505
- action.type === "result" && /* @__PURE__ */ jsxs("span", { className: "action-text", children: [
506
- "✓ ",
507
- action.result
508
- ] }),
509
- action.type === "error" && /* @__PURE__ */ jsxs("span", { className: "action-text error", children: [
510
- "⚠️ ",
511
- action.error
512
- ] })
513
- ] }, idx)) }),
514
- task.affectedFiles.length > 0 && /* @__PURE__ */ jsxs("div", { className: "task-files", children: [
515
- /* @__PURE__ */ jsx("span", { className: "files-label", children: "Files:" }),
516
- task.affectedFiles.map((file, idx) => /* @__PURE__ */ jsx("span", { className: "file-badge", children: file.split("/").pop() }, idx))
517
- ] })
518
- ]
519
- }
520
- );
521
- }
522
- function MessageItem({ message }) {
523
- var _a, _b, _c;
524
- if ("role" in message) {
525
- const commentMsg = message;
526
- if (commentMsg.role === "user") {
527
- return /* @__PURE__ */ jsx("div", { className: "message message-user", children: /* @__PURE__ */ jsx("div", { className: "message-content", children: /* @__PURE__ */ jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], children: commentMsg.content }) }) });
528
- }
529
- if (commentMsg.role === "assistant") {
530
- return /* @__PURE__ */ jsx("div", { className: "message message-assistant", children: /* @__PURE__ */ jsx("div", { className: "message-content", children: /* @__PURE__ */ jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], children: commentMsg.content }) }) });
531
- }
532
- }
533
- const chatMsg = message;
534
- if (chatMsg.type === "connected" || chatMsg.type === "system") {
535
- return null;
536
- }
537
- if (chatMsg.type === "user") {
538
- return /* @__PURE__ */ jsx("div", { className: "message message-user", children: /* @__PURE__ */ jsx("div", { className: "message-content", children: /* @__PURE__ */ jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], children: chatMsg.message }) }) });
539
- }
540
- if (chatMsg.type === "assistant") {
541
- const content = ((_c = (_b = (_a = chatMsg.message) == null ? void 0 : _a.content) == null ? void 0 : _b[0]) == null ? void 0 : _c.text) || "";
542
- return /* @__PURE__ */ jsx("div", { className: "message message-assistant", children: /* @__PURE__ */ jsx("div", { className: "message-content", children: /* @__PURE__ */ jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], children: content }) }) });
543
- }
544
- if (chatMsg.type === "tool_use") {
545
- const toolInfo = {
546
- read_file: { icon: "📖", description: "Reading code" },
547
- write_file: { icon: "✏️", description: "Writing changes" },
548
- edit_file: { icon: "✨", description: "Editing code" },
549
- search_files: { icon: "🔍", description: "Searching files" },
550
- list_files: { icon: "📁", description: "Listing files" },
551
- run_command: { icon: "⚙️", description: "Running command" }
552
- };
553
- const info = toolInfo[chatMsg.tool] || { icon: "🔧", description: "Working" };
554
- return /* @__PURE__ */ jsxs("div", { className: "message message-tool", children: [
555
- /* @__PURE__ */ jsx("span", { className: "tool-icon", children: info.icon }),
556
- /* @__PURE__ */ jsx("span", { className: "tool-name", children: info.description }),
557
- /* @__PURE__ */ jsxs("span", { className: "tool-description", children: [
558
- "• ",
559
- chatMsg.tool
560
- ] })
561
- ] });
562
- }
563
- if (chatMsg.type === "tool_result") {
564
- return /* @__PURE__ */ jsxs("div", { className: "message message-tool-result", children: [
565
- /* @__PURE__ */ jsx("div", { className: "message-avatar", children: "✓" }),
566
- /* @__PURE__ */ jsx("div", { className: "message-content", children: /* @__PURE__ */ jsxs("div", { className: "tool-result", children: [
567
- /* @__PURE__ */ jsx("strong", { children: "Tool result:" }),
568
- " ",
569
- chatMsg.tool,
570
- /* @__PURE__ */ jsxs("details", { className: "tool-details", children: [
571
- /* @__PURE__ */ jsx("summary", { children: "View output" }),
572
- /* @__PURE__ */ jsx("pre", { children: typeof chatMsg.output === "string" ? chatMsg.output : JSON.stringify(chatMsg.output, null, 2) })
573
- ] })
574
- ] }) })
575
- ] });
576
- }
577
- if (chatMsg.type === "result") {
578
- return /* @__PURE__ */ jsx("div", { className: "message message-result", children: /* @__PURE__ */ jsxs("div", { className: "message-content", children: [
579
- /* @__PURE__ */ jsxs("div", { className: "result-header", children: [
580
- /* @__PURE__ */ jsx("span", { className: "result-icon", children: "✓" }),
581
- /* @__PURE__ */ jsx("span", { className: "result-label", children: "Changes Applied" })
582
- ] }),
583
- chatMsg.result && /* @__PURE__ */ jsx("div", { className: "result-description", children: /* @__PURE__ */ jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], children: chatMsg.result }) }),
584
- chatMsg.total_cost_usd && /* @__PURE__ */ jsxs("div", { className: "result-cost", children: [
585
- "Cost: $",
586
- chatMsg.total_cost_usd.toFixed(4)
587
- ] })
588
- ] }) });
589
- }
590
- if (chatMsg.type === "error") {
591
- return /* @__PURE__ */ jsxs("div", { className: "message message-error", children: [
592
- /* @__PURE__ */ jsx("div", { className: "message-avatar", children: "❌" }),
593
- /* @__PURE__ */ jsxs("div", { className: "message-content", children: [
594
- /* @__PURE__ */ jsx("strong", { children: "Error:" }),
595
- " ",
596
- chatMsg.error
597
- ] })
598
- ] });
599
- }
600
- if (chatMsg.type === "done") {
601
- return /* @__PURE__ */ jsx("div", { className: "message message-done", children: /* @__PURE__ */ jsx("div", { className: "message-separator" }) });
602
- }
603
- const unknownMessage = message;
604
- return /* @__PURE__ */ jsxs("div", { className: "message message-unknown", children: [
605
- /* @__PURE__ */ jsx("div", { className: "message-avatar", children: "?" }),
606
- /* @__PURE__ */ jsx("div", { className: "message-content", children: /* @__PURE__ */ jsxs("details", { className: "tool-details", children: [
607
- /* @__PURE__ */ jsxs("summary", { children: [
608
- "Unknown message type: ",
609
- unknownMessage.type || "unknown"
610
- ] }),
611
- /* @__PURE__ */ jsx("pre", { children: JSON.stringify(message, null, 2) })
612
- ] }) })
613
- ] });
614
- }
615
- function MessageList({ messages, isStreaming }) {
616
- const messagesEndRef = useRef(null);
617
- const containerRef = useRef(null);
618
- useEffect(() => {
619
- if (messagesEndRef.current) {
620
- messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
621
- }
622
- }, [messages]);
623
- if (messages.length === 0) {
624
- return /* @__PURE__ */ jsx("div", { className: "message-list-empty", children: /* @__PURE__ */ jsxs("div", { className: "empty-state", children: [
625
- /* @__PURE__ */ jsx("div", { className: "empty-icon", children: "💬" }),
626
- /* @__PURE__ */ jsx("h3", { children: "Start a conversation" }),
627
- /* @__PURE__ */ jsx("p", { children: "Ask Claude to help you edit components, add features, or refactor code." })
628
- ] }) });
629
- }
630
- return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "message-list", children: [
631
- messages.map((message, index) => /* @__PURE__ */ jsx(MessageItem, { message }, index)),
632
- isStreaming && /* @__PURE__ */ jsxs("div", { className: "message message-streaming", children: [
633
- /* @__PURE__ */ jsx("div", { className: "message-avatar", children: "⋯" }),
634
- /* @__PURE__ */ jsx("div", { className: "message-content", children: /* @__PURE__ */ jsxs("div", { className: "streaming-indicator", children: [
635
- /* @__PURE__ */ jsx("span", { className: "dot" }),
636
- /* @__PURE__ */ jsx("span", { className: "dot" }),
637
- /* @__PURE__ */ jsx("span", { className: "dot" })
638
- ] }) })
639
- ] }),
640
- /* @__PURE__ */ jsx("div", { ref: messagesEndRef })
641
- ] });
642
- }
643
- function useChatStream(options) {
644
- const { threadId, sessionId: initialSessionId, onMessage, onError, onDone } = options;
645
- const [state, setState] = useState({
646
- messages: [],
647
- isConnected: false,
648
- isStreaming: false,
649
- error: null,
650
- sessionId: initialSessionId || null
651
- });
652
- const eventSourceRef = useRef(null);
653
- const abortControllerRef = useRef(null);
654
- const parseEventData = useCallback((data) => {
655
- try {
656
- return JSON.parse(data);
657
- } catch (error) {
658
- console.error("Failed to parse SSE data:", data, error);
659
- return null;
660
- }
661
- }, []);
662
- const sendMessage = useCallback(
663
- async (message, componentContext) => {
664
- var _a;
665
- if (eventSourceRef.current) {
666
- eventSourceRef.current.close();
667
- eventSourceRef.current = null;
668
- }
669
- if (abortControllerRef.current) {
670
- abortControllerRef.current.abort();
671
- }
672
- abortControllerRef.current = new AbortController();
673
- const userMessage = { type: "user", message };
674
- setState((prev) => ({
675
- ...prev,
676
- messages: [...prev.messages, userMessage],
677
- isStreaming: true,
678
- error: null
679
- }));
680
- try {
681
- const response = await fetch("/api/ai-editor/chat", {
682
- method: "POST",
683
- headers: {
684
- "Content-Type": "application/json"
685
- },
686
- body: JSON.stringify({
687
- threadId,
688
- sessionId: state.sessionId,
689
- message,
690
- componentContext
691
- }),
692
- signal: abortControllerRef.current.signal
693
- });
694
- if (!response.ok) {
695
- throw new Error(`HTTP error! status: ${response.status}`);
696
- }
697
- const reader = (_a = response.body) == null ? void 0 : _a.getReader();
698
- const decoder = new TextDecoder();
699
- if (!reader) {
700
- throw new Error("No response body");
701
- }
702
- let currentAssistantMessage = null;
703
- while (true) {
704
- const { done, value } = await reader.read();
705
- if (done) {
706
- break;
707
- }
708
- const chunk = decoder.decode(value, { stream: true });
709
- const lines = chunk.split("\n");
710
- for (const line of lines) {
711
- if (line.startsWith("data: ")) {
712
- const data = line.slice(6);
713
- const event = parseEventData(data);
714
- if (event) {
715
- if (event.type === "assistant") {
716
- if (currentAssistantMessage) {
717
- setState((prev) => {
718
- var _a2, _b, _c;
719
- const messages = [...prev.messages];
720
- const lastIndex = messages.length - 1;
721
- if (((_a2 = messages[lastIndex]) == null ? void 0 : _a2.type) === "assistant") {
722
- const existingContent = ((_b = messages[lastIndex].message) == null ? void 0 : _b.content) || [];
723
- const newContent = ((_c = event.message) == null ? void 0 : _c.content) || [];
724
- messages[lastIndex] = {
725
- ...messages[lastIndex],
726
- message: {
727
- ...messages[lastIndex].message,
728
- content: [...existingContent, ...newContent]
729
- }
730
- };
731
- }
732
- return { ...prev, messages };
733
- });
734
- } else {
735
- currentAssistantMessage = event;
736
- setState((prev) => ({
737
- ...prev,
738
- messages: [...prev.messages, event]
739
- }));
740
- }
741
- onMessage == null ? void 0 : onMessage(event);
742
- continue;
743
- }
744
- currentAssistantMessage = null;
745
- if (event.type === "tool_result" || event.type === "user") {
746
- continue;
747
- }
748
- if (event.type === "tool_use") {
749
- setState((prev) => ({
750
- ...prev,
751
- messages: [...prev.messages, event]
752
- }));
753
- onMessage == null ? void 0 : onMessage(event);
754
- continue;
755
- }
756
- if (event.type === "result") {
757
- setState((prev) => {
758
- var _a2, _b, _c;
759
- const lastMsg = prev.messages[prev.messages.length - 1];
760
- if ((lastMsg == null ? void 0 : lastMsg.type) === "assistant") {
761
- const lastText = ((_c = (_b = (_a2 = lastMsg.message) == null ? void 0 : _a2.content) == null ? void 0 : _b[0]) == null ? void 0 : _c.text) || "";
762
- const resultText = event.result || "";
763
- if (lastText.includes(resultText) || resultText.includes(lastText)) {
764
- return prev;
765
- }
766
- }
767
- return {
768
- ...prev,
769
- messages: [...prev.messages, event]
770
- };
771
- });
772
- onMessage == null ? void 0 : onMessage(event);
773
- continue;
774
- }
775
- if (event.type === "connected") {
776
- setState((prev) => ({
777
- ...prev,
778
- sessionId: event.sessionId,
779
- isConnected: true
780
- }));
781
- continue;
782
- }
783
- if (event.type === "done") {
784
- setState((prev) => ({
785
- ...prev,
786
- isStreaming: false,
787
- messages: [...prev.messages, event]
788
- }));
789
- onDone == null ? void 0 : onDone();
790
- continue;
791
- }
792
- if (event.type === "error") {
793
- setState((prev) => ({
794
- ...prev,
795
- isStreaming: false,
796
- error: event.error,
797
- messages: [...prev.messages, event]
798
- }));
799
- onError == null ? void 0 : onError(event.error);
800
- continue;
801
- }
802
- setState((prev) => ({
803
- ...prev,
804
- messages: [...prev.messages, event]
805
- }));
806
- onMessage == null ? void 0 : onMessage(event);
807
- }
808
- }
809
- }
810
- }
811
- } catch (error) {
812
- if (error.name === "AbortError") {
813
- return;
814
- }
815
- const errorMessage = error.message || "Failed to send message";
816
- setState((prev) => ({
817
- ...prev,
818
- isStreaming: false,
819
- error: errorMessage
820
- }));
821
- onError == null ? void 0 : onError(errorMessage);
822
- }
823
- },
824
- [threadId, state.sessionId, parseEventData, onMessage, onError, onDone]
825
- );
826
- const clearMessages = useCallback(() => {
827
- setState((prev) => ({
828
- ...prev,
829
- messages: []
830
- }));
831
- }, []);
832
- useEffect(() => {
833
- return () => {
834
- if (eventSourceRef.current) {
835
- eventSourceRef.current.close();
836
- }
837
- if (abortControllerRef.current) {
838
- abortControllerRef.current.abort();
839
- }
840
- };
841
- }, []);
842
- return {
843
- ...state,
844
- sendMessage,
845
- clearMessages
846
- };
847
- }
848
- function ChatPanel({
849
- isExpanded,
850
- onToggle,
851
- activeTool,
852
- onToolChange,
853
- attachedContext,
854
- onClearContext,
855
- theme = "dark"
856
- }) {
857
- const [message, setMessage] = useState("");
858
- const [threadId] = useState(() => `thread-${Date.now()}`);
859
- const textareaRef = useRef(null);
860
- const [contextRanges, setContextRanges] = useState([]);
861
- const [isMessagesCollapsed, setMessagesCollapsed] = useState(false);
862
- const { messages, isStreaming, sendMessage } = useChatStream({
863
- threadId
864
- });
865
- useEffect(() => {
866
- if (attachedContext) {
867
- setContextRanges((prev) => [...prev, {
868
- start: 0,
869
- end: 0,
870
- context: attachedContext
871
- }]);
872
- if (textareaRef.current) {
873
- textareaRef.current.focus();
874
- }
875
- onClearContext();
876
- }
877
- }, [attachedContext, onClearContext]);
878
- const handleInput = useCallback((e) => {
879
- const target = e.target;
880
- setMessage(target.value);
881
- target.style.height = "auto";
882
- target.style.height = Math.min(target.scrollHeight, 200) + "px";
883
- }, []);
884
- const handleSend = useCallback(async () => {
885
- if (!message.trim() || isStreaming) return;
886
- const messageText = message;
887
- const componentContext = contextRanges.length > 0 && contextRanges[0].context.type === "component" ? contextRanges[0].context.data : void 0;
888
- setMessage("");
889
- setContextRanges([]);
890
- if (textareaRef.current) {
891
- textareaRef.current.style.height = "auto";
892
- }
893
- await sendMessage(messageText, componentContext);
894
- }, [message, isStreaming, contextRanges, sendMessage]);
895
- const handleKeyDown = useCallback((e) => {
896
- if ((e.ctrlKey || e.metaKey) && e.key === "Enter") {
897
- e.preventDefault();
898
- handleSend();
899
- }
900
- if (e.key === "Escape") {
901
- e.preventDefault();
902
- onToggle();
903
- }
904
- }, [handleSend, onToggle]);
905
- const commentMessages = messages.filter(
906
- (msg) => msg.type === "user" || msg.type === "assistant" || msg.type === "tool_use" || msg.type === "tool_result"
907
- ).map((msg) => {
908
- var _a, _b, _c;
909
- if (msg.type === "user") {
910
- return { role: "user", content: msg.message };
911
- }
912
- if (msg.type === "assistant") {
913
- const content = typeof msg.message === "string" ? msg.message : ((_c = (_b = (_a = msg.message) == null ? void 0 : _a.content) == null ? void 0 : _b[0]) == null ? void 0 : _c.text) || "";
914
- return { role: "assistant", content };
915
- }
916
- return msg;
917
- });
918
- if (!isExpanded) {
919
- return null;
920
- }
921
- return /* @__PURE__ */ jsxs("div", { className: `chat-panel ai-editor-ui ${theme}`, children: [
922
- commentMessages.length > 0 && /* @__PURE__ */ jsxs("div", { className: `chat-panel-messages-container ${isMessagesCollapsed ? "collapsed" : "expanded"}`, children: [
923
- /* @__PURE__ */ jsxs(
924
- "div",
925
- {
926
- className: "chat-panel-messages-header",
927
- onClick: () => setMessagesCollapsed(!isMessagesCollapsed),
928
- children: [
929
- /* @__PURE__ */ jsxs("span", { className: "chat-panel-messages-title", children: [
930
- commentMessages.length,
931
- " message",
932
- commentMessages.length !== 1 ? "s" : ""
933
- ] }),
934
- /* @__PURE__ */ jsx("span", { className: "chat-panel-messages-toggle", children: "▼" })
935
- ]
936
- }
937
- ),
938
- /* @__PURE__ */ jsx("div", { className: "chat-panel-messages", children: /* @__PURE__ */ jsx(MessageList, { messages: commentMessages, isStreaming }) })
939
- ] }),
940
- /* @__PURE__ */ jsxs("div", { className: "chat-panel-input", children: [
941
- contextRanges.length > 0 && /* @__PURE__ */ jsx("div", { className: "context-pills-container", children: contextRanges.map((range, idx) => /* @__PURE__ */ jsxs("div", { className: "context-pill-inline", children: [
942
- /* @__PURE__ */ jsx("span", { children: range.context.displayText }),
943
- /* @__PURE__ */ jsx(
944
- "button",
945
- {
946
- className: "context-pill-remove",
947
- onClick: () => {
948
- setContextRanges((prev) => prev.filter((_, i) => i !== idx));
949
- },
950
- title: "Remove context",
951
- children: "×"
952
- }
953
- )
954
- ] }, idx)) }),
955
- /* @__PURE__ */ jsxs("div", { className: "input-row", children: [
956
- /* @__PURE__ */ jsx(
957
- "textarea",
958
- {
959
- ref: textareaRef,
960
- className: "message-input",
961
- placeholder: "Ask Claude to edit your code...",
962
- value: message,
963
- onChange: handleInput,
964
- onKeyDown: handleKeyDown,
965
- disabled: isStreaming,
966
- rows: 1
967
- }
968
- ),
969
- /* @__PURE__ */ jsx(
970
- "button",
971
- {
972
- className: "send-button",
973
- onClick: handleSend,
974
- disabled: !message.trim() || isStreaming,
975
- children: "↑"
976
- }
977
- )
978
- ] }),
979
- /* @__PURE__ */ jsxs("div", { className: "chat-panel-toolbar", children: [
980
- /* @__PURE__ */ jsxs("div", { className: "chat-panel-tools", children: [
981
- /* @__PURE__ */ jsx(
982
- "button",
983
- {
984
- className: `tool-button ${activeTool === "component" ? "active" : ""}`,
985
- onClick: () => onToolChange(activeTool === "component" ? null : "component"),
986
- title: "Select Component",
987
- children: /* @__PURE__ */ jsx(FileCode, { size: 16 })
988
- }
989
- ),
990
- /* @__PURE__ */ jsx(
991
- "button",
992
- {
993
- className: `tool-button ${activeTool === "area" ? "active" : ""}`,
994
- onClick: () => onToolChange(activeTool === "area" ? null : "area"),
995
- title: "Select Area",
996
- children: /* @__PURE__ */ jsx(Box, { size: 16 })
997
- }
998
- ),
999
- /* @__PURE__ */ jsx(
1000
- "button",
1001
- {
1002
- className: `tool-button ${activeTool === "text" ? "active" : ""}`,
1003
- onClick: () => onToolChange(activeTool === "text" ? null : "text"),
1004
- title: "Select Text",
1005
- children: /* @__PURE__ */ jsx(Type, { size: 16 })
1006
- }
1007
- )
1008
- ] }),
1009
- /* @__PURE__ */ jsx(
1010
- "button",
1011
- {
1012
- className: "close-button",
1013
- onClick: onToggle,
1014
- title: "Close (Esc)",
1015
- children: /* @__PURE__ */ jsx(X, { size: 16 })
1016
- }
1017
- )
1018
- ] })
1019
- ] })
1020
- ] });
1021
- }
1022
- function ControlPill({
1023
- isExpanded,
1024
- onToggle,
1025
- activeTool
1026
- }) {
1027
- useEffect(() => {
1028
- const handleKeyDown = (e) => {
1029
- if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) {
1030
- return;
1031
- }
1032
- if (e.key === " " || e.key === "/") {
1033
- e.preventDefault();
1034
- onToggle();
1035
- }
1036
- if (e.key === "Escape" && isExpanded) {
1037
- e.preventDefault();
1038
- onToggle();
1039
- }
1040
- };
1041
- window.addEventListener("keydown", handleKeyDown);
1042
- return () => window.removeEventListener("keydown", handleKeyDown);
1043
- }, [onToggle, isExpanded]);
1044
- if (isExpanded) {
1045
- return null;
1046
- }
1047
- return /* @__PURE__ */ jsxs("div", { className: "control-pill ai-editor-ui", onClick: onToggle, children: [
1048
- /* @__PURE__ */ jsxs("div", { className: "control-pill-inner", children: [
1049
- /* @__PURE__ */ jsx(Pencil, { size: 16, className: "pill-icon" }),
1050
- /* @__PURE__ */ jsx("span", { className: "pill-label", children: "Edit" }),
1051
- activeTool && /* @__PURE__ */ jsxs("span", { className: "active-tool-indicator", title: `${activeTool} tool active`, children: [
1052
- activeTool === "component" && "🎯",
1053
- activeTool === "area" && "⬛",
1054
- activeTool === "text" && "📝"
1055
- ] })
1056
- ] }),
1057
- /* @__PURE__ */ jsxs("div", { className: "control-pill-hint", children: [
1058
- "Press ",
1059
- /* @__PURE__ */ jsx("kbd", { children: "Space" }),
1060
- " or ",
1061
- /* @__PURE__ */ jsx("kbd", { children: "/" }),
1062
- " to toggle"
1063
- ] })
1064
- ] });
1065
- }
1066
- const sourceResolutionCache = /* @__PURE__ */ new Map();
1067
- const inflightSourceResolutions = /* @__PURE__ */ new Map();
1068
- function inferComponentNameFromPath(filePath) {
1069
- const fileName = filePath.split("/").pop() || "";
1070
- const nameWithoutExt = fileName.replace(/\.(tsx?|jsx?)$/, "");
1071
- return nameWithoutExt.charAt(0).toUpperCase() + nameWithoutExt.slice(1);
1072
- }
1073
- async function resolveSourceLocation(source) {
1074
- if (typeof window === "undefined" || process.env.NODE_ENV !== "development") {
1075
- return null;
1076
- }
1077
- if (!source.debugStack) {
1078
- console.warn("No debugStack available for resolution:", source);
1079
- return null;
1080
- }
1081
- const cacheKey = source.debugStack;
1082
- const cached = sourceResolutionCache.get(cacheKey);
1083
- if (cached) {
1084
- const resolved2 = { ...source, ...cached };
1085
- if (resolved2.componentName === "Unknown" && resolved2.filePath) {
1086
- resolved2.componentName = inferComponentNameFromPath(resolved2.filePath);
1087
- }
1088
- return resolved2;
1089
- }
1090
- let inflight = inflightSourceResolutions.get(cacheKey);
1091
- if (!inflight) {
1092
- inflight = fetch("/api/ai-editor/resolve", {
1093
- method: "POST",
1094
- headers: { "Content-Type": "application/json" },
1095
- body: JSON.stringify({ debugStack: cacheKey })
1096
- }).then(async (res) => {
1097
- if (!res.ok) {
1098
- console.error(`Resolve API error ${res.status}`);
1099
- return null;
1100
- }
1101
- const data = await res.json();
1102
- if ((data == null ? void 0 : data.success) && data.filePath && data.lineNumber) {
1103
- const resolved2 = {
1104
- filePath: data.filePath,
1105
- lineNumber: data.lineNumber,
1106
- columnNumber: typeof data.columnNumber === "number" ? data.columnNumber : source.columnNumber
1107
- };
1108
- sourceResolutionCache.set(cacheKey, resolved2);
1109
- return resolved2;
1110
- }
1111
- return null;
1112
- }).catch((err) => {
1113
- console.error("Error calling resolve API:", err);
1114
- return null;
1115
- }).finally(() => {
1116
- inflightSourceResolutions.delete(cacheKey);
1117
- });
1118
- inflightSourceResolutions.set(cacheKey, inflight);
1119
- }
1120
- const resolvedInfo = await inflight;
1121
- if (!resolvedInfo) return null;
1122
- const resolved = {
1123
- ...source,
1124
- filePath: resolvedInfo.filePath,
1125
- lineNumber: resolvedInfo.lineNumber,
1126
- columnNumber: resolvedInfo.columnNumber
1127
- };
1128
- if (resolved.componentName === "Unknown" && resolved.filePath) {
1129
- resolved.componentName = inferComponentNameFromPath(resolved.filePath);
1130
- }
1131
- return resolved;
1132
- }
1133
- const EditorContext = createContext(null);
1134
- function AIEditorProvider({
1135
- children,
1136
- theme = "dark",
1137
- enabled
1138
- }) {
1139
- const [serverEnabled, setServerEnabled] = useState(null);
1140
- useEffect(() => {
1141
- if (enabled !== void 0) return;
1142
- fetch("/api/ai-editor/config").then((res) => res.json()).then((data) => {
1143
- setServerEnabled(data.enabled === true);
1144
- }).catch(() => {
1145
- setServerEnabled(false);
1146
- });
1147
- }, [enabled]);
1148
- const isEditorEnabled = enabled ?? serverEnabled;
1149
- const [isEnabled, setEnabled] = useState(false);
1150
- const [isHistoryOpen, setHistoryOpen] = useState(false);
1151
- const [taskHistory, setTaskHistory] = useState([]);
1152
- const [isChatExpanded, setChatExpanded] = useState(false);
1153
- const [activeTool, setActiveTool] = useState(null);
1154
- const [attachedContext, setAttachedContext] = useState(null);
1155
- useCallback(
1156
- (taskUpdate) => {
1157
- setTaskHistory((prev) => {
1158
- const existing = prev.find((t) => t.id === taskUpdate.id);
1159
- if (existing) {
1160
- return prev.map(
1161
- (t) => t.id === taskUpdate.id ? { ...t, ...taskUpdate } : t
1162
- );
1163
- } else {
1164
- return [...prev, taskUpdate];
1165
- }
1166
- });
1167
- },
1168
- []
1169
- );
1170
- const handleChatToggle = useCallback(() => {
1171
- setChatExpanded((prev) => !prev);
1172
- }, []);
1173
- const handleToolChange = useCallback((tool) => {
1174
- setActiveTool(tool);
1175
- if (tool === null) {
1176
- setAttachedContext(null);
1177
- }
1178
- }, []);
1179
- const handleClearContext = useCallback(() => {
1180
- setAttachedContext(null);
1181
- setActiveTool(null);
1182
- }, []);
1183
- useEffect(() => {
1184
- const handleKey = (e) => {
1185
- if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key.toLowerCase() === "e") {
1186
- e.preventDefault();
1187
- setEnabled((p) => !p);
1188
- }
1189
- if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key.toLowerCase() === "h") {
1190
- e.preventDefault();
1191
- setHistoryOpen((p) => !p);
1192
- }
1193
- if (e.key === "Escape" && isHistoryOpen) {
1194
- e.preventDefault();
1195
- setHistoryOpen(false);
1196
- }
1197
- };
1198
- window.addEventListener("keydown", handleKey);
1199
- return () => window.removeEventListener("keydown", handleKey);
1200
- }, [isHistoryOpen]);
1201
- if (!isEditorEnabled) {
1202
- return /* @__PURE__ */ jsx(Fragment, { children });
1203
- }
1204
- return /* @__PURE__ */ jsxs(
1205
- EditorContext.Provider,
1206
- {
1207
- value: {
1208
- isEnabled,
1209
- setEnabled
1210
- },
1211
- children: [
1212
- children,
1213
- activeTool === "component" && /* @__PURE__ */ jsx(
1214
- EditorOverlay,
1215
- {
1216
- theme,
1217
- onComponentSelect: (source, position) => {
1218
- const contextData = {
1219
- type: "component",
1220
- data: source,
1221
- displayText: `${source.componentName} (${source.filePath}:${source.lineNumber})`
1222
- };
1223
- setAttachedContext(contextData);
1224
- setActiveTool(null);
1225
- if (!isChatExpanded) {
1226
- setChatExpanded(true);
1227
- }
1228
- }
1229
- }
1230
- ),
1231
- /* @__PURE__ */ jsxs(
1232
- "div",
1233
- {
1234
- className: `editor-container ai-editor-ui ${isChatExpanded ? "expanded" : "collapsed"} ${theme}`,
1235
- children: [
1236
- /* @__PURE__ */ jsx(
1237
- ChatPanel,
1238
- {
1239
- isExpanded: isChatExpanded,
1240
- onToggle: handleChatToggle,
1241
- activeTool,
1242
- onToolChange: handleToolChange,
1243
- attachedContext,
1244
- onClearContext: handleClearContext,
1245
- theme
1246
- }
1247
- ),
1248
- /* @__PURE__ */ jsx(
1249
- ControlPill,
1250
- {
1251
- isExpanded: isChatExpanded,
1252
- onToggle: handleChatToggle,
1253
- activeTool
1254
- }
1255
- )
1256
- ]
1257
- }
1258
- ),
1259
- /* @__PURE__ */ jsx(
1260
- TaskHistoryPanel,
1261
- {
1262
- isOpen: isHistoryOpen,
1263
- onClose: () => setHistoryOpen(false),
1264
- tasks: taskHistory
1265
- }
1266
- )
1267
- ]
1268
- }
1269
- );
1270
- }
1271
- function EditorOverlay({ theme, onComponentSelect }) {
1272
- var _a;
1273
- const [hoveredSource, setHoveredSource] = useState(
1274
- null
1275
- );
1276
- const [hoveredElement, setHoveredElement] = useState(null);
1277
- const [hoveredRect, setHoveredRect] = useState(null);
1278
- const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
1279
- const [isResolvingSource, setIsResolvingSource] = useState(false);
1280
- const lastHoverStateRef = useRef(null);
1281
- useEffect(() => {
1282
- if (hoveredSource && hoveredElement) {
1283
- lastHoverStateRef.current = {
1284
- source: hoveredSource,
1285
- element: hoveredElement
1286
- };
1287
- }
1288
- }, [hoveredSource, hoveredElement]);
1289
- const isDark = theme === "dark";
1290
- const c = {
1291
- text: isDark ? "#e4e4e7" : "#18181b",
1292
- muted: isDark ? "#71717a" : "#a1a1aa",
1293
- accent: "#818cf8",
1294
- bg: isDark ? "#0d0d14" : "#fff",
1295
- border: isDark ? "#27273f" : "#e4e4e7",
1296
- success: "#34d399"
1297
- };
1298
- useEffect(() => {
1299
- let lastEl = null;
1300
- let raf;
1301
- const onMove = (e) => {
1302
- setMousePos({ x: e.clientX, y: e.clientY });
1303
- cancelAnimationFrame(raf);
1304
- raf = requestAnimationFrame(() => {
1305
- const el = document.elementFromPoint(e.clientX, e.clientY);
1306
- if (!el || el.closest(".ai-editor-ui") || el === lastEl) return;
1307
- lastEl = el;
1308
- const source = getSourceFromElement(el);
1309
- if (source) {
1310
- setHoveredElement(el);
1311
- setHoveredRect(el.getBoundingClientRect());
1312
- 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/"));
1313
- if (isCompiledPath && source.debugStack) {
1314
- setIsResolvingSource(true);
1315
- resolveSourceLocation(source).then((resolved) => {
1316
- setIsResolvingSource(false);
1317
- setHoveredSource(resolved || source);
1318
- }).catch((err) => {
1319
- console.error("Error resolving source location:", err);
1320
- setIsResolvingSource(false);
1321
- setHoveredSource(source);
1322
- });
1323
- } else {
1324
- setIsResolvingSource(false);
1325
- setHoveredSource(source);
1326
- }
1327
- } else {
1328
- setHoveredSource(null);
1329
- setHoveredElement(null);
1330
- setHoveredRect(null);
1331
- setIsResolvingSource(false);
1332
- }
1333
- });
1334
- };
1335
- const onClick = async (e) => {
1336
- if (e.target.closest(".ai-editor-ui")) {
1337
- return;
1338
- }
1339
- const el = e.target;
1340
- const source = getSourceFromElement(el);
1341
- if (source) {
1342
- e.preventDefault();
1343
- e.stopPropagation();
1344
- 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/"));
1345
- if (isCompiledPath && source.debugStack) {
1346
- const resolved = await resolveSourceLocation(source);
1347
- onComponentSelect(resolved || source, { x: e.clientX, y: e.clientY });
1348
- } else {
1349
- onComponentSelect(source, { x: e.clientX, y: e.clientY });
1350
- }
1351
- }
1352
- };
1353
- document.addEventListener("mousemove", onMove, { passive: true });
1354
- document.addEventListener("click", onClick, true);
1355
- return () => {
1356
- document.removeEventListener("mousemove", onMove);
1357
- document.removeEventListener("click", onClick, true);
1358
- cancelAnimationFrame(raf);
1359
- };
1360
- }, [onComponentSelect]);
1361
- return /* @__PURE__ */ jsxs(
1362
- "div",
1363
- {
1364
- className: "ai-editor-ui",
1365
- style: { fontFamily: "system-ui, sans-serif" },
1366
- children: [
1367
- hoveredRect && /* @__PURE__ */ jsx(
1368
- "div",
1369
- {
1370
- style: {
1371
- position: "fixed",
1372
- left: hoveredRect.left - 2,
1373
- top: hoveredRect.top - 2,
1374
- width: hoveredRect.width + 4,
1375
- height: hoveredRect.height + 4,
1376
- border: `2px solid ${c.accent}`,
1377
- borderRadius: 6,
1378
- background: `${c.accent}15`,
1379
- pointerEvents: "none",
1380
- zIndex: 99998
1381
- }
1382
- }
1383
- ),
1384
- hoveredSource && !isResolvingSource && /* @__PURE__ */ jsxs(
1385
- "div",
1386
- {
1387
- style: {
1388
- position: "fixed",
1389
- left: Math.min(mousePos.x + 14, window.innerWidth - 340),
1390
- top: mousePos.y + 14,
1391
- background: c.bg,
1392
- color: c.text,
1393
- border: `1px solid ${c.border}`,
1394
- borderRadius: 10,
1395
- padding: "12px 16px",
1396
- fontSize: 12,
1397
- fontFamily: "ui-monospace, monospace",
1398
- zIndex: 99999,
1399
- boxShadow: `0 8px 30px rgba(0,0,0,${isDark ? 0.5 : 0.15})`,
1400
- maxWidth: 320,
1401
- pointerEvents: "none"
1402
- },
1403
- children: [
1404
- /* @__PURE__ */ jsxs(
1405
- "div",
1406
- {
1407
- style: {
1408
- fontWeight: 700,
1409
- color: c.accent,
1410
- marginBottom: 4,
1411
- fontSize: 14
1412
- },
1413
- children: [
1414
- "<",
1415
- hoveredSource.componentName,
1416
- " />"
1417
- ]
1418
- }
1419
- ),
1420
- /* @__PURE__ */ jsxs("div", { style: { color: c.muted, fontSize: 11 }, children: [
1421
- hoveredSource.filePath,
1422
- ":",
1423
- hoveredSource.lineNumber
1424
- ] }),
1425
- ((_a = hoveredSource.elementContext) == null ? void 0 : _a.textContent) && /* @__PURE__ */ jsxs(
1426
- "div",
1427
- {
1428
- style: {
1429
- color: c.muted,
1430
- fontSize: 10,
1431
- marginTop: 4,
1432
- fontStyle: "italic"
1433
- },
1434
- children: [
1435
- '"',
1436
- hoveredSource.elementContext.textContent,
1437
- '"'
1438
- ]
1439
- }
1440
- )
1441
- ]
1442
- }
1443
- ),
1444
- /* @__PURE__ */ jsxs(
1445
- "div",
1446
- {
1447
- style: {
1448
- position: "fixed",
1449
- bottom: 20,
1450
- right: 20,
1451
- background: c.bg,
1452
- color: c.success,
1453
- padding: "10px 16px",
1454
- borderRadius: 20,
1455
- fontSize: 13,
1456
- fontWeight: 600,
1457
- zIndex: 99997,
1458
- display: "flex",
1459
- alignItems: "center",
1460
- gap: 8,
1461
- border: `1px solid ${c.border}`,
1462
- boxShadow: `0 4px 20px rgba(0,0,0,${isDark ? 0.4 : 0.1})`
1463
- },
1464
- children: [
1465
- /* @__PURE__ */ jsx(
1466
- "span",
1467
- {
1468
- style: {
1469
- width: 8,
1470
- height: 8,
1471
- borderRadius: "50%",
1472
- background: c.success
1473
- }
1474
- }
1475
- ),
1476
- "Select Component"
1477
- ]
1478
- }
1479
- )
1480
- ]
1481
- }
1482
- );
1483
- }
1484
- export {
1485
- AIEditorProvider as A,
1486
- ChatPanel as C,
1487
- ControlPill as a
1488
- };
1489
- //# sourceMappingURL=AIEditorProvider-DWId5Qmv.js.map