@wix/agent-skills 1.19.1 → 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.
Files changed (99) hide show
  1. package/package.json +1 -1
  2. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +1 -1
  3. package/skills/wix-app/references/SERVICE_PLUGIN.md +14 -1
  4. package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +7 -2
  5. package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +53 -28
  6. package/skills/wix-app/references/editor-react-component/COMPONENT-CONTRACT.md +17 -4
  7. package/skills/wix-app/references/editor-react-component/FUNCTION-HANDLERS.md +71 -31
  8. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +3 -4
  9. package/skills/wix-app/references/service-plugin/ADDITIONAL-FEES.md +4 -0
  10. package/skills/wix-app/references/service-plugin/AVAILABILITY-TIME-SLOTS-CONFIGURATION.md +118 -0
  11. package/skills/wix-app/references/service-plugin/BOOKING-POLICY.md +65 -0
  12. package/skills/wix-app/references/service-plugin/BOOKINGS-PRICING.md +71 -0
  13. package/skills/wix-app/references/service-plugin/BOOKINGS-STAFF-SORTING.md +11 -3
  14. package/skills/wix-app/references/service-plugin/BOOKINGS-VALIDATION.md +137 -0
  15. package/skills/wix-app/references/service-plugin/DISCOUNT-TRIGGERS.md +23 -16
  16. package/skills/wix-app/references/service-plugin/GIFT-CARDS.md +4 -0
  17. package/skills/wix-app/references/service-plugin/PAYMENT-SETTINGS.md +4 -2
  18. package/skills/wix-app/references/service-plugin/REALTIME-PERMISSIONS.md +45 -0
  19. package/skills/wix-app/references/service-plugin/SHIPPING-RATES.md +15 -0
  20. package/skills/wix-app/references/service-plugin/TOOLS_PROVIDER.md +4 -0
  21. package/skills/wix-app/references/service-plugin/VALIDATIONS.md +4 -0
  22. package/skills/wix-base44-connector/SKILL.md +46 -24
  23. package/skills/wix-headless/SKILL.md +1 -0
  24. package/skills/wix-headless/references/CAPABILITIES.md +7 -2
  25. package/skills/wix-headless/references/SDK_HANDOFF.md +2 -1
  26. package/skills/wix-headless/references/inline-recipes/experience-store.md +49 -0
  27. package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +431 -115
  28. package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +1 -1
  29. package/skills/wix-headless/references/managed/CREATE.md +5 -0
  30. package/skills/wix-headless/references/managed/DEPLOYMENT.md +8 -8
  31. package/skills/wix-headless-fast/install/fast-path.mjs +3 -1
  32. package/skills/wix-headless-fast/references/shared/app/wix/media.ts +13 -0
  33. package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +227 -56
  34. package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartDrawer.tsx +37 -3
  35. package/skills/wix-headless-fast/references/storefront/app/components/storefront/FilterPanel.tsx +266 -0
  36. package/skills/wix-headless-fast/references/storefront/app/components/storefront/QuickAdd.tsx +165 -0
  37. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +48 -15
  38. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +129 -11
  39. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +36 -6
  40. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +190 -24
  41. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/types.ts +62 -4
  42. package/skills/wix-headless-fast/references/storefront/app-astro/layouts/SiteLayout.astro +21 -2
  43. package/skills/wix-headless-fast/references/storefront/seed/SEED.md +5 -1
  44. package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +59 -6
  45. package/skills/wix-manage/SKILL.md +8 -5
  46. package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +4 -1
  47. package/skills/wix-manage/references/bookings/create-appointment-service.md +3 -1
  48. package/skills/wix-manage/references/bookings/create-class-service.md +3 -1
  49. package/skills/wix-manage/references/bookings/create-course-service.md +4 -2
  50. package/skills/wix-manage/references/bookings/end-to-end-booking-flow.md +68 -3
  51. package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-flow-seasonal-promotion.md +1 -1
  52. package/skills/wix-manage/references/events/create-wix-event.md +46 -0
  53. package/skills/wix-manage/references/events/manage-wix-events.md +51 -1
  54. package/skills/wix-manage/references/google-ads/manage-campaign-lifecycle.md +6 -6
  55. package/skills/wix-manage/references/google-ads/manage-campaign-success-guide.md +108 -14
  56. package/skills/wix-manage/references/seo/manage-content-plan.md +1 -1
  57. package/skills/wix-manage/references/seo/manage-seo-suggestions.md +351 -0
  58. package/skills/wix-manage/references/sites/upload-static-site.md +264 -34
  59. package/skills/wix-vibe-headless/install/deploy.cjs +33 -2
  60. package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed-with-install.md +1 -1
  61. package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +13 -4
  62. package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +1 -1
  63. package/skills/wix-vibe-headless/platforms/base44.md +19 -4
  64. package/skills/wix-vibe-headless/platforms/generic.md +6 -0
  65. package/skills/wix-vibe-headless/references/_shared/nav/nav.tanstack.js +30 -0
  66. package/skills/wix-vibe-headless/references/_shared/routing.md +86 -0
  67. package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +44 -0
  68. package/skills/wix-vibe-headless/references/blog/app/components/PostCard.jsx +1 -1
  69. package/skills/wix-vibe-headless/references/blog/app/components/PostChips.jsx +1 -1
  70. package/skills/wix-vibe-headless/references/blog/app/pages/CategoryPage.jsx +1 -1
  71. package/skills/wix-vibe-headless/references/blog/app/pages/PostDetail.jsx +1 -1
  72. package/skills/wix-vibe-headless/references/blog/app/pages/TagPage.jsx +1 -1
  73. package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +42 -0
  74. package/skills/wix-vibe-headless/references/bookings/app/components/ServiceCard.jsx +1 -1
  75. package/skills/wix-vibe-headless/references/bookings/app/pages/ServiceDetail.jsx +1 -1
  76. package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +42 -0
  77. package/skills/wix-vibe-headless/references/events/app/components/EventCard.jsx +1 -1
  78. package/skills/wix-vibe-headless/references/events/app/pages/EventDetail.jsx +1 -1
  79. package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +42 -0
  80. package/skills/wix-vibe-headless/references/members/app/components/MemberMenu.jsx +1 -1
  81. package/skills/wix-vibe-headless/references/members/app/components/RequireAuth.jsx +1 -1
  82. package/skills/wix-vibe-headless/references/members/app/pages/Login.jsx +1 -1
  83. package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +43 -0
  84. package/skills/wix-vibe-headless/references/portfolio/app/components/CollectionCard.jsx +1 -1
  85. package/skills/wix-vibe-headless/references/portfolio/app/components/ProjectCard.jsx +1 -1
  86. package/skills/wix-vibe-headless/references/portfolio/app/pages/CollectionPage.jsx +1 -1
  87. package/skills/wix-vibe-headless/references/portfolio/app/pages/ProjectDetail.jsx +1 -1
  88. package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +43 -0
  89. package/skills/wix-vibe-headless/references/pricing-plans/app/components/PlanCard.jsx +1 -1
  90. package/skills/wix-vibe-headless/references/pricing-plans/app/pages/PlanDetail.jsx +1 -1
  91. package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +36 -0
  92. package/skills/wix-vibe-headless/references/shared/app/lib/nav.js +9 -0
  93. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +110 -27
  94. package/skills/wix-vibe-headless/references/storefront/app/components/CartDrawer.jsx +32 -14
  95. package/skills/wix-vibe-headless/references/storefront/app/context/CartContext.jsx +14 -3
  96. package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductCard.js +47 -35
  97. package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductDetail.js +77 -30
  98. package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-cart.js +31 -2
  99. package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-catalog.js +31 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/agent-skills",
3
- "version": "1.19.1",
3
+ "version": "1.20.0",
4
4
  "description": "Wix Skills for AI coding agents — versioned npm distribution of the wix/skills content.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -110,7 +110,7 @@ extension configuration, and supporting files.
110
110
  | Trigger | Read |
111
111
  | --- | --- |
112
112
  | Interactive/selectable part or custom state | [`DESIGN-STATES.md`](editor-react-component/DESIGN-STATES.md) |
113
- | Public event callbacks added or changed | [`FUNCTION-HANDLERS.md`](editor-react-component/FUNCTION-HANDLERS.md) |
113
+ | Creating interactive components or changing interactions/callbacks | [`FUNCTION-HANDLERS.md`](editor-react-component/FUNCTION-HANDLERS.md) |
114
114
  | Browser APIs, effects, or time-dependent output | [`SSR.md`](editor-react-component/SSR.md) |
115
115
  | Non-established CSS feature or DOM API, or user asks for one by name | [`BROWSER-SUPPORT.md`](editor-react-component/BROWSER-SUPPORT.md) |
116
116
  | `npx wix build` or manifest generation exits with an error | [`MANIFEST-ERRORS.md`](editor-react-component/MANIFEST-ERRORS.md) |
@@ -1,7 +1,7 @@
1
1
 
2
2
  # Wix Service Plugin (SPI) Builder
3
3
 
4
- Service plugins are a set of APIs defined by Wix that let you inject custom logic into the existing backend flows of Wix business solutions or introduce entirely new flows. When you implement a service plugin, Wix calls your custom functions during specific flows. Common use cases include eCommerce customization (shipping, fees, payment settings, validations) and Bookings customization (staff sorting).
4
+ Service plugins are a set of APIs defined by Wix that let you inject custom logic into the existing backend flows of Wix business solutions or introduce entirely new flows. When you implement a service plugin, Wix calls your custom functions during specific flows. Common use cases include eCommerce customization (shipping, fees, payment settings, validations), Bookings customization (staff sorting, booking validation, policies, pricing, availability), Realtime channel permissions, and exposing tools to the Wix AI assistant.
5
5
 
6
6
  ## Scaffold
7
7
 
@@ -16,12 +16,19 @@ Use `wix generate --params` with `extensionType: SERVICE_PLUGIN`. `pluginType` i
16
16
  | `ECOM_PAYMENT_SETTINGS` | Payment Settings | No |
17
17
  | `GIFT_CARDS_PROVIDER` | Gift Cards Provider | **Yes** |
18
18
  | `STAFF_SORTING_PROVIDER` | Bookings Staff Sorting | No |
19
+ | `BOOKINGS_VALIDATION_PROVIDER` | Bookings Validation | No |
20
+ | `BOOKING_POLICY_PROVIDER` | Booking Policy | No |
21
+ | `BOOKINGS_PRICING_PROVIDER` | Bookings Pricing Provider | No |
22
+ | `AVAILABILITY_TIME_SLOTS_CONFIGURATION_PROVIDER` | Availability Time Slots Configuration | No |
19
23
  | `REALTIME_PERMISSIONS_PROVIDER` | Realtime Permissions Provider | No |
24
+ | `TOOLS_PROVIDER_CONFIG` | Tools Provider | No |
20
25
 
21
26
  > **Singular types** — `ECOM_SHIPPING_RATES`, `ECOM_DISCOUNTS_TRIGGER`, and `GIFT_CARDS_PROVIDER` are **singular**: only one component of each type is allowed per app. Never scaffold or include two components of the same singular type in the same app payload.
22
27
 
23
28
  `name` must be lowercase alphanumeric + hyphens, max 19 characters. The CLI generates the folder, `plugin.ts`, the builder file, the UUID, and the `src/extensions.ts` registration with the appropriate builder method for the SPI type. Some SPI types (e.g., `ECOM_SHIPPING_RATES`) get a `description` placeholder field in the generated builder — replace it with your real copy.
24
29
 
30
+ > **Missing dependency after scaffolding:** `wix generate` adds the SPI's SDK package (e.g. `@wix/bookings`, `@wix/realtime`) to `package.json`, but does not always install it into `node_modules`. If `tsc`/`wix build` reports the import as unresolved right after scaffolding, run `npm install <package>` for just that package (no need for a full reinstall) before debugging further.
31
+
25
32
  ## References
26
33
 
27
34
  **You MUST read the relevant reference document before implementing an SPI**, and **call `ReadFullDocsMethodSchema`** with the docs URL it points at to get the exact request/response types — **do NOT edit code until you have the schema**. If the schema alone isn't enough, follow up with `ReadFullDocsArticle` on the same URL for prose explanations and additional code examples. Each reference also contains the correct imports, handler signatures, response structures, and a worked example.
@@ -35,6 +42,12 @@ Use `wix generate --params` with `extensionType: SERVICE_PLUGIN`. `pluginType` i
35
42
  | Shipping Rates | [SHIPPING-RATES.md](service-plugin/SHIPPING-RATES.md) |
36
43
  | Validations | [VALIDATIONS.md](service-plugin/VALIDATIONS.md) |
37
44
  | Bookings Staff Sorting | [BOOKINGS-STAFF-SORTING.md](service-plugin/BOOKINGS-STAFF-SORTING.md) |
45
+ | Bookings Validation | [BOOKINGS-VALIDATION.md](service-plugin/BOOKINGS-VALIDATION.md) |
46
+ | Booking Policy | [BOOKING-POLICY.md](service-plugin/BOOKING-POLICY.md) |
47
+ | Bookings Pricing Provider | [BOOKINGS-PRICING.md](service-plugin/BOOKINGS-PRICING.md) |
48
+ | Availability Time Slots Configuration | [AVAILABILITY-TIME-SLOTS-CONFIGURATION.md](service-plugin/AVAILABILITY-TIME-SLOTS-CONFIGURATION.md) |
49
+ | Realtime Permissions Provider | [REALTIME-PERMISSIONS.md](service-plugin/REALTIME-PERMISSIONS.md) |
50
+ | Tools Provider | [TOOLS_PROVIDER.md](service-plugin/TOOLS_PROVIDER.md) |
38
51
 
39
52
  ## Implementation Pattern
40
53
 
@@ -192,13 +192,18 @@ the `a11y` contract, and SSR safety. Verify what it cannot see:
192
192
  meaning stays in the accessibility tree.
193
193
  - State hidden through `--display` or transforms agrees with focusability and
194
194
  the accessibility tree; disabled and inert states behave consistently.
195
- - Custom widgets (tabs, menus, dialogs, sliders) implement their keyboard
196
- pattern: arrow keys, Home/End, Escape, roving `tabIndex`.
195
+ - Custom widgets (tabs, menus, dialogs, sliders) implement their full APG
196
+ keyboard pattern (arrow keys, Home/End, Escape, roving `tabIndex`) and
197
+ structural relationships, or use a plain native element instead of
198
+ borrowing the role.
197
199
  - Interactive controls have a hit area of at least 24×24 CSS px and visible
198
200
  focus.
199
201
  - The root implements the direction contract, and every `ReactNode` slot
200
202
  isolates nested content with `dir="ltr"`.
201
203
 
204
+ - An auto-rotating set of readable parallel items uses `aria-live="off"` while
205
+ it is rotating and `aria-live="polite"` while it is stopped.
206
+
202
207
  ## Pre-Fix Checks for Non-Interactive Controls
203
208
 
204
209
  Before adding `role="button"`, `tabIndex`, and keyboard handlers to a non-native
@@ -9,10 +9,12 @@ Every such component must ship an on-stage **play/pause button**.
9
9
 
10
10
  - [Apply When](#apply-when)
11
11
  - [Required Contract](#required-contract)
12
+ - [Announce Manually Selected Parallel Content](#announce-manually-selected-parallel-content)
12
13
  - [Define Props](#1-define-props)
13
14
  - [Manage Playback State](#2-manage-playback-state)
14
15
  - [Add the Play/Pause Button](#3-add-the-playpause-button)
15
16
  - [Suppress Autoplay in `component.preview.tsx`](#4-suppress-autoplay-in-componentpreviewtsx)
17
+ - [Edit the generated `component.preview.tsx`](#edit-the-generated-componentpreviewtsx)
16
18
  - [Checklist](#checklist)
17
19
 
18
20
  ## Apply When
@@ -41,12 +43,26 @@ implementation detail.
41
43
  existing playback prop names and add only the missing safety contract.
42
44
  Carousels, sliders, slideshows, and galleries **must** define `autoPlay`
43
45
  (default `true`).
44
- 2. **Playback state** — `isPlaying` + `handlePause` / `handleResume`
46
+ 2. **Playback state** — play/pause toggle state, derived `isPlaying`, and
47
+ `handlePause` / `handleResume`
45
48
  3. **Play/pause button** — overlay `<button>` with inline SVG icon and CSS
46
49
  positioning; it must be a named part with `elementProps` wiring, a hover
47
50
  design state, and a standalone `:focus-visible` keyboard indicator
48
51
  4. **Modify `component.preview.tsx`** — suppress autoplay in editor design mode
49
- 5. **Respect `prefers-reduced-motion`** — suppress autoplay when the OS requests reduced motion
52
+ 5. **Respect `prefers-reduced-motion`** — start paused when the OS requests reduced motion
53
+
54
+ ### Announce Manually Selected Parallel Content
55
+
56
+ For carousel-like components that swap readable parallel items, make the item
57
+ wrapper a live region:
58
+
59
+ - Use `aria-live={isPlaying ? 'off' : 'polite'}` and `aria-atomic="false"`.
60
+ - `isPlaying = isPlayOn && !isHovered && !isStoppedByFocus`. Reduced motion
61
+ starts paused; editor mode, `autoPlay={false}`, and pause set it false.
62
+ Keyboard focus remains stopped until the rotation control restarts; hover
63
+ pauses temporarily.
64
+ - Previous/next changes items. Use root `onMouseEnter`/`onMouseLeave`;
65
+ leaving resumes unless focus has stopped rotation.
50
66
 
51
67
  ## 1. Define Props
52
68
 
@@ -83,40 +99,48 @@ support repeat behavior; do not add it only to match the example.
83
99
 
84
100
  ## 2. Manage Playback State
85
101
 
86
- Track play/pause state with `useState`. Initialize it from `autoPlay` and the OS reduced-motion preference — when the visitor has requested reduced motion, the animation starts paused:
102
+ Track play/pause button state with `useState`. Initialize it from `autoPlay`
103
+ and reduced motion; reduced motion starts paused, but a visitor can start it.
87
104
 
88
105
  ```tsx
89
106
  import { useReducedMotion } from '@wix/react-component-utils';
90
107
 
91
- // inside the component:
92
108
  const reducedMotion = useReducedMotion();
93
109
 
94
- const [isPlaying, setIsPlaying] = React.useState((autoPlay ?? true) && !reducedMotion);
95
-
96
- // when autoPlay prop changes, re-sync (but still respect reduced motion)
97
- React.useEffect(() => {
98
- setIsPlaying((autoPlay ?? true) && !reducedMotion);
99
- }, [autoPlay]);
110
+ const [isPlayOn, setIsPlayOn] = React.useState(() => (autoPlay ?? true) && !reducedMotion);
100
111
 
101
- // when reduced motion is enabled, suppress playback; never re-enable on its own
102
- // (visitor may have manually paused — don't restart against their will)
112
+ const skipInitialAutoPlaySync = React.useRef(true);
103
113
  React.useEffect(() => {
104
- if (reducedMotion) {
105
- setIsPlaying(false);
114
+ if (skipInitialAutoPlaySync.current) {
115
+ skipInitialAutoPlaySync.current = false;
116
+ return;
106
117
  }
107
- }, [reducedMotion]);
118
+ setIsPlayOn(autoPlay ?? true);
119
+ }, [autoPlay]);
108
120
 
109
- const handlePause = () => setIsPlaying(false);
110
- const handleResume = () => setIsPlaying(true);
121
+ const handlePause = () => setIsPlayOn(false);
122
+ const handleResume = () => setIsPlayOn(true);
111
123
  ```
112
124
 
113
- **`reducedMotion`** is `true` when the hook reports that reduced motion is enabled. When active, the animation starts paused — the visitor can still press the play button to start it manually. It is a runtime browser signal, not a manifest/data prop; never expose it as a component prop or in the manifest.
125
+ `reducedMotion` is a runtime signal, not a manifest/data prop.
126
+
127
+ Derive runtime playback from `isPlayOn` plus only needed conditions. Without
128
+ another condition, use `const isPlaying = isPlayOn`. A carousel, slideshow,
129
+ slider, or gallery also uses hover and focus-stop state:
130
+
131
+ ```tsx
132
+ const isPlaying = isPlayOn && !isHovered && !isStoppedByFocus;
133
+ ```
114
134
 
115
- Pass `isPlaying` to the animation renderer and toggle between `handlePause` / `handleResume` on button click.
135
+ Pass `isPlaying` to the renderer. The button uses `isPlayOn` and toggles
136
+ between `handlePause` / `handleResume` on click. In a carousel-like component,
137
+ focus entering anything except that button sets `isPlayOn` to `false` and
138
+ `isStoppedByFocus` to `true`; `handleResume` sets `isPlayOn` to `true` and
139
+ clears `isStoppedByFocus`.
116
140
 
117
141
  ## 3. Add the Play/Pause Button
118
142
 
119
- 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.
143
+ 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 and color of SVG should be controlled by CSS variables declared on the button level: --icon-size and --icon-color.
120
144
 
121
145
  Position the button absolutely so it overlays the content without pushing other elements out of place:
122
146
 
@@ -128,14 +152,14 @@ Position the button absolutely so it overlays the content without pushing other
128
152
  }
129
153
 
130
154
  .playButton {
155
+ --icon-size: 20px;
156
+ --icon-color: #ffffff;
131
157
  position: absolute;
132
158
  inset-inline-end: 5px;
133
159
  inset-block-start: 5px;
134
160
  }
135
161
 
136
- /* Design-state selectors: pair native pseudo-class with editor-injected modifier class.
137
- The editor applies the modifier class (e.g. my-animation-play-button--hover) when
138
- the site owner previews that state in the design panel, so both selectors must exist. */
162
+ /* Design-state selectors: pair native pseudo-class with editor-injected modifier class. */
139
163
  .playButton:global(.my-animation-play-button--hover),
140
164
  .playButton:hover {
141
165
  /* e.g. background: rgba(255, 255, 255, 1); */
@@ -177,12 +201,12 @@ Wire the named part completely:
177
201
  styles.playButton,
178
202
  elementProps?.playButton?.className,
179
203
  )}
180
- onClick={isPlaying ? handlePause : handleResume}
204
+ onClick={isPlayOn ? handlePause : handleResume}
181
205
  aria-label={
182
- isPlaying ? ARIA_LABELS.pauseButton : ARIA_LABELS.playButton
206
+ isPlayOn ? ARIA_LABELS.pauseButton : ARIA_LABELS.playButton
183
207
  }
184
208
  >
185
- {isPlaying ? <PauseIcon /> : <PlayIcon />}
209
+ {isPlayOn ? <PauseIcon /> : <PlayIcon />}
186
210
  </button>
187
211
  ```
188
212
 
@@ -233,8 +257,7 @@ const ComponentNamePreview: FC<ComponentProps<typeof Component>> = (props) => {
233
257
  };
234
258
  ```
235
259
 
236
- 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.
237
- In preview mode (`isEditMode` is `false`) → both use the user's configured values.
260
+ Design mode stops autoplay and exposes the control; preview uses the user's values.
238
261
 
239
262
  ## Checklist
240
263
 
@@ -243,6 +266,8 @@ In preview mode (`isEditMode` is `false`) → both use the user's configured val
243
266
  present only when repeat behavior is supported.
244
267
  - [ ] Reduced motion starts paused and never restarts playback automatically.
245
268
  - [ ] The play/pause button is a fully wired named part with a stable accessible name.
269
+ - [ ] Both icons inherit button-owned `--icon-size` for width and height and
270
+ `--icon-color` via `color` and `currentColor` for fill or stroke.
246
271
  - [ ] Hover has a paired editor design state; `:focus-visible` remains a
247
272
  standalone keyboard indicator.
248
273
  - [ ] Hover-only visibility changes behavior, not the button's editable styling surface.
@@ -33,6 +33,10 @@ export type PlanCardProps = {
33
33
  heading?: string;
34
34
  plans?: Array<Plan>;
35
35
  onClick?: (event: React.MouseEvent) => void;
36
+ onMouseIn?: (event: React.MouseEvent) => void;
37
+ onMouseOut?: (event: React.MouseEvent) => void;
38
+ onFocus?: (event: React.FocusEvent) => void;
39
+ onBlur?: (event: React.FocusEvent) => void;
36
40
 
37
41
  elementProps?: {
38
42
  cta?: { className?: string; href?: string };
@@ -47,9 +51,8 @@ Rules:
47
51
  code. Do not add one-off ARIA props or spread the whole object.
48
52
  - Expose only content and behavior that the site owner controls. Keep derived
49
53
  values internal.
50
- - Add only callbacks required by the component specification. Use supported SDK
51
- event names and types for public callbacks; keep implementation-only handlers
52
- internal.
54
+ - Default to common optional SDK callbacks by capability; add specialized ones
55
+ when requested. Keep implementation handlers internal.
53
56
  - Use `Array<T>`, not `T[]`, for exported arrays.
54
57
 
55
58
  ## Numeric Range Constraints
@@ -176,7 +179,16 @@ export const defaultProps = {
176
179
  ### Wix Data Types
177
180
 
178
181
  Use `Image`, `Link`, `Video`, `Audio`, `VectorArt`, `RichText` from
179
- `@wix/editor-react-types`. See `node_modules/@wix/react-component-schema/dist/editor-react-types.d.ts` for the full list.
182
+ `@wix/editor-react-types`. Model authored media with the corresponding Wix
183
+ media type, whether it is a top-level prop or a field in an array item. Name
184
+ the field for the media itself (`image`, `video`, `audio`, and so on), not for
185
+ one representation of it.
186
+
187
+ Do not represent media as a URL/source string or split its metadata across
188
+ primitive props. Preserve the media object through the public contract so the
189
+ component can consume all of its supported data. See
190
+ `node_modules/@wix/react-component-schema/dist/editor-react-types.d.ts` for the
191
+ full list.
180
192
 
181
193
  ## Defaults and Resources
182
194
 
@@ -242,5 +254,6 @@ Do not extract tiny fragments merely to satisfy a line-count threshold.
242
254
  - [ ] Named inner parts have `elementProps` wiring; leaf components avoid exported `children`.
243
255
  - [ ] One-body-visible arrays use the active-item contract and render all bodies.
244
256
  - [ ] Array elements are objects with semantic named fields. No separate `id` field added to item types; React keys use item fields (stable unique → slug → index), not a typed `id`.
257
+ - [ ] Authored media uses the corresponding Wix media type, not URL/source strings or flattened metadata.
245
258
  - [ ] Defaults live only in the props file (no JSX fallbacks).
246
259
  - [ ] Resources are Wix-hosted, prop-supplied, or locally bundled.
@@ -1,57 +1,92 @@
1
1
  # Function Handlers
2
2
 
3
- Use this reference only when the component specification exposes callbacks to
4
- site owners. Keep implementation-only handlers out of the public props type.
3
+ When creating a component or changing interactions, expose common optional
4
+ callbacks by capability, without requiring an explicit request.
5
+
6
+ ## Default Support
7
+
8
+ Apply defaults to the public interaction surface:
9
+
10
+ | Component capability | Default optional props |
11
+ | --- | --- |
12
+ | Pointer/click interaction, including native controls | `onClick`, `onMouseIn`, `onMouseOut` |
13
+ | Focusable control | `onFocus`, `onBlur` |
14
+ | Editable value, text, or selection | `onChange` |
15
+
16
+ Combine matching rows; leave decorative elements alone. Explicit API restrictions
17
+ override defaults. Specialized callbacks (`onDblClick`, `onPlay`, `onEnded`,
18
+ `onActivate`, etc.) require a request.
19
+
20
+ Classify composite controls by their interactive descendants. For example, a
21
+ radio-based rating input matches all three rows and exposes all six callbacks.
5
22
 
6
23
  ## Standard SDK Handlers
7
24
 
8
25
  Use the exact SDK prop name and wire it to the matching React DOM event.
9
26
 
10
- | SDK prop | React DOM prop | Type |
11
- | --- | --- | --- |
12
- | `onClick` | `onClick` | `(event: React.MouseEvent) => void` |
13
- | `onDblClick` | `onDoubleClick` | `(event: React.MouseEvent) => void` |
14
- | `onChange` | `onChange` | `(event: React.ChangeEvent<HTMLElement>) => void` |
15
- | `onFocus` | `onFocus` | `(event: React.FocusEvent) => void` |
16
- | `onBlur` | `onBlur` | `(event: React.FocusEvent) => void` |
17
- | `onMouseIn` | `onMouseEnter` | `(event: React.MouseEvent) => void` |
18
- | `onMouseOut` | `onMouseLeave` | `(event: React.MouseEvent) => void` |
19
-
20
- Declare only the handlers the component actually exposes.
27
+ | SDK prop | React DOM prop | Type | Zero config `domEvent` |
28
+ | --- | --- | --- | --- |
29
+ | `onClick` | `onClick` | `(event: React.MouseEvent) => void` | `POINTER` |
30
+ | `onDblClick` | `onDoubleClick` | `(event: React.MouseEvent) => void` | `POINTER` |
31
+ | `onChange` | `onChange` | `(event: React.ChangeEvent<HTMLElement>) => void` | `CHANGE` |
32
+ | `onFocus` | `onFocus` | `(event: React.FocusEvent) => void` | `FOCUS` |
33
+ | `onBlur` | `onBlur` | `(event: React.FocusEvent) => void` | `FOCUS` |
34
+ | `onMouseIn` | `onMouseEnter` | `(event: React.MouseEvent) => void` | `POINTER` |
35
+ | `onMouseOut` | `onMouseLeave` | `(event: React.MouseEvent) => void` | `POINTER` |
36
+
37
+ React `onChange` fires on live text edits and selection changes. Pass that
38
+ `ChangeEvent` directly. Do not synthesize a commit callback from blur or cast a
39
+ `FocusEvent` to `ChangeEvent`.
40
+
41
+ Callbacks only notify; they never enable controlled mode. Internal handlers run
42
+ component logic, then call the optional callback once, preserving event payloads
43
+ and disabled/read-only guards. Behavior must work without callbacks or consumer
44
+ prop updates. Forward directly when no internal logic is needed. Add controlled
45
+ state only when requested; preserve existing controlled APIs when editing.
21
46
 
22
47
  ```tsx
48
+ import * as React from 'react';
49
+
23
50
  export type ActionProps = {
24
51
  onClick?: (event: React.MouseEvent) => void;
25
- onDblClick?: (event: React.MouseEvent) => void;
26
52
  onMouseIn?: (event: React.MouseEvent) => void;
27
53
  onMouseOut?: (event: React.MouseEvent) => void;
54
+ onFocus?: (event: React.FocusEvent) => void;
55
+ onBlur?: (event: React.FocusEvent) => void;
28
56
  };
29
57
 
30
58
  export const Action: React.FC<ActionProps> = (props) => {
31
- const { onClick, onDblClick, onMouseIn, onMouseOut } = props;
59
+ const { onClick, onMouseIn, onMouseOut, onFocus, onBlur } = props;
60
+ const [count, setCount] = React.useState(0);
61
+
62
+ const handleClick = (event: React.MouseEvent) => {
63
+ setCount((value) => value + 1);
64
+ onClick?.(event);
65
+ };
32
66
 
33
67
  return (
34
68
  <button
35
69
  type="button"
36
- onClick={onClick}
37
- onDoubleClick={onDblClick}
70
+ onClick={handleClick}
38
71
  onMouseEnter={onMouseIn}
39
72
  onMouseLeave={onMouseOut}
40
- />
73
+ onFocus={onFocus}
74
+ onBlur={onBlur}
75
+ >
76
+ Clicked {count} times
77
+ </button>
41
78
  );
42
79
  };
43
80
  ```
44
81
 
45
- Do not expose DOM names such as `onDoubleClick`, `onMouseEnter`, or
46
- `onMouseLeave` in the component API; zero config recognizes the SDK names.
82
+ Zero config requires SDK names, not `onDoubleClick`, `onMouseEnter`, or
83
+ `onMouseLeave`, in public props.
47
84
 
48
85
  ## Custom Callbacks
49
86
 
50
- A component-level notification that is not one of the standard SDK DOM events
51
- uses `() => void` unless the component contract explicitly defines a serializable
52
- payload. Do not expose a React synthetic event for custom callbacks.
53
-
54
- Use this pattern:
87
+ Custom notifications use `() => void`; zero config emits no event parameter for
88
+ unrecognized callback names. Custom callbacks must not declare React event
89
+ parameters. Use a standard SDK handler above when the event is public:
55
90
 
56
91
  ```tsx
57
92
  export type VideoPlayerProps = {
@@ -62,14 +97,19 @@ export type VideoPlayerProps = {
62
97
 
63
98
  export const VideoPlayer: React.FC<VideoPlayerProps> = (props) => {
64
99
  const { onPlay, onPause, onEnded } = props;
65
- return <video onPlay={onPlay} onPause={onPause} onEnded={onEnded} />;
100
+ return (
101
+ <video
102
+ controls
103
+ onPlay={() => onPlay?.()}
104
+ onPause={() => onPause?.()}
105
+ onEnded={() => onEnded?.()}
106
+ />
107
+ );
66
108
  };
67
109
  ```
68
110
 
69
111
  ## Checklist
70
112
 
71
- - [ ] The specification explicitly requires each public callback.
72
- - [ ] Standard handler names match the SDK contract exactly.
73
- - [ ] Mismatched SDK/DOM names are mapped at the rendered element.
74
- - [ ] Custom callbacks do not leak React event objects.
75
- - [ ] Internal handlers remain implementation details.
113
+ - [ ] Defaults match capabilities; specialized callbacks are requested.
114
+ - [ ] SDK names, DOM mappings, and payloads match the contract.
115
+ - [ ] Internal behavior works independently and notifies callbacks once.
@@ -12,8 +12,7 @@ contracts changed by the request:
12
12
  2. Separate user-authored content and stable behavior from derived values and
13
13
  visual styling.
14
14
  3. Identify interaction and selectable states affected by the request.
15
- 4. Keep implementation behavior internal unless the component specification
16
- requests a public callback.
15
+ 4. Include common callbacks by capability; keep implementation handlers internal.
17
16
  5. Identify runtime concerns such as browser APIs, autoplay, editor-only
18
17
  behavior, or live site context before implementation.
19
18
 
@@ -49,8 +48,8 @@ export const ProfileCard: React.FC<ProfileCardProps> = (props) => {
49
48
  - When local state intentionally mirrors a prop, synchronize it when that prop
50
49
  changes. Do not use effect-managed state for a value that can be derived
51
50
  during render.
52
- - Keep implementation-only handlers internal. Expose a callback only when the
53
- specification makes it part of the component API.
51
+ - Internal handlers run component behavior, then notify optional callbacks.
52
+ Callback presence never enables controlled mode.
54
53
 
55
54
  ### Structure and Parts
56
55
 
@@ -87,6 +87,10 @@ additionalFees.provideHandlers({
87
87
  });
88
88
  ```
89
89
 
90
+ ## Manual Setup Required
91
+
92
+ None — confirmed live. Once the app is installed and released, Wix calls this plugin automatically on every cart/checkout calculation; no dashboard configuration or extra API call is needed to "turn it on."
93
+
90
94
  ## Key Implementation Notes
91
95
 
92
96
  1. **Elevate permissions for API calls** - Use `auth.elevate` from `@wix/essentials` when calling Wix APIs from service plugins
@@ -0,0 +1,118 @@
1
+ # Availability Time Slots Configuration Service Plugin Reference
2
+
3
+ ## Overview
4
+
5
+ The Availability Time Slots Configuration SPI lets you customize how Wix calculates available time slots for booking services: duration, split interval, buffer time, locations, and required/available resources. Wix calls your `listAvailabilityTimeSlotConfigurations` handler whenever a customer views available times or books a service. [Resources](https://dev.wix.com/docs/api-reference/business-solutions/bookings/resources/resources-v2/introduction) must already exist before time slot calculation runs.
6
+
7
+ ## ⚠️ `resourceTypeId` is not a placeholder you can invent
8
+
9
+ `resourceTypes[].resourceTypeId` must be a real resource type ID or Wix rejects every availability query for the service — confirmed live: a made-up GUID here 500s `List Availability Time Slots` and fails booking creation with `FAILED_VALIDATING_AVAILABILITY`, for every service on the site, not just the one you're configuring. If the service is staffed (the common case), use the universal Wix Bookings staff-member resource type ID, which is the same across every site — it is not something you provision or look up per site:
10
+
11
+ ```typescript
12
+ const STAFF_MEMBER_RESOURCE_TYPE_ID = '1cd44cf8-756f-41c3-bd90-3e2ffcaf1155';
13
+ ```
14
+
15
+ For a non-staff resource (a room, piece of equipment, etc.), get the real ID from [Get Resource](https://dev.wix.com/docs/api-reference/business-solutions/bookings/resources/resources-v2/get-resource) (`resource.typeId`) — never fabricate one.
16
+
17
+ ## ⚠️ `resourceTypes[].resourceIds` — populate it, don't leave it implicit
18
+
19
+ `resourceIds` ("IDs of resources available for booking within this resource type... may be a subset of all resources in the type") tells Wix *which* resources of that type are valid for this specific service. Omitting it (relying on `resourceTypeId` alone) was part of the same live 500 chain above — once `resourceTypeId` and `scheduleId` were both fixed, availability queries still failed until `resourceIds` was populated too. Get the service's actual assigned staff from [Get Service](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/services-v2/get-service)'s `service.staffMemberIds`.
20
+
21
+ ## ⚠️ `scheduleId` is required — omitting it also 500s
22
+
23
+ The response schema requires `scheduleId` (confirmed live: leaving it out 500s the same as a bad `resourceTypeId`), but it's easy to miss since it's not mentioned in the request/response summary above. Fetch it from the service itself — [Get Service](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/services-v2/get-service)'s `service.schedule._id` (REST docs call this field `id`; the SDK's `Service.schedule` type names it `_id`):
24
+
25
+ ```typescript
26
+ import { auth } from "@wix/essentials";
27
+ import { services } from "@wix/bookings";
28
+
29
+ const elevatedGetService = auth.elevate(services.getService);
30
+ const service = await elevatedGetService(serviceId);
31
+ const scheduleId = service.schedule?._id;
32
+ ```
33
+
34
+ ## ⚠️ `locations[]._id` is required when `locationType` is `BUSINESS`
35
+
36
+ The schema notes `_id` is "returned only for business locations" — read that as *required whenever `locationType: BUSINESS`*, not optional. Omitting it was the third and last piece of the same live 500 chain. Get the service's actual configured locations from [Get Service](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/services-v2/get-service)'s `service.locations[]` (each has its own `_id`) rather than hardcoding a bare `{ locationType: "BUSINESS" }`.
37
+
38
+ ## Manual Setup Required
39
+
40
+ None for activation — Wix calls this plugin automatically once installed and released. But get all three fields above right, or every availability query for every service on the site fails with a bare 500, not just a validation error on the one service you're configuring.
41
+
42
+ ## Request and Response Schema
43
+
44
+ Before implementing, call `ReadFullDocsMethodSchema` on the docs URL to get the full request/response types.
45
+
46
+ | Handler | Docs URL |
47
+ | --- | --- |
48
+ | `listAvailabilityTimeSlotConfigurations` | https://dev.wix.com/docs/api-reference/business-solutions/bookings/time-slots/availability-time-slots-configuration-service-plugin/list-availability-time-slot-configurations?apiView=SDK |
49
+
50
+ The request's `services` array (up to 100 entries) lists each service to configure, with the customer's `customerChoices` (selected duration variant and/or add-ons) — the same service GUID can appear more than once with different choices. Your response's `configurations` array must contain exactly one configuration per requested entry, **in the same order**, with `resourceTypes`, `locations`, and `scheduleId` fully populated.
51
+
52
+ For services with a bookable duration **range** (the customer picks how long to book), return `durationUnit` (`HOUR` or `DAY`) with `minDuration`/`maxDuration` instead of a fixed `duration`; for hourly ranges also return `duration.defaultInMinutes` as the minimum.
53
+
54
+ ## Example: Duration From Add-Ons, Single Resource Type
55
+
56
+ ```typescript
57
+ import { availabilityTimeSlotsConfiguration } from "@wix/bookings/service-plugins";
58
+ import { auth } from "@wix/essentials";
59
+ import { services } from "@wix/bookings";
60
+
61
+ // Universal Wix Bookings staff-member resource type ID — same on every site, not per-site data.
62
+ const STAFF_MEMBER_RESOURCE_TYPE_ID = "1cd44cf8-756f-41c3-bd90-3e2ffcaf1155";
63
+
64
+ availabilityTimeSlotsConfiguration.provideHandlers({
65
+ listAvailabilityTimeSlotConfigurations: async (payload) => {
66
+ const { request } = payload;
67
+ // services is optional on the request type — default it, or the .map()
68
+ // calls below fail `tsc` with "possibly undefined."
69
+ const { services: requestedServices = [] } = request;
70
+
71
+ // scheduleId is required in the response but isn't in the request — fetch it per service.
72
+ const elevatedGetService = auth.elevate(services.getService);
73
+ const serviceById = new Map(
74
+ await Promise.all(
75
+ requestedServices.map(async ({ serviceId }) =>
76
+ [serviceId, serviceId ? await elevatedGetService(serviceId) : undefined] as const
77
+ )
78
+ )
79
+ );
80
+
81
+ const configurations = requestedServices.map(({ serviceId, customerChoices }) => {
82
+ const baseDuration = 60;
83
+ const addOnMinutes = (customerChoices?.addOnIds ?? []).length * 15;
84
+ const service = serviceById.get(serviceId);
85
+
86
+ return {
87
+ serviceId,
88
+ resourceTypes: [
89
+ {
90
+ resourceTypeId: STAFF_MEMBER_RESOURCE_TYPE_ID,
91
+ resourceIds: service?.staffMemberIds ?? [],
92
+ numberOfResourcesRequired: 1,
93
+ },
94
+ ],
95
+ locations: (service?.locations ?? []).map((location) => ({
96
+ _id: location._id,
97
+ locationType: availabilityTimeSlotsConfiguration.LocationType.BUSINESS,
98
+ })),
99
+ splitIntervalInMinutes: baseDuration + addOnMinutes,
100
+ duration: { defaultInMinutes: baseDuration + addOnMinutes },
101
+ bufferTimeInMinutes: 10,
102
+ scheduleId: service?.schedule?._id,
103
+ };
104
+ });
105
+
106
+ return { configurations };
107
+ },
108
+ });
109
+ ```
110
+
111
+ ## Key Implementation Notes
112
+
113
+ 1. **One configuration per requested entry, same order** — the response array must line up positionally with the request's `services`, even when the same service GUID repeats with different `customerChoices`.
114
+ 2. **Prefer `addOnIds` over `durationInMinutes`** in the request — when both are present they must agree, so let Wix Bookings compute total duration from add-ons rather than passing a duration you'd have to keep in sync.
115
+ 3. **`splitIntervalInMinutes` vs `duration`** — when the split interval matches the service duration, `splitIntervalInMinutes` is `duration.defaultInMinutes + bufferTimeInMinutes`.
116
+ 4. **Duration ranges** — use `durationUnit` for services where the customer picks the length of the booking; leave `duration` empty for `DAY`-unit ranges.
117
+ 5. **Called on the hot path** — this handler runs on every availability query, so keep it fast.
118
+ 6. **`services` is optional on the request type** — default it to `[]` when destructuring, or the `.map()` calls fail `tsc` with "possibly undefined."