tinacms 3.9.2 → 3.9.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.
@@ -1,6 +1,6 @@
1
1
  import { Collection } from '@tinacms/schema-tools';
2
- import React from 'react';
3
2
  import type { TinaCMS } from '@tinacms/toolkit';
3
+ import React from 'react';
4
4
  declare const CollectionCreatePage: () => React.JSX.Element;
5
5
  export declare const RenderForm: ({ cms, collection, folder, templateName, mutationInfo, customDefaults, }: {
6
6
  cms: TinaCMS;
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ import { useHTMLInputCursorState, useComboboxInput } from "@udecode/plate-combob
36
36
  import { useComboboxContext, Combobox as Combobox$1, useComboboxStore, ComboboxProvider, Portal, ComboboxPopover, ComboboxItem } from "@ariakit/react";
37
37
  import { filterWords } from "@udecode/plate-combobox";
38
38
  import { toggleList, unwrapList, getListItemEntry } from "@udecode/plate-list";
39
- import { normalizeHeadingLevels, ALL_HEADING_LEVELS, isHeadingLevel, TinaSchema, addNamespaceToSchema, parseURL, resolveForm, normalizePath, canonicalPath, validateSchema } from "@tinacms/schema-tools";
39
+ import { normalizeHeadingLevels, ALL_HEADING_LEVELS, canonicalPath, isHeadingLevel, TinaSchema, addNamespaceToSchema, parseURL, resolveForm, normalizePath, validateSchema } from "@tinacms/schema-tools";
40
40
  import { NAMER, resolveField } from "@tinacms/schema-tools";
41
41
  import { useBlockSelected, BlockSelectionPlugin } from "@udecode/plate-selection/react";
42
42
  import "@udecode/plate-dnd";
@@ -52,7 +52,7 @@ import arrayMutators from "final-form-arrays";
52
52
  import setFieldData from "final-form-set-field-data";
53
53
  import { Field, Form as Form$1 } from "react-final-form";
54
54
  import PropTypes from "prop-types";
55
- import { BiCheckCircle, BiError, BiInfoCircle, BiCheck, BiFileBlank, BiEdit, BiPencil, BiSearch, BiX, BiRefresh, BiGitBranch, BiPlus, BiLockAlt, BiLinkExternal, BiGitRepoForked, BiCopyAlt, BiFile, BiFolder, BiMovie, BiArrowToBottom, BiCloudUpload, BiGridAlt, BiListUl, BiRightArrowAlt, BiMenu, BiExit, BiChevronRight, BiFolderOpen, BiLeftArrowAlt, BiChevronDown, BiCopy, BiRename, BiTrash, BiArrowBack, BiSync } from "react-icons/bi";
55
+ import { BiCheckCircle, BiError, BiInfoCircle, BiCheck, BiFileBlank, BiEdit, BiPencil, BiSearch, BiX, BiRefresh, BiGitBranch, BiPlus, BiLockAlt, BiLinkExternal, BiGitRepoForked, BiCopyAlt, BiFile, BiFolder, BiMovie, BiArrowToBottom, BiCloudUpload, BiGridAlt, BiListUl, BiRightArrowAlt, BiMenu, BiExit, BiChevronRight, BiFolderOpen, BiArrowBack, BiLeftArrowAlt, BiChevronDown, BiCopy, BiRename, BiTrash, BiSync } from "react-icons/bi";
56
56
  import { BsArrowRightShort, BsExclamationOctagonFill, BsCheckCircleFill } from "react-icons/bs";
57
57
  import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, closestCenter } from "@dnd-kit/core";
58
58
  import { sortableKeyboardCoordinates, useSortable, SortableContext, verticalListSortingStrategy, defaultAnimateLayoutChanges } from "@dnd-kit/sortable";
@@ -63,7 +63,7 @@ import { buildSchema, print, getIntrospectionQuery, buildClientSchema, parse as
63
63
  import { diff as diff$1 } from "@graphql-inspector/core";
64
64
  import { processDocumentForIndexing, queryToSearchIndexQuery, optionsToSearchIndexOptions, parseSearchIndexResponse } from "@tinacms/search/index-client";
65
65
  import { HexColorPicker } from "react-colorful";
66
- import { MdKeyboardArrowDown, MdOutlineClear, MdArrowForward, MdAccessTime, MdOutlineDataSaverOff, MdCheckCircle, MdWifiOff, MdOutlineSettings, MdOutlinePhotoLibrary, MdVpnKey, MdImage, MdOutlineCloud, MdWarning, MdInfo, MdError, MdSyncProblem, MdOutlinePerson, MdOutlineHelpOutline } from "react-icons/md";
66
+ import { MdKeyboardArrowDown, MdOutlineClear, MdArrowForward, MdAccessTime, MdOutlineDataSaverOff, MdCheckCircle, MdWifiOff, MdOutlineLanguage, MdOutlinePhotoLibrary, MdVpnKey, MdImage, MdOutlineCloud, MdWarning, MdInfo, MdError, MdSyncProblem, MdOutlinePerson, MdOutlineHelpOutline } from "react-icons/md";
67
67
  import * as dropzone from "react-dropzone";
68
68
  import { IoMdArrowDropup, IoMdArrowDropdown, IoMdClose, IoMdRefresh } from "react-icons/io";
69
69
  import { Command as Command$3 } from "@udecode/cmdk";
@@ -82,12 +82,10 @@ import { getLinkAttributes } from "@udecode/plate-link";
82
82
  import "@heroicons/react/solid";
83
83
  import { autoformatSmartQuotes, autoformatPunctuation, autoformatLegal, autoformatArrow, autoformatMath } from "@udecode/plate-autoformat";
84
84
  import * as yup from "yup";
85
- import moment from "moment";
86
- import "moment-timezone";
85
+ import { format as format$3, formatDistanceToNow } from "date-fns";
87
86
  import { enUS } from "date-fns/locale";
88
87
  import { DayPicker } from "react-day-picker";
89
88
  import * as SelectPrimitive from "@radix-ui/react-select";
90
- import { formatDistanceToNow } from "date-fns";
91
89
  import { GrCircleQuestion } from "react-icons/gr";
92
90
  import { useReactTable, getCoreRowModel, getSortedRowModel, flexRender, getFilteredRowModel, getExpandedRowModel } from "@tanstack/react-table";
93
91
  import { FiInfo } from "react-icons/fi";
@@ -96,7 +94,7 @@ import { TbLogs } from "react-icons/tb";
96
94
  import { ImUsers, ImFilesEmpty } from "react-icons/im";
97
95
  import { PiSidebarSimpleLight } from "react-icons/pi";
98
96
  import gql from "graphql-tag";
99
- import { useLocation, NavLink, useNavigate, useParams, Link as Link$1, HashRouter, Routes, Route } from "react-router-dom";
97
+ import { useLocation, NavLink, useNavigate, useParams, Navigate, Link as Link$1, HashRouter, Routes, Route } from "react-router-dom";
100
98
  import { RiHome2Line } from "react-icons/ri";
101
99
  import { serializeMDX } from "@tinacms/mdx";
102
100
  const ModalProvider = ({ children }) => {
@@ -2034,6 +2032,27 @@ const Icons = {
2034
2032
  sun: SunMedium,
2035
2033
  twitter: Twitter
2036
2034
  };
2035
+ const EllipsisIcon = ({ title }) => {
2036
+ return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, title && /* @__PURE__ */ React__default.createElement("span", { className: "sr-only" }, title), /* @__PURE__ */ React__default.createElement(
2037
+ "svg",
2038
+ {
2039
+ xmlns: "http://www.w3.org/2000/svg",
2040
+ className: "h-5 w-5",
2041
+ fill: "none",
2042
+ viewBox: "0 0 24 24",
2043
+ stroke: "currentColor"
2044
+ },
2045
+ /* @__PURE__ */ React__default.createElement(
2046
+ "path",
2047
+ {
2048
+ strokeLinecap: "round",
2049
+ strokeLinejoin: "round",
2050
+ strokeWidth: 2,
2051
+ d: "M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z"
2052
+ }
2053
+ )
2054
+ ));
2055
+ };
2037
2056
  function UnorderedListIcon(props) {
2038
2057
  const title = props.title || "format list bulleted";
2039
2058
  return /* @__PURE__ */ React__default.createElement(
@@ -5946,7 +5965,7 @@ const useGetOptionSets = (cms, collections, collectionFilter) => {
5946
5965
  }, [cms, collections]);
5947
5966
  return { optionSets, loading };
5948
5967
  };
5949
- const getFilename = (optionSets, value) => {
5968
+ const getFilename$1 = (optionSets, value) => {
5950
5969
  const nodes = optionSets.flatMap(
5951
5970
  (optionSet) => optionSet.edges.map((edge) => edge.node)
5952
5971
  );
@@ -5964,7 +5983,7 @@ const Combobox = ({ cms, input, field }) => {
5964
5983
  );
5965
5984
  const [filteredOptionsList, setFilteredOptionsList] = React.useState(optionSets);
5966
5985
  React.useEffect(() => {
5967
- setDisplayText(getFilename(optionSets, value));
5986
+ setDisplayText(getFilename$1(optionSets, value));
5968
5987
  input.onChange(value);
5969
5988
  }, [value, input, optionSets]);
5970
5989
  React.useEffect(() => {
@@ -7603,6 +7622,81 @@ const PopoverContent = React.forwardRef(({ className, align = "center", sideOffs
7603
7622
  }
7604
7623
  )));
7605
7624
  PopoverContent.displayName = PopoverPrimitive.Content.displayName;
7625
+ const TOKEN_MAP = [
7626
+ // Year
7627
+ ["YYYY", "yyyy"],
7628
+ ["YY", "yy"],
7629
+ // Month
7630
+ ["MMMM", "MMMM"],
7631
+ ["MMM", "MMM"],
7632
+ ["MM", "MM"],
7633
+ ["M", "M"],
7634
+ // Day of month (uppercase D = day-of-month in moment)
7635
+ ["DD", "dd"],
7636
+ ["Do", "do"],
7637
+ ["D", "d"],
7638
+ // Weekday (lowercase d = weekday in moment)
7639
+ ["dddd", "EEEE"],
7640
+ ["ddd", "EEE"],
7641
+ ["dd", "EEEEEE"],
7642
+ // 24-hour
7643
+ ["HH", "HH"],
7644
+ ["H", "H"],
7645
+ // 12-hour
7646
+ ["hh", "hh"],
7647
+ ["h", "h"],
7648
+ // Minute
7649
+ ["mm", "mm"],
7650
+ ["m", "m"],
7651
+ // Second
7652
+ ["ss", "ss"],
7653
+ ["s", "s"],
7654
+ // Millisecond
7655
+ ["SSS", "SSS"],
7656
+ // AM/PM
7657
+ ["A", "a"],
7658
+ ["a", "aaa"],
7659
+ // Timezone offset
7660
+ ["ZZ", "xx"],
7661
+ ["Z", "xxx"],
7662
+ // Unix timestamps
7663
+ ["X", "t"],
7664
+ ["x", "T"]
7665
+ ];
7666
+ function momentToDateFns(momentFormat) {
7667
+ let result = "";
7668
+ let i = 0;
7669
+ while (i < momentFormat.length) {
7670
+ if (momentFormat[i] === "[") {
7671
+ const end = momentFormat.indexOf("]", i + 1);
7672
+ if (end !== -1) {
7673
+ const literal = momentFormat.slice(i + 1, end).replace(/'/g, "''");
7674
+ result += `'${literal}'`;
7675
+ i = end + 1;
7676
+ continue;
7677
+ }
7678
+ }
7679
+ let matched = false;
7680
+ for (const [momentToken, dateFnsToken] of TOKEN_MAP) {
7681
+ if (momentFormat.startsWith(momentToken, i)) {
7682
+ result += dateFnsToken;
7683
+ i += momentToken.length;
7684
+ matched = true;
7685
+ break;
7686
+ }
7687
+ }
7688
+ if (!matched) {
7689
+ const ch = momentFormat[i];
7690
+ if (/[a-zA-Z]/.test(ch)) {
7691
+ result += `'${ch}'`;
7692
+ } else {
7693
+ result += ch;
7694
+ }
7695
+ i++;
7696
+ }
7697
+ }
7698
+ return result;
7699
+ }
7606
7700
  const Select = SelectPrimitive.Root;
7607
7701
  const SelectValue = SelectPrimitive.Value;
7608
7702
  const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ React.createElement(
@@ -8353,8 +8447,7 @@ const DateTimePicker = React.forwardRef(
8353
8447
  );
8354
8448
  DateTimePicker.displayName = "DateTimePicker";
8355
8449
  const format$1 = (date, format2) => {
8356
- const m = moment(date);
8357
- return m.format(format2);
8450
+ return format$3(date, momentToDateFns(format2));
8358
8451
  };
8359
8452
  const DateField = wrapFieldsWithMeta(
8360
8453
  ({
@@ -9713,13 +9806,22 @@ class GlobalFormPlugin {
9713
9806
  __publicField(this, "layout");
9714
9807
  this.form = form;
9715
9808
  this.name = form.label;
9716
- this.Icon = icon || MdOutlineSettings;
9809
+ this.Icon = icon || MdOutlineLanguage;
9717
9810
  this.layout = layout || "popup";
9718
9811
  this.Component = () => {
9719
9812
  const cms = useCMS();
9720
9813
  const cmsForm = cms.state.forms.find(
9721
9814
  ({ tinaForm }) => tinaForm.id === form.id
9722
9815
  );
9816
+ React.useEffect(() => {
9817
+ if (!cmsForm) {
9818
+ cms.dispatch({ type: "forms:add", value: form });
9819
+ cms.dispatch({ type: "forms:set-active-form-id", value: form.id });
9820
+ }
9821
+ }, [cms, cmsForm]);
9822
+ if (!cmsForm) {
9823
+ return null;
9824
+ }
9723
9825
  return /* @__PURE__ */ React.createElement(FormBuilder, { form: cmsForm });
9724
9826
  };
9725
9827
  }
@@ -13159,34 +13261,10 @@ const FormModal = ({ plugin, close: close2 }) => {
13159
13261
  );
13160
13262
  return /* @__PURE__ */ React.createElement(Modal, { id: "content-creator-modal", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React.createElement(PopupModal, null, /* @__PURE__ */ React.createElement(ModalHeader, { close: close2 }, plugin.name), /* @__PURE__ */ React.createElement(ModalBody, null, /* @__PURE__ */ React.createElement(FormBuilder, { form: { tinaForm: form } }))));
13161
13263
  };
13162
- const NavContext = createContext(void 0);
13163
- const useNav = () => {
13164
- const context = useContext(NavContext);
13165
- if (!context) {
13166
- throw new Error("useNav must be used within a NavProvider");
13167
- }
13168
- return context;
13169
- };
13170
- const NavProvider = ({
13171
- children,
13172
- defaultOpen = false
13173
- }) => {
13174
- const [menuIsOpen, setMenuIsOpen] = useState(defaultOpen);
13175
- const toggleMenu = () => setMenuIsOpen((prev) => !prev);
13176
- const openMenu = () => setMenuIsOpen(true);
13177
- const closeMenu = () => setMenuIsOpen(false);
13178
- const value = {
13179
- menuIsOpen,
13180
- toggleMenu,
13181
- openMenu,
13182
- closeMenu
13183
- };
13184
- return /* @__PURE__ */ React__default.createElement(NavContext.Provider, { value }, children);
13185
- };
13186
13264
  const name = "tinacms";
13187
13265
  const type = "module";
13188
13266
  const typings = "dist/index.d.ts";
13189
- const version$1 = "3.9.2";
13267
+ const version$1 = "3.9.4";
13190
13268
  const main = "dist/index.js";
13191
13269
  const module = "./dist/index.js";
13192
13270
  const exports = {
@@ -13274,7 +13352,6 @@ const dependencies = {
13274
13352
  "@udecode/plate-slash-command": "catalog:",
13275
13353
  "@udecode/plate-table": "catalog:",
13276
13354
  "@udecode/plate-trailing-block": "catalog:",
13277
- add: "catalog:",
13278
13355
  "async-lock": "catalog:",
13279
13356
  "class-variance-authority": "catalog:",
13280
13357
  clsx: "catalog:",
@@ -13291,14 +13368,11 @@ const dependencies = {
13291
13368
  "is-hotkey": "catalog:",
13292
13369
  "lucide-react": "catalog:",
13293
13370
  mermaid: "^11.12.2",
13294
- moment: "catalog:",
13295
- "moment-timezone": "^0.6.0",
13296
13371
  "monaco-editor": "catalog:",
13297
13372
  "posthog-js": "^1.347.1",
13298
13373
  "prism-react-renderer": "catalog:",
13299
13374
  "prop-types": "catalog:",
13300
13375
  "react-colorful": "catalog:",
13301
- "react-datetime": "catalog:",
13302
13376
  "react-day-picker": "^9.13.0",
13303
13377
  "react-dnd": "catalog:",
13304
13378
  "react-dnd-html5-backend": "catalog:",
@@ -13413,6 +13487,30 @@ const LatestVersionWarning = ({
13413
13487
  }
13414
13488
  return /* @__PURE__ */ React__default.createElement("span", { className: "text-yellow-700" }, /* @__PURE__ */ React__default.createElement(TriangleAlert, { className: "w-4 h-4 inline-block mb-px" }), /* @__PURE__ */ React__default.createElement("br", null), "v", latestVersion, " published ", relativePublishedAt);
13415
13489
  };
13490
+ const NavContext = createContext(void 0);
13491
+ const useNav = () => {
13492
+ const context = useContext(NavContext);
13493
+ if (!context) {
13494
+ throw new Error("useNav must be used within a NavProvider");
13495
+ }
13496
+ return context;
13497
+ };
13498
+ const NavProvider = ({
13499
+ children,
13500
+ defaultOpen = false
13501
+ }) => {
13502
+ const [menuIsOpen, setMenuIsOpen] = useState(defaultOpen);
13503
+ const toggleMenu = () => setMenuIsOpen((prev) => !prev);
13504
+ const openMenu = () => setMenuIsOpen(true);
13505
+ const closeMenu = () => setMenuIsOpen(false);
13506
+ const value = {
13507
+ menuIsOpen,
13508
+ toggleMenu,
13509
+ openMenu,
13510
+ closeMenu
13511
+ };
13512
+ return /* @__PURE__ */ React__default.createElement(NavContext.Provider, { value }, children);
13513
+ };
13416
13514
  const useGetEvents = (cms, cursor, existingEvents) => {
13417
13515
  const [events, setEvents] = useState([]);
13418
13516
  const [nextCursor, setNextCursor] = useState(void 0);
@@ -13574,6 +13672,7 @@ const Nav = ({
13574
13672
  sidebarWidth,
13575
13673
  RenderNavSite,
13576
13674
  RenderNavCloud,
13675
+ RenderNavGlobal,
13577
13676
  RenderNavCollection,
13578
13677
  AuthRenderNavCollection,
13579
13678
  ...props
@@ -13584,17 +13683,21 @@ const Nav = ({
13584
13683
  const menuIsOpen = menuIsOpenProp ?? (navContext == null ? void 0 : navContext.menuIsOpen) ?? false;
13585
13684
  const toggleMenu = toggleMenuProp ?? (navContext == null ? void 0 : navContext.toggleMenu) ?? (() => {
13586
13685
  });
13587
- const { contentCollections, authCollection } = collectionsInfo.collections.reduce(
13686
+ const { contentCollections, authCollection, globalCollections } = collectionsInfo.collections.reduce(
13588
13687
  (acc, collection) => {
13688
+ var _a;
13589
13689
  if (collection.isAuthCollection) {
13590
13690
  acc.authCollection = collection;
13691
+ } else if ((_a = collection.ui) == null ? void 0 : _a.global) {
13692
+ acc.globalCollections.push(collection);
13591
13693
  } else {
13592
13694
  acc.contentCollections.push(collection);
13593
13695
  }
13594
13696
  return acc;
13595
13697
  },
13596
13698
  {
13597
- contentCollections: []
13699
+ contentCollections: [],
13700
+ globalCollections: []
13598
13701
  }
13599
13702
  );
13600
13703
  const screenCategories = screens.reduce(
@@ -13654,7 +13757,9 @@ const Nav = ({
13654
13757
  RenderNavCollection,
13655
13758
  collections: contentCollections
13656
13759
  }
13657
- )), (screenCategories.Site.length > 0 || contentCreators.length) > 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("h4", { className: "uppercase font-sans font-bold text-sm mb-3 mt-8 text-gray-700" }, "Site"), /* @__PURE__ */ React.createElement("ul", { className: "flex flex-col gap-4" }, screenCategories.Site.map((view) => {
13760
+ )), (globalCollections.length > 0 || screenCategories.Site.length > 0 || contentCreators.length > 0) && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("h4", { className: "uppercase font-sans font-bold text-sm mb-3 mt-8 text-gray-700" }, "Site"), /* @__PURE__ */ React.createElement("ul", { className: "flex flex-col gap-4" }, globalCollections.map((collection) => {
13761
+ return /* @__PURE__ */ React.createElement("li", { key: `nav-global-${collection.name}` }, /* @__PURE__ */ React.createElement(RenderNavGlobal, { collection }));
13762
+ }), screenCategories.Site.map((view) => {
13658
13763
  return /* @__PURE__ */ React.createElement("li", { key: `nav-site-${view.name}` }, /* @__PURE__ */ React.createElement(RenderNavSite, { view }));
13659
13764
  }), contentCreators.map((plugin, idx) => {
13660
13765
  return /* @__PURE__ */ React.createElement(
@@ -13896,7 +14001,7 @@ function BreadcrumbSeparator({
13896
14001
  className: cn("[&>svg]:size-3.5", className),
13897
14002
  ...props
13898
14003
  },
13899
- children ?? /* @__PURE__ */ React.createElement(ChevronRight, null)
14004
+ children ?? "/"
13900
14005
  );
13901
14006
  }
13902
14007
  function BreadcrumbEllipsis({
@@ -13916,6 +14021,32 @@ function BreadcrumbEllipsis({
13916
14021
  /* @__PURE__ */ React.createElement("span", { className: "sr-only" }, "More")
13917
14022
  );
13918
14023
  }
14024
+ const TooltipProvider = TooltipPrimitive.Provider;
14025
+ const Tooltip = TooltipPrimitive.Root;
14026
+ const TooltipTrigger = TooltipPrimitive.Trigger;
14027
+ const TooltipContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ React.createElement(TooltipPrimitive.Portal, null, /* @__PURE__ */ React.createElement(
14028
+ TooltipPrimitive.Content,
14029
+ {
14030
+ ref,
14031
+ sideOffset,
14032
+ className: cn(
14033
+ "z-[10000] overflow-hidden rounded bg-[#FFF] px-3 py-1.5 text-xs text-[#504E5E] shadow-sm animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin]",
14034
+ className
14035
+ ),
14036
+ ...props
14037
+ }
14038
+ )));
14039
+ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
14040
+ const documentListPath = (collectionName, filePath) => {
14041
+ const folderPath = canonicalPath(filePath).split("/").slice(0, -1).join("/");
14042
+ return `/collections/${collectionName}/~${folderPath ? `/${folderPath}` : ""}`;
14043
+ };
14044
+ const collectionListPathForDocument = (fullPath, collection) => {
14045
+ const collectionPath = canonicalPath(collection.path || "");
14046
+ const canonical = canonicalPath(fullPath);
14047
+ const relativePath = collectionPath ? canonical.slice(collectionPath.length + 1) : canonical;
14048
+ return documentListPath(collection.name, relativePath);
14049
+ };
13919
14050
  const getPaddingClass = (depth) => `${1.5 + depth * 1.35}rem`;
13920
14051
  const collectAllDocumentItems = (items, isGlobalFn) => {
13921
14052
  const allItems = [];
@@ -14314,22 +14445,6 @@ const Emoji$1 = ({ className = "", ...props }) => /* @__PURE__ */ React.createEl
14314
14445
  ...props
14315
14446
  }
14316
14447
  );
14317
- const TooltipProvider = TooltipPrimitive.Provider;
14318
- const Tooltip = TooltipPrimitive.Root;
14319
- const TooltipTrigger = TooltipPrimitive.Trigger;
14320
- const TooltipContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ React.createElement(TooltipPrimitive.Portal, null, /* @__PURE__ */ React.createElement(
14321
- TooltipPrimitive.Content,
14322
- {
14323
- ref,
14324
- sideOffset,
14325
- className: cn(
14326
- "z-[10000] overflow-hidden rounded bg-[#FFF] px-3 py-1.5 text-xs text-[#504E5E] shadow-sm animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin]",
14327
- className
14328
- ),
14329
- ...props
14330
- }
14331
- )));
14332
- TooltipContent.displayName = TooltipPrimitive.Content.displayName;
14333
14448
  const minimumTimeToShowLoadingIndicator = 1e3;
14334
14449
  const FormsView = ({ loadingPlaceholder } = {}) => {
14335
14450
  var _a, _b, _c, _d;
@@ -14434,18 +14549,42 @@ const FormsView = ({ loadingPlaceholder } = {}) => {
14434
14549
  ))
14435
14550
  ));
14436
14551
  };
14552
+ const getFilename = (path) => {
14553
+ var _a;
14554
+ return (_a = path == null ? void 0 : path.split("/").pop()) == null ? void 0 : _a.replace(/\.[^/.]+$/, "");
14555
+ };
14437
14556
  const FormHeader = ({
14438
14557
  activeForm,
14439
14558
  repoProvider,
14440
14559
  branch,
14441
14560
  isLocalMode
14442
14561
  }) => {
14562
+ var _a, _b;
14563
+ const cms = useCMS$1();
14443
14564
  const { formIsPristine } = React.useContext(SidebarContext);
14565
+ const path = activeForm.tinaForm.path;
14566
+ let collectionCrumb;
14567
+ try {
14568
+ const collection = (_b = (_a = cms.api.tina.schema) == null ? void 0 : _a.getCollectionByFullPath) == null ? void 0 : _b.call(_a, path);
14569
+ if (collection) {
14570
+ const tinaPreview = cms.flags.get("tina-preview") || false;
14571
+ const href = `${tinaPreview ? `/${tinaPreview}/index.html#` : "/admin#"}${collectionListPathForDocument(path, collection)}`;
14572
+ collectionCrumb = {
14573
+ label: collection.label || collection.name,
14574
+ onClick: () => {
14575
+ window.location.href = href;
14576
+ }
14577
+ };
14578
+ }
14579
+ } catch {
14580
+ }
14444
14581
  return /* @__PURE__ */ React.createElement("div", { className: "px-4 pt-2 pb-4 flex flex-row flex-nowrap justify-between items-center gap-2 bg-gradient-to-t from-white to-gray-50 border-b border-gray-100" }, /* @__PURE__ */ React.createElement(
14445
14582
  FormBreadcrumbs,
14446
14583
  {
14447
14584
  className: "w-[calc(100%-3rem)]",
14448
- contentPath: activeForm.tinaForm.path
14585
+ rootBreadcrumbName: getFilename(path),
14586
+ contentPath: path,
14587
+ collectionCrumb
14449
14588
  }
14450
14589
  ), /* @__PURE__ */ React.createElement(
14451
14590
  FileHistoryProvider,
@@ -14493,26 +14632,50 @@ const BreadcrumbItemLink = ({
14493
14632
  breadcrumb,
14494
14633
  onClick
14495
14634
  }) => {
14496
- return /* @__PURE__ */ React.createElement(BreadcrumbItem, { className: "shrink truncate" }, /* @__PURE__ */ React.createElement(
14635
+ return /* @__PURE__ */ React.createElement(BreadcrumbItem, { className: "shrink min-w-0" }, /* @__PURE__ */ React.createElement(
14497
14636
  BreadcrumbLink,
14498
14637
  {
14499
14638
  asChild: true,
14500
- className: "text-gray-700 truncate hover:text-orange-500"
14639
+ className: "block min-w-0 truncate text-gray-700 hover:text-orange-500"
14501
14640
  },
14502
14641
  /* @__PURE__ */ React.createElement("button", { type: "button", onClick }, breadcrumb)
14503
14642
  ));
14504
14643
  };
14644
+ const CollectionBreadcrumbItem = ({
14645
+ label,
14646
+ onClick
14647
+ }) => {
14648
+ return /* @__PURE__ */ React.createElement(BreadcrumbItem, { className: "shrink min-w-0" }, /* @__PURE__ */ React.createElement(
14649
+ BreadcrumbLink,
14650
+ {
14651
+ asChild: true,
14652
+ className: "min-w-0 text-gray-700 hover:text-orange-500"
14653
+ },
14654
+ /* @__PURE__ */ React.createElement(
14655
+ "button",
14656
+ {
14657
+ type: "button",
14658
+ onClick,
14659
+ "aria-label": `Back to ${label}`,
14660
+ className: "flex items-center gap-1.5 min-w-0"
14661
+ },
14662
+ /* @__PURE__ */ React.createElement(BiArrowBack, { className: "w-4 h-4 shrink-0 opacity-80" }),
14663
+ /* @__PURE__ */ React.createElement("span", { className: "truncate min-w-0" }, label)
14664
+ )
14665
+ ));
14666
+ };
14505
14667
  const FinalBreadcrumbItem = ({ breadcrumb }) => {
14506
- return /* @__PURE__ */ React.createElement(BreadcrumbItem, { className: "shrink truncate" }, /* @__PURE__ */ React.createElement(BreadcrumbPage, { className: "text-gray-700 font-medium cursor-default" }, breadcrumb));
14668
+ return /* @__PURE__ */ React.createElement(BreadcrumbItem, { className: "shrink min-w-0" }, /* @__PURE__ */ React.createElement(BreadcrumbPage, { className: "block min-w-0 truncate text-gray-700 font-medium cursor-default" }, breadcrumb));
14507
14669
  };
14508
14670
  const FormBreadcrumbs = ({
14509
14671
  rootBreadcrumbName,
14510
14672
  contentPath,
14673
+ collectionCrumb,
14511
14674
  ...props
14512
14675
  }) => {
14513
14676
  const cms = useCMS$1();
14514
14677
  const breadcrumbs = cms.state.breadcrumbs;
14515
- if (breadcrumbs.length === 0) {
14678
+ if (breadcrumbs.length === 0 && !collectionCrumb) {
14516
14679
  return null;
14517
14680
  }
14518
14681
  const goBack = (formId, fieldName) => {
@@ -14528,7 +14691,13 @@ const FormBreadcrumbs = ({
14528
14691
  const secondLastBreadcrumb = breadcrumbs.length > 2 ? breadcrumbs[breadcrumbs.length - 2] : null;
14529
14692
  const lastBreadcrumb = breadcrumbs.length > 1 ? breadcrumbs[breadcrumbs.length - 1] : null;
14530
14693
  const dropdownBreadcrumbs = breadcrumbs.length > 3 ? breadcrumbs.slice(1, -2) : [];
14531
- return /* @__PURE__ */ React.createElement(Breadcrumb$1, { ...props }, /* @__PURE__ */ React.createElement(BreadcrumbList, { className: "flex-nowrap text-nowrap" }, /* @__PURE__ */ React.createElement(TooltipProvider, null, /* @__PURE__ */ React.createElement(Tooltip, null, /* @__PURE__ */ React.createElement(TooltipTrigger, { asChild: true }, /* @__PURE__ */ React.createElement("span", null, breadcrumbs.length > 1 ? /* @__PURE__ */ React.createElement(
14694
+ return /* @__PURE__ */ React.createElement(Breadcrumb$1, { ...props }, /* @__PURE__ */ React.createElement(BreadcrumbList, { className: "flex-nowrap text-nowrap overflow-hidden" }, collectionCrumb && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
14695
+ CollectionBreadcrumbItem,
14696
+ {
14697
+ label: collectionCrumb.label,
14698
+ onClick: collectionCrumb.onClick
14699
+ }
14700
+ ), breadcrumbs.length > 0 && /* @__PURE__ */ React.createElement(BreadcrumbSeparator, null)), /* @__PURE__ */ React.createElement(TooltipProvider, null, /* @__PURE__ */ React.createElement(Tooltip, null, /* @__PURE__ */ React.createElement(TooltipTrigger, { asChild: true }, /* @__PURE__ */ React.createElement("span", { className: "flex min-w-0 shrink" }, breadcrumbs.length > 1 ? /* @__PURE__ */ React.createElement(
14532
14701
  BreadcrumbItemLink,
14533
14702
  {
14534
14703
  breadcrumb: rootBreadcrumbName || firstBreadcrumb.label,
@@ -14772,6 +14941,15 @@ const Sidebar$1 = ({
14772
14941
  }
14773
14942
  ),
14774
14943
  RenderNavCloud: ({ config }) => /* @__PURE__ */ React.createElement(NavCloudLink, { config }),
14944
+ RenderNavGlobal: ({ collection }) => /* @__PURE__ */ React.createElement(
14945
+ SidebarGlobalLink,
14946
+ {
14947
+ collection,
14948
+ onClick: () => {
14949
+ setMenuIsOpen(false);
14950
+ }
14951
+ }
14952
+ ),
14775
14953
  RenderNavCollection: ({ collection }) => /* @__PURE__ */ React.createElement(
14776
14954
  SidebarCollectionLink,
14777
14955
  {
@@ -14879,6 +15057,25 @@ const SidebarSiteLink = ({
14879
15057
  view.name
14880
15058
  );
14881
15059
  };
15060
+ const SidebarGlobalLink = ({
15061
+ collection,
15062
+ onClick
15063
+ }) => {
15064
+ const cms = useCMS$1();
15065
+ const tinaPreview = cms.flags.get("tina-preview") || false;
15066
+ const href = `${tinaPreview ? `/${tinaPreview}/index.html#` : "/admin#"}/collections/${collection.name}/~`;
15067
+ return /* @__PURE__ */ React.createElement(
15068
+ "a",
15069
+ {
15070
+ onClick,
15071
+ href,
15072
+ className: "text-base tracking-wide text-gray-500 hover:text-blue-600 flex items-center opacity-90 hover:opacity-100"
15073
+ },
15074
+ /* @__PURE__ */ React.createElement(MdOutlineLanguage, { className: "mr-2 h-6 opacity-80 w-auto" }),
15075
+ " ",
15076
+ collection.label ? collection.label : collection.name
15077
+ );
15078
+ };
14882
15079
  const SidebarCollectionLink = ({
14883
15080
  Icon = ImFilesEmpty,
14884
15081
  collection,
@@ -17190,27 +17387,6 @@ const insertImg = (editor, media) => {
17190
17387
  }
17191
17388
  editor.tf.normalize({ force: true });
17192
17389
  };
17193
- const EllipsisIcon = ({ title }) => {
17194
- return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, title && /* @__PURE__ */ React__default.createElement("span", { className: "sr-only" }, title), /* @__PURE__ */ React__default.createElement(
17195
- "svg",
17196
- {
17197
- xmlns: "http://www.w3.org/2000/svg",
17198
- className: "h-5 w-5",
17199
- fill: "none",
17200
- viewBox: "0 0 24 24",
17201
- stroke: "currentColor"
17202
- },
17203
- /* @__PURE__ */ React__default.createElement(
17204
- "path",
17205
- {
17206
- strokeLinecap: "round",
17207
- strokeLinejoin: "round",
17208
- strokeWidth: 2,
17209
- d: "M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z"
17210
- }
17211
- )
17212
- ));
17213
- };
17214
17390
  const Wrapper = ({ inline, children }) => {
17215
17391
  const Component = inline ? "span" : "div";
17216
17392
  return /* @__PURE__ */ React__default.createElement(
@@ -70597,6 +70773,7 @@ class AuthenticationCancelledError extends Error {
70597
70773
  const authenticate = (clientId, frontendUrl) => {
70598
70774
  return new Promise((resolve, reject) => {
70599
70775
  const origin = `${window.location.protocol}//${window.location.host}`;
70776
+ const expectedOrigin = new URL(frontendUrl).origin;
70600
70777
  const authTab = popupWindow(
70601
70778
  `${frontendUrl}/signin?clientId=${clientId}&origin=${origin}`,
70602
70779
  "_blank",
@@ -70612,10 +70789,16 @@ const authenticate = (clientId, frontendUrl) => {
70612
70789
  );
70613
70790
  return;
70614
70791
  }
70792
+ const cleanup = () => {
70793
+ clearInterval(pollInterval);
70794
+ window.removeEventListener("message", messageHandler);
70795
+ };
70615
70796
  const messageHandler = (e) => {
70797
+ if (e.origin !== expectedOrigin || e.source !== authTab) {
70798
+ return;
70799
+ }
70616
70800
  if (e.data.source === TINA_LOGIN_EVENT) {
70617
- clearInterval(pollInterval);
70618
- window.removeEventListener("message", messageHandler);
70801
+ cleanup();
70619
70802
  if (authTab) {
70620
70803
  authTab.close();
70621
70804
  }
@@ -70628,8 +70811,7 @@ const authenticate = (clientId, frontendUrl) => {
70628
70811
  };
70629
70812
  const pollInterval = setInterval(() => {
70630
70813
  if (authTab.closed) {
70631
- clearInterval(pollInterval);
70632
- window.removeEventListener("message", messageHandler);
70814
+ cleanup();
70633
70815
  reject(new AuthenticationCancelledError("Popup was closed"));
70634
70816
  }
70635
70817
  }, 500);
@@ -72565,6 +72747,14 @@ const Sidebar = ({ cms }) => {
72565
72747
  }
72566
72748
  ),
72567
72749
  RenderNavCloud: ({ config }) => /* @__PURE__ */ React__default.createElement(NavCloudLink, { config }),
72750
+ RenderNavGlobal: ({ collection }) => /* @__PURE__ */ React__default.createElement(
72751
+ SidebarLink,
72752
+ {
72753
+ label: collection.label ? collection.label : collection.name,
72754
+ to: `/collections/${collection.name}/~`,
72755
+ Icon: MdOutlineLanguage
72756
+ }
72757
+ ),
72568
72758
  RenderNavCollection: ({ collection }) => /* @__PURE__ */ React__default.createElement(
72569
72759
  SidebarLink,
72570
72760
  {
@@ -72977,12 +73167,29 @@ const CollectionListPage = () => {
72977
73167
  ),
72978
73168
  search
72979
73169
  },
72980
- (collection, _loading, reFetchCollection, collectionExtra) => {
72981
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
73170
+ (collection, loading, reFetchCollection, collectionExtra) => {
73171
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
72982
73172
  const documents = collection.documents.edges;
72983
73173
  const admin = cms.api.admin;
72984
73174
  const pageInfo = collection.documents.pageInfo;
72985
- const fields = (((_a = collectionExtra.templates) == null ? void 0 : _a.length) ? getUniqueTemplateFields(collectionExtra) : collectionExtra.fields).filter(
73175
+ if (((_a = collectionExtra == null ? void 0 : collectionExtra.ui) == null ? void 0 : _a.global) && !folder.fullyQualifiedName.startsWith("~/")) {
73176
+ if (loading || collection.name !== collectionName) {
73177
+ return /* @__PURE__ */ React__default.createElement(LoadingPage, null);
73178
+ }
73179
+ if ((documents == null ? void 0 : documents.length) === 1) {
73180
+ const globalDoc = (_b = documents[0]) == null ? void 0 : _b.node;
73181
+ if ((_c = globalDoc == null ? void 0 : globalDoc._sys) == null ? void 0 : _c.breadcrumbs) {
73182
+ return /* @__PURE__ */ React__default.createElement(
73183
+ Navigate,
73184
+ {
73185
+ replace: true,
73186
+ to: `/collections/edit/${collection.name}/${globalDoc._sys.breadcrumbs.join("/")}`
73187
+ }
73188
+ );
73189
+ }
73190
+ }
73191
+ }
73192
+ const fields = (((_d = collectionExtra.templates) == null ? void 0 : _d.length) ? getUniqueTemplateFields(collectionExtra) : collectionExtra.fields).filter(
72986
73193
  (x) => (
72987
73194
  // only allow sortable fields
72988
73195
  ["string", "number", "datetime", "boolean"].includes(x.type)
@@ -72991,15 +73198,16 @@ const CollectionListPage = () => {
72991
73198
  const sortField = fields == null ? void 0 : fields.find(
72992
73199
  (field) => field.name === sortName
72993
73200
  );
72994
- const searchEnabled = !!((_d = (_c = (_b = cms.api.tina.schema) == null ? void 0 : _b.config) == null ? void 0 : _c.config) == null ? void 0 : _d.search);
73201
+ const searchEnabled = !!((_g = (_f = (_e = cms.api.tina.schema) == null ? void 0 : _e.config) == null ? void 0 : _f.config) == null ? void 0 : _g.search);
72995
73202
  const collectionDefinition = cms.api.tina.schema.getCollection(
72996
73203
  collection.name
72997
73204
  );
72998
- const parse2 = (_f = (_e = collectionDefinition == null ? void 0 : collectionDefinition.ui) == null ? void 0 : _e.filename) == null ? void 0 : _f.parse;
72999
- const allowCreate = ((_h = (_g = collectionDefinition == null ? void 0 : collectionDefinition.ui) == null ? void 0 : _g.allowedActions) == null ? void 0 : _h.create) ?? true;
73000
- const allowDelete = ((_j = (_i = collectionDefinition == null ? void 0 : collectionDefinition.ui) == null ? void 0 : _i.allowedActions) == null ? void 0 : _j.delete) ?? true;
73001
- const allowCreateFolder = ((_l = (_k = collectionDefinition == null ? void 0 : collectionDefinition.ui) == null ? void 0 : _k.allowedActions) == null ? void 0 : _l.createFolder) ?? true;
73002
- const allowCreateNestedFolder = ((_n = (_m = collectionDefinition == null ? void 0 : collectionDefinition.ui) == null ? void 0 : _m.allowedActions) == null ? void 0 : _n.createNestedFolder) ?? true;
73205
+ const parse2 = (_i = (_h = collectionDefinition == null ? void 0 : collectionDefinition.ui) == null ? void 0 : _h.filename) == null ? void 0 : _i.parse;
73206
+ const isGlobalCollection = !!((_j = collectionExtra == null ? void 0 : collectionExtra.ui) == null ? void 0 : _j.global);
73207
+ const allowCreate = (((_l = (_k = collectionDefinition == null ? void 0 : collectionDefinition.ui) == null ? void 0 : _k.allowedActions) == null ? void 0 : _l.create) ?? true) && (!isGlobalCollection || documents.length === 0);
73208
+ const allowDelete = ((_n = (_m = collectionDefinition == null ? void 0 : collectionDefinition.ui) == null ? void 0 : _m.allowedActions) == null ? void 0 : _n.delete) ?? true;
73209
+ const allowCreateFolder = !isGlobalCollection && (((_p = (_o = collectionDefinition == null ? void 0 : collectionDefinition.ui) == null ? void 0 : _o.allowedActions) == null ? void 0 : _p.createFolder) ?? true);
73210
+ const allowCreateNestedFolder = !isGlobalCollection && (((_r = (_q = collectionDefinition == null ? void 0 : collectionDefinition.ui) == null ? void 0 : _q.allowedActions) == null ? void 0 : _r.createNestedFolder) ?? true);
73003
73211
  const folderView = folder.fullyQualifiedName !== "";
73004
73212
  return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, deleteModalOpen && !cms.api.tina.usingProtectedBranch() && /* @__PURE__ */ React__default.createElement(
73005
73213
  DeleteModal,
@@ -73119,7 +73327,7 @@ const CollectionListPage = () => {
73119
73327
  return { ...vars2, folderName };
73120
73328
  });
73121
73329
  },
73122
- validationRegex: (_q = (_p = (_o = cms.api.tina) == null ? void 0 : _o.schema.config.config.ui) == null ? void 0 : _p.regexValidation) == null ? void 0 : _q.folderNameRegex,
73330
+ validationRegex: (_u = (_t = (_s = cms.api.tina) == null ? void 0 : _s.schema.config.config.ui) == null ? void 0 : _t.regexValidation) == null ? void 0 : _u.folderNameRegex,
73123
73331
  createFunc: async () => {
73124
73332
  try {
73125
73333
  await admin.createFolder(
@@ -73219,7 +73427,7 @@ const CollectionListPage = () => {
73219
73427
  {
73220
73428
  cms,
73221
73429
  collectionName,
73222
- loading: _loading,
73430
+ loading,
73223
73431
  search,
73224
73432
  setSearch,
73225
73433
  searchInput,
@@ -74293,6 +74501,7 @@ const RenderForm$1 = ({
74293
74501
  const activeForm = cms.state.forms.find(
74294
74502
  ({ tinaForm }) => tinaForm.id === form.id
74295
74503
  );
74504
+ const collectionListPath = `/collections/${collection.name}${folder.fullyQualifiedName ? `/${folder.fullyQualifiedName}` : ""}`;
74296
74505
  return /* @__PURE__ */ React__default.createElement(PageWrapper, { headerClassName: "bg-white" }, /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(
74297
74506
  "div",
74298
74507
  {
@@ -74302,7 +74511,11 @@ const RenderForm$1 = ({
74302
74511
  FormBreadcrumbs,
74303
74512
  {
74304
74513
  className: "w-[calc(100%-3rem)]",
74305
- rootBreadcrumbName: "Create New"
74514
+ rootBreadcrumbName: "Create New",
74515
+ collectionCrumb: {
74516
+ label: collection.label || collection.name,
74517
+ onClick: () => navigate(collectionListPath)
74518
+ }
74306
74519
  }
74307
74520
  ), /* @__PURE__ */ React__default.createElement(FormStatus, { pristine: formIsPristine }))
74308
74521
  ), activeForm && /* @__PURE__ */ React__default.createElement(FormBuilder, { form: activeForm, onPristineChange: setFormIsPristine })));
@@ -74471,7 +74684,9 @@ const RenderForm = ({
74471
74684
  mutationInfo
74472
74685
  }) => {
74473
74686
  var _a, _b, _c, _d;
74687
+ const navigate = useNavigate();
74474
74688
  const [formIsPristine, setFormIsPristine] = useState(true);
74689
+ const collectionListPath = documentListPath(collection.name, relativePath);
74475
74690
  const schema = cms.api.tina.schema;
74476
74691
  const schemaCollection = schema.getCollection(collection.name);
74477
74692
  const template = schema.getTemplateForData({
@@ -74540,7 +74755,11 @@ const RenderForm = ({
74540
74755
  FormBreadcrumbs,
74541
74756
  {
74542
74757
  className: "w-[calc(100%-3rem)]",
74543
- rootBreadcrumbName: `${filename}.${collection.format}`
74758
+ rootBreadcrumbName: getFilename(filename),
74759
+ collectionCrumb: {
74760
+ label: collection.label || collection.name,
74761
+ onClick: () => navigate(collectionListPath)
74762
+ }
74544
74763
  }
74545
74764
  ), /* @__PURE__ */ React__default.createElement(
74546
74765
  FileHistoryProvider,
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ export declare function TinaAdminOriginProvider(props: {
3
+ origin: string | string[];
4
+ children: React.ReactNode;
5
+ }): React.ReactElement;
6
+ export declare function useTrustedAdminOrigins(): string[];
7
+ export declare function isFromAdmin(event: MessageEvent, trustedOrigins: string[]): boolean;
package/dist/react.js CHANGED
@@ -3,6 +3,25 @@ import { addMetadata as addMetadata2, hashFromQuery as hashFromQuery2 } from "@t
3
3
  import { QUICK_EDIT_CSS } from "@tinacms/bridge/quick-edit-css";
4
4
  import React from "react";
5
5
  import { tinaField } from "@tinacms/bridge/tina-field";
6
+ const TinaAdminOriginContext = React.createContext(
7
+ null
8
+ );
9
+ function useTrustedAdminOrigins() {
10
+ const configured = React.useContext(TinaAdminOriginContext);
11
+ return React.useMemo(() => {
12
+ if (configured == null) {
13
+ return typeof window !== "undefined" ? [window.location.origin] : [];
14
+ }
15
+ return Array.isArray(configured) ? [...configured] : [configured];
16
+ }, [configured]);
17
+ }
18
+ function isFromAdmin(event, trustedOrigins) {
19
+ if (typeof window === "undefined")
20
+ return false;
21
+ if (!trustedOrigins.includes(event.origin))
22
+ return false;
23
+ return event.source === window.parent;
24
+ }
6
25
  function useTina(props) {
7
26
  const stringifiedQuery = JSON.stringify({
8
27
  query: props.query,
@@ -18,6 +37,7 @@ function useTina(props) {
18
37
  return addMetadata(id, dataCopy, []);
19
38
  }
20
39
  }, [props.data, id]);
40
+ const trustedAdminOrigins = useTrustedAdminOrigins();
21
41
  const [data, setData] = React.useState(processedData);
22
42
  const [isClient, setIsClient] = React.useState(false);
23
43
  const [quickEditEnabled, setQuickEditEnabled] = React.useState(false);
@@ -89,6 +109,8 @@ function useTina(props) {
89
109
  const { experimental___selectFormByFormId, ...rest } = props;
90
110
  parent.postMessage({ type: "open", ...rest, id }, window.location.origin);
91
111
  const handleMessage = (event) => {
112
+ if (!isFromAdmin(event, trustedAdminOrigins))
113
+ return;
92
114
  if (event.data.type === "quickEditEnabled") {
93
115
  setQuickEditEnabled(event.data.value);
94
116
  }
@@ -120,7 +142,7 @@ function useTina(props) {
120
142
  window.removeEventListener("message", handleMessage);
121
143
  parent.postMessage({ type: "close", id }, window.location.origin);
122
144
  };
123
- }, [id, setQuickEditEnabled]);
145
+ }, [id, setQuickEditEnabled, trustedAdminOrigins]);
124
146
  return { data, isClient };
125
147
  }
126
148
  function useEditState() {
@@ -1,3 +1,4 @@
1
+ import { sanitizeUrl } from "@tinacms/mdx";
1
2
  import React from "react";
2
3
  const TinaMarkdown = ({
3
4
  content,
@@ -122,7 +123,7 @@ const Node = ({ components, child }) => {
122
123
  const Component2 = components[child.type];
123
124
  return /* @__PURE__ */ React.createElement(Component2, { ...props });
124
125
  }
125
- return /* @__PURE__ */ React.createElement("img", { src: child.url, alt: child.alt });
126
+ return /* @__PURE__ */ React.createElement("img", { src: sanitizeUrl(child.url), alt: child.alt });
126
127
  case "a":
127
128
  if (components[child.type]) {
128
129
  const Component2 = components[child.type];
@@ -133,7 +134,7 @@ const Node = ({ components, child }) => {
133
134
  }
134
135
  return (
135
136
  // @ts-ignore FIXME: TinaMarkdownContent needs to be a union of all possible node types
136
- /* @__PURE__ */ React.createElement("a", { href: child.url }, /* @__PURE__ */ React.createElement(TinaMarkdown, { components, content: children }))
137
+ /* @__PURE__ */ React.createElement("a", { href: sanitizeUrl(child.url) }, /* @__PURE__ */ React.createElement(TinaMarkdown, { components, content: children }))
137
138
  );
138
139
  case "code_block": {
139
140
  let codeString = "";
@@ -1,3 +1,4 @@
1
+ import { sanitizeUrl } from "@tinacms/mdx";
1
2
  import React from "react";
2
3
  const StaticTinaMarkdown = ({
3
4
  content,
@@ -91,7 +92,7 @@ const Node = ({
91
92
  const Component2 = components[child.type];
92
93
  return /* @__PURE__ */ React.createElement(Component2, { ...props });
93
94
  }
94
- return /* @__PURE__ */ React.createElement("img", { src: child.url, alt: child.alt });
95
+ return /* @__PURE__ */ React.createElement("img", { src: sanitizeUrl(child.url), alt: child.alt });
95
96
  case "a":
96
97
  if (components[child.type]) {
97
98
  const Component2 = components[child.type];
@@ -100,7 +101,7 @@ const Node = ({
100
101
  /* @__PURE__ */ React.createElement(Component2, { ...props }, /* @__PURE__ */ React.createElement(StaticTinaMarkdown, { components, content: children }))
101
102
  );
102
103
  }
103
- return /* @__PURE__ */ React.createElement("a", { href: child.url }, /* @__PURE__ */ React.createElement(StaticTinaMarkdown, { components, content: children }));
104
+ return /* @__PURE__ */ React.createElement("a", { href: sanitizeUrl(child.url) }, /* @__PURE__ */ React.createElement(StaticTinaMarkdown, { components, content: children }));
104
105
  case "code_block": {
105
106
  let codeString = "";
106
107
  if (Array.isArray(child.children)) {
@@ -1,4 +1,3 @@
1
- import 'moment-timezone';
2
1
  import * as React from 'react';
3
2
  import { type DayPickerProps } from 'react-day-picker';
4
3
  type TimePickerType = 'minutes' | 'seconds' | 'hours' | '12hours';
@@ -106,6 +105,6 @@ declare const DateTimePicker: React.ForwardRefExoticComponent<{
106
105
  * Show the default month and time when popup the calendar. Default is the current Date().
107
106
  **/
108
107
  defaultPopupValue?: Date;
109
- } & Pick<DayPickerProps, "showOutsideDays" | "showWeekNumber" | "locale" | "weekStartsOn"> & React.RefAttributes<Partial<DateTimePickerRef>>>;
108
+ } & Pick<DayPickerProps, "locale" | "showOutsideDays" | "showWeekNumber" | "weekStartsOn"> & React.RefAttributes<Partial<DateTimePickerRef>>>;
110
109
  export { DateTimePicker, TimePickerInput, TimePicker, formatCurrentDate };
111
110
  export type { TimePickerType, DateTimePickerProps, DateTimePickerRef };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Converts a moment.js format string to the equivalent date-fns format string.
3
+ * This is a non-breaking shim so callers that pass moment tokens (the public API
4
+ * contract for the date field's dateFormat/timeFormat options) continue to work
5
+ * after moment has been removed.
6
+ */
7
+ export declare function momentToDateFns(momentFormat: string): string;
@@ -1,12 +1,12 @@
1
1
  import React from 'react';
2
2
  import { type InputProps } from '../components';
3
- import type { DatetimepickerProps } from 'react-datetime';
4
- export declare const DateField: (props: import("./wrap-field-with-meta").InputFieldType<InputProps, DatetimepickerProps>) => React.JSX.Element;
3
+ import { type DateFieldProps } from './date-format';
4
+ export declare const DateField: (props: import("./wrap-field-with-meta").InputFieldType<InputProps, DateFieldProps>) => React.JSX.Element;
5
5
  export declare const DateFieldPlugin: {
6
6
  __type: string;
7
7
  name: string;
8
- Component: (props: import("./wrap-field-with-meta").InputFieldType<InputProps, DatetimepickerProps>) => React.JSX.Element;
9
- format: (val: string, _name: string, field: DatetimepickerProps) => string;
8
+ Component: (props: import("./wrap-field-with-meta").InputFieldType<InputProps, DateFieldProps>) => React.JSX.Element;
9
+ format: (val: string, _name: string, field: DateFieldProps) => string;
10
10
  parse: (val: string) => string;
11
11
  validate(value: any, values: any, meta: any, field: any): string;
12
12
  };
@@ -1,5 +1,13 @@
1
- import type { DatetimepickerProps } from 'react-datetime';
1
+ export interface DateFieldProps {
2
+ dateFormat?: string | boolean;
3
+ timeFormat?: string | boolean;
4
+ locale?: string;
5
+ experimental_focusIntent?: boolean;
6
+ onChange?: (value: unknown) => void;
7
+ required?: boolean;
8
+ [key: string]: unknown;
9
+ }
2
10
  export declare const DEFAULT_DATE_DISPLAY_FORMAT = "MMM DD, YYYY";
3
11
  export declare const DEFAULT_TIME_DISPLAY_FORMAT = "h:mm A";
4
- export declare const format: (val: string, _name: string, field: DatetimepickerProps) => string;
12
+ export declare const format: (val: string, _name: string, field: DateFieldProps) => string;
5
13
  export declare const parse: (val: string) => string;
@@ -0,0 +1,5 @@
1
+ export declare const documentListPath: (collectionName: string, filePath: string) => string;
2
+ export declare const collectionListPathForDocument: (fullPath: string, collection: {
3
+ name: string;
4
+ path?: string;
5
+ }) => string;
@@ -5,6 +5,9 @@ interface NavCollection {
5
5
  label?: string;
6
6
  name: string;
7
7
  isAuthCollection?: boolean;
8
+ ui?: {
9
+ global?: boolean | object;
10
+ };
8
11
  }
9
12
  interface NavProps {
10
13
  isLocalMode: boolean;
@@ -28,6 +31,12 @@ interface NavProps {
28
31
  RenderNavCloud: React.ComponentType<{
29
32
  config: CloudConfigPlugin;
30
33
  }>;
34
+ RenderNavGlobal: React.ComponentType<{
35
+ collection: {
36
+ label?: string;
37
+ name: string;
38
+ };
39
+ }>;
31
40
  RenderNavCollection: React.ComponentType<{
32
41
  collection: {
33
42
  label: string;
@@ -41,5 +50,5 @@ interface NavProps {
41
50
  };
42
51
  }>;
43
52
  }
44
- export declare const Nav: ({ isLocalMode, showHamburger, menuIsOpen: menuIsOpenProp, toggleMenu: toggleMenuProp, className, children, showCollections, collectionsInfo, screens, cloudConfigs, contentCreators, sidebarWidth, RenderNavSite, RenderNavCloud, RenderNavCollection, AuthRenderNavCollection, ...props }: NavProps) => React.JSX.Element;
53
+ export declare const Nav: ({ isLocalMode, showHamburger, menuIsOpen: menuIsOpenProp, toggleMenu: toggleMenuProp, className, children, showCollections, collectionsInfo, screens, cloudConfigs, contentCreators, sidebarWidth, RenderNavSite, RenderNavCloud, RenderNavGlobal, RenderNavCollection, AuthRenderNavCollection, ...props }: NavProps) => React.JSX.Element;
45
54
  export {};
@@ -21,6 +21,7 @@ export interface FormHeaderProps {
21
21
  };
22
22
  };
23
23
  }
24
+ export declare const getFilename: (path?: string) => string;
24
25
  export declare const FormHeader: ({ activeForm, repoProvider, branch, isLocalMode, }: FormHeaderProps) => React.JSX.Element;
25
26
  interface RepositoryProviderProps {
26
27
  contentRelativePath: string;
@@ -35,8 +36,12 @@ interface RepositoryProviderProps {
35
36
  };
36
37
  }
37
38
  export declare const FileHistoryProvider: ({ contentRelativePath, tinaBranch, defaultBranchName, historyUrl, isLocalMode, }: RepositoryProviderProps) => React.JSX.Element;
38
- export declare const FormBreadcrumbs: ({ rootBreadcrumbName, contentPath, ...props }: {
39
+ export declare const FormBreadcrumbs: ({ rootBreadcrumbName, contentPath, collectionCrumb, ...props }: {
39
40
  rootBreadcrumbName?: string;
40
41
  contentPath?: string;
42
+ collectionCrumb?: {
43
+ label: string;
44
+ onClick: () => void;
45
+ };
41
46
  } & React.HTMLAttributes<HTMLDivElement>) => React.JSX.Element;
42
47
  export {};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "tinacms",
3
3
  "type": "module",
4
4
  "typings": "dist/index.d.ts",
5
- "version": "3.9.2",
5
+ "version": "3.9.4",
6
6
  "main": "dist/index.js",
7
7
  "module": "./dist/index.js",
8
8
  "exports": {
@@ -80,7 +80,6 @@
80
80
  "@udecode/plate-slash-command": "^48.0.0",
81
81
  "@udecode/plate-table": "^48.0.0",
82
82
  "@udecode/plate-trailing-block": "^48.0.0",
83
- "add": "^2.0.6",
84
83
  "async-lock": "^1.4.1",
85
84
  "class-variance-authority": "^0.7.1",
86
85
  "clsx": "^2.1.1",
@@ -97,14 +96,11 @@
97
96
  "is-hotkey": "^0.2.0",
98
97
  "lucide-react": "^0.424.0",
99
98
  "mermaid": "^11.12.2",
100
- "moment": "2.29.4",
101
- "moment-timezone": "^0.6.0",
102
99
  "monaco-editor": "0.31.0",
103
100
  "posthog-js": "^1.347.1",
104
101
  "prism-react-renderer": "^2.4.1",
105
102
  "prop-types": "15.7.2",
106
103
  "react-colorful": "^5.6.1",
107
- "react-datetime": "^3.3.1",
108
104
  "react-day-picker": "^9.13.0",
109
105
  "react-dnd": "^16.0.1",
110
106
  "react-dnd-html5-backend": "^16.0.1",
@@ -118,9 +114,9 @@
118
114
  "yup": "^1.6.1",
119
115
  "zod": "^3.24.2",
120
116
  "@tinacms/bridge": "0.3.0",
121
- "@tinacms/mdx": "2.1.6",
122
- "@tinacms/schema-tools": "2.8.1",
123
- "@tinacms/search": "1.2.18"
117
+ "@tinacms/search": "1.2.20",
118
+ "@tinacms/mdx": "2.1.8",
119
+ "@tinacms/schema-tools": "2.8.2"
124
120
  },
125
121
  "devDependencies": {
126
122
  "@graphql-tools/utils": "^10.8.1",
@@ -148,7 +144,7 @@
148
144
  "typescript": "^5.7.3",
149
145
  "vite": "^5.4.14",
150
146
  "vitest": "^2.1.9",
151
- "@tinacms/scripts": "1.6.1"
147
+ "@tinacms/scripts": "1.6.2"
152
148
  },
153
149
  "peerDependencies": {
154
150
  "react": ">=16.14.0",
@@ -1,24 +0,0 @@
1
- import React from 'react';
2
- export declare const ToolbarIcon: ({ name }: {
3
- name: string;
4
- }) => any;
5
- export declare const EllipsisIcon: ({ title }: {
6
- title: any;
7
- }) => React.JSX.Element;
8
- export declare function UnorderedListIcon(props: any): React.JSX.Element;
9
- export declare function HeadingIcon(props: any): React.JSX.Element;
10
- export declare function OrderedListIcon(props: any): React.JSX.Element;
11
- export declare function QuoteIcon(props: any): React.JSX.Element;
12
- export declare function LinkIcon(props: any): React.JSX.Element;
13
- export declare function CodeIcon(props: any): React.JSX.Element;
14
- export declare function CodeBlockIcon(props: any): React.JSX.Element;
15
- export declare function ImageIcon(props: any): React.JSX.Element;
16
- export declare function BoldIcon(props: any): React.JSX.Element;
17
- export declare function ItalicIcon(props: any): React.JSX.Element;
18
- export declare function UnderlineIcon(props: any): React.JSX.Element;
19
- export declare function StrikethroughIcon(props: any): React.JSX.Element;
20
- export declare function LightningIcon(props: any): React.JSX.Element;
21
- export declare function ArrowDownIcon(props: any): React.JSX.Element;
22
- export declare function PlusIcon({ className }: {
23
- className?: string;
24
- }): React.JSX.Element;
@@ -1,9 +0,0 @@
1
- export default function ViewNavigation({ onClickPrev, onClickSwitch, onClickNext, switchContent, switchColSpan, switchProps, }: {
2
- onClickPrev: any;
3
- onClickSwitch: any;
4
- onClickNext: any;
5
- switchContent: any;
6
- switchColSpan: any;
7
- switchProps: any;
8
- }): React.JSX.Element;
9
- import React from 'react';
@@ -1,16 +0,0 @@
1
- export default class DaysView extends React.Component<any, any, any> {
2
- static defaultProps: {
3
- isValidDate: () => boolean;
4
- renderDay: (props: any, date: any) => React.JSX.Element;
5
- };
6
- constructor(props: any);
7
- constructor(props: any, context: any);
8
- render(): React.JSX.Element;
9
- renderNavigation(): React.JSX.Element;
10
- renderDayHeaders(): React.JSX.Element;
11
- renderDays(): React.JSX.Element[];
12
- renderDay(date: any, startOfMonth: any, endOfMonth: any): any;
13
- renderFooter(): React.JSX.Element;
14
- _setDate: (e: any) => void;
15
- }
16
- import React from 'react';
@@ -1,12 +0,0 @@
1
- export default class MonthsView extends React.Component<any, any, any> {
2
- constructor(props: any);
3
- constructor(props: any, context: any);
4
- render(): React.JSX.Element;
5
- renderNavigation(): React.JSX.Element;
6
- renderMonths(): React.JSX.Element[];
7
- renderMonth(month: any): any;
8
- isDisabledMonth(month: any): boolean;
9
- getMonthText(month: any): any;
10
- _updateSelectedMonth: (event: any) => void;
11
- }
12
- import React from 'react';
@@ -1,32 +0,0 @@
1
- export default class TimeView extends React.Component<any, any, any> {
2
- constructor(props: any);
3
- constraints: {};
4
- state: {
5
- hours: string;
6
- minutes: string;
7
- seconds: string;
8
- milliseconds: string;
9
- ampm: string;
10
- };
11
- render(): React.JSX.Element;
12
- renderCounter(type: any, value: any): React.JSX.Element;
13
- renderHeader(): React.JSX.Element;
14
- onStartClicking(e: any, action: any, type: any): void;
15
- timer: NodeJS.Timeout;
16
- increaseTimer: NodeJS.Timeout;
17
- mouseUpListener: any;
18
- toggleDayPart(): void;
19
- increase(type: any): string;
20
- decrease(type: any): string;
21
- getCounters(): string[];
22
- isAMPM(): boolean;
23
- getTimeParts(date: any): {
24
- hours: string;
25
- minutes: string;
26
- seconds: string;
27
- milliseconds: string;
28
- ampm: string;
29
- };
30
- componentDidUpdate(prevProps: any): void;
31
- }
32
- import React from 'react';
@@ -1,17 +0,0 @@
1
- export default class YearsView extends React.Component<any, any, any> {
2
- static defaultProps: {
3
- renderYear: (props: any, year: any) => React.JSX.Element;
4
- };
5
- constructor(props: any);
6
- constructor(props: any, context: any);
7
- render(): React.JSX.Element;
8
- renderNavigation(): React.JSX.Element;
9
- renderYears(): React.JSX.Element[];
10
- renderYear(year: any): any;
11
- getViewYear(): number;
12
- getSelectedYear(): any;
13
- disabledYearsCache: {};
14
- isDisabledYear(year: any): any;
15
- _updateSelectedYear: (event: any) => void;
16
- }
17
- import React from 'react';