@webmaster-droid/web 0.1.1 → 0.3.0

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.js CHANGED
@@ -642,6 +642,13 @@ function getSupabaseBrowserClient(config) {
642
642
  // src/context.tsx
643
643
  import { jsx as jsx2 } from "react/jsx-runtime";
644
644
  var WebmasterDroidContext = createContext2(null);
645
+ var DEFAULT_MODEL_CAPABILITIES = {
646
+ contentEdit: true,
647
+ themeTokenEdit: true,
648
+ imageGenerate: false,
649
+ imageEdit: false,
650
+ visionAssist: false
651
+ };
645
652
  function WebmasterDroidProvider(props) {
646
653
  const resolvedConfig = useMemo(
647
654
  () => resolveWebmasterDroidConfig(props.config),
@@ -673,6 +680,9 @@ function WebmasterDroidProvider(props) {
673
680
  const [modelId, setModelId] = useState(null);
674
681
  const [showModelPickerState, setShowModelPickerState] = useState(false);
675
682
  const [modelOptions, setModelOptions] = useState([]);
683
+ const [capabilities, setCapabilities] = useState(
684
+ DEFAULT_MODEL_CAPABILITIES
685
+ );
676
686
  const [includeThinking, setIncludeThinking] = useState(false);
677
687
  const [refreshKey, setRefreshKey] = useState(0);
678
688
  const [selectedElement, setSelectedElement] = useState(null);
@@ -707,6 +717,7 @@ function WebmasterDroidProvider(props) {
707
717
  const preferredDefault = options.some((option) => option.id === models.defaultModelId) ? models.defaultModelId : options[0]?.id ?? models.defaultModelId;
708
718
  setShowModelPickerState(models.showModelPicker);
709
719
  setModelOptions(options);
720
+ setCapabilities(models.capabilities ?? DEFAULT_MODEL_CAPABILITIES);
710
721
  setModelId((current) => {
711
722
  if (current && options.some((option) => option.id === current)) {
712
723
  return current;
@@ -717,6 +728,7 @@ function WebmasterDroidProvider(props) {
717
728
  if (!ignore) {
718
729
  setShowModelPickerState(false);
719
730
  setModelOptions([]);
731
+ setCapabilities(DEFAULT_MODEL_CAPABILITIES);
720
732
  setModelId((current) => current ?? resolvedConfig.defaultModelId);
721
733
  }
722
734
  });
@@ -737,6 +749,7 @@ function WebmasterDroidProvider(props) {
737
749
  setModelId,
738
750
  showModelPicker,
739
751
  modelOptions,
752
+ capabilities,
740
753
  includeThinking,
741
754
  setIncludeThinking,
742
755
  refreshKey,
@@ -753,6 +766,7 @@ function WebmasterDroidProvider(props) {
753
766
  modelId,
754
767
  showModelPicker,
755
768
  modelOptions,
769
+ capabilities,
756
770
  includeThinking,
757
771
  refreshKey,
758
772
  authConfigured,
@@ -769,6 +783,9 @@ function useWebmasterDroid() {
769
783
  return context;
770
784
  }
771
785
 
786
+ // src/overlay.tsx
787
+ import { useEffect as useEffect3 } from "react";
788
+
772
789
  // src/overlay/utils.ts
773
790
  function createMessage(role, text, status) {
774
791
  return {
@@ -863,6 +880,84 @@ var OVERLAY_FONT_FAMILY = "var(--font-ibm-plex-mono), ui-monospace, SFMono-Regul
863
880
  // src/overlay/components.tsx
864
881
  import ReactMarkdown from "react-markdown";
865
882
  import remarkGfm from "remark-gfm";
883
+
884
+ // src/overlay/class-names.ts
885
+ var OVERLAY_CLASS_NAMES = {
886
+ panel: "wmd-panel",
887
+ launcherButton: "wmd-launcher",
888
+ header: "wmd-header",
889
+ headerRow: "wmd-header-row",
890
+ publishState: "wmd-publish-state",
891
+ publishStatePublished: "wmd-publish-state--published",
892
+ publishStateUnpublished: "wmd-publish-state--unpublished",
893
+ publishButton: "wmd-publish-button",
894
+ tabs: "wmd-tabs",
895
+ tabButton: "wmd-tab-button",
896
+ tabButtonActive: "wmd-tab-button--active",
897
+ loginTitle: "wmd-login-title",
898
+ headerActions: "wmd-header-actions",
899
+ iconButton: "wmd-icon-button",
900
+ icon: "wmd-icon",
901
+ closeButton: "wmd-close-button",
902
+ loginSection: "wmd-login-section",
903
+ loginWarning: "wmd-login-warning",
904
+ loginCard: "wmd-login-card",
905
+ loginHeading: "wmd-login-heading",
906
+ loginFields: "wmd-login-fields",
907
+ fieldInput: "wmd-field-input",
908
+ primaryButton: "wmd-primary-button",
909
+ chatSection: "wmd-chat-section",
910
+ message: "wmd-message",
911
+ messageTool: "wmd-message--tool",
912
+ messageUser: "wmd-message--user",
913
+ messageThinking: "wmd-message--thinking",
914
+ messageAssistant: "wmd-message--assistant",
915
+ messageFallback: "wmd-message--fallback",
916
+ assistantAvatar: "wmd-assistant-avatar",
917
+ assistantAvatarFallback: "wmd-assistant-avatar-fallback",
918
+ assistantAvatarPending: "wmd-assistant-avatar--pending",
919
+ markdownContent: "wmd-markdown-content",
920
+ pendingShim: "wmd-pending-shim",
921
+ footer: "wmd-footer",
922
+ modelRow: "wmd-model-row",
923
+ modelLabel: "wmd-model-label",
924
+ modelSelect: "wmd-model-select",
925
+ selectedElement: "wmd-selected-element",
926
+ selectedKind: "wmd-selected-kind",
927
+ selectedText: "wmd-selected-text",
928
+ selectedLabel: "wmd-selected-label",
929
+ selectedClearButton: "wmd-selected-clear",
930
+ selectedClearIcon: "wmd-selected-clear-icon",
931
+ composerRow: "wmd-composer-row",
932
+ composerInput: "wmd-composer-input",
933
+ sendButton: "wmd-send-button",
934
+ historySection: "wmd-history-section",
935
+ historyColumns: "wmd-history-columns",
936
+ historyCard: "wmd-history-card",
937
+ historyCardTitle: "wmd-history-card-title",
938
+ historyList: "wmd-history-list",
939
+ historyStack: "wmd-history-stack",
940
+ historyItem: "wmd-history-item",
941
+ historyItemCheckpoint: "wmd-history-item--checkpoint",
942
+ historyTextBlock: "wmd-history-text",
943
+ historyTimestamp: "wmd-history-timestamp",
944
+ historyReason: "wmd-history-reason",
945
+ historyActions: "wmd-history-actions",
946
+ historyAction: "wmd-history-action",
947
+ historyDelete: "wmd-history-delete",
948
+ emptyText: "wmd-empty-text"
949
+ };
950
+ function overlayClass(slot, className) {
951
+ if (!className) {
952
+ return OVERLAY_CLASS_NAMES[slot];
953
+ }
954
+ return `${OVERLAY_CLASS_NAMES[slot]} ${className}`;
955
+ }
956
+ function joinClassNames(...parts) {
957
+ return parts.filter(Boolean).join(" ");
958
+ }
959
+
960
+ // src/overlay/components.tsx
866
961
  import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
867
962
  function OverlayHeader({
868
963
  isAuthenticated,
@@ -875,12 +970,15 @@ function OverlayHeader({
875
970
  onClearChat,
876
971
  onClose
877
972
  }) {
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: [
973
+ return /* @__PURE__ */ jsx3("header", { className: overlayClass("header", "border-b border-stone-300 bg-[#f3eee5] p-2"), children: /* @__PURE__ */ jsxs("div", { className: overlayClass("headerRow", "flex items-center gap-2"), children: [
879
974
  isAuthenticated ? /* @__PURE__ */ jsxs(Fragment, { children: [
880
975
  /* @__PURE__ */ jsx3(
881
976
  "span",
882
977
  {
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"}`,
978
+ className: joinClassNames(
979
+ overlayClass("publishState", "rounded border px-1.5 py-0.5 text-[10px] font-medium leading-4"),
980
+ publishState === "Published" ? overlayClass("publishStatePublished", "border-stone-300 bg-[#ece5d9] text-stone-600") : overlayClass("publishStateUnpublished", "border-stone-500 bg-[#ded4c3] text-stone-800")
981
+ ),
884
982
  children: publishState
885
983
  }
886
984
  ),
@@ -888,18 +986,24 @@ function OverlayHeader({
888
986
  "button",
889
987
  {
890
988
  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",
989
+ className: overlayClass(
990
+ "publishButton",
991
+ "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"
992
+ ),
892
993
  onClick: onPublish,
893
994
  disabled: !isAuthenticated,
894
995
  children: "Publish"
895
996
  }
896
997
  ),
897
- /* @__PURE__ */ jsxs("div", { className: "inline-flex rounded-md border border-stone-300 bg-[#e8dfd1] p-0.5", children: [
998
+ /* @__PURE__ */ jsxs("div", { className: overlayClass("tabs", "inline-flex rounded-md border border-stone-300 bg-[#e8dfd1] p-0.5"), children: [
898
999
  /* @__PURE__ */ jsx3(
899
1000
  "button",
900
1001
  {
901
1002
  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"}`,
1003
+ className: joinClassNames(
1004
+ overlayClass("tabButton", "rounded px-2 py-1 text-[11px] font-medium leading-4"),
1005
+ activeTab === "chat" ? overlayClass("tabButtonActive", "bg-[#f7f2e8] text-stone-900 shadow-sm") : "text-stone-600 hover:text-stone-900"
1006
+ ),
903
1007
  onClick: () => onTabChange("chat"),
904
1008
  children: "Chat"
905
1009
  }
@@ -908,7 +1012,10 @@ function OverlayHeader({
908
1012
  "button",
909
1013
  {
910
1014
  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"}`,
1015
+ className: joinClassNames(
1016
+ overlayClass("tabButton", "rounded px-2 py-1 text-[11px] font-medium leading-4"),
1017
+ activeTab === "history" ? overlayClass("tabButtonActive", "bg-[#f7f2e8] text-stone-900 shadow-sm") : "text-stone-600 hover:text-stone-900"
1018
+ ),
912
1019
  onClick: () => onTabChange("history"),
913
1020
  children: [
914
1021
  "History (",
@@ -918,8 +1025,8 @@ function OverlayHeader({
918
1025
  }
919
1026
  )
920
1027
  ] })
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: [
1028
+ ] }) : /* @__PURE__ */ jsx3("h2", { className: overlayClass("loginTitle", "text-[12px] font-semibold text-stone-700"), children: "Login" }),
1029
+ /* @__PURE__ */ jsxs("div", { className: overlayClass("headerActions", "ml-auto flex items-center gap-1"), children: [
923
1030
  isAuthenticated ? /* @__PURE__ */ jsx3(
924
1031
  "button",
925
1032
  {
@@ -927,25 +1034,40 @@ function OverlayHeader({
927
1034
  "aria-label": "Clear chat",
928
1035
  title: "Clear chat",
929
1036
  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",
1037
+ className: overlayClass(
1038
+ "iconButton",
1039
+ "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"
1040
+ ),
931
1041
  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",
1042
+ children: /* @__PURE__ */ jsx3(
1043
+ "svg",
934
1044
  {
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"
1045
+ viewBox: "0 0 20 20",
1046
+ fill: "none",
1047
+ className: overlayClass("icon", "h-3.5 w-3.5"),
1048
+ "aria-hidden": "true",
1049
+ children: /* @__PURE__ */ jsx3(
1050
+ "path",
1051
+ {
1052
+ 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",
1053
+ stroke: "currentColor",
1054
+ strokeWidth: "1.4",
1055
+ strokeLinecap: "round",
1056
+ strokeLinejoin: "round"
1057
+ }
1058
+ )
940
1059
  }
941
- ) })
1060
+ )
942
1061
  }
943
1062
  ) : null,
944
1063
  /* @__PURE__ */ jsx3(
945
1064
  "button",
946
1065
  {
947
1066
  type: "button",
948
- className: "rounded border border-stone-300 px-2 py-1 text-[11px] leading-4 text-stone-700 hover:bg-[#efe8dc]",
1067
+ className: overlayClass(
1068
+ "closeButton",
1069
+ "rounded border border-stone-300 px-2 py-1 text-[11px] leading-4 text-stone-700 hover:bg-[#efe8dc]"
1070
+ ),
949
1071
  onClick: onClose,
950
1072
  children: "Close"
951
1073
  }
@@ -962,41 +1084,68 @@ function OverlayLoginPanel({
962
1084
  onPasswordChange,
963
1085
  onSignIn
964
1086
  }) {
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
- }
1087
+ return /* @__PURE__ */ jsx3(
1088
+ "section",
1089
+ {
1090
+ className: overlayClass(
1091
+ "loginSection",
1092
+ "flex min-h-0 flex-1 items-center justify-center bg-[#ece7dd] p-3"
987
1093
  ),
988
- /* @__PURE__ */ jsx3(
989
- "button",
1094
+ children: !authConfigured ? /* @__PURE__ */ jsx3(
1095
+ "div",
990
1096
  {
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"
1097
+ className: overlayClass(
1098
+ "loginWarning",
1099
+ "w-full max-w-sm rounded border border-red-300 bg-[#f8f3e9] p-3 text-[11px] leading-4 text-red-700"
1100
+ ),
1101
+ children: "Missing Supabase config (`supabaseUrl` / `supabaseAnonKey`)."
996
1102
  }
997
- )
998
- ] })
999
- ] }) });
1103
+ ) : /* @__PURE__ */ jsxs("div", { className: overlayClass("loginCard", "w-full max-w-sm rounded border border-stone-300 bg-[#f8f3e9] p-3"), children: [
1104
+ /* @__PURE__ */ jsx3("h3", { className: overlayClass("loginHeading", "mb-2 text-[12px] font-semibold text-stone-700"), children: "Sign in" }),
1105
+ /* @__PURE__ */ jsxs("div", { className: overlayClass("loginFields", "space-y-2"), children: [
1106
+ /* @__PURE__ */ jsx3(
1107
+ "input",
1108
+ {
1109
+ type: "text",
1110
+ value: email,
1111
+ onChange: (event) => onEmailChange(event.target.value),
1112
+ placeholder: "login",
1113
+ className: overlayClass(
1114
+ "fieldInput",
1115
+ "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"
1116
+ )
1117
+ }
1118
+ ),
1119
+ /* @__PURE__ */ jsx3(
1120
+ "input",
1121
+ {
1122
+ type: "password",
1123
+ value: password,
1124
+ onChange: (event) => onPasswordChange(event.target.value),
1125
+ placeholder: "Password",
1126
+ className: overlayClass(
1127
+ "fieldInput",
1128
+ "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"
1129
+ )
1130
+ }
1131
+ ),
1132
+ /* @__PURE__ */ jsx3(
1133
+ "button",
1134
+ {
1135
+ type: "button",
1136
+ onClick: onSignIn,
1137
+ disabled: signingIn || !email.trim() || !password,
1138
+ className: overlayClass(
1139
+ "primaryButton",
1140
+ "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"
1141
+ ),
1142
+ children: signingIn ? "Signing in" : "Sign in"
1143
+ }
1144
+ )
1145
+ ] })
1146
+ ] })
1147
+ }
1148
+ );
1000
1149
  }
1001
1150
  function OverlayChatPanel({
1002
1151
  messages,
@@ -1019,14 +1168,29 @@ function OverlayChatPanel({
1019
1168
  isAuthenticated
1020
1169
  }) {
1021
1170
  return /* @__PURE__ */ jsxs(Fragment, { children: [
1022
- /* @__PURE__ */ jsxs("section", { className: "flex-1 space-y-1 overflow-auto bg-[#ece7dd] p-2", children: [
1171
+ /* @__PURE__ */ jsxs("section", { className: overlayClass("chatSection", "flex-1 space-y-1 overflow-auto bg-[#ece7dd] p-2"), children: [
1023
1172
  messages.map((entry) => {
1024
1173
  const isAssistant = entry.role === "assistant";
1025
1174
  const isPendingAssistant = isAssistant && entry.status === "pending";
1026
1175
  return /* @__PURE__ */ jsx3(
1027
1176
  "div",
1028
1177
  {
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"}`,
1178
+ className: joinClassNames(
1179
+ overlayClass("message"),
1180
+ entry.role === "tool" ? overlayClass("messageTool", "max-w-[96%] px-0.5 py-0 text-[10px] leading-tight text-stone-500") : entry.role === "user" ? overlayClass(
1181
+ "messageUser",
1182
+ "ml-auto max-w-[92%] rounded-md bg-[#2e2b27] px-2 py-1.5 text-[12px] leading-4 text-stone-50"
1183
+ ) : entry.role === "thinking" ? overlayClass(
1184
+ "messageThinking",
1185
+ "max-w-[92%] rounded-md bg-[#e3dbce] px-2 py-1.5 text-[12px] leading-4 text-stone-700"
1186
+ ) : isAssistant ? overlayClass(
1187
+ "messageAssistant",
1188
+ "relative max-w-[92%] rounded-md border border-[#d6ccbb] bg-[#f8f3e9] pl-8 pr-2 py-1.5 text-[12px] leading-4 text-stone-800"
1189
+ ) : overlayClass(
1190
+ "messageFallback",
1191
+ "max-w-[92%] rounded-md bg-[#ddd2bf] px-2 py-1.5 text-[12px] leading-4 text-stone-800"
1192
+ )
1193
+ ),
1030
1194
  children: entry.role === "tool" ? /* @__PURE__ */ jsx3("span", { children: entry.text }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1031
1195
  isAssistant ? showAssistantAvatarImage ? /* @__PURE__ */ jsx3(
1032
1196
  "img",
@@ -1034,18 +1198,39 @@ function OverlayChatPanel({
1034
1198
  src: assistantAvatarUrl,
1035
1199
  alt: "",
1036
1200
  "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" : ""}`,
1201
+ className: joinClassNames(
1202
+ overlayClass(
1203
+ "assistantAvatar",
1204
+ "pointer-events-none absolute left-2 top-1.5 h-[18px] w-[18px] select-none rounded-full border border-[#d6ccbb] bg-[#efe8dc] object-cover"
1205
+ ),
1206
+ isPendingAssistant ? overlayClass("assistantAvatarPending", "animate-pulse") : null
1207
+ ),
1038
1208
  onError: onAssistantAvatarError
1039
1209
  }
1040
1210
  ) : /* @__PURE__ */ jsx3(
1041
1211
  "span",
1042
1212
  {
1043
1213
  "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" : ""}`,
1214
+ className: joinClassNames(
1215
+ overlayClass(
1216
+ "assistantAvatarFallback",
1217
+ "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"
1218
+ ),
1219
+ isPendingAssistant ? overlayClass("assistantAvatarPending", "animate-pulse") : null
1220
+ ),
1045
1221
  children: assistantAvatarFallbackLabel
1046
1222
  }
1047
1223
  ) : 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 }) })
1224
+ /* @__PURE__ */ jsx3(
1225
+ "div",
1226
+ {
1227
+ className: overlayClass(
1228
+ "markdownContent",
1229
+ "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"
1230
+ ),
1231
+ children: isPendingAssistant && !entry.text.trim() ? /* @__PURE__ */ jsx3("span", { className: overlayClass("pendingShim", "block h-4"), "aria-hidden": "true" }) : /* @__PURE__ */ jsx3(ReactMarkdown, { remarkPlugins: [remarkGfm], children: entry.text })
1232
+ }
1233
+ )
1049
1234
  ] })
1050
1235
  },
1051
1236
  entry.id
@@ -1053,13 +1238,13 @@ function OverlayChatPanel({
1053
1238
  }),
1054
1239
  /* @__PURE__ */ jsx3("div", { ref: chatEndRef })
1055
1240
  ] }),
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: [
1241
+ /* @__PURE__ */ jsxs("footer", { className: overlayClass("footer", "border-t border-stone-300 bg-[#f3eee5] p-2"), children: [
1242
+ showModelPicker && selectableModels.length > 1 ? /* @__PURE__ */ jsxs("div", { className: overlayClass("modelRow", "mb-1 flex items-center gap-1.5"), children: [
1058
1243
  /* @__PURE__ */ jsx3(
1059
1244
  "label",
1060
1245
  {
1061
1246
  htmlFor: "admin-model-picker",
1062
- className: "text-[10px] font-semibold uppercase tracking-wide text-stone-600",
1247
+ className: overlayClass("modelLabel", "text-[10px] font-semibold uppercase tracking-wide text-stone-600"),
1063
1248
  children: "Model"
1064
1249
  }
1065
1250
  ),
@@ -1070,45 +1255,78 @@ function OverlayChatPanel({
1070
1255
  value: modelId ?? selectableModels[0]?.id,
1071
1256
  onChange: (event) => onModelChange(event.target.value),
1072
1257
  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",
1258
+ className: overlayClass(
1259
+ "modelSelect",
1260
+ "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"
1261
+ ),
1074
1262
  children: selectableModels.map((option) => /* @__PURE__ */ jsx3("option", { value: option.id, children: option.label }, option.id))
1075
1263
  }
1076
1264
  )
1077
1265
  ] }) : 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",
1266
+ selectedElement ? /* @__PURE__ */ jsxs(
1267
+ "div",
1268
+ {
1269
+ className: overlayClass(
1270
+ "selectedElement",
1271
+ "mb-1 flex items-center gap-1 rounded border border-stone-300 bg-[#e8dfd1] px-1.5 py-1"
1272
+ ),
1273
+ children: [
1274
+ /* @__PURE__ */ jsx3(
1275
+ "span",
1101
1276
  {
1102
- d: "M5 5L15 15M15 5L5 15",
1103
- stroke: "currentColor",
1104
- strokeWidth: "1.5",
1105
- strokeLinecap: "round"
1277
+ className: overlayClass(
1278
+ "selectedKind",
1279
+ "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"
1280
+ ),
1281
+ children: kindIcon(selectedElement.kind)
1106
1282
  }
1107
- ) })
1108
- }
1109
- )
1110
- ] }) : null,
1111
- /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5", children: [
1283
+ ),
1284
+ /* @__PURE__ */ jsxs("p", { className: overlayClass("selectedText", "min-w-0 flex-1 truncate text-[10px] leading-3.5 text-stone-600"), children: [
1285
+ /* @__PURE__ */ jsx3("span", { className: overlayClass("selectedLabel", "font-semibold text-stone-800"), children: selectedElement.label }),
1286
+ /* @__PURE__ */ jsxs("span", { children: [
1287
+ " \xB7 ",
1288
+ selectedElement.path
1289
+ ] }),
1290
+ selectedElement.preview ? /* @__PURE__ */ jsxs("span", { children: [
1291
+ " \xB7 ",
1292
+ selectedElement.preview
1293
+ ] }) : null
1294
+ ] }),
1295
+ /* @__PURE__ */ jsx3(
1296
+ "button",
1297
+ {
1298
+ type: "button",
1299
+ "aria-label": "Clear selected element",
1300
+ title: "Clear selected element",
1301
+ className: overlayClass(
1302
+ "selectedClearButton",
1303
+ "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]"
1304
+ ),
1305
+ onClick: onClearSelectedElement,
1306
+ children: /* @__PURE__ */ jsx3(
1307
+ "svg",
1308
+ {
1309
+ viewBox: "0 0 20 20",
1310
+ fill: "none",
1311
+ className: overlayClass("selectedClearIcon", "h-3 w-3"),
1312
+ "aria-hidden": "true",
1313
+ children: /* @__PURE__ */ jsx3(
1314
+ "path",
1315
+ {
1316
+ d: "M5 5L15 15M15 5L5 15",
1317
+ stroke: "currentColor",
1318
+ strokeWidth: "1.5",
1319
+ strokeLinecap: "round"
1320
+ }
1321
+ )
1322
+ }
1323
+ )
1324
+ }
1325
+ )
1326
+ ]
1327
+ }
1328
+ ) : null,
1329
+ /* @__PURE__ */ jsxs("div", { className: overlayClass("composerRow", "flex gap-1.5"), children: [
1112
1330
  /* @__PURE__ */ jsx3(
1113
1331
  "textarea",
1114
1332
  {
@@ -1117,7 +1335,10 @@ function OverlayChatPanel({
1117
1335
  onKeyDown: onMessageKeyDown,
1118
1336
  rows: 2,
1119
1337
  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"
1338
+ className: overlayClass(
1339
+ "composerInput",
1340
+ "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"
1341
+ )
1121
1342
  }
1122
1343
  ),
1123
1344
  /* @__PURE__ */ jsx3(
@@ -1126,7 +1347,10 @@ function OverlayChatPanel({
1126
1347
  type: "button",
1127
1348
  onClick: onSend,
1128
1349
  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",
1350
+ className: overlayClass(
1351
+ "sendButton",
1352
+ "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"
1353
+ ),
1130
1354
  children: sending ? "Sending" : "Send"
1131
1355
  }
1132
1356
  )
@@ -1141,24 +1365,30 @@ function OverlayHistoryPanel({
1141
1365
  onRestoreCheckpoint,
1142
1366
  onDeleteCheckpoint
1143
1367
  }) {
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: [
1368
+ return /* @__PURE__ */ jsx3("section", { className: overlayClass("historySection", "flex min-h-0 flex-1 flex-col p-2 text-[11px] leading-4"), children: /* @__PURE__ */ jsxs("div", { className: overlayClass("historyColumns", "flex min-h-0 flex-1 flex-col gap-2 overflow-hidden"), children: [
1369
+ /* @__PURE__ */ jsxs("div", { className: overlayClass("historyCard", "rounded border border-stone-300 bg-[#f8f3e9]"), children: [
1370
+ /* @__PURE__ */ jsxs("div", { className: overlayClass("historyCardTitle", "border-b border-stone-200 px-2 py-1 font-semibold text-stone-700"), children: [
1147
1371
  "Published (",
1148
1372
  history.published.length,
1149
1373
  ")"
1150
1374
  ] }),
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(
1375
+ /* @__PURE__ */ jsx3("div", { className: overlayClass("historyList", "max-h-40 overflow-auto px-2 py-1.5"), children: history.published.length > 0 ? /* @__PURE__ */ jsx3("div", { className: overlayClass("historyStack", "space-y-1"), children: history.published.map((item) => /* @__PURE__ */ jsxs(
1152
1376
  "div",
1153
1377
  {
1154
- className: "flex items-center justify-between gap-2 rounded border border-stone-200 bg-[#f2ecdf] px-2 py-1",
1378
+ className: overlayClass(
1379
+ "historyItem",
1380
+ "flex items-center justify-between gap-2 rounded border border-stone-200 bg-[#f2ecdf] px-2 py-1"
1381
+ ),
1155
1382
  children: [
1156
- /* @__PURE__ */ jsx3("span", { className: "truncate text-[10px] text-stone-700", children: formatHistoryTime(item.createdAt) }),
1383
+ /* @__PURE__ */ jsx3("span", { className: overlayClass("historyTimestamp", "truncate text-[10px] text-stone-700"), children: formatHistoryTime(item.createdAt) }),
1157
1384
  /* @__PURE__ */ jsx3(
1158
1385
  "button",
1159
1386
  {
1160
1387
  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]",
1388
+ className: overlayClass(
1389
+ "historyAction",
1390
+ "rounded border border-stone-300 bg-[#f7f2e8] px-1.5 py-0.5 text-[10px] text-stone-700 hover:bg-[#efe8dc]"
1391
+ ),
1162
1392
  onClick: () => onRestorePublished(item),
1163
1393
  children: "Restore"
1164
1394
  }
@@ -1166,61 +1396,88 @@ function OverlayHistoryPanel({
1166
1396
  ]
1167
1397
  },
1168
1398
  `pub-${item.id}`
1169
- )) }) : /* @__PURE__ */ jsx3("p", { className: "text-[10px] text-stone-500", children: "No published snapshots." }) })
1399
+ )) }) : /* @__PURE__ */ jsx3("p", { className: overlayClass("emptyText", "text-[10px] text-stone-500"), children: "No published snapshots." }) })
1170
1400
  ] }),
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
- }
1401
+ /* @__PURE__ */ jsxs(
1402
+ "div",
1403
+ {
1404
+ className: overlayClass(
1405
+ "historyCard",
1406
+ "flex min-h-0 flex-1 flex-col rounded border border-stone-300 bg-[#f8f3e9]"
1407
+ ),
1408
+ children: [
1409
+ /* @__PURE__ */ jsxs("div", { className: overlayClass("historyCardTitle", "border-b border-stone-200 px-2 py-1 font-semibold text-stone-700"), children: [
1410
+ "Checkpoints (",
1411
+ history.checkpoints.length,
1412
+ ")"
1413
+ ] }),
1414
+ /* @__PURE__ */ jsx3("div", { className: overlayClass("historyList", "min-h-0 flex-1 overflow-auto px-2 py-1.5"), children: history.checkpoints.length > 0 ? /* @__PURE__ */ jsx3("div", { className: overlayClass("historyStack", "space-y-1"), children: history.checkpoints.map((item) => /* @__PURE__ */ jsxs(
1415
+ "div",
1416
+ {
1417
+ className: overlayClass(
1418
+ "historyItemCheckpoint",
1419
+ "flex items-start justify-between gap-2 rounded border border-stone-200 bg-[#f2ecdf] px-2 py-1"
1196
1420
  ),
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",
1421
+ children: [
1422
+ /* @__PURE__ */ jsxs("div", { className: overlayClass("historyTextBlock", "min-w-0 flex-1"), children: [
1423
+ /* @__PURE__ */ jsx3("p", { className: overlayClass("historyTimestamp", "truncate text-[10px] text-stone-700"), children: formatHistoryTime(item.createdAt) }),
1424
+ item.reason ? /* @__PURE__ */ jsx3("p", { className: overlayClass("historyReason", "truncate text-[10px] text-stone-500"), children: item.reason }) : null
1425
+ ] }),
1426
+ /* @__PURE__ */ jsxs("div", { className: overlayClass("historyActions", "flex items-center gap-1"), children: [
1427
+ /* @__PURE__ */ jsx3(
1428
+ "button",
1208
1429
  {
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"
1430
+ type: "button",
1431
+ disabled: deletingCheckpointId === item.id,
1432
+ className: overlayClass(
1433
+ "historyAction",
1434
+ "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"
1435
+ ),
1436
+ onClick: () => onRestoreCheckpoint(item),
1437
+ children: "Restore"
1214
1438
  }
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
- ] })
1439
+ ),
1440
+ /* @__PURE__ */ jsx3(
1441
+ "button",
1442
+ {
1443
+ type: "button",
1444
+ "aria-label": "Delete checkpoint",
1445
+ title: "Delete checkpoint",
1446
+ disabled: deletingCheckpointId === item.id,
1447
+ className: overlayClass(
1448
+ "historyDelete",
1449
+ "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"
1450
+ ),
1451
+ onClick: () => onDeleteCheckpoint(item),
1452
+ children: /* @__PURE__ */ jsx3(
1453
+ "svg",
1454
+ {
1455
+ viewBox: "0 0 20 20",
1456
+ fill: "none",
1457
+ className: overlayClass("icon", "h-3.5 w-3.5"),
1458
+ "aria-hidden": "true",
1459
+ children: /* @__PURE__ */ jsx3(
1460
+ "path",
1461
+ {
1462
+ 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",
1463
+ stroke: "currentColor",
1464
+ strokeWidth: "1.4",
1465
+ strokeLinecap: "round",
1466
+ strokeLinejoin: "round"
1467
+ }
1468
+ )
1469
+ }
1470
+ )
1471
+ }
1472
+ )
1473
+ ] })
1474
+ ]
1475
+ },
1476
+ `cp-${item.id}`
1477
+ )) }) : /* @__PURE__ */ jsx3("p", { className: overlayClass("emptyText", "text-[10px] text-stone-500"), children: "No checkpoints yet." }) })
1478
+ ]
1479
+ }
1480
+ )
1224
1481
  ] }) });
1225
1482
  }
1226
1483
  function OverlayLauncherButton({ onOpen }) {
@@ -1229,13 +1486,74 @@ function OverlayLauncherButton({ onOpen }) {
1229
1486
  {
1230
1487
  type: "button",
1231
1488
  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",
1489
+ className: overlayClass(
1490
+ "launcherButton",
1491
+ "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"
1492
+ ),
1233
1493
  style: { fontFamily: OVERLAY_FONT_FAMILY },
1234
1494
  children: "Chat to Webmaster"
1235
1495
  }
1236
1496
  );
1237
1497
  }
1238
1498
 
1499
+ // src/overlay/core-styles.ts
1500
+ var OVERLAY_CORE_STYLE_ID = "wmd-core-styles";
1501
+ var OVERLAY_CORE_STYLE_TEXT = `
1502
+ .wmd-panel{position:fixed;right:1rem;bottom:1rem;z-index:100;display:flex;flex-direction:column;width:min(480px,calc(100vw - 1.5rem));height:62vh;max-height:calc(100vh - 1.5rem);overflow:hidden;box-sizing:border-box}
1503
+ .wmd-panel *{box-sizing:border-box}
1504
+ .wmd-launcher{position:fixed;right:1rem;bottom:1rem;z-index:100;box-sizing:border-box}
1505
+ .wmd-header{flex-shrink:0}
1506
+ .wmd-header-row{display:flex;align-items:center;gap:.5rem}
1507
+ .wmd-header-actions{margin-left:auto;display:flex;align-items:center;gap:.25rem}
1508
+ .wmd-tabs{display:inline-flex;align-items:center}
1509
+ .wmd-login-section{display:flex;flex:1;min-height:0;align-items:center;justify-content:center}
1510
+ .wmd-login-card,.wmd-login-warning{width:100%;max-width:24rem}
1511
+ .wmd-login-fields{display:flex;flex-direction:column;gap:.5rem}
1512
+ .wmd-chat-section{display:flex;flex:1;min-height:0;flex-direction:column;gap:.25rem;overflow:auto}
1513
+ .wmd-message--tool{max-width:96%}
1514
+ .wmd-message--user,.wmd-message--thinking,.wmd-message--assistant,.wmd-message--fallback{max-width:92%}
1515
+ .wmd-message--user{margin-left:auto}
1516
+ .wmd-message--assistant{position:relative}
1517
+ .wmd-assistant-avatar,.wmd-assistant-avatar-fallback{position:absolute;top:.375rem;left:.5rem;width:18px;height:18px}
1518
+ .wmd-assistant-avatar--pending{animation:wmd-pulse 2s cubic-bezier(.4,0,.6,1) infinite}
1519
+ .wmd-footer{flex-shrink:0}
1520
+ .wmd-model-row,.wmd-selected-element,.wmd-composer-row{display:flex;align-items:center;gap:.375rem}
1521
+ .wmd-model-select,.wmd-composer-input{min-width:0;flex:1}
1522
+ .wmd-composer-input{resize:none}
1523
+ .wmd-history-section{display:flex;flex:1;min-height:0;flex-direction:column}
1524
+ .wmd-history-columns{display:flex;flex:1;min-height:0;flex-direction:column;gap:.5rem;overflow:hidden}
1525
+ .wmd-history-card{display:flex;min-height:0;flex-direction:column}
1526
+ .wmd-history-list{overflow:auto}
1527
+ .wmd-history-stack{display:flex;flex-direction:column;gap:.25rem}
1528
+ .wmd-history-item{display:flex;align-items:center;justify-content:space-between;gap:.5rem}
1529
+ .wmd-history-item--checkpoint{display:flex;align-items:flex-start;justify-content:space-between;gap:.5rem}
1530
+ .wmd-history-text{min-width:0;flex:1}
1531
+ .wmd-history-actions{display:flex;align-items:center;gap:.25rem}
1532
+ .wmd-history-timestamp,.wmd-history-reason,.wmd-selected-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
1533
+ .wmd-icon-button,.wmd-selected-clear,.wmd-history-delete{display:inline-flex;align-items:center;justify-content:center}
1534
+ .wmd-icon{width:14px;height:14px}
1535
+ .wmd-selected-clear-icon{width:12px;height:12px}
1536
+ @media (max-width:640px){.wmd-panel{left:.75rem;right:.75rem;bottom:.75rem;width:auto;height:min(72vh,540px)}}
1537
+ @keyframes wmd-pulse{50%{opacity:.5}}
1538
+ `.trim();
1539
+ function ensureOverlayCoreStyles(doc) {
1540
+ const target = doc ?? (typeof document !== "undefined" ? document : void 0);
1541
+ if (!target) {
1542
+ return;
1543
+ }
1544
+ if (target.getElementById(OVERLAY_CORE_STYLE_ID)) {
1545
+ return;
1546
+ }
1547
+ const mountTarget = target.head ?? target.documentElement;
1548
+ if (!mountTarget) {
1549
+ return;
1550
+ }
1551
+ const styleElement = target.createElement("style");
1552
+ styleElement.id = OVERLAY_CORE_STYLE_ID;
1553
+ styleElement.textContent = OVERLAY_CORE_STYLE_TEXT;
1554
+ mountTarget.appendChild(styleElement);
1555
+ }
1556
+
1239
1557
  // src/overlay/controller.ts
1240
1558
  import {
1241
1559
  useCallback,
@@ -1666,8 +1984,14 @@ Reason: ${checkpoint.reason}` : "";
1666
1984
 
1667
1985
  // src/overlay.tsx
1668
1986
  import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
1669
- function WebmasterDroidOverlay() {
1987
+ function WebmasterDroidOverlay({ injectCoreStyles = true }) {
1670
1988
  const controller = useOverlayController();
1989
+ useEffect3(() => {
1990
+ if (!injectCoreStyles) {
1991
+ return;
1992
+ }
1993
+ ensureOverlayCoreStyles();
1994
+ }, [injectCoreStyles]);
1671
1995
  if (!controller.isAdminMode) {
1672
1996
  return null;
1673
1997
  }
@@ -1676,7 +2000,10 @@ function WebmasterDroidOverlay() {
1676
2000
  {
1677
2001
  ref: controller.overlayRootRef,
1678
2002
  "data-admin-overlay-root": true,
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",
2003
+ className: overlayClass(
2004
+ "panel",
2005
+ "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"
2006
+ ),
1680
2007
  style: { fontFamily: OVERLAY_FONT_FAMILY },
1681
2008
  children: [
1682
2009
  /* @__PURE__ */ jsx4(
@@ -1763,13 +2090,61 @@ function WebmasterDroidOverlay() {
1763
2090
  import {
1764
2091
  createContext as createContext3,
1765
2092
  useContext as useContext3,
1766
- useEffect as useEffect3,
2093
+ useEffect as useEffect4,
1767
2094
  useMemo as useMemo3,
1768
2095
  useState as useState3
1769
2096
  } from "react";
1770
2097
  import {
1771
2098
  createDefaultCmsDocument
1772
2099
  } from "@webmaster-droid/contracts";
2100
+
2101
+ // src/normalize-document.ts
2102
+ import {
2103
+ normalizeCmsDocument
2104
+ } from "@webmaster-droid/contracts";
2105
+ function isRecord(value) {
2106
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2107
+ }
2108
+ function cloneValue(value) {
2109
+ if (Array.isArray(value)) {
2110
+ return value.map((item) => cloneValue(item));
2111
+ }
2112
+ if (isRecord(value)) {
2113
+ const out = {};
2114
+ for (const [key, item] of Object.entries(value)) {
2115
+ out[key] = cloneValue(item);
2116
+ }
2117
+ return out;
2118
+ }
2119
+ return value;
2120
+ }
2121
+ function mergeMissing(target, defaults) {
2122
+ if (Array.isArray(target) || Array.isArray(defaults)) {
2123
+ return target === void 0 ? cloneValue(defaults) : target;
2124
+ }
2125
+ if (!isRecord(defaults)) {
2126
+ return target === void 0 ? defaults : target;
2127
+ }
2128
+ if (!isRecord(target)) {
2129
+ return cloneValue(defaults);
2130
+ }
2131
+ const out = { ...target };
2132
+ for (const [key, value] of Object.entries(defaults)) {
2133
+ out[key] = mergeMissing(out[key], value);
2134
+ }
2135
+ return out;
2136
+ }
2137
+ function normalizeCmsDocumentWithFallback(document2, fallbackDocument) {
2138
+ const merged = mergeMissing(
2139
+ isRecord(document2) ? document2 : {},
2140
+ fallbackDocument
2141
+ );
2142
+ return normalizeCmsDocument(
2143
+ merged
2144
+ );
2145
+ }
2146
+
2147
+ // src/runtime.tsx
1773
2148
  import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1774
2149
  var CmsRuntimeContext = createContext3(null);
1775
2150
  function createThemeCssVariables(tokens) {
@@ -1802,7 +2177,7 @@ function CmsRuntimeBridge(props) {
1802
2177
  document: defaultDocument,
1803
2178
  error: null
1804
2179
  });
1805
- useEffect3(() => {
2180
+ useEffect4(() => {
1806
2181
  let ignore = false;
1807
2182
  fetchCmsContent(config.apiBaseUrl, stage, token).then((content2) => {
1808
2183
  if (ignore) {
@@ -1810,7 +2185,10 @@ function CmsRuntimeBridge(props) {
1810
2185
  }
1811
2186
  setState({
1812
2187
  requestKey,
1813
- document: content2,
2188
+ document: normalizeCmsDocumentWithFallback(
2189
+ content2,
2190
+ defaultDocument
2191
+ ),
1814
2192
  error: null
1815
2193
  });
1816
2194
  }).catch((error2) => {
@@ -1842,7 +2220,7 @@ function CmsRuntimeBridge(props) {
1842
2220
  const content = props.applyThemeTokens ? /* @__PURE__ */ jsx5("div", { style: createThemeCssVariables(value.document.themeTokens), children: props.children }) : props.children;
1843
2221
  return /* @__PURE__ */ jsxs3(CmsRuntimeContext.Provider, { value, children: [
1844
2222
  /* @__PURE__ */ jsx5(EditableProvider, { document: value.document, mode: stage, enabled: isAdminMode, children: content }),
1845
- props.includeOverlay ? /* @__PURE__ */ jsx5(WebmasterDroidOverlay, {}) : null
2223
+ props.includeOverlay ? /* @__PURE__ */ jsx5(WebmasterDroidOverlay, { injectCoreStyles: props.injectCoreStyles }) : null
1846
2224
  ] });
1847
2225
  }
1848
2226
  function WebmasterDroidRuntime(props) {
@@ -1852,6 +2230,7 @@ function WebmasterDroidRuntime(props) {
1852
2230
  fallbackDocument: props.fallbackDocument,
1853
2231
  includeOverlay: props.includeOverlay ?? true,
1854
2232
  applyThemeTokens: props.applyThemeTokens ?? true,
2233
+ injectCoreStyles: props.injectCoreStyles ?? true,
1855
2234
  children: props.children
1856
2235
  }
1857
2236
  ) });
@@ -1879,6 +2258,7 @@ export {
1879
2258
  fetchHistory,
1880
2259
  fetchModels,
1881
2260
  getSupabaseBrowserClient,
2261
+ normalizeCmsDocumentWithFallback,
1882
2262
  parseSelectedEditableFromTarget,
1883
2263
  publishDraft,
1884
2264
  resolveWebmasterDroidConfig,