@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.
- package/package.json +63 -0
- package/src/category/i18n/en.ts +172 -0
- package/src/category/i18n/es.ts +172 -0
- package/src/category/i18n/fr.ts +172 -0
- package/src/category/index.ts +23 -0
- package/src/category/seo.astro +15 -0
- package/src/components/PreviewNavSidebar.astro +116 -0
- package/src/components/PreviewToolbar.astro +143 -0
- package/src/data.ts +11 -0
- package/src/env.d.ts +5 -0
- package/src/index.ts +90 -0
- package/src/layouts/PreviewLayout.astro +117 -0
- package/src/pages/[locale]/[slug].astro +146 -0
- package/src/pages/[locale].astro +251 -0
- package/src/pages/index.astro +4 -0
- package/src/tests/faq_count.test.ts +19 -0
- package/src/tests/locale_completeness.test.ts +42 -0
- package/src/tests/mocks/astro_mock.js +2 -0
- package/src/tests/no_h1_in_components.test.ts +48 -0
- package/src/tests/schemas_fulfillment.test.ts +23 -0
- package/src/tests/seo_length.test.ts +23 -0
- package/src/tests/title_quality.test.ts +56 -0
- package/src/tests/tool_validation.test.ts +17 -0
- package/src/tool/date-diff-calculator/bibliography.astro +14 -0
- package/src/tool/date-diff-calculator/component.astro +370 -0
- package/src/tool/date-diff-calculator/i18n/en.ts +132 -0
- package/src/tool/date-diff-calculator/i18n/es.ts +132 -0
- package/src/tool/date-diff-calculator/i18n/fr.ts +132 -0
- package/src/tool/date-diff-calculator/index.ts +22 -0
- package/src/tool/date-diff-calculator/seo.astro +14 -0
- package/src/tool/date-diff-calculator/ui.ts +17 -0
- package/src/tool/drive-direct-link/bibliography.astro +14 -0
- package/src/tool/drive-direct-link/component.astro +280 -0
- package/src/tool/drive-direct-link/i18n/en.ts +118 -0
- package/src/tool/drive-direct-link/i18n/es.ts +118 -0
- package/src/tool/drive-direct-link/i18n/fr.ts +118 -0
- package/src/tool/drive-direct-link/index.ts +22 -0
- package/src/tool/drive-direct-link/seo.astro +14 -0
- package/src/tool/drive-direct-link/ui.ts +10 -0
- package/src/tool/email-list-cleaner/bibliography.astro +14 -0
- package/src/tool/email-list-cleaner/component.astro +375 -0
- package/src/tool/email-list-cleaner/i18n/en.ts +140 -0
- package/src/tool/email-list-cleaner/i18n/es.ts +140 -0
- package/src/tool/email-list-cleaner/i18n/fr.ts +140 -0
- package/src/tool/email-list-cleaner/index.ts +22 -0
- package/src/tool/email-list-cleaner/seo.astro +14 -0
- package/src/tool/email-list-cleaner/ui.ts +15 -0
- package/src/tool/env-badge-spain/bibliography.astro +14 -0
- package/src/tool/env-badge-spain/component.astro +303 -0
- package/src/tool/env-badge-spain/components/BadgeForm.astro +243 -0
- package/src/tool/env-badge-spain/components/BadgeResult.astro +151 -0
- package/src/tool/env-badge-spain/i18n/en.ts +153 -0
- package/src/tool/env-badge-spain/i18n/es.ts +153 -0
- package/src/tool/env-badge-spain/i18n/fr.ts +153 -0
- package/src/tool/env-badge-spain/index.ts +22 -0
- package/src/tool/env-badge-spain/seo.astro +14 -0
- package/src/tool/env-badge-spain/ui.ts +53 -0
- package/src/tool/morse-beacon/bibliography.astro +14 -0
- package/src/tool/morse-beacon/component.astro +534 -0
- package/src/tool/morse-beacon/i18n/en.ts +157 -0
- package/src/tool/morse-beacon/i18n/es.ts +157 -0
- package/src/tool/morse-beacon/i18n/fr.ts +157 -0
- package/src/tool/morse-beacon/index.ts +22 -0
- package/src/tool/morse-beacon/logic/MorseEngine.ts +124 -0
- package/src/tool/morse-beacon/seo.astro +14 -0
- package/src/tool/morse-beacon/ui.ts +18 -0
- package/src/tool/password-generator/bibliography.astro +14 -0
- package/src/tool/password-generator/component.astro +259 -0
- package/src/tool/password-generator/components/Config.astro +227 -0
- package/src/tool/password-generator/components/Display.astro +147 -0
- package/src/tool/password-generator/components/Strength.astro +70 -0
- package/src/tool/password-generator/i18n/en.ts +166 -0
- package/src/tool/password-generator/i18n/es.ts +166 -0
- package/src/tool/password-generator/i18n/fr.ts +166 -0
- package/src/tool/password-generator/index.ts +22 -0
- package/src/tool/password-generator/seo.astro +14 -0
- package/src/tool/password-generator/ui.ts +16 -0
- package/src/tool/routes/bibliography.astro +14 -0
- package/src/tool/routes/component.astro +543 -0
- package/src/tool/routes/i18n/en.ts +157 -0
- package/src/tool/routes/i18n/es.ts +157 -0
- package/src/tool/routes/i18n/fr.ts +157 -0
- package/src/tool/routes/index.ts +22 -0
- package/src/tool/routes/logic/GeocodingService.ts +60 -0
- package/src/tool/routes/logic/RouteManager.ts +192 -0
- package/src/tool/routes/logic/RouteService.ts +66 -0
- package/src/tool/routes/seo.astro +14 -0
- package/src/tool/routes/ui.ts +16 -0
- package/src/tool/rule-of-three/bibliography.astro +14 -0
- package/src/tool/rule-of-three/component.astro +369 -0
- package/src/tool/rule-of-three/i18n/en.ts +171 -0
- package/src/tool/rule-of-three/i18n/es.ts +171 -0
- package/src/tool/rule-of-three/i18n/fr.ts +171 -0
- package/src/tool/rule-of-three/index.ts +22 -0
- package/src/tool/rule-of-three/seo.astro +14 -0
- package/src/tool/rule-of-three/ui.ts +13 -0
- package/src/tool/seo-content-optimizer/bibliography.astro +14 -0
- package/src/tool/seo-content-optimizer/component.astro +552 -0
- package/src/tool/seo-content-optimizer/i18n/en.ts +136 -0
- package/src/tool/seo-content-optimizer/i18n/es.ts +136 -0
- package/src/tool/seo-content-optimizer/i18n/fr.ts +136 -0
- package/src/tool/seo-content-optimizer/index.ts +22 -0
- package/src/tool/seo-content-optimizer/seo.astro +14 -0
- package/src/tool/seo-content-optimizer/ui.ts +29 -0
- package/src/tool/speed-reader/bibliography.astro +14 -0
- package/src/tool/speed-reader/component.astro +586 -0
- package/src/tool/speed-reader/i18n/en.ts +152 -0
- package/src/tool/speed-reader/i18n/es.ts +152 -0
- package/src/tool/speed-reader/i18n/fr.ts +152 -0
- package/src/tool/speed-reader/index.ts +22 -0
- package/src/tool/speed-reader/logic/RSVPEngine.ts +106 -0
- package/src/tool/speed-reader/seo.astro +14 -0
- package/src/tool/speed-reader/ui.ts +14 -0
- package/src/tool/text-pixel-calculator/bibliography.astro +14 -0
- package/src/tool/text-pixel-calculator/component.astro +315 -0
- package/src/tool/text-pixel-calculator/components/Editor.astro +240 -0
- package/src/tool/text-pixel-calculator/components/Preview.astro +155 -0
- package/src/tool/text-pixel-calculator/components/Stats.astro +87 -0
- package/src/tool/text-pixel-calculator/i18n/en.ts +133 -0
- package/src/tool/text-pixel-calculator/i18n/es.ts +133 -0
- package/src/tool/text-pixel-calculator/i18n/fr.ts +133 -0
- package/src/tool/text-pixel-calculator/index.ts +22 -0
- package/src/tool/text-pixel-calculator/seo.astro +14 -0
- package/src/tool/text-pixel-calculator/ui.ts +15 -0
- package/src/tool/whatsapp-link/bibliography.astro +14 -0
- package/src/tool/whatsapp-link/component.astro +455 -0
- package/src/tool/whatsapp-link/i18n/en.ts +128 -0
- package/src/tool/whatsapp-link/i18n/es.ts +128 -0
- package/src/tool/whatsapp-link/i18n/fr.ts +128 -0
- package/src/tool/whatsapp-link/index.ts +22 -0
- package/src/tool/whatsapp-link/seo.astro +14 -0
- package/src/tool/whatsapp-link/ui.ts +15 -0
- package/src/tools.ts +15 -0
- package/src/types.ts +72 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import type { ToolLocaleContent } from '../../../types';
|
|
2
|
+
import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
3
|
+
import type { MorseBeaconUI } from '../ui';
|
|
4
|
+
|
|
5
|
+
const faqData = [
|
|
6
|
+
{
|
|
7
|
+
question: '¿Cuál es la señal de socorro SOS en código Morse?',
|
|
8
|
+
answer: "La señal es '... --- ...' (tres puntos, tres rayas, tres puntos). Se transmite de forma continua sin espacios entre las letras para indicar una emergencia inmediata.",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
question: '¿Por qué no funciona el flash en mi navegador?',
|
|
12
|
+
answer: 'La activación del flash (Torch) requiere que el navegador tenga permisos de cámara. Algunos navegadores móviles o versiones antiguas de escritorio no soportan esta API por motivos de privacidad.',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
question: '¿Es visible la señal de SOS de la pantalla?',
|
|
16
|
+
answer: 'Sí, en condiciones de oscuridad absoluta, el brillo máximo de una pantalla blanca parpadeando en Morse puede verse a varios cientos de metros de distancia, siendo una alternativa útil si el flash falla.',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
question: '¿Qué es el código Morse internacional?',
|
|
20
|
+
answer: 'Es un sistema de comunicación que utiliza secuencias de señales cortas (puntos) y largas (rayas) para representar letras y números, estandarizado por la UIT para comunicaciones de radio y señales ópticas.',
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const howToData = [
|
|
25
|
+
{
|
|
26
|
+
name: 'Escribir el mensaje',
|
|
27
|
+
text: "Introduce el texto que quieres transmitir o utiliza el botón preconfigurado de 'SOS' para emergencias.",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'Configurar la velocidad',
|
|
31
|
+
text: 'Ajusta los WPM (palabras por minuto) para que la señal sea más rápida o más lenta según la visibilidad.',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'Elegir fuente de luz',
|
|
35
|
+
text: 'Activa el parpadeo de la pantalla completa o permite el acceso al flash de la cámara para una señal más potente.',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'Iniciar transmisión',
|
|
39
|
+
text: 'Pulsa Transmitir para que el sistema convierta el texto en impulsos visuales automáticos siguiendo el estándar Morse.',
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const faqSchema: WithContext<FAQPage> = {
|
|
44
|
+
'@context': 'https://schema.org',
|
|
45
|
+
'@type': 'FAQPage',
|
|
46
|
+
mainEntity: faqData.map((item) => ({
|
|
47
|
+
'@type': 'Question',
|
|
48
|
+
name: item.question,
|
|
49
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
50
|
+
})),
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const howToSchema: WithContext<HowTo> = {
|
|
54
|
+
'@context': 'https://schema.org',
|
|
55
|
+
'@type': 'HowTo',
|
|
56
|
+
name: 'Cómo usar la baliza Morse para transmitir mensajes',
|
|
57
|
+
step: howToData.map((s) => ({ '@type': 'HowToStep', name: s.name, text: s.text })),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const appSchema: WithContext<SoftwareApplication> = {
|
|
61
|
+
'@context': 'https://schema.org',
|
|
62
|
+
'@type': 'SoftwareApplication',
|
|
63
|
+
name: 'Baliza Morse: Transmisor Táctico SOS',
|
|
64
|
+
applicationCategory: 'UtilitiesApplication',
|
|
65
|
+
operatingSystem: 'Web',
|
|
66
|
+
offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
|
|
67
|
+
description: 'Convierte tu dispositivo en una estación de transmisión Morse. Utiliza flash y pantalla como señales de luz de emergencia y comunicación táctica.',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const ui: MorseBeaconUI = {
|
|
71
|
+
labelMessage: 'Mensaje a Transmitir',
|
|
72
|
+
placeholder: 'Escribe aquí tu mensaje (SOS, HOLA, AYUDA)...',
|
|
73
|
+
btnTransmit: 'Transmitir',
|
|
74
|
+
btnSosLoop: 'Bucle SOS',
|
|
75
|
+
btnStop: 'Detener',
|
|
76
|
+
labelTorch: 'Flash/Linterna',
|
|
77
|
+
statusStandby: 'STANDBY',
|
|
78
|
+
statusTransmitting: 'TRANSMITIENDO',
|
|
79
|
+
statusStopping: 'DETENIENDO...',
|
|
80
|
+
statusWaiting: 'EN ESPERA',
|
|
81
|
+
statusReady: 'HARDWARE LISTO',
|
|
82
|
+
statusNoTorch: 'SIN LINTERNA',
|
|
83
|
+
statusNoPermission: 'SIN PERMISO',
|
|
84
|
+
statusNotSupported: 'NO SOPORTADO',
|
|
85
|
+
statusReqHttps: 'REQ. HTTPS',
|
|
86
|
+
statusSending: 'ENVIANDO:',
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const content: ToolLocaleContent<MorseBeaconUI> = {
|
|
90
|
+
slug: 'baliza-morse',
|
|
91
|
+
title: 'Baliza Morse: Transmisor Táctico SOS',
|
|
92
|
+
description: 'Convierte tu dispositivo en una estación de transmisión Morse. Utiliza flash y pantalla como señales de luz de emergencia y comunicación táctica.',
|
|
93
|
+
ui,
|
|
94
|
+
faqTitle: 'Preguntas Frecuentes',
|
|
95
|
+
faq: faqData,
|
|
96
|
+
howTo: howToData,
|
|
97
|
+
bibliographyTitle: 'Referencias',
|
|
98
|
+
bibliography: [
|
|
99
|
+
{ name: 'ITU-R M.1677-1 — International Morse Code', url: 'https://www.itu.int/dms_pubrec/itu-r/rec/m/R-REC-M.1677-1-200910-I!!PDF-E.pdf' },
|
|
100
|
+
{ name: 'W3C MediaCapture Image — Torch', url: 'https://w3c.github.io/mediacapture-image/#torch' },
|
|
101
|
+
{ name: 'Código Morse — Wikipedia', url: 'https://es.wikipedia.org/wiki/C%C3%B3digo_Morse' },
|
|
102
|
+
],
|
|
103
|
+
schemas: [faqSchema, howToSchema, appSchema],
|
|
104
|
+
seo: [
|
|
105
|
+
{
|
|
106
|
+
type: 'title',
|
|
107
|
+
text: 'El Lenguaje de la Luz',
|
|
108
|
+
level: 2,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
type: 'paragraph',
|
|
112
|
+
html: 'Esta herramienta convierte tu dispositivo en una baliza de señalización óptica capaz de transmitir mensajes visibles a kilómetros de distancia. Utilizando el estándar internacional del Código Morse, permite la comunicación silenciosa o de emergencia mediante pulsos de luz (linterna y pantalla).',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
type: 'comparative',
|
|
116
|
+
columns: 2,
|
|
117
|
+
items: [
|
|
118
|
+
{
|
|
119
|
+
icon: 'mdi:history',
|
|
120
|
+
title: 'Un Estándar Universal',
|
|
121
|
+
description: 'Desarrollado en 1830 por Samuel Morse, este sistema binario de puntos y rayas revolucionó las telecomunicaciones. Su simplicidad lo hace extremadamente robusto: puede transmitirse por sonido, radio, electricidad o luz, siendo legible incluso con interferencias severas.',
|
|
122
|
+
points: [],
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
icon: 'mdi:flash-alert',
|
|
126
|
+
title: 'Modo SOS de Emergencia',
|
|
127
|
+
description: "El botón 'Bucle SOS' transmite continuamente la secuencia ··· --- ···. Esta señal es reconocida mundialmente como petición de auxilio y, gracias al alto contraste del flash LED, es visible a gran distancia incluso a plena luz del día en ciertas condiciones.",
|
|
128
|
+
points: [],
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
type: 'title',
|
|
134
|
+
text: 'Estándar ITU-R M.1677-1',
|
|
135
|
+
level: 2,
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
type: 'paragraph',
|
|
139
|
+
html: 'Esta utilidad respeta estrictamente los <strong>tiempos reglamentarios</strong> del código Morse internacional definidos por la Unión Internacional de Telecomunicaciones.',
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
type: 'list',
|
|
143
|
+
items: [
|
|
144
|
+
'1 punto = 1 unidad de tiempo',
|
|
145
|
+
'1 raya = 3 unidades de tiempo',
|
|
146
|
+
'Espacio entre elementos = 1 unidad',
|
|
147
|
+
'Espacio entre letras = 3 unidades',
|
|
148
|
+
'Espacio entre palabras = 7 unidades',
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
type: 'tip',
|
|
153
|
+
title: 'Velocidad estándar',
|
|
154
|
+
html: 'La velocidad por defecto es de <strong>15 WPM</strong> (palabras por minuto), que corresponde a un ritmo de transmisión profesional. A 15 WPM, 1 unidad = 80 ms.',
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import type { ToolLocaleContent } from '../../../types';
|
|
2
|
+
import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
3
|
+
import type { MorseBeaconUI } from '../ui';
|
|
4
|
+
|
|
5
|
+
const faqData = [
|
|
6
|
+
{
|
|
7
|
+
question: 'Quel est le signal de détresse SOS en code Morse ?',
|
|
8
|
+
answer: "Le signal est '... --- ...' (trois points, trois traits, trois points). Il est transmis en continu sans espaces entre les lettres pour indiquer une urgence immédiate.",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
question: 'Pourquoi la torche ne fonctionne-t-elle pas dans mon navigateur ?',
|
|
12
|
+
answer: "L'activation de la torche nécessite que le navigateur dispose des autorisations de la caméra. Certains navigateurs mobiles ou les anciennes versions de bureau ne prennent pas en charge cette API pour des raisons de confidentialité.",
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
question: "Le signal SOS de l'écran est-il visible ?",
|
|
16
|
+
answer: "Oui, dans des conditions d'obscurité totale, la luminosité maximale d'un écran blanc clignotant en Morse peut être vue à plusieurs centaines de mètres, ce qui en fait une alternative utile si la torche est défaillante.",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
question: "Qu'est-ce que le code Morse international ?",
|
|
20
|
+
answer: "C'est un système de communication qui utilise des séquences de signaux courts (points) et longs (traits) pour représenter des lettres et des chiffres, normalisé par l'UIT pour les communications radio et les signaux optiques.",
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const howToData = [
|
|
25
|
+
{
|
|
26
|
+
name: 'Écrire le message',
|
|
27
|
+
text: "Saisissez le texte que vous souhaitez transmettre ou utilisez le bouton préconfiguré 'SOS' pour les urgences.",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'Configurer la vitesse',
|
|
31
|
+
text: 'Ajustez les MPM (mots par minute) pour rendre le signal plus rapide ou plus lent selon la visibilité.',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'Choisir la source de lumière',
|
|
35
|
+
text: "Activez le clignotement en plein écran ou autorisez l'accès à la torche de l'appareil photo pour un signal plus puissant.",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'Démarrer la transmission',
|
|
39
|
+
text: 'Appuyez sur Transmettre pour que le système convertisse le texte en impulsions visuelles automatiques selon la norme Morse.',
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const faqSchema: WithContext<FAQPage> = {
|
|
44
|
+
'@context': 'https://schema.org',
|
|
45
|
+
'@type': 'FAQPage',
|
|
46
|
+
mainEntity: faqData.map((item) => ({
|
|
47
|
+
'@type': 'Question',
|
|
48
|
+
name: item.question,
|
|
49
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
50
|
+
})),
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const howToSchema: WithContext<HowTo> = {
|
|
54
|
+
'@context': 'https://schema.org',
|
|
55
|
+
'@type': 'HowTo',
|
|
56
|
+
name: 'Comment utiliser la balise Morse pour transmettre des messages',
|
|
57
|
+
step: howToData.map((s) => ({ '@type': 'HowToStep', name: s.name, text: s.text })),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const appSchema: WithContext<SoftwareApplication> = {
|
|
61
|
+
'@context': 'https://schema.org',
|
|
62
|
+
'@type': 'SoftwareApplication',
|
|
63
|
+
name: 'Balise Morse : Émetteur tactique SOS',
|
|
64
|
+
applicationCategory: 'UtilitiesApplication',
|
|
65
|
+
operatingSystem: 'Web',
|
|
66
|
+
offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
|
|
67
|
+
description: 'Transformez votre appareil en station d\'émission Morse. Utilisez le flash et l\'écran comme signaux lumineux d\'urgence et de communication tactique.',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const ui: MorseBeaconUI = {
|
|
71
|
+
labelMessage: 'Message à transmettre',
|
|
72
|
+
placeholder: 'Tapez votre message ici (SOS, BONJOUR, AIDE)...',
|
|
73
|
+
btnTransmit: 'Transmettre',
|
|
74
|
+
btnSosLoop: 'Boucle SOS',
|
|
75
|
+
btnStop: 'Arrêter',
|
|
76
|
+
labelTorch: 'Flash/Lampe',
|
|
77
|
+
statusStandby: 'EN VEILLE',
|
|
78
|
+
statusTransmitting: 'TRANSMISSION',
|
|
79
|
+
statusStopping: 'ARRÊT...',
|
|
80
|
+
statusWaiting: 'EN ATTENTE',
|
|
81
|
+
statusReady: 'MATÉRIEL PRÊT',
|
|
82
|
+
statusNoTorch: 'SANS LAMPE',
|
|
83
|
+
statusNoPermission: 'SANS PERMISSION',
|
|
84
|
+
statusNotSupported: 'NON SUPPORTÉ',
|
|
85
|
+
statusReqHttps: 'REQ. HTTPS',
|
|
86
|
+
statusSending: 'ENVOI :',
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const content: ToolLocaleContent<MorseBeaconUI> = {
|
|
90
|
+
slug: 'balise-morse',
|
|
91
|
+
title: 'Balise Morse : Émetteur tactique SOS',
|
|
92
|
+
description: "Transformez votre appareil en station d'émission Morse. Utilisez le flash et l'écran comme signaux lumineux d'urgence et de communication tactique.",
|
|
93
|
+
ui,
|
|
94
|
+
faqTitle: 'Questions fréquentes',
|
|
95
|
+
faq: faqData,
|
|
96
|
+
howTo: howToData,
|
|
97
|
+
bibliographyTitle: 'Références',
|
|
98
|
+
bibliography: [
|
|
99
|
+
{ name: 'ITU-R M.1677-1 — Code Morse international', url: 'https://www.itu.int/dms_pubrec/itu-r/rec/m/R-REC-M.1677-1-200910-I!!PDF-E.pdf' },
|
|
100
|
+
{ name: 'W3C MediaCapture Image — Torch', url: 'https://w3c.github.io/mediacapture-image/#torch' },
|
|
101
|
+
{ name: 'Code Morse — Wikipédia', url: 'https://fr.wikipedia.org/wiki/Code_Morse_international' },
|
|
102
|
+
],
|
|
103
|
+
schemas: [faqSchema, howToSchema, appSchema],
|
|
104
|
+
seo: [
|
|
105
|
+
{
|
|
106
|
+
type: 'title',
|
|
107
|
+
text: 'Le langage de la lumière',
|
|
108
|
+
level: 2,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
type: 'paragraph',
|
|
112
|
+
html: "Cet outil transforme votre appareil en balise de signalisation optique capable de transmettre des messages visibles à des kilomètres de distance. En utilisant la norme internationale du code Morse, il permet une communication silencieuse ou d'urgence par impulsions lumineuses (lampe de poche et écran).",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
type: 'comparative',
|
|
116
|
+
columns: 2,
|
|
117
|
+
items: [
|
|
118
|
+
{
|
|
119
|
+
icon: 'mdi:history',
|
|
120
|
+
title: 'Une norme universelle',
|
|
121
|
+
description: "Développé en 1830 par Samuel Morse, ce système binaire de points et de traits a révolutionné les télécommunications. Sa simplicité le rend extrêmement robuste : il peut être transmis par le son, la radio, l'électricité ou la lumière, et reste lisible même avec de fortes interférences.",
|
|
122
|
+
points: [],
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
icon: 'mdi:flash-alert',
|
|
126
|
+
title: "Mode d'urgence SOS",
|
|
127
|
+
description: "Le bouton 'Boucle SOS' transmet en continu la séquence ··· --- ···. Ce signal est universellement reconnu comme appel de détresse et, grâce au contraste élevé du flash LED, est visible à grande distance même en plein jour dans certaines conditions.",
|
|
128
|
+
points: [],
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
type: 'title',
|
|
134
|
+
text: 'Norme ITU-R M.1677-1',
|
|
135
|
+
level: 2,
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
type: 'paragraph',
|
|
139
|
+
html: "Cet outil respecte strictement les <strong>délais réglementaires</strong> du code Morse international tels que définis par l'Union internationale des télécommunications.",
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
type: 'list',
|
|
143
|
+
items: [
|
|
144
|
+
'1 point = 1 unité de temps',
|
|
145
|
+
'1 trait = 3 unités de temps',
|
|
146
|
+
'Espace entre éléments = 1 unité',
|
|
147
|
+
'Espace entre lettres = 3 unités',
|
|
148
|
+
'Espace entre mots = 7 unités',
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
type: 'tip',
|
|
153
|
+
title: 'Vitesse standard',
|
|
154
|
+
html: 'La vitesse par défaut est de <strong>15 MPM</strong> (mots par minute), ce qui correspond à un rythme de transmission professionnel. À 15 MPM, 1 unité = 80 ms.',
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ToolDefinition, ToolsToolEntry } from '../../types';
|
|
2
|
+
import type { MorseBeaconUI } from './ui';
|
|
3
|
+
import MorseBeaconComponent from './component.astro';
|
|
4
|
+
import MorseBeaconSEO from './seo.astro';
|
|
5
|
+
import MorseBeaconBibliography from './bibliography.astro';
|
|
6
|
+
|
|
7
|
+
export const morseBeacon: ToolsToolEntry<MorseBeaconUI> = {
|
|
8
|
+
id: 'morse-beacon',
|
|
9
|
+
icons: { bg: 'mdi:transmission-tower', fg: 'mdi:dots-horizontal' },
|
|
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 MORSE_BEACON_TOOL: ToolDefinition = {
|
|
18
|
+
entry: morseBeacon,
|
|
19
|
+
Component: MorseBeaconComponent,
|
|
20
|
+
SEOComponent: MorseBeaconSEO,
|
|
21
|
+
BibliographyComponent: MorseBeaconBibliography,
|
|
22
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
export type MorseSignal = '.' | '-' | ' ';
|
|
2
|
+
|
|
3
|
+
export const MORSE_DICTIONARY: Record<string, string> = {
|
|
4
|
+
A: '.-', B: '-...', C: '-.-.', D: '-..', E: '.', F: '..-.', G: '--.', H: '....',
|
|
5
|
+
I: '..', J: '.---', K: '-.-', L: '.-..', M: '--', N: '-.', O: '---', P: '.--.',
|
|
6
|
+
Q: '--.-', R: '.-.', S: '...', T: '-', U: '..-', V: '...-', W: '.--', X: '-..-',
|
|
7
|
+
Y: '-.--', Z: '--..',
|
|
8
|
+
'1': '.----', '2': '..---', '3': '...--', '4': '....-', '5': '.....',
|
|
9
|
+
'6': '-....', '7': '--...', '8': '---..', '9': '----.', '0': '-----',
|
|
10
|
+
'.': '.-.-.-', ',': '--..--', '?': '..--..', "'": '.----.', '!': '-.-.--',
|
|
11
|
+
'/': '-..-.', '(': '-.--.', ')': '-.--.-', '&': '.-...', ':': '---...',
|
|
12
|
+
';': '-.-.-.', '=': '-...-', '+': '.-.-.', '-': '-....-', _: '..--.-',
|
|
13
|
+
'"': '.-..-.', $: '...-..-', '@': '.--.-.',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type MorseBitType = 'dot' | 'dash' | 'gap' | 'charSpace' | 'wordSpace';
|
|
17
|
+
|
|
18
|
+
export class MorseBit {
|
|
19
|
+
type: MorseBitType;
|
|
20
|
+
duration: number;
|
|
21
|
+
isActive: boolean;
|
|
22
|
+
|
|
23
|
+
constructor(type: MorseBitType, unit: number) {
|
|
24
|
+
this.type = type;
|
|
25
|
+
this.isActive = type === 'dot' || type === 'dash';
|
|
26
|
+
|
|
27
|
+
const durations: Record<MorseBitType, number> = {
|
|
28
|
+
dot: unit, dash: unit * 3, gap: unit, charSpace: unit * 3, wordSpace: unit * 7,
|
|
29
|
+
};
|
|
30
|
+
this.duration = durations[type];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class MorseEngine {
|
|
35
|
+
private unit: number;
|
|
36
|
+
private isPlaying = false;
|
|
37
|
+
private stopSignal = false;
|
|
38
|
+
private onStateChange: (active: boolean, bit: MorseBit | null) => void;
|
|
39
|
+
|
|
40
|
+
constructor(wpm = 15, onStateChange: (active: boolean, bit: MorseBit | null) => void) {
|
|
41
|
+
this.unit = 1200 / wpm;
|
|
42
|
+
this.onStateChange = onStateChange;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static textToMorse(text: string): string {
|
|
46
|
+
return text.toUpperCase().split('').map((char) => {
|
|
47
|
+
if (char === ' ') return '/';
|
|
48
|
+
return MORSE_DICTIONARY[char] ?? '?';
|
|
49
|
+
}).join(' ');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private static compileCharBits(bits: string[], unit: number, addSpace: boolean): MorseBit[] {
|
|
53
|
+
const result: MorseBit[] = [];
|
|
54
|
+
for (let j = 0; j < bits.length; j++) {
|
|
55
|
+
const bit = bits[j];
|
|
56
|
+
if (bit === '.') result.push(new MorseBit('dot', unit));
|
|
57
|
+
if (bit === '-') result.push(new MorseBit('dash', unit));
|
|
58
|
+
if (j < bits.length - 1) result.push(new MorseBit('gap', unit));
|
|
59
|
+
}
|
|
60
|
+
if (addSpace) result.push(new MorseBit('charSpace', unit));
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static compileSequence(text: string, wpm: number): MorseBit[] {
|
|
65
|
+
const unit = 1200 / wpm;
|
|
66
|
+
const sequence: MorseBit[] = [];
|
|
67
|
+
const normalized = text.toUpperCase();
|
|
68
|
+
|
|
69
|
+
for (let i = 0; i < normalized.length; i++) {
|
|
70
|
+
const char = normalized[i];
|
|
71
|
+
if (!char) continue;
|
|
72
|
+
|
|
73
|
+
if (char === ' ') {
|
|
74
|
+
sequence.push(new MorseBit('wordSpace', unit));
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const code = MORSE_DICTIONARY[char];
|
|
79
|
+
if (!code) continue;
|
|
80
|
+
|
|
81
|
+
const nextChar = normalized[i + 1];
|
|
82
|
+
const addSpace = i < normalized.length - 1 && nextChar !== ' ';
|
|
83
|
+
sequence.push(...MorseEngine.compileCharBits(code.split(''), unit, addSpace));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return sequence;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
stop() { this.stopSignal = true; }
|
|
90
|
+
|
|
91
|
+
private async playBit(bit: MorseBit): Promise<void> {
|
|
92
|
+
this.onStateChange(bit.isActive, bit);
|
|
93
|
+
if (bit.isActive && navigator.vibrate) navigator.vibrate(bit.duration);
|
|
94
|
+
await this.wait(bit.duration);
|
|
95
|
+
if (bit.isActive) this.onStateChange(false, null);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private async playOnce(sequence: MorseBit[]): Promise<void> {
|
|
99
|
+
for (const bit of sequence) {
|
|
100
|
+
if (this.stopSignal) break;
|
|
101
|
+
await this.playBit(bit);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async transmit(sequence: MorseBit[], loop = false): Promise<void> {
|
|
106
|
+
if (this.isPlaying) return;
|
|
107
|
+
this.isPlaying = true;
|
|
108
|
+
this.stopSignal = false;
|
|
109
|
+
|
|
110
|
+
do {
|
|
111
|
+
await this.playOnce(sequence);
|
|
112
|
+
if (loop && !this.stopSignal) await this.wait(this.unit * 7);
|
|
113
|
+
} while (loop && !this.stopSignal);
|
|
114
|
+
|
|
115
|
+
this.isPlaying = false;
|
|
116
|
+
this.onStateChange(false, null);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private wait(ms: number): Promise<void> {
|
|
120
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
isActive(): boolean { return this.isPlaying; }
|
|
124
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SEORenderer } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { morseBeacon } 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 morseBeacon.i18n[locale]?.();
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
{content && <SEORenderer content={{ locale, sections: content.seo }} />}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface MorseBeaconUI extends Record<string, string> {
|
|
2
|
+
labelMessage: string;
|
|
3
|
+
placeholder: string;
|
|
4
|
+
btnTransmit: string;
|
|
5
|
+
btnSosLoop: string;
|
|
6
|
+
btnStop: string;
|
|
7
|
+
labelTorch: string;
|
|
8
|
+
statusStandby: string;
|
|
9
|
+
statusTransmitting: string;
|
|
10
|
+
statusStopping: string;
|
|
11
|
+
statusWaiting: string;
|
|
12
|
+
statusReady: string;
|
|
13
|
+
statusNoTorch: string;
|
|
14
|
+
statusNoPermission: string;
|
|
15
|
+
statusNotSupported: string;
|
|
16
|
+
statusReqHttps: string;
|
|
17
|
+
statusSending: string;
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { passwordGenerator } 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 passwordGenerator.i18n[locale]?.();
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
{content && <SharedBibliography links={content.bibliography} />}
|