@samva/markup 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/ast.js +1 -1
  2. package/dist/{checks-CW0nFj5n.js → checks-D-zFQt5R.js} +2 -2
  3. package/dist/checks.js +1 -1
  4. package/dist/compile-sms.d.ts +1 -1
  5. package/dist/compile-whatsapp.d.ts +1 -1
  6. package/dist/components.d.ts +3 -3
  7. package/dist/conditionals.js +1 -1
  8. package/dist/diagnostics.js +2 -2
  9. package/dist/jsx-dev-runtime.d.ts +1 -1
  10. package/dist/{jsx-runtime-fSxcvlx4.d.ts → jsx-runtime-eNIMY87F.d.ts} +2 -2
  11. package/dist/jsx-runtime.d.ts +1 -1
  12. package/dist/ops.js +2 -2
  13. package/dist/{parser-yzjUOGhc.d.ts → parser-DkQKIjDa.d.ts} +11 -2
  14. package/dist/parser.d.ts +2 -2
  15. package/dist/parser.js +48 -9
  16. package/dist/predicate-CvkzT-a2.js +539 -0
  17. package/dist/{predicate-DTtVtLK7.d.ts → predicate-G2SeSvoN.d.ts} +9 -2
  18. package/dist/predicate.d.ts +2 -2
  19. package/dist/predicate.js +2 -417
  20. package/dist/presets/category-mapping.d.ts +1 -1
  21. package/dist/presets/public-gallery.d.ts +2 -490
  22. package/dist/presets/public-gallery.js +2 -1
  23. package/dist/presets/public-handoff.d.ts +2 -68
  24. package/dist/presets/public-handoff.js +110 -24
  25. package/dist/presets/public-preview-artifacts.d.ts +2 -2
  26. package/dist/presets/public-preview.d.ts +2 -1168
  27. package/dist/presets/public-preview.js +17 -17
  28. package/dist/presets/sources.js +17 -17
  29. package/dist/public-gallery-D0Zv0pZE.d.ts +611 -0
  30. package/dist/public-preview-u1Z9aaGq.d.ts +1168 -0
  31. package/dist/render-template.d.ts +2 -2
  32. package/dist/render-template.js +1 -1
  33. package/dist/{variables-3NGLpNTB.d.ts → variables-if1VrV34.d.ts} +2 -2
  34. package/dist/variables.d.ts +1 -1
  35. package/dist/variables.js +1 -1
  36. package/generated/sml-email-language-reference.md +1 -1
  37. package/generated/sml-language-reference.md +1 -1
  38. package/package.json +6 -3
  39. package/dist/ast.js.map +0 -1
  40. package/dist/candidate.js.map +0 -1
  41. package/dist/checks-CW0nFj5n.js.map +0 -1
  42. package/dist/classes.js.map +0 -1
  43. package/dist/compile-sms.js.map +0 -1
  44. package/dist/compile-whatsapp.js.map +0 -1
  45. package/dist/compile.js.map +0 -1
  46. package/dist/components.js.map +0 -1
  47. package/dist/conditionals.js.map +0 -1
  48. package/dist/diagnostics.js.map +0 -1
  49. package/dist/emit.js.map +0 -1
  50. package/dist/footer.js.map +0 -1
  51. package/dist/interpolate.js.map +0 -1
  52. package/dist/jsx-dev-runtime.js.map +0 -1
  53. package/dist/jsx-runtime.js.map +0 -1
  54. package/dist/lexer.js.map +0 -1
  55. package/dist/model.js.map +0 -1
  56. package/dist/ops.js.map +0 -1
  57. package/dist/parser.js.map +0 -1
  58. package/dist/predicate.js.map +0 -1
  59. package/dist/presets/category-mapping.js.map +0 -1
  60. package/dist/presets/public-gallery.js.map +0 -1
  61. package/dist/presets/public-handoff.js.map +0 -1
  62. package/dist/presets/public-preview-artifacts.js.map +0 -1
  63. package/dist/presets/public-preview.js.map +0 -1
  64. package/dist/presets/sources.js.map +0 -1
  65. package/dist/presets.js.map +0 -1
  66. package/dist/printer.js.map +0 -1
  67. package/dist/registry.js.map +0 -1
  68. package/dist/render-template.js.map +0 -1
  69. package/dist/sms-segments.js.map +0 -1
  70. package/dist/theme-css.js.map +0 -1
  71. package/dist/theme.js.map +0 -1
  72. package/dist/tokens.js.map +0 -1
  73. package/dist/variables.js.map +0 -1
  74. package/dist/version-BITr58kk.js.map +0 -1
@@ -1 +0,0 @@
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"}
@@ -1 +0,0 @@
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":";AA2BA,MAAM,YAAY;AAClB,MAAM,SAAS;AAKf,MAAM,cAAc,UAClB,MACG,WAAW,KAAK,OAAO,CAAC,CACxB,WAAW,KAAK,MAAM,CAAC,CACvB,WAAW,KAAK,MAAM,CAAC,CACvB,WAAW,KAAK,QAAQ;AAE7B,MAAM,mBAAmB,UACvB,MAAM,WAAW,KAAK,OAAO,CAAC,CAAC,WAAW,KAAK,MAAM,CAAC,CAAC,WAAW,MAAK,QAAQ;AAEjF,MAAM,kBAAkB,cACtB,UAAU,MAAM,SAAS,WACrB,GAAG,UAAU,KAAK,IAAI,UAAU,MAAM,MAAM,KAC5C,GAAG,UAAU,KAAK,IAAI,gBAAgB,UAAU,MAAM,KAAK,EAAE;AAEnE,MAAM,sBAAsB,SAC1B,KAAK,WAAW,UAAU,KAAK,KAAK,KAAK,MAAM,MAAM,KAAK,KAAK;AAEjE,MAAM,mBAAmB,aAA6C;CACpE,IAAI,SAAS;CACb,KAAK,MAAM,SAAS,UAClB,IAAI,MAAM,SAAS,QAAQ,UAAU,WAAW,MAAM,KAAK;MACtD,IAAI,MAAM,SAAS,iBAAiB,UAAU,mBAAmB,KAAK;MACtE;EACH,MAAM,aAAa,MAAM,WAAW,KAAK,MAAM,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE;EAC/E,UACE,MAAM,SAAS,WAAW,IACtB,IAAI,MAAM,MAAM,WAAW,OAC3B,IAAI,MAAM,MAAM,WAAW,GAAG,gBAAgB,MAAM,QAAQ,EAAE,IAAI,MAAM,IAAI;CACpF;CAEF,OAAO;AACT;AAEA,MAAM,qBAAqB,SACzB,KAAK,QAAQ,UACb,KAAK,QAAQ,aACb,KAAK,QAAQ,YACb,KAAK,QAAQ,aACb,KAAK,QAAQ,aACb,KAAK,QAAQ,cACb,KAAK,QAAQ,OACb,KAAK,QAAQ,YACb,KAAK,QAAQ;;;;;;;AAQf,MAAM,mBAAmB,aAAoD;CAC3E,MAAM,SAAwB,CAAC;CAC/B,IAAI,OAAO;CAEX,MAAM,QAAQ,SAAuB;EACnC,IAAI,KAAK,WAAW,GAAG;EACvB,IAAI,QAAQ,OAAO,SAAS,GAAG,OAAO,OAAO,SAAS,MAAM;OACvD,OAAO,KAAK,IAAI;EACrB,OAAO;CACT;CACA,MAAM,cAAoB;EACxB,OAAO;CACT;CAEA,MAAM,SAAS,UAAwC;EACrD,KAAK,MAAM,SAAS,OAClB,IAAI,MAAM,SAAS,QAIjB,WADyB,MAAM,KAAK,CAAC,CAAC,MAAM,GACxC,CAAC,CAAC,SAAS,MAAM,UAAU;GAC7B,IAAI,QAAQ,GAAG,MAAM;GACrB,KAAK,IAAI;EACX,CAAC;OACI,IAAI,MAAM,SAAS,iBACxB,KAAK,mBAAmB,KAAK,CAAC;OACzB;GACL,MAAM,aAAa,MAAM,WAAW,KAAK,MAAM,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE;GAC/E,IAAI,MAAM,SAAS,WAAW,GAC5B,KAAK,IAAI,MAAM,MAAM,WAAW,IAAI;QAC/B;IACL,KAAK,IAAI,MAAM,MAAM,WAAW,EAAE;IAClC,MAAM,MAAM,QAAQ;IACpB,KAAK,KAAK,MAAM,IAAI,EAAE;GACxB;EACF;CAEJ;CACA,MAAM,QAAQ;CACd,OAAO;AACT;;AAGA,MAAM,oBAAoB,UAAkC,WAA2B;CACrF,MAAM,SAAS,gBAAgB,QAAQ;CACvC,MAAM,QAAuB,CAAC;CAC9B,IAAI,OAAsB;CAC1B,KAAK,MAAM,SAAS,QAClB,IAAI,SAAS,MACX,OAAO,GAAG,SAAS;MACd,IAAI,KAAK,SAAS,IAAI,MAAM,UAAU,WAC3C,QAAQ,IAAI;MACP;EACL,MAAM,KAAK,IAAI;EACf,OAAO,GAAG,SAAS;CACrB;CAEF,IAAI,SAAS,MAAM,MAAM,KAAK,IAAI;CAClC,OAAO,MAAM,KAAK,IAAI;AACxB;AAEA,MAAM,gBAAgB,MAAmB,QAAgB,gBAAiC;CACxF,MAAM,aAAa,KAAK,WAAW,KAAK,MAAM,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE;CAC9E,MAAM,aAAa,GAAG,OAAO,GAAG,KAAK,MAAM,aAAa,cAAc,QAAQ;CAC9E,IAAI,WAAW,UAAU,aAAa,KAAK,WAAW,WAAW,GAAG,OAAO;CAC3E,MAAM,QAAQ,CAAC,GAAG,OAAO,GAAG,KAAK,KAAK;CACtC,KAAK,MAAM,aAAa,KAAK,YAC3B,MAAM,KAAK,GAAG,SAAS,SAAS,eAAe,SAAS,GAAG;CAE7D,MAAM,KAAK,GAAG,SAAS,cAAc,OAAO,KAAK;CACjD,OAAO,MAAM,KAAK,IAAI;AACxB;;;;;;;AAUA,MAAM,qBAAqB,aAA6C;CACtE,IAAI,SAAS;CACb,KAAK,MAAM,SAAS,UAClB,IAAI,MAAM,SAAS,QAAQ,UAAU,WAAW,MAAM,KAAK;MACtD,IAAI,MAAM,SAAS,iBAAiB,UAAU,mBAAmB,KAAK;MACtE,UAAU,qBAAqB,KAAK;CAE3C,OAAO;AACT;AAEA,MAAM,wBAAwB,SAA8B;CAC1D,MAAM,aAAa,KAAK,WAAW,KAAK,MAAM,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE;CAC9E,OAAO,KAAK,SAAS,WAAW,IAC5B,IAAI,KAAK,MAAM,WAAW,OAC1B,IAAI,KAAK,MAAM,WAAW,GAAG,kBAAkB,KAAK,QAAQ,EAAE,IAAI,KAAK,IAAI;AACjF;;AAGA,MAAM,0BAA0B,MAAmB,WAA2B;CAC5E,IAAI,KAAK,SAAS,WAAW,GAAG,OAAO,aAAa,MAAM,QAAQ,IAAI;CACtE,MAAM,aAAa,KAAK,WAAW,KAAK,MAAM,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE;CAC9E,OAAO,GAAG,OAAO,GAAG,KAAK,MAAM,WAAW,GAAG,kBAAkB,KAAK,QAAQ,EAAE,IAAI,KAAK,IAAI;AAC7F;;AAGA,MAAM,uBAAuB,MAAmB,eAC9C,KAAK,QAAQ,SACb,KAAK,QAAQ,YACb,KAAK,QAAQ,UACb,KAAK,QAAQ,YACZ,cAAc,KAAK,QAAQ;AAE9B,MAAM,gBAAgB,MAAmB,OAAe,aAAa,UAAkB;CACrF,MAAM,SAAS,OAAO,OAAO,KAAK;CAClC,IAAI,oBAAoB,MAAM,UAAU,GAAG,OAAO,uBAAuB,MAAM,MAAM;CACrF,IAAI,KAAK,SAAS,WAAW,GAAG,OAAO,aAAa,MAAM,QAAQ,IAAI;CAEtE,IAAI,kBAAkB,IAAI,KAAK,CAAC,YAAY;EAC1C,MAAM,OAAO,gBAAgB,KAAK,QAAQ;EAC1C,MAAM,UAAU,aAAa,MAAM,QAAQ,KAAK;EAChD,MAAM,aAAa,GAAG,UAAU,KAAK,IAAI,KAAK,IAAI;EAClD,IAAI,CAAC,QAAQ,SAAS,IAAI,KAAK,WAAW,UAAU,WAAW,OAAO;EACtE,OAAO,GAAG,QAAQ,IAAI,iBAAiB,KAAK,UAAU,SAAS,MAAM,EAAE,IAAI,OAAO,IAAI,KAAK,IAAI;CACjG;CAEA,MAAM,iBAAiB,cAAc,KAAK,QAAQ;CAWlD,OAAO,GAVS,aAAa,MAAM,QAAQ,KAU3B,EAAE,IATD,KAAK,SACnB,KAAK,UACJ,MAAM,SAAS,YACX,aAAa,OAAO,QAAQ,GAAG,cAAc,IAC7C,MAAM,SAAS,SACb,GAAG,SAAS,SAAS,WAAW,MAAM,KAAK,MAC3C,GAAG,SAAS,SAAS,mBAAmB,KAAK,GACrD,CAAC,CACA,KAAK,IACqB,EAAE,IAAI,OAAO,IAAI,KAAK,IAAI;AACzD;AAEA,MAAa,YAAY,aAAkC,GAAG,aAAa,SAAS,MAAM,CAAC,EAAE"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"registry.js","names":[],"sources":["../src/registry.ts"],"sourcesContent":["import type { TokenTable } from \"./theme\";\n\n// Declarative utility registry (kumo component-registry pattern): every\n// supported class family is a data row — match root, resolve value against the\n// layered token table, emit literal declarations. Adding a family means adding\n// a row; the resolver (classes.ts) never changes. `description` feeds agent\n// docs and the generated language reference (M8); `caniemailSlugs` wires each\n// family into the checks engine (M3).\n\nexport interface Declaration {\n readonly property: string;\n readonly value: string;\n}\n\nexport interface UtilityFamily {\n readonly root: string;\n /** Accepts `root` with no value (\"border\", \"rounded\", \"block\"). */\n readonly bare?: ReadonlyArray<Declaration> | undefined;\n /** Resolve a named value; null = this family does not accept it. */\n readonly named?:\n | ((value: string, theme: TokenTable) => ReadonlyArray<Declaration> | null)\n | undefined;\n /** Resolve an arbitrary `[...]` value; null = malformed for this family. */\n readonly arbitrary?:\n | ((value: string, theme: TokenTable) => ReadonlyArray<Declaration> | null)\n | undefined;\n /** dark: variant is only legal on color families. */\n readonly colorFamily?: boolean | undefined;\n /**\n * Base-variant only: sm:/dark: are rejected. Set on the gradient family —\n * its declarations are compiler-internal placeholders that must be combined\n * per node, which head rules cannot express.\n */\n readonly baseOnly?: boolean | undefined;\n /** \"warn\" families compile but surface a lint warning. */\n readonly warn?: string | undefined;\n readonly description: string;\n readonly caniemailSlugs: ReadonlyArray<string>;\n}\n\nexport interface ForbiddenUtility {\n /** Exact class or `prefix-` match against the candidate base. */\n readonly match: string;\n readonly message: string;\n}\n\nconst SPACING_PATTERN = /^\\d+(\\.(25|5|75))?$/;\nconst ARBITRARY_LENGTH_PATTERN = /^\\d+(\\.\\d+)?(px|%)$/;\nconst ARBITRARY_PX_PATTERN = /^\\d+(\\.\\d+)?px$/;\nconst ARBITRARY_HEX_PATTERN = /^#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$/;\n\nconst px = (value: number): string => `${roundPx(value)}px`;\n\n// Avoid float dust like 4.800000000000001px for fractional spacing steps.\nconst roundPx = (value: number): number => Math.round(value * 100) / 100;\n\nconst spacingValue = (value: string, theme: TokenTable): string | null => {\n if (value === \"px\") return \"1px\";\n if (!SPACING_PATTERN.test(value)) return null;\n return px(Number.parseFloat(value) * theme.spacingPx);\n};\n\nconst arbitraryLength = (value: string): ReadonlyArray<string> | null =>\n ARBITRARY_LENGTH_PATTERN.test(value) ? [value] : null;\n\nconst colorValue = (value: string, theme: TokenTable): string | null =>\n value === \"transparent\" ? \"transparent\" : (theme.colors[value] ?? null);\n\nconst declarations = (\n properties: ReadonlyArray<string>,\n value: string,\n): ReadonlyArray<Declaration> => properties.map((property) => ({ property, value }));\n\nconst textSizeDeclarations = (sizePx: number): ReadonlyArray<Declaration> => [\n { property: \"font-size\", value: px(sizePx) },\n { property: \"line-height\", value: px(sizePx * 1.5) },\n];\n\nconst arbitraryTextSize = (value: string): ReadonlyArray<Declaration> | null => {\n if (!ARBITRARY_PX_PATTERN.test(value)) return null;\n const size = Number.parseFloat(value);\n return size >= 8 && size <= 72 ? textSizeDeclarations(size) : null;\n};\n\nconst radiusValue = (value: string, theme: TokenTable): string | null => {\n if (value === \"none\") return \"0px\";\n if (value === \"full\") return \"9999px\";\n const radius = theme.radiusPx[value];\n return radius === undefined ? null : px(radius);\n};\n\nconst radiusFamily = (\n root: string,\n properties: ReadonlyArray<string>,\n description: string,\n): UtilityFamily => {\n const family: UtilityFamily = {\n root,\n named: (value, theme) => {\n const resolved = radiusValue(value, theme);\n return resolved === null ? null : declarations(properties, resolved);\n },\n arbitrary: (value) =>\n ARBITRARY_PX_PATTERN.test(value) ? declarations(properties, value) : null,\n description,\n caniemailSlugs: [\"css-border-radius\"],\n };\n return root === \"rounded\"\n ? {\n ...family,\n bare: properties.map((property) => ({ property, value: \"4px\" })),\n }\n : family;\n};\n\nconst spacingFamily = (\n root: string,\n properties: ReadonlyArray<string>,\n options: {\n readonly description: string;\n readonly caniemailSlugs: ReadonlyArray<string>;\n readonly warn?: string | undefined;\n readonly auto?: boolean | undefined;\n },\n): UtilityFamily => ({\n root,\n named: (value, theme) => {\n if (options.auto === true && value === \"auto\") return declarations(properties, \"auto\");\n const resolved = spacingValue(value, theme);\n return resolved === null ? null : declarations(properties, resolved);\n },\n arbitrary: (value) => (ARBITRARY_PX_PATTERN.test(value) ? declarations(properties, value) : null),\n description: options.description,\n caniemailSlugs: options.caniemailSlugs,\n warn: options.warn,\n});\n\nconst MARGIN_WARNING =\n \"margin is stripped by some clients (Outlook.com dark mode, some webmail); prefer padding or <Spacer>\";\n\n// ── Gradient placeholders ─────────────────────────────────────────────────────\n// The gradient family resolves to compiler-internal placeholder declarations;\n// the compiler combines the three per node into real background declarations\n// (modern base: background-image linear-gradient + a solid background-color\n// fallback; outlook-safe base: the solid `from` color only — Outlook for\n// Windows has no gradient support worth a VML hack). They never reach an\n// artifact.\nexport const GRADIENT_DIRECTION_PROPERTY = \"-sml-gradient-direction\";\nexport const GRADIENT_FROM_PROPERTY = \"-sml-gradient-from\";\nexport const GRADIENT_TO_PROPERTY = \"-sml-gradient-to\";\n\nconst GRADIENT_DIRECTIONS: Readonly<Record<string, string>> = {\n \"to-t\": \"to top\",\n \"to-b\": \"to bottom\",\n \"to-l\": \"to left\",\n \"to-r\": \"to right\",\n};\n\nexport const utilityRegistry: ReadonlyArray<UtilityFamily> = [\n // ── Padding ────────────────────────────────────────────────────────────────\n spacingFamily(\"p\", [\"padding\"], {\n description: \"Padding on all sides (p-4 = 16px). Compiles onto table cells for Outlook.\",\n caniemailSlugs: [\"css-padding\"],\n }),\n spacingFamily(\"px\", [\"padding-left\", \"padding-right\"], {\n description: \"Horizontal padding.\",\n caniemailSlugs: [\"css-padding\"],\n }),\n spacingFamily(\"py\", [\"padding-top\", \"padding-bottom\"], {\n description: \"Vertical padding.\",\n caniemailSlugs: [\"css-padding\"],\n }),\n spacingFamily(\"pt\", [\"padding-top\"], {\n description: \"Top padding.\",\n caniemailSlugs: [\"css-padding\"],\n }),\n spacingFamily(\"pr\", [\"padding-right\"], {\n description: \"Right padding.\",\n caniemailSlugs: [\"css-padding\"],\n }),\n spacingFamily(\"pb\", [\"padding-bottom\"], {\n description: \"Bottom padding.\",\n caniemailSlugs: [\"css-padding\"],\n }),\n spacingFamily(\"pl\", [\"padding-left\"], {\n description: \"Left padding.\",\n caniemailSlugs: [\"css-padding\"],\n }),\n\n // ── Margin (warn tier) ─────────────────────────────────────────────────────\n spacingFamily(\"m\", [\"margin\"], {\n description: \"Margin on all sides. Warn tier — some clients strip margins.\",\n caniemailSlugs: [\"css-margin\"],\n warn: MARGIN_WARNING,\n }),\n spacingFamily(\"mx\", [\"margin-left\", \"margin-right\"], {\n description: \"Horizontal margin; mx-auto centers block elements.\",\n caniemailSlugs: [\"css-margin\"],\n warn: MARGIN_WARNING,\n auto: true,\n }),\n spacingFamily(\"my\", [\"margin-top\", \"margin-bottom\"], {\n description: \"Vertical margin.\",\n caniemailSlugs: [\"css-margin\"],\n warn: MARGIN_WARNING,\n }),\n spacingFamily(\"mt\", [\"margin-top\"], {\n description: \"Top margin.\",\n caniemailSlugs: [\"css-margin\"],\n warn: MARGIN_WARNING,\n }),\n spacingFamily(\"mr\", [\"margin-right\"], {\n description: \"Right margin.\",\n caniemailSlugs: [\"css-margin\"],\n warn: MARGIN_WARNING,\n auto: true,\n }),\n spacingFamily(\"mb\", [\"margin-bottom\"], {\n description: \"Bottom margin.\",\n caniemailSlugs: [\"css-margin\"],\n warn: MARGIN_WARNING,\n }),\n spacingFamily(\"ml\", [\"margin-left\"], {\n description: \"Left margin.\",\n caniemailSlugs: [\"css-margin\"],\n warn: MARGIN_WARNING,\n auto: true,\n }),\n\n // ── Sizing ─────────────────────────────────────────────────────────────────\n {\n root: \"w\",\n named: (value, theme) => {\n if (value === \"full\") return [{ property: \"width\", value: \"100%\" }];\n const resolved = spacingValue(value, theme);\n return resolved === null ? null : [{ property: \"width\", value: resolved }];\n },\n arbitrary: (value) => (arbitraryLength(value) === null ? null : [{ property: \"width\", value }]),\n description: \"Width from the spacing scale (w-32 = 128px), w-full, or w-[600px]/w-[50%].\",\n caniemailSlugs: [\"css-width\"],\n },\n {\n root: \"max-w\",\n named: (value, theme) => {\n if (value === \"full\") return [{ property: \"max-width\", value: \"100%\" }];\n if (value === \"none\") return [{ property: \"max-width\", value: \"none\" }];\n const container = theme.containerPx[value];\n return container === undefined ? null : [{ property: \"max-width\", value: px(container) }];\n },\n arbitrary: (value) =>\n arbitraryLength(value) === null ? null : [{ property: \"max-width\", value }],\n description: \"Max width from the container scale (max-w-lg = 512px) or max-w-[600px].\",\n caniemailSlugs: [\"css-max-width\"],\n },\n {\n root: \"h\",\n named: (value, theme) => {\n if (value === \"full\") return [{ property: \"height\", value: \"100%\" }];\n const resolved = spacingValue(value, theme);\n return resolved === null ? null : [{ property: \"height\", value: resolved }];\n },\n arbitrary: (value) =>\n arbitraryLength(value) === null ? null : [{ property: \"height\", value }],\n description: \"Height from the spacing scale (h-12 = 48px) or h-[80px].\",\n caniemailSlugs: [\"css-height\"],\n },\n\n // ── Typography ─────────────────────────────────────────────────────────────\n {\n root: \"text\",\n named: (value, theme) => {\n const size = theme.text[value];\n if (size === undefined) return null;\n return [\n ...declarations([\"font-size\"], px(size.sizePx)),\n ...declarations([\"line-height\"], px(size.lineHeightPx)),\n ];\n },\n arbitrary: arbitraryTextSize,\n description:\n \"Font size with paired line-height (text-sm = 14px/20px) or bounded px value (text-[15px] = 15px/22.5px). Put an explicit leading-* after it to override line-height.\",\n caniemailSlugs: [\"css-font-size\", \"css-line-height\"],\n },\n {\n root: \"text\",\n named: (value) =>\n value === \"left\" || value === \"center\" || value === \"right\" || value === \"justify\"\n ? [{ property: \"text-align\", value }]\n : null,\n description: \"Text alignment: text-left, text-center, text-right, text-justify.\",\n caniemailSlugs: [\"css-text-align\"],\n },\n {\n root: \"text\",\n colorFamily: true,\n named: (value, theme) => {\n const color = colorValue(value, theme);\n return color === null ? null : [{ property: \"color\", value: color }];\n },\n arbitrary: (value) =>\n ARBITRARY_HEX_PATTERN.test(value)\n ? [{ property: \"color\", value: value.toLowerCase() }]\n : null,\n description: \"Text color from the palette (text-gray-700) or text-[#374151].\",\n caniemailSlugs: [\"css-color\"],\n },\n {\n root: \"leading\",\n named: (value, theme) => {\n if (value === \"none\") return [{ property: \"line-height\", value: \"1\" }];\n const ratio = theme.leading[value];\n if (ratio !== undefined) return [{ property: \"line-height\", value: String(ratio) }];\n const resolved = spacingValue(value, theme);\n return resolved === null ? null : [{ property: \"line-height\", value: resolved }];\n },\n arbitrary: (value) =>\n ARBITRARY_PX_PATTERN.test(value) ? [{ property: \"line-height\", value }] : null,\n description: \"Line height: named ratios (leading-relaxed) or spacing steps (leading-6 = 24px).\",\n caniemailSlugs: [\"css-line-height\"],\n },\n {\n root: \"font\",\n named: (value, theme) => {\n const weight = theme.fontWeights[value];\n return weight === undefined ? null : [{ property: \"font-weight\", value: String(weight) }];\n },\n description: \"Font weight: font-normal, font-medium, font-semibold, font-bold, ...\",\n caniemailSlugs: [\"css-font-weight\"],\n },\n {\n root: \"font\",\n named: (value, theme) => {\n const family = theme.fontFamilies[value];\n return family === undefined ? null : [{ property: \"font-family\", value: family }];\n },\n description: \"Font stack: font-sans, font-serif, font-mono.\",\n caniemailSlugs: [\"css-font\"],\n },\n {\n root: \"tracking\",\n named: (value, theme) => {\n const em = theme.trackingEm[value];\n return em === undefined ? null : [{ property: \"letter-spacing\", value: `${em}em` }];\n },\n description:\n \"Letter spacing: tracking-tighter … tracking-widest (em-based; inlines fine in Outlook). Pair tracking-wide/wider with uppercase for eyebrows.\",\n caniemailSlugs: [\"css-letter-spacing\"],\n },\n {\n root: \"uppercase\",\n bare: [{ property: \"text-transform\", value: \"uppercase\" }],\n description: \"Uppercase text transform.\",\n caniemailSlugs: [\"css-text-transform\"],\n },\n {\n root: \"underline\",\n bare: [{ property: \"text-decoration\", value: \"underline\" }],\n description: \"Underlined text.\",\n caniemailSlugs: [\"css-text-decoration\"],\n },\n {\n root: \"no-underline\",\n bare: [{ property: \"text-decoration\", value: \"none\" }],\n description: \"Removes the underline (links render underlined by default in most clients).\",\n caniemailSlugs: [\"css-text-decoration\"],\n },\n\n // ── Color ──────────────────────────────────────────────────────────────────\n {\n root: \"bg\",\n colorFamily: true,\n named: (value, theme) => {\n const color = colorValue(value, theme);\n return color === null ? null : [{ property: \"background-color\", value: color }];\n },\n arbitrary: (value) =>\n ARBITRARY_HEX_PATTERN.test(value)\n ? [{ property: \"background-color\", value: value.toLowerCase() }]\n : null,\n description: \"Background color from the palette (bg-white, bg-blue-600) or bg-[#0088cc].\",\n caniemailSlugs: [\"css-background-color\"],\n },\n\n // ── Gradient (bg-gradient-to-* + from-* + to-*) ────────────────────────────\n {\n root: \"bg-gradient\",\n baseOnly: true,\n named: (value) => {\n const direction = GRADIENT_DIRECTIONS[value];\n return direction === undefined\n ? null\n : [{ property: GRADIENT_DIRECTION_PROPERTY, value: direction }];\n },\n description:\n \"Linear gradient background for hero sections: bg-gradient-to-b/t/l/r with from-{color} and to-{color}. Modern base compiles a CSS linear-gradient (plus a solid from-color fallback); the outlook-safe base degrades to the solid from-color — Outlook for Windows never renders the gradient.\",\n caniemailSlugs: [\"css-linear-gradient\", \"css-background-image\"],\n },\n {\n root: \"from\",\n baseOnly: true,\n named: (value, theme) => {\n const color = colorValue(value, theme);\n return color === null ? null : [{ property: GRADIENT_FROM_PROPERTY, value: color }];\n },\n arbitrary: (value) =>\n ARBITRARY_HEX_PATTERN.test(value)\n ? [{ property: GRADIENT_FROM_PROPERTY, value: value.toLowerCase() }]\n : null,\n description:\n \"Gradient start color (from-indigo-600). Doubles as the solid fallback color on clients without gradient support (Outlook for Windows).\",\n caniemailSlugs: [\"css-linear-gradient\"],\n },\n {\n root: \"to\",\n baseOnly: true,\n named: (value, theme) => {\n const color = colorValue(value, theme);\n return color === null ? null : [{ property: GRADIENT_TO_PROPERTY, value: color }];\n },\n arbitrary: (value) =>\n ARBITRARY_HEX_PATTERN.test(value)\n ? [{ property: GRADIENT_TO_PROPERTY, value: value.toLowerCase() }]\n : null,\n description: \"Gradient end color (to-purple-600).\",\n caniemailSlugs: [\"css-linear-gradient\"],\n },\n\n // ── Border ─────────────────────────────────────────────────────────────────\n {\n root: \"border\",\n bare: [\n { property: \"border-width\", value: \"1px\" },\n { property: \"border-style\", value: \"solid\" },\n ],\n named: (value) =>\n /^\\d+$/.test(value)\n ? [\n { property: \"border-width\", value: `${value}px` },\n { property: \"border-style\", value: \"solid\" },\n ]\n : null,\n description: \"Solid border width: border (1px), border-2, border-0.\",\n caniemailSlugs: [\"css-border\"],\n },\n {\n root: \"border\",\n colorFamily: true,\n named: (value, theme) => {\n const color = colorValue(value, theme);\n return color === null ? null : [{ property: \"border-color\", value: color }];\n },\n arbitrary: (value) =>\n ARBITRARY_HEX_PATTERN.test(value)\n ? [{ property: \"border-color\", value: value.toLowerCase() }]\n : null,\n description: \"Border color from the palette (border-gray-200).\",\n caniemailSlugs: [\"css-border\"],\n },\n radiusFamily(\n \"rounded\",\n [\"border-radius\"],\n \"Border radius (rounded-md = 6px). Ignored by Outlook for Windows — buttons degrade to square corners.\",\n ),\n radiusFamily(\n \"rounded-t\",\n [\"border-top-left-radius\", \"border-top-right-radius\"],\n \"Top border radius for card/header blocks (rounded-t-lg). Degrades to square corners in Outlook for Windows.\",\n ),\n radiusFamily(\n \"rounded-r\",\n [\"border-top-right-radius\", \"border-bottom-right-radius\"],\n \"Right-side border radius (rounded-r-lg). Degrades to square corners in Outlook for Windows.\",\n ),\n radiusFamily(\n \"rounded-b\",\n [\"border-bottom-right-radius\", \"border-bottom-left-radius\"],\n \"Bottom border radius for card/footer blocks (rounded-b-lg). Degrades to square corners in Outlook for Windows.\",\n ),\n radiusFamily(\n \"rounded-l\",\n [\"border-top-left-radius\", \"border-bottom-left-radius\"],\n \"Left-side border radius (rounded-l-lg). Degrades to square corners in Outlook for Windows.\",\n ),\n\n // ── Display ────────────────────────────────────────────────────────────────\n {\n root: \"block\",\n bare: [{ property: \"display\", value: \"block\" }],\n description: \"display: block.\",\n caniemailSlugs: [\"css-display\"],\n },\n {\n root: \"inline-block\",\n bare: [{ property: \"display\", value: \"inline-block\" }],\n description: \"display: inline-block.\",\n caniemailSlugs: [\"css-display\"],\n },\n {\n root: \"hidden\",\n bare: [{ property: \"display\", value: \"none\" }],\n description: \"display: none. Pair with sm:block for mobile-only content.\",\n caniemailSlugs: [\"css-display\"],\n },\n];\n\n// Forbidden utilities are typed compile errors that name the failing client and\n// the supported alternative — drop features rather than break clients.\nexport const forbiddenUtilities: ReadonlyArray<ForbiddenUtility> = [\n {\n match: \"flex\",\n message:\n \"display:flex is unsupported in Outlook for Windows (Word engine) — use <Row>/<Column>\",\n },\n {\n match: \"inline-flex\",\n message: \"display:inline-flex is unsupported in Outlook for Windows — use <Row>/<Column>\",\n },\n {\n match: \"grid\",\n message: \"display:grid is unsupported in Outlook for Windows and Gmail — use <Row>/<Column>\",\n },\n {\n match: \"inline-grid\",\n message: \"display:inline-grid is unsupported in Outlook for Windows — use <Row>/<Column>\",\n },\n {\n match: \"gap-\",\n message: \"gap requires flex/grid, unsupported in Outlook for Windows — use padding on <Column>\",\n },\n {\n match: \"space-\",\n message: \"space-* margins require selectors Gmail strips — use padding or <Spacer>\",\n },\n {\n match: \"items-\",\n message: \"flexbox alignment is unsupported in Outlook for Windows — use <Row>/<Column>\",\n },\n {\n match: \"justify-\",\n message:\n \"flexbox alignment is unsupported in Outlook for Windows — use <Row>/<Column> and text-center\",\n },\n {\n match: \"self-\",\n message: \"flexbox alignment is unsupported in Outlook for Windows — use <Row>/<Column>\",\n },\n {\n match: \"content-\",\n message: \"flexbox alignment is unsupported in Outlook for Windows — use <Row>/<Column>\",\n },\n {\n match: \"order-\",\n message: \"flex order is unsupported in Outlook for Windows — reorder the source instead\",\n },\n {\n match: \"absolute\",\n message: \"CSS position is ignored by Gmail and Outlook — use nested <Section>s\",\n },\n {\n match: \"relative\",\n message: \"CSS position is ignored by Gmail and Outlook — use nested <Section>s\",\n },\n {\n match: \"fixed\",\n message: \"CSS position is ignored by Gmail and Outlook — use nested <Section>s\",\n },\n {\n match: \"sticky\",\n message: \"CSS position is ignored by Gmail and Outlook — use nested <Section>s\",\n },\n { match: \"static\", message: \"CSS position is ignored by Gmail and Outlook — remove it\" },\n { match: \"inset-\", message: \"position offsets are ignored by Gmail and Outlook — use padding\" },\n { match: \"top-\", message: \"position offsets are ignored by Gmail and Outlook — use padding\" },\n { match: \"right-\", message: \"position offsets are ignored by Gmail and Outlook — use padding\" },\n { match: \"bottom-\", message: \"position offsets are ignored by Gmail and Outlook — use padding\" },\n { match: \"left-\", message: \"position offsets are ignored by Gmail and Outlook — use padding\" },\n { match: \"z-\", message: \"z-index is ignored by Gmail and Outlook — remove it\" },\n { match: \"float-\", message: \"float is unreliable across email clients — use <Row>/<Column>\" },\n {\n match: \"shadow\",\n message: \"box-shadow is unsupported in Outlook for Windows and Gmail — use a border instead\",\n },\n {\n match: \"shadow-\",\n message: \"box-shadow is unsupported in Outlook for Windows and Gmail — use a border instead\",\n },\n {\n match: \"opacity-\",\n message: \"opacity is unsupported in Outlook for Windows — bake the tint into the color\",\n },\n { match: \"transform\", message: \"CSS transforms are unsupported in email clients — remove them\" },\n { match: \"rotate-\", message: \"CSS transforms are unsupported in email clients — remove them\" },\n { match: \"scale-\", message: \"CSS transforms are unsupported in email clients — remove them\" },\n { match: \"translate-\", message: \"CSS transforms are unsupported in email clients — remove them\" },\n { match: \"skew-\", message: \"CSS transforms are unsupported in email clients — remove them\" },\n { match: \"transition\", message: \"transitions don't run in email — remove them\" },\n { match: \"transition-\", message: \"transitions don't run in email — remove them\" },\n { match: \"duration-\", message: \"transitions don't run in email — remove them\" },\n { match: \"animate-\", message: \"animations don't run in email — remove them\" },\n { match: \"blur-\", message: \"CSS filters are unsupported in most email clients — remove them\" },\n {\n match: \"brightness-\",\n message: \"CSS filters are unsupported in most email clients — remove them\",\n },\n {\n match: \"grayscale\",\n message: \"CSS filters are unsupported in most email clients — remove them\",\n },\n {\n match: \"grayscale-\",\n message: \"CSS filters are unsupported in most email clients — remove them\",\n },\n {\n match: \"truncate\",\n message: \"text truncation is unsupported in Outlook for Windows — shorten the copy\",\n },\n {\n match: \"line-clamp-\",\n message: \"line clamping is unsupported in email clients — shorten the copy\",\n },\n {\n match: \"aspect-\",\n message: \"aspect-ratio is unsupported in Outlook and Gmail — set width/height on <Image>\",\n },\n {\n match: \"overflow-\",\n message: \"overflow is unreliable across email clients — restructure the layout\",\n },\n];\n\n/** Every root the resolver may try to split on, longest-match first (candidate.ts). */\nexport const registryRoots: ReadonlySet<string> = new Set(\n utilityRegistry.map((family) => family.root),\n);\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"render-template.js","names":[],"sources":["../src/render-template.ts"],"sourcesContent":["import { Result } from \"effect\";\n\nimport type { ElementNode, SmlDocument, SmlNode } from \"./ast\";\nimport { isRootTag } from \"./ast\";\nimport type { SmlParseError } from \"./diagnostics\";\nimport type { JsxResult } from \"./jsx-runtime\";\nimport { parseSml } from \"./parser\";\nimport { collectPredicateVariables, parsePredicate } from \"./predicate\";\nimport { printSml } from \"./printer\";\nimport type { VariableKind } from \"./variables\";\nimport { createVariablesProxy, VariableRecorder } from \"./variables\";\n\n// renderTemplate — the emit boundary for the TSX authoring layer (design brief\n// §7b). It invokes a named template function with the recording proxy, turns the\n// constructed draft tree into canonical SML source, then parses that source back:\n//\n// fn(proxy) → draft <Email> → printSml → source → parseSml → SmlDocument\n//\n// Parsing the printed source is the whole validation strategy: the parser is the\n// single source of grammatical truth (content models, attribute types/bounds,\n// predicate grammar, node-id assignment). A draft that violates the grammar —\n// text where only blocks are allowed, an out-of-range Heading level, an <Else>\n// that isn't last — fails here with the same span-precise diagnostics an agent\n// or dashboard user would get, so the two authoring paths cannot diverge.\n//\n// Node ids are therefore PARSE-assigned (document order, \"n0\"…): identical\n// template output yields identical ids, matching what the dashboard/canvas and\n// compiler see for the equivalent hand-authored .samva.\n\n/**\n * A template is a named function of the recording proxy returning the draft\n * root. The JSX transform types every element expression as `JSX.Element`\n * (`JsxResult`); a well-formed template returns an `<Email>` element, and a\n * stray Fragment root is caught at render time.\n */\nexport type Template<T extends Record<string, unknown>> = (\n variables: import(\"./variables\").Variables<T>,\n) => JsxResult;\n\n/** One entry of a template's inferred variable schema. */\nexport interface TemplateVariable {\n readonly name: string;\n /** Kind evidence from usage: numeric comparison ⇒ number, string ⇒ string, else unknown. */\n readonly kind: VariableKind;\n /**\n * `true` when the variable is referenced outside every `<If>`/`<Else>` subtree\n * (always needed to render); `false` when it only appears inside conditional\n * branches (needed only when that branch is selected).\n */\n readonly required: boolean;\n}\n\nexport interface RenderedTemplate {\n readonly document: SmlDocument;\n readonly source: string;\n readonly variables: ReadonlyArray<TemplateVariable>;\n}\n\n/**\n * Runs a template function and returns its canonical document, source, and\n * inferred variable schema. Grammar violations in the constructed tree surface\n * as a parse `SmlParseError` (Result failure) with spans into the printed\n * source. The template function itself may throw (e.g. the coercion guard) —\n * that is a programmer error in the user's build, so it propagates rather than\n * becoming a diagnostic.\n */\nexport const renderTemplate = <T extends Record<string, unknown>>(\n template: Template<T>,\n): Result.Result<RenderedTemplate, SmlParseError> => {\n const { proxy, recorder } = createVariablesProxy<T>();\n const root = template(proxy);\n if (root.kind !== \"element\" || !isRootTag(root.tag)) {\n // oxlint-disable-next-line samva/no-try-catch-or-throw, samva/no-error-constructor -- User-build authoring boundary: a non-root template result is a programmer error that must fail the user's TSX build loudly, not become a typed Effect failure.\n throw new TypeError(\n \"a template must return a single channel root element (<Email>, <Sms>, or <WhatsApp>), not a Fragment or other node\",\n );\n }\n const source = printSml({ kind: \"document\", root: root satisfies ElementNode });\n const parsed = parseSml(source);\n if (Result.isFailure(parsed)) return Result.fail(parsed.failure);\n const document = parsed.success;\n return Result.succeed({\n document,\n source,\n variables: collectVariableSchema(document, recorder),\n });\n};\n\n/**\n * Merges the recorder's kind evidence with structural required-ness read off the\n * parsed document. A name referenced anywhere outside an `<If>` subtree is\n * required; names seen only inside conditional branches are optional.\n */\nconst collectVariableSchema = (\n document: SmlDocument,\n recorder: VariableRecorder,\n): ReadonlyArray<TemplateVariable> => {\n const unconditional = new Set<string>();\n const conditional = new Set<string>();\n const kindOf = new Map(recorder.collected().map((variable) => [variable.name, variable.kind]));\n\n const visit = (node: SmlNode, insideIf: boolean): void => {\n if (node.kind === \"interpolation\") {\n (insideIf ? conditional : unconditional).add(node.name);\n return;\n }\n if (node.kind === \"text\") return;\n const nowInsideIf = insideIf || node.tag === \"If\";\n for (const attribute of node.attributes) {\n if (attribute.value.kind !== \"string\") continue;\n // An <If test> predicate is evaluated whenever the If itself is reached,\n // so its variables take the If's OWN position: required at top level,\n // conditional only when this If is nested inside another branch.\n if (node.tag === \"If\" && attribute.name === \"test\") {\n const parsed = parsePredicate(attribute.value.value, 0);\n if (Result.isSuccess(parsed)) {\n for (const name of collectPredicateVariables(parsed.success)) {\n (insideIf ? conditional : unconditional).add(name);\n }\n }\n continue;\n }\n for (const match of attribute.value.value.matchAll(INTERPOLATION_PATTERN)) {\n const name = match[1] ?? match[2];\n if (name !== undefined) (nowInsideIf ? conditional : unconditional).add(name);\n }\n }\n for (const child of node.children) visit(child, nowInsideIf);\n };\n visit(document.root, false);\n\n // Names come from the document itself (interpolations + predicates) — the\n // recorder only contributes KIND evidence, so a stray proxy access can't\n // mint a schema entry the template never uses.\n const names = new Set<string>([...unconditional, ...conditional]);\n return [...names].map((name) => ({\n name,\n kind: kindOf.get(name) ?? \"unknown\",\n required: unconditional.has(name),\n }));\n};\n\nconst INTERPOLATION_PATTERN = /\\$\\{\\s*\\/?([\\w.]+)\\s*\\}|\\{\\{\\s*([\\w.]+)\\s*\\}\\}/g;\n"],"mappings":";;;;;;;;;;;;;;;AAkEA,MAAa,kBACX,aACmD;CACnD,MAAM,EAAE,OAAO,aAAa,qBAAwB;CACpD,MAAM,OAAO,SAAS,KAAK;CAC3B,IAAI,KAAK,SAAS,aAAa,CAAC,UAAU,KAAK,GAAG,GAEhD,MAAM,IAAI,UACR,oHACF;CAEF,MAAM,SAAS,SAAS;EAAE,MAAM;EAAkB;CAA2B,CAAC;CAC9E,MAAM,SAAS,SAAS,MAAM;CAC9B,IAAI,OAAO,UAAU,MAAM,GAAG,OAAO,OAAO,KAAK,OAAO,OAAO;CAC/D,MAAM,WAAW,OAAO;CACxB,OAAO,OAAO,QAAQ;EACpB;EACA;EACA,WAAW,sBAAsB,UAAU,QAAQ;CACrD,CAAC;AACH;;;;;;AAOA,MAAM,yBACJ,UACA,aACoC;CACpC,MAAM,gCAAgB,IAAI,IAAY;CACtC,MAAM,8BAAc,IAAI,IAAY;CACpC,MAAM,SAAS,IAAI,IAAI,SAAS,UAAU,CAAC,CAAC,KAAK,aAAa,CAAC,SAAS,MAAM,SAAS,IAAI,CAAC,CAAC;CAE7F,MAAM,SAAS,MAAe,aAA4B;EACxD,IAAI,KAAK,SAAS,iBAAiB;GACjC,CAAC,WAAW,cAAc,cAAA,CAAe,IAAI,KAAK,IAAI;GACtD;EACF;EACA,IAAI,KAAK,SAAS,QAAQ;EAC1B,MAAM,cAAc,YAAY,KAAK,QAAQ;EAC7C,KAAK,MAAM,aAAa,KAAK,YAAY;GACvC,IAAI,UAAU,MAAM,SAAS,UAAU;GAIvC,IAAI,KAAK,QAAQ,QAAQ,UAAU,SAAS,QAAQ;IAClD,MAAM,SAAS,eAAe,UAAU,MAAM,OAAO,CAAC;IACtD,IAAI,OAAO,UAAU,MAAM,GACzB,KAAK,MAAM,QAAQ,0BAA0B,OAAO,OAAO,GACzD,CAAC,WAAW,cAAc,cAAA,CAAe,IAAI,IAAI;IAGrD;GACF;GACA,KAAK,MAAM,SAAS,UAAU,MAAM,MAAM,SAAS,qBAAqB,GAAG;IACzE,MAAM,OAAO,MAAM,MAAM,MAAM;IAC/B,IAAI,SAAS,KAAA,GAAW,CAAC,cAAc,cAAc,cAAA,CAAe,IAAI,IAAI;GAC9E;EACF;EACA,KAAK,MAAM,SAAS,KAAK,UAAU,MAAM,OAAO,WAAW;CAC7D;CACA,MAAM,SAAS,MAAM,KAAK;CAM1B,OAAO,CAAC,mBAAG,IADO,IAAY,CAAC,GAAG,eAAe,GAAG,WAAW,CAChD,CAAC,CAAC,CAAC,KAAK,UAAU;EAC/B;EACA,MAAM,OAAO,IAAI,IAAI,KAAK;EAC1B,UAAU,cAAc,IAAI,IAAI;CAClC,EAAE;AACJ;AAEA,MAAM,wBAAwB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"sms-segments.js","names":[],"sources":["../src/sms-segments.ts"],"sourcesContent":["// SMS segment math, GSM 03.38. Ported from @samva/document channel-checks so\n// the compile backend (and later the dashboard character counter) share one\n// implementation; @samva/document is deleted in mk3.\n// GSM-7: 160 septets single / 153 multi-segment (UDH uses 7). Extended GSM-7\n// characters cost 2 septets. UCS-2 (any non-GSM char): 70 single / 67 multi.\n// @see https://en.wikipedia.org/wiki/GSM_03.38\n\nconst GSM7_EXTENDED = new Set([\"[\", \"]\", \"{\", \"}\", \"\\\\\", \"~\", \"|\", \"€\", \"^\", \"\\f\"]);\n\nconst GSM7_BASIC_PATTERN =\n /^[@£$¥èéùìòÇ\\nØø\\rÅåΔ_ΦΓΛΩΠΨΣΘΞÆæßÉ !\"#¤%&'()*+,\\-./:;<=>?¡0-9ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà]*$/;\n\nconst containsNonGsmCharacters = (text: string): boolean => {\n for (const char of text) {\n if (GSM7_EXTENDED.has(char)) continue;\n if (!GSM7_BASIC_PATTERN.test(char)) return true;\n }\n return false;\n};\n\n/** Lowercase per the CompiledSms union (document's \"GSM-7\"/\"UCS-2\" renamed). */\nexport type SmsEncoding = \"gsm7\" | \"ucs2\";\n\nexport interface SmsSegmentInfo {\n readonly encoding: SmsEncoding;\n readonly characterCount: number;\n readonly segmentCount: number;\n}\n\nexport const smsSegmentInfo = (text: string): SmsSegmentInfo => {\n if (containsNonGsmCharacters(text)) {\n const codePoints = [...text].length;\n const segmentCount = codePoints <= 70 ? 1 : Math.ceil(codePoints / 67);\n return { encoding: \"ucs2\", characterCount: text.length, segmentCount };\n }\n\n let encodedLength = 0;\n for (const char of text) {\n encodedLength += GSM7_EXTENDED.has(char) ? 2 : 1;\n }\n const segmentCount = encodedLength <= 160 ? 1 : Math.ceil(encodedLength / 153);\n return { encoding: \"gsm7\", characterCount: text.length, segmentCount };\n};\n\n// Recognizes \"STOP\", \"reply STOP\", \"text STOP to ...\", \"STOP to cancel\", etc.\nconst SMS_OPT_OUT_PATTERN = /\\b(stop|unsubscribe|opt[\\s-]?out|cancel|quit)\\b/i;\n\nexport const hasSmsOptOut = (text: string): boolean => SMS_OPT_OUT_PATTERN.test(text);\n"],"mappings":";AAOA,MAAM,gCAAgB,IAAI,IAAI;CAAC;CAAK;CAAK;CAAK;CAAK;CAAM;CAAK;CAAK;CAAK;CAAK;AAAI,CAAC;AAElF,MAAM,qBACJ;AAEF,MAAM,4BAA4B,SAA0B;CAC1D,KAAK,MAAM,QAAQ,MAAM;EACvB,IAAI,cAAc,IAAI,IAAI,GAAG;EAC7B,IAAI,CAAC,mBAAmB,KAAK,IAAI,GAAG,OAAO;CAC7C;CACA,OAAO;AACT;AAWA,MAAa,kBAAkB,SAAiC;CAC9D,IAAI,yBAAyB,IAAI,GAAG;EAClC,MAAM,aAAa,CAAC,GAAG,IAAI,CAAC,CAAC;EAC7B,MAAM,eAAe,cAAc,KAAK,IAAI,KAAK,KAAK,aAAa,EAAE;EACrE,OAAO;GAAE,UAAU;GAAQ,gBAAgB,KAAK;GAAQ;EAAa;CACvE;CAEA,IAAI,gBAAgB;CACpB,KAAK,MAAM,QAAQ,MACjB,iBAAiB,cAAc,IAAI,IAAI,IAAI,IAAI;CAEjD,MAAM,eAAe,iBAAiB,MAAM,IAAI,KAAK,KAAK,gBAAgB,GAAG;CAC7E,OAAO;EAAE,UAAU;EAAQ,gBAAgB,KAAK;EAAQ;CAAa;AACvE;AAGA,MAAM,sBAAsB;AAE5B,MAAa,gBAAgB,SAA0B,oBAAoB,KAAK,IAAI"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"theme-css.js","names":[],"sources":["../src/theme-css.ts"],"sourcesContent":["import { Result } from \"effect\";\n\nimport type { Diagnostic, Span } from \"./diagnostics\";\nimport type { TextToken, TokenOverrides } from \"./theme\";\n\n// `@theme` CSS authoring: literal Tailwind v4 syntax parsed by a closed-grammar\n// pure-TS parser (Worker + browser safe, no CSS library). The grammar is:\n//\n// file = { trivia } block { block } { trivia }\n// block = theme-block | dark-block\n// theme-block = \"@theme\" \"{\" { declaration } \"}\"\n// dark-block = \"@media\" \"(\" \"prefers-color-scheme\" \":\" \"dark\" \")\"\n// \"{\" theme-block { theme-block } \"}\" (colors only)\n// declaration = \"--\" namespace \"-\" name \":\" value \";\"\n// trivia = whitespace | \"/*\" ... \"*/\"\n//\n// Namespace mapping (Tailwind v4 names → ParsedTheme fields): --color-* →\n// colors (hex only, normalized to 6-digit lowercase), --radius-* → radiusPx,\n// --font-* → fontFamilies, --font-weight-* → fontWeights, --text-* → text\n// (\"16px\" or \"16px/24px\"; a bare size defaults line-height to round(1.5×size),\n// the leading-normal ratio), --leading-* → leading, --spacing → spacingPx,\n// --container-* → containerPx, --tracking-* → tracking (em numbers).\n//\n// Closed means closed: unknown namespaces, malformed values, duplicate\n// declarations, non-color namespaces in the dark block, and unbalanced braces\n// are all typed diagnostics with spans — collected exhaustively, never\n// fail-fast, so agents fix a list at a time. Structural scanning is\n// charCode-level like lexer.ts; leaf value validation uses the same small-regex\n// style as registry.ts.\n\n/**\n * `TokenOverrides` plus `tracking`. The compiler's `TokenOverrides` names its\n * letter-spacing slot `trackingEm`; the `@theme` CSS namespace and the persisted\n * theme schema both call it `tracking`, so the parser surfaces it under that\n * name and apps/api maps `tracking → trackingEm` at the compile call sites.\n */\nexport interface ParsedTheme extends TokenOverrides {\n /** tracking-{name} → letter-spacing in em (negative allowed). */\n readonly tracking?: Readonly<Record<string, number>> | undefined;\n}\n\nconst CHAR_TAB = 9;\nconst CHAR_LINE_FEED = 10;\nconst CHAR_CARRIAGE_RETURN = 13;\nconst CHAR_SPACE = 32;\nconst CHAR_STAR = 42;\nconst CHAR_HYPHEN = 45;\nconst CHAR_SLASH = 47;\nconst CHAR_COLON = 58;\nconst CHAR_SEMICOLON = 59;\nconst CHAR_AT = 64;\nconst CHAR_OPEN_PAREN = 40;\nconst CHAR_CLOSE_PAREN = 41;\nconst CHAR_OPEN_BRACE = 123;\nconst CHAR_CLOSE_BRACE = 125;\nconst CHAR_0 = 48;\nconst CHAR_9 = 57;\nconst CHAR_UPPER_A = 65;\nconst CHAR_UPPER_Z = 90;\nconst CHAR_LOWER_A = 97;\nconst CHAR_LOWER_Z = 122;\n\nconst isWhitespace = (code: number): boolean =>\n code === CHAR_SPACE ||\n code === CHAR_TAB ||\n code === CHAR_LINE_FEED ||\n code === CHAR_CARRIAGE_RETURN;\n\nconst isIdentChar = (code: number): boolean =>\n (code >= CHAR_LOWER_A && code <= CHAR_LOWER_Z) ||\n (code >= CHAR_UPPER_A && code <= CHAR_UPPER_Z) ||\n (code >= CHAR_0 && code <= CHAR_9) ||\n code === CHAR_HYPHEN;\n\n// ── Leaf value validators (registry.ts regex style) ──────────────────────────\n\nconst HEX_PATTERN = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;\n// Number form: `12`, `12.5`, or bare leading-dot `.5` — hand-authored themes\n// and editor completions often omit the leading zero, so accept both (the\n// alternation requires at least one digit; bare \"px\"/\"em\" never match).\nconst NUM = String.raw`(?:\\d+(?:\\.\\d+)?|\\.\\d+)`;\nconst PX_PATTERN = new RegExp(`^(${NUM})px$`);\nconst TEXT_PATTERN = new RegExp(`^(${NUM})px(?:\\\\s*/\\\\s*(${NUM})px)?$`);\nconst UNITLESS_PATTERN = new RegExp(`^${NUM}$`);\nconst EM_PATTERN = new RegExp(`^(-?${NUM})em$`);\nconst INTEGER_PATTERN = /^\\d+$/;\n\n/** #rgb/#rrggbb → 6-digit lowercase, or null. */\nconst normalizeHex = (value: string): string | null => {\n const match = HEX_PATTERN.exec(value);\n if (match === null) return null;\n const digits = match[1]!.toLowerCase();\n if (digits.length === 6) return `#${digits}`;\n return `#${digits[0]!}${digits[0]!}${digits[1]!}${digits[1]!}${digits[2]!}${digits[2]!}`;\n};\n\nconst pxNumber = (value: string): number | null => {\n const match = PX_PATTERN.exec(value);\n return match === null ? null : Number.parseFloat(match[1]!);\n};\n\n// ── Parser ────────────────────────────────────────────────────────────────────\n\ntype Scope = \"light\" | \"dark\";\n\ninterface Accumulator {\n readonly colors: Record<string, string>;\n readonly colorsDark: Record<string, string>;\n readonly text: Record<string, TextToken>;\n readonly leading: Record<string, number>;\n readonly radiusPx: Record<string, number>;\n readonly fontWeights: Record<string, number>;\n readonly fontFamilies: Record<string, string>;\n readonly containerPx: Record<string, number>;\n readonly tracking: Record<string, number>;\n spacingPx: number | undefined;\n}\n\nconst NAMESPACE_SUMMARY =\n \"supported namespaces: --color-*, --radius-*, --font-*, --font-weight-*, --text-*, --leading-*, --spacing, --container-*, --tracking-*\";\n\nexport const parseThemeCss = (\n css: string,\n): Result.Result<ParsedTheme, ReadonlyArray<Diagnostic>> => {\n const length = css.length;\n let position = 0;\n const diagnostics: Array<Diagnostic> = [];\n const seen = new Set<string>();\n const accumulator: Accumulator = {\n colors: {},\n colorsDark: {},\n text: {},\n leading: {},\n radiusPx: {},\n fontWeights: {},\n fontFamilies: {},\n containerPx: {},\n tracking: {},\n spacingPx: undefined,\n };\n let themeBlockCount = 0;\n\n const report = (code: string, message: string, span: Span): void => {\n diagnostics.push({ code, message, span });\n };\n\n const skipTrivia = (): void => {\n while (position < length) {\n const code = css.charCodeAt(position);\n if (isWhitespace(code)) {\n position++;\n continue;\n }\n if (code === CHAR_SLASH && css.charCodeAt(position + 1) === CHAR_STAR) {\n const start = position;\n const close = css.indexOf(\"*/\", position + 2);\n if (close === -1) {\n report(\"theme-unterminated-comment\", \"unterminated /* comment — add */\", {\n start,\n end: length,\n });\n position = length;\n return;\n }\n position = close + 2;\n continue;\n }\n return;\n }\n };\n\n /** `@word` at the current position, not followed by more ident chars. */\n const atKeyword = (word: string): boolean =>\n css.startsWith(word, position) && !isIdentChar(css.charCodeAt(position + word.length));\n\n /** Skips a balanced `{ ... }` region (position on or before the `{`). */\n const skipBalancedBlock = (): void => {\n while (position < length && css.charCodeAt(position) !== CHAR_OPEN_BRACE) position++;\n let depth = 0;\n while (position < length) {\n const code = css.charCodeAt(position);\n position++;\n if (code === CHAR_OPEN_BRACE) depth++;\n else if (code === CHAR_CLOSE_BRACE) {\n depth--;\n if (depth === 0) return;\n }\n }\n };\n\n /** Declaration-level recovery: past the next `;`, or up to the block's `}`. */\n const skipToDeclarationBoundary = (): void => {\n while (position < length) {\n const code = css.charCodeAt(position);\n if (code === CHAR_SEMICOLON) {\n position++;\n return;\n }\n if (code === CHAR_CLOSE_BRACE) return;\n position++;\n }\n };\n\n const record = <Value>(\n target: Record<string, Value>,\n scope: Scope,\n field: string,\n name: string,\n value: Value,\n propertySpan: Span,\n property: string,\n ): void => {\n const key = `${scope}:${field}:${name}`;\n if (seen.has(key)) {\n report(\n \"theme-duplicate-declaration\",\n `duplicate declaration --${property} — declare each token once`,\n propertySpan,\n );\n return;\n }\n seen.add(key);\n target[name] = value;\n };\n\n const parseDeclaration = (scope: Scope): void => {\n const propertyStart = position;\n position += 2; // consume --\n while (position < length && isIdentChar(css.charCodeAt(position))) position++;\n const property = css.slice(propertyStart + 2, position);\n const propertySpan: Span = { start: propertyStart, end: position };\n\n skipTrivia();\n if (css.charCodeAt(position) !== CHAR_COLON) {\n report(\"theme-expected-declaration\", `expected : after --${property}`, {\n start: position,\n end: Math.min(position + 1, length),\n });\n skipToDeclarationBoundary();\n return;\n }\n position++;\n skipTrivia();\n\n const valueStart = position;\n while (position < length) {\n const code = css.charCodeAt(position);\n if (code === CHAR_SEMICOLON || code === CHAR_CLOSE_BRACE || code === CHAR_OPEN_BRACE) break;\n position++;\n }\n let valueEnd = position;\n while (valueEnd > valueStart && isWhitespace(css.charCodeAt(valueEnd - 1))) valueEnd--;\n const value = css.slice(valueStart, valueEnd).replace(/\\s+/g, \" \");\n const valueSpan: Span = { start: valueStart, end: Math.max(valueEnd, valueStart + 1) };\n\n if (css.charCodeAt(position) === CHAR_SEMICOLON) {\n position++;\n } else {\n report(\"theme-missing-semicolon\", `missing ; after --${property} declaration`, propertySpan);\n }\n\n routeDeclaration(scope, property, propertySpan, value, valueSpan);\n };\n\n const invalidValue = (property: string, value: string, expected: string, span: Span): void => {\n report(\"theme-invalid-value\", `invalid value \"${value}\" for --${property} — ${expected}`, span);\n };\n\n const routeDeclaration = (\n scope: Scope,\n property: string,\n propertySpan: Span,\n value: string,\n valueSpan: Span,\n ): void => {\n const named = (prefix: string): string | null => {\n if (!property.startsWith(prefix)) return null;\n const name = property.slice(prefix.length);\n if (name.length === 0) {\n report(\n \"theme-invalid-value\",\n `--${property} is missing a token name — write --${prefix}<name>`,\n propertySpan,\n );\n return null;\n }\n if (name.includes(\"--\")) {\n const hint =\n prefix === \"text-\"\n ? \" — use the size/line-height shorthand instead: --text-xl: 20px/28px\"\n : \" — token names use single hyphens\";\n report(\"theme-invalid-value\", `malformed token name in --${property}${hint}`, propertySpan);\n return null;\n }\n return name;\n };\n\n if (value.length === 0) {\n invalidValue(property, value, \"the value is empty\", valueSpan);\n return;\n }\n\n if (property.startsWith(\"color-\")) {\n const name = named(\"color-\");\n if (name === null) return;\n const hex = normalizeHex(value);\n if (hex === null) {\n invalidValue(\n property,\n value,\n \"expected a hex color like #4f46e5 or #fff (named colors, oklch(), and rgb() are outside the closed grammar)\",\n valueSpan,\n );\n return;\n }\n if (scope === \"dark\") {\n record(accumulator.colorsDark, scope, \"colors\", name, hex, propertySpan, property);\n } else {\n record(accumulator.colors, scope, \"colors\", name, hex, propertySpan, property);\n }\n return;\n }\n\n if (scope === \"dark\") {\n report(\n \"theme-dark-non-color\",\n `--${property} is not allowed in the dark theme block — only --color-* tokens have dark counterparts`,\n propertySpan,\n );\n return;\n }\n\n if (property.startsWith(\"radius-\")) {\n const name = named(\"radius-\");\n if (name === null) return;\n const px = pxNumber(value);\n if (px === null) {\n invalidValue(property, value, 'expected a px length like \"12px\"', valueSpan);\n return;\n }\n record(accumulator.radiusPx, scope, \"radiusPx\", name, px, propertySpan, property);\n return;\n }\n\n if (property.startsWith(\"font-weight-\")) {\n const name = named(\"font-weight-\");\n if (name === null) return;\n const weight = INTEGER_PATTERN.test(value) ? Number.parseInt(value, 10) : null;\n if (weight === null || weight < 100 || weight > 900) {\n invalidValue(property, value, \"expected a font weight between 100 and 900\", valueSpan);\n return;\n }\n record(accumulator.fontWeights, scope, \"fontWeights\", name, weight, propertySpan, property);\n return;\n }\n\n if (property.startsWith(\"font-\")) {\n const name = named(\"font-\");\n if (name === null) return;\n if (value.includes(\"var(\") || value.includes(\"calc(\") || value.includes(\"oklch(\")) {\n invalidValue(\n property,\n value,\n \"expected a literal font stack — var()/calc() don't exist in compiled email\",\n valueSpan,\n );\n return;\n }\n // Canonicalize quoted font names to single quotes — the system-wide\n // convention (baseTokens.fontFamilies, the compiler's inline font-family\n // flatten) and safe inside a double-quoted style=\"\" attribute without\n // escaping to &quot;. A CSS formatter may write the source with double\n // quotes; we normalize here just like hex is lowercased and whitespace\n // collapsed, so re-parse and downstream emission stay canonical.\n const stack = value.replaceAll('\"', \"'\");\n record(accumulator.fontFamilies, scope, \"fontFamilies\", name, stack, propertySpan, property);\n return;\n }\n\n if (property.startsWith(\"text-\")) {\n const name = named(\"text-\");\n if (name === null) return;\n const match = TEXT_PATTERN.exec(value);\n if (match === null) {\n invalidValue(\n property,\n value,\n 'expected \"16px\" or \"16px/24px\" (font-size/line-height)',\n valueSpan,\n );\n return;\n }\n const sizePx = Number.parseFloat(match[1]!);\n const lineHeightPx =\n match[2] === undefined ? Math.round(sizePx * 1.5) : Number.parseFloat(match[2]);\n record(\n accumulator.text,\n scope,\n \"text\",\n name,\n { sizePx, lineHeightPx },\n propertySpan,\n property,\n );\n return;\n }\n\n if (property.startsWith(\"leading-\")) {\n const name = named(\"leading-\");\n if (name === null) return;\n if (!UNITLESS_PATTERN.test(value) || Number.parseFloat(value) <= 0) {\n invalidValue(\n property,\n value,\n 'expected a unitless line-height ratio like \"1.5\"',\n valueSpan,\n );\n return;\n }\n record(\n accumulator.leading,\n scope,\n \"leading\",\n name,\n Number.parseFloat(value),\n propertySpan,\n property,\n );\n return;\n }\n\n if (property.startsWith(\"tracking-\")) {\n const name = named(\"tracking-\");\n if (name === null) return;\n const match = EM_PATTERN.exec(value);\n if (match === null) {\n invalidValue(\n property,\n value,\n 'expected an em value like \"0.025em\" or \"-0.05em\"',\n valueSpan,\n );\n return;\n }\n record(\n accumulator.tracking,\n scope,\n \"tracking\",\n name,\n Number.parseFloat(match[1]!),\n propertySpan,\n property,\n );\n return;\n }\n\n if (property.startsWith(\"container-\")) {\n const name = named(\"container-\");\n if (name === null) return;\n const px = pxNumber(value);\n if (px === null) {\n invalidValue(property, value, 'expected a px length like \"600px\"', valueSpan);\n return;\n }\n record(accumulator.containerPx, scope, \"containerPx\", name, px, propertySpan, property);\n return;\n }\n\n if (property === \"spacing\") {\n const px = pxNumber(value);\n if (px === null || px <= 0) {\n invalidValue(property, value, 'expected a positive px length like \"4px\"', valueSpan);\n return;\n }\n const key = \"light:spacingPx\";\n if (seen.has(key)) {\n report(\n \"theme-duplicate-declaration\",\n \"duplicate declaration --spacing — declare each token once\",\n propertySpan,\n );\n return;\n }\n seen.add(key);\n accumulator.spacingPx = px;\n return;\n }\n\n if (property.startsWith(\"spacing-\")) {\n report(\n \"theme-unknown-namespace\",\n `--${property} is not supported — only the bare --spacing multiplier is (spacing steps derive from it)`,\n propertySpan,\n );\n return;\n }\n\n report(\n \"theme-unknown-namespace\",\n `unknown namespace in --${property} — ${NAMESPACE_SUMMARY}`,\n propertySpan,\n );\n };\n\n /** Position after the `@theme` keyword; parses `{ declarations }`. */\n const parseThemeBlock = (scope: Scope): void => {\n skipTrivia();\n if (css.charCodeAt(position) !== CHAR_OPEN_BRACE) {\n report(\"theme-expected-block\", \"expected { after @theme\", {\n start: position,\n end: Math.min(position + 1, length),\n });\n return;\n }\n const openBrace = position;\n position++;\n themeBlockCount++;\n while (true) {\n skipTrivia();\n if (position >= length) {\n report(\"theme-unbalanced-braces\", \"unclosed @theme block — add }\", {\n start: openBrace,\n end: openBrace + 1,\n });\n return;\n }\n const code = css.charCodeAt(position);\n if (code === CHAR_CLOSE_BRACE) {\n position++;\n return;\n }\n if (code === CHAR_HYPHEN && css.charCodeAt(position + 1) === CHAR_HYPHEN) {\n parseDeclaration(scope);\n continue;\n }\n report(\"theme-expected-declaration\", \"expected a --<namespace>-<name> declaration\", {\n start: position,\n end: Math.min(position + 1, length),\n });\n skipToDeclarationBoundary();\n }\n };\n\n /** Position after the `@media` keyword; parses the dark wrapper block. */\n const parseMediaBlock = (): void => {\n const mediaStart = position - \"@media\".length;\n const preludeError = (): void => {\n report(\n \"theme-unsupported-media\",\n \"only @media (prefers-color-scheme: dark) is supported — the dark theme wrapper\",\n { start: mediaStart, end: Math.min(position + 1, length) },\n );\n skipBalancedBlock();\n };\n\n skipTrivia();\n if (css.charCodeAt(position) !== CHAR_OPEN_PAREN) return preludeError();\n position++;\n skipTrivia();\n if (!css.startsWith(\"prefers-color-scheme\", position)) return preludeError();\n position += \"prefers-color-scheme\".length;\n skipTrivia();\n if (css.charCodeAt(position) !== CHAR_COLON) return preludeError();\n position++;\n skipTrivia();\n if (!atKeyword(\"dark\")) return preludeError();\n position += \"dark\".length;\n skipTrivia();\n if (css.charCodeAt(position) !== CHAR_CLOSE_PAREN) return preludeError();\n position++;\n skipTrivia();\n if (css.charCodeAt(position) !== CHAR_OPEN_BRACE) {\n report(\"theme-expected-block\", \"expected { after the dark media query\", {\n start: position,\n end: Math.min(position + 1, length),\n });\n return;\n }\n const openBrace = position;\n position++;\n\n let sawThemeBlock = false;\n while (true) {\n skipTrivia();\n if (position >= length) {\n report(\"theme-unbalanced-braces\", \"unclosed @media block — add }\", {\n start: openBrace,\n end: openBrace + 1,\n });\n return;\n }\n const code = css.charCodeAt(position);\n if (code === CHAR_CLOSE_BRACE) {\n position++;\n break;\n }\n if (code === CHAR_AT && atKeyword(\"@theme\")) {\n position += \"@theme\".length;\n parseThemeBlock(\"dark\");\n sawThemeBlock = true;\n continue;\n }\n report(\"theme-expected-block\", \"expected an @theme block inside the dark media query\", {\n start: position,\n end: Math.min(position + 1, length),\n });\n skipBalancedBlock();\n }\n if (!sawThemeBlock) {\n report(\"theme-expected-block\", \"the dark media query contains no @theme block\", {\n start: openBrace,\n end: openBrace + 1,\n });\n }\n };\n\n while (position < length) {\n skipTrivia();\n if (position >= length) break;\n if (atKeyword(\"@theme\")) {\n position += \"@theme\".length;\n parseThemeBlock(\"light\");\n continue;\n }\n if (atKeyword(\"@media\")) {\n position += \"@media\".length;\n parseMediaBlock();\n continue;\n }\n report(\n \"theme-unexpected-content\",\n \"expected @theme or @media (prefers-color-scheme: dark) at the top level\",\n { start: position, end: Math.min(position + 1, length) },\n );\n // Recover past one balanced block (a stray rule) or to the next @.\n const next = position;\n while (position < length) {\n const code = css.charCodeAt(position);\n if (code === CHAR_AT && position > next) break;\n if (code === CHAR_OPEN_BRACE) {\n skipBalancedBlock();\n break;\n }\n position++;\n }\n }\n\n if (themeBlockCount === 0 && diagnostics.length === 0) {\n report(\n \"theme-missing-block\",\n \"no @theme block found — write @theme { --color-brand: #4f46e5; }\",\n {\n start: 0,\n end: Math.min(1, length),\n },\n );\n }\n\n if (diagnostics.length > 0) return Result.fail(diagnostics);\n\n const nonEmpty = <Value>(\n entries: Readonly<Record<string, Value>>,\n ): Readonly<Record<string, Value>> | undefined =>\n Object.keys(entries).length > 0 ? entries : undefined;\n\n const colors = nonEmpty(accumulator.colors);\n const colorsDark = nonEmpty(accumulator.colorsDark);\n const text = nonEmpty(accumulator.text);\n const leading = nonEmpty(accumulator.leading);\n const radiusPx = nonEmpty(accumulator.radiusPx);\n const fontWeights = nonEmpty(accumulator.fontWeights);\n const fontFamilies = nonEmpty(accumulator.fontFamilies);\n const containerPx = nonEmpty(accumulator.containerPx);\n const tracking = nonEmpty(accumulator.tracking);\n\n return Result.succeed({\n colors,\n colorsDark,\n spacingPx: accumulator.spacingPx,\n text,\n leading,\n radiusPx,\n fontWeights,\n fontFamilies,\n containerPx,\n tracking,\n });\n};\n\n// ── Printer (inverse of parseThemeCss) ────────────────────────────────────────\n// Emits Tailwind-v4 `@theme` CSS from a ParsedTheme. Round-trips through\n// parseThemeCss: `parseThemeCss(printThemeCss(t))` yields back `t` for any\n// ParsedTheme parseThemeCss can produce. Values are printed in the exact forms\n// the leaf validators accept (6-digit lowercase hex, `<n>px`, `<size>px/<lh>px`,\n// unitless leading, `<n>em` tracking) so nothing is re-normalized on re-parse.\n\n/** Trim a JS number to a stable decimal string (no exponent, no trailing zeros). */\nconst numberText = (value: number): string => {\n const text = String(value);\n if (!text.includes(\"e\") && !text.includes(\"E\")) return text;\n // |value| below ~5e-7 or at/above 1e21 stringifies in exponent form, which\n // the value patterns reject — expand to plain decimals and trim the zeros so\n // the printed theme always re-parses (the round-trip guarantee).\n const expanded = value.toLocaleString(\"en-US\", {\n useGrouping: false,\n maximumFractionDigits: 20,\n });\n if (!expanded.includes(\".\")) return expanded;\n return expanded.replace(/0+$/, \"\").replace(/\\.$/, \"\");\n};\n\nconst declaration = (property: string, value: string): string => ` --${property}: ${value};`;\n\nconst colorDeclarations = (colors: Readonly<Record<string, string>> | undefined): string[] =>\n colors === undefined\n ? []\n : Object.entries(colors).map(([name, hex]) => declaration(`color-${name}`, hex));\n\n/**\n * Serialize a ParsedTheme to `@theme` CSS. Light tokens go in the top-level\n * `@theme` block; `colorsDark` (colors-only, matching the parser) goes in the\n * `@media (prefers-color-scheme: dark) { @theme { … } }` wrapper.\n */\nexport const printThemeCss = (theme: ParsedTheme): string => {\n const light: string[] = [\n ...colorDeclarations(theme.colors),\n ...(theme.spacingPx !== undefined\n ? [declaration(\"spacing\", `${numberText(theme.spacingPx)}px`)]\n : []),\n ...(theme.radiusPx === undefined\n ? []\n : Object.entries(theme.radiusPx).map(([name, px]) =>\n declaration(`radius-${name}`, `${numberText(px)}px`),\n )),\n ...(theme.fontFamilies === undefined\n ? []\n : Object.entries(theme.fontFamilies).map(([name, stack]) =>\n declaration(`font-${name}`, stack),\n )),\n ...(theme.fontWeights === undefined\n ? []\n : Object.entries(theme.fontWeights).map(([name, weight]) =>\n declaration(`font-weight-${name}`, numberText(weight)),\n )),\n ...(theme.text === undefined\n ? []\n : Object.entries(theme.text).map(([name, token]) =>\n declaration(\n `text-${name}`,\n `${numberText(token.sizePx)}px/${numberText(token.lineHeightPx)}px`,\n ),\n )),\n ...(theme.leading === undefined\n ? []\n : Object.entries(theme.leading).map(([name, ratio]) =>\n declaration(`leading-${name}`, numberText(ratio)),\n )),\n // ParsedTheme surfaces letter-spacing under `tracking` (the `@theme` CSS and\n // persisted-schema name); the compiler's own `trackingEm` alias is folded in\n // so either input shape prints once, never twice.\n ...(() => {\n const trackingSource = theme.tracking ?? theme.trackingEm;\n return trackingSource === undefined\n ? []\n : Object.entries(trackingSource).map(([name, em]) =>\n declaration(`tracking-${name}`, `${numberText(em)}em`),\n );\n })(),\n ...(theme.containerPx === undefined\n ? []\n : Object.entries(theme.containerPx).map(([name, px]) =>\n declaration(`container-${name}`, `${numberText(px)}px`),\n )),\n ];\n\n const dark = colorDeclarations(theme.colorsDark);\n\n const blocks: string[] = [`@theme {\\n${light.join(\"\\n\")}\\n}`];\n if (dark.length > 0) {\n blocks.push(\n `@media (prefers-color-scheme: dark) {\\n @theme {\\n ${dark.join(\"\\n \")}\\n }\\n}`,\n );\n }\n return `${blocks.join(\"\\n\\n\")}\\n`;\n};\n"],"mappings":"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"}
package/dist/theme.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"theme.js","names":[],"sources":["../src/theme.ts"],"sourcesContent":["// Layered design-token table. Classes resolve against a merged table:\n// base Tailwind tokens ← org theme ← template overrides. The layering is\n// the theming mechanism (`bg-brand` is just an org-theme color named \"brand\");\n// every resolver call takes the layered table.\n\nexport interface TextToken {\n readonly sizePx: number;\n readonly lineHeightPx: number;\n}\n\n/** One hosted @font-face: the served woff2 src + its weight/style, and the\n * css2 unicode-range (absent for a whole-file upload). */\nexport interface FontFace {\n readonly weight: number;\n readonly style: \"normal\" | \"italic\";\n readonly src: string;\n readonly unicodeRange?: string | undefined;\n}\n\n/** The @font-face set for one font token: the family the rules declare (and that\n * the token's inline stack leads with) plus its hosted faces. */\nexport interface FontFaceSet {\n readonly family: string;\n readonly faces: ReadonlyArray<FontFace>;\n}\n\n/**\n * Escape a value interpolated inside a single-quoted CSS string — a\n * `font-family` name or a `url('…')` src. A raw `'` closes the quote early (for\n * a src that also lets a crafted value escape the `url()`), and a raw `\\` is read\n * as a CSS escape sequence. `family` and `src` are only min-length-validated\n * upstream, so escaping both keeps every emitted CSS string grammar-safe. Used\n * both here (the inline `font-{name}` stack) and by the compiler's @font-face\n * block, so an escapable family never lands unescaped in either place.\n *\n * The `@font-face` block lives inside a `<style>` element, and an HTML parser\n * scans raw-text elements for the literal `</style>` end tag BEFORE any CSS\n * parsing. So a crafted `family`/`src` containing `</style>` would close the\n * style element and let the remainder render as HTML in the delivered email\n * (a stored-XSS breakout). Rewriting `</` to `<\\/` neutralises this: in a\n * quoted CSS string `\\/` is identical to `/`, but the HTML tokenizer no longer\n * matches `<\\/style>` as a raw-text end tag.\n */\nexport const escapeCssString = (value: string): string =>\n value.replace(/\\\\/g, \"\\\\\\\\\").replace(/'/g, \"\\\\'\").replace(/<\\//g, \"<\\\\/\");\n\n/** A persisted font token: the family the inline stack leads with, the fallback\n * appended after it, and (for a self-hosted webfont) the hosted faces. The\n * public compiler owns the single flatten transform for this shape. */\nexport interface FontToken {\n readonly family: string;\n readonly fallback: string;\n readonly faces?: ReadonlyArray<FontFace> | undefined;\n}\n\n/**\n * Flatten persisted font tokens into the compiler's two font fields, so the\n * server (apps/api) and the in-browser canvas (apps/web) produce byte-identical\n * output from ONE transform rather than two hand-synced copies:\n * - `fontFamilies` = the inline stack the `font-{name}` utility emits — the\n * family quoted (multi-word-safe) then the fallback (which the picker\n * guarantees ends in a generic, so no client is ever fontless).\n * - `fontFaces` = the @font-face set for tokens that self-host a webfont.\n * Tokens without `faces` (system stacks) contribute a family but no face set.\n */\nexport const flattenFontTokens = (\n tokens: Readonly<Record<string, FontToken>>,\n): {\n fontFamilies: Record<string, string>;\n fontFaces: Record<string, FontFaceSet>;\n} => {\n const fontFamilies: Record<string, string> = {};\n const fontFaces: Record<string, FontFaceSet> = {};\n for (const [name, token] of Object.entries(tokens)) {\n // The family leads a single-quoted CSS value here, so escape it — an\n // unescaped quote (`d'Font`) would produce `'d'Font', …`, invalid CSS the\n // browser drops entirely, falling back to (often) Times New Roman: exactly\n // the footgun webfonts exist to close. The @font-face block escapes the same\n // family again at emit, so both surfaces stay grammar-safe.\n fontFamilies[name] = `'${escapeCssString(token.family)}', ${token.fallback}`;\n if (token.faces != null) {\n fontFaces[name] = { family: token.family, faces: token.faces };\n }\n }\n return { fontFamilies, fontFaces };\n};\n\nexport interface TokenTable {\n /** Flattened color names (\"red-500\", \"white\", \"brand\") → 6-digit lowercase hex. */\n readonly colors: Readonly<Record<string, string>>;\n /**\n * Dark counterparts, colors namespace only (dark radius/fonts are not a\n * thing). Only tokens with a dark pair appear; a color-family utility that\n * resolves a paired token also emits the dark head rule automatically.\n */\n readonly colorsDark: Readonly<Record<string, string>>;\n /** px per spacing unit (`p-4` → 4 × spacingPx). */\n readonly spacingPx: number;\n /** text-{size} → font-size / line-height px pair. */\n readonly text: Readonly<Record<string, TextToken>>;\n /** leading-{name} → unitless line-height ratio. */\n readonly leading: Readonly<Record<string, number>>;\n /** tracking-{name} → letter-spacing em. */\n readonly trackingEm: Readonly<Record<string, number>>;\n /** rounded-{name} → border-radius px. */\n readonly radiusPx: Readonly<Record<string, number>>;\n /** font-{name} → font-weight number. */\n readonly fontWeights: Readonly<Record<string, number>>;\n /** font-{name} → the resolved inline font-family stack (family + fallback,\n * guaranteed to end in a generic). */\n readonly fontFamilies: Readonly<Record<string, string>>;\n /** font-{name} → its @font-face set, for tokens that self-host a webfont.\n * Tokens with no faces (system stacks) are absent. */\n readonly fontFaces: Readonly<Record<string, FontFaceSet>>;\n /** max-w-{name} → px. */\n readonly containerPx: Readonly<Record<string, number>>;\n}\n\nexport interface TokenOverrides {\n readonly colors?: Readonly<Record<string, string>> | undefined;\n readonly colorsDark?: Readonly<Record<string, string>> | undefined;\n readonly spacingPx?: number | undefined;\n readonly text?: Readonly<Record<string, TextToken>> | undefined;\n readonly leading?: Readonly<Record<string, number>> | undefined;\n readonly trackingEm?: Readonly<Record<string, number>> | undefined;\n readonly radiusPx?: Readonly<Record<string, number>> | undefined;\n readonly fontWeights?: Readonly<Record<string, number>> | undefined;\n readonly fontFamilies?: Readonly<Record<string, string>> | undefined;\n readonly fontFaces?: Readonly<Record<string, FontFaceSet>> | undefined;\n readonly containerPx?: Readonly<Record<string, number>> | undefined;\n}\n\nexport interface ThemeLayers {\n readonly base: TokenTable;\n /**\n * Ordered token-override layers, least → most specific (organization →\n * domain → template). Each is merged onto the running table in turn, so a\n * later layer's token wins. The caller (the settings cascade in apps/api)\n * owns assembling this list from the effective design.\n */\n readonly overrides?: ReadonlyArray<TokenOverrides> | undefined;\n}\n\nconst mergeLayer = (table: TokenTable, overrides: TokenOverrides | undefined): TokenTable => {\n if (overrides === undefined) return table;\n return {\n colors: { ...table.colors, ...overrides.colors },\n colorsDark: { ...table.colorsDark, ...overrides.colorsDark },\n spacingPx: overrides.spacingPx ?? table.spacingPx,\n text: { ...table.text, ...overrides.text },\n leading: { ...table.leading, ...overrides.leading },\n trackingEm: { ...table.trackingEm, ...overrides.trackingEm },\n radiusPx: { ...table.radiusPx, ...overrides.radiusPx },\n fontWeights: { ...table.fontWeights, ...overrides.fontWeights },\n fontFamilies: { ...table.fontFamilies, ...overrides.fontFamilies },\n fontFaces: { ...table.fontFaces, ...overrides.fontFaces },\n containerPx: { ...table.containerPx, ...overrides.containerPx },\n };\n};\n\nexport const resolveTokenTable = (layers: ThemeLayers): TokenTable =>\n (layers.overrides ?? []).reduce(mergeLayer, layers.base);\n"],"mappings":";;;;;;;;;;;;;;;;;;AA2CA,MAAa,mBAAmB,UAC9B,MAAM,QAAQ,OAAO,MAAM,CAAC,CAAC,QAAQ,MAAM,KAAK,CAAC,CAAC,QAAQ,QAAQ,MAAM;;;;;;;;;;;AAqB1E,MAAa,qBACX,WAIG;CACH,MAAM,eAAuC,CAAC;CAC9C,MAAM,YAAyC,CAAC;CAChD,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,MAAM,GAAG;EAMlD,aAAa,QAAQ,IAAI,gBAAgB,MAAM,MAAM,EAAE,KAAK,MAAM;EAClE,IAAI,MAAM,SAAS,MACjB,UAAU,QAAQ;GAAE,QAAQ,MAAM;GAAQ,OAAO,MAAM;EAAM;CAEjE;CACA,OAAO;EAAE;EAAc;CAAU;AACnC;AA0DA,MAAM,cAAc,OAAmB,cAAsD;CAC3F,IAAI,cAAc,KAAA,GAAW,OAAO;CACpC,OAAO;EACL,QAAQ;GAAE,GAAG,MAAM;GAAQ,GAAG,UAAU;EAAO;EAC/C,YAAY;GAAE,GAAG,MAAM;GAAY,GAAG,UAAU;EAAW;EAC3D,WAAW,UAAU,aAAa,MAAM;EACxC,MAAM;GAAE,GAAG,MAAM;GAAM,GAAG,UAAU;EAAK;EACzC,SAAS;GAAE,GAAG,MAAM;GAAS,GAAG,UAAU;EAAQ;EAClD,YAAY;GAAE,GAAG,MAAM;GAAY,GAAG,UAAU;EAAW;EAC3D,UAAU;GAAE,GAAG,MAAM;GAAU,GAAG,UAAU;EAAS;EACrD,aAAa;GAAE,GAAG,MAAM;GAAa,GAAG,UAAU;EAAY;EAC9D,cAAc;GAAE,GAAG,MAAM;GAAc,GAAG,UAAU;EAAa;EACjE,WAAW;GAAE,GAAG,MAAM;GAAW,GAAG,UAAU;EAAU;EACxD,aAAa;GAAE,GAAG,MAAM;GAAa,GAAG,UAAU;EAAY;CAChE;AACF;AAEA,MAAa,qBAAqB,YAC/B,OAAO,aAAa,CAAC,EAAA,CAAG,OAAO,YAAY,OAAO,IAAI"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tokens.js","names":[],"sources":["../src/tokens.ts"],"sourcesContent":["// GENERATED FILE — do not edit. Regenerate with `bun run codegen:tokens`.\n// Source: tailwindcss@4.3.2 theme.css. All values are literal hex/px —\n// oklch → sRGB and rem → px conversion happened at codegen time. The brand-*\n// entries are Samva's neutral defaults (codegen script), not Tailwind tokens;\n// org brand kits override them by key.\nimport type { TokenTable } from \"./theme\";\n\nexport const baseTokens: TokenTable = {\n colors: {\n \"red-50\": \"#fef2f2\",\n \"red-100\": \"#ffe2e2\",\n \"red-200\": \"#ffc9c9\",\n \"red-300\": \"#ffa2a2\",\n \"red-400\": \"#ff6467\",\n \"red-500\": \"#fb2c36\",\n \"red-600\": \"#e7000b\",\n \"red-700\": \"#c10007\",\n \"red-800\": \"#9f0712\",\n \"red-900\": \"#82181a\",\n \"red-950\": \"#460809\",\n \"orange-50\": \"#fff7ed\",\n \"orange-100\": \"#ffedd4\",\n \"orange-200\": \"#ffd6a7\",\n \"orange-300\": \"#ffb86a\",\n \"orange-400\": \"#ff8904\",\n \"orange-500\": \"#ff6900\",\n \"orange-600\": \"#f54900\",\n \"orange-700\": \"#ca3500\",\n \"orange-800\": \"#9f2d00\",\n \"orange-900\": \"#7e2a0c\",\n \"orange-950\": \"#441306\",\n \"amber-50\": \"#fffbeb\",\n \"amber-100\": \"#fef3c6\",\n \"amber-200\": \"#fee685\",\n \"amber-300\": \"#ffd230\",\n \"amber-400\": \"#ffb900\",\n \"amber-500\": \"#fe9a00\",\n \"amber-600\": \"#e17100\",\n \"amber-700\": \"#bb4d00\",\n \"amber-800\": \"#973c00\",\n \"amber-900\": \"#7b3306\",\n \"amber-950\": \"#461901\",\n \"yellow-50\": \"#fefce8\",\n \"yellow-100\": \"#fef9c2\",\n \"yellow-200\": \"#fff085\",\n \"yellow-300\": \"#ffdf20\",\n \"yellow-400\": \"#fdc700\",\n \"yellow-500\": \"#f0b100\",\n \"yellow-600\": \"#d08700\",\n \"yellow-700\": \"#a65f00\",\n \"yellow-800\": \"#894b00\",\n \"yellow-900\": \"#733e0a\",\n \"yellow-950\": \"#432004\",\n \"lime-50\": \"#f7fee7\",\n \"lime-100\": \"#ecfcca\",\n \"lime-200\": \"#d8f999\",\n \"lime-300\": \"#bbf451\",\n \"lime-400\": \"#9ae600\",\n \"lime-500\": \"#7ccf00\",\n \"lime-600\": \"#5ea500\",\n \"lime-700\": \"#497d00\",\n \"lime-800\": \"#3c6300\",\n \"lime-900\": \"#35530e\",\n \"lime-950\": \"#192e03\",\n \"green-50\": \"#f0fdf4\",\n \"green-100\": \"#dcfce7\",\n \"green-200\": \"#b9f8cf\",\n \"green-300\": \"#7bf1a8\",\n \"green-400\": \"#05df72\",\n \"green-500\": \"#00c950\",\n \"green-600\": \"#00a63e\",\n \"green-700\": \"#008236\",\n \"green-800\": \"#016630\",\n \"green-900\": \"#0d542b\",\n \"green-950\": \"#032e15\",\n \"emerald-50\": \"#ecfdf5\",\n \"emerald-100\": \"#d0fae5\",\n \"emerald-200\": \"#a4f4cf\",\n \"emerald-300\": \"#5ee9b5\",\n \"emerald-400\": \"#00d492\",\n \"emerald-500\": \"#00bc7d\",\n \"emerald-600\": \"#009966\",\n \"emerald-700\": \"#007a55\",\n \"emerald-800\": \"#006045\",\n \"emerald-900\": \"#004f3b\",\n \"emerald-950\": \"#002c22\",\n \"teal-50\": \"#f0fdfa\",\n \"teal-100\": \"#cbfbf1\",\n \"teal-200\": \"#96f7e4\",\n \"teal-300\": \"#46ecd5\",\n \"teal-400\": \"#00d5be\",\n \"teal-500\": \"#00bba7\",\n \"teal-600\": \"#009689\",\n \"teal-700\": \"#00786f\",\n \"teal-800\": \"#005f5a\",\n \"teal-900\": \"#0b4f4a\",\n \"teal-950\": \"#022f2e\",\n \"cyan-50\": \"#ecfeff\",\n \"cyan-100\": \"#cefafe\",\n \"cyan-200\": \"#a2f4fd\",\n \"cyan-300\": \"#53eafd\",\n \"cyan-400\": \"#00d3f2\",\n \"cyan-500\": \"#00b8db\",\n \"cyan-600\": \"#0092b8\",\n \"cyan-700\": \"#007595\",\n \"cyan-800\": \"#005f78\",\n \"cyan-900\": \"#104e64\",\n \"cyan-950\": \"#053345\",\n \"sky-50\": \"#f0f9ff\",\n \"sky-100\": \"#dff2fe\",\n \"sky-200\": \"#b8e6fe\",\n \"sky-300\": \"#74d4ff\",\n \"sky-400\": \"#00bcff\",\n \"sky-500\": \"#00a6f4\",\n \"sky-600\": \"#0084d1\",\n \"sky-700\": \"#0069a8\",\n \"sky-800\": \"#00598a\",\n \"sky-900\": \"#024a70\",\n \"sky-950\": \"#052f4a\",\n \"blue-50\": \"#eff6ff\",\n \"blue-100\": \"#dbeafe\",\n \"blue-200\": \"#bedbff\",\n \"blue-300\": \"#8ec5ff\",\n \"blue-400\": \"#51a2ff\",\n \"blue-500\": \"#2b7fff\",\n \"blue-600\": \"#155dfc\",\n \"blue-700\": \"#1447e6\",\n \"blue-800\": \"#193cb8\",\n \"blue-900\": \"#1c398e\",\n \"blue-950\": \"#162456\",\n \"indigo-50\": \"#eef2ff\",\n \"indigo-100\": \"#e0e7ff\",\n \"indigo-200\": \"#c6d2ff\",\n \"indigo-300\": \"#a3b3ff\",\n \"indigo-400\": \"#7c86ff\",\n \"indigo-500\": \"#615fff\",\n \"indigo-600\": \"#4f39f6\",\n \"indigo-700\": \"#432dd7\",\n \"indigo-800\": \"#372aac\",\n \"indigo-900\": \"#312c85\",\n \"indigo-950\": \"#1e1a4d\",\n \"violet-50\": \"#f5f3ff\",\n \"violet-100\": \"#ede9fe\",\n \"violet-200\": \"#ddd6ff\",\n \"violet-300\": \"#c4b4ff\",\n \"violet-400\": \"#a684ff\",\n \"violet-500\": \"#8e51ff\",\n \"violet-600\": \"#7f22fe\",\n \"violet-700\": \"#7008e7\",\n \"violet-800\": \"#5d0ec0\",\n \"violet-900\": \"#4d179a\",\n \"violet-950\": \"#2f0d68\",\n \"purple-50\": \"#faf5ff\",\n \"purple-100\": \"#f3e8ff\",\n \"purple-200\": \"#e9d4ff\",\n \"purple-300\": \"#dab2ff\",\n \"purple-400\": \"#c27aff\",\n \"purple-500\": \"#ad46ff\",\n \"purple-600\": \"#9810fa\",\n \"purple-700\": \"#8200db\",\n \"purple-800\": \"#6e11b0\",\n \"purple-900\": \"#59168b\",\n \"purple-950\": \"#3c0366\",\n \"fuchsia-50\": \"#fdf4ff\",\n \"fuchsia-100\": \"#fae8ff\",\n \"fuchsia-200\": \"#f6cfff\",\n \"fuchsia-300\": \"#f4a8ff\",\n \"fuchsia-400\": \"#ed6aff\",\n \"fuchsia-500\": \"#e12afb\",\n \"fuchsia-600\": \"#c800de\",\n \"fuchsia-700\": \"#a800b7\",\n \"fuchsia-800\": \"#8a0194\",\n \"fuchsia-900\": \"#721378\",\n \"fuchsia-950\": \"#4b004f\",\n \"pink-50\": \"#fdf2f8\",\n \"pink-100\": \"#fce7f3\",\n \"pink-200\": \"#fccee8\",\n \"pink-300\": \"#fda5d5\",\n \"pink-400\": \"#fb64b6\",\n \"pink-500\": \"#f6339a\",\n \"pink-600\": \"#e60076\",\n \"pink-700\": \"#c6005c\",\n \"pink-800\": \"#a3004c\",\n \"pink-900\": \"#861043\",\n \"pink-950\": \"#510424\",\n \"rose-50\": \"#fff1f2\",\n \"rose-100\": \"#ffe4e6\",\n \"rose-200\": \"#ffccd3\",\n \"rose-300\": \"#ffa1ad\",\n \"rose-400\": \"#ff637e\",\n \"rose-500\": \"#ff2056\",\n \"rose-600\": \"#ec003f\",\n \"rose-700\": \"#c70036\",\n \"rose-800\": \"#a50036\",\n \"rose-900\": \"#8b0836\",\n \"rose-950\": \"#4d0218\",\n \"slate-50\": \"#f8fafc\",\n \"slate-100\": \"#f1f5f9\",\n \"slate-200\": \"#e2e8f0\",\n \"slate-300\": \"#cad5e2\",\n \"slate-400\": \"#90a1b9\",\n \"slate-500\": \"#62748e\",\n \"slate-600\": \"#45556c\",\n \"slate-700\": \"#314158\",\n \"slate-800\": \"#1d293d\",\n \"slate-900\": \"#0f172b\",\n \"slate-950\": \"#020618\",\n \"gray-50\": \"#f9fafb\",\n \"gray-100\": \"#f3f4f6\",\n \"gray-200\": \"#e5e7eb\",\n \"gray-300\": \"#d1d5dc\",\n \"gray-400\": \"#99a1af\",\n \"gray-500\": \"#6a7282\",\n \"gray-600\": \"#4a5565\",\n \"gray-700\": \"#364153\",\n \"gray-800\": \"#1e2939\",\n \"gray-900\": \"#101828\",\n \"gray-950\": \"#030712\",\n \"zinc-50\": \"#fafafa\",\n \"zinc-100\": \"#f4f4f5\",\n \"zinc-200\": \"#e4e4e7\",\n \"zinc-300\": \"#d4d4d8\",\n \"zinc-400\": \"#9f9fa9\",\n \"zinc-500\": \"#71717b\",\n \"zinc-600\": \"#52525c\",\n \"zinc-700\": \"#3f3f46\",\n \"zinc-800\": \"#27272a\",\n \"zinc-900\": \"#18181b\",\n \"zinc-950\": \"#09090b\",\n \"neutral-50\": \"#fafafa\",\n \"neutral-100\": \"#f5f5f5\",\n \"neutral-200\": \"#e5e5e5\",\n \"neutral-300\": \"#d4d4d4\",\n \"neutral-400\": \"#a1a1a1\",\n \"neutral-500\": \"#737373\",\n \"neutral-600\": \"#525252\",\n \"neutral-700\": \"#404040\",\n \"neutral-800\": \"#262626\",\n \"neutral-900\": \"#171717\",\n \"neutral-950\": \"#0a0a0a\",\n \"stone-50\": \"#fafaf9\",\n \"stone-100\": \"#f5f5f4\",\n \"stone-200\": \"#e7e5e4\",\n \"stone-300\": \"#d6d3d1\",\n \"stone-400\": \"#a6a09b\",\n \"stone-500\": \"#79716b\",\n \"stone-600\": \"#57534d\",\n \"stone-700\": \"#44403b\",\n \"stone-800\": \"#292524\",\n \"stone-900\": \"#1c1917\",\n \"stone-950\": \"#0c0a09\",\n \"mauve-50\": \"#fafafa\",\n \"mauve-100\": \"#f3f1f3\",\n \"mauve-200\": \"#e7e4e7\",\n \"mauve-300\": \"#d7d0d7\",\n \"mauve-400\": \"#a89ea9\",\n \"mauve-500\": \"#79697b\",\n \"mauve-600\": \"#594c5b\",\n \"mauve-700\": \"#463947\",\n \"mauve-800\": \"#2a212c\",\n \"mauve-900\": \"#1d161e\",\n \"mauve-950\": \"#0c090c\",\n \"olive-50\": \"#fbfbf9\",\n \"olive-100\": \"#f4f4f0\",\n \"olive-200\": \"#e8e8e3\",\n \"olive-300\": \"#d8d8d0\",\n \"olive-400\": \"#abab9c\",\n \"olive-500\": \"#7c7c67\",\n \"olive-600\": \"#5b5b4b\",\n \"olive-700\": \"#474739\",\n \"olive-800\": \"#2b2b22\",\n \"olive-900\": \"#1d1d16\",\n \"olive-950\": \"#0c0c09\",\n \"mist-50\": \"#f9fbfb\",\n \"mist-100\": \"#f1f3f3\",\n \"mist-200\": \"#e3e7e8\",\n \"mist-300\": \"#d0d6d8\",\n \"mist-400\": \"#9ca8ab\",\n \"mist-500\": \"#67787c\",\n \"mist-600\": \"#4b585b\",\n \"mist-700\": \"#394447\",\n \"mist-800\": \"#22292b\",\n \"mist-900\": \"#161b1d\",\n \"mist-950\": \"#090b0c\",\n \"taupe-50\": \"#fbfaf9\",\n \"taupe-100\": \"#f3f1f1\",\n \"taupe-200\": \"#e8e4e3\",\n \"taupe-300\": \"#d8d2d0\",\n \"taupe-400\": \"#aba09c\",\n \"taupe-500\": \"#7c6d67\",\n \"taupe-600\": \"#5b4f4b\",\n \"taupe-700\": \"#473c39\",\n \"taupe-800\": \"#2b2422\",\n \"taupe-900\": \"#1d1816\",\n \"taupe-950\": \"#0c0a09\",\n black: \"#000000\",\n white: \"#ffffff\",\n brand: \"#171717\",\n \"brand-foreground\": \"#fafafa\",\n },\n colorsDark: {\n brand: \"#fafafa\",\n \"brand-foreground\": \"#171717\",\n },\n spacingPx: 4,\n text: {\n xs: { sizePx: 12, lineHeightPx: 16 },\n sm: { sizePx: 14, lineHeightPx: 20 },\n base: { sizePx: 16, lineHeightPx: 24 },\n lg: { sizePx: 18, lineHeightPx: 28 },\n xl: { sizePx: 20, lineHeightPx: 28 },\n \"2xl\": { sizePx: 24, lineHeightPx: 32 },\n \"3xl\": { sizePx: 30, lineHeightPx: 36 },\n \"4xl\": { sizePx: 36, lineHeightPx: 40 },\n \"5xl\": { sizePx: 48, lineHeightPx: 48 },\n \"6xl\": { sizePx: 60, lineHeightPx: 60 },\n \"7xl\": { sizePx: 72, lineHeightPx: 72 },\n \"8xl\": { sizePx: 96, lineHeightPx: 96 },\n \"9xl\": { sizePx: 128, lineHeightPx: 128 },\n },\n leading: {\n tight: 1.25,\n snug: 1.375,\n normal: 1.5,\n relaxed: 1.625,\n loose: 2,\n },\n trackingEm: {\n tighter: -0.05,\n tight: -0.025,\n normal: 0,\n wide: 0.025,\n wider: 0.05,\n widest: 0.1,\n },\n radiusPx: {\n brand: 8,\n xs: 2,\n sm: 4,\n md: 6,\n lg: 8,\n xl: 12,\n \"2xl\": 16,\n \"3xl\": 24,\n \"4xl\": 32,\n },\n fontWeights: {\n thin: 100,\n extralight: 200,\n light: 300,\n normal: 400,\n medium: 500,\n semibold: 600,\n bold: 700,\n extrabold: 800,\n black: 900,\n },\n fontFamilies: {\n // The default body token — a Times-free system stack. Presets and the\n // <Email>/<Text> defaults author against `font-body`, so an un-themed org\n // never renders Times New Roman. An org opts a hosted webfont in via the\n // picker (which replaces this token's value with a `family, fallback` stack).\n body: \"ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji'\",\n sans: \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', 'Noto Sans', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'\",\n serif: \"ui-serif, Georgia, Cambria, 'Times New Roman', Times, serif\",\n mono: \"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace\",\n },\n // No base webfont faces — a self-hosted face is opted in per token via the\n // org/template theme; the compiler emits @font-face only for tokens present here.\n fontFaces: {},\n containerPx: {\n \"3xs\": 256,\n \"2xs\": 288,\n xs: 320,\n sm: 384,\n md: 448,\n lg: 512,\n xl: 576,\n \"2xl\": 672,\n \"3xl\": 768,\n \"4xl\": 896,\n \"5xl\": 1024,\n \"6xl\": 1152,\n \"7xl\": 1280,\n },\n};\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"variables.js","names":[],"sources":["../src/variables.ts"],"sourcesContent":["import { Predicate } from \"effect\";\n\nimport type { ComparisonOp, PredicateAst, PredicateLiteral } from \"./predicate\";\nimport { printPredicate } from \"./predicate\";\n\n// Recording proxy + typed predicate builders for the TSX authoring layer\n// (design brief §7b/§7c). A template function is invoked with `Variables<T>`;\n// every property access returns a VariableRef that:\n//\n// • prints as a `{{name}}` interpolation when used as a child (see\n// jsx-runtime), and\n// • carries typed predicate builders (eq/ne for all fields, gt/gte/lt/lte for\n// number fields, exists/notExists) that build PredicateAst wrapped in a\n// PredicateRef for `<If test={...}>`.\n//\n// Coercion is a build-time error: variables are SEND-TIME holes, so comparing/\n// concatenating/doing arithmetic on a VariableRef with plain operators cannot\n// mean anything — branch on build-time data (plain JS) or use the predicate\n// builders instead. `Symbol.toPrimitive`/`valueOf`/`toString` therefore throw.\n//\n// The declared `T` doubles as the template's variable schema: renderTemplate\n// reads which keys were accessed and their declared kinds.\n\nconst PLACEHOLDER_SPAN = { start: 0, end: 0 } as const;\n\n/** Brand carried by every VariableRef, keyed off a private symbol. */\nexport const VARIABLE_REF = Symbol.for(\"@samva/markup/VariableRef\");\n\n/** Brand carried by every PredicateRef. */\nexport const PREDICATE_REF = Symbol.for(\"@samva/markup/PredicateRef\");\n\nexport interface VariableRefBrand {\n readonly path: string;\n /** The recorder shared across every ref/proxy from one template invocation. */\n readonly recorder: VariableRecorder;\n}\n\nconst COERCION_MESSAGE = \"variables are send-time values; branch on build-time data instead\";\n\n// ── Predicate builders ──────────────────────────────────────────────────────────\n\n/** Builders present on every VariableRef regardless of field type. */\ninterface CommonPredicateBuilders {\n eq(value: string | number): PredicateRef;\n ne(value: string | number): PredicateRef;\n exists(): PredicateRef;\n notExists(): PredicateRef;\n}\n\n/** Ordering builders — only surfaced (by the `Variables<T>` mapping) on number fields. */\ninterface NumericPredicateBuilders {\n gt(value: number): PredicateRef;\n gte(value: number): PredicateRef;\n lt(value: number): PredicateRef;\n lte(value: number): PredicateRef;\n}\n\n/** A recorded reference to a send-time variable. See module docs. */\nexport type VariableRef<T> = {\n readonly [VARIABLE_REF]: VariableRefBrand;\n} & CommonPredicateBuilders &\n (T extends number ? NumericPredicateBuilders : Record<never, never>);\n\n/** A recorded predicate expression for `<If test={...}>`; chains with and/or/not. */\nexport interface PredicateRef {\n readonly [PREDICATE_REF]: { readonly ast: PredicateAst };\n and(other: PredicateRef): PredicateRef;\n or(other: PredicateRef): PredicateRef;\n not(): PredicateRef;\n}\n\nexport const isVariableRef = (value: unknown): value is VariableRef<unknown> =>\n Predicate.hasProperty(value, VARIABLE_REF);\n\nexport const isPredicateRef = (value: unknown): value is PredicateRef =>\n Predicate.hasProperty(value, PREDICATE_REF);\n\nconst literalFor = (value: string | number): PredicateLiteral =>\n typeof value === \"number\" ? { kind: \"number\", value } : { kind: \"string\", value };\n\nconst makePredicateRef = (ast: PredicateAst): PredicateRef => ({\n [PREDICATE_REF]: { ast },\n and: (other) =>\n makePredicateRef({\n kind: \"and\",\n left: ast,\n right: predicateAst(other),\n span: PLACEHOLDER_SPAN,\n }),\n or: (other) =>\n makePredicateRef({ kind: \"or\", left: ast, right: predicateAst(other), span: PLACEHOLDER_SPAN }),\n not: () => makePredicateRef({ kind: \"not\", operand: ast, span: PLACEHOLDER_SPAN }),\n});\n\nconst predicateAst = (ref: PredicateRef): PredicateAst => ref[PREDICATE_REF].ast;\n\n/** The predicate source string (`test=\"...\"`) for a PredicateRef. */\nexport const printPredicateRef = (ref: PredicateRef): string => printPredicate(predicateAst(ref));\n\n// ── The recorder + proxy factory ────────────────────────────────────────────────\n\nexport type VariableKind = \"string\" | \"number\" | \"unknown\";\n\nexport interface CollectedVariable {\n readonly name: string;\n readonly kind: VariableKind;\n}\n\n/**\n * Shared across every ref produced from one template invocation. Records which\n * variables were touched and the strongest kind evidence seen for each (a\n * numeric comparison implies `number`; a string comparison implies `string`;\n * bare interpolation/exists leaves it `unknown`).\n */\nexport class VariableRecorder {\n private readonly kinds = new Map<string, VariableKind>();\n\n observe(name: string, kind: VariableKind): void {\n const existing = this.kinds.get(name);\n if (existing === undefined || existing === \"unknown\") {\n this.kinds.set(name, kind);\n }\n }\n\n collected(): ReadonlyArray<CollectedVariable> {\n return [...this.kinds].map(([name, kind]) => ({ name, kind }));\n }\n}\n\nconst makeComparison = (\n brand: VariableRefBrand,\n op: ComparisonOp,\n value: string | number,\n): PredicateRef => {\n brand.recorder.observe(brand.path, typeof value === \"number\" ? \"number\" : \"string\");\n return makePredicateRef({\n kind: \"comparison\",\n name: brand.path,\n op,\n literal: literalFor(value),\n span: PLACEHOLDER_SPAN,\n });\n};\n\nconst makeVariableRef = (path: string, recorder: VariableRecorder): VariableRef<unknown> => {\n const brand: VariableRefBrand = { path, recorder };\n const builders: CommonPredicateBuilders & NumericPredicateBuilders = {\n eq: (value) => makeComparison(brand, \"==\", value),\n ne: (value) => makeComparison(brand, \"!=\", value),\n gt: (value) => makeComparison(brand, \">\", value),\n gte: (value) => makeComparison(brand, \">=\", value),\n lt: (value) => makeComparison(brand, \"<\", value),\n lte: (value) => makeComparison(brand, \"<=\", value),\n exists: () => {\n recorder.observe(path, \"unknown\");\n return makePredicateRef({ kind: \"name\", name: path, span: PLACEHOLDER_SPAN });\n },\n notExists: () => {\n recorder.observe(path, \"unknown\");\n return makePredicateRef({\n kind: \"not\",\n operand: { kind: \"name\", name: path, span: PLACEHOLDER_SPAN },\n span: PLACEHOLDER_SPAN,\n });\n },\n };\n\n const target = { [VARIABLE_REF]: brand, ...builders };\n return new Proxy(target, {\n get(base, key, receiver) {\n // Coercion attempts (comparison/concat/arithmetic/template literals) route\n // through these — checked BEFORE own/inherited props so we shadow\n // Object.prototype's valueOf/toString. Variables have no build-time value,\n // so reject loudly with the exact guard message.\n if (key === Symbol.toPrimitive || key === \"valueOf\" || key === \"toString\") {\n return () => {\n // oxlint-disable-next-line samva/no-try-catch-or-throw, samva/no-error-constructor -- User-build authoring boundary: coercing a send-time variable at build time is a programmer error that must fail the user's TSX build loudly, not become a typed Effect failure.\n throw new TypeError(COERCION_MESSAGE);\n };\n }\n if (key === VARIABLE_REF || key in base) return Reflect.get(base, key, receiver);\n if (key === Symbol.toStringTag) return \"VariableRef\";\n // A nested access (`v.user.name`) extends the path — interpolation names\n // may be dotted; predicate builders on a dotted path fail print → parse.\n if (typeof key === \"string\") {\n return makeVariableRef(`${path}.${key}`, recorder);\n }\n return Reflect.get(base, key, receiver);\n },\n }) as unknown as VariableRef<unknown>;\n};\n\n/**\n * Maps a declared variable type to the recording-proxy shape a template\n * receives: `export default function Welcome(v: Variables<{ firstName: string }>)`.\n * Number fields additionally expose ordering builders.\n */\nexport type Variables<T extends Record<string, unknown>> = {\n readonly [K in keyof T]: VariableRef<T[K]>;\n};\n\n/**\n * Builds the recording proxy for a template invocation. Every top-level\n * property access records the variable (kind `unknown` until a typed comparison\n * refines it) and returns a VariableRef. Returns the proxy plus the shared\n * recorder so renderTemplate can read the collected schema.\n */\nexport const createVariablesProxy = <T extends Record<string, unknown>>(): {\n readonly proxy: Variables<T>;\n readonly recorder: VariableRecorder;\n} => {\n const recorder = new VariableRecorder();\n const proxy = new Proxy(\n {},\n {\n get(_base, key) {\n if (typeof key !== \"string\") return undefined;\n recorder.observe(key, \"unknown\");\n return makeVariableRef(key, recorder);\n },\n },\n ) as Variables<T>;\n return { proxy, recorder };\n};\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"version-BITr58kk.js","names":[],"sources":["../src/channel-text.ts","../src/version.ts"],"sourcesContent":["import type { ElementNode, SmlNode } from \"./ast\";\nimport { stringAttribute } from \"./ast\";\n\n// Shared text-slot lowering for the structured channels (sms body, whatsapp\n// component text). Two views of the same verbatim flow:\n//\n// - `flattenChannelText` is the COMPILED form: text verbatim, interpolations\n// canonical, <If>/<Else> lowered to the same `<!--sml:if:id test-->` comment\n// markers the email compiler emits — so resolveArtifactConditionals selects\n// branches identically on every channel's compiled content at send time.\n// - `channelTextVariants` enumerates every branch combination MARKER-FREE, for\n// analysis that needs concrete rendered lengths (sms segment math, whatsapp\n// Meta limits). It mirrors the send-time semantics exactly, including the\n// `.trim()` resolveArtifactConditionals applies to a selected branch.\n\n/** How an interpolation counts when measuring a variant's rendered text. */\nexport type InterpolationWidth = \"literal\" | { readonly worstCaseChars: number };\n\nconst interpolationText = (\n node: Extract<SmlNode, { kind: \"interpolation\" }>,\n width: InterpolationWidth,\n): string => {\n if (width === \"literal\") {\n return node.syntax === \"curly\" ? `{{${node.name}}}` : `\\${${node.name}}`;\n }\n // A GSM-7-safe filler: variables are send-time holes, so they count at the\n // documented worst-case width and never flip the encoding to UCS-2.\n return \"x\".repeat(width.worstCaseChars);\n};\n\nconst ifBranches = (\n node: ElementNode,\n): { readonly then: ReadonlyArray<SmlNode>; readonly otherwise: ReadonlyArray<SmlNode> } => {\n const elseNode = node.children.find(\n (child): child is ElementNode => child.kind === \"element\" && child.tag === \"Else\",\n );\n return {\n then: node.children.filter((child) => !(child.kind === \"element\" && child.tag === \"Else\")),\n otherwise: elseNode?.children ?? [],\n };\n};\n\n/** Verbatim flow → compiled text with `sml:` conditional markers embedded. */\nexport const flattenChannelText = (nodes: ReadonlyArray<SmlNode>): string => {\n let result = \"\";\n for (const node of nodes) {\n if (node.kind === \"text\") {\n result += node.value;\n continue;\n }\n if (node.kind === \"interpolation\") {\n result += interpolationText(node, \"literal\");\n continue;\n }\n // Grammar guarantees the only elements in a verbatim flow are <If> (with\n // an optional trailing <Else> child).\n const test = stringAttribute(node, \"test\") ?? \"\";\n const { then, otherwise } = ifBranches(node);\n result += `<!--sml:if:${node.id} ${test}-->${flattenChannelText(then)}`;\n if (otherwise.length > 0)\n result += `<!--sml:else:${node.id}-->${flattenChannelText(otherwise)}`;\n result += `<!--sml:endif:${node.id}-->`;\n }\n return result;\n};\n\n/** Safety valve: beyond this many branch combinations, stop forking. */\nconst MAX_VARIANTS = 256;\n\n/**\n * Every possible send-time rendering of a verbatim flow (each `<If>` resolved\n * both ways), marker-free. Selected branches are trimmed exactly like\n * resolveArtifactConditionals trims them, so measured lengths match what a\n * recipient can actually receive. Capped at MAX_VARIANTS combinations —\n * beyond the cap later `<If>`s stop forking (their true branch is kept),\n * which keeps worst-case analysis approximate instead of exponential.\n */\nexport const channelTextVariants = (\n nodes: ReadonlyArray<SmlNode>,\n width: InterpolationWidth,\n): ReadonlyArray<string> => {\n let variants: Array<string> = [\"\"];\n const append = (suffixes: ReadonlyArray<string>): void => {\n if (suffixes.length === 1) {\n variants = variants.map((variant) => variant + suffixes[0]);\n return;\n }\n const next: Array<string> = [];\n for (const variant of variants) {\n for (const suffix of suffixes) {\n next.push(variant + suffix);\n if (next.length >= MAX_VARIANTS) break;\n }\n if (next.length >= MAX_VARIANTS) break;\n }\n variants = next;\n };\n\n for (const node of nodes) {\n if (node.kind === \"text\") {\n append([node.value]);\n continue;\n }\n if (node.kind === \"interpolation\") {\n append([interpolationText(node, width)]);\n continue;\n }\n const { then, otherwise } = ifBranches(node);\n const thenVariants = channelTextVariants(then, width).map((variant) => variant.trim());\n const elseVariants = channelTextVariants(otherwise, width).map((variant) => variant.trim());\n const branchVariants = [...new Set([...thenVariants, ...elseVariants])];\n append(\n variants.length * branchVariants.length > MAX_VARIANTS\n ? [thenVariants[0] ?? \"\"]\n : branchVariants,\n );\n }\n return [...new Set(variants)];\n};\n\n/**\n * The primary send-time rendering: every `<If>` takes its true branch (the\n * branch that carries the main copy by convention). Used for the single\n * `estimatedSegments` number and the opt-out check.\n */\nexport const primaryChannelText = (\n nodes: ReadonlyArray<SmlNode>,\n width: InterpolationWidth,\n): string => {\n let result = \"\";\n for (const node of nodes) {\n if (node.kind === \"text\") result += node.value;\n else if (node.kind === \"interpolation\") result += interpolationText(node, width);\n else result += primaryChannelText(ifBranches(node).then, width).trim();\n }\n return result;\n};\n","// Version stamp shared by every compile backend. Bump on any change that\n// alters compiled bytes for identical input — content addressing depends on it.\nexport const COMPILER_VERSION = \"0.1.0\";\n"],"mappings":";;AAkBA,MAAM,qBACJ,MACA,UACW;CACX,IAAI,UAAU,WACZ,OAAO,KAAK,WAAW,UAAU,KAAK,KAAK,KAAK,MAAM,MAAM,KAAK,KAAK;CAIxE,OAAO,IAAI,OAAO,MAAM,cAAc;AACxC;AAEA,MAAM,cACJ,SAC0F;CAC1F,MAAM,WAAW,KAAK,SAAS,MAC5B,UAAgC,MAAM,SAAS,aAAa,MAAM,QAAQ,MAC7E;CACA,OAAO;EACL,MAAM,KAAK,SAAS,QAAQ,UAAU,EAAE,MAAM,SAAS,aAAa,MAAM,QAAQ,OAAO;EACzF,WAAW,UAAU,YAAY,CAAC;CACpC;AACF;;AAGA,MAAa,sBAAsB,UAA0C;CAC3E,IAAI,SAAS;CACb,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,KAAK,SAAS,QAAQ;GACxB,UAAU,KAAK;GACf;EACF;EACA,IAAI,KAAK,SAAS,iBAAiB;GACjC,UAAU,kBAAkB,MAAM,SAAS;GAC3C;EACF;EAGA,MAAM,OAAO,gBAAgB,MAAM,MAAM,KAAK;EAC9C,MAAM,EAAE,MAAM,cAAc,WAAW,IAAI;EAC3C,UAAU,cAAc,KAAK,GAAG,GAAG,KAAK,KAAK,mBAAmB,IAAI;EACpE,IAAI,UAAU,SAAS,GACrB,UAAU,gBAAgB,KAAK,GAAG,KAAK,mBAAmB,SAAS;EACrE,UAAU,iBAAiB,KAAK,GAAG;CACrC;CACA,OAAO;AACT;;AAGA,MAAM,eAAe;;;;;;;;;AAUrB,MAAa,uBACX,OACA,UAC0B;CAC1B,IAAI,WAA0B,CAAC,EAAE;CACjC,MAAM,UAAU,aAA0C;EACxD,IAAI,SAAS,WAAW,GAAG;GACzB,WAAW,SAAS,KAAK,YAAY,UAAU,SAAS,EAAE;GAC1D;EACF;EACA,MAAM,OAAsB,CAAC;EAC7B,KAAK,MAAM,WAAW,UAAU;GAC9B,KAAK,MAAM,UAAU,UAAU;IAC7B,KAAK,KAAK,UAAU,MAAM;IAC1B,IAAI,KAAK,UAAU,cAAc;GACnC;GACA,IAAI,KAAK,UAAU,cAAc;EACnC;EACA,WAAW;CACb;CAEA,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,KAAK,SAAS,QAAQ;GACxB,OAAO,CAAC,KAAK,KAAK,CAAC;GACnB;EACF;EACA,IAAI,KAAK,SAAS,iBAAiB;GACjC,OAAO,CAAC,kBAAkB,MAAM,KAAK,CAAC,CAAC;GACvC;EACF;EACA,MAAM,EAAE,MAAM,cAAc,WAAW,IAAI;EAC3C,MAAM,eAAe,oBAAoB,MAAM,KAAK,CAAC,CAAC,KAAK,YAAY,QAAQ,KAAK,CAAC;EACrF,MAAM,eAAe,oBAAoB,WAAW,KAAK,CAAC,CAAC,KAAK,YAAY,QAAQ,KAAK,CAAC;EAC1F,MAAM,iBAAiB,CAAC,mBAAG,IAAI,IAAI,CAAC,GAAG,cAAc,GAAG,YAAY,CAAC,CAAC;EACtE,OACE,SAAS,SAAS,eAAe,SAAS,eACtC,CAAC,aAAa,MAAM,EAAE,IACtB,cACN;CACF;CACA,OAAO,CAAC,GAAG,IAAI,IAAI,QAAQ,CAAC;AAC9B;;;;;;AAOA,MAAa,sBACX,OACA,UACW;CACX,IAAI,SAAS;CACb,KAAK,MAAM,QAAQ,OACjB,IAAI,KAAK,SAAS,QAAQ,UAAU,KAAK;MACpC,IAAI,KAAK,SAAS,iBAAiB,UAAU,kBAAkB,MAAM,KAAK;MAC1E,UAAU,mBAAmB,WAAW,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,KAAK;CAEvE,OAAO;AACT;;;ACtIA,MAAa,mBAAmB"}