@0001-labs/design-system-1 0.0.0-stage → 0.3.0-alpha.13

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 (699) hide show
  1. package/DS1/0-face/control.ts +99 -0
  2. package/DS1/0-face/define.ts +5 -0
  3. package/DS1/0-face/device.ts +414 -0
  4. package/DS1/0-face/grid-fit.ts +63 -0
  5. package/DS1/0-face/grid.ts +144 -0
  6. package/DS1/0-face/i18n.ts +1023 -0
  7. package/DS1/0-face/language-routing.ts +135 -0
  8. package/DS1/0-face/language-validator.ts +46 -0
  9. package/DS1/0-face/languages.ts +242 -0
  10. package/DS1/0-face/layout-inspector.ts +184 -0
  11. package/DS1/0-face/preferences.ts +39 -0
  12. package/DS1/0-face/pricing.ts +57 -0
  13. package/DS1/0-face/scaling.ts +25 -0
  14. package/DS1/0-face/shared-words.ts +45 -0
  15. package/DS1/0-face/theme.ts +58 -0
  16. package/DS1/0-face/tooltips.ts +82 -0
  17. package/DS1/0-face/translations/da.json +63 -0
  18. package/DS1/0-face/translations/de.json +63 -0
  19. package/DS1/0-face/translations/en.json +63 -0
  20. package/DS1/0-face/translations/es.json +63 -0
  21. package/DS1/0-face/translations/fi.json +63 -0
  22. package/DS1/0-face/translations/fr.json +63 -0
  23. package/DS1/0-face/translations/is.json +56 -0
  24. package/DS1/0-face/translations/it.json +63 -0
  25. package/DS1/0-face/translations/ja.json +63 -0
  26. package/DS1/0-face/translations/ko.json +63 -0
  27. package/DS1/0-face/translations/nb.json +63 -0
  28. package/DS1/0-face/translations/pt-BR.json +63 -0
  29. package/DS1/0-face/translations/pt.json +63 -0
  30. package/DS1/0-face/translations/sv.json +63 -0
  31. package/DS1/0-face/translations/zh.json +63 -0
  32. package/DS1/0-face/translations-format.ts +216 -0
  33. package/DS1/1-root/component-appearance.css +32 -0
  34. package/DS1/1-root/component-metrics.css +21 -0
  35. package/DS1/1-root/component-tokens.css +90 -0
  36. package/DS1/1-root/controls.css +261 -0
  37. package/DS1/1-root/fonts/GT-America-Compressed-Regular.woff2 +0 -0
  38. package/DS1/1-root/fonts/GT-America-Standard-Medium.woff2 +0 -0
  39. package/DS1/1-root/fonts/GT-America-Standard-Regular.woff2 +0 -0
  40. package/DS1/1-root/fonts/GT-Canon-M-Standard-Medium-Trial.woff2 +0 -0
  41. package/DS1/1-root/fonts/Inter-Arrows.woff2 +0 -0
  42. package/DS1/1-root/fonts/Inter-LICENSE.md +7 -0
  43. package/DS1/1-root/fonts/Iosevka-LICENSE.md +7 -0
  44. package/DS1/1-root/fonts/Iosevka-Regular.woff2 +0 -0
  45. package/DS1/1-root/grid.css +31 -0
  46. package/DS1/1-root/one.css +602 -0
  47. package/DS1/1-root/one.semantic.css +683 -0
  48. package/DS1/1-root/palette.ts +91 -0
  49. package/DS1/2-core/ds-avatar.ts +130 -0
  50. package/DS1/2-core/ds-badge.ts +77 -0
  51. package/DS1/2-core/ds-banner.ts +122 -0
  52. package/DS1/2-core/ds-button.ts +163 -0
  53. package/DS1/2-core/ds-card.ts +137 -0
  54. package/DS1/2-core/ds-checkbox.ts +14 -0
  55. package/DS1/2-core/ds-color.ts +32 -0
  56. package/DS1/2-core/ds-composer.ts +182 -0
  57. package/DS1/2-core/ds-cycle.ts +628 -0
  58. package/DS1/2-core/ds-date.ts +24 -0
  59. package/DS1/2-core/ds-file-picker.ts +83 -0
  60. package/DS1/2-core/ds-gap.ts +34 -0
  61. package/DS1/2-core/ds-icon.ts +161 -0
  62. package/DS1/2-core/ds-input.ts +518 -0
  63. package/DS1/2-core/ds-language.ts +94 -0
  64. package/DS1/2-core/ds-pagedots.ts +52 -0
  65. package/DS1/2-core/ds-radio.ts +25 -0
  66. package/DS1/2-core/ds-select.ts +44 -0
  67. package/DS1/2-core/ds-split.ts +122 -0
  68. package/DS1/2-core/ds-swatch.ts +20 -0
  69. package/DS1/2-core/ds-switch.ts +13 -0
  70. package/DS1/2-core/ds-text.ts +190 -0
  71. package/DS1/2-core/ds-textarea.ts +136 -0
  72. package/DS1/2-core/ds-token.ts +40 -0
  73. package/DS1/2-core/ds-tooltip.ts +115 -0
  74. package/DS1/2-core/generated/ds-icon-map.ts +583 -0
  75. package/DS1/2-core/generated/icons-extra.ts +243 -0
  76. package/DS1/2-core/generated/icons-immediate.ts +49 -0
  77. package/DS1/2-core/styles/ds-avatar.css +125 -0
  78. package/DS1/2-core/styles/ds-badge.css +26 -0
  79. package/DS1/2-core/styles/ds-banner.css +77 -0
  80. package/DS1/2-core/styles/ds-button-host.css +214 -0
  81. package/DS1/2-core/styles/ds-button.css +131 -0
  82. package/DS1/2-core/styles/ds-checkbox.css +48 -0
  83. package/DS1/2-core/styles/ds-color.css +24 -0
  84. package/DS1/2-core/styles/ds-composer-frame.css +11 -0
  85. package/DS1/2-core/styles/ds-composer.css +55 -0
  86. package/DS1/2-core/styles/ds-cycle.css +18 -0
  87. package/DS1/2-core/styles/ds-date.css +9 -0
  88. package/DS1/2-core/styles/ds-file-picker.css +66 -0
  89. package/DS1/2-core/styles/ds-gap.css +71 -0
  90. package/DS1/2-core/styles/ds-icon.css +44 -0
  91. package/DS1/2-core/styles/ds-input.css +233 -0
  92. package/DS1/2-core/styles/ds-language.css +78 -0
  93. package/DS1/2-core/styles/ds-pagedots.css +31 -0
  94. package/DS1/2-core/styles/ds-radio.css +83 -0
  95. package/DS1/2-core/styles/ds-select.css +95 -0
  96. package/DS1/2-core/styles/ds-split.css +62 -0
  97. package/DS1/2-core/styles/ds-switch.css +50 -0
  98. package/DS1/2-core/styles/ds-text.css +87 -0
  99. package/DS1/2-core/styles/ds-textarea.css +27 -0
  100. package/DS1/2-core/styles/ds-token.css +9 -0
  101. package/DS1/2-core/styles/ds-tooltip.css +37 -0
  102. package/DS1/3-unit/context-trigger.ts +104 -0
  103. package/DS1/3-unit/ds-accordion.ts +120 -0
  104. package/DS1/3-unit/ds-avatar-group.ts +99 -0
  105. package/DS1/3-unit/ds-form.ts +304 -0
  106. package/DS1/3-unit/ds-list.ts +36 -0
  107. package/DS1/3-unit/ds-menu.ts +482 -0
  108. package/DS1/3-unit/ds-row.ts +48 -0
  109. package/DS1/3-unit/ds-table.ts +68 -0
  110. package/DS1/3-unit/ds-tabs.ts +77 -0
  111. package/DS1/3-unit/ds-token-field.ts +127 -0
  112. package/DS1/3-unit/styles/context-trigger.css +6 -0
  113. package/DS1/3-unit/styles/ds-accordion.css +64 -0
  114. package/DS1/3-unit/styles/ds-avatar-group.css +51 -0
  115. package/DS1/3-unit/styles/ds-list.css +26 -0
  116. package/DS1/3-unit/styles/ds-menu.css +97 -0
  117. package/DS1/3-unit/styles/ds-row.css +37 -0
  118. package/DS1/3-unit/styles/ds-table.css +80 -0
  119. package/DS1/3-unit/styles/ds-tabs.css +44 -0
  120. package/DS1/3-unit/styles/ds-token-field.css +11 -0
  121. package/DS1/4-page/ds-container.ts +28 -0
  122. package/DS1/4-page/ds-dialog.ts +331 -0
  123. package/DS1/4-page/ds-grid.ts +74 -0
  124. package/DS1/4-page/ds-layout.ts +949 -0
  125. package/DS1/4-page/ds-panel.ts +38 -0
  126. package/DS1/4-page/email.ts +170 -0
  127. package/DS1/4-page/styles/ds-container.css +35 -0
  128. package/DS1/4-page/styles/ds-dialog.css +94 -0
  129. package/DS1/4-page/styles/ds-grid.css +95 -0
  130. package/DS1/4-page/styles/ds-layout.css +246 -0
  131. package/DS1/4-page/styles/ds-panel.css +31 -0
  132. package/DS1/components.ts +13 -0
  133. package/DS1/index.ts +86 -0
  134. package/DS1/react/accordion.ts +33 -0
  135. package/DS1/react/avatar-group.ts +11 -0
  136. package/DS1/react/avatar.ts +42 -0
  137. package/DS1/react/badge.ts +7 -0
  138. package/DS1/react/button.ts +63 -0
  139. package/DS1/react/choice.ts +64 -0
  140. package/DS1/react/composer.ts +18 -0
  141. package/DS1/react/context-menu.ts +167 -0
  142. package/DS1/react/dialog.ts +102 -0
  143. package/DS1/react/file-picker.ts +34 -0
  144. package/DS1/react/input.ts +184 -0
  145. package/DS1/react/menu.ts +28 -0
  146. package/DS1/react/split.ts +42 -0
  147. package/DS1/react/token-field.ts +32 -0
  148. package/DS1/vite-env.d.ts +13 -0
  149. package/DS1/x-icon/1x.svg +4 -0
  150. package/DS1/x-icon/1xdots.svg +11 -0
  151. package/DS1/x-icon/1xgrid.svg +8 -0
  152. package/DS1/x-icon/1xlines.svg +5 -0
  153. package/DS1/x-icon/2x.svg +4 -0
  154. package/DS1/x-icon/2xdots.svg +18 -0
  155. package/DS1/x-icon/2xgrid-alt-2.svg +9 -0
  156. package/DS1/x-icon/2xgrid-alt-3.svg +8 -0
  157. package/DS1/x-icon/2xgrid-alt.svg +15 -0
  158. package/DS1/x-icon/2xgrid.svg +6 -0
  159. package/DS1/x-icon/2xlines.svg +6 -0
  160. package/DS1/x-icon/4x4.svg +18 -0
  161. package/DS1/x-icon/app.svg +4 -0
  162. package/DS1/x-icon/apple.svg +3 -0
  163. package/DS1/x-icon/archive-alt.svg +4 -0
  164. package/DS1/x-icon/archive.svg +4 -0
  165. package/DS1/x-icon/arrange-archive.svg +1 -0
  166. package/DS1/x-icon/arrange-check.svg +1 -0
  167. package/DS1/x-icon/arrange-chevron.svg +1 -0
  168. package/DS1/x-icon/arrange-connect.svg +1 -0
  169. package/DS1/x-icon/arrange-draft.svg +1 -0
  170. package/DS1/x-icon/arrange-dropdown.svg +1 -0
  171. package/DS1/x-icon/arrange-home.svg +1 -0
  172. package/DS1/x-icon/arrange-link.svg +1 -0
  173. package/DS1/x-icon/arrange-menu.svg +1 -0
  174. package/DS1/x-icon/arrange-more.svg +1 -0
  175. package/DS1/x-icon/arrange-page.svg +1 -0
  176. package/DS1/x-icon/arrange-plus.svg +1 -0
  177. package/DS1/x-icon/arrange-site.svg +1 -0
  178. package/DS1/x-icon/avatar.svg +4 -0
  179. package/DS1/x-icon/badge-bands-3d.svg +1 -0
  180. package/DS1/x-icon/badge-bands.svg +1 -0
  181. package/DS1/x-icon/badge-blue-fan-3d.svg +1 -0
  182. package/DS1/x-icon/badge-blue-fan.svg +1 -0
  183. package/DS1/x-icon/badge-bricks-3d.svg +1 -0
  184. package/DS1/x-icon/badge-bricks.svg +1 -0
  185. package/DS1/x-icon/badge-broken-stripes-3d.svg +1 -0
  186. package/DS1/x-icon/badge-broken-stripes.svg +1 -0
  187. package/DS1/x-icon/badge-checker-3d.svg +1 -0
  188. package/DS1/x-icon/badge-checker.svg +1 -0
  189. package/DS1/x-icon/badge-diamond-3d.svg +1 -0
  190. package/DS1/x-icon/badge-diamond.svg +1 -0
  191. package/DS1/x-icon/badge-fan-3d.svg +1 -0
  192. package/DS1/x-icon/badge-fan-dark-3d.svg +1 -0
  193. package/DS1/x-icon/badge-fan-dark.svg +1 -0
  194. package/DS1/x-icon/badge-fan.svg +1 -0
  195. package/DS1/x-icon/badge-halves-3d.svg +1 -0
  196. package/DS1/x-icon/badge-halves-green-3d.svg +1 -0
  197. package/DS1/x-icon/badge-halves-green.svg +1 -0
  198. package/DS1/x-icon/badge-halves.svg +1 -0
  199. package/DS1/x-icon/badge-red-3d.svg +1 -0
  200. package/DS1/x-icon/badge-red-blue-bricks-3d.svg +1 -0
  201. package/DS1/x-icon/badge-red-blue-bricks.svg +1 -0
  202. package/DS1/x-icon/badge-red.svg +1 -0
  203. package/DS1/x-icon/badge-round-fan-3d.svg +1 -0
  204. package/DS1/x-icon/badge-round-fan.svg +1 -0
  205. package/DS1/x-icon/badge-shard-3d.svg +1 -0
  206. package/DS1/x-icon/badge-shard.svg +1 -0
  207. package/DS1/x-icon/badge-split-circle-3d.svg +1 -0
  208. package/DS1/x-icon/badge-split-circle.svg +1 -0
  209. package/DS1/x-icon/badge-tall-bricks-3d.svg +1 -0
  210. package/DS1/x-icon/badge-tall-bricks.svg +1 -0
  211. package/DS1/x-icon/badge-weave-3d.svg +1 -0
  212. package/DS1/x-icon/badge-weave.svg +1 -0
  213. package/DS1/x-icon/bag.svg +4 -0
  214. package/DS1/x-icon/block-accordion.svg +1 -0
  215. package/DS1/x-icon/block-banner.svg +1 -0
  216. package/DS1/x-icon/block-breadcrumbs.svg +1 -0
  217. package/DS1/x-icon/block-button.svg +1 -0
  218. package/DS1/x-icon/block-divider.svg +1 -0
  219. package/DS1/x-icon/block-forms.svg +1 -0
  220. package/DS1/x-icon/block-group.svg +1 -0
  221. package/DS1/x-icon/block-heading.svg +1 -0
  222. package/DS1/x-icon/block-image.svg +1 -0
  223. package/DS1/x-icon/block-logo-marquee.svg +1 -0
  224. package/DS1/x-icon/block-page-progress.svg +1 -0
  225. package/DS1/x-icon/block-section.svg +1 -0
  226. package/DS1/x-icon/block-table.svg +1 -0
  227. package/DS1/x-icon/block-tabs.svg +1 -0
  228. package/DS1/x-icon/block-text.svg +1 -0
  229. package/DS1/x-icon/bold.svg +3 -0
  230. package/DS1/x-icon/bullet.svg +5 -0
  231. package/DS1/x-icon/calendar.svg +6 -0
  232. package/DS1/x-icon/callout.svg +5 -0
  233. package/DS1/x-icon/camera.svg +5 -0
  234. package/DS1/x-icon/categories.svg +12 -0
  235. package/DS1/x-icon/check-alt.svg +3 -0
  236. package/DS1/x-icon/check.svg +3 -0
  237. package/DS1/x-icon/close.svg +3 -0
  238. package/DS1/x-icon/code-block.svg +4 -0
  239. package/DS1/x-icon/code.svg +3 -0
  240. package/DS1/x-icon/collapse.svg +3 -0
  241. package/DS1/x-icon/color.svg +4 -0
  242. package/DS1/x-icon/column.svg +3 -0
  243. package/DS1/x-icon/comment.svg +3 -0
  244. package/DS1/x-icon/compose.svg +4 -0
  245. package/DS1/x-icon/connect.svg +5 -0
  246. package/DS1/x-icon/content.svg +6 -0
  247. package/DS1/x-icon/default.svg +4 -0
  248. package/DS1/x-icon/delete.svg +5 -0
  249. package/DS1/x-icon/dictate.svg +6 -0
  250. package/DS1/x-icon/divider.svg +3 -0
  251. package/DS1/x-icon/down.svg +3 -0
  252. package/DS1/x-icon/duplicate.svg +8 -0
  253. package/DS1/x-icon/email.svg +3 -0
  254. package/DS1/x-icon/equation.svg +3 -0
  255. package/DS1/x-icon/exercise.svg +3 -0
  256. package/DS1/x-icon/expand.svg +3 -0
  257. package/DS1/x-icon/ezo-accordion-chevron.svg +11 -0
  258. package/DS1/x-icon/ezo-account-close.svg +1 -0
  259. package/DS1/x-icon/ezo-android-screen-archive.svg +4 -0
  260. package/DS1/x-icon/ezo-android-screen-calendar.svg +6 -0
  261. package/DS1/x-icon/ezo-android-screen-camera.svg +5 -0
  262. package/DS1/x-icon/ezo-android-screen-check.svg +3 -0
  263. package/DS1/x-icon/ezo-android-screen-compose.svg +4 -0
  264. package/DS1/x-icon/ezo-android-screen-globe.svg +1 -0
  265. package/DS1/x-icon/ezo-android-screen-lists.svg +3 -0
  266. package/DS1/x-icon/ezo-android-screen-shared.svg +5 -0
  267. package/DS1/x-icon/ezo-android-screen-stats.svg +3 -0
  268. package/DS1/x-icon/ezo-android-screen-store.svg +4 -0
  269. package/DS1/x-icon/ezo-android-screen-views.svg +4 -0
  270. package/DS1/x-icon/ezo-android-screen-work.svg +3 -0
  271. package/DS1/x-icon/ezo-arrow-cursor.svg +8 -0
  272. package/DS1/x-icon/ezo-block-accordion.svg +1 -0
  273. package/DS1/x-icon/ezo-block-divider.svg +1 -0
  274. package/DS1/x-icon/ezo-block-group.svg +1 -0
  275. package/DS1/x-icon/ezo-block-image.svg +1 -0
  276. package/DS1/x-icon/ezo-block-section.svg +1 -0
  277. package/DS1/x-icon/ezo-block-table.svg +1 -0
  278. package/DS1/x-icon/ezo-code.svg +14 -0
  279. package/DS1/x-icon/ezo-down.svg +3 -0
  280. package/DS1/x-icon/ezo-figma-premium-star.svg +5 -0
  281. package/DS1/x-icon/ezo-item-button-complete.svg +4 -0
  282. package/DS1/x-icon/ezo-item-button-header.svg +3 -0
  283. package/DS1/x-icon/ezo-item-button-open.svg +3 -0
  284. package/DS1/x-icon/ezo-item-button-reopen.svg +3 -0
  285. package/DS1/x-icon/ezo-item-button-star.svg +3 -0
  286. package/DS1/x-icon/ezo-item-button-start.svg +3 -0
  287. package/DS1/x-icon/ezo-item-button-unheader.svg +7 -0
  288. package/DS1/x-icon/ezo-item-button-unstar.svg +5 -0
  289. package/DS1/x-icon/ezo-item-star.svg +5 -0
  290. package/DS1/x-icon/ezo-left.svg +3 -0
  291. package/DS1/x-icon/ezo-logo.svg +1 -0
  292. package/DS1/x-icon/ezo-mic.svg +8 -0
  293. package/DS1/x-icon/ezo-move-column.svg +14 -0
  294. package/DS1/x-icon/ezo-move-row.svg +18 -0
  295. package/DS1/x-icon/ezo-navigation-check.svg +1 -0
  296. package/DS1/x-icon/ezo-note-back.svg +5 -0
  297. package/DS1/x-icon/ezo-note-menu-complete.svg +11 -0
  298. package/DS1/x-icon/ezo-note-menu-delete.svg +4 -0
  299. package/DS1/x-icon/ezo-note-menu-duplicate.svg +8 -0
  300. package/DS1/x-icon/ezo-note-menu-note.svg +5 -0
  301. package/DS1/x-icon/ezo-note-menu-page.svg +3 -0
  302. package/DS1/x-icon/ezo-note-menu-star.svg +5 -0
  303. package/DS1/x-icon/ezo-note-menu-start.svg +6 -0
  304. package/DS1/x-icon/ezo-note-menu-stop.svg +6 -0
  305. package/DS1/x-icon/ezo-note-more.svg +7 -0
  306. package/DS1/x-icon/ezo-note-star.svg +5 -0
  307. package/DS1/x-icon/ezo-password-visibility-see.svg +6 -0
  308. package/DS1/x-icon/ezo-password-visibility-unsee.svg +9 -0
  309. package/DS1/x-icon/ezo-pattern-call-to-action.svg +1 -0
  310. package/DS1/x-icon/ezo-play.svg +5 -0
  311. package/DS1/x-icon/ezo-premium-star.svg +5 -0
  312. package/DS1/x-icon/ezo-setting-1xdots.svg +13 -0
  313. package/DS1/x-icon/ezo-setting-1xgrid.svg +10 -0
  314. package/DS1/x-icon/ezo-setting-1xlines.svg +7 -0
  315. package/DS1/x-icon/ezo-setting-2xdots.svg +8 -0
  316. package/DS1/x-icon/ezo-setting-2xgrid.svg +8 -0
  317. package/DS1/x-icon/ezo-setting-2xlines.svg +6 -0
  318. package/DS1/x-icon/ezo-setting-blank.svg +5 -0
  319. package/DS1/x-icon/ezo-store-chevron.svg +1 -0
  320. package/DS1/x-icon/ezo-upgrade-close.svg +5 -0
  321. package/DS1/x-icon/folder.svg +3 -0
  322. package/DS1/x-icon/form.svg +5 -0
  323. package/DS1/x-icon/gallery.svg +3 -0
  324. package/DS1/x-icon/globe.svg +1 -0
  325. package/DS1/x-icon/google.svg +6 -0
  326. package/DS1/x-icon/group-alt-2.svg +3 -0
  327. package/DS1/x-icon/group-alt.svg +7 -0
  328. package/DS1/x-icon/group.svg +3 -0
  329. package/DS1/x-icon/h1.svg +3 -0
  330. package/DS1/x-icon/h2.svg +3 -0
  331. package/DS1/x-icon/h3.svg +3 -0
  332. package/DS1/x-icon/handle.svg +8 -0
  333. package/DS1/x-icon/head.svg +3 -0
  334. package/DS1/x-icon/help-alt.svg +3 -0
  335. package/DS1/x-icon/help.svg +5 -0
  336. package/DS1/x-icon/home.svg +3 -0
  337. package/DS1/x-icon/icon.svg +4 -0
  338. package/DS1/x-icon/image.svg +5 -0
  339. package/DS1/x-icon/in.svg +4 -0
  340. package/DS1/x-icon/indent.svg +4 -0
  341. package/DS1/x-icon/italic.svg +3 -0
  342. package/DS1/x-icon/item.svg +3 -0
  343. package/DS1/x-icon/left.svg +4 -0
  344. package/DS1/x-icon/life.svg +8 -0
  345. package/DS1/x-icon/link.svg +3 -0
  346. package/DS1/x-icon/list-line.svg +3 -0
  347. package/DS1/x-icon/lists.svg +3 -0
  348. package/DS1/x-icon/lock-filled.svg +3 -0
  349. package/DS1/x-icon/lock.svg +3 -0
  350. package/DS1/x-icon/logo-marquee.svg +5 -0
  351. package/DS1/x-icon/loop.svg +6 -0
  352. package/DS1/x-icon/mention.svg +4 -0
  353. package/DS1/x-icon/menu.svg +3 -0
  354. package/DS1/x-icon/mic.svg +5 -0
  355. package/DS1/x-icon/minimize-alt.svg +4 -0
  356. package/DS1/x-icon/minimize.svg +4 -0
  357. package/DS1/x-icon/money.svg +6 -0
  358. package/DS1/x-icon/more.svg +5 -0
  359. package/DS1/x-icon/navigation-check.svg +1 -0
  360. package/DS1/x-icon/note-alt-2.svg +6 -0
  361. package/DS1/x-icon/note-alt.svg +4 -0
  362. package/DS1/x-icon/note-lines.svg +6 -0
  363. package/DS1/x-icon/note.svg +4 -0
  364. package/DS1/x-icon/notifications.svg +3 -0
  365. package/DS1/x-icon/numbered.svg +4 -0
  366. package/DS1/x-icon/open-alt.svg +3 -0
  367. package/DS1/x-icon/open-source.svg +3 -0
  368. package/DS1/x-icon/open.svg +3 -0
  369. package/DS1/x-icon/outdent.svg +4 -0
  370. package/DS1/x-icon/pad.svg +8 -0
  371. package/DS1/x-icon/page-18.svg +3 -0
  372. package/DS1/x-icon/page-alt.svg +3 -0
  373. package/DS1/x-icon/page-note.svg +1 -0
  374. package/DS1/x-icon/page.svg +3 -0
  375. package/DS1/x-icon/panel.svg +4 -0
  376. package/DS1/x-icon/pattern-call-to-action.svg +1 -0
  377. package/DS1/x-icon/pattern-card.svg +1 -0
  378. package/DS1/x-icon/pattern-feature.svg +1 -0
  379. package/DS1/x-icon/pattern-hero.svg +1 -0
  380. package/DS1/x-icon/pen.svg +4 -0
  381. package/DS1/x-icon/play-alt.svg +3 -0
  382. package/DS1/x-icon/plus.svg +3 -0
  383. package/DS1/x-icon/product-sources.json +2557 -0
  384. package/DS1/x-icon/quote.svg +3 -0
  385. package/DS1/x-icon/redo.svg +3 -0
  386. package/DS1/x-icon/relationships.svg +3 -0
  387. package/DS1/x-icon/rewind-30-alt.svg +4 -0
  388. package/DS1/x-icon/rewind-30.svg +5 -0
  389. package/DS1/x-icon/rewind.svg +5 -0
  390. package/DS1/x-icon/right.svg +3 -0
  391. package/DS1/x-icon/row.svg +3 -0
  392. package/DS1/x-icon/school.svg +4 -0
  393. package/DS1/x-icon/sdown.svg +3 -0
  394. package/DS1/x-icon/search-30.svg +4 -0
  395. package/DS1/x-icon/search.svg +3 -0
  396. package/DS1/x-icon/see-30.svg +4 -0
  397. package/DS1/x-icon/see.svg +4 -0
  398. package/DS1/x-icon/settings.svg +8 -0
  399. package/DS1/x-icon/shared.svg +5 -0
  400. package/DS1/x-icon/slash.svg +3 -0
  401. package/DS1/x-icon/star-alt-2.svg +3 -0
  402. package/DS1/x-icon/star-alt.svg +3 -0
  403. package/DS1/x-icon/star.svg +3 -0
  404. package/DS1/x-icon/stats.svg +3 -0
  405. package/DS1/x-icon/status-alt.svg +11 -0
  406. package/DS1/x-icon/status-filled.svg +26 -0
  407. package/DS1/x-icon/status.svg +10 -0
  408. package/DS1/x-icon/store.svg +4 -0
  409. package/DS1/x-icon/strike.svg +3 -0
  410. package/DS1/x-icon/sup.svg +3 -0
  411. package/DS1/x-icon/table.svg +4 -0
  412. package/DS1/x-icon/template.svg +4 -0
  413. package/DS1/x-icon/templates.svg +4 -0
  414. package/DS1/x-icon/text.svg +3 -0
  415. package/DS1/x-icon/title.svg +3 -0
  416. package/DS1/x-icon/titlestyle.svg +11 -0
  417. package/DS1/x-icon/todo.svg +5 -0
  418. package/DS1/x-icon/toggle.svg +4 -0
  419. package/DS1/x-icon/triangle.svg +3 -0
  420. package/DS1/x-icon/underline.svg +3 -0
  421. package/DS1/x-icon/undo.svg +3 -0
  422. package/DS1/x-icon/ungroup.svg +4 -0
  423. package/DS1/x-icon/unhead.svg +7 -0
  424. package/DS1/x-icon/unicon.svg +6 -0
  425. package/DS1/x-icon/unlock.svg +3 -0
  426. package/DS1/x-icon/unmic.svg +6 -0
  427. package/DS1/x-icon/unsee.svg +5 -0
  428. package/DS1/x-icon/unstar.svg +5 -0
  429. package/DS1/x-icon/untitle.svg +7 -0
  430. package/DS1/x-icon/up-right.svg +3 -0
  431. package/DS1/x-icon/up.svg +3 -0
  432. package/DS1/x-icon/views.svg +4 -0
  433. package/DS1/x-icon/warning.svg +3 -0
  434. package/DS1/x-icon/work.svg +3 -0
  435. package/LICENSE +21 -0
  436. package/README.md +240 -2
  437. package/dist/0-face/control.d.ts +24 -0
  438. package/dist/0-face/control.d.ts.map +1 -0
  439. package/dist/0-face/control.js +117 -0
  440. package/dist/0-face/define.d.ts +3 -0
  441. package/dist/0-face/define.d.ts.map +1 -0
  442. package/dist/0-face/define.js +7 -0
  443. package/dist/0-face/device.d.ts +71 -0
  444. package/dist/0-face/device.d.ts.map +1 -0
  445. package/dist/0-face/device.js +318 -0
  446. package/dist/0-face/grid-fit.d.ts +3 -0
  447. package/dist/0-face/grid-fit.d.ts.map +1 -0
  448. package/dist/0-face/grid-fit.js +73 -0
  449. package/dist/0-face/grid.d.ts +3 -0
  450. package/dist/0-face/grid.d.ts.map +1 -0
  451. package/dist/0-face/grid.js +144 -0
  452. package/dist/0-face/i18n.d.ts +138 -0
  453. package/dist/0-face/i18n.d.ts.map +1 -0
  454. package/dist/0-face/i18n.js +725 -0
  455. package/dist/0-face/language-routing.d.ts +47 -0
  456. package/dist/0-face/language-routing.d.ts.map +1 -0
  457. package/dist/0-face/language-routing.js +91 -0
  458. package/dist/0-face/language-validator.d.ts +11 -0
  459. package/dist/0-face/language-validator.d.ts.map +1 -0
  460. package/dist/0-face/language-validator.js +31 -0
  461. package/dist/0-face/languages.d.ts +53 -0
  462. package/dist/0-face/languages.d.ts.map +1 -0
  463. package/dist/0-face/languages.js +192 -0
  464. package/dist/0-face/layout-inspector.d.ts +6 -0
  465. package/dist/0-face/layout-inspector.d.ts.map +1 -0
  466. package/dist/0-face/layout-inspector.js +197 -0
  467. package/dist/0-face/preferences.d.ts +20 -0
  468. package/dist/0-face/preferences.d.ts.map +1 -0
  469. package/dist/0-face/preferences.js +19 -0
  470. package/dist/0-face/pricing.d.ts +15 -0
  471. package/dist/0-face/pricing.d.ts.map +1 -0
  472. package/dist/0-face/pricing.js +46 -0
  473. package/dist/0-face/scaling.d.ts +10 -0
  474. package/dist/0-face/scaling.d.ts.map +1 -0
  475. package/dist/0-face/scaling.js +14 -0
  476. package/dist/0-face/shared-words.d.ts +14 -0
  477. package/dist/0-face/shared-words.d.ts.map +1 -0
  478. package/dist/0-face/shared-words.js +32 -0
  479. package/dist/0-face/theme.d.ts +4 -0
  480. package/dist/0-face/theme.d.ts.map +1 -0
  481. package/dist/0-face/theme.js +48 -0
  482. package/dist/0-face/tooltips.d.ts +8 -0
  483. package/dist/0-face/tooltips.d.ts.map +1 -0
  484. package/dist/0-face/tooltips.js +90 -0
  485. package/dist/0-face/translations/da.json +63 -0
  486. package/dist/0-face/translations/de.json +63 -0
  487. package/dist/0-face/translations/en.json +63 -0
  488. package/dist/0-face/translations/es.json +63 -0
  489. package/dist/0-face/translations/fi.json +63 -0
  490. package/dist/0-face/translations/fr.json +63 -0
  491. package/dist/0-face/translations/is.json +56 -0
  492. package/dist/0-face/translations/it.json +63 -0
  493. package/dist/0-face/translations/ja.json +63 -0
  494. package/dist/0-face/translations/ko.json +63 -0
  495. package/dist/0-face/translations/nb.json +63 -0
  496. package/dist/0-face/translations/pt-BR.json +63 -0
  497. package/dist/0-face/translations/pt.json +63 -0
  498. package/dist/0-face/translations/sv.json +63 -0
  499. package/dist/0-face/translations/zh.json +63 -0
  500. package/dist/0-face/translations-format.d.ts +62 -0
  501. package/dist/0-face/translations-format.d.ts.map +1 -0
  502. package/dist/0-face/translations-format.js +159 -0
  503. package/dist/1-root/palette.d.ts +31 -0
  504. package/dist/1-root/palette.d.ts.map +1 -0
  505. package/dist/1-root/palette.js +83 -0
  506. package/dist/2-core/ds-avatar.d.ts +82 -0
  507. package/dist/2-core/ds-avatar.d.ts.map +1 -0
  508. package/dist/2-core/ds-avatar.js +102 -0
  509. package/dist/2-core/ds-badge.d.ts +33 -0
  510. package/dist/2-core/ds-badge.d.ts.map +1 -0
  511. package/dist/2-core/ds-badge.js +77 -0
  512. package/dist/2-core/ds-banner.d.ts +68 -0
  513. package/dist/2-core/ds-banner.d.ts.map +1 -0
  514. package/dist/2-core/ds-banner.js +98 -0
  515. package/dist/2-core/ds-button.d.ts +102 -0
  516. package/dist/2-core/ds-button.d.ts.map +1 -0
  517. package/dist/2-core/ds-button.js +133 -0
  518. package/dist/2-core/ds-card.d.ts +39 -0
  519. package/dist/2-core/ds-card.d.ts.map +1 -0
  520. package/dist/2-core/ds-card.js +119 -0
  521. package/dist/2-core/ds-checkbox.d.ts +13 -0
  522. package/dist/2-core/ds-checkbox.d.ts.map +1 -0
  523. package/dist/2-core/ds-checkbox.js +11 -0
  524. package/dist/2-core/ds-color.d.ts +15 -0
  525. package/dist/2-core/ds-color.d.ts.map +1 -0
  526. package/dist/2-core/ds-color.js +30 -0
  527. package/dist/2-core/ds-composer.d.ts +69 -0
  528. package/dist/2-core/ds-composer.d.ts.map +1 -0
  529. package/dist/2-core/ds-composer.js +183 -0
  530. package/dist/2-core/ds-cycle.d.ts +85 -0
  531. package/dist/2-core/ds-cycle.d.ts.map +1 -0
  532. package/dist/2-core/ds-cycle.js +497 -0
  533. package/dist/2-core/ds-date.d.ts +16 -0
  534. package/dist/2-core/ds-date.d.ts.map +1 -0
  535. package/dist/2-core/ds-date.js +15 -0
  536. package/dist/2-core/ds-file-picker.d.ts +65 -0
  537. package/dist/2-core/ds-file-picker.d.ts.map +1 -0
  538. package/dist/2-core/ds-file-picker.js +80 -0
  539. package/dist/2-core/ds-gap.d.ts +27 -0
  540. package/dist/2-core/ds-gap.d.ts.map +1 -0
  541. package/dist/2-core/ds-gap.js +22 -0
  542. package/dist/2-core/ds-icon.d.ts +58 -0
  543. package/dist/2-core/ds-icon.d.ts.map +1 -0
  544. package/dist/2-core/ds-icon.js +128 -0
  545. package/dist/2-core/ds-input.d.ts +97 -0
  546. package/dist/2-core/ds-input.d.ts.map +1 -0
  547. package/dist/2-core/ds-input.js +429 -0
  548. package/dist/2-core/ds-language.d.ts +75 -0
  549. package/dist/2-core/ds-language.d.ts.map +1 -0
  550. package/dist/2-core/ds-language.js +74 -0
  551. package/dist/2-core/ds-pagedots.d.ts +32 -0
  552. package/dist/2-core/ds-pagedots.d.ts.map +1 -0
  553. package/dist/2-core/ds-pagedots.js +36 -0
  554. package/dist/2-core/ds-radio.d.ts +35 -0
  555. package/dist/2-core/ds-radio.d.ts.map +1 -0
  556. package/dist/2-core/ds-radio.js +19 -0
  557. package/dist/2-core/ds-select.d.ts +30 -0
  558. package/dist/2-core/ds-select.d.ts.map +1 -0
  559. package/dist/2-core/ds-select.js +45 -0
  560. package/dist/2-core/ds-split.d.ts +75 -0
  561. package/dist/2-core/ds-split.d.ts.map +1 -0
  562. package/dist/2-core/ds-split.js +101 -0
  563. package/dist/2-core/ds-swatch.d.ts +24 -0
  564. package/dist/2-core/ds-swatch.d.ts.map +1 -0
  565. package/dist/2-core/ds-swatch.js +16 -0
  566. package/dist/2-core/ds-switch.d.ts +13 -0
  567. package/dist/2-core/ds-switch.d.ts.map +1 -0
  568. package/dist/2-core/ds-switch.js +10 -0
  569. package/dist/2-core/ds-text.d.ts +75 -0
  570. package/dist/2-core/ds-text.d.ts.map +1 -0
  571. package/dist/2-core/ds-text.js +142 -0
  572. package/dist/2-core/ds-textarea.d.ts +46 -0
  573. package/dist/2-core/ds-textarea.d.ts.map +1 -0
  574. package/dist/2-core/ds-textarea.js +121 -0
  575. package/dist/2-core/ds-token.d.ts +48 -0
  576. package/dist/2-core/ds-token.d.ts.map +1 -0
  577. package/dist/2-core/ds-token.js +44 -0
  578. package/dist/2-core/ds-tooltip.d.ts +48 -0
  579. package/dist/2-core/ds-tooltip.d.ts.map +1 -0
  580. package/dist/2-core/ds-tooltip.js +99 -0
  581. package/dist/2-core/generated/ds-icon-map.d.ts +295 -0
  582. package/dist/2-core/generated/ds-icon-map.d.ts.map +1 -0
  583. package/dist/2-core/generated/ds-icon-map.js +579 -0
  584. package/dist/2-core/generated/icons-extra.d.ts +2 -0
  585. package/dist/2-core/generated/icons-extra.d.ts.map +1 -0
  586. package/dist/2-core/generated/icons-extra.js +243 -0
  587. package/dist/2-core/generated/icons-immediate.d.ts +3 -0
  588. package/dist/2-core/generated/icons-immediate.d.ts.map +1 -0
  589. package/dist/2-core/generated/icons-immediate.js +49 -0
  590. package/dist/3-unit/context-trigger.d.ts +22 -0
  591. package/dist/3-unit/context-trigger.d.ts.map +1 -0
  592. package/dist/3-unit/context-trigger.js +130 -0
  593. package/dist/3-unit/ds-accordion.d.ts +55 -0
  594. package/dist/3-unit/ds-accordion.d.ts.map +1 -0
  595. package/dist/3-unit/ds-accordion.js +105 -0
  596. package/dist/3-unit/ds-avatar-group.d.ts +40 -0
  597. package/dist/3-unit/ds-avatar-group.d.ts.map +1 -0
  598. package/dist/3-unit/ds-avatar-group.js +84 -0
  599. package/dist/3-unit/ds-form.d.ts +70 -0
  600. package/dist/3-unit/ds-form.d.ts.map +1 -0
  601. package/dist/3-unit/ds-form.js +232 -0
  602. package/dist/3-unit/ds-list.d.ts +25 -0
  603. package/dist/3-unit/ds-list.d.ts.map +1 -0
  604. package/dist/3-unit/ds-list.js +25 -0
  605. package/dist/3-unit/ds-menu.d.ts +162 -0
  606. package/dist/3-unit/ds-menu.d.ts.map +1 -0
  607. package/dist/3-unit/ds-menu.js +607 -0
  608. package/dist/3-unit/ds-row.d.ts +43 -0
  609. package/dist/3-unit/ds-row.d.ts.map +1 -0
  610. package/dist/3-unit/ds-row.js +32 -0
  611. package/dist/3-unit/ds-table.d.ts +33 -0
  612. package/dist/3-unit/ds-table.d.ts.map +1 -0
  613. package/dist/3-unit/ds-table.js +43 -0
  614. package/dist/3-unit/ds-tabs.d.ts +50 -0
  615. package/dist/3-unit/ds-tabs.d.ts.map +1 -0
  616. package/dist/3-unit/ds-tabs.js +64 -0
  617. package/dist/3-unit/ds-token-field.d.ts +64 -0
  618. package/dist/3-unit/ds-token-field.d.ts.map +1 -0
  619. package/dist/3-unit/ds-token-field.js +184 -0
  620. package/dist/4-page/ds-container.d.ts +17 -0
  621. package/dist/4-page/ds-container.d.ts.map +1 -0
  622. package/dist/4-page/ds-container.js +11 -0
  623. package/dist/4-page/ds-dialog.d.ts +105 -0
  624. package/dist/4-page/ds-dialog.d.ts.map +1 -0
  625. package/dist/4-page/ds-dialog.js +358 -0
  626. package/dist/4-page/ds-grid.d.ts +28 -0
  627. package/dist/4-page/ds-grid.d.ts.map +1 -0
  628. package/dist/4-page/ds-grid.js +49 -0
  629. package/dist/4-page/ds-layout.d.ts +31 -0
  630. package/dist/4-page/ds-layout.d.ts.map +1 -0
  631. package/dist/4-page/ds-layout.js +930 -0
  632. package/dist/4-page/ds-panel.d.ts +26 -0
  633. package/dist/4-page/ds-panel.d.ts.map +1 -0
  634. package/dist/4-page/ds-panel.js +29 -0
  635. package/dist/4-page/email.d.ts +49 -0
  636. package/dist/4-page/email.d.ts.map +1 -0
  637. package/dist/4-page/email.js +134 -0
  638. package/dist/components.d.ts +14 -0
  639. package/dist/components.d.ts.map +1 -0
  640. package/dist/components.js +13 -0
  641. package/dist/design-system-1.bundle.js +9115 -0
  642. package/dist/design-system-1.bundle.js.map +1 -0
  643. package/dist/design-system-1.bundle.min.js +6879 -0
  644. package/dist/design-system-1.bundle.min.js.map +1 -0
  645. package/dist/design-system-1.css +2 -0
  646. package/dist/icons-extra-BoAACA4X.js +944 -0
  647. package/dist/icons-extra-BoAACA4X.js.map +1 -0
  648. package/dist/icons-extra-DlxTo_Y4.js +247 -0
  649. package/dist/icons-extra-DlxTo_Y4.js.map +1 -0
  650. package/dist/index.d.ts +57 -0
  651. package/dist/index.d.ts.map +1 -0
  652. package/dist/index.js +73 -0
  653. package/dist/react/accordion.d.ts +21 -0
  654. package/dist/react/accordion.d.ts.map +1 -0
  655. package/dist/react/accordion.js +30 -0
  656. package/dist/react/avatar-group.d.ts +6 -0
  657. package/dist/react/avatar-group.d.ts.map +1 -0
  658. package/dist/react/avatar-group.js +5 -0
  659. package/dist/react/avatar.d.ts +13 -0
  660. package/dist/react/avatar.d.ts.map +1 -0
  661. package/dist/react/avatar.js +18 -0
  662. package/dist/react/badge.d.ts +11 -0
  663. package/dist/react/badge.d.ts.map +1 -0
  664. package/dist/react/badge.js +4 -0
  665. package/dist/react/button.d.ts +13 -0
  666. package/dist/react/button.d.ts.map +1 -0
  667. package/dist/react/button.js +37 -0
  668. package/dist/react/choice.d.ts +43 -0
  669. package/dist/react/choice.d.ts.map +1 -0
  670. package/dist/react/choice.js +32 -0
  671. package/dist/react/composer.d.ts +17 -0
  672. package/dist/react/composer.d.ts.map +1 -0
  673. package/dist/react/composer.js +8 -0
  674. package/dist/react/context-menu.d.ts +657 -0
  675. package/dist/react/context-menu.d.ts.map +1 -0
  676. package/dist/react/context-menu.js +133 -0
  677. package/dist/react/dialog.d.ts +1173 -0
  678. package/dist/react/dialog.d.ts.map +1 -0
  679. package/dist/react/dialog.js +79 -0
  680. package/dist/react/file-picker.d.ts +29 -0
  681. package/dist/react/file-picker.d.ts.map +1 -0
  682. package/dist/react/file-picker.js +17 -0
  683. package/dist/react/input.d.ts +35 -0
  684. package/dist/react/input.d.ts.map +1 -0
  685. package/dist/react/input.js +128 -0
  686. package/dist/react/menu.d.ts +37 -0
  687. package/dist/react/menu.d.ts.map +1 -0
  688. package/dist/react/menu.js +31 -0
  689. package/dist/react/split.d.ts +15 -0
  690. package/dist/react/split.d.ts.map +1 -0
  691. package/dist/react/split.js +32 -0
  692. package/dist/react/token-field.d.ts +36 -0
  693. package/dist/react/token-field.d.ts.map +1 -0
  694. package/dist/react/token-field.js +33 -0
  695. package/dist/shared-words-Cg6Phtx0.js +915 -0
  696. package/dist/shared-words-Cg6Phtx0.js.map +1 -0
  697. package/dist/shared-words-Ddz0yUEF.js +933 -0
  698. package/dist/shared-words-Ddz0yUEF.js.map +1 -0
  699. package/package.json +149 -3
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Addresses that carry a language: `/ja/pricing` is the pricing page in
3
+ * Japanese, `/pricing` the same page in the default language.
4
+ *
5
+ * Pure functions on paths, with no window access, so they run in a browser,
6
+ * a Worker or a test alike. A product says which languages get a prefix and
7
+ * whether the default language has one, through `configureLanguageRouting`
8
+ * (or `configureI18n({ routing })`), or per call.
9
+ */
10
+ import { DEFAULT_LANGUAGE, LANGUAGES } from "./languages";
11
+
12
+ export type LanguageRoutingOptions = {
13
+ /** The languages that get a prefix. Defaults to every language in the list. */
14
+ languages?: readonly string[];
15
+ /** The language of an address without a prefix. Defaults to `en`. */
16
+ defaultLanguage?: string;
17
+ /**
18
+ * Whether the default language has a prefix too (`/en/pricing`). Off by
19
+ * default: the default language's addresses carry none.
20
+ */
21
+ prefixDefaultLanguage?: boolean;
22
+ };
23
+
24
+ export type LanguagePath = {
25
+ /** The language the address names, as configured (`pt-BR`), or null */
26
+ language: string | null;
27
+ /** The address without the prefix, always starting with `/` */
28
+ restPath: string;
29
+ };
30
+
31
+ let routing: LanguageRoutingOptions = {};
32
+
33
+ /** Set which languages get a prefix, and whether the default language has one. */
34
+ export function configureLanguageRouting(options: LanguageRoutingOptions): void {
35
+ routing = { ...routing, ...options };
36
+ }
37
+
38
+ /** The routing options in force, with the defaults filled in. */
39
+ export function getLanguageRoutingOptions(): Required<LanguageRoutingOptions> {
40
+ return resolve();
41
+ }
42
+
43
+ function resolve(options?: LanguageRoutingOptions): Required<LanguageRoutingOptions> {
44
+ const merged = { ...routing, ...options };
45
+ return {
46
+ languages: merged.languages?.length ? merged.languages : LANGUAGES,
47
+ defaultLanguage: merged.defaultLanguage || DEFAULT_LANGUAGE,
48
+ prefixDefaultLanguage: merged.prefixDefaultLanguage ?? false,
49
+ };
50
+ }
51
+
52
+ /** A language's segment in an address: its code in lower case (`pt-br`). */
53
+ export function languageSlug(language: string): string {
54
+ return String(language ?? "").toLowerCase().replace(/_/g, "-");
55
+ }
56
+
57
+ /**
58
+ * The configured language a path segment names (`pt-br` gives `pt-BR`), or
59
+ * null. Matching ignores case.
60
+ */
61
+ export function languageFromSlug(
62
+ segment: string,
63
+ languages: readonly string[] = resolve().languages
64
+ ): string | null {
65
+ const slug = languageSlug(segment);
66
+ if (!slug) {
67
+ return null;
68
+ }
69
+ return languages.find((code) => languageSlug(code) === slug) ?? null;
70
+ }
71
+
72
+ /** Whether a path segment names one of the languages that get a prefix. */
73
+ export function isLanguageSlug(segment: string, languages?: readonly string[]): boolean {
74
+ return languageFromSlug(segment, languages) !== null;
75
+ }
76
+
77
+ /** Split an address into its path and what follows (`?query#hash`). */
78
+ function splitSuffix(path: string): [string, string] {
79
+ const index = path.search(/[?#]/);
80
+ return index === -1 ? [path, ""] : [path.slice(0, index), path.slice(index)];
81
+ }
82
+
83
+ /**
84
+ * The language an address names in its first segment, and the address
85
+ * without it: `/ja/pricing` gives `{ language: "ja", restPath: "/pricing" }`,
86
+ * `/pricing` gives `{ language: null, restPath: "/pricing" }`. A query and
87
+ * hash stay on the rest.
88
+ */
89
+ export function parseLanguagePath(
90
+ pathname: string,
91
+ options?: LanguageRoutingOptions
92
+ ): LanguagePath {
93
+ const { languages } = resolve(options);
94
+ const [path, suffix] = splitSuffix(pathname || "/");
95
+ const absolute = path.startsWith("/") ? path : `/${path}`;
96
+ const first = absolute.split("/")[1] ?? "";
97
+ const language = languageFromSlug(first, languages);
98
+
99
+ if (!language) {
100
+ return { language: null, restPath: `${absolute}${suffix}` };
101
+ }
102
+
103
+ const rest = absolute.slice(first.length + 1) || "/";
104
+ return { language, restPath: `${rest}${suffix}` };
105
+ }
106
+
107
+ /** The address without a language prefix: `/ja/pricing` gives `/pricing`. */
108
+ export function stripLanguagePrefix(path: string, options?: LanguageRoutingOptions): string {
109
+ return parseLanguagePath(path, options).restPath;
110
+ }
111
+
112
+ /**
113
+ * The address in `language`: `withLanguagePrefix("/pricing", "ja")` gives
114
+ * `/ja/pricing`. A prefix already there is replaced, never doubled. The
115
+ * default language, and languages without a prefix, give the bare address
116
+ * (unless `prefixDefaultLanguage` is on).
117
+ */
118
+ export function withLanguagePrefix(
119
+ path: string,
120
+ language: string,
121
+ options?: LanguageRoutingOptions
122
+ ): string {
123
+ const resolved = resolve(options);
124
+ const rest = stripLanguagePrefix(path, options);
125
+ const configured = languageFromSlug(language, resolved.languages);
126
+ const isDefault = languageSlug(language) === languageSlug(resolved.defaultLanguage);
127
+
128
+ if (!configured || (isDefault && !resolved.prefixDefaultLanguage)) {
129
+ return rest;
130
+ }
131
+
132
+ const [restPath, suffix] = splitSuffix(rest);
133
+ const tail = restPath === "/" ? "" : restPath;
134
+ return `/${languageSlug(configured)}${tail}${suffix}`;
135
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * The server's check that a value is a language, for a product's Convex
3
+ * functions and schema. Built from the list in `./languages`, so it cannot
4
+ * drift from what the interface offers.
5
+ *
6
+ * Import it by path, from a product's `convex/` folder:
7
+ *
8
+ * import { languageValidator } from "@0001-labs/design-system-1/DS1/0-face/language-validator.ts";
9
+ *
10
+ * It is not exported from `DS1/index.ts`, so the browser bundle stays free of
11
+ * `convex/values`.
12
+ */
13
+ import { v } from "convex/values";
14
+ import type { VLiteral, VUnion } from "convex/values";
15
+ import { LANGUAGES, isLanguage } from "./languages";
16
+ import type { Language } from "./languages";
17
+
18
+ export type LanguageValidator<L extends Language = Language> = VUnion<
19
+ L,
20
+ VLiteral<L, "required">[],
21
+ "required",
22
+ never
23
+ >;
24
+
25
+ /**
26
+ * A validator for some of the languages: the ones a product saves, say. Every
27
+ * code must be in the list, and there must be at least one.
28
+ */
29
+ export function languageValidatorFor<L extends Language>(
30
+ languages: readonly L[]
31
+ ): LanguageValidator<L> {
32
+ if (languages.length === 0) {
33
+ throw new Error("languageValidatorFor needs at least one language.");
34
+ }
35
+ const unknown = languages.filter((code) => !isLanguage(code));
36
+ if (unknown.length > 0) {
37
+ throw new Error(
38
+ `languageValidatorFor got codes that are not in the language list: ${unknown.join(", ")}`
39
+ );
40
+ }
41
+ const unique = [...new Set(languages)];
42
+ return v.union(...unique.map((code) => v.literal(code))) as LanguageValidator<L>;
43
+ }
44
+
45
+ /** Accepts every language in the list (`LANGUAGES`). */
46
+ export const languageValidator: LanguageValidator = languageValidatorFor(LANGUAGES);
@@ -0,0 +1,242 @@
1
+ /**
2
+ * The languages every 0001 product chooses from: one list, with the name of
3
+ * each in English and in itself, and how to read whatever a browser, a URL or
4
+ * a saved setting calls a language.
5
+ *
6
+ * No browser code, so a product's backend (Convex, Node) imports it as well.
7
+ * Each product turns on the subset it translates (`configureI18n({ languages })`).
8
+ */
9
+
10
+ /** Every language Design system 1 knows, in the order pickers show them. */
11
+ export const LANGUAGES = [
12
+ "da",
13
+ "de",
14
+ "en",
15
+ "es",
16
+ "fi",
17
+ "fr",
18
+ "is",
19
+ "it",
20
+ "ja",
21
+ "ko",
22
+ "nb",
23
+ "nn",
24
+ "no",
25
+ "pt",
26
+ "pt-BR",
27
+ "sv",
28
+ "zh",
29
+ ] as const;
30
+
31
+ export type Language = (typeof LANGUAGES)[number];
32
+
33
+ /** The language every product falls back to. */
34
+ export const DEFAULT_LANGUAGE: Language = "en";
35
+
36
+ export type LanguageMetadata = {
37
+ code: Language;
38
+ /** The name in English */
39
+ label: string;
40
+ /** The name in the language itself, as a picker shows it */
41
+ nativeLabel: string;
42
+ /** Other tags that mean this language, lower case (`da-dk`) */
43
+ aliases: readonly string[];
44
+ };
45
+
46
+ export const LANGUAGE_METADATA: Readonly<Record<Language, LanguageMetadata>> = {
47
+ da: { code: "da", label: "Danish", nativeLabel: "Dansk", aliases: ["da-dk"] },
48
+ de: {
49
+ code: "de",
50
+ label: "German",
51
+ nativeLabel: "Deutsch",
52
+ aliases: ["de-de", "de-at", "de-ch"],
53
+ },
54
+ en: {
55
+ code: "en",
56
+ label: "English",
57
+ nativeLabel: "English",
58
+ aliases: ["en-us", "en-gb", "en-ca", "en-au", "en-nz", "en-ie"],
59
+ },
60
+ es: {
61
+ code: "es",
62
+ label: "Spanish",
63
+ nativeLabel: "Español",
64
+ aliases: ["es-es", "es-mx", "es-ar", "es-cl", "es-co"],
65
+ },
66
+ fi: { code: "fi", label: "Finnish", nativeLabel: "Suomi", aliases: ["fi-fi"] },
67
+ fr: {
68
+ code: "fr",
69
+ label: "French",
70
+ nativeLabel: "Français",
71
+ aliases: ["fr-fr", "fr-ca", "fr-be", "fr-ch"],
72
+ },
73
+ is: { code: "is", label: "Icelandic", nativeLabel: "Íslenska", aliases: ["is-is"] },
74
+ it: { code: "it", label: "Italian", nativeLabel: "Italiano", aliases: ["it-it", "it-ch"] },
75
+ ja: { code: "ja", label: "Japanese", nativeLabel: "日本語", aliases: ["ja-jp"] },
76
+ ko: { code: "ko", label: "Korean", nativeLabel: "한국어", aliases: ["ko-kr"] },
77
+ nb: {
78
+ code: "nb",
79
+ label: "Norwegian Bokmål",
80
+ nativeLabel: "Norsk bokmål",
81
+ aliases: ["nb-no"],
82
+ },
83
+ nn: {
84
+ code: "nn",
85
+ label: "Norwegian Nynorsk",
86
+ nativeLabel: "Norsk nynorsk",
87
+ aliases: ["nn-no"],
88
+ },
89
+ no: { code: "no", label: "Norwegian", nativeLabel: "Norsk", aliases: ["no-no"] },
90
+ pt: { code: "pt", label: "Portuguese", nativeLabel: "Português", aliases: ["pt-pt"] },
91
+ "pt-BR": {
92
+ code: "pt-BR",
93
+ label: "Brazilian Portuguese",
94
+ nativeLabel: "Português (Brasil)",
95
+ aliases: ["pt-br"],
96
+ },
97
+ sv: { code: "sv", label: "Swedish", nativeLabel: "Svenska", aliases: ["sv-se", "sv-fi"] },
98
+ zh: {
99
+ code: "zh",
100
+ label: "Chinese",
101
+ nativeLabel: "中文",
102
+ aliases: ["zh-cn", "zh-tw", "zh-hk", "zh-sg", "zh-hans", "zh-hant"],
103
+ },
104
+ };
105
+
106
+ /**
107
+ * Languages close enough to stand in for each other, nearest first. A product
108
+ * that speaks only one of them gets it for the others (Link speaks `nb`, so
109
+ * `no` and `nn` read as `nb`; it speaks `pt-BR`, so `pt` reads as `pt-BR`), and
110
+ * a missing word is looked up in them before English.
111
+ */
112
+ const RELATED_LANGUAGES: Readonly<Partial<Record<Language, readonly Language[]>>> = {
113
+ nb: ["no", "nn"],
114
+ no: ["nb", "nn"],
115
+ nn: ["nb", "no"],
116
+ pt: ["pt-BR"],
117
+ "pt-BR": ["pt"],
118
+ };
119
+
120
+ /** Every tag, lower case, to the language it means. */
121
+ const ALIASES = new Map<string, Language>();
122
+ for (const code of LANGUAGES) {
123
+ ALIASES.set(code.toLowerCase(), code);
124
+ for (const alias of LANGUAGE_METADATA[code].aliases) {
125
+ ALIASES.set(alias, code);
126
+ }
127
+ }
128
+
129
+ const ORDER = new Map<string, number>(LANGUAGES.map((code, index) => [code, index]));
130
+
131
+ /** Whether a value is one of the list's codes, exactly as written there. */
132
+ export function isLanguage(value: unknown): value is Language {
133
+ return typeof value === "string" && ORDER.has(value);
134
+ }
135
+
136
+ function primarySubtag(tag: string): string {
137
+ return tag.split("-")[0] ?? "";
138
+ }
139
+
140
+ /** The first of `language` and its relatives that `allowed` holds. */
141
+ function within(language: Language, allowed: ReadonlySet<Language>): Language | null {
142
+ if (allowed.has(language)) {
143
+ return language;
144
+ }
145
+ return RELATED_LANGUAGES[language]?.find((related) => allowed.has(related)) ?? null;
146
+ }
147
+
148
+ /**
149
+ * Read any way of naming a language as one of the list: `da-DK`, `nb_NO`,
150
+ * `pt-br`, or a browser's list (`"nl-NL,da;q=0.8"`, the first language it
151
+ * knows wins). Pass the product's languages to land on one it speaks: a
152
+ * relative stands in where the product lacks the language itself, so with
153
+ * Link's languages `no` and `nn` read as `nb`, and `pt` as `pt-BR`. Returns
154
+ * `fallback` when nothing matches (`null` to tell).
155
+ */
156
+ export function normalizeLanguage(
157
+ input: string | null | undefined,
158
+ fallback?: Language,
159
+ languages?: readonly Language[]
160
+ ): Language;
161
+ export function normalizeLanguage(
162
+ input: string | null | undefined,
163
+ fallback: null,
164
+ languages?: readonly Language[]
165
+ ): Language | null;
166
+ export function normalizeLanguage(
167
+ input: string | null | undefined,
168
+ fallback: Language | null = DEFAULT_LANGUAGE,
169
+ languages: readonly Language[] = LANGUAGES
170
+ ): Language | null {
171
+ const allowed = new Set(languages);
172
+ const tokens = String(input ?? "")
173
+ .split(",")
174
+ .map((token) => token.split(";")[0]?.trim().toLowerCase().replace(/_/g, "-") ?? "")
175
+ .filter(Boolean);
176
+
177
+ for (const token of tokens) {
178
+ const exact = ALIASES.get(token);
179
+ const match = exact ? within(exact, allowed) : null;
180
+ if (match) {
181
+ return match;
182
+ }
183
+ const base = ALIASES.get(primarySubtag(token));
184
+ const baseMatch = base ? within(base, allowed) : null;
185
+ if (baseMatch) {
186
+ return baseMatch;
187
+ }
188
+ }
189
+
190
+ return fallback;
191
+ }
192
+
193
+ /**
194
+ * The languages of `subset` that the list knows, in the list's order, each
195
+ * once. Codes are matched whatever their case (`pt-br` is `pt-BR`); others are
196
+ * left out.
197
+ */
198
+ export function pickLanguages(subset: Iterable<string>): Language[] {
199
+ const wanted = new Set<Language>();
200
+ for (const code of subset) {
201
+ const member = ALIASES.get(String(code).toLowerCase().replace(/_/g, "-"));
202
+ if (member && member.toLowerCase() === String(code).toLowerCase().replace(/_/g, "-")) {
203
+ wanted.add(member);
204
+ }
205
+ }
206
+ return LANGUAGES.filter((code) => wanted.has(code));
207
+ }
208
+
209
+ /**
210
+ * Where a language sits in the list's order, for sorting: the code itself,
211
+ * else its primary subtag; codes the list does not know come last.
212
+ */
213
+ export function languageOrder(code: string): number {
214
+ const normalized = String(code ?? "").toLowerCase().replace(/_/g, "-");
215
+ const member = ALIASES.get(normalized) ?? ALIASES.get(primarySubtag(normalized));
216
+ return member ? (ORDER.get(member) ?? LANGUAGES.length) : LANGUAGES.length;
217
+ }
218
+
219
+ /**
220
+ * The languages to look a word up in for `language`, before English: the
221
+ * language as given, its primary subtag (`ja-JP` reads `ja`), then its
222
+ * relatives (`nn` reads `nb`, `pt-BR` reads `pt`).
223
+ */
224
+ export function fallbackLanguages(language: string): string[] {
225
+ const codes: string[] = [];
226
+ const add = (code: string | undefined) => {
227
+ if (code && !codes.includes(code)) {
228
+ codes.push(code);
229
+ }
230
+ };
231
+
232
+ add(language);
233
+ const normalized = String(language ?? "").toLowerCase().replace(/_/g, "-");
234
+ const member = ALIASES.get(normalized);
235
+ add(member);
236
+ add(primarySubtag(normalized));
237
+ const related = member ?? ALIASES.get(primarySubtag(normalized));
238
+ for (const code of related ? (RELATED_LANGUAGES[related] ?? []) : []) {
239
+ add(code);
240
+ }
241
+ return codes;
242
+ }
@@ -0,0 +1,184 @@
1
+ /** Inspect named page areas without changing their size, opacity, or interaction. */
2
+ export function createLayoutInspector() {
3
+ const root = document.documentElement;
4
+ const overlay = document.createElement('div');
5
+ overlay.setAttribute('data-ds-layout-overlay', '');
6
+ overlay.setAttribute('aria-hidden', 'true');
7
+ overlay.hidden = true;
8
+ const shadow = overlay.attachShadow({ mode: 'open' });
9
+ shadow.innerHTML = `<style>
10
+ :host {
11
+ all: initial !important;
12
+ display: block !important;
13
+ position: fixed !important;
14
+ inset: 0 !important;
15
+ z-index: 2147483646 !important;
16
+ pointer-events: none !important;
17
+ color-scheme: inherit !important;
18
+ }
19
+ :host([hidden]) { display: none !important; }
20
+ .veil, .areas { position: absolute; inset: 0; overflow: hidden; }
21
+ .veil { background: color-mix(in srgb, var(--page-background, Canvas) 78%, transparent); }
22
+ .area {
23
+ position: absolute;
24
+ box-sizing: border-box;
25
+ display: grid;
26
+ place-items: center;
27
+ background: color-mix(in srgb, var(--pastel) 40%, transparent);
28
+ border: 1px solid color-mix(in srgb, var(--pastel) 65%, #82718d);
29
+ }
30
+ .name {
31
+ box-sizing: border-box;
32
+ max-width: 100%;
33
+ padding: 3px 8px;
34
+ background: var(--page-background, Canvas);
35
+ color: var(--base-slate, CanvasText);
36
+ font: 400 14px/20px var(--font-regular, var(--typeface-regular, sans-serif));
37
+ text-align: center;
38
+ overflow-wrap: anywhere;
39
+ }
40
+ .area[data-container] {
41
+ background: transparent;
42
+ }
43
+ .area[data-container] > .name {
44
+ position: absolute;
45
+ top: 0;
46
+ left: 0;
47
+ }
48
+ .area[data-compact] .name {
49
+ position: absolute;
50
+ bottom: 100%;
51
+ left: 50%;
52
+ transform: translateX(-50%);
53
+ width: max-content;
54
+ max-width: none;
55
+ padding: 2px 4px;
56
+ font-size: 12px;
57
+ line-height: 16px;
58
+ white-space: nowrap;
59
+ }
60
+ .dimensions {
61
+ display: block;
62
+ font-size: 12px;
63
+ line-height: 16px;
64
+ font-variant-numeric: tabular-nums;
65
+ white-space: nowrap;
66
+ }
67
+ @media print { :host { display: none !important; } }
68
+ </style><div class="veil"></div><div class="areas"></div>`;
69
+ document.body.append(overlay);
70
+ const layer = shadow.querySelector<HTMLDivElement>('.areas')!;
71
+ const colors = ['#efc9dc', '#d5c9ef', '#c2d9eb', '#bfe0cd', '#eee3b7', '#efcfb8'];
72
+ let frame = 0;
73
+ let visible = false;
74
+ const observed = new Set<HTMLElement>();
75
+ const observedRoots = new Set<ShadowRoot>();
76
+
77
+ const getAreas = () => {
78
+ const named: HTMLElement[] = [];
79
+ const collect = (scope: Document | ShadowRoot | HTMLElement) => {
80
+ named.push(...scope.querySelectorAll<HTMLElement>('[data-ds-area]'));
81
+ scope.querySelectorAll<HTMLElement>('[data-ds-layout-root]').forEach((host) => {
82
+ if (!host.shadowRoot) return;
83
+ if (!observedRoots.has(host.shadowRoot)) {
84
+ mutations.observe(host.shadowRoot, { subtree: true, childList: true, characterData: true });
85
+ observedRoots.add(host.shadowRoot);
86
+ }
87
+ collect(host.shadowRoot);
88
+ });
89
+ };
90
+ collect(document.querySelector<HTMLElement>('[data-ds-inspect-active]') ?? document);
91
+ if (named.length) return named;
92
+ // Semantic fallback for pages that have not supplied their own area names.
93
+ return Array.from(document.querySelectorAll<HTMLElement>('header, main, section, footer, one-footer'))
94
+ .filter((element) => !element.querySelector('section, header, footer, one-footer'));
95
+ };
96
+ const render = () => {
97
+ frame = 0;
98
+ if (!visible) return;
99
+ const areas = getAreas();
100
+ const current = new Set(areas);
101
+ for (const element of observed) {
102
+ if (!current.has(element)) { resize.unobserve(element); observed.delete(element); }
103
+ }
104
+ const fragments = document.createDocumentFragment();
105
+ areas.forEach((element, index) => {
106
+ if (!observed.has(element)) { resize.observe(element); observed.add(element); }
107
+ const rect = element.getBoundingClientRect();
108
+ if (!rect.width || !rect.height || rect.bottom <= 0 || rect.top >= innerHeight || rect.right <= 0 || rect.left >= innerWidth) return;
109
+ if (getComputedStyle(element).visibility === 'hidden') return;
110
+ const panel = document.createElement('div');
111
+ panel.className = 'area';
112
+ panel.toggleAttribute('data-container', !!element.shadowRoot?.querySelector('[data-ds-area]'));
113
+ panel.toggleAttribute('data-compact', rect.width < 80 || rect.height < 24);
114
+ panel.style.left = `${rect.left}px`;
115
+ panel.style.top = `${rect.top}px`;
116
+ panel.style.width = `${rect.width}px`;
117
+ panel.style.height = `${rect.height}px`;
118
+ panel.style.setProperty('--pastel', colors[index % colors.length]);
119
+ const label = document.createElement('span');
120
+ label.className = 'name';
121
+ const areaName = element.dataset.dsArea || element.getAttribute('aria-label') ||
122
+ (element.localName === 'one-footer' ? 'footer' : element.localName);
123
+ label.textContent = areaName.replace(/[-_]/g, ' ').replace(/^./, (letter) => letter.toUpperCase());
124
+ const dimensions = document.createElement('span');
125
+ dimensions.className = 'dimensions';
126
+ dimensions.textContent = `${Number(rect.width.toFixed(1))} × ${Number(rect.height.toFixed(1))} px`;
127
+ label.append(dimensions);
128
+ const ratio = document.createElement('span');
129
+ ratio.className = 'dimensions';
130
+ // Reduce dimensions measured to a tenth of a CSS pixel; use a decimal
131
+ // for fluid shapes whose exact fraction would be too long to read.
132
+ const width = Math.max(1, Math.round(rect.width * 10));
133
+ const height = Math.max(1, Math.round(rect.height * 10));
134
+ let divisor = width;
135
+ let remainder = height;
136
+ while (remainder) {
137
+ [divisor, remainder] = [remainder, divisor % remainder];
138
+ }
139
+ const horizontal = width / divisor;
140
+ const vertical = height / divisor;
141
+ ratio.textContent = horizontal <= 100 && vertical <= 100
142
+ ? `${horizontal}:${vertical}`
143
+ : `≈ ${Number((rect.width / rect.height).toFixed(2))}:1`;
144
+ label.append(ratio);
145
+ panel.append(label);
146
+ fragments.append(panel);
147
+ });
148
+ layer.replaceChildren(fragments);
149
+ };
150
+ const schedule = () => { if (visible && !frame) frame = requestAnimationFrame(render); };
151
+ const resize = new ResizeObserver(schedule);
152
+ const mutations = new MutationObserver(schedule);
153
+ const setVisible = (next: boolean) => {
154
+ if (next === visible) return;
155
+ visible = next;
156
+ overlay.hidden = !visible;
157
+ root.toggleAttribute('data-ds-layout-visible', visible);
158
+ if (visible) {
159
+ resize.observe(document.body);
160
+ mutations.observe(document.body, { subtree: true, childList: true, characterData: true });
161
+ window.addEventListener('scroll', schedule, { passive: true, capture: true });
162
+ window.addEventListener('resize', schedule);
163
+ window.addEventListener('scaling-changed', schedule);
164
+ window.addEventListener('design-system-layout-sized', schedule);
165
+ window.addEventListener('design-system-layout-ready', schedule);
166
+ schedule();
167
+ } else {
168
+ resize.disconnect();
169
+ observed.clear();
170
+ mutations.disconnect();
171
+ observedRoots.clear();
172
+ window.removeEventListener('scroll', schedule, true);
173
+ window.removeEventListener('resize', schedule);
174
+ window.removeEventListener('scaling-changed', schedule);
175
+ window.removeEventListener('design-system-layout-sized', schedule);
176
+ window.removeEventListener('design-system-layout-ready', schedule);
177
+ cancelAnimationFrame(frame);
178
+ frame = 0;
179
+ layer.replaceChildren();
180
+ }
181
+ window.dispatchEvent(new CustomEvent('design-system-layout-changed', { detail: { visible } }));
182
+ };
183
+ return { toggle: () => setVisible(!visible), hide: () => setVisible(false) };
184
+ }
@@ -0,0 +1,39 @@
1
+ import type { LanguageCode } from "./i18n";
2
+ import type { ThemeType } from "./theme";
3
+
4
+ export type Preferences = {
5
+ language?: LanguageCode;
6
+ theme?: ThemeType;
7
+ [key: string]: unknown;
8
+ };
9
+
10
+ /** Product storage names are configuration; the implementation stays shared. */
11
+ export interface PreferenceOptions {
12
+ storageKey?: string;
13
+ themeStorageKey?: string;
14
+ }
15
+
16
+ declare global {
17
+ interface Window { DS_ONE_PREFERENCES?: PreferenceOptions; }
18
+ }
19
+
20
+ export function preferenceStorageKey(kind: "preferences" | "theme"): string {
21
+ const options = typeof window === "undefined" ? undefined : window.DS_ONE_PREFERENCES;
22
+ return (kind === "theme" ? options?.themeStorageKey : options?.storageKey) || `ds-one:${kind}`;
23
+ }
24
+
25
+ export function savePreferences(preferences: Preferences): void {
26
+ if (typeof window === "undefined") {
27
+ return;
28
+ }
29
+
30
+ try {
31
+ const key = preferenceStorageKey("preferences");
32
+ const raw = window.localStorage?.getItem(key);
33
+ const existing = raw ? (JSON.parse(raw) as Record<string, unknown>) : {};
34
+ const next = { ...existing, ...preferences };
35
+ window.localStorage?.setItem(key, JSON.stringify(next));
36
+ } catch (error) {
37
+ console.warn("ds-one: unable to persist preferences", error);
38
+ }
39
+ }