@alpaca-editor/core 1.0.4135 → 1.0.4141
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/components/index.d.ts +2 -0
- package/dist/components/index.js +1 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/ui/select.d.ts +2 -1
- package/dist/components/ui/select.js +2 -2
- package/dist/components/ui/select.js.map +1 -1
- package/dist/components/ui/tabs.d.ts +17 -0
- package/dist/components/ui/tabs.js +27 -0
- package/dist/components/ui/tabs.js.map +1 -0
- package/dist/config/config.js +7 -1
- package/dist/config/config.js.map +1 -1
- package/dist/editor/FieldListField.js +3 -4
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/ImageEditButton.d.ts +2 -1
- package/dist/editor/ImageEditButton.js +4 -4
- package/dist/editor/ImageEditButton.js.map +1 -1
- package/dist/editor/PictureEditor.js +42 -1
- package/dist/editor/PictureEditor.js.map +1 -1
- package/dist/editor/Terminal.js +1 -1
- package/dist/editor/Terminal.js.map +1 -1
- package/dist/editor/Titlebar.js +0 -1
- package/dist/editor/Titlebar.js.map +1 -1
- package/dist/editor/ai/AgentCostDisplay.d.ts +3 -1
- package/dist/editor/ai/AgentCostDisplay.js +26 -2
- package/dist/editor/ai/AgentCostDisplay.js.map +1 -1
- package/dist/editor/ai/AgentStatusBadge.d.ts +26 -0
- package/dist/editor/ai/AgentStatusBadge.js +110 -0
- package/dist/editor/ai/AgentStatusBadge.js.map +1 -0
- package/dist/editor/ai/AgentTerminal.js +289 -198
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/Agents.d.ts +2 -2
- package/dist/editor/ai/Agents.js +115 -19
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.js +259 -45
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/ContextInfoBar.js +124 -113
- package/dist/editor/ai/ContextInfoBar.js.map +1 -1
- package/dist/editor/ai/ToolCallDisplay.d.ts +1 -0
- package/dist/editor/ai/ToolCallDisplay.js +70 -58
- package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
- package/dist/editor/ai/useAgentStatus.d.ts +13 -0
- package/dist/editor/ai/useAgentStatus.js +101 -0
- package/dist/editor/ai/useAgentStatus.js.map +1 -0
- package/dist/editor/client/EditorShell.js +23 -8
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/commands/localizeItem/LocalizeItemDialog.js +5 -5
- package/dist/editor/commands/localizeItem/LocalizeItemDialog.js.map +1 -1
- package/dist/editor/control-center/About.js +1 -1
- package/dist/editor/control-center/About.js.map +1 -1
- package/dist/editor/control-center/AllAgentsPanel.d.ts +5 -0
- package/dist/editor/control-center/AllAgentsPanel.js +126 -0
- package/dist/editor/control-center/AllAgentsPanel.js.map +1 -0
- package/dist/editor/control-center/WebSocketMessages.js +1 -0
- package/dist/editor/control-center/WebSocketMessages.js.map +1 -1
- package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.js +42 -7
- package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.js.map +1 -1
- package/dist/editor/media-selector/AiImageSearch.d.ts +1 -1
- package/dist/editor/media-selector/AiImageSearch.js +162 -103
- package/dist/editor/media-selector/AiImageSearch.js.map +1 -1
- package/dist/editor/media-selector/TreeSelector.js +20 -4
- package/dist/editor/media-selector/TreeSelector.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js +5 -2
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.d.ts +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +7 -5
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js.map +1 -1
- package/dist/editor/page-viewer/DeviceToolbar.js +2 -2
- package/dist/editor/page-viewer/DeviceToolbar.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +18 -11
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +53 -48
- package/dist/editor/services/agentService.js +137 -79
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +1 -1
- package/dist/editor/services/editService.js +1 -0
- package/dist/editor/services/editService.js.map +1 -1
- package/dist/editor/sidebar/GraphQL.js +20 -7
- package/dist/editor/sidebar/GraphQL.js.map +1 -1
- package/dist/editor/sidebar/SEOInfo.js +1 -2
- package/dist/editor/sidebar/SEOInfo.js.map +1 -1
- package/dist/editor/sidebar/Translations.js +10 -7
- package/dist/editor/sidebar/Translations.js.map +1 -1
- package/dist/editor/ui/ItemNameDialogNew.js +1 -1
- package/dist/editor/ui/ItemSearch.js +10 -4
- package/dist/editor/ui/ItemSearch.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/page-wizard/steps/CollectStep.js +2 -2
- package/dist/page-wizard/steps/CollectStep.js.map +1 -1
- package/dist/page-wizard/steps/FieldEditor.js +2 -2
- package/dist/page-wizard/steps/FieldEditor.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/splash-screen/RecentPages.js +1 -1
- package/dist/splash-screen/RecentPages.js.map +1 -1
- package/dist/styles.css +167 -15
- package/dist/tour/Tour.js +15 -11
- package/dist/tour/Tour.js.map +1 -1
- package/package.json +1 -1
- package/src/components/index.ts +2 -0
- package/src/components/ui/select.tsx +3 -0
- package/src/components/ui/tabs.tsx +87 -0
- package/src/config/config.tsx +7 -1
- package/src/editor/FieldListField.tsx +13 -13
- package/src/editor/ImageEditButton.tsx +5 -3
- package/src/editor/PictureEditor.tsx +48 -1
- package/src/editor/Terminal.tsx +1 -1
- package/src/editor/Titlebar.tsx +0 -1
- package/src/editor/ai/AgentCostDisplay.tsx +57 -1
- package/src/editor/ai/AgentStatusBadge.tsx +144 -0
- package/src/editor/ai/AgentTerminal.tsx +345 -219
- package/src/editor/ai/Agents.tsx +179 -30
- package/src/editor/ai/AiResponseMessage.tsx +411 -114
- package/src/editor/ai/ContextInfoBar.tsx +134 -131
- package/src/editor/ai/ToolCallDisplay.tsx +217 -176
- package/src/editor/ai/useAgentStatus.ts +123 -0
- package/src/editor/client/EditorShell.tsx +34 -8
- package/src/editor/client/itemsRepository.ts +1 -2
- package/src/editor/commands/localizeItem/LocalizeItemDialog.tsx +5 -5
- package/src/editor/control-center/About.tsx +0 -14
- package/src/editor/control-center/AllAgentsPanel.tsx +300 -0
- package/src/editor/control-center/WebSocketMessages.tsx +1 -0
- package/src/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.tsx +49 -8
- package/src/editor/media-selector/AiImageSearch.tsx +162 -172
- package/src/editor/media-selector/TreeSelector.tsx +137 -116
- package/src/editor/menubar/toolbar-sections/UtilityControls.tsx +9 -1
- package/src/editor/page-editor-chrome/PlaceholderDropZone.tsx +7 -4
- package/src/editor/page-viewer/DeviceToolbar.tsx +15 -11
- package/src/editor/page-viewer/PageViewerFrame.tsx +20 -14
- package/src/editor/services/agentService.ts +217 -129
- package/src/editor/services/aiService.ts +2 -2
- package/src/editor/services/editService.ts +1 -0
- package/src/editor/sidebar/GraphQL.tsx +143 -117
- package/src/editor/sidebar/SEOInfo.tsx +1 -2
- package/src/editor/sidebar/Translations.tsx +14 -12
- package/src/editor/ui/ItemNameDialogNew.tsx +1 -1
- package/src/editor/ui/ItemSearch.tsx +11 -4
- package/src/editor/ui/SimpleTabs.tsx +1 -1
- package/src/index.ts +6 -0
- package/src/page-wizard/steps/CollectStep.tsx +2 -2
- package/src/page-wizard/steps/FieldEditor.tsx +13 -15
- package/src/revision.ts +2 -2
- package/src/splash-screen/NewPage.tsx +2 -2
- package/src/splash-screen/RecentPages.tsx +1 -1
- package/src/tour/Tour.tsx +61 -48
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../../lib/utils";
|
|
5
|
+
|
|
6
|
+
export interface TabViewProps {
|
|
7
|
+
activeIndex?: number;
|
|
8
|
+
onTabChange?: (e: { index: number }) => void;
|
|
9
|
+
className?: string;
|
|
10
|
+
style?: React.CSSProperties;
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface TabPanelProps {
|
|
15
|
+
header: string;
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
className?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function TabView({
|
|
21
|
+
activeIndex = 0,
|
|
22
|
+
onTabChange,
|
|
23
|
+
className,
|
|
24
|
+
style,
|
|
25
|
+
children,
|
|
26
|
+
}: TabViewProps) {
|
|
27
|
+
const [internalActiveIndex, setInternalActiveIndex] = React.useState(activeIndex);
|
|
28
|
+
|
|
29
|
+
const currentActiveIndex = activeIndex !== undefined ? activeIndex : internalActiveIndex;
|
|
30
|
+
|
|
31
|
+
const handleTabClick = (index: number) => {
|
|
32
|
+
if (onTabChange) {
|
|
33
|
+
onTabChange({ index });
|
|
34
|
+
} else {
|
|
35
|
+
setInternalActiveIndex(index);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Extract tab headers and content from children
|
|
40
|
+
const tabPanels = React.Children.toArray(children).filter(
|
|
41
|
+
(child): child is React.ReactElement<TabPanelProps> =>
|
|
42
|
+
React.isValidElement(child) && child.type === TabPanel
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div className={cn("flex flex-col h-full", className)} style={style}>
|
|
47
|
+
{/* Tab Headers */}
|
|
48
|
+
<div className="flex border-b border-gray-200 px-4">
|
|
49
|
+
{tabPanels.map((panel, index) => (
|
|
50
|
+
<button
|
|
51
|
+
key={index}
|
|
52
|
+
onClick={() => handleTabClick(index)}
|
|
53
|
+
className={cn(
|
|
54
|
+
"px-6 py-3 text-sm font-medium border-b-2 transition-colors",
|
|
55
|
+
currentActiveIndex === index
|
|
56
|
+
? "border-blue-500 text-blue-600 bg-blue-50"
|
|
57
|
+
: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"
|
|
58
|
+
)}
|
|
59
|
+
>
|
|
60
|
+
{panel.props.header}
|
|
61
|
+
</button>
|
|
62
|
+
))}
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
{/* Tab Content */}
|
|
66
|
+
<div className="flex-1 overflow-auto p-4">
|
|
67
|
+
{tabPanels.map((panel, index) => (
|
|
68
|
+
<div
|
|
69
|
+
key={index}
|
|
70
|
+
className={cn(
|
|
71
|
+
"h-full",
|
|
72
|
+
currentActiveIndex === index ? "block" : "hidden"
|
|
73
|
+
)}
|
|
74
|
+
>
|
|
75
|
+
{panel.props.children}
|
|
76
|
+
</div>
|
|
77
|
+
))}
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function TabPanel({ header, children, className }: TabPanelProps) {
|
|
84
|
+
// This component is used for structure but doesn't render directly
|
|
85
|
+
// The TabView component extracts the props and renders the content
|
|
86
|
+
return null;
|
|
87
|
+
}
|
package/src/config/config.tsx
CHANGED
|
@@ -80,6 +80,7 @@ import { QuotaInfo } from "../editor/control-center/QuotaInfo";
|
|
|
80
80
|
import { About } from "../editor/control-center/About";
|
|
81
81
|
import { WebSocketMessages } from "../editor/control-center/WebSocketMessages";
|
|
82
82
|
import { LatestFeedback } from "../editor/control-center/LatestFeedback";
|
|
83
|
+
import { AllAgentsPanel } from "../editor/control-center/AllAgentsPanel";
|
|
83
84
|
import { DbSetupStep } from "../editor/control-center/setup-steps/DbSetupStep";
|
|
84
85
|
import { SettingsSetupStep } from "../editor/control-center/setup-steps/SettingsSetupStep";
|
|
85
86
|
import { AiSetupStep } from "../editor/control-center/setup-steps/AiSetupStep";
|
|
@@ -287,7 +288,6 @@ export const getConfiguration = (): EditorConfiguration => {
|
|
|
287
288
|
},
|
|
288
289
|
image: {
|
|
289
290
|
editor: ImageFieldEditor,
|
|
290
|
-
buttons: [],
|
|
291
291
|
},
|
|
292
292
|
droplink: {
|
|
293
293
|
editor: DropLinkEditor,
|
|
@@ -445,6 +445,12 @@ export const getConfiguration = (): EditorConfiguration => {
|
|
|
445
445
|
icon: <i className="pi pi-comments" />,
|
|
446
446
|
content: <WebSocketMessages />,
|
|
447
447
|
},
|
|
448
|
+
{
|
|
449
|
+
id: "all-agents",
|
|
450
|
+
title: "All Agents",
|
|
451
|
+
icon: <i className="pi pi-users" />,
|
|
452
|
+
content: <AllAgentsPanel />,
|
|
453
|
+
},
|
|
448
454
|
],
|
|
449
455
|
},
|
|
450
456
|
{
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { Tooltip } from "primereact/tooltip";
|
|
2
|
-
|
|
3
1
|
import { useEditContext, useFieldsEditContext } from "./client/editContext";
|
|
4
2
|
import { useFieldModification } from "./client/fieldModificationStore";
|
|
5
3
|
import { EditorConfiguration } from "../config/types";
|
|
@@ -35,7 +33,7 @@ import {
|
|
|
35
33
|
PopoverTrigger,
|
|
36
34
|
} from "../components/ui/popover";
|
|
37
35
|
import {
|
|
38
|
-
Tooltip
|
|
36
|
+
Tooltip,
|
|
39
37
|
TooltipContent,
|
|
40
38
|
TooltipTrigger,
|
|
41
39
|
} from "../components/ui/tooltip";
|
|
@@ -244,19 +242,21 @@ export default function FieldListField({
|
|
|
244
242
|
className={classNames}
|
|
245
243
|
>
|
|
246
244
|
{validationBarClassNames && (
|
|
247
|
-
|
|
248
|
-
<
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
245
|
+
<Tooltip>
|
|
246
|
+
<TooltipTrigger asChild>
|
|
247
|
+
<div
|
|
248
|
+
className={validationBarClassNames}
|
|
249
|
+
id={"validation_" + field.id}
|
|
250
|
+
/>
|
|
251
|
+
</TooltipTrigger>
|
|
252
|
+
<TooltipContent>
|
|
253
253
|
{validators
|
|
254
254
|
.filter((x) => x.result > 1)
|
|
255
255
|
.map((x) => (
|
|
256
256
|
<div key={x.validator}>{x.message}</div>
|
|
257
257
|
))}
|
|
258
|
-
</
|
|
259
|
-
|
|
258
|
+
</TooltipContent>
|
|
259
|
+
</Tooltip>
|
|
260
260
|
)}
|
|
261
261
|
|
|
262
262
|
<div className="flex-1">
|
|
@@ -443,7 +443,7 @@ export default function FieldListField({
|
|
|
443
443
|
/>
|
|
444
444
|
)}
|
|
445
445
|
{!readonly && renderGeneratorButtons}
|
|
446
|
-
<
|
|
446
|
+
<Tooltip>
|
|
447
447
|
<Popover
|
|
448
448
|
open={fieldHistoryOpen}
|
|
449
449
|
onOpenChange={(open) => {
|
|
@@ -476,7 +476,7 @@ export default function FieldListField({
|
|
|
476
476
|
</PopoverContent>
|
|
477
477
|
</Popover>
|
|
478
478
|
<TooltipContent>Field History</TooltipContent>
|
|
479
|
-
</
|
|
479
|
+
</Tooltip>
|
|
480
480
|
</div>
|
|
481
481
|
</>
|
|
482
482
|
)}
|
|
@@ -31,6 +31,7 @@ interface ImageEditButtonProps {
|
|
|
31
31
|
testId?: string;
|
|
32
32
|
/** Additional CSS classes for the trigger button */
|
|
33
33
|
buttonClassName?: string;
|
|
34
|
+
defaultActionId?: string;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
export function ImageEditButton({
|
|
@@ -41,10 +42,11 @@ export function ImageEditButton({
|
|
|
41
42
|
buttonIcon = "pi pi-ellipsis-v",
|
|
42
43
|
testId = "menu-toggle-button",
|
|
43
44
|
buttonClassName,
|
|
45
|
+
defaultActionId = "select",
|
|
44
46
|
}: ImageEditButtonProps) {
|
|
45
47
|
const [showMenu, setShowMenu] = useState(false);
|
|
46
48
|
|
|
47
|
-
const
|
|
49
|
+
const defaultAction = actions.find((a) => a.id === defaultActionId);
|
|
48
50
|
|
|
49
51
|
const defaultTriggerButton = (
|
|
50
52
|
<div
|
|
@@ -54,13 +56,13 @@ export function ImageEditButton({
|
|
|
54
56
|
e.stopPropagation();
|
|
55
57
|
}}
|
|
56
58
|
>
|
|
57
|
-
{
|
|
59
|
+
{defaultAction && (
|
|
58
60
|
<button
|
|
59
61
|
type="button"
|
|
60
62
|
className="hover:bg-gray-5 cursor-pointer px-2 py-1.5"
|
|
61
63
|
onClick={(e) => {
|
|
62
64
|
e.stopPropagation();
|
|
63
|
-
|
|
65
|
+
defaultAction.onClick?.(e);
|
|
64
66
|
setShowMenu(false);
|
|
65
67
|
}}
|
|
66
68
|
data-testid={`${testId}-quick-select`}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
FieldActionsOverlayRef,
|
|
11
11
|
} from "./FieldActionsOverlay";
|
|
12
12
|
import { ImageEditButton, MenuAction } from "./ImageEditButton";
|
|
13
|
+
import { DamSelector, DamImageValue, DamSelectorProps, SharedienDamExtension } from "@alpaca-editor/sharedien-dam";
|
|
13
14
|
|
|
14
15
|
export function PictureEditor({
|
|
15
16
|
field,
|
|
@@ -147,10 +148,52 @@ export function PictureEditor({
|
|
|
147
148
|
if (selectedVideoId) videoSelected(selectedVideoId);
|
|
148
149
|
}
|
|
149
150
|
|
|
151
|
+
async function selectFromDam() {
|
|
152
|
+
const data = await editContext?.openDialog<DamImageValue, DamSelectorProps>(DamSelector, {
|
|
153
|
+
language: field.descriptor.item.language
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
if (data?.mediaId || data?.videoId) {
|
|
157
|
+
// Create a new variant with the selected DAM asset
|
|
158
|
+
const selected = raw.Variants?.find((x) => x.Name == variantName);
|
|
159
|
+
if (!selected) {
|
|
160
|
+
if (!raw.Variants) raw.Variants = [];
|
|
161
|
+
raw.Variants.push({
|
|
162
|
+
Name: variantName,
|
|
163
|
+
MediaId: data.mediaId,
|
|
164
|
+
VideoId: data.videoId,
|
|
165
|
+
});
|
|
166
|
+
} else {
|
|
167
|
+
selected.MediaId = data.mediaId;
|
|
168
|
+
selected.VideoId = data.videoId;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
editContext!.operations.editField({
|
|
172
|
+
field: field.descriptor,
|
|
173
|
+
rawValue: JSON.stringify(raw),
|
|
174
|
+
refresh: "immediate",
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
150
179
|
const notEmpty = variant?.mediaId;
|
|
151
180
|
|
|
181
|
+
// Check if DAM selector should be enabled from configuration
|
|
182
|
+
const sharedienDamConfig = editContext?.configuration?.extensions?.sharedienDam as SharedienDamExtension;
|
|
183
|
+
const enableDamSelector = sharedienDamConfig?.enableDamSelector ?? false;
|
|
184
|
+
|
|
152
185
|
// Build actions for the dropdown menu
|
|
153
186
|
const menuActions: MenuAction[] = [
|
|
187
|
+
...(enableDamSelector
|
|
188
|
+
? [
|
|
189
|
+
{
|
|
190
|
+
id: "select-dam",
|
|
191
|
+
label: "Select from DAM",
|
|
192
|
+
icon: "pi pi-external-link",
|
|
193
|
+
onClick: () => selectFromDam(),
|
|
194
|
+
},
|
|
195
|
+
]
|
|
196
|
+
: []),
|
|
154
197
|
{
|
|
155
198
|
id: "select",
|
|
156
199
|
label: "Select",
|
|
@@ -235,7 +278,11 @@ export function PictureEditor({
|
|
|
235
278
|
style={style}
|
|
236
279
|
data-testid="select-media"
|
|
237
280
|
>
|
|
238
|
-
<ImageEditButton
|
|
281
|
+
<ImageEditButton
|
|
282
|
+
actions={menuActions}
|
|
283
|
+
compact={true}
|
|
284
|
+
defaultActionId={enableDamSelector ? "select-dam" : "select"}
|
|
285
|
+
/>
|
|
239
286
|
|
|
240
287
|
{showCropper && (
|
|
241
288
|
<PictureCropper
|
package/src/editor/Terminal.tsx
CHANGED
|
@@ -97,7 +97,7 @@ export const Terminal = forwardRef<
|
|
|
97
97
|
setMessages([...messageRef.current, { type: "response", text }]);
|
|
98
98
|
}
|
|
99
99
|
};
|
|
100
|
-
// @ts-ignore
|
|
100
|
+
// @ts-ignore
|
|
101
101
|
TerminalService.on("response", responseHandler);
|
|
102
102
|
return () => {
|
|
103
103
|
// @ts-ignore
|
package/src/editor/Titlebar.tsx
CHANGED
|
@@ -36,7 +36,6 @@ export function Titlebar() {
|
|
|
36
36
|
const isInsideMenu = menuElement && menuElement.contains(target);
|
|
37
37
|
const isInsideButton = buttonElement && buttonElement.contains(target);
|
|
38
38
|
|
|
39
|
-
// Check if the click is inside a PrimeReact overlay (which renders in portals)
|
|
40
39
|
const isInsideOverlay = target.closest(".p-overlaypanel") !== null;
|
|
41
40
|
|
|
42
41
|
// Close menu only if click is outside menu, button, and overlays
|
|
@@ -33,12 +33,15 @@ interface AgentCostDisplayProps {
|
|
|
33
33
|
outputCost: number;
|
|
34
34
|
cachedCost: number;
|
|
35
35
|
};
|
|
36
|
+
/** Cost limit for the agent (in USD) */
|
|
37
|
+
costLimit?: number | null;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
export function AgentCostDisplay({
|
|
39
41
|
className = "",
|
|
40
42
|
response,
|
|
41
43
|
totalTokens,
|
|
44
|
+
costLimit,
|
|
42
45
|
}: AgentCostDisplayProps) {
|
|
43
46
|
const [showCostPopover, setShowCostPopover] = useState(false);
|
|
44
47
|
const costPopoverRef = useRef<HTMLDivElement>(null);
|
|
@@ -118,16 +121,31 @@ export function AgentCostDisplay({
|
|
|
118
121
|
return null;
|
|
119
122
|
}
|
|
120
123
|
|
|
124
|
+
// Calculate percentage of limit used
|
|
125
|
+
const percentUsed = costLimit && costLimit > 0
|
|
126
|
+
? Math.min((displayTotalCost / costLimit) * 100, 100)
|
|
127
|
+
: null;
|
|
128
|
+
|
|
129
|
+
// Determine color based on usage
|
|
130
|
+
const getColorClass = () => {
|
|
131
|
+
if (!percentUsed) return "text-gray-400";
|
|
132
|
+
if (percentUsed >= 100) return "text-red-500";
|
|
133
|
+
if (percentUsed >= 80) return "text-orange-500";
|
|
134
|
+
if (percentUsed >= 60) return "text-yellow-600";
|
|
135
|
+
return "text-gray-400";
|
|
136
|
+
};
|
|
137
|
+
|
|
121
138
|
return (
|
|
122
139
|
<div className={`relative ${className}`} ref={costPopoverRef}>
|
|
123
140
|
<Popover open={showCostPopover} onOpenChange={setShowCostPopover}>
|
|
124
141
|
<PopoverTrigger asChild>
|
|
125
142
|
<div
|
|
126
|
-
className=
|
|
143
|
+
className={`cursor-pointer text-right hover:text-gray-600 ${getColorClass()}`}
|
|
127
144
|
style={{ fontSize: "10px" }}
|
|
128
145
|
onClick={() => setShowCostPopover(!showCostPopover)}
|
|
129
146
|
>
|
|
130
147
|
Total Cost: {displayTotalCost.toFixed(2)} {currency}
|
|
148
|
+
{costLimit && costLimit > 0 && ` / ${costLimit.toFixed(2)} ${currency}`}
|
|
131
149
|
</div>
|
|
132
150
|
</PopoverTrigger>
|
|
133
151
|
<PopoverContent className="w-80 p-4" align="end" side="bottom">
|
|
@@ -137,10 +155,48 @@ export function AgentCostDisplay({
|
|
|
137
155
|
<div>Cost Breakdown</div>
|
|
138
156
|
<span className="text-sm font-semibold">
|
|
139
157
|
{displayTotalCost.toFixed(2)} {currency}
|
|
158
|
+
{costLimit && costLimit > 0 && (
|
|
159
|
+
<span className="ml-1 text-xs text-gray-500">
|
|
160
|
+
/ {costLimit.toFixed(2)} {currency}
|
|
161
|
+
</span>
|
|
162
|
+
)}
|
|
140
163
|
</span>
|
|
141
164
|
</h4>
|
|
142
165
|
</div>
|
|
143
166
|
|
|
167
|
+
{/* Cost limit warning */}
|
|
168
|
+
{costLimit && costLimit > 0 && (
|
|
169
|
+
<div className="rounded-md bg-gray-50 p-3">
|
|
170
|
+
<div className="mb-2 flex items-center justify-between text-xs">
|
|
171
|
+
<span className="font-medium text-gray-700">Cost Limit Usage</span>
|
|
172
|
+
<span className={`font-semibold ${getColorClass()}`}>
|
|
173
|
+
{percentUsed?.toFixed(1)}%
|
|
174
|
+
</span>
|
|
175
|
+
</div>
|
|
176
|
+
<div className="h-2 w-full overflow-hidden rounded-full bg-gray-200">
|
|
177
|
+
<div
|
|
178
|
+
className={`h-full transition-all ${
|
|
179
|
+
percentUsed && percentUsed >= 100
|
|
180
|
+
? "bg-red-500"
|
|
181
|
+
: percentUsed && percentUsed >= 80
|
|
182
|
+
? "bg-orange-500"
|
|
183
|
+
: percentUsed && percentUsed >= 60
|
|
184
|
+
? "bg-yellow-500"
|
|
185
|
+
: "bg-blue-500"
|
|
186
|
+
}`}
|
|
187
|
+
style={{ width: `${Math.min(percentUsed || 0, 100)}%` }}
|
|
188
|
+
/>
|
|
189
|
+
</div>
|
|
190
|
+
{percentUsed && percentUsed >= 90 && (
|
|
191
|
+
<p className="mt-2 text-xs text-gray-600">
|
|
192
|
+
{percentUsed >= 100
|
|
193
|
+
? "Cost limit reached. Execution will stop on next API call."
|
|
194
|
+
: "Approaching cost limit. Consider extending if needed."}
|
|
195
|
+
</p>
|
|
196
|
+
)}
|
|
197
|
+
</div>
|
|
198
|
+
)}
|
|
199
|
+
|
|
144
200
|
<div className="space-y-3">
|
|
145
201
|
{/* Session Cost Breakdown */}
|
|
146
202
|
{(hasTotalData || hasTokenData) && (
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { cn } from "../../lib/utils";
|
|
3
|
+
import { AgentStatusSummary } from "./useAgentStatus";
|
|
4
|
+
import { Agent } from "../services/agentService";
|
|
5
|
+
|
|
6
|
+
interface AgentStatusBadgeProps {
|
|
7
|
+
status: AgentStatusSummary;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface BadgeConfig {
|
|
12
|
+
baseClasses: string;
|
|
13
|
+
bgColor: string;
|
|
14
|
+
title: string;
|
|
15
|
+
content?: React.ReactNode;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface AgentStatusConfig {
|
|
19
|
+
color: string;
|
|
20
|
+
label: string;
|
|
21
|
+
shouldPulse: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Helper function to get status configuration for an individual agent
|
|
26
|
+
* Handles both numeric and string status values
|
|
27
|
+
* Enum: New=0, Running=1, WaitingForApproval=2, Completed=3, Error=4, Closed=5, Idle=6
|
|
28
|
+
*/
|
|
29
|
+
export function getAgentStatusConfig(agent: Agent): AgentStatusConfig {
|
|
30
|
+
const status: string | number = agent.status;
|
|
31
|
+
|
|
32
|
+
// Handle both string (camelCase from backend) and numeric (enum value) status
|
|
33
|
+
if (status === "new" || status === 0) {
|
|
34
|
+
return {
|
|
35
|
+
color: "bg-gray-400",
|
|
36
|
+
label: "Status: New",
|
|
37
|
+
shouldPulse: false,
|
|
38
|
+
};
|
|
39
|
+
} else if (status === "running" || status === 1) {
|
|
40
|
+
return {
|
|
41
|
+
color: "bg-blue-500",
|
|
42
|
+
label: "Status: Running",
|
|
43
|
+
shouldPulse: true,
|
|
44
|
+
};
|
|
45
|
+
} else if (status === "waitingForApproval" || status === 2) {
|
|
46
|
+
return {
|
|
47
|
+
color: "bg-amber-500",
|
|
48
|
+
label: "Status: Waiting for Approval",
|
|
49
|
+
shouldPulse: false,
|
|
50
|
+
};
|
|
51
|
+
} else if (status === "completed" || status === 3) {
|
|
52
|
+
return {
|
|
53
|
+
color: "bg-green-500",
|
|
54
|
+
label: "Status: Completed",
|
|
55
|
+
shouldPulse: false,
|
|
56
|
+
};
|
|
57
|
+
} else if (status === "error" || status === 4) {
|
|
58
|
+
return {
|
|
59
|
+
color: "bg-red-500",
|
|
60
|
+
label: "Status: Error",
|
|
61
|
+
shouldPulse: false,
|
|
62
|
+
};
|
|
63
|
+
} else if (status === "closed" || status === 5) {
|
|
64
|
+
return {
|
|
65
|
+
color: "bg-gray-400",
|
|
66
|
+
label: "Status: Closed",
|
|
67
|
+
shouldPulse: false,
|
|
68
|
+
};
|
|
69
|
+
} else if (status === "idle" || status === 6) {
|
|
70
|
+
return {
|
|
71
|
+
color: "bg-gray-300",
|
|
72
|
+
label: "Status: Idle",
|
|
73
|
+
shouldPulse: false,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Default fallback - log for debugging
|
|
78
|
+
console.warn("Unknown agent status:", status, typeof status);
|
|
79
|
+
return {
|
|
80
|
+
color: "bg-gray-400",
|
|
81
|
+
label: `Status: Unknown (${status})`,
|
|
82
|
+
shouldPulse: false,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Badge that overlays on the agent icon to show status
|
|
88
|
+
* Always displays the total number of active agents with color-coded status:
|
|
89
|
+
* - Amber: Agents waiting for approval (highest priority)
|
|
90
|
+
* - Blue (pulsing): Agents actively running
|
|
91
|
+
* - Green: Other active agents
|
|
92
|
+
*/
|
|
93
|
+
export function AgentStatusBadge({ status, className }: AgentStatusBadgeProps) {
|
|
94
|
+
// No activity - don't show anything
|
|
95
|
+
if (!status.hasActivity) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const getBadgeConfig = (): BadgeConfig => {
|
|
100
|
+
// Waiting for approval takes priority (most urgent)
|
|
101
|
+
if (status.waitingForApproval > 0) {
|
|
102
|
+
return {
|
|
103
|
+
baseClasses: "-right-2 -top-2",
|
|
104
|
+
bgColor: "bg-amber-500",
|
|
105
|
+
title: `${status.waitingForApproval} agent${status.waitingForApproval > 1 ? "s" : ""} waiting for approval (${status.total} total active)`,
|
|
106
|
+
content: <span className="text-[8px] font-bold">{status.total}</span>,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Running agents - show count with pulsing indicator
|
|
111
|
+
if (status.running > 0) {
|
|
112
|
+
return {
|
|
113
|
+
baseClasses: "-right-2 -top-2",
|
|
114
|
+
bgColor: "bg-blue-500 animate-pulse",
|
|
115
|
+
title: `${status.running} agent${status.running > 1 ? "s" : ""} running (${status.total} total active)`,
|
|
116
|
+
content: <span className="text-[8px] font-bold">{status.total}</span>,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Other active agents - show count
|
|
121
|
+
return {
|
|
122
|
+
baseClasses: "-right-2 -top-2",
|
|
123
|
+
bgColor: "bg-green-500",
|
|
124
|
+
title: `${status.total} active agent${status.total > 1 ? "s" : ""}`,
|
|
125
|
+
content: <span className="text-[8px] font-bold">{status.total}</span>,
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const config = getBadgeConfig();
|
|
130
|
+
|
|
131
|
+
return (
|
|
132
|
+
<div
|
|
133
|
+
className={cn(
|
|
134
|
+
"absolute flex h-3 w-3 items-center justify-center rounded-full text-white shadow-md ring-1 ring-white",
|
|
135
|
+
config.baseClasses,
|
|
136
|
+
config.bgColor,
|
|
137
|
+
className,
|
|
138
|
+
)}
|
|
139
|
+
title={config.title}
|
|
140
|
+
>
|
|
141
|
+
{config.content}
|
|
142
|
+
</div>
|
|
143
|
+
);
|
|
144
|
+
}
|