@alpaca-editor/core 1.0.4000 → 1.0.4007
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/client-components/index.d.ts +1 -0
- package/dist/client-components/index.js +1 -0
- package/dist/client-components/index.js.map +1 -1
- package/dist/components/ui/calendar.d.ts +7 -0
- package/dist/components/ui/calendar.js +62 -0
- package/dist/components/ui/calendar.js.map +1 -0
- package/dist/config/config.js +11 -0
- package/dist/config/config.js.map +1 -1
- package/dist/editor/ContentTree.d.ts +2 -1
- package/dist/editor/ContentTree.js +2 -2
- package/dist/editor/ContentTree.js.map +1 -1
- package/dist/editor/FieldList.js +37 -10
- package/dist/editor/FieldList.js.map +1 -1
- package/dist/editor/FieldListField.js +21 -10
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/client/EditorClient.js +5 -3
- package/dist/editor/client/EditorClient.js.map +1 -1
- package/dist/editor/client/itemsRepository.js +57 -9
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/client/pageModelBuilder.js +1 -1
- package/dist/editor/client/pageModelBuilder.js.map +1 -1
- package/dist/editor/commands/itemCommands.js +1 -1
- package/dist/editor/commands/itemCommands.js.map +1 -1
- package/dist/editor/field-types/DateFieldEditor.d.ts +5 -0
- package/dist/editor/field-types/DateFieldEditor.js +93 -0
- package/dist/editor/field-types/DateFieldEditor.js.map +1 -0
- package/dist/editor/field-types/ImageFieldEditor.js +1 -1
- package/dist/editor/field-types/ImageFieldEditor.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 +1 -1
- package/dist/editor/field-types/LinkFieldEditor.js.map +1 -1
- package/dist/editor/field-types/MultiLineText.js +3 -1
- package/dist/editor/field-types/MultiLineText.js.map +1 -1
- package/dist/editor/field-types/PictureFieldEditor.js +1 -1
- package/dist/editor/field-types/PictureFieldEditor.js.map +1 -1
- package/dist/editor/field-types/SingleLineText.js +3 -1
- package/dist/editor/field-types/SingleLineText.js.map +1 -1
- package/dist/editor/field-types/TreeListEditor.js +71 -6
- package/dist/editor/field-types/TreeListEditor.js.map +1 -1
- package/dist/editor/field-types/richtext/components/ReactSlate.js +1 -1
- package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -1
- package/dist/editor/fieldTypes.d.ts +1 -1
- package/dist/editor/media-selector/AiImageSearch.js +2 -1
- package/dist/editor/media-selector/AiImageSearch.js.map +1 -1
- package/dist/editor/media-selector/AiImageSearchPrompt.js +2 -1
- package/dist/editor/media-selector/AiImageSearchPrompt.js.map +1 -1
- package/dist/editor/page-viewer/DeviceToolbar.js +3 -2
- package/dist/editor/page-viewer/DeviceToolbar.js.map +1 -1
- package/dist/editor/page-viewer/MiniMap.js +6 -1
- package/dist/editor/page-viewer/MiniMap.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +9 -1
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/sidebar/Insert.js +1 -1
- package/dist/editor/sidebar/Insert.js.map +1 -1
- package/dist/editor/sidebar/MainContentTree.d.ts +1 -1
- package/dist/editor/sidebar/MainContentTree.js +19 -7
- package/dist/editor/sidebar/MainContentTree.js.map +1 -1
- package/dist/editor/ui/Icons.js +1 -1
- package/dist/editor/ui/Icons.js.map +1 -1
- package/dist/editor/ui/ItemNameDialogNew.js +14 -10
- package/dist/editor/ui/ItemNameDialogNew.js.map +1 -1
- package/dist/editor/ui/PerfectTree.d.ts +2 -0
- package/dist/editor/ui/PerfectTree.js +1 -1
- package/dist/editor/ui/PerfectTree.js.map +1 -1
- package/dist/editor/ui/Spinner.js +1 -1
- package/dist/editor/ui/Spinner.js.map +1 -1
- package/dist/page-wizard/steps/ContentStep.js +56 -17
- package/dist/page-wizard/steps/ContentStep.js.map +1 -1
- package/dist/page-wizard/steps/FindItemsStep.d.ts +2 -0
- package/dist/page-wizard/steps/FindItemsStep.js +293 -0
- package/dist/page-wizard/steps/FindItemsStep.js.map +1 -0
- package/dist/page-wizard/steps/ImagesStep.js +9 -1
- package/dist/page-wizard/steps/ImagesStep.js.map +1 -1
- package/dist/page-wizard/steps/LayoutStep.js +24 -14
- package/dist/page-wizard/steps/LayoutStep.js.map +1 -1
- package/dist/page-wizard/steps/MetaDataStep.js +21 -11
- package/dist/page-wizard/steps/MetaDataStep.js.map +1 -1
- package/dist/page-wizard/steps/SelectStep.js +38 -31
- package/dist/page-wizard/steps/SelectStep.js.map +1 -1
- package/dist/page-wizard/steps/StructureStep.d.ts +2 -0
- package/dist/page-wizard/steps/StructureStep.js +156 -0
- package/dist/page-wizard/steps/StructureStep.js.map +1 -0
- package/dist/page-wizard/utils/dataAccessor.d.ts +39 -0
- package/dist/page-wizard/utils/dataAccessor.js +222 -0
- package/dist/page-wizard/utils/dataAccessor.js.map +1 -0
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/splash-screen/RecentPages.js +14 -3
- package/dist/splash-screen/RecentPages.js.map +1 -1
- package/dist/styles.css +178 -0
- package/package.json +3 -1
- package/src/client-components/index.ts +1 -3
- package/src/components/ui/calendar.tsx +175 -0
- package/src/config/config.tsx +11 -1
- package/src/editor/ContentTree.tsx +4 -1
- package/src/editor/FieldList.tsx +44 -18
- package/src/editor/FieldListField.tsx +98 -23
- package/src/editor/client/EditorClient.tsx +6 -3
- package/src/editor/client/itemsRepository.ts +74 -10
- package/src/editor/client/pageModelBuilder.ts +1 -1
- package/src/editor/commands/itemCommands.tsx +2 -3
- package/src/editor/field-types/DateFieldEditor.tsx +132 -0
- package/src/editor/field-types/ImageFieldEditor.tsx +1 -1
- package/src/editor/field-types/InternalLinkFieldEditor.tsx +2 -2
- package/src/editor/field-types/LinkFieldEditor.tsx +2 -2
- package/src/editor/field-types/MultiLineText.tsx +5 -1
- package/src/editor/field-types/PictureFieldEditor.tsx +1 -1
- package/src/editor/field-types/SingleLineText.tsx +5 -1
- package/src/editor/field-types/TreeListEditor.tsx +136 -50
- package/src/editor/field-types/richtext/components/ReactSlate.tsx +5 -0
- package/src/editor/fieldTypes.ts +1 -1
- package/src/editor/media-selector/AiImageSearch.tsx +2 -1
- package/src/editor/media-selector/AiImageSearchPrompt.tsx +2 -1
- package/src/editor/page-viewer/DeviceToolbar.tsx +17 -5
- package/src/editor/page-viewer/MiniMap.tsx +7 -2
- package/src/editor/services/aiService.ts +10 -1
- package/src/editor/sidebar/Insert.tsx +1 -1
- package/src/editor/sidebar/MainContentTree.tsx +26 -14
- package/src/editor/ui/Icons.tsx +16 -5
- package/src/editor/ui/ItemNameDialogNew.tsx +42 -30
- package/src/editor/ui/PerfectTree.tsx +3 -1
- package/src/editor/ui/Spinner.tsx +3 -1
- package/src/page-wizard/steps/ContentStep.tsx +68 -20
- package/src/page-wizard/steps/FindItemsStep.tsx +546 -0
- package/src/page-wizard/steps/ImagesStep.tsx +13 -1
- package/src/page-wizard/steps/LayoutStep.tsx +27 -16
- package/src/page-wizard/steps/MetaDataStep.tsx +26 -13
- package/src/page-wizard/steps/SelectStep.tsx +61 -32
- package/src/page-wizard/steps/StructureStep.tsx +350 -0
- package/src/page-wizard/utils/dataAccessor.ts +275 -0
- package/src/revision.ts +2 -2
- package/src/splash-screen/RecentPages.tsx +22 -3
- package/dist/editor/ai/GhostWriter.d.ts +0 -1
- package/dist/editor/ai/GhostWriter.js +0 -347
- package/dist/editor/ai/GhostWriter.js.map +0 -1
- package/dist/editor/component-designer/ComponentDesignerAiTerminal.d.ts +0 -1
- package/dist/editor/component-designer/ComponentDesignerAiTerminal.js +0 -7
- package/dist/editor/component-designer/ComponentDesignerAiTerminal.js.map +0 -1
- /package/src/editor/ai/{GhostWriter.tsx → GhostWriter.tsx_} +0 -0
- /package/src/editor/component-designer/{ComponentDesignerAiTerminal.tsx → ComponentDesignerAiTerminal.tsx_} +0 -0
|
@@ -2,7 +2,6 @@ import { Splitter, SplitterPanel } from "primereact/splitter";
|
|
|
2
2
|
import { useEditContext } from "../client/editContext";
|
|
3
3
|
import { useEffect, useState } from "react";
|
|
4
4
|
|
|
5
|
-
import { ListBox } from "primereact/listbox";
|
|
6
5
|
import { ReferencedItem, TreeListField } from "../fieldTypes";
|
|
7
6
|
import { getLookupSources } from "../services/editService";
|
|
8
7
|
import { SimpleIconButton } from "../ui/SimpleIconButton";
|
|
@@ -39,6 +38,7 @@ export default function TreeListEditor({
|
|
|
39
38
|
const [values, setValues] = useState<ReferencedItem[]>([]);
|
|
40
39
|
const [hoveredItemId, setHoveredItemId] = useState<string | undefined>();
|
|
41
40
|
const [hoveredItem, setHoveredItem] = useState<FullItem | undefined>();
|
|
41
|
+
const [draggedIndex, setDraggedIndex] = useState<number | null>(null);
|
|
42
42
|
|
|
43
43
|
if (!editContext) return;
|
|
44
44
|
|
|
@@ -147,9 +147,49 @@ export default function TreeListEditor({
|
|
|
147
147
|
});
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
const handleDragStart = (event: React.DragEvent, index: number) => {
|
|
151
|
+
event.dataTransfer.setData("text/plain", index.toString());
|
|
152
|
+
event.dataTransfer.effectAllowed = "move";
|
|
153
|
+
setDraggedIndex(index);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const handleDragOver = (event: React.DragEvent) => {
|
|
157
|
+
event.preventDefault();
|
|
158
|
+
event.dataTransfer.dropEffect = "move";
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const handleDrop = async (event: React.DragEvent, dropIndex: number) => {
|
|
162
|
+
event.preventDefault();
|
|
163
|
+
const dragIndex = parseInt(event.dataTransfer.getData("text/plain"));
|
|
164
|
+
|
|
165
|
+
if (dragIndex === dropIndex) {
|
|
166
|
+
setDraggedIndex(null);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const newValues = [...values];
|
|
171
|
+
const [draggedItem] = newValues.splice(dragIndex, 1);
|
|
172
|
+
if (!draggedItem) return;
|
|
173
|
+
newValues.splice(dropIndex, 0, draggedItem);
|
|
174
|
+
|
|
175
|
+
setValues(newValues);
|
|
176
|
+
setDraggedIndex(null);
|
|
177
|
+
|
|
178
|
+
await editContext?.operations.editField({
|
|
179
|
+
field: field.descriptor,
|
|
180
|
+
value: newValues,
|
|
181
|
+
rawValue: newValues.map((x) => normalizeGuid(x.id)).join("|"),
|
|
182
|
+
refresh: "waitForQuietPeriod",
|
|
183
|
+
});
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const handleDragEnd = () => {
|
|
187
|
+
setDraggedIndex(null);
|
|
188
|
+
};
|
|
189
|
+
|
|
150
190
|
return (
|
|
151
191
|
<div className="focus-shadow border border-gray-200">
|
|
152
|
-
<div className="mb-1 border-b border-gray-200
|
|
192
|
+
<div className="bg-gray-5 mb-1 border-b border-gray-200 p-2">
|
|
153
193
|
<ItemSearch
|
|
154
194
|
rootItemIds={rootItemIds.map((x) => normalizeGuid(x))}
|
|
155
195
|
itemSelected={async (item) => {
|
|
@@ -208,56 +248,102 @@ export default function TreeListEditor({
|
|
|
208
248
|
/>
|
|
209
249
|
</div>
|
|
210
250
|
<div className="relative h-full flex-1">
|
|
211
|
-
<
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
metaKeySelection={true}
|
|
216
|
-
emptyMessage={"None selected"}
|
|
217
|
-
multiple={true}
|
|
218
|
-
value={selectedFromList}
|
|
219
|
-
onChange={(e) => setSelectedFromList(e.value)}
|
|
220
|
-
itemTemplate={(option) => (
|
|
221
|
-
<div
|
|
222
|
-
className="group relative flex items-center gap-1.5 select-none hover:bg-gray-50"
|
|
223
|
-
onMouseEnter={async () => {
|
|
224
|
-
setHoveredItemId(option.id);
|
|
225
|
-
// setExpandIdPath(option.idPath || "");
|
|
226
|
-
setSelectedItemNodesInTree([option]);
|
|
227
|
-
}}
|
|
228
|
-
onDoubleClick={() => {
|
|
229
|
-
if (!readOnly) removeFromList([option]);
|
|
230
|
-
}}
|
|
231
|
-
>
|
|
232
|
-
<img src={option.icon} className="h-4 w-4" />
|
|
233
|
-
{trimPath(option.path)}{" "}
|
|
234
|
-
{activeOpenButtonId === option.id ? (
|
|
235
|
-
<Button
|
|
236
|
-
className="h-4 p-1"
|
|
237
|
-
size="sm"
|
|
238
|
-
onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
|
|
239
|
-
e.stopPropagation();
|
|
240
|
-
editContext.loadItem(option.id);
|
|
241
|
-
setActiveOpenButtonId("");
|
|
242
|
-
}}
|
|
243
|
-
>
|
|
244
|
-
Open
|
|
245
|
-
</Button>
|
|
246
|
-
) : (
|
|
247
|
-
<button
|
|
248
|
-
className="cursor-pointer opacity-0 transition-opacity group-hover:opacity-100"
|
|
249
|
-
onClick={(e) => {
|
|
250
|
-
e.stopPropagation();
|
|
251
|
-
setActiveOpenButtonId(option.id);
|
|
252
|
-
}}
|
|
253
|
-
title="Open"
|
|
254
|
-
>
|
|
255
|
-
<i className="pi pi-arrow-up-right text-xs text-gray-500" />
|
|
256
|
-
</button>
|
|
257
|
-
)}
|
|
251
|
+
<div className="absolute inset-0 overflow-auto border border-gray-300 bg-white text-xs">
|
|
252
|
+
{values.length === 0 ? (
|
|
253
|
+
<div className="p-3 text-center text-gray-500">
|
|
254
|
+
None selected
|
|
258
255
|
</div>
|
|
256
|
+
) : (
|
|
257
|
+
values.map((option, index) => (
|
|
258
|
+
<div
|
|
259
|
+
key={option.id}
|
|
260
|
+
draggable={!readOnly}
|
|
261
|
+
onDragStart={(e) => handleDragStart(e, index)}
|
|
262
|
+
onDragOver={handleDragOver}
|
|
263
|
+
onDrop={(e) => handleDrop(e, index)}
|
|
264
|
+
onDragEnd={handleDragEnd}
|
|
265
|
+
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 ${
|
|
266
|
+
selectedFromList.includes(option) ? "bg-blue-100" : ""
|
|
267
|
+
} ${draggedIndex === index ? "opacity-50" : ""} ${
|
|
268
|
+
!readOnly ? "cursor-move" : ""
|
|
269
|
+
}`}
|
|
270
|
+
onClick={(e) => {
|
|
271
|
+
if (e.ctrlKey) {
|
|
272
|
+
// Multi-select mode when Ctrl is pressed
|
|
273
|
+
if (selectedFromList.includes(option)) {
|
|
274
|
+
setSelectedFromList(
|
|
275
|
+
selectedFromList.filter(
|
|
276
|
+
(item) => item.id !== option.id,
|
|
277
|
+
),
|
|
278
|
+
);
|
|
279
|
+
} else {
|
|
280
|
+
setSelectedFromList([
|
|
281
|
+
...selectedFromList,
|
|
282
|
+
option,
|
|
283
|
+
]);
|
|
284
|
+
}
|
|
285
|
+
} else {
|
|
286
|
+
// Single-select mode when Ctrl is not pressed
|
|
287
|
+
if (
|
|
288
|
+
selectedFromList.length === 1 &&
|
|
289
|
+
selectedFromList.includes(option)
|
|
290
|
+
) {
|
|
291
|
+
// Deselect if this is the only selected item
|
|
292
|
+
setSelectedFromList([]);
|
|
293
|
+
} else {
|
|
294
|
+
// Replace selection with just this item
|
|
295
|
+
setSelectedFromList([option]);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}}
|
|
299
|
+
onMouseEnter={async () => {
|
|
300
|
+
setHoveredItemId(option.id);
|
|
301
|
+
setSelectedItemNodesInTree([
|
|
302
|
+
{
|
|
303
|
+
id: option.id,
|
|
304
|
+
path: option.path,
|
|
305
|
+
name: option.name,
|
|
306
|
+
icon: option.icon,
|
|
307
|
+
idPath: option.idPath || "",
|
|
308
|
+
},
|
|
309
|
+
]);
|
|
310
|
+
}}
|
|
311
|
+
onDoubleClick={() => {
|
|
312
|
+
if (!readOnly) removeFromList([option]);
|
|
313
|
+
}}
|
|
314
|
+
>
|
|
315
|
+
<img src={option.icon} className="h-4 w-4" />
|
|
316
|
+
{trimPath(option.path)}{" "}
|
|
317
|
+
{activeOpenButtonId === option.id ? (
|
|
318
|
+
<Button
|
|
319
|
+
className="h-4 p-1"
|
|
320
|
+
size="sm"
|
|
321
|
+
onClick={(
|
|
322
|
+
e: React.MouseEvent<HTMLButtonElement>,
|
|
323
|
+
) => {
|
|
324
|
+
e.stopPropagation();
|
|
325
|
+
editContext.loadItem(option.id);
|
|
326
|
+
setActiveOpenButtonId("");
|
|
327
|
+
}}
|
|
328
|
+
>
|
|
329
|
+
Open
|
|
330
|
+
</Button>
|
|
331
|
+
) : (
|
|
332
|
+
<button
|
|
333
|
+
className="cursor-pointer opacity-0 transition-opacity group-hover:opacity-100"
|
|
334
|
+
onClick={(e) => {
|
|
335
|
+
e.stopPropagation();
|
|
336
|
+
setActiveOpenButtonId(option.id);
|
|
337
|
+
}}
|
|
338
|
+
title="Open"
|
|
339
|
+
>
|
|
340
|
+
<i className="pi pi-arrow-up-right text-xs text-gray-500" />
|
|
341
|
+
</button>
|
|
342
|
+
)}
|
|
343
|
+
</div>
|
|
344
|
+
))
|
|
259
345
|
)}
|
|
260
|
-
|
|
346
|
+
</div>
|
|
261
347
|
</div>
|
|
262
348
|
</div>
|
|
263
349
|
</div>
|
|
@@ -724,6 +724,11 @@ export const ReactSlate = forwardRef<any, ReactSlateProps>((props, ref) => {
|
|
|
724
724
|
)}
|
|
725
725
|
readOnly={readOnly}
|
|
726
726
|
placeholder={placeholder}
|
|
727
|
+
renderPlaceholder={({ attributes, children }) => (
|
|
728
|
+
<span {...attributes} className="p-2 text-gray-500">
|
|
729
|
+
{children}
|
|
730
|
+
</span>
|
|
731
|
+
)}
|
|
727
732
|
onFocus={onFocus}
|
|
728
733
|
onBlur={onBlur}
|
|
729
734
|
onKeyDown={handleKeyDown}
|
package/src/editor/fieldTypes.ts
CHANGED
|
@@ -70,7 +70,8 @@ export function AiImageSearch({
|
|
|
70
70
|
);
|
|
71
71
|
|
|
72
72
|
setLoadingPrompt(false);
|
|
73
|
-
const
|
|
73
|
+
const lastMessage = result?.messages?.[result.messages.length - 1];
|
|
74
|
+
const newPrompt = lastMessage?.content?.replaceAll("\n", "") || "";
|
|
74
75
|
setPrompt(newPrompt);
|
|
75
76
|
} catch (error) {
|
|
76
77
|
if (error instanceof Error && error.name !== "AbortError") {
|
|
@@ -53,7 +53,8 @@ export function AiImageSearchPrompt({
|
|
|
53
53
|
);
|
|
54
54
|
|
|
55
55
|
setLoadingPrompt(false);
|
|
56
|
-
const
|
|
56
|
+
const lastMessage = result?.messages?.[result.messages.length - 1];
|
|
57
|
+
const newPrompt = lastMessage?.content?.replaceAll("\n", "") || "";
|
|
57
58
|
setPrompt(newPrompt);
|
|
58
59
|
} catch (error) {
|
|
59
60
|
if (error instanceof Error && error.name !== "AbortError") {
|
|
@@ -5,6 +5,7 @@ import { RotateDeviceIcon } from "../ui/Icons";
|
|
|
5
5
|
import { useDebouncedCallback } from "use-debounce";
|
|
6
6
|
import { PageViewContext } from "./pageViewContext";
|
|
7
7
|
import { EditorConfiguration } from "../../config/types";
|
|
8
|
+
import { cn } from "../../lib/utils";
|
|
8
9
|
|
|
9
10
|
export function DeviceToolbar({
|
|
10
11
|
pageViewContext,
|
|
@@ -17,17 +18,17 @@ export function DeviceToolbar({
|
|
|
17
18
|
(width: number | undefined) => {
|
|
18
19
|
pageViewContext.setDeviceWidth(width);
|
|
19
20
|
},
|
|
20
|
-
400
|
|
21
|
+
400,
|
|
21
22
|
);
|
|
22
23
|
const debouncedSetDeviceHeight = useDebouncedCallback(
|
|
23
24
|
(height: number | undefined) => {
|
|
24
25
|
pageViewContext.setDeviceHeight(height);
|
|
25
26
|
},
|
|
26
|
-
400
|
|
27
|
+
400,
|
|
27
28
|
);
|
|
28
29
|
|
|
29
30
|
return (
|
|
30
|
-
<div className="
|
|
31
|
+
<div className="bg-gray-4 z-1000 mb-2 flex w-full items-center justify-center gap-2 text-sm">
|
|
31
32
|
<Dropdown
|
|
32
33
|
options={configuration.devices.map((x) => ({
|
|
33
34
|
label: x.name,
|
|
@@ -53,6 +54,7 @@ export function DeviceToolbar({
|
|
|
53
54
|
/>
|
|
54
55
|
<SimpleIconButton
|
|
55
56
|
icon="pi pi-lock"
|
|
57
|
+
className="hover:bg-gray-3"
|
|
56
58
|
selected={pageViewContext.lockHeight || false}
|
|
57
59
|
onClick={() =>
|
|
58
60
|
pageViewContext.setLockHeight(!pageViewContext.lockHeight)
|
|
@@ -60,10 +62,20 @@ export function DeviceToolbar({
|
|
|
60
62
|
label="Lock Height"
|
|
61
63
|
/>
|
|
62
64
|
<SimpleIconButton
|
|
63
|
-
|
|
65
|
+
className="hover:bg-gray-3"
|
|
66
|
+
icon={
|
|
67
|
+
<RotateDeviceIcon
|
|
68
|
+
className={cn(
|
|
69
|
+
"h-4 w-4 transition-transform duration-300",
|
|
70
|
+
pageViewContext.rotate ? "rotate-90" : "",
|
|
71
|
+
)}
|
|
72
|
+
/>
|
|
73
|
+
}
|
|
64
74
|
selected={pageViewContext.rotate || false}
|
|
65
75
|
onClick={() => pageViewContext.setRotate(!pageViewContext.rotate)}
|
|
66
|
-
label=
|
|
76
|
+
label={
|
|
77
|
+
pageViewContext.rotate ? "Switch to Portrait" : "Switch to Landscape"
|
|
78
|
+
}
|
|
67
79
|
/>
|
|
68
80
|
</div>
|
|
69
81
|
);
|
|
@@ -123,9 +123,14 @@ export function MiniMap({
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
if (correspondingNode && originalNode) {
|
|
126
|
-
if (originalNode.tagName === "HEAD")
|
|
126
|
+
if (originalNode.tagName === "HEAD") {
|
|
127
127
|
correspondingNode.innerHTML = originalNode.innerHTML;
|
|
128
|
-
else
|
|
128
|
+
} else if (
|
|
129
|
+
correspondingNode.parentNode?.nodeType === Node.DOCUMENT_NODE
|
|
130
|
+
) {
|
|
131
|
+
// Can't use outerHTML when parent is the document node
|
|
132
|
+
correspondingNode.innerHTML = originalNode.innerHTML;
|
|
133
|
+
} else {
|
|
129
134
|
correspondingNode.outerHTML = originalNode.outerHTML;
|
|
130
135
|
}
|
|
131
136
|
}
|
|
@@ -34,6 +34,15 @@ type Message = {
|
|
|
34
34
|
role: string;
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
+
export interface ExecutePromptResponse {
|
|
38
|
+
editOperations: any[];
|
|
39
|
+
messages: Message[];
|
|
40
|
+
numInputTokens: number;
|
|
41
|
+
numOutputTokens: number;
|
|
42
|
+
numCachedTokens: number;
|
|
43
|
+
state: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
37
46
|
export async function executePrompt(
|
|
38
47
|
messages: Message[],
|
|
39
48
|
editContext: EditContextType,
|
|
@@ -48,7 +57,7 @@ export async function executePrompt(
|
|
|
48
57
|
options?: RequestInit,
|
|
49
58
|
model?: string,
|
|
50
59
|
callback?: (response: any) => void,
|
|
51
|
-
): Promise<
|
|
60
|
+
): Promise<ExecutePromptResponse | null> {
|
|
52
61
|
const context = createAiContext({ editContext });
|
|
53
62
|
|
|
54
63
|
const response = await fetch(context.endpoint, {
|
|
@@ -10,7 +10,7 @@ export function MainContentTree({
|
|
|
10
10
|
mode,
|
|
11
11
|
rootItemId,
|
|
12
12
|
}: {
|
|
13
|
-
mode: "insert" | "normal" | "select-page";
|
|
13
|
+
mode: "insert-component" | "normal" | "select-page";
|
|
14
14
|
rootItemId?: string;
|
|
15
15
|
}) {
|
|
16
16
|
const editContext = useEditContext();
|
|
@@ -22,17 +22,28 @@ export function MainContentTree({
|
|
|
22
22
|
const [selectedItemIds, setSelectedItemIds] = useState<string[]>([]);
|
|
23
23
|
|
|
24
24
|
const isDraggable = useCallback(
|
|
25
|
-
(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
25
|
+
(item: ItemTreeNodeData) => {
|
|
26
|
+
// Only allow dragging in insert-component mode
|
|
27
|
+
if (mode !== "insert-component") {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!insertOptions) return false;
|
|
32
|
+
|
|
33
|
+
// Check if the item's template is compatible with any insert option
|
|
34
|
+
return insertOptions.some((insertOption) => {
|
|
35
|
+
// Direct type match
|
|
36
|
+
if (insertOption.typeId === item.templateId) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Compatible type match
|
|
41
|
+
return (
|
|
42
|
+
insertOption.compatibleTypeIds?.some(
|
|
43
|
+
(compatibleTypeId) => compatibleTypeId === item.templateId,
|
|
44
|
+
) ?? false
|
|
45
|
+
);
|
|
46
|
+
});
|
|
36
47
|
},
|
|
37
48
|
[mode, insertOptions],
|
|
38
49
|
);
|
|
@@ -76,10 +87,10 @@ export function MainContentTree({
|
|
|
76
87
|
scrollToSelected={true}
|
|
77
88
|
className="h-full"
|
|
78
89
|
selectPagesOnly={mode === "select-page"}
|
|
79
|
-
selectionMode={mode == "insert" ? "none" : "multiple"}
|
|
90
|
+
selectionMode={mode == "insert-component" ? "none" : "multiple"}
|
|
80
91
|
selectedItemIds={selectedItemIds}
|
|
81
92
|
onSelectionChange={(selection) => {
|
|
82
|
-
if (mode === "insert") return;
|
|
93
|
+
if (mode === "insert-component") return;
|
|
83
94
|
const selectedItems = selection as ItemTreeNodeData[];
|
|
84
95
|
setSelectedItemIds(selectedItems.map((x) => x.id));
|
|
85
96
|
if (selectedItems.length > 0 && selectedItems[0])
|
|
@@ -89,6 +100,7 @@ export function MainContentTree({
|
|
|
89
100
|
version: selectedItems[0].version,
|
|
90
101
|
});
|
|
91
102
|
}}
|
|
103
|
+
showGrabCursorForDraggableNodes={mode === "insert-component"}
|
|
92
104
|
isDraggable={isDraggable}
|
|
93
105
|
renderNode={(node, defaultRenderer) => (
|
|
94
106
|
<div className="group flex w-full gap-4">
|
package/src/editor/ui/Icons.tsx
CHANGED
|
@@ -393,14 +393,25 @@ export function FormEditIcon({ className }: { className?: string }) {
|
|
|
393
393
|
export function RotateDeviceIcon({ className }: { className?: string }) {
|
|
394
394
|
return (
|
|
395
395
|
<svg
|
|
396
|
-
|
|
396
|
+
width="16"
|
|
397
|
+
height="18"
|
|
398
|
+
viewBox="0 0 16 18"
|
|
399
|
+
fill="none"
|
|
397
400
|
xmlns="http://www.w3.org/2000/svg"
|
|
398
401
|
className={className}
|
|
399
|
-
stroke="currentColor"
|
|
400
|
-
fill="currentColor"
|
|
401
402
|
>
|
|
402
|
-
<path
|
|
403
|
-
|
|
403
|
+
<path
|
|
404
|
+
d="M10.24 9C10.8561 9 11.1641 9 11.3994 9.12456C11.6064 9.23413 11.7746 9.40897 11.8801 9.62401C12 9.86848 12 10.1885 12 10.8286L12 15.1714C12 15.8115 12 16.1315 11.8801 16.376C11.7746 16.591 11.6064 16.7659 11.3994 16.8754C11.1641 17 10.8561 17 10.24 17L2.76 17C2.14394 17 1.83591 17 1.60061 16.8754C1.39363 16.7659 1.22535 16.591 1.11989 16.376C0.999999 16.1315 0.999999 15.8115 0.999999 15.1714L1 10.8286C1 10.1885 1 9.86848 1.11989 9.62401C1.22535 9.40897 1.39363 9.23413 1.60061 9.12456C1.83591 9 2.14394 9 2.76 9L10.24 9Z"
|
|
405
|
+
stroke="#111111"
|
|
406
|
+
stroke-linecap="round"
|
|
407
|
+
stroke-linejoin="round"
|
|
408
|
+
/>
|
|
409
|
+
<path
|
|
410
|
+
d="M15 11L15 9.11111C15 7.24427 15 6.31085 14.6321 5.59781C14.3086 4.9706 13.7923 4.46067 13.1572 4.14109C12.4353 3.77778 11.4902 3.77778 9.6 3.77778L6 3.77778M6 3.77778L8.8125 6.55556M6 3.77778L8.8125 1"
|
|
411
|
+
stroke="#111111"
|
|
412
|
+
stroke-linecap="round"
|
|
413
|
+
stroke-linejoin="round"
|
|
414
|
+
/>
|
|
404
415
|
</svg>
|
|
405
416
|
);
|
|
406
417
|
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Dialog,
|
|
3
|
+
DialogContent,
|
|
4
|
+
DialogHeader,
|
|
5
|
+
DialogTitle,
|
|
6
|
+
} from "../../components/ui/dialog";
|
|
2
7
|
|
|
3
8
|
import { useEffect, useRef, useState } from "react";
|
|
4
9
|
import DialogButtons from "./DialogButtons";
|
|
@@ -32,6 +37,12 @@ export function ItemNameDialog(
|
|
|
32
37
|
setName(props?.name || "");
|
|
33
38
|
}, [props]);
|
|
34
39
|
|
|
40
|
+
// Focus and select the input when dialog opens
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
nameRef.current?.focus();
|
|
43
|
+
nameRef.current?.select();
|
|
44
|
+
}, []);
|
|
45
|
+
|
|
35
46
|
const checkNameValidDebounced = useDebouncedCallback(
|
|
36
47
|
async () => checkName(),
|
|
37
48
|
500,
|
|
@@ -81,38 +92,39 @@ export function ItemNameDialog(
|
|
|
81
92
|
|
|
82
93
|
return (
|
|
83
94
|
<Dialog
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
nameRef.current?.focus();
|
|
90
|
-
nameRef.current?.select();
|
|
95
|
+
open={true}
|
|
96
|
+
onOpenChange={(open) => {
|
|
97
|
+
if (!open) {
|
|
98
|
+
props.onClose?.(null);
|
|
99
|
+
}
|
|
91
100
|
}}
|
|
92
|
-
style={{ width: "400px" }}
|
|
93
|
-
header={props?.title ?? "Name"}
|
|
94
101
|
>
|
|
95
|
-
<
|
|
96
|
-
<
|
|
97
|
-
{props
|
|
102
|
+
<DialogContent style={{ width: "400px" }}>
|
|
103
|
+
<DialogHeader>
|
|
104
|
+
<DialogTitle>{props?.title ?? "Name"}</DialogTitle>
|
|
105
|
+
</DialogHeader>
|
|
106
|
+
<div className="p-3">
|
|
107
|
+
<div className="my-2 text-sm">
|
|
108
|
+
{props.message || "Enter a name for the new item:"}
|
|
109
|
+
</div>
|
|
110
|
+
<InputText
|
|
111
|
+
ref={nameRef}
|
|
112
|
+
value={name}
|
|
113
|
+
className="w-full"
|
|
114
|
+
onChange={(e) => setName(e.target.value)}
|
|
115
|
+
onKeyDown={(ev) => {
|
|
116
|
+
if (ev.key === "Enter" && isValid) handleOk();
|
|
117
|
+
}}
|
|
118
|
+
/>
|
|
119
|
+
{validationError && (
|
|
120
|
+
<div className="mt-2 text-red-500">{validationError}</div>
|
|
121
|
+
)}
|
|
98
122
|
</div>
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
onKeyDown={(ev) => {
|
|
105
|
-
if (ev.key === "Enter" && isValid) handleOk();
|
|
106
|
-
}}
|
|
107
|
-
/>
|
|
108
|
-
{validationError && (
|
|
109
|
-
<div className="mt-2 text-red-500">{validationError}</div>
|
|
110
|
-
)}
|
|
111
|
-
</div>
|
|
112
|
-
<DialogButtons>
|
|
113
|
-
<Button onClick={handleOk} label="Ok" disabled={!isValid} />
|
|
114
|
-
<Button onClick={() => props.onClose?.(null)} label="Cancel" />
|
|
115
|
-
</DialogButtons>
|
|
123
|
+
<DialogButtons>
|
|
124
|
+
<Button onClick={handleOk} label="Ok" disabled={!isValid} />
|
|
125
|
+
<Button onClick={() => props.onClose?.(null)} label="Cancel" />
|
|
126
|
+
</DialogButtons>
|
|
127
|
+
</DialogContent>
|
|
116
128
|
</Dialog>
|
|
117
129
|
);
|
|
118
130
|
}
|
|
@@ -16,6 +16,8 @@ export interface TreeNode<T = any> {
|
|
|
16
16
|
data?: T;
|
|
17
17
|
/** Indicates if the node is expandable (has or can have children) */
|
|
18
18
|
hasChildren?: boolean;
|
|
19
|
+
/** Indicates if the node can be dragged */
|
|
20
|
+
isDraggable?: boolean;
|
|
19
21
|
/**
|
|
20
22
|
* If present, contains the node's children.
|
|
21
23
|
* Use undefined to signal that children have not yet been loaded.
|
|
@@ -530,7 +532,7 @@ const NodeContent = memo(
|
|
|
530
532
|
return (
|
|
531
533
|
<div
|
|
532
534
|
className="tree-node mb-0.5 flex cursor-pointer items-center"
|
|
533
|
-
draggable={enableDragAndDrop}
|
|
535
|
+
draggable={enableDragAndDrop && !!node.isDraggable}
|
|
534
536
|
onClick={handleSelect}
|
|
535
537
|
onDragStart={(event) => handleDragStart(event)}
|
|
536
538
|
onDragEnd={onDragEnd as any}
|