next-ai-editor 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,7 @@ import { useRef, useEffect, useState, useCallback, createContext } from "react";
4
4
  import { c as cleanPath, s as shouldSkipPath } from "./path-utils-Bai2xKx9.js";
5
5
  import ReactMarkdown from "react-markdown";
6
6
  import remarkGfm from "remark-gfm";
7
- import { FileCode, Box, Type } from "lucide-react";
7
+ import { FileCode, Box, Type, X, Pencil } from "lucide-react";
8
8
  function getFiberFromElement(element) {
9
9
  const key = Object.keys(element).find(
10
10
  (k) => k.startsWith("__reactFiber$") || k.startsWith("__reactInternalInstance$")
@@ -858,6 +858,7 @@ function ChatPanel({
858
858
  const [threadId] = useState(() => `thread-${Date.now()}`);
859
859
  const textareaRef = useRef(null);
860
860
  const [contextRanges, setContextRanges] = useState([]);
861
+ const [isMessagesCollapsed, setMessagesCollapsed] = useState(false);
861
862
  const { messages, isStreaming, sendMessage } = useChatStream({
862
863
  threadId
863
864
  });
@@ -918,37 +919,25 @@ function ChatPanel({
918
919
  return null;
919
920
  }
920
921
  return /* @__PURE__ */ jsxs("div", { className: `chat-panel ai-editor-ui ${theme}`, children: [
921
- commentMessages.length > 0 && /* @__PURE__ */ jsx("div", { className: "chat-panel-messages", children: /* @__PURE__ */ jsx(MessageList, { messages: commentMessages, isStreaming }) }),
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
+ ] }),
922
940
  /* @__PURE__ */ jsxs("div", { className: "chat-panel-input", children: [
923
- /* @__PURE__ */ jsxs("div", { className: "chat-panel-tools", children: [
924
- /* @__PURE__ */ jsx(
925
- "button",
926
- {
927
- className: `tool-button ${activeTool === "component" ? "active" : ""}`,
928
- onClick: () => onToolChange(activeTool === "component" ? null : "component"),
929
- title: "Select Component",
930
- children: /* @__PURE__ */ jsx(FileCode, { size: 16 })
931
- }
932
- ),
933
- /* @__PURE__ */ jsx(
934
- "button",
935
- {
936
- className: `tool-button ${activeTool === "area" ? "active" : ""}`,
937
- onClick: () => onToolChange(activeTool === "area" ? null : "area"),
938
- title: "Select Area",
939
- children: /* @__PURE__ */ jsx(Box, { size: 16 })
940
- }
941
- ),
942
- /* @__PURE__ */ jsx(
943
- "button",
944
- {
945
- className: `tool-button ${activeTool === "text" ? "active" : ""}`,
946
- onClick: () => onToolChange(activeTool === "text" ? null : "text"),
947
- title: "Select Text",
948
- children: /* @__PURE__ */ jsx(Type, { size: 16 })
949
- }
950
- )
951
- ] }),
952
941
  contextRanges.length > 0 && /* @__PURE__ */ jsx("div", { className: "context-pills-container", children: contextRanges.map((range, idx) => /* @__PURE__ */ jsxs("div", { className: "context-pill-inline", children: [
953
942
  /* @__PURE__ */ jsx("span", { children: range.context.displayText }),
954
943
  /* @__PURE__ */ jsx(
@@ -987,11 +976,46 @@ function ChatPanel({
987
976
  }
988
977
  )
989
978
  ] }),
990
- /* @__PURE__ */ jsx("div", { className: "input-hint", children: activeTool ? /* @__PURE__ */ jsxs("span", { children: [
991
- "Select a ",
992
- activeTool,
993
- " to attach context"
994
- ] }) : /* @__PURE__ */ jsx("span", { children: "⌘↵ to send • Esc to close" }) })
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
+ ] })
995
1019
  ] })
996
1020
  ] });
997
1021
  }
@@ -1017,11 +1041,14 @@ function ControlPill({
1017
1041
  window.addEventListener("keydown", handleKeyDown);
1018
1042
  return () => window.removeEventListener("keydown", handleKeyDown);
1019
1043
  }, [onToggle, isExpanded]);
1044
+ if (isExpanded) {
1045
+ return null;
1046
+ }
1020
1047
  return /* @__PURE__ */ jsxs("div", { className: "control-pill ai-editor-ui", onClick: onToggle, children: [
1021
1048
  /* @__PURE__ */ jsxs("div", { className: "control-pill-inner", children: [
1022
- /* @__PURE__ */ jsx("span", { className: "pill-icon", children: "💬" }),
1023
- /* @__PURE__ */ jsx("span", { className: "pill-label", children: isExpanded ? "Close Chat" : "AI Editor" }),
1024
- activeTool && !isExpanded && /* @__PURE__ */ jsxs("span", { className: "active-tool-indicator", title: `${activeTool} tool active`, 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: [
1025
1052
  activeTool === "component" && "🎯",
1026
1053
  activeTool === "area" && "⬛",
1027
1054
  activeTool === "text" && "📝"
@@ -1106,26 +1133,40 @@ async function resolveSourceLocation(source) {
1106
1133
  const EditorContext = createContext(null);
1107
1134
  function AIEditorProvider({
1108
1135
  children,
1109
- theme = "dark"
1136
+ theme = "dark",
1137
+ enabled
1110
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;
1111
1149
  const [isEnabled, setEnabled] = useState(false);
1112
1150
  const [isHistoryOpen, setHistoryOpen] = useState(false);
1113
1151
  const [taskHistory, setTaskHistory] = useState([]);
1114
1152
  const [isChatExpanded, setChatExpanded] = useState(false);
1115
1153
  const [activeTool, setActiveTool] = useState(null);
1116
1154
  const [attachedContext, setAttachedContext] = useState(null);
1117
- useCallback((taskUpdate) => {
1118
- setTaskHistory((prev) => {
1119
- const existing = prev.find((t) => t.id === taskUpdate.id);
1120
- if (existing) {
1121
- return prev.map(
1122
- (t) => t.id === taskUpdate.id ? { ...t, ...taskUpdate } : t
1123
- );
1124
- } else {
1125
- return [...prev, taskUpdate];
1126
- }
1127
- });
1128
- }, []);
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
+ );
1129
1170
  const handleChatToggle = useCallback(() => {
1130
1171
  setChatExpanded((prev) => !prev);
1131
1172
  }, []);
@@ -1157,7 +1198,7 @@ function AIEditorProvider({
1157
1198
  window.addEventListener("keydown", handleKey);
1158
1199
  return () => window.removeEventListener("keydown", handleKey);
1159
1200
  }, [isHistoryOpen]);
1160
- if (process.env.NODE_ENV !== "development") {
1201
+ if (!isEditorEnabled) {
1161
1202
  return /* @__PURE__ */ jsx(Fragment, { children });
1162
1203
  }
1163
1204
  return /* @__PURE__ */ jsxs(
@@ -1187,24 +1228,32 @@ function AIEditorProvider({
1187
1228
  }
1188
1229
  }
1189
1230
  ),
1190
- /* @__PURE__ */ jsx(
1191
- ChatPanel,
1192
- {
1193
- isExpanded: isChatExpanded,
1194
- onToggle: handleChatToggle,
1195
- activeTool,
1196
- onToolChange: handleToolChange,
1197
- attachedContext,
1198
- onClearContext: handleClearContext,
1199
- theme
1200
- }
1201
- ),
1202
- /* @__PURE__ */ jsx(
1203
- ControlPill,
1231
+ /* @__PURE__ */ jsxs(
1232
+ "div",
1204
1233
  {
1205
- isExpanded: isChatExpanded,
1206
- onToggle: handleChatToggle,
1207
- activeTool
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
+ ]
1208
1257
  }
1209
1258
  ),
1210
1259
  /* @__PURE__ */ jsx(
@@ -1221,7 +1270,9 @@ function AIEditorProvider({
1221
1270
  }
1222
1271
  function EditorOverlay({ theme, onComponentSelect }) {
1223
1272
  var _a;
1224
- const [hoveredSource, setHoveredSource] = useState(null);
1273
+ const [hoveredSource, setHoveredSource] = useState(
1274
+ null
1275
+ );
1225
1276
  const [hoveredElement, setHoveredElement] = useState(null);
1226
1277
  const [hoveredRect, setHoveredRect] = useState(null);
1227
1278
  const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
@@ -1229,7 +1280,10 @@ function EditorOverlay({ theme, onComponentSelect }) {
1229
1280
  const lastHoverStateRef = useRef(null);
1230
1281
  useEffect(() => {
1231
1282
  if (hoveredSource && hoveredElement) {
1232
- lastHoverStateRef.current = { source: hoveredSource, element: hoveredElement };
1283
+ lastHoverStateRef.current = {
1284
+ source: hoveredSource,
1285
+ element: hoveredElement
1286
+ };
1233
1287
  }
1234
1288
  }, [hoveredSource, hoveredElement]);
1235
1289
  const isDark = theme === "dark";
@@ -1304,125 +1358,132 @@ function EditorOverlay({ theme, onComponentSelect }) {
1304
1358
  cancelAnimationFrame(raf);
1305
1359
  };
1306
1360
  }, [onComponentSelect]);
1307
- return /* @__PURE__ */ jsxs("div", { className: "ai-editor-ui", style: { fontFamily: "system-ui, sans-serif" }, children: [
1308
- hoveredRect && /* @__PURE__ */ jsx(
1309
- "div",
1310
- {
1311
- style: {
1312
- position: "fixed",
1313
- left: hoveredRect.left - 2,
1314
- top: hoveredRect.top - 2,
1315
- width: hoveredRect.width + 4,
1316
- height: hoveredRect.height + 4,
1317
- border: `2px solid ${c.accent}`,
1318
- borderRadius: 6,
1319
- background: `${c.accent}15`,
1320
- pointerEvents: "none",
1321
- zIndex: 99998
1322
- }
1323
- }
1324
- ),
1325
- hoveredSource && !isResolvingSource && /* @__PURE__ */ jsxs(
1326
- "div",
1327
- {
1328
- style: {
1329
- position: "fixed",
1330
- left: Math.min(mousePos.x + 14, window.innerWidth - 340),
1331
- top: mousePos.y + 14,
1332
- background: c.bg,
1333
- color: c.text,
1334
- border: `1px solid ${c.border}`,
1335
- borderRadius: 10,
1336
- padding: "12px 16px",
1337
- fontSize: 12,
1338
- fontFamily: "ui-monospace, monospace",
1339
- zIndex: 99999,
1340
- boxShadow: `0 8px 30px rgba(0,0,0,${isDark ? 0.5 : 0.15})`,
1341
- maxWidth: 320,
1342
- pointerEvents: "none"
1343
- },
1344
- children: [
1345
- /* @__PURE__ */ jsxs(
1346
- "div",
1347
- {
1348
- style: {
1349
- fontWeight: 700,
1350
- color: c.accent,
1351
- marginBottom: 4,
1352
- fontSize: 14
1353
- },
1354
- children: [
1355
- "<",
1356
- hoveredSource.componentName,
1357
- " />"
1358
- ]
1359
- }
1360
- ),
1361
- /* @__PURE__ */ jsxs("div", { style: { color: c.muted, fontSize: 11 }, children: [
1362
- hoveredSource.filePath,
1363
- ":",
1364
- hoveredSource.lineNumber
1365
- ] }),
1366
- ((_a = hoveredSource.elementContext) == null ? void 0 : _a.textContent) && /* @__PURE__ */ jsxs(
1367
- "div",
1368
- {
1369
- style: {
1370
- color: c.muted,
1371
- fontSize: 10,
1372
- marginTop: 4,
1373
- fontStyle: "italic"
1374
- },
1375
- children: [
1376
- '"',
1377
- hoveredSource.elementContext.textContent,
1378
- '"'
1379
- ]
1380
- }
1381
- )
1382
- ]
1383
- }
1384
- ),
1385
- /* @__PURE__ */ jsxs(
1386
- "div",
1387
- {
1388
- style: {
1389
- position: "fixed",
1390
- bottom: 20,
1391
- right: 20,
1392
- background: c.bg,
1393
- color: c.success,
1394
- padding: "10px 16px",
1395
- borderRadius: 20,
1396
- fontSize: 13,
1397
- fontWeight: 600,
1398
- zIndex: 99997,
1399
- display: "flex",
1400
- alignItems: "center",
1401
- gap: 8,
1402
- border: `1px solid ${c.border}`,
1403
- boxShadow: `0 4px 20px rgba(0,0,0,${isDark ? 0.4 : 0.1})`
1404
- },
1405
- children: [
1406
- /* @__PURE__ */ jsx(
1407
- "span",
1408
- {
1409
- style: {
1410
- width: 8,
1411
- height: 8,
1412
- borderRadius: "50%",
1413
- background: c.success
1414
- }
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
1415
1381
  }
1416
- ),
1417
- "Select Component"
1418
- ]
1419
- }
1420
- )
1421
- ] });
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
+ );
1422
1483
  }
1423
1484
  export {
1424
1485
  AIEditorProvider as A,
1425
1486
  ChatPanel as C,
1426
1487
  ControlPill as a
1427
1488
  };
1428
- //# sourceMappingURL=AIEditorProvider-CKA2K_g2.js.map
1489
+ //# sourceMappingURL=AIEditorProvider-DWId5Qmv.js.map