@prenta/admin 1.17.1 → 1.17.3

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 (293) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/__tests__/lib/posts-service.test.js +35 -0
  3. package/dist/__tests__/lib/posts-service.test.js.map +1 -1
  4. package/dist/__tests__/lib/public-path.test.d.ts +2 -0
  5. package/dist/__tests__/lib/public-path.test.d.ts.map +1 -0
  6. package/dist/__tests__/lib/public-path.test.js +25 -0
  7. package/dist/__tests__/lib/public-path.test.js.map +1 -0
  8. package/dist/__tests__/views/ai-settings.render.test.js +67 -1
  9. package/dist/__tests__/views/ai-settings.render.test.js.map +1 -1
  10. package/dist/__tests__/views/posts-list-view.render.test.js +11 -0
  11. package/dist/__tests__/views/posts-list-view.render.test.js.map +1 -1
  12. package/dist/__tests__/views/seo-autopilot-card.render.test.js +4 -2
  13. package/dist/__tests__/views/seo-autopilot-card.render.test.js.map +1 -1
  14. package/dist/components/CoauthorResultPopover.d.ts.map +1 -1
  15. package/dist/components/CoauthorResultPopover.js +4 -1
  16. package/dist/components/CoauthorResultPopover.js.map +1 -1
  17. package/dist/components/CommentSidePanel.d.ts +0 -7
  18. package/dist/components/CommentSidePanel.d.ts.map +1 -1
  19. package/dist/components/CommentSidePanel.js +0 -2
  20. package/dist/components/CommentSidePanel.js.map +1 -1
  21. package/dist/components/EditorRail.d.ts +2 -1
  22. package/dist/components/EditorRail.d.ts.map +1 -1
  23. package/dist/components/MediaPickerModal.d.ts +2 -1
  24. package/dist/components/MediaPickerModal.d.ts.map +1 -1
  25. package/dist/components/TipTapEditor.d.ts +3 -2
  26. package/dist/components/TipTapEditor.d.ts.map +1 -1
  27. package/dist/components/forms/EntriesTable.d.ts +0 -1
  28. package/dist/components/forms/EntriesTable.d.ts.map +1 -1
  29. package/dist/components/forms/EntriesTable.js +1 -1
  30. package/dist/components/forms/EntriesTable.js.map +1 -1
  31. package/dist/components/pages/tag-badges.d.ts +2 -2
  32. package/dist/components/pages/tag-badges.d.ts.map +1 -1
  33. package/dist/components/pages/tag-badges.js.map +1 -1
  34. package/dist/components/seo/FaqProposalPreview.d.ts +2 -3
  35. package/dist/components/seo/FaqProposalPreview.d.ts.map +1 -1
  36. package/dist/components/seo/FaqProposalPreview.js +1 -1
  37. package/dist/components/seo/FaqProposalPreview.js.map +1 -1
  38. package/dist/components/seo/SeoCopilotDrawer.js +1 -1
  39. package/dist/components/seo/SeoCopilotDrawer.js.map +1 -1
  40. package/dist/components/seo/SeoEditorPane.d.ts.map +1 -1
  41. package/dist/components/seo/SeoEditorPane.js +4 -1
  42. package/dist/components/seo/SeoEditorPane.js.map +1 -1
  43. package/dist/components/ui/Badge.d.ts +3 -9
  44. package/dist/components/ui/Badge.d.ts.map +1 -1
  45. package/dist/components/ui/Badge.js +2 -3
  46. package/dist/components/ui/Badge.js.map +1 -1
  47. package/dist/components/ui/Button.d.ts +2 -2
  48. package/dist/components/ui/Button.d.ts.map +1 -1
  49. package/dist/components/ui/Button.js +0 -1
  50. package/dist/components/ui/Button.js.map +1 -1
  51. package/dist/components/ui/Card.d.ts +2 -3
  52. package/dist/components/ui/Card.d.ts.map +1 -1
  53. package/dist/components/ui/Card.js +0 -4
  54. package/dist/components/ui/Card.js.map +1 -1
  55. package/dist/components/ui/Input.d.ts +6 -8
  56. package/dist/components/ui/Input.d.ts.map +1 -1
  57. package/dist/components/ui/Input.js +6 -6
  58. package/dist/components/ui/Input.js.map +1 -1
  59. package/dist/components/ui/Modal.d.ts +1 -1
  60. package/dist/components/ui/Modal.d.ts.map +1 -1
  61. package/dist/components/ui/ResizableAside.d.ts +0 -13
  62. package/dist/components/ui/ResizableAside.d.ts.map +1 -1
  63. package/dist/components/ui/ResizableAside.js +5 -5
  64. package/dist/components/ui/ResizableAside.js.map +1 -1
  65. package/dist/components/ui/StatusBadge.d.ts +2 -2
  66. package/dist/components/ui/StatusBadge.d.ts.map +1 -1
  67. package/dist/components/ui/StatusBadge.js +0 -1
  68. package/dist/components/ui/StatusBadge.js.map +1 -1
  69. package/dist/components/ui/Toggle.d.ts +2 -8
  70. package/dist/components/ui/Toggle.d.ts.map +1 -1
  71. package/dist/components/ui/Toggle.js +0 -1
  72. package/dist/components/ui/Toggle.js.map +1 -1
  73. package/dist/fields/FieldRenderer.d.ts +2 -1
  74. package/dist/fields/FieldRenderer.d.ts.map +1 -1
  75. package/dist/fields/index.d.ts +0 -17
  76. package/dist/fields/index.d.ts.map +1 -1
  77. package/dist/fields/index.js +0 -15
  78. package/dist/fields/index.js.map +1 -1
  79. package/dist/layout/primitives/tokens.d.ts +0 -2
  80. package/dist/layout/primitives/tokens.d.ts.map +1 -1
  81. package/dist/layout/primitives/tokens.js +0 -6
  82. package/dist/layout/primitives/tokens.js.map +1 -1
  83. package/dist/lib/brand-voice-client.d.ts +14 -1
  84. package/dist/lib/brand-voice-client.d.ts.map +1 -1
  85. package/dist/lib/brand-voice-client.js +7 -0
  86. package/dist/lib/brand-voice-client.js.map +1 -1
  87. package/dist/lib/coauthor-client.d.ts +2 -1
  88. package/dist/lib/coauthor-client.d.ts.map +1 -1
  89. package/dist/lib/collection-schema.d.ts +0 -9
  90. package/dist/lib/collection-schema.d.ts.map +1 -1
  91. package/dist/lib/collection-schema.js +2 -2
  92. package/dist/lib/collection-schema.js.map +1 -1
  93. package/dist/lib/editor-seo-content.d.ts +0 -10
  94. package/dist/lib/editor-seo-content.d.ts.map +1 -1
  95. package/dist/lib/editor-seo-content.js +0 -13
  96. package/dist/lib/editor-seo-content.js.map +1 -1
  97. package/dist/lib/forms-service.d.ts +3 -21
  98. package/dist/lib/forms-service.d.ts.map +1 -1
  99. package/dist/lib/forms-service.js +0 -25
  100. package/dist/lib/forms-service.js.map +1 -1
  101. package/dist/lib/notifications-client.d.ts +4 -3
  102. package/dist/lib/notifications-client.d.ts.map +1 -1
  103. package/dist/lib/page-audit-input.d.ts +0 -10
  104. package/dist/lib/page-audit-input.d.ts.map +1 -1
  105. package/dist/lib/page-audit-input.js +1 -1
  106. package/dist/lib/page-audit-input.js.map +1 -1
  107. package/dist/lib/page-editor-service.d.ts +2 -4
  108. package/dist/lib/page-editor-service.d.ts.map +1 -1
  109. package/dist/lib/page-editor-service.js +3 -3
  110. package/dist/lib/page-editor-service.js.map +1 -1
  111. package/dist/lib/pages-service.d.ts +2 -13
  112. package/dist/lib/pages-service.d.ts.map +1 -1
  113. package/dist/lib/pages-service.js +3 -24
  114. package/dist/lib/pages-service.js.map +1 -1
  115. package/dist/lib/plan.d.ts +2 -1
  116. package/dist/lib/plan.d.ts.map +1 -1
  117. package/dist/lib/post-editor-service.d.ts +3 -13
  118. package/dist/lib/post-editor-service.d.ts.map +1 -1
  119. package/dist/lib/post-editor-service.js +5 -6
  120. package/dist/lib/post-editor-service.js.map +1 -1
  121. package/dist/lib/post-types-service.d.ts +2 -3
  122. package/dist/lib/post-types-service.d.ts.map +1 -1
  123. package/dist/lib/post-types-service.js +1 -1
  124. package/dist/lib/post-types-service.js.map +1 -1
  125. package/dist/lib/posts-service.d.ts +8 -1
  126. package/dist/lib/posts-service.d.ts.map +1 -1
  127. package/dist/lib/posts-service.js +6 -1
  128. package/dist/lib/posts-service.js.map +1 -1
  129. package/dist/lib/preview-link.d.ts +0 -9
  130. package/dist/lib/preview-link.d.ts.map +1 -1
  131. package/dist/lib/preview-link.js +0 -15
  132. package/dist/lib/preview-link.js.map +1 -1
  133. package/dist/lib/public-path.d.ts +21 -0
  134. package/dist/lib/public-path.d.ts.map +1 -0
  135. package/dist/lib/public-path.js +28 -0
  136. package/dist/lib/public-path.js.map +1 -0
  137. package/dist/lib/rail-width.d.ts +0 -1
  138. package/dist/lib/rail-width.d.ts.map +1 -1
  139. package/dist/lib/rail-width.js +1 -1
  140. package/dist/lib/rail-width.js.map +1 -1
  141. package/dist/lib/reflow-probe.d.ts +0 -67
  142. package/dist/lib/reflow-probe.d.ts.map +1 -1
  143. package/dist/lib/reflow-probe.js +4 -4
  144. package/dist/lib/reflow-probe.js.map +1 -1
  145. package/dist/lib/seo-service.d.ts +14 -32
  146. package/dist/lib/seo-service.d.ts.map +1 -1
  147. package/dist/lib/seo-service.js +7 -26
  148. package/dist/lib/seo-service.js.map +1 -1
  149. package/dist/lib/theme-chrome.d.ts +0 -1
  150. package/dist/lib/theme-chrome.d.ts.map +1 -1
  151. package/dist/lib/theme-chrome.js +1 -1
  152. package/dist/lib/theme-chrome.js.map +1 -1
  153. package/dist/lib/version-history.d.ts +2 -2
  154. package/dist/lib/version-history.d.ts.map +1 -1
  155. package/dist/lib/workflow-chrome.d.ts +0 -3
  156. package/dist/lib/workflow-chrome.d.ts.map +1 -1
  157. package/dist/lib/workflow-chrome.js +3 -3
  158. package/dist/lib/workflow-chrome.js.map +1 -1
  159. package/dist/views/Login.d.ts +2 -1
  160. package/dist/views/Login.d.ts.map +1 -1
  161. package/dist/views/MediaBrowser.d.ts.map +1 -1
  162. package/dist/views/MediaBrowser.js +3 -2
  163. package/dist/views/MediaBrowser.js.map +1 -1
  164. package/dist/views/PageEditor.d.ts +2 -1
  165. package/dist/views/PageEditor.d.ts.map +1 -1
  166. package/dist/views/Pages/PagesListView.d.ts +0 -1
  167. package/dist/views/Pages/PagesListView.d.ts.map +1 -1
  168. package/dist/views/Pages/PagesListView.js +1 -1
  169. package/dist/views/Pages/PagesListView.js.map +1 -1
  170. package/dist/views/Pages.d.ts +2 -1
  171. package/dist/views/Pages.d.ts.map +1 -1
  172. package/dist/views/Posts/PostsListView.d.ts.map +1 -1
  173. package/dist/views/Posts/PostsListView.js +1 -1
  174. package/dist/views/Posts/PostsListView.js.map +1 -1
  175. package/dist/views/Redirects.d.ts +2 -1
  176. package/dist/views/Redirects.d.ts.map +1 -1
  177. package/dist/views/page-editor/section-types.d.ts +2 -2
  178. package/dist/views/page-editor/section-types.d.ts.map +1 -1
  179. package/dist/views/page-editor/section-types.js +1 -1
  180. package/dist/views/page-editor/section-types.js.map +1 -1
  181. package/dist/views/page-editor/sections/admin-renderers.d.ts +2 -1
  182. package/dist/views/page-editor/sections/admin-renderers.d.ts.map +1 -1
  183. package/dist/views/page-editor/theme-chrome/index.d.ts +1 -6
  184. package/dist/views/page-editor/theme-chrome/index.d.ts.map +1 -1
  185. package/dist/views/page-editor/theme-chrome/index.js +1 -3
  186. package/dist/views/page-editor/theme-chrome/index.js.map +1 -1
  187. package/dist/views/page-editor/viewports.d.ts +1 -1
  188. package/dist/views/page-editor/viewports.d.ts.map +1 -1
  189. package/dist/views/page-editor/viewports.js +1 -1
  190. package/dist/views/page-editor/viewports.js.map +1 -1
  191. package/dist/views/settings/AISettingsTab.d.ts.map +1 -1
  192. package/dist/views/settings/AISettingsTab.js +13 -3
  193. package/dist/views/settings/AISettingsTab.js.map +1 -1
  194. package/dist/views/settings/BrandVoiceCard.d.ts +3 -0
  195. package/dist/views/settings/BrandVoiceCard.d.ts.map +1 -1
  196. package/dist/views/settings/BrandVoiceCard.js +2 -2
  197. package/dist/views/settings/BrandVoiceCard.js.map +1 -1
  198. package/dist/views/settings/brand-voice/BrandVoiceAlignmentPanel.d.ts.map +1 -1
  199. package/dist/views/settings/brand-voice/BrandVoiceAlignmentPanel.js +4 -1
  200. package/dist/views/settings/brand-voice/BrandVoiceAlignmentPanel.js.map +1 -1
  201. package/dist/views/settings/featureModel.d.ts +0 -3
  202. package/dist/views/settings/featureModel.d.ts.map +1 -1
  203. package/dist/views/settings/featureModel.js +2 -2
  204. package/dist/views/settings/featureModel.js.map +1 -1
  205. package/dist/views/settings/useAiSettings.d.ts +2 -2
  206. package/dist/views/settings/useAiSettings.d.ts.map +1 -1
  207. package/dist/views/settings/useAiSettings.js +6 -2
  208. package/dist/views/settings/useAiSettings.js.map +1 -1
  209. package/dist/views/settings/useAppearanceSettings.d.ts +5 -6
  210. package/dist/views/settings/useAppearanceSettings.d.ts.map +1 -1
  211. package/dist/views/settings/useAppearanceSettings.js +1 -1
  212. package/dist/views/settings/useAppearanceSettings.js.map +1 -1
  213. package/dist/views/settings/useEmailSettings.d.ts +3 -2
  214. package/dist/views/settings/useEmailSettings.d.ts.map +1 -1
  215. package/dist/views/settings/useGeneralSettings.d.ts +2 -11
  216. package/dist/views/settings/useGeneralSettings.d.ts.map +1 -1
  217. package/dist/views/settings/useGeneralSettings.js +3 -3
  218. package/dist/views/settings/useGeneralSettings.js.map +1 -1
  219. package/dist/views/settings/useSeoSettings.d.ts +1 -1
  220. package/dist/views/settings/useSeoSettings.d.ts.map +1 -1
  221. package/dist/views/settings/useUpdates.d.ts +1 -3
  222. package/dist/views/settings/useUpdates.d.ts.map +1 -1
  223. package/dist/views/settings/useUpdates.js +1 -1
  224. package/dist/views/settings/useUpdates.js.map +1 -1
  225. package/package.json +4 -4
  226. package/src/__tests__/lib/posts-service.test.ts +35 -0
  227. package/src/__tests__/lib/public-path.test.ts +37 -0
  228. package/src/__tests__/views/ai-settings.render.test.tsx +86 -2
  229. package/src/__tests__/views/posts-list-view.render.test.tsx +12 -0
  230. package/src/__tests__/views/seo-autopilot-card.render.test.tsx +4 -4
  231. package/src/components/CoauthorResultPopover.tsx +3 -1
  232. package/src/components/CommentSidePanel.tsx +0 -1
  233. package/src/components/EditorRail.tsx +1 -1
  234. package/src/components/MediaPickerModal.tsx +1 -1
  235. package/src/components/TipTapEditor.tsx +2 -2
  236. package/src/components/forms/EntriesTable.tsx +1 -1
  237. package/src/components/pages/tag-badges.tsx +1 -1
  238. package/src/components/seo/FaqProposalPreview.tsx +2 -2
  239. package/src/components/seo/SeoCopilotDrawer.tsx +1 -1
  240. package/src/components/seo/SeoEditorPane.tsx +3 -1
  241. package/src/components/ui/Badge.tsx +4 -6
  242. package/src/components/ui/Button.tsx +1 -3
  243. package/src/components/ui/Card.tsx +1 -14
  244. package/src/components/ui/Input.tsx +7 -15
  245. package/src/components/ui/Modal.tsx +1 -1
  246. package/src/components/ui/ResizableAside.tsx +5 -5
  247. package/src/components/ui/StatusBadge.tsx +1 -3
  248. package/src/components/ui/Toggle.tsx +1 -3
  249. package/src/fields/FieldRenderer.tsx +1 -1
  250. package/src/fields/index.ts +0 -17
  251. package/src/layout/primitives/tokens.ts +0 -7
  252. package/src/lib/brand-voice-client.ts +18 -1
  253. package/src/lib/coauthor-client.ts +1 -1
  254. package/src/lib/collection-schema.ts +2 -2
  255. package/src/lib/editor-seo-content.ts +0 -22
  256. package/src/lib/forms-service.ts +2 -42
  257. package/src/lib/notifications-client.ts +3 -3
  258. package/src/lib/page-audit-input.ts +1 -1
  259. package/src/lib/page-editor-service.ts +1 -3
  260. package/src/lib/pages-service.ts +4 -29
  261. package/src/lib/plan.ts +1 -1
  262. package/src/lib/post-editor-service.ts +4 -10
  263. package/src/lib/post-types-service.ts +2 -2
  264. package/src/lib/posts-service.ts +13 -2
  265. package/src/lib/preview-link.ts +0 -14
  266. package/src/lib/public-path.ts +33 -0
  267. package/src/lib/rail-width.ts +1 -1
  268. package/src/lib/reflow-probe.ts +4 -4
  269. package/src/lib/seo-service.ts +20 -60
  270. package/src/lib/theme-chrome.ts +1 -1
  271. package/src/lib/version-history.ts +2 -2
  272. package/src/lib/workflow-chrome.ts +3 -3
  273. package/src/views/Login.tsx +1 -1
  274. package/src/views/MediaBrowser.tsx +3 -2
  275. package/src/views/PageEditor.tsx +1 -1
  276. package/src/views/Pages/PagesListView.tsx +1 -1
  277. package/src/views/Pages.tsx +1 -1
  278. package/src/views/Posts/PostsListView.tsx +2 -1
  279. package/src/views/Redirects.tsx +1 -1
  280. package/src/views/page-editor/section-types.ts +0 -9
  281. package/src/views/page-editor/sections/admin-renderers.tsx +1 -1
  282. package/src/views/page-editor/theme-chrome/index.ts +2 -10
  283. package/src/views/page-editor/viewports.ts +0 -3
  284. package/src/views/settings/AISettingsTab.tsx +15 -3
  285. package/src/views/settings/BrandVoiceCard.tsx +11 -1
  286. package/src/views/settings/brand-voice/BrandVoiceAlignmentPanel.tsx +3 -1
  287. package/src/views/settings/featureModel.ts +2 -2
  288. package/src/views/settings/useAiSettings.ts +8 -3
  289. package/src/views/settings/useAppearanceSettings.ts +4 -6
  290. package/src/views/settings/useEmailSettings.ts +2 -2
  291. package/src/views/settings/useGeneralSettings.ts +5 -10
  292. package/src/views/settings/useSeoSettings.ts +1 -1
  293. package/src/views/settings/useUpdates.ts +2 -2
@@ -66,7 +66,7 @@ export function linesFromMetrics(height: number, lineHeight: number, fontSize: n
66
66
  * whose content is a single replaced node, or a layout engine that gives no
67
67
  * rects for empty text.
68
68
  */
69
- export function countLines(el: HTMLElement): number {
69
+ function countLines(el: HTMLElement): number {
70
70
  const doc = el.ownerDocument
71
71
  try {
72
72
  const range = doc.createRange()
@@ -106,7 +106,7 @@ export function countLines(el: HTMLElement): number {
106
106
  *
107
107
  * Use `mountProbeHost` rather than appending by hand.
108
108
  */
109
- export function createProbeHost(doc: Document = document): HTMLElement {
109
+ function createProbeHost(doc: Document = document): HTMLElement {
110
110
  const host = doc.createElement('div')
111
111
  host.setAttribute('aria-hidden', 'true')
112
112
  host.dataset.reflowProbe = 'true'
@@ -128,7 +128,7 @@ export function createProbeHost(doc: Document = document): HTMLElement {
128
128
  * root exists (it always does in the app) — and callers should treat a
129
129
  * fallback mount as untrustworthy rather than silently measuring.
130
130
  */
131
- export function mountProbeHost(
131
+ function mountProbeHost(
132
132
  host: HTMLElement,
133
133
  doc: Document = document,
134
134
  ): { detach: () => void; scoped: boolean } {
@@ -156,7 +156,7 @@ export function mountProbeHost(
156
156
  * Reporting a line count from the wrong element would be worse than reporting
157
157
  * none: an editor reads a number as a fact.
158
158
  */
159
- export function measureFieldAcrossBreakpoints(
159
+ function measureFieldAcrossBreakpoints(
160
160
  container: HTMLElement,
161
161
  fieldPath: string,
162
162
  scope?: string,
@@ -31,7 +31,7 @@ function throwIfError(res: { error?: string }): void {
31
31
  export type SeoGrade = 'good' | 'fair' | 'poor' | 'critical'
32
32
  export type SeoSeverity = 'critical' | 'warning' | 'info'
33
33
  export type SeoIssueStatus = 'open' | 'ignored' | 'resolved' | 'recurring'
34
- export type SeoIssueCategory =
34
+ type SeoIssueCategory =
35
35
  | 'content'
36
36
  | 'technical'
37
37
  | 'redirects'
@@ -44,7 +44,7 @@ export type RedirectStatusCode = 301 | 302 | 307 | 308 | 410
44
44
 
45
45
  // ─── Overview ────────────────────────────────────────────────────────
46
46
 
47
- export interface SeoSiteScore {
47
+ interface SeoSiteScore {
48
48
  score: number
49
49
  grade: SeoGrade
50
50
  /** Change vs the previous audit run, e.g. +6. */
@@ -93,7 +93,7 @@ export interface QuickWin {
93
93
  fixActionType: string | null
94
94
  }
95
95
 
96
- export interface SearchPerformanceRow {
96
+ interface SearchPerformanceRow {
97
97
  url: string
98
98
  title: string
99
99
  impressions: number
@@ -135,20 +135,9 @@ export async function fetchSeoOverview(): Promise<SeoOverview> {
135
135
  return res.data ?? EMPTY_OVERVIEW
136
136
  }
137
137
 
138
- export async function fetchSearchPerformance(
139
- params: {
140
- limit?: number
141
- } = {},
142
- ): Promise<SearchPerformanceRow[]> {
143
- const qs = params.limit ? `?limit=${params.limit}` : ''
144
- const res = await cmsApi<{ rows: SearchPerformanceRow[] }>(`/seo/search-performance${qs}`)
145
- throwIfError(res)
146
- return res.data?.rows ?? []
147
- }
148
-
149
138
  // ─── Per-document search insight (synced GSC + CrUX) ─────────────────
150
139
 
151
- export interface SearchInsightTopQuery {
140
+ interface SearchInsightTopQuery {
152
141
  query: string
153
142
  clicks: number
154
143
  impressions: number
@@ -201,8 +190,6 @@ export const SEO_SCHEMA_TYPES = [
201
190
  'Recipe',
202
191
  ] as const
203
192
 
204
- export type SeoSchemaType = (typeof SEO_SCHEMA_TYPES)[number]
205
-
206
193
  export interface SeoContentRecord {
207
194
  id: string
208
195
  entityType: 'page' | 'post'
@@ -423,12 +410,6 @@ export async function fetchSeoAuditRuns(): Promise<SeoAuditRun[]> {
423
410
  return res.data?.runs ?? []
424
411
  }
425
412
 
426
- export async function fetchSeoAuditRun(auditRunId: string): Promise<SeoAuditRun | null> {
427
- const res = await cmsApi<SeoAuditRun>(`/seo/audits/${encodeURIComponent(auditRunId)}`)
428
- throwIfError(res)
429
- return res.data ?? null
430
- }
431
-
432
413
  export interface SeoIssueQuery {
433
414
  severity?: SeoSeverity | 'all'
434
415
  category?: SeoIssueCategory | 'all'
@@ -566,7 +547,7 @@ export async function exportSeoFixBrief(
566
547
  }
567
548
 
568
549
  /** Issue types with inline Approve fix in the Audit tab (keep in sync with cms-core). */
569
- export const SEO_INLINE_FIX_ISSUE_TYPES = new Set([
550
+ const SEO_INLINE_FIX_ISSUE_TYPES = new Set([
570
551
  'meta-title-length',
571
552
  'meta-description-length',
572
553
  'accidental-noindex',
@@ -585,7 +566,7 @@ export function isInlineSeoFixIssue(type: string): boolean {
585
566
  return SEO_INLINE_FIX_ISSUE_TYPES.has(type)
586
567
  }
587
568
 
588
- export const SEO_CONTENT_BRIEF_ISSUE_TYPES = [
569
+ const SEO_CONTENT_BRIEF_ISSUE_TYPES = [
589
570
  'thin-content',
590
571
  'missing-h1',
591
572
  'multiple-h1',
@@ -596,7 +577,7 @@ export function supportsSeoContentBrief(type: string): boolean {
596
577
  return (SEO_CONTENT_BRIEF_ISSUE_TYPES as readonly string[]).includes(type)
597
578
  }
598
579
 
599
- export interface SeoContentBriefSection {
580
+ interface SeoContentBriefSection {
600
581
  heading: string
601
582
  bullets: string[]
602
583
  }
@@ -643,13 +624,13 @@ export async function generateSeoContentBrief(
643
624
  return { brief: res.data?.brief }
644
625
  }
645
626
 
646
- export const SEO_RESEARCH_ISSUE_TYPES = ['thin-content'] as const
627
+ const SEO_RESEARCH_ISSUE_TYPES = ['thin-content'] as const
647
628
 
648
629
  export function supportsSeoContentResearch(type: string): boolean {
649
630
  return (SEO_RESEARCH_ISSUE_TYPES as readonly string[]).includes(type)
650
631
  }
651
632
 
652
- export interface SeoContentResearchCompetitor {
633
+ interface SeoContentResearchCompetitor {
653
634
  url: string
654
635
  title: string
655
636
  note: string
@@ -688,21 +669,6 @@ export async function fetchSeoContentResearch(issueId: string): Promise<{
688
669
  }
689
670
  }
690
671
 
691
- export async function generateSeoContentResearch(
692
- issueId: string,
693
- competitorUrls?: string[],
694
- ): Promise<{ research?: SeoContentResearch; error?: string }> {
695
- const res = await cmsApi<{ research: SeoContentResearch }>(
696
- `/seo/issues/${encodeURIComponent(issueId)}/content-research`,
697
- {
698
- method: 'POST',
699
- body: JSON.stringify({ competitorUrls: competitorUrls?.filter(Boolean) }),
700
- },
701
- )
702
- if (res.error) return { error: res.error }
703
- return { research: res.data?.research }
704
- }
705
-
706
672
  export async function researchAndBriefSeoIssue(
707
673
  issueId: string,
708
674
  competitorUrls?: string[],
@@ -916,14 +882,14 @@ export interface SeoAutopilotRunSummary {
916
882
  haltedReason: string | null
917
883
  }
918
884
 
919
- export interface SeoAutopilotOutcomeWatch {
885
+ interface SeoAutopilotOutcomeWatch {
920
886
  enabled: boolean
921
887
  days: number
922
888
  dropRatio: number
923
889
  minPriorClicks: number
924
890
  }
925
891
 
926
- export interface SeoAutopilotGovernance {
892
+ interface SeoAutopilotGovernance {
927
893
  /** Settings → AI "Requires approval" on the issue-fix feature vetoes every metadata type. */
928
894
  issueFixApprovalRequired: boolean
929
895
  /** Same veto for 404-recovery redirects. */
@@ -1137,7 +1103,7 @@ export async function revertAutopilotChange(
1137
1103
 
1138
1104
  // ─── AI crawler activity ─────────────────────────────────────────────
1139
1105
 
1140
- export interface AiCrawlerRow {
1106
+ interface AiCrawlerRow {
1141
1107
  bot: string
1142
1108
  surface: string
1143
1109
  category: string
@@ -1182,7 +1148,7 @@ export interface SeoGeoEntityRef {
1182
1148
  * Why `enabled` is `false`: the kill switch in Settings → SEO (`settings`) or
1183
1149
  * the GEO tables are not migrated on the CMS database (`unavailable`).
1184
1150
  */
1185
- export type SeoGeoDisabledReason = 'settings' | 'unavailable'
1151
+ type SeoGeoDisabledReason = 'settings' | 'unavailable'
1186
1152
 
1187
1153
  export interface SeoGeoReport {
1188
1154
  enabled: boolean
@@ -1354,13 +1320,6 @@ export interface RedirectOverview {
1354
1320
  trafficRecovered: number
1355
1321
  }
1356
1322
 
1357
- export interface Redirect404 {
1358
- id: string
1359
- path: string
1360
- hits: number
1361
- lastSeenAt: string
1362
- }
1363
-
1364
1323
  export interface RedirectSuggestion {
1365
1324
  id: string
1366
1325
  fromPath: string
@@ -1581,9 +1540,10 @@ export interface SeoAiResult {
1581
1540
  }
1582
1541
 
1583
1542
  /**
1584
- * AI SEO endpoints report unavailability with two statuses: 501 (plugin-ai not
1585
- * installed) and 503 (installed but the provider isn't configured). Both map to
1586
- * the same graceful "AI unavailable" UX.
1543
+ * AI SEO endpoints report unavailability as 503 (no provider connection or
1544
+ * model configured in Settings → AI; the body carries `code` + `hint`). 501 is
1545
+ * kept for CMS builds older than @prenta/core 7. Both map to the same graceful
1546
+ * "AI unavailable" UX.
1587
1547
  */
1588
1548
  function isAiUnavailable(status?: number): boolean {
1589
1549
  return status === 501 || status === 503
@@ -1755,7 +1715,7 @@ export async function fetchLinkHealthIssues(): Promise<{
1755
1715
  // `seo/faq-signals.ts` (`FaqSignals`). The server is the contract.
1756
1716
 
1757
1717
  /** Same union as core's `SeoProposalFixStrategy`; `null` for plain metadata writes. */
1758
- export type SeoProposalFixStrategy = 'redirect' | 'fix-link' | 'insert-link' | 'add-section'
1718
+ type SeoProposalFixStrategy = 'redirect' | 'fix-link' | 'insert-link' | 'add-section'
1759
1719
 
1760
1720
  /** Why a page got a `missing-faq` proposal (28-day window). */
1761
1721
  export interface SeoFaqSignals {
@@ -1769,7 +1729,7 @@ export interface SeoFaqSignals {
1769
1729
  queries: Array<{ query: string; impressions: number; clicks: number }>
1770
1730
  }
1771
1731
 
1772
- export interface SeoFaqSectionDraft {
1732
+ interface SeoFaqSectionDraft {
1773
1733
  sectionType: 'faq'
1774
1734
  content: { heading: string; items: Array<{ question: string; answer: string }> }
1775
1735
  }
@@ -1845,7 +1805,7 @@ export interface SeoProposalBulkApplyItem {
1845
1805
  }
1846
1806
 
1847
1807
  /** Mirrors core's `BulkApplyItemStatus` (`seo/proposals-bulk-apply.ts`). */
1848
- export type SeoProposalBulkApplyItemStatus =
1808
+ type SeoProposalBulkApplyItemStatus =
1849
1809
  | 'applied'
1850
1810
  | 'stale'
1851
1811
  | 'verify-failed'
@@ -55,7 +55,7 @@ export interface AdminThemeChrome {
55
55
  diagnostics?: { navLinkCount?: number; brandPresent?: boolean }
56
56
  }
57
57
 
58
- export const THEME_CHROME_MODE_KEY = 'prenta-theme-chrome-mode'
58
+ const THEME_CHROME_MODE_KEY = 'prenta-theme-chrome-mode'
59
59
  export const THEME_CHROME_MAIN_ID = 'prenta-canvas-main'
60
60
 
61
61
  export function isThemeChromeEnabled(
@@ -21,9 +21,9 @@ import { coerceSections, type PageSection } from '@prenta/core/page-sections'
21
21
  * `UPDATE` as "Saved" rather than "Draft" — the neutral reading is the only
22
22
  * one that is true for both old and new rows.
23
23
  */
24
- export type VersionChangeType = 'CREATE' | 'UPDATE' | 'PUBLISH' | 'RESTORE' | 'DELETE'
24
+ type VersionChangeType = 'CREATE' | 'UPDATE' | 'PUBLISH' | 'RESTORE' | 'DELETE'
25
25
 
26
- export interface VersionAuthor {
26
+ interface VersionAuthor {
27
27
  id: string
28
28
  /** Display name, falling back to email and then to a non-blank placeholder. */
29
29
  name: string
@@ -5,11 +5,11 @@ export type WorkflowChromeAction = 'submit' | 'approve' | 'waiting'
5
5
  export const SUBMIT_FOR_REVIEW_LABEL = 'Submit for review'
6
6
  export const APPROVE_LABEL = 'Approve'
7
7
  export const REQUEST_CHANGES_LABEL = 'Request changes'
8
- export const WAITING_FOR_REVIEW_LABEL = 'Waiting for review.'
8
+ const WAITING_FOR_REVIEW_LABEL = 'Waiting for review.'
9
9
  export const REVIEWER_NOTE_PREFIX = 'Reviewer note:'
10
10
  export const SUBMIT_BEFORE_PUBLISH_ERROR = 'Submit for review before publishing.'
11
- export const SAVE_DRAFT_LABEL = 'Save Draft'
12
- export const UPDATE_LIVE_LABEL = 'Update live'
11
+ const SAVE_DRAFT_LABEL = 'Save Draft'
12
+ const UPDATE_LIVE_LABEL = 'Update live'
13
13
 
14
14
  const REVIEWER_ROLES = new Set(['ADMIN', 'EDITOR'])
15
15
  const SUBMIT_ROLES = new Set(['ADMIN', 'EDITOR', 'AUTHOR'])
@@ -18,7 +18,7 @@ export interface CaptchaConfig {
18
18
  * sidebar so integrators only need to set it once. `logo` should be a URL or
19
19
  * data URL; pass `null` to opt out of any logo entirely.
20
20
  */
21
- export interface LoginBrandingConfig {
21
+ interface LoginBrandingConfig {
22
22
  logo?: string | null
23
23
  name?: string
24
24
  tagline?: string
@@ -342,8 +342,9 @@ export function MediaBrowser({ onNavigate, currentPath }: MediaBrowserProps) {
342
342
  }>('/ai/media-metadata', { method: 'POST', body: JSON.stringify({ mediaId: activeItem.id }) })
343
343
 
344
344
  if (res.error) {
345
- // AI plugin/provider not configured (e.g. local dev): fall back to a
346
- // basic filename-derived suggestion so the action still helps.
345
+ // No AI provider / model configured in Settings → AI (e.g. local dev):
346
+ // fall back to a basic filename-derived suggestion so the action still
347
+ // helps.
347
348
  const base = (activeItem.name ?? '')
348
349
  .replace(/\.\w+$/, '')
349
350
  .replace(/[-_]/g, ' ')
@@ -7,7 +7,7 @@ import { SEOPanel, type SEOData } from '../components/SEOPanel.js'
7
7
  import { useApiData } from '../lib/useApiData.js'
8
8
  import { cmsApi } from '../lib/api.js'
9
9
 
10
- export interface PageEditorProps {
10
+ interface PageEditorProps {
11
11
  id?: string
12
12
  onNavigate?: (path: string) => void
13
13
  }
@@ -1451,7 +1451,7 @@ function formatDateShort(input: string): string {
1451
1451
  return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })
1452
1452
  }
1453
1453
 
1454
- export function pageNumbers(current: number, total: number): Array<number | '…'> {
1454
+ function pageNumbers(current: number, total: number): Array<number | '…'> {
1455
1455
  if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1)
1456
1456
  const out: Array<number | '…'> = [1]
1457
1457
  const start = Math.max(2, current - 1)
@@ -24,7 +24,7 @@ import { FolderTree, type FolderSelection } from '../components/FolderTree.js'
24
24
 
25
25
  type PageSortKey = 'title' | 'author' | 'template' | 'status' | 'date'
26
26
 
27
- export interface PagesProps {
27
+ interface PagesProps {
28
28
  onNavigate?: (path: string) => void
29
29
  }
30
30
 
@@ -825,7 +825,8 @@ function PostRow({
825
825
  {post.title}
826
826
  </span>
827
827
  <span className="text-muted-foreground block truncate text-xs">
828
- /{post.postTypeId}/{post.slug || '—'}
828
+ {post.publicPath}
829
+ {post.slug ? '' : '—'}
829
830
  </span>
830
831
  </span>
831
832
  </button>
@@ -22,7 +22,7 @@ import { cmsApi } from '../lib/api.js'
22
22
 
23
23
  type RedirectSortKey = 'from' | 'to' | 'type' | 'hits' | 'active'
24
24
 
25
- export interface RedirectsProps {
25
+ interface RedirectsProps {
26
26
  onNavigate?: (path: string) => void
27
27
  }
28
28
 
@@ -10,27 +10,18 @@ export {
10
10
  SECTION_TYPES,
11
11
  SECTION_TYPE_LIST,
12
12
  getSectionType,
13
- getSectionTypesForScope,
14
13
  getAddableSectionTypesForScope,
15
- isSectionTypeAddable,
16
14
  isSectionTypeHideable,
17
15
  isSectionEffectivelyVisible,
18
- sectionTypeLabel,
19
- isKnownSectionType,
20
16
  resolveRepeaterField,
21
- isRepeaterField,
22
17
  blankRepeaterRow,
23
18
  } from '@prenta/core/page-sections'
24
19
 
25
20
  export type {
26
21
  SectionTypeId,
27
22
  SectionScope,
28
- SectionFieldType,
29
23
  SectionFieldDef,
30
24
  SectionSettings,
31
- SectionTypeDef,
32
25
  ResolvedRepeater,
33
- RepeaterRow,
34
26
  PostHeaderConfig,
35
- PostTemplate,
36
27
  } from '@prenta/core/page-sections'
@@ -62,7 +62,7 @@ export interface AdminSectionPostContext {
62
62
  * Paint budget to protect, but sections-react renderers call `claim()` during
63
63
  * render, so the shape must exist or a real renderer throws on its first image.
64
64
  */
65
- export interface AdminLcpController {
65
+ interface AdminLcpController {
66
66
  claim(): boolean
67
67
  readonly claimed: boolean
68
68
  }
@@ -1,12 +1,4 @@
1
- export {
2
- ThemeChromeProvider,
3
- useThemeChromeSpec,
4
- useThemeChromeContext,
5
- } from './ThemeChromeProvider.js'
1
+ export { ThemeChromeProvider, useThemeChromeContext } from './ThemeChromeProvider.js'
6
2
  export { ThemeChromeFrame } from './ThemeChromeFrame.js'
7
- export type { ThemeChromeFrameProps, ThemeChromeHealthState } from './ThemeChromeFrame.js'
3
+
8
4
  export { ThemeChromeHealth } from './ThemeChromeHealth.js'
9
- export type { ThemeChromeHealthProps } from './ThemeChromeHealth.js'
10
- export { ThemeChromeModeToggle } from './ThemeChromeModeToggle.js'
11
- export type { ThemeChromeModeToggleProps } from './ThemeChromeModeToggle.js'
12
- export { useThemeChromeMode } from './useThemeChromeMode.js'
@@ -5,9 +5,6 @@
5
5
  * mid-migration; import from `../../lib/breakpoints.js` in new code.
6
6
  */
7
7
  export {
8
- BREAKPOINTS as VIEWPORTS,
9
8
  DEFAULT_BREAKPOINT as DEFAULT_VIEWPORT,
10
- getBreakpoint as getViewport,
11
- type Breakpoint as Viewport,
12
9
  type BreakpointId as ViewportId,
13
10
  } from '../../lib/breakpoints.js'
@@ -54,7 +54,14 @@ export function AISettingsTab({ role, config }: AISettingsTabProps) {
54
54
  'Connect an AI provider under Provider connections before generating a brand voice profile.',
55
55
  }
56
56
  }
57
- if (!ai.form.defaultModelId) {
57
+ // The runtime resolves the provider's curated default when no default
58
+ // model is set. Only built-in providers have a curated catalog — a
59
+ // `custom` endpoint cannot supply that default, so a custom-only site (or
60
+ // one with no keyed built-in connection) still needs an explicit model.
61
+ const hasKeyedProvider = ai.providers.some(
62
+ (p) => p.enabled && p.hasKey && p.provider !== 'custom',
63
+ )
64
+ if (!ai.form.defaultModelId && !hasKeyedProvider) {
58
65
  return {
59
66
  ready: false,
60
67
  message:
@@ -67,8 +74,13 @@ export function AISettingsTab({ role, config }: AISettingsTabProps) {
67
74
  message: 'Save your AI settings before generating a brand voice profile.',
68
75
  }
69
76
  }
70
- return { ready: true }
71
- }, [ai.dirty, ai.form.defaultModelId, ai.form.features, ai.providers.length])
77
+ return {
78
+ ready: true,
79
+ ...(ai.form.defaultModelId
80
+ ? {}
81
+ : { note: 'No default model selected — using the provider’s recommended default.' }),
82
+ }
83
+ }, [ai.dirty, ai.form.defaultModelId, ai.form.features, ai.providers])
72
84
 
73
85
  if (ai.loading) {
74
86
  return (
@@ -9,7 +9,7 @@ import {
9
9
  type BrandAlignmentScoreResult,
10
10
  type BrandVoiceStructuredProfile,
11
11
  } from '@prenta/core/brand-voice'
12
- import { AlertTriangle, Download, FileUp, Link2, Loader2, Sparkles } from 'lucide-react'
12
+ import { AlertTriangle, Download, FileUp, Info, Link2, Loader2, Sparkles } from 'lucide-react'
13
13
  import { useCallback, useId, useMemo, useRef, useState } from 'react'
14
14
  import { toast } from 'sonner'
15
15
  import { BrandVoiceAlignmentPanel } from './brand-voice/BrandVoiceAlignmentPanel.js'
@@ -95,7 +95,10 @@ function downloadTextFile(filename: string, contents: string) {
95
95
  */
96
96
  export interface BrandVoiceAiReadiness {
97
97
  ready: boolean
98
+ /** Why AI generation is blocked (rendered as a warning when `ready` is false). */
98
99
  message?: string
100
+ /** Informational context when ready — e.g. running on the provider's default model. */
101
+ note?: string
99
102
  }
100
103
 
101
104
  export function BrandVoiceCard({
@@ -348,6 +351,13 @@ export function BrandVoiceCard({
348
351
  </div>
349
352
  )}
350
353
 
354
+ {aiReadiness?.ready && aiReadiness.note && (
355
+ <p className="text-muted-foreground flex items-start gap-2 text-sm">
356
+ <Info size={16} className="mt-0.5 shrink-0" aria-hidden="true" />
357
+ <span>{aiReadiness.note}</span>
358
+ </p>
359
+ )}
360
+
351
361
  <div className="flex flex-wrap items-center gap-2">
352
362
  <button
353
363
  type="button"
@@ -5,7 +5,7 @@ import { BRAND_VOICE_CHANNEL_PRESETS } from '@prenta/core/brand-voice'
5
5
  import { Loader2, Wand2 } from 'lucide-react'
6
6
  import { useId, useState } from 'react'
7
7
  import { toast } from 'sonner'
8
- import { fixBrandVoiceContent } from '../../../lib/brand-voice-client.js'
8
+ import { brandVoiceFixAiNotice, fixBrandVoiceContent } from '../../../lib/brand-voice-client.js'
9
9
 
10
10
  const INPUT_CLASS =
11
11
  'w-full rounded-md border border-border bg-input-background px-3 py-2 text-base text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-60'
@@ -78,6 +78,8 @@ export function BrandVoiceAlignmentPanel({
78
78
  ? 'Copy revised to match brand voice.'
79
79
  : 'Applied brand voice rule fixes.',
80
80
  )
81
+ const notice = brandVoiceFixAiNotice(res.result)
82
+ if (notice) toast.info(notice)
81
83
  } finally {
82
84
  setFixing(false)
83
85
  }
@@ -21,13 +21,13 @@ const CAPABILITY_LABELS: Record<string, string> = {
21
21
  longContext: 'long context',
22
22
  }
23
23
 
24
- export function findModel(models: AiModelView[], id: string | undefined): AiModelView | undefined {
24
+ function findModel(models: AiModelView[], id: string | undefined): AiModelView | undefined {
25
25
  if (!id) return undefined
26
26
  return models.find((m) => m.id === id || m.providerModelId === id)
27
27
  }
28
28
 
29
29
  /** Required capabilities a model is missing. Unknown capability strings are ignored. */
30
- export function missingCapabilities(model: AiModelView | undefined, required: string[]): string[] {
30
+ function missingCapabilities(model: AiModelView | undefined, required: string[]): string[] {
31
31
  const wanted = required.filter((c) => c in CAPABILITY_LABELS)
32
32
  if (!model) return wanted
33
33
  return wanted.filter((c) => !model.capabilities?.[c])
@@ -31,7 +31,7 @@ export type AiConnectionStatus =
31
31
  | 'provider_down'
32
32
  | 'needs_setup'
33
33
  | 'unknown'
34
- export type AiModelStatus = 'available' | 'unavailable' | 'deprecated' | 'unknown'
34
+ type AiModelStatus = 'available' | 'unavailable' | 'deprecated' | 'unknown'
35
35
 
36
36
  export interface AiModelView {
37
37
  id: string
@@ -147,7 +147,7 @@ const EMPTY_BRAND_VOICE: BrandVoiceForm = {
147
147
  }
148
148
 
149
149
  /** The buffered (Save-bar) portion of AI settings. */
150
- export interface AiSettingsForm {
150
+ interface AiSettingsForm {
151
151
  defaultProviderId: string
152
152
  defaultModelId: string
153
153
  features: AiFeatureView[]
@@ -453,7 +453,12 @@ export function useAiSettings(canEdit: boolean): UseAiSettings {
453
453
  method: 'POST',
454
454
  body: JSON.stringify(input),
455
455
  })
456
- if (res.error) return { ok: false, error: res.error }
456
+ if (res.error) {
457
+ // 503 "not configured" bodies carry the runtime's setup `hint`
458
+ // separately from `error` (same contract as every other AI route).
459
+ const hint = (res.errorBody as { hint?: unknown } | undefined)?.hint
460
+ return { ok: false, error: typeof hint === 'string' ? `${res.error} ${hint}` : res.error }
461
+ }
457
462
  if (!res.data?.draft) return { ok: false, error: 'No draft profile was returned.' }
458
463
  const structured = res.data.draft.document?.trim()
459
464
  ? parseStructuredProfile(res.data.draft.document)
@@ -30,10 +30,10 @@ import { cmsApi } from '../../lib/api.js'
30
30
  import { BRAND_UPDATED_EVENT } from '../../lib/favicon.js'
31
31
  import type { SettingsSourceMetadata, ValidationIssue } from './useGeneralSettings.js'
32
32
 
33
- export type SaveState = 'idle' | 'saving' | 'success' | 'error'
33
+ type SaveState = 'idle' | 'saving' | 'success' | 'error'
34
34
 
35
35
  /** Theme + typography + motion form (the `appearance` settings blob). */
36
- export interface AppearanceForm {
36
+ interface AppearanceForm {
37
37
  colorMode: ColorMode
38
38
  accent: string
39
39
  density: SidebarDensity
@@ -42,7 +42,7 @@ export interface AppearanceForm {
42
42
  }
43
43
 
44
44
  /** Brand asset references + admin branding mode (the `brand` settings blob). */
45
- export interface BrandForm {
45
+ interface BrandForm {
46
46
  mode: AdminBrandingMode
47
47
  publicPrimaryLogoAssetId?: string
48
48
  publicLogoMarkAssetId?: string
@@ -64,7 +64,7 @@ export type BrandAssetField =
64
64
  | 'adminDarkLogoAssetId'
65
65
  | 'faviconSourceAssetId'
66
66
 
67
- export const FIELD_ROLE: Record<BrandAssetField, BrandAssetRole> = {
67
+ const FIELD_ROLE: Record<BrandAssetField, BrandAssetRole> = {
68
68
  publicPrimaryLogoAssetId: 'publicPrimaryLogo',
69
69
  publicLogoMarkAssetId: 'publicLogoMark',
70
70
  publicDarkLogoAssetId: 'publicDarkLogo',
@@ -74,8 +74,6 @@ export const FIELD_ROLE: Record<BrandAssetField, BrandAssetRole> = {
74
74
  faviconSourceAssetId: 'faviconSource',
75
75
  }
76
76
 
77
- export type { AssetPreview } from '../../lib/appearance-api.js'
78
-
79
77
  /** An existing media-library asset chosen for a brand field via the picker. */
80
78
  export interface SelectedBrandAsset {
81
79
  id: string
@@ -5,7 +5,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
5
5
  import { cmsApi } from '../../lib/api.js'
6
6
 
7
7
  export type EmailProviderKey = 'resend' | 'smtpcom' | 'ses' | 'smtp'
8
- export type SaveState = 'idle' | 'saving' | 'success' | 'error'
8
+ type SaveState = 'idle' | 'saving' | 'success' | 'error'
9
9
 
10
10
  export const EMAIL_PROVIDER_OPTIONS: Array<{ value: EmailProviderKey; label: string }> = [
11
11
  { value: 'resend', label: 'Resend' },
@@ -15,7 +15,7 @@ export const EMAIL_PROVIDER_OPTIONS: Array<{ value: EmailProviderKey; label: str
15
15
  ]
16
16
 
17
17
  /** Form state. Secrets are write-only: inputs start empty and never echo stored values. */
18
- export interface EmailSettingsForm {
18
+ interface EmailSettingsForm {
19
19
  /** Empty string = email delivery not configured here. */
20
20
  provider: EmailProviderKey | ''
21
21
  from: string