@jjlmoya/utils-chrono 1.2.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 (277) hide show
  1. package/package.json +65 -0
  2. package/scripts/postinstall.mjs +27 -0
  3. package/src/category/ChronoCategorySEO.astro +8 -0
  4. package/src/category/i18n/de.ts +23 -0
  5. package/src/category/i18n/en.ts +23 -0
  6. package/src/category/i18n/es.ts +23 -0
  7. package/src/category/i18n/fr.ts +23 -0
  8. package/src/category/i18n/id.ts +23 -0
  9. package/src/category/i18n/it.ts +23 -0
  10. package/src/category/i18n/ja.ts +23 -0
  11. package/src/category/i18n/ko.ts +23 -0
  12. package/src/category/i18n/nl.ts +23 -0
  13. package/src/category/i18n/pl.ts +23 -0
  14. package/src/category/i18n/pt.ts +23 -0
  15. package/src/category/i18n/ru.ts +23 -0
  16. package/src/category/i18n/sv.ts +23 -0
  17. package/src/category/i18n/tr.ts +23 -0
  18. package/src/category/i18n/zh.ts +23 -0
  19. package/src/category/index.ts +42 -0
  20. package/src/components/PreviewNavSidebar.astro +116 -0
  21. package/src/components/PreviewToolbar.astro +143 -0
  22. package/src/data.ts +11 -0
  23. package/src/entries.ts +32 -0
  24. package/src/env.d.ts +5 -0
  25. package/src/index.ts +28 -0
  26. package/src/layouts/PreviewLayout.astro +117 -0
  27. package/src/pages/[locale]/[slug].astro +161 -0
  28. package/src/pages/[locale].astro +251 -0
  29. package/src/pages/index.astro +4 -0
  30. package/src/tests/faq_count.test.ts +19 -0
  31. package/src/tests/i18n_coverage.test.ts +36 -0
  32. package/src/tests/locale_completeness.test.ts +29 -0
  33. package/src/tests/mocks/astro_mock.js +2 -0
  34. package/src/tests/no_h1_in_components.test.ts +48 -0
  35. package/src/tests/schemas_fulfillment.test.ts +23 -0
  36. package/src/tests/seo_length.test.ts +22 -0
  37. package/src/tests/shared-test-helpers.ts +56 -0
  38. package/src/tests/slug_language_code_format.test.ts +23 -0
  39. package/src/tests/slug_uniqueness.test.ts +81 -0
  40. package/src/tests/title_quality.test.ts +55 -0
  41. package/src/tests/tool_exports.test.ts +34 -0
  42. package/src/tests/tool_validation.test.ts +18 -0
  43. package/src/tool/beat-rate-converter/beat-rate-converter.css +301 -0
  44. package/src/tool/beat-rate-converter/bibliography.astro +16 -0
  45. package/src/tool/beat-rate-converter/bibliography.ts +12 -0
  46. package/src/tool/beat-rate-converter/client.ts +46 -0
  47. package/src/tool/beat-rate-converter/component.astro +15 -0
  48. package/src/tool/beat-rate-converter/components/ConverterPanel.astro +87 -0
  49. package/src/tool/beat-rate-converter/entry.ts +42 -0
  50. package/src/tool/beat-rate-converter/i18n/de.ts +138 -0
  51. package/src/tool/beat-rate-converter/i18n/en.ts +138 -0
  52. package/src/tool/beat-rate-converter/i18n/es.ts +138 -0
  53. package/src/tool/beat-rate-converter/i18n/fr.ts +138 -0
  54. package/src/tool/beat-rate-converter/i18n/id.ts +138 -0
  55. package/src/tool/beat-rate-converter/i18n/it.ts +138 -0
  56. package/src/tool/beat-rate-converter/i18n/ja.ts +138 -0
  57. package/src/tool/beat-rate-converter/i18n/ko.ts +138 -0
  58. package/src/tool/beat-rate-converter/i18n/nl.ts +138 -0
  59. package/src/tool/beat-rate-converter/i18n/pl.ts +138 -0
  60. package/src/tool/beat-rate-converter/i18n/pt.ts +138 -0
  61. package/src/tool/beat-rate-converter/i18n/ru.ts +138 -0
  62. package/src/tool/beat-rate-converter/i18n/sv.ts +138 -0
  63. package/src/tool/beat-rate-converter/i18n/tr.ts +138 -0
  64. package/src/tool/beat-rate-converter/i18n/zh.ts +138 -0
  65. package/src/tool/beat-rate-converter/index.ts +11 -0
  66. package/src/tool/beat-rate-converter/seo.astro +16 -0
  67. package/src/tool/crown-reference-guide/bibliography.astro +16 -0
  68. package/src/tool/crown-reference-guide/bibliography.ts +20 -0
  69. package/src/tool/crown-reference-guide/client.ts +193 -0
  70. package/src/tool/crown-reference-guide/component.astro +69 -0
  71. package/src/tool/crown-reference-guide/components/CrownView.astro +40 -0
  72. package/src/tool/crown-reference-guide/components/MovementSelector.astro +26 -0
  73. package/src/tool/crown-reference-guide/components/PositionPanel.astro +59 -0
  74. package/src/tool/crown-reference-guide/crown-reference-guide.css +383 -0
  75. package/src/tool/crown-reference-guide/entry.ts +60 -0
  76. package/src/tool/crown-reference-guide/i18n/de.ts +207 -0
  77. package/src/tool/crown-reference-guide/i18n/en.ts +207 -0
  78. package/src/tool/crown-reference-guide/i18n/es.ts +207 -0
  79. package/src/tool/crown-reference-guide/i18n/fr.ts +207 -0
  80. package/src/tool/crown-reference-guide/i18n/id.ts +207 -0
  81. package/src/tool/crown-reference-guide/i18n/it.ts +207 -0
  82. package/src/tool/crown-reference-guide/i18n/ja.ts +207 -0
  83. package/src/tool/crown-reference-guide/i18n/ko.ts +207 -0
  84. package/src/tool/crown-reference-guide/i18n/nl.ts +207 -0
  85. package/src/tool/crown-reference-guide/i18n/pl.ts +207 -0
  86. package/src/tool/crown-reference-guide/i18n/pt.ts +207 -0
  87. package/src/tool/crown-reference-guide/i18n/ru.ts +207 -0
  88. package/src/tool/crown-reference-guide/i18n/sv.ts +207 -0
  89. package/src/tool/crown-reference-guide/i18n/tr.ts +207 -0
  90. package/src/tool/crown-reference-guide/i18n/zh.ts +207 -0
  91. package/src/tool/crown-reference-guide/index.ts +11 -0
  92. package/src/tool/crown-reference-guide/seo.astro +16 -0
  93. package/src/tool/crown-reference-guide/utils.ts +0 -0
  94. package/src/tool/demagnetizing-timer/bibliography.astro +16 -0
  95. package/src/tool/demagnetizing-timer/bibliography.ts +12 -0
  96. package/src/tool/demagnetizing-timer/client.ts +221 -0
  97. package/src/tool/demagnetizing-timer/component.astro +15 -0
  98. package/src/tool/demagnetizing-timer/components/TimerPanel.astro +92 -0
  99. package/src/tool/demagnetizing-timer/demagnetizing-timer.css +389 -0
  100. package/src/tool/demagnetizing-timer/entry.ts +50 -0
  101. package/src/tool/demagnetizing-timer/helpers/field.ts +134 -0
  102. package/src/tool/demagnetizing-timer/i18n/de.ts +143 -0
  103. package/src/tool/demagnetizing-timer/i18n/en.ts +143 -0
  104. package/src/tool/demagnetizing-timer/i18n/es.ts +143 -0
  105. package/src/tool/demagnetizing-timer/i18n/fr.ts +143 -0
  106. package/src/tool/demagnetizing-timer/i18n/id.ts +143 -0
  107. package/src/tool/demagnetizing-timer/i18n/it.ts +143 -0
  108. package/src/tool/demagnetizing-timer/i18n/ja.ts +143 -0
  109. package/src/tool/demagnetizing-timer/i18n/ko.ts +143 -0
  110. package/src/tool/demagnetizing-timer/i18n/nl.ts +143 -0
  111. package/src/tool/demagnetizing-timer/i18n/pl.ts +143 -0
  112. package/src/tool/demagnetizing-timer/i18n/pt.ts +143 -0
  113. package/src/tool/demagnetizing-timer/i18n/ru.ts +143 -0
  114. package/src/tool/demagnetizing-timer/i18n/sv.ts +143 -0
  115. package/src/tool/demagnetizing-timer/i18n/tr.ts +143 -0
  116. package/src/tool/demagnetizing-timer/i18n/zh.ts +143 -0
  117. package/src/tool/demagnetizing-timer/index.ts +11 -0
  118. package/src/tool/demagnetizing-timer/seo.astro +16 -0
  119. package/src/tool/demagnetizing-timer/utils.ts +0 -0
  120. package/src/tool/power-reserve-estimator/bibliography.astro +16 -0
  121. package/src/tool/power-reserve-estimator/bibliography.ts +16 -0
  122. package/src/tool/power-reserve-estimator/client.ts +139 -0
  123. package/src/tool/power-reserve-estimator/component.astro +15 -0
  124. package/src/tool/power-reserve-estimator/components/EstimatorPanel.astro +150 -0
  125. package/src/tool/power-reserve-estimator/entry.ts +51 -0
  126. package/src/tool/power-reserve-estimator/i18n/de.ts +158 -0
  127. package/src/tool/power-reserve-estimator/i18n/en.ts +158 -0
  128. package/src/tool/power-reserve-estimator/i18n/es.ts +158 -0
  129. package/src/tool/power-reserve-estimator/i18n/fr.ts +158 -0
  130. package/src/tool/power-reserve-estimator/i18n/id.ts +158 -0
  131. package/src/tool/power-reserve-estimator/i18n/it.ts +158 -0
  132. package/src/tool/power-reserve-estimator/i18n/ja.ts +158 -0
  133. package/src/tool/power-reserve-estimator/i18n/ko.ts +158 -0
  134. package/src/tool/power-reserve-estimator/i18n/nl.ts +158 -0
  135. package/src/tool/power-reserve-estimator/i18n/pl.ts +158 -0
  136. package/src/tool/power-reserve-estimator/i18n/pt.ts +158 -0
  137. package/src/tool/power-reserve-estimator/i18n/ru.ts +158 -0
  138. package/src/tool/power-reserve-estimator/i18n/sv.ts +158 -0
  139. package/src/tool/power-reserve-estimator/i18n/tr.ts +158 -0
  140. package/src/tool/power-reserve-estimator/i18n/zh.ts +158 -0
  141. package/src/tool/power-reserve-estimator/index.ts +11 -0
  142. package/src/tool/power-reserve-estimator/power-reserve-estimator.css +402 -0
  143. package/src/tool/power-reserve-estimator/seo.astro +16 -0
  144. package/src/tool/strap-taper-calculator/bibliography.astro +16 -0
  145. package/src/tool/strap-taper-calculator/bibliography.ts +12 -0
  146. package/src/tool/strap-taper-calculator/client.ts +129 -0
  147. package/src/tool/strap-taper-calculator/component.astro +15 -0
  148. package/src/tool/strap-taper-calculator/components/CalculatorPanel.astro +114 -0
  149. package/src/tool/strap-taper-calculator/entry.ts +56 -0
  150. package/src/tool/strap-taper-calculator/i18n/de.ts +152 -0
  151. package/src/tool/strap-taper-calculator/i18n/en.ts +152 -0
  152. package/src/tool/strap-taper-calculator/i18n/es.ts +152 -0
  153. package/src/tool/strap-taper-calculator/i18n/fr.ts +152 -0
  154. package/src/tool/strap-taper-calculator/i18n/id.ts +152 -0
  155. package/src/tool/strap-taper-calculator/i18n/it.ts +152 -0
  156. package/src/tool/strap-taper-calculator/i18n/ja.ts +152 -0
  157. package/src/tool/strap-taper-calculator/i18n/ko.ts +152 -0
  158. package/src/tool/strap-taper-calculator/i18n/nl.ts +152 -0
  159. package/src/tool/strap-taper-calculator/i18n/pl.ts +152 -0
  160. package/src/tool/strap-taper-calculator/i18n/pt.ts +152 -0
  161. package/src/tool/strap-taper-calculator/i18n/ru.ts +152 -0
  162. package/src/tool/strap-taper-calculator/i18n/sv.ts +152 -0
  163. package/src/tool/strap-taper-calculator/i18n/tr.ts +152 -0
  164. package/src/tool/strap-taper-calculator/i18n/zh.ts +152 -0
  165. package/src/tool/strap-taper-calculator/index.ts +11 -0
  166. package/src/tool/strap-taper-calculator/seo.astro +16 -0
  167. package/src/tool/strap-taper-calculator/strap-taper-calculator.css +320 -0
  168. package/src/tool/watch-accuracy-tracker/bibliography.astro +16 -0
  169. package/src/tool/watch-accuracy-tracker/bibliography.ts +12 -0
  170. package/src/tool/watch-accuracy-tracker/chart.ts +126 -0
  171. package/src/tool/watch-accuracy-tracker/client.ts +287 -0
  172. package/src/tool/watch-accuracy-tracker/component.astro +35 -0
  173. package/src/tool/watch-accuracy-tracker/components/AccuracyTracker.astro +96 -0
  174. package/src/tool/watch-accuracy-tracker/components/DriftPredictor.astro +126 -0
  175. package/src/tool/watch-accuracy-tracker/components/LogHistory.astro +66 -0
  176. package/src/tool/watch-accuracy-tracker/entry.ts +94 -0
  177. package/src/tool/watch-accuracy-tracker/i18n/de.ts +167 -0
  178. package/src/tool/watch-accuracy-tracker/i18n/en.ts +167 -0
  179. package/src/tool/watch-accuracy-tracker/i18n/es.ts +167 -0
  180. package/src/tool/watch-accuracy-tracker/i18n/fr.ts +167 -0
  181. package/src/tool/watch-accuracy-tracker/i18n/id.ts +167 -0
  182. package/src/tool/watch-accuracy-tracker/i18n/it.ts +167 -0
  183. package/src/tool/watch-accuracy-tracker/i18n/ja.ts +167 -0
  184. package/src/tool/watch-accuracy-tracker/i18n/ko.ts +167 -0
  185. package/src/tool/watch-accuracy-tracker/i18n/nl.ts +167 -0
  186. package/src/tool/watch-accuracy-tracker/i18n/pl.ts +167 -0
  187. package/src/tool/watch-accuracy-tracker/i18n/pt.ts +167 -0
  188. package/src/tool/watch-accuracy-tracker/i18n/ru.ts +167 -0
  189. package/src/tool/watch-accuracy-tracker/i18n/sv.ts +167 -0
  190. package/src/tool/watch-accuracy-tracker/i18n/tr.ts +167 -0
  191. package/src/tool/watch-accuracy-tracker/i18n/zh.ts +167 -0
  192. package/src/tool/watch-accuracy-tracker/index.ts +9 -0
  193. package/src/tool/watch-accuracy-tracker/logger.ts +105 -0
  194. package/src/tool/watch-accuracy-tracker/seo.astro +16 -0
  195. package/src/tool/watch-accuracy-tracker/utils.ts +99 -0
  196. package/src/tool/watch-accuracy-tracker/watch-accuracy-tracker.css +564 -0
  197. package/src/tool/watch-savings-planner/bibliography.astro +16 -0
  198. package/src/tool/watch-savings-planner/bibliography.ts +8 -0
  199. package/src/tool/watch-savings-planner/client.ts +271 -0
  200. package/src/tool/watch-savings-planner/component.astro +33 -0
  201. package/src/tool/watch-savings-planner/components/AddGoalForm.astro +49 -0
  202. package/src/tool/watch-savings-planner/components/GoalCard.astro +58 -0
  203. package/src/tool/watch-savings-planner/entry.ts +62 -0
  204. package/src/tool/watch-savings-planner/i18n/de.ts +153 -0
  205. package/src/tool/watch-savings-planner/i18n/en.ts +155 -0
  206. package/src/tool/watch-savings-planner/i18n/es.ts +153 -0
  207. package/src/tool/watch-savings-planner/i18n/fr.ts +153 -0
  208. package/src/tool/watch-savings-planner/i18n/id.ts +153 -0
  209. package/src/tool/watch-savings-planner/i18n/it.ts +153 -0
  210. package/src/tool/watch-savings-planner/i18n/ja.ts +153 -0
  211. package/src/tool/watch-savings-planner/i18n/ko.ts +153 -0
  212. package/src/tool/watch-savings-planner/i18n/nl.ts +153 -0
  213. package/src/tool/watch-savings-planner/i18n/pl.ts +153 -0
  214. package/src/tool/watch-savings-planner/i18n/pt.ts +153 -0
  215. package/src/tool/watch-savings-planner/i18n/ru.ts +153 -0
  216. package/src/tool/watch-savings-planner/i18n/sv.ts +153 -0
  217. package/src/tool/watch-savings-planner/i18n/tr.ts +153 -0
  218. package/src/tool/watch-savings-planner/i18n/zh.ts +153 -0
  219. package/src/tool/watch-savings-planner/index.ts +11 -0
  220. package/src/tool/watch-savings-planner/seo.astro +16 -0
  221. package/src/tool/watch-savings-planner/utils.ts +0 -0
  222. package/src/tool/watch-savings-planner/watch-savings-planner.css +460 -0
  223. package/src/tool/water-resistance-converter/bibliography.astro +16 -0
  224. package/src/tool/water-resistance-converter/bibliography.ts +16 -0
  225. package/src/tool/water-resistance-converter/client.ts +56 -0
  226. package/src/tool/water-resistance-converter/component.astro +15 -0
  227. package/src/tool/water-resistance-converter/components/ConverterPanel.astro +113 -0
  228. package/src/tool/water-resistance-converter/entry.ts +52 -0
  229. package/src/tool/water-resistance-converter/i18n/de.ts +148 -0
  230. package/src/tool/water-resistance-converter/i18n/en.ts +148 -0
  231. package/src/tool/water-resistance-converter/i18n/es.ts +148 -0
  232. package/src/tool/water-resistance-converter/i18n/fr.ts +148 -0
  233. package/src/tool/water-resistance-converter/i18n/id.ts +148 -0
  234. package/src/tool/water-resistance-converter/i18n/it.ts +148 -0
  235. package/src/tool/water-resistance-converter/i18n/ja.ts +148 -0
  236. package/src/tool/water-resistance-converter/i18n/ko.ts +148 -0
  237. package/src/tool/water-resistance-converter/i18n/nl.ts +148 -0
  238. package/src/tool/water-resistance-converter/i18n/pl.ts +148 -0
  239. package/src/tool/water-resistance-converter/i18n/pt.ts +148 -0
  240. package/src/tool/water-resistance-converter/i18n/ru.ts +148 -0
  241. package/src/tool/water-resistance-converter/i18n/sv.ts +148 -0
  242. package/src/tool/water-resistance-converter/i18n/tr.ts +148 -0
  243. package/src/tool/water-resistance-converter/i18n/zh.ts +148 -0
  244. package/src/tool/water-resistance-converter/index.ts +11 -0
  245. package/src/tool/water-resistance-converter/seo.astro +16 -0
  246. package/src/tool/water-resistance-converter/water-resistance-converter.css +254 -0
  247. package/src/tool/wrist-presence-calculator/bibliography.astro +16 -0
  248. package/src/tool/wrist-presence-calculator/bibliography.ts +12 -0
  249. package/src/tool/wrist-presence-calculator/client.ts +180 -0
  250. package/src/tool/wrist-presence-calculator/component.astro +23 -0
  251. package/src/tool/wrist-presence-calculator/components/CalculatorInputs.astro +97 -0
  252. package/src/tool/wrist-presence-calculator/components/FitResult.astro +68 -0
  253. package/src/tool/wrist-presence-calculator/components/Visualizer.astro +16 -0
  254. package/src/tool/wrist-presence-calculator/entry.ts +59 -0
  255. package/src/tool/wrist-presence-calculator/helpers/canvas.ts +189 -0
  256. package/src/tool/wrist-presence-calculator/helpers/results.ts +78 -0
  257. package/src/tool/wrist-presence-calculator/i18n/de.ts +139 -0
  258. package/src/tool/wrist-presence-calculator/i18n/en.ts +155 -0
  259. package/src/tool/wrist-presence-calculator/i18n/es.ts +139 -0
  260. package/src/tool/wrist-presence-calculator/i18n/fr.ts +139 -0
  261. package/src/tool/wrist-presence-calculator/i18n/id.ts +139 -0
  262. package/src/tool/wrist-presence-calculator/i18n/it.ts +139 -0
  263. package/src/tool/wrist-presence-calculator/i18n/ja.ts +139 -0
  264. package/src/tool/wrist-presence-calculator/i18n/ko.ts +139 -0
  265. package/src/tool/wrist-presence-calculator/i18n/nl.ts +139 -0
  266. package/src/tool/wrist-presence-calculator/i18n/pl.ts +139 -0
  267. package/src/tool/wrist-presence-calculator/i18n/pt.ts +139 -0
  268. package/src/tool/wrist-presence-calculator/i18n/ru.ts +139 -0
  269. package/src/tool/wrist-presence-calculator/i18n/sv.ts +139 -0
  270. package/src/tool/wrist-presence-calculator/i18n/tr.ts +139 -0
  271. package/src/tool/wrist-presence-calculator/i18n/zh.ts +155 -0
  272. package/src/tool/wrist-presence-calculator/index.ts +11 -0
  273. package/src/tool/wrist-presence-calculator/seo.astro +16 -0
  274. package/src/tool/wrist-presence-calculator/utils.ts +30 -0
  275. package/src/tool/wrist-presence-calculator/wrist-presence-calculator.css +372 -0
  276. package/src/tools.ts +26 -0
  277. package/src/types.ts +70 -0
@@ -0,0 +1,155 @@
1
+ import type { ToolLocaleContent } from '../../../types';
2
+ import type { WristPresenceCalculatorUI } from '../entry';
3
+ import { bibliography } from '../bibliography';
4
+
5
+ export const content: ToolLocaleContent<WristPresenceCalculatorUI> = {
6
+ slug: 'wrist-presence-calculator',
7
+ title: '手表腕上存在感与尺寸计算器',
8
+ description: '计算表壳与手腕比例、平腕宽度覆盖率,根据美学指南确定最适合您手腕的理想手表尺寸。',
9
+ ui: {
10
+ title: '手腕尺寸设置',
11
+ wristCircumferenceLabel: '手腕周长',
12
+ caseDiameterLabel: '表壳直径',
13
+ lugToLugLabel: '表耳间距',
14
+ wristWidthLabel: '平腕宽度',
15
+ autoEstimateWidthLabel: '自动估算平腕宽度(30%)',
16
+ unitCm: '厘米',
17
+ unitInches: '英寸',
18
+ unitMm: '毫米',
19
+ fitAnalysisTitle: '佩戴贴合度评估',
20
+ caseWristRatioLabel: '表壳与手腕比例',
21
+ wristCoverageLabel: '手腕覆盖率',
22
+ verdictLabel: '结论',
23
+ verdictOversized: '过大',
24
+ verdictBold: '大胆 / 现代',
25
+ verdictGolden: '黄金 / 经典',
26
+ verdictVintage: '复古 / 偏小',
27
+ coverageSafe: '安全贴合',
28
+ coverageBold: '大胆贴合',
29
+ coverageOverhang: '表耳悬空警告',
30
+ sweetSpotTitle: '佩戴风格',
31
+ sweetSpotDesc: '最佳比例区间代表了最平衡的视觉效果。',
32
+ minClassicLabel: '复古 / 经典',
33
+ sweetSpotLabel: '美学最佳区间',
34
+ maxBoldLabel: '现代 / 大胆',
35
+ maxLugToLugLabel: '最大推荐表耳间距',
36
+ idealSizesTitle: '理想尺寸指南',
37
+ visualizerTitle: '实时佩戴模拟',
38
+ wristWidthHelp: '根据手腕形状估算。',
39
+ estimatedLabel: '估算值',
40
+ customLabel: '手腕',
41
+ },
42
+ seo: [
43
+ { type: 'title', text: '如何选择完美的手表尺寸:比例贴合指南', level: 2 },
44
+ { type: 'paragraph', html: '选择合适的手表尺寸是数学、几何与个人风格的结合。在钟表学中,时计在手臂上的呈现方式被称为<strong>腕上存在感</strong>。一块手表应当看起来平衡——既不会因难看的悬空而支配手腕,也不会像一个小点般消失。要实现这种平衡,手表藏家会分析三个主要指标:表壳直径、表耳间距和手腕形状。' },
45
+ { type: 'title', text: '理解表壳与手腕的比例', level: 3 },
46
+ { type: 'paragraph', html: '衡量手表比例的传统方法是表壳与手腕之比。将手腕周长转换为毫米后除以表壳直径,即可得到这个比例。比例在<strong>4.5 到 5.0</strong>之间是经典的最佳区间。比例从<strong>4.0 到 4.5</strong>营造出大胆、运动的现代感。比例高于5.0呈现复古正装表的美感,而低于4.0则被认为过大。' },
47
+ { type: 'title', text: '为什么表耳间距和平腕宽度决定了真正的贴合度', level: 3 },
48
+ { type: 'paragraph', html: '虽然表壳直径备受关注,但<strong>表耳间距</strong>(从上方表耳尖端到下方表耳尖端的垂直长度)才是贴合度的真正考验。手表尺寸的黄金法则是表耳间距不应超过手腕的平腕宽度。如果表耳延伸到手腕边缘之外,就会产生表耳悬空。这会导致手表滑动、表带与皮肤之间出现缝隙,以及视觉上的不协调。' },
49
+ { type: 'title', text: '扁平手腕 vs 圆形手腕:针对具体手型的尺寸选择', level: 3 },
50
+ { type: 'paragraph', html: '手腕周长只能说明部分问题。相同周长的手腕可能有不同的横截面形状:扁平或圆形。扁平手腕的上表面更宽,可以安全佩戴表耳间距更长的手表。圆形手腕更接近圆柱形,上表面较窄,需要更短的表耳间距以避免悬空。' },
51
+ ],
52
+ faq: [
53
+ {
54
+ question: '理想的表壳与手腕比例是多少?',
55
+ answer: '理想的表壳与手腕比例在4.0到5.0之间。用周长除以表壳直径,4.5是传统钟表学的最佳比例。4.0到4.5呈现现代、大胆的腕上存在感,高于5.0适合复古正装表,低于4.0则显得过大。',
56
+ },
57
+ {
58
+ question: '为什么表耳间距比表壳直径更重要?',
59
+ answer: '表壳直径只测量表盘的宽度,而表耳间距决定了手表在手腕上的总长度。如果表耳间距超过手腕的平腕宽度,表耳就会悬空,导致表带垂直下垂。这会让手表看起来不稳定且比例失调。',
60
+ },
61
+ {
62
+ question: '扁平手腕和圆形手腕对手表尺寸和贴合有什么影响?',
63
+ answer: '两个周长相同的手腕(例如17厘米)佩戴手表的效果可能截然不同。扁平手腕的上表面更宽,可以容纳更长表耳间距的手表而不产生悬空。圆形手腕的上表面较窄,需要更短的表耳间距才能稳定贴合。',
64
+ },
65
+ {
66
+ question: '厚度和表圈宽度如何改变手表的视觉大小?',
67
+ answer: '表圈极薄的手表(如正装表)表盘面积更大,看起来比实际直径更大。带有厚旋转表圈的潜水表表盘较小,显得更紧凑。同样,厚度超过13毫米的手表看起来更厚重,视觉重量也更大。',
68
+ },
69
+ ],
70
+ bibliography,
71
+ howTo: [
72
+ {
73
+ name: '测量手腕',
74
+ text: '用软尺环绕手腕骨骼一周,以厘米或英寸为单位测量周长。',
75
+ },
76
+ {
77
+ name: '输入手表尺寸',
78
+ text: '输入您要评估的手表的表壳直径和表耳间距。',
79
+ },
80
+ {
81
+ name: '分析贴合度与模拟',
82
+ text: '查看表壳与手腕比例、表耳悬空警告,并观察实时模拟图。',
83
+ },
84
+ ],
85
+ schemas: [
86
+ {
87
+ '@context': 'https://schema.org',
88
+ '@type': 'FAQPage',
89
+ 'mainEntity': [
90
+ {
91
+ '@type': 'Question',
92
+ 'name': '理想的表壳与手腕比例是多少?',
93
+ 'acceptedAnswer': {
94
+ '@type': 'Answer',
95
+ 'text': '理想的表壳与手腕比例在4.0到5.0之间。用周长除以表壳直径,4.5是传统钟表学的最佳比例。4.0到4.5呈现现代、大胆的腕上存在感,高于5.0适合复古正装表,低于4.0则显得过大。'
96
+ }
97
+ },
98
+ {
99
+ '@type': 'Question',
100
+ 'name': '为什么表耳间距比表壳直径更重要?',
101
+ 'acceptedAnswer': {
102
+ '@type': 'Answer',
103
+ 'text': '表壳直径只测量表盘的宽度,而表耳间距决定了手表在手腕上的总长度。如果表耳间距超过手腕的平腕宽度,表耳就会悬空,导致表带垂直下垂。这会让手表看起来不稳定且比例失调。'
104
+ }
105
+ },
106
+ {
107
+ '@type': 'Question',
108
+ 'name': '扁平手腕和圆形手腕对手表尺寸和贴合有什么影响?',
109
+ 'acceptedAnswer': {
110
+ '@type': 'Answer',
111
+ 'text': '两个周长相同的手腕(例如17厘米)佩戴手表的效果可能截然不同。扁平手腕的上表面更宽,可以容纳更长表耳间距的手表而不产生悬空。圆形手腕的上表面较窄,需要更短的表耳间距才能稳定贴合。'
112
+ }
113
+ },
114
+ {
115
+ '@type': 'Question',
116
+ 'name': '厚度和表圈宽度如何改变手表的视觉大小?',
117
+ 'acceptedAnswer': {
118
+ '@type': 'Answer',
119
+ 'text': '表圈极薄的手表(如正装表)表盘面积更大,看起来比实际直径更大。带有厚旋转表圈的潜水表表盘较小,显得更紧凑。同样,厚度超过13毫米的手表看起来更厚重,视觉重量也更大。'
120
+ }
121
+ }
122
+ ]
123
+ },
124
+ {
125
+ '@context': 'https://schema.org',
126
+ '@type': 'SoftwareApplication',
127
+ 'name': '手表腕上存在感与尺寸计算器',
128
+ 'operatingSystem': 'All',
129
+ 'applicationCategory': 'UtilitiesApplication',
130
+ 'browserRequirements': 'Requires HTML5. Requires JavaScript.'
131
+ },
132
+ {
133
+ '@context': 'https://schema.org',
134
+ '@type': 'HowTo',
135
+ 'name': '如何确定完美的腕表尺寸',
136
+ 'step': [
137
+ {
138
+ '@type': 'HowToStep',
139
+ 'name': '测量手腕',
140
+ 'text': '用软尺环绕手腕骨骼一周,以厘米或英寸为单位测量周长。'
141
+ },
142
+ {
143
+ '@type': 'HowToStep',
144
+ 'name': '输入手表尺寸',
145
+ 'text': '输入您要评估的手表的表壳直径和表耳间距。'
146
+ },
147
+ {
148
+ '@type': 'HowToStep',
149
+ 'name': '分析贴合度与模拟',
150
+ 'text': '查看表壳与手腕比例、表耳悬空警告,并观察实时模拟图。'
151
+ }
152
+ ]
153
+ }
154
+ ]
155
+ };
@@ -0,0 +1,11 @@
1
+ import type { ToolDefinition } from '../../types';
2
+ import { wristPresenceCalculator } from './entry';
3
+
4
+ export * from './entry';
5
+
6
+ export const WRIST_PRESENCE_CALCULATOR_TOOL: ToolDefinition = {
7
+ entry: wristPresenceCalculator,
8
+ Component: () => import('./component.astro'),
9
+ SEOComponent: () => import('./seo.astro'),
10
+ BibliographyComponent: () => import('./bibliography.astro'),
11
+ };
@@ -0,0 +1,16 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { wristPresenceCalculator } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'en' } = Astro.props;
11
+ const loader = wristPresenceCalculator.i18n[locale] || wristPresenceCalculator.i18n.en;
12
+ const content = await loader?.();
13
+ if (!content) return null;
14
+ ---
15
+
16
+ {content.seo && content.seo.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
@@ -0,0 +1,30 @@
1
+ export function calculateCaseToWristRatio(circumference: number, caseDiameter: number): number {
2
+ if (!caseDiameter) return 0;
3
+ return circumference / caseDiameter;
4
+ }
5
+
6
+ export function estimateWristWidth(circumference: number): number {
7
+ return circumference * 0.3;
8
+ }
9
+
10
+ export function getIdealCaseRange(circumference: number) {
11
+ return {
12
+ min: circumference / 5.0,
13
+ sweetSpot: circumference / 4.5,
14
+ max: circumference / 4.0,
15
+ maxLugToLug: estimateWristWidth(circumference)
16
+ };
17
+ }
18
+
19
+ export function getRatioClass(ratio: number): string {
20
+ if (ratio < 4.0) return 'verdict-oversized';
21
+ if (ratio >= 4.0 && ratio < 4.5) return 'verdict-bold';
22
+ if (ratio >= 4.5 && ratio <= 5.0) return 'verdict-golden';
23
+ return 'verdict-vintage';
24
+ }
25
+
26
+ export function getCoverageClass(coverage: number): string {
27
+ if (coverage < 80) return 'coverage-safe';
28
+ if (coverage >= 80 && coverage <= 100) return 'coverage-bold';
29
+ return 'coverage-overhang';
30
+ }
@@ -0,0 +1,372 @@
1
+ .tool-main-card {
2
+ background: var(--bg-surface);
3
+ border: 1px solid var(--border-color);
4
+ border-radius: 1rem;
5
+ padding: 2rem;
6
+ display: flex;
7
+ flex-direction: column;
8
+ gap: 2rem;
9
+ box-shadow: var(--shadow-base);
10
+
11
+ --color-white: #fff;
12
+ --visualizer-wrist-fill: rgba(255, 255, 255, 0.05);
13
+ --visualizer-wrist-stroke: rgba(255, 255, 255, 0.15);
14
+ --visualizer-outer-circle: rgba(255, 255, 255, 0.03);
15
+ --visualizer-outer-stroke: rgba(255, 255, 255, 0.08);
16
+ --visualizer-watch-face: rgba(25, 25, 35, 0.95);
17
+ --visualizer-watch-inner: rgba(255, 255, 255, 0.08);
18
+ --visualizer-watch-hands: rgba(255, 255, 255, 0.35);
19
+ --visualizer-dim-line: rgba(255, 255, 255, 0.2);
20
+ --visualizer-dim-text: rgba(255, 255, 255, 0.6);
21
+ --color-fit-golden: #10b981;
22
+ --color-fit-golden-bg: rgba(16, 185, 129, 0.1);
23
+ --color-fit-golden-border: rgba(16, 185, 129, 0.25);
24
+ --color-fit-bold: #3b82f6;
25
+ --color-fit-bold-bg: rgba(59, 130, 246, 0.1);
26
+ --color-fit-bold-border: rgba(59, 130, 246, 0.25);
27
+ --color-fit-oversized: #ef4444;
28
+ --color-fit-oversized-bg: rgba(239, 68, 68, 0.1);
29
+ --color-fit-oversized-border: rgba(239, 68, 68, 0.25);
30
+ --color-fit-vintage: #f59e0b;
31
+ --color-fit-vintage-bg: rgba(245, 158, 11, 0.1);
32
+ --color-fit-vintage-border: rgba(245, 158, 11, 0.25);
33
+ }
34
+
35
+ .theme-light .tool-main-card {
36
+ --visualizer-wrist-fill: rgba(0, 0, 0, 0.04);
37
+ --visualizer-wrist-stroke: rgba(0, 0, 0, 0.12);
38
+ --visualizer-outer-circle: rgba(0, 0, 0, 0.02);
39
+ --visualizer-outer-stroke: rgba(0, 0, 0, 0.06);
40
+ --visualizer-watch-face: rgba(255, 255, 255, 0.95);
41
+ --visualizer-watch-inner: rgba(0, 0, 0, 0.04);
42
+ --visualizer-watch-hands: rgba(0, 0, 0, 0.6);
43
+ --visualizer-dim-line: rgba(0, 0, 0, 0.25);
44
+ --visualizer-dim-text: rgba(0, 0, 0, 0.75);
45
+ --color-fit-golden: #047857;
46
+ --color-fit-golden-bg: rgba(16, 185, 129, 0.08);
47
+ --color-fit-golden-border: rgba(16, 185, 129, 0.2);
48
+ --color-fit-bold: #1d4ed8;
49
+ --color-fit-bold-bg: rgba(59, 130, 246, 0.08);
50
+ --color-fit-bold-border: rgba(59, 130, 246, 0.2);
51
+ --color-fit-oversized: #b91c1c;
52
+ --color-fit-oversized-bg: rgba(239, 68, 68, 0.08);
53
+ --color-fit-oversized-border: rgba(239, 68, 68, 0.2);
54
+ --color-fit-vintage: #b45309;
55
+ --color-fit-vintage-bg: rgba(245, 158, 11, 0.08);
56
+ --color-fit-vintage-border: rgba(245, 158, 11, 0.2);
57
+ }
58
+
59
+ .wrist-calculator-layout {
60
+ display: grid;
61
+ grid-template-columns: 1.1fr 0.9fr;
62
+ gap: 2.5rem;
63
+ }
64
+
65
+ @media (max-width: 992px) {
66
+ .wrist-calculator-layout {
67
+ grid-template-columns: 1fr;
68
+ gap: 2rem;
69
+ }
70
+ }
71
+
72
+ .calc-panel {
73
+ display: flex;
74
+ flex-direction: column;
75
+ gap: 2rem;
76
+ }
77
+
78
+ .inputs-section,
79
+ .visualizer-section,
80
+ .results-section,
81
+ .recommendations-section {
82
+ display: flex;
83
+ flex-direction: column;
84
+ background: transparent;
85
+ border: none;
86
+ border-radius: 0;
87
+ padding: 0;
88
+ margin-bottom: 0;
89
+ }
90
+
91
+ .results-wrapper {
92
+ display: flex;
93
+ flex-direction: column;
94
+ gap: 2rem;
95
+ }
96
+
97
+ .section-header {
98
+ display: flex;
99
+ justify-content: space-between;
100
+ align-items: center;
101
+ border-bottom: 1px solid var(--border-color);
102
+ padding-bottom: 0.5rem;
103
+ margin-bottom: 1.25rem;
104
+ }
105
+
106
+ .section-header h3 {
107
+ font-size: 1.05rem;
108
+ font-weight: 700;
109
+ margin: 0;
110
+ color: var(--text-base);
111
+ }
112
+
113
+ .input-group {
114
+ margin-bottom: 1.25rem;
115
+ }
116
+
117
+ .input-group:last-child {
118
+ margin-bottom: 0;
119
+ }
120
+
121
+ .input-header {
122
+ display: flex;
123
+ justify-content: space-between;
124
+ align-items: center;
125
+ margin-bottom: 0.5rem;
126
+ }
127
+
128
+ .input-label {
129
+ font-size: 0.85rem;
130
+ font-weight: 600;
131
+ color: var(--text-muted);
132
+ }
133
+
134
+ .value-display {
135
+ font-size: 0.9rem;
136
+ font-weight: 700;
137
+ color: var(--accent);
138
+ background: rgba(244, 63, 94, 0.08);
139
+ padding: 0.15rem 0.45rem;
140
+ border-radius: 0.3rem;
141
+ }
142
+
143
+ .slider-control {
144
+ width: 100%;
145
+ height: 6px;
146
+ border-radius: 3px;
147
+ background: var(--border-color);
148
+ outline: none;
149
+ -webkit-appearance: none;
150
+ appearance: none;
151
+ }
152
+
153
+ .slider-control::-webkit-slider-thumb {
154
+ -webkit-appearance: none;
155
+ appearance: none;
156
+ width: 16px;
157
+ height: 16px;
158
+ border-radius: 50%;
159
+ background: var(--accent);
160
+ cursor: pointer;
161
+ transition: transform 0.1s ease;
162
+ }
163
+
164
+ .slider-control::-webkit-slider-thumb:hover {
165
+ transform: scale(1.2);
166
+ }
167
+
168
+ .toggle-container {
169
+ display: flex;
170
+ align-items: center;
171
+ gap: 0.5rem;
172
+ }
173
+
174
+ .checkbox-control {
175
+ width: 15px;
176
+ height: 15px;
177
+ accent-color: var(--accent);
178
+ cursor: pointer;
179
+ }
180
+
181
+ .toggle-label {
182
+ font-size: 0.8rem;
183
+ color: var(--text-muted);
184
+ cursor: pointer;
185
+ user-select: none;
186
+ }
187
+
188
+ .unit-selector {
189
+ display: flex;
190
+ background: var(--bg-muted);
191
+ border: 1px solid var(--border-color);
192
+ border-radius: 0.4rem;
193
+ padding: 0.1rem;
194
+ }
195
+
196
+ .unit-btn {
197
+ background: transparent;
198
+ border: none;
199
+ color: var(--text-muted);
200
+ font-size: 0.7rem;
201
+ font-weight: 700;
202
+ padding: 0.2rem 0.5rem;
203
+ border-radius: 0.25rem;
204
+ cursor: pointer;
205
+ transition: all 0.15s ease;
206
+ }
207
+
208
+ .unit-btn.active {
209
+ background: var(--accent);
210
+ color: var(--color-white);
211
+ }
212
+
213
+ .canvas-wrapper {
214
+ position: relative;
215
+ width: 100%;
216
+ aspect-ratio: 16 / 9;
217
+ background: var(--bg-muted);
218
+ border-radius: 0.5rem;
219
+ overflow: hidden;
220
+ border: 1px solid var(--border-color);
221
+ }
222
+
223
+ .fit-canvas {
224
+ width: 100%;
225
+ height: 100%;
226
+ display: block;
227
+ }
228
+
229
+ .metrics-summary-grid {
230
+ display: grid;
231
+ grid-template-columns: 1fr 1fr;
232
+ gap: 1rem;
233
+ }
234
+
235
+ @media (max-width: 576px) {
236
+ .metrics-summary-grid {
237
+ grid-template-columns: 1fr;
238
+ }
239
+ }
240
+
241
+ .metric-box {
242
+ background: var(--bg-muted);
243
+ border: 1px solid var(--border-color);
244
+ border-radius: 0.5rem;
245
+ padding: 1rem;
246
+ display: flex;
247
+ flex-direction: column;
248
+ align-items: center;
249
+ text-align: center;
250
+ }
251
+
252
+ .metric-val {
253
+ font-size: 1.5rem;
254
+ font-weight: 800;
255
+ color: var(--text-base);
256
+ margin-bottom: 0.2rem;
257
+ letter-spacing: -0.010em;
258
+ }
259
+
260
+ .metric-lbl {
261
+ font-size: 0.75rem;
262
+ color: var(--text-muted);
263
+ text-transform: uppercase;
264
+ letter-spacing: 0.04em;
265
+ margin-bottom: 0.5rem;
266
+ }
267
+
268
+ .verdict-badge {
269
+ font-size: 0.75rem;
270
+ font-weight: 700;
271
+ padding: 0.25rem 0.65rem;
272
+ border-radius: 99px;
273
+ display: inline-block;
274
+ }
275
+
276
+ .verdict-golden {
277
+ color: var(--color-fit-golden);
278
+ background: var(--color-fit-golden-bg);
279
+ border: 1px solid var(--color-fit-golden-border);
280
+ }
281
+
282
+ .verdict-bold {
283
+ color: var(--color-fit-bold);
284
+ background: var(--color-fit-bold-bg);
285
+ border: 1px solid var(--color-fit-bold-border);
286
+ }
287
+
288
+ .verdict-oversized {
289
+ color: var(--color-fit-oversized);
290
+ background: var(--color-fit-oversized-bg);
291
+ border: 1px solid var(--color-fit-oversized-border);
292
+ }
293
+
294
+ .verdict-vintage {
295
+ color: var(--color-fit-vintage);
296
+ background: var(--color-fit-vintage-bg);
297
+ border: 1px solid var(--color-fit-vintage-border);
298
+ }
299
+
300
+ .coverage-safe {
301
+ color: var(--color-fit-golden);
302
+ background: var(--color-fit-golden-bg);
303
+ border: 1px solid var(--color-fit-golden-border);
304
+ }
305
+
306
+ .coverage-bold {
307
+ color: var(--color-fit-vintage);
308
+ background: var(--color-fit-vintage-bg);
309
+ border: 1px solid var(--color-fit-vintage-border);
310
+ }
311
+
312
+ .coverage-overhang {
313
+ color: var(--color-fit-oversized);
314
+ background: var(--color-fit-oversized-bg);
315
+ border: 1px solid var(--color-fit-oversized-border);
316
+ }
317
+
318
+ .verdict-box {
319
+ margin-top: 1rem;
320
+ padding: 0.85rem;
321
+ border-radius: 0.5rem;
322
+ background: var(--bg-muted);
323
+ border: 1px solid var(--border-color);
324
+ font-size: 0.85rem;
325
+ line-height: 1.4;
326
+ color: var(--text-muted);
327
+ }
328
+
329
+ .verdict-box strong {
330
+ color: var(--text-base);
331
+ }
332
+
333
+ .recommendations-table-wrapper {
334
+ overflow-x: auto;
335
+ }
336
+
337
+ .recommendations-table {
338
+ width: 100%;
339
+ border-collapse: collapse;
340
+ text-align: left;
341
+ }
342
+
343
+ .recommendations-table th {
344
+ font-size: 0.75rem;
345
+ font-weight: 700;
346
+ color: var(--text-muted);
347
+ text-transform: uppercase;
348
+ letter-spacing: 0.04em;
349
+ padding: 0.5rem 0.75rem;
350
+ background: var(--bg-muted);
351
+ border-bottom: 1px solid var(--border-color);
352
+ }
353
+
354
+ .recommendations-table td {
355
+ font-size: 0.85rem;
356
+ padding: 0.75rem;
357
+ border-bottom: 1px solid var(--border-color);
358
+ }
359
+
360
+ .recommendations-table tr:last-child td {
361
+ border-bottom: none;
362
+ }
363
+
364
+ .rec-value {
365
+ font-weight: 700;
366
+ color: var(--accent);
367
+ }
368
+
369
+ .rec-target {
370
+ font-weight: 600;
371
+ color: var(--text-base);
372
+ }
package/src/tools.ts ADDED
@@ -0,0 +1,26 @@
1
+ export { ALL_ENTRIES } from './entries';
2
+ import type { ToolDefinition } from './types';
3
+ import { WATCH_ACCURACY_TRACKER_TOOL } from './tool/watch-accuracy-tracker';
4
+ import { WRIST_PRESENCE_CALCULATOR_TOOL } from './tool/wrist-presence-calculator';
5
+ import { DEMAGNETIZING_TIMER_TOOL } from './tool/demagnetizing-timer';
6
+ import { WATCH_SAVINGS_PLANNER_TOOL } from './tool/watch-savings-planner';
7
+ import { CROWN_REFERENCE_GUIDE_TOOL } from './tool/crown-reference-guide';
8
+ import { POWER_RESERVE_ESTIMATOR_TOOL } from './tool/power-reserve-estimator';
9
+ import { BEAT_RATE_CONVERTER_TOOL } from './tool/beat-rate-converter';
10
+ import { WATER_RESISTANCE_CONVERTER_TOOL } from './tool/water-resistance-converter';
11
+ import { STRAP_TAPER_CALCULATOR_TOOL } from './tool/strap-taper-calculator';
12
+
13
+ export const ALL_TOOLS: ToolDefinition[] = [
14
+ WATCH_ACCURACY_TRACKER_TOOL,
15
+ WRIST_PRESENCE_CALCULATOR_TOOL,
16
+ DEMAGNETIZING_TIMER_TOOL,
17
+ WATCH_SAVINGS_PLANNER_TOOL,
18
+ CROWN_REFERENCE_GUIDE_TOOL,
19
+ POWER_RESERVE_ESTIMATOR_TOOL,
20
+ BEAT_RATE_CONVERTER_TOOL,
21
+ WATER_RESISTANCE_CONVERTER_TOOL,
22
+ STRAP_TAPER_CALCULATOR_TOOL,
23
+ ];
24
+
25
+
26
+
package/src/types.ts ADDED
@@ -0,0 +1,70 @@
1
+ import type { SEOSection } from '@jjlmoya/utils-shared';
2
+ import type { WithContext, Thing } from 'schema-dts';
3
+
4
+ export type { SEOSection };
5
+
6
+ export type KnownLocale =
7
+ | 'ar' | 'da' | 'de' | 'en' | 'es' | 'fi'
8
+ | 'fr' | 'id' | 'it' | 'ja' | 'ko' | 'nb' | 'nl'
9
+ | 'pl' | 'pt' | 'ru' | 'sv' | 'tr' | 'zh';
10
+
11
+ export interface FAQItem {
12
+ question: string;
13
+ answer: string;
14
+ }
15
+
16
+ export interface BibliographyEntry {
17
+ name: string;
18
+ url: string;
19
+ }
20
+
21
+ export interface HowToStep {
22
+ name: string;
23
+ text: string;
24
+ }
25
+
26
+ export interface ToolLocaleContent<TUI extends Record<string, string> = Record<string, string>> {
27
+ slug: string;
28
+ title: string;
29
+ description: string;
30
+ ui: TUI;
31
+ seo: SEOSection[];
32
+ faq: FAQItem[];
33
+ bibliography: BibliographyEntry[];
34
+ howTo: HowToStep[];
35
+ schemas: WithContext<Thing>[];
36
+ }
37
+
38
+ export interface CategoryLocaleContent {
39
+ slug: string;
40
+ title: string;
41
+ description: string;
42
+ seo: SEOSection[];
43
+ }
44
+
45
+ export type LocaleLoader<T> = () => Promise<T>;
46
+
47
+ export type LocaleMap<T> = Partial<Record<KnownLocale, LocaleLoader<T>>>;
48
+
49
+ export interface ChronoToolEntry<TUI extends Record<string, string> = Record<string, string>> {
50
+ id: string;
51
+ icons: {
52
+ bg: string;
53
+ fg: string;
54
+ };
55
+ i18n: LocaleMap<ToolLocaleContent<TUI>>;
56
+ }
57
+
58
+ export interface ChronoCategoryEntry {
59
+ icon: string;
60
+ tools: ChronoToolEntry[];
61
+ i18n: LocaleMap<CategoryLocaleContent>;
62
+ }
63
+
64
+ export interface ToolDefinition {
65
+ entry: ChronoToolEntry;
66
+ Component: unknown;
67
+ SEOComponent: unknown;
68
+ BibliographyComponent: unknown;
69
+ }
70
+