@page-speed/agent-everywhere 0.3.0 → 0.4.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/README.md CHANGED
@@ -71,6 +71,41 @@ import { AgentSurface } from '@page-speed/agent-everywhere';
71
71
  />;
72
72
  ```
73
73
 
74
+ ### Inline confirmation / proposed-plan panels
75
+
76
+ Confirmation and proposed-plan panels flow **inline in the transcript** — attach
77
+ one to a message via `message.confirmation` and it renders after that message's
78
+ content, never pinned over the response. The plan body reuses the same markdown
79
+ rendering and spacing as assistant messages, so long multi-paragraph / bulleted
80
+ plans stay readable. Wire `onConfirmAction` (or use `ConfirmationPanel`
81
+ directly) to handle the action buttons.
82
+
83
+ ```tsx
84
+ const messages = [
85
+ {
86
+ id: 'm1',
87
+ role: 'assistant',
88
+ content: 'Here is the response and the plan.',
89
+ timestamp: new Date(),
90
+ confirmation: {
91
+ title: 'Proposed plan',
92
+ summary: '3 pages to change',
93
+ body: '## What changes\n\n- Rebuild the platform page\n- Refresh OG images',
94
+ actions: [
95
+ { id: 'apply', label: 'Apply changes' },
96
+ { id: 'cancel', label: 'Cancel', variant: 'outline' },
97
+ ],
98
+ },
99
+ },
100
+ ];
101
+
102
+ <AgentSurface
103
+ mode="widget"
104
+ messages={messages}
105
+ onConfirmAction={(messageId, actionId) => apply(messageId, actionId)}
106
+ />;
107
+ ```
108
+
74
109
  ## Artifacts
75
110
 
76
111
  Components an agent can render inside responses, grouped by category. All are
@@ -81,7 +116,7 @@ prop-driven and individually importable:
81
116
  `AnalyticsDashboard`, `ReportView`.
82
117
  - **Interactive** — `EntityCard`, `OptionCards`, `ListingFeed`, `ControlGrid`,
83
118
  `RecommendationCards`, `ScheduleTimeline`, `SettingsPanel`, `AgentHandoff`,
84
- `PersonaSelector`.
119
+ `ConfirmationPanel`, `PersonaSelector`.
85
120
  - **Messages** — `MessageList`, `MessageWithReasoning`, `MessageWithSteps`,
86
121
  `MessageWithFeedback`, `MessageWithAttachments`, `ConversationArtifact`.
87
122
  - **Input** — `PromptInput`, `MultimodalInput`, `QuickReplies`,
package/dist/index.cjs CHANGED
@@ -1007,33 +1007,70 @@ function MessageBubble({ role, children, className, unstyled }) {
1007
1007
  }
1008
1008
  );
1009
1009
  }
1010
- var PROSE_CLASSES = cn(
1011
- "text-sm leading-relaxed",
1012
- "[&_p]:my-1.5 first:[&_p]:mt-0 last:[&_p]:mb-0",
1013
- "[&_ul]:my-1.5 [&_ul]:list-disc [&_ul]:pl-5",
1014
- "[&_ol]:my-1.5 [&_ol]:list-decimal [&_ol]:pl-5",
1015
- "[&_li]:my-0.5",
1016
- "[&_h1]:mt-2 [&_h1]:mb-1 [&_h1]:text-base [&_h1]:font-semibold",
1017
- "[&_h2]:mt-2 [&_h2]:mb-1 [&_h2]:text-sm [&_h2]:font-semibold",
1018
- "[&_h3]:mt-2 [&_h3]:mb-1 [&_h3]:text-sm [&_h3]:font-semibold",
1019
- "[&_strong]:font-semibold",
1020
- "[&_a]:underline [&_a]:underline-offset-2",
1021
- "[&_code]:rounded [&_code]:bg-black/10 [&_code]:px-1 [&_code]:py-0.5 [&_code]:text-[0.85em]",
1022
- "[&_pre]:my-1.5 [&_pre]:overflow-x-auto [&_pre]:rounded-md [&_pre]:bg-black/10 [&_pre]:p-2 [&_pre]:text-xs",
1023
- "[&_pre_code]:bg-transparent [&_pre_code]:p-0",
1024
- "[&_blockquote]:border-l-2 [&_blockquote]:border-current/30 [&_blockquote]:pl-3 [&_blockquote]:opacity-90",
1025
- "[&_hr]:my-2 [&_hr]:border-current/20",
1026
- "whitespace-normal"
1010
+ var SPACE = "0.5rem";
1011
+ var mdStyle = { lineHeight: 1.6 };
1012
+ var MdP = ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: `${SPACE} 0` }, children });
1013
+ var MdUl = ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { margin: `${SPACE} 0`, paddingLeft: "1.25rem", listStyle: "disc" }, children });
1014
+ var MdOl = ({ children }) => /* @__PURE__ */ jsxRuntime.jsx(
1015
+ "ol",
1016
+ {
1017
+ style: { margin: `${SPACE} 0`, paddingLeft: "1.25rem", listStyle: "decimal" },
1018
+ children
1019
+ }
1027
1020
  );
1021
+ var MdLi = ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("li", { style: { margin: "0.2rem 0" }, children });
1022
+ var mkHeading = (level, fontSize) => ({ children }) => {
1023
+ const Tag = `h${level}`;
1024
+ return /* @__PURE__ */ jsxRuntime.jsx(Tag, { style: { margin: `${SPACE} 0 0.25rem`, fontSize, fontWeight: 600 }, children });
1025
+ };
1026
+ var MdBlockquote = ({ children }) => /* @__PURE__ */ jsxRuntime.jsx(
1027
+ "blockquote",
1028
+ {
1029
+ style: {
1030
+ margin: `${SPACE} 0`,
1031
+ paddingLeft: "0.75rem",
1032
+ borderLeft: "2px solid currentColor",
1033
+ opacity: 0.85
1034
+ },
1035
+ children
1036
+ }
1037
+ );
1038
+ var MARKDOWN_OVERRIDES = {
1039
+ p: MdP,
1040
+ ul: MdUl,
1041
+ ol: MdOl,
1042
+ li: MdLi,
1043
+ h1: mkHeading(1, "1rem"),
1044
+ h2: mkHeading(2, "0.95rem"),
1045
+ h3: mkHeading(3, "0.9rem"),
1046
+ blockquote: MdBlockquote
1047
+ };
1028
1048
  function MessageContent({
1029
1049
  children,
1030
1050
  className,
1031
1051
  markdown = true
1032
1052
  }) {
1033
1053
  if (markdown && typeof children === "string") {
1034
- return /* @__PURE__ */ jsxRuntime.jsx(markdownToJsx.Markdown, { className: cn(PROSE_CLASSES, className), children });
1054
+ return /* @__PURE__ */ jsxRuntime.jsx(
1055
+ markdownToJsx.Markdown,
1056
+ {
1057
+ className: cn(
1058
+ "text-sm [&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
1059
+ className
1060
+ ),
1061
+ overrides: MARKDOWN_OVERRIDES,
1062
+ children
1063
+ }
1064
+ );
1035
1065
  }
1036
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("text-sm leading-relaxed whitespace-pre-wrap", className), children });
1066
+ return /* @__PURE__ */ jsxRuntime.jsx(
1067
+ "div",
1068
+ {
1069
+ className: cn("text-sm leading-relaxed whitespace-pre-wrap", className),
1070
+ style: mdStyle,
1071
+ children
1072
+ }
1073
+ );
1037
1074
  }
1038
1075
  function MessageContainer({ role, children, className }) {
1039
1076
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -1201,7 +1238,7 @@ var PromptInput = React4.forwardRef(
1201
1238
  [handleSubmit]
1202
1239
  );
1203
1240
  const variantStyles = {
1204
- default: "border-t px-3 py-2.5",
1241
+ default: "px-3 py-2.5",
1205
1242
  minimal: "px-3 py-2",
1206
1243
  bordered: "border rounded-lg px-3 py-2.5"
1207
1244
  };
@@ -1210,41 +1247,48 @@ var PromptInput = React4.forwardRef(
1210
1247
  minimal: "bg-transparent px-0",
1211
1248
  bordered: "bg-muted/50"
1212
1249
  };
1213
- return /* @__PURE__ */ jsxRuntime.jsx("form", { onSubmit: handleSubmit, className: cn(variantStyles[variant], className), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end gap-2", children: [
1214
- leftActions,
1215
- /* @__PURE__ */ jsxRuntime.jsx(
1216
- "textarea",
1217
- {
1218
- ref: textareaRef,
1219
- value,
1220
- onChange: (e) => onChange(e.target.value),
1221
- onKeyDown: handleKeyDown,
1222
- placeholder,
1223
- disabled: disabled || loading,
1224
- autoFocus,
1225
- rows: minRows,
1226
- className: cn(
1227
- "flex-1 resize-none border-0 px-0 py-1 text-sm leading-5 shadow-none",
1228
- "placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0",
1229
- "disabled:cursor-not-allowed disabled:opacity-50",
1230
- inputVariantStyles[variant],
1231
- inputClassName
1250
+ return /* @__PURE__ */ jsxRuntime.jsx(
1251
+ "form",
1252
+ {
1253
+ onSubmit: handleSubmit,
1254
+ className: cn(variantStyles[variant], className),
1255
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end gap-2", children: [
1256
+ leftActions,
1257
+ /* @__PURE__ */ jsxRuntime.jsx(
1258
+ "textarea",
1259
+ {
1260
+ ref: textareaRef,
1261
+ value,
1262
+ onChange: (e) => onChange(e.target.value),
1263
+ onKeyDown: handleKeyDown,
1264
+ placeholder,
1265
+ disabled: disabled || loading,
1266
+ autoFocus,
1267
+ rows: minRows,
1268
+ className: cn(
1269
+ "flex-1 resize-none border-0 px-0 py-1 text-sm leading-5 shadow-none",
1270
+ "placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0",
1271
+ "disabled:cursor-not-allowed disabled:opacity-50",
1272
+ inputVariantStyles[variant],
1273
+ inputClassName
1274
+ )
1275
+ }
1276
+ ),
1277
+ rightActions,
1278
+ showSendButton && /* @__PURE__ */ jsxRuntime.jsx(
1279
+ Button,
1280
+ {
1281
+ type: "submit",
1282
+ size: "sm",
1283
+ variant: variant === "minimal" ? "ghost" : "outline",
1284
+ disabled: !value.trim() || disabled || loading,
1285
+ className: "size-8 shrink-0 p-0",
1286
+ children: sendButtonContent || /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SendIcon, { className: "size-4" })
1287
+ }
1232
1288
  )
1233
- }
1234
- ),
1235
- rightActions,
1236
- showSendButton && /* @__PURE__ */ jsxRuntime.jsx(
1237
- Button,
1238
- {
1239
- type: "submit",
1240
- size: "sm",
1241
- variant: variant === "minimal" ? "ghost" : "outline",
1242
- disabled: !value.trim() || disabled || loading,
1243
- className: "size-8 shrink-0 p-0",
1244
- children: sendButtonContent || /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SendIcon, { className: "size-4" })
1245
- }
1246
- )
1247
- ] }) });
1289
+ ] })
1290
+ }
1291
+ );
1248
1292
  }
1249
1293
  );
1250
1294
  PromptInput.displayName = "PromptInput";
@@ -7680,10 +7724,58 @@ function ReportView({ report, className }) {
7680
7724
  ))
7681
7725
  ] });
7682
7726
  }
7727
+ function ConfirmationPanel({
7728
+ title = "Proposed plan",
7729
+ summary,
7730
+ body,
7731
+ markdown = true,
7732
+ icon,
7733
+ actions,
7734
+ onAction,
7735
+ ariaLabel,
7736
+ className
7737
+ }) {
7738
+ const hasBody = body !== void 0 && body !== null && body !== "";
7739
+ return /* @__PURE__ */ jsxRuntime.jsxs(
7740
+ react.motion.section,
7741
+ {
7742
+ initial: { opacity: 0, y: 6 },
7743
+ animate: { opacity: 1, y: 0 },
7744
+ transition: { duration: 0.2 },
7745
+ "aria-label": ariaLabel ?? title,
7746
+ className: cn(
7747
+ "flex w-full min-w-0 flex-col rounded-lg border bg-card p-4",
7748
+ className
7749
+ ),
7750
+ children: [
7751
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex items-center gap-2", children: [
7752
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex size-6 shrink-0 items-center justify-center rounded-md bg-muted/60 text-muted-foreground", children: icon ?? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ClipboardListIcon, { className: "size-3.5" }) }),
7753
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "min-w-0 flex-1 truncate font-medium text-sm", children: title }),
7754
+ summary && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "secondary", className: "shrink-0 text-[10px] font-normal", children: summary })
7755
+ ] }),
7756
+ hasBody && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 min-w-0", children: typeof body === "string" ? /* @__PURE__ */ jsxRuntime.jsx(MessageContent, { markdown, children: body }) : body }),
7757
+ actions && actions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 flex flex-wrap gap-2", children: actions.map((action, index) => /* @__PURE__ */ jsxRuntime.jsx(
7758
+ Button,
7759
+ {
7760
+ type: "button",
7761
+ size: "sm",
7762
+ variant: action.variant ?? (index === 0 ? "default" : "outline"),
7763
+ disabled: action.disabled || action.busy,
7764
+ "aria-busy": action.busy || void 0,
7765
+ onClick: () => onAction?.(action.id),
7766
+ children: action.label
7767
+ },
7768
+ action.id
7769
+ )) })
7770
+ ]
7771
+ }
7772
+ );
7773
+ }
7683
7774
  function MessageList({
7684
7775
  messages,
7685
7776
  showAvatars = true,
7686
7777
  renderMessage,
7778
+ onConfirmAction,
7687
7779
  className
7688
7780
  }) {
7689
7781
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex flex-col gap-4", className), children: messages.map((message) => {
@@ -7700,7 +7792,18 @@ function MessageList({
7700
7792
  hasSteps && /* @__PURE__ */ jsxRuntime.jsx(MessageWithSteps, { steps: message.steps }),
7701
7793
  message.content && /* @__PURE__ */ jsxRuntime.jsx(MessageBubble, { role: message.role, children: /* @__PURE__ */ jsxRuntime.jsx(MessageContent, { children: message.content }) }),
7702
7794
  hasAttachments && /* @__PURE__ */ jsxRuntime.jsx(MessageWithAttachments, { attachments: message.attachments }),
7703
- message.data && /* @__PURE__ */ jsxRuntime.jsx(DataPayloadView, { payload: message.data })
7795
+ message.data && /* @__PURE__ */ jsxRuntime.jsx(DataPayloadView, { payload: message.data }),
7796
+ message.confirmation && /* @__PURE__ */ jsxRuntime.jsx(
7797
+ ConfirmationPanel,
7798
+ {
7799
+ title: message.confirmation.title,
7800
+ summary: message.confirmation.summary,
7801
+ body: message.confirmation.body,
7802
+ markdown: message.confirmation.markdown,
7803
+ actions: message.confirmation.actions,
7804
+ onAction: (actionId) => onConfirmAction?.(message.id, actionId)
7805
+ }
7806
+ )
7704
7807
  ] })
7705
7808
  ] }, message.id);
7706
7809
  }) });
@@ -7921,6 +8024,7 @@ function AgentSurface({
7921
8024
  showAvatars = true,
7922
8025
  renderMessage,
7923
8026
  onFeedback,
8027
+ onConfirmAction,
7924
8028
  report,
7925
8029
  dataPanel,
7926
8030
  isOpen = true,
@@ -7937,7 +8041,8 @@ function AgentSurface({
7937
8041
  messages,
7938
8042
  showAvatars,
7939
8043
  renderMessage,
7940
- onFeedback
8044
+ onFeedback,
8045
+ onConfirmAction
7941
8046
  }
7942
8047
  );
7943
8048
  const resolvedInput = input ?? (onInputChange && onSubmit !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t p-2", children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -8060,6 +8165,7 @@ exports.ChatPanel = ChatPanel;
8060
8165
  exports.Collapsible = Collapsible;
8061
8166
  exports.CollapsibleContent = CollapsibleContent2;
8062
8167
  exports.CollapsibleTrigger = CollapsibleTrigger2;
8168
+ exports.ConfirmationPanel = ConfirmationPanel;
8063
8169
  exports.ControlGrid = ControlGrid;
8064
8170
  exports.ConversationAnalytics = ConversationAnalytics;
8065
8171
  exports.ConversationArtifact = ConversationArtifact;
@@ -8080,6 +8186,8 @@ exports.MediaEditorCanvas = MediaEditorCanvas;
8080
8186
  exports.MediaGallery = MediaGallery;
8081
8187
  exports.MessageActions = MessageActions;
8082
8188
  exports.MessageBubble = MessageBubble;
8189
+ exports.MessageContainer = MessageContainer;
8190
+ exports.MessageContent = MessageContent;
8083
8191
  exports.MessageList = MessageList;
8084
8192
  exports.MessageWithAttachments = MessageWithAttachments;
8085
8193
  exports.MessageWithFeedback = MessageWithFeedback;