@alpaca-editor/core 1.0.4093 → 1.0.4094
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ui/input.js +1 -1
- package/dist/components/ui/popover.d.ts +3 -1
- package/dist/components/ui/popover.js +1 -1
- package/dist/components/ui/popover.js.map +1 -1
- package/dist/components/ui/select.js +5 -3
- package/dist/components/ui/select.js.map +1 -1
- package/dist/config/config.js +1 -9
- package/dist/config/config.js.map +1 -1
- package/dist/editor/Editor.js +1 -1
- package/dist/editor/Editor.js.map +1 -1
- package/dist/editor/FieldList.js +1 -1
- package/dist/editor/FieldList.js.map +1 -1
- package/dist/editor/FieldListField.js +9 -3
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/ItemInfo.js +1 -1
- package/dist/editor/ItemInfo.js.map +1 -1
- package/dist/editor/LinkEditorDialog.js +6 -6
- package/dist/editor/LinkEditorDialog.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.js +32 -3
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.js +2 -2
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/control-center/setup-steps/AiSetupStep.js +251 -7
- package/dist/editor/control-center/setup-steps/AiSetupStep.js.map +1 -1
- package/dist/editor/field-types/DateFieldEditor.js +1 -1
- package/dist/editor/field-types/DateFieldEditor.js.map +1 -1
- package/dist/editor/field-types/DateTimeFieldEditor.js +1 -1
- package/dist/editor/field-types/DateTimeFieldEditor.js.map +1 -1
- package/dist/editor/field-types/DropLinkEditor.js +1 -1
- package/dist/editor/field-types/DropLinkEditor.js.map +1 -1
- package/dist/editor/field-types/DropListEditor.js +1 -1
- package/dist/editor/field-types/DropListEditor.js.map +1 -1
- package/dist/editor/field-types/InternalLinkFieldEditor.js +1 -1
- package/dist/editor/field-types/InternalLinkFieldEditor.js.map +1 -1
- package/dist/editor/field-types/LinkFieldEditor.js +2 -1
- package/dist/editor/field-types/LinkFieldEditor.js.map +1 -1
- package/dist/editor/field-types/TreeListEditor.js +75 -70
- package/dist/editor/field-types/TreeListEditor.js.map +1 -1
- package/dist/editor/menubar/PageSelector.js +2 -1
- package/dist/editor/menubar/PageSelector.js.map +1 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js +2 -2
- package/dist/editor/page-editor-chrome/InlineEditor.js +8 -0
- package/dist/editor/page-editor-chrome/InlineEditor.js.map +1 -1
- package/dist/editor/page-editor-chrome/PictureEditorOverlay.js +8 -0
- package/dist/editor/page-editor-chrome/PictureEditorOverlay.js.map +1 -1
- package/dist/editor/page-viewer/DeviceToolbar.js +2 -1
- package/dist/editor/page-viewer/DeviceToolbar.js.map +1 -1
- package/dist/editor/page-viewer/EditorForm.js +1 -5
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +17 -0
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/pageModel.d.ts +1 -0
- package/dist/editor/services/aiService.d.ts +1 -0
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/ui/Splitter.d.ts +1 -0
- package/dist/editor/ui/Splitter.js +9 -4
- package/dist/editor/ui/Splitter.js.map +1 -1
- package/dist/page-wizard/steps/ComponentTypesSelector.js +1 -1
- package/dist/page-wizard/steps/ComponentTypesSelector.js.map +1 -1
- package/dist/page-wizard/steps/ContentStep.js +81 -69
- package/dist/page-wizard/steps/ContentStep.js.map +1 -1
- package/dist/page-wizard/utils/dataAccessor.js +11 -5
- package/dist/page-wizard/utils/dataAccessor.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/styles.css +12 -3
- package/package.json +1 -1
- package/src/components/ui/input.tsx +1 -1
- package/src/components/ui/popover.tsx +22 -15
- package/src/components/ui/select.tsx +44 -29
- package/src/config/config.tsx +0 -12
- package/src/editor/Editor.tsx +1 -1
- package/src/editor/FieldList.tsx +4 -1
- package/src/editor/FieldListField.tsx +10 -4
- package/src/editor/ItemInfo.tsx +1 -1
- package/src/editor/LinkEditorDialog.tsx +25 -19
- package/src/editor/ai/AgentTerminal.tsx +58 -28
- package/src/editor/ai/AiResponseMessage.tsx +4 -4
- package/src/editor/control-center/setup-steps/AiSetupStep.tsx +362 -40
- package/src/editor/field-types/DateFieldEditor.tsx +2 -1
- package/src/editor/field-types/DateTimeFieldEditor.tsx +1 -1
- package/src/editor/field-types/DropLinkEditor.tsx +8 -6
- package/src/editor/field-types/DropListEditor.tsx +11 -9
- package/src/editor/field-types/InternalLinkFieldEditor.tsx +1 -1
- package/src/editor/field-types/LinkFieldEditor.tsx +2 -1
- package/src/editor/field-types/TreeListEditor.tsx +178 -178
- package/src/editor/menubar/PageSelector.tsx +3 -3
- package/src/editor/page-editor-chrome/FrameMenu.tsx +1 -1
- package/src/editor/page-editor-chrome/InlineEditor.tsx +10 -0
- package/src/editor/page-editor-chrome/PictureEditorOverlay.tsx +11 -0
- package/src/editor/page-viewer/DeviceToolbar.tsx +9 -2
- package/src/editor/page-viewer/EditorForm.tsx +1 -6
- package/src/editor/page-viewer/PageViewerFrame.tsx +28 -0
- package/src/editor/pageModel.ts +1 -0
- package/src/editor/services/aiService.ts +2 -0
- package/src/editor/ui/Splitter.tsx +26 -2
- package/src/page-wizard/steps/ComponentTypesSelector.tsx +1 -1
- package/src/page-wizard/steps/ContentStep.tsx +129 -106
- package/src/page-wizard/utils/dataAccessor.ts +13 -5
- package/src/revision.ts +2 -2
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Splitter,
|
|
3
|
+
SplitterPanel as InternalSplitterPanel,
|
|
4
|
+
} from "../ui/Splitter";
|
|
2
5
|
import { useEditContext } from "../client/editContext";
|
|
3
6
|
import { useEffect, useState } from "react";
|
|
4
7
|
|
|
@@ -8,6 +11,7 @@ import { SimpleIconButton } from "../ui/SimpleIconButton";
|
|
|
8
11
|
import { normalizeGuid } from "../utils";
|
|
9
12
|
import ItemSearch from "../ui/ItemSearch";
|
|
10
13
|
import { Button } from "../../components/ui/button";
|
|
14
|
+
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
11
15
|
import {
|
|
12
16
|
Tooltip,
|
|
13
17
|
TooltipContent,
|
|
@@ -193,9 +197,154 @@ export default function TreeListEditor({
|
|
|
193
197
|
setDraggedIndex(null);
|
|
194
198
|
};
|
|
195
199
|
|
|
200
|
+
const panels: InternalSplitterPanel[] = [
|
|
201
|
+
{
|
|
202
|
+
name: "tree",
|
|
203
|
+
defaultSize: "auto",
|
|
204
|
+
className: "relative",
|
|
205
|
+
content: (
|
|
206
|
+
<div
|
|
207
|
+
className={`absolute inset-0 overflow-auto ${readOnly ? "bg-gray-5" : ""}`}
|
|
208
|
+
>
|
|
209
|
+
<ContentTree
|
|
210
|
+
expandIdPath={hoveredItem?.idPath}
|
|
211
|
+
language={editContext.currentItemDescriptor?.language ?? "en"}
|
|
212
|
+
onDoubleClick={(node) => {
|
|
213
|
+
if (!readOnly) addToList([node]);
|
|
214
|
+
}}
|
|
215
|
+
selectionMode="multiple"
|
|
216
|
+
selectedItemIds={
|
|
217
|
+
selectedItemNodesInTree.length
|
|
218
|
+
? selectedItemNodesInTree.map((x) => x.id)
|
|
219
|
+
: hoveredItemId
|
|
220
|
+
? [hoveredItemId]
|
|
221
|
+
: []
|
|
222
|
+
}
|
|
223
|
+
onSelectionChange={(e) => {
|
|
224
|
+
setSelectedItemNodesInTree(e as ItemData[]);
|
|
225
|
+
}}
|
|
226
|
+
rootItemIds={rootItemIds}
|
|
227
|
+
includeItemPath={true}
|
|
228
|
+
/>
|
|
229
|
+
</div>
|
|
230
|
+
),
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: "selectedList",
|
|
234
|
+
defaultSize: "auto",
|
|
235
|
+
className: "relative",
|
|
236
|
+
content: (
|
|
237
|
+
<div className="relative h-full w-full">
|
|
238
|
+
<div
|
|
239
|
+
className={`absolute inset-0 overflow-auto text-xs ${readOnly ? "bg-gray-5" : "bg-white"}`}
|
|
240
|
+
>
|
|
241
|
+
{values.length === 0 ? (
|
|
242
|
+
<div className="p-3 text-center text-gray-500">None selected</div>
|
|
243
|
+
) : (
|
|
244
|
+
values.map((option, index) => (
|
|
245
|
+
<Tooltip key={`${option.id}-${index}`} delayDuration={1000}>
|
|
246
|
+
<TooltipTrigger asChild>
|
|
247
|
+
<div
|
|
248
|
+
draggable={!readOnly}
|
|
249
|
+
onDragStart={(e) => handleDragStart(e, index)}
|
|
250
|
+
onDragOver={handleDragOver}
|
|
251
|
+
onDrop={(e) => handleDrop(e, index)}
|
|
252
|
+
onDragEnd={handleDragEnd}
|
|
253
|
+
className={`group relative flex cursor-pointer items-center gap-1.5 border-b border-gray-100 p-1 select-none last:border-b-0 hover:bg-gray-50 ${
|
|
254
|
+
selectedFromList.includes(option) ? "bg-blue-100" : ""
|
|
255
|
+
} ${draggedIndex === index ? "opacity-50" : ""} ${
|
|
256
|
+
!readOnly ? "cursor-move" : ""
|
|
257
|
+
}`}
|
|
258
|
+
onClick={(e) => {
|
|
259
|
+
if (e.shiftKey && lastClickedIndex !== null) {
|
|
260
|
+
const startIndex = Math.min(lastClickedIndex, index);
|
|
261
|
+
const endIndex = Math.max(lastClickedIndex, index);
|
|
262
|
+
const rangeItems = values.slice(
|
|
263
|
+
startIndex,
|
|
264
|
+
endIndex + 1,
|
|
265
|
+
);
|
|
266
|
+
setSelectedFromList(rangeItems);
|
|
267
|
+
} else if (e.ctrlKey) {
|
|
268
|
+
if (selectedFromList.includes(option)) {
|
|
269
|
+
setSelectedFromList(
|
|
270
|
+
selectedFromList.filter(
|
|
271
|
+
(item) => item !== option,
|
|
272
|
+
),
|
|
273
|
+
);
|
|
274
|
+
} else {
|
|
275
|
+
setSelectedFromList([...selectedFromList, option]);
|
|
276
|
+
}
|
|
277
|
+
setLastClickedIndex(index);
|
|
278
|
+
} else {
|
|
279
|
+
if (
|
|
280
|
+
selectedFromList.length === 1 &&
|
|
281
|
+
selectedFromList.includes(option)
|
|
282
|
+
) {
|
|
283
|
+
setSelectedFromList([]);
|
|
284
|
+
setLastClickedIndex(null);
|
|
285
|
+
} else {
|
|
286
|
+
setSelectedFromList([option]);
|
|
287
|
+
setLastClickedIndex(index);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}}
|
|
291
|
+
onMouseEnter={async () => {
|
|
292
|
+
setHoveredItemId(option.id);
|
|
293
|
+
setSelectedItemNodesInTree([
|
|
294
|
+
{
|
|
295
|
+
id: option.id,
|
|
296
|
+
path: option.path,
|
|
297
|
+
name: option.name,
|
|
298
|
+
icon: option.icon,
|
|
299
|
+
idPath: option.idPath || "",
|
|
300
|
+
},
|
|
301
|
+
]);
|
|
302
|
+
}}
|
|
303
|
+
onDoubleClick={() => {
|
|
304
|
+
if (!readOnly) removeFromList([option]);
|
|
305
|
+
}}
|
|
306
|
+
>
|
|
307
|
+
<img src={option.icon} className="h-4 w-4" />
|
|
308
|
+
{trimPath(option.path)}{" "}
|
|
309
|
+
{activeOpenButtonId === option.id ? (
|
|
310
|
+
<Button
|
|
311
|
+
className="h-4 p-1"
|
|
312
|
+
size="sm"
|
|
313
|
+
onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
|
|
314
|
+
e.stopPropagation();
|
|
315
|
+
editContext.loadItem(option.id);
|
|
316
|
+
setActiveOpenButtonId("");
|
|
317
|
+
}}
|
|
318
|
+
>
|
|
319
|
+
Open
|
|
320
|
+
</Button>
|
|
321
|
+
) : (
|
|
322
|
+
<button
|
|
323
|
+
className="cursor-pointer opacity-0 transition-opacity group-hover:opacity-100"
|
|
324
|
+
onClick={(e) => {
|
|
325
|
+
e.stopPropagation();
|
|
326
|
+
setActiveOpenButtonId(option.id);
|
|
327
|
+
}}
|
|
328
|
+
title="Open"
|
|
329
|
+
>
|
|
330
|
+
<i className="pi pi-arrow-up-right text-xs text-gray-500" />
|
|
331
|
+
</button>
|
|
332
|
+
)}
|
|
333
|
+
</div>
|
|
334
|
+
</TooltipTrigger>
|
|
335
|
+
<TooltipContent>{option.path}</TooltipContent>
|
|
336
|
+
</Tooltip>
|
|
337
|
+
))
|
|
338
|
+
)}
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
341
|
+
),
|
|
342
|
+
},
|
|
343
|
+
];
|
|
344
|
+
|
|
196
345
|
return (
|
|
197
346
|
<div
|
|
198
|
-
className={`focus-shadow border border-gray-200 ${readOnly ? "bg-gray-5" : ""}`}
|
|
347
|
+
className={`focus-shadow rounded-sm border border-gray-200 ${readOnly ? "bg-gray-5" : ""}`}
|
|
199
348
|
>
|
|
200
349
|
<div
|
|
201
350
|
className={`border-b border-gray-200 p-2 ${readOnly ? "bg-gray-5" : "bg-gray-5"}`}
|
|
@@ -216,183 +365,34 @@ export default function TreeListEditor({
|
|
|
216
365
|
}}
|
|
217
366
|
/>
|
|
218
367
|
</div>
|
|
219
|
-
<Splitter
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
<
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
/>
|
|
244
|
-
</div>
|
|
245
|
-
</SplitterPanel>
|
|
246
|
-
<SplitterPanel>
|
|
247
|
-
<div className="relative h-full w-full">
|
|
248
|
-
<div className="absolute inset-0 flex">
|
|
249
|
-
<div
|
|
250
|
-
className={`flex flex-col justify-center gap-1 p-1 ${readOnly ? "bg-gray-5" : "bg-gray-100"}`}
|
|
251
|
-
>
|
|
252
|
-
<SimpleIconButton
|
|
253
|
-
label="Add"
|
|
254
|
-
icon="pi pi-angle-right"
|
|
255
|
-
onClick={() => addToList(selectedItemNodesInTree)}
|
|
256
|
-
disabled={readOnly}
|
|
257
|
-
/>
|
|
258
|
-
<SimpleIconButton
|
|
259
|
-
label="Remove"
|
|
260
|
-
icon="pi pi-angle-left"
|
|
261
|
-
onClick={() => removeFromList(selectedFromList)}
|
|
262
|
-
disabled={readOnly}
|
|
263
|
-
/>
|
|
264
|
-
</div>
|
|
265
|
-
<div className="relative h-full flex-1">
|
|
266
|
-
<div
|
|
267
|
-
className={`absolute inset-0 overflow-auto text-xs ${readOnly ? "bg-gray-5" : "bg-white"}`}
|
|
268
|
-
>
|
|
269
|
-
{values.length === 0 ? (
|
|
270
|
-
<div className="p-3 text-center text-gray-500">
|
|
271
|
-
None selected
|
|
272
|
-
</div>
|
|
273
|
-
) : (
|
|
274
|
-
values.map((option, index) => (
|
|
275
|
-
<Tooltip
|
|
276
|
-
key={`${option.id}-${index}`}
|
|
277
|
-
delayDuration={1000}
|
|
278
|
-
>
|
|
279
|
-
<TooltipTrigger asChild>
|
|
280
|
-
<div
|
|
281
|
-
draggable={!readOnly}
|
|
282
|
-
onDragStart={(e) => handleDragStart(e, index)}
|
|
283
|
-
onDragOver={handleDragOver}
|
|
284
|
-
onDrop={(e) => handleDrop(e, index)}
|
|
285
|
-
onDragEnd={handleDragEnd}
|
|
286
|
-
className={`group relative flex cursor-pointer items-center gap-1.5 border-b border-gray-100 p-1 select-none last:border-b-0 hover:bg-gray-50 ${
|
|
287
|
-
selectedFromList.includes(option)
|
|
288
|
-
? "bg-blue-100"
|
|
289
|
-
: ""
|
|
290
|
-
} ${draggedIndex === index ? "opacity-50" : ""} ${
|
|
291
|
-
!readOnly ? "cursor-move" : ""
|
|
292
|
-
}`}
|
|
293
|
-
onClick={(e) => {
|
|
294
|
-
if (e.shiftKey && lastClickedIndex !== null) {
|
|
295
|
-
// Range selection mode when Shift is pressed
|
|
296
|
-
const startIndex = Math.min(
|
|
297
|
-
lastClickedIndex,
|
|
298
|
-
index,
|
|
299
|
-
);
|
|
300
|
-
const endIndex = Math.max(
|
|
301
|
-
lastClickedIndex,
|
|
302
|
-
index,
|
|
303
|
-
);
|
|
304
|
-
const rangeItems = values.slice(
|
|
305
|
-
startIndex,
|
|
306
|
-
endIndex + 1,
|
|
307
|
-
);
|
|
308
|
-
setSelectedFromList(rangeItems);
|
|
309
|
-
} else if (e.ctrlKey) {
|
|
310
|
-
// Multi-select mode when Ctrl is pressed
|
|
311
|
-
if (selectedFromList.includes(option)) {
|
|
312
|
-
setSelectedFromList(
|
|
313
|
-
selectedFromList.filter(
|
|
314
|
-
(item) => item !== option,
|
|
315
|
-
),
|
|
316
|
-
);
|
|
317
|
-
} else {
|
|
318
|
-
setSelectedFromList([
|
|
319
|
-
...selectedFromList,
|
|
320
|
-
option,
|
|
321
|
-
]);
|
|
322
|
-
}
|
|
323
|
-
setLastClickedIndex(index);
|
|
324
|
-
} else {
|
|
325
|
-
// Single-select mode when Ctrl is not pressed
|
|
326
|
-
if (
|
|
327
|
-
selectedFromList.length === 1 &&
|
|
328
|
-
selectedFromList.includes(option)
|
|
329
|
-
) {
|
|
330
|
-
// Deselect if this is the only selected item
|
|
331
|
-
setSelectedFromList([]);
|
|
332
|
-
setLastClickedIndex(null);
|
|
333
|
-
} else {
|
|
334
|
-
// Replace selection with just this item
|
|
335
|
-
setSelectedFromList([option]);
|
|
336
|
-
setLastClickedIndex(index);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}}
|
|
340
|
-
onMouseEnter={async () => {
|
|
341
|
-
setHoveredItemId(option.id);
|
|
342
|
-
setSelectedItemNodesInTree([
|
|
343
|
-
{
|
|
344
|
-
id: option.id,
|
|
345
|
-
path: option.path,
|
|
346
|
-
name: option.name,
|
|
347
|
-
icon: option.icon,
|
|
348
|
-
idPath: option.idPath || "",
|
|
349
|
-
},
|
|
350
|
-
]);
|
|
351
|
-
}}
|
|
352
|
-
onDoubleClick={() => {
|
|
353
|
-
if (!readOnly) removeFromList([option]);
|
|
354
|
-
}}
|
|
355
|
-
>
|
|
356
|
-
<img src={option.icon} className="h-4 w-4" />
|
|
357
|
-
{trimPath(option.path)}{" "}
|
|
358
|
-
{activeOpenButtonId === option.id ? (
|
|
359
|
-
<Button
|
|
360
|
-
className="h-4 p-1"
|
|
361
|
-
size="sm"
|
|
362
|
-
onClick={(
|
|
363
|
-
e: React.MouseEvent<HTMLButtonElement>,
|
|
364
|
-
) => {
|
|
365
|
-
e.stopPropagation();
|
|
366
|
-
editContext.loadItem(option.id);
|
|
367
|
-
setActiveOpenButtonId("");
|
|
368
|
-
}}
|
|
369
|
-
>
|
|
370
|
-
Open
|
|
371
|
-
</Button>
|
|
372
|
-
) : (
|
|
373
|
-
<button
|
|
374
|
-
className="cursor-pointer opacity-0 transition-opacity group-hover:opacity-100"
|
|
375
|
-
onClick={(e) => {
|
|
376
|
-
e.stopPropagation();
|
|
377
|
-
setActiveOpenButtonId(option.id);
|
|
378
|
-
}}
|
|
379
|
-
title="Open"
|
|
380
|
-
>
|
|
381
|
-
<i className="pi pi-arrow-up-right text-xs text-gray-500" />
|
|
382
|
-
</button>
|
|
383
|
-
)}
|
|
384
|
-
</div>
|
|
385
|
-
</TooltipTrigger>
|
|
386
|
-
<TooltipContent>{option.path}</TooltipContent>
|
|
387
|
-
</Tooltip>
|
|
388
|
-
))
|
|
389
|
-
)}
|
|
390
|
-
</div>
|
|
391
|
-
</div>
|
|
368
|
+
<Splitter
|
|
369
|
+
className="h-60"
|
|
370
|
+
panels={panels}
|
|
371
|
+
handleContent={(index) =>
|
|
372
|
+
index === 0 ? (
|
|
373
|
+
<div
|
|
374
|
+
className={`flex flex-col items-center gap-1 rounded bg-gray-100 py-1 ${readOnly ? "opacity-50" : ""}`}
|
|
375
|
+
>
|
|
376
|
+
<SimpleIconButton
|
|
377
|
+
label="Add"
|
|
378
|
+
icon={<ChevronRight size={14} strokeWidth={1} />}
|
|
379
|
+
onClick={() => addToList(selectedItemNodesInTree)}
|
|
380
|
+
disabled={readOnly}
|
|
381
|
+
showTooltip={false}
|
|
382
|
+
className="px-0"
|
|
383
|
+
/>
|
|
384
|
+
<SimpleIconButton
|
|
385
|
+
label="Remove"
|
|
386
|
+
icon={<ChevronLeft size={14} strokeWidth={1} />}
|
|
387
|
+
onClick={() => removeFromList(selectedFromList)}
|
|
388
|
+
disabled={readOnly}
|
|
389
|
+
showTooltip={false}
|
|
390
|
+
className="px-0"
|
|
391
|
+
/>
|
|
392
392
|
</div>
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
393
|
+
) : null
|
|
394
|
+
}
|
|
395
|
+
/>
|
|
396
396
|
</div>
|
|
397
397
|
);
|
|
398
398
|
}
|
|
@@ -13,7 +13,7 @@ import { ItemTreeNodeData } from "../services/contentService";
|
|
|
13
13
|
import { SimpleIconButton } from "../ui/SimpleIconButton";
|
|
14
14
|
import { executeSearch } from "../services/searchService";
|
|
15
15
|
import { ItemList } from "../ui/ItemList";
|
|
16
|
-
import {
|
|
16
|
+
import { History, FolderTree } from "lucide-react";
|
|
17
17
|
import {
|
|
18
18
|
Popover,
|
|
19
19
|
PopoverContent,
|
|
@@ -211,14 +211,14 @@ export function PageSelector({
|
|
|
211
211
|
</div>
|
|
212
212
|
<div className="flex gap-1">
|
|
213
213
|
<SimpleIconButton
|
|
214
|
-
icon="
|
|
214
|
+
icon={<FolderTree className="h-4 w-4" strokeWidth={1} />}
|
|
215
215
|
label="Search Content"
|
|
216
216
|
selected={!showHistory}
|
|
217
217
|
onClick={() => setShowHistory(false)}
|
|
218
218
|
size="small"
|
|
219
219
|
/>
|
|
220
220
|
<SimpleIconButton
|
|
221
|
-
icon="
|
|
221
|
+
icon={<History className="h-4 w-4" strokeWidth={1} />}
|
|
222
222
|
label="Filter History"
|
|
223
223
|
selected={showHistory}
|
|
224
224
|
onClick={() => setShowHistory(true)}
|
|
@@ -241,8 +241,8 @@ export function FrameMenu({
|
|
|
241
241
|
function getColor() {
|
|
242
242
|
if (isReadonly) return "readonly";
|
|
243
243
|
if (editContext?.mode === "suggestions") return "suggestions";
|
|
244
|
-
if (isShared) return "shared";
|
|
245
244
|
if (isLayout) return "layout";
|
|
245
|
+
if (isShared) return "shared";
|
|
246
246
|
if (component.canBeMoved) return "default";
|
|
247
247
|
return "nonMovable";
|
|
248
248
|
}
|
|
@@ -12,6 +12,7 @@ import { createPatch } from "diff";
|
|
|
12
12
|
// import { useDebouncedCallback } from "use-debounce";
|
|
13
13
|
// import { executePrompt } from "../services/aiService";
|
|
14
14
|
import { useInlineAiCompletion } from "./useInlineAICompletion";
|
|
15
|
+
import { getComponentById } from "../componentTreeHelper";
|
|
15
16
|
|
|
16
17
|
export function InlineEditor({
|
|
17
18
|
pageViewContext,
|
|
@@ -112,6 +113,15 @@ export function InlineEditor({
|
|
|
112
113
|
|
|
113
114
|
if (!fieldId || !itemId || !language || !version) return;
|
|
114
115
|
|
|
116
|
+
// Guard: if layout components are hidden, do not allow editing fields of layout components
|
|
117
|
+
if (
|
|
118
|
+
contextRef.current?.showLayoutComponents === false &&
|
|
119
|
+
contextRef.current?.page
|
|
120
|
+
) {
|
|
121
|
+
const owner = getComponentById(itemId, contextRef.current.page);
|
|
122
|
+
if (owner?.layoutId) return;
|
|
123
|
+
}
|
|
124
|
+
|
|
115
125
|
const fieldDescriptor = getFieldDescriptorFromElement(element);
|
|
116
126
|
|
|
117
127
|
const hasFielLock =
|
|
@@ -57,6 +57,17 @@ export function PictureEditorOverlay() {
|
|
|
57
57
|
|
|
58
58
|
if (component == null) return;
|
|
59
59
|
|
|
60
|
+
// Guard: if layout components are hidden, do not show picture editor overlay for layout components
|
|
61
|
+
if (
|
|
62
|
+
editContextRef.current?.showLayoutComponents === false &&
|
|
63
|
+
component.layoutId
|
|
64
|
+
) {
|
|
65
|
+
setField(undefined);
|
|
66
|
+
setVariantName(undefined);
|
|
67
|
+
setElement(undefined);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
60
71
|
const item = await editContext!.itemsRepository.getItem(
|
|
61
72
|
component.datasourceItem!,
|
|
62
73
|
);
|
|
@@ -6,6 +6,7 @@ import { useDebouncedCallback } from "use-debounce";
|
|
|
6
6
|
import { PageViewContext } from "./pageViewContext";
|
|
7
7
|
import { EditorConfiguration } from "../../config/types";
|
|
8
8
|
import { cn } from "../../lib/utils";
|
|
9
|
+
import { Lock, LockOpen } from "lucide-react";
|
|
9
10
|
|
|
10
11
|
export function DeviceToolbar({
|
|
11
12
|
pageViewContext,
|
|
@@ -53,9 +54,15 @@ export function DeviceToolbar({
|
|
|
53
54
|
onChange={(e) => debouncedSetDeviceHeight(e.value || undefined)}
|
|
54
55
|
/>
|
|
55
56
|
<SimpleIconButton
|
|
56
|
-
icon=
|
|
57
|
+
icon={
|
|
58
|
+
pageViewContext.lockHeight ? (
|
|
59
|
+
<Lock className="h-4 w-4" strokeWidth={1} />
|
|
60
|
+
) : (
|
|
61
|
+
<LockOpen className="h-4 w-4" strokeWidth={1} />
|
|
62
|
+
)
|
|
63
|
+
}
|
|
57
64
|
className="hover:bg-gray-3"
|
|
58
|
-
selected={
|
|
65
|
+
selected={false}
|
|
59
66
|
onClick={() =>
|
|
60
67
|
pageViewContext.setLockHeight(!pageViewContext.lockHeight)
|
|
61
68
|
}
|
|
@@ -180,13 +180,8 @@ export function EditorForm({
|
|
|
180
180
|
fullItem.language === item.language &&
|
|
181
181
|
fullItem.version === item.version;
|
|
182
182
|
|
|
183
|
-
// While the new full item is loading, render nothing to avoid showing stale content
|
|
184
|
-
if (hasDatasource && (!item || !fullItemMatchesSelection)) {
|
|
185
|
-
return <div className="h-full w-full"></div>;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
183
|
// Keep showing a spinner only when loading additional component items
|
|
189
|
-
if (
|
|
184
|
+
if (!fullItemMatchesSelection)
|
|
190
185
|
return (
|
|
191
186
|
<div className="grid h-full w-full items-center justify-center">
|
|
192
187
|
<Spinner />
|
|
@@ -428,6 +428,19 @@ export function PageViewerFrame({
|
|
|
428
428
|
|
|
429
429
|
const range = sel.getRangeAt(0);
|
|
430
430
|
|
|
431
|
+
// Guard: if layout components are hidden, do not set focus for fields of layout components
|
|
432
|
+
if (
|
|
433
|
+
editContextRef.current?.showLayoutComponents === false &&
|
|
434
|
+
pageViewContextRef.current?.page
|
|
435
|
+
) {
|
|
436
|
+
const ownerId = fieldElement.getAttribute("data-itemid") || "";
|
|
437
|
+
const owner = getComponentById(
|
|
438
|
+
ownerId,
|
|
439
|
+
pageViewContextRef.current!.page!,
|
|
440
|
+
);
|
|
441
|
+
if (owner?.layoutId) return;
|
|
442
|
+
}
|
|
443
|
+
|
|
431
444
|
fieldsContextRef.current?.setFocusedField(
|
|
432
445
|
{
|
|
433
446
|
fieldId: fieldId,
|
|
@@ -554,6 +567,21 @@ export function PageViewerFrame({
|
|
|
554
567
|
) {
|
|
555
568
|
const fieldElement = findParentWithAttribute(target, "data-fieldid");
|
|
556
569
|
if (fieldElement?.hasAttribute("data-itemid")) {
|
|
570
|
+
// Guard: if layout components are hidden, do not allow editing fields of layout components
|
|
571
|
+
if (
|
|
572
|
+
editContextRef.current?.showLayoutComponents === false &&
|
|
573
|
+
pageViewContextRef.current?.page
|
|
574
|
+
) {
|
|
575
|
+
const owningItemId = fieldElement.getAttribute("data-itemid") || "";
|
|
576
|
+
const ownerComponent = getComponentById(
|
|
577
|
+
owningItemId,
|
|
578
|
+
pageViewContextRef.current!.page!,
|
|
579
|
+
);
|
|
580
|
+
if (ownerComponent?.layoutId) {
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
557
585
|
blockBlurEventRef.current = Date.now() + 500;
|
|
558
586
|
|
|
559
587
|
const hasLock =
|
package/src/editor/pageModel.ts
CHANGED
|
@@ -18,6 +18,8 @@ export type AiProfile = {
|
|
|
18
18
|
models: AiModel[]; // Array of model objects with id and name
|
|
19
19
|
prompts: { prompt: string; title: string }[];
|
|
20
20
|
errorMessage?: string;
|
|
21
|
+
// Whether a new agent should be seeded with current item/selection/field
|
|
22
|
+
includeEditorContextOnCreate?: boolean;
|
|
21
23
|
};
|
|
22
24
|
|
|
23
25
|
export async function loadAiProfiles(
|
|
@@ -19,6 +19,9 @@ interface SplitterProps {
|
|
|
19
19
|
className?: string;
|
|
20
20
|
splitterClassName?: string;
|
|
21
21
|
expandButtonClassName?: string;
|
|
22
|
+
// Optional render function to inject custom content into a splitter handle.
|
|
23
|
+
// Receives the handle index (between panel i and i+1). Return null to render nothing.
|
|
24
|
+
handleContent?: (index: number) => React.ReactNode;
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
type StoredSizes = { panels: PanelSizes; lastCollapsed: boolean };
|
|
@@ -32,6 +35,7 @@ export const Splitter: React.FC<SplitterProps> = ({
|
|
|
32
35
|
className,
|
|
33
36
|
splitterClassName,
|
|
34
37
|
expandButtonClassName,
|
|
38
|
+
handleContent,
|
|
35
39
|
}) => {
|
|
36
40
|
const totalPanels = panels.length;
|
|
37
41
|
|
|
@@ -357,7 +361,7 @@ export const Splitter: React.FC<SplitterProps> = ({
|
|
|
357
361
|
minWidth: isHorizontal ? "4px" : undefined,
|
|
358
362
|
boxSizing: "border-box",
|
|
359
363
|
userSelect: "none",
|
|
360
|
-
zIndex:
|
|
364
|
+
zIndex: 1, // Ensure we're above any nested splitters
|
|
361
365
|
}}
|
|
362
366
|
onDoubleClick={
|
|
363
367
|
isLastResizer(index) ? toggleLastPanelCollapse : undefined
|
|
@@ -383,6 +387,22 @@ export const Splitter: React.FC<SplitterProps> = ({
|
|
|
383
387
|
}`}
|
|
384
388
|
/>
|
|
385
389
|
|
|
390
|
+
{/* Custom handle content */}
|
|
391
|
+
{handleContent && (
|
|
392
|
+
<div
|
|
393
|
+
className="pointer-events-auto flex items-center justify-center"
|
|
394
|
+
onMouseDown={(e) => {
|
|
395
|
+
// Prevent starting drag when interacting with handle content
|
|
396
|
+
e.stopPropagation();
|
|
397
|
+
}}
|
|
398
|
+
onClick={(e) => {
|
|
399
|
+
e.stopPropagation();
|
|
400
|
+
}}
|
|
401
|
+
>
|
|
402
|
+
{handleContent(index)}
|
|
403
|
+
</div>
|
|
404
|
+
)}
|
|
405
|
+
|
|
386
406
|
{/* Small collapse button for collapsible panels */}
|
|
387
407
|
{lastPanelCollapsible &&
|
|
388
408
|
!isLastPanelCollapsed &&
|
|
@@ -435,7 +455,11 @@ export const Splitter: React.FC<SplitterProps> = ({
|
|
|
435
455
|
|
|
436
456
|
return (
|
|
437
457
|
<div
|
|
438
|
-
className={`flex ${
|
|
458
|
+
className={`flex ${
|
|
459
|
+
direction === "horizontal" ? "flex-row" : "flex-col"
|
|
460
|
+
} ${
|
|
461
|
+
className && /(^|\s)h-/.test(className) ? "" : "h-full"
|
|
462
|
+
} ${className && /(^|\s)w-/.test(className) ? "" : "w-full"} overflow-hidden ${className || ""}`}
|
|
439
463
|
ref={splitterRef}
|
|
440
464
|
>
|
|
441
465
|
{panels.map((panel, index) => (
|
|
@@ -171,7 +171,7 @@ export function ComponentTypeSelector({
|
|
|
171
171
|
{/* Component Types Section */}
|
|
172
172
|
<div className="min-w-0 flex-1 rounded">
|
|
173
173
|
<div className="mb-2 flex items-center justify-between">
|
|
174
|
-
<h4 className="text-sm
|
|
174
|
+
<h4 className="text-sm">Component Types</h4>
|
|
175
175
|
</div>
|
|
176
176
|
|
|
177
177
|
{/* Selected component tags */}
|