@hjmds/design-contracts 1.7.0 → 1.8.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 (228) hide show
  1. package/THIRD_PARTY_NOTICES.md +58 -0
  2. package/dist/agreement.d.ts +3 -3
  3. package/dist/agreement.js +1 -1
  4. package/dist/agreement.js.map +1 -1
  5. package/dist/asset.d.ts +1 -1
  6. package/dist/asset.js +1 -1
  7. package/dist/asset.js.map +1 -1
  8. package/dist/base-recipes.d.ts +12 -12
  9. package/dist/base-recipes.d.ts.map +1 -1
  10. package/dist/base-recipes.js +15 -14
  11. package/dist/base-recipes.js.map +1 -1
  12. package/dist/behaviors.d.ts +1 -1
  13. package/dist/catalog.d.ts +417 -561
  14. package/dist/catalog.d.ts.map +1 -1
  15. package/dist/catalog.js +138 -90
  16. package/dist/catalog.js.map +1 -1
  17. package/dist/command-palette.d.ts +3 -3
  18. package/dist/component-contracts.d.ts +4 -4
  19. package/dist/component-contracts.d.ts.map +1 -1
  20. package/dist/component-contracts.js +5 -4
  21. package/dist/component-contracts.js.map +1 -1
  22. package/dist/component-definitions.d.ts +1 -4
  23. package/dist/component-definitions.d.ts.map +1 -1
  24. package/dist/component-definitions.js +1 -4
  25. package/dist/component-definitions.js.map +1 -1
  26. package/dist/component-recipes.d.ts +76 -83
  27. package/dist/component-recipes.d.ts.map +1 -1
  28. package/dist/component-recipes.js +29 -49
  29. package/dist/component-recipes.js.map +1 -1
  30. package/dist/component-references.d.ts +0 -15
  31. package/dist/component-references.d.ts.map +1 -1
  32. package/dist/component-references.js +0 -3
  33. package/dist/component-references.js.map +1 -1
  34. package/dist/data-table.d.ts +3 -3
  35. package/dist/data-table.js +1 -1
  36. package/dist/data-table.js.map +1 -1
  37. package/dist/date-picker.d.ts +2 -2
  38. package/dist/icon-button-recipe.d.ts +2 -2
  39. package/dist/icon-button-recipe.js +1 -1
  40. package/dist/icon-button-recipe.js.map +1 -1
  41. package/dist/image.d.ts +2 -2
  42. package/dist/image.js +2 -2
  43. package/dist/image.js.map +1 -1
  44. package/dist/index.d.ts +1 -1
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +1 -1
  47. package/dist/index.js.map +1 -1
  48. package/dist/internal/thinking-orb/braid.d.ts +3 -0
  49. package/dist/internal/thinking-orb/braid.d.ts.map +1 -0
  50. package/dist/internal/thinking-orb/braid.js +47 -0
  51. package/dist/internal/thinking-orb/braid.js.map +1 -0
  52. package/dist/internal/thinking-orb/core.d.ts +60 -0
  53. package/dist/internal/thinking-orb/core.d.ts.map +1 -0
  54. package/dist/internal/thinking-orb/core.js +86 -0
  55. package/dist/internal/thinking-orb/core.js.map +1 -0
  56. package/dist/internal/thinking-orb/lattice.d.ts +5 -0
  57. package/dist/internal/thinking-orb/lattice.d.ts.map +1 -0
  58. package/dist/internal/thinking-orb/lattice.js +187 -0
  59. package/dist/internal/thinking-orb/lattice.js.map +1 -0
  60. package/dist/internal/thinking-orb/morph.d.ts +3 -0
  61. package/dist/internal/thinking-orb/morph.d.ts.map +1 -0
  62. package/dist/internal/thinking-orb/morph.js +127 -0
  63. package/dist/internal/thinking-orb/morph.js.map +1 -0
  64. package/dist/internal/thinking-orb/orbits.d.ts +3 -0
  65. package/dist/internal/thinking-orb/orbits.d.ts.map +1 -0
  66. package/dist/internal/thinking-orb/orbits.js +68 -0
  67. package/dist/internal/thinking-orb/orbits.js.map +1 -0
  68. package/dist/internal/thinking-orb/presets.d.ts +21 -0
  69. package/dist/internal/thinking-orb/presets.d.ts.map +1 -0
  70. package/dist/internal/thinking-orb/presets.js +77 -0
  71. package/dist/internal/thinking-orb/presets.js.map +1 -0
  72. package/dist/internal/thinking-orb/profiles.d.ts +8 -0
  73. package/dist/internal/thinking-orb/profiles.d.ts.map +1 -0
  74. package/dist/internal/thinking-orb/profiles.js +158 -0
  75. package/dist/internal/thinking-orb/profiles.js.map +1 -0
  76. package/dist/internal/thinking-orb/registry.d.ts +9 -0
  77. package/dist/internal/thinking-orb/registry.d.ts.map +1 -0
  78. package/dist/internal/thinking-orb/registry.js +27 -0
  79. package/dist/internal/thinking-orb/registry.js.map +1 -0
  80. package/dist/internal/thinking-orb/ribbon.d.ts +3 -0
  81. package/dist/internal/thinking-orb/ribbon.d.ts.map +1 -0
  82. package/dist/internal/thinking-orb/ribbon.js +87 -0
  83. package/dist/internal/thinking-orb/ribbon.js.map +1 -0
  84. package/dist/internal/thinking-orb/types.d.ts +15 -0
  85. package/dist/internal/thinking-orb/types.d.ts.map +1 -0
  86. package/dist/internal/thinking-orb/types.js +4 -0
  87. package/dist/internal/thinking-orb/types.js.map +1 -0
  88. package/dist/internal/thinking-orb/web.d.ts +3 -0
  89. package/dist/internal/thinking-orb/web.d.ts.map +1 -0
  90. package/dist/internal/thinking-orb/web.js +92 -0
  91. package/dist/internal/thinking-orb/web.js.map +1 -0
  92. package/dist/masonry.d.ts +17 -0
  93. package/dist/masonry.d.ts.map +1 -0
  94. package/dist/masonry.js +18 -0
  95. package/dist/masonry.js.map +1 -0
  96. package/dist/menubar.d.ts +3 -3
  97. package/dist/menubar.js +1 -1
  98. package/dist/menubar.js.map +1 -1
  99. package/dist/number-field.d.ts +2 -2
  100. package/dist/otp-field.d.ts +1 -1
  101. package/dist/password-field.d.ts +2 -2
  102. package/dist/popover.d.ts +7 -8
  103. package/dist/popover.d.ts.map +1 -1
  104. package/dist/popover.js +2 -0
  105. package/dist/popover.js.map +1 -1
  106. package/dist/qr-code-recipe.d.ts +7 -0
  107. package/dist/qr-code-recipe.d.ts.map +1 -0
  108. package/dist/qr-code-recipe.js +2 -0
  109. package/dist/qr-code-recipe.js.map +1 -0
  110. package/dist/qr-code.d.ts +16 -0
  111. package/dist/qr-code.d.ts.map +1 -0
  112. package/dist/qr-code.js +37 -0
  113. package/dist/qr-code.js.map +1 -0
  114. package/dist/semantic-colors.d.ts +1 -1
  115. package/dist/semantic-colors.js +1 -1
  116. package/dist/semantic-colors.js.map +1 -1
  117. package/dist/showcase.d.ts +5 -1
  118. package/dist/showcase.d.ts.map +1 -1
  119. package/dist/showcase.js +51 -13
  120. package/dist/showcase.js.map +1 -1
  121. package/dist/sidebar.d.ts +3 -3
  122. package/dist/sidebar.js +1 -1
  123. package/dist/sidebar.js.map +1 -1
  124. package/dist/tag.d.ts +4 -4
  125. package/dist/tag.js +3 -3
  126. package/dist/tag.js.map +1 -1
  127. package/dist/tags-input.d.ts +3 -3
  128. package/dist/tags-input.js +1 -1
  129. package/dist/tags-input.js.map +1 -1
  130. package/dist/text-formats.d.ts +2 -2
  131. package/dist/text-formats.js +2 -2
  132. package/dist/text-formats.js.map +1 -1
  133. package/dist/thinking-orb-recipe.d.ts +15 -0
  134. package/dist/thinking-orb-recipe.d.ts.map +1 -0
  135. package/dist/thinking-orb-recipe.js +9 -0
  136. package/dist/thinking-orb-recipe.js.map +1 -0
  137. package/dist/thinking-orb.d.ts +25 -0
  138. package/dist/thinking-orb.d.ts.map +1 -0
  139. package/dist/thinking-orb.js +40 -0
  140. package/dist/thinking-orb.js.map +1 -0
  141. package/dist/toast-liquid.d.ts +116 -0
  142. package/dist/toast-liquid.d.ts.map +1 -0
  143. package/dist/toast-liquid.js +100 -0
  144. package/dist/toast-liquid.js.map +1 -0
  145. package/dist/toast.d.ts +7 -1
  146. package/dist/toast.d.ts.map +1 -1
  147. package/dist/toast.js +5 -0
  148. package/dist/toast.js.map +1 -1
  149. package/dist/toggle-group.d.ts +2 -2
  150. package/dist/toggle-group.js +3 -3
  151. package/dist/toggle-group.js.map +1 -1
  152. package/dist/transfer-list.d.ts +3 -3
  153. package/dist/transfer-list.js +1 -1
  154. package/dist/transfer-list.js.map +1 -1
  155. package/dist/tree.d.ts +2 -2
  156. package/dist/version.d.ts +1 -1
  157. package/dist/version.js +1 -1
  158. package/dist/version.js.map +1 -1
  159. package/dist/virtual-list.d.ts +13 -0
  160. package/dist/virtual-list.d.ts.map +1 -0
  161. package/dist/virtual-list.js +13 -0
  162. package/dist/virtual-list.js.map +1 -0
  163. package/docs/anchor.md +5 -4
  164. package/docs/ant-design-coverage.md +2 -2
  165. package/docs/architecture.md +6 -2
  166. package/docs/authoring-brief.md +3 -2
  167. package/docs/bottom-navigation.md +7 -6
  168. package/docs/breadcrumb.md +5 -2
  169. package/docs/calendar.md +6 -3
  170. package/docs/carousel.md +2 -2
  171. package/docs/cascader.md +6 -0
  172. package/docs/catalog-cleanup.md +7 -0
  173. package/docs/catalog-decision-status.md +3 -3
  174. package/docs/catalog-freeze.json +9 -7
  175. package/docs/collapsible.md +12 -0
  176. package/docs/command-palette.md +5 -3
  177. package/docs/consumer-policy.md +29 -12
  178. package/docs/context-menu.md +12 -2
  179. package/docs/data-table.md +11 -6
  180. package/docs/date-picker.md +6 -4
  181. package/docs/date-range.md +4 -2
  182. package/docs/description-list.md +8 -9
  183. package/docs/dialog.md +39 -0
  184. package/docs/expansion-roadmap.md +11 -7
  185. package/docs/file-picker.md +5 -3
  186. package/docs/form.md +16 -4
  187. package/docs/generated/component-maturity.md +88 -91
  188. package/docs/generated/renderer-evidence.json +1848 -1023
  189. package/docs/generated/renderer-evidence.md +153 -145
  190. package/docs/generated/showcase-manifest.json +431 -497
  191. package/docs/layout.md +8 -9
  192. package/docs/library-gap-analysis.md +1 -1
  193. package/docs/masonry.md +11 -0
  194. package/docs/mentions.md +14 -7
  195. package/docs/optional-adapters.md +106 -0
  196. package/docs/otp-field.md +2 -1
  197. package/docs/pagination.md +4 -1
  198. package/docs/password-field.md +2 -1
  199. package/docs/popover.md +9 -6
  200. package/docs/promotion-candidates.md +2 -1
  201. package/docs/qr-code.md +11 -65
  202. package/docs/react-native-completion.md +1 -1
  203. package/docs/result.md +8 -5
  204. package/docs/screen-chrome.md +2 -1
  205. package/docs/sheet.md +8 -2
  206. package/docs/showcase.md +10 -2
  207. package/docs/side-panel.md +12 -6
  208. package/docs/sidebar.md +3 -0
  209. package/docs/splitter.md +6 -6
  210. package/docs/stable-core.md +72 -0
  211. package/docs/stable-promotion.md +47 -43
  212. package/docs/tags-input.md +5 -2
  213. package/docs/theme-palette.md +27 -0
  214. package/docs/thinking-orb.md +124 -0
  215. package/docs/timeline.md +2 -3
  216. package/docs/toast.md +6 -0
  217. package/docs/tooltip.md +2 -1
  218. package/docs/tour.md +14 -8
  219. package/docs/transfer-list.md +24 -21
  220. package/docs/tree-select.md +2 -1
  221. package/docs/tree.md +12 -7
  222. package/docs/upload-item.md +7 -3
  223. package/docs/virtual-list.md +10 -67
  224. package/package.json +30 -3
  225. package/docs/app-provider.md +0 -50
  226. package/docs/border-beam.md +0 -66
  227. package/docs/chart.md +0 -33
  228. package/docs/utility.md +0 -53
package/dist/showcase.js CHANGED
@@ -101,6 +101,11 @@ export const showcaseScenarios = [
101
101
  label: "Keyboard",
102
102
  description: "Focus order and documented keyboard behavior run as interaction tests.",
103
103
  },
104
+ {
105
+ id: "native-actions",
106
+ label: "Native actions",
107
+ description: "Accessible host actions and their state transitions run against the Native renderer.",
108
+ },
104
109
  {
105
110
  id: "platform-parity",
106
111
  label: "Web / Native parity",
@@ -129,22 +134,56 @@ export function getRequiredShowcaseScenarios(entry) {
129
134
  const activeSurfaces = ["web", "native"].filter((surface) => isRendererMaturity(getComponentSurfaceStatus(entry, surface)));
130
135
  if (activeSurfaces.length === 0)
131
136
  return plannedRequirements;
132
- return ["contract", ...getRendererShowcaseScenarios(entry, activeSurfaces)];
137
+ const rendererScenarios = [...new Set(activeSurfaces.flatMap((surface) => getRendererShowcaseScenarios(entry, surface)))];
138
+ return [
139
+ "contract",
140
+ ...rendererScenarios,
141
+ ];
133
142
  }
134
143
  function isRendererMaturity(status) {
135
144
  return status === "stable" || status === "beta";
136
145
  }
137
- // 글자 슬롯이 없어 long-copy를 증명할 수 없는 컴포넌트. 근거: docs/stable-core.md 1.5.0.
138
- const textlessComponentNames = new Set(["Icon", "IconButton", "Skeleton", "Spinner", "Divider"]);
139
- function getRendererShowcaseScenarios(entry, activeSurfaces) {
146
+ // These outputs expose no consumer-authored visible prose; default and accessibility proofs remain required.
147
+ const textlessComponentNames = new Set([
148
+ "Icon", "IconButton", "Skeleton", "Spinner", "ThinkingOrb", "Divider",
149
+ "Avatar", "Asset", "CounterBadge", "Image", "VisuallyHidden", "Pagination",
150
+ // These own form/range behavior but no long-form text slot; their child fields
151
+ // and calendar cells own their copy, so duplicating that proof here misattributes it.
152
+ "Form", "DateRangePicker",
153
+ ]);
154
+ // These behaviors expose no interaction on either rendered surface.
155
+ const semanticOnlyBehaviors = new Set([
156
+ "top", "authScreen", "heading", "bottomInfo", "nativePlatform", "textFormat", "asset",
157
+ ]);
158
+ // Keep surface capability lookup local: importing behaviorRegistry here would pull every
159
+ // behavior contract into the lightweight showcase/evidence entry points. The alternative was
160
+ // importing that registry; showcase.test.ts cross-checks these names against it to prevent drift.
161
+ const webKeyboardBehaviors = new Set([
162
+ "agreement", "alertDialog", "anchor", "authProviderButton", "bottomNavigation", "breadcrumb",
163
+ "calendar", "carousel", "checkbox", "checkboxGroup", "chip", "collapsible", "combobox",
164
+ "commandPalette", "contextMenu", "dataTable", "datePicker", "dateRange", "dialog",
165
+ "disclosureGroup", "field", "filePicker", "floatingActionButton", "form", "layout", "link",
166
+ "loadMore", "menu", "menubar", "numberField", "otpField", "pagination", "passwordField",
167
+ "popover", "radioGroup", "searchField", "segmentedControl", "select", "sheet", "sidePanel",
168
+ "sidebar", "skipNav", "slider", "splitter", "switch", "tabs", "tagsInput", "toast",
169
+ "toggleGroup", "tooltip", "tour", "transferList", "tree", "uploadItem",
170
+ ]);
171
+ const nativeActionBehaviors = new Set([
172
+ "agreement", "alertDialog", "authProviderButton", "bottomNavigation", "calendar", "carousel",
173
+ "checkbox", "checkboxGroup", "chip", "collapsible", "combobox", "datePicker", "dateRange",
174
+ "dialog", "disclosureGroup", "field", "filePicker", "floatingActionButton", "form", "link",
175
+ "loadMore", "menu", "numberField", "otpField", "passwordField", "radioGroup", "searchField",
176
+ "segmentedControl", "select", "sheet", "slider", "switch", "tabs", "tagsInput", "toast",
177
+ "toggleGroup", "transferList", "uploadItem",
178
+ ]);
179
+ function getRendererShowcaseScenarios(entry, surface) {
140
180
  const requirements = rendererRequirements.filter((scenario) => scenario !== "long-copy" || !textlessComponentNames.has(entry.name));
141
- if (entry.behavior) {
142
- requirements.push("keyboard");
143
- }
144
- if (entry.platform === "adaptive" &&
145
- activeSurfaces.includes("web") &&
146
- activeSurfaces.includes("native")) {
147
- requirements.push("platform-parity");
181
+ if (entry.behavior && !semanticOnlyBehaviors.has(entry.behavior)) {
182
+ // Web owns DOM key bindings; Native declares host accessibility actions instead, so Pressable tests must not claim physical keyboard proof.
183
+ if (surface === "web" && webKeyboardBehaviors.has(entry.behavior))
184
+ requirements.push("keyboard");
185
+ if (surface === "native" && nativeActionBehaviors.has(entry.behavior))
186
+ requirements.push("native-actions");
148
187
  }
149
188
  return requirements;
150
189
  }
@@ -156,9 +195,8 @@ export function getRequiredShowcaseEvidence(entry) {
156
195
  { surface: "contract", scenarios: ["contract"] },
157
196
  ];
158
197
  const activeSurfaces = ["web", "native"].filter((surface) => isRendererMaturity(getComponentSurfaceStatus(entry, surface)));
159
- const rendererScenarios = getRendererShowcaseScenarios(entry, activeSurfaces);
160
198
  for (const surface of activeSurfaces) {
161
- requirements.push({ surface, scenarios: rendererScenarios });
199
+ requirements.push({ surface, scenarios: getRendererShowcaseScenarios(entry, surface) });
162
200
  }
163
201
  return requirements;
164
202
  }
@@ -1 +1 @@
1
- {"version":3,"file":"showcase.js","sourceRoot":"","sources":["../src/showcase.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n componentCatalog,\n getComponentSurfaceStatus,\n type ComponentCatalogEntry,\n type ComponentStatus,\n type ComponentSurface,\n type ComponentSurfaceMaturity,\n type ComponentSurfaceStatus,\n} from \"./catalog.js\";\nimport type { ResolvedTheme } from \"./colors.js\";\nimport type {\n DesignSystemDirection,\n DesignSystemEnvironmentInput,\n DesignSystemTextScale,\n} from \"./design-system-provider.js\";\n\nexport type ShowcaseTheme = ResolvedTheme;\nexport type ShowcaseDirection = DesignSystemDirection;\n/** Closed fixture subset of the Provider's continuous text-scale signal. */\nexport type ShowcaseTextScale = 1 | 1.5 | 2;\nexport type ShowcaseMotionPreference = \"full\" | \"reduced\";\n\nexport type ShowcaseEnvironment = Readonly<{\n id: \"default\" | \"dark\" | \"large-text\" | \"rtl\" | \"reduced-motion\";\n label: string;\n theme: ShowcaseTheme;\n direction: ShowcaseDirection;\n textScale: ShowcaseTextScale;\n motion: ShowcaseMotionPreference;\n}>;\n\n/**\n * A small, deliberate environment matrix catches the high-risk differences\n * without multiplying every story into an unreadable Cartesian product.\n */\nexport const showcaseEnvironmentMatrix = [\n {\n id: \"default\",\n label: \"Light · LTR · 100%\",\n theme: \"light\",\n direction: \"ltr\",\n textScale: 1,\n motion: \"full\",\n },\n {\n id: \"dark\",\n label: \"Dark · LTR · 100%\",\n theme: \"dark\",\n direction: \"ltr\",\n textScale: 1,\n motion: \"full\",\n },\n {\n id: \"large-text\",\n label: \"Light · LTR · 200%\",\n theme: \"light\",\n direction: \"ltr\",\n textScale: 2,\n motion: \"full\",\n },\n {\n id: \"rtl\",\n label: \"Light · RTL · 100%\",\n theme: \"light\",\n direction: \"rtl\",\n textScale: 1,\n motion: \"full\",\n },\n {\n id: \"reduced-motion\",\n label: \"Light · LTR · Reduced motion\",\n theme: \"light\",\n direction: \"ltr\",\n textScale: 1,\n motion: \"reduced\",\n },\n] as const satisfies readonly ShowcaseEnvironment[];\n\n/** Bridges documentation fixtures to the same environment contract products consume. */\nexport function getShowcaseEnvironmentInput(\n environment: ShowcaseEnvironment,\n): DesignSystemEnvironmentInput {\n const textScale: DesignSystemTextScale = environment.textScale;\n return {\n theme: environment.theme,\n direction: environment.direction,\n textScale,\n reducedMotion: environment.motion === \"reduced\",\n };\n}\n\nexport type ShowcaseScenarioId =\n | \"contract\"\n | \"default\"\n | \"dark\"\n | \"long-copy\"\n | \"large-text\"\n | \"rtl\"\n | \"reduced-motion\"\n | \"accessibility\"\n | \"keyboard\"\n | \"platform-parity\";\n\nexport type ShowcaseSurface = \"contract\" | \"web\" | \"native\";\n\nexport type ShowcaseScenario = Readonly<{\n id: ShowcaseScenarioId;\n label: string;\n description: string;\n}>;\n\nexport const showcaseScenarios = [\n {\n id: \"contract\",\n label: \"Contract\",\n description: \"Anatomy, defaults, supported axes, platform and maturity are visible.\",\n },\n {\n id: \"default\",\n label: \"Default\",\n description: \"The normal state renders using recipe defaults without product overrides.\",\n },\n {\n id: \"dark\",\n label: \"Dark\",\n description: \"Semantic colors remain legible on the dark theme.\",\n },\n {\n id: \"long-copy\",\n label: \"Long copy\",\n description: \"Long Korean and English copy wraps without clipping or hiding meaning.\",\n },\n {\n id: \"large-text\",\n label: \"200% text\",\n description: \"Text can grow to 200% without truncating required information.\",\n },\n {\n id: \"rtl\",\n label: \"RTL\",\n description: \"Logical start/end layout and directional icons mirror correctly.\",\n },\n {\n id: \"reduced-motion\",\n label: \"Reduced motion\",\n description: \"Motion follows the recipe reduced-motion fallback.\",\n },\n {\n id: \"accessibility\",\n label: \"Accessibility\",\n description: \"Names, states, relationships, contrast and touch targets are inspectable.\",\n },\n {\n id: \"keyboard\",\n label: \"Keyboard\",\n description: \"Focus order and documented keyboard behavior run as interaction tests.\",\n },\n {\n id: \"platform-parity\",\n label: \"Web / Native parity\",\n description: \"Adaptive renderers preserve intent while using platform-native behavior.\",\n },\n] as const satisfies readonly ShowcaseScenario[];\n\nconst plannedRequirements = [\"contract\"] as const satisfies readonly ShowcaseScenarioId[];\nconst rendererRequirements = [\n \"default\",\n \"dark\",\n \"long-copy\",\n \"large-text\",\n \"rtl\",\n \"reduced-motion\",\n \"accessibility\",\n] as const satisfies readonly Exclude<ShowcaseScenarioId, \"contract\">[];\n\nexport type ShowcaseComponentEntry = Readonly<{\n storyId: string;\n component: ComponentCatalogEntry;\n /** Resolved Web/Native renderer maturity, including legacy catalog inputs. */\n surfaceMaturity: ComponentSurfaceMaturity;\n requirements: readonly ShowcaseEvidenceRequirement[];\n requiredScenarios: readonly ShowcaseScenarioId[];\n requiredSurfaces: readonly ShowcaseSurface[];\n}>;\n\nexport type ShowcaseEvidenceRequirement = Readonly<{\n surface: ShowcaseSurface;\n scenarios: readonly ShowcaseScenarioId[];\n}>;\n\nexport type ShowcaseEvidenceEntry = Readonly<{\n storyId: string;\n surface: ShowcaseSurface;\n scenarios: readonly ShowcaseScenarioId[];\n}>;\n\nexport type ShowcaseCoverageEntry = Readonly<{\n storyId: string;\n component: ComponentCatalogEntry;\n missingEvidence: readonly Readonly<{\n surface: ShowcaseSurface;\n scenario: ShowcaseScenarioId;\n }>[];\n missingSurfaces: readonly ShowcaseSurface[];\n missingScenarios: readonly ShowcaseScenarioId[];\n complete: boolean;\n}>;\n\nexport function getShowcaseStoryId(entry: ComponentCatalogEntry): string {\n const slug = entry.name\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .replace(/[^a-zA-Z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\")\n .toLowerCase();\n return `${entry.category}/${slug}`;\n}\n\nexport function getRequiredShowcaseScenarios(\n entry: ComponentCatalogEntry,\n): readonly ShowcaseScenarioId[] {\n const activeSurfaces = ([\"web\", \"native\"] as const).filter((surface) =>\n isRendererMaturity(getComponentSurfaceStatus(entry, surface)),\n );\n if (activeSurfaces.length === 0) return plannedRequirements;\n\n return [\"contract\", ...getRendererShowcaseScenarios(entry, activeSurfaces)];\n}\n\nfunction isRendererMaturity(\n status: ComponentSurfaceStatus,\n): status is Extract<ComponentStatus, \"stable\" | \"beta\"> {\n return status === \"stable\" || status === \"beta\";\n}\n\n// 글자 슬롯이 없어 long-copy를 증명할 수 없는 컴포넌트. 근거: docs/stable-core.md 1.5.0.\nconst textlessComponentNames: ReadonlySet<string> = new Set([\"Icon\", \"IconButton\", \"Skeleton\", \"Spinner\", \"Divider\"]);\n\nfunction getRendererShowcaseScenarios(\n entry: ComponentCatalogEntry,\n activeSurfaces: readonly ComponentSurface[],\n): readonly Exclude<ShowcaseScenarioId, \"contract\">[] {\n const requirements: Exclude<ShowcaseScenarioId, \"contract\">[] = rendererRequirements.filter(\n (scenario) => scenario !== \"long-copy\" || !textlessComponentNames.has(entry.name),\n );\n if (entry.behavior) {\n requirements.push(\"keyboard\");\n }\n if (\n entry.platform === \"adaptive\" &&\n activeSurfaces.includes(\"web\") &&\n activeSurfaces.includes(\"native\")\n ) {\n requirements.push(\"platform-parity\");\n }\n return requirements;\n}\n\nexport function getRequiredShowcaseSurfaces(\n entry: ComponentCatalogEntry,\n): readonly ShowcaseSurface[] {\n return getRequiredShowcaseEvidence(entry).map(({ surface }) => surface);\n}\n\nexport function getRequiredShowcaseEvidence(\n entry: ComponentCatalogEntry,\n): readonly ShowcaseEvidenceRequirement[] {\n const requirements: ShowcaseEvidenceRequirement[] = [\n { surface: \"contract\", scenarios: [\"contract\"] },\n ];\n const activeSurfaces = ([\"web\", \"native\"] as const).filter((surface) =>\n isRendererMaturity(getComponentSurfaceStatus(entry, surface)),\n );\n const rendererScenarios = getRendererShowcaseScenarios(entry, activeSurfaces);\n for (const surface of activeSurfaces) {\n requirements.push({ surface, scenarios: rendererScenarios });\n }\n return requirements;\n}\n\nexport function createShowcaseManifest(\n entries: readonly ComponentCatalogEntry[] = componentCatalog,\n): readonly ShowcaseComponentEntry[] {\n return entries.map((component) => ({\n storyId: getShowcaseStoryId(component),\n component,\n surfaceMaturity: {\n web: getComponentSurfaceStatus(component, \"web\"),\n native: getComponentSurfaceStatus(component, \"native\"),\n },\n requirements: getRequiredShowcaseEvidence(component),\n requiredScenarios: getRequiredShowcaseScenarios(component),\n requiredSurfaces: getRequiredShowcaseSurfaces(component),\n }));\n}\n\nexport const showcaseManifest = createShowcaseManifest();\n\nexport function createShowcaseCoverage(\n evidence: readonly ShowcaseEvidenceEntry[],\n manifest: readonly ShowcaseComponentEntry[] = showcaseManifest,\n): readonly ShowcaseCoverageEntry[] {\n return manifest.map(({ storyId, component, requirements }) => {\n const componentEvidence = evidence.filter((entry) => entry.storyId === storyId);\n const missingEvidence = requirements.flatMap(({ surface, scenarios }) =>\n scenarios\n .filter(\n (scenario) =>\n !componentEvidence.some(\n (entry) => entry.surface === surface && entry.scenarios.includes(scenario),\n ),\n )\n .map((scenario) => ({ surface, scenario })),\n );\n const missingSurfaces = [...new Set(missingEvidence.map(({ surface }) => surface))];\n const missingScenarios = [...new Set(missingEvidence.map(({ scenario }) => scenario))];\n return {\n storyId,\n component,\n missingEvidence,\n missingSurfaces,\n missingScenarios,\n complete: missingSurfaces.length === 0 && missingScenarios.length === 0,\n };\n });\n}\n\nexport function assertShowcaseCoverage(\n evidence: readonly ShowcaseEvidenceEntry[],\n manifest: readonly ShowcaseComponentEntry[] = showcaseManifest,\n): void {\n const missing = createShowcaseCoverage(evidence, manifest).filter(({ complete }) => !complete);\n if (missing.length === 0) return;\n const details = missing\n .map(({ storyId, missingEvidence }) =>\n `${storyId} (${missingEvidence.map(({ surface, scenario }) => `${surface}/${scenario}`).join(\", \") || \"none\"})`,\n )\n .join(\"\\n\");\n throw new Error(`Showcase evidence is incomplete:\\n${details}`);\n}\n\nexport function summarizeShowcaseMaturity(\n entries: readonly ComponentCatalogEntry[] = componentCatalog,\n): Readonly<Record<ComponentStatus, number>> {\n return entries.reduce<Record<ComponentStatus, number>>(\n (summary, entry) => {\n summary[entry.status] += 1;\n return summary;\n },\n { stable: 0, beta: 0, planned: 0, deprecated: 0 },\n );\n}\n"]}
1
+ {"version":3,"file":"showcase.js","sourceRoot":"","sources":["../src/showcase.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n componentCatalog,\n getComponentSurfaceStatus,\n type ComponentCatalogEntry,\n type ComponentStatus,\n type ComponentSurfaceMaturity,\n type ComponentSurfaceStatus,\n} from \"./catalog.js\";\nimport type { BehaviorName } from \"./behaviors.js\";\nimport type { ResolvedTheme } from \"./colors.js\";\nimport type {\n DesignSystemDirection,\n DesignSystemEnvironmentInput,\n DesignSystemTextScale,\n} from \"./design-system-provider.js\";\n\nexport type ShowcaseTheme = ResolvedTheme;\nexport type ShowcaseDirection = DesignSystemDirection;\n/** Closed fixture subset of the Provider's continuous text-scale signal. */\nexport type ShowcaseTextScale = 1 | 1.5 | 2;\nexport type ShowcaseMotionPreference = \"full\" | \"reduced\";\n\nexport type ShowcaseEnvironment = Readonly<{\n id: \"default\" | \"dark\" | \"large-text\" | \"rtl\" | \"reduced-motion\";\n label: string;\n theme: ShowcaseTheme;\n direction: ShowcaseDirection;\n textScale: ShowcaseTextScale;\n motion: ShowcaseMotionPreference;\n}>;\n\n/**\n * A small, deliberate environment matrix catches the high-risk differences\n * without multiplying every story into an unreadable Cartesian product.\n */\nexport const showcaseEnvironmentMatrix = [\n {\n id: \"default\",\n label: \"Light · LTR · 100%\",\n theme: \"light\",\n direction: \"ltr\",\n textScale: 1,\n motion: \"full\",\n },\n {\n id: \"dark\",\n label: \"Dark · LTR · 100%\",\n theme: \"dark\",\n direction: \"ltr\",\n textScale: 1,\n motion: \"full\",\n },\n {\n id: \"large-text\",\n label: \"Light · LTR · 200%\",\n theme: \"light\",\n direction: \"ltr\",\n textScale: 2,\n motion: \"full\",\n },\n {\n id: \"rtl\",\n label: \"Light · RTL · 100%\",\n theme: \"light\",\n direction: \"rtl\",\n textScale: 1,\n motion: \"full\",\n },\n {\n id: \"reduced-motion\",\n label: \"Light · LTR · Reduced motion\",\n theme: \"light\",\n direction: \"ltr\",\n textScale: 1,\n motion: \"reduced\",\n },\n] as const satisfies readonly ShowcaseEnvironment[];\n\n/** Bridges documentation fixtures to the same environment contract products consume. */\nexport function getShowcaseEnvironmentInput(\n environment: ShowcaseEnvironment,\n): DesignSystemEnvironmentInput {\n const textScale: DesignSystemTextScale = environment.textScale;\n return {\n theme: environment.theme,\n direction: environment.direction,\n textScale,\n reducedMotion: environment.motion === \"reduced\",\n };\n}\n\nexport type ShowcaseScenarioId =\n | \"contract\"\n | \"default\"\n | \"dark\"\n | \"long-copy\"\n | \"large-text\"\n | \"rtl\"\n | \"reduced-motion\"\n | \"accessibility\"\n | \"keyboard\"\n | \"native-actions\"\n | \"platform-parity\";\n\nexport type ShowcaseSurface = \"contract\" | \"web\" | \"native\";\n\nexport type ShowcaseScenario = Readonly<{\n id: ShowcaseScenarioId;\n label: string;\n description: string;\n}>;\n\nexport const showcaseScenarios = [\n {\n id: \"contract\",\n label: \"Contract\",\n description: \"Anatomy, defaults, supported axes, platform and maturity are visible.\",\n },\n {\n id: \"default\",\n label: \"Default\",\n description: \"The normal state renders using recipe defaults without product overrides.\",\n },\n {\n id: \"dark\",\n label: \"Dark\",\n description: \"Semantic colors remain legible on the dark theme.\",\n },\n {\n id: \"long-copy\",\n label: \"Long copy\",\n description: \"Long Korean and English copy wraps without clipping or hiding meaning.\",\n },\n {\n id: \"large-text\",\n label: \"200% text\",\n description: \"Text can grow to 200% without truncating required information.\",\n },\n {\n id: \"rtl\",\n label: \"RTL\",\n description: \"Logical start/end layout and directional icons mirror correctly.\",\n },\n {\n id: \"reduced-motion\",\n label: \"Reduced motion\",\n description: \"Motion follows the recipe reduced-motion fallback.\",\n },\n {\n id: \"accessibility\",\n label: \"Accessibility\",\n description: \"Names, states, relationships, contrast and touch targets are inspectable.\",\n },\n {\n id: \"keyboard\",\n label: \"Keyboard\",\n description: \"Focus order and documented keyboard behavior run as interaction tests.\",\n },\n {\n id: \"native-actions\",\n label: \"Native actions\",\n description: \"Accessible host actions and their state transitions run against the Native renderer.\",\n },\n {\n id: \"platform-parity\",\n label: \"Web / Native parity\",\n description: \"Adaptive renderers preserve intent while using platform-native behavior.\",\n },\n] as const satisfies readonly ShowcaseScenario[];\n\nconst plannedRequirements = [\"contract\"] as const satisfies readonly ShowcaseScenarioId[];\nconst rendererRequirements = [\n \"default\",\n \"dark\",\n \"long-copy\",\n \"large-text\",\n \"rtl\",\n \"reduced-motion\",\n \"accessibility\",\n] as const satisfies readonly Exclude<ShowcaseScenarioId, \"contract\">[];\n\nexport type ShowcaseComponentEntry = Readonly<{\n storyId: string;\n component: ComponentCatalogEntry;\n /** Resolved Web/Native renderer maturity, including legacy catalog inputs. */\n surfaceMaturity: ComponentSurfaceMaturity;\n requirements: readonly ShowcaseEvidenceRequirement[];\n requiredScenarios: readonly ShowcaseScenarioId[];\n requiredSurfaces: readonly ShowcaseSurface[];\n}>;\n\nexport type ShowcaseEvidenceRequirement = Readonly<{\n surface: ShowcaseSurface;\n scenarios: readonly ShowcaseScenarioId[];\n}>;\n\nexport type ShowcaseEvidenceEntry = Readonly<{\n storyId: string;\n surface: ShowcaseSurface;\n scenarios: readonly ShowcaseScenarioId[];\n}>;\n\nexport type ShowcaseCoverageEntry = Readonly<{\n storyId: string;\n component: ComponentCatalogEntry;\n missingEvidence: readonly Readonly<{\n surface: ShowcaseSurface;\n scenario: ShowcaseScenarioId;\n }>[];\n missingSurfaces: readonly ShowcaseSurface[];\n missingScenarios: readonly ShowcaseScenarioId[];\n complete: boolean;\n}>;\n\nexport function getShowcaseStoryId(entry: ComponentCatalogEntry): string {\n const slug = entry.name\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .replace(/[^a-zA-Z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\")\n .toLowerCase();\n return `${entry.category}/${slug}`;\n}\n\nexport function getRequiredShowcaseScenarios(\n entry: ComponentCatalogEntry,\n): readonly ShowcaseScenarioId[] {\n const activeSurfaces = ([\"web\", \"native\"] as const).filter((surface) =>\n isRendererMaturity(getComponentSurfaceStatus(entry, surface)),\n );\n if (activeSurfaces.length === 0) return plannedRequirements;\n\n const rendererScenarios = [...new Set(activeSurfaces.flatMap((surface) =>\n getRendererShowcaseScenarios(entry, surface),\n ))];\n return [\n \"contract\",\n ...rendererScenarios,\n ];\n}\n\nfunction isRendererMaturity(\n status: ComponentSurfaceStatus,\n): status is Extract<ComponentStatus, \"stable\" | \"beta\"> {\n return status === \"stable\" || status === \"beta\";\n}\n\n// These outputs expose no consumer-authored visible prose; default and accessibility proofs remain required.\nconst textlessComponentNames: ReadonlySet<string> = new Set([\n \"Icon\", \"IconButton\", \"Skeleton\", \"Spinner\", \"ThinkingOrb\", \"Divider\",\n \"Avatar\", \"Asset\", \"CounterBadge\", \"Image\", \"VisuallyHidden\", \"Pagination\",\n // These own form/range behavior but no long-form text slot; their child fields\n // and calendar cells own their copy, so duplicating that proof here misattributes it.\n \"Form\", \"DateRangePicker\",\n]);\n\n// These behaviors expose no interaction on either rendered surface.\nconst semanticOnlyBehaviors: ReadonlySet<NonNullable<ComponentCatalogEntry[\"behavior\"]>> = new Set([\n \"top\", \"authScreen\", \"heading\", \"bottomInfo\", \"nativePlatform\", \"textFormat\", \"asset\",\n]);\n\n// Keep surface capability lookup local: importing behaviorRegistry here would pull every\n// behavior contract into the lightweight showcase/evidence entry points. The alternative was\n// importing that registry; showcase.test.ts cross-checks these names against it to prevent drift.\nconst webKeyboardBehaviors: ReadonlySet<BehaviorName> = new Set([\n \"agreement\", \"alertDialog\", \"anchor\", \"authProviderButton\", \"bottomNavigation\", \"breadcrumb\",\n \"calendar\", \"carousel\", \"checkbox\", \"checkboxGroup\", \"chip\", \"collapsible\", \"combobox\",\n \"commandPalette\", \"contextMenu\", \"dataTable\", \"datePicker\", \"dateRange\", \"dialog\",\n \"disclosureGroup\", \"field\", \"filePicker\", \"floatingActionButton\", \"form\", \"layout\", \"link\",\n \"loadMore\", \"menu\", \"menubar\", \"numberField\", \"otpField\", \"pagination\", \"passwordField\",\n \"popover\", \"radioGroup\", \"searchField\", \"segmentedControl\", \"select\", \"sheet\", \"sidePanel\",\n \"sidebar\", \"skipNav\", \"slider\", \"splitter\", \"switch\", \"tabs\", \"tagsInput\", \"toast\",\n \"toggleGroup\", \"tooltip\", \"tour\", \"transferList\", \"tree\", \"uploadItem\",\n]);\nconst nativeActionBehaviors: ReadonlySet<BehaviorName> = new Set([\n \"agreement\", \"alertDialog\", \"authProviderButton\", \"bottomNavigation\", \"calendar\", \"carousel\",\n \"checkbox\", \"checkboxGroup\", \"chip\", \"collapsible\", \"combobox\", \"datePicker\", \"dateRange\",\n \"dialog\", \"disclosureGroup\", \"field\", \"filePicker\", \"floatingActionButton\", \"form\", \"link\",\n \"loadMore\", \"menu\", \"numberField\", \"otpField\", \"passwordField\", \"radioGroup\", \"searchField\",\n \"segmentedControl\", \"select\", \"sheet\", \"slider\", \"switch\", \"tabs\", \"tagsInput\", \"toast\",\n \"toggleGroup\", \"transferList\", \"uploadItem\",\n]);\n\nfunction getRendererShowcaseScenarios(\n entry: ComponentCatalogEntry,\n surface?: \"web\" | \"native\",\n): readonly Exclude<ShowcaseScenarioId, \"contract\">[] {\n const requirements: Exclude<ShowcaseScenarioId, \"contract\">[] = rendererRequirements.filter(\n (scenario) => scenario !== \"long-copy\" || !textlessComponentNames.has(entry.name),\n );\n if (entry.behavior && !semanticOnlyBehaviors.has(entry.behavior)) {\n // Web owns DOM key bindings; Native declares host accessibility actions instead, so Pressable tests must not claim physical keyboard proof.\n if (surface === \"web\" && webKeyboardBehaviors.has(entry.behavior)) requirements.push(\"keyboard\");\n if (surface === \"native\" && nativeActionBehaviors.has(entry.behavior)) requirements.push(\"native-actions\");\n }\n return requirements;\n}\n\nexport function getRequiredShowcaseSurfaces(\n entry: ComponentCatalogEntry,\n): readonly ShowcaseSurface[] {\n return getRequiredShowcaseEvidence(entry).map(({ surface }) => surface);\n}\n\nexport function getRequiredShowcaseEvidence(\n entry: ComponentCatalogEntry,\n): readonly ShowcaseEvidenceRequirement[] {\n const requirements: ShowcaseEvidenceRequirement[] = [\n { surface: \"contract\", scenarios: [\"contract\"] },\n ];\n const activeSurfaces = ([\"web\", \"native\"] as const).filter((surface) =>\n isRendererMaturity(getComponentSurfaceStatus(entry, surface)),\n );\n for (const surface of activeSurfaces) {\n requirements.push({ surface, scenarios: getRendererShowcaseScenarios(entry, surface) });\n }\n return requirements;\n}\n\nexport function createShowcaseManifest(\n entries: readonly ComponentCatalogEntry[] = componentCatalog,\n): readonly ShowcaseComponentEntry[] {\n return entries.map((component) => ({\n storyId: getShowcaseStoryId(component),\n component,\n surfaceMaturity: {\n web: getComponentSurfaceStatus(component, \"web\"),\n native: getComponentSurfaceStatus(component, \"native\"),\n },\n requirements: getRequiredShowcaseEvidence(component),\n requiredScenarios: getRequiredShowcaseScenarios(component),\n requiredSurfaces: getRequiredShowcaseSurfaces(component),\n }));\n}\n\nexport const showcaseManifest = createShowcaseManifest();\n\nexport function createShowcaseCoverage(\n evidence: readonly ShowcaseEvidenceEntry[],\n manifest: readonly ShowcaseComponentEntry[] = showcaseManifest,\n): readonly ShowcaseCoverageEntry[] {\n return manifest.map(({ storyId, component, requirements }) => {\n const componentEvidence = evidence.filter((entry) => entry.storyId === storyId);\n const missingEvidence = requirements.flatMap(({ surface, scenarios }) =>\n scenarios\n .filter(\n (scenario) =>\n !componentEvidence.some(\n (entry) => entry.surface === surface && entry.scenarios.includes(scenario),\n ),\n )\n .map((scenario) => ({ surface, scenario })),\n );\n const missingSurfaces = [...new Set(missingEvidence.map(({ surface }) => surface))];\n const missingScenarios = [...new Set(missingEvidence.map(({ scenario }) => scenario))];\n return {\n storyId,\n component,\n missingEvidence,\n missingSurfaces,\n missingScenarios,\n complete: missingSurfaces.length === 0 && missingScenarios.length === 0,\n };\n });\n}\n\nexport function assertShowcaseCoverage(\n evidence: readonly ShowcaseEvidenceEntry[],\n manifest: readonly ShowcaseComponentEntry[] = showcaseManifest,\n): void {\n const missing = createShowcaseCoverage(evidence, manifest).filter(({ complete }) => !complete);\n if (missing.length === 0) return;\n const details = missing\n .map(({ storyId, missingEvidence }) =>\n `${storyId} (${missingEvidence.map(({ surface, scenario }) => `${surface}/${scenario}`).join(\", \") || \"none\"})`,\n )\n .join(\"\\n\");\n throw new Error(`Showcase evidence is incomplete:\\n${details}`);\n}\n\nexport function summarizeShowcaseMaturity(\n entries: readonly ComponentCatalogEntry[] = componentCatalog,\n): Readonly<Record<ComponentStatus, number>> {\n return entries.reduce<Record<ComponentStatus, number>>(\n (summary, entry) => {\n summary[entry.status] += 1;\n return summary;\n },\n { stable: 0, beta: 0, planned: 0, deprecated: 0 },\n );\n}\n"]}
package/dist/sidebar.d.ts CHANGED
@@ -51,7 +51,7 @@ export declare const sidebarRecipe: {
51
51
  };
52
52
  readonly background: Readonly<{
53
53
  source: "theme";
54
- key: "surfaceAlt";
54
+ key: "bg";
55
55
  alpha?: number;
56
56
  }>;
57
57
  readonly border: Readonly<{
@@ -97,7 +97,7 @@ export declare const sidebarRecipe: {
97
97
  };
98
98
  readonly highlightedBackground: Readonly<{
99
99
  source: "theme";
100
- key: "text";
100
+ key: "bg";
101
101
  alpha?: number;
102
102
  }>;
103
103
  readonly focus: {
@@ -111,7 +111,7 @@ export declare const sidebarRecipe: {
111
111
  };
112
112
  readonly selectedBackground: Readonly<{
113
113
  source: "theme";
114
- key: "primary";
114
+ key: "bg";
115
115
  alpha?: number;
116
116
  }>;
117
117
  readonly selectedIndicator: Readonly<{
package/dist/sidebar.js CHANGED
@@ -50,7 +50,7 @@ export const sidebarRecipe = {
50
50
  slots: ["root", "group", "groupLabel", "item", "label", "badge", "toggle"],
51
51
  defaults: sidebarDefaults,
52
52
  widths: { expanded: 260, collapsed: 72 },
53
- background: semanticColors.surface.sunken,
53
+ background: semanticColors.canvas,
54
54
  border: semanticColors.border.default,
55
55
  borderWidth: stroke.default,
56
56
  paddingVertical: spacing.sm,
@@ -1 +1 @@
1
- {"version":3,"file":"sidebar.js","sourceRoot":"","sources":["../src/sidebar.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BehaviorContract } from \"./behaviors.js\";\nimport type { ColorReference } from \"./color-references.js\";\nimport { collectionItemContract, focusIndicatorContract } from \"./component-contracts.js\";\nimport type { LinkDestination } from \"./link.js\";\nimport { control, radius, spacing, stroke } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * 데스크톱 웹의 세로 내비게이션. 관리자 화면·문서·작업 도구처럼 넓은 화면에서 좌우로\n * 나뉜 셸의 왼쪽이다.\n *\n * `BottomNavigation`과 같은 문제를 푸는 다른 표면이 아니다 — 두 축이 다르다.\n * BottomNavigation은 **3~5개 최상위 목적지**를 모바일 하단에 고정하고 그룹이 없다.\n * Sidebar는 **그룹이 있는 긴 목록**이고 접힌다. 한 컴포넌트의 `orientation` 축으로\n * 합치면 \"그룹이 있는 하단 탭바\"라는 존재하지 않는 조합이 타입에 생긴다.\n *\n * `Layout`의 sidebar 영역과도 다르다. Layout은 **자리**(폭·순서·반응형 규칙)를 갖고\n * 내용을 모른다. 이 계약은 그 자리에 들어가는 **내비게이션 자체**다.\n */\nexport type SidebarItemDescriptor<Id extends string = string> = Readonly<{\n id: Id;\n label: string;\n destination?: LinkDestination;\n /** 접힌 상태에서도 보이는 상태 수치. Badge/CounterBadge가 그린다. */\n badgeCount?: number;\n disabled?: boolean;\n}>;\n\nexport type SidebarGroupDescriptor<\n Id extends string = string,\n GroupId extends string = string,\n> = Readonly<{\n id: GroupId;\n /** 그룹 제목. 없으면 구분선만 있는 묶음이다. */\n label?: string;\n items: readonly SidebarItemDescriptor<Id>[];\n}>;\n\nexport type SidebarDescriptor<\n Id extends string = string,\n GroupId extends string = string,\n> = Readonly<{\n accessibilityLabel: string;\n groups: readonly SidebarGroupDescriptor<Id, GroupId>[];\n /** 현재 위치. 링크 내비게이션의 `aria-current`가 된다. */\n currentId: Id | null;\n}>;\n\nfunction assertNonEmpty(value: string, field: string): void {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`Sidebar ${field} must not be empty`);\n }\n}\n\nexport function validateSidebarDescriptor<Id extends string, GroupId extends string>(\n descriptor: SidebarDescriptor<Id, GroupId>,\n): void {\n assertNonEmpty(descriptor.accessibilityLabel, \"accessibilityLabel\");\n if (!Array.isArray(descriptor.groups) || descriptor.groups.length === 0) {\n throw new RangeError(\"Sidebar must contain at least one group\");\n }\n const itemIds = new Set<Id>();\n const groupIds = new Set<GroupId>();\n for (const group of descriptor.groups) {\n assertNonEmpty(group.id, \"group id\");\n if (groupIds.has(group.id)) throw new TypeError(`Duplicate Sidebar group id: ${group.id}`);\n groupIds.add(group.id);\n if (group.label !== undefined) assertNonEmpty(group.label, `group ${group.id} label`);\n if (!Array.isArray(group.items) || group.items.length === 0) {\n throw new RangeError(`Sidebar group ${group.id} must contain at least one item`);\n }\n for (const item of group.items) {\n assertNonEmpty(item.id, \"item id\");\n assertNonEmpty(item.label, `item ${item.id} label`);\n if (itemIds.has(item.id)) throw new TypeError(`Duplicate Sidebar item id: ${item.id}`);\n itemIds.add(item.id);\n if (item.badgeCount !== undefined && (!Number.isInteger(item.badgeCount) || item.badgeCount < 0)) {\n throw new RangeError(`Sidebar item ${item.id} badgeCount must be a non-negative integer`);\n }\n }\n }\n if (descriptor.currentId !== null && !itemIds.has(descriptor.currentId)) {\n throw new RangeError(`Sidebar currentId does not match any item: ${String(descriptor.currentId)}`);\n }\n}\n\n/**\n * 접힘은 **표시 밀도**이지 내용이 아니다. 접혀도 항목은 모두 남고 라벨만 숨는다 —\n * 접을 때 항목을 빼면 \"접었더니 메뉴가 사라졌다\"가 되고, 그건 다른 컴포넌트다.\n * 라벨이 숨으면 아이콘만으로는 이름을 알 수 없으므로 renderer가 접근성 이름을 유지한다.\n */\nexport const sidebarDefaults = {\n collapsed: false,\n} as const satisfies Readonly<{ collapsed: boolean }>;\n\nexport const sidebarRecipe = {\n slots: [\"root\", \"group\", \"groupLabel\", \"item\", \"label\", \"badge\", \"toggle\"] as const,\n defaults: sidebarDefaults,\n widths: { expanded: 260, collapsed: 72 },\n background: semanticColors.surface.sunken,\n border: semanticColors.border.default,\n borderWidth: stroke.default,\n paddingVertical: spacing.sm,\n paddingHorizontal: spacing.xs,\n gap: spacing.xxs,\n groupGap: spacing.md,\n groupLabel: {\n color: semanticColors.content.secondary,\n textVariant: \"label\" as const,\n paddingHorizontal: spacing.sm,\n paddingVertical: spacing.xxs,\n },\n item: collectionItemContract,\n itemRadius: radius.md,\n itemMinHeight: control.minTouchTarget,\n states: { focus: focusIndicatorContract },\n} as const satisfies {\n slots: readonly [\"root\", \"group\", \"groupLabel\", \"item\", \"label\", \"badge\", \"toggle\"];\n defaults: { collapsed: boolean };\n widths: { expanded: number; collapsed: number };\n background: ColorReference;\n border: ColorReference;\n borderWidth: number;\n paddingVertical: number;\n paddingHorizontal: number;\n gap: number;\n groupGap: number;\n groupLabel: { color: ColorReference; textVariant: \"label\"; paddingHorizontal: number; paddingVertical: number };\n item: typeof collectionItemContract;\n itemRadius: number;\n itemMinHeight: number;\n states: { focus: typeof focusIndicatorContract };\n};\n\nexport const sidebarBehavior = {\n controlled: [\"collapsed\", \"defaultCollapsed\", \"onCollapsedChange\"],\n inputs: [\"groups\", \"currentId\", \"accessibilityLabel\"],\n events: [\"onNavigate\"],\n defaults: sidebarDefaults,\n stateAxes: {\n availability: [\"enabled\", \"disabled\"],\n value: [\"selected\"],\n interaction: [\"idle\", \"hover\", \"focusVisible\", \"pressed\"],\n },\n web: {\n roles: [\"navigation\", \"list\", \"listitem\", \"link\", \"button\"],\n keyboard: [\"Tab\", \"Enter\"],\n /** Each item is a link in document order; no roving focus and no arrow-key grid. */\n focus: \"native\",\n },\n /** Web-only: a phone uses BottomNavigation, and a native tablet split view is a navigator concern. */\n native: { roles: [], states: [], actions: [] },\n scenarios: [\n \"the-current-item-is-announced-with-aria-current-not-only-painted\",\n \"collapsing-hides-labels-but-keeps-every-item-and-its-accessible-name\",\n \"groups-are-real-list-groupings-with-their-own-label-not-visual-dividers-only\",\n \"an-item-badge-stays-visible-while-collapsed-because-it-is-the-reason-to-look\",\n \"bottom-navigation-owns-the-three-to-five-destination-mobile-case-this-contract-never-adds-it\",\n \"layout-owns-where-the-sidebar-sits-this-contract-owns-what-is-inside-it\",\n ],\n} as const satisfies BehaviorContract;\n"]}
1
+ {"version":3,"file":"sidebar.js","sourceRoot":"","sources":["../src/sidebar.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BehaviorContract } from \"./behaviors.js\";\nimport type { ColorReference } from \"./color-references.js\";\nimport { collectionItemContract, focusIndicatorContract } from \"./component-contracts.js\";\nimport type { LinkDestination } from \"./link.js\";\nimport { control, radius, spacing, stroke } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * 데스크톱 웹의 세로 내비게이션. 관리자 화면·문서·작업 도구처럼 넓은 화면에서 좌우로\n * 나뉜 셸의 왼쪽이다.\n *\n * `BottomNavigation`과 같은 문제를 푸는 다른 표면이 아니다 — 두 축이 다르다.\n * BottomNavigation은 **3~5개 최상위 목적지**를 모바일 하단에 고정하고 그룹이 없다.\n * Sidebar는 **그룹이 있는 긴 목록**이고 접힌다. 한 컴포넌트의 `orientation` 축으로\n * 합치면 \"그룹이 있는 하단 탭바\"라는 존재하지 않는 조합이 타입에 생긴다.\n *\n * `Layout`의 sidebar 영역과도 다르다. Layout은 **자리**(폭·순서·반응형 규칙)를 갖고\n * 내용을 모른다. 이 계약은 그 자리에 들어가는 **내비게이션 자체**다.\n */\nexport type SidebarItemDescriptor<Id extends string = string> = Readonly<{\n id: Id;\n label: string;\n destination?: LinkDestination;\n /** 접힌 상태에서도 보이는 상태 수치. Badge/CounterBadge가 그린다. */\n badgeCount?: number;\n disabled?: boolean;\n}>;\n\nexport type SidebarGroupDescriptor<\n Id extends string = string,\n GroupId extends string = string,\n> = Readonly<{\n id: GroupId;\n /** 그룹 제목. 없으면 구분선만 있는 묶음이다. */\n label?: string;\n items: readonly SidebarItemDescriptor<Id>[];\n}>;\n\nexport type SidebarDescriptor<\n Id extends string = string,\n GroupId extends string = string,\n> = Readonly<{\n accessibilityLabel: string;\n groups: readonly SidebarGroupDescriptor<Id, GroupId>[];\n /** 현재 위치. 링크 내비게이션의 `aria-current`가 된다. */\n currentId: Id | null;\n}>;\n\nfunction assertNonEmpty(value: string, field: string): void {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`Sidebar ${field} must not be empty`);\n }\n}\n\nexport function validateSidebarDescriptor<Id extends string, GroupId extends string>(\n descriptor: SidebarDescriptor<Id, GroupId>,\n): void {\n assertNonEmpty(descriptor.accessibilityLabel, \"accessibilityLabel\");\n if (!Array.isArray(descriptor.groups) || descriptor.groups.length === 0) {\n throw new RangeError(\"Sidebar must contain at least one group\");\n }\n const itemIds = new Set<Id>();\n const groupIds = new Set<GroupId>();\n for (const group of descriptor.groups) {\n assertNonEmpty(group.id, \"group id\");\n if (groupIds.has(group.id)) throw new TypeError(`Duplicate Sidebar group id: ${group.id}`);\n groupIds.add(group.id);\n if (group.label !== undefined) assertNonEmpty(group.label, `group ${group.id} label`);\n if (!Array.isArray(group.items) || group.items.length === 0) {\n throw new RangeError(`Sidebar group ${group.id} must contain at least one item`);\n }\n for (const item of group.items) {\n assertNonEmpty(item.id, \"item id\");\n assertNonEmpty(item.label, `item ${item.id} label`);\n if (itemIds.has(item.id)) throw new TypeError(`Duplicate Sidebar item id: ${item.id}`);\n itemIds.add(item.id);\n if (item.badgeCount !== undefined && (!Number.isInteger(item.badgeCount) || item.badgeCount < 0)) {\n throw new RangeError(`Sidebar item ${item.id} badgeCount must be a non-negative integer`);\n }\n }\n }\n if (descriptor.currentId !== null && !itemIds.has(descriptor.currentId)) {\n throw new RangeError(`Sidebar currentId does not match any item: ${String(descriptor.currentId)}`);\n }\n}\n\n/**\n * 접힘은 **표시 밀도**이지 내용이 아니다. 접혀도 항목은 모두 남고 라벨만 숨는다 —\n * 접을 때 항목을 빼면 \"접었더니 메뉴가 사라졌다\"가 되고, 그건 다른 컴포넌트다.\n * 라벨이 숨으면 아이콘만으로는 이름을 알 수 없으므로 renderer가 접근성 이름을 유지한다.\n */\nexport const sidebarDefaults = {\n collapsed: false,\n} as const satisfies Readonly<{ collapsed: boolean }>;\n\nexport const sidebarRecipe = {\n slots: [\"root\", \"group\", \"groupLabel\", \"item\", \"label\", \"badge\", \"toggle\"] as const,\n defaults: sidebarDefaults,\n widths: { expanded: 260, collapsed: 72 },\n background: semanticColors.canvas,\n border: semanticColors.border.default,\n borderWidth: stroke.default,\n paddingVertical: spacing.sm,\n paddingHorizontal: spacing.xs,\n gap: spacing.xxs,\n groupGap: spacing.md,\n groupLabel: {\n color: semanticColors.content.secondary,\n textVariant: \"label\" as const,\n paddingHorizontal: spacing.sm,\n paddingVertical: spacing.xxs,\n },\n item: collectionItemContract,\n itemRadius: radius.md,\n itemMinHeight: control.minTouchTarget,\n states: { focus: focusIndicatorContract },\n} as const satisfies {\n slots: readonly [\"root\", \"group\", \"groupLabel\", \"item\", \"label\", \"badge\", \"toggle\"];\n defaults: { collapsed: boolean };\n widths: { expanded: number; collapsed: number };\n background: ColorReference;\n border: ColorReference;\n borderWidth: number;\n paddingVertical: number;\n paddingHorizontal: number;\n gap: number;\n groupGap: number;\n groupLabel: { color: ColorReference; textVariant: \"label\"; paddingHorizontal: number; paddingVertical: number };\n item: typeof collectionItemContract;\n itemRadius: number;\n itemMinHeight: number;\n states: { focus: typeof focusIndicatorContract };\n};\n\nexport const sidebarBehavior = {\n controlled: [\"collapsed\", \"defaultCollapsed\", \"onCollapsedChange\"],\n inputs: [\"groups\", \"currentId\", \"accessibilityLabel\"],\n events: [\"onNavigate\"],\n defaults: sidebarDefaults,\n stateAxes: {\n availability: [\"enabled\", \"disabled\"],\n value: [\"selected\"],\n interaction: [\"idle\", \"hover\", \"focusVisible\", \"pressed\"],\n },\n web: {\n roles: [\"navigation\", \"list\", \"listitem\", \"link\", \"button\"],\n keyboard: [\"Tab\", \"Enter\"],\n /** Each item is a link in document order; no roving focus and no arrow-key grid. */\n focus: \"native\",\n },\n /** Web-only: a phone uses BottomNavigation, and a native tablet split view is a navigator concern. */\n native: { roles: [], states: [], actions: [] },\n scenarios: [\n \"the-current-item-is-announced-with-aria-current-not-only-painted\",\n \"collapsing-hides-labels-but-keeps-every-item-and-its-accessible-name\",\n \"groups-are-real-list-groupings-with-their-own-label-not-visual-dividers-only\",\n \"an-item-badge-stays-visible-while-collapsed-because-it-is-the-reason-to-look\",\n \"bottom-navigation-owns-the-three-to-five-destination-mobile-case-this-contract-never-adds-it\",\n \"layout-owns-where-the-sidebar-sits-this-contract-owns-what-is-inside-it\",\n ],\n} as const satisfies BehaviorContract;\n"]}
package/dist/tag.d.ts CHANGED
@@ -33,7 +33,7 @@ export declare const tagRecipe: {
33
33
  readonly neutral: {
34
34
  readonly background: Readonly<{
35
35
  source: "theme";
36
- key: "surfaceAlt";
36
+ key: "bg";
37
37
  alpha?: number;
38
38
  }>;
39
39
  readonly content: Readonly<{
@@ -46,7 +46,7 @@ export declare const tagRecipe: {
46
46
  readonly brand: {
47
47
  readonly background: Readonly<{
48
48
  source: "theme";
49
- key: "surfaceAccent";
49
+ key: "bg";
50
50
  alpha?: number;
51
51
  }>;
52
52
  readonly content: Readonly<{
@@ -58,8 +58,8 @@ export declare const tagRecipe: {
58
58
  };
59
59
  readonly info: {
60
60
  readonly background: Readonly<{
61
- source: "accent";
62
- key: "info";
61
+ source: "theme";
62
+ key: "bg";
63
63
  alpha?: number;
64
64
  }>;
65
65
  readonly content: Readonly<{
package/dist/tag.js CHANGED
@@ -41,17 +41,17 @@ export const tagRecipe = {
41
41
  defaults: tagDefaults,
42
42
  tones: {
43
43
  neutral: {
44
- background: semanticColors.surface.sunken,
44
+ background: semanticColors.canvas,
45
45
  content: semanticColors.content.secondary,
46
46
  border: null,
47
47
  },
48
48
  brand: {
49
- background: semanticColors.surface.brand,
49
+ background: semanticColors.canvas,
50
50
  content: semanticColors.content.brand,
51
51
  border: null,
52
52
  },
53
53
  info: {
54
- background: semanticColors.feedback.info.badgeBackground,
54
+ background: semanticColors.canvas,
55
55
  content: semanticColors.feedback.info.foreground,
56
56
  border: semanticColors.feedback.info.border,
57
57
  },
package/dist/tag.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tag.js","sourceRoot":"","sources":["../src/tag.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n resolveColorReference,\n type ColorReferencePalette,\n} from \"./color-references.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\nimport { fontWeight, spacing, stroke } from \"./foundations.js\";\n\nexport type TagTone = \"neutral\" | \"info\" | \"success\" | \"attention\" | \"brand\";\n\nexport const tagDefaults = {\n tone: \"neutral\",\n} as const satisfies Readonly<{ tone: TagTone }>;\n\n/**\n * A single piece of static metadata (a position, a grade, a season). Tag has\n * no interaction axis at all — no selected, no pressed, no closable. Removing\n * or toggling a tag is deselection, which belongs to `Chip`.\n */\nexport type TagDescriptor = Readonly<{\n label: string;\n tone?: TagTone;\n}>;\n\nexport type ResolvedTagDescriptor = Readonly<{\n label: string;\n tone: TagTone;\n}>;\n\nfunction assertCopy(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Tag ${field} must not be empty`);\n }\n}\n\nconst supportedTones: readonly TagTone[] = [\n \"neutral\",\n \"info\",\n \"success\",\n \"attention\",\n \"brand\",\n];\n\nexport function validateTagDescriptor(descriptor: TagDescriptor): void {\n assertCopy(descriptor.label, \"label\");\n if (\n descriptor.tone !== undefined &&\n !supportedTones.includes(descriptor.tone)\n ) {\n throw new TypeError(`Unsupported Tag tone: ${String(descriptor.tone)}`);\n }\n}\n\nexport function resolveTagDescriptor(\n descriptor: TagDescriptor,\n): ResolvedTagDescriptor {\n validateTagDescriptor(descriptor);\n return {\n label: descriptor.label,\n tone: descriptor.tone ?? tagDefaults.tone,\n };\n}\n\n/**\n * Rectangular, not the pill `radius.full` that `chipRecipe` and `badgeRecipe`\n * use, so a static Tag never reads as a pressable Chip or a status Badge at a\n * glance. No `states` and no `focus` contract: nothing here is ever pressed\n * or focused.\n */\nexport const tagRecipe = {\n slots: [\"root\", \"label\"] as const,\n defaults: tagDefaults,\n tones: {\n neutral: {\n background: semanticColors.surface.sunken,\n content: semanticColors.content.secondary,\n border: null,\n },\n brand: {\n background: semanticColors.surface.brand,\n content: semanticColors.content.brand,\n border: null,\n },\n info: {\n background: semanticColors.feedback.info.badgeBackground,\n content: semanticColors.feedback.info.foreground,\n border: semanticColors.feedback.info.border,\n },\n success: {\n background: semanticColors.feedback.success.badgeBackground,\n content: semanticColors.feedback.success.foreground,\n border: semanticColors.feedback.success.border,\n },\n attention: {\n background: semanticColors.feedback.attention.badgeBackground,\n content: semanticColors.feedback.attention.foreground,\n border: semanticColors.feedback.attention.border,\n },\n },\n size: {\n // Static metadata still participates in Dynamic Type. A fixed 20px frame\n // clips the label at accessibility sizes; minHeight preserves the compact\n // base shape while allowing the intrinsic line box to grow.\n minHeight: 20,\n paddingHorizontal: spacing.xxs,\n gap: spacing.xxs,\n textVariant: \"caption\",\n fontWeight: fontWeight.semibold,\n },\n radius: \"sm\",\n borderWidth: stroke.default,\n} as const;\n\nexport type ResolvedTagPresentation = Readonly<{\n background: string;\n content: string;\n border: string | null;\n}>;\n\n/** Resolve the same Tag recipe for non-CSS renderers. */\nexport function resolveTagPresentation(\n tone: TagTone,\n palette: ColorReferencePalette,\n): ResolvedTagPresentation {\n const contract = tagRecipe.tones[tone];\n return {\n background: resolveColorReference(contract.background, palette),\n content: resolveColorReference(contract.content, palette),\n border:\n contract.border === null\n ? null\n : resolveColorReference(contract.border, palette),\n };\n}\n"]}
1
+ {"version":3,"file":"tag.js","sourceRoot":"","sources":["../src/tag.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n resolveColorReference,\n type ColorReferencePalette,\n} from \"./color-references.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\nimport { fontWeight, spacing, stroke } from \"./foundations.js\";\n\nexport type TagTone = \"neutral\" | \"info\" | \"success\" | \"attention\" | \"brand\";\n\nexport const tagDefaults = {\n tone: \"neutral\",\n} as const satisfies Readonly<{ tone: TagTone }>;\n\n/**\n * A single piece of static metadata (a position, a grade, a season). Tag has\n * no interaction axis at all — no selected, no pressed, no closable. Removing\n * or toggling a tag is deselection, which belongs to `Chip`.\n */\nexport type TagDescriptor = Readonly<{\n label: string;\n tone?: TagTone;\n}>;\n\nexport type ResolvedTagDescriptor = Readonly<{\n label: string;\n tone: TagTone;\n}>;\n\nfunction assertCopy(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Tag ${field} must not be empty`);\n }\n}\n\nconst supportedTones: readonly TagTone[] = [\n \"neutral\",\n \"info\",\n \"success\",\n \"attention\",\n \"brand\",\n];\n\nexport function validateTagDescriptor(descriptor: TagDescriptor): void {\n assertCopy(descriptor.label, \"label\");\n if (\n descriptor.tone !== undefined &&\n !supportedTones.includes(descriptor.tone)\n ) {\n throw new TypeError(`Unsupported Tag tone: ${String(descriptor.tone)}`);\n }\n}\n\nexport function resolveTagDescriptor(\n descriptor: TagDescriptor,\n): ResolvedTagDescriptor {\n validateTagDescriptor(descriptor);\n return {\n label: descriptor.label,\n tone: descriptor.tone ?? tagDefaults.tone,\n };\n}\n\n/**\n * Rectangular, not the pill `radius.full` that `chipRecipe` and `badgeRecipe`\n * use, so a static Tag never reads as a pressable Chip or a status Badge at a\n * glance. No `states` and no `focus` contract: nothing here is ever pressed\n * or focused.\n */\nexport const tagRecipe = {\n slots: [\"root\", \"label\"] as const,\n defaults: tagDefaults,\n tones: {\n neutral: {\n background: semanticColors.canvas,\n content: semanticColors.content.secondary,\n border: null,\n },\n brand: {\n background: semanticColors.canvas,\n content: semanticColors.content.brand,\n border: null,\n },\n info: {\n background: semanticColors.canvas,\n content: semanticColors.feedback.info.foreground,\n border: semanticColors.feedback.info.border,\n },\n success: {\n background: semanticColors.feedback.success.badgeBackground,\n content: semanticColors.feedback.success.foreground,\n border: semanticColors.feedback.success.border,\n },\n attention: {\n background: semanticColors.feedback.attention.badgeBackground,\n content: semanticColors.feedback.attention.foreground,\n border: semanticColors.feedback.attention.border,\n },\n },\n size: {\n // Static metadata still participates in Dynamic Type. A fixed 20px frame\n // clips the label at accessibility sizes; minHeight preserves the compact\n // base shape while allowing the intrinsic line box to grow.\n minHeight: 20,\n paddingHorizontal: spacing.xxs,\n gap: spacing.xxs,\n textVariant: \"caption\",\n fontWeight: fontWeight.semibold,\n },\n radius: \"sm\",\n borderWidth: stroke.default,\n} as const;\n\nexport type ResolvedTagPresentation = Readonly<{\n background: string;\n content: string;\n border: string | null;\n}>;\n\n/** Resolve the same Tag recipe for non-CSS renderers. */\nexport function resolveTagPresentation(\n tone: TagTone,\n palette: ColorReferencePalette,\n): ResolvedTagPresentation {\n const contract = tagRecipe.tones[tone];\n return {\n background: resolveColorReference(contract.background, palette),\n content: resolveColorReference(contract.content, palette),\n border:\n contract.border === null\n ? null\n : resolveColorReference(contract.border, palette),\n };\n}\n"]}
@@ -76,12 +76,12 @@ export declare const tagsInputRecipe: {
76
76
  readonly gap: 8;
77
77
  readonly border: Readonly<{
78
78
  source: "theme";
79
- key: "textMuted";
79
+ key: "borderControl";
80
80
  alpha?: number;
81
81
  }>;
82
82
  readonly background: Readonly<{
83
83
  source: "theme";
84
- key: "surface";
84
+ key: "bg";
85
85
  alpha?: number;
86
86
  }>;
87
87
  };
@@ -91,7 +91,7 @@ export declare const tagsInputRecipe: {
91
91
  readonly gap: 4;
92
92
  readonly background: Readonly<{
93
93
  source: "theme";
94
- key: "surfaceAlt";
94
+ key: "bg";
95
95
  alpha?: number;
96
96
  }>;
97
97
  readonly color: Readonly<{
@@ -65,7 +65,7 @@ export const tagsInputRecipe = {
65
65
  minHeight: 28,
66
66
  paddingHorizontal: spacing.xs,
67
67
  gap: spacing.xxs,
68
- background: semanticColors.surface.sunken,
68
+ background: semanticColors.canvas,
69
69
  color: semanticColors.content.body,
70
70
  },
71
71
  /** 삭제 버튼은 태그보다 작아도 되지만 포인터 타깃은 공용 최소값을 지킨다. */
@@ -1 +1 @@
1
- {"version":3,"file":"tags-input.js","sourceRoot":"","sources":["../src/tags-input.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BehaviorContract } from \"./behaviors.js\";\nimport type { ColorReference } from \"./color-references.js\";\nimport { fieldFrameContract, focusIndicatorContract } from \"./component-contracts.js\";\nimport { control, spacing } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * 자유 입력으로 여러 값을 모으는 필드 — 해시태그, 초대할 사람, 검색 필터.\n *\n * Combobox와 다르다. Combobox는 **주어진 목록에서 고른다**. TagsInput은 목록이 없거나\n * 있어도 목록 밖의 값을 만들 수 있다. 그래서 Combobox에 `multiple` 축을 붙이는 대신\n * 별도로 둔다 — 합치면 \"목록에 없는 값을 고를 수 있는 Select\"라는 모순이 생긴다.\n * (제품이 후보 목록을 함께 보여 주고 싶으면 이 필드와 Combobox의 listbox를 조합한다.)\n *\n * 이 계약이 소유하는 것은 **언제 태그가 확정되는가**와 **빈 입력에서 지우면 무엇이\n * 지워지는가** 둘이다. 중복 허용 여부·최대 개수·글자 규칙은 제품 정책이라 판정 결과를\n * 받기만 한다.\n */\nexport type TagsInputCommitKey = \"Enter\" | \"Comma\" | \"Space\" | \"Blur\";\n\n/**\n * 후보 목록. Combobox의 listbox 계약을 **이 필드 안에서** 쓰는 형태다.\n *\n * \"Combobox에 multiple을 붙이자\"가 아니라 여기로 온 이유: Combobox의 값은 목록 안의\n * 키 하나이고 이 필드의 값은 문자열 배열이다. 값 모양이 다른 둘을 한 컴포넌트에 넣으면\n * 모든 소비자가 매번 좁히기를 해야 한다. 자유 입력이 이미 여기 있으므로 후보를 여기에\n * 더하는 쪽이 값 하나로 끝난다.\n *\n * 필터링은 제품이 한다 — 이 계약은 \"무엇을 보여줄지\"가 아니라 \"고른 것을 어떻게\n * 확정할지\"만 안다.\n */\nexport type TagsInputSuggestion = Readonly<{\n id: string;\n label: string;\n /** 확정될 문자열. 생략하면 `label`. */\n value?: string;\n disabled?: boolean;\n}>;\n\n/**\n * 활성 후보를 방향키로 옮긴 결과. 목록 끝에서 순환한다 — 후보가 보통 짧아서 끝에\n * 막히면 반대 끝까지 다시 내려가야 한다.\n */\nexport function resolveTagsInputActiveSuggestion(\n suggestions: readonly TagsInputSuggestion[],\n currentId: string | null,\n direction: \"next\" | \"previous\",\n): string | null {\n const enabled = suggestions.filter((item) => item.disabled !== true);\n if (enabled.length === 0) return null;\n const index = enabled.findIndex((item) => item.id === currentId);\n if (index === -1) return direction === \"next\" ? enabled[0]!.id : enabled.at(-1)!.id;\n const next = direction === \"next\" ? index + 1 : index - 1;\n return enabled[(next + enabled.length) % enabled.length]!.id;\n}\n\nexport const tagsInputBehaviorDefaults = {\n /**\n * Enter만 기본이다. 쉼표·공백은 언어에 따라 값의 일부다(한국어 태그에 공백이 흔하다).\n * 제품이 자기 도메인을 알 때 켠다.\n */\n commitKeys: [\"Enter\"] as readonly TagsInputCommitKey[],\n /** 빈 입력에서 Backspace는 마지막 태그를 **선택**만 한다. 한 번 더 눌러야 지운다. */\n backspaceRemovesLastTag: false,\n} as const;\n\nexport type TagsInputRejectionReason = \"empty\" | \"duplicate\" | \"limit\" | \"invalid\";\n\nexport type TagsInputCommitResult = Readonly<{\n accepted: boolean;\n /** 정리된 값(앞뒤 공백 제거). 거절돼도 제품이 문구에 쓸 수 있게 돌려준다. */\n value: string;\n reason?: TagsInputRejectionReason;\n}>;\n\nexport type TagsInputPolicy = Readonly<{\n /** 같은 값을 다시 넣을 수 있는가. 기본은 막는다. */\n allowDuplicates?: boolean;\n maxTags?: number;\n /** 제품 도메인 규칙. 실패 사유는 `invalid` 하나로 합쳐 돌려준다. */\n isValid?: (value: string) => boolean;\n}>;\n\n/**\n * 태그 하나를 확정할 수 있는지 판정한다. 값을 바꾸지 않고 결과만 돌려준다 —\n * 실제 상태 갱신은 renderer가, 사용자에게 보여 줄 문장은 제품이 만든다.\n */\nexport function resolveTagsInputCommit(\n current: readonly string[],\n rawValue: string,\n policy: TagsInputPolicy = {},\n): TagsInputCommitResult {\n const value = rawValue.trim();\n if (value.length === 0) return { accepted: false, value, reason: \"empty\" };\n if (policy.allowDuplicates !== true && current.includes(value)) {\n return { accepted: false, value, reason: \"duplicate\" };\n }\n if (policy.maxTags !== undefined && current.length >= policy.maxTags) {\n return { accepted: false, value, reason: \"limit\" };\n }\n if (policy.isValid !== undefined && !policy.isValid(value)) {\n return { accepted: false, value, reason: \"invalid\" };\n }\n return { accepted: true, value };\n}\n\n/** 태그 하나를 뺀 목록. 같은 값이 여러 개면 해당 위치 하나만 지운다. */\nexport function removeTagAt(current: readonly string[], index: number): readonly string[] {\n if (!Number.isInteger(index) || index < 0 || index >= current.length) {\n throw new RangeError(`TagsInput index out of range: ${String(index)}`);\n }\n return [...current.slice(0, index), ...current.slice(index + 1)];\n}\n\nexport const tagsInputRecipe = {\n slots: [\"root\", \"frame\", \"tag\", \"remove\", \"input\"] as const,\n defaults: { commitKeys: tagsInputBehaviorDefaults.commitKeys },\n frame: {\n minHeight: fieldFrameContract.minHeight,\n paddingHorizontal: fieldFrameContract.paddingHorizontal,\n paddingVertical: spacing.xxs,\n gap: spacing.xs,\n border: fieldFrameContract.border,\n background: fieldFrameContract.background,\n },\n tag: {\n minHeight: 28,\n paddingHorizontal: spacing.xs,\n gap: spacing.xxs,\n background: semanticColors.surface.sunken,\n color: semanticColors.content.body,\n },\n /** 삭제 버튼은 태그보다 작아도 되지만 포인터 타깃은 공용 최소값을 지킨다. */\n remove: { minTouchTarget: control.minTouchTarget, color: semanticColors.content.secondary },\n selectedTagOutline: semanticColors.border.focus,\n states: { focus: focusIndicatorContract },\n} as const satisfies {\n slots: readonly [\"root\", \"frame\", \"tag\", \"remove\", \"input\"];\n defaults: { commitKeys: readonly TagsInputCommitKey[] };\n frame: {\n minHeight: number;\n paddingHorizontal: number;\n paddingVertical: number;\n gap: number;\n border: ColorReference;\n background: ColorReference;\n };\n tag: { minHeight: number; paddingHorizontal: number; gap: number; background: ColorReference; color: ColorReference };\n remove: { minTouchTarget: number; color: ColorReference };\n selectedTagOutline: ColorReference;\n states: { focus: typeof focusIndicatorContract };\n};\n\nexport const tagsInputBehavior = {\n controlled: [\"tags\", \"defaultTags\", \"onTagsChange\"],\n inputs: [\"policy\", \"commitKeys\", \"label\", \"removeLabel\"],\n /**\n * `onDraftChange`가 없으면 제품이 후보를 거를 수 없다 — 무엇을 입력 중인지 모르기\n * 때문이다. 후보 목록을 계약에 넣는 순간 이 이벤트도 함께 필요하다.\n */\n events: [\"onReject\", \"onDraftChange\"],\n defaults: { backspaceRemovesLastTag: tagsInputBehaviorDefaults.backspaceRemovesLastTag },\n stateAxes: {\n availability: [\"enabled\", \"disabled\"],\n value: [\"empty\", \"filled\"],\n validation: [\"valid\", \"invalid\"],\n },\n web: {\n roles: [\"combobox\", \"listbox\", \"option\", \"button\", \"list\", \"listitem\"],\n keyboard: [\"Enter\", \"Backspace\", \"ArrowUp\", \"ArrowDown\", \"ArrowLeft\", \"ArrowRight\", \"Escape\", \"Tab\"],\n focus: \"native\",\n },\n native: { roles: [\"button\"], states: [\"disabled\"], actions: [\"commit\", \"remove\"] },\n scenarios: [\n \"enter-commits-the-trimmed-value-and-clears-the-input\",\n \"an-empty-or-whitespace-only-value-is-never-committed\",\n \"duplicates-are-rejected-by-default-and-the-reason-is-reported-not-swallowed\",\n \"backspace-in-an-empty-input-selects-the-last-tag-before-a-second-press-removes-it\",\n \"every-tag-carries-its-own-remove-control-with-a-localized-name\",\n \"the-policy-judges-and-the-product-writes-the-sentence-the-user-reads\",\n \"a-candidate-list-lives-in-this-field-because-the-value-shape-is-this-fields-not-comboboxs\",\n \"enter-commits-the-active-candidate-when-one-is-highlighted-and-the-typed-text-otherwise\",\n \"candidate-filtering-belongs-to-the-product-the-contract-only-commits-the-choice\",\n ],\n} as const satisfies BehaviorContract;\n"]}
1
+ {"version":3,"file":"tags-input.js","sourceRoot":"","sources":["../src/tags-input.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BehaviorContract } from \"./behaviors.js\";\nimport type { ColorReference } from \"./color-references.js\";\nimport { fieldFrameContract, focusIndicatorContract } from \"./component-contracts.js\";\nimport { control, spacing } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * 자유 입력으로 여러 값을 모으는 필드 — 해시태그, 초대할 사람, 검색 필터.\n *\n * Combobox와 다르다. Combobox는 **주어진 목록에서 고른다**. TagsInput은 목록이 없거나\n * 있어도 목록 밖의 값을 만들 수 있다. 그래서 Combobox에 `multiple` 축을 붙이는 대신\n * 별도로 둔다 — 합치면 \"목록에 없는 값을 고를 수 있는 Select\"라는 모순이 생긴다.\n * (제품이 후보 목록을 함께 보여 주고 싶으면 이 필드와 Combobox의 listbox를 조합한다.)\n *\n * 이 계약이 소유하는 것은 **언제 태그가 확정되는가**와 **빈 입력에서 지우면 무엇이\n * 지워지는가** 둘이다. 중복 허용 여부·최대 개수·글자 규칙은 제품 정책이라 판정 결과를\n * 받기만 한다.\n */\nexport type TagsInputCommitKey = \"Enter\" | \"Comma\" | \"Space\" | \"Blur\";\n\n/**\n * 후보 목록. Combobox의 listbox 계약을 **이 필드 안에서** 쓰는 형태다.\n *\n * \"Combobox에 multiple을 붙이자\"가 아니라 여기로 온 이유: Combobox의 값은 목록 안의\n * 키 하나이고 이 필드의 값은 문자열 배열이다. 값 모양이 다른 둘을 한 컴포넌트에 넣으면\n * 모든 소비자가 매번 좁히기를 해야 한다. 자유 입력이 이미 여기 있으므로 후보를 여기에\n * 더하는 쪽이 값 하나로 끝난다.\n *\n * 필터링은 제품이 한다 — 이 계약은 \"무엇을 보여줄지\"가 아니라 \"고른 것을 어떻게\n * 확정할지\"만 안다.\n */\nexport type TagsInputSuggestion = Readonly<{\n id: string;\n label: string;\n /** 확정될 문자열. 생략하면 `label`. */\n value?: string;\n disabled?: boolean;\n}>;\n\n/**\n * 활성 후보를 방향키로 옮긴 결과. 목록 끝에서 순환한다 — 후보가 보통 짧아서 끝에\n * 막히면 반대 끝까지 다시 내려가야 한다.\n */\nexport function resolveTagsInputActiveSuggestion(\n suggestions: readonly TagsInputSuggestion[],\n currentId: string | null,\n direction: \"next\" | \"previous\",\n): string | null {\n const enabled = suggestions.filter((item) => item.disabled !== true);\n if (enabled.length === 0) return null;\n const index = enabled.findIndex((item) => item.id === currentId);\n if (index === -1) return direction === \"next\" ? enabled[0]!.id : enabled.at(-1)!.id;\n const next = direction === \"next\" ? index + 1 : index - 1;\n return enabled[(next + enabled.length) % enabled.length]!.id;\n}\n\nexport const tagsInputBehaviorDefaults = {\n /**\n * Enter만 기본이다. 쉼표·공백은 언어에 따라 값의 일부다(한국어 태그에 공백이 흔하다).\n * 제품이 자기 도메인을 알 때 켠다.\n */\n commitKeys: [\"Enter\"] as readonly TagsInputCommitKey[],\n /** 빈 입력에서 Backspace는 마지막 태그를 **선택**만 한다. 한 번 더 눌러야 지운다. */\n backspaceRemovesLastTag: false,\n} as const;\n\nexport type TagsInputRejectionReason = \"empty\" | \"duplicate\" | \"limit\" | \"invalid\";\n\nexport type TagsInputCommitResult = Readonly<{\n accepted: boolean;\n /** 정리된 값(앞뒤 공백 제거). 거절돼도 제품이 문구에 쓸 수 있게 돌려준다. */\n value: string;\n reason?: TagsInputRejectionReason;\n}>;\n\nexport type TagsInputPolicy = Readonly<{\n /** 같은 값을 다시 넣을 수 있는가. 기본은 막는다. */\n allowDuplicates?: boolean;\n maxTags?: number;\n /** 제품 도메인 규칙. 실패 사유는 `invalid` 하나로 합쳐 돌려준다. */\n isValid?: (value: string) => boolean;\n}>;\n\n/**\n * 태그 하나를 확정할 수 있는지 판정한다. 값을 바꾸지 않고 결과만 돌려준다 —\n * 실제 상태 갱신은 renderer가, 사용자에게 보여 줄 문장은 제품이 만든다.\n */\nexport function resolveTagsInputCommit(\n current: readonly string[],\n rawValue: string,\n policy: TagsInputPolicy = {},\n): TagsInputCommitResult {\n const value = rawValue.trim();\n if (value.length === 0) return { accepted: false, value, reason: \"empty\" };\n if (policy.allowDuplicates !== true && current.includes(value)) {\n return { accepted: false, value, reason: \"duplicate\" };\n }\n if (policy.maxTags !== undefined && current.length >= policy.maxTags) {\n return { accepted: false, value, reason: \"limit\" };\n }\n if (policy.isValid !== undefined && !policy.isValid(value)) {\n return { accepted: false, value, reason: \"invalid\" };\n }\n return { accepted: true, value };\n}\n\n/** 태그 하나를 뺀 목록. 같은 값이 여러 개면 해당 위치 하나만 지운다. */\nexport function removeTagAt(current: readonly string[], index: number): readonly string[] {\n if (!Number.isInteger(index) || index < 0 || index >= current.length) {\n throw new RangeError(`TagsInput index out of range: ${String(index)}`);\n }\n return [...current.slice(0, index), ...current.slice(index + 1)];\n}\n\nexport const tagsInputRecipe = {\n slots: [\"root\", \"frame\", \"tag\", \"remove\", \"input\"] as const,\n defaults: { commitKeys: tagsInputBehaviorDefaults.commitKeys },\n frame: {\n minHeight: fieldFrameContract.minHeight,\n paddingHorizontal: fieldFrameContract.paddingHorizontal,\n paddingVertical: spacing.xxs,\n gap: spacing.xs,\n border: fieldFrameContract.border,\n background: fieldFrameContract.background,\n },\n tag: {\n minHeight: 28,\n paddingHorizontal: spacing.xs,\n gap: spacing.xxs,\n background: semanticColors.canvas,\n color: semanticColors.content.body,\n },\n /** 삭제 버튼은 태그보다 작아도 되지만 포인터 타깃은 공용 최소값을 지킨다. */\n remove: { minTouchTarget: control.minTouchTarget, color: semanticColors.content.secondary },\n selectedTagOutline: semanticColors.border.focus,\n states: { focus: focusIndicatorContract },\n} as const satisfies {\n slots: readonly [\"root\", \"frame\", \"tag\", \"remove\", \"input\"];\n defaults: { commitKeys: readonly TagsInputCommitKey[] };\n frame: {\n minHeight: number;\n paddingHorizontal: number;\n paddingVertical: number;\n gap: number;\n border: ColorReference;\n background: ColorReference;\n };\n tag: { minHeight: number; paddingHorizontal: number; gap: number; background: ColorReference; color: ColorReference };\n remove: { minTouchTarget: number; color: ColorReference };\n selectedTagOutline: ColorReference;\n states: { focus: typeof focusIndicatorContract };\n};\n\nexport const tagsInputBehavior = {\n controlled: [\"tags\", \"defaultTags\", \"onTagsChange\"],\n inputs: [\"policy\", \"commitKeys\", \"label\", \"removeLabel\"],\n /**\n * `onDraftChange`가 없으면 제품이 후보를 거를 수 없다 — 무엇을 입력 중인지 모르기\n * 때문이다. 후보 목록을 계약에 넣는 순간 이 이벤트도 함께 필요하다.\n */\n events: [\"onReject\", \"onDraftChange\"],\n defaults: { backspaceRemovesLastTag: tagsInputBehaviorDefaults.backspaceRemovesLastTag },\n stateAxes: {\n availability: [\"enabled\", \"disabled\"],\n value: [\"empty\", \"filled\"],\n validation: [\"valid\", \"invalid\"],\n },\n web: {\n roles: [\"combobox\", \"listbox\", \"option\", \"button\", \"list\", \"listitem\"],\n keyboard: [\"Enter\", \"Backspace\", \"ArrowUp\", \"ArrowDown\", \"ArrowLeft\", \"ArrowRight\", \"Escape\", \"Tab\"],\n focus: \"native\",\n },\n native: { roles: [\"button\"], states: [\"disabled\"], actions: [\"commit\", \"remove\"] },\n scenarios: [\n \"enter-commits-the-trimmed-value-and-clears-the-input\",\n \"an-empty-or-whitespace-only-value-is-never-committed\",\n \"duplicates-are-rejected-by-default-and-the-reason-is-reported-not-swallowed\",\n \"backspace-in-an-empty-input-selects-the-last-tag-before-a-second-press-removes-it\",\n \"every-tag-carries-its-own-remove-control-with-a-localized-name\",\n \"the-policy-judges-and-the-product-writes-the-sentence-the-user-reads\",\n \"a-candidate-list-lives-in-this-field-because-the-value-shape-is-this-fields-not-comboboxs\",\n \"enter-commits-the-active-candidate-when-one-is-highlighted-and-the-typed-text-otherwise\",\n \"candidate-filtering-belongs-to-the-product-the-contract-only-commits-the-choice\",\n ],\n} as const satisfies BehaviorContract;\n"]}
@@ -17,7 +17,7 @@ export declare const textFormatRecipe: {
17
17
  readonly radius: 8;
18
18
  readonly background: Readonly<{
19
19
  source: "theme";
20
- key: "surfaceAlt";
20
+ key: "bg";
21
21
  alpha?: number;
22
22
  }>;
23
23
  readonly color: Readonly<{
@@ -38,7 +38,7 @@ export declare const textFormatRecipe: {
38
38
  readonly radius: 8;
39
39
  readonly background: Readonly<{
40
40
  source: "theme";
41
- key: "surfaceAlt";
41
+ key: "bg";
42
42
  alpha?: number;
43
43
  }>;
44
44
  readonly color: Readonly<{
@@ -7,7 +7,7 @@ export const textFormatRecipe = {
7
7
  textVariant: "label",
8
8
  paddingHorizontal: spacing.xxs,
9
9
  radius: radius.sm,
10
- background: semanticColors.surface.sunken,
10
+ background: semanticColors.canvas,
11
11
  color: semanticColors.content.body,
12
12
  border: semanticColors.border.default,
13
13
  },
@@ -16,7 +16,7 @@ export const textFormatRecipe = {
16
16
  textVariant: "body",
17
17
  paddingHorizontal: spacing.xxs,
18
18
  radius: radius.sm,
19
- background: semanticColors.surface.sunken,
19
+ background: semanticColors.canvas,
20
20
  color: semanticColors.content.primary,
21
21
  },
22
22
  quote: {
@@ -1 +1 @@
1
- {"version":3,"file":"text-formats.js","sourceRoot":"","sources":["../src/text-formats.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAatD,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,KAAK,EAAE,CAAC,MAAM,CAAU;IACxB,GAAG,EAAE;QACH,UAAU,EAAE,UAAU,CAAC,IAAI;QAC3B,WAAW,EAAE,OAAgB;QAC7B,iBAAiB,EAAE,OAAO,CAAC,GAAG;QAC9B,MAAM,EAAE,MAAM,CAAC,EAAE;QACjB,UAAU,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM;QACzC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,IAAI;QAClC,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,OAAO;KACtC;IACD,IAAI,EAAE;QACJ,UAAU,EAAE,UAAU,CAAC,IAAI;QAC3B,WAAW,EAAE,MAAe;QAC5B,iBAAiB,EAAE,OAAO,CAAC,GAAG;QAC9B,MAAM,EAAE,MAAM,CAAC,EAAE;QACjB,UAAU,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM;QACzC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,OAAO;KACtC;IACD,KAAK,EAAE;QACL,WAAW,EAAE,MAAe;QAC5B,kBAAkB,EAAE,OAAO,CAAC,EAAE;QAC9B,WAAW,EAAE,CAAC;QACd,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,OAAO;QACrC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,IAAI;KACnC;CA2BF,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,UAAU,EAAE,EAAE;IACd,MAAM,EAAE,CAAC,MAAM,CAAC;IAChB,aAAa,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IACjD,SAAS,EAAE,EAAE;IACb,GAAG,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;IAC/C,MAAM,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;IAC9C,SAAS,EAAE;QACT,qFAAqF;QACrF,4EAA4E;QAC5E,yFAAyF;KAC1F;CACkC,CAAC","sourcesContent":["import type { BehaviorContract } from \"./behaviors.js\";\nimport type { ColorReference } from \"./color-references.js\";\nimport { fontFamily, radius, spacing, typography } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * `Text` 하나로는 만들 수 없는 **의미 있는 글자 조각들**이다 — 단축키, 코드, 인용.\n * 도움말·개발자 문서·약관 본문이 반복해서 필요로 하고, 지금까지는 제품이 `<kbd>`에\n * 직접 CSS를 붙여 만들었다.\n *\n * 왜 `Text`의 variant가 아닌가: 이들은 **크기가 아니라 요소**다. `<kbd>`, `<code>`,\n * `<blockquote>`는 각자 의미를 가진 HTML 요소이고 보조기기가 다르게 읽는다. variant로\n * 두면 `<span>`에 코드처럼 보이는 스타일만 입히게 되고, 그 차이가 사라진다.\n */\nexport type TextFormatKind = \"kbd\" | \"code\" | \"quote\";\n\nexport const textFormatRecipe = {\n slots: [\"root\"] as const,\n kbd: {\n fontFamily: fontFamily.code,\n textVariant: \"label\" as const,\n paddingHorizontal: spacing.xxs,\n radius: radius.sm,\n background: semanticColors.surface.sunken,\n color: semanticColors.content.body,\n border: semanticColors.border.default,\n },\n code: {\n fontFamily: fontFamily.code,\n textVariant: \"body\" as const,\n paddingHorizontal: spacing.xxs,\n radius: radius.sm,\n background: semanticColors.surface.sunken,\n color: semanticColors.content.primary,\n },\n quote: {\n textVariant: \"body\" as const,\n paddingInlineStart: spacing.md,\n borderWidth: 2,\n border: semanticColors.border.default,\n color: semanticColors.content.body,\n },\n} as const satisfies {\n slots: readonly [\"root\"];\n kbd: {\n fontFamily: typeof fontFamily.code;\n textVariant: keyof typeof typography;\n paddingHorizontal: number;\n radius: number;\n background: ColorReference;\n color: ColorReference;\n border: ColorReference;\n };\n code: {\n fontFamily: typeof fontFamily.code;\n textVariant: keyof typeof typography;\n paddingHorizontal: number;\n radius: number;\n background: ColorReference;\n color: ColorReference;\n };\n quote: {\n textVariant: keyof typeof typography;\n paddingInlineStart: number;\n borderWidth: number;\n border: ColorReference;\n color: ColorReference;\n };\n};\n\nexport const textFormatBehavior = {\n controlled: [],\n inputs: [\"kind\"],\n configuration: { kind: [\"kbd\", \"code\", \"quote\"] },\n stateAxes: {},\n web: { roles: [], keyboard: [], focus: \"none\" },\n native: { roles: [], states: [], actions: [] },\n scenarios: [\n \"each-kind-emits-its-own-html-element-so-assistive-technology-reads-it-as-what-it-is\",\n \"these-are-elements-not-text-sizes-which-is-why-they-are-not-a-text-variant\",\n \"a-key-name-is-product-copy-because-the-same-key-is-called-different-things-per-platform\",\n ],\n} as const satisfies BehaviorContract;\n"]}
1
+ {"version":3,"file":"text-formats.js","sourceRoot":"","sources":["../src/text-formats.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAatD,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,KAAK,EAAE,CAAC,MAAM,CAAU;IACxB,GAAG,EAAE;QACH,UAAU,EAAE,UAAU,CAAC,IAAI;QAC3B,WAAW,EAAE,OAAgB;QAC7B,iBAAiB,EAAE,OAAO,CAAC,GAAG;QAC9B,MAAM,EAAE,MAAM,CAAC,EAAE;QACjB,UAAU,EAAE,cAAc,CAAC,MAAM;QACjC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,IAAI;QAClC,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,OAAO;KACtC;IACD,IAAI,EAAE;QACJ,UAAU,EAAE,UAAU,CAAC,IAAI;QAC3B,WAAW,EAAE,MAAe;QAC5B,iBAAiB,EAAE,OAAO,CAAC,GAAG;QAC9B,MAAM,EAAE,MAAM,CAAC,EAAE;QACjB,UAAU,EAAE,cAAc,CAAC,MAAM;QACjC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,OAAO;KACtC;IACD,KAAK,EAAE;QACL,WAAW,EAAE,MAAe;QAC5B,kBAAkB,EAAE,OAAO,CAAC,EAAE;QAC9B,WAAW,EAAE,CAAC;QACd,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,OAAO;QACrC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,IAAI;KACnC;CA2BF,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,UAAU,EAAE,EAAE;IACd,MAAM,EAAE,CAAC,MAAM,CAAC;IAChB,aAAa,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IACjD,SAAS,EAAE,EAAE;IACb,GAAG,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;IAC/C,MAAM,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;IAC9C,SAAS,EAAE;QACT,qFAAqF;QACrF,4EAA4E;QAC5E,yFAAyF;KAC1F;CACkC,CAAC","sourcesContent":["import type { BehaviorContract } from \"./behaviors.js\";\nimport type { ColorReference } from \"./color-references.js\";\nimport { fontFamily, radius, spacing, typography } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * `Text` 하나로는 만들 수 없는 **의미 있는 글자 조각들**이다 — 단축키, 코드, 인용.\n * 도움말·개발자 문서·약관 본문이 반복해서 필요로 하고, 지금까지는 제품이 `<kbd>`에\n * 직접 CSS를 붙여 만들었다.\n *\n * 왜 `Text`의 variant가 아닌가: 이들은 **크기가 아니라 요소**다. `<kbd>`, `<code>`,\n * `<blockquote>`는 각자 의미를 가진 HTML 요소이고 보조기기가 다르게 읽는다. variant로\n * 두면 `<span>`에 코드처럼 보이는 스타일만 입히게 되고, 그 차이가 사라진다.\n */\nexport type TextFormatKind = \"kbd\" | \"code\" | \"quote\";\n\nexport const textFormatRecipe = {\n slots: [\"root\"] as const,\n kbd: {\n fontFamily: fontFamily.code,\n textVariant: \"label\" as const,\n paddingHorizontal: spacing.xxs,\n radius: radius.sm,\n background: semanticColors.canvas,\n color: semanticColors.content.body,\n border: semanticColors.border.default,\n },\n code: {\n fontFamily: fontFamily.code,\n textVariant: \"body\" as const,\n paddingHorizontal: spacing.xxs,\n radius: radius.sm,\n background: semanticColors.canvas,\n color: semanticColors.content.primary,\n },\n quote: {\n textVariant: \"body\" as const,\n paddingInlineStart: spacing.md,\n borderWidth: 2,\n border: semanticColors.border.default,\n color: semanticColors.content.body,\n },\n} as const satisfies {\n slots: readonly [\"root\"];\n kbd: {\n fontFamily: typeof fontFamily.code;\n textVariant: keyof typeof typography;\n paddingHorizontal: number;\n radius: number;\n background: ColorReference;\n color: ColorReference;\n border: ColorReference;\n };\n code: {\n fontFamily: typeof fontFamily.code;\n textVariant: keyof typeof typography;\n paddingHorizontal: number;\n radius: number;\n background: ColorReference;\n color: ColorReference;\n };\n quote: {\n textVariant: keyof typeof typography;\n paddingInlineStart: number;\n borderWidth: number;\n border: ColorReference;\n color: ColorReference;\n };\n};\n\nexport const textFormatBehavior = {\n controlled: [],\n inputs: [\"kind\"],\n configuration: { kind: [\"kbd\", \"code\", \"quote\"] },\n stateAxes: {},\n web: { roles: [], keyboard: [], focus: \"none\" },\n native: { roles: [], states: [], actions: [] },\n scenarios: [\n \"each-kind-emits-its-own-html-element-so-assistive-technology-reads-it-as-what-it-is\",\n \"these-are-elements-not-text-sizes-which-is-why-they-are-not-a-text-variant\",\n \"a-key-name-is-product-copy-because-the-same-key-is-called-different-things-per-platform\",\n ],\n} as const satisfies BehaviorContract;\n"]}
@@ -0,0 +1,15 @@
1
+ export declare const thinkingOrbRecipe: {
2
+ readonly defaults: {
3
+ readonly state: "working";
4
+ readonly size: 64;
5
+ readonly speed: 1;
6
+ };
7
+ readonly sizes: readonly [20, 64];
8
+ readonly motion: {
9
+ readonly reducedMotion: "static";
10
+ readonly staticTime: 0.6;
11
+ readonly maxDeltaMs: 64;
12
+ };
13
+ readonly maxPixelRatio: 2;
14
+ };
15
+ //# sourceMappingURL=thinking-orb-recipe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thinking-orb-recipe.d.ts","sourceRoot":"","sources":["../src/thinking-orb-recipe.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;CAMpB,CAAC"}
@@ -0,0 +1,9 @@
1
+ // Recipe stays separate from geometry so catalog consumers do not load animation code.
2
+ export const thinkingOrbRecipe = {
3
+ defaults: { state: "working", size: 64, speed: 1 },
4
+ sizes: [20, 64],
5
+ // Use a deterministic representative frame and retain the upstream DPR cap.
6
+ motion: { reducedMotion: "static", staticTime: 0.6, maxDeltaMs: 64 },
7
+ maxPixelRatio: 2,
8
+ };
9
+ //# sourceMappingURL=thinking-orb-recipe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thinking-orb-recipe.js","sourceRoot":"","sources":["../src/thinking-orb-recipe.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,QAAQ,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE;IAClD,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;IACf,4EAA4E;IAC5E,MAAM,EAAE,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE;IACpE,aAAa,EAAE,CAAC;CACR,CAAC","sourcesContent":["// Recipe stays separate from geometry so catalog consumers do not load animation code.\nexport const thinkingOrbRecipe = {\n defaults: { state: \"working\", size: 64, speed: 1 },\n sizes: [20, 64],\n // Use a deterministic representative frame and retain the upstream DPR cap.\n motion: { reducedMotion: \"static\", staticTime: 0.6, maxDeltaMs: 64 },\n maxPixelRatio: 2,\n} as const;\n"]}
@@ -0,0 +1,25 @@
1
+ import type { OrbState, OrbSize, OrbFrame } from "./internal/thinking-orb/types.js";
2
+ export type { OrbState, OrbSize, OrbFrame };
3
+ export declare const thinkingOrbStates: readonly ["working", "searching", "solving", "listening", "connecting", "weaving", "composing", "breathing", "shaping"];
4
+ export type ThinkingOrbOptions = Readonly<{
5
+ /** Actual operation phase, supplied by the host; the orb never simulates progress. */
6
+ state?: OrbState;
7
+ size?: OrbSize;
8
+ /** Localized description of the current operation. */
9
+ label: string;
10
+ speed?: number;
11
+ paused?: boolean;
12
+ /** Host navigation/list visibility; required for mounted but hidden native screens. */
13
+ active?: boolean;
14
+ }>;
15
+ export { thinkingOrbRecipe } from "./thinking-orb-recipe.js";
16
+ export declare function validateThinkingOrb({ state, size, speed, label }: ThinkingOrbOptions): void;
17
+ /** Theme-free, deterministic geometry; no DOM/React/native imports. */
18
+ export declare function buildThinkingOrbFrame(state: OrbState, size: OrbSize, time: number): OrbFrame;
19
+ /** A local elapsed clock freezes exactly, instead of jumping to wall time after resume. */
20
+ export declare function createThinkingOrbClock(): {
21
+ resetDelta(): void;
22
+ sample(now: number, speed: number): number;
23
+ readonly time: number;
24
+ };
25
+ //# sourceMappingURL=thinking-orb.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thinking-orb.d.ts","sourceRoot":"","sources":["../src/thinking-orb.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AACpF,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AAE5C,eAAO,MAAM,iBAAiB,yHAA0H,CAAC;AACzJ,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,sFAAsF;IACtF,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uFAAuF;IACvF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC,CAAC;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG7D,wBAAgB,mBAAmB,CAAC,EAAE,KAAiB,EAAE,IAAS,EAAE,KAAS,EAAE,KAAK,EAAE,EAAE,kBAAkB,GAAG,IAAI,CAKhH;AAED,uEAAuE;AACvE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,CAK5F;AAED,2FAA2F;AAC3F,wBAAgB,sBAAsB;;gBAKtB,MAAM,SAAS,MAAM;;EAQpC"}