@hjmds/design-contracts 1.14.0 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/dist/design-profile-layout.d.ts +11 -0
  2. package/dist/design-profile-layout.d.ts.map +1 -0
  3. package/dist/design-profile-layout.js +18 -0
  4. package/dist/design-profile-layout.js.map +1 -0
  5. package/dist/design-profile.d.ts +89 -0
  6. package/dist/design-profile.d.ts.map +1 -0
  7. package/dist/design-profile.js +262 -0
  8. package/dist/design-profile.js.map +1 -0
  9. package/dist/design-system-provider.d.ts +6 -0
  10. package/dist/design-system-provider.d.ts.map +1 -1
  11. package/dist/design-system-provider.js +4 -2
  12. package/dist/design-system-provider.js.map +1 -1
  13. package/dist/gooey-navigation.d.ts +19 -1
  14. package/dist/gooey-navigation.d.ts.map +1 -1
  15. package/dist/gooey-navigation.js +37 -2
  16. package/dist/gooey-navigation.js.map +1 -1
  17. package/dist/palette-contrast.d.ts +6 -0
  18. package/dist/palette-contrast.d.ts.map +1 -1
  19. package/dist/palette-contrast.js +17 -0
  20. package/dist/palette-contrast.js.map +1 -1
  21. package/dist/screen-patterns.d.ts +16 -1
  22. package/dist/screen-patterns.d.ts.map +1 -1
  23. package/dist/screen-patterns.js +4 -0
  24. package/dist/screen-patterns.js.map +1 -1
  25. package/dist/toast-liquid.d.ts +3 -1
  26. package/dist/toast-liquid.d.ts.map +1 -1
  27. package/dist/toast-liquid.js +6 -2
  28. package/dist/toast-liquid.js.map +1 -1
  29. package/dist/version.d.ts +1 -1
  30. package/dist/version.js +1 -1
  31. package/dist/version.js.map +1 -1
  32. package/docs/asset.md +7 -0
  33. package/docs/brand-boundary.md +14 -5
  34. package/docs/code-block.md +12 -1
  35. package/docs/collapsible.md +6 -0
  36. package/docs/design-profile.md +263 -0
  37. package/docs/design-system-provider.md +7 -0
  38. package/docs/dialog.md +5 -1
  39. package/docs/generated/component-maturity.md +1 -1
  40. package/docs/generated/renderer-evidence.json +3 -3
  41. package/docs/generated/renderer-evidence.md +1 -1
  42. package/docs/generated/showcase-manifest.json +1 -1
  43. package/docs/gooey-navigation.md +37 -6
  44. package/docs/heading.md +15 -0
  45. package/docs/theming.md +18 -5
  46. package/docs/usage/README.md +5 -0
  47. package/docs/usage/components/activity-heatmap.md +3 -1
  48. package/docs/usage/components/agreement.md +2 -0
  49. package/docs/usage/components/alert-dialog.md +9 -0
  50. package/docs/usage/components/asset.md +9 -2
  51. package/docs/usage/components/badge.md +3 -1
  52. package/docs/usage/components/bottom-cta.md +3 -1
  53. package/docs/usage/components/bottom-navigation.md +5 -1
  54. package/docs/usage/components/button.md +7 -1
  55. package/docs/usage/components/calendar.md +3 -1
  56. package/docs/usage/components/card.md +19 -2
  57. package/docs/usage/components/carousel.md +5 -0
  58. package/docs/usage/components/code-block.md +14 -2
  59. package/docs/usage/components/collapsible.md +10 -2
  60. package/docs/usage/components/combobox.md +12 -1
  61. package/docs/usage/components/command-palette.md +11 -6
  62. package/docs/usage/components/content-transition.md +1 -1
  63. package/docs/usage/components/context-menu.md +7 -1
  64. package/docs/usage/components/date-picker.md +3 -1
  65. package/docs/usage/components/design-system-provider.md +7 -5
  66. package/docs/usage/components/dialog.md +53 -1
  67. package/docs/usage/components/field.md +3 -1
  68. package/docs/usage/components/form.md +7 -2
  69. package/docs/usage/components/heading.md +7 -1
  70. package/docs/usage/components/image.md +2 -0
  71. package/docs/usage/components/list-row.md +2 -0
  72. package/docs/usage/components/list.md +2 -0
  73. package/docs/usage/components/load-more.md +3 -1
  74. package/docs/usage/components/mentions.md +3 -1
  75. package/docs/usage/components/menu.md +3 -1
  76. package/docs/usage/components/menubar.md +7 -1
  77. package/docs/usage/components/message-composer.md +11 -5
  78. package/docs/usage/components/notice.md +9 -0
  79. package/docs/usage/components/number-field.md +3 -1
  80. package/docs/usage/components/onboarding-screen.md +16 -9
  81. package/docs/usage/components/overview-screen.md +73 -0
  82. package/docs/usage/components/password-field.md +5 -1
  83. package/docs/usage/components/popover.md +7 -0
  84. package/docs/usage/components/saved-items-screen.md +3 -1
  85. package/docs/usage/components/screen-layout.md +9 -1
  86. package/docs/usage/components/search-field.md +3 -1
  87. package/docs/usage/components/select.md +9 -0
  88. package/docs/usage/components/sheet.md +14 -1
  89. package/docs/usage/components/skeleton.md +9 -0
  90. package/docs/usage/components/slider.md +3 -1
  91. package/docs/usage/components/statistic.md +3 -1
  92. package/docs/usage/components/surface.md +8 -1
  93. package/docs/usage/components/tabs.md +11 -2
  94. package/docs/usage/components/tag.md +3 -1
  95. package/docs/usage/components/tags-input.md +6 -1
  96. package/docs/usage/components/text-area.md +3 -1
  97. package/docs/usage/components/text-transition.md +8 -2
  98. package/docs/usage/components/text.md +3 -1
  99. package/docs/usage/components/toast.md +18 -1
  100. package/docs/usage/components/top-bar.md +3 -1
  101. package/docs/usage/compositions/command-records.md +106 -0
  102. package/docs/usage/compositions/content-transition-comparison.md +108 -0
  103. package/docs/usage/compositions/date-time-selection.md +110 -0
  104. package/docs/usage/compositions/design-profile-comparison.md +114 -0
  105. package/docs/usage/tokens/color.md +9 -2
  106. package/docs/usage/tokens/elevation-opacity.md +8 -1
  107. package/docs/usage/tokens/motion.md +10 -1
  108. package/docs/usage/tokens/radius.md +8 -1
  109. package/docs/usage/tokens/typography.md +15 -1
  110. package/package.json +13 -1
@@ -1 +1 @@
1
- {"version":3,"file":"design-system-provider.js","sourceRoot":"","sources":["../src/design-system-provider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EACL,OAAO,EACP,MAAM,EACN,UAAU,EACV,iBAAiB,GAIlB,MAAM,aAAa,CAAC;AAgBrB;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,SAAgC;IAC/D,OAAO,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,kBAAkB,CAAC;AACvE,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAClC,YAAoB,EACpB,mBAA4B;IAE5B,OAAO,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;AAC7F,CAAC;AAcD;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CACnC,OAA4B,EAC5B,UAAoE;IAEpE,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC;AAC7E,CAAC;AAiBD,MAAM,CAAC,MAAM,+BAA+B,GAAG;IAC7C,KAAK,EAAE,QAAQ;IACf,SAAS,EAAE,KAAK;IAChB,SAAS,EAAE,CAAC;IACZ,aAAa,EAAE,KAAK;IACpB,mBAAmB,EAAE,KAAK;IAC1B,OAAO,EAAE,aAAa;CAQtB,CAAC;AAyCH,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAA2C,CAAC;AAC3F,MAAM,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAA4C,CAAC;AAC9F,MAAM,gBAAgB,GAAG,iBAAiB,CAAC;AAE3C,SAAS,iBAAiB,CACxB,KAAc,EACd,IAAuB,EACvB,KAAa;IAEb,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,SAAS,CAAC,6BAA6B,KAAK,yBAAyB,CAAC,CAAC;IACnF,CAAC;IACD,MAAM,MAAM,GAAG,KAA0C,CAAC;IAC1D,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/D,MAAM,IAAI,SAAS,CACjB,6BAA6B,KAAK,IAAI,GAAG,gCAAgC,CAC1E,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iCAAiC,CAC/C,KAAgC;IAEhC,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,IAAI,SAAS,CAAC,6CAA6C,CAAC,CAAC;IACrE,CAAC;IACD,IAAI,KAAK,CAAC,WAAW,KAAK,IAAI,IAAI,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ,EAAE,CAAC;QACxE,MAAM,IAAI,SAAS,CAAC,yDAAyD,CAAC,CAAC;IACjF,CAAC;IACD,uCAAuC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC9B,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QACpD,MAAM,IAAI,SAAS,CAAC,qDAAqD,CAAC,CAAC;IAC7E,CAAC;IACD,iBAAiB,CAAC,OAAO,CAAC,KAAK,EAAE,cAAc,EAAE,eAAe,CAAC,CAAC;IAClE,iBAAiB,CACf,OAAO,CAAC,aAAa,EACrB,eAAe,EACf,uBAAuB,CACxB,CAAC;IACF,iBAAiB,CACf,OAAO,CAAC,iBAAiB,EACzB,eAAe,EACf,2BAA2B,CAC5B,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,KAAc,EAAE,KAAa;IAClD,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,MAAM,IAAI,SAAS,CAAC,2BAA2B,KAAK,oBAAoB,CAAC,CAAC;IAC5E,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,KAA4B,EAAE,KAAa;IAClE,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;QACvC,MAAM,IAAI,SAAS,CAAC,uCAAuC,KAAK,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACxF,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,KAA4B,EAAE,KAAa;IAClE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QACvE,MAAM,IAAI,UAAU,CAClB,2BAA2B,KAAK,yCAAyC,CAC1E,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,KAAc,EAAE,KAAa;IAClD,IAAI,KAAK,KAAK,aAAa,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACnD,MAAM,IAAI,SAAS,CAAC,uCAAuC,KAAK,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACxF,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oCAAoC,CAClD,KAAmC;IAEnC,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,SAAS,CAAC,8CAA8C,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC3F,CAAC;IACD,IACE,KAAK,CAAC,SAAS,KAAK,SAAS;QAC7B,KAAK,CAAC,SAAS,KAAK,KAAK;QACzB,KAAK,CAAC,SAAS,KAAK,KAAK,EACzB,CAAC;QACD,MAAM,IAAI,SAAS,CACjB,kDAAkD,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAC5E,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAClC,eAAe,CAAC,KAAK,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,KAAK,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QACtC,aAAa,CAAC,KAAK,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;IACtD,CAAC;IACD,IAAI,KAAK,CAAC,mBAAmB,KAAK,SAAS,EAAE,CAAC;QAC5C,aAAa,CAAC,KAAK,CAAC,mBAAmB,EAAE,qBAAqB,CAAC,CAAC;IAClE,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAChC,aAAa,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAC1C,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uCAAuC,CACrD,WAA4C;IAE5C,IAAI,WAAW,CAAC,KAAK,KAAK,OAAO,IAAI,WAAW,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;QAClE,MAAM,IAAI,SAAS,CACjB,sDAAsD,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAClF,CAAC;IACJ,CAAC;IACD,eAAe,CAAC,WAAW,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAC;IAC3D,eAAe,CAAC,WAAW,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAC;IAC3D,aAAa,CAAC,WAAW,CAAC,aAAa,EAAE,sBAAsB,CAAC,CAAC;IACjE,aAAa,CAAC,WAAW,CAAC,mBAAmB,EAAE,4BAA4B,CAAC,CAAC;IAC7E,aAAa,CAAC,WAAW,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;AACvD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,8BAA8B,CAC5C,KAAmC,EACnC,OAA8C;IAE9C,oCAAoC,CAAC,KAAK,CAAC,CAAC;IAC5C,IAAI,OAAO,CAAC,WAAW,KAAK,OAAO,IAAI,OAAO,CAAC,WAAW,KAAK,MAAM,EAAE,CAAC;QACtE,MAAM,IAAI,SAAS,CAAC,oDAAoD,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IACzG,CAAC;IACD,IAAI,OAAO,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;QAC1C,eAAe,CAAC,OAAO,CAAC,eAAe,EAAE,iBAAiB,CAAC,CAAC;IAC9D,CAAC;IACD,IAAI,OAAO,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;QAC1C,eAAe,CAAC,OAAO,CAAC,eAAe,EAAE,iBAAiB,CAAC,CAAC;IAC9D,CAAC;IACD,IAAI,OAAO,CAAC,mBAAmB,KAAK,SAAS,EAAE,CAAC;QAC9C,aAAa,CAAC,OAAO,CAAC,mBAAmB,EAAE,qBAAqB,CAAC,CAAC;IACpE,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACjC,uCAAuC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,KAAK,GACT,KAAK,CAAC,KAAK,IAAI,OAAO,CAAC,MAAM,EAAE,KAAK,IAAI,+BAA+B,CAAC,KAAK,CAAC;IAChF,OAAO;QACL,KAAK,EAAE,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK;QACvD,SAAS,EACP,KAAK,CAAC,SAAS;YACf,OAAO,CAAC,MAAM,EAAE,SAAS;YACzB,OAAO,CAAC,eAAe;YACvB,+BAA+B,CAAC,SAAS;QAC3C,SAAS,EACP,KAAK,CAAC,SAAS;YACf,OAAO,CAAC,MAAM,EAAE,SAAS;YACzB,OAAO,CAAC,eAAe;YACvB,+BAA+B,CAAC,SAAS;QAC3C,aAAa,EACX,KAAK,CAAC,aAAa;YACnB,OAAO,CAAC,MAAM,EAAE,aAAa;YAC7B,OAAO,CAAC,mBAAmB;YAC3B,+BAA+B,CAAC,aAAa;QAC/C,mBAAmB,EACjB,KAAK,CAAC,mBAAmB;YACzB,OAAO,CAAC,MAAM,EAAE,mBAAmB;YACnC,+BAA+B,CAAC,mBAAmB;QACrD,OAAO,EACL,KAAK,CAAC,OAAO;YACb,OAAO,CAAC,MAAM,EAAE,OAAO;YACvB,+BAA+B,CAAC,OAAO;KAC1C,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gCAAgC,CAC9C,KAAmC,EACnC,OAA8C;IAE9C,MAAM,WAAW,GAAG,8BAA8B,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACnE,MAAM,aAAa,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG;QACZ,WAAW;QACX,OAAO,EAAE;YACP,KAAK,EAAE,aAAa,KAAK,SAAS;gBAChC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC;gBAC3B,CAAC,CAAC,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,GAAG,aAAa,EAAE;YACtD,aAAa,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC;YACzC,iBAAiB,EAAE,UAAU;SAC9B;KACF,CAAC;IACF,iCAAiC,CAAC,KAAK,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["import type { ColorReferencePalette } from \"./color-references.js\";\nimport { control, largeTextThreshold } from \"./foundations.js\";\nimport {\n ACCENTS,\n THEMES,\n accentFill,\n isThemePreference,\n type ResolvedTheme,\n type ThemeColors,\n type ThemePreference,\n} from \"./colors.js\";\n\n/**\n * Canonical logical direction. Existing public component-specific names stay\n * source-compatible as aliases of this type instead of redeclaring the union.\n */\nexport type DesignSystemDirection = \"ltr\" | \"rtl\";\n\n/**\n * A continuous OS/user font-scale multiplier (iOS Dynamic Type, Android font\n * size, browser zoom) — not `ShowcaseTextScale`'s closed `1 | 1.5 | 2`, which\n * only names Showcase's five fixture stories, or `description-list.ts`'s\n * local layout clamp. This is the single upstream signal both now consume.\n */\nexport type DesignSystemTextScale = number;\n\n/**\n * Whether a resolved `textScale` has crossed into large-text layout. A\n * stylesheet cannot compare numbers, so the provider resolves the axis once and\n * publishes the result for surfaces that can only match a flag (#20).\n */\nexport function isLargeTextScale(textScale: DesignSystemTextScale): boolean {\n return Number.isFinite(textScale) && textScale >= largeTextThreshold;\n}\n\n/**\n * Visible height a compact control paints under a product's target-size policy.\n *\n * The compact recipes stay at 36 and reach the 44pt target through hit slop,\n * which satisfies WCAG 2.5.8 but not the stricter iOS/Android guidance some\n * products commit to. `minimumVisualTarget` turns that commitment into one\n * resolved axis, so both renderers compute the same geometry instead of each\n * consumer re-adding `minHeight` in product styles. It lives beside the axis\n * rather than in `foundations` because it is a policy over a token, not a token.\n */\nexport function visibleControlHeight(\n recipeHeight: number,\n minimumVisualTarget: boolean,\n): number {\n return minimumVisualTarget ? Math.max(recipeHeight, control.minTouchTarget) : recipeHeight;\n}\n\n/**\n * 한 화면 안에서 목록·메뉴·표가 각각 다른 밀도로 그려지던 문제를 하나의 축으로 모은다.\n * 지금까지는 `ListRow density`, `Menu density`, DataTable의 행 높이가 서로 모르는 값이라\n * 제품이 세 군데를 따로 맞췄다. 컴포넌트의 명시적 prop이 언제나 이 축을 이긴다 —\n * 전역값은 기본이지 강제가 아니다.\n *\n * OS 신호가 없다. 밀도는 제품의 입장(같은 화면에 얼마나 담을 것인가)이지 사용자 설정이\n * 아니라서 `system*` 짝을 두지 않았다. 반대로 큰 글자 설정은 사용자 설정이므로\n * `textScale`이 큰 상태에서 `compact`를 쓰는 것은 제품이 스스로 막아야 한다.\n */\nexport type DesignSystemDensity = \"comfortable\" | \"compact\";\n\n/**\n * 전역 밀도를 컴포넌트마다 다른 밀도 어휘로 옮긴다. 컴포넌트의 명시적 prop이 언제나\n * 이긴다 — 전역값은 기본이지 강제가 아니다. 어휘를 하나로 통일하지 않은 이유: 목록의\n * `relaxed`와 표의 `regular`는 같은 말이 아니고, 억지로 합치면 둘 중 하나가 거짓말을 한다.\n */\nexport function resolveDensityDefault<Comfortable extends string, Compact extends string>(\n density: DesignSystemDensity,\n vocabulary: Readonly<{ comfortable: Comfortable; compact: Compact }>,\n): Comfortable | Compact {\n return density === \"compact\" ? vocabulary.compact : vocabulary.comfortable;\n}\n\nexport type DesignSystemEnvironmentInput = Readonly<{\n theme?: ThemePreference;\n direction?: DesignSystemDirection;\n textScale?: DesignSystemTextScale;\n reducedMotion?: boolean;\n /**\n * Paint the minimum touch target as visible control geometry instead of\n * reaching it with hit slop. This is a product accessibility stance, not an\n * OS signal, so it has no `system*` counterpart.\n */\n minimumVisualTarget?: boolean;\n /** Default row/menu/table density for everything below this provider. */\n density?: DesignSystemDensity;\n}>;\n\nexport const designSystemEnvironmentDefaults = {\n theme: \"system\",\n direction: \"ltr\",\n textScale: 1,\n reducedMotion: false,\n minimumVisualTarget: false,\n density: \"comfortable\",\n} as const satisfies Readonly<{\n theme: ThemePreference;\n direction: DesignSystemDirection;\n textScale: DesignSystemTextScale;\n reducedMotion: boolean;\n minimumVisualTarget: boolean;\n density: DesignSystemDensity;\n}>;\n\nexport type ResolvedDesignSystemEnvironment = Readonly<{\n theme: ResolvedTheme;\n direction: DesignSystemDirection;\n textScale: DesignSystemTextScale;\n reducedMotion: boolean;\n minimumVisualTarget: boolean;\n density: DesignSystemDensity;\n}>;\n\nexport type ResolveDesignSystemEnvironmentOptions = Readonly<{\n /**\n * The platform's current OS-level scheme, consulted only when `theme`\n * resolves to `\"system\"`. Renderers detect this themselves\n * (`Appearance.getColorScheme()`, `matchMedia('(prefers-color-scheme)')`)\n * — this package never queries the OS.\n */\n systemTheme: ResolvedTheme;\n /** Optional OS/renderer signals used when neither input nor a parent supplies the axis. */\n systemDirection?: DesignSystemDirection;\n systemTextScale?: DesignSystemTextScale;\n systemReducedMotion?: boolean;\n /**\n * Product brand colors layered over the HJM semantic keys. The key set is\n * unchanged, so recipes and contrast rules keep applying. Without this entry\n * point a consumer has to build its own token layer and override the CSS\n * variables, which is a copy of the canonical palette. Only the supplied\n * keys are replaced; the rest keep the HJM defaults.\n */\n brandPalette?: Readonly<Partial<Record<ResolvedTheme, Readonly<Partial<ThemeColors>>>>>;\n /** A nested renderer inherits the already-resolved parent before consulting OS defaults. */\n parent?: ResolvedDesignSystemEnvironment;\n}>;\n\nexport type DesignSystemProviderValue = Readonly<{\n environment: ResolvedDesignSystemEnvironment;\n /** Palette consumed directly by `resolveColorReference`. */\n palette: ColorReferencePalette;\n}>;\n\nconst themeColorKeys = Object.keys(THEMES.light) as readonly (keyof typeof THEMES.light)[];\nconst accentColorKeys = Object.keys(ACCENTS.light) as readonly (keyof typeof ACCENTS.light)[];\nconst sixDigitHexColor = /^#[0-9a-f]{6}$/i;\n\nfunction assertColorRecord(\n value: unknown,\n keys: readonly string[],\n field: string,\n): asserts value is Readonly<Record<string, string>> {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n throw new TypeError(`DesignSystemProviderValue ${field} must be a color record`);\n }\n const record = value as Readonly<Record<string, unknown>>;\n for (const key of keys) {\n const color = record[key];\n if (typeof color !== \"string\" || !sixDigitHexColor.test(color)) {\n throw new TypeError(\n `DesignSystemProviderValue ${field}.${key} must be a six-digit hex color`,\n );\n }\n }\n}\n\n/**\n * Runtime boundary for the resolved palette a renderer receives, including one\n * merged from a partial `brandPalette`: after merging, every semantic role a\n * recipe reads must be present, and alpha composition requires six-digit hex.\n * Brand rules: docs/brand-boundary.md.\n */\nexport function validateDesignSystemProviderValue(\n value: DesignSystemProviderValue,\n): void {\n if (value === null || typeof value !== \"object\") {\n throw new TypeError(\"DesignSystemProviderValue must be an object\");\n }\n if (value.environment === null || typeof value.environment !== \"object\") {\n throw new TypeError(\"DesignSystemProviderValue environment must be an object\");\n }\n validateResolvedDesignSystemEnvironment(value.environment);\n const palette = value.palette;\n if (palette === null || typeof palette !== \"object\") {\n throw new TypeError(\"DesignSystemProviderValue palette must be an object\");\n }\n assertColorRecord(palette.theme, themeColorKeys, \"palette.theme\");\n assertColorRecord(\n palette.statusAccents,\n accentColorKeys,\n \"palette.statusAccents\",\n );\n assertColorRecord(\n palette.statusAccentFills,\n accentColorKeys,\n \"palette.statusAccentFills\",\n );\n}\n\nfunction assertBoolean(value: boolean, field: string): void {\n if (typeof value !== \"boolean\") {\n throw new TypeError(`DesignSystemEnvironment ${field} must be a boolean`);\n }\n}\n\nfunction assertDirection(value: DesignSystemDirection, field: string): void {\n if (value !== \"ltr\" && value !== \"rtl\") {\n throw new TypeError(`Unsupported DesignSystemEnvironment ${field}: ${String(value)}`);\n }\n}\n\nfunction assertTextScale(value: DesignSystemTextScale, field: string): void {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) {\n throw new RangeError(\n `DesignSystemEnvironment ${field} must be a finite number greater than 0`,\n );\n }\n}\n\nfunction assertDensity(value: unknown, field: string): asserts value is DesignSystemDensity {\n if (value !== \"comfortable\" && value !== \"compact\") {\n throw new TypeError(`Unsupported DesignSystemEnvironment ${field}: ${String(value)}`);\n }\n}\n\nexport function validateDesignSystemEnvironmentInput(\n input: DesignSystemEnvironmentInput,\n): void {\n if (input.theme !== undefined && !isThemePreference(input.theme)) {\n throw new TypeError(`Unsupported DesignSystemEnvironment theme: ${String(input.theme)}`);\n }\n if (\n input.direction !== undefined &&\n input.direction !== \"ltr\" &&\n input.direction !== \"rtl\"\n ) {\n throw new TypeError(\n `Unsupported DesignSystemEnvironment direction: ${String(input.direction)}`,\n );\n }\n if (input.textScale !== undefined) {\n assertTextScale(input.textScale, \"textScale\");\n }\n if (input.reducedMotion !== undefined) {\n assertBoolean(input.reducedMotion, \"reducedMotion\");\n }\n if (input.minimumVisualTarget !== undefined) {\n assertBoolean(input.minimumVisualTarget, \"minimumVisualTarget\");\n }\n if (input.density !== undefined) {\n assertDensity(input.density, \"density\");\n }\n}\n\n/**\n * A parent has already crossed the system-preference boundary. Unlike the\n * partial input validator, this rejects `\"system\"`, missing axes, and every\n * malformed resolved value instead of silently resolving them again.\n */\nexport function validateResolvedDesignSystemEnvironment(\n environment: ResolvedDesignSystemEnvironment,\n): void {\n if (environment.theme !== \"light\" && environment.theme !== \"dark\") {\n throw new TypeError(\n `Unsupported ResolvedDesignSystemEnvironment theme: ${String(environment.theme)}`,\n );\n }\n assertDirection(environment.direction, \"parent direction\");\n assertTextScale(environment.textScale, \"parent textScale\");\n assertBoolean(environment.reducedMotion, \"parent reducedMotion\");\n assertBoolean(environment.minimumVisualTarget, \"parent minimumVisualTarget\");\n assertDensity(environment.density, \"parent density\");\n}\n\n/**\n * Merges partial signals with safe defaults and resolves `\"system\"` against\n * the renderer-supplied `systemTheme`. This — not a React/RN context, which\n * this package cannot own — is the entire portable contract behind antd\n * `ConfigProvider`: see docs/design-system-provider.md for what was\n * deliberately left to the renderer.\n */\nexport function resolveDesignSystemEnvironment(\n input: DesignSystemEnvironmentInput,\n options: ResolveDesignSystemEnvironmentOptions,\n): ResolvedDesignSystemEnvironment {\n validateDesignSystemEnvironmentInput(input);\n if (options.systemTheme !== \"light\" && options.systemTheme !== \"dark\") {\n throw new TypeError(`Unsupported DesignSystemEnvironment systemTheme: ${String(options.systemTheme)}`);\n }\n if (options.systemDirection !== undefined) {\n assertDirection(options.systemDirection, \"systemDirection\");\n }\n if (options.systemTextScale !== undefined) {\n assertTextScale(options.systemTextScale, \"systemTextScale\");\n }\n if (options.systemReducedMotion !== undefined) {\n assertBoolean(options.systemReducedMotion, \"systemReducedMotion\");\n }\n if (options.parent !== undefined) {\n validateResolvedDesignSystemEnvironment(options.parent);\n }\n\n const theme =\n input.theme ?? options.parent?.theme ?? designSystemEnvironmentDefaults.theme;\n return {\n theme: theme === \"system\" ? options.systemTheme : theme,\n direction:\n input.direction ??\n options.parent?.direction ??\n options.systemDirection ??\n designSystemEnvironmentDefaults.direction,\n textScale:\n input.textScale ??\n options.parent?.textScale ??\n options.systemTextScale ??\n designSystemEnvironmentDefaults.textScale,\n reducedMotion:\n input.reducedMotion ??\n options.parent?.reducedMotion ??\n options.systemReducedMotion ??\n designSystemEnvironmentDefaults.reducedMotion,\n minimumVisualTarget:\n input.minimumVisualTarget ??\n options.parent?.minimumVisualTarget ??\n designSystemEnvironmentDefaults.minimumVisualTarget,\n density:\n input.density ??\n options.parent?.density ??\n designSystemEnvironmentDefaults.density,\n };\n}\n\n/**\n * Resolves the portable Provider value without owning React/RN Context. A\n * renderer stores this object in its own context and feeds `palette` directly\n * to recipe color resolution.\n */\nexport function resolveDesignSystemProviderValue(\n input: DesignSystemEnvironmentInput,\n options: ResolveDesignSystemEnvironmentOptions,\n): DesignSystemProviderValue {\n const environment = resolveDesignSystemEnvironment(input, options);\n const brandOverride = options.brandPalette?.[environment.theme];\n const value = {\n environment,\n palette: {\n theme: brandOverride === undefined\n ? THEMES[environment.theme]\n : { ...THEMES[environment.theme], ...brandOverride },\n statusAccents: ACCENTS[environment.theme],\n statusAccentFills: accentFill,\n },\n };\n validateDesignSystemProviderValue(value);\n return value;\n}\n"]}
1
+ {"version":3,"file":"design-system-provider.js","sourceRoot":"","sources":["../src/design-system-provider.ts"],"names":[],"mappings":"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","sourcesContent":["import type { HjmDesignProfile } from \"./design-profile.js\";\nimport type { ColorReferencePalette } from \"./color-references.js\";\nimport { control, largeTextThreshold } from \"./foundations.js\";\nimport {\n ACCENTS,\n THEMES,\n accentFill,\n isThemePreference,\n type ResolvedTheme,\n type ThemeColors,\n type ThemePreference,\n} from \"./colors.js\";\n\n/**\n * Canonical logical direction. Existing public component-specific names stay\n * source-compatible as aliases of this type instead of redeclaring the union.\n */\nexport type DesignSystemDirection = \"ltr\" | \"rtl\";\n\n/**\n * A continuous OS/user font-scale multiplier (iOS Dynamic Type, Android font\n * size, browser zoom) — not `ShowcaseTextScale`'s closed `1 | 1.5 | 2`, which\n * only names Showcase's five fixture stories, or `description-list.ts`'s\n * local layout clamp. This is the single upstream signal both now consume.\n */\nexport type DesignSystemTextScale = number;\n\n/**\n * Whether a resolved `textScale` has crossed into large-text layout. A\n * stylesheet cannot compare numbers, so the provider resolves the axis once and\n * publishes the result for surfaces that can only match a flag (#20).\n */\nexport function isLargeTextScale(textScale: DesignSystemTextScale): boolean {\n return Number.isFinite(textScale) && textScale >= largeTextThreshold;\n}\n\n/**\n * Visible height a compact control paints under a product's target-size policy.\n *\n * The compact recipes stay at 36 and reach the 44pt target through hit slop,\n * which satisfies WCAG 2.5.8 but not the stricter iOS/Android guidance some\n * products commit to. `minimumVisualTarget` turns that commitment into one\n * resolved axis, so both renderers compute the same geometry instead of each\n * consumer re-adding `minHeight` in product styles. It lives beside the axis\n * rather than in `foundations` because it is a policy over a token, not a token.\n */\nexport function visibleControlHeight(\n recipeHeight: number,\n minimumVisualTarget: boolean,\n): number {\n return minimumVisualTarget ? Math.max(recipeHeight, control.minTouchTarget) : recipeHeight;\n}\n\n/**\n * 한 화면 안에서 목록·메뉴·표가 각각 다른 밀도로 그려지던 문제를 하나의 축으로 모은다.\n * 지금까지는 `ListRow density`, `Menu density`, DataTable의 행 높이가 서로 모르는 값이라\n * 제품이 세 군데를 따로 맞췄다. 컴포넌트의 명시적 prop이 언제나 이 축을 이긴다 —\n * 전역값은 기본이지 강제가 아니다.\n *\n * OS 신호가 없다. 밀도는 제품의 입장(같은 화면에 얼마나 담을 것인가)이지 사용자 설정이\n * 아니라서 `system*` 짝을 두지 않았다. 반대로 큰 글자 설정은 사용자 설정이므로\n * `textScale`이 큰 상태에서 `compact`를 쓰는 것은 제품이 스스로 막아야 한다.\n */\nexport type DesignSystemDensity = \"comfortable\" | \"compact\";\n\n/**\n * 전역 밀도를 컴포넌트마다 다른 밀도 어휘로 옮긴다. 컴포넌트의 명시적 prop이 언제나\n * 이긴다 — 전역값은 기본이지 강제가 아니다. 어휘를 하나로 통일하지 않은 이유: 목록의\n * `relaxed`와 표의 `regular`는 같은 말이 아니고, 억지로 합치면 둘 중 하나가 거짓말을 한다.\n */\nexport function resolveDensityDefault<Comfortable extends string, Compact extends string>(\n density: DesignSystemDensity,\n vocabulary: Readonly<{ comfortable: Comfortable; compact: Compact }>,\n): Comfortable | Compact {\n return density === \"compact\" ? vocabulary.compact : vocabulary.comfortable;\n}\n\nexport type DesignSystemEnvironmentInput = Readonly<{\n theme?: ThemePreference;\n direction?: DesignSystemDirection;\n textScale?: DesignSystemTextScale;\n reducedMotion?: boolean;\n /**\n * Paint the minimum touch target as visible control geometry instead of\n * reaching it with hit slop. This is a product accessibility stance, not an\n * OS signal, so it has no `system*` counterpart.\n */\n minimumVisualTarget?: boolean;\n /** Default row/menu/table density for everything below this provider. */\n density?: DesignSystemDensity;\n}>;\n\nexport const designSystemEnvironmentDefaults = {\n theme: \"system\",\n direction: \"ltr\",\n textScale: 1,\n reducedMotion: false,\n minimumVisualTarget: false,\n density: \"comfortable\",\n} as const satisfies Readonly<{\n theme: ThemePreference;\n direction: DesignSystemDirection;\n textScale: DesignSystemTextScale;\n reducedMotion: boolean;\n minimumVisualTarget: boolean;\n density: DesignSystemDensity;\n}>;\n\nexport type ResolvedDesignSystemEnvironment = Readonly<{\n theme: ResolvedTheme;\n direction: DesignSystemDirection;\n textScale: DesignSystemTextScale;\n reducedMotion: boolean;\n minimumVisualTarget: boolean;\n density: DesignSystemDensity;\n}>;\n\nexport type ResolveDesignSystemEnvironmentOptions = Readonly<{\n /**\n * The platform's current OS-level scheme, consulted only when `theme`\n * resolves to `\"system\"`. Renderers detect this themselves\n * (`Appearance.getColorScheme()`, `matchMedia('(prefers-color-scheme)')`)\n * — this package never queries the OS.\n */\n systemTheme: ResolvedTheme;\n /** Optional OS/renderer signals used when neither input nor a parent supplies the axis. */\n systemDirection?: DesignSystemDirection;\n systemTextScale?: DesignSystemTextScale;\n systemReducedMotion?: boolean;\n /**\n * Product brand colors layered over the HJM semantic keys. The key set is\n * unchanged, so recipes and contrast rules keep applying. Without this entry\n * point a consumer has to build its own token layer and override the CSS\n * variables, which is a copy of the canonical palette. Only the supplied\n * keys are replaced; the rest keep the HJM defaults.\n */\n brandPalette?: Readonly<Partial<Record<ResolvedTheme, Readonly<Partial<ThemeColors>>>>>;\n /** Use defineHjmDesignProfile once at the product boundary; importing its registry\n * here at runtime would force every granular component to bundle all reference packs. */\n designProfile?: HjmDesignProfile;\n /** A nested renderer inherits the already-resolved parent before consulting OS defaults. */\n parent?: ResolvedDesignSystemEnvironment;\n}>;\n\nexport type DesignSystemProviderValue = Readonly<{\n environment: ResolvedDesignSystemEnvironment;\n /** Palette consumed directly by `resolveColorReference`. */\n palette: ColorReferencePalette;\n /** Optional to preserve existing pre-resolved provider values. */\n designProfile?: HjmDesignProfile;\n}>;\n\nconst themeColorKeys = Object.keys(THEMES.light) as readonly (keyof typeof THEMES.light)[];\nconst accentColorKeys = Object.keys(ACCENTS.light) as readonly (keyof typeof ACCENTS.light)[];\nconst sixDigitHexColor = /^#[0-9a-f]{6}$/i;\n\nfunction assertColorRecord(\n value: unknown,\n keys: readonly string[],\n field: string,\n): asserts value is Readonly<Record<string, string>> {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n throw new TypeError(`DesignSystemProviderValue ${field} must be a color record`);\n }\n const record = value as Readonly<Record<string, unknown>>;\n for (const key of keys) {\n const color = record[key];\n if (typeof color !== \"string\" || !sixDigitHexColor.test(color)) {\n throw new TypeError(\n `DesignSystemProviderValue ${field}.${key} must be a six-digit hex color`,\n );\n }\n }\n}\n\n/**\n * Runtime boundary for the resolved palette a renderer receives, including one\n * merged from a partial `brandPalette`: after merging, every semantic role a\n * recipe reads must be present, and alpha composition requires six-digit hex.\n * Brand rules: docs/brand-boundary.md.\n */\nexport function validateDesignSystemProviderValue(\n value: DesignSystemProviderValue,\n): void {\n if (value === null || typeof value !== \"object\") {\n throw new TypeError(\"DesignSystemProviderValue must be an object\");\n }\n if (value.environment === null || typeof value.environment !== \"object\") {\n throw new TypeError(\"DesignSystemProviderValue environment must be an object\");\n }\n validateResolvedDesignSystemEnvironment(value.environment);\n const palette = value.palette;\n if (palette === null || typeof palette !== \"object\") {\n throw new TypeError(\"DesignSystemProviderValue palette must be an object\");\n }\n assertColorRecord(palette.theme, themeColorKeys, \"palette.theme\");\n assertColorRecord(\n palette.statusAccents,\n accentColorKeys,\n \"palette.statusAccents\",\n );\n assertColorRecord(\n palette.statusAccentFills,\n accentColorKeys,\n \"palette.statusAccentFills\",\n );\n}\n\nfunction assertBoolean(value: boolean, field: string): void {\n if (typeof value !== \"boolean\") {\n throw new TypeError(`DesignSystemEnvironment ${field} must be a boolean`);\n }\n}\n\nfunction assertDirection(value: DesignSystemDirection, field: string): void {\n if (value !== \"ltr\" && value !== \"rtl\") {\n throw new TypeError(`Unsupported DesignSystemEnvironment ${field}: ${String(value)}`);\n }\n}\n\nfunction assertTextScale(value: DesignSystemTextScale, field: string): void {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) {\n throw new RangeError(\n `DesignSystemEnvironment ${field} must be a finite number greater than 0`,\n );\n }\n}\n\nfunction assertDensity(value: unknown, field: string): asserts value is DesignSystemDensity {\n if (value !== \"comfortable\" && value !== \"compact\") {\n throw new TypeError(`Unsupported DesignSystemEnvironment ${field}: ${String(value)}`);\n }\n}\n\nexport function validateDesignSystemEnvironmentInput(\n input: DesignSystemEnvironmentInput,\n): void {\n if (input.theme !== undefined && !isThemePreference(input.theme)) {\n throw new TypeError(`Unsupported DesignSystemEnvironment theme: ${String(input.theme)}`);\n }\n if (\n input.direction !== undefined &&\n input.direction !== \"ltr\" &&\n input.direction !== \"rtl\"\n ) {\n throw new TypeError(\n `Unsupported DesignSystemEnvironment direction: ${String(input.direction)}`,\n );\n }\n if (input.textScale !== undefined) {\n assertTextScale(input.textScale, \"textScale\");\n }\n if (input.reducedMotion !== undefined) {\n assertBoolean(input.reducedMotion, \"reducedMotion\");\n }\n if (input.minimumVisualTarget !== undefined) {\n assertBoolean(input.minimumVisualTarget, \"minimumVisualTarget\");\n }\n if (input.density !== undefined) {\n assertDensity(input.density, \"density\");\n }\n}\n\n/**\n * A parent has already crossed the system-preference boundary. Unlike the\n * partial input validator, this rejects `\"system\"`, missing axes, and every\n * malformed resolved value instead of silently resolving them again.\n */\nexport function validateResolvedDesignSystemEnvironment(\n environment: ResolvedDesignSystemEnvironment,\n): void {\n if (environment.theme !== \"light\" && environment.theme !== \"dark\") {\n throw new TypeError(\n `Unsupported ResolvedDesignSystemEnvironment theme: ${String(environment.theme)}`,\n );\n }\n assertDirection(environment.direction, \"parent direction\");\n assertTextScale(environment.textScale, \"parent textScale\");\n assertBoolean(environment.reducedMotion, \"parent reducedMotion\");\n assertBoolean(environment.minimumVisualTarget, \"parent minimumVisualTarget\");\n assertDensity(environment.density, \"parent density\");\n}\n\n/**\n * Merges partial signals with safe defaults and resolves `\"system\"` against\n * the renderer-supplied `systemTheme`. This — not a React/RN context, which\n * this package cannot own — is the entire portable contract behind antd\n * `ConfigProvider`: see docs/design-system-provider.md for what was\n * deliberately left to the renderer.\n */\nexport function resolveDesignSystemEnvironment(\n input: DesignSystemEnvironmentInput,\n options: ResolveDesignSystemEnvironmentOptions,\n): ResolvedDesignSystemEnvironment {\n validateDesignSystemEnvironmentInput(input);\n if (options.systemTheme !== \"light\" && options.systemTheme !== \"dark\") {\n throw new TypeError(`Unsupported DesignSystemEnvironment systemTheme: ${String(options.systemTheme)}`);\n }\n if (options.systemDirection !== undefined) {\n assertDirection(options.systemDirection, \"systemDirection\");\n }\n if (options.systemTextScale !== undefined) {\n assertTextScale(options.systemTextScale, \"systemTextScale\");\n }\n if (options.systemReducedMotion !== undefined) {\n assertBoolean(options.systemReducedMotion, \"systemReducedMotion\");\n }\n if (options.parent !== undefined) {\n validateResolvedDesignSystemEnvironment(options.parent);\n }\n\n const theme =\n input.theme ?? options.parent?.theme ?? designSystemEnvironmentDefaults.theme;\n return {\n theme: theme === \"system\" ? options.systemTheme : theme,\n direction:\n input.direction ??\n options.parent?.direction ??\n options.systemDirection ??\n designSystemEnvironmentDefaults.direction,\n textScale:\n input.textScale ??\n options.parent?.textScale ??\n options.systemTextScale ??\n designSystemEnvironmentDefaults.textScale,\n reducedMotion:\n input.reducedMotion ??\n options.parent?.reducedMotion ??\n options.systemReducedMotion ??\n designSystemEnvironmentDefaults.reducedMotion,\n minimumVisualTarget:\n input.minimumVisualTarget ??\n options.parent?.minimumVisualTarget ??\n designSystemEnvironmentDefaults.minimumVisualTarget,\n density:\n input.density ??\n options.parent?.density ??\n designSystemEnvironmentDefaults.density,\n };\n}\n\n/**\n * Resolves the portable Provider value without owning React/RN Context. A\n * renderer stores this object in its own context and feeds `palette` directly\n * to recipe color resolution.\n */\nexport function resolveDesignSystemProviderValue(\n input: DesignSystemEnvironmentInput,\n options: ResolveDesignSystemEnvironmentOptions,\n): DesignSystemProviderValue {\n const environment = resolveDesignSystemEnvironment(input, options);\n const brandOverride = options.brandPalette?.[environment.theme];\n const basePalette = options.designProfile?.palette[environment.theme] ?? THEMES[environment.theme];\n const value = {\n environment,\n ...(options.designProfile === undefined ? {} : { designProfile: options.designProfile }),\n palette: {\n theme: brandOverride === undefined\n ? basePalette\n : { ...basePalette, ...brandOverride },\n statusAccents: ACCENTS[environment.theme],\n statusAccentFills: accentFill,\n },\n };\n validateDesignSystemProviderValue(value);\n return value;\n}\n"]}
@@ -1,8 +1,26 @@
1
- export type TabsAppearance = "standard" | "gooey";
1
+ export type TabsAppearance = "standard" | "slide" | "gooey";
2
2
  export type GooeyIndicatorRect = Readonly<{
3
3
  x: number;
4
4
  width: number;
5
5
  }>;
6
+ /** A product's explicit appearance wins; vertical tabs keep the canonical line.
7
+ * Plain profile sliding is not an implicit request for elastic gooey stretching. */
8
+ export declare function resolveTabsAppearance(appearance: TabsAppearance | undefined, selectionMotion?: "none" | "slide", orientation?: "horizontal" | "vertical"): TabsAppearance;
9
+ /** Both renderers interpolate measured bounds without changing selection or panels. */
10
+ export declare function resolveTabIndicator(from: GooeyIndicatorRect, to: GooeyIndicatorRect, appearance: "slide" | "gooey"): {
11
+ readonly duration: 320;
12
+ readonly input: readonly [0, 0.45, 1];
13
+ readonly x: readonly [number, number, number];
14
+ readonly width: readonly [number, number, number];
15
+ } | {
16
+ readonly duration: 200;
17
+ readonly input: readonly [0, 1];
18
+ readonly x: readonly [number, number];
19
+ readonly width: readonly [number, number];
20
+ };
21
+ /** Capture the current JS-driver frame before cancellation so rapid input starts
22
+ * where the indicator is visible, rather than jumping to the abandoned target. */
23
+ export declare function sampleTabIndicator(recipe: ReturnType<typeof resolveTabIndicator>, progress: number): GooeyIndicatorRect;
6
24
  /** Stretch only the selected indicator; tab hit targets and text never move. */
7
25
  export declare function resolveGooeyIndicator(from: GooeyIndicatorRect, to: GooeyIndicatorRect): {
8
26
  readonly duration: 320;
@@ -1 +1 @@
1
- {"version":3,"file":"gooey-navigation.d.ts","sourceRoot":"","sources":["../src/gooey-navigation.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,OAAO,CAAC;AAClD,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AACxE,gFAAgF;AAChF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,kBAAkB;;;;;EAMrF"}
1
+ {"version":3,"file":"gooey-navigation.d.ts","sourceRoot":"","sources":["../src/gooey-navigation.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;AAC5D,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAExE;oFACoF;AACpF,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,cAAc,GAAG,SAAS,EACtC,eAAe,GAAE,MAAM,GAAG,OAAgB,EAC1C,WAAW,GAAE,YAAY,GAAG,UAAyB,GACpD,cAAc,CAGhB;AAED,uFAAuF;AACvF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,kBAAkB,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO;;;;;;;;;;EAOlH;AAED;kFACkF;AAClF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,kBAAkB,CAUvH;AAKD,gFAAgF;AAChF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,kBAAkB;;;;;EAMrF"}
@@ -1,8 +1,43 @@
1
- /** Stretch only the selected indicator; tab hit targets and text never move. */
2
- export function resolveGooeyIndicator(from, to) {
1
+ import { motion } from "./foundations.js";
2
+ /** A product's explicit appearance wins; vertical tabs keep the canonical line.
3
+ * Plain profile sliding is not an implicit request for elastic gooey stretching. */
4
+ export function resolveTabsAppearance(appearance, selectionMotion = "none", orientation = "horizontal") {
5
+ if (appearance !== undefined && !["standard", "slide", "gooey"].includes(appearance))
6
+ throw new TypeError("Unsupported tabs appearance");
7
+ return orientation === "vertical" ? "standard" : appearance ?? (selectionMotion === "slide" ? "slide" : "standard");
8
+ }
9
+ /** Both renderers interpolate measured bounds without changing selection or panels. */
10
+ export function resolveTabIndicator(from, to, appearance) {
11
+ if (appearance === "gooey")
12
+ return resolveGooeyIndicator(from, to);
13
+ if (appearance !== "slide")
14
+ throw new TypeError("Unsupported moving tab indicator");
15
+ validateRects(from, to);
16
+ // Ordinary selection feedback uses the shared normal duration; retain the
17
+ // existing 320ms elastic recipe for explicit gooey consumers.
18
+ return { duration: motion.normal, input: [0, 1], x: [from.x, to.x], width: [from.width, to.width] };
19
+ }
20
+ /** Capture the current JS-driver frame before cancellation so rapid input starts
21
+ * where the indicator is visible, rather than jumping to the abandoned target. */
22
+ export function sampleTabIndicator(recipe, progress) {
23
+ const value = Number.isFinite(progress) ? Math.max(0, Math.min(1, progress)) : 1;
24
+ const index = recipe.input.findIndex((offset, i) => i > 0 && offset >= value);
25
+ const end = index < 0 ? recipe.input.length - 1 : index;
26
+ const start = Math.max(0, end - 1);
27
+ const fraction = (value - recipe.input[start]) / (recipe.input[end] - recipe.input[start]);
28
+ return {
29
+ x: recipe.x[start] + (recipe.x[end] - recipe.x[start]) * fraction,
30
+ width: recipe.width[start] + (recipe.width[end] - recipe.width[start]) * fraction,
31
+ };
32
+ }
33
+ function validateRects(from, to) {
3
34
  for (const rect of [from, to])
4
35
  if (!Number.isFinite(rect.x) || !Number.isFinite(rect.width) || rect.width <= 0)
5
36
  throw new RangeError("Indicator needs finite coordinates and positive widths");
37
+ }
38
+ /** Stretch only the selected indicator; tab hit targets and text never move. */
39
+ export function resolveGooeyIndicator(from, to) {
40
+ validateRects(from, to);
6
41
  // Keep the elastic bridge within the measured source/destination span, including RTL layouts.
7
42
  const left = Math.min(from.x, to.x);
8
43
  const right = Math.max(from.x + from.width, to.x + to.width);
@@ -1 +1 @@
1
- {"version":3,"file":"gooey-navigation.js","sourceRoot":"","sources":["../src/gooey-navigation.ts"],"names":[],"mappings":"AAEA,gFAAgF;AAChF,MAAM,UAAU,qBAAqB,CAAC,IAAwB,EAAE,EAAsB;IACpF,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC;YAAE,MAAM,IAAI,UAAU,CAAC,wDAAwD,CAAC,CAAC;IAC/L,8FAA8F;IAC9F,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;IAC7D,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,EAAW,CAAC;AAC/H,CAAC","sourcesContent":["export type TabsAppearance = \"standard\" | \"gooey\";\nexport type GooeyIndicatorRect = Readonly<{ x: number; width: number }>;\n/** Stretch only the selected indicator; tab hit targets and text never move. */\nexport function resolveGooeyIndicator(from: GooeyIndicatorRect, to: GooeyIndicatorRect) {\n for (const rect of [from, to]) if (!Number.isFinite(rect.x) || !Number.isFinite(rect.width) || rect.width <= 0) throw new RangeError(\"Indicator needs finite coordinates and positive widths\");\n // Keep the elastic bridge within the measured source/destination span, including RTL layouts.\n const left = Math.min(from.x, to.x);\n const right = Math.max(from.x + from.width, to.x + to.width);\n return { duration: 320, input: [0, 0.45, 1], x: [from.x, left, to.x], width: [from.width, right - left, to.width] } as const;\n}\n"]}
1
+ {"version":3,"file":"gooey-navigation.js","sourceRoot":"","sources":["../src/gooey-navigation.ts"],"names":[],"mappings":"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","sourcesContent":["import { motion } from \"./foundations.js\";\n\nexport type TabsAppearance = \"standard\" | \"slide\" | \"gooey\";\nexport type GooeyIndicatorRect = Readonly<{ x: number; width: number }>;\n\n/** A product's explicit appearance wins; vertical tabs keep the canonical line.\n * Plain profile sliding is not an implicit request for elastic gooey stretching. */\nexport function resolveTabsAppearance(\n appearance: TabsAppearance | undefined,\n selectionMotion: \"none\" | \"slide\" = \"none\",\n orientation: \"horizontal\" | \"vertical\" = \"horizontal\",\n): TabsAppearance {\n if (appearance !== undefined && ![\"standard\", \"slide\", \"gooey\"].includes(appearance)) throw new TypeError(\"Unsupported tabs appearance\");\n return orientation === \"vertical\" ? \"standard\" : appearance ?? (selectionMotion === \"slide\" ? \"slide\" : \"standard\");\n}\n\n/** Both renderers interpolate measured bounds without changing selection or panels. */\nexport function resolveTabIndicator(from: GooeyIndicatorRect, to: GooeyIndicatorRect, appearance: \"slide\" | \"gooey\") {\n if (appearance === \"gooey\") return resolveGooeyIndicator(from, to);\n if (appearance !== \"slide\") throw new TypeError(\"Unsupported moving tab indicator\");\n validateRects(from, to);\n // Ordinary selection feedback uses the shared normal duration; retain the\n // existing 320ms elastic recipe for explicit gooey consumers.\n return { duration: motion.normal, input: [0, 1], x: [from.x, to.x], width: [from.width, to.width] } as const;\n}\n\n/** Capture the current JS-driver frame before cancellation so rapid input starts\n * where the indicator is visible, rather than jumping to the abandoned target. */\nexport function sampleTabIndicator(recipe: ReturnType<typeof resolveTabIndicator>, progress: number): GooeyIndicatorRect {\n const value = Number.isFinite(progress) ? Math.max(0, Math.min(1, progress)) : 1;\n const index = recipe.input.findIndex((offset, i) => i > 0 && offset >= value);\n const end = index < 0 ? recipe.input.length - 1 : index;\n const start = Math.max(0, end - 1);\n const fraction = (value - recipe.input[start]!) / (recipe.input[end]! - recipe.input[start]!);\n return {\n x: recipe.x[start]! + (recipe.x[end]! - recipe.x[start]!) * fraction,\n width: recipe.width[start]! + (recipe.width[end]! - recipe.width[start]!) * fraction,\n };\n}\n\nfunction validateRects(from: GooeyIndicatorRect, to: GooeyIndicatorRect) {\n for (const rect of [from, to]) if (!Number.isFinite(rect.x) || !Number.isFinite(rect.width) || rect.width <= 0) throw new RangeError(\"Indicator needs finite coordinates and positive widths\");\n}\n/** Stretch only the selected indicator; tab hit targets and text never move. */\nexport function resolveGooeyIndicator(from: GooeyIndicatorRect, to: GooeyIndicatorRect) {\n validateRects(from, to);\n // Keep the elastic bridge within the measured source/destination span, including RTL layouts.\n const left = Math.min(from.x, to.x);\n const right = Math.max(from.x + from.width, to.x + to.width);\n return { duration: 320, input: [0, 0.45, 1], x: [from.x, left, to.x], width: [from.width, right - left, to.width] } as const;\n}\n"]}
@@ -16,4 +16,10 @@ export declare function contrastRatio(foreground: string, background: string): n
16
16
  export declare function checkPaletteContrast(palette: Readonly<ThemeColors>): readonly PaletteContrastFinding[];
17
17
  /** Checks a `brandPalette` the way the Provider applies it: each theme merged over the HJM defaults. */
18
18
  export declare function checkBrandPaletteContrast(brandPalette: Readonly<Partial<Record<ResolvedTheme, Readonly<Partial<ThemeColors>>>>>): Readonly<Record<ResolvedTheme, readonly PaletteContrastFinding[]>>;
19
+ /** Shared whole-surface fill adaptation. This palette-only entry keeps ordinary
20
+ * renderer imports independent of the optional preset registry/effect descriptors.
21
+ * The supported 0.85..1 interval keeps readable foregrounds outside the backdrop
22
+ * luminance interval; testing its black/white endpoints bounds any opaque backdrop.
23
+ * See docs/design-profile.md for why a reference card's low alpha is not copied. */
24
+ export declare function resolveSurfaceFillOpacity(requested: number, palette: Readonly<ThemeColors>): number;
19
25
  //# sourceMappingURL=palette-contrast.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"palette-contrast.d.ts","sourceRoot":"","sources":["../src/palette-contrast.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAE3E,mEAAmE;AACnE,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACzC,UAAU,EAAE,MAAM,WAAW,CAAC;IAC9B,UAAU,EAAE,MAAM,WAAW,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;CAC3B,CAAC,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,mBAAmB,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAKvF,eAAO,MAAM,oBAAoB,EAAE,SAAS,mBAAmB,EAY9D,CAAC;AAaF,gEAAgE;AAChE,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAI5E;AAED,gGAAgG;AAChG,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,GAC7B,SAAS,sBAAsB,EAAE,CAOnC;AAED,wGAAwG;AACxG,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,GACrF,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,SAAS,sBAAsB,EAAE,CAAC,CAAC,CAKpE"}
1
+ {"version":3,"file":"palette-contrast.d.ts","sourceRoot":"","sources":["../src/palette-contrast.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAE3E,mEAAmE;AACnE,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACzC,UAAU,EAAE,MAAM,WAAW,CAAC;IAC9B,UAAU,EAAE,MAAM,WAAW,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;CAC3B,CAAC,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,mBAAmB,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAKvF,eAAO,MAAM,oBAAoB,EAAE,SAAS,mBAAmB,EAY9D,CAAC;AAaF,gEAAgE;AAChE,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAI5E;AAED,gGAAgG;AAChG,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,GAC7B,SAAS,sBAAsB,EAAE,CAOnC;AAED,wGAAwG;AACxG,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,GACrF,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,SAAS,sBAAsB,EAAE,CAAC,CAAC,CAKpE;AAED;;;;oFAIoF;AACpF,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,MAAM,CASnG"}
@@ -43,4 +43,21 @@ export function checkBrandPaletteContrast(brandPalette) {
43
43
  dark: checkPaletteContrast({ ...THEMES.dark, ...brandPalette.dark }),
44
44
  };
45
45
  }
46
+ /** Shared whole-surface fill adaptation. This palette-only entry keeps ordinary
47
+ * renderer imports independent of the optional preset registry/effect descriptors.
48
+ * The supported 0.85..1 interval keeps readable foregrounds outside the backdrop
49
+ * luminance interval; testing its black/white endpoints bounds any opaque backdrop.
50
+ * See docs/design-profile.md for why a reference card's low alpha is not copied. */
51
+ export function resolveSurfaceFillOpacity(requested, palette) {
52
+ if (!Number.isFinite(requested) || requested < 0.85 || requested > 1)
53
+ throw new RangeError("Surface fill opacity must be between 0.85 and 1");
54
+ const channels = [1, 3, 5].map(offset => Number.parseInt(palette.bg.slice(offset, offset + 2), 16));
55
+ const rules = paletteContrastRules.filter(rule => rule.background === "bg");
56
+ for (let opacity = requested; opacity < 1; opacity = Math.min(1, opacity + 0.01)) {
57
+ const endpoints = [0, 255].map(backdrop => "#" + channels.map(channel => Math.round(channel * opacity + backdrop * (1 - opacity)).toString(16).padStart(2, "0")).join(""));
58
+ if (endpoints.every(color => rules.every(rule => contrastRatio(palette[rule.foreground], color) >= rule.minimum)))
59
+ return opacity;
60
+ }
61
+ return 1;
62
+ }
46
63
  //# sourceMappingURL=palette-contrast.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"palette-contrast.js","sourceRoot":"","sources":["../src/palette-contrast.ts"],"names":[],"mappings":"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","sourcesContent":["import { THEMES, type ResolvedTheme, type ThemeColors } from \"./colors.js\";\n\n/** Pair thresholds and their rationale: docs/brand-boundary.md. */\nexport type PaletteContrastRule = Readonly<{\n foreground: keyof ThemeColors;\n background: keyof ThemeColors;\n minimum: number;\n kind: \"text\" | \"non-text\";\n}>;\n\nexport type PaletteContrastFinding = PaletteContrastRule & Readonly<{ ratio: number }>;\n\nconst readable = [\"text\", \"textBody\", \"textMuted\", \"textSub\", \"contentBrand\", \"danger\"] as const;\nconst surfaces = [\"bg\", \"surface\"] as const;\n\nexport const paletteContrastRules: readonly PaletteContrastRule[] = [\n ...readable.flatMap((foreground) =>\n surfaces.map((background) => ({ foreground, background, minimum: 4.5, kind: \"text\" as const })),\n ),\n { foreground: \"onPrimary\", background: \"primary\", minimum: 4.5, kind: \"text\" },\n { foreground: \"onDanger\", background: \"dangerFill\", minimum: 4.5, kind: \"text\" },\n ...([\"primary\", \"borderControl\"] as const).flatMap((foreground) =>\n surfaces.map((background) => ({ foreground, background, minimum: 3, kind: \"non-text\" as const })),\n ),\n // textWeak is the disabled/decorative/placeholder tier, exempt from text AA;\n // it also paints border.strong on canvas, which must stay a visible boundary.\n { foreground: \"textWeak\", background: \"bg\", minimum: 3, kind: \"non-text\" },\n];\n\nconst sixDigitHex = /^#[0-9a-f]{6}$/i;\n\nfunction luminance(hex: string): number {\n if (!sixDigitHex.test(hex)) throw new TypeError(`Expected a six-digit hex color, got ${hex}`);\n const [r, g, b] = [1, 3, 5].map((offset) => {\n const channel = Number.parseInt(hex.slice(offset, offset + 2), 16) / 255;\n return channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;\n }) as [number, number, number];\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\n/** WCAG 2.x contrast ratio between two six-digit hex colors. */\nexport function contrastRatio(foreground: string, background: string): number {\n const a = luminance(foreground);\n const b = luminance(background);\n return (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);\n}\n\n/** Pairs of a resolved theme palette that fall below their threshold; empty means it passes. */\nexport function checkPaletteContrast(\n palette: Readonly<ThemeColors>,\n): readonly PaletteContrastFinding[] {\n const findings: PaletteContrastFinding[] = [];\n for (const rule of paletteContrastRules) {\n const ratio = contrastRatio(palette[rule.foreground], palette[rule.background]);\n if (ratio < rule.minimum) findings.push({ ...rule, ratio: Math.round(ratio * 100) / 100 });\n }\n return findings;\n}\n\n/** Checks a `brandPalette` the way the Provider applies it: each theme merged over the HJM defaults. */\nexport function checkBrandPaletteContrast(\n brandPalette: Readonly<Partial<Record<ResolvedTheme, Readonly<Partial<ThemeColors>>>>>,\n): Readonly<Record<ResolvedTheme, readonly PaletteContrastFinding[]>> {\n return {\n light: checkPaletteContrast({ ...THEMES.light, ...brandPalette.light }),\n dark: checkPaletteContrast({ ...THEMES.dark, ...brandPalette.dark }),\n };\n}\n"]}
1
+ {"version":3,"file":"palette-contrast.js","sourceRoot":"","sources":["../src/palette-contrast.ts"],"names":[],"mappings":"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","sourcesContent":["import { THEMES, type ResolvedTheme, type ThemeColors } from \"./colors.js\";\n\n/** Pair thresholds and their rationale: docs/brand-boundary.md. */\nexport type PaletteContrastRule = Readonly<{\n foreground: keyof ThemeColors;\n background: keyof ThemeColors;\n minimum: number;\n kind: \"text\" | \"non-text\";\n}>;\n\nexport type PaletteContrastFinding = PaletteContrastRule & Readonly<{ ratio: number }>;\n\nconst readable = [\"text\", \"textBody\", \"textMuted\", \"textSub\", \"contentBrand\", \"danger\"] as const;\nconst surfaces = [\"bg\", \"surface\"] as const;\n\nexport const paletteContrastRules: readonly PaletteContrastRule[] = [\n ...readable.flatMap((foreground) =>\n surfaces.map((background) => ({ foreground, background, minimum: 4.5, kind: \"text\" as const })),\n ),\n { foreground: \"onPrimary\", background: \"primary\", minimum: 4.5, kind: \"text\" },\n { foreground: \"onDanger\", background: \"dangerFill\", minimum: 4.5, kind: \"text\" },\n ...([\"primary\", \"borderControl\"] as const).flatMap((foreground) =>\n surfaces.map((background) => ({ foreground, background, minimum: 3, kind: \"non-text\" as const })),\n ),\n // textWeak is the disabled/decorative/placeholder tier, exempt from text AA;\n // it also paints border.strong on canvas, which must stay a visible boundary.\n { foreground: \"textWeak\", background: \"bg\", minimum: 3, kind: \"non-text\" },\n];\n\nconst sixDigitHex = /^#[0-9a-f]{6}$/i;\n\nfunction luminance(hex: string): number {\n if (!sixDigitHex.test(hex)) throw new TypeError(`Expected a six-digit hex color, got ${hex}`);\n const [r, g, b] = [1, 3, 5].map((offset) => {\n const channel = Number.parseInt(hex.slice(offset, offset + 2), 16) / 255;\n return channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;\n }) as [number, number, number];\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\n/** WCAG 2.x contrast ratio between two six-digit hex colors. */\nexport function contrastRatio(foreground: string, background: string): number {\n const a = luminance(foreground);\n const b = luminance(background);\n return (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);\n}\n\n/** Pairs of a resolved theme palette that fall below their threshold; empty means it passes. */\nexport function checkPaletteContrast(\n palette: Readonly<ThemeColors>,\n): readonly PaletteContrastFinding[] {\n const findings: PaletteContrastFinding[] = [];\n for (const rule of paletteContrastRules) {\n const ratio = contrastRatio(palette[rule.foreground], palette[rule.background]);\n if (ratio < rule.minimum) findings.push({ ...rule, ratio: Math.round(ratio * 100) / 100 });\n }\n return findings;\n}\n\n/** Checks a `brandPalette` the way the Provider applies it: each theme merged over the HJM defaults. */\nexport function checkBrandPaletteContrast(\n brandPalette: Readonly<Partial<Record<ResolvedTheme, Readonly<Partial<ThemeColors>>>>>,\n): Readonly<Record<ResolvedTheme, readonly PaletteContrastFinding[]>> {\n return {\n light: checkPaletteContrast({ ...THEMES.light, ...brandPalette.light }),\n dark: checkPaletteContrast({ ...THEMES.dark, ...brandPalette.dark }),\n };\n}\n\n/** Shared whole-surface fill adaptation. This palette-only entry keeps ordinary\n * renderer imports independent of the optional preset registry/effect descriptors.\n * The supported 0.85..1 interval keeps readable foregrounds outside the backdrop\n * luminance interval; testing its black/white endpoints bounds any opaque backdrop.\n * See docs/design-profile.md for why a reference card's low alpha is not copied. */\nexport function resolveSurfaceFillOpacity(requested: number, palette: Readonly<ThemeColors>): number {\n if (!Number.isFinite(requested) || requested < 0.85 || requested > 1) throw new RangeError(\"Surface fill opacity must be between 0.85 and 1\");\n const channels = [1, 3, 5].map(offset => Number.parseInt(palette.bg.slice(offset, offset + 2), 16));\n const rules = paletteContrastRules.filter(rule => rule.background === \"bg\");\n for (let opacity = requested; opacity < 1; opacity = Math.min(1, opacity + 0.01)) {\n const endpoints = [0, 255].map(backdrop => \"#\" + channels.map(channel => Math.round(channel * opacity + backdrop * (1 - opacity)).toString(16).padStart(2, \"0\")).join(\"\"));\n if (endpoints.every(color => rules.every(rule => contrastRatio(palette[rule.foreground], color) >= rule.minimum))) return opacity;\n }\n return 1;\n}\n"]}
@@ -34,10 +34,24 @@ export type MessageComposerDescriptor = Readonly<{
34
34
  value: string;
35
35
  label: string;
36
36
  sendLabel: string;
37
+ /** Short visual copy may differ from the complete accessible input name. */
38
+ placeholder?: string;
39
+ description?: string;
40
+ error?: string;
41
+ invalid?: boolean;
42
+ /** Newline remains the compatible default; desktop chat may explicitly opt into send. */
43
+ submitMode?: "newline" | "send";
37
44
  disabled?: boolean;
38
45
  pending?: boolean;
39
46
  attachmentCount?: number;
40
47
  }>;
48
+ /** Safari may report the final IME Enter as keyCode 229 after isComposing becomes false. */
49
+ export declare function shouldSubmitMessageKey(event: Readonly<{
50
+ key: string;
51
+ shiftKey?: boolean;
52
+ isComposing?: boolean;
53
+ keyCode?: number;
54
+ }>, mode?: "newline" | "send"): boolean;
41
55
  export declare function canSubmitMessage({ value, disabled, pending, attachmentCount }: MessageComposerDescriptor): boolean;
42
56
  /** Bubble alignment expresses authorship, not delivery; delivery text always comes from the product receipt. */
43
57
  export type ChatMessageDescriptor = Readonly<{
@@ -61,7 +75,8 @@ export declare function validateCommentThread(items: readonly {
61
75
  /** Source bytes, permission requests, ordering and upload limits remain product-owned. */
62
76
  export type MessageAttachmentDescriptor = Readonly<{
63
77
  id: string;
64
- removeLabel: string;
78
+ removeLabel: string; /** Local preparation can lock removal without locking text entry. */
79
+ disabled?: boolean;
65
80
  }>;
66
81
  export declare function validateMessageAttachments(attachments: readonly MessageAttachmentDescriptor[]): void;
67
82
  /** Require horizontal intent so a timeline scroll cannot accidentally start a reply. */
@@ -1 +1 @@
1
- {"version":3,"file":"screen-patterns.d.ts","sourceRoot":"","sources":["../src/screen-patterns.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"screen-patterns.d.ts","sourceRoot":"","sources":["../src/screen-patterns.ts"],"names":[],"mappings":"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"}
@@ -34,6 +34,10 @@ export const screenPatternRecipe = {
34
34
  composerMinLines: 1,
35
35
  composerMaxLines: 5,
36
36
  };
37
+ /** Safari may report the final IME Enter as keyCode 229 after isComposing becomes false. */
38
+ export function shouldSubmitMessageKey(event, mode = "newline") {
39
+ return mode === "send" && event.key === "Enter" && !event.shiftKey && !event.isComposing && event.keyCode !== 229;
40
+ }
37
41
  export function canSubmitMessage({ value, disabled = false, pending = false, attachmentCount = 0 }) {
38
42
  if (!Number.isSafeInteger(attachmentCount) || attachmentCount < 0)
39
43
  throw new RangeError("Attachment count must be a nonnegative integer");
@@ -1 +1 @@
1
- {"version":3,"file":"screen-patterns.js","sourceRoot":"","sources":["../src/screen-patterns.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAOnD,MAAM,UAAU,yBAAyB,CAAC,QAA4B,EAAE,IAAI,EAAE,OAAO,EAAE;IACrF,IAAI,CAAC,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/E,MAAM,IAAI,SAAS,CAAC,8BAA8B,CAAC,CAAC;IACtD,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QAClD,MAAM,IAAI,SAAS,CAAC,yCAAyC,CAAC,CAAC;IACjE,CAAC;IACD,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,eAAe,EAAE,KAAK,CAAC,IAAI,KAAK,OAAO;QACvC,IAAI,EAAE,KAAK,CAAC,IAAI,KAAK,SAAS;QAC9B,YAAY,EAAE,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ;KACrD,CAAC;AACb,CAAC;AAED,qGAAqG;AACrG,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAC,eAAe;IAChC,OAAO,EAAE,OAAO,CAAC,EAAE;IACnB,UAAU,EAAE,OAAO,CAAC,EAAE;IACtB,OAAO,EAAE,OAAO,CAAC,EAAE;IACnB,QAAQ,EAAE,OAAO,CAAC,EAAE;IACpB,oIAAoI;IACpI,cAAc,EAAE,MAAM,CAAC,eAAe,GAAG,CAAC;IAC1C,4FAA4F;IAC5F,eAAe,EAAE,KAAK;IACtB,wFAAwF;IACxF,cAAc,EAAE,EAAE;IAClB,oBAAoB,EAAE,EAAE;IACxB,cAAc,EAAE,GAAG;IACnB,qBAAqB,EAAE,EAAE;IACzB,iBAAiB,EAAE,GAAG;IACtB,kBAAkB,EAAE,EAAE;IACtB,gBAAgB,EAAE,CAAC;IACnB,gBAAgB,EAAE,CAAC;CACX,CAAC;AAWX,MAAM,UAAU,gBAAgB,CAAC,EAAE,KAAK,EAAE,QAAQ,GAAG,KAAK,EAAE,OAAO,GAAG,KAAK,EAAE,eAAe,GAAG,CAAC,EAA6B;IAC3H,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,eAAe,CAAC,IAAI,eAAe,GAAG,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,gDAAgD,CAAC,CAAC;IAC1I,OAAO,CAAC,QAAQ,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,eAAe,GAAG,CAAC,CAAC,CAAC;AACnF,CAAC;AAYD,MAAM,UAAU,uBAAuB,CAAC,MAA6B;IACnE,QAAO,MAAM,EAAC,CAAC;QAAA,KAAK,QAAQ,CAAC,CAAA,OAAO,SAAS,CAAC;QAAA,KAAK,QAAQ,CAAC,CAAA,OAAO,UAAU,CAAC;QAAA,KAAK,SAAS,CAAC,CAAA,OAAO,UAAU,CAAC;QAAA,KAAK,aAAa,CAAC,CAAA,OAAO,IAAI,CAAC;QAAA,OAAO,CAAC,CAAA,MAAM,IAAI,SAAS,CAAC,0BAA0B,CAAC,CAAC;IAAA,CAAC;AACzM,CAAC;AACD,MAAM,UAAU,qBAAqB,CAAC,KAAY,EAAC,KAAY;IAC9D,IAAG,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAE,KAAK,GAAC,CAAC,IAAE,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAE,KAAK,GAAC,CAAC,IAAE,KAAK,IAAE,KAAK;QAAC,MAAM,IAAI,UAAU,CAAC,wCAAwC,CAAC,CAAC;IACrJ,OAAO,EAAC,KAAK,EAAC,KAAK,KAAG,CAAC,EAAC,IAAI,EAAC,KAAK,KAAG,KAAK,GAAC,CAAC,EAAC,CAAC;AAC/C,CAAC;AAED,qGAAqG;AACrG,MAAM,UAAU,qBAAqB,CAAC,KAAiD;IACtF,MAAM,KAAK,GAAC,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAA,EAAE,CAAA,IAAI,CAAC,QAAQ,KAAG,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAA,EAAE,CAAA,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAAA,MAAM,GAAG,GAAC,IAAI,GAAG,EAAU,CAAC;IAC7G,KAAI,MAAM,IAAI,IAAI,KAAK,EAAC,CAAC;QAAA,IAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,IAAE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAAC,MAAM,IAAI,SAAS,CAAC,yCAAyC,CAAC,CAAC;QAAA,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAAA,IAAG,IAAI,CAAC,QAAQ,KAAG,IAAI,IAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAE,IAAI,CAAC,QAAQ,KAAG,IAAI,CAAC,EAAE,CAAC;YAAC,MAAM,IAAI,SAAS,CAAC,0CAA0C,CAAC,CAAC;IAAA,CAAC;AAC9R,CAAC;AAID,MAAM,UAAU,0BAA0B,CAAC,WAAmD;IAC5F,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE;YAAE,MAAM,IAAI,SAAS,CAAC,6DAA6D,CAAC,CAAC;QACxJ,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnB,CAAC;AACH,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,YAAY,CAAC,EAAU,EAAE,EAAU;IACjD,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,mBAAmB,CAAC,kBAAkB,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;AACnG,CAAC;AAUD,oGAAoG;AACpG,MAAM,UAAU,iBAAiB,CAAsB,KAAkB,EAAC,WAAsC,EAAC,YAAkC,EAAC,cAAoC;IACtL,MAAM,GAAG,GAAC,IAAI,GAAG,EAAU,EAAC,MAAM,GAAC,IAAI,GAAG,EAAU,CAAC;IACrD,KAAI,MAAM,IAAI,IAAI,KAAK,EAAC,CAAC;QAAA,IAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,IAAE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;YAAC,MAAM,IAAI,SAAS,CAAC,oDAAoD,CAAC,CAAC;QAAA,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAAA,CAAC;IAC9K,KAAI,MAAM,KAAK,IAAI,WAAW,EAAC,CAAC;QAAA,IAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,IAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE;YAAC,MAAM,IAAI,SAAS,CAAC,0DAA0D,CAAC,CAAC;QAAA,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAAA,CAAC;IACrM,MAAM,UAAU,GAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAA,EAAE,CAAA,IAAI,CAAC,EAAE,KAAG,YAAY,CAAC,CAAC;IAChE,+FAA+F;IAC/F,MAAM,IAAI,GAAC,YAAY,KAAG,SAAS,IAAE,CAAC,YAAY,KAAG,IAAI,IAAE,CAAC,UAAU,CAAC,CAAC;IACxE,MAAM,OAAO,GAAC,IAAI,IAAE,YAAY,KAAG,IAAI,CAAA,CAAC,CAAA,KAAK,CAAA,CAAC,CAAA,KAAK,CAAC,MAAM,CAAC,IAAI,CAAA,EAAE,CAAA,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IACxG,OAAO,EAAC,IAAI,EAAC,UAAU,EAAC,OAAO,EAAC,QAAQ,EAAC,IAAI,CAAA,CAAC,CAAA,SAAS,CAAA,CAAC,CAAA,OAAO,CAAC,IAAI,CAAC,IAAI,CAAA,EAAE,CAAA,IAAI,CAAC,EAAE,KAAG,cAAc,CAAC,EAAC,CAAC;AACxG,CAAC;AAUD,MAAM,UAAU,wBAAwB,CAAC,KAAa,EAAE,cAAuB;IAC7E,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,CAAC,KAAK;QAAE,OAAO,MAAM,CAAC;IAC1B,IAAI,cAAc,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACnD,OAAO,KAAK,KAAK,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;AAChE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC1B,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5B,CAAC;AASD,MAAM,UAAU,uBAAuB,CAAC,KAAoB,EAAE,kBAA0B;IACtF,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC;IAC/C,IAAI,KAAK,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,kBAAkB,CAAC,IAAI,kBAAkB,GAAG,CAAC,EAAE,CAAC;QACrH,MAAM,IAAI,UAAU,CAAC,4CAA4C,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,kBAAkB,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;AACtD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,YAAoB,EAAE,SAAiB;IAC9E,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,YAAY,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QACzG,MAAM,IAAI,UAAU,CAAC,8CAA8C,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC;AACtE,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,8GAA8G;IAC9G,aAAa,EAAE,CAAC;IAChB,kDAAkD;IAClD,iBAAiB,EAAE,CAAC;IACpB,2DAA2D;IAC3D,WAAW,EAAE,CAAC;IACd,iDAAiD;IACjD,UAAU,EAAE,OAAO,CAAC,EAAE;IACtB,wEAAwE;IACxE,SAAS,EAAE,OAAO,CAAC,EAAE;IACrB,8DAA8D;IAC9D,OAAO,EAAE,OAAO,CAAC,EAAE;CACX,CAAC","sourcesContent":["import { layout, spacing } from \"./foundations.js\";\n\n/** Initial load replaces the body; refresh/save errors belong beside retained content. */\nexport type ScreenContentState =\n | Readonly<{ kind: \"ready\" }>\n | Readonly<{ kind: \"loading\" | \"empty\" | \"error\" | \"restricted\"; title: string; description?: string }>;\n\nexport function resolveScreenContentState(state: ScreenContentState = { kind: \"ready\" }) {\n if (![\"ready\", \"loading\", \"empty\", \"error\", \"restricted\"].includes(state.kind)) {\n throw new TypeError(\"Unknown screen content state\");\n }\n if (state.kind !== \"ready\" && !state.title.trim()) {\n throw new TypeError(\"Screen state requires a localized title\");\n }\n return {\n kind: state.kind,\n replacesContent: state.kind !== \"ready\",\n busy: state.kind === \"loading\",\n announcement: state.kind === \"error\" ? \"assertive\" : \"polite\",\n } as const;\n}\n\n// Reuse reading-width and spacing tokens; do not derive geometry from a product's device or artwork.\nexport const screenPatternRecipe = {\n maxWidth: layout.readingMaxWidth,\n padding: spacing.md,\n sectionGap: spacing.xl,\n itemGap: spacing.sm,\n stateGap: spacing.md,\n // Compact screens must keep back/title/save on one row; scale this floor for large text instead of reserving a desktop-width title.\n headerMinWidth: layout.readingMaxWidth / 6,\n // Reserve the opposite edge for authorship and cap the composer so history remains visible.\n messageMaxWidth: \"84%\",\n // Social thumbnails stay compact; selection order and removal replace a second toolbar.\n attachmentSize: 80,\n attachmentRemoveSize: 24,\n reactionHoldMs: 450,\n reactionMoveTolerance: 10,\n reactionMenuWidth: 320,\n replySwipeDistance: 60,\n composerMinLines: 1,\n composerMaxLines: 5,\n} as const;\n\n/** Controlled composer: HJM never clears drafts or interprets a fulfilled callback as server delivery. */\nexport type MessageComposerDescriptor = Readonly<{\n value: string;\n label: string;\n sendLabel: string;\n disabled?: boolean;\n pending?: boolean;\n attachmentCount?: number;\n}>;\nexport function canSubmitMessage({ value, disabled = false, pending = false, attachmentCount = 0 }: MessageComposerDescriptor): boolean {\n if (!Number.isSafeInteger(attachmentCount) || attachmentCount < 0) throw new RangeError(\"Attachment count must be a nonnegative integer\");\n return !disabled && !pending && (value.trim().length > 0 || attachmentCount > 0);\n}\n\n/** Bubble alignment expresses authorship, not delivery; delivery text always comes from the product receipt. */\nexport type ChatMessageDescriptor = Readonly<{\n direction: \"incoming\" | \"outgoing\";\n author: string;\n timestamp: string;\n deliveryLabel?: string;\n}>;\n\n/** A denied permission goes to settings; rendering never requests OS access automatically. */\nexport type PermissionScreenStatus = \"prompt\" | \"denied\" | \"granted\" | \"unavailable\";\nexport function resolvePermissionAction(status:PermissionScreenStatus) {\n switch(status){case \"prompt\":return \"request\";case \"denied\":return \"settings\";case \"granted\":return \"continue\";case \"unavailable\":return null;default:throw new TypeError(\"Unknown permission state\");}\n}\nexport function resolveOnboardingStep(total:number,index:number){\n if(!Number.isInteger(total)||total<1||!Number.isInteger(index)||index<0||index>=total)throw new RangeError(\"Onboarding requires a valid step index\");\n return {first:index===0,last:index===total-1};\n}\n\n/** Replies are one level deep; reject orphaned/duplicate records instead of silently losing them. */\nexport function validateCommentThread(items:readonly {id:string;parentId:string|null}[]){\n const roots=new Set(items.filter(item=>item.parentId===null).map(item=>item.id));const ids=new Set<string>();\n for(const item of items){if(!item.id.trim()||ids.has(item.id))throw new TypeError(\"Comment ids must be unique and nonempty\");ids.add(item.id);if(item.parentId!==null&&(!roots.has(item.parentId)||item.parentId===item.id))throw new TypeError(\"Replies require an existing root comment\");}\n}\n\n/** Source bytes, permission requests, ordering and upload limits remain product-owned. */\nexport type MessageAttachmentDescriptor = Readonly<{ id: string; removeLabel: string }>;\nexport function validateMessageAttachments(attachments: readonly MessageAttachmentDescriptor[]): void {\n const ids = new Set<string>();\n for (const item of attachments) {\n if (!item.id.trim() || ids.has(item.id) || !item.removeLabel.trim()) throw new TypeError(\"Attachments require unique ids and localized removal labels\");\n ids.add(item.id);\n }\n}\n\n/** Require horizontal intent so a timeline scroll cannot accidentally start a reply. */\nexport function isReplySwipe(dx: number, dy: number): boolean {\n return Math.abs(dx) >= screenPatternRecipe.replySwipeDistance && Math.abs(dx) > Math.abs(dy) * 2;\n}\n\n/** The UI never infers permission or camera availability; product adapters own both. */\nexport type PhotoSource = \"library\" | \"camera\";\nexport type PhotoSourceLabels = Readonly<{ title:string; library:string; camera:string; cancel:string }>;\n\nexport type SavedItem = Readonly<{ id: string; title: string }>;\nexport type SavedCollection = Readonly<{ id: string; title: string; itemIds: readonly string[] }>;\nexport type SavedItemsLabels = Readonly<{ allItems: string; privateNotice: string; back: string; empty: string; createCollection: string }>;\n\n/** Unsave removes an item from every collection view without rewriting product-owned membership. */\nexport function resolveSavedItems<T extends SavedItem>(items:readonly T[],collections:readonly SavedCollection[],collectionId:string|null|undefined,selectedItemId:string|null|undefined){\n const ids=new Set<string>(),groups=new Set<string>();\n for(const item of items){if(!item.id.trim()||ids.has(item.id)||!item.title.trim())throw new TypeError(\"Saved items require unique ids and nonempty titles\");ids.add(item.id);}\n for(const group of collections){if(!group.id.trim()||groups.has(group.id)||!group.title.trim())throw new TypeError(\"Saved collections require unique ids and nonempty titles\");groups.add(group.id);}\n const collection=collections.find(item=>item.id===collectionId);\n // A deleted collection returns to the overview instead of showing a mislabeled all-posts view.\n const home=collectionId===undefined||(collectionId!==null&&!collection);\n const visible=home||collectionId===null?items:items.filter(item=>collection?.itemIds.includes(item.id));\n return {home,collection,visible,selected:home?undefined:visible.find(item=>item.id===selectedItemId)};\n}\n\n/**\n * SearchScreen phases (2026-10-06 search redesign, docs/screen-patterns.md#searchscreen-두-단계-검색).\n * Without a committed query (one-step search, the pre-1.13 behavior) any nonblank query is already\n * `results`. With one, typing a different query is `typing`: suggestions replace the results until the\n * user commits (Enter, a suggestion, a recent or suggested query). Comparing trimmed values keeps a\n * trailing space from flipping committed results back into suggestions.\n */\nexport type SearchScreenPhase = \"idle\" | \"typing\" | \"results\";\nexport function resolveSearchScreenPhase(query: string, committedQuery?: string): SearchScreenPhase {\n const typed = query.trim();\n if (!typed) return \"idle\";\n if (committedQuery === undefined) return \"results\";\n return typed === committedQuery.trim() ? \"results\" : \"typing\";\n}\n\n/**\n * The value a commit records, or `null` when there is nothing to commit. Blank commits are dropped\n * so a stray Enter never writes an empty recent search or fetches \"everything\".\n */\nexport function resolveSearchCommit(value: string): string | null {\n const next = value.trim();\n return next ? next : null;\n}\n\n/**\n * Why a committed search shows nothing. `filters` keeps the filter rail and offers \"clear all\";\n * `query` hides the rail (no filter can fix it) and offers suggested queries instead. Reference\n * services hide the rail on every zero result, which removes the only way to undo a filter.\n * `null` count means the result is still loading, so no cause is reported yet.\n */\nexport type SearchEmptyCause = \"none\" | \"filters\" | \"query\";\nexport function resolveSearchEmptyCause(count: number | null, appliedFilterCount: number): SearchEmptyCause {\n if (count === null || count > 0) return \"none\";\n if (count < 0 || !Number.isSafeInteger(count) || !Number.isSafeInteger(appliedFilterCount) || appliedFilterCount < 0) {\n throw new RangeError(\"Search counts must be nonnegative integers\");\n }\n return appliedFilterCount > 0 ? \"filters\" : \"query\";\n}\n\n/**\n * Index to focus after removing item `removedIndex` from a list that now has `remaining` items:\n * the item that slid into its place, else the new last item, else -1 (move to the fallback target).\n * Removing a focused chip or row would otherwise drop focus to <body>.\n */\nexport function resolveFocusAfterRemoval(removedIndex: number, remaining: number): number {\n if (!Number.isInteger(removedIndex) || removedIndex < 0 || !Number.isInteger(remaining) || remaining < 0) {\n throw new RangeError(\"Removal indexes must be nonnegative integers\");\n }\n return remaining === 0 ? -1 : Math.min(removedIndex, remaining - 1);\n}\n\n/** Recipe for the SearchScreen phase sections; geometry stays on shared spacing tokens. */\nexport const searchScreenRecipe = {\n /** Recent searches shown before the user types; reference services show 5–10 and let the rest scroll away. */\n recentVisible: 5,\n /** Suggestions under the \"search for ‘q’\" row. */\n suggestionVisible: 6,\n /** Skeleton rows while the first page of results loads. */\n loadingRows: 4,\n /** Between idle sections (recent, suggested). */\n sectionGap: spacing.xl,\n /** Section title to its list, and results header to applied filters. */\n headerGap: spacing.sm,\n /** Between chips in applied filters and suggested queries. */\n chipGap: spacing.xs,\n} as const;\n"]}
1
+ {"version":3,"file":"screen-patterns.js","sourceRoot":"","sources":["../src/screen-patterns.ts"],"names":[],"mappings":"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","sourcesContent":["import { layout, spacing } from \"./foundations.js\";\n\n/** Initial load replaces the body; refresh/save errors belong beside retained content. */\nexport type ScreenContentState =\n | Readonly<{ kind: \"ready\" }>\n | Readonly<{ kind: \"loading\" | \"empty\" | \"error\" | \"restricted\"; title: string; description?: string }>;\n\nexport function resolveScreenContentState(state: ScreenContentState = { kind: \"ready\" }) {\n if (![\"ready\", \"loading\", \"empty\", \"error\", \"restricted\"].includes(state.kind)) {\n throw new TypeError(\"Unknown screen content state\");\n }\n if (state.kind !== \"ready\" && !state.title.trim()) {\n throw new TypeError(\"Screen state requires a localized title\");\n }\n return {\n kind: state.kind,\n replacesContent: state.kind !== \"ready\",\n busy: state.kind === \"loading\",\n announcement: state.kind === \"error\" ? \"assertive\" : \"polite\",\n } as const;\n}\n\n// Reuse reading-width and spacing tokens; do not derive geometry from a product's device or artwork.\nexport const screenPatternRecipe = {\n maxWidth: layout.readingMaxWidth,\n padding: spacing.md,\n sectionGap: spacing.xl,\n itemGap: spacing.sm,\n stateGap: spacing.md,\n // Compact screens must keep back/title/save on one row; scale this floor for large text instead of reserving a desktop-width title.\n headerMinWidth: layout.readingMaxWidth / 6,\n // Reserve the opposite edge for authorship and cap the composer so history remains visible.\n messageMaxWidth: \"84%\",\n // Social thumbnails stay compact; selection order and removal replace a second toolbar.\n attachmentSize: 80,\n attachmentRemoveSize: 24,\n reactionHoldMs: 450,\n reactionMoveTolerance: 10,\n reactionMenuWidth: 320,\n replySwipeDistance: 60,\n composerMinLines: 1,\n composerMaxLines: 5,\n} as const;\n\n/** Controlled composer: HJM never clears drafts or interprets a fulfilled callback as server delivery. */\nexport type MessageComposerDescriptor = Readonly<{\n value: string;\n label: string;\n sendLabel: string;\n /** Short visual copy may differ from the complete accessible input name. */\n placeholder?: string;\n description?: string;\n error?: string;\n invalid?: boolean;\n /** Newline remains the compatible default; desktop chat may explicitly opt into send. */\n submitMode?: \"newline\" | \"send\";\n disabled?: boolean;\n pending?: boolean;\n attachmentCount?: number;\n}>;\n/** Safari may report the final IME Enter as keyCode 229 after isComposing becomes false. */\nexport function shouldSubmitMessageKey(event: Readonly<{ key: string; shiftKey?: boolean; isComposing?: boolean; keyCode?: number }>, mode: \"newline\" | \"send\" = \"newline\"): boolean {\n return mode === \"send\" && event.key === \"Enter\" && !event.shiftKey && !event.isComposing && event.keyCode !== 229;\n}\nexport function canSubmitMessage({ value, disabled = false, pending = false, attachmentCount = 0 }: MessageComposerDescriptor): boolean {\n if (!Number.isSafeInteger(attachmentCount) || attachmentCount < 0) throw new RangeError(\"Attachment count must be a nonnegative integer\");\n return !disabled && !pending && (value.trim().length > 0 || attachmentCount > 0);\n}\n\n/** Bubble alignment expresses authorship, not delivery; delivery text always comes from the product receipt. */\nexport type ChatMessageDescriptor = Readonly<{\n direction: \"incoming\" | \"outgoing\";\n author: string;\n timestamp: string;\n deliveryLabel?: string;\n}>;\n\n/** A denied permission goes to settings; rendering never requests OS access automatically. */\nexport type PermissionScreenStatus = \"prompt\" | \"denied\" | \"granted\" | \"unavailable\";\nexport function resolvePermissionAction(status:PermissionScreenStatus) {\n switch(status){case \"prompt\":return \"request\";case \"denied\":return \"settings\";case \"granted\":return \"continue\";case \"unavailable\":return null;default:throw new TypeError(\"Unknown permission state\");}\n}\nexport function resolveOnboardingStep(total:number,index:number){\n if(!Number.isInteger(total)||total<1||!Number.isInteger(index)||index<0||index>=total)throw new RangeError(\"Onboarding requires a valid step index\");\n return {first:index===0,last:index===total-1};\n}\n\n/** Replies are one level deep; reject orphaned/duplicate records instead of silently losing them. */\nexport function validateCommentThread(items:readonly {id:string;parentId:string|null}[]){\n const roots=new Set(items.filter(item=>item.parentId===null).map(item=>item.id));const ids=new Set<string>();\n for(const item of items){if(!item.id.trim()||ids.has(item.id))throw new TypeError(\"Comment ids must be unique and nonempty\");ids.add(item.id);if(item.parentId!==null&&(!roots.has(item.parentId)||item.parentId===item.id))throw new TypeError(\"Replies require an existing root comment\");}\n}\n\n/** Source bytes, permission requests, ordering and upload limits remain product-owned. */\nexport type MessageAttachmentDescriptor = Readonly<{ id: string; removeLabel: string; /** Local preparation can lock removal without locking text entry. */ disabled?: boolean }>;\nexport function validateMessageAttachments(attachments: readonly MessageAttachmentDescriptor[]): void {\n const ids = new Set<string>();\n for (const item of attachments) {\n if (!item.id.trim() || ids.has(item.id) || !item.removeLabel.trim()) throw new TypeError(\"Attachments require unique ids and localized removal labels\");\n ids.add(item.id);\n }\n}\n\n/** Require horizontal intent so a timeline scroll cannot accidentally start a reply. */\nexport function isReplySwipe(dx: number, dy: number): boolean {\n return Math.abs(dx) >= screenPatternRecipe.replySwipeDistance && Math.abs(dx) > Math.abs(dy) * 2;\n}\n\n/** The UI never infers permission or camera availability; product adapters own both. */\nexport type PhotoSource = \"library\" | \"camera\";\nexport type PhotoSourceLabels = Readonly<{ title:string; library:string; camera:string; cancel:string }>;\n\nexport type SavedItem = Readonly<{ id: string; title: string }>;\nexport type SavedCollection = Readonly<{ id: string; title: string; itemIds: readonly string[] }>;\nexport type SavedItemsLabels = Readonly<{ allItems: string; privateNotice: string; back: string; empty: string; createCollection: string }>;\n\n/** Unsave removes an item from every collection view without rewriting product-owned membership. */\nexport function resolveSavedItems<T extends SavedItem>(items:readonly T[],collections:readonly SavedCollection[],collectionId:string|null|undefined,selectedItemId:string|null|undefined){\n const ids=new Set<string>(),groups=new Set<string>();\n for(const item of items){if(!item.id.trim()||ids.has(item.id)||!item.title.trim())throw new TypeError(\"Saved items require unique ids and nonempty titles\");ids.add(item.id);}\n for(const group of collections){if(!group.id.trim()||groups.has(group.id)||!group.title.trim())throw new TypeError(\"Saved collections require unique ids and nonempty titles\");groups.add(group.id);}\n const collection=collections.find(item=>item.id===collectionId);\n // A deleted collection returns to the overview instead of showing a mislabeled all-posts view.\n const home=collectionId===undefined||(collectionId!==null&&!collection);\n const visible=home||collectionId===null?items:items.filter(item=>collection?.itemIds.includes(item.id));\n return {home,collection,visible,selected:home?undefined:visible.find(item=>item.id===selectedItemId)};\n}\n\n/**\n * SearchScreen phases (2026-10-06 search redesign, docs/screen-patterns.md#searchscreen-두-단계-검색).\n * Without a committed query (one-step search, the pre-1.13 behavior) any nonblank query is already\n * `results`. With one, typing a different query is `typing`: suggestions replace the results until the\n * user commits (Enter, a suggestion, a recent or suggested query). Comparing trimmed values keeps a\n * trailing space from flipping committed results back into suggestions.\n */\nexport type SearchScreenPhase = \"idle\" | \"typing\" | \"results\";\nexport function resolveSearchScreenPhase(query: string, committedQuery?: string): SearchScreenPhase {\n const typed = query.trim();\n if (!typed) return \"idle\";\n if (committedQuery === undefined) return \"results\";\n return typed === committedQuery.trim() ? \"results\" : \"typing\";\n}\n\n/**\n * The value a commit records, or `null` when there is nothing to commit. Blank commits are dropped\n * so a stray Enter never writes an empty recent search or fetches \"everything\".\n */\nexport function resolveSearchCommit(value: string): string | null {\n const next = value.trim();\n return next ? next : null;\n}\n\n/**\n * Why a committed search shows nothing. `filters` keeps the filter rail and offers \"clear all\";\n * `query` hides the rail (no filter can fix it) and offers suggested queries instead. Reference\n * services hide the rail on every zero result, which removes the only way to undo a filter.\n * `null` count means the result is still loading, so no cause is reported yet.\n */\nexport type SearchEmptyCause = \"none\" | \"filters\" | \"query\";\nexport function resolveSearchEmptyCause(count: number | null, appliedFilterCount: number): SearchEmptyCause {\n if (count === null || count > 0) return \"none\";\n if (count < 0 || !Number.isSafeInteger(count) || !Number.isSafeInteger(appliedFilterCount) || appliedFilterCount < 0) {\n throw new RangeError(\"Search counts must be nonnegative integers\");\n }\n return appliedFilterCount > 0 ? \"filters\" : \"query\";\n}\n\n/**\n * Index to focus after removing item `removedIndex` from a list that now has `remaining` items:\n * the item that slid into its place, else the new last item, else -1 (move to the fallback target).\n * Removing a focused chip or row would otherwise drop focus to <body>.\n */\nexport function resolveFocusAfterRemoval(removedIndex: number, remaining: number): number {\n if (!Number.isInteger(removedIndex) || removedIndex < 0 || !Number.isInteger(remaining) || remaining < 0) {\n throw new RangeError(\"Removal indexes must be nonnegative integers\");\n }\n return remaining === 0 ? -1 : Math.min(removedIndex, remaining - 1);\n}\n\n/** Recipe for the SearchScreen phase sections; geometry stays on shared spacing tokens. */\nexport const searchScreenRecipe = {\n /** Recent searches shown before the user types; reference services show 5–10 and let the rest scroll away. */\n recentVisible: 5,\n /** Suggestions under the \"search for ‘q’\" row. */\n suggestionVisible: 6,\n /** Skeleton rows while the first page of results loads. */\n loadingRows: 4,\n /** Between idle sections (recent, suggested). */\n sectionGap: spacing.xl,\n /** Section title to its list, and results header to applied filters. */\n headerGap: spacing.sm,\n /** Between chips in applied filters and suggested queries. */\n chipGap: spacing.xs,\n} as const;\n"]}
@@ -101,7 +101,9 @@ export declare function resolveLiquidToastLayout(input: Readonly<{
101
101
  y: number;
102
102
  }>;
103
103
  }>): LiquidToastLayout;
104
- export declare function buildLiquidToastGeometry(drop: number, expand: number, layout: LiquidToastLayout): {
104
+ /** A themed corner changes only the settled card, never the circular origin.
105
+ * Keep the default recipe for existing callers and clamp to measured paint bounds. */
106
+ export declare function buildLiquidToastGeometry(drop: number, expand: number, layout: LiquidToastLayout, cardRadius?: number): {
105
107
  x: number;
106
108
  y: number;
107
109
  width: number;
@@ -1 +1 @@
1
- {"version":3,"file":"toast-liquid.d.ts","sourceRoot":"","sources":["../src/toast-liquid.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BpB,CAAC;AAEX,4FAA4F;AAC5F,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC,GAAG,QAAQ,CAAC;IACvE,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,QAAQ,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC1E,CAAC,CAAC;AAEH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAOzE;AAED,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACvC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IACpF,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAC5E,SAAS,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;CACxD,CAAC,CAAC;AAEH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,QAAQ,CAAC;IACvD,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAC;IAClF,YAAY,CAAC,EAAE,QAAQ,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnD,CAAC,GAAG,iBAAiB,CA2BrB;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB;;;;;;;;;;;EAyB/F"}
1
+ {"version":3,"file":"toast-liquid.d.ts","sourceRoot":"","sources":["../src/toast-liquid.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BpB,CAAC;AAEX,4FAA4F;AAC5F,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC,GAAG,QAAQ,CAAC;IACvE,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,QAAQ,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC1E,CAAC,CAAC;AAEH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAOzE;AAED,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACvC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IACpF,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAC5E,SAAS,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;CACxD,CAAC,CAAC;AAEH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,QAAQ,CAAC;IACvD,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAC;IAClF,YAAY,CAAC,EAAE,QAAQ,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnD,CAAC,GAAG,iBAAiB,CA2BrB;AAED;sFACsF;AACtF,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,UAAU,GAAE,MAAiC;;;;;;;;;;;EA0B9I"}
@@ -75,8 +75,12 @@ export function resolveLiquidToastLayout(input) {
75
75
  fits: width >= 240 && height > 0 && cardTop + height <= availableHeight,
76
76
  };
77
77
  }
78
- export function buildLiquidToastGeometry(drop, expand, layout) {
78
+ /** A themed corner changes only the settled card, never the circular origin.
79
+ * Keep the default recipe for existing callers and clamp to measured paint bounds. */
80
+ export function buildLiquidToastGeometry(drop, expand, layout, cardRadius = liquidToastRecipe.radius) {
79
81
  "worklet";
82
+ if (!Number.isFinite(cardRadius) || cardRadius < 0)
83
+ throw new RangeError("Liquid Toast card radius must be finite and non-negative");
80
84
  const clamp = (n) => Math.max(0, Math.min(1, n));
81
85
  const mix = (a, b, t) => a + (b - a) * t;
82
86
  const grow = 1 - (1 - clamp(drop / 0.7)) ** 1.25;
@@ -95,7 +99,7 @@ export function buildLiquidToastGeometry(drop, expand, layout) {
95
99
  const neckY = layout.anchorY + layout.anchorHeight / 2;
96
100
  return {
97
101
  x: centerX - width / 2, y: centerY - height / 2, width, height,
98
- radius: Math.max(0, Math.min(mix(droplet / 2, liquidToastRecipe.radius, expand), width / 2, height / 2)),
102
+ radius: Math.max(0, Math.min(mix(droplet / 2, cardRadius, expand), width / 2, height / 2)),
99
103
  neckX: layout.anchorX + layout.anchorWidth / 2 - neckWidth / 2,
100
104
  neckY, neckWidth, neckHeight: Math.max(0, centerY - neckY),
101
105
  offsetY: centerY - (layout.cardTop + layout.height / 2),
@@ -1 +1 @@
1
- {"version":3,"file":"toast-liquid.js","sourceRoot":"","sources":["../src/toast-liquid.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Geometry adapted from rit3zh/expo-dynamic-notifications, commit 5de059a5cbef.\n * The MIT notice is shipped in THIRD_PARTY_NOTICES.md. See docs/plans/liquid-toast.md.\n * Kept separate from the lifecycle: animation must never own a second queue.\n */\nexport const liquidToastRecipe = {\n // 2026-10-02: use a circular in-app origin rather than a second Dynamic Island.\n // Keep the public capsule key; changing geometry does not require callers to migrate.\n capsule: { width: 32, height: 32 },\n // Preserve the settled card top (58) when increasing the anchor height from 24 to 32.\n gap: 26,\n minHeight: 74,\n maxWidth: 396,\n // 2026-10-02 card redesign: the medium 12-unit corner supports a banner silhouette.\n radius: 12,\n dropSize: 52,\n neckWidth: 60,\n blur: 14.3,\n gain: 22,\n threshold: 0.43,\n // These intentionally exceed general HJM motion: the neck needs time to separate.\n // Original choreography is the comparison baseline, not a new global motion scale.\n spring: {\n drop: { duration: 1150, dampingRatio: 0.82 },\n expand: { duration: 1000, dampingRatio: 0.8 },\n reveal: { duration: 700, dampingRatio: 1 },\n tint: { duration: 700, dampingRatio: 1 },\n collapse: { duration: 660, dampingRatio: 0.92, velocity: 2 },\n return: { duration: 1150, dampingRatio: 0.9 },\n fade: { duration: 360, dampingRatio: 1 },\n drag: { duration: 560, dampingRatio: 0.7 },\n },\n // Keep the drop anchor-colored through descent; early tint looked like a gray blob on iPhone 17 (2026-09-30).\n delay: { tint: 420, expand: 340, reveal: 560, collapse: 100, return: 280 },\n swipe: { distance: -18, velocity: -420 },\n} as const;\n\n/** Island frames are explicitly verified window coordinates, never inferred from insets. */\nexport type LiquidToastAnchor = Readonly<{ kind: \"capsule\" }> | Readonly<{\n kind: \"island\";\n frame: Readonly<{ x: number; y: number; width: number; height: number }>;\n}>;\n\nexport function validateLiquidToastAnchor(anchor: LiquidToastAnchor): void {\n if (anchor.kind === \"capsule\") return;\n if (anchor.kind !== \"island\") throw new TypeError(\"Unsupported liquid toast anchor\");\n const frame = anchor.frame;\n if (!frame || ![frame.x, frame.y, frame.width, frame.height].every(Number.isFinite) || frame.x < 0 || frame.y < 0 || frame.width <= 0 || frame.height <= 0) {\n throw new RangeError(\"Liquid Toast island frame must be finite with positive dimensions\");\n }\n}\n\nexport type LiquidToastLayout = Readonly<{\n width: number; height: number; cardWidth: number; cardTop: number; cardLeft: number;\n anchorX: number; anchorY: number; anchorWidth: number; anchorHeight: number;\n canvasTop: number; canvasHeight: number; fits: boolean;\n}>;\n\nexport function resolveLiquidToastLayout(input: Readonly<{\n width: number; height: number; availableHeight: number; anchor: LiquidToastAnchor;\n windowOrigin?: Readonly<{ x: number; y: number }>;\n}>): LiquidToastLayout {\n const { width, height, availableHeight, anchor, windowOrigin } = input;\n validateLiquidToastAnchor(anchor);\n if (![width, height, availableHeight].every(Number.isFinite) || width < 0 || height < 0 || availableHeight < 0) {\n throw new RangeError(\"Liquid Toast measurements must be finite and non-negative\");\n }\n let anchorWidth: number = liquidToastRecipe.capsule.width;\n let anchorHeight: number = liquidToastRecipe.capsule.height;\n let anchorX = (width - anchorWidth) / 2;\n let anchorY = 0;\n if (anchor.kind === \"island\" && windowOrigin) {\n const x = anchor.frame.x - windowOrigin.x;\n const y = anchor.frame.y - windowOrigin.y;\n // Rotation/stale host measurements fall back to capsule instead of drawing over a cutout.\n if (x >= 0 && x + anchor.frame.width <= width && y + anchor.frame.height <= availableHeight) {\n anchorX = x; anchorY = y; anchorWidth = anchor.frame.width; anchorHeight = anchor.frame.height;\n }\n }\n const cardWidth = Math.min(width, liquidToastRecipe.maxWidth);\n const cardTop = Math.max(0, anchorY + anchorHeight + liquidToastRecipe.gap);\n const canvasTop = Math.min(0, anchorY) - liquidToastRecipe.blur * 2;\n return {\n width, height, cardWidth, cardTop, cardLeft: (width - cardWidth) / 2,\n anchorX, anchorY, anchorWidth, anchorHeight, canvasTop,\n canvasHeight: cardTop + height - canvasTop + 32,\n fits: width >= 240 && height > 0 && cardTop + height <= availableHeight,\n };\n}\n\nexport function buildLiquidToastGeometry(drop: number, expand: number, layout: LiquidToastLayout) {\n \"worklet\";\n const clamp = (n: number) => Math.max(0, Math.min(1, n));\n const mix = (a: number, b: number, t: number) => a + (b - a) * t;\n const grow = 1 - (1 - clamp(drop / 0.7)) ** 1.25;\n const n = clamp(drop / 0.82);\n const peak = 1.6 / 3;\n const normal = peak ** 1.6 * (1 - peak) ** 1.4;\n const neck = n <= 0 || n >= 1 ? 0 : (n ** 1.6 * (1 - n) ** 1.4) / normal;\n const stretch = 1 + 0.38 * neck;\n const droplet = liquidToastRecipe.dropSize * grow;\n const width = Math.max(0, Math.min(layout.width, mix(droplet / stretch, layout.cardWidth, expand)));\n const height = Math.max(0, mix(droplet * stretch, layout.height, expand));\n const originY = layout.anchorY + layout.anchorHeight * 0.66;\n const centerY = mix(originY, layout.cardTop + layout.height / 2, drop);\n const centerX = mix(layout.anchorX + layout.anchorWidth / 2, layout.width / 2, clamp(expand));\n const neckWidth = Math.max(0, Math.min(liquidToastRecipe.neckWidth, width) * neck);\n const neckY = layout.anchorY + layout.anchorHeight / 2;\n return {\n x: centerX - width / 2, y: centerY - height / 2, width, height,\n radius: Math.max(0, Math.min(mix(droplet / 2, liquidToastRecipe.radius, expand), width / 2, height / 2)),\n neckX: layout.anchorX + layout.anchorWidth / 2 - neckWidth / 2,\n neckY, neckWidth, neckHeight: Math.max(0, centerY - neckY),\n offsetY: centerY - (layout.cardTop + layout.height / 2),\n };\n}\n"]}
1
+ {"version":3,"file":"toast-liquid.js","sourceRoot":"","sources":["../src/toast-liquid.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Geometry adapted from rit3zh/expo-dynamic-notifications, commit 5de059a5cbef.\n * The MIT notice is shipped in THIRD_PARTY_NOTICES.md. See docs/plans/liquid-toast.md.\n * Kept separate from the lifecycle: animation must never own a second queue.\n */\nexport const liquidToastRecipe = {\n // 2026-10-02: use a circular in-app origin rather than a second Dynamic Island.\n // Keep the public capsule key; changing geometry does not require callers to migrate.\n capsule: { width: 32, height: 32 },\n // Preserve the settled card top (58) when increasing the anchor height from 24 to 32.\n gap: 26,\n minHeight: 74,\n maxWidth: 396,\n // 2026-10-02 card redesign: the medium 12-unit corner supports a banner silhouette.\n radius: 12,\n dropSize: 52,\n neckWidth: 60,\n blur: 14.3,\n gain: 22,\n threshold: 0.43,\n // These intentionally exceed general HJM motion: the neck needs time to separate.\n // Original choreography is the comparison baseline, not a new global motion scale.\n spring: {\n drop: { duration: 1150, dampingRatio: 0.82 },\n expand: { duration: 1000, dampingRatio: 0.8 },\n reveal: { duration: 700, dampingRatio: 1 },\n tint: { duration: 700, dampingRatio: 1 },\n collapse: { duration: 660, dampingRatio: 0.92, velocity: 2 },\n return: { duration: 1150, dampingRatio: 0.9 },\n fade: { duration: 360, dampingRatio: 1 },\n drag: { duration: 560, dampingRatio: 0.7 },\n },\n // Keep the drop anchor-colored through descent; early tint looked like a gray blob on iPhone 17 (2026-09-30).\n delay: { tint: 420, expand: 340, reveal: 560, collapse: 100, return: 280 },\n swipe: { distance: -18, velocity: -420 },\n} as const;\n\n/** Island frames are explicitly verified window coordinates, never inferred from insets. */\nexport type LiquidToastAnchor = Readonly<{ kind: \"capsule\" }> | Readonly<{\n kind: \"island\";\n frame: Readonly<{ x: number; y: number; width: number; height: number }>;\n}>;\n\nexport function validateLiquidToastAnchor(anchor: LiquidToastAnchor): void {\n if (anchor.kind === \"capsule\") return;\n if (anchor.kind !== \"island\") throw new TypeError(\"Unsupported liquid toast anchor\");\n const frame = anchor.frame;\n if (!frame || ![frame.x, frame.y, frame.width, frame.height].every(Number.isFinite) || frame.x < 0 || frame.y < 0 || frame.width <= 0 || frame.height <= 0) {\n throw new RangeError(\"Liquid Toast island frame must be finite with positive dimensions\");\n }\n}\n\nexport type LiquidToastLayout = Readonly<{\n width: number; height: number; cardWidth: number; cardTop: number; cardLeft: number;\n anchorX: number; anchorY: number; anchorWidth: number; anchorHeight: number;\n canvasTop: number; canvasHeight: number; fits: boolean;\n}>;\n\nexport function resolveLiquidToastLayout(input: Readonly<{\n width: number; height: number; availableHeight: number; anchor: LiquidToastAnchor;\n windowOrigin?: Readonly<{ x: number; y: number }>;\n}>): LiquidToastLayout {\n const { width, height, availableHeight, anchor, windowOrigin } = input;\n validateLiquidToastAnchor(anchor);\n if (![width, height, availableHeight].every(Number.isFinite) || width < 0 || height < 0 || availableHeight < 0) {\n throw new RangeError(\"Liquid Toast measurements must be finite and non-negative\");\n }\n let anchorWidth: number = liquidToastRecipe.capsule.width;\n let anchorHeight: number = liquidToastRecipe.capsule.height;\n let anchorX = (width - anchorWidth) / 2;\n let anchorY = 0;\n if (anchor.kind === \"island\" && windowOrigin) {\n const x = anchor.frame.x - windowOrigin.x;\n const y = anchor.frame.y - windowOrigin.y;\n // Rotation/stale host measurements fall back to capsule instead of drawing over a cutout.\n if (x >= 0 && x + anchor.frame.width <= width && y + anchor.frame.height <= availableHeight) {\n anchorX = x; anchorY = y; anchorWidth = anchor.frame.width; anchorHeight = anchor.frame.height;\n }\n }\n const cardWidth = Math.min(width, liquidToastRecipe.maxWidth);\n const cardTop = Math.max(0, anchorY + anchorHeight + liquidToastRecipe.gap);\n const canvasTop = Math.min(0, anchorY) - liquidToastRecipe.blur * 2;\n return {\n width, height, cardWidth, cardTop, cardLeft: (width - cardWidth) / 2,\n anchorX, anchorY, anchorWidth, anchorHeight, canvasTop,\n canvasHeight: cardTop + height - canvasTop + 32,\n fits: width >= 240 && height > 0 && cardTop + height <= availableHeight,\n };\n}\n\n/** A themed corner changes only the settled card, never the circular origin.\n * Keep the default recipe for existing callers and clamp to measured paint bounds. */\nexport function buildLiquidToastGeometry(drop: number, expand: number, layout: LiquidToastLayout, cardRadius: number = liquidToastRecipe.radius) {\n \"worklet\";\n if (!Number.isFinite(cardRadius) || cardRadius < 0) throw new RangeError(\"Liquid Toast card radius must be finite and non-negative\");\n const clamp = (n: number) => Math.max(0, Math.min(1, n));\n const mix = (a: number, b: number, t: number) => a + (b - a) * t;\n const grow = 1 - (1 - clamp(drop / 0.7)) ** 1.25;\n const n = clamp(drop / 0.82);\n const peak = 1.6 / 3;\n const normal = peak ** 1.6 * (1 - peak) ** 1.4;\n const neck = n <= 0 || n >= 1 ? 0 : (n ** 1.6 * (1 - n) ** 1.4) / normal;\n const stretch = 1 + 0.38 * neck;\n const droplet = liquidToastRecipe.dropSize * grow;\n const width = Math.max(0, Math.min(layout.width, mix(droplet / stretch, layout.cardWidth, expand)));\n const height = Math.max(0, mix(droplet * stretch, layout.height, expand));\n const originY = layout.anchorY + layout.anchorHeight * 0.66;\n const centerY = mix(originY, layout.cardTop + layout.height / 2, drop);\n const centerX = mix(layout.anchorX + layout.anchorWidth / 2, layout.width / 2, clamp(expand));\n const neckWidth = Math.max(0, Math.min(liquidToastRecipe.neckWidth, width) * neck);\n const neckY = layout.anchorY + layout.anchorHeight / 2;\n return {\n x: centerX - width / 2, y: centerY - height / 2, width, height,\n radius: Math.max(0, Math.min(mix(droplet / 2, cardRadius, expand), width / 2, height / 2)),\n neckX: layout.anchorX + layout.anchorWidth / 2 - neckWidth / 2,\n neckY, neckWidth, neckHeight: Math.max(0, centerY - neckY),\n offsetY: centerY - (layout.cardTop + layout.height / 2),\n };\n}\n"]}
package/dist/version.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  /** Package release shown by documentation surfaces. Kept in sync by a test. */
2
- export declare const designSystemVersion: "1.14.0";
2
+ export declare const designSystemVersion: "1.15.0";
3
3
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  /** Package release shown by documentation surfaces. Kept in sync by a test. */
2
- export const designSystemVersion = "1.14.0";
2
+ export const designSystemVersion = "1.15.0";
3
3
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAiB,CAAC","sourcesContent":["/** Package release shown by documentation surfaces. Kept in sync by a test. */\nexport const designSystemVersion = \"1.14.0\" as const;\n"]}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAiB,CAAC","sourcesContent":["/** Package release shown by documentation surfaces. Kept in sync by a test. */\nexport const designSystemVersion = \"1.15.0\" as const;\n"]}
package/docs/asset.md CHANGED
@@ -24,3 +24,10 @@
24
24
 
25
25
  **겹침 비율은 Avatar와 같은 값이다.** 한 줄에 섞여 나오는 것이 이 컴포넌트가 생긴 이유라
26
26
  두 값이 다르면 그 줄이 다시 어긋난다.
27
+
28
+ **둥근 액자는 프로필을 따르고 명시한 정사각·원형은 유지한다.** 2026-10-07 3dicons의
29
+ 재질/각도 선택과 기존 Asset을 대조하다 양 renderer가 foundation 12px를 직접 사용해
30
+ 종이·레트로·사용자 프로필의 radius를 우회함을 확인했다. 미게시 보강에서는 `rounded`를
31
+ 가장 가까운 profile의 `tokens.radius.md`로 해석한다. square/circle까지 브랜드 값으로
32
+ 바꾸는 대안은 매체의 명시적 geometry를 바꾸므로 사용하지 않는다. 크기·라벨·재생기·자산
33
+ 선택의 소유권은 그대로다([조사/재현](../../../docs/qa/2026-10-07-3dicons-page-review.md)).