@jjlmoya/utils-tools 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 (134) hide show
  1. package/package.json +63 -0
  2. package/src/category/i18n/en.ts +172 -0
  3. package/src/category/i18n/es.ts +172 -0
  4. package/src/category/i18n/fr.ts +172 -0
  5. package/src/category/index.ts +23 -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 +11 -0
  10. package/src/env.d.ts +5 -0
  11. package/src/index.ts +90 -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 +23 -0
  22. package/src/tests/title_quality.test.ts +56 -0
  23. package/src/tests/tool_validation.test.ts +17 -0
  24. package/src/tool/date-diff-calculator/bibliography.astro +14 -0
  25. package/src/tool/date-diff-calculator/component.astro +370 -0
  26. package/src/tool/date-diff-calculator/i18n/en.ts +132 -0
  27. package/src/tool/date-diff-calculator/i18n/es.ts +132 -0
  28. package/src/tool/date-diff-calculator/i18n/fr.ts +132 -0
  29. package/src/tool/date-diff-calculator/index.ts +22 -0
  30. package/src/tool/date-diff-calculator/seo.astro +14 -0
  31. package/src/tool/date-diff-calculator/ui.ts +17 -0
  32. package/src/tool/drive-direct-link/bibliography.astro +14 -0
  33. package/src/tool/drive-direct-link/component.astro +280 -0
  34. package/src/tool/drive-direct-link/i18n/en.ts +118 -0
  35. package/src/tool/drive-direct-link/i18n/es.ts +118 -0
  36. package/src/tool/drive-direct-link/i18n/fr.ts +118 -0
  37. package/src/tool/drive-direct-link/index.ts +22 -0
  38. package/src/tool/drive-direct-link/seo.astro +14 -0
  39. package/src/tool/drive-direct-link/ui.ts +10 -0
  40. package/src/tool/email-list-cleaner/bibliography.astro +14 -0
  41. package/src/tool/email-list-cleaner/component.astro +375 -0
  42. package/src/tool/email-list-cleaner/i18n/en.ts +140 -0
  43. package/src/tool/email-list-cleaner/i18n/es.ts +140 -0
  44. package/src/tool/email-list-cleaner/i18n/fr.ts +140 -0
  45. package/src/tool/email-list-cleaner/index.ts +22 -0
  46. package/src/tool/email-list-cleaner/seo.astro +14 -0
  47. package/src/tool/email-list-cleaner/ui.ts +15 -0
  48. package/src/tool/env-badge-spain/bibliography.astro +14 -0
  49. package/src/tool/env-badge-spain/component.astro +303 -0
  50. package/src/tool/env-badge-spain/components/BadgeForm.astro +243 -0
  51. package/src/tool/env-badge-spain/components/BadgeResult.astro +151 -0
  52. package/src/tool/env-badge-spain/i18n/en.ts +153 -0
  53. package/src/tool/env-badge-spain/i18n/es.ts +153 -0
  54. package/src/tool/env-badge-spain/i18n/fr.ts +153 -0
  55. package/src/tool/env-badge-spain/index.ts +22 -0
  56. package/src/tool/env-badge-spain/seo.astro +14 -0
  57. package/src/tool/env-badge-spain/ui.ts +53 -0
  58. package/src/tool/morse-beacon/bibliography.astro +14 -0
  59. package/src/tool/morse-beacon/component.astro +534 -0
  60. package/src/tool/morse-beacon/i18n/en.ts +157 -0
  61. package/src/tool/morse-beacon/i18n/es.ts +157 -0
  62. package/src/tool/morse-beacon/i18n/fr.ts +157 -0
  63. package/src/tool/morse-beacon/index.ts +22 -0
  64. package/src/tool/morse-beacon/logic/MorseEngine.ts +124 -0
  65. package/src/tool/morse-beacon/seo.astro +14 -0
  66. package/src/tool/morse-beacon/ui.ts +18 -0
  67. package/src/tool/password-generator/bibliography.astro +14 -0
  68. package/src/tool/password-generator/component.astro +259 -0
  69. package/src/tool/password-generator/components/Config.astro +227 -0
  70. package/src/tool/password-generator/components/Display.astro +147 -0
  71. package/src/tool/password-generator/components/Strength.astro +70 -0
  72. package/src/tool/password-generator/i18n/en.ts +166 -0
  73. package/src/tool/password-generator/i18n/es.ts +166 -0
  74. package/src/tool/password-generator/i18n/fr.ts +166 -0
  75. package/src/tool/password-generator/index.ts +22 -0
  76. package/src/tool/password-generator/seo.astro +14 -0
  77. package/src/tool/password-generator/ui.ts +16 -0
  78. package/src/tool/routes/bibliography.astro +14 -0
  79. package/src/tool/routes/component.astro +543 -0
  80. package/src/tool/routes/i18n/en.ts +157 -0
  81. package/src/tool/routes/i18n/es.ts +157 -0
  82. package/src/tool/routes/i18n/fr.ts +157 -0
  83. package/src/tool/routes/index.ts +22 -0
  84. package/src/tool/routes/logic/GeocodingService.ts +60 -0
  85. package/src/tool/routes/logic/RouteManager.ts +192 -0
  86. package/src/tool/routes/logic/RouteService.ts +66 -0
  87. package/src/tool/routes/seo.astro +14 -0
  88. package/src/tool/routes/ui.ts +16 -0
  89. package/src/tool/rule-of-three/bibliography.astro +14 -0
  90. package/src/tool/rule-of-three/component.astro +369 -0
  91. package/src/tool/rule-of-three/i18n/en.ts +171 -0
  92. package/src/tool/rule-of-three/i18n/es.ts +171 -0
  93. package/src/tool/rule-of-three/i18n/fr.ts +171 -0
  94. package/src/tool/rule-of-three/index.ts +22 -0
  95. package/src/tool/rule-of-three/seo.astro +14 -0
  96. package/src/tool/rule-of-three/ui.ts +13 -0
  97. package/src/tool/seo-content-optimizer/bibliography.astro +14 -0
  98. package/src/tool/seo-content-optimizer/component.astro +552 -0
  99. package/src/tool/seo-content-optimizer/i18n/en.ts +136 -0
  100. package/src/tool/seo-content-optimizer/i18n/es.ts +136 -0
  101. package/src/tool/seo-content-optimizer/i18n/fr.ts +136 -0
  102. package/src/tool/seo-content-optimizer/index.ts +22 -0
  103. package/src/tool/seo-content-optimizer/seo.astro +14 -0
  104. package/src/tool/seo-content-optimizer/ui.ts +29 -0
  105. package/src/tool/speed-reader/bibliography.astro +14 -0
  106. package/src/tool/speed-reader/component.astro +586 -0
  107. package/src/tool/speed-reader/i18n/en.ts +152 -0
  108. package/src/tool/speed-reader/i18n/es.ts +152 -0
  109. package/src/tool/speed-reader/i18n/fr.ts +152 -0
  110. package/src/tool/speed-reader/index.ts +22 -0
  111. package/src/tool/speed-reader/logic/RSVPEngine.ts +106 -0
  112. package/src/tool/speed-reader/seo.astro +14 -0
  113. package/src/tool/speed-reader/ui.ts +14 -0
  114. package/src/tool/text-pixel-calculator/bibliography.astro +14 -0
  115. package/src/tool/text-pixel-calculator/component.astro +315 -0
  116. package/src/tool/text-pixel-calculator/components/Editor.astro +240 -0
  117. package/src/tool/text-pixel-calculator/components/Preview.astro +155 -0
  118. package/src/tool/text-pixel-calculator/components/Stats.astro +87 -0
  119. package/src/tool/text-pixel-calculator/i18n/en.ts +133 -0
  120. package/src/tool/text-pixel-calculator/i18n/es.ts +133 -0
  121. package/src/tool/text-pixel-calculator/i18n/fr.ts +133 -0
  122. package/src/tool/text-pixel-calculator/index.ts +22 -0
  123. package/src/tool/text-pixel-calculator/seo.astro +14 -0
  124. package/src/tool/text-pixel-calculator/ui.ts +15 -0
  125. package/src/tool/whatsapp-link/bibliography.astro +14 -0
  126. package/src/tool/whatsapp-link/component.astro +455 -0
  127. package/src/tool/whatsapp-link/i18n/en.ts +128 -0
  128. package/src/tool/whatsapp-link/i18n/es.ts +128 -0
  129. package/src/tool/whatsapp-link/i18n/fr.ts +128 -0
  130. package/src/tool/whatsapp-link/index.ts +22 -0
  131. package/src/tool/whatsapp-link/seo.astro +14 -0
  132. package/src/tool/whatsapp-link/ui.ts +15 -0
  133. package/src/tools.ts +15 -0
  134. package/src/types.ts +72 -0
@@ -0,0 +1,87 @@
1
+ ---
2
+ interface Props {
3
+ widthLabel: string;
4
+ charsLabel: string;
5
+ }
6
+
7
+ const { widthLabel, charsLabel } = Astro.props;
8
+ ---
9
+
10
+ <div class="tpc-stats">
11
+ <div class="tpc-stat-card">
12
+ <div class="tpc-stat-glow"></div>
13
+ <div class="tpc-stat-inner">
14
+ <span class="tpc-stat-value" id="tpc-width">0</span>
15
+ <span class="tpc-stat-label">{widthLabel} (px)</span>
16
+ </div>
17
+ </div>
18
+ <div class="tpc-stat-card">
19
+ <div class="tpc-stat-inner">
20
+ <span class="tpc-stat-value" id="tpc-chars">0</span>
21
+ <span class="tpc-stat-label">{charsLabel}</span>
22
+ </div>
23
+ </div>
24
+ </div>
25
+
26
+ <style>
27
+ .tpc-stats {
28
+ display: grid;
29
+ grid-template-columns: 1fr 1fr;
30
+ gap: 1rem;
31
+ padding: 1.5rem 0;
32
+ }
33
+
34
+ .tpc-stat-card {
35
+ position: relative;
36
+ background: rgba(255, 255, 255, 0.4);
37
+ border: 1px solid rgba(0, 0, 0, 0.05);
38
+ border-radius: 1.25rem;
39
+ padding: 1.25rem;
40
+ overflow: hidden;
41
+ transition: transform 0.2s;
42
+ }
43
+
44
+ :global(.theme-dark) .tpc-stat-card {
45
+ background: rgba(15, 23, 42, 0.6);
46
+ border-color: rgba(255, 255, 255, 0.05);
47
+ }
48
+
49
+ .tpc-stat-card:hover {
50
+ transform: translateY(-2px);
51
+ }
52
+
53
+ .tpc-stat-inner {
54
+ position: relative;
55
+ z-index: 1;
56
+ display: flex;
57
+ flex-direction: column;
58
+ align-items: center;
59
+ gap: 0.25rem;
60
+ }
61
+
62
+ .tpc-stat-value {
63
+ font-size: 2.5rem;
64
+ font-weight: 800;
65
+ color: var(--tpc-cyan);
66
+ line-height: 1.1;
67
+ font-family: 'JetBrains Mono', monospace;
68
+ }
69
+
70
+ .tpc-stat-label {
71
+ font-size: 0.625rem;
72
+ font-weight: 700;
73
+ color: var(--tpc-text-label);
74
+ text-transform: uppercase;
75
+ letter-spacing: 0.1em;
76
+ }
77
+
78
+ .tpc-stat-glow {
79
+ position: absolute;
80
+ top: -50%;
81
+ left: -50%;
82
+ width: 200%;
83
+ height: 200%;
84
+ background: radial-gradient(circle, rgba(6, 182, 212, 0.1) 0%, transparent 70%);
85
+ pointer-events: none;
86
+ }
87
+ </style>
@@ -0,0 +1,133 @@
1
+ import type { ToolLocaleContent } from '../../../types';
2
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
3
+ import type { TextPixelCalculatorUI } from '../ui';
4
+
5
+ const faqData = [
6
+ {
7
+ question: 'How do I calculate the pixel width of text online?',
8
+ answer: 'Paste your text into the input box, configure the font and size, and the tool will automatically use the browser Canvas API to return the exact width in pixels.',
9
+ },
10
+ {
11
+ question: 'Why does pixel width vary between typefaces?',
12
+ answer: "Most fonts are proportional, meaning each character has a different width. For example, an uppercase 'M' is always wider than a lowercase 'i' in a standard sans-serif font.",
13
+ },
14
+ {
15
+ question: 'Is measuring characters the same as measuring pixels?',
16
+ answer: 'No. Measuring characters gives you the string length, while measuring pixels gives you the visual space it occupies. This is crucial to ensure text does not overflow its container in a web design.',
17
+ },
18
+ {
19
+ question: 'Can I use any Google Fonts typeface?',
20
+ answer: 'Yes, as long as the font is installed on your operating system or loaded on the current page, the tool will measure its dimensions accurately.',
21
+ },
22
+ {
23
+ question: 'Is it safe to process private text or code snippets?',
24
+ answer: 'Yes. The calculator works entirely locally. No data is sent to external servers, guaranteeing complete privacy for your projects.',
25
+ },
26
+ ];
27
+
28
+ const howToData = [
29
+ { name: 'Enter the text', text: 'Type or paste the text you want to measure into the input area.' },
30
+ { name: 'Configure the font', text: 'Set the font family, size in pixels, weight and whether it is italic.' },
31
+ { name: 'Read the result', text: 'The pixel width and character count update in real time.' },
32
+ { name: 'Copy the value', text: 'Click "Copy Width" to save the pixel number to the clipboard.' },
33
+ ];
34
+
35
+ const faqSchema: WithContext<FAQPage> = {
36
+ '@context': 'https://schema.org',
37
+ '@type': 'FAQPage',
38
+ mainEntity: faqData.map((item) => ({
39
+ '@type': 'Question',
40
+ name: item.question,
41
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
42
+ })),
43
+ };
44
+
45
+ const howToSchema: WithContext<HowTo> = {
46
+ '@context': 'https://schema.org',
47
+ '@type': 'HowTo',
48
+ name: 'How to measure text width in pixels',
49
+ step: howToData.map((s) => ({ '@type': 'HowToStep', name: s.name, text: s.text })),
50
+ };
51
+
52
+ const appSchema: WithContext<SoftwareApplication> = {
53
+ '@context': 'https://schema.org',
54
+ '@type': 'SoftwareApplication',
55
+ name: 'Text Pixel Width Calculator',
56
+ applicationCategory: 'UtilitiesApplication',
57
+ operatingSystem: 'Web',
58
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
59
+ description: 'Accurately calculate the pixel width of any text based on font, size and style.',
60
+ };
61
+
62
+ const ui: TextPixelCalculatorUI = {
63
+ textLabel: 'Text to measure',
64
+ textPlaceholder: 'Type or paste here the text you want to measure...',
65
+ defaultText: 'Hello World',
66
+ fontLabel: 'Font family',
67
+ sizeLabel: 'Size (px)',
68
+ weightLabel: 'Weight',
69
+ italicLabel: 'Italic',
70
+ widthLabel: 'Width (pixels)',
71
+ charsLabel: 'Characters',
72
+ previewLabel: 'Visual preview',
73
+ copyBtn: 'Copy Width',
74
+ resetBtn: 'Reset',
75
+ copyDone: 'Width copied',
76
+ };
77
+
78
+ export const content: ToolLocaleContent<TextPixelCalculatorUI> = {
79
+ slug: 'text-pixel-width-calculator',
80
+ title: 'Text Pixel Width Calculator',
81
+ description: 'Accurately calculate how wide any text is in pixels based on font, size and style. Free tool for designers and developers.',
82
+ ui,
83
+ faqTitle: 'Frequently Asked Questions',
84
+ faq: faqData,
85
+ howTo: howToData,
86
+ bibliographyTitle: 'References',
87
+ bibliography: [
88
+ { name: 'W3C: CSS Text Module Level 3', url: 'https://www.w3.org/TR/css-text-3/' },
89
+ { name: 'Google Fonts: Understanding variable fonts', url: 'https://fonts.google.com/knowledge/glossary/variable_fonts' },
90
+ ],
91
+ schemas: [faqSchema, howToSchema, appSchema],
92
+ seo: [
93
+ { type: 'title', level: 2, text: 'Measure the exact pixel width of any text' },
94
+ {
95
+ type: 'paragraph',
96
+ html: 'Is your text overflowing its container? Need to know how much space a heading takes up before rendering it? The <strong>browser Canvas API</strong> allows you to measure the exact width of any text string with pixel precision, without rendering it in the DOM.',
97
+ },
98
+ { type: 'title', level: 3, text: 'Why character counting is not enough' },
99
+ {
100
+ type: 'paragraph',
101
+ html: 'Modern typefaces are <strong>proportional</strong>: each glyph has a different width. A "W" can take up three times more space than an "i". The number of characters tells you nothing about the actual visual space the text occupies.',
102
+ },
103
+ {
104
+ type: 'list',
105
+ items: [
106
+ '<strong>Web design:</strong> Prevent overflow in buttons, labels and table cells.',
107
+ '<strong>SEO:</strong> Search engines truncate titles by pixels, not by characters.',
108
+ '<strong>Canvas and PDF:</strong> Calculate the exact position before drawing text programmatically.',
109
+ '<strong>Tooltips and bubbles:</strong> Dynamically size containers based on inner text.',
110
+ ],
111
+ },
112
+ { type: 'title', level: 3, text: 'How measurement works with Canvas' },
113
+ {
114
+ type: 'paragraph',
115
+ html: 'The <code>ctx.measureText()</code> method of the Canvas API returns a <code>TextMetrics</code> object with a <code>width</code> property reflecting the CSS pixel width using the currently active font. This tool configures the context with your font, size, weight and style before measuring.',
116
+ },
117
+ {
118
+ type: 'code',
119
+ ariaLabel: 'Canvas measureText example',
120
+ code: "const ctx = document.createElement('canvas').getContext('2d');\nctx.font = '700 16px Inter, system-ui, sans-serif';\nconst width = ctx.measureText('Hello World').width; // e.g. 74.5",
121
+ },
122
+ { type: 'title', level: 3, text: 'Privacy and local processing' },
123
+ {
124
+ type: 'paragraph',
125
+ html: 'All calculation happens in your browser. No text, code snippet or private data leaves your device.',
126
+ },
127
+ {
128
+ type: 'tip',
129
+ title: 'Google Fonts typefaces',
130
+ html: 'To measure a Google Fonts typeface accurately, first make sure it is loaded on the page or installed on your system. Otherwise the browser will fall back to a substitute font and the result will differ.',
131
+ },
132
+ ],
133
+ };
@@ -0,0 +1,133 @@
1
+ import type { ToolLocaleContent } from '../../../types';
2
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
3
+ import type { TextPixelCalculatorUI } from '../ui';
4
+
5
+ const faqData = [
6
+ {
7
+ question: '¿Cómo calcular el ancho de un texto en píxeles online?',
8
+ answer: 'Pega tu texto en el cuadro de entrada, configura la fuente y el tamaño, y la herramienta usará automáticamente la API de Canvas del navegador para devolverte el ancho exacto en píxeles.',
9
+ },
10
+ {
11
+ question: '¿Por qué el ancho en píxeles varía entre tipografías?',
12
+ answer: "La mayoría de las fuentes son proporcionales, lo que significa que cada carácter tiene un ancho distinto. Por ejemplo, una 'M' mayúscula siempre será más ancha que una 'i' minúscula en una fuente sans-serif estándar.",
13
+ },
14
+ {
15
+ question: '¿Es lo mismo medir caracteres que medir píxeles?',
16
+ answer: 'No. Medir caracteres te da la longitud de la cadena, mientras que medir píxeles te da el espacio visual que ocupa. Esto es crucial para asegurar que el texto no se salga de su contenedor en un diseño web.',
17
+ },
18
+ {
19
+ question: '¿Puedo usar cualquier fuente de Google Fonts?',
20
+ answer: 'Sí, siempre que la fuente esté instalada en tu sistema operativo o cargada en la página actual, la utilidad podrá medir sus dimensiones con precisión.',
21
+ },
22
+ {
23
+ question: '¿Es seguro procesar textos privados o fragmentos de código?',
24
+ answer: 'Sí. La calculadora funciona íntegramente de forma local. Ningún dato se envía a servidores externos, garantizando una privacidad absoluta.',
25
+ },
26
+ ];
27
+
28
+ const howToData = [
29
+ { name: 'Introducir el texto', text: 'Escribe o pega el texto que quieres medir en el área de entrada.' },
30
+ { name: 'Configurar la fuente', text: 'Indica la familia tipográfica, el tamaño en píxeles, el peso y si es itálica.' },
31
+ { name: 'Leer el resultado', text: 'El ancho en píxeles y el número de caracteres se actualizan en tiempo real.' },
32
+ { name: 'Copiar el valor', text: 'Pulsa "Copiar Ancho" para guardar el número de píxeles en el portapapeles.' },
33
+ ];
34
+
35
+ const faqSchema: WithContext<FAQPage> = {
36
+ '@context': 'https://schema.org',
37
+ '@type': 'FAQPage',
38
+ mainEntity: faqData.map((item) => ({
39
+ '@type': 'Question',
40
+ name: item.question,
41
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
42
+ })),
43
+ };
44
+
45
+ const howToSchema: WithContext<HowTo> = {
46
+ '@context': 'https://schema.org',
47
+ '@type': 'HowTo',
48
+ name: 'Cómo medir el ancho de texto en píxeles',
49
+ step: howToData.map((s) => ({ '@type': 'HowToStep', name: s.name, text: s.text })),
50
+ };
51
+
52
+ const appSchema: WithContext<SoftwareApplication> = {
53
+ '@context': 'https://schema.org',
54
+ '@type': 'SoftwareApplication',
55
+ name: 'Calculadora de Ancho de Texto en Píxeles',
56
+ applicationCategory: 'UtilitiesApplication',
57
+ operatingSystem: 'Web',
58
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
59
+ description: 'Calcula con precisión el ancho en píxeles de cualquier texto según su tipografía, tamaño y estilo.',
60
+ };
61
+
62
+ const ui: TextPixelCalculatorUI = {
63
+ textLabel: 'Texto a medir',
64
+ textPlaceholder: 'Escribe o pega aquí el texto que quieres medir...',
65
+ defaultText: 'Hola Mundo',
66
+ fontLabel: 'Tipografía',
67
+ sizeLabel: 'Tamaño (px)',
68
+ weightLabel: 'Peso',
69
+ italicLabel: 'Itálica',
70
+ widthLabel: 'Ancho (píxeles)',
71
+ charsLabel: 'Caracteres',
72
+ previewLabel: 'Vista previa visual',
73
+ copyBtn: 'Copiar Ancho',
74
+ resetBtn: 'Reiniciar',
75
+ copyDone: 'Ancho copiado',
76
+ };
77
+
78
+ export const content: ToolLocaleContent<TextPixelCalculatorUI> = {
79
+ slug: 'calculadora-ancho-pixel-texto',
80
+ title: 'Calculadora de Ancho de Texto en Píxeles',
81
+ description: 'Calcula con precisión cuánto ancho ocupa cualquier texto en píxeles según su tipografía, tamaño y estilo. Herramienta gratuita para diseñadores y desarrolladores.',
82
+ ui,
83
+ faqTitle: 'Preguntas Frecuentes',
84
+ faq: faqData,
85
+ howTo: howToData,
86
+ bibliographyTitle: 'Referencias Técnicas',
87
+ bibliography: [
88
+ { name: 'W3C: Gestión de la tipografía y el renderizado de texto (CSS Text Level 3)', url: 'https://www.w3.org/TR/css-text-3/' },
89
+ { name: 'Google Fonts: Entendiendo las fuentes variables', url: 'https://fonts.google.com/knowledge/glossary/variable_fonts' },
90
+ ],
91
+ schemas: [faqSchema, howToSchema, appSchema],
92
+ seo: [
93
+ { type: 'title', level: 2, text: 'Mide el ancho exacto de cualquier texto en píxeles' },
94
+ {
95
+ type: 'paragraph',
96
+ html: '¿Tu texto se sale del contenedor? ¿Necesitas saber cuánto espacio ocupa un titular antes de renderizarlo? La <strong>API Canvas del navegador</strong> permite medir el ancho exacto de cualquier cadena de texto con la precisión de un píxel, sin necesidad de renderizarlo en el DOM.',
97
+ },
98
+ { type: 'title', level: 3, text: 'Por qué contar caracteres no es suficiente' },
99
+ {
100
+ type: 'paragraph',
101
+ html: 'Las fuentes tipográficas modernas son <strong>proporcionales</strong>: cada glifo tiene un ancho diferente. Una "W" puede ocupar tres veces más que una "i". El número de caracteres no te dice nada sobre el espacio visual real que ocupa el texto.',
102
+ },
103
+ {
104
+ type: 'list',
105
+ items: [
106
+ '<strong>Diseño web:</strong> Evita desbordamientos en botones, etiquetas y celdas de tabla.',
107
+ '<strong>SEO:</strong> Los motores de búsqueda truncan los títulos por píxeles, no por caracteres.',
108
+ '<strong>Canvas y PDF:</strong> Calcula la posición exacta antes de dibujar texto programáticamente.',
109
+ '<strong>Tooltips y globos:</strong> Dimensiona dinámicamente los contenedores según el texto interior.',
110
+ ],
111
+ },
112
+ { type: 'title', level: 3, text: 'Cómo funciona la medición con Canvas' },
113
+ {
114
+ type: 'paragraph',
115
+ html: 'El método <code>ctx.measureText()</code> de la API Canvas devuelve un objeto <code>TextMetrics</code> con la propiedad <code>width</code>, que refleja el ancho en píxeles CSS con la fuente activa en ese momento. Esta herramienta configura el contexto con tu fuente, tamaño, peso y estilo antes de medir.',
116
+ },
117
+ {
118
+ type: 'code',
119
+ ariaLabel: 'Ejemplo de Canvas measureText',
120
+ code: "const ctx = document.createElement('canvas').getContext('2d');\nctx.font = '700 16px Inter, system-ui, sans-serif';\nconst width = ctx.measureText('Hola Mundo').width; // ej. 74.5",
121
+ },
122
+ { type: 'title', level: 3, text: 'Privacidad y procesamiento local' },
123
+ {
124
+ type: 'paragraph',
125
+ html: 'Todo el cálculo ocurre en tu navegador. Ningún texto, fragmento de código ni dato privado sale de tu dispositivo.',
126
+ },
127
+ {
128
+ type: 'tip',
129
+ title: 'Fuentes de Google Fonts',
130
+ html: 'Para medir una fuente de Google Fonts con precisión, primero asegúrate de que esté cargada en la página o instalada en tu sistema. De lo contrario el navegador usará la fuente de respaldo y el resultado variará.',
131
+ },
132
+ ],
133
+ };
@@ -0,0 +1,133 @@
1
+ import type { ToolLocaleContent } from '../../../types';
2
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
3
+ import type { TextPixelCalculatorUI } from '../ui';
4
+
5
+ const faqData = [
6
+ {
7
+ question: 'Comment calculer la largeur en pixels d\'un texte en ligne ?',
8
+ answer: 'Collez votre texte dans le champ de saisie, configurez la police et la taille, et l\'outil utilisera automatiquement l\'API Canvas du navigateur pour vous renvoyer la largeur exacte en pixels.',
9
+ },
10
+ {
11
+ question: 'Pourquoi la largeur en pixels varie-t-elle selon les polices ?',
12
+ answer: "La plupart des polices sont proportionnelles, ce qui signifie que chaque caractère a une largeur différente. Par exemple, un 'M' majuscule est toujours plus large qu'un 'i' minuscule dans une police sans-serif standard.",
13
+ },
14
+ {
15
+ question: 'Mesurer les caractères est-il identique à mesurer les pixels ?',
16
+ answer: 'Non. Mesurer les caractères donne la longueur de la chaîne, tandis que mesurer les pixels donne l\'espace visuel occupé. C\'est essentiel pour éviter que le texte ne déborde de son conteneur.',
17
+ },
18
+ {
19
+ question: 'Puis-je utiliser n\'importe quelle police Google Fonts ?',
20
+ answer: 'Oui, à condition que la police soit installée sur votre système ou chargée sur la page actuelle, l\'outil mesurera ses dimensions avec précision.',
21
+ },
22
+ {
23
+ question: 'Est-il sûr de traiter des textes privés ou des extraits de code ?',
24
+ answer: 'Oui. La calculatrice fonctionne entièrement en local. Aucune donnée n\'est envoyée à des serveurs externes, garantissant une confidentialité totale.',
25
+ },
26
+ ];
27
+
28
+ const howToData = [
29
+ { name: 'Saisir le texte', text: 'Tapez ou collez le texte que vous souhaitez mesurer dans la zone de saisie.' },
30
+ { name: 'Configurer la police', text: 'Indiquez la famille de polices, la taille en pixels, la graisse et si elle est en italique.' },
31
+ { name: 'Lire le résultat', text: 'La largeur en pixels et le nombre de caractères se mettent à jour en temps réel.' },
32
+ { name: 'Copier la valeur', text: 'Cliquez sur "Copier la largeur" pour enregistrer le nombre de pixels dans le presse-papiers.' },
33
+ ];
34
+
35
+ const faqSchema: WithContext<FAQPage> = {
36
+ '@context': 'https://schema.org',
37
+ '@type': 'FAQPage',
38
+ mainEntity: faqData.map((item) => ({
39
+ '@type': 'Question',
40
+ name: item.question,
41
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
42
+ })),
43
+ };
44
+
45
+ const howToSchema: WithContext<HowTo> = {
46
+ '@context': 'https://schema.org',
47
+ '@type': 'HowTo',
48
+ name: 'Comment mesurer la largeur d\'un texte en pixels',
49
+ step: howToData.map((s) => ({ '@type': 'HowToStep', name: s.name, text: s.text })),
50
+ };
51
+
52
+ const appSchema: WithContext<SoftwareApplication> = {
53
+ '@context': 'https://schema.org',
54
+ '@type': 'SoftwareApplication',
55
+ name: 'Calculateur de Largeur de Texte en Pixels',
56
+ applicationCategory: 'UtilitiesApplication',
57
+ operatingSystem: 'Web',
58
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
59
+ description: 'Calculez avec précision la largeur en pixels de n\'importe quel texte selon sa police, sa taille et son style.',
60
+ };
61
+
62
+ const ui: TextPixelCalculatorUI = {
63
+ textLabel: 'Texte à mesurer',
64
+ textPlaceholder: 'Saisissez ou collez ici le texte que vous souhaitez mesurer...',
65
+ defaultText: 'Bonjour Monde',
66
+ fontLabel: 'Police',
67
+ sizeLabel: 'Taille (px)',
68
+ weightLabel: 'Graisse',
69
+ italicLabel: 'Italique',
70
+ widthLabel: 'Largeur (pixels)',
71
+ charsLabel: 'Caractères',
72
+ previewLabel: 'Aperçu visuel',
73
+ copyBtn: 'Copier la largeur',
74
+ resetBtn: 'Réinitialiser',
75
+ copyDone: 'Largeur copiée',
76
+ };
77
+
78
+ export const content: ToolLocaleContent<TextPixelCalculatorUI> = {
79
+ slug: 'calculateur-largeur-pixel-texte',
80
+ title: 'Calculateur de Largeur de Texte en Pixels',
81
+ description: 'Calculez avec précision l\'espace horizontal occupé par votre texte en pixels. Outil gratuit pour designers et développeurs.',
82
+ ui,
83
+ faqTitle: 'Questions Fréquentes',
84
+ faq: faqData,
85
+ howTo: howToData,
86
+ bibliographyTitle: 'Références',
87
+ bibliography: [
88
+ { name: 'W3C : Module CSS Text niveau 3', url: 'https://www.w3.org/TR/css-text-3/' },
89
+ { name: 'Google Fonts : Comprendre les polices variables', url: 'https://fonts.google.com/knowledge/glossary/variable_fonts' },
90
+ ],
91
+ schemas: [faqSchema, howToSchema, appSchema],
92
+ seo: [
93
+ { type: 'title', level: 2, text: 'Mesurez la largeur exacte en pixels de n\'importe quel texte' },
94
+ {
95
+ type: 'paragraph',
96
+ html: 'Votre texte déborde-t-il de son conteneur ? Besoin de connaître l\'espace occupé par un titre avant de le rendre ? L\'<strong>API Canvas du navigateur</strong> permet de mesurer la largeur exacte de n\'importe quelle chaîne de texte avec une précision au pixel près, sans avoir à la rendre dans le DOM.',
97
+ },
98
+ { type: 'title', level: 3, text: 'Pourquoi compter les caractères ne suffit pas' },
99
+ {
100
+ type: 'paragraph',
101
+ html: 'Les polices modernes sont <strong>proportionnelles</strong> : chaque glyphe a une largeur différente. Un "W" peut occuper trois fois plus de place qu\'un "i". Le nombre de caractères ne vous dit rien sur l\'espace visuel réel occupé par le texte.',
102
+ },
103
+ {
104
+ type: 'list',
105
+ items: [
106
+ '<strong>Design web :</strong> Évitez les débordements dans les boutons, étiquettes et cellules de tableau.',
107
+ '<strong>SEO :</strong> Les moteurs de recherche tronquent les titres en pixels, pas en caractères.',
108
+ '<strong>Canvas et PDF :</strong> Calculez la position exacte avant de dessiner du texte par programmation.',
109
+ '<strong>Infobulles et bulles :</strong> Dimensionnez dynamiquement les conteneurs selon le texte intérieur.',
110
+ ],
111
+ },
112
+ { type: 'title', level: 3, text: 'Comment fonctionne la mesure avec Canvas' },
113
+ {
114
+ type: 'paragraph',
115
+ html: 'La méthode <code>ctx.measureText()</code> de l\'API Canvas retourne un objet <code>TextMetrics</code> avec une propriété <code>width</code> reflétant la largeur en pixels CSS avec la police active. Cet outil configure le contexte avec votre police, taille, graisse et style avant de mesurer.',
116
+ },
117
+ {
118
+ type: 'code',
119
+ ariaLabel: 'Exemple Canvas measureText',
120
+ code: "const ctx = document.createElement('canvas').getContext('2d');\nctx.font = '700 16px Inter, system-ui, sans-serif';\nconst width = ctx.measureText('Bonjour Monde').width; // ex. 74.5",
121
+ },
122
+ { type: 'title', level: 3, text: 'Confidentialité et traitement local' },
123
+ {
124
+ type: 'paragraph',
125
+ html: 'Tout le calcul s\'effectue dans votre navigateur. Aucun texte, extrait de code ni donnée privée ne quitte votre appareil.',
126
+ },
127
+ {
128
+ type: 'tip',
129
+ title: 'Polices Google Fonts',
130
+ html: 'Pour mesurer une police Google Fonts avec précision, assurez-vous qu\'elle est chargée sur la page ou installée sur votre système. Sinon le navigateur utilisera une police de substitution et le résultat variera.',
131
+ },
132
+ ],
133
+ };
@@ -0,0 +1,22 @@
1
+ import type { ToolDefinition, ToolsToolEntry } from '../../types';
2
+ import type { TextPixelCalculatorUI } from './ui';
3
+ import TextPixelCalculatorComponent from './component.astro';
4
+ import TextPixelCalculatorSEO from './seo.astro';
5
+ import TextPixelCalculatorBibliography from './bibliography.astro';
6
+
7
+ export const textPixelCalculator: ToolsToolEntry<TextPixelCalculatorUI> = {
8
+ id: 'text-pixel-calculator',
9
+ icons: { bg: 'mdi:format-text', fg: 'mdi:ruler' },
10
+ i18n: {
11
+ es: () => import('./i18n/es').then((m) => m.content),
12
+ en: () => import('./i18n/en').then((m) => m.content),
13
+ fr: () => import('./i18n/fr').then((m) => m.content),
14
+ },
15
+ };
16
+
17
+ export const TEXT_PIXEL_CALCULATOR_TOOL: ToolDefinition = {
18
+ entry: textPixelCalculator,
19
+ Component: TextPixelCalculatorComponent,
20
+ SEOComponent: TextPixelCalculatorSEO,
21
+ BibliographyComponent: TextPixelCalculatorBibliography,
22
+ };
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { textPixelCalculator } 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 textPixelCalculator.i18n[locale]?.();
12
+ ---
13
+
14
+ {content && <SEORenderer content={{ locale, sections: content.seo }} />}
@@ -0,0 +1,15 @@
1
+ export interface TextPixelCalculatorUI extends Record<string, string> {
2
+ textLabel: string;
3
+ textPlaceholder: string;
4
+ defaultText: string;
5
+ fontLabel: string;
6
+ sizeLabel: string;
7
+ weightLabel: string;
8
+ italicLabel: string;
9
+ widthLabel: string;
10
+ charsLabel: string;
11
+ previewLabel: string;
12
+ copyBtn: string;
13
+ resetBtn: string;
14
+ copyDone: string;
15
+ }
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
3
+ import { whatsappLink } 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 whatsappLink.i18n[locale]?.();
12
+ ---
13
+
14
+ {content && <SharedBibliography links={content.bibliography} />}