@alpaca-editor/core 1.0.3956 → 1.0.3960
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/build.css +1 -1
- package/dist/components/ui/badge.d.ts +9 -0
- package/dist/components/ui/badge.js +23 -0
- package/dist/components/ui/badge.js.map +1 -0
- package/dist/components/ui/button.js +3 -3
- package/dist/components/ui/button.js.map +1 -1
- package/dist/components/ui/command.d.ts +18 -0
- package/dist/components/ui/command.js +35 -0
- package/dist/components/ui/command.js.map +1 -0
- package/dist/components/ui/dialog.d.ts +15 -0
- package/dist/components/ui/dialog.js +37 -0
- package/dist/components/ui/dialog.js.map +1 -0
- package/dist/components/ui/dropdown-menu.d.ts +25 -0
- package/dist/components/ui/dropdown-menu.js +52 -0
- package/dist/components/ui/dropdown-menu.js.map +1 -0
- package/dist/components/ui/input.d.ts +3 -0
- package/dist/components/ui/input.js +7 -0
- package/dist/components/ui/input.js.map +1 -0
- package/dist/components/ui/menubar.d.ts +26 -0
- package/dist/components/ui/menubar.js +55 -0
- package/dist/components/ui/menubar.js.map +1 -0
- package/dist/components/ui/popover.d.ts +9 -0
- package/dist/components/ui/popover.js +63 -0
- package/dist/components/ui/popover.js.map +1 -0
- package/dist/components/ui/switch.d.ts +4 -0
- package/dist/components/ui/switch.js +9 -0
- package/dist/components/ui/switch.js.map +1 -0
- package/dist/components/ui/tooltip.d.ts +7 -0
- package/dist/components/ui/tooltip.js +18 -0
- package/dist/components/ui/tooltip.js.map +1 -0
- package/dist/config/config.js +79 -63
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +3 -3
- package/dist/editor/ContentTree.js +1 -1
- package/dist/editor/ContentTree.js.map +1 -1
- package/dist/editor/Editor.js +6 -2
- 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 +1 -1
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/ImageEditor.js +16 -6
- package/dist/editor/ImageEditor.js.map +1 -1
- package/dist/editor/MainLayout.js +4 -4
- package/dist/editor/MainLayout.js.map +1 -1
- package/dist/editor/MobileLayout.js +3 -3
- package/dist/editor/MobileLayout.js.map +1 -1
- package/dist/editor/PictureEditor.js +29 -15
- package/dist/editor/PictureEditor.js.map +1 -1
- package/dist/editor/Titlebar.js +6 -11
- package/dist/editor/Titlebar.js.map +1 -1
- package/dist/editor/ai/GhostWriter.js +1 -1
- package/dist/editor/ai/GhostWriter.js.map +1 -1
- package/dist/editor/client/EditorClient.d.ts +4 -2
- package/dist/editor/client/EditorClient.js +32 -11
- package/dist/editor/client/EditorClient.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +4 -1
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/client/operations.js +2 -2
- package/dist/editor/client/pageModelBuilder.js +3 -6
- package/dist/editor/client/pageModelBuilder.js.map +1 -1
- package/dist/editor/commands/itemCommands.d.ts +2 -0
- package/dist/editor/commands/itemCommands.js +180 -0
- package/dist/editor/commands/itemCommands.js.map +1 -1
- package/dist/editor/field-types/MultiLineText.js +1 -1
- package/dist/editor/field-types/MultiLineText.js.map +1 -1
- package/dist/editor/field-types/SingleLineText.js +1 -1
- package/dist/editor/field-types/SingleLineText.js.map +1 -1
- package/dist/editor/menubar/ActiveUsers.js +98 -4
- package/dist/editor/menubar/ActiveUsers.js.map +1 -1
- package/dist/editor/menubar/{ActionsMenu.d.ts → ItemActionsMenu.d.ts} +1 -1
- package/dist/editor/menubar/ItemActionsMenu.js +23 -0
- package/dist/editor/menubar/ItemActionsMenu.js.map +1 -0
- package/dist/editor/menubar/ItemLanguageVersion.js +2 -2
- package/dist/editor/menubar/ItemLanguageVersion.js.map +1 -1
- package/dist/editor/menubar/LanguageSelector.d.ts +1 -2
- package/dist/editor/menubar/LanguageSelector.js +23 -23
- package/dist/editor/menubar/LanguageSelector.js.map +1 -1
- package/dist/editor/menubar/PageSelector.js +7 -8
- package/dist/editor/menubar/PageSelector.js.map +1 -1
- package/dist/editor/menubar/PageViewerControls.js +22 -19
- package/dist/editor/menubar/PageViewerControls.js.map +1 -1
- package/dist/editor/menubar/PreviewSecondaryControls.js +2 -3
- package/dist/editor/menubar/PreviewSecondaryControls.js.map +1 -1
- package/dist/editor/menubar/User.js +1 -1
- package/dist/editor/menubar/User.js.map +1 -1
- package/dist/editor/menubar/VersionSelector.js +36 -31
- package/dist/editor/menubar/VersionSelector.js.map +1 -1
- package/dist/editor/menubar/WorkflowButton.d.ts +1 -0
- package/dist/editor/menubar/WorkflowButton.js +41 -0
- package/dist/editor/menubar/WorkflowButton.js.map +1 -0
- package/dist/editor/page-editor-chrome/FrameMenu.js +5 -5
- package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
- package/dist/editor/page-editor-chrome/SuggestionHighlightings.js +2 -2
- package/dist/editor/page-editor-chrome/SuggestionHighlightings.js.map +1 -1
- package/dist/editor/page-viewer/EditorForm.d.ts +2 -1
- package/dist/editor/page-viewer/EditorForm.js +61 -49
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/PageViewer.d.ts +2 -1
- package/dist/editor/page-viewer/PageViewer.js +28 -44
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/reviews/Comments.js +9 -9
- package/dist/editor/reviews/Comments.js.map +1 -1
- package/dist/editor/reviews/SuggestedEdit.js +3 -3
- package/dist/editor/services/contentService.d.ts +18 -0
- package/dist/editor/services/contentService.js +6 -0
- package/dist/editor/services/contentService.js.map +1 -1
- package/dist/editor/services/editService.d.ts +5 -0
- package/dist/editor/services/editService.js +4 -0
- package/dist/editor/services/editService.js.map +1 -1
- package/dist/editor/services/systemService.d.ts +2 -1
- package/dist/editor/services/systemService.js +4 -1
- package/dist/editor/services/systemService.js.map +1 -1
- package/dist/editor/sidebar/ComponentTree.js +26 -10
- package/dist/editor/sidebar/ComponentTree.js.map +1 -1
- package/dist/editor/sidebar/Divider.d.ts +6 -0
- package/dist/editor/sidebar/Divider.js +6 -0
- package/dist/editor/sidebar/Divider.js.map +1 -0
- package/dist/editor/sidebar/LeftToolbar.d.ts +1 -0
- package/dist/editor/sidebar/LeftToolbar.js +16 -0
- package/dist/editor/sidebar/LeftToolbar.js.map +1 -0
- package/dist/editor/sidebar/SEOInfo.d.ts +1 -0
- package/dist/editor/sidebar/SEOInfo.js +169 -0
- package/dist/editor/sidebar/SEOInfo.js.map +1 -0
- package/dist/editor/sidebar/Sidebar.js +1 -1
- package/dist/editor/sidebar/Sidebar.js.map +1 -1
- package/dist/editor/sidebar/SidebarView.d.ts +3 -2
- package/dist/editor/sidebar/SidebarView.js +22 -60
- package/dist/editor/sidebar/SidebarView.js.map +1 -1
- package/dist/editor/sidebar/ViewSelector.js +66 -20
- package/dist/editor/sidebar/ViewSelector.js.map +1 -1
- package/dist/editor/ui/Icons.d.ts +4 -0
- package/dist/editor/ui/Icons.js +15 -3
- package/dist/editor/ui/Icons.js.map +1 -1
- package/dist/editor/ui/Section.js +1 -1
- package/dist/editor/ui/Section.js.map +1 -1
- package/dist/editor/ui/SimpleIconButton.d.ts +1 -2
- package/dist/editor/ui/SimpleIconButton.js +8 -13
- package/dist/editor/ui/SimpleIconButton.js.map +1 -1
- package/dist/editor/ui/SimpleTabs.js +2 -2
- package/dist/editor/ui/SimpleTabs.js.map +1 -1
- package/dist/editor/ui/SimpleToolbar.js +1 -1
- package/dist/editor/ui/SimpleToolbar.js.map +1 -1
- package/dist/editor/ui/Splitter.d.ts +4 -0
- package/dist/editor/ui/Splitter.js +6 -7
- package/dist/editor/ui/Splitter.js.map +1 -1
- package/dist/editor/views/CompareView.js +16 -4
- package/dist/editor/views/CompareView.js.map +1 -1
- package/dist/editor/views/SingleEditView.d.ts +2 -1
- package/dist/editor/views/SingleEditView.js +2 -2
- package/dist/editor/views/SingleEditView.js.map +1 -1
- package/dist/page-wizard/steps/ContentStep.js +1 -1
- package/dist/page-wizard/steps/ContentStep.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/splash-screen/NewPage.js +8 -6
- package/dist/splash-screen/NewPage.js.map +1 -1
- package/dist/splash-screen/RecentPages.js +3 -8
- package/dist/splash-screen/RecentPages.js.map +1 -1
- package/dist/styles.css +1519 -543
- package/dist/tour/Tour.js +79 -10
- package/dist/tour/Tour.js.map +1 -1
- package/dist/tour/default-tour.js +55 -45
- package/dist/tour/default-tour.js.map +1 -1
- package/dist/types.d.ts +19 -1
- package/package.json +13 -5
- package/src/components/ui/badge.tsx +46 -0
- package/src/components/ui/button.tsx +3 -3
- package/src/components/ui/command.tsx +184 -0
- package/src/components/ui/dialog.tsx +143 -0
- package/src/components/ui/dropdown-menu.tsx +257 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/menubar.tsx +276 -0
- package/src/components/ui/popover.tsx +113 -0
- package/src/components/ui/switch.tsx +31 -0
- package/src/components/ui/tooltip.tsx +61 -0
- package/src/config/config.tsx +102 -65
- package/src/config/types.ts +3 -3
- package/src/editor/ContentTree.tsx +1 -1
- package/src/editor/Editor.tsx +8 -2
- package/src/editor/FieldList.tsx +2 -2
- package/src/editor/FieldListField.tsx +1 -1
- package/src/editor/ImageEditor.tsx +44 -21
- package/src/editor/MainLayout.tsx +21 -16
- package/src/editor/MobileLayout.tsx +3 -2
- package/src/editor/PictureEditor.tsx +74 -45
- package/src/editor/Titlebar.tsx +12 -24
- package/src/editor/ai/GhostWriter.tsx +1 -1
- package/src/editor/client/EditorClient.tsx +55 -13
- package/src/editor/client/editContext.ts +5 -0
- package/src/editor/client/operations.ts +2 -2
- package/src/editor/client/pageModelBuilder.ts +3 -7
- package/src/editor/commands/itemCommands.tsx +272 -0
- package/src/editor/field-types/MultiLineText.tsx +1 -1
- package/src/editor/field-types/SingleLineText.tsx +1 -1
- package/src/editor/menubar/ActiveUsers.tsx +271 -5
- package/src/editor/menubar/ItemActionsMenu.tsx +89 -0
- package/src/editor/menubar/ItemLanguageVersion.tsx +7 -5
- package/src/editor/menubar/LanguageSelector.tsx +105 -134
- package/src/editor/menubar/PageSelector.tsx +25 -27
- package/src/editor/menubar/PageViewerControls.tsx +126 -78
- package/src/editor/menubar/PreviewSecondaryControls.tsx +0 -2
- package/src/editor/menubar/User.tsx +2 -2
- package/src/editor/menubar/VersionSelector.tsx +124 -99
- package/src/editor/menubar/WorkflowButton.tsx +115 -0
- package/src/editor/page-editor-chrome/FrameMenu.tsx +5 -5
- package/src/editor/page-editor-chrome/SuggestionHighlightings.tsx +2 -2
- package/src/editor/page-viewer/EditorForm.tsx +112 -87
- package/src/editor/page-viewer/PageViewer.tsx +75 -92
- package/src/editor/page-viewer/PageViewerFrame.tsx +1 -1
- package/src/editor/reviews/Comments.tsx +19 -20
- package/src/editor/reviews/SuggestedEdit.tsx +3 -3
- package/src/editor/services/contentService.ts +28 -0
- package/src/editor/services/editService.ts +12 -0
- package/src/editor/services/systemService.ts +5 -2
- package/src/editor/sidebar/ComponentTree.tsx +34 -12
- package/src/editor/sidebar/Divider.tsx +22 -0
- package/src/editor/sidebar/LeftToolbar.tsx +36 -0
- package/src/editor/sidebar/SEOInfo.tsx +265 -0
- package/src/editor/sidebar/Sidebar.tsx +1 -0
- package/src/editor/sidebar/SidebarView.tsx +77 -111
- package/src/editor/sidebar/ViewSelector.tsx +211 -43
- package/src/editor/ui/Icons.tsx +155 -10
- package/src/editor/ui/Section.tsx +1 -1
- package/src/editor/ui/SimpleIconButton.tsx +30 -28
- package/src/editor/ui/SimpleTabs.tsx +3 -3
- package/src/editor/ui/SimpleToolbar.tsx +1 -1
- package/src/editor/ui/Splitter.tsx +14 -7
- package/src/editor/views/CompareView.tsx +23 -11
- package/src/editor/views/SingleEditView.tsx +3 -0
- package/src/page-wizard/steps/ContentStep.tsx +0 -1
- package/src/revision.ts +2 -2
- package/src/splash-screen/NewPage.tsx +18 -13
- package/src/splash-screen/RecentPages.tsx +4 -10
- package/src/tour/Tour.tsx +125 -34
- package/src/tour/default-tour.tsx +55 -45
- package/src/types.ts +21 -1
- package/styles.css +301 -1
- package/dist/editor/menubar/ActionsMenu.js +0 -49
- package/dist/editor/menubar/ActionsMenu.js.map +0 -1
- package/dist/editor/menubar/SecondaryControls.d.ts +0 -1
- package/dist/editor/menubar/SecondaryControls.js +0 -17
- package/dist/editor/menubar/SecondaryControls.js.map +0 -1
- package/src/editor/menubar/ActionsMenu.tsx +0 -94
- package/src/editor/menubar/SecondaryControls.tsx +0 -45
|
@@ -29,7 +29,7 @@ export function SuggestedEditComponent({ edit }: { edit: SuggestedEditType }) {
|
|
|
29
29
|
const [ignoreFormatting, setIgnoreFormatting] = useState(true);
|
|
30
30
|
const [clipUnchanged, setClipUnchanged] = useState(true);
|
|
31
31
|
|
|
32
|
-
const canApply = editContext?.mode === "edit" && edit.status !== "
|
|
32
|
+
const canApply = editContext?.mode === "edit" && edit.status !== "applied";
|
|
33
33
|
|
|
34
34
|
// Load the full item from the repository.
|
|
35
35
|
useEffect(() => {
|
|
@@ -247,7 +247,7 @@ export function SuggestedEditComponent({ edit }: { edit: SuggestedEditType }) {
|
|
|
247
247
|
refresh: "immediate",
|
|
248
248
|
});
|
|
249
249
|
// Update the suggestion status to "Applied" and persist the update.
|
|
250
|
-
edit.status = "
|
|
250
|
+
edit.status = "applied";
|
|
251
251
|
await createOrUpdateSuggestedEdit(edit);
|
|
252
252
|
setApplied(true);
|
|
253
253
|
setPatchWarning("");
|
|
@@ -269,7 +269,7 @@ export function SuggestedEditComponent({ edit }: { edit: SuggestedEditType }) {
|
|
|
269
269
|
rawValue: edit.newValue,
|
|
270
270
|
refresh: "immediate",
|
|
271
271
|
});
|
|
272
|
-
edit.status = "
|
|
272
|
+
edit.status = "applied";
|
|
273
273
|
await createOrUpdateSuggestedEdit(edit);
|
|
274
274
|
setApplied(true);
|
|
275
275
|
setPatchWarning("");
|
|
@@ -178,3 +178,31 @@ export async function getContentEditorWarnings(item: ItemDescriptor) {
|
|
|
178
178
|
}
|
|
179
179
|
return [];
|
|
180
180
|
}
|
|
181
|
+
|
|
182
|
+
export type ExportItemsRequest = {
|
|
183
|
+
items: ItemDescriptor[];
|
|
184
|
+
versionOption: "latest" | "all";
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
export type ExportItemsResult = {
|
|
188
|
+
yaml: string;
|
|
189
|
+
itemCount: number;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
export type ImportItemsRequest = {
|
|
193
|
+
targetItem: ItemDescriptor;
|
|
194
|
+
yaml: string;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
export type ImportItemsResult = {
|
|
198
|
+
createdItems: ItemDescriptor[];
|
|
199
|
+
itemCount: number;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
export async function exportItems(request: ExportItemsRequest) {
|
|
203
|
+
return await post<ExportItemsResult>("/alpaca/editor/exportItems", request);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export async function importItems(request: ImportItemsRequest) {
|
|
207
|
+
return await post<ImportItemsResult>("/alpaca/editor/importItems", request);
|
|
208
|
+
}
|
|
@@ -452,6 +452,18 @@ export async function getUserInfo(): Promise<UserInfo> {
|
|
|
452
452
|
return await response.json();
|
|
453
453
|
}
|
|
454
454
|
|
|
455
|
+
export type ItemVisitor = {
|
|
456
|
+
userName: string;
|
|
457
|
+
visitedAt: string; // ISO date string
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
export async function getItemVisitors(
|
|
461
|
+
item: ItemDescriptor,
|
|
462
|
+
): Promise<ExecutionResult<ItemVisitor[]>> {
|
|
463
|
+
const url = `/alpaca/editor/ItemVisitors?itemId=${item.id}&language=${item.language}&version=${item.version}`;
|
|
464
|
+
return get<ItemVisitor[]>(url);
|
|
465
|
+
}
|
|
466
|
+
|
|
455
467
|
export async function executeMoveItems(
|
|
456
468
|
items: ItemDescriptor[],
|
|
457
469
|
target: ItemDescriptor,
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import { get } from "./serviceHelper";
|
|
2
|
-
import { SystemStatus } from "../../types";
|
|
1
|
+
import { get, post } from "./serviceHelper";
|
|
2
|
+
import { SystemStatus, UserPreferences } from "../../types";
|
|
3
3
|
export function getSystemStatus() {
|
|
4
4
|
return get<SystemStatus>("/alpaca/editor/status");
|
|
5
5
|
}
|
|
6
|
+
export function saveUserPreferences(preferences: UserPreferences) {
|
|
7
|
+
return post<UserPreferences>("/alpaca/editor/savepreferences", preferences);
|
|
8
|
+
}
|
|
@@ -102,8 +102,12 @@ export function ComponentTree({}) {
|
|
|
102
102
|
data: p,
|
|
103
103
|
parent: parent,
|
|
104
104
|
type: "placeholder",
|
|
105
|
+
className: !p.editable ? "text-gray-400" : "",
|
|
106
|
+
tags: [],
|
|
105
107
|
};
|
|
108
|
+
|
|
106
109
|
node.children = p.components.map((c) => mapComponentNode(c, node));
|
|
110
|
+
|
|
107
111
|
return node;
|
|
108
112
|
}
|
|
109
113
|
|
|
@@ -111,16 +115,27 @@ export function ComponentTree({}) {
|
|
|
111
115
|
c: Component,
|
|
112
116
|
parent: CustomTreeNode,
|
|
113
117
|
): CustomTreeNode[] {
|
|
114
|
-
if (!c?.placeholders)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
118
|
+
if (!c?.placeholders) {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Show all placeholders (both editable and non-editable)
|
|
123
|
+
const allPlaceholders = c.placeholders;
|
|
124
|
+
|
|
125
|
+
if (allPlaceholders.length > 1) {
|
|
126
|
+
return allPlaceholders.map((x) => mapPlaceholderNode(x, parent));
|
|
127
|
+
} else if (allPlaceholders.length > 0) {
|
|
128
|
+
const placeholder = allPlaceholders[0]!;
|
|
129
|
+
// For single placeholders, always map the components directly (don't show placeholder node)
|
|
130
|
+
return placeholder.components.map((c) => {
|
|
131
|
+
const componentNode = mapComponentNode(c, parent);
|
|
132
|
+
// Apply gray styling to components in non-editable placeholders
|
|
133
|
+
if (!placeholder.editable) {
|
|
134
|
+
componentNode.className =
|
|
135
|
+
(componentNode.className || "") + " text-gray-400";
|
|
136
|
+
}
|
|
137
|
+
return componentNode;
|
|
138
|
+
});
|
|
124
139
|
}
|
|
125
140
|
return [];
|
|
126
141
|
}
|
|
@@ -134,6 +149,8 @@ export function ComponentTree({}) {
|
|
|
134
149
|
componentId: c.id,
|
|
135
150
|
label: c.name,
|
|
136
151
|
icon: c.datasourceItem?.icon ? (
|
|
152
|
+
<img src={c.datasourceItem.icon} width={16} height={16} />
|
|
153
|
+
) : c.icon ? (
|
|
137
154
|
<img src={c.icon} width={16} height={16} />
|
|
138
155
|
) : (
|
|
139
156
|
"pi pi-stop"
|
|
@@ -237,6 +254,7 @@ export function ComponentTree({}) {
|
|
|
237
254
|
children: [],
|
|
238
255
|
type: "placeholder",
|
|
239
256
|
});
|
|
257
|
+
|
|
240
258
|
const dict: { [key: string]: CustomTreeNode } = {};
|
|
241
259
|
createMap(treeNodes, dict);
|
|
242
260
|
|
|
@@ -248,7 +266,9 @@ export function ComponentTree({}) {
|
|
|
248
266
|
type: "other",
|
|
249
267
|
};
|
|
250
268
|
|
|
251
|
-
if (otherNode.children?.length)
|
|
269
|
+
if (otherNode.children?.length) {
|
|
270
|
+
treeNodes.push(otherNode);
|
|
271
|
+
}
|
|
252
272
|
|
|
253
273
|
const pageNode: CustomTreeNode = {
|
|
254
274
|
key: "page",
|
|
@@ -259,7 +279,9 @@ export function ComponentTree({}) {
|
|
|
259
279
|
type: "page",
|
|
260
280
|
};
|
|
261
281
|
|
|
262
|
-
|
|
282
|
+
const finalNodes = [pageNode, ...treeNodes];
|
|
283
|
+
|
|
284
|
+
setRootNodes(finalNodes);
|
|
263
285
|
setNodeDictionary(dict);
|
|
264
286
|
}, [page]);
|
|
265
287
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { cn } from "../../lib/utils";
|
|
3
|
+
|
|
4
|
+
interface DividerProps {
|
|
5
|
+
className?: string;
|
|
6
|
+
orientation?: "horizontal" | "vertical";
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function Divider({
|
|
10
|
+
className,
|
|
11
|
+
orientation = "horizontal",
|
|
12
|
+
}: DividerProps) {
|
|
13
|
+
return (
|
|
14
|
+
<div
|
|
15
|
+
className={cn(
|
|
16
|
+
"border-gray-3",
|
|
17
|
+
orientation === "horizontal" ? "w-full border-b" : "h-full border-r",
|
|
18
|
+
className,
|
|
19
|
+
)}
|
|
20
|
+
/>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { cn } from "../../lib/utils";
|
|
3
|
+
import { DashboardIcon } from "../ui/Icons";
|
|
4
|
+
import { useEditContext } from "../client/editContext";
|
|
5
|
+
import { ViewSelector } from "./ViewSelector";
|
|
6
|
+
import { Divider } from "./Divider";
|
|
7
|
+
|
|
8
|
+
export function LeftToolbar() {
|
|
9
|
+
const editContext = useEditContext();
|
|
10
|
+
const showViewNames =
|
|
11
|
+
editContext?.userInfo.preferences?.showViewNames ?? false;
|
|
12
|
+
return (
|
|
13
|
+
<div
|
|
14
|
+
className={cn(
|
|
15
|
+
"border-gray-4 flex items-center gap-3 border-r bg-white p-2",
|
|
16
|
+
editContext?.isMobile
|
|
17
|
+
? "scrollbar-hide flex-row overflow-x-auto"
|
|
18
|
+
: `flex-col ${showViewNames ? "w-18" : "w-11"}`,
|
|
19
|
+
)}
|
|
20
|
+
>
|
|
21
|
+
{/* Dashboard/Home button */}
|
|
22
|
+
<a
|
|
23
|
+
className="cursor-pointer rounded-sm bg-gradient-to-r from-blue-500 to-purple-500 transition-all hover:scale-105 hover:bg-gradient-to-r hover:from-blue-600 hover:to-purple-600"
|
|
24
|
+
onClick={() => {
|
|
25
|
+
editContext?.switchView("splash-screen");
|
|
26
|
+
}}
|
|
27
|
+
>
|
|
28
|
+
<DashboardIcon />
|
|
29
|
+
</a>
|
|
30
|
+
|
|
31
|
+
<Divider />
|
|
32
|
+
|
|
33
|
+
<ViewSelector />
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { Button } from "primereact/button";
|
|
3
|
+
import { Divider } from "primereact/divider";
|
|
4
|
+
import { useEditContext } from "../client/editContext";
|
|
5
|
+
|
|
6
|
+
interface SEOData {
|
|
7
|
+
title?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
keywords?: string[];
|
|
10
|
+
url?: string;
|
|
11
|
+
isAnalyzing?: boolean;
|
|
12
|
+
lastAnalyzed?: Date;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function SEOInfo() {
|
|
16
|
+
const [seoData, setSeoData] = useState<SEOData>({});
|
|
17
|
+
const [isAnalyzing, setIsAnalyzing] = useState(false);
|
|
18
|
+
const editContext = useEditContext();
|
|
19
|
+
|
|
20
|
+
const runSEOAnalysis = async () => {
|
|
21
|
+
if (!editContext) {
|
|
22
|
+
console.error("No edit context available");
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
setIsAnalyzing(true);
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
// Get current page content for analysis
|
|
30
|
+
|
|
31
|
+
const item = editContext.item || editContext.contentEditorItem;
|
|
32
|
+
|
|
33
|
+
console.log("Starting SEO Analysis...", { item });
|
|
34
|
+
|
|
35
|
+
// Extract title and description from page fields if available
|
|
36
|
+
const titleField = item?.fields?.find((f) =>
|
|
37
|
+
f.name?.toLowerCase().includes("title"),
|
|
38
|
+
);
|
|
39
|
+
const descriptionField = item?.fields?.find(
|
|
40
|
+
(f) =>
|
|
41
|
+
f.name?.toLowerCase().includes("description") ||
|
|
42
|
+
f.name?.toLowerCase().includes("summary"),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// Update SEO data immediately with current content (this makes the button responsive)
|
|
46
|
+
const currentTitle = (titleField?.value as string) || item?.name || "";
|
|
47
|
+
const currentDescription = (descriptionField?.value as string) || "";
|
|
48
|
+
const currentUrl = item?.path || "";
|
|
49
|
+
|
|
50
|
+
setSeoData({
|
|
51
|
+
title: currentTitle,
|
|
52
|
+
description: currentDescription,
|
|
53
|
+
keywords: [], // Will be populated by AI analysis
|
|
54
|
+
url: currentUrl,
|
|
55
|
+
lastAnalyzed: new Date(),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
console.log("Updated SEO data with current content:", {
|
|
59
|
+
title: currentTitle,
|
|
60
|
+
description: currentDescription,
|
|
61
|
+
url: currentUrl,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Show immediate feedback
|
|
65
|
+
editContext.showToast({
|
|
66
|
+
severity: "info",
|
|
67
|
+
summary: "SEO Analysis Started",
|
|
68
|
+
detail: "Analyzing current page content...",
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Try to send AI analysis request
|
|
72
|
+
const aiServiceUrl =
|
|
73
|
+
editContext.configuration.services.aiService.promptUrl;
|
|
74
|
+
console.log("AI Service URL:", aiServiceUrl);
|
|
75
|
+
|
|
76
|
+
if (!aiServiceUrl) {
|
|
77
|
+
throw new Error("AI service URL not configured");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Prepare SEO optimization prompt in correct message format
|
|
81
|
+
const seoPrompt = `Analyze the current page for SEO optimization and provide specific recommendations.
|
|
82
|
+
|
|
83
|
+
Current page details:
|
|
84
|
+
- Page: ${item?.name || "Unknown"}
|
|
85
|
+
- Path: ${item?.path || "Unknown"}
|
|
86
|
+
- Title: ${currentTitle || "No title found"}
|
|
87
|
+
- Description: ${currentDescription || "No description found"}
|
|
88
|
+
|
|
89
|
+
Please analyze and provide:
|
|
90
|
+
1. Title optimization suggestions (should be 50-60 characters)
|
|
91
|
+
2. Meta description recommendations (120-155 characters)
|
|
92
|
+
3. Keyword analysis and suggestions
|
|
93
|
+
4. Content structure improvements
|
|
94
|
+
5. Overall SEO recommendations
|
|
95
|
+
|
|
96
|
+
Focus on actionable insights for improving search engine visibility.`;
|
|
97
|
+
|
|
98
|
+
// Format as proper message for AI service
|
|
99
|
+
const messages = [
|
|
100
|
+
{
|
|
101
|
+
id: Date.now(),
|
|
102
|
+
content: seoPrompt,
|
|
103
|
+
role: "user",
|
|
104
|
+
name: "user",
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
// Send to AI service using correct format
|
|
109
|
+
const response = await fetch(aiServiceUrl, {
|
|
110
|
+
method: "POST",
|
|
111
|
+
headers: {
|
|
112
|
+
"Content-Type": "application/json",
|
|
113
|
+
},
|
|
114
|
+
body: JSON.stringify({
|
|
115
|
+
profileId: "Editor", // Use default editor profile
|
|
116
|
+
messages: messages,
|
|
117
|
+
selection: editContext.selection || [],
|
|
118
|
+
addSelectedComponents: true,
|
|
119
|
+
selectedText: null,
|
|
120
|
+
model: null, // Let AI service use default model
|
|
121
|
+
sessionId: null,
|
|
122
|
+
itemid: editContext.currentItemDescriptor?.id,
|
|
123
|
+
language: editContext.currentItemDescriptor?.language,
|
|
124
|
+
version: editContext.currentItemDescriptor?.version,
|
|
125
|
+
}),
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
console.log("AI Service Response:", response.status, response.statusText);
|
|
129
|
+
|
|
130
|
+
if (response.ok) {
|
|
131
|
+
// Handle streaming response like the AI terminal does
|
|
132
|
+
if (response.body) {
|
|
133
|
+
const reader = response.body.getReader();
|
|
134
|
+
const decoder = new TextDecoder();
|
|
135
|
+
let buffer = "";
|
|
136
|
+
|
|
137
|
+
while (true) {
|
|
138
|
+
const { done, value } = await reader.read();
|
|
139
|
+
if (done) break;
|
|
140
|
+
|
|
141
|
+
buffer += decoder.decode(value, { stream: true });
|
|
142
|
+
const lines = buffer.split("\n");
|
|
143
|
+
|
|
144
|
+
if (lines.length > 0) {
|
|
145
|
+
buffer = lines.pop() || "";
|
|
146
|
+
|
|
147
|
+
for (let line of lines) {
|
|
148
|
+
if (line.trim() === "") continue;
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
const jsonData = JSON.parse(line);
|
|
152
|
+
console.log("AI Response chunk:", jsonData);
|
|
153
|
+
|
|
154
|
+
// The AI response contains messages - this will show up in the comments panel
|
|
155
|
+
if (jsonData.messages && jsonData.messages.length > 0) {
|
|
156
|
+
// Final response received
|
|
157
|
+
console.log("Final AI response:", jsonData);
|
|
158
|
+
}
|
|
159
|
+
} catch (e) {
|
|
160
|
+
console.error("Error parsing AI response line:", line, e);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Process any remaining buffer content
|
|
167
|
+
if (buffer.trim() !== "") {
|
|
168
|
+
try {
|
|
169
|
+
const jsonData = JSON.parse(buffer);
|
|
170
|
+
console.log("Final AI buffer:", jsonData);
|
|
171
|
+
} catch (e) {
|
|
172
|
+
console.error("Error parsing final buffer:", e);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Show success message
|
|
178
|
+
editContext.showToast({
|
|
179
|
+
severity: "success",
|
|
180
|
+
summary: "SEO Analysis Complete",
|
|
181
|
+
detail:
|
|
182
|
+
"AI analysis completed. Check the Comments panel for SEO recommendations.",
|
|
183
|
+
});
|
|
184
|
+
} else {
|
|
185
|
+
const errorText = await response.text();
|
|
186
|
+
throw new Error(
|
|
187
|
+
`AI service responded with ${response.status}: ${errorText}`,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error("SEO analysis failed:", error);
|
|
192
|
+
editContext.showToast({
|
|
193
|
+
severity: "warn",
|
|
194
|
+
summary: "SEO Analysis Partial",
|
|
195
|
+
detail: "Current page data extracted. AI analysis unavailable.",
|
|
196
|
+
});
|
|
197
|
+
} finally {
|
|
198
|
+
setIsAnalyzing(false);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
return (
|
|
203
|
+
<div className="space-y-4 p-4">
|
|
204
|
+
{/* Analysis Button */}
|
|
205
|
+
<Button
|
|
206
|
+
label="Run SEO Analysis"
|
|
207
|
+
icon="pi pi-search"
|
|
208
|
+
onClick={runSEOAnalysis}
|
|
209
|
+
loading={isAnalyzing}
|
|
210
|
+
className="w-full"
|
|
211
|
+
severity="success"
|
|
212
|
+
/>
|
|
213
|
+
|
|
214
|
+
<Divider />
|
|
215
|
+
|
|
216
|
+
{/* SEO Information */}
|
|
217
|
+
<div className="space-y-3">
|
|
218
|
+
<div>
|
|
219
|
+
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
220
|
+
Page Title
|
|
221
|
+
</label>
|
|
222
|
+
<div className="rounded bg-gray-50 p-2 text-sm text-gray-600">
|
|
223
|
+
{seoData.title || "No title detected"}
|
|
224
|
+
</div>
|
|
225
|
+
{seoData.title && (
|
|
226
|
+
<div className="mt-1 text-xs text-gray-500">
|
|
227
|
+
Length: {seoData.title.length}/60 characters
|
|
228
|
+
</div>
|
|
229
|
+
)}
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
<div>
|
|
233
|
+
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
234
|
+
Meta Description
|
|
235
|
+
</label>
|
|
236
|
+
<div className="rounded bg-gray-50 p-2 text-sm text-gray-600">
|
|
237
|
+
{seoData.description || "No description detected"}
|
|
238
|
+
</div>
|
|
239
|
+
{seoData.description && (
|
|
240
|
+
<div className="mt-1 text-xs text-gray-500">
|
|
241
|
+
Length: {seoData.description.length}/155 characters
|
|
242
|
+
</div>
|
|
243
|
+
)}
|
|
244
|
+
</div>
|
|
245
|
+
|
|
246
|
+
<div>
|
|
247
|
+
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
248
|
+
Keywords
|
|
249
|
+
</label>
|
|
250
|
+
<div className="rounded bg-gray-50 p-2 text-sm text-gray-600">
|
|
251
|
+
{seoData.keywords && seoData.keywords.length > 0
|
|
252
|
+
? seoData.keywords.join(", ")
|
|
253
|
+
: "No keywords detected"}
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
256
|
+
|
|
257
|
+
{seoData.lastAnalyzed && (
|
|
258
|
+
<div className="border-t pt-2 text-xs text-gray-500">
|
|
259
|
+
Last analyzed: {seoData.lastAnalyzed.toLocaleString()}
|
|
260
|
+
</div>
|
|
261
|
+
)}
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
);
|
|
265
|
+
}
|