@webmaster-droid/web 0.1.0-alpha.2 → 0.1.1
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/index.d.ts +7 -7
- package/dist/index.js +732 -442
- package/package.json +5 -3
package/dist/index.js
CHANGED
|
@@ -6,11 +6,36 @@ import {
|
|
|
6
6
|
createElement,
|
|
7
7
|
useContext
|
|
8
8
|
} from "react";
|
|
9
|
+
import sanitizeHtml from "sanitize-html";
|
|
9
10
|
import { jsx } from "react/jsx-runtime";
|
|
10
11
|
var EDITABLE_ROOTS = ["pages.", "layout.", "seo.", "themeTokens."];
|
|
11
12
|
var MAX_PATH_LENGTH = 320;
|
|
12
13
|
var MAX_LABEL_LENGTH = 120;
|
|
13
14
|
var MAX_PREVIEW_LENGTH = 140;
|
|
15
|
+
var RICH_TEXT_ALLOWED_TAGS = [
|
|
16
|
+
"a",
|
|
17
|
+
"b",
|
|
18
|
+
"blockquote",
|
|
19
|
+
"br",
|
|
20
|
+
"code",
|
|
21
|
+
"em",
|
|
22
|
+
"h1",
|
|
23
|
+
"h2",
|
|
24
|
+
"h3",
|
|
25
|
+
"h4",
|
|
26
|
+
"h5",
|
|
27
|
+
"h6",
|
|
28
|
+
"hr",
|
|
29
|
+
"i",
|
|
30
|
+
"li",
|
|
31
|
+
"ol",
|
|
32
|
+
"p",
|
|
33
|
+
"pre",
|
|
34
|
+
"strong",
|
|
35
|
+
"u",
|
|
36
|
+
"ul"
|
|
37
|
+
];
|
|
38
|
+
var RICH_TEXT_ALLOWED_ATTRS = ["href", "title", "aria-label", "rel"];
|
|
14
39
|
var EditableContext = createContext(null);
|
|
15
40
|
function EditableProvider(props) {
|
|
16
41
|
return /* @__PURE__ */ jsx(
|
|
@@ -172,9 +197,31 @@ function parseSelectedEditableFromTarget(target, pagePath) {
|
|
|
172
197
|
}
|
|
173
198
|
return selected;
|
|
174
199
|
}
|
|
175
|
-
function pickStringValue(document2, path, fallback) {
|
|
200
|
+
function pickStringValue(document2, path, fallback, componentName, fallbackPropName, missingBehavior = "throw") {
|
|
176
201
|
const value = readByPath(document2, path);
|
|
177
|
-
|
|
202
|
+
if (typeof value === "string" && value.trim()) {
|
|
203
|
+
return value;
|
|
204
|
+
}
|
|
205
|
+
if (typeof fallback === "string") {
|
|
206
|
+
return fallback;
|
|
207
|
+
}
|
|
208
|
+
const message = `${componentName} missing content for "${path}". Provide a CMS value or set \`${fallbackPropName}\`.`;
|
|
209
|
+
if (missingBehavior === "empty") {
|
|
210
|
+
console.error(message);
|
|
211
|
+
return "";
|
|
212
|
+
}
|
|
213
|
+
throw new Error(message);
|
|
214
|
+
}
|
|
215
|
+
function sanitizeRichTextHtml(html) {
|
|
216
|
+
return sanitizeHtml(html, {
|
|
217
|
+
allowedTags: [...RICH_TEXT_ALLOWED_TAGS],
|
|
218
|
+
allowedAttributes: {
|
|
219
|
+
a: [...RICH_TEXT_ALLOWED_ATTRS]
|
|
220
|
+
},
|
|
221
|
+
allowedSchemes: ["http", "https", "mailto", "tel"],
|
|
222
|
+
allowProtocolRelative: false,
|
|
223
|
+
disallowedTagsMode: "discard"
|
|
224
|
+
});
|
|
178
225
|
}
|
|
179
226
|
function EditableText({
|
|
180
227
|
path,
|
|
@@ -185,7 +232,7 @@ function EditableText({
|
|
|
185
232
|
...rest
|
|
186
233
|
}) {
|
|
187
234
|
const { document: document2, enabled } = useEditableDocument();
|
|
188
|
-
const value = pickStringValue(document2, path, fallback);
|
|
235
|
+
const value = pickStringValue(document2, path, fallback, "EditableText", "fallback", "empty");
|
|
189
236
|
const attrs = enabled ? editableMeta({
|
|
190
237
|
path,
|
|
191
238
|
label: label ?? path,
|
|
@@ -203,7 +250,15 @@ function EditableRichText({
|
|
|
203
250
|
...rest
|
|
204
251
|
}) {
|
|
205
252
|
const { document: document2, enabled } = useEditableDocument();
|
|
206
|
-
const value = pickStringValue(
|
|
253
|
+
const value = pickStringValue(
|
|
254
|
+
document2,
|
|
255
|
+
path,
|
|
256
|
+
fallback,
|
|
257
|
+
"EditableRichText",
|
|
258
|
+
"fallback",
|
|
259
|
+
"empty"
|
|
260
|
+
);
|
|
261
|
+
const sanitizedHtml = sanitizeRichTextHtml(value);
|
|
207
262
|
const attrs = enabled ? editableMeta({
|
|
208
263
|
path,
|
|
209
264
|
label: label ?? path,
|
|
@@ -213,7 +268,7 @@ function EditableRichText({
|
|
|
213
268
|
return createElement(as, {
|
|
214
269
|
...rest,
|
|
215
270
|
...attrs,
|
|
216
|
-
dangerouslySetInnerHTML: { __html:
|
|
271
|
+
dangerouslySetInnerHTML: { __html: sanitizedHtml }
|
|
217
272
|
});
|
|
218
273
|
}
|
|
219
274
|
function EditableImage({
|
|
@@ -225,8 +280,22 @@ function EditableImage({
|
|
|
225
280
|
...rest
|
|
226
281
|
}) {
|
|
227
282
|
const { document: document2, enabled } = useEditableDocument();
|
|
228
|
-
const src = pickStringValue(
|
|
229
|
-
|
|
283
|
+
const src = pickStringValue(
|
|
284
|
+
document2,
|
|
285
|
+
path,
|
|
286
|
+
fallbackSrc,
|
|
287
|
+
"EditableImage",
|
|
288
|
+
"fallbackSrc",
|
|
289
|
+
"empty"
|
|
290
|
+
);
|
|
291
|
+
const alt = altPath ? pickStringValue(
|
|
292
|
+
document2,
|
|
293
|
+
altPath,
|
|
294
|
+
fallbackAlt,
|
|
295
|
+
"EditableImage",
|
|
296
|
+
"fallbackAlt",
|
|
297
|
+
"empty"
|
|
298
|
+
) : fallbackAlt ?? "";
|
|
230
299
|
const attrs = enabled ? editableMeta({
|
|
231
300
|
path,
|
|
232
301
|
label: label ?? path,
|
|
@@ -245,8 +314,22 @@ function EditableLink({
|
|
|
245
314
|
...rest
|
|
246
315
|
}) {
|
|
247
316
|
const { document: document2, enabled } = useEditableDocument();
|
|
248
|
-
const href = pickStringValue(
|
|
249
|
-
|
|
317
|
+
const href = pickStringValue(
|
|
318
|
+
document2,
|
|
319
|
+
hrefPath,
|
|
320
|
+
fallbackHref,
|
|
321
|
+
"EditableLink",
|
|
322
|
+
"fallbackHref",
|
|
323
|
+
"empty"
|
|
324
|
+
);
|
|
325
|
+
const text = pickStringValue(
|
|
326
|
+
document2,
|
|
327
|
+
labelPath,
|
|
328
|
+
fallbackLabel,
|
|
329
|
+
"EditableLink",
|
|
330
|
+
"fallbackLabel",
|
|
331
|
+
"empty"
|
|
332
|
+
);
|
|
250
333
|
const attrs = enabled ? editableMeta({
|
|
251
334
|
path: labelPath,
|
|
252
335
|
label: label ?? labelPath,
|
|
@@ -686,18 +769,7 @@ function useWebmasterDroid() {
|
|
|
686
769
|
return context;
|
|
687
770
|
}
|
|
688
771
|
|
|
689
|
-
// src/overlay.
|
|
690
|
-
import {
|
|
691
|
-
useCallback,
|
|
692
|
-
useEffect as useEffect2,
|
|
693
|
-
useMemo as useMemo2,
|
|
694
|
-
useRef,
|
|
695
|
-
useState as useState2
|
|
696
|
-
} from "react";
|
|
697
|
-
import ReactMarkdown from "react-markdown";
|
|
698
|
-
import remarkGfm from "remark-gfm";
|
|
699
|
-
import { REQUIRED_PUBLISH_CONFIRMATION } from "@webmaster-droid/contracts";
|
|
700
|
-
import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
772
|
+
// src/overlay/utils.ts
|
|
701
773
|
function createMessage(role, text, status) {
|
|
702
774
|
return {
|
|
703
775
|
id: `${role}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
|
@@ -786,7 +858,394 @@ function kindIcon(kind) {
|
|
|
786
858
|
}
|
|
787
859
|
return "TXT";
|
|
788
860
|
}
|
|
789
|
-
|
|
861
|
+
var OVERLAY_FONT_FAMILY = "var(--font-ibm-plex-mono), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace";
|
|
862
|
+
|
|
863
|
+
// src/overlay/components.tsx
|
|
864
|
+
import ReactMarkdown from "react-markdown";
|
|
865
|
+
import remarkGfm from "remark-gfm";
|
|
866
|
+
import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
867
|
+
function OverlayHeader({
|
|
868
|
+
isAuthenticated,
|
|
869
|
+
publishState,
|
|
870
|
+
activeTab,
|
|
871
|
+
historyCount,
|
|
872
|
+
clearChatDisabled,
|
|
873
|
+
onPublish,
|
|
874
|
+
onTabChange,
|
|
875
|
+
onClearChat,
|
|
876
|
+
onClose
|
|
877
|
+
}) {
|
|
878
|
+
return /* @__PURE__ */ jsx3("header", { className: "border-b border-stone-300 bg-[#f3eee5] p-2", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
879
|
+
isAuthenticated ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
880
|
+
/* @__PURE__ */ jsx3(
|
|
881
|
+
"span",
|
|
882
|
+
{
|
|
883
|
+
className: `rounded border px-1.5 py-0.5 text-[10px] font-medium leading-4 ${publishState === "Published" ? "border-stone-300 bg-[#ece5d9] text-stone-600" : "border-stone-500 bg-[#ded4c3] text-stone-800"}`,
|
|
884
|
+
children: publishState
|
|
885
|
+
}
|
|
886
|
+
),
|
|
887
|
+
/* @__PURE__ */ jsx3(
|
|
888
|
+
"button",
|
|
889
|
+
{
|
|
890
|
+
type: "button",
|
|
891
|
+
className: "rounded border border-stone-700 bg-stone-800 px-2 py-1 text-[11px] font-semibold leading-4 text-stone-100 hover:bg-stone-700 disabled:cursor-not-allowed disabled:opacity-50",
|
|
892
|
+
onClick: onPublish,
|
|
893
|
+
disabled: !isAuthenticated,
|
|
894
|
+
children: "Publish"
|
|
895
|
+
}
|
|
896
|
+
),
|
|
897
|
+
/* @__PURE__ */ jsxs("div", { className: "inline-flex rounded-md border border-stone-300 bg-[#e8dfd1] p-0.5", children: [
|
|
898
|
+
/* @__PURE__ */ jsx3(
|
|
899
|
+
"button",
|
|
900
|
+
{
|
|
901
|
+
type: "button",
|
|
902
|
+
className: `rounded px-2 py-1 text-[11px] font-medium leading-4 ${activeTab === "chat" ? "bg-[#f7f2e8] text-stone-900 shadow-sm" : "text-stone-600 hover:text-stone-900"}`,
|
|
903
|
+
onClick: () => onTabChange("chat"),
|
|
904
|
+
children: "Chat"
|
|
905
|
+
}
|
|
906
|
+
),
|
|
907
|
+
/* @__PURE__ */ jsxs(
|
|
908
|
+
"button",
|
|
909
|
+
{
|
|
910
|
+
type: "button",
|
|
911
|
+
className: `rounded px-2 py-1 text-[11px] font-medium leading-4 ${activeTab === "history" ? "bg-[#f7f2e8] text-stone-900 shadow-sm" : "text-stone-600 hover:text-stone-900"}`,
|
|
912
|
+
onClick: () => onTabChange("history"),
|
|
913
|
+
children: [
|
|
914
|
+
"History (",
|
|
915
|
+
historyCount,
|
|
916
|
+
")"
|
|
917
|
+
]
|
|
918
|
+
}
|
|
919
|
+
)
|
|
920
|
+
] })
|
|
921
|
+
] }) : /* @__PURE__ */ jsx3("h2", { className: "text-[12px] font-semibold text-stone-700", children: "Login" }),
|
|
922
|
+
/* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-1", children: [
|
|
923
|
+
isAuthenticated ? /* @__PURE__ */ jsx3(
|
|
924
|
+
"button",
|
|
925
|
+
{
|
|
926
|
+
type: "button",
|
|
927
|
+
"aria-label": "Clear chat",
|
|
928
|
+
title: "Clear chat",
|
|
929
|
+
disabled: clearChatDisabled,
|
|
930
|
+
className: "inline-flex h-6 w-6 items-center justify-center rounded border border-stone-300 text-stone-600 hover:bg-[#efe8dc] hover:text-stone-800 disabled:cursor-not-allowed disabled:opacity-50",
|
|
931
|
+
onClick: onClearChat,
|
|
932
|
+
children: /* @__PURE__ */ jsx3("svg", { viewBox: "0 0 20 20", fill: "none", className: "h-3.5 w-3.5", "aria-hidden": "true", children: /* @__PURE__ */ jsx3(
|
|
933
|
+
"path",
|
|
934
|
+
{
|
|
935
|
+
d: "M4.5 5.5H15.5M8 3.75H12M7 7.5V13.5M10 7.5V13.5M13 7.5V13.5M6.5 5.5L7 15C7.03 15.6 7.53 16.08 8.13 16.08H11.87C12.47 16.08 12.97 15.6 13 15L13.5 5.5",
|
|
936
|
+
stroke: "currentColor",
|
|
937
|
+
strokeWidth: "1.4",
|
|
938
|
+
strokeLinecap: "round",
|
|
939
|
+
strokeLinejoin: "round"
|
|
940
|
+
}
|
|
941
|
+
) })
|
|
942
|
+
}
|
|
943
|
+
) : null,
|
|
944
|
+
/* @__PURE__ */ jsx3(
|
|
945
|
+
"button",
|
|
946
|
+
{
|
|
947
|
+
type: "button",
|
|
948
|
+
className: "rounded border border-stone-300 px-2 py-1 text-[11px] leading-4 text-stone-700 hover:bg-[#efe8dc]",
|
|
949
|
+
onClick: onClose,
|
|
950
|
+
children: "Close"
|
|
951
|
+
}
|
|
952
|
+
)
|
|
953
|
+
] })
|
|
954
|
+
] }) });
|
|
955
|
+
}
|
|
956
|
+
function OverlayLoginPanel({
|
|
957
|
+
authConfigured,
|
|
958
|
+
email,
|
|
959
|
+
password,
|
|
960
|
+
signingIn,
|
|
961
|
+
onEmailChange,
|
|
962
|
+
onPasswordChange,
|
|
963
|
+
onSignIn
|
|
964
|
+
}) {
|
|
965
|
+
return /* @__PURE__ */ jsx3("section", { className: "flex min-h-0 flex-1 items-center justify-center bg-[#ece7dd] p-3", children: !authConfigured ? /* @__PURE__ */ jsx3("div", { className: "w-full max-w-sm rounded border border-red-300 bg-[#f8f3e9] p-3 text-[11px] leading-4 text-red-700", children: "Missing Supabase config (`supabaseUrl` / `supabaseAnonKey`)." }) : /* @__PURE__ */ jsxs("div", { className: "w-full max-w-sm rounded border border-stone-300 bg-[#f8f3e9] p-3", children: [
|
|
966
|
+
/* @__PURE__ */ jsx3("h3", { className: "mb-2 text-[12px] font-semibold text-stone-700", children: "Sign in" }),
|
|
967
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
968
|
+
/* @__PURE__ */ jsx3(
|
|
969
|
+
"input",
|
|
970
|
+
{
|
|
971
|
+
type: "text",
|
|
972
|
+
value: email,
|
|
973
|
+
onChange: (event) => onEmailChange(event.target.value),
|
|
974
|
+
placeholder: "login",
|
|
975
|
+
className: "w-full rounded border border-stone-300 bg-[#f4efe6] px-2 py-1.5 text-[12px] text-stone-900 outline-none focus:border-stone-500"
|
|
976
|
+
}
|
|
977
|
+
),
|
|
978
|
+
/* @__PURE__ */ jsx3(
|
|
979
|
+
"input",
|
|
980
|
+
{
|
|
981
|
+
type: "password",
|
|
982
|
+
value: password,
|
|
983
|
+
onChange: (event) => onPasswordChange(event.target.value),
|
|
984
|
+
placeholder: "Password",
|
|
985
|
+
className: "w-full rounded border border-stone-300 bg-[#f4efe6] px-2 py-1.5 text-[12px] text-stone-900 outline-none focus:border-stone-500"
|
|
986
|
+
}
|
|
987
|
+
),
|
|
988
|
+
/* @__PURE__ */ jsx3(
|
|
989
|
+
"button",
|
|
990
|
+
{
|
|
991
|
+
type: "button",
|
|
992
|
+
onClick: onSignIn,
|
|
993
|
+
disabled: signingIn || !email.trim() || !password,
|
|
994
|
+
className: "w-full rounded border border-stone-700 bg-stone-800 px-2 py-1.5 text-[12px] font-medium text-stone-100 hover:bg-stone-700 disabled:cursor-not-allowed disabled:opacity-50",
|
|
995
|
+
children: signingIn ? "Signing in" : "Sign in"
|
|
996
|
+
}
|
|
997
|
+
)
|
|
998
|
+
] })
|
|
999
|
+
] }) });
|
|
1000
|
+
}
|
|
1001
|
+
function OverlayChatPanel({
|
|
1002
|
+
messages,
|
|
1003
|
+
chatEndRef,
|
|
1004
|
+
showAssistantAvatarImage,
|
|
1005
|
+
assistantAvatarUrl,
|
|
1006
|
+
assistantAvatarFallbackLabel,
|
|
1007
|
+
onAssistantAvatarError,
|
|
1008
|
+
showModelPicker,
|
|
1009
|
+
selectableModels,
|
|
1010
|
+
modelId,
|
|
1011
|
+
sending,
|
|
1012
|
+
onModelChange,
|
|
1013
|
+
selectedElement,
|
|
1014
|
+
onClearSelectedElement,
|
|
1015
|
+
message,
|
|
1016
|
+
onMessageChange,
|
|
1017
|
+
onMessageKeyDown,
|
|
1018
|
+
onSend,
|
|
1019
|
+
isAuthenticated
|
|
1020
|
+
}) {
|
|
1021
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1022
|
+
/* @__PURE__ */ jsxs("section", { className: "flex-1 space-y-1 overflow-auto bg-[#ece7dd] p-2", children: [
|
|
1023
|
+
messages.map((entry) => {
|
|
1024
|
+
const isAssistant = entry.role === "assistant";
|
|
1025
|
+
const isPendingAssistant = isAssistant && entry.status === "pending";
|
|
1026
|
+
return /* @__PURE__ */ jsx3(
|
|
1027
|
+
"div",
|
|
1028
|
+
{
|
|
1029
|
+
className: entry.role === "tool" ? "max-w-[96%] px-0.5 py-0 text-[10px] leading-tight text-stone-500" : `max-w-[92%] rounded-md py-1.5 text-[12px] leading-4 ${entry.role === "user" ? "ml-auto bg-[#2e2b27] px-2 text-stone-50" : entry.role === "thinking" ? "bg-[#e3dbce] px-2 text-stone-700" : isAssistant ? "relative border border-[#d6ccbb] bg-[#f8f3e9] pl-8 pr-2 text-stone-800" : "bg-[#ddd2bf] px-2 text-stone-800"}`,
|
|
1030
|
+
children: entry.role === "tool" ? /* @__PURE__ */ jsx3("span", { children: entry.text }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1031
|
+
isAssistant ? showAssistantAvatarImage ? /* @__PURE__ */ jsx3(
|
|
1032
|
+
"img",
|
|
1033
|
+
{
|
|
1034
|
+
src: assistantAvatarUrl,
|
|
1035
|
+
alt: "",
|
|
1036
|
+
"aria-hidden": "true",
|
|
1037
|
+
className: `pointer-events-none absolute left-2 top-1.5 h-[18px] w-[18px] select-none rounded-full border border-[#d6ccbb] bg-[#efe8dc] object-cover ${isPendingAssistant ? "animate-pulse" : ""}`,
|
|
1038
|
+
onError: onAssistantAvatarError
|
|
1039
|
+
}
|
|
1040
|
+
) : /* @__PURE__ */ jsx3(
|
|
1041
|
+
"span",
|
|
1042
|
+
{
|
|
1043
|
+
"aria-hidden": "true",
|
|
1044
|
+
className: `pointer-events-none absolute left-2 top-1.5 inline-flex h-[18px] w-[18px] select-none items-center justify-center rounded-full border border-[#d6ccbb] bg-[#efe8dc] text-[9px] font-semibold text-stone-700 ${isPendingAssistant ? "animate-pulse" : ""}`,
|
|
1045
|
+
children: assistantAvatarFallbackLabel
|
|
1046
|
+
}
|
|
1047
|
+
) : null,
|
|
1048
|
+
/* @__PURE__ */ jsx3("div", { className: "max-w-none text-inherit [&_code]:rounded [&_code]:bg-stone-900/10 [&_code]:px-1 [&_ol]:list-decimal [&_ol]:pl-4 [&_p]:mb-1 [&_p:last-child]:mb-0 [&_ul]:list-disc [&_ul]:pl-4", children: isPendingAssistant && !entry.text.trim() ? /* @__PURE__ */ jsx3("span", { className: "block h-4", "aria-hidden": "true" }) : /* @__PURE__ */ jsx3(ReactMarkdown, { remarkPlugins: [remarkGfm], children: entry.text }) })
|
|
1049
|
+
] })
|
|
1050
|
+
},
|
|
1051
|
+
entry.id
|
|
1052
|
+
);
|
|
1053
|
+
}),
|
|
1054
|
+
/* @__PURE__ */ jsx3("div", { ref: chatEndRef })
|
|
1055
|
+
] }),
|
|
1056
|
+
/* @__PURE__ */ jsxs("footer", { className: "border-t border-stone-300 bg-[#f3eee5] p-2", children: [
|
|
1057
|
+
showModelPicker && selectableModels.length > 1 ? /* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center gap-1.5", children: [
|
|
1058
|
+
/* @__PURE__ */ jsx3(
|
|
1059
|
+
"label",
|
|
1060
|
+
{
|
|
1061
|
+
htmlFor: "admin-model-picker",
|
|
1062
|
+
className: "text-[10px] font-semibold uppercase tracking-wide text-stone-600",
|
|
1063
|
+
children: "Model"
|
|
1064
|
+
}
|
|
1065
|
+
),
|
|
1066
|
+
/* @__PURE__ */ jsx3(
|
|
1067
|
+
"select",
|
|
1068
|
+
{
|
|
1069
|
+
id: "admin-model-picker",
|
|
1070
|
+
value: modelId ?? selectableModels[0]?.id,
|
|
1071
|
+
onChange: (event) => onModelChange(event.target.value),
|
|
1072
|
+
disabled: sending,
|
|
1073
|
+
className: "h-7 min-w-0 flex-1 rounded border border-stone-300 bg-[#f7f2e8] px-2 text-[11px] text-stone-800 outline-none focus:border-stone-500 disabled:cursor-not-allowed disabled:opacity-60",
|
|
1074
|
+
children: selectableModels.map((option) => /* @__PURE__ */ jsx3("option", { value: option.id, children: option.label }, option.id))
|
|
1075
|
+
}
|
|
1076
|
+
)
|
|
1077
|
+
] }) : null,
|
|
1078
|
+
selectedElement ? /* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center gap-1 rounded border border-stone-300 bg-[#e8dfd1] px-1.5 py-1", children: [
|
|
1079
|
+
/* @__PURE__ */ jsx3("span", { className: "inline-flex shrink-0 items-center justify-center rounded border border-stone-300 bg-[#f7f2e8] px-1 py-0.5 text-[9px] font-semibold text-stone-700", children: kindIcon(selectedElement.kind) }),
|
|
1080
|
+
/* @__PURE__ */ jsxs("p", { className: "min-w-0 flex-1 truncate text-[10px] leading-3.5 text-stone-600", children: [
|
|
1081
|
+
/* @__PURE__ */ jsx3("span", { className: "font-semibold text-stone-800", children: selectedElement.label }),
|
|
1082
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1083
|
+
" \xB7 ",
|
|
1084
|
+
selectedElement.path
|
|
1085
|
+
] }),
|
|
1086
|
+
selectedElement.preview ? /* @__PURE__ */ jsxs("span", { children: [
|
|
1087
|
+
" \xB7 ",
|
|
1088
|
+
selectedElement.preview
|
|
1089
|
+
] }) : null
|
|
1090
|
+
] }),
|
|
1091
|
+
/* @__PURE__ */ jsx3(
|
|
1092
|
+
"button",
|
|
1093
|
+
{
|
|
1094
|
+
type: "button",
|
|
1095
|
+
"aria-label": "Clear selected element",
|
|
1096
|
+
title: "Clear selected element",
|
|
1097
|
+
className: "inline-flex h-5 w-5 shrink-0 items-center justify-center rounded border border-stone-300 bg-[#f7f2e8] text-stone-700 hover:bg-[#efe8dc]",
|
|
1098
|
+
onClick: onClearSelectedElement,
|
|
1099
|
+
children: /* @__PURE__ */ jsx3("svg", { viewBox: "0 0 20 20", fill: "none", className: "h-3 w-3", "aria-hidden": "true", children: /* @__PURE__ */ jsx3(
|
|
1100
|
+
"path",
|
|
1101
|
+
{
|
|
1102
|
+
d: "M5 5L15 15M15 5L5 15",
|
|
1103
|
+
stroke: "currentColor",
|
|
1104
|
+
strokeWidth: "1.5",
|
|
1105
|
+
strokeLinecap: "round"
|
|
1106
|
+
}
|
|
1107
|
+
) })
|
|
1108
|
+
}
|
|
1109
|
+
)
|
|
1110
|
+
] }) : null,
|
|
1111
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-1.5", children: [
|
|
1112
|
+
/* @__PURE__ */ jsx3(
|
|
1113
|
+
"textarea",
|
|
1114
|
+
{
|
|
1115
|
+
value: message,
|
|
1116
|
+
onChange: (event) => onMessageChange(event.target.value),
|
|
1117
|
+
onKeyDown: onMessageKeyDown,
|
|
1118
|
+
rows: 2,
|
|
1119
|
+
placeholder: "Ask the agent to edit text, image URLs, or theme tokens",
|
|
1120
|
+
className: "flex-1 resize-none rounded border border-stone-300 bg-[#f4efe6] px-2 py-1.5 text-[12px] leading-4 text-stone-900 outline-none placeholder:text-stone-500 focus:border-stone-500"
|
|
1121
|
+
}
|
|
1122
|
+
),
|
|
1123
|
+
/* @__PURE__ */ jsx3(
|
|
1124
|
+
"button",
|
|
1125
|
+
{
|
|
1126
|
+
type: "button",
|
|
1127
|
+
onClick: onSend,
|
|
1128
|
+
disabled: !isAuthenticated || sending || !message.trim(),
|
|
1129
|
+
className: "rounded border border-stone-500 bg-stone-600 px-3 py-1.5 text-[12px] font-semibold text-stone-100 hover:bg-stone-700 disabled:cursor-not-allowed disabled:opacity-50",
|
|
1130
|
+
children: sending ? "Sending" : "Send"
|
|
1131
|
+
}
|
|
1132
|
+
)
|
|
1133
|
+
] })
|
|
1134
|
+
] })
|
|
1135
|
+
] });
|
|
1136
|
+
}
|
|
1137
|
+
function OverlayHistoryPanel({
|
|
1138
|
+
history,
|
|
1139
|
+
deletingCheckpointId,
|
|
1140
|
+
onRestorePublished,
|
|
1141
|
+
onRestoreCheckpoint,
|
|
1142
|
+
onDeleteCheckpoint
|
|
1143
|
+
}) {
|
|
1144
|
+
return /* @__PURE__ */ jsx3("section", { className: "flex min-h-0 flex-1 flex-col p-2 text-[11px] leading-4", children: /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col gap-2 overflow-hidden", children: [
|
|
1145
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded border border-stone-300 bg-[#f8f3e9]", children: [
|
|
1146
|
+
/* @__PURE__ */ jsxs("div", { className: "border-b border-stone-200 px-2 py-1 font-semibold text-stone-700", children: [
|
|
1147
|
+
"Published (",
|
|
1148
|
+
history.published.length,
|
|
1149
|
+
")"
|
|
1150
|
+
] }),
|
|
1151
|
+
/* @__PURE__ */ jsx3("div", { className: "max-h-40 overflow-auto px-2 py-1.5", children: history.published.length > 0 ? /* @__PURE__ */ jsx3("div", { className: "space-y-1", children: history.published.map((item) => /* @__PURE__ */ jsxs(
|
|
1152
|
+
"div",
|
|
1153
|
+
{
|
|
1154
|
+
className: "flex items-center justify-between gap-2 rounded border border-stone-200 bg-[#f2ecdf] px-2 py-1",
|
|
1155
|
+
children: [
|
|
1156
|
+
/* @__PURE__ */ jsx3("span", { className: "truncate text-[10px] text-stone-700", children: formatHistoryTime(item.createdAt) }),
|
|
1157
|
+
/* @__PURE__ */ jsx3(
|
|
1158
|
+
"button",
|
|
1159
|
+
{
|
|
1160
|
+
type: "button",
|
|
1161
|
+
className: "rounded border border-stone-300 bg-[#f7f2e8] px-1.5 py-0.5 text-[10px] text-stone-700 hover:bg-[#efe8dc]",
|
|
1162
|
+
onClick: () => onRestorePublished(item),
|
|
1163
|
+
children: "Restore"
|
|
1164
|
+
}
|
|
1165
|
+
)
|
|
1166
|
+
]
|
|
1167
|
+
},
|
|
1168
|
+
`pub-${item.id}`
|
|
1169
|
+
)) }) : /* @__PURE__ */ jsx3("p", { className: "text-[10px] text-stone-500", children: "No published snapshots." }) })
|
|
1170
|
+
] }),
|
|
1171
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col rounded border border-stone-300 bg-[#f8f3e9]", children: [
|
|
1172
|
+
/* @__PURE__ */ jsxs("div", { className: "border-b border-stone-200 px-2 py-1 font-semibold text-stone-700", children: [
|
|
1173
|
+
"Checkpoints (",
|
|
1174
|
+
history.checkpoints.length,
|
|
1175
|
+
")"
|
|
1176
|
+
] }),
|
|
1177
|
+
/* @__PURE__ */ jsx3("div", { className: "min-h-0 flex-1 overflow-auto px-2 py-1.5", children: history.checkpoints.length > 0 ? /* @__PURE__ */ jsx3("div", { className: "space-y-1", children: history.checkpoints.map((item) => /* @__PURE__ */ jsxs(
|
|
1178
|
+
"div",
|
|
1179
|
+
{
|
|
1180
|
+
className: "flex items-start justify-between gap-2 rounded border border-stone-200 bg-[#f2ecdf] px-2 py-1",
|
|
1181
|
+
children: [
|
|
1182
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
1183
|
+
/* @__PURE__ */ jsx3("p", { className: "truncate text-[10px] text-stone-700", children: formatHistoryTime(item.createdAt) }),
|
|
1184
|
+
item.reason ? /* @__PURE__ */ jsx3("p", { className: "truncate text-[10px] text-stone-500", children: item.reason }) : null
|
|
1185
|
+
] }),
|
|
1186
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1187
|
+
/* @__PURE__ */ jsx3(
|
|
1188
|
+
"button",
|
|
1189
|
+
{
|
|
1190
|
+
type: "button",
|
|
1191
|
+
disabled: deletingCheckpointId === item.id,
|
|
1192
|
+
className: "rounded border border-stone-300 bg-[#f7f2e8] px-1.5 py-0.5 text-[10px] text-stone-700 hover:bg-[#efe8dc] disabled:cursor-not-allowed disabled:opacity-50",
|
|
1193
|
+
onClick: () => onRestoreCheckpoint(item),
|
|
1194
|
+
children: "Restore"
|
|
1195
|
+
}
|
|
1196
|
+
),
|
|
1197
|
+
/* @__PURE__ */ jsx3(
|
|
1198
|
+
"button",
|
|
1199
|
+
{
|
|
1200
|
+
type: "button",
|
|
1201
|
+
"aria-label": "Delete checkpoint",
|
|
1202
|
+
title: "Delete checkpoint",
|
|
1203
|
+
disabled: deletingCheckpointId === item.id,
|
|
1204
|
+
className: "inline-flex h-6 w-6 items-center justify-center rounded border border-stone-300 bg-[#f7f2e8] text-stone-700 hover:bg-[#efe8dc] disabled:cursor-not-allowed disabled:opacity-50",
|
|
1205
|
+
onClick: () => onDeleteCheckpoint(item),
|
|
1206
|
+
children: /* @__PURE__ */ jsx3("svg", { viewBox: "0 0 20 20", fill: "none", className: "h-3.5 w-3.5", "aria-hidden": "true", children: /* @__PURE__ */ jsx3(
|
|
1207
|
+
"path",
|
|
1208
|
+
{
|
|
1209
|
+
d: "M4.5 5.5H15.5M8 3.75H12M7 7.5V13.5M10 7.5V13.5M13 7.5V13.5M6.5 5.5L7 15C7.03 15.6 7.53 16.08 8.13 16.08H11.87C12.47 16.08 12.97 15.6 13 15L13.5 5.5",
|
|
1210
|
+
stroke: "currentColor",
|
|
1211
|
+
strokeWidth: "1.4",
|
|
1212
|
+
strokeLinecap: "round",
|
|
1213
|
+
strokeLinejoin: "round"
|
|
1214
|
+
}
|
|
1215
|
+
) })
|
|
1216
|
+
}
|
|
1217
|
+
)
|
|
1218
|
+
] })
|
|
1219
|
+
]
|
|
1220
|
+
},
|
|
1221
|
+
`cp-${item.id}`
|
|
1222
|
+
)) }) : /* @__PURE__ */ jsx3("p", { className: "text-[10px] text-stone-500", children: "No checkpoints yet." }) })
|
|
1223
|
+
] })
|
|
1224
|
+
] }) });
|
|
1225
|
+
}
|
|
1226
|
+
function OverlayLauncherButton({ onOpen }) {
|
|
1227
|
+
return /* @__PURE__ */ jsx3(
|
|
1228
|
+
"button",
|
|
1229
|
+
{
|
|
1230
|
+
type: "button",
|
|
1231
|
+
onClick: onOpen,
|
|
1232
|
+
className: "fixed bottom-4 right-4 z-[100] rounded-full border border-stone-600 bg-stone-700 px-4 py-2 text-[12px] font-semibold text-stone-100 shadow-xl hover:bg-stone-800",
|
|
1233
|
+
style: { fontFamily: OVERLAY_FONT_FAMILY },
|
|
1234
|
+
children: "Chat to Webmaster"
|
|
1235
|
+
}
|
|
1236
|
+
);
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
// src/overlay/controller.ts
|
|
1240
|
+
import {
|
|
1241
|
+
useCallback,
|
|
1242
|
+
useEffect as useEffect2,
|
|
1243
|
+
useMemo as useMemo2,
|
|
1244
|
+
useRef,
|
|
1245
|
+
useState as useState2
|
|
1246
|
+
} from "react";
|
|
1247
|
+
import { REQUIRED_PUBLISH_CONFIRMATION } from "@webmaster-droid/contracts";
|
|
1248
|
+
function useOverlayController() {
|
|
790
1249
|
const {
|
|
791
1250
|
config,
|
|
792
1251
|
isAdminMode,
|
|
@@ -878,11 +1337,7 @@ function WebmasterDroidOverlay() {
|
|
|
878
1337
|
document.removeEventListener("click", onDocumentClickCapture, true);
|
|
879
1338
|
};
|
|
880
1339
|
}, [isAdminMode, isOpen, setSelectedElement]);
|
|
881
|
-
const
|
|
882
|
-
if (!isAdminMode) {
|
|
883
|
-
return null;
|
|
884
|
-
}
|
|
885
|
-
const signInWithPassword = async () => {
|
|
1340
|
+
const signInWithPassword = useCallback(async () => {
|
|
886
1341
|
if (!supabase) {
|
|
887
1342
|
setMessages((prev) => [
|
|
888
1343
|
...prev,
|
|
@@ -915,8 +1370,8 @@ function WebmasterDroidOverlay() {
|
|
|
915
1370
|
} finally {
|
|
916
1371
|
setSigningIn(false);
|
|
917
1372
|
}
|
|
918
|
-
};
|
|
919
|
-
const onSend = async () => {
|
|
1373
|
+
}, [email, password, supabase]);
|
|
1374
|
+
const onSend = useCallback(async () => {
|
|
920
1375
|
if (!token || !message.trim() || sending) {
|
|
921
1376
|
return;
|
|
922
1377
|
}
|
|
@@ -1020,14 +1475,28 @@ function WebmasterDroidOverlay() {
|
|
|
1020
1475
|
} catch (error) {
|
|
1021
1476
|
const detail = error instanceof Error ? error.message : "Chat failed.";
|
|
1022
1477
|
const pendingAssistantId = pendingAssistantIdRef.current;
|
|
1023
|
-
setMessages((prev) => [
|
|
1478
|
+
setMessages((prev) => [
|
|
1479
|
+
...removeMessageById(prev, pendingAssistantId),
|
|
1480
|
+
createMessage("system", detail)
|
|
1481
|
+
]);
|
|
1024
1482
|
pendingAssistantIdRef.current = null;
|
|
1025
1483
|
} finally {
|
|
1026
1484
|
pendingAssistantIdRef.current = null;
|
|
1027
1485
|
setSending(false);
|
|
1028
1486
|
}
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1487
|
+
}, [
|
|
1488
|
+
config.apiBaseUrl,
|
|
1489
|
+
includeThinking,
|
|
1490
|
+
message,
|
|
1491
|
+
messages,
|
|
1492
|
+
modelId,
|
|
1493
|
+
requestRefresh,
|
|
1494
|
+
refreshHistory,
|
|
1495
|
+
selectedElement,
|
|
1496
|
+
sending,
|
|
1497
|
+
token
|
|
1498
|
+
]);
|
|
1499
|
+
const onPublish = useCallback(async () => {
|
|
1031
1500
|
if (!token) {
|
|
1032
1501
|
return;
|
|
1033
1502
|
}
|
|
@@ -1051,53 +1520,59 @@ function WebmasterDroidOverlay() {
|
|
|
1051
1520
|
const detail = error instanceof Error ? error.message : "Publish failed.";
|
|
1052
1521
|
setMessages((prev) => [...prev, createMessage("system", detail)]);
|
|
1053
1522
|
}
|
|
1054
|
-
};
|
|
1055
|
-
const onRollback =
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1523
|
+
}, [config.apiBaseUrl, requestRefresh, refreshHistory, token]);
|
|
1524
|
+
const onRollback = useCallback(
|
|
1525
|
+
async (request, label) => {
|
|
1526
|
+
if (!token) {
|
|
1527
|
+
return;
|
|
1528
|
+
}
|
|
1529
|
+
try {
|
|
1530
|
+
await rollbackDraft(config.apiBaseUrl, token, request);
|
|
1531
|
+
requestRefresh();
|
|
1532
|
+
await refreshHistory(false);
|
|
1533
|
+
setMessages((prev) => [
|
|
1534
|
+
...prev,
|
|
1535
|
+
createMessage("system", `Draft restored from ${label}.`)
|
|
1536
|
+
]);
|
|
1537
|
+
} catch (error) {
|
|
1538
|
+
const detail = error instanceof Error ? error.message : "Rollback failed.";
|
|
1539
|
+
setMessages((prev) => [...prev, createMessage("system", detail)]);
|
|
1540
|
+
}
|
|
1541
|
+
},
|
|
1542
|
+
[config.apiBaseUrl, requestRefresh, refreshHistory, token]
|
|
1543
|
+
);
|
|
1544
|
+
const onDeleteCheckpoint = useCallback(
|
|
1545
|
+
async (checkpoint) => {
|
|
1546
|
+
if (!token || deletingCheckpointId) {
|
|
1547
|
+
return;
|
|
1548
|
+
}
|
|
1549
|
+
const timestampLabel = formatHistoryTime(checkpoint.createdAt);
|
|
1550
|
+
const reasonLine = checkpoint.reason ? `
|
|
1078
1551
|
Reason: ${checkpoint.reason}` : "";
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1552
|
+
const approved = window.confirm(
|
|
1553
|
+
`Delete checkpoint from ${timestampLabel}? This cannot be undone.${reasonLine}`
|
|
1554
|
+
);
|
|
1555
|
+
if (!approved) {
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
setDeletingCheckpointId(checkpoint.id);
|
|
1559
|
+
try {
|
|
1560
|
+
await deleteCheckpoint(config.apiBaseUrl, token, { checkpointId: checkpoint.id });
|
|
1561
|
+
await refreshHistory(false);
|
|
1562
|
+
setMessages((prev) => [
|
|
1563
|
+
...prev,
|
|
1564
|
+
createMessage("system", `Deleted checkpoint from ${timestampLabel}.`)
|
|
1565
|
+
]);
|
|
1566
|
+
} catch (error) {
|
|
1567
|
+
const detail = error instanceof Error ? error.message : "Delete checkpoint failed.";
|
|
1568
|
+
setMessages((prev) => [...prev, createMessage("system", detail)]);
|
|
1569
|
+
} finally {
|
|
1570
|
+
setDeletingCheckpointId((current) => current === checkpoint.id ? null : current);
|
|
1571
|
+
}
|
|
1572
|
+
},
|
|
1573
|
+
[config.apiBaseUrl, deletingCheckpointId, refreshHistory, token]
|
|
1574
|
+
);
|
|
1575
|
+
const onClearChat = useCallback(() => {
|
|
1101
1576
|
if (sending) {
|
|
1102
1577
|
return;
|
|
1103
1578
|
}
|
|
@@ -1108,372 +1583,180 @@ Reason: ${checkpoint.reason}` : "";
|
|
|
1108
1583
|
setMessages([]);
|
|
1109
1584
|
setMessage("");
|
|
1110
1585
|
clearSelectedElement();
|
|
1111
|
-
};
|
|
1112
|
-
const onMessageKeyDown = (
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1586
|
+
}, [clearSelectedElement, message, messages.length, selectedElement, sending]);
|
|
1587
|
+
const onMessageKeyDown = useCallback(
|
|
1588
|
+
(event) => {
|
|
1589
|
+
if (event.key !== "Enter" || event.shiftKey || event.nativeEvent.isComposing) {
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
event.preventDefault();
|
|
1593
|
+
if (!isAuthenticated || sending || !message.trim()) {
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
void onSend();
|
|
1597
|
+
},
|
|
1598
|
+
[isAuthenticated, message, onSend, sending]
|
|
1599
|
+
);
|
|
1600
|
+
const latestPublished = useMemo2(
|
|
1601
|
+
() => history.published.reduce((max, item) => {
|
|
1602
|
+
const value = historyTimestamp(item.createdAt);
|
|
1603
|
+
if (value === null) {
|
|
1604
|
+
return max;
|
|
1605
|
+
}
|
|
1606
|
+
return max === null ? value : Math.max(max, value);
|
|
1607
|
+
}, null),
|
|
1608
|
+
[history.published]
|
|
1609
|
+
);
|
|
1610
|
+
const latestCheckpoint = useMemo2(
|
|
1611
|
+
() => history.checkpoints.reduce((max, item) => {
|
|
1612
|
+
const value = historyTimestamp(item.createdAt);
|
|
1613
|
+
if (value === null) {
|
|
1614
|
+
return max;
|
|
1615
|
+
}
|
|
1616
|
+
return max === null ? value : Math.max(max, value);
|
|
1617
|
+
}, null),
|
|
1618
|
+
[history.checkpoints]
|
|
1619
|
+
);
|
|
1136
1620
|
const publishState = latestCheckpoint !== null && (latestPublished === null || latestCheckpoint > latestPublished) ? "Unpublished" : "Published";
|
|
1137
1621
|
const assistantAvatarFallbackLabel = (config.assistantAvatarFallback || "W").trim().charAt(0).toUpperCase() || "W";
|
|
1138
1622
|
const showAssistantAvatarImage = Boolean(config.assistantAvatarUrl) && !assistantAvatarFailed;
|
|
1139
|
-
return
|
|
1623
|
+
return {
|
|
1624
|
+
isAdminMode,
|
|
1625
|
+
isAuthenticated,
|
|
1626
|
+
authConfigured,
|
|
1627
|
+
isOpen,
|
|
1628
|
+
setIsOpen,
|
|
1629
|
+
activeTab,
|
|
1630
|
+
setActiveTab,
|
|
1631
|
+
overlayRootRef,
|
|
1632
|
+
chatEndRef,
|
|
1633
|
+
publishState,
|
|
1634
|
+
selectableModels: modelOptions,
|
|
1635
|
+
showModelPicker,
|
|
1636
|
+
modelId,
|
|
1637
|
+
setModelId,
|
|
1638
|
+
includeThinking,
|
|
1639
|
+
selectedElement,
|
|
1640
|
+
clearSelectedElement,
|
|
1641
|
+
email,
|
|
1642
|
+
setEmail,
|
|
1643
|
+
password,
|
|
1644
|
+
setPassword,
|
|
1645
|
+
signingIn,
|
|
1646
|
+
signInWithPassword,
|
|
1647
|
+
message,
|
|
1648
|
+
setMessage,
|
|
1649
|
+
messages,
|
|
1650
|
+
sending,
|
|
1651
|
+
onSend,
|
|
1652
|
+
onPublish,
|
|
1653
|
+
onRollback,
|
|
1654
|
+
history,
|
|
1655
|
+
deletingCheckpointId,
|
|
1656
|
+
onDeleteCheckpoint,
|
|
1657
|
+
onClearChat,
|
|
1658
|
+
onMessageKeyDown,
|
|
1659
|
+
assistantAvatarFallbackLabel,
|
|
1660
|
+
showAssistantAvatarImage,
|
|
1661
|
+
assistantAvatarUrl: config.assistantAvatarUrl,
|
|
1662
|
+
setAssistantAvatarFailed,
|
|
1663
|
+
clearChatDisabled: sending || messages.length === 0 && !message.trim() && !selectedElement
|
|
1664
|
+
};
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
// src/overlay.tsx
|
|
1668
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1669
|
+
function WebmasterDroidOverlay() {
|
|
1670
|
+
const controller = useOverlayController();
|
|
1671
|
+
if (!controller.isAdminMode) {
|
|
1672
|
+
return null;
|
|
1673
|
+
}
|
|
1674
|
+
return /* @__PURE__ */ jsx4(Fragment2, { children: controller.isOpen ? /* @__PURE__ */ jsxs2(
|
|
1140
1675
|
"div",
|
|
1141
1676
|
{
|
|
1142
|
-
ref: overlayRootRef,
|
|
1677
|
+
ref: controller.overlayRootRef,
|
|
1143
1678
|
"data-admin-overlay-root": true,
|
|
1144
1679
|
className: "fixed bottom-4 right-4 z-[100] flex h-[62vh] w-[min(480px,calc(100vw-1.5rem))] flex-col overflow-hidden rounded-lg border border-stone-300 bg-[#f6f2eb] text-stone-900 shadow-2xl",
|
|
1145
|
-
style: {
|
|
1146
|
-
fontFamily: "var(--font-ibm-plex-mono), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace"
|
|
1147
|
-
},
|
|
1680
|
+
style: { fontFamily: OVERLAY_FONT_FAMILY },
|
|
1148
1681
|
children: [
|
|
1149
|
-
/* @__PURE__ */
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
)
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
}
|
|
1212
|
-
) })
|
|
1213
|
-
}
|
|
1214
|
-
) : null,
|
|
1215
|
-
/* @__PURE__ */ jsx3(
|
|
1216
|
-
"button",
|
|
1217
|
-
{
|
|
1218
|
-
type: "button",
|
|
1219
|
-
className: "rounded border border-stone-300 px-2 py-1 text-[11px] leading-4 text-stone-700 hover:bg-[#efe8dc]",
|
|
1220
|
-
onClick: () => setIsOpen(false),
|
|
1221
|
-
children: "Close"
|
|
1222
|
-
}
|
|
1223
|
-
)
|
|
1224
|
-
] })
|
|
1225
|
-
] }) }),
|
|
1226
|
-
!isAuthenticated ? /* @__PURE__ */ jsx3("section", { className: "flex min-h-0 flex-1 items-center justify-center bg-[#ece7dd] p-3", children: !authConfigured ? /* @__PURE__ */ jsx3("div", { className: "w-full max-w-sm rounded border border-red-300 bg-[#f8f3e9] p-3 text-[11px] leading-4 text-red-700", children: "Missing Supabase config (`supabaseUrl` / `supabaseAnonKey`)." }) : /* @__PURE__ */ jsxs("div", { className: "w-full max-w-sm rounded border border-stone-300 bg-[#f8f3e9] p-3", children: [
|
|
1227
|
-
/* @__PURE__ */ jsx3("h3", { className: "mb-2 text-[12px] font-semibold text-stone-700", children: "Sign in" }),
|
|
1228
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1229
|
-
/* @__PURE__ */ jsx3(
|
|
1230
|
-
"input",
|
|
1231
|
-
{
|
|
1232
|
-
type: "text",
|
|
1233
|
-
value: email,
|
|
1234
|
-
onChange: (event) => setEmail(event.target.value),
|
|
1235
|
-
placeholder: "login",
|
|
1236
|
-
className: "w-full rounded border border-stone-300 bg-[#f4efe6] px-2 py-1.5 text-[12px] text-stone-900 outline-none focus:border-stone-500"
|
|
1237
|
-
}
|
|
1238
|
-
),
|
|
1239
|
-
/* @__PURE__ */ jsx3(
|
|
1240
|
-
"input",
|
|
1241
|
-
{
|
|
1242
|
-
type: "password",
|
|
1243
|
-
value: password,
|
|
1244
|
-
onChange: (event) => setPassword(event.target.value),
|
|
1245
|
-
placeholder: "Password",
|
|
1246
|
-
className: "w-full rounded border border-stone-300 bg-[#f4efe6] px-2 py-1.5 text-[12px] text-stone-900 outline-none focus:border-stone-500"
|
|
1247
|
-
}
|
|
1248
|
-
),
|
|
1249
|
-
/* @__PURE__ */ jsx3(
|
|
1250
|
-
"button",
|
|
1251
|
-
{
|
|
1252
|
-
type: "button",
|
|
1253
|
-
onClick: signInWithPassword,
|
|
1254
|
-
disabled: signingIn || !email.trim() || !password,
|
|
1255
|
-
className: "w-full rounded border border-stone-700 bg-stone-800 px-2 py-1.5 text-[12px] font-medium text-stone-100 hover:bg-stone-700 disabled:cursor-not-allowed disabled:opacity-50",
|
|
1256
|
-
children: signingIn ? "Signing in" : "Sign in"
|
|
1257
|
-
}
|
|
1258
|
-
)
|
|
1259
|
-
] })
|
|
1260
|
-
] }) }) : activeTab === "chat" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1261
|
-
/* @__PURE__ */ jsxs("section", { className: "flex-1 space-y-1 overflow-auto bg-[#ece7dd] p-2", children: [
|
|
1262
|
-
messages.map((entry) => {
|
|
1263
|
-
const isAssistant = entry.role === "assistant";
|
|
1264
|
-
const isPendingAssistant = isAssistant && entry.status === "pending";
|
|
1265
|
-
return /* @__PURE__ */ jsx3(
|
|
1266
|
-
"div",
|
|
1267
|
-
{
|
|
1268
|
-
className: entry.role === "tool" ? "max-w-[96%] px-0.5 py-0 text-[10px] leading-tight text-stone-500" : `max-w-[92%] rounded-md py-1.5 text-[12px] leading-4 ${entry.role === "user" ? "ml-auto bg-[#2e2b27] px-2 text-stone-50" : entry.role === "thinking" ? "bg-[#e3dbce] px-2 text-stone-700" : isAssistant ? "relative border border-[#d6ccbb] bg-[#f8f3e9] pl-8 pr-2 text-stone-800" : "bg-[#ddd2bf] px-2 text-stone-800"}`,
|
|
1269
|
-
children: entry.role === "tool" ? /* @__PURE__ */ jsx3("span", { children: entry.text }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1270
|
-
isAssistant ? showAssistantAvatarImage ? /* @__PURE__ */ jsx3(
|
|
1271
|
-
"img",
|
|
1272
|
-
{
|
|
1273
|
-
src: config.assistantAvatarUrl,
|
|
1274
|
-
alt: "",
|
|
1275
|
-
"aria-hidden": "true",
|
|
1276
|
-
className: `pointer-events-none absolute left-2 top-1.5 h-[18px] w-[18px] select-none rounded-full border border-[#d6ccbb] bg-[#efe8dc] object-cover ${isPendingAssistant ? "animate-pulse" : ""}`,
|
|
1277
|
-
onError: () => setAssistantAvatarFailed(true)
|
|
1278
|
-
}
|
|
1279
|
-
) : /* @__PURE__ */ jsx3(
|
|
1280
|
-
"span",
|
|
1281
|
-
{
|
|
1282
|
-
"aria-hidden": "true",
|
|
1283
|
-
className: `pointer-events-none absolute left-2 top-1.5 inline-flex h-[18px] w-[18px] select-none items-center justify-center rounded-full border border-[#d6ccbb] bg-[#efe8dc] text-[9px] font-semibold text-stone-700 ${isPendingAssistant ? "animate-pulse" : ""}`,
|
|
1284
|
-
children: assistantAvatarFallbackLabel
|
|
1285
|
-
}
|
|
1286
|
-
) : null,
|
|
1287
|
-
/* @__PURE__ */ jsx3("div", { className: "max-w-none text-inherit [&_code]:rounded [&_code]:bg-stone-900/10 [&_code]:px-1 [&_ol]:list-decimal [&_ol]:pl-4 [&_p]:mb-1 [&_p:last-child]:mb-0 [&_ul]:list-disc [&_ul]:pl-4", children: isPendingAssistant && !entry.text.trim() ? /* @__PURE__ */ jsx3("span", { className: "block h-4", "aria-hidden": "true" }) : /* @__PURE__ */ jsx3(ReactMarkdown, { remarkPlugins: [remarkGfm], children: entry.text }) })
|
|
1288
|
-
] })
|
|
1289
|
-
},
|
|
1290
|
-
entry.id
|
|
1682
|
+
/* @__PURE__ */ jsx4(
|
|
1683
|
+
OverlayHeader,
|
|
1684
|
+
{
|
|
1685
|
+
isAuthenticated: controller.isAuthenticated,
|
|
1686
|
+
publishState: controller.publishState,
|
|
1687
|
+
activeTab: controller.activeTab,
|
|
1688
|
+
historyCount: controller.history.published.length + controller.history.checkpoints.length,
|
|
1689
|
+
clearChatDisabled: controller.clearChatDisabled,
|
|
1690
|
+
onPublish: () => {
|
|
1691
|
+
void controller.onPublish();
|
|
1692
|
+
},
|
|
1693
|
+
onTabChange: controller.setActiveTab,
|
|
1694
|
+
onClearChat: controller.onClearChat,
|
|
1695
|
+
onClose: () => controller.setIsOpen(false)
|
|
1696
|
+
}
|
|
1697
|
+
),
|
|
1698
|
+
!controller.isAuthenticated ? /* @__PURE__ */ jsx4(
|
|
1699
|
+
OverlayLoginPanel,
|
|
1700
|
+
{
|
|
1701
|
+
authConfigured: controller.authConfigured,
|
|
1702
|
+
email: controller.email,
|
|
1703
|
+
password: controller.password,
|
|
1704
|
+
signingIn: controller.signingIn,
|
|
1705
|
+
onEmailChange: controller.setEmail,
|
|
1706
|
+
onPasswordChange: controller.setPassword,
|
|
1707
|
+
onSignIn: () => {
|
|
1708
|
+
void controller.signInWithPassword();
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
) : controller.activeTab === "chat" ? /* @__PURE__ */ jsx4(
|
|
1712
|
+
OverlayChatPanel,
|
|
1713
|
+
{
|
|
1714
|
+
messages: controller.messages,
|
|
1715
|
+
chatEndRef: controller.chatEndRef,
|
|
1716
|
+
showAssistantAvatarImage: controller.showAssistantAvatarImage,
|
|
1717
|
+
assistantAvatarUrl: controller.assistantAvatarUrl,
|
|
1718
|
+
assistantAvatarFallbackLabel: controller.assistantAvatarFallbackLabel,
|
|
1719
|
+
onAssistantAvatarError: () => controller.setAssistantAvatarFailed(true),
|
|
1720
|
+
showModelPicker: controller.showModelPicker,
|
|
1721
|
+
selectableModels: controller.selectableModels,
|
|
1722
|
+
modelId: controller.modelId,
|
|
1723
|
+
sending: controller.sending,
|
|
1724
|
+
onModelChange: controller.setModelId,
|
|
1725
|
+
selectedElement: controller.selectedElement,
|
|
1726
|
+
onClearSelectedElement: controller.clearSelectedElement,
|
|
1727
|
+
message: controller.message,
|
|
1728
|
+
onMessageChange: controller.setMessage,
|
|
1729
|
+
onMessageKeyDown: controller.onMessageKeyDown,
|
|
1730
|
+
onSend: () => {
|
|
1731
|
+
void controller.onSend();
|
|
1732
|
+
},
|
|
1733
|
+
isAuthenticated: controller.isAuthenticated
|
|
1734
|
+
}
|
|
1735
|
+
) : /* @__PURE__ */ jsx4(
|
|
1736
|
+
OverlayHistoryPanel,
|
|
1737
|
+
{
|
|
1738
|
+
history: controller.history,
|
|
1739
|
+
deletingCheckpointId: controller.deletingCheckpointId,
|
|
1740
|
+
onRestorePublished: (item) => {
|
|
1741
|
+
void controller.onRollback(
|
|
1742
|
+
{ sourceType: "published", sourceId: item.id },
|
|
1743
|
+
`published snapshot at ${formatHistoryTime(item.createdAt)}`
|
|
1291
1744
|
);
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
),
|
|
1305
|
-
/* @__PURE__ */ jsx3(
|
|
1306
|
-
"select",
|
|
1307
|
-
{
|
|
1308
|
-
id: "admin-model-picker",
|
|
1309
|
-
value: modelId ?? selectableModels[0]?.id,
|
|
1310
|
-
onChange: (event) => setModelId(event.target.value),
|
|
1311
|
-
disabled: sending,
|
|
1312
|
-
className: "h-7 min-w-0 flex-1 rounded border border-stone-300 bg-[#f7f2e8] px-2 text-[11px] text-stone-800 outline-none focus:border-stone-500 disabled:cursor-not-allowed disabled:opacity-60",
|
|
1313
|
-
children: selectableModels.map((option) => /* @__PURE__ */ jsx3("option", { value: option.id, children: option.label }, option.id))
|
|
1314
|
-
}
|
|
1315
|
-
)
|
|
1316
|
-
] }) : null,
|
|
1317
|
-
selectedElement ? /* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center gap-1 rounded border border-stone-300 bg-[#e8dfd1] px-1.5 py-1", children: [
|
|
1318
|
-
/* @__PURE__ */ jsx3("span", { className: "inline-flex shrink-0 items-center justify-center rounded border border-stone-300 bg-[#f7f2e8] px-1 py-0.5 text-[9px] font-semibold text-stone-700", children: kindIcon(selectedElement.kind) }),
|
|
1319
|
-
/* @__PURE__ */ jsxs("p", { className: "min-w-0 flex-1 truncate text-[10px] leading-3.5 text-stone-600", children: [
|
|
1320
|
-
/* @__PURE__ */ jsx3("span", { className: "font-semibold text-stone-800", children: selectedElement.label }),
|
|
1321
|
-
/* @__PURE__ */ jsxs("span", { children: [
|
|
1322
|
-
" \xB7 ",
|
|
1323
|
-
selectedElement.path
|
|
1324
|
-
] }),
|
|
1325
|
-
selectedElement.preview ? /* @__PURE__ */ jsxs("span", { children: [
|
|
1326
|
-
" \xB7 ",
|
|
1327
|
-
selectedElement.preview
|
|
1328
|
-
] }) : null
|
|
1329
|
-
] }),
|
|
1330
|
-
/* @__PURE__ */ jsx3(
|
|
1331
|
-
"button",
|
|
1332
|
-
{
|
|
1333
|
-
type: "button",
|
|
1334
|
-
"aria-label": "Clear selected element",
|
|
1335
|
-
title: "Clear selected element",
|
|
1336
|
-
className: "inline-flex h-5 w-5 shrink-0 items-center justify-center rounded border border-stone-300 bg-[#f7f2e8] text-stone-700 hover:bg-[#efe8dc]",
|
|
1337
|
-
onClick: clearSelectedElement,
|
|
1338
|
-
children: /* @__PURE__ */ jsx3("svg", { viewBox: "0 0 20 20", fill: "none", className: "h-3 w-3", "aria-hidden": "true", children: /* @__PURE__ */ jsx3(
|
|
1339
|
-
"path",
|
|
1340
|
-
{
|
|
1341
|
-
d: "M5 5L15 15M15 5L5 15",
|
|
1342
|
-
stroke: "currentColor",
|
|
1343
|
-
strokeWidth: "1.5",
|
|
1344
|
-
strokeLinecap: "round"
|
|
1345
|
-
}
|
|
1346
|
-
) })
|
|
1347
|
-
}
|
|
1348
|
-
)
|
|
1349
|
-
] }) : null,
|
|
1350
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-1.5", children: [
|
|
1351
|
-
/* @__PURE__ */ jsx3(
|
|
1352
|
-
"textarea",
|
|
1353
|
-
{
|
|
1354
|
-
value: message,
|
|
1355
|
-
onChange: (event) => setMessage(event.target.value),
|
|
1356
|
-
onKeyDown: onMessageKeyDown,
|
|
1357
|
-
rows: 2,
|
|
1358
|
-
placeholder: "Ask the agent to edit text, image URLs, or theme tokens",
|
|
1359
|
-
className: "flex-1 resize-none rounded border border-stone-300 bg-[#f4efe6] px-2 py-1.5 text-[12px] leading-4 text-stone-900 outline-none placeholder:text-stone-500 focus:border-stone-500"
|
|
1360
|
-
}
|
|
1361
|
-
),
|
|
1362
|
-
/* @__PURE__ */ jsx3(
|
|
1363
|
-
"button",
|
|
1364
|
-
{
|
|
1365
|
-
type: "button",
|
|
1366
|
-
onClick: onSend,
|
|
1367
|
-
disabled: !isAuthenticated || sending || !message.trim(),
|
|
1368
|
-
className: "rounded border border-stone-500 bg-stone-600 px-3 py-1.5 text-[12px] font-semibold text-stone-100 hover:bg-stone-700 disabled:cursor-not-allowed disabled:opacity-50",
|
|
1369
|
-
children: sending ? "Sending" : "Send"
|
|
1370
|
-
}
|
|
1371
|
-
)
|
|
1372
|
-
] })
|
|
1373
|
-
] })
|
|
1374
|
-
] }) : /* @__PURE__ */ jsx3("section", { className: "flex min-h-0 flex-1 flex-col p-2 text-[11px] leading-4", children: /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col gap-2 overflow-hidden", children: [
|
|
1375
|
-
/* @__PURE__ */ jsxs("div", { className: "rounded border border-stone-300 bg-[#f8f3e9]", children: [
|
|
1376
|
-
/* @__PURE__ */ jsxs("div", { className: "border-b border-stone-200 px-2 py-1 font-semibold text-stone-700", children: [
|
|
1377
|
-
"Published (",
|
|
1378
|
-
history.published.length,
|
|
1379
|
-
")"
|
|
1380
|
-
] }),
|
|
1381
|
-
/* @__PURE__ */ jsx3("div", { className: "max-h-40 overflow-auto px-2 py-1.5", children: history.published.length > 0 ? /* @__PURE__ */ jsx3("div", { className: "space-y-1", children: history.published.map((item) => /* @__PURE__ */ jsxs(
|
|
1382
|
-
"div",
|
|
1383
|
-
{
|
|
1384
|
-
className: "flex items-center justify-between gap-2 rounded border border-stone-200 bg-[#f2ecdf] px-2 py-1",
|
|
1385
|
-
children: [
|
|
1386
|
-
/* @__PURE__ */ jsx3("span", { className: "truncate text-[10px] text-stone-700", children: formatHistoryTime(item.createdAt) }),
|
|
1387
|
-
/* @__PURE__ */ jsx3(
|
|
1388
|
-
"button",
|
|
1389
|
-
{
|
|
1390
|
-
type: "button",
|
|
1391
|
-
className: "rounded border border-stone-300 bg-[#f7f2e8] px-1.5 py-0.5 text-[10px] text-stone-700 hover:bg-[#efe8dc]",
|
|
1392
|
-
onClick: () => onRollback(
|
|
1393
|
-
{ sourceType: "published", sourceId: item.id },
|
|
1394
|
-
`published snapshot at ${formatHistoryTime(item.createdAt)}`
|
|
1395
|
-
),
|
|
1396
|
-
children: "Restore"
|
|
1397
|
-
}
|
|
1398
|
-
)
|
|
1399
|
-
]
|
|
1400
|
-
},
|
|
1401
|
-
`pub-${item.id}`
|
|
1402
|
-
)) }) : /* @__PURE__ */ jsx3("p", { className: "text-[10px] text-stone-500", children: "No published snapshots." }) })
|
|
1403
|
-
] }),
|
|
1404
|
-
/* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col rounded border border-stone-300 bg-[#f8f3e9]", children: [
|
|
1405
|
-
/* @__PURE__ */ jsxs("div", { className: "border-b border-stone-200 px-2 py-1 font-semibold text-stone-700", children: [
|
|
1406
|
-
"Checkpoints (",
|
|
1407
|
-
history.checkpoints.length,
|
|
1408
|
-
")"
|
|
1409
|
-
] }),
|
|
1410
|
-
/* @__PURE__ */ jsx3("div", { className: "min-h-0 flex-1 overflow-auto px-2 py-1.5", children: history.checkpoints.length > 0 ? /* @__PURE__ */ jsx3("div", { className: "space-y-1", children: history.checkpoints.map((item) => /* @__PURE__ */ jsxs(
|
|
1411
|
-
"div",
|
|
1412
|
-
{
|
|
1413
|
-
className: "flex items-start justify-between gap-2 rounded border border-stone-200 bg-[#f2ecdf] px-2 py-1",
|
|
1414
|
-
children: [
|
|
1415
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
1416
|
-
/* @__PURE__ */ jsx3("p", { className: "truncate text-[10px] text-stone-700", children: formatHistoryTime(item.createdAt) }),
|
|
1417
|
-
item.reason ? /* @__PURE__ */ jsx3("p", { className: "truncate text-[10px] text-stone-500", children: item.reason }) : null
|
|
1418
|
-
] }),
|
|
1419
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1420
|
-
/* @__PURE__ */ jsx3(
|
|
1421
|
-
"button",
|
|
1422
|
-
{
|
|
1423
|
-
type: "button",
|
|
1424
|
-
disabled: deletingCheckpointId === item.id,
|
|
1425
|
-
className: "rounded border border-stone-300 bg-[#f7f2e8] px-1.5 py-0.5 text-[10px] text-stone-700 hover:bg-[#efe8dc] disabled:cursor-not-allowed disabled:opacity-50",
|
|
1426
|
-
onClick: () => onRollback(
|
|
1427
|
-
{ sourceType: "checkpoint", sourceId: item.id },
|
|
1428
|
-
`checkpoint at ${formatHistoryTime(item.createdAt)}`
|
|
1429
|
-
),
|
|
1430
|
-
children: "Restore"
|
|
1431
|
-
}
|
|
1432
|
-
),
|
|
1433
|
-
/* @__PURE__ */ jsx3(
|
|
1434
|
-
"button",
|
|
1435
|
-
{
|
|
1436
|
-
type: "button",
|
|
1437
|
-
"aria-label": "Delete checkpoint",
|
|
1438
|
-
title: "Delete checkpoint",
|
|
1439
|
-
disabled: deletingCheckpointId === item.id,
|
|
1440
|
-
className: "inline-flex h-6 w-6 items-center justify-center rounded border border-stone-300 bg-[#f7f2e8] text-stone-700 hover:bg-[#efe8dc] disabled:cursor-not-allowed disabled:opacity-50",
|
|
1441
|
-
onClick: () => {
|
|
1442
|
-
void onDeleteCheckpoint(item);
|
|
1443
|
-
},
|
|
1444
|
-
children: /* @__PURE__ */ jsx3("svg", { viewBox: "0 0 20 20", fill: "none", className: "h-3.5 w-3.5", "aria-hidden": "true", children: /* @__PURE__ */ jsx3(
|
|
1445
|
-
"path",
|
|
1446
|
-
{
|
|
1447
|
-
d: "M4.5 5.5H15.5M8 3.75H12M7 7.5V13.5M10 7.5V13.5M13 7.5V13.5M6.5 5.5L7 15C7.03 15.6 7.53 16.08 8.13 16.08H11.87C12.47 16.08 12.97 15.6 13 15L13.5 5.5",
|
|
1448
|
-
stroke: "currentColor",
|
|
1449
|
-
strokeWidth: "1.4",
|
|
1450
|
-
strokeLinecap: "round",
|
|
1451
|
-
strokeLinejoin: "round"
|
|
1452
|
-
}
|
|
1453
|
-
) })
|
|
1454
|
-
}
|
|
1455
|
-
)
|
|
1456
|
-
] })
|
|
1457
|
-
]
|
|
1458
|
-
},
|
|
1459
|
-
`cp-${item.id}`
|
|
1460
|
-
)) }) : /* @__PURE__ */ jsx3("p", { className: "text-[10px] text-stone-500", children: "No checkpoints yet." }) })
|
|
1461
|
-
] })
|
|
1462
|
-
] }) })
|
|
1745
|
+
},
|
|
1746
|
+
onRestoreCheckpoint: (item) => {
|
|
1747
|
+
void controller.onRollback(
|
|
1748
|
+
{ sourceType: "checkpoint", sourceId: item.id },
|
|
1749
|
+
`checkpoint at ${formatHistoryTime(item.createdAt)}`
|
|
1750
|
+
);
|
|
1751
|
+
},
|
|
1752
|
+
onDeleteCheckpoint: (item) => {
|
|
1753
|
+
void controller.onDeleteCheckpoint(item);
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
)
|
|
1463
1757
|
]
|
|
1464
1758
|
}
|
|
1465
|
-
) : /* @__PURE__ */
|
|
1466
|
-
"button",
|
|
1467
|
-
{
|
|
1468
|
-
type: "button",
|
|
1469
|
-
onClick: () => setIsOpen(true),
|
|
1470
|
-
className: "fixed bottom-4 right-4 z-[100] rounded-full border border-stone-600 bg-stone-700 px-4 py-2 text-[12px] font-semibold text-stone-100 shadow-xl hover:bg-stone-800",
|
|
1471
|
-
style: {
|
|
1472
|
-
fontFamily: "var(--font-ibm-plex-mono), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace"
|
|
1473
|
-
},
|
|
1474
|
-
children: "Chat to Webmaster"
|
|
1475
|
-
}
|
|
1476
|
-
) });
|
|
1759
|
+
) : /* @__PURE__ */ jsx4(OverlayLauncherButton, { onOpen: () => controller.setIsOpen(true) }) });
|
|
1477
1760
|
}
|
|
1478
1761
|
|
|
1479
1762
|
// src/runtime.tsx
|
|
@@ -1484,7 +1767,10 @@ import {
|
|
|
1484
1767
|
useMemo as useMemo3,
|
|
1485
1768
|
useState as useState3
|
|
1486
1769
|
} from "react";
|
|
1487
|
-
import {
|
|
1770
|
+
import {
|
|
1771
|
+
createDefaultCmsDocument
|
|
1772
|
+
} from "@webmaster-droid/contracts";
|
|
1773
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1488
1774
|
var CmsRuntimeContext = createContext3(null);
|
|
1489
1775
|
function createThemeCssVariables(tokens) {
|
|
1490
1776
|
return {
|
|
@@ -1499,6 +1785,10 @@ function createThemeCssVariables(tokens) {
|
|
|
1499
1785
|
}
|
|
1500
1786
|
function CmsRuntimeBridge(props) {
|
|
1501
1787
|
const { config, isAdminMode, isAuthenticated, token, refreshKey } = useWebmasterDroid();
|
|
1788
|
+
const defaultDocument = useMemo3(
|
|
1789
|
+
() => props.fallbackDocument ?? createDefaultCmsDocument(),
|
|
1790
|
+
[props.fallbackDocument]
|
|
1791
|
+
);
|
|
1502
1792
|
const stage = useMemo3(
|
|
1503
1793
|
() => isAdminMode && isAuthenticated ? "draft" : "live",
|
|
1504
1794
|
[isAdminMode, isAuthenticated]
|
|
@@ -1509,7 +1799,7 @@ function CmsRuntimeBridge(props) {
|
|
|
1509
1799
|
);
|
|
1510
1800
|
const [state, setState] = useState3({
|
|
1511
1801
|
requestKey: "",
|
|
1512
|
-
document:
|
|
1802
|
+
document: defaultDocument,
|
|
1513
1803
|
error: null
|
|
1514
1804
|
});
|
|
1515
1805
|
useEffect3(() => {
|
|
@@ -1530,14 +1820,14 @@ function CmsRuntimeBridge(props) {
|
|
|
1530
1820
|
const message = error2 instanceof Error ? error2.message : "Failed to load content.";
|
|
1531
1821
|
setState({
|
|
1532
1822
|
requestKey,
|
|
1533
|
-
document:
|
|
1823
|
+
document: defaultDocument,
|
|
1534
1824
|
error: message
|
|
1535
1825
|
});
|
|
1536
1826
|
});
|
|
1537
1827
|
return () => {
|
|
1538
1828
|
ignore = true;
|
|
1539
1829
|
};
|
|
1540
|
-
}, [config.apiBaseUrl,
|
|
1830
|
+
}, [config.apiBaseUrl, defaultDocument, requestKey, stage, token]);
|
|
1541
1831
|
const loading = state.requestKey !== requestKey;
|
|
1542
1832
|
const error = loading ? null : state.error;
|
|
1543
1833
|
const value = useMemo3(
|
|
@@ -1549,14 +1839,14 @@ function CmsRuntimeBridge(props) {
|
|
|
1549
1839
|
}),
|
|
1550
1840
|
[error, loading, stage, state.document]
|
|
1551
1841
|
);
|
|
1552
|
-
const content = props.applyThemeTokens ? /* @__PURE__ */
|
|
1553
|
-
return /* @__PURE__ */
|
|
1554
|
-
/* @__PURE__ */
|
|
1555
|
-
props.includeOverlay ? /* @__PURE__ */
|
|
1842
|
+
const content = props.applyThemeTokens ? /* @__PURE__ */ jsx5("div", { style: createThemeCssVariables(value.document.themeTokens), children: props.children }) : props.children;
|
|
1843
|
+
return /* @__PURE__ */ jsxs3(CmsRuntimeContext.Provider, { value, children: [
|
|
1844
|
+
/* @__PURE__ */ jsx5(EditableProvider, { document: value.document, mode: stage, enabled: isAdminMode, children: content }),
|
|
1845
|
+
props.includeOverlay ? /* @__PURE__ */ jsx5(WebmasterDroidOverlay, {}) : null
|
|
1556
1846
|
] });
|
|
1557
1847
|
}
|
|
1558
1848
|
function WebmasterDroidRuntime(props) {
|
|
1559
|
-
return /* @__PURE__ */
|
|
1849
|
+
return /* @__PURE__ */ jsx5(WebmasterDroidProvider, { config: props.config, children: /* @__PURE__ */ jsx5(
|
|
1560
1850
|
CmsRuntimeBridge,
|
|
1561
1851
|
{
|
|
1562
1852
|
fallbackDocument: props.fallbackDocument,
|