@blinkk/root-cms 3.1.2 → 3.1.4

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 (136) hide show
  1. package/dist/app.js +1 -4
  2. package/dist/chunk-MYN76V3L.js +764 -0
  3. package/dist/{chunk-4VZTYUQ7.js → chunk-NC3CRIFA.js} +191 -13
  4. package/dist/core/ai-tools.d.ts +82 -42
  5. package/dist/core/ai.d.ts +54 -218
  6. package/dist/core/plugin.d.ts +10 -4
  7. package/dist/functions.js +1 -2
  8. package/dist/plugin.js +42 -208
  9. package/dist/shared/ai/models.d.ts +110 -0
  10. package/dist/shared/ai/prompt-utils.d.ts +59 -0
  11. package/dist/ui/AIPage-4W4MFELY.js +1 -0
  12. package/dist/ui/{AssetsPage-KLGNE6D6.js → AssetsPage-QUL2NBWK.js} +1 -1
  13. package/dist/ui/CollectionPage-7VU6SUUP.js +1 -0
  14. package/dist/ui/ComparePage-DCP5QHDE.js +1 -0
  15. package/dist/ui/DataPage-TUVFBALT.js +1 -0
  16. package/dist/ui/DataSourcePage-YE2ZOHYI.js +1 -0
  17. package/dist/ui/DocTranslationsPage-3ILTQOP2.js +1 -0
  18. package/dist/ui/DocumentPage-JFQ3Y2GH.js +1 -0
  19. package/dist/ui/EditDataSourcePage-WHPZDMD4.js +1 -0
  20. package/dist/ui/EditReleasePage-QKWHIURV.js +1 -0
  21. package/dist/ui/EmbeddedAIPage-SQLOV4IT.js +1 -0
  22. package/dist/ui/{EmbeddedDocumentPage-4TLMCDSO.js → EmbeddedDocumentPage-6FYHUHTE.js} +1 -1
  23. package/dist/ui/{ImageEditorDialog-GGS4VFEA.js → ImageEditorDialog-Q44YTLB5.js} +1 -1
  24. package/dist/ui/LogsPage-RC5O722W.js +1 -0
  25. package/dist/ui/NewDataSourcePage-QXW54CHQ.js +1 -0
  26. package/dist/ui/NewReleasePage-TKYWVTOH.js +1 -0
  27. package/dist/ui/{NotFoundPage-A6OCYREA.js → NotFoundPage-RMNPVSD3.js} +1 -1
  28. package/dist/ui/{ProjectPage-UHRXNN7O.js → ProjectPage-VY4EJX6J.js} +1 -1
  29. package/dist/ui/ReleasePage-VZAL4KUI.js +1 -0
  30. package/dist/ui/ReleasesPage-A4BLUNJE.js +1 -0
  31. package/dist/ui/SettingsPage-MP3YHQDP.js +1 -0
  32. package/dist/ui/{SidebarToolsPage-VXVRZ2C3.js → SidebarToolsPage-S5DMIEQ6.js} +1 -1
  33. package/dist/ui/{TaskPage-LDPRN3G6.js → TaskPage-LCBDMA4I.js} +1 -1
  34. package/dist/ui/TasksPage-72KVSUQY.js +1 -0
  35. package/dist/ui/TranslationsArbPage-B7TIRREV.js +2 -0
  36. package/dist/ui/TranslationsEditPage-VQG4YHA6.js +1 -0
  37. package/dist/ui/{TranslationsPage-674JU6L4.js → TranslationsPage-6VCVMNXI.js} +1 -1
  38. package/dist/ui/ai-tools-7S4URTMT.js +1 -0
  39. package/dist/ui/chunk-2PDMFZBF.js +1 -0
  40. package/dist/ui/{chunk-SKQECSUR.js → chunk-3WQCVKK4.js} +1 -1
  41. package/dist/ui/{chunk-WMICEDOT.js → chunk-4N6CPIOQ.js} +1 -1
  42. package/dist/ui/{chunk-NJMNOITA.js → chunk-5SY2SVEX.js} +1 -1
  43. package/dist/ui/chunk-62VPTZJL.js +1 -0
  44. package/dist/ui/{chunk-GT5EQSEX.js → chunk-6P75AZ3B.js} +1 -1
  45. package/dist/ui/{chunk-CAY5NM4Z.js → chunk-74OB6FM4.js} +1 -1
  46. package/dist/ui/{chunk-XWX2622O.js → chunk-76PG55CM.js} +1 -1
  47. package/dist/ui/chunk-AAR7DBEN.js +110 -0
  48. package/dist/ui/{chunk-JS6TK4OP.js → chunk-ATTW36EM.js} +1 -1
  49. package/dist/ui/chunk-AZ2SK2ZK.js +1 -0
  50. package/dist/ui/chunk-CSUKZZK5.js +1 -0
  51. package/dist/ui/chunk-DZYYWCHX.js +3 -0
  52. package/dist/ui/{chunk-NSOTG64N.js → chunk-EIMRPYCD.js} +1 -1
  53. package/dist/ui/{chunk-HARRKJFZ.js → chunk-ELKXEMOU.js} +1 -1
  54. package/dist/ui/{chunk-LROR6N3B.js → chunk-ETPZ3TXX.js} +2 -2
  55. package/dist/ui/{chunk-LROR6N3B.js.LEGAL.txt → chunk-ETPZ3TXX.js.LEGAL.txt} +1 -0
  56. package/dist/ui/{chunk-J5E457OI.js → chunk-EYVQCOAD.js} +1 -1
  57. package/dist/ui/{chunk-Q7IU4QCY.js → chunk-F4F5MH7A.js} +1 -1
  58. package/dist/ui/chunk-FF4CLAZS.js +1 -0
  59. package/dist/ui/{chunk-W6LP22IK.js → chunk-GCMX5V3P.js} +1 -1
  60. package/dist/ui/{chunk-FJLNXXE3.js → chunk-H3PYPGNR.js} +1 -1
  61. package/dist/ui/{chunk-WFQU7V3W.js → chunk-HVYNDEA7.js} +1 -1
  62. package/dist/ui/{chunk-TVYAZN2Q.js → chunk-IMXZTYAA.js} +1 -1
  63. package/dist/ui/{chunk-YXSXWFLZ.js → chunk-J5KDO4RM.js} +1 -1
  64. package/dist/ui/chunk-JSWRHHTZ.js +1 -0
  65. package/dist/ui/{chunk-MSYTAJBV.js → chunk-K3UM5OQF.js} +1 -1
  66. package/dist/ui/{chunk-BW2MKGNP.js → chunk-KBJ4AK5O.js} +1 -1
  67. package/dist/ui/{chunk-4FGASIIX.js → chunk-L3O3CLWO.js} +1 -1
  68. package/dist/ui/{chunk-JTIQBW33.js → chunk-LC6WLVRS.js} +1 -1
  69. package/dist/ui/{chunk-VJUMFJIX.js → chunk-MAZJRNAL.js} +1 -1
  70. package/dist/ui/{chunk-3E6CASIN.js → chunk-MYKJ6VAM.js} +1 -1
  71. package/dist/ui/chunk-NEA3XU6N.js +19 -0
  72. package/dist/ui/{chunk-UUVAD4U4.js → chunk-PR7NB5VK.js} +1 -1
  73. package/dist/ui/{chunk-QF3GPZTI.js → chunk-QHOZXW7Y.js} +1 -1
  74. package/dist/ui/chunk-RN4XVVWD.js +1 -0
  75. package/dist/ui/{chunk-PTPYMVFW.js → chunk-SLLTYDVX.js} +1 -1
  76. package/dist/ui/chunk-SWTNOWNE.js +1 -0
  77. package/dist/ui/{chunk-JBHRZ4EX.js → chunk-SYDHFYRC.js} +1 -1
  78. package/dist/ui/{chunk-ZMS6NIGG.js → chunk-UHK5N75L.js} +1 -1
  79. package/dist/ui/chunk-V3G3554T.js +31 -0
  80. package/dist/ui/chunk-VB5VMEN7.js +1 -0
  81. package/dist/ui/{chunk-2EPQJUGM.js → chunk-VRRRFAIV.js} +1 -1
  82. package/dist/ui/{chunk-4ALFT4ES.js → chunk-W2ZAVRAT.js} +56 -56
  83. package/dist/ui/{chunk-P2Z66DHV.js → chunk-W3ICOHMF.js} +1 -1
  84. package/dist/ui/{chunk-ENQKSLGJ.js → chunk-WAT22UHZ.js} +1 -1
  85. package/dist/ui/chunk-WN7HUDGY.js +1 -0
  86. package/dist/ui/{chunk-BUNR5EU3.js → chunk-WS37MDM3.js} +1 -1
  87. package/dist/ui/chunk-XACLTYBB.js +5 -0
  88. package/dist/ui/chunk-XJUQOX4E.js +1 -0
  89. package/dist/ui/{chunk-QHJOTXYB.js → chunk-XM35ZJBQ.js} +1 -1
  90. package/dist/ui/{chunk-VD6G5NV4.js → chunk-XOWWPE7X.js} +12 -12
  91. package/dist/ui/chunk-YV5SGR24.js +1 -0
  92. package/dist/ui/ui.css +1 -1
  93. package/dist/ui/ui.js +1 -1
  94. package/package.json +3 -3
  95. package/dist/chunk-AJDMNTZQ.js +0 -187
  96. package/dist/chunk-X5CNR4CK.js +0 -1411
  97. package/dist/ui/AIPage-2N5UYBRX.js +0 -1
  98. package/dist/ui/CollectionPage-D7IG6526.js +0 -1
  99. package/dist/ui/ComparePage-726NIJW5.js +0 -1
  100. package/dist/ui/DataPage-YMSQCIVK.js +0 -1
  101. package/dist/ui/DataSourcePage-7VPFUG3K.js +0 -1
  102. package/dist/ui/DocTranslationsPage-FO7O76VK.js +0 -1
  103. package/dist/ui/DocumentPage-M7SJ4ITQ.js +0 -1
  104. package/dist/ui/EditDataSourcePage-Y7YK6IWT.js +0 -1
  105. package/dist/ui/EditReleasePage-XZ4RHHRT.js +0 -1
  106. package/dist/ui/EmbeddedAIPage-RJOUIPF2.js +0 -1
  107. package/dist/ui/LogsPage-IGDDZGEI.js +0 -1
  108. package/dist/ui/NewDataSourcePage-YUPRHL7F.js +0 -1
  109. package/dist/ui/NewReleasePage-6HHLYOQP.js +0 -1
  110. package/dist/ui/ReleasePage-V2JMPWX7.js +0 -1
  111. package/dist/ui/ReleasesPage-SCR4SVKN.js +0 -1
  112. package/dist/ui/SettingsPage-5XKHPG6K.js +0 -1
  113. package/dist/ui/TasksPage-DIXUWYXM.js +0 -1
  114. package/dist/ui/TranslationsArbPage-QPD3ZBT4.js +0 -2
  115. package/dist/ui/TranslationsEditPage-475Z7UPC.js +0 -1
  116. package/dist/ui/ai-tools-4DWMFQS6.js +0 -1
  117. package/dist/ui/chunk-2RAVKX2Z.js +0 -19
  118. package/dist/ui/chunk-2S5HMST6.js +0 -1
  119. package/dist/ui/chunk-344GZOGG.js +0 -84
  120. package/dist/ui/chunk-4ISFTE2G.js +0 -5
  121. package/dist/ui/chunk-AGFPVTBA.js +0 -1
  122. package/dist/ui/chunk-D3UIZHRK.js +0 -1
  123. package/dist/ui/chunk-HO3W4RVG.js +0 -1
  124. package/dist/ui/chunk-KRNQ6L35.js +0 -1
  125. package/dist/ui/chunk-N3JSLTMV.js +0 -1
  126. package/dist/ui/chunk-NWHGT476.js +0 -1
  127. package/dist/ui/chunk-RHAJNURN.js +0 -1
  128. package/dist/ui/chunk-SENZVSVZ.js +0 -1
  129. package/dist/ui/chunk-TOGLZDK3.js +0 -1
  130. package/dist/ui/chunk-TPJCPCRR.js +0 -1
  131. package/dist/ui/chunk-XMESZUC5.js +0 -1
  132. package/dist/ui/chunk-YMBBWVBB.js +0 -3
  133. /package/dist/ui/{ImageEditorDialog-GGS4VFEA.js.LEGAL.txt → ImageEditorDialog-Q44YTLB5.js.LEGAL.txt} +0 -0
  134. /package/dist/ui/{chunk-NJMNOITA.js.LEGAL.txt → chunk-5SY2SVEX.js.LEGAL.txt} +0 -0
  135. /package/dist/ui/{chunk-4ALFT4ES.js.LEGAL.txt → chunk-W2ZAVRAT.js.LEGAL.txt} +0 -0
  136. /package/dist/ui/{chunk-VD6G5NV4.js.LEGAL.txt → chunk-XOWWPE7X.js.LEGAL.txt} +0 -0
package/dist/plugin.js CHANGED
@@ -1,25 +1,22 @@
1
1
  import {
2
- ChatStore,
2
+ buildChatSystemPrompt,
3
+ buildEditSystemPrompt,
3
4
  findModel,
4
5
  generateAltText,
5
6
  generateImage,
6
7
  generatePublishMessage,
7
8
  getAiConfig,
8
9
  getServerVersion,
9
- mergeIncomingMessage,
10
10
  normalizeExecutionMode,
11
- runChatStream,
12
- runEditObjectStream,
11
+ serializeAiClientModel,
13
12
  serializeAiConfig,
14
13
  summarizeDiff,
15
14
  translateString
16
- } from "./chunk-X5CNR4CK.js";
15
+ } from "./chunk-MYN76V3L.js";
17
16
  import {
17
+ SearchIndexService,
18
18
  runCronJobs
19
- } from "./chunk-AJDMNTZQ.js";
20
- import {
21
- SearchIndexService
22
- } from "./chunk-4VZTYUQ7.js";
19
+ } from "./chunk-NC3CRIFA.js";
23
20
  import {
24
21
  RootCMSClient,
25
22
  parseDocId,
@@ -244,42 +241,6 @@ function isRecord(value) {
244
241
  function isDocVersion(value) {
245
242
  return value === "draft" || value === "published";
246
243
  }
247
- function withNoTransform(cacheControl) {
248
- const value = cacheControl?.trim() || "no-cache";
249
- if (/(?:^|,)\s*no-transform\s*(?:,|$)/i.test(value)) {
250
- return value;
251
- }
252
- return `${value}, no-transform`;
253
- }
254
- async function pipeWebResponse(webResponse, res) {
255
- res.status(webResponse.status);
256
- webResponse.headers.forEach((value, key) => {
257
- res.setHeader(key, value);
258
- });
259
- res.setHeader(
260
- "Cache-Control",
261
- withNoTransform(webResponse.headers.get("cache-control"))
262
- );
263
- res.setHeader("X-Accel-Buffering", "no");
264
- if (!webResponse.body) {
265
- res.end();
266
- return;
267
- }
268
- const reader = webResponse.body.getReader();
269
- try {
270
- let done = false;
271
- while (!done) {
272
- const next = await reader.read();
273
- done = next.done;
274
- if (next.value) {
275
- res.write(next.value);
276
- }
277
- }
278
- } finally {
279
- reader.releaseLock();
280
- res.end();
281
- }
282
- }
283
244
  function api(server, options) {
284
245
  async function getCollectionSchema(req, collectionId) {
285
246
  if (req.viteServer) {
@@ -677,9 +638,17 @@ function api(server, options) {
677
638
  res.status(200).json({ success: true, enabled: false });
678
639
  return;
679
640
  }
680
- res.status(200).json({ success: true, enabled: true, ...serializeAiConfig(aiConfig) });
641
+ const cmsClient = new RootCMSClient(req.rootConfig);
642
+ const role = await cmsClient.getUserRole(req.user.email).catch(() => null);
643
+ res.status(200).json({
644
+ success: true,
645
+ enabled: true,
646
+ ...serializeAiConfig(aiConfig),
647
+ role: role || null,
648
+ canAutoApply: role === "ADMIN" || role === "EDITOR"
649
+ });
681
650
  });
682
- server.use("/cms/api/ai.chat", async (req, res) => {
651
+ server.use("/cms/api/ai.chat.prepare", async (req, res) => {
683
652
  if (req.method !== "POST") {
684
653
  res.status(400).json({ success: false, error: "BAD_REQUEST" });
685
654
  return;
@@ -699,93 +668,44 @@ function api(server, options) {
699
668
  res.status(400).json({ success: false, error: "UNKNOWN_MODEL" });
700
669
  return;
701
670
  }
702
- const executionMode = normalizeExecutionMode(body.executionMode);
671
+ const requestedMode = normalizeExecutionMode(body.executionMode);
703
672
  const activeDocId = typeof body.docId === "string" && body.docId.trim() ? body.docId.trim() : void 0;
704
- const cmsClient = new RootCMSClient(req.rootConfig);
673
+ let canAutoApply = false;
705
674
  try {
675
+ const cmsClient = new RootCMSClient(req.rootConfig);
706
676
  const role = await cmsClient.getUserRole(req.user.email);
707
677
  if (!role) {
708
678
  res.status(403).json({ success: false, error: "FORBIDDEN" });
709
679
  return;
710
680
  }
711
- if (executionMode === "auto" && role !== "ADMIN" && role !== "EDITOR") {
712
- res.status(403).json({ success: false, error: "AUTO_MODE_REQUIRES_EDITOR" });
713
- return;
714
- }
681
+ canAutoApply = role === "ADMIN" || role === "EDITOR";
715
682
  } catch (err) {
716
683
  console.error("failed to resolve user role:", err);
717
684
  res.status(500).json({ success: false, error: "UNKNOWN" });
718
685
  return;
719
686
  }
720
- const store = new ChatStore(cmsClient, req.user.email);
721
- const requestedChatId = typeof body.chatId === "string" ? body.chatId.trim() : "";
722
- let chatId;
723
- let storedMessages = [];
724
- let existingTitle = "";
725
- if (requestedChatId) {
726
- const existing = await store.getChat(requestedChatId);
727
- if (existing) {
728
- chatId = existing.id;
729
- storedMessages = existing.messages || [];
730
- existingTitle = existing.title || "";
731
- } else {
732
- try {
733
- const created = await store.createChat({
734
- id: requestedChatId,
735
- modelId: model.id
736
- });
737
- chatId = created.id;
738
- } catch (err) {
739
- if (err?.code === 6) {
740
- res.status(409).json({ success: false, error: "CHAT_ID_CONFLICT" });
741
- return;
742
- }
743
- throw err;
744
- }
745
- }
746
- } else {
747
- const created = await store.createChat({ modelId: model.id });
748
- chatId = created.id;
749
- }
750
- let messages;
751
- if (body.message && typeof body.message === "object") {
752
- messages = mergeIncomingMessage(
753
- storedMessages,
754
- body.message
755
- );
756
- } else if (Array.isArray(body.messages) && body.messages.length > 0) {
757
- messages = body.messages;
758
- } else {
759
- res.status(400).json({ success: false, error: "MISSING_MESSAGES" });
760
- return;
761
- }
687
+ const executionMode = requestedMode === "auto" && !canAutoApply ? "approve" : requestedMode;
762
688
  try {
763
- const streamResponse = await runChatStream({
689
+ const system = await buildChatSystemPrompt({
764
690
  rootConfig: req.rootConfig,
765
- cmsClient,
766
691
  config: aiConfig,
767
- model,
768
- messages,
769
- chatId,
770
- user: req.user.email,
771
692
  executionMode,
772
- existingTitle,
773
- activeDocId,
774
- loadCollection: (collectionId) => getCollectionSchema(req, collectionId),
775
- loadAllCollections: async () => (await options.getRenderer(req)).getCollections()
693
+ activeDocId
694
+ });
695
+ res.status(200).json({
696
+ success: true,
697
+ model: serializeAiClientModel(model),
698
+ system,
699
+ executionMode,
700
+ canAutoApply,
701
+ maxSteps: aiConfig.maxSteps ?? 10
776
702
  });
777
- streamResponse.headers.set("x-root-cms-chat-id", chatId);
778
- await pipeWebResponse(streamResponse, res);
779
703
  } catch (err) {
780
704
  console.error(err.stack || err);
781
- if (!res.headersSent) {
782
- res.status(500).json({ success: false, error: err.message || "UNKNOWN" });
783
- } else {
784
- res.end();
785
- }
705
+ res.status(500).json({ success: false, error: err.message || "UNKNOWN" });
786
706
  }
787
707
  });
788
- server.use("/cms/api/ai.edit_object", async (req, res) => {
708
+ server.use("/cms/api/ai.edit.prepare", async (req, res) => {
789
709
  if (req.method !== "POST") {
790
710
  res.status(400).json({ success: false, error: "BAD_REQUEST" });
791
711
  return;
@@ -800,19 +720,14 @@ function api(server, options) {
800
720
  return;
801
721
  }
802
722
  const body = req.body || {};
803
- const messages = body.messages || [];
804
- if (!Array.isArray(messages) || messages.length === 0) {
805
- res.status(400).json({ success: false, error: "MISSING_MESSAGES" });
806
- return;
807
- }
808
723
  const model = findModel(aiConfig, body.modelId);
809
724
  if (!model) {
810
725
  res.status(400).json({ success: false, error: "UNKNOWN_MODEL" });
811
726
  return;
812
727
  }
813
- const editCmsClient = new RootCMSClient(req.rootConfig);
814
728
  try {
815
- const role = await editCmsClient.getUserRole(req.user.email);
729
+ const cmsClient = new RootCMSClient(req.rootConfig);
730
+ const role = await cmsClient.getUserRole(req.user.email);
816
731
  if (!role) {
817
732
  res.status(403).json({ success: false, error: "FORBIDDEN" });
818
733
  return;
@@ -823,102 +738,21 @@ function api(server, options) {
823
738
  return;
824
739
  }
825
740
  try {
826
- const streamResponse = await runEditObjectStream({
741
+ const system = await buildEditSystemPrompt({
827
742
  rootConfig: req.rootConfig,
828
- cmsClient: editCmsClient,
829
- user: req.user.email,
830
- config: aiConfig,
831
- model,
832
- messages,
833
- editData: body.editData,
834
- loadCollection: (collectionId) => getCollectionSchema(req, collectionId),
835
- loadAllCollections: async () => (await options.getRenderer(req)).getCollections()
743
+ editData: body.editData
836
744
  });
837
- await pipeWebResponse(streamResponse, res);
838
- } catch (err) {
839
- console.error(err.stack || err);
840
- if (!res.headersSent) {
841
- res.status(500).json({ success: false, error: err.message || "UNKNOWN" });
842
- } else {
843
- res.end();
844
- }
845
- }
846
- });
847
- server.use("/cms/api/ai.chats.list", async (req, res) => {
848
- if (!req.user?.email) {
849
- res.status(401).json({ success: false, error: "UNAUTHORIZED" });
850
- return;
851
- }
852
- const cmsClient = new RootCMSClient(req.rootConfig);
853
- const store = new ChatStore(cmsClient, req.user.email);
854
- try {
855
- const chats = await store.listChats({ limit: req.body?.limit });
856
745
  res.status(200).json({
857
746
  success: true,
858
- chats: chats.map((c) => ({
859
- id: c.id,
860
- title: c.title,
861
- modelId: c.modelId,
862
- createdAt: c.createdAt.toMillis(),
863
- modifiedAt: c.modifiedAt.toMillis()
864
- }))
747
+ model: serializeAiClientModel(model),
748
+ system,
749
+ maxSteps: aiConfig.maxSteps ?? 10
865
750
  });
866
751
  } catch (err) {
867
752
  console.error(err.stack || err);
868
- res.status(500).json({ success: false, error: "UNKNOWN" });
869
- }
870
- });
871
- server.use("/cms/api/ai.chats.get", async (req, res) => {
872
- if (!req.user?.email) {
873
- res.status(401).json({ success: false, error: "UNAUTHORIZED" });
874
- return;
875
- }
876
- const id = String(req.body?.id || "").trim();
877
- if (!id) {
878
- res.status(400).json({ success: false, error: "MISSING_ID" });
879
- return;
880
- }
881
- const cmsClient = new RootCMSClient(req.rootConfig);
882
- const store = new ChatStore(cmsClient, req.user.email);
883
- const chat = await store.getChat(id);
884
- if (!chat) {
885
- res.status(404).json({ success: false, error: "NOT_FOUND" });
886
- return;
753
+ res.status(500).json({ success: false, error: err.message || "UNKNOWN" });
887
754
  }
888
- res.status(200).json({
889
- success: true,
890
- chat: {
891
- id: chat.id,
892
- title: chat.title,
893
- modelId: chat.modelId,
894
- messages: chat.messages,
895
- createdAt: chat.createdAt.toMillis(),
896
- modifiedAt: chat.modifiedAt.toMillis()
897
- }
898
- });
899
755
  });
900
- server.use(
901
- "/cms/api/ai.chats.delete",
902
- async (req, res) => {
903
- if (req.method !== "POST") {
904
- res.status(400).json({ success: false, error: "BAD_REQUEST" });
905
- return;
906
- }
907
- if (!req.user?.email) {
908
- res.status(401).json({ success: false, error: "UNAUTHORIZED" });
909
- return;
910
- }
911
- const id = String(req.body?.id || "").trim();
912
- if (!id) {
913
- res.status(400).json({ success: false, error: "MISSING_ID" });
914
- return;
915
- }
916
- const cmsClient = new RootCMSClient(req.rootConfig);
917
- const store = new ChatStore(cmsClient, req.user.email);
918
- await store.deleteChat(id);
919
- res.status(200).json({ success: true });
920
- }
921
- );
922
756
  server.use("/cms/api/ai.translate", async (req, res) => {
923
757
  if (req.method !== "POST" || !String(req.get("content-type")).startsWith("application/json")) {
924
758
  res.status(400).json({ success: false, error: "BAD_REQUEST" });
@@ -1643,7 +1477,7 @@ function cmsPlugin(options) {
1643
1477
  */
1644
1478
  configureServer: async (server, serverOptions) => {
1645
1479
  server.use(
1646
- ["/cms/api/ai.chat", "/cms/api/ai.edit_object"],
1480
+ ["/cms/api/ai.chat.prepare", "/cms/api/ai.edit.prepare"],
1647
1481
  bodyParser.json({ limit: "4mb" })
1648
1482
  );
1649
1483
  server.use(bodyParser.json());
@@ -0,0 +1,110 @@
1
+ import { ImageModel, LanguageModel } from 'ai';
2
+ export type AiExecutionMode = 'read' | 'approve' | 'auto';
3
+ /**
4
+ * Provider type for an AI model. Use `openai-compatible` for any OpenAI-style
5
+ * endpoint (e.g. local Ollama, vLLM, OpenRouter).
6
+ */
7
+ export type AiProvider = 'openai' | 'openai-compatible' | 'anthropic' | 'google';
8
+ /** Capabilities advertised to the UI. */
9
+ export interface AiModelCapabilities {
10
+ /** Whether the model can call tools. Defaults to `true`. */
11
+ tools?: boolean;
12
+ /** Whether the model can stream reasoning/thinking. Defaults to `false`. */
13
+ reasoning?: boolean;
14
+ /** Whether the model accepts image attachments. Defaults to `false`. */
15
+ attachments?: boolean;
16
+ }
17
+ /**
18
+ * Configuration for a single chat model.
19
+ *
20
+ * Inspired by Ollama's `Modelfile` and LiteLLM's model config: each entry maps
21
+ * a CMS-facing id to a provider, model id and credentials.
22
+ */
23
+ export interface AiModelConfig {
24
+ /** Stable id used by the CMS UI and stored alongside chat history. */
25
+ id: string;
26
+ /** Optional human-readable label rendered in the model picker. */
27
+ label?: string;
28
+ /** Optional description shown under the label. */
29
+ description?: string;
30
+ /** AI provider/family to route requests to. */
31
+ provider: AiProvider;
32
+ /**
33
+ * Provider-specific model id (e.g. `gpt-4o`, `claude-opus-4-5`,
34
+ * `gemini-2.5-pro`). Defaults to `id` if omitted.
35
+ */
36
+ modelId?: string;
37
+ /** API key for the provider. */
38
+ apiKey?: string;
39
+ /** Override the provider's base URL (required for `openai-compatible`). */
40
+ baseURL?: string;
41
+ /** Custom headers to send with each request. */
42
+ headers?: Record<string, string>;
43
+ /** Capabilities advertised to the UI. */
44
+ capabilities?: AiModelCapabilities;
45
+ }
46
+ /**
47
+ * Full AI config registered on the cms plugin.
48
+ */
49
+ export interface AiConfig {
50
+ /** Models exposed in the model picker. The first entry is the default. */
51
+ models: AiModelConfig[];
52
+ /** Id of the default model. Defaults to the first model in `models`. */
53
+ defaultModel?: string;
54
+ /**
55
+ * Image generation models. Used by the image generator and any other
56
+ * features that produce images. Only the `openai` and `google` providers
57
+ * support image generation.
58
+ */
59
+ imageModels?: AiModelConfig[];
60
+ /** Id of the default image model. Defaults to the first entry in `imageModels`. */
61
+ defaultImageModel?: string;
62
+ /**
63
+ * Optional system prompt prepended to every conversation. If a `ROOT.md`
64
+ * file exists at the project root, its contents are appended to this
65
+ * prompt automatically.
66
+ */
67
+ systemPrompt?: string;
68
+ /** Maximum tool-loop steps before stopping. Defaults to 10. */
69
+ maxSteps?: number;
70
+ }
71
+ /**
72
+ * A chat model's connection config serialized for the browser, INCLUDING the
73
+ * API key, so the client can build a `LanguageModel` for direct
74
+ * browser-to-provider streaming. Returned only from authenticated endpoints
75
+ * (see `serializeAiClientModel` in `core/ai.ts`).
76
+ */
77
+ export interface SerializedClientModel {
78
+ id: string;
79
+ label: string;
80
+ description?: string;
81
+ provider: AiProvider;
82
+ modelId: string;
83
+ apiKey?: string;
84
+ baseURL?: string;
85
+ headers?: Record<string, string>;
86
+ capabilities: {
87
+ tools: boolean;
88
+ reasoning: boolean;
89
+ attachments: boolean;
90
+ };
91
+ }
92
+ /**
93
+ * Anthropic blocks direct browser requests by default. Sending this header
94
+ * opts in to the browser-CORS behavior so the `/cms/ai` chat can call the
95
+ * Anthropic API straight from the client. OpenAI and Google's
96
+ * `generativelanguage` endpoint already allow browser requests with an API
97
+ * key, so no equivalent header is required for them.
98
+ */
99
+ export declare const ANTHROPIC_BROWSER_ACCESS_HEADER = "anthropic-dangerous-direct-browser-access";
100
+ /**
101
+ * Returns a copy of `model` with any provider-specific headers required for
102
+ * direct browser-to-provider calls. Used when the AI SDK runs in the browser
103
+ * (client-side streaming) rather than on the server.
104
+ */
105
+ export declare function withBrowserHeaders(model: AiModelConfig): AiModelConfig;
106
+ /** Resolves an `AiModelConfig` to an AI SDK `LanguageModel` instance. */
107
+ export declare function resolveLanguageModel(model: AiModelConfig): LanguageModel;
108
+ /** Resolves an `AiModelConfig` to an AI SDK `ImageModel` instance. */
109
+ export declare function resolveImageModel(model: AiModelConfig): ImageModel;
110
+ export declare function normalizeExecutionMode(value: unknown): AiExecutionMode;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Pure, browser-safe helpers for the Root AI chat: deriving/sanitizing chat
3
+ * titles, healing abandoned tool calls, and stripping `undefined` before
4
+ * persisting to Firestore.
5
+ *
6
+ * These were previously defined in `core/ai.ts` (server-only). They moved here
7
+ * so the browser can reuse them now that the `/cms/ai` chat streams directly
8
+ * from the client. `core/ai.ts` re-exports them for back-compat and tests.
9
+ *
10
+ * Keep this module free of Node-only and `firebase-admin` imports.
11
+ */
12
+ import type { UIMessage } from 'ai';
13
+ /** Derives a short title from the first user message (used as fallback). */
14
+ export declare function deriveChatTitle(messages: UIMessage[]): string;
15
+ /**
16
+ * Extracts the first user turn and first assistant text turn as a plain-text
17
+ * transcript, suitable for the title-generation prompt. Skips tool-call
18
+ * parts, reasoning chunks, and attachments so the model sees the
19
+ * conversational substance and nothing else.
20
+ *
21
+ * Only the opening exchange is included on purpose — the chat title should
22
+ * describe what the conversation is *about*, anchored on the user's initial
23
+ * ask, not drift as later follow-ups arrive.
24
+ */
25
+ export declare function buildTitlePromptContext(messages: UIMessage[]): string;
26
+ /**
27
+ * Strips the cruft LLMs commonly emit around a title — leading "Title:"
28
+ * preamble, surrounding quotes, trailing punctuation, internal newlines —
29
+ * and enforces a hard length cap.
30
+ */
31
+ export declare function sanitizeGeneratedTitle(raw: string): string;
32
+ /** System prompt used to generate a short summary title for a chat. */
33
+ export declare const TITLE_GENERATION_SYSTEM_PROMPT: string;
34
+ /** Builds the user prompt for title generation from the opening exchange. */
35
+ export declare function buildTitlePrompt(context: string): string;
36
+ /**
37
+ * Synthesizes an aborted result for any tool-call part still awaiting one, so
38
+ * every `tool_use` keeps a matching `tool_result` when the history is handed
39
+ * to the model. Left unresolved, such parts (from an abandoned turn) make the
40
+ * provider reject the next request. Returns the original array unchanged when
41
+ * there is nothing to repair.
42
+ */
43
+ export declare function sanitizeDanglingToolCalls(messages: UIMessage[]): UIMessage[];
44
+ /**
45
+ * Merges the latest incoming message into a persisted chat history. Replaces a
46
+ * matched entry (and drops anything after it) so message ids stay unique and
47
+ * tool-call/result pairing stays intact; a genuinely new id is appended.
48
+ */
49
+ export declare function mergeIncomingMessage(stored: UIMessage[], incoming: UIMessage): UIMessage[];
50
+ /**
51
+ * Recursively removes `undefined` values from an object/array. Returns a new
52
+ * structure; the input is not mutated. Used to clean payloads before sending
53
+ * them to Firestore, which rejects `undefined` outright.
54
+ */
55
+ export declare function stripUndefined<T>(value: T): T;
56
+ /**
57
+ * Extracts JSON from an AI response that may contain markdown code blocks.
58
+ */
59
+ export declare function extractJsonFromResponse(responseText: string): string;
@@ -0,0 +1 @@
1
+ import{a}from"./chunk-NEA3XU6N.js";import"./chunk-V3G3554T.js";import"./chunk-VRRRFAIV.js";import"./chunk-5SY2SVEX.js";import"./chunk-AAR7DBEN.js";import"./chunk-YDO2EDVY.js";import"./chunk-5MV6XVGA.js";import{a as i}from"./chunk-W3ICOHMF.js";import{b as o}from"./chunk-4N6CPIOQ.js";import"./chunk-AAHM6RYJ.js";import"./chunk-YV5SGR24.js";import"./chunk-DZYYWCHX.js";import"./chunk-XOWWPE7X.js";import"./chunk-WS37MDM3.js";import{Ga as t}from"./chunk-ETPZ3TXX.js";import"./chunk-KUCVLANF.js";import"./chunk-7SK47HHZ.js";import"./chunk-DTMZLUNY.js";import"./chunk-PVAE643M.js";function n(r){return i("AI"),t(o,{children:t(a,{variant:"page",initialChatId:r.chatId})})}export{n as AIPage};
@@ -1 +1 @@
1
- import{a as p}from"./chunk-W6LP22IK.js";import{a as i}from"./chunk-XMESZUC5.js";import"./chunk-ENQKSLGJ.js";import"./chunk-CAY5NM4Z.js";import{a as n}from"./chunk-MSYTAJBV.js";import"./chunk-AGFPVTBA.js";import"./chunk-YMBBWVBB.js";import"./chunk-W5DOO6ID.js";import"./chunk-NSOTG64N.js";import"./chunk-J5E457OI.js";import"./chunk-QHJOTXYB.js";import{a as d}from"./chunk-BW2MKGNP.js";import"./chunk-KRNQ6L35.js";import{a as g}from"./chunk-P2Z66DHV.js";import{b as m}from"./chunk-WMICEDOT.js";import"./chunk-AAHM6RYJ.js";import"./chunk-VD6G5NV4.js";import{d as o}from"./chunk-BUNR5EU3.js";import{Ga as e,a as t,c as r,j as h}from"./chunk-LROR6N3B.js";import"./chunk-KUCVLANF.js";import"./chunk-7SK47HHZ.js";import"./chunk-DTMZLUNY.js";import"./chunk-PVAE643M.js";h();function k(){g("Asset Library");let[l,u]=p("folder",""),{query:c,route:f}=o(),[a]=t(c.asset||"");return r(()=>{if(a){let s=new URL(window.location.href);s.searchParams.delete("asset"),f(s.pathname+s.search,!0)}},[]),e(m,{children:e("div",{className:"AssetsPage","data-testid":"assets-page",children:[e("div",{className:"AssetsPage__header",children:[e(n,{size:"h1",children:"Asset Library"}),e(d,{as:"p",children:"The asset library can be used to store and organize files used throughout the project. Replacing a file in the asset library automatically updates all draft docs that use it (requires re-publishing)."})]}),e(i,{className:"AssetsPage__browser",mode:"manage",folder:l,onFolderChange:u,initialAssetId:a})]})})}export{k as AssetsPage};
1
+ import{a as p}from"./chunk-GCMX5V3P.js";import{a as i}from"./chunk-WN7HUDGY.js";import"./chunk-FF4CLAZS.js";import"./chunk-WAT22UHZ.js";import"./chunk-74OB6FM4.js";import{a as n}from"./chunk-K3UM5OQF.js";import"./chunk-W5DOO6ID.js";import"./chunk-EIMRPYCD.js";import"./chunk-EYVQCOAD.js";import"./chunk-XM35ZJBQ.js";import{a as d}from"./chunk-KBJ4AK5O.js";import{a as g}from"./chunk-W3ICOHMF.js";import{b as m}from"./chunk-4N6CPIOQ.js";import"./chunk-AAHM6RYJ.js";import"./chunk-YV5SGR24.js";import"./chunk-DZYYWCHX.js";import"./chunk-XOWWPE7X.js";import{d as o}from"./chunk-WS37MDM3.js";import{Ga as e,a as t,c as r,j as h}from"./chunk-ETPZ3TXX.js";import"./chunk-KUCVLANF.js";import"./chunk-7SK47HHZ.js";import"./chunk-DTMZLUNY.js";import"./chunk-PVAE643M.js";h();function k(){g("Asset Library");let[l,u]=p("folder",""),{query:c,route:f}=o(),[a]=t(c.asset||"");return r(()=>{if(a){let s=new URL(window.location.href);s.searchParams.delete("asset"),f(s.pathname+s.search,!0)}},[]),e(m,{children:e("div",{className:"AssetsPage","data-testid":"assets-page",children:[e("div",{className:"AssetsPage__header",children:[e(n,{size:"h1",children:"Asset Library"}),e(d,{as:"p",children:"The asset library can be used to store and organize files used throughout the project. Replacing a file in the asset library automatically updates all draft docs that use it (requires re-publishing)."})]}),e(i,{className:"AssetsPage__browser",mode:"manage",folder:l,onFolderChange:u,initialAssetId:a})]})})}export{k as AssetsPage};
@@ -0,0 +1 @@
1
+ import{a as j}from"./chunk-L3O3CLWO.js";import{a as U}from"./chunk-SWTNOWNE.js";import{h as B}from"./chunk-F4F5MH7A.js";import{a as z,e as A,j as E}from"./chunk-J5KDO4RM.js";import"./chunk-LC6WLVRS.js";import{c as O,e as I}from"./chunk-SLLTYDVX.js";import"./chunk-CSUKZZK5.js";import{a as w}from"./chunk-H3PYPGNR.js";import"./chunk-JDW4UORS.js";import{a as P}from"./chunk-6P75AZ3B.js";import"./chunk-RN4XVVWD.js";import"./chunk-ELKXEMOU.js";import"./chunk-ATTW36EM.js";import"./chunk-YDO2EDVY.js";import{b as h}from"./chunk-5MV6XVGA.js";import{b as S}from"./chunk-W5DOO6ID.js";import{a as D}from"./chunk-EIMRPYCD.js";import"./chunk-EYVQCOAD.js";import"./chunk-XM35ZJBQ.js";import"./chunk-KBJ4AK5O.js";import{a as $}from"./chunk-W3ICOHMF.js";import{b as R}from"./chunk-4N6CPIOQ.js";import"./chunk-AAHM6RYJ.js";import"./chunk-YV5SGR24.js";import"./chunk-DZYYWCHX.js";import"./chunk-XOWWPE7X.js";import{d as k}from"./chunk-WS37MDM3.js";import{Ca as L,Ga as o,V as y,a as x,c as T,cb as u,da as C,j as M,za as N}from"./chunk-ETPZ3TXX.js";import{d as f}from"./chunk-KUCVLANF.js";import"./chunk-7SK47HHZ.js";import"./chunk-DTMZLUNY.js";import"./chunk-PVAE643M.js";M();function p(e){let{route:i}=k(),l=window.__ROOT_CTX.rootConfig.projectId;$(e.collection?e.collection:"Content"),T(()=>{let a=window.__ROOT_CTX.collections,s=`root-cms::${l}::last_collection`;if(e.collection)window.localStorage.setItem(s,e.collection);else{let t=window.localStorage.getItem(s);if(t&&a[t]){i(`/cms/content/${t}`);return}if(window.__ROOT_CTX.collections.Pages){i("/cms/content/Pages");return}let _=Object.keys(window.__ROOT_CTX.collections)||[];if(_.length>0){let d=_[0];i(`/cms/content/${d}`);return}console.warn("no collections")}},[e.collection,l]);let n=window.__ROOT_CTX.collections;return o(R,{children:o("div",{className:"CollectionPage",children:o("div",{className:"CollectionPage__layout",children:[o("div",{className:"CollectionPage__side",children:o(j,{collections:n,activeCollectionId:e.collection,projectId:l})}),o("div",{className:"CollectionPage__main",children:e.collection&&o(p.Collection,{collection:e.collection},e.collection)})]})})})}p.Collection=e=>{let{roles:i}=D(),l=window.firebase.user.email||"",n=S(i,l),[a,s]=w(`root::CollectionPage:${e.collection}:orderBy`,"modifiedAt"),[t,_]=w(`root::CollectionPage:${e.collection}:showArchived`,!1),[d,r]=x(!1),m=window.__ROOT_CTX.collections[e.collection];if(!m)return route("/cms/content"),o(f,{});let v=[{value:"slug",label:"A-Z"},{value:"slugDesc",label:"Z-A"},{value:"newest",label:"Newest"},{value:"oldest",label:"Oldest"},{value:"modifiedAt",label:"Last modified"},...m.sortOptions?.map(g=>({value:g.id,label:g.label}))||[]],[c,X,b]=A(e.collection,{orderBy:a,includeArchived:t});return o(f,{children:[o(E,{collection:e.collection,opened:d,onClose:()=>r(!1)}),o("div",{className:"CollectionPage__collection",children:o("div",{className:"CollectionPage__collection__docsTab",children:[!c&&o("div",{className:"CollectionPage__collection__docsTab__header",children:[o("div",{className:"CollectionPage__collection__docsTab__header__title",children:m.name||e.collection}),o("div",{className:"CollectionPage__collection__docsTab__controls",children:[o("div",{className:"CollectionPage__collection__docsTab__controls__showArchived",children:o(L,{size:"sm",color:"dark",label:"Show archived:",checked:t,onChange:g=>_(!!g.currentTarget.checked)})}),o("div",{className:"CollectionPage__collection__docsTab__controls__sort",children:[o("div",{className:"CollectionPage__collection__docsTab__controls__sort__label",children:"Sort:"}),o(N,{size:"xs",value:a,onChange:g=>s(g||"modifiedAt"),data:v})]}),o("div",{className:"CollectionPage__collection__docsTab__controls__newDoc",children:o(P,{label:"You don't have access to create new documents",condition:!n,children:o(C,{color:"dark",size:"xs",leftIcon:o(u,{size:16}),onClick:()=>r(!0),disabled:!n,children:"New"})})})]})]}),o(U,{className:"CollectionPage__collection__docsTab__content",children:c?o("div",{className:"CollectionPage__collection__docsTab__content__loading",children:o(y,{color:"gray",size:"xl"})}):b.length===0?o("div",{class:"CollectionPage__collection__docsEmpty",children:[o("div",{class:"CollectionPage__collection__docsEmpty__title",children:"Collection is empty."}),o("div",{class:"CollectionPage__collection__docsEmpty__button",children:o(P,{label:"You don't have access to create new documents",condition:!n,children:o(C,{color:"dark",size:"xs",leftIcon:o(u,{size:16}),onClick:()=>r(!0),disabled:!n,children:"New"})})})]}):o(p.DocsList,{collection:e.collection,docs:b,reloadDocs:()=>X()})})]})})]})};p.DocsList=e=>{let i=e.collection,l=window.__ROOT_CTX.collections[e.collection];if(!l)throw new Error(`could not find collection: ${i}`);let n=!!l.url,a=e.docs||[];function s(t){return n?z({collectionId:i,slug:t}):""}return o("div",{className:"CollectionPage__collection__docsList",children:a.map(t=>{let _=`/cms/content/${i}/${t.slug}`,d=s(t.slug),r=t.fields||{},m=h(r,l.preview?.title||"meta.title"),v=h(r,l.preview?.image||"meta.image")||l.preview?.defaultImage;return o("div",{className:"CollectionPage__collection__docsList__doc",children:[o("div",{className:"CollectionPage__collection__docsList__doc__image",children:o("a",{href:_,children:o(I,{file:v,width:120,height:90,withPlaceholder:!v?.src})})}),o("a",{className:"CollectionPage__collection__docsList__doc__content",href:_,children:[o("div",{className:"CollectionPage__collection__docsList__doc__content__header",children:[o("div",{className:"CollectionPage__collection__docsList__doc__content__header__docId",children:t.id}),o(O,{doc:t})]}),o("div",{className:"CollectionPage__collection__docsList__doc__content__title",children:m||"[UNTITLED]"}),n&&d&&o("div",{className:"CollectionPage__collection__docsList__doc__content__url",children:d})]}),o("div",{className:"CollectionPage__collection__docsList__doc__controls",children:o(B,{docId:t.id,data:t,onAction:c=>{(c.action==="archive"||c.action==="copy"||c.action==="unarchive"||c.action==="delete"||c.action==="unpublish"||c.action==="locked"||c.action==="unlocked")&&e.reloadDocs()}})})]},t.id)})})};export{p as CollectionPage};
@@ -0,0 +1 @@
1
+ import{b as s}from"./chunk-3WQCVKK4.js";import"./chunk-VRRRFAIV.js";import"./chunk-5SY2SVEX.js";import"./chunk-5MV6XVGA.js";import"./chunk-KBJ4AK5O.js";import{a as m}from"./chunk-W3ICOHMF.js";import{b as d}from"./chunk-4N6CPIOQ.js";import"./chunk-AAHM6RYJ.js";import"./chunk-YV5SGR24.js";import"./chunk-DZYYWCHX.js";import"./chunk-XOWWPE7X.js";import{d as t}from"./chunk-WS37MDM3.js";import{Ga as a,g as i,j as p}from"./chunk-ETPZ3TXX.js";import"./chunk-KUCVLANF.js";import"./chunk-7SK47HHZ.js";import"./chunk-DTMZLUNY.js";import"./chunk-PVAE643M.js";p();function h(){let{query:r}=t();m("Compare");let o=i(()=>n(r.left||""),[r.left]),e=i(()=>n(r.right||""),[r.right]);return a(d,{children:a("div",{className:"ComparePage",children:o&&e?a(s,{className:"ComparePage__diff",left:o,right:e}):a("div",{className:"ComparePage__error",children:"Invalid params, double check query params."})})})}function n(r){let[o,e]=String(r).split("@");return o&&e?{docId:o,versionId:e}:null}export{h as ComparePage};
@@ -0,0 +1 @@
1
+ import{a as m}from"./chunk-JSWRHHTZ.js";import"./chunk-MYKJ6VAM.js";import{a as y}from"./chunk-K3UM5OQF.js";import{a as k}from"./chunk-SWTNOWNE.js";import{a as x}from"./chunk-H3PYPGNR.js";import{a as w}from"./chunk-6P75AZ3B.js";import{b as v}from"./chunk-RN4XVVWD.js";import"./chunk-ATTW36EM.js";import{b as P}from"./chunk-W5DOO6ID.js";import{a as T}from"./chunk-EIMRPYCD.js";import"./chunk-XM35ZJBQ.js";import{a as p}from"./chunk-KBJ4AK5O.js";import{a as B}from"./chunk-W3ICOHMF.js";import{b as A}from"./chunk-4N6CPIOQ.js";import"./chunk-AAHM6RYJ.js";import{z as u}from"./chunk-XOWWPE7X.js";import"./chunk-WS37MDM3.js";import{Ca as b,Da as D,Fa as i,Ga as e,Nc as S,V as g,W as f,a as d,ba as s,c as h,da as _,g as c,j as G}from"./chunk-ETPZ3TXX.js";import"./chunk-KUCVLANF.js";import"./chunk-DTMZLUNY.js";import"./chunk-PVAE643M.js";G();var z={transition:"pop"};function O(a){let o=a.dataSource;return N(o.archivedAt)?e(i,{...z,label:`Archived ${L(o.archivedAt)} by ${o.archivedBy}`,children:e(s,{size:"xs",color:"gray",variant:"filled",children:"Archived"})}):N(o.publishedAt)?e(i,{...z,label:`Published ${L(o.publishedAt)} by ${o.publishedBy}`,children:e(s,{size:"xs",variant:"gradient",gradient:{from:"teal",to:"lime",deg:105},children:"Published"})}):e(s,{size:"xs",variant:"gradient",gradient:{from:"indigo",to:"cyan"},children:"Unpublished"})}function N(a){return!!(a&&a.toMillis)}function L(a){return a?.toMillis?u(a.toMillis()):u(new Date().getTime())}function E(){B("Data Sources");let{roles:a}=T(),o=window.firebase.user.email||"",r=P(a,o);return e(A,{children:e("div",{className:"DataPage",children:[e("div",{className:"DataPage__header",children:[e(y,{size:"h1",children:"Data Sources"}),e(p,{as:"p",children:"Add data sources to sync data from external services, like Google Sheets."}),e("div",{className:"DataPage__header__buttons",children:e(w,{label:"You don't have access to create new data sources",condition:!r,children:e(_,{component:"a",color:"blue",size:"xs",href:"/cms/data/new",disabled:!r,style:r?void 0:{pointerEvents:"none"},children:"New data source"})})})]}),e(E.DataSourcesTable,{})]})})}E.DataSourcesTable=()=>{let[a,o]=d(!0),[r,I]=d([]),[l,$]=x("root::DataPage:showArchived",!1),C=t=>!!t&&t.startsWith("https://docs.google.com/spreadsheets/");async function M(){let t=await v();I(t),o(!1)}let H=c(()=>r.some(t=>!!t.archivedAt),[r]),n=c(()=>r.filter(t=>{let U=!!t.archivedAt;return l?!0:!U}),[r,l]);return h(()=>{M()},[]),e("div",{className:"DataPage__DataSourcesTable",children:[!a&&H&&e("div",{className:"DataPage__DataSourcesTable__filters",children:e(b,{size:"sm",color:"dark",label:"Show archived",checked:l,onChange:t=>$(!!t.currentTarget.checked)})}),e(k,{children:[a&&e(g,{color:"gray",size:"xl"}),!a&&n.length>0&&e(D,{verticalSpacing:"xs",striped:!0,highlightOnHover:!0,fontSize:"xs",children:[e("thead",{children:e("tr",{children:[e("th",{children:"id"}),e("th",{children:"description"}),e("th",{children:"type"}),e("th",{children:"url"}),e("th",{children:"status"}),e("th",{children:"last synced"}),e("th",{children:"last published"})]})}),e("tbody",{children:n.map(t=>e("tr",{children:[e("td",{children:e("a",{href:`/cms/data/${t.id}`,children:t.id})}),e("td",{children:t.description||""}),e("td",{children:t.type}),e("td",{children:C(t.url)?e("div",{className:"DataPage__DataSourcesTable__url",children:[e(i,{label:"Open spreadsheet",children:e(f,{component:"a",href:t.url,target:"_blank",rel:"noreferrer",variant:"filled",color:"green",size:"sm","aria-label":"Open spreadsheet",children:e(S,{size:16,stroke:"2.25"})})}),e("a",{className:"DataPage__DataSourcesTable__url__text",href:t.url,target:"_blank",rel:"noreferrer",children:t.url})]}):t.url||""}),e("td",{children:e(O,{dataSource:t})}),e("td",{children:e(m,{dataSource:t,action:"sync"})}),e("td",{children:e(m,{dataSource:t,action:"publish"})})]},t.id))})]}),!a&&n.length===0&&e(p,{as:"p",children:"No data sources found for this filter."})]})]})};export{E as DataPage};
@@ -0,0 +1 @@
1
+ import{a as f}from"./chunk-JSWRHHTZ.js";import"./chunk-MYKJ6VAM.js";import{a as g}from"./chunk-K3UM5OQF.js";import{a as h}from"./chunk-SWTNOWNE.js";import"./chunk-6P75AZ3B.js";import{c as T,d as N}from"./chunk-RN4XVVWD.js";import"./chunk-ATTW36EM.js";import"./chunk-W5DOO6ID.js";import"./chunk-EIMRPYCD.js";import"./chunk-XM35ZJBQ.js";import{a as A}from"./chunk-KBJ4AK5O.js";import{a as H}from"./chunk-W3ICOHMF.js";import{b as W}from"./chunk-4N6CPIOQ.js";import"./chunk-AAHM6RYJ.js";import"./chunk-XOWWPE7X.js";import"./chunk-WS37MDM3.js";import{Da as D,Fa as p,Ga as a,Gc as k,Nc as z,V as y,W as m,a as _,c as b,ca as v,da as P,j as B,na as x,oa as w}from"./chunk-ETPZ3TXX.js";import"./chunk-KUCVLANF.js";import"./chunk-DTMZLUNY.js";import"./chunk-PVAE643M.js";B();function r(t){H(`Data Source: ${t.id}`);let[o,l]=_(!0),[e,u]=_(null),[n,c]=_(null),i=t.id;async function d(){let s=await T(i);if(u(s),s){let I=await N(i,{mode:"draft"});c(I)}l(!1)}b(()=>{d()},[]);function S(s){s==="sync"&&d()}return a(W,{children:a("div",{className:"DataSourcePage",children:[a("div",{className:"DataSourcePage__header",children:[a(v,{className:"DataSourcePage__header__breadcrumbs",children:[a("a",{href:"/cms/data",children:"Data"}),a("div",{children:i})]}),a("div",{className:"DataSourcePage__header__titleWrap",children:[a(g,{size:"h1",children:["Data Source: ",i]}),a("div",{className:"DataSourcePage__header__controls",children:a(p,{label:"Configure",children:a(m,{component:"a",href:`/cms/data/${t.id}/edit`,children:a(k,{size:24,stroke:"1.5"})})})})]}),e?.description&&a(A,{as:"p",children:e.description})]}),e&&a(r.SyncStatus,{dataSource:e,onAction:S}),o?a(y,{color:"gray",size:"xl"}):e?a(r.DataSection,{dataSource:e,data:n}):a("div",{className:"DataSourcePage__notFound",children:"Not Found"})]})})}r.SyncStatus=t=>{let o=t.dataSource;return a("div",{className:"DataSourcePage__SyncStatus",children:[a(g,{size:"h2",children:"Status"}),a(h,{className:"DataSourcePage__SyncStatus__content",children:a(D,{verticalSpacing:"xs",striped:!0,highlightOnHover:!0,fontSize:"xs",children:[a("thead",{children:a("tr",{children:[a("th",{children:"last synced"}),a("th",{children:"last published"})]})}),a("tbody",{children:a("tr",{children:[a("td",{children:a(f,{dataSource:o,action:"sync",onAction:()=>{t.onAction&&t.onAction("sync")}})}),a("td",{children:a(f,{dataSource:o,action:"publish"})})]})})]})})]})};r.DataSection=t=>{let{data:o,headers:l}=t.data||{},e=t.dataSource;if(!o)return null;let u=e.dataFormat||"map";if(e.type==="gsheet"){let n=l,c=[];if(u==="array")c=o;else if(u==="map"){let i=o;if(!n){let d=new Set;i.forEach(S=>{for(let s in S)s&&d.add(s)}),n=Array.from(d)}i.forEach(d=>{c.push(n.map(S=>d[S]||""))})}return a(r.DataSectionWrap,{dataSource:e,children:a(r.DataTable,{headers:n,rows:c})})}return e.type==="http"?a(r.DataSectionWrap,{dataSource:e,children:a(r.DefaultDataDisplay,{data:o})}):(console.log(`unsupported data type: ${e.type}`),null)};r.DataSectionWrap=t=>a("div",{className:"DataSourcePage__DataSection",children:[a("div",{className:"DataSourcePage__DataSection__header",children:[a(g,{size:"h2",children:"Data"}),a("div",{className:"DataSourcePage__DataSection__header__actions",children:[t.dataSource.url?.startsWith("https://docs.google.com/spreadsheets/")&&a(p,{label:"Open spreadsheet",children:a(m,{component:"a",href:t.dataSource.url,target:"_blank",variant:"filled",color:"green",children:a(z,{size:16,stroke:"2.25"})})}),t.dataSource.previewUrl&&a(P,{component:"a",variant:"default",size:"xs",href:t.dataSource.previewUrl,target:"_blank",children:"Preview"})]})]}),a(h,{className:"DataSourcePage__DataSection__content",children:t.children})]});r.DataTable=t=>{let o=t.headers||[],l=t.rows||[];return a("div",{className:"DataSourcePage__DataTable",children:a(D,{className:"DataSourcePage__DataTable__table",verticalSpacing:"xs",striped:!0,highlightOnHover:!0,fontSize:"xs",children:[o.length>0&&a("thead",{children:a("tr",{children:o.map(e=>a("th",{children:e},e))})}),a("tbody",{children:l.map((e,u)=>a("tr",{children:e.map((n,c)=>a("td",{children:n},c))},u))})]})})};r.DefaultDataDisplay=t=>{let o=t.data;return typeof o=="object"?a(w,{value:JSON.stringify(o,null,2),minRows:20,maxRows:100}):a(x,{value:String(o),minRows:12,maxRows:100})};export{r as DataSourcePage};
@@ -0,0 +1 @@
1
+ import{a as sa}from"./chunk-MAZJRNAL.js";import{a as ta}from"./chunk-SYDHFYRC.js";import{c as ia}from"./chunk-GCMX5V3P.js";import{a as na}from"./chunk-K3UM5OQF.js";import"./chunk-ELKXEMOU.js";import{b as U}from"./chunk-ATTW36EM.js";import"./chunk-YDO2EDVY.js";import"./chunk-5MV6XVGA.js";import{b as G}from"./chunk-EYVQCOAD.js";import{a as O,b as j}from"./chunk-XM35ZJBQ.js";import"./chunk-KBJ4AK5O.js";import{a as ra}from"./chunk-W3ICOHMF.js";import{b as N}from"./chunk-4N6CPIOQ.js";import"./chunk-AAHM6RYJ.js";import{Q as J,X as K}from"./chunk-YV5SGR24.js";import{c as V,g as X,i as C,l as S,n as q}from"./chunk-DZYYWCHX.js";import"./chunk-XOWWPE7X.js";import{e as M}from"./chunk-WS37MDM3.js";import{Ab as Y,Cc as aa,Fa as P,Ga as a,Nc as ea,Rc as oa,V as B,W as k,a as r,c as w,ca as W,da as b,e as E,g as R,j as fa,kc as Z,qa as y,zb as Q}from"./chunk-ETPZ3TXX.js";import"./chunk-KUCVLANF.js";import"./chunk-7SK47HHZ.js";import"./chunk-DTMZLUNY.js";import"./chunk-PVAE643M.js";fa();function u(e){ra(`Translations: ${e.collection}/${e.slug}`);let[g,l]=r(!0),[h,_]=r(!1),[o,t]=r([]),[s,c]=r({}),[L,la]=r(null),[ca,F]=r(!1),[I,z]=r({}),[da,H]=r(!1),p=e.collection,x=e.slug,i=`${p}/${x}`,ga=sa(),_a=window.__ROOT_CTX.rootConfig.i18n||{},D=S((_a.locales||[]).filter(n=>!C(n))),[$,A]=ia("locale",D);async function Ta(){try{let[n,d]=await Promise.all([ta(i),K(i)]),T=await Promise.all(n.map(f=>X(f))),m=await V(T);t(n),c(m),la(d),l(!1)}catch(n){console.error(n),l(!1),_(!0)}}if(w(()=>{Ta()},[]),h)return a(u.NotFound,{docId:i});function ua(n,d,T){F(!0),z(m=>{let f={...m},v=f[n]??{};return v.source=n,v[d]=T,f[n]=v,f})}async function ha(){H(!0),await G(async()=>{console.log("EditTranslationsModal.onSave()");let n=`edit-translations-${i}`;O({id:n,loading:!0,title:"Saving translations",message:`Updating for ${i}...`,autoClose:!1,disallowClose:!0});let d=Object.values(I);console.log(d),await J(i,d),j({id:n,title:"Saved translations",message:`Updated translations for ${i}!`,autoClose:!0}),z({}),F(!1)}),H(!1)}function ma(n){$.length===D.length?A([n]):A(D)}return a(N,{children:a("div",{className:"DocTranslationsPage",children:[a("div",{className:"DocTranslationsPage__header",children:[a(W,{className:"DocTranslationsPage__header__breadcrumbs",children:[a("a",{href:`/cms/content/${p}`,children:p}),a("a",{href:`/cms/content/${p}/${x}`,children:x}),a("div",{children:"translations"})]}),a("div",{className:"DocTranslationsPage__header__titleWrap",children:a(na,{size:"h1",children:["Translations: ",i]})}),a("div",{className:"DocTranslationsPage__header__controls",children:[L&&a("div",{className:"DocTranslationsPage__header__linkedSheet",children:a("div",{className:"DocTranslationsPage__header__linkedSheet__controls",children:[a(P,{label:"Open Google Sheet",children:a(k,{component:"a",href:U(L),target:"_blank",variant:"filled",color:"green",size:"sm",children:a(ea,{size:16,strokeWidth:2.25})})}),a(b,{variant:"default",size:"xs",leftIcon:a(Y,{size:16,strokeWidth:1.75}),children:"Import from Sheet"}),a(b,{variant:"default",size:"xs",leftIcon:a(Q,{size:16,strokeWidth:1.75}),children:"Export to Sheet"})]})}),a(y,{className:"DocTranslationsPage__toolsMenu",control:a(P,{label:"Tools",children:a(k,{variant:"default",children:a(oa,{size:16})})}),size:220,children:a(y.Item,{className:"DocTranslationsPage__toolsMenu__item",icon:a(aa,{size:16}),onClick:()=>ga.open({docId:i,sourceStrings:o,onPruned:n=>{c(d=>{let T={...d};return n.forEach(m=>delete T[m]),T})}}),children:"Prune unused strings"})})]})]}),a("div",{className:M("DocTranslationsPage__content",g&&"DocTranslationsPage__content--loading"),children:g?a(B,{color:"gray",size:"xl"}):a(u.Table,{locales:$,sourceStrings:o,translationsMap:s,onChange:ua,changesMap:I,onSelectLocale:n=>ma(n)})}),a("div",{className:"DocTranslationsPage__footer",children:a("div",{className:"DocTranslationsPage__footer__buttons",children:a(b,{variant:"filled",color:"dark",disabled:!ca,loading:da,onClick:()=>ha(),children:"Save"})})})]})})}u.NotFound=e=>a(N,{children:a("div",{className:"DocTranslationsPage DocTranslationsPage--error",children:a("div",{className:"DocTranslationsPage__error",children:[a("div",{className:"DocTranslationsPage__error__icon",children:a(Z,{size:60})}),a("div",{className:"DocTranslationsPage__error__title",children:["Not found: ",e.docId]})]})})});u.Table=e=>{let g=window.__ROOT_CTX.rootConfig.i18n||{},l=S((g.locales||[]).filter(o=>!C(o))),h=R(()=>{let o={};return Object.values(e.translationsMap).forEach(t=>{o[t.source]=t}),o},[e.translationsMap]);function _(o,t){let s=h[o]||{};return{translation:q(s,t),hasChanges:!1}}return a("div",{className:"DocTranslationsPage__TableWrap",children:a("table",{className:"DocTranslationsPage__Table",children:[a("thead",{children:a("tr",{children:[a("th",{className:"DocTranslationsPage__Table__sourceCellHeader",children:a("div",{className:"DocTranslationsPage__Table__headerLabel",children:"source"})}),e.locales.map(o=>a("th",{onClick:()=>e.onSelectLocale(o),className:"DocTranslationsPage__Table__localeHeader",children:a(P,{className:"DocTranslationsPage__Table__headerTooltip",placement:"start",withArrow:!0,label:e.locales.length===l.length?`Show only ${o}`:"Show all locales",children:a("div",{className:"DocTranslationsPage__Table__headerLabel",children:o})})},o))]})}),a("tbody",{children:e.sourceStrings.map(o=>a("tr",{children:[a("td",{className:"DocTranslationsPage__Table__sourceCellWrap",children:a("div",{className:"DocTranslationsPage__Table__sourceCell",children:o})}),e.locales.map(t=>{let s=_(o,t);return a("td",{children:a(u.Textarea,{source:o,locale:t,value:s.translation,onChange:e.onChange,changesMap:e.changesMap})},t)})]},o))})]})})};u.Textarea=e=>{let g=E(null),[l,h]=r(e.value),_=null;e.locale in(e.changesMap[e.source]||{})&&(_=e.changesMap[e.source][e.locale]);let o=_!==null&&_!==e.value;function t(){window.requestAnimationFrame(()=>{let s=g.current;s.style.minHeight="auto";let c=s.scrollHeight;s.style.minHeight=`${c}px`})}return w(()=>{t()},[l]),a("textarea",{ref:g,className:M("DocTranslationsPage__Textarea",o&&"DocTranslationsPage__Textarea--hasChanges",!l&&"DocTranslationsPage__Textarea--empty"),onFocus:()=>t(),rows:1,onChange:s=>{let c=s.target.value;h(c),e.onChange(e.source,e.locale,c)},value:l})};export{u as DocTranslationsPage};