@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 @@
1
+ {"version":3,"sources":["../../src/admin/PlanCard.tsx"],"sourcesContent":["'use client'\n\nimport { Button } from '@payloadcms/ui'\nimport { useId, useState } from 'react'\n\nimport type {\n AssistantChange,\n AssistantPlanView,\n FieldChangeView,\n MediaView,\n PlanStatus,\n SubmitPlanOutput,\n} from '../types.js'\nimport type { OpenDocument } from './openDocument.js'\nimport type { PillTone } from './Pill.js'\n\nimport styles from './AiAssistantPanel.module.scss'\nimport { currentOpenDocument } from './openDocument.js'\nimport { Pill } from './Pill.js'\nimport { useEntityLabel } from './useEntityLabel.js'\n\ninterface PlanCardProps {\n apiRoute: string\n onOutcome: (planId: string, update: { changes?: AssistantChange[]; status: PlanStatus }) => void\n output: Extract<SubmitPlanOutput, { ok: true }>\n}\n\n/** A plan the assistant proposed. Nothing is written until the editor chooses Proceed. */\nexport function PlanCard({ output, apiRoute, onOutcome }: PlanCardProps) {\n const headingId = useId()\n const [pending, setPending] = useState(false)\n const [error, setError] = useState('')\n const [editor, setEditor] = useState<EditorSync>()\n const { plan, status } = output\n\n const proceed = async () => {\n setError('')\n setPending(true)\n try {\n // Only the plan's id: the files it saves are already stored, and the server reads them from there.\n const response = await fetch(`${apiRoute}/ai-assistant/apply`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ planId: plan.id }),\n }).catch(() => {\n throw new Error(\n 'The changes could not be sent. Check your connection and choose Proceed again.',\n )\n })\n const payload: unknown = await response.json().catch(() => null)\n if (!response.ok) {\n throw new Error(messageOf(payload) ?? 'The changes could not be saved.')\n }\n const changes =\n payload &&\n typeof payload === 'object' &&\n 'changes' in payload &&\n Array.isArray(payload.changes)\n ? (payload.changes as AssistantChange[])\n : []\n onOutcome(plan.id, { status: 'applied', changes })\n setEditor(await syncOpenEditor(changes))\n } catch (caught) {\n setError(\n caught instanceof Error && caught.message\n ? caught.message\n : 'The changes could not be saved.',\n )\n } finally {\n setPending(false)\n }\n }\n\n const loadIntoEditor = async () => {\n const open = currentOpenDocument()\n if (!open) {\n return\n }\n setEditor(await reloadEditor(open))\n }\n\n const cancel = async () => {\n setError('')\n setPending(true)\n try {\n const response = await fetch(`${apiRoute}/ai-assistant/cancel`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ planId: plan.id }),\n })\n if (!response.ok) {\n throw new Error(\n messageOf(await response.json().catch(() => null)) ?? 'The plan could not be cancelled.',\n )\n }\n onOutcome(plan.id, { status: 'cancelled' })\n } catch (caught) {\n setError(\n caught instanceof Error && caught.message\n ? caught.message\n : 'The plan could not be cancelled.',\n )\n } finally {\n setPending(false)\n }\n }\n\n return (\n <section aria-labelledby={headingId} className={styles.assistant__plan} data-status={status}>\n <div className={styles.assistant__planHeader}>\n <h4 className={styles.assistant__planTitle} id={headingId}>\n {plan.blocked ? 'Needs a decision' : 'Plan'}\n </h4>\n <Pill tone={statusTone[status]}>{statusLabel[status]}</Pill>\n </div>\n <p className={styles.assistant__text}>{plan.summary}</p>\n\n {plan.blocked ? <Decision plan={plan} /> : <Operations plan={plan} />}\n\n {status === 'applied' ? <Changes changes={output.changes ?? []} /> : null}\n {editor === 'reloaded' ? (\n <p className={styles.assistant__meta}>\n The page behind this panel now shows these changes.\n </p>\n ) : null}\n {editor === 'unsaved' ? (\n <div className={styles.assistant__notice}>\n <p className={styles.assistant__meta}>\n This page has edits you have not saved, so the editor still shows them. Loading the\n saved changes replaces your unsaved edits.\n </p>\n <Button\n buttonStyle=\"secondary\"\n margin={false}\n onClick={() => void loadIntoEditor()}\n size=\"small\"\n type=\"button\"\n >\n Load saved changes\n </Button>\n </div>\n ) : null}\n {editor === 'failed' ? (\n <p className={styles.assistant__error} role=\"alert\">\n The changes were saved, but the editor could not be refreshed. Reload the page to see\n them.\n </p>\n ) : null}\n {status === 'cancelled' ? (\n <p className={styles.assistant__meta}>Cancelled. Nothing was saved.</p>\n ) : null}\n\n {status === 'proposed' ? (\n <>\n <div className={styles.assistant__actions}>\n <Button\n buttonStyle=\"primary\"\n disabled={pending}\n margin={false}\n onClick={() => void proceed()}\n size=\"small\"\n type=\"button\"\n >\n Proceed\n </Button>\n <Button\n buttonStyle=\"secondary\"\n disabled={pending}\n margin={false}\n onClick={() => void cancel()}\n size=\"small\"\n type=\"button\"\n >\n Cancel\n </Button>\n </div>\n </>\n ) : null}\n {error ? (\n <p className={styles.assistant__error} role=\"alert\">\n {error}\n </p>\n ) : null}\n </section>\n )\n}\n\n/** What happened to the edit screen behind the panel after Proceed. */\ntype EditorSync = 'failed' | 'reloaded' | 'unsaved'\n\n/**\n * When a plan updates the document that is open, its form still shows the old values. Reload it,\n * unless the editor has unsaved edits there: those are theirs to keep or replace.\n */\nasync function syncOpenEditor(changes: AssistantChange[]): Promise<EditorSync | undefined> {\n const open = currentOpenDocument()\n if (!open) {\n return undefined\n }\n const { kind, slug, id } = open.context\n const touched = changes.some(\n (change) =>\n change.action === 'updated' &&\n change.collection === slug &&\n (kind === 'global' || change.id === id),\n )\n if (!touched) {\n return undefined\n }\n if (open.isModified()) {\n return 'unsaved'\n }\n return reloadEditor(open)\n}\n\nasync function reloadEditor(open: OpenDocument): Promise<EditorSync> {\n try {\n await open.reload()\n return 'reloaded'\n } catch {\n return 'failed'\n }\n}\n\nconst statusLabel: Record<PlanStatus, string> = {\n proposed: 'Ready to review',\n blocked: 'Waiting on you',\n applied: 'Saved',\n cancelled: 'Cancelled',\n}\n\nconst statusTone: Record<PlanStatus, PillTone> = {\n proposed: 'info',\n blocked: 'warning',\n applied: 'success',\n cancelled: 'neutral',\n}\n\nfunction Decision({ plan }: { plan: AssistantPlanView }) {\n const entityLabel = useEntityLabel()\n return (\n <>\n <ul className={styles.assistant__list}>\n {plan.questions.map((question) => (\n <li key={question}>{question}</li>\n ))}\n </ul>\n {plan.candidates.length > 0 ? (\n <ul className={styles.assistant__list}>\n {plan.candidates.map((candidate) => (\n <li key={`${candidate.slug}-${candidate.id ?? candidate.label}`}>\n <Pill entity={candidate.slug}>{entityLabel(candidate.slug, candidate.kind)}</Pill>{' '}\n {candidate.href ? <a href={candidate.href}>{candidate.label}</a> : candidate.label}\n </li>\n ))}\n </ul>\n ) : null}\n <p className={styles.assistant__meta}>Reply below to choose.</p>\n </>\n )\n}\n\nfunction Operations({ plan }: { plan: AssistantPlanView }) {\n const entityLabel = useEntityLabel()\n return (\n <>\n {plan.operations.length > 0 ? (\n <ul className={styles.assistant__ops}>\n {plan.operations.map((operation, index) => (\n <li className={styles.assistant__op} key={`${operation.slug}-${operation.id ?? index}`}>\n <div className={styles.assistant__opHeader}>\n <Pill tone={operation.action === 'create' ? 'success' : 'info'}>\n {operation.action === 'create' ? 'Create' : 'Update'}\n </Pill>\n <Pill entity={operation.slug}>{entityLabel(operation.slug, operation.kind)}</Pill>\n <SavePill immediate={operation.savesImmediately} />\n </div>\n <p className={styles.assistant__opLabel}>\n {operation.href ? <a href={operation.href}>{operation.label}</a> : operation.label}\n </p>\n <p className={styles.assistant__meta}>{operation.summary}</p>\n {operation.fieldChanges && operation.fieldChanges.length > 0 ? (\n <FieldChanges changes={operation.fieldChanges} />\n ) : operation.fields.length > 0 ? (\n <p className={styles.assistant__meta}>Fields: {operation.fields.join(', ')}</p>\n ) : null}\n </li>\n ))}\n </ul>\n ) : null}\n {plan.files.length > 0 ? (\n <ul className={styles.assistant__ops}>\n {plan.files.map((file) => (\n <li className={styles.assistant__op} key={file.index}>\n <div className={styles.assistant__opHeader}>\n <Pill>Upload</Pill>\n <Pill entity={file.collection}>{entityLabel(file.collection)}</Pill>\n <SavePill immediate={file.savesImmediately} />\n </div>\n <p className={styles.assistant__opLabel}>{file.name}</p>\n <p className={styles.assistant__meta}>{file.note}</p>\n </li>\n ))}\n </ul>\n ) : null}\n </>\n )\n}\n\nfunction SavePill({ immediate }: { immediate: boolean }) {\n return immediate ? <Pill tone=\"warning\">Saves immediately</Pill> : <Pill>Draft</Pill>\n}\n\n/** Native disclosure, so it works with a keyboard and screen reader and the values stay in the page. */\nfunction FieldChanges({ changes }: { changes: FieldChangeView[] }) {\n return (\n <details className={styles.assistant__diff} open={changes.length <= 3}>\n <summary>\n Show changes <span className={styles.assistant__meta}>({changes.length})</span>\n </summary>\n <dl className={styles.assistant__diffList}>\n {changes.map((change) => (\n <div className={styles.assistant__diffItem} key={change.path}>\n <dt className={styles.assistant__diffPath}>\n <code>{change.path}</code>\n {change.added ? <Pill tone=\"success\">Added blocks</Pill> : null}\n </dt>\n {change.before !== undefined ? (\n <dd className={styles.assistant__diffValue} data-side=\"before\">\n <span className={styles.assistant__diffLabel}>Now</span>\n {change.media?.before ? <Media media={change.media.before} /> : change.before}\n </dd>\n ) : null}\n <dd className={styles.assistant__diffValue} data-side=\"after\">\n <span className={styles.assistant__diffLabel}>\n {change.before === undefined ? 'New' : 'Proposed'}\n </span>\n {change.media?.after ? <Media media={change.media.after} /> : change.after}\n </dd>\n </div>\n ))}\n </dl>\n </details>\n )\n}\n\n/** A file in a diff. The thumbnail is decorative; the name says which file it is. */\nfunction Media({ media }: { media: MediaView }) {\n return (\n <span className={styles.assistant__media}>\n {media.thumbnail ? (\n // A plain img: an admin preview of an existing upload, already resized by its own URL.\n <img\n alt=\"\"\n className={styles.assistant__mediaThumb}\n height={40}\n loading=\"lazy\"\n src={media.thumbnail}\n width={40}\n />\n ) : (\n <span aria-hidden=\"true\" className={styles.assistant__mediaThumb} />\n )}\n <span>\n {media.label}\n {media.pending ? (\n <span className={styles.assistant__meta}> (attached, saved on Proceed)</span>\n ) : null}\n </span>\n </span>\n )\n}\n\nfunction Changes({ changes }: { changes: AssistantChange[] }) {\n if (changes.length === 0) {\n return <p className={styles.assistant__meta}>Saved. There is no document to open.</p>\n }\n return (\n <ul className={styles.assistant__list}>\n {changes.map((change) => (\n <li key={change.href}>\n <a href={change.href}>{change.label}</a>\n <span className={styles.assistant__meta}>\n {' '}\n {change.action}\n {change.detail ? `. ${change.detail}` : ''}\n </span>\n </li>\n ))}\n </ul>\n )\n}\n\nfunction messageOf(payload: unknown): string | undefined {\n if (\n payload &&\n typeof payload === 'object' &&\n 'message' in payload &&\n typeof payload.message === 'string'\n ) {\n return payload.message\n }\n return undefined\n}\n"],"names":["Button","useId","useState","styles","currentOpenDocument","Pill","useEntityLabel","PlanCard","output","apiRoute","onOutcome","headingId","pending","setPending","error","setError","editor","setEditor","plan","status","proceed","response","fetch","method","credentials","headers","body","JSON","stringify","planId","id","catch","Error","payload","json","ok","messageOf","changes","Array","isArray","syncOpenEditor","caught","message","loadIntoEditor","open","reloadEditor","cancel","section","aria-labelledby","className","assistant__plan","data-status","div","assistant__planHeader","h4","assistant__planTitle","blocked","tone","statusTone","statusLabel","p","assistant__text","summary","Decision","Operations","Changes","assistant__meta","assistant__notice","buttonStyle","margin","onClick","size","type","assistant__error","role","assistant__actions","disabled","undefined","kind","slug","context","touched","some","change","action","collection","isModified","reload","proposed","applied","cancelled","entityLabel","ul","assistant__list","questions","map","question","li","candidates","length","candidate","entity","href","a","label","operations","assistant__ops","operation","index","assistant__op","assistant__opHeader","SavePill","immediate","savesImmediately","assistant__opLabel","fieldChanges","FieldChanges","fields","join","files","file","name","note","details","assistant__diff","span","dl","assistant__diffList","assistant__diffItem","dt","assistant__diffPath","code","path","added","before","dd","assistant__diffValue","data-side","assistant__diffLabel","media","Media","after","assistant__media","thumbnail","img","alt","assistant__mediaThumb","height","loading","src","width","aria-hidden","detail"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { UIFieldServerProps } from 'payload';
2
+ /**
3
+ * Warnings at the top of the Humaan AI settings screen, for problems with the site's own provider key. Server-rendered
4
+ * from the config's summary of the key, so no credentials reach this component. The fallback warning follows
5
+ * AI Source as it stands in the form.
6
+ */
7
+ export declare function SettingsAlerts({ payload, req }: UIFieldServerProps): Promise<import("react").JSX.Element | null>;
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Banner } from '@payloadcms/ui';
3
+ import { dayIn, monthStart, usageTotals } from '../chat/usage.js';
4
+ import { pluginRuntime } from '../runtime.js';
5
+ import { AiSourceSwitch } from './AiSourceSwitch.js';
6
+ /**
7
+ * Warnings at the top of the Humaan AI settings screen, for problems with the site's own provider key. Server-rendered
8
+ * from the config's summary of the key, so no credentials reach this component. The fallback warning follows
9
+ * AI Source as it stands in the form.
10
+ */ export async function SettingsAlerts({ payload, req }) {
11
+ const runtime = pluginRuntime(payload.config);
12
+ if (!runtime.ownKey) {
13
+ return runtime.ownKeyIncomplete.length > 0 ? /*#__PURE__*/ _jsx(Banner, {
14
+ type: "warning",
15
+ children: "A custom provider key has been configured, however the settings are not correct. The default Humaan AI budget will be used."
16
+ }) : null;
17
+ }
18
+ const today = dayIn(runtime.timeZone);
19
+ const { fallbackSteps } = await usageTotals(req, monthStart(today), today);
20
+ if (fallbackSteps === 0) {
21
+ return null;
22
+ }
23
+ return /*#__PURE__*/ _jsx(AiSourceSwitch, {
24
+ humaanAi: null,
25
+ ownKey: /*#__PURE__*/ _jsxs(Banner, {
26
+ type: "warning",
27
+ children: [
28
+ fallbackSteps,
29
+ " model ",
30
+ fallbackSteps === 1 ? 'call' : 'calls',
31
+ " this month ran on Humaan AI's credit because the credentials failed, for example when they were revoked, out of credit, or rate limited. Check them and the provider account."
32
+ ]
33
+ })
34
+ });
35
+ }
36
+
37
+ //# sourceMappingURL=SettingsAlerts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/admin/SettingsAlerts.tsx"],"sourcesContent":["import type { UIFieldServerProps } from 'payload'\n\nimport { Banner } from '@payloadcms/ui'\n\nimport { dayIn, monthStart, usageTotals } from '../chat/usage.js'\nimport { pluginRuntime } from '../runtime.js'\nimport { AiSourceSwitch } from './AiSourceSwitch.js'\n\n/**\n * Warnings at the top of the Humaan AI settings screen, for problems with the site's own provider key. Server-rendered\n * from the config's summary of the key, so no credentials reach this component. The fallback warning follows\n * AI Source as it stands in the form.\n */\nexport async function SettingsAlerts({ payload, req }: UIFieldServerProps) {\n const runtime = pluginRuntime(payload.config)\n\n if (!runtime.ownKey) {\n return runtime.ownKeyIncomplete.length > 0 ? (\n <Banner type=\"warning\">\n A custom provider key has been configured, however the settings are not correct. The default\n Humaan AI budget will be used.\n </Banner>\n ) : null\n }\n\n const today = dayIn(runtime.timeZone)\n const { fallbackSteps } = await usageTotals(req, monthStart(today), today)\n if (fallbackSteps === 0) {\n return null\n }\n return (\n <AiSourceSwitch\n humaanAi={null}\n ownKey={\n <Banner type=\"warning\">\n {fallbackSteps} model {fallbackSteps === 1 ? 'call' : 'calls'} this month ran on Humaan\n AI&apos;s credit because the credentials failed, for example when they were revoked, out\n of credit, or rate limited. Check them and the provider account.\n </Banner>\n }\n />\n )\n}\n"],"names":["Banner","dayIn","monthStart","usageTotals","pluginRuntime","AiSourceSwitch","SettingsAlerts","payload","req","runtime","config","ownKey","ownKeyIncomplete","length","type","today","timeZone","fallbackSteps","humaanAi"],"mappings":";AAEA,SAASA,MAAM,QAAQ,iBAAgB;AAEvC,SAASC,KAAK,EAAEC,UAAU,EAAEC,WAAW,QAAQ,mBAAkB;AACjE,SAASC,aAAa,QAAQ,gBAAe;AAC7C,SAASC,cAAc,QAAQ,sBAAqB;AAEpD;;;;CAIC,GACD,OAAO,eAAeC,eAAe,EAAEC,OAAO,EAAEC,GAAG,EAAsB;IACvE,MAAMC,UAAUL,cAAcG,QAAQG,MAAM;IAE5C,IAAI,CAACD,QAAQE,MAAM,EAAE;QACnB,OAAOF,QAAQG,gBAAgB,CAACC,MAAM,GAAG,kBACvC,KAACb;YAAOc,MAAK;sBAAU;aAIrB;IACN;IAEA,MAAMC,QAAQd,MAAMQ,QAAQO,QAAQ;IACpC,MAAM,EAAEC,aAAa,EAAE,GAAG,MAAMd,YAAYK,KAAKN,WAAWa,QAAQA;IACpE,IAAIE,kBAAkB,GAAG;QACvB,OAAO;IACT;IACA,qBACE,KAACZ;QACCa,UAAU;QACVP,sBACE,MAACX;YAAOc,MAAK;;gBACVG;gBAAc;gBAAQA,kBAAkB,IAAI,SAAS;gBAAQ;;;;AAOxE"}
@@ -0,0 +1,11 @@
1
+ interface UsageSummaryProps {
2
+ apiRoute: string;
3
+ /** Refreshes each time the panel opens. */
4
+ open: boolean;
5
+ }
6
+ /**
7
+ * On Vercel billing: the editor's share of today's allowance and, for admins, the share of the project's budget
8
+ * left, as percentages. On the own key: which provider pays and, for admins, today's spend on it.
9
+ */
10
+ export declare function UsageSummary({ apiRoute, open }: UsageSummaryProps): import("react").JSX.Element | null;
11
+ export {};
@@ -0,0 +1,129 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useEffect, useState } from 'react';
4
+ import styles from './AiAssistantPanel.module.scss';
5
+ import { percentOf, usdOf } from './percent.js';
6
+ import { Pill } from './Pill.js';
7
+ /**
8
+ * On Vercel billing: the editor's share of today's allowance and, for admins, the share of the project's budget
9
+ * left, as percentages. On the own key: which provider pays and, for admins, today's spend on it.
10
+ */ export function UsageSummary({ apiRoute, open }) {
11
+ const [usage, setUsage] = useState();
12
+ useEffect(()=>{
13
+ if (!open) {
14
+ return;
15
+ }
16
+ const controller = new AbortController();
17
+ fetch(`${apiRoute}/ai-assistant/usage`, {
18
+ credentials: 'include',
19
+ signal: controller.signal
20
+ }).then((response)=>response.ok ? response.json() : undefined).then((next)=>{
21
+ if (next) {
22
+ setUsage(next);
23
+ }
24
+ }).catch(()=>undefined);
25
+ return ()=>controller.abort();
26
+ }, [
27
+ apiRoute,
28
+ open
29
+ ]);
30
+ if (!usage) {
31
+ return null;
32
+ }
33
+ return /*#__PURE__*/ _jsx("span", {
34
+ className: styles.panel__usage,
35
+ children: usage.ownKey ? /*#__PURE__*/ _jsx(OwnKey, {
36
+ ownKey: usage.ownKey
37
+ }) : /*#__PURE__*/ _jsxs(_Fragment, {
38
+ children: [
39
+ /*#__PURE__*/ _jsx(Today, {
40
+ today: usage.today
41
+ }),
42
+ usage.budget ? /*#__PURE__*/ _jsx(ProjectBudget, {
43
+ budget: usage.budget
44
+ }) : null
45
+ ]
46
+ })
47
+ });
48
+ }
49
+ function Today({ today }) {
50
+ if (!today) {
51
+ return null;
52
+ }
53
+ return /*#__PURE__*/ _jsxs(Pill, {
54
+ tone: today.usedShare >= 0.8 ? 'warning' : 'neutral',
55
+ children: [
56
+ percentOf(today.usedShare, 1),
57
+ " of today's allowance",
58
+ /*#__PURE__*/ _jsx("span", {
59
+ className: styles.assistant__sr,
60
+ children: " used. It resets at midnight."
61
+ })
62
+ ]
63
+ });
64
+ }
65
+ /** What is left of the project's AI Gateway budget for this period. */ function ProjectBudget({ budget }) {
66
+ return /*#__PURE__*/ _jsxs(Pill, {
67
+ tone: budget.low ? 'warning' : 'neutral',
68
+ children: [
69
+ percentOf(budget.remainingShare, 1),
70
+ " budget left",
71
+ budget.low ? ' (low)' : '',
72
+ /*#__PURE__*/ _jsx("span", {
73
+ className: styles.assistant__sr,
74
+ children: " in this project's AI Gateway budget."
75
+ })
76
+ ]
77
+ });
78
+ }
79
+ /** The site's own key pays. Admins also see today's spend on it, and calls that fell back to Vercel's credit. */ function OwnKey({ ownKey }) {
80
+ return /*#__PURE__*/ _jsxs(_Fragment, {
81
+ children: [
82
+ /*#__PURE__*/ _jsxs(Pill, {
83
+ tone: "info",
84
+ children: [
85
+ ownKey.provider,
86
+ " key",
87
+ /*#__PURE__*/ _jsx("span", {
88
+ className: styles.assistant__sr,
89
+ children: " pays for replies."
90
+ })
91
+ ]
92
+ }),
93
+ ownKey.todayUsd !== undefined ? /*#__PURE__*/ _jsxs(Pill, {
94
+ children: [
95
+ usdOf(ownKey.todayUsd),
96
+ " today",
97
+ /*#__PURE__*/ _jsxs("span", {
98
+ className: styles.assistant__sr,
99
+ children: [
100
+ " on the ",
101
+ ownKey.provider,
102
+ " key, for everyone."
103
+ ]
104
+ })
105
+ ]
106
+ }) : null,
107
+ ownKey.fallbackSteps ? /*#__PURE__*/ _jsxs(Pill, {
108
+ tone: "warning",
109
+ children: [
110
+ "Key failed today",
111
+ /*#__PURE__*/ _jsxs("span", {
112
+ className: styles.assistant__sr,
113
+ children: [
114
+ ": ",
115
+ ownKey.fallbackSteps,
116
+ " ",
117
+ ownKey.fallbackSteps === 1 ? 'call' : 'calls',
118
+ " ran on Vercel's credit instead. Check the key and its ",
119
+ ownKey.provider,
120
+ " account."
121
+ ]
122
+ })
123
+ ]
124
+ }) : null
125
+ ]
126
+ });
127
+ }
128
+
129
+ //# sourceMappingURL=UsageSummary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/admin/UsageSummary.tsx"],"sourcesContent":["'use client'\n\nimport { useEffect, useState } from 'react'\n\nimport type { BudgetView, OwnKeyView, UsageView } from '../types.js'\n\nimport styles from './AiAssistantPanel.module.scss'\nimport { percentOf, usdOf } from './percent.js'\nimport { Pill } from './Pill.js'\n\ninterface UsageSummaryProps {\n apiRoute: string\n /** Refreshes each time the panel opens. */\n open: boolean\n}\n\n/**\n * On Vercel billing: the editor's share of today's allowance and, for admins, the share of the project's budget\n * left, as percentages. On the own key: which provider pays and, for admins, today's spend on it.\n */\nexport function UsageSummary({ apiRoute, open }: UsageSummaryProps) {\n const [usage, setUsage] = useState<undefined | UsageView>()\n\n useEffect(() => {\n if (!open) {\n return\n }\n const controller = new AbortController()\n fetch(`${apiRoute}/ai-assistant/usage`, { credentials: 'include', signal: controller.signal })\n .then((response) => (response.ok ? (response.json() as Promise<UsageView>) : undefined))\n .then((next) => {\n if (next) {\n setUsage(next)\n }\n })\n .catch(() => undefined)\n return () => controller.abort()\n }, [apiRoute, open])\n\n if (!usage) {\n return null\n }\n return (\n <span className={styles.panel__usage}>\n {usage.ownKey ? (\n <OwnKey ownKey={usage.ownKey} />\n ) : (\n <>\n <Today today={usage.today} />\n {usage.budget ? <ProjectBudget budget={usage.budget} /> : null}\n </>\n )}\n </span>\n )\n}\n\nfunction Today({ today }: { today: UsageView['today'] }) {\n if (!today) {\n return null\n }\n return (\n <Pill tone={today.usedShare >= 0.8 ? 'warning' : 'neutral'}>\n {percentOf(today.usedShare, 1)} of today&apos;s allowance\n <span className={styles.assistant__sr}> used. It resets at midnight.</span>\n </Pill>\n )\n}\n\n/** What is left of the project's AI Gateway budget for this period. */\nfunction ProjectBudget({ budget }: { budget: BudgetView }) {\n return (\n <Pill tone={budget.low ? 'warning' : 'neutral'}>\n {percentOf(budget.remainingShare, 1)} budget left\n {budget.low ? ' (low)' : ''}\n <span className={styles.assistant__sr}> in this project&apos;s AI Gateway budget.</span>\n </Pill>\n )\n}\n\n/** The site's own key pays. Admins also see today's spend on it, and calls that fell back to Vercel's credit. */\nfunction OwnKey({ ownKey }: { ownKey: OwnKeyView }) {\n return (\n <>\n <Pill tone=\"info\">\n {ownKey.provider} key\n <span className={styles.assistant__sr}> pays for replies.</span>\n </Pill>\n {ownKey.todayUsd !== undefined ? (\n <Pill>\n {usdOf(ownKey.todayUsd)} today\n <span className={styles.assistant__sr}> on the {ownKey.provider} key, for everyone.</span>\n </Pill>\n ) : null}\n {ownKey.fallbackSteps ? (\n <Pill tone=\"warning\">\n Key failed today\n <span className={styles.assistant__sr}>\n : {ownKey.fallbackSteps} {ownKey.fallbackSteps === 1 ? 'call' : 'calls'} ran on\n Vercel&apos;s credit instead. Check the key and its {ownKey.provider} account.\n </span>\n </Pill>\n ) : null}\n </>\n )\n}\n"],"names":["useEffect","useState","styles","percentOf","usdOf","Pill","UsageSummary","apiRoute","open","usage","setUsage","controller","AbortController","fetch","credentials","signal","then","response","ok","json","undefined","next","catch","abort","span","className","panel__usage","ownKey","OwnKey","Today","today","budget","ProjectBudget","tone","usedShare","assistant__sr","low","remainingShare","provider","todayUsd","fallbackSteps"],"mappings":"AAAA;;AAEA,SAASA,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAI3C,OAAOC,YAAY,iCAAgC;AACnD,SAASC,SAAS,EAAEC,KAAK,QAAQ,eAAc;AAC/C,SAASC,IAAI,QAAQ,YAAW;AAQhC;;;CAGC,GACD,OAAO,SAASC,aAAa,EAAEC,QAAQ,EAAEC,IAAI,EAAqB;IAChE,MAAM,CAACC,OAAOC,SAAS,GAAGT;IAE1BD,UAAU;QACR,IAAI,CAACQ,MAAM;YACT;QACF;QACA,MAAMG,aAAa,IAAIC;QACvBC,MAAM,GAAGN,SAAS,mBAAmB,CAAC,EAAE;YAAEO,aAAa;YAAWC,QAAQJ,WAAWI,MAAM;QAAC,GACzFC,IAAI,CAAC,CAACC,WAAcA,SAASC,EAAE,GAAID,SAASE,IAAI,KAA4BC,WAC5EJ,IAAI,CAAC,CAACK;YACL,IAAIA,MAAM;gBACRX,SAASW;YACX;QACF,GACCC,KAAK,CAAC,IAAMF;QACf,OAAO,IAAMT,WAAWY,KAAK;IAC/B,GAAG;QAAChB;QAAUC;KAAK;IAEnB,IAAI,CAACC,OAAO;QACV,OAAO;IACT;IACA,qBACE,KAACe;QAAKC,WAAWvB,OAAOwB,YAAY;kBACjCjB,MAAMkB,MAAM,iBACX,KAACC;YAAOD,QAAQlB,MAAMkB,MAAM;2BAE5B;;8BACE,KAACE;oBAAMC,OAAOrB,MAAMqB,KAAK;;gBACxBrB,MAAMsB,MAAM,iBAAG,KAACC;oBAAcD,QAAQtB,MAAMsB,MAAM;qBAAO;;;;AAKpE;AAEA,SAASF,MAAM,EAAEC,KAAK,EAAiC;IACrD,IAAI,CAACA,OAAO;QACV,OAAO;IACT;IACA,qBACE,MAACzB;QAAK4B,MAAMH,MAAMI,SAAS,IAAI,MAAM,YAAY;;YAC9C/B,UAAU2B,MAAMI,SAAS,EAAE;YAAG;0BAC/B,KAACV;gBAAKC,WAAWvB,OAAOiC,aAAa;0BAAE;;;;AAG7C;AAEA,qEAAqE,GACrE,SAASH,cAAc,EAAED,MAAM,EAA0B;IACvD,qBACE,MAAC1B;QAAK4B,MAAMF,OAAOK,GAAG,GAAG,YAAY;;YAClCjC,UAAU4B,OAAOM,cAAc,EAAE;YAAG;YACpCN,OAAOK,GAAG,GAAG,WAAW;0BACzB,KAACZ;gBAAKC,WAAWvB,OAAOiC,aAAa;0BAAE;;;;AAG7C;AAEA,+GAA+G,GAC/G,SAASP,OAAO,EAAED,MAAM,EAA0B;IAChD,qBACE;;0BACE,MAACtB;gBAAK4B,MAAK;;oBACRN,OAAOW,QAAQ;oBAAC;kCACjB,KAACd;wBAAKC,WAAWvB,OAAOiC,aAAa;kCAAE;;;;YAExCR,OAAOY,QAAQ,KAAKnB,0BACnB,MAACf;;oBACED,MAAMuB,OAAOY,QAAQ;oBAAE;kCACxB,MAACf;wBAAKC,WAAWvB,OAAOiC,aAAa;;4BAAE;4BAASR,OAAOW,QAAQ;4BAAC;;;;iBAEhE;YACHX,OAAOa,aAAa,iBACnB,MAACnC;gBAAK4B,MAAK;;oBAAU;kCAEnB,MAACT;wBAAKC,WAAWvB,OAAOiC,aAAa;;4BAAE;4BAClCR,OAAOa,aAAa;4BAAC;4BAAEb,OAAOa,aAAa,KAAK,IAAI,SAAS;4BAAQ;4BACnBb,OAAOW,QAAQ;4BAAC;;;;iBAGvE;;;AAGV"}
@@ -0,0 +1,25 @@
1
+ /** An attachment once it is stored. The file itself stays in the project's storage. */
2
+ export interface UploadedAttachment {
3
+ id: string;
4
+ name: string;
5
+ size: number;
6
+ type: string;
7
+ }
8
+ type UploadHandler = (args: {
9
+ file: File;
10
+ updateFilename: (filename: string) => void;
11
+ }) => Promise<unknown>;
12
+ /**
13
+ * Stores a file in the attachments collection the way the admin's own upload fields do. When the storage adapter
14
+ * has client uploads on, its handler sends the file straight to storage and only a small description reaches Payload,
15
+ * which keeps large files clear of hosting request limits such as Vercel's. Without a handler, the file goes to
16
+ * Payload directly. This works the same for ImageKit, S3, or any adapter that supports client uploads.
17
+ */
18
+ export declare function uploadAttachment({ apiRoute, file, handler, }: {
19
+ apiRoute: string;
20
+ file: File;
21
+ handler: null | UploadHandler;
22
+ }): Promise<UploadedAttachment>;
23
+ /** Removes an attachment the editor took off before sending. Best effort: unused ones expire anyway. */
24
+ export declare function removeAttachment(apiRoute: string, id: string): Promise<void>;
25
+ export {};
@@ -0,0 +1,76 @@
1
+ import { uploadRequiresServerValidation } from 'payload/shared';
2
+ import { aiAssistantAttachmentSlug } from '../constants.js';
3
+ /**
4
+ * Stores a file in the attachments collection the way the admin's own upload fields do. When the storage adapter
5
+ * has client uploads on, its handler sends the file straight to storage and only a small description reaches Payload,
6
+ * which keeps large files clear of hosting request limits such as Vercel's. Without a handler, the file goes to
7
+ * Payload directly. This works the same for ImageKit, S3, or any adapter that supports client uploads.
8
+ */ export async function uploadAttachment({ apiRoute, file, handler }) {
9
+ const collectionSlug = aiAssistantAttachmentSlug;
10
+ const body = new FormData();
11
+ body.set('_payload', JSON.stringify({}));
12
+ // Payload sends some types, such as SVG, through the server so it can check them.
13
+ if (handler && !uploadRequiresServerValidation({
14
+ filename: file.name,
15
+ mimeType: file.type
16
+ })) {
17
+ let filename = file.name;
18
+ const clientUploadContext = await handler({
19
+ file,
20
+ updateFilename: (value)=>{
21
+ filename = value;
22
+ }
23
+ });
24
+ body.set('file', JSON.stringify({
25
+ clientUploadContext,
26
+ collectionSlug,
27
+ filename,
28
+ mimeType: file.type,
29
+ size: file.size
30
+ }));
31
+ } else {
32
+ body.set('file', file);
33
+ }
34
+ const response = await fetch(`${apiRoute}/${collectionSlug}?depth=0`, {
35
+ method: 'POST',
36
+ body,
37
+ credentials: 'include'
38
+ });
39
+ const payload = await response.json().catch(()=>null);
40
+ if (!response.ok) {
41
+ throw new Error(errorOf(payload) ?? `${file.name} could not be uploaded.`);
42
+ }
43
+ const doc = payload && typeof payload === 'object' && 'doc' in payload ? payload.doc : undefined;
44
+ if (!doc || doc.id === undefined) {
45
+ throw new Error(`${file.name} could not be uploaded.`);
46
+ }
47
+ return {
48
+ id: String(doc.id),
49
+ name: typeof doc.filename === 'string' ? doc.filename : file.name,
50
+ size: typeof doc.filesize === 'number' ? doc.filesize : file.size,
51
+ type: typeof doc.mimeType === 'string' ? doc.mimeType : file.type
52
+ };
53
+ }
54
+ /** Removes an attachment the editor took off before sending. Best effort: unused ones expire anyway. */ export async function removeAttachment(apiRoute, id) {
55
+ await fetch(`${apiRoute}/${aiAssistantAttachmentSlug}/${id}`, {
56
+ method: 'DELETE',
57
+ credentials: 'include'
58
+ }).catch(()=>undefined);
59
+ }
60
+ function errorOf(payload) {
61
+ if (!payload || typeof payload !== 'object') {
62
+ return undefined;
63
+ }
64
+ if ('message' in payload && typeof payload.message === 'string') {
65
+ return payload.message;
66
+ }
67
+ if ('errors' in payload && Array.isArray(payload.errors)) {
68
+ const first = payload.errors[0];
69
+ if (first && typeof first.message === 'string') {
70
+ return first.message;
71
+ }
72
+ }
73
+ return undefined;
74
+ }
75
+
76
+ //# sourceMappingURL=attachmentUpload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/admin/attachmentUpload.ts"],"sourcesContent":["import type { UploadCollectionSlug } from 'payload'\n\nimport { uploadRequiresServerValidation } from 'payload/shared'\n\nimport { aiAssistantAttachmentSlug } from '../constants.js'\n\n/** An attachment once it is stored. The file itself stays in the project's storage. */\nexport interface UploadedAttachment {\n id: string\n name: string\n size: number\n type: string\n}\n\ntype UploadHandler = (args: {\n file: File\n updateFilename: (filename: string) => void\n}) => Promise<unknown>\n\n/**\n * Stores a file in the attachments collection the way the admin's own upload fields do. When the storage adapter\n * has client uploads on, its handler sends the file straight to storage and only a small description reaches Payload,\n * which keeps large files clear of hosting request limits such as Vercel's. Without a handler, the file goes to\n * Payload directly. This works the same for ImageKit, S3, or any adapter that supports client uploads.\n */\nexport async function uploadAttachment({\n apiRoute,\n file,\n handler,\n}: {\n apiRoute: string\n file: File\n handler: null | UploadHandler\n}): Promise<UploadedAttachment> {\n const collectionSlug = aiAssistantAttachmentSlug as UploadCollectionSlug\n const body = new FormData()\n body.set('_payload', JSON.stringify({}))\n\n // Payload sends some types, such as SVG, through the server so it can check them.\n if (handler && !uploadRequiresServerValidation({ filename: file.name, mimeType: file.type })) {\n let filename = file.name\n const clientUploadContext = await handler({\n file,\n updateFilename: (value) => {\n filename = value\n },\n })\n body.set(\n 'file',\n JSON.stringify({\n clientUploadContext,\n collectionSlug,\n filename,\n mimeType: file.type,\n size: file.size,\n }),\n )\n } else {\n body.set('file', file)\n }\n\n const response = await fetch(`${apiRoute}/${collectionSlug}?depth=0`, {\n method: 'POST',\n body,\n credentials: 'include',\n })\n const payload: unknown = await response.json().catch(() => null)\n if (!response.ok) {\n throw new Error(errorOf(payload) ?? `${file.name} could not be uploaded.`)\n }\n const doc =\n payload && typeof payload === 'object' && 'doc' in payload\n ? (payload.doc as Record<string, unknown>)\n : undefined\n if (!doc || doc.id === undefined) {\n throw new Error(`${file.name} could not be uploaded.`)\n }\n return {\n id: String(doc.id as number | string),\n name: typeof doc.filename === 'string' ? doc.filename : file.name,\n size: typeof doc.filesize === 'number' ? doc.filesize : file.size,\n type: typeof doc.mimeType === 'string' ? doc.mimeType : file.type,\n }\n}\n\n/** Removes an attachment the editor took off before sending. Best effort: unused ones expire anyway. */\nexport async function removeAttachment(apiRoute: string, id: string): Promise<void> {\n await fetch(`${apiRoute}/${aiAssistantAttachmentSlug}/${id}`, {\n method: 'DELETE',\n credentials: 'include',\n }).catch(() => undefined)\n}\n\nfunction errorOf(payload: unknown): string | undefined {\n if (!payload || typeof payload !== 'object') {\n return undefined\n }\n if ('message' in payload && typeof payload.message === 'string') {\n return payload.message\n }\n if ('errors' in payload && Array.isArray(payload.errors)) {\n const first = payload.errors[0] as { message?: unknown } | undefined\n if (first && typeof first.message === 'string') {\n return first.message\n }\n }\n return undefined\n}\n"],"names":["uploadRequiresServerValidation","aiAssistantAttachmentSlug","uploadAttachment","apiRoute","file","handler","collectionSlug","body","FormData","set","JSON","stringify","filename","name","mimeType","type","clientUploadContext","updateFilename","value","size","response","fetch","method","credentials","payload","json","catch","ok","Error","errorOf","doc","undefined","id","String","filesize","removeAttachment","message","Array","isArray","errors","first"],"mappings":"AAEA,SAASA,8BAA8B,QAAQ,iBAAgB;AAE/D,SAASC,yBAAyB,QAAQ,kBAAiB;AAe3D;;;;;CAKC,GACD,OAAO,eAAeC,iBAAiB,EACrCC,QAAQ,EACRC,IAAI,EACJC,OAAO,EAKR;IACC,MAAMC,iBAAiBL;IACvB,MAAMM,OAAO,IAAIC;IACjBD,KAAKE,GAAG,CAAC,YAAYC,KAAKC,SAAS,CAAC,CAAC;IAErC,kFAAkF;IAClF,IAAIN,WAAW,CAACL,+BAA+B;QAAEY,UAAUR,KAAKS,IAAI;QAAEC,UAAUV,KAAKW,IAAI;IAAC,IAAI;QAC5F,IAAIH,WAAWR,KAAKS,IAAI;QACxB,MAAMG,sBAAsB,MAAMX,QAAQ;YACxCD;YACAa,gBAAgB,CAACC;gBACfN,WAAWM;YACb;QACF;QACAX,KAAKE,GAAG,CACN,QACAC,KAAKC,SAAS,CAAC;YACbK;YACAV;YACAM;YACAE,UAAUV,KAAKW,IAAI;YACnBI,MAAMf,KAAKe,IAAI;QACjB;IAEJ,OAAO;QACLZ,KAAKE,GAAG,CAAC,QAAQL;IACnB;IAEA,MAAMgB,WAAW,MAAMC,MAAM,GAAGlB,SAAS,CAAC,EAAEG,eAAe,QAAQ,CAAC,EAAE;QACpEgB,QAAQ;QACRf;QACAgB,aAAa;IACf;IACA,MAAMC,UAAmB,MAAMJ,SAASK,IAAI,GAAGC,KAAK,CAAC,IAAM;IAC3D,IAAI,CAACN,SAASO,EAAE,EAAE;QAChB,MAAM,IAAIC,MAAMC,QAAQL,YAAY,GAAGpB,KAAKS,IAAI,CAAC,uBAAuB,CAAC;IAC3E;IACA,MAAMiB,MACJN,WAAW,OAAOA,YAAY,YAAY,SAASA,UAC9CA,QAAQM,GAAG,GACZC;IACN,IAAI,CAACD,OAAOA,IAAIE,EAAE,KAAKD,WAAW;QAChC,MAAM,IAAIH,MAAM,GAAGxB,KAAKS,IAAI,CAAC,uBAAuB,CAAC;IACvD;IACA,OAAO;QACLmB,IAAIC,OAAOH,IAAIE,EAAE;QACjBnB,MAAM,OAAOiB,IAAIlB,QAAQ,KAAK,WAAWkB,IAAIlB,QAAQ,GAAGR,KAAKS,IAAI;QACjEM,MAAM,OAAOW,IAAII,QAAQ,KAAK,WAAWJ,IAAII,QAAQ,GAAG9B,KAAKe,IAAI;QACjEJ,MAAM,OAAOe,IAAIhB,QAAQ,KAAK,WAAWgB,IAAIhB,QAAQ,GAAGV,KAAKW,IAAI;IACnE;AACF;AAEA,sGAAsG,GACtG,OAAO,eAAeoB,iBAAiBhC,QAAgB,EAAE6B,EAAU;IACjE,MAAMX,MAAM,GAAGlB,SAAS,CAAC,EAAEF,0BAA0B,CAAC,EAAE+B,IAAI,EAAE;QAC5DV,QAAQ;QACRC,aAAa;IACf,GAAGG,KAAK,CAAC,IAAMK;AACjB;AAEA,SAASF,QAAQL,OAAgB;IAC/B,IAAI,CAACA,WAAW,OAAOA,YAAY,UAAU;QAC3C,OAAOO;IACT;IACA,IAAI,aAAaP,WAAW,OAAOA,QAAQY,OAAO,KAAK,UAAU;QAC/D,OAAOZ,QAAQY,OAAO;IACxB;IACA,IAAI,YAAYZ,WAAWa,MAAMC,OAAO,CAACd,QAAQe,MAAM,GAAG;QACxD,MAAMC,QAAQhB,QAAQe,MAAM,CAAC,EAAE;QAC/B,IAAIC,SAAS,OAAOA,MAAMJ,OAAO,KAAK,UAAU;YAC9C,OAAOI,MAAMJ,OAAO;QACtB;IACF;IACA,OAAOL;AACT"}
@@ -0,0 +1,13 @@
1
+ import type { PageContext } from '../types.js';
2
+ /** A document open in the admin. getData returns its current form values, including unsaved changes. */
3
+ export interface OpenDocument {
4
+ context: PageContext;
5
+ getData: () => unknown;
6
+ /** The form has edits that are not saved. */
7
+ isModified: () => boolean;
8
+ /** Loads the saved draft into the form, replacing what is on screen. */
9
+ reload: () => Promise<void>;
10
+ }
11
+ export declare function registerOpenDocument(document: OpenDocument): () => void;
12
+ export declare function currentOpenDocument(): OpenDocument | undefined;
13
+ export declare function useOpenDocument(): OpenDocument | undefined;
@@ -0,0 +1,34 @@
1
+ import { useSyncExternalStore } from 'react';
2
+ /**
3
+ * The drawer lives in the admin header, outside the document's form, so the edit view reports itself here.
4
+ * A document opened in a drawer on top of another one is the newest entry, and the one the editor is looking at.
5
+ */ const open = [];
6
+ const listeners = new Set();
7
+ export function registerOpenDocument(document) {
8
+ open.push(document);
9
+ notify();
10
+ return ()=>{
11
+ const index = open.lastIndexOf(document);
12
+ if (index >= 0) {
13
+ open.splice(index, 1);
14
+ }
15
+ notify();
16
+ };
17
+ }
18
+ export function currentOpenDocument() {
19
+ return open.at(-1);
20
+ }
21
+ export function useOpenDocument() {
22
+ return useSyncExternalStore(subscribe, currentOpenDocument, ()=>undefined);
23
+ }
24
+ function subscribe(listener) {
25
+ listeners.add(listener);
26
+ return ()=>listeners.delete(listener);
27
+ }
28
+ function notify() {
29
+ for (const listener of listeners){
30
+ listener();
31
+ }
32
+ }
33
+
34
+ //# sourceMappingURL=openDocument.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/admin/openDocument.ts"],"sourcesContent":["import { useSyncExternalStore } from 'react'\n\nimport type { PageContext } from '../types.js'\n\n/** A document open in the admin. getData returns its current form values, including unsaved changes. */\nexport interface OpenDocument {\n context: PageContext\n getData: () => unknown\n /** The form has edits that are not saved. */\n isModified: () => boolean\n /** Loads the saved draft into the form, replacing what is on screen. */\n reload: () => Promise<void>\n}\n\n/**\n * The drawer lives in the admin header, outside the document's form, so the edit view reports itself here.\n * A document opened in a drawer on top of another one is the newest entry, and the one the editor is looking at.\n */\nconst open: OpenDocument[] = []\nconst listeners = new Set<() => void>()\n\nexport function registerOpenDocument(document: OpenDocument): () => void {\n open.push(document)\n notify()\n return () => {\n const index = open.lastIndexOf(document)\n if (index >= 0) {\n open.splice(index, 1)\n }\n notify()\n }\n}\n\nexport function currentOpenDocument(): OpenDocument | undefined {\n return open.at(-1)\n}\n\nexport function useOpenDocument(): OpenDocument | undefined {\n return useSyncExternalStore(subscribe, currentOpenDocument, () => undefined)\n}\n\nfunction subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => listeners.delete(listener)\n}\n\nfunction notify(): void {\n for (const listener of listeners) {\n listener()\n }\n}\n"],"names":["useSyncExternalStore","open","listeners","Set","registerOpenDocument","document","push","notify","index","lastIndexOf","splice","currentOpenDocument","at","useOpenDocument","subscribe","undefined","listener","add","delete"],"mappings":"AAAA,SAASA,oBAAoB,QAAQ,QAAO;AAc5C;;;CAGC,GACD,MAAMC,OAAuB,EAAE;AAC/B,MAAMC,YAAY,IAAIC;AAEtB,OAAO,SAASC,qBAAqBC,QAAsB;IACzDJ,KAAKK,IAAI,CAACD;IACVE;IACA,OAAO;QACL,MAAMC,QAAQP,KAAKQ,WAAW,CAACJ;QAC/B,IAAIG,SAAS,GAAG;YACdP,KAAKS,MAAM,CAACF,OAAO;QACrB;QACAD;IACF;AACF;AAEA,OAAO,SAASI;IACd,OAAOV,KAAKW,EAAE,CAAC,CAAC;AAClB;AAEA,OAAO,SAASC;IACd,OAAOb,qBAAqBc,WAAWH,qBAAqB,IAAMI;AACpE;AAEA,SAASD,UAAUE,QAAoB;IACrCd,UAAUe,GAAG,CAACD;IACd,OAAO,IAAMd,UAAUgB,MAAM,CAACF;AAChC;AAEA,SAAST;IACP,KAAK,MAAMS,YAAYd,UAAW;QAChCc;IACF;AACF"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * A whole-number percentage for the panel header. Spend above zero never shows as 0%,
3
+ * and the figure stays between 0% and 100%.
4
+ */
5
+ export declare function percentOf(part: number, whole: number): string;
6
+ /**
7
+ * US dollars for the site's own spend on its provider key, such as `US$12.34`. Spend above zero never shows as
8
+ * US$0.00. Only used for the own key: the Vercel budget is shown as shares.
9
+ */
10
+ export declare function usdOf(amount: number): string;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * A whole-number percentage for the panel header. Spend above zero never shows as 0%,
3
+ * and the figure stays between 0% and 100%.
4
+ */ export function percentOf(part, whole) {
5
+ if (!(whole > 0) || !(part > 0)) {
6
+ return '0%';
7
+ }
8
+ const share = Math.min(part / whole, 1) * 100;
9
+ return share < 1 ? '<1%' : `${Math.round(share)}%`;
10
+ }
11
+ /**
12
+ * US dollars for the site's own spend on its provider key, such as `US$12.34`. Spend above zero never shows as
13
+ * US$0.00. Only used for the own key: the Vercel budget is shown as shares.
14
+ */ export function usdOf(amount) {
15
+ if (!(amount > 0)) {
16
+ return 'US$0.00';
17
+ }
18
+ return amount < 0.01 ? '<US$0.01' : `US$${amount.toFixed(2)}`;
19
+ }
20
+
21
+ //# sourceMappingURL=percent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/admin/percent.ts"],"sourcesContent":["/**\n * A whole-number percentage for the panel header. Spend above zero never shows as 0%,\n * and the figure stays between 0% and 100%.\n */\nexport function percentOf(part: number, whole: number): string {\n if (!(whole > 0) || !(part > 0)) {\n return '0%'\n }\n const share = Math.min(part / whole, 1) * 100\n return share < 1 ? '<1%' : `${Math.round(share)}%`\n}\n\n/**\n * US dollars for the site's own spend on its provider key, such as `US$12.34`. Spend above zero never shows as\n * US$0.00. Only used for the own key: the Vercel budget is shown as shares.\n */\nexport function usdOf(amount: number): string {\n if (!(amount > 0)) {\n return 'US$0.00'\n }\n return amount < 0.01 ? '<US$0.01' : `US$${amount.toFixed(2)}`\n}\n"],"names":["percentOf","part","whole","share","Math","min","round","usdOf","amount","toFixed"],"mappings":"AAAA;;;CAGC,GACD,OAAO,SAASA,UAAUC,IAAY,EAAEC,KAAa;IACnD,IAAI,CAAEA,CAAAA,QAAQ,CAAA,KAAM,CAAED,CAAAA,OAAO,CAAA,GAAI;QAC/B,OAAO;IACT;IACA,MAAME,QAAQC,KAAKC,GAAG,CAACJ,OAAOC,OAAO,KAAK;IAC1C,OAAOC,QAAQ,IAAI,QAAQ,GAAGC,KAAKE,KAAK,CAACH,OAAO,CAAC,CAAC;AACpD;AAEA;;;CAGC,GACD,OAAO,SAASI,MAAMC,MAAc;IAClC,IAAI,CAAEA,CAAAA,SAAS,CAAA,GAAI;QACjB,OAAO;IACT;IACA,OAAOA,SAAS,OAAO,aAAa,CAAC,GAAG,EAAEA,OAAOC,OAAO,CAAC,IAAI;AAC/D"}
@@ -0,0 +1,21 @@
1
+ import type { AssistantUIMessage } from '../chat/messages.js';
2
+ import type { UploadedAttachment } from './attachmentUpload.js';
3
+ export interface ThreadSummary {
4
+ chatId: string;
5
+ title: string;
6
+ updatedAt: string;
7
+ }
8
+ export interface LoadedThread {
9
+ /** Attachments stored for this conversation. Ones from before attachments were stored are left out. */
10
+ attachments: UploadedAttachment[];
11
+ chatId: string;
12
+ messages: AssistantUIMessage[];
13
+ }
14
+ /** The editor's most recently updated conversations. Read access on the collection returns only their own. */
15
+ export declare function listThreads(apiRoute: string, signal?: AbortSignal): Promise<ThreadSummary[]>;
16
+ export declare function loadThread(apiRoute: string, chatId: string): Promise<LoadedThread>;
17
+ /**
18
+ * Option text for the conversation picker, in the editor's local time. The date comes first so a long title
19
+ * cannot push it out of view: "Today, 11:15am: Title", "Yesterday, 9:05pm: Title", or "14/10/2026, 3:20pm: Title".
20
+ */
21
+ export declare function threadOptionLabel(thread: ThreadSummary, now?: Date): string;
@@ -0,0 +1,92 @@
1
+ import { restoreStoredMessages } from '../chat/history.js';
2
+ import { aiAssistantThreadSlug } from '../constants.js';
3
+ /** How many recent conversations the picker lists. */ const recentThreadLimit = 10;
4
+ /** The editor's most recently updated conversations. Read access on the collection returns only their own. */ export async function listThreads(apiRoute, signal) {
5
+ const params = new URLSearchParams({
6
+ sort: '-updatedAt',
7
+ limit: String(recentThreadLimit),
8
+ depth: '0',
9
+ 'select[title]': 'true',
10
+ 'select[chatId]': 'true',
11
+ 'select[updatedAt]': 'true'
12
+ });
13
+ const response = await fetch(`${apiRoute}/${aiAssistantThreadSlug}?${params}`, {
14
+ credentials: 'include',
15
+ signal
16
+ });
17
+ if (!response.ok) {
18
+ return [];
19
+ }
20
+ const payload = await response.json().catch(()=>null);
21
+ return docsOf(payload).flatMap((doc)=>typeof doc.chatId === 'string' ? [
22
+ {
23
+ chatId: doc.chatId,
24
+ title: typeof doc.title === 'string' && doc.title ? doc.title : 'Untitled conversation',
25
+ updatedAt: typeof doc.updatedAt === 'string' ? doc.updatedAt : ''
26
+ }
27
+ ] : []);
28
+ }
29
+ export async function loadThread(apiRoute, chatId) {
30
+ const params = new URLSearchParams({
31
+ 'where[chatId][equals]': chatId,
32
+ limit: '1',
33
+ depth: '0'
34
+ });
35
+ const response = await fetch(`${apiRoute}/${aiAssistantThreadSlug}?${params}`, {
36
+ credentials: 'include'
37
+ });
38
+ const doc = response.ok ? docsOf(await response.json().catch(()=>null))[0] : undefined;
39
+ if (!doc) {
40
+ throw new Error('That conversation could not be opened.');
41
+ }
42
+ return {
43
+ chatId,
44
+ messages: Array.isArray(doc.messages) ? restoreStoredMessages(doc.messages) : [],
45
+ attachments: Array.isArray(doc.files) ? doc.files.flatMap(storedAttachment) : []
46
+ };
47
+ }
48
+ /**
49
+ * Option text for the conversation picker, in the editor's local time. The date comes first so a long title
50
+ * cannot push it out of view: "Today, 11:15am: Title", "Yesterday, 9:05pm: Title", or "14/10/2026, 3:20pm: Title".
51
+ */ export function threadOptionLabel(thread, now = new Date()) {
52
+ const updated = new Date(thread.updatedAt);
53
+ if (Number.isNaN(updated.getTime())) {
54
+ return thread.title;
55
+ }
56
+ const hours = updated.getHours();
57
+ const time = `${hours % 12 || 12}:${pad(updated.getMinutes())}${hours < 12 ? 'am' : 'pm'}`;
58
+ const days = Math.round((startOfDay(now) - startOfDay(updated)) / 86_400_000);
59
+ const day = days === 0 ? 'Today' : days === 1 ? 'Yesterday' : `${pad(updated.getDate())}/${pad(updated.getMonth() + 1)}/${updated.getFullYear()}`;
60
+ return `${day}, ${time}: ${thread.title}`;
61
+ }
62
+ function startOfDay(date) {
63
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
64
+ }
65
+ function pad(value) {
66
+ return String(value).padStart(2, '0');
67
+ }
68
+ function storedAttachment(value) {
69
+ if (!value || typeof value !== 'object') {
70
+ return [];
71
+ }
72
+ const file = value;
73
+ if (typeof file.id !== 'string' || typeof file.name !== 'string') {
74
+ return [];
75
+ }
76
+ return [
77
+ {
78
+ id: file.id,
79
+ name: file.name,
80
+ size: typeof file.size === 'number' ? file.size : 0,
81
+ type: typeof file.type === 'string' ? file.type : ''
82
+ }
83
+ ];
84
+ }
85
+ function docsOf(payload) {
86
+ if (!payload || typeof payload !== 'object' || !('docs' in payload) || !Array.isArray(payload.docs)) {
87
+ return [];
88
+ }
89
+ return payload.docs.filter((doc)=>Boolean(doc) && typeof doc === 'object');
90
+ }
91
+
92
+ //# sourceMappingURL=threads.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/admin/threads.ts"],"sourcesContent":["import type { AssistantUIMessage } from '../chat/messages.js'\nimport type { UploadedAttachment } from './attachmentUpload.js'\n\nimport { restoreStoredMessages } from '../chat/history.js'\nimport { aiAssistantThreadSlug } from '../constants.js'\n\nexport interface ThreadSummary {\n chatId: string\n title: string\n updatedAt: string\n}\n\nexport interface LoadedThread {\n /** Attachments stored for this conversation. Ones from before attachments were stored are left out. */\n attachments: UploadedAttachment[]\n chatId: string\n messages: AssistantUIMessage[]\n}\n\n/** How many recent conversations the picker lists. */\nconst recentThreadLimit = 10\n\n/** The editor's most recently updated conversations. Read access on the collection returns only their own. */\nexport async function listThreads(\n apiRoute: string,\n signal?: AbortSignal,\n): Promise<ThreadSummary[]> {\n const params = new URLSearchParams({\n sort: '-updatedAt',\n limit: String(recentThreadLimit),\n depth: '0',\n 'select[title]': 'true',\n 'select[chatId]': 'true',\n 'select[updatedAt]': 'true',\n })\n const response = await fetch(`${apiRoute}/${aiAssistantThreadSlug}?${params}`, {\n credentials: 'include',\n signal,\n })\n if (!response.ok) {\n return []\n }\n const payload: unknown = await response.json().catch(() => null)\n return docsOf(payload).flatMap((doc) =>\n typeof doc.chatId === 'string'\n ? [\n {\n chatId: doc.chatId,\n title: typeof doc.title === 'string' && doc.title ? doc.title : 'Untitled conversation',\n updatedAt: typeof doc.updatedAt === 'string' ? doc.updatedAt : '',\n },\n ]\n : [],\n )\n}\n\nexport async function loadThread(apiRoute: string, chatId: string): Promise<LoadedThread> {\n const params = new URLSearchParams({ 'where[chatId][equals]': chatId, limit: '1', depth: '0' })\n const response = await fetch(`${apiRoute}/${aiAssistantThreadSlug}?${params}`, {\n credentials: 'include',\n })\n const doc = response.ok ? docsOf(await response.json().catch(() => null))[0] : undefined\n if (!doc) {\n throw new Error('That conversation could not be opened.')\n }\n return {\n chatId,\n messages: Array.isArray(doc.messages) ? restoreStoredMessages(doc.messages) : [],\n attachments: Array.isArray(doc.files) ? doc.files.flatMap(storedAttachment) : [],\n }\n}\n\n/**\n * Option text for the conversation picker, in the editor's local time. The date comes first so a long title\n * cannot push it out of view: \"Today, 11:15am: Title\", \"Yesterday, 9:05pm: Title\", or \"14/10/2026, 3:20pm: Title\".\n */\nexport function threadOptionLabel(thread: ThreadSummary, now = new Date()): string {\n const updated = new Date(thread.updatedAt)\n if (Number.isNaN(updated.getTime())) {\n return thread.title\n }\n const hours = updated.getHours()\n const time = `${hours % 12 || 12}:${pad(updated.getMinutes())}${hours < 12 ? 'am' : 'pm'}`\n const days = Math.round((startOfDay(now) - startOfDay(updated)) / 86_400_000)\n const day =\n days === 0\n ? 'Today'\n : days === 1\n ? 'Yesterday'\n : `${pad(updated.getDate())}/${pad(updated.getMonth() + 1)}/${updated.getFullYear()}`\n return `${day}, ${time}: ${thread.title}`\n}\n\nfunction startOfDay(date: Date): number {\n return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime()\n}\n\nfunction pad(value: number): string {\n return String(value).padStart(2, '0')\n}\n\nfunction storedAttachment(value: unknown): UploadedAttachment[] {\n if (!value || typeof value !== 'object') {\n return []\n }\n const file = value as Record<string, unknown>\n if (typeof file.id !== 'string' || typeof file.name !== 'string') {\n return []\n }\n return [\n {\n id: file.id,\n name: file.name,\n size: typeof file.size === 'number' ? file.size : 0,\n type: typeof file.type === 'string' ? file.type : '',\n },\n ]\n}\n\nfunction docsOf(payload: unknown): Record<string, unknown>[] {\n if (\n !payload ||\n typeof payload !== 'object' ||\n !('docs' in payload) ||\n !Array.isArray(payload.docs)\n ) {\n return []\n }\n return payload.docs.filter(\n (doc): doc is Record<string, unknown> => Boolean(doc) && typeof doc === 'object',\n )\n}\n"],"names":["restoreStoredMessages","aiAssistantThreadSlug","recentThreadLimit","listThreads","apiRoute","signal","params","URLSearchParams","sort","limit","String","depth","response","fetch","credentials","ok","payload","json","catch","docsOf","flatMap","doc","chatId","title","updatedAt","loadThread","undefined","Error","messages","Array","isArray","attachments","files","storedAttachment","threadOptionLabel","thread","now","Date","updated","Number","isNaN","getTime","hours","getHours","time","pad","getMinutes","days","Math","round","startOfDay","day","getDate","getMonth","getFullYear","date","value","padStart","file","id","name","size","type","docs","filter","Boolean"],"mappings":"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"}