@blinkk/root-cms 1.0.0-beta.24 → 1.0.0-beta.25

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.
package/dist/ui/ui.js CHANGED
@@ -38205,7 +38205,7 @@ ${this.customData.serverResponse}`;
38205
38205
  // package.json
38206
38206
  var package_default = {
38207
38207
  name: "@blinkk/root-cms",
38208
- version: "1.0.0-beta.24",
38208
+ version: "1.0.0-beta.25",
38209
38209
  author: "s@blinkk.com",
38210
38210
  license: "MIT",
38211
38211
  engines: {
@@ -38254,12 +38254,14 @@ ${this.customData.serverResponse}`;
38254
38254
  "body-parser": "^1.20.0",
38255
38255
  commander: "^10.0.0",
38256
38256
  "cookie-parser": "^1.4.6",
38257
+ "csv-parse": "^5.3.8",
38257
38258
  "csv-stringify": "^6.3.2",
38258
38259
  dotenv: "^16.0.0",
38259
38260
  "dts-dom": "^3.7.0",
38260
38261
  "firebase-admin": "^11.5.0",
38261
38262
  "js-beautify": "^1.14.7",
38262
38263
  kleur: "^4.1.5",
38264
+ multer: "1.4.5-lts.1",
38263
38265
  "preact-render-to-string": "^5.2.3",
38264
38266
  sirv: "^2.0.2",
38265
38267
  "tiny-glob": "^0.2.9"
@@ -38281,6 +38283,7 @@ ${this.customData.serverResponse}`;
38281
38283
  "@types/cookie-parser": "^1.4.3",
38282
38284
  "@types/jsonwebtoken": "^9.0.1",
38283
38285
  "@types/marked": "^4.0.8",
38286
+ "@types/multer": "^1.4.7",
38284
38287
  "@types/node": "^18.11.8",
38285
38288
  concurrently: "^7.6.0",
38286
38289
  esbuild: "0.17.12",
@@ -38296,7 +38299,7 @@ ${this.customData.serverResponse}`;
38296
38299
  vitest: "^0.18.1"
38297
38300
  },
38298
38301
  peerDependencies: {
38299
- "@blinkk/root": "1.0.0-beta.24",
38302
+ "@blinkk/root": "1.0.0-beta.25",
38300
38303
  "firebase-admin": ">=11",
38301
38304
  preact: "*"
38302
38305
  }
@@ -38417,6 +38420,19 @@ ${this.customData.serverResponse}`;
38417
38420
  ] }) });
38418
38421
  }
38419
38422
 
38423
+ // ui/utils/l10n.ts
38424
+ async function sourceHash(str) {
38425
+ return sha1(str);
38426
+ }
38427
+ async function sha1(str) {
38428
+ const encoder = new TextEncoder();
38429
+ const data = encoder.encode(str);
38430
+ const hash5 = await crypto.subtle.digest("SHA-1", data);
38431
+ const hashArray = Array.from(new Uint8Array(hash5));
38432
+ const hex = hashArray.map((b6) => b6.toString(16).padStart(2, "0")).join("");
38433
+ return hex;
38434
+ }
38435
+
38420
38436
  // ui/utils/doc.ts
38421
38437
  async function cmsDeleteDoc(docId) {
38422
38438
  const projectId = window.__ROOT_CTX.rootConfig.projectId;
@@ -38468,7 +38484,7 @@ ${this.customData.serverResponse}`;
38468
38484
  slug
38469
38485
  );
38470
38486
  await lf(db3, async (transaction) => {
38471
- const draftDoc = await Kl(draftDocRef);
38487
+ const draftDoc = await transaction.get(draftDocRef);
38472
38488
  if (!draftDoc.exists()) {
38473
38489
  throw new Error(`${draftDocRef.id} does not exist`);
38474
38490
  }
@@ -38484,6 +38500,114 @@ ${this.customData.serverResponse}`;
38484
38500
  transaction.delete(publishedDocRef);
38485
38501
  });
38486
38502
  }
38503
+ async function cmsCopyDoc(fromDocId, toDocId) {
38504
+ const fromDocRef = getDocRef(fromDocId);
38505
+ const fromDoc = await Kl(fromDocRef);
38506
+ if (!fromDoc.exists()) {
38507
+ throw new Error(`doc ${fromDocId} does not exist`);
38508
+ }
38509
+ const fields = fromDoc.data().fields ?? {};
38510
+ await cmsCreateDoc(toDocId, { fields });
38511
+ }
38512
+ async function cmsCreateDoc(docId, options2) {
38513
+ const [collectionId, slug] = docId.split("/");
38514
+ const docRef = getDocRef(docId);
38515
+ const doc = await Kl(docRef);
38516
+ if (doc.exists()) {
38517
+ throw new Error(`${docId} already exists`);
38518
+ }
38519
+ const data = {
38520
+ id: docId,
38521
+ collection: collectionId,
38522
+ slug,
38523
+ sys: {
38524
+ createdAt: df(),
38525
+ createdBy: window.firebase.user.email,
38526
+ modifiedAt: df(),
38527
+ modifiedBy: window.firebase.user.email
38528
+ },
38529
+ fields: options2?.fields ?? {}
38530
+ };
38531
+ await Jl(docRef, data);
38532
+ }
38533
+ async function cmsDocImportCsv(docId, csvData) {
38534
+ const translationsDocRef = getTranslationsDocRef(docId);
38535
+ const translationsMap = {};
38536
+ const i18nConfig = window.__ROOT_CTX.rootConfig.i18n || {};
38537
+ const i18nLocales = i18nConfig.locales || ["en"];
38538
+ function normalizeStr(str) {
38539
+ return String(str).trim();
38540
+ }
38541
+ function normalizeLocale(locale) {
38542
+ for (const l6 of i18nLocales) {
38543
+ if (String(l6).toLowerCase() === locale.toLowerCase()) {
38544
+ return l6;
38545
+ }
38546
+ }
38547
+ return locale;
38548
+ }
38549
+ for (const row of csvData) {
38550
+ if (!row.source) {
38551
+ continue;
38552
+ }
38553
+ const translation = {
38554
+ source: normalizeStr(row.source)
38555
+ };
38556
+ Object.entries(row).forEach(([column3, str]) => {
38557
+ if (column3 === "source") {
38558
+ return;
38559
+ }
38560
+ const locale = normalizeLocale(column3);
38561
+ translation[locale] = normalizeStr(str || "");
38562
+ });
38563
+ const hash5 = await sourceHash(translation.source);
38564
+ translationsMap[hash5] = translation;
38565
+ }
38566
+ const db3 = window.firebase.db;
38567
+ await lf(db3, async (transaction) => {
38568
+ const translationsDoc = await transaction.get(translationsDocRef);
38569
+ const currentData = translationsDoc.data() || {};
38570
+ const data = { ...currentData };
38571
+ data.sys = {
38572
+ ...data.sys ?? {},
38573
+ modifiedAt: df(),
38574
+ modifiedBy: window.firebase.user.email
38575
+ };
38576
+ data.translations = {
38577
+ ...data.translations ?? {},
38578
+ ...translationsMap
38579
+ };
38580
+ transaction.set(translationsDocRef, data);
38581
+ });
38582
+ }
38583
+ function getDocRef(docId) {
38584
+ const projectId = window.__ROOT_CTX.rootConfig.projectId;
38585
+ const db3 = window.firebase.db;
38586
+ const [collectionId, slug] = docId.split("/");
38587
+ return Za(
38588
+ db3,
38589
+ "Projects",
38590
+ projectId,
38591
+ "Collections",
38592
+ collectionId,
38593
+ "Drafts",
38594
+ slug
38595
+ );
38596
+ }
38597
+ function getTranslationsDocRef(docId) {
38598
+ const projectId = window.__ROOT_CTX.rootConfig.projectId;
38599
+ const db3 = window.firebase.db;
38600
+ const [collectionId, slug] = docId.split("/");
38601
+ return Za(
38602
+ db3,
38603
+ "Projects",
38604
+ projectId,
38605
+ "Collections",
38606
+ collectionId,
38607
+ "Translations",
38608
+ slug
38609
+ );
38610
+ }
38487
38611
 
38488
38612
  // ui/components/DocActionsMenu/DocActionsMenu.tsx
38489
38613
  function DocActionsMenu(props) {
@@ -38491,13 +38615,15 @@ ${this.customData.serverResponse}`;
38491
38615
  const data = props.data || {};
38492
38616
  const sys = data.sys || {};
38493
38617
  const modals = useModals();
38618
+ const theme = useMantineTheme();
38494
38619
  const onCopyDoc = () => {
38495
- if (props.onCopy) {
38496
- props.onCopy();
38497
- }
38498
- if (props.onAction) {
38499
- props.onAction({ action: "copy" });
38500
- }
38620
+ modals.openContextModal("copyDoc", {
38621
+ title: "Copy",
38622
+ overlayColor: theme.colorScheme === "dark" ? theme.colors.dark[9] : theme.colors.gray[2],
38623
+ innerProps: {
38624
+ fromDocId: docId
38625
+ }
38626
+ });
38501
38627
  };
38502
38628
  const onUnpublishDoc = () => {
38503
38629
  const notificationId = `unpublish-doc-${docId}`;
@@ -40837,12 +40963,106 @@ ${content}</tr>
40837
40963
  );
40838
40964
  }
40839
40965
 
40840
- // ui/components/NewDocModal/NewDocModal.tsx
40966
+ // ui/utils/slug.ts
40841
40967
  function isSlugValid(slug) {
40842
40968
  return Boolean(slug && slug.match(/^[a-z0-9]+(?:--?[a-z0-9]+)*$/));
40843
40969
  }
40844
40970
  function normalizeSlug(slug) {
40845
- return slug.replace(/^[\s/]*/g, "").replace(/[\s/]*$/g, "").toLowerCase().replaceAll("/", "--");
40971
+ return slug.replace(/^[\s/]*/g, "").replace(/[\s/]*$/g, "").replace(/^\/+|\/+$/g, "").toLowerCase().replaceAll("/", "--");
40972
+ }
40973
+
40974
+ // ui/components/SlugInput/SlugInput.tsx
40975
+ function SlugInput(props) {
40976
+ const [collectionId, setCollectionId] = y2(props.collectionId || "");
40977
+ const slugRef = A2(null);
40978
+ const [slug, setSlug] = y2("");
40979
+ const rootCollection = window.__ROOT_CTX.collections[collectionId];
40980
+ const collectionOptions = [];
40981
+ if (props.collectionId && props.collectionDisabled) {
40982
+ collectionOptions.push({
40983
+ value: props.collectionId,
40984
+ label: props.collectionId
40985
+ });
40986
+ } else {
40987
+ Object.keys(window.__ROOT_CTX.collections).forEach((id2) => {
40988
+ collectionOptions.push({
40989
+ value: id2,
40990
+ label: id2
40991
+ });
40992
+ });
40993
+ }
40994
+ const domain2 = window.__ROOT_CTX.rootConfig?.domain || "https://example.com";
40995
+ let urlHelp = "";
40996
+ if (rootCollection?.url) {
40997
+ if (slug) {
40998
+ const cleanSlug = normalizeSlug(slug);
40999
+ if (isSlugValid(cleanSlug)) {
41000
+ const cleanSlugPath = cleanSlug.replaceAll("--", "/");
41001
+ let urlPath = rootCollection.url.replace(/\[.*slug\]/, cleanSlugPath);
41002
+ if (urlPath === "/index") {
41003
+ urlPath = "/";
41004
+ }
41005
+ urlHelp = `${domain2}${urlPath}`;
41006
+ } else {
41007
+ urlHelp = "INVALID SLUG";
41008
+ }
41009
+ } else {
41010
+ urlHelp = `${domain2}${rootCollection.url}`;
41011
+ }
41012
+ }
41013
+ function onCollectionChange(newCollectionId) {
41014
+ setCollectionId(newCollectionId);
41015
+ if (props.onChange) {
41016
+ props.onChange({ collectionId: newCollectionId, slug });
41017
+ }
41018
+ }
41019
+ function onSlugChange(newSlug) {
41020
+ setSlug(newSlug);
41021
+ if (props.onChange) {
41022
+ props.onChange({ collectionId, slug: newSlug });
41023
+ }
41024
+ }
41025
+ return /* @__PURE__ */ o4("div", { className: joinClassNames(props.className, "SlugInput"), children: [
41026
+ /* @__PURE__ */ o4("div", { className: "SlugInput__inputs", children: [
41027
+ /* @__PURE__ */ o4(
41028
+ Select,
41029
+ {
41030
+ className: "SlugInput__collection",
41031
+ placeholder: "Collection",
41032
+ size: "xs",
41033
+ data: collectionOptions,
41034
+ onChange: (newValue) => onCollectionChange(newValue),
41035
+ value: collectionId,
41036
+ disabled: !!props.collectionDisabled
41037
+ }
41038
+ ),
41039
+ /* @__PURE__ */ o4("div", { className: "SlugInput__divider", children: "/" }),
41040
+ /* @__PURE__ */ o4(
41041
+ TextInput,
41042
+ {
41043
+ className: "SlugInput__slug",
41044
+ name: "slug",
41045
+ ref: slugRef,
41046
+ value: slug,
41047
+ onChange: (event) => {
41048
+ onSlugChange(event.currentTarget.value);
41049
+ },
41050
+ placeholder: "slug",
41051
+ autoComplete: "off",
41052
+ size: "xs"
41053
+ }
41054
+ )
41055
+ ] }),
41056
+ urlHelp && /* @__PURE__ */ o4(Text2, { className: "SlugInput__urlHelp", size: "body-sm", children: urlHelp })
41057
+ ] });
41058
+ }
41059
+
41060
+ // ui/components/NewDocModal/NewDocModal.tsx
41061
+ function isSlugValid2(slug) {
41062
+ return Boolean(slug && slug.match(/^[a-z0-9]+(?:--?[a-z0-9]+)*$/));
41063
+ }
41064
+ function normalizeSlug2(slug) {
41065
+ return slug.replace(/^[\s/]*/g, "").replace(/[\s/]*$/g, "").replace(/^\/+|\/+$/g, "").toLowerCase().replaceAll("/", "--");
40846
41066
  }
40847
41067
  function getDefaultFields(collection) {
40848
41068
  const fields = {};
@@ -40858,38 +41078,14 @@ ${content}</tr>
40858
41078
  }
40859
41079
  function NewDocModal(props) {
40860
41080
  const collectionId = props.collection;
40861
- const slugRef = A2(null);
40862
41081
  const [slug, setSlug] = y2("");
40863
41082
  const [loading, setLoading] = y2(false);
40864
41083
  const [slugError, setSlugError] = y2("");
40865
- const firebase = useFirebase();
40866
41084
  const theme = useMantineTheme();
40867
- const projectId = window.__ROOT_CTX.rootConfig.projectId;
40868
- const dbCollection = Ja2(
40869
- firebase.db,
40870
- "Projects",
40871
- projectId,
40872
- "Collections",
40873
- collectionId,
40874
- "Drafts"
40875
- );
40876
41085
  const rootCollection = window.__ROOT_CTX.collections[collectionId];
40877
41086
  if (!rootCollection) {
40878
41087
  throw new Error(`collection not found: ${collectionId}`);
40879
41088
  }
40880
- const domain2 = window.__ROOT_CTX.rootConfig.domain || "https://example.com";
40881
- let urlHelp = "";
40882
- if (rootCollection?.url) {
40883
- if (slug) {
40884
- let urlPath = rootCollection.url.replace(/\[.*slug\]/, slug);
40885
- if (urlPath === "/index") {
40886
- urlPath = "/";
40887
- }
40888
- urlHelp = `${domain2}${urlPath}`;
40889
- } else {
40890
- urlHelp = `${domain2}${rootCollection.url}`;
40891
- }
40892
- }
40893
41089
  function onClose() {
40894
41090
  if (props.onClose) {
40895
41091
  props.onClose();
@@ -40899,34 +41095,23 @@ ${content}</tr>
40899
41095
  e3.preventDefault();
40900
41096
  setLoading(true);
40901
41097
  setSlugError("");
40902
- const slug2 = normalizeSlug(String(slugRef.current.value));
40903
- if (!isSlugValid(slug2)) {
41098
+ const cleanSlug = normalizeSlug2(slug);
41099
+ if (!isSlugValid2(cleanSlug)) {
40904
41100
  setSlugError('Please enter a valid slug (e.g. "foo-bar-123").');
40905
41101
  setLoading(false);
40906
41102
  return;
40907
41103
  }
40908
- const docId = `${collectionId}/${slug2}`;
40909
- const docRef = Za(dbCollection, slug2);
40910
- const snapshot = await Kl(docRef);
40911
- if (await snapshot.exists()) {
40912
- setSlugError(`${docId} already exists`);
41104
+ const docId = `${collectionId}/${cleanSlug}`;
41105
+ try {
41106
+ const fields = getDefaultFields(rootCollection);
41107
+ await cmsCreateDoc(docId, { fields });
41108
+ } catch (err) {
41109
+ setSlugError(String(err));
40913
41110
  setLoading(false);
40914
41111
  return;
40915
41112
  }
40916
- await Jl(docRef, {
40917
- id: docId,
40918
- slug: slug2,
40919
- collection: collectionId,
40920
- sys: {
40921
- createdAt: df(),
40922
- createdBy: window.firebase.user.email,
40923
- modifiedAt: df(),
40924
- modifiedBy: window.firebase.user.email
40925
- },
40926
- fields: getDefaultFields(rootCollection)
40927
- });
40928
41113
  setLoading(false);
40929
- $4(`/cms/content/${props.collection}/${slug2}?new=true`);
41114
+ $4(`/cms/content/${props.collection}/${cleanSlug}?new=true`);
40930
41115
  }
40931
41116
  return /* @__PURE__ */ o4(
40932
41117
  Modal,
@@ -40934,25 +41119,23 @@ ${content}</tr>
40934
41119
  className: "NewDocModal",
40935
41120
  opened: props.opened || false,
40936
41121
  onClose: () => onClose(),
40937
- title: `${props.collection}: New doc`,
41122
+ title: "New",
41123
+ size: "500px",
40938
41124
  overlayColor: theme.colorScheme === "dark" ? theme.colors.dark[9] : theme.colors.gray[2],
40939
41125
  children: [
40940
41126
  /* @__PURE__ */ o4("div", { className: "NewDocModal__body", children: "Enter a slug for the new doc. The slug is the ID of the page and is what's used in the URL. Use only lowercase letters, numbers, and dashes." }),
40941
41127
  /* @__PURE__ */ o4("form", { onSubmit: (e3) => onSubmit(e3), children: [
40942
- /* @__PURE__ */ o4("div", { className: "NewDocModal__slug", children: /* @__PURE__ */ o4(
40943
- TextInput,
41128
+ /* @__PURE__ */ o4(
41129
+ SlugInput,
40944
41130
  {
40945
- name: "slug",
40946
- ref: slugRef,
40947
- value: slug,
40948
- onChange: (event) => setSlug(event.currentTarget.value),
40949
- placeholder: "slug",
40950
- autoComplete: "off",
40951
- size: "xs",
40952
- description: urlHelp,
40953
- error: slugError
41131
+ className: "NewDocModal__slug",
41132
+ collectionId,
41133
+ onChange: (newValue) => {
41134
+ setSlug(newValue.slug);
41135
+ }
40954
41136
  }
40955
- ) }),
41137
+ ),
41138
+ slugError && /* @__PURE__ */ o4("div", { className: "NewDocModal__slugError", children: slugError }),
40956
41139
  /* @__PURE__ */ o4("div", { className: "NewDocModal__buttons", children: [
40957
41140
  /* @__PURE__ */ o4(
40958
41141
  Button,
@@ -41160,7 +41343,7 @@ ${content}</tr>
41160
41343
  s2(() => {
41161
41344
  setLoading(true);
41162
41345
  listDocs();
41163
- }, []);
41346
+ }, [Ja2, options2.orderBy]);
41164
41347
  return [loading, listDocs, docs];
41165
41348
  }
41166
41349
  function CollectionPage(props) {
@@ -41646,10 +41829,39 @@ ${content}</tr>
41646
41829
  };
41647
41830
  }
41648
41831
 
41832
+ // ui/utils/events.ts
41833
+ var EventListener = class {
41834
+ constructor() {
41835
+ this.events = /* @__PURE__ */ new Map();
41836
+ }
41837
+ on(eventName, callback) {
41838
+ const callbacks = this.events.get(eventName) ?? [];
41839
+ this.events.set(eventName, [...callbacks, callback]);
41840
+ return () => {
41841
+ this.off(eventName, callback);
41842
+ };
41843
+ }
41844
+ off(eventName, callback) {
41845
+ const eventCallbacks = this.events.get(eventName) ?? [];
41846
+ const index3 = eventCallbacks.indexOf(callback);
41847
+ if (index3 >= 0) {
41848
+ eventCallbacks.splice(index3, 1);
41849
+ }
41850
+ }
41851
+ dispatch(eventName, ...args) {
41852
+ const eventCallbacks = this.events.get(eventName) ?? [];
41853
+ eventCallbacks.forEach((callback) => callback(...args));
41854
+ }
41855
+ dispose() {
41856
+ this.events.clear();
41857
+ }
41858
+ };
41859
+
41649
41860
  // ui/hooks/useDraft.ts
41650
41861
  var SAVE_DELAY_MS = 3 * 1e3;
41651
- var DraftController = class {
41862
+ var DraftController = class extends EventListener {
41652
41863
  constructor(docId) {
41864
+ super();
41653
41865
  this.pendingUpdates = /* @__PURE__ */ new Map();
41654
41866
  this.cachedData = {};
41655
41867
  this.subscribers = {};
@@ -41683,7 +41895,6 @@ ${content}</tr>
41683
41895
  if (this.started) {
41684
41896
  return;
41685
41897
  }
41686
- console.log("start()");
41687
41898
  this.started = true;
41688
41899
  this.dbUnsubscribe = tf(this.docRef, (snapshot) => {
41689
41900
  const data = snapshot.data();
@@ -41691,7 +41902,6 @@ ${content}</tr>
41691
41902
  applyUpdates(data, Object.fromEntries(this.pendingUpdates));
41692
41903
  }
41693
41904
  this.cachedData = data;
41694
- console.log("onSnapshot()", data, this.pendingUpdates);
41695
41905
  this.notifySubscribers();
41696
41906
  });
41697
41907
  }
@@ -41702,7 +41912,6 @@ ${content}</tr>
41702
41912
  if (!this.started) {
41703
41913
  return;
41704
41914
  }
41705
- console.log("stop()");
41706
41915
  if (this.dbUnsubscribe) {
41707
41916
  this.dbUnsubscribe();
41708
41917
  }
@@ -41710,16 +41919,22 @@ ${content}</tr>
41710
41919
  this.started = false;
41711
41920
  }
41712
41921
  /**
41713
- * Adds a listener for change events.
41922
+ * Adds a listener for data change events.
41714
41923
  */
41715
41924
  onChange(callback) {
41716
- this.onChangeCallback = callback;
41925
+ return this.on("CHANGE" /* CHANGE */, callback);
41717
41926
  }
41718
41927
  /**
41719
41928
  * Adds a listener for save state change events.
41720
41929
  */
41721
41930
  onSaveStateChange(callback) {
41722
- this.onSaveStateChangeCallback = callback;
41931
+ return this.on("SAVE_STATE_CHANGE" /* SAVE_STATE_CHANGE */, callback);
41932
+ }
41933
+ /**
41934
+ * Adds a listener for db write events.
41935
+ */
41936
+ onFlush(callback) {
41937
+ return this.on("FLUSH" /* FLUSH */, callback);
41723
41938
  }
41724
41939
  /**
41725
41940
  * Subscribes to remote changes for a given key. Returns a callback function
@@ -41727,7 +41942,6 @@ ${content}</tr>
41727
41942
  */
41728
41943
  subscribe(key, callback) {
41729
41944
  var _a2;
41730
- console.log("subscribe()", key);
41731
41945
  (_a2 = this.subscribers)[key] ?? (_a2[key] = /* @__PURE__ */ new Set());
41732
41946
  this.subscribers[key].add(callback);
41733
41947
  callback(getNestedValue(this.cachedData, key));
@@ -41743,11 +41957,8 @@ ${content}</tr>
41743
41957
  * Notifies subscribers of changes.
41744
41958
  */
41745
41959
  notifySubscribers() {
41746
- console.log("notifySubscribers()");
41747
41960
  const data = this.cachedData;
41748
- if (this.onChangeCallback) {
41749
- this.onChangeCallback(data);
41750
- }
41961
+ this.dispatch("CHANGE" /* CHANGE */, data);
41751
41962
  notify(this.subscribers, data);
41752
41963
  }
41753
41964
  /**
@@ -41765,9 +41976,7 @@ ${content}</tr>
41765
41976
  this.pendingUpdates.set(key, updates[key]);
41766
41977
  }
41767
41978
  applyUpdates(this.cachedData, updates);
41768
- if (this.onChangeCallback) {
41769
- this.onChangeCallback(this.cachedData);
41770
- }
41979
+ this.dispatch("CHANGE" /* CHANGE */, this.cachedData);
41771
41980
  this.setSaveState("UPDATE_PENDING" /* UPDATES_PENDING */);
41772
41981
  this.queueChanges();
41773
41982
  }
@@ -41777,9 +41986,7 @@ ${content}</tr>
41777
41986
  async removeKey(key) {
41778
41987
  this.pendingUpdates.set(key, ff());
41779
41988
  applyUpdates(this.cachedData, { [key]: void 0 });
41780
- if (this.onChangeCallback) {
41781
- this.onChangeCallback({ ...this.cachedData });
41782
- }
41989
+ this.dispatch("CHANGE" /* CHANGE */, this.cachedData);
41783
41990
  this.setSaveState("UPDATE_PENDING" /* UPDATES_PENDING */);
41784
41991
  this.queueChanges();
41785
41992
  }
@@ -41796,9 +42003,7 @@ ${content}</tr>
41796
42003
  }
41797
42004
  }, SAVE_DELAY_MS);
41798
42005
  }
41799
- if (this.onSaveStateChangeCallback) {
41800
- this.onSaveStateChangeCallback(newSaveState);
41801
- }
42006
+ this.dispatch("SAVE_STATE_CHANGE" /* SAVE_STATE_CHANGE */, newSaveState);
41802
42007
  }
41803
42008
  /**
41804
42009
  * Immediately write all queued data to the DB.
@@ -41816,10 +42021,17 @@ ${content}</tr>
41816
42021
  this.setSaveState("SAVING" /* SAVING */);
41817
42022
  await Yl(this.docRef, updates);
41818
42023
  this.setSaveState("SAVED" /* SAVED */);
42024
+ this.dispatch("FLUSH" /* FLUSH */);
41819
42025
  } catch (err) {
41820
42026
  console.error("failed to update doc");
41821
42027
  console.error(err);
41822
42028
  this.setSaveState("ERROR" /* ERROR */);
42029
+ showNotification({
42030
+ title: "Failed to save",
42031
+ message: `Failed to save changes to ${this.slug}`,
42032
+ color: "red",
42033
+ autoClose: false
42034
+ });
41823
42035
  for (const key in updates) {
41824
42036
  if (key.startsWith("sys.")) {
41825
42037
  continue;
@@ -41846,6 +42058,7 @@ ${content}</tr>
41846
42058
  * Stops all listeners and disposes the controller.
41847
42059
  */
41848
42060
  async dispose() {
42061
+ super.dispose();
41849
42062
  this.stop();
41850
42063
  }
41851
42064
  };
@@ -41893,28 +42106,28 @@ ${content}</tr>
41893
42106
  function useDraft(docId) {
41894
42107
  const [loading, setLoading] = y2(true);
41895
42108
  const [data, setData] = y2({});
41896
- const draft = T2(() => new DraftController(docId), []);
42109
+ const controller = T2(() => new DraftController(docId), []);
41897
42110
  const [saveState, setSaveState] = y2("NO_CHANGES" /* NO_CHANGES */);
41898
42111
  s2(() => {
41899
- draft.onChange((data2) => {
42112
+ controller.onChange((data2) => {
41900
42113
  setData(data2);
41901
42114
  setLoading(false);
41902
42115
  });
41903
- draft.onSaveStateChange((newSaveState) => setSaveState(newSaveState));
41904
- draft.start();
42116
+ controller.onSaveStateChange((newSaveState) => setSaveState(newSaveState));
42117
+ controller.start();
41905
42118
  document.addEventListener("visibilitychange", () => {
41906
42119
  if (document.hidden || document.visibilityState !== "visible") {
41907
- draft.stop();
42120
+ controller.stop();
41908
42121
  } else {
41909
- if (!draft.started) {
42122
+ if (!controller.started) {
41910
42123
  setLoading(true);
41911
- draft.start();
42124
+ controller.start();
41912
42125
  }
41913
42126
  }
41914
42127
  });
41915
- return () => draft.dispose();
42128
+ return () => controller.dispose();
41916
42129
  }, []);
41917
- return { loading, saveState, draft, data };
42130
+ return { loading, saveState, controller, data };
41918
42131
  }
41919
42132
 
41920
42133
  // ui/utils/str-format.ts
@@ -41961,11 +42174,11 @@ ${content}</tr>
41961
42174
  LocalizationModal.ConfigLocales = (props) => {
41962
42175
  const [enabledLocales, setEnabledLocales] = y2(["en"]);
41963
42176
  const i18nConfig = window.__ROOT_CTX.rootConfig.i18n || {};
41964
- const podLocales = i18nConfig.locales || ["en"];
42177
+ const i18nLocales = i18nConfig.locales || ["en"];
41965
42178
  const localeGroups = i18nConfig.groups || {
41966
42179
  default: {
41967
42180
  label: "",
41968
- locales: podLocales
42181
+ locales: i18nLocales
41969
42182
  }
41970
42183
  };
41971
42184
  s2(() => {
@@ -42013,7 +42226,7 @@ ${content}</tr>
42013
42226
  /* @__PURE__ */ o4(
42014
42227
  LocalizationModal.AllNoneButtons,
42015
42228
  {
42016
- onAllClicked: () => updateEnabledLocales(podLocales),
42229
+ onAllClicked: () => updateEnabledLocales(i18nLocales),
42017
42230
  onNoneClicked: () => updateEnabledLocales([])
42018
42231
  }
42019
42232
  )
@@ -42121,6 +42334,7 @@ ${content}</tr>
42121
42334
  const [loading, setLoading] = y2(true);
42122
42335
  const [sourceStrings, setSourceStrings] = y2([]);
42123
42336
  const [selectedLocale, setSelectedLocale] = y2("");
42337
+ const [localeTranslations, setLocaleTranslations] = y2({});
42124
42338
  const locales = window.__ROOT_CTX.rootConfig.i18n.locales || [];
42125
42339
  const localeOptions = locales.map((locale) => ({
42126
42340
  value: locale,
@@ -42136,6 +42350,25 @@ ${content}</tr>
42136
42350
  setLoading(false);
42137
42351
  });
42138
42352
  }, [props.opened]);
42353
+ s2(() => {
42354
+ if (!selectedLocale) {
42355
+ setLocaleTranslations({});
42356
+ return;
42357
+ }
42358
+ const translationsRef = getTranslationsDocRef(props.docId);
42359
+ Kl(translationsRef).then((translationsDoc) => {
42360
+ const data = translationsDoc.data() || {};
42361
+ return data.translations || {};
42362
+ }).then((translationsMap) => {
42363
+ const localeTranslations2 = {};
42364
+ Object.values(translationsMap).forEach(
42365
+ (translation) => {
42366
+ localeTranslations2[translation.source] = translation[selectedLocale] || "";
42367
+ }
42368
+ );
42369
+ setLocaleTranslations(localeTranslations2);
42370
+ });
42371
+ }, [selectedLocale]);
42139
42372
  async function downloadCsv() {
42140
42373
  const headers = ["source", "en"];
42141
42374
  const rows = [];
@@ -42160,12 +42393,39 @@ ${content}</tr>
42160
42393
  window.location.assign(file);
42161
42394
  window.URL.revokeObjectURL(file);
42162
42395
  }
42396
+ async function importCsv() {
42397
+ const fileInput = document.createElement("input");
42398
+ fileInput.type = "file";
42399
+ fileInput.accept = ".csv";
42400
+ fileInput.addEventListener("change", async () => {
42401
+ const file = fileInput.files?.[0];
42402
+ if (file) {
42403
+ const formData = new FormData();
42404
+ formData.append("file", file);
42405
+ const res = await fetch("/cms/api/csv.import", {
42406
+ method: "POST",
42407
+ body: formData
42408
+ }).then((res2) => res2.json());
42409
+ await cmsDocImportCsv(props.docId, res.data);
42410
+ showNotification({
42411
+ title: "Saved!",
42412
+ message: `Successfully imported translations for ${props.docId}.`,
42413
+ autoClose: 5e3
42414
+ });
42415
+ }
42416
+ });
42417
+ fileInput.click();
42418
+ }
42163
42419
  function onAction(action) {
42164
42420
  switch (action) {
42165
42421
  case "export-download-csv": {
42166
42422
  downloadCsv();
42167
42423
  return;
42168
42424
  }
42425
+ case "import-csv": {
42426
+ importCsv();
42427
+ return;
42428
+ }
42169
42429
  }
42170
42430
  }
42171
42431
  if (loading) {
@@ -42241,7 +42501,7 @@ ${content}</tr>
42241
42501
  borderRadius: 4,
42242
42502
  height: "100%"
42243
42503
  }),
42244
- children: /* @__PURE__ */ o4(Text, { size: "xs", sx: { whiteSpace: "pre-wrap" }, children: "\xA0" })
42504
+ children: /* @__PURE__ */ o4(Text, { size: "xs", sx: { whiteSpace: "pre-wrap" }, children: localeTranslations[source] || " " })
42245
42505
  }
42246
42506
  ) })
42247
42507
  ] }, i3))
@@ -42335,7 +42595,7 @@ ${content}</tr>
42335
42595
  /* @__PURE__ */ o4(Menu.Item, { disabled: true, onClick: () => dispatch("import-google-sheet"), children: "Import Google Sheet" }),
42336
42596
  /* @__PURE__ */ o4(Divider, {}),
42337
42597
  /* @__PURE__ */ o4(Menu.Label, { children: "File" }),
42338
- /* @__PURE__ */ o4(Menu.Item, { onClick: () => dispatch("import-upload-csv"), children: "Upload .csv" })
42598
+ /* @__PURE__ */ o4(Menu.Item, { onClick: () => dispatch("import-csv"), children: "Import .csv" })
42339
42599
  ]
42340
42600
  }
42341
42601
  );
@@ -42547,14 +42807,9 @@ ${content}</tr>
42547
42807
  // ui/components/DocEditor/DocEditor.tsx
42548
42808
  function DocEditor(props) {
42549
42809
  const fields = props.collection.fields || [];
42550
- const { loading, draft, saveState, data } = useDraft(props.docId);
42810
+ const { loading, controller, saveState, data } = props.draft;
42551
42811
  const [publishDocModalOpen, setPublishDocModalOpen] = y2(false);
42552
42812
  const [l10nModalOpen, setL10nModalOpen] = y2(false);
42553
- s2(() => {
42554
- if (draft && props.onDraftController) {
42555
- props.onDraftController(draft);
42556
- }
42557
- }, [draft]);
42558
42813
  function goBack() {
42559
42814
  const collectionId = props.docId.split("/")[0];
42560
42815
  $4(`/cms/content/${collectionId}`);
@@ -42571,7 +42826,7 @@ ${content}</tr>
42571
42826
  /* @__PURE__ */ o4(
42572
42827
  LocalizationModal,
42573
42828
  {
42574
- draft,
42829
+ draft: controller,
42575
42830
  collection: props.collection,
42576
42831
  docId: props.docId,
42577
42832
  opened: l10nModalOpen,
@@ -42631,7 +42886,7 @@ ${content}</tr>
42631
42886
  field,
42632
42887
  shallowKey: field.id,
42633
42888
  deepKey: `fields.${field.id}`,
42634
- draft
42889
+ draft: controller
42635
42890
  },
42636
42891
  field.id
42637
42892
  )) })
@@ -42687,7 +42942,9 @@ ${content}</tr>
42687
42942
  minRows: 2,
42688
42943
  maxRows: field.maxRows || 12,
42689
42944
  value,
42690
- onChange: (e3) => onChange(e3.currentTarget.value)
42945
+ onChange: (e3) => {
42946
+ onChange(e3.currentTarget.value);
42947
+ }
42691
42948
  }
42692
42949
  );
42693
42950
  }
@@ -42697,7 +42954,9 @@ ${content}</tr>
42697
42954
  size: "xs",
42698
42955
  radius: 0,
42699
42956
  value,
42700
- onChange: (e3) => onChange(e3.currentTarget.value)
42957
+ onChange: (e3) => {
42958
+ onChange(e3.currentTarget.value);
42959
+ }
42701
42960
  }
42702
42961
  );
42703
42962
  };
@@ -42711,7 +42970,7 @@ ${content}</tr>
42711
42970
  async function uploadFileToGCS(file) {
42712
42971
  const projectId = window.__ROOT_CTX.rootConfig.projectId;
42713
42972
  const hashHex = await sha256(file);
42714
- const ext = file.name.split(".").at(-1);
42973
+ const ext = normalizeExt(file.name.split(".").at(-1) || "");
42715
42974
  const filePath = `${projectId}/uploads/${hashHex}.${ext}`;
42716
42975
  const gcsRef = ref(window.firebase.storage, filePath);
42717
42976
  await uploadBytes(gcsRef, file);
@@ -42741,6 +43000,13 @@ ${content}</tr>
42741
43000
  src: imageSrc
42742
43001
  };
42743
43002
  }
43003
+ function normalizeExt(ext) {
43004
+ let output = String(ext).toLowerCase();
43005
+ if (output === ".jpeg") {
43006
+ output = ".jpg";
43007
+ }
43008
+ return output;
43009
+ }
42744
43010
  async function getGciUrl(gcsPath) {
42745
43011
  const gciDomain = window.__ROOT_CTX.rootConfig.gci;
42746
43012
  if (!gciDomain) {
@@ -42866,7 +43132,9 @@ ${content}</tr>
42866
43132
  radius: 0,
42867
43133
  value: img.alt,
42868
43134
  label: "Alt text",
42869
- onChange: (e3) => setAltText(e3.target.value)
43135
+ onChange: (e3) => {
43136
+ setAltText(e3.currentTarget.value);
43137
+ }
42870
43138
  }
42871
43139
  )
42872
43140
  ] }) : /* @__PURE__ */ o4("div", { className: "DocEditor__ImageField__noImage", children: "No image" }),
@@ -42986,7 +43254,6 @@ ${content}</tr>
42986
43254
  )) }) });
42987
43255
  };
42988
43256
  function arrayReducer(state, action) {
42989
- console.log(action);
42990
43257
  switch (action.type) {
42991
43258
  case "update": {
42992
43259
  const newlyAdded = state._new || [];
@@ -43124,7 +43391,6 @@ ${content}</tr>
43124
43391
  const unsubscribe = props.draft.subscribe(
43125
43392
  props.deepKey,
43126
43393
  (newValue) => {
43127
- console.log("onRemoteChange()", newValue);
43128
43394
  dispatch({ type: "update", newValue });
43129
43395
  }
43130
43396
  );
@@ -43468,7 +43734,6 @@ ${content}</tr>
43468
43734
  placeholders._index = String(index3);
43469
43735
  placeholders["_index:02"] = placeholders._index.padStart(2, "0");
43470
43736
  placeholders["_index:03"] = placeholders._index.padStart(3, "0");
43471
- console.log(placeholders);
43472
43737
  while (templates.length > 0) {
43473
43738
  const template = templates.shift();
43474
43739
  const preview = strFormat(template, placeholders);
@@ -43543,15 +43808,12 @@ ${content}</tr>
43543
43808
  const slug = props.slug;
43544
43809
  const docId = `${collectionId}/${slug}`;
43545
43810
  const collection = window.__ROOT_CTX.collections[collectionId];
43546
- const [draft, setDraft] = y2(null);
43811
+ const draft = useDraft(docId);
43547
43812
  if (!collection) {
43548
43813
  return /* @__PURE__ */ o4("div", { children: "Could not find collection." });
43549
43814
  }
43550
43815
  function saveDraft() {
43551
- console.log("saveDraft()");
43552
- if (draft) {
43553
- draft.flush();
43554
- }
43816
+ draft.controller.flush();
43555
43817
  }
43556
43818
  useHotkeys([["mod+S", () => saveDraft()]]);
43557
43819
  return /* @__PURE__ */ o4(Layout, { children: /* @__PURE__ */ o4(SplitPanel, { className: "DocumentPage", localStorageId: "DocumentPage", children: [
@@ -43574,14 +43836,7 @@ ${content}</tr>
43574
43836
  }
43575
43837
  ) })
43576
43838
  ] }),
43577
- /* @__PURE__ */ o4("div", { className: "DocumentPage__side__editor", children: /* @__PURE__ */ o4(
43578
- DocEditor,
43579
- {
43580
- collection,
43581
- docId,
43582
- onDraftController: (draft2) => setDraft(draft2)
43583
- }
43584
- ) })
43839
+ /* @__PURE__ */ o4("div", { className: "DocumentPage__side__editor", children: /* @__PURE__ */ o4(DocEditor, { collection, docId, draft }) })
43585
43840
  ] }),
43586
43841
  /* @__PURE__ */ o4(SplitPanel.Item, { className: "DocumentPage__main", fluid: true, children: /* @__PURE__ */ o4(DocumentPage.Preview, { docId, draft }) })
43587
43842
  ] }) });
@@ -43604,10 +43859,20 @@ ${content}</tr>
43604
43859
  "--iframe-scale": "1"
43605
43860
  });
43606
43861
  const iframeRef = A2(null);
43862
+ function reloadIframe() {
43863
+ const iframe = iframeRef.current;
43864
+ iframe.src = "about:blank";
43865
+ window.setTimeout(() => {
43866
+ if (iframe.src !== previewPath) {
43867
+ iframe.src = previewPath;
43868
+ } else {
43869
+ iframe.contentWindow.location.reload();
43870
+ }
43871
+ }, 20);
43872
+ }
43607
43873
  s2(() => {
43608
43874
  const iframe = iframeRef.current;
43609
43875
  function onIframeLoad() {
43610
- console.log("iframe load", iframe.contentWindow?.document.title);
43611
43876
  const iframeWindow = iframe.contentWindow;
43612
43877
  if (!iframeWindow) {
43613
43878
  return;
@@ -43618,7 +43883,11 @@ ${content}</tr>
43618
43883
  }
43619
43884
  }
43620
43885
  iframe.addEventListener("load", onIframeLoad);
43886
+ const removeOnFlush = props.draft.controller.onFlush(() => {
43887
+ reloadIframe();
43888
+ });
43621
43889
  return () => {
43890
+ removeOnFlush();
43622
43891
  iframe.removeEventListener("load", onIframeLoad);
43623
43892
  };
43624
43893
  }, []);
@@ -43630,23 +43899,8 @@ ${content}</tr>
43630
43899
  return device2;
43631
43900
  });
43632
43901
  }
43633
- function reload2() {
43634
- const reloadIframe = () => {
43635
- const iframe = iframeRef.current;
43636
- iframe.src = "about:blank";
43637
- window.setTimeout(() => {
43638
- if (iframe.src !== previewPath) {
43639
- iframe.src = previewPath;
43640
- } else {
43641
- iframe.contentWindow.location.reload();
43642
- }
43643
- }, 20);
43644
- };
43645
- if (props.draft) {
43646
- props.draft.flush().then(() => reloadIframe());
43647
- } else {
43648
- reloadIframe();
43649
- }
43902
+ function onReloadClick() {
43903
+ props.draft.controller.flush().then(() => reloadIframe());
43650
43904
  }
43651
43905
  function openNewTab() {
43652
43906
  const tab = window.open(previewPath, "_blank");
@@ -43724,7 +43978,7 @@ ${content}</tr>
43724
43978
  ActionIcon,
43725
43979
  {
43726
43980
  className: "DocumentPage__main__previewBar__button DocumentPage__main__previewBar__button--reload",
43727
- onClick: () => reload2(),
43981
+ onClick: () => onReloadClick(),
43728
43982
  children: /* @__PURE__ */ o4(IconReload, { size: 16 })
43729
43983
  }
43730
43984
  ) }),
@@ -44025,6 +44279,94 @@ ${content}</tr>
44025
44279
  ] }) });
44026
44280
  }
44027
44281
 
44282
+ // ui/components/CopyDocModal/CopyDocModal.tsx
44283
+ function CopyDocModal(modalProps) {
44284
+ const { innerProps: props, context, id: id2 } = modalProps;
44285
+ const [toCollectionId, setToCollectionId] = y2("");
44286
+ const [toSlug, setToSlug] = y2("");
44287
+ const [error, setError] = y2("");
44288
+ const [loading, setLoading] = y2(false);
44289
+ const fromDocId = props.fromDocId;
44290
+ const fromCollectionId = fromDocId.split("/")[0];
44291
+ async function onSubmit(e3) {
44292
+ e3.preventDefault();
44293
+ setLoading(true);
44294
+ setError("");
44295
+ if (!fromCollectionId) {
44296
+ setError("Please select a collection.");
44297
+ setLoading(false);
44298
+ return;
44299
+ }
44300
+ const cleanSlug = normalizeSlug(toSlug);
44301
+ if (!isSlugValid(cleanSlug)) {
44302
+ setError('Please enter a valid slug (e.g. "foo-bar-123").');
44303
+ setLoading(false);
44304
+ return;
44305
+ }
44306
+ const toDocId = `${toCollectionId}/${cleanSlug}`;
44307
+ try {
44308
+ await cmsCopyDoc(fromDocId, toDocId);
44309
+ context.closeModal(id2);
44310
+ showNotification({
44311
+ title: "Copied!",
44312
+ message: `Succesfully copied ${fromDocId} to ${toDocId}.`,
44313
+ autoClose: 5e3
44314
+ });
44315
+ } catch (err) {
44316
+ setError(String(err));
44317
+ setLoading(false);
44318
+ return;
44319
+ }
44320
+ setLoading(false);
44321
+ $4(`/cms/content/${toCollectionId}/${cleanSlug}`);
44322
+ }
44323
+ return /* @__PURE__ */ o4("div", { className: "CopyDocModal", children: /* @__PURE__ */ o4("form", { onSubmit: (e3) => onSubmit(e3), children: [
44324
+ /* @__PURE__ */ o4("div", { className: "CopyDocModal__from", children: [
44325
+ /* @__PURE__ */ o4(Text2, { className: "CopyDocModal__from__label", size: "body", weight: "bold", children: "From:" }),
44326
+ /* @__PURE__ */ o4(Text2, { className: "CopyDocModal__from__value", size: "body-sm", children: /* @__PURE__ */ o4("code", { children: props.fromDocId }) })
44327
+ ] }),
44328
+ /* @__PURE__ */ o4("div", { className: "CopyDocModal__to", children: [
44329
+ /* @__PURE__ */ o4(Text2, { className: "CopyDocModal__to__label", size: "body", weight: "bold", children: "To:" }),
44330
+ /* @__PURE__ */ o4(
44331
+ SlugInput,
44332
+ {
44333
+ className: "CopyDocModal__slug",
44334
+ collectionId: fromCollectionId,
44335
+ onChange: (newValue) => {
44336
+ setToCollectionId(newValue.collectionId);
44337
+ setToSlug(newValue.slug);
44338
+ }
44339
+ }
44340
+ )
44341
+ ] }),
44342
+ error && /* @__PURE__ */ o4("div", { className: "CopyDocModal__error", children: error }),
44343
+ /* @__PURE__ */ o4("div", { className: "CopyDocModal__buttons", children: [
44344
+ /* @__PURE__ */ o4(
44345
+ Button,
44346
+ {
44347
+ variant: "outline",
44348
+ onClick: () => context.closeModal(id2),
44349
+ type: "button",
44350
+ size: "xs",
44351
+ color: "dark",
44352
+ children: "Cancel"
44353
+ }
44354
+ ),
44355
+ /* @__PURE__ */ o4(
44356
+ Button,
44357
+ {
44358
+ variant: "filled",
44359
+ type: "submit",
44360
+ size: "xs",
44361
+ color: "dark",
44362
+ loading,
44363
+ children: "Submit"
44364
+ }
44365
+ )
44366
+ ] })
44367
+ ] }) });
44368
+ }
44369
+
44028
44370
  // ui/ui.tsx
44029
44371
  function App() {
44030
44372
  return /* @__PURE__ */ o4(
@@ -44034,7 +44376,7 @@ ${content}</tr>
44034
44376
  fontFamily: "Inter, sans-serif",
44035
44377
  fontSizes: { xs: 12, sm: 14, md: 16, lg: 18, xl: 20 }
44036
44378
  },
44037
- children: /* @__PURE__ */ o4(NotificationsProvider, { children: /* @__PURE__ */ o4(ModalsProvider, { children: /* @__PURE__ */ o4(FirebaseContext.Provider, { value: window.firebase, children: /* @__PURE__ */ o4(D4, { children: [
44379
+ children: /* @__PURE__ */ o4(NotificationsProvider, { children: /* @__PURE__ */ o4(ModalsProvider, { modals: { copyDoc: CopyDocModal }, children: /* @__PURE__ */ o4(FirebaseContext.Provider, { value: window.firebase, children: /* @__PURE__ */ o4(D4, { children: [
44038
44380
  /* @__PURE__ */ o4(L6, { path: "/cms", component: ProjectPage }),
44039
44381
  /* @__PURE__ */ o4(L6, { path: "/cms/assets", component: AssetsPage }),
44040
44382
  /* @__PURE__ */ o4(
@@ -44102,7 +44444,6 @@ ${content}</tr>
44102
44444
  console.log(res);
44103
44445
  return;
44104
44446
  }
44105
- console.log("updated user session");
44106
44447
  }
44107
44448
  })();
44108
44449
  /*! Bundled license information: