tinacms 3.9.3 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import React__default, { useState, useCallback, useRef, useEffect, createContext
10
10
  import { createPortal } from "react-dom";
11
11
  import { clsx } from "clsx";
12
12
  import { twMerge } from "tailwind-merge";
13
- import { withRef, cn as cn$1, withCn, createPrimitiveElement, withVariants, withProps, useComposedRef as useComposedRef$1, PortalBody } from "@udecode/cn";
13
+ import { withRef, cn as cn$1, withCn, createPrimitiveElement, withVariants, withProps, PortalBody, useComposedRef as useComposedRef$1 } from "@udecode/cn";
14
14
  import { CodePlugin, UnderlinePlugin, StrikethroughPlugin, ItalicPlugin, BoldPlugin, BasicMarksPlugin } from "@udecode/plate-basic-marks/react";
15
15
  import { BlockquotePlugin } from "@udecode/plate-block-quote/react";
16
16
  import { CodeBlockPlugin, CodeLinePlugin, CodeSyntaxPlugin } from "@udecode/plate-code-block/react";
@@ -23,7 +23,7 @@ import { SlashInputPlugin, SlashPlugin } from "@udecode/plate-slash-command/reac
23
23
  import { TablePlugin, TableRowPlugin, useTableCellElement, TableProvider, useTableElement, useTableMergeState, useTableBordersDropdownMenuContentState, TableCellHeaderPlugin, TableCellPlugin } from "@udecode/plate-table/react";
24
24
  import { PlateElement, useReadOnly, useElement, PlateLeaf, useSelected, useFocused, useEditorRef, withRef as withRef$1, useComposedRef, useEditorPlugin, useElementSelector, withHOC, usePluginOption, useRemoveNodeButton, useEditorSelector, ParagraphPlugin, createPlatePlugin, PlateContent, PlateContainer, useEditorState, useMarkToolbarButtonState, useMarkToolbarButton, usePlateEditor, useFormInputProps, useEditorSelection, useEditorReadOnly, useOnClickOutside, useEventEditorValue, Plate } from "@udecode/plate/react";
25
25
  import { isLangSupported, formatCodeBlock, insertEmptyCodeBlock, unwrapCodeBlock, isCodeBlockEmpty, isSelectionAtCodeBlockStart } from "@udecode/plate-code-block";
26
- import { X, Search, ChevronDown, Check, AlertTriangle, BracesIcon, Plus, AlignCenter as AlignCenter$1, AlignJustify, AlignLeft as AlignLeft$1, AlignRight as AlignRight$1, PaintBucket, Quote, ChevronRight, ChevronsUpDown, FileCode, Baseline, RectangleVertical, Combine, Ungroup, MessageSquare, MessageSquarePlus, Trash, GripVertical, Edit2, Smile, ExternalLink, Heading1 as Heading1$1, Heading2 as Heading2$1, Heading3 as Heading3$1, Heading4 as Heading4$1, Heading5 as Heading5$1, Heading6 as Heading6$1, Indent, Keyboard, WrapText, Minus, MoreHorizontal, Outdent, Pilcrow, RotateCcw, RectangleHorizontal, Settings, Highlighter, Strikethrough, Subscript, Superscript, Table as Table$1, Text, Underline, Link2Off, Eye, SeparatorHorizontal, Moon, SunMedium, Twitter, PaintBucketIcon, CombineIcon, SquareSplitHorizontalIcon, Grid2X2Icon, Trash2Icon, ArrowUp, ArrowDown, XIcon, ArrowLeft, ArrowRight, EraserIcon, ChevronDownIcon as ChevronDownIcon$1, ChevronUp, Clock, CalendarCheck, Calendar as Calendar$1, CalendarDays, RotateCw, ChevronLeft, ArrowUpDown, AlertCircle, Image, RefreshCw, Database, CheckCircle2, ImageOff, LoaderCircle, TriangleAlert, FileStack, History, GitBranchIcon, List as List$1, ListOrdered, Grid3x3Icon, CircleX, Link, Unlink } from "lucide-react";
26
+ import { X, Search, ChevronDown, Check, AlertTriangle, BracesIcon, Plus, AlignCenter as AlignCenter$1, AlignJustify, AlignLeft as AlignLeft$1, AlignRight as AlignRight$1, PaintBucket, Quote, ChevronRight, ChevronsUpDown, FileCode, Baseline, RectangleVertical, Combine, Ungroup, MessageSquare, MessageSquarePlus, Trash, GripVertical, Edit2, Smile, ExternalLink, Heading1 as Heading1$1, Heading2 as Heading2$1, Heading3 as Heading3$1, Heading4 as Heading4$1, Heading5 as Heading5$1, Heading6 as Heading6$1, Indent, Keyboard, WrapText, Minus, MoreHorizontal, Outdent, Pilcrow, RotateCcw, RectangleHorizontal, Settings, Highlighter, Strikethrough, Subscript, Superscript, Table as Table$1, Text, Underline, Link2Off, Eye, SeparatorHorizontal, Moon, SunMedium, Twitter, PaintBucketIcon, CombineIcon, SquareSplitHorizontalIcon, Grid2X2Icon, Trash2Icon, ArrowUp, ArrowDown, XIcon, ArrowLeft, ArrowRight, EraserIcon, ChevronDownIcon as ChevronDownIcon$1, ChevronUp, Clock, CalendarCheck, Calendar as Calendar$1, CalendarDays, RotateCw, ChevronLeft, ArrowUpDown, AlertCircle, Image, RefreshCw, Database, CheckCircle2, ImageOff, LoaderCircle, TriangleAlert, FileStack, History, GitBranchIcon, FileText, Globe, List as List$1, ListOrdered, Grid3x3Icon, CircleX, Link, Unlink } from "lucide-react";
27
27
  import mermaid from "mermaid";
28
28
  import { Slot } from "@radix-ui/react-slot";
29
29
  import { cva } from "class-variance-authority";
@@ -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, ASYNC_POLLER_ERROR, canonicalPath, ERR_BRANCH_EXISTS, ERR_BRANCH_CONFLICT, isHeadingLevel, TinaSchema, addNamespaceToSchema, parseURL, ERR_HAS_REFERENCES, ERR_ALREADY_EXISTS, isValidRelativePath, RELATIVE_PATH_ALLOWED_CHARS_MESSAGE, resolveForm, RELATIVE_PATH_REGEX, normalizePath, ERR_NOT_INDEXED, 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,11 +63,11 @@ 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";
70
- import { Popover as Popover$4, PopoverButton, Transition, PopoverPanel, TransitionChild, Disclosure, DisclosureButton, DisclosurePanel, Menu, MenuButton, MenuItems, MenuItem } from "@headlessui/react";
70
+ import { Popover as Popover$4, PopoverButton, Transition, PopoverPanel, TransitionChild, Disclosure, DisclosureButton, DisclosurePanel, Menu, MenuButton, MenuItems, MenuItem as MenuItem$1 } from "@headlessui/react";
71
71
  import { isHotkey } from "is-hotkey";
72
72
  import { AutoformatPlugin } from "@udecode/plate-autoformat/react";
73
73
  import { ExitBreakPlugin, SoftBreakPlugin } from "@udecode/plate-break/react";
@@ -77,17 +77,15 @@ import { BaseBlockquotePlugin } from "@udecode/plate-block-quote";
77
77
  import { HeadingPlugin } from "@udecode/plate-heading/react";
78
78
  import { IndentListPlugin } from "@udecode/plate-indent-list/react";
79
79
  import { NodeIdPlugin } from "@udecode/plate-node-id";
80
- import { offset, flip, useVirtualFloating, getSelectionBoundingClientRect } from "@udecode/plate-floating";
80
+ import { offset, flip, shift, limitShift, useVirtualFloating, getSelectionBoundingClientRect } from "@udecode/plate-floating";
81
81
  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(
@@ -2261,6 +2280,7 @@ const BranchSwitcherDropDownEvent = "branch-switcher-dropdown";
2261
2280
  const BranchSwitcherPRClickedEvent = "branch-switcher-pr-clicked";
2262
2281
  const SavedContentEvent = "saved-content";
2263
2282
  const SaveContentErrorEvent = "save-content-error";
2283
+ const EditorialWorkflowSaveEvent = "editorial-workflow-save";
2264
2284
  const FormResetEvent = "form-reset";
2265
2285
  const MediaManagerContentUploadedEvent = "media-manager-content-uploaded";
2266
2286
  const MediaManagerContentDeletedEvent = "media-manager-content-deleted";
@@ -4474,6 +4494,21 @@ function DropdownMenuSeparator({
4474
4494
  }
4475
4495
  );
4476
4496
  }
4497
+ const MenuItem = ({ item }) => {
4498
+ const menuItem = /* @__PURE__ */ React.createElement(
4499
+ DropdownMenuItem,
4500
+ {
4501
+ onClick: item.onClick,
4502
+ disabled: item.disabled,
4503
+ variant: item.variant
4504
+ },
4505
+ item.icon && item.icon,
4506
+ item.label
4507
+ );
4508
+ if (!item.tooltip)
4509
+ return menuItem;
4510
+ return /* @__PURE__ */ React.createElement(Tooltip$1, null, /* @__PURE__ */ React.createElement(TooltipTrigger$1, { asChild: true }, /* @__PURE__ */ React.createElement("div", null, menuItem)), /* @__PURE__ */ React.createElement(TooltipPortal, null, /* @__PURE__ */ React.createElement(TooltipContent$1, { side: "left" }, item.tooltip)));
4511
+ };
4477
4512
  const DropdownButton = React.forwardRef(
4478
4513
  ({
4479
4514
  variant = "primary",
@@ -4511,17 +4546,7 @@ const DropdownButton = React.forwardRef(
4511
4546
  )
4512
4547
  }
4513
4548
  )
4514
- )), /* @__PURE__ */ React.createElement(DropdownMenuContent, { align: "end", side: "bottom", className: "z-[100000]" }, items.map((item, index) => /* @__PURE__ */ React.createElement(
4515
- DropdownMenuItem,
4516
- {
4517
- key: index,
4518
- onClick: item.onClick,
4519
- disabled: item.disabled,
4520
- variant: item.variant
4521
- },
4522
- item.icon && item.icon,
4523
- item.label
4524
- ))));
4549
+ )), /* @__PURE__ */ React.createElement(DropdownMenuContent, { align: "end", side: "bottom", className: "z-[100000]" }, /* @__PURE__ */ React.createElement(TooltipProvider$1, null, items.map((item, index) => /* @__PURE__ */ React.createElement(MenuItem, { key: index, item })))));
4525
4550
  }
4526
4551
  return /* @__PURE__ */ React.createElement("div", { className: cn("inline-flex", className) }, /* @__PURE__ */ React.createElement(
4527
4552
  Button$2,
@@ -4558,19 +4583,10 @@ const DropdownButton = React.forwardRef(
4558
4583
  style: { fill: "none" }
4559
4584
  }
4560
4585
  )
4561
- )), /* @__PURE__ */ React.createElement(DropdownMenuContent, { align: "end", side: "bottom" }, items.map((item, index) => {
4586
+ )), /* @__PURE__ */ React.createElement(DropdownMenuContent, { align: "end", side: "bottom" }, /* @__PURE__ */ React.createElement(TooltipProvider$1, null, items.map((item, index) => {
4562
4587
  var _a;
4563
- return /* @__PURE__ */ React.createElement(React.Fragment, { key: index }, /* @__PURE__ */ React.createElement(
4564
- DropdownMenuItem,
4565
- {
4566
- onClick: item.onClick,
4567
- disabled: item.disabled,
4568
- variant: item.variant
4569
- },
4570
- item.icon && item.icon,
4571
- item.label
4572
- ), item.variant === "destructive" && index < items.length - 1 && ((_a = items[index + 1]) == null ? void 0 : _a.variant) !== "destructive" && /* @__PURE__ */ React.createElement(DropdownMenuSeparator, null));
4573
- }))));
4588
+ return /* @__PURE__ */ React.createElement(React.Fragment, { key: index }, /* @__PURE__ */ React.createElement(MenuItem, { item }), item.variant === "destructive" && index < items.length - 1 && ((_a = items[index + 1]) == null ? void 0 : _a.variant) !== "destructive" && /* @__PURE__ */ React.createElement(DropdownMenuSeparator, null));
4589
+ })))));
4574
4590
  }
4575
4591
  );
4576
4592
  DropdownButton.displayName = "DropdownButton";
@@ -5946,7 +5962,7 @@ const useGetOptionSets = (cms, collections, collectionFilter) => {
5946
5962
  }, [cms, collections]);
5947
5963
  return { optionSets, loading };
5948
5964
  };
5949
- const getFilename = (optionSets, value) => {
5965
+ const getFilename$1 = (optionSets, value) => {
5950
5966
  const nodes = optionSets.flatMap(
5951
5967
  (optionSet) => optionSet.edges.map((edge) => edge.node)
5952
5968
  );
@@ -5964,7 +5980,7 @@ const Combobox = ({ cms, input, field }) => {
5964
5980
  );
5965
5981
  const [filteredOptionsList, setFilteredOptionsList] = React.useState(optionSets);
5966
5982
  React.useEffect(() => {
5967
- setDisplayText(getFilename(optionSets, value));
5983
+ setDisplayText(getFilename$1(optionSets, value));
5968
5984
  input.onChange(value);
5969
5985
  }, [value, input, optionSets]);
5970
5986
  React.useEffect(() => {
@@ -7603,6 +7619,81 @@ const PopoverContent = React.forwardRef(({ className, align = "center", sideOffs
7603
7619
  }
7604
7620
  )));
7605
7621
  PopoverContent.displayName = PopoverPrimitive.Content.displayName;
7622
+ const TOKEN_MAP = [
7623
+ // Year
7624
+ ["YYYY", "yyyy"],
7625
+ ["YY", "yy"],
7626
+ // Month
7627
+ ["MMMM", "MMMM"],
7628
+ ["MMM", "MMM"],
7629
+ ["MM", "MM"],
7630
+ ["M", "M"],
7631
+ // Day of month (uppercase D = day-of-month in moment)
7632
+ ["DD", "dd"],
7633
+ ["Do", "do"],
7634
+ ["D", "d"],
7635
+ // Weekday (lowercase d = weekday in moment)
7636
+ ["dddd", "EEEE"],
7637
+ ["ddd", "EEE"],
7638
+ ["dd", "EEEEEE"],
7639
+ // 24-hour
7640
+ ["HH", "HH"],
7641
+ ["H", "H"],
7642
+ // 12-hour
7643
+ ["hh", "hh"],
7644
+ ["h", "h"],
7645
+ // Minute
7646
+ ["mm", "mm"],
7647
+ ["m", "m"],
7648
+ // Second
7649
+ ["ss", "ss"],
7650
+ ["s", "s"],
7651
+ // Millisecond
7652
+ ["SSS", "SSS"],
7653
+ // AM/PM
7654
+ ["A", "a"],
7655
+ ["a", "aaa"],
7656
+ // Timezone offset
7657
+ ["ZZ", "xx"],
7658
+ ["Z", "xxx"],
7659
+ // Unix timestamps
7660
+ ["X", "t"],
7661
+ ["x", "T"]
7662
+ ];
7663
+ function momentToDateFns(momentFormat) {
7664
+ let result = "";
7665
+ let i = 0;
7666
+ while (i < momentFormat.length) {
7667
+ if (momentFormat[i] === "[") {
7668
+ const end = momentFormat.indexOf("]", i + 1);
7669
+ if (end !== -1) {
7670
+ const literal = momentFormat.slice(i + 1, end).replace(/'/g, "''");
7671
+ result += `'${literal}'`;
7672
+ i = end + 1;
7673
+ continue;
7674
+ }
7675
+ }
7676
+ let matched = false;
7677
+ for (const [momentToken, dateFnsToken] of TOKEN_MAP) {
7678
+ if (momentFormat.startsWith(momentToken, i)) {
7679
+ result += dateFnsToken;
7680
+ i += momentToken.length;
7681
+ matched = true;
7682
+ break;
7683
+ }
7684
+ }
7685
+ if (!matched) {
7686
+ const ch = momentFormat[i];
7687
+ if (/[a-zA-Z]/.test(ch)) {
7688
+ result += `'${ch}'`;
7689
+ } else {
7690
+ result += ch;
7691
+ }
7692
+ i++;
7693
+ }
7694
+ }
7695
+ return result;
7696
+ }
7606
7697
  const Select = SelectPrimitive.Root;
7607
7698
  const SelectValue = SelectPrimitive.Value;
7608
7699
  const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ React.createElement(
@@ -8353,8 +8444,7 @@ const DateTimePicker = React.forwardRef(
8353
8444
  );
8354
8445
  DateTimePicker.displayName = "DateTimePicker";
8355
8446
  const format$1 = (date, format2) => {
8356
- const m = moment(date);
8357
- return m.format(format2);
8447
+ return format$3(date, momentToDateFns(format2));
8358
8448
  };
8359
8449
  const DateField = wrapFieldsWithMeta(
8360
8450
  ({
@@ -8687,7 +8777,7 @@ const BranchSwitcherLegacy = ({
8687
8777
  return newBranchList;
8688
8778
  });
8689
8779
  }).catch((e) => {
8690
- if (e.message === "AsyncPoller: cancelled")
8780
+ if (e.message === ASYNC_POLLER_ERROR.CANCELLED)
8691
8781
  return;
8692
8782
  console.error(e);
8693
8783
  });
@@ -9415,7 +9505,7 @@ const EditoralBranchSwitcher = ({
9415
9505
  return newBranchList;
9416
9506
  });
9417
9507
  }).catch((e) => {
9418
- if (e.message === "AsyncPoller: cancelled")
9508
+ if (e.message === ASYNC_POLLER_ERROR.CANCELLED)
9419
9509
  return;
9420
9510
  console.error(e);
9421
9511
  });
@@ -9713,7 +9803,7 @@ class GlobalFormPlugin {
9713
9803
  __publicField(this, "layout");
9714
9804
  this.form = form;
9715
9805
  this.name = form.label;
9716
- this.Icon = icon || MdOutlineSettings;
9806
+ this.Icon = icon || MdOutlineLanguage;
9717
9807
  this.layout = layout || "popup";
9718
9808
  this.Component = () => {
9719
9809
  const cms = useCMS();
@@ -13168,34 +13258,10 @@ const FormModal = ({ plugin, close: close2 }) => {
13168
13258
  );
13169
13259
  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 } }))));
13170
13260
  };
13171
- const NavContext = createContext(void 0);
13172
- const useNav = () => {
13173
- const context = useContext(NavContext);
13174
- if (!context) {
13175
- throw new Error("useNav must be used within a NavProvider");
13176
- }
13177
- return context;
13178
- };
13179
- const NavProvider = ({
13180
- children,
13181
- defaultOpen = false
13182
- }) => {
13183
- const [menuIsOpen, setMenuIsOpen] = useState(defaultOpen);
13184
- const toggleMenu = () => setMenuIsOpen((prev) => !prev);
13185
- const openMenu = () => setMenuIsOpen(true);
13186
- const closeMenu = () => setMenuIsOpen(false);
13187
- const value = {
13188
- menuIsOpen,
13189
- toggleMenu,
13190
- openMenu,
13191
- closeMenu
13192
- };
13193
- return /* @__PURE__ */ React__default.createElement(NavContext.Provider, { value }, children);
13194
- };
13195
13261
  const name = "tinacms";
13196
13262
  const type = "module";
13197
13263
  const typings = "dist/index.d.ts";
13198
- const version$1 = "3.9.3";
13264
+ const version$1 = "3.10.0";
13199
13265
  const main = "dist/index.js";
13200
13266
  const module = "./dist/index.js";
13201
13267
  const exports = {
@@ -13244,13 +13310,12 @@ const dependencies = {
13244
13310
  "@headlessui/react": "2.1.8",
13245
13311
  "@heroicons/react": "^1.0.6",
13246
13312
  "@monaco-editor/react": "catalog:",
13247
- "@radix-ui/react-checkbox": "catalog:",
13248
13313
  "@radix-ui/react-dialog": "catalog:",
13249
13314
  "@radix-ui/react-dropdown-menu": "catalog:",
13250
13315
  "@radix-ui/react-popover": "catalog:",
13251
13316
  "@radix-ui/react-select": "catalog:",
13252
13317
  "@radix-ui/react-separator": "catalog:",
13253
- "@radix-ui/react-slot": "^1.1.2",
13318
+ "@radix-ui/react-slot": "catalog:",
13254
13319
  "@radix-ui/react-toolbar": "catalog:",
13255
13320
  "@radix-ui/react-tooltip": "catalog:",
13256
13321
  "@react-hook/window-size": "catalog:",
@@ -13283,7 +13348,6 @@ const dependencies = {
13283
13348
  "@udecode/plate-slash-command": "catalog:",
13284
13349
  "@udecode/plate-table": "catalog:",
13285
13350
  "@udecode/plate-trailing-block": "catalog:",
13286
- add: "catalog:",
13287
13351
  "async-lock": "catalog:",
13288
13352
  "class-variance-authority": "catalog:",
13289
13353
  clsx: "catalog:",
@@ -13300,14 +13364,11 @@ const dependencies = {
13300
13364
  "is-hotkey": "catalog:",
13301
13365
  "lucide-react": "catalog:",
13302
13366
  mermaid: "^11.12.2",
13303
- moment: "catalog:",
13304
- "moment-timezone": "^0.6.0",
13305
13367
  "monaco-editor": "catalog:",
13306
13368
  "posthog-js": "^1.347.1",
13307
13369
  "prism-react-renderer": "catalog:",
13308
13370
  "prop-types": "catalog:",
13309
13371
  "react-colorful": "catalog:",
13310
- "react-datetime": "catalog:",
13311
13372
  "react-day-picker": "^9.13.0",
13312
13373
  "react-dnd": "catalog:",
13313
13374
  "react-dnd-html5-backend": "catalog:",
@@ -13422,6 +13483,30 @@ const LatestVersionWarning = ({
13422
13483
  }
13423
13484
  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);
13424
13485
  };
13486
+ const NavContext = createContext(void 0);
13487
+ const useNav = () => {
13488
+ const context = useContext(NavContext);
13489
+ if (!context) {
13490
+ throw new Error("useNav must be used within a NavProvider");
13491
+ }
13492
+ return context;
13493
+ };
13494
+ const NavProvider = ({
13495
+ children,
13496
+ defaultOpen = false
13497
+ }) => {
13498
+ const [menuIsOpen, setMenuIsOpen] = useState(defaultOpen);
13499
+ const toggleMenu = () => setMenuIsOpen((prev) => !prev);
13500
+ const openMenu = () => setMenuIsOpen(true);
13501
+ const closeMenu = () => setMenuIsOpen(false);
13502
+ const value = {
13503
+ menuIsOpen,
13504
+ toggleMenu,
13505
+ openMenu,
13506
+ closeMenu
13507
+ };
13508
+ return /* @__PURE__ */ React__default.createElement(NavContext.Provider, { value }, children);
13509
+ };
13425
13510
  const useGetEvents = (cms, cursor, existingEvents) => {
13426
13511
  const [events, setEvents] = useState([]);
13427
13512
  const [nextCursor, setNextCursor] = useState(void 0);
@@ -13583,6 +13668,7 @@ const Nav = ({
13583
13668
  sidebarWidth,
13584
13669
  RenderNavSite,
13585
13670
  RenderNavCloud,
13671
+ RenderNavGlobal,
13586
13672
  RenderNavCollection,
13587
13673
  AuthRenderNavCollection,
13588
13674
  ...props
@@ -13593,17 +13679,21 @@ const Nav = ({
13593
13679
  const menuIsOpen = menuIsOpenProp ?? (navContext == null ? void 0 : navContext.menuIsOpen) ?? false;
13594
13680
  const toggleMenu = toggleMenuProp ?? (navContext == null ? void 0 : navContext.toggleMenu) ?? (() => {
13595
13681
  });
13596
- const { contentCollections, authCollection } = collectionsInfo.collections.reduce(
13682
+ const { contentCollections, authCollection, globalCollections } = collectionsInfo.collections.reduce(
13597
13683
  (acc, collection) => {
13684
+ var _a;
13598
13685
  if (collection.isAuthCollection) {
13599
13686
  acc.authCollection = collection;
13687
+ } else if ((_a = collection.ui) == null ? void 0 : _a.global) {
13688
+ acc.globalCollections.push(collection);
13600
13689
  } else {
13601
13690
  acc.contentCollections.push(collection);
13602
13691
  }
13603
13692
  return acc;
13604
13693
  },
13605
13694
  {
13606
- contentCollections: []
13695
+ contentCollections: [],
13696
+ globalCollections: []
13607
13697
  }
13608
13698
  );
13609
13699
  const screenCategories = screens.reduce(
@@ -13663,7 +13753,9 @@ const Nav = ({
13663
13753
  RenderNavCollection,
13664
13754
  collections: contentCollections
13665
13755
  }
13666
- )), (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) => {
13756
+ )), (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) => {
13757
+ return /* @__PURE__ */ React.createElement("li", { key: `nav-global-${collection.name}` }, /* @__PURE__ */ React.createElement(RenderNavGlobal, { collection }));
13758
+ }), screenCategories.Site.map((view) => {
13667
13759
  return /* @__PURE__ */ React.createElement("li", { key: `nav-site-${view.name}` }, /* @__PURE__ */ React.createElement(RenderNavSite, { view }));
13668
13760
  }), contentCreators.map((plugin, idx) => {
13669
13761
  return /* @__PURE__ */ React.createElement(
@@ -13905,7 +13997,7 @@ function BreadcrumbSeparator({
13905
13997
  className: cn("[&>svg]:size-3.5", className),
13906
13998
  ...props
13907
13999
  },
13908
- children ?? /* @__PURE__ */ React.createElement(ChevronRight, null)
14000
+ children ?? "/"
13909
14001
  );
13910
14002
  }
13911
14003
  function BreadcrumbEllipsis({
@@ -13925,6 +14017,32 @@ function BreadcrumbEllipsis({
13925
14017
  /* @__PURE__ */ React.createElement("span", { className: "sr-only" }, "More")
13926
14018
  );
13927
14019
  }
14020
+ const TooltipProvider = TooltipPrimitive.Provider;
14021
+ const Tooltip = TooltipPrimitive.Root;
14022
+ const TooltipTrigger = TooltipPrimitive.Trigger;
14023
+ const TooltipContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ React.createElement(TooltipPrimitive.Portal, null, /* @__PURE__ */ React.createElement(
14024
+ TooltipPrimitive.Content,
14025
+ {
14026
+ ref,
14027
+ sideOffset,
14028
+ className: cn(
14029
+ "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]",
14030
+ className
14031
+ ),
14032
+ ...props
14033
+ }
14034
+ )));
14035
+ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
14036
+ const documentListPath = (collectionName, filePath) => {
14037
+ const folderPath = canonicalPath(filePath).split("/").slice(0, -1).join("/");
14038
+ return `/collections/${collectionName}/~${folderPath ? `/${folderPath}` : ""}`;
14039
+ };
14040
+ const collectionListPathForDocument = (fullPath, collection) => {
14041
+ const collectionPath = canonicalPath(collection.path || "");
14042
+ const canonical = canonicalPath(fullPath);
14043
+ const relativePath = collectionPath ? canonical.slice(collectionPath.length + 1) : canonical;
14044
+ return documentListPath(collection.name, relativePath);
14045
+ };
13928
14046
  const getPaddingClass = (depth) => `${1.5 + depth * 1.35}rem`;
13929
14047
  const collectAllDocumentItems = (items, isGlobalFn) => {
13930
14048
  const allItems = [];
@@ -14323,22 +14441,6 @@ const Emoji$1 = ({ className = "", ...props }) => /* @__PURE__ */ React.createEl
14323
14441
  ...props
14324
14442
  }
14325
14443
  );
14326
- const TooltipProvider = TooltipPrimitive.Provider;
14327
- const Tooltip = TooltipPrimitive.Root;
14328
- const TooltipTrigger = TooltipPrimitive.Trigger;
14329
- const TooltipContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ React.createElement(TooltipPrimitive.Portal, null, /* @__PURE__ */ React.createElement(
14330
- TooltipPrimitive.Content,
14331
- {
14332
- ref,
14333
- sideOffset,
14334
- className: cn(
14335
- "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]",
14336
- className
14337
- ),
14338
- ...props
14339
- }
14340
- )));
14341
- TooltipContent.displayName = TooltipPrimitive.Content.displayName;
14342
14444
  const minimumTimeToShowLoadingIndicator = 1e3;
14343
14445
  const FormsView = ({ loadingPlaceholder } = {}) => {
14344
14446
  var _a, _b, _c, _d;
@@ -14443,18 +14545,42 @@ const FormsView = ({ loadingPlaceholder } = {}) => {
14443
14545
  ))
14444
14546
  ));
14445
14547
  };
14548
+ const getFilename = (path) => {
14549
+ var _a;
14550
+ return (_a = path == null ? void 0 : path.split("/").pop()) == null ? void 0 : _a.replace(/\.[^/.]+$/, "");
14551
+ };
14446
14552
  const FormHeader = ({
14447
14553
  activeForm,
14448
14554
  repoProvider,
14449
14555
  branch,
14450
14556
  isLocalMode
14451
14557
  }) => {
14558
+ var _a, _b;
14559
+ const cms = useCMS$1();
14452
14560
  const { formIsPristine } = React.useContext(SidebarContext);
14561
+ const path = activeForm.tinaForm.path;
14562
+ let collectionCrumb;
14563
+ try {
14564
+ const collection = (_b = (_a = cms.api.tina.schema) == null ? void 0 : _a.getCollectionByFullPath) == null ? void 0 : _b.call(_a, path);
14565
+ if (collection) {
14566
+ const tinaPreview = cms.flags.get("tina-preview") || false;
14567
+ const href = `${tinaPreview ? `/${tinaPreview}/index.html#` : "/admin#"}${collectionListPathForDocument(path, collection)}`;
14568
+ collectionCrumb = {
14569
+ label: collection.label || collection.name,
14570
+ onClick: () => {
14571
+ window.location.href = href;
14572
+ }
14573
+ };
14574
+ }
14575
+ } catch {
14576
+ }
14453
14577
  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(
14454
14578
  FormBreadcrumbs,
14455
14579
  {
14456
14580
  className: "w-[calc(100%-3rem)]",
14457
- contentPath: activeForm.tinaForm.path
14581
+ rootBreadcrumbName: getFilename(path),
14582
+ contentPath: path,
14583
+ collectionCrumb
14458
14584
  }
14459
14585
  ), /* @__PURE__ */ React.createElement(
14460
14586
  FileHistoryProvider,
@@ -14502,26 +14628,50 @@ const BreadcrumbItemLink = ({
14502
14628
  breadcrumb,
14503
14629
  onClick
14504
14630
  }) => {
14505
- return /* @__PURE__ */ React.createElement(BreadcrumbItem, { className: "shrink truncate" }, /* @__PURE__ */ React.createElement(
14631
+ return /* @__PURE__ */ React.createElement(BreadcrumbItem, { className: "shrink min-w-0" }, /* @__PURE__ */ React.createElement(
14506
14632
  BreadcrumbLink,
14507
14633
  {
14508
14634
  asChild: true,
14509
- className: "text-gray-700 truncate hover:text-orange-500"
14635
+ className: "block min-w-0 truncate text-gray-700 hover:text-orange-500"
14510
14636
  },
14511
14637
  /* @__PURE__ */ React.createElement("button", { type: "button", onClick }, breadcrumb)
14512
14638
  ));
14513
14639
  };
14640
+ const CollectionBreadcrumbItem = ({
14641
+ label,
14642
+ onClick
14643
+ }) => {
14644
+ return /* @__PURE__ */ React.createElement(BreadcrumbItem, { className: "shrink min-w-0" }, /* @__PURE__ */ React.createElement(
14645
+ BreadcrumbLink,
14646
+ {
14647
+ asChild: true,
14648
+ className: "min-w-0 text-gray-700 hover:text-orange-500"
14649
+ },
14650
+ /* @__PURE__ */ React.createElement(
14651
+ "button",
14652
+ {
14653
+ type: "button",
14654
+ onClick,
14655
+ "aria-label": `Back to ${label}`,
14656
+ className: "flex items-center gap-1.5 min-w-0"
14657
+ },
14658
+ /* @__PURE__ */ React.createElement(BiArrowBack, { className: "w-4 h-4 shrink-0 opacity-80" }),
14659
+ /* @__PURE__ */ React.createElement("span", { className: "truncate min-w-0" }, label)
14660
+ )
14661
+ ));
14662
+ };
14514
14663
  const FinalBreadcrumbItem = ({ breadcrumb }) => {
14515
- return /* @__PURE__ */ React.createElement(BreadcrumbItem, { className: "shrink truncate" }, /* @__PURE__ */ React.createElement(BreadcrumbPage, { className: "text-gray-700 font-medium cursor-default" }, breadcrumb));
14664
+ 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));
14516
14665
  };
14517
14666
  const FormBreadcrumbs = ({
14518
14667
  rootBreadcrumbName,
14519
14668
  contentPath,
14669
+ collectionCrumb,
14520
14670
  ...props
14521
14671
  }) => {
14522
14672
  const cms = useCMS$1();
14523
14673
  const breadcrumbs = cms.state.breadcrumbs;
14524
- if (breadcrumbs.length === 0) {
14674
+ if (breadcrumbs.length === 0 && !collectionCrumb) {
14525
14675
  return null;
14526
14676
  }
14527
14677
  const goBack = (formId, fieldName) => {
@@ -14537,7 +14687,13 @@ const FormBreadcrumbs = ({
14537
14687
  const secondLastBreadcrumb = breadcrumbs.length > 2 ? breadcrumbs[breadcrumbs.length - 2] : null;
14538
14688
  const lastBreadcrumb = breadcrumbs.length > 1 ? breadcrumbs[breadcrumbs.length - 1] : null;
14539
14689
  const dropdownBreadcrumbs = breadcrumbs.length > 3 ? breadcrumbs.slice(1, -2) : [];
14540
- 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(
14690
+ 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(
14691
+ CollectionBreadcrumbItem,
14692
+ {
14693
+ label: collectionCrumb.label,
14694
+ onClick: collectionCrumb.onClick
14695
+ }
14696
+ ), 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(
14541
14697
  BreadcrumbItemLink,
14542
14698
  {
14543
14699
  breadcrumb: rootBreadcrumbName || firstBreadcrumb.label,
@@ -14781,6 +14937,15 @@ const Sidebar$1 = ({
14781
14937
  }
14782
14938
  ),
14783
14939
  RenderNavCloud: ({ config }) => /* @__PURE__ */ React.createElement(NavCloudLink, { config }),
14940
+ RenderNavGlobal: ({ collection }) => /* @__PURE__ */ React.createElement(
14941
+ SidebarGlobalLink,
14942
+ {
14943
+ collection,
14944
+ onClick: () => {
14945
+ setMenuIsOpen(false);
14946
+ }
14947
+ }
14948
+ ),
14784
14949
  RenderNavCollection: ({ collection }) => /* @__PURE__ */ React.createElement(
14785
14950
  SidebarCollectionLink,
14786
14951
  {
@@ -14888,6 +15053,25 @@ const SidebarSiteLink = ({
14888
15053
  view.name
14889
15054
  );
14890
15055
  };
15056
+ const SidebarGlobalLink = ({
15057
+ collection,
15058
+ onClick
15059
+ }) => {
15060
+ const cms = useCMS$1();
15061
+ const tinaPreview = cms.flags.get("tina-preview") || false;
15062
+ const href = `${tinaPreview ? `/${tinaPreview}/index.html#` : "/admin#"}/collections/${collection.name}/~`;
15063
+ return /* @__PURE__ */ React.createElement(
15064
+ "a",
15065
+ {
15066
+ onClick,
15067
+ href,
15068
+ className: "text-base tracking-wide text-gray-500 hover:text-blue-600 flex items-center opacity-90 hover:opacity-100"
15069
+ },
15070
+ /* @__PURE__ */ React.createElement(MdOutlineLanguage, { className: "mr-2 h-6 opacity-80 w-auto" }),
15071
+ " ",
15072
+ collection.label ? collection.label : collection.name
15073
+ );
15074
+ };
14891
15075
  const SidebarCollectionLink = ({
14892
15076
  Icon = ImFilesEmpty,
14893
15077
  collection,
@@ -16182,9 +16366,9 @@ const getEditorialWorkflowErrorMessage = (e) => {
16182
16366
  break;
16183
16367
  }
16184
16368
  } else if (err.message) {
16185
- if (err.message.toLowerCase().includes("already exists")) {
16369
+ if (err.message.toLowerCase().includes(ERR_BRANCH_EXISTS)) {
16186
16370
  errMessage = "A branch with this name already exists";
16187
- } else if (err.message.toLowerCase().includes("conflict")) {
16371
+ } else if (err.message.toLowerCase().includes(ERR_BRANCH_CONFLICT)) {
16188
16372
  errMessage = err.message;
16189
16373
  }
16190
16374
  }
@@ -16224,12 +16408,13 @@ function useEditorialWorkflow() {
16224
16408
  values,
16225
16409
  crudType,
16226
16410
  tinaForm,
16227
- signal
16411
+ signal,
16412
+ isDraft
16228
16413
  }) => {
16229
16414
  var _a;
16230
16415
  try {
16231
16416
  if (signal == null ? void 0 : signal.aborted)
16232
- return false;
16417
+ return { success: false };
16233
16418
  const targetBranchExists = await checkTargetBranchExists(
16234
16419
  tinaApi,
16235
16420
  branchName,
@@ -16237,12 +16422,12 @@ function useEditorialWorkflow() {
16237
16422
  signal
16238
16423
  );
16239
16424
  if (signal == null ? void 0 : signal.aborted)
16240
- return false;
16425
+ return { success: false };
16241
16426
  if (targetBranchExists) {
16242
16427
  setErrorMessage(TARGET_BRANCH_EXISTS_ERROR);
16243
16428
  setIsExecuting(false);
16244
16429
  setCurrentStep(0);
16245
- return false;
16430
+ return { success: false, error: TARGET_BRANCH_EXISTS_ERROR };
16246
16431
  }
16247
16432
  setIsExecuting(true);
16248
16433
  setCurrentStep(1);
@@ -16268,6 +16453,7 @@ function useEditorialWorkflow() {
16268
16453
  branchName,
16269
16454
  baseBranch,
16270
16455
  prTitle: getEditorialWorkflowPrTitle(branchName),
16456
+ isDraft,
16271
16457
  graphQLContentOp: {
16272
16458
  query: graphql2,
16273
16459
  variables: {
@@ -16313,14 +16499,14 @@ function useEditorialWorkflow() {
16313
16499
  const folderPath = relativePath.includes("/") ? relativePath.substring(0, relativePath.lastIndexOf("/")) : "";
16314
16500
  window.location.hash = `#/collections/${collection.name}${folderPath ? `/${folderPath}` : ""}`;
16315
16501
  }
16316
- return true;
16502
+ return { success: true };
16317
16503
  } catch (e) {
16318
16504
  console.error(e);
16319
16505
  const errMessage = getEditorialWorkflowErrorMessage(e);
16320
16506
  setErrorMessage(errMessage);
16321
16507
  setIsExecuting(false);
16322
16508
  setCurrentStep(0);
16323
- return false;
16509
+ return { success: false, error: errMessage };
16324
16510
  }
16325
16511
  };
16326
16512
  return {
@@ -16416,6 +16602,41 @@ const EditorialWorkflowProgressModal = ({
16416
16602
  elapsedTime
16417
16603
  }
16418
16604
  ))));
16605
+ function useLocalStorage(key, initialValue) {
16606
+ const [storedValue, setStoredValue] = React.useState(() => {
16607
+ try {
16608
+ const valueFromStorage = typeof window !== "undefined" && window.localStorage && window.localStorage.getItem(key);
16609
+ if (valueFromStorage != null) {
16610
+ return JSON.parse(valueFromStorage);
16611
+ }
16612
+ } catch (error2) {
16613
+ }
16614
+ return initialValue;
16615
+ });
16616
+ React.useEffect(() => {
16617
+ const valueFromStorage = window.localStorage && window.localStorage.getItem(key);
16618
+ if (valueFromStorage != null && valueFromStorage != void 0) {
16619
+ setStoredValue(JSON.parse(valueFromStorage));
16620
+ }
16621
+ }, [key]);
16622
+ const setValue = (value) => {
16623
+ try {
16624
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
16625
+ setStoredValue(valueToStore);
16626
+ localStorage.setItem(key, JSON.stringify(valueToStore));
16627
+ } catch (error2) {
16628
+ console.log(error2);
16629
+ }
16630
+ };
16631
+ return [storedValue, setValue];
16632
+ }
16633
+ const SAVE_CHOICE_KEY = "tina.editorialWorkflow.saveChoice";
16634
+ const SAVE_CHOICE_ORDER = ["draft", "review", "publish"];
16635
+ const resolveSaveOptions = (lastChoice, disablePublish) => {
16636
+ const main2 = lastChoice === "publish" && disablePublish ? "draft" : lastChoice;
16637
+ const menu = SAVE_CHOICE_ORDER.filter((choice) => choice !== main2);
16638
+ return { main: main2, menu };
16639
+ };
16419
16640
  const formatDefaultBranchName = (filePath, crudType) => {
16420
16641
  let result = filePath;
16421
16642
  const contentPrefix = "content/";
@@ -16468,7 +16689,7 @@ const CreateBranchModal = ({
16468
16689
  (_a = branchGuardAbortRef.current) == null ? void 0 : _a.abort();
16469
16690
  };
16470
16691
  }, []);
16471
- const executeEditorialWorkflow = async () => {
16692
+ const executeEditorialWorkflow = async (isDraft) => {
16472
16693
  abortBranchGuard();
16473
16694
  const abortController = new AbortController();
16474
16695
  branchGuardAbortRef.current = abortController;
@@ -16492,18 +16713,26 @@ const CreateBranchModal = ({
16492
16713
  return;
16493
16714
  }
16494
16715
  setIsBranchGuardChecking(false);
16495
- const success = await executeWorkflow({
16716
+ const { success, error: error2 } = await executeWorkflow({
16496
16717
  branchName: targetBranch,
16497
16718
  baseBranch,
16498
16719
  path,
16499
16720
  values,
16500
16721
  crudType,
16501
16722
  tinaForm,
16502
- signal: abortController.signal
16723
+ signal: abortController.signal,
16724
+ isDraft
16503
16725
  });
16504
16726
  if (branchGuardAbortRef.current === abortController) {
16505
16727
  branchGuardAbortRef.current = null;
16506
16728
  }
16729
+ if (abortController.signal.aborted)
16730
+ return;
16731
+ captureEvent(EditorialWorkflowSaveEvent, {
16732
+ choice: isDraft ? "draft" : "review",
16733
+ success,
16734
+ error: error2
16735
+ });
16507
16736
  if (success) {
16508
16737
  close2();
16509
16738
  }
@@ -16537,8 +16766,10 @@ const CreateBranchModal = ({
16537
16766
  onSaveToProtectedBranch: () => {
16538
16767
  abortBranchGuard();
16539
16768
  close2();
16540
- safeSubmit();
16541
- }
16769
+ safeSubmit("publish");
16770
+ },
16771
+ showSaveOptions: true,
16772
+ disablePublish: !!tinaApi.usingProtectedBranch()
16542
16773
  }
16543
16774
  );
16544
16775
  };
@@ -16549,8 +16780,36 @@ const CreateBranchPromptModal = ({
16549
16780
  errorMessage,
16550
16781
  onBranchNameChange,
16551
16782
  onCreateBranch,
16552
- onSaveToProtectedBranch
16783
+ onSaveToProtectedBranch,
16784
+ showSaveOptions = false,
16785
+ disablePublish = false
16553
16786
  }) => {
16787
+ const [lastChoice, setLastChoice] = useLocalStorage(
16788
+ SAVE_CHOICE_KEY,
16789
+ "draft"
16790
+ );
16791
+ const { main: main2, menu } = resolveSaveOptions(lastChoice, disablePublish);
16792
+ const choices = {
16793
+ draft: {
16794
+ label: "Save draft",
16795
+ Icon: FileText,
16796
+ run: () => onCreateBranch(true)
16797
+ },
16798
+ review: {
16799
+ label: "Save (ready for review)",
16800
+ Icon: Eye,
16801
+ run: () => onCreateBranch(false)
16802
+ },
16803
+ publish: {
16804
+ label: "Save and publish",
16805
+ Icon: Globe,
16806
+ run: onSaveToProtectedBranch,
16807
+ disabled: disablePublish,
16808
+ tooltip: disablePublish ? "This branch is protected. Save a draft or send it for review instead." : void 0
16809
+ }
16810
+ };
16811
+ const mainChoice = choices[main2] ?? choices.draft;
16812
+ const MainIcon = mainChoice.Icon;
16554
16813
  return /* @__PURE__ */ React.createElement(Modal, { className: "flex" }, /* @__PURE__ */ React.createElement(PopupModal, { className: "w-auto" }, /* @__PURE__ */ React.createElement(ModalHeader, { close: close2 }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between w-full" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center" }, "Save changes to new branch"))), /* @__PURE__ */ React.createElement(ModalBody, { padded: true }, /* @__PURE__ */ React.createElement("div", { className: "max-w-sm" }, errorMessage && /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-1 text-red-700 py-2 px-3 mb-4 bg-red-50 border border-red-200 rounded" }, /* @__PURE__ */ React.createElement(BiError, { className: "w-5 h-auto text-red-400 flex-shrink-0" }), /* @__PURE__ */ React.createElement("span", { className: "text-sm" }, /* @__PURE__ */ React.createElement("b", null, "Error:"), " ", errorMessage)), /* @__PURE__ */ React.createElement("p", { className: "text-lg text-gray-700 font-bold mb-2" }, "First, let's create a copy"), /* @__PURE__ */ React.createElement("p", { className: "text-sm text-gray-700 mb-4 max-w-sm" }, "To make changes, you need to create a copy then get it approved and merged for it to go live.", /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("span", { className: "text-gray-500" }, "Learn more about "), /* @__PURE__ */ React.createElement(
16555
16814
  "a",
16556
16815
  {
@@ -16578,14 +16837,39 @@ const CreateBranchPromptModal = ({
16578
16837
  onClick: close2
16579
16838
  },
16580
16839
  "Cancel"
16581
- ), /* @__PURE__ */ React.createElement(
16840
+ ), showSaveOptions ? /* @__PURE__ */ React.createElement(
16582
16841
  DropdownButton,
16583
16842
  {
16584
16843
  variant: "primary",
16585
16844
  align: "start",
16586
16845
  className: "w-full sm:w-auto",
16587
16846
  disabled,
16588
- onMainAction: onCreateBranch,
16847
+ onMainAction: mainChoice.run,
16848
+ items: menu.map((choice) => {
16849
+ const option = choices[choice];
16850
+ const OptionIcon = option.Icon;
16851
+ return {
16852
+ label: option.label,
16853
+ icon: /* @__PURE__ */ React.createElement(OptionIcon, { className: "w-4 h-4" }),
16854
+ disabled: option.disabled,
16855
+ tooltip: option.tooltip,
16856
+ onClick: () => {
16857
+ setLastChoice(choice);
16858
+ option.run();
16859
+ }
16860
+ };
16861
+ })
16862
+ },
16863
+ /* @__PURE__ */ React.createElement(MainIcon, { className: "w-4 h-4 mr-1", style: { fill: "none" } }),
16864
+ mainChoice.label
16865
+ ) : /* @__PURE__ */ React.createElement(
16866
+ DropdownButton,
16867
+ {
16868
+ variant: "primary",
16869
+ align: "start",
16870
+ className: "w-full sm:w-auto",
16871
+ disabled,
16872
+ onMainAction: () => onCreateBranch(false),
16589
16873
  items: [
16590
16874
  {
16591
16875
  label: "Save to Protected Branch",
@@ -16594,7 +16878,13 @@ const CreateBranchPromptModal = ({
16594
16878
  }
16595
16879
  ]
16596
16880
  },
16597
- /* @__PURE__ */ React.createElement(GitBranchIcon, { className: "w-4 h-4 mr-1", style: { fill: "none" } }),
16881
+ /* @__PURE__ */ React.createElement(
16882
+ GitBranchIcon,
16883
+ {
16884
+ className: "w-4 h-4 mr-1",
16885
+ style: { fill: "none" }
16886
+ }
16887
+ ),
16598
16888
  "Save to a new branch"
16599
16889
  ))));
16600
16890
  };
@@ -16634,7 +16924,7 @@ const BranchDeletedModal = ({
16634
16924
  reset
16635
16925
  } = useEditorialWorkflow();
16636
16926
  const handleCreate = async () => {
16637
- const success = await executeWorkflow({
16927
+ const { success } = await executeWorkflow({
16638
16928
  branchName: `tina/${newBranchName}`,
16639
16929
  baseBranch,
16640
16930
  path,
@@ -16811,7 +17101,16 @@ const FormBuilder = ({
16811
17101
  }) => {
16812
17102
  const usingProtectedBranch = cms.api.tina.usingProtectedBranch();
16813
17103
  const canSubmit = !pristine && !submitting && !hasValidationErrors && !(invalid && !dirtySinceLastSubmit);
16814
- const safeSubmit = async () => {
17104
+ const safeSubmit = async (editorialWorkflowChoice) => {
17105
+ const captureWorkflowChoice = (success, error2) => {
17106
+ if (editorialWorkflowChoice) {
17107
+ captureEvent(EditorialWorkflowSaveEvent, {
17108
+ choice: editorialWorkflowChoice,
17109
+ success,
17110
+ error: error2
17111
+ });
17112
+ }
17113
+ };
16815
17114
  if (canSubmit) {
16816
17115
  const alertsBefore = new Set(cms.alerts.all.map((a) => a.id));
16817
17116
  console.debug(
@@ -16834,6 +17133,7 @@ const FormBuilder = ({
16834
17133
  cms.alerts.dismiss(alert);
16835
17134
  }
16836
17135
  }
17136
+ captureWorkflowChoice(false, errorMsg);
16837
17137
  setDeletedBranchModalOpen(true);
16838
17138
  return;
16839
17139
  }
@@ -16841,10 +17141,12 @@ const FormBuilder = ({
16841
17141
  documentPath: tinaForm.path,
16842
17142
  error: errorMsg
16843
17143
  });
17144
+ captureWorkflowChoice(false, errorMsg);
16844
17145
  } else {
16845
17146
  captureEvent(SavedContentEvent, {
16846
17147
  documentPath: tinaForm.path
16847
17148
  });
17149
+ captureWorkflowChoice(true);
16848
17150
  }
16849
17151
  } else {
16850
17152
  console.debug(
@@ -17199,27 +17501,6 @@ const insertImg = (editor, media) => {
17199
17501
  }
17200
17502
  editor.tf.normalize({ force: true });
17201
17503
  };
17202
- const EllipsisIcon = ({ title }) => {
17203
- return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, title && /* @__PURE__ */ React__default.createElement("span", { className: "sr-only" }, title), /* @__PURE__ */ React__default.createElement(
17204
- "svg",
17205
- {
17206
- xmlns: "http://www.w3.org/2000/svg",
17207
- className: "h-5 w-5",
17208
- fill: "none",
17209
- viewBox: "0 0 24 24",
17210
- stroke: "currentColor"
17211
- },
17212
- /* @__PURE__ */ React__default.createElement(
17213
- "path",
17214
- {
17215
- strokeLinecap: "round",
17216
- strokeLinejoin: "round",
17217
- strokeWidth: 2,
17218
- 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"
17219
- }
17220
- )
17221
- ));
17222
- };
17223
17504
  const Wrapper = ({ inline, children }) => {
17224
17505
  const Component = inline ? "span" : "div";
17225
17506
  return /* @__PURE__ */ React__default.createElement(
@@ -18742,7 +19023,7 @@ const useCreateEditor = ({
18742
19023
  });
18743
19024
  };
18744
19025
  const popoverVariants = cva(
18745
- "z-50 w-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-hidden"
19026
+ "z-[999999] w-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-hidden"
18746
19027
  );
18747
19028
  const inputVariants = cva(
18748
19029
  "flex h-[28px] w-full rounded-md border-none bg-transparent px-1.5 py-1 text-base placeholder:text-muted-foreground focus-visible:ring-transparent focus-visible:outline-none md:text-sm"
@@ -18776,7 +19057,8 @@ function LinkFloatingToolbar({
18776
19057
  flip({
18777
19058
  fallbackPlacements: ["bottom-end", "top-start", "top-end"],
18778
19059
  padding: 12
18779
- })
19060
+ }),
19061
+ shift({ padding: 8, limiter: limitShift() })
18780
19062
  ],
18781
19063
  placement: activeSuggestionId || activeCommentId ? "top-start" : "bottom-start"
18782
19064
  };
@@ -18896,13 +19178,13 @@ function LinkFloatingToolbar({
18896
19178
  },
18897
19179
  /* @__PURE__ */ React.createElement(Unlink, { width: 18 })
18898
19180
  ));
18899
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
19181
+ return /* @__PURE__ */ React.createElement(PortalBody, null, /* @__PURE__ */ React.createElement(
18900
19182
  "div",
18901
19183
  {
18902
19184
  ref: insertRef,
18903
19185
  className: popoverVariants(),
18904
19186
  ...insertProps,
18905
- style: insertProps.style
19187
+ style: { ...insertProps.style }
18906
19188
  },
18907
19189
  input
18908
19190
  ), /* @__PURE__ */ React.createElement(
@@ -18911,7 +19193,7 @@ function LinkFloatingToolbar({
18911
19193
  ref: editRef,
18912
19194
  className: popoverVariants(),
18913
19195
  ...editProps,
18914
- style: editProps.style
19196
+ style: { ...editProps.style }
18915
19197
  },
18916
19198
  editContent
18917
19199
  ));
@@ -70358,25 +70640,6 @@ class FormMetaPlugin {
70358
70640
  this.Component = options.Component;
70359
70641
  }
70360
70642
  }
70361
- function useLocalStorage(key, initialValue) {
70362
- const [storedValue, setStoredValue] = React.useState(initialValue);
70363
- React.useEffect(() => {
70364
- const valueFromStorage = window.localStorage && window.localStorage.getItem(key);
70365
- if (valueFromStorage != null && valueFromStorage != void 0) {
70366
- setStoredValue(JSON.parse(valueFromStorage));
70367
- }
70368
- }, [key]);
70369
- const setValue = (value) => {
70370
- try {
70371
- const valueToStore = value instanceof Function ? value(storedValue) : value;
70372
- setStoredValue(valueToStore);
70373
- localStorage.setItem(key, JSON.stringify(valueToStore));
70374
- } catch (error2) {
70375
- console.log(error2);
70376
- }
70377
- };
70378
- return [storedValue, setValue];
70379
- }
70380
70643
  const MediaWorkflowOverlay = () => {
70381
70644
  const cms = useCMS$1();
70382
70645
  const { setCurrentBranch } = useBranchData();
@@ -70572,13 +70835,13 @@ function asyncPoll(fn, pollInterval = 5 * 1e3, pollTimeout = 30 * 1e3) {
70572
70835
  Promise.resolve(fn()).then((result) => {
70573
70836
  const now = (/* @__PURE__ */ new Date()).getTime();
70574
70837
  if (stop) {
70575
- reject(new Error("AsyncPoller: cancelled"));
70838
+ reject(new Error(ASYNC_POLLER_ERROR.CANCELLED));
70576
70839
  } else if (result.done) {
70577
70840
  resolve(result.data);
70578
70841
  } else if (now < endTime) {
70579
70842
  setTimeout(checkCondition, pollInterval, resolve, reject);
70580
70843
  } else {
70581
- reject(new Error("AsyncPoller: reached timeout"));
70844
+ reject(new Error(ASYNC_POLLER_ERROR.TIMEOUT));
70582
70845
  }
70583
70846
  }).catch((err) => {
70584
70847
  reject(err);
@@ -71192,9 +71455,7 @@ mutation addPendingDocumentMutation(
71192
71455
  if (result.status === "unknown") {
71193
71456
  unknownCount++;
71194
71457
  if (unknownCount > 5) {
71195
- throw new Error(
71196
- "AsyncPoller: status unknown for too long, please check indexing progress the TinaCloud dashboard"
71197
- );
71458
+ throw new Error(ASYNC_POLLER_ERROR.STATUS_UNKNOWN);
71198
71459
  }
71199
71460
  }
71200
71461
  return Promise.resolve({
@@ -71214,7 +71475,7 @@ mutation addPendingDocumentMutation(
71214
71475
  );
71215
71476
  return [prom, cancel];
71216
71477
  } catch (error2) {
71217
- if (error2.message === "AsyncPoller: reached timeout") {
71478
+ if (error2.message === ASYNC_POLLER_ERROR.TIMEOUT) {
71218
71479
  console.warn(error2);
71219
71480
  return [Promise.resolve({ status: "timeout" }), () => {
71220
71481
  }];
@@ -71408,6 +71669,7 @@ mutation addPendingDocumentMutation(
71408
71669
  branchName: options.branchName,
71409
71670
  baseBranch: options.baseBranch,
71410
71671
  prTitle: options.prTitle,
71672
+ isDraft: options.isDraft,
71411
71673
  graphQLContentOp: options.graphQLContentOp
71412
71674
  },
71413
71675
  "Failed to start editorial workflow"
@@ -72580,6 +72842,14 @@ const Sidebar = ({ cms }) => {
72580
72842
  }
72581
72843
  ),
72582
72844
  RenderNavCloud: ({ config }) => /* @__PURE__ */ React__default.createElement(NavCloudLink, { config }),
72845
+ RenderNavGlobal: ({ collection }) => /* @__PURE__ */ React__default.createElement(
72846
+ SidebarLink,
72847
+ {
72848
+ label: collection.label ? collection.label : collection.name,
72849
+ to: `/collections/${collection.name}/~`,
72850
+ Icon: MdOutlineLanguage
72851
+ }
72852
+ ),
72583
72853
  RenderNavCollection: ({ collection }) => /* @__PURE__ */ React__default.createElement(
72584
72854
  SidebarLink,
72585
72855
  {
@@ -72839,7 +73109,7 @@ const TemplateMenu = ({
72839
73109
  leaveFrom: "transform opacity-100 scale-100",
72840
73110
  leaveTo: "transform opacity-0 scale-95"
72841
73111
  },
72842
- /* @__PURE__ */ React__default.createElement(MenuItems, { className: "origin-top-right absolute right-0 mt-2 z-menu w-56 rounded shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none" }, /* @__PURE__ */ React__default.createElement("div", { className: "py-1" }, templates.map((template) => /* @__PURE__ */ React__default.createElement(MenuItem, { key: `${template.label}-${template.name}` }, ({ focus }) => /* @__PURE__ */ React__default.createElement(
73112
+ /* @__PURE__ */ React__default.createElement(MenuItems, { className: "origin-top-right absolute right-0 mt-2 z-menu w-56 rounded shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none" }, /* @__PURE__ */ React__default.createElement("div", { className: "py-1" }, templates.map((template) => /* @__PURE__ */ React__default.createElement(MenuItem$1, { key: `${template.label}-${template.name}` }, ({ focus }) => /* @__PURE__ */ React__default.createElement(
72843
73113
  Link$1,
72844
73114
  {
72845
73115
  to: `/${folder.fullyQualifiedName ? [
@@ -72992,12 +73262,29 @@ const CollectionListPage = () => {
72992
73262
  ),
72993
73263
  search
72994
73264
  },
72995
- (collection, _loading, reFetchCollection, collectionExtra) => {
72996
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
73265
+ (collection, loading, reFetchCollection, collectionExtra) => {
73266
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
72997
73267
  const documents = collection.documents.edges;
72998
73268
  const admin = cms.api.admin;
72999
73269
  const pageInfo = collection.documents.pageInfo;
73000
- const fields = (((_a = collectionExtra.templates) == null ? void 0 : _a.length) ? getUniqueTemplateFields(collectionExtra) : collectionExtra.fields).filter(
73270
+ if (((_a = collectionExtra == null ? void 0 : collectionExtra.ui) == null ? void 0 : _a.global) && !folder.fullyQualifiedName.startsWith("~/")) {
73271
+ if (loading || collection.name !== collectionName) {
73272
+ return /* @__PURE__ */ React__default.createElement(LoadingPage, null);
73273
+ }
73274
+ if ((documents == null ? void 0 : documents.length) === 1) {
73275
+ const globalDoc = (_b = documents[0]) == null ? void 0 : _b.node;
73276
+ if ((_c = globalDoc == null ? void 0 : globalDoc._sys) == null ? void 0 : _c.breadcrumbs) {
73277
+ return /* @__PURE__ */ React__default.createElement(
73278
+ Navigate,
73279
+ {
73280
+ replace: true,
73281
+ to: `/collections/edit/${collection.name}/${globalDoc._sys.breadcrumbs.join("/")}`
73282
+ }
73283
+ );
73284
+ }
73285
+ }
73286
+ }
73287
+ const fields = (((_d = collectionExtra.templates) == null ? void 0 : _d.length) ? getUniqueTemplateFields(collectionExtra) : collectionExtra.fields).filter(
73001
73288
  (x) => (
73002
73289
  // only allow sortable fields
73003
73290
  ["string", "number", "datetime", "boolean"].includes(x.type)
@@ -73006,15 +73293,16 @@ const CollectionListPage = () => {
73006
73293
  const sortField = fields == null ? void 0 : fields.find(
73007
73294
  (field) => field.name === sortName
73008
73295
  );
73009
- const searchEnabled = !!((_d = (_c = (_b = cms.api.tina.schema) == null ? void 0 : _b.config) == null ? void 0 : _c.config) == null ? void 0 : _d.search);
73296
+ const searchEnabled = !!((_g = (_f = (_e = cms.api.tina.schema) == null ? void 0 : _e.config) == null ? void 0 : _f.config) == null ? void 0 : _g.search);
73010
73297
  const collectionDefinition = cms.api.tina.schema.getCollection(
73011
73298
  collection.name
73012
73299
  );
73013
- const parse2 = (_f = (_e = collectionDefinition == null ? void 0 : collectionDefinition.ui) == null ? void 0 : _e.filename) == null ? void 0 : _f.parse;
73014
- const allowCreate = ((_h = (_g = collectionDefinition == null ? void 0 : collectionDefinition.ui) == null ? void 0 : _g.allowedActions) == null ? void 0 : _h.create) ?? true;
73015
- const allowDelete = ((_j = (_i = collectionDefinition == null ? void 0 : collectionDefinition.ui) == null ? void 0 : _i.allowedActions) == null ? void 0 : _j.delete) ?? true;
73016
- const allowCreateFolder = ((_l = (_k = collectionDefinition == null ? void 0 : collectionDefinition.ui) == null ? void 0 : _k.allowedActions) == null ? void 0 : _l.createFolder) ?? true;
73017
- const allowCreateNestedFolder = ((_n = (_m = collectionDefinition == null ? void 0 : collectionDefinition.ui) == null ? void 0 : _m.allowedActions) == null ? void 0 : _n.createNestedFolder) ?? true;
73300
+ const parse2 = (_i = (_h = collectionDefinition == null ? void 0 : collectionDefinition.ui) == null ? void 0 : _h.filename) == null ? void 0 : _i.parse;
73301
+ const isGlobalCollection = !!((_j = collectionExtra == null ? void 0 : collectionExtra.ui) == null ? void 0 : _j.global);
73302
+ 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);
73303
+ const allowDelete = ((_n = (_m = collectionDefinition == null ? void 0 : collectionDefinition.ui) == null ? void 0 : _m.allowedActions) == null ? void 0 : _n.delete) ?? true;
73304
+ const allowCreateFolder = !isGlobalCollection && (((_p = (_o = collectionDefinition == null ? void 0 : collectionDefinition.ui) == null ? void 0 : _o.allowedActions) == null ? void 0 : _p.createFolder) ?? true);
73305
+ const allowCreateNestedFolder = !isGlobalCollection && (((_r = (_q = collectionDefinition == null ? void 0 : collectionDefinition.ui) == null ? void 0 : _q.allowedActions) == null ? void 0 : _r.createNestedFolder) ?? true);
73018
73306
  const folderView = folder.fullyQualifiedName !== "";
73019
73307
  return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, deleteModalOpen && !cms.api.tina.usingProtectedBranch() && /* @__PURE__ */ React__default.createElement(
73020
73308
  DeleteModal,
@@ -73045,7 +73333,7 @@ const CollectionListPage = () => {
73045
73333
  );
73046
73334
  reFetchCollection();
73047
73335
  } catch (error2) {
73048
- if (error2.message.indexOf("has references")) {
73336
+ if (error2.message.includes(ERR_HAS_REFERENCES)) {
73049
73337
  cms.alerts.error(
73050
73338
  error2.message.split("\n ").filter(Boolean)[1]
73051
73339
  );
@@ -73055,7 +73343,6 @@ const CollectionListPage = () => {
73055
73343
  "Document was not deleted, ask a developer for help or check the console for an error message"
73056
73344
  );
73057
73345
  console.error(error2);
73058
- throw error2;
73059
73346
  }
73060
73347
  },
73061
73348
  close: () => setDeleteModalOpen(false)
@@ -73107,11 +73394,11 @@ const CollectionListPage = () => {
73107
73394
  );
73108
73395
  reFetchCollection();
73109
73396
  } catch (error2) {
73110
- if (error2.message && error2.message.includes("already exists")) {
73397
+ if (error2.message && error2.message.includes(ERR_ALREADY_EXISTS)) {
73111
73398
  cms.alerts.error(
73112
73399
  `Document was not renamed. The filename "${vars.newRelativePath}" is already used by another document, please choose a different name.`
73113
73400
  );
73114
- } else if (error2.message && error2.message.includes("has references")) {
73401
+ } else if (error2.message && error2.message.includes(ERR_HAS_REFERENCES)) {
73115
73402
  cms.alerts.error(
73116
73403
  error2.message.split("\n ").filter(Boolean)[1]
73117
73404
  );
@@ -73134,7 +73421,7 @@ const CollectionListPage = () => {
73134
73421
  return { ...vars2, folderName };
73135
73422
  });
73136
73423
  },
73137
- 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,
73424
+ 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,
73138
73425
  createFunc: async () => {
73139
73426
  try {
73140
73427
  await admin.createFolder(
@@ -73153,7 +73440,7 @@ const CollectionListPage = () => {
73153
73440
  );
73154
73441
  cms.alerts.info("Folder was successfully created");
73155
73442
  } catch (error2) {
73156
- if (error2.message && error2.message.includes("already exists")) {
73443
+ if (error2.message && error2.message.includes(ERR_ALREADY_EXISTS)) {
73157
73444
  cms.alerts.error(
73158
73445
  `Folder was not created, folder with name "${vars.folderName}" already exists ${folder.name ? `in ${folder.name}` : ""}`
73159
73446
  );
@@ -73234,7 +73521,7 @@ const CollectionListPage = () => {
73234
73521
  {
73235
73522
  cms,
73236
73523
  collectionName,
73237
- loading: _loading,
73524
+ loading,
73238
73525
  search,
73239
73526
  setSearch,
73240
73527
  searchInput,
@@ -73739,38 +74026,56 @@ const FolderModal = ({
73739
74026
  validationRegex
73740
74027
  }) => {
73741
74028
  const [isFolderNameValid, setIsFolderNameValid] = useState(false);
74029
+ const [validationMessage, setValidationMessage] = useState(
74030
+ null
74031
+ );
73742
74032
  const [isInteracted, setIsInteracted] = useState(false);
73743
74033
  useEffect(() => {
73744
74034
  validateFolderName(folderName);
73745
74035
  }, [folderName]);
73746
74036
  const validateFolderName = (name2) => {
73747
- if (!validationRegex || !name2.trim()) {
73748
- setIsFolderNameValid(!!name2.trim());
73749
- return !!name2.trim();
74037
+ if (!name2.trim()) {
74038
+ setIsFolderNameValid(false);
74039
+ setValidationMessage(null);
74040
+ return false;
73750
74041
  }
73751
- try {
73752
- const regex = new RegExp(validationRegex);
73753
- const valid = regex.test(name2);
73754
- setIsFolderNameValid(valid);
73755
- return valid;
73756
- } catch (error2) {
74042
+ if (!isValidRelativePath(name2)) {
73757
74043
  setIsFolderNameValid(false);
74044
+ setValidationMessage(RELATIVE_PATH_ALLOWED_CHARS_MESSAGE);
73758
74045
  return false;
73759
74046
  }
74047
+ if (validationRegex) {
74048
+ let passesCustomRegex = false;
74049
+ try {
74050
+ passesCustomRegex = new RegExp(validationRegex).test(name2);
74051
+ } catch (error2) {
74052
+ passesCustomRegex = false;
74053
+ }
74054
+ if (!passesCustomRegex) {
74055
+ setIsFolderNameValid(false);
74056
+ setValidationMessage(
74057
+ "Folder name is not valid, please enter a valid folder name."
74058
+ );
74059
+ return false;
74060
+ }
74061
+ }
74062
+ setIsFolderNameValid(true);
74063
+ setValidationMessage(null);
74064
+ return true;
73760
74065
  };
73761
74066
  return /* @__PURE__ */ React__default.createElement(Modal, null, /* @__PURE__ */ React__default.createElement(PopupModal, null, /* @__PURE__ */ React__default.createElement(ModalHeader, { close: close2 }, "Create Folder"), /* @__PURE__ */ React__default.createElement(ModalBody, { padded: true }, /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(
73762
74067
  BaseTextField,
73763
74068
  {
73764
74069
  placeholder: "Enter the name of the new folder",
73765
74070
  value: folderName,
73766
- className: `mb-4 ${!isFolderNameValid && isInteracted ? "border-red-500" : ""}`,
74071
+ className: `mb-4 ${isInteracted && validationMessage ? "border-red-500" : ""}`,
73767
74072
  onChange: (event) => {
73768
74073
  setFolderName(event.target.value);
73769
74074
  setIsInteracted(true);
73770
74075
  validateFolderName(event.target.value);
73771
74076
  }
73772
74077
  }
73773
- ), !isFolderNameValid && isInteracted && /* @__PURE__ */ React__default.createElement("p", { className: "text-red-500 text-sm pl-1" }, "Folder name is not valid – please enter a valid folder name."))), /* @__PURE__ */ React__default.createElement(ModalActions, null, /* @__PURE__ */ React__default.createElement(Button$2, { style: { flexGrow: 2 }, onClick: close2 }, "Cancel"), /* @__PURE__ */ React__default.createElement(
74078
+ ), isInteracted && validationMessage && /* @__PURE__ */ React__default.createElement("p", { className: "text-red-500 text-sm pl-1" }, validationMessage))), /* @__PURE__ */ React__default.createElement(ModalActions, null, /* @__PURE__ */ React__default.createElement(Button$2, { style: { flexGrow: 2 }, onClick: close2 }, "Cancel"), /* @__PURE__ */ React__default.createElement(
73774
74079
  Button$2,
73775
74080
  {
73776
74081
  style: { flexGrow: 3 },
@@ -74210,9 +74515,8 @@ const RenderForm$1 = ({
74210
74515
  }
74211
74516
  return true;
74212
74517
  }
74213
- const isValid = /^[\.\-_\/a-zA-Z0-9]*$/.test(value);
74214
- if (value && !isValid) {
74215
- return "Must contain only a-z, A-Z, 0-9, -, _, ., or /.";
74518
+ if (!RELATIVE_PATH_REGEX.test(value)) {
74519
+ return RELATIVE_PATH_ALLOWED_CHARS_MESSAGE;
74216
74520
  }
74217
74521
  if (((_a2 = schemaCollection.match) == null ? void 0 : _a2.exclude) || ((_b2 = schemaCollection.match) == null ? void 0 : _b2.include)) {
74218
74522
  const filePath = `${normalizePath(schemaCollection.path)}/${value}.${schemaCollection.format || "md"}`;
@@ -74274,7 +74578,7 @@ const RenderForm$1 = ({
74274
74578
  }, 10);
74275
74579
  } catch (error2) {
74276
74580
  const defaultErrorText = "There was a problem saving your document.";
74277
- if (error2.message && error2.message.includes("already exists")) {
74581
+ if (error2.message && error2.message.includes(ERR_ALREADY_EXISTS)) {
74278
74582
  cms.alerts.error(
74279
74583
  `${defaultErrorText} The filename "${form.values.filename}.${collection.format || "md"}" is already used for another document, please modify it.`
74280
74584
  );
@@ -74308,6 +74612,7 @@ const RenderForm$1 = ({
74308
74612
  const activeForm = cms.state.forms.find(
74309
74613
  ({ tinaForm }) => tinaForm.id === form.id
74310
74614
  );
74615
+ const collectionListPath = `/collections/${collection.name}${folder.fullyQualifiedName ? `/${folder.fullyQualifiedName}` : ""}`;
74311
74616
  return /* @__PURE__ */ React__default.createElement(PageWrapper, { headerClassName: "bg-white" }, /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(
74312
74617
  "div",
74313
74618
  {
@@ -74317,7 +74622,11 @@ const RenderForm$1 = ({
74317
74622
  FormBreadcrumbs,
74318
74623
  {
74319
74624
  className: "w-[calc(100%-3rem)]",
74320
- rootBreadcrumbName: "Create New"
74625
+ rootBreadcrumbName: "Create New",
74626
+ collectionCrumb: {
74627
+ label: collection.label || collection.name,
74628
+ onClick: () => navigate(collectionListPath)
74629
+ }
74321
74630
  }
74322
74631
  ), /* @__PURE__ */ React__default.createElement(FormStatus, { pristine: formIsPristine }))
74323
74632
  ), activeForm && /* @__PURE__ */ React__default.createElement(FormBuilder, { form: activeForm, onPristineChange: setFormIsPristine })));
@@ -74486,7 +74795,9 @@ const RenderForm = ({
74486
74795
  mutationInfo
74487
74796
  }) => {
74488
74797
  var _a, _b, _c, _d;
74798
+ const navigate = useNavigate();
74489
74799
  const [formIsPristine, setFormIsPristine] = useState(true);
74800
+ const collectionListPath = documentListPath(collection.name, relativePath);
74490
74801
  const schema = cms.api.tina.schema;
74491
74802
  const schemaCollection = schema.getCollection(collection.name);
74492
74803
  const template = schema.getTemplateForData({
@@ -74555,7 +74866,11 @@ const RenderForm = ({
74555
74866
  FormBreadcrumbs,
74556
74867
  {
74557
74868
  className: "w-[calc(100%-3rem)]",
74558
- rootBreadcrumbName: `${filename}.${collection.format}`
74869
+ rootBreadcrumbName: getFilename(filename),
74870
+ collectionCrumb: {
74871
+ label: collection.label || collection.name,
74872
+ onClick: () => navigate(collectionListPath)
74873
+ }
74559
74874
  }
74560
74875
  ), /* @__PURE__ */ React__default.createElement(
74561
74876
  FileHistoryProvider,
@@ -74724,7 +75039,7 @@ const CheckSchema = ({
74724
75039
  );
74725
75040
  }
74726
75041
  }).catch((error2) => {
74727
- if (error2.message.includes("has not been indexed by TinaCloud")) {
75042
+ if (error2.message.includes(ERR_NOT_INDEXED)) {
74728
75043
  setSchemaMissingError(true);
74729
75044
  } else {
74730
75045
  console.error("Unexpected error checking schema:", error2);