tinacms 3.7.2 → 3.7.3

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
@@ -39571,12 +39571,6 @@ const ResetModal = ({ close: close2, reset: reset2 }) => {
39571
39571
  "Reset"
39572
39572
  ))));
39573
39573
  };
39574
- function AiFillWarning(props) {
39575
- return GenIcon({ "tag": "svg", "attr": { "viewBox": "0 0 1024 1024" }, "child": [{ "tag": "path", "attr": { "d": "M955.7 856l-416-720c-6.2-10.7-16.9-16-27.7-16s-21.6 5.3-27.7 16l-416 720C56 877.4 71.4 904 96 904h832c24.6 0 40-26.6 27.7-48zM480 416c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v184c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V416zm32 352a48.01 48.01 0 0 1 0-96 48.01 48.01 0 0 1 0 96z" }, "child": [] }] })(props);
39576
- }
39577
- function AiOutlineLoading(props) {
39578
- return GenIcon({ "tag": "svg", "attr": { "viewBox": "0 0 1024 1024" }, "child": [{ "tag": "path", "attr": { "d": "M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 0 0-94.3-139.9 437.71 437.71 0 0 0-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z" }, "child": [] }] })(props);
39579
- }
39580
39574
  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";
39581
39575
  const disabledClasses$1 = "opacity-50 pointer-events-none cursor-not-allowed";
39582
39576
  const BaseTextField = React.forwardRef(({ className, disabled, ...rest }, ref) => {
@@ -41935,13 +41929,8 @@ const ImageField = wrapFieldsWithMeta(
41935
41929
  async function onChange(media) {
41936
41930
  var _a2, _b;
41937
41931
  if (media) {
41938
- const parsedValue = (
41939
- // @ts-ignore
41940
- typeof ((_b = (_a2 = cms == null ? void 0 : cms.media) == null ? void 0 : _a2.store) == null ? void 0 : _b.parse) === "function" ? (
41941
- // @ts-ignore
41942
- cms.media.store.parse(media)
41943
- ) : media
41944
- );
41932
+ const item = Array.isArray(media) ? media[0] : media;
41933
+ const parsedValue = typeof ((_b = (_a2 = cms == null ? void 0 : cms.media) == null ? void 0 : _a2.store) == null ? void 0 : _b.parse) === "function" ? cms.media.store.parse(item) : item.src || item;
41945
41934
  props.input.onChange(parsedValue);
41946
41935
  }
41947
41936
  }
@@ -43774,6 +43763,38 @@ const PasswordFieldPlugin = {
43774
43763
  },
43775
43764
  parse: parse$2
43776
43765
  };
43766
+ const DefaultDisplayOnlyField = () => {
43767
+ return /* @__PURE__ */ React.createElement("div", { className: "rounded-lg border border-gray-200 bg-gray-50 p-3 text-sm text-gray-500 italic" }, "Display-only field — provide a component via ", /* @__PURE__ */ React.createElement("code", null, "ui.component"));
43768
+ };
43769
+ const DisplayOnlyFieldPlugin = {
43770
+ name: "displayOnly",
43771
+ Component: wrapFieldWithNoHeader(DefaultDisplayOnlyField)
43772
+ };
43773
+ const InfoBox = ({
43774
+ message,
43775
+ links
43776
+ }) => {
43777
+ const InfoBoxComponent = () => {
43778
+ return /* @__PURE__ */ React.createElement("div", { className: "relative w-full px-2 mb-5 last:mb-0" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-lg border border-blue-200 bg-blue-50 p-3 text-sm w-full" }, /* @__PURE__ */ React.createElement("p", { className: "text-gray-700 whitespace-normal break-words" }, message), links && links.length > 0 && /* @__PURE__ */ React.createElement("ul", { className: "flex flex-col gap-1 mt-2" }, links.map((link) => /* @__PURE__ */ React.createElement("li", { key: link.url }, /* @__PURE__ */ React.createElement(
43779
+ "a",
43780
+ {
43781
+ href: link.url,
43782
+ target: "_blank",
43783
+ rel: "noopener noreferrer",
43784
+ className: "text-blue-600 underline hover:text-blue-800"
43785
+ },
43786
+ link.text
43787
+ ))))));
43788
+ };
43789
+ InfoBoxComponent.displayName = "InfoBox";
43790
+ return InfoBoxComponent;
43791
+ };
43792
+ function AiFillWarning(props) {
43793
+ return GenIcon({ "tag": "svg", "attr": { "viewBox": "0 0 1024 1024" }, "child": [{ "tag": "path", "attr": { "d": "M955.7 856l-416-720c-6.2-10.7-16.9-16-27.7-16s-21.6 5.3-27.7 16l-416 720C56 877.4 71.4 904 96 904h832c24.6 0 40-26.6 27.7-48zM480 416c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v184c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V416zm32 352a48.01 48.01 0 0 1 0-96 48.01 48.01 0 0 1 0 96z" }, "child": [] }] })(props);
43794
+ }
43795
+ function AiOutlineLoading(props) {
43796
+ return GenIcon({ "tag": "svg", "attr": { "viewBox": "0 0 1024 1024" }, "child": [{ "tag": "path", "attr": { "d": "M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 0 0-94.3-139.9 437.71 437.71 0 0 0-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z" }, "child": [] }] })(props);
43797
+ }
43777
43798
  function GrCircleQuestion(props) {
43778
43799
  return GenIcon({ "tag": "svg", "attr": { "viewBox": "0 0 24 24" }, "child": [{ "tag": "path", "attr": { "fill": "none", "strokeWidth": "2", "d": "M12,22 C17.5228475,22 22,17.5228475 22,12 C22,6.4771525 17.5228475,2 12,2 C6.4771525,2 2,6.4771525 2,12 C2,17.5228475 6.4771525,22 12,22 Z M12,15 L12,14 C12,13 12,12.5 13,12 C14,11.5 15,11 15,9.5 C15,8.5 14,7 12,7 C10,7 9,8.26413718 9,10 M12,16 L12,18" }, "child": [] }] })(props);
43779
43800
  }
@@ -44179,6 +44200,9 @@ function BranchSelectorTable({
44179
44200
  null
44180
44201
  );
44181
44202
  const cms = useCMS$1();
44203
+ const isCurrentBranchDeleted = !branchList.some(
44204
+ (b) => b.name === currentBranch
44205
+ );
44182
44206
  const columns = [
44183
44207
  {
44184
44208
  accessorKey: "name",
@@ -44355,7 +44379,7 @@ function BranchSelectorTable({
44355
44379
  {
44356
44380
  className: cn(
44357
44381
  "bg-transparent hover:!bg-transparent",
44358
- currentBranch === currentBranchData.name ? "border-l-[3px] border-l-tina-orange bg-[#f8fafc]" : ""
44382
+ isCurrentBranchDeleted ? "border-l-[3px] border-l-yellow-400 bg-yellow-50" : "border-l-[3px] border-l-tina-orange bg-[#f8fafc]"
44359
44383
  )
44360
44384
  },
44361
44385
  /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2" }, currentBranchData.protected ? /* @__PURE__ */ React.createElement(BiLockAlt, { className: "w-4 h-auto opacity-70 text-blue-500 flex-shrink-0" }) : /* @__PURE__ */ React.createElement(BiGitBranch, { className: "w-4 h-auto opacity-70 text-gray-600 flex-shrink-0" }), /* @__PURE__ */ React.createElement("p", { className: "font-bold" }, currentBranchData.name)), /* @__PURE__ */ React.createElement("div", { className: "w-fit mt-1" }, /* @__PURE__ */ React.createElement(
@@ -44367,6 +44391,15 @@ function BranchSelectorTable({
44367
44391
  },
44368
44392
  /* @__PURE__ */ React.createElement(BiPencil, { className: "w-3 h-auto inline-block mr-1" }),
44369
44393
  "Currently editing"
44394
+ )), isCurrentBranchDeleted && /* @__PURE__ */ React.createElement("div", { className: "w-fit" }, /* @__PURE__ */ React.createElement(
44395
+ Badge,
44396
+ {
44397
+ displayIcon: false,
44398
+ calloutStyle: "warning",
44399
+ className: "w-fit flex-shrink-0"
44400
+ },
44401
+ /* @__PURE__ */ React.createElement(AiFillWarning, { className: "w-3 h-auto inline-block mr-1" }),
44402
+ "Branch no longer exists"
44370
44403
  )))),
44371
44404
  /* @__PURE__ */ React.createElement(TableCell, null, ((_e = currentBranchData.indexStatus) == null ? void 0 : _e.timestamp) && /* @__PURE__ */ React.createElement("div", null, formatDistanceToNow(
44372
44405
  new Date(currentBranchData.indexStatus.timestamp)
@@ -46100,10 +46133,10 @@ function GridMediaItem({ item, active, onClick }) {
46100
46133
  "button",
46101
46134
  {
46102
46135
  className: cn(
46103
- "relative flex flex-col items-center justify-center w-full outline-none",
46136
+ "relative flex flex-col gap-1 items-center w-full outline-none rounded-lg overflow-hidden border-2 transition",
46104
46137
  {
46105
- "shadow hover:shadow-md hover:scale-103 hover:border-gray-150": !active,
46106
- "ring-2 ring-tina-orange": active,
46138
+ "border-black/10 hover:border-tina-orange/50 shadow-sm hover:shadow-md bg-white": !active,
46139
+ "border-tina-orange bg-tina-orange/10 shadow-md": active,
46107
46140
  "cursor-pointer": item.type === "dir"
46108
46141
  }
46109
46142
  ),
@@ -46115,35 +46148,23 @@ function GridMediaItem({ item, active, onClick }) {
46115
46148
  }
46116
46149
  }
46117
46150
  },
46118
- /* @__PURE__ */ React__default.createElement(
46119
- "span",
46120
- {
46121
- className: cn(
46122
- "absolute bottom-0 left-0 w-full text-xs text-white px-2 py-1 truncate z-10",
46123
- active ? "bg-tina-orange/60" : "bg-black/60"
46124
- ),
46125
- style: { pointerEvents: "none" }
46126
- },
46127
- item.filename
46128
- ),
46129
46151
  item.new && /* @__PURE__ */ React__default.createElement("span", { className: "absolute top-1 right-1 rounded shadow bg-green-100 border border-green-200 text-[10px] tracking-wide font-bold text-green-600 px-1.5 py-0.5 z-10" }, "NEW"),
46130
- /* @__PURE__ */ React__default.createElement("div", { className: "relative w-full flex items-center justify-center" }, itemIsImage ? /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(
46152
+ /* @__PURE__ */ React__default.createElement("div", { className: "w-full overflow-hidden aspect-[1/1]" }, itemIsImage ? /* @__PURE__ */ React__default.createElement(
46131
46153
  "img",
46132
46154
  {
46133
- className: cn(
46134
- "block overflow-hidden object-center object-contain max-w-full max-h-[16rem] m-auto shadow"
46135
- ),
46155
+ className: "block w-full h-full object-center object-cover",
46136
46156
  style: checkerboardStyle,
46137
46157
  src: thumbnail,
46138
46158
  alt: item.filename
46139
46159
  }
46140
- )) : /* @__PURE__ */ React__default.createElement("div", { className: "p-4 w-full flex flex-col gap-4 items-center justify-center" }, /* @__PURE__ */ React__default.createElement(
46160
+ ) : /* @__PURE__ */ React__default.createElement("div", { className: "w-full h-full flex flex-col items-center justify-center" }, /* @__PURE__ */ React__default.createElement(
46141
46161
  FileIcon,
46142
46162
  {
46143
46163
  className: `w-[40%] h-auto ${item.type === "dir" ? "fill-tina-orange" : "fill-gray-300"}`,
46144
46164
  size: 40
46145
46165
  }
46146
- )))
46166
+ ))),
46167
+ /* @__PURE__ */ React__default.createElement("div", { className: "mt-auto w-full px-2 py-1 text-sm truncate" }, item.filename)
46147
46168
  ));
46148
46169
  }
46149
46170
  const DeleteModal$1 = ({
@@ -48004,7 +48025,7 @@ const NavProvider = ({
48004
48025
  const name = "tinacms";
48005
48026
  const type = "module";
48006
48027
  const typings = "dist/index.d.ts";
48007
- const version$1 = "3.7.2";
48028
+ const version$1 = "3.7.3";
48008
48029
  const main = "dist/index.js";
48009
48030
  const module = "./dist/index.js";
48010
48031
  const exports = {
@@ -48966,7 +48987,7 @@ const TreeNodeComponent = ({
48966
48987
  node3.children.length > 0 && /* @__PURE__ */ React.createElement(
48967
48988
  BiChevronRight,
48968
48989
  {
48969
- className: `w-4 h-4 text-gray-500 transition-transform duration-150 -ml-1 ${isExpanded ? "rotate-90" : ""}`
48990
+ className: `w-4 h-4 flex-none text-gray-500 transition-transform duration-150 -ml-1 ${isExpanded ? "rotate-90" : ""}`
48970
48991
  }
48971
48992
  ),
48972
48993
  isExpanded ? /* @__PURE__ */ React.createElement(BiFolderOpen, { className: "w-4 h-4 text-orange-500 flex-none" }) : /* @__PURE__ */ React.createElement(BiFolder, { className: "w-4 h-4 text-orange-500 flex-none" }),
@@ -49057,16 +49078,21 @@ const FormLists = (props) => {
49057
49078
  onChange: (e3) => setShowReferences(e3.target.checked),
49058
49079
  className: "w-4 h-4 text-orange-500 border-gray-300 rounded focus:ring-orange-500"
49059
49080
  }
49060
- ), /* @__PURE__ */ React.createElement("span", null, "Show all references"))), /* @__PURE__ */ React.createElement("div", { className: "flex-1 overflow-x-auto overflow-y-auto min-h-0" }, cms.state.formLists.map((formList, index) => /* @__PURE__ */ React.createElement("div", { key: `${formList.id}-${index}` }, /* @__PURE__ */ React.createElement(
49081
+ ), /* @__PURE__ */ React.createElement("span", null, "Show all references"))), /* @__PURE__ */ React.createElement("div", { className: "flex-1 overflow-x-auto overflow-y-auto min-h-0 pb-16" }, /* @__PURE__ */ React.createElement(
49061
49082
  FormList,
49062
49083
  {
49063
49084
  setActiveFormId: (id2) => {
49064
49085
  cms.dispatch({ type: "forms:set-active-form-id", value: id2 });
49065
49086
  },
49066
- formList,
49087
+ formList: {
49088
+ id: "merged",
49089
+ label: "All",
49090
+ items: cms.state.formLists.flatMap((fl) => fl.items),
49091
+ formIds: cms.state.formLists.flatMap((fl) => fl.formIds)
49092
+ },
49067
49093
  showReferences
49068
49094
  }
49069
- )))));
49095
+ )));
49070
49096
  };
49071
49097
  const FormList = (props) => {
49072
49098
  const cms = useCMS();
@@ -49784,7 +49810,8 @@ const DEFAULT_FIELDS = [
49784
49810
  ReferenceFieldPlugin,
49785
49811
  ButtonToggleFieldPlugin,
49786
49812
  HiddenFieldPlugin,
49787
- PasswordFieldPlugin
49813
+ PasswordFieldPlugin,
49814
+ DisplayOnlyFieldPlugin
49788
49815
  ];
49789
49816
  class TinaCMS extends CMS {
49790
49817
  constructor({
@@ -66034,37 +66061,20 @@ const pathRelativeToCollection = (collectionPath, fullPath) => {
66034
66061
  `Path ${fullPath} not within collection path ${collectionPath}`
66035
66062
  );
66036
66063
  };
66037
- const formatDefaultBranchName = (filePath, crudType) => {
66038
- let result = filePath;
66039
- const contentPrefix = "content/";
66040
- if (result.startsWith(contentPrefix)) {
66041
- result = result.substring(contentPrefix.length);
66042
- }
66043
- const lastDot = result.lastIndexOf(".");
66044
- const lastSlash = Math.max(result.lastIndexOf("/"), result.lastIndexOf("\\"));
66045
- if (lastDot > lastSlash && lastDot > 0) {
66046
- result = result.slice(0, lastDot);
66047
- }
66048
- if (crudType === "delete") {
66049
- result = `❌-${result}`;
66050
- }
66051
- return result;
66064
+ const WORKFLOW_STEPS = [
66065
+ { id: 1, name: "Creating branch", description: "Setting up workspace" },
66066
+ { id: 2, name: "Updating branch", description: "Syncing content to branch" },
66067
+ { id: 3, name: "Creating pull request", description: "Preparing for review" }
66068
+ ];
66069
+ const formatTime = (seconds) => {
66070
+ const mins = Math.floor(seconds / 60);
66071
+ const secs = seconds % 60;
66072
+ return `${mins}:${secs.toString().padStart(2, "0")}`;
66052
66073
  };
66053
- const CreateBranchModal = ({
66054
- close: close2,
66055
- safeSubmit,
66056
- path: path3,
66057
- values,
66058
- crudType,
66059
- tinaForm
66060
- }) => {
66074
+ function useEditorialWorkflow() {
66061
66075
  const cms = useCMS$1();
66062
66076
  const tinaApi = cms.api.tina;
66063
66077
  const { setCurrentBranch } = useBranchData();
66064
- const [disabled, setDisabled] = React.useState(false);
66065
- const [newBranchName, setNewBranchName] = React.useState(
66066
- formatDefaultBranchName(path3, crudType)
66067
- );
66068
66078
  const [isExecuting, setIsExecuting] = React.useState(false);
66069
66079
  const [errorMessage, setErrorMessage] = React.useState("");
66070
66080
  const [currentStep, setCurrentStep] = React.useState(0);
@@ -66083,33 +66093,21 @@ const CreateBranchModal = ({
66083
66093
  clearInterval(interval);
66084
66094
  };
66085
66095
  }, [isExecuting, currentStep]);
66086
- const formatTime = (seconds) => {
66087
- const mins = Math.floor(seconds / 60);
66088
- const secs = seconds % 60;
66089
- return `${mins}:${secs.toString().padStart(2, "0")}`;
66096
+ const reset2 = () => {
66097
+ setErrorMessage("");
66098
+ setIsExecuting(false);
66099
+ setCurrentStep(0);
66090
66100
  };
66091
- const steps = [
66092
- {
66093
- id: 1,
66094
- name: "Creating branch",
66095
- description: "Setting up workspace"
66096
- },
66097
- {
66098
- id: 2,
66099
- name: "Updating branch",
66100
- description: "Syncing content to branch"
66101
- },
66102
- {
66103
- id: 3,
66104
- name: "Creating pull request",
66105
- description: "Preparing for review"
66106
- }
66107
- ];
66108
- const executeEditorialWorkflow = async () => {
66101
+ const executeWorkflow = async ({
66102
+ branchName,
66103
+ baseBranch,
66104
+ path: path3,
66105
+ values,
66106
+ crudType,
66107
+ tinaForm
66108
+ }) => {
66109
66109
  var _a2;
66110
66110
  try {
66111
- const branchName = `tina/${newBranchName}`;
66112
- setDisabled(true);
66113
66111
  setIsExecuting(true);
66114
66112
  setCurrentStep(1);
66115
66113
  let graphql2 = "";
@@ -66139,7 +66137,7 @@ const CreateBranchModal = ({
66139
66137
  const relativePath = pathRelativeToCollection(collection.path, path3);
66140
66138
  const result = await tinaApi.executeEditorialWorkflow({
66141
66139
  branchName,
66142
- baseBranch: tinaApi.branch,
66140
+ baseBranch,
66143
66141
  prTitle: `${branchName.replace("tina/", "").replace("-", " ")} (PR from TinaCMS)`,
66144
66142
  graphQLContentOp: {
66145
66143
  query: graphql2,
@@ -66186,118 +66184,209 @@ const CreateBranchModal = ({
66186
66184
  const folderPath = relativePath.includes("/") ? relativePath.substring(0, relativePath.lastIndexOf("/")) : "";
66187
66185
  window.location.hash = `#/collections/${collection.name}${folderPath ? `/${folderPath}` : ""}`;
66188
66186
  }
66189
- close2();
66187
+ return true;
66190
66188
  } catch (e3) {
66191
66189
  console.error(e3);
66192
- let errorMessage2 = "Branch operation failed. Talking to GitHub was unsuccessful, please try again. If the problem persists please contact support at https://tina.io/support 🦙";
66190
+ let errMessage = "Branch operation failed. Talking to GitHub was unsuccessful, please try again. If the problem persists please contact support at https://tina.io/support 🦙";
66193
66191
  const err = e3;
66194
66192
  if (err.errorCode) {
66195
66193
  switch (err.errorCode) {
66196
66194
  case EDITORIAL_WORKFLOW_ERROR.BRANCH_EXISTS:
66197
- errorMessage2 = "A branch with this name already exists";
66195
+ errMessage = "A branch with this name already exists";
66198
66196
  break;
66199
66197
  case EDITORIAL_WORKFLOW_ERROR.BRANCH_HIERARCHY_CONFLICT:
66200
- errorMessage2 = err.message || "Branch name conflicts with an existing branch";
66198
+ errMessage = err.message || "Branch name conflicts with an existing branch";
66201
66199
  break;
66202
66200
  case EDITORIAL_WORKFLOW_ERROR.VALIDATION_FAILED:
66203
- errorMessage2 = err.message || "Invalid branch name";
66201
+ errMessage = err.message || "Invalid branch name";
66204
66202
  break;
66205
66203
  default:
66206
- errorMessage2 = err.message || errorMessage2;
66204
+ errMessage = err.message || errMessage;
66207
66205
  break;
66208
66206
  }
66209
66207
  } else if (err.message) {
66210
66208
  if (err.message.toLowerCase().includes("already exists")) {
66211
- errorMessage2 = "A branch with this name already exists";
66209
+ errMessage = "A branch with this name already exists";
66212
66210
  } else if (err.message.toLowerCase().includes("conflict")) {
66213
- errorMessage2 = err.message;
66211
+ errMessage = err.message;
66214
66212
  }
66215
66213
  }
66216
- setErrorMessage(errorMessage2);
66217
- setDisabled(false);
66214
+ setErrorMessage(errMessage);
66218
66215
  setIsExecuting(false);
66219
66216
  setCurrentStep(0);
66217
+ return false;
66220
66218
  }
66221
66219
  };
66222
- const renderProgressIndicator = () => {
66223
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex justify-between my-8 relative px-5 sm:gap-x-8" }, /* @__PURE__ */ React.createElement(
66224
- "div",
66225
- {
66226
- className: "absolute top-5 h-0.5 bg-gray-200 -z-10",
66227
- style: { left: "50px", right: "50px" }
66220
+ return {
66221
+ isExecuting,
66222
+ errorMessage,
66223
+ currentStep,
66224
+ elapsedTime,
66225
+ executeWorkflow,
66226
+ reset: reset2
66227
+ };
66228
+ }
66229
+ const WorkflowProgressIndicator = ({
66230
+ currentStep,
66231
+ isExecuting,
66232
+ elapsedTime
66233
+ }) => {
66234
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex justify-between my-8 relative px-5 sm:gap-x-8" }, /* @__PURE__ */ React.createElement(
66235
+ "div",
66236
+ {
66237
+ className: "absolute top-5 h-0.5 bg-gray-200 -z-10",
66238
+ style: { left: "50px", right: "50px" }
66239
+ }
66240
+ ), currentStep > 1 && currentStep <= WORKFLOW_STEPS.length && /* @__PURE__ */ React.createElement(
66241
+ "div",
66242
+ {
66243
+ className: "absolute top-5 h-0.5 bg-tina-orange -z-10 transition-all duration-500",
66244
+ style: {
66245
+ left: "50px",
66246
+ width: `calc((100% - 100px) * ${(currentStep - 1) / (WORKFLOW_STEPS.length - 1)})`
66228
66247
  }
66229
- ), currentStep > 1 && currentStep <= steps.length && /* @__PURE__ */ React.createElement(
66230
- "div",
66231
- {
66232
- className: "absolute top-5 h-0.5 bg-tina-orange -z-10 transition-all duration-500",
66233
- style: {
66234
- left: "50px",
66235
- width: `calc((100% - 100px) * ${(currentStep - 1) / (steps.length - 1)})`
66236
- }
66248
+ }
66249
+ ), currentStep > 2 && /* @__PURE__ */ React.createElement(
66250
+ "div",
66251
+ {
66252
+ className: "absolute top-5 h-0.5 bg-green-500 -z-10 transition-all duration-500",
66253
+ style: {
66254
+ left: "50px",
66255
+ width: `calc((100% - 100px) * ${Math.min(1, (currentStep - 2) / (WORKFLOW_STEPS.length - 1))})`
66237
66256
  }
66238
- ), currentStep > 2 && /* @__PURE__ */ React.createElement(
66257
+ }
66258
+ ), WORKFLOW_STEPS.map((step, index) => {
66259
+ const stepNumber = index + 1;
66260
+ const isActive = stepNumber === currentStep;
66261
+ const isCompleted = stepNumber < currentStep;
66262
+ return /* @__PURE__ */ React.createElement("div", { key: step.id, className: "flex flex-col items-center relative" }, /* @__PURE__ */ React.createElement(
66239
66263
  "div",
66240
66264
  {
66241
- className: "absolute top-5 h-0.5 bg-green-500 -z-10 transition-all duration-500",
66242
- style: {
66243
- left: "50px",
66244
- width: `calc((100% - 100px) * ${Math.min(1, (currentStep - 2) / (steps.length - 1))})`
66245
- }
66246
- }
66247
- ), steps.map((step, index) => {
66248
- const stepNumber = index + 1;
66249
- const isActive = stepNumber === currentStep;
66250
- const isCompleted = stepNumber < currentStep;
66251
- return /* @__PURE__ */ React.createElement(
66252
- "div",
66265
+ className: `w-10 h-10 rounded-full flex items-center justify-center font-medium mb-3 border-2 transition-all duration-300 select-none ${isCompleted ? "bg-green-500 border-green-500 text-white" : isActive ? "bg-tina-orange border-tina-orange text-white" : "bg-white border-gray-200 text-gray-400"}`
66266
+ },
66267
+ isCompleted ? /* @__PURE__ */ React.createElement(
66268
+ "svg",
66253
66269
  {
66254
- key: step.id,
66255
- className: "flex flex-col items-center relative"
66270
+ className: "w-5 h-5",
66271
+ fill: "currentColor",
66272
+ viewBox: "0 0 20 20"
66256
66273
  },
66257
66274
  /* @__PURE__ */ React.createElement(
66258
- "div",
66275
+ "path",
66259
66276
  {
66260
- className: `w-10 h-10 rounded-full flex items-center justify-center font-medium mb-3 border-2 transition-all duration-300 select-none ${isCompleted ? "bg-green-500 border-green-500 text-white" : isActive ? "bg-tina-orange border-tina-orange text-white" : "bg-white border-gray-200 text-gray-400"}`
66261
- },
66262
- isCompleted ? /* @__PURE__ */ React.createElement(
66263
- "svg",
66264
- {
66265
- className: "w-5 h-5",
66266
- fill: "currentColor",
66267
- viewBox: "0 0 20 20"
66268
- },
66269
- /* @__PURE__ */ React.createElement(
66270
- "path",
66271
- {
66272
- fillRule: "evenodd",
66273
- d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z",
66274
- clipRule: "evenodd"
66275
- }
66276
- )
66277
- ) : isActive ? /* @__PURE__ */ React.createElement(AiOutlineLoading, { className: "animate-spin text-lg" }) : stepNumber
66278
- ),
66279
- /* @__PURE__ */ React.createElement("div", { className: "text-center max-w-24" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold leading-tight" }, step.name), /* @__PURE__ */ React.createElement("div", { className: "text-xs text-gray-400 mt-1 leading-tight" }, step.description))
66277
+ fillRule: "evenodd",
66278
+ d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z",
66279
+ clipRule: "evenodd"
66280
+ }
66281
+ )
66282
+ ) : isActive ? /* @__PURE__ */ React.createElement(AiOutlineLoading, { className: "animate-spin text-lg" }) : stepNumber
66283
+ ), /* @__PURE__ */ React.createElement("div", { className: "text-center max-w-24" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold leading-tight" }, step.name), /* @__PURE__ */ React.createElement("div", { className: "text-xs text-gray-400 mt-1 leading-tight" }, step.description)));
66284
+ })), /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React.createElement("div", { className: "text-xs text-gray-500" }, "Estimated time: 1-2 min"), isExecuting && currentStep > 0 && /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-1 text-sm text-gray-500 tabular-nums" }, /* @__PURE__ */ React.createElement("svg", { className: "w-4 h-4", fill: "currentColor", viewBox: "0 0 20 20" }, /* @__PURE__ */ React.createElement(
66285
+ "path",
66286
+ {
66287
+ fillRule: "evenodd",
66288
+ d: "M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z",
66289
+ clipRule: "evenodd"
66290
+ }
66291
+ )), formatTime(elapsedTime))), /* @__PURE__ */ React.createElement(
66292
+ "a",
66293
+ {
66294
+ className: "underline text-tina-orange-dark font-medium text-xs",
66295
+ href: "https://tina.io/docs/tinacloud/editorial-workflow",
66296
+ target: "_blank"
66297
+ },
66298
+ "Learn more about Editorial Workflow"
66299
+ ));
66300
+ };
66301
+ const formatDefaultBranchName = (filePath, crudType) => {
66302
+ let result = filePath;
66303
+ const contentPrefix = "content/";
66304
+ if (result.startsWith(contentPrefix)) {
66305
+ result = result.substring(contentPrefix.length);
66306
+ }
66307
+ const lastDot = result.lastIndexOf(".");
66308
+ const lastSlash = Math.max(result.lastIndexOf("/"), result.lastIndexOf("\\"));
66309
+ if (lastDot > lastSlash && lastDot > 0) {
66310
+ result = result.slice(0, lastDot);
66311
+ }
66312
+ if (crudType === "delete") {
66313
+ result = `❌-${result}`;
66314
+ }
66315
+ return result;
66316
+ };
66317
+ const CreateBranchModal = ({
66318
+ close: close2,
66319
+ safeSubmit,
66320
+ path: path3,
66321
+ values,
66322
+ crudType,
66323
+ tinaForm,
66324
+ onBaseBranchDeleted
66325
+ }) => {
66326
+ const cms = useCMS$1();
66327
+ const tinaApi = cms.api.tina;
66328
+ const [newBranchName, setNewBranchName] = React.useState(
66329
+ formatDefaultBranchName(path3, crudType)
66330
+ );
66331
+ const [isBranchGuardChecking, setIsBranchGuardChecking] = React.useState(false);
66332
+ const {
66333
+ isExecuting,
66334
+ errorMessage,
66335
+ currentStep,
66336
+ elapsedTime,
66337
+ executeWorkflow,
66338
+ reset: reset2
66339
+ } = useEditorialWorkflow();
66340
+ const executeEditorialWorkflow = async () => {
66341
+ setIsBranchGuardChecking(true);
66342
+ const baseBranch = decodeURIComponent(tinaApi.branch);
66343
+ let baseBranchExists = true;
66344
+ try {
66345
+ console.debug(
66346
+ "[tina:branch-guard] executeEditorialWorkflow: checking base branch:",
66347
+ baseBranch
66280
66348
  );
66281
- })), /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React.createElement("div", { className: "text-xs text-gray-500" }, "Estimated time: 1-2 min "), isExecuting && currentStep > 0 && /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-1 text-sm text-gray-500 tabular-nums" }, /* @__PURE__ */ React.createElement("svg", { className: "w-4 h-4", fill: "currentColor", viewBox: "0 0 20 20" }, /* @__PURE__ */ React.createElement(
66282
- "path",
66283
- {
66284
- fillRule: "evenodd",
66285
- d: "M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z",
66286
- clipRule: "evenodd"
66287
- }
66288
- )), formatTime(elapsedTime))), /* @__PURE__ */ React.createElement(
66289
- "a",
66290
- {
66291
- className: "underline text-tina-orange-dark font-medium text-xs",
66292
- href: "https://tina.io/docs/tinacloud/editorial-workflow",
66293
- target: "_blank"
66294
- },
66295
- "Learn more about Editorial Workflow"
66296
- ));
66349
+ baseBranchExists = await tinaApi.branchExists(baseBranch);
66350
+ } catch (err) {
66351
+ console.error(
66352
+ "[tina:branch-guard] executeEditorialWorkflow: branchExists threw, failing open:",
66353
+ err
66354
+ );
66355
+ }
66356
+ console.debug(
66357
+ "[tina:branch-guard] executeEditorialWorkflow: base branch exists?",
66358
+ baseBranchExists
66359
+ );
66360
+ if (!baseBranchExists) {
66361
+ console.debug(
66362
+ "[tina:branch-guard] executeEditorialWorkflow: base branch deleted — handing off"
66363
+ );
66364
+ onBaseBranchDeleted == null ? void 0 : onBaseBranchDeleted();
66365
+ return;
66366
+ }
66367
+ setIsBranchGuardChecking(false);
66368
+ const success = await executeWorkflow({
66369
+ branchName: `tina/${newBranchName}`,
66370
+ baseBranch,
66371
+ path: path3,
66372
+ values,
66373
+ crudType,
66374
+ tinaForm
66375
+ });
66376
+ if (success) {
66377
+ close2();
66378
+ }
66297
66379
  };
66298
66380
  const renderStateContent = () => {
66299
66381
  if (isExecuting) {
66300
- return renderProgressIndicator();
66382
+ return /* @__PURE__ */ React.createElement(
66383
+ WorkflowProgressIndicator,
66384
+ {
66385
+ currentStep,
66386
+ isExecuting,
66387
+ elapsedTime
66388
+ }
66389
+ );
66301
66390
  } else {
66302
66391
  return /* @__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(
66303
66392
  "a",
@@ -66315,7 +66404,7 @@ const CreateBranchModal = ({
66315
66404
  placeholder: "e.g. {{PAGE-NAME}}-updates",
66316
66405
  value: newBranchName,
66317
66406
  onChange: (e3) => {
66318
- setErrorMessage("");
66407
+ reset2();
66319
66408
  setNewBranchName(e3.target.value);
66320
66409
  }
66321
66410
  }
@@ -66336,7 +66425,7 @@ const CreateBranchModal = ({
66336
66425
  variant: "primary",
66337
66426
  align: "start",
66338
66427
  className: "w-full sm:w-auto",
66339
- disabled: newBranchName === "" || disabled,
66428
+ disabled: newBranchName === "" || isBranchGuardChecking,
66340
66429
  onMainAction: executeEditorialWorkflow,
66341
66430
  items: [
66342
66431
  {
@@ -66374,6 +66463,89 @@ const PrefixedTextField = ({
66374
66463
  }
66375
66464
  )));
66376
66465
  };
66466
+ const BranchDeletedModal = ({
66467
+ branchName,
66468
+ close: close2,
66469
+ path: path3,
66470
+ values,
66471
+ crudType,
66472
+ tinaForm
66473
+ }) => {
66474
+ const cms = useCMS$1();
66475
+ const tinaApi = cms.api.tina;
66476
+ const [newBranchName, setNewBranchName] = React.useState("");
66477
+ const baseBranch = tinaApi.protectedBranches[0] || cms.api.tina.schema.config.config.repoProvider.defaultBranchName || "main";
66478
+ const {
66479
+ isExecuting,
66480
+ errorMessage,
66481
+ currentStep,
66482
+ elapsedTime,
66483
+ executeWorkflow,
66484
+ reset: reset2
66485
+ } = useEditorialWorkflow();
66486
+ const handleCreate = async () => {
66487
+ const success = await executeWorkflow({
66488
+ branchName: `tina/${newBranchName}`,
66489
+ baseBranch,
66490
+ path: path3,
66491
+ values,
66492
+ crudType,
66493
+ tinaForm
66494
+ });
66495
+ if (success)
66496
+ close2();
66497
+ };
66498
+ return /* @__PURE__ */ React.createElement(Modal, { className: "flex" }, /* @__PURE__ */ React.createElement(PopupModal, { className: "w-auto" }, /* @__PURE__ */ React.createElement(ModalHeader, { close: isExecuting ? void 0 : close2 }, "Branch no longer exists"), /* @__PURE__ */ React.createElement(ModalBody, { padded: true }, isExecuting ? /* @__PURE__ */ React.createElement(
66499
+ WorkflowProgressIndicator,
66500
+ {
66501
+ currentStep,
66502
+ isExecuting,
66503
+ elapsedTime
66504
+ }
66505
+ ) : /* @__PURE__ */ React.createElement("div", { className: "max-w-sm" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start gap-3 p-3 mb-4 bg-yellow-50 border border-yellow-200 rounded text-yellow-800 text-sm" }, /* @__PURE__ */ React.createElement(
66506
+ GitBranchIcon,
66507
+ {
66508
+ className: "w-4 h-4 mt-0.5 flex-shrink-0 text-yellow-600",
66509
+ style: { fill: "none" }
66510
+ }
66511
+ ), /* @__PURE__ */ React.createElement("span", null, "The branch", " ", /* @__PURE__ */ React.createElement("span", { className: "font-mono font-semibold" }, branchName), " ", "no longer exists. It may have been merged or deleted. Your changes cannot be pushed to it.")), /* @__PURE__ */ React.createElement("p", { className: "text-sm text-gray-700 mb-4" }, "Create a new branch from", " ", /* @__PURE__ */ React.createElement("span", { className: "font-mono font-semibold" }, baseBranch), " to continue editing, or cancel and switch to an existing branch from the branch menu."), 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(
66512
+ PrefixedTextField,
66513
+ {
66514
+ name: "new-branch-name",
66515
+ label: "New Branch Name",
66516
+ placeholder: "e.g. my-updates",
66517
+ value: newBranchName,
66518
+ onChange: (e3) => {
66519
+ reset2();
66520
+ setNewBranchName(formatBranchName(e3.target.value));
66521
+ }
66522
+ }
66523
+ ))), !isExecuting && /* @__PURE__ */ React.createElement(ModalActions, { align: "end" }, /* @__PURE__ */ React.createElement(
66524
+ Button$2,
66525
+ {
66526
+ variant: "secondary",
66527
+ className: "w-full sm:w-auto",
66528
+ onClick: close2
66529
+ },
66530
+ "Cancel"
66531
+ ), /* @__PURE__ */ React.createElement(
66532
+ Button$2,
66533
+ {
66534
+ variant: "primary",
66535
+ className: "w-full sm:w-auto",
66536
+ disabled: !newBranchName,
66537
+ onClick: handleCreate
66538
+ },
66539
+ /* @__PURE__ */ React.createElement(
66540
+ GitBranchIcon,
66541
+ {
66542
+ className: "w-4 h-4 mr-1",
66543
+ style: { fill: "none" }
66544
+ }
66545
+ ),
66546
+ "Create new branch"
66547
+ ))));
66548
+ };
66377
66549
  const NoFieldsPlaceholder = () => /* @__PURE__ */ React.createElement(
66378
66550
  "div",
66379
66551
  {
@@ -66430,6 +66602,8 @@ const FormBuilder = ({
66430
66602
  const cms = useCMS$1();
66431
66603
  const hideFooter = !!rest.hideFooter;
66432
66604
  const [createBranchModalOpen, setCreateBranchModalOpen] = React.useState(false);
66605
+ const [deletedBranchModalOpen, setDeletedBranchModalOpen] = React.useState(false);
66606
+ const [isGuardChecking, setIsGuardChecking] = React.useState(false);
66433
66607
  const tinaForm = form.tinaForm;
66434
66608
  const finalForm = form.tinaForm.finalForm;
66435
66609
  React.useEffect(() => {
@@ -66489,26 +66663,79 @@ const FormBuilder = ({
66489
66663
  const canSubmit = !pristine && !submitting && !hasValidationErrors && !(invalid && !dirtySinceLastSubmit);
66490
66664
  const safeSubmit = async () => {
66491
66665
  if (canSubmit) {
66666
+ const alertsBefore = new Set(cms.alerts.all.map((a2) => a2.id));
66667
+ console.debug(
66668
+ "[tina:branch-guard] safeSubmit: calling handleSubmit"
66669
+ );
66492
66670
  const result = await handleSubmit();
66493
66671
  if (result && result[FORM_ERROR]) {
66494
66672
  const error2 = result[FORM_ERROR];
66673
+ const errorMsg = error2 instanceof Error ? error2.message : String(error2);
66674
+ console.debug(
66675
+ "[tina:branch-guard] safeSubmit: FORM_ERROR detected:",
66676
+ errorMsg
66677
+ );
66678
+ if (/branch.*not found/i.test(errorMsg)) {
66679
+ console.debug(
66680
+ "[tina:branch-guard] safeSubmit: branch-not-found — dismissing alert and opening modal"
66681
+ );
66682
+ for (const alert of cms.alerts.all) {
66683
+ if (!alertsBefore.has(alert.id) && alert.level === "error") {
66684
+ cms.alerts.dismiss(alert);
66685
+ }
66686
+ }
66687
+ setDeletedBranchModalOpen(true);
66688
+ return;
66689
+ }
66495
66690
  captureEvent(SaveContentErrorEvent, {
66496
66691
  documentPath: tinaForm.path,
66497
- error: error2 instanceof Error ? error2.message : String(error2)
66692
+ error: errorMsg
66498
66693
  });
66499
66694
  } else {
66500
66695
  captureEvent(SavedContentEvent, {
66501
66696
  documentPath: tinaForm.path
66502
66697
  });
66503
66698
  }
66699
+ } else {
66700
+ console.debug(
66701
+ "[tina:branch-guard] safeSubmit: skipped — canSubmit is false"
66702
+ );
66504
66703
  }
66505
66704
  };
66506
66705
  const safeHandleSubmit = async () => {
66706
+ setIsGuardChecking(true);
66707
+ const currentBranch = decodeURIComponent(cms.api.tina.getBranch());
66708
+ let exists = true;
66709
+ try {
66710
+ console.debug(
66711
+ "[tina:branch-guard] safeHandleSubmit: checking branch:",
66712
+ currentBranch
66713
+ );
66714
+ exists = await cms.api.tina.branchExists(currentBranch);
66715
+ } catch (err) {
66716
+ console.error(
66717
+ "[tina:branch-guard] safeHandleSubmit: branchExists threw, failing open:",
66718
+ err
66719
+ );
66720
+ }
66721
+ console.debug(
66722
+ "[tina:branch-guard] safeHandleSubmit: branchExists returned:",
66723
+ exists
66724
+ );
66725
+ if (!exists) {
66726
+ console.debug(
66727
+ "[tina:branch-guard] safeHandleSubmit: branch missing — opening modal"
66728
+ );
66729
+ setIsGuardChecking(false);
66730
+ setDeletedBranchModalOpen(true);
66731
+ return;
66732
+ }
66507
66733
  if (usingProtectedBranch) {
66508
66734
  setCreateBranchModalOpen(true);
66509
66735
  } else {
66510
- safeSubmit();
66736
+ await safeSubmit();
66511
66737
  }
66738
+ setIsGuardChecking(false);
66512
66739
  };
66513
66740
  return /* @__PURE__ */ React.createElement(React.Fragment, null, createBranchModalOpen && /* @__PURE__ */ React.createElement(
66514
66741
  CreateBranchModal,
@@ -66518,7 +66745,21 @@ const FormBuilder = ({
66518
66745
  path: tinaForm.path,
66519
66746
  values: tinaForm.values,
66520
66747
  tinaForm,
66521
- close: () => setCreateBranchModalOpen(false)
66748
+ close: () => setCreateBranchModalOpen(false),
66749
+ onBaseBranchDeleted: () => {
66750
+ setCreateBranchModalOpen(false);
66751
+ setDeletedBranchModalOpen(true);
66752
+ }
66753
+ }
66754
+ ), deletedBranchModalOpen && /* @__PURE__ */ React.createElement(
66755
+ BranchDeletedModal,
66756
+ {
66757
+ branchName: decodeURIComponent(cms.api.tina.getBranch()),
66758
+ close: () => setDeletedBranchModalOpen(false),
66759
+ path: tinaForm.path,
66760
+ values: tinaForm.values,
66761
+ crudType: tinaForm.crudType,
66762
+ tinaForm
66522
66763
  }
66523
66764
  ), /* @__PURE__ */ React.createElement(DragDropContext, { onDragEnd: moveArrayItem }, /* @__PURE__ */ React.createElement(FormKeyBindings, { onSubmit: safeHandleSubmit }), /* @__PURE__ */ React.createElement(FormPortalProvider, null, /* @__PURE__ */ React.createElement(FormWrapper, { id: tinaForm.id }, (tinaForm == null ? void 0 : tinaForm.fields.length) ? /* @__PURE__ */ React.createElement(
66524
66765
  FieldsBuilder,
@@ -66542,8 +66783,8 @@ const FormBuilder = ({
66542
66783
  Button$2,
66543
66784
  {
66544
66785
  onClick: safeHandleSubmit,
66545
- disabled: !canSubmit,
66546
- busy: submitting,
66786
+ disabled: !canSubmit || isGuardChecking,
66787
+ busy: submitting || isGuardChecking,
66547
66788
  variant: "primary"
66548
66789
  },
66549
66790
  submitting && /* @__PURE__ */ React.createElement(LoadingDots, null),
@@ -68183,7 +68424,9 @@ const useImageToolbarButton = (state) => {
68183
68424
  allowDelete: true,
68184
68425
  directory: "",
68185
68426
  onSelect: (media) => {
68186
- insertImg(editor, media);
68427
+ var _a2, _b;
68428
+ const src = typeof ((_b = (_a2 = cms == null ? void 0 : cms.media) == null ? void 0 : _a2.store) == null ? void 0 : _b.parse) === "function" ? cms.media.store.parse(media) : media.src;
68429
+ insertImg(editor, { ...media, src: src || media.src });
68187
68430
  }
68188
68431
  });
68189
68432
  };
@@ -121847,6 +122090,12 @@ mutation addPendingDocumentMutation(
121847
122090
  var _a2;
121848
122091
  return this.usingEditorialWorkflow && ((_a2 = this.protectedBranches) == null ? void 0 : _a2.includes(decodeURIComponent(this.branch)));
121849
122092
  }
122093
+ async branchExists(branchName) {
122094
+ if (this.isLocalMode)
122095
+ return true;
122096
+ const branches = await this.listBranches({ includeIndexStatus: false });
122097
+ return branches.some((b) => b.name === branchName);
122098
+ }
121850
122099
  async createBranch({ baseBranch, branchName }) {
121851
122100
  const url = `${this.contentApiBase}/github/${this.clientId}/create_branch`;
121852
122101
  try {
@@ -124754,7 +125003,7 @@ const RenderForm$1 = ({
124754
125003
  }
124755
125004
  return true;
124756
125005
  }
124757
- const isValid = /[\.\-_\/a-zA-Z0-9]*$/.test(value);
125006
+ const isValid = /^[\.\-_\/a-zA-Z0-9]*$/.test(value);
124758
125007
  if (value && !isValid) {
124759
125008
  return "Must contain only a-z, A-Z, 0-9, -, _, ., or /.";
124760
125009
  }
@@ -125604,6 +125853,7 @@ export {
125604
125853
  DateFieldPlugin,
125605
125854
  DeleteImageButton,
125606
125855
  Dismissible,
125856
+ DisplayOnlyFieldPlugin,
125607
125857
  DragHandle,
125608
125858
  DragIcon,
125609
125859
  DropdownButton,
@@ -125651,6 +125901,7 @@ export {
125651
125901
  ImageField,
125652
125902
  ImageFieldPlugin,
125653
125903
  ImageUpload,
125904
+ InfoBox,
125654
125905
  InfoIcon,
125655
125906
  Input$1 as Input,
125656
125907
  ItalicIcon$1 as ItalicIcon,
@@ -147,6 +147,7 @@ export declare class Client {
147
147
  githubPullRequestUrl?: string;
148
148
  }[]>;
149
149
  usingProtectedBranch(): boolean;
150
+ branchExists(branchName: string): Promise<boolean>;
150
151
  createBranch({ baseBranch, branchName }: BranchData): Promise<string>;
151
152
  getLatestVersion(): Promise<LatestVersionResponse>;
152
153
  /**
@@ -71,6 +71,13 @@ export interface MediaStore {
71
71
  * @default false
72
72
  */
73
73
  isStatic?: boolean;
74
+ /**
75
+ * Converts a Media object to the value stored in a form field.
76
+ *
77
+ * Typically returns `media.src`. If not implemented, the image field
78
+ * plugin falls back to `media.src`.
79
+ */
80
+ parse?(media: Media): string;
74
81
  }
75
82
  export declare type MediaListOffset = string | number;
76
83
  /**
@@ -0,0 +1,12 @@
1
+ import * as React from 'react';
2
+ export declare const DisplayOnlyFieldPlugin: {
3
+ name: string;
4
+ Component: (props: any) => React.JSX.Element;
5
+ };
6
+ export declare const InfoBox: ({ message, links, }: {
7
+ message: string;
8
+ links?: {
9
+ text: string;
10
+ url: string;
11
+ }[];
12
+ }) => React.FC<any>;
@@ -18,3 +18,4 @@ export * from './reference-field-plugin';
18
18
  export * from './button-toggle-field-plugin';
19
19
  export * from './hidden-field-plugin';
20
20
  export * from './password-field-plugin';
21
+ export * from './display-only-field-plugin';
@@ -0,0 +1,10 @@
1
+ import * as React from 'react';
2
+ import { Form } from '../forms';
3
+ export declare const BranchDeletedModal: ({ branchName, close, path, values, crudType, tinaForm, }: {
4
+ branchName: string;
5
+ close: () => void;
6
+ path: string;
7
+ values: Record<string, unknown>;
8
+ crudType: string;
9
+ tinaForm?: Form;
10
+ }) => React.JSX.Element;
@@ -1,12 +1,13 @@
1
1
  import * as React from 'react';
2
2
  import { Form } from '../forms';
3
- export declare const CreateBranchModal: ({ close, safeSubmit, path, values, crudType, tinaForm, }: {
3
+ export declare const CreateBranchModal: ({ close, safeSubmit, path, values, crudType, tinaForm, onBaseBranchDeleted, }: {
4
4
  safeSubmit: () => Promise<void>;
5
5
  close: () => void;
6
6
  path: string;
7
7
  values: Record<string, unknown>;
8
8
  crudType: string;
9
9
  tinaForm?: Form;
10
+ onBaseBranchDeleted?: () => void;
10
11
  }) => React.JSX.Element;
11
12
  export declare const PrefixedTextField: ({ label, prefix, ...props }: {
12
13
  [x: string]: any;
@@ -0,0 +1,26 @@
1
+ import { Form } from '../forms';
2
+ export declare const WORKFLOW_STEPS: {
3
+ id: number;
4
+ name: string;
5
+ description: string;
6
+ }[];
7
+ export declare const formatTime: (seconds: number) => string;
8
+ export interface ExecuteWorkflowOptions {
9
+ branchName: string;
10
+ baseBranch: string;
11
+ path: string;
12
+ values: Record<string, unknown>;
13
+ crudType: string;
14
+ tinaForm?: Form;
15
+ }
16
+ export interface UseEditorialWorkflowResult {
17
+ isExecuting: boolean;
18
+ errorMessage: string;
19
+ currentStep: number;
20
+ elapsedTime: number;
21
+ /** Returns true on success, false on failure (error captured in errorMessage) */
22
+ executeWorkflow: (opts: ExecuteWorkflowOptions) => Promise<boolean>;
23
+ /** Reset error/executing state so the form can be retried */
24
+ reset: () => void;
25
+ }
26
+ export declare function useEditorialWorkflow(): UseEditorialWorkflowResult;
@@ -0,0 +1,8 @@
1
+ import * as React from 'react';
2
+ interface WorkflowProgressIndicatorProps {
3
+ currentStep: number;
4
+ isExecuting: boolean;
5
+ elapsedTime: number;
6
+ }
7
+ export declare const WorkflowProgressIndicator: ({ currentStep, isExecuting, elapsedTime, }: WorkflowProgressIndicatorProps) => React.JSX.Element;
8
+ export {};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "tinacms",
3
3
  "type": "module",
4
4
  "typings": "dist/index.d.ts",
5
- "version": "3.7.2",
5
+ "version": "3.7.3",
6
6
  "main": "dist/index.js",
7
7
  "module": "./dist/index.js",
8
8
  "exports": {
@@ -114,9 +114,9 @@
114
114
  "webfontloader": "1.6.28",
115
115
  "yup": "^1.6.1",
116
116
  "zod": "^3.24.2",
117
- "@tinacms/mdx": "2.1.1",
118
- "@tinacms/schema-tools": "2.7.1",
119
- "@tinacms/search": "1.2.9"
117
+ "@tinacms/schema-tools": "2.7.2",
118
+ "@tinacms/search": "1.2.10",
119
+ "@tinacms/mdx": "2.1.2"
120
120
  },
121
121
  "devDependencies": {
122
122
  "@graphql-tools/utils": "^10.8.1",