@samva/markup 0.1.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 (124) hide show
  1. package/AGENTS.md +8 -0
  2. package/LICENSE +21 -0
  3. package/README.md +41 -0
  4. package/dist/ast-DAhnAvQN.d.ts +120 -0
  5. package/dist/ast.d.ts +2 -0
  6. package/dist/ast.js +159 -0
  7. package/dist/ast.js.map +1 -0
  8. package/dist/candidate.d.ts +29 -0
  9. package/dist/candidate.js +88 -0
  10. package/dist/candidate.js.map +1 -0
  11. package/dist/checks-CW0nFj5n.js +58922 -0
  12. package/dist/checks-CW0nFj5n.js.map +1 -0
  13. package/dist/checks.d.ts +26 -0
  14. package/dist/checks.js +2 -0
  15. package/dist/classes.d.ts +27 -0
  16. package/dist/classes.js +184 -0
  17. package/dist/classes.js.map +1 -0
  18. package/dist/compile-sms.d.ts +28 -0
  19. package/dist/compile-sms.js +44 -0
  20. package/dist/compile-sms.js.map +1 -0
  21. package/dist/compile-whatsapp.d.ts +56 -0
  22. package/dist/compile-whatsapp.js +186 -0
  23. package/dist/compile-whatsapp.js.map +1 -0
  24. package/dist/compile.d.ts +93 -0
  25. package/dist/compile.js +938 -0
  26. package/dist/compile.js.map +1 -0
  27. package/dist/components.d.ts +289 -0
  28. package/dist/components.js +106 -0
  29. package/dist/components.js.map +1 -0
  30. package/dist/conditionals.d.ts +10 -0
  31. package/dist/conditionals.js +42 -0
  32. package/dist/conditionals.js.map +1 -0
  33. package/dist/diagnostics-oveE-laV.d.ts +79 -0
  34. package/dist/diagnostics.d.ts +2 -0
  35. package/dist/diagnostics.js +62 -0
  36. package/dist/diagnostics.js.map +1 -0
  37. package/dist/emit.d.ts +50 -0
  38. package/dist/emit.js +151 -0
  39. package/dist/emit.js.map +1 -0
  40. package/dist/footer.d.ts +66 -0
  41. package/dist/footer.js +90 -0
  42. package/dist/footer.js.map +1 -0
  43. package/dist/interpolate.d.ts +27 -0
  44. package/dist/interpolate.js +61 -0
  45. package/dist/interpolate.js.map +1 -0
  46. package/dist/jsx-dev-runtime.d.ts +10 -0
  47. package/dist/jsx-dev-runtime.js +7 -0
  48. package/dist/jsx-dev-runtime.js.map +1 -0
  49. package/dist/jsx-runtime-fSxcvlx4.d.ts +57 -0
  50. package/dist/jsx-runtime.d.ts +2 -0
  51. package/dist/jsx-runtime.js +102 -0
  52. package/dist/jsx-runtime.js.map +1 -0
  53. package/dist/lexer.d.ts +60 -0
  54. package/dist/lexer.js +269 -0
  55. package/dist/lexer.js.map +1 -0
  56. package/dist/model.d.ts +39 -0
  57. package/dist/model.js +90 -0
  58. package/dist/model.js.map +1 -0
  59. package/dist/ops.d.ts +208 -0
  60. package/dist/ops.js +546 -0
  61. package/dist/ops.js.map +1 -0
  62. package/dist/parser-yzjUOGhc.d.ts +387 -0
  63. package/dist/parser.d.ts +2 -0
  64. package/dist/parser.js +821 -0
  65. package/dist/parser.js.map +1 -0
  66. package/dist/predicate-DTtVtLK7.d.ts +49 -0
  67. package/dist/predicate.d.ts +2 -0
  68. package/dist/predicate.js +417 -0
  69. package/dist/predicate.js.map +1 -0
  70. package/dist/presets/category-mapping.d.ts +13 -0
  71. package/dist/presets/category-mapping.js +49 -0
  72. package/dist/presets/category-mapping.js.map +1 -0
  73. package/dist/presets/public-gallery.d.ts +490 -0
  74. package/dist/presets/public-gallery.js +563 -0
  75. package/dist/presets/public-gallery.js.map +1 -0
  76. package/dist/presets/public-handoff.d.ts +68 -0
  77. package/dist/presets/public-handoff.js +105 -0
  78. package/dist/presets/public-handoff.js.map +1 -0
  79. package/dist/presets/public-preview-artifacts.d.ts +41 -0
  80. package/dist/presets/public-preview-artifacts.js +109 -0
  81. package/dist/presets/public-preview-artifacts.js.map +1 -0
  82. package/dist/presets/public-preview.d.ts +1168 -0
  83. package/dist/presets/public-preview.js +1158 -0
  84. package/dist/presets/public-preview.js.map +1 -0
  85. package/dist/presets/sources.d.ts +7 -0
  86. package/dist/presets/sources.js +54 -0
  87. package/dist/presets/sources.js.map +1 -0
  88. package/dist/presets.d.ts +350 -0
  89. package/dist/presets.js +1251 -0
  90. package/dist/presets.js.map +1 -0
  91. package/dist/printer.d.ts +6 -0
  92. package/dist/printer.js +122 -0
  93. package/dist/printer.js.map +1 -0
  94. package/dist/registry.d.ts +42 -0
  95. package/dist/registry.js +646 -0
  96. package/dist/registry.js.map +1 -0
  97. package/dist/render-template.d.ts +42 -0
  98. package/dist/render-template.js +74 -0
  99. package/dist/render-template.js.map +1 -0
  100. package/dist/sms-segments-y4FpPilJ.d.ts +13 -0
  101. package/dist/sms-segments.d.ts +2 -0
  102. package/dist/sms-segments.js +46 -0
  103. package/dist/sms-segments.js.map +1 -0
  104. package/dist/theme-6ApjjiBo.d.ts +116 -0
  105. package/dist/theme-css.d.ts +24 -0
  106. package/dist/theme-css.js +509 -0
  107. package/dist/theme-css.js.map +1 -0
  108. package/dist/theme.d.ts +2 -0
  109. package/dist/theme.js +95 -0
  110. package/dist/theme.js.map +1 -0
  111. package/dist/tokens.d.ts +6 -0
  112. package/dist/tokens.js +417 -0
  113. package/dist/tokens.js.map +1 -0
  114. package/dist/variables-3NGLpNTB.d.ts +77 -0
  115. package/dist/variables.d.ts +2 -0
  116. package/dist/variables.js +141 -0
  117. package/dist/variables.js.map +1 -0
  118. package/dist/version-BITr58kk.js +96 -0
  119. package/dist/version-BITr58kk.js.map +1 -0
  120. package/generated/sml-agent-contract.json +917 -0
  121. package/generated/sml-email-agent-contract.json +790 -0
  122. package/generated/sml-email-language-reference.md +160 -0
  123. package/generated/sml-language-reference.md +220 -0
  124. package/package.json +203 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets.js","names":[],"sources":["../presets/catalog.ts"],"sourcesContent":["import type { Channel } from \"../src/ast\";\nimport type { TokenOverrides } from \"../src/theme\";\n\n// Preset catalog: Samva-authored SML starter templates, the \"start from\n// something good\" path for a new org. Each entry is pure metadata — the\n// `.samva` source lives next to this file as `${id}.samva`, keeping this module\n// Worker-safe (no fs, no bundled source strings). The shape is public-registry\n// ready (a shadcn-for-email cut is deferred, not precluded): stable `id`,\n// `channel` (the gallery filters by it), `category`, human `name`/\n// `description`, and the `variables` a sender personalizes. `variables` is the\n// exact set of {{curly}} / ${dollar} references in the source — the test\n// asserts parity against collectVariableReferences, so this list never\n// silently drifts from the copy.\n\nexport type PresetCategory = \"transactional\" | \"digest\" | \"marketing\" | \"lifecycle\";\n\nexport interface PresetMeta {\n /** Stable id and source filename stem (`${id}.samva`). */\n readonly id: string;\n /** The source's root-tag channel; the gallery filters by it. */\n readonly channel: Channel;\n readonly category: PresetCategory;\n /** Human title for the gallery card. */\n readonly name: string;\n /** One-line description of when to reach for this preset. */\n readonly description: string;\n /**\n * Every variable the source interpolates, sorted. A sender fills these in;\n * an org theme re-themes the brand tokens. Kept in exact parity with\n * collectVariableReferences(source) by the preset test suite.\n */\n readonly variables: ReadonlyArray<string>;\n}\n\n// The brand tokens every preset is authored against. Presets style with\n// `bg-brand` / `text-brand-foreground` / `rounded-brand` where theming should\n// apply, so swapping this theme re-themes the whole gallery. Compiling a preset\n// without a theme that supplies these tokens is a hard error by design — the\n// tokens are the contract. This is the neutral default theme; a real org theme\n// overrides it.\nexport const RECOMMENDED_THEME: TokenOverrides = {\n colors: {\n brand: \"#4f46e5\",\n \"brand-foreground\": \"#ffffff\",\n },\n radiusPx: {\n brand: 8,\n },\n};\n\nexport const presetCatalog = [\n // ── Transactional ───────────────────────────────────────────────────────────\n {\n id: \"welcome\",\n channel: \"email\",\n category: \"transactional\",\n name: \"Welcome\",\n description:\n \"First email after signup — orient the new user and point them at their dashboard.\",\n variables: [\"ctaUrl\", \"docsUrl\", \"firstName\", \"logoUrl\", \"workspace\"],\n },\n {\n id: \"email-verification\",\n channel: \"email\",\n category: \"transactional\",\n name: \"Email verification\",\n description: \"Confirm a new email address with a one-click button and a fallback code.\",\n variables: [\"email\", \"firstName\", \"logoUrl\", \"verificationCode\", \"verifyUrl\", \"workspace\"],\n },\n {\n id: \"otp\",\n channel: \"email\",\n category: \"transactional\",\n name: \"One-time passcode\",\n description: \"Deliver a short-lived sign-in or 2FA code with a prominent, copyable value.\",\n variables: [\"code\", \"expiryMinutes\", \"logoUrl\", \"workspace\"],\n },\n {\n id: \"password-reset\",\n channel: \"email\",\n category: \"transactional\",\n name: \"Password reset\",\n description: \"Send a secure, expiring reset link with a paste-able fallback URL.\",\n variables: [\"expiryMinutes\", \"firstName\", \"logoUrl\", \"resetUrl\", \"workspace\"],\n },\n {\n id: \"receipt\",\n channel: \"email\",\n category: \"transactional\",\n name: \"Receipt\",\n description: \"Confirm a completed payment with an itemized summary and an invoice link.\",\n variables: [\n \"firstName\",\n \"invoiceUrl\",\n \"logoUrl\",\n \"orderNumber\",\n \"paidDate\",\n \"paymentMethod\",\n \"total\",\n \"workspace\",\n ],\n },\n {\n id: \"payment-failed\",\n channel: \"email\",\n category: \"transactional\",\n name: \"Payment failed\",\n description: \"Alert a customer to a declined charge and prompt them to update billing.\",\n variables: [\n \"amountDue\",\n \"contactUrl\",\n \"firstName\",\n \"logoUrl\",\n \"paymentMethod\",\n \"planName\",\n \"retryDate\",\n \"updatePaymentUrl\",\n \"workspace\",\n ],\n },\n {\n id: \"shipping-update\",\n channel: \"email\",\n category: \"transactional\",\n name: \"Shipping update\",\n description: \"Tell a customer their order shipped, with carrier, ETA, and a tracking link.\",\n variables: [\n \"carrier\",\n \"estimatedDelivery\",\n \"firstName\",\n \"logoUrl\",\n \"orderNumber\",\n \"trackingNumber\",\n \"trackingUrl\",\n \"workspace\",\n ],\n },\n {\n id: \"notification\",\n channel: \"email\",\n category: \"transactional\",\n name: \"Activity notification\",\n description: \"Notify a user that someone acted in their workspace, with a deep link back.\",\n variables: [\n \"actionSummary\",\n \"actorName\",\n \"ctaUrl\",\n \"logoUrl\",\n \"notificationBody\",\n \"settingsUrl\",\n \"timestamp\",\n \"workspace\",\n ],\n },\n {\n id: \"team-invite\",\n channel: \"email\",\n category: \"transactional\",\n name: \"Team invite\",\n description: \"Invite someone to collaborate in a workspace with a single accept action.\",\n variables: [\n \"acceptUrl\",\n \"inviteeEmail\",\n \"inviterEmail\",\n \"inviterName\",\n \"logoUrl\",\n \"productName\",\n \"workspace\",\n ],\n },\n\n {\n id: \"order-delivered\",\n channel: \"email\",\n category: \"transactional\",\n name: \"Order delivered\",\n description:\n \"Confirm a delivered order with the itemized contents, delivery address, tracking, and a reorder path.\",\n variables: [\n \"apparelUrl\",\n \"carrierName\",\n \"gearUrl\",\n \"homeUrl\",\n \"productOneImageUrl\",\n \"productOneName\",\n \"productOneQuantity\",\n \"productOneTotal\",\n \"productOneVariant\",\n \"productThreeImageUrl\",\n \"productThreeName\",\n \"productThreeQuantity\",\n \"productThreeTotal\",\n \"productThreeVariant\",\n \"productTwoImageUrl\",\n \"productTwoName\",\n \"productTwoQuantity\",\n \"productTwoTotal\",\n \"productTwoVariant\",\n \"recipientName\",\n \"shippingAddressLine1\",\n \"shippingCityStateZip\",\n \"shippingCountry\",\n \"shopAllUrl\",\n \"socialFourIconUrl\",\n \"socialFourUrl\",\n \"socialOneIconUrl\",\n \"socialOneUrl\",\n \"socialThreeIconUrl\",\n \"socialThreeUrl\",\n \"socialTwoIconUrl\",\n \"socialTwoUrl\",\n \"trackOrderUrl\",\n \"trackingNumber\",\n \"trackingUrl\",\n \"unsubscribeUrl\",\n ],\n },\n {\n id: \"data-sharing-review\",\n channel: \"email\",\n category: \"transactional\",\n name: \"Data-sharing review\",\n description:\n \"Prompt a customer to review a third-party data connection and keep or remove it, with a reference code.\",\n variables: [\n \"accountSettingsUrl\",\n \"appDownloadIconUrl\",\n \"appDownloadUrl\",\n \"connectedServiceName\",\n \"contactUrl\",\n \"copyrightYear\",\n \"daysSinceConnection\",\n \"fraudCenterUrl\",\n \"fraudHelpUrl\",\n \"keepConnectionUrl\",\n \"privacyUrl\",\n \"recipientEmail\",\n \"referenceCode\",\n \"removeConnectionUrl\",\n \"signInIconUrl\",\n \"signInUrl\",\n \"socialFiveIconUrl\",\n \"socialFourIconUrl\",\n \"socialOneIconUrl\",\n \"socialThreeIconUrl\",\n \"socialTwoIconUrl\",\n ],\n },\n\n // ── Digest ──────────────────────────────────────────────────────────────────\n {\n id: \"product-digest\",\n channel: \"email\",\n category: \"digest\",\n name: \"Weekly digest\",\n description: \"Recap the week's activity as a scannable list of stories with read-more links.\",\n variables: [\n \"dashboardUrl\",\n \"highlightCount\",\n \"logoUrl\",\n \"storyOneSummary\",\n \"storyOneTitle\",\n \"storyOneUrl\",\n \"storyTwoSummary\",\n \"storyTwoTitle\",\n \"storyTwoUrl\",\n \"unsubscribeUrl\",\n \"weekRange\",\n \"workspace\",\n ],\n },\n {\n id: \"newsletter\",\n channel: \"email\",\n category: \"digest\",\n name: \"Newsletter\",\n description:\n \"An editorial newsletter with a hero story, secondary links, and subscribe controls.\",\n variables: [\n \"heroImage\",\n \"issueNumber\",\n \"leadBody\",\n \"leadHeadline\",\n \"leadUrl\",\n \"linkOneTitle\",\n \"linkOneUrl\",\n \"linkTwoTitle\",\n \"linkTwoUrl\",\n \"newsletterName\",\n \"publishDate\",\n \"subscribeUrl\",\n \"unsubscribeUrl\",\n ],\n },\n\n {\n id: \"editorial-newsletter\",\n channel: \"email\",\n category: \"digest\",\n name: \"Long-form newsletter\",\n description:\n \"Long-form editorial newsletter with a lead story, sponsor slot, and a reading list.\",\n variables: [\n \"advertiseEmailUrl\",\n \"authorAvatarUrl\",\n \"authorProfileUrl\",\n \"bookUrl\",\n \"commentIconUrl\",\n \"commentUrl\",\n \"communityUrl\",\n \"concordStoryUrl\",\n \"founderQuoteUrl\",\n \"heroImageUrl\",\n \"leakStoryUrl\",\n \"likeIconUrl\",\n \"likeUrl\",\n \"marketReportUrl\",\n \"northbeamStoryUrl\",\n \"platformCtaImageUrl\",\n \"podcastCastUrl\",\n \"podcastGenericUrl\",\n \"podcastImageUrl\",\n \"podcastStreamUrl\",\n \"postUrl\",\n \"publishDate\",\n \"quoteOfWeekUrl\",\n \"quoteSourceUrl\",\n \"readInAppIconUrl\",\n \"readInAppUrl\",\n \"reading1Url\",\n \"reading2Url\",\n \"reading3Url\",\n \"reading4Url\",\n \"reading5Url\",\n \"repostIconUrl\",\n \"repostUrl\",\n \"shareIconUrl\",\n \"shareMailIconUrl\",\n \"shareNotesIconUrl\",\n \"shareSocialOneIconUrl\",\n \"shareSocialTwoIconUrl\",\n \"shareUrl\",\n \"sponsorImageUrl\",\n \"sponsorUrl\",\n \"stagnationStoryUrl\",\n \"statOfWeek\",\n \"statStoryUrl\",\n \"subscribeUrl\",\n \"subscriptionPrice\",\n \"tipsEmailUrl\",\n \"unsubscribeUrl\",\n \"upgradeUrl\",\n \"usersStoryUrl\",\n \"valuationStoryUrl\",\n ],\n },\n {\n id: \"events-roundup\",\n channel: \"email\",\n category: \"digest\",\n name: \"Events roundup\",\n description:\n \"A dated events digest: a grid of upcoming shows with images, dates, prices, and per-event links.\",\n variables: [\n \"copyrightYear\",\n \"event10Date\",\n \"event10ImageUrl\",\n \"event10Price\",\n \"event10Url\",\n \"event11Date\",\n \"event11ImageUrl\",\n \"event11Price\",\n \"event11Url\",\n \"event1Date\",\n \"event1ImageUrl\",\n \"event1Price\",\n \"event1Url\",\n \"event2Date\",\n \"event2ImageUrl\",\n \"event2Price\",\n \"event2Url\",\n \"event3Date\",\n \"event3ImageUrl\",\n \"event3Price\",\n \"event3Url\",\n \"event4Date\",\n \"event4ImageUrl\",\n \"event4Price\",\n \"event4Url\",\n \"event5Date\",\n \"event5ImageUrl\",\n \"event5Price\",\n \"event5Url\",\n \"event6Date\",\n \"event6ImageUrl\",\n \"event6Price\",\n \"event6Url\",\n \"event7Date\",\n \"event7ImageUrl\",\n \"event7Price\",\n \"event7Url\",\n \"event8Date\",\n \"event8ImageUrl\",\n \"event8Price\",\n \"event8Url\",\n \"event9Date\",\n \"event9ImageUrl\",\n \"event9Price\",\n \"event9Url\",\n \"helpUrl\",\n \"moreEventsUrl\",\n \"socialFourIconUrl\",\n \"socialOneIconUrl\",\n \"socialThreeIconUrl\",\n \"socialTwoIconUrl\",\n \"termsUrl\",\n \"unsubscribeUrl\",\n ],\n },\n {\n id: \"industry-news-digest\",\n channel: \"email\",\n category: \"digest\",\n name: \"Industry news digest\",\n description:\n \"A dense industry news digest with lead stories, a jobs board, tools index, sponsors, and a workshop.\",\n variables: [\n \"advertiseUrl\",\n \"audienceSize\",\n \"bastionClaimsUrl\",\n \"bastionDealImageUrl\",\n \"bastionDealUrl\",\n \"bastionLeakUrl\",\n \"bastionReplyingUrl\",\n \"bastionReportedUrl\",\n \"bastionSayingUrl\",\n \"bastionStruckUrl\",\n \"bastionUnveilingUrl\",\n \"copyrightYear\",\n \"feedbackAverageUrl\",\n \"feedbackFailUrl\",\n \"feedbackGreatUrl\",\n \"freeSignupUrl\",\n \"jobGatebenchUrl\",\n \"jobLedgerUrl\",\n \"jobScalebenchUrl\",\n \"jobVaultbridgeUrl\",\n \"jobsIndexUrl\",\n \"latentPrivilegeImageUrl\",\n \"latentPrivilegePublishedUrl\",\n \"latentPrivilegeUrl\",\n \"moreNewsBetaUrl\",\n \"moreNewsComputeUrl\",\n \"moreNewsFlashUrl\",\n \"moreNewsHiredUrl\",\n \"moreNewsMedalUrl\",\n \"moreNewsPoachedUrl\",\n \"moreNewsQuillUrl\",\n \"moreNewsStanceUrl\",\n \"moreNewsUrl\",\n \"playbookTutorialImageUrl\",\n \"playbookTutorialUrl\",\n \"podcastUrl\",\n \"pulsebandAcquiringUrl\",\n \"pulsebandDealImageUrl\",\n \"pulsebandDealUrl\",\n \"pulsebandReviewsUrl\",\n \"readOnlineUrl\",\n \"referralUrl\",\n \"ridgelockCareersUrl\",\n \"ridgelockSponsorImageUrl\",\n \"ridgelockSponsorUrl\",\n \"sealpathCtaUrl\",\n \"sealpathSponsorImageUrl\",\n \"sealpathSponsorUrl\",\n \"signUpUrl\",\n \"socialOneIconUrl\",\n \"socialThreeIconUrl\",\n \"socialTwoIconUrl\",\n \"sponsorContactUrl\",\n \"termsUrl\",\n \"toolActTwoUrl\",\n \"toolClipcastUrl\",\n \"toolHierarchyUrl\",\n \"toolQuillUrl\",\n \"toolsIndexUrl\",\n \"universityFooterUrl\",\n \"universityUrl\",\n \"unsubscribeUrl\",\n \"workshopDate\",\n \"workshopDay\",\n \"workshopImageUrl\",\n \"workshopRsvpUrl\",\n \"workshopTime\",\n \"workshopUrl\",\n ],\n },\n\n // ── Marketing ───────────────────────────────────────────────────────────────\n {\n id: \"announcement\",\n channel: \"email\",\n category: \"marketing\",\n name: \"Feature announcement\",\n description: \"Announce a new feature with a bold branded header and a link to the changelog.\",\n variables: [\n \"changelogUrl\",\n \"ctaLabel\",\n \"ctaUrl\",\n \"featureDescription\",\n \"featureImage\",\n \"featureName\",\n \"featureTagline\",\n \"firstName\",\n \"unsubscribeUrl\",\n \"workspace\",\n ],\n },\n {\n id: \"promo\",\n channel: \"email\",\n category: \"marketing\",\n name: \"Promotion\",\n description: \"Drive a limited-time offer with a highlighted promo code and a shop CTA.\",\n variables: [\n \"expiryDate\",\n \"firstName\",\n \"offerHeadline\",\n \"offerSubhead\",\n \"promoCode\",\n \"shopUrl\",\n \"termsUrl\",\n \"unsubscribeUrl\",\n ],\n },\n {\n id: \"event-invite\",\n channel: \"email\",\n category: \"marketing\",\n name: \"Event invite\",\n description: \"Invite recipients to an event with when/where details and an RSVP button.\",\n variables: [\n \"declineUrl\",\n \"eventAddress\",\n \"eventDate\",\n \"eventDescription\",\n \"eventLocation\",\n \"eventName\",\n \"eventTime\",\n \"firstName\",\n \"rsvpUrl\",\n \"unsubscribeUrl\",\n ],\n },\n {\n id: \"membership-promo\",\n channel: \"email\",\n category: \"marketing\",\n name: \"Membership promotion\",\n description:\n \"Promote a paid membership with a tiered plan comparison, a discount code, and a repeated upgrade CTA.\",\n variables: [\n \"advertiseUrl\",\n \"appDownloadUrl\",\n \"claimOfferUrl\",\n \"discountPercent\",\n \"flexibleDatesImageUrl\",\n \"getAlertsUrl\",\n \"offerEndDate\",\n \"preferencesUrl\",\n \"promoCode\",\n \"promoCodeUrl\",\n \"socialFiveIconUrl\",\n \"socialFourIconUrl\",\n \"socialOneIconUrl\",\n \"socialThreeIconUrl\",\n \"socialTwoIconUrl\",\n \"specificTripImageUrl\",\n \"summitNowPrice\",\n \"summitWasPrice\",\n \"trailNowPrice\",\n \"trailWasPrice\",\n \"unsubscribeUrl\",\n \"websiteUrl\",\n ],\n },\n\n {\n id: \"member-referral-offer\",\n channel: \"email\",\n category: \"marketing\",\n name: \"Member referral offer\",\n description:\n \"Invite customers to add members to their account for a limited-time reward, with the full terms.\",\n variables: [\n \"accountUrl\",\n \"addMemberUrl\",\n \"addressUrl\",\n \"appIconImageUrl\",\n \"applicationEvalUrl\",\n \"circleFeatureImageUrl\",\n \"coOwnerBonus\",\n \"coOwnerSpendMin\",\n \"copyrightYear\",\n \"creditBuildAge\",\n \"creditReportingUrl\",\n \"customerAgreementUrl\",\n \"heroImageUrl\",\n \"learnCircleUrl\",\n \"learnDailyTideUrl\",\n \"maxBonusAmount\",\n \"maxNewMembers\",\n \"memberRisksUrl\",\n \"minMemberAge\",\n \"offerEndDate\",\n \"offerEndDateFull\",\n \"offerStartDate\",\n \"participantBonus\",\n \"participantSpendMin\",\n \"privacyUrl\",\n \"rewardsFeatureImageUrl\",\n \"signInUrl\",\n \"supportUrl\",\n \"termsUrl\",\n \"tidePayInfoUrl\",\n \"unsubscribeUrl\",\n ],\n },\n {\n id: \"catalog-sale-newsletter\",\n channel: \"email\",\n category: \"marketing\",\n name: \"Catalog sale newsletter\",\n description:\n \"Announce a new release and run a catalog-wide bundle sale with per-item discount links.\",\n variables: [\n \"bundleListPrice\",\n \"bundleSalePrice\",\n \"bundleUrl\",\n \"courseUrl\",\n \"discountAdvancedOneUrl\",\n \"discountAdvancedThreeUrl\",\n \"discountAdvancedTwoUrl\",\n \"discountCanvasPlayUrl\",\n \"discountChallengesUrl\",\n \"discountConcurrencyUrl\",\n \"discountDataLayerUrl\",\n \"discountDesktopPathUrl\",\n \"discountInterfaceByExampleUrl\",\n \"discountLegacyInteropUrl\",\n \"discountLiveSeriesUrl\",\n \"discountLivingRoomPathUrl\",\n \"discountMobilePathUrl\",\n \"discountOutsideEditorUrl\",\n \"discountPatternsUrl\",\n \"discountProInterfaceUrl\",\n \"discountProSystemsUrl\",\n \"discountSundayBuildsUrl\",\n \"discountTestingUrl\",\n \"discountWearablesPathUrl\",\n \"editionLabel\",\n \"editionYear\",\n \"eventChicagoUrl\",\n \"eventEnglandUrl\",\n \"eventFranceUrl\",\n \"eventGermanyUrl\",\n \"eventItalyUrl\",\n \"eventSkopjeUrl\",\n \"eventTokyoUrl\",\n \"eventWalesUrl\",\n \"friendBookUrl\",\n \"friendVideoUrl\",\n \"harborcraftUrl\",\n \"podcastEpisodeUrl\",\n \"poweredByUrl\",\n \"sponsorUrl\",\n \"unsubscribeUrl\",\n ],\n },\n {\n id: \"limited-time-savings\",\n channel: \"email\",\n category: \"marketing\",\n name: \"Limited-time savings\",\n description:\n \"A focused, time-boxed savings offer with a single enrol action and a countdown of hours remaining.\",\n variables: [\n \"advocacyLetterUrl\",\n \"enrollUrl\",\n \"essentialsDiscountPercent\",\n \"essentialsOfferUrl\",\n \"hoursRemaining\",\n \"listPrice\",\n \"productUrl\",\n \"salePrice\",\n \"savingsAmount\",\n \"timezoneGuideUrl\",\n \"unsubscribeUrl\",\n ],\n },\n {\n id: \"rewards-milestone\",\n channel: \"email\",\n category: \"marketing\",\n name: \"Rewards milestone\",\n description:\n \"Drive engagement with a milestone reward program: complete steps, unlock perks, and see the reward tiers.\",\n variables: [\n \"accountLastFour\",\n \"airAccidentCover\",\n \"beSecureUrl\",\n \"benefitIconUrl\",\n \"campaignCode\",\n \"continueGuideUrl\",\n \"diningCreditAmount\",\n \"eventTicketCount\",\n \"familyMemberLimit\",\n \"heroBannerImageUrl\",\n \"internetBankingIconUrl\",\n \"internetBankingUrl\",\n \"learnMoreUrl\",\n \"lockerDiscountPercent\",\n \"loungeVisitCount\",\n \"maxRewardAmount\",\n \"milestoneCount\",\n \"milestoneLabel\",\n \"mobileAppIconUrl\",\n \"mobileAppUrl\",\n \"mobileVersionUrl\",\n \"movieTicketCount\",\n \"personalAccidentCover\",\n \"recipientName\",\n \"rewardsCardImageUrl\",\n \"rmSmsKeyword\",\n \"rmSmsPhone\",\n \"rmSmsShortcode\",\n \"secureChatIconUrl\",\n \"secureChatPhone\",\n \"secureChatUrl\",\n \"securityPolicyUrl\",\n \"termsUrl\",\n \"tollFreePhone\",\n \"unsubscribeUrl\",\n \"webVersionUrl\",\n \"websiteUrl\",\n \"welcomeRewardAmount\",\n ],\n },\n {\n id: \"travel-rewards-promo\",\n channel: \"email\",\n category: \"marketing\",\n name: \"Travel rewards promotion\",\n description:\n \"A rewards-program promotion with bonus tiers, partner discount codes, and a member balance summary.\",\n variables: [\n \"accountPromptImageUrl\",\n \"accountUrl\",\n \"additionalTermsUrl\",\n \"androidAppUrl\",\n \"appDownloadUrl\",\n \"bookUrl\",\n \"campaignCode\",\n \"contactUrl\",\n \"copyrightYear\",\n \"couponBonusMiles\",\n \"dealsUrl\",\n \"discountPercent\",\n \"firstName\",\n \"heroImageUrl\",\n \"iosAppUrl\",\n \"longBonusMiles\",\n \"longRentalDays\",\n \"maxBonusMiles\",\n \"memberId\",\n \"midBonusMiles\",\n \"midRentalDays\",\n \"mileageBalance\",\n \"offerEndDate\",\n \"offerEndDateShort\",\n \"offerStartDate\",\n \"partnerOneCoupon\",\n \"partnerOneDiscountCode\",\n \"partnerOneDiscountCodeAlt\",\n \"partnerTwoCoupon\",\n \"partnerTwoDiscountCode\",\n \"partnerTwoDiscountCodeAlt\",\n \"partnerTwoDiscountPercent\",\n \"preferencesUrl\",\n \"privacyUrl\",\n \"recipientEmail\",\n \"recipientEmailUrl\",\n \"reservationsUrl\",\n \"rewardsUrl\",\n \"signInUrl\",\n \"socialFourIconUrl\",\n \"socialOneIconUrl\",\n \"socialThreeIconUrl\",\n \"socialTwoIconUrl\",\n \"tierFourMiles\",\n \"tierOneMiles\",\n \"tierThreeMiles\",\n \"tierTwoMiles\",\n \"unsubscribeUrl\",\n \"viewOnlineUrl\",\n \"websiteUrl\",\n ],\n },\n {\n id: \"product-updates-roundup\",\n channel: \"email\",\n category: \"marketing\",\n name: \"Product updates roundup\",\n description:\n \"Round up recent product updates and an upcoming event, with stats and a walkthrough video.\",\n variables: [\n \"accessibilityGuideUrl\",\n \"accessibilityReportUrl\",\n \"accessibilityReportYear\",\n \"agentAdapterGuideUrl\",\n \"agentAdapterUrl\",\n \"blastVolume\",\n \"conferenceDate\",\n \"conferenceTicketsUrl\",\n \"contactsImportUrl\",\n \"customerStoryUrl\",\n \"domainTransferUrl\",\n \"emailsAnalyzed\",\n \"emailsPassed\",\n \"meetupDate\",\n \"meetupTime\",\n \"meetupUrl\",\n \"meetupVenue\",\n \"passRate\",\n \"unsubscribeUrl\",\n \"walkthroughVideoUrl\",\n ],\n },\n {\n id: \"flash-getaway-offer\",\n channel: \"email\",\n category: \"marketing\",\n name: \"Flash getaway offer\",\n description:\n \"A limited-time getaway offer with a live countdown, two destinations, and a points-or-cash price.\",\n variables: [\n \"copyrightYear\",\n \"countdownDays\",\n \"countdownHours\",\n \"countdownMinutes\",\n \"destinationOneImageUrl\",\n \"destinationOneRetailValue\",\n \"destinationOneUrl\",\n \"destinationTwoImageUrl\",\n \"destinationTwoRetailValue\",\n \"destinationTwoUrl\",\n \"exploreOffersUrl\",\n \"invitationId\",\n \"learnMoreUrl\",\n \"marketingCode\",\n \"minimumPoints\",\n \"nightlyValueRange\",\n \"offerExpiresDate\",\n \"ownershipPriceHigh\",\n \"ownershipPriceLow\",\n \"phoneDisplay\",\n \"phoneTelUrl\",\n \"pointsPriceHigh\",\n \"pointsPriceLow\",\n \"privacyUrl\",\n \"recipientInitials\",\n \"stayDurationLabel\",\n \"stayPrice\",\n \"termsUrl\",\n \"travelWindowMonths\",\n \"unsubscribeUrl\",\n \"viewInBrowserUrl\",\n ],\n },\n {\n id: \"membership-benefits-promo\",\n channel: \"email\",\n category: \"marketing\",\n name: \"Membership benefits promotion\",\n description:\n \"Promote a paid membership by laying out its perks with icons and a single get-started action.\",\n variables: [\n \"accountSettingsUrl\",\n \"cafeLocationsUrl\",\n \"cafePerkIconUrl\",\n \"contactUsUrl\",\n \"deliveryIconUrl\",\n \"getStartedUrl\",\n \"groceryIconUrl\",\n \"logoImageUrl\",\n \"monthlyCreditIconUrl\",\n \"offerGalleryUrl\",\n \"onlineShoppingFaqsUrl\",\n \"organicIconUrl\",\n \"petCareIconUrl\",\n \"privacyPolicyUrl\",\n \"recipientEmail\",\n \"rewardsIconUrl\",\n \"rewardsTermsUrl\",\n \"supportEmail\",\n \"termsOfUseUrl\",\n \"unsubscribeUrl\",\n \"vipPhoneIconUrl\",\n ],\n },\n {\n id: \"weekly-deals-circular\",\n channel: \"email\",\n category: \"marketing\",\n name: \"Weekly deals circular\",\n description:\n \"A weekly retail circular with multi-buy deals, clippable coupons, membership perks, and app links.\",\n variables: [\n \"allDealsUrl\",\n \"androidAppBadgeUrl\",\n \"androidAppUrl\",\n \"browserViewUrl\",\n \"clipCouponsUrl\",\n \"contactUrl\",\n \"copyrightYear\",\n \"couponFourImageUrl\",\n \"couponOneImageUrl\",\n \"couponThreeImageUrl\",\n \"couponTwoImageUrl\",\n \"couponsUrl\",\n \"deliveryMinimum\",\n \"digitalCouponsUrl\",\n \"feedbackUrl\",\n \"homeUrl\",\n \"iosAppBadgeUrl\",\n \"iosAppUrl\",\n \"membershipDetailsUrl\",\n \"membershipTrialUrl\",\n \"membershipUrl\",\n \"multiBuyQuantity\",\n \"multiBuyUnitSavings\",\n \"pointsMultiplier\",\n \"preferencesUrl\",\n \"privacyUrl\",\n \"produceUrl\",\n \"recipientEmail\",\n \"shopDealUrl\",\n \"socialFourIconUrl\",\n \"socialFourUrl\",\n \"socialOneIconUrl\",\n \"socialOneUrl\",\n \"socialThreeIconUrl\",\n \"socialThreeUrl\",\n \"socialTwoIconUrl\",\n \"socialTwoUrl\",\n \"specialtySavings\",\n \"termsUrl\",\n \"trialDays\",\n \"unsubscribeUrl\",\n \"weeklyAdNavUrl\",\n \"weeklyAdUrl\",\n \"wellnessDiscountPercent\",\n \"wellnessUrl\",\n ],\n },\n {\n id: \"seasonal-sale-flyer\",\n channel: \"email\",\n category: \"marketing\",\n name: \"Seasonal sale flyer\",\n description:\n \"A dense seasonal sale flyer with tiered promo codes, gift-card offers, product grids, and category links.\",\n variables: [\n \"alternatorsImageUrl\",\n \"alternatorsUrl\",\n \"antifreezeImageUrl\",\n \"antifreezeUrl\",\n \"appBadgeFiveImageUrl\",\n \"appBadgeFourImageUrl\",\n \"appBadgeOneImageUrl\",\n \"appBadgeThreeImageUrl\",\n \"appBadgeTwoImageUrl\",\n \"appDownloadUrl\",\n \"batteriesImageUrl\",\n \"batteriesUrl\",\n \"brakePadsImageUrl\",\n \"brakePadsUrl\",\n \"contactUrl\",\n \"copyrightYear\",\n \"findStoreUrl\",\n \"fogGiftCardAmount\",\n \"fogOfferImageUrl\",\n \"fogOfferUrl\",\n \"freeServicesDetailsUrl\",\n \"headlightGiftCardAmount\",\n \"headlightGuideUrl\",\n \"headlightHeroImageUrl\",\n \"headlightOfferUrl\",\n \"howtoBannerImageUrl\",\n \"lightingBrandStripImageUrl\",\n \"lightingImageUrl\",\n \"lightingUrl\",\n \"offerEndDate\",\n \"offersValidRange\",\n \"oilBundlesDetailsUrl\",\n \"oilBundlesImageUrl\",\n \"oilBundlesUrl\",\n \"privacyUrl\",\n \"productThumbFourImageUrl\",\n \"productThumbOneImageUrl\",\n \"productThumbThreeImageUrl\",\n \"productThumbTwoImageUrl\",\n \"promoCode\",\n \"promoCodeUrl\",\n \"promoMaxSavings\",\n \"promoOrderThreshold\",\n \"promoPercentNoMin\",\n \"promoPercentThreshold\",\n \"promoShopUrl\",\n \"rewardsUrl\",\n \"servicesUrl\",\n \"shopOnlineUrl\",\n \"sparkPlugsImageUrl\",\n \"sparkPlugsUrl\",\n \"unsubscribeUrl\",\n \"viewInBrowserUrl\",\n \"websiteUrl\",\n \"weeklyDealsUrl\",\n \"wiperGiftCardAmount\",\n \"wiperOfferImageUrl\",\n \"wiperOfferUrl\",\n \"wipersImageUrl\",\n \"wipersUrl\",\n ],\n },\n {\n id: \"feature-release-roundup\",\n channel: \"email\",\n category: \"marketing\",\n name: \"Feature release roundup\",\n description:\n \"Announce several new features in one email, each with an illustration, detail, and a learn-more link.\",\n variables: [\n \"activityFeedImageUrl\",\n \"activityFeedUrl\",\n \"apiBlogUrl\",\n \"apiDocsUrl\",\n \"apiGaImageUrl\",\n \"brandedShortenersImageUrl\",\n \"changelogUrl\",\n \"continentsAnalyticsUrl\",\n \"domainMgmtUrl\",\n \"ledgerpathShortenerUrl\",\n \"linksWorkspaceBlogUrl\",\n \"linksWorkspaceImageUrl\",\n \"notificationsUrl\",\n \"preferencesUrl\",\n \"reviewUrl\",\n \"shortenerSunsetUrl\",\n \"sketchlaneShortenerUrl\",\n \"socialOneIconUrl\",\n \"socialThreeIconUrl\",\n \"socialTwoIconUrl\",\n \"unsubscribeUrl\",\n ],\n },\n {\n id: \"season-clearance-promo\",\n channel: \"email\",\n category: \"marketing\",\n name: \"Season clearance promotion\",\n description:\n \"A last-chance clearance promotion with a hero banner, a deadline phrase, and a single shop action.\",\n variables: [\n \"contactEmailUrl\",\n \"grabDealsUrl\",\n \"heroImageUrl\",\n \"offerDeadlinePhrase\",\n \"offerEndLong\",\n \"offerEndShort\",\n \"onlineVersionUrl\",\n \"preferencesUrl\",\n \"seasonYear\",\n \"secondaryBannerImageUrl\",\n \"socialFourIconUrl\",\n \"socialOneIconUrl\",\n \"socialThreeIconUrl\",\n \"socialTwoIconUrl\",\n \"termsUrl\",\n \"unsubscribeUrl\",\n ],\n },\n\n // ── Lifecycle ───────────────────────────────────────────────────────────────\n {\n id: \"onboarding-step\",\n channel: \"email\",\n category: \"lifecycle\",\n name: \"Onboarding step\",\n description: \"One step in an onboarding sequence, with progress and a single next action.\",\n variables: [\n \"ctaLabel\",\n \"ctaUrl\",\n \"docsUrl\",\n \"firstName\",\n \"logoUrl\",\n \"stepDescription\",\n \"stepNumber\",\n \"stepTitle\",\n \"stepTotal\",\n \"workspace\",\n ],\n },\n {\n id: \"trial-ending\",\n channel: \"email\",\n category: \"lifecycle\",\n name: \"Trial ending\",\n description: \"Warn a trial user their access is ending and prompt an upgrade before it lapses.\",\n variables: [\n \"contactUrl\",\n \"daysLeft\",\n \"firstName\",\n \"logoUrl\",\n \"planName\",\n \"planPrice\",\n \"planSummary\",\n \"trialEndDate\",\n \"upgradeUrl\",\n \"workspace\",\n ],\n },\n {\n id: \"win-back\",\n channel: \"email\",\n category: \"lifecycle\",\n name: \"Win-back\",\n description: \"Re-engage a lapsed user by surfacing what's new and inviting them back.\",\n variables: [\"ctaUrl\", \"firstName\", \"logoUrl\", \"unsubscribeUrl\", \"whatsNew\", \"workspace\"],\n },\n {\n id: \"feedback-request\",\n channel: \"email\",\n category: \"lifecycle\",\n name: \"Feedback request\",\n description: \"Ask an active user for feedback with a short survey and a reply-to fallback.\",\n variables: [\"firstName\", \"logoUrl\", \"senderName\", \"surveyUrl\", \"tenure\", \"workspace\"],\n },\n\n {\n id: \"product-onboarding-tour\",\n channel: \"email\",\n category: \"lifecycle\",\n name: \"Product onboarding tour\",\n description:\n \"Walk a new user through the product's core features with an illustrated tour and app-store links.\",\n variables: [\n \"appStoreBadgeUrl\",\n \"emailGuideImageUrl\",\n \"emailGuideUrl\",\n \"helpCenterUrl\",\n \"incomeDashboardImageUrl\",\n \"incomeDashboardUrl\",\n \"incomeGuideImageUrl\",\n \"incomeGuideUrl\",\n \"mediaKitImageUrl\",\n \"mediaKitUrl\",\n \"playStoreBadgeUrl\",\n \"socialOneIconUrl\",\n \"socialThreeIconUrl\",\n \"socialTwoIconUrl\",\n \"unsubscribeUrl\",\n \"welcomeEmailImageUrl\",\n \"welcomeEmailUrl\",\n ],\n },\n {\n id: \"setup-checklist\",\n channel: \"email\",\n category: \"lifecycle\",\n name: \"Setup checklist\",\n description:\n \"A short welcome email that walks a new customer through three setup steps to launch their storefront.\",\n variables: [\n \"addAppsImageUrl\",\n \"continueUrl\",\n \"copyrightYear\",\n \"pricingImageUrl\",\n \"storePageImageUrl\",\n \"unsubscribeUrl\",\n ],\n },\n\n // ── SMS ─────────────────────────────────────────────────────────────────────\n {\n id: \"sms-otp\",\n channel: \"sms\",\n category: \"transactional\",\n name: \"One-time passcode (SMS)\",\n description: \"Deliver a short-lived sign-in or 2FA code in a single GSM-7 segment.\",\n variables: [\"code\", \"expiryMinutes\", \"workspace\"],\n },\n {\n id: \"sms-shipping-update\",\n channel: \"sms\",\n category: \"transactional\",\n name: \"Shipping update (SMS)\",\n description: \"Tell a customer their order shipped, with a tracking link.\",\n variables: [\"orderNumber\", \"trackingUrl\", \"workspace\"],\n },\n {\n id: \"sms-appointment-reminder\",\n channel: \"sms\",\n category: \"lifecycle\",\n name: \"Appointment reminder (SMS)\",\n description: \"Remind a customer of an upcoming appointment with a reschedule path.\",\n variables: [\"appointmentDate\", \"appointmentTime\", \"workspace\"],\n },\n {\n id: \"sms-flash-sale\",\n channel: \"sms\",\n category: \"marketing\",\n name: \"Flash sale (SMS)\",\n description: \"Drive a limited-time offer with a promo code and a conditional urgency line.\",\n variables: [\"endsToday\", \"offerHeadline\", \"promoCode\", \"shopUrl\", \"workspace\"],\n },\n\n // ── WhatsApp ────────────────────────────────────────────────────────────────\n {\n id: \"whatsapp-order-update\",\n channel: \"whatsapp\",\n category: \"transactional\",\n name: \"Order update (WhatsApp)\",\n description: \"Utility template: shipping confirmation with a tracking URL button.\",\n variables: [\"estimatedDelivery\", \"firstName\", \"orderNumber\", \"trackingUrl\"],\n },\n {\n id: \"whatsapp-appointment-reminder\",\n channel: \"whatsapp\",\n category: \"lifecycle\",\n name: \"Appointment reminder (WhatsApp)\",\n description: \"Utility template: appointment reminder with confirm/reschedule quick replies.\",\n variables: [\"appointmentDate\", \"appointmentTime\", \"firstName\", \"workspace\"],\n },\n {\n id: \"whatsapp-flash-sale\",\n channel: \"whatsapp\",\n category: \"marketing\",\n name: \"Flash sale (WhatsApp)\",\n description:\n \"Marketing template: limited-time offer with a copyable promo code and a shop button.\",\n variables: [\"endsToday\", \"firstName\", \"offerDetails\", \"offerHeadline\", \"promoCode\", \"shopUrl\"],\n },\n] as const satisfies ReadonlyArray<PresetMeta>;\n\nexport type PresetId = (typeof presetCatalog)[number][\"id\"];\nexport type PresetVariable = (typeof presetCatalog)[number][\"variables\"][number];\ntype EmailPreset = Extract<(typeof presetCatalog)[number], { channel: \"email\" }>;\nexport type EmailPresetId = EmailPreset[\"id\"];\nexport type EmailPresetVariable = EmailPreset extends infer Preset\n ? Preset extends { readonly variables: ReadonlyArray<infer Variable> }\n ? Variable\n : never\n : never;\n"],"mappings":";AAwCA,MAAa,oBAAoC;CAC/C,QAAQ;EACN,OAAO;EACP,oBAAoB;CACtB;CACA,UAAU,EACR,OAAO,EACT;AACF;AAEA,MAAa,gBAAgB;CAE3B;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GAAC;GAAU;GAAW;GAAa;GAAW;EAAW;CACtE;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GAAC;GAAS;GAAa;GAAW;GAAoB;GAAa;EAAW;CAC3F;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GAAC;GAAQ;GAAiB;GAAW;EAAW;CAC7D;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GAAC;GAAiB;GAAa;GAAW;GAAY;EAAW;CAC9E;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CAEA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CAGA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CAEA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CAGA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CAEA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CAGA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GAAC;GAAU;GAAa;GAAW;GAAkB;GAAY;EAAW;CACzF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GAAC;GAAa;GAAW;GAAc;GAAa;GAAU;EAAW;CACtF;CAEA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CAGA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GAAC;GAAQ;GAAiB;EAAW;CAClD;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GAAC;GAAe;GAAe;EAAW;CACvD;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GAAC;GAAmB;GAAmB;EAAW;CAC/D;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GAAC;GAAa;GAAiB;GAAa;GAAW;EAAW;CAC/E;CAGA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GAAC;GAAqB;GAAa;GAAe;EAAa;CAC5E;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,WAAW;GAAC;GAAmB;GAAmB;GAAa;EAAW;CAC5E;CACA;EACE,IAAI;EACJ,SAAS;EACT,UAAU;EACV,MAAM;EACN,aACE;EACF,WAAW;GAAC;GAAa;GAAa;GAAgB;GAAiB;GAAa;EAAS;CAC/F;AACF"}
@@ -0,0 +1,6 @@
1
+ import { C as SmlDocument } from "./ast-DAhnAvQN.js";
2
+ //#region src/printer.d.ts
3
+ declare const printSml: (document: SmlDocument) => string;
4
+ //#endregion
5
+ export { printSml };
6
+ //# sourceMappingURL=printer.d.ts.map
@@ -0,0 +1,122 @@
1
+ //#region src/printer.ts
2
+ const MAX_WIDTH = 100;
3
+ const INDENT = " ";
4
+ const escapeText = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("{", "&#123;");
5
+ const escapeAttribute = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;");
6
+ const printAttribute = (attribute) => attribute.value.kind === "number" ? `${attribute.name}={${attribute.value.value}}` : `${attribute.name}="${escapeAttribute(attribute.value.value)}"`;
7
+ const printInterpolation = (node) => node.syntax === "curly" ? `{{${node.name}}}` : `\${${node.name}}`;
8
+ const printInlineFlow = (children) => {
9
+ let result = "";
10
+ for (const child of children) if (child.kind === "text") result += escapeText(child.value);
11
+ else if (child.kind === "interpolation") result += printInterpolation(child);
12
+ else {
13
+ const attributes = child.attributes.map((a) => ` ${printAttribute(a)}`).join("");
14
+ result += child.children.length === 0 ? `<${child.tag}${attributes} />` : `<${child.tag}${attributes}>${printInlineFlow(child.children)}</${child.tag}>`;
15
+ }
16
+ return result;
17
+ };
18
+ const isInlineContainer = (node) => node.tag === "Text" || node.tag === "Heading" || node.tag === "Button" || node.tag === "Subject" || node.tag === "Preview" || node.tag === "ListItem" || node.tag === "a" || node.tag === "strong" || node.tag === "em";
19
+ /**
20
+ * Splits an inline flow into reflow chunks. Chunk boundaries are exactly the
21
+ * spaces in text nodes — the only positions where a line break re-parses to
22
+ * the same AST. Everything between two spaces (words, interpolations, whole
23
+ * or partial inline elements) glues into one atomic chunk.
24
+ */
25
+ const chunkInlineFlow = (children) => {
26
+ const chunks = [];
27
+ let glue = false;
28
+ const emit = (text) => {
29
+ if (text.length === 0) return;
30
+ if (glue && chunks.length > 0) chunks[chunks.length - 1] += text;
31
+ else chunks.push(text);
32
+ glue = true;
33
+ };
34
+ const space = () => {
35
+ glue = false;
36
+ };
37
+ const visit = (nodes) => {
38
+ for (const child of nodes) if (child.kind === "text") escapeText(child.value).split(" ").forEach((word, index) => {
39
+ if (index > 0) space();
40
+ emit(word);
41
+ });
42
+ else if (child.kind === "interpolation") emit(printInterpolation(child));
43
+ else {
44
+ const attributes = child.attributes.map((a) => ` ${printAttribute(a)}`).join("");
45
+ if (child.children.length === 0) emit(`<${child.tag}${attributes} />`);
46
+ else {
47
+ emit(`<${child.tag}${attributes}>`);
48
+ visit(child.children);
49
+ emit(`</${child.tag}>`);
50
+ }
51
+ }
52
+ };
53
+ visit(children);
54
+ return chunks;
55
+ };
56
+ /** Greedy fill: chunks joined by single spaces, wrapped at MAX_WIDTH. */
57
+ const reflowInlineFlow = (children, indent) => {
58
+ const chunks = chunkInlineFlow(children);
59
+ const lines = [];
60
+ let line = null;
61
+ for (const chunk of chunks) if (line === null) line = `${indent}${chunk}`;
62
+ else if (line.length + 1 + chunk.length <= MAX_WIDTH) line += ` ${chunk}`;
63
+ else {
64
+ lines.push(line);
65
+ line = `${indent}${chunk}`;
66
+ }
67
+ if (line !== null) lines.push(line);
68
+ return lines.join("\n");
69
+ };
70
+ const printOpenTag = (node, indent, selfClosing) => {
71
+ const attributes = node.attributes.map((a) => ` ${printAttribute(a)}`).join("");
72
+ const singleLine = `${indent}<${node.tag}${attributes}${selfClosing ? " />" : ">"}`;
73
+ if (singleLine.length <= MAX_WIDTH || node.attributes.length === 0) return singleLine;
74
+ const lines = [`${indent}<${node.tag}`];
75
+ for (const attribute of node.attributes) lines.push(`${indent}${INDENT}${printAttribute(attribute)}`);
76
+ lines.push(`${indent}${selfClosing ? "/>" : ">"}`);
77
+ return lines.join("\n");
78
+ };
79
+ /**
80
+ * A verbatim flow prints its content byte-preserving: text exactly as stored
81
+ * (escaped), interpolations canonical, and <If>/<Else> glued into the flow
82
+ * with single-line open tags (whitespace inside a TAG is insignificant, but a
83
+ * line break in the FLOW would become content on reparse).
84
+ */
85
+ const printVerbatimFlow = (children) => {
86
+ let result = "";
87
+ for (const child of children) if (child.kind === "text") result += escapeText(child.value);
88
+ else if (child.kind === "interpolation") result += printInterpolation(child);
89
+ else result += printVerbatimElement(child);
90
+ return result;
91
+ };
92
+ const printVerbatimElement = (node) => {
93
+ const attributes = node.attributes.map((a) => ` ${printAttribute(a)}`).join("");
94
+ return node.children.length === 0 ? `<${node.tag}${attributes} />` : `<${node.tag}${attributes}>${printVerbatimFlow(node.children)}</${node.tag}>`;
95
+ };
96
+ /** A verbatim container: open tag at its indent, content glued, close tag glued. */
97
+ const printVerbatimContainer = (node, indent) => {
98
+ if (node.children.length === 0) return printOpenTag(node, indent, true);
99
+ const attributes = node.attributes.map((a) => ` ${printAttribute(a)}`).join("");
100
+ return `${indent}<${node.tag}${attributes}>${printVerbatimFlow(node.children)}</${node.tag}>`;
101
+ };
102
+ /** Tags whose content is verbatim. <Button> is verbatim only inside <WhatsApp>. */
103
+ const isVerbatimContainer = (node, inWhatsApp) => node.tag === "Sms" || node.tag === "Header" || node.tag === "Body" || node.tag === "Footer" || inWhatsApp && node.tag === "Button";
104
+ const printElement = (node, depth, inWhatsApp = false) => {
105
+ const indent = INDENT.repeat(depth);
106
+ if (isVerbatimContainer(node, inWhatsApp)) return printVerbatimContainer(node, indent);
107
+ if (node.children.length === 0) return printOpenTag(node, indent, true);
108
+ if (isInlineContainer(node) && !inWhatsApp) {
109
+ const flow = printInlineFlow(node.children);
110
+ const openTag = printOpenTag(node, indent, false);
111
+ const singleLine = `${openTag}${flow}</${node.tag}>`;
112
+ if (!openTag.includes("\n") && singleLine.length <= MAX_WIDTH) return singleLine;
113
+ return `${openTag}\n${reflowInlineFlow(node.children, indent + INDENT)}\n${indent}</${node.tag}>`;
114
+ }
115
+ const nextInWhatsApp = inWhatsApp || node.tag === "WhatsApp";
116
+ return `${printOpenTag(node, indent, false)}\n${node.children.map((child) => child.kind === "element" ? printElement(child, depth + 1, nextInWhatsApp) : child.kind === "text" ? `${indent}${INDENT}${escapeText(child.value)}` : `${indent}${INDENT}${printInterpolation(child)}`).join("\n")}\n${indent}</${node.tag}>`;
117
+ };
118
+ const printSml = (document) => `${printElement(document.root, 0)}\n`;
119
+ //#endregion
120
+ export { printSml };
121
+
122
+ //# sourceMappingURL=printer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"printer.js","names":[],"sources":["../src/printer.ts"],"sourcesContent":["import type { Attribute, ElementNode, SmlDocument, SmlNode } from \"./ast\";\n\n// Canonical gofmt-style printer: one formatting for every AST, so\n// parse ∘ print is a fixpoint (`parse(print(parse(s)))` equals `parse(s)` up\n// to spans) and version history stays line-diffable.\n//\n// Rules:\n// - 2-space indent per depth; block children each on their own line.\n// - Elements with inline content (Text/Heading/Button/Subject/Preview/ListItem/a/\n// strong/em) print on one line when the line fits MAX_WIDTH; otherwise the\n// inline flow moves to its own indented lines, reflowed at MAX_WIDTH.\n// - Reflow breaks only at spaces that exist in the AST (the parser collapses\n// inline whitespace runs to single spaces, so a newline+indent re-parses to\n// the same single space): words never split, interpolations are atomic, and\n// element tags stay glued to adjacent non-space content. A chunk longer than\n// MAX_WIDTH (a long URL) overflows rather than breaking mid-token.\n// - Attributes stay in authored order (order is part of the AST); when an open\n// tag exceeds MAX_WIDTH each attribute gets its own line.\n// - No children ⇒ self-closing `<Tag />`.\n// - Interpolations print canonically as {{name}} / ${name}.\n//\n// Verbatim content (the <Sms> body and WhatsApp text slots) has ONE rule: the\n// printer inserts nothing and removes nothing. Text prints exactly where it\n// sits — no indent, no reflow, no line breaks — so newlines and space runs\n// round-trip byte-exactly. Tags inside a verbatim flow (<If>/<Else>) glue to\n// their surrounding content, and verbatim open tags never split across lines.\n\nconst MAX_WIDTH = 100;\nconst INDENT = \" \";\n\n// `{` is escaped so text that literally contains `{{`/`${` (reachable via\n// entities in source or via TSX-constructed text nodes) re-lexes as text\n// instead of an interpolation — otherwise parse ∘ print is not a fixpoint.\nconst escapeText = (value: string): string =>\n value\n .replaceAll(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\n .replaceAll(\"{\", \"&#123;\");\n\nconst escapeAttribute = (value: string): string =>\n value.replaceAll(\"&\", \"&amp;\").replaceAll(\"<\", \"&lt;\").replaceAll('\"', \"&quot;\");\n\nconst printAttribute = (attribute: Attribute): string =>\n attribute.value.kind === \"number\"\n ? `${attribute.name}={${attribute.value.value}}`\n : `${attribute.name}=\"${escapeAttribute(attribute.value.value)}\"`;\n\nconst printInterpolation = (node: Extract<SmlNode, { kind: \"interpolation\" }>): string =>\n node.syntax === \"curly\" ? `{{${node.name}}}` : `\\${${node.name}}`;\n\nconst printInlineFlow = (children: ReadonlyArray<SmlNode>): string => {\n let result = \"\";\n for (const child of children) {\n if (child.kind === \"text\") result += escapeText(child.value);\n else if (child.kind === \"interpolation\") result += printInterpolation(child);\n else {\n const attributes = child.attributes.map((a) => ` ${printAttribute(a)}`).join(\"\");\n result +=\n child.children.length === 0\n ? `<${child.tag}${attributes} />`\n : `<${child.tag}${attributes}>${printInlineFlow(child.children)}</${child.tag}>`;\n }\n }\n return result;\n};\n\nconst isInlineContainer = (node: ElementNode): boolean =>\n node.tag === \"Text\" ||\n node.tag === \"Heading\" ||\n node.tag === \"Button\" ||\n node.tag === \"Subject\" ||\n node.tag === \"Preview\" ||\n node.tag === \"ListItem\" ||\n node.tag === \"a\" ||\n node.tag === \"strong\" ||\n node.tag === \"em\";\n\n/**\n * Splits an inline flow into reflow chunks. Chunk boundaries are exactly the\n * spaces in text nodes — the only positions where a line break re-parses to\n * the same AST. Everything between two spaces (words, interpolations, whole\n * or partial inline elements) glues into one atomic chunk.\n */\nconst chunkInlineFlow = (children: ReadonlyArray<SmlNode>): Array<string> => {\n const chunks: Array<string> = [];\n let glue = false;\n\n const emit = (text: string): void => {\n if (text.length === 0) return;\n if (glue && chunks.length > 0) chunks[chunks.length - 1] += text;\n else chunks.push(text);\n glue = true;\n };\n const space = (): void => {\n glue = false;\n };\n\n const visit = (nodes: ReadonlyArray<SmlNode>): void => {\n for (const child of nodes) {\n if (child.kind === \"text\") {\n // Escapes contain no spaces, so escaped-word splitting equals raw-word\n // splitting.\n const words = escapeText(child.value).split(\" \");\n words.forEach((word, index) => {\n if (index > 0) space();\n emit(word);\n });\n } else if (child.kind === \"interpolation\") {\n emit(printInterpolation(child));\n } else {\n const attributes = child.attributes.map((a) => ` ${printAttribute(a)}`).join(\"\");\n if (child.children.length === 0) {\n emit(`<${child.tag}${attributes} />`);\n } else {\n emit(`<${child.tag}${attributes}>`);\n visit(child.children);\n emit(`</${child.tag}>`);\n }\n }\n }\n };\n visit(children);\n return chunks;\n};\n\n/** Greedy fill: chunks joined by single spaces, wrapped at MAX_WIDTH. */\nconst reflowInlineFlow = (children: ReadonlyArray<SmlNode>, indent: string): string => {\n const chunks = chunkInlineFlow(children);\n const lines: Array<string> = [];\n let line: string | null = null;\n for (const chunk of chunks) {\n if (line === null) {\n line = `${indent}${chunk}`;\n } else if (line.length + 1 + chunk.length <= MAX_WIDTH) {\n line += ` ${chunk}`;\n } else {\n lines.push(line);\n line = `${indent}${chunk}`;\n }\n }\n if (line !== null) lines.push(line);\n return lines.join(\"\\n\");\n};\n\nconst printOpenTag = (node: ElementNode, indent: string, selfClosing: boolean): string => {\n const attributes = node.attributes.map((a) => ` ${printAttribute(a)}`).join(\"\");\n const singleLine = `${indent}<${node.tag}${attributes}${selfClosing ? \" />\" : \">\"}`;\n if (singleLine.length <= MAX_WIDTH || node.attributes.length === 0) return singleLine;\n const lines = [`${indent}<${node.tag}`];\n for (const attribute of node.attributes) {\n lines.push(`${indent}${INDENT}${printAttribute(attribute)}`);\n }\n lines.push(`${indent}${selfClosing ? \"/>\" : \">\"}`);\n return lines.join(\"\\n\");\n};\n\n// ── Verbatim printing (sms body, whatsapp text slots) ────────────────────────\n\n/**\n * A verbatim flow prints its content byte-preserving: text exactly as stored\n * (escaped), interpolations canonical, and <If>/<Else> glued into the flow\n * with single-line open tags (whitespace inside a TAG is insignificant, but a\n * line break in the FLOW would become content on reparse).\n */\nconst printVerbatimFlow = (children: ReadonlyArray<SmlNode>): string => {\n let result = \"\";\n for (const child of children) {\n if (child.kind === \"text\") result += escapeText(child.value);\n else if (child.kind === \"interpolation\") result += printInterpolation(child);\n else result += printVerbatimElement(child);\n }\n return result;\n};\n\nconst printVerbatimElement = (node: ElementNode): string => {\n const attributes = node.attributes.map((a) => ` ${printAttribute(a)}`).join(\"\");\n return node.children.length === 0\n ? `<${node.tag}${attributes} />`\n : `<${node.tag}${attributes}>${printVerbatimFlow(node.children)}</${node.tag}>`;\n};\n\n/** A verbatim container: open tag at its indent, content glued, close tag glued. */\nconst printVerbatimContainer = (node: ElementNode, indent: string): string => {\n if (node.children.length === 0) return printOpenTag(node, indent, true);\n const attributes = node.attributes.map((a) => ` ${printAttribute(a)}`).join(\"\");\n return `${indent}<${node.tag}${attributes}>${printVerbatimFlow(node.children)}</${node.tag}>`;\n};\n\n/** Tags whose content is verbatim. <Button> is verbatim only inside <WhatsApp>. */\nconst isVerbatimContainer = (node: ElementNode, inWhatsApp: boolean): boolean =>\n node.tag === \"Sms\" ||\n node.tag === \"Header\" ||\n node.tag === \"Body\" ||\n node.tag === \"Footer\" ||\n (inWhatsApp && node.tag === \"Button\");\n\nconst printElement = (node: ElementNode, depth: number, inWhatsApp = false): string => {\n const indent = INDENT.repeat(depth);\n if (isVerbatimContainer(node, inWhatsApp)) return printVerbatimContainer(node, indent);\n if (node.children.length === 0) return printOpenTag(node, indent, true);\n\n if (isInlineContainer(node) && !inWhatsApp) {\n const flow = printInlineFlow(node.children);\n const openTag = printOpenTag(node, indent, false);\n const singleLine = `${openTag}${flow}</${node.tag}>`;\n if (!openTag.includes(\"\\n\") && singleLine.length <= MAX_WIDTH) return singleLine;\n return `${openTag}\\n${reflowInlineFlow(node.children, indent + INDENT)}\\n${indent}</${node.tag}>`;\n }\n\n const nextInWhatsApp = inWhatsApp || node.tag === \"WhatsApp\";\n const openTag = printOpenTag(node, indent, false);\n const children = node.children\n .map((child) =>\n child.kind === \"element\"\n ? printElement(child, depth + 1, nextInWhatsApp)\n : child.kind === \"text\"\n ? `${indent}${INDENT}${escapeText(child.value)}`\n : `${indent}${INDENT}${printInterpolation(child)}`,\n )\n .join(\"\\n\");\n return `${openTag}\\n${children}\\n${indent}</${node.tag}>`;\n};\n\nexport const printSml = (document: SmlDocument): string => `${printElement(document.root, 0)}\\n`;\n"],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { s as TokenTable } from "./theme-6ApjjiBo.js";
2
+ //#region src/registry.d.ts
3
+ interface Declaration {
4
+ readonly property: string;
5
+ readonly value: string;
6
+ }
7
+ interface UtilityFamily {
8
+ readonly root: string;
9
+ /** Accepts `root` with no value ("border", "rounded", "block"). */
10
+ readonly bare?: ReadonlyArray<Declaration> | undefined;
11
+ /** Resolve a named value; null = this family does not accept it. */
12
+ readonly named?: ((value: string, theme: TokenTable) => ReadonlyArray<Declaration> | null) | undefined;
13
+ /** Resolve an arbitrary `[...]` value; null = malformed for this family. */
14
+ readonly arbitrary?: ((value: string, theme: TokenTable) => ReadonlyArray<Declaration> | null) | undefined;
15
+ /** dark: variant is only legal on color families. */
16
+ readonly colorFamily?: boolean | undefined;
17
+ /**
18
+ * Base-variant only: sm:/dark: are rejected. Set on the gradient family —
19
+ * its declarations are compiler-internal placeholders that must be combined
20
+ * per node, which head rules cannot express.
21
+ */
22
+ readonly baseOnly?: boolean | undefined;
23
+ /** "warn" families compile but surface a lint warning. */
24
+ readonly warn?: string | undefined;
25
+ readonly description: string;
26
+ readonly caniemailSlugs: ReadonlyArray<string>;
27
+ }
28
+ interface ForbiddenUtility {
29
+ /** Exact class or `prefix-` match against the candidate base. */
30
+ readonly match: string;
31
+ readonly message: string;
32
+ }
33
+ declare const GRADIENT_DIRECTION_PROPERTY = "-sml-gradient-direction";
34
+ declare const GRADIENT_FROM_PROPERTY = "-sml-gradient-from";
35
+ declare const GRADIENT_TO_PROPERTY = "-sml-gradient-to";
36
+ declare const utilityRegistry: ReadonlyArray<UtilityFamily>;
37
+ declare const forbiddenUtilities: ReadonlyArray<ForbiddenUtility>;
38
+ /** Every root the resolver may try to split on, longest-match first (candidate.ts). */
39
+ declare const registryRoots: ReadonlySet<string>;
40
+ //#endregion
41
+ export { Declaration, ForbiddenUtility, GRADIENT_DIRECTION_PROPERTY, GRADIENT_FROM_PROPERTY, GRADIENT_TO_PROPERTY, UtilityFamily, forbiddenUtilities, registryRoots, utilityRegistry };
42
+ //# sourceMappingURL=registry.d.ts.map