@prenta/admin 1.17.1 → 1.17.2

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 (268) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/__tests__/views/ai-settings.render.test.js +67 -1
  3. package/dist/__tests__/views/ai-settings.render.test.js.map +1 -1
  4. package/dist/components/CoauthorResultPopover.d.ts.map +1 -1
  5. package/dist/components/CoauthorResultPopover.js +4 -1
  6. package/dist/components/CoauthorResultPopover.js.map +1 -1
  7. package/dist/components/CommentSidePanel.d.ts +0 -7
  8. package/dist/components/CommentSidePanel.d.ts.map +1 -1
  9. package/dist/components/CommentSidePanel.js +0 -2
  10. package/dist/components/CommentSidePanel.js.map +1 -1
  11. package/dist/components/EditorRail.d.ts +2 -1
  12. package/dist/components/EditorRail.d.ts.map +1 -1
  13. package/dist/components/MediaPickerModal.d.ts +2 -1
  14. package/dist/components/MediaPickerModal.d.ts.map +1 -1
  15. package/dist/components/TipTapEditor.d.ts +3 -2
  16. package/dist/components/TipTapEditor.d.ts.map +1 -1
  17. package/dist/components/forms/EntriesTable.d.ts +0 -1
  18. package/dist/components/forms/EntriesTable.d.ts.map +1 -1
  19. package/dist/components/forms/EntriesTable.js +1 -1
  20. package/dist/components/forms/EntriesTable.js.map +1 -1
  21. package/dist/components/pages/tag-badges.d.ts +2 -2
  22. package/dist/components/pages/tag-badges.d.ts.map +1 -1
  23. package/dist/components/pages/tag-badges.js.map +1 -1
  24. package/dist/components/seo/FaqProposalPreview.d.ts +2 -3
  25. package/dist/components/seo/FaqProposalPreview.d.ts.map +1 -1
  26. package/dist/components/seo/FaqProposalPreview.js +1 -1
  27. package/dist/components/seo/FaqProposalPreview.js.map +1 -1
  28. package/dist/components/seo/SeoCopilotDrawer.js +1 -1
  29. package/dist/components/seo/SeoCopilotDrawer.js.map +1 -1
  30. package/dist/components/seo/SeoEditorPane.d.ts.map +1 -1
  31. package/dist/components/seo/SeoEditorPane.js +4 -1
  32. package/dist/components/seo/SeoEditorPane.js.map +1 -1
  33. package/dist/components/ui/Badge.d.ts +3 -9
  34. package/dist/components/ui/Badge.d.ts.map +1 -1
  35. package/dist/components/ui/Badge.js +2 -3
  36. package/dist/components/ui/Badge.js.map +1 -1
  37. package/dist/components/ui/Button.d.ts +2 -2
  38. package/dist/components/ui/Button.d.ts.map +1 -1
  39. package/dist/components/ui/Button.js +0 -1
  40. package/dist/components/ui/Button.js.map +1 -1
  41. package/dist/components/ui/Card.d.ts +2 -3
  42. package/dist/components/ui/Card.d.ts.map +1 -1
  43. package/dist/components/ui/Card.js +0 -4
  44. package/dist/components/ui/Card.js.map +1 -1
  45. package/dist/components/ui/Input.d.ts +6 -8
  46. package/dist/components/ui/Input.d.ts.map +1 -1
  47. package/dist/components/ui/Input.js +6 -6
  48. package/dist/components/ui/Input.js.map +1 -1
  49. package/dist/components/ui/Modal.d.ts +1 -1
  50. package/dist/components/ui/Modal.d.ts.map +1 -1
  51. package/dist/components/ui/ResizableAside.d.ts +0 -13
  52. package/dist/components/ui/ResizableAside.d.ts.map +1 -1
  53. package/dist/components/ui/ResizableAside.js +5 -5
  54. package/dist/components/ui/ResizableAside.js.map +1 -1
  55. package/dist/components/ui/StatusBadge.d.ts +2 -2
  56. package/dist/components/ui/StatusBadge.d.ts.map +1 -1
  57. package/dist/components/ui/StatusBadge.js +0 -1
  58. package/dist/components/ui/StatusBadge.js.map +1 -1
  59. package/dist/components/ui/Toggle.d.ts +2 -8
  60. package/dist/components/ui/Toggle.d.ts.map +1 -1
  61. package/dist/components/ui/Toggle.js +0 -1
  62. package/dist/components/ui/Toggle.js.map +1 -1
  63. package/dist/fields/FieldRenderer.d.ts +2 -1
  64. package/dist/fields/FieldRenderer.d.ts.map +1 -1
  65. package/dist/fields/index.d.ts +0 -17
  66. package/dist/fields/index.d.ts.map +1 -1
  67. package/dist/fields/index.js +0 -15
  68. package/dist/fields/index.js.map +1 -1
  69. package/dist/layout/primitives/tokens.d.ts +0 -2
  70. package/dist/layout/primitives/tokens.d.ts.map +1 -1
  71. package/dist/layout/primitives/tokens.js +0 -6
  72. package/dist/layout/primitives/tokens.js.map +1 -1
  73. package/dist/lib/brand-voice-client.d.ts +14 -1
  74. package/dist/lib/brand-voice-client.d.ts.map +1 -1
  75. package/dist/lib/brand-voice-client.js +7 -0
  76. package/dist/lib/brand-voice-client.js.map +1 -1
  77. package/dist/lib/coauthor-client.d.ts +2 -1
  78. package/dist/lib/coauthor-client.d.ts.map +1 -1
  79. package/dist/lib/collection-schema.d.ts +0 -9
  80. package/dist/lib/collection-schema.d.ts.map +1 -1
  81. package/dist/lib/collection-schema.js +2 -2
  82. package/dist/lib/collection-schema.js.map +1 -1
  83. package/dist/lib/editor-seo-content.d.ts +0 -10
  84. package/dist/lib/editor-seo-content.d.ts.map +1 -1
  85. package/dist/lib/editor-seo-content.js +0 -13
  86. package/dist/lib/editor-seo-content.js.map +1 -1
  87. package/dist/lib/forms-service.d.ts +3 -21
  88. package/dist/lib/forms-service.d.ts.map +1 -1
  89. package/dist/lib/forms-service.js +0 -25
  90. package/dist/lib/forms-service.js.map +1 -1
  91. package/dist/lib/notifications-client.d.ts +4 -3
  92. package/dist/lib/notifications-client.d.ts.map +1 -1
  93. package/dist/lib/page-audit-input.d.ts +0 -10
  94. package/dist/lib/page-audit-input.d.ts.map +1 -1
  95. package/dist/lib/page-audit-input.js +1 -1
  96. package/dist/lib/page-audit-input.js.map +1 -1
  97. package/dist/lib/page-editor-service.d.ts +2 -4
  98. package/dist/lib/page-editor-service.d.ts.map +1 -1
  99. package/dist/lib/page-editor-service.js +3 -3
  100. package/dist/lib/page-editor-service.js.map +1 -1
  101. package/dist/lib/pages-service.d.ts +2 -13
  102. package/dist/lib/pages-service.d.ts.map +1 -1
  103. package/dist/lib/pages-service.js +3 -24
  104. package/dist/lib/pages-service.js.map +1 -1
  105. package/dist/lib/plan.d.ts +2 -1
  106. package/dist/lib/plan.d.ts.map +1 -1
  107. package/dist/lib/post-editor-service.d.ts +3 -13
  108. package/dist/lib/post-editor-service.d.ts.map +1 -1
  109. package/dist/lib/post-editor-service.js +5 -6
  110. package/dist/lib/post-editor-service.js.map +1 -1
  111. package/dist/lib/post-types-service.d.ts +2 -3
  112. package/dist/lib/post-types-service.d.ts.map +1 -1
  113. package/dist/lib/post-types-service.js +1 -1
  114. package/dist/lib/post-types-service.js.map +1 -1
  115. package/dist/lib/posts-service.d.ts +2 -1
  116. package/dist/lib/posts-service.d.ts.map +1 -1
  117. package/dist/lib/preview-link.d.ts +0 -9
  118. package/dist/lib/preview-link.d.ts.map +1 -1
  119. package/dist/lib/preview-link.js +0 -15
  120. package/dist/lib/preview-link.js.map +1 -1
  121. package/dist/lib/rail-width.d.ts +0 -1
  122. package/dist/lib/rail-width.d.ts.map +1 -1
  123. package/dist/lib/rail-width.js +1 -1
  124. package/dist/lib/rail-width.js.map +1 -1
  125. package/dist/lib/reflow-probe.d.ts +0 -67
  126. package/dist/lib/reflow-probe.d.ts.map +1 -1
  127. package/dist/lib/reflow-probe.js +4 -4
  128. package/dist/lib/reflow-probe.js.map +1 -1
  129. package/dist/lib/seo-service.d.ts +14 -32
  130. package/dist/lib/seo-service.d.ts.map +1 -1
  131. package/dist/lib/seo-service.js +7 -26
  132. package/dist/lib/seo-service.js.map +1 -1
  133. package/dist/lib/theme-chrome.d.ts +0 -1
  134. package/dist/lib/theme-chrome.d.ts.map +1 -1
  135. package/dist/lib/theme-chrome.js +1 -1
  136. package/dist/lib/theme-chrome.js.map +1 -1
  137. package/dist/lib/version-history.d.ts +2 -2
  138. package/dist/lib/version-history.d.ts.map +1 -1
  139. package/dist/lib/workflow-chrome.d.ts +0 -3
  140. package/dist/lib/workflow-chrome.d.ts.map +1 -1
  141. package/dist/lib/workflow-chrome.js +3 -3
  142. package/dist/lib/workflow-chrome.js.map +1 -1
  143. package/dist/views/Login.d.ts +2 -1
  144. package/dist/views/Login.d.ts.map +1 -1
  145. package/dist/views/MediaBrowser.d.ts.map +1 -1
  146. package/dist/views/MediaBrowser.js +3 -2
  147. package/dist/views/MediaBrowser.js.map +1 -1
  148. package/dist/views/PageEditor.d.ts +2 -1
  149. package/dist/views/PageEditor.d.ts.map +1 -1
  150. package/dist/views/Pages/PagesListView.d.ts +0 -1
  151. package/dist/views/Pages/PagesListView.d.ts.map +1 -1
  152. package/dist/views/Pages/PagesListView.js +1 -1
  153. package/dist/views/Pages/PagesListView.js.map +1 -1
  154. package/dist/views/Pages.d.ts +2 -1
  155. package/dist/views/Pages.d.ts.map +1 -1
  156. package/dist/views/Redirects.d.ts +2 -1
  157. package/dist/views/Redirects.d.ts.map +1 -1
  158. package/dist/views/page-editor/section-types.d.ts +2 -2
  159. package/dist/views/page-editor/section-types.d.ts.map +1 -1
  160. package/dist/views/page-editor/section-types.js +1 -1
  161. package/dist/views/page-editor/section-types.js.map +1 -1
  162. package/dist/views/page-editor/sections/admin-renderers.d.ts +2 -1
  163. package/dist/views/page-editor/sections/admin-renderers.d.ts.map +1 -1
  164. package/dist/views/page-editor/theme-chrome/index.d.ts +1 -6
  165. package/dist/views/page-editor/theme-chrome/index.d.ts.map +1 -1
  166. package/dist/views/page-editor/theme-chrome/index.js +1 -3
  167. package/dist/views/page-editor/theme-chrome/index.js.map +1 -1
  168. package/dist/views/page-editor/viewports.d.ts +1 -1
  169. package/dist/views/page-editor/viewports.d.ts.map +1 -1
  170. package/dist/views/page-editor/viewports.js +1 -1
  171. package/dist/views/page-editor/viewports.js.map +1 -1
  172. package/dist/views/settings/AISettingsTab.d.ts.map +1 -1
  173. package/dist/views/settings/AISettingsTab.js +13 -3
  174. package/dist/views/settings/AISettingsTab.js.map +1 -1
  175. package/dist/views/settings/BrandVoiceCard.d.ts +3 -0
  176. package/dist/views/settings/BrandVoiceCard.d.ts.map +1 -1
  177. package/dist/views/settings/BrandVoiceCard.js +2 -2
  178. package/dist/views/settings/BrandVoiceCard.js.map +1 -1
  179. package/dist/views/settings/brand-voice/BrandVoiceAlignmentPanel.d.ts.map +1 -1
  180. package/dist/views/settings/brand-voice/BrandVoiceAlignmentPanel.js +4 -1
  181. package/dist/views/settings/brand-voice/BrandVoiceAlignmentPanel.js.map +1 -1
  182. package/dist/views/settings/featureModel.d.ts +0 -3
  183. package/dist/views/settings/featureModel.d.ts.map +1 -1
  184. package/dist/views/settings/featureModel.js +2 -2
  185. package/dist/views/settings/featureModel.js.map +1 -1
  186. package/dist/views/settings/useAiSettings.d.ts +2 -2
  187. package/dist/views/settings/useAiSettings.d.ts.map +1 -1
  188. package/dist/views/settings/useAiSettings.js +6 -2
  189. package/dist/views/settings/useAiSettings.js.map +1 -1
  190. package/dist/views/settings/useAppearanceSettings.d.ts +5 -6
  191. package/dist/views/settings/useAppearanceSettings.d.ts.map +1 -1
  192. package/dist/views/settings/useAppearanceSettings.js +1 -1
  193. package/dist/views/settings/useAppearanceSettings.js.map +1 -1
  194. package/dist/views/settings/useEmailSettings.d.ts +3 -2
  195. package/dist/views/settings/useEmailSettings.d.ts.map +1 -1
  196. package/dist/views/settings/useGeneralSettings.d.ts +2 -11
  197. package/dist/views/settings/useGeneralSettings.d.ts.map +1 -1
  198. package/dist/views/settings/useGeneralSettings.js +3 -3
  199. package/dist/views/settings/useGeneralSettings.js.map +1 -1
  200. package/dist/views/settings/useSeoSettings.d.ts +1 -1
  201. package/dist/views/settings/useSeoSettings.d.ts.map +1 -1
  202. package/dist/views/settings/useUpdates.d.ts +1 -3
  203. package/dist/views/settings/useUpdates.d.ts.map +1 -1
  204. package/dist/views/settings/useUpdates.js +1 -1
  205. package/dist/views/settings/useUpdates.js.map +1 -1
  206. package/package.json +4 -4
  207. package/src/__tests__/views/ai-settings.render.test.tsx +86 -2
  208. package/src/components/CoauthorResultPopover.tsx +3 -1
  209. package/src/components/CommentSidePanel.tsx +0 -1
  210. package/src/components/EditorRail.tsx +1 -1
  211. package/src/components/MediaPickerModal.tsx +1 -1
  212. package/src/components/TipTapEditor.tsx +2 -2
  213. package/src/components/forms/EntriesTable.tsx +1 -1
  214. package/src/components/pages/tag-badges.tsx +1 -1
  215. package/src/components/seo/FaqProposalPreview.tsx +2 -2
  216. package/src/components/seo/SeoCopilotDrawer.tsx +1 -1
  217. package/src/components/seo/SeoEditorPane.tsx +3 -1
  218. package/src/components/ui/Badge.tsx +4 -6
  219. package/src/components/ui/Button.tsx +1 -3
  220. package/src/components/ui/Card.tsx +1 -14
  221. package/src/components/ui/Input.tsx +7 -15
  222. package/src/components/ui/Modal.tsx +1 -1
  223. package/src/components/ui/ResizableAside.tsx +5 -5
  224. package/src/components/ui/StatusBadge.tsx +1 -3
  225. package/src/components/ui/Toggle.tsx +1 -3
  226. package/src/fields/FieldRenderer.tsx +1 -1
  227. package/src/fields/index.ts +0 -17
  228. package/src/layout/primitives/tokens.ts +0 -7
  229. package/src/lib/brand-voice-client.ts +18 -1
  230. package/src/lib/coauthor-client.ts +1 -1
  231. package/src/lib/collection-schema.ts +2 -2
  232. package/src/lib/editor-seo-content.ts +0 -22
  233. package/src/lib/forms-service.ts +2 -42
  234. package/src/lib/notifications-client.ts +3 -3
  235. package/src/lib/page-audit-input.ts +1 -1
  236. package/src/lib/page-editor-service.ts +1 -3
  237. package/src/lib/pages-service.ts +4 -29
  238. package/src/lib/plan.ts +1 -1
  239. package/src/lib/post-editor-service.ts +4 -10
  240. package/src/lib/post-types-service.ts +2 -2
  241. package/src/lib/posts-service.ts +1 -1
  242. package/src/lib/preview-link.ts +0 -14
  243. package/src/lib/rail-width.ts +1 -1
  244. package/src/lib/reflow-probe.ts +4 -4
  245. package/src/lib/seo-service.ts +20 -60
  246. package/src/lib/theme-chrome.ts +1 -1
  247. package/src/lib/version-history.ts +2 -2
  248. package/src/lib/workflow-chrome.ts +3 -3
  249. package/src/views/Login.tsx +1 -1
  250. package/src/views/MediaBrowser.tsx +3 -2
  251. package/src/views/PageEditor.tsx +1 -1
  252. package/src/views/Pages/PagesListView.tsx +1 -1
  253. package/src/views/Pages.tsx +1 -1
  254. package/src/views/Redirects.tsx +1 -1
  255. package/src/views/page-editor/section-types.ts +0 -9
  256. package/src/views/page-editor/sections/admin-renderers.tsx +1 -1
  257. package/src/views/page-editor/theme-chrome/index.ts +2 -10
  258. package/src/views/page-editor/viewports.ts +0 -3
  259. package/src/views/settings/AISettingsTab.tsx +15 -3
  260. package/src/views/settings/BrandVoiceCard.tsx +11 -1
  261. package/src/views/settings/brand-voice/BrandVoiceAlignmentPanel.tsx +3 -1
  262. package/src/views/settings/featureModel.ts +2 -2
  263. package/src/views/settings/useAiSettings.ts +8 -3
  264. package/src/views/settings/useAppearanceSettings.ts +4 -6
  265. package/src/views/settings/useEmailSettings.ts +2 -2
  266. package/src/views/settings/useGeneralSettings.ts +5 -10
  267. package/src/views/settings/useSeoSettings.ts +1 -1
  268. package/src/views/settings/useUpdates.ts +2 -2
@@ -34,21 +34,16 @@ export type {
34
34
  FormSchemaVersion,
35
35
  FormField,
36
36
  FormFieldType,
37
- FormFieldOption,
38
- FieldValidationRules,
39
37
  FormAnalyticsConfig,
40
38
  FormNotificationSettings,
41
39
  FormEmbedSettings,
42
40
  FormWebhookConfig,
43
- FormWebhookEvent,
44
41
  FormStatus,
45
42
  FormStats,
46
43
  FormsSidebarCount,
47
44
  FormSubmission,
48
- FormSubmissionFile,
49
45
  FetchFormEntriesParams,
50
46
  EntryCounts,
51
- SpamStatus,
52
47
  } from '@prenta/core'
53
48
 
54
49
  /**
@@ -170,34 +165,6 @@ export async function updateForm(
170
165
  return { data: res.data, error: res.error }
171
166
  }
172
167
 
173
- export async function duplicateForm(
174
- id: string,
175
- ): Promise<{ data?: FormDefinition; error?: string }> {
176
- const res = await cmsApi<FormDefinition>(`/forms/${encodeURIComponent(id)}/duplicate`, {
177
- method: 'POST',
178
- })
179
- return { data: res.data, error: res.error }
180
- }
181
-
182
- /** Move a form to the archived state (hidden from embed pickers, no new submissions). */
183
- export async function archiveForm(id: string): Promise<{ error?: string }> {
184
- return updateForm(id, { status: 'archived' })
185
- }
186
-
187
- /** Restore an archived form back to draft. */
188
- export async function restoreForm(id: string): Promise<{ error?: string }> {
189
- return updateForm(id, { status: 'draft' })
190
- }
191
-
192
- export async function deleteForm(
193
- id: string,
194
- opts: { force?: boolean } = {},
195
- ): Promise<{ error?: string }> {
196
- const qs = opts.force ? '?force=true' : ''
197
- const res = await cmsApi(`/forms/${encodeURIComponent(id)}${qs}`, { method: 'DELETE' })
198
- return { error: res.error }
199
- }
200
-
201
168
  // ─── Schema ──────────────────────────────────────────────────────────────
202
169
 
203
170
  export async function fetchFormSchema(id: string): Promise<FormSchemaResult> {
@@ -496,8 +463,8 @@ export async function downloadEntriesExport(
496
463
 
497
464
  // ─── Delivery attempts (#675) ─────────────────────────────────────────────
498
465
 
499
- export type FormDeliveryChannel = 'email_notify' | 'email_autoresponder' | 'form_webhook'
500
- export type FormDeliveryStatus = 'pending' | 'retrying' | 'success' | 'failed' | 'skipped'
466
+ type FormDeliveryChannel = 'email_notify' | 'email_autoresponder' | 'form_webhook'
467
+ type FormDeliveryStatus = 'pending' | 'retrying' | 'success' | 'failed' | 'skipped'
501
468
 
502
469
  export interface FormDeliveryAttempt {
503
470
  id: string
@@ -560,10 +527,3 @@ export async function retryFormDelivery(
560
527
  )
561
528
  return { data: res.data, error: res.error }
562
529
  }
563
-
564
- export async function fetchFailedDeliveryCount(formId?: string): Promise<number> {
565
- const qs = formId ? `?formId=${encodeURIComponent(formId)}` : ''
566
- const res = await cmsApi<{ count: number }>(`/forms/deliveries/failed-count${qs}`)
567
- throwIfError(res)
568
- return res.data?.count ?? 0
569
- }
@@ -15,21 +15,21 @@ import { cmsApi, getApiBase } from './api.js'
15
15
 
16
16
  export type NotificationKind = 'comment_reply' | 'comment_resolved' | 'comment_mention'
17
17
 
18
- export interface CommentReplyPayload {
18
+ interface CommentReplyPayload {
19
19
  kind: 'comment_reply'
20
20
  actorId: string | null
21
21
  actorName: string | null
22
22
  excerpt: string
23
23
  }
24
24
 
25
- export interface CommentResolvedPayload {
25
+ interface CommentResolvedPayload {
26
26
  kind: 'comment_resolved'
27
27
  actorId: string | null
28
28
  actorName: string | null
29
29
  rootExcerpt: string
30
30
  }
31
31
 
32
- export interface CommentMentionPayload {
32
+ interface CommentMentionPayload {
33
33
  kind: 'comment_mention'
34
34
  actorId: string | null
35
35
  actorName: string | null
@@ -78,7 +78,7 @@ function stripHtml(html: string): string {
78
78
  * when one is set — otherwise `imageSlots` would count an empty media field
79
79
  * and fail alt-text for an image that is not on the page.
80
80
  */
81
- export function withPostAuditSlots(sections: PageSection[], post?: PostAuditExtras): PageSection[] {
81
+ function withPostAuditSlots(sections: PageSection[], post?: PostAuditExtras): PageSection[] {
82
82
  if (!post) return sections
83
83
 
84
84
  const extras: PageSection[] = []
@@ -27,7 +27,6 @@ import {
27
27
  moveSection,
28
28
  reorderSections,
29
29
  updateSectionContent,
30
- updateSectionContentAtPath,
31
30
  updateSectionSettings,
32
31
  updateSectionMeta,
33
32
  coerceSections as coerceSectionsCore,
@@ -55,7 +54,6 @@ export {
55
54
  moveSection,
56
55
  reorderSections,
57
56
  updateSectionContent,
58
- updateSectionContentAtPath,
59
57
  updateSectionSettings,
60
58
  updateSectionMeta,
61
59
  }
@@ -278,7 +276,7 @@ function toWriteBody(page: EditorPage, extra?: Record<string, unknown>): string
278
276
  }
279
277
 
280
278
  /** Create a new page document, returning the new id. */
281
- export async function createPage(page: EditorPage): Promise<EditorPage> {
279
+ async function createPage(page: EditorPage): Promise<EditorPage> {
282
280
  const res = await cmsApi<DocumentApiRow>(`/collections/${PAGES_COLLECTION}`, {
283
281
  method: 'POST',
284
282
  body: toWriteBody(page),
@@ -30,8 +30,8 @@ import { cmsApi } from './api.js'
30
30
 
31
31
  export type PageStatus = 'DRAFT' | 'PUBLISHED' | 'SCHEDULED' | 'ARCHIVED'
32
32
 
33
- export const PAGES_COLLECTION = 'pages'
34
- export const PAGE_TAGS_COLLECTION = 'page-tags'
33
+ const PAGES_COLLECTION = 'pages'
34
+ const PAGE_TAGS_COLLECTION = 'page-tags'
35
35
 
36
36
  export interface PageTag {
37
37
  id: string // == slug (stable join key; pages store slugs in data.tags)
@@ -47,7 +47,7 @@ export interface PageTag {
47
47
  updatedAt: string | null
48
48
  }
49
49
 
50
- export interface PageAuthor {
50
+ interface PageAuthor {
51
51
  id: string
52
52
  name: string
53
53
  initials: string
@@ -222,22 +222,6 @@ async function fetchTagDocs(): Promise<DocumentApiRow[]> {
222
222
  return (res.data?.docs ?? []) as DocumentApiRow[]
223
223
  }
224
224
 
225
- /**
226
- * Fetch all tags. `pageCounts` (slug → count) is optional so the tag
227
- * manager / pickers can render before page data is loaded.
228
- */
229
- export async function fetchPageTags(pageCounts?: Map<string, number>): Promise<PageTag[]> {
230
- const docs = await fetchTagDocs()
231
- const tags = docs.map((d) => docToTag(d, pageCounts?.get(d.slug ?? d.id) ?? 0))
232
- tags.sort((a, b) => a.sortOrder - b.sortOrder || a.name.localeCompare(b.name))
233
- return tags
234
- }
235
-
236
- export async function fetchAuthors(): Promise<PageAuthor[]> {
237
- const map = await fetchAuthorsMap()
238
- return [...map.values()].sort((a, b) => a.name.localeCompare(b.name))
239
- }
240
-
241
225
  // ─── Pages ───────────────────────────────────────────────────────────
242
226
 
243
227
  function docToPage(d: DocumentApiRow, authors: Map<string, PageAuthor>): Page {
@@ -439,15 +423,6 @@ export async function fetchPages(query: PagesQuery = {}): Promise<PagesResult> {
439
423
  }
440
424
  }
441
425
 
442
- export async function fetchPageById(pageId: string): Promise<Page | null> {
443
- const res = await cmsApi<DocumentApiRow>(
444
- `/collections/${PAGES_COLLECTION}/${encodeURIComponent(pageId)}`,
445
- )
446
- if (res.error || !res.data) return null
447
- const authors = await fetchAuthorsMap()
448
- return docToPage(res.data, authors)
449
- }
450
-
451
426
  // ─── Bulk page mutations ─────────────────────────────────────────────
452
427
 
453
428
  export interface BulkResult {
@@ -621,7 +596,7 @@ export async function updatePageTag(
621
596
  return res.error ? { error: res.error } : {}
622
597
  }
623
598
 
624
- export async function deletePageTag(tagDocId: string): Promise<{ error?: string }> {
599
+ async function deletePageTag(tagDocId: string): Promise<{ error?: string }> {
625
600
  const res = await cmsApi(`/collections/${PAGE_TAGS_COLLECTION}/${encodeURIComponent(tagDocId)}`, {
626
601
  method: 'DELETE',
627
602
  })
package/src/lib/plan.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { cmsApi } from './api.js'
2
2
 
3
- export type PlanTier = 'starter' | 'pro' | 'enterprise'
3
+ type PlanTier = 'starter' | 'pro' | 'enterprise'
4
4
 
5
5
  export type PlanFeature =
6
6
  | 'seo.inlineApply'
@@ -30,10 +30,8 @@ import {
30
30
  removeSection,
31
31
  duplicateSection,
32
32
  toggleSectionVisibility,
33
- moveSection,
34
33
  reorderSections,
35
34
  updateSectionContent,
36
- updateSectionContentAtPath,
37
35
  updateSectionSettings,
38
36
  updateSectionMeta,
39
37
  coerceSections,
@@ -56,17 +54,13 @@ export {
56
54
  removeSection,
57
55
  duplicateSection,
58
56
  toggleSectionVisibility,
59
- moveSection,
60
57
  reorderSections,
61
58
  updateSectionContent,
62
- updateSectionContentAtPath,
63
59
  updateSectionSettings,
64
60
  updateSectionMeta,
65
61
  }
66
62
 
67
- export type PostStatus = 'DRAFT' | 'PUBLISHED' | 'SCHEDULED' | 'ARCHIVED'
68
-
69
- export const TEMPLATES_COLLECTION = 'post-templates'
63
+ type PostStatus = 'DRAFT' | 'PUBLISHED' | 'SCHEDULED' | 'ARCHIVED'
70
64
 
71
65
  export interface EditorPost {
72
66
  id: string | null
@@ -230,7 +224,7 @@ export interface FeaturedImageRef {
230
224
  }
231
225
 
232
226
  /** True for paths and http(s) URLs — never a bare media cuid. */
233
- export function isDisplayableImageSrc(value: string): boolean {
227
+ function isDisplayableImageSrc(value: string): boolean {
234
228
  const v = value.trim()
235
229
  return v.startsWith('/') || /^https?:\/\//i.test(v)
236
230
  }
@@ -272,7 +266,7 @@ export function parseFeaturedImage(raw: unknown): string {
272
266
  * `[{ tag: 'AI' }]` rows; older/hand-rolled configs may store `['AI']`
273
267
  * directly — accept both so no site loses tags in the editor.
274
268
  */
275
- export function parseTags(raw: unknown): string[] {
269
+ function parseTags(raw: unknown): string[] {
276
270
  if (!Array.isArray(raw)) return []
277
271
  const out: string[] = []
278
272
  for (const item of raw) {
@@ -435,7 +429,7 @@ function toWriteBody(post: EditorPost, extra?: Record<string, unknown>): string
435
429
  return JSON.stringify(payload)
436
430
  }
437
431
 
438
- export async function createPost(post: EditorPost): Promise<EditorPost> {
432
+ async function createPost(post: EditorPost): Promise<EditorPost> {
439
433
  const res = await cmsApi<DocumentApiRow>(`/collections/${encodeURIComponent(post.postType)}`, {
440
434
  method: 'POST',
441
435
  body: toWriteBody(post),
@@ -6,7 +6,7 @@ import { cmsApi } from './api.js'
6
6
  * schemas are file-defined in `prenta.config.ts`, so the Post Type editor
7
7
  * inspects the live schema and generates config snippets for changes.
8
8
  */
9
- export interface PostTypeFieldSummary {
9
+ interface PostTypeFieldSummary {
10
10
  name: string
11
11
  type: string
12
12
  required: boolean
@@ -57,7 +57,7 @@ export async function fetchPostTypeDetail(slug: string): Promise<PostTypeDetail
57
57
  }
58
58
 
59
59
  /** Friendly display names for cms-core field types (design's type badges). */
60
- export const FIELD_TYPE_LABELS: Record<string, string> = {
60
+ const FIELD_TYPE_LABELS: Record<string, string> = {
61
61
  text: 'Short Text',
62
62
  richText: 'Rich Text',
63
63
  number: 'Number',
@@ -30,7 +30,7 @@ export interface PostType {
30
30
  active: boolean
31
31
  }
32
32
 
33
- export interface PostAuthor {
33
+ interface PostAuthor {
34
34
  id: string
35
35
  name: string
36
36
  initials: string
@@ -101,20 +101,6 @@ export function buildPublicUrl({
101
101
  return resolveDocumentUrl({ siteUrl, urlPrefix, slug, path, data, trailingSlash })
102
102
  }
103
103
 
104
- /**
105
- * Resolve the public site origin for link building.
106
- *
107
- * Prefer an explicit URL (effective public apex from `/site/public-url` or
108
- * `config.seo.siteUrl`). Falling back to `window.location.origin` is a last
109
- * resort — on Astro-split installs the admin tab is the Next CMS host, so that
110
- * fallback opens the wrong site. Callers should prefer {@link usePublicSiteUrl}.
111
- */
112
- export function resolveSiteUrl(configSiteUrl?: string | null): string {
113
- if (configSiteUrl && configSiteUrl.trim()) return configSiteUrl.trim().replace(/\/+$/, '')
114
- if (typeof window !== 'undefined') return window.location.origin
115
- return ''
116
- }
117
-
118
104
  /** True when `siteUrl` shares an origin with the admin tab (split-deploy smell). */
119
105
  export function siteUrlMatchesAdminOrigin(
120
106
  siteUrl: string | null | undefined,
@@ -10,7 +10,7 @@ export const RAIL_DEFAULT_WIDTH = 360
10
10
  /** Width of the strip that replaces the rail when it is collapsed. */
11
11
  export const RAIL_COLLAPSED_WIDTH = 38
12
12
 
13
- export const RAIL_WIDTH_STORAGE_KEY = 'pb.panelW'
13
+ const RAIL_WIDTH_STORAGE_KEY = 'pb.panelW'
14
14
 
15
15
  /** Constrain a candidate width to the rail's range, rejecting junk. */
16
16
  export function clampRailWidth(width: number): number {
@@ -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)