@growth-labs/cms 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1093) hide show
  1. package/README.md +165 -0
  2. package/dist/engine/activity-log.d.ts +17 -0
  3. package/dist/engine/activity-log.d.ts.map +1 -0
  4. package/dist/engine/activity-log.js +17 -0
  5. package/dist/engine/activity-log.js.map +1 -0
  6. package/dist/engine/ai-prompts.d.ts +57 -0
  7. package/dist/engine/ai-prompts.d.ts.map +1 -0
  8. package/dist/engine/ai-prompts.js +90 -0
  9. package/dist/engine/ai-prompts.js.map +1 -0
  10. package/dist/engine/ai-writeback.d.ts +36 -0
  11. package/dist/engine/ai-writeback.d.ts.map +1 -0
  12. package/dist/engine/ai-writeback.js +45 -0
  13. package/dist/engine/ai-writeback.js.map +1 -0
  14. package/dist/engine/api-keys.d.ts +76 -0
  15. package/dist/engine/api-keys.d.ts.map +1 -0
  16. package/dist/engine/api-keys.js +165 -0
  17. package/dist/engine/api-keys.js.map +1 -0
  18. package/dist/engine/content-insights.d.ts +36 -0
  19. package/dist/engine/content-insights.d.ts.map +1 -0
  20. package/dist/engine/content-insights.js +114 -0
  21. package/dist/engine/content-insights.js.map +1 -0
  22. package/dist/engine/contributors.d.ts +25 -0
  23. package/dist/engine/contributors.d.ts.map +1 -0
  24. package/dist/engine/contributors.js +59 -0
  25. package/dist/engine/contributors.js.map +1 -0
  26. package/dist/engine/cron.d.ts +15 -0
  27. package/dist/engine/cron.d.ts.map +1 -0
  28. package/dist/engine/cron.js +33 -0
  29. package/dist/engine/cron.js.map +1 -0
  30. package/dist/engine/d1.d.ts +16 -0
  31. package/dist/engine/d1.d.ts.map +1 -0
  32. package/dist/engine/d1.js +13 -0
  33. package/dist/engine/d1.js.map +1 -0
  34. package/dist/engine/foundry-dispatch.d.ts +52 -0
  35. package/dist/engine/foundry-dispatch.d.ts.map +1 -0
  36. package/dist/engine/foundry-dispatch.js +290 -0
  37. package/dist/engine/foundry-dispatch.js.map +1 -0
  38. package/dist/engine/import-parsers.d.ts +11 -0
  39. package/dist/engine/import-parsers.d.ts.map +1 -0
  40. package/dist/engine/import-parsers.js +373 -0
  41. package/dist/engine/import-parsers.js.map +1 -0
  42. package/dist/engine/index.d.ts +28 -0
  43. package/dist/engine/index.d.ts.map +1 -0
  44. package/dist/engine/index.js +57 -0
  45. package/dist/engine/index.js.map +1 -0
  46. package/dist/engine/invites.d.ts +78 -0
  47. package/dist/engine/invites.d.ts.map +1 -0
  48. package/dist/engine/invites.js +158 -0
  49. package/dist/engine/invites.js.map +1 -0
  50. package/dist/engine/members.d.ts +59 -0
  51. package/dist/engine/members.d.ts.map +1 -0
  52. package/dist/engine/members.js +124 -0
  53. package/dist/engine/members.js.map +1 -0
  54. package/dist/engine/membership-rules.d.ts +25 -0
  55. package/dist/engine/membership-rules.d.ts.map +1 -0
  56. package/dist/engine/membership-rules.js +44 -0
  57. package/dist/engine/membership-rules.js.map +1 -0
  58. package/dist/engine/og-render.d.ts +40 -0
  59. package/dist/engine/og-render.d.ts.map +1 -0
  60. package/dist/engine/og-render.js +26 -0
  61. package/dist/engine/og-render.js.map +1 -0
  62. package/dist/engine/publish-guard.d.ts +58 -0
  63. package/dist/engine/publish-guard.d.ts.map +1 -0
  64. package/dist/engine/publish-guard.js +80 -0
  65. package/dist/engine/publish-guard.js.map +1 -0
  66. package/dist/engine/publisher.d.ts +171 -0
  67. package/dist/engine/publisher.d.ts.map +1 -0
  68. package/dist/engine/publisher.js +597 -0
  69. package/dist/engine/publisher.js.map +1 -0
  70. package/dist/engine/revisions.d.ts +39 -0
  71. package/dist/engine/revisions.d.ts.map +1 -0
  72. package/dist/engine/revisions.js +203 -0
  73. package/dist/engine/revisions.js.map +1 -0
  74. package/dist/engine/sanitize.d.ts +52 -0
  75. package/dist/engine/sanitize.d.ts.map +1 -0
  76. package/dist/engine/sanitize.js +155 -0
  77. package/dist/engine/sanitize.js.map +1 -0
  78. package/dist/engine/seed-membership.d.ts +29 -0
  79. package/dist/engine/seed-membership.d.ts.map +1 -0
  80. package/dist/engine/seed-membership.js +65 -0
  81. package/dist/engine/seed-membership.js.map +1 -0
  82. package/dist/engine/seo.d.ts +20 -0
  83. package/dist/engine/seo.d.ts.map +1 -0
  84. package/dist/engine/seo.js +50 -0
  85. package/dist/engine/seo.js.map +1 -0
  86. package/dist/engine/slug-redirects.d.ts +8 -0
  87. package/dist/engine/slug-redirects.d.ts.map +1 -0
  88. package/dist/engine/slug-redirects.js +26 -0
  89. package/dist/engine/slug-redirects.js.map +1 -0
  90. package/dist/engine/slug.d.ts +6 -0
  91. package/dist/engine/slug.d.ts.map +1 -0
  92. package/dist/engine/slug.js +28 -0
  93. package/dist/engine/slug.js.map +1 -0
  94. package/dist/engine/soft-delete.d.ts +8 -0
  95. package/dist/engine/soft-delete.d.ts.map +1 -0
  96. package/dist/engine/soft-delete.js +28 -0
  97. package/dist/engine/soft-delete.js.map +1 -0
  98. package/dist/engine/tags.d.ts +14 -0
  99. package/dist/engine/tags.d.ts.map +1 -0
  100. package/dist/engine/tags.js +79 -0
  101. package/dist/engine/tags.js.map +1 -0
  102. package/dist/engine/topics.d.ts +10 -0
  103. package/dist/engine/topics.d.ts.map +1 -0
  104. package/dist/engine/topics.js +140 -0
  105. package/dist/engine/topics.js.map +1 -0
  106. package/dist/engine/url-guard.d.ts +12 -0
  107. package/dist/engine/url-guard.d.ts.map +1 -0
  108. package/dist/engine/url-guard.js +129 -0
  109. package/dist/engine/url-guard.js.map +1 -0
  110. package/dist/engine/validator/checks/bare-url-not-autolinked.d.ts +20 -0
  111. package/dist/engine/validator/checks/bare-url-not-autolinked.d.ts.map +1 -0
  112. package/dist/engine/validator/checks/bare-url-not-autolinked.js +54 -0
  113. package/dist/engine/validator/checks/bare-url-not-autolinked.js.map +1 -0
  114. package/dist/engine/validator/checks/broken-footnote-label.d.ts +16 -0
  115. package/dist/engine/validator/checks/broken-footnote-label.d.ts.map +1 -0
  116. package/dist/engine/validator/checks/broken-footnote-label.js +17 -0
  117. package/dist/engine/validator/checks/broken-footnote-label.js.map +1 -0
  118. package/dist/engine/validator/checks/double-encoded-entities.d.ts +18 -0
  119. package/dist/engine/validator/checks/double-encoded-entities.d.ts.map +1 -0
  120. package/dist/engine/validator/checks/double-encoded-entities.js +23 -0
  121. package/dist/engine/validator/checks/double-encoded-entities.js.map +1 -0
  122. package/dist/engine/validator/checks/empty-alt-text.d.ts +14 -0
  123. package/dist/engine/validator/checks/empty-alt-text.d.ts.map +1 -0
  124. package/dist/engine/validator/checks/empty-alt-text.js +23 -0
  125. package/dist/engine/validator/checks/empty-alt-text.js.map +1 -0
  126. package/dist/engine/validator/checks/heading-hierarchy-skip.d.ts +11 -0
  127. package/dist/engine/validator/checks/heading-hierarchy-skip.d.ts.map +1 -0
  128. package/dist/engine/validator/checks/heading-hierarchy-skip.js +20 -0
  129. package/dist/engine/validator/checks/heading-hierarchy-skip.js.map +1 -0
  130. package/dist/engine/validator/checks/html-comment-leak.d.ts +20 -0
  131. package/dist/engine/validator/checks/html-comment-leak.d.ts.map +1 -0
  132. package/dist/engine/validator/checks/html-comment-leak.js +30 -0
  133. package/dist/engine/validator/checks/html-comment-leak.js.map +1 -0
  134. package/dist/engine/validator/checks/iframe-missing-dims-and-wrapper.d.ts +12 -0
  135. package/dist/engine/validator/checks/iframe-missing-dims-and-wrapper.d.ts.map +1 -0
  136. package/dist/engine/validator/checks/iframe-missing-dims-and-wrapper.js +17 -0
  137. package/dist/engine/validator/checks/iframe-missing-dims-and-wrapper.js.map +1 -0
  138. package/dist/engine/validator/checks/invisible-control-chars.d.ts +24 -0
  139. package/dist/engine/validator/checks/invisible-control-chars.d.ts.map +1 -0
  140. package/dist/engine/validator/checks/invisible-control-chars.js +30 -0
  141. package/dist/engine/validator/checks/invisible-control-chars.js.map +1 -0
  142. package/dist/engine/validator/checks/paywall-marker-leak.d.ts +17 -0
  143. package/dist/engine/validator/checks/paywall-marker-leak.d.ts.map +1 -0
  144. package/dist/engine/validator/checks/paywall-marker-leak.js +22 -0
  145. package/dist/engine/validator/checks/paywall-marker-leak.js.map +1 -0
  146. package/dist/engine/validator/checks/raw-block-html.d.ts +28 -0
  147. package/dist/engine/validator/checks/raw-block-html.d.ts.map +1 -0
  148. package/dist/engine/validator/checks/raw-block-html.js +38 -0
  149. package/dist/engine/validator/checks/raw-block-html.js.map +1 -0
  150. package/dist/engine/validator/checks/stale-body-html.d.ts +28 -0
  151. package/dist/engine/validator/checks/stale-body-html.d.ts.map +1 -0
  152. package/dist/engine/validator/checks/stale-body-html.js +15 -0
  153. package/dist/engine/validator/checks/stale-body-html.js.map +1 -0
  154. package/dist/engine/validator/checks/unresolved-footnote-anchor.d.ts +11 -0
  155. package/dist/engine/validator/checks/unresolved-footnote-anchor.d.ts.map +1 -0
  156. package/dist/engine/validator/checks/unresolved-footnote-anchor.js +48 -0
  157. package/dist/engine/validator/checks/unresolved-footnote-anchor.js.map +1 -0
  158. package/dist/engine/validator/checks/word-gdocs-paste-artifacts.d.ts +23 -0
  159. package/dist/engine/validator/checks/word-gdocs-paste-artifacts.d.ts.map +1 -0
  160. package/dist/engine/validator/checks/word-gdocs-paste-artifacts.js +47 -0
  161. package/dist/engine/validator/checks/word-gdocs-paste-artifacts.js.map +1 -0
  162. package/dist/engine/validator/index.d.ts +75 -0
  163. package/dist/engine/validator/index.d.ts.map +1 -0
  164. package/dist/engine/validator/index.js +313 -0
  165. package/dist/engine/validator/index.js.map +1 -0
  166. package/dist/engine/validator/scan.d.ts +28 -0
  167. package/dist/engine/validator/scan.d.ts.map +1 -0
  168. package/dist/engine/validator/scan.js +97 -0
  169. package/dist/engine/validator/scan.js.map +1 -0
  170. package/dist/engine/validator/types.d.ts +50 -0
  171. package/dist/engine/validator/types.d.ts.map +1 -0
  172. package/dist/engine/validator/types.js +51 -0
  173. package/dist/engine/validator/types.js.map +1 -0
  174. package/dist/engine/webhook-signer.d.ts +39 -0
  175. package/dist/engine/webhook-signer.d.ts.map +1 -0
  176. package/dist/engine/webhook-signer.js +117 -0
  177. package/dist/engine/webhook-signer.js.map +1 -0
  178. package/dist/engine/webhooks.d.ts +75 -0
  179. package/dist/engine/webhooks.d.ts.map +1 -0
  180. package/dist/engine/webhooks.js +139 -0
  181. package/dist/engine/webhooks.js.map +1 -0
  182. package/dist/index.d.ts +5 -0
  183. package/dist/index.d.ts.map +1 -0
  184. package/dist/index.js +40 -0
  185. package/dist/index.js.map +1 -0
  186. package/dist/integration/index.d.ts +6 -0
  187. package/dist/integration/index.d.ts.map +1 -0
  188. package/dist/integration/index.js +294 -0
  189. package/dist/integration/index.js.map +1 -0
  190. package/dist/integration/options.d.ts +105 -0
  191. package/dist/integration/options.d.ts.map +1 -0
  192. package/dist/integration/options.js +25 -0
  193. package/dist/integration/options.js.map +1 -0
  194. package/dist/integration/vite-plugin.d.ts +4 -0
  195. package/dist/integration/vite-plugin.d.ts.map +1 -0
  196. package/dist/integration/vite-plugin.js +37 -0
  197. package/dist/integration/vite-plugin.js.map +1 -0
  198. package/dist/providers/index.d.ts +3 -0
  199. package/dist/providers/index.d.ts.map +1 -0
  200. package/dist/providers/index.js +3 -0
  201. package/dist/providers/index.js.map +1 -0
  202. package/dist/providers/null.d.ts +9 -0
  203. package/dist/providers/null.d.ts.map +1 -0
  204. package/dist/providers/null.js +144 -0
  205. package/dist/providers/null.js.map +1 -0
  206. package/dist/providers/types.d.ts +277 -0
  207. package/dist/providers/types.d.ts.map +1 -0
  208. package/dist/providers/types.js +2 -0
  209. package/dist/providers/types.js.map +1 -0
  210. package/dist/routes/ai.d.ts +25 -0
  211. package/dist/routes/ai.d.ts.map +1 -0
  212. package/dist/routes/ai.js +381 -0
  213. package/dist/routes/ai.js.map +1 -0
  214. package/dist/routes/analytics.d.ts +15 -0
  215. package/dist/routes/analytics.d.ts.map +1 -0
  216. package/dist/routes/analytics.js +61 -0
  217. package/dist/routes/analytics.js.map +1 -0
  218. package/dist/routes/api-keys.d.ts +13 -0
  219. package/dist/routes/api-keys.d.ts.map +1 -0
  220. package/dist/routes/api-keys.js +109 -0
  221. package/dist/routes/api-keys.js.map +1 -0
  222. package/dist/routes/authors.d.ts +19 -0
  223. package/dist/routes/authors.d.ts.map +1 -0
  224. package/dist/routes/authors.js +202 -0
  225. package/dist/routes/authors.js.map +1 -0
  226. package/dist/routes/authz-matrix.d.ts +78 -0
  227. package/dist/routes/authz-matrix.d.ts.map +1 -0
  228. package/dist/routes/authz-matrix.js +170 -0
  229. package/dist/routes/authz-matrix.js.map +1 -0
  230. package/dist/routes/calendar.d.ts +19 -0
  231. package/dist/routes/calendar.d.ts.map +1 -0
  232. package/dist/routes/calendar.js +89 -0
  233. package/dist/routes/calendar.js.map +1 -0
  234. package/dist/routes/config.d.ts +70 -0
  235. package/dist/routes/config.d.ts.map +1 -0
  236. package/dist/routes/config.js +23 -0
  237. package/dist/routes/config.js.map +1 -0
  238. package/dist/routes/content-insights.d.ts +18 -0
  239. package/dist/routes/content-insights.d.ts.map +1 -0
  240. package/dist/routes/content-insights.js +137 -0
  241. package/dist/routes/content-insights.js.map +1 -0
  242. package/dist/routes/content.d.ts +145 -0
  243. package/dist/routes/content.d.ts.map +1 -0
  244. package/dist/routes/content.js +1374 -0
  245. package/dist/routes/content.js.map +1 -0
  246. package/dist/routes/context.d.ts +104 -0
  247. package/dist/routes/context.d.ts.map +1 -0
  248. package/dist/routes/context.js +26 -0
  249. package/dist/routes/context.js.map +1 -0
  250. package/dist/routes/cron.d.ts +8 -0
  251. package/dist/routes/cron.d.ts.map +1 -0
  252. package/dist/routes/cron.js +20 -0
  253. package/dist/routes/cron.js.map +1 -0
  254. package/dist/routes/dashboard.d.ts +12 -0
  255. package/dist/routes/dashboard.d.ts.map +1 -0
  256. package/dist/routes/dashboard.js +113 -0
  257. package/dist/routes/dashboard.js.map +1 -0
  258. package/dist/routes/imports.d.ts +10 -0
  259. package/dist/routes/imports.d.ts.map +1 -0
  260. package/dist/routes/imports.js +149 -0
  261. package/dist/routes/imports.js.map +1 -0
  262. package/dist/routes/index.d.ts +75 -0
  263. package/dist/routes/index.d.ts.map +1 -0
  264. package/dist/routes/index.js +141 -0
  265. package/dist/routes/index.js.map +1 -0
  266. package/dist/routes/media-lib.d.ts +75 -0
  267. package/dist/routes/media-lib.d.ts.map +1 -0
  268. package/dist/routes/media-lib.js +305 -0
  269. package/dist/routes/media-lib.js.map +1 -0
  270. package/dist/routes/media.d.ts +32 -0
  271. package/dist/routes/media.d.ts.map +1 -0
  272. package/dist/routes/media.js +756 -0
  273. package/dist/routes/media.js.map +1 -0
  274. package/dist/routes/preview.d.ts +19 -0
  275. package/dist/routes/preview.d.ts.map +1 -0
  276. package/dist/routes/preview.js +150 -0
  277. package/dist/routes/preview.js.map +1 -0
  278. package/dist/routes/rbac-invites.d.ts +31 -0
  279. package/dist/routes/rbac-invites.d.ts.map +1 -0
  280. package/dist/routes/rbac-invites.js +174 -0
  281. package/dist/routes/rbac-invites.js.map +1 -0
  282. package/dist/routes/rbac.d.ts +12 -0
  283. package/dist/routes/rbac.d.ts.map +1 -0
  284. package/dist/routes/rbac.js +126 -0
  285. package/dist/routes/rbac.js.map +1 -0
  286. package/dist/routes/shell.d.ts +22 -0
  287. package/dist/routes/shell.d.ts.map +1 -0
  288. package/dist/routes/shell.js +123 -0
  289. package/dist/routes/shell.js.map +1 -0
  290. package/dist/routes/subscriptions.d.ts +21 -0
  291. package/dist/routes/subscriptions.d.ts.map +1 -0
  292. package/dist/routes/subscriptions.js +127 -0
  293. package/dist/routes/subscriptions.js.map +1 -0
  294. package/dist/routes/tags.d.ts +23 -0
  295. package/dist/routes/tags.d.ts.map +1 -0
  296. package/dist/routes/tags.js +68 -0
  297. package/dist/routes/tags.js.map +1 -0
  298. package/dist/routes/topics.d.ts +12 -0
  299. package/dist/routes/topics.d.ts.map +1 -0
  300. package/dist/routes/topics.js +49 -0
  301. package/dist/routes/topics.js.map +1 -0
  302. package/dist/routes/webhooks.d.ts +31 -0
  303. package/dist/routes/webhooks.d.ts.map +1 -0
  304. package/dist/routes/webhooks.js +173 -0
  305. package/dist/routes/webhooks.js.map +1 -0
  306. package/dist/schema/index.d.ts +4 -0
  307. package/dist/schema/index.d.ts.map +1 -0
  308. package/dist/schema/index.js +6 -0
  309. package/dist/schema/index.js.map +1 -0
  310. package/dist/schema/insights-ingest.d.ts +959 -0
  311. package/dist/schema/insights-ingest.d.ts.map +1 -0
  312. package/dist/schema/insights-ingest.js +112 -0
  313. package/dist/schema/insights-ingest.js.map +1 -0
  314. package/dist/schema/migrations.d.ts +63 -0
  315. package/dist/schema/migrations.d.ts.map +1 -0
  316. package/dist/schema/migrations.js +589 -0
  317. package/dist/schema/migrations.js.map +1 -0
  318. package/dist/schema/tables.d.ts +11 -0
  319. package/dist/schema/tables.d.ts.map +1 -0
  320. package/dist/schema/tables.js +56 -0
  321. package/dist/schema/tables.js.map +1 -0
  322. package/dist/schema/types.d.ts +476 -0
  323. package/dist/schema/types.d.ts.map +1 -0
  324. package/dist/schema/types.js +37 -0
  325. package/dist/schema/types.js.map +1 -0
  326. package/dist/ui/api/_authz.d.ts +6 -0
  327. package/dist/ui/api/_authz.d.ts.map +1 -0
  328. package/dist/ui/api/_authz.js +74 -0
  329. package/dist/ui/api/_authz.js.map +1 -0
  330. package/dist/ui/api/_content-config.d.ts +22 -0
  331. package/dist/ui/api/_content-config.d.ts.map +1 -0
  332. package/dist/ui/api/_content-config.js +50 -0
  333. package/dist/ui/api/_content-config.js.map +1 -0
  334. package/dist/ui/api/activity.d.ts +3 -0
  335. package/dist/ui/api/activity.d.ts.map +1 -0
  336. package/dist/ui/api/activity.js +28 -0
  337. package/dist/ui/api/activity.js.map +1 -0
  338. package/dist/ui/api/analytics.d.ts +3 -0
  339. package/dist/ui/api/analytics.d.ts.map +1 -0
  340. package/dist/ui/api/analytics.js +36 -0
  341. package/dist/ui/api/analytics.js.map +1 -0
  342. package/dist/ui/api/authors/[id].d.ts +4 -0
  343. package/dist/ui/api/authors/[id].d.ts.map +1 -0
  344. package/dist/ui/api/authors/[id].js +17 -0
  345. package/dist/ui/api/authors/[id].js.map +1 -0
  346. package/dist/ui/api/authors.d.ts +4 -0
  347. package/dist/ui/api/authors.d.ts.map +1 -0
  348. package/dist/ui/api/authors.js +12 -0
  349. package/dist/ui/api/authors.js.map +1 -0
  350. package/dist/ui/api/calendar.d.ts +3 -0
  351. package/dist/ui/api/calendar.d.ts.map +1 -0
  352. package/dist/ui/api/calendar.js +16 -0
  353. package/dist/ui/api/calendar.js.map +1 -0
  354. package/dist/ui/api/content/[id]/ai/headlines.d.ts +3 -0
  355. package/dist/ui/api/content/[id]/ai/headlines.d.ts.map +1 -0
  356. package/dist/ui/api/content/[id]/ai/headlines.js +7 -0
  357. package/dist/ui/api/content/[id]/ai/headlines.js.map +1 -0
  358. package/dist/ui/api/content/[id]/ai/meta-description.d.ts +3 -0
  359. package/dist/ui/api/content/[id]/ai/meta-description.d.ts.map +1 -0
  360. package/dist/ui/api/content/[id]/ai/meta-description.js +6 -0
  361. package/dist/ui/api/content/[id]/ai/meta-description.js.map +1 -0
  362. package/dist/ui/api/content/[id]/ai/og-image.d.ts +3 -0
  363. package/dist/ui/api/content/[id]/ai/og-image.d.ts.map +1 -0
  364. package/dist/ui/api/content/[id]/ai/og-image.js +7 -0
  365. package/dist/ui/api/content/[id]/ai/og-image.js.map +1 -0
  366. package/dist/ui/api/content/[id]/ai/proofread.d.ts +3 -0
  367. package/dist/ui/api/content/[id]/ai/proofread.d.ts.map +1 -0
  368. package/dist/ui/api/content/[id]/ai/proofread.js +7 -0
  369. package/dist/ui/api/content/[id]/ai/proofread.js.map +1 -0
  370. package/dist/ui/api/content/[id]/ai/takeaways.d.ts +3 -0
  371. package/dist/ui/api/content/[id]/ai/takeaways.d.ts.map +1 -0
  372. package/dist/ui/api/content/[id]/ai/takeaways.js +6 -0
  373. package/dist/ui/api/content/[id]/ai/takeaways.js.map +1 -0
  374. package/dist/ui/api/content/[id]/contributors.d.ts +4 -0
  375. package/dist/ui/api/content/[id]/contributors.d.ts.map +1 -0
  376. package/dist/ui/api/content/[id]/contributors.js +8 -0
  377. package/dist/ui/api/content/[id]/contributors.js.map +1 -0
  378. package/dist/ui/api/content/[id]/preview-token.d.ts +3 -0
  379. package/dist/ui/api/content/[id]/preview-token.d.ts.map +1 -0
  380. package/dist/ui/api/content/[id]/preview-token.js +16 -0
  381. package/dist/ui/api/content/[id]/preview-token.js.map +1 -0
  382. package/dist/ui/api/content/[id]/revisions/[rev]/restore.d.ts +3 -0
  383. package/dist/ui/api/content/[id]/revisions/[rev]/restore.d.ts.map +1 -0
  384. package/dist/ui/api/content/[id]/revisions/[rev]/restore.js +7 -0
  385. package/dist/ui/api/content/[id]/revisions/[rev]/restore.js.map +1 -0
  386. package/dist/ui/api/content/[id]/revisions/[rev].d.ts +3 -0
  387. package/dist/ui/api/content/[id]/revisions/[rev].d.ts.map +1 -0
  388. package/dist/ui/api/content/[id]/revisions/[rev].js +6 -0
  389. package/dist/ui/api/content/[id]/revisions/[rev].js.map +1 -0
  390. package/dist/ui/api/content/[id]/revisions.d.ts +3 -0
  391. package/dist/ui/api/content/[id]/revisions.d.ts.map +1 -0
  392. package/dist/ui/api/content/[id]/revisions.js +6 -0
  393. package/dist/ui/api/content/[id]/revisions.js.map +1 -0
  394. package/dist/ui/api/content/[id]/seo-score.d.ts +3 -0
  395. package/dist/ui/api/content/[id]/seo-score.d.ts.map +1 -0
  396. package/dist/ui/api/content/[id]/seo-score.js +7 -0
  397. package/dist/ui/api/content/[id]/seo-score.js.map +1 -0
  398. package/dist/ui/api/content/[id].d.ts +5 -0
  399. package/dist/ui/api/content/[id].d.ts.map +1 -0
  400. package/dist/ui/api/content/[id].js +17 -0
  401. package/dist/ui/api/content/[id].js.map +1 -0
  402. package/dist/ui/api/content/bulk.d.ts +3 -0
  403. package/dist/ui/api/content/bulk.d.ts.map +1 -0
  404. package/dist/ui/api/content/bulk.js +7 -0
  405. package/dist/ui/api/content/bulk.js.map +1 -0
  406. package/dist/ui/api/content/counts.d.ts +3 -0
  407. package/dist/ui/api/content/counts.d.ts.map +1 -0
  408. package/dist/ui/api/content/counts.js +8 -0
  409. package/dist/ui/api/content/counts.js.map +1 -0
  410. package/dist/ui/api/content/foundry-callback.d.ts +3 -0
  411. package/dist/ui/api/content/foundry-callback.d.ts.map +1 -0
  412. package/dist/ui/api/content/foundry-callback.js +8 -0
  413. package/dist/ui/api/content/foundry-callback.js.map +1 -0
  414. package/dist/ui/api/content/import/confirm.d.ts +3 -0
  415. package/dist/ui/api/content/import/confirm.d.ts.map +1 -0
  416. package/dist/ui/api/content/import/confirm.js +11 -0
  417. package/dist/ui/api/content/import/confirm.js.map +1 -0
  418. package/dist/ui/api/content/import/parse.d.ts +3 -0
  419. package/dist/ui/api/content/import/parse.d.ts.map +1 -0
  420. package/dist/ui/api/content/import/parse.js +11 -0
  421. package/dist/ui/api/content/import/parse.js.map +1 -0
  422. package/dist/ui/api/content/insights-ingest.d.ts +3 -0
  423. package/dist/ui/api/content/insights-ingest.d.ts.map +1 -0
  424. package/dist/ui/api/content/insights-ingest.js +15 -0
  425. package/dist/ui/api/content/insights-ingest.js.map +1 -0
  426. package/dist/ui/api/content-insights/dismiss.d.ts +3 -0
  427. package/dist/ui/api/content-insights/dismiss.d.ts.map +1 -0
  428. package/dist/ui/api/content-insights/dismiss.js +16 -0
  429. package/dist/ui/api/content-insights/dismiss.js.map +1 -0
  430. package/dist/ui/api/content-insights/index.d.ts +3 -0
  431. package/dist/ui/api/content-insights/index.d.ts.map +1 -0
  432. package/dist/ui/api/content-insights/index.js +12 -0
  433. package/dist/ui/api/content-insights/index.js.map +1 -0
  434. package/dist/ui/api/content-insights/undismiss.d.ts +3 -0
  435. package/dist/ui/api/content-insights/undismiss.d.ts.map +1 -0
  436. package/dist/ui/api/content-insights/undismiss.js +16 -0
  437. package/dist/ui/api/content-insights/undismiss.js.map +1 -0
  438. package/dist/ui/api/content.d.ts +4 -0
  439. package/dist/ui/api/content.d.ts.map +1 -0
  440. package/dist/ui/api/content.js +16 -0
  441. package/dist/ui/api/content.js.map +1 -0
  442. package/dist/ui/api/dashboard.d.ts +3 -0
  443. package/dist/ui/api/dashboard.d.ts.map +1 -0
  444. package/dist/ui/api/dashboard.js +22 -0
  445. package/dist/ui/api/dashboard.js.map +1 -0
  446. package/dist/ui/api/me.d.ts +3 -0
  447. package/dist/ui/api/me.d.ts.map +1 -0
  448. package/dist/ui/api/me.js +18 -0
  449. package/dist/ui/api/me.js.map +1 -0
  450. package/dist/ui/api/media/[id].d.ts +3 -0
  451. package/dist/ui/api/media/[id].d.ts.map +1 -0
  452. package/dist/ui/api/media/[id].js +17 -0
  453. package/dist/ui/api/media/[id].js.map +1 -0
  454. package/dist/ui/api/media/images.d.ts +3 -0
  455. package/dist/ui/api/media/images.d.ts.map +1 -0
  456. package/dist/ui/api/media/images.js +6 -0
  457. package/dist/ui/api/media/images.js.map +1 -0
  458. package/dist/ui/api/media/library/[id].d.ts +3 -0
  459. package/dist/ui/api/media/library/[id].d.ts.map +1 -0
  460. package/dist/ui/api/media/library/[id].js +6 -0
  461. package/dist/ui/api/media/library/[id].js.map +1 -0
  462. package/dist/ui/api/media/library.d.ts +3 -0
  463. package/dist/ui/api/media/library.d.ts.map +1 -0
  464. package/dist/ui/api/media/library.js +17 -0
  465. package/dist/ui/api/media/library.js.map +1 -0
  466. package/dist/ui/api/media/podcast/abort.d.ts +3 -0
  467. package/dist/ui/api/media/podcast/abort.d.ts.map +1 -0
  468. package/dist/ui/api/media/podcast/abort.js +4 -0
  469. package/dist/ui/api/media/podcast/abort.js.map +1 -0
  470. package/dist/ui/api/media/podcast/complete.d.ts +3 -0
  471. package/dist/ui/api/media/podcast/complete.d.ts.map +1 -0
  472. package/dist/ui/api/media/podcast/complete.js +4 -0
  473. package/dist/ui/api/media/podcast/complete.js.map +1 -0
  474. package/dist/ui/api/media/podcast/init.d.ts +3 -0
  475. package/dist/ui/api/media/podcast/init.d.ts.map +1 -0
  476. package/dist/ui/api/media/podcast/init.js +4 -0
  477. package/dist/ui/api/media/podcast/init.js.map +1 -0
  478. package/dist/ui/api/media/podcast/part.d.ts +3 -0
  479. package/dist/ui/api/media/podcast/part.d.ts.map +1 -0
  480. package/dist/ui/api/media/podcast/part.js +4 -0
  481. package/dist/ui/api/media/podcast/part.js.map +1 -0
  482. package/dist/ui/api/media/podcast.d.ts +3 -0
  483. package/dist/ui/api/media/podcast.d.ts.map +1 -0
  484. package/dist/ui/api/media/podcast.js +6 -0
  485. package/dist/ui/api/media/podcast.js.map +1 -0
  486. package/dist/ui/api/media/videos/abort.d.ts +3 -0
  487. package/dist/ui/api/media/videos/abort.d.ts.map +1 -0
  488. package/dist/ui/api/media/videos/abort.js +6 -0
  489. package/dist/ui/api/media/videos/abort.js.map +1 -0
  490. package/dist/ui/api/media/videos/complete.d.ts +3 -0
  491. package/dist/ui/api/media/videos/complete.d.ts.map +1 -0
  492. package/dist/ui/api/media/videos/complete.js +6 -0
  493. package/dist/ui/api/media/videos/complete.js.map +1 -0
  494. package/dist/ui/api/media/videos/init.d.ts +3 -0
  495. package/dist/ui/api/media/videos/init.d.ts.map +1 -0
  496. package/dist/ui/api/media/videos/init.js +6 -0
  497. package/dist/ui/api/media/videos/init.js.map +1 -0
  498. package/dist/ui/api/media/videos/part.d.ts +3 -0
  499. package/dist/ui/api/media/videos/part.d.ts.map +1 -0
  500. package/dist/ui/api/media/videos/part.js +6 -0
  501. package/dist/ui/api/media/videos/part.js.map +1 -0
  502. package/dist/ui/api/notifications.d.ts +4 -0
  503. package/dist/ui/api/notifications.d.ts.map +1 -0
  504. package/dist/ui/api/notifications.js +20 -0
  505. package/dist/ui/api/notifications.js.map +1 -0
  506. package/dist/ui/api/search.d.ts +3 -0
  507. package/dist/ui/api/search.d.ts.map +1 -0
  508. package/dist/ui/api/search.js +18 -0
  509. package/dist/ui/api/search.js.map +1 -0
  510. package/dist/ui/api/settings/api-keys/[id].d.ts +3 -0
  511. package/dist/ui/api/settings/api-keys/[id].d.ts.map +1 -0
  512. package/dist/ui/api/settings/api-keys/[id].js +22 -0
  513. package/dist/ui/api/settings/api-keys/[id].js.map +1 -0
  514. package/dist/ui/api/settings/api-keys.d.ts +4 -0
  515. package/dist/ui/api/settings/api-keys.d.ts.map +1 -0
  516. package/dist/ui/api/settings/api-keys.js +19 -0
  517. package/dist/ui/api/settings/api-keys.js.map +1 -0
  518. package/dist/ui/api/settings/domains.d.ts +3 -0
  519. package/dist/ui/api/settings/domains.d.ts.map +1 -0
  520. package/dist/ui/api/settings/domains.js +32 -0
  521. package/dist/ui/api/settings/domains.js.map +1 -0
  522. package/dist/ui/api/settings/integrations.d.ts +3 -0
  523. package/dist/ui/api/settings/integrations.d.ts.map +1 -0
  524. package/dist/ui/api/settings/integrations.js +32 -0
  525. package/dist/ui/api/settings/integrations.js.map +1 -0
  526. package/dist/ui/api/settings/members/[userId].d.ts +4 -0
  527. package/dist/ui/api/settings/members/[userId].d.ts.map +1 -0
  528. package/dist/ui/api/settings/members/[userId].js +26 -0
  529. package/dist/ui/api/settings/members/[userId].js.map +1 -0
  530. package/dist/ui/api/settings/members/invite.d.ts +3 -0
  531. package/dist/ui/api/settings/members/invite.d.ts.map +1 -0
  532. package/dist/ui/api/settings/members/invite.js +21 -0
  533. package/dist/ui/api/settings/members/invite.js.map +1 -0
  534. package/dist/ui/api/settings/members.d.ts +3 -0
  535. package/dist/ui/api/settings/members.d.ts.map +1 -0
  536. package/dist/ui/api/settings/members.js +17 -0
  537. package/dist/ui/api/settings/members.js.map +1 -0
  538. package/dist/ui/api/settings/webhooks/[id]/test.d.ts +3 -0
  539. package/dist/ui/api/settings/webhooks/[id]/test.d.ts.map +1 -0
  540. package/dist/ui/api/settings/webhooks/[id]/test.js +24 -0
  541. package/dist/ui/api/settings/webhooks/[id]/test.js.map +1 -0
  542. package/dist/ui/api/settings/webhooks/[id].d.ts +3 -0
  543. package/dist/ui/api/settings/webhooks/[id].d.ts.map +1 -0
  544. package/dist/ui/api/settings/webhooks/[id].js +23 -0
  545. package/dist/ui/api/settings/webhooks/[id].js.map +1 -0
  546. package/dist/ui/api/settings/webhooks.d.ts +4 -0
  547. package/dist/ui/api/settings/webhooks.d.ts.map +1 -0
  548. package/dist/ui/api/settings/webhooks.js +21 -0
  549. package/dist/ui/api/settings/webhooks.js.map +1 -0
  550. package/dist/ui/api/subscriptions.d.ts +4 -0
  551. package/dist/ui/api/subscriptions.d.ts.map +1 -0
  552. package/dist/ui/api/subscriptions.js +47 -0
  553. package/dist/ui/api/subscriptions.js.map +1 -0
  554. package/dist/ui/api/tags/[id].d.ts +4 -0
  555. package/dist/ui/api/tags/[id].d.ts.map +1 -0
  556. package/dist/ui/api/tags/[id].js +18 -0
  557. package/dist/ui/api/tags/[id].js.map +1 -0
  558. package/dist/ui/api/tags/index.d.ts +4 -0
  559. package/dist/ui/api/tags/index.d.ts.map +1 -0
  560. package/dist/ui/api/tags/index.js +13 -0
  561. package/dist/ui/api/tags/index.js.map +1 -0
  562. package/dist/ui/api/topics/[id].d.ts +3 -0
  563. package/dist/ui/api/topics/[id].d.ts.map +1 -0
  564. package/dist/ui/api/topics/[id].js +15 -0
  565. package/dist/ui/api/topics/[id].js.map +1 -0
  566. package/dist/ui/api/topics/index.d.ts +4 -0
  567. package/dist/ui/api/topics/index.d.ts.map +1 -0
  568. package/dist/ui/api/topics/index.js +12 -0
  569. package/dist/ui/api/topics/index.js.map +1 -0
  570. package/dist/ui/api/workspace-settings.d.ts +4 -0
  571. package/dist/ui/api/workspace-settings.d.ts.map +1 -0
  572. package/dist/ui/api/workspace-settings.js +20 -0
  573. package/dist/ui/api/workspace-settings.js.map +1 -0
  574. package/dist/ui/client/boot-state.d.ts +15 -0
  575. package/dist/ui/client/boot-state.d.ts.map +1 -0
  576. package/dist/ui/client/boot-state.js +36 -0
  577. package/dist/ui/client/boot-state.js.map +1 -0
  578. package/dist/ui/client/mount.d.ts +3 -0
  579. package/dist/ui/client/mount.d.ts.map +1 -0
  580. package/dist/ui/client/mount.js +37 -0
  581. package/dist/ui/client/mount.js.map +1 -0
  582. package/dist/ui/commands.d.ts +23 -0
  583. package/dist/ui/commands.d.ts.map +1 -0
  584. package/dist/ui/commands.js +48 -0
  585. package/dist/ui/commands.js.map +1 -0
  586. package/dist/ui/components/CmsApp.d.ts +16 -0
  587. package/dist/ui/components/CmsApp.d.ts.map +1 -0
  588. package/dist/ui/components/CmsApp.js +74 -0
  589. package/dist/ui/components/CmsApp.js.map +1 -0
  590. package/dist/ui/components/CommandPalette.d.ts +7 -0
  591. package/dist/ui/components/CommandPalette.d.ts.map +1 -0
  592. package/dist/ui/components/CommandPalette.js +61 -0
  593. package/dist/ui/components/CommandPalette.js.map +1 -0
  594. package/dist/ui/components/NoAccessScreen.d.ts +2 -0
  595. package/dist/ui/components/NoAccessScreen.d.ts.map +1 -0
  596. package/dist/ui/components/NoAccessScreen.js +42 -0
  597. package/dist/ui/components/NoAccessScreen.js.map +1 -0
  598. package/dist/ui/components/ShareModal.d.ts +27 -0
  599. package/dist/ui/components/ShareModal.d.ts.map +1 -0
  600. package/dist/ui/components/ShareModal.js +208 -0
  601. package/dist/ui/components/ShareModal.js.map +1 -0
  602. package/dist/ui/components/SharePickers.d.ts +39 -0
  603. package/dist/ui/components/SharePickers.d.ts.map +1 -0
  604. package/dist/ui/components/SharePickers.js +352 -0
  605. package/dist/ui/components/SharePickers.js.map +1 -0
  606. package/dist/ui/components/ShareStatsPanel.d.ts +22 -0
  607. package/dist/ui/components/ShareStatsPanel.d.ts.map +1 -0
  608. package/dist/ui/components/ShareStatsPanel.js +317 -0
  609. package/dist/ui/components/ShareStatsPanel.js.map +1 -0
  610. package/dist/ui/components/Sidebar.d.ts +7 -0
  611. package/dist/ui/components/Sidebar.d.ts.map +1 -0
  612. package/dist/ui/components/Sidebar.js +20 -0
  613. package/dist/ui/components/Sidebar.js.map +1 -0
  614. package/dist/ui/components/SiteSwitcher.d.ts +4 -0
  615. package/dist/ui/components/SiteSwitcher.d.ts.map +1 -0
  616. package/dist/ui/components/SiteSwitcher.js +35 -0
  617. package/dist/ui/components/SiteSwitcher.js.map +1 -0
  618. package/dist/ui/components/Topbar.d.ts +9 -0
  619. package/dist/ui/components/Topbar.d.ts.map +1 -0
  620. package/dist/ui/components/Topbar.js +20 -0
  621. package/dist/ui/components/Topbar.js.map +1 -0
  622. package/dist/ui/editor/AiAssistPanel.d.ts +8 -0
  623. package/dist/ui/editor/AiAssistPanel.d.ts.map +1 -0
  624. package/dist/ui/editor/AiAssistPanel.js +221 -0
  625. package/dist/ui/editor/AiAssistPanel.js.map +1 -0
  626. package/dist/ui/editor/ContentForm.d.ts +46 -0
  627. package/dist/ui/editor/ContentForm.d.ts.map +1 -0
  628. package/dist/ui/editor/ContentForm.js +821 -0
  629. package/dist/ui/editor/ContentForm.js.map +1 -0
  630. package/dist/ui/editor/Rte.d.ts +16 -0
  631. package/dist/ui/editor/Rte.d.ts.map +1 -0
  632. package/dist/ui/editor/Rte.js +272 -0
  633. package/dist/ui/editor/Rte.js.map +1 -0
  634. package/dist/ui/editor/ai-assist.d.ts +43 -0
  635. package/dist/ui/editor/ai-assist.d.ts.map +1 -0
  636. package/dist/ui/editor/ai-assist.js +114 -0
  637. package/dist/ui/editor/ai-assist.js.map +1 -0
  638. package/dist/ui/editor/autosave.d.ts +18 -0
  639. package/dist/ui/editor/autosave.d.ts.map +1 -0
  640. package/dist/ui/editor/autosave.js +23 -0
  641. package/dist/ui/editor/autosave.js.map +1 -0
  642. package/dist/ui/editor/content-payload.d.ts +19 -0
  643. package/dist/ui/editor/content-payload.d.ts.map +1 -0
  644. package/dist/ui/editor/content-payload.js +97 -0
  645. package/dist/ui/editor/content-payload.js.map +1 -0
  646. package/dist/ui/editor/editor-media-upload.d.ts +6 -0
  647. package/dist/ui/editor/editor-media-upload.d.ts.map +1 -0
  648. package/dist/ui/editor/editor-media-upload.js +20 -0
  649. package/dist/ui/editor/editor-media-upload.js.map +1 -0
  650. package/dist/ui/editor/serialize.d.ts +6 -0
  651. package/dist/ui/editor/serialize.d.ts.map +1 -0
  652. package/dist/ui/editor/serialize.js +479 -0
  653. package/dist/ui/editor/serialize.js.map +1 -0
  654. package/dist/ui/editor/tweet-embed.d.ts +4 -0
  655. package/dist/ui/editor/tweet-embed.d.ts.map +1 -0
  656. package/dist/ui/editor/tweet-embed.js +49 -0
  657. package/dist/ui/editor/tweet-embed.js.map +1 -0
  658. package/dist/ui/hash-router.d.ts +5 -0
  659. package/dist/ui/hash-router.d.ts.map +1 -0
  660. package/dist/ui/hash-router.js +25 -0
  661. package/dist/ui/hash-router.js.map +1 -0
  662. package/dist/ui/icons.d.ts +32 -0
  663. package/dist/ui/icons.d.ts.map +1 -0
  664. package/dist/ui/icons.js +86 -0
  665. package/dist/ui/icons.js.map +1 -0
  666. package/dist/ui/inspector/Field.d.ts +12 -0
  667. package/dist/ui/inspector/Field.d.ts.map +1 -0
  668. package/dist/ui/inspector/Field.js +8 -0
  669. package/dist/ui/inspector/Field.js.map +1 -0
  670. package/dist/ui/inspector/FoundryTab.d.ts +9 -0
  671. package/dist/ui/inspector/FoundryTab.d.ts.map +1 -0
  672. package/dist/ui/inspector/FoundryTab.js +362 -0
  673. package/dist/ui/inspector/FoundryTab.js.map +1 -0
  674. package/dist/ui/inspector/HistoryTab.d.ts +7 -0
  675. package/dist/ui/inspector/HistoryTab.d.ts.map +1 -0
  676. package/dist/ui/inspector/HistoryTab.js +289 -0
  677. package/dist/ui/inspector/HistoryTab.js.map +1 -0
  678. package/dist/ui/inspector/Inspector.d.ts +13 -0
  679. package/dist/ui/inspector/Inspector.d.ts.map +1 -0
  680. package/dist/ui/inspector/Inspector.js +163 -0
  681. package/dist/ui/inspector/Inspector.js.map +1 -0
  682. package/dist/ui/inspector/OrganizeTab.d.ts +15 -0
  683. package/dist/ui/inspector/OrganizeTab.d.ts.map +1 -0
  684. package/dist/ui/inspector/OrganizeTab.js +319 -0
  685. package/dist/ui/inspector/OrganizeTab.js.map +1 -0
  686. package/dist/ui/inspector/PublishTab.d.ts +18 -0
  687. package/dist/ui/inspector/PublishTab.d.ts.map +1 -0
  688. package/dist/ui/inspector/PublishTab.js +339 -0
  689. package/dist/ui/inspector/PublishTab.js.map +1 -0
  690. package/dist/ui/inspector/Section.d.ts +10 -0
  691. package/dist/ui/inspector/Section.d.ts.map +1 -0
  692. package/dist/ui/inspector/Section.js +40 -0
  693. package/dist/ui/inspector/Section.js.map +1 -0
  694. package/dist/ui/inspector/SeoTab.d.ts +19 -0
  695. package/dist/ui/inspector/SeoTab.d.ts.map +1 -0
  696. package/dist/ui/inspector/SeoTab.js +328 -0
  697. package/dist/ui/inspector/SeoTab.js.map +1 -0
  698. package/dist/ui/inspector/foundry-stages.d.ts +36 -0
  699. package/dist/ui/inspector/foundry-stages.d.ts.map +1 -0
  700. package/dist/ui/inspector/foundry-stages.js +101 -0
  701. package/dist/ui/inspector/foundry-stages.js.map +1 -0
  702. package/dist/ui/inspector/inspector-data.d.ts +80 -0
  703. package/dist/ui/inspector/inspector-data.d.ts.map +1 -0
  704. package/dist/ui/inspector/inspector-data.js +172 -0
  705. package/dist/ui/inspector/inspector-data.js.map +1 -0
  706. package/dist/ui/inspector/organize-data.d.ts +23 -0
  707. package/dist/ui/inspector/organize-data.d.ts.map +1 -0
  708. package/dist/ui/inspector/organize-data.js +28 -0
  709. package/dist/ui/inspector/organize-data.js.map +1 -0
  710. package/dist/ui/inspector/revision-diff.d.ts +49 -0
  711. package/dist/ui/inspector/revision-diff.d.ts.map +1 -0
  712. package/dist/ui/inspector/revision-diff.js +166 -0
  713. package/dist/ui/inspector/revision-diff.js.map +1 -0
  714. package/dist/ui/inspector/seo-helpers.d.ts +37 -0
  715. package/dist/ui/inspector/seo-helpers.d.ts.map +1 -0
  716. package/dist/ui/inspector/seo-helpers.js +37 -0
  717. package/dist/ui/inspector/seo-helpers.js.map +1 -0
  718. package/dist/ui/inspector/tab-visibility.d.ts +14 -0
  719. package/dist/ui/inspector/tab-visibility.d.ts.map +1 -0
  720. package/dist/ui/inspector/tab-visibility.js +28 -0
  721. package/dist/ui/inspector/tab-visibility.js.map +1 -0
  722. package/dist/ui/nav.d.ts +16 -0
  723. package/dist/ui/nav.d.ts.map +1 -0
  724. package/dist/ui/nav.js +33 -0
  725. package/dist/ui/nav.js.map +1 -0
  726. package/dist/ui/pages/admin.astro +32 -0
  727. package/dist/ui/preview/draft-page.d.ts +37 -0
  728. package/dist/ui/preview/draft-page.d.ts.map +1 -0
  729. package/dist/ui/preview/draft-page.js +212 -0
  730. package/dist/ui/preview/draft-page.js.map +1 -0
  731. package/dist/ui/preview/preview-layout.d.ts +23 -0
  732. package/dist/ui/preview/preview-layout.d.ts.map +1 -0
  733. package/dist/ui/preview/preview-layout.js +30 -0
  734. package/dist/ui/preview/preview-layout.js.map +1 -0
  735. package/dist/ui/screens/AnalyticsScreen.d.ts +2 -0
  736. package/dist/ui/screens/AnalyticsScreen.d.ts.map +1 -0
  737. package/dist/ui/screens/AnalyticsScreen.js +408 -0
  738. package/dist/ui/screens/AnalyticsScreen.js.map +1 -0
  739. package/dist/ui/screens/AuthorsScreen.d.ts +2 -0
  740. package/dist/ui/screens/AuthorsScreen.d.ts.map +1 -0
  741. package/dist/ui/screens/AuthorsScreen.js +225 -0
  742. package/dist/ui/screens/AuthorsScreen.js.map +1 -0
  743. package/dist/ui/screens/CalendarScreen.d.ts +6 -0
  744. package/dist/ui/screens/CalendarScreen.d.ts.map +1 -0
  745. package/dist/ui/screens/CalendarScreen.js +327 -0
  746. package/dist/ui/screens/CalendarScreen.js.map +1 -0
  747. package/dist/ui/screens/ContentInsightsScreen.d.ts +2 -0
  748. package/dist/ui/screens/ContentInsightsScreen.d.ts.map +1 -0
  749. package/dist/ui/screens/ContentInsightsScreen.js +129 -0
  750. package/dist/ui/screens/ContentInsightsScreen.js.map +1 -0
  751. package/dist/ui/screens/ContentRoute.d.ts +2 -0
  752. package/dist/ui/screens/ContentRoute.d.ts.map +1 -0
  753. package/dist/ui/screens/ContentRoute.js +32 -0
  754. package/dist/ui/screens/ContentRoute.js.map +1 -0
  755. package/dist/ui/screens/DashboardScreen.d.ts +6 -0
  756. package/dist/ui/screens/DashboardScreen.d.ts.map +1 -0
  757. package/dist/ui/screens/DashboardScreen.js +273 -0
  758. package/dist/ui/screens/DashboardScreen.js.map +1 -0
  759. package/dist/ui/screens/EditorScreen.d.ts +7 -0
  760. package/dist/ui/screens/EditorScreen.d.ts.map +1 -0
  761. package/dist/ui/screens/EditorScreen.js +426 -0
  762. package/dist/ui/screens/EditorScreen.js.map +1 -0
  763. package/dist/ui/screens/LibraryScreen.d.ts +6 -0
  764. package/dist/ui/screens/LibraryScreen.d.ts.map +1 -0
  765. package/dist/ui/screens/LibraryScreen.js +580 -0
  766. package/dist/ui/screens/LibraryScreen.js.map +1 -0
  767. package/dist/ui/screens/MediaScreen.d.ts +2 -0
  768. package/dist/ui/screens/MediaScreen.d.ts.map +1 -0
  769. package/dist/ui/screens/MediaScreen.js +173 -0
  770. package/dist/ui/screens/MediaScreen.js.map +1 -0
  771. package/dist/ui/screens/SettingsScreen.d.ts +4 -0
  772. package/dist/ui/screens/SettingsScreen.d.ts.map +1 -0
  773. package/dist/ui/screens/SettingsScreen.js +751 -0
  774. package/dist/ui/screens/SettingsScreen.js.map +1 -0
  775. package/dist/ui/screens/SocialShareScreen.d.ts +2 -0
  776. package/dist/ui/screens/SocialShareScreen.d.ts.map +1 -0
  777. package/dist/ui/screens/SocialShareScreen.js +224 -0
  778. package/dist/ui/screens/SocialShareScreen.js.map +1 -0
  779. package/dist/ui/screens/SubscriptionsScreen.d.ts +2 -0
  780. package/dist/ui/screens/SubscriptionsScreen.d.ts.map +1 -0
  781. package/dist/ui/screens/SubscriptionsScreen.js +441 -0
  782. package/dist/ui/screens/SubscriptionsScreen.js.map +1 -0
  783. package/dist/ui/screens/TopicsScreen.d.ts +2 -0
  784. package/dist/ui/screens/TopicsScreen.d.ts.map +1 -0
  785. package/dist/ui/screens/TopicsScreen.js +360 -0
  786. package/dist/ui/screens/TopicsScreen.js.map +1 -0
  787. package/dist/ui/screens/analytics-data.d.ts +19 -0
  788. package/dist/ui/screens/analytics-data.d.ts.map +1 -0
  789. package/dist/ui/screens/analytics-data.js +42 -0
  790. package/dist/ui/screens/analytics-data.js.map +1 -0
  791. package/dist/ui/screens/calendar-data.d.ts +45 -0
  792. package/dist/ui/screens/calendar-data.d.ts.map +1 -0
  793. package/dist/ui/screens/calendar-data.js +70 -0
  794. package/dist/ui/screens/calendar-data.js.map +1 -0
  795. package/dist/ui/screens/content-insights-data.d.ts +54 -0
  796. package/dist/ui/screens/content-insights-data.d.ts.map +1 -0
  797. package/dist/ui/screens/content-insights-data.js +82 -0
  798. package/dist/ui/screens/content-insights-data.js.map +1 -0
  799. package/dist/ui/screens/content-view.d.ts +21 -0
  800. package/dist/ui/screens/content-view.d.ts.map +1 -0
  801. package/dist/ui/screens/content-view.js +17 -0
  802. package/dist/ui/screens/content-view.js.map +1 -0
  803. package/dist/ui/screens/library-data.d.ts +76 -0
  804. package/dist/ui/screens/library-data.d.ts.map +1 -0
  805. package/dist/ui/screens/library-data.js +116 -0
  806. package/dist/ui/screens/library-data.js.map +1 -0
  807. package/dist/ui/screens/media-upload.d.ts +19 -0
  808. package/dist/ui/screens/media-upload.d.ts.map +1 -0
  809. package/dist/ui/screens/media-upload.js +187 -0
  810. package/dist/ui/screens/media-upload.js.map +1 -0
  811. package/dist/ui/screens/public-url.d.ts +24 -0
  812. package/dist/ui/screens/public-url.d.ts.map +1 -0
  813. package/dist/ui/screens/public-url.js +74 -0
  814. package/dist/ui/screens/public-url.js.map +1 -0
  815. package/dist/ui/screens/registry.d.ts +3 -0
  816. package/dist/ui/screens/registry.d.ts.map +1 -0
  817. package/dist/ui/screens/registry.js +38 -0
  818. package/dist/ui/screens/registry.js.map +1 -0
  819. package/dist/ui/screens/render-state.d.ts +105 -0
  820. package/dist/ui/screens/render-state.d.ts.map +1 -0
  821. package/dist/ui/screens/render-state.js +127 -0
  822. package/dist/ui/screens/render-state.js.map +1 -0
  823. package/dist/ui/screens/settings-data.d.ts +55 -0
  824. package/dist/ui/screens/settings-data.d.ts.map +1 -0
  825. package/dist/ui/screens/settings-data.js +89 -0
  826. package/dist/ui/screens/settings-data.js.map +1 -0
  827. package/dist/ui/screens/settings-panels-data.d.ts +58 -0
  828. package/dist/ui/screens/settings-panels-data.d.ts.map +1 -0
  829. package/dist/ui/screens/settings-panels-data.js +88 -0
  830. package/dist/ui/screens/settings-panels-data.js.map +1 -0
  831. package/dist/ui/screens/social-share-data.d.ts +307 -0
  832. package/dist/ui/screens/social-share-data.d.ts.map +1 -0
  833. package/dist/ui/screens/social-share-data.js +447 -0
  834. package/dist/ui/screens/social-share-data.js.map +1 -0
  835. package/dist/ui/screens/topics-data.d.ts +38 -0
  836. package/dist/ui/screens/topics-data.d.ts.map +1 -0
  837. package/dist/ui/screens/topics-data.js +50 -0
  838. package/dist/ui/screens/topics-data.js.map +1 -0
  839. package/dist/ui/styles/broadsheet.css +394 -0
  840. package/dist/ui/theme.d.ts +23 -0
  841. package/dist/ui/theme.d.ts.map +1 -0
  842. package/dist/ui/theme.js +87 -0
  843. package/dist/ui/theme.js.map +1 -0
  844. package/dist/ui/tweaks.d.ts +7 -0
  845. package/dist/ui/tweaks.d.ts.map +1 -0
  846. package/dist/ui/tweaks.js +31 -0
  847. package/dist/ui/tweaks.js.map +1 -0
  848. package/dist/ui/use-hash-router.d.ts +6 -0
  849. package/dist/ui/use-hash-router.d.ts.map +1 -0
  850. package/dist/ui/use-hash-router.js +15 -0
  851. package/dist/ui/use-hash-router.js.map +1 -0
  852. package/dist/ui/use-tweaks.d.ts +6 -0
  853. package/dist/ui/use-tweaks.d.ts.map +1 -0
  854. package/dist/ui/use-tweaks.js +24 -0
  855. package/dist/ui/use-tweaks.js.map +1 -0
  856. package/dist/ui/workspace-context.d.ts +34 -0
  857. package/dist/ui/workspace-context.d.ts.map +1 -0
  858. package/dist/ui/workspace-context.js +32 -0
  859. package/dist/ui/workspace-context.js.map +1 -0
  860. package/migrations/0001_create_cms_tables.sql +200 -0
  861. package/migrations/0002_review_softdelete_board.sql +112 -0
  862. package/migrations/0003_content_contributors.sql +16 -0
  863. package/migrations/0004_seo_faq_columns.sql +6 -0
  864. package/migrations/0005_revision_delta_columns.sql +5 -0
  865. package/migrations/0006_processing_trigger_token.sql +4 -0
  866. package/migrations/0007_content_slug_redirects.sql +9 -0
  867. package/migrations/0008_workspace_settings.sql +17 -0
  868. package/migrations/0009_workspace_memberships.sql +36 -0
  869. package/migrations/0010_api_keys_webhooks.sql +21 -0
  870. package/migrations/0011_notifications_activity.sql +22 -0
  871. package/migrations/0012_content_imports.sql +10 -0
  872. package/migrations/0013_media_normalization.sql +4 -0
  873. package/migrations/0014_api_key_prefix.sql +3 -0
  874. package/migrations/0015_cms_topics.sql +7 -0
  875. package/migrations/0016_content_insights.sql +53 -0
  876. package/package.json +82 -0
  877. package/src/engine/activity-log.ts +39 -0
  878. package/src/engine/ai-prompts.ts +124 -0
  879. package/src/engine/ai-writeback.ts +62 -0
  880. package/src/engine/api-keys.ts +239 -0
  881. package/src/engine/content-insights.ts +198 -0
  882. package/src/engine/contributors.ts +95 -0
  883. package/src/engine/cron.ts +62 -0
  884. package/src/engine/d1.ts +29 -0
  885. package/src/engine/foundry-dispatch.ts +417 -0
  886. package/src/engine/import-parsers.ts +478 -0
  887. package/src/engine/index.ts +230 -0
  888. package/src/engine/invites.ts +271 -0
  889. package/src/engine/members.ts +216 -0
  890. package/src/engine/membership-rules.ts +63 -0
  891. package/src/engine/og-render.ts +59 -0
  892. package/src/engine/publish-guard.ts +123 -0
  893. package/src/engine/publisher.ts +1032 -0
  894. package/src/engine/revisions.ts +292 -0
  895. package/src/engine/sanitize.ts +183 -0
  896. package/src/engine/seed-membership.ts +92 -0
  897. package/src/engine/seo.ts +72 -0
  898. package/src/engine/slug-redirects.ts +34 -0
  899. package/src/engine/slug.ts +33 -0
  900. package/src/engine/soft-delete.ts +42 -0
  901. package/src/engine/tags.ts +95 -0
  902. package/src/engine/topics.ts +158 -0
  903. package/src/engine/url-guard.ts +136 -0
  904. package/src/engine/validator/checks/bare-url-not-autolinked.ts +78 -0
  905. package/src/engine/validator/checks/broken-footnote-label.ts +33 -0
  906. package/src/engine/validator/checks/double-encoded-entities.ts +46 -0
  907. package/src/engine/validator/checks/empty-alt-text.ts +35 -0
  908. package/src/engine/validator/checks/heading-hierarchy-skip.ts +33 -0
  909. package/src/engine/validator/checks/html-comment-leak.ts +58 -0
  910. package/src/engine/validator/checks/iframe-missing-dims-and-wrapper.ts +34 -0
  911. package/src/engine/validator/checks/invisible-control-chars.ts +58 -0
  912. package/src/engine/validator/checks/paywall-marker-leak.ts +43 -0
  913. package/src/engine/validator/checks/raw-block-html.ts +65 -0
  914. package/src/engine/validator/checks/stale-body-html.ts +39 -0
  915. package/src/engine/validator/checks/unresolved-footnote-anchor.ts +61 -0
  916. package/src/engine/validator/checks/word-gdocs-paste-artifacts.ts +72 -0
  917. package/src/engine/validator/index.ts +385 -0
  918. package/src/engine/validator/scan.ts +103 -0
  919. package/src/engine/validator/types.ts +114 -0
  920. package/src/engine/webhook-signer.ts +139 -0
  921. package/src/engine/webhooks.ts +224 -0
  922. package/src/index.ts +79 -0
  923. package/src/integration/index.ts +298 -0
  924. package/src/integration/options.ts +30 -0
  925. package/src/integration/vite-plugin.ts +37 -0
  926. package/src/providers/index.ts +2 -0
  927. package/src/providers/null.ts +160 -0
  928. package/src/providers/types.ts +284 -0
  929. package/src/routes/ai.ts +461 -0
  930. package/src/routes/analytics.ts +78 -0
  931. package/src/routes/api-keys.ts +133 -0
  932. package/src/routes/authors.ts +282 -0
  933. package/src/routes/authz-matrix.ts +239 -0
  934. package/src/routes/calendar.ts +127 -0
  935. package/src/routes/config.ts +99 -0
  936. package/src/routes/content-insights.ts +159 -0
  937. package/src/routes/content.ts +1753 -0
  938. package/src/routes/context.ts +146 -0
  939. package/src/routes/cron.ts +27 -0
  940. package/src/routes/dashboard.ts +174 -0
  941. package/src/routes/imports.ts +190 -0
  942. package/src/routes/index.ts +295 -0
  943. package/src/routes/media-lib.ts +405 -0
  944. package/src/routes/media.ts +944 -0
  945. package/src/routes/preview.ts +182 -0
  946. package/src/routes/rbac-invites.ts +220 -0
  947. package/src/routes/rbac.ts +155 -0
  948. package/src/routes/shell.ts +163 -0
  949. package/src/routes/subscriptions.ts +167 -0
  950. package/src/routes/tags.ts +93 -0
  951. package/src/routes/topics.ts +58 -0
  952. package/src/routes/webhooks.ts +233 -0
  953. package/src/schema/index.ts +45 -0
  954. package/src/schema/insights-ingest.ts +126 -0
  955. package/src/schema/migrations.ts +599 -0
  956. package/src/schema/tables.ts +59 -0
  957. package/src/schema/types.ts +576 -0
  958. package/src/ui/api/_authz.ts +100 -0
  959. package/src/ui/api/_content-config.ts +75 -0
  960. package/src/ui/api/activity.ts +33 -0
  961. package/src/ui/api/analytics.ts +42 -0
  962. package/src/ui/api/authors/[id].ts +23 -0
  963. package/src/ui/api/authors.ts +19 -0
  964. package/src/ui/api/calendar.ts +21 -0
  965. package/src/ui/api/content/[id]/ai/headlines.ts +10 -0
  966. package/src/ui/api/content/[id]/ai/meta-description.ts +11 -0
  967. package/src/ui/api/content/[id]/ai/og-image.ts +10 -0
  968. package/src/ui/api/content/[id]/ai/proofread.ts +10 -0
  969. package/src/ui/api/content/[id]/ai/takeaways.ts +11 -0
  970. package/src/ui/api/content/[id]/contributors.ts +13 -0
  971. package/src/ui/api/content/[id]/preview-token.ts +21 -0
  972. package/src/ui/api/content/[id]/revisions/[rev]/restore.ts +12 -0
  973. package/src/ui/api/content/[id]/revisions/[rev].ts +11 -0
  974. package/src/ui/api/content/[id]/revisions.ts +9 -0
  975. package/src/ui/api/content/[id]/seo-score.ts +10 -0
  976. package/src/ui/api/content/[id].ts +23 -0
  977. package/src/ui/api/content/bulk.ts +10 -0
  978. package/src/ui/api/content/counts.ts +11 -0
  979. package/src/ui/api/content/foundry-callback.ts +11 -0
  980. package/src/ui/api/content/import/confirm.ts +16 -0
  981. package/src/ui/api/content/import/parse.ts +16 -0
  982. package/src/ui/api/content/insights-ingest.ts +24 -0
  983. package/src/ui/api/content-insights/dismiss.ts +23 -0
  984. package/src/ui/api/content-insights/index.ts +21 -0
  985. package/src/ui/api/content-insights/undismiss.ts +23 -0
  986. package/src/ui/api/content.ts +21 -0
  987. package/src/ui/api/dashboard.ts +28 -0
  988. package/src/ui/api/me.ts +23 -0
  989. package/src/ui/api/media/[id].ts +22 -0
  990. package/src/ui/api/media/images.ts +9 -0
  991. package/src/ui/api/media/library/[id].ts +9 -0
  992. package/src/ui/api/media/library.ts +22 -0
  993. package/src/ui/api/media/podcast/abort.ts +6 -0
  994. package/src/ui/api/media/podcast/complete.ts +6 -0
  995. package/src/ui/api/media/podcast/init.ts +6 -0
  996. package/src/ui/api/media/podcast/part.ts +6 -0
  997. package/src/ui/api/media/podcast.ts +9 -0
  998. package/src/ui/api/media/videos/abort.ts +9 -0
  999. package/src/ui/api/media/videos/complete.ts +9 -0
  1000. package/src/ui/api/media/videos/init.ts +9 -0
  1001. package/src/ui/api/media/videos/part.ts +9 -0
  1002. package/src/ui/api/notifications.ts +26 -0
  1003. package/src/ui/api/search.ts +23 -0
  1004. package/src/ui/api/settings/api-keys/[id].ts +28 -0
  1005. package/src/ui/api/settings/api-keys.ts +25 -0
  1006. package/src/ui/api/settings/domains.ts +37 -0
  1007. package/src/ui/api/settings/integrations.ts +40 -0
  1008. package/src/ui/api/settings/members/[userId].ts +33 -0
  1009. package/src/ui/api/settings/members/invite.ts +27 -0
  1010. package/src/ui/api/settings/members.ts +23 -0
  1011. package/src/ui/api/settings/webhooks/[id]/test.ts +30 -0
  1012. package/src/ui/api/settings/webhooks/[id].ts +29 -0
  1013. package/src/ui/api/settings/webhooks.ts +27 -0
  1014. package/src/ui/api/subscriptions.ts +56 -0
  1015. package/src/ui/api/tags/[id].ts +24 -0
  1016. package/src/ui/api/tags/index.ts +18 -0
  1017. package/src/ui/api/topics/[id].ts +20 -0
  1018. package/src/ui/api/topics/index.ts +17 -0
  1019. package/src/ui/api/workspace-settings.ts +26 -0
  1020. package/src/ui/client/boot-state.ts +42 -0
  1021. package/src/ui/client/mount.tsx +41 -0
  1022. package/src/ui/commands.ts +62 -0
  1023. package/src/ui/components/CmsApp.tsx +149 -0
  1024. package/src/ui/components/CommandPalette.tsx +118 -0
  1025. package/src/ui/components/NoAccessScreen.tsx +79 -0
  1026. package/src/ui/components/ShareModal.tsx +650 -0
  1027. package/src/ui/components/SharePickers.tsx +790 -0
  1028. package/src/ui/components/ShareStatsPanel.tsx +721 -0
  1029. package/src/ui/components/Sidebar.tsx +86 -0
  1030. package/src/ui/components/SiteSwitcher.tsx +100 -0
  1031. package/src/ui/components/Topbar.tsx +93 -0
  1032. package/src/ui/editor/AiAssistPanel.tsx +407 -0
  1033. package/src/ui/editor/ContentForm.tsx +1462 -0
  1034. package/src/ui/editor/Rte.tsx +382 -0
  1035. package/src/ui/editor/ai-assist.ts +139 -0
  1036. package/src/ui/editor/autosave.ts +36 -0
  1037. package/src/ui/editor/content-payload.ts +125 -0
  1038. package/src/ui/editor/editor-media-upload.ts +26 -0
  1039. package/src/ui/editor/serialize.ts +522 -0
  1040. package/src/ui/editor/tweet-embed.ts +60 -0
  1041. package/src/ui/hash-router.ts +30 -0
  1042. package/src/ui/icons.tsx +208 -0
  1043. package/src/ui/inspector/Field.tsx +30 -0
  1044. package/src/ui/inspector/FoundryTab.tsx +613 -0
  1045. package/src/ui/inspector/HistoryTab.tsx +482 -0
  1046. package/src/ui/inspector/Inspector.tsx +328 -0
  1047. package/src/ui/inspector/OrganizeTab.tsx +534 -0
  1048. package/src/ui/inspector/PublishTab.tsx +626 -0
  1049. package/src/ui/inspector/Section.tsx +81 -0
  1050. package/src/ui/inspector/SeoTab.tsx +573 -0
  1051. package/src/ui/inspector/foundry-stages.ts +140 -0
  1052. package/src/ui/inspector/inspector-data.ts +232 -0
  1053. package/src/ui/inspector/organize-data.ts +51 -0
  1054. package/src/ui/inspector/revision-diff.ts +213 -0
  1055. package/src/ui/inspector/seo-helpers.ts +71 -0
  1056. package/src/ui/inspector/tab-visibility.ts +37 -0
  1057. package/src/ui/nav.ts +48 -0
  1058. package/src/ui/pages/admin.astro +32 -0
  1059. package/src/ui/preview/draft-page.tsx +395 -0
  1060. package/src/ui/preview/preview-layout.ts +49 -0
  1061. package/src/ui/screens/AnalyticsScreen.tsx +938 -0
  1062. package/src/ui/screens/AuthorsScreen.tsx +524 -0
  1063. package/src/ui/screens/CalendarScreen.tsx +694 -0
  1064. package/src/ui/screens/ContentInsightsScreen.tsx +417 -0
  1065. package/src/ui/screens/ContentRoute.tsx +35 -0
  1066. package/src/ui/screens/DashboardScreen.tsx +654 -0
  1067. package/src/ui/screens/EditorScreen.tsx +673 -0
  1068. package/src/ui/screens/LibraryScreen.tsx +1350 -0
  1069. package/src/ui/screens/MediaScreen.tsx +357 -0
  1070. package/src/ui/screens/SettingsScreen.tsx +1841 -0
  1071. package/src/ui/screens/SocialShareScreen.tsx +670 -0
  1072. package/src/ui/screens/SubscriptionsScreen.tsx +1240 -0
  1073. package/src/ui/screens/TopicsScreen.tsx +912 -0
  1074. package/src/ui/screens/analytics-data.ts +68 -0
  1075. package/src/ui/screens/calendar-data.ts +126 -0
  1076. package/src/ui/screens/content-insights-data.ts +127 -0
  1077. package/src/ui/screens/content-view.ts +30 -0
  1078. package/src/ui/screens/library-data.ts +177 -0
  1079. package/src/ui/screens/media-upload.ts +283 -0
  1080. package/src/ui/screens/public-url.ts +81 -0
  1081. package/src/ui/screens/registry.tsx +53 -0
  1082. package/src/ui/screens/render-state.ts +228 -0
  1083. package/src/ui/screens/settings-data.ts +140 -0
  1084. package/src/ui/screens/settings-panels-data.ts +142 -0
  1085. package/src/ui/screens/social-share-data.ts +753 -0
  1086. package/src/ui/screens/topics-data.ts +75 -0
  1087. package/src/ui/styles/broadsheet.css +394 -0
  1088. package/src/ui/theme.ts +104 -0
  1089. package/src/ui/tweaks.ts +37 -0
  1090. package/src/ui/use-hash-router.ts +17 -0
  1091. package/src/ui/use-tweaks.ts +31 -0
  1092. package/src/ui/workspace-context.tsx +62 -0
  1093. package/src/virtual.d.ts +4 -0
@@ -0,0 +1,1350 @@
1
+ // src/ui/screens/LibraryScreen.tsx — content library: list/board/filters/bulk/import.
2
+ //
3
+ // Status tabs (counts from GET /content/counts), type segment, search box,
4
+ // list table + BoardView (kanban columns), selection/bulk bar (POST /content/bulk),
5
+ // Import button (parse flow), New Content (→ openDoc({id:null, contentType})).
6
+ //
7
+ // Ported from the library.jsx design surface. All data logic lives in the
8
+ // pure helpers in library-data.ts; this file is the React shell.
9
+ //
10
+ // NOTE: the per-row Share action is a STUB (P6 feature) — it renders as an
11
+ // icon button that is currently a no-op.
12
+
13
+ import { useCallback, useEffect, useReducer, useRef, useState } from 'react'
14
+ import type { ContentStatus, ContentType } from '../../schema/types.js'
15
+ import { buildSharePrefill, ShareModal } from '../components/ShareModal.js'
16
+ import { Icon } from '../icons.js'
17
+ import { useWorkspace } from '../workspace-context.js'
18
+ import {
19
+ deriveStatusCounts,
20
+ filterRows,
21
+ groupByStatus,
22
+ type LibraryRow,
23
+ type StatusCounts,
24
+ } from './library-data.js'
25
+
26
+ // ---------------------------------------------------------------------------
27
+ // Props
28
+ // ---------------------------------------------------------------------------
29
+
30
+ export interface LibraryScreenProps {
31
+ openDoc: (id: string | null, contentType?: ContentType) => void
32
+ }
33
+
34
+ // ---------------------------------------------------------------------------
35
+ // Status tab metadata
36
+ // ---------------------------------------------------------------------------
37
+
38
+ const STATUS_TABS: Array<{ key: keyof StatusCounts; label: string }> = [
39
+ { key: 'all', label: 'All' },
40
+ { key: 'draft', label: 'Draft' },
41
+ { key: 'review', label: 'Review' },
42
+ { key: 'scheduled', label: 'Scheduled' },
43
+ { key: 'published', label: 'Published' },
44
+ { key: 'processing', label: 'Processing' },
45
+ ]
46
+
47
+ const TYPE_OPTIONS: Array<{ value: ContentType | ''; label: string }> = [
48
+ { value: '', label: 'All types' },
49
+ { value: 'article', label: 'Article' },
50
+ { value: 'video', label: 'Video' },
51
+ { value: 'podcast', label: 'Podcast' },
52
+ { value: 'newsletter', label: 'Newsletter' },
53
+ ]
54
+
55
+ const CONTENT_TYPE_ICONS: Record<ContentType, string> = {
56
+ article: 'doc',
57
+ video: 'video',
58
+ podcast: 'mic',
59
+ newsletter: 'mail',
60
+ }
61
+
62
+ // ---------------------------------------------------------------------------
63
+ // Internal state for this screen
64
+ // ---------------------------------------------------------------------------
65
+
66
+ interface LibraryState {
67
+ rows: LibraryRow[]
68
+ counts: StatusCounts
69
+ loading: boolean
70
+ error: string | null
71
+ /** The currently active status tab filter. */
72
+ statusFilter: ContentStatus | 'all' | 'processing'
73
+ typeFilter: ContentType | ''
74
+ q: string
75
+ view: 'list' | 'board'
76
+ /** Row IDs currently selected. */
77
+ selected: Set<string>
78
+ /** Whether the import modal is open. */
79
+ importOpen: boolean
80
+ /** The row currently being shared (opens ShareModal). Null = closed. */
81
+ shareRow: LibraryRow | null
82
+ /** Cursor for next-page load. */
83
+ cursor: string | null
84
+ hasMore: boolean
85
+ }
86
+
87
+ type LibraryAction =
88
+ | { type: 'fetch-start' }
89
+ | { type: 'fetch-ok'; rows: LibraryRow[]; counts: StatusCounts; cursor: string | null }
90
+ | { type: 'fetch-error'; error: string }
91
+ | { type: 'set-status-filter'; status: ContentStatus | 'all' | 'processing' }
92
+ | { type: 'set-type-filter'; contentType: ContentType | '' }
93
+ | { type: 'set-q'; q: string }
94
+ | { type: 'set-view'; view: 'list' | 'board' }
95
+ | { type: 'toggle-select'; id: string }
96
+ | { type: 'select-all' }
97
+ | { type: 'clear-selection' }
98
+ | { type: 'toggle-import' }
99
+ | { type: 'close-import' }
100
+ | { type: 'open-share'; row: LibraryRow }
101
+ | { type: 'close-share' }
102
+ | { type: 'bulk-done'; removedIds?: string[] }
103
+
104
+ function initLibrary(): LibraryState {
105
+ return {
106
+ rows: [],
107
+ counts: { all: 0, draft: 0, review: 0, scheduled: 0, published: 0, processing: 0 },
108
+ loading: true,
109
+ error: null,
110
+ statusFilter: 'all',
111
+ typeFilter: '',
112
+ q: '',
113
+ view: 'list',
114
+ selected: new Set(),
115
+ importOpen: false,
116
+ shareRow: null,
117
+ cursor: null,
118
+ hasMore: false,
119
+ }
120
+ }
121
+
122
+ function libraryReducer(state: LibraryState, action: LibraryAction): LibraryState {
123
+ switch (action.type) {
124
+ case 'fetch-start':
125
+ return { ...state, loading: true, error: null }
126
+ case 'fetch-ok':
127
+ return {
128
+ ...state,
129
+ loading: false,
130
+ rows: action.rows,
131
+ counts: action.counts,
132
+ cursor: action.cursor,
133
+ hasMore: action.cursor !== null,
134
+ }
135
+ case 'fetch-error':
136
+ return { ...state, loading: false, error: action.error }
137
+ case 'set-status-filter':
138
+ return { ...state, statusFilter: action.status, selected: new Set() }
139
+ case 'set-type-filter':
140
+ return { ...state, typeFilter: action.contentType, selected: new Set() }
141
+ case 'set-q':
142
+ return { ...state, q: action.q }
143
+ case 'set-view':
144
+ return { ...state, view: action.view, selected: new Set() }
145
+ case 'toggle-select': {
146
+ const next = new Set(state.selected)
147
+ if (next.has(action.id)) {
148
+ next.delete(action.id)
149
+ } else {
150
+ next.add(action.id)
151
+ }
152
+ return { ...state, selected: next }
153
+ }
154
+ case 'select-all': {
155
+ const visible = getVisibleRows(state)
156
+ return { ...state, selected: new Set(visible.map((r) => r.id)) }
157
+ }
158
+ case 'clear-selection':
159
+ return { ...state, selected: new Set() }
160
+ case 'toggle-import':
161
+ return { ...state, importOpen: !state.importOpen }
162
+ case 'close-import':
163
+ return { ...state, importOpen: false }
164
+ case 'open-share':
165
+ return { ...state, shareRow: action.row }
166
+ case 'close-share':
167
+ return { ...state, shareRow: null }
168
+ case 'bulk-done': {
169
+ const removed = action.removedIds ?? []
170
+ const next = state.rows.filter((r) => !removed.includes(r.id))
171
+ return {
172
+ ...state,
173
+ rows: next,
174
+ counts: deriveStatusCounts(next),
175
+ selected: new Set(),
176
+ }
177
+ }
178
+ default:
179
+ return state
180
+ }
181
+ }
182
+
183
+ /** Returns the currently visible rows after applying client-side filter. */
184
+ function getVisibleRows(state: LibraryState): LibraryRow[] {
185
+ return filterRows(state.rows, {
186
+ status:
187
+ state.statusFilter !== 'all' && state.statusFilter !== 'processing'
188
+ ? (state.statusFilter as ContentStatus)
189
+ : undefined,
190
+ type: state.typeFilter !== '' ? state.typeFilter : undefined,
191
+ q: state.q || undefined,
192
+ })
193
+ }
194
+
195
+ // ---------------------------------------------------------------------------
196
+ // Main component
197
+ // ---------------------------------------------------------------------------
198
+
199
+ export function LibraryScreen({ openDoc }: LibraryScreenProps) {
200
+ const { state: ws } = useWorkspace()
201
+ const [lib, dispatch] = useReducer(libraryReducer, undefined, initLibrary)
202
+ const searchRef = useRef<HTMLInputElement>(null)
203
+
204
+ // Build the API base URL from the workspace active id
205
+ const baseUrl = `/admin/api`
206
+
207
+ const fetchContent = useCallback(async () => {
208
+ dispatch({ type: 'fetch-start' })
209
+ try {
210
+ // Fetch list + counts in parallel
211
+ const params = new URLSearchParams({ limit: '50' })
212
+ if (lib.statusFilter !== 'all' && lib.statusFilter !== 'processing') {
213
+ params.set('status', lib.statusFilter)
214
+ }
215
+ if (lib.typeFilter) params.set('types', lib.typeFilter)
216
+ if (lib.q) params.set('q', lib.q)
217
+
218
+ const [listRes, countsRes] = await Promise.all([
219
+ fetch(`${baseUrl}/content?${params.toString()}`),
220
+ fetch(`${baseUrl}/content/counts`),
221
+ ])
222
+
223
+ if (!listRes.ok || !countsRes.ok) {
224
+ const msg = !listRes.ok
225
+ ? `List error ${listRes.status}`
226
+ : `Counts error ${countsRes.status}`
227
+ dispatch({ type: 'fetch-error', error: msg })
228
+ return
229
+ }
230
+
231
+ const listJson = (await listRes.json()) as {
232
+ items: LibraryRow[]
233
+ nextCursor?: string | null
234
+ }
235
+ const countsJson = (await countsRes.json()) as { counts: StatusCounts }
236
+
237
+ dispatch({
238
+ type: 'fetch-ok',
239
+ rows: listJson.items ?? [],
240
+ counts: countsJson.counts ?? deriveStatusCounts(listJson.items ?? []),
241
+ cursor: listJson.nextCursor ?? null,
242
+ })
243
+ } catch (err) {
244
+ dispatch({ type: 'fetch-error', error: String(err) })
245
+ }
246
+ }, [lib.statusFilter, lib.typeFilter, lib.q]) // fetchContent identity changes when filters change → useEffect re-runs
247
+
248
+ // Initial fetch + refetch when filters change
249
+ useEffect(() => {
250
+ void fetchContent()
251
+ }, [fetchContent])
252
+
253
+ const visible = getVisibleRows(lib)
254
+ const allVisibleSelected = visible.length > 0 && visible.every((r) => lib.selected.has(r.id))
255
+
256
+ return (
257
+ <div className="page page-wide library-screen fade-in" style={{ maxWidth: 1340 }}>
258
+ {/* Header */}
259
+ <div className="page-head">
260
+ <div>
261
+ <h1 className="page-title">Content</h1>
262
+ <div className="page-sub">
263
+ {lib.counts.all} items across articles, video, podcasts and newsletters.
264
+ </div>
265
+ </div>
266
+ <div style={{ display: 'flex', gap: 8 }}>
267
+ {/* Import */}
268
+ <button type="button" className="btn" onClick={() => dispatch({ type: 'toggle-import' })}>
269
+ <Icon name="upload" size={16} />
270
+ <span>Import</span>
271
+ </button>
272
+
273
+ {/* New content dropdown stub — opens a menu per type */}
274
+ <NewContentMenu openDoc={openDoc} />
275
+ </div>
276
+ </div>
277
+
278
+ {/* Status tabs */}
279
+ <div
280
+ style={{
281
+ display: 'flex',
282
+ alignItems: 'center',
283
+ gap: 4,
284
+ borderBottom: '1px solid var(--border)',
285
+ marginBottom: 16,
286
+ }}
287
+ >
288
+ {STATUS_TABS.map((tab) => {
289
+ const active = lib.statusFilter === tab.key
290
+ const count =
291
+ tab.key === 'all'
292
+ ? lib.counts.all
293
+ : tab.key === 'processing'
294
+ ? lib.counts.processing
295
+ : lib.counts[tab.key as keyof Omit<StatusCounts, 'all' | 'processing'>]
296
+ return (
297
+ <button
298
+ key={tab.key}
299
+ type="button"
300
+ onClick={() =>
301
+ dispatch({
302
+ type: 'set-status-filter',
303
+ status: tab.key as LibraryState['statusFilter'],
304
+ })
305
+ }
306
+ style={{
307
+ ...tabStyle,
308
+ color: active ? 'var(--ink)' : 'var(--ink-muted)',
309
+ borderBottom: active ? '2px solid var(--accent)' : '2px solid transparent',
310
+ }}
311
+ >
312
+ {tab.label}
313
+ <span
314
+ className="tnum"
315
+ style={{
316
+ fontSize: 11,
317
+ color: 'var(--ink-faint)',
318
+ background: 'var(--hover)',
319
+ padding: '1px 7px',
320
+ borderRadius: 12,
321
+ }}
322
+ >
323
+ {count}
324
+ </span>
325
+ </button>
326
+ )
327
+ })}
328
+ </div>
329
+
330
+ {/* Filter row */}
331
+ <div
332
+ style={{
333
+ display: 'flex',
334
+ alignItems: 'center',
335
+ gap: 10,
336
+ marginBottom: 14,
337
+ flexWrap: 'wrap',
338
+ }}
339
+ >
340
+ {/* Search */}
341
+ <div className="search" style={{ maxWidth: 300, height: 34 }}>
342
+ <Icon name="search" size={15} />
343
+ <input
344
+ ref={searchRef}
345
+ type="search"
346
+ placeholder="Title or slug…"
347
+ value={lib.q}
348
+ onChange={(e) => dispatch({ type: 'set-q', q: e.currentTarget.value })}
349
+ />
350
+ </div>
351
+
352
+ {/* Type filter — segmented control */}
353
+ <div className="seg">
354
+ {TYPE_OPTIONS.map((o) => (
355
+ <button
356
+ key={o.value || 'all'}
357
+ type="button"
358
+ className={lib.typeFilter === o.value ? 'on' : ''}
359
+ onClick={() => dispatch({ type: 'set-type-filter', contentType: o.value })}
360
+ >
361
+ {o.value !== '' && (
362
+ <Icon
363
+ name={CONTENT_TYPE_ICONS[o.value] as Parameters<typeof Icon>[0]['name']}
364
+ size={14}
365
+ />
366
+ )}
367
+ {o.label}
368
+ </button>
369
+ ))}
370
+ </div>
371
+
372
+ <div style={{ flex: 1 }} />
373
+
374
+ {/* View toggle — segmented control */}
375
+ <div className="seg">
376
+ <button
377
+ type="button"
378
+ title="List view"
379
+ className={lib.view === 'list' ? 'on' : ''}
380
+ onClick={() => dispatch({ type: 'set-view', view: 'list' })}
381
+ >
382
+ <Icon name="rows" size={15} />
383
+ </button>
384
+ <button
385
+ type="button"
386
+ title="Board view"
387
+ className={lib.view === 'board' ? 'on' : ''}
388
+ onClick={() => dispatch({ type: 'set-view', view: 'board' })}
389
+ >
390
+ <Icon name="columns" size={15} />
391
+ </button>
392
+ </div>
393
+ </div>
394
+
395
+ {/* Bulk action bar */}
396
+ {lib.selected.size > 0 && (
397
+ <BulkBar
398
+ selectedIds={Array.from(lib.selected)}
399
+ workspaceId={ws.activeId}
400
+ onDone={(removedIds) => dispatch({ type: 'bulk-done', removedIds })}
401
+ onCancel={() => dispatch({ type: 'clear-selection' })}
402
+ />
403
+ )}
404
+
405
+ {/* Content area */}
406
+ {lib.loading && (
407
+ <div className="panel panel-pad" style={{ textAlign: 'center', color: 'var(--ink-muted)' }}>
408
+ Loading…
409
+ </div>
410
+ )}
411
+
412
+ {!lib.loading && lib.error && (
413
+ <div className="panel panel-pad" style={{ textAlign: 'center', color: 'var(--red)' }}>
414
+ {lib.error}
415
+ <button
416
+ type="button"
417
+ className="btn sm"
418
+ onClick={() => void fetchContent()}
419
+ style={{ marginLeft: 12 }}
420
+ >
421
+ Retry
422
+ </button>
423
+ </div>
424
+ )}
425
+
426
+ {!lib.loading && !lib.error && visible.length === 0 && (
427
+ <div
428
+ className="panel panel-pad"
429
+ style={{ padding: 60, textAlign: 'center', color: 'var(--ink-muted)' }}
430
+ >
431
+ <Icon
432
+ name="doc"
433
+ size={32}
434
+ style={{ opacity: 0.3, display: 'block', margin: '0 auto 12px' }}
435
+ />
436
+ <div style={{ fontSize: 14 }}>No content found.</div>
437
+ {lib.q && (
438
+ <button
439
+ type="button"
440
+ className="btn sm"
441
+ onClick={() => dispatch({ type: 'set-q', q: '' })}
442
+ style={{ marginTop: 8 }}
443
+ >
444
+ Clear search
445
+ </button>
446
+ )}
447
+ </div>
448
+ )}
449
+
450
+ {!lib.loading &&
451
+ !lib.error &&
452
+ visible.length > 0 &&
453
+ (lib.view === 'list' ? (
454
+ <ListView
455
+ rows={visible}
456
+ selected={lib.selected}
457
+ allSelected={allVisibleSelected}
458
+ onToggleSelect={(id) => dispatch({ type: 'toggle-select', id })}
459
+ onSelectAll={() =>
460
+ allVisibleSelected
461
+ ? dispatch({ type: 'clear-selection' })
462
+ : dispatch({ type: 'select-all' })
463
+ }
464
+ onOpen={(row) => openDoc(row.id, row.type)}
465
+ onShare={(row) => dispatch({ type: 'open-share', row })}
466
+ />
467
+ ) : (
468
+ <BoardView rows={visible} onOpen={(row) => openDoc(row.id, row.type)} />
469
+ ))}
470
+
471
+ {/* Import modal */}
472
+ {lib.importOpen && (
473
+ <ImportModal
474
+ workspaceId={ws.activeId}
475
+ onClose={() => dispatch({ type: 'close-import' })}
476
+ onImported={() => {
477
+ dispatch({ type: 'close-import' })
478
+ void fetchContent()
479
+ }}
480
+ />
481
+ )}
482
+
483
+ {/* Share modal — opened by the per-row Share button */}
484
+ {lib.shareRow && (
485
+ <ShareModal
486
+ prefill={buildSharePrefill(lib.shareRow)}
487
+ onClose={() => dispatch({ type: 'close-share' })}
488
+ />
489
+ )}
490
+ </div>
491
+ )
492
+ }
493
+
494
+ // ---------------------------------------------------------------------------
495
+ // New content menu
496
+ // ---------------------------------------------------------------------------
497
+
498
+ function NewContentMenu({ openDoc }: { openDoc: LibraryScreenProps['openDoc'] }) {
499
+ const [open, setOpen] = useState(false)
500
+ const ref = useRef<HTMLDivElement>(null)
501
+
502
+ useEffect(() => {
503
+ if (!open) return
504
+ function handler(e: MouseEvent) {
505
+ if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false)
506
+ }
507
+ document.addEventListener('mousedown', handler)
508
+ return () => document.removeEventListener('mousedown', handler)
509
+ }, [open])
510
+
511
+ const types: ContentType[] = ['article', 'video', 'podcast', 'newsletter']
512
+
513
+ return (
514
+ <div ref={ref} style={{ position: 'relative' }}>
515
+ <button type="button" className="btn primary" onClick={() => setOpen((v) => !v)}>
516
+ <Icon name="plus" size={16} />
517
+ <span>New content</span>
518
+ <Icon name="chevDown" size={13} />
519
+ </button>
520
+
521
+ {open && (
522
+ <div
523
+ className="panel"
524
+ style={{
525
+ position: 'absolute',
526
+ top: 'calc(100% + 4px)',
527
+ right: 0,
528
+ boxShadow: 'var(--shadow-md)',
529
+ minWidth: 160,
530
+ zIndex: 100,
531
+ overflow: 'hidden',
532
+ }}
533
+ >
534
+ {types.map((t) => (
535
+ <button
536
+ key={t}
537
+ type="button"
538
+ onClick={() => {
539
+ setOpen(false)
540
+ openDoc(null, t)
541
+ }}
542
+ style={{
543
+ width: '100%',
544
+ display: 'flex',
545
+ alignItems: 'center',
546
+ gap: 8,
547
+ padding: '9px 14px',
548
+ background: 'transparent',
549
+ border: 'none',
550
+ cursor: 'pointer',
551
+ fontSize: 13,
552
+ color: 'var(--ink)',
553
+ textAlign: 'left',
554
+ }}
555
+ >
556
+ <Icon
557
+ name={CONTENT_TYPE_ICONS[t] as Parameters<typeof Icon>[0]['name']}
558
+ size={15}
559
+ style={{ color: 'var(--ink-muted)' }}
560
+ />
561
+ {t.charAt(0).toUpperCase() + t.slice(1)}
562
+ </button>
563
+ ))}
564
+ </div>
565
+ )}
566
+ </div>
567
+ )
568
+ }
569
+
570
+ // ---------------------------------------------------------------------------
571
+ // List view
572
+ // ---------------------------------------------------------------------------
573
+
574
+ function ListView({
575
+ rows,
576
+ selected,
577
+ allSelected,
578
+ onToggleSelect,
579
+ onSelectAll,
580
+ onOpen,
581
+ onShare,
582
+ }: {
583
+ rows: LibraryRow[]
584
+ selected: Set<string>
585
+ allSelected: boolean
586
+ onToggleSelect: (id: string) => void
587
+ onSelectAll: () => void
588
+ onOpen: (row: LibraryRow) => void
589
+ onShare: (row: LibraryRow) => void
590
+ }) {
591
+ return (
592
+ <div className="panel" style={{ overflow: 'hidden' }}>
593
+ <div style={{ overflowX: 'auto' }}>
594
+ <table className="tbl" style={{ minWidth: 820 }}>
595
+ <thead>
596
+ <tr>
597
+ <th style={{ paddingLeft: 16, width: 34 }}>
598
+ <input
599
+ type="checkbox"
600
+ checked={allSelected}
601
+ onChange={onSelectAll}
602
+ aria-label="Select all"
603
+ />
604
+ </th>
605
+ <th>Title</th>
606
+ <th style={{ width: 110 }}>Type</th>
607
+ <th style={{ width: 120 }}>Status</th>
608
+ <th style={{ width: 150 }}>Author</th>
609
+ <th style={{ width: 130 }}>Updated</th>
610
+ <th style={{ width: 60 }} />
611
+ </tr>
612
+ </thead>
613
+ <tbody>
614
+ {rows.map((row) => (
615
+ <ListRow
616
+ key={row.id}
617
+ row={row}
618
+ isSelected={selected.has(row.id)}
619
+ onSelect={() => onToggleSelect(row.id)}
620
+ onOpen={() => onOpen(row)}
621
+ onShare={() => onShare(row)}
622
+ />
623
+ ))}
624
+ </tbody>
625
+ </table>
626
+ </div>
627
+ </div>
628
+ )
629
+ }
630
+
631
+ function ListRow({
632
+ row,
633
+ isSelected,
634
+ onSelect,
635
+ onOpen,
636
+ onShare,
637
+ }: {
638
+ row: LibraryRow
639
+ isSelected: boolean
640
+ onSelect: () => void
641
+ onOpen: () => void
642
+ onShare: () => void
643
+ }) {
644
+ return (
645
+ <tr style={isSelected ? { background: 'var(--accent-tint)' } : undefined} onClick={onOpen}>
646
+ <td
647
+ style={{ paddingLeft: 16, width: 34 }}
648
+ onClick={(e) => {
649
+ e.stopPropagation()
650
+ onSelect()
651
+ }}
652
+ onKeyUp={(e) => {
653
+ if (e.key === 'Enter' || e.key === ' ') {
654
+ e.stopPropagation()
655
+ onSelect()
656
+ }
657
+ }}
658
+ >
659
+ <span
660
+ style={{
661
+ width: 17,
662
+ height: 17,
663
+ borderRadius: 4,
664
+ border: '1.5px solid var(--field-border)',
665
+ display: 'grid',
666
+ placeItems: 'center',
667
+ background: isSelected ? 'var(--accent)' : 'transparent',
668
+ borderColor: isSelected ? 'var(--accent)' : 'var(--field-border)',
669
+ }}
670
+ >
671
+ {isSelected && <Icon name="check" size={12} style={{ color: '#fff' }} />}
672
+ </span>
673
+ </td>
674
+
675
+ <td style={{ maxWidth: 380 }}>
676
+ <div style={{ display: 'flex', alignItems: 'center', gap: 11 }}>
677
+ <div
678
+ style={{
679
+ width: 34,
680
+ height: 34,
681
+ borderRadius: 7,
682
+ flex: 'none',
683
+ display: 'grid',
684
+ placeItems: 'center',
685
+ background: 'var(--panel-2)',
686
+ color: 'var(--ink-muted)',
687
+ }}
688
+ >
689
+ <Icon
690
+ name={CONTENT_TYPE_ICONS[row.type] as Parameters<typeof Icon>[0]['name']}
691
+ size={16}
692
+ />
693
+ </div>
694
+ <div style={{ minWidth: 0 }}>
695
+ <div
696
+ style={{
697
+ fontWeight: 600,
698
+ color: 'var(--ink)',
699
+ whiteSpace: 'nowrap',
700
+ overflow: 'hidden',
701
+ textOverflow: 'ellipsis',
702
+ display: 'flex',
703
+ alignItems: 'center',
704
+ gap: 7,
705
+ }}
706
+ >
707
+ {row.title || '(Untitled)'}
708
+ {row.featured && (
709
+ <span title="Premium" style={{ color: 'var(--gold)', flex: 'none' }}>
710
+ <Icon name="star" size={13} />
711
+ </span>
712
+ )}
713
+ </div>
714
+ <div
715
+ className="mono"
716
+ style={{
717
+ fontSize: 11.5,
718
+ color: 'var(--ink-faint)',
719
+ whiteSpace: 'nowrap',
720
+ overflow: 'hidden',
721
+ textOverflow: 'ellipsis',
722
+ }}
723
+ >
724
+ /{row.slug}
725
+ </div>
726
+ </div>
727
+ </div>
728
+ </td>
729
+
730
+ <td>
731
+ <span className="type-tag">
732
+ <Icon
733
+ name={CONTENT_TYPE_ICONS[row.type] as Parameters<typeof Icon>[0]['name']}
734
+ size={14}
735
+ />{' '}
736
+ {row.type.charAt(0).toUpperCase() + row.type.slice(1)}
737
+ </span>
738
+ </td>
739
+
740
+ <td>
741
+ <StatusPill row={row} />
742
+ </td>
743
+
744
+ <td>
745
+ <span style={{ fontSize: 12.5, color: 'var(--ink-soft)' }}>{row.authorName ?? '—'}</span>
746
+ </td>
747
+
748
+ <td className="tnum" style={{ fontSize: 12, color: 'var(--ink-muted)' }}>
749
+ {row.updatedAt ? formatDate(row.updatedAt) : '—'}
750
+ </td>
751
+
752
+ {/* Actions column */}
753
+ <td onClick={(e) => e.stopPropagation()} onKeyUp={(e) => e.stopPropagation()}>
754
+ <div style={{ display: 'flex', gap: 2, justifyContent: 'flex-end' }}>
755
+ {/* Share — opens the ShareModal pre-filled from this row */}
756
+ <button
757
+ type="button"
758
+ className="icon-btn"
759
+ title="Share"
760
+ onClick={(e) => {
761
+ e.stopPropagation()
762
+ onShare()
763
+ }}
764
+ style={{ width: 28, height: 28 }}
765
+ >
766
+ <Icon name="share" size={14} />
767
+ </button>
768
+ <button
769
+ type="button"
770
+ className="icon-btn"
771
+ title="Open"
772
+ onClick={onOpen}
773
+ style={{ width: 28, height: 28 }}
774
+ >
775
+ <Icon name="chevRight" size={14} />
776
+ </button>
777
+ </div>
778
+ </td>
779
+ </tr>
780
+ )
781
+ }
782
+
783
+ // ---------------------------------------------------------------------------
784
+ // Status pill
785
+ // ---------------------------------------------------------------------------
786
+
787
+ /** Maps a content status (or the derived 'processing' pseudo-state) to its
788
+ * broadsheet `.pill` modifier class + human label. */
789
+ const STATUS_PILL: Record<ContentStatus | 'processing', { cls: string; label: string }> = {
790
+ published: { cls: 'published', label: 'Published' },
791
+ draft: { cls: 'draft', label: 'Draft' },
792
+ review: { cls: 'review', label: 'In review' },
793
+ scheduled: { cls: 'scheduled', label: 'Scheduled' },
794
+ processing: { cls: 'processing', label: 'Processing' },
795
+ // archived has no prototype pill state — fall back to the muted draft idiom.
796
+ archived: { cls: 'draft', label: 'Archived' },
797
+ }
798
+
799
+ function StatusPill({ row }: { row: LibraryRow }) {
800
+ // `!= null` (not `!== null`) so an absent field (undefined) is treated as
801
+ // not-processing — otherwise every non-video row mis-renders as PROCESSING.
802
+ const isProcessing =
803
+ row.processingState != null && !['ready', 'failed'].includes(row.processingState ?? '')
804
+ const { cls, label } = isProcessing ? STATUS_PILL.processing : STATUS_PILL[row.status]
805
+
806
+ return (
807
+ <span className={`pill ${cls}`}>
808
+ <span className="pdot" />
809
+ {label}
810
+ </span>
811
+ )
812
+ }
813
+
814
+ // ---------------------------------------------------------------------------
815
+ // Board view (kanban)
816
+ // ---------------------------------------------------------------------------
817
+
818
+ const BOARD_COLUMNS: Array<{
819
+ key: keyof ReturnType<typeof groupByStatus>
820
+ label: string
821
+ }> = [
822
+ { key: 'draft', label: 'Draft' },
823
+ { key: 'review', label: 'Review' },
824
+ { key: 'scheduled', label: 'Scheduled' },
825
+ { key: 'processing', label: 'Processing' },
826
+ { key: 'published', label: 'Published' },
827
+ ]
828
+
829
+ const BOARD_PILL: Record<(typeof BOARD_COLUMNS)[number]['key'], string> = {
830
+ draft: 'draft',
831
+ review: 'review',
832
+ scheduled: 'scheduled',
833
+ processing: 'processing',
834
+ published: 'published',
835
+ }
836
+
837
+ function BoardView({ rows, onOpen }: { rows: LibraryRow[]; onOpen: (row: LibraryRow) => void }) {
838
+ const columns = groupByStatus(rows)
839
+
840
+ return (
841
+ <div
842
+ style={{
843
+ display: 'grid',
844
+ gridTemplateColumns: 'repeat(5, minmax(232px, 1fr))',
845
+ gap: 14,
846
+ overflowX: 'auto',
847
+ paddingBottom: 8,
848
+ }}
849
+ >
850
+ {BOARD_COLUMNS.map((col) => {
851
+ const colRows = columns[col.key]
852
+ return (
853
+ <div key={col.key} style={{ minWidth: 232 }}>
854
+ <div
855
+ style={{
856
+ display: 'flex',
857
+ alignItems: 'center',
858
+ gap: 8,
859
+ marginBottom: 10,
860
+ padding: '0 2px',
861
+ }}
862
+ >
863
+ <span className={`pill ${BOARD_PILL[col.key]}`}>
864
+ <span className="pdot" />
865
+ {col.label}
866
+ </span>
867
+ <span className="tnum" style={{ fontSize: 12, color: 'var(--ink-faint)' }}>
868
+ {colRows.length}
869
+ </span>
870
+ </div>
871
+
872
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
873
+ {colRows.length === 0 && (
874
+ <div
875
+ style={{
876
+ padding: '16px 0',
877
+ textAlign: 'center',
878
+ fontSize: 11,
879
+ color: 'var(--ink-faint)',
880
+ }}
881
+ >
882
+ Empty
883
+ </div>
884
+ )}
885
+ {colRows.map((row) => (
886
+ <BoardCard key={row.id} row={row} onOpen={() => onOpen(row)} />
887
+ ))}
888
+ </div>
889
+ </div>
890
+ )
891
+ })}
892
+ </div>
893
+ )
894
+ }
895
+
896
+ function BoardCard({ row, onOpen }: { row: LibraryRow; onOpen: () => void }) {
897
+ const isProcessing =
898
+ row.processingState != null && !['ready', 'failed'].includes(row.processingState ?? '')
899
+
900
+ return (
901
+ <button
902
+ type="button"
903
+ className="panel"
904
+ aria-label={`Open ${row.title ?? 'library item'}`}
905
+ style={{
906
+ padding: 12,
907
+ cursor: 'pointer',
908
+ display: 'block',
909
+ width: '100%',
910
+ textAlign: 'left',
911
+ font: 'inherit',
912
+ color: 'inherit',
913
+ }}
914
+ onClick={onOpen}
915
+ onMouseEnter={(e) => {
916
+ e.currentTarget.style.borderColor = 'var(--accent-ring)'
917
+ }}
918
+ onMouseLeave={(e) => {
919
+ e.currentTarget.style.borderColor = 'var(--border)'
920
+ }}
921
+ >
922
+ {/* Hero band: type-tag overlay + (honest) indeterminate processing bar */}
923
+ <div
924
+ style={{
925
+ height: 78,
926
+ borderRadius: 7,
927
+ background: 'var(--panel-2)',
928
+ marginBottom: 10,
929
+ position: 'relative',
930
+ display: 'grid',
931
+ placeItems: 'center',
932
+ }}
933
+ >
934
+ <Icon
935
+ name={CONTENT_TYPE_ICONS[row.type] as Parameters<typeof Icon>[0]['name']}
936
+ size={22}
937
+ style={{ color: 'var(--ink-faint)' }}
938
+ />
939
+ <span style={{ position: 'absolute', top: 8, left: 8 }}>
940
+ <span
941
+ style={{
942
+ display: 'inline-flex',
943
+ alignItems: 'center',
944
+ gap: 5,
945
+ background: 'rgba(0,0,0,.4)',
946
+ color: '#fff',
947
+ padding: '3px 8px',
948
+ borderRadius: 20,
949
+ fontSize: 10.5,
950
+ fontFamily: 'var(--mono)',
951
+ }}
952
+ >
953
+ <Icon
954
+ name={CONTENT_TYPE_ICONS[row.type] as Parameters<typeof Icon>[0]['name']}
955
+ size={12}
956
+ />{' '}
957
+ {row.type.charAt(0).toUpperCase() + row.type.slice(1)}
958
+ </span>
959
+ </span>
960
+ {isProcessing && (
961
+ <span
962
+ className="skeleton-shimmer"
963
+ title={`Processing: ${row.processingState}`}
964
+ style={{
965
+ position: 'absolute',
966
+ bottom: 8,
967
+ left: 8,
968
+ right: 8,
969
+ height: 4,
970
+ borderRadius: 3,
971
+ overflow: 'hidden',
972
+ }}
973
+ />
974
+ )}
975
+ </div>
976
+ <div
977
+ style={{
978
+ fontSize: 13.5,
979
+ fontWeight: 600,
980
+ color: 'var(--ink)',
981
+ lineHeight: 1.35,
982
+ marginBottom: 8,
983
+ }}
984
+ >
985
+ {row.title || '(Untitled)'}
986
+ </div>
987
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
988
+ <span style={{ fontSize: 11.5, color: 'var(--ink-muted)' }}>
989
+ {row.updatedAt ? formatDate(row.updatedAt) : (row.authorName ?? '—')}
990
+ </span>
991
+ {row.featured && (
992
+ <span style={{ marginLeft: 'auto', color: 'var(--gold)' }}>
993
+ <Icon name="star" size={12} />
994
+ </span>
995
+ )}
996
+ </div>
997
+ </button>
998
+ )
999
+ }
1000
+
1001
+ // ---------------------------------------------------------------------------
1002
+ // Bulk action bar
1003
+ // ---------------------------------------------------------------------------
1004
+
1005
+ const BULK_OPS = [
1006
+ { op: 'trash', label: 'Trash', icon: 'trash' as const, danger: true },
1007
+ { op: 'review', label: 'Move to Review', icon: 'flag' as const, danger: false },
1008
+ ] as const
1009
+
1010
+ function BulkBar({
1011
+ selectedIds,
1012
+ workspaceId: _workspaceId,
1013
+ onDone,
1014
+ onCancel,
1015
+ }: {
1016
+ selectedIds: string[]
1017
+ workspaceId: string
1018
+ onDone: (removedIds?: string[]) => void
1019
+ onCancel: () => void
1020
+ }) {
1021
+ const [busy, setBusy] = useState(false)
1022
+
1023
+ async function runBulk(op: string) {
1024
+ setBusy(true)
1025
+ try {
1026
+ const res = await fetch(`/admin/api/content/bulk`, {
1027
+ method: 'POST',
1028
+ headers: { 'content-type': 'application/json' },
1029
+ body: JSON.stringify({ ids: selectedIds, op }),
1030
+ })
1031
+ if (!res.ok) {
1032
+ const err = (await res.json().catch(() => ({ error: res.statusText }))) as {
1033
+ error?: string
1034
+ }
1035
+ alert(`Bulk ${op} failed: ${err.error ?? res.statusText}`)
1036
+ setBusy(false)
1037
+ return
1038
+ }
1039
+ // For trash, the rows are gone; for others we just refresh
1040
+ onDone(op === 'trash' ? selectedIds : undefined)
1041
+ } catch (err) {
1042
+ alert(String(err))
1043
+ setBusy(false)
1044
+ }
1045
+ }
1046
+
1047
+ return (
1048
+ <div
1049
+ className="panel fade-only"
1050
+ style={{
1051
+ display: 'flex',
1052
+ alignItems: 'center',
1053
+ gap: 12,
1054
+ padding: '10px 14px',
1055
+ marginBottom: 12,
1056
+ background: 'var(--accent-tint)',
1057
+ borderColor: 'var(--accent-ring)',
1058
+ }}
1059
+ >
1060
+ <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--accent)' }}>
1061
+ {selectedIds.length} selected
1062
+ </span>
1063
+
1064
+ <div style={{ flex: 1 }} />
1065
+
1066
+ {BULK_OPS.map(({ op, label, icon, danger }) => (
1067
+ <button
1068
+ key={op}
1069
+ type="button"
1070
+ className={danger ? 'btn sm danger' : 'btn sm'}
1071
+ disabled={busy}
1072
+ onClick={() => void runBulk(op)}
1073
+ style={busy ? { opacity: 0.6 } : undefined}
1074
+ >
1075
+ <Icon name={icon} size={14} />
1076
+ <span>{label}</span>
1077
+ </button>
1078
+ ))}
1079
+
1080
+ <button type="button" className="icon-btn" onClick={onCancel} title="Cancel selection">
1081
+ <Icon name="x" size={16} />
1082
+ </button>
1083
+ </div>
1084
+ )
1085
+ }
1086
+
1087
+ // ---------------------------------------------------------------------------
1088
+ // Import modal
1089
+ // ---------------------------------------------------------------------------
1090
+
1091
+ type ImportSource = 'markdown' | 'ghost' | 'substack'
1092
+
1093
+ const IMPORT_SOURCES: Array<{ value: ImportSource; label: string }> = [
1094
+ { value: 'markdown', label: 'Markdown file' },
1095
+ { value: 'ghost', label: 'Ghost JSON export' },
1096
+ { value: 'substack', label: 'Substack CSV export' },
1097
+ ]
1098
+
1099
+ interface ImportPreviewItem {
1100
+ title: string
1101
+ slug: string
1102
+ excerpt?: string
1103
+ }
1104
+
1105
+ interface ParseResult {
1106
+ importId: string
1107
+ items: ImportPreviewItem[]
1108
+ }
1109
+
1110
+ function ImportModal({
1111
+ workspaceId: _workspaceId,
1112
+ onClose,
1113
+ onImported,
1114
+ }: {
1115
+ workspaceId: string
1116
+ onClose: () => void
1117
+ onImported: () => void
1118
+ }) {
1119
+ const [step, setStep] = useState<'pick' | 'preview' | 'confirming'>('pick')
1120
+ const [source, setSource] = useState<ImportSource>('markdown')
1121
+ const [payload, setPayload] = useState('')
1122
+ const [parseResult, setParseResult] = useState<ParseResult | null>(null)
1123
+ const [error, setError] = useState<string | null>(null)
1124
+
1125
+ async function handleParse() {
1126
+ setError(null)
1127
+ try {
1128
+ const res = await fetch('/admin/api/content/import/parse', {
1129
+ method: 'POST',
1130
+ headers: { 'content-type': 'application/json' },
1131
+ body: JSON.stringify({ source, payload }),
1132
+ })
1133
+ if (!res.ok) {
1134
+ const err = (await res.json().catch(() => ({ error: res.statusText }))) as {
1135
+ error?: string
1136
+ }
1137
+ setError(err.error ?? 'Parse failed')
1138
+ return
1139
+ }
1140
+ const data = (await res.json()) as ParseResult
1141
+ setParseResult(data)
1142
+ setStep('preview')
1143
+ } catch (err) {
1144
+ setError(String(err))
1145
+ }
1146
+ }
1147
+
1148
+ async function handleConfirm() {
1149
+ if (!parseResult) return
1150
+ setStep('confirming')
1151
+ setError(null)
1152
+ try {
1153
+ const res = await fetch('/admin/api/content/import/confirm', {
1154
+ method: 'POST',
1155
+ headers: { 'content-type': 'application/json' },
1156
+ body: JSON.stringify({ importId: parseResult.importId }),
1157
+ })
1158
+ if (!res.ok) {
1159
+ const err = (await res.json().catch(() => ({ error: res.statusText }))) as {
1160
+ error?: string
1161
+ }
1162
+ setError(err.error ?? 'Confirm failed')
1163
+ setStep('preview')
1164
+ return
1165
+ }
1166
+ onImported()
1167
+ } catch (err) {
1168
+ setError(String(err))
1169
+ setStep('preview')
1170
+ }
1171
+ }
1172
+
1173
+ return (
1174
+ <dialog
1175
+ open
1176
+ aria-label="Import content"
1177
+ className="scrim"
1178
+ style={{ display: 'grid', placeItems: 'center', border: 'none', padding: 16 }}
1179
+ onClose={onClose}
1180
+ onClick={(e) => {
1181
+ if (e.target === e.currentTarget) onClose()
1182
+ }}
1183
+ onKeyUp={(e) => {
1184
+ if (e.key === 'Escape') onClose()
1185
+ }}
1186
+ >
1187
+ <div
1188
+ className="panel panel-pad fade-in"
1189
+ style={{ width: '100%', maxWidth: 540, boxShadow: 'var(--shadow-lg)' }}
1190
+ >
1191
+ <div
1192
+ style={{
1193
+ display: 'flex',
1194
+ alignItems: 'flex-start',
1195
+ justifyContent: 'space-between',
1196
+ gap: 12,
1197
+ }}
1198
+ >
1199
+ <div className="kicker">Import</div>
1200
+ <button type="button" className="icon-btn" onClick={onClose} title="Close">
1201
+ <Icon name="x" size={18} />
1202
+ </button>
1203
+ </div>
1204
+ <h2 className="card-title">Import content</h2>
1205
+ <div className="masthead-rule" style={{ marginBottom: 16 }} />
1206
+
1207
+ {step === 'pick' && (
1208
+ <>
1209
+ <div style={{ marginBottom: 14 }}>
1210
+ <label htmlFor="import-source" className="field-label">
1211
+ Source format
1212
+ </label>
1213
+ <select
1214
+ id="import-source"
1215
+ className="selectbox"
1216
+ value={source}
1217
+ onChange={(e) => setSource(e.currentTarget.value as ImportSource)}
1218
+ >
1219
+ {IMPORT_SOURCES.map((s) => (
1220
+ <option key={s.value} value={s.value}>
1221
+ {s.label}
1222
+ </option>
1223
+ ))}
1224
+ </select>
1225
+ </div>
1226
+
1227
+ <div style={{ marginBottom: 16 }}>
1228
+ <label htmlFor="import-payload" className="field-label">
1229
+ Paste content
1230
+ </label>
1231
+ <textarea
1232
+ id="import-payload"
1233
+ className="textarea mono"
1234
+ value={payload}
1235
+ onChange={(e) => setPayload(e.currentTarget.value)}
1236
+ rows={10}
1237
+ placeholder={`Paste your ${source} content here…`}
1238
+ style={{ fontSize: 12 }}
1239
+ />
1240
+ </div>
1241
+
1242
+ {error && (
1243
+ <div style={{ marginBottom: 12, color: 'var(--red)', fontSize: 12 }}>{error}</div>
1244
+ )}
1245
+
1246
+ <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
1247
+ <button type="button" className="btn" onClick={onClose}>
1248
+ Cancel
1249
+ </button>
1250
+ <button
1251
+ type="button"
1252
+ className="btn primary"
1253
+ onClick={() => void handleParse()}
1254
+ disabled={!payload.trim()}
1255
+ style={payload.trim() ? undefined : { opacity: 0.5 }}
1256
+ >
1257
+ Parse
1258
+ </button>
1259
+ </div>
1260
+ </>
1261
+ )}
1262
+
1263
+ {step === 'preview' && parseResult && (
1264
+ <>
1265
+ <div style={{ marginBottom: 12, fontSize: 13, color: 'var(--ink-muted)' }}>
1266
+ Found {parseResult.items.length} item
1267
+ {parseResult.items.length !== 1 ? 's' : ''} to import.
1268
+ </div>
1269
+
1270
+ <div className="panel" style={{ maxHeight: 240, overflow: 'auto', marginBottom: 16 }}>
1271
+ {parseResult.items.map((item) => (
1272
+ <div
1273
+ key={item.slug}
1274
+ style={{
1275
+ padding: '8px 12px',
1276
+ borderBottom: '1px solid var(--border-soft)',
1277
+ }}
1278
+ >
1279
+ <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink)' }}>
1280
+ {item.title}
1281
+ </div>
1282
+ <div className="mono" style={{ fontSize: 11, color: 'var(--ink-faint)' }}>
1283
+ /{item.slug}
1284
+ </div>
1285
+ </div>
1286
+ ))}
1287
+ </div>
1288
+
1289
+ {error && (
1290
+ <div style={{ marginBottom: 12, color: 'var(--red)', fontSize: 12 }}>{error}</div>
1291
+ )}
1292
+
1293
+ <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
1294
+ <button type="button" className="btn" onClick={() => setStep('pick')}>
1295
+ Back
1296
+ </button>
1297
+ <button type="button" className="btn primary" onClick={() => void handleConfirm()}>
1298
+ Import {parseResult.items.length} item{parseResult.items.length !== 1 ? 's' : ''}
1299
+ </button>
1300
+ </div>
1301
+ </>
1302
+ )}
1303
+
1304
+ {step === 'confirming' && (
1305
+ <div style={{ padding: '24px 0', textAlign: 'center', color: 'var(--ink-muted)' }}>
1306
+ Importing…
1307
+ </div>
1308
+ )}
1309
+ </div>
1310
+ </dialog>
1311
+ )
1312
+ }
1313
+
1314
+ // ---------------------------------------------------------------------------
1315
+ // Helpers
1316
+ // ---------------------------------------------------------------------------
1317
+
1318
+ function formatDate(iso: string): string {
1319
+ try {
1320
+ const d = new Date(iso)
1321
+ const now = new Date()
1322
+ const diff = now.getTime() - d.getTime()
1323
+ if (diff < 60_000) return 'just now'
1324
+ if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}m ago`
1325
+ if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)}h ago`
1326
+ return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' })
1327
+ } catch {
1328
+ return iso
1329
+ }
1330
+ }
1331
+
1332
+ // ---------------------------------------------------------------------------
1333
+ // Micro-style tokens (inline — the broadsheet status-tab underline pattern,
1334
+ // which has no dedicated class in broadsheet.css).
1335
+ // ---------------------------------------------------------------------------
1336
+
1337
+ const tabStyle: React.CSSProperties = {
1338
+ border: 0,
1339
+ background: 'transparent',
1340
+ padding: '10px 12px 12px',
1341
+ fontSize: 13.5,
1342
+ fontWeight: 600,
1343
+ marginBottom: -1,
1344
+ cursor: 'pointer',
1345
+ display: 'inline-flex',
1346
+ alignItems: 'center',
1347
+ gap: 7,
1348
+ whiteSpace: 'nowrap',
1349
+ transition: 'color 120ms',
1350
+ }