@alpaca-editor/core 1.0.4013 → 1.0.4014
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/editor/Terminal.d.ts +6 -0
- package/dist/editor/Terminal.js +10 -4
- package/dist/editor/Terminal.js.map +1 -1
- package/dist/editor/ai/Agents.js +133 -13
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiTerminal.d.ts +2 -1
- package/dist/editor/ai/AiTerminal.js +86 -139
- package/dist/editor/ai/AiTerminal.js.map +1 -1
- package/dist/editor/media-selector/AiImageSearch.js +2 -1
- package/dist/editor/media-selector/AiImageSearch.js.map +1 -1
- package/dist/editor/media-selector/AiImageSearchPrompt.js +2 -1
- package/dist/editor/media-selector/AiImageSearchPrompt.js.map +1 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js +4 -2
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +59 -0
- package/dist/editor/services/agentService.js +26 -0
- package/dist/editor/services/agentService.js.map +1 -0
- package/dist/editor/services/aiService.d.ts +22 -4
- package/dist/editor/services/aiService.js +131 -20
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/services/contextService.js +6 -4
- package/dist/editor/services/contextService.js.map +1 -1
- package/dist/editor/sidebar/SidebarView.js +2 -5
- package/dist/editor/sidebar/SidebarView.js.map +1 -1
- package/dist/editor/sidebar/ViewSelector.js +1 -1
- package/dist/editor/sidebar/ViewSelector.js.map +1 -1
- package/dist/editor/ui/SimpleIconButton.d.ts +2 -2
- package/dist/editor/ui/SimpleIconButton.js +5 -3
- package/dist/editor/ui/SimpleIconButton.js.map +1 -1
- package/dist/editor/utils/jsonCleaner.d.ts +8 -0
- package/dist/editor/utils/jsonCleaner.js +76 -0
- package/dist/editor/utils/jsonCleaner.js.map +1 -0
- package/dist/editor/views/CompareView.js +2 -2
- package/dist/editor/views/CompareView.js.map +1 -1
- package/dist/page-wizard/steps/ContentStep.js +7 -2
- package/dist/page-wizard/steps/ContentStep.js.map +1 -1
- package/dist/page-wizard/steps/FindItemsStep.js +4 -1
- package/dist/page-wizard/steps/FindItemsStep.js.map +1 -1
- package/dist/page-wizard/steps/ImagesStep.js +13 -7
- package/dist/page-wizard/steps/ImagesStep.js.map +1 -1
- package/dist/page-wizard/steps/LayoutStep.d.ts +1 -1
- package/dist/page-wizard/steps/LayoutStep.js +22 -20
- package/dist/page-wizard/steps/LayoutStep.js.map +1 -1
- package/dist/page-wizard/steps/MetaDataStep.js +8 -15
- package/dist/page-wizard/steps/MetaDataStep.js.map +1 -1
- package/dist/page-wizard/steps/SelectStep.js +9 -4
- package/dist/page-wizard/steps/SelectStep.js.map +1 -1
- package/dist/page-wizard/steps/StructureStep.js +3 -1
- package/dist/page-wizard/steps/StructureStep.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/styles.css +10 -11
- package/package.json +1 -1
- package/src/editor/Terminal.tsx +12 -3
- package/src/editor/ai/Agents.tsx +212 -16
- package/src/editor/ai/AiTerminal.tsx +113 -173
- package/src/editor/ai/GhostWriter.tsx_ +3 -3
- package/src/editor/media-selector/AiImageSearch.tsx +2 -2
- package/src/editor/media-selector/AiImageSearchPrompt.tsx +2 -2
- package/src/editor/page-editor-chrome/useInlineAICompletion.tsx +5 -5
- package/src/editor/services/agentService.ts +83 -0
- package/src/editor/services/aiService.ts +176 -33
- package/src/editor/services/contextService.ts +5 -6
- package/src/editor/sidebar/SidebarView.tsx +10 -6
- package/src/editor/sidebar/ViewSelector.tsx +2 -2
- package/src/editor/ui/SimpleIconButton.tsx +20 -14
- package/src/editor/utils/jsonCleaner.ts +92 -0
- package/src/editor/views/CompareView.tsx +2 -2
- package/src/page-wizard/steps/ContentStep.tsx +10 -9
- package/src/page-wizard/steps/FindItemsStep.tsx +7 -5
- package/src/page-wizard/steps/ImagesStep.tsx +16 -11
- package/src/page-wizard/steps/LayoutStep.tsx +24 -28
- package/src/page-wizard/steps/MetaDataStep.tsx +11 -21
- package/src/page-wizard/steps/SelectStep.tsx +11 -8
- package/src/page-wizard/steps/StructureStep.tsx +4 -5
- package/src/revision.ts +2 -2
|
@@ -44,10 +44,10 @@ export function AiImageSearchPrompt({
|
|
|
44
44
|
"Reply with search terms only!",
|
|
45
45
|
name: "user",
|
|
46
46
|
role: "user",
|
|
47
|
+
id: crypto.randomUUID(), // Use proper UUID instead of Date.now()
|
|
47
48
|
},
|
|
48
49
|
],
|
|
49
|
-
editContext,
|
|
50
|
-
createEditorAiContext,
|
|
50
|
+
{ editContext, createAiContext: createEditorAiContext },
|
|
51
51
|
{ allowedFunctions: ["get-content"], addContextContent: true },
|
|
52
52
|
{ signal: abortController.signal },
|
|
53
53
|
);
|
|
@@ -350,11 +350,13 @@ ONLY provide the completion text (what comes after the user's text), never repea
|
|
|
350
350
|
name: "system",
|
|
351
351
|
role: "system",
|
|
352
352
|
content: systemPrompt,
|
|
353
|
+
id: crypto.randomUUID(), // Use proper UUID instead of Date.now()
|
|
353
354
|
},
|
|
354
355
|
{
|
|
355
356
|
name: "user",
|
|
356
357
|
role: "user",
|
|
357
358
|
content: `Complete this text. ONLY provide the completion (do not repeat my text): ${contentUpToCursor}`,
|
|
359
|
+
id: crypto.randomUUID(), // Use proper UUID instead of Date.now()
|
|
358
360
|
},
|
|
359
361
|
];
|
|
360
362
|
|
|
@@ -365,8 +367,7 @@ ONLY provide the completion text (what comes after the user's text), never repea
|
|
|
365
367
|
try {
|
|
366
368
|
const result = await executePrompt(
|
|
367
369
|
messages,
|
|
368
|
-
|
|
369
|
-
() => ({
|
|
370
|
+
{
|
|
370
371
|
endpoint: "/alpaca/editor/ai/complete",
|
|
371
372
|
promptData: {
|
|
372
373
|
itemid: itemId,
|
|
@@ -376,10 +377,9 @@ ONLY provide the completion text (what comes after the user's text), never repea
|
|
|
376
377
|
fieldname: fieldName,
|
|
377
378
|
content: contentUpToCursor,
|
|
378
379
|
},
|
|
379
|
-
}
|
|
380
|
-
{},
|
|
380
|
+
},
|
|
381
|
+
{ model: "gpt-4.1-nano" },
|
|
381
382
|
{ signal },
|
|
382
|
-
"gpt-4.1-nano",
|
|
383
383
|
);
|
|
384
384
|
|
|
385
385
|
return result;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { get } from "./serviceHelper";
|
|
2
|
+
|
|
3
|
+
export interface AgentChat {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
sessionId: string;
|
|
7
|
+
userId: string;
|
|
8
|
+
userDisplayName: string;
|
|
9
|
+
itemId: string;
|
|
10
|
+
itemPath: string;
|
|
11
|
+
language: string;
|
|
12
|
+
version: number;
|
|
13
|
+
profileId?: string;
|
|
14
|
+
profileName: string;
|
|
15
|
+
model: string;
|
|
16
|
+
status: "active" | "completed" | "error" | "abandoned";
|
|
17
|
+
createdDate: string;
|
|
18
|
+
updatedDate: string;
|
|
19
|
+
lastMessageDate?: string;
|
|
20
|
+
totalTokensUsed: number;
|
|
21
|
+
totalInputTokens: number;
|
|
22
|
+
totalOutputTokens: number;
|
|
23
|
+
messageCount: number;
|
|
24
|
+
metadata?: string;
|
|
25
|
+
messages?: AgentChatMessage[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface AgentChatMessage {
|
|
29
|
+
id: string;
|
|
30
|
+
agentId: string;
|
|
31
|
+
messageIndex: number;
|
|
32
|
+
role: string;
|
|
33
|
+
content: string;
|
|
34
|
+
name: string;
|
|
35
|
+
toolCallId?: string;
|
|
36
|
+
functionName?: string;
|
|
37
|
+
functionArguments?: string;
|
|
38
|
+
functionResult?: any;
|
|
39
|
+
functionError?: string;
|
|
40
|
+
isCompleted: boolean;
|
|
41
|
+
model?: string;
|
|
42
|
+
tokensUsed: number;
|
|
43
|
+
inputTokens: number;
|
|
44
|
+
outputTokens: number;
|
|
45
|
+
responseTimeMs?: number;
|
|
46
|
+
createdDate: string;
|
|
47
|
+
editOperations?: any[];
|
|
48
|
+
longRunningTaskIds?: string[];
|
|
49
|
+
metadata?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Gets all agents for the current user
|
|
54
|
+
*/
|
|
55
|
+
export async function getAgents(
|
|
56
|
+
status?: "active" | "completed" | "error" | "abandoned",
|
|
57
|
+
limit = 100
|
|
58
|
+
) {
|
|
59
|
+
const url = `/alpaca/editor/ai/GetAgents${status ? `?status=${status}` : ""}${limit ? `${status ? "&" : "?"}limit=${limit}` : ""}`;
|
|
60
|
+
const response = await get<AgentChat[]>(url);
|
|
61
|
+
return response;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Gets a specific agent with its messages
|
|
66
|
+
*/
|
|
67
|
+
export async function getAgent(agentId: string) {
|
|
68
|
+
const url = `/alpaca/editor/ai/GetAgent?agentId=${agentId}`;
|
|
69
|
+
const response = await get<AgentChat>(url);
|
|
70
|
+
return response;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Gets chat history for the current user
|
|
75
|
+
*/
|
|
76
|
+
export async function getChatHistory(
|
|
77
|
+
status?: "active" | "completed" | "error" | "abandoned",
|
|
78
|
+
limit = 50
|
|
79
|
+
) {
|
|
80
|
+
const url = `/alpaca/editor/ai/GetChatHistory${status ? `?status=${status}` : ""}${limit ? `${status ? "&" : "?"}limit=${limit}` : ""}`;
|
|
81
|
+
const response = await get<AgentChat[]>(url);
|
|
82
|
+
return response;
|
|
83
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AiContext } from "../ai/AiTerminal";
|
|
2
2
|
import { EditContextType } from "../client/editContext";
|
|
3
3
|
import { FieldDescriptor, ItemDescriptor } from "../pageModel";
|
|
4
|
+
import { JsonCleaner } from "../utils/jsonCleaner";
|
|
4
5
|
|
|
5
6
|
import { ExecutionResult, get, post } from "./serviceHelper";
|
|
6
7
|
|
|
@@ -32,6 +33,9 @@ type Message = {
|
|
|
32
33
|
content: string;
|
|
33
34
|
name: string;
|
|
34
35
|
role: string;
|
|
36
|
+
id: string; // Changed from number to string to support UUIDs
|
|
37
|
+
tool_calls?: any[]; // Optional tool_calls for compatibility
|
|
38
|
+
tool_call_id?: string; // Optional tool_call_id for compatibility
|
|
35
39
|
};
|
|
36
40
|
|
|
37
41
|
export interface ExecutePromptResponse {
|
|
@@ -41,49 +45,117 @@ export interface ExecutePromptResponse {
|
|
|
41
45
|
numOutputTokens: number;
|
|
42
46
|
numCachedTokens: number;
|
|
43
47
|
state: string;
|
|
48
|
+
// New properties for incremental updates
|
|
49
|
+
isIncremental?: boolean;
|
|
50
|
+
deltaContent?: string;
|
|
51
|
+
previousContentLength?: number;
|
|
52
|
+
totalContentLength?: number;
|
|
44
53
|
}
|
|
45
54
|
|
|
55
|
+
// Unified options interface that supports both calling patterns
|
|
56
|
+
export interface ExecutePromptOptions {
|
|
57
|
+
// Common options
|
|
58
|
+
sessionId?: string | null;
|
|
59
|
+
model?: string | null;
|
|
60
|
+
|
|
61
|
+
// AiTerminal style options
|
|
62
|
+
profileId?: string;
|
|
63
|
+
selection?: string[];
|
|
64
|
+
selectedText?: string | null;
|
|
65
|
+
agentId?: string;
|
|
66
|
+
addSelectedComponents?: boolean;
|
|
67
|
+
|
|
68
|
+
// Other callers style options
|
|
69
|
+
allowedFunctions?: string[];
|
|
70
|
+
addContextContent?: boolean;
|
|
71
|
+
addAllContent?: boolean;
|
|
72
|
+
profile?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Unified function signature
|
|
46
76
|
export async function executePrompt(
|
|
47
77
|
messages: Message[],
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
options?: RequestInit,
|
|
58
|
-
model?: string,
|
|
78
|
+
context:
|
|
79
|
+
| AiContext
|
|
80
|
+
| {
|
|
81
|
+
editContext: EditContextType;
|
|
82
|
+
createAiContext: ({ editContext }: { editContext: any }) => AiContext;
|
|
83
|
+
},
|
|
84
|
+
options: ExecutePromptOptions = {},
|
|
85
|
+
requestOptions?: RequestInit,
|
|
59
86
|
callback?: (response: any) => void,
|
|
60
87
|
): Promise<ExecutePromptResponse | null> {
|
|
61
|
-
|
|
88
|
+
let endpoint: string;
|
|
89
|
+
let requestBody: any;
|
|
90
|
+
let finalRequestOptions: RequestInit;
|
|
62
91
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
92
|
+
// Handle context - either direct AiContext or need to create it
|
|
93
|
+
let aiContext: AiContext;
|
|
94
|
+
if ("endpoint" in context) {
|
|
95
|
+
// Direct AiContext (AiTerminal style)
|
|
96
|
+
aiContext = context;
|
|
97
|
+
} else {
|
|
98
|
+
// Create context (other callers style)
|
|
99
|
+
aiContext = context.createAiContext({ editContext: context.editContext });
|
|
100
|
+
// Default sessionId from editContext if not provided
|
|
101
|
+
if (options.sessionId === undefined) {
|
|
102
|
+
options.sessionId = context.editContext.sessionId;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
endpoint = aiContext.endpoint;
|
|
72
107
|
|
|
108
|
+
// Build unified request body
|
|
109
|
+
requestBody = {
|
|
110
|
+
messages,
|
|
111
|
+
...aiContext.promptData,
|
|
112
|
+
sessionId: options.sessionId,
|
|
113
|
+
model: options.model,
|
|
114
|
+
|
|
115
|
+
// Include all provided options
|
|
116
|
+
...options,
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
finalRequestOptions = {
|
|
120
|
+
method: "POST",
|
|
121
|
+
body: JSON.stringify(requestBody),
|
|
73
122
|
credentials: "include",
|
|
74
123
|
headers: {
|
|
75
124
|
"Content-Type": "application/json",
|
|
76
125
|
},
|
|
77
|
-
...
|
|
78
|
-
}
|
|
126
|
+
...requestOptions,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const response = await fetch(endpoint, finalRequestOptions);
|
|
130
|
+
|
|
131
|
+
if (!response.ok) {
|
|
132
|
+
// Always return rich error format
|
|
133
|
+
const text = await response.text();
|
|
134
|
+
return {
|
|
135
|
+
messages: [
|
|
136
|
+
{
|
|
137
|
+
content: "There was an error processing your request: " + text,
|
|
138
|
+
name: "assistant",
|
|
139
|
+
role: "assistant",
|
|
140
|
+
id: crypto.randomUUID(),
|
|
141
|
+
tool_calls: [],
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
editOperations: [],
|
|
145
|
+
numInputTokens: 0,
|
|
146
|
+
numOutputTokens: 0,
|
|
147
|
+
numCachedTokens: 0,
|
|
148
|
+
state: "error",
|
|
149
|
+
};
|
|
150
|
+
}
|
|
79
151
|
|
|
80
152
|
if (!response?.body) return null;
|
|
81
153
|
|
|
82
154
|
const reader = response.body.getReader();
|
|
83
155
|
const decoder = new TextDecoder();
|
|
84
156
|
let buffer = "";
|
|
85
|
-
|
|
86
157
|
let result = null;
|
|
158
|
+
let accumulatedContent = "";
|
|
87
159
|
|
|
88
160
|
while (true) {
|
|
89
161
|
const { done, value } = await reader.read();
|
|
@@ -92,21 +164,58 @@ export async function executePrompt(
|
|
|
92
164
|
break;
|
|
93
165
|
}
|
|
94
166
|
|
|
95
|
-
buffer += decoder.decode(value, { stream: true });
|
|
96
|
-
|
|
97
|
-
// Split the buffer by newline and keep the last partial line for the next iteration.
|
|
167
|
+
buffer += decoder.decode(value, { stream: true });
|
|
98
168
|
const lines = buffer.split("\n");
|
|
99
169
|
|
|
100
170
|
if (lines.length > 0) {
|
|
101
|
-
buffer = lines.pop() || "";
|
|
171
|
+
buffer = lines.pop() || "";
|
|
102
172
|
|
|
103
173
|
for (let line of lines) {
|
|
104
|
-
if (line.trim() === "") continue;
|
|
174
|
+
if (line.trim() === "") continue;
|
|
105
175
|
|
|
106
176
|
try {
|
|
107
177
|
const jsonData = JSON.parse(line);
|
|
108
|
-
|
|
109
|
-
|
|
178
|
+
|
|
179
|
+
if (jsonData.isIncremental) {
|
|
180
|
+
if (jsonData.deltaContent) {
|
|
181
|
+
accumulatedContent += jsonData.deltaContent;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Always apply JsonCleaner for streaming responses
|
|
185
|
+
const cleanupResult = JsonCleaner.cleanupJson(accumulatedContent);
|
|
186
|
+
|
|
187
|
+
const incrementalResponse = {
|
|
188
|
+
...jsonData,
|
|
189
|
+
messages: [
|
|
190
|
+
{
|
|
191
|
+
content: cleanupResult.cleanedJson,
|
|
192
|
+
role: "assistant",
|
|
193
|
+
name: "assistant",
|
|
194
|
+
id: Date.now(),
|
|
195
|
+
tool_calls: [],
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
callback?.(incrementalResponse);
|
|
201
|
+
result = incrementalResponse;
|
|
202
|
+
} else {
|
|
203
|
+
// Always ensure messages have IDs and tool_calls
|
|
204
|
+
let processedResponse = jsonData;
|
|
205
|
+
if (jsonData.messages) {
|
|
206
|
+
processedResponse = {
|
|
207
|
+
...jsonData,
|
|
208
|
+
messages: jsonData.messages.map((msg: any, index: number) => ({
|
|
209
|
+
...msg,
|
|
210
|
+
id: msg.id || Date.now() + index,
|
|
211
|
+
tool_calls: msg.tool_calls || [],
|
|
212
|
+
})),
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
callback?.(processedResponse);
|
|
217
|
+
result = processedResponse;
|
|
218
|
+
}
|
|
110
219
|
} catch (e) {
|
|
111
220
|
console.error("Error parsing line:" + line, e);
|
|
112
221
|
}
|
|
@@ -114,11 +223,45 @@ export async function executePrompt(
|
|
|
114
223
|
}
|
|
115
224
|
}
|
|
116
225
|
|
|
117
|
-
//
|
|
226
|
+
// Handle final buffer
|
|
118
227
|
if (buffer.trim() !== "") {
|
|
119
228
|
try {
|
|
120
229
|
const jsonData = JSON.parse(buffer);
|
|
121
|
-
|
|
230
|
+
|
|
231
|
+
if (jsonData.isIncremental) {
|
|
232
|
+
if (jsonData.deltaContent) {
|
|
233
|
+
accumulatedContent += jsonData.deltaContent;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const cleanupResult = JsonCleaner.cleanupJson(accumulatedContent);
|
|
237
|
+
|
|
238
|
+
result = {
|
|
239
|
+
...jsonData,
|
|
240
|
+
messages: [
|
|
241
|
+
{
|
|
242
|
+
content: cleanupResult.cleanedJson,
|
|
243
|
+
role: "assistant",
|
|
244
|
+
name: "assistant",
|
|
245
|
+
id: Date.now(),
|
|
246
|
+
tool_calls: [],
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
};
|
|
250
|
+
} else {
|
|
251
|
+
// Always ensure messages have IDs
|
|
252
|
+
if (jsonData.messages) {
|
|
253
|
+
result = {
|
|
254
|
+
...jsonData,
|
|
255
|
+
messages: jsonData.messages.map((msg: any, index: number) => ({
|
|
256
|
+
...msg,
|
|
257
|
+
id: msg.id || Date.now() + index,
|
|
258
|
+
tool_calls: msg.tool_calls || [],
|
|
259
|
+
})),
|
|
260
|
+
};
|
|
261
|
+
} else {
|
|
262
|
+
result = jsonData;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
122
265
|
} catch (e) {
|
|
123
266
|
console.error("Error parsing the final buffer content:", e);
|
|
124
267
|
}
|
|
@@ -51,28 +51,27 @@ export async function generatePageContext(
|
|
|
51
51
|
role: "system",
|
|
52
52
|
content:
|
|
53
53
|
"You are a content summarization tool. You have access to a get-content function that can extract text content from the current page. Use this function to get the page content, then create a concise abstract in exactly 100 tokens or less. Focus on the main topics, purpose, and key information. Be specific and informative. Your summary will be fed into a text completion prompt, so make it as concise as possible.",
|
|
54
|
+
id: crypto.randomUUID(), // Use proper UUID instead of Date.now()
|
|
54
55
|
},
|
|
55
56
|
];
|
|
56
57
|
|
|
57
58
|
const result = await executePrompt(
|
|
58
59
|
messages,
|
|
59
|
-
|
|
60
|
-
() => ({
|
|
60
|
+
{
|
|
61
61
|
endpoint: "/alpaca/editor/ai/prompt",
|
|
62
62
|
promptData: {
|
|
63
63
|
itemid: editContext.contentEditorItem!.id,
|
|
64
64
|
language: editContext.contentEditorItem!.language,
|
|
65
65
|
version: editContext.contentEditorItem!.version,
|
|
66
66
|
},
|
|
67
|
-
|
|
68
|
-
}),
|
|
67
|
+
},
|
|
69
68
|
{
|
|
70
69
|
profile: "context-generation",
|
|
71
70
|
addContextContent: false,
|
|
72
71
|
addAllContent: false,
|
|
72
|
+
model: "gpt-4o-mini", // Use a fast, efficient model for context generation
|
|
73
|
+
allowedFunctions: ["get-content"],
|
|
73
74
|
},
|
|
74
|
-
undefined,
|
|
75
|
-
"gpt-4o-mini", // Use a fast, efficient model for context generation
|
|
76
75
|
);
|
|
77
76
|
|
|
78
77
|
console.log("result", result);
|
|
@@ -52,10 +52,6 @@ export function SidebarView({
|
|
|
52
52
|
);
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
-
if (!active) {
|
|
56
|
-
return <div className="hidden h-full" />;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
55
|
// Single panel - no splitter needed
|
|
60
56
|
if (resolvedPanels.length === 1) {
|
|
61
57
|
const panel = resolvedPanels[0];
|
|
@@ -63,7 +59,11 @@ export function SidebarView({
|
|
|
63
59
|
|
|
64
60
|
return (
|
|
65
61
|
<div
|
|
66
|
-
className={cn(
|
|
62
|
+
className={cn(
|
|
63
|
+
"h-full",
|
|
64
|
+
detached ? "p-2" : "border-gray-3 border-r",
|
|
65
|
+
!active ? "hidden" : ""
|
|
66
|
+
)}
|
|
67
67
|
>
|
|
68
68
|
<div
|
|
69
69
|
className={cn(
|
|
@@ -111,7 +111,11 @@ export function SidebarView({
|
|
|
111
111
|
);
|
|
112
112
|
|
|
113
113
|
return (
|
|
114
|
-
<div className={cn(
|
|
114
|
+
<div className={cn(
|
|
115
|
+
"h-full",
|
|
116
|
+
detached ? "p-2" : "",
|
|
117
|
+
!active ? "hidden" : ""
|
|
118
|
+
)}>
|
|
115
119
|
<Splitter
|
|
116
120
|
panels={splitterPanels}
|
|
117
121
|
direction="vertical"
|
|
@@ -140,8 +140,8 @@ export function ViewSelector() {
|
|
|
140
140
|
<VerticalDotsIcon />
|
|
141
141
|
</Button>
|
|
142
142
|
</PopoverTrigger>
|
|
143
|
-
</TooltipTrigger>
|
|
144
|
-
<TooltipContent>More views</TooltipContent>
|
|
143
|
+
</TooltipTrigger >
|
|
144
|
+
<TooltipContent side="right">More views</TooltipContent>
|
|
145
145
|
</Tooltip>
|
|
146
146
|
<PopoverContent
|
|
147
147
|
className="w-56 p-2"
|
|
@@ -5,9 +5,22 @@ import {
|
|
|
5
5
|
TooltipTrigger,
|
|
6
6
|
} from "../../components/ui/tooltip";
|
|
7
7
|
import { cn } from "../../lib/utils";
|
|
8
|
-
import { MouseEventHandler } from "react";
|
|
8
|
+
import { MouseEventHandler, forwardRef } from "react";
|
|
9
9
|
|
|
10
|
-
export
|
|
10
|
+
export const SimpleIconButton = forwardRef<
|
|
11
|
+
HTMLButtonElement,
|
|
12
|
+
{
|
|
13
|
+
onClick: MouseEventHandler;
|
|
14
|
+
className?: string;
|
|
15
|
+
icon?: React.ReactNode;
|
|
16
|
+
label: string;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
id?: string;
|
|
19
|
+
size?: "large" | "small";
|
|
20
|
+
selected?: boolean;
|
|
21
|
+
children?: React.ReactNode;
|
|
22
|
+
}
|
|
23
|
+
>(({
|
|
11
24
|
onClick,
|
|
12
25
|
className,
|
|
13
26
|
icon,
|
|
@@ -17,21 +30,12 @@ export function SimpleIconButton({
|
|
|
17
30
|
id,
|
|
18
31
|
selected,
|
|
19
32
|
children,
|
|
20
|
-
}
|
|
21
|
-
onClick: MouseEventHandler;
|
|
22
|
-
className?: string;
|
|
23
|
-
icon?: React.ReactNode;
|
|
24
|
-
label: string;
|
|
25
|
-
disabled?: boolean;
|
|
26
|
-
id?: string;
|
|
27
|
-
size?: "large" | "small";
|
|
28
|
-
selected?: boolean;
|
|
29
|
-
children?: React.ReactNode;
|
|
30
|
-
}) {
|
|
33
|
+
}, ref) => {
|
|
31
34
|
return (
|
|
32
35
|
<Tooltip>
|
|
33
36
|
<TooltipTrigger asChild>
|
|
34
37
|
<button
|
|
38
|
+
ref={ref}
|
|
35
39
|
id={id}
|
|
36
40
|
disabled={disabled}
|
|
37
41
|
className={cn(
|
|
@@ -54,4 +58,6 @@ export function SimpleIconButton({
|
|
|
54
58
|
<TooltipContent>{label}</TooltipContent>
|
|
55
59
|
</Tooltip>
|
|
56
60
|
);
|
|
57
|
-
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
SimpleIconButton.displayName = "SimpleIconButton";
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export interface JsonCleanupResult {
|
|
2
|
+
cleanedJson: string;
|
|
3
|
+
isComplete: boolean;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export class JsonCleaner {
|
|
7
|
+
public static cleanupJson(json: string): JsonCleanupResult {
|
|
8
|
+
// Handle dangling commas
|
|
9
|
+
json = json.replace(/,}/g, "}");
|
|
10
|
+
json = json.replace(/,]/g, "]");
|
|
11
|
+
|
|
12
|
+
const stringResult = this.correctUnendedStrings(json);
|
|
13
|
+
json = stringResult.correctedJson;
|
|
14
|
+
let isComplete = stringResult.isComplete;
|
|
15
|
+
|
|
16
|
+
// Process the JSON string character by character
|
|
17
|
+
let isInString = false;
|
|
18
|
+
let isEscape = false;
|
|
19
|
+
const openStack: string[] = [];
|
|
20
|
+
|
|
21
|
+
for (let i = 0; i < json.length; i++) {
|
|
22
|
+
const c = json[i];
|
|
23
|
+
|
|
24
|
+
if (c === '"' && !isEscape) {
|
|
25
|
+
isInString = !isInString;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!isInString) {
|
|
29
|
+
switch (c) {
|
|
30
|
+
case '{':
|
|
31
|
+
openStack.push(c);
|
|
32
|
+
break;
|
|
33
|
+
case '}':
|
|
34
|
+
if (openStack.length > 0) {
|
|
35
|
+
openStack.pop();
|
|
36
|
+
}
|
|
37
|
+
break;
|
|
38
|
+
case '[':
|
|
39
|
+
openStack.push(c);
|
|
40
|
+
break;
|
|
41
|
+
case ']':
|
|
42
|
+
if (openStack.length > 0) {
|
|
43
|
+
openStack.pop();
|
|
44
|
+
}
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
isEscape = isInString && c === '\\' && !isEscape;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Append missing closing brackets
|
|
53
|
+
while (openStack.length > 0) {
|
|
54
|
+
const open = openStack.pop();
|
|
55
|
+
if (open === '{') {
|
|
56
|
+
json += "}";
|
|
57
|
+
} else if (open === '[') {
|
|
58
|
+
json += "]";
|
|
59
|
+
}
|
|
60
|
+
isComplete = false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
cleanedJson: json,
|
|
65
|
+
isComplete: isComplete
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private static correctUnendedStrings(json: string): { correctedJson: string; isComplete: boolean } {
|
|
70
|
+
let isComplete = true;
|
|
71
|
+
let isInString = false;
|
|
72
|
+
let isEscape = false;
|
|
73
|
+
|
|
74
|
+
for (let i = 0; i < json.length; i++) {
|
|
75
|
+
if (json[i] === '"' && !isEscape) {
|
|
76
|
+
isInString = !isInString;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
isEscape = isInString && json[i] === '\\' && !isEscape;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (isInString) {
|
|
83
|
+
json += '[...]"';
|
|
84
|
+
isComplete = false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
correctedJson: json,
|
|
89
|
+
isComplete: isComplete
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -115,7 +115,7 @@ export function CompareView() {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
const originalSelector = (
|
|
118
|
-
<div className="flex flex-1 flex-shrink-0 items-center justify-center border-b bg-white p-1 text-sm text-gray-600">
|
|
118
|
+
<div className=" gap-1 flex flex-1 flex-shrink-0 items-center justify-center border-b bg-white p-1 text-sm text-gray-600">
|
|
119
119
|
<LanguageSelector
|
|
120
120
|
selectedLanguage={editContext.contentEditorItem?.descriptor?.language}
|
|
121
121
|
showAllLanguagesSwitch={false}
|
|
@@ -149,7 +149,7 @@ export function CompareView() {
|
|
|
149
149
|
);
|
|
150
150
|
|
|
151
151
|
const compareToSelector = (
|
|
152
|
-
<div className="flex flex-1 flex-shrink-0 items-center justify-center border-b bg-white p-1 text-sm text-gray-600">
|
|
152
|
+
<div className="gap-1 flex flex-1 flex-shrink-0 items-center justify-center border-b bg-white p-1 text-sm text-gray-600">
|
|
153
153
|
<LanguageSelector
|
|
154
154
|
selectedLanguage={compareTo?.language}
|
|
155
155
|
showAllLanguagesSwitch={false}
|