@tinacms/app 2.4.7 → 2.5.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,35 @@
1
1
  # @tinacms/app
2
2
 
3
+ ## 2.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#6843](https://github.com/tinacms/tinacms/pull/6843) [`0509095`](https://github.com/tinacms/tinacms/commit/0509095601fedc87f05a622e219e6414ef51a6b6) Thanks [@wicksipedia](https://github.com/wicksipedia)! - Support TinaCMS visual editing on statically-built Astro pages.
8
+
9
+ Wrap editable regions in `<TinaIsland>` and visual editing now works under `output: 'static'` (and mixed static/SSR), provided the adapter can serve the one on-demand route `/tina-island/[name]`. Highlights:
10
+
11
+ - **Static page support.** `<TinaIsland>` emits a tiny in-iframe bootstrap that fetches `/admin/bridge.js`; on init the bridge "primes" any page with island markers but no server-injected form payloads by calling the island endpoints, which now return the page's form payloads alongside region HTML.
12
+ - **Bridge served as a static asset.** Dropped the injected `/_tina/bridge.js` route (some adapters 404'd it) in favour of serving `/admin/bridge.js` from a dev-only Vite plugin and emitting the bundle into the build client output — no source-tree writes. The `@tinacms/astro/bridge-route` subpath export is removed.
13
+ - **Re-prime on soft navigation.** `refreshForms` now re-primes when it sees island markers without server-injected payloads, so Astro `ClientRouter` swaps work without a hard reload.
14
+ - **Primary-form selection.** Mark the page's main form via `requestWithMetadata(..., { priority: 'primary' })` (SSR) or the `primary` prop on `<TinaIsland>` (static); the admin reducer routes around its default selection so multi-form pages no longer land on "Referenced Files".
15
+ - **Prerender-safe middleware.** `tina()` now short-circuits on `context.isPrerendered`, fixing the `Astro.request.headers` warnings that fired on every prerendered route during `astro build`.
16
+ - **New `tinaAdminDevRedirect` Vite plugin** at `@tinacms/astro/vite` — redirects `/admin` and `/admin/` to `/admin/index.html` during `astro dev` so a bare `/admin` request lands on the SPA.
17
+
18
+ ### Patch Changes
19
+
20
+ - [#6938](https://github.com/tinacms/tinacms/pull/6938) [`4757225`](https://github.com/tinacms/tinacms/commit/475722599ff350b45bfdb4f7a6af2e37d33c81c3) Thanks [@isaaclombardssw](https://github.com/isaaclombardssw)! - chore(deps): upgrade react-router-dom from 6.3.0 to ^6.30.3 to resolve GHSA-9jcx-v3wj-wh4m (unexpected external redirect via untrusted paths)
21
+
22
+ - Updated dependencies [[`33feeac`](https://github.com/tinacms/tinacms/commit/33feeacf6585be2736a0a14c5a800c1b6db34e44), [`8ac0776`](https://github.com/tinacms/tinacms/commit/8ac0776dea0c0650a5e5098c143b24c17fc25b8e), [`df50cbf`](https://github.com/tinacms/tinacms/commit/df50cbf35536bf2028a742832aebd57701dc3bb6), [`b9eaf61`](https://github.com/tinacms/tinacms/commit/b9eaf61c28c25814ae65b5fbe72d5b33df0b3596), [`cf73a11`](https://github.com/tinacms/tinacms/commit/cf73a115c3a58fac26e2518734dd3cb49133260d), [`4757225`](https://github.com/tinacms/tinacms/commit/475722599ff350b45bfdb4f7a6af2e37d33c81c3)]:
23
+ - tinacms@3.8.2
24
+ - @tinacms/mdx@2.1.5
25
+
26
+ ## 2.4.8
27
+
28
+ ### Patch Changes
29
+
30
+ - Updated dependencies []:
31
+ - tinacms@3.8.1
32
+
3
33
  ## 2.4.7
4
34
 
5
35
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinacms/app",
3
- "version": "2.4.7",
3
+ "version": "2.5.0",
4
4
  "main": "src/main.tsx",
5
5
  "license": "Apache-2.0",
6
6
  "devDependencies": {
@@ -23,11 +23,11 @@
23
23
  "monaco-editor": "0.31.0",
24
24
  "react": "^18.3.1",
25
25
  "react-dom": "^18.3.1",
26
- "react-router-dom": "6.3.0",
26
+ "react-router-dom": "^6.30.3",
27
27
  "typescript": "^5.7.3",
28
28
  "zod": "^3.24.2",
29
- "tinacms": "3.8.0",
30
- "@tinacms/mdx": "2.1.4"
29
+ "tinacms": "3.8.2",
30
+ "@tinacms/mdx": "2.1.5"
31
31
  },
32
32
  "repository": {
33
33
  "url": "https://github.com/tinacms/tinacms.git",
@@ -190,6 +190,13 @@ export const useGraphQLReducer = (
190
190
  ResolvedDocument[]
191
191
  >([]);
192
192
  const [operationIndex, setOperationIndex] = React.useState(0);
193
+ // Pending `user-select-form` request whose form hasn't been built yet.
194
+ // Set when the bridge's `open` is still in flight (the bridge retries
195
+ // user-select-form but stops once it sees `updateData`, so we can't
196
+ // rely on a future message landing after `forms:add` resolves).
197
+ const [pendingPrimaryId, setPendingPrimaryId] = React.useState<string | null>(
198
+ null
199
+ );
193
200
 
194
201
  const activeField = searchParams.get('active-field');
195
202
 
@@ -498,13 +505,32 @@ export const useGraphQLReducer = (
498
505
  ]
499
506
  );
500
507
 
508
+ // The bridge sends `formId` keyed by query id (hashFromQuery output), but
509
+ // `state.forms` is keyed by document path. Match by either so callers using
510
+ // `useTina`'s `experimental___selectFormByFormId` (which usually returns a
511
+ // path) keep working too. Returns false when the form isn't built yet.
512
+ const activateFormByWireId = React.useCallback(
513
+ (wireId: string): boolean => {
514
+ const match = cms.state.forms.find(
515
+ ({ tinaForm }) =>
516
+ tinaForm.id === wireId || tinaForm.queries.includes(wireId)
517
+ );
518
+ if (!match) return false;
519
+ cms.dispatch({
520
+ type: 'forms:set-active-form-id',
521
+ value: match.tinaForm.id,
522
+ });
523
+ return true;
524
+ },
525
+ [cms]
526
+ );
527
+
501
528
  const handleMessage = React.useCallback(
502
529
  (event: MessageEvent<PostMessage>) => {
503
530
  if (event.data.type === 'user-select-form') {
504
- cms.dispatch({
505
- type: 'forms:set-active-form-id',
506
- value: event.data.formId,
507
- });
531
+ const incoming = event.data.formId;
532
+ // Buffer until `forms:add` resolves it if the form isn't built yet.
533
+ setPendingPrimaryId(activateFormByWireId(incoming) ? null : incoming);
508
534
  }
509
535
 
510
536
  if (event?.data?.type === 'quick-edit') {
@@ -586,11 +612,20 @@ export const useGraphQLReducer = (
586
612
  return () => {
587
613
  setPayloads([]);
588
614
  setResults([]);
615
+ setPendingPrimaryId(null);
589
616
  cms.removeAllForms();
590
617
  cms.dispatch({ type: 'form-lists:clear' });
591
618
  };
592
619
  }, [url]);
593
620
 
621
+ // Drain a buffered `user-select-form` once the matching form lands in
622
+ // `state.forms`. Watches `forms.length` rather than the array identity
623
+ // so the effect doesn't re-run on unrelated form mutations.
624
+ React.useEffect(() => {
625
+ if (!pendingPrimaryId) return;
626
+ if (activateFormByWireId(pendingPrimaryId)) setPendingPrimaryId(null);
627
+ }, [cms.state.forms.length, pendingPrimaryId, activateFormByWireId]);
628
+
594
629
  React.useEffect(() => {
595
630
  iframe.current?.contentWindow?.postMessage({
596
631
  type: 'quickEditEnabled',
@@ -1005,6 +1040,13 @@ const buildForm = ({
1005
1040
  }
1006
1041
  }
1007
1042
  if (form) {
1043
+ // Track the payload (query) id on the form so wire-side lookups —
1044
+ // notably `user-select-form`, which the bridge sends keyed by query
1045
+ // id — can resolve to the document-path-keyed `form.id` used in
1046
+ // `state.forms`. `addQuery` is otherwise only called on the second
1047
+ // open for the same document (see the `existingForm` branch above),
1048
+ // so without this the very first open leaves `form.queries` empty.
1049
+ form.addQuery(payloadId);
1008
1050
  if (shouldRegisterForm) {
1009
1051
  if (collection.ui?.global) {
1010
1052
  cms.plugins.add(new GlobalFormPlugin(form));