@mesgflow/ui-kit 0.6.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 (230) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +200 -0
  3. package/fesm2022/mesgflow-ui-kit-accordion.mjs +75 -0
  4. package/fesm2022/mesgflow-ui-kit-accordion.mjs.map +1 -0
  5. package/fesm2022/mesgflow-ui-kit-alert.mjs +165 -0
  6. package/fesm2022/mesgflow-ui-kit-alert.mjs.map +1 -0
  7. package/fesm2022/mesgflow-ui-kit-avatar-group.mjs +88 -0
  8. package/fesm2022/mesgflow-ui-kit-avatar-group.mjs.map +1 -0
  9. package/fesm2022/mesgflow-ui-kit-avatar.mjs +123 -0
  10. package/fesm2022/mesgflow-ui-kit-avatar.mjs.map +1 -0
  11. package/fesm2022/mesgflow-ui-kit-badge.mjs +68 -0
  12. package/fesm2022/mesgflow-ui-kit-badge.mjs.map +1 -0
  13. package/fesm2022/mesgflow-ui-kit-banner.mjs +111 -0
  14. package/fesm2022/mesgflow-ui-kit-banner.mjs.map +1 -0
  15. package/fesm2022/mesgflow-ui-kit-box-input.mjs +78 -0
  16. package/fesm2022/mesgflow-ui-kit-box-input.mjs.map +1 -0
  17. package/fesm2022/mesgflow-ui-kit-brand.mjs +120 -0
  18. package/fesm2022/mesgflow-ui-kit-brand.mjs.map +1 -0
  19. package/fesm2022/mesgflow-ui-kit-breadcrumb.mjs +84 -0
  20. package/fesm2022/mesgflow-ui-kit-breadcrumb.mjs.map +1 -0
  21. package/fesm2022/mesgflow-ui-kit-button-group.mjs +37 -0
  22. package/fesm2022/mesgflow-ui-kit-button-group.mjs.map +1 -0
  23. package/fesm2022/mesgflow-ui-kit-button.mjs +359 -0
  24. package/fesm2022/mesgflow-ui-kit-button.mjs.map +1 -0
  25. package/fesm2022/mesgflow-ui-kit-card.mjs +128 -0
  26. package/fesm2022/mesgflow-ui-kit-card.mjs.map +1 -0
  27. package/fesm2022/mesgflow-ui-kit-chart.mjs +538 -0
  28. package/fesm2022/mesgflow-ui-kit-chart.mjs.map +1 -0
  29. package/fesm2022/mesgflow-ui-kit-checkbox.mjs +45 -0
  30. package/fesm2022/mesgflow-ui-kit-checkbox.mjs.map +1 -0
  31. package/fesm2022/mesgflow-ui-kit-chip.mjs +123 -0
  32. package/fesm2022/mesgflow-ui-kit-chip.mjs.map +1 -0
  33. package/fesm2022/mesgflow-ui-kit-code-block.mjs +71 -0
  34. package/fesm2022/mesgflow-ui-kit-code-block.mjs.map +1 -0
  35. package/fesm2022/mesgflow-ui-kit-collapsible.mjs +89 -0
  36. package/fesm2022/mesgflow-ui-kit-collapsible.mjs.map +1 -0
  37. package/fesm2022/mesgflow-ui-kit-color-picker.mjs +210 -0
  38. package/fesm2022/mesgflow-ui-kit-color-picker.mjs.map +1 -0
  39. package/fesm2022/mesgflow-ui-kit-combobox.mjs +181 -0
  40. package/fesm2022/mesgflow-ui-kit-combobox.mjs.map +1 -0
  41. package/fesm2022/mesgflow-ui-kit-command.mjs +491 -0
  42. package/fesm2022/mesgflow-ui-kit-command.mjs.map +1 -0
  43. package/fesm2022/mesgflow-ui-kit-context-menu.mjs +92 -0
  44. package/fesm2022/mesgflow-ui-kit-context-menu.mjs.map +1 -0
  45. package/fesm2022/mesgflow-ui-kit-copy-button.mjs +167 -0
  46. package/fesm2022/mesgflow-ui-kit-copy-button.mjs.map +1 -0
  47. package/fesm2022/mesgflow-ui-kit-core.mjs +624 -0
  48. package/fesm2022/mesgflow-ui-kit-core.mjs.map +1 -0
  49. package/fesm2022/mesgflow-ui-kit-date-picker.mjs +643 -0
  50. package/fesm2022/mesgflow-ui-kit-date-picker.mjs.map +1 -0
  51. package/fesm2022/mesgflow-ui-kit-description-list.mjs +76 -0
  52. package/fesm2022/mesgflow-ui-kit-description-list.mjs.map +1 -0
  53. package/fesm2022/mesgflow-ui-kit-dialog.mjs +700 -0
  54. package/fesm2022/mesgflow-ui-kit-dialog.mjs.map +1 -0
  55. package/fesm2022/mesgflow-ui-kit-divider.mjs +62 -0
  56. package/fesm2022/mesgflow-ui-kit-divider.mjs.map +1 -0
  57. package/fesm2022/mesgflow-ui-kit-dropzone.mjs +126 -0
  58. package/fesm2022/mesgflow-ui-kit-dropzone.mjs.map +1 -0
  59. package/fesm2022/mesgflow-ui-kit-empty-state.mjs +70 -0
  60. package/fesm2022/mesgflow-ui-kit-empty-state.mjs.map +1 -0
  61. package/fesm2022/mesgflow-ui-kit-field.mjs +42 -0
  62. package/fesm2022/mesgflow-ui-kit-field.mjs.map +1 -0
  63. package/fesm2022/mesgflow-ui-kit-hover-card.mjs +171 -0
  64. package/fesm2022/mesgflow-ui-kit-hover-card.mjs.map +1 -0
  65. package/fesm2022/mesgflow-ui-kit-i18n-tr.mjs +258 -0
  66. package/fesm2022/mesgflow-ui-kit-i18n-tr.mjs.map +1 -0
  67. package/fesm2022/mesgflow-ui-kit-icon.mjs +174 -0
  68. package/fesm2022/mesgflow-ui-kit-icon.mjs.map +1 -0
  69. package/fesm2022/mesgflow-ui-kit-input.mjs +448 -0
  70. package/fesm2022/mesgflow-ui-kit-input.mjs.map +1 -0
  71. package/fesm2022/mesgflow-ui-kit-kbd.mjs +24 -0
  72. package/fesm2022/mesgflow-ui-kit-kbd.mjs.map +1 -0
  73. package/fesm2022/mesgflow-ui-kit-link.mjs +48 -0
  74. package/fesm2022/mesgflow-ui-kit-link.mjs.map +1 -0
  75. package/fesm2022/mesgflow-ui-kit-menu.mjs +105 -0
  76. package/fesm2022/mesgflow-ui-kit-menu.mjs.map +1 -0
  77. package/fesm2022/mesgflow-ui-kit-number-input.mjs +153 -0
  78. package/fesm2022/mesgflow-ui-kit-number-input.mjs.map +1 -0
  79. package/fesm2022/mesgflow-ui-kit-otp-input.mjs +164 -0
  80. package/fesm2022/mesgflow-ui-kit-otp-input.mjs.map +1 -0
  81. package/fesm2022/mesgflow-ui-kit-page-header.mjs +83 -0
  82. package/fesm2022/mesgflow-ui-kit-page-header.mjs.map +1 -0
  83. package/fesm2022/mesgflow-ui-kit-pagination.mjs +99 -0
  84. package/fesm2022/mesgflow-ui-kit-pagination.mjs.map +1 -0
  85. package/fesm2022/mesgflow-ui-kit-password-input.mjs +120 -0
  86. package/fesm2022/mesgflow-ui-kit-password-input.mjs.map +1 -0
  87. package/fesm2022/mesgflow-ui-kit-popover.mjs +213 -0
  88. package/fesm2022/mesgflow-ui-kit-popover.mjs.map +1 -0
  89. package/fesm2022/mesgflow-ui-kit-progress.mjs +82 -0
  90. package/fesm2022/mesgflow-ui-kit-progress.mjs.map +1 -0
  91. package/fesm2022/mesgflow-ui-kit-radio.mjs +123 -0
  92. package/fesm2022/mesgflow-ui-kit-radio.mjs.map +1 -0
  93. package/fesm2022/mesgflow-ui-kit-rating.mjs +116 -0
  94. package/fesm2022/mesgflow-ui-kit-rating.mjs.map +1 -0
  95. package/fesm2022/mesgflow-ui-kit-scroll-area.mjs +84 -0
  96. package/fesm2022/mesgflow-ui-kit-scroll-area.mjs.map +1 -0
  97. package/fesm2022/mesgflow-ui-kit-search-input.mjs +154 -0
  98. package/fesm2022/mesgflow-ui-kit-search-input.mjs.map +1 -0
  99. package/fesm2022/mesgflow-ui-kit-segmented-control.mjs +95 -0
  100. package/fesm2022/mesgflow-ui-kit-segmented-control.mjs.map +1 -0
  101. package/fesm2022/mesgflow-ui-kit-select.mjs +227 -0
  102. package/fesm2022/mesgflow-ui-kit-select.mjs.map +1 -0
  103. package/fesm2022/mesgflow-ui-kit-sidebar.mjs +449 -0
  104. package/fesm2022/mesgflow-ui-kit-sidebar.mjs.map +1 -0
  105. package/fesm2022/mesgflow-ui-kit-skeleton.mjs +74 -0
  106. package/fesm2022/mesgflow-ui-kit-skeleton.mjs.map +1 -0
  107. package/fesm2022/mesgflow-ui-kit-slider.mjs +179 -0
  108. package/fesm2022/mesgflow-ui-kit-slider.mjs.map +1 -0
  109. package/fesm2022/mesgflow-ui-kit-spinner.mjs +67 -0
  110. package/fesm2022/mesgflow-ui-kit-spinner.mjs.map +1 -0
  111. package/fesm2022/mesgflow-ui-kit-splitter.mjs +97 -0
  112. package/fesm2022/mesgflow-ui-kit-splitter.mjs.map +1 -0
  113. package/fesm2022/mesgflow-ui-kit-stat-tile.mjs +98 -0
  114. package/fesm2022/mesgflow-ui-kit-stat-tile.mjs.map +1 -0
  115. package/fesm2022/mesgflow-ui-kit-stepper.mjs +133 -0
  116. package/fesm2022/mesgflow-ui-kit-stepper.mjs.map +1 -0
  117. package/fesm2022/mesgflow-ui-kit-switch.mjs +73 -0
  118. package/fesm2022/mesgflow-ui-kit-switch.mjs.map +1 -0
  119. package/fesm2022/mesgflow-ui-kit-table.mjs +1783 -0
  120. package/fesm2022/mesgflow-ui-kit-table.mjs.map +1 -0
  121. package/fesm2022/mesgflow-ui-kit-tabs.mjs +569 -0
  122. package/fesm2022/mesgflow-ui-kit-tabs.mjs.map +1 -0
  123. package/fesm2022/mesgflow-ui-kit-tag-input.mjs +138 -0
  124. package/fesm2022/mesgflow-ui-kit-tag-input.mjs.map +1 -0
  125. package/fesm2022/mesgflow-ui-kit-theme-switcher.mjs +53 -0
  126. package/fesm2022/mesgflow-ui-kit-theme-switcher.mjs.map +1 -0
  127. package/fesm2022/mesgflow-ui-kit-theming.mjs +1016 -0
  128. package/fesm2022/mesgflow-ui-kit-theming.mjs.map +1 -0
  129. package/fesm2022/mesgflow-ui-kit-time-picker.mjs +138 -0
  130. package/fesm2022/mesgflow-ui-kit-time-picker.mjs.map +1 -0
  131. package/fesm2022/mesgflow-ui-kit-timeline.mjs +91 -0
  132. package/fesm2022/mesgflow-ui-kit-timeline.mjs.map +1 -0
  133. package/fesm2022/mesgflow-ui-kit-toast.mjs +202 -0
  134. package/fesm2022/mesgflow-ui-kit-toast.mjs.map +1 -0
  135. package/fesm2022/mesgflow-ui-kit-toggle.mjs +243 -0
  136. package/fesm2022/mesgflow-ui-kit-toggle.mjs.map +1 -0
  137. package/fesm2022/mesgflow-ui-kit-toolbar.mjs +99 -0
  138. package/fesm2022/mesgflow-ui-kit-toolbar.mjs.map +1 -0
  139. package/fesm2022/mesgflow-ui-kit-tooltip.mjs +160 -0
  140. package/fesm2022/mesgflow-ui-kit-tooltip.mjs.map +1 -0
  141. package/fesm2022/mesgflow-ui-kit-tour.mjs +272 -0
  142. package/fesm2022/mesgflow-ui-kit-tour.mjs.map +1 -0
  143. package/fesm2022/mesgflow-ui-kit-tree-select.mjs +177 -0
  144. package/fesm2022/mesgflow-ui-kit-tree-select.mjs.map +1 -0
  145. package/fesm2022/mesgflow-ui-kit-tree.mjs +82 -0
  146. package/fesm2022/mesgflow-ui-kit-tree.mjs.map +1 -0
  147. package/fesm2022/mesgflow-ui-kit.mjs +12851 -0
  148. package/fesm2022/mesgflow-ui-kit.mjs.map +1 -0
  149. package/package.json +374 -0
  150. package/preset.css +20 -0
  151. package/tokens/color.css +693 -0
  152. package/tokens/contrast.css +68 -0
  153. package/tokens/elevation.css +68 -0
  154. package/tokens/motion.css +277 -0
  155. package/tokens/radius.css +69 -0
  156. package/tokens/typography.css +127 -0
  157. package/tokens.css +41 -0
  158. package/types/mesgflow-ui-kit-accordion.d.ts +42 -0
  159. package/types/mesgflow-ui-kit-alert.d.ts +60 -0
  160. package/types/mesgflow-ui-kit-avatar-group.d.ts +28 -0
  161. package/types/mesgflow-ui-kit-avatar.d.ts +23 -0
  162. package/types/mesgflow-ui-kit-badge.d.ts +39 -0
  163. package/types/mesgflow-ui-kit-banner.d.ts +42 -0
  164. package/types/mesgflow-ui-kit-box-input.d.ts +45 -0
  165. package/types/mesgflow-ui-kit-brand.d.ts +48 -0
  166. package/types/mesgflow-ui-kit-breadcrumb.d.ts +26 -0
  167. package/types/mesgflow-ui-kit-button-group.d.ts +17 -0
  168. package/types/mesgflow-ui-kit-button.d.ts +75 -0
  169. package/types/mesgflow-ui-kit-card.d.ts +71 -0
  170. package/types/mesgflow-ui-kit-chart.d.ts +242 -0
  171. package/types/mesgflow-ui-kit-checkbox.d.ts +17 -0
  172. package/types/mesgflow-ui-kit-chip.d.ts +46 -0
  173. package/types/mesgflow-ui-kit-code-block.d.ts +56 -0
  174. package/types/mesgflow-ui-kit-collapsible.d.ts +39 -0
  175. package/types/mesgflow-ui-kit-color-picker.d.ts +78 -0
  176. package/types/mesgflow-ui-kit-combobox.d.ts +74 -0
  177. package/types/mesgflow-ui-kit-command.d.ts +185 -0
  178. package/types/mesgflow-ui-kit-context-menu.d.ts +40 -0
  179. package/types/mesgflow-ui-kit-copy-button.d.ts +47 -0
  180. package/types/mesgflow-ui-kit-core.d.ts +508 -0
  181. package/types/mesgflow-ui-kit-date-picker.d.ts +215 -0
  182. package/types/mesgflow-ui-kit-description-list.d.ts +23 -0
  183. package/types/mesgflow-ui-kit-dialog.d.ts +342 -0
  184. package/types/mesgflow-ui-kit-divider.d.ts +23 -0
  185. package/types/mesgflow-ui-kit-dropzone.d.ts +47 -0
  186. package/types/mesgflow-ui-kit-empty-state.d.ts +18 -0
  187. package/types/mesgflow-ui-kit-field.d.ts +26 -0
  188. package/types/mesgflow-ui-kit-hover-card.d.ts +52 -0
  189. package/types/mesgflow-ui-kit-i18n-tr.d.ts +14 -0
  190. package/types/mesgflow-ui-kit-icon.d.ts +76 -0
  191. package/types/mesgflow-ui-kit-input.d.ts +186 -0
  192. package/types/mesgflow-ui-kit-kbd.d.ts +9 -0
  193. package/types/mesgflow-ui-kit-link.d.ts +20 -0
  194. package/types/mesgflow-ui-kit-menu.d.ts +53 -0
  195. package/types/mesgflow-ui-kit-number-input.d.ts +55 -0
  196. package/types/mesgflow-ui-kit-otp-input.d.ts +44 -0
  197. package/types/mesgflow-ui-kit-page-header.d.ts +31 -0
  198. package/types/mesgflow-ui-kit-pagination.d.ts +50 -0
  199. package/types/mesgflow-ui-kit-password-input.d.ts +33 -0
  200. package/types/mesgflow-ui-kit-popover.d.ts +62 -0
  201. package/types/mesgflow-ui-kit-progress.d.ts +32 -0
  202. package/types/mesgflow-ui-kit-radio.d.ts +59 -0
  203. package/types/mesgflow-ui-kit-rating.d.ts +56 -0
  204. package/types/mesgflow-ui-kit-scroll-area.d.ts +27 -0
  205. package/types/mesgflow-ui-kit-search-input.d.ts +72 -0
  206. package/types/mesgflow-ui-kit-segmented-control.d.ts +54 -0
  207. package/types/mesgflow-ui-kit-select.d.ts +89 -0
  208. package/types/mesgflow-ui-kit-sidebar.d.ts +224 -0
  209. package/types/mesgflow-ui-kit-skeleton.d.ts +29 -0
  210. package/types/mesgflow-ui-kit-slider.d.ts +40 -0
  211. package/types/mesgflow-ui-kit-spinner.d.ts +31 -0
  212. package/types/mesgflow-ui-kit-splitter.d.ts +40 -0
  213. package/types/mesgflow-ui-kit-stat-tile.d.ts +54 -0
  214. package/types/mesgflow-ui-kit-stepper.d.ts +48 -0
  215. package/types/mesgflow-ui-kit-switch.d.ts +34 -0
  216. package/types/mesgflow-ui-kit-table.d.ts +805 -0
  217. package/types/mesgflow-ui-kit-tabs.d.ts +269 -0
  218. package/types/mesgflow-ui-kit-tag-input.d.ts +33 -0
  219. package/types/mesgflow-ui-kit-theme-switcher.d.ts +27 -0
  220. package/types/mesgflow-ui-kit-theming.d.ts +745 -0
  221. package/types/mesgflow-ui-kit-time-picker.d.ts +54 -0
  222. package/types/mesgflow-ui-kit-timeline.d.ts +27 -0
  223. package/types/mesgflow-ui-kit-toast.d.ts +94 -0
  224. package/types/mesgflow-ui-kit-toggle.d.ts +110 -0
  225. package/types/mesgflow-ui-kit-toolbar.d.ts +52 -0
  226. package/types/mesgflow-ui-kit-tooltip.d.ts +55 -0
  227. package/types/mesgflow-ui-kit-tour.d.ts +66 -0
  228. package/types/mesgflow-ui-kit-tree-select.d.ts +65 -0
  229. package/types/mesgflow-ui-kit-tree.d.ts +44 -0
  230. package/types/mesgflow-ui-kit.d.ts +5444 -0
@@ -0,0 +1,1016 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, DOCUMENT, signal, computed, DestroyRef, effect, Service } from '@angular/core';
3
+
4
+ /**
5
+ * mesgflow — OKLCH renk matematigi (Bjorn Ottosson formulleri,
6
+ * https://bottosson.github.io/posts/oklab/). BAGIMSIZ (culori vb. bir
7
+ * kutuphane YOK, bkz. plan Faz 2) — yalnizca `theming/ramp.ts`'in ihtiyaci
8
+ * kadar: hex <-> OKLCH donusumu + basit gamut-kirpma. Bu dosya PUBLIC API
9
+ * DEGIL, `theming/index.ts` bunu re-export ETMEZ.
10
+ */
11
+ function srgbToLinear(c8) {
12
+ const v = c8 / 255;
13
+ return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
14
+ }
15
+ function linearToSrgb8(v) {
16
+ const clamped = Math.min(1, Math.max(0, v));
17
+ const c = clamped <= 0.0031308 ? clamped * 12.92 : 1.055 * Math.pow(clamped, 1 / 2.4) - 0.055;
18
+ return Math.round(c * 255);
19
+ }
20
+ function linearRgbToOklab(r, g, b) {
21
+ const l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
22
+ const m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;
23
+ const s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;
24
+ const l_ = Math.cbrt(l);
25
+ const m_ = Math.cbrt(m);
26
+ const s_ = Math.cbrt(s);
27
+ return [
28
+ 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_,
29
+ 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_,
30
+ 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_,
31
+ ];
32
+ }
33
+ function oklabToLinearRgb(L, a, b) {
34
+ const l_ = L + 0.3963377774 * a + 0.2158037573 * b;
35
+ const m_ = L - 0.1055613458 * a - 0.0638541728 * b;
36
+ const s_ = L - 0.0894841775 * a - 1.291485548 * b;
37
+ const l = l_ * l_ * l_;
38
+ const m = m_ * m_ * m_;
39
+ const s = s_ * s_ * s_;
40
+ return [
41
+ 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
42
+ -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
43
+ -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s,
44
+ ];
45
+ }
46
+ /** `#rgb`/`#rrggbb` -> `[r,g,b]` (0-255). Gecersiz girdi icin `[0,0,0]` (cagiran `parseHex` ile once dogrular). */
47
+ function hexToRgb(hex) {
48
+ const clean = hex.trim().replace('#', '');
49
+ const full = clean.length === 3
50
+ ? clean
51
+ .split('')
52
+ .map((c) => c + c)
53
+ .join('')
54
+ : clean;
55
+ const num = parseInt(full, 16);
56
+ if (Number.isNaN(num))
57
+ return [0, 0, 0];
58
+ return [(num >> 16) & 255, (num >> 8) & 255, num & 255];
59
+ }
60
+ function rgbToHex(r, g, b) {
61
+ const toHex = (n) => Math.round(Math.min(255, Math.max(0, n)))
62
+ .toString(16)
63
+ .padStart(2, '0');
64
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
65
+ }
66
+ /** `/^#([0-9a-f]{3}|[0-9a-f]{6})$/i` — `createAccentRamp`/`applyMfTheme`'in girdi dogrulamasi icin. */
67
+ function isValidHex(hex) {
68
+ return /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(hex.trim());
69
+ }
70
+ function hexToOklch(hex) {
71
+ const [r8, g8, b8] = hexToRgb(hex);
72
+ const r = srgbToLinear(r8);
73
+ const g = srgbToLinear(g8);
74
+ const b = srgbToLinear(b8);
75
+ const [L, a, bLab] = linearRgbToOklab(r, g, b);
76
+ const c = Math.sqrt(a * a + bLab * bLab);
77
+ let h = (Math.atan2(bLab, a) * 180) / Math.PI;
78
+ if (h < 0)
79
+ h += 360;
80
+ return { l: L, c, h };
81
+ }
82
+ /**
83
+ * OKLCH -> hex, basit gamut-kirpma ile: sRGB disina tasan L/C/H kombinasyonlari
84
+ * icin kroma kademeli olarak (%92 adimlarla) azaltilir ta ki her kanal [0,1]
85
+ * araligina girene kadar (en fazla 24 deneme — pratikte 5-6 yeter). Tam bir
86
+ * gamut-mapping algoritmasi DEGIL (ör. CSS Color 4'un "CSS Gamut Mapping"i)
87
+ * ama bir tema uretici icin yeterli: sonuc HER ZAMAN gecerli bir sRGB hex,
88
+ * ton hafifce kayabilir (asiri doygun girdilerde) ama asla clamp-artefact'i
89
+ * (yanlis renk) uretmez.
90
+ */
91
+ function oklchToHex(l, c, h) {
92
+ const hRad = (h * Math.PI) / 180;
93
+ let chroma = c;
94
+ for (let i = 0; i < 24; i++) {
95
+ const a = chroma * Math.cos(hRad);
96
+ const b = chroma * Math.sin(hRad);
97
+ const [r, g, bl] = oklabToLinearRgb(l, a, b);
98
+ const inGamut = [r, g, bl].every((v) => v >= -0.001 && v <= 1.001);
99
+ if (inGamut || chroma <= 0.0001) {
100
+ return rgbToHex(linearToSrgb8(r), linearToSrgb8(g), linearToSrgb8(bl));
101
+ }
102
+ chroma *= 0.92;
103
+ }
104
+ const [r, g, bl] = oklabToLinearRgb(l, 0, 0);
105
+ return rgbToHex(linearToSrgb8(r), linearToSrgb8(g), linearToSrgb8(bl));
106
+ }
107
+ /** WCAG göreli parlaklık (0-1) — `pickForeground`'ın kontrast oranı hesabının temeli. (`npm run check:contrast`, `scripts/check-contrast.mjs`, kitten BAĞIMSIZ kendi kopyasını taşır — aynı formül, farklı dosya.) */
108
+ function relativeLuminance(hex) {
109
+ const [r8, g8, b8] = hexToRgb(hex);
110
+ const lin = (c8) => {
111
+ const v = c8 / 255;
112
+ return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
113
+ };
114
+ return 0.2126 * lin(r8) + 0.7152 * lin(g8) + 0.0722 * lin(b8);
115
+ }
116
+ /** İki hex arası WCAG kontrast oranı (1-21). */
117
+ function contrastRatio(hexA, hexB) {
118
+ const lA = relativeLuminance(hexA);
119
+ const lB = relativeLuminance(hexB);
120
+ const lighter = Math.max(lA, lB);
121
+ const darker = Math.min(lA, lB);
122
+ return (lighter + 0.05) / (darker + 0.05);
123
+ }
124
+ /** `background` üstünde `light`/`dark` metninden HANGİSİ daha kontrastlıysa onu döner — `fgOnAccent` bunu kullanır. */
125
+ function pickForeground(background, light, dark) {
126
+ return contrastRatio(background, light) >= contrastRatio(background, dark) ? light : dark;
127
+ }
128
+ /** `a`/`b` hex'lerini `t` (0-1) oranında karıştırır — `neutralTint`'in ince renk katması için (OKLCH DEĞİL, düz sRGB lerp: küçük `t` değerlerinde (≤0.08) fark edilmez, 13 basamaklı nötr rampayı yeniden OKLCH'de üretmekten çok daha basit ve güvenli). */
129
+ function mixHex(a, b, t) {
130
+ const [ar, ag, ab] = hexToRgb(a);
131
+ const [br, bg, bb] = hexToRgb(b);
132
+ const mix = (x, y) => x + (y - x) * t;
133
+ return rgbToHex(mix(ar, br), mix(ag, bg), mix(ab, bb));
134
+ }
135
+
136
+ /**
137
+ * Basamak bağımsız SABİT bir eğri — herhangi bir girdi hue'sunda "aynı ağırlık
138
+ * hissini" garanti eder (bkz. plan Faz 2: tüketici hangi accent'i seçerse
139
+ * seçsin ramp'in 600 basamağı HER ZAMAN "metin/kenarlık" ağırlığında olmalı,
140
+ * 950 basamağı HER ZAMAN "neredeyse siyah" olmalı). `color.css`'teki
141
+ * varsayılan "Deep Emerald" rampasıyla (primary-600 = #004d40, L≈0.38)
142
+ * TAM ÖRTÜŞMEZ — bu eğride 600 = L 0.50, kasıtlı: elle taşınan varsayılan
143
+ * rampa marka için özel olarak koyulaştırıldı. Ayrıca DOLU BLOK (`accent-fill`)
144
+ * artık `accent`/`accent-strong` (600/700 tabanlı) metin/kenarlık rolünden
145
+ * TAMAMEN AYRI bir basamak çifti kullanıyor — kitin varsayılan rampasında
146
+ * LIGHT 900 / DARK 400 (spec'in "Forest Emerald" CTA'sı + canlı zümrüt CTA'sı,
147
+ * kullanıcı kararı); tüketici aksanlarında ise (bkz. `theming/apply.ts`
148
+ * `accentRoleLines()`) bu eğrinin kendi 600/400 basamakları kullanılır —
149
+ * varsayılandan farklı bir "ağırlık" üretir, bu FARK belgelidir, hata değildir.
150
+ */
151
+ const LIGHTNESS_BY_STEP = {
152
+ '50': 0.97,
153
+ '100': 0.94,
154
+ '200': 0.88,
155
+ '300': 0.79,
156
+ '400': 0.7,
157
+ '500': 0.6,
158
+ '600': 0.5,
159
+ '700': 0.42,
160
+ '800': 0.34,
161
+ '900': 0.27,
162
+ '950': 0.2,
163
+ };
164
+ /**
165
+ * Girdinin KENDİ kroması, uç basamaklarda (çok açık/çok koyu) doğal olarak
166
+ * azalır — aksi halde `950` basamağı "doygun ama neredeyse siyah" (gamut
167
+ * dışı, kirpma sonrası tonu kayan) bir renge dönüşür. `600` = girdinin TAM
168
+ * kroması (referans basamak).
169
+ */
170
+ const CHROMA_MULTIPLIER_BY_STEP = {
171
+ '50': 0.05,
172
+ '100': 0.12,
173
+ '200': 0.25,
174
+ '300': 0.45,
175
+ '400': 0.7,
176
+ '500': 0.9,
177
+ '600': 1,
178
+ '700': 0.95,
179
+ '800': 0.85,
180
+ '900': 0.7,
181
+ '950': 0.55,
182
+ };
183
+ const RAMP_STEPS = [
184
+ '50',
185
+ '100',
186
+ '200',
187
+ '300',
188
+ '400',
189
+ '500',
190
+ '600',
191
+ '700',
192
+ '800',
193
+ '900',
194
+ '950',
195
+ ];
196
+ /**
197
+ * `hex` bir accent'in "600 basamağı" gibi ele alınır (kitin kendi kuralı,
198
+ * bkz. `color.css` "Her rampada basamak 600 = LIGHT modda dolu/etkileşimli
199
+ * kullanım" notu) — girdinin hue'su + kroma karakteri korunur, tüm 11
200
+ * basamak `LIGHTNESS_BY_STEP`/`CHROMA_MULTIPLIER_BY_STEP` sabit eğrisiyle
201
+ * YENİDEN üretilir (girdinin TAM OLARAK 600'e denk gelen bir L'de olması
202
+ * ŞART DEĞİL — ör. çok açık bir hex verilse bile 950 basamağı yine
203
+ * "neredeyse siyah" çıkar, ramp her zaman tutarlı).
204
+ *
205
+ * @throws girdi `#rgb`/`#rrggbb` değilse.
206
+ */
207
+ function createAccentRamp(hex) {
208
+ if (!isValidHex(hex)) {
209
+ throw new Error(`createAccentRamp: geçersiz hex "${hex}" — #rgb veya #rrggbb bekleniyor.`);
210
+ }
211
+ const { c, h } = hexToOklch(hex);
212
+ const ramp = {};
213
+ for (const step of RAMP_STEPS) {
214
+ ramp[step] = oklchToHex(LIGHTNESS_BY_STEP[step], c * CHROMA_MULTIPLIER_BY_STEP[step], h);
215
+ }
216
+ return ramp;
217
+ }
218
+ /**
219
+ * Bir rampa basamağının üstüne binecek metin rengi — `#ffffff` veya kitin
220
+ * `--mf-surface-inverse`'i (`#04110d`, `color.css`) arasından HANGİSİ WCAG
221
+ * kontrastı daha yüksekse. `theming/apply.ts`'teki `accentRoleLines()` bunu
222
+ * `ramp['600']` (LIGHT yarı) VE `ramp['400']` (DARK yarı) için AYRI AYRI
223
+ * çağırır — accent-fill artık İKİ basamak kullanıyor (kitin varsayılanında
224
+ * 900/400, tüketici aksanlarında 600/400), genelde çok farklı parlaklıkta,
225
+ * her biri kendi doğru metin rengini WCAG hesabıyla (sabit varsayım DEĞİL)
226
+ * bulur.
227
+ */
228
+ function fgOnAccent(accentHex) {
229
+ return pickForeground(accentHex, '#ffffff', '#04110d');
230
+ }
231
+ /** `ramp`'in `600` basamağının kontrast oranını (beyaz metne karşı) döner — `ramp.spec.ts`/tüketici sağlık kontrolü için. */
232
+ function accentContrastWithWhite(ramp) {
233
+ return contrastRatio(ramp['600'], '#ffffff');
234
+ }
235
+
236
+ /**
237
+ * Nötr rampa 13 basamaklı (`850`/`975` DAHİL) — diğer altı rampanın (primary/
238
+ * success/warning/danger/info/help) 11 basamaklık `RampStep`'inden AYRI bir
239
+ * tip (bkz. `color.css` "RENK — HAM RAMPALAR" notu, `NEUTRAL_STEPS` — `apply.ts`).
240
+ */
241
+ const MF_NEUTRAL_RAMP_STEPS = [
242
+ '50',
243
+ '100',
244
+ '200',
245
+ '300',
246
+ '400',
247
+ '500',
248
+ '600',
249
+ '700',
250
+ '800',
251
+ '850',
252
+ '900',
253
+ '950',
254
+ '975',
255
+ ];
256
+ /** `ramps.<tone>` altında elle geçilebilen altı 11-basamaklı rampa — `neutral` (13 basamak) AYRI ele alınır. */
257
+ const MF_RAMP_TONES = ['primary', 'success', 'warning', 'danger', 'info', 'help'];
258
+ /**
259
+ * `color.css`'in rol katmanındaki, bir RAMPA basamağına `var()` İLE
260
+ * BAĞLI OLMAYAN (yani `ramps.*` override'ıyla OTOMATİK takip ETMEYEN) TEK
261
+ * TEK yazılmış literal token'lar — `accent`/`ramps.*` DIŞINDA kalan, marka
262
+ * beyaz etiketlemesi için hâlâ ayarlanabilir olması gereken son katman.
263
+ * (`--mf-accent`, `--mf-success-fill`, `--mf-success-tint` vb. BURADA YOK —
264
+ * hepsi zaten `ramps.primary`/`ramps.success` override'ıyla otomatik
265
+ * güncellenir, bkz. `color.css`'teki `var(--color-X-NNN)` referansları.)
266
+ */
267
+ const MF_ROLE_TOKENS = [
268
+ 'surface',
269
+ 'chrome',
270
+ 'overlay',
271
+ 'surface-inverse',
272
+ 'glass',
273
+ 'fg',
274
+ 'fg-muted',
275
+ 'fg-on-accent',
276
+ 'fg-inverse',
277
+ 'hairline',
278
+ 'neutral-fill',
279
+ 'neutral-fill-hover',
280
+ 'neutral-fill-fg',
281
+ 'success-fill-fg',
282
+ 'warning-fill-fg',
283
+ 'danger-fill-fg',
284
+ 'info-fill-fg',
285
+ 'help-fill-fg',
286
+ 'chart-1',
287
+ 'chart-2',
288
+ 'chart-3',
289
+ 'chart-4',
290
+ 'chart-5',
291
+ 'chart-6',
292
+ 'chart-7',
293
+ 'chart-8',
294
+ 'chart-other',
295
+ ];
296
+
297
+ const RADIUS_PX = {
298
+ sharp: { xs: 2, sm: 3, md: 4, lg: 6, xl: 8 },
299
+ default: { xs: 4, sm: 6, md: 8, lg: 12, xl: 16 },
300
+ soft: { xs: 6, sm: 10, md: 14, lg: 20, xl: 28 },
301
+ };
302
+ /** `radius.css`'teki `--control-h-*` ile AYNI değerler — `[data-density]` seçicileri de bunları kullanır. */
303
+ const CONTROL_H_PX = {
304
+ compact: { sm: 28, md: 32, lg: 36 },
305
+ default: { sm: 32, md: 36, lg: 40 },
306
+ comfortable: { sm: 36, md: 40, lg: 44 },
307
+ };
308
+ const CONTROL_H_KEYS = ['sm', 'md', 'lg'];
309
+ function densityLines(density) {
310
+ return CONTROL_H_KEYS.map((key) => [
311
+ `--control-h-${key}`,
312
+ `${CONTROL_H_PX[density][key] / 16}rem`,
313
+ ]);
314
+ }
315
+ /** `color.css`'teki 13 basamaklı nötr merdiven — tint karışımı bunların HEPSİNİ, moddan bağımsız (raw ramp) günceller. */
316
+ const NEUTRAL_STEPS = MF_NEUTRAL_RAMP_STEPS;
317
+ /** `ramps.<tone>` genel-amaçlı döngüsü için — `primary` kendi özel `accentRoleLines()` türetmesi yüzünden AYRI ele alınır (bkz. altta). */
318
+ const OTHER_RAMP_TONES = MF_RAMP_TONES.filter((tone) => tone !== 'primary');
319
+ /** Sabit, hue'ya dayalı iki tint ucu — accent'ten BAĞIMSIZ (bkz. plan Faz 2 "neutralTint"). `mixHex` küçük `t` ile çağrıldığı için nötrün kendi algısal parlaklığı korunur. */
320
+ const TINT_HEX = {
321
+ cool: '#3b82f6',
322
+ warm: '#f59e0b',
323
+ };
324
+ const TINT_AMOUNT = 0.06;
325
+ /**
326
+ * `--mf-fg-on-accent`/`--mf-accent-fill`/`--mf-accent-fill-hover` rol
327
+ * token'ları rampa basamağı gibi `--color-primary-*` ADI ALTINDA değil,
328
+ * kendi `--mf-*` isimleriyle yazılır (bkz. color.css rol katmanı) — bu
329
+ * yüzden `RAMP_STEPS` döngüsüne dahil DEĞİLLER, `accent: null` temizliğinin
330
+ * de bunları AYRICA silmesi gerekiyor.
331
+ */
332
+ const ACCENT_ROLE_PROPS = [
333
+ '--mf-fg-on-accent',
334
+ '--mf-accent-fill',
335
+ '--mf-accent-fill-hover',
336
+ ];
337
+ /**
338
+ * Bir accent ramp'inden üç rol token satırını üretir — `applyMfTheme` (runtime
339
+ * `setProperty`) ve `exportMfThemeCss` (statik string) İKİSİ de bunu kullanır,
340
+ * tek kaynak. Kitin KENDİ varsayılan rampasıyla AYNI desen (color.css): dolu
341
+ * blok LIGHT'ta 600, DARK'ta 400 (hover 700/500) — `light-dark()`'ın İKİ
342
+ * yarısı da AYRI hesaplanır, tek bir sabit değer DEĞİL. `fgOnAccent` her
343
+ * yarı için AYRI çağrılır çünkü 600 ile 400 basamağı genelde çok farklı
344
+ * parlaklıkta (600 koyu → genelde beyaz metin ister, 400 açık → genelde
345
+ * mürekkep ister) — tüketicinin verdiği HERHANGİ bir aksan renginde bu iki
346
+ * yarı FARKLI metin rengine ihtiyaç duyabilir (bkz. kitin varsayılan primary
347
+ * rampası: light `#062c22` + beyaz, dark `#10b981` + mürekkep — aynı asimetri).
348
+ * `ramp` EKSİK basamak taşıyorsa (ör. `ramps.primary` kısmi verildiyse,
349
+ * `resolvedPrimaryRamp`'in okunmuş/birleştirilmiş sonucu DEĞİLSE) `null`
350
+ * döner — çağıran bu durumda satırları YAZMAZ (bkz. `exportMfThemeCss`).
351
+ */
352
+ function accentRoleLines(ramp) {
353
+ const c400 = ramp['400'];
354
+ const c500 = ramp['500'];
355
+ const c600 = ramp['600'];
356
+ const c700 = ramp['700'];
357
+ if (!c400 || !c500 || !c600 || !c700)
358
+ return null;
359
+ const fgLight = fgOnAccent(c600);
360
+ const fgDark = fgOnAccent(c400);
361
+ return [
362
+ ['--mf-fg-on-accent', `light-dark(${fgLight}, ${fgDark})`],
363
+ ['--mf-accent-fill', `light-dark(${c600}, ${c400})`],
364
+ ['--mf-accent-fill-hover', `light-dark(${c700}, ${c500})`],
365
+ ];
366
+ }
367
+ function isBrowser() {
368
+ return typeof document !== 'undefined';
369
+ }
370
+ /**
371
+ * `config.ramps.primary` (kısmi olabilir) ile `el`'in O ANKİ hesaplanmış
372
+ * `--color-primary-*` değerlerini BİRLEŞTİRİR — `accentRoleLines()`'ın TAM
373
+ * bir ramp'e (400/500/600/700) ihtiyacı var, tüketici SADECE birkaç basamağı
374
+ * override etse bile (ör. sadece `600`) doğru `--mf-accent-fill` vb.
375
+ * türetilsin diye (bkz. `neutralTint`'in AYNI "önce oku, sonra karıştır"
376
+ * ilkesi, altta).
377
+ */
378
+ function resolvedPrimaryRamp(el, override) {
379
+ const computed = getComputedStyle(el);
380
+ const ramp = {};
381
+ for (const step of RAMP_STEPS) {
382
+ ramp[step] = override[step] ?? computed.getPropertyValue(`--color-primary-${step}`).trim();
383
+ }
384
+ return ramp;
385
+ }
386
+ /**
387
+ * `config.roles.light[key]`/`config.roles.dark[key]`'i TEK bir `light-dark()`
388
+ * CSS değerine çevirir — sadece BİRİ verilirse İKİ modda da AYNI değer
389
+ * kullanılır (bkz. `theme-json.ts`'teki `MfThemeJson.roles` doc'u — hangi
390
+ * modda olunduğunu PROBE etmek yerine BİLEREK basit/öngörülebilir).
391
+ */
392
+ function roleLightDarkValue(light, dark) {
393
+ const resolvedLight = light ?? dark;
394
+ const resolvedDark = dark ?? light;
395
+ if (resolvedLight === undefined || resolvedDark === undefined)
396
+ return null;
397
+ return `light-dark(${resolvedLight}, ${resolvedDark})`;
398
+ }
399
+ /**
400
+ * `MfThemeJson`'ı CSS custom property'leri olarak `target`'a (varsayılan
401
+ * `document.documentElement`) YAZAR — sayfa YENİDEN YÜKLENMEDEN, hiçbir
402
+ * bileşen dosyasına dokunmadan tüm kit anında yeni temaya geçer. Bunun
403
+ * mümkün olmasının nedeni: Tailwind v4'ün ürettiği utility'ler (`bg-primary-600`,
404
+ * `rounded-md`, ...) derleme zamanında SABİT bir değere değil, doğrudan
405
+ * `var(--color-primary-600)`/`var(--radius-md)` REFERANSINA derleniyor (bkz.
406
+ * `docs/tr/mimari.md` "Token mimarisi") — CSS cascade'de bu satırlar
407
+ * `@theme`'in KENDİ tanımından SONRA (inline stil her zaman kazanır)
408
+ * çalıştığı için token'lar üzerinden TÜM bileşenler güncellenir. SADECE
409
+ * `config`'te VERİLEN alanlar yazılır ("delta-only") — bu fonksiyon kitin
410
+ * TÜM varsayılan token setini bilmek/üretmek ZORUNDA DEĞİL, bu yüzden
411
+ * kitin CSS kaynağıyla asla senkron dışı kalamaz.
412
+ *
413
+ * - `accent` verilirse: `createAccentRamp()` ile 11 basamaklı `--color-primary-*`
414
+ * YENİDEN üretilir + `accentRoleLines()` (`--mf-fg-on-accent`/`-accent-fill`/
415
+ * `-accent-fill-hover`, `light-dark()` literal değerleri — bkz. altta).
416
+ * `:root`/`.dark` bunları zaten `var()` ile referans aldığı için (bkz.
417
+ * `color.css` rol katmanı) `--mf-accent`/`--mf-accent-strong`/`--mf-focus-ring`/
418
+ * `--mf-primary-tint(-fg)` AYRICA yazılmaz — otomatik takip eder.
419
+ * - `accent: null` verilirse: yukarıdaki override'ların HEPSİ temizlenir, kit
420
+ * `color.css`'teki varsayılan "Deep Emerald" rampasına döner.
421
+ * - `ramps.primary` verilirse (`accent`'i EZER — aynı çağrıda ikisi de
422
+ * verilirse `ramps.primary` kazanır): basamak başına TAM kontrol, kısmi
423
+ * olabilir (`resolvedPrimaryRamp` ile O ANKİ değerlerle birleştirilir).
424
+ * - `ramps.success`/`warning`/`danger`/`info`/`help`/`neutral` verilirse:
425
+ * verilen basamaklar DOĞRUDAN yazılır — bu tonların TÜM türetilmiş rol
426
+ * token'ları (`--mf-X`, `--mf-X-fill`, `--mf-X-tint` vb.) `color.css`'te
427
+ * zaten `var(--color-X-NNN)` ile bu rampaya bağlı, OTOMATİK güncellenir.
428
+ * - `roles.light`/`roles.dark` verilirse: rampaya BAĞLI OLMAYAN literal
429
+ * token'lar (bkz. `MfRoleToken`) `light-dark()` ile yazılır.
430
+ * - `fonts`/`radius` verilirse: moddan bağımsız düz değerler yazılır —
431
+ * `radius` `radiusScale`'İN ÜSTÜNE (aynı çağrıda ikisi de verilirse SONRA
432
+ * uygulanır) basamak-bazlı ince ayar sağlar.
433
+ * - `radiusScale` verilirse: `--radius-{xs,sm,md,lg,xl}` nested ölçeği
434
+ * KORUYARAK (bkz. `radius.css` doktrini) birlikte küçülür/büyür.
435
+ * - `density` verilirse: `--control-h-{sm,md,lg}` merdiveni yazılır — tüm
436
+ * kontroller (Button/Input/Select/...) aynı hizada birlikte büyür/küçülür.
437
+ * - `neutralTint` verilirse: 13 basamaklı `--color-neutral-*` rampası ÇOK
438
+ * düşük oranda (bkz. `color-math.ts` `mixHex`) soğuk/sıcak bir tona kayar
439
+ * — `ramps.neutral` AYNI ÇAĞRIDA verildiyse ONUN ÜSTÜNE uygulanır (bu
440
+ * fonksiyondaki sıra: ramps → neutralTint).
441
+ *
442
+ * SSR güvenli — `document` yoksa no-op.
443
+ */
444
+ function applyMfTheme(config, target) {
445
+ if (!isBrowser())
446
+ return;
447
+ const el = target ?? document.documentElement;
448
+ if (config.accent === null && !config.ramps?.primary) {
449
+ for (const step of RAMP_STEPS) {
450
+ el.style.removeProperty(`--color-primary-${step}`);
451
+ }
452
+ for (const prop of ACCENT_ROLE_PROPS) {
453
+ el.style.removeProperty(prop);
454
+ }
455
+ }
456
+ else if (config.ramps?.primary) {
457
+ const ramp = resolvedPrimaryRamp(el, config.ramps.primary);
458
+ for (const step of RAMP_STEPS) {
459
+ el.style.setProperty(`--color-primary-${step}`, ramp[step]);
460
+ }
461
+ const lines = accentRoleLines(ramp);
462
+ if (lines) {
463
+ for (const [prop, value] of lines)
464
+ el.style.setProperty(prop, value);
465
+ }
466
+ }
467
+ else if (config.accent) {
468
+ const ramp = createAccentRamp(config.accent);
469
+ for (const step of RAMP_STEPS) {
470
+ el.style.setProperty(`--color-primary-${step}`, ramp[step]);
471
+ }
472
+ const lines = accentRoleLines(ramp);
473
+ if (lines) {
474
+ for (const [prop, value] of lines)
475
+ el.style.setProperty(prop, value);
476
+ }
477
+ }
478
+ for (const tone of OTHER_RAMP_TONES) {
479
+ const override = config.ramps?.[tone];
480
+ if (!override)
481
+ continue;
482
+ for (const step of RAMP_STEPS) {
483
+ const hex = override[step];
484
+ if (hex)
485
+ el.style.setProperty(`--color-${tone}-${step}`, hex);
486
+ }
487
+ }
488
+ if (config.ramps?.neutral) {
489
+ for (const step of NEUTRAL_STEPS) {
490
+ const hex = config.ramps.neutral[step];
491
+ if (hex)
492
+ el.style.setProperty(`--color-neutral-${step}`, hex);
493
+ }
494
+ }
495
+ if (config.roles) {
496
+ for (const key of MF_ROLE_TOKENS) {
497
+ const value = roleLightDarkValue(config.roles.light?.[key], config.roles.dark?.[key]);
498
+ if (value)
499
+ el.style.setProperty(`--mf-${key}`, value);
500
+ }
501
+ }
502
+ if (config.fonts?.sans)
503
+ el.style.setProperty('--font-sans', config.fonts.sans);
504
+ if (config.fonts?.display)
505
+ el.style.setProperty('--font-display', config.fonts.display);
506
+ if (config.fonts?.mono)
507
+ el.style.setProperty('--font-mono', config.fonts.mono);
508
+ if (config.radiusScale) {
509
+ const px = RADIUS_PX[config.radiusScale];
510
+ for (const [key, value] of Object.entries(px)) {
511
+ el.style.setProperty(`--radius-${key}`, `${value}px`);
512
+ }
513
+ }
514
+ if (config.radius) {
515
+ for (const [key, value] of Object.entries(config.radius)) {
516
+ if (value)
517
+ el.style.setProperty(`--radius-${key}`, value);
518
+ }
519
+ }
520
+ if (config.density) {
521
+ for (const [prop, value] of densityLines(config.density))
522
+ el.style.setProperty(prop, value);
523
+ }
524
+ if (config.neutralTint !== undefined) {
525
+ // ÖNCE inline override'ı TEMİZLE (`removeProperty`), SONRA (tint !=
526
+ // 'neutral' ise) `getComputedStyle`'ı yeniden oku — aksi halde art arda
527
+ // iki `applyMfTheme({ neutralTint: 'cool' })` çağrısı (ör. tema
528
+ // özelleştiricide kullanıcı aynı seçimi tekrar tetiklerse) İKİNCİ
529
+ // çağrıda ZATEN tint'lenmiş değerin üstüne BİR TINT DAHA karıştırırdı
530
+ // (`getComputedStyle` kendi az önce yazdığı inline değeri geri okur) —
531
+ // gerçek bulgu, kümülatif/katlanan bir renk kayması üretiyordu.
532
+ for (const step of NEUTRAL_STEPS) {
533
+ el.style.removeProperty(`--color-neutral-${step}`);
534
+ }
535
+ if (config.neutralTint !== 'neutral') {
536
+ const tint = TINT_HEX[config.neutralTint];
537
+ const computed = getComputedStyle(el);
538
+ for (const step of NEUTRAL_STEPS) {
539
+ const current = computed.getPropertyValue(`--color-neutral-${step}`).trim();
540
+ if (!current)
541
+ continue;
542
+ el.style.setProperty(`--color-neutral-${step}`, mixHex(current, tint, TINT_AMOUNT));
543
+ }
544
+ }
545
+ }
546
+ }
547
+ /**
548
+ * `applyMfTheme()`'in YAZABİLECEĞİ HER inline override'ı temizler — bir
549
+ * tema özelleştiricideki "Varsayılana sıfırla" düğmesi için `accent: null`'ın
550
+ * genişletilmiş hali (o SADECE accent'i temizlerdi). Kit `color.css`'teki
551
+ * varsayılanlara TAMAMEN geri döner.
552
+ */
553
+ function resetMfTheme(target) {
554
+ if (!isBrowser())
555
+ return;
556
+ const el = target ?? document.documentElement;
557
+ for (const step of RAMP_STEPS)
558
+ el.style.removeProperty(`--color-primary-${step}`);
559
+ for (const prop of ACCENT_ROLE_PROPS)
560
+ el.style.removeProperty(prop);
561
+ for (const tone of OTHER_RAMP_TONES) {
562
+ for (const step of RAMP_STEPS)
563
+ el.style.removeProperty(`--color-${tone}-${step}`);
564
+ }
565
+ for (const step of NEUTRAL_STEPS)
566
+ el.style.removeProperty(`--color-neutral-${step}`);
567
+ for (const key of MF_ROLE_TOKENS)
568
+ el.style.removeProperty(`--mf-${key}`);
569
+ el.style.removeProperty('--font-sans');
570
+ el.style.removeProperty('--font-display');
571
+ el.style.removeProperty('--font-mono');
572
+ for (const key of ['xs', 'sm', 'md', 'lg', 'xl'])
573
+ el.style.removeProperty(`--radius-${key}`);
574
+ for (const key of CONTROL_H_KEYS)
575
+ el.style.removeProperty(`--control-h-${key}`);
576
+ }
577
+ /**
578
+ * `applyMfTheme` ile AYNI hesaplamayı yapar ama runtime'da uygulamak yerine
579
+ * yapıştırılabilir bir `{ selector } { ... }` bloğu (varsayılan `:root`)
580
+ * döner — tüketici build-time'da kendi `styles.css`'ine ekleyebilir (docs
581
+ * sitesindeki tema özelleştiricinin "CSS'i kopyala" düğmesi bunu kullanır).
582
+ * `roles.*` İÇİN de (runtime'daki AYNI `light-dark()` deseni) tek blokla
583
+ * hem açık hem koyu mod kapsanır, AYRI bir `.dark` bloğu GEREKMEZ. `ramps.
584
+ * primary` KISMİ verilip 400/500/600/700'ün TAMAMI yoksa `--mf-accent-fill`
585
+ * vb. satırlar (canlı DOM'dan O ANKİ değeri okuyamadığı için) ATLANIR — bu
586
+ * durumda `applyMfTheme`'i (runtime, `resolvedPrimaryRamp` ile tamamlar)
587
+ * tercih edin. `neutralTint` BURADA desteklenmez — mevcut nötr değerleri
588
+ * okumak için canlı bir DOM/`getComputedStyle` gerekir, statik bir string
589
+ * üretici bunu bilemez (tüketici tint'i kendi `applyMfTheme()` çağrısıyla
590
+ * runtime'da uygulamalı).
591
+ */
592
+ function exportMfThemeCss(config, options) {
593
+ const selector = options?.selector ?? ':root';
594
+ const lines = [`${selector} {`];
595
+ if (config.ramps?.primary) {
596
+ const ramp = config.ramps.primary;
597
+ for (const step of RAMP_STEPS) {
598
+ const hex = ramp[step];
599
+ if (hex)
600
+ lines.push(` --color-primary-${step}: ${hex};`);
601
+ }
602
+ const roleLines = accentRoleLines(ramp);
603
+ if (roleLines) {
604
+ for (const [prop, value] of roleLines)
605
+ lines.push(` ${prop}: ${value};`);
606
+ }
607
+ }
608
+ else if (config.accent) {
609
+ const ramp = createAccentRamp(config.accent);
610
+ for (const step of RAMP_STEPS) {
611
+ lines.push(` --color-primary-${step}: ${ramp[step]};`);
612
+ }
613
+ const roleLines = accentRoleLines(ramp);
614
+ if (roleLines) {
615
+ for (const [prop, value] of roleLines)
616
+ lines.push(` ${prop}: ${value};`);
617
+ }
618
+ }
619
+ for (const tone of OTHER_RAMP_TONES) {
620
+ const override = config.ramps?.[tone];
621
+ if (!override)
622
+ continue;
623
+ for (const step of RAMP_STEPS) {
624
+ const hex = override[step];
625
+ if (hex)
626
+ lines.push(` --color-${tone}-${step}: ${hex};`);
627
+ }
628
+ }
629
+ if (config.ramps?.neutral) {
630
+ for (const step of NEUTRAL_STEPS) {
631
+ const hex = config.ramps.neutral[step];
632
+ if (hex)
633
+ lines.push(` --color-neutral-${step}: ${hex};`);
634
+ }
635
+ }
636
+ if (config.roles) {
637
+ for (const key of MF_ROLE_TOKENS) {
638
+ const value = roleLightDarkValue(config.roles.light?.[key], config.roles.dark?.[key]);
639
+ if (value)
640
+ lines.push(` --mf-${key}: ${value};`);
641
+ }
642
+ }
643
+ if (config.fonts?.sans)
644
+ lines.push(` --font-sans: ${config.fonts.sans};`);
645
+ if (config.fonts?.display)
646
+ lines.push(` --font-display: ${config.fonts.display};`);
647
+ if (config.fonts?.mono)
648
+ lines.push(` --font-mono: ${config.fonts.mono};`);
649
+ if (config.radiusScale) {
650
+ const px = RADIUS_PX[config.radiusScale];
651
+ for (const [key, value] of Object.entries(px)) {
652
+ lines.push(` --radius-${key}: ${value}px;`);
653
+ }
654
+ }
655
+ if (config.radius) {
656
+ for (const [key, value] of Object.entries(config.radius)) {
657
+ if (value)
658
+ lines.push(` --radius-${key}: ${value};`);
659
+ }
660
+ }
661
+ if (config.density) {
662
+ for (const [prop, value] of densityLines(config.density))
663
+ lines.push(` ${prop}: ${value};`);
664
+ }
665
+ lines.push('}');
666
+ return lines.join('\n');
667
+ }
668
+
669
+ /** `color.css`'in `--mf-surface-inverse`/`-fill-fg` literallerinde tekrarlanan "mürekkep" tonu — `X-fill-fg` HER ZAMAN bunun ya da beyazın arasından seçilir (bkz. rol katmanı). */
670
+ const INK = '#04110d';
671
+ const AA_NORMAL_TEXT = 4.5;
672
+ function checkHex(path, value, issues) {
673
+ if (value === undefined)
674
+ return;
675
+ if (!isValidHex(value)) {
676
+ issues.push({
677
+ path,
678
+ message: `"${value}" geçerli bir #rgb/#rrggbb hex değeri değil.`,
679
+ severity: 'error',
680
+ });
681
+ }
682
+ }
683
+ /**
684
+ * `MfThemeJson`'ı iki açıdan denetler:
685
+ *
686
+ * 1. Her hex alanının GEÇERLİ bir `#rgb`/`#rrggbb` olup olmadığı.
687
+ * 2. `ramps.<tone>['600']` (kitin "dolu blok" LIGHT çapası, bkz. `color.css`
688
+ * rol katmanı doktrini — `X-fill-fg` her zaman `#ffffff` ya da mürekkep
689
+ * `#04110d`'den seçilir) verilen bir ton için NE beyaz NE mürekkep
690
+ * üstünde WCAG AA (4.5:1) sağlayan okunaklı bir metin rengi bulunup
691
+ * bulunamadığı — ikisi de başarısızsa bu basamak "çok orta tonlu", dolu
692
+ * blok üstünde okunaklı metin GARANTİSİ yok demektir. `accent` (`ramps.
693
+ * primary`/`createAccentRamp` + `fgOnAccent`) BU KONTROLE İHTİYAÇ
694
+ * DUYMAZ — `pickForeground` HER ZAMAN iki adaydan daha iyisini seçtiği
695
+ * için kontrast garantisi İNŞA GEREĞİ var (bkz. `apply.ts`
696
+ * `accentRoleLines`).
697
+ *
698
+ * Şema/yapı hatalarını (bilinmeyen alan vb.) DENETLEMEZ — TypeScript
699
+ * derleme zamanında zaten yakalar; `MfThemeJson` harici bir kaynaktan (ör.
700
+ * `JSON.parse`, bir tema dosyası yükleme formu) geliyorsa bu fonksiyon
701
+ * SADECE değer-düzeyi sorunları (geçersiz hex, düşük kontrast) yakalar.
702
+ */
703
+ function validateMfTheme(theme) {
704
+ const issues = [];
705
+ checkHex('accent', theme.accent ?? undefined, issues);
706
+ for (const tone of MF_RAMP_TONES) {
707
+ const ramp = theme.ramps?.[tone];
708
+ if (!ramp)
709
+ continue;
710
+ for (const step of RAMP_STEPS) {
711
+ checkHex(`ramps.${tone}.${step}`, ramp[step], issues);
712
+ }
713
+ if (tone === 'primary')
714
+ continue;
715
+ const fill = ramp['600'];
716
+ if (!fill || !isValidHex(fill))
717
+ continue;
718
+ const onWhite = contrastRatio(fill, '#ffffff');
719
+ const onInk = contrastRatio(fill, INK);
720
+ if (onWhite < AA_NORMAL_TEXT && onInk < AA_NORMAL_TEXT) {
721
+ issues.push({
722
+ path: `ramps.${tone}.600`,
723
+ message: `"${fill}" dolu blok üstünde ne beyaz (${onWhite.toFixed(2)}:1) ne mürekkep (${onInk.toFixed(2)}:1) WCAG AA (4.5:1) sağlıyor — daha koyu/açık bir basamak seçin.`,
724
+ severity: 'warning',
725
+ });
726
+ }
727
+ }
728
+ if (theme.ramps?.neutral) {
729
+ for (const [step, value] of Object.entries(theme.ramps.neutral)) {
730
+ checkHex(`ramps.neutral.${step}`, value, issues);
731
+ }
732
+ }
733
+ if (theme.roles) {
734
+ for (const [mode, values] of Object.entries(theme.roles)) {
735
+ if (!values)
736
+ continue;
737
+ for (const [key, value] of Object.entries(values)) {
738
+ checkHex(`roles.${mode}.${key}`, value, issues);
739
+ }
740
+ }
741
+ }
742
+ return issues;
743
+ }
744
+
745
+ /**
746
+ * `MfThemeJson`'ın JSON Schema (2020-12) tanımı — IDE otomatik tamamlama
747
+ * (`"$schema"` alanı) ve harici doğrulayıcılar (ajv vb.) için. Düz bir JS
748
+ * nesnesi olarak dışa açılıyor — kitin sıfır çalışma zamanı bağımlılığı
749
+ * ilkesi gereği (bkz. `projects/ui-kit/README.md` "Bağımlılık politikası")
750
+ * hiçbir şema doğrulama KÜTÜPHANESİ KULLANILMIYOR/GEREKTİRİLMİYOR; asıl
751
+ * DEĞER-düzeyi doğrulama (geçerli hex, WCAG kontrastı) `validateMfTheme()`
752
+ * (bkz. `validate.ts`) tarafında, bu şema SADECE YAPISAL/tip doğrulaması
753
+ * içindir — `@mesgflow/email-core`'un `EMAIL_DOCUMENT_JSON_SCHEMA`'sıyla
754
+ * AYNI desen.
755
+ */
756
+ const HEX_PATTERN = '^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$';
757
+ const HEX = { type: 'string', pattern: HEX_PATTERN };
758
+ const RAMP_11 = {
759
+ type: 'object',
760
+ additionalProperties: false,
761
+ properties: {
762
+ '50': HEX,
763
+ '100': HEX,
764
+ '200': HEX,
765
+ '300': HEX,
766
+ '400': HEX,
767
+ '500': HEX,
768
+ '600': HEX,
769
+ '700': HEX,
770
+ '800': HEX,
771
+ '900': HEX,
772
+ '950': HEX,
773
+ },
774
+ };
775
+ const RAMP_13 = {
776
+ type: 'object',
777
+ additionalProperties: false,
778
+ properties: {
779
+ ...RAMP_11.properties,
780
+ '850': HEX,
781
+ '975': HEX,
782
+ },
783
+ };
784
+ const ROLE_TOKEN_NAMES = [
785
+ 'surface',
786
+ 'chrome',
787
+ 'overlay',
788
+ 'surface-inverse',
789
+ 'glass',
790
+ 'fg',
791
+ 'fg-muted',
792
+ 'fg-on-accent',
793
+ 'fg-inverse',
794
+ 'hairline',
795
+ 'neutral-fill',
796
+ 'neutral-fill-hover',
797
+ 'neutral-fill-fg',
798
+ 'success-fill-fg',
799
+ 'warning-fill-fg',
800
+ 'danger-fill-fg',
801
+ 'info-fill-fg',
802
+ 'help-fill-fg',
803
+ 'chart-1',
804
+ 'chart-2',
805
+ 'chart-3',
806
+ 'chart-4',
807
+ 'chart-5',
808
+ 'chart-6',
809
+ 'chart-7',
810
+ 'chart-8',
811
+ 'chart-other',
812
+ ];
813
+ const ROLE_MAP = {
814
+ type: 'object',
815
+ additionalProperties: false,
816
+ properties: Object.fromEntries(ROLE_TOKEN_NAMES.map((key) => [key, { type: 'string' }])),
817
+ };
818
+ const MF_THEME_JSON_SCHEMA = {
819
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
820
+ $id: 'https://hurdenizyener.github.io/mesgflow-ui-kit/schema/theme.json',
821
+ title: 'MesgFlow ui theme',
822
+ description: 'Tam beyaz etiket tema tanımı — applyMfTheme()/exportMfThemeCss()e verilir. Her alan opsiyonel.',
823
+ type: 'object',
824
+ additionalProperties: false,
825
+ properties: {
826
+ $schema: { type: 'string' },
827
+ name: { type: 'string' },
828
+ accent: { oneOf: [HEX, { type: 'null' }] },
829
+ radiusScale: { enum: ['sharp', 'default', 'soft'] },
830
+ density: { enum: ['compact', 'default', 'comfortable'] },
831
+ neutralTint: { enum: ['cool', 'neutral', 'warm'] },
832
+ ramps: {
833
+ type: 'object',
834
+ additionalProperties: false,
835
+ properties: {
836
+ primary: RAMP_11,
837
+ success: RAMP_11,
838
+ warning: RAMP_11,
839
+ danger: RAMP_11,
840
+ info: RAMP_11,
841
+ help: RAMP_11,
842
+ neutral: RAMP_13,
843
+ },
844
+ },
845
+ roles: {
846
+ type: 'object',
847
+ additionalProperties: false,
848
+ properties: {
849
+ light: ROLE_MAP,
850
+ dark: ROLE_MAP,
851
+ },
852
+ },
853
+ fonts: {
854
+ type: 'object',
855
+ additionalProperties: false,
856
+ properties: {
857
+ sans: { type: 'string' },
858
+ display: { type: 'string' },
859
+ mono: { type: 'string' },
860
+ },
861
+ },
862
+ radius: {
863
+ type: 'object',
864
+ additionalProperties: false,
865
+ properties: {
866
+ xs: { type: 'string' },
867
+ sm: { type: 'string' },
868
+ md: { type: 'string' },
869
+ lg: { type: 'string' },
870
+ xl: { type: 'string' },
871
+ },
872
+ },
873
+ },
874
+ };
875
+
876
+ /**
877
+ * Tema değişikliğini (`.dark` toggle'ı, `applyMfTheme()` çağrısı vb.) View
878
+ * Transitions API ile yumuşak bir çapraz geçişe sarar — sayfa her token'ı
879
+ * tek tek `transition` etmek yerine eski/yeni ekran görüntüsü arasında TEK
880
+ * bir kompozit geçiş yapar (yüzlerce `transition-colors`'ın aynı anda ve
881
+ * farklı sürelerle kayması yerine).
882
+ *
883
+ * `update` HER ZAMAN çağrılır — sadece NASIL çağrıldığı değişir. Geçiş
884
+ * atlanıp `update` doğrudan (senkron) çalıştırılır:
885
+ * - SSR / `document` yok,
886
+ * - tarayıcı `startViewTransition` desteklemiyor,
887
+ * - kullanıcı `prefers-reduced-motion: reduce` istiyor.
888
+ *
889
+ * ```ts
890
+ * transitionMfTheme(() => document.documentElement.classList.toggle('dark'));
891
+ * ```
892
+ */
893
+ function transitionMfTheme(update) {
894
+ if (typeof document === 'undefined') {
895
+ update();
896
+ return;
897
+ }
898
+ const reducedMotion = typeof matchMedia !== 'undefined' && matchMedia('(prefers-reduced-motion: reduce)').matches;
899
+ // DOM lib tipi bunu zorunlu sayar, ama Firefox < 144 ve jsdom'da YOK.
900
+ if (typeof document.startViewTransition !== 'function' || reducedMotion) {
901
+ update();
902
+ return;
903
+ }
904
+ document.startViewTransition(update);
905
+ }
906
+
907
+ const MF_COLOR_MODE_CONFIG = new InjectionToken('mesgflow.color-mode-config', { factory: () => ({ storageKey: 'mf-color-mode', defaultMode: 'system' }) });
908
+ /** `providers: [provideMfColorMode({ storageKey: 'app-theme' })]` */
909
+ function provideMfColorMode(config) {
910
+ return {
911
+ provide: MF_COLOR_MODE_CONFIG,
912
+ useValue: { storageKey: 'mf-color-mode', defaultMode: 'system', ...config },
913
+ };
914
+ }
915
+ const DARK_QUERY = '(prefers-color-scheme: dark)';
916
+ /**
917
+ * Açık / koyu / sistem modu — kitin TEK kaynağı (`<mf-theme-switcher>` bunu kullanır; kendi
918
+ * anahtarınızla da doğrudan enjekte edebilirsiniz).
919
+ *
920
+ * - `mode`: kullanıcının seçimi (kalıcı, `localStorage`); `resolved`: gerçekte uygulanan
921
+ * `light`/`dark` — `system`'de işletim sistemi tercihini CANLI izler.
922
+ * - Uygulama: kökte `.dark` sınıfı + `color-scheme` (yerel kaydırma çubukları ve form
923
+ * kontrolleri de temaya uyar); değişim `transitionMfTheme` ile yumuşak (azaltılmış harekette
924
+ * anında). Sınıf zaten doğruysa (ilk boyamada `colorModeInitScript` uyguladı) dokunulmaz.
925
+ * - Depolama/`matchMedia` yoksa (gizli pencere, SSR, test) sessizce varsayılana düşer.
926
+ */
927
+ class MfColorModeService {
928
+ config = inject(MF_COLOR_MODE_CONFIG);
929
+ document = inject(DOCUMENT);
930
+ media = this.document.defaultView?.matchMedia?.(DARK_QUERY) ?? null;
931
+ systemDark = signal(this.media?.matches ?? false, /* @ts-ignore */
932
+ ...(ngDevMode ? [{ debugName: "systemDark" }] : /* istanbul ignore next */ []));
933
+ mode = signal(this.readStored(), /* @ts-ignore */
934
+ ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
935
+ resolved = computed(() => {
936
+ const mode = this.mode();
937
+ return mode === 'system' ? (this.systemDark() ? 'dark' : 'light') : mode;
938
+ }, /* @ts-ignore */
939
+ ...(ngDevMode ? [{ debugName: "resolved" }] : /* istanbul ignore next */ []));
940
+ constructor() {
941
+ const onChange = (event) => this.systemDark.set(event.matches);
942
+ this.media?.addEventListener?.('change', onChange);
943
+ inject(DestroyRef).onDestroy(() => this.media?.removeEventListener?.('change', onChange));
944
+ effect(() => {
945
+ const mode = this.mode();
946
+ try {
947
+ this.document.defaultView?.localStorage.setItem(this.config.storageKey, mode);
948
+ }
949
+ catch {
950
+ // Gizli pencere / engelli depolama — yalnızca bu oturumda.
951
+ }
952
+ });
953
+ effect(() => {
954
+ const dark = this.resolved() === 'dark';
955
+ const root = this.document.documentElement;
956
+ root.style.colorScheme = dark ? 'dark' : 'light';
957
+ if (root.classList.contains('dark') === dark)
958
+ return;
959
+ transitionMfTheme(() => root.classList.toggle('dark', dark));
960
+ });
961
+ }
962
+ setMode(mode) {
963
+ this.mode.set(mode);
964
+ }
965
+ /** light → dark → system → light (tek düğmeli değiştirici için). */
966
+ cycle() {
967
+ const order = ['light', 'dark', 'system'];
968
+ this.mode.set(order[(order.indexOf(this.mode()) + 1) % order.length]);
969
+ }
970
+ readStored() {
971
+ try {
972
+ const stored = this.document.defaultView?.localStorage.getItem(this.config.storageKey);
973
+ if (stored === 'light' || stored === 'dark' || stored === 'system')
974
+ return stored;
975
+ }
976
+ catch {
977
+ // Depolama erişilemez.
978
+ }
979
+ return this.config.defaultMode;
980
+ }
981
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MfColorModeService, deps: [], target: i0.ɵɵFactoryTarget.Service });
982
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.6", ngImport: i0, type: MfColorModeService });
983
+ }
984
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MfColorModeService, decorators: [{
985
+ type: Service
986
+ }], ctorParameters: () => [] });
987
+ /**
988
+ * `index.html` `<head>`'ine satır içi konacak betik — Angular yüklenmeden ÖNCE `.dark`'ı uygular,
989
+ * koyu modda beyaz flaşı önler. Anahtar ve varsayılan servisle AYNI olmalı.
990
+ *
991
+ * ```html
992
+ * <script>/* colorModeInitScript('app-theme') çıktısı *\/</script>
993
+ * ```
994
+ */
995
+ function colorModeInitScript(storageKey = 'mf-color-mode', defaultMode = 'system') {
996
+ return (`(function(){try{var m=localStorage.getItem(${JSON.stringify(storageKey)})||${JSON.stringify(defaultMode)};` +
997
+ `var d=m==='dark'||(m==='system'&&matchMedia('${DARK_QUERY}').matches);` +
998
+ `var r=document.documentElement;r.classList.toggle('dark',d);r.style.colorScheme=d?'dark':'light';}catch(e){}})();`);
999
+ }
1000
+
1001
+ /*
1002
+ * `@mesgflow/ui-kit/theming` — ikincil giriş noktası. `i18n/tr` gibi BİLEREK
1003
+ * birincil barrel'dan (`projects/ui-kit/index.ts`) re-export EDİLMEZ: tema
1004
+ * özelleştirme her tüketicinin ihtiyacı değil, opt-in kalmalı (bkz.
1005
+ * `docs/tr/mimari.md` "i18n mimarisi"nin aynı gerekçesi).
1006
+ *
1007
+ * `color-math.ts` BİLEREK burada export EDİLMEZ — iç uygulama detayı
1008
+ * (hex<->OKLCH), public API `ramp.ts`/`apply.ts`'in üstünde.
1009
+ */
1010
+
1011
+ /**
1012
+ * Generated bundle index. Do not edit.
1013
+ */
1014
+
1015
+ export { MF_COLOR_MODE_CONFIG, MF_NEUTRAL_RAMP_STEPS, MF_RAMP_TONES, MF_ROLE_TOKENS, MF_THEME_JSON_SCHEMA, MfColorModeService, RAMP_STEPS, accentContrastWithWhite, applyMfTheme, colorModeInitScript, createAccentRamp, exportMfThemeCss, fgOnAccent, provideMfColorMode, resetMfTheme, transitionMfTheme, validateMfTheme };
1016
+ //# sourceMappingURL=mesgflow-ui-kit-theming.mjs.map