@wix/agent-skills 1.19.2 → 1.20.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/package.json +1 -1
- package/skills/wix-app/references/SERVICE_PLUGIN.md +14 -1
- package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +7 -6
- package/skills/wix-app/references/service-plugin/ADDITIONAL-FEES.md +4 -0
- package/skills/wix-app/references/service-plugin/AVAILABILITY-TIME-SLOTS-CONFIGURATION.md +118 -0
- package/skills/wix-app/references/service-plugin/BOOKING-POLICY.md +65 -0
- package/skills/wix-app/references/service-plugin/BOOKINGS-PRICING.md +71 -0
- package/skills/wix-app/references/service-plugin/BOOKINGS-STAFF-SORTING.md +11 -3
- package/skills/wix-app/references/service-plugin/BOOKINGS-VALIDATION.md +137 -0
- package/skills/wix-app/references/service-plugin/DISCOUNT-TRIGGERS.md +23 -16
- package/skills/wix-app/references/service-plugin/GIFT-CARDS.md +4 -0
- package/skills/wix-app/references/service-plugin/PAYMENT-SETTINGS.md +4 -2
- package/skills/wix-app/references/service-plugin/REALTIME-PERMISSIONS.md +45 -0
- package/skills/wix-app/references/service-plugin/SHIPPING-RATES.md +15 -0
- package/skills/wix-app/references/service-plugin/TOOLS_PROVIDER.md +4 -0
- package/skills/wix-app/references/service-plugin/VALIDATIONS.md +4 -0
- package/skills/wix-headless/references/CAPABILITIES.md +2 -2
- package/skills/wix-headless/references/inline-recipes/experience-store.md +4 -3
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +98 -18
- package/skills/wix-headless/references/managed/DEPLOYMENT.md +8 -8
- package/skills/wix-headless-fast/install/fast-path.mjs +3 -1
- package/skills/wix-headless-fast/references/shared/app/wix/media.ts +13 -0
- package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +227 -56
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartDrawer.tsx +37 -3
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/FilterPanel.tsx +266 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/QuickAdd.tsx +165 -0
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +48 -15
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +129 -11
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +36 -6
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +190 -24
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/types.ts +62 -4
- package/skills/wix-headless-fast/references/storefront/app-astro/layouts/SiteLayout.astro +21 -2
- package/skills/wix-headless-fast/references/storefront/seed/SEED.md +5 -1
- package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +59 -6
- package/skills/wix-manage/SKILL.md +4 -1
- package/skills/wix-manage/references/events/create-wix-event.md +46 -0
- package/skills/wix-manage/references/events/manage-wix-events.md +51 -1
- package/skills/wix-manage/references/google-ads/manage-campaign-lifecycle.md +6 -6
- package/skills/wix-manage/references/seo/manage-seo-suggestions.md +351 -0
- package/skills/wix-vibe-headless/install/deploy.cjs +33 -2
- package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed-with-install.md +1 -1
- package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +13 -4
- package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +1 -1
- package/skills/wix-vibe-headless/platforms/base44.md +19 -4
- package/skills/wix-vibe-headless/platforms/generic.md +6 -0
- package/skills/wix-vibe-headless/references/_shared/nav/nav.tanstack.js +30 -0
- package/skills/wix-vibe-headless/references/_shared/routing.md +86 -0
- package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +44 -0
- package/skills/wix-vibe-headless/references/blog/app/components/PostCard.jsx +1 -1
- package/skills/wix-vibe-headless/references/blog/app/components/PostChips.jsx +1 -1
- package/skills/wix-vibe-headless/references/blog/app/pages/CategoryPage.jsx +1 -1
- package/skills/wix-vibe-headless/references/blog/app/pages/PostDetail.jsx +1 -1
- package/skills/wix-vibe-headless/references/blog/app/pages/TagPage.jsx +1 -1
- package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +42 -0
- package/skills/wix-vibe-headless/references/bookings/app/components/ServiceCard.jsx +1 -1
- package/skills/wix-vibe-headless/references/bookings/app/pages/ServiceDetail.jsx +1 -1
- package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +42 -0
- package/skills/wix-vibe-headless/references/events/app/components/EventCard.jsx +1 -1
- package/skills/wix-vibe-headless/references/events/app/pages/EventDetail.jsx +1 -1
- package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +42 -0
- package/skills/wix-vibe-headless/references/members/app/components/MemberMenu.jsx +1 -1
- package/skills/wix-vibe-headless/references/members/app/components/RequireAuth.jsx +1 -1
- package/skills/wix-vibe-headless/references/members/app/pages/Login.jsx +1 -1
- package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +43 -0
- package/skills/wix-vibe-headless/references/portfolio/app/components/CollectionCard.jsx +1 -1
- package/skills/wix-vibe-headless/references/portfolio/app/components/ProjectCard.jsx +1 -1
- package/skills/wix-vibe-headless/references/portfolio/app/pages/CollectionPage.jsx +1 -1
- package/skills/wix-vibe-headless/references/portfolio/app/pages/ProjectDetail.jsx +1 -1
- package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +43 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/components/PlanCard.jsx +1 -1
- package/skills/wix-vibe-headless/references/pricing-plans/app/pages/PlanDetail.jsx +1 -1
- package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +36 -0
- package/skills/wix-vibe-headless/references/shared/app/lib/nav.js +9 -0
- package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +43 -2
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductCard.js +7 -1
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "Generate AI SEO Suggestions for a Wix Site"
|
|
3
|
+
description: "Generate AI-written SEO text for a Wix site through the public Tag Suggestions and Page Optimization APIs: title tag and meta description options for a page, alt text for the images of a page, store product, or blog post (one image or up to 20 per call), corrected heading levels for a page's headings, and a whole-page rewrite of title, description, headings, and body text aimed at the page's focus keyword, returned as before/after pairs. Use it when the user wants to improve, optimize, audit, or write the SEO text of a page or the homepage. Suggestions are returned for review; applying one is a separate SEO tags write."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Generate AI SEO Suggestions for a Wix Site
|
|
7
|
+
|
|
8
|
+
The **Tag Suggestions API** writes single texts from content you pass in: title
|
|
9
|
+
tags, meta descriptions, image alt text, heading levels. The **Page Optimization
|
|
10
|
+
API** reads a page from the Wix site itself and returns a whole-page rewrite as
|
|
11
|
+
before/after pairs. Neither changes the site: every result is a suggestion for
|
|
12
|
+
the user to review. Both select the site from the caller's authorization context
|
|
13
|
+
and take no site ID. Use the site already selected in the environment. If none
|
|
14
|
+
is selected, list the available sites. Select the only site automatically; if
|
|
15
|
+
several are available, ask the user to choose by name before making
|
|
16
|
+
site-specific calls. Never invent a site ID or ask the user to type one.
|
|
17
|
+
|
|
18
|
+
All endpoints are under `https://www.wixapis.com/seo/suggestions/v1`. Reads of
|
|
19
|
+
the site's pages and writes of its tags use the SEO Tags API under
|
|
20
|
+
`https://www.wixapis.com/promote/seo/v1`. Every request and response shape a
|
|
21
|
+
flow needs is in this recipe, verified against the live API: build the calls
|
|
22
|
+
from here, including the two SEO Tags calls (List Item SEO Tags, and the
|
|
23
|
+
focus-keyword write). Do not open the linked reference articles or search API
|
|
24
|
+
schemas before calling; the links are for cases this recipe does not cover.
|
|
25
|
+
|
|
26
|
+
| User asks for | Call |
|
|
27
|
+
|---|---|
|
|
28
|
+
| A better SEO title or meta description for a page | Generate Title Suggestions, Generate Description Suggestions |
|
|
29
|
+
| Alt text for images of a page, product, or post | Generate Alt Text Suggestions (one) or Bulk Generate Alt Text Suggestions (up to 20) |
|
|
30
|
+
| Check or fix a page's heading hierarchy | Generate Heading Structure Suggestions |
|
|
31
|
+
| Optimize or rewrite a page for a keyword, "improve this page's SEO" | Trigger Page Optimization, or Trigger Home Page Optimization for the homepage, then poll Get Page Optimization Results |
|
|
32
|
+
|
|
33
|
+
"Optimize", "rewrite", or "improve the SEO of" a page means the page
|
|
34
|
+
optimization flow: it reads the page's real text and rewrites title,
|
|
35
|
+
description, headings, and body together. The title and description generators
|
|
36
|
+
are not a substitute for it; they know nothing about the page's content and
|
|
37
|
+
cover two tags only. Use them when the user asks for title or description
|
|
38
|
+
options, not when they ask to optimize a page.
|
|
39
|
+
|
|
40
|
+
Each call spends AI generation and returns fresh text; nothing is stored except
|
|
41
|
+
page optimization results. Suggestions come back in the site's language, and
|
|
42
|
+
there is no parameter to change it.
|
|
43
|
+
|
|
44
|
+
## Find the page first
|
|
45
|
+
|
|
46
|
+
Every flow except title and description needs the page's ID, and most need to
|
|
47
|
+
know what the page renders today. Read it once with
|
|
48
|
+
[List Item SEO Tags](https://dev.wix.com/docs/api-reference/business-management/seo/item-seo-tags-v1/list-item-seo-tags):
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
GET https://www.wixapis.com/promote/seo/v1/item-seo-tags/STATIC_PAGE?paging.limit=50
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Relevant response fields (other tags omitted; IDs are illustrative):
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{ "itemSeoTags": [ { "itemId": "c1dmp", "itemType": "STATIC_PAGE",
|
|
58
|
+
"focusKeywords": [ { "term": "jewelry making styles", "isMain": true } ],
|
|
59
|
+
"resolvedTags": [
|
|
60
|
+
{ "tag": { "type": "title", "children": "Home | Luna" }, "source": "TAG_SOURCE_DEFAULT_PATTERN" },
|
|
61
|
+
{ "tag": { "type": "link", "props": { "rel": "canonical", "href": "https://www.example.com" } }, "source": "TAG_SOURCE_DEFAULT_PATTERN" },
|
|
62
|
+
{ "tag": { "type": "meta", "props": { "name": "description", "content": "..." } }, "source": "TAG_SOURCE_ITEM" } ] } ],
|
|
63
|
+
"pagingMetadata": { "hasNext": false, "cursors": {} } }
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
- `itemId` is the page ID (`pageId` in every call below). Identify the page by
|
|
67
|
+
its resolved `title` tag; if that is ambiguous, ask the user which page.
|
|
68
|
+
- The page path is the path of the canonical `link` `href`: `/cart-page` for
|
|
69
|
+
`https://www.example.com/cart-page`.
|
|
70
|
+
- The homepage is the entry whose canonical `link` `href` equals the site URL,
|
|
71
|
+
ignoring a trailing slash; on a free site that URL includes a path. Compare
|
|
72
|
+
only canonicals with `source` `TAG_SOURCE_DEFAULT_PATTERN`. Its path is `/`.
|
|
73
|
+
- `focusKeywords` with `isMain: true` is the focus keyword page optimization uses.
|
|
74
|
+
- The current title and description are the `title` tag and the `meta` tag named
|
|
75
|
+
`description` in `resolvedTags`; pass them as context, never present them as
|
|
76
|
+
suggestions.
|
|
77
|
+
- If `pagingMetadata.hasNext` is `true`, pass `pagingMetadata.cursors.next` as
|
|
78
|
+
`paging.cursor` on the next request until it is `false`.
|
|
79
|
+
|
|
80
|
+
The SEO Tags API names a site page `STATIC_PAGE`; the suggestion APIs name the
|
|
81
|
+
same page `STATIC_PAGE_V2`. Other item types are spelled the same in both:
|
|
82
|
+
`STORES_PRODUCT`, `BLOG_POST`, `BOOKINGS_SERVICE`. Any other string returns
|
|
83
|
+
`INVALID_ARGUMENT` with "expected itemType to be a seo supported item type".
|
|
84
|
+
|
|
85
|
+
## Title and description suggestions
|
|
86
|
+
|
|
87
|
+
Both take the content in the request, so they work for any page, including one
|
|
88
|
+
the site does not have yet. Default `pageType` is `WEBSITE_PAGE`; use `HOMEPAGE`
|
|
89
|
+
for the homepage, `PRODUCT_PAGE`, `PRODUCT_CATEGORY_PAGE`, `BLOG_POST`,
|
|
90
|
+
`BLOG_CATEGORY_PAGE`, `SERVICE_BOOKING_PAGE`, `EVENT_PAGE`,
|
|
91
|
+
`RESTAURANT_MENU_PAGE`, `PORTFOLIO_PROJECT_PAGE`, or `OTHER_APP_PAGE` as fits.
|
|
92
|
+
`primaryKeyword` is the keyword the user wants the page to rank for; when the
|
|
93
|
+
user named none, use the page's `isMain` focus keyword if it has one.
|
|
94
|
+
|
|
95
|
+
[Generate Title Suggestions](https://dev.wix.com/docs/api-reference/business-management/seo/tag-suggestions-v1/generate-title-suggestions),
|
|
96
|
+
`pageName` required:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
POST https://www.wixapis.com/seo/suggestions/v1/tag-suggestions/title
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{ "pageType": "HOMEPAGE", "pageName": "Home", "primaryKeyword": "boutique hotel santorini" }
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{ "tagSuggestions": { "suggestions": [ "Experience Boutique Hotel Santorini Luxury and Charm | Luna", "Discover Boutique Hotel Santorini: Unique Stays & Stunning Views | Luna" ] } }
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
[Generate Description Suggestions](https://dev.wix.com/docs/api-reference/business-management/seo/tag-suggestions-v1/generate-description-suggestions),
|
|
111
|
+
`pageName` and `topic` required; `primaryKeyword`, `toneOfVoice`,
|
|
112
|
+
`businessType`, `businessLocation`, and `pageContent` (the page's text, up to
|
|
113
|
+
100,000 characters) are optional hints that may or may not appear in the text:
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
POST https://www.wixapis.com/seo/suggestions/v1/tag-suggestions/description
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{ "pageType": "HOMEPAGE", "pageName": "Home", "topic": "Boutique hotel in Santorini",
|
|
121
|
+
"primaryKeyword": "boutique hotel santorini", "toneOfVoice": "warm" }
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The response has the same `tagSuggestions.suggestions` shape. Both return best
|
|
125
|
+
match first, up to 10, often fewer: texts outside the target length or missing
|
|
126
|
+
the keyword are filtered out. Titles aim for 45 to 65 characters, descriptions
|
|
127
|
+
for 130 to 165. Send page content only in the free-text fields, never personal
|
|
128
|
+
data: they are sent to the AI model and written to service logs.
|
|
129
|
+
|
|
130
|
+
Applying a text is a separate write that needs the user's approval. Read the
|
|
131
|
+
page's current tags with
|
|
132
|
+
`GET https://www.wixapis.com/promote/seo/v1/item-seo-tags/STATIC_PAGE/{pageId}`,
|
|
133
|
+
merge the new `{ "type": "title", "children": "..." }` or
|
|
134
|
+
`{ "type": "meta", "props": { "name": "description", "content": "..." } }` into
|
|
135
|
+
the full `tags` array it returns, and send the whole array back with
|
|
136
|
+
`PATCH https://www.wixapis.com/promote/seo/v1/item-seo-tags/STATIC_PAGE/{pageId}`
|
|
137
|
+
and body `{ "itemSeoTags": { "tags": [ ... ] }, "fieldMask": "tags" }`. The
|
|
138
|
+
write replaces the page's tags in full, so a body holding only the new tag
|
|
139
|
+
deletes every other tag the page had. Static pages save to the draft unless the
|
|
140
|
+
body also has `"publish": true`. Full rules:
|
|
141
|
+
[Set Item SEO Tags](https://dev.wix.com/docs/api-reference/business-management/seo/item-seo-tags-v1/set-item-seo-tags).
|
|
142
|
+
|
|
143
|
+
## Alt text suggestions
|
|
144
|
+
|
|
145
|
+
Alt text is generated only for images that belong to a Wix item, identified by
|
|
146
|
+
the item's ID and type; the item's stored SEO data gives the AI its context.
|
|
147
|
+
The image URL must be on a Wix media domain (`static.wixstatic.com` or another
|
|
148
|
+
`wixstatic.com` / `wixmp.com` host). Any other host fails the whole call with
|
|
149
|
+
`INVALID_ARGUMENT` on `image.url`: "Alt text can only be generated for
|
|
150
|
+
Wix-hosted images". Do not retry with the same URL.
|
|
151
|
+
|
|
152
|
+
For a store product, get the ID and image URLs from Query Products
|
|
153
|
+
(`POST https://www.wixapis.com/stores/v3/products/query` with
|
|
154
|
+
`{ "query": { "cursorPaging": { "limit": 20 } }, "fields": ["MEDIA_ITEMS_INFO"] }`):
|
|
155
|
+
each product's `id` is the `parentItemId`, and `media.itemsInfo.items[].image.url`
|
|
156
|
+
are its image URLs. For a site page, `parentItemId` is the page's `itemId` from
|
|
157
|
+
List Item SEO Tags (listed there under `STATIC_PAGE`), and the alt-text call's
|
|
158
|
+
`itemType` is `STATIC_PAGE_V2`.
|
|
159
|
+
|
|
160
|
+
[Generate Alt Text Suggestions](https://dev.wix.com/docs/api-reference/business-management/seo/tag-suggestions-v1/generate-alt-text-suggestions),
|
|
161
|
+
one image; `id` is your own label, echoed back:
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
POST https://www.wixapis.com/seo/suggestions/v1/tag-suggestions/alt-text
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{ "itemType": "STORES_PRODUCT",
|
|
169
|
+
"image": { "id": "main", "url": "https://static.wixstatic.com/media/22e53e_5a34c410f18f4760b451208652e8cf27~mv2.jpg",
|
|
170
|
+
"parentItemId": "7ef5f127-5e5d-4ebb-9957-d122ce56daec" } }
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{ "altTextSuggestions": { "imageId": "main", "url": "https://static.wixstatic.com/media/22e53e_5a34c410f18f4760b451208652e8cf27~mv2.jpg",
|
|
175
|
+
"suggestions": [ "Ceramic flower vase with dried roses on a stone surface" ] } }
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
[Bulk Generate Alt Text Suggestions](https://dev.wix.com/docs/api-reference/business-management/seo/tag-suggestions-v1/bulk-generate-alt-text-suggestions),
|
|
179
|
+
up to 20 images of the same `itemType` per call; split larger sets:
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
POST https://www.wixapis.com/seo/suggestions/v1/tag-suggestions/alt-text-bulk
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
```json
|
|
186
|
+
{ "itemType": "STORES_PRODUCT", "images": [
|
|
187
|
+
{ "id": "main", "url": "https://static.wixstatic.com/media/22e53e_5a34c410f18f4760b451208652e8cf27~mv2.jpg", "parentItemId": "7ef5f127-5e5d-4ebb-9957-d122ce56daec" },
|
|
188
|
+
{ "id": "second", "url": "https://static.wixstatic.com/media/22e53e_0a3fe4969636459b8fde2bde4bbe109d~mv2.jpg", "parentItemId": "7ef5f127-5e5d-4ebb-9957-d122ce56daec" } ] }
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
```json
|
|
192
|
+
{ "results": [ { "imageId": "main", "success": true, "suggestions": [ "Dried roses in a matte ceramic flower vase on a stone ledge." ] },
|
|
193
|
+
{ "imageId": "second", "success": true, "suggestions": [ "Minimalist matte beige ceramic flower vase" ] } ],
|
|
194
|
+
"successCount": 2, "failureCount": 0 }
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
`results` follow request order. An entry with `success: false` has no
|
|
198
|
+
suggestions: retry that image alone with the single-image call, once. Up to 5
|
|
199
|
+
suggestions per image, often one. Report `failureCount` as returned. Applying
|
|
200
|
+
alt text happens in the product's or page's own settings, not through these APIs.
|
|
201
|
+
|
|
202
|
+
## Heading structure suggestions
|
|
203
|
+
|
|
204
|
+
[Generate Heading Structure Suggestions](https://dev.wix.com/docs/api-reference/business-management/seo/tag-suggestions-v1/generate-heading-structure-suggestions)
|
|
205
|
+
takes the page's headings in page order, each with its raw HTML and your own
|
|
206
|
+
`id`; `sectionId` groups headings that belong to one section. It must be called
|
|
207
|
+
with a Wix user identity (a signed-in site owner or an app acting for one);
|
|
208
|
+
with an API key the call fails, by design, because the page title is read
|
|
209
|
+
through the site's accessibility analysis, which runs as the user.
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
POST https://www.wixapis.com/seo/suggestions/v1/tag-suggestions/heading-structure
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
```json
|
|
216
|
+
{ "pageId": "c1dmp", "itemType": "STATIC_PAGE_V2",
|
|
217
|
+
"headings": [ { "id": "hero", "htmlString": "<h1>Luna Boutique Hotel</h1>", "sectionId": "hero" },
|
|
218
|
+
{ "id": "rooms", "htmlString": "<h3>Our rooms</h3>", "sectionId": "rooms" },
|
|
219
|
+
{ "id": "rooms-suite", "htmlString": "<h4>Caldera suite</h4>", "sectionId": "rooms" },
|
|
220
|
+
{ "id": "contact", "htmlString": "<h1>Contact us</h1>", "sectionId": "contact" } ] }
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
```json
|
|
224
|
+
{ "suggestions": [ { "headingId": "rooms", "currentTag": "H3", "proposedTag": "H2" },
|
|
225
|
+
{ "headingId": "rooms-suite", "currentTag": "H4", "proposedTag": "H3" },
|
|
226
|
+
{ "headingId": "contact", "currentTag": "H1", "proposedTag": "H2" } ] }
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Only headings that should change are returned; an empty `suggestions` array
|
|
230
|
+
means the structure is already correct. `proposedTag` `P` means the text should
|
|
231
|
+
not be a heading. Up to 200 headings per call. The heading text comes from the
|
|
232
|
+
user or from the page content you already have; this recipe has no method that
|
|
233
|
+
fetches a page's headings.
|
|
234
|
+
|
|
235
|
+
## Page optimization: trigger, then poll
|
|
236
|
+
|
|
237
|
+
Page optimization reads the page from the site's latest saved revision, so
|
|
238
|
+
unpublished edits count and the site need not be published. It needs two
|
|
239
|
+
things, checked in this order:
|
|
240
|
+
|
|
241
|
+
1. **A Wix-rendered site.** Headless projects fail with `FAILED_PRECONDITION`,
|
|
242
|
+
code `SITE_NOT_SUPPORTED`. Stop and tell the user: nothing else in this
|
|
243
|
+
section can work on that site.
|
|
244
|
+
2. **A focus keyword on the page** (`focusKeywords` entry with `isMain: true`
|
|
245
|
+
from List Item SEO Tags). Without one the trigger fails with
|
|
246
|
+
`FAILED_PRECONDITION`, code `FOCUS_KEYWORD_NOT_SET`. If the user named the
|
|
247
|
+
keyword they want the page to rank for, set it, then trigger; if they did
|
|
248
|
+
not, ask for it. The focus keyword is the input the job needs, not one of the
|
|
249
|
+
suggested changes: a user who says "optimize my page for X but don't apply
|
|
250
|
+
anything yet" still expects X to be set as the focus keyword, and only the
|
|
251
|
+
returned before/after texts to be held back. Setting it is a write to the
|
|
252
|
+
page's SEO settings:
|
|
253
|
+
|
|
254
|
+
```
|
|
255
|
+
PATCH https://www.wixapis.com/promote/seo/v1/item-seo-tags/STATIC_PAGE/{pageId}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
```json
|
|
259
|
+
{ "itemSeoTags": { "focusKeywords": [ { "term": "handmade ceramic mugs", "isMain": true } ] }, "fieldMask": "focusKeywords" }
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
The field mask limits the write to `focusKeywords`, so the page's tags are
|
|
263
|
+
untouched and no read is needed before this write: List Item SEO Tags already
|
|
264
|
+
returned the page's current `focusKeywords`, and the SEO Tags API's
|
|
265
|
+
read-before-write rule exists for the `tags` array, which this call does not
|
|
266
|
+
send. The response echoes the page with its new `focusKeywords`.
|
|
267
|
+
|
|
268
|
+
Then start one job. Use
|
|
269
|
+
[Trigger Home Page Optimization](https://dev.wix.com/docs/api-reference/business-management/seo/page-optimization-v1/trigger-home-page-optimization)
|
|
270
|
+
for the homepage (its text represents the whole site, so it gets its own
|
|
271
|
+
guidance) and
|
|
272
|
+
[Trigger Page Optimization](https://dev.wix.com/docs/api-reference/business-management/seo/page-optimization-v1/trigger-page-optimization)
|
|
273
|
+
for any other page. Both need a Wix user identity, like heading structure;
|
|
274
|
+
both take the page ID and its path:
|
|
275
|
+
|
|
276
|
+
```
|
|
277
|
+
POST https://www.wixapis.com/seo/suggestions/v1/page-optimization/trigger-home
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
```json
|
|
281
|
+
{ "pageId": "c1dmp", "pagePath": "/" }
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
```json
|
|
285
|
+
{ "predictionId": "2247d724-1440-483d-bf66-f7edf8582a34" }
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Keep `predictionId`. Triggering the same page again while its job runs returns
|
|
289
|
+
the same `predictionId`, so a duplicate call is harmless, but do not send one on
|
|
290
|
+
purpose: each trigger is counted against the daily cap below. Then poll
|
|
291
|
+
[Get Page Optimization Results](https://dev.wix.com/docs/api-reference/business-management/seo/page-optimization-v1/get-page-optimization-results)
|
|
292
|
+
every few seconds until `status` leaves `IN_PROGRESS`; generation usually
|
|
293
|
+
finishes within two minutes:
|
|
294
|
+
|
|
295
|
+
```
|
|
296
|
+
GET https://www.wixapis.com/seo/suggestions/v1/page-optimization/results?predictionId=2247d724-1440-483d-bf66-f7edf8582a34
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
```json
|
|
300
|
+
{ "status": "IN_PROGRESS" }
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
```json
|
|
304
|
+
{ "pageOptimization": {
|
|
305
|
+
"metaTitle": { "before": "Home | Luna", "after": "Jewelry Making Styles by Luna in New York City | Online" },
|
|
306
|
+
"metaDescription": { "before": "", "after": "Explore jewelry making styles from Luna in New York City – shop our online store today." },
|
|
307
|
+
"h1": { "before": "Luna & Loop", "after": "Artisanal Jewelry Making Styles by Luna & Loop" },
|
|
308
|
+
"h2OrH3": { "before": "Luna & Loop Jewelry", "after": "Luna & Loop Jewelry Making Styles" },
|
|
309
|
+
"content": [] },
|
|
310
|
+
"status": "COMPLETED" }
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
- `COMPLETED`: present each pair as the current text and the proposed text. An
|
|
314
|
+
empty `before` means the page has no text in that slot yet (here, no meta
|
|
315
|
+
description). `content` lists body-text rewrites and can be empty.
|
|
316
|
+
- `FAILED`: the job failed; trigger once more, and if it fails again report it.
|
|
317
|
+
- `NOT_FOUND`: no job matches. The `predictionId` expires two hours after the
|
|
318
|
+
trigger, and a later trigger for the same page replaces the earlier job.
|
|
319
|
+
`GET .../page-optimization/results?pageId=c1dmp` returns the page's latest
|
|
320
|
+
completed set at any time, so use it when the user comes back later.
|
|
321
|
+
- Applying a pair is the user's decision. Title and description go through Set
|
|
322
|
+
Item SEO Tags as described above; headings and body text are edited in the
|
|
323
|
+
page itself, which these APIs cannot do.
|
|
324
|
+
|
|
325
|
+
## Limits and errors
|
|
326
|
+
|
|
327
|
+
- Per-second limits: 5 requests per second per user for tag suggestions, 2 for
|
|
328
|
+
page optimization. A call rejected for rate limiting can be retried once after
|
|
329
|
+
a short pause.
|
|
330
|
+
- Per-user daily caps on `www.wixapis.com`: 200 title, 200 description, 500
|
|
331
|
+
alt-text, 100 bulk alt-text, and 50 page-optimization triggers per day. A `429`
|
|
332
|
+
means the cap for that method is spent: stop calling it and tell the user it
|
|
333
|
+
resets the next day. Do not switch to another method to get the same text.
|
|
334
|
+
- `403` or `PERMISSION_DENIED`: stop after the first one and report that the
|
|
335
|
+
current identity lacks the SEO permission (reads and generators need view
|
|
336
|
+
access to SEO settings, triggers and tag writes need manage access). Do not
|
|
337
|
+
try another site, path, method, or request shape: the same identity gets the
|
|
338
|
+
same answer.
|
|
339
|
+
- Heading structure or a trigger failing with an internal error or `403` under
|
|
340
|
+
an API key is the identity rule above, not an outage: say a Wix user identity
|
|
341
|
+
is required and stop.
|
|
342
|
+
- `FAILED_PRECONDITION`: read `details.applicationError.code`.
|
|
343
|
+
`FOCUS_KEYWORD_NOT_SET` and `SITE_NOT_SUPPORTED` are covered above;
|
|
344
|
+
`CONTENT_TOO_SHORT` means the page has too little text to optimize, so the user
|
|
345
|
+
must add content first; `SUGGESTIONS_ALREADY_IN_PROGRESS` means a job with a
|
|
346
|
+
different focus keyword is still running, so poll that page by `pageId` and
|
|
347
|
+
trigger again when it completes. None of these succeeds on retry.
|
|
348
|
+
- `GENERATION_FAILED` (`500`): the AI pipeline failed for this call; retry once,
|
|
349
|
+
then report.
|
|
350
|
+
- `INVALID_ARGUMENT` (`400`): fix the field named in `fieldViolations` and send
|
|
351
|
+
once more; never cycle through guessed shapes.
|
|
@@ -15,10 +15,11 @@
|
|
|
15
15
|
// No vertical arg -> deploys just the shared transport; re-run with the vertical(s) once known.
|
|
16
16
|
// NOTE: .cjs on purpose — the app is an ESM package ("type":"module"); a .js here would load as ESM
|
|
17
17
|
// and require()/module.exports would throw.
|
|
18
|
-
const { existsSync, cpSync, readFileSync, writeFileSync } = require('fs');
|
|
18
|
+
const { existsSync, cpSync, mkdirSync, readFileSync, writeFileSync } = require('fs');
|
|
19
19
|
|
|
20
20
|
const REF = '/app/.agents/skills/wix-vibe-headless/references';
|
|
21
21
|
const WIX_CONFIG = '/app/src/rest/wix-config.js';
|
|
22
|
+
const NAV_ADAPTER = '/app/src/lib/nav.js';
|
|
22
23
|
const VERTICALS = ['storefront', 'bookings', 'blog', 'cms', 'forms', 'portfolio', 'pricing-plans', 'events', 'members', 'restaurants'];
|
|
23
24
|
|
|
24
25
|
// force:false + errorOnExist:false — fill in only files that AREN'T there yet; never overwrite.
|
|
@@ -69,6 +70,32 @@ function replaceMembersAuthLeftovers() {
|
|
|
69
70
|
return result;
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
// --- nav adapter --------------------------------------------------------------------------------
|
|
74
|
+
//
|
|
75
|
+
// Every shipped component imports Link/useParams/etc from "@/lib/nav", so one set of sources runs on
|
|
76
|
+
// either Base44 template and the router is named in exactly one file. Which adapter belongs there is
|
|
77
|
+
// a filesystem fact, so resolve it here rather than leaving the agent to infer the template and
|
|
78
|
+
// hand-write the file: the react-router one is a re-export a model can guess, but the TanStack one
|
|
79
|
+
// normalises useParams (strict:false) and useNavigate (object arg), and a guess breaks every page.
|
|
80
|
+
//
|
|
81
|
+
// The react-router adapter ships INSIDE shared/app/ (-> src/lib/nav.js), so it lands on every
|
|
82
|
+
// install path, including hosts that copy that tree themselves and never run this script. Only the
|
|
83
|
+
// TanStack override needs writing, and it goes down BEFORE the shared copy — that copy is
|
|
84
|
+
// force:false, so whatever is at the path first wins.
|
|
85
|
+
function detectTemplate() {
|
|
86
|
+
return existsSync('/app/src/routes/__root.jsx') ? 'tanstack' : 'react-router';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function installNavAdapter(template) {
|
|
90
|
+
if (template !== 'tanstack') return 'from_shared_tree';
|
|
91
|
+
const src = `${REF}/_shared/nav/nav.tanstack.js`;
|
|
92
|
+
if (!existsSync(src)) return 'adapter_missing_from_skill';
|
|
93
|
+
if (existsSync(NAV_ADAPTER)) return 'already_present'; // same don't-clobber contract as COPY
|
|
94
|
+
mkdirSync('/app/src/lib', { recursive: true });
|
|
95
|
+
cpSync(src, NAV_ADAPTER);
|
|
96
|
+
return 'written';
|
|
97
|
+
}
|
|
98
|
+
|
|
72
99
|
// --- WRITE the credentials ----------------------------------------------------------------------
|
|
73
100
|
//
|
|
74
101
|
// The two ids arrive as flags so this script writes them once, character-for-character, instead of a
|
|
@@ -128,7 +155,11 @@ const deployed = { verticals: [] };
|
|
|
128
155
|
const aliased = [...new Set(positional.filter((a) => ALIASES[a]))].map((a) => `${a} -> ${ALIASES[a]}`);
|
|
129
156
|
if (aliased.length) deployed.aliased = aliased;
|
|
130
157
|
|
|
131
|
-
//
|
|
158
|
+
// Nav adapter — before the shared copy, which carries the react-router default at the same path.
|
|
159
|
+
deployed.template = detectTemplate();
|
|
160
|
+
deployed.navAdapter = installNavAdapter(deployed.template);
|
|
161
|
+
|
|
162
|
+
// Shared transport — always (app/rest/wix-client.js, wix-config.js -> src/rest/, lib/nav.js).
|
|
132
163
|
if (existsSync(`${REF}/shared/app`)) cpSync(`${REF}/shared/app`, '/app/src', COPY);
|
|
133
164
|
|
|
134
165
|
// Each named vertical — its app/ (UI + app/rest/ helpers) -> src/.
|
|
@@ -22,7 +22,7 @@ Install three skills — they land under `.agents/skills/`:
|
|
|
22
22
|
- **`wix-base44-connector`** — Wix connector usage, API contracts, and documentation discovery.
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
Run this through exec_tool, exactly as written — installs all three skills, deploys the `storefront` REST scaffolds + UI into `src
|
|
25
|
+
Run this through exec_tool, exactly as written — installs all three skills, deploys the `storefront` REST scaffolds + UI into `src/` (the nav adapter at `src/lib/nav.js` among them), and pins the AGENTS.md note. **Its `deploy` result reports this app's `template`** — `react-router` wires routes into `src/App.jsx`, `tanstack` mounts them as files under `src/routes/`; see [`../references/_shared/routing.md`](../references/_shared/routing.md).
|
|
26
26
|
|
|
27
27
|
```js
|
|
28
28
|
const { execSync } = require('child_process');
|
|
@@ -18,11 +18,20 @@ These skills are already installed under `.agents/skills/`:
|
|
|
18
18
|
- **`wix-manage`** — REST recipes for managing and configuring the Wix site.
|
|
19
19
|
- **`wix-base44-connector`** — Wix connector usage, API contracts, and documentation discovery.
|
|
20
20
|
|
|
21
|
-
Base44 has also deployed the storefront hooks, cart components/context, image helpers,
|
|
22
|
-
scaffolds into `src/`, configured the Wix connector/client,
|
|
23
|
-
No installation, deployment, or AGENTS.md update is needed.
|
|
21
|
+
Base44 has also deployed the storefront hooks, cart components/context, image helpers, REST
|
|
22
|
+
scaffolds and the nav adapter (`src/lib/nav.js`) into `src/`, configured the Wix connector/client,
|
|
23
|
+
and added the Wix note to `AGENTS.md`. No installation, deployment, or AGENTS.md update is needed.
|
|
24
|
+
|
|
24
25
|
The deployed implementation and configuration files do not need to be read; use the build and seed
|
|
25
|
-
guides below for their contracts and usage.
|
|
26
|
+
guides below for their contracts and usage.
|
|
27
|
+
|
|
28
|
+
**Mount pages the way this app's template expects** — ask `src/routes/__root.jsx` first: present →
|
|
29
|
+
TanStack Start, where pages mount as route files and the deployed `src/lib/nav.js` needs swapping
|
|
30
|
+
for `_shared/nav/nav.tanstack.js`; absent → React Router, routes wired into `src/App.jsx`. Building for the wrong one
|
|
31
|
+
renders a blank preview with no error to explain it:
|
|
32
|
+
[`../references/_shared/routing.md`](../references/_shared/routing.md).
|
|
33
|
+
|
|
34
|
+
Continue with STEP 1.
|
|
26
35
|
|
|
27
36
|
Follow STEPs 1–3 below exactly. Fire product image generations first, then build the client
|
|
28
37
|
(STEP 1), and seed (STEP 2) **after** the client is built — generation finishes while you build.
|
|
@@ -21,7 +21,7 @@ Install three skills — they land under `.agents/skills/`:
|
|
|
21
21
|
- **`wix-base44-connector`** — Wix connector usage, API contracts, and documentation discovery.
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
Run this through exec_tool, exactly as written — installs all three skills, deploys the `storefront` REST scaffolds + UI into `src
|
|
24
|
+
Run this through exec_tool, exactly as written — installs all three skills, deploys the `storefront` REST scaffolds + UI into `src/` (the nav adapter at `src/lib/nav.js` among them), and pins the AGENTS.md note. **Its `deploy` result reports this app's `template`** — `react-router` wires routes into `src/App.jsx`, `tanstack` mounts them as files under `src/routes/`; see [`../references/_shared/routing.md`](../references/_shared/routing.md).
|
|
25
25
|
|
|
26
26
|
```js
|
|
27
27
|
const { execSync } = require('child_process');
|
|
@@ -18,7 +18,7 @@ STEP 3, and **except a seed that attaches entity images**, which runs after the
|
|
|
18
18
|
|
|
19
19
|
Install three skills under `.agents/skills/`: **`wix-vibe-headless`** (the client build + seed guide — your main source of truth), **`wix-manage`** (REST recipes to manage/configure the site), and **`wix-base44-connector`** (site context + API-doc discovery).
|
|
20
20
|
|
|
21
|
-
Run this through exec_tool, exactly as written — installs all three skills, deploys REST scaffolds + UI into `src
|
|
21
|
+
Run this through exec_tool, exactly as written — installs all three skills, deploys REST scaffolds + UI into `src/` (the nav adapter at `src/lib/nav.js` among them), writes `wix-config.js`, and pins the AGENTS.md note. **Its `deploy` result reports this app's `template`** (`react-router` or `tanstack`) — STEP 3 needs it.
|
|
22
22
|
|
|
23
23
|
**Set `VERTICALS`** to what the prompt asks for — **list every vertical the app uses**, since several often join the main one (too vague to tell? do STEP 2 first, then set it). Adding one later: re-run with the extra name.
|
|
24
24
|
|
|
@@ -91,9 +91,24 @@ truth for how the client is built.
|
|
|
91
91
|
file the seed writes. Read its `INSTRUCTIONS.md` **Prerequisites** before building any form UI; seed
|
|
92
92
|
it first (STEP 4) and build the rest of the app meanwhile.
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
**Mount pages the way this app's template expects.** Base44 ships more than one and they route
|
|
95
|
+
differently; building for the wrong one renders a blank preview with no error to explain it. STEP 1
|
|
96
|
+
resolved which it is and reported it as `template`:
|
|
97
|
+
|
|
98
|
+
- `tanstack` → **TanStack Start**: file-based routes under `src/routes/`, no `src/App.jsx`, no
|
|
99
|
+
`index.html`, build output in `.output/` rather than `dist/`. Pages mount as route files; chrome
|
|
100
|
+
and providers live in `__root.jsx`.
|
|
101
|
+
- `react-router` → **React Router**: routes and providers are wired into `src/App.jsx`.
|
|
102
|
+
|
|
103
|
+
Read the app's own `AGENTS.md` first either way — both templates ship one and it states their
|
|
104
|
+
conventions. The mounting pattern for each, and what STEP 1 put in `src/lib/nav.js`:
|
|
105
|
+
[`../references/_shared/routing.md`](../references/_shared/routing.md).
|
|
106
|
+
|
|
107
|
+
**On the React Router template, `src/App.jsx` is edited surgically, never rewritten.** It carries
|
|
108
|
+
required platform auth scaffolding (`AuthProvider`/`useAuth` from `@/lib/AuthContext`); a full
|
|
109
|
+
rewrite drops them → the validator rejects the write. Wire routes/imports in with `find_replace`,
|
|
110
|
+
leave the rest as-is. TanStack keeps the same scaffolding in `src/routes/__root.jsx` and
|
|
111
|
+
`src/lib/AuthContext.jsx`, under the same rule: add around it, never replace it.
|
|
97
112
|
|
|
98
113
|
## STEP 4 — Manage and seed the business
|
|
99
114
|
|
|
@@ -17,6 +17,12 @@ Everything is under `.agents/skills/wix-vibe-headless/references/`.
|
|
|
17
17
|
| `wix-client.js` | The transport: exchanges `WIX_CLIENT_ID` for an anonymous **visitor token**, persists + refreshes it (that token *is* the cart/session identity), and exposes `wixApiRequest(path, {method, body, query})` — plus the member-session swap (`setSessionTokens` / `clearSession` / `isMember`). Plain `fetch`; SSR-guards `window`/`localStorage`; maps 402 + error bodies. |
|
|
18
18
|
| `wix-config.js` | Holds `WIX_CLIENT_ID` + `WIX_METASITE_ID` — the only values to fill in. |
|
|
19
19
|
|
|
20
|
+
**Plus `shared/app/lib/nav.js`.** The reference UI imports `Link`, `useParams`, `useNavigate`,
|
|
21
|
+
`useLocation` and `Navigate` from `@/lib/nav` rather than from a router, so one set of sources runs
|
|
22
|
+
on any of them. Point that path at your own router's equivalents — the shipped file re-exports
|
|
23
|
+
react-router's, and `_shared/nav/nav.tanstack.js` shows the kind of wrapping another router needs
|
|
24
|
+
(params scoped to the current match, a `navigate` you call with a path).
|
|
25
|
+
|
|
20
26
|
**Every `references/<vertical>/` holds the same four things:**
|
|
21
27
|
|
|
22
28
|
- `app/rest/wix-*.js` — the vertical's **data layer**: named calls carrying the exact request/response
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Navigation adapter — TanStack Start template (src/routes/ file-based routing).
|
|
2
|
+
// Copy to src/lib/nav.js. Shipped components import Link/useParams/etc from here so the same
|
|
3
|
+
// files run on either Base44 template; this file is the only place the router is named.
|
|
4
|
+
//
|
|
5
|
+
// Two shapes differ from react-router and are normalised here:
|
|
6
|
+
// useParams — TanStack scopes params to a route; `strict: false` returns the current match's
|
|
7
|
+
// params, which is what a shared component wants.
|
|
8
|
+
// useNavigate — TanStack takes an object; this returns a function you call with a path, and
|
|
9
|
+
// `navigate(-1)` still goes back.
|
|
10
|
+
import {
|
|
11
|
+
Link as RouterLink,
|
|
12
|
+
Navigate,
|
|
13
|
+
useLocation,
|
|
14
|
+
useNavigate as useRouterNavigate,
|
|
15
|
+
useParams as useRouterParams,
|
|
16
|
+
} from "@tanstack/react-router";
|
|
17
|
+
|
|
18
|
+
// `to` is an already-resolved path (`/blog/my-post`); the router matches it at runtime.
|
|
19
|
+
export const Link = RouterLink;
|
|
20
|
+
export { Navigate, useLocation };
|
|
21
|
+
|
|
22
|
+
export function useParams() {
|
|
23
|
+
return useRouterParams({ strict: false });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function useNavigate() {
|
|
27
|
+
const navigate = useRouterNavigate();
|
|
28
|
+
return (to, options) =>
|
|
29
|
+
typeof to === "number" ? window.history.go(to) : navigate({ to, ...options });
|
|
30
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Mounting pages: which template is this app?
|
|
2
|
+
|
|
3
|
+
Base44 ships more than one app template, and they route differently — so which one you are on
|
|
4
|
+
decides how a page gets mounted. A page mounted the other template's way renders nothing, and the
|
|
5
|
+
preview comes back blank with no error to explain it.
|
|
6
|
+
|
|
7
|
+
**An install that ran `deploy.cjs` already told you** — its result carries the template it resolved
|
|
8
|
+
from disk, as `{ "template": "react-router" }` or `{ "template": "tanstack" }`. Where the host
|
|
9
|
+
deployed the tree itself there is no such line, and the same fact reads straight off the app —
|
|
10
|
+
**ask `src/routes/__root.jsx` first**: present → TanStack Start (file-based routes, build output in
|
|
11
|
+
`.output/` rather than `dist/`); absent → React Router SPA, routed from `src/App.jsx` with an
|
|
12
|
+
`index.html` beside it. That order matters, because an `src/App.jsx` on a TanStack app is
|
|
13
|
+
something an agent wrote believing the app was React Router — `__root.jsx` is never there by
|
|
14
|
+
mistake. The app's own `AGENTS.md` (both templates ship one) states its conventions — worth a read
|
|
15
|
+
before the first write.
|
|
16
|
+
|
|
17
|
+
## On TanStack, four things differ from the React Router form
|
|
18
|
+
|
|
19
|
+
Routing guidance you meet elsewhere — routes wired into `src/App.jsx`, `react-router-dom` imports,
|
|
20
|
+
its `AuthProvider` / `QueryClientProvider` wrappers — describes the React Router template. On this
|
|
21
|
+
one:
|
|
22
|
+
|
|
23
|
+
- **There is no `src/App.jsx`, and that is the finished state.** Leave it absent; creating one
|
|
24
|
+
wires a router the app does not use, and the pages mounted in it never render.
|
|
25
|
+
- **Route files import `createFileRoute` and `Link` from `@tanstack/react-router`**, which this
|
|
26
|
+
template installs.
|
|
27
|
+
- **The wrappers live in `src/routes/__root.jsx`.** Same rule as `App.jsx` carries on the other
|
|
28
|
+
template: add around them, never replace them.
|
|
29
|
+
- **Telling the two apart costs nothing** — the app's files are already in your context.
|
|
30
|
+
|
|
31
|
+
## The nav adapter is already installed
|
|
32
|
+
|
|
33
|
+
Shipped components import `Link`, `useParams` and friends from `@/lib/nav`, never from a router
|
|
34
|
+
package, so one set of sources runs on either template. `src/lib/nav.js` arrives with the rest of
|
|
35
|
+
the deployed tree, and it is the one place the router is named. Write your own pages against
|
|
36
|
+
`@/lib/nav` too and they stay portable for free.
|
|
37
|
+
|
|
38
|
+
**On TanStack, check what landed.** The deployed default is the React Router adapter, and
|
|
39
|
+
`deploy.cjs` swaps the TanStack one in when it runs — but a host that copies the tree itself lands
|
|
40
|
+
the default whatever the template. So on a TanStack app, open `src/lib/nav.js` and, if it
|
|
41
|
+
re-exports from `react-router-dom`, copy `_shared/nav/nav.tanstack.js` over it. That file carries
|
|
42
|
+
the two normalisations the imports depend on: `useParams` scoped with `strict: false`, and
|
|
43
|
+
`useNavigate` returning a function you call with a path (`navigate(-1)` included).
|
|
44
|
+
|
|
45
|
+
## React Router template
|
|
46
|
+
|
|
47
|
+
One `Layout` holding your chrome renders `<Outlet/>`; every route nests under a single pathless
|
|
48
|
+
route so the chrome wraps all pages. Each vertical's `STEP 3` carries the exact snippet, including
|
|
49
|
+
where the preview banner sits.
|
|
50
|
+
|
|
51
|
+
## TanStack Start template
|
|
52
|
+
|
|
53
|
+
Same structure, expressed as files. The chrome that lives in `Layout` goes in `src/routes/__root.jsx`
|
|
54
|
+
(it already renders the document and providers — add your header/footer around its `<Outlet/>`),
|
|
55
|
+
and each route is its own file exporting `createFileRoute`:
|
|
56
|
+
|
|
57
|
+
```jsx
|
|
58
|
+
// src/routes/shop.jsx → /shop
|
|
59
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
60
|
+
import Shop from "@/pages/Shop"; // the shipped page, unchanged
|
|
61
|
+
|
|
62
|
+
export const Route = createFileRoute("/shop")({ component: Shop });
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```jsx
|
|
66
|
+
// src/routes/product.$slug.jsx → /product/:slug
|
|
67
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
68
|
+
import ProductDetail from "@/pages/ProductDetail"; // shipped; reads its param via @/lib/nav
|
|
69
|
+
|
|
70
|
+
export const Route = createFileRoute("/product/$slug")({ component: ProductDetail });
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Three things to carry over from the React Router wiring:
|
|
74
|
+
|
|
75
|
+
- **Shipped pages stay where they are** (`src/pages/…`) and stay unedited. A route file is a
|
|
76
|
+
two-line mount, not a copy of the page.
|
|
77
|
+
- **Path params use `$name`**, so `/product/:slug` becomes the file `product.$slug.jsx` and the
|
|
78
|
+
route path `/product/$slug`. `useParams()` from `@/lib/nav` returns them the same way.
|
|
79
|
+
- **Providers** a vertical asks you to wrap the tree in (`CartProvider`, `MemberProvider`, …) go in
|
|
80
|
+
`__root.jsx` around `<Outlet/>`, once — not per route file.
|
|
81
|
+
|
|
82
|
+
Per-user routes should also set `ssr: false` so personal data never lands in cacheable HTML:
|
|
83
|
+
|
|
84
|
+
```jsx
|
|
85
|
+
export const Route = createFileRoute("/my-plans")({ component: MyPlans, ssr: false });
|
|
86
|
+
```
|