@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.
- package/dist/components/ui/textarea.js +1 -1
- package/dist/components/ui/textarea.js.map +1 -1
- package/dist/editor/Terminal.js +3 -3
- package/dist/editor/Terminal.js.map +1 -1
- package/dist/editor/ai/AgentCostDisplay.js +2 -2
- package/dist/editor/ai/AgentCostDisplay.js.map +1 -1
- package/dist/editor/ai/AgentHistory.d.ts +4 -4
- package/dist/editor/ai/AgentHistory.js +1 -1
- package/dist/editor/ai/AgentHistory.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.d.ts +4 -0
- package/dist/editor/ai/AgentTerminal.js +753 -0
- package/dist/editor/ai/AgentTerminal.js.map +1 -0
- package/dist/editor/ai/Agents.d.ts +1 -3
- package/dist/editor/ai/Agents.js +213 -353
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiPromptPopover.js +2 -2
- package/dist/editor/ai/AiPromptPopover.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.d.ts +0 -1
- package/dist/editor/ai/AiResponseMessage.js +23 -143
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/AiTerminal.d.ts +5 -23
- package/dist/editor/ai/AiTerminal.js +81 -824
- package/dist/editor/ai/AiTerminal.js.map +1 -1
- package/dist/editor/ai/DancingDots.d.ts +1 -0
- package/dist/editor/ai/DancingDots.js +6 -0
- package/dist/editor/ai/DancingDots.js.map +1 -0
- package/dist/editor/ai/ToolCallDisplay.d.ts +37 -0
- package/dist/editor/ai/ToolCallDisplay.js +154 -0
- package/dist/editor/ai/ToolCallDisplay.js.map +1 -0
- package/dist/editor/client/EditorClient.js +5 -1
- package/dist/editor/client/EditorClient.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +23 -30
- package/dist/editor/services/agentService.js +62 -124
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/sidebar/GraphQL.js +1 -0
- package/dist/editor/sidebar/GraphQL.js.map +1 -1
- package/dist/editor/sidebar/ViewSelector.js +8 -6
- package/dist/editor/sidebar/ViewSelector.js.map +1 -1
- package/dist/editor/ui/Section.js +4 -3
- package/dist/editor/ui/Section.js.map +1 -1
- package/dist/editor/utils.d.ts +4 -0
- package/dist/editor/utils.js +23 -0
- package/dist/editor/utils.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/styles.css +18 -33
- package/package.json +1 -1
- package/src/components/ui/textarea.tsx +1 -1
- package/src/editor/Terminal.tsx +4 -4
- package/src/editor/ai/AgentCostDisplay.tsx +7 -11
- package/src/editor/ai/AgentHistory.tsx +7 -9
- package/src/editor/ai/AgentTerminal.tsx +1094 -0
- package/src/editor/ai/Agents.tsx +340 -477
- package/src/editor/ai/AiPromptPopover.tsx +2 -2
- package/src/editor/ai/AiResponseMessage.tsx +85 -366
- package/src/editor/ai/AiTerminal.tsx +142 -1213
- package/src/editor/ai/DancingDots.tsx +14 -0
- package/src/editor/ai/ToolCallDisplay.tsx +363 -0
- package/src/editor/client/EditorClient.tsx +6 -1
- package/src/editor/services/agentService.ts +89 -162
- package/src/editor/sidebar/GraphQL.tsx +1 -0
- package/src/editor/sidebar/ViewSelector.tsx +82 -57
- package/src/editor/ui/Section.tsx +4 -3
- package/src/editor/utils.ts +29 -0
- package/src/revision.ts +2 -2
- package/dist/editor/ai/EditorAiTerminal.d.ts +0 -6
- package/dist/editor/ai/EditorAiTerminal.js +0 -7
- package/dist/editor/ai/EditorAiTerminal.js.map +0 -1
- package/src/editor/ai/EditorAiTerminal.tsx +0 -23
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
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
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
49
|
+
export type Agent = {
|
|
48
50
|
id: string;
|
|
49
51
|
name: string;
|
|
50
|
-
sessionId: string;
|
|
51
52
|
userId: string;
|
|
52
|
-
|
|
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
|
-
|
|
65
|
+
|
|
61
66
|
createdDate: string;
|
|
62
|
-
|
|
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
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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: ${
|
|
137
|
+
`Failed to start agent: ${result.summary || "Unknown error"} ${result.details || ""}`,
|
|
137
138
|
);
|
|
138
139
|
}
|
|
139
140
|
|
|
140
|
-
return
|
|
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 =
|
|
258
|
+
message.type = "statusUpdate";
|
|
259
259
|
break;
|
|
260
260
|
case 1:
|
|
261
|
-
message.type =
|
|
261
|
+
message.type = "contentChunk";
|
|
262
262
|
break;
|
|
263
263
|
case 2:
|
|
264
|
-
message.type =
|
|
264
|
+
message.type = "toolCall";
|
|
265
265
|
break;
|
|
266
266
|
case 3:
|
|
267
|
-
message.type =
|
|
267
|
+
message.type = "toolResult";
|
|
268
268
|
break;
|
|
269
269
|
case 4:
|
|
270
|
-
message.type =
|
|
270
|
+
message.type = "editOperations";
|
|
271
271
|
break;
|
|
272
272
|
case 5:
|
|
273
|
-
message.type =
|
|
273
|
+
message.type = "completed";
|
|
274
274
|
break;
|
|
275
275
|
case 6:
|
|
276
|
-
message.type =
|
|
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:
|
|
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
|
-
|
|
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 (
|
|
317
|
+
if (result.type !== "success") {
|
|
330
318
|
throw new Error(
|
|
331
|
-
`Failed to get agent status: ${
|
|
319
|
+
`Failed to get agent status: ${result.summary || "Unknown error"} ${result.details || ""}`,
|
|
332
320
|
);
|
|
333
321
|
}
|
|
334
322
|
|
|
335
|
-
return
|
|
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(
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
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: ${
|
|
334
|
+
`Failed to get running agents: ${result.summary || "Unknown error"} ${result.details || ""}`,
|
|
352
335
|
);
|
|
353
336
|
}
|
|
354
337
|
|
|
355
|
-
return
|
|
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
|
-
|
|
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 (
|
|
349
|
+
if (result.type !== "success") {
|
|
376
350
|
throw new Error(
|
|
377
|
-
`Failed to get agent: ${
|
|
351
|
+
`Failed to get agent: ${result.summary || "Unknown error"} ${result.details || ""}`,
|
|
378
352
|
);
|
|
379
353
|
}
|
|
380
354
|
|
|
381
|
-
return
|
|
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
|
|
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
|
|
401
|
-
method: "GET",
|
|
402
|
-
headers: {
|
|
403
|
-
"Content-Type": "application/json",
|
|
404
|
-
},
|
|
405
|
-
});
|
|
369
|
+
const result = await get<Agent[]>(url);
|
|
406
370
|
|
|
407
|
-
if (
|
|
371
|
+
if (result.type !== "success") {
|
|
408
372
|
throw new Error(
|
|
409
|
-
`Failed to get agents: ${
|
|
373
|
+
`Failed to get active agents: ${result.summary || "Unknown error"} ${result.details || ""}`,
|
|
410
374
|
);
|
|
411
375
|
}
|
|
412
376
|
|
|
413
|
-
return
|
|
377
|
+
return result.data || [];
|
|
414
378
|
}
|
|
415
379
|
|
|
416
380
|
/**
|
|
417
|
-
* Gets
|
|
381
|
+
* Gets all closed agents for the current user
|
|
418
382
|
*/
|
|
419
|
-
export async function
|
|
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
|
-
|
|
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 + "/
|
|
390
|
+
AGENT_BASE_URL + "/getAgents" + (queryString ? `?${queryString}` : "");
|
|
431
391
|
|
|
432
|
-
const
|
|
433
|
-
method: "GET",
|
|
434
|
-
headers: {
|
|
435
|
-
"Content-Type": "application/json",
|
|
436
|
-
},
|
|
437
|
-
});
|
|
392
|
+
const result = await get<Agent[]>(url);
|
|
438
393
|
|
|
439
|
-
if (
|
|
394
|
+
if (result.type !== "success") {
|
|
440
395
|
throw new Error(
|
|
441
|
-
`Failed to get
|
|
396
|
+
`Failed to get closed agents: ${result.summary || "Unknown error"} ${result.details || ""}`,
|
|
442
397
|
);
|
|
443
398
|
}
|
|
444
399
|
|
|
445
|
-
return
|
|
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
|
|
453
|
-
|
|
454
|
-
)
|
|
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: ${
|
|
411
|
+
`Failed to cancel agent: ${result.summary || "Unknown error"} ${result.details || ""}`,
|
|
466
412
|
);
|
|
467
413
|
}
|
|
468
414
|
|
|
469
|
-
return
|
|
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
|
|
477
|
-
|
|
478
|
-
)
|
|
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: ${
|
|
426
|
+
`Failed to delete agent: ${result.summary || "Unknown error"} ${result.details || ""}`,
|
|
490
427
|
);
|
|
491
428
|
}
|
|
492
429
|
|
|
493
|
-
return
|
|
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
|
|
501
|
-
|
|
502
|
-
)
|
|
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: ${
|
|
441
|
+
`Failed to close agent: ${result.summary || "Unknown error"} ${result.details || ""}`,
|
|
514
442
|
);
|
|
515
443
|
}
|
|
516
444
|
|
|
517
|
-
return
|
|
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
|
-
|
|
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
|
|
455
|
+
const agent = await getAgent(agentId);
|
|
529
456
|
|
|
530
|
-
const isRunning = agent.status ===
|
|
457
|
+
const isRunning = agent.status === "running";
|
|
531
458
|
|
|
532
459
|
return {
|
|
533
460
|
exists: true,
|
|
@@ -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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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)) ||
|
|
59
|
+
(x.visible && editContext && x.visible(editContext)) ||
|
|
60
|
+
(!x.visible && !x.hidden),
|
|
56
61
|
)
|
|
57
62
|
.filter(
|
|
58
63
|
(x) =>
|
|
59
|
-
!contextValues.userViews ||
|
|
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(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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 ?
|
|
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 {
|
|
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
|
|
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={() =>
|
|
24
|
+
onClick={() => toggleSection(title)}
|
|
24
25
|
>
|
|
25
26
|
{title}
|
|
26
27
|
<ChevronDown
|