@alpaca-editor/core 1.0.4184 → 1.0.4186

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/agents-view/AgentCard.d.ts +12 -0
  2. package/dist/agents-view/AgentCard.js +30 -0
  3. package/dist/agents-view/AgentCard.js.map +1 -0
  4. package/dist/agents-view/AgentsView.d.ts +2 -2
  5. package/dist/agents-view/AgentsView.js +102 -85
  6. package/dist/agents-view/AgentsView.js.map +1 -1
  7. package/dist/agents-view/ProfileAgentsGroup.d.ts +17 -0
  8. package/dist/agents-view/ProfileAgentsGroup.js +13 -0
  9. package/dist/agents-view/ProfileAgentsGroup.js.map +1 -0
  10. package/dist/components/ui/popover.d.ts +3 -3
  11. package/dist/config/config.js +8 -1
  12. package/dist/config/config.js.map +1 -1
  13. package/dist/editor/ConfirmationDialog.js.map +1 -1
  14. package/dist/editor/ImageEditButton.js.map +1 -1
  15. package/dist/editor/ai/AgentProfilesOverview.js +1 -1
  16. package/dist/editor/ai/AgentProfilesOverview.js.map +1 -1
  17. package/dist/editor/ai/Agents.js +6 -2
  18. package/dist/editor/ai/Agents.js.map +1 -1
  19. package/dist/editor/client/EditorShell.js +17 -0
  20. package/dist/editor/client/EditorShell.js.map +1 -1
  21. package/dist/editor/client/ui/EditorChrome.js +1 -0
  22. package/dist/editor/client/ui/EditorChrome.js.map +1 -1
  23. package/dist/editor/commands/componentCommands.js +19 -1
  24. package/dist/editor/commands/componentCommands.js.map +1 -1
  25. package/dist/editor/field-types/DateTimeFieldEditor.js +1 -1
  26. package/dist/editor/field-types/DateTimeFieldEditor.js.map +1 -1
  27. package/dist/editor/page-editor-chrome/FrameMenu.js +7 -3
  28. package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
  29. package/dist/editor/page-viewer/PageViewerFrame.js +7 -28
  30. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  31. package/dist/editor/reviews/Comment.js +56 -4
  32. package/dist/editor/reviews/Comment.js.map +1 -1
  33. package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -1
  34. package/dist/editor/reviews/CommentPopover.js +1 -1
  35. package/dist/editor/reviews/CommentPopover.js.map +1 -1
  36. package/dist/editor/reviews/SuggestionDisplayPopover.js.map +1 -1
  37. package/dist/editor/services/agentService.d.ts +19 -0
  38. package/dist/editor/services/agentService.js +39 -0
  39. package/dist/editor/services/agentService.js.map +1 -1
  40. package/dist/editor/services/aiService.d.ts +2 -0
  41. package/dist/editor/services/aiService.js.map +1 -1
  42. package/dist/editor/services/serviceHelper.d.ts +1 -0
  43. package/dist/editor/services/serviceHelper.js +58 -4
  44. package/dist/editor/services/serviceHelper.js.map +1 -1
  45. package/dist/editor/views/ParheliaView.d.ts +5 -0
  46. package/dist/editor/views/ParheliaView.js +136 -0
  47. package/dist/editor/views/ParheliaView.js.map +1 -0
  48. package/dist/revision.d.ts +2 -2
  49. package/dist/revision.js +2 -2
  50. package/dist/styles.css +8 -6
  51. package/dist/tour/default-tour.js +24 -9
  52. package/dist/tour/default-tour.js.map +1 -1
  53. package/package.json +12 -8
  54. package/src/agents-view/AgentCard.tsx +162 -0
  55. package/src/agents-view/AgentsView.tsx +218 -253
  56. package/src/agents-view/ProfileAgentsGroup.tsx +123 -0
  57. package/src/config/config.tsx +10 -1
  58. package/src/editor/ConfirmationDialog.tsx +1 -1
  59. package/src/editor/ImageEditButton.tsx +4 -2
  60. package/src/editor/ai/AgentProfilesOverview.tsx +1 -2
  61. package/src/editor/ai/Agents.tsx +7 -2
  62. package/src/editor/client/EditorShell.tsx +18 -0
  63. package/src/editor/client/ui/EditorChrome.tsx +1 -0
  64. package/src/editor/commands/componentCommands.tsx +19 -1
  65. package/src/editor/field-types/DateTimeFieldEditor.tsx +5 -4
  66. package/src/editor/page-editor-chrome/FrameMenu.tsx +9 -3
  67. package/src/editor/page-viewer/PageViewerFrame.tsx +7 -36
  68. package/src/editor/reviews/Comment.tsx +85 -4
  69. package/src/editor/reviews/CommentDisplayPopover.tsx +2 -2
  70. package/src/editor/reviews/CommentPopover.tsx +2 -2
  71. package/src/editor/reviews/SuggestionDisplayPopover.tsx +1 -1
  72. package/src/editor/services/agentService.ts +77 -0
  73. package/src/editor/services/aiService.ts +4 -0
  74. package/src/editor/services/serviceHelper.ts +92 -28
  75. package/src/editor/views/ParheliaView.tsx +207 -0
  76. package/src/revision.ts +2 -2
  77. package/src/tour/default-tour.tsx +63 -48
@@ -517,6 +517,19 @@ export interface GetAgentsResponse {
517
517
  hasMore: boolean;
518
518
  }
519
519
 
520
+ export interface ProfileAgentsGroup {
521
+ profileId: string | null;
522
+ profileName: string;
523
+ profileSvgIcon?: string;
524
+ totalClosedCount: number;
525
+ agents: Agent[];
526
+ }
527
+
528
+ export interface GetAgentsGroupedResponse {
529
+ activeAgents: Agent[];
530
+ closedAgentsByProfile: ProfileAgentsGroup[];
531
+ }
532
+
520
533
  /**
521
534
  * Gets all agents for the current user with pagination and search support
522
535
  */
@@ -550,6 +563,70 @@ export async function getActiveAgents(
550
563
  return result.data || { agents: [], totalCount: 0, hasMore: false };
551
564
  }
552
565
 
566
+ /**
567
+ * Gets all active agents and closed agents grouped by profile for the current user
568
+ */
569
+ export async function getAgentsGrouped(
570
+ searchTerm?: string,
571
+ includeShared: boolean = true,
572
+ includeOwned: boolean = true,
573
+ ): Promise<GetAgentsGroupedResponse> {
574
+ const queryParams = new URLSearchParams();
575
+
576
+ if (searchTerm) queryParams.append("searchTerm", searchTerm);
577
+ queryParams.append("includeShared", includeShared.toString());
578
+ queryParams.append("includeOwned", includeOwned.toString());
579
+
580
+ const queryString = queryParams.toString();
581
+ const url =
582
+ AGENT_BASE_URL +
583
+ "/getAgentsGroupedByProfile" +
584
+ (queryString ? `?${queryString}` : "");
585
+
586
+ const result = await get<GetAgentsGroupedResponse>(url);
587
+
588
+ if (result.type !== "success") {
589
+ throw new Error(
590
+ `Failed to get grouped agents: ${result.summary || "Unknown error"} ${result.details || ""}`,
591
+ );
592
+ }
593
+
594
+ return result.data || { activeAgents: [], closedAgentsByProfile: [] };
595
+ }
596
+
597
+ /**
598
+ * Gets more closed agents for a specific profile
599
+ */
600
+ export async function getClosedAgentsByProfile(
601
+ profileId: string,
602
+ skip: number,
603
+ limit: number,
604
+ searchTerm?: string,
605
+ ): Promise<GetAgentsResponse> {
606
+ const queryParams = new URLSearchParams();
607
+
608
+ queryParams.append("profileId", profileId);
609
+ queryParams.append("skip", skip.toString());
610
+ queryParams.append("limit", limit.toString());
611
+ if (searchTerm) queryParams.append("searchTerm", searchTerm);
612
+
613
+ const queryString = queryParams.toString();
614
+ const url =
615
+ AGENT_BASE_URL +
616
+ "/getClosedAgentsByProfile" +
617
+ (queryString ? `?${queryString}` : "");
618
+
619
+ const result = await get<GetAgentsResponse>(url);
620
+
621
+ if (result.type !== "success") {
622
+ throw new Error(
623
+ `Failed to get closed agents by profile: ${result.summary || "Unknown error"} ${result.details || ""}`,
624
+ );
625
+ }
626
+
627
+ return result.data || { agents: [], totalCount: 0, hasMore: false };
628
+ }
629
+
553
630
  /**
554
631
  * Gets all closed agents for the current user
555
632
  * @deprecated Use getActiveAgents() instead - the backend now returns all agents (including closed) by default
@@ -24,6 +24,10 @@ export type AiProfile = {
24
24
  hiddenFromOverview?: boolean;
25
25
  // Optional cost limit configured on the profile (USD)
26
26
  costLimit?: number | null;
27
+ // Number of user browse history items to include (0 = disabled)
28
+ includeUserBrowseHistory?: number;
29
+ // Whether to include user favorites
30
+ includeUserFavorites?: boolean;
27
31
  };
28
32
 
29
33
  // In-flight de-duplication and short TTL cache for AI profiles to avoid
@@ -2,47 +2,102 @@ export type ExecutionResult<T> = {
2
2
  type: "success" | "error" | "unauthorized";
3
3
  summary?: string;
4
4
  details?: string;
5
+ rawDetails?: string; // Original unprocessed error details (for logging/debugging)
5
6
  response: Response;
6
7
  data?: T;
7
8
  };
8
9
 
10
+ /**
11
+ * Extracts a user-friendly error message from the response.
12
+ * Handles HTML error pages by extracting the title or exception details.
13
+ * Also extracts source file information if available.
14
+ */
15
+ function extractErrorMessage(message: string): string {
16
+ // Check if it's an HTML error page
17
+ if (message.includes("<!DOCTYPE html>") || message.includes("<html>")) {
18
+ let errorMessage = "";
19
+
20
+ // Try to extract the title (which usually contains the error message)
21
+ const titleMatch = message.match(/<title>(.*?)<\/title>/i);
22
+ if (titleMatch && titleMatch[1]) {
23
+ // Remove "Server Error" prefix if present
24
+ errorMessage = titleMatch[1].replace(
25
+ /^Server Error in '.*?' Application\.\s*/i,
26
+ "",
27
+ );
28
+ }
29
+
30
+ // If no title, try h2 tag (exception message)
31
+ if (!errorMessage) {
32
+ const h2Match = message.match(/<h2>\s*<i>(.*?)<\/i>\s*<\/h2>/i);
33
+ if (h2Match && h2Match[1]) {
34
+ errorMessage = h2Match[1];
35
+ }
36
+ }
37
+
38
+ // If no h2, try Exception Details
39
+ if (!errorMessage) {
40
+ const exceptionMatch = message.match(
41
+ /<b>\s*Exception Details:\s*<\/b>(.*?)(?:<br>|<\/)/i,
42
+ );
43
+ if (exceptionMatch && exceptionMatch[1]) {
44
+ errorMessage = exceptionMatch[1].replace(/<[^>]*>/g, "").trim();
45
+ }
46
+ }
47
+
48
+ // Try to extract source file and line information
49
+ const sourceMatch = message.match(
50
+ /<b>\s*Source File:\s*<\/b>\s*([^<]+)<b>\s*&nbsp;&nbsp;\s*Line:\s*<\/b>\s*(\d+)/i,
51
+ );
52
+ if (sourceMatch && sourceMatch[1] && sourceMatch[2]) {
53
+ const fileName = sourceMatch[1].trim().split("\\").pop();
54
+ errorMessage += ` (${fileName}:${sourceMatch[2]})`;
55
+ }
56
+
57
+ if (errorMessage) {
58
+ return errorMessage;
59
+ }
60
+
61
+ // If we can't parse it, just strip all HTML tags
62
+ const cleaned = message.replace(/<[^>]*>/g, "").trim();
63
+ // Limit length for toast display
64
+ return cleaned.length > 200 ? cleaned.substring(0, 200) + "..." : cleaned;
65
+ }
66
+
67
+ return message;
68
+ }
69
+
9
70
  export async function post<T>(
10
71
  url: string,
11
72
  body: any,
12
- session?: string
73
+ session?: string,
13
74
  ): Promise<ExecutionResult<T>> {
14
75
  if (session) url += "?sessionId=" + session;
15
76
 
16
- const response = await fetch(
17
- url,
18
- {
19
- method: "POST",
20
- body: JSON.stringify(body),
21
- credentials: "include",
22
- headers: {
23
- "Content-Type": "application/json",
24
- },
25
- }
26
- );
77
+ const response = await fetch(url, {
78
+ method: "POST",
79
+ body: JSON.stringify(body),
80
+ credentials: "include",
81
+ headers: {
82
+ "Content-Type": "application/json",
83
+ },
84
+ });
27
85
 
28
86
  return handleResponse<T>(response);
29
87
  }
30
88
 
31
89
  export async function get<T>(
32
90
  url: string,
33
- session?: string
91
+ session?: string,
34
92
  ): Promise<ExecutionResult<T>> {
35
93
  if (session) url += "?sessionId=" + session;
36
- const response = await fetch(
37
- url,
38
- {
39
- method: "GET",
40
- credentials: "include",
41
- headers: {
42
- "Content-Type": "application/json",
43
- },
44
- }
45
- );
94
+ const response = await fetch(url, {
95
+ method: "GET",
96
+ credentials: "include",
97
+ headers: {
98
+ "Content-Type": "application/json",
99
+ },
100
+ });
46
101
 
47
102
  return handleResponse<T>(response);
48
103
  }
@@ -54,17 +109,20 @@ async function handleResponse<T>(
54
109
  const data = await response.json();
55
110
  return {
56
111
  type: "error",
57
- summary: data.summary,
58
- details: data.details,
112
+ summary: data.summary ? extractErrorMessage(data.summary) : data.summary,
113
+ details: data.details ? extractErrorMessage(data.details) : data.details,
114
+ rawDetails: data.details || data.summary, // Preserve original for logging
59
115
  response,
60
116
  };
61
117
  }
62
118
 
63
119
  if (response.status === 500 || response.redirected) {
120
+ const rawText = await response.text();
64
121
  return {
65
122
  type: "error",
66
123
  summary: "Error",
67
- details: await response.text(),
124
+ details: extractErrorMessage(rawText),
125
+ rawDetails: rawText, // Preserve full HTML error with stack trace
68
126
  response,
69
127
  };
70
128
  }
@@ -77,9 +135,15 @@ async function handleResponse<T>(
77
135
  response,
78
136
  };
79
137
  }
80
-
138
+
81
139
  if (response.status !== 200) {
82
- return { type: "error", response, details: await response.text() };
140
+ const rawText = await response.text();
141
+ return {
142
+ type: "error",
143
+ response,
144
+ details: extractErrorMessage(rawText),
145
+ rawDetails: rawText, // Preserve original for logging
146
+ };
83
147
  }
84
148
 
85
149
  if (!response.headers.get("content-type")?.includes("application/json")) {
@@ -0,0 +1,207 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import { AgentTerminal } from "../ai/AgentTerminal";
3
+ import { Agent, AgentMetadata } from "../services/agentService";
4
+ import { useEditContext } from "../client/editContext";
5
+ import { AiProfile, loadAiProfiles } from "../services/aiService";
6
+ import { AgentProfilesOverview } from "../ai/AgentProfilesOverview";
7
+
8
+ /**
9
+ * ParheliaView - A minimalistic, focused view for a single agent terminal
10
+ * This view provides a distraction-free interface with just the agent terminal centered on screen
11
+ */
12
+ export function ParheliaView() {
13
+ const editContext = useEditContext();
14
+ const [agent, setAgent] = useState<Agent | null>(null);
15
+ const [initialMetadata, setInitialMetadata] = useState<
16
+ AgentMetadata | undefined
17
+ >(undefined);
18
+ const [availableProfiles, setAvailableProfiles] = useState<AiProfile[]>([]);
19
+ const [loadingProfiles, setLoadingProfiles] = useState(false);
20
+
21
+ // Load available profiles
22
+ useEffect(() => {
23
+ let cancelled = false;
24
+ (async () => {
25
+ try {
26
+ setLoadingProfiles(true);
27
+ const profiles = await loadAiProfiles(
28
+ editContext?.currentItemDescriptor,
29
+ );
30
+ if (cancelled) return;
31
+ setAvailableProfiles(profiles || []);
32
+ } catch (e) {
33
+ console.error("Failed to load AI profiles", e);
34
+ } finally {
35
+ if (!cancelled) setLoadingProfiles(false);
36
+ }
37
+ })();
38
+ return () => {
39
+ cancelled = true;
40
+ };
41
+ }, [editContext?.currentItemDescriptor?.id]);
42
+
43
+ // Listen for external requests to add a new agent
44
+ useEffect(() => {
45
+ const handleAddNewAgent = (ev: Event) => {
46
+ let metadata: AgentMetadata | undefined = undefined;
47
+ try {
48
+ const ce = ev as unknown as CustomEvent;
49
+ metadata = (ce.detail && (ce.detail as any).metadata) as
50
+ | AgentMetadata
51
+ | undefined;
52
+ } catch {}
53
+
54
+ // Create a new agent
55
+ const newAgent: Agent = {
56
+ status: "new",
57
+ id: crypto.randomUUID(),
58
+ name: `New Agent`,
59
+ updatedDate: new Date().toISOString(),
60
+ userId: "",
61
+ };
62
+ setAgent(newAgent);
63
+ setInitialMetadata(metadata);
64
+
65
+ // Focus the prompt after the agent mounts
66
+ setTimeout(() => {
67
+ try {
68
+ window.dispatchEvent(new CustomEvent("editor:focusAgentPrompt"));
69
+ } catch {}
70
+ }, 60);
71
+ };
72
+
73
+ window.addEventListener(
74
+ "editor:addNewAgent",
75
+ handleAddNewAgent as EventListener,
76
+ );
77
+ return () =>
78
+ window.removeEventListener(
79
+ "editor:addNewAgent",
80
+ handleAddNewAgent as EventListener,
81
+ );
82
+ }, []);
83
+
84
+ // Listen for external requests to open an existing agent
85
+ useEffect(() => {
86
+ const handleOpenAgent = (ev: Event) => {
87
+ try {
88
+ const ce = ev as unknown as CustomEvent;
89
+ const agentId = (ce.detail && (ce.detail as any).agentId) as
90
+ | string
91
+ | undefined;
92
+
93
+ if (!agentId) return;
94
+
95
+ // Load the agent
96
+ const loadedAgent: Agent = {
97
+ id: agentId,
98
+ name: "Loading...",
99
+ status: "running",
100
+ userId: "",
101
+ updatedDate: new Date().toISOString(),
102
+ };
103
+ setAgent(loadedAgent);
104
+ } catch (error) {
105
+ console.error("Error opening agent:", error);
106
+ }
107
+ };
108
+
109
+ window.addEventListener(
110
+ "editor:openAgent",
111
+ handleOpenAgent as EventListener,
112
+ );
113
+ return () =>
114
+ window.removeEventListener(
115
+ "editor:openAgent",
116
+ handleOpenAgent as EventListener,
117
+ );
118
+ }, []);
119
+
120
+ const handleSelectProfile = (profileId: string) => {
121
+ try {
122
+ const selected = availableProfiles.find((p) => p.id === profileId);
123
+ const metadata = selected
124
+ ? {
125
+ profile: selected.name,
126
+ additionalData: {
127
+ profileId: selected.id,
128
+ profileName: selected.name,
129
+ },
130
+ }
131
+ : undefined;
132
+
133
+ // Create a new agent with the selected profile
134
+ const newAgent: Agent = {
135
+ status: "new",
136
+ id: crypto.randomUUID(),
137
+ name: `New Agent`,
138
+ updatedDate: new Date().toISOString(),
139
+ userId: "",
140
+ };
141
+ setAgent(newAgent);
142
+ setInitialMetadata(metadata);
143
+
144
+ // Focus the prompt after the agent mounts
145
+ setTimeout(() => {
146
+ try {
147
+ window.dispatchEvent(new CustomEvent("editor:focusAgentPrompt"));
148
+ } catch {}
149
+ }, 60);
150
+ } catch (error) {
151
+ console.error("Error creating agent with profile:", error);
152
+ }
153
+ };
154
+
155
+ // Empty state when no agent is active
156
+ if (!agent) {
157
+ return (
158
+ <div className="flex h-full items-center justify-center bg-gray-50 p-8">
159
+ <div className="w-full max-w-4xl">
160
+ {loadingProfiles ? (
161
+ <div className="text-center text-sm text-gray-500">
162
+ Loading profiles...
163
+ </div>
164
+ ) : availableProfiles.length > 0 ? (
165
+ <div className="flex flex-col gap-6">
166
+ <div className="text-center">
167
+ <h2 className="text-2xl font-light tracking-wide text-gray-900">
168
+ parhelia
169
+ </h2>
170
+ <p className="mt-2 text-sm text-gray-500">
171
+ Select an AI agent profile to begin
172
+ </p>
173
+ </div>
174
+ <AgentProfilesOverview
175
+ profiles={availableProfiles}
176
+ onSelectProfile={handleSelectProfile}
177
+ />
178
+ </div>
179
+ ) : (
180
+ <div className="text-center">
181
+ <h2 className="text-2xl font-light tracking-wide text-gray-900">
182
+ parhelia
183
+ </h2>
184
+ <p className="mt-2 text-sm text-gray-500">
185
+ No agent profiles available
186
+ </p>
187
+ </div>
188
+ )}
189
+ </div>
190
+ </div>
191
+ );
192
+ }
193
+
194
+ // Active agent terminal view
195
+ return (
196
+ <div className="flex h-full items-center justify-center bg-gray-50">
197
+ <div className="h-full w-full max-w-6xl">
198
+ <AgentTerminal
199
+ agentStub={agent}
200
+ initialMetadata={initialMetadata}
201
+ profiles={availableProfiles}
202
+ isActive={true}
203
+ />
204
+ </div>
205
+ </div>
206
+ );
207
+ }
package/src/revision.ts CHANGED
@@ -1,2 +1,2 @@
1
- export const version = "1.0.4184";
2
- export const buildDate = "2025-10-23 01:46:04";
1
+ export const version = "1.0.4186";
2
+ export const buildDate = "2025-10-24 01:52:39";
@@ -63,7 +63,7 @@ export function getDefaultTourSteps(
63
63
  </>
64
64
  ),
65
65
  focusElement: '#create-new-page-button, [data-testid="create-new-page-button"]',
66
- bubblePosition: "top-right",
66
+ bubblePosition: "right",
67
67
  waitForUserInput: async () => {
68
68
  await waitForElement(".tour-pick-location");
69
69
  },
@@ -261,63 +261,78 @@ export function getDefaultTourSteps(
261
261
  <>
262
262
  <ArrowRight className="inline h-4 w-4" /> Great, show me!
263
263
  </>
264
- ),
265
- onClick: () => nextStep("open-agents-panel"),
264
+ ),
265
+ onClick: () => nextStep("open-agents-panel"),
266
+ },
267
+ ],
266
268
  },
267
- ],
268
- },
269
269
  "open-agents-panel": {
270
- title: "Open AI Agents",
271
- description: "Open the Agents panel to start an AI chat.",
272
- focusElement: `#agents-panel-button, [data-testid="agents-panel-button"]`,
273
- bubblePosition: "left",
274
- waitForUserInput: async () => {
275
- // Falls das Panel schon offen & Prompt da ist → sofort weiter
276
- //if (document.querySelector(`[data-testid="agent-terminal-prompt"]`)) return;
277
-
278
- const btn = document.querySelector(`#agents-panel-button, [data-testid="agents-panel-button"]`);
279
- if (btn) {
280
- await new Promise<void>((resolve) => {
270
+ title: "Open AI Agents",
271
+ description: "Open the Agents panel to start an AI chat.",
272
+ focusElement: "#agents-panel-button, [data-testid='agents-panel-button']",
273
+ bubblePosition: "left",
274
+ waitForUserInput: async () => {
275
+ const btn = document.querySelector(
276
+ "#agents-panel-button, [data-testid='agents-panel-button']"
277
+ );
278
+ if (btn) {
279
+ await new Promise<void>((resolve) => {
281
280
  btn.addEventListener("click", () => resolve(), { once: true });
282
- });
283
- }
284
-
285
- await delay(500);
286
- await waitForElement(`[data-testid="agent-terminal-prompt"]`);
287
- },
288
- nextStep: "start-agent-chat",
289
- },
281
+ });
282
+ }
283
+ await delay(500);
284
+ await waitForElement("[data-testid='agent-profile-card']");
285
+ },
286
+ nextStep: "choose-agent",
287
+ },
288
+ "choose-agent": {
289
+ title: "Choose an AI Agent",
290
+ description: "Choose an AI agent to assist you with your tasks.",
291
+ focusElement: "#agent-profile-card, [data-testid='agent-profile-card']",
292
+ bubblePosition: "left",
293
+ waitForUserInput: async () => {
294
+ const btn = document.querySelector(
295
+ "#agent-profile-card, [data-testid='agent-profile-card']"
296
+ );
297
+ if (btn) {
298
+ await new Promise<void>((resolve) => {
299
+ btn.addEventListener("click", () => resolve(), { once: true });
300
+ });
301
+ }
302
+ await delay(500);
303
+ await waitForElement("[data-testid='agent-terminal-prompt']");
304
+ },
305
+ nextStep: "start-agent-chat",
306
+ },
290
307
  "start-agent-chat": {
291
- title: "Start a chat",
292
- description: "Type a prompt for the agent - Let AI create your whole webppage or just parts and press Enter.",
293
- focusElement: `[data-testid="agent-terminal-prompt"]`,
294
- bubblePosition: "top-left",
295
- buttons: [
296
- {
297
- label: (
308
+ title: "Start a chat",
309
+ description:
310
+ "Type a prompt for the agent - Let AI create your whole webpage or just parts and press Enter.",
311
+ focusElement: "[data-testid='agent-terminal-prompt']",
312
+ bubblePosition: "top-left",
313
+ buttons: [
314
+ {
315
+ label: (
298
316
  <>
299
317
  <Check className="inline h-4 w-4" /> Got it
300
318
  </>
301
- ),
302
- onClick: () => nextStep("congratulations"),
303
- className: "button button-primary mt-2",
319
+ ),
320
+ onClick: () => nextStep("congratulations"),
321
+ className: "button button-primary mt-2",
322
+ },
323
+ ],
324
+ waitForUserInput: async () => {
325
+ await waitForInput("[data-testid='agent-terminal-prompt'] textarea", 5);
326
+ },
327
+ nextStep: "congratulations",
304
328
  },
305
- ],
306
- waitForUserInput: async () => {
307
- await waitForInput(
308
- `[data-testid="agent-terminal-prompt"] textarea`,
309
- 5,
310
- );
311
- },
312
- nextStep: "congratulations",
313
- },
314
329
 
315
330
  congratulations: {
316
- title: "Congratulations!",
317
- description: "You have completed the tour and are now an AI Editor Pro!",
318
- buttons: [
319
- {
320
- label: (
331
+ title: "Congratulations!",
332
+ description: "You have completed the tour and are now an AI Editor Pro!",
333
+ buttons: [
334
+ {
335
+ label: (
321
336
  <>
322
337
  <Check className="inline h-4 w-4" /> Finish Tour
323
338
  </>