@alpaca-editor/core 1.0.4049 → 1.0.4052

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 (69) hide show
  1. package/dist/components/ui/textarea.js +1 -1
  2. package/dist/components/ui/textarea.js.map +1 -1
  3. package/dist/editor/Terminal.js +3 -3
  4. package/dist/editor/Terminal.js.map +1 -1
  5. package/dist/editor/ai/AgentCostDisplay.js +2 -2
  6. package/dist/editor/ai/AgentCostDisplay.js.map +1 -1
  7. package/dist/editor/ai/AgentHistory.d.ts +4 -4
  8. package/dist/editor/ai/AgentHistory.js +1 -1
  9. package/dist/editor/ai/AgentHistory.js.map +1 -1
  10. package/dist/editor/ai/AgentTerminal.d.ts +4 -0
  11. package/dist/editor/ai/AgentTerminal.js +753 -0
  12. package/dist/editor/ai/AgentTerminal.js.map +1 -0
  13. package/dist/editor/ai/Agents.d.ts +1 -3
  14. package/dist/editor/ai/Agents.js +213 -353
  15. package/dist/editor/ai/Agents.js.map +1 -1
  16. package/dist/editor/ai/AiPromptPopover.js +2 -2
  17. package/dist/editor/ai/AiPromptPopover.js.map +1 -1
  18. package/dist/editor/ai/AiResponseMessage.d.ts +0 -1
  19. package/dist/editor/ai/AiResponseMessage.js +23 -143
  20. package/dist/editor/ai/AiResponseMessage.js.map +1 -1
  21. package/dist/editor/ai/AiTerminal.d.ts +5 -23
  22. package/dist/editor/ai/AiTerminal.js +81 -824
  23. package/dist/editor/ai/AiTerminal.js.map +1 -1
  24. package/dist/editor/ai/DancingDots.d.ts +1 -0
  25. package/dist/editor/ai/DancingDots.js +6 -0
  26. package/dist/editor/ai/DancingDots.js.map +1 -0
  27. package/dist/editor/ai/ToolCallDisplay.d.ts +37 -0
  28. package/dist/editor/ai/ToolCallDisplay.js +154 -0
  29. package/dist/editor/ai/ToolCallDisplay.js.map +1 -0
  30. package/dist/editor/client/EditorClient.js +5 -1
  31. package/dist/editor/client/EditorClient.js.map +1 -1
  32. package/dist/editor/services/agentService.d.ts +23 -30
  33. package/dist/editor/services/agentService.js +62 -124
  34. package/dist/editor/services/agentService.js.map +1 -1
  35. package/dist/editor/sidebar/GraphQL.js +1 -0
  36. package/dist/editor/sidebar/GraphQL.js.map +1 -1
  37. package/dist/editor/sidebar/ViewSelector.js +8 -6
  38. package/dist/editor/sidebar/ViewSelector.js.map +1 -1
  39. package/dist/editor/ui/Section.js +4 -3
  40. package/dist/editor/ui/Section.js.map +1 -1
  41. package/dist/editor/utils.d.ts +4 -0
  42. package/dist/editor/utils.js +23 -0
  43. package/dist/editor/utils.js.map +1 -1
  44. package/dist/revision.d.ts +2 -2
  45. package/dist/revision.js +2 -2
  46. package/dist/styles.css +18 -33
  47. package/package.json +1 -1
  48. package/src/components/ui/textarea.tsx +1 -1
  49. package/src/editor/Terminal.tsx +4 -4
  50. package/src/editor/ai/AgentCostDisplay.tsx +7 -11
  51. package/src/editor/ai/AgentHistory.tsx +7 -9
  52. package/src/editor/ai/AgentTerminal.tsx +1094 -0
  53. package/src/editor/ai/Agents.tsx +340 -477
  54. package/src/editor/ai/AiPromptPopover.tsx +2 -2
  55. package/src/editor/ai/AiResponseMessage.tsx +85 -366
  56. package/src/editor/ai/AiTerminal.tsx +142 -1213
  57. package/src/editor/ai/DancingDots.tsx +14 -0
  58. package/src/editor/ai/ToolCallDisplay.tsx +363 -0
  59. package/src/editor/client/EditorClient.tsx +6 -1
  60. package/src/editor/services/agentService.ts +89 -162
  61. package/src/editor/sidebar/GraphQL.tsx +1 -0
  62. package/src/editor/sidebar/ViewSelector.tsx +82 -57
  63. package/src/editor/ui/Section.tsx +4 -3
  64. package/src/editor/utils.ts +29 -0
  65. package/src/revision.ts +2 -2
  66. package/dist/editor/ai/EditorAiTerminal.d.ts +0 -6
  67. package/dist/editor/ai/EditorAiTerminal.js +0 -7
  68. package/dist/editor/ai/EditorAiTerminal.js.map +0 -1
  69. package/src/editor/ai/EditorAiTerminal.tsx +0 -23
@@ -1,10 +1,11 @@
1
- import { AiContext, Message } from "../ai/AiTerminal";
1
+ import { Message } from "../ai/AiTerminal";
2
+ import { get, post } from "./serviceHelper";
2
3
 
3
4
  export const AGENT_BASE_URL = "/alpaca/editor/agent";
4
5
 
5
6
  export interface StartAgentRequest {
6
7
  agentId: string;
7
- messages: any[];
8
+ message: string;
8
9
  sessionId: string;
9
10
  profileId: string;
10
11
  model?: string;
@@ -34,22 +35,26 @@ export interface AgentStreamMessage {
34
35
  error?: string;
35
36
  }
36
37
 
37
- export enum AgentStreamMessageType {
38
- StatusUpdate = "StatusUpdate",
39
- ContentChunk = "ContentChunk",
40
- ToolCall = "ToolCall",
41
- ToolResult = "ToolResult",
42
- EditOperations = "EditOperations",
43
- Completed = "Completed",
44
- Error = "Error",
45
- }
38
+ export type AgentStreamMessageType =
39
+ | "statusUpdate"
40
+ | "contentChunk"
41
+ | "toolCall"
42
+ | "toolResult"
43
+ | "editOperations"
44
+ | "completed"
45
+ | "error";
46
+
47
+ export type AgentStatus = "new" | "running" | "closed" | "cancelled";
46
48
 
47
- export interface AgentChat {
49
+ export type Agent = {
48
50
  id: string;
49
51
  name: string;
50
- sessionId: string;
51
52
  userId: string;
52
- userDisplayName: string;
53
+ updatedDate: string;
54
+ status: AgentStatus;
55
+ };
56
+
57
+ export type AgentDetails = Agent & {
53
58
  itemId: string;
54
59
  itemPath: string;
55
60
  language: string;
@@ -57,9 +62,9 @@ export interface AgentChat {
57
62
  profileId?: string;
58
63
  profileName: string;
59
64
  model: string;
60
- status: string;
65
+
61
66
  createdDate: string;
62
- updatedDate: string;
67
+
63
68
  lastMessageDate?: string;
64
69
  totalTokensUsed: number;
65
70
  totalInputTokens: number;
@@ -73,7 +78,7 @@ export interface AgentChat {
73
78
  messageCount: number;
74
79
  metadata?: string;
75
80
  messages?: AgentChatMessage[];
76
- }
81
+ };
77
82
 
78
83
  export interface AgentChatMessage {
79
84
  id: string;
@@ -121,23 +126,19 @@ export interface AgentChatToolCall {
121
126
  */
122
127
  export async function startAgent(
123
128
  request: StartAgentRequest,
124
- context: AiContext,
125
129
  ): Promise<StartAgentResponse> {
126
- const response = await fetch(AGENT_BASE_URL + "/start", {
127
- method: "POST",
128
- headers: {
129
- "Content-Type": "application/json",
130
- },
131
- body: JSON.stringify(request),
132
- });
133
-
134
- if (!response.ok) {
130
+ const result = await post<StartAgentResponse>(
131
+ AGENT_BASE_URL + "/start",
132
+ request,
133
+ );
134
+
135
+ if (result.type !== "success") {
135
136
  throw new Error(
136
- `Failed to start agent: ${response.status} ${response.statusText}`,
137
+ `Failed to start agent: ${result.summary || "Unknown error"} ${result.details || ""}`,
137
138
  );
138
139
  }
139
140
 
140
- return await response.json();
141
+ return result.data!;
141
142
  }
142
143
 
143
144
  /**
@@ -145,7 +146,6 @@ export async function startAgent(
145
146
  */
146
147
  export async function connectToAgentStream(
147
148
  agentId: string,
148
- context: AiContext,
149
149
  onMessage: (message: AgentStreamMessage) => void,
150
150
  signal?: AbortSignal,
151
151
  ): Promise<void> {
@@ -255,25 +255,25 @@ async function processEventStream(
255
255
  const originalType = message.type;
256
256
  switch (message.type) {
257
257
  case 0:
258
- message.type = AgentStreamMessageType.StatusUpdate;
258
+ message.type = "statusUpdate";
259
259
  break;
260
260
  case 1:
261
- message.type = AgentStreamMessageType.ContentChunk;
261
+ message.type = "contentChunk";
262
262
  break;
263
263
  case 2:
264
- message.type = AgentStreamMessageType.ToolCall;
264
+ message.type = "toolCall";
265
265
  break;
266
266
  case 3:
267
- message.type = AgentStreamMessageType.ToolResult;
267
+ message.type = "toolResult";
268
268
  break;
269
269
  case 4:
270
- message.type = AgentStreamMessageType.EditOperations;
270
+ message.type = "editOperations";
271
271
  break;
272
272
  case 5:
273
- message.type = AgentStreamMessageType.Completed;
273
+ message.type = "completed";
274
274
  break;
275
275
  case 6:
276
- message.type = AgentStreamMessageType.Error;
276
+ message.type = "error";
277
277
  break;
278
278
  default:
279
279
  message.type = `Unknown_${message.type}`;
@@ -283,17 +283,14 @@ async function processEventStream(
283
283
  onMessage(message);
284
284
 
285
285
  // Break on completion or error
286
- if (
287
- message.type === AgentStreamMessageType.Completed ||
288
- message.type === AgentStreamMessageType.Error
289
- ) {
286
+ if (message.type === "completed" || message.type === "error") {
290
287
  return;
291
288
  }
292
289
  }
293
290
  } catch (error) {
294
291
  // Send error message to the client to notify user
295
292
  onMessage({
296
- type: AgentStreamMessageType.Error,
293
+ type: "error",
297
294
  error: `Failed to parse stream message: ${error instanceof Error ? error.message : "Unknown parsing error"}`,
298
295
  data: null,
299
296
  timestamp: new Date().toISOString(),
@@ -312,209 +309,140 @@ async function processEventStream(
312
309
  /**
313
310
  * Gets the current status of an agent execution
314
311
  */
315
- export async function getAgentStatus(
316
- agentId: string,
317
- context: AiContext,
318
- ): Promise<any> {
319
- const response = await fetch(
312
+ export async function getAgentStatus(agentId: string): Promise<any> {
313
+ const result = await get<any>(
320
314
  AGENT_BASE_URL + "/getAgentStatus?agentId=" + agentId,
321
- {
322
- method: "GET",
323
- headers: {
324
- "Content-Type": "application/json",
325
- },
326
- },
327
315
  );
328
316
 
329
- if (!response.ok) {
317
+ if (result.type !== "success") {
330
318
  throw new Error(
331
- `Failed to get agent status: ${response.status} ${response.statusText}`,
319
+ `Failed to get agent status: ${result.summary || "Unknown error"} ${result.details || ""}`,
332
320
  );
333
321
  }
334
322
 
335
- return await response.json();
323
+ return result.data;
336
324
  }
337
325
 
338
326
  /**
339
327
  * Gets all currently running agents for monitoring
340
328
  */
341
- export async function getRunningAgents(context: AiContext): Promise<any> {
342
- const response = await fetch(AGENT_BASE_URL + "/getRunningAgents", {
343
- method: "GET",
344
- headers: {
345
- "Content-Type": "application/json",
346
- },
347
- });
348
-
349
- if (!response.ok) {
329
+ export async function getRunningAgents(): Promise<any> {
330
+ const result = await get<any>(AGENT_BASE_URL + "/getRunningAgents");
331
+
332
+ if (result.type !== "success") {
350
333
  throw new Error(
351
- `Failed to get running agents: ${response.status} ${response.statusText}`,
334
+ `Failed to get running agents: ${result.summary || "Unknown error"} ${result.details || ""}`,
352
335
  );
353
336
  }
354
337
 
355
- return await response.json();
338
+ return result.data;
356
339
  }
357
340
 
358
341
  /**
359
342
  * Gets a single agent by ID with complete message history and cost information
360
343
  */
361
- export async function getAgent(
362
- agentId: string,
363
- context: AiContext,
364
- ): Promise<any> {
365
- const response = await fetch(
344
+ export async function getAgent(agentId: string): Promise<AgentDetails> {
345
+ const result = await get<AgentDetails>(
366
346
  AGENT_BASE_URL + "/getAgent?agentId=" + agentId,
367
- {
368
- method: "GET",
369
- headers: {
370
- "Content-Type": "application/json",
371
- },
372
- },
373
347
  );
374
348
 
375
- if (!response.ok) {
349
+ if (result.type !== "success") {
376
350
  throw new Error(
377
- `Failed to get agent: ${response.status} ${response.statusText}`,
351
+ `Failed to get agent: ${result.summary || "Unknown error"} ${result.details || ""}`,
378
352
  );
379
353
  }
380
354
 
381
- return await response.json();
355
+ return result.data!;
382
356
  }
383
357
 
384
358
  /**
385
359
  * Gets all agents for the current user
386
360
  */
387
- export async function getAgents(
388
- context: AiContext,
389
- status?: string,
390
- limit?: number,
391
- ): Promise<any> {
361
+ export async function getActiveAgents(limit?: number): Promise<Agent[]> {
392
362
  const params = new URLSearchParams();
393
- if (status) params.append("status", status);
394
363
  if (limit) params.append("limit", limit.toString());
395
364
 
396
365
  const queryString = params.toString();
397
366
  const url =
398
367
  AGENT_BASE_URL + "/getAgents" + (queryString ? `?${queryString}` : "");
399
368
 
400
- const response = await fetch(url, {
401
- method: "GET",
402
- headers: {
403
- "Content-Type": "application/json",
404
- },
405
- });
369
+ const result = await get<Agent[]>(url);
406
370
 
407
- if (!response.ok) {
371
+ if (result.type !== "success") {
408
372
  throw new Error(
409
- `Failed to get agents: ${response.status} ${response.statusText}`,
373
+ `Failed to get active agents: ${result.summary || "Unknown error"} ${result.details || ""}`,
410
374
  );
411
375
  }
412
376
 
413
- return await response.json();
377
+ return result.data || [];
414
378
  }
415
379
 
416
380
  /**
417
- * Gets chat history for the current user
381
+ * Gets all closed agents for the current user
418
382
  */
419
- export async function getChatHistory(
420
- context: AiContext,
421
- status?: string,
422
- limit?: number,
423
- ): Promise<any> {
383
+ export async function getClosedAgents(limit?: number): Promise<Agent[]> {
424
384
  const params = new URLSearchParams();
425
- if (status) params.append("status", status);
385
+ params.append("status", "closed");
426
386
  if (limit) params.append("limit", limit.toString());
427
387
 
428
388
  const queryString = params.toString();
429
389
  const url =
430
- AGENT_BASE_URL + "/chatHistory" + (queryString ? `?${queryString}` : "");
390
+ AGENT_BASE_URL + "/getAgents" + (queryString ? `?${queryString}` : "");
431
391
 
432
- const response = await fetch(url, {
433
- method: "GET",
434
- headers: {
435
- "Content-Type": "application/json",
436
- },
437
- });
392
+ const result = await get<Agent[]>(url);
438
393
 
439
- if (!response.ok) {
394
+ if (result.type !== "success") {
440
395
  throw new Error(
441
- `Failed to get chat history: ${response.status} ${response.statusText}`,
396
+ `Failed to get closed agents: ${result.summary || "Unknown error"} ${result.details || ""}`,
442
397
  );
443
398
  }
444
399
 
445
- return await response.json();
400
+ return result.data || [];
446
401
  }
447
402
 
448
403
  /**
449
404
  * Cancels a running agent execution
450
405
  */
451
- export async function cancelAgent(
452
- agentId: string,
453
- context: AiContext,
454
- ): Promise<any> {
455
- const response = await fetch(AGENT_BASE_URL + "/cancel", {
456
- method: "POST",
457
- headers: {
458
- "Content-Type": "application/json",
459
- },
460
- body: JSON.stringify({ agentId }),
461
- });
462
-
463
- if (!response.ok) {
406
+ export async function cancelAgent(agentId: string): Promise<any> {
407
+ const result = await post<any>(AGENT_BASE_URL + "/cancel", { agentId });
408
+
409
+ if (result.type !== "success") {
464
410
  throw new Error(
465
- `Failed to cancel agent: ${response.status} ${response.statusText}`,
411
+ `Failed to cancel agent: ${result.summary || "Unknown error"} ${result.details || ""}`,
466
412
  );
467
413
  }
468
414
 
469
- return await response.json();
415
+ return result.data;
470
416
  }
471
417
 
472
418
  /**
473
419
  * Permanently deletes an agent and all its messages
474
420
  */
475
- export async function deleteAgent(
476
- agentId: string,
477
- context: AiContext,
478
- ): Promise<any> {
479
- const response = await fetch(AGENT_BASE_URL + "/deleteAgent", {
480
- method: "POST",
481
- headers: {
482
- "Content-Type": "application/json",
483
- },
484
- body: JSON.stringify({ agentId }),
485
- });
486
-
487
- if (!response.ok) {
421
+ export async function deleteAgent(agentId: string): Promise<any> {
422
+ const result = await post<any>(AGENT_BASE_URL + "/deleteAgent", { agentId });
423
+
424
+ if (result.type !== "success") {
488
425
  throw new Error(
489
- `Failed to delete agent: ${response.status} ${response.statusText}`,
426
+ `Failed to delete agent: ${result.summary || "Unknown error"} ${result.details || ""}`,
490
427
  );
491
428
  }
492
429
 
493
- return await response.json();
430
+ return result.data;
494
431
  }
495
432
 
496
433
  /**
497
434
  * Closes an agent by canceling execution and setting status to closed
498
435
  */
499
- export async function closeAgent(
500
- agentId: string,
501
- context: AiContext,
502
- ): Promise<any> {
503
- const response = await fetch(AGENT_BASE_URL + "/closeAgent", {
504
- method: "POST",
505
- headers: {
506
- "Content-Type": "application/json",
507
- },
508
- body: JSON.stringify({ agentId }),
509
- });
510
-
511
- if (!response.ok) {
436
+ export async function closeAgent(agentId: string): Promise<any> {
437
+ const result = await post<any>(AGENT_BASE_URL + "/closeAgent", { agentId });
438
+
439
+ if (result.type !== "success") {
512
440
  throw new Error(
513
- `Failed to close agent: ${response.status} ${response.statusText}`,
441
+ `Failed to close agent: ${result.summary || "Unknown error"} ${result.details || ""}`,
514
442
  );
515
443
  }
516
444
 
517
- return await response.json();
445
+ return result.data;
518
446
  }
519
447
 
520
448
  /**
@@ -522,12 +450,11 @@ export async function closeAgent(
522
450
  */
523
451
  export async function checkAgentState(
524
452
  agentId: string,
525
- context: AiContext,
526
- ): Promise<{ exists: boolean; agent?: AgentChat; isRunning: boolean }> {
453
+ ): Promise<{ exists: boolean; agent?: Agent; isRunning: boolean }> {
527
454
  try {
528
- const agent = await getAgent(agentId, context);
455
+ const agent = await getAgent(agentId);
529
456
 
530
- const isRunning = agent.status === 0;
457
+ const isRunning = agent.status === "running";
531
458
 
532
459
  return {
533
460
  exists: true,
@@ -197,6 +197,7 @@ export function GraphQL() {
197
197
 
198
198
  return {
199
199
  ...baseContext,
200
+ endpoint: "/alpaca/editor/graphql/prompt",
200
201
  promptData: {
201
202
  ...baseContext.promptData,
202
203
  graphqlSchema: schemaResult?.data || null,
@@ -20,30 +20,34 @@ import { DrawingPinFilledIcon, DrawingPinIcon } from "@radix-ui/react-icons";
20
20
 
21
21
  function ViewSelectorComponent() {
22
22
  const editContext = useEditContext();
23
-
23
+
24
24
  // Extract and memoize specific values to prevent unnecessary re-renders
25
- const contextValues = useMemo(() => ({
26
- userViews: editContext?.userInfo.views,
27
- pinnedViews: editContext?.userInfo.preferences?.pinnedViews ||
28
- editContext?.configuration.editor.defaultPinnedViews ||
29
- [],
30
- showViewNames: editContext?.userInfo.preferences?.showViewNames ?? false,
31
- viewName: editContext?.viewName,
32
- isMobile: editContext?.isMobile,
33
- views: editContext?.configuration.editor.views,
34
- switchView: editContext?.switchView,
35
- setUserPreferences: editContext?.setUserPreferences
36
- }), [
37
- editContext?.userInfo.views,
38
- editContext?.userInfo.preferences?.pinnedViews,
39
- editContext?.configuration.editor.defaultPinnedViews,
40
- editContext?.userInfo.preferences?.showViewNames,
41
- editContext?.viewName,
42
- editContext?.isMobile,
43
- editContext?.configuration.editor.views,
44
- editContext?.switchView,
45
- editContext?.setUserPreferences
46
- ]);
25
+ const contextValues = useMemo(
26
+ () => ({
27
+ userViews: editContext?.userInfo.views,
28
+ pinnedViews:
29
+ editContext?.userInfo.preferences?.pinnedViews ||
30
+ editContext?.configuration.editor.defaultPinnedViews ||
31
+ [],
32
+ showViewNames: editContext?.userInfo.preferences?.showViewNames ?? false,
33
+ viewName: editContext?.viewName,
34
+ isMobile: editContext?.isMobile,
35
+ views: editContext?.configuration.editor.views,
36
+ switchView: editContext?.switchView,
37
+ setUserPreferences: editContext?.setUserPreferences,
38
+ }),
39
+ [
40
+ editContext?.userInfo.views,
41
+ editContext?.userInfo.preferences?.pinnedViews,
42
+ editContext?.configuration.editor.defaultPinnedViews,
43
+ editContext?.userInfo.preferences?.showViewNames,
44
+ editContext?.viewName,
45
+ editContext?.isMobile,
46
+ editContext?.configuration.editor.views,
47
+ editContext?.switchView,
48
+ editContext?.setUserPreferences,
49
+ ],
50
+ );
47
51
 
48
52
  const [isPopoverOpen, setIsPopoverOpen] = useState(false);
49
53
 
@@ -52,11 +56,13 @@ function ViewSelectorComponent() {
52
56
  contextValues.views
53
57
  ?.filter(
54
58
  (x) =>
55
- (x.visible && editContext && x.visible(editContext)) || (!x.visible && !x.hidden),
59
+ (x.visible && editContext && x.visible(editContext)) ||
60
+ (!x.visible && !x.hidden),
56
61
  )
57
62
  .filter(
58
63
  (x) =>
59
- !contextValues.userViews || contextValues.userViews.map((view) => view.name).includes(x.name),
64
+ !contextValues.userViews ||
65
+ contextValues.userViews.map((view) => view.name).includes(x.name),
60
66
  ) ?? []
61
67
  );
62
68
  }, [contextValues.views, contextValues.userViews, editContext]);
@@ -64,14 +70,17 @@ function ViewSelectorComponent() {
64
70
  // Use visibleViews from editContext instead of calculating locally
65
71
  const visibleViews = editContext?.visibleViews ?? [];
66
72
 
67
- const togglePin = useCallback((viewName: string) => {
68
- const newPinnedViews = contextValues.pinnedViews.includes(viewName)
69
- ? contextValues.pinnedViews.filter((name) => name !== viewName)
70
- : [...contextValues.pinnedViews, viewName];
73
+ const togglePin = useCallback(
74
+ (viewName: string) => {
75
+ const newPinnedViews = contextValues.pinnedViews.includes(viewName)
76
+ ? contextValues.pinnedViews.filter((name) => name !== viewName)
77
+ : [...contextValues.pinnedViews, viewName];
71
78
 
72
- // Save to user preferences via editContext
73
- contextValues.setUserPreferences?.({ pinnedViews: newPinnedViews });
74
- }, [contextValues.pinnedViews, contextValues.setUserPreferences]);
79
+ // Save to user preferences via editContext
80
+ contextValues.setUserPreferences?.({ pinnedViews: newPinnedViews });
81
+ },
82
+ [contextValues.pinnedViews, contextValues.setUserPreferences],
83
+ );
75
84
 
76
85
  const toggleShowNames = useCallback(() => {
77
86
  const newShowNames = !contextValues.showViewNames;
@@ -79,30 +88,42 @@ function ViewSelectorComponent() {
79
88
  contextValues.setUserPreferences?.({ showViewNames: newShowNames });
80
89
  }, [contextValues.showViewNames, contextValues.setUserPreferences]);
81
90
 
82
- const renderViewIcon = useCallback((view: any, additionalClassName?: string) => {
83
- if (view.icon && React.isValidElement(view.icon)) {
84
- return React.cloneElement(view.icon as React.ReactElement<any>, {
85
- className: cn("w-5 h-5", additionalClassName),
86
- });
87
- }
88
- return null;
89
- }, []);
90
-
91
- const handleViewClick = useCallback((viewName: string) => {
92
- if (viewName !== contextValues.viewName) {
93
- contextValues.switchView?.(viewName);
94
- }
95
- }, [contextValues.viewName, contextValues.switchView]);
91
+ const renderViewIcon = useCallback(
92
+ (view: any, additionalClassName?: string) => {
93
+ if (view.icon && React.isValidElement(view.icon)) {
94
+ return React.cloneElement(view.icon as React.ReactElement<any>, {
95
+ className: cn("w-5 h-5", additionalClassName),
96
+ });
97
+ }
98
+ return null;
99
+ },
100
+ [],
101
+ );
96
102
 
97
- const handleViewClickAndClosePopover = useCallback((viewName: string) => {
98
- contextValues.switchView?.(viewName);
99
- setIsPopoverOpen(false);
100
- }, [contextValues.switchView]);
103
+ const handleViewClick = useCallback(
104
+ (viewName: string) => {
105
+ if (viewName !== contextValues.viewName) {
106
+ contextValues.switchView?.(viewName);
107
+ }
108
+ },
109
+ [contextValues.viewName, contextValues.switchView],
110
+ );
101
111
 
102
- const handlePinClick = useCallback((e: React.MouseEvent, viewName: string) => {
103
- e.stopPropagation();
104
- togglePin(viewName);
105
- }, [togglePin]);
112
+ const handleViewClickAndClosePopover = useCallback(
113
+ (viewName: string) => {
114
+ contextValues.switchView?.(viewName);
115
+ setIsPopoverOpen(false);
116
+ },
117
+ [contextValues.switchView],
118
+ );
119
+
120
+ const handlePinClick = useCallback(
121
+ (e: React.MouseEvent, viewName: string) => {
122
+ e.stopPropagation();
123
+ togglePin(viewName);
124
+ },
125
+ [togglePin],
126
+ );
106
127
 
107
128
  return (
108
129
  <TooltipProvider>
@@ -110,7 +131,7 @@ function ViewSelectorComponent() {
110
131
  {visibleViews
111
132
  .filter((x) => x.icon)
112
133
  .map((view, i) => (
113
- <Tooltip key={`visible-${i}`}>
134
+ <Tooltip key={`visible-${i}`} delayDuration={500}>
114
135
  <TooltipTrigger asChild>
115
136
  <div
116
137
  className={classNames(
@@ -158,7 +179,7 @@ function ViewSelectorComponent() {
158
179
  <VerticalDotsIcon />
159
180
  </Button>
160
181
  </PopoverTrigger>
161
- </TooltipTrigger >
182
+ </TooltipTrigger>
162
183
  <TooltipContent side="right">More views</TooltipContent>
163
184
  </Tooltip>
164
185
  <PopoverContent
@@ -229,7 +250,11 @@ function ViewSelectorComponent() {
229
250
  )}
230
251
  onClick={toggleShowNames}
231
252
  >
232
- {contextValues.showViewNames ? <ViewToggleIcon /> : <ViewToggleIcon />}
253
+ {contextValues.showViewNames ? (
254
+ <ViewToggleIcon />
255
+ ) : (
256
+ <ViewToggleIcon />
257
+ )}
233
258
  </Button>
234
259
  </TooltipTrigger>
235
260
  <TooltipContent>
@@ -1,5 +1,5 @@
1
1
  import { classNames } from "primereact/utils";
2
- import { useLocalStorage } from "../utils";
2
+ import { useCollapsedSections } from "../utils";
3
3
  import { ChevronDown } from "lucide-react";
4
4
 
5
5
  export function Section({
@@ -9,7 +9,8 @@ export function Section({
9
9
  title: string;
10
10
  children: React.ReactNode;
11
11
  }) {
12
- const [open, setOpen] = useLocalStorage("editor.showSection-" + title, true);
12
+ const { isSectionCollapsed, toggleSection } = useCollapsedSections();
13
+ const open = !isSectionCollapsed(title);
13
14
 
14
15
  return (
15
16
  <div>
@@ -20,7 +21,7 @@ export function Section({
20
21
  : "border-gray-400 bg-gray-400 hover:border-gray-300 hover:bg-gray-500",
21
22
  "flex cursor-pointer items-center justify-between border-l-[7px] px-3 py-2 text-xs text-white transition-all duration-200 ease-in-out",
22
23
  )}
23
- onClick={() => setOpen(!open)}
24
+ onClick={() => toggleSection(title)}
24
25
  >
25
26
  {title}
26
27
  <ChevronDown