@parhelia/core 0.1.12390 → 0.1.12397
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/Editor.js +32 -17
- package/dist/editor/Editor.js.map +1 -1
- package/dist/editor/PictureCropper.js +9 -4
- package/dist/editor/PictureCropper.js.map +1 -1
- package/dist/editor/PictureEditor.js +12 -13
- package/dist/editor/PictureEditor.js.map +1 -1
- package/dist/editor/SetupWizard.js +20 -2
- package/dist/editor/SetupWizard.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.js +14 -3
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/dialogs/agentDialogTypes.d.ts +1 -1
- package/dist/editor/client/editContext.d.ts +4 -0
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js +283 -298
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
- package/dist/editor/pictureRawValue.d.ts +3 -0
- package/dist/editor/pictureRawValue.js +30 -0
- package/dist/editor/pictureRawValue.js.map +1 -0
- package/dist/editor/services/templateBuilderService.d.ts +7 -0
- package/dist/editor/services/templateBuilderService.js +7 -1
- package/dist/editor/services/templateBuilderService.js.map +1 -1
- package/dist/editor/settings/About.js +25 -19
- package/dist/editor/settings/About.js.map +1 -1
- package/dist/editor/settings/panels/AgentProfileEditorPanel.d.ts +14 -0
- package/dist/editor/settings/panels/AgentProfileEditorPanel.js +7 -0
- package/dist/editor/settings/panels/AgentProfileEditorPanel.js.map +1 -0
- package/dist/editor/settings/panels/AgentsPanel.js +2 -2
- package/dist/editor/settings/panels/AgentsPanel.js.map +1 -1
- package/dist/editor/settings/panels/ProjectTemplatesPanel.js +146 -8
- package/dist/editor/settings/panels/ProjectTemplatesPanel.js.map +1 -1
- package/dist/editor/setup-wizard/steps/CompleteStep.d.ts +2 -1
- package/dist/editor/setup-wizard/steps/CompleteStep.js +2 -1
- package/dist/editor/setup-wizard/steps/CompleteStep.js.map +1 -1
- package/dist/editor/setup-wizard/steps/LicenseActivationStep.d.ts +9 -0
- package/dist/editor/setup-wizard/steps/LicenseActivationStep.js +160 -0
- package/dist/editor/setup-wizard/steps/LicenseActivationStep.js.map +1 -0
- package/dist/editor/setup-wizard/steps/LicenseEmailStep.d.ts +10 -0
- package/dist/editor/setup-wizard/steps/LicenseEmailStep.js +101 -0
- package/dist/editor/setup-wizard/steps/LicenseEmailStep.js.map +1 -0
- package/dist/editor/template-wizard/TemplateStructureInlineEditor.js +422 -65
- package/dist/editor/template-wizard/TemplateStructureInlineEditor.js.map +1 -1
- package/dist/licensing/EmailEntry.js +1 -1
- package/dist/licensing/EmailEntry.js.map +1 -1
- package/dist/licensing/LicenseActivationForm.js +1 -1
- package/dist/licensing/LicenseActivationForm.js.map +1 -1
- package/dist/licensing/LicenseCodeEntry.js +2 -2
- package/dist/licensing/LicenseCodeEntry.js.map +1 -1
- package/dist/licensing/LicenseContext.js +18 -9
- package/dist/licensing/LicenseContext.js.map +1 -1
- package/dist/licensing/LicenseOverlay.js +2 -1
- package/dist/licensing/LicenseOverlay.js.map +1 -1
- package/dist/licensing/licenseService.d.ts +10 -0
- package/dist/licensing/licenseService.js +28 -0
- package/dist/licensing/licenseService.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/task-board/TaskBoardWorkspace.js +3 -2
- package/dist/task-board/TaskBoardWorkspace.js.map +1 -1
- package/dist/task-board/components/ProjectDashboard.d.ts +4 -0
- package/dist/task-board/components/ProjectDashboard.js +1 -1
- package/dist/task-board/components/ProjectDashboard.js.map +1 -1
- package/dist/task-board/components/ProjectListContent.d.ts +1 -1
- package/dist/task-board/components/ProjectListContent.js +4 -1
- package/dist/task-board/components/ProjectListContent.js.map +1 -1
- package/dist/task-board/components/ProjectOverviewContent.d.ts +17 -0
- package/dist/task-board/components/ProjectOverviewContent.js +134 -0
- package/dist/task-board/components/ProjectOverviewContent.js.map +1 -0
- package/dist/task-board/components/ProjectSelector.d.ts +1 -1
- package/dist/task-board/components/ProjectSelector.js +1 -1
- package/dist/task-board/components/ProjectSelector.js.map +1 -1
- package/dist/task-board/components/TaskDetailPanel.js +59 -9
- package/dist/task-board/components/TaskDetailPanel.js.map +1 -1
- package/dist/task-board/services/taskService.d.ts +4 -1
- package/dist/task-board/services/taskService.js +3 -0
- package/dist/task-board/services/taskService.js.map +1 -1
- package/dist/task-board/taskBoardNavStore.d.ts +3 -1
- package/dist/task-board/taskBoardNavStore.js.map +1 -1
- package/dist/task-board/types.d.ts +30 -0
- package/package.json +1 -1
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { Bot, Check, ChevronDown, ChevronRight, GripVertical, Plus, Search, Settings2, Trash2, X, } from "lucide-react";
|
|
5
|
+
import { Button } from "../../components/ui/button";
|
|
6
|
+
import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover";
|
|
5
7
|
import { AgentTerminal } from "../ai/AgentTerminal";
|
|
8
|
+
import DialogButtons from "../ui/DialogButtons";
|
|
6
9
|
import { useEditContext } from "../client/editContext";
|
|
7
10
|
import TreeListSelector from "../ui/TreeListSelector";
|
|
8
|
-
import { parseTemplateBuilderSuggestionText, TEMPLATE_BUILDER_AGENT_PROFILE_ID, TEMPLATE_BUILDER_AGENT_PROFILE_NAME, TEMPLATE_BUILDER_SUGGESTION_SCHEMA_DESCRIPTION, templateBuilderCreate, templateBuilderFieldTypes, templateBuilderGet, templateBuilderUpdate, } from "../services/templateBuilderService";
|
|
11
|
+
import { parseTemplateBuilderSuggestionText, TEMPLATE_BUILDER_AGENT_PROFILE_ID, TEMPLATE_BUILDER_AGENT_PROFILE_NAME, TEMPLATE_BUILDER_SUGGESTION_SCHEMA_DESCRIPTION, templateBuilderCreate, templateBuilderFieldTypes, templateBuilderGet, templateBuilderSkills, templateBuilderUpdate, } from "../services/templateBuilderService";
|
|
9
12
|
import { loadAiProfiles } from "../services/aiService";
|
|
10
13
|
import { normalizeGuid } from "../utils";
|
|
11
14
|
const TEMPLATE_TEMPLATE_ID = "AB86861A-6030-46C5-B394-E8F99E8B87DB";
|
|
@@ -69,6 +72,234 @@ const toWizardSection = (section) => ({
|
|
|
69
72
|
name: section?.name || "",
|
|
70
73
|
fields: (section?.fields || []).map((field) => toWizardField(field)),
|
|
71
74
|
});
|
|
75
|
+
const cloneWizardField = (field) => ({
|
|
76
|
+
...field,
|
|
77
|
+
});
|
|
78
|
+
const cloneWizardSection = (section) => ({
|
|
79
|
+
...section,
|
|
80
|
+
fields: section.fields.map(cloneWizardField),
|
|
81
|
+
});
|
|
82
|
+
const sameBaseTemplateChangeTarget = (left, right) => (!!left.templateId &&
|
|
83
|
+
!!right.templateId &&
|
|
84
|
+
normalizeGuid(left.templateId) === normalizeGuid(right.templateId)) ||
|
|
85
|
+
normalizeName(left.templateName) === normalizeName(right.templateName) ||
|
|
86
|
+
(!!left.templatePath &&
|
|
87
|
+
!!right.templatePath &&
|
|
88
|
+
normalizeName(left.templatePath) === normalizeName(right.templatePath));
|
|
89
|
+
const findSectionIndex = (sectionList, targetSectionLocalId, targetSectionName) => sectionList.findIndex((sectionItem) => (!!targetSectionLocalId && sectionItem.localId === targetSectionLocalId) ||
|
|
90
|
+
(!!targetSectionName &&
|
|
91
|
+
normalizeName(sectionItem.name) === normalizeName(targetSectionName)));
|
|
92
|
+
const findFieldIndex = (fieldList, targetFieldLocalId, targetFieldName) => fieldList.findIndex((fieldItem) => (!!targetFieldLocalId && fieldItem.localId === targetFieldLocalId) ||
|
|
93
|
+
(!!targetFieldName && normalizeName(fieldItem.name) === normalizeName(targetFieldName)));
|
|
94
|
+
function applyPendingSuggestionsToSections(baseSections, pendingChanges) {
|
|
95
|
+
const mergedSections = baseSections.map(cloneWizardSection);
|
|
96
|
+
for (const change of pendingChanges) {
|
|
97
|
+
if (change.kind === "section") {
|
|
98
|
+
if (change.action === "add") {
|
|
99
|
+
mergedSections.push(cloneWizardSection(change.section));
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const sectionIndex = findSectionIndex(mergedSections, change.targetSectionLocalId, change.targetSectionName);
|
|
103
|
+
if (sectionIndex < 0)
|
|
104
|
+
continue;
|
|
105
|
+
if (change.action === "remove") {
|
|
106
|
+
mergedSections.splice(sectionIndex, 1);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const sectionToUpdate = mergedSections[sectionIndex];
|
|
110
|
+
if (!sectionToUpdate)
|
|
111
|
+
continue;
|
|
112
|
+
mergedSections[sectionIndex] = {
|
|
113
|
+
...sectionToUpdate,
|
|
114
|
+
name: change.section.name,
|
|
115
|
+
};
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (change.kind !== "field")
|
|
119
|
+
continue;
|
|
120
|
+
const sectionIndex = findSectionIndex(mergedSections, change.sectionLocalId, change.targetSectionName);
|
|
121
|
+
if (sectionIndex < 0)
|
|
122
|
+
continue;
|
|
123
|
+
const targetSection = mergedSections[sectionIndex];
|
|
124
|
+
if (!targetSection)
|
|
125
|
+
continue;
|
|
126
|
+
if (change.action === "add") {
|
|
127
|
+
targetSection.fields.push(cloneWizardField(change.field));
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const fieldIndex = findFieldIndex(targetSection.fields, change.targetFieldLocalId, change.targetFieldName);
|
|
131
|
+
if (fieldIndex < 0)
|
|
132
|
+
continue;
|
|
133
|
+
if (change.action === "remove") {
|
|
134
|
+
targetSection.fields.splice(fieldIndex, 1);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const fieldToUpdate = targetSection.fields[fieldIndex];
|
|
138
|
+
if (!fieldToUpdate)
|
|
139
|
+
continue;
|
|
140
|
+
targetSection.fields[fieldIndex] = cloneWizardField(change.field);
|
|
141
|
+
}
|
|
142
|
+
return mergedSections;
|
|
143
|
+
}
|
|
144
|
+
function mergePendingSuggestionChanges(existingChanges, incomingChanges) {
|
|
145
|
+
const merged = [...existingChanges];
|
|
146
|
+
for (const change of incomingChanges) {
|
|
147
|
+
if (change.kind === "baseTemplate") {
|
|
148
|
+
const inverseIndex = merged.findIndex((entry) => entry.kind === "baseTemplate" &&
|
|
149
|
+
entry.action !== change.action &&
|
|
150
|
+
sameBaseTemplateChangeTarget(entry, change));
|
|
151
|
+
if (inverseIndex >= 0) {
|
|
152
|
+
merged.splice(inverseIndex, 1);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
merged.push(change);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (change.kind === "section" && change.action === "add") {
|
|
159
|
+
const duplicateSectionAddIndex = merged.findIndex((entry) => entry.kind === "section" &&
|
|
160
|
+
entry.action === "add" &&
|
|
161
|
+
normalizeName(entry.section.name) === normalizeName(change.section.name));
|
|
162
|
+
if (duplicateSectionAddIndex >= 0) {
|
|
163
|
+
const existingEntry = merged[duplicateSectionAddIndex];
|
|
164
|
+
if (existingEntry &&
|
|
165
|
+
existingEntry.kind === "section" &&
|
|
166
|
+
existingEntry.action === "add") {
|
|
167
|
+
merged[duplicateSectionAddIndex] = {
|
|
168
|
+
...existingEntry,
|
|
169
|
+
reason: change.reason ?? existingEntry.reason,
|
|
170
|
+
section: {
|
|
171
|
+
...existingEntry.section,
|
|
172
|
+
name: change.section.name,
|
|
173
|
+
fields: change.section.fields.map(cloneWizardField),
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
merged.push(change);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (change.kind === "section" && change.action !== "add") {
|
|
183
|
+
const pendingSectionAddIndex = merged.findIndex((entry) => entry.kind === "section" &&
|
|
184
|
+
entry.action === "add" &&
|
|
185
|
+
entry.section.localId === change.targetSectionLocalId);
|
|
186
|
+
if (pendingSectionAddIndex >= 0) {
|
|
187
|
+
const pendingSectionEntry = merged[pendingSectionAddIndex];
|
|
188
|
+
if (pendingSectionEntry &&
|
|
189
|
+
pendingSectionEntry.kind === "section" &&
|
|
190
|
+
pendingSectionEntry.action === "add") {
|
|
191
|
+
if (change.action === "remove") {
|
|
192
|
+
const removedSectionLocalId = pendingSectionEntry.section.localId;
|
|
193
|
+
merged.splice(pendingSectionAddIndex, 1);
|
|
194
|
+
for (let index = merged.length - 1; index >= 0; index--) {
|
|
195
|
+
const entry = merged[index];
|
|
196
|
+
if (!entry)
|
|
197
|
+
continue;
|
|
198
|
+
if (entry.kind === "field" &&
|
|
199
|
+
entry.sectionLocalId === removedSectionLocalId) {
|
|
200
|
+
merged.splice(index, 1);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
const updatedPendingSection = {
|
|
206
|
+
...pendingSectionEntry,
|
|
207
|
+
section: {
|
|
208
|
+
...pendingSectionEntry.section,
|
|
209
|
+
name: change.section.name,
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
merged[pendingSectionAddIndex] = updatedPendingSection;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
merged.push(change);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (change.kind === "field") {
|
|
220
|
+
const pendingSectionAddIndex = merged.findIndex((entry) => entry.kind === "section" &&
|
|
221
|
+
entry.action === "add" &&
|
|
222
|
+
entry.section.localId === change.sectionLocalId);
|
|
223
|
+
if (pendingSectionAddIndex >= 0) {
|
|
224
|
+
const pendingSectionAdd = merged[pendingSectionAddIndex];
|
|
225
|
+
if (!pendingSectionAdd ||
|
|
226
|
+
pendingSectionAdd.kind !== "section" ||
|
|
227
|
+
pendingSectionAdd.action !== "add") {
|
|
228
|
+
// Fall through to default handling
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
const pendingFieldIndex = change.action === "add"
|
|
232
|
+
? -1
|
|
233
|
+
: findFieldIndex(pendingSectionAdd.section.fields, change.targetFieldLocalId, change.targetFieldName);
|
|
234
|
+
if (change.action === "add") {
|
|
235
|
+
const duplicateFieldIndex = findFieldIndex(pendingSectionAdd.section.fields, undefined, change.field.name);
|
|
236
|
+
const nextFields = [...pendingSectionAdd.section.fields];
|
|
237
|
+
if (duplicateFieldIndex >= 0) {
|
|
238
|
+
nextFields[duplicateFieldIndex] = cloneWizardField(change.field);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
nextFields.push(cloneWizardField(change.field));
|
|
242
|
+
}
|
|
243
|
+
const updated = {
|
|
244
|
+
...pendingSectionAdd,
|
|
245
|
+
section: {
|
|
246
|
+
...pendingSectionAdd.section,
|
|
247
|
+
fields: nextFields,
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
merged[pendingSectionAddIndex] = updated;
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
if (pendingFieldIndex >= 0) {
|
|
254
|
+
const nextFields = [...pendingSectionAdd.section.fields];
|
|
255
|
+
if (change.action === "remove") {
|
|
256
|
+
nextFields.splice(pendingFieldIndex, 1);
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
nextFields[pendingFieldIndex] = cloneWizardField(change.field);
|
|
260
|
+
}
|
|
261
|
+
const updated = {
|
|
262
|
+
...pendingSectionAdd,
|
|
263
|
+
section: {
|
|
264
|
+
...pendingSectionAdd.section,
|
|
265
|
+
fields: nextFields,
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
merged[pendingSectionAddIndex] = updated;
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (change.action !== "add") {
|
|
274
|
+
const pendingFieldAddIndex = merged.findIndex((entry) => entry.kind === "field" &&
|
|
275
|
+
entry.action === "add" &&
|
|
276
|
+
entry.sectionLocalId === change.sectionLocalId &&
|
|
277
|
+
(findFieldIndex([entry.field], change.targetFieldLocalId, change.targetFieldName) >= 0 ||
|
|
278
|
+
normalizeName(entry.field.name) === normalizeName(change.field.name)));
|
|
279
|
+
if (pendingFieldAddIndex >= 0) {
|
|
280
|
+
const pendingFieldEntry = merged[pendingFieldAddIndex];
|
|
281
|
+
if (pendingFieldEntry && pendingFieldEntry.kind === "field") {
|
|
282
|
+
if (change.action === "remove") {
|
|
283
|
+
merged.splice(pendingFieldAddIndex, 1);
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
const updated = {
|
|
287
|
+
...pendingFieldEntry,
|
|
288
|
+
field: cloneWizardField(change.field),
|
|
289
|
+
};
|
|
290
|
+
merged[pendingFieldAddIndex] = updated;
|
|
291
|
+
}
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
merged.push(change);
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
merged.push(change);
|
|
300
|
+
}
|
|
301
|
+
return merged;
|
|
302
|
+
}
|
|
72
303
|
function toRequestSections(sections) {
|
|
73
304
|
return sections
|
|
74
305
|
.filter((section) => section.name.trim())
|
|
@@ -100,7 +331,7 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
100
331
|
const [isLoading, setIsLoading] = useState(true);
|
|
101
332
|
const [isSaving, setIsSaving] = useState(false);
|
|
102
333
|
const [fieldTypeGroups, setFieldTypeGroups] = useState([{ name: "General", types: defaultFieldTypeOptions }]);
|
|
103
|
-
const [templateName, setTemplateName] = useState(initialName || "New Template");
|
|
334
|
+
const [templateName, setTemplateName] = useState(() => initialName || item?.name || "New Template");
|
|
104
335
|
const [baseTemplates, setBaseTemplates] = useState([]);
|
|
105
336
|
const [selectedTemplateNodesInTree, setSelectedTemplateNodesInTree] = useState([]);
|
|
106
337
|
const [fieldSearchQuery, setFieldSearchQuery] = useState("");
|
|
@@ -120,12 +351,16 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
120
351
|
const [dragOverFieldEndSectionIdx, setDragOverFieldEndSectionIdx] = useState(null);
|
|
121
352
|
const [focusFieldDraftSectionIndex, setFocusFieldDraftSectionIndex] = useState(null);
|
|
122
353
|
const [aiProfiles, setAiProfiles] = useState([]);
|
|
354
|
+
const [templateBuilderConfiguredSkills, setTemplateBuilderConfiguredSkills] = useState([]);
|
|
123
355
|
const [pendingSuggestions, setPendingSuggestions] = useState([]);
|
|
124
356
|
const [latestSuggestionSummary, setLatestSuggestionSummary] = useState(null);
|
|
125
357
|
const [suggestionParseError, setSuggestionParseError] = useState(null);
|
|
126
358
|
const [suggestionStatus, setSuggestionStatus] = useState(null);
|
|
359
|
+
const [templatePickerOpen, setTemplatePickerOpen] = useState(false);
|
|
360
|
+
const [templatePickerPortalContainer, setTemplatePickerPortalContainer] = useState(null);
|
|
127
361
|
const newFieldNameInputRefs = useRef({});
|
|
128
362
|
const processedSuggestionMessageIdsRef = useRef(new Set());
|
|
363
|
+
const templatePickerTriggerRef = useRef(null);
|
|
129
364
|
const flatFieldTypeOptions = fieldTypeGroups.flatMap((group) => group.types || []);
|
|
130
365
|
const createEmptyFieldWithDefaultType = () => ({
|
|
131
366
|
...createEmptyField(),
|
|
@@ -136,6 +371,33 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
136
371
|
name: "",
|
|
137
372
|
fields: [createEmptyFieldWithDefaultType()],
|
|
138
373
|
});
|
|
374
|
+
const effectiveSections = useMemo(() => applyPendingSuggestionsToSections(sections, pendingSuggestions), [pendingSuggestions, sections]);
|
|
375
|
+
const effectiveBaseTemplates = useMemo(() => {
|
|
376
|
+
const merged = [...baseTemplates];
|
|
377
|
+
for (const change of pendingSuggestions) {
|
|
378
|
+
if (change.kind !== "baseTemplate")
|
|
379
|
+
continue;
|
|
380
|
+
const existingIndex = merged.findIndex((template) => sameBaseTemplateChangeTarget({
|
|
381
|
+
templateId: template.id,
|
|
382
|
+
templateName: template.name,
|
|
383
|
+
templatePath: template.path,
|
|
384
|
+
}, change));
|
|
385
|
+
if (change.action === "remove") {
|
|
386
|
+
if (existingIndex >= 0) {
|
|
387
|
+
merged.splice(existingIndex, 1);
|
|
388
|
+
}
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
if (existingIndex < 0) {
|
|
392
|
+
merged.push({
|
|
393
|
+
id: change.templateId || createLocalId("base-template"),
|
|
394
|
+
name: change.templateName,
|
|
395
|
+
path: change.templatePath,
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return merged;
|
|
400
|
+
}, [baseTemplates, pendingSuggestions]);
|
|
139
401
|
const [templateBuilderAgent] = useState(() => ({
|
|
140
402
|
id: createGuid(),
|
|
141
403
|
name: "Template Builder",
|
|
@@ -510,6 +772,7 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
510
772
|
setSections([createEmptySectionWithDefaultType()]);
|
|
511
773
|
return;
|
|
512
774
|
}
|
|
775
|
+
setTemplateName(item.name?.trim() || initialName || "New Template");
|
|
513
776
|
const result = await templateBuilderGet({ template: item.descriptor });
|
|
514
777
|
if (result.type !== "success" || !result.data) {
|
|
515
778
|
editContextRef.current?.showErrorToast({
|
|
@@ -579,7 +842,7 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
579
842
|
item,
|
|
580
843
|
]);
|
|
581
844
|
const save = async () => {
|
|
582
|
-
if (
|
|
845
|
+
if (!templateName.trim()) {
|
|
583
846
|
editContext?.showErrorToast({
|
|
584
847
|
summary: "Template name is required",
|
|
585
848
|
});
|
|
@@ -674,6 +937,7 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
674
937
|
}
|
|
675
938
|
const result = await templateBuilderUpdate({
|
|
676
939
|
template: item.descriptor,
|
|
940
|
+
name: templateName.trim(),
|
|
677
941
|
baseTemplateIds,
|
|
678
942
|
sections: toRequestSections(sections),
|
|
679
943
|
createStandardValues: hasStandardValues,
|
|
@@ -738,16 +1002,13 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
738
1002
|
};
|
|
739
1003
|
useEffect(() => {
|
|
740
1004
|
let cancelled = false;
|
|
741
|
-
loadAiProfiles()
|
|
742
|
-
.then((loadedProfiles) => {
|
|
1005
|
+
Promise.allSettled([loadAiProfiles(), templateBuilderSkills()]).then((results) => {
|
|
743
1006
|
if (cancelled)
|
|
744
1007
|
return;
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
setAiProfiles([]);
|
|
750
|
-
}
|
|
1008
|
+
const loadedProfiles = results[0];
|
|
1009
|
+
setAiProfiles(loadedProfiles.status === "fulfilled" ? loadedProfiles.value : []);
|
|
1010
|
+
const configuredSkills = results[1];
|
|
1011
|
+
setTemplateBuilderConfiguredSkills(configuredSkills.status === "fulfilled" ? configuredSkills.value : []);
|
|
751
1012
|
});
|
|
752
1013
|
return () => {
|
|
753
1014
|
cancelled = true;
|
|
@@ -758,13 +1019,13 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
758
1019
|
templateName: templateName.trim() || "New Template",
|
|
759
1020
|
template: item?.descriptor,
|
|
760
1021
|
parent: parentItem?.descriptor,
|
|
761
|
-
baseTemplates:
|
|
1022
|
+
baseTemplates: effectiveBaseTemplates.map((template) => ({
|
|
762
1023
|
id: template.id,
|
|
763
1024
|
name: template.name,
|
|
764
1025
|
path: template.path,
|
|
765
1026
|
icon: template.icon,
|
|
766
1027
|
})),
|
|
767
|
-
sections:
|
|
1028
|
+
sections: effectiveSections.map((sectionItem) => ({
|
|
768
1029
|
name: sectionItem.name,
|
|
769
1030
|
fields: sectionItem.fields.map((field) => ({
|
|
770
1031
|
name: field.name,
|
|
@@ -778,7 +1039,7 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
778
1039
|
unversioned: field.unversioned,
|
|
779
1040
|
})),
|
|
780
1041
|
})),
|
|
781
|
-
}), [
|
|
1042
|
+
}), [effectiveBaseTemplates, effectiveSections, isCreateMode, item, parentItem, templateName]);
|
|
782
1043
|
const currentTemplateId = useMemo(() => (item?.id ? normalizeGuid(item.id) : ""), [item?.id]);
|
|
783
1044
|
const templateBuilderAgentMetadata = useMemo(() => ({
|
|
784
1045
|
mode: "read-only",
|
|
@@ -791,21 +1052,45 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
791
1052
|
},
|
|
792
1053
|
}), [currentTemplateBuilderState]);
|
|
793
1054
|
const effectiveAiProfiles = useMemo(() => {
|
|
1055
|
+
const mergeTemplateBuilderSkills = (profile) => {
|
|
1056
|
+
if (normalizeGuid(profile.id) !== normalizeGuid(TEMPLATE_BUILDER_AGENT_PROFILE_ID)) {
|
|
1057
|
+
return profile;
|
|
1058
|
+
}
|
|
1059
|
+
const mergedSkills = [
|
|
1060
|
+
...(profile.allowedSkills || []),
|
|
1061
|
+
...templateBuilderConfiguredSkills,
|
|
1062
|
+
].reduce((acc, skill) => {
|
|
1063
|
+
if (!skill?.id || !skill?.name)
|
|
1064
|
+
return acc;
|
|
1065
|
+
const exists = acc.some((entry) => normalizeGuid(entry.id) === normalizeGuid(skill.id));
|
|
1066
|
+
if (!exists) {
|
|
1067
|
+
acc.push(skill);
|
|
1068
|
+
}
|
|
1069
|
+
return acc;
|
|
1070
|
+
}, []);
|
|
1071
|
+
if (mergedSkills.length === 0) {
|
|
1072
|
+
return profile;
|
|
1073
|
+
}
|
|
1074
|
+
return {
|
|
1075
|
+
...profile,
|
|
1076
|
+
allowedSkills: mergedSkills,
|
|
1077
|
+
};
|
|
1078
|
+
};
|
|
794
1079
|
if (aiProfiles.some((profile) => normalizeGuid(profile.id) === normalizeGuid(TEMPLATE_BUILDER_AGENT_PROFILE_ID))) {
|
|
795
|
-
return aiProfiles;
|
|
1080
|
+
return aiProfiles.map(mergeTemplateBuilderSkills);
|
|
796
1081
|
}
|
|
797
1082
|
return [
|
|
798
|
-
{
|
|
1083
|
+
mergeTemplateBuilderSkills({
|
|
799
1084
|
id: TEMPLATE_BUILDER_AGENT_PROFILE_ID,
|
|
800
1085
|
name: TEMPLATE_BUILDER_AGENT_PROFILE_NAME,
|
|
801
1086
|
defaultModelId: "",
|
|
802
1087
|
models: [],
|
|
803
1088
|
prompts: [],
|
|
804
1089
|
hiddenFromOverview: true,
|
|
805
|
-
},
|
|
1090
|
+
}),
|
|
806
1091
|
...aiProfiles,
|
|
807
1092
|
];
|
|
808
|
-
}, [aiProfiles]);
|
|
1093
|
+
}, [aiProfiles, templateBuilderConfiguredSkills]);
|
|
809
1094
|
const hydrateFieldFromPatch = useCallback((patch, fallback) => ({
|
|
810
1095
|
localId: createLocalId("field"),
|
|
811
1096
|
name: patch.name ?? fallback?.name ?? "",
|
|
@@ -824,6 +1109,7 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
824
1109
|
}), [flatFieldTypeOptions]);
|
|
825
1110
|
const convertSuggestionToPendingChanges = useCallback((suggestion) => {
|
|
826
1111
|
const nextChanges = [];
|
|
1112
|
+
const workingSections = effectiveSections.map(cloneWizardSection);
|
|
827
1113
|
for (const baseTemplateChange of suggestion.baseTemplates || []) {
|
|
828
1114
|
nextChanges.push({
|
|
829
1115
|
id: createLocalId("base-template-suggestion"),
|
|
@@ -837,23 +1123,26 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
837
1123
|
}
|
|
838
1124
|
for (const sectionChange of suggestion.sections || []) {
|
|
839
1125
|
if (sectionChange.action === "add") {
|
|
1126
|
+
const nextSection = {
|
|
1127
|
+
...toWizardSection(sectionChange.section),
|
|
1128
|
+
fields: (sectionChange.section.fields || []).map((field) => hydrateFieldFromPatch(field)),
|
|
1129
|
+
};
|
|
840
1130
|
nextChanges.push({
|
|
841
1131
|
id: createLocalId("section-suggestion"),
|
|
842
1132
|
kind: "section",
|
|
843
1133
|
action: "add",
|
|
844
|
-
section:
|
|
845
|
-
...toWizardSection(sectionChange.section),
|
|
846
|
-
fields: (sectionChange.section.fields || []).map((field) => hydrateFieldFromPatch(field)),
|
|
847
|
-
},
|
|
1134
|
+
section: nextSection,
|
|
848
1135
|
reason: sectionChange.reason,
|
|
849
1136
|
});
|
|
1137
|
+
workingSections.push(cloneWizardSection(nextSection));
|
|
850
1138
|
continue;
|
|
851
1139
|
}
|
|
852
|
-
const matchedSection =
|
|
1140
|
+
const matchedSection = workingSections.find((sectionItem) => normalizeName(sectionItem.name) ===
|
|
853
1141
|
normalizeName(sectionChange.targetSectionName));
|
|
854
1142
|
if (!matchedSection)
|
|
855
1143
|
continue;
|
|
856
1144
|
if (sectionChange.action === "update") {
|
|
1145
|
+
matchedSection.name = sectionChange.section.name ?? matchedSection.name;
|
|
857
1146
|
nextChanges.push({
|
|
858
1147
|
id: createLocalId("section-suggestion"),
|
|
859
1148
|
kind: "section",
|
|
@@ -869,6 +1158,10 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
869
1158
|
});
|
|
870
1159
|
continue;
|
|
871
1160
|
}
|
|
1161
|
+
const matchedSectionIndex = workingSections.findIndex((sectionItem) => sectionItem.localId === matchedSection.localId);
|
|
1162
|
+
if (matchedSectionIndex >= 0) {
|
|
1163
|
+
workingSections.splice(matchedSectionIndex, 1);
|
|
1164
|
+
}
|
|
872
1165
|
nextChanges.push({
|
|
873
1166
|
id: createLocalId("section-suggestion"),
|
|
874
1167
|
kind: "section",
|
|
@@ -880,38 +1173,66 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
880
1173
|
});
|
|
881
1174
|
}
|
|
882
1175
|
for (const fieldChange of suggestion.fields || []) {
|
|
883
|
-
|
|
884
|
-
if (!matchedSection)
|
|
885
|
-
continue;
|
|
1176
|
+
let matchedSection = workingSections.find((sectionItem) => normalizeName(sectionItem.name) === normalizeName(fieldChange.targetSectionName));
|
|
886
1177
|
if (fieldChange.action === "add") {
|
|
1178
|
+
if (!matchedSection) {
|
|
1179
|
+
const syntheticSection = {
|
|
1180
|
+
localId: createLocalId("section"),
|
|
1181
|
+
name: fieldChange.targetSectionName,
|
|
1182
|
+
fields: [],
|
|
1183
|
+
};
|
|
1184
|
+
const workingSyntheticSection = cloneWizardSection(syntheticSection);
|
|
1185
|
+
nextChanges.push({
|
|
1186
|
+
id: createLocalId("section-suggestion"),
|
|
1187
|
+
kind: "section",
|
|
1188
|
+
action: "add",
|
|
1189
|
+
section: syntheticSection,
|
|
1190
|
+
reason: fieldChange.reason,
|
|
1191
|
+
});
|
|
1192
|
+
workingSections.push(workingSyntheticSection);
|
|
1193
|
+
matchedSection = workingSyntheticSection;
|
|
1194
|
+
}
|
|
1195
|
+
const nextField = hydrateFieldFromPatch(fieldChange.field);
|
|
1196
|
+
matchedSection.fields.push(cloneWizardField(nextField));
|
|
887
1197
|
nextChanges.push({
|
|
888
1198
|
id: createLocalId("field-suggestion"),
|
|
889
1199
|
kind: "field",
|
|
890
1200
|
action: "add",
|
|
891
1201
|
sectionLocalId: matchedSection.localId,
|
|
892
1202
|
targetSectionName: matchedSection.name,
|
|
893
|
-
field:
|
|
1203
|
+
field: nextField,
|
|
894
1204
|
reason: fieldChange.reason,
|
|
895
1205
|
});
|
|
896
1206
|
continue;
|
|
897
1207
|
}
|
|
1208
|
+
if (!matchedSection)
|
|
1209
|
+
continue;
|
|
898
1210
|
const matchedField = matchedSection.fields.find((fieldItem) => normalizeName(fieldItem.name) === normalizeName(fieldChange.targetFieldName));
|
|
899
1211
|
if (!matchedField)
|
|
900
1212
|
continue;
|
|
901
1213
|
if (fieldChange.action === "update") {
|
|
1214
|
+
const updatedField = hydrateFieldFromPatch(fieldChange.field, matchedField);
|
|
1215
|
+
const matchedFieldIndex = matchedSection.fields.findIndex((fieldItem) => fieldItem.localId === matchedField.localId);
|
|
1216
|
+
if (matchedFieldIndex >= 0) {
|
|
1217
|
+
matchedSection.fields[matchedFieldIndex] = cloneWizardField(updatedField);
|
|
1218
|
+
}
|
|
902
1219
|
nextChanges.push({
|
|
903
1220
|
id: createLocalId("field-suggestion"),
|
|
904
1221
|
kind: "field",
|
|
905
1222
|
action: "update",
|
|
906
1223
|
sectionLocalId: matchedSection.localId,
|
|
907
1224
|
targetSectionName: matchedSection.name,
|
|
908
|
-
field:
|
|
1225
|
+
field: updatedField,
|
|
909
1226
|
targetFieldLocalId: matchedField.localId,
|
|
910
1227
|
targetFieldName: matchedField.name,
|
|
911
1228
|
reason: fieldChange.reason,
|
|
912
1229
|
});
|
|
913
1230
|
continue;
|
|
914
1231
|
}
|
|
1232
|
+
const matchedFieldIndex = matchedSection.fields.findIndex((fieldItem) => fieldItem.localId === matchedField.localId);
|
|
1233
|
+
if (matchedFieldIndex >= 0) {
|
|
1234
|
+
matchedSection.fields.splice(matchedFieldIndex, 1);
|
|
1235
|
+
}
|
|
915
1236
|
nextChanges.push({
|
|
916
1237
|
id: createLocalId("field-suggestion"),
|
|
917
1238
|
kind: "field",
|
|
@@ -925,7 +1246,7 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
925
1246
|
});
|
|
926
1247
|
}
|
|
927
1248
|
return nextChanges;
|
|
928
|
-
}, [
|
|
1249
|
+
}, [effectiveSections, hydrateFieldFromPatch]);
|
|
929
1250
|
const handleAgentMessage = useCallback((message) => {
|
|
930
1251
|
if (!message.id ||
|
|
931
1252
|
!message.isCompleted ||
|
|
@@ -945,7 +1266,7 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
945
1266
|
const changes = convertSuggestionToPendingChanges(suggestion);
|
|
946
1267
|
if (changes.length === 0)
|
|
947
1268
|
return;
|
|
948
|
-
setPendingSuggestions((prev) =>
|
|
1269
|
+
setPendingSuggestions((prev) => mergePendingSuggestionChanges(prev, changes));
|
|
949
1270
|
setLatestSuggestionSummary(suggestion.summary || null);
|
|
950
1271
|
setSuggestionStatus(`${changes.length} AI suggestion${changes.length === 1 ? "" : "s"} ready for review.`);
|
|
951
1272
|
setSuggestionParseError(null);
|
|
@@ -1108,14 +1429,14 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
1108
1429
|
};
|
|
1109
1430
|
const renderToggle = (checked, onClick) => (_jsx("button", { type: "button", role: "switch", "aria-checked": checked, className: `relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition ${checked ? "bg-blue-600" : "bg-gray-300"}`, onClick: onClick, children: _jsx("span", { className: `inline-block h-4 w-4 transform rounded-full bg-white transition ${checked ? "translate-x-4" : "translate-x-0.5"}` }) }));
|
|
1110
1431
|
const renderTypeSelect = (value, onChange) => (_jsx("select", { className: cls.select, value: value, onChange: (e) => onChange(e.target.value), children: fieldTypeGroups.map((group) => (_jsx("optgroup", { label: group.name, children: (group.types || []).map((t) => (_jsx("option", { value: t, children: t }, `${group.name}-${t}`))) }, group.name))) }));
|
|
1111
|
-
const renderSuggestionActions = (change) => (_jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", className: "rounded border border-violet-200 bg-white px-2 py-0.5 text-
|
|
1432
|
+
const renderSuggestionActions = (change) => (_jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", className: "rounded border border-violet-200 bg-white px-2 py-0.5 text-xs font-semibold text-violet-700 transition hover:bg-violet-50", onClick: (event) => {
|
|
1112
1433
|
event.stopPropagation();
|
|
1113
1434
|
void approveSuggestion(change.id);
|
|
1114
|
-
}, children: "Approve" }), _jsx("button", { type: "button", className: "rounded border border-gray-200 bg-white px-2 py-0.5 text-
|
|
1435
|
+
}, children: "Approve" }), _jsx("button", { type: "button", className: "rounded border border-gray-200 bg-white px-2 py-0.5 text-xs font-semibold text-gray-600 transition hover:bg-gray-50", onClick: (event) => {
|
|
1115
1436
|
event.stopPropagation();
|
|
1116
1437
|
clearSuggestion(change.id);
|
|
1117
1438
|
}, children: "Reject" })] }));
|
|
1118
|
-
const renderFooter = () => (_jsxs(
|
|
1439
|
+
const renderFooter = () => (_jsxs(DialogButtons, { className: "mt-auto items-center justify-between border-t bg-gray-50/80 px-4 py-4 backdrop-blur-sm", children: [_jsxs("label", { className: "inline-flex items-center gap-2 text-xs font-medium text-gray-500", children: ["Standard Values", renderToggle(hasStandardValues, () => setHasStandardValues((v) => !v))] }), _jsxs("div", { className: "flex items-center gap-2", children: [onClose && (_jsx(Button, { variant: "ghost", onClick: onClose, disabled: isSaving, children: "Cancel" })), _jsx(Button, { onClick: save, disabled: isSaving, className: "min-w-[140px] shadow-sm", children: isSaving ? (isCreateMode ? "Creating..." : "Updating...") : isCreateMode ? "Create" : "Update" })] })] }));
|
|
1119
1440
|
/* ────────────────────────────────────────────────────────────
|
|
1120
1441
|
* Studio Layout
|
|
1121
1442
|
* Three-column: left = inheritance tree, center = field list,
|
|
@@ -1141,27 +1462,63 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
1141
1462
|
const section = selectedFieldRef?.section;
|
|
1142
1463
|
const si = selectedFieldRef?.si;
|
|
1143
1464
|
const fi = selectedFieldRef?.fi;
|
|
1144
|
-
return (_jsxs("div", { className: "flex h-full min-h-0 flex-col gap-0 overflow-hidden rounded-lg border border-gray-200 bg-white text-gray-900", children: [
|
|
1145
|
-
|
|
1146
|
-
.
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1465
|
+
return (_jsxs("div", { className: "flex h-full min-h-0 flex-col gap-0 overflow-hidden rounded-lg border border-gray-200 bg-white text-gray-900", children: [_jsxs("div", { className: "grid min-h-0 flex-1 grid-cols-1 gap-0 lg:grid-cols-[240px_1fr_280px_420px]", children: [_jsx("div", { className: "flex flex-col gap-0 overflow-y-auto border-r border-gray-100 bg-gray-50/30", children: _jsx("div", { className: "flex-1 overflow-y-auto p-3", children: _jsxs("div", { className: "flex min-h-full flex-col", children: [(isCreateMode || item) && (_jsxs("div", { className: "mb-3 border-b border-gray-100 pb-3", children: [_jsx("div", { className: "mb-1 text-xs font-bold uppercase tracking-wider text-gray-400", children: "Template name" }), _jsx("input", { className: cls.input, value: templateName, onChange: (e) => setTemplateName(e.target.value), placeholder: "New Template" })] })), _jsx("div", { className: "mb-1.5 text-xs font-bold uppercase tracking-widest text-gray-400", children: "Base templates" }), _jsxs("div", { className: "space-y-1", children: [baseTemplates.length === 0 && pendingBaseTemplateAdditions.length === 0 && (_jsx("div", { className: "text-xs text-gray-400 italic", children: "None" })), baseTemplates.map((t) => {
|
|
1466
|
+
const templateSuggestions = getBaseTemplateSuggestions(t);
|
|
1467
|
+
const hasRemoveSuggestion = templateSuggestions.some((entry) => entry.action === "remove");
|
|
1468
|
+
return (_jsxs("div", { className: `space-y-1 rounded border px-2 py-1 text-xs ${hasRemoveSuggestion
|
|
1469
|
+
? "border-violet-200 bg-violet-50/80 text-violet-900"
|
|
1470
|
+
: "border-gray-200 bg-white text-gray-700"}`, children: [_jsxs("div", { className: "group flex items-center gap-1", children: [t.icon && _jsx("img", { src: t.icon, className: "h-3 w-3 shrink-0" }), _jsx("span", { className: `flex-1 truncate ${hasRemoveSuggestion ? "line-through" : ""}`, children: t.name }), _jsx("button", { className: "text-gray-400 transition hover:text-red-500", onClick: () => removeBaseTemplates([t]), children: _jsx(X, { className: "h-2.5 w-2.5" }) })] }), templateSuggestions.map((entry) => (_jsxs("div", { className: "flex items-center justify-between gap-2 rounded border border-violet-200 bg-white/80 px-2 py-1 text-xs text-violet-700", children: [_jsxs("span", { className: "min-w-0 flex-1 truncate", children: ["AI suggests ", entry.action === "remove" ? "removing" : "adding", " this base template", entry.reason ? ` · ${entry.reason}` : ""] }), renderSuggestionActions(entry)] }, entry.id)))] }, t.id));
|
|
1471
|
+
}), pendingBaseTemplateAdditions.map((entry) => (_jsx("div", { className: "rounded border border-dashed border-violet-300 bg-violet-50 px-2 py-1 text-xs text-violet-800", children: _jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate font-semibold", children: entry.templateName }), _jsxs("div", { className: "truncate text-xs text-violet-600", children: ["AI suggestion", entry.reason ? ` · ${entry.reason}` : ""] })] }), renderSuggestionActions(entry)] }) }, entry.id))), _jsx("div", { className: "flex justify-end pt-1", children: _jsxs(Popover, { open: templatePickerOpen, onOpenChange: (nextOpen) => {
|
|
1472
|
+
if (nextOpen && templatePickerTriggerRef.current) {
|
|
1473
|
+
const dialogContent = templatePickerTriggerRef.current.closest('[data-slot="dialog-content"]');
|
|
1474
|
+
setTemplatePickerPortalContainer(dialogContent);
|
|
1475
|
+
}
|
|
1476
|
+
setTemplatePickerOpen(nextOpen);
|
|
1477
|
+
}, enableIframeClickDetection: false, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { ref: templatePickerTriggerRef, variant: "default", size: "iconSm", className: "h-7 w-7 rounded-md shadow-sm", title: "Add inherited template", "aria-label": "Add inherited template", children: _jsx(Plus, { className: "h-4 w-4" }) }) }), _jsxs(PopoverContent, { className: "w-[420px] p-0", side: "top", align: "start", container: templatePickerPortalContainer, onMouseDown: (event) => event.stopPropagation(), onClick: (event) => event.stopPropagation(), children: [_jsxs("div", { className: "border-b border-gray-100 px-3 py-2", children: [_jsx("div", { className: "text-xs font-semibold text-gray-700", children: "Select template" }), _jsx("div", { className: "text-xs text-gray-500", children: "Choose a template to add to inherited templates." })] }), _jsx("div", { className: "h-[320px] overflow-hidden rounded-b-md bg-white", children: _jsx(TreeListSelector, { language: activeLanguage, rootItemIds: [TEMPLATES_ROOT_ID], selectedItemIds: selectedTemplateNodesInTree.map((x) => x.id), onSelectionChange: (nodes) => {
|
|
1478
|
+
const selected = nodes
|
|
1479
|
+
.filter((n) => normalizeGuid(n.templateId) ===
|
|
1480
|
+
normalizeGuid(TEMPLATE_TEMPLATE_ID))
|
|
1481
|
+
.map((n) => ({
|
|
1482
|
+
id: n.id,
|
|
1483
|
+
name: n.name,
|
|
1484
|
+
path: n.path,
|
|
1485
|
+
icon: n.icon,
|
|
1486
|
+
idPath: n.idPath,
|
|
1487
|
+
}));
|
|
1488
|
+
setSelectedTemplateNodesInTree(selected);
|
|
1489
|
+
}, onDoubleClick: (node) => {
|
|
1490
|
+
if (normalizeGuid(node.templateId) === normalizeGuid(TEMPLATE_TEMPLATE_ID)) {
|
|
1491
|
+
addBaseTemplates([
|
|
1492
|
+
{
|
|
1493
|
+
id: node.id,
|
|
1494
|
+
name: node.name,
|
|
1495
|
+
path: node.path,
|
|
1496
|
+
icon: node.icon,
|
|
1497
|
+
idPath: node.idPath,
|
|
1498
|
+
},
|
|
1499
|
+
]);
|
|
1500
|
+
setTemplatePickerOpen(false);
|
|
1501
|
+
}
|
|
1502
|
+
}, onItemSelected: async (node) => {
|
|
1503
|
+
const sel = await editContext.itemsRepository.getItem({
|
|
1504
|
+
id: node.id,
|
|
1505
|
+
language: activeLanguage,
|
|
1506
|
+
version: 0,
|
|
1507
|
+
});
|
|
1508
|
+
if (sel &&
|
|
1509
|
+
normalizeGuid(sel.templateId) === normalizeGuid(TEMPLATE_TEMPLATE_ID)) {
|
|
1510
|
+
addBaseTemplates([
|
|
1511
|
+
{
|
|
1512
|
+
id: sel.id,
|
|
1513
|
+
name: sel.name,
|
|
1514
|
+
path: sel.path,
|
|
1515
|
+
icon: sel.icon,
|
|
1516
|
+
idPath: sel.idPath,
|
|
1517
|
+
},
|
|
1518
|
+
]);
|
|
1519
|
+
setTemplatePickerOpen(false);
|
|
1520
|
+
}
|
|
1521
|
+
} }) })] })] }) })] })] }) }) }), _jsxs("div", { className: "flex flex-col overflow-y-auto bg-white", children: [_jsx("div", { className: "border-b border-gray-100 bg-gray-50/30 px-3 py-2", children: _jsxs("div", { className: "flex items-center gap-2 rounded border border-gray-200 bg-white px-2 py-1.5", children: [_jsx(Search, { className: "h-3.5 w-3.5 text-gray-400" }), _jsx("input", { className: "w-full bg-transparent text-sm text-gray-800 outline-none placeholder:text-gray-400", placeholder: "Search fields or sections...", value: fieldSearchQuery, onChange: (e) => setFieldSearchQuery(e.target.value) })] }) }), _jsxs("div", { className: "flex-1 overflow-y-auto", children: [sections.map((sectionItem, si_idx) => {
|
|
1165
1522
|
const isSectionDragging = draggedSectionIdx === si_idx;
|
|
1166
1523
|
const isSectionDragOver = dragOverSectionIdx === si_idx;
|
|
1167
1524
|
const sectionSuggestions = getSectionSuggestions(sectionItem.localId);
|
|
@@ -1227,7 +1584,7 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
1227
1584
|
toggleSectionCollapsed(si_idx);
|
|
1228
1585
|
}, children: isCollapsed ? (_jsx(ChevronRight, { className: "h-3.5 w-3.5" })) : (_jsx(ChevronDown, { className: "h-3.5 w-3.5" })) }), _jsx("input", { className: `flex-1 rounded-md border bg-white px-3 py-1.5 text-sm font-semibold shadow-sm outline-none placeholder:text-gray-400 focus:border-blue-400 focus:ring-2 focus:ring-blue-100 ${sectionSuggestions.length > 0
|
|
1229
1586
|
? "border-violet-200 text-violet-900"
|
|
1230
|
-
: "border-gray-200 text-gray-900"}`, value: sectionItem.name || "", placeholder: `Section ${si_idx + 1}`, onChange: (e) => updateSectionName(si_idx, e.target.value), onClick: (e) => e.stopPropagation() }), _jsx("span", { className: "text-
|
|
1587
|
+
: "border-gray-200 text-gray-900"}`, value: sectionItem.name || "", placeholder: `Section ${si_idx + 1}`, onChange: (e) => updateSectionName(si_idx, e.target.value), onClick: (e) => e.stopPropagation() }), _jsx("span", { className: "text-xs text-gray-400", children: sectionItem.fields.length }), _jsxs("div", { className: "relative h-5 w-12 shrink-0", children: [_jsx("div", { className: `absolute inset-0 flex items-center justify-center transition-all duration-150 ${sectionDeleteConfirmIdx === si_idx
|
|
1231
1588
|
? "scale-90 opacity-0 pointer-events-none"
|
|
1232
1589
|
: "scale-100 opacity-100"}`, children: _jsx("button", { className: "text-gray-300 transition hover:text-red-500", onClick: (e) => {
|
|
1233
1590
|
e.stopPropagation();
|
|
@@ -1241,7 +1598,7 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
1241
1598
|
}, title: "Confirm section delete", "aria-label": "Confirm section delete", children: _jsx(Check, { className: "h-3.5 w-3.5" }) }), _jsx("button", { className: "text-red-500 transition hover:text-red-600", onClick: (e) => {
|
|
1242
1599
|
e.stopPropagation();
|
|
1243
1600
|
setSectionDeleteConfirmIdx(null);
|
|
1244
|
-
}, title: "Cancel section delete", "aria-label": "Cancel section delete", children: _jsx(X, { className: "h-3.5 w-3.5" }) })] })] })] }), sectionSuggestions.length > 0 && (_jsx("div", { className: "mx-3 mb-2 rounded border border-violet-200 bg-violet-50 px-3 py-2 text-
|
|
1601
|
+
}, title: "Cancel section delete", "aria-label": "Cancel section delete", children: _jsx(X, { className: "h-3.5 w-3.5" }) })] })] })] }), sectionSuggestions.length > 0 && (_jsx("div", { className: "mx-3 mb-2 rounded border border-violet-200 bg-violet-50 px-3 py-2 text-xs text-violet-800", children: sectionSuggestions.map((entry) => (_jsxs("div", { className: "flex items-center justify-between gap-3 py-1", children: [_jsxs("span", { className: "min-w-0 flex-1 truncate", children: [entry.action === "remove"
|
|
1245
1602
|
? "AI suggests removing this section"
|
|
1246
1603
|
: `AI suggests renaming this section to "${entry.section.name}"`, entry.reason ? ` · ${entry.reason}` : ""] }), renderSuggestionActions(entry)] }, entry.id))) })), !isCollapsed && (_jsxs("div", { className: "pb-2", children: [_jsxs("div", { className: "space-y-1 px-3", children: [visibleFields.map(({ fld, fi_idx }) => {
|
|
1247
1604
|
const isSelected = selectedFieldKey?.si === si_idx && selectedFieldKey?.fi === fi_idx;
|
|
@@ -1297,11 +1654,11 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
1297
1654
|
: isSelected
|
|
1298
1655
|
? "text-blue-700"
|
|
1299
1656
|
: "text-gray-700"} ${hasFieldRemovalSuggestion ? "line-through" : ""}`, children: fieldUpdateSuggestion?.field.name ||
|
|
1300
|
-
fld.name || (_jsx("span", { className: "font-normal italic text-gray-400", children: "Unnamed" })) }), _jsxs("div", { className: "text-
|
|
1657
|
+
fld.name || (_jsx("span", { className: "font-normal italic text-gray-400", children: "Unnamed" })) }), _jsxs("div", { className: "text-xs text-gray-500", children: [fld.type, fld.source ? ` · ${fld.source}` : ""] }), fieldSuggestions.map((entry) => (_jsxs("div", { className: "mt-1 flex items-center justify-between gap-2 text-xs text-violet-700", children: [_jsx("span", { className: "min-w-0 flex-1 truncate", children: entry.action === "remove"
|
|
1301
1658
|
? "AI suggests removing this field"
|
|
1302
|
-
: `AI suggests updating this field${entry.reason ? ` · ${entry.reason}` : ""}` }), renderSuggestionActions(entry)] }, entry.id)))] }), _jsxs("div", { className: "flex items-center gap-2", children: [fld.shared && _jsx("span", { className: "rounded border border-blue-100 bg-blue-50 px-1.5 py-0.5 text-
|
|
1303
|
-
}), visibleFields.length === 0 && normalizedFieldSearchQuery ? (_jsx("div", { className: "ml-8 px-3 py-2 text-
|
|
1304
|
-
pendingFieldAdditions.map((entry) => (_jsx("div", { className: "mt-2 mr-3 ml-11 rounded border border-dashed border-violet-300 bg-violet-50 px-3 py-2 text-
|
|
1659
|
+
: `AI suggests updating this field${entry.reason ? ` · ${entry.reason}` : ""}` }), renderSuggestionActions(entry)] }, entry.id)))] }), _jsxs("div", { className: "flex items-center gap-2", children: [fld.shared && _jsx("span", { className: "rounded border border-blue-100 bg-blue-50 px-1.5 py-0.5 text-xs font-bold text-blue-600", children: "S" }), fld.unversioned && _jsx("span", { className: "rounded border border-amber-100 bg-amber-50 px-1.5 py-0.5 text-xs font-bold text-amber-600", children: "U" })] }), _jsx("button", { className: "text-gray-300 transition hover:text-red-500", onClick: (e) => { e.stopPropagation(); removeField(si_idx, fi_idx); }, children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }, fld.localId));
|
|
1660
|
+
}), visibleFields.length === 0 && normalizedFieldSearchQuery ? (_jsx("div", { className: "ml-8 px-3 py-2 text-xs text-gray-400 italic", children: "No matching fields" })) : null] }), !isSearching &&
|
|
1661
|
+
pendingFieldAdditions.map((entry) => (_jsx("div", { className: "mt-2 mr-3 ml-11 rounded border border-dashed border-violet-300 bg-violet-50 px-3 py-2 text-xs text-violet-800", children: _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "truncate font-semibold", children: entry.field.name || "Suggested field" }), _jsxs("div", { className: "truncate text-xs text-violet-600", children: [entry.field.type, entry.reason ? ` · ${entry.reason}` : ""] })] }), renderSuggestionActions(entry)] }) }, entry.id))), !isSearching && draggedFieldRef ? (_jsx("div", { className: `mt-2 mr-3 ml-11 rounded border border-dashed px-2 py-1.5 text-xs transition ${dragOverFieldEndSectionIdx === si_idx
|
|
1305
1662
|
? "border-blue-300 bg-blue-50 text-blue-600"
|
|
1306
1663
|
: "border-gray-200 bg-white text-gray-400"}`, onDragOver: (e) => {
|
|
1307
1664
|
e.preventDefault();
|
|
@@ -1313,10 +1670,10 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
1313
1670
|
setDraggedFieldRef(null);
|
|
1314
1671
|
setDragOverFieldRef(null);
|
|
1315
1672
|
setDragOverFieldEndSectionIdx(null);
|
|
1316
|
-
}, children: "Drop field here to place at end" })) : !isSearching ? (_jsxs("div", { className: "mt-2 mr-3 ml-11 flex items-center gap-2", children: [_jsx("input", { ref: (el) => { newFieldNameInputRefs.current[si_idx] = el; }, className: "flex-1 rounded border border-gray-200 bg-white px-2 py-1.5 text-
|
|
1317
|
-
appendFieldToSection(si_idx); } }), _jsx("button", { className: "rounded bg-
|
|
1673
|
+
}, children: "Drop field here to place at end" })) : !isSearching ? (_jsxs("div", { className: "mt-2 mr-3 ml-11 flex items-center gap-2", children: [_jsx("input", { ref: (el) => { newFieldNameInputRefs.current[si_idx] = el; }, className: "flex-1 rounded border border-gray-200 bg-white px-2 py-1.5 text-sm text-gray-800 outline-none placeholder:text-gray-400 focus:border-blue-400", placeholder: "Add field...", value: getFieldDraft(si_idx).name, onChange: (e) => updateFieldDraft(si_idx, { name: e.target.value }), onKeyDown: (e) => { if (e.key === "Enter")
|
|
1674
|
+
appendFieldToSection(si_idx); } }), _jsx("button", { className: "rounded bg-theme-secondary p-1.5 text-white transition hover:bg-theme-secondary/90 shadow-sm", onClick: () => appendFieldToSection(si_idx), children: _jsx(Plus, { className: "h-3.5 w-3.5" }) })] })) : null] }))] }, sectionItem.localId));
|
|
1318
1675
|
}), !isSearching &&
|
|
1319
|
-
pendingSectionAdditions.map((entry) => (_jsx("div", { className: "mx-3 mt-2 rounded border border-dashed border-violet-300 bg-violet-50 px-3 py-3 text-violet-900", children: _jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "truncate text-sm font-semibold", children: entry.section.name || "Suggested section" }), _jsxs("div", { className: "text-
|
|
1676
|
+
pendingSectionAdditions.map((entry) => (_jsx("div", { className: "mx-3 mt-2 rounded border border-dashed border-violet-300 bg-violet-50 px-3 py-3 text-violet-900", children: _jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "truncate text-sm font-semibold", children: entry.section.name || "Suggested section" }), _jsxs("div", { className: "text-xs text-violet-700", children: ["AI section suggestion", entry.reason ? ` · ${entry.reason}` : ""] }), entry.section.fields.length > 0 && (_jsx("div", { className: "mt-2 space-y-1", children: entry.section.fields.map((field) => (_jsxs("div", { className: "rounded border border-violet-200 bg-white/70 px-2 py-1 text-xs text-violet-700", children: [field.name || "Suggested field", " \u00B7 ", field.type] }, field.localId))) }))] }), renderSuggestionActions(entry)] }) }, entry.id))), !isSearching && draggedSectionIdx !== null ? (_jsx("div", { className: `mx-3 mt-2 mb-3 rounded border border-dashed px-2 py-1.5 text-xs transition ${isDragOverSectionEnd
|
|
1320
1677
|
? "border-blue-300 bg-blue-50 text-blue-600"
|
|
1321
1678
|
: "border-gray-200 bg-white text-gray-400"}`, onDragOver: (e) => {
|
|
1322
1679
|
e.preventDefault();
|
|
@@ -1330,7 +1687,7 @@ export function TemplateStructureInlineEditor({ item, parentItem, initialName, i
|
|
|
1330
1687
|
setDraggedSectionIdx(null);
|
|
1331
1688
|
setDragOverSectionIdx(null);
|
|
1332
1689
|
setIsDragOverSectionEnd(false);
|
|
1333
|
-
}, children: "Drop section here to place at end" })) : !isSearching ? (_jsxs("div", { className: "mx-3 mt-2 mb-3 flex items-center gap-2", children: [_jsx("input", { className: "flex-1 rounded border border-gray-200 bg-white px-2 py-1 text-
|
|
1334
|
-
appendSection(); } }), _jsx("button", { className: "rounded bg-
|
|
1690
|
+
}, children: "Drop section here to place at end" })) : !isSearching ? (_jsxs("div", { className: "mx-3 mt-2 mb-3 flex items-center gap-2", children: [_jsx("input", { className: "flex-1 rounded border border-gray-200 bg-white px-2 py-1 text-sm text-gray-800 outline-none placeholder:text-gray-400 focus:border-blue-400", placeholder: "New section...", value: newSectionName, onChange: (e) => setNewSectionName(e.target.value), onKeyDown: (e) => { if (e.key === "Enter")
|
|
1691
|
+
appendSection(); } }), _jsx("button", { className: "rounded bg-theme-secondary p-1 text-white transition hover:bg-theme-secondary/90 shadow-sm", onClick: appendSection, children: _jsx(Plus, { className: "h-3.5 w-3.5" }) })] })) : null] })] }), _jsxs("div", { className: "flex flex-col overflow-y-auto border-l border-gray-100 bg-gray-50/20", children: [_jsx("div", { className: "border-b border-gray-100 px-4 py-2 bg-gray-50/30", children: _jsxs("div", { className: "flex items-center gap-2 text-xs font-bold text-gray-500 uppercase tracking-wider", children: [_jsx(Settings2, { className: "h-3.5 w-3.5" }), " Properties"] }) }), field && section && si !== undefined && fi !== undefined ? (_jsxs("div", { className: "space-y-4 p-4", children: [_jsxs("div", { children: [_jsx("div", { className: "mb-1 text-xs font-bold uppercase tracking-wider text-gray-400", children: "Name" }), _jsx("input", { className: cls.input, value: field.name, onChange: (e) => updateField(si, fi, { name: e.target.value }) })] }), _jsxs("div", { children: [_jsx("div", { className: "mb-1 text-xs font-bold uppercase tracking-wider text-gray-400", children: "Type" }), renderTypeSelect(field.type, (v) => updateField(si, fi, { type: v }))] }), _jsxs("div", { children: [_jsx("div", { className: "mb-1 text-xs font-bold uppercase tracking-wider text-gray-400", children: "Source" }), _jsx("input", { className: cls.input, placeholder: "Optional", value: field.source, onChange: (e) => updateField(si, fi, { source: e.target.value }) })] }), _jsxs("div", { children: [_jsx("div", { className: "mb-1 text-xs font-bold uppercase tracking-wider text-gray-400", children: "Short description" }), _jsx("input", { className: cls.input, placeholder: "Optional", value: field.shortDescription, onChange: (e) => updateField(si, fi, { shortDescription: e.target.value }) })] }), _jsxs("div", { children: [_jsx("div", { className: "mb-1 text-xs font-bold uppercase tracking-wider text-gray-400", children: "Default value" }), _jsx("input", { className: cls.input, placeholder: "Optional", value: field.defaultValue, onChange: (e) => updateField(si, fi, { defaultValue: e.target.value }) })] }), _jsxs("div", { className: "space-y-3 border-t border-gray-100 pt-3", children: [_jsxs("label", { className: "flex items-center justify-between text-xs text-gray-600 font-medium", children: [_jsx("span", { children: "Shared" }), renderToggle(field.shared, () => updateField(si, fi, { shared: !field.shared }))] }), _jsxs("label", { className: "flex items-center justify-between text-xs text-gray-600 font-medium", children: [_jsx("span", { children: "Unversioned" }), renderToggle(field.unversioned, () => updateField(si, fi, { unversioned: !field.unversioned }))] })] }), _jsxs("button", { className: "mt-2 flex w-full items-center justify-center gap-1.5 rounded-md border border-red-200 bg-red-50 py-2 text-xs font-semibold text-red-600 transition hover:bg-red-100", onClick: () => { removeField(si, fi); setSelectedFieldKey(null); }, children: [_jsx(Trash2, { className: "h-3.5 w-3.5" }), " Remove field"] })] })) : (_jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-2 p-6 text-gray-300", children: [_jsx(Settings2, { className: "h-8 w-8" }), _jsx("p", { className: "text-xs font-medium", children: "Select a field to inspect" })] }))] }), _jsxs("div", { className: "flex min-h-0 flex-col border-l border-gray-100 bg-white", children: [_jsx("div", { className: "border-b border-gray-100 bg-gray-50/30 px-4 py-2", children: _jsxs("div", { className: "flex items-center gap-2 text-xs font-bold uppercase tracking-wider text-gray-500", children: [_jsx(Bot, { className: "h-3.5 w-3.5" }), " Template Builder AI"] }) }), _jsxs("div", { className: "border-b border-gray-100 px-4 py-3", children: [latestSuggestionSummary && (_jsxs("div", { className: "rounded border border-violet-200 bg-violet-50 px-3 py-2 text-xs text-violet-800", children: [_jsx("span", { className: "font-semibold", children: "Latest suggestion:" }), " ", latestSuggestionSummary] })), suggestionStatus && (_jsx("div", { className: "mt-2 text-xs text-gray-500", children: suggestionStatus })), suggestionParseError && (_jsxs("div", { className: "mt-2 rounded border border-red-200 bg-red-50 px-3 py-2 text-xs text-red-700", children: ["Failed to parse AI response: ", suggestionParseError] })), pendingSuggestions.length > 0 && (_jsxs("div", { className: "mt-2 text-xs text-gray-600", children: [pendingSuggestions.length, " pending suggestion", pendingSuggestions.length === 1 ? "" : "s", " highlighted in purple."] }))] }), _jsx("div", { className: "min-h-0 flex-1", children: _jsx(AgentTerminal, { agentStub: templateBuilderAgent, profiles: effectiveAiProfiles, compact: true, simpleMode: true, hideContext: true, defaultCollapseJson: true, initialMetadata: templateBuilderAgentMetadata, onMessage: handleAgentMessage, className: "h-full" }) })] })] }), renderFooter()] }));
|
|
1335
1692
|
}
|
|
1336
1693
|
//# sourceMappingURL=TemplateStructureInlineEditor.js.map
|