@theia/ai-ide 1.62.0-next.3 → 1.62.1
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/lib/browser/ai-configuration/agent-configuration-widget.d.ts +3 -3
- package/lib/browser/ai-configuration/agent-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/agent-configuration-widget.js +24 -18
- package/lib/browser/ai-configuration/agent-configuration-widget.js.map +1 -1
- package/lib/browser/ai-configuration/ai-configuration-view-contribution.js +1 -1
- package/lib/browser/ai-configuration/ai-configuration-view-contribution.js.map +1 -1
- package/lib/browser/ai-configuration/ai-configuration-widget.d.ts +2 -0
- package/lib/browser/ai-configuration/ai-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/ai-configuration-widget.js +6 -0
- package/lib/browser/ai-configuration/ai-configuration-widget.js.map +1 -1
- package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.d.ts +138 -0
- package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.d.ts.map +1 -0
- package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.js +492 -0
- package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.js.map +1 -0
- package/lib/browser/ai-configuration/template-settings-renderer.d.ts +4 -5
- package/lib/browser/ai-configuration/template-settings-renderer.d.ts.map +1 -1
- package/lib/browser/ai-configuration/template-settings-renderer.js +15 -31
- package/lib/browser/ai-configuration/template-settings-renderer.js.map +1 -1
- package/lib/browser/architect-agent.d.ts +1 -1
- package/lib/browser/architect-agent.d.ts.map +1 -1
- package/lib/browser/architect-agent.js +2 -2
- package/lib/browser/architect-agent.js.map +1 -1
- package/lib/browser/coder-agent.d.ts +2 -2
- package/lib/browser/coder-agent.d.ts.map +1 -1
- package/lib/browser/coder-agent.js +7 -3
- package/lib/browser/coder-agent.js.map +1 -1
- package/lib/browser/file-changeset-functions.d.ts +14 -1
- package/lib/browser/file-changeset-functions.d.ts.map +1 -1
- package/lib/browser/file-changeset-functions.js +153 -14
- package/lib/browser/file-changeset-functions.js.map +1 -1
- package/lib/browser/frontend-module.d.ts.map +1 -1
- package/lib/browser/frontend-module.js +11 -1
- package/lib/browser/frontend-module.js.map +1 -1
- package/lib/browser/template-preference-contribution.d.ts +2 -2
- package/lib/browser/template-preference-contribution.d.ts.map +1 -1
- package/lib/browser/template-preference-contribution.js +2 -2
- package/lib/browser/template-preference-contribution.js.map +1 -1
- package/lib/browser/workspace-functions.d.ts +1 -1
- package/lib/browser/workspace-functions.js +13 -13
- package/lib/common/architect-prompt-template.d.ts +2 -3
- package/lib/common/architect-prompt-template.d.ts.map +1 -1
- package/lib/common/architect-prompt-template.js +49 -11
- package/lib/common/architect-prompt-template.js.map +1 -1
- package/lib/common/coder-replace-prompt-template.d.ts +6 -3
- package/lib/common/coder-replace-prompt-template.d.ts.map +1 -1
- package/lib/common/coder-replace-prompt-template.js +148 -22
- package/lib/common/coder-replace-prompt-template.js.map +1 -1
- package/lib/common/command-chat-agents.d.ts +1 -1
- package/lib/common/command-chat-agents.d.ts.map +1 -1
- package/lib/common/command-chat-agents.js +4 -4
- package/lib/common/command-chat-agents.js.map +1 -1
- package/lib/common/command-prompt-template.d.ts +2 -2
- package/lib/common/command-prompt-template.d.ts.map +1 -1
- package/lib/common/command-prompt-template.js +18 -15
- package/lib/common/command-prompt-template.js.map +1 -1
- package/lib/common/orchestrator-chat-agent.d.ts +2 -3
- package/lib/common/orchestrator-chat-agent.d.ts.map +1 -1
- package/lib/common/orchestrator-chat-agent.js +11 -26
- package/lib/common/orchestrator-chat-agent.js.map +1 -1
- package/lib/common/orchestrator-prompt-template.d.ts +2 -2
- package/lib/common/orchestrator-prompt-template.d.ts.map +1 -1
- package/lib/common/orchestrator-prompt-template.js +4 -1
- package/lib/common/orchestrator-prompt-template.js.map +1 -1
- package/lib/common/universal-chat-agent.d.ts +5 -1
- package/lib/common/universal-chat-agent.d.ts.map +1 -1
- package/lib/common/universal-chat-agent.js +2 -2
- package/lib/common/universal-chat-agent.js.map +1 -1
- package/lib/common/universal-prompt-template.d.ts +3 -3
- package/lib/common/universal-prompt-template.d.ts.map +1 -1
- package/lib/common/universal-prompt-template.js +1 -2
- package/lib/common/universal-prompt-template.js.map +1 -1
- package/package.json +17 -17
- package/src/browser/ai-configuration/agent-configuration-widget.tsx +31 -24
- package/src/browser/ai-configuration/ai-configuration-view-contribution.ts +1 -1
- package/src/browser/ai-configuration/ai-configuration-widget.tsx +6 -0
- package/src/browser/ai-configuration/prompt-fragments-configuration-widget.tsx +710 -0
- package/src/browser/ai-configuration/template-settings-renderer.tsx +18 -38
- package/src/browser/architect-agent.ts +3 -3
- package/src/browser/coder-agent.ts +10 -5
- package/src/browser/file-changeset-functions.ts +152 -14
- package/src/browser/frontend-module.ts +14 -2
- package/src/browser/style/index.css +320 -0
- package/src/browser/template-preference-contribution.ts +4 -4
- package/src/browser/workspace-functions.ts +3 -3
- package/src/common/architect-prompt-template.ts +54 -14
- package/src/common/coder-replace-prompt-template.ts +150 -24
- package/src/common/command-chat-agents.ts +4 -4
- package/src/common/command-prompt-template.ts +21 -18
- package/src/common/orchestrator-chat-agent.ts +12 -28
- package/src/common/orchestrator-prompt-template.ts +7 -4
- package/src/common/universal-chat-agent.ts +2 -2
- package/src/common/universal-prompt-template.ts +4 -5
|
@@ -0,0 +1,710 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2025 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { nls } from '@theia/core';
|
|
18
|
+
import { ConfirmDialog, ReactWidget, codicon } from '@theia/core/lib/browser';
|
|
19
|
+
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
|
20
|
+
import {
|
|
21
|
+
CustomizedPromptFragment,
|
|
22
|
+
PromptFragment,
|
|
23
|
+
isCustomizedPromptFragment,
|
|
24
|
+
isBasePromptFragment,
|
|
25
|
+
PromptService,
|
|
26
|
+
BasePromptFragment
|
|
27
|
+
} from '@theia/ai-core/lib/common/prompt-service';
|
|
28
|
+
import * as React from '@theia/core/shared/react';
|
|
29
|
+
import '../../../src/browser/style/index.css';
|
|
30
|
+
import { AgentService } from '@theia/ai-core/lib/common/agent-service';
|
|
31
|
+
import { Agent } from '@theia/ai-core/lib/common/agent';
|
|
32
|
+
import { CustomizationSource } from '@theia/ai-core/lib/browser/frontend-prompt-customization-service';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Widget for configuring AI prompt fragments and prompt variant sets.
|
|
36
|
+
* Allows users to view, create, edit, and manage various types of prompt
|
|
37
|
+
* fragments including their customizations and variants.
|
|
38
|
+
*/
|
|
39
|
+
@injectable()
|
|
40
|
+
export class AIPromptFragmentsConfigurationWidget extends ReactWidget {
|
|
41
|
+
|
|
42
|
+
static readonly ID = 'ai-prompt-fragments-configuration';
|
|
43
|
+
static readonly LABEL = nls.localize('theia/ai/core/promptFragmentsConfiguration/label', 'Prompt Fragments');
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Stores all available prompt fragments by ID
|
|
47
|
+
*/
|
|
48
|
+
protected promptFragmentMap: Map<string, PromptFragment[]> = new Map<string, PromptFragment[]>();
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Stores prompt variant sets and their variant IDs
|
|
52
|
+
*/
|
|
53
|
+
protected promptVariantsMap: Map<string, string[]> = new Map<string, string[]>();
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Currently active prompt fragments
|
|
57
|
+
*/
|
|
58
|
+
protected activePromptFragments: PromptFragment[] = [];
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Tracks expanded state of prompt fragment sections in the UI
|
|
62
|
+
*/
|
|
63
|
+
protected expandedPromptFragmentIds: Set<string> = new Set();
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Tracks expanded state of prompt content display
|
|
67
|
+
*/
|
|
68
|
+
protected expandedPromptFragmentTemplates: Set<string> = new Set();
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Tracks expanded state of prompt variant set sections
|
|
72
|
+
*/
|
|
73
|
+
protected expandedPromptVariantSetIds: Set<string> = new Set();
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* All available agents that may use prompts
|
|
77
|
+
*/
|
|
78
|
+
protected availableAgents: Agent[] = [];
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Maps prompt variant set IDs to their currently selected variant IDs
|
|
82
|
+
*/
|
|
83
|
+
protected selectedVariantIds: Map<string, string | undefined> = new Map();
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Maps prompt variant set IDs to their default variant IDs
|
|
87
|
+
*/
|
|
88
|
+
protected defaultVariantIds: Map<string, string | undefined> = new Map();
|
|
89
|
+
|
|
90
|
+
@inject(PromptService) protected promptService: PromptService;
|
|
91
|
+
@inject(AgentService) protected agentService: AgentService;
|
|
92
|
+
|
|
93
|
+
@postConstruct()
|
|
94
|
+
protected init(): void {
|
|
95
|
+
this.id = AIPromptFragmentsConfigurationWidget.ID;
|
|
96
|
+
this.title.label = AIPromptFragmentsConfigurationWidget.LABEL;
|
|
97
|
+
this.title.caption = AIPromptFragmentsConfigurationWidget.LABEL;
|
|
98
|
+
this.title.closable = true;
|
|
99
|
+
this.addClass('ai-configuration-tab-content');
|
|
100
|
+
this.loadPromptFragments();
|
|
101
|
+
this.loadAgents();
|
|
102
|
+
|
|
103
|
+
this.toDispose.pushAll([
|
|
104
|
+
this.promptService.onPromptsChange(() => {
|
|
105
|
+
this.loadPromptFragments();
|
|
106
|
+
}),
|
|
107
|
+
this.promptService.onSelectedVariantChange(notification => {
|
|
108
|
+
this.selectedVariantIds.set(notification.promptVariantSetId, notification.variantId);
|
|
109
|
+
this.update();
|
|
110
|
+
}),
|
|
111
|
+
this.agentService.onDidChangeAgents(() => {
|
|
112
|
+
this.loadAgents();
|
|
113
|
+
})
|
|
114
|
+
]);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Loads all prompt fragments and prompt variant sets from the prompt service.
|
|
119
|
+
* Preserves UI expansion states and updates variant information.
|
|
120
|
+
*/
|
|
121
|
+
protected async loadPromptFragments(): Promise<void> {
|
|
122
|
+
this.promptFragmentMap = this.promptService.getAllPromptFragments();
|
|
123
|
+
this.promptVariantsMap = this.promptService.getPromptVariantSets();
|
|
124
|
+
this.activePromptFragments = this.promptService.getActivePromptFragments();
|
|
125
|
+
|
|
126
|
+
// Preserve expansion state when reloading
|
|
127
|
+
const existingExpandedFragmentIds = new Set(this.expandedPromptFragmentIds);
|
|
128
|
+
const existingExpandedPromptVariantIds = new Set(this.expandedPromptVariantSetIds);
|
|
129
|
+
const existingExpandedTemplates = new Set(this.expandedPromptFragmentTemplates);
|
|
130
|
+
|
|
131
|
+
// If no sections were previously expanded, expand all by default
|
|
132
|
+
if (existingExpandedFragmentIds.size === 0) {
|
|
133
|
+
this.expandedPromptFragmentIds = new Set(Array.from(this.promptFragmentMap.keys()));
|
|
134
|
+
} else {
|
|
135
|
+
// Keep existing expansion state but remove entries for fragments that no longer exist
|
|
136
|
+
this.expandedPromptFragmentIds = new Set(
|
|
137
|
+
Array.from(existingExpandedFragmentIds).filter(id => this.promptFragmentMap.has(id))
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (existingExpandedPromptVariantIds.size === 0) {
|
|
142
|
+
this.expandedPromptVariantSetIds = new Set(Array.from(this.promptVariantsMap.keys()));
|
|
143
|
+
} else {
|
|
144
|
+
// Keep existing expansion state but remove entries for prompt variant sets that no longer exist
|
|
145
|
+
this.expandedPromptVariantSetIds = new Set(
|
|
146
|
+
Array.from(existingExpandedPromptVariantIds).filter(id => this.promptVariantsMap.has(id))
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// For templates, preserve existing expanded states - don't expand by default
|
|
151
|
+
this.expandedPromptFragmentTemplates = new Set(
|
|
152
|
+
Array.from(existingExpandedTemplates).filter(id => {
|
|
153
|
+
const [fragmentId] = id.split('_');
|
|
154
|
+
return this.promptFragmentMap.has(fragmentId);
|
|
155
|
+
})
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
// Update variant information (selected/default) for prompt variant sets
|
|
159
|
+
for (const promptVariantSetId of this.promptVariantsMap.keys()) {
|
|
160
|
+
const selectedId = await this.promptService.getSelectedVariantId(promptVariantSetId);
|
|
161
|
+
const defaultId = await this.promptService.getDefaultVariantId(promptVariantSetId);
|
|
162
|
+
this.selectedVariantIds.set(promptVariantSetId, selectedId);
|
|
163
|
+
this.defaultVariantIds.set(promptVariantSetId, defaultId);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
this.update();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Loads all available agents from the agent service
|
|
171
|
+
*/
|
|
172
|
+
protected loadAgents(): void {
|
|
173
|
+
this.availableAgents = this.agentService.getAllAgents();
|
|
174
|
+
this.update();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Finds agents that use a specific prompt variant set
|
|
179
|
+
* @param promptVariantSetId ID of the prompt variant set to match
|
|
180
|
+
* @returns Array of agents that use the prompt variant set
|
|
181
|
+
*/
|
|
182
|
+
protected getAgentsUsingPromptVariantId(promptVariantSetId: string): Agent[] {
|
|
183
|
+
return this.availableAgents.filter((agent: Agent) =>
|
|
184
|
+
agent.prompts.find(promptVariantSet => promptVariantSet.id === promptVariantSetId)
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
protected togglePromptVariantSetExpansion = (promptVariantSetId: string): void => {
|
|
189
|
+
if (this.expandedPromptVariantSetIds.has(promptVariantSetId)) {
|
|
190
|
+
this.expandedPromptVariantSetIds.delete(promptVariantSetId);
|
|
191
|
+
} else {
|
|
192
|
+
this.expandedPromptVariantSetIds.add(promptVariantSetId);
|
|
193
|
+
}
|
|
194
|
+
this.update();
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
protected togglePromptFragmentExpansion = (promptFragmentId: string): void => {
|
|
198
|
+
if (this.expandedPromptFragmentIds.has(promptFragmentId)) {
|
|
199
|
+
this.expandedPromptFragmentIds.delete(promptFragmentId);
|
|
200
|
+
} else {
|
|
201
|
+
this.expandedPromptFragmentIds.add(promptFragmentId);
|
|
202
|
+
}
|
|
203
|
+
this.update();
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
protected toggleTemplateExpansion = (fragmentKey: string, event: React.MouseEvent): void => {
|
|
207
|
+
event.stopPropagation();
|
|
208
|
+
if (this.expandedPromptFragmentTemplates.has(fragmentKey)) {
|
|
209
|
+
this.expandedPromptFragmentTemplates.delete(fragmentKey);
|
|
210
|
+
} else {
|
|
211
|
+
this.expandedPromptFragmentTemplates.add(fragmentKey);
|
|
212
|
+
}
|
|
213
|
+
this.update();
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Call the edit action for the provided customized prompt fragment
|
|
218
|
+
* @param promptFragment Fragment to edit
|
|
219
|
+
* @param event Mouse event
|
|
220
|
+
*/
|
|
221
|
+
protected editPromptCustomization = (promptFragment: CustomizedPromptFragment, event: React.MouseEvent): void => {
|
|
222
|
+
event.stopPropagation();
|
|
223
|
+
this.promptService.editCustomization(promptFragment.id, promptFragment.customizationId);
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Determines if a prompt fragment is currently the active one for its ID
|
|
228
|
+
* @param promptFragment The prompt fragment to check
|
|
229
|
+
* @returns True if this prompt fragment is the active customization
|
|
230
|
+
*/
|
|
231
|
+
protected isActiveCustomization(promptFragment: PromptFragment): boolean {
|
|
232
|
+
const activePromptFragment = this.activePromptFragments.find(activePrompt => activePrompt.id === promptFragment.id);
|
|
233
|
+
if (!activePromptFragment) {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (isCustomizedPromptFragment(activePromptFragment) && isCustomizedPromptFragment(promptFragment)) {
|
|
238
|
+
return (
|
|
239
|
+
activePromptFragment.id === promptFragment.id &&
|
|
240
|
+
activePromptFragment.template === promptFragment.template &&
|
|
241
|
+
activePromptFragment.customizationId === promptFragment.customizationId &&
|
|
242
|
+
activePromptFragment.priority === promptFragment.priority
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (isBasePromptFragment(activePromptFragment) && isBasePromptFragment(promptFragment)) {
|
|
247
|
+
return (
|
|
248
|
+
activePromptFragment.id === promptFragment.id &&
|
|
249
|
+
activePromptFragment.template === promptFragment.template
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Resets a prompt fragment to use a specific customization (with confirmation dialog)
|
|
258
|
+
* @param customization customization to reset to
|
|
259
|
+
* @param event Mouse event
|
|
260
|
+
*/
|
|
261
|
+
protected resetToPromptFragment = async (customization: PromptFragment, event: React.MouseEvent): Promise<void> => {
|
|
262
|
+
event.stopPropagation();
|
|
263
|
+
|
|
264
|
+
if (isCustomizedPromptFragment(customization)) {
|
|
265
|
+
// Get the customization type to show in the confirmation dialog
|
|
266
|
+
const type = await this.promptService.getCustomizationType(customization.id, customization.customizationId);
|
|
267
|
+
|
|
268
|
+
const dialog = new ConfirmDialog({
|
|
269
|
+
title: nls.localize('theia/ai/core/promptFragmentsConfiguration/resetToCustomizationDialogTitle', 'Reset to Customization'),
|
|
270
|
+
msg: nls.localize('theia/ai/core/promptFragmentsConfiguration/resetToCustomizationDialogMsg',
|
|
271
|
+
'Are you sure you want to reset the prompt fragment "{0}" to use the {1} customization? This will remove all higher-priority customizations.',
|
|
272
|
+
customization.id, type),
|
|
273
|
+
ok: nls.localize('theia/ai/core/promptFragmentsConfiguration/resetButton', 'Reset'),
|
|
274
|
+
cancel: nls.localize('theia/ai/core/promptFragmentsConfiguration/cancelButton', 'Cancel')
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
const shouldReset = await dialog.open();
|
|
278
|
+
if (shouldReset) {
|
|
279
|
+
await this.promptService.resetToCustomization(customization.id, customization.customizationId);
|
|
280
|
+
}
|
|
281
|
+
} else {
|
|
282
|
+
const dialog = new ConfirmDialog({
|
|
283
|
+
title: nls.localize('theia/ai/core/promptFragmentsConfiguration/resetToBuiltInDialogTitle', 'Reset to Built-in'),
|
|
284
|
+
msg: nls.localize('theia/ai/core/promptFragmentsConfiguration/resetToBuiltInDialogMsg',
|
|
285
|
+
'Are you sure you want to reset the prompt fragment "{0}" to its built-in version? This will remove all customizations.', customization.id),
|
|
286
|
+
ok: nls.localize('theia/ai/core/promptFragmentsConfiguration/resetButton', 'Reset'),
|
|
287
|
+
cancel: nls.localize('theia/ai/core/promptFragmentsConfiguration/cancelButton', 'Cancel')
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
const shouldReset = await dialog.open();
|
|
291
|
+
if (shouldReset) {
|
|
292
|
+
await this.promptService.resetToBuiltIn(customization.id);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Creates a new customization for a built-in prompt fragment
|
|
299
|
+
* @param promptFragment Built-in prompt fragment to customize
|
|
300
|
+
* @param event Mouse event
|
|
301
|
+
*/
|
|
302
|
+
protected createPromptFragmentCustomization = (promptFragment: BasePromptFragment, event: React.MouseEvent): void => {
|
|
303
|
+
event.stopPropagation();
|
|
304
|
+
this.promptService.createBuiltInCustomization(promptFragment.id);
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Deletes a customization with confirmation dialog
|
|
309
|
+
* @param customization Customized prompt fragment to delete
|
|
310
|
+
* @param event Mouse event
|
|
311
|
+
*/
|
|
312
|
+
protected deletePromptFragmentCustomization = async (customization: CustomizedPromptFragment, event: React.MouseEvent): Promise<void> => {
|
|
313
|
+
event.stopPropagation();
|
|
314
|
+
|
|
315
|
+
// First get the customization type and description to show in the confirmation dialog
|
|
316
|
+
const type = await this.promptService.getCustomizationType(customization.id, customization.customizationId) || '';
|
|
317
|
+
const description = await this.promptService.getCustomizationDescription(customization.id, customization.customizationId) || '';
|
|
318
|
+
|
|
319
|
+
const dialog = new ConfirmDialog({
|
|
320
|
+
title: nls.localize('theia/ai/core/promptFragmentsConfiguration/removeCustomizationDialogTitle', 'Remove Customization'),
|
|
321
|
+
msg: description ?
|
|
322
|
+
nls.localize('theia/ai/core/promptFragmentsConfiguration/removeCustomizationWithDescDialogMsg',
|
|
323
|
+
'Are you sure you want to remove the {0} customization for prompt fragment "{1}" ({2})?', type, customization.id, description) :
|
|
324
|
+
nls.localize('theia/ai/core/promptFragmentsConfiguration/removeCustomizationDialogMsg',
|
|
325
|
+
'Are you sure you want to remove the {0} customization for prompt fragment "{1}"?', type, customization.id),
|
|
326
|
+
ok: nls.localize('theia/ai/core/promptFragmentsConfiguration/removeButton', 'Remove'),
|
|
327
|
+
cancel: nls.localize('theia/ai/core/promptFragmentsConfiguration/cancelButton', 'Cancel')
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
const shouldDelete = await dialog.open();
|
|
331
|
+
if (shouldDelete) {
|
|
332
|
+
await this.promptService.removeCustomization(customization.id, customization.customizationId);
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Removes all prompt customizations (resets to built-in versions) with confirmation
|
|
338
|
+
*/
|
|
339
|
+
protected removeAllCustomizations = async (): Promise<void> => {
|
|
340
|
+
const dialog = new ConfirmDialog({
|
|
341
|
+
title: nls.localize('theia/ai/core/promptFragmentsConfiguration/resetAllCustomizationsDialogTitle', 'Reset All Customizations'),
|
|
342
|
+
msg: nls.localize('theia/ai/core/promptFragmentsConfiguration/resetAllCustomizationsDialogMsg',
|
|
343
|
+
'Are you sure you want to reset all prompt fragments to their built-in versions? This will remove all customizations.'),
|
|
344
|
+
ok: nls.localize('theia/ai/core/promptFragmentsConfiguration/resetAllButton', 'Reset All'),
|
|
345
|
+
cancel: nls.localize('theia/ai/core/promptFragmentsConfiguration/cancelButton', 'Cancel')
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
const shouldReset = await dialog.open();
|
|
349
|
+
if (shouldReset) {
|
|
350
|
+
this.promptFragmentMap.forEach(fragments => {
|
|
351
|
+
this.promptService.resetToBuiltIn(fragments[0].id);
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Main render method for the widget
|
|
358
|
+
* @returns Complete UI for the configuration widget
|
|
359
|
+
*/
|
|
360
|
+
protected render(): React.ReactNode {
|
|
361
|
+
const nonSystemPromptFragments = this.getNonPromptVariantSetFragments();
|
|
362
|
+
|
|
363
|
+
return (
|
|
364
|
+
<div className='ai-prompt-fragments-configuration'>
|
|
365
|
+
<div className="prompt-fragments-header">
|
|
366
|
+
<h2>{nls.localize('theia/ai/core/promptFragmentsConfiguration/headerTitle', 'Prompt Fragments')}</h2>
|
|
367
|
+
<div className="global-actions">
|
|
368
|
+
<button
|
|
369
|
+
className="global-action-button"
|
|
370
|
+
onClick={this.removeAllCustomizations}
|
|
371
|
+
title={nls.localize('theia/ai/core/promptFragmentsConfiguration/resetAllCustomizationsTitle', 'Reset all customizations')}
|
|
372
|
+
>
|
|
373
|
+
{nls.localize('theia/ai/core/promptFragmentsConfiguration/resetAllPromptFragments',
|
|
374
|
+
'Reset all prompt fragments')} <span className={codicon('clear-all')}></span>
|
|
375
|
+
</button>
|
|
376
|
+
</div>
|
|
377
|
+
</div>
|
|
378
|
+
|
|
379
|
+
<div className="prompt-variants-container">
|
|
380
|
+
<h3 className="section-header">{nls.localize('theia/ai/core/promptFragmentsConfiguration/promptVariantsHeader', 'Prompt Variant Sets')}</h3>
|
|
381
|
+
{Array.from(this.promptVariantsMap.entries()).map(([promptVariantSetId, variantIds]) =>
|
|
382
|
+
this.renderPromptVariantSet(promptVariantSetId, variantIds)
|
|
383
|
+
)}
|
|
384
|
+
</div>
|
|
385
|
+
|
|
386
|
+
{nonSystemPromptFragments.size > 0 && <div className="prompt-fragments-container">
|
|
387
|
+
<h3 className="section-header">{nls.localize('theia/ai/core/promptFragmentsConfiguration/otherPromptFragmentsHeader', 'Other Prompt Fragments')}</h3>
|
|
388
|
+
{Array.from(nonSystemPromptFragments.entries()).map(([promptFragmentId, fragments]) =>
|
|
389
|
+
this.renderPromptFragment(promptFragmentId, fragments)
|
|
390
|
+
)}
|
|
391
|
+
</div>}
|
|
392
|
+
|
|
393
|
+
{this.promptFragmentMap.size === 0 && (
|
|
394
|
+
<div className="no-fragments">
|
|
395
|
+
<p>{nls.localize('theia/ai/core/promptFragmentsConfiguration/noFragmentsAvailable', 'No prompt fragments available.')}</p>
|
|
396
|
+
</div>
|
|
397
|
+
)}
|
|
398
|
+
</div>
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Renders a prompt variant set with its variants
|
|
404
|
+
* @param promptVariantSetId ID of the prompt variant set
|
|
405
|
+
* @param variantIds Array of variant IDs
|
|
406
|
+
* @returns React node for the prompt variant set group
|
|
407
|
+
*/
|
|
408
|
+
protected renderPromptVariantSet(promptVariantSetId: string, variantIds: string[]): React.ReactNode {
|
|
409
|
+
const isSectionExpanded = this.expandedPromptVariantSetIds.has(promptVariantSetId);
|
|
410
|
+
|
|
411
|
+
// Get selected and default variant IDs from our class properties
|
|
412
|
+
const selectedVariantId = this.selectedVariantIds.get(promptVariantSetId);
|
|
413
|
+
const defaultVariantId = this.defaultVariantIds.get(promptVariantSetId);
|
|
414
|
+
|
|
415
|
+
// Get variant fragments grouped by ID
|
|
416
|
+
const variantGroups = new Map<string, PromptFragment[]>();
|
|
417
|
+
|
|
418
|
+
// First, collect all actual fragments for each variant ID
|
|
419
|
+
for (const variantId of variantIds) {
|
|
420
|
+
if (this.promptFragmentMap.has(variantId)) {
|
|
421
|
+
variantGroups.set(variantId, this.promptFragmentMap.get(variantId)!);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const relatedAgents = this.getAgentsUsingPromptVariantId(promptVariantSetId);
|
|
426
|
+
|
|
427
|
+
return (
|
|
428
|
+
<div className="prompt-fragment-section" key={`variant-${promptVariantSetId}`}>
|
|
429
|
+
<div
|
|
430
|
+
className={`prompt-fragment-header ${isSectionExpanded ? 'expanded' : ''}`}
|
|
431
|
+
onClick={() => this.togglePromptVariantSetExpansion(promptVariantSetId)}
|
|
432
|
+
>
|
|
433
|
+
<div className="prompt-fragment-title">
|
|
434
|
+
<span className="expansion-icon">{isSectionExpanded ? '▼' : '▶'}</span>
|
|
435
|
+
<h2>{promptVariantSetId}</h2>
|
|
436
|
+
</div>
|
|
437
|
+
{relatedAgents.length > 0 && (
|
|
438
|
+
<div className="agent-chips-container">
|
|
439
|
+
{relatedAgents.map(agent => (
|
|
440
|
+
<span key={agent.id} className="agent-chip"
|
|
441
|
+
title={nls.localize('theia/ai/core/promptFragmentsConfiguration/usedByAgentTitle', 'Used by agent: {0}', agent.name)}
|
|
442
|
+
onClick={e => e.stopPropagation()}>
|
|
443
|
+
<span className={codicon('copilot')}></span>
|
|
444
|
+
{agent.name}
|
|
445
|
+
</span>
|
|
446
|
+
))}
|
|
447
|
+
</div>
|
|
448
|
+
)}
|
|
449
|
+
</div>
|
|
450
|
+
{isSectionExpanded && (
|
|
451
|
+
<div className="prompt-fragment-body">
|
|
452
|
+
<div className="prompt-fragment-description">
|
|
453
|
+
<p>{nls.localize('theia/ai/core/promptFragmentsConfiguration/variantsOfSystemPrompt', 'Variants of this prompt variant set:')}</p>
|
|
454
|
+
</div>
|
|
455
|
+
{Array.from(variantGroups.entries()).map(([variantId, fragments]) => {
|
|
456
|
+
const isVariantExpanded = this.expandedPromptFragmentIds.has(variantId);
|
|
457
|
+
|
|
458
|
+
return (
|
|
459
|
+
<div key={variantId} className={`prompt-fragment-section ${selectedVariantId === variantId ? 'selected-variant' : ''}`}>
|
|
460
|
+
<div
|
|
461
|
+
className={`prompt-fragment-header ${isVariantExpanded ? 'expanded' : ''}`}
|
|
462
|
+
onClick={() => this.togglePromptFragmentExpansion(variantId)}
|
|
463
|
+
>
|
|
464
|
+
<div className="prompt-fragment-title">
|
|
465
|
+
<span className="expansion-icon">{isVariantExpanded ? '▼' : '▶'}</span>
|
|
466
|
+
<h4>{variantId}</h4>
|
|
467
|
+
{defaultVariantId === variantId && (
|
|
468
|
+
<span className="badge default-variant"
|
|
469
|
+
title={nls.localize('theia/ai/core/promptFragmentsConfiguration/defaultVariantTitle', 'Default variant')}>
|
|
470
|
+
{nls.localize('theia/ai/core/promptFragmentsConfiguration/defaultVariantLabel', 'Default')}
|
|
471
|
+
</span>
|
|
472
|
+
)}
|
|
473
|
+
{selectedVariantId === variantId && (
|
|
474
|
+
<span className="selected-indicator"
|
|
475
|
+
title={nls.localize('theia/ai/core/promptFragmentsConfiguration/selectedVariantTitle', 'Selected variant')}>
|
|
476
|
+
{nls.localize('theia/ai/core/promptFragmentsConfiguration/selectedVariantLabel', 'Selected')}
|
|
477
|
+
</span>
|
|
478
|
+
)}
|
|
479
|
+
</div>
|
|
480
|
+
</div>
|
|
481
|
+
{isVariantExpanded && (
|
|
482
|
+
<div className='prompt-fragment-body'>
|
|
483
|
+
{fragments.map(fragment => this.renderPromptFragmentCustomization(fragment))}
|
|
484
|
+
</div>
|
|
485
|
+
)}
|
|
486
|
+
</div>
|
|
487
|
+
);
|
|
488
|
+
})}
|
|
489
|
+
</div>
|
|
490
|
+
)}
|
|
491
|
+
</div>
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Gets fragments that aren't part of any prompt variant set
|
|
497
|
+
* @returns Map of fragment IDs to their customizations
|
|
498
|
+
*/
|
|
499
|
+
protected getNonPromptVariantSetFragments(): Map<string, PromptFragment[]> {
|
|
500
|
+
const nonSystemPromptFragments = new Map<string, PromptFragment[]>();
|
|
501
|
+
const allVariantIds = new Set<string>();
|
|
502
|
+
|
|
503
|
+
// Collect all variant IDs from prompt variant sets
|
|
504
|
+
this.promptVariantsMap.forEach((variants, _) => {
|
|
505
|
+
variants.forEach(variantId => allVariantIds.add(variantId));
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
// Add prompt variant set main IDs
|
|
509
|
+
this.promptVariantsMap.forEach((_, promptVariantSetId) => {
|
|
510
|
+
allVariantIds.add(promptVariantSetId);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
// Filter the fragment map to only include non-prompt variant set fragments
|
|
514
|
+
this.promptFragmentMap.forEach((fragments, promptFragmentId) => {
|
|
515
|
+
if (!allVariantIds.has(promptFragmentId)) {
|
|
516
|
+
nonSystemPromptFragments.set(promptFragmentId, fragments);
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
return nonSystemPromptFragments;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Renders a prompt fragment with all of its customizations
|
|
525
|
+
* @param promptFragmentId ID of the prompt fragment
|
|
526
|
+
* @param customizations Array of the customizations
|
|
527
|
+
* @returns React node for the prompt fragment
|
|
528
|
+
*/
|
|
529
|
+
protected renderPromptFragment(promptFragmentId: string, customizations: PromptFragment[]): React.ReactNode {
|
|
530
|
+
const isSectionExpanded = this.expandedPromptFragmentIds.has(promptFragmentId);
|
|
531
|
+
|
|
532
|
+
return (
|
|
533
|
+
<div className={'prompt-fragment-group'} key={promptFragmentId}>
|
|
534
|
+
<div
|
|
535
|
+
className={`prompt-fragment-header ${isSectionExpanded ? 'expanded' : ''}`}
|
|
536
|
+
onClick={() => this.togglePromptFragmentExpansion(promptFragmentId)}
|
|
537
|
+
>
|
|
538
|
+
<div className="prompt-fragment-title">
|
|
539
|
+
<span className="expansion-icon">{isSectionExpanded ? '▼' : '▶'}</span>
|
|
540
|
+
{promptFragmentId}
|
|
541
|
+
</div>
|
|
542
|
+
</div>
|
|
543
|
+
{isSectionExpanded && (
|
|
544
|
+
<div className="prompt-fragment-body">
|
|
545
|
+
{customizations.map(fragment => this.renderPromptFragmentCustomization(fragment))}
|
|
546
|
+
</div>
|
|
547
|
+
)}
|
|
548
|
+
</div>
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Renders a single prompt fragment customization with its controls and content
|
|
554
|
+
* @param promptFragment The prompt fragment to render
|
|
555
|
+
* @returns React node for the prompt fragment
|
|
556
|
+
*/
|
|
557
|
+
protected renderPromptFragmentCustomization(promptFragment: PromptFragment): React.ReactNode {
|
|
558
|
+
const isCustomized = isCustomizedPromptFragment(promptFragment);
|
|
559
|
+
const isActive = this.isActiveCustomization(promptFragment);
|
|
560
|
+
// Create a unique key for this fragment to track expansion state
|
|
561
|
+
const fragmentKey = `${promptFragment.id}_${isCustomized ? promptFragment.customizationId : 'built-in'}`;
|
|
562
|
+
const isTemplateExpanded = this.expandedPromptFragmentTemplates.has(fragmentKey);
|
|
563
|
+
const hasCustomizedBuiltIn =
|
|
564
|
+
this.promptFragmentMap.get(promptFragment.id)?.some(fragment => isCustomizedPromptFragment(fragment) && fragment.priority === CustomizationSource.CUSTOMIZED);
|
|
565
|
+
|
|
566
|
+
return (
|
|
567
|
+
<div
|
|
568
|
+
className={`prompt-customization ${isActive ? 'active-customization' : ''}`}
|
|
569
|
+
key={fragmentKey}
|
|
570
|
+
>
|
|
571
|
+
<div className="prompt-customization-header">
|
|
572
|
+
<div className="prompt-customization-title">
|
|
573
|
+
<React.Suspense fallback={<div>Loading...</div>}>
|
|
574
|
+
<CustomizationTypeBadge promptFragment={promptFragment} promptService={this.promptService} />
|
|
575
|
+
</React.Suspense>
|
|
576
|
+
{isActive && (
|
|
577
|
+
<span className="active-indicator"
|
|
578
|
+
title={nls.localize('theia/ai/core/promptFragmentsConfiguration/activeCustomizationTitle', 'Active customization')}>
|
|
579
|
+
{nls.localize('theia/ai/core/promptFragmentsConfiguration/activeCustomizationLabel', 'Active')}
|
|
580
|
+
</span>
|
|
581
|
+
)}
|
|
582
|
+
</div>
|
|
583
|
+
<div className="prompt-customization-actions">
|
|
584
|
+
{!isCustomized && !hasCustomizedBuiltIn && (
|
|
585
|
+
<button
|
|
586
|
+
className="template-action-button config-button"
|
|
587
|
+
onClick={e => this.createPromptFragmentCustomization(promptFragment, e)}
|
|
588
|
+
title={nls.localize('theia/ai/core/promptFragmentsConfiguration/createCustomizationTitle', 'Create Customization')}
|
|
589
|
+
>
|
|
590
|
+
<span className={codicon('add')}></span>
|
|
591
|
+
</button>
|
|
592
|
+
)}
|
|
593
|
+
{isCustomized && (
|
|
594
|
+
<button
|
|
595
|
+
className="source-uri-button"
|
|
596
|
+
onClick={e => this.editPromptCustomization(promptFragment, e)}
|
|
597
|
+
title={nls.localize('theia/ai/core/promptFragmentsConfiguration/editTemplateTitle', 'Edit template')}
|
|
598
|
+
>
|
|
599
|
+
<span className={codicon('edit')}></span>
|
|
600
|
+
</button>
|
|
601
|
+
)}
|
|
602
|
+
{!isActive && (
|
|
603
|
+
<button
|
|
604
|
+
className="template-action-button reset-button"
|
|
605
|
+
onClick={e => this.resetToPromptFragment(promptFragment, e)}
|
|
606
|
+
title={!isCustomized ?
|
|
607
|
+
nls.localize('theia/ai/core/promptFragmentsConfiguration/resetToBuiltInTitle', 'Reset to this built-in') :
|
|
608
|
+
nls.localize('theia/ai/core/promptFragmentsConfiguration/resetToCustomizationTitle', 'Reset to this customization')}
|
|
609
|
+
>
|
|
610
|
+
<span className={codicon('discard')}></span>
|
|
611
|
+
</button>
|
|
612
|
+
)}
|
|
613
|
+
{isCustomized && (
|
|
614
|
+
<button
|
|
615
|
+
className="template-action-button delete-button"
|
|
616
|
+
onClick={e => this.deletePromptFragmentCustomization(promptFragment, e)}
|
|
617
|
+
title={nls.localize('theia/ai/core/promptFragmentsConfiguration/deleteCustomizationTitle', 'Delete Customization')}
|
|
618
|
+
>
|
|
619
|
+
<span className={codicon('trash')}></span>
|
|
620
|
+
</button>
|
|
621
|
+
)}
|
|
622
|
+
</div>
|
|
623
|
+
</div>
|
|
624
|
+
|
|
625
|
+
{isCustomized && (
|
|
626
|
+
<React.Suspense fallback={<div>Loading...</div>}>
|
|
627
|
+
<DescriptionBadge promptFragment={promptFragment} promptService={this.promptService} />
|
|
628
|
+
</React.Suspense>
|
|
629
|
+
)}
|
|
630
|
+
|
|
631
|
+
<div className="template-content-container">
|
|
632
|
+
<div
|
|
633
|
+
className="template-toggle-button"
|
|
634
|
+
onClick={e => this.toggleTemplateExpansion(fragmentKey, e)}
|
|
635
|
+
>
|
|
636
|
+
<span className="template-expansion-icon">{isTemplateExpanded ? '▼' : '▶'}</span>
|
|
637
|
+
<span>{nls.localize('theia/ai/core/promptFragmentsConfiguration/promptTemplateText', 'Prompt Template Text')}</span>
|
|
638
|
+
</div>
|
|
639
|
+
|
|
640
|
+
{isTemplateExpanded && (
|
|
641
|
+
<div className="template-content">
|
|
642
|
+
<pre>{promptFragment.template}</pre>
|
|
643
|
+
</div>
|
|
644
|
+
)}
|
|
645
|
+
</div>
|
|
646
|
+
</div>
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Props for the CustomizationTypeBadge component
|
|
653
|
+
*/
|
|
654
|
+
interface CustomizationTypeBadgeProps {
|
|
655
|
+
promptFragment: PromptFragment;
|
|
656
|
+
promptService: PromptService;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Displays a badge indicating the type of a prompt fragment customization (built-in, user, workspace)
|
|
661
|
+
*/
|
|
662
|
+
const CustomizationTypeBadge: React.FC<CustomizationTypeBadgeProps> = ({ promptFragment, promptService }) => {
|
|
663
|
+
const [typeLabel, setTypeLabel] = React.useState<string>('unknown');
|
|
664
|
+
|
|
665
|
+
React.useEffect(() => {
|
|
666
|
+
const fetchCustomizationType = async () => {
|
|
667
|
+
if (isCustomizedPromptFragment(promptFragment)) {
|
|
668
|
+
const customizationType = await promptService.getCustomizationType(promptFragment.id, promptFragment.customizationId);
|
|
669
|
+
setTypeLabel(`${customizationType ?
|
|
670
|
+
customizationType + ' ' + nls.localize('theia/ai/core/promptFragmentsConfiguration/customization', 'customization')
|
|
671
|
+
: nls.localize('theia/ai/core/promptFragmentsConfiguration/customizationLabel', 'Customization')}`);
|
|
672
|
+
} else {
|
|
673
|
+
setTypeLabel(nls.localize('theia/ai/core/promptFragmentsConfiguration/builtInLabel', 'Built-in'));
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
fetchCustomizationType();
|
|
678
|
+
}, [promptFragment, promptService]);
|
|
679
|
+
|
|
680
|
+
return <span>{typeLabel}</span>;
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Props for the DescriptionBadge component
|
|
685
|
+
*/
|
|
686
|
+
interface CustomizationDescriptionBadgeProps {
|
|
687
|
+
promptFragment: CustomizedPromptFragment;
|
|
688
|
+
promptService: PromptService;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* Displays the description of a customized prompt fragment if available
|
|
693
|
+
*/
|
|
694
|
+
const DescriptionBadge: React.FC<CustomizationDescriptionBadgeProps> = ({ promptFragment, promptService }) => {
|
|
695
|
+
const [description, setDescription] = React.useState<string>('');
|
|
696
|
+
|
|
697
|
+
React.useEffect(() => {
|
|
698
|
+
const fetchDescription = async () => {
|
|
699
|
+
const customizationDescription = await promptService.getCustomizationDescription(
|
|
700
|
+
promptFragment.id,
|
|
701
|
+
promptFragment.customizationId
|
|
702
|
+
);
|
|
703
|
+
setDescription(customizationDescription || '');
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
fetchDescription();
|
|
707
|
+
}, [promptFragment.id, promptFragment.customizationId, promptService]);
|
|
708
|
+
|
|
709
|
+
return <span className="prompt-customization-description">{description}</span>;
|
|
710
|
+
};
|