@elytracms/studio 0.0.80 → 0.0.82

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 (196) hide show
  1. package/convex/contract-parity.test.ts +1 -1
  2. package/convex/functions.ts +13 -3
  3. package/convex/http-routes.test.ts +143 -0
  4. package/convex/http.ts +19 -9
  5. package/convex/redirect-revalidate.test.ts +130 -0
  6. package/convex/revalidate.ts +9 -7
  7. package/convex/schema.ts +0 -19
  8. package/convex/triggers.ts +33 -3
  9. package/convex/users.ts +1 -1
  10. package/dist/components/ai/ai-chat-panel.js +26 -4
  11. package/dist/components/ai/ai-chat-panel.js.map +1 -1
  12. package/dist/components/ai/proposal-card.d.ts +27 -0
  13. package/dist/components/ai/proposal-card.js +31 -3
  14. package/dist/components/ai/proposal-card.js.map +1 -1
  15. package/dist/components/ai/proposal-issues.d.ts +55 -0
  16. package/dist/components/ai/proposal-issues.js +69 -0
  17. package/dist/components/ai/proposal-issues.js.map +1 -0
  18. package/dist/components/ai/translate-document-dialog.js +38 -3
  19. package/dist/components/ai/translate-document-dialog.js.map +1 -1
  20. package/dist/components/app-views/default-modules.d.ts +141 -44
  21. package/dist/components/app-views/default-modules.js +229 -65
  22. package/dist/components/app-views/default-modules.js.map +1 -1
  23. package/dist/components/app-views/module.d.ts +38 -0
  24. package/dist/components/app-views/module.js.map +1 -1
  25. package/dist/components/app-views/sidebar.d.ts +5 -2
  26. package/dist/components/app-views/sidebar.js +16 -5
  27. package/dist/components/app-views/sidebar.js.map +1 -1
  28. package/dist/components/app-views/studio-config.d.ts +28 -1
  29. package/dist/components/app-views/studio-config.js +1 -1
  30. package/dist/components/app-views/studio-config.js.map +1 -1
  31. package/dist/components/builder/canvas-render-context.js +10 -2
  32. package/dist/components/builder/canvas-render-context.js.map +1 -1
  33. package/dist/components/builder/clipboard-actions.js +5 -3
  34. package/dist/components/builder/clipboard-actions.js.map +1 -1
  35. package/dist/components/builder/reuse-context.js +7 -1
  36. package/dist/components/builder/reuse-context.js.map +1 -1
  37. package/dist/components/builder/reuse.d.ts +94 -16
  38. package/dist/components/builder/reuse.js +201 -19
  39. package/dist/components/builder/reuse.js.map +1 -1
  40. package/dist/components/cms/bulk-actions.js +10 -5
  41. package/dist/components/cms/bulk-actions.js.map +1 -1
  42. package/dist/components/cms/cell-edit.d.ts +18 -3
  43. package/dist/components/cms/cell-edit.js +29 -7
  44. package/dist/components/cms/cell-edit.js.map +1 -1
  45. package/dist/components/cms/cms-store.d.ts +54 -14
  46. package/dist/components/cms/cms-store.js +142 -45
  47. package/dist/components/cms/cms-store.js.map +1 -1
  48. package/dist/components/cms/collection-table.js +2 -2
  49. package/dist/components/cms/collection-table.js.map +1 -1
  50. package/dist/components/cms/detail-editor.js +15 -14
  51. package/dist/components/cms/detail-editor.js.map +1 -1
  52. package/dist/components/cms/document-editor-context.d.ts +12 -1
  53. package/dist/components/cms/document-editor-context.js.map +1 -1
  54. package/dist/components/cms/field-control.js +22 -8
  55. package/dist/components/cms/field-control.js.map +1 -1
  56. package/dist/components/cms/field-renderer.js +8 -1
  57. package/dist/components/cms/field-renderer.js.map +1 -1
  58. package/dist/components/cms/gate-validation.d.ts +36 -0
  59. package/dist/components/cms/gate-validation.js +17 -0
  60. package/dist/components/cms/gate-validation.js.map +1 -0
  61. package/dist/components/cms/import-export.js +2 -1
  62. package/dist/components/cms/import-export.js.map +1 -1
  63. package/dist/components/cms/inline-field-errors.d.ts +8 -0
  64. package/dist/components/cms/inline-field-errors.js +52 -0
  65. package/dist/components/cms/inline-field-errors.js.map +1 -0
  66. package/dist/components/cms/issues-pane.d.ts +4 -2
  67. package/dist/components/cms/issues-pane.js +13 -7
  68. package/dist/components/cms/issues-pane.js.map +1 -1
  69. package/dist/components/cms/slug-collision-report.d.ts +41 -0
  70. package/dist/components/cms/slug-collision-report.js +52 -0
  71. package/dist/components/cms/slug-collision-report.js.map +1 -0
  72. package/dist/components/components/transfer-dialog.js +10 -12
  73. package/dist/components/components/transfer-dialog.js.map +1 -1
  74. package/dist/components/deployment-setup-gate.d.ts +24 -0
  75. package/dist/components/deployment-setup-gate.js +29 -4
  76. package/dist/components/deployment-setup-gate.js.map +1 -1
  77. package/dist/components/issue-severity.d.ts +73 -0
  78. package/dist/components/issue-severity.js +56 -0
  79. package/dist/components/issue-severity.js.map +1 -0
  80. package/dist/components/nav-issues.d.ts +68 -0
  81. package/dist/components/nav-issues.js +78 -0
  82. package/dist/components/nav-issues.js.map +1 -0
  83. package/dist/components/operation-error.js +3 -3
  84. package/dist/components/operation-error.js.map +1 -1
  85. package/dist/components/project-list.d.ts +9 -3
  86. package/dist/components/project-list.js +11 -5
  87. package/dist/components/project-list.js.map +1 -1
  88. package/dist/components/project-switcher.d.ts +3 -1
  89. package/dist/components/project-switcher.js +4 -2
  90. package/dist/components/project-switcher.js.map +1 -1
  91. package/dist/components/redirects/redirects-store.d.ts +21 -0
  92. package/dist/components/redirects/redirects-store.js +17 -4
  93. package/dist/components/redirects/redirects-store.js.map +1 -1
  94. package/dist/components/section-nav.js +75 -10
  95. package/dist/components/section-nav.js.map +1 -1
  96. package/dist/customization.d.ts +7 -0
  97. package/dist/customization.js.map +1 -1
  98. package/dist/lib/ai/apply-content.js +6 -7
  99. package/dist/lib/ai/apply-content.js.map +1 -1
  100. package/dist/lib/ai/chat-context.d.ts +32 -26
  101. package/dist/lib/ai/chat-context.js +81 -9
  102. package/dist/lib/ai/chat-context.js.map +1 -1
  103. package/dist/lib/ai/constraints.d.ts +42 -0
  104. package/dist/lib/ai/constraints.js +259 -0
  105. package/dist/lib/ai/constraints.js.map +1 -0
  106. package/dist/lib/ai/index.d.ts +2 -0
  107. package/dist/lib/ai/index.js +2 -0
  108. package/dist/lib/ai/index.js.map +1 -1
  109. package/dist/lib/ai/proposal-validation.d.ts +26 -0
  110. package/dist/lib/ai/proposal-validation.js +17 -0
  111. package/dist/lib/ai/proposal-validation.js.map +1 -0
  112. package/dist/lib/ai/shape.js +60 -13
  113. package/dist/lib/ai/shape.js.map +1 -1
  114. package/dist/lib/ai/tools.d.ts +8 -4
  115. package/dist/lib/ai/tools.js +43 -14
  116. package/dist/lib/ai/tools.js.map +1 -1
  117. package/dist/lib/ai/translate-document.d.ts +35 -1
  118. package/dist/lib/ai/translate-document.js +16 -1
  119. package/dist/lib/ai/translate-document.js.map +1 -1
  120. package/dist/lib/ai/types.d.ts +11 -6
  121. package/dist/lib/ai/types.js.map +1 -1
  122. package/dist/lib/ai/update-fields.d.ts +8 -6
  123. package/dist/lib/ai/update-fields.js +9 -22
  124. package/dist/lib/ai/update-fields.js.map +1 -1
  125. package/dist/lib/clipboard/paste-issues.d.ts +5 -0
  126. package/dist/lib/clipboard/paste-issues.js +16 -6
  127. package/dist/lib/clipboard/paste-issues.js.map +1 -1
  128. package/dist/lib/cms/document-gate.d.ts +37 -0
  129. package/dist/lib/cms/document-gate.js +40 -0
  130. package/dist/lib/cms/document-gate.js.map +1 -0
  131. package/dist/lib/cms-views/view-model.d.ts +5 -0
  132. package/dist/lib/cms-views/view-model.js +10 -4
  133. package/dist/lib/cms-views/view-model.js.map +1 -1
  134. package/dist/lib/data/collection-content.d.ts +6 -1
  135. package/dist/lib/data/collection-content.js +51 -15
  136. package/dist/lib/data/collection-content.js.map +1 -1
  137. package/dist/lib/i18n/i18n.d.ts +51 -16
  138. package/dist/lib/i18n/i18n.js +119 -43
  139. package/dist/lib/i18n/i18n.js.map +1 -1
  140. package/dist/lib/i18n/index.d.ts +8 -1
  141. package/dist/lib/i18n/index.js +1 -1
  142. package/dist/lib/i18n/index.js.map +1 -1
  143. package/dist/lib/i18n/messages.de.js +200 -0
  144. package/dist/lib/i18n/messages.de.js.map +1 -1
  145. package/dist/lib/i18n/messages.en.d.ts +162 -0
  146. package/dist/lib/i18n/messages.en.js +226 -0
  147. package/dist/lib/i18n/messages.en.js.map +1 -1
  148. package/dist/lib/i18n/types.d.ts +15 -9
  149. package/dist/lib/i18n/types.js.map +1 -1
  150. package/dist/lib/locale/binding-fallback.d.ts +8 -1
  151. package/dist/lib/locale/binding-fallback.js +8 -1
  152. package/dist/lib/locale/binding-fallback.js.map +1 -1
  153. package/dist/lib/locale/locale-store.d.ts +41 -9
  154. package/dist/lib/locale/locale-store.js +34 -15
  155. package/dist/lib/locale/locale-store.js.map +1 -1
  156. package/dist/lib/locale/locale-switcher.d.ts +3 -1
  157. package/dist/lib/locale/locale-switcher.js +4 -2
  158. package/dist/lib/locale/locale-switcher.js.map +1 -1
  159. package/dist/lib/locale/use-studio-locale.d.ts +9 -4
  160. package/dist/lib/locale/use-studio-locale.js.map +1 -1
  161. package/dist/lib/operations/studio-operations.js +19 -2
  162. package/dist/lib/operations/studio-operations.js.map +1 -1
  163. package/dist/lib/persistence/convex-adapter.d.ts +1 -3
  164. package/dist/lib/persistence/convex-adapter.js +0 -55
  165. package/dist/lib/persistence/convex-adapter.js.map +1 -1
  166. package/dist/lib/persistence/deployment-readiness.js +10 -0
  167. package/dist/lib/persistence/deployment-readiness.js.map +1 -1
  168. package/dist/lib/persona/labels.d.ts +85 -9
  169. package/dist/lib/persona/labels.js +198 -58
  170. package/dist/lib/persona/labels.js.map +1 -1
  171. package/dist/lib/routes/route-issues.d.ts +9 -0
  172. package/dist/lib/routes/route-issues.js +117 -0
  173. package/dist/lib/routes/route-issues.js.map +1 -0
  174. package/dist/lib/webhooks/dispatcher.d.ts +5 -2
  175. package/dist/lib/webhooks/dispatcher.js.map +1 -1
  176. package/dist/lib/webhooks/tags.js +150 -17
  177. package/dist/lib/webhooks/tags.js.map +1 -1
  178. package/dist/lib/workspace/config-issues.d.ts +56 -0
  179. package/dist/lib/workspace/config-issues.js +72 -0
  180. package/dist/lib/workspace/config-issues.js.map +1 -0
  181. package/dist/lib/workspace/index.d.ts +24 -8
  182. package/dist/lib/workspace/index.js +28 -18
  183. package/dist/lib/workspace/index.js.map +1 -1
  184. package/dist/lib/workspace/switcher-source.d.ts +20 -3
  185. package/dist/lib/workspace/switcher-source.js.map +1 -1
  186. package/dist/lib/workspace/workspace-source.js +27 -0
  187. package/dist/lib/workspace/workspace-source.js.map +1 -1
  188. package/dist/previews/manifest-format.d.ts +13 -6
  189. package/dist/previews/manifest-format.js +9 -2
  190. package/dist/previews/manifest-format.js.map +1 -1
  191. package/dist/router/root.d.ts +36 -1
  192. package/dist/router/root.js +54 -7
  193. package/dist/router/root.js.map +1 -1
  194. package/package.json +2 -2
  195. package/convex/cliTokens.ts +0 -63
  196. package/convex/sync.ts +0 -297
@@ -1 +1 @@
1
- {"version":3,"file":"ai-chat-panel.js","sourceRoot":"","sources":["../../../src/components/ai/ai-chat-panel.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useMemo, useReducer, useRef, useState } from 'react'\nimport { MessageSquarePlus, X } from 'lucide-react'\nimport { DefaultChatTransport, lastAssistantMessageIsCompleteWithToolCalls } from 'ai'\nimport type { UIMessage } from 'ai'\nimport { useChat } from '@ai-sdk/react'\nimport {\n createCollectionRegistry,\n resolveDocumentTitle,\n} from '@elytracms/core/cms-core'\nimport type { CmsDocument, FieldValues } from '@elytracms/core/cms-core'\nimport { Button, Heading, Row, Spacer, Text } from '@elytracms/studio/ui'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { STUDIO_PROJECT_ROUTE, studioPath, useNavigate } from '@elytracms/studio/router'\nimport { resolveDeploymentAi } from '@elytracms/studio/lib/workspace'\nimport { getStudioOperations } from '@elytracms/studio/lib/operations'\nimport {\n DEFAULT_MAX_STEPS,\n buildChatContext,\n chatReviewReducer,\n clearConversation,\n createChatCandidateStore,\n createChatToolExecutor,\n initialChatReviewState,\n renderCurrentDocumentSection,\n resolveAiConfig,\n resolveChatTarget,\n resolveKnowledge,\n withStepCap,\n} from '@elytracms/studio/lib/ai'\nimport { getAiCurrentDocument } from '@elytracms/studio/lib/ai-current-document'\nimport type {\n ChatCandidateStore,\n ChatToolReaders,\n KnowledgeCollectionReader,\n ProposalReview,\n ResolvedKnowledge,\n RouteHeaders,\n} from '@elytracms/studio/lib/ai'\nimport { useCms } from '../cms/cms-store'\nimport { validateComposition, validateRichTextEmbeds } from '../cms/cms-composition-validators'\nimport {\n AssistantTurn,\n ChatErrorNote,\n Conversation,\n ConversationEmpty,\n PromptInput,\n ToolStatus,\n UserTurn,\n WorkingIndicator,\n describeToolPart,\n messageText,\n segmentMessageParts,\n} from './chat-ui'\nimport type { MessagePartView } from './chat-ui'\nimport { ProposalCard } from './proposal-card'\nimport { setAiPanelOpen } from '@elytracms/studio/lib/ai-panel'\n\n/**\n * The AI assistant chat panel (EC-199, moved onto the SDK's native protocol by\n * EC-334) — the right-hand collapsible surface in the CMS area. `useChat` +\n * `DefaultChatTransport` own the conversation against the route's native chat\n * branch (visible token streaming); `onToolCall` dispatches into the client\n * executor map (operations reads + terminal engines, all with the user's own\n * session); `sendAutomaticallyWhen` is the SDK predicate wrapped with the\n * per-turn step cap. What the SDK does not own lives in the pure review reducer:\n * full proposals keyed by `toolCallId` (reviewed as a diff, applied as a DRAFT\n * via operations upsert + a provenance-tagged snapshot — the EC-199 trust\n * boundary, unmoved) and blocking errors. EC-380: the \"new conversation\"\n * affordance clears busy-safely (stop → new chat id + review reset; pending\n * proposals are discarded with reject semantics).\n *\n * NEVER a modal (house rule). The whole module (including `ai` + `@ai-sdk/react`)\n * sits behind the lazy boundary (`ai-chat-panel-lazy`) — pinned in bundle tests.\n */\n\n/** Adapt a `RouteHeaders` (static or thunk) to the transport's headers resolvable. */\nfunction toTransportHeaders(headers: RouteHeaders | undefined): () => Promise<Record<string, string>> {\n return async () => {\n const resolved = typeof headers === 'function' ? await headers() : headers\n const record: Record<string, string> = {}\n new Headers(resolved ?? {}).forEach((value, key) => {\n record[key] = value\n })\n return record\n }\n}\n\nexport function AiChatPanel() {\n const cms = useCms()\n const navigate = useNavigate()\n const [review, dispatch] = useReducer(chatReviewReducer, initialChatReviewState)\n const [input, setInput] = useState('')\n // EC-380: the conversation counter — bumping it mints a fresh `useChat` id\n // (a brand-new conversation) and a fresh relation-candidate store.\n const [conversation, setConversation] = useState(1)\n\n const projectId = cms.projectId\n const activeLocale = cms.activeLocale\n\n // Resolve the AI platform config once (presence + capability gate). The panel only\n // mounts when configured, but a misconfigured provider still fails loud here.\n const resolved = useMemo(() => resolveAiConfig(resolveDeploymentAi()), [])\n const provider = resolved.resolved?.provider ?? null\n const knowledgeSources = useMemo(() => resolved.resolved?.knowledge ?? [], [resolved])\n const maxRepairAttempts = resolved.resolved?.limits?.maxRepairAttempts\n const ready = provider !== null && resolved.ok && projectId !== undefined\n\n const registry = useMemo(() => createCollectionRegistry(cms.collections), [cms.collections])\n\n /** Merge a document's non-localized + active-locale values for title/prose reads. */\n const localeValues = useCallback(\n (doc: CmsDocument): FieldValues => ({\n ...doc.values,\n ...(doc.localized?.[activeLocale] ?? {}),\n }),\n [activeLocale],\n )\n\n /** Operations-backed tool readers — client-side, with the user's own session. */\n const readers = useMemo<ChatToolReaders>(() => {\n const matches = (query: string | undefined, ...fields: (string | undefined)[]) =>\n !query || fields.some((f) => f?.toLowerCase().includes(query.toLowerCase()))\n return {\n async searchDocuments({ collection, query }) {\n if (projectId === undefined) return []\n const { operations } = await getStudioOperations()\n const result = await operations.documents.list({ projectId, collection })\n if (!result.ok) return []\n const target = cms.getCollection(collection)\n return result.value\n .map((rec) => ({\n id: rec.document.id,\n title: target\n ? resolveDocumentTitle(target, localeValues(rec.document), rec.document.id)\n : rec.document.id,\n }))\n .filter((r) => matches(query, r.title, r.id))\n },\n async listAssets({ query }) {\n if (projectId === undefined) return []\n const { operations } = await getStudioOperations()\n const result = await operations.assets.list({ projectId })\n if (!result.ok) return []\n return result.value\n .map((asset) => ({ id: asset.id, title: asset.title ?? asset.filename }))\n .filter((r) => matches(query, r.title, r.id))\n },\n // Full document (draft values + localized) — the getDocument/translate tools.\n async getDocument({ collection, id }) {\n if (projectId === undefined) return null\n const { operations } = await getStudioOperations()\n const result = await operations.documents.get({ projectId, ref: { collection, id } })\n return result.ok ? result.value?.document ?? null : null\n },\n }\n }, [projectId, cms, localeValues])\n\n /** A fresh, collision-free draft id for the target collection. */\n const mintDocumentId = useCallback(\n (collectionId: string): string => {\n const existing = new Set(cms.documentsOf(collectionId).map((d) => d.id))\n let n = 1\n while (existing.has(`new-${collectionId}-${n}`)) n += 1\n return `new-${collectionId}-${n}`\n },\n [cms],\n )\n\n /** Read `fromCollection(...)` knowledge prose through the operations layer. */\n const readCollection = useCallback<KnowledgeCollectionReader>(\n async (source) => {\n if (projectId === undefined) return []\n const { operations } = await getStudioOperations()\n const result = await operations.documents.list({ projectId, collection: source.collectionId })\n if (!result.ok) return []\n const target = cms.getCollection(source.collectionId)\n const field =\n source.field ?? target?.fields.find((f) => f.type === 'text' || f.type === 'richText')?.name\n return result.value\n .map((rec) => {\n const values = localeValues(rec.document)\n const content = field ? String(values[field] ?? '') : ''\n return { content }\n })\n .filter((item) => item.content.trim().length > 0)\n },\n [projectId, cms, localeValues],\n )\n\n // The knowledge snapshot the executors read — refreshed by the body thunk on\n // every request, so the terminal engines see what the model saw this turn.\n const knowledgeRef = useRef<ResolvedKnowledge>({ items: [] })\n\n // The chat transport target: the configured route provider's endpoint +\n // session-header thunk (EC-371), re-evaluated per request.\n const chatTarget = useMemo(() => resolveChatTarget(provider), [provider])\n\n const transport = useMemo(\n () =>\n new DefaultChatTransport({\n api: chatTarget.endpoint,\n headers: toTransportHeaders(chatTarget.headers),\n // The `body` THUNK — re-evaluated per request (fresh context every turn,\n // continuations included): collections-catalog summary + client-resolved\n // knowledge + EC-378's current-document block (read from the seam store\n // at REQUEST time, so every turn sees what is open NOW). The route folds\n // these sections into the system prompt; it resolves no data itself\n // (zero-data-access proxy). The block is advisory — the deterministic\n // omitted-target fallback lives in the executor.\n body: async () => {\n const knowledge = await resolveKnowledge(knowledgeSources, { readCollection })\n knowledgeRef.current = knowledge\n const current = getAiCurrentDocument()\n return {\n context: buildChatContext({\n collections: cms.collections,\n knowledge,\n ...(current\n ? {\n extraSections: [\n renderCurrentDocumentSection({\n collection: current.collection,\n id: current.id,\n title: current.title,\n locale: activeLocale,\n }),\n ],\n }\n : {}),\n }),\n }\n },\n }),\n [chatTarget, knowledgeSources, readCollection, cms.collections, activeLocale],\n )\n\n // Per-conversation relation-candidate store (EC-380: replaced on clear, so a\n // fresh conversation cannot reference the previous one's searched ids).\n const candidatesRef = useRef<{ conversation: number; store: ChatCandidateStore }>({\n conversation,\n store: createChatCandidateStore(),\n })\n if (candidatesRef.current.conversation !== conversation) {\n candidatesRef.current = { conversation, store: createChatCandidateStore() }\n }\n const candidateStore = candidatesRef.current.store\n\n const executor = useMemo(() => {\n if (!provider) return null\n return createChatToolExecutor({\n provider,\n registry,\n collections: cms.collections,\n readers,\n mintDocumentId,\n candidates: candidateStore,\n onProposal: (toolCallId, proposal) => dispatch({ type: 'propose', toolCallId, proposal }),\n knowledge: () => knowledgeRef.current,\n // EC-378: the current-document seam, read at EXECUTION time — the\n // deterministic fallback when the model omits a tool's target.\n currentDocument: () => getAiCurrentDocument(),\n locale: activeLocale,\n ...(cms.localeConfig ? { localeConfig: cms.localeConfig } : {}),\n validateRichTextEmbeds,\n validateComposition,\n ...(maxRepairAttempts !== undefined ? { maxRepairAttempts } : {}),\n })\n }, [\n provider,\n registry,\n cms.collections,\n cms.localeConfig,\n readers,\n mintDocumentId,\n activeLocale,\n maxRepairAttempts,\n candidateStore,\n ])\n\n const chat = useChat({\n // A new conversation is a new chat id — `useChat` recreates its state.\n id: `elytra-assistant-${conversation}`,\n transport,\n onToolCall: async ({ toolCall }) => {\n if (!executor) return\n const outcome = await executor.execute({\n toolCallId: toolCall.toolCallId,\n toolName: toolCall.toolName,\n input: toolCall.input,\n })\n // Deliberately NOT awaited: `onToolCall` runs inside the chat's serial\n // stream job, and `addToolOutput` enqueues on the same executor —\n // awaiting it here deadlocks the stream (verified against ai@7.0.22).\n if (outcome.ok) {\n void chat.addToolOutput({\n tool: toolCall.toolName,\n toolCallId: toolCall.toolCallId,\n output: outcome.output,\n })\n } else {\n void chat.addToolOutput({\n state: 'output-error',\n tool: toolCall.toolName,\n toolCallId: toolCall.toolCallId,\n errorText: outcome.errorText,\n })\n }\n },\n // The SDK predicate wrapped with the per-user-turn step cap: a capped turn\n // stops cleanly with a visible note — never a silent stall (EC-334).\n sendAutomaticallyWhen: withStepCap(lastAssistantMessageIsCompleteWithToolCalls, {\n maxSteps: DEFAULT_MAX_STEPS,\n onCap: () =>\n dispatch({ type: 'fail', message: translate('ai.error.stepCap', { max: DEFAULT_MAX_STEPS }) }),\n }),\n onError: () => dispatch({ type: 'fail', message: translate('ai.error.route') }),\n })\n\n const busy = chat.status === 'submitted' || chat.status === 'streaming'\n\n const submit = useCallback(() => {\n const text = input.trim()\n if (text.length === 0 || busy || !ready) return\n setInput('')\n dispatch({ type: 'clear-error' })\n void chat.sendMessage({ text })\n }, [input, busy, ready, chat])\n\n // EC-380: clear / new topic — busy-safe (stop first), pending proposals\n // discarded with reject semantics (the review state resets; nothing was applied).\n const startNewConversation = useCallback(() => {\n void clearConversation({ status: chat.status, stop: chat.stop }, () => {\n dispatch({ type: 'clear' })\n setInput('')\n setConversation((current) => current + 1)\n })\n }, [chat.status, chat.stop])\n\n const applyProposal = useCallback(\n async (proposal: ProposalReview) => {\n if (projectId === undefined) return\n // For an update/translate of an EXISTING document, spread the stored doc\n // first so document-level extras (alias, defaultLocale) survive the apply —\n // the patch replaces only values/localized.\n const existing = cms.getDocument({ collection: proposal.collection, id: proposal.documentId })\n const doc: CmsDocument = {\n ...(existing ?? {}),\n collection: proposal.collection,\n id: proposal.documentId,\n values: proposal.patch.values,\n ...(proposal.patch.localized ? { localized: proposal.patch.localized } : {}),\n }\n const { operations } = await getStudioOperations()\n const upserted = await operations.documents.upsert({ projectId, document: doc })\n if (!upserted.ok) {\n dispatch({ type: 'fail', message: translate('ai.error.applyFailed') })\n return\n }\n // Provenance: record an AI-tagged version snapshot (a restore point + the\n // 'assistant' origin surfaced in the versions pane). Best-effort — the draft\n // already landed above; a snapshot failure must not lose the applied content.\n try {\n await operations.documents.snapshot({\n projectId,\n ref: { collection: proposal.collection, id: proposal.documentId },\n label: translate('cms.version.aiAuthoredTitle'),\n origin: 'assistant',\n })\n } catch {\n // ignore — provenance is additive\n }\n // Reflect in the session so the new draft shows in the list + is navigable.\n cms.saveDocument(doc)\n // EC-378: when the applied proposal targets the document OPEN in the editor,\n // adopt it into the editor's draft buffer — otherwise the editor's autosave\n // would write its stale buffer back and silently revert the applied change.\n const current = getAiCurrentDocument()\n if (\n current &&\n current.collection === proposal.collection &&\n current.id === proposal.documentId\n ) {\n current.adoptDraft(doc)\n }\n dispatch({ type: 'apply', toolCallId: proposal.id })\n },\n [projectId, cms],\n )\n\n const openProposal = useCallback(\n (proposal: ProposalReview) => {\n if (projectId === undefined) return\n void navigate({\n to: STUDIO_PROJECT_ROUTE,\n params: { projectId, _splat: studioPath.cmsDocument(proposal.collection, proposal.documentId) },\n })\n },\n [navigate, projectId],\n )\n\n const hasProposal = useCallback(\n (toolCallId: string) => review.proposals[toolCallId] !== undefined,\n [review.proposals],\n )\n\n const renderAssistantMessage = (message: UIMessage) => {\n const segments = segmentMessageParts(message.parts as MessagePartView[], hasProposal)\n return segments.map((segment, index) => {\n if (segment.kind === 'text') {\n return (\n <AssistantTurn key={`${message.id}-${index}`}>\n <Text size=\"sm\" className=\"whitespace-pre-wrap break-words\">\n {segment.text}\n </Text>\n </AssistantTurn>\n )\n }\n if (segment.kind === 'tools') {\n return (\n <AssistantTurn key={`${message.id}-${index}`}>\n <ToolStatus steps={segment.parts.map(describeToolPart)} />\n </AssistantTurn>\n )\n }\n const proposal = review.proposals[segment.toolCallId]\n if (!proposal) return null\n return (\n <ProposalCard\n key={segment.toolCallId}\n proposal={proposal}\n onApply={() => void applyProposal(proposal)}\n onDiscard={() => dispatch({ type: 'reject', toolCallId: proposal.id })}\n onOpen={() => openProposal(proposal)}\n />\n )\n })\n }\n\n return (\n <aside className=\"flex h-full min-h-0 w-[360px] shrink-0 flex-col border-l border-border bg-card\">\n <Row gap=\"2\" align=\"center\" className=\"h-12 shrink-0 border-b border-border px-3\">\n <Heading level={2} size=\"h4\">\n {translate('ai.panel.title')}\n </Heading>\n <Spacer />\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n onClick={startNewConversation}\n disabled={chat.messages.length === 0 && review.error === null && !busy}\n title={translate('ai.panel.newConversation')}\n aria-label={translate('ai.panel.newConversation')}\n >\n <MessageSquarePlus />\n </Button>\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n onClick={() => setAiPanelOpen(false)}\n title={translate('ai.panel.close')}\n aria-label={translate('ai.panel.close')}\n >\n <X />\n </Button>\n </Row>\n\n <Conversation atBottomKey={chat.messages}>\n {chat.messages.length === 0 ? <ConversationEmpty /> : null}\n {chat.messages.map((message) =>\n message.role === 'user' ? (\n <UserTurn key={message.id} text={messageText(message)} />\n ) : (\n <div key={message.id} className=\"contents\">\n {renderAssistantMessage(message)}\n </div>\n ),\n )}\n {chat.status === 'submitted' ? (\n <AssistantTurn>\n <WorkingIndicator />\n </AssistantTurn>\n ) : null}\n {review.error !== null ? (\n <AssistantTurn>\n <ChatErrorNote message={review.error} />\n </AssistantTurn>\n ) : null}\n </Conversation>\n\n {ready ? (\n <PromptInput value={input} onChange={setInput} onSubmit={submit} disabled={busy} />\n ) : (\n <div className=\"border-t border-border p-3\">\n <Text size=\"xs\" tone=\"muted\">\n {translate('ai.panel.unavailable')}\n </Text>\n </div>\n )}\n </aside>\n )\n}\n"]}
1
+ {"version":3,"file":"ai-chat-panel.js","sourceRoot":"","sources":["../../../src/components/ai/ai-chat-panel.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAC1E,OAAO,EAAE,iBAAiB,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,EAAE,oBAAoB,EAAE,2CAA2C,EAAE,MAAM,IAAI,CAAA;AAEtF,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EACL,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,0BAA0B,CAAA;AAEjC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AACzE,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AACtD,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACxF,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAA;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAA;AACtE,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,wBAAwB,EACxB,sBAAsB,EACtB,sBAAsB,EACtB,gBAAgB,EAChB,4BAA4B,EAC5B,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,WAAW,GACZ,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAShF,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,EACL,aAAa,EACb,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,UAAU,EACV,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,mBAAmB,GACpB,MAAM,WAAW,CAAA;AAElB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAE/D;;;;;;;;;;;;;;;;GAgBG;AAEH,sFAAsF;AACtF,SAAS,kBAAkB,CAAC,OAAiC;IAC3D,OAAO,KAAK,IAAI,EAAE;QAChB,MAAM,QAAQ,GAAG,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAA;QAC1E,MAAM,MAAM,GAA2B,EAAE,CAAA;QACzC,IAAI,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YACjD,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;QACrB,CAAC,CAAC,CAAA;QACF,OAAO,MAAM,CAAA;IACf,CAAC,CAAA;AACH,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,MAAM,GAAG,GAAG,MAAM,EAAE,CAAA;IACpB,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAA;IAC9B,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,UAAU,CAAC,iBAAiB,EAAE,sBAAsB,CAAC,CAAA;IAChF,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAA;IACtC,2EAA2E;IAC3E,mEAAmE;IACnE,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IAEnD,MAAM,SAAS,GAAG,GAAG,CAAC,SAAS,CAAA;IAC/B,MAAM,YAAY,GAAG,GAAG,CAAC,YAAY,CAAA;IAErC,mFAAmF;IACnF,8EAA8E;IAC9E,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,mBAAmB,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;IAC1E,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,EAAE,QAAQ,IAAI,IAAI,CAAA;IACpD,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAA;IACtF,MAAM,iBAAiB,GAAG,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,iBAAiB,CAAA;IACtE,MAAM,KAAK,GAAG,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC,EAAE,IAAI,SAAS,KAAK,SAAS,CAAA;IAEzE,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,wBAAwB,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAA;IAE5F,qFAAqF;IACrF,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,GAAgB,EAAe,EAAE,CAAC,CAAC;QAClC,GAAG,GAAG,CAAC,MAAM;QACb,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;KACzC,CAAC,EACF,CAAC,YAAY,CAAC,CACf,CAAA;IAED,iFAAiF;IACjF,MAAM,OAAO,GAAG,OAAO,CAAkB,GAAG,EAAE;QAC5C,MAAM,OAAO,GAAG,CAAC,KAAyB,EAAE,GAAG,MAA8B,EAAE,EAAE,CAC/E,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAA;QAC9E,OAAO;YACL,KAAK,CAAC,eAAe,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE;gBACzC,IAAI,SAAS,KAAK,SAAS;oBAAE,OAAO,EAAE,CAAA;gBACtC,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,mBAAmB,EAAE,CAAA;gBAClD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAAA;gBACzE,IAAI,CAAC,MAAM,CAAC,EAAE;oBAAE,OAAO,EAAE,CAAA;gBACzB,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,CAAC,UAAU,CAAC,CAAA;gBAC5C,OAAO,MAAM,CAAC,KAAK;qBAChB,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;oBACb,EAAE,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE;oBACnB,KAAK,EAAE,MAAM;wBACX,CAAC,CAAC,oBAAoB,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;wBAC3E,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;iBACpB,CAAC,CAAC;qBACF,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;YACjD,CAAC;YACD,KAAK,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE;gBACxB,IAAI,SAAS,KAAK,SAAS;oBAAE,OAAO,EAAE,CAAA;gBACtC,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,mBAAmB,EAAE,CAAA;gBAClD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;gBAC1D,IAAI,CAAC,MAAM,CAAC,EAAE;oBAAE,OAAO,EAAE,CAAA;gBACzB,OAAO,MAAM,CAAC,KAAK;qBAChB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;qBACxE,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;YACjD,CAAC;YACD,8EAA8E;YAC9E,KAAK,CAAC,WAAW,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE;gBAClC,IAAI,SAAS,KAAK,SAAS;oBAAE,OAAO,IAAI,CAAA;gBACxC,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,mBAAmB,EAAE,CAAA;gBAClD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;gBACrF,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;YAC1D,CAAC;SACF,CAAA;IACH,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC,CAAA;IAElC,kEAAkE;IAClE,MAAM,cAAc,GAAG,WAAW,CAChC,CAAC,YAAoB,EAAU,EAAE;QAC/B,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QACxE,IAAI,CAAC,GAAG,CAAC,CAAA;QACT,OAAO,QAAQ,CAAC,GAAG,CAAC,OAAO,YAAY,IAAI,CAAC,EAAE,CAAC;YAAE,CAAC,IAAI,CAAC,CAAA;QACvD,OAAO,OAAO,YAAY,IAAI,CAAC,EAAE,CAAA;IACnC,CAAC,EACD,CAAC,GAAG,CAAC,CACN,CAAA;IAED,+EAA+E;IAC/E,MAAM,cAAc,GAAG,WAAW,CAChC,KAAK,EAAE,MAAM,EAAE,EAAE;QACf,IAAI,SAAS,KAAK,SAAS;YAAE,OAAO,EAAE,CAAA;QACtC,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,mBAAmB,EAAE,CAAA;QAClD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAA;QAC9F,IAAI,CAAC,MAAM,CAAC,EAAE;YAAE,OAAO,EAAE,CAAA;QACzB,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;QACrD,MAAM,KAAK,GACT,MAAM,CAAC,KAAK,IAAI,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,EAAE,IAAI,CAAA;QAC9F,OAAO,MAAM,CAAC,KAAK;aAChB,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YACX,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;YACzC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;YACxD,OAAO,EAAE,OAAO,EAAE,CAAA;QACpB,CAAC,CAAC;aACD,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IACrD,CAAC,EACD,CAAC,SAAS,EAAE,GAAG,EAAE,YAAY,CAAC,CAC/B,CAAA;IAED,6EAA6E;IAC7E,2EAA2E;IAC3E,MAAM,YAAY,GAAG,MAAM,CAAoB,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAA;IAE7D,wEAAwE;IACxE,2DAA2D;IAC3D,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAA;IAEzE,MAAM,SAAS,GAAG,OAAO,CACvB,GAAG,EAAE,CACH,IAAI,oBAAoB,CAAC;QACvB,GAAG,EAAE,UAAU,CAAC,QAAQ;QACxB,OAAO,EAAE,kBAAkB,CAAC,UAAU,CAAC,OAAO,CAAC;QAC/C,yEAAyE;QACzE,yEAAyE;QACzE,wEAAwE;QACxE,yEAAyE;QACzE,oEAAoE;QACpE,sEAAsE;QACtE,iDAAiD;QACjD,IAAI,EAAE,KAAK,IAAI,EAAE;YACf,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,gBAAgB,EAAE,EAAE,cAAc,EAAE,CAAC,CAAA;YAC9E,YAAY,CAAC,OAAO,GAAG,SAAS,CAAA;YAChC,MAAM,OAAO,GAAG,oBAAoB,EAAE,CAAA;YACtC,OAAO;gBACL,OAAO,EAAE,gBAAgB,CAAC;oBACxB,WAAW,EAAE,GAAG,CAAC,WAAW;oBAC5B,SAAS;oBACT,GAAG,CAAC,OAAO;wBACT,CAAC,CAAC;4BACE,aAAa,EAAE;gCACb,4BAA4B,CAAC;oCAC3B,UAAU,EAAE,OAAO,CAAC,UAAU;oCAC9B,EAAE,EAAE,OAAO,CAAC,EAAE;oCACd,KAAK,EAAE,OAAO,CAAC,KAAK;oCACpB,MAAM,EAAE,YAAY;iCACrB,CAAC;6BACH;yBACF;wBACH,CAAC,CAAC,EAAE,CAAC;iBACR,CAAC;aACH,CAAA;QACH,CAAC;KACF,CAAC,EACJ,CAAC,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,GAAG,CAAC,WAAW,EAAE,YAAY,CAAC,CAC9E,CAAA;IAED,6EAA6E;IAC7E,wEAAwE;IACxE,MAAM,aAAa,GAAG,MAAM,CAAsD;QAChF,YAAY;QACZ,KAAK,EAAE,wBAAwB,EAAE;KAClC,CAAC,CAAA;IACF,IAAI,aAAa,CAAC,OAAO,CAAC,YAAY,KAAK,YAAY,EAAE,CAAC;QACxD,aAAa,CAAC,OAAO,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,wBAAwB,EAAE,EAAE,CAAA;IAC7E,CAAC;IACD,MAAM,cAAc,GAAG,aAAa,CAAC,OAAO,CAAC,KAAK,CAAA;IAElD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE;QAC5B,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAA;QAC1B,OAAO,sBAAsB,CAAC;YAC5B,QAAQ;YACR,QAAQ;YACR,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,OAAO;YACP,cAAc;YACd,UAAU,EAAE,cAAc;YAC1B,UAAU,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;YACzF,SAAS,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO;YACrC,kEAAkE;YAClE,+DAA+D;YAC/D,eAAe,EAAE,GAAG,EAAE,CAAC,oBAAoB,EAAE;YAC7C,MAAM,EAAE,YAAY;YACpB,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/D,wEAAwE;YACxE,2EAA2E;YAC3E,2EAA2E;YAC3E,gEAAgE;YAChE,eAAe,EAAE,GAAG,CAAC,eAAe;YACpC,GAAG,CAAC,iBAAiB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClE,CAAC,CAAA;IACJ,CAAC,EAAE;QACD,QAAQ;QACR,QAAQ;QACR,GAAG,CAAC,WAAW;QACf,GAAG,CAAC,YAAY;QAChB,GAAG,CAAC,eAAe;QACnB,OAAO;QACP,cAAc;QACd,YAAY;QACZ,iBAAiB;QACjB,cAAc;KACf,CAAC,CAAA;IAEF,MAAM,IAAI,GAAG,OAAO,CAAC;QACnB,uEAAuE;QACvE,EAAE,EAAE,oBAAoB,YAAY,EAAE;QACtC,SAAS;QACT,UAAU,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;YACjC,IAAI,CAAC,QAAQ;gBAAE,OAAM;YACrB,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC;gBACrC,UAAU,EAAE,QAAQ,CAAC,UAAU;gBAC/B,QAAQ,EAAE,QAAQ,CAAC,QAAQ;gBAC3B,KAAK,EAAE,QAAQ,CAAC,KAAK;aACtB,CAAC,CAAA;YACF,uEAAuE;YACvE,kEAAkE;YAClE,sEAAsE;YACtE,IAAI,OAAO,CAAC,EAAE,EAAE,CAAC;gBACf,KAAK,IAAI,CAAC,aAAa,CAAC;oBACtB,IAAI,EAAE,QAAQ,CAAC,QAAQ;oBACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;oBAC/B,MAAM,EAAE,OAAO,CAAC,MAAM;iBACvB,CAAC,CAAA;YACJ,CAAC;iBAAM,CAAC;gBACN,KAAK,IAAI,CAAC,aAAa,CAAC;oBACtB,KAAK,EAAE,cAAc;oBACrB,IAAI,EAAE,QAAQ,CAAC,QAAQ;oBACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;oBAC/B,SAAS,EAAE,OAAO,CAAC,SAAS;iBAC7B,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QACD,2EAA2E;QAC3E,qEAAqE;QACrE,qBAAqB,EAAE,WAAW,CAAC,2CAA2C,EAAE;YAC9E,QAAQ,EAAE,iBAAiB;YAC3B,KAAK,EAAE,GAAG,EAAE,CACV,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,kBAAkB,EAAE,EAAE,GAAG,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAC;SACjG,CAAC;QACF,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,gBAAgB,CAAC,EAAE,CAAC;KAChF,CAAC,CAAA;IAEF,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,KAAK,WAAW,IAAI,IAAI,CAAC,MAAM,KAAK,WAAW,CAAA;IAEvE,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE;QAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;QACzB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK;YAAE,OAAM;QAC/C,QAAQ,CAAC,EAAE,CAAC,CAAA;QACZ,QAAQ,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAA;QACjC,KAAK,IAAI,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,CAAC,CAAA;IACjC,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAA;IAE9B,wEAAwE;IACxE,kFAAkF;IAClF,MAAM,oBAAoB,GAAG,WAAW,CAAC,GAAG,EAAE;QAC5C,KAAK,iBAAiB,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE;YACpE,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;YAC3B,QAAQ,CAAC,EAAE,CAAC,CAAA;YACZ,eAAe,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,GAAG,CAAC,CAAC,CAAA;QAC3C,CAAC,CAAC,CAAA;IACJ,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IAE5B,MAAM,aAAa,GAAG,WAAW,CAC/B,KAAK,EAAE,QAAwB,EAAE,EAAE;QACjC,IAAI,SAAS,KAAK,SAAS;YAAE,OAAM;QACnC,yEAAyE;QACzE,4EAA4E;QAC5E,4CAA4C;QAC5C,MAAM,QAAQ,GAAG,GAAG,CAAC,WAAW,CAAC,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,EAAE,EAAE,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;QAC9F,MAAM,GAAG,GAAgB;YACvB,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC;YACnB,UAAU,EAAE,QAAQ,CAAC,UAAU;YAC/B,EAAE,EAAE,QAAQ,CAAC,UAAU;YACvB,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM;YAC7B,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC7E,CAAA;QACD,gFAAgF;QAChF,2EAA2E;QAC3E,+EAA+E;QAC/E,2EAA2E;QAC3E,8EAA8E;QAC9E,+EAA+E;QAC/E,MAAM,UAAU,GAAG,gBAAgB,CACjC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,EACtC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAClB,CAAA;QACD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,QAAQ,CAAC;gBACP,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,SAAS,CAAC,uBAAuB,EAAE;oBAC1C,MAAM,EAAE,UAAU;yBACf,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;yBACX,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;yBAC7B,IAAI,CAAC,IAAI,CAAC;iBACd,CAAC;aACH,CAAC,CAAA;YACF,OAAM;QACR,CAAC;QACD,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,mBAAmB,EAAE,CAAA;QAClD,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAA;QAChF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAA;YACtE,OAAM;QACR,CAAC;QACD,0EAA0E;QAC1E,6EAA6E;QAC7E,8EAA8E;QAC9E,IAAI,CAAC;YACH,MAAM,UAAU,CAAC,SAAS,CAAC,QAAQ,CAAC;gBAClC,SAAS;gBACT,GAAG,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,EAAE,EAAE,EAAE,QAAQ,CAAC,UAAU,EAAE;gBACjE,KAAK,EAAE,SAAS,CAAC,6BAA6B,CAAC;gBAC/C,MAAM,EAAE,WAAW;aACpB,CAAC,CAAA;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,kCAAkC;QACpC,CAAC;QACD,4EAA4E;QAC5E,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAA;QACrB,6EAA6E;QAC7E,4EAA4E;QAC5E,4EAA4E;QAC5E,MAAM,OAAO,GAAG,oBAAoB,EAAE,CAAA;QACtC,IACE,OAAO;YACP,OAAO,CAAC,UAAU,KAAK,QAAQ,CAAC,UAAU;YAC1C,OAAO,CAAC,EAAE,KAAK,QAAQ,CAAC,UAAU,EAClC,CAAC;YACD,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;QACzB,CAAC;QACD,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAA;IACtD,CAAC,EACD,CAAC,SAAS,EAAE,GAAG,CAAC,CACjB,CAAA;IAED,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,QAAwB,EAAE,EAAE;QAC3B,IAAI,SAAS,KAAK,SAAS;YAAE,OAAM;QACnC,KAAK,QAAQ,CAAC;YACZ,EAAE,EAAE,oBAAoB;YACxB,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,WAAW,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE;SAChG,CAAC,CAAA;IACJ,CAAC,EACD,CAAC,QAAQ,EAAE,SAAS,CAAC,CACtB,CAAA;IAED,MAAM,WAAW,GAAG,WAAW,CAC7B,CAAC,UAAkB,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,SAAS,EAClE,CAAC,MAAM,CAAC,SAAS,CAAC,CACnB,CAAA;IAED,MAAM,sBAAsB,GAAG,CAAC,OAAkB,EAAE,EAAE;QACpD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,OAAO,CAAC,KAA0B,EAAE,WAAW,CAAC,CAAA;QACrF,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;YACrC,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC5B,OAAO,CACL,KAAC,aAAa,cACZ,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,SAAS,EAAC,iCAAiC,YACxD,OAAO,CAAC,IAAI,GACR,IAHW,GAAG,OAAO,CAAC,EAAE,IAAI,KAAK,EAAE,CAI5B,CACjB,CAAA;YACH,CAAC;YACD,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBAC7B,OAAO,CACL,KAAC,aAAa,cACZ,KAAC,UAAU,IAAC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,GAAI,IADxC,GAAG,OAAO,CAAC,EAAE,IAAI,KAAK,EAAE,CAE5B,CACjB,CAAA;YACH,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;YACrD,IAAI,CAAC,QAAQ;gBAAE,OAAO,IAAI,CAAA;YAC1B,OAAO,CACL,KAAC,YAAY,IAEX,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,aAAa,CAAC,QAAQ,CAAC,EAC3C,SAAS,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,EACtE,MAAM,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,IAJ/B,OAAO,CAAC,UAAU,CAKvB,CACH,CAAA;QACH,CAAC,CAAC,CAAA;IACJ,CAAC,CAAA;IAED,OAAO,CACL,iBAAO,SAAS,EAAC,gFAAgF,aAC/F,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,KAAK,EAAC,QAAQ,EAAC,SAAS,EAAC,2CAA2C,aAC/E,KAAC,OAAO,IAAC,KAAK,EAAE,CAAC,EAAE,IAAI,EAAC,IAAI,YACzB,SAAS,CAAC,gBAAgB,CAAC,GACpB,EACV,KAAC,MAAM,KAAG,EACV,KAAC,MAAM,IACL,IAAI,EAAC,SAAS,EACd,OAAO,EAAC,OAAO,EACf,OAAO,EAAE,oBAAoB,EAC7B,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,IAAI,EACtE,KAAK,EAAE,SAAS,CAAC,0BAA0B,CAAC,gBAChC,SAAS,CAAC,0BAA0B,CAAC,YAEjD,KAAC,iBAAiB,KAAG,GACd,EACT,KAAC,MAAM,IACL,IAAI,EAAC,SAAS,EACd,OAAO,EAAC,OAAO,EACf,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,EACpC,KAAK,EAAE,SAAS,CAAC,gBAAgB,CAAC,gBACtB,SAAS,CAAC,gBAAgB,CAAC,YAEvC,KAAC,CAAC,KAAG,GACE,IACL,EAEN,MAAC,YAAY,IAAC,WAAW,EAAE,IAAI,CAAC,QAAQ,aACrC,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAC,iBAAiB,KAAG,CAAC,CAAC,CAAC,IAAI,EACzD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAC7B,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CACxB,KAAC,QAAQ,IAAkB,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,IAAtC,OAAO,CAAC,EAAE,CAAgC,CAC1D,CAAC,CAAC,CAAC,CACF,cAAsB,SAAS,EAAC,UAAU,YACvC,sBAAsB,CAAC,OAAO,CAAC,IADxB,OAAO,CAAC,EAAE,CAEd,CACP,CACF,EACA,IAAI,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,CAC7B,KAAC,aAAa,cACZ,KAAC,gBAAgB,KAAG,GACN,CACjB,CAAC,CAAC,CAAC,IAAI,EACP,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,CACvB,KAAC,aAAa,cACZ,KAAC,aAAa,IAAC,OAAO,EAAE,MAAM,CAAC,KAAK,GAAI,GAC1B,CACjB,CAAC,CAAC,CAAC,IAAI,IACK,EAEd,KAAK,CAAC,CAAC,CAAC,CACP,KAAC,WAAW,IAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,GAAI,CACpF,CAAC,CAAC,CAAC,CACF,cAAK,SAAS,EAAC,4BAA4B,YACzC,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,YACzB,SAAS,CAAC,sBAAsB,CAAC,GAC7B,GACH,CACP,IACK,CACT,CAAA;AACH,CAAC","sourcesContent":["import { useCallback, useMemo, useReducer, useRef, useState } from 'react'\nimport { MessageSquarePlus, X } from 'lucide-react'\nimport { DefaultChatTransport, lastAssistantMessageIsCompleteWithToolCalls } from 'ai'\nimport type { UIMessage } from 'ai'\nimport { useChat } from '@ai-sdk/react'\nimport {\n createCollectionRegistry,\n resolveDocumentTitle,\n} from '@elytracms/core/cms-core'\nimport type { CmsDocument, FieldValues } from '@elytracms/core/cms-core'\nimport { Button, Heading, Row, Spacer, Text } from '@elytracms/studio/ui'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { STUDIO_PROJECT_ROUTE, studioPath, useNavigate } from '@elytracms/studio/router'\nimport { resolveDeploymentAi } from '@elytracms/studio/lib/workspace'\nimport { getStudioOperations } from '@elytracms/studio/lib/operations'\nimport {\n DEFAULT_MAX_STEPS,\n buildChatContext,\n chatReviewReducer,\n clearConversation,\n createChatCandidateStore,\n createChatToolExecutor,\n initialChatReviewState,\n introducedErrors,\n renderCurrentDocumentSection,\n resolveAiConfig,\n resolveChatTarget,\n resolveKnowledge,\n withStepCap,\n} from '@elytracms/studio/lib/ai'\nimport { getAiCurrentDocument } from '@elytracms/studio/lib/ai-current-document'\nimport type {\n ChatCandidateStore,\n ChatToolReaders,\n KnowledgeCollectionReader,\n ProposalReview,\n ResolvedKnowledge,\n RouteHeaders,\n} from '@elytracms/studio/lib/ai'\nimport { useCms } from '../cms/cms-store'\nimport {\n AssistantTurn,\n ChatErrorNote,\n Conversation,\n ConversationEmpty,\n PromptInput,\n ToolStatus,\n UserTurn,\n WorkingIndicator,\n describeToolPart,\n messageText,\n segmentMessageParts,\n} from './chat-ui'\nimport type { MessagePartView } from './chat-ui'\nimport { ProposalCard } from './proposal-card'\nimport { setAiPanelOpen } from '@elytracms/studio/lib/ai-panel'\n\n/**\n * The AI assistant chat panel (EC-199, moved onto the SDK's native protocol by\n * EC-334) — the right-hand collapsible surface in the CMS area. `useChat` +\n * `DefaultChatTransport` own the conversation against the route's native chat\n * branch (visible token streaming); `onToolCall` dispatches into the client\n * executor map (operations reads + terminal engines, all with the user's own\n * session); `sendAutomaticallyWhen` is the SDK predicate wrapped with the\n * per-turn step cap. What the SDK does not own lives in the pure review reducer:\n * full proposals keyed by `toolCallId` (reviewed as a diff, applied as a DRAFT\n * via operations upsert + a provenance-tagged snapshot — the EC-199 trust\n * boundary, unmoved) and blocking errors. EC-380: the \"new conversation\"\n * affordance clears busy-safely (stop → new chat id + review reset; pending\n * proposals are discarded with reject semantics).\n *\n * NEVER a modal (house rule). The whole module (including `ai` + `@ai-sdk/react`)\n * sits behind the lazy boundary (`ai-chat-panel-lazy`) — pinned in bundle tests.\n */\n\n/** Adapt a `RouteHeaders` (static or thunk) to the transport's headers resolvable. */\nfunction toTransportHeaders(headers: RouteHeaders | undefined): () => Promise<Record<string, string>> {\n return async () => {\n const resolved = typeof headers === 'function' ? await headers() : headers\n const record: Record<string, string> = {}\n new Headers(resolved ?? {}).forEach((value, key) => {\n record[key] = value\n })\n return record\n }\n}\n\nexport function AiChatPanel() {\n const cms = useCms()\n const navigate = useNavigate()\n const [review, dispatch] = useReducer(chatReviewReducer, initialChatReviewState)\n const [input, setInput] = useState('')\n // EC-380: the conversation counter — bumping it mints a fresh `useChat` id\n // (a brand-new conversation) and a fresh relation-candidate store.\n const [conversation, setConversation] = useState(1)\n\n const projectId = cms.projectId\n const activeLocale = cms.activeLocale\n\n // Resolve the AI platform config once (presence + capability gate). The panel only\n // mounts when configured, but a misconfigured provider still fails loud here.\n const resolved = useMemo(() => resolveAiConfig(resolveDeploymentAi()), [])\n const provider = resolved.resolved?.provider ?? null\n const knowledgeSources = useMemo(() => resolved.resolved?.knowledge ?? [], [resolved])\n const maxRepairAttempts = resolved.resolved?.limits?.maxRepairAttempts\n const ready = provider !== null && resolved.ok && projectId !== undefined\n\n const registry = useMemo(() => createCollectionRegistry(cms.collections), [cms.collections])\n\n /** Merge a document's non-localized + active-locale values for title/prose reads. */\n const localeValues = useCallback(\n (doc: CmsDocument): FieldValues => ({\n ...doc.values,\n ...(doc.localized?.[activeLocale] ?? {}),\n }),\n [activeLocale],\n )\n\n /** Operations-backed tool readers — client-side, with the user's own session. */\n const readers = useMemo<ChatToolReaders>(() => {\n const matches = (query: string | undefined, ...fields: (string | undefined)[]) =>\n !query || fields.some((f) => f?.toLowerCase().includes(query.toLowerCase()))\n return {\n async searchDocuments({ collection, query }) {\n if (projectId === undefined) return []\n const { operations } = await getStudioOperations()\n const result = await operations.documents.list({ projectId, collection })\n if (!result.ok) return []\n const target = cms.getCollection(collection)\n return result.value\n .map((rec) => ({\n id: rec.document.id,\n title: target\n ? resolveDocumentTitle(target, localeValues(rec.document), rec.document.id)\n : rec.document.id,\n }))\n .filter((r) => matches(query, r.title, r.id))\n },\n async listAssets({ query }) {\n if (projectId === undefined) return []\n const { operations } = await getStudioOperations()\n const result = await operations.assets.list({ projectId })\n if (!result.ok) return []\n return result.value\n .map((asset) => ({ id: asset.id, title: asset.title ?? asset.filename }))\n .filter((r) => matches(query, r.title, r.id))\n },\n // Full document (draft values + localized) — the getDocument/translate tools.\n async getDocument({ collection, id }) {\n if (projectId === undefined) return null\n const { operations } = await getStudioOperations()\n const result = await operations.documents.get({ projectId, ref: { collection, id } })\n return result.ok ? result.value?.document ?? null : null\n },\n }\n }, [projectId, cms, localeValues])\n\n /** A fresh, collision-free draft id for the target collection. */\n const mintDocumentId = useCallback(\n (collectionId: string): string => {\n const existing = new Set(cms.documentsOf(collectionId).map((d) => d.id))\n let n = 1\n while (existing.has(`new-${collectionId}-${n}`)) n += 1\n return `new-${collectionId}-${n}`\n },\n [cms],\n )\n\n /** Read `fromCollection(...)` knowledge prose through the operations layer. */\n const readCollection = useCallback<KnowledgeCollectionReader>(\n async (source) => {\n if (projectId === undefined) return []\n const { operations } = await getStudioOperations()\n const result = await operations.documents.list({ projectId, collection: source.collectionId })\n if (!result.ok) return []\n const target = cms.getCollection(source.collectionId)\n const field =\n source.field ?? target?.fields.find((f) => f.type === 'text' || f.type === 'richText')?.name\n return result.value\n .map((rec) => {\n const values = localeValues(rec.document)\n const content = field ? String(values[field] ?? '') : ''\n return { content }\n })\n .filter((item) => item.content.trim().length > 0)\n },\n [projectId, cms, localeValues],\n )\n\n // The knowledge snapshot the executors read — refreshed by the body thunk on\n // every request, so the terminal engines see what the model saw this turn.\n const knowledgeRef = useRef<ResolvedKnowledge>({ items: [] })\n\n // The chat transport target: the configured route provider's endpoint +\n // session-header thunk (EC-371), re-evaluated per request.\n const chatTarget = useMemo(() => resolveChatTarget(provider), [provider])\n\n const transport = useMemo(\n () =>\n new DefaultChatTransport({\n api: chatTarget.endpoint,\n headers: toTransportHeaders(chatTarget.headers),\n // The `body` THUNK — re-evaluated per request (fresh context every turn,\n // continuations included): collections-catalog summary + client-resolved\n // knowledge + EC-378's current-document block (read from the seam store\n // at REQUEST time, so every turn sees what is open NOW). The route folds\n // these sections into the system prompt; it resolves no data itself\n // (zero-data-access proxy). The block is advisory — the deterministic\n // omitted-target fallback lives in the executor.\n body: async () => {\n const knowledge = await resolveKnowledge(knowledgeSources, { readCollection })\n knowledgeRef.current = knowledge\n const current = getAiCurrentDocument()\n return {\n context: buildChatContext({\n collections: cms.collections,\n knowledge,\n ...(current\n ? {\n extraSections: [\n renderCurrentDocumentSection({\n collection: current.collection,\n id: current.id,\n title: current.title,\n locale: activeLocale,\n }),\n ],\n }\n : {}),\n }),\n }\n },\n }),\n [chatTarget, knowledgeSources, readCollection, cms.collections, activeLocale],\n )\n\n // Per-conversation relation-candidate store (EC-380: replaced on clear, so a\n // fresh conversation cannot reference the previous one's searched ids).\n const candidatesRef = useRef<{ conversation: number; store: ChatCandidateStore }>({\n conversation,\n store: createChatCandidateStore(),\n })\n if (candidatesRef.current.conversation !== conversation) {\n candidatesRef.current = { conversation, store: createChatCandidateStore() }\n }\n const candidateStore = candidatesRef.current.store\n\n const executor = useMemo(() => {\n if (!provider) return null\n return createChatToolExecutor({\n provider,\n registry,\n collections: cms.collections,\n readers,\n mintDocumentId,\n candidates: candidateStore,\n onProposal: (toolCallId, proposal) => dispatch({ type: 'propose', toolCallId, proposal }),\n knowledge: () => knowledgeRef.current,\n // EC-378: the current-document seam, read at EXECUTION time — the\n // deterministic fallback when the model omits a tool's target.\n currentDocument: () => getAiCurrentDocument(),\n locale: activeLocale,\n ...(cms.localeConfig ? { localeConfig: cms.localeConfig } : {}),\n // EC-473 established that the assistant must validate with the EDITOR's\n // rules, not a subset. EC-497 hands over THE bag itself — one value, built\n // once by `gateValidateOptions` — so the assistant's gate and the editor's\n // gate cannot drift by someone forgetting a key at a call site.\n validateOptions: cms.validateOptions,\n ...(maxRepairAttempts !== undefined ? { maxRepairAttempts } : {}),\n })\n }, [\n provider,\n registry,\n cms.collections,\n cms.localeConfig,\n cms.validateOptions,\n readers,\n mintDocumentId,\n activeLocale,\n maxRepairAttempts,\n candidateStore,\n ])\n\n const chat = useChat({\n // A new conversation is a new chat id — `useChat` recreates its state.\n id: `elytra-assistant-${conversation}`,\n transport,\n onToolCall: async ({ toolCall }) => {\n if (!executor) return\n const outcome = await executor.execute({\n toolCallId: toolCall.toolCallId,\n toolName: toolCall.toolName,\n input: toolCall.input,\n })\n // Deliberately NOT awaited: `onToolCall` runs inside the chat's serial\n // stream job, and `addToolOutput` enqueues on the same executor —\n // awaiting it here deadlocks the stream (verified against ai@7.0.22).\n if (outcome.ok) {\n void chat.addToolOutput({\n tool: toolCall.toolName,\n toolCallId: toolCall.toolCallId,\n output: outcome.output,\n })\n } else {\n void chat.addToolOutput({\n state: 'output-error',\n tool: toolCall.toolName,\n toolCallId: toolCall.toolCallId,\n errorText: outcome.errorText,\n })\n }\n },\n // The SDK predicate wrapped with the per-user-turn step cap: a capped turn\n // stops cleanly with a visible note — never a silent stall (EC-334).\n sendAutomaticallyWhen: withStepCap(lastAssistantMessageIsCompleteWithToolCalls, {\n maxSteps: DEFAULT_MAX_STEPS,\n onCap: () =>\n dispatch({ type: 'fail', message: translate('ai.error.stepCap', { max: DEFAULT_MAX_STEPS }) }),\n }),\n onError: () => dispatch({ type: 'fail', message: translate('ai.error.route') }),\n })\n\n const busy = chat.status === 'submitted' || chat.status === 'streaming'\n\n const submit = useCallback(() => {\n const text = input.trim()\n if (text.length === 0 || busy || !ready) return\n setInput('')\n dispatch({ type: 'clear-error' })\n void chat.sendMessage({ text })\n }, [input, busy, ready, chat])\n\n // EC-380: clear / new topic — busy-safe (stop first), pending proposals\n // discarded with reject semantics (the review state resets; nothing was applied).\n const startNewConversation = useCallback(() => {\n void clearConversation({ status: chat.status, stop: chat.stop }, () => {\n dispatch({ type: 'clear' })\n setInput('')\n setConversation((current) => current + 1)\n })\n }, [chat.status, chat.stop])\n\n const applyProposal = useCallback(\n async (proposal: ProposalReview) => {\n if (projectId === undefined) return\n // For an update/translate of an EXISTING document, spread the stored doc\n // first so document-level extras (alias, defaultLocale) survive the apply —\n // the patch replaces only values/localized.\n const existing = cms.getDocument({ collection: proposal.collection, id: proposal.documentId })\n const doc: CmsDocument = {\n ...(existing ?? {}),\n collection: proposal.collection,\n id: proposal.documentId,\n values: proposal.patch.values,\n ...(proposal.patch.localized ? { localized: proposal.patch.localized } : {}),\n }\n // EC-473 — re-validate BEFORE the write. The proposal was validated when it was\n // built, but the editor may have changed the document since (or applied an\n // earlier proposal on top of it), so the patch is re-judged against what is in\n // the session NOW. Same gate as every other AI write path: only errors the\n // apply would INTRODUCE block it — pre-existing brokenness on the document is\n // not the assistant's doing and must not make an unrelated change unappliable.\n const introduced = introducedErrors(\n existing ? cms.validate(existing) : [],\n cms.validate(doc),\n )\n if (introduced.length > 0) {\n dispatch({\n type: 'fail',\n message: translate('ai.error.applyInvalid', {\n issues: introduced\n .slice(0, 3)\n .map((issue) => issue.message)\n .join('; '),\n }),\n })\n return\n }\n const { operations } = await getStudioOperations()\n const upserted = await operations.documents.upsert({ projectId, document: doc })\n if (!upserted.ok) {\n dispatch({ type: 'fail', message: translate('ai.error.applyFailed') })\n return\n }\n // Provenance: record an AI-tagged version snapshot (a restore point + the\n // 'assistant' origin surfaced in the versions pane). Best-effort — the draft\n // already landed above; a snapshot failure must not lose the applied content.\n try {\n await operations.documents.snapshot({\n projectId,\n ref: { collection: proposal.collection, id: proposal.documentId },\n label: translate('cms.version.aiAuthoredTitle'),\n origin: 'assistant',\n })\n } catch {\n // ignore — provenance is additive\n }\n // Reflect in the session so the new draft shows in the list + is navigable.\n cms.saveDocument(doc)\n // EC-378: when the applied proposal targets the document OPEN in the editor,\n // adopt it into the editor's draft buffer — otherwise the editor's autosave\n // would write its stale buffer back and silently revert the applied change.\n const current = getAiCurrentDocument()\n if (\n current &&\n current.collection === proposal.collection &&\n current.id === proposal.documentId\n ) {\n current.adoptDraft(doc)\n }\n dispatch({ type: 'apply', toolCallId: proposal.id })\n },\n [projectId, cms],\n )\n\n const openProposal = useCallback(\n (proposal: ProposalReview) => {\n if (projectId === undefined) return\n void navigate({\n to: STUDIO_PROJECT_ROUTE,\n params: { projectId, _splat: studioPath.cmsDocument(proposal.collection, proposal.documentId) },\n })\n },\n [navigate, projectId],\n )\n\n const hasProposal = useCallback(\n (toolCallId: string) => review.proposals[toolCallId] !== undefined,\n [review.proposals],\n )\n\n const renderAssistantMessage = (message: UIMessage) => {\n const segments = segmentMessageParts(message.parts as MessagePartView[], hasProposal)\n return segments.map((segment, index) => {\n if (segment.kind === 'text') {\n return (\n <AssistantTurn key={`${message.id}-${index}`}>\n <Text size=\"sm\" className=\"whitespace-pre-wrap break-words\">\n {segment.text}\n </Text>\n </AssistantTurn>\n )\n }\n if (segment.kind === 'tools') {\n return (\n <AssistantTurn key={`${message.id}-${index}`}>\n <ToolStatus steps={segment.parts.map(describeToolPart)} />\n </AssistantTurn>\n )\n }\n const proposal = review.proposals[segment.toolCallId]\n if (!proposal) return null\n return (\n <ProposalCard\n key={segment.toolCallId}\n proposal={proposal}\n onApply={() => void applyProposal(proposal)}\n onDiscard={() => dispatch({ type: 'reject', toolCallId: proposal.id })}\n onOpen={() => openProposal(proposal)}\n />\n )\n })\n }\n\n return (\n <aside className=\"flex h-full min-h-0 w-[360px] shrink-0 flex-col border-l border-border bg-card\">\n <Row gap=\"2\" align=\"center\" className=\"h-12 shrink-0 border-b border-border px-3\">\n <Heading level={2} size=\"h4\">\n {translate('ai.panel.title')}\n </Heading>\n <Spacer />\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n onClick={startNewConversation}\n disabled={chat.messages.length === 0 && review.error === null && !busy}\n title={translate('ai.panel.newConversation')}\n aria-label={translate('ai.panel.newConversation')}\n >\n <MessageSquarePlus />\n </Button>\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n onClick={() => setAiPanelOpen(false)}\n title={translate('ai.panel.close')}\n aria-label={translate('ai.panel.close')}\n >\n <X />\n </Button>\n </Row>\n\n <Conversation atBottomKey={chat.messages}>\n {chat.messages.length === 0 ? <ConversationEmpty /> : null}\n {chat.messages.map((message) =>\n message.role === 'user' ? (\n <UserTurn key={message.id} text={messageText(message)} />\n ) : (\n <div key={message.id} className=\"contents\">\n {renderAssistantMessage(message)}\n </div>\n ),\n )}\n {chat.status === 'submitted' ? (\n <AssistantTurn>\n <WorkingIndicator />\n </AssistantTurn>\n ) : null}\n {review.error !== null ? (\n <AssistantTurn>\n <ChatErrorNote message={review.error} />\n </AssistantTurn>\n ) : null}\n </Conversation>\n\n {ready ? (\n <PromptInput value={input} onChange={setInput} onSubmit={submit} disabled={busy} />\n ) : (\n <div className=\"border-t border-border p-3\">\n <Text size=\"xs\" tone=\"muted\">\n {translate('ai.panel.unavailable')}\n </Text>\n </div>\n )}\n </aside>\n )\n}\n"]}
@@ -13,6 +13,33 @@ import type { CollectionViewSource } from '@elytracms/studio/lib/collection-view
13
13
  * {@link ProposalCard} is the CMS-wired shell; {@link ProposalCardView} is the
14
14
  * presentational card over an injected collection + view source (node-smoked
15
15
  * with `renderToStaticMarkup`, house pattern).
16
+ *
17
+ * ## EC-496 — the proposal's own findings
18
+ *
19
+ * The diff answers *what changes*; {@link ProposalIssues} answers *what the engine
20
+ * flagged about it*, immediately above the Apply/Discard row so it is read while the
21
+ * decision is being made. Two calls made here, both deliberate:
22
+ *
23
+ * **An error on the card does NOT block apply.** Grounded before deciding
24
+ * (`file:line`, read 2026-08-01): every producer already filters this list to
25
+ * non-error findings (`lib/ai/apply-content.ts:259`, `lib/ai/update-fields.ts:442`,
26
+ * `lib/ai/tools.ts:764`), so a card-blocking gate would be dead code today. More
27
+ * importantly the real gate already exists one layer up and is strictly better
28
+ * placed: EC-473 made apply RE-VALIDATE against the session as it is NOW
29
+ * (`ai-chat-panel.tsx:371`) and refuse with `ai.error.applyInvalid` when the write
30
+ * would INTRODUCE an error. A build-time snapshot cannot see an edit the editor made
31
+ * since, and gating on pre-existing brokenness is exactly what EC-473 argued makes an
32
+ * assistant unusable on real content. So this surface REPORTS truthfully — errors
33
+ * render as errors and sort first ({@link orderedProposalIssues}) — and the gate
34
+ * stays where it can see the truth.
35
+ *
36
+ * **`attempts` is not rendered** (the EC-496 scope-2 owner call). The repair-loop
37
+ * count is provenance about the MODEL, not about the content: a proposal repaired on
38
+ * the second attempt passed the identical validation gate as a first-shot one, so the
39
+ * number changes nothing an editor can act on — and spending sidebar space plus a new
40
+ * catalog string on it would trade a real signal (the findings) for a trivia line. The
41
+ * field stays on `ChatProposal` (it is deterministic, cheap, and the natural input for
42
+ * a future AI-diagnostics surface); it simply has no place on the editor's card.
16
43
  */
17
44
  /** The card's origin-dependent captions (title + diff column headers). */
18
45
  export declare function proposalCaptions(origin: ProposalOrigin, collectionLabel: string): {
@@ -10,6 +10,7 @@ import { useCms } from '../cms/cms-store.js';
10
10
  import { useCmsCollectionViewSource } from '../collection-view/cms-collection-view-source.js';
11
11
  import { CollectionViewProvider } from '../collection-view/collection-view-context.js';
12
12
  import { DocumentDiffView } from '../cms/document-diff-view.js';
13
+ import { ProposalIssues } from './proposal-issues.js';
13
14
  /**
14
15
  * The AI proposal review (EC-199, extended by EC-332/EC-378): a valid,
15
16
  * leash-compliant proposal the editor reviews as a DIFF (the shared EC-317
@@ -22,6 +23,33 @@ import { DocumentDiffView } from '../cms/document-diff-view.js';
22
23
  * {@link ProposalCard} is the CMS-wired shell; {@link ProposalCardView} is the
23
24
  * presentational card over an injected collection + view source (node-smoked
24
25
  * with `renderToStaticMarkup`, house pattern).
26
+ *
27
+ * ## EC-496 — the proposal's own findings
28
+ *
29
+ * The diff answers *what changes*; {@link ProposalIssues} answers *what the engine
30
+ * flagged about it*, immediately above the Apply/Discard row so it is read while the
31
+ * decision is being made. Two calls made here, both deliberate:
32
+ *
33
+ * **An error on the card does NOT block apply.** Grounded before deciding
34
+ * (`file:line`, read 2026-08-01): every producer already filters this list to
35
+ * non-error findings (`lib/ai/apply-content.ts:259`, `lib/ai/update-fields.ts:442`,
36
+ * `lib/ai/tools.ts:764`), so a card-blocking gate would be dead code today. More
37
+ * importantly the real gate already exists one layer up and is strictly better
38
+ * placed: EC-473 made apply RE-VALIDATE against the session as it is NOW
39
+ * (`ai-chat-panel.tsx:371`) and refuse with `ai.error.applyInvalid` when the write
40
+ * would INTRODUCE an error. A build-time snapshot cannot see an edit the editor made
41
+ * since, and gating on pre-existing brokenness is exactly what EC-473 argued makes an
42
+ * assistant unusable on real content. So this surface REPORTS truthfully — errors
43
+ * render as errors and sort first ({@link orderedProposalIssues}) — and the gate
44
+ * stays where it can see the truth.
45
+ *
46
+ * **`attempts` is not rendered** (the EC-496 scope-2 owner call). The repair-loop
47
+ * count is provenance about the MODEL, not about the content: a proposal repaired on
48
+ * the second attempt passed the identical validation gate as a first-shot one, so the
49
+ * number changes nothing an editor can act on — and spending sidebar space plus a new
50
+ * catalog string on it would trade a real signal (the findings) for a trivia line. The
51
+ * field stays on `ChatProposal` (it is deterministic, cheap, and the natural input for
52
+ * a future AI-diagnostics surface); it simply has no place on the editor's card.
25
53
  */
26
54
  /** The card's origin-dependent captions (title + diff column headers). */
27
55
  export function proposalCaptions(origin, collectionLabel) {
@@ -73,7 +101,7 @@ export function ProposalCardView({ proposal, collection, source, onApply, onDisc
73
101
  // roomy full-height sheet is offered for a large proposal (never a modal form).
74
102
  const [reviewOpen, setReviewOpen] = useState(false);
75
103
  const reviewable = diff !== null && source !== null;
76
- return (_jsxs(Stack, { gap: "2", className: "overflow-hidden rounded-xl border border-border bg-card", children: [_jsxs(Row, { gap: "2", align: "center", className: "border-b border-border/60 bg-muted/30 px-3 py-2", children: [_jsx(Sparkles, { className: "size-4 shrink-0 text-primary" }), _jsx(Text, { size: "sm", weight: "medium", className: "min-w-0 truncate", children: captions.title }), _jsx(Spacer, {}), applied ? _jsx(Badge, { variant: "success", children: translate('ai.proposal.applied') }) : null, rejected ? _jsx(Badge, { variant: "outline", children: translate('ai.proposal.discarded') }) : null, reviewable ? (_jsx(Button, { size: "icon-sm", variant: "ghost", onClick: () => setReviewOpen(true), title: translate('ai.proposal.expandReview'), "aria-label": translate('ai.proposal.expandReview'), children: _jsx(Maximize2, {}) })) : null] }), _jsx("div", { className: "px-3", children: reviewable ? (_jsx(CollectionViewProvider, { source: source, children: _jsx(DocumentDiffView, { diff: diff, collection: collection, columnHeaders: columnHeaders, layout: "stacked" }) })) : (_jsx(Text, { size: "xs", tone: "muted", className: "py-2", children: translate('ai.proposal.unavailable') })) }), _jsx(Row, { gap: "2", align: "center", className: "border-t border-border/60 px-3 py-2", children: proposal.status === 'pending' ? (_jsxs(_Fragment, { children: [_jsx(Text, { size: "xs", tone: "muted", className: "min-w-0 flex-1", children: translate('ai.proposal.reviewHint') }), _jsxs(Button, { size: "sm", variant: "ghost", onClick: onDiscard, children: [_jsx(X, {}), " ", translate('ai.proposal.discard')] }), _jsxs(Button, { size: "sm", onClick: onApply, children: [_jsx(Check, {}), " ", translate('ai.proposal.apply')] })] })) : applied ? (_jsxs(_Fragment, { children: [_jsx(Spacer, {}), _jsxs(Button, { size: "sm", variant: "outline", onClick: onOpen, children: [_jsx(ArrowUpRight, {}), " ", translate('ai.proposal.open')] })] })) : (_jsx(Text, { size: "xs", tone: "muted", children: translate('ai.proposal.discardedNote') })) }), reviewable && reviewOpen ? (_jsx(ProposalReviewSheet, { title: captions.title, diff: diff, collection: collection, source: source, columnHeaders: columnHeaders, status: proposal.status, onApply: onApply, onDiscard: onDiscard, onClose: () => setReviewOpen(false) })) : null] }));
104
+ return (_jsxs(Stack, { gap: "2", className: "overflow-hidden rounded-xl border border-border bg-card", children: [_jsxs(Row, { gap: "2", align: "center", className: "border-b border-border/60 bg-muted/30 px-3 py-2", children: [_jsx(Sparkles, { className: "size-4 shrink-0 text-primary" }), _jsx(Text, { size: "sm", weight: "medium", className: "min-w-0 truncate", children: captions.title }), _jsx(Spacer, {}), applied ? _jsx(Badge, { variant: "success", children: translate('ai.proposal.applied') }) : null, rejected ? _jsx(Badge, { variant: "outline", children: translate('ai.proposal.discarded') }) : null, reviewable ? (_jsx(Button, { size: "icon-sm", variant: "ghost", onClick: () => setReviewOpen(true), title: translate('ai.proposal.expandReview'), "aria-label": translate('ai.proposal.expandReview'), children: _jsx(Maximize2, {}) })) : null] }), _jsx("div", { className: "px-3", children: reviewable ? (_jsx(CollectionViewProvider, { source: source, children: _jsx(DocumentDiffView, { diff: diff, collection: collection, columnHeaders: columnHeaders, layout: "stacked" }) })) : (_jsx(Text, { size: "xs", tone: "muted", className: "py-2", children: translate('ai.proposal.unavailable') })) }), _jsx(ProposalIssues, { issues: proposal.issues, className: "px-3" }), _jsx(Row, { gap: "2", align: "center", className: "border-t border-border/60 px-3 py-2", children: proposal.status === 'pending' ? (_jsxs(_Fragment, { children: [_jsx(Text, { size: "xs", tone: "muted", className: "min-w-0 flex-1", children: translate('ai.proposal.reviewHint') }), _jsxs(Button, { size: "sm", variant: "ghost", onClick: onDiscard, children: [_jsx(X, {}), " ", translate('ai.proposal.discard')] }), _jsxs(Button, { size: "sm", onClick: onApply, children: [_jsx(Check, {}), " ", translate('ai.proposal.apply')] })] })) : applied ? (_jsxs(_Fragment, { children: [_jsx(Spacer, {}), _jsxs(Button, { size: "sm", variant: "outline", onClick: onOpen, children: [_jsx(ArrowUpRight, {}), " ", translate('ai.proposal.open')] })] })) : (_jsx(Text, { size: "xs", tone: "muted", children: translate('ai.proposal.discardedNote') })) }), reviewable && reviewOpen ? (_jsx(ProposalReviewSheet, { title: captions.title, diff: diff, collection: collection, source: source, columnHeaders: columnHeaders, issues: proposal.issues, status: proposal.status, onApply: onApply, onDiscard: onDiscard, onClose: () => setReviewOpen(false) })) : null] }));
77
105
  }
78
106
  /**
79
107
  * The expandable review surface (EC-382): the proposal's diff promoted into a
@@ -88,8 +116,8 @@ export function ProposalCardView({ proposal, collection, source, onApply, onDisc
88
116
  * narrow AI panel; the card default keeps it closed, so the SSR smoke path
89
117
  * never mounts the portal.
90
118
  */
91
- function ProposalReviewSheet({ title, diff, collection, source, columnHeaders, status, onApply, onDiscard, onClose, }) {
92
- return (_jsxs(ExpandableSurface, { title: title, closeLabel: translate('ai.proposal.collapseReview'), width: "min(720px, 92vw)", onDismiss: onClose, headerLeading: _jsx(Sparkles, { className: "size-4 shrink-0 text-primary" }), children: [_jsx("div", { className: "min-h-0 flex-1 overflow-y-auto p-4 sm:p-6", children: _jsx("div", { className: "mx-auto w-full max-w-3xl", children: _jsx(CollectionViewProvider, { source: source, children: _jsx(DocumentDiffView, { diff: diff, collection: collection, columnHeaders: columnHeaders }) }) }) }), status === 'pending' ? (_jsxs(Row, { gap: "2", align: "center", className: "shrink-0 border-t border-border px-4 py-3", children: [_jsx(Text, { size: "xs", tone: "muted", className: "min-w-0 flex-1", children: translate('ai.proposal.reviewHint') }), _jsxs(Button, { size: "sm", variant: "ghost", onClick: () => {
119
+ function ProposalReviewSheet({ title, diff, collection, source, columnHeaders, issues, status, onApply, onDiscard, onClose, }) {
120
+ return (_jsxs(ExpandableSurface, { title: title, closeLabel: translate('ai.proposal.collapseReview'), width: "min(720px, 92vw)", onDismiss: onClose, headerLeading: _jsx(Sparkles, { className: "size-4 shrink-0 text-primary" }), children: [_jsx("div", { className: "min-h-0 flex-1 overflow-y-auto p-4 sm:p-6", children: _jsxs("div", { className: "mx-auto w-full max-w-3xl", children: [_jsx(CollectionViewProvider, { source: source, children: _jsx(DocumentDiffView, { diff: diff, collection: collection, columnHeaders: columnHeaders }) }), _jsx(ProposalIssues, { issues: issues, className: "mt-4" })] }) }), status === 'pending' ? (_jsxs(Row, { gap: "2", align: "center", className: "shrink-0 border-t border-border px-4 py-3", children: [_jsx(Text, { size: "xs", tone: "muted", className: "min-w-0 flex-1", children: translate('ai.proposal.reviewHint') }), _jsxs(Button, { size: "sm", variant: "ghost", onClick: () => {
93
121
  onDiscard();
94
122
  onClose();
95
123
  }, children: [_jsx(X, {}), " ", translate('ai.proposal.discard')] }), _jsxs(Button, { size: "sm", onClick: () => {
@@ -1 +1 @@
1
- {"version":3,"file":"proposal-card.js","sourceRoot":"","sources":["../../../src/components/ai/proposal-card.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useState } from 'react'\nimport { ArrowUpRight, Check, Maximize2, Sparkles, X } from 'lucide-react'\nimport { diffDocuments } from '@elytracms/core/content'\nimport type { DocumentDiff } from '@elytracms/core/content'\nimport { resolveLabel } from '@elytracms/core/cms-core'\nimport type { CollectionDef } from '@elytracms/core/cms-core'\nimport { Badge, Button, ExpandableSurface, Row, Spacer, Stack, Text } from '@elytracms/studio/ui'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { proposalDocuments } from '@elytracms/studio/lib/ai'\nimport type { ProposalOrigin, ProposalReview } from '@elytracms/studio/lib/ai'\nimport type { CollectionViewSource } from '@elytracms/studio/lib/collection-view/collection-view-source'\nimport { useCms } from '../cms/cms-store'\nimport { useCmsCollectionViewSource } from '../collection-view/cms-collection-view-source'\nimport { CollectionViewProvider } from '../collection-view/collection-view-context'\nimport { DocumentDiffView } from '../cms/document-diff-view'\n\n/**\n * The AI proposal review (EC-199, extended by EC-332/EC-378): a valid,\n * leash-compliant proposal the editor reviews as a DIFF (the shared EC-317\n * {@link DocumentDiffView} — never a second renderer) before applying it as a\n * draft or discarding it. Create proposals diff `empty-before` vs\n * `proposed-after`; translate/update proposals diff the EXISTING document\n * (old → new). Apply/discard are handled by the panel (the operations write +\n * provenance snapshot happen there); this card is the surface.\n *\n * {@link ProposalCard} is the CMS-wired shell; {@link ProposalCardView} is the\n * presentational card over an injected collection + view source (node-smoked\n * with `renderToStaticMarkup`, house pattern).\n */\n\n/** The card's origin-dependent captions (title + diff column headers). */\nexport function proposalCaptions(\n origin: ProposalOrigin,\n collectionLabel: string,\n): { title: string; unified: string; before: string; after: string } {\n if (origin === 'translate') {\n return {\n title: translate('ai.translate.proposalTitle', { collection: collectionLabel }),\n unified: translate('ai.proposal.diffUnified'),\n before: translate('ai.translate.diffBefore'),\n after: translate('ai.translate.diffAfter'),\n }\n }\n if (origin === 'update') {\n return {\n title: translate('ai.update.proposalTitle', { collection: collectionLabel }),\n unified: translate('ai.proposal.diffUnified'),\n before: translate('ai.update.diffBefore'),\n after: translate('ai.update.diffAfter'),\n }\n }\n return {\n title: translate('ai.proposal.title', { collection: collectionLabel }),\n unified: translate('ai.proposal.diffUnified'),\n before: translate('ai.proposal.diffBefore'),\n after: translate('ai.proposal.diffAfter'),\n }\n}\n\nexport function ProposalCard({\n proposal,\n onApply,\n onDiscard,\n onOpen,\n}: {\n proposal: ProposalReview\n onApply: () => void\n onDiscard: () => void\n onOpen: () => void\n}) {\n const cms = useCms()\n const collection = cms.getCollection(proposal.collection)\n const source = useCmsCollectionViewSource(proposal.collection)\n return (\n <ProposalCardView\n proposal={proposal}\n collection={collection}\n source={source}\n onApply={onApply}\n onDiscard={onDiscard}\n onOpen={onOpen}\n />\n )\n}\n\n/** The presentational proposal card over an injected collection + view source. */\nexport function ProposalCardView({\n proposal,\n collection,\n source,\n onApply,\n onDiscard,\n onOpen,\n}: {\n proposal: ProposalReview\n collection: CollectionDef | undefined\n source: CollectionViewSource | null\n onApply: () => void\n onDiscard: () => void\n onOpen: () => void\n}) {\n const diff = useMemo(() => {\n if (!collection) return null\n const { before, after } = proposalDocuments(proposal.patch, proposal.before)\n return diffDocuments(collection, before, after)\n }, [collection, proposal.patch, proposal.before])\n\n const collectionLabel =\n (collection?.form?.labelSingular && resolveLabel(collection.form.labelSingular)) ??\n (collection?.form?.label && resolveLabel(collection.form.label)) ??\n proposal.collection\n\n const captions = proposalCaptions(proposal.origin, collectionLabel)\n const applied = proposal.status === 'applied'\n const rejected = proposal.status === 'rejected'\n const columnHeaders = { unified: captions.unified, before: captions.before, after: captions.after }\n\n // EC-382 — the expandable review surface: the sidebar card stays stacked; a\n // roomy full-height sheet is offered for a large proposal (never a modal form).\n const [reviewOpen, setReviewOpen] = useState(false)\n const reviewable = diff !== null && source !== null\n\n return (\n <Stack\n gap=\"2\"\n className=\"overflow-hidden rounded-xl border border-border bg-card\"\n >\n <Row gap=\"2\" align=\"center\" className=\"border-b border-border/60 bg-muted/30 px-3 py-2\">\n <Sparkles className=\"size-4 shrink-0 text-primary\" />\n <Text size=\"sm\" weight=\"medium\" className=\"min-w-0 truncate\">\n {captions.title}\n </Text>\n <Spacer />\n {applied ? <Badge variant=\"success\">{translate('ai.proposal.applied')}</Badge> : null}\n {rejected ? <Badge variant=\"outline\">{translate('ai.proposal.discarded')}</Badge> : null}\n {reviewable ? (\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n onClick={() => setReviewOpen(true)}\n title={translate('ai.proposal.expandReview')}\n aria-label={translate('ai.proposal.expandReview')}\n >\n <Maximize2 />\n </Button>\n ) : null}\n </Row>\n\n <div className=\"px-3\">\n {reviewable ? (\n <CollectionViewProvider source={source!}>\n <DocumentDiffView\n diff={diff!}\n collection={collection!}\n columnHeaders={columnHeaders}\n layout=\"stacked\"\n />\n </CollectionViewProvider>\n ) : (\n <Text size=\"xs\" tone=\"muted\" className=\"py-2\">\n {translate('ai.proposal.unavailable')}\n </Text>\n )}\n </div>\n\n <Row gap=\"2\" align=\"center\" className=\"border-t border-border/60 px-3 py-2\">\n {proposal.status === 'pending' ? (\n <>\n <Text size=\"xs\" tone=\"muted\" className=\"min-w-0 flex-1\">\n {translate('ai.proposal.reviewHint')}\n </Text>\n <Button size=\"sm\" variant=\"ghost\" onClick={onDiscard}>\n <X /> {translate('ai.proposal.discard')}\n </Button>\n <Button size=\"sm\" onClick={onApply}>\n <Check /> {translate('ai.proposal.apply')}\n </Button>\n </>\n ) : applied ? (\n <>\n <Spacer />\n <Button size=\"sm\" variant=\"outline\" onClick={onOpen}>\n <ArrowUpRight /> {translate('ai.proposal.open')}\n </Button>\n </>\n ) : (\n <Text size=\"xs\" tone=\"muted\">\n {translate('ai.proposal.discardedNote')}\n </Text>\n )}\n </Row>\n\n {reviewable && reviewOpen ? (\n <ProposalReviewSheet\n title={captions.title}\n diff={diff!}\n collection={collection!}\n source={source!}\n columnHeaders={columnHeaders}\n status={proposal.status}\n onApply={onApply}\n onDiscard={onDiscard}\n onClose={() => setReviewOpen(false)}\n />\n ) : null}\n </Stack>\n )\n}\n\n/**\n * The expandable review surface (EC-382): the proposal's diff promoted into a\n * roomy, full-height right-anchored sheet where the wide `grid` layout is\n * comfortable — for a large proposal the 320px sidebar can only cramp. It is a\n * REVIEW surface, not a form, so it stays lightly dismissible (backdrop click /\n * Escape / close button) rather than a trapping modal — the forms-never-modal\n * spirit, applied. Presentation only: Apply/Discard call the same handlers the\n * card does; it changes no proposal data. Since EC-392 it renders through the\n * shared {@link ExpandableSurface} primitive (portal-to-body, backdrop, header\n * close, layer-stacked Escape) at the shipped 720px width, so it escapes the\n * narrow AI panel; the card default keeps it closed, so the SSR smoke path\n * never mounts the portal.\n */\nfunction ProposalReviewSheet({\n title,\n diff,\n collection,\n source,\n columnHeaders,\n status,\n onApply,\n onDiscard,\n onClose,\n}: {\n title: string\n diff: DocumentDiff\n collection: CollectionDef\n source: CollectionViewSource\n columnHeaders: { unified: string; before: string; after: string }\n status: ProposalReview['status']\n onApply: () => void\n onDiscard: () => void\n onClose: () => void\n}) {\n return (\n <ExpandableSurface\n title={title}\n closeLabel={translate('ai.proposal.collapseReview')}\n width=\"min(720px, 92vw)\"\n onDismiss={onClose}\n headerLeading={<Sparkles className=\"size-4 shrink-0 text-primary\" />}\n >\n <div className=\"min-h-0 flex-1 overflow-y-auto p-4 sm:p-6\">\n <div className=\"mx-auto w-full max-w-3xl\">\n <CollectionViewProvider source={source}>\n <DocumentDiffView diff={diff} collection={collection} columnHeaders={columnHeaders} />\n </CollectionViewProvider>\n </div>\n </div>\n\n {status === 'pending' ? (\n <Row gap=\"2\" align=\"center\" className=\"shrink-0 border-t border-border px-4 py-3\">\n <Text size=\"xs\" tone=\"muted\" className=\"min-w-0 flex-1\">\n {translate('ai.proposal.reviewHint')}\n </Text>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n onDiscard()\n onClose()\n }}\n >\n <X /> {translate('ai.proposal.discard')}\n </Button>\n <Button\n size=\"sm\"\n onClick={() => {\n onApply()\n onClose()\n }}\n >\n <Check /> {translate('ai.proposal.apply')}\n </Button>\n </Row>\n ) : null}\n </ExpandableSurface>\n )\n}\n"]}
1
+ {"version":3,"file":"proposal-card.js","sourceRoot":"","sources":["../../../src/components/ai/proposal-card.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useState } from 'react'\nimport { ArrowUpRight, Check, Maximize2, Sparkles, X } from 'lucide-react'\nimport { diffDocuments } from '@elytracms/core/content'\nimport type { DocumentDiff } from '@elytracms/core/content'\nimport { resolveLabel } from '@elytracms/core/cms-core'\nimport type { CmsValidationIssue, CollectionDef } from '@elytracms/core/cms-core'\nimport { Badge, Button, ExpandableSurface, Row, Spacer, Stack, Text } from '@elytracms/studio/ui'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { proposalDocuments } from '@elytracms/studio/lib/ai'\nimport type { ProposalOrigin, ProposalReview } from '@elytracms/studio/lib/ai'\nimport type { CollectionViewSource } from '@elytracms/studio/lib/collection-view/collection-view-source'\nimport { useCms } from '../cms/cms-store'\nimport { useCmsCollectionViewSource } from '../collection-view/cms-collection-view-source'\nimport { CollectionViewProvider } from '../collection-view/collection-view-context'\nimport { DocumentDiffView } from '../cms/document-diff-view'\nimport { ProposalIssues } from './proposal-issues'\n\n/**\n * The AI proposal review (EC-199, extended by EC-332/EC-378): a valid,\n * leash-compliant proposal the editor reviews as a DIFF (the shared EC-317\n * {@link DocumentDiffView} — never a second renderer) before applying it as a\n * draft or discarding it. Create proposals diff `empty-before` vs\n * `proposed-after`; translate/update proposals diff the EXISTING document\n * (old → new). Apply/discard are handled by the panel (the operations write +\n * provenance snapshot happen there); this card is the surface.\n *\n * {@link ProposalCard} is the CMS-wired shell; {@link ProposalCardView} is the\n * presentational card over an injected collection + view source (node-smoked\n * with `renderToStaticMarkup`, house pattern).\n *\n * ## EC-496 — the proposal's own findings\n *\n * The diff answers *what changes*; {@link ProposalIssues} answers *what the engine\n * flagged about it*, immediately above the Apply/Discard row so it is read while the\n * decision is being made. Two calls made here, both deliberate:\n *\n * **An error on the card does NOT block apply.** Grounded before deciding\n * (`file:line`, read 2026-08-01): every producer already filters this list to\n * non-error findings (`lib/ai/apply-content.ts:259`, `lib/ai/update-fields.ts:442`,\n * `lib/ai/tools.ts:764`), so a card-blocking gate would be dead code today. More\n * importantly the real gate already exists one layer up and is strictly better\n * placed: EC-473 made apply RE-VALIDATE against the session as it is NOW\n * (`ai-chat-panel.tsx:371`) and refuse with `ai.error.applyInvalid` when the write\n * would INTRODUCE an error. A build-time snapshot cannot see an edit the editor made\n * since, and gating on pre-existing brokenness is exactly what EC-473 argued makes an\n * assistant unusable on real content. So this surface REPORTS truthfully — errors\n * render as errors and sort first ({@link orderedProposalIssues}) — and the gate\n * stays where it can see the truth.\n *\n * **`attempts` is not rendered** (the EC-496 scope-2 owner call). The repair-loop\n * count is provenance about the MODEL, not about the content: a proposal repaired on\n * the second attempt passed the identical validation gate as a first-shot one, so the\n * number changes nothing an editor can act on — and spending sidebar space plus a new\n * catalog string on it would trade a real signal (the findings) for a trivia line. The\n * field stays on `ChatProposal` (it is deterministic, cheap, and the natural input for\n * a future AI-diagnostics surface); it simply has no place on the editor's card.\n */\n\n/** The card's origin-dependent captions (title + diff column headers). */\nexport function proposalCaptions(\n origin: ProposalOrigin,\n collectionLabel: string,\n): { title: string; unified: string; before: string; after: string } {\n if (origin === 'translate') {\n return {\n title: translate('ai.translate.proposalTitle', { collection: collectionLabel }),\n unified: translate('ai.proposal.diffUnified'),\n before: translate('ai.translate.diffBefore'),\n after: translate('ai.translate.diffAfter'),\n }\n }\n if (origin === 'update') {\n return {\n title: translate('ai.update.proposalTitle', { collection: collectionLabel }),\n unified: translate('ai.proposal.diffUnified'),\n before: translate('ai.update.diffBefore'),\n after: translate('ai.update.diffAfter'),\n }\n }\n return {\n title: translate('ai.proposal.title', { collection: collectionLabel }),\n unified: translate('ai.proposal.diffUnified'),\n before: translate('ai.proposal.diffBefore'),\n after: translate('ai.proposal.diffAfter'),\n }\n}\n\nexport function ProposalCard({\n proposal,\n onApply,\n onDiscard,\n onOpen,\n}: {\n proposal: ProposalReview\n onApply: () => void\n onDiscard: () => void\n onOpen: () => void\n}) {\n const cms = useCms()\n const collection = cms.getCollection(proposal.collection)\n const source = useCmsCollectionViewSource(proposal.collection)\n return (\n <ProposalCardView\n proposal={proposal}\n collection={collection}\n source={source}\n onApply={onApply}\n onDiscard={onDiscard}\n onOpen={onOpen}\n />\n )\n}\n\n/** The presentational proposal card over an injected collection + view source. */\nexport function ProposalCardView({\n proposal,\n collection,\n source,\n onApply,\n onDiscard,\n onOpen,\n}: {\n proposal: ProposalReview\n collection: CollectionDef | undefined\n source: CollectionViewSource | null\n onApply: () => void\n onDiscard: () => void\n onOpen: () => void\n}) {\n const diff = useMemo(() => {\n if (!collection) return null\n const { before, after } = proposalDocuments(proposal.patch, proposal.before)\n return diffDocuments(collection, before, after)\n }, [collection, proposal.patch, proposal.before])\n\n const collectionLabel =\n (collection?.form?.labelSingular && resolveLabel(collection.form.labelSingular)) ??\n (collection?.form?.label && resolveLabel(collection.form.label)) ??\n proposal.collection\n\n const captions = proposalCaptions(proposal.origin, collectionLabel)\n const applied = proposal.status === 'applied'\n const rejected = proposal.status === 'rejected'\n const columnHeaders = { unified: captions.unified, before: captions.before, after: captions.after }\n\n // EC-382 — the expandable review surface: the sidebar card stays stacked; a\n // roomy full-height sheet is offered for a large proposal (never a modal form).\n const [reviewOpen, setReviewOpen] = useState(false)\n const reviewable = diff !== null && source !== null\n\n return (\n <Stack\n gap=\"2\"\n className=\"overflow-hidden rounded-xl border border-border bg-card\"\n >\n <Row gap=\"2\" align=\"center\" className=\"border-b border-border/60 bg-muted/30 px-3 py-2\">\n <Sparkles className=\"size-4 shrink-0 text-primary\" />\n <Text size=\"sm\" weight=\"medium\" className=\"min-w-0 truncate\">\n {captions.title}\n </Text>\n <Spacer />\n {applied ? <Badge variant=\"success\">{translate('ai.proposal.applied')}</Badge> : null}\n {rejected ? <Badge variant=\"outline\">{translate('ai.proposal.discarded')}</Badge> : null}\n {reviewable ? (\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n onClick={() => setReviewOpen(true)}\n title={translate('ai.proposal.expandReview')}\n aria-label={translate('ai.proposal.expandReview')}\n >\n <Maximize2 />\n </Button>\n ) : null}\n </Row>\n\n <div className=\"px-3\">\n {reviewable ? (\n <CollectionViewProvider source={source!}>\n <DocumentDiffView\n diff={diff!}\n collection={collection!}\n columnHeaders={columnHeaders}\n layout=\"stacked\"\n />\n </CollectionViewProvider>\n ) : (\n <Text size=\"xs\" tone=\"muted\" className=\"py-2\">\n {translate('ai.proposal.unavailable')}\n </Text>\n )}\n </div>\n\n {/* EC-496 — the engine's surviving findings, last thing before the actions. */}\n <ProposalIssues issues={proposal.issues} className=\"px-3\" />\n\n <Row gap=\"2\" align=\"center\" className=\"border-t border-border/60 px-3 py-2\">\n {proposal.status === 'pending' ? (\n <>\n <Text size=\"xs\" tone=\"muted\" className=\"min-w-0 flex-1\">\n {translate('ai.proposal.reviewHint')}\n </Text>\n <Button size=\"sm\" variant=\"ghost\" onClick={onDiscard}>\n <X /> {translate('ai.proposal.discard')}\n </Button>\n <Button size=\"sm\" onClick={onApply}>\n <Check /> {translate('ai.proposal.apply')}\n </Button>\n </>\n ) : applied ? (\n <>\n <Spacer />\n <Button size=\"sm\" variant=\"outline\" onClick={onOpen}>\n <ArrowUpRight /> {translate('ai.proposal.open')}\n </Button>\n </>\n ) : (\n <Text size=\"xs\" tone=\"muted\">\n {translate('ai.proposal.discardedNote')}\n </Text>\n )}\n </Row>\n\n {reviewable && reviewOpen ? (\n <ProposalReviewSheet\n title={captions.title}\n diff={diff!}\n collection={collection!}\n source={source!}\n columnHeaders={columnHeaders}\n issues={proposal.issues}\n status={proposal.status}\n onApply={onApply}\n onDiscard={onDiscard}\n onClose={() => setReviewOpen(false)}\n />\n ) : null}\n </Stack>\n )\n}\n\n/**\n * The expandable review surface (EC-382): the proposal's diff promoted into a\n * roomy, full-height right-anchored sheet where the wide `grid` layout is\n * comfortable — for a large proposal the 320px sidebar can only cramp. It is a\n * REVIEW surface, not a form, so it stays lightly dismissible (backdrop click /\n * Escape / close button) rather than a trapping modal — the forms-never-modal\n * spirit, applied. Presentation only: Apply/Discard call the same handlers the\n * card does; it changes no proposal data. Since EC-392 it renders through the\n * shared {@link ExpandableSurface} primitive (portal-to-body, backdrop, header\n * close, layer-stacked Escape) at the shipped 720px width, so it escapes the\n * narrow AI panel; the card default keeps it closed, so the SSR smoke path\n * never mounts the portal.\n */\nfunction ProposalReviewSheet({\n title,\n diff,\n collection,\n source,\n columnHeaders,\n issues,\n status,\n onApply,\n onDiscard,\n onClose,\n}: {\n title: string\n diff: DocumentDiff\n collection: CollectionDef\n source: CollectionViewSource\n columnHeaders: { unified: string; before: string; after: string }\n /** EC-496 — the same findings the card shows; the roomy surface must not hide them. */\n issues: readonly CmsValidationIssue[]\n status: ProposalReview['status']\n onApply: () => void\n onDiscard: () => void\n onClose: () => void\n}) {\n return (\n <ExpandableSurface\n title={title}\n closeLabel={translate('ai.proposal.collapseReview')}\n width=\"min(720px, 92vw)\"\n onDismiss={onClose}\n headerLeading={<Sparkles className=\"size-4 shrink-0 text-primary\" />}\n >\n <div className=\"min-h-0 flex-1 overflow-y-auto p-4 sm:p-6\">\n <div className=\"mx-auto w-full max-w-3xl\">\n <CollectionViewProvider source={source}>\n <DocumentDiffView diff={diff} collection={collection} columnHeaders={columnHeaders} />\n </CollectionViewProvider>\n <ProposalIssues issues={issues} className=\"mt-4\" />\n </div>\n </div>\n\n {status === 'pending' ? (\n <Row gap=\"2\" align=\"center\" className=\"shrink-0 border-t border-border px-4 py-3\">\n <Text size=\"xs\" tone=\"muted\" className=\"min-w-0 flex-1\">\n {translate('ai.proposal.reviewHint')}\n </Text>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n onDiscard()\n onClose()\n }}\n >\n <X /> {translate('ai.proposal.discard')}\n </Button>\n <Button\n size=\"sm\"\n onClick={() => {\n onApply()\n onClose()\n }}\n >\n <Check /> {translate('ai.proposal.apply')}\n </Button>\n </Row>\n ) : null}\n </ExpandableSurface>\n )\n}\n"]}
@@ -0,0 +1,55 @@
1
+ import type { CmsValidationIssue } from '@elytracms/core/cms-core';
2
+ /**
3
+ * EC-496 — the findings the engine attached to an AI proposal, rendered on the
4
+ * review surface.
5
+ *
6
+ * A proposal carries the non-error issues its content produced
7
+ * (`ChatProposal.issues`): the repair loop and the EC-473 gate block on errors the
8
+ * change INTRODUCES, and everything else — an EC-472 implausible year, an EC-390
9
+ * localized-plain-value note, an unpublished relation target, a nesting-depth
10
+ * warning — deliberately survives into the proposal. Until this ticket nothing
11
+ * read the field, so the one consumer of a warning-severity finding on the AI path
12
+ * was a UI that ignored it: an editor pressed **Apply as draft** without ever being
13
+ * told the engine had flagged the content.
14
+ *
15
+ * The treatment is the studio's ONE severity vocabulary, not a third one: the
16
+ * palette and glyph come from {@link issueSeverityStyle} / {@link issueSeverityIcon}
17
+ * in `components/issue-severity.ts` — the pure cores EC-493 first extracted from the
18
+ * document Issues pane, which EC-507 then moved out of the section rail into a home
19
+ * neither surface owns (importing them from `nav-issues` was how this card ended up
20
+ * depending on a `nav`-prefixed core). The card copy follows the pane too: the plain-language
21
+ * {@link cmsIssueLabel} as the title with the raw `code` in `title=` for engineers
22
+ * (the EC-192 persona boundary), and the localized body via
23
+ * {@link cmsValidationIssueMessage} (EC-487's `messageRef` carriage, degrading to the
24
+ * producer's English for a ref-less issue).
25
+ *
26
+ * `data-proposal-issue-severity` on each card is the machine-readable form of the
27
+ * same distinction — what the smoke test pins, and what an agent inspecting the live
28
+ * DOM reads without decoding Tailwind classes.
29
+ */
30
+ /**
31
+ * The proposal's findings in render order: **errors first**, warnings after, stable
32
+ * within each severity.
33
+ *
34
+ * The card's primary action is *Apply as draft*, and the reader scans top-down — so
35
+ * the finding that should give pause must not sit below one that should merely
36
+ * inform. That is the shared rank rule (`error` outranks `warning`), so since EC-507
37
+ * this delegates to {@link orderBySeverity} rather than re-expressing it: the rail's
38
+ * `highestIssueSeverity` is the same rule reduced to one aggregate glyph.
39
+ *
40
+ * Errors are not *expected* here — all three producers filter to non-error findings
41
+ * (`apply-content.ts:259`, `update-fields.ts:442`, `tools.ts:764`) — but
42
+ * `ChatProposal.issues` is typed `CmsValidationIssue[]`, so this surface renders
43
+ * whatever it is handed, truthfully. See the apply-gate note in `proposal-card.tsx`.
44
+ */
45
+ export declare function orderedProposalIssues(issues: readonly CmsValidationIssue[]): CmsValidationIssue[];
46
+ /**
47
+ * The proposal's findings, severity-tinted. Renders NOTHING for an empty list — a
48
+ * clean proposal gets no issue affordance at all, never an empty box captioned
49
+ * "no issues" (the card is a narrow sidebar surface; silence is the correct
50
+ * negative).
51
+ */
52
+ export declare function ProposalIssues({ issues, className, }: {
53
+ issues: readonly CmsValidationIssue[];
54
+ className?: string;
55
+ }): import("react").JSX.Element | null;
@@ -0,0 +1,69 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from '@elytracms/studio/lib/utils';
3
+ import { cmsIssueLabel, cmsValidationIssueMessage } from '@elytracms/studio/lib/persona/labels';
4
+ import { Row, Stack, Text } from '@elytracms/studio/ui';
5
+ import { issueSeverityIcon, issueSeverityStyle, orderBySeverity } from '../issue-severity.js';
6
+ /**
7
+ * EC-496 — the findings the engine attached to an AI proposal, rendered on the
8
+ * review surface.
9
+ *
10
+ * A proposal carries the non-error issues its content produced
11
+ * (`ChatProposal.issues`): the repair loop and the EC-473 gate block on errors the
12
+ * change INTRODUCES, and everything else — an EC-472 implausible year, an EC-390
13
+ * localized-plain-value note, an unpublished relation target, a nesting-depth
14
+ * warning — deliberately survives into the proposal. Until this ticket nothing
15
+ * read the field, so the one consumer of a warning-severity finding on the AI path
16
+ * was a UI that ignored it: an editor pressed **Apply as draft** without ever being
17
+ * told the engine had flagged the content.
18
+ *
19
+ * The treatment is the studio's ONE severity vocabulary, not a third one: the
20
+ * palette and glyph come from {@link issueSeverityStyle} / {@link issueSeverityIcon}
21
+ * in `components/issue-severity.ts` — the pure cores EC-493 first extracted from the
22
+ * document Issues pane, which EC-507 then moved out of the section rail into a home
23
+ * neither surface owns (importing them from `nav-issues` was how this card ended up
24
+ * depending on a `nav`-prefixed core). The card copy follows the pane too: the plain-language
25
+ * {@link cmsIssueLabel} as the title with the raw `code` in `title=` for engineers
26
+ * (the EC-192 persona boundary), and the localized body via
27
+ * {@link cmsValidationIssueMessage} (EC-487's `messageRef` carriage, degrading to the
28
+ * producer's English for a ref-less issue).
29
+ *
30
+ * `data-proposal-issue-severity` on each card is the machine-readable form of the
31
+ * same distinction — what the smoke test pins, and what an agent inspecting the live
32
+ * DOM reads without decoding Tailwind classes.
33
+ */
34
+ /**
35
+ * The proposal's findings in render order: **errors first**, warnings after, stable
36
+ * within each severity.
37
+ *
38
+ * The card's primary action is *Apply as draft*, and the reader scans top-down — so
39
+ * the finding that should give pause must not sit below one that should merely
40
+ * inform. That is the shared rank rule (`error` outranks `warning`), so since EC-507
41
+ * this delegates to {@link orderBySeverity} rather than re-expressing it: the rail's
42
+ * `highestIssueSeverity` is the same rule reduced to one aggregate glyph.
43
+ *
44
+ * Errors are not *expected* here — all three producers filter to non-error findings
45
+ * (`apply-content.ts:259`, `update-fields.ts:442`, `tools.ts:764`) — but
46
+ * `ChatProposal.issues` is typed `CmsValidationIssue[]`, so this surface renders
47
+ * whatever it is handed, truthfully. See the apply-gate note in `proposal-card.tsx`.
48
+ */
49
+ export function orderedProposalIssues(issues) {
50
+ return orderBySeverity(issues);
51
+ }
52
+ /**
53
+ * The proposal's findings, severity-tinted. Renders NOTHING for an empty list — a
54
+ * clean proposal gets no issue affordance at all, never an empty box captioned
55
+ * "no issues" (the card is a narrow sidebar surface; silence is the correct
56
+ * negative).
57
+ */
58
+ export function ProposalIssues({ issues, className, }) {
59
+ if (issues.length === 0)
60
+ return null;
61
+ return (_jsx(Stack, { gap: "1", className: className, "data-testid": "proposal-issues", children: orderedProposalIssues(issues).map((issue, index) => (_jsx(ProposalIssueCard, { issue: issue }, `${issue.code}:${issue.path.join('/')}:${index}`))) }));
62
+ }
63
+ /** One finding on the proposal, tinted + glyphed by its severity. */
64
+ function ProposalIssueCard({ issue }) {
65
+ const style = issueSeverityStyle(issue.severity);
66
+ const Icon = issueSeverityIcon(issue.severity);
67
+ return (_jsxs(Row, { gap: "2", align: "start", className: cn('rounded-md border px-2.5 py-2', style.card, style.text), "data-proposal-issue-severity": issue.severity, children: [_jsx(Icon, { className: "mt-0.5 size-3.5 shrink-0", "aria-hidden": true }), _jsxs(Stack, { gap: "1", className: "min-w-0", children: [_jsx(Text, { size: "xs", weight: "semibold", title: issue.code, children: cmsIssueLabel(issue.code, issue.severity) }), _jsx(Text, { size: "xs", className: "min-w-0 leading-snug", children: cmsValidationIssueMessage(issue) })] })] }));
68
+ }
69
+ //# sourceMappingURL=proposal-issues.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proposal-issues.js","sourceRoot":"","sources":["../../../src/components/ai/proposal-issues.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,6BAA6B,CAAA;AAChD,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAA;AAC/F,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAE1F;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAqC;IAErC,OAAO,eAAe,CAAC,MAAM,CAAC,CAAA;AAChC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,EAC7B,MAAM,EACN,SAAS,GAIV;IACC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACpC,OAAO,CACL,KAAC,KAAK,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAE,SAAS,iBAAc,iBAAiB,YAC/D,qBAAqB,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CACnD,KAAC,iBAAiB,IAEhB,KAAK,EAAE,KAAK,IADP,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,EAAE,CAErD,CACH,CAAC,GACI,CACT,CAAA;AACH,CAAC;AAED,qEAAqE;AACrE,SAAS,iBAAiB,CAAC,EAAE,KAAK,EAAiC;IACjE,MAAM,KAAK,GAAG,kBAAkB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;IAChD,MAAM,IAAI,GAAG,iBAAiB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;IAC9C,OAAO,CACL,MAAC,GAAG,IACF,GAAG,EAAC,GAAG,EACP,KAAK,EAAC,OAAO,EACb,SAAS,EAAE,EAAE,CAAC,+BAA+B,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,kCACxC,KAAK,CAAC,QAAQ,aAE5C,KAAC,IAAI,IAAC,SAAS,EAAC,0BAA0B,wBAAe,EACzD,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,SAAS,aAChC,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,MAAM,EAAC,UAAU,EAAC,KAAK,EAAE,KAAK,CAAC,IAAI,YAChD,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,GACrC,EACP,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,SAAS,EAAC,sBAAsB,YAC7C,yBAAyB,CAAC,KAAK,CAAC,GAC5B,IACD,IACJ,CACP,CAAA;AACH,CAAC","sourcesContent":["import type { CmsValidationIssue } from '@elytracms/core/cms-core'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { cmsIssueLabel, cmsValidationIssueMessage } from '@elytracms/studio/lib/persona/labels'\nimport { Row, Stack, Text } from '@elytracms/studio/ui'\nimport { issueSeverityIcon, issueSeverityStyle, orderBySeverity } from '../issue-severity'\n\n/**\n * EC-496 — the findings the engine attached to an AI proposal, rendered on the\n * review surface.\n *\n * A proposal carries the non-error issues its content produced\n * (`ChatProposal.issues`): the repair loop and the EC-473 gate block on errors the\n * change INTRODUCES, and everything else — an EC-472 implausible year, an EC-390\n * localized-plain-value note, an unpublished relation target, a nesting-depth\n * warning — deliberately survives into the proposal. Until this ticket nothing\n * read the field, so the one consumer of a warning-severity finding on the AI path\n * was a UI that ignored it: an editor pressed **Apply as draft** without ever being\n * told the engine had flagged the content.\n *\n * The treatment is the studio's ONE severity vocabulary, not a third one: the\n * palette and glyph come from {@link issueSeverityStyle} / {@link issueSeverityIcon}\n * in `components/issue-severity.ts` — the pure cores EC-493 first extracted from the\n * document Issues pane, which EC-507 then moved out of the section rail into a home\n * neither surface owns (importing them from `nav-issues` was how this card ended up\n * depending on a `nav`-prefixed core). The card copy follows the pane too: the plain-language\n * {@link cmsIssueLabel} as the title with the raw `code` in `title=` for engineers\n * (the EC-192 persona boundary), and the localized body via\n * {@link cmsValidationIssueMessage} (EC-487's `messageRef` carriage, degrading to the\n * producer's English for a ref-less issue).\n *\n * `data-proposal-issue-severity` on each card is the machine-readable form of the\n * same distinction — what the smoke test pins, and what an agent inspecting the live\n * DOM reads without decoding Tailwind classes.\n */\n\n/**\n * The proposal's findings in render order: **errors first**, warnings after, stable\n * within each severity.\n *\n * The card's primary action is *Apply as draft*, and the reader scans top-down — so\n * the finding that should give pause must not sit below one that should merely\n * inform. That is the shared rank rule (`error` outranks `warning`), so since EC-507\n * this delegates to {@link orderBySeverity} rather than re-expressing it: the rail's\n * `highestIssueSeverity` is the same rule reduced to one aggregate glyph.\n *\n * Errors are not *expected* here — all three producers filter to non-error findings\n * (`apply-content.ts:259`, `update-fields.ts:442`, `tools.ts:764`) — but\n * `ChatProposal.issues` is typed `CmsValidationIssue[]`, so this surface renders\n * whatever it is handed, truthfully. See the apply-gate note in `proposal-card.tsx`.\n */\nexport function orderedProposalIssues(\n issues: readonly CmsValidationIssue[],\n): CmsValidationIssue[] {\n return orderBySeverity(issues)\n}\n\n/**\n * The proposal's findings, severity-tinted. Renders NOTHING for an empty list — a\n * clean proposal gets no issue affordance at all, never an empty box captioned\n * \"no issues\" (the card is a narrow sidebar surface; silence is the correct\n * negative).\n */\nexport function ProposalIssues({\n issues,\n className,\n}: {\n issues: readonly CmsValidationIssue[]\n className?: string\n}) {\n if (issues.length === 0) return null\n return (\n <Stack gap=\"1\" className={className} data-testid=\"proposal-issues\">\n {orderedProposalIssues(issues).map((issue, index) => (\n <ProposalIssueCard\n key={`${issue.code}:${issue.path.join('/')}:${index}`}\n issue={issue}\n />\n ))}\n </Stack>\n )\n}\n\n/** One finding on the proposal, tinted + glyphed by its severity. */\nfunction ProposalIssueCard({ issue }: { issue: CmsValidationIssue }) {\n const style = issueSeverityStyle(issue.severity)\n const Icon = issueSeverityIcon(issue.severity)\n return (\n <Row\n gap=\"2\"\n align=\"start\"\n className={cn('rounded-md border px-2.5 py-2', style.card, style.text)}\n data-proposal-issue-severity={issue.severity}\n >\n <Icon className=\"mt-0.5 size-3.5 shrink-0\" aria-hidden />\n <Stack gap=\"1\" className=\"min-w-0\">\n <Text size=\"xs\" weight=\"semibold\" title={issue.code}>\n {cmsIssueLabel(issue.code, issue.severity)}\n </Text>\n <Text size=\"xs\" className=\"min-w-0 leading-snug\">\n {cmsValidationIssueMessage(issue)}\n </Text>\n </Stack>\n </Row>\n )\n}\n"]}