@burdenoff/microfe-coolandlovely 2026.1031.2 → 2026.1101.2

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 (141) hide show
  1. package/dist/coolandlovely/CoolAndLovelyRoot.d.ts.map +1 -1
  2. package/dist/coolandlovely/CoolAndLovelyRoot.js +16 -4
  3. package/dist/coolandlovely/CoolAndLovelyRoot.js.map +1 -1
  4. package/dist/coolandlovely/CoolAndLovelyRoutes.d.ts.map +1 -1
  5. package/dist/coolandlovely/CoolAndLovelyRoutes.js +19 -15
  6. package/dist/coolandlovely/CoolAndLovelyRoutes.js.map +1 -1
  7. package/dist/coolandlovely/components/ActivityTrail.js +1 -1
  8. package/dist/coolandlovely/components/ActivityTrail.js.map +1 -1
  9. package/dist/coolandlovely/components/ActorName.js.map +1 -1
  10. package/dist/coolandlovely/components/AddToCartPanel.js.map +1 -1
  11. package/dist/coolandlovely/components/AddToLookbookButton.js.map +1 -1
  12. package/dist/coolandlovely/components/BrandCard.js.map +1 -1
  13. package/dist/coolandlovely/components/BrandCover.js.map +1 -1
  14. package/dist/coolandlovely/components/BrandCreatorAvatars.js.map +1 -1
  15. package/dist/coolandlovely/components/CollaborationFeeCard.js.map +1 -1
  16. package/dist/coolandlovely/components/CollectionCard.js.map +1 -1
  17. package/dist/coolandlovely/components/ColourSwatchRow.js.map +1 -1
  18. package/dist/coolandlovely/components/Comments.js.map +1 -1
  19. package/dist/coolandlovely/components/EmptyState.js.map +1 -1
  20. package/dist/coolandlovely/components/FeedbackWidget.js.map +1 -1
  21. package/dist/coolandlovely/components/Field.js.map +1 -1
  22. package/dist/coolandlovely/components/FileImage.js.map +1 -1
  23. package/dist/coolandlovely/components/FollowButton.js.map +1 -1
  24. package/dist/coolandlovely/components/FulfilmentTimeline.js.map +1 -1
  25. package/dist/coolandlovely/components/ImageUploadField.js +3 -1
  26. package/dist/coolandlovely/components/ImageUploadField.js.map +1 -1
  27. package/dist/coolandlovely/components/LookCard.js.map +1 -1
  28. package/dist/coolandlovely/components/LookbookCard.js.map +1 -1
  29. package/dist/coolandlovely/components/MetricsCard.js.map +1 -1
  30. package/dist/coolandlovely/components/PageLayout.js.map +1 -1
  31. package/dist/coolandlovely/components/PayForOrder.js.map +1 -1
  32. package/dist/coolandlovely/components/RefundPanel.js +1 -1
  33. package/dist/coolandlovely/components/RefundPanel.js.map +1 -1
  34. package/dist/coolandlovely/components/ReturnRequests.js +1 -1
  35. package/dist/coolandlovely/components/ReturnRequests.js.map +1 -1
  36. package/dist/coolandlovely/components/ShopItemCard.js.map +1 -1
  37. package/dist/coolandlovely/components/StateViews.js.map +1 -1
  38. package/dist/coolandlovely/components/StatusBadge.js +1 -1
  39. package/dist/coolandlovely/components/StatusBadge.js.map +1 -1
  40. package/dist/coolandlovely/components/StepTracker.js.map +1 -1
  41. package/dist/coolandlovely/components/StockEditor.js.map +1 -1
  42. package/dist/coolandlovely/components/StoreListingCard.js.map +1 -1
  43. package/dist/coolandlovely/components/TagList.js.map +1 -1
  44. package/dist/coolandlovely/components/Thumbnail.js +1 -1
  45. package/dist/coolandlovely/components/Thumbnail.js.map +1 -1
  46. package/dist/coolandlovely/components/VisibilityChip.js.map +1 -1
  47. package/dist/coolandlovely/components/WardrobeItemCard.js.map +1 -1
  48. package/dist/coolandlovely/components/index.js +2 -1
  49. package/dist/coolandlovely/context/CoolAndLovelyContext.js +2 -1
  50. package/dist/coolandlovely/context/CoolAndLovelyContext.js.map +1 -1
  51. package/dist/coolandlovely/context/index.js +2 -1
  52. package/dist/coolandlovely/hooks/index.js +4 -3
  53. package/dist/coolandlovely/hooks/useActivity.d.ts +1 -1
  54. package/dist/coolandlovely/hooks/useActivity.d.ts.map +1 -1
  55. package/dist/coolandlovely/hooks/useActivity.js.map +1 -1
  56. package/dist/coolandlovely/hooks/useAssistantEntity.js.map +1 -1
  57. package/dist/coolandlovely/hooks/useBrandOrdersApi.d.ts +4 -4
  58. package/dist/coolandlovely/hooks/useBrandOrdersApi.d.ts.map +1 -1
  59. package/dist/coolandlovely/hooks/useBrandOrdersApi.js.map +1 -1
  60. package/dist/coolandlovely/hooks/useCatalogImport.js.map +1 -1
  61. package/dist/coolandlovely/hooks/useComments.d.ts +1 -1
  62. package/dist/coolandlovely/hooks/useComments.d.ts.map +1 -1
  63. package/dist/coolandlovely/hooks/useComments.js.map +1 -1
  64. package/dist/coolandlovely/hooks/useCommerceApi.d.ts +7 -7
  65. package/dist/coolandlovely/hooks/useCommerceApi.d.ts.map +1 -1
  66. package/dist/coolandlovely/hooks/useCommerceApi.js.map +1 -1
  67. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.d.ts +24 -24
  68. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.d.ts.map +1 -1
  69. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js.map +1 -1
  70. package/dist/coolandlovely/hooks/useEditorialApi.d.ts +95 -0
  71. package/dist/coolandlovely/hooks/useEditorialApi.d.ts.map +1 -0
  72. package/dist/coolandlovely/hooks/useEditorialApi.js +107 -0
  73. package/dist/coolandlovely/hooks/useEditorialApi.js.map +1 -0
  74. package/dist/coolandlovely/hooks/useFiles.js.map +1 -1
  75. package/dist/coolandlovely/hooks/useInventoryApi.d.ts +1 -1
  76. package/dist/coolandlovely/hooks/useInventoryApi.d.ts.map +1 -1
  77. package/dist/coolandlovely/hooks/useInventoryApi.js.map +1 -1
  78. package/dist/coolandlovely/hooks/useTr.js.map +1 -1
  79. package/dist/coolandlovely/index.js +6 -5
  80. package/dist/coolandlovely/pages/HomePage.js.map +1 -1
  81. package/dist/coolandlovely/pages/admin/AdminBrandsPage.js.map +1 -1
  82. package/dist/coolandlovely/pages/admin/AdminCollaborationsPage.js.map +1 -1
  83. package/dist/coolandlovely/pages/admin/AdminFeaturedItemsPage.js.map +1 -1
  84. package/dist/coolandlovely/pages/brands/BrandDetailPage.js.map +1 -1
  85. package/dist/coolandlovely/pages/brands/BrandFormPage.js.map +1 -1
  86. package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -1
  87. package/dist/coolandlovely/pages/brands/ImportCatalogPage.js.map +1 -1
  88. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -1
  89. package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +1 -1
  90. package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js.map +1 -1
  91. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js.map +1 -1
  92. package/dist/coolandlovely/pages/collections/CollectionDetailPage.js.map +1 -1
  93. package/dist/coolandlovely/pages/collections/CollectionFormPage.js.map +1 -1
  94. package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -1
  95. package/dist/coolandlovely/pages/commerce/BrandOrdersPage.js.map +1 -1
  96. package/dist/coolandlovely/pages/commerce/CartPage.js.map +1 -1
  97. package/dist/coolandlovely/pages/commerce/CreatorEarningsPage.js.map +1 -1
  98. package/dist/coolandlovely/pages/commerce/OrdersPage.js.map +1 -1
  99. package/dist/coolandlovely/pages/discover/DiscoverPage.js +1 -1
  100. package/dist/coolandlovely/pages/discover/DiscoverPage.js.map +1 -1
  101. package/dist/coolandlovely/pages/editorial/EditorialPage.d.ts +2 -0
  102. package/dist/coolandlovely/pages/editorial/EditorialPage.d.ts.map +1 -0
  103. package/dist/coolandlovely/pages/editorial/EditorialPage.js +486 -0
  104. package/dist/coolandlovely/pages/editorial/EditorialPage.js.map +1 -0
  105. package/dist/coolandlovely/pages/items/StoreListedPieceView.js.map +1 -1
  106. package/dist/coolandlovely/pages/items/StyleItemDetailPage.js.map +1 -1
  107. package/dist/coolandlovely/pages/items/StyleItemFormPage.js.map +1 -1
  108. package/dist/coolandlovely/pages/items/StyleItemsListPage.js.map +1 -1
  109. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js.map +1 -1
  110. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -1
  111. package/dist/coolandlovely/pages/looks/LookDetailPage.js.map +1 -1
  112. package/dist/coolandlovely/pages/looks/LookFormPage.js.map +1 -1
  113. package/dist/coolandlovely/pages/looks/LooksListPage.js.map +1 -1
  114. package/dist/coolandlovely/pages/profile/StyleProfilePage.js +1 -1
  115. package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -1
  116. package/dist/coolandlovely/pages/trends/TrendsPage.js.map +1 -1
  117. package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js.map +1 -1
  118. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -1
  119. package/dist/coolandlovely/store/coolandlovelyStore.js.map +1 -1
  120. package/dist/coolandlovely/store/index.js +2 -1
  121. package/dist/coolandlovely/types/commerce.d.ts.map +1 -1
  122. package/dist/coolandlovely/types/index.d.ts.map +1 -1
  123. package/dist/coolandlovely/utils/cn.js +1 -1
  124. package/dist/coolandlovely/utils/cn.js.map +1 -1
  125. package/dist/coolandlovely/utils/formatters.js.map +1 -1
  126. package/dist/coolandlovely/utils/icons.js.map +1 -1
  127. package/dist/coolandlovely/utils/index.js +2 -1
  128. package/dist/coolandlovely/utils/navigation.js.map +1 -1
  129. package/dist/coolandlovely/utils/paymentProviders.js +22 -22
  130. package/dist/coolandlovely/utils/paymentProviders.js.map +1 -1
  131. package/dist/coolandlovely/utils/sparkline.js +13 -15
  132. package/dist/coolandlovely/utils/sparkline.js.map +1 -1
  133. package/dist/node_modules/clsx/dist/clsx.js +1 -1
  134. package/dist/node_modules/clsx/dist/clsx.js.map +1 -1
  135. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +225 -146
  136. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -1
  137. package/dist/services/createSandboxAssistantTransport.js.map +1 -1
  138. package/dist/stories/DiscoverPage.stories.d.ts.map +1 -1
  139. package/dist/utils/pageContext.js +1 -1
  140. package/dist/utils/pageContext.js.map +1 -1
  141. package/package.json +6 -4
@@ -1 +1 @@
1
- {"version":3,"file":"createSandboxAssistantTransport.js","names":[],"sources":["../../src/services/createSandboxAssistantTransport.ts"],"sourcesContent":["/**\n * coolandlovely adapter for the shared fe-libs AssistantWidget transport.\n *\n * The sandbox lifecycle + poll/stream loop now lives in\n * `@burdenoff/fe-libs/shared/assistant` (one implementation for all products);\n * this is a thin wrapper. The exported name/shape are unchanged so app-shell\n * imports don't move. The public return type is a LOCALLY-declared structural\n * mirror — never re-export fe-libs types (vite-plugin-dts path rewriting).\n */\n\nimport {\n dispatchAssistantQuotaExhausted,\n useSandboxAssistantTransport as useSharedTransport,\n} from '@burdenoff/fe-libs/shared/assistant';\nimport { gatherPageContext } from '@/utils/pageContext';\n\ninterface AssistantSendArgs {\n prompt: string;\n onProgress: (partialText: string) => void;\n signal: AbortSignal;\n}\n\nexport interface AssistantTransport {\n sendPrompt: (args: AssistantSendArgs) => Promise<{ text: string }>;\n prewarm?: (level?: 'probe' | 'full') => void;\n suspend?: () => void;\n}\n\n/**\n * A spent `coolandlovely.ai.credits` budget refuses the send in the shared\n * transport, and the widget shows that refusal in place of an answer\n * (BOFF-10274). This reports the wall on the product's\n * `coolandlovely:quota-exhausted` event as well, so a credits meter refetches\n * and a shell that mounts the upgrade bridge can offer the way past it.\n * Module-scoped so the transport (memoised on product/mode/user) never holds a\n * stale callback.\n */\nfunction onQuotaExhausted(): void {\n dispatchAssistantQuotaExhausted('coolandlovely', 'ai-credits');\n}\n\nexport function useSandboxAssistantTransport(): AssistantTransport {\n return useSharedTransport({\n product: 'coolandlovely',\n gatherPageContext: gatherPageContext,\n enableCredits: true,\n onQuotaExhausted,\n });\n}\n"],"mappings":";;;AAqCA,SAAS,IAAyB;AAChC,GAAgC,iBAAiB,aAAa;;AAGhE,SAAgB,IAAmD;AACjE,QAAO,EAAmB;EACxB,SAAS;EACU;EACnB,eAAe;EACf;EACD,CAAC"}
1
+ {"version":3,"file":"createSandboxAssistantTransport.js","names":[],"sources":["../../src/services/createSandboxAssistantTransport.ts"],"sourcesContent":["/**\n * coolandlovely adapter for the shared fe-libs AssistantWidget transport.\n *\n * The sandbox lifecycle + poll/stream loop now lives in\n * `@burdenoff/fe-libs/shared/assistant` (one implementation for all products);\n * this is a thin wrapper. The exported name/shape are unchanged so app-shell\n * imports don't move. The public return type is a LOCALLY-declared structural\n * mirror — never re-export fe-libs types (vite-plugin-dts path rewriting).\n */\n\nimport {\n dispatchAssistantQuotaExhausted,\n useSandboxAssistantTransport as useSharedTransport,\n} from '@burdenoff/fe-libs/shared/assistant';\nimport { gatherPageContext } from '@/utils/pageContext';\n\ninterface AssistantSendArgs {\n prompt: string;\n onProgress: (partialText: string) => void;\n signal: AbortSignal;\n}\n\nexport interface AssistantTransport {\n sendPrompt: (args: AssistantSendArgs) => Promise<{ text: string }>;\n prewarm?: (level?: 'probe' | 'full') => void;\n suspend?: () => void;\n}\n\n/**\n * A spent `coolandlovely.ai.credits` budget refuses the send in the shared\n * transport, and the widget shows that refusal in place of an answer\n * (BOFF-10274). This reports the wall on the product's\n * `coolandlovely:quota-exhausted` event as well, so a credits meter refetches\n * and a shell that mounts the upgrade bridge can offer the way past it.\n * Module-scoped so the transport (memoised on product/mode/user) never holds a\n * stale callback.\n */\nfunction onQuotaExhausted(): void {\n dispatchAssistantQuotaExhausted('coolandlovely', 'ai-credits');\n}\n\nexport function useSandboxAssistantTransport(): AssistantTransport {\n return useSharedTransport({\n product: 'coolandlovely',\n gatherPageContext: gatherPageContext,\n enableCredits: true,\n onQuotaExhausted,\n });\n}\n"],"mappings":";;;AAqCA,SAAS,IAAyB;CAChC,EAAgC,iBAAiB,YAAY;AAC/D;AAEA,SAAgB,IAAmD;CACjE,OAAO,EAAmB;EACxB,SAAS;EACU;EACnB,eAAe;EACf;CACF,CAAC;AACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"DiscoverPage.stories.d.ts","sourceRoot":"","sources":["../../src/stories/DiscoverPage.stories.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,8CAA8C,CAAC;AAS5E,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,YAAY,CAInC,CAAC;AAEF,eAAe,IAAI,CAAC;AAEpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,YAAY,CAAC,CAAC;AA6C3C,eAAO,MAAM,MAAM,EAAE,KAoCpB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAezB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAezB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAQrB,CAAC"}
1
+ {"version":3,"file":"DiscoverPage.stories.d.ts","sourceRoot":"","sources":["../../src/stories/DiscoverPage.stories.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,8CAA8C,CAAC;AAQ5E,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,YAAY,CAInC,CAAC;AAEF,eAAe,IAAI,CAAC;AAEpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,YAAY,CAAC,CAAC;AA2C3C,eAAO,MAAM,MAAM,EAAE,KAoCpB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAezB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAezB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAQrB,CAAC"}
@@ -53,6 +53,6 @@ function o(e, t) {
53
53
  return s ? { activeTab: s } : {};
54
54
  }
55
55
  //#endregion
56
- export { n as clearActiveEntityName, r as gatherPageContext, t as setActiveEntityName };
56
+ export { n as clearActiveEntityName, r as gatherPageContext, o as resolveCoolAndLovelyConversationContext, t as setActiveEntityName };
57
57
 
58
58
  //# sourceMappingURL=pageContext.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pageContext.js","names":[],"sources":["../../src/utils/pageContext.ts"],"sourcesContent":["/**\n * Page context gathering for the CoolAndLovely AI assistant.\n *\n * Captures the user's current browser context (URL, route, entity) so the\n * floating assistant can answer \"this page\" / \"current\" questions and run\n * operations scoped to the screen the user is looking at.\n *\n * Every field on `PageContext` MUST be one of the keys in\n * `ALLOWED_CONTEXT_KEYS` (~/products/wspace/sandbox/ai-assistant/src/context-loader.ts)\n * or the sandbox silently drops it before it ever reaches the model — there is\n * no `metadata`/nested-object passthrough, so entity detail must be flattened\n * into `entityType`/`entityId`, and route detail into `activeTab`.\n */\n\nexport interface PageContext {\n currentUrl: string;\n currentPage: string;\n entityType?: string;\n entityId?: string;\n /**\n * Human-readable name of the entity at `entityId` (a brand/collection/item/\n * look/collaboration/lookbook name), published by the detail page that\n * already loaded it. `entityName` is one of the agent's allowed context keys,\n * so this is what lets the assistant say \"Ribbed Merino Crewneck\" instead of\n * a UUID and resolve \"this item\" by name.\n */\n entityName?: string;\n /** The non-entity route/tab the shopper is on (e.g. 'wardrobe', 'discover', 'profile'). */\n activeTab?: string;\n platform: string;\n version: string;\n viewportSize: string;\n userAgent: string;\n timestamp: string;\n}\n\ninterface ConversationContextShape {\n entityType?: string;\n entityId?: string;\n activeTab?: string;\n}\n\n/**\n * The entity name most recently published by a detail page, kept module-level\n * because `gatherPageContext()` is called by the assistant transport outside\n * React (once per prompt), not from a component tree.\n */\nlet activeEntityName: { entityId: string; entityName: string } | null = null;\n\n/**\n * Publish the display name of the entity the shopper is looking at.\n *\n * Detail pages call this with the name from their OWN loaded query result —\n * never a guess and never a name assembled client-side. Pairing it with the\n * entity id is what makes the staleness guard in `gatherPageContext()`\n * possible: a name is only ever sent alongside the id it belongs to.\n */\nexport function setActiveEntityName(entityId: string, entityName: string): void {\n const name = entityName.trim();\n if (!entityId || !name) return;\n activeEntityName = { entityId, entityName: name };\n}\n\n/**\n * Drop the published name when the detail page unmounts (or its entity\n * changes). Scoped by id so a page that has already been replaced cannot clear\n * the name the next page just published.\n */\nexport function clearActiveEntityName(entityId: string): void {\n if (activeEntityName?.entityId === entityId) {\n activeEntityName = null;\n }\n}\n\nexport function gatherPageContext(): PageContext {\n const url = window.location.href;\n const pathSegments = window.location.pathname.split('/').filter(Boolean);\n const currentPage = pathSegments.slice(0, 3).join('/') || 'home';\n const context = resolveCoolAndLovelyConversationContext(\n window.location.pathname,\n window.location.search\n );\n\n return {\n currentUrl: url,\n currentPage,\n entityType: context.entityType,\n entityId: context.entityId,\n // Only forward the name when it belongs to the entity the URL currently\n // resolves to — otherwise the assistant would be told the wrong item's\n // name after a navigation the publisher has not caught up with.\n entityName:\n context.entityId && activeEntityName?.entityId === context.entityId\n ? activeEntityName.entityName\n : undefined,\n activeTab: context.activeTab,\n platform: 'coolandlovely',\n version: import.meta.env.VITE_APP_VERSION ?? '2026.614.1',\n viewportSize: `${window.innerWidth}x${window.innerHeight}`,\n userAgent: navigator.userAgent,\n timestamp: new Date().toISOString(),\n };\n}\n\nfunction matchPath(pattern: RegExp, pathname: string): RegExpMatchArray | null {\n return pathname.match(pattern);\n}\n\n/**\n * One capture group `id`, matched whether the URL ends right there or\n * continues into a sub-route (`/edit`, `/new`, etc.) — e.g. both\n * `/coolandlovely/brands/b1` and `/coolandlovely/brands/b1/edit` resolve to\n * the same `entityId: 'b1'`. Unanchored `$` was the original bug: it silently\n * dropped entityId on every `/:id/edit` route.\n */\nfunction entityRoute(segment: string): RegExp {\n return new RegExp(`^/coolandlovely/${segment}/([^/]+)(?:/.*)?$`);\n}\n\nexport function resolveCoolAndLovelyConversationContext(\n pathname: string,\n search: string\n): ConversationContextShape {\n const params = new URLSearchParams(search);\n\n const directMatches: Array<[RegExp, string]> = [\n [entityRoute('brands'), 'COOLANDLOVELY_BRAND'],\n [entityRoute('collections'), 'COOLANDLOVELY_COLLECTION'],\n [entityRoute('items'), 'COOLANDLOVELY_STYLE_ITEM'],\n [entityRoute('looks'), 'COOLANDLOVELY_LOOK'],\n [entityRoute('collaborations'), 'COOLANDLOVELY_COLLABORATION'],\n [entityRoute('lookbooks'), 'COOLANDLOVELY_LOOKBOOK'],\n // /coolandlovely/profile/:userId — a public style profile. Bare\n // /coolandlovely/profile (the caller's own profile, no id segment) is\n // intentionally NOT matched here; it falls through to the activeTab path\n // below since it has no third path segment to capture as an id.\n [entityRoute('profile'), 'COOLANDLOVELY_STYLE_PROFILE'],\n ];\n\n for (const [pattern, entityType] of directMatches) {\n const match = matchPath(pattern, pathname);\n if (match && match[1] !== 'new') {\n return { entityType, entityId: match[1] };\n }\n }\n\n // Non-entity routes (list pages, the shopper's own /profile, /wardrobe,\n // /discover, /trends, …): surface the route as `activeTab` so the assistant\n // still knows what screen the shopper is on even without a specific entity.\n const segments = pathname.split('/').filter(Boolean);\n const routeTab = segments[1]; // segments[0] === 'coolandlovely'\n const activeTab = params.get('tab') ?? routeTab ?? undefined;\n\n return activeTab ? { activeTab } : {};\n}\n"],"mappings":";AA+CA,IAAI,IAAoE;AAUxE,SAAgB,EAAoB,GAAkB,GAA0B;CAC9E,IAAM,IAAO,EAAW,MAAM;AAC1B,EAAC,KAAY,CAAC,MAClB,IAAmB;EAAE;EAAU,YAAY;EAAM;;AAQnD,SAAgB,EAAsB,GAAwB;AAC5D,CAAI,GAAkB,aAAa,MACjC,IAAmB;;AAIvB,SAAgB,IAAiC;CAC/C,IAAM,IAAM,OAAO,SAAS,MAEtB,IADe,OAAO,SAAS,SAAS,MAAM,IAAI,CAAC,OAAO,QAAQ,CACvC,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,IAAI,QACpD,IAAU,EACd,OAAO,SAAS,UAChB,OAAO,SAAS,OACjB;AAED,QAAO;EACL,YAAY;EACZ;EACA,YAAY,EAAQ;EACpB,UAAU,EAAQ;EAIlB,YACE,EAAQ,YAAY,GAAkB,aAAa,EAAQ,WACvD,EAAiB,aACjB,KAAA;EACN,WAAW,EAAQ;EACnB,UAAU;EACV,SAA6C;EAC7C,cAAc,GAAG,OAAO,WAAW,GAAG,OAAO;EAC7C,WAAW,UAAU;EACrB,4BAAW,IAAI,MAAM,EAAC,aAAa;EACpC;;AAGH,SAAS,EAAU,GAAiB,GAA2C;AAC7E,QAAO,EAAS,MAAM,EAAQ;;AAUhC,SAAS,EAAY,GAAyB;AAC5C,QAAW,OAAO,mBAAmB,EAAQ,mBAAmB;;AAGlE,SAAgB,EACd,GACA,GAC0B;CAC1B,IAAM,IAAS,IAAI,gBAAgB,EAAO,EAEpC,IAAyC;EAC7C,CAAC,EAAY,SAAS,EAAE,sBAAsB;EAC9C,CAAC,EAAY,cAAc,EAAE,2BAA2B;EACxD,CAAC,EAAY,QAAQ,EAAE,2BAA2B;EAClD,CAAC,EAAY,QAAQ,EAAE,qBAAqB;EAC5C,CAAC,EAAY,iBAAiB,EAAE,8BAA8B;EAC9D,CAAC,EAAY,YAAY,EAAE,yBAAyB;EAKpD,CAAC,EAAY,UAAU,EAAE,8BAA8B;EACxD;AAED,MAAK,IAAM,CAAC,GAAS,MAAe,GAAe;EACjD,IAAM,IAAQ,EAAU,GAAS,EAAS;AAC1C,MAAI,KAAS,EAAM,OAAO,MACxB,QAAO;GAAE;GAAY,UAAU,EAAM;GAAI;;CAQ7C,IAAM,IADW,EAAS,MAAM,IAAI,CAAC,OAAO,QAAQ,CAC1B,IACpB,IAAY,EAAO,IAAI,MAAM,IAAI,KAAY,KAAA;AAEnD,QAAO,IAAY,EAAE,cAAW,GAAG,EAAE"}
1
+ {"version":3,"file":"pageContext.js","names":[],"sources":["../../src/utils/pageContext.ts"],"sourcesContent":["/**\n * Page context gathering for the CoolAndLovely AI assistant.\n *\n * Captures the user's current browser context (URL, route, entity) so the\n * floating assistant can answer \"this page\" / \"current\" questions and run\n * operations scoped to the screen the user is looking at.\n *\n * Every field on `PageContext` MUST be one of the keys in\n * `ALLOWED_CONTEXT_KEYS` (~/products/wspace/sandbox/ai-assistant/src/context-loader.ts)\n * or the sandbox silently drops it before it ever reaches the model — there is\n * no `metadata`/nested-object passthrough, so entity detail must be flattened\n * into `entityType`/`entityId`, and route detail into `activeTab`.\n */\n\nexport interface PageContext {\n currentUrl: string;\n currentPage: string;\n entityType?: string;\n entityId?: string;\n /**\n * Human-readable name of the entity at `entityId` (a brand/collection/item/\n * look/collaboration/lookbook name), published by the detail page that\n * already loaded it. `entityName` is one of the agent's allowed context keys,\n * so this is what lets the assistant say \"Ribbed Merino Crewneck\" instead of\n * a UUID and resolve \"this item\" by name.\n */\n entityName?: string;\n /** The non-entity route/tab the shopper is on (e.g. 'wardrobe', 'discover', 'profile'). */\n activeTab?: string;\n platform: string;\n version: string;\n viewportSize: string;\n userAgent: string;\n timestamp: string;\n}\n\ninterface ConversationContextShape {\n entityType?: string;\n entityId?: string;\n activeTab?: string;\n}\n\n/**\n * The entity name most recently published by a detail page, kept module-level\n * because `gatherPageContext()` is called by the assistant transport outside\n * React (once per prompt), not from a component tree.\n */\nlet activeEntityName: { entityId: string; entityName: string } | null = null;\n\n/**\n * Publish the display name of the entity the shopper is looking at.\n *\n * Detail pages call this with the name from their OWN loaded query result —\n * never a guess and never a name assembled client-side. Pairing it with the\n * entity id is what makes the staleness guard in `gatherPageContext()`\n * possible: a name is only ever sent alongside the id it belongs to.\n */\nexport function setActiveEntityName(entityId: string, entityName: string): void {\n const name = entityName.trim();\n if (!entityId || !name) return;\n activeEntityName = { entityId, entityName: name };\n}\n\n/**\n * Drop the published name when the detail page unmounts (or its entity\n * changes). Scoped by id so a page that has already been replaced cannot clear\n * the name the next page just published.\n */\nexport function clearActiveEntityName(entityId: string): void {\n if (activeEntityName?.entityId === entityId) {\n activeEntityName = null;\n }\n}\n\nexport function gatherPageContext(): PageContext {\n const url = window.location.href;\n const pathSegments = window.location.pathname.split('/').filter(Boolean);\n const currentPage = pathSegments.slice(0, 3).join('/') || 'home';\n const context = resolveCoolAndLovelyConversationContext(\n window.location.pathname,\n window.location.search\n );\n\n return {\n currentUrl: url,\n currentPage,\n entityType: context.entityType,\n entityId: context.entityId,\n // Only forward the name when it belongs to the entity the URL currently\n // resolves to — otherwise the assistant would be told the wrong item's\n // name after a navigation the publisher has not caught up with.\n entityName:\n context.entityId && activeEntityName?.entityId === context.entityId\n ? activeEntityName.entityName\n : undefined,\n activeTab: context.activeTab,\n platform: 'coolandlovely',\n version: import.meta.env.VITE_APP_VERSION ?? '2026.614.1',\n viewportSize: `${window.innerWidth}x${window.innerHeight}`,\n userAgent: navigator.userAgent,\n timestamp: new Date().toISOString(),\n };\n}\n\nfunction matchPath(pattern: RegExp, pathname: string): RegExpMatchArray | null {\n return pathname.match(pattern);\n}\n\n/**\n * One capture group `id`, matched whether the URL ends right there or\n * continues into a sub-route (`/edit`, `/new`, etc.) — e.g. both\n * `/coolandlovely/brands/b1` and `/coolandlovely/brands/b1/edit` resolve to\n * the same `entityId: 'b1'`. Unanchored `$` was the original bug: it silently\n * dropped entityId on every `/:id/edit` route.\n */\nfunction entityRoute(segment: string): RegExp {\n return new RegExp(`^/coolandlovely/${segment}/([^/]+)(?:/.*)?$`);\n}\n\nexport function resolveCoolAndLovelyConversationContext(\n pathname: string,\n search: string\n): ConversationContextShape {\n const params = new URLSearchParams(search);\n\n const directMatches: Array<[RegExp, string]> = [\n [entityRoute('brands'), 'COOLANDLOVELY_BRAND'],\n [entityRoute('collections'), 'COOLANDLOVELY_COLLECTION'],\n [entityRoute('items'), 'COOLANDLOVELY_STYLE_ITEM'],\n [entityRoute('looks'), 'COOLANDLOVELY_LOOK'],\n [entityRoute('collaborations'), 'COOLANDLOVELY_COLLABORATION'],\n [entityRoute('lookbooks'), 'COOLANDLOVELY_LOOKBOOK'],\n // /coolandlovely/profile/:userId — a public style profile. Bare\n // /coolandlovely/profile (the caller's own profile, no id segment) is\n // intentionally NOT matched here; it falls through to the activeTab path\n // below since it has no third path segment to capture as an id.\n [entityRoute('profile'), 'COOLANDLOVELY_STYLE_PROFILE'],\n ];\n\n for (const [pattern, entityType] of directMatches) {\n const match = matchPath(pattern, pathname);\n if (match && match[1] !== 'new') {\n return { entityType, entityId: match[1] };\n }\n }\n\n // Non-entity routes (list pages, the shopper's own /profile, /wardrobe,\n // /discover, /trends, …): surface the route as `activeTab` so the assistant\n // still knows what screen the shopper is on even without a specific entity.\n const segments = pathname.split('/').filter(Boolean);\n const routeTab = segments[1]; // segments[0] === 'coolandlovely'\n const activeTab = params.get('tab') ?? routeTab ?? undefined;\n\n return activeTab ? { activeTab } : {};\n}\n"],"mappings":";AA+CA,IAAI,IAAoE;AAUxE,SAAgB,EAAoB,GAAkB,GAA0B;CAC9E,IAAM,IAAO,EAAW,KAAK;CACzB,CAAC,KAAY,CAAC,MAClB,IAAmB;EAAE;EAAU,YAAY;CAAK;AAClD;AAOA,SAAgB,EAAsB,GAAwB;CAC5D,AAAI,GAAkB,aAAa,MACjC,IAAmB;AAEvB;AAEA,SAAgB,IAAiC;CAC/C,IAAM,IAAM,OAAO,SAAS,MAEtB,IADe,OAAO,SAAS,SAAS,MAAM,GAAG,CAAC,CAAC,OAAO,OAC5C,CAAA,CAAa,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,QACpD,IAAU,EACd,OAAO,SAAS,UAChB,OAAO,SAAS,MAClB;CAEA,OAAO;EACL,YAAY;EACZ;EACA,YAAY,EAAQ;EACpB,UAAU,EAAQ;EAIlB,YACE,EAAQ,YAAY,GAAkB,aAAa,EAAQ,WACvD,EAAiB,aACjB,KAAA;EACN,WAAW,EAAQ;EACnB,UAAU;EACV,SAA6C;EAC7C,cAAc,GAAG,OAAO,WAAW,GAAG,OAAO;EAC7C,WAAW,UAAU;EACrB,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;CACpC;AACF;AAEA,SAAS,EAAU,GAAiB,GAA2C;CAC7E,OAAO,EAAS,MAAM,CAAO;AAC/B;AASA,SAAS,EAAY,GAAyB;CAC5C,OAAW,OAAO,mBAAmB,EAAQ,kBAAkB;AACjE;AAEA,SAAgB,EACd,GACA,GAC0B;CAC1B,IAAM,IAAS,IAAI,gBAAgB,CAAM,GAEnC,IAAyC;EAC7C,CAAC,EAAY,QAAQ,GAAG,qBAAqB;EAC7C,CAAC,EAAY,aAAa,GAAG,0BAA0B;EACvD,CAAC,EAAY,OAAO,GAAG,0BAA0B;EACjD,CAAC,EAAY,OAAO,GAAG,oBAAoB;EAC3C,CAAC,EAAY,gBAAgB,GAAG,6BAA6B;EAC7D,CAAC,EAAY,WAAW,GAAG,wBAAwB;EAKnD,CAAC,EAAY,SAAS,GAAG,6BAA6B;CACxD;CAEA,KAAK,IAAM,CAAC,GAAS,MAAe,GAAe;EACjD,IAAM,IAAQ,EAAU,GAAS,CAAQ;EACzC,IAAI,KAAS,EAAM,OAAO,OACxB,OAAO;GAAE;GAAY,UAAU,EAAM;EAAG;CAE5C;CAMA,IAAM,IADW,EAAS,MAAM,GAAG,CAAC,CAAC,OAAO,OAC3B,CAAA,CAAS,IACpB,IAAY,EAAO,IAAI,KAAK,KAAK,KAAY,KAAA;CAEnD,OAAO,IAAY,EAAE,aAAU,IAAI,CAAC;AACtC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-coolandlovely",
3
- "version": "2026.1031.2",
3
+ "version": "2026.1101.2",
4
4
  "description": "Burdenoff CoolAndLovely Microfrontend",
5
5
  "type": "module",
6
6
  "files": [
@@ -38,7 +38,7 @@
38
38
  "storybook": "storybook dev -p 6006",
39
39
  "build-storybook": "storybook build",
40
40
  "test:storybook": "test-storybook",
41
- "test:storybook:ci": "bun run build-storybook -- --output-dir storybook-static && python3 -m http.server 6006 --directory storybook-static > /dev/null 2>&1 & SERVER_PID=$! && sleep 3 && bun run test:storybook; EXIT_CODE=$?; kill $SERVER_PID 2>/dev/null; exit $EXIT_CODE",
41
+ "test:storybook:ci": "bash scripts/test-storybook-ci.sh",
42
42
  "switch:status": "bun scripts/deps-mode.ts status",
43
43
  "switch:remote": "bun scripts/deps-mode.ts remote",
44
44
  "switch:local": "bun scripts/deps-mode.ts local",
@@ -81,7 +81,7 @@
81
81
  "tailwindcss": "^4.0.0",
82
82
  "typescript": "^5.9.0",
83
83
  "typescript-eslint": "^8.46.0",
84
- "vite": "8.0.8",
84
+ "vite": "8.1.6",
85
85
  "vite-plugin-dts": "3.9.1",
86
86
  "@storybook/addon-a11y": "^8.6.0",
87
87
  "@storybook/addon-essentials": "^8.6.0",
@@ -119,6 +119,8 @@
119
119
  "yaml": ">=2.8.3",
120
120
  "picomatch": ">=2.3.2",
121
121
  "immutable": ">=3.8.3",
122
- "minimatch": ">=3.1.3"
122
+ "minimatch": ">=3.1.3",
123
+ "zod": "4.6.7",
124
+ "@graphql-tools/utils": "12.0.2"
123
125
  }
124
126
  }