tinacms 3.13.0 → 3.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
  [![npm version](https://img.shields.io/npm/v/tinacms.svg?style=flat)](https://www.npmjs.com/package/tinacms)
3
3
  [![Build, Test, Lint for Main](https://github.com/tinacms/tinacms/actions/workflows/main.yml/badge.svg?branch=main&event=push)](https://github.com/tinacms/tinacms/actions/workflows/main.yml)
4
4
 
5
- # [![TINA CMS](./.github/assets/tinacms-logo-small-default.svg)](https://tina.io)
5
+ # [![TINA CMS](https://raw.githubusercontent.com/tinacms/tinacms/main/.github/assets/tinacms-logo-small-default.svg)](https://tina.io)
6
6
 
7
- [![Tina Demo](./.github/assets/homepage-demo-2.gif)](https://tina.io/)
7
+ [![Tina Demo](https://raw.githubusercontent.com/tinacms/tinacms/main/.github/assets/homepage-demo-2.gif)](https://tina.io/)
8
8
 
9
9
  Tina is a headless content management system with support for **Markdown**, MDX, JSON, YAML, and more.
10
10
 
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Resolve a router splat into a same-origin preview path.
3
+ *
4
+ * Do not filter the splat instead: the URL parser treats `\` as `/` and drops
5
+ * leading tabs, so a character filter does not hold. Resolve the value first,
6
+ * then reject any result that left this origin.
7
+ *
8
+ * `offOrigin` is true only when a splat was supplied and resolved elsewhere, so
9
+ * callers can tell a rejected link from an empty one.
10
+ */
11
+ export declare function resolvePreviewPath(splat: string | undefined, baseOrigin?: string): {
12
+ path: string;
13
+ offOrigin: boolean;
14
+ };
package/dist/index.js CHANGED
@@ -11115,7 +11115,7 @@ class TinaMediaStore {
11115
11115
  if (!this.isLocal) {
11116
11116
  const encodedBranch = this.encodedBranchParam();
11117
11117
  res = await this.api.authProvider.fetchWithToken(
11118
- `${this.listUrl}/list/${options.directory || ""}?limit=${options.limit || 20}${options.offset ? `&cursor=${options.offset}` : ""}${encodedBranch ? `&branch=${encodedBranch}` : ""}${options.search ? `&search=${encodeURIComponent(options.search)}` : ""}${((_a = options.ext) == null ? void 0 : _a.length) ? `&ext=${encodeURIComponent(options.ext.join(","))}` : ""}`
11118
+ `${this.listUrl}/list/${options.directory || ""}?limit=${options.limit || 20}${options.offset ? `&cursor=${encodeURIComponent(options.offset)}` : ""}${encodedBranch ? `&branch=${encodedBranch}` : ""}${options.search ? `&search=${encodeURIComponent(options.search)}` : ""}${((_a = options.ext) == null ? void 0 : _a.length) ? `&ext=${encodeURIComponent(options.ext.join(","))}` : ""}`
11119
11119
  );
11120
11120
  if (res.status == 401) {
11121
11121
  throw E_UNAUTHORIZED;
@@ -11125,7 +11125,7 @@ class TinaMediaStore {
11125
11125
  }
11126
11126
  } else {
11127
11127
  res = await this.fetchFunction(
11128
- `${this.url}/list/${options.directory || ""}?limit=${options.limit || 20}${options.offset ? `&cursor=${options.offset}` : ""}${options.search ? `&search=${encodeURIComponent(options.search)}` : ""}${((_b = options.ext) == null ? void 0 : _b.length) ? `&ext=${encodeURIComponent(options.ext.join(","))}` : ""}`
11128
+ `${this.url}/list/${options.directory || ""}?limit=${options.limit || 20}${options.offset ? `&cursor=${encodeURIComponent(options.offset)}` : ""}${options.search ? `&search=${encodeURIComponent(options.search)}` : ""}${((_b = options.ext) == null ? void 0 : _b.length) ? `&ext=${encodeURIComponent(options.ext.join(","))}` : ""}`
11129
11129
  );
11130
11130
  if (res.status == 404) {
11131
11131
  throw E_BAD_ROUTE;
@@ -14005,6 +14005,70 @@ function createCloudConfig({
14005
14005
  ...options
14006
14006
  };
14007
14007
  }
14008
+ const LOADING_MESSAGES = [
14009
+ "Hang tight, TinaCMS is looking for llamas 🦙 🦙 🦙",
14010
+ "Reticulating llama splines 🦙",
14011
+ "Herding llamas into collections 🦙",
14012
+ "Brushing llama fur 🦙 🦙",
14013
+ "Teaching a llama to write frontmatter 🦙",
14014
+ "Feeding the GraphQL llama 🦙",
14015
+ "Counting llamas in your content folder 🦙",
14016
+ "Asking a llama what changed in git 🦙",
14017
+ "Llamas are proofreading your schema 🦙",
14018
+ "Untangling markdown with llama teeth 🦙",
14019
+ "Waking the llamas in the media library 🦙",
14020
+ "Alphabetising llamas by collection 🦙",
14021
+ "Convincing a llama that MDX is fine 🦙",
14022
+ "Saddling up the content layer 🦙",
14023
+ "Polishing the sidebar, llama style 🦙",
14024
+ "One more llama, almost there 🦙"
14025
+ ];
14026
+ const CYCLE_MS = 2e3;
14027
+ const FADE_MS = 200;
14028
+ const prefersReducedMotion = () => typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
14029
+ const pickOther = (current, length) => {
14030
+ if (length < 2) return 0;
14031
+ const offset2 = 1 + Math.floor(Math.random() * (length - 1));
14032
+ return (current + offset2) % length;
14033
+ };
14034
+ const LoadingMessage = ({
14035
+ className,
14036
+ style,
14037
+ messages = LOADING_MESSAGES
14038
+ }) => {
14039
+ const [index, setIndex] = React__default.useState(0);
14040
+ const [visible, setVisible] = React__default.useState(false);
14041
+ const [reducedMotion] = React__default.useState(prefersReducedMotion);
14042
+ React__default.useEffect(() => {
14043
+ setIndex((current) => pickOther(current, messages.length));
14044
+ setVisible(true);
14045
+ }, [messages.length]);
14046
+ React__default.useEffect(() => {
14047
+ const fadeOut = setTimeout(() => setVisible(false), CYCLE_MS - FADE_MS);
14048
+ const next = setTimeout(() => {
14049
+ setIndex((current) => pickOther(current, messages.length));
14050
+ setVisible(true);
14051
+ }, CYCLE_MS);
14052
+ return () => {
14053
+ clearTimeout(fadeOut);
14054
+ clearTimeout(next);
14055
+ };
14056
+ }, [index, messages.length]);
14057
+ return /* @__PURE__ */ React__default.createElement(
14058
+ "p",
14059
+ {
14060
+ className,
14061
+ "aria-live": "polite",
14062
+ style: {
14063
+ ...style,
14064
+ opacity: reducedMotion || visible ? 1 : 0,
14065
+ transform: reducedMotion || visible ? "none" : "translateY(4px)",
14066
+ transition: reducedMotion ? void 0 : `opacity ${FADE_MS}ms ease-out, transform ${FADE_MS}ms ease-out`
14067
+ }
14068
+ },
14069
+ messages[index]
14070
+ );
14071
+ };
14008
14072
  const SidebarLoadingPlaceholder = () => /* @__PURE__ */ React.createElement(
14009
14073
  "div",
14010
14074
  {
@@ -14018,7 +14082,7 @@ const SidebarLoadingPlaceholder = () => /* @__PURE__ */ React.createElement(
14018
14082
  animationDuration: "150ms"
14019
14083
  }
14020
14084
  },
14021
- /* @__PURE__ */ React.createElement("p", { className: "block pb-5" }, "Please wait while TinaCMS", /* @__PURE__ */ React.createElement("br", null), "loads your content"),
14085
+ /* @__PURE__ */ React.createElement(LoadingMessage, { className: "block pb-5" }),
14022
14086
  /* @__PURE__ */ React.createElement(LoadingDots, { color: "var(--tina-color-primary)" })
14023
14087
  );
14024
14088
  class SidebarState {
@@ -14158,7 +14222,7 @@ const FormModal = ({ plugin, close: close2 }) => {
14158
14222
  const name = "tinacms";
14159
14223
  const type = "module";
14160
14224
  const typings = "dist/index.d.ts";
14161
- const version = "3.13.0";
14225
+ const version = "3.14.0";
14162
14226
  const main = "dist/index.js";
14163
14227
  const module$1 = "./dist/index.js";
14164
14228
  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" };
@@ -16553,6 +16617,9 @@ const useFieldReference = (fieldName) => {
16553
16617
  } else {
16554
16618
  doc = document;
16555
16619
  }
16620
+ if (!doc) {
16621
+ return;
16622
+ }
16556
16623
  const fieldEle = doc.querySelector(
16557
16624
  `[data-tinafield="${fieldName}"]`
16558
16625
  );
@@ -73902,7 +73969,7 @@ const Loader = (props) => {
73902
73969
  ))
73903
73970
  ),
73904
73971
  /* @__PURE__ */ React__default.createElement(
73905
- "p",
73972
+ LoadingMessage,
73906
73973
  {
73907
73974
  style: {
73908
73975
  fontSize: "18px",
@@ -73912,8 +73979,7 @@ const Loader = (props) => {
73912
73979
  fontFamily: "'Inter', sans-serif",
73913
73980
  fontWeight: "normal"
73914
73981
  }
73915
- },
73916
- "Please wait, Tina is loading data..."
73982
+ }
73917
73983
  )
73918
73984
  )
73919
73985
  ), props.children);
@@ -74214,7 +74280,7 @@ const LoadingPage = () => /* @__PURE__ */ React__default.createElement(React__de
74214
74280
  ))
74215
74281
  ),
74216
74282
  /* @__PURE__ */ React__default.createElement(
74217
- "p",
74283
+ LoadingMessage,
74218
74284
  {
74219
74285
  style: {
74220
74286
  fontSize: "16px",
@@ -74224,8 +74290,7 @@ const LoadingPage = () => /* @__PURE__ */ React__default.createElement(React__de
74224
74290
  fontFamily: "'Inter', sans-serif",
74225
74291
  fontWeight: "normal"
74226
74292
  }
74227
- },
74228
- "Please wait, Tina is loading data..."
74293
+ }
74229
74294
  )
74230
74295
  )
74231
74296
  ));
@@ -76172,6 +76237,24 @@ const ScreenPage = () => {
76172
76237
  } })));
76173
76238
  });
76174
76239
  };
76240
+ function resolvePreviewPath(splat, baseOrigin = typeof window !== "undefined" ? window.location.origin : "") {
76241
+ if (!baseOrigin || !splat) {
76242
+ return { path: "/", offOrigin: false };
76243
+ }
76244
+ try {
76245
+ const resolved = new URL(`/${splat}`, baseOrigin);
76246
+ if (resolved.origin !== baseOrigin) {
76247
+ return { path: "/", offOrigin: true };
76248
+ }
76249
+ const path = `${resolved.pathname}${resolved.search}${resolved.hash}`;
76250
+ if (new URL(path, baseOrigin).origin !== baseOrigin) {
76251
+ return { path: "/", offOrigin: true };
76252
+ }
76253
+ return { path, offOrigin: false };
76254
+ } catch {
76255
+ return { path: "/", offOrigin: true };
76256
+ }
76257
+ }
76175
76258
  const TROUBLESHOOTING_URL = "https://tina.io/docs/tinacloud/troubleshooting";
76176
76259
  const ErrorModalContent = (props) => {
76177
76260
  const { title, message } = props;
@@ -76251,12 +76334,23 @@ const SetPreviewFlag = ({
76251
76334
  return null;
76252
76335
  };
76253
76336
  const PreviewInner = ({ preview, config }) => {
76337
+ const cms = useCMS$1();
76254
76338
  const params = useParams();
76255
76339
  const navigate = useNavigate();
76256
- const [url, setURL] = React__default.useState(`/${params["*"]}`);
76340
+ const splat = params["*"];
76341
+ const { path: paramURL, offOrigin } = resolvePreviewPath(splat);
76342
+ const [url, setURL] = React__default.useState(paramURL);
76257
76343
  const [reportedURL, setReportedURL] = useState(null);
76258
76344
  const ref = React__default.useRef(null);
76259
- const paramURL = `/${params["*"]}`;
76345
+ React__default.useEffect(() => {
76346
+ if (!offOrigin) {
76347
+ return;
76348
+ }
76349
+ cms.alerts.warn(
76350
+ "This preview link points to a different site, so it was not opened."
76351
+ );
76352
+ navigate(`/~${paramURL}`, { replace: true });
76353
+ }, [splat, offOrigin]);
76260
76354
  React__default.useEffect(() => {
76261
76355
  if (reportedURL !== paramURL && paramURL) {
76262
76356
  setURL(paramURL);
@@ -146,7 +146,7 @@ export type TinaMarkdownContent = {
146
146
  export declare const TinaMarkdown: <CustomComponents extends {
147
147
  [key: string]: object;
148
148
  } = any>({ content, components, }: {
149
- content: TinaMarkdownContent | TinaMarkdownContent[];
149
+ content: TinaMarkdownContent | TinaMarkdownContent[] | null | undefined;
150
150
  components?: Components<{}> | Components<{ [BK in keyof CustomComponents]: (props: CustomComponents[BK]) => JSX.Element; }>;
151
151
  }) => React.JSX.Element;
152
152
  export {};
@@ -148,7 +148,7 @@ export type TinaMarkdownContent = {
148
148
  export declare const StaticTinaMarkdown: <CustomComponents extends {
149
149
  [key: string]: object;
150
150
  } = any>({ content, components, }: {
151
- content: TinaMarkdownContent | TinaMarkdownContent[];
151
+ content: TinaMarkdownContent | TinaMarkdownContent[] | null | undefined;
152
152
  components?: Components<{}> | Components<{ [BK in keyof CustomComponents]: (props: CustomComponents[BK]) => JSX.Element; }>;
153
153
  }) => React.JSX.Element;
154
154
  export {};
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ export declare const LOADING_MESSAGES: string[];
3
+ interface LoadingMessageProps {
4
+ className?: string;
5
+ style?: React.CSSProperties;
6
+ messages?: string[];
7
+ }
8
+ export declare const LoadingMessage: ({ className, style, messages, }: LoadingMessageProps) => React.JSX.Element;
9
+ export {};
@@ -3,9 +3,10 @@ import { FieldRenderProps } from '../../form-builder';
3
3
  /**
4
4
  * Props every field plugin receives.
5
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.
6
+ * `react-final-form` v6 declares `[otherProp: string]: any` on `FieldRenderProps`,
7
+ * so the extras `FieldsBuilder` passes down would type-check implicitly. They are
8
+ * declared explicitly anyway: v7 drops that index signature, and the next bump
9
+ * should not have to rediscover them.
9
10
  */
10
11
  export interface FieldProps<InputProps> extends FieldRenderProps<any, HTMLElement> {
11
12
  field: Field & InputProps;
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.13.0",
5
+ "version": "3.14.0",
6
6
  "main": "dist/index.js",
7
7
  "module": "./dist/index.js",
8
8
  "exports": {
@@ -82,8 +82,8 @@
82
82
  "color-string": "^1.9.1",
83
83
  "date-fns": "4.1.0",
84
84
  "es-toolkit": "^1.42.0",
85
- "final-form": "^5.0.1",
86
- "final-form-arrays": "^4.0.1",
85
+ "final-form": "4.20.10",
86
+ "final-form-arrays": "^3.1.0",
87
87
  "final-form-set-field-data": "^1.0.2",
88
88
  "graphql": "15.8.0",
89
89
  "graphql-tag": "^2.12.6",
@@ -98,7 +98,7 @@
98
98
  "react-dnd": "^16.0.1",
99
99
  "react-dnd-html5-backend": "^16.0.1",
100
100
  "react-dropzone": "14.2.3",
101
- "react-final-form": "^7.0.1",
101
+ "react-final-form": "^6.5.9",
102
102
  "react-router-dom": "^6.30.3",
103
103
  "react-use": "^17.6.0",
104
104
  "tailwind-merge": "^2.6.0",
@@ -106,9 +106,9 @@
106
106
  "yup": "^1.6.1",
107
107
  "zod": "^3.24.2",
108
108
  "@tinacms/bridge": "^0.3.1",
109
- "@tinacms/mdx": "^2.2.2",
110
- "@tinacms/schema-tools": "^2.10.0",
111
- "@tinacms/search": "^1.2.24"
109
+ "@tinacms/mdx": "^2.2.3",
110
+ "@tinacms/search": "^1.2.25",
111
+ "@tinacms/schema-tools": "^2.10.0"
112
112
  },
113
113
  "devDependencies": {
114
114
  "@graphql-tools/utils": "^10.8.1",