@alpaca-editor/core 1.0.4063 → 1.0.4064
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.
- package/dist/config/config.js +3 -21
- package/dist/config/config.js.map +1 -1
- package/dist/editor/ConfirmationDialog.js +1 -1
- package/dist/editor/ConfirmationDialog.js.map +1 -1
- package/dist/editor/ContextMenu.js +1 -0
- package/dist/editor/ContextMenu.js.map +1 -1
- package/dist/editor/FieldListField.js +2 -2
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/FieldListFieldWithFallbacks.js +1 -1
- package/dist/editor/FieldListFieldWithFallbacks.js.map +1 -1
- package/dist/editor/MainLayout.d.ts +2 -0
- package/dist/editor/MainLayout.js +8 -0
- package/dist/editor/MainLayout.js.map +1 -1
- package/dist/editor/ScrollingContentTree.js +9 -4
- package/dist/editor/ScrollingContentTree.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.js +382 -3
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/Agents.js +44 -3
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/client/EditorClient.js +54 -16
- package/dist/editor/client/EditorClient.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +4 -2
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/media-selector/TreeSelector.js +1 -1
- package/dist/editor/media-selector/TreeSelector.js.map +1 -1
- package/dist/editor/menubar/VersionSelector.js +1 -1
- package/dist/editor/menubar/VersionSelector.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js +2 -1
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
- package/dist/editor/page-viewer/EditorForm.js +7 -0
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +2 -1
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/reviews/Comment.js +57 -9
- package/dist/editor/reviews/Comment.js.map +1 -1
- package/dist/editor/reviews/CommentEditor.js +2 -2
- package/dist/editor/reviews/CommentEditor.js.map +1 -1
- package/dist/editor/reviews/CommentPopover.js +1 -1
- package/dist/editor/reviews/CommentPopover.js.map +1 -1
- package/dist/editor/reviews/CommentView.js +5 -5
- package/dist/editor/reviews/CommentView.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +41 -0
- package/dist/editor/services/agentService.js +10 -0
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/sidebar/ComponentTree.js +3 -2
- package/dist/editor/sidebar/ComponentTree.js.map +1 -1
- package/dist/editor/sidebar/SidebarView.d.ts +2 -1
- package/dist/editor/sidebar/SidebarView.js +2 -2
- package/dist/editor/sidebar/SidebarView.js.map +1 -1
- package/dist/editor/ui/PerfectTree.js +1 -1
- package/dist/editor/ui/PerfectTree.js.map +1 -1
- package/dist/editor/ui/SimpleTabs.js +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/splash-screen/NewPage.js +2 -2
- package/dist/splash-screen/NewPage.js.map +1 -1
- package/dist/styles.css +6 -0
- package/dist/types.d.ts +2 -1
- package/package.json +1 -1
- package/src/config/config.tsx +5 -20
- package/src/editor/ConfirmationDialog.tsx +4 -1
- package/src/editor/ContextMenu.tsx +1 -0
- package/src/editor/FieldListField.tsx +13 -11
- package/src/editor/FieldListFieldWithFallbacks.tsx +1 -0
- package/src/editor/MainLayout.tsx +11 -0
- package/src/editor/ScrollingContentTree.tsx +10 -5
- package/src/editor/ai/AgentTerminal.tsx +555 -1
- package/src/editor/ai/Agents.tsx +64 -8
- package/src/editor/client/EditorClient.tsx +98 -29
- package/src/editor/client/editContext.ts +5 -2
- package/src/editor/media-selector/TreeSelector.tsx +1 -0
- package/src/editor/menubar/VersionSelector.tsx +4 -1
- package/src/editor/menubar/toolbar-sections/UtilityControls.tsx +15 -2
- package/src/editor/page-viewer/EditorForm.tsx +13 -0
- package/src/editor/page-viewer/PageViewerFrame.tsx +2 -1
- package/src/editor/reviews/Comment.tsx +65 -9
- package/src/editor/reviews/CommentEditor.tsx +8 -1
- package/src/editor/reviews/CommentPopover.tsx +1 -0
- package/src/editor/reviews/CommentView.tsx +24 -3
- package/src/editor/services/agentService.ts +58 -0
- package/src/editor/sidebar/ComponentTree.tsx +6 -2
- package/src/editor/sidebar/SidebarView.tsx +8 -7
- package/src/editor/ui/PerfectTree.tsx +2 -1
- package/src/editor/ui/SimpleTabs.tsx +1 -1
- package/src/index.ts +0 -2
- package/src/revision.ts +2 -2
- package/src/splash-screen/NewPage.tsx +2 -2
- package/src/types.ts +2 -1
- package/styles.css +0 -2
- package/dist/fonts/index.d.ts +0 -4
- package/dist/fonts/index.js +0 -9
- package/dist/fonts/index.js.map +0 -1
- package/src/fonts/Geist-Black.woff2 +0 -0
- package/src/fonts/Geist-Bold.woff2 +0 -0
- package/src/fonts/Geist-ExtraBold.woff2 +0 -0
- package/src/fonts/Geist-ExtraLight.woff2 +0 -0
- package/src/fonts/Geist-Light.woff2 +0 -0
- package/src/fonts/Geist-Medium.woff2 +0 -0
- package/src/fonts/Geist-Regular.woff2 +0 -0
- package/src/fonts/Geist-SemiBold.woff2 +0 -0
- package/src/fonts/Geist-Thin.woff2 +0 -0
- package/src/fonts/Geist[wght].woff2 +0 -0
- package/src/fonts/index.ts +0 -10
package/src/editor/ai/Agents.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import React, { useState, useRef, useEffect } from "react";
|
|
|
3
3
|
import { SimpleIconButton } from "../ui/SimpleIconButton";
|
|
4
4
|
import { Plus, X, History, MoreVertical, Trash } from "lucide-react";
|
|
5
5
|
import { cn } from "../../lib/utils";
|
|
6
|
+
import { MenuItem } from "../../config/types";
|
|
6
7
|
|
|
7
8
|
import {
|
|
8
9
|
Popover,
|
|
@@ -139,6 +140,10 @@ export function Agents({ closeButton }: { closeButton?: React.ReactNode }) {
|
|
|
139
140
|
userId: "", // Will be populated from backend if needed
|
|
140
141
|
updatedDate: new Date().toISOString(),
|
|
141
142
|
};
|
|
143
|
+
|
|
144
|
+
// Automatically select the new agent
|
|
145
|
+
setActiveAgentIdWithStorage(agentId);
|
|
146
|
+
|
|
142
147
|
return [...prevAgents, newAgent];
|
|
143
148
|
}
|
|
144
149
|
});
|
|
@@ -173,7 +178,6 @@ export function Agents({ closeButton }: { closeButton?: React.ReactNode }) {
|
|
|
173
178
|
selectedAgentId = storedAgent.id;
|
|
174
179
|
} else {
|
|
175
180
|
// Fall back to the most recently updated agent
|
|
176
|
-
console.log("get most recent agent", activeAgentsResult);
|
|
177
181
|
const mostRecentAgent = getMostRecentAgent(activeAgentsResult);
|
|
178
182
|
selectedAgentId = mostRecentAgent?.id || null;
|
|
179
183
|
}
|
|
@@ -268,6 +272,19 @@ export function Agents({ closeButton }: { closeButton?: React.ReactNode }) {
|
|
|
268
272
|
setMenuPopoverOpen(false);
|
|
269
273
|
};
|
|
270
274
|
|
|
275
|
+
const getTabsMenuItems = (): MenuItem[] => {
|
|
276
|
+
return [
|
|
277
|
+
{
|
|
278
|
+
id: "close-other",
|
|
279
|
+
label: "Close Other",
|
|
280
|
+
command: async () => {
|
|
281
|
+
await closeOtherAgents();
|
|
282
|
+
},
|
|
283
|
+
disabled: agents.length <= 1,
|
|
284
|
+
},
|
|
285
|
+
];
|
|
286
|
+
};
|
|
287
|
+
|
|
271
288
|
const openAgentFromHistory = async (agent: Agent) => {
|
|
272
289
|
// Check if this agent is already open as a terminal
|
|
273
290
|
const existingAgent = agents.find((a) => a.id === agent.id);
|
|
@@ -331,6 +348,35 @@ export function Agents({ closeButton }: { closeButton?: React.ReactNode }) {
|
|
|
331
348
|
: "hover:bg-gray-100",
|
|
332
349
|
)}
|
|
333
350
|
onClick={() => setActiveAgentIdWithStorage(agent.id)}
|
|
351
|
+
onContextMenu={(e) => {
|
|
352
|
+
e.preventDefault();
|
|
353
|
+
e.stopPropagation();
|
|
354
|
+
// Capture coordinates before state update to avoid pooled/react event issues
|
|
355
|
+
const contextEvent = {
|
|
356
|
+
clientX: e.clientX,
|
|
357
|
+
clientY: e.clientY,
|
|
358
|
+
pageX: e.pageX,
|
|
359
|
+
pageY: e.pageY,
|
|
360
|
+
screenX: (e as any).screenX,
|
|
361
|
+
screenY: (e as any).screenY,
|
|
362
|
+
button: 2,
|
|
363
|
+
buttons: 2,
|
|
364
|
+
ctrlKey: e.ctrlKey,
|
|
365
|
+
shiftKey: e.shiftKey,
|
|
366
|
+
altKey: e.altKey,
|
|
367
|
+
preventDefault: () => {},
|
|
368
|
+
} as any;
|
|
369
|
+
|
|
370
|
+
// Show the menu first at the correct position
|
|
371
|
+
setTimeout(() => {
|
|
372
|
+
editContext?.showContextMenu(
|
|
373
|
+
contextEvent,
|
|
374
|
+
getTabsMenuItems(),
|
|
375
|
+
);
|
|
376
|
+
// Then update the active tab on the next tick to avoid interfering with positioning
|
|
377
|
+
}, 200);
|
|
378
|
+
setActiveAgentIdWithStorage(agent.id);
|
|
379
|
+
}}
|
|
334
380
|
>
|
|
335
381
|
<span className="truncate">{agent.name}</span>
|
|
336
382
|
{agents.length > 1 && (
|
|
@@ -415,13 +461,23 @@ export function Agents({ closeButton }: { closeButton?: React.ReactNode }) {
|
|
|
415
461
|
</PopoverTrigger>
|
|
416
462
|
<PopoverContent className="w-48 p-0" align="end">
|
|
417
463
|
<div className="py-1">
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
464
|
+
{getTabsMenuItems().map((item) =>
|
|
465
|
+
item.separator ? (
|
|
466
|
+
<div key={item.id} className="my-1 h-px bg-gray-100" />
|
|
467
|
+
) : (
|
|
468
|
+
<button
|
|
469
|
+
key={item.id}
|
|
470
|
+
onClick={async (e) => {
|
|
471
|
+
if (item.command) await item.command(e);
|
|
472
|
+
setMenuPopoverOpen(false);
|
|
473
|
+
}}
|
|
474
|
+
disabled={!!item.disabled}
|
|
475
|
+
className="w-full px-3 py-2 text-left text-xs hover:bg-gray-50 disabled:cursor-not-allowed disabled:text-gray-400"
|
|
476
|
+
>
|
|
477
|
+
{item.label}
|
|
478
|
+
</button>
|
|
479
|
+
),
|
|
480
|
+
)}
|
|
425
481
|
</div>
|
|
426
482
|
</PopoverContent>
|
|
427
483
|
</Popover>
|
|
@@ -129,6 +129,7 @@ import { usePageWizard } from "../../page-wizard/usePageWizard";
|
|
|
129
129
|
import { requestQuota } from "../services/aiService";
|
|
130
130
|
|
|
131
131
|
import { Shrink, Monitor, Smartphone } from "lucide-react";
|
|
132
|
+
import { Agents } from "../ai/Agents";
|
|
132
133
|
|
|
133
134
|
export type FieldAction = {
|
|
134
135
|
field: FieldDescriptor;
|
|
@@ -363,8 +364,11 @@ export function EditorClient({
|
|
|
363
364
|
const [focusFieldComponentId, setFocusFieldComponentId] = useState<string>();
|
|
364
365
|
|
|
365
366
|
const [enableCompletions, setEnableCompletions] = useState(false);
|
|
366
|
-
const [
|
|
367
|
-
userPreferences.
|
|
367
|
+
const [showComponentNavigator, setShowComponentNavigator] = useState(
|
|
368
|
+
userPreferences.showComponentNavigator ?? true,
|
|
369
|
+
);
|
|
370
|
+
const [showAgentsPanel, setShowAgentsPanel] = useState(
|
|
371
|
+
userPreferences.showAgentsPanel ?? false,
|
|
368
372
|
);
|
|
369
373
|
const [activeEditorTab, setActiveEditorTab] = useState<string | null>(null);
|
|
370
374
|
const [hideNonEditableComponents, setHideNonEditableComponents] = useState(
|
|
@@ -490,14 +494,24 @@ export function EditorClient({
|
|
|
490
494
|
setIsTourActive(true);
|
|
491
495
|
}, [setIsTourActive]);
|
|
492
496
|
|
|
493
|
-
const
|
|
497
|
+
const handleSetShowComponentNavigator = useCallback(
|
|
498
|
+
(value: boolean | ((prev: boolean) => boolean)) => {
|
|
499
|
+
const newValue =
|
|
500
|
+
typeof value === "function" ? value(showComponentNavigator) : value;
|
|
501
|
+
setShowComponentNavigator(newValue);
|
|
502
|
+
setUserPreferences({ showComponentNavigator: newValue });
|
|
503
|
+
},
|
|
504
|
+
[showComponentNavigator, setShowComponentNavigator, setUserPreferences],
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
const handleSetShowAgentsPanel = useCallback(
|
|
494
508
|
(value: boolean | ((prev: boolean) => boolean)) => {
|
|
495
509
|
const newValue =
|
|
496
|
-
typeof value === "function" ? value(
|
|
497
|
-
|
|
498
|
-
setUserPreferences({
|
|
510
|
+
typeof value === "function" ? value(showAgentsPanel) : value;
|
|
511
|
+
setShowAgentsPanel(newValue);
|
|
512
|
+
setUserPreferences({ showAgentsPanel: newValue });
|
|
499
513
|
},
|
|
500
|
-
[
|
|
514
|
+
[showAgentsPanel, setShowAgentsPanel, setUserPreferences],
|
|
501
515
|
);
|
|
502
516
|
|
|
503
517
|
const handleSetHideNonEditableComponents = useCallback(
|
|
@@ -1001,6 +1015,10 @@ export function EditorClient({
|
|
|
1001
1015
|
const isMobile = useMediaQuery("(max-width: 768px)");
|
|
1002
1016
|
|
|
1003
1017
|
useEffect(() => {
|
|
1018
|
+
// Suppress auto-start tour when `noTour` query parameter is present
|
|
1019
|
+
if (searchParams.get("noTour") !== null) {
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1004
1022
|
const tour = configuration.activeTour;
|
|
1005
1023
|
const key =
|
|
1006
1024
|
tour === "default" ? "editor.tourShown" : "editor.tourShown." + tour;
|
|
@@ -1281,7 +1299,10 @@ export function EditorClient({
|
|
|
1281
1299
|
|
|
1282
1300
|
useEffect(() => {
|
|
1283
1301
|
// Handle fullscreen on initial load only
|
|
1284
|
-
if (
|
|
1302
|
+
if (
|
|
1303
|
+
isInitialLoad &&
|
|
1304
|
+
(searchParams.get("fullscreen") || configuration.forceFullscreen)
|
|
1305
|
+
) {
|
|
1285
1306
|
pageViewContext.setFullscreen(true);
|
|
1286
1307
|
}
|
|
1287
1308
|
const handleMessage = (event: MessageEvent) => {
|
|
@@ -1295,7 +1316,13 @@ export function EditorClient({
|
|
|
1295
1316
|
return () => {
|
|
1296
1317
|
window.removeEventListener("message", handleMessage);
|
|
1297
1318
|
};
|
|
1298
|
-
}, [
|
|
1319
|
+
}, [
|
|
1320
|
+
isInitialLoad,
|
|
1321
|
+
searchParams,
|
|
1322
|
+
pathname,
|
|
1323
|
+
pageViewContext,
|
|
1324
|
+
configuration.forceFullscreen,
|
|
1325
|
+
]);
|
|
1299
1326
|
|
|
1300
1327
|
const loadHistory = useDebouncedCallback(async (item: ItemDescriptor) => {
|
|
1301
1328
|
const result = await getEditHistory(item);
|
|
@@ -1552,8 +1579,11 @@ export function EditorClient({
|
|
|
1552
1579
|
);
|
|
1553
1580
|
|
|
1554
1581
|
useEffect(() => {
|
|
1555
|
-
if (
|
|
1556
|
-
|
|
1582
|
+
if (
|
|
1583
|
+
pageViewContext.fullscreen &&
|
|
1584
|
+
!searchParams.get("fullscreen") &&
|
|
1585
|
+
!configuration.forceFullscreen
|
|
1586
|
+
)
|
|
1557
1587
|
setShowFullscreenHint(true);
|
|
1558
1588
|
}, [pageViewContext.fullscreen, configuration.forceFullscreen]);
|
|
1559
1589
|
|
|
@@ -2419,8 +2449,10 @@ export function EditorClient({
|
|
|
2419
2449
|
setShowSuggestedEditsDiff,
|
|
2420
2450
|
enableCompletions,
|
|
2421
2451
|
setEnableCompletions,
|
|
2422
|
-
|
|
2423
|
-
|
|
2452
|
+
showComponentNavigator,
|
|
2453
|
+
setShowComponentNavigator: handleSetShowComponentNavigator,
|
|
2454
|
+
showAgentsPanel,
|
|
2455
|
+
setShowAgentsPanel: handleSetShowAgentsPanel,
|
|
2424
2456
|
activeEditorTab,
|
|
2425
2457
|
setActiveEditorTab,
|
|
2426
2458
|
hideNonEditableComponents,
|
|
@@ -2514,8 +2546,10 @@ export function EditorClient({
|
|
|
2514
2546
|
setShowSuggestedEdits,
|
|
2515
2547
|
showSuggestedEditsDiff,
|
|
2516
2548
|
setShowSuggestedEditsDiff,
|
|
2517
|
-
|
|
2518
|
-
|
|
2549
|
+
showComponentNavigator,
|
|
2550
|
+
handleSetShowComponentNavigator,
|
|
2551
|
+
showAgentsPanel,
|
|
2552
|
+
handleSetShowAgentsPanel,
|
|
2519
2553
|
activeEditorTab,
|
|
2520
2554
|
setActiveEditorTab,
|
|
2521
2555
|
hideNonEditableComponents,
|
|
@@ -2791,7 +2825,7 @@ export function EditorClient({
|
|
|
2791
2825
|
/>
|
|
2792
2826
|
{/* Control buttons in top right corner */}
|
|
2793
2827
|
<div className="fixed top-4 right-4 z-[9999] flex gap-2">
|
|
2794
|
-
{/* Device toggle button */}
|
|
2828
|
+
{/* Device toggle button */}
|
|
2795
2829
|
<button
|
|
2796
2830
|
onClick={() => {
|
|
2797
2831
|
const currentDevice = pageViewContext.device;
|
|
@@ -2807,8 +2841,17 @@ export function EditorClient({
|
|
|
2807
2841
|
}
|
|
2808
2842
|
}}
|
|
2809
2843
|
className="flex h-10 w-10 cursor-pointer items-center justify-center rounded-full bg-black/20 text-white backdrop-blur-sm transition-colors duration-200 hover:bg-black/40"
|
|
2810
|
-
aria-label={
|
|
2811
|
-
|
|
2844
|
+
aria-label={
|
|
2845
|
+
pageViewContext.device === "desktop"
|
|
2846
|
+
? "Switch to mobile view"
|
|
2847
|
+
: "Switch to desktop view"
|
|
2848
|
+
}
|
|
2849
|
+
title={
|
|
2850
|
+
pageViewContext.device === "desktop"
|
|
2851
|
+
? "Switch to mobile view"
|
|
2852
|
+
: "Switch to desktop view"
|
|
2853
|
+
}
|
|
2854
|
+
data-testid="fullscreen-device-toggle"
|
|
2812
2855
|
>
|
|
2813
2856
|
{pageViewContext.device === "desktop" ? (
|
|
2814
2857
|
<Smartphone className="h-5 w-5" />
|
|
@@ -2816,17 +2859,18 @@ export function EditorClient({
|
|
|
2816
2859
|
<Monitor className="h-5 w-5" />
|
|
2817
2860
|
)}
|
|
2818
2861
|
</button>
|
|
2819
|
-
|
|
2862
|
+
|
|
2820
2863
|
{/* Exit fullscreen button */}
|
|
2821
2864
|
{!configuration.forceFullscreen && (
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2865
|
+
<button
|
|
2866
|
+
onClick={() => pageViewContext.setFullscreen(false)}
|
|
2867
|
+
className="flex h-10 w-10 cursor-pointer items-center justify-center rounded-full bg-black/20 text-white backdrop-blur-sm transition-colors duration-200 hover:bg-black/40"
|
|
2868
|
+
aria-label="Exit fullscreen"
|
|
2869
|
+
title="Return to normal view"
|
|
2870
|
+
data-testid="fullscreen-exit-button"
|
|
2871
|
+
>
|
|
2872
|
+
<Shrink className="h-5 w-5" />
|
|
2873
|
+
</button>
|
|
2830
2874
|
)}
|
|
2831
2875
|
</div>
|
|
2832
2876
|
</div>
|
|
@@ -2842,6 +2886,7 @@ export function EditorClient({
|
|
|
2842
2886
|
setShowFullscreenHint(false);
|
|
2843
2887
|
}, 600);
|
|
2844
2888
|
}}
|
|
2889
|
+
data-testid="fullscreen-hint-overlay"
|
|
2845
2890
|
>
|
|
2846
2891
|
<div className="fixed top-3 left-1/2 -translate-x-1/2 transform rounded-sm bg-gray-200 p-12">
|
|
2847
2892
|
Press Ctrl + F11 to exit fullscreen mode
|
|
@@ -2857,17 +2902,41 @@ export function EditorClient({
|
|
|
2857
2902
|
centerPanelView={centerPanelView}
|
|
2858
2903
|
rightSidebar={
|
|
2859
2904
|
currentView.rightSidebar &&
|
|
2860
|
-
|
|
2905
|
+
showComponentNavigator && (
|
|
2861
2906
|
<SidebarView
|
|
2862
2907
|
sidebar={currentView.rightSidebar}
|
|
2863
2908
|
editContext={editContext}
|
|
2864
2909
|
active={true}
|
|
2865
2910
|
detached={true}
|
|
2866
|
-
onClose={() =>
|
|
2911
|
+
onClose={() => handleSetShowComponentNavigator(false)}
|
|
2867
2912
|
/>
|
|
2868
2913
|
)
|
|
2869
2914
|
}
|
|
2870
2915
|
rightSidebarTitle={currentView.rightSidebar?.title}
|
|
2916
|
+
farRightSidebar={
|
|
2917
|
+
showAgentsPanel && (
|
|
2918
|
+
<SidebarView
|
|
2919
|
+
sidebar={{
|
|
2920
|
+
title: "Agents",
|
|
2921
|
+
panels: [
|
|
2922
|
+
{
|
|
2923
|
+
name: "agents",
|
|
2924
|
+
title: "Agents",
|
|
2925
|
+
content: <Agents />,
|
|
2926
|
+
initialSize: 70,
|
|
2927
|
+
noOverflow: true,
|
|
2928
|
+
},
|
|
2929
|
+
],
|
|
2930
|
+
}}
|
|
2931
|
+
editContext={editContext}
|
|
2932
|
+
active={true}
|
|
2933
|
+
detached={true}
|
|
2934
|
+
paddingRight={true}
|
|
2935
|
+
onClose={() => handleSetShowAgentsPanel(false)}
|
|
2936
|
+
/>
|
|
2937
|
+
)
|
|
2938
|
+
}
|
|
2939
|
+
farRightSidebarTitle={"AGENTS"}
|
|
2871
2940
|
/>
|
|
2872
2941
|
|
|
2873
2942
|
{isTourActive && <Tour tourStopCallback={() => setIsTourActive(false)} />}
|
|
@@ -322,8 +322,11 @@ export type EditContextType = {
|
|
|
322
322
|
getQuotaWarningMessage: () => string | null;
|
|
323
323
|
isMobile: boolean;
|
|
324
324
|
|
|
325
|
-
|
|
326
|
-
|
|
325
|
+
showComponentNavigator: boolean;
|
|
326
|
+
setShowComponentNavigator: React.Dispatch<React.SetStateAction<boolean>>;
|
|
327
|
+
|
|
328
|
+
showAgentsPanel: boolean;
|
|
329
|
+
setShowAgentsPanel: React.Dispatch<React.SetStateAction<boolean>>;
|
|
327
330
|
|
|
328
331
|
activeEditorTab: string | null;
|
|
329
332
|
setActiveEditorTab: React.Dispatch<React.SetStateAction<string | null>>;
|
|
@@ -92,7 +92,10 @@ export function VersionSelector({
|
|
|
92
92
|
</PopoverTrigger>
|
|
93
93
|
<PopoverContent className="w-64 p-0" align="start">
|
|
94
94
|
<Command>
|
|
95
|
-
<CommandList
|
|
95
|
+
<CommandList
|
|
96
|
+
className="max-h-[30vh]"
|
|
97
|
+
data-testid="version-selector-list"
|
|
98
|
+
>
|
|
96
99
|
<CommandEmpty>No versions available</CommandEmpty>
|
|
97
100
|
<CommandGroup>
|
|
98
101
|
{versions.length > 0 && (
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useEditContext } from "../../client/editContext";
|
|
2
2
|
import { SimpleIconButton } from "../../ui/SimpleIconButton";
|
|
3
3
|
import { Layers } from "lucide-react";
|
|
4
|
+
import { SecretAgentIcon } from "../../ui/Icons";
|
|
4
5
|
import { EnterFullScreenIcon } from "@radix-ui/react-icons";
|
|
5
6
|
import { CompareIcon } from "../../ui/Icons";
|
|
6
7
|
|
|
@@ -25,9 +26,21 @@ export function UtilityControls() {
|
|
|
25
26
|
label="Component Navigator"
|
|
26
27
|
size="large"
|
|
27
28
|
data-testid="component-navigator-button"
|
|
28
|
-
selected={editContext.
|
|
29
|
+
selected={editContext.showComponentNavigator}
|
|
29
30
|
onClick={() =>
|
|
30
|
-
editContext.
|
|
31
|
+
editContext.setShowComponentNavigator(
|
|
32
|
+
!editContext.showComponentNavigator,
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
/>
|
|
36
|
+
<SimpleIconButton
|
|
37
|
+
icon={<SecretAgentIcon />}
|
|
38
|
+
label="Agents"
|
|
39
|
+
size="large"
|
|
40
|
+
data-testid="agents-panel-button"
|
|
41
|
+
selected={editContext.showAgentsPanel}
|
|
42
|
+
onClick={() =>
|
|
43
|
+
editContext.setShowAgentsPanel(!editContext.showAgentsPanel)
|
|
31
44
|
}
|
|
32
45
|
/>
|
|
33
46
|
</>
|
|
@@ -12,6 +12,7 @@ import { Spinner } from "../ui/Spinner";
|
|
|
12
12
|
import { PageViewContext } from "./pageViewContext";
|
|
13
13
|
import { SimpleTabs, Tab } from "../ui/SimpleTabs";
|
|
14
14
|
import { ChevronLeft, X } from "lucide-react";
|
|
15
|
+
import { ComponentTree } from "../sidebar/ComponentTree";
|
|
15
16
|
|
|
16
17
|
export function EditorForm({
|
|
17
18
|
pageViewContext,
|
|
@@ -322,6 +323,18 @@ export function EditorForm({
|
|
|
322
323
|
),
|
|
323
324
|
id: "advanced",
|
|
324
325
|
});
|
|
326
|
+
// Component tree tab
|
|
327
|
+
tabPanels.push({
|
|
328
|
+
label: "Components",
|
|
329
|
+
content: (
|
|
330
|
+
<div className="relative h-full">
|
|
331
|
+
<div className="absolute inset-0 overflow-auto">
|
|
332
|
+
<ComponentTree />
|
|
333
|
+
</div>
|
|
334
|
+
</div>
|
|
335
|
+
),
|
|
336
|
+
id: "tree",
|
|
337
|
+
});
|
|
325
338
|
|
|
326
339
|
return (
|
|
327
340
|
<div className="flex h-full flex-col" data-testid="editor-sidepanel">
|
|
@@ -775,7 +775,8 @@ export function PageViewerFrame({
|
|
|
775
775
|
"relative flex h-full w-full flex-col items-center select-none",
|
|
776
776
|
|
|
777
777
|
className,
|
|
778
|
-
editContext.
|
|
778
|
+
(editContext.showComponentNavigator || editContext.showAgentsPanel) &&
|
|
779
|
+
"pr-0",
|
|
779
780
|
)}
|
|
780
781
|
>
|
|
781
782
|
{!editContext.pageView.fullscreen && (
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
resolveComment,
|
|
8
8
|
unresolveComment,
|
|
9
9
|
} from "../services/reviewsService";
|
|
10
|
+
import { startAgent, StartAgentRequest } from "../services/agentService";
|
|
10
11
|
import { useDebouncedCallback } from "use-debounce";
|
|
11
12
|
import { CommentView } from "./CommentView";
|
|
12
13
|
import { CommentEditor } from "./CommentEditor";
|
|
@@ -99,15 +100,70 @@ export function Comment({
|
|
|
99
100
|
await unresolveComment(comment);
|
|
100
101
|
};
|
|
101
102
|
|
|
102
|
-
const handleAiAction = () => {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
103
|
+
const handleAiAction = async () => {
|
|
104
|
+
if (!editContext) return;
|
|
105
|
+
|
|
106
|
+
// Respect quota if available
|
|
107
|
+
if ((editContext as any).isQuotaExceeded) {
|
|
108
|
+
editContext.showToast?.(
|
|
109
|
+
"AI quota exceeded. Please try again later or adjust settings.",
|
|
110
|
+
);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const selectedText = (() => {
|
|
115
|
+
if (
|
|
116
|
+
typeof comment.rangeStart === "number" &&
|
|
117
|
+
typeof comment.rangeEnd === "number" &&
|
|
118
|
+
comment.fieldValue
|
|
119
|
+
) {
|
|
120
|
+
try {
|
|
121
|
+
return comment.fieldValue.substring(
|
|
122
|
+
Math.max(0, comment.rangeStart),
|
|
123
|
+
Math.max(comment.rangeStart, comment.rangeEnd),
|
|
124
|
+
);
|
|
125
|
+
} catch {
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return undefined;
|
|
130
|
+
})();
|
|
131
|
+
|
|
132
|
+
const promptParts: string[] = [];
|
|
133
|
+
const text = (comment.text || "").trim();
|
|
134
|
+
if (text) promptParts.push(`Comment: "${text}"`);
|
|
135
|
+
if (comment.fieldName) promptParts.push(`Field: ${comment.fieldName}`);
|
|
136
|
+
if (comment.itemName) promptParts.push(`Item: ${comment.itemName}`);
|
|
137
|
+
if (selectedText) promptParts.push(`Selected text: "${selectedText}"`);
|
|
138
|
+
|
|
139
|
+
const initialPrompt =
|
|
140
|
+
promptParts.length > 0
|
|
141
|
+
? `Please help resolve this review comment. ${promptParts.join(" | ")}`
|
|
142
|
+
: "Please help resolve this review comment.";
|
|
143
|
+
|
|
144
|
+
const agentId = crypto.randomUUID();
|
|
145
|
+
|
|
146
|
+
const request: StartAgentRequest = {
|
|
147
|
+
agentId,
|
|
148
|
+
message: initialPrompt,
|
|
149
|
+
sessionId: editContext.sessionId,
|
|
150
|
+
profileId: "Editor",
|
|
151
|
+
itemid: comment.mainItemId || editContext.currentItemDescriptor?.id || "",
|
|
152
|
+
language: comment.language,
|
|
153
|
+
version: comment.version,
|
|
154
|
+
selectedText: selectedText,
|
|
155
|
+
addContextContent: true,
|
|
156
|
+
addSelectedComponents: false,
|
|
157
|
+
profile: "Editor",
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
editContext.setShowAgentsPanel(true);
|
|
162
|
+
await startAgent(request);
|
|
163
|
+
} catch (e) {
|
|
164
|
+
console.error("Failed to start agent for comment:", e);
|
|
165
|
+
editContext.showToast?.("Failed to start AI agent for this comment.");
|
|
166
|
+
}
|
|
111
167
|
};
|
|
112
168
|
|
|
113
169
|
if (isEditing) {
|
|
@@ -82,6 +82,7 @@ export function CommentEditor({
|
|
|
82
82
|
<div className="space-y-3">
|
|
83
83
|
<Textarea
|
|
84
84
|
ref={textareaRef}
|
|
85
|
+
data-testid="add-comment-textarea"
|
|
85
86
|
value={text}
|
|
86
87
|
onChange={(e) => setText(e.target.value)}
|
|
87
88
|
onKeyDown={handleKeyDown}
|
|
@@ -129,13 +130,19 @@ export function CommentEditor({
|
|
|
129
130
|
|
|
130
131
|
{/* Action Buttons */}
|
|
131
132
|
<div className="flex justify-end gap-2">
|
|
132
|
-
<ActionButton
|
|
133
|
+
<ActionButton
|
|
134
|
+
variant="outline"
|
|
135
|
+
onClick={onCancel}
|
|
136
|
+
disabled={isSaving}
|
|
137
|
+
data-testid="add-comment-cancel"
|
|
138
|
+
>
|
|
133
139
|
{cancelLabel}
|
|
134
140
|
</ActionButton>
|
|
135
141
|
<ActionButton
|
|
136
142
|
onClick={handleSave}
|
|
137
143
|
disabled={!text.trim() || isSaving}
|
|
138
144
|
className="flex items-center gap-1"
|
|
145
|
+
data-testid="add-comment-submit"
|
|
139
146
|
>
|
|
140
147
|
{compact && <Send size={14} strokeWidth={1} />}
|
|
141
148
|
{isSaving ? "Saving..." : submitLabel}
|
|
@@ -81,15 +81,26 @@ export function CommentView({
|
|
|
81
81
|
return (
|
|
82
82
|
<div
|
|
83
83
|
className={`${compact ? "mt-2" : "mt-3"} flex items-center border-t pt-${compact ? "2" : "3"} text-xs`}
|
|
84
|
+
data-testid="comment-context-info"
|
|
84
85
|
>
|
|
85
86
|
{showItemName && (
|
|
86
|
-
<div
|
|
87
|
+
<div
|
|
88
|
+
className="text-2xs text-gray-500"
|
|
89
|
+
data-testid="comment-item-name"
|
|
90
|
+
>
|
|
91
|
+
{comment.itemName}
|
|
92
|
+
</div>
|
|
87
93
|
)}
|
|
88
94
|
{showFieldName && showItemName && (
|
|
89
95
|
<div className="text-2xs mx-2 text-gray-500">></div>
|
|
90
96
|
)}
|
|
91
97
|
{showFieldName && (
|
|
92
|
-
<div
|
|
98
|
+
<div
|
|
99
|
+
className="text-2xs text-gray-500"
|
|
100
|
+
data-testid="comment-field-name"
|
|
101
|
+
>
|
|
102
|
+
{comment.fieldName}
|
|
103
|
+
</div>
|
|
93
104
|
)}
|
|
94
105
|
</div>
|
|
95
106
|
);
|
|
@@ -118,6 +129,7 @@ export function CommentView({
|
|
|
118
129
|
<SimpleIconButton
|
|
119
130
|
icon={<Edit className="h-3.5 w-3.5" strokeWidth={1} />}
|
|
120
131
|
label="Edit"
|
|
132
|
+
data-testid="comment-action-edit"
|
|
121
133
|
onClick={onEdit}
|
|
122
134
|
/>
|
|
123
135
|
)}
|
|
@@ -130,11 +142,16 @@ export function CommentView({
|
|
|
130
142
|
<button
|
|
131
143
|
className="hover:bg-gray-5 cursor-pointer rounded-full p-[6px]"
|
|
132
144
|
title="Delete"
|
|
145
|
+
data-testid="comment-action-delete"
|
|
133
146
|
>
|
|
134
147
|
<Trash2 className="h-3.5 w-3.5" strokeWidth={1} />
|
|
135
148
|
</button>
|
|
136
149
|
</PopoverTrigger>
|
|
137
|
-
<PopoverContent
|
|
150
|
+
<PopoverContent
|
|
151
|
+
className="w-auto p-2"
|
|
152
|
+
align="end"
|
|
153
|
+
data-testid="comment-delete-popover"
|
|
154
|
+
>
|
|
138
155
|
<Button
|
|
139
156
|
variant="outline"
|
|
140
157
|
size="sm"
|
|
@@ -152,6 +169,7 @@ export function CommentView({
|
|
|
152
169
|
<SimpleIconButton
|
|
153
170
|
icon={<Check className="h-3.5 w-3.5" strokeWidth={1} />}
|
|
154
171
|
label="Resolve"
|
|
172
|
+
data-testid="comment-action-resolve"
|
|
155
173
|
onClick={onResolve}
|
|
156
174
|
/>
|
|
157
175
|
)}
|
|
@@ -170,6 +188,7 @@ export function CommentView({
|
|
|
170
188
|
formatDate(new Date(comment.resolvedDate!)) +
|
|
171
189
|
")"
|
|
172
190
|
}
|
|
191
|
+
data-testid="comment-resolved-indicator"
|
|
173
192
|
>
|
|
174
193
|
<Check
|
|
175
194
|
className="h-3.5 w-3.5 text-green-500"
|
|
@@ -195,6 +214,7 @@ export function CommentView({
|
|
|
195
214
|
<SimpleIconButton
|
|
196
215
|
icon={<Sparkles className="h-3.5 w-3.5" strokeWidth={1} />}
|
|
197
216
|
label="AI"
|
|
217
|
+
data-testid="comment-action-ai"
|
|
198
218
|
onClick={onAiAction}
|
|
199
219
|
/>
|
|
200
220
|
)}
|
|
@@ -205,6 +225,7 @@ export function CommentView({
|
|
|
205
225
|
{/* Comment Text */}
|
|
206
226
|
<div
|
|
207
227
|
className={`${compact ? "text-sm" : "text-sm"} whitespace-pre-wrap text-gray-700`}
|
|
228
|
+
data-testid="comment-text"
|
|
208
229
|
>
|
|
209
230
|
{comment.text}
|
|
210
231
|
</div>
|