tinacms 3.12.0 → 3.12.1

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,8 +1,8 @@
1
1
  /**
2
2
 
3
3
  */
4
- import React from 'react';
5
4
  import type { TinaCMS } from '@tinacms/toolkit';
5
+ import React from 'react';
6
6
  import type { DocumentForm } from '../types';
7
7
  export declare const useGetDocument: (cms: TinaCMS, collectionName: string, relativePath: string) => {
8
8
  document: DocumentForm;
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ export declare const UnableToLoadModal: ({ message }: {
3
+ message: string;
4
+ }) => React.JSX.Element;
package/dist/index.js CHANGED
@@ -52,8 +52,9 @@ import PropTypes from "prop-types";
52
52
  import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, closestCenter } from "@dnd-kit/core";
53
53
  import { sortableKeyboardCoordinates, SortableContext, verticalListSortingStrategy, useSortable, defaultAnimateLayoutChanges } from "@dnd-kit/sortable";
54
54
  import { CSS } from "@dnd-kit/utilities";
55
- import { format as format$3, formatDistanceToNow } from "date-fns";
56
- import { useReactTable, getSortedRowModel, getCoreRowModel, flexRender, getExpandedRowModel, getFilteredRowModel } from "@tanstack/react-table";
55
+ import { buildSchema, print, getIntrospectionQuery, buildClientSchema, parse as parse$3 } from "graphql";
56
+ import { diff as diff$1 } from "@graphql-inspector/core";
57
+ import { processDocumentForIndexing, queryToSearchIndexQuery, optionsToSearchIndexOptions, parseSearchIndexResponse } from "@tinacms/search/index-client";
57
58
  import { HexColorPicker } from "react-colorful";
58
59
  import { Command as Command$3 } from "@udecode/cmdk";
59
60
  import { isHotkey } from "is-hotkey";
@@ -69,12 +70,11 @@ import { offset, flip, shift, limitShift, useVirtualFloating, getSelectionBoundi
69
70
  import { getLinkAttributes } from "@udecode/plate-link";
70
71
  import { autoformatSmartQuotes, autoformatPunctuation, autoformatLegal, autoformatArrow, autoformatMath } from "@udecode/plate-autoformat";
71
72
  import * as yup from "yup";
73
+ import { format as format$3, formatDistanceToNow } from "date-fns";
72
74
  import { enUS } from "date-fns/locale";
73
75
  import { DayPicker } from "react-day-picker";
74
76
  import * as SelectPrimitive from "@radix-ui/react-select";
75
- import { buildSchema, print, getIntrospectionQuery, buildClientSchema, parse as parse$3 } from "graphql";
76
- import { diff as diff$1 } from "@graphql-inspector/core";
77
- import { processDocumentForIndexing, queryToSearchIndexQuery, optionsToSearchIndexOptions, parseSearchIndexResponse } from "@tinacms/search/index-client";
77
+ import { useReactTable, getSortedRowModel, getCoreRowModel, flexRender, getExpandedRowModel, getFilteredRowModel } from "@tanstack/react-table";
78
78
  import * as dropzone from "react-dropzone";
79
79
  import gql from "graphql-tag";
80
80
  import { useLocation, NavLink, useNavigate, useParams, Navigate, Link as Link$1, HashRouter, Routes, Route } from "react-router-dom";
@@ -4225,6 +4225,8 @@ const Button$2 = ({
4225
4225
  medium: `text-sm h-10 px-8`,
4226
4226
  custom: ``
4227
4227
  };
4228
+ const isDisabled = disabled || busy;
4229
+ const inertProps = typeof Tag2 !== "string" || Tag2 === "button" ? { disabled: isDisabled } : isDisabled && { "aria-disabled": true, tabIndex: -1, href: void 0 };
4228
4230
  return /* @__PURE__ */ React.createElement(
4229
4231
  Tag2,
4230
4232
  {
@@ -4236,7 +4238,9 @@ const Button$2 = ({
4236
4238
  roundedClasses[rounded],
4237
4239
  className
4238
4240
  ),
4239
- ...props
4241
+ ...props,
4242
+ ...inertProps,
4243
+ onClick: isDisabled ? void 0 : props.onClick
4240
4244
  },
4241
4245
  busy ? /* @__PURE__ */ React.createElement(LoadingDots, { color: "currentColor" }) : children
4242
4246
  );
@@ -4279,7 +4283,8 @@ const IconButton = ({
4279
4283
  stateClasses[state],
4280
4284
  className
4281
4285
  ),
4282
- ...props
4286
+ ...props,
4287
+ disabled: disabled || busy
4283
4288
  },
4284
4289
  children
4285
4290
  );
@@ -4698,6 +4703,56 @@ const SortableProvider = ({
4698
4703
  }) => {
4699
4704
  return /* @__PURE__ */ React__default.createElement(SortableContext, { items, strategy: verticalListSortingStrategy }, children);
4700
4705
  };
4706
+ const formatDefaultBranchName = (filePath, crudType) => {
4707
+ let result = filePath;
4708
+ const contentPrefix = "content/";
4709
+ if (result.startsWith(contentPrefix)) {
4710
+ result = result.substring(contentPrefix.length);
4711
+ }
4712
+ const lastDot = result.lastIndexOf(".");
4713
+ const lastSlash = Math.max(result.lastIndexOf("/"), result.lastIndexOf("\\"));
4714
+ if (lastDot > lastSlash && lastDot > 0) {
4715
+ result = result.slice(0, lastDot);
4716
+ }
4717
+ result = normalizeBranchName(result);
4718
+ if (crudType === "delete") {
4719
+ result = `❌-${result}`;
4720
+ }
4721
+ return result;
4722
+ };
4723
+ const trimRefComponent = (part) => {
4724
+ let result = part.replace(/\.{2,}/g, ".");
4725
+ while (result.startsWith(".")) {
4726
+ result = result.slice(1);
4727
+ }
4728
+ let previous = "";
4729
+ while (previous !== result) {
4730
+ previous = result;
4731
+ if (result.endsWith(".lock")) {
4732
+ result = result.slice(0, -".lock".length);
4733
+ } else if (result.endsWith(".")) {
4734
+ result = result.slice(0, -1);
4735
+ }
4736
+ }
4737
+ return result;
4738
+ };
4739
+ const normalizeBranchName = (name2) => name2.replace(/[\x00-\x20\x7f~^:?*\[\\]+/g, "-").replace(/@\{/g, "-").split("/").map(trimRefComponent).filter(Boolean).join("/");
4740
+ function formatBranchName(str) {
4741
+ let result = "";
4742
+ let replacingInvalidChars = false;
4743
+ for (const char of str.toLowerCase()) {
4744
+ const code = char.charCodeAt(0);
4745
+ const isValid = char === "/" || char === "-" || char === "_" || code >= 48 && code <= 57 || code >= 97 && code <= 122;
4746
+ if (isValid) {
4747
+ result += char;
4748
+ replacingInvalidChars = false;
4749
+ } else if (!replacingInvalidChars) {
4750
+ result += "-";
4751
+ replacingInvalidChars = true;
4752
+ }
4753
+ }
4754
+ return result;
4755
+ }
4701
4756
  const textFieldClasses = "shadow-inner focus:shadow-outline focus:border-blue-500 focus:outline-none block text-base placeholder:text-gray-300 px-3 py-2 text-gray-600 w-full bg-white border border-gray-200 transition-all ease-out duration-150 focus:text-gray-900 rounded";
4702
4757
  const disabledClasses$1 = "opacity-50 pointer-events-none cursor-not-allowed";
4703
4758
  const BaseTextField = React.forwardRef(({ className, disabled, ...rest }, ref) => {
@@ -6193,6 +6248,7 @@ const Group$1 = ({ tinaForm, form, field, input, meta, index }) => {
6193
6248
  return /* @__PURE__ */ React__default.createElement(
6194
6249
  ListFieldMeta,
6195
6250
  {
6251
+ "data-test": `list-${field.name}`,
6196
6252
  name: input.name,
6197
6253
  label: field.label,
6198
6254
  description: field.description,
@@ -6203,6 +6259,7 @@ const Group$1 = ({ tinaForm, form, field, input, meta, index }) => {
6203
6259
  actions: (!fixedLength || fixedLength && !isMax) && /* @__PURE__ */ React__default.createElement(
6204
6260
  IconButton,
6205
6261
  {
6262
+ "data-test": `add-item-${field.name}`,
6206
6263
  onClick: addItem,
6207
6264
  disabled: isMax,
6208
6265
  variant: "primary",
@@ -6295,7 +6352,14 @@ const Item$1 = ({
6295
6352
  /* @__PURE__ */ React__default.createElement(GroupLabel, null, title),
6296
6353
  /* @__PURE__ */ React__default.createElement(Pencil, { className: "h-5 w-auto text-gray-200 group-hover:text-inherit transition-colors duration-150 ease-out" })
6297
6354
  ),
6298
- (!fixedLength || fixedLength && !isMin) && /* @__PURE__ */ React__default.createElement(ItemDeleteButton, { disabled: isMin, onClick: removeItem })
6355
+ (!fixedLength || fixedLength && !isMin) && /* @__PURE__ */ React__default.createElement(
6356
+ ItemDeleteButton,
6357
+ {
6358
+ disabled: isMin,
6359
+ onClick: removeItem,
6360
+ fieldName: `${field.name}.${index}`
6361
+ }
6362
+ )
6299
6363
  )));
6300
6364
  };
6301
6365
  const ItemClickTarget = ({ children, ...props }) => {
@@ -6342,11 +6406,16 @@ const ItemHeader = ({
6342
6406
  children
6343
6407
  );
6344
6408
  };
6345
- const ItemDeleteButton = ({ onClick, disabled = false }) => {
6409
+ const ItemDeleteButton = ({
6410
+ onClick,
6411
+ disabled = false,
6412
+ fieldName = ""
6413
+ }) => {
6346
6414
  return /* @__PURE__ */ React__default.createElement(
6347
6415
  "button",
6348
6416
  {
6349
6417
  type: "button",
6418
+ "data-test": fieldName ? `delete-item-${fieldName}` : "delete-item",
6350
6419
  className: `w-8 px-1 py-2.5 flex items-center justify-center text-gray-200 hover:opacity-100 opacity-30 hover:bg-gray-50 ${disabled && "pointer-events-none opacity-30 cursor-not-allowed"}`,
6351
6420
  onClick
6352
6421
  },
@@ -6389,7 +6458,8 @@ const PopoverContent = React.forwardRef(({ className, align = "center", sideOffs
6389
6458
  PopoverContent.displayName = PopoverPrimitive.Content.displayName;
6390
6459
  const BlockSelector = ({
6391
6460
  templates,
6392
- addItem
6461
+ addItem,
6462
+ fieldName
6393
6463
  }) => {
6394
6464
  const showFilter = React.useMemo(() => {
6395
6465
  return Object.entries(templates).length > 6;
@@ -6404,6 +6474,7 @@ const BlockSelector = ({
6404
6474
  return /* @__PURE__ */ React.createElement(Popover, { open, onOpenChange: setOpen }, /* @__PURE__ */ React.createElement(PopoverTrigger, { asChild: true }, /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement(
6405
6475
  IconButton,
6406
6476
  {
6477
+ "data-test": fieldName ? `add-item-${fieldName}` : void 0,
6407
6478
  variant: open ? "secondary" : "primary",
6408
6479
  size: "small",
6409
6480
  className: `${open ? `rotate-45 pointer-events-none` : ``}`
@@ -6703,7 +6774,8 @@ const constructUrlWithBasePath = (url, basePath) => {
6703
6774
  const BlockSelectorBig = ({
6704
6775
  templates,
6705
6776
  addItem,
6706
- label
6777
+ label,
6778
+ fieldName
6707
6779
  }) => {
6708
6780
  const FormPortal = useFormPortal();
6709
6781
  const [pickerIsOpen, setPickerIsOpen] = React.useState(false);
@@ -6748,6 +6820,7 @@ const BlockSelectorBig = ({
6748
6820
  return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
6749
6821
  IconButton,
6750
6822
  {
6823
+ "data-test": fieldName ? `add-item-${fieldName}` : void 0,
6751
6824
  variant: pickerIsOpen ? "secondary" : "primary",
6752
6825
  size: "small",
6753
6826
  className: `${pickerIsOpen ? "rotate-45 pointer-events-none" : ""}`,
@@ -6962,6 +7035,7 @@ const Blocks = ({
6962
7035
  return /* @__PURE__ */ React.createElement(
6963
7036
  ListFieldMeta,
6964
7037
  {
7038
+ "data-test": `list-${field.name}`,
6965
7039
  name: input.name,
6966
7040
  label: field.label,
6967
7041
  description: field.description,
@@ -6970,9 +7044,17 @@ const Blocks = ({
6970
7044
  index,
6971
7045
  tinaForm,
6972
7046
  actions: (!fixedLength || fixedLength && !isMax) && // @ts-ignore
6973
- (!field.visualSelector ? /* @__PURE__ */ React.createElement(BlockSelector, { templates: field.templates, addItem }) : /* @__PURE__ */ React.createElement(
7047
+ (!field.visualSelector ? /* @__PURE__ */ React.createElement(
7048
+ BlockSelector,
7049
+ {
7050
+ templates: field.templates,
7051
+ addItem,
7052
+ fieldName: field.name
7053
+ }
7054
+ ) : /* @__PURE__ */ React.createElement(
6974
7055
  BlockSelectorBig,
6975
7056
  {
7057
+ fieldName: field.name,
6976
7058
  label: field.label || field.name,
6977
7059
  templates: field.templates,
6978
7060
  addItem
@@ -7074,7 +7156,14 @@ const BlockListItem = ({
7074
7156
  /* @__PURE__ */ React.createElement(GroupLabel, null, label || template.label),
7075
7157
  isNew && /* @__PURE__ */ React.createElement("span", { className: "mr-1.5 inline-flex items-center px-1.5 py-0.5 rounded text-[10px] border-[0.5px] border-tina-orange/50 font-semibold bg-tina-orange/10 text-tina-orange leading-none" }, "NEW"),
7076
7158
  /* @__PURE__ */ React.createElement(Pencil, { className: "h-5 w-auto text-gray-200 group-hover:text-inherit transition-colors duration-150 ease-out" })
7077
- ), (!fixedLength || fixedLength && !isMin) && /* @__PURE__ */ React.createElement(ItemDeleteButton, { disabled: isMin, onClick: removeItem }))));
7159
+ ), (!fixedLength || fixedLength && !isMin) && /* @__PURE__ */ React.createElement(
7160
+ ItemDeleteButton,
7161
+ {
7162
+ disabled: isMin,
7163
+ onClick: removeItem,
7164
+ fieldName: `${field.name}.${index}`
7165
+ }
7166
+ ))));
7078
7167
  };
7079
7168
  const InvalidBlockListItem = ({
7080
7169
  tinaForm,
@@ -7090,7 +7179,13 @@ const InvalidBlockListItem = ({
7090
7179
  isDragging: snapshot.isDragging,
7091
7180
  dragHandleProps: provider.dragHandleProps
7092
7181
  }
7093
- ), /* @__PURE__ */ React.createElement(ItemClickTarget, null, /* @__PURE__ */ React.createElement(GroupLabel, { error: true }, "Invalid Block")), /* @__PURE__ */ React.createElement(ItemDeleteButton, { onClick: removeItem })));
7182
+ ), /* @__PURE__ */ React.createElement(ItemClickTarget, null, /* @__PURE__ */ React.createElement(GroupLabel, { error: true }, "Invalid Block")), /* @__PURE__ */ React.createElement(
7183
+ ItemDeleteButton,
7184
+ {
7185
+ onClick: removeItem,
7186
+ fieldName: `${field.name}.${index}`
7187
+ }
7188
+ )));
7094
7189
  };
7095
7190
  const BlocksField = Blocks;
7096
7191
  const BlocksFieldPlugin = {
@@ -7148,6 +7243,7 @@ const List = ({ tinaForm, form, field, input, meta, index }) => {
7148
7243
  return /* @__PURE__ */ React.createElement(
7149
7244
  ListFieldMeta,
7150
7245
  {
7246
+ "data-test": `list-${field.name}`,
7151
7247
  name: input.name,
7152
7248
  label: field.label,
7153
7249
  description: field.description,
@@ -7157,6 +7253,7 @@ const List = ({ tinaForm, form, field, input, meta, index }) => {
7157
7253
  actions: /* @__PURE__ */ React.createElement(
7158
7254
  IconButton,
7159
7255
  {
7256
+ "data-test": `add-item-${field.name}`,
7160
7257
  onClick: addItem,
7161
7258
  variant: "primary",
7162
7259
  size: "small",
@@ -7215,7 +7312,14 @@ const Item = ({
7215
7312
  isDragging: snapshot.isDragging,
7216
7313
  dragHandleProps: provider.dragHandleProps
7217
7314
  }
7218
- ), /* @__PURE__ */ React.createElement(ItemClickTarget, null, /* @__PURE__ */ React.createElement(FieldsBuilder, { padding: false, form: tinaForm, fields })), (!fixedLength || fixedLength && !isMin) && /* @__PURE__ */ React.createElement(ItemDeleteButton, { disabled: isMin, onClick: removeItem })));
7315
+ ), /* @__PURE__ */ React.createElement(ItemClickTarget, null, /* @__PURE__ */ React.createElement(FieldsBuilder, { padding: false, form: tinaForm, fields })), (!fixedLength || fixedLength && !isMin) && /* @__PURE__ */ React.createElement(
7316
+ ItemDeleteButton,
7317
+ {
7318
+ disabled: isMin,
7319
+ onClick: removeItem,
7320
+ fieldName: `${field.name}.${index}`
7321
+ }
7322
+ )));
7219
7323
  };
7220
7324
  const ListField = List;
7221
7325
  const ListFieldPlugin = {
@@ -8647,6 +8751,34 @@ const InfoBox = ({
8647
8751
  InfoBoxComponent.displayName = "InfoBox";
8648
8752
  return InfoBoxComponent;
8649
8753
  };
8754
+ function useLocalStorage(key, initialValue) {
8755
+ const [storedValue, setStoredValue] = React.useState(() => {
8756
+ try {
8757
+ const valueFromStorage = typeof window !== "undefined" && window.localStorage && window.localStorage.getItem(key);
8758
+ if (valueFromStorage != null) {
8759
+ return JSON.parse(valueFromStorage);
8760
+ }
8761
+ } catch (error) {
8762
+ }
8763
+ return initialValue;
8764
+ });
8765
+ React.useEffect(() => {
8766
+ const valueFromStorage = window.localStorage && window.localStorage.getItem(key);
8767
+ if (valueFromStorage != null && valueFromStorage != void 0) {
8768
+ setStoredValue(JSON.parse(valueFromStorage));
8769
+ }
8770
+ }, [key]);
8771
+ const setValue = (value) => {
8772
+ try {
8773
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
8774
+ setStoredValue(valueToStore);
8775
+ localStorage.setItem(key, JSON.stringify(valueToStore));
8776
+ } catch (error) {
8777
+ console.log(error);
8778
+ }
8779
+ };
8780
+ return [storedValue, setValue];
8781
+ }
8650
8782
  const Badge = ({
8651
8783
  children,
8652
8784
  calloutStyle = "warning",
@@ -8708,11 +8840,6 @@ const useBranchData = () => {
8708
8840
  }, [branchData.currentBranch]);
8709
8841
  return branchData;
8710
8842
  };
8711
- function formatBranchName$1(str) {
8712
- const pattern = /[^/\w-]+/g;
8713
- const formattedStr = str.replace(pattern, "");
8714
- return formattedStr.toLowerCase();
8715
- }
8716
8843
  const BranchSwitcherLegacy = ({
8717
8844
  listBranches,
8718
8845
  createBranch,
@@ -8735,7 +8862,7 @@ const BranchSwitcherLegacy = ({
8735
8862
  const handleCreateBranch = React.useCallback((value) => {
8736
8863
  setListState("loading");
8737
8864
  createBranch({
8738
- branchName: formatBranchName$1(value),
8865
+ branchName: normalizeBranchName(formatBranchName(value)),
8739
8866
  baseBranch: currentBranch
8740
8867
  }).then(async (createdBranchName) => {
8741
8868
  cms.alerts.success("Branch created.");
@@ -8905,6 +9032,9 @@ const BranchSelector = ({
8905
9032
  ));
8906
9033
  };
8907
9034
  const CreateBranch = ({ currentBranch, newBranchName, onCreateBranch, setNewBranchName }) => {
9035
+ const normalizedBranchName = normalizeBranchName(
9036
+ formatBranchName(newBranchName)
9037
+ );
8908
9038
  return /* @__PURE__ */ React.createElement("div", { className: "border-t border-gray-150 pt-4 mt-3 flex flex-col gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm" }, "Create a new branch from ", /* @__PURE__ */ React.createElement("b", null, currentBranch), ". Once created you will need to wait for indexing to complete before you can switch branches."), /* @__PURE__ */ React.createElement("div", { className: "flex justify-between items-center w-full gap-3" }, /* @__PURE__ */ React.createElement(
8909
9039
  BaseTextField,
8910
9040
  {
@@ -8918,8 +9048,11 @@ const CreateBranch = ({ currentBranch, newBranchName, onCreateBranch, setNewBran
8918
9048
  className: "flex-0 flex items-center gap-2 whitespace-nowrap",
8919
9049
  size: "medium",
8920
9050
  variant: "white",
8921
- disabled: newBranchName === "",
8922
- onClick: () => onCreateBranch(newBranchName)
9051
+ disabled: normalizedBranchName === "",
9052
+ onClick: () => {
9053
+ if (!normalizedBranchName) return;
9054
+ onCreateBranch(normalizedBranchName);
9055
+ }
8923
9056
  },
8924
9057
  /* @__PURE__ */ React.createElement(Plus, { className: "w-5 h-auto opacity-70" }),
8925
9058
  " Create Branch"
@@ -9373,22 +9506,6 @@ const PullRequestCell = ({
9373
9506
  }
9374
9507
  return null;
9375
9508
  };
9376
- function formatBranchName(str) {
9377
- let result = "";
9378
- let replacingInvalidChars = false;
9379
- for (const char of str.toLowerCase()) {
9380
- const code = char.charCodeAt(0);
9381
- const isValid = char === "/" || char === "-" || char === "_" || code >= 48 && code <= 57 || code >= 97 && code <= 122;
9382
- if (isValid) {
9383
- result += char;
9384
- replacingInvalidChars = false;
9385
- } else if (!replacingInvalidChars) {
9386
- result += "-";
9387
- replacingInvalidChars = true;
9388
- }
9389
- }
9390
- return result;
9391
- }
9392
9509
  const tableHeadingStyle = "px-3 py-3 text-left text-xs font-bold text-gray-700 tracking-wider sticky top-0 bg-gray-100 z-20 border-b-2 border-gray-200 ";
9393
9510
  const BranchSwitcher = (props) => {
9394
9511
  const cms = useCMS$1();
@@ -9429,7 +9546,7 @@ const EditoralBranchSwitcher = ({
9429
9546
  const handleCreateBranch = React.useCallback((value) => {
9430
9547
  setListState("loading");
9431
9548
  createBranch({
9432
- branchName: formatBranchName(value),
9549
+ branchName: normalizeBranchName(formatBranchName(value)),
9433
9550
  baseBranch: currentBranch
9434
9551
  }).then(async (createdBranchName) => {
9435
9552
  cms.alerts.success("Branch created.");
@@ -9573,46 +9690,58 @@ const sortBranchListFn = (sortValue) => {
9573
9690
  }
9574
9691
  };
9575
9692
  };
9576
- const BranchCreator = ({ setViewState, handleCreateBranch, currentBranch }) => {
9693
+ const BranchCreator = ({
9694
+ setViewState,
9695
+ handleCreateBranch,
9696
+ currentBranch
9697
+ }) => {
9577
9698
  const [branchName, setBranchName] = React.useState("");
9578
- return /* @__PURE__ */ React.createElement("form", null, /* @__PURE__ */ React.createElement("div", { className: "" }, /* @__PURE__ */ React.createElement("p", { className: "text-base text-gray-700 mb-4" }, "Create a new branch from ", /* @__PURE__ */ React.createElement("strong", null, currentBranch), "."), /* @__PURE__ */ React.createElement("div", { className: "mb-3" }, /* @__PURE__ */ React.createElement(FieldLabel, { name: "current-branch-name" }, "Current Branch Name"), /* @__PURE__ */ React.createElement(
9579
- BaseTextField,
9580
- {
9581
- name: "current-branch-name",
9582
- value: currentBranch,
9583
- disabled: true
9584
- }
9585
- )), /* @__PURE__ */ React.createElement("div", { className: "mb-6" }, /* @__PURE__ */ React.createElement(FieldLabel, { name: "branch-name" }, "New Branch Name"), /* @__PURE__ */ React.createElement(
9586
- PrefixedTextField,
9587
- {
9588
- placeholder: "",
9589
- name: "branch-name",
9590
- value: branchName,
9591
- onChange: (e) => setBranchName(e.target.value)
9592
- }
9593
- ))), /* @__PURE__ */ React.createElement("div", { className: "w-full flex justify-between gap-4 items-center" }, /* @__PURE__ */ React.createElement(
9594
- Button$2,
9595
- {
9596
- style: { flexGrow: 1 },
9597
- onClick: () => {
9598
- setViewState("list");
9699
+ const normalizedBranchName = normalizeBranchName(
9700
+ formatBranchName(branchName)
9701
+ );
9702
+ return (
9703
+ // Submit is handled by the button below; without this the implicit
9704
+ // submission from pressing Enter in the field reloads the page
9705
+ /* @__PURE__ */ React.createElement("form", { onSubmit: (e) => e.preventDefault() }, /* @__PURE__ */ React.createElement("div", { className: "" }, /* @__PURE__ */ React.createElement("p", { className: "text-base text-gray-700 mb-4" }, "Create a new branch from ", /* @__PURE__ */ React.createElement("strong", null, currentBranch), "."), /* @__PURE__ */ React.createElement("div", { className: "mb-3" }, /* @__PURE__ */ React.createElement(FieldLabel, { name: "current-branch-name" }, "Current Branch Name"), /* @__PURE__ */ React.createElement(
9706
+ BaseTextField,
9707
+ {
9708
+ name: "current-branch-name",
9709
+ value: currentBranch,
9710
+ disabled: true
9599
9711
  }
9600
- },
9601
- "Cancel"
9602
- ), /* @__PURE__ */ React.createElement(
9603
- Button$2,
9604
- {
9605
- variant: "primary",
9606
- type: "submit",
9607
- style: { flexGrow: 2 },
9608
- disabled: branchName === "",
9609
- onClick: () => {
9610
- handleCreateBranch("tina/" + branchName);
9712
+ )), /* @__PURE__ */ React.createElement("div", { className: "mb-6" }, /* @__PURE__ */ React.createElement(FieldLabel, { name: "branch-name" }, "New Branch Name"), /* @__PURE__ */ React.createElement(
9713
+ PrefixedTextField,
9714
+ {
9715
+ placeholder: "",
9716
+ name: "branch-name",
9717
+ value: branchName,
9718
+ onChange: (e) => setBranchName(e.target.value)
9611
9719
  }
9612
- },
9613
- "Create Branch ",
9614
- /* @__PURE__ */ React.createElement(GitBranch, { className: "w-5 h-full ml-1 opacity-70" })
9615
- )));
9720
+ ))), /* @__PURE__ */ React.createElement("div", { className: "w-full flex justify-between gap-4 items-center" }, /* @__PURE__ */ React.createElement(
9721
+ Button$2,
9722
+ {
9723
+ style: { flexGrow: 1 },
9724
+ onClick: () => {
9725
+ setViewState("list");
9726
+ }
9727
+ },
9728
+ "Cancel"
9729
+ ), /* @__PURE__ */ React.createElement(
9730
+ Button$2,
9731
+ {
9732
+ variant: "primary",
9733
+ type: "submit",
9734
+ style: { flexGrow: 2 },
9735
+ disabled: normalizedBranchName === "",
9736
+ onClick: () => {
9737
+ if (!normalizedBranchName) return;
9738
+ handleCreateBranch("tina/" + normalizedBranchName);
9739
+ }
9740
+ },
9741
+ "Create Branch ",
9742
+ /* @__PURE__ */ React.createElement(GitBranch, { className: "w-5 h-full ml-1 opacity-70" })
9743
+ )))
9744
+ );
9616
9745
  };
9617
9746
  const extractPullRequestId = (url) => {
9618
9747
  return url.split("/").pop() || "";
@@ -11580,7 +11709,6 @@ const RenameModal$1 = ({
11580
11709
  variant: "primary",
11581
11710
  disabled: !canSubmit,
11582
11711
  onClick: async () => {
11583
- if (!canSubmit) return;
11584
11712
  setProcessing(true);
11585
11713
  setError(null);
11586
11714
  try {
@@ -13591,7 +13719,7 @@ const FormModal = ({ plugin, close: close2 }) => {
13591
13719
  const name = "tinacms";
13592
13720
  const type = "module";
13593
13721
  const typings = "dist/index.d.ts";
13594
- const version = "3.12.0";
13722
+ const version = "3.12.1";
13595
13723
  const main = "dist/index.js";
13596
13724
  const module$1 = "./dist/index.js";
13597
13725
  const exports$1 = { ".": "./dist/index.js", "./dist/client": "./dist/client.js", "./react": "./dist/react.js", "./dist/react": "./dist/react.js", "./tina-field": "./dist/tina-field.js", "./dist/tina-field": "./dist/tina-field.js", "./dist/rich-text": "./dist/rich-text/index.js", "./dist/rich-text/static": "./dist/rich-text/static.js", "./dist/rich-text/prism": "./dist/rich-text/prism.js" };
@@ -13599,8 +13727,8 @@ const files = ["dist"];
13599
13727
  const buildConfig = { "entryPoints": ["src/index.ts", "src/rich-text/index.tsx", "src/rich-text/static.tsx", "src/rich-text/prism.tsx", "src/react.tsx", "src/tina-field.ts", { "name": "src/client.ts", "target": "node" }] };
13600
13728
  const license = "Apache-2.0";
13601
13729
  const scripts = { "types": "pnpm tsc && pnpm tsc-alias", "build": "tinacms-scripts build", "test": "vitest run", "test-watch": "vitest" };
13602
- const dependencies = { "@ariakit/react": "catalog:", "@dnd-kit/core": "catalog:", "@dnd-kit/sortable": "catalog:", "@dnd-kit/utilities": "catalog:", "@floating-ui/dom": "catalog:", "@floating-ui/react-dom": "catalog:", "@graphql-inspector/core": "^6.2.1", "@radix-ui/react-dialog": "catalog:", "@radix-ui/react-dropdown-menu": "catalog:", "@radix-ui/react-popover": "catalog:", "@radix-ui/react-select": "catalog:", "@radix-ui/react-separator": "catalog:", "@radix-ui/react-slot": "catalog:", "@radix-ui/react-toolbar": "catalog:", "@radix-ui/react-tooltip": "catalog:", "@react-hook/window-size": "catalog:", "@tanstack/react-table": "^8.21.3", "@tinacms/bridge": "workspace:^", "@tinacms/mdx": "workspace:^", "@tinacms/schema-tools": "workspace:^", "@tinacms/search": "workspace:^", "@udecode/cmdk": "catalog:", "@udecode/cn": "catalog:", "@udecode/plate": "catalog:", "@udecode/plate-autoformat": "catalog:", "@udecode/plate-basic-marks": "catalog:", "@udecode/plate-block-quote": "catalog:", "@udecode/plate-break": "catalog:", "@udecode/plate-code-block": "catalog:", "@udecode/plate-combobox": "catalog:", "@udecode/plate-dnd": "catalog:", "@udecode/plate-floating": "catalog:", "@udecode/plate-heading": "catalog:", "@udecode/plate-highlight": "catalog:", "@udecode/plate-horizontal-rule": "catalog:", "@udecode/plate-indent-list": "catalog:", "@udecode/plate-link": "catalog:", "@udecode/plate-list": "catalog:", "@udecode/plate-node-id": "catalog:", "@udecode/plate-reset-node": "catalog:", "@udecode/plate-resizable": "catalog:", "@udecode/plate-selection": "catalog:", "@udecode/plate-slash-command": "catalog:", "@udecode/plate-table": "catalog:", "@udecode/plate-trailing-block": "catalog:", "class-variance-authority": "catalog:", "clsx": "catalog:", "cmdk": "catalog:", "color-string": "catalog:", "crypto-js": "catalog:", "date-fns": "4.1.0", "es-toolkit": "^1.42.0", "final-form": "catalog:", "final-form-arrays": "catalog:", "final-form-set-field-data": "catalog:", "graphql": "15.8.0", "graphql-tag": "catalog:", "is-hotkey": "catalog:", "lucide-react": "catalog:", "mermaid": "catalog:", "posthog-js": "^1.347.1", "prism-react-renderer": "catalog:", "prop-types": "catalog:", "react-colorful": "catalog:", "react-day-picker": "^9.13.0", "react-dnd": "catalog:", "react-dnd-html5-backend": "catalog:", "react-dropzone": "catalog:", "react-final-form": "catalog:", "react-router-dom": "catalog:", "react-use": "catalog:", "tailwind-merge": "^2.6.0", "webfontloader": "catalog:", "yup": "^1.6.1", "zod": "catalog:" };
13603
- const devDependencies = { "@graphql-tools/utils": "catalog:", "@testing-library/dom": "catalog:", "@testing-library/jest-dom": "catalog:", "@testing-library/react": "catalog:", "@testing-library/user-event": "catalog:", "@tinacms/scripts": "workspace:*", "@types/atob": "catalog:", "@types/codemirror": "catalog:", "@types/color-string": "catalog:", "@types/node": "^22.13.1", "@types/prop-types": "catalog:", "@types/react": "^18.3.18", "@types/react-dom": "catalog:", "happy-dom": "catalog:", "identity-obj-proxy": "catalog:", "jest-file-snapshot": "^0.7.0", "lowlight": "catalog:", "next": "14.2.35", "react": "^18.3.1", "react-dom": "^18.3.1", "react-is": "^18.3.1", "tsc-alias": "catalog:", "tslib": "catalog:", "typescript": "^5.7.3", "vite": "^5.4.14", "vitest": "^2.1.9" };
13730
+ const dependencies = { "@ariakit/react": "catalog:", "@dnd-kit/core": "catalog:", "@dnd-kit/sortable": "catalog:", "@dnd-kit/utilities": "catalog:", "@floating-ui/dom": "catalog:", "@floating-ui/react-dom": "catalog:", "@graphql-inspector/core": "^6.2.1", "@radix-ui/react-dialog": "catalog:", "@radix-ui/react-dropdown-menu": "catalog:", "@radix-ui/react-popover": "catalog:", "@radix-ui/react-select": "catalog:", "@radix-ui/react-separator": "catalog:", "@radix-ui/react-slot": "catalog:", "@radix-ui/react-toolbar": "catalog:", "@radix-ui/react-tooltip": "catalog:", "@react-hook/window-size": "catalog:", "@tanstack/react-table": "^8.21.3", "@tinacms/bridge": "workspace:^", "@tinacms/mdx": "workspace:^", "@tinacms/schema-tools": "workspace:^", "@tinacms/search": "workspace:^", "@udecode/cmdk": "catalog:", "@udecode/cn": "catalog:", "@udecode/plate": "catalog:", "@udecode/plate-autoformat": "catalog:", "@udecode/plate-basic-marks": "catalog:", "@udecode/plate-block-quote": "catalog:", "@udecode/plate-break": "catalog:", "@udecode/plate-code-block": "catalog:", "@udecode/plate-combobox": "catalog:", "@udecode/plate-dnd": "catalog:", "@udecode/plate-floating": "catalog:", "@udecode/plate-heading": "catalog:", "@udecode/plate-highlight": "catalog:", "@udecode/plate-horizontal-rule": "catalog:", "@udecode/plate-indent-list": "catalog:", "@udecode/plate-link": "catalog:", "@udecode/plate-list": "catalog:", "@udecode/plate-node-id": "catalog:", "@udecode/plate-reset-node": "catalog:", "@udecode/plate-resizable": "catalog:", "@udecode/plate-selection": "catalog:", "@udecode/plate-slash-command": "catalog:", "@udecode/plate-table": "catalog:", "@udecode/plate-trailing-block": "catalog:", "class-variance-authority": "catalog:", "clsx": "catalog:", "cmdk": "catalog:", "color-string": "catalog:", "date-fns": "4.1.0", "es-toolkit": "^1.42.0", "final-form": "catalog:", "final-form-arrays": "catalog:", "final-form-set-field-data": "catalog:", "graphql": "15.8.0", "graphql-tag": "catalog:", "is-hotkey": "catalog:", "lucide-react": "catalog:", "mermaid": "catalog:", "posthog-js": "^1.347.1", "prism-react-renderer": "catalog:", "prop-types": "catalog:", "react-colorful": "catalog:", "react-day-picker": "^9.13.0", "react-dnd": "catalog:", "react-dnd-html5-backend": "catalog:", "react-dropzone": "catalog:", "react-final-form": "catalog:", "react-router-dom": "catalog:", "react-use": "catalog:", "tailwind-merge": "^2.6.0", "webfontloader": "catalog:", "yup": "^1.6.1", "zod": "catalog:" };
13731
+ const devDependencies = { "@graphql-tools/utils": "catalog:", "@testing-library/dom": "catalog:", "@testing-library/jest-dom": "catalog:", "@testing-library/react": "catalog:", "@testing-library/user-event": "catalog:", "@tinacms/scripts": "workspace:*", "@types/atob": "catalog:", "@types/codemirror": "catalog:", "@types/color-string": "catalog:", "@types/node": "^22.13.1", "@types/prop-types": "catalog:", "@types/react": "^18.3.18", "@types/react-dom": "catalog:", "happy-dom": "catalog:", "identity-obj-proxy": "catalog:", "jest-file-snapshot": "^0.7.0", "lowlight": "catalog:", "next": "15.5.21", "react": "^18.3.1", "react-dom": "^18.3.1", "react-is": "^18.3.1", "tsc-alias": "catalog:", "tslib": "catalog:", "typescript": "^5.7.3", "vite": "^5.4.14", "vitest": "^2.1.9" };
13604
13732
  const peerDependencies = { "react": ">=16.14.0", "react-dom": ">=16.14.0" };
13605
13733
  const publishConfig = { "registry": "https://registry.npmjs.org" };
13606
13734
  const repository = { "url": "https://github.com/tinacms/tinacms.git", "directory": "packages/tinacms" };
@@ -16256,34 +16384,6 @@ const BranchPreviewButton = (props) => {
16256
16384
  /* @__PURE__ */ React.createElement(ExternalLink, { className: "h-5 w-auto" })
16257
16385
  );
16258
16386
  };
16259
- function useLocalStorage(key, initialValue) {
16260
- const [storedValue, setStoredValue] = React.useState(() => {
16261
- try {
16262
- const valueFromStorage = typeof window !== "undefined" && window.localStorage && window.localStorage.getItem(key);
16263
- if (valueFromStorage != null) {
16264
- return JSON.parse(valueFromStorage);
16265
- }
16266
- } catch (error) {
16267
- }
16268
- return initialValue;
16269
- });
16270
- React.useEffect(() => {
16271
- const valueFromStorage = window.localStorage && window.localStorage.getItem(key);
16272
- if (valueFromStorage != null && valueFromStorage != void 0) {
16273
- setStoredValue(JSON.parse(valueFromStorage));
16274
- }
16275
- }, [key]);
16276
- const setValue = (value) => {
16277
- try {
16278
- const valueToStore = value instanceof Function ? value(storedValue) : value;
16279
- setStoredValue(valueToStore);
16280
- localStorage.setItem(key, JSON.stringify(valueToStore));
16281
- } catch (error) {
16282
- console.log(error);
16283
- }
16284
- };
16285
- return [storedValue, setValue];
16286
- }
16287
16387
  const CREATE_DOCUMENT_GQL = `#graphql
16288
16388
  mutation($collection: String!, $relativePath: String!, $params: DocumentMutation!) {
16289
16389
  createDocument(
@@ -16923,22 +17023,6 @@ const resolveSaveOptions = (lastChoice, disablePublish) => {
16923
17023
  const menu = SAVE_CHOICE_ORDER.filter((choice) => choice !== main2);
16924
17024
  return { main: main2, menu };
16925
17025
  };
16926
- const formatDefaultBranchName = (filePath, crudType) => {
16927
- let result = filePath;
16928
- const contentPrefix = "content/";
16929
- if (result.startsWith(contentPrefix)) {
16930
- result = result.substring(contentPrefix.length);
16931
- }
16932
- const lastDot = result.lastIndexOf(".");
16933
- const lastSlash = Math.max(result.lastIndexOf("/"), result.lastIndexOf("\\"));
16934
- if (lastDot > lastSlash && lastDot > 0) {
16935
- result = result.slice(0, lastDot);
16936
- }
16937
- if (crudType === "delete") {
16938
- result = `❌-${result}`;
16939
- }
16940
- return result;
16941
- };
16942
17026
  const CreateBranchModal = ({
16943
17027
  close: close2,
16944
17028
  safeSubmit,
@@ -16954,6 +17038,7 @@ const CreateBranchModal = ({
16954
17038
  formatDefaultBranchName(path, crudType)
16955
17039
  );
16956
17040
  const [isBranchGuardChecking, setIsBranchGuardChecking] = React.useState(false);
17041
+ const normalizedBranchName = normalizeBranchName(newBranchName);
16957
17042
  const branchGuardAbortRef = React.useRef(null);
16958
17043
  const {
16959
17044
  isExecuting,
@@ -16981,7 +17066,7 @@ const CreateBranchModal = ({
16981
17066
  branchGuardAbortRef.current = abortController;
16982
17067
  setIsBranchGuardChecking(true);
16983
17068
  const baseBranch = decodeURIComponent(tinaApi.branch);
16984
- const targetBranch = `tina/${newBranchName}`;
17069
+ const targetBranch = `tina/${normalizedBranchName}`;
16985
17070
  const { baseBranchExists, targetBranchExists } = await checkBranchGuard(
16986
17071
  tinaApi,
16987
17072
  baseBranch,
@@ -17042,7 +17127,7 @@ const CreateBranchModal = ({
17042
17127
  close2();
17043
17128
  },
17044
17129
  errorMessage,
17045
- disabled: newBranchName === "" || isBranchGuardChecking,
17130
+ disabled: normalizedBranchName === "" || isBranchGuardChecking,
17046
17131
  onBranchNameChange: (value) => {
17047
17132
  abortBranchGuard();
17048
17133
  reset();
@@ -17200,6 +17285,7 @@ const BranchDeletedModal = ({
17200
17285
  const cms = useCMS$1();
17201
17286
  const tinaApi = cms.api.tina;
17202
17287
  const [newBranchName, setNewBranchName] = React.useState("");
17288
+ const normalizedBranchName = normalizeBranchName(newBranchName);
17203
17289
  const baseBranch = tinaApi.protectedBranches[0] || cms.api.tina.schema.config.config.repoProvider.defaultBranchName || "main";
17204
17290
  const {
17205
17291
  isExecuting,
@@ -17211,7 +17297,7 @@ const BranchDeletedModal = ({
17211
17297
  } = useEditorialWorkflow();
17212
17298
  const handleCreate = async () => {
17213
17299
  const { success } = await executeWorkflow({
17214
- branchName: `tina/${newBranchName}`,
17300
+ branchName: `tina/${normalizedBranchName}`,
17215
17301
  baseBranch,
17216
17302
  path,
17217
17303
  values,
@@ -17258,7 +17344,7 @@ const BranchDeletedModal = ({
17258
17344
  {
17259
17345
  variant: "primary",
17260
17346
  className: "w-full sm:w-auto",
17261
- disabled: !newBranchName,
17347
+ disabled: !normalizedBranchName,
17262
17348
  onClick: handleCreate
17263
17349
  },
17264
17350
  /* @__PURE__ */ React.createElement(
@@ -71033,7 +71119,7 @@ const MediaWorkflowOverlay = () => {
71033
71119
  if (state.phase !== "confirming") return;
71034
71120
  const confirmState = state;
71035
71121
  const branchName = confirmState.branchName;
71036
- const targetBranch = `tina/${branchName}`;
71122
+ const targetBranch = `tina/${normalizeBranchName(branchName)}`;
71037
71123
  abortPreflight();
71038
71124
  const abortController = new AbortController();
71039
71125
  preflightAbortRef.current = abortController;
@@ -71101,7 +71187,7 @@ const MediaWorkflowOverlay = () => {
71101
71187
  state.onCancel();
71102
71188
  setState({ phase: "idle" });
71103
71189
  },
71104
- disabled: state.branchName === "" || state.isChecking,
71190
+ disabled: normalizeBranchName(state.branchName) === "" || state.isChecking,
71105
71191
  errorMessage: state.errorMessage,
71106
71192
  onBranchNameChange: (branchName) => {
71107
71193
  abortPreflight();
@@ -71315,8 +71401,7 @@ class AbstractAuthProvider {
71315
71401
  */
71316
71402
  async fetchWithToken(input, init) {
71317
71403
  const headers = (init == null ? void 0 : init.headers) || {};
71318
- const token = await this.getToken();
71319
- const accessToken = (token == null ? void 0 : token.access_token) ?? (token == null ? void 0 : token.id_token);
71404
+ const accessToken = await this.getAccessToken();
71320
71405
  if (accessToken) {
71321
71406
  headers["Authorization"] = "Bearer " + accessToken;
71322
71407
  }
@@ -71325,6 +71410,10 @@ class AbstractAuthProvider {
71325
71410
  headers: new Headers(headers)
71326
71411
  });
71327
71412
  }
71413
+ async getAccessToken() {
71414
+ const token = await this.getToken();
71415
+ return (token == null ? void 0 : token.access_token) ?? (token == null ? void 0 : token.id_token) ?? null;
71416
+ }
71328
71417
  async authorize(context) {
71329
71418
  return this.getToken();
71330
71419
  }
@@ -71362,6 +71451,7 @@ class TinaCloudAuthProvider extends AbstractAuthProvider {
71362
71451
  __publicField(this, "frontendUrl");
71363
71452
  __publicField(this, "token");
71364
71453
  // used with memory storage
71454
+ __publicField(this, "hasWarnedNoSession", false);
71365
71455
  __publicField(this, "setToken");
71366
71456
  __publicField(this, "getToken");
71367
71457
  this.frontendUrl = frontendUrl;
@@ -71430,12 +71520,24 @@ class TinaCloudAuthProvider extends AbstractAuthProvider {
71430
71520
  }
71431
71521
  const url = `${this.identityApiUrl}/v2/apps/${this.clientId}/currentUser`;
71432
71522
  try {
71523
+ if (!await this.getAccessToken()) {
71524
+ if (!this.hasWarnedNoSession) {
71525
+ this.hasWarnedNoSession = true;
71526
+ console.warn(
71527
+ "TinaCMS: no TinaCloud session found. If login fails, check the console inside the login popup window for the underlying error."
71528
+ );
71529
+ }
71530
+ return null;
71531
+ }
71433
71532
  const res = await this.fetchWithToken(url, {
71434
71533
  method: "GET"
71435
71534
  });
71436
71535
  const val = await res.json();
71437
71536
  if (!res.status.toString().startsWith("2")) {
71438
- console.error(val.error);
71537
+ console.error(
71538
+ `TinaCMS: TinaCloud session check failed (status ${res.status}).`,
71539
+ (val == null ? void 0 : val.error) ?? val
71540
+ );
71439
71541
  return null;
71440
71542
  }
71441
71543
  return val;
@@ -72396,6 +72498,7 @@ const useTinaAuthRedirect = (params, enabled = true) => {
72396
72498
  function sleep(ms) {
72397
72499
  return new Promise((resolve) => setTimeout(resolve, ms));
72398
72500
  }
72501
+ const SESSION_EXPIRED_MESSAGE = "Your session has ended. Please sign in again to continue editing.";
72399
72502
  const AuthWallInner = ({
72400
72503
  children,
72401
72504
  cms,
@@ -72417,6 +72520,15 @@ const AuthWallInner = ({
72417
72520
  const [showChildren, setShowChildren] = useState(false);
72418
72521
  const [authProps, setAuthProps] = useState({ username: "", password: "" });
72419
72522
  const [authenticated, setAuthenticated] = useState(false);
72523
+ const [sessionExpired, setSessionExpired] = useState(false);
72524
+ React__default.useEffect(
72525
+ () => cms.events.subscribe("cms:session-expired", () => {
72526
+ setSessionExpired(true);
72527
+ setShowChildren(false);
72528
+ setActiveModal("authenticate");
72529
+ }),
72530
+ []
72531
+ );
72420
72532
  React__default.useEffect(() => {
72421
72533
  let mounted = true;
72422
72534
  if (isAuthRedirect) {
@@ -72467,6 +72579,7 @@ const AuthWallInner = ({
72467
72579
  }, [authenticated]);
72468
72580
  const onAuthenticated = async () => {
72469
72581
  setAuthenticated(true);
72582
+ setSessionExpired(false);
72470
72583
  setActiveModal(null);
72471
72584
  cms.events.dispatch({ type: "cms:login" });
72472
72585
  };
@@ -72513,6 +72626,7 @@ const AuthWallInner = ({
72513
72626
  ModalBuilder,
72514
72627
  {
72515
72628
  title: modalTitle,
72629
+ error: sessionExpired ? SESSION_EXPIRED_MESSAGE : void 0,
72516
72630
  message: isTinaCloud ? /* @__PURE__ */ React__default.createElement(
72517
72631
  "img",
72518
72632
  {
@@ -72538,6 +72652,7 @@ const AuthWallInner = ({
72538
72652
  ModalBuilder,
72539
72653
  {
72540
72654
  title: modalTitle,
72655
+ error: sessionExpired ? SESSION_EXPIRED_MESSAGE : void 0,
72541
72656
  message: "",
72542
72657
  close,
72543
72658
  busy: isAuthRedirect,
@@ -72759,9 +72874,14 @@ const TinaCloudProvider = (props) => {
72759
72874
  }
72760
72875
  }, []);
72761
72876
  React__default.useEffect(() => {
72762
- const setupEditorialWorkflow = () => {
72763
- client.getProject().then(async (project) => {
72764
- var _a2;
72877
+ const setupEditorialWorkflow = async () => {
72878
+ var _a2;
72879
+ try {
72880
+ const token = await client.authProvider.getToken();
72881
+ if (!(token == null ? void 0 : token.access_token) && !(token == null ? void 0 : token.id_token)) {
72882
+ return;
72883
+ }
72884
+ const project = await client.getProject();
72765
72885
  if ((_a2 = project == null ? void 0 : project.features) == null ? void 0 : _a2.includes("editorial-workflow")) {
72766
72886
  cms.flags.set("branch-switcher", true);
72767
72887
  client.usingEditorialWorkflow = true;
@@ -72770,7 +72890,9 @@ const TinaCloudProvider = (props) => {
72770
72890
  setCurrentBranch(project.defaultBranch || "main");
72771
72891
  }
72772
72892
  }
72773
- });
72893
+ } catch (e) {
72894
+ console.error("TinaCMS: unable to load project settings", e);
72895
+ }
72774
72896
  };
72775
72897
  if (isTinaCloud) {
72776
72898
  setupEditorialWorkflow();
@@ -74657,6 +74779,18 @@ const shouldAutoOpenCollectionDocument = ({
74657
74779
  ((_b = (_a = collection.documents) == null ? void 0 : _a.edges) == null ? void 0 : _b.length) === 1 && // Check to make sure the file is not a folder
74658
74780
  ((_e = (_d = (_c = collection.documents) == null ? void 0 : _c.edges[0]) == null ? void 0 : _d.node) == null ? void 0 : _e.__typename) !== "Folder";
74659
74781
  };
74782
+ const UnableToLoadModal = ({ message }) => /* @__PURE__ */ React__default.createElement(Modal, null, /* @__PURE__ */ React__default.createElement(PopupModal, null, /* @__PURE__ */ React__default.createElement(ModalHeader, null, "Unable to load"), /* @__PURE__ */ React__default.createElement(ModalBody, { padded: true }, /* @__PURE__ */ React__default.createElement("div", { className: "tina-prose" }, message)), /* @__PURE__ */ React__default.createElement(ModalActions, null, /* @__PURE__ */ React__default.createElement("div", { className: "flex-1" }), /* @__PURE__ */ React__default.createElement(
74783
+ Button$2,
74784
+ {
74785
+ style: { flexGrow: 1 },
74786
+ className: "w-full",
74787
+ variant: "primary",
74788
+ onClick: () => {
74789
+ window.location.reload();
74790
+ }
74791
+ },
74792
+ "Reload"
74793
+ ))));
74660
74794
  const isValidSortKey = (sortKey, collection) => {
74661
74795
  if (collection.fields) {
74662
74796
  const sortKeys = collection.fields.map((x) => x.name);
@@ -74697,10 +74831,13 @@ const useGetCollection = (cms, collectionName, includeDocuments = true, folder,
74697
74831
  useEffect(() => {
74698
74832
  let cancelled = false;
74699
74833
  const fetchCollection = async () => {
74700
- if (await api.isAuthenticated() && !folder.loading && !cancelled) {
74701
- const { name: name2, order } = JSON.parse(sortKey || "{}");
74702
- const validSortKey = isValidSortKey(name2, collectionExtra) ? name2 : void 0;
74703
- try {
74834
+ if (folder.loading) {
74835
+ return;
74836
+ }
74837
+ try {
74838
+ if (await api.isAuthenticated() && !cancelled) {
74839
+ const { name: name2, order } = JSON.parse(sortKey || "{}");
74840
+ const validSortKey = isValidSortKey(name2, collectionExtra) ? name2 : void 0;
74704
74841
  const collection2 = await api.fetchCollection(
74705
74842
  collectionName,
74706
74843
  includeDocuments,
@@ -74711,14 +74848,19 @@ const useGetCollection = (cms, collectionName, includeDocuments = true, folder,
74711
74848
  filterArgs
74712
74849
  );
74713
74850
  setCollection(collection2);
74714
- } catch (error2) {
74715
- cms.alerts.error(
74716
- `[${error2.name}] GetCollection failed: ${error2.message}`
74717
- );
74718
- console.error(error2);
74851
+ } else if (!cancelled) {
74852
+ cms.events.dispatch({ type: "cms:session-expired" });
74719
74853
  setCollection(void 0);
74720
- setError(error2);
74721
74854
  }
74855
+ } catch (error2) {
74856
+ cms.alerts.error(
74857
+ `[${error2.name}] GetCollection failed: ${error2.message}`
74858
+ );
74859
+ console.error(error2);
74860
+ setCollection(void 0);
74861
+ setError(error2);
74862
+ }
74863
+ if (!cancelled) {
74722
74864
  setLoading(false);
74723
74865
  }
74724
74866
  };
@@ -74751,8 +74893,11 @@ const useSearchCollection = (cms, collectionName, includeDocuments = true, folde
74751
74893
  useEffect(() => {
74752
74894
  let cancelled = false;
74753
74895
  const searchCollection = async () => {
74754
- if (await api.isAuthenticated() && !folder.loading && !cancelled) {
74755
- try {
74896
+ if (folder.loading) {
74897
+ return;
74898
+ }
74899
+ try {
74900
+ if (await api.isAuthenticated() && !cancelled) {
74756
74901
  const response = await cms.api.search.query(search, {
74757
74902
  limit: 15,
74758
74903
  cursor: after,
@@ -74786,14 +74931,19 @@ const useSearchCollection = (cms, collectionName, includeDocuments = true, folde
74786
74931
  }
74787
74932
  };
74788
74933
  setCollection(collectionData);
74789
- } catch (error2) {
74790
- cms.alerts.error(
74791
- `[${error2.name}] GetCollection failed: ${error2.message}`
74792
- );
74793
- console.error(error2);
74934
+ } else if (!cancelled) {
74935
+ cms.events.dispatch({ type: "cms:session-expired" });
74794
74936
  setCollection(void 0);
74795
- setError(error2);
74796
74937
  }
74938
+ } catch (error2) {
74939
+ cms.alerts.error(
74940
+ `[${error2.name}] GetCollection failed: ${error2.message}`
74941
+ );
74942
+ console.error(error2);
74943
+ setCollection(void 0);
74944
+ setError(error2);
74945
+ }
74946
+ if (!cancelled) {
74797
74947
  setLoading(false);
74798
74948
  }
74799
74949
  };
@@ -74851,7 +75001,7 @@ const GetCollection = ({
74851
75001
  );
74852
75002
  useEffect(() => {
74853
75003
  var _a, _b, _c, _d;
74854
- if (loading) return;
75004
+ if (loading || !collection) return;
74855
75005
  const collectionDefinition = cms.api.tina.schema.getCollection(
74856
75006
  collection.name
74857
75007
  );
@@ -74880,6 +75030,9 @@ const GetCollection = ({
74880
75030
  if (loading) {
74881
75031
  return /* @__PURE__ */ React__default.createElement(LoadingPage, null);
74882
75032
  }
75033
+ if (!collection) {
75034
+ return /* @__PURE__ */ React__default.createElement(UnableToLoadModal, { message: "This collection could not be loaded." });
75035
+ }
74883
75036
  return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, children(collection, loading, reFetchCollection, collectionExtra));
74884
75037
  };
74885
75038
  const createDocument = async (cms, collection, template, mutationInfo, folder, values) => {
@@ -75160,8 +75313,8 @@ const useGetDocument = (cms, collectionName, relativePath) => {
75160
75313
  useEffect(() => {
75161
75314
  let isCancelled = false;
75162
75315
  const fetchDocument = async () => {
75163
- if (api.isAuthenticated() && !isCancelled) {
75164
- try {
75316
+ try {
75317
+ if (await api.isAuthenticated() && !isCancelled) {
75165
75318
  const response = await api.fetchDocument(
75166
75319
  collectionName,
75167
75320
  relativePath
@@ -75169,20 +75322,23 @@ const useGetDocument = (cms, collectionName, relativePath) => {
75169
75322
  if (!isCancelled) {
75170
75323
  setDocument(response.document);
75171
75324
  }
75172
- } catch (error2) {
75173
- if (!isCancelled) {
75174
- cms.alerts.error(
75175
- `[${error2.name}] GetDocument failed: ${error2.message}`
75176
- );
75177
- console.error(error2);
75178
- setDocument(void 0);
75179
- setError(error2);
75180
- }
75325
+ } else if (!isCancelled) {
75326
+ cms.events.dispatch({ type: "cms:session-expired" });
75327
+ setDocument(void 0);
75181
75328
  }
75329
+ } catch (error2) {
75182
75330
  if (!isCancelled) {
75183
- setLoading(false);
75331
+ cms.alerts.error(
75332
+ `[${error2.name}] GetDocument failed: ${error2.message}`
75333
+ );
75334
+ console.error(error2);
75335
+ setDocument(void 0);
75336
+ setError(error2);
75184
75337
  }
75185
75338
  }
75339
+ if (!isCancelled) {
75340
+ setLoading(false);
75341
+ }
75186
75342
  };
75187
75343
  setLoading(true);
75188
75344
  fetchDocument();
@@ -75209,6 +75365,9 @@ const GetDocument = ({
75209
75365
  if (loading) {
75210
75366
  return /* @__PURE__ */ React__default.createElement(LoadingPage, null);
75211
75367
  }
75368
+ if (!document2) {
75369
+ return /* @__PURE__ */ React__default.createElement(UnableToLoadModal, { message: "This document could not be loaded." });
75370
+ }
75212
75371
  return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, children(document2, loading));
75213
75372
  };
75214
75373
  const CollectionDuplicatePage = () => {
@@ -75871,6 +76030,7 @@ export {
75871
76030
  BlocksFieldPlugin,
75872
76031
  BoldIcon$1 as BoldIcon,
75873
76032
  BranchButton,
76033
+ BranchCreator,
75874
76034
  BranchDataProvider,
75875
76035
  BranchItem,
75876
76036
  BranchPreviewButton,
@@ -13,6 +13,7 @@ export declare abstract class AbstractAuthProvider implements AuthProvider {
13
13
  * @param init fetch function init
14
14
  */
15
15
  fetchWithToken(input: Input, init: Init): FetchReturn;
16
+ getAccessToken(): Promise<string | null>;
16
17
  authorize(context?: any): Promise<any>;
17
18
  isAuthorized(context?: any): Promise<boolean>;
18
19
  isAuthenticated(): Promise<boolean>;
@@ -33,6 +34,7 @@ export declare class TinaCloudAuthProvider extends AbstractAuthProvider {
33
34
  identityApiUrl: string;
34
35
  frontendUrl: string;
35
36
  token: TokenObject;
37
+ hasWarnedNoSession: boolean;
36
38
  setToken: (_token: TokenObject | null) => void;
37
39
  getToken: () => Promise<TokenObject>;
38
40
  constructor({ clientId, identityApiUrl, tokenStorage, frontendUrl, ...options }: {
@@ -1,9 +1,10 @@
1
1
  import * as React from 'react';
2
2
  import type { BlockTemplate } from '.';
3
- export declare const BlockSelectorBig: ({ templates, addItem, label, }: {
3
+ export declare const BlockSelectorBig: ({ templates, addItem, label, fieldName, }: {
4
4
  templates: {
5
5
  [key: string]: BlockTemplate;
6
6
  };
7
7
  addItem: any;
8
8
  label: string | boolean;
9
+ fieldName?: string;
9
10
  }) => React.JSX.Element;
@@ -1,8 +1,9 @@
1
1
  import * as React from 'react';
2
2
  import type { BlockTemplate } from '.';
3
- export declare const BlockSelector: ({ templates, addItem, }: {
3
+ export declare const BlockSelector: ({ templates, addItem, fieldName, }: {
4
4
  templates: {
5
5
  [key: string]: BlockTemplate;
6
6
  };
7
7
  addItem: any;
8
+ fieldName?: string;
8
9
  }) => React.JSX.Element;
@@ -1,6 +1,17 @@
1
- import { Field, FormApi } from '../../forms';
1
+ import { Field, Form, FormApi } from '../../forms';
2
2
  import { FieldRenderProps } from '../../form-builder';
3
+ /**
4
+ * Props every field plugin receives.
5
+ *
6
+ * `react-final-form` v6 declared `[otherProp: string]: any` on `FieldRenderProps`,
7
+ * so the extras `FieldsBuilder` passes down type-checked implicitly. v7 dropped
8
+ * that index signature, so they are declared here instead.
9
+ */
3
10
  export interface FieldProps<InputProps> extends FieldRenderProps<any, HTMLElement> {
4
11
  field: Field & InputProps;
5
12
  form: FormApi;
13
+ tinaForm: Form;
14
+ index?: number;
15
+ children?: React.ReactNode;
16
+ experimental_focusIntent?: boolean;
6
17
  }
@@ -48,9 +48,10 @@ export declare const ItemHeader: ({ isDragging, children, provider, ...props }:
48
48
  children: any | any[];
49
49
  provider: any;
50
50
  } & any) => React.JSX.Element;
51
- export declare const ItemDeleteButton: ({ onClick, disabled }: {
51
+ export declare const ItemDeleteButton: ({ onClick, disabled, fieldName, }: {
52
52
  onClick: any;
53
53
  disabled?: boolean;
54
+ fieldName?: string;
54
55
  }) => React.JSX.Element;
55
56
  export declare const DragHandle: ({ isDragging, dragHandleProps, }: {
56
57
  isDragging: boolean;
@@ -19,7 +19,11 @@ export declare const MdxFieldPlugin: {
19
19
  export declare const MdxFieldPluginExtendible: {
20
20
  name: string;
21
21
  validate(value: any): string;
22
- Component: (props: InputFieldType<InputProps, {
22
+ Component: (props: InputFieldType<InputProps & {
23
+ rawMode?: boolean;
24
+ setRawMode?: (rawMode: boolean) => void;
25
+ rawEditor?: React.ReactNode;
26
+ }, {
23
27
  templates: MdxTemplate[];
24
28
  toolbarOverride?: ToolbarOverrideType[];
25
29
  overrides?: ToolbarOverrides;
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { Branch, BranchSwitcherProps } from './types';
3
- export declare function formatBranchName(str: string): string;
3
+ export { formatBranchName } from '../../utils/branch-name';
4
4
  export declare const BranchSwitcherLegacy: ({ listBranches, createBranch, chooseBranch, }: BranchSwitcherProps) => React.JSX.Element;
5
5
  export declare const getFilteredBranchList: (branchList: Branch[], filter: string, currentBranchName: string) => Branch[];
6
6
  export declare const CreateBranch: React.FC<{
@@ -1,12 +1,16 @@
1
1
  import * as React from 'react';
2
2
  import { Branch, BranchSwitcherProps } from './types';
3
3
  export declare const tableHeadingStyle = "px-3 py-3 text-left text-xs font-bold text-gray-700 tracking-wider sticky top-0 bg-gray-100 z-20 border-b-2 border-gray-200 ";
4
- import { formatBranchName } from './format-branch-name';
5
- export { formatBranchName };
4
+ export { formatBranchName } from '../../utils/branch-name';
6
5
  export declare const BranchSwitcher: (props: BranchSwitcherProps) => React.JSX.Element;
7
6
  export declare const EditoralBranchSwitcher: ({ listBranches, createBranch, chooseBranch, setModalTitle, }: BranchSwitcherProps) => React.JSX.Element;
8
7
  export declare const getFilteredBranchList: (branchList: Branch[], search: string, currentBranchName: string, filter?: "content" | "all") => Branch[];
9
8
  export declare const sortBranchListFn: (sortValue: "default" | "updated" | "name") => (a: Branch, b: Branch) => number;
9
+ export declare const BranchCreator: ({ setViewState, handleCreateBranch, currentBranch, }: {
10
+ setViewState: any;
11
+ handleCreateBranch: any;
12
+ currentBranch: any;
13
+ }) => React.JSX.Element;
10
14
  export declare const extractPullRequestId: (url: string) => string;
11
15
  export declare const BranchItem: ({ branch, currentBranch, onChange, refreshBranchList, previewFunction, cms, selectedBranch, onSelectBranch, }: {
12
16
  branch: Branch;
@@ -0,0 +1,3 @@
1
+ export declare const formatDefaultBranchName: (filePath: string, crudType: string) => string;
2
+ export declare const normalizeBranchName: (name: string) => string;
3
+ export declare function formatBranchName(str: string): string;
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.12.0",
5
+ "version": "3.12.1",
6
6
  "main": "dist/index.js",
7
7
  "module": "./dist/index.js",
8
8
  "exports": {
@@ -80,11 +80,10 @@
80
80
  "clsx": "^2.1.1",
81
81
  "cmdk": "^1.0.4",
82
82
  "color-string": "^1.9.1",
83
- "crypto-js": "^4.2.0",
84
83
  "date-fns": "4.1.0",
85
84
  "es-toolkit": "^1.42.0",
86
- "final-form": "4.20.10",
87
- "final-form-arrays": "^3.1.0",
85
+ "final-form": "^5.0.1",
86
+ "final-form-arrays": "^4.0.1",
88
87
  "final-form-set-field-data": "^1.0.2",
89
88
  "graphql": "15.8.0",
90
89
  "graphql-tag": "^2.12.6",
@@ -99,17 +98,17 @@
99
98
  "react-dnd": "^16.0.1",
100
99
  "react-dnd-html5-backend": "^16.0.1",
101
100
  "react-dropzone": "14.2.3",
102
- "react-final-form": "^6.5.9",
101
+ "react-final-form": "^7.0.1",
103
102
  "react-router-dom": "^6.30.3",
104
103
  "react-use": "^17.6.0",
105
104
  "tailwind-merge": "^2.6.0",
106
105
  "webfontloader": "1.6.28",
107
106
  "yup": "^1.6.1",
108
107
  "zod": "^3.24.2",
109
- "@tinacms/bridge": "^0.3.1",
110
- "@tinacms/mdx": "^2.2.0",
111
- "@tinacms/search": "^1.2.23",
112
- "@tinacms/schema-tools": "^2.9.0"
108
+ "@tinacms/search": "^1.2.24",
109
+ "@tinacms/schema-tools": "^2.9.0",
110
+ "@tinacms/mdx": "^2.2.1",
111
+ "@tinacms/bridge": "^0.3.1"
113
112
  },
114
113
  "devDependencies": {
115
114
  "@graphql-tools/utils": "^10.8.1",
@@ -128,7 +127,7 @@
128
127
  "identity-obj-proxy": "^3.0.0",
129
128
  "jest-file-snapshot": "^0.7.0",
130
129
  "lowlight": "^3.3.0",
131
- "next": "14.2.35",
130
+ "next": "15.5.21",
132
131
  "react": "^18.3.1",
133
132
  "react-dom": "^18.3.1",
134
133
  "react-is": "^18.3.1",
@@ -137,7 +136,7 @@
137
136
  "typescript": "^5.7.3",
138
137
  "vite": "^5.4.14",
139
138
  "vitest": "^2.1.9",
140
- "@tinacms/scripts": "1.6.3"
139
+ "@tinacms/scripts": "1.6.4"
141
140
  },
142
141
  "peerDependencies": {
143
142
  "react": ">=16.14.0",
@@ -1,8 +0,0 @@
1
- /**
2
- * Normalizes user-supplied strings into git-ref-safe branch segments.
3
- * Kept in its own file (no React or workspace-internal imports) so
4
- * non-React callers like `core/media-store.default.ts` can import it
5
- * without dragging in the rest of `plugin-branch-switcher` and creating
6
- * a cycle through `@toolkit/core`.
7
- */
8
- export declare function formatBranchName(str: string): string;