@jjlmoya/utils-health 1.1.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 (155) hide show
  1. package/package.json +60 -0
  2. package/src/category/i18n/en.ts +60 -0
  3. package/src/category/i18n/es.ts +60 -0
  4. package/src/category/i18n/fr.ts +60 -0
  5. package/src/category/index.ts +22 -0
  6. package/src/category/seo.astro +15 -0
  7. package/src/components/PreviewNavSidebar.astro +116 -0
  8. package/src/components/PreviewToolbar.astro +143 -0
  9. package/src/data.ts +28 -0
  10. package/src/env.d.ts +5 -0
  11. package/src/index.ts +36 -0
  12. package/src/layouts/PreviewLayout.astro +117 -0
  13. package/src/pages/[locale]/[slug].astro +146 -0
  14. package/src/pages/[locale].astro +251 -0
  15. package/src/pages/index.astro +4 -0
  16. package/src/tests/faq_count.test.ts +19 -0
  17. package/src/tests/locale_completeness.test.ts +42 -0
  18. package/src/tests/mocks/astro_mock.js +2 -0
  19. package/src/tests/no_h1_in_components.test.ts +48 -0
  20. package/src/tests/schemas_fulfillment.test.ts +23 -0
  21. package/src/tests/seo_length.test.ts +22 -0
  22. package/src/tests/title_quality.test.ts +55 -0
  23. package/src/tests/tool_validation.test.ts +17 -0
  24. package/src/tool/binauralTuner/bibliography.astro +14 -0
  25. package/src/tool/binauralTuner/component.astro +687 -0
  26. package/src/tool/binauralTuner/i18n/en.ts +187 -0
  27. package/src/tool/binauralTuner/i18n/es.ts +187 -0
  28. package/src/tool/binauralTuner/i18n/fr.ts +187 -0
  29. package/src/tool/binauralTuner/index.ts +27 -0
  30. package/src/tool/binauralTuner/seo.astro +14 -0
  31. package/src/tool/binauralTuner/ui.ts +18 -0
  32. package/src/tool/bloodUnitConverter/bibliography.astro +14 -0
  33. package/src/tool/bloodUnitConverter/component.astro +915 -0
  34. package/src/tool/bloodUnitConverter/i18n/en.ts +227 -0
  35. package/src/tool/bloodUnitConverter/i18n/es.ts +250 -0
  36. package/src/tool/bloodUnitConverter/i18n/fr.ts +218 -0
  37. package/src/tool/bloodUnitConverter/index.ts +27 -0
  38. package/src/tool/bloodUnitConverter/seo.astro +14 -0
  39. package/src/tool/bloodUnitConverter/ui.ts +38 -0
  40. package/src/tool/bmiCalculator/bibliography.astro +14 -0
  41. package/src/tool/bmiCalculator/component.astro +415 -0
  42. package/src/tool/bmiCalculator/i18n/en.ts +217 -0
  43. package/src/tool/bmiCalculator/i18n/es.ts +221 -0
  44. package/src/tool/bmiCalculator/i18n/fr.ts +217 -0
  45. package/src/tool/bmiCalculator/index.ts +27 -0
  46. package/src/tool/bmiCalculator/seo.astro +14 -0
  47. package/src/tool/bmiCalculator/ui.ts +21 -0
  48. package/src/tool/breathingVisualizer/bibliography.astro +14 -0
  49. package/src/tool/breathingVisualizer/component.astro +636 -0
  50. package/src/tool/breathingVisualizer/i18n/en.ts +206 -0
  51. package/src/tool/breathingVisualizer/i18n/es.ts +206 -0
  52. package/src/tool/breathingVisualizer/i18n/fr.ts +206 -0
  53. package/src/tool/breathingVisualizer/index.ts +27 -0
  54. package/src/tool/breathingVisualizer/seo.astro +14 -0
  55. package/src/tool/breathingVisualizer/ui.ts +31 -0
  56. package/src/tool/caffeineTracker/bibliography.astro +14 -0
  57. package/src/tool/caffeineTracker/component.astro +1210 -0
  58. package/src/tool/caffeineTracker/i18n/en.ts +198 -0
  59. package/src/tool/caffeineTracker/i18n/es.ts +198 -0
  60. package/src/tool/caffeineTracker/i18n/fr.ts +198 -0
  61. package/src/tool/caffeineTracker/index.ts +27 -0
  62. package/src/tool/caffeineTracker/logic.ts +31 -0
  63. package/src/tool/caffeineTracker/seo.astro +14 -0
  64. package/src/tool/caffeineTracker/ui.ts +36 -0
  65. package/src/tool/daltonismSimulator/bibliography.astro +14 -0
  66. package/src/tool/daltonismSimulator/component.astro +383 -0
  67. package/src/tool/daltonismSimulator/i18n/en.ts +188 -0
  68. package/src/tool/daltonismSimulator/i18n/es.ts +218 -0
  69. package/src/tool/daltonismSimulator/i18n/fr.ts +168 -0
  70. package/src/tool/daltonismSimulator/index.ts +27 -0
  71. package/src/tool/daltonismSimulator/seo.astro +14 -0
  72. package/src/tool/daltonismSimulator/ui.ts +20 -0
  73. package/src/tool/digestionStopwatch/bibliography.astro +14 -0
  74. package/src/tool/digestionStopwatch/component.astro +627 -0
  75. package/src/tool/digestionStopwatch/i18n/en.ts +173 -0
  76. package/src/tool/digestionStopwatch/i18n/es.ts +173 -0
  77. package/src/tool/digestionStopwatch/i18n/fr.ts +173 -0
  78. package/src/tool/digestionStopwatch/index.ts +27 -0
  79. package/src/tool/digestionStopwatch/logic.ts +63 -0
  80. package/src/tool/digestionStopwatch/seo.astro +14 -0
  81. package/src/tool/digestionStopwatch/ui.ts +20 -0
  82. package/src/tool/epworthSleepinessScale/bibliography.astro +14 -0
  83. package/src/tool/epworthSleepinessScale/component.astro +528 -0
  84. package/src/tool/epworthSleepinessScale/i18n/en.ts +217 -0
  85. package/src/tool/epworthSleepinessScale/i18n/es.ts +217 -0
  86. package/src/tool/epworthSleepinessScale/i18n/fr.ts +217 -0
  87. package/src/tool/epworthSleepinessScale/index.ts +27 -0
  88. package/src/tool/epworthSleepinessScale/seo.astro +14 -0
  89. package/src/tool/epworthSleepinessScale/ui.ts +27 -0
  90. package/src/tool/hydrationCalculator/bibliography.astro +14 -0
  91. package/src/tool/hydrationCalculator/component.astro +694 -0
  92. package/src/tool/hydrationCalculator/i18n/en.ts +217 -0
  93. package/src/tool/hydrationCalculator/i18n/es.ts +222 -0
  94. package/src/tool/hydrationCalculator/i18n/fr.ts +199 -0
  95. package/src/tool/hydrationCalculator/index.ts +27 -0
  96. package/src/tool/hydrationCalculator/seo.astro +14 -0
  97. package/src/tool/hydrationCalculator/ui.ts +28 -0
  98. package/src/tool/pelliRobsonTest/bibliography.astro +14 -0
  99. package/src/tool/pelliRobsonTest/component.astro +653 -0
  100. package/src/tool/pelliRobsonTest/i18n/en.ts +205 -0
  101. package/src/tool/pelliRobsonTest/i18n/es.ts +205 -0
  102. package/src/tool/pelliRobsonTest/i18n/fr.ts +205 -0
  103. package/src/tool/pelliRobsonTest/index.ts +27 -0
  104. package/src/tool/pelliRobsonTest/seo.astro +14 -0
  105. package/src/tool/pelliRobsonTest/ui.ts +21 -0
  106. package/src/tool/peripheralVisionTrainer/bibliography.astro +14 -0
  107. package/src/tool/peripheralVisionTrainer/component.astro +678 -0
  108. package/src/tool/peripheralVisionTrainer/i18n/en.ts +224 -0
  109. package/src/tool/peripheralVisionTrainer/i18n/es.ts +224 -0
  110. package/src/tool/peripheralVisionTrainer/i18n/fr.ts +211 -0
  111. package/src/tool/peripheralVisionTrainer/index.ts +27 -0
  112. package/src/tool/peripheralVisionTrainer/seo.astro +14 -0
  113. package/src/tool/peripheralVisionTrainer/ui.ts +26 -0
  114. package/src/tool/readingDistanceCalculator/bibliography.astro +14 -0
  115. package/src/tool/readingDistanceCalculator/component.astro +588 -0
  116. package/src/tool/readingDistanceCalculator/i18n/en.ts +202 -0
  117. package/src/tool/readingDistanceCalculator/i18n/es.ts +215 -0
  118. package/src/tool/readingDistanceCalculator/i18n/fr.ts +193 -0
  119. package/src/tool/readingDistanceCalculator/index.ts +31 -0
  120. package/src/tool/readingDistanceCalculator/seo.astro +14 -0
  121. package/src/tool/readingDistanceCalculator/ui.ts +18 -0
  122. package/src/tool/screenDecompressionTime/bibliography.astro +14 -0
  123. package/src/tool/screenDecompressionTime/component.astro +671 -0
  124. package/src/tool/screenDecompressionTime/i18n/en.ts +225 -0
  125. package/src/tool/screenDecompressionTime/i18n/es.ts +247 -0
  126. package/src/tool/screenDecompressionTime/i18n/fr.ts +225 -0
  127. package/src/tool/screenDecompressionTime/index.ts +27 -0
  128. package/src/tool/screenDecompressionTime/seo.astro +14 -0
  129. package/src/tool/screenDecompressionTime/ui.ts +32 -0
  130. package/src/tool/tinnitusReliever/bibliography.astro +14 -0
  131. package/src/tool/tinnitusReliever/component.astro +581 -0
  132. package/src/tool/tinnitusReliever/i18n/en.ts +161 -0
  133. package/src/tool/tinnitusReliever/i18n/es.ts +161 -0
  134. package/src/tool/tinnitusReliever/i18n/fr.ts +161 -0
  135. package/src/tool/tinnitusReliever/index.ts +27 -0
  136. package/src/tool/tinnitusReliever/seo.astro +14 -0
  137. package/src/tool/tinnitusReliever/ui.ts +9 -0
  138. package/src/tool/ubeCalculator/bibliography.astro +14 -0
  139. package/src/tool/ubeCalculator/component.astro +683 -0
  140. package/src/tool/ubeCalculator/i18n/en.ts +200 -0
  141. package/src/tool/ubeCalculator/i18n/es.ts +200 -0
  142. package/src/tool/ubeCalculator/i18n/fr.ts +196 -0
  143. package/src/tool/ubeCalculator/index.ts +27 -0
  144. package/src/tool/ubeCalculator/seo.astro +14 -0
  145. package/src/tool/ubeCalculator/ui.ts +26 -0
  146. package/src/tool/waterPurifier/bibliography.astro +14 -0
  147. package/src/tool/waterPurifier/component.astro +628 -0
  148. package/src/tool/waterPurifier/i18n/en.ts +167 -0
  149. package/src/tool/waterPurifier/i18n/es.ts +167 -0
  150. package/src/tool/waterPurifier/i18n/fr.ts +167 -0
  151. package/src/tool/waterPurifier/index.ts +27 -0
  152. package/src/tool/waterPurifier/seo.astro +14 -0
  153. package/src/tool/waterPurifier/ui.ts +18 -0
  154. package/src/tools.ts +19 -0
  155. package/src/types.ts +72 -0
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@jjlmoya/utils-health",
3
+ "version": "1.1.0",
4
+ "type": "module",
5
+ "main": "./src/index.ts",
6
+ "types": "./src/index.ts",
7
+ "exports": {
8
+ ".": "./src/index.ts",
9
+ "./data": "./src/data.ts"
10
+ },
11
+ "files": [
12
+ "src"
13
+ ],
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "scripts": {
18
+ "dev": "astro dev",
19
+ "start": "astro dev",
20
+ "build": "astro build",
21
+ "preview": "astro preview",
22
+ "astro": "astro",
23
+ "lint": "eslint src/ --max-warnings 0 && stylelint \"src/**/*.{css,astro}\"",
24
+ "check": "astro check",
25
+ "type-check": "astro check",
26
+ "test": "vitest run",
27
+ "preversion": "npm run lint && npm run test",
28
+ "postversion": "git push && git push --tags",
29
+ "patch": "npm version patch",
30
+ "minor": "npm version minor",
31
+ "major": "npm version major"
32
+ },
33
+ "lint-staged": {
34
+ "*.{ts,tsx,astro}": [
35
+ "eslint --fix"
36
+ ]
37
+ },
38
+ "dependencies": {
39
+ "@iconify-json/mdi": "^1.2.3",
40
+ "@jjlmoya/utils-shared": "^1.2.0",
41
+ "astro": "^6.1.2",
42
+ "astro-icon": "^1.1.0"
43
+ },
44
+ "devDependencies": {
45
+ "@astrojs/check": "^0.9.8",
46
+ "eslint": "^9.39.4",
47
+ "eslint-plugin-astro": "^1.6.0",
48
+ "eslint-plugin-no-comments": "^1.1.10",
49
+ "husky": "^9.1.7",
50
+ "lint-staged": "^16.4.0",
51
+ "postcss-html": "^1.8.1",
52
+ "schema-dts": "^1.1.2",
53
+ "stylelint": "^17.6.0",
54
+ "stylelint-config-standard": "^40.0.0",
55
+ "stylelint-declaration-strict-value": "^1.11.1",
56
+ "typescript": "^5.4.0",
57
+ "typescript-eslint": "^8.58.0",
58
+ "vitest": "^4.1.2"
59
+ }
60
+ }
@@ -0,0 +1,60 @@
1
+ import type { CategoryLocaleContent } from '../../types';
2
+
3
+ export const content: CategoryLocaleContent = {
4
+ slug: 'health',
5
+ title: 'Online Health Tools',
6
+ description:
7
+ 'Free health calculators and utilities: BMI, body fat percentage, calories, hydration and more. Based on international medical standards.',
8
+ seo: [
9
+ {
10
+ type: 'summary',
11
+ title: 'What you will find in this category',
12
+ items: [
13
+ 'BMI calculator with visual scale and WHO categories',
14
+ 'Results based on verified medical algorithms',
15
+ 'Completely free, no registration or tracking cookies',
16
+ 'Full privacy: calculations happen on your device',
17
+ ],
18
+ },
19
+ {
20
+ type: 'title',
21
+ text: 'Science-Based Health Tools',
22
+ level: 2,
23
+ },
24
+ {
25
+ type: 'paragraph',
26
+ html: 'Personal health is one of the areas where precise information makes a real difference. Our collection of <strong>online health utilities</strong> is designed to provide objective data, based on the standards of the World Health Organization (WHO) and other internationally recognized medical institutions.',
27
+ },
28
+ {
29
+ type: 'paragraph',
30
+ html: 'Unlike mobile apps that require access to personal data or device sensors, all these tools work directly in the browser. <strong>No health data leaves your device</strong>. No servers, no storage, no user profiles.',
31
+ },
32
+ {
33
+ type: 'title',
34
+ text: 'Why a number is not everything',
35
+ level: 3,
36
+ },
37
+ {
38
+ type: 'paragraph',
39
+ html: 'Health calculators offer an <strong>indicative reference</strong>, not a diagnosis. The Body Mass Index, for example, is a useful statistical screening tool for the general population, but it does not differentiate between muscle tissue and body fat. An elite athlete may have a technically elevated BMI without presenting any metabolic health problems.',
40
+ },
41
+ {
42
+ type: 'tip',
43
+ title: 'Important medical disclaimer',
44
+ html: 'All tools in this category are informational and educational in nature. They do not replace the judgment of a healthcare professional under any circumstances. If you have concerns about your health, always consult your doctor or specialist.',
45
+ },
46
+ {
47
+ type: 'title',
48
+ text: 'Fundamentals of Health Metrics',
49
+ level: 2,
50
+ },
51
+ {
52
+ type: 'list',
53
+ items: [
54
+ '<strong>BMI (Body Mass Index):</strong> Weight/height ratio. WHO screening indicator to classify nutritional status in adults.',
55
+ '<strong>Privacy by design:</strong> Calculations performed exclusively on the client. No telemetry, no tracking cookies, no personal data analysis.',
56
+ '<strong>International standards:</strong> The ranges and thresholds used correspond to guidelines published by the WHO and reference medical bodies.',
57
+ ],
58
+ },
59
+ ],
60
+ };
@@ -0,0 +1,60 @@
1
+ import type { CategoryLocaleContent } from '../../types';
2
+
3
+ export const content: CategoryLocaleContent = {
4
+ slug: 'salud',
5
+ title: 'Herramientas de Salud Online',
6
+ description:
7
+ 'Calculadoras y utilidades de salud gratuitas: IMC, porcentaje de grasa, calorías, hidratación y más. Basadas en estándares médicos internacionales.',
8
+ seo: [
9
+ {
10
+ type: 'summary',
11
+ title: 'Qué encontrarás en esta categoría',
12
+ items: [
13
+ 'Calculadora de IMC con escala visual y categorías según la OMS',
14
+ 'Resultados basados en algoritmos médicos verificados',
15
+ 'Totalmente gratuito, sin registro ni cookies de seguimiento',
16
+ 'Privacidad total: los cálculos ocurren en tu dispositivo',
17
+ ],
18
+ },
19
+ {
20
+ type: 'title',
21
+ text: 'Herramientas de Salud Basadas en Ciencia',
22
+ level: 2,
23
+ },
24
+ {
25
+ type: 'paragraph',
26
+ html: 'La salud personal es uno de los ámbitos donde la información precisa marca la diferencia. Nuestra colección de <strong>utilidades de salud online</strong> está diseñada para proporcionar datos objetivos, basados en los estándares de la Organización Mundial de la Salud (OMS) y otras instituciones médicas de referencia internacional.',
27
+ },
28
+ {
29
+ type: 'paragraph',
30
+ html: 'A diferencia de las aplicaciones móviles que requieren acceso a datos personales o sensores del dispositivo, todas estas herramientas funcionan directamente en el navegador. <strong>Ningún dato de salud abandona tu dispositivo</strong>. No hay servidores, no hay almacenamiento, no hay perfiles de usuario.',
31
+ },
32
+ {
33
+ type: 'title',
34
+ text: 'Por qué un número no lo es todo',
35
+ level: 3,
36
+ },
37
+ {
38
+ type: 'paragraph',
39
+ html: 'Las calculadoras de salud ofrecen una <strong>referencia orientativa</strong>, no un diagnóstico. El Índice de Masa Corporal, por ejemplo, es una herramienta de cribado estadístico útil para la población general, pero no diferencia entre tejido muscular y grasa corporal. Un deportista de élite puede tener un IMC técnicamente elevado sin presentar ningún problema de salud metabólico.',
40
+ },
41
+ {
42
+ type: 'tip',
43
+ title: 'Aviso médico importante',
44
+ html: 'Todas las herramientas de esta categoría son de carácter informativo y educativo. No sustituyen en ningún caso el criterio de un profesional sanitario. Si tienes preocupaciones sobre tu salud, consulta siempre con tu médico o especialista.',
45
+ },
46
+ {
47
+ type: 'title',
48
+ text: 'Fundamentos de las Métricas de Salud',
49
+ level: 2,
50
+ },
51
+ {
52
+ type: 'list',
53
+ items: [
54
+ '<strong>IMC (Índice de Masa Corporal):</strong> Relación peso/altura. Indicador de cribado de la OMS para clasificar el estado nutricional en adultos.',
55
+ '<strong>Privacidad por diseño:</strong> Cálculos realizados exclusivamente en el cliente. Sin telemetría, sin cookies de seguimiento, sin análisis de datos personales.',
56
+ '<strong>Estándares internacionales:</strong> Los rangos y umbrales utilizados corresponden a las guías publicadas por la OMS y organismos médicos de referencia.',
57
+ ],
58
+ },
59
+ ],
60
+ };
@@ -0,0 +1,60 @@
1
+ import type { CategoryLocaleContent } from '../../types';
2
+
3
+ export const content: CategoryLocaleContent = {
4
+ slug: 'sante',
5
+ title: 'Outils de Santé en Ligne',
6
+ description:
7
+ "Calculatrices et utilitaires de santé gratuits : IMC, pourcentage de graisse, calories, hydratation et plus. Basés sur les normes médicales internationales.",
8
+ seo: [
9
+ {
10
+ type: 'summary',
11
+ title: 'Ce que vous trouverez dans cette catégorie',
12
+ items: [
13
+ "Calculatrice d'IMC avec échelle visuelle et catégories OMS",
14
+ 'Résultats basés sur des algorithmes médicaux vérifiés',
15
+ 'Totalement gratuit, sans inscription ni cookies de suivi',
16
+ 'Confidentialité totale : les calculs se font sur votre appareil',
17
+ ],
18
+ },
19
+ {
20
+ type: 'title',
21
+ text: "Outils de Santé Basés sur la Science",
22
+ level: 2,
23
+ },
24
+ {
25
+ type: 'paragraph',
26
+ html: "La santé personnelle est l'un des domaines où une information précise fait vraiment la différence. Notre collection d'<strong>utilitaires de santé en ligne</strong> est conçue pour fournir des données objectives, basées sur les normes de l'Organisation Mondiale de la Santé (OMS) et d'autres institutions médicales internationales de référence.",
27
+ },
28
+ {
29
+ type: 'paragraph',
30
+ html: "Contrairement aux applications mobiles qui nécessitent l'accès aux données personnelles ou aux capteurs de l'appareil, tous ces outils fonctionnent directement dans le navigateur. <strong>Aucune donnée de santé ne quitte votre appareil</strong>. Pas de serveurs, pas de stockage, pas de profils utilisateur.",
31
+ },
32
+ {
33
+ type: 'title',
34
+ text: "Pourquoi un chiffre ne fait pas tout",
35
+ level: 3,
36
+ },
37
+ {
38
+ type: 'paragraph',
39
+ html: "Les calculatrices de santé offrent une <strong>référence indicative</strong>, pas un diagnostic. L'Indice de Masse Corporelle, par exemple, est un outil de dépistage statistique utile pour la population générale, mais ne différencie pas entre le tissu musculaire et la graisse corporelle. Un athlète d'élite peut avoir un IMC techniquement élevé sans présenter aucun problème de santé métabolique.",
40
+ },
41
+ {
42
+ type: 'tip',
43
+ title: 'Avis médical important',
44
+ html: "Tous les outils de cette catégorie sont de nature informative et éducative. Ils ne remplacent en aucun cas le jugement d'un professionnel de santé. Si vous avez des préoccupations concernant votre santé, consultez toujours votre médecin ou spécialiste.",
45
+ },
46
+ {
47
+ type: 'title',
48
+ text: 'Fondamentaux des Métriques de Santé',
49
+ level: 2,
50
+ },
51
+ {
52
+ type: 'list',
53
+ items: [
54
+ "<strong>IMC (Indice de Masse Corporelle) :</strong> Rapport poids/taille. Indicateur de dépistage de l'OMS pour classer l'état nutritionnel chez les adultes.",
55
+ '<strong>Confidentialité par conception :</strong> Calculs effectués exclusivement côté client. Sans télémétrie, sans cookies de suivi, sans analyse de données personnelles.',
56
+ "<strong>Normes internationales :</strong> Les plages et seuils utilisés correspondent aux recommandations publiées par l'OMS et les organismes médicaux de référence.",
57
+ ],
58
+ },
59
+ ],
60
+ };
@@ -0,0 +1,22 @@
1
+ import type { HealthCategoryEntry } from '../types';
2
+ import { bmiCalculator } from '../tool/bmiCalculator';
3
+ import { daltonismSimulator } from '../tool/daltonismSimulator';
4
+ import { tinnitusReliever } from '../tool/tinnitusReliever';
5
+ import { breathingVisualizer } from '../tool/breathingVisualizer';
6
+ import { caffeineTracker } from '../tool/caffeineTracker';
7
+ import { waterPurifier } from '../tool/waterPurifier';
8
+ import { digestionStopwatch } from '../tool/digestionStopwatch';
9
+ import { binauralTuner } from '../tool/binauralTuner';
10
+ import { pelliRobsonTest } from '../tool/pelliRobsonTest';
11
+ import { peripheralVisionTrainer } from '../tool/peripheralVisionTrainer';
12
+ import { epworthSleepinessScale } from '../tool/epworthSleepinessScale';
13
+
14
+ export const healthCategory: HealthCategoryEntry = {
15
+ icon: 'mdi:heart-pulse',
16
+ tools: [bmiCalculator, daltonismSimulator, tinnitusReliever, breathingVisualizer, caffeineTracker, waterPurifier, digestionStopwatch, binauralTuner, pelliRobsonTest, peripheralVisionTrainer, epworthSleepinessScale],
17
+ i18n: {
18
+ es: () => import('./i18n/es').then((m) => m.content),
19
+ en: () => import('./i18n/en').then((m) => m.content),
20
+ fr: () => import('./i18n/fr').then((m) => m.content),
21
+ },
22
+ };
@@ -0,0 +1,15 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { healthCategory } from './index';
4
+ import type { KnownLocale } from '../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'es' } = Astro.props;
11
+ const content = await healthCategory.i18n[locale]?.();
12
+ ---
13
+
14
+ {content && <SEORenderer content={{ locale, sections: content.seo }} />}
15
+
@@ -0,0 +1,116 @@
1
+ ---
2
+ interface NavItem {
3
+ id: string;
4
+ title: string;
5
+ href: string;
6
+ isActive?: boolean;
7
+ }
8
+
9
+ interface Props {
10
+ categoryTitle: string;
11
+ tools?: NavItem[];
12
+ }
13
+
14
+ const { categoryTitle, tools = [] } = Astro.props;
15
+ ---
16
+
17
+ <nav class="preview-nav-sidebar">
18
+ <div class="sidebar-header">
19
+ <h3>{categoryTitle}</h3>
20
+ </div>
21
+
22
+ <ul class="tools-list">
23
+ {tools.map((tool) => (
24
+ <li>
25
+ <a
26
+ href={tool.href}
27
+ class:list={['tool-link', { active: tool.isActive }]}
28
+ >
29
+ <span class="tool-title">{tool.title}</span>
30
+ </a>
31
+ </li>
32
+ ))}
33
+ </ul>
34
+ </nav>
35
+
36
+ <style>
37
+ .preview-nav-sidebar {
38
+ display: flex;
39
+ flex-direction: column;
40
+ height: 100%;
41
+ background: var(--bg-surface, #0f172a);
42
+ }
43
+
44
+ .sidebar-header {
45
+ padding: 2rem 1.5rem 1.5rem;
46
+ border-bottom: 1px solid var(--border-color, #1e293b);
47
+ }
48
+
49
+ .sidebar-header h3 {
50
+ margin: 0;
51
+ font-size: 0.75rem;
52
+ font-weight: 900;
53
+ text-transform: uppercase;
54
+ letter-spacing: 0.1em;
55
+ color: var(--text-muted, #94a3b8);
56
+ }
57
+
58
+ .tools-list {
59
+ list-style: none;
60
+ margin: 0;
61
+ padding: 0.5rem 0;
62
+ display: flex;
63
+ flex-direction: column;
64
+ gap: 0.25rem;
65
+ }
66
+
67
+ .tools-list li {
68
+ margin: 0;
69
+ }
70
+
71
+ .tool-link {
72
+ display: flex;
73
+ align-items: center;
74
+ padding: 0.75rem 1.5rem;
75
+ color: var(--text-muted, #94a3b8);
76
+ text-decoration: none;
77
+ font-size: 0.9375rem;
78
+ font-weight: 500;
79
+ transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
80
+ border-left: 3px solid transparent;
81
+ position: relative;
82
+ }
83
+
84
+ .tool-link:hover {
85
+ color: var(--text-base, #f1f5f9);
86
+ background: rgba(255, 255, 255, 0.08);
87
+ padding-left: 1.75rem;
88
+ }
89
+
90
+ .tool-link.active {
91
+ color: var(--accent, #f43f5e);
92
+ background: rgba(244, 63, 94, 0.15);
93
+ border-left-color: var(--accent, #f43f5e);
94
+ font-weight: 600;
95
+ }
96
+
97
+ .tool-link.active::before {
98
+ content: '';
99
+ position: absolute;
100
+ left: 0;
101
+ top: 50%;
102
+ transform: translateY(-50%);
103
+ width: 3px;
104
+ height: 24px;
105
+ background: var(--accent, #f43f5e);
106
+ border-radius: 0 2px 2px 0;
107
+ }
108
+
109
+ .tool-title {
110
+ display: block;
111
+ overflow: hidden;
112
+ text-overflow: ellipsis;
113
+ white-space: nowrap;
114
+ }
115
+ </style>
116
+
@@ -0,0 +1,143 @@
1
+ ---
2
+ import type { KnownLocale } from '../types';
3
+
4
+ interface Props {
5
+ currentLocale: KnownLocale;
6
+ localeUrls?: Partial<Record<KnownLocale, string>>;
7
+ }
8
+
9
+ const { currentLocale, localeUrls = {} } = Astro.props;
10
+
11
+ const otherLocales = Object.entries(localeUrls).filter(([l]) => l !== currentLocale) as [
12
+ KnownLocale,
13
+ string,
14
+ ][];
15
+ ---
16
+
17
+ <nav class="preview-toolbar">
18
+ {
19
+ otherLocales.length > 0 && (
20
+ <div class="locale-group">
21
+ <span class="locale-current">{currentLocale.toUpperCase()}</span>
22
+ {otherLocales.map(([locale, url]) => (
23
+ <a href={url} class="btn-locale">
24
+ {locale.toUpperCase()}
25
+ </a>
26
+ ))}
27
+ </div>
28
+ )
29
+ }
30
+
31
+ <button id="theme-toggle" class="btn-theme" aria-label="Toggle theme" title="Toggle theme">
32
+ <span class="theme-icon"></span>
33
+ </button>
34
+ </nav>
35
+
36
+ <script>
37
+ function initToolbar() {
38
+ const btn = document.getElementById('theme-toggle');
39
+ if (!btn) return;
40
+
41
+ const applyTheme = (theme: string) => {
42
+ document.documentElement.classList.remove('theme-light', 'theme-dark');
43
+ document.documentElement.classList.add(theme);
44
+ localStorage.setItem('theme', theme);
45
+ };
46
+
47
+ btn.addEventListener('click', () => {
48
+ const next = document.documentElement.classList.contains('theme-dark')
49
+ ? 'theme-light'
50
+ : 'theme-dark';
51
+ applyTheme(next);
52
+ });
53
+ }
54
+
55
+ initToolbar();
56
+ document.addEventListener('astro:after-swap', initToolbar);
57
+ </script>
58
+
59
+ <style>
60
+ .preview-toolbar {
61
+ position: fixed;
62
+ top: 1rem;
63
+ right: 1.5rem;
64
+ display: flex;
65
+ align-items: center;
66
+ gap: 0.5rem;
67
+ z-index: 1000;
68
+ background: var(--bg-surface);
69
+ padding: 0.4rem;
70
+ border-radius: 1rem;
71
+ border: 1px solid var(--border-color);
72
+ backdrop-filter: blur(12px);
73
+ }
74
+
75
+ .locale-group {
76
+ display: flex;
77
+ align-items: center;
78
+ gap: 0.25rem;
79
+ padding-right: 0.5rem;
80
+ border-right: 1px solid var(--border-color);
81
+ }
82
+
83
+ .locale-current {
84
+ font-size: 0.7rem;
85
+ font-weight: 900;
86
+ color: var(--text-muted);
87
+ padding: 0.4rem 0.6rem;
88
+ letter-spacing: 0.08em;
89
+ }
90
+
91
+ .btn-locale {
92
+ background: transparent;
93
+ border: 1px solid var(--border-color);
94
+ color: var(--text-main);
95
+ cursor: pointer;
96
+ padding: 0.4rem 0.7rem;
97
+ border-radius: 0.5rem;
98
+ font-weight: 700;
99
+ font-size: 0.7rem;
100
+ text-decoration: none;
101
+ letter-spacing: 0.08em;
102
+ transition: all 0.2s ease;
103
+ }
104
+
105
+ .btn-locale:hover {
106
+ border-color: var(--accent);
107
+ color: var(--accent);
108
+ }
109
+
110
+ .btn-theme {
111
+ background: transparent;
112
+ border: none;
113
+ cursor: pointer;
114
+ padding: 0.5rem;
115
+ border-radius: 0.6rem;
116
+ display: flex;
117
+ align-items: center;
118
+ justify-content: center;
119
+ transition: background 0.2s ease;
120
+ }
121
+
122
+ .btn-theme:hover {
123
+ background: rgba(255, 255, 255, 0.08);
124
+ }
125
+
126
+ .theme-icon {
127
+ display: block;
128
+ width: 16px;
129
+ height: 16px;
130
+ border-radius: 50%;
131
+ }
132
+
133
+ :global(.theme-light) .theme-icon {
134
+ background: #f59e0b;
135
+ box-shadow: 0 0 8px #fbbf24;
136
+ }
137
+
138
+ :global(.theme-dark) .theme-icon {
139
+ background: #94a3b8;
140
+ box-shadow: inset -4px -2px 0 #1e293b;
141
+ }
142
+ </style>
143
+
package/src/data.ts ADDED
@@ -0,0 +1,28 @@
1
+ export { bmiCalculator } from './tool/bmiCalculator';
2
+ export { daltonismSimulator } from './tool/daltonismSimulator';
3
+ export { tinnitusReliever } from './tool/tinnitusReliever';
4
+ export { breathingVisualizer } from './tool/breathingVisualizer';
5
+ export { caffeineTracker } from './tool/caffeineTracker';
6
+ export { waterPurifier } from './tool/waterPurifier';
7
+ export { digestionStopwatch } from './tool/digestionStopwatch';
8
+ export { binauralTuner } from './tool/binauralTuner';
9
+ export { pelliRobsonTest } from './tool/pelliRobsonTest';
10
+ export { peripheralVisionTrainer } from './tool/peripheralVisionTrainer';
11
+ export { epworthSleepinessScale } from './tool/epworthSleepinessScale';
12
+ export { ubeCalculator } from './tool/ubeCalculator';
13
+ export { screenDecompressionTime } from './tool/screenDecompressionTime';
14
+ export { bloodUnitConverter } from './tool/bloodUnitConverter';
15
+ export { readingDistanceCalculator } from './tool/readingDistanceCalculator';
16
+ export { hydrationCalculator } from './tool/hydrationCalculator';
17
+
18
+ export { healthCategory } from './category';
19
+
20
+ export type {
21
+ KnownLocale,
22
+ ToolLocaleContent,
23
+ CategoryLocaleContent,
24
+ LocaleMap,
25
+ HealthToolEntry,
26
+ HealthCategoryEntry,
27
+ } from './types';
28
+
package/src/env.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ declare module '*.astro' {
2
+ const Component: (_props: Record<string, unknown>) => unknown;
3
+ export default Component;
4
+ }
5
+
package/src/index.ts ADDED
@@ -0,0 +1,36 @@
1
+ export * from './tool/bmiCalculator';
2
+ export * from './tool/daltonismSimulator';
3
+ export * from './tool/tinnitusReliever';
4
+ export * from './tool/breathingVisualizer';
5
+ export * from './tool/caffeineTracker';
6
+ export * from './tool/waterPurifier';
7
+ export * from './tool/digestionStopwatch';
8
+ export * from './tool/binauralTuner';
9
+ export * from './tool/pelliRobsonTest';
10
+ export * from './tool/peripheralVisionTrainer';
11
+ export * from './tool/epworthSleepinessScale';
12
+ export * from './tool/ubeCalculator';
13
+ export * from './tool/screenDecompressionTime';
14
+ export * from './tool/bloodUnitConverter';
15
+ export * from './tool/readingDistanceCalculator';
16
+ export * from './tool/hydrationCalculator';
17
+
18
+ export { healthCategory } from './category';
19
+ export { default as healthCategorySEO } from './category/seo.astro';
20
+
21
+ export type {
22
+ KnownLocale,
23
+ FAQItem,
24
+ BibliographyEntry,
25
+ HowToStep,
26
+ ToolLocaleContent,
27
+ CategoryLocaleContent,
28
+ LocaleLoader,
29
+ LocaleMap,
30
+ HealthToolEntry,
31
+ HealthCategoryEntry,
32
+ ToolDefinition,
33
+ } from './types';
34
+
35
+ export { ALL_TOOLS } from './tools';
36
+