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