@wix/agent-skills 1.0.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 (220) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +111 -0
  3. package/package.json +29 -0
  4. package/skills/wix-app/SKILL.md +344 -0
  5. package/skills/wix-app/references/APP_IDENTIFIERS.md +35 -0
  6. package/skills/wix-app/references/APP_MARKET_REVIEW.md +311 -0
  7. package/skills/wix-app/references/APP_VALIDATION.md +171 -0
  8. package/skills/wix-app/references/BACKEND_API.md +233 -0
  9. package/skills/wix-app/references/BACKEND_EVENT.md +116 -0
  10. package/skills/wix-app/references/CODE_QUALITY.md +33 -0
  11. package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +587 -0
  12. package/skills/wix-app/references/DASHBOARD_MENU_PLUGIN.md +157 -0
  13. package/skills/wix-app/references/DASHBOARD_MODAL.md +283 -0
  14. package/skills/wix-app/references/DASHBOARD_PAGE.md +403 -0
  15. package/skills/wix-app/references/DASHBOARD_PLUGIN.md +205 -0
  16. package/skills/wix-app/references/DATA_COLLECTION.md +451 -0
  17. package/skills/wix-app/references/DOCUMENTATION.md +42 -0
  18. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +65 -0
  19. package/skills/wix-app/references/EMBEDDED_SCRIPT.md +468 -0
  20. package/skills/wix-app/references/EXTENSION_REGISTRATION.md +96 -0
  21. package/skills/wix-app/references/SERVICE_PLUGIN.md +235 -0
  22. package/skills/wix-app/references/SITE_PLUGIN.md +436 -0
  23. package/skills/wix-app/references/STORES_VERSIONING.md +334 -0
  24. package/skills/wix-app/references/backend-event/COMMON-EVENTS.md +84 -0
  25. package/skills/wix-app/references/custom-element-widget/SETTINGS_PANEL.md +345 -0
  26. package/skills/wix-app/references/dashboard-menu-plugin/blog-slots.md +71 -0
  27. package/skills/wix-app/references/dashboard-menu-plugin/bookings-slots.md +58 -0
  28. package/skills/wix-app/references/dashboard-menu-plugin/crm-slots.md +8 -0
  29. package/skills/wix-app/references/dashboard-menu-plugin/ecommerce-slots.md +17 -0
  30. package/skills/wix-app/references/dashboard-menu-plugin/events-slots.md +29 -0
  31. package/skills/wix-app/references/dashboard-menu-plugin/restaurants-slots.md +27 -0
  32. package/skills/wix-app/references/dashboard-menu-plugin/stores-slots.md +35 -0
  33. package/skills/wix-app/references/dashboard-page/API_SPEC.md +138 -0
  34. package/skills/wix-app/references/dashboard-page/DASHBOARD_API.md +573 -0
  35. package/skills/wix-app/references/dashboard-page/DYNAMIC_PARAMETERS.md +269 -0
  36. package/skills/wix-app/references/dashboard-page/ECOM_NAVIGATION.md +164 -0
  37. package/skills/wix-app/references/dashboard-plugin/SLOTS.md +294 -0
  38. package/skills/wix-app/references/data-collection/WIX_DATA.md +270 -0
  39. package/skills/wix-app/references/data-collection/extension-template.ts +107 -0
  40. package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +138 -0
  41. package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +454 -0
  42. package/skills/wix-app/references/editor-react-component/COMPONENT-CONFIGURATION.md +165 -0
  43. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +576 -0
  44. package/skills/wix-app/references/editor-react-component/DIRECTIONALITY.md +170 -0
  45. package/skills/wix-app/references/editor-react-component/EDIT-FLOW.md +6 -0
  46. package/skills/wix-app/references/editor-react-component/PARTS.md +25 -0
  47. package/skills/wix-app/references/editor-react-component/PROPS-VS-CSS.md +203 -0
  48. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +167 -0
  49. package/skills/wix-app/references/editor-react-component/REACT-PATTERNS.md +137 -0
  50. package/skills/wix-app/references/service-plugin/ADDITIONAL-FEES.md +112 -0
  51. package/skills/wix-app/references/service-plugin/BOOKINGS-STAFF-SORTING.md +118 -0
  52. package/skills/wix-app/references/service-plugin/DISCOUNT-TRIGGERS.md +87 -0
  53. package/skills/wix-app/references/service-plugin/GIFT-CARDS.md +86 -0
  54. package/skills/wix-app/references/service-plugin/PAYMENT-SETTINGS.md +135 -0
  55. package/skills/wix-app/references/service-plugin/SHIPPING-RATES.md +79 -0
  56. package/skills/wix-app/references/service-plugin/VALIDATIONS.md +68 -0
  57. package/skills/wix-app/references/site-plugin/EXAMPLES.md +311 -0
  58. package/skills/wix-app/references/site-plugin/SLOTS.md +263 -0
  59. package/skills/wix-design-system/SKILL.md +129 -0
  60. package/skills/wix-design-system/references/file-structure.md +56 -0
  61. package/skills/wix-design-system/scripts/wds.cjs +531 -0
  62. package/skills/wix-headless/SKILL.md +418 -0
  63. package/skills/wix-headless/references/DISCOVERY.md +332 -0
  64. package/skills/wix-headless/references/ORCHESTRATION.md +269 -0
  65. package/skills/wix-headless/references/SETUP.md +154 -0
  66. package/skills/wix-headless/references/blog/BLOG_CONTENT.md +152 -0
  67. package/skills/wix-headless/references/blog/BLOG_PAGES.md +299 -0
  68. package/skills/wix-headless/references/blog/BLOG_SETUP.md +218 -0
  69. package/skills/wix-headless/references/blog/INSTRUCTIONS.md +32 -0
  70. package/skills/wix-headless/references/cms/CMS_FOUNDATIONS.md +373 -0
  71. package/skills/wix-headless/references/cms/FAQ_KNOWLEDGE_BASE.md +229 -0
  72. package/skills/wix-headless/references/cms/INSTRUCTIONS.md +40 -0
  73. package/skills/wix-headless/references/cms/PORTFOLIO.md +454 -0
  74. package/skills/wix-headless/references/cms/RESOURCE_LIBRARY.md +356 -0
  75. package/skills/wix-headless/references/cms/TEAM_DIRECTORY.md +299 -0
  76. package/skills/wix-headless/references/commands/install-app.md +42 -0
  77. package/skills/wix-headless/references/commands/known-apps.json +5 -0
  78. package/skills/wix-headless/references/commands/mcp-bootstrap.md +28 -0
  79. package/skills/wix-headless/references/designer/INSTRUCTIONS.md +828 -0
  80. package/skills/wix-headless/references/ecom/CART_PAGES.md +120 -0
  81. package/skills/wix-headless/references/ecom/CART_WIRING.md +103 -0
  82. package/skills/wix-headless/references/ecom/COMPONENTS_CSS.md +132 -0
  83. package/skills/wix-headless/references/ecom/INSTRUCTIONS.md +59 -0
  84. package/skills/wix-headless/references/forms/CONTACT_FORM.md +477 -0
  85. package/skills/wix-headless/references/forms/INSTRUCTIONS.md +43 -0
  86. package/skills/wix-headless/references/gift-cards/COMPONENTS.md +119 -0
  87. package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +51 -0
  88. package/skills/wix-headless/references/gift-cards/PAGES.md +129 -0
  89. package/skills/wix-headless/references/images/INSTRUCTIONS.md +359 -0
  90. package/skills/wix-headless/references/shared/BUILD_NOISE.md +8 -0
  91. package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +185 -0
  92. package/skills/wix-headless/references/shared/IMPLEMENTER.md +155 -0
  93. package/skills/wix-headless/references/shared/MCP_PREFIX.md +105 -0
  94. package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +97 -0
  95. package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +423 -0
  96. package/skills/wix-headless/references/shared/STYLING.md +117 -0
  97. package/skills/wix-headless/references/stores/BACK_IN_STOCK.md +178 -0
  98. package/skills/wix-headless/references/stores/CATEGORY_PAGES.md +86 -0
  99. package/skills/wix-headless/references/stores/COMPONENTS_CSS.md +129 -0
  100. package/skills/wix-headless/references/stores/HOME_AND_NAV.md +227 -0
  101. package/skills/wix-headless/references/stores/INSTRUCTIONS.md +96 -0
  102. package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +249 -0
  103. package/skills/wix-headless/references/stores/PRODUCT_PAGES.md +213 -0
  104. package/skills/wix-headless/references/stores/SHARED_WIRING.md +164 -0
  105. package/skills/wix-headless/references/verticals/_schema.json +160 -0
  106. package/skills/wix-headless/references/verticals/_schema.md +185 -0
  107. package/skills/wix-headless/references/verticals/blog.md +125 -0
  108. package/skills/wix-headless/references/verticals/cms.md +121 -0
  109. package/skills/wix-headless/references/verticals/ecom.md +96 -0
  110. package/skills/wix-headless/references/verticals/forms.md +106 -0
  111. package/skills/wix-headless/references/verticals/gift-cards.md +121 -0
  112. package/skills/wix-headless/references/verticals/stores.md +167 -0
  113. package/skills/wix-headless/scripts/check-manifest.mjs +198 -0
  114. package/skills/wix-headless/scripts/copy-utility-templates.mjs +153 -0
  115. package/skills/wix-headless/scripts/emit-design-tokens.mjs +77 -0
  116. package/skills/wix-headless/scripts/finalize-run-json.mjs +141 -0
  117. package/skills/wix-headless/scripts/init-site-json.mjs +63 -0
  118. package/skills/wix-headless/scripts/patch-decorative-slots.mjs +124 -0
  119. package/skills/wix-headless/scripts/plan-entity-image-waves.mjs +190 -0
  120. package/skills/wix-headless/scripts/preview.sh +45 -0
  121. package/skills/wix-headless/scripts/release.sh +73 -0
  122. package/skills/wix-headless/scripts/scaffold.sh +53 -0
  123. package/skills/wix-headless/scripts/seed-utilities.sh +55 -0
  124. package/skills/wix-headless/shared-utilities/analytics.ts +40 -0
  125. package/skills/wix-headless/shared-utilities/ricos.ts +179 -0
  126. package/skills/wix-headless/shared-utilities/wix-image.ts +72 -0
  127. package/skills/wix-headless/templates/ecom/CartBadge.tsx +84 -0
  128. package/skills/wix-headless/templates/ecom/CartView.tsx +543 -0
  129. package/skills/wix-headless/templates/ecom/cart.astro +13 -0
  130. package/skills/wix-headless/templates/ecom/components-ecom.css +215 -0
  131. package/skills/wix-headless/templates/ecom/discounts.ts +71 -0
  132. package/skills/wix-headless/templates/ecom/thank-you.astro +24 -0
  133. package/skills/wix-headless/templates/gift-cards/GiftCardPurchase.tsx +172 -0
  134. package/skills/wix-headless/templates/gift-cards/_home-teaser-snippet.astro +42 -0
  135. package/skills/wix-headless/templates/gift-cards/_nav-snippet.astro +11 -0
  136. package/skills/wix-headless/templates/gift-cards/components-gift-cards.css +219 -0
  137. package/skills/wix-headless/templates/gift-cards/gift-cards.astro +51 -0
  138. package/skills/wix-headless/templates/gift-cards/gift-cards.ts +75 -0
  139. package/skills/wix-headless/templates/stores/AddToCartButton.tsx +77 -0
  140. package/skills/wix-headless/templates/stores/BackInStockForm.tsx +176 -0
  141. package/skills/wix-headless/templates/stores/CategoryRail.astro +69 -0
  142. package/skills/wix-headless/templates/stores/ProductCard.astro +79 -0
  143. package/skills/wix-headless/templates/stores/ProductPurchase.tsx +397 -0
  144. package/skills/wix-headless/templates/stores/SeoTags.astro +21 -0
  145. package/skills/wix-headless/templates/stores/back-in-stock.ts +67 -0
  146. package/skills/wix-headless/templates/stores/categories.ts +155 -0
  147. package/skills/wix-headless/templates/stores/category/[slug].astro +130 -0
  148. package/skills/wix-headless/templates/stores/components-stores.css +295 -0
  149. package/skills/wix-headless/templates/stores/products/[slug].astro +128 -0
  150. package/skills/wix-headless/templates/stores/products/index.astro +101 -0
  151. package/skills/wix-manage/SKILL.md +280 -0
  152. package/skills/wix-manage/references/app-installation/install-wix-apps.md +155 -0
  153. package/skills/wix-manage/references/app-installation/list-installed-apps.md +124 -0
  154. package/skills/wix-manage/references/blog/how-to-create-blog-posts.md +226 -0
  155. package/skills/wix-manage/references/bookings/booking-service-policy-setup.md +95 -0
  156. package/skills/wix-manage/references/bookings/booking-system-integration-gaps.md +416 -0
  157. package/skills/wix-manage/references/bookings/bookings-staff-setup.md +255 -0
  158. package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +462 -0
  159. package/skills/wix-manage/references/bookings/end-to-end-booking-flow.md +278 -0
  160. package/skills/wix-manage/references/bookings/external-calendar-integration.md +258 -0
  161. package/skills/wix-manage/references/bookings/multi-resource-service-creation.md +231 -0
  162. package/skills/wix-manage/references/calendar/configure-default-business-hours.md +226 -0
  163. package/skills/wix-manage/references/cms/cms-data-items-crud.md +422 -0
  164. package/skills/wix-manage/references/cms/cms-data-operations-extended.md +263 -0
  165. package/skills/wix-manage/references/cms/cms-ecommerce-catalog-integration.md +136 -0
  166. package/skills/wix-manage/references/cms/cms-references-and-relationships.md +160 -0
  167. package/skills/wix-manage/references/cms/cms-schema-management.md +143 -0
  168. package/skills/wix-manage/references/contacts/bulk-delete-contacts.md +73 -0
  169. package/skills/wix-manage/references/contacts/bulk-label-and-unlabel-contacts.md +67 -0
  170. package/skills/wix-manage/references/domains/domain-search-and-purchase.md +434 -0
  171. package/skills/wix-manage/references/ecommerce/api-recommendation-tracking.md +420 -0
  172. package/skills/wix-manage/references/ecommerce/flow-add-free-shipping.md +267 -0
  173. package/skills/wix-manage/references/ecommerce/flow-bundle-and-save.md +290 -0
  174. package/skills/wix-manage/references/ecommerce/flow-fix-coverage-gaps.md +300 -0
  175. package/skills/wix-manage/references/ecommerce/flow-optimize-shipping-rates.md +364 -0
  176. package/skills/wix-manage/references/ecommerce/flow-seasonal-promotion.md +315 -0
  177. package/skills/wix-manage/references/ecommerce/flow-stock-mover.md +308 -0
  178. package/skills/wix-manage/references/ecommerce/flow-upsell-boost.md +271 -0
  179. package/skills/wix-manage/references/ecommerce/goal-clear-inventory.md +118 -0
  180. package/skills/wix-manage/references/ecommerce/goal-drive-cross-sells.md +122 -0
  181. package/skills/wix-manage/references/ecommerce/goal-increase-aov.md +132 -0
  182. package/skills/wix-manage/references/ecommerce/goal-reduce-cart-abandonment.md +148 -0
  183. package/skills/wix-manage/references/ecommerce/goal-seasonal-revenue.md +146 -0
  184. package/skills/wix-manage/references/ecommerce/guardrail-discount-conflicts.md +118 -0
  185. package/skills/wix-manage/references/ecommerce/guardrail-margin-protection.md +88 -0
  186. package/skills/wix-manage/references/ecommerce/guardrail-rate-pricing-sanity.md +108 -0
  187. package/skills/wix-manage/references/ecommerce/guardrail-shipping-health.md +124 -0
  188. package/skills/wix-manage/references/ecommerce/recipe-apply-shipping-recommendations.md +299 -0
  189. package/skills/wix-manage/references/ecommerce/recommend-ecommerce-strategy.md +582 -0
  190. package/skills/wix-manage/references/ecommerce/setup-coupons.md +338 -0
  191. package/skills/wix-manage/references/ecommerce/setup-discount-rules.md +444 -0
  192. package/skills/wix-manage/references/ecommerce/setup-shipping-rates.md +86 -0
  193. package/skills/wix-manage/references/ecommerce/setup-shipping-regions.md +56 -0
  194. package/skills/wix-manage/references/ecommerce/setup-store-pickup-location.md +209 -0
  195. package/skills/wix-manage/references/ecommerce/skill-graph.md +139 -0
  196. package/skills/wix-manage/references/ecommerce/troubleshoot-checkout-delivery-dropoff.md +124 -0
  197. package/skills/wix-manage/references/ecommerce/troubleshoot-discount-not-applying.md +95 -0
  198. package/skills/wix-manage/references/events/list-events.md +203 -0
  199. package/skills/wix-manage/references/forms/create-form.md +260 -0
  200. package/skills/wix-manage/references/get-paid/create-payment-links.md +212 -0
  201. package/skills/wix-manage/references/get-paid/how-to-setup-wix-payments.md +57 -0
  202. package/skills/wix-manage/references/get-paid/payment-links-for-bookings.md +101 -0
  203. package/skills/wix-manage/references/media/upload-media-to-wix.md +216 -0
  204. package/skills/wix-manage/references/pricing-plans/create-and-update-pricing-plans.md +77 -0
  205. package/skills/wix-manage/references/pricing-plans/pricing-plans-bookings-integration.md +166 -0
  206. package/skills/wix-manage/references/restaurants/wix-restaurants-setup.md +336 -0
  207. package/skills/wix-manage/references/rich-content/ricos-converter-service.md +295 -0
  208. package/skills/wix-manage/references/site-properties/change-payment-currency-site-properties.md +59 -0
  209. package/skills/wix-manage/references/sites/create-site-from-template.md +183 -0
  210. package/skills/wix-manage/references/sites/query-sites.md +125 -0
  211. package/skills/wix-manage/references/stores/add-store-pages-to-site.md +57 -0
  212. package/skills/wix-manage/references/stores/bulk-create-products-with-options.md +1041 -0
  213. package/skills/wix-manage/references/stores/create-product-catalog-v1.md +125 -0
  214. package/skills/wix-manage/references/stores/create-product-from-image.md +824 -0
  215. package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +566 -0
  216. package/skills/wix-manage/references/stores/find-products-query-and-search-catalog-v3.md +194 -0
  217. package/skills/wix-manage/references/stores/query-products-catalog-v1.md +80 -0
  218. package/skills/wix-manage/references/stores/setup-online-store-catalog-v3.md +99 -0
  219. package/skills/wix-manage/references/stores/update-product-pre-order.md +213 -0
  220. package/skills/wix-manage/references/stores/update-product-with-options.md +318 -0
@@ -0,0 +1,179 @@
1
+ /**
2
+ * SSR-friendly Ricos JSON to HTML renderer.
3
+ *
4
+ * Ricos is Wix's structured rich-text format. CMS rich-text fields (about-content
5
+ * body, FAQ answer, etc.) are stored as a node tree, not HTML strings. The Wix
6
+ * dashboard's rich-text editor reads and writes this format.
7
+ *
8
+ * Use this util on Astro SSR pages that need to render Ricos content as HTML
9
+ * without pulling the full @wix/ricos React package (~80kb) into a static page.
10
+ * The blog vertical uses @wix/ricos's RicosViewer React component because blog
11
+ * posts can carry the full Ricos feature set (galleries, polls, embedded media).
12
+ * CMS pages render a much narrower subset, so this walker covers about 80% of
13
+ * what the dashboard editor produces:
14
+ *
15
+ * - PARAGRAPH, HEADING (levels 1–6), BULLETED_LIST, ORDERED_LIST, LIST_ITEM
16
+ * - BLOCKQUOTE, DIVIDER
17
+ * - TEXT decorations: BOLD, ITALIC, UNDERLINE, LINK
18
+ *
19
+ * Anything outside this set is rendered as a defensive `<p>` with the raw
20
+ * `textData.text` (or empty string), so unknown nodes never throw — they
21
+ * just render plainly. Add cases as the CMS surface grows.
22
+ *
23
+ * Usage:
24
+ * import { renderRicos } from "../utils/ricos";
25
+ * ---
26
+ * const html = renderRicos(item.body);
27
+ * ---
28
+ * <div class="prose" set:html={html} />
29
+ */
30
+
31
+ type RicosTextDecoration =
32
+ | { type: "BOLD" }
33
+ | { type: "ITALIC" }
34
+ | { type: "UNDERLINE" }
35
+ | { type: "LINK"; linkData?: { link?: { url?: string; target?: string; rel?: string } } }
36
+ | { type: string; [key: string]: unknown };
37
+
38
+ interface RicosTextData {
39
+ text?: string;
40
+ decorations?: RicosTextDecoration[];
41
+ }
42
+
43
+ interface RicosNode {
44
+ type?: string;
45
+ nodes?: RicosNode[];
46
+ textData?: RicosTextData;
47
+ headingData?: { level?: number };
48
+ [key: string]: unknown;
49
+ }
50
+
51
+ interface RicosDocument {
52
+ nodes?: RicosNode[];
53
+ [key: string]: unknown;
54
+ }
55
+
56
+ /**
57
+ * Render a Ricos document (or a JSON-encoded Ricos string) as HTML.
58
+ * Returns an empty string on null/undefined input — pages can `set:html=""`
59
+ * without throwing.
60
+ */
61
+ export function renderRicos(
62
+ content: RicosDocument | string | null | undefined,
63
+ ): string {
64
+ if (!content) return "";
65
+
66
+ const doc = typeof content === "string" ? safeParse(content) : content;
67
+ if (!doc || !Array.isArray(doc.nodes)) return "";
68
+
69
+ return doc.nodes.map(renderBlockNode).join("");
70
+ }
71
+
72
+ function safeParse(raw: string): RicosDocument | null {
73
+ try {
74
+ const parsed = JSON.parse(raw);
75
+ if (parsed && typeof parsed === "object") return parsed as RicosDocument;
76
+ return null;
77
+ } catch {
78
+ return null;
79
+ }
80
+ }
81
+
82
+ function renderBlockNode(node: RicosNode): string {
83
+ switch (node.type) {
84
+ case "PARAGRAPH":
85
+ return `<p>${renderInlineChildren(node)}</p>`;
86
+
87
+ case "HEADING": {
88
+ const level = clampHeadingLevel(node.headingData?.level);
89
+ return `<h${level}>${renderInlineChildren(node)}</h${level}>`;
90
+ }
91
+
92
+ case "BULLETED_LIST":
93
+ return `<ul>${(node.nodes ?? []).map(renderBlockNode).join("")}</ul>`;
94
+
95
+ case "ORDERED_LIST":
96
+ return `<ol>${(node.nodes ?? []).map(renderBlockNode).join("")}</ol>`;
97
+
98
+ case "LIST_ITEM":
99
+ return `<li>${(node.nodes ?? []).map(renderBlockNode).join("")}</li>`;
100
+
101
+ case "BLOCKQUOTE":
102
+ return `<blockquote>${(node.nodes ?? []).map(renderBlockNode).join("")}</blockquote>`;
103
+
104
+ case "DIVIDER":
105
+ return "<hr />";
106
+
107
+ default:
108
+ return `<p>${renderInlineChildren(node)}</p>`;
109
+ }
110
+ }
111
+
112
+ function renderInlineChildren(node: RicosNode): string {
113
+ if (!Array.isArray(node.nodes)) return "";
114
+ return node.nodes.map(renderInlineNode).join("");
115
+ }
116
+
117
+ function renderInlineNode(node: RicosNode): string {
118
+ if (node.type !== "TEXT") {
119
+ if (Array.isArray(node.nodes)) return renderInlineChildren(node);
120
+ return "";
121
+ }
122
+
123
+ const text = escapeHtml(node.textData?.text ?? "");
124
+ const decorations = node.textData?.decorations ?? [];
125
+
126
+ let html = text;
127
+ let linkOpen = "";
128
+ let linkClose = "";
129
+
130
+ for (const decoration of decorations) {
131
+ switch (decoration.type) {
132
+ case "BOLD":
133
+ html = `<strong>${html}</strong>`;
134
+ break;
135
+ case "ITALIC":
136
+ html = `<em>${html}</em>`;
137
+ break;
138
+ case "UNDERLINE":
139
+ html = `<u>${html}</u>`;
140
+ break;
141
+ case "LINK": {
142
+ const url = decoration.linkData?.link?.url;
143
+ if (url) {
144
+ const target = decoration.linkData?.link?.target;
145
+ const rel = decoration.linkData?.link?.rel ?? (target === "_blank" ? "noopener noreferrer" : undefined);
146
+ linkOpen = `<a href="${escapeAttr(url)}"${target ? ` target="${escapeAttr(target)}"` : ""}${rel ? ` rel="${escapeAttr(rel)}"` : ""}>`;
147
+ linkClose = "</a>";
148
+ }
149
+ break;
150
+ }
151
+ default:
152
+ break;
153
+ }
154
+ }
155
+
156
+ return `${linkOpen}${html}${linkClose}`;
157
+ }
158
+
159
+ function clampHeadingLevel(raw: number | undefined): 1 | 2 | 3 | 4 | 5 | 6 {
160
+ if (typeof raw !== "number" || !Number.isFinite(raw)) return 2;
161
+ const n = Math.floor(raw);
162
+ if (n < 1) return 1;
163
+ if (n > 6) return 6;
164
+ return n as 1 | 2 | 3 | 4 | 5 | 6;
165
+ }
166
+
167
+ function escapeHtml(s: string): string {
168
+ return s
169
+ .replace(/&/g, "&amp;")
170
+ .replace(/</g, "&lt;")
171
+ .replace(/>/g, "&gt;");
172
+ }
173
+
174
+ function escapeAttr(s: string): string {
175
+ return s
176
+ .replace(/&/g, "&amp;")
177
+ .replace(/"/g, "&quot;")
178
+ .replace(/</g, "&lt;");
179
+ }
@@ -0,0 +1,72 @@
1
+ import { media } from "@wix/sdk";
2
+
3
+ export type WixImage = {
4
+ url: string;
5
+ altText?: string;
6
+ width?: number;
7
+ height?: number;
8
+ };
9
+
10
+ export function resolveWixImage(
11
+ image: string | { id?: string; url?: string } | undefined
12
+ ): WixImage | null {
13
+ if (!image) return null;
14
+
15
+ if (typeof image === "string") {
16
+ if (image.startsWith("wix:image://")) {
17
+ const resolved = media.getImageUrl(image);
18
+ return { url: resolved.url, width: resolved.width, height: resolved.height };
19
+ }
20
+ return { url: image };
21
+ }
22
+
23
+ if (image.url) {
24
+ return { url: image.url };
25
+ }
26
+
27
+ if (image.id) {
28
+ const resolved = media.getImageUrl(image.id);
29
+ return { url: resolved.url, width: resolved.width, height: resolved.height };
30
+ }
31
+
32
+ return null;
33
+ }
34
+
35
+ export function scaledSrc(img: WixImage, width: number): string {
36
+ const url = new URL(img.url);
37
+ url.searchParams.set("w", String(width));
38
+ return url.toString();
39
+ }
40
+
41
+ export function srcSet(img: WixImage, widths: number[]): string {
42
+ return widths.map((w) => `${scaledSrc(img, w)} ${w}w`).join(", ");
43
+ }
44
+
45
+ // Consumed by every vertical that renders a Wix image at a specific size
46
+ // (stores ProductCard + detail page, blog post hero, cms list thumbnails).
47
+ // Kept alongside resolveWixImage so verticals do not ship their own copy.
48
+ export function resolveWixImageUrl(
49
+ image: string | { id?: string; url?: string } | undefined,
50
+ width?: number,
51
+ height?: number,
52
+ ): string | null {
53
+ if (!image) return null;
54
+
55
+ if (
56
+ typeof image === "string" &&
57
+ image.startsWith("wix:image://") &&
58
+ width != null &&
59
+ height != null
60
+ ) {
61
+ return media.getScaledToFillImageUrl(image, width, height, {});
62
+ }
63
+
64
+ const resolved = resolveWixImage(image);
65
+ if (!resolved) return null;
66
+ if (width == null && height == null) return resolved.url;
67
+
68
+ const url = new URL(resolved.url);
69
+ if (width != null) url.searchParams.set("w", String(width));
70
+ if (height != null) url.searchParams.set("h", String(height));
71
+ return url.toString();
72
+ }
@@ -0,0 +1,84 @@
1
+ import { useState, useEffect } from "react";
2
+ import { currentCart } from "@wix/ecom";
3
+
4
+ // CartView writes { lineItems, summary } to sessionStorage on every successful
5
+ // fetch/update. Read the snapshot here so the badge count renders instantly
6
+ // on every navigation instead of flashing empty while the fresh fetch runs.
7
+ // Same key as CartView; CartBadge is a read-only consumer.
8
+ const CART_CACHE_KEY = "cart:last-snapshot";
9
+
10
+ function readCachedCount(): number {
11
+ if (typeof window === "undefined") return 0;
12
+ try {
13
+ const raw = window.sessionStorage.getItem(CART_CACHE_KEY);
14
+ if (!raw) return 0;
15
+ const parsed = JSON.parse(raw);
16
+ const items = Array.isArray(parsed?.lineItems) ? parsed.lineItems : [];
17
+ return items.reduce(
18
+ (sum: number, item: any) => sum + (item?.quantity ?? 0),
19
+ 0,
20
+ );
21
+ } catch {
22
+ return 0;
23
+ }
24
+ }
25
+
26
+ export default function CartBadge() {
27
+ const [count, setCount] = useState(() => readCachedCount());
28
+
29
+ const fetchCount = async () => {
30
+ try {
31
+ const cart = await currentCart.getCurrentCart();
32
+ const total = (cart.lineItems ?? []).reduce(
33
+ (sum, item) => sum + (item.quantity ?? 0),
34
+ 0
35
+ );
36
+ setCount(total);
37
+ } catch {
38
+ setCount(0);
39
+ }
40
+ };
41
+
42
+ useEffect(() => {
43
+ // Always fetch client-side — cart count is per-visitor and must not
44
+ // be server-rendered (would break SSR caching)
45
+ fetchCount();
46
+
47
+ const handleCartUpdate = (e: Event) => {
48
+ const detail = (e as CustomEvent).detail;
49
+ if (detail?.cart?.lineItems) {
50
+ const total = (detail.cart.lineItems as any[]).reduce(
51
+ (sum: number, item: any) => sum + (item.quantity ?? 0),
52
+ 0
53
+ );
54
+ setCount(total);
55
+ } else if (detail?.delta) {
56
+ setCount((prev) => prev + detail.delta);
57
+ } else {
58
+ fetchCount();
59
+ }
60
+ };
61
+ window.addEventListener("cart-updated", handleCartUpdate);
62
+ return () => window.removeEventListener("cart-updated", handleCartUpdate);
63
+ }, []);
64
+
65
+ return (
66
+ <a href="/cart" className="cart-badge" aria-label={count > 0 ? `Cart, ${count} items` : "Cart"}>
67
+ <svg
68
+ className="cart-badge-icon"
69
+ viewBox="0 0 24 24"
70
+ fill="none"
71
+ stroke="currentColor"
72
+ strokeWidth="1.6"
73
+ strokeLinecap="round"
74
+ strokeLinejoin="round"
75
+ aria-hidden="true"
76
+ focusable="false"
77
+ >
78
+ <path d="M5 8h14l-1.2 10.2a2 2 0 0 1-2 1.8H8.2a2 2 0 0 1-2-1.8L5 8Z" />
79
+ <path d="M9 8V6a3 3 0 0 1 6 0v2" />
80
+ </svg>
81
+ {count > 0 && <span className="cart-badge-count">{count}</span>}
82
+ </a>
83
+ );
84
+ }