@mohasinac/appkit 4.69.1 → 4.71.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 (182) hide show
  1. package/PROVIDERS.md +190 -0
  2. package/dist/.build-stamp.json +3 -3
  3. package/dist/_internal/client/features/lottery/LotteryAdminSlotView.js +2 -1
  4. package/dist/_internal/server/features/checkout/actions.js +43 -8
  5. package/dist/_internal/server/features/orders/actions.js +2 -1
  6. package/dist/_internal/server/features/orders/adapters.js +18 -0
  7. package/dist/_internal/server/features/orders/balance.js +2 -1
  8. package/dist/_internal/server/features/orders/cancellation-refund.d.ts +0 -89
  9. package/dist/_internal/server/features/orders/cancellation-refund.js +3 -2
  10. package/dist/_internal/server/features/payouts/actions.js +2 -1
  11. package/dist/_internal/server/features/points/award.js +3 -2
  12. package/dist/_internal/server/features/protection-claims/actions.js +2 -1
  13. package/dist/_internal/server/features/refunds/actions.js +3 -2
  14. package/dist/_internal/server/features/shipping/courier-block.js +5 -2
  15. package/dist/_internal/server/features/shipping/reconcile.js +3 -2
  16. package/dist/_internal/server/features/shipping/reimbursement.js +3 -2
  17. package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
  18. package/dist/_internal/server/features/stores/adapters.js +12 -2
  19. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +7 -3
  20. package/dist/_internal/server/jobs/core/emiInstallmentReminder.js +2 -1
  21. package/dist/_internal/server/jobs/core/obligationSweep.js +5 -4
  22. package/dist/_internal/server/jobs/core/onScamReportCreate.js +2 -1
  23. package/dist/_internal/server/jobs/core/paymentReconcile.js +5 -4
  24. package/dist/_internal/shared/checkout/rules/auction.rule.js +25 -7
  25. package/dist/_internal/shared/checkout/rules/prize-draw.rule.js +26 -8
  26. package/dist/_internal/shared/config/schema.d.ts +15 -0
  27. package/dist/_internal/shared/content/published-values.d.ts +2 -2
  28. package/dist/_internal/shared/content/published-values.js +31 -12
  29. package/dist/_internal/shared/features/auctions/errors.js +2 -1
  30. package/dist/_internal/shared/features/orders/refund-copy.js +3 -2
  31. package/dist/_internal/shared/features/promotions/errors.js +2 -1
  32. package/dist/constants/field-names.d.ts +23 -0
  33. package/dist/constants/field-names.js +23 -0
  34. package/dist/constants/storage-keys.d.ts +90 -0
  35. package/dist/constants/storage-keys.js +119 -0
  36. package/dist/contracts/index.d.ts +1 -1
  37. package/dist/contracts/payment.d.ts +42 -0
  38. package/dist/contracts/registry.d.ts +8 -0
  39. package/dist/contracts/tax.d.ts +118 -0
  40. package/dist/contracts/tax.js +3 -0
  41. package/dist/core/brand-resolver.d.ts +92 -0
  42. package/dist/core/brand-resolver.js +120 -0
  43. package/dist/core/brand-text.d.ts +15 -0
  44. package/dist/core/brand-text.js +73 -0
  45. package/dist/core/hooks/useNewsletterSubscribe.d.ts +30 -0
  46. package/dist/core/hooks/useNewsletterSubscribe.js +121 -0
  47. package/dist/features/about/components/DeveloperView.js +2 -1
  48. package/dist/features/about/index.d.ts +0 -2
  49. package/dist/features/about/index.js +0 -1
  50. package/dist/features/account/components/BuyerAccountGuideView.js +2 -1
  51. package/dist/features/account/components/BuyerAuctionsGuideView.js +4 -3
  52. package/dist/features/account/components/BuyerOrdersGuideView.js +6 -5
  53. package/dist/features/account/components/BuyerShoppingGuideView.js +3 -2
  54. package/dist/features/account/components/UserOffersPanel.js +2 -1
  55. package/dist/features/account/hooks/useCollapsedSections.js +5 -3
  56. package/dist/features/account/schemas/index.js +1 -1
  57. package/dist/features/admin/actions/notification-actions.js +9 -2
  58. package/dist/features/admin/components/AdminAnalyticsGuideView.js +2 -1
  59. package/dist/features/admin/components/AdminBundlesView.js +2 -1
  60. package/dist/features/admin/components/AdminCatalogGuideView.js +2 -1
  61. package/dist/features/admin/components/AdminContentGuideView.js +2 -1
  62. package/dist/features/admin/components/AdminFaqEditorView.js +2 -1
  63. package/dist/features/admin/components/AdminGatewayEventsView.js +2 -1
  64. package/dist/features/admin/components/AdminGuideHubView.js +2 -1
  65. package/dist/features/admin/components/AdminMoneyCasesView.js +2 -1
  66. package/dist/features/admin/components/AdminOrdersGuideView.js +2 -1
  67. package/dist/features/admin/components/AdminProductsView.js +3 -2
  68. package/dist/features/admin/components/AdminSectionsView.js +3 -2
  69. package/dist/features/admin/components/AdminSiteSettingsView.js +8 -7
  70. package/dist/features/admin/components/AdminStoresGuideView.js +2 -1
  71. package/dist/features/admin/components/AdminSupportTicketDetailView.js +2 -1
  72. package/dist/features/admin/components/AdminTeamGuideView.js +2 -1
  73. package/dist/features/admin/components/AdminTrustGuideView.js +3 -2
  74. package/dist/features/admin/components/AdminUsersGuideView.js +2 -1
  75. package/dist/features/admin/components/sections/adminSectionsTypes.js +1 -1
  76. package/dist/features/admin/schemas/firestore.js +0 -1
  77. package/dist/features/auth/actions/user-slug.js +21 -2
  78. package/dist/features/auth/hooks/useAuth.js +11 -4
  79. package/dist/features/cart/utils/pending-ops.js +16 -3
  80. package/dist/features/catalogue/utils/product-from-item.d.ts +18 -2
  81. package/dist/features/catalogue/utils/product-from-item.js +23 -4
  82. package/dist/features/categories/components/BundleItemsPicker.js +2 -1
  83. package/dist/features/categories/components/CategoryDetailPageView.js +2 -1
  84. package/dist/features/categories/components/ContextualTrail.d.ts +9 -2
  85. package/dist/features/categories/components/ContextualTrail.js +11 -3
  86. package/dist/features/categories/components/LastCategoryBeacon.js +2 -2
  87. package/dist/features/checkout/actions/checkout-value-otp-actions.js +9 -1
  88. package/dist/features/contact/email.js +11 -12
  89. package/dist/features/email/notification-templates.js +4 -5
  90. package/dist/features/email/render-static-markup.d.ts +34 -0
  91. package/dist/features/email/render-static-markup.js +4 -0
  92. package/dist/features/events/utils/event-charts.js +2 -1
  93. package/dist/features/homepage/components/AnnouncementBar.js +5 -3
  94. package/dist/features/homepage/components/FAQSection.js +1 -1
  95. package/dist/features/homepage/components/MarketplaceHomepageView.js +34 -0
  96. package/dist/features/homepage/components/WelcomeSection.d.ts +3 -1
  97. package/dist/features/homepage/components/WelcomeSection.js +2 -2
  98. package/dist/features/homepage/lib/section-renderer.d.ts +8 -0
  99. package/dist/features/homepage/lib/section-renderer.js +1 -1
  100. package/dist/features/layout/AppLayoutShell.d.ts +3 -1
  101. package/dist/features/layout/AppLayoutShell.js +2 -2
  102. package/dist/features/layout/TitleBarLayout.d.ts +8 -1
  103. package/dist/features/layout/TitleBarLayout.js +2 -2
  104. package/dist/features/media/MediaVideo.js +2 -1
  105. package/dist/features/orders/actions/refund-actions.js +3 -2
  106. package/dist/features/orders/components/RefundRequestView.js +15 -2
  107. package/dist/features/orders/repository/orders.repository.js +2 -1
  108. package/dist/features/orders/types/index.d.ts +14 -0
  109. package/dist/features/orders/utils/payment-proof-message.js +5 -2
  110. package/dist/features/price-index/components/PriceIndexToolView.js +6 -5
  111. package/dist/features/pricing/components/AdminPricingView.js +2 -1
  112. package/dist/features/print/components/labels.js +2 -1
  113. package/dist/features/products/components/CityCollectiblesView.js +2 -1
  114. package/dist/features/products/components/MakeOfferButton.js +2 -1
  115. package/dist/features/products/components/PrizeDrawCollage.js +3 -2
  116. package/dist/features/products/components/PrizeRevealModal.js +2 -1
  117. package/dist/features/products/repository/products.repository.js +2 -1
  118. package/dist/features/promotions/components/CouponCard.js +3 -2
  119. package/dist/features/promotions/components/CouponHelpDetails.js +2 -1
  120. package/dist/features/promotions/constants/coupon-help.d.ts +1 -1
  121. package/dist/features/promotions/constants/coupon-help.js +8 -1
  122. package/dist/features/scams/components/ScamAwarenessModal.js +2 -1
  123. package/dist/features/scams/components/ScamProfileView.js +5 -3
  124. package/dist/features/scams/components/ScamRegistryView.js +2 -1
  125. package/dist/features/scams/constants/scam-types.d.ts +13 -1
  126. package/dist/features/scams/constants/scam-types.js +67 -23
  127. package/dist/features/scams/schemas/firestore.js +1 -1
  128. package/dist/features/search/components/Search.js +5 -1
  129. package/dist/features/seller/actions/offer-actions.js +47 -9
  130. package/dist/features/seller/components/DispatchForm.js +3 -2
  131. package/dist/features/seller/components/PrintCenterView.js +4 -2
  132. package/dist/features/seller/components/SellerBundlesView.js +2 -1
  133. package/dist/features/seller/components/SellerPayoutSettingsView.js +3 -2
  134. package/dist/features/seller/components/SellerProductShell.js +3 -2
  135. package/dist/features/seller/components/SellerProductsView.js +4 -3
  136. package/dist/features/store-extensions/repository/store-extensions.repositories.d.ts +30 -4
  137. package/dist/features/store-extensions/repository/store-extensions.repositories.js +46 -7
  138. package/dist/features/store-extensions/schemas/firestore.d.ts +22 -3
  139. package/dist/features/store-extensions/schemas/firestore.js +20 -0
  140. package/dist/features/stores/components/StoreCapabilitiesGuideView.js +7 -6
  141. package/dist/features/stores/components/StoreFinanceGuideView.js +6 -5
  142. package/dist/features/stores/components/StoreGuideHubView.js +2 -1
  143. package/dist/features/stores/components/StoreListingsGuideView.js +2 -1
  144. package/dist/features/stores/components/StoreOrdersGuideView.js +5 -4
  145. package/dist/features/stores/components/StoreSettingsGuideView.js +2 -1
  146. package/dist/features/support/schemas/index.d.ts +2 -2
  147. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.d.ts +1 -1
  148. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +3 -2
  149. package/dist/features/whatsapp-bot/helpers/whatsapp.js +5 -2
  150. package/dist/index.d.ts +0 -3
  151. package/dist/index.js +0 -6
  152. package/dist/providers/payment-manual/index.d.ts +11 -0
  153. package/dist/providers/payment-manual/index.js +13 -0
  154. package/dist/providers/payment-phonepe/index.d.ts +23 -7
  155. package/dist/providers/payment-phonepe/index.js +15 -0
  156. package/dist/providers/payment-razorpay/index.d.ts +13 -0
  157. package/dist/providers/payment-razorpay/index.js +21 -1
  158. package/dist/providers/tax-india-gst/index.d.ts +45 -0
  159. package/dist/providers/tax-india-gst/index.js +78 -0
  160. package/dist/react/hooks/useScrollToTop.d.ts +5 -0
  161. package/dist/react/hooks/useScrollToTop.js +70 -0
  162. package/dist/schemas/registry.d.ts +2 -2
  163. package/dist/seo/json-ld.js +25 -4
  164. package/dist/styles.css +20 -0
  165. package/dist/ui/components/SiteLogo.d.ts +20 -2
  166. package/dist/ui/components/SiteLogo.js +28 -6
  167. package/dist/ui/components/SiteMark.d.ts +1 -1
  168. package/dist/ui/components/SiteMark.js +21 -6
  169. package/dist/ui/components/Tabs.d.ts +9 -1
  170. package/dist/ui/components/Tabs.js +20 -3
  171. package/dist/ui/components/Tabs.style.css +20 -0
  172. package/dist/ui/rich-text/RichText.d.ts +0 -12
  173. package/dist/ui/rich-text/RichText.js +52 -1
  174. package/dist/utils/date.formatter.d.ts +20 -0
  175. package/dist/utils/date.formatter.js +25 -0
  176. package/dist/utils/number.formatter.d.ts +47 -0
  177. package/dist/utils/number.formatter.js +79 -1
  178. package/dist/validation/phone.validator.d.ts +0 -1
  179. package/dist/validation/phone.validator.js +17 -3
  180. package/package.json +17 -3
  181. package/dist/features/about/components/AboutView.d.ts +0 -30
  182. package/dist/features/about/components/AboutView.js +0 -14
@@ -13,7 +13,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  *
14
14
  * 1. Almost no small seller will create a Meta app and mint a system user
15
15
  * token. A setting nobody can complete is a feature nobody has.
16
- * 2. It made LetItRip the custodian of a live Meta credential per seller.
16
+ * 2. It made {getBrandName()} the custodian of a live Meta credential per seller.
17
17
  * `StoreRepository.mapDoc` decrypted that token on EVERY read, so any
18
18
  * code path that forgot to project was handling plaintext — a gap
19
19
  * CLAUDE.md flags as deserving its own session.
@@ -48,6 +48,7 @@ import { Toggle } from "../../../ui/components/Toggle.js";
48
48
  import { useToast } from "../../../ui/components/toast-context.js";
49
49
  import { normalizeError } from "../../../errors/normalize.js";
50
50
  import { formatCurrency } from "../../../utils/number.formatter.js";
51
+ import { getBrandName } from "../../../core/brand-resolver.js";
51
52
  export function SellerWhatsAppSettingsView({ subscribed, monthlyFee, currency = "INR", channelEnabled, onToggleSubscription, hasCapability = true, }) {
52
53
  const [isOn, setIsOn] = useState(subscribed);
53
54
  const [saving, setSaving] = useState(false);
@@ -79,5 +80,5 @@ export function SellerWhatsAppSettingsView({ subscribed, monthlyFee, currency =
79
80
  setSaving(false);
80
81
  }
81
82
  };
82
- return (_jsx(Section, { children: _jsxs(Div, { className: "mx-auto max-w-2xl", children: [_jsx(Heading, { level: 1, className: "mb-1", color: "primary", size: "2xl", weight: "bold", children: "WhatsApp order updates" }), _jsx(Text, { variant: "secondary", className: "mb-6", size: "sm", children: "Send your buyers order updates on WhatsApp \u2014 confirmed, shipped, delivered \u2014 from LetItRip's verified business number. You don't need a WhatsApp Business account of your own, and nothing is installed on your phone." }), !channelEnabled && (_jsx(Alert, { variant: "warning", className: "mb-4", children: "WhatsApp updates are currently switched off platform-wide, so nothing will be sent even if you subscribe. We'll start sending as soon as it's back on." })), _jsxs(Stack, { gap: "md", surface: "card", padding: "md", children: [_jsxs(Row, { justify: "between", align: "center", gap: "md", children: [_jsxs(Div, { className: "flex-1 min-w-0", children: [_jsxs(Row, { gap: "sm", align: "center", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Subscription" }), _jsx(Badge, { variant: isOn ? "success" : "inactive", children: isOn ? "Active" : "Not subscribed" })] }), _jsxs(Text, { color: "muted", size: "sm", children: [formatCurrency(monthlyFee, currency), " per month. Covers every order update to every buyer \u2014 not charged per message."] })] }), _jsx(Toggle, { checked: isOn, onChange: handleToggle, disabled: saving || !hasCapability, size: "md" })] }), !hasCapability && (_jsx(Div, { textSize: "sm", className: "border border-warning/20", color: "warning", surface: "warning-surface", padding: "inlineSm", rounded: "lg", children: "Your store doesn't have this add-on enabled. Contact support to turn it on." }))] }), _jsx(Heading, { level: 2, className: "mt-8 mb-3", size: "lg", weight: "semibold", children: "What your buyers get" }), _jsxs(Stack, { gap: "sm", surface: "card", padding: "md", children: [_jsxs(Text, { color: "muted", size: "sm", children: ["A WhatsApp message when an order is ", _jsx(Span, { weight: "semibold", children: "confirmed" }), ",", " ", _jsx(Span, { weight: "semibold", children: "shipped" }), ", ", _jsx(Span, { weight: "semibold", children: "delivered" }), ",", " ", _jsx(Span, { weight: "semibold", children: "cancelled" }), " or ", _jsx(Span, { weight: "semibold", children: "refunded" }), ". Nothing promotional \u2014 buyers who opted out of WhatsApp never receive these."] }), _jsx(Text, { color: "muted", size: "sm", children: "Messages come from LetItRip's number, not yours, so your personal number stays private and replies land with our support team rather than on your phone." }), _jsx(Text, { color: "muted", size: "sm", children: "Turning this off changes nothing else: buyers keep getting the same updates by email and in their account." })] })] }) }));
83
+ return (_jsx(Section, { children: _jsxs(Div, { className: "mx-auto max-w-2xl", children: [_jsx(Heading, { level: 1, className: "mb-1", color: "primary", size: "2xl", weight: "bold", children: "WhatsApp order updates" }), _jsxs(Text, { variant: "secondary", className: "mb-6", size: "sm", children: ["Send your buyers order updates on WhatsApp \u2014 confirmed, shipped, delivered \u2014 from", getBrandName(), "'s verified business number. You don't need a WhatsApp Business account of your own, and nothing is installed on your phone."] }), !channelEnabled && (_jsx(Alert, { variant: "warning", className: "mb-4", children: "WhatsApp updates are currently switched off platform-wide, so nothing will be sent even if you subscribe. We'll start sending as soon as it's back on." })), _jsxs(Stack, { gap: "md", surface: "card", padding: "md", children: [_jsxs(Row, { justify: "between", align: "center", gap: "md", children: [_jsxs(Div, { className: "flex-1 min-w-0", children: [_jsxs(Row, { gap: "sm", align: "center", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Subscription" }), _jsx(Badge, { variant: isOn ? "success" : "inactive", children: isOn ? "Active" : "Not subscribed" })] }), _jsxs(Text, { color: "muted", size: "sm", children: [formatCurrency(monthlyFee, currency), " per month. Covers every order update to every buyer \u2014 not charged per message."] })] }), _jsx(Toggle, { checked: isOn, onChange: handleToggle, disabled: saving || !hasCapability, size: "md" })] }), !hasCapability && (_jsx(Div, { textSize: "sm", className: "border border-warning/20", color: "warning", surface: "warning-surface", padding: "inlineSm", rounded: "lg", children: "Your store doesn't have this add-on enabled. Contact support to turn it on." }))] }), _jsx(Heading, { level: 2, className: "mt-8 mb-3", size: "lg", weight: "semibold", children: "What your buyers get" }), _jsxs(Stack, { gap: "sm", surface: "card", padding: "md", children: [_jsxs(Text, { color: "muted", size: "sm", children: ["A WhatsApp message when an order is ", _jsx(Span, { weight: "semibold", children: "confirmed" }), ",", " ", _jsx(Span, { weight: "semibold", children: "shipped" }), ", ", _jsx(Span, { weight: "semibold", children: "delivered" }), ",", " ", _jsx(Span, { weight: "semibold", children: "cancelled" }), " or ", _jsx(Span, { weight: "semibold", children: "refunded" }), ". Nothing promotional \u2014 buyers who opted out of WhatsApp never receive these."] }), _jsxs(Text, { color: "muted", size: "sm", children: ["Messages come from ", getBrandName(), "'s number, not yours, so your personal number stays private and replies land with our support team rather than on your phone."] }), _jsx(Text, { color: "muted", size: "sm", children: "Turning this off changes nothing else: buyers keep getting the same updates by email and in their account." })] })] }) }));
83
84
  }
@@ -1,6 +1,7 @@
1
1
  import { normalizeError } from "../../../errors/normalize.js";
2
2
  import { createHmac, timingSafeEqual } from "crypto";
3
3
  import { getDefaultCurrencySymbol, getDefaultLocale, } from "../../../core/baseline-resolver.js";
4
+ import { formatMoney } from "../../../utils/number.formatter.js";
4
5
  const META_GRAPH_BASE = "https://graph.facebook.com/v20.0";
5
6
  export function buildCheckoutMessageURL(input) {
6
7
  const { waNumber, cart, total, address, isPreorder = false } = input;
@@ -241,8 +242,10 @@ export async function syncProductsToCatalog(input) {
241
242
  export function buildPurchaseAnnouncementMessage(input) {
242
243
  const { buyerName, firstItemName, additionalItemCount, totalAmount, orderId } = input;
243
244
  const extra = additionalItemCount > 0 ? ` + ${additionalItemCount} more item${additionalItemCount > 1 ? "s" : ""}` : "";
244
- const amount = totalAmount.toLocaleString("en-IN");
245
- return `🛍️ New order! ${buyerName} purchased ${firstItemName}${extra} for ₹${amount}. Order #${orderId}`;
245
+ // `formatMoney` groups and places the symbol from the market profile; the
246
+ // intermediate `toLocaleString("en-IN")` this replaced hardcoded the locale.
247
+ const amount = formatMoney(totalAmount);
248
+ return `🛍️ New order! ${buyerName} purchased ${firstItemName}${extra} for ${amount}. Order #${orderId}`;
246
249
  }
247
250
  /**
248
251
  * Build a wa.me share link with a pre-filled message.
package/dist/index.d.ts CHANGED
@@ -286,7 +286,6 @@ export { isNumeric } from "./validation/input.validator";
286
286
  export { isRequired } from "./validation/input.validator";
287
287
  export { isValidEmail } from "./validation/email.validator";
288
288
  export { isValidEmailDomain } from "./validation/email.validator";
289
- export { isValidIndianPincode } from "./validation/phone.validator";
290
289
  export { isValidMobile } from "./validation/phone.validator";
291
290
  export { isValidPhone } from "./validation/phone.validator";
292
291
  export { isValidUrl } from "./validation/url.validator";
@@ -753,7 +752,6 @@ export { validateFileSize } from "./providers/storage-firebase/client";
753
752
  export { validateFileType } from "./providers/storage-firebase/client";
754
753
  export { validateImage } from "./providers/storage-firebase/client";
755
754
  export type { StorageHelpers } from "./providers/storage-firebase/client";
756
- export { AboutView } from "./features/about/components/AboutView";
757
755
  export { FAQPageView } from "./features/about/components/FAQPageView";
758
756
  export { FeesView } from "./features/about/components/FeesView";
759
757
  export { HelpPageView } from "./features/about/components/HelpPageView";
@@ -774,7 +772,6 @@ export { TrackOrderView } from "./features/about/components/TrackOrderView";
774
772
  export type { AboutHowItem } from "./features/about/schemas/firestore";
775
773
  export type { AboutMilestone } from "./features/about/schemas/firestore";
776
774
  export type { AboutValueItem } from "./features/about/schemas/firestore";
777
- export type { AboutViewProps } from "./features/about/components/AboutView";
778
775
  export type { AboutTeamMember } from "./features/about/schemas/firestore";
779
776
  export type { AboutContentDocument } from "./features/about/schemas/firestore";
780
777
  export type { FAQPageViewProps } from "./features/about/components/FAQPageView";
package/dist/index.js CHANGED
@@ -447,8 +447,6 @@ export { isValidEmail } from "./validation/email.validator.js";
447
447
  // isValidEmailDomain - Shared export for is valid email domain.
448
448
  export { isValidEmailDomain } from "./validation/email.validator.js";
449
449
  // [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
450
- // isValidIndianPincode - Shared export for is valid indian pincode.
451
- export { isValidIndianPincode } from "./validation/phone.validator.js";
452
450
  // [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
453
451
  // isValidMobile - Shared export for is valid mobile.
454
452
  export { isValidMobile } from "./validation/phone.validator.js";
@@ -1580,10 +1578,6 @@ export { validateFileType } from "./providers/storage-firebase/client.js";
1580
1578
  // [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
1581
1579
  // validateImage - Helper for validate image.
1582
1580
  export { validateImage } from "./providers/storage-firebase/client.js";
1583
- // ./features/about/index
1584
- // [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
1585
- // AboutView - Component for about view.
1586
- export { AboutView } from "./features/about/components/AboutView.js";
1587
1581
  // [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
1588
1582
  // FAQPageView - Component for faqpage view.
1589
1583
  export { FAQPageView } from "./features/about/components/FAQPageView.js";
@@ -25,6 +25,17 @@ export declare class ManualPaymentProvider extends IPaymentProvider {
25
25
  createOrder(amount: number, currency: string, metadata?: Record<string, JsonValue>): Promise<PaymentOrder>;
26
26
  /** Manual payments have no webhook source — always false. */
27
27
  verifyWebhook(_payload: string, _signature: string): boolean;
28
+ /**
29
+ * Manual payment has no gateway callback to verify, so there is nothing that
30
+ * could make this true.
31
+ *
32
+ * 🛑 `false`, never `true`. This is the check that decides whether a payment
33
+ * is genuine; a provider that cannot verify must REJECT, not wave through.
34
+ * Returning `true` here would mean a forged gateway callback is accepted on
35
+ * any install where the gateway flag is off — which is exactly the install
36
+ * where nobody is watching for one.
37
+ */
38
+ verifyCheckoutSignature(_payload: Record<string, string>, _secret: string): boolean;
28
39
  /**
29
40
  * Manual payments are considered captured the moment the buyer's
30
41
  * reference/proof is recorded — there is no separate auth+capture step.
@@ -40,6 +40,19 @@ export class ManualPaymentProvider extends IPaymentProvider {
40
40
  verifyWebhook(_payload, _signature) {
41
41
  return false;
42
42
  }
43
+ /**
44
+ * Manual payment has no gateway callback to verify, so there is nothing that
45
+ * could make this true.
46
+ *
47
+ * 🛑 `false`, never `true`. This is the check that decides whether a payment
48
+ * is genuine; a provider that cannot verify must REJECT, not wave through.
49
+ * Returning `true` here would mean a forged gateway callback is accepted on
50
+ * any install where the gateway flag is off — which is exactly the install
51
+ * where nobody is watching for one.
52
+ */
53
+ verifyCheckoutSignature(_payload, _secret) {
54
+ return false;
55
+ }
43
56
  /**
44
57
  * Manual payments are considered captured the moment the buyer's
45
58
  * reference/proof is recorded — there is no separate auth+capture step.
@@ -38,7 +38,7 @@
38
38
  */
39
39
  import type { JsonValue } from "@mohasinac/appkit";
40
40
  import { IPaymentProvider } from "../../contracts";
41
- import type { PaymentOrder, PaymentCapture, Refund, Dispute, GatewayPaymentRecord } from "../../contracts";
41
+ import type { CheckoutIntentMetadata, PaymentOrder, PaymentCapture, Refund, Dispute, GatewayPaymentRecord } from "../../contracts";
42
42
  /**
43
43
  * PhonePe's `metaInfo` (udf1-udf5, ≤256 chars each) is the only channel that
44
44
  * survives from `createOrder()` through to BOTH the Order Status API AND the
@@ -48,12 +48,15 @@ import type { PaymentOrder, PaymentCapture, Refund, Dispute, GatewayPaymentRecor
48
48
  * the tab mid-payment. See `confirmAndPlacePhonePeOrderAction` in
49
49
  * `_internal/server/features/checkout/actions.ts`.
50
50
  */
51
- export interface PhonePeCheckoutIntent {
52
- uid: string;
53
- addressId?: string;
54
- outOfStockPolicy?: string;
55
- notes?: string;
56
- }
51
+ /**
52
+ * @deprecated Use `CheckoutIntentMetadata` from `contracts/payment`.
53
+ *
54
+ * Kept as an alias so existing imports keep compiling. Every field is ours —
55
+ * the only PhonePe-specific thing was the name, and it forced generic checkout
56
+ * code to import this provider for a TYPE (A4). PhonePe carries it in
57
+ * `udf1..udf4`; that is a packing detail, not a definition.
58
+ */
59
+ export type PhonePeCheckoutIntent = CheckoutIntentMetadata;
57
60
  export interface PhonePeConfig {
58
61
  clientId: string;
59
62
  clientSecret: string;
@@ -97,6 +100,19 @@ export declare class PhonePeProvider extends IPaymentProvider {
97
100
  * the existing webhook route already does.
98
101
  */
99
102
  verifyWebhook(payload: string, signature: string): boolean;
103
+ /**
104
+ * PhonePe has no buyer-present SIGNED callback of this shape.
105
+ *
106
+ * Its redirect carries no signature — confirmation comes from the Order
107
+ * Status API or the webhook (`verifyWebhook` above, which does the real
108
+ * `validateCallback`). So there is nothing here to verify, and the honest
109
+ * answer is rejection.
110
+ *
111
+ * 🛑 `false`, never `true`. "No signature to check" and "the signature is
112
+ * good" must never collapse into the same return value on the call that
113
+ * decides whether a payment is genuine.
114
+ */
115
+ verifyCheckoutSignature(_payload: Record<string, string>, _secret: string): boolean;
100
116
  /** Wraps the Order Status API. `orderId` here is our `merchantOrderId`. */
101
117
  capturePayment(orderId: string): Promise<PaymentCapture>;
102
118
  /**
@@ -158,6 +158,21 @@ export class PhonePeProvider extends IPaymentProvider {
158
158
  return false;
159
159
  }
160
160
  }
161
+ /**
162
+ * PhonePe has no buyer-present SIGNED callback of this shape.
163
+ *
164
+ * Its redirect carries no signature — confirmation comes from the Order
165
+ * Status API or the webhook (`verifyWebhook` above, which does the real
166
+ * `validateCallback`). So there is nothing here to verify, and the honest
167
+ * answer is rejection.
168
+ *
169
+ * 🛑 `false`, never `true`. "No signature to check" and "the signature is
170
+ * good" must never collapse into the same return value on the call that
171
+ * decides whether a payment is genuine.
172
+ */
173
+ verifyCheckoutSignature(_payload, _secret) {
174
+ return false;
175
+ }
161
176
  /** Wraps the Order Status API. `orderId` here is our `merchantOrderId`. */
162
177
  async capturePayment(orderId) {
163
178
  const status = await this.client.getOrderStatus(orderId, true);
@@ -89,6 +89,19 @@ export declare class RazorpayProvider extends IPaymentProvider {
89
89
  * is not guaranteed to reproduce the bytes that were signed.
90
90
  */
91
91
  verifyWebhook(payload: string, signature: string): boolean;
92
+ /**
93
+ * The buyer-present callback from `checkout.js`.
94
+ *
95
+ * 🛑 Delegates to the SAME `verifyPaymentSignature` the checkout action
96
+ * called directly before A4b — identical HMAC, identical `hexEqual`
97
+ * comparison, identical `if (!keySecret) return false`. The dispatch moved
98
+ * behind the contract; the arithmetic did not move at all. That is what let
99
+ * this be proven equivalent rather than argued about.
100
+ *
101
+ * The three fields are checked before the cast: a callback missing any of
102
+ * them cannot be verified, and "cannot verify" is rejection.
103
+ */
104
+ verifyCheckoutSignature(payload: Record<string, string>, secret: string): boolean;
92
105
  /**
93
106
  * Resolves a `pay_…` id for either id shape.
94
107
  *
@@ -54,7 +54,9 @@ import Razorpay from "razorpay";
54
54
  import { IPaymentProvider } from "../../contracts/index.js";
55
55
  import { getDefaultCurrency } from "../../core/baseline-resolver.js";
56
56
  import { paiseToRupees } from "../../core/money.js";
57
- import { verifyWebhookSignature } from "./signature.js";
57
+ // `verifyPaymentSignature` is re-exported below, but a re-export does not bring
58
+ // it into THIS module's scope — `verifyCheckoutSignature` calls it directly.
59
+ import { verifyPaymentSignature, verifyWebhookSignature } from "./signature.js";
58
60
  export { verifyPaymentSignature, verifyWebhookSignature, } from "./signature.js";
59
61
  /** Razorpay's own order status vocabulary → the contract's. */
60
62
  function mapOrderStatus(status) {
@@ -144,6 +146,24 @@ export class RazorpayProvider extends IPaymentProvider {
144
146
  return false;
145
147
  return verifyWebhookSignature(payload, signature, this.webhookSecret);
146
148
  }
149
+ /**
150
+ * The buyer-present callback from `checkout.js`.
151
+ *
152
+ * 🛑 Delegates to the SAME `verifyPaymentSignature` the checkout action
153
+ * called directly before A4b — identical HMAC, identical `hexEqual`
154
+ * comparison, identical `if (!keySecret) return false`. The dispatch moved
155
+ * behind the contract; the arithmetic did not move at all. That is what let
156
+ * this be proven equivalent rather than argued about.
157
+ *
158
+ * The three fields are checked before the cast: a callback missing any of
159
+ * them cannot be verified, and "cannot verify" is rejection.
160
+ */
161
+ verifyCheckoutSignature(payload, secret) {
162
+ const { razorpay_order_id, razorpay_payment_id, razorpay_signature } = payload;
163
+ if (!razorpay_order_id || !razorpay_payment_id || !razorpay_signature)
164
+ return false;
165
+ return verifyPaymentSignature({ razorpay_order_id, razorpay_payment_id, razorpay_signature }, secret);
166
+ }
147
167
  /**
148
168
  * Resolves a `pay_…` id for either id shape.
149
169
  *
@@ -0,0 +1,45 @@
1
+ /**
2
+ * India GST — the default `ITaxProvider`.
3
+ *
4
+ * ## 🛑 This DELEGATES. Every figure still comes from `calculateGst` /
5
+ * `calculateGstInclusive`.
6
+ *
7
+ * Not a rewrite, on purpose. Those two functions split the intra-state half
8
+ * differently and the difference is load-bearing:
9
+ *
10
+ * calculateGst cgst = sgst = round(gst / 2); gst RE-DERIVED as half*2
11
+ * calculateGstInclusive cgst = round(gst / 2); sgst = round(gst - cgst)
12
+ *
13
+ * Measured on ₹66.67 at 18% inclusive: cgst 5.09, sgst 5.08 — the remainder
14
+ * lands in sgst so the parts still sum to 10.17. Reimplementing the maths here
15
+ * to make the two agree would move a paisa on figures GST is filed on. The
16
+ * seam's whole value is that it adds an abstraction WITHOUT touching arithmetic.
17
+ *
18
+ * Verified by `.build-memory/tax-roundtrip.mjs` — 397 breakdowns across every
19
+ * slab, both supply directions and the rounding edges, byte-identical.
20
+ *
21
+ * @tag domain:tax
22
+ * @tag layer:provider
23
+ * @tag access:isomorphic
24
+ */
25
+ import { ITaxProvider, type TaxBreakdown, type TaxJurisdiction, type TaxableItem } from "../../contracts/tax";
26
+ export declare class IndiaGstTaxProvider extends ITaxProvider {
27
+ readonly id = "india-gst";
28
+ /**
29
+ * Intra-state when origin and destination states match.
30
+ *
31
+ * 🛑 Absent information is treated as INTRA-state, which preserves the
32
+ * existing behaviour exactly rather than improving on it. Every current
33
+ * caller passes an `intraState` boolean it computed itself, and the defaults
34
+ * across those call sites resolve to intra-state when a place of supply is
35
+ * unknown — changing that here would alter tax on orders with an incomplete
36
+ * delivery snapshot, which is precisely the class of row most likely to
37
+ * exist. A stricter rule is a product decision, not a side effect of adding
38
+ * an interface.
39
+ */
40
+ isSameRegion(jurisdiction: TaxJurisdiction): boolean;
41
+ onExclusive(item: TaxableItem, jurisdiction: TaxJurisdiction, taxableAmount: number): TaxBreakdown;
42
+ onInclusive(item: TaxableItem, jurisdiction: TaxJurisdiction, gross: number): TaxBreakdown;
43
+ }
44
+ /** The default instance. A consumer in another jurisdiction registers its own. */
45
+ export declare const indiaGstTaxProvider: IndiaGstTaxProvider;
@@ -0,0 +1,78 @@
1
+ /**
2
+ * India GST — the default `ITaxProvider`.
3
+ *
4
+ * ## 🛑 This DELEGATES. Every figure still comes from `calculateGst` /
5
+ * `calculateGstInclusive`.
6
+ *
7
+ * Not a rewrite, on purpose. Those two functions split the intra-state half
8
+ * differently and the difference is load-bearing:
9
+ *
10
+ * calculateGst cgst = sgst = round(gst / 2); gst RE-DERIVED as half*2
11
+ * calculateGstInclusive cgst = round(gst / 2); sgst = round(gst - cgst)
12
+ *
13
+ * Measured on ₹66.67 at 18% inclusive: cgst 5.09, sgst 5.08 — the remainder
14
+ * lands in sgst so the parts still sum to 10.17. Reimplementing the maths here
15
+ * to make the two agree would move a paisa on figures GST is filed on. The
16
+ * seam's whole value is that it adds an abstraction WITHOUT touching arithmetic.
17
+ *
18
+ * Verified by `.build-memory/tax-roundtrip.mjs` — 397 breakdowns across every
19
+ * slab, both supply directions and the rounding edges, byte-identical.
20
+ *
21
+ * @tag domain:tax
22
+ * @tag layer:provider
23
+ * @tag access:isomorphic
24
+ */
25
+ import { ITaxProvider, } from "../../contracts/tax.js";
26
+ import { calculateGst, calculateGstInclusive, } from "../../_internal/shared/fees/calculator.js";
27
+ /** CGST and SGST are each HALF the headline rate; IGST carries all of it. */
28
+ function componentsFrom(breakdown, rate) {
29
+ const half = rate / 2;
30
+ if (breakdown.igst > 0 || (breakdown.cgst === 0 && breakdown.sgst === 0 && breakdown.gstAmount > 0)) {
31
+ return [{ code: "igst", label: "IGST", rate, amount: breakdown.igst }];
32
+ }
33
+ if (breakdown.gstAmount === 0)
34
+ return [];
35
+ return [
36
+ { code: "cgst", label: "CGST", rate: half, amount: breakdown.cgst },
37
+ { code: "sgst", label: "SGST", rate: half, amount: breakdown.sgst },
38
+ ];
39
+ }
40
+ function toBreakdown(breakdown, rate) {
41
+ return {
42
+ taxableAmount: breakdown.taxableAmount,
43
+ taxAmount: breakdown.gstAmount,
44
+ components: componentsFrom(breakdown, rate),
45
+ };
46
+ }
47
+ export class IndiaGstTaxProvider extends ITaxProvider {
48
+ constructor() {
49
+ super(...arguments);
50
+ this.id = "india-gst";
51
+ }
52
+ /**
53
+ * Intra-state when origin and destination states match.
54
+ *
55
+ * 🛑 Absent information is treated as INTRA-state, which preserves the
56
+ * existing behaviour exactly rather than improving on it. Every current
57
+ * caller passes an `intraState` boolean it computed itself, and the defaults
58
+ * across those call sites resolve to intra-state when a place of supply is
59
+ * unknown — changing that here would alter tax on orders with an incomplete
60
+ * delivery snapshot, which is precisely the class of row most likely to
61
+ * exist. A stricter rule is a product decision, not a side effect of adding
62
+ * an interface.
63
+ */
64
+ isSameRegion(jurisdiction) {
65
+ const { origin, destination } = jurisdiction;
66
+ if (!origin || !destination)
67
+ return true;
68
+ return origin.trim().toUpperCase() === destination.trim().toUpperCase();
69
+ }
70
+ onExclusive(item, jurisdiction, taxableAmount) {
71
+ return toBreakdown(calculateGst(item.rate, this.isSameRegion(jurisdiction), taxableAmount), item.rate);
72
+ }
73
+ onInclusive(item, jurisdiction, gross) {
74
+ return toBreakdown(calculateGstInclusive(item.rate, this.isSameRegion(jurisdiction), gross), item.rate);
75
+ }
76
+ }
77
+ /** The default instance. A consumer in another jurisdiction registers its own. */
78
+ export const indiaGstTaxProvider = new IndiaGstTaxProvider();
@@ -0,0 +1,5 @@
1
+ export interface UseScrollToTopOptions {
2
+ /** Skip scrolling without unmounting the caller. Default `true`. */
3
+ enabled?: boolean;
4
+ }
5
+ export declare function useScrollToTop(key: string | null | undefined, { enabled }?: UseScrollToTopOptions): void;
@@ -0,0 +1,70 @@
1
+ "use client";
2
+ /**
3
+ * useScrollToTop — jump to the top whenever a key changes.
4
+ *
5
+ * ## Why a HOOK and not a `<ScrollToTop />` component
6
+ *
7
+ * The consumer had a 17-line component doing exactly this. Lifting it as a
8
+ * component would not work: the only interesting line is
9
+ * `usePathname()` from `@/i18n/navigation`, and that is the consumer's own
10
+ * routing choice (next-intl, locale-prefixed). appkit cannot import it, and
11
+ * taking a `pathname` prop would mean a component whose entire job is to pass
12
+ * one value to one effect.
13
+ *
14
+ * So the mechanism moves and the binding stays: the consumer keeps a four-line
15
+ * mount that supplies its own `usePathname()`.
16
+ *
17
+ * ## 🛑 `behavior: "auto"`, deliberately
18
+ *
19
+ * Not `"smooth"`. A route change should look instantaneous; smooth-scrolling a
20
+ * fresh page animates content the reader has not seen yet, and on a long page
21
+ * the animation is still running when they start reading. It also ignores
22
+ * `prefers-reduced-motion`, which `"auto"` sidesteps entirely by not animating.
23
+ *
24
+ * ## 🛑 Does NOT fire on mount
25
+ *
26
+ * `useEffect` with a dependency runs on mount too, which would fight the
27
+ * browser's own scroll restoration on a back-navigation — the reader returns to
28
+ * a list, the browser restores their place, and this yanks them to the top.
29
+ * The first run is skipped so only genuine changes scroll.
30
+ *
31
+ * That is a behaviour FIX, not a port: the consumer's component scrolled on
32
+ * mount. It was unnoticeable there because Next remounts the tree on a hard
33
+ * load anyway, but it is wrong for a hook anyone may mount mid-tree.
34
+ *
35
+ * @example
36
+ * ```tsx
37
+ * "use client";
38
+ * import { usePathname } from "@/i18n/navigation";
39
+ * import { useScrollToTop } from "@mohasinac/appkit/react/hooks/useScrollToTop";
40
+ *
41
+ * export function ScrollToTop() {
42
+ * useScrollToTop(usePathname());
43
+ * return null;
44
+ * }
45
+ * ```
46
+ *
47
+ * @tag domain:routing
48
+ * @tag layer:client
49
+ * @tag access:client
50
+ * @tag sideEffects:dom
51
+ */
52
+ import { useEffect, useRef } from "react";
53
+ export function useScrollToTop(key, { enabled = true } = {}) {
54
+ const previous = useRef(undefined);
55
+ useEffect(() => {
56
+ if (!enabled)
57
+ return;
58
+ // First run: record and do nothing — see the note on scroll restoration.
59
+ if (previous.current === undefined) {
60
+ previous.current = key;
61
+ return;
62
+ }
63
+ if (previous.current === key)
64
+ return;
65
+ previous.current = key;
66
+ if (typeof window === "undefined")
67
+ return;
68
+ window.scrollTo({ top: 0, left: 0, behavior: "auto" });
69
+ }, [key, enabled]);
70
+ }
@@ -4608,7 +4608,7 @@ export declare const SCHEMAS: {
4608
4608
  status: "open" | "closed" | "in_progress" | "waiting_on_user" | "resolved";
4609
4609
  id: string;
4610
4610
  description: string;
4611
- category: "general" | "account" | "order_issue" | "billing_payment" | "listing_dispute" | "scam_report" | "refund_request" | "auction_dispute" | "store_change_request" | "order_modification_request" | "unban_request";
4611
+ category: "general" | "order_issue" | "billing_payment" | "account" | "listing_dispute" | "scam_report" | "refund_request" | "auction_dispute" | "store_change_request" | "order_modification_request" | "unban_request";
4612
4612
  createdAt: string | Date | import("zod").objectOutputType<{
4613
4613
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
4614
4614
  }, import("zod").ZodTypeAny, "passthrough">;
@@ -4651,7 +4651,7 @@ export declare const SCHEMAS: {
4651
4651
  status: "open" | "closed" | "in_progress" | "waiting_on_user" | "resolved";
4652
4652
  id: string;
4653
4653
  description: string;
4654
- category: "general" | "account" | "order_issue" | "billing_payment" | "listing_dispute" | "scam_report" | "refund_request" | "auction_dispute" | "store_change_request" | "order_modification_request" | "unban_request";
4654
+ category: "general" | "order_issue" | "billing_payment" | "account" | "listing_dispute" | "scam_report" | "refund_request" | "auction_dispute" | "store_change_request" | "order_modification_request" | "unban_request";
4655
4655
  createdAt: string | Date | import("zod").objectInputType<{
4656
4656
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
4657
4657
  }, import("zod").ZodTypeAny, "passthrough">;
@@ -13,7 +13,27 @@
13
13
  * ```
14
14
  */
15
15
  const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL ?? "";
16
- const SITE_NAME = process.env.NEXT_PUBLIC_SITE_NAME ?? "App";
16
+ /**
17
+ * The brand name, from the injected identity rather than an env var.
18
+ *
19
+ * 🛑 A FUNCTION, not a module `const`, and that matters twice over. This was
20
+ * `const SITE_NAME = process.env.NEXT_PUBLIC_SITE_NAME ?? "App"`, which:
21
+ *
22
+ * - captured its value at IMPORT time, so even after `configureBrand()` runs
23
+ * it would have kept whatever it read first; and
24
+ * - read the env var this file's own comment at `SiteIdentityOptions` records
25
+ * as having shipped `"Letitrip"` to production — the wrong casing, in the
26
+ * `Organization.name` Google may put in a Knowledge Panel.
27
+ *
28
+ * That comment fixed the two site-wide nodes by taking `opts.siteName`. It did
29
+ * not fix `publisher` below, which has no opts path, so every article still
30
+ * emitted the env value. Resolving through `getBrandName()` closes both: there
31
+ * is now one owner (`appkit.config.js → brand`) and no mis-typeable env var in
32
+ * the path.
33
+ */
34
+ function siteName() {
35
+ return getBrandName();
36
+ }
17
37
  /**
18
38
  * schema.org requires ABSOLUTE URLs. A relative `image` or `item` is not a
19
39
  * lenient-parse case — Google rejects the property, and `image` is *required*
@@ -57,6 +77,7 @@ function absoluteUrl(value, base = SITE_URL) {
57
77
  return `${base.replace(/\/+$/, "")}/${v.replace(/^\/+/, "")}`;
58
78
  }
59
79
  import { ProductStatusValues } from "../features/products/schemas/index.js";
80
+ import { getBrandName } from "../core/brand-resolver.js";
60
81
  import { getDefaultCurrency } from "../core/baseline-resolver.js";
61
82
  // The defining module, never the `/client` entry: this file is reached from
62
83
  // server-only metadata paths and must not pull in a `"use client"` module
@@ -252,12 +273,12 @@ export function blogPostJsonLd(post) {
252
273
  author: post.authorName
253
274
  ? { "@type": "Person", name: post.authorName, image: post.authorAvatar }
254
275
  : undefined,
255
- publisher: { "@type": "Organization", name: SITE_NAME, url: SITE_URL },
276
+ publisher: { "@type": "Organization", name: siteName(), url: SITE_URL },
256
277
  mainEntityOfPage: { "@type": "WebPage", "@id": url },
257
278
  };
258
279
  }
259
280
  export function organizationJsonLd(opts) {
260
- const name = opts?.siteName?.trim() || SITE_NAME;
281
+ const name = opts?.siteName?.trim() || siteName();
261
282
  const base = (opts?.siteUrl?.trim() || SITE_URL).replace(/\/+$/, "");
262
283
  // `sameAs: []` was hardcoded empty while appkit.config.js carried real
263
284
  // socialUrls that nothing read. An empty array is worse than omitting the key.
@@ -305,7 +326,7 @@ export function organizationJsonLd(opts) {
305
326
  };
306
327
  }
307
328
  export function searchBoxJsonLd(opts) {
308
- const name = opts?.siteName?.trim() || SITE_NAME;
329
+ const name = opts?.siteName?.trim() || siteName();
309
330
  const base = (opts?.siteUrl?.trim() || SITE_URL).replace(/\/+$/, "");
310
331
  return {
311
332
  "@context": "https://schema.org",
package/dist/styles.css CHANGED
@@ -7444,6 +7444,26 @@
7444
7444
  color: var(--appkit-tabs-ink-active);
7445
7445
  }
7446
7446
 
7447
+ /*
7448
+ * Wrapping tab bar — `<TabsList wrap>`.
7449
+ *
7450
+ * 🛑 For CONTENT tabs inside a page section, never for navigation. The
7451
+ * default list scrolls horizontally and collapses into a dropdown once it
7452
+ * overflows, which is right for a nav bar on a phone and wrong in the middle
7453
+ * of a homepage section: there it renders as a stray form control, and the
7454
+ * visitor cannot see what the section contains without opening it.
7455
+ *
7456
+ * Wrapping keeps every tab visible on a second line. `overflow: visible` is
7457
+ * required alongside `flex-wrap` — the base rule's `overflow-x: auto` would
7458
+ * otherwise still clip the wrapped rows and reintroduce a scrollbar.
7459
+ */
7460
+ .appkit-tabs-list--wrap {
7461
+ flex-wrap: wrap;
7462
+ overflow-x: visible;
7463
+ overflow-y: visible;
7464
+ scrollbar-width: auto;
7465
+ }
7466
+
7447
7467
  /* Radio — toggle and classic radio group variants */
7448
7468
 
7449
7469
  .appkit-radio-group {
@@ -15,8 +15,26 @@ export interface SiteLogoProps {
15
15
  * dark-primary surfaces in light mode etc.
16
16
  */
17
17
  tone?: SiteLogoTone;
18
- /** Accessible title; defaults to "LetItRip.in". */
18
+ /** Accessible title. Defaults to `wordmark` + `tld`, so one prop usually suffices. */
19
19
  title?: string;
20
+ /**
21
+ * The wordmark GLYPHS. Defaults to `title`.
22
+ *
23
+ * 🛑 Separate from `title` because they are different things and conflating
24
+ * them is what hid the brand here. `title` is the accessible name; this is
25
+ * the text actually drawn in the SVG — and until A5 that text was the literal
26
+ * `LetItRip`, unreachable by any prop. A consumer could rename the accessible
27
+ * title and still render someone else's brand.
28
+ */
29
+ wordmark?: string;
30
+ /**
31
+ * Optional TLD badge drawn in a pill after the wordmark, e.g. `"in"`.
32
+ *
33
+ * Omitted by default: a library must not assume its consumer has a TLD worth
34
+ * showing, let alone which one. `TitleBarLayout` threads `brandTld` through
35
+ * for the installs that want it.
36
+ */
37
+ tld?: string;
20
38
  /**
21
39
  * Optional admin-uploaded image URL. When present, renders an `<MediaImage>`
22
40
  * instead of the SVG wordmark (so the watermark proxy applies). Falls back
@@ -24,4 +42,4 @@ export interface SiteLogoProps {
24
42
  */
25
43
  src?: string;
26
44
  }
27
- export declare function SiteLogo({ size, tone, title, src, }: SiteLogoProps): import("react").JSX.Element;
45
+ export declare function SiteLogo({ size, tone, title, wordmark, tld, src, }: SiteLogoProps): import("react").JSX.Element;