@wix/agent-skills 1.15.0 → 1.15.1

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.
Files changed (61) hide show
  1. package/README.md +8 -0
  2. package/package.json +1 -1
  3. package/skills/wix-app/references/DATA_COLLECTION.md +5 -3
  4. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +2 -0
  5. package/skills/wix-app/references/STORES_VERSIONING.md +9 -211
  6. package/skills/wix-app/references/backend-event/COMMON-EVENTS.md +5 -0
  7. package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +151 -0
  8. package/skills/wix-app/references/editor-react-component/COMPONENT-PREVIEW.md +57 -0
  9. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +6 -0
  10. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +3 -1
  11. package/skills/wix-app/references/stores/CATEGORIES.md +22 -0
  12. package/skills/wix-app/references/stores/GET_PRODUCT.md +99 -0
  13. package/skills/wix-app/references/stores/INVENTORY.md +37 -0
  14. package/skills/wix-app/references/stores/QUERY.md +87 -0
  15. package/skills/wix-app/references/stores/WRITE.md +55 -0
  16. package/skills/wix-headless/SKILL.md +6 -1
  17. package/skills/wix-headless/entry/bootstrap.mjs +2 -2
  18. package/skills/wix-headless/entry/skill.md +0 -2
  19. package/skills/wix-headless/references/CAPABILITIES.md +6 -4
  20. package/skills/wix-headless/references/DISCOVERY.md +1 -1
  21. package/skills/wix-headless/references/FEEDBACK.md +16 -5
  22. package/skills/wix-headless/references/SDK_HANDOFF.md +2 -0
  23. package/skills/wix-headless/references/SEED.md +3 -1
  24. package/skills/wix-headless/references/SETUP.md +1 -1
  25. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-reservations.md +42 -2
  26. package/skills/wix-headless/references/inline-recipes/setup-bookings.md +5 -1
  27. package/skills/wix-headless/references/inline-recipes/setup-forms.md +1 -1
  28. package/skills/wix-headless/references/inline-recipes/setup-online-store.md +21 -5
  29. package/skills/wix-headless/references/inline-recipes/setup-portfolio.md +3 -1
  30. package/skills/wix-headless/references/inline-recipes/setup-restaurant-experiences.md +73 -0
  31. package/skills/wix-headless/references/inline-recipes/setup-restaurant-orders.md +38 -2
  32. package/skills/wix-headless/references/inline-recipes/setup-restaurant-reservations.md +20 -1
  33. package/skills/wix-headless/references/inline-recipes/setup-restaurants.md +1 -1
  34. package/skills/wix-headless/references/managed/CONNECT.md +1 -1
  35. package/skills/wix-headless/references/managed/CREATE.md +1 -1
  36. package/skills/wix-headless/references/managed/DEPLOYMENT.md +22 -0
  37. package/skills/wix-headless/references/self-managed/DEPLOYMENT.md +8 -0
  38. package/skills/wix-headless/references/stripe/DEPLOYMENT.md +8 -0
  39. package/skills/wix-manage/SKILL.md +8 -5
  40. package/skills/wix-manage/references/analytics/query-site-analytics.md +66 -9
  41. package/skills/wix-manage/references/app-installation/install-wix-apps.md +3 -0
  42. package/skills/wix-manage/references/bookings/create-booking-service-from-prompt.md +6 -5
  43. package/skills/wix-manage/references/bookings/create-course-service.md +30 -5
  44. package/skills/wix-manage/references/bookings/diagnose-availability-issues.md +76 -42
  45. package/skills/wix-manage/references/cms/cms-schema-management.md +6 -0
  46. package/skills/wix-manage/references/get-paid/how-to-setup-wix-payments.md +30 -7
  47. package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +225 -144
  48. package/skills/wix-manage/references/marketing/generate-and-publish-marketing-plan.md +19 -10
  49. package/skills/wix-manage/references/sites/site-import.md +270 -0
  50. package/skills/wix-manage/references/stores/create-product-from-image.md +8 -6
  51. package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +16 -10
  52. package/skills/wix-vibe-headless/SKILL.md +6 -0
  53. package/skills/wix-vibe-headless/platforms/README.md +22 -0
  54. package/skills/wix-vibe-headless/platforms/base44.md +142 -0
  55. package/skills/wix-vibe-headless/platforms/generic.md +87 -0
  56. package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +5 -1
  57. package/skills/wix-vibe-headless/references/bookings/wix-bookings-services.js +3 -1
  58. package/skills/wix-vibe-headless/references/events/wix-events-browse.js +2 -1
  59. package/skills/wix-vibe-headless/references/portfolio/wix-portfolio.js +84 -55
  60. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +11 -2
  61. package/skills/wix-vibe-headless/references/storefront/wix-store-catalog.js +5 -1
package/README.md CHANGED
@@ -91,6 +91,14 @@ When a major bump is required (a breaking change in the underlying `wix-cli`), t
91
91
 
92
92
  Run the [`release-bump`](.github/workflows/release-bump.yml) workflow from the **Actions** tab and pick a `version_strategy`. The rest is automatic — the bump PR auto-merges once checks pass and [`release.yml`](.github/workflows/release.yml) publishes to npm via Trusted Publishing.
93
93
 
94
+ ## Serving & caching (`www.wix.com/skills`)
95
+
96
+ Besides the npm package, skills are served as raw markdown / `.tgz` at `https://www.wix.com/skills/<name>` (also `dev.wix.com/skills`). That registry is **not** in this repo — it's an Express service in [`wix-private/docs`](https://github.com/wix-private/docs) (`docs-web-app`) that serves this repo's `main` branch directly, with no build step.
97
+
98
+ Responses are cached at the Wix edge (Pepyaka, tag `skills`) plus a ~5-min per-pod tarball cache. A push to `main` touching `skills/**` **auto-invalidates** the edge tag (via `md-resolver`'s GitHub-push consumer), so a merge reflects within a few minutes. To force it immediately, hit the manual flush (`POST /api/v1/invalidate-ssr-cache/skills`, BO staff) or the Fire Console `invalidateCache` call.
99
+
100
+ Full details — endpoints, `wix-` prefix stripping, host-relative links, invalidation — are in the [docs-web-app skills README](https://github.com/wix-private/docs/blob/main/packages/docs-web-app/express/skills/README.md).
101
+
94
102
  ## Contributing
95
103
 
96
104
  See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on adding new skills.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/agent-skills",
3
- "version": "1.15.0",
3
+ "version": "1.15.1",
4
4
  "description": "Wix Skills for AI coding agents — versioned npm distribution of the wix/skills content.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -80,18 +80,20 @@ export default {
80
80
  | `ARRAY_DOCUMENT` | Array of documents | File collections |
81
81
  | `ANY` | Any type | Most flexible |
82
82
 
83
- **CRITICAL: OBJECT fields require `objectOptions`.** When using `type: "OBJECT"`, you MUST include the `objectOptions` property — the API will reject OBJECT fields without it. Use an empty object `{}` if you don't need schema validation:
83
+ **CRITICAL: OBJECT fields require `objectOptions` with a `fields` array.** When using `type: "OBJECT"`, you MUST include `objectOptions: { fields: [] }` — the API will reject OBJECT fields without it. Use an empty `fields` array if you don't need a fixed schema (the object will still accept arbitrary JSON):
84
84
 
85
85
  ```json
86
86
  {
87
87
  "key": "settings",
88
88
  "displayName": "Settings",
89
89
  "type": "OBJECT",
90
- "objectOptions": {}
90
+ "objectOptions": { "fields": [] }
91
91
  }
92
92
  ```
93
93
 
94
- For structured objects, define nested fields inside `objectOptions.fields`:
94
+ > ⚠️ `objectOptions: {}` (without the `fields` key) is **not valid** and will cause a runtime error. Always include `fields`, even as an empty array.
95
+
96
+ For structured objects with a defined schema, list the nested fields inside `objectOptions.fields`:
95
97
 
96
98
  ```json
97
99
  {
@@ -87,6 +87,8 @@ Topic-focused references (rules + patterns + common mistakes in one place):
87
87
  - [`editor-react-component/PROPS-VS-CSS.md`](editor-react-component/PROPS-VS-CSS.md) — What should be a React prop vs CSS
88
88
  - [`editor-react-component/COMPONENT-API.md`](editor-react-component/COMPONENT-API.md) — Props structure, elementProps, data types, file splitting, containers, array props
89
89
  - [`editor-react-component/REACT-PATTERNS.md`](editor-react-component/REACT-PATTERNS.md) — SSR-safe patterns, CSS rules, remaining common mistakes
90
+ - [`editor-react-component/ANIMATED-COMPONENTS.md`](editor-react-component/ANIMATED-COMPONENTS.md) — Play/pause controls for animated components (Lottie, GIF, autoPlay props)
91
+ - [`editor-react-component/COMPONENT-PREVIEW.md`](editor-react-component/COMPONENT-PREVIEW.md) — `component.preview.tsx` patterns and `useIsEditMode()` usage
90
92
 
91
93
  ## CSS guidelines
92
94
 
@@ -1,4 +1,3 @@
1
-
2
1
  # Wix Stores Catalog Versioning (V1 / V3)
3
2
 
4
3
  Wix Stores has two catalog versions that are **NOT backwards compatible**. Apps **must support both** — single-version apps cannot list in the App Market and break on new sites.
@@ -82,214 +81,6 @@ Handle `STORES_NOT_INSTALLED` gracefully — return empty results, do not throw.
82
81
 
83
82
  ---
84
83
 
85
- ## Copy-Paste Recipes
86
-
87
- Each recipe handles both versions. `V3Product` and V1 `Product` types are **not** re-exported from `@wix/stores` top-level — let TS infer or use structural types.
88
-
89
- ### List products with pagination
90
-
91
- Both versions expose a fluent query builder, but the paging method differs:
92
-
93
- | Aspect | V1 (`products.queryProducts()`) | V3 (`productsV3.queryProducts()`) |
94
- |--------|--------------------------------|----------------------------------|
95
- | API style | Fluent builder: `.skip().limit().find()` | Fluent builder: `.skipTo(cursor).limit().find()` |
96
- | Pagination | Offset (`.skip(n)`) | Cursor (`.skipTo(cursor)`) |
97
- | Result `items` | `res.items` (V1 `Product[]`) | `res.items` (V3 `Product[]`) |
98
- | Total count | `res.totalCount` | **None** — V3 only has `cursors.next` + `hasNext()` |
99
- | `hasNext` | `res.hasNext()` (method) | `res.hasNext()` (method) |
100
- | Next cursor | n/a | `res.cursors.next` (string) |
101
-
102
- ```typescript
103
- import { catalogVersioning, products, productsV3 } from '@wix/stores';
104
-
105
- export interface ProductsPage {
106
- products: unknown[]; // narrow at call site
107
- nextCursor: string | null; // V3 only
108
- hasNext: boolean;
109
- totalCount: number | null; // V1 only
110
- }
111
-
112
- export async function listProductsPage(
113
- limit: number,
114
- cursorOrSkip: string | number | undefined,
115
- ): Promise<ProductsPage> {
116
- const v = await getVersion();
117
- if (v === 'STORES_NOT_INSTALLED') {
118
- return { products: [], nextCursor: null, hasNext: false, totalCount: 0 };
119
- }
120
-
121
- if (v === 'V3_CATALOG') {
122
- let builder = productsV3.queryProducts().limit(limit);
123
- if (typeof cursorOrSkip === 'string') builder = builder.skipTo(cursorOrSkip);
124
- // Do NOT chain a sort — see gotcha #10.
125
- const res = await builder.find();
126
- return {
127
- products: res.items,
128
- nextCursor: res.cursors.next ?? null,
129
- hasNext: res.hasNext(),
130
- totalCount: null,
131
- };
132
- }
133
-
134
- const skip = typeof cursorOrSkip === 'number' ? cursorOrSkip : 0;
135
- const res = await products.queryProducts().skip(skip).limit(limit).find();
136
- return {
137
- products: res.items,
138
- nextCursor: null,
139
- hasNext: res.hasNext(),
140
- totalCount: res.totalCount ?? null,
141
- };
142
- }
143
- ```
144
-
145
- > **Two ways to call V3 `queryProducts`:** the canonical builder shown above, and a direct-call form `productsV3.queryProducts({ cursorPaging: { limit, cursor } })` returning a `Promise<{ products, pagingMetadata }>`. Both compile and run, but the builder is more idiomatic and matches V1's shape.
146
-
147
- ### Display price/stock without fetching variants
148
-
149
- V3 `queryProducts` does not return variants. Read product-level rollup fields instead:
150
-
151
- ```typescript
152
- function displayPrice(p: { actualPriceRange?: { minValue?: { amount?: string }; maxValue?: { amount?: string } } }): string {
153
- const min = p.actualPriceRange?.minValue?.amount;
154
- const max = p.actualPriceRange?.maxValue?.amount;
155
- if (!min) return '—';
156
- return max && max !== min ? `${min} – ${max}` : min;
157
- }
158
-
159
- function stockLabel(status: string | undefined): string {
160
- switch (status) {
161
- case 'IN_STOCK': return 'In Stock';
162
- case 'OUT_OF_STOCK': return 'Out of Stock';
163
- case 'PARTIALLY_OUT_OF_STOCK': return 'Limited';
164
- case 'PREORDER': return 'Pre-order';
165
- default: return '—';
166
- }
167
- }
168
- // V1 path: product.stock.inventoryStatus (same UPPER_SNAKE_CASE values)
169
- // V3 path: product.inventory.availabilityStatus (adds PREORDER)
170
- // SKU lives on the variant — show "—" in lists, or use Read-Only Variants API.
171
- ```
172
-
173
- ### Get a single product
174
-
175
- ```typescript
176
- if (v === 'V3_CATALOG') {
177
- const product = await productsV3.getProduct(id); // returns Product directly
178
- return product;
179
- }
180
- const { product } = await products.getProduct(id); // V1 wraps in { product }
181
- return product;
182
- ```
183
-
184
- ### Create a product (single-variant, with price)
185
-
186
- ```typescript
187
- if (v === 'V3_CATALOG') {
188
- const product = await productsV3.createProduct({
189
- name: 'My Product',
190
- productType: 'PHYSICAL', // UPPER_CASE
191
- variantsInfo: {
192
- variants: [{
193
- price: { actualPrice: { amount: '19.99' } }, // string, on the variant
194
- sku: 'SKU-001',
195
- }],
196
- },
197
- });
198
- return product;
199
- }
200
-
201
- const { product } = await products.createProduct({
202
- name: 'My Product',
203
- productType: 'physical', // lower-case
204
- priceData: { price: 19.99 }, // number, on the product
205
- sku: 'SKU-001',
206
- });
207
- return product;
208
- ```
209
-
210
- ### Update a product (V3 needs `revision`)
211
-
212
- ```typescript
213
- if (v === 'V3_CATALOG') {
214
- // Signature: updateProduct(_id, productFields). Returns Product directly.
215
- const current = await productsV3.getProduct(id);
216
- if (current.revision == null) throw new Error(`Product ${id} has no revision`);
217
- return await productsV3.updateProduct(id, {
218
- revision: current.revision, // narrowed — required under exactOptionalPropertyTypes
219
- name: 'New name',
220
- });
221
- }
222
- // V1: updateProduct(id, productFields). Returns { product }.
223
- const { product } = await products.updateProduct(id, { name: 'New name' });
224
- return product;
225
- ```
226
-
227
- ### Delete a product
228
-
229
- ```typescript
230
- if (v === 'V3_CATALOG') await productsV3.deleteProduct(id);
231
- else await products.deleteProduct(id);
232
- ```
233
-
234
- ### Inventory: increment stock
235
-
236
- ```typescript
237
- import { inventory, inventoryItemsV3 } from '@wix/stores';
238
-
239
- if (v === 'V3_CATALOG') {
240
- // Per-variant; flat shape.
241
- await inventoryItemsV3.bulkIncrementInventoryItems([
242
- { inventoryItemId, incrementBy: 5 },
243
- ]);
244
- } else {
245
- // Per-product (variant optional).
246
- await inventory.incrementInventory([
247
- { productId, variantId, incrementBy: 5 },
248
- ]);
249
- }
250
- ```
251
-
252
- To find a V3 inventory item ID, use `inventoryItemsV3.searchInventoryItems` filtered by `productId` / `variantId`.
253
-
254
- ### Query inventory
255
-
256
- ```typescript
257
- if (v === 'V3_CATALOG') {
258
- const res = await inventoryItemsV3.queryInventoryItems().eq('productId', productId).find();
259
- return res.items;
260
- }
261
- // V1: filter is a JSON-stringified expression.
262
- const res = await inventory.queryInventory({
263
- query: { filter: JSON.stringify({ productId }) },
264
- });
265
- return res.inventoryItems;
266
- ```
267
-
268
- ### Collections (V1) ↔ Categories (V3)
269
-
270
- ```typescript
271
- // V1 write ops (createCollection, updateCollection, addProductsToCollection, …)
272
- // live on the `products` namespace, NOT `collections`. The `collections` namespace is read-only.
273
- import { products } from '@wix/stores';
274
- import { categories } from '@wix/categories';
275
-
276
- if (v === 'V3_CATALOG') {
277
- // V3 createCategory(category, options). treeReference is REQUIRED and goes in `options`.
278
- // For Wix Stores categories: appNamespace MUST be the literal "@wix/stores", treeKey is null.
279
- const category = await categories.createCategory(
280
- { name: 'Sale' },
281
- { treeReference: { appNamespace: '@wix/stores', treeKey: null } },
282
- );
283
- return category;
284
- }
285
- const { collection } = await products.createCollection({ name: 'Sale' }); // V1 returns { collection }
286
- return collection;
287
- ```
288
-
289
- V3 categories are tree-structured. Reference them on a product via `directCategories[]`, not `collectionIds[]`. **All Stores category API calls must pass `treeReference: { appNamespace: '@wix/stores', treeKey: null }` in `options`** — applies to `createCategory`, `updateCategory`, `queryCategories`, etc.
290
-
291
- ---
292
-
293
84
  ## V1 → V3 Field Mapping (most common)
294
85
 
295
86
  For the full table see the [Catalog V1 to V3 Migration Guide](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/catalog-v1-to-v3-migration-guide?apiView=SDK).
@@ -311,6 +102,7 @@ For the full table see the [Catalog V1 to V3 Migration Guide](https://dev.wix.co
311
102
  | `collectionIds[i]` | `directCategories[i].id` |
312
103
  | `ribbon`, `brand` (string) | `ribbon.name`, `brand.name` (managed via `ribbonsV3` / `brandsV3`) |
313
104
  | `media.mainMedia.image.url` (main image URL) | `media.main.image ?? media.main.url` — in V3, `ProductMedia.image` is a `string` (Wix-hosted URL), `url` is a `string` (external URL); check both |
105
+ | `lastUpdated` | `updatedDate` |
314
106
 
315
107
  **Pricing model** — V1 had a `discount` object; V3 removed it. Discounts are now expressed by the relationship between two fields on the variant: `actualPrice` (required, what the customer pays) vs `compareAtPrice` (optional, original price shown struck through). With discount: V1 `price`/`discountedPrice` → V3 `compareAtPrice`/`actualPrice`. Without discount: V1 `price` → V3 `actualPrice`, leave `compareAtPrice` empty.
316
108
 
@@ -330,6 +122,12 @@ Payload changes: V1 `changedFields` → V3 `modifiedFields`. Top-level `entityId
330
122
 
331
123
  ---
332
124
 
333
- ## Falling back to MCP
125
+ ## Detailed references
126
+
127
+ - [stores/QUERY.md](stores/QUERY.md) — list/query products, pagination, price and stock display
128
+ - [stores/GET_PRODUCT.md](stores/GET_PRODUCT.md) — getProduct, options/choices, modifiers, variant choices
129
+ - [stores/WRITE.md](stores/WRITE.md) — create, update, delete products
130
+ - [stores/INVENTORY.md](stores/INVENTORY.md) — inventory read and write operations
131
+ - [stores/CATEGORIES.md](stores/CATEGORIES.md) — collections (V1) ↔ categories (V3)
334
132
 
335
- This file covers the common 80%. For methods not listed (subscriptions, brands, ribbons, etc.) or full request schemas, use `SearchWixSDKDocumentation` then `ReadFullDocsArticle`. Always return the required permission scopes to the user.
133
+ For methods not listed here, use `SearchWixSDKDocumentation` then `ReadFullDocsArticle`. Always return the required permission scopes to the user.
@@ -12,10 +12,15 @@ This reference lists common event types, SDK imports, permissions, and links. Fo
12
12
 
13
13
  ## eCommerce Events
14
14
 
15
+ Order events use **`@wix/ecom`** (namespace `orders`). Order **refund** events are the exception: they live in **`@wix/payments`** (namespace `refunds`), not `@wix/ecom`.
16
+
15
17
  | Event | Import | Handler Call | Permission | API Reference |
16
18
  | --- | --- | --- | --- | --- |
17
19
  | Order created | `import { orders } from "@wix/ecom"` | `orders.onOrderCreated(handler)` | `SCOPE.DC-STORES.READ-ORDERS` | [onOrderCreated](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/orders/order-created?apiView=SDK) |
18
20
  | Order approved | `import { orders } from "@wix/ecom"` | `orders.onOrderApproved(handler)` | `SCOPE.DC-STORES.READ-ORDERS` | [onOrderApproved](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/orders/order-approved?apiView=SDK) |
21
+ | Order canceled | `import { orders } from "@wix/ecom"` | `orders.onOrderCanceled(handler)` | `SCOPE.DC-STORES.READ-ORDERS` | [onOrderCanceled](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/orders/order-canceled?apiView=SDK) |
22
+ | Refund created | `import { refunds } from "@wix/payments"` | `refunds.onRefundCreated(handler)` | `SCOPE.DC-PAYMENTS.READ-REFUNDS` | [onRefundCreated](https://dev.wix.com/docs/api-reference/business-management/payments/refunds/refund-created?apiView=SDK) |
23
+ | Refund updated | `import { refunds } from "@wix/payments"` | `refunds.onRefundUpdated(handler)` | `SCOPE.DC-PAYMENTS.READ-REFUNDS` | [onRefundUpdated](https://dev.wix.com/docs/api-reference/business-management/payments/refunds/refund-updated?apiView=SDK) |
19
24
  | Cart created | `import { cart } from "@wix/ecom"` | `cart.onCartCreated(handler)` | `SCOPE.DC-STORES.READ-ORDERS` | [onCartCreated](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/cart/cart/cart-created?apiView=SDK) |
20
25
  | Cart updated | `import { cart } from "@wix/ecom"` | `cart.onCartUpdated(handler)` | `SCOPE.DC-STORES.READ-ORDERS` | [onCartUpdated](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/cart/cart/cart-updated?apiView=SDK) |
21
26
 
@@ -0,0 +1,151 @@
1
+ # Animated Components (Play/Pause Control)
2
+
3
+ Rules and patterns for components whose primary content is a **playable
4
+ animation** — a Lottie/JSON animation, an animated GIF/SVG, a canvas/WebGL
5
+ loop, or a video-like surface.
6
+
7
+ Every such component MUST ship an on-stage **play/pause button**.
8
+
9
+ ---
10
+
11
+ ## When this applies
12
+
13
+ Apply automatically — without being asked — whenever the component has an
14
+ `autoPlay` prop **or** its primary content is a **startable / loopable**
15
+ animation a visitor would reasonably want to start or stop:
16
+
17
+ - Any component with an `autoPlay` prop (gallery, slider, carousel, etc.)
18
+ - Lottie / JSON vector animations
19
+ - Animated GIFs or animated SVGs
20
+ - Canvas / WebGL animation loops
21
+ - Any video-like playing surface
22
+
23
+ **Key rule:** If the component accepts an `autoPlay` prop — even if the
24
+ underlying mechanism is a `setInterval` advancing an index rather than a media
25
+ player — it MUST expose a play/pause button so visitors can stop the
26
+ auto-advancing behavior. "Autoplay" is a behavior contract, not an
27
+ implementation detail.
28
+
29
+ ---
30
+
31
+ ## What to add (checklist)
32
+
33
+ 1. **Props** — `autoPlay`, `loop`, `pauseButtonVisibility`
34
+ 2. **Playback state** — `isPlaying` + `handlePause` / `handleResume`
35
+ 3. **Play/pause button** — overlay `<button>` with inline SVG icon + CSS positioning and optional hover-only visibility
36
+ 4. **Modify `component.preview.tsx`** — suppress autoplay in editor design mode
37
+
38
+ ---
39
+
40
+ ## 1. Props
41
+
42
+ Playback controls are **behavior** props (see [`PROPS-VS-CSS.md`](PROPS-VS-CSS.md)):
43
+
44
+ ```typescript
45
+ import type { A11y, Direction } from '@wix/editor-react-types';
46
+
47
+ export interface MyAnimationProps {
48
+ id?: string;
49
+ className?: string;
50
+ direction?: Direction;
51
+ a11y?: A11y;
52
+
53
+ /** Start playing on load. Default `true`. */
54
+ autoPlay?: boolean;
55
+
56
+ /** Repeat when finished. Default `true`. */
57
+ loop?: boolean;
58
+
59
+ /** Show play/pause button. Default `'showOnHover'`. */
60
+ pauseButtonVisibility?: 'showAlways' | 'showOnHover';
61
+ }
62
+ ```
63
+
64
+ ---
65
+
66
+ ## 2. Playback state
67
+
68
+ Track play/pause state with `useState`, initialized from the `autoPlay` prop:
69
+
70
+ ```tsx
71
+ const [isPlaying, setIsPlaying] = React.useState(autoPlay ?? true);
72
+
73
+ const handlePause = () => setIsPlaying(false);
74
+ const handleResume = () => setIsPlaying(true);
75
+ ```
76
+
77
+ Pass `isPlaying` to the animation renderer and toggle between `handlePause` / `handleResume` on button click.
78
+
79
+ ---
80
+
81
+ ## 3. Play/pause button
82
+
83
+ Create play/pause icons that visually match the component's style. Use simple recognizable shapes — a triangle for play, two rectangles for pause — implemented as inline SVG so there is no external icon dependency. Size, stroke, and fill should feel native to the component's design.
84
+
85
+ Position the button absolutely so it overlays the content without pushing other elements out of place:
86
+
87
+ ```css
88
+ .animationContainer {
89
+ position: relative;
90
+ block-size: 100%;
91
+ inline-size: 100%;
92
+ }
93
+
94
+ .playButton {
95
+ position: absolute;
96
+ inset-inline-end: 5px;
97
+ inset-block-start: 5px;
98
+ }
99
+ ```
100
+
101
+ ```css
102
+ /* pauseButtonVisibility variants — structural CSS, not named-part rules */
103
+
104
+ /* showOnHover (default) */
105
+ .root[data-pause-button-visibility="showOnHover"] .playButton {
106
+ opacity: 0;
107
+ pointer-events: none;
108
+ }
109
+
110
+ .root[data-pause-button-visibility="showOnHover"]:hover .playButton {
111
+ opacity: 1;
112
+ pointer-events: auto;
113
+ }
114
+
115
+ /* showAlways — no extra CSS needed; button is visible by default */
116
+ ```
117
+
118
+ ---
119
+
120
+ ## 4. Modify `component.preview.tsx` for autoplay suppression
121
+
122
+ For animated components, modify the generated `component.preview.tsx` to suppress autoplay in editor design mode. See [`COMPONENT-PREVIEW.md`](COMPONENT-PREVIEW.md) for how the preview file works.
123
+
124
+ ### Target state of `component.preview.tsx`
125
+
126
+ Replace the generated passthrough with the following. Replace `MyComponent` with the real component name.
127
+
128
+ ```tsx
129
+ import React from 'react';
130
+ import MyComponent from './component';
131
+ import { useIsEditMode } from '@wix/react-component-utils';
132
+
133
+ export default function MyComponentPreview(
134
+ props: React.ComponentProps<typeof MyComponent>,
135
+ ) {
136
+ const isEditMode = useIsEditMode();
137
+
138
+ return (
139
+ <MyComponent
140
+ {...props}
141
+ autoPlay={isEditMode ? false : props.autoPlay}
142
+ pauseButtonVisibility={isEditMode ? 'showAlways' : props.pauseButtonVisibility}
143
+ />
144
+ );
145
+ }
146
+ ```
147
+
148
+ In editor design mode (`isEditMode` is `true`) → `autoPlay` is forced to `false` and `pauseButtonVisibility` is forced to `'showAlways'` so the site owner can always see and interact with the button.
149
+ In preview mode (`isEditMode` is `false`) → both use the user's configured values.
150
+
151
+ ---
@@ -0,0 +1,57 @@
1
+ # Component Preview (`component.preview.tsx`)
2
+
3
+ Rules and patterns for the editor-specific entry point that every Editor React
4
+ component ships alongside its main `component.tsx`.
5
+
6
+ ---
7
+
8
+ ## What it is
9
+
10
+ `component.preview.tsx` is the editor-specific entry point for a component. The
11
+ Wix CLI loads it **instead of** `component.tsx` when rendering the component
12
+ inside the Harmony Editor. It is generated automatically by the scaffold as a
13
+ passthrough that forwards all props unchanged:
14
+
15
+ ```tsx
16
+ const MyComponentPreview = (props: React.ComponentProps<typeof MyComponent>) => (
17
+ <MyComponent {...props} />
18
+ );
19
+ ```
20
+
21
+ The extension file is also pre-wired to load it — no manual wiring is needed:
22
+
23
+ ```ts
24
+ import componentPreviewUrl from './component.preview.tsx?url';
25
+
26
+ resources: {
27
+ client: { componentUrl },
28
+ editor: { componentUrl: componentPreviewUrl },
29
+ },
30
+ ```
31
+
32
+ ---
33
+
34
+ ## `useIsEditMode()`
35
+
36
+ When you need to distinguish **editor design mode** from **editor preview mode**,
37
+ use `useIsEditMode()` from `@wix/react-component-utils`:
38
+
39
+ ```tsx
40
+ import { useIsEditMode } from '@wix/react-component-utils';
41
+
42
+ const isEditMode = useIsEditMode();
43
+ // true → editor is in design mode
44
+ // false → editor is in preview mode
45
+ ```
46
+
47
+ `@wix/react-component-utils` is already a base dependency — no additional install needed.
48
+
49
+ ---
50
+
51
+ ## When to modify it
52
+
53
+ Modify `component.preview.tsx` whenever the component needs to behave differently
54
+ inside the editor than it does on the live site. The most common case is
55
+ suppressing autoplay so animations don't run while the site owner is designing.
56
+
57
+ → See [`ANIMATED-COMPONENTS.md`](ANIMATED-COMPONENTS.md) §4 for the autoplay suppression pattern.
@@ -465,6 +465,12 @@ slide instead.
465
465
  }
466
466
  ```
467
467
 
468
+ **Media playback is functional, not decorative.** When the component's content
469
+ *is* a playing animation (Lottie/JSON, animated GIF/SVG, canvas/WebGL, video),
470
+ letting it play is allowed — the motion is the content. Such components get a
471
+ play/pause control positioned in CSS. See
472
+ [`ANIMATED-COMPONENTS.md`](ANIMATED-COMPONENTS.md).
473
+
468
474
  ### Set `pointer-events: auto` on the root and every interactive element
469
475
 
470
476
  The root selector and every nested class that the user can click,
@@ -9,6 +9,8 @@ This guide defines rules and guidance on **how to implement** production-quality
9
9
  - [`DIRECTIONALITY.md`](DIRECTIONALITY.md) — RTL/LTR rules and patterns
10
10
  - [`PROPS-VS-CSS.md`](PROPS-VS-CSS.md) — What should be a React prop vs CSS
11
11
  - [`COMPONENT-API.md`](COMPONENT-API.md) — Props structure, elementProps, data types, file splitting, containers, array props
12
+ - [`ANIMATED-COMPONENTS.md`](ANIMATED-COMPONENTS.md) — Play/pause control and autoplay for animated/playable components
13
+ - [`COMPONENT-PREVIEW.md`](COMPONENT-PREVIEW.md) — `component.preview.tsx` patterns and `useIsEditMode()` usage
12
14
  - [`REACT-PATTERNS.md`](REACT-PATTERNS.md) — SSR-safe patterns, CSS rules, common mistakes
13
15
 
14
16
  ## React 18 features are not supported
@@ -23,7 +25,7 @@ Understand the component and infer its behavior. Extract what is provided; use r
23
25
 
24
26
  1. **Identity** — Component name.
25
27
  2. **Structure** — Identify named parts by applying the mandatory filter in [`PARTS.md`](PARTS.md) to every candidate element before accepting it as a part. Include content/data props (labels, items, types, required vs optional) and configuration (toggles, choices, ranges, modes).
26
- 3. **Interactions** — Clicks, hover/focus; what is exposed as event props vs handled internally.
28
+ 3. **Interactions** — Clicks, hover/focus; what is exposed as event props vs handled internally. If the component's primary content is a **playable animation** (Lottie/JSON, animated GIF/SVG, canvas/WebGL loop, video-like surface), it gets a play/pause control — follow [`ANIMATED-COMPONENTS.md`](ANIMATED-COMPONENTS.md).
27
29
  4. **States** — For every part, decide which design states it supports (native: `hover`/`focus`/`disabled`/`invalid`; custom: `selected`/`active`/`open`/… ) by applying the heuristic in [`DESIGN-STATES.md`](DESIGN-STATES.md), and record them in the plan. These become the editor's per-element state styling controls.
28
30
 
29
31
  **Understanding → implementation (checklist):** For every part, decide `elementProps` vs CSS-only and which design states it supports ([`DESIGN-STATES.md`](DESIGN-STATES.md)); build the props interface (data + behavior); wire interaction in React (`useState`, `useEffect`, refs, native event handlers); give every named inner element an `elementProps` entry and spread it + merge its `className` ([`COMPONENT-API.md`](COMPONENT-API.md)); generate TS + CSS. Infer reasonable defaults where anything is unspecified.
@@ -0,0 +1,22 @@
1
+ # Stores — Collections (V1) ↔ Categories (V3)
2
+
3
+ ```typescript
4
+ // V1 write ops (createCollection, updateCollection, addProductsToCollection, …)
5
+ // live on the `products` namespace, NOT `collections`. The `collections` namespace is read-only.
6
+ import { products } from '@wix/stores';
7
+ import { categories } from '@wix/categories';
8
+
9
+ if (v === 'V3_CATALOG') {
10
+ // V3 createCategory(category, options). treeReference is REQUIRED and goes in `options`.
11
+ // For Wix Stores categories: appNamespace MUST be the literal "@wix/stores", treeKey is null.
12
+ const category = await categories.createCategory(
13
+ { name: 'Sale' },
14
+ { treeReference: { appNamespace: '@wix/stores', treeKey: null } },
15
+ );
16
+ return category;
17
+ }
18
+ const { collection } = await products.createCollection({ name: 'Sale' }); // V1 returns { collection }
19
+ return collection;
20
+ ```
21
+
22
+ V3 categories are tree-structured. Reference them on a product via `directCategories[]`, not `collectionIds[]`. **All Stores category API calls must pass `treeReference: { appNamespace: '@wix/stores', treeKey: null }` in `options`** — applies to `createCategory`, `updateCategory`, `queryCategories`, etc.