@humaan/payload-plugin-ai-assistant 0.0.0-stage → 0.1.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.
Files changed (190) hide show
  1. package/README.md +277 -2
  2. package/dist/admin/AiAssistantControl.d.ts +11 -0
  3. package/dist/admin/AiAssistantControl.js +31 -0
  4. package/dist/admin/AiAssistantControl.js.map +1 -0
  5. package/dist/admin/AiAssistantPanel.d.ts +10 -0
  6. package/dist/admin/AiAssistantPanel.js +208 -0
  7. package/dist/admin/AiAssistantPanel.js.map +1 -0
  8. package/dist/admin/AiAssistantPanel.module.scss +689 -0
  9. package/dist/admin/AiSourceSwitch.d.ts +13 -0
  10. package/dist/admin/AiSourceSwitch.js +14 -0
  11. package/dist/admin/AiSourceSwitch.js.map +1 -0
  12. package/dist/admin/BudgetField.d.ts +8 -0
  13. package/dist/admin/BudgetField.js +145 -0
  14. package/dist/admin/BudgetField.js.map +1 -0
  15. package/dist/admin/BudgetField.module.scss +47 -0
  16. package/dist/admin/Conversation.d.ts +12 -0
  17. package/dist/admin/Conversation.js +469 -0
  18. package/dist/admin/Conversation.js.map +1 -0
  19. package/dist/admin/Markdown.d.ts +9 -0
  20. package/dist/admin/Markdown.js +63 -0
  21. package/dist/admin/Markdown.js.map +1 -0
  22. package/dist/admin/PageContextBridge.d.ts +5 -0
  23. package/dist/admin/PageContextBridge.js +72 -0
  24. package/dist/admin/PageContextBridge.js.map +1 -0
  25. package/dist/admin/Pill.d.ts +11 -0
  26. package/dist/admin/Pill.js +27 -0
  27. package/dist/admin/Pill.js.map +1 -0
  28. package/dist/admin/PlanCard.d.ts +14 -0
  29. package/dist/admin/PlanCard.js +465 -0
  30. package/dist/admin/PlanCard.js.map +1 -0
  31. package/dist/admin/SettingsAlerts.d.ts +7 -0
  32. package/dist/admin/SettingsAlerts.js +37 -0
  33. package/dist/admin/SettingsAlerts.js.map +1 -0
  34. package/dist/admin/UsageSummary.d.ts +11 -0
  35. package/dist/admin/UsageSummary.js +129 -0
  36. package/dist/admin/UsageSummary.js.map +1 -0
  37. package/dist/admin/attachmentUpload.d.ts +25 -0
  38. package/dist/admin/attachmentUpload.js +76 -0
  39. package/dist/admin/attachmentUpload.js.map +1 -0
  40. package/dist/admin/openDocument.d.ts +13 -0
  41. package/dist/admin/openDocument.js +34 -0
  42. package/dist/admin/openDocument.js.map +1 -0
  43. package/dist/admin/percent.d.ts +10 -0
  44. package/dist/admin/percent.js +21 -0
  45. package/dist/admin/percent.js.map +1 -0
  46. package/dist/admin/threads.d.ts +21 -0
  47. package/dist/admin/threads.js +92 -0
  48. package/dist/admin/threads.js.map +1 -0
  49. package/dist/admin/toolSteps.d.ts +12 -0
  50. package/dist/admin/toolSteps.js +57 -0
  51. package/dist/admin/toolSteps.js.map +1 -0
  52. package/dist/admin/transport.d.ts +24 -0
  53. package/dist/admin/transport.js +75 -0
  54. package/dist/admin/transport.js.map +1 -0
  55. package/dist/admin/useAttachments.d.ts +32 -0
  56. package/dist/admin/useAttachments.js +91 -0
  57. package/dist/admin/useAttachments.js.map +1 -0
  58. package/dist/admin/useEntityLabel.d.ts +3 -0
  59. package/dist/admin/useEntityLabel.js +23 -0
  60. package/dist/admin/useEntityLabel.js.map +1 -0
  61. package/dist/attachments/collection.d.ts +15 -0
  62. package/dist/attachments/collection.js +90 -0
  63. package/dist/attachments/collection.js.map +1 -0
  64. package/dist/attachments/conversation.d.ts +25 -0
  65. package/dist/attachments/conversation.js +105 -0
  66. package/dist/attachments/conversation.js.map +1 -0
  67. package/dist/attachments/rules.d.ts +9 -0
  68. package/dist/attachments/rules.js +17 -0
  69. package/dist/attachments/rules.js.map +1 -0
  70. package/dist/attachments/store.d.ts +27 -0
  71. package/dist/attachments/store.js +187 -0
  72. package/dist/attachments/store.js.map +1 -0
  73. package/dist/billing.d.ts +60 -0
  74. package/dist/billing.js +116 -0
  75. package/dist/billing.js.map +1 -0
  76. package/dist/chat/history.d.ts +37 -0
  77. package/dist/chat/history.js +165 -0
  78. package/dist/chat/history.js.map +1 -0
  79. package/dist/chat/messages.d.ts +5 -0
  80. package/dist/chat/messages.js +3 -0
  81. package/dist/chat/messages.js.map +1 -0
  82. package/dist/chat/threads.d.ts +33 -0
  83. package/dist/chat/threads.js +77 -0
  84. package/dist/chat/threads.js.map +1 -0
  85. package/dist/chat/tools.d.ts +76 -0
  86. package/dist/chat/tools.js +596 -0
  87. package/dist/chat/tools.js.map +1 -0
  88. package/dist/chat/usage.d.ts +66 -0
  89. package/dist/chat/usage.js +219 -0
  90. package/dist/chat/usage.js.map +1 -0
  91. package/dist/collectionAccess.d.ts +6 -0
  92. package/dist/collectionAccess.js +32 -0
  93. package/dist/collectionAccess.js.map +1 -0
  94. package/dist/constants.d.ts +64 -0
  95. package/dist/constants.js +51 -0
  96. package/dist/constants.js.map +1 -0
  97. package/dist/endpoints/access.d.ts +4 -0
  98. package/dist/endpoints/access.js +28 -0
  99. package/dist/endpoints/access.js.map +1 -0
  100. package/dist/endpoints/allowance.d.ts +22 -0
  101. package/dist/endpoints/allowance.js +37 -0
  102. package/dist/endpoints/allowance.js.map +1 -0
  103. package/dist/endpoints/applyEndpoint.d.ts +12 -0
  104. package/dist/endpoints/applyEndpoint.js +170 -0
  105. package/dist/endpoints/applyEndpoint.js.map +1 -0
  106. package/dist/endpoints/budget.d.ts +30 -0
  107. package/dist/endpoints/budget.js +153 -0
  108. package/dist/endpoints/budget.js.map +1 -0
  109. package/dist/endpoints/chatEndpoint.d.ts +12 -0
  110. package/dist/endpoints/chatEndpoint.js +295 -0
  111. package/dist/endpoints/chatEndpoint.js.map +1 -0
  112. package/dist/endpoints/form.d.ts +23 -0
  113. package/dist/endpoints/form.js +95 -0
  114. package/dist/endpoints/form.js.map +1 -0
  115. package/dist/endpoints/usageEndpoint.d.ts +13 -0
  116. package/dist/endpoints/usageEndpoint.js +70 -0
  117. package/dist/endpoints/usageEndpoint.js.map +1 -0
  118. package/dist/index.d.ts +4 -0
  119. package/dist/index.js +5 -0
  120. package/dist/index.js.map +1 -0
  121. package/dist/lexical/editorConfig.d.ts +4 -0
  122. package/dist/lexical/editorConfig.js +19 -0
  123. package/dist/lexical/editorConfig.js.map +1 -0
  124. package/dist/lexical/lexicalToMarkdown.d.ts +10 -0
  125. package/dist/lexical/lexicalToMarkdown.js +10 -0
  126. package/dist/lexical/lexicalToMarkdown.js.map +1 -0
  127. package/dist/lexical/markdownToLexical.d.ts +7 -0
  128. package/dist/lexical/markdownToLexical.js +10 -0
  129. package/dist/lexical/markdownToLexical.js.map +1 -0
  130. package/dist/plan/allowed.d.ts +7 -0
  131. package/dist/plan/allowed.js +19 -0
  132. package/dist/plan/allowed.js.map +1 -0
  133. package/dist/plan/applyPlan.d.ts +16 -0
  134. package/dist/plan/applyPlan.js +353 -0
  135. package/dist/plan/applyPlan.js.map +1 -0
  136. package/dist/plan/catalogue.d.ts +37 -0
  137. package/dist/plan/catalogue.js +157 -0
  138. package/dist/plan/catalogue.js.map +1 -0
  139. package/dist/plan/diff.d.ts +13 -0
  140. package/dist/plan/diff.js +87 -0
  141. package/dist/plan/diff.js.map +1 -0
  142. package/dist/plan/parsePlan.d.ts +27 -0
  143. package/dist/plan/parsePlan.js +322 -0
  144. package/dist/plan/parsePlan.js.map +1 -0
  145. package/dist/plan/prompt.d.ts +1 -0
  146. package/dist/plan/prompt.js +45 -0
  147. package/dist/plan/prompt.js.map +1 -0
  148. package/dist/plan/stored.d.ts +53 -0
  149. package/dist/plan/stored.js +3 -0
  150. package/dist/plan/stored.js.map +1 -0
  151. package/dist/plan/tokens.d.ts +8 -0
  152. package/dist/plan/tokens.js +79 -0
  153. package/dist/plan/tokens.js.map +1 -0
  154. package/dist/plan/view.d.ts +4 -0
  155. package/dist/plan/view.js +43 -0
  156. package/dist/plan/view.js.map +1 -0
  157. package/dist/plugin.d.ts +3 -0
  158. package/dist/plugin.js +464 -0
  159. package/dist/plugin.js.map +1 -0
  160. package/dist/read/outline.d.ts +7 -0
  161. package/dist/read/outline.js +70 -0
  162. package/dist/read/outline.js.map +1 -0
  163. package/dist/read/readTarget.d.ts +4 -0
  164. package/dist/read/readTarget.js +13 -0
  165. package/dist/read/readTarget.js.map +1 -0
  166. package/dist/read/readable.d.ts +8 -0
  167. package/dist/read/readable.js +131 -0
  168. package/dist/read/readable.js.map +1 -0
  169. package/dist/runtime.d.ts +15 -0
  170. package/dist/runtime.js +14 -0
  171. package/dist/runtime.js.map +1 -0
  172. package/dist/schema/fields.d.ts +113 -0
  173. package/dist/schema/fields.js +1036 -0
  174. package/dist/schema/fields.js.map +1 -0
  175. package/dist/schema/listingFields.d.ts +9 -0
  176. package/dist/schema/listingFields.js +95 -0
  177. package/dist/schema/listingFields.js.map +1 -0
  178. package/dist/schema/slugify.d.ts +2 -0
  179. package/dist/schema/slugify.js +5 -0
  180. package/dist/schema/slugify.js.map +1 -0
  181. package/dist/settings.d.ts +10 -0
  182. package/dist/settings.js +54 -0
  183. package/dist/settings.js.map +1 -0
  184. package/dist/types.d.ts +198 -0
  185. package/dist/types.js +6 -0
  186. package/dist/types.js.map +1 -0
  187. package/dist/uploads/routeAsset.d.ts +27 -0
  188. package/dist/uploads/routeAsset.js +127 -0
  189. package/dist/uploads/routeAsset.js.map +1 -0
  190. package/package.json +122 -3
@@ -0,0 +1,469 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useChat } from '@ai-sdk/react';
4
+ import { Button } from '@payloadcms/ui';
5
+ import { useEffect, useId, useRef, useState } from 'react';
6
+ import { markPlan } from '../chat/history.js';
7
+ import { maxFiles } from '../constants.js';
8
+ import styles from './AiAssistantPanel.module.scss';
9
+ import { Markdown } from './Markdown.js';
10
+ import { currentOpenDocument, useOpenDocument } from './openDocument.js';
11
+ import { Pill } from './Pill.js';
12
+ import { PlanCard } from './PlanCard.js';
13
+ import { toolStep } from './toolSteps.js';
14
+ import { AssistantTransport } from './transport.js';
15
+ import { useAttachments } from './useAttachments.js';
16
+ import { useEntityLabel } from './useEntityLabel.js';
17
+ /** One conversation. Remounted with a new key to switch conversations. */ export function Conversation({ apiRoute, session, attachmentsEnabled, onReply }) {
18
+ const openDocument = useOpenDocument();
19
+ const [draft, setDraft] = useState('');
20
+ const [fileNotice, setFileNotice] = useState('');
21
+ const attachments = useAttachments({
22
+ apiRoute,
23
+ initial: session.attachments
24
+ });
25
+ const fileInputRef = useRef(null);
26
+ const endRef = useRef(null);
27
+ const messageId = useId();
28
+ const [transport] = useState(()=>new AssistantTransport({
29
+ api: `${apiRoute}/ai-assistant/chat`,
30
+ openDocument: currentOpenDocument
31
+ }));
32
+ const { messages, sendMessage, setMessages, status, stop, error, clearError } = useChat({
33
+ id: session.chatId,
34
+ messages: session.messages,
35
+ transport,
36
+ onFinish: onReply
37
+ });
38
+ const busy = status === 'submitted' || status === 'streaming';
39
+ const { unsent, uploading } = attachments;
40
+ const canAttach = attachmentsEnabled && !attachments.full;
41
+ // Keep the newest message in view as it streams.
42
+ useEffect(()=>{
43
+ endRef.current?.scrollIntoView({
44
+ block: 'end'
45
+ });
46
+ }, [
47
+ messages,
48
+ status
49
+ ]);
50
+ const onPickFiles = (event)=>{
51
+ const picked = [
52
+ ...event.target.files ?? []
53
+ ];
54
+ event.target.value = '';
55
+ const omitted = attachments.add(picked);
56
+ setFileNotice(omitted > 0 ? `A conversation can have up to ${maxFiles} files. Extra files were left out.` : '');
57
+ };
58
+ const submit = ()=>{
59
+ const text = draft.trim();
60
+ if (busy || uploading || !text && unsent.length === 0) {
61
+ return;
62
+ }
63
+ const sentBefore = attachments.sentIds.size;
64
+ const added = unsent.map((file, offset)=>({
65
+ index: sentBefore + offset,
66
+ name: file.name,
67
+ type: file.type
68
+ }));
69
+ const context = currentOpenDocument()?.context;
70
+ clearError();
71
+ transport.setAttachmentIds(attachments.readyIds);
72
+ void sendMessage({
73
+ text,
74
+ metadata: {
75
+ ...added.length > 0 ? {
76
+ attachments: added
77
+ } : {},
78
+ ...context ? {
79
+ context
80
+ } : {}
81
+ }
82
+ });
83
+ attachments.markSent(unsent.map((file)=>file.id));
84
+ setDraft('');
85
+ setFileNotice('');
86
+ };
87
+ const onKeyDown = (event)=>{
88
+ if (event.key !== 'Enter' || event.shiftKey || event.nativeEvent.isComposing) {
89
+ return;
90
+ }
91
+ event.preventDefault();
92
+ submit();
93
+ };
94
+ const recordOutcome = (planId, update)=>{
95
+ setMessages((current)=>markPlan(current, planId, update).messages);
96
+ };
97
+ return /*#__PURE__*/ _jsxs("div", {
98
+ className: styles.assistant__chat,
99
+ children: [
100
+ /*#__PURE__*/ _jsxs("div", {
101
+ className: styles.assistant__log,
102
+ children: [
103
+ messages.length === 0 ? /*#__PURE__*/ _jsx(Welcome, {
104
+ context: openDocument?.context
105
+ }) : null,
106
+ messages.map((message, index)=>message.role === 'user' ? /*#__PURE__*/ _jsx(UserMessage, {
107
+ message: message
108
+ }, message.id) : /*#__PURE__*/ _jsx(AssistantMessage, {
109
+ apiRoute: apiRoute,
110
+ message: message,
111
+ onOutcome: recordOutcome,
112
+ running: busy && index === messages.length - 1
113
+ }, message.id)),
114
+ status === 'submitted' ? /*#__PURE__*/ _jsx("p", {
115
+ className: styles.assistant__thinking,
116
+ children: "Thinking…"
117
+ }) : null,
118
+ /*#__PURE__*/ _jsx("div", {
119
+ "aria-hidden": "true",
120
+ ref: endRef
121
+ })
122
+ ]
123
+ }),
124
+ busy ? /*#__PURE__*/ _jsx("div", {
125
+ "aria-label": "The assistant is working",
126
+ className: styles.assistant__progress,
127
+ role: "progressbar"
128
+ }) : null,
129
+ /*#__PURE__*/ _jsx("p", {
130
+ className: styles.assistant__sr,
131
+ role: "status",
132
+ children: uploading ? 'Uploading attachments.' : announcement(messages, busy)
133
+ }),
134
+ error ? /*#__PURE__*/ _jsx("p", {
135
+ className: styles.assistant__error,
136
+ role: "alert",
137
+ children: error.message || 'The assistant could not reply. Try again.'
138
+ }) : null,
139
+ /*#__PURE__*/ _jsxs("form", {
140
+ className: styles.assistant__composer,
141
+ onSubmit: (event)=>{
142
+ event.preventDefault();
143
+ submit();
144
+ },
145
+ children: [
146
+ openDocument ? /*#__PURE__*/ _jsx(ContextPill, {
147
+ context: openDocument.context
148
+ }) : null,
149
+ attachments.items.length > 0 ? /*#__PURE__*/ _jsx("ul", {
150
+ "aria-label": "Attached files",
151
+ className: styles.assistant__chips,
152
+ children: attachments.items.map((item)=>/*#__PURE__*/ _jsx(AttachmentChip, {
153
+ item: item,
154
+ onRemove: ()=>attachments.remove(item.key),
155
+ removable: !busy && !(item.id && attachments.sentIds.has(item.id))
156
+ }, item.key))
157
+ }) : null,
158
+ /*#__PURE__*/ _jsx("label", {
159
+ className: styles.assistant__sr,
160
+ htmlFor: messageId,
161
+ children: "Message"
162
+ }),
163
+ /*#__PURE__*/ _jsx("textarea", {
164
+ className: styles.assistant__textarea,
165
+ id: messageId,
166
+ onChange: (event)=>setDraft(event.target.value),
167
+ onKeyDown: onKeyDown,
168
+ placeholder: "Ask about the site, or describe a change",
169
+ rows: 3,
170
+ value: draft
171
+ }),
172
+ /*#__PURE__*/ _jsx("input", {
173
+ "aria-hidden": true,
174
+ className: styles.assistant__sr,
175
+ disabled: !canAttach,
176
+ multiple: true,
177
+ onChange: onPickFiles,
178
+ ref: fileInputRef,
179
+ tabIndex: -1,
180
+ type: "file"
181
+ }),
182
+ /*#__PURE__*/ _jsx("div", {
183
+ className: styles.assistant__composerRow,
184
+ children: /*#__PURE__*/ _jsxs("div", {
185
+ className: styles.assistant__composerActions,
186
+ children: [
187
+ attachmentsEnabled ? /*#__PURE__*/ _jsx(Button, {
188
+ buttonStyle: "secondary",
189
+ disabled: !canAttach || busy,
190
+ margin: false,
191
+ onClick: ()=>fileInputRef.current?.click(),
192
+ size: "small",
193
+ type: "button",
194
+ children: "Attach files"
195
+ }) : null,
196
+ busy ? /*#__PURE__*/ _jsx(Button, {
197
+ buttonStyle: "secondary",
198
+ margin: false,
199
+ onClick: ()=>void stop(),
200
+ size: "small",
201
+ type: "button",
202
+ children: "Stop"
203
+ }) : /*#__PURE__*/ _jsx(Button, {
204
+ buttonStyle: "primary",
205
+ disabled: uploading || !draft.trim() && unsent.length === 0,
206
+ margin: false,
207
+ size: "small",
208
+ type: "submit",
209
+ children: "Send"
210
+ })
211
+ ]
212
+ })
213
+ }),
214
+ fileNotice ? /*#__PURE__*/ _jsx("p", {
215
+ className: styles.assistant__hint,
216
+ role: "status",
217
+ children: fileNotice
218
+ }) : null
219
+ ]
220
+ })
221
+ ]
222
+ });
223
+ }
224
+ /** A file in the composer. Its status is part of its name for screen readers, and announced as it changes. */ function AttachmentChip({ item, removable, onRemove }) {
225
+ return /*#__PURE__*/ _jsxs("li", {
226
+ className: styles.assistant__chip,
227
+ "data-status": item.status,
228
+ children: [
229
+ /*#__PURE__*/ _jsx("span", {
230
+ className: styles.assistant__chipName,
231
+ children: item.name
232
+ }),
233
+ item.status === 'uploading' ? /*#__PURE__*/ _jsx("span", {
234
+ className: styles.assistant__chipStatus,
235
+ children: "Uploading…"
236
+ }) : null,
237
+ item.status === 'failed' ? /*#__PURE__*/ _jsx("span", {
238
+ className: styles.assistant__chipStatus,
239
+ children: item.error ?? 'Upload failed'
240
+ }) : null,
241
+ removable ? /*#__PURE__*/ _jsxs("button", {
242
+ className: styles.assistant__chipRemove,
243
+ onClick: onRemove,
244
+ type: "button",
245
+ children: [
246
+ /*#__PURE__*/ _jsx("span", {
247
+ "aria-hidden": "true",
248
+ children: "×"
249
+ }),
250
+ /*#__PURE__*/ _jsxs("span", {
251
+ className: styles.assistant__sr,
252
+ children: [
253
+ "Remove ",
254
+ item.name
255
+ ]
256
+ })
257
+ ]
258
+ }) : null
259
+ ]
260
+ });
261
+ }
262
+ function Welcome({ context }) {
263
+ return /*#__PURE__*/ _jsxs("div", {
264
+ className: styles.assistant__welcome,
265
+ children: [
266
+ /*#__PURE__*/ _jsx("p", {
267
+ children: "Ask a question about the site's content, or describe a page or change you want. The assistant reads the content it needs and proposes a plan. Nothing is saved until you choose Proceed."
268
+ }),
269
+ context ? /*#__PURE__*/ _jsxs("p", {
270
+ children: [
271
+ "It can see ",
272
+ context.label,
273
+ ", including changes you have not saved yet."
274
+ ]
275
+ }) : null
276
+ ]
277
+ });
278
+ }
279
+ function ContextPill({ context }) {
280
+ const entityLabel = useEntityLabel();
281
+ return /*#__PURE__*/ _jsxs("p", {
282
+ className: styles.assistant__context,
283
+ children: [
284
+ /*#__PURE__*/ _jsx(Pill, {
285
+ entity: context.slug,
286
+ children: entityLabel(context.slug, context.kind)
287
+ }),
288
+ /*#__PURE__*/ _jsxs("span", {
289
+ children: [
290
+ "Using ",
291
+ /*#__PURE__*/ _jsx("strong", {
292
+ children: context.label
293
+ })
294
+ ]
295
+ })
296
+ ]
297
+ });
298
+ }
299
+ function UserMessage({ message }) {
300
+ const text = message.parts.flatMap((part)=>part.type === 'text' ? [
301
+ part.text
302
+ ] : []).join('\n');
303
+ const attachments = message.metadata?.attachments ?? [];
304
+ return /*#__PURE__*/ _jsxs("div", {
305
+ className: styles.assistant__user,
306
+ children: [
307
+ /*#__PURE__*/ _jsx("h3", {
308
+ className: styles.assistant__sr,
309
+ children: "You said"
310
+ }),
311
+ text ? /*#__PURE__*/ _jsx("p", {
312
+ className: styles.assistant__text,
313
+ children: text
314
+ }) : null,
315
+ attachments.length > 0 ? /*#__PURE__*/ _jsx("ul", {
316
+ "aria-label": "Attached",
317
+ className: styles.assistant__chips,
318
+ children: attachments.map((file)=>/*#__PURE__*/ _jsx("li", {
319
+ className: styles.assistant__chip,
320
+ children: /*#__PURE__*/ _jsx("span", {
321
+ className: styles.assistant__chipName,
322
+ children: file.name
323
+ })
324
+ }, file.index))
325
+ }) : null,
326
+ message.metadata?.context ? /*#__PURE__*/ _jsxs("p", {
327
+ className: styles.assistant__meta,
328
+ children: [
329
+ "On ",
330
+ message.metadata.context.label
331
+ ]
332
+ }) : null
333
+ ]
334
+ });
335
+ }
336
+ function AssistantMessage({ message, running, apiRoute, onOutcome }) {
337
+ const usage = message.metadata?.usage;
338
+ const entityLabel = useEntityLabel();
339
+ const segments = segmentsOf(message, entityLabel);
340
+ return /*#__PURE__*/ _jsxs("div", {
341
+ className: styles.assistant__reply,
342
+ children: [
343
+ /*#__PURE__*/ _jsx("h3", {
344
+ className: styles.assistant__sr,
345
+ children: "Humaan AI said"
346
+ }),
347
+ segments.map((segment)=>{
348
+ if (segment.kind === 'text') {
349
+ return /*#__PURE__*/ _jsx(Markdown, {
350
+ text: segment.text
351
+ }, segment.key);
352
+ }
353
+ if (segment.kind === 'steps') {
354
+ return /*#__PURE__*/ _jsx(Steps, {
355
+ live: running && segment === segments.at(-1),
356
+ steps: segment.steps
357
+ }, segment.key);
358
+ }
359
+ return /*#__PURE__*/ _jsx(PlanCard, {
360
+ apiRoute: apiRoute,
361
+ onOutcome: onOutcome,
362
+ output: segment.output
363
+ }, segment.key);
364
+ }),
365
+ usage?.totalTokens ? /*#__PURE__*/ _jsxs("p", {
366
+ className: styles.assistant__meta,
367
+ children: [
368
+ usage.totalTokens.toLocaleString('en-AU'),
369
+ " tokens",
370
+ usage.cachedInputTokens ? `, ${usage.cachedInputTokens.toLocaleString('en-AU')} from cache` : ''
371
+ ]
372
+ }) : null
373
+ ]
374
+ });
375
+ }
376
+ /** While the reply streams, steps show as they run. Afterwards they fold into one line the editor can open. */ function Steps({ steps, live }) {
377
+ const list = /*#__PURE__*/ _jsx("ol", {
378
+ "aria-label": "Steps",
379
+ className: styles.assistant__steps,
380
+ children: steps.map((step)=>/*#__PURE__*/ _jsxs("li", {
381
+ className: styles.assistant__step,
382
+ "data-state": step.state,
383
+ children: [
384
+ /*#__PURE__*/ _jsx("span", {
385
+ "aria-hidden": "true",
386
+ children: step.state === 'done' ? '✓' : step.state === 'failed' ? '!' : '…'
387
+ }),
388
+ step.label,
389
+ step.state === 'failed' ? /*#__PURE__*/ _jsx("span", {
390
+ className: styles.assistant__sr,
391
+ children: " (failed)"
392
+ }) : null
393
+ ]
394
+ }, step.id))
395
+ });
396
+ if (live) {
397
+ return list;
398
+ }
399
+ const failed = steps.filter((step)=>step.state === 'failed').length;
400
+ return /*#__PURE__*/ _jsxs("details", {
401
+ className: styles.assistant__stepsFold,
402
+ children: [
403
+ /*#__PURE__*/ _jsxs("summary", {
404
+ children: [
405
+ "Worked through ",
406
+ steps.length,
407
+ " ",
408
+ steps.length === 1 ? 'step' : 'steps',
409
+ failed > 0 ? `, ${failed} failed` : ''
410
+ ]
411
+ }),
412
+ list
413
+ ]
414
+ });
415
+ }
416
+ /** Groups tool calls that sit next to each other into one list of steps. */ function segmentsOf(message, entityLabel) {
417
+ const segments = [];
418
+ message.parts.forEach((part, index)=>{
419
+ const key = `${message.id}-${index}`;
420
+ if (part.type === 'text') {
421
+ if (part.text.trim()) {
422
+ segments.push({
423
+ kind: 'text',
424
+ key,
425
+ text: part.text.trim()
426
+ });
427
+ }
428
+ return;
429
+ }
430
+ if (part.type === 'tool-submitPlan' && part.state === 'output-available' && part.output.ok) {
431
+ segments.push({
432
+ kind: 'plan',
433
+ key,
434
+ output: part.output
435
+ });
436
+ return;
437
+ }
438
+ const step = toolStep(part, entityLabel);
439
+ if (!step) {
440
+ return;
441
+ }
442
+ const last = segments.at(-1);
443
+ if (last?.kind === 'steps') {
444
+ last.steps.push(step);
445
+ } else {
446
+ segments.push({
447
+ kind: 'steps',
448
+ key,
449
+ steps: [
450
+ step
451
+ ]
452
+ });
453
+ }
454
+ });
455
+ return segments;
456
+ }
457
+ /** Read by screen readers instead of the streaming text, which would be announced word by word. */ function announcement(messages, busy) {
458
+ if (busy) {
459
+ return 'The assistant is replying.';
460
+ }
461
+ const last = messages.at(-1);
462
+ if (last?.role !== 'assistant') {
463
+ return '';
464
+ }
465
+ const plan = last.parts.some((part)=>part.type === 'tool-submitPlan' && part.state === 'output-available' && part.output.ok && part.output.status === 'proposed');
466
+ return plan ? 'The assistant replied with a plan to review.' : 'The assistant replied.';
467
+ }
468
+
469
+ //# sourceMappingURL=Conversation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/admin/Conversation.tsx"],"sourcesContent":["'use client'\n\nimport type { ChangeEvent, KeyboardEvent } from 'react'\n\nimport { useChat } from '@ai-sdk/react'\nimport { Button } from '@payloadcms/ui'\nimport { useEffect, useId, useRef, useState } from 'react'\n\nimport type { AssistantUIMessage } from '../chat/messages.js'\nimport type { AssistantChange, AttachmentView, PageContext, PlanStatus } from '../types.js'\nimport type { LoadedThread } from './threads.js'\nimport type { EntityLabel, ToolStep } from './toolSteps.js'\nimport type { ComposerAttachment } from './useAttachments.js'\n\nimport { markPlan } from '../chat/history.js'\nimport { maxFiles } from '../constants.js'\nimport styles from './AiAssistantPanel.module.scss'\nimport { Markdown } from './Markdown.js'\nimport { currentOpenDocument, useOpenDocument } from './openDocument.js'\nimport { Pill } from './Pill.js'\nimport { PlanCard } from './PlanCard.js'\nimport { toolStep } from './toolSteps.js'\nimport { AssistantTransport } from './transport.js'\nimport { useAttachments } from './useAttachments.js'\nimport { useEntityLabel } from './useEntityLabel.js'\n\ninterface ConversationProps {\n apiRoute: string\n /** Whether the project registered the attachments collection. */\n attachmentsEnabled: boolean\n /** Called when a reply finishes and the thread has been saved. */\n onReply: () => void\n session: LoadedThread\n}\n\n/** One conversation. Remounted with a new key to switch conversations. */\nexport function Conversation({\n apiRoute,\n session,\n attachmentsEnabled,\n onReply,\n}: ConversationProps) {\n const openDocument = useOpenDocument()\n const [draft, setDraft] = useState('')\n const [fileNotice, setFileNotice] = useState('')\n const attachments = useAttachments({ apiRoute, initial: session.attachments })\n const fileInputRef = useRef<HTMLInputElement>(null)\n const endRef = useRef<HTMLDivElement>(null)\n const messageId = useId()\n const [transport] = useState(\n () =>\n new AssistantTransport({\n api: `${apiRoute}/ai-assistant/chat`,\n openDocument: currentOpenDocument,\n }),\n )\n const { messages, sendMessage, setMessages, status, stop, error, clearError } =\n useChat<AssistantUIMessage>({\n id: session.chatId,\n messages: session.messages,\n transport,\n onFinish: onReply,\n })\n\n const busy = status === 'submitted' || status === 'streaming'\n const { unsent, uploading } = attachments\n const canAttach = attachmentsEnabled && !attachments.full\n\n // Keep the newest message in view as it streams.\n useEffect(() => {\n endRef.current?.scrollIntoView({ block: 'end' })\n }, [messages, status])\n\n const onPickFiles = (event: ChangeEvent<HTMLInputElement>) => {\n const picked = [...(event.target.files ?? [])]\n event.target.value = ''\n const omitted = attachments.add(picked)\n setFileNotice(\n omitted > 0\n ? `A conversation can have up to ${maxFiles} files. Extra files were left out.`\n : '',\n )\n }\n\n const submit = () => {\n const text = draft.trim()\n if (busy || uploading || (!text && unsent.length === 0)) {\n return\n }\n const sentBefore = attachments.sentIds.size\n const added: AttachmentView[] = unsent.map((file, offset) => ({\n index: sentBefore + offset,\n name: file.name,\n type: file.type,\n }))\n const context = currentOpenDocument()?.context\n clearError()\n transport.setAttachmentIds(attachments.readyIds)\n void sendMessage({\n text,\n metadata: {\n ...(added.length > 0 ? { attachments: added } : {}),\n ...(context ? { context } : {}),\n },\n })\n attachments.markSent(unsent.map((file) => file.id))\n setDraft('')\n setFileNotice('')\n }\n\n const onKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n if (event.key !== 'Enter' || event.shiftKey || event.nativeEvent.isComposing) {\n return\n }\n event.preventDefault()\n submit()\n }\n\n const recordOutcome = (\n planId: string,\n update: { changes?: AssistantChange[]; status: PlanStatus },\n ) => {\n setMessages((current) => markPlan(current, planId, update).messages)\n }\n\n return (\n <div className={styles.assistant__chat}>\n <div className={styles.assistant__log}>\n {messages.length === 0 ? <Welcome context={openDocument?.context} /> : null}\n {messages.map((message, index) =>\n message.role === 'user' ? (\n <UserMessage key={message.id} message={message} />\n ) : (\n <AssistantMessage\n apiRoute={apiRoute}\n key={message.id}\n message={message}\n onOutcome={recordOutcome}\n running={busy && index === messages.length - 1}\n />\n ),\n )}\n {status === 'submitted' ? <p className={styles.assistant__thinking}>Thinking…</p> : null}\n <div aria-hidden=\"true\" ref={endRef} />\n </div>\n\n {busy ? (\n <div\n aria-label=\"The assistant is working\"\n className={styles.assistant__progress}\n role=\"progressbar\"\n />\n ) : null}\n <p className={styles.assistant__sr} role=\"status\">\n {uploading ? 'Uploading attachments.' : announcement(messages, busy)}\n </p>\n {error ? (\n <p className={styles.assistant__error} role=\"alert\">\n {error.message || 'The assistant could not reply. Try again.'}\n </p>\n ) : null}\n\n <form\n className={styles.assistant__composer}\n onSubmit={(event) => {\n event.preventDefault()\n submit()\n }}\n >\n {openDocument ? <ContextPill context={openDocument.context} /> : null}\n {attachments.items.length > 0 ? (\n <ul aria-label=\"Attached files\" className={styles.assistant__chips}>\n {attachments.items.map((item) => (\n <AttachmentChip\n item={item}\n key={item.key}\n onRemove={() => attachments.remove(item.key)}\n removable={!busy && !(item.id && attachments.sentIds.has(item.id))}\n />\n ))}\n </ul>\n ) : null}\n <label className={styles.assistant__sr} htmlFor={messageId}>\n Message\n </label>\n {/* eslint-disable-next-line jsx-a11y/control-has-associated-label -- labelled by the <label htmlFor> above */}\n <textarea\n className={styles.assistant__textarea}\n id={messageId}\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={onKeyDown}\n placeholder=\"Ask about the site, or describe a change\"\n rows={3}\n value={draft}\n />\n <input\n aria-hidden\n className={styles.assistant__sr}\n disabled={!canAttach}\n multiple\n onChange={onPickFiles}\n ref={fileInputRef}\n tabIndex={-1}\n type=\"file\"\n />\n <div className={styles.assistant__composerRow}>\n <div className={styles.assistant__composerActions}>\n {attachmentsEnabled ? (\n <Button\n buttonStyle=\"secondary\"\n disabled={!canAttach || busy}\n margin={false}\n onClick={() => fileInputRef.current?.click()}\n size=\"small\"\n type=\"button\"\n >\n Attach files\n </Button>\n ) : null}\n {busy ? (\n <Button\n buttonStyle=\"secondary\"\n margin={false}\n onClick={() => void stop()}\n size=\"small\"\n type=\"button\"\n >\n Stop\n </Button>\n ) : (\n <Button\n buttonStyle=\"primary\"\n disabled={uploading || (!draft.trim() && unsent.length === 0)}\n margin={false}\n size=\"small\"\n type=\"submit\"\n >\n Send\n </Button>\n )}\n </div>\n </div>\n {fileNotice ? (\n <p className={styles.assistant__hint} role=\"status\">\n {fileNotice}\n </p>\n ) : null}\n </form>\n </div>\n )\n}\n\n/** A file in the composer. Its status is part of its name for screen readers, and announced as it changes. */\nfunction AttachmentChip({\n item,\n removable,\n onRemove,\n}: {\n item: ComposerAttachment\n onRemove: () => void\n removable: boolean\n}) {\n return (\n <li className={styles.assistant__chip} data-status={item.status}>\n <span className={styles.assistant__chipName}>{item.name}</span>\n {item.status === 'uploading' ? (\n <span className={styles.assistant__chipStatus}>Uploading…</span>\n ) : null}\n {item.status === 'failed' ? (\n <span className={styles.assistant__chipStatus}>{item.error ?? 'Upload failed'}</span>\n ) : null}\n {removable ? (\n <button className={styles.assistant__chipRemove} onClick={onRemove} type=\"button\">\n <span aria-hidden=\"true\">×</span>\n <span className={styles.assistant__sr}>Remove {item.name}</span>\n </button>\n ) : null}\n </li>\n )\n}\n\nfunction Welcome({ context }: { context?: PageContext }) {\n return (\n <div className={styles.assistant__welcome}>\n <p>\n Ask a question about the site&apos;s content, or describe a page or change you want. The\n assistant reads the content it needs and proposes a plan. Nothing is saved until you choose\n Proceed.\n </p>\n {context ? (\n <p>It can see {context.label}, including changes you have not saved yet.</p>\n ) : null}\n </div>\n )\n}\n\nfunction ContextPill({ context }: { context: PageContext }) {\n const entityLabel = useEntityLabel()\n return (\n <p className={styles.assistant__context}>\n <Pill entity={context.slug}>{entityLabel(context.slug, context.kind)}</Pill>\n <span>\n Using <strong>{context.label}</strong>\n </span>\n </p>\n )\n}\n\nfunction UserMessage({ message }: { message: AssistantUIMessage }) {\n const text = message.parts.flatMap((part) => (part.type === 'text' ? [part.text] : [])).join('\\n')\n const attachments = message.metadata?.attachments ?? []\n return (\n <div className={styles.assistant__user}>\n <h3 className={styles.assistant__sr}>You said</h3>\n {text ? <p className={styles.assistant__text}>{text}</p> : null}\n {attachments.length > 0 ? (\n <ul aria-label=\"Attached\" className={styles.assistant__chips}>\n {attachments.map((file) => (\n <li className={styles.assistant__chip} key={file.index}>\n <span className={styles.assistant__chipName}>{file.name}</span>\n </li>\n ))}\n </ul>\n ) : null}\n {message.metadata?.context ? (\n <p className={styles.assistant__meta}>On {message.metadata.context.label}</p>\n ) : null}\n </div>\n )\n}\n\ntype Segment =\n | { key: string; kind: 'plan'; output: Extract<PlanOutput, { ok: true }> }\n | { key: string; kind: 'steps'; steps: ToolStep[] }\n | { key: string; kind: 'text'; text: string }\n\ntype PlanOutput = Extract<\n AssistantUIMessage['parts'][number],\n { state: 'output-available'; type: 'tool-submitPlan' }\n>['output']\n\nfunction AssistantMessage({\n message,\n running,\n apiRoute,\n onOutcome,\n}: {\n apiRoute: string\n message: AssistantUIMessage\n onOutcome: (planId: string, update: { changes?: AssistantChange[]; status: PlanStatus }) => void\n /** This reply is still streaming, so its newest steps stay in view. */\n running: boolean\n}) {\n const usage = message.metadata?.usage\n const entityLabel = useEntityLabel()\n const segments = segmentsOf(message, entityLabel)\n return (\n <div className={styles.assistant__reply}>\n <h3 className={styles.assistant__sr}>Humaan AI said</h3>\n {segments.map((segment) => {\n if (segment.kind === 'text') {\n return <Markdown key={segment.key} text={segment.text} />\n }\n if (segment.kind === 'steps') {\n return (\n <Steps\n key={segment.key}\n live={running && segment === segments.at(-1)}\n steps={segment.steps}\n />\n )\n }\n return (\n <PlanCard\n apiRoute={apiRoute}\n key={segment.key}\n onOutcome={onOutcome}\n output={segment.output}\n />\n )\n })}\n {usage?.totalTokens ? (\n <p className={styles.assistant__meta}>\n {usage.totalTokens.toLocaleString('en-AU')} tokens\n {usage.cachedInputTokens\n ? `, ${usage.cachedInputTokens.toLocaleString('en-AU')} from cache`\n : ''}\n </p>\n ) : null}\n </div>\n )\n}\n\n/** While the reply streams, steps show as they run. Afterwards they fold into one line the editor can open. */\nfunction Steps({ steps, live }: { live: boolean; steps: ToolStep[] }) {\n const list = (\n <ol aria-label=\"Steps\" className={styles.assistant__steps}>\n {steps.map((step) => (\n <li className={styles.assistant__step} data-state={step.state} key={step.id}>\n <span aria-hidden=\"true\">\n {step.state === 'done' ? '✓' : step.state === 'failed' ? '!' : '…'}\n </span>\n {step.label}\n {step.state === 'failed' ? <span className={styles.assistant__sr}> (failed)</span> : null}\n </li>\n ))}\n </ol>\n )\n if (live) {\n return list\n }\n const failed = steps.filter((step) => step.state === 'failed').length\n return (\n <details className={styles.assistant__stepsFold}>\n <summary>\n Worked through {steps.length} {steps.length === 1 ? 'step' : 'steps'}\n {failed > 0 ? `, ${failed} failed` : ''}\n </summary>\n {list}\n </details>\n )\n}\n\n/** Groups tool calls that sit next to each other into one list of steps. */\nfunction segmentsOf(message: AssistantUIMessage, entityLabel: EntityLabel): Segment[] {\n const segments: Segment[] = []\n message.parts.forEach((part, index) => {\n const key = `${message.id}-${index}`\n if (part.type === 'text') {\n if (part.text.trim()) {\n segments.push({ kind: 'text', key, text: part.text.trim() })\n }\n return\n }\n if (part.type === 'tool-submitPlan' && part.state === 'output-available' && part.output.ok) {\n segments.push({ kind: 'plan', key, output: part.output })\n return\n }\n const step = toolStep(part, entityLabel)\n if (!step) {\n return\n }\n const last = segments.at(-1)\n if (last?.kind === 'steps') {\n last.steps.push(step)\n } else {\n segments.push({ kind: 'steps', key, steps: [step] })\n }\n })\n return segments\n}\n\n/** Read by screen readers instead of the streaming text, which would be announced word by word. */\nfunction announcement(messages: AssistantUIMessage[], busy: boolean): string {\n if (busy) {\n return 'The assistant is replying.'\n }\n const last = messages.at(-1)\n if (last?.role !== 'assistant') {\n return ''\n }\n const plan = last.parts.some(\n (part) =>\n part.type === 'tool-submitPlan' &&\n part.state === 'output-available' &&\n part.output.ok &&\n part.output.status === 'proposed',\n )\n return plan ? 'The assistant replied with a plan to review.' : 'The assistant replied.'\n}\n"],"names":["useChat","Button","useEffect","useId","useRef","useState","markPlan","maxFiles","styles","Markdown","currentOpenDocument","useOpenDocument","Pill","PlanCard","toolStep","AssistantTransport","useAttachments","useEntityLabel","Conversation","apiRoute","session","attachmentsEnabled","onReply","openDocument","draft","setDraft","fileNotice","setFileNotice","attachments","initial","fileInputRef","endRef","messageId","transport","api","messages","sendMessage","setMessages","status","stop","error","clearError","id","chatId","onFinish","busy","unsent","uploading","canAttach","full","current","scrollIntoView","block","onPickFiles","event","picked","target","files","value","omitted","add","submit","text","trim","length","sentBefore","sentIds","size","added","map","file","offset","index","name","type","context","setAttachmentIds","readyIds","metadata","markSent","onKeyDown","key","shiftKey","nativeEvent","isComposing","preventDefault","recordOutcome","planId","update","div","className","assistant__chat","assistant__log","Welcome","message","role","UserMessage","AssistantMessage","onOutcome","running","p","assistant__thinking","aria-hidden","ref","aria-label","assistant__progress","assistant__sr","announcement","assistant__error","form","assistant__composer","onSubmit","ContextPill","items","ul","assistant__chips","item","AttachmentChip","onRemove","remove","removable","has","label","htmlFor","textarea","assistant__textarea","onChange","placeholder","rows","input","disabled","multiple","tabIndex","assistant__composerRow","assistant__composerActions","buttonStyle","margin","onClick","click","assistant__hint","li","assistant__chip","data-status","span","assistant__chipName","assistant__chipStatus","button","assistant__chipRemove","assistant__welcome","entityLabel","assistant__context","entity","slug","kind","strong","parts","flatMap","part","join","assistant__user","h3","assistant__text","assistant__meta","usage","segments","segmentsOf","assistant__reply","segment","Steps","live","at","steps","output","totalTokens","toLocaleString","cachedInputTokens","list","ol","assistant__steps","step","assistant__step","data-state","state","failed","filter","details","assistant__stepsFold","summary","forEach","push","ok","last","plan","some"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ interface MarkdownProps {
2
+ text: string;
3
+ }
4
+ /**
5
+ * Renders an assistant reply. Raw HTML is dropped, so content the model read cannot inject markup,
6
+ * and react-markdown removes unsafe link protocols.
7
+ */
8
+ export declare function Markdown({ text }: MarkdownProps): import("react").JSX.Element;
9
+ export {};
@@ -0,0 +1,63 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import ReactMarkdown from 'react-markdown';
4
+ import remarkGfm from 'remark-gfm';
5
+ import styles from './AiAssistantPanel.module.scss';
6
+ /**
7
+ * Renders an assistant reply. Raw HTML is dropped, so content the model read cannot inject markup,
8
+ * and react-markdown removes unsafe link protocols.
9
+ */ export function Markdown({ text }) {
10
+ return /*#__PURE__*/ _jsx("div", {
11
+ className: styles.assistant__markdown,
12
+ children: /*#__PURE__*/ _jsx(ReactMarkdown, {
13
+ components: components,
14
+ remarkPlugins: [
15
+ remarkGfm
16
+ ],
17
+ skipHtml: true,
18
+ children: text
19
+ })
20
+ });
21
+ }
22
+ /** Headings in a reply would break the drawer's outline, so they render as bold paragraphs. */ function Heading({ children }) {
23
+ return /*#__PURE__*/ _jsx("p", {
24
+ className: styles.assistant__markdownHeading,
25
+ children: /*#__PURE__*/ _jsx("strong", {
26
+ children: children
27
+ })
28
+ });
29
+ }
30
+ const components = {
31
+ h1: Heading,
32
+ h2: Heading,
33
+ h3: Heading,
34
+ h4: Heading,
35
+ h5: Heading,
36
+ h6: Heading,
37
+ a: ({ href, children })=>{
38
+ // Admin links are relative, so an absolute URL leaves the CMS.
39
+ const external = typeof href === 'string' && /^https?:\/\//.test(href);
40
+ return /*#__PURE__*/ _jsxs("a", {
41
+ href: href,
42
+ ...external ? {
43
+ target: '_blank',
44
+ rel: 'noopener noreferrer'
45
+ } : {},
46
+ children: [
47
+ children,
48
+ external ? /*#__PURE__*/ _jsx("span", {
49
+ className: styles.assistant__sr,
50
+ children: " (opens in a new tab)"
51
+ }) : null
52
+ ]
53
+ });
54
+ },
55
+ table: ({ children })=>/*#__PURE__*/ _jsx("div", {
56
+ className: styles.assistant__markdownTable,
57
+ children: /*#__PURE__*/ _jsx("table", {
58
+ children: children
59
+ })
60
+ })
61
+ };
62
+
63
+ //# sourceMappingURL=Markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/admin/Markdown.tsx"],"sourcesContent":["'use client'\n\nimport type { ReactNode } from 'react'\n\nimport ReactMarkdown, { type Components } from 'react-markdown'\nimport remarkGfm from 'remark-gfm'\n\nimport styles from './AiAssistantPanel.module.scss'\n\ninterface MarkdownProps {\n text: string\n}\n\n/**\n * Renders an assistant reply. Raw HTML is dropped, so content the model read cannot inject markup,\n * and react-markdown removes unsafe link protocols.\n */\nexport function Markdown({ text }: MarkdownProps) {\n return (\n <div className={styles.assistant__markdown}>\n <ReactMarkdown components={components} remarkPlugins={[remarkGfm]} skipHtml>\n {text}\n </ReactMarkdown>\n </div>\n )\n}\n\n/** Headings in a reply would break the drawer's outline, so they render as bold paragraphs. */\nfunction Heading({ children }: { children?: ReactNode }) {\n return (\n <p className={styles.assistant__markdownHeading}>\n <strong>{children}</strong>\n </p>\n )\n}\n\nconst components: Components = {\n h1: Heading,\n h2: Heading,\n h3: Heading,\n h4: Heading,\n h5: Heading,\n h6: Heading,\n a: ({ href, children }) => {\n // Admin links are relative, so an absolute URL leaves the CMS.\n const external = typeof href === 'string' && /^https?:\\/\\//.test(href)\n return (\n <a href={href} {...(external ? { target: '_blank', rel: 'noopener noreferrer' } : {})}>\n {children}\n {external ? <span className={styles.assistant__sr}> (opens in a new tab)</span> : null}\n </a>\n )\n },\n table: ({ children }) => (\n <div className={styles.assistant__markdownTable}>\n <table>{children}</table>\n </div>\n ),\n}\n"],"names":["ReactMarkdown","remarkGfm","styles","Markdown","text","div","className","assistant__markdown","components","remarkPlugins","skipHtml","Heading","children","p","assistant__markdownHeading","strong","h1","h2","h3","h4","h5","h6","a","href","external","test","target","rel","span","assistant__sr","table","assistant__markdownTable"],"mappings":"AAAA;;AAIA,OAAOA,mBAAwC,iBAAgB;AAC/D,OAAOC,eAAe,aAAY;AAElC,OAAOC,YAAY,iCAAgC;AAMnD;;;CAGC,GACD,OAAO,SAASC,SAAS,EAAEC,IAAI,EAAiB;IAC9C,qBACE,KAACC;QAAIC,WAAWJ,OAAOK,mBAAmB;kBACxC,cAAA,KAACP;YAAcQ,YAAYA;YAAYC,eAAe;gBAACR;aAAU;YAAES,QAAQ;sBACxEN;;;AAIT;AAEA,6FAA6F,GAC7F,SAASO,QAAQ,EAAEC,QAAQ,EAA4B;IACrD,qBACE,KAACC;QAAEP,WAAWJ,OAAOY,0BAA0B;kBAC7C,cAAA,KAACC;sBAAQH;;;AAGf;AAEA,MAAMJ,aAAyB;IAC7BQ,IAAIL;IACJM,IAAIN;IACJO,IAAIP;IACJQ,IAAIR;IACJS,IAAIT;IACJU,IAAIV;IACJW,GAAG,CAAC,EAAEC,IAAI,EAAEX,QAAQ,EAAE;QACpB,+DAA+D;QAC/D,MAAMY,WAAW,OAAOD,SAAS,YAAY,eAAeE,IAAI,CAACF;QACjE,qBACE,MAACD;YAAEC,MAAMA;YAAO,GAAIC,WAAW;gBAAEE,QAAQ;gBAAUC,KAAK;YAAsB,IAAI,CAAC,CAAC;;gBACjFf;gBACAY,yBAAW,KAACI;oBAAKtB,WAAWJ,OAAO2B,aAAa;8BAAE;qBAA+B;;;IAGxF;IACAC,OAAO,CAAC,EAAElB,QAAQ,EAAE,iBAClB,KAACP;YAAIC,WAAWJ,OAAO6B,wBAAwB;sBAC7C,cAAA,KAACD;0BAAOlB;;;AAGd"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Rendered before the document controls on every collection and global the assistant can read.
3
+ * It renders nothing. It tells the assistant drawer which document is open and how to read its form.
4
+ */
5
+ export declare function PageContextBridge(): null;