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