@alpaca-editor/core 1.0.3896 → 1.0.3897
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/ActionButton.js +2 -2
- package/dist/components/ActionButton.js.map +1 -1
- package/dist/components/ui/button.js +2 -2
- package/dist/components/ui/button.js.map +1 -1
- package/dist/config/config.js +37 -22
- package/dist/config/config.js.map +1 -1
- package/dist/editor/FieldListField.js +1 -1
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/Titlebar.js +2 -1
- package/dist/editor/Titlebar.js.map +1 -1
- package/dist/editor/client/EditorClient.d.ts +18 -2
- package/dist/editor/client/EditorClient.js +117 -1
- 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/itemsRepository.js +1 -1
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/client/operations.js +1 -1
- package/dist/editor/client/operations.js.map +1 -1
- package/dist/editor/control-center/About.d.ts +1 -0
- package/dist/editor/control-center/About.js +8 -0
- package/dist/editor/control-center/About.js.map +1 -0
- package/dist/editor/control-center/ControlCenterMenu.js +3 -0
- package/dist/editor/control-center/ControlCenterMenu.js.map +1 -1
- package/dist/editor/control-center/Info.d.ts +1 -0
- package/dist/editor/control-center/Info.js +10 -0
- package/dist/editor/control-center/Info.js.map +1 -0
- package/dist/editor/control-center/QuotaInfo.d.ts +1 -0
- package/dist/editor/control-center/QuotaInfo.js +52 -0
- package/dist/editor/control-center/QuotaInfo.js.map +1 -0
- package/dist/editor/control-center/Status.js +69 -2
- package/dist/editor/control-center/Status.js.map +1 -1
- package/dist/editor/page-editor-chrome/FieldActionIndicator.js +7 -6
- package/dist/editor/page-editor-chrome/FieldActionIndicator.js.map +1 -1
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +6 -1
- package/dist/editor/services/aiService.js +4 -0
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/sidebar/ComponentTree.js +1 -1
- package/dist/editor/sidebar/ComponentTree.js.map +1 -1
- package/dist/editor/sidebar/ViewSelector.js +9 -4
- package/dist/editor/sidebar/ViewSelector.js.map +1 -1
- package/dist/editor/ui/Icons.d.ts +17 -1
- package/dist/editor/ui/Icons.js +23 -5
- package/dist/editor/ui/Icons.js.map +1 -1
- package/dist/editor/ui/SimpleMenu.js +1 -1
- package/dist/editor/ui/SimpleMenu.js.map +1 -1
- package/dist/images/wizard-bg.png +0 -0
- package/dist/page-wizard/WizardBox.d.ts +8 -0
- package/dist/page-wizard/WizardBox.js +6 -0
- package/dist/page-wizard/WizardBox.js.map +1 -0
- package/dist/page-wizard/WizardBoxConnector.d.ts +3 -0
- package/dist/page-wizard/WizardBoxConnector.js +6 -0
- package/dist/page-wizard/WizardBoxConnector.js.map +1 -0
- package/dist/page-wizard/WizardSteps.js +63 -16
- package/dist/page-wizard/WizardSteps.js.map +1 -1
- package/dist/page-wizard/steps/CollectStep.js +16 -21
- package/dist/page-wizard/steps/CollectStep.js.map +1 -1
- package/dist/page-wizard/steps/ComponentTypesSelector.js +50 -45
- package/dist/page-wizard/steps/ComponentTypesSelector.js.map +1 -1
- package/dist/page-wizard/steps/CreatePageAndLayoutStep.js +21 -28
- package/dist/page-wizard/steps/CreatePageAndLayoutStep.js.map +1 -1
- package/dist/page-wizard/steps/usePageCreator.js +41 -12
- package/dist/page-wizard/steps/usePageCreator.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/styles.css +144 -49
- package/images/wizard-bg.png +0 -0
- package/package.json +1 -1
- package/src/components/ActionButton.tsx +6 -8
- package/src/components/ui/button.tsx +2 -2
- package/src/config/config.tsx +47 -22
- package/src/editor/FieldListField.tsx +2 -2
- package/src/editor/Titlebar.tsx +2 -1
- package/src/editor/client/EditorClient.tsx +153 -8
- package/src/editor/client/editContext.ts +4 -2
- package/src/editor/client/itemsRepository.ts +1 -1
- package/src/editor/client/operations.ts +1 -1
- package/src/editor/control-center/About.tsx +342 -0
- package/src/editor/control-center/ControlCenterMenu.tsx +5 -0
- package/src/editor/control-center/Info.tsx +104 -0
- package/src/editor/control-center/QuotaInfo.tsx +171 -0
- package/src/editor/control-center/Status.tsx +108 -2
- package/src/editor/page-editor-chrome/FieldActionIndicator.tsx +20 -5
- package/src/editor/page-viewer/PageViewer.tsx +1 -1
- package/src/editor/services/aiService.ts +13 -1
- package/src/editor/sidebar/ComponentTree.tsx +1 -1
- package/src/editor/sidebar/ViewSelector.tsx +10 -11
- package/src/editor/ui/Icons.tsx +147 -26
- package/src/editor/ui/SimpleMenu.tsx +1 -1
- package/src/page-wizard/WizardBox.tsx +40 -0
- package/src/page-wizard/WizardBoxConnector.tsx +21 -0
- package/src/page-wizard/WizardSteps.tsx +134 -84
- package/src/page-wizard/steps/CollectStep.tsx +129 -67
- package/src/page-wizard/steps/ComponentTypesSelector.tsx +32 -11
- package/src/page-wizard/steps/CreatePageAndLayoutStep.tsx +47 -30
- package/src/page-wizard/steps/usePageCreator.ts +40 -14
- package/src/revision.ts +2 -2
- package/styles.css +11 -8
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useEditContext } from "../client/editContext";
|
|
3
|
+
|
|
4
|
+
export function Info() {
|
|
5
|
+
const editContext = useEditContext();
|
|
6
|
+
const user = editContext?.user;
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<div className="space-y-6 p-4">
|
|
10
|
+
<div className="space-y-4">
|
|
11
|
+
<h2 className="text-xl font-semibold text-gray-800">
|
|
12
|
+
System Information
|
|
13
|
+
</h2>
|
|
14
|
+
|
|
15
|
+
{/* User Information */}
|
|
16
|
+
{user && (
|
|
17
|
+
<div className="rounded-lg border border-gray-200 bg-white p-4">
|
|
18
|
+
<h3 className="mb-3 text-lg font-medium text-gray-700">
|
|
19
|
+
User Information
|
|
20
|
+
</h3>
|
|
21
|
+
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
22
|
+
<div>
|
|
23
|
+
<label className="text-sm font-medium text-gray-500">
|
|
24
|
+
Name
|
|
25
|
+
</label>
|
|
26
|
+
<p className="text-gray-900">{user.displayName || user.name}</p>
|
|
27
|
+
</div>
|
|
28
|
+
<div>
|
|
29
|
+
<label className="text-sm font-medium text-gray-500">
|
|
30
|
+
Email
|
|
31
|
+
</label>
|
|
32
|
+
<p className="text-gray-900">{user.email || "Not available"}</p>
|
|
33
|
+
</div>
|
|
34
|
+
<div>
|
|
35
|
+
<label className="text-sm font-medium text-gray-500">
|
|
36
|
+
User Type
|
|
37
|
+
</label>
|
|
38
|
+
<p className="text-gray-900">
|
|
39
|
+
{user.isLimitedPreviewUser
|
|
40
|
+
? "Limited Preview User"
|
|
41
|
+
: "Regular User"}
|
|
42
|
+
</p>
|
|
43
|
+
</div>
|
|
44
|
+
<div>
|
|
45
|
+
<label className="text-sm font-medium text-gray-500">
|
|
46
|
+
Session ID
|
|
47
|
+
</label>
|
|
48
|
+
<p className="font-mono text-xs text-gray-900">
|
|
49
|
+
{editContext?.sessionId}
|
|
50
|
+
</p>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
)}
|
|
55
|
+
|
|
56
|
+
{/* Editor Information */}
|
|
57
|
+
<div className="rounded-lg border border-gray-200 bg-white p-4">
|
|
58
|
+
<h3 className="mb-3 text-lg font-medium text-gray-700">
|
|
59
|
+
Editor Information
|
|
60
|
+
</h3>
|
|
61
|
+
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
62
|
+
<div>
|
|
63
|
+
<label className="text-sm font-medium text-gray-500">
|
|
64
|
+
Active Sessions
|
|
65
|
+
</label>
|
|
66
|
+
<p className="text-gray-900">
|
|
67
|
+
{editContext?.activeSessions?.length || 0}
|
|
68
|
+
</p>
|
|
69
|
+
</div>
|
|
70
|
+
<div>
|
|
71
|
+
<label className="text-sm font-medium text-gray-500">
|
|
72
|
+
Mobile View
|
|
73
|
+
</label>
|
|
74
|
+
<p className="text-gray-900">
|
|
75
|
+
{editContext?.isMobile ? "Yes" : "No"}
|
|
76
|
+
</p>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
{/* Technical Information */}
|
|
82
|
+
<div className="rounded-lg border border-gray-200 bg-white p-4">
|
|
83
|
+
<h3 className="mb-3 text-lg font-medium text-gray-700">
|
|
84
|
+
Technical Information
|
|
85
|
+
</h3>
|
|
86
|
+
<div className="space-y-2 text-sm">
|
|
87
|
+
<div>
|
|
88
|
+
<span className="font-medium text-gray-500">User Agent:</span>
|
|
89
|
+
<p className="mt-1 font-mono text-xs break-all text-gray-900">
|
|
90
|
+
{navigator.userAgent}
|
|
91
|
+
</p>
|
|
92
|
+
</div>
|
|
93
|
+
<div>
|
|
94
|
+
<span className="font-medium text-gray-500">Viewport Size:</span>
|
|
95
|
+
<p className="text-gray-900">
|
|
96
|
+
{window.innerWidth} × {window.innerHeight}
|
|
97
|
+
</p>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useEditContext } from "../client/editContext";
|
|
3
|
+
|
|
4
|
+
export function QuotaInfo() {
|
|
5
|
+
const editContext = useEditContext();
|
|
6
|
+
const quotaInfo = editContext?.quotaInfo;
|
|
7
|
+
|
|
8
|
+
const formatQuotaPercentage = (used: number, limit: number) => {
|
|
9
|
+
if (limit === 0) return "No limit";
|
|
10
|
+
const percentage = Math.round((used / limit) * 100);
|
|
11
|
+
return `${percentage}%`;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const getQuotaStatus = (used: number, limit: number) => {
|
|
15
|
+
if (limit === 0) return "unlimited";
|
|
16
|
+
const percentage = (used / limit) * 100;
|
|
17
|
+
if (percentage >= 100) return "exceeded";
|
|
18
|
+
if (percentage >= 90) return "warning";
|
|
19
|
+
if (percentage >= 75) return "caution";
|
|
20
|
+
return "ok";
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
if (!quotaInfo) {
|
|
24
|
+
return (
|
|
25
|
+
<div className="space-y-6 p-4">
|
|
26
|
+
<div className="rounded-lg border border-gray-200 bg-white p-4">
|
|
27
|
+
<h2 className="mb-3 text-xl font-semibold text-gray-800">
|
|
28
|
+
AI Usage Quota
|
|
29
|
+
</h2>
|
|
30
|
+
<p className="text-gray-600">No quota information available</p>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div className="space-y-6 p-4">
|
|
38
|
+
<div className="rounded-lg border border-gray-200 bg-white p-4">
|
|
39
|
+
<h2 className="mb-3 text-xl font-semibold text-gray-800">
|
|
40
|
+
AI Usage Quota
|
|
41
|
+
</h2>
|
|
42
|
+
<div className="space-y-4">
|
|
43
|
+
{/* Token Usage */}
|
|
44
|
+
<div>
|
|
45
|
+
<div className="mb-2 flex items-center justify-between">
|
|
46
|
+
<label className="text-sm font-medium text-gray-500">
|
|
47
|
+
Token Usage
|
|
48
|
+
</label>
|
|
49
|
+
<span className="text-sm text-gray-600">
|
|
50
|
+
{quotaInfo.usage.tokens.toLocaleString()} /{" "}
|
|
51
|
+
{quotaInfo.limits.absoluteTokens > 0
|
|
52
|
+
? quotaInfo.limits.absoluteTokens.toLocaleString()
|
|
53
|
+
: "∞"}
|
|
54
|
+
</span>
|
|
55
|
+
</div>
|
|
56
|
+
{quotaInfo.limits.absoluteTokens > 0 && (
|
|
57
|
+
<div className="h-2 w-full rounded-full bg-gray-200">
|
|
58
|
+
<div
|
|
59
|
+
className={`h-2 rounded-full ${
|
|
60
|
+
getQuotaStatus(
|
|
61
|
+
quotaInfo.usage.tokens,
|
|
62
|
+
quotaInfo.limits.absoluteTokens,
|
|
63
|
+
) === "exceeded"
|
|
64
|
+
? "bg-red-500"
|
|
65
|
+
: getQuotaStatus(
|
|
66
|
+
quotaInfo.usage.tokens,
|
|
67
|
+
quotaInfo.limits.absoluteTokens,
|
|
68
|
+
) === "warning"
|
|
69
|
+
? "bg-orange-500"
|
|
70
|
+
: getQuotaStatus(
|
|
71
|
+
quotaInfo.usage.tokens,
|
|
72
|
+
quotaInfo.limits.absoluteTokens,
|
|
73
|
+
) === "caution"
|
|
74
|
+
? "bg-yellow-500"
|
|
75
|
+
: "bg-green-500"
|
|
76
|
+
}`}
|
|
77
|
+
style={{
|
|
78
|
+
width: `${Math.min(100, (quotaInfo.usage.tokens / quotaInfo.limits.absoluteTokens) * 100)}%`,
|
|
79
|
+
}}
|
|
80
|
+
></div>
|
|
81
|
+
</div>
|
|
82
|
+
)}
|
|
83
|
+
<p className="mt-1 text-xs text-gray-500">
|
|
84
|
+
{formatQuotaPercentage(
|
|
85
|
+
quotaInfo.usage.tokens,
|
|
86
|
+
quotaInfo.limits.absoluteTokens,
|
|
87
|
+
)}{" "}
|
|
88
|
+
used
|
|
89
|
+
</p>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
{/* Image Usage */}
|
|
93
|
+
<div>
|
|
94
|
+
<div className="mb-2 flex items-center justify-between">
|
|
95
|
+
<label className="text-sm font-medium text-gray-500">
|
|
96
|
+
Image Usage
|
|
97
|
+
</label>
|
|
98
|
+
<span className="text-sm text-gray-600">
|
|
99
|
+
{quotaInfo.usage.images.toLocaleString()} /{" "}
|
|
100
|
+
{quotaInfo.limits.absoluteImages > 0
|
|
101
|
+
? quotaInfo.limits.absoluteImages.toLocaleString()
|
|
102
|
+
: "∞"}
|
|
103
|
+
</span>
|
|
104
|
+
</div>
|
|
105
|
+
{quotaInfo.limits.absoluteImages > 0 && (
|
|
106
|
+
<div className="h-2 w-full rounded-full bg-gray-200">
|
|
107
|
+
<div
|
|
108
|
+
className={`h-2 rounded-full ${
|
|
109
|
+
getQuotaStatus(
|
|
110
|
+
quotaInfo.usage.images,
|
|
111
|
+
quotaInfo.limits.absoluteImages,
|
|
112
|
+
) === "exceeded"
|
|
113
|
+
? "bg-red-500"
|
|
114
|
+
: getQuotaStatus(
|
|
115
|
+
quotaInfo.usage.images,
|
|
116
|
+
quotaInfo.limits.absoluteImages,
|
|
117
|
+
) === "warning"
|
|
118
|
+
? "bg-orange-500"
|
|
119
|
+
: getQuotaStatus(
|
|
120
|
+
quotaInfo.usage.images,
|
|
121
|
+
quotaInfo.limits.absoluteImages,
|
|
122
|
+
) === "caution"
|
|
123
|
+
? "bg-yellow-500"
|
|
124
|
+
: "bg-green-500"
|
|
125
|
+
}`}
|
|
126
|
+
style={{
|
|
127
|
+
width: `${Math.min(100, (quotaInfo.usage.images / quotaInfo.limits.absoluteImages) * 100)}%`,
|
|
128
|
+
}}
|
|
129
|
+
></div>
|
|
130
|
+
</div>
|
|
131
|
+
)}
|
|
132
|
+
<p className="mt-1 text-xs text-gray-500">
|
|
133
|
+
{formatQuotaPercentage(
|
|
134
|
+
quotaInfo.usage.images,
|
|
135
|
+
quotaInfo.limits.absoluteImages,
|
|
136
|
+
)}{" "}
|
|
137
|
+
used
|
|
138
|
+
</p>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
{/* Quota Status Summary */}
|
|
142
|
+
<div className="mt-4 rounded-lg bg-gray-50 p-3">
|
|
143
|
+
<h4 className="mb-2 text-sm font-medium text-gray-700">
|
|
144
|
+
Quota Status
|
|
145
|
+
</h4>
|
|
146
|
+
<div className="space-y-1 text-sm">
|
|
147
|
+
{editContext?.isQuotaExceeded && (
|
|
148
|
+
<p className="font-medium text-red-600">
|
|
149
|
+
⚠️ Quota limits have been exceeded
|
|
150
|
+
</p>
|
|
151
|
+
)}
|
|
152
|
+
{editContext?.getQuotaWarningMessage &&
|
|
153
|
+
editContext.getQuotaWarningMessage() &&
|
|
154
|
+
!editContext.isQuotaExceeded && (
|
|
155
|
+
<p className="text-orange-600">
|
|
156
|
+
⚠️ {editContext.getQuotaWarningMessage()}
|
|
157
|
+
</p>
|
|
158
|
+
)}
|
|
159
|
+
{!editContext?.isQuotaExceeded &&
|
|
160
|
+
!editContext?.getQuotaWarningMessage?.() && (
|
|
161
|
+
<p className="text-green-600">
|
|
162
|
+
✅ All quotas are within limits
|
|
163
|
+
</p>
|
|
164
|
+
)}
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
);
|
|
171
|
+
}
|
|
@@ -1,7 +1,113 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { useRouter } from "next/navigation";
|
|
3
|
+
import { usePathname, useSearchParams } from "next/navigation";
|
|
4
|
+
import { useEditContext } from "../client/editContext";
|
|
5
|
+
import { SimpleMenu } from "../ui/SimpleMenu";
|
|
6
|
+
import { Splitter, SplitterPanel } from "../ui/Splitter";
|
|
7
|
+
|
|
1
8
|
export function Status() {
|
|
9
|
+
const editContext = useEditContext();
|
|
10
|
+
const config = editContext?.configuration;
|
|
11
|
+
const searchParams = useSearchParams();
|
|
12
|
+
const urlActiveItemKey = searchParams.get("ccpanel");
|
|
13
|
+
|
|
14
|
+
// Get the first available panel as default
|
|
15
|
+
const defaultActiveItemKey = config?.controlCenter.groups?.flatMap(
|
|
16
|
+
(x) => x.panels,
|
|
17
|
+
)?.[0]?.id;
|
|
18
|
+
|
|
19
|
+
const [activeItemKey, setActiveItemKey] = useState<string | null>(
|
|
20
|
+
urlActiveItemKey || defaultActiveItemKey || null,
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const router = useRouter();
|
|
24
|
+
const pathname = usePathname();
|
|
25
|
+
|
|
26
|
+
const updateUrl = (key: string | null) => {
|
|
27
|
+
if (urlActiveItemKey === key) return;
|
|
28
|
+
|
|
29
|
+
const current = new URLSearchParams(Array.from(searchParams.entries()));
|
|
30
|
+
|
|
31
|
+
if (key) {
|
|
32
|
+
current.set("ccpanel", key);
|
|
33
|
+
} else {
|
|
34
|
+
current.delete("ccpanel");
|
|
35
|
+
}
|
|
36
|
+
router.push(`${pathname}?${current.toString()}`, { scroll: false });
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Set default active item when config loads and no active item is set
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!activeItemKey && defaultActiveItemKey) {
|
|
42
|
+
setActiveItemKey(defaultActiveItemKey);
|
|
43
|
+
}
|
|
44
|
+
}, [defaultActiveItemKey, activeItemKey]);
|
|
45
|
+
|
|
46
|
+
const items = config?.controlCenter.groups.map((group) => {
|
|
47
|
+
return {
|
|
48
|
+
id: group.title,
|
|
49
|
+
label: group.title,
|
|
50
|
+
icon: group.icon,
|
|
51
|
+
items:
|
|
52
|
+
group.panels.map((panel) => ({
|
|
53
|
+
id: panel.id,
|
|
54
|
+
label: panel.title,
|
|
55
|
+
})) || [],
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Find the currently selected panel content
|
|
60
|
+
const selectedPanel = config?.controlCenter.groups
|
|
61
|
+
?.flatMap((x) => x.panels)
|
|
62
|
+
?.find((item) => item.id === activeItemKey);
|
|
63
|
+
|
|
64
|
+
if (!items) {
|
|
65
|
+
return (
|
|
66
|
+
<div className="flex h-full flex-col items-center justify-center">
|
|
67
|
+
Loading...
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const panels: SplitterPanel[] = [
|
|
73
|
+
{
|
|
74
|
+
name: "menu",
|
|
75
|
+
defaultSize: 300,
|
|
76
|
+
content: (
|
|
77
|
+
<div className="h-full border-r border-gray-200">
|
|
78
|
+
<SimpleMenu
|
|
79
|
+
items={items}
|
|
80
|
+
activeItemKey={activeItemKey}
|
|
81
|
+
onItemClick={(item) => {
|
|
82
|
+
setActiveItemKey(item.id);
|
|
83
|
+
// Only update URL when user explicitly clicks on an item
|
|
84
|
+
updateUrl(item.id);
|
|
85
|
+
}}
|
|
86
|
+
/>
|
|
87
|
+
</div>
|
|
88
|
+
),
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "content",
|
|
92
|
+
defaultSize: "auto",
|
|
93
|
+
content: (
|
|
94
|
+
<div className="absolute inset-0 overflow-auto">
|
|
95
|
+
{selectedPanel ? (
|
|
96
|
+
selectedPanel.content
|
|
97
|
+
) : (
|
|
98
|
+
<div className="flex h-full flex-col items-center justify-center text-gray-500">
|
|
99
|
+
<i className="pi pi-info-circle mb-4 text-4xl"></i>
|
|
100
|
+
<p>Select a panel from the menu to view its content</p>
|
|
101
|
+
</div>
|
|
102
|
+
)}
|
|
103
|
+
</div>
|
|
104
|
+
),
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
|
|
2
108
|
return (
|
|
3
|
-
<div className="
|
|
4
|
-
|
|
109
|
+
<div className="h-full">
|
|
110
|
+
<Splitter panels={panels} localStorageKey="control-center-splitter" />
|
|
5
111
|
</div>
|
|
6
112
|
);
|
|
7
113
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ProgressSpinner } from "primereact/progressspinner";
|
|
1
2
|
import { FieldAction } from "../client/EditorClient";
|
|
2
3
|
import { useEditContext } from "../client/editContext";
|
|
3
4
|
|
|
@@ -9,7 +10,7 @@ export function FieldActionIndicator({ action }: { action: FieldAction }) {
|
|
|
9
10
|
const field = action.field;
|
|
10
11
|
const fieldElements =
|
|
11
12
|
pageViewContext.editorIframeRef.current?.contentWindow?.document.querySelectorAll(
|
|
12
|
-
`[data-fieldid="${field.fieldId}"][data-itemid="${field.item.id}"][data-language="${field.item.language}"]
|
|
13
|
+
`[data-fieldid="${field.fieldId}"][data-itemid="${field.item.id}"][data-language="${field.item.language}"][data-version="${field.item.version}"]`,
|
|
13
14
|
);
|
|
14
15
|
|
|
15
16
|
if (!fieldElements) return null;
|
|
@@ -19,19 +20,25 @@ export function FieldActionIndicator({ action }: { action: FieldAction }) {
|
|
|
19
20
|
<SingleIndicator
|
|
20
21
|
element={element}
|
|
21
22
|
key={action.field.fieldId + action.field + index}
|
|
23
|
+
action={action}
|
|
22
24
|
/>
|
|
23
25
|
))}
|
|
24
26
|
</>
|
|
25
27
|
);
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
function SingleIndicator({
|
|
30
|
+
function SingleIndicator({
|
|
31
|
+
element,
|
|
32
|
+
action,
|
|
33
|
+
}: {
|
|
34
|
+
element: Element;
|
|
35
|
+
action: FieldAction;
|
|
36
|
+
}) {
|
|
29
37
|
const rect = element.getBoundingClientRect();
|
|
30
|
-
|
|
31
38
|
const indicatorRect = rect;
|
|
32
39
|
return (
|
|
33
40
|
<div
|
|
34
|
-
className={`pointer-events-none absolute
|
|
41
|
+
className={`focus-shadow executing-action pointer-events-none absolute flex items-center justify-center bg-blue-500/20`}
|
|
35
42
|
style={{
|
|
36
43
|
left: indicatorRect.x,
|
|
37
44
|
top: indicatorRect.y,
|
|
@@ -39,6 +46,14 @@ function SingleIndicator({ element }: { element: Element }) {
|
|
|
39
46
|
height: indicatorRect.height,
|
|
40
47
|
zIndex: 800,
|
|
41
48
|
}}
|
|
42
|
-
|
|
49
|
+
>
|
|
50
|
+
<div className="flex flex-col items-center justify-center gap-1.5 rounded-md bg-gray-100 p-3 text-sm font-bold text-blue-500">
|
|
51
|
+
<div className="flex items-center gap-1">
|
|
52
|
+
<ProgressSpinner style={{ width: "1rem", height: "1rem" }} />
|
|
53
|
+
{action.label}
|
|
54
|
+
</div>
|
|
55
|
+
<div className="text-xs">{action.message}</div>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
43
58
|
);
|
|
44
59
|
}
|
|
@@ -5,7 +5,7 @@ import { PageViewerFrame } from "./PageViewerFrame";
|
|
|
5
5
|
import { useEffect, useState } from "react";
|
|
6
6
|
import { useRef } from "react";
|
|
7
7
|
import { SimpleIconButton } from "../ui/SimpleIconButton";
|
|
8
|
-
import {
|
|
8
|
+
import { useEditContext } from "../client/editContext";
|
|
9
9
|
import { useDebouncedCallback } from "use-debounce";
|
|
10
10
|
import { Ellipsis, PanelLeftClose, PanelLeftOpen } from "lucide-react";
|
|
11
11
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AiContext } from "../ai/AiTerminal";
|
|
2
2
|
import { EditContextType } from "../client/editContext";
|
|
3
|
-
import { ItemDescriptor } from "../pageModel";
|
|
3
|
+
import { FieldDescriptor, ItemDescriptor } from "../pageModel";
|
|
4
|
+
|
|
4
5
|
import { ExecutionResult, post } from "./serviceHelper";
|
|
5
6
|
|
|
6
7
|
export type AiProfile = {
|
|
@@ -157,3 +158,14 @@ export async function executeSearch({
|
|
|
157
158
|
|
|
158
159
|
return { type: "success", response, data: await response.json() };
|
|
159
160
|
}
|
|
161
|
+
|
|
162
|
+
export async function generateImage(
|
|
163
|
+
options: FieldDescriptor & {
|
|
164
|
+
prompt: string;
|
|
165
|
+
sessionId: string;
|
|
166
|
+
pageItem: ItemDescriptor;
|
|
167
|
+
},
|
|
168
|
+
): Promise<ExecutionResult<any>> {
|
|
169
|
+
const response = await post("/alpaca/editor/ai/generateImage", options);
|
|
170
|
+
return response;
|
|
171
|
+
}
|
|
@@ -474,7 +474,7 @@ export function ComponentTree({}) {
|
|
|
474
474
|
function renderNode(node: CustomTreeNode) {
|
|
475
475
|
return (
|
|
476
476
|
<div>
|
|
477
|
-
<div className="
|
|
477
|
+
<div className="flex items-center gap-2 text-[12px] text-gray-600">
|
|
478
478
|
{typeof node.icon === "string" ? (
|
|
479
479
|
<i className={node.icon}></i>
|
|
480
480
|
) : (
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React from "react";
|
|
1
2
|
import { classNames } from "primereact/utils";
|
|
2
3
|
|
|
3
4
|
import { useEditContext } from "../client/editContext";
|
|
@@ -14,7 +15,7 @@ export function ViewSelector() {
|
|
|
14
15
|
return (
|
|
15
16
|
<div
|
|
16
17
|
className={cn(
|
|
17
|
-
"z-10 flex items-center gap-4 bg-
|
|
18
|
+
"z-10 flex items-center gap-4 bg-neutral-200 p-2 shadow-md",
|
|
18
19
|
editContext?.isMobile ? "flex-row justify-around" : "max-w-11 flex-col",
|
|
19
20
|
)}
|
|
20
21
|
>
|
|
@@ -26,9 +27,9 @@ export function ViewSelector() {
|
|
|
26
27
|
key={i}
|
|
27
28
|
className={classNames(
|
|
28
29
|
editContext?.viewName == x.name
|
|
29
|
-
? "active text-
|
|
30
|
-
: "text-
|
|
31
|
-
"cursor-pointer",
|
|
30
|
+
? "active bg-neutral-350 rounded-sm text-neutral-800"
|
|
31
|
+
: "text-neutral-500 hover:text-gray-900",
|
|
32
|
+
"cursor-pointer p-1",
|
|
32
33
|
)}
|
|
33
34
|
data-sidebarview-name={x.name}
|
|
34
35
|
onClick={() => {
|
|
@@ -37,13 +38,11 @@ export function ViewSelector() {
|
|
|
37
38
|
}
|
|
38
39
|
}}
|
|
39
40
|
>
|
|
40
|
-
{
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
</div>
|
|
46
|
-
)}
|
|
41
|
+
{x.icon &&
|
|
42
|
+
React.isValidElement(x.icon) &&
|
|
43
|
+
React.cloneElement(x.icon as React.ReactElement<any>, {
|
|
44
|
+
className: "w-6 h-6",
|
|
45
|
+
})}
|
|
47
46
|
</div>
|
|
48
47
|
))}
|
|
49
48
|
</div>
|