@aphexcms/cms-core 9.6.0 → 9.8.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 (491) hide show
  1. package/dist/agent-chat-state.svelte.d.ts +38 -0
  2. package/dist/agent-chat-state.svelte.d.ts.map +1 -0
  3. package/dist/agent-chat-state.svelte.js +86 -0
  4. package/dist/ai/content-workspace-tools.d.ts +3 -0
  5. package/dist/ai/content-workspace-tools.d.ts.map +1 -0
  6. package/dist/ai/content-workspace-tools.js +50 -0
  7. package/dist/ai/default-system-prompt.d.ts +2 -0
  8. package/dist/ai/default-system-prompt.d.ts.map +1 -0
  9. package/dist/ai/default-system-prompt.js +15 -0
  10. package/dist/ai/index.d.ts +2 -0
  11. package/dist/ai/index.d.ts.map +1 -0
  12. package/dist/ai/index.js +4 -0
  13. package/dist/ai/interfaces/ai-provider.d.ts +67 -0
  14. package/dist/ai/interfaces/ai-provider.d.ts.map +1 -0
  15. package/dist/ai/interfaces/ai-provider.js +16 -0
  16. package/dist/ai/run-agent-turn.d.ts +29 -0
  17. package/dist/ai/run-agent-turn.d.ts.map +1 -0
  18. package/dist/ai/run-agent-turn.js +166 -0
  19. package/dist/api/agent-chat.d.ts +27 -0
  20. package/dist/api/agent-chat.d.ts.map +1 -0
  21. package/dist/api/agent-chat.js +64 -0
  22. package/dist/api/documents.d.ts +36 -3
  23. package/dist/api/documents.d.ts.map +1 -1
  24. package/dist/api/documents.js +24 -6
  25. package/dist/api/schemas/agent-chat.d.ts +45 -0
  26. package/dist/api/schemas/agent-chat.d.ts.map +1 -0
  27. package/dist/api/schemas/agent-chat.js +35 -0
  28. package/dist/api/schemas/agent-operations.d.ts +13 -0
  29. package/dist/api/schemas/agent-operations.d.ts.map +1 -0
  30. package/dist/api/schemas/agent-operations.js +15 -0
  31. package/dist/api/schemas/documents.d.ts +41 -0
  32. package/dist/api/schemas/documents.d.ts.map +1 -1
  33. package/dist/api/schemas/documents.js +38 -2
  34. package/dist/chunk-recovery.d.ts +33 -0
  35. package/dist/chunk-recovery.d.ts.map +1 -0
  36. package/dist/chunk-recovery.js +131 -0
  37. package/dist/client/index.d.ts +1 -0
  38. package/dist/client/index.d.ts.map +1 -1
  39. package/dist/client/index.js +1 -0
  40. package/dist/client/ui.d.ts +2 -0
  41. package/dist/client/ui.d.ts.map +1 -1
  42. package/dist/client/ui.js +3 -0
  43. package/dist/components/AdminApp.svelte +37 -3
  44. package/dist/components/AdminApp.svelte.d.ts.map +1 -1
  45. package/dist/components/admin/ActivityView.svelte +436 -0
  46. package/dist/components/admin/ActivityView.svelte.d.ts +4 -0
  47. package/dist/components/admin/ActivityView.svelte.d.ts.map +1 -0
  48. package/dist/components/admin/AgentChat.svelte +977 -0
  49. package/dist/components/admin/AgentChat.svelte.d.ts +17 -0
  50. package/dist/components/admin/AgentChat.svelte.d.ts.map +1 -0
  51. package/dist/components/admin/DocumentEditor.svelte +469 -70
  52. package/dist/components/admin/DocumentEditor.svelte.d.ts +6 -0
  53. package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -1
  54. package/dist/components/admin/ObjectModal.svelte +4 -2
  55. package/dist/components/admin/ScheduleDialog.svelte +174 -0
  56. package/dist/components/admin/ScheduleDialog.svelte.d.ts +20 -0
  57. package/dist/components/admin/ScheduleDialog.svelte.d.ts.map +1 -0
  58. package/dist/components/admin/fields/ArrayField.svelte +4 -1
  59. package/dist/components/admin/fields/ReferenceField.svelte +12 -1
  60. package/dist/components/admin/fields/ReferenceField.svelte.d.ts.map +1 -1
  61. package/dist/components/index.d.ts +1 -0
  62. package/dist/components/index.d.ts.map +1 -1
  63. package/dist/components/index.js +1 -0
  64. package/dist/components/layout/OrganizationSwitcher.svelte +3 -3
  65. package/dist/components/layout/Sidebar.svelte +26 -2
  66. package/dist/components/layout/Sidebar.svelte.d.ts +1 -0
  67. package/dist/components/layout/Sidebar.svelte.d.ts.map +1 -1
  68. package/dist/components/layout/sidebar/NavUser.svelte +5 -5
  69. package/dist/db/interfaces/agent-change-sets.d.ts +17 -0
  70. package/dist/db/interfaces/agent-change-sets.d.ts.map +1 -0
  71. package/dist/db/interfaces/agent-change-sets.js +1 -0
  72. package/dist/db/interfaces/document.d.ts +25 -3
  73. package/dist/db/interfaces/document.d.ts.map +1 -1
  74. package/dist/db/interfaces/document.js +19 -1
  75. package/dist/db/interfaces/events.d.ts +55 -0
  76. package/dist/db/interfaces/events.d.ts.map +1 -0
  77. package/dist/db/interfaces/events.js +1 -0
  78. package/dist/db/interfaces/index.d.ts +13 -2
  79. package/dist/db/interfaces/index.d.ts.map +1 -1
  80. package/dist/db/interfaces/index.js +1 -1
  81. package/dist/db/interfaces/plugin-storage.d.ts +39 -0
  82. package/dist/db/interfaces/plugin-storage.d.ts.map +1 -0
  83. package/dist/db/interfaces/plugin-storage.js +1 -0
  84. package/dist/document-refresh.svelte.d.ts +2 -0
  85. package/dist/document-refresh.svelte.d.ts.map +1 -1
  86. package/dist/document-refresh.svelte.js +15 -0
  87. package/dist/document-workspace-registry.svelte.d.ts +15 -0
  88. package/dist/document-workspace-registry.svelte.d.ts.map +1 -0
  89. package/dist/document-workspace-registry.svelte.js +30 -0
  90. package/dist/events/catalog.d.ts +8 -0
  91. package/dist/events/catalog.d.ts.map +1 -0
  92. package/dist/events/catalog.js +11 -0
  93. package/dist/events/consumer.d.ts +79 -0
  94. package/dist/events/consumer.d.ts.map +1 -0
  95. package/dist/events/consumer.js +76 -0
  96. package/dist/events/define-event.d.ts +17 -0
  97. package/dist/events/define-event.d.ts.map +1 -0
  98. package/dist/events/define-event.js +7 -0
  99. package/dist/events/emit.d.ts +11 -0
  100. package/dist/events/emit.d.ts.map +1 -0
  101. package/dist/events/emit.js +20 -0
  102. package/dist/events/index.d.ts +4 -0
  103. package/dist/events/index.d.ts.map +1 -0
  104. package/dist/events/index.js +3 -0
  105. package/dist/field-validation/rule.d.ts.map +1 -1
  106. package/dist/field-validation/rule.js +15 -0
  107. package/dist/field-validation/utils.d.ts.map +1 -1
  108. package/dist/field-validation/utils.js +211 -29
  109. package/dist/forms/define-form.d.ts +25 -0
  110. package/dist/forms/define-form.d.ts.map +1 -0
  111. package/dist/forms/define-form.js +22 -0
  112. package/dist/forms/index.d.ts +4 -0
  113. package/dist/forms/index.d.ts.map +1 -0
  114. package/dist/forms/index.js +5 -0
  115. package/dist/forms/types.d.ts +49 -0
  116. package/dist/forms/types.d.ts.map +1 -0
  117. package/dist/forms/types.js +1 -0
  118. package/dist/forms/validate.d.ts +9 -0
  119. package/dist/forms/validate.d.ts.map +1 -0
  120. package/dist/forms/validate.js +18 -0
  121. package/dist/hooks.d.ts +2 -0
  122. package/dist/hooks.d.ts.map +1 -1
  123. package/dist/hooks.js +17 -0
  124. package/dist/index.d.ts +2 -0
  125. package/dist/index.d.ts.map +1 -1
  126. package/dist/index.js +6 -0
  127. package/dist/jobs/document-jobs.d.ts +27 -0
  128. package/dist/jobs/document-jobs.d.ts.map +1 -0
  129. package/dist/jobs/document-jobs.js +42 -0
  130. package/dist/jobs/embedded-runner.d.ts +25 -0
  131. package/dist/jobs/embedded-runner.d.ts.map +1 -0
  132. package/dist/jobs/embedded-runner.js +46 -0
  133. package/dist/jobs/index.d.ts +7 -0
  134. package/dist/jobs/index.d.ts.map +1 -0
  135. package/dist/jobs/index.js +8 -0
  136. package/dist/jobs/relay.d.ts +27 -0
  137. package/dist/jobs/relay.d.ts.map +1 -0
  138. package/dist/jobs/relay.js +60 -0
  139. package/dist/jobs/run-batch.d.ts +52 -0
  140. package/dist/jobs/run-batch.d.ts.map +1 -0
  141. package/dist/jobs/run-batch.js +61 -0
  142. package/dist/jobs/run-due-jobs.d.ts +30 -0
  143. package/dist/jobs/run-due-jobs.d.ts.map +1 -0
  144. package/dist/jobs/run-due-jobs.js +69 -0
  145. package/dist/jobs/types.d.ts +18 -0
  146. package/dist/jobs/types.d.ts.map +1 -0
  147. package/dist/jobs/types.js +1 -0
  148. package/dist/lib/agent-chat-state.svelte.d.ts +38 -0
  149. package/dist/lib/agent-chat-state.svelte.d.ts.map +1 -0
  150. package/dist/lib/agent-chat-state.svelte.js +87 -0
  151. package/dist/lib/agent-chat-state.svelte.js.map +1 -0
  152. package/dist/lib/ai/content-workspace-tools.d.ts +3 -0
  153. package/dist/lib/ai/content-workspace-tools.d.ts.map +1 -0
  154. package/dist/lib/ai/content-workspace-tools.js +51 -0
  155. package/dist/lib/ai/content-workspace-tools.js.map +1 -0
  156. package/dist/lib/ai/default-system-prompt.d.ts +2 -0
  157. package/dist/lib/ai/default-system-prompt.d.ts.map +1 -0
  158. package/dist/lib/ai/default-system-prompt.js +16 -0
  159. package/dist/lib/ai/default-system-prompt.js.map +1 -0
  160. package/dist/lib/ai/index.d.ts +2 -0
  161. package/dist/lib/ai/index.d.ts.map +1 -0
  162. package/dist/lib/ai/index.js +5 -0
  163. package/dist/lib/ai/index.js.map +1 -0
  164. package/dist/lib/ai/interfaces/ai-provider.d.ts +67 -0
  165. package/dist/lib/ai/interfaces/ai-provider.d.ts.map +1 -0
  166. package/dist/lib/ai/interfaces/ai-provider.js +17 -0
  167. package/dist/lib/ai/interfaces/ai-provider.js.map +1 -0
  168. package/dist/lib/ai/run-agent-turn.d.ts +29 -0
  169. package/dist/lib/ai/run-agent-turn.d.ts.map +1 -0
  170. package/dist/lib/ai/run-agent-turn.js +167 -0
  171. package/dist/lib/ai/run-agent-turn.js.map +1 -0
  172. package/dist/lib/api/agent-chat.d.ts +27 -0
  173. package/dist/lib/api/agent-chat.d.ts.map +1 -0
  174. package/dist/lib/api/agent-chat.js +65 -0
  175. package/dist/lib/api/agent-chat.js.map +1 -0
  176. package/dist/lib/api/documents.d.ts +36 -3
  177. package/dist/lib/api/documents.d.ts.map +1 -1
  178. package/dist/lib/api/documents.js +24 -6
  179. package/dist/lib/api/documents.js.map +1 -1
  180. package/dist/lib/api/schemas/agent-chat.d.ts +45 -0
  181. package/dist/lib/api/schemas/agent-chat.d.ts.map +1 -0
  182. package/dist/lib/api/schemas/agent-chat.js +36 -0
  183. package/dist/lib/api/schemas/agent-chat.js.map +1 -0
  184. package/dist/lib/api/schemas/agent-operations.d.ts +13 -0
  185. package/dist/lib/api/schemas/agent-operations.d.ts.map +1 -0
  186. package/dist/lib/api/schemas/agent-operations.js +16 -0
  187. package/dist/lib/api/schemas/agent-operations.js.map +1 -0
  188. package/dist/lib/api/schemas/documents.d.ts +41 -0
  189. package/dist/lib/api/schemas/documents.d.ts.map +1 -1
  190. package/dist/lib/api/schemas/documents.js +38 -2
  191. package/dist/lib/api/schemas/documents.js.map +1 -1
  192. package/dist/lib/chunk-recovery.d.ts +33 -0
  193. package/dist/lib/chunk-recovery.d.ts.map +1 -0
  194. package/dist/lib/chunk-recovery.js +132 -0
  195. package/dist/lib/chunk-recovery.js.map +1 -0
  196. package/dist/lib/client/index.d.ts +1 -0
  197. package/dist/lib/client/index.d.ts.map +1 -1
  198. package/dist/lib/client/index.js +1 -0
  199. package/dist/lib/client/index.js.map +1 -1
  200. package/dist/lib/client/ui.d.ts +2 -0
  201. package/dist/lib/client/ui.d.ts.map +1 -1
  202. package/dist/lib/client/ui.js +3 -0
  203. package/dist/lib/client/ui.js.map +1 -1
  204. package/dist/lib/components/index.d.ts +1 -0
  205. package/dist/lib/components/index.d.ts.map +1 -1
  206. package/dist/lib/components/index.js +1 -0
  207. package/dist/lib/components/index.js.map +1 -1
  208. package/dist/lib/db/interfaces/agent-change-sets.d.ts +17 -0
  209. package/dist/lib/db/interfaces/agent-change-sets.d.ts.map +1 -0
  210. package/dist/lib/db/interfaces/agent-change-sets.js +2 -0
  211. package/dist/lib/db/interfaces/agent-change-sets.js.map +1 -0
  212. package/dist/lib/db/interfaces/document.d.ts +25 -3
  213. package/dist/lib/db/interfaces/document.d.ts.map +1 -1
  214. package/dist/lib/db/interfaces/document.js +19 -1
  215. package/dist/lib/db/interfaces/document.js.map +1 -1
  216. package/dist/lib/db/interfaces/events.d.ts +55 -0
  217. package/dist/lib/db/interfaces/events.d.ts.map +1 -0
  218. package/dist/lib/db/interfaces/events.js +2 -0
  219. package/dist/lib/db/interfaces/events.js.map +1 -0
  220. package/dist/lib/db/interfaces/index.d.ts +13 -2
  221. package/dist/lib/db/interfaces/index.d.ts.map +1 -1
  222. package/dist/lib/db/interfaces/index.js +1 -1
  223. package/dist/lib/db/interfaces/index.js.map +1 -1
  224. package/dist/lib/db/interfaces/plugin-storage.d.ts +39 -0
  225. package/dist/lib/db/interfaces/plugin-storage.d.ts.map +1 -0
  226. package/dist/lib/db/interfaces/plugin-storage.js +2 -0
  227. package/dist/lib/db/interfaces/plugin-storage.js.map +1 -0
  228. package/dist/lib/document-refresh.svelte.d.ts +2 -0
  229. package/dist/lib/document-refresh.svelte.d.ts.map +1 -1
  230. package/dist/lib/document-refresh.svelte.js +15 -0
  231. package/dist/lib/document-refresh.svelte.js.map +1 -1
  232. package/dist/lib/document-workspace-registry.svelte.d.ts +15 -0
  233. package/dist/lib/document-workspace-registry.svelte.d.ts.map +1 -0
  234. package/dist/lib/document-workspace-registry.svelte.js +31 -0
  235. package/dist/lib/document-workspace-registry.svelte.js.map +1 -0
  236. package/dist/lib/events/catalog.d.ts +8 -0
  237. package/dist/lib/events/catalog.d.ts.map +1 -0
  238. package/dist/lib/events/catalog.js +12 -0
  239. package/dist/lib/events/catalog.js.map +1 -0
  240. package/dist/lib/events/consumer.d.ts +79 -0
  241. package/dist/lib/events/consumer.d.ts.map +1 -0
  242. package/dist/lib/events/consumer.js +77 -0
  243. package/dist/lib/events/consumer.js.map +1 -0
  244. package/dist/lib/events/define-event.d.ts +17 -0
  245. package/dist/lib/events/define-event.d.ts.map +1 -0
  246. package/dist/lib/events/define-event.js +8 -0
  247. package/dist/lib/events/define-event.js.map +1 -0
  248. package/dist/lib/events/emit.d.ts +11 -0
  249. package/dist/lib/events/emit.d.ts.map +1 -0
  250. package/dist/lib/events/emit.js +21 -0
  251. package/dist/lib/events/emit.js.map +1 -0
  252. package/dist/lib/events/index.d.ts +4 -0
  253. package/dist/lib/events/index.d.ts.map +1 -0
  254. package/dist/lib/events/index.js +4 -0
  255. package/dist/lib/events/index.js.map +1 -0
  256. package/dist/lib/field-validation/rule.d.ts.map +1 -1
  257. package/dist/lib/field-validation/rule.js +15 -0
  258. package/dist/lib/field-validation/rule.js.map +1 -1
  259. package/dist/lib/field-validation/utils.d.ts.map +1 -1
  260. package/dist/lib/field-validation/utils.js +211 -29
  261. package/dist/lib/field-validation/utils.js.map +1 -1
  262. package/dist/lib/forms/define-form.d.ts +25 -0
  263. package/dist/lib/forms/define-form.d.ts.map +1 -0
  264. package/dist/lib/forms/define-form.js +23 -0
  265. package/dist/lib/forms/define-form.js.map +1 -0
  266. package/dist/lib/forms/index.d.ts +4 -0
  267. package/dist/lib/forms/index.d.ts.map +1 -0
  268. package/dist/lib/forms/index.js +6 -0
  269. package/dist/lib/forms/index.js.map +1 -0
  270. package/dist/lib/forms/types.d.ts +49 -0
  271. package/dist/lib/forms/types.d.ts.map +1 -0
  272. package/dist/lib/forms/types.js +2 -0
  273. package/dist/lib/forms/types.js.map +1 -0
  274. package/dist/lib/forms/validate.d.ts +9 -0
  275. package/dist/lib/forms/validate.d.ts.map +1 -0
  276. package/dist/lib/forms/validate.js +19 -0
  277. package/dist/lib/forms/validate.js.map +1 -0
  278. package/dist/lib/hooks.d.ts +2 -0
  279. package/dist/lib/hooks.d.ts.map +1 -1
  280. package/dist/lib/hooks.js +17 -0
  281. package/dist/lib/hooks.js.map +1 -1
  282. package/dist/lib/index.d.ts +2 -0
  283. package/dist/lib/index.d.ts.map +1 -1
  284. package/dist/lib/index.js +6 -0
  285. package/dist/lib/index.js.map +1 -1
  286. package/dist/lib/jobs/document-jobs.d.ts +27 -0
  287. package/dist/lib/jobs/document-jobs.d.ts.map +1 -0
  288. package/dist/lib/jobs/document-jobs.js +43 -0
  289. package/dist/lib/jobs/document-jobs.js.map +1 -0
  290. package/dist/lib/jobs/embedded-runner.d.ts +25 -0
  291. package/dist/lib/jobs/embedded-runner.d.ts.map +1 -0
  292. package/dist/lib/jobs/embedded-runner.js +47 -0
  293. package/dist/lib/jobs/embedded-runner.js.map +1 -0
  294. package/dist/lib/jobs/index.d.ts +7 -0
  295. package/dist/lib/jobs/index.d.ts.map +1 -0
  296. package/dist/lib/jobs/index.js +9 -0
  297. package/dist/lib/jobs/index.js.map +1 -0
  298. package/dist/lib/jobs/relay.d.ts +27 -0
  299. package/dist/lib/jobs/relay.d.ts.map +1 -0
  300. package/dist/lib/jobs/relay.js +61 -0
  301. package/dist/lib/jobs/relay.js.map +1 -0
  302. package/dist/lib/jobs/run-batch.d.ts +52 -0
  303. package/dist/lib/jobs/run-batch.d.ts.map +1 -0
  304. package/dist/lib/jobs/run-batch.js +62 -0
  305. package/dist/lib/jobs/run-batch.js.map +1 -0
  306. package/dist/lib/jobs/run-due-jobs.d.ts +30 -0
  307. package/dist/lib/jobs/run-due-jobs.d.ts.map +1 -0
  308. package/dist/lib/jobs/run-due-jobs.js +70 -0
  309. package/dist/lib/jobs/run-due-jobs.js.map +1 -0
  310. package/dist/lib/jobs/types.d.ts +18 -0
  311. package/dist/lib/jobs/types.d.ts.map +1 -0
  312. package/dist/lib/jobs/types.js +2 -0
  313. package/dist/lib/jobs/types.js.map +1 -0
  314. package/dist/lib/local-api/collection-api.d.ts +49 -2
  315. package/dist/lib/local-api/collection-api.d.ts.map +1 -1
  316. package/dist/lib/local-api/collection-api.js +193 -27
  317. package/dist/lib/local-api/collection-api.js.map +1 -1
  318. package/dist/lib/mcp/tools.d.ts +39 -4
  319. package/dist/lib/mcp/tools.d.ts.map +1 -1
  320. package/dist/lib/mcp/tools.js +546 -362
  321. package/dist/lib/mcp/tools.js.map +1 -1
  322. package/dist/lib/plugins/resolver.d.ts +19 -1
  323. package/dist/lib/plugins/resolver.d.ts.map +1 -1
  324. package/dist/lib/plugins/resolver.js +17 -1
  325. package/dist/lib/plugins/resolver.js.map +1 -1
  326. package/dist/lib/plugins/types.d.ts +68 -2
  327. package/dist/lib/plugins/types.d.ts.map +1 -1
  328. package/dist/lib/plugins/types.js.map +1 -1
  329. package/dist/lib/preview/stega.d.ts +2 -2
  330. package/dist/lib/preview/stega.d.ts.map +1 -1
  331. package/dist/lib/preview/stega.js +52 -29
  332. package/dist/lib/preview/stega.js.map +1 -1
  333. package/dist/lib/schema-utils/validator.d.ts.map +1 -1
  334. package/dist/lib/schema-utils/validator.js +5 -2
  335. package/dist/lib/schema-utils/validator.js.map +1 -1
  336. package/dist/lib/server/api/index.d.ts.map +1 -1
  337. package/dist/lib/server/api/index.js +16 -0
  338. package/dist/lib/server/api/index.js.map +1 -1
  339. package/dist/lib/server/api/routes/agent-change-sets.d.ts +4 -0
  340. package/dist/lib/server/api/routes/agent-change-sets.d.ts.map +1 -0
  341. package/dist/lib/server/api/routes/agent-change-sets.js +132 -0
  342. package/dist/lib/server/api/routes/agent-change-sets.js.map +1 -0
  343. package/dist/lib/server/api/routes/agent-chat.d.ts +14 -0
  344. package/dist/lib/server/api/routes/agent-chat.d.ts.map +1 -0
  345. package/dist/lib/server/api/routes/agent-chat.js +224 -0
  346. package/dist/lib/server/api/routes/agent-chat.js.map +1 -0
  347. package/dist/lib/server/api/routes/document-versions.d.ts.map +1 -1
  348. package/dist/lib/server/api/routes/document-versions.js +16 -2
  349. package/dist/lib/server/api/routes/document-versions.js.map +1 -1
  350. package/dist/lib/server/api/routes/documents-by-id.d.ts.map +1 -1
  351. package/dist/lib/server/api/routes/documents-by-id.js +11 -1
  352. package/dist/lib/server/api/routes/documents-by-id.js.map +1 -1
  353. package/dist/lib/server/api/routes/documents-publish.d.ts.map +1 -1
  354. package/dist/lib/server/api/routes/documents-publish.js +145 -2
  355. package/dist/lib/server/api/routes/documents-publish.js.map +1 -1
  356. package/dist/lib/server/api/routes/jobs.d.ts +4 -0
  357. package/dist/lib/server/api/routes/jobs.d.ts.map +1 -0
  358. package/dist/lib/server/api/routes/jobs.js +95 -0
  359. package/dist/lib/server/api/routes/jobs.js.map +1 -0
  360. package/dist/lib/server/api/routes/resolve-created-by.d.ts +13 -0
  361. package/dist/lib/server/api/routes/resolve-created-by.d.ts.map +1 -0
  362. package/dist/lib/server/api/routes/resolve-created-by.js +31 -0
  363. package/dist/lib/server/api/routes/resolve-created-by.js.map +1 -0
  364. package/dist/lib/server/api/routes/workers-run.d.ts +4 -0
  365. package/dist/lib/server/api/routes/workers-run.d.ts.map +1 -0
  366. package/dist/lib/server/api/routes/workers-run.js +41 -0
  367. package/dist/lib/server/api/routes/workers-run.js.map +1 -0
  368. package/dist/lib/server/index.d.ts +2 -0
  369. package/dist/lib/server/index.d.ts.map +1 -1
  370. package/dist/lib/server/index.js +3 -0
  371. package/dist/lib/server/index.js.map +1 -1
  372. package/dist/lib/services/version-service.d.ts +19 -3
  373. package/dist/lib/services/version-service.d.ts.map +1 -1
  374. package/dist/lib/services/version-service.js +75 -69
  375. package/dist/lib/services/version-service.js.map +1 -1
  376. package/dist/lib/type-gen.d.ts.map +1 -1
  377. package/dist/lib/type-gen.js +11 -0
  378. package/dist/lib/type-gen.js.map +1 -1
  379. package/dist/lib/types/agent-change-sets.d.ts +70 -0
  380. package/dist/lib/types/agent-change-sets.d.ts.map +1 -0
  381. package/dist/lib/types/agent-change-sets.js +7 -0
  382. package/dist/lib/types/agent-change-sets.js.map +1 -0
  383. package/dist/lib/types/agent-stream.d.ts +60 -0
  384. package/dist/lib/types/agent-stream.d.ts.map +1 -0
  385. package/dist/lib/types/agent-stream.js +12 -0
  386. package/dist/lib/types/agent-stream.js.map +1 -0
  387. package/dist/lib/types/agent-tools.d.ts +65 -0
  388. package/dist/lib/types/agent-tools.d.ts.map +1 -0
  389. package/dist/lib/types/agent-tools.js +2 -0
  390. package/dist/lib/types/agent-tools.js.map +1 -0
  391. package/dist/lib/types/config.d.ts +62 -0
  392. package/dist/lib/types/config.d.ts.map +1 -1
  393. package/dist/lib/types/document-workspace.d.ts +53 -0
  394. package/dist/lib/types/document-workspace.d.ts.map +1 -0
  395. package/dist/lib/types/document-workspace.js +15 -0
  396. package/dist/lib/types/document-workspace.js.map +1 -0
  397. package/dist/lib/types/document.d.ts +5 -0
  398. package/dist/lib/types/document.d.ts.map +1 -1
  399. package/dist/lib/types/events.d.ts +134 -0
  400. package/dist/lib/types/events.d.ts.map +1 -0
  401. package/dist/lib/types/events.js +4 -0
  402. package/dist/lib/types/events.js.map +1 -0
  403. package/dist/lib/types/index.d.ts +3 -0
  404. package/dist/lib/types/index.d.ts.map +1 -1
  405. package/dist/lib/types/index.js +3 -0
  406. package/dist/lib/types/index.js.map +1 -1
  407. package/dist/lib/types/schemas.js +1 -1
  408. package/dist/lib/types/schemas.js.map +1 -1
  409. package/dist/lib/utils/fetch-remote-file.d.ts +11 -0
  410. package/dist/lib/utils/fetch-remote-file.d.ts.map +1 -0
  411. package/dist/lib/utils/fetch-remote-file.js +169 -0
  412. package/dist/lib/utils/fetch-remote-file.js.map +1 -0
  413. package/dist/lib/utils/index.d.ts +1 -1
  414. package/dist/lib/utils/index.d.ts.map +1 -1
  415. package/dist/lib/utils/index.js.map +1 -1
  416. package/dist/local-api/collection-api.d.ts +49 -2
  417. package/dist/local-api/collection-api.d.ts.map +1 -1
  418. package/dist/local-api/collection-api.js +193 -27
  419. package/dist/mcp/tools.d.ts +39 -4
  420. package/dist/mcp/tools.d.ts.map +1 -1
  421. package/dist/mcp/tools.js +546 -362
  422. package/dist/plugins/resolver.d.ts +19 -1
  423. package/dist/plugins/resolver.d.ts.map +1 -1
  424. package/dist/plugins/resolver.js +17 -1
  425. package/dist/plugins/types.d.ts +68 -2
  426. package/dist/plugins/types.d.ts.map +1 -1
  427. package/dist/preview/stega.d.ts +2 -2
  428. package/dist/preview/stega.d.ts.map +1 -1
  429. package/dist/preview/stega.js +52 -29
  430. package/dist/schema-utils/validator.d.ts.map +1 -1
  431. package/dist/schema-utils/validator.js +5 -2
  432. package/dist/server/api/index.d.ts.map +1 -1
  433. package/dist/server/api/index.js +16 -0
  434. package/dist/server/api/routes/agent-change-sets.d.ts +4 -0
  435. package/dist/server/api/routes/agent-change-sets.d.ts.map +1 -0
  436. package/dist/server/api/routes/agent-change-sets.js +131 -0
  437. package/dist/server/api/routes/agent-chat.d.ts +14 -0
  438. package/dist/server/api/routes/agent-chat.d.ts.map +1 -0
  439. package/dist/server/api/routes/agent-chat.js +223 -0
  440. package/dist/server/api/routes/document-versions.d.ts.map +1 -1
  441. package/dist/server/api/routes/document-versions.js +16 -2
  442. package/dist/server/api/routes/documents-by-id.d.ts.map +1 -1
  443. package/dist/server/api/routes/documents-by-id.js +11 -1
  444. package/dist/server/api/routes/documents-publish.d.ts.map +1 -1
  445. package/dist/server/api/routes/documents-publish.js +145 -2
  446. package/dist/server/api/routes/jobs.d.ts +4 -0
  447. package/dist/server/api/routes/jobs.d.ts.map +1 -0
  448. package/dist/server/api/routes/jobs.js +94 -0
  449. package/dist/server/api/routes/resolve-created-by.d.ts +13 -0
  450. package/dist/server/api/routes/resolve-created-by.d.ts.map +1 -0
  451. package/dist/server/api/routes/resolve-created-by.js +30 -0
  452. package/dist/server/api/routes/workers-run.d.ts +4 -0
  453. package/dist/server/api/routes/workers-run.d.ts.map +1 -0
  454. package/dist/server/api/routes/workers-run.js +40 -0
  455. package/dist/server/index.d.ts +2 -0
  456. package/dist/server/index.d.ts.map +1 -1
  457. package/dist/server/index.js +3 -0
  458. package/dist/services/version-service.d.ts +19 -3
  459. package/dist/services/version-service.d.ts.map +1 -1
  460. package/dist/services/version-service.js +75 -69
  461. package/dist/type-gen.d.ts.map +1 -1
  462. package/dist/type-gen.js +11 -0
  463. package/dist/types/agent-change-sets.d.ts +70 -0
  464. package/dist/types/agent-change-sets.d.ts.map +1 -0
  465. package/dist/types/agent-change-sets.js +6 -0
  466. package/dist/types/agent-stream.d.ts +60 -0
  467. package/dist/types/agent-stream.d.ts.map +1 -0
  468. package/dist/types/agent-stream.js +11 -0
  469. package/dist/types/agent-tools.d.ts +65 -0
  470. package/dist/types/agent-tools.d.ts.map +1 -0
  471. package/dist/types/agent-tools.js +1 -0
  472. package/dist/types/config.d.ts +62 -0
  473. package/dist/types/config.d.ts.map +1 -1
  474. package/dist/types/document-workspace.d.ts +53 -0
  475. package/dist/types/document-workspace.d.ts.map +1 -0
  476. package/dist/types/document-workspace.js +14 -0
  477. package/dist/types/document.d.ts +5 -0
  478. package/dist/types/document.d.ts.map +1 -1
  479. package/dist/types/events.d.ts +134 -0
  480. package/dist/types/events.d.ts.map +1 -0
  481. package/dist/types/events.js +3 -0
  482. package/dist/types/index.d.ts +3 -0
  483. package/dist/types/index.d.ts.map +1 -1
  484. package/dist/types/index.js +3 -0
  485. package/dist/types/schemas.js +1 -1
  486. package/dist/utils/fetch-remote-file.d.ts +11 -0
  487. package/dist/utils/fetch-remote-file.d.ts.map +1 -0
  488. package/dist/utils/fetch-remote-file.js +168 -0
  489. package/dist/utils/index.d.ts +1 -1
  490. package/dist/utils/index.d.ts.map +1 -1
  491. package/package.json +5 -1
@@ -14,6 +14,7 @@
14
14
  import { setSaveStateContext } from '../../save-state-context.svelte';
15
15
  import { usePermissions } from '../../permissions-context.svelte';
16
16
  import AdminSlot from './AdminSlot.svelte';
17
+ import ScheduleDialog from './ScheduleDialog.svelte';
17
18
  import { createPartResolver } from '../../plugins/resolver';
18
19
  import type { CMSPlugin, DocumentActionProps } from '../../plugins/types';
19
20
  import { getDefaultValueForFieldType } from '../../utils/field-defaults';
@@ -37,11 +38,19 @@
37
38
  Smartphone,
38
39
  ZoomIn,
39
40
  ZoomOut,
40
- ArrowLeft
41
+ ArrowLeft,
42
+ CalendarClock,
43
+ X,
44
+ Lock
41
45
  } from '@lucide/svelte';
42
46
  import { stegaEncodeDocument } from '../../preview/stega.js';
43
47
  import { collectAssetRefs, injectAssetData, type ResolvedAsset } from '../../preview/assets.js';
44
48
  import { setRichtextEditorRegistry } from '../../richtext-context.svelte.js';
49
+ import { documentWorkspaceRegistry } from '../../document-workspace-registry.svelte';
50
+ import type {
51
+ DocumentWorkspace,
52
+ DocumentWorkspaceOperation
53
+ } from '../../types/document-workspace';
45
54
 
46
55
  interface Props {
47
56
  schemas: SchemaType[];
@@ -68,6 +77,12 @@
68
77
  createdAt?: string;
69
78
  } | null;
70
79
  isReadOnly?: boolean;
80
+ /** Suppresses the bottom action bar (Publish / Schedule / Unpublish / Delete).
81
+ * Set by the host when another editor is stacked on top of this one and owns
82
+ * the actions instead — two action bars in the same corner give no clue which
83
+ * document each one publishes. This hides the bar only; the document keeps
84
+ * auto-saving and its status stays visible in the header. */
85
+ hideActionBar?: boolean;
71
86
  /** When true, the host has hidden side panels — show a Minimize toggle. */
72
87
  focusMode?: boolean;
73
88
  /** Toggle host-driven focus mode. Omit to hide the focus button entirely. */
@@ -105,6 +120,7 @@
105
120
  onOpenVersionHistory,
106
121
  externalVersionPreview = null,
107
122
  isReadOnly = false,
123
+ hideActionBar = false,
108
124
  focusMode = false,
109
125
  onToggleFocus,
110
126
  presentationMode = false,
@@ -160,6 +176,14 @@
160
176
  let publishedData = $state<Record<string, any> | null>(null);
161
177
  const isViewingPublished = $derived(perspective === 'published');
162
178
 
179
+ // The scheduled action follows the active perspective — the tab IS the intent:
180
+ // Draft tab → publish (send this draft live later); Published tab → unpublish (take it
181
+ // down later). No toggle in the dialog. The control is gated on the matching permission.
182
+ const scheduleAction = $derived<'publish' | 'unpublish'>(
183
+ perspective === 'published' ? 'unpublish' : 'publish'
184
+ );
185
+ const canScheduleNow = $derived(scheduleAction === 'publish' ? canPublishDoc : canUnpublishDoc);
186
+
163
187
  // Plugin document actions applicable to this type. Capability-gated, with
164
188
  // built-in privileged roles bypassing. Cheap to rebuild from the plugin list.
165
189
  const pluginDocumentActions = $derived(
@@ -198,6 +222,80 @@
198
222
  : null
199
223
  );
200
224
 
225
+ // A live handle onto this document's editable state — the seam an external caller (today:
226
+ // the agent's content_patch_fields/content_save_draft bridge tools) plugs into instead of
227
+ // reaching into this component's internals directly. See types/document-workspace.ts for
228
+ // why this is shaped generically rather than as an AI-specific bridge.
229
+ const documentWorkspace: DocumentWorkspace = {
230
+ getSnapshot() {
231
+ return {
232
+ documentId: documentId ?? fullDocument?.id ?? null,
233
+ collection: documentType,
234
+ data: $state.snapshot(documentData),
235
+ status: !documentId
236
+ ? 'new'
237
+ : fullDocument?._meta?.status === 'published'
238
+ ? 'published'
239
+ : fullDocument?._meta?.status === 'unpublished'
240
+ ? 'unpublished'
241
+ : 'draft',
242
+ revision: (fullDocument?._meta?.revision as number | undefined) ?? null
243
+ };
244
+ },
245
+ getSelection() {
246
+ return { fieldName: focusedFieldName };
247
+ },
248
+ apply(operation: DocumentWorkspaceOperation) {
249
+ if (operation.type === 'patchFields') {
250
+ documentData = { ...documentData, ...operation.fields };
251
+ hasUnsavedChanges = true;
252
+ }
253
+ },
254
+ async validate() {
255
+ const invalid = await validateAllFields();
256
+ return {
257
+ isValid: invalid.length === 0,
258
+ errors: invalid.map((f) => ({ field: f.name, errors: f.messages }))
259
+ };
260
+ },
261
+ async flushSave(_expectedRevision) {
262
+ return await saveDocument(false);
263
+ },
264
+ async publish(_expectedRevision) {
265
+ await publishDocument();
266
+ if (saveError) {
267
+ return { success: false, conflict: saveError.startsWith('Conflict'), error: saveError };
268
+ }
269
+ return {
270
+ success: true,
271
+ revision: (fullDocument?._meta?.revision as number | undefined) ?? undefined
272
+ };
273
+ },
274
+ beginBatch(_source) {
275
+ batchActive = true;
276
+ },
277
+ endBatch() {
278
+ batchActive = false;
279
+ }
280
+ };
281
+
282
+ // Registers/clears this document's workspace handle in the shared registry
283
+ // (document-workspace-registry.svelte.ts) whenever the active document id changes —
284
+ // covers create→save (id appears), navigating between two open documents, and unmount.
285
+ $effect(() => {
286
+ const activeId = documentId ?? fullDocument?.id ?? null;
287
+ if (activeId) {
288
+ documentWorkspaceRegistry.register({
289
+ documentId: activeId,
290
+ collection: documentType,
291
+ workspace: documentWorkspace
292
+ });
293
+ }
294
+ return () => {
295
+ if (activeId) documentWorkspaceRegistry.clear(activeId);
296
+ };
297
+ });
298
+
201
299
  // Field-level access: mirrors the server's strip/drop logic so the form
202
300
  // only shows fields the user can see and disables fields they can't write.
203
301
  function fieldRoleAllowed(list: string[] | undefined): boolean {
@@ -299,7 +397,9 @@
299
397
  type: 'aphex:data',
300
398
  documentType,
301
399
  documentId: documentId ?? undefined,
302
- document: iframeStega ? stegaEncodeDocument(snapshot, schema?.fields ?? []) : snapshot
400
+ document: iframeStega
401
+ ? stegaEncodeDocument(snapshot, schema?.fields ?? [], schemas)
402
+ : snapshot
303
403
  },
304
404
  '*'
305
405
  );
@@ -620,6 +720,39 @@
620
720
  return;
621
721
  }
622
722
 
723
+ // 2a. Object array item — objectPath like "[0].heading" (or "[0]" alone). The
724
+ // item's fields live behind ObjectModal, so open that item's modal, then focus
725
+ // the subfield by the remaining dotted path (the modal renders subfields with
726
+ // bare data-field-path, e.g. "heading" or "seo.title").
727
+ if (objectPath) {
728
+ const itemMatch = objectPath.match(/^\[(\d+)\](?:\.(.*))?$/);
729
+ if (itemMatch) {
730
+ const itemIndex = Number(itemMatch[1]);
731
+ const subPath = itemMatch[2] ?? '';
732
+ const card = container.querySelector<HTMLElement>(`[data-array-item-index="${itemIndex}"]`);
733
+ if (card) {
734
+ card.click(); // opens the item's ObjectModal
735
+ if (subPath) {
736
+ // The modal mounts/animates in; poll a few frames for the subfield.
737
+ for (let attempt = 0; attempt < 20; attempt++) {
738
+ await tick();
739
+ const target = document.querySelector<HTMLElement>(`[data-field-path="${subPath}"]`);
740
+ if (target) {
741
+ target.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
742
+ const focusable = target.querySelector<HTMLElement>(
743
+ 'input:not([disabled]):not([readonly]), textarea:not([disabled]):not([readonly])'
744
+ );
745
+ focusable?.focus();
746
+ break;
747
+ }
748
+ await new Promise((r) => requestAnimationFrame(r));
749
+ }
750
+ }
751
+ return;
752
+ }
753
+ }
754
+ }
755
+
623
756
  // 2. Nested object subfield — find the subfield container by its dotted path
624
757
  if (objectPath) {
625
758
  const subContainer = container.querySelector<HTMLElement>(
@@ -635,7 +768,30 @@
635
768
  }
636
769
  }
637
770
 
638
- // 3. Primitive array — focus the Nth input inside the array field
771
+ // 3a. Object / reference array — reveal the Nth *row* rather than opening it.
772
+ // These rows are the drag-to-reorder handles, so surfacing the row in place is
773
+ // what lets an author act on it (reorder, remove, or click in themselves);
774
+ // auto-opening the row's editor would take that away and, for a reference row,
775
+ // navigate off the document entirely.
776
+ if (arrayIndex != null) {
777
+ const row = container.querySelector<HTMLElement>(`[data-array-index="${arrayIndex}"]`);
778
+ if (row) {
779
+ row.scrollIntoView({ behavior: 'smooth', block: 'center' });
780
+ // Animated directly rather than via `focusedFieldName` state: that ring is
781
+ // field-wide, and plumbing a per-row index down through SchemaField →
782
+ // ArrayField would add a prop to every array just for a 1.2s flash.
783
+ row.animate(
784
+ [
785
+ { outline: '2px solid var(--primary)', outlineOffset: '3px', borderRadius: '6px' },
786
+ { outline: '2px solid transparent', outlineOffset: '8px', borderRadius: '6px' }
787
+ ],
788
+ { duration: 1200, easing: 'ease-out', fill: 'none' }
789
+ );
790
+ return;
791
+ }
792
+ }
793
+
794
+ // 3b. Primitive array — focus the Nth input inside the array field
639
795
  if (arrayIndex != null) {
640
796
  const inputs = container.querySelectorAll<HTMLElement>(
641
797
  'input:not([disabled]):not([readonly]), textarea:not([disabled]):not([readonly])'
@@ -706,6 +862,53 @@
706
862
 
707
863
  // Header options dropdown
708
864
  let showHeaderMenu = $state(false);
865
+ let showScheduleDialog = $state(false);
866
+
867
+ // Pending scheduled publish/unpublish for this document (replace semantics → at most one).
868
+ type ScheduledJob = { jobId: string; type: string; runAt: string; status: string };
869
+ let scheduledJobs = $state<ScheduledJob[]>([]);
870
+ // The soonest pending schedule — what the header indicator shows.
871
+ const nextSchedule = $derived(
872
+ [...scheduledJobs].sort(
873
+ (a, b) => new Date(a.runAt).getTime() - new Date(b.runAt).getTime()
874
+ )[0] ?? null
875
+ );
876
+
877
+ async function loadSchedule() {
878
+ if (!documentId) {
879
+ scheduledJobs = [];
880
+ return;
881
+ }
882
+ try {
883
+ const res = await documents.getSchedule(documentId);
884
+ scheduledJobs = res.success ? (res.data ?? []) : [];
885
+ } catch {
886
+ scheduledJobs = [];
887
+ }
888
+ }
889
+
890
+ // Optimistic: the schedule call returns the created job, so show the badge immediately.
891
+ // Replace semantics on the server mean this new job supersedes any prior one. Reconcile
892
+ // with a background fetch so we converge on server truth (id, exact runAt).
893
+ function onScheduleCreated(job: ScheduledJob) {
894
+ scheduledJobs = [job];
895
+ loadSchedule();
896
+ }
897
+
898
+ async function cancelSchedule() {
899
+ if (!documentId) return;
900
+ // Optimistically clear the badge; restore from the server if the cancel fails.
901
+ const previous = scheduledJobs;
902
+ scheduledJobs = [];
903
+ const res = await documents.cancelSchedule(documentId);
904
+ if (res.success) {
905
+ toast.success('Schedule cancelled');
906
+ loadSchedule();
907
+ } else {
908
+ scheduledJobs = previous;
909
+ toast.error(res.error || 'Failed to cancel schedule');
910
+ }
911
+ }
709
912
 
710
913
  // Version history
711
914
  let showVersionHistory = $state(false);
@@ -775,9 +978,40 @@
775
978
 
776
979
  const savedAgoText = $derived(lastSaved ? `Saved ${timeAgo(lastSaved)}` : null);
777
980
 
981
+ // Relative future time for the scheduled-publish indicator ("in 3h"). Reads `now` so it ticks.
982
+ function timeUntil(date: Date): string {
983
+ const seconds = Math.floor((date.getTime() - now) / 1000);
984
+ if (seconds <= 0) return 'now';
985
+ if (seconds < 60) return `in ${seconds}s`;
986
+ const minutes = Math.floor(seconds / 60);
987
+ if (minutes < 60) return `in ${minutes}m`;
988
+ const hours = Math.floor(minutes / 60);
989
+ if (hours < 24) return `in ${hours}h`;
990
+ const days = Math.floor(hours / 24);
991
+ return `in ${days}d`;
992
+ }
993
+
994
+ // Human phrasing for the schedule banner: "today/tomorrow at 8:00 AM", "on Monday at …",
995
+ // or "on Nov 10 at …" further out. Reads `now` so it stays current.
996
+ function humanizeSchedule(date: Date): string {
997
+ const time = date.toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' });
998
+ const startOfDay = (x: Date) => new Date(x.getFullYear(), x.getMonth(), x.getDate()).getTime();
999
+ const days = Math.round((startOfDay(date) - startOfDay(new Date(now))) / 86_400_000);
1000
+ let day: string;
1001
+ if (days <= 0) day = 'today';
1002
+ else if (days === 1) day = 'tomorrow';
1003
+ else if (days < 7) day = `on ${date.toLocaleDateString(undefined, { weekday: 'long' })}`;
1004
+ else day = `on ${date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' })}`;
1005
+ return `${day} at ${time}`;
1006
+ }
1007
+
778
1008
  // Auto-save functionality (every 2 seconds when there are changes)
779
1009
  let hasUnsavedChanges = $state(false);
780
1010
  let autoSaveTimer: ReturnType<typeof setTimeout> | null = null;
1011
+ // Set while a `DocumentWorkspace` batch (currently: one agent turn) is applying a burst of
1012
+ // `apply()` patches — suppresses autosave so N patches don't produce N separate CAS writes;
1013
+ // the batch's caller is responsible for one explicit `flushSave` before/at `endBatch()`.
1014
+ let batchActive = $state(false);
781
1015
  let hasValidationErrors = $state(false);
782
1016
  // Snapshot of the default-initialized data for a brand-new (unsaved) document.
783
1017
  // Used to detect real user edits without relying on "is the value truthy" heuristics,
@@ -817,6 +1051,14 @@
817
1051
  (hasUnpublishedContent || isUnpublished) && !saving && documentId && !hasValidationErrors
818
1052
  );
819
1053
 
1054
+ // The schedule button mirrors the inline action button's enabled state (permission is checked
1055
+ // separately, for visibility): scheduling a PUBLISH is enabled exactly when the Publish button
1056
+ // is (`canPublish` — there are unpublished, valid changes), so you can't queue a no-op publish;
1057
+ // scheduling an UNPUBLISH mirrors the Unpublish button (enabled whenever not mid-save).
1058
+ const canScheduleActionNow = $derived(
1059
+ scheduleAction === 'publish' ? Boolean(canPublish) : !saving
1060
+ );
1061
+
820
1062
  // Get preview title based on schema config. When viewing in published
821
1063
  // perspective, read from the loaded published snapshot — otherwise the
822
1064
  // header shows the draft's title even though the body shows published
@@ -884,6 +1126,14 @@
884
1126
  }
885
1127
  });
886
1128
 
1129
+ // Keep the schedule indicator in sync with the current document.
1130
+ $effect(() => {
1131
+ // Reference documentId so this re-runs when the editor switches documents.
1132
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- reactive dependency
1133
+ documentId;
1134
+ loadSchedule();
1135
+ });
1136
+
887
1137
  // Load existing document data when editing
888
1138
  $effect(() => {
889
1139
  if (!isCreating && documentId) {
@@ -1084,7 +1334,7 @@
1084
1334
  // Debounced auto-save. Comparing against the initial-defaults snapshot (for new
1085
1335
  // docs) or the saved draft (for existing docs) means any real user edit triggers
1086
1336
  // save — including unchecking a boolean or clearing a field.
1087
- if (hasChanges && schema && canWriteCurrentDoc) {
1337
+ if (hasChanges && schema && canWriteCurrentDoc && !batchActive) {
1088
1338
  autoSaveTimer = setTimeout(() => {
1089
1339
  cmsLogger.debug(
1090
1340
  '[Document Editor]',
@@ -1106,12 +1356,15 @@
1106
1356
  };
1107
1357
  });
1108
1358
 
1109
- async function saveDocument(isAutoSave = false) {
1110
- if (saving) return;
1111
-
1112
- saving = true;
1113
- saveError = null;
1114
-
1359
+ /** The actual persist no toasts, no side-effect UI updates, just the write + CAS-conflict
1360
+ * detection, returning a result `DocumentWorkspace.flushSave` can also consume directly.
1361
+ * `saveDocument` (below) is the UI-facing wrapper that adds toasts/state-sync around this. */
1362
+ async function performSave(): Promise<{
1363
+ success: boolean;
1364
+ revision?: number;
1365
+ conflict?: boolean;
1366
+ error?: string;
1367
+ }> {
1115
1368
  try {
1116
1369
  let response;
1117
1370
 
@@ -1142,12 +1395,15 @@
1142
1395
  // Always call onSaved to switch to edit mode after creation
1143
1396
  onSaved?.(response.data.id);
1144
1397
  } else {
1145
- toast.error(response?.error || 'Failed to create document');
1398
+ return { success: false, error: response?.error || 'Failed to create document' };
1146
1399
  }
1147
1400
  } else if (documentId) {
1148
- // Update existing document
1401
+ // Update existing document. Send the revision we last read so a stale
1402
+ // write (another tab, an AI agent) surfaces as a 409 conflict below
1403
+ // instead of silently overwriting whatever changed in between.
1149
1404
  response = await documents.updateById(documentId, {
1150
- data: documentData
1405
+ data: documentData,
1406
+ expectedRevision: fullDocument?._meta?.revision as number | undefined
1151
1407
  });
1152
1408
 
1153
1409
  // Update fullDocument with the response to keep saved data in sync
@@ -1167,47 +1423,84 @@
1167
1423
  }
1168
1424
 
1169
1425
  if (response?.success) {
1170
- lastSaved = new Date();
1171
- hasUnsavedChanges = false;
1172
- if (response.data?.id) notifyDocumentChanged(response.data.id);
1173
- if (presentationMode && iframeRef?.contentWindow) {
1174
- iframeRef.contentWindow.postMessage({ type: 'aphex:refresh' }, '*');
1175
- }
1176
- if (isAutoSave) {
1177
- // Trigger validation on all fields after auto-save
1178
- validateAllFields(); // Update validation status
1179
- schemaFields.forEach((fieldComponent, index) => {
1180
- const field = schema?.fields[index];
1181
- if (fieldComponent && field) {
1182
- fieldComponent.performValidation(documentData[field.name], {});
1183
- }
1184
- }); // Notify parent of autosave with current title
1185
- if (onAutoSaved && documentId) {
1186
- onAutoSaved(documentId, getPreviewTitle());
1187
- }
1188
- }
1189
- if (showVersionHistory) {
1190
- loadVersions();
1191
- }
1192
- } else {
1193
- throw new Error(response?.error || 'Failed to save document');
1426
+ return { success: true, revision: fullDocument?._meta?.revision as number | undefined };
1194
1427
  }
1428
+ return { success: false, error: response?.error || 'Failed to save document' };
1195
1429
  } catch (err) {
1196
- toast.error(err instanceof ApiError ? err.message : 'Failed to save document');
1197
-
1198
- // Extract validation errors if present
1430
+ // A 409 means someone else (another tab, an AI agent) saved this document
1431
+ // since we last read it — surface that distinctly rather than a generic
1432
+ // error, and never retry with a blind overwrite. The caller must reload to
1433
+ // see the other writer's change before saving again.
1434
+ if (err instanceof ApiError && err.status === 409) {
1435
+ return {
1436
+ success: false,
1437
+ conflict: true,
1438
+ error:
1439
+ 'Conflict: this document was updated by someone else. Reload the page to continue editing.'
1440
+ };
1441
+ }
1199
1442
  if (err instanceof ApiError && err.response?.validationErrors) {
1200
1443
  const validationErrors = err.response.validationErrors;
1201
1444
  const errorMessages = validationErrors
1202
1445
  .map((ve: any) => `${ve.field}: ${ve.errors.join(', ')}`)
1203
1446
  .join('; ');
1204
- saveError = `Validation failed: ${errorMessages}`;
1447
+ return { success: false, error: `Validation failed: ${errorMessages}` };
1448
+ }
1449
+ return {
1450
+ success: false,
1451
+ error: err instanceof ApiError ? err.message : 'Failed to save document'
1452
+ };
1453
+ }
1454
+ }
1455
+
1456
+ async function saveDocument(isAutoSave = false) {
1457
+ if (saving)
1458
+ return { success: false, error: 'Already saving' } as {
1459
+ success: boolean;
1460
+ revision?: number;
1461
+ conflict?: boolean;
1462
+ error?: string;
1463
+ };
1464
+
1465
+ saving = true;
1466
+ saveError = null;
1467
+
1468
+ const result = await performSave();
1469
+
1470
+ if (result.success) {
1471
+ lastSaved = new Date();
1472
+ hasUnsavedChanges = false;
1473
+ if (fullDocument?.id) notifyDocumentChanged(fullDocument.id);
1474
+ if (presentationMode && iframeRef?.contentWindow) {
1475
+ iframeRef.contentWindow.postMessage({ type: 'aphex:refresh' }, '*');
1476
+ }
1477
+ if (isAutoSave) {
1478
+ // Trigger validation on all fields after auto-save
1479
+ validateAllFields(); // Update validation status
1480
+ schemaFields.forEach((fieldComponent, index) => {
1481
+ const field = schema?.fields[index];
1482
+ if (fieldComponent && field) {
1483
+ fieldComponent.performValidation(documentData[field.name], {});
1484
+ }
1485
+ }); // Notify parent of autosave with current title
1486
+ if (onAutoSaved && documentId) {
1487
+ onAutoSaved(documentId, getPreviewTitle());
1488
+ }
1489
+ }
1490
+ if (showVersionHistory) {
1491
+ loadVersions();
1492
+ }
1493
+ } else {
1494
+ if (result.conflict) {
1495
+ toast.error('This document was changed elsewhere. Reload to see the latest version.');
1205
1496
  } else {
1206
- saveError = err instanceof ApiError ? err.message : 'Failed to save document';
1497
+ toast.error(result.error ?? 'Failed to save document');
1207
1498
  }
1208
- } finally {
1209
- saving = false;
1499
+ saveError = result.error ?? 'Failed to save document';
1210
1500
  }
1501
+
1502
+ saving = false;
1503
+ return result;
1211
1504
  }
1212
1505
 
1213
1506
  async function publishDocument() {
@@ -1287,7 +1580,9 @@
1287
1580
  saveError = null;
1288
1581
 
1289
1582
  try {
1290
- const response = await documents.publish(documentId);
1583
+ const response = await documents.publish(documentId, {
1584
+ expectedRevision: fullDocument?._meta?.revision as number | undefined
1585
+ });
1291
1586
 
1292
1587
  if (response.success && response.data) {
1293
1588
  // Update local state with new published hash. Sync documentData from
@@ -1315,17 +1610,23 @@
1315
1610
  throw new Error(response.error || 'Failed to publish document');
1316
1611
  }
1317
1612
  } catch (err) {
1318
- toast.error(err instanceof ApiError ? err.message : 'Failed to publish document');
1319
-
1320
- // Extract validation errors if present
1321
- if (err instanceof ApiError && err.response?.validationErrors) {
1322
- const validationErrors = err.response.validationErrors;
1323
- const errorMessages = validationErrors
1324
- .map((ve: any) => `${ve.field}: ${ve.errors.join(', ')}`)
1325
- .join('; ');
1326
- saveError = `Cannot publish - Validation failed: ${errorMessages}`;
1613
+ if (err instanceof ApiError && err.status === 409) {
1614
+ toast.error('This document was changed elsewhere. Reload to see the latest version.');
1615
+ saveError =
1616
+ 'Conflict: this document was updated by someone else. Reload the page to continue editing.';
1327
1617
  } else {
1328
- saveError = err instanceof ApiError ? err.message : 'Failed to publish document';
1618
+ toast.error(err instanceof ApiError ? err.message : 'Failed to publish document');
1619
+
1620
+ // Extract validation errors if present
1621
+ if (err instanceof ApiError && err.response?.validationErrors) {
1622
+ const validationErrors = err.response.validationErrors;
1623
+ const errorMessages = validationErrors
1624
+ .map((ve: any) => `${ve.field}: ${ve.errors.join(', ')}`)
1625
+ .join('; ');
1626
+ saveError = `Cannot publish - Validation failed: ${errorMessages}`;
1627
+ } else {
1628
+ saveError = err instanceof ApiError ? err.message : 'Failed to publish document';
1629
+ }
1329
1630
  }
1330
1631
  } finally {
1331
1632
  saving = false;
@@ -1388,7 +1689,9 @@
1388
1689
  saveError = null;
1389
1690
 
1390
1691
  try {
1391
- const response = await documents.unpublish(documentId);
1692
+ const response = await documents.unpublish(documentId, {
1693
+ expectedRevision: fullDocument?._meta?.revision as number | undefined
1694
+ });
1392
1695
  if (response.success) {
1393
1696
  fullDocument = {
1394
1697
  ...fullDocument,
@@ -1403,7 +1706,11 @@
1403
1706
  throw new Error(response.error || 'Failed to unpublish');
1404
1707
  }
1405
1708
  } catch (err) {
1406
- toast.error(err instanceof ApiError ? err.message : 'Failed to unpublish document');
1709
+ if (err instanceof ApiError && err.status === 409) {
1710
+ toast.error('This document was changed elsewhere. Reload to see the latest version.');
1711
+ } else {
1712
+ toast.error(err instanceof ApiError ? err.message : 'Failed to unpublish document');
1713
+ }
1407
1714
  } finally {
1408
1715
  saving = false;
1409
1716
  }
@@ -1775,6 +2082,41 @@
1775
2082
  {/if}
1776
2083
 
1777
2084
  <div class="relative flex h-full w-full min-w-0 flex-col overflow-hidden">
2085
+ <!-- Scheduled publish/unpublish banner — a full-width strip pinned to the very top edge of
2086
+ the editor, above the hero header (breadcrumb + actions), so a pending schedule is the
2087
+ first thing seen. Inner padding matches the hero's (px-4 lg:px-6) so its text lines up
2088
+ with the title below. -->
2089
+ {#if !presentationMode && nextSchedule}
2090
+ {@const isPub = nextSchedule.type === 'document.publish'}
2091
+ {@const runAtDate = new Date(nextSchedule.runAt)}
2092
+ <div
2093
+ class="border-primary/15 bg-primary/5 text-primary flex w-full flex-wrap items-center gap-x-2 gap-y-1 border-b px-4 py-2.5 lg:px-6"
2094
+ >
2095
+ <Lock class="h-4 w-4 shrink-0" />
2096
+ <span class="min-w-0 text-[0.9375rem] leading-tight font-medium">
2097
+ Scheduled to be {isPub ? 'published' : 'unpublished'}
2098
+ {humanizeSchedule(runAtDate)}
2099
+ <span class="text-primary/50 font-normal">· {timeUntil(runAtDate)}</span>
2100
+ </span>
2101
+ <div class="ml-auto flex items-center gap-1">
2102
+ <button
2103
+ type="button"
2104
+ class="hover:bg-primary/10 rounded px-2 py-1 text-xs font-medium transition-colors"
2105
+ onclick={() => (showScheduleDialog = true)}
2106
+ >
2107
+ Reschedule
2108
+ </button>
2109
+ <button
2110
+ type="button"
2111
+ class="hover:bg-primary/10 flex items-center gap-1 rounded px-2 py-1 text-xs font-medium transition-colors"
2112
+ onclick={cancelSchedule}
2113
+ >
2114
+ <X class="h-3 w-3" /> Cancel
2115
+ </button>
2116
+ </div>
2117
+ </div>
2118
+ {/if}
2119
+
1778
2120
  <!-- Hero Header -->
1779
2121
  {#if !presentationMode}
1780
2122
  <div class="bg-background w-full min-w-0 overflow-x-clip px-4 pt-4 pb-5 lg:px-6 lg:pt-5">
@@ -1795,7 +2137,7 @@
1795
2137
  <!-- Always one line (`truncate`); font shrinks where space is tight (narrow
1796
2138
  editor / reference panel) so more of the title fits before the ellipsis,
1797
2139
  and stays large on wide screens. -->
1798
- <h1 class="block w-full min-w-0 truncate text-xl font-semibold tracking-tight lg:text-2xl">
2140
+ <h1 class="block w-full min-w-0 truncate text-2xl font-semibold tracking-tight lg:text-4xl">
1799
2141
  {getPreviewTitle()}
1800
2142
  </h1>
1801
2143
 
@@ -2205,7 +2547,7 @@
2205
2547
  </div>
2206
2548
 
2207
2549
  <!-- Sanity-style bottom bar -->
2208
- {#if documentId}
2550
+ {#if documentId && !hideActionBar}
2209
2551
  <div class="border-rule bg-background relative z-50 border-t p-4">
2210
2552
  {#if isPreviewingVersion && activePreview}
2211
2553
  <!-- Version preview footer -->
@@ -2221,7 +2563,9 @@
2221
2563
  onclick={async () => {
2222
2564
  if (!documentId || !activePreview) return;
2223
2565
  try {
2224
- await documents.restoreVersion(documentId, activePreview.versionNumber);
2566
+ await documents.restoreVersion(documentId, activePreview.versionNumber, {
2567
+ expectedRevision: fullDocument?._meta?.revision as number | undefined
2568
+ });
2225
2569
  const docRes = await documents.getById(documentId);
2226
2570
  if (docRes.success && docRes.data) {
2227
2571
  const doc = docRes.data as Record<string, any>;
@@ -2245,8 +2589,14 @@
2245
2589
  if (onRestored && documentId) {
2246
2590
  onRestored(documentId);
2247
2591
  }
2248
- } catch {
2249
- toast.error('Failed to restore revision');
2592
+ } catch (err) {
2593
+ if (err instanceof ApiError && err.status === 409) {
2594
+ toast.error(
2595
+ 'This document was changed elsewhere. Reload to see the latest version.'
2596
+ );
2597
+ } else {
2598
+ toast.error('Failed to restore revision');
2599
+ }
2250
2600
  }
2251
2601
  }}
2252
2602
  >
@@ -2272,15 +2622,37 @@
2272
2622
  : 'Unknown'}
2273
2623
  {/if}
2274
2624
  </p>
2275
- {#if fullDocument?._meta?.status === 'unpublished'}
2276
- {#if canPublishDoc}
2277
- <Button size="sm" onclick={publishDocument} disabled={saving}>Publish</Button>
2625
+ <div class="flex items-center gap-2">
2626
+ {#if documentId && canScheduleNow}
2627
+ <Button
2628
+ variant="ghost"
2629
+ size="icon"
2630
+ disabled={!canScheduleActionNow}
2631
+ onclick={() => (showScheduleDialog = true)}
2632
+ class="h-8 w-8 {canScheduleActionNow ? 'cursor-pointer' : ''} {nextSchedule
2633
+ ? 'text-primary'
2634
+ : ''}"
2635
+ title={!canScheduleActionNow
2636
+ ? 'Nothing to publish — make a change first'
2637
+ : nextSchedule
2638
+ ? 'Reschedule'
2639
+ : scheduleAction === 'publish'
2640
+ ? 'Schedule publish'
2641
+ : 'Schedule unpublish'}
2642
+ >
2643
+ <CalendarClock class="h-4 w-4" />
2644
+ </Button>
2278
2645
  {/if}
2279
- {:else if canUnpublishDoc}
2280
- <Button size="sm" variant="secondary" onclick={unpublishDocument} disabled={saving}>
2281
- Unpublish
2282
- </Button>
2283
- {/if}
2646
+ {#if fullDocument?._meta?.status === 'unpublished'}
2647
+ {#if canPublishDoc}
2648
+ <Button size="sm" onclick={publishDocument} disabled={saving}>Publish</Button>
2649
+ {/if}
2650
+ {:else if canUnpublishDoc}
2651
+ <Button size="sm" variant="secondary" onclick={unpublishDocument} disabled={saving}>
2652
+ Unpublish
2653
+ </Button>
2654
+ {/if}
2655
+ </div>
2284
2656
  </div>
2285
2657
  {:else}
2286
2658
  <div class="flex items-center justify-between">
@@ -2293,6 +2665,24 @@
2293
2665
 
2294
2666
  <!-- Right: Publish button + horizontal three dots menu -->
2295
2667
  <div class="flex items-center gap-2">
2668
+ {#if documentId && canScheduleNow}
2669
+ <Button
2670
+ variant="ghost"
2671
+ size="icon"
2672
+ disabled={!canScheduleActionNow}
2673
+ onclick={() => (showScheduleDialog = true)}
2674
+ class="h-8 w-8 {canScheduleActionNow ? 'cursor-pointer' : ''} {nextSchedule
2675
+ ? 'text-primary'
2676
+ : ''}"
2677
+ title={!canScheduleActionNow
2678
+ ? 'Nothing to publish — make a change first'
2679
+ : nextSchedule
2680
+ ? 'Reschedule'
2681
+ : 'Schedule publish'}
2682
+ >
2683
+ <CalendarClock class="h-4 w-4" />
2684
+ </Button>
2685
+ {/if}
2296
2686
  {#if canPublishDoc && !isViewingPublished}
2297
2687
  <Button
2298
2688
  onclick={publishDocument}
@@ -2428,6 +2818,15 @@
2428
2818
  {/if}
2429
2819
 
2430
2820
  <!-- Inspect Modal -->
2821
+ {#if documentId}
2822
+ <ScheduleDialog
2823
+ bind:open={showScheduleDialog}
2824
+ {documentId}
2825
+ action={scheduleAction}
2826
+ onScheduled={onScheduleCreated}
2827
+ />
2828
+ {/if}
2829
+
2431
2830
  {#if showInspect}
2432
2831
  <div class="absolute inset-0 z-50 flex items-center justify-center bg-black/50">
2433
2832
  <div