tinacms 0.60.2 → 0.63.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/CHANGELOG.md CHANGED
@@ -1,5 +1,68 @@
1
1
  # tinacms
2
2
 
3
+ ## 0.63.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3897ec5d9: Replace `branch`, `clientId`, `isLocalClient` props with single `apiURL`. When working locally, this should be `http://localhost:4001/graphql`. For Tina Cloud, use `https://content.tinajs.io/content/<my-client-id>/github/<my-branch>`
8
+
9
+ ```tsx
10
+ // _app.tsx
11
+ // ...
12
+ <TinaCMS apiURL={process.env.NEXT_PUBLIC_TINA_API_URL} {...pageProps}>
13
+ {livePageProps => <Component {...livePageProps} />}
14
+ </TinaCMS>
15
+ ```
16
+
17
+ DEPRECATION NOTICE: `branch`, `clientId`, `isLocalClient` props will be deprecated in the future
18
+
19
+ ### Patch Changes
20
+
21
+ - 96e4a77e2: Fixed types
22
+ - b5c22503a: Changes messaging on login page for TinaAdmin when in local-mode
23
+ - Updated dependencies [60f939f34]
24
+ - @tinacms/toolkit@0.56.6
25
+
26
+ ## 0.62.0
27
+
28
+ ### Minor Changes
29
+
30
+ - 70da62fe8: deprecated the use of `getStaticPropsForTina`
31
+
32
+ ### Patch Changes
33
+
34
+ - 0afa75df1: 2342 - [TinaAdmin] Truncates the `filename` column for the Document List View
35
+ - 7dafce89d: Fixed issue where content creator was invalid
36
+ - 3de8c6165: Enabled branch creation in branch switcher
37
+ - fee183f8f: add "switch to default branch" recover option to error boundary
38
+ - 5c070a83f: feat: Add UI banner for when in localMode
39
+ - Updated dependencies [ddf81a4fd]
40
+ - Updated dependencies [20260a82d]
41
+ - Updated dependencies [0370147fb]
42
+ - Updated dependencies [3de8c6165]
43
+ - Updated dependencies [2eaad97bf]
44
+ - Updated dependencies [5c070a83f]
45
+ - @tinacms/toolkit@0.56.5
46
+
47
+ ## 0.61.1
48
+
49
+ ### Patch Changes
50
+
51
+ - Updated dependencies [2c7718636]
52
+ - @tinacms/toolkit@0.56.4
53
+
54
+ ## 0.61.0
55
+
56
+ ### Minor Changes
57
+
58
+ - 229feda1d: add .nvmrc file for setting preferred node version
59
+
60
+ ## 0.60.3
61
+
62
+ ### Patch Changes
63
+
64
+ - 4adaf15af: Fix types which weren't included in previous patch
65
+
3
66
  ## 0.60.2
4
67
 
5
68
  ### Patch Changes
package/README.md CHANGED
@@ -1,289 +1,4 @@
1
- > Heads up - if you haven't already done so, read through the [CLI documentation](https://tina.io/docs/tina-cloud/cli/) to make sure you have a GraphQL server running locally.
1
+ # TinaCMS
2
+ > The Fastest Way to Edit Next.js Content
2
3
 
3
- For a real-world example of how this is being used checkout the [Tina Cloud Starter](https://github.com/tinacms/tina-cloud-starter).
4
-
5
- ## Getting started
6
-
7
- Npm:
8
-
9
- ```bash
10
- npm install --save-dev tinacms
11
- ```
12
-
13
- Yarn:
14
-
15
- ```bash
16
- yarn add --dev tinacms
17
- ```
18
-
19
- ## The TinaCMS API Client
20
-
21
- This package exports a class which acts as [TinaCMS external API](https://tina.io/docs/apis/) for the Tina Content API. This is a headless GraphQL API that's serverd via Tina Cloud or locally from within the Tina CLI.
22
-
23
- ```ts
24
- import { Client, LocalClient } from "tinacms";
25
- const client = new Client({
26
- organizationId: "the ID you get from Tina Cloud",
27
- clientId: "the client ID you get from Tina Cloud",
28
- branch: "main",
29
- tokenStorage: "LOCAL_STORAGE" | "MEMORY" | "CUSTOM",
30
- });
31
-
32
- // For a simpler setup while working locally you can instantiate the LocalClient as a convenience
33
- const client = new LocalClient();
34
- ```
35
-
36
- The `Client` does a few things:
37
-
38
- - Manages auth with Tina Cloud
39
- - Provides a `request` function for working with the GraphQL API
40
-
41
- Start by initializing the `LocalClient` - which automatically connects with your locally-running GraphQL server. From there, you can make GraphQL requests:
42
-
43
- ### `client.request`
44
-
45
- ```ts
46
- const client = new LocalClient();
47
-
48
- await client.request(
49
- (gql) => gql`#graphql
50
- query BlogPostQuery($relativePath: String!) {
51
- {
52
- getPostsDocument(relativePath: "") {
53
- data {
54
- title
55
- }
56
- }
57
- }
58
- }
59
- `,
60
- { variables: { relativePath: "hello-world.md" } }
61
- );
62
- ```
63
-
64
- > This API currently doesn't support filtering and sorting "list" queries. We have plans to tackle that in upcoming cycles.
65
-
66
- ## `useGraphQLForms`
67
-
68
- While GraphQL is a great tool, using it with Tina can be difficult. GraphQL can query across multiple nodes, but since each document would require its own Tina form it could be difficult to sync the data with your query with all of the forms you'd need to build. The Tina GraphQL server knows all about your content schema so we're actually able to build forms automatically by inspecting your query. To see this in action, pass your query into the `useGraphqlForms` hook:
69
-
70
- ```tsx
71
- import { useGraphqlForms } from 'tinacms'
72
-
73
- const query = gql => gql`#graphql
74
- query BlogPostQuery($relativePath: String!) {
75
- {
76
- getPostsDocument(relativePath: $relativePath) {
77
- data {
78
- title
79
- }
80
- }
81
- }
82
- }
83
- `
84
-
85
- const MyPage = (props) => {
86
- const [payload, isLoading] = useGraphqlForms<PostQueryResponseType>({
87
- query,
88
- variables: { relativePath: `${props.filename}.md` },
89
- });
90
-
91
- isLoading ? <div>Loading...</div> : <MyComponent {...payload}>
92
- }
93
- ```
94
-
95
- If Tina is enabled you can see a form for the `getPostsDocument` request. If you query for multiple documents, you should see multiple forms:
96
-
97
- ```tsx
98
- const query = (gql) => gql`#graphql
99
- query BlogPostQuery($relativePath: String!) {
100
- {
101
- # this generates a Tina Form
102
- getSiteNavsDocument(relativePath: "site-nav.md") {
103
- data {
104
- items {
105
- title
106
- link
107
- }
108
- }
109
- }
110
- # this generates a separate Tina Form
111
- getPostsDocument(relativePath: $relativePath) {
112
- data {
113
- title
114
- }
115
- }
116
- }
117
- }
118
- `;
119
- ```
120
-
121
- ### Formify
122
-
123
- If you'd like to control the output of those forms, tap into the `formify` callback:
124
-
125
- ##### Form customization:
126
-
127
- ```tsx
128
- import { useGraphqlForms } from "tinacms";
129
- import { useCMS } from "tinacms";
130
-
131
- const [payload, isLoading] = useGraphqlForms({
132
- query,
133
- formify: ({ formConfig, createForm, createGlobalForm, skip }) => {
134
- if (formConfig.id === "getSiteNavsDocument") {
135
- return createGlobalForm(formConfig);
136
- }
137
-
138
- return createForm(formConfig);
139
- },
140
- variables: { relativePath: `${props.filename}.md` },
141
- });
142
-
143
- // or to skip the nav from creating a form altogether:
144
- const [payload, isLoading] = useGraphqlForms({
145
- query,
146
- formify: ({ formConfig, createForm, skip }) => {
147
- if (formConfig.id === "getSiteNavsDocument") {
148
- return skip();
149
- }
150
-
151
- return createForm(formConfig);
152
- },
153
- variables: { relativePath: `${props.filename}.md` },
154
- });
155
- ```
156
-
157
- ##### Field customization:
158
-
159
- Since your forms are built automatically, `formify` can also be used to customize fields:
160
-
161
- ```tsx
162
- const [payload, isLoading] = useGraphqlForms({
163
- query,
164
- formify: ({ formConfig, createForm, skip }) => {
165
- return createForm({
166
- ...formConfig,
167
- fields: formConfig.fields.map((field) => {
168
- if (field.name === "title") {
169
- // replace `text` with `textarea`
170
- field.component = "textarea";
171
- }
172
- return field;
173
- }),
174
- });
175
- },
176
- variables: { relativePath: `${props.filename}.md` },
177
- });
178
- ```
179
-
180
- ## `useDocumentCreatorPlugin`
181
-
182
- This hook allows your editors to safely create new pages. Note that you'll be responsible for redirecting the user after a new document has been created. To use this:
183
-
184
- ```tsx
185
- import { useDocumentCreatorPlugin } from "tinacms";
186
-
187
- // args is of type:
188
- // {
189
- // collection: {
190
- // slug: string;
191
- // };
192
- // relativePath: string;
193
- // breadcrumbs: string[];
194
- // path: string;
195
- // }
196
- useDocumentCreatorPlugin((args) => window.location.assign(buildMyRouter(args)));
197
- ```
198
-
199
- ### Customizing the content creator options
200
-
201
- To prevent editors from creating documents from certain collections, provide a filter function:
202
-
203
- ```tsx
204
- // options are of type:
205
- // {
206
- // label: string;
207
- // value: string;
208
- // }[]
209
- useDocumentCreatorPlugin(null, (options) =>
210
- options.filter((option) => option.name !== "post")
211
- );
212
- ```
213
-
214
- ## Authentication with Tina Cloud
215
-
216
- While this package comes with low-level APIs for authentication with Tina Cloud, the easiest way to get started is to use the `TinaCloudAuthWall` component, which prevents children from rendering until a valid session has been established with Tina Cloud.
217
-
218
- ### `TinaCloudAuthWall`
219
-
220
- ```tsx
221
- import { TinaCloudAuthWall, Client } from "tinacms";
222
-
223
- const TinaWrapper = ({ children }) => {
224
- const cms = React.useMemo(() => {
225
- return new TinaCMS({
226
- apis: {
227
- tina: new Client({
228
- // config
229
- })
230
- },
231
- ...
232
- });
233
- }, []);
234
-
235
- return <TinaCloudAuthWall cms={cms}>{children}</TinaCloudAuthWall>;
236
- };
237
- ```
238
-
239
- Props for TinaCloudAuthWall
240
-
241
- | Prop | Description |
242
- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
243
- | `cms` | An instance of a [CMS](http://localhost:3000/docs/cms/#cms-configuration) |
244
- | `getModalActions` (optional) | A function that returns a list of actions / buttons that will be rendered to the model. Each button has name, action, and can be primary or not. The name is the text that will be displayed. The action is a function that will be run when the button is clicked. See example below for more details |
245
-
246
- ```tsx
247
- return (
248
- <TinaCloudAuthWall
249
- cms={cms}
250
- getModalActions={({ closeModal }) => {
251
- return [
252
- {
253
- action: async () => {
254
- // use your own state to get in and out of edit mode
255
- closeModal();
256
- },
257
- name: "close",
258
- primary: false,
259
- },
260
- ];
261
- }}
262
- >
263
- <Component {...pageProps} />
264
- </TinaCloudAuthWall>
265
- );
266
- ```
267
-
268
- > Note: when using the LocalClient, TinaCloudAuthWall won't display a login screen, there is no authentication for the local GraphQL server.
269
-
270
- ### Authenticating without TinaCloudAuthWall
271
-
272
- You can also authenticate with the `Client` directly:
273
-
274
- ```ts
275
- const client = new Client({
276
- // config
277
- });
278
-
279
- const EditSiteButton = () => {
280
- const cms = useCMS();
281
- const onClick = async () => {
282
- await client.authenticate().then((token) => {
283
- cms.enable();
284
- });
285
- };
286
-
287
- return <button onClick={onClick}>Edit This Site</button>;
288
- };
289
- ```
4
+ [Checkout the docs](https://tina.io/docs/setup-overview/) for information on how to get started
@@ -42,6 +42,7 @@ export declare class Client {
42
42
  private options;
43
43
  events: EventBus;
44
44
  constructor({ tokenStorage, ...options }: ServerOptions);
45
+ get isLocalMode(): boolean;
45
46
  setBranch(branchName: string): void;
46
47
  addPendingContent: (props: any) => Promise<unknown>;
47
48
  getSchema: () => Promise<GraphQLSchema>;
@@ -1 +1,20 @@
1
- export * from "../src/edit-state"
1
+ /**
2
+ Copyright 2021 Forestry.io Holdings, Inc.
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0
7
+ Unless required by applicable law or agreed to in writing, software
8
+ distributed under the License is distributed on an "AS IS" BASIS,
9
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ See the License for the specific language governing permissions and
11
+ limitations under the License.
12
+ */
13
+ import { isEditing, setEditing, useEditState } from '@tinacms/sharedctx';
14
+ import React from 'react';
15
+ export { isEditing, setEditing, useEditState };
16
+ export declare const TinaEditProvider: ({ showEditButton, ...props }: {
17
+ showEditButton?: boolean;
18
+ children: React.ReactNode;
19
+ editMode: React.ReactNode;
20
+ }) => JSX.Element;
package/dist/index.d.ts CHANGED
@@ -1 +1,23 @@
1
- export * from "../src/index"
1
+ /**
2
+ Copyright 2021 Forestry.io Holdings, Inc.
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0
7
+ Unless required by applicable law or agreed to in writing, software
8
+ distributed under the License is distributed on an "AS IS" BASIS,
9
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ See the License for the specific language governing permissions and
11
+ limitations under the License.
12
+ */
13
+ export * from './client';
14
+ export * from './auth';
15
+ export * from './utils';
16
+ export * from './tina-cms';
17
+ export { useGraphqlForms } from './hooks/use-graphql-forms';
18
+ export { useDocumentCreatorPlugin } from './hooks/use-content-creator';
19
+ export * from '@tinacms/toolkit';
20
+ export { TinaAdmin } from './admin';
21
+ export { RouteMappingPlugin } from './admin/plugins/route-mapping';
22
+ import { TinaCMSProvider2 } from './tina-cms';
23
+ export default TinaCMSProvider2;
package/dist/index.es.js CHANGED
@@ -39,6 +39,7 @@ import styled from "styled-components";
39
39
  import * as yup from "yup";
40
40
  import { setEditing, useEditState } from "@tinacms/sharedctx";
41
41
  import { getIn, setIn } from "final-form";
42
+ import UrlPattern from "url-pattern";
42
43
  import { NavLink, useLocation, useParams, Link, useNavigate, BrowserRouter, Routes, Route } from "react-router-dom";
43
44
  import { Menu, Transition } from "@headlessui/react";
44
45
  function popupWindow(url, title, window2, w, h) {
@@ -374,6 +375,11 @@ function assertIsUnionType(type) {
374
375
  throw new Error(`Expected an instance of GraphQLUnionType for type ${type.name}`);
375
376
  }
376
377
  }
378
+ const captureBranchName = /^refs\/heads\/(.*)/;
379
+ const parseRefForBranchName = (ref) => {
380
+ const matches = ref.match(captureBranchName);
381
+ return matches[1];
382
+ };
377
383
  class Client {
378
384
  constructor(_a) {
379
385
  var _b = _a, { tokenStorage = "MEMORY" } = _b, options = __objRest(_b, ["tokenStorage"]);
@@ -464,6 +470,9 @@ mutation addPendingDocumentMutation(
464
470
  break;
465
471
  }
466
472
  }
473
+ get isLocalMode() {
474
+ return this.contentApiUrl.includes("localhost");
475
+ }
467
476
  setBranch(branchName) {
468
477
  var _a, _b, _c;
469
478
  const encodedBranch = encodeURIComponent(branchName);
@@ -591,15 +600,15 @@ mutation addPendingDocumentMutation(
591
600
  try {
592
601
  const res = await this.fetchWithToken(url, {
593
602
  method: "POST",
594
- body: {
603
+ body: JSON.stringify({
595
604
  baseBranch,
596
605
  branchName
597
- },
606
+ }),
598
607
  headers: {
599
608
  "Content-Type": "application/json"
600
609
  }
601
610
  });
602
- return JSON.stringify(res);
611
+ return await res.json().then((r) => parseRefForBranchName(r.data.ref));
603
612
  } catch (error) {
604
613
  console.error("There was an error creating a new branch.", error);
605
614
  return null;
@@ -1370,7 +1379,7 @@ const useDocumentCreatorPlugin = (args) => {
1370
1379
  }));
1371
1380
  };
1372
1381
  run();
1373
- }, [cms, values == null ? void 0 : values.collection]);
1382
+ }, [cms]);
1374
1383
  React.useEffect(() => {
1375
1384
  if (plugin) {
1376
1385
  cms.plugins.add(plugin);
@@ -1382,6 +1391,18 @@ const useDocumentCreatorPlugin = (args) => {
1382
1391
  };
1383
1392
  }, [plugin]);
1384
1393
  };
1394
+ const errorButtonStyles = {
1395
+ background: "#eb6337",
1396
+ padding: "12px 18px",
1397
+ cursor: "pointer",
1398
+ borderRadius: "50px",
1399
+ textTransform: "uppercase",
1400
+ letterSpacing: "2px",
1401
+ fontWeight: "bold",
1402
+ border: "none",
1403
+ color: "white",
1404
+ margin: "1rem 0"
1405
+ };
1385
1406
  const SetupHooks = (props) => {
1386
1407
  const cms = useCMS();
1387
1408
  const [payload, isLoading] = useGraphqlForms({
@@ -1411,6 +1432,8 @@ class ErrorBoundary extends React.Component {
1411
1432
  return { hasError: true, message: error.message };
1412
1433
  }
1413
1434
  render() {
1435
+ const branchData = window.localStorage.getItem("tinacms-current-branch");
1436
+ const hasBranchData = branchData && branchData.length > 0;
1414
1437
  if (this.state.hasError && !this.state.pageRefresh) {
1415
1438
  return /* @__PURE__ */ React.createElement("div", {
1416
1439
  style: {
@@ -1431,25 +1454,21 @@ class ErrorBoundary extends React.Component {
1431
1454
  }
1432
1455
  }, /* @__PURE__ */ React.createElement("h3", {
1433
1456
  style: { color: "#eb6337" }
1434
- }, "TinaCMS Render Error"), /* @__PURE__ */ React.createElement("p", null, "Tina caught an error while updating the page:"), /* @__PURE__ */ React.createElement("pre", null, this.state.message), /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("p", null, `If you've just updated the form, undo your most recent changes and click "refresh". If after a few refreshes, you're still encountering this error. There is a bigger issue with the site. Please reach out to your site admin.`), /* @__PURE__ */ React.createElement("div", {
1435
- style: { padding: "10px 0" }
1436
- }, /* @__PURE__ */ React.createElement("button", {
1437
- style: {
1438
- background: "#eb6337",
1439
- padding: "12px 18px",
1440
- cursor: "pointer",
1441
- borderRadius: "50px",
1442
- textTransform: "uppercase",
1443
- letterSpacing: "2px",
1444
- fontWeight: "bold",
1445
- border: "none",
1446
- color: "white"
1447
- },
1457
+ }, "TinaCMS Render Error"), /* @__PURE__ */ React.createElement("p", null, "Tina caught an error while updating the page:"), /* @__PURE__ */ React.createElement("pre", {
1458
+ style: { marginTop: "1rem", overflowX: "auto" }
1459
+ }, this.state.message), /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("p", null, `If you've just updated the form, undo your most recent changes and click "refresh". If after a few refreshes, you're still encountering this error. There is a bigger issue with the site. Please reach out to your site admin.`), /* @__PURE__ */ React.createElement("button", {
1460
+ style: errorButtonStyles,
1448
1461
  onClick: () => {
1449
1462
  this.setState({ pageRefresh: true });
1450
1463
  setTimeout(() => this.setState({ hasError: false, pageRefresh: false }), 3e3);
1451
1464
  }
1452
- }, "Refresh"))));
1465
+ }, "Refresh"), hasBranchData && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("p", null, `If you're using the branch switcher, you may currently be on a "stale" branch that has been deleted or whose content is not compatible with the latest version of the site's layout. Click the button below to switch back to the default branch for this deployment.`), /* @__PURE__ */ React.createElement("button", {
1466
+ style: errorButtonStyles,
1467
+ onClick: () => {
1468
+ window.localStorage.removeItem("tinacms-current-branch");
1469
+ window.location.reload();
1470
+ }
1471
+ }, "Switch to default branch"))));
1453
1472
  }
1454
1473
  if (this.state.pageRefresh) {
1455
1474
  return /* @__PURE__ */ React.createElement(Loader, null, "Let's try that again.");
@@ -1457,20 +1476,29 @@ class ErrorBoundary extends React.Component {
1457
1476
  return this.props.children;
1458
1477
  }
1459
1478
  }
1479
+ const parseURL = (url) => {
1480
+ if (url.includes("localhost")) {
1481
+ return { branch: null, isLocalClient: true, clientId: null };
1482
+ }
1483
+ const tinaHost = "content.tinajs.io";
1484
+ const params = new URL(url);
1485
+ const pattern = new UrlPattern("/content/:clientId/github/:branch");
1486
+ const result = pattern.match(params.pathname);
1487
+ if (params.host !== tinaHost) {
1488
+ throw new Error(`The only supported hosts are ${tinaHost} or localhost, but received ${params.host}.`);
1489
+ }
1490
+ return __spreadProps(__spreadValues({}, result), {
1491
+ isLocalClient: false
1492
+ });
1493
+ };
1460
1494
  const TinaCMSProvider2 = (_c) => {
1461
1495
  var _d = _c, {
1462
1496
  children,
1463
- branch,
1464
- clientId,
1465
- isLocalClient,
1466
1497
  cmsCallback,
1467
1498
  mediaStore,
1468
1499
  tinaioConfig
1469
1500
  } = _d, props = __objRest(_d, [
1470
1501
  "children",
1471
- "branch",
1472
- "clientId",
1473
- "isLocalClient",
1474
1502
  "cmsCallback",
1475
1503
  "mediaStore",
1476
1504
  "tinaioConfig"
@@ -1478,6 +1506,14 @@ const TinaCMSProvider2 = (_c) => {
1478
1506
  if (typeof props.query === "string") {
1479
1507
  props.query;
1480
1508
  }
1509
+ if (!props.apiURL && !((props == null ? void 0 : props.clientId) || (props == null ? void 0 : props.isLocalClient))) {
1510
+ throw new Error(`apiURL is a required field`);
1511
+ }
1512
+ const { branch, clientId, isLocalClient } = props.apiURL ? parseURL(props.apiURL) : {
1513
+ branch: props.branch,
1514
+ clientId: props.clientId,
1515
+ isLocalClient: props.isLocalClient
1516
+ };
1481
1517
  return /* @__PURE__ */ React.createElement(TinaCloudProvider, {
1482
1518
  branch,
1483
1519
  clientId,
@@ -1619,10 +1655,10 @@ const staticRequest = async ({
1619
1655
  if (!is_server()) {
1620
1656
  console.warn(`Whoops! Looks like you are using \`staticRequest\` in the browser to fetch data.
1621
1657
 
1622
- The local server is not available outside of \`getStaticProps\` or \`getStaticPaths\` functions.
1658
+ The local server is not available outside of \`getStaticProps\` or \`getStaticPaths\` functions.
1623
1659
  This function should only be called on the server at build time.
1624
1660
 
1625
- This will work when developing locally but NOT when deployed to production.
1661
+ This will work when developing locally but NOT when deployed to production.
1626
1662
  `);
1627
1663
  }
1628
1664
  return client.request(query, { variables });
@@ -1766,7 +1802,7 @@ const AuthTemplate = ({
1766
1802
  return /* @__PURE__ */ React.createElement("div", {
1767
1803
  className: "h-screen w-full bg-gradient-to-b from-blue-900 to-gray-900 flex items-center justify-center px-4 py-6"
1768
1804
  }, /* @__PURE__ */ React.createElement("div", {
1769
- className: "bg-white rounded-lg overflow-hidden shadow-lg w-full max-w-md"
1805
+ className: "bg-white rounded-lg overflow-hidden shadow-lg w-full max-w-lg"
1770
1806
  }, /* @__PURE__ */ React.createElement("div", {
1771
1807
  className: "px-5 py-4 border-b border-gray-150"
1772
1808
  }, /* @__PURE__ */ React.createElement("h2", {
@@ -1791,9 +1827,7 @@ const AuthTemplate = ({
1791
1827
  const LoginPage = () => {
1792
1828
  const { setEdit } = useEditState();
1793
1829
  const login = () => setEdit(true);
1794
- return /* @__PURE__ */ React.createElement(AuthTemplate, {
1795
- message: "Please log in to Tina Cloud to access your content."
1796
- }, /* @__PURE__ */ React.createElement("a", {
1830
+ return /* @__PURE__ */ React.createElement(AuthTemplate, null, /* @__PURE__ */ React.createElement("a", {
1797
1831
  href: "/",
1798
1832
  className: "flex-1 text-center inline-flex justify-center items-center px-8 py-3 shadow-sm text-sm leading-4 font-medium rounded-full text-gray-600 border border-gray-150 hover:opacity-80 hover:bg-gray-50 focus:outline-none focus:shadow-outline-blue transition duration-150 ease-out"
1799
1833
  }, /* @__PURE__ */ React.createElement(MdOutlineArrowBack, {
@@ -1805,7 +1839,7 @@ const LoginPage = () => {
1805
1839
  style: { background: "#0084FF" }
1806
1840
  }, /* @__PURE__ */ React.createElement(BiLogIn, {
1807
1841
  className: "w-6 h-auto mr-1.5 opacity-80"
1808
- }), " Log in"));
1842
+ }), " Enter edit-mode"));
1809
1843
  };
1810
1844
  const logout = () => {
1811
1845
  setEditing(false);
@@ -1973,11 +2007,11 @@ const CollectionListPage = () => {
1973
2007
  className: "block text-xs mb-0.5 text-gray-400 uppercase"
1974
2008
  }, "Filename"), /* @__PURE__ */ React.createElement(Link, {
1975
2009
  to: `${location2.pathname}/${document2.node.sys.filename}`,
1976
- className: "h-5 leading-5 block"
2010
+ className: "h-5 leading-5 flex max-w-xs"
1977
2011
  }, /* @__PURE__ */ React.createElement("span", {
1978
- className: "leading-5 font-medium text-base overflow-ellipsis overflow-hidden whitespace-nowrap text-gray-700"
2012
+ className: "flex-shrink-1 leading-5 font-medium text-base overflow-ellipsis overflow-hidden whitespace-nowrap text-gray-700"
1979
2013
  }, document2.node.sys.filename), /* @__PURE__ */ React.createElement("span", {
1980
- className: "leading-5 text-base font-medium text-gray-300"
2014
+ className: "flex-shrink-0 leading-5 text-base font-medium text-gray-300"
1981
2015
  }, document2.node.sys.extension))), /* @__PURE__ */ React.createElement("td", {
1982
2016
  className: "px-5 py-3 whitespace-nowrap"
1983
2017
  }, /* @__PURE__ */ React.createElement("span", {