@jjlmoya/utils-developer 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/package.json +64 -0
  2. package/src/category/i18n/en.ts +34 -0
  3. package/src/category/i18n/es.ts +34 -0
  4. package/src/category/i18n/fr.ts +34 -0
  5. package/src/category/index.ts +31 -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 +78 -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/css_specificity_logic.test.ts +39 -0
  17. package/src/tests/faq_count.test.ts +19 -0
  18. package/src/tests/locale_completeness.test.ts +42 -0
  19. package/src/tests/mocks/astro_mock.js +2 -0
  20. package/src/tests/no_h1_in_components.test.ts +48 -0
  21. package/src/tests/schemas_fulfillment.test.ts +23 -0
  22. package/src/tests/seo_length.test.ts +22 -0
  23. package/src/tests/title_quality.test.ts +55 -0
  24. package/src/tests/tool_validation.test.ts +17 -0
  25. package/src/tool/aspectRatio/bibliography.astro +14 -0
  26. package/src/tool/aspectRatio/component.astro +468 -0
  27. package/src/tool/aspectRatio/i18n/en.ts +177 -0
  28. package/src/tool/aspectRatio/i18n/es.ts +177 -0
  29. package/src/tool/aspectRatio/i18n/fr.ts +177 -0
  30. package/src/tool/aspectRatio/index.ts +34 -0
  31. package/src/tool/aspectRatio/seo.astro +14 -0
  32. package/src/tool/aspectRatio/ui.ts +12 -0
  33. package/src/tool/calculadoraTiempoDatos/bibliography.astro +14 -0
  34. package/src/tool/calculadoraTiempoDatos/component.astro +829 -0
  35. package/src/tool/calculadoraTiempoDatos/i18n/en.ts +150 -0
  36. package/src/tool/calculadoraTiempoDatos/i18n/es.ts +163 -0
  37. package/src/tool/calculadoraTiempoDatos/i18n/fr.ts +150 -0
  38. package/src/tool/calculadoraTiempoDatos/index.ts +33 -0
  39. package/src/tool/calculadoraTiempoDatos/seo.astro +14 -0
  40. package/src/tool/calculadoraTiempoDatos/ui.ts +24 -0
  41. package/src/tool/colorConverter/bibliography.astro +14 -0
  42. package/src/tool/colorConverter/component.astro +504 -0
  43. package/src/tool/colorConverter/i18n/en.ts +126 -0
  44. package/src/tool/colorConverter/i18n/es.ts +126 -0
  45. package/src/tool/colorConverter/i18n/fr.ts +126 -0
  46. package/src/tool/colorConverter/index.ts +33 -0
  47. package/src/tool/colorConverter/seo.astro +14 -0
  48. package/src/tool/colorConverter/ui.ts +16 -0
  49. package/src/tool/conversorExcelCsvHtml/bibliography.astro +14 -0
  50. package/src/tool/conversorExcelCsvHtml/component.astro +595 -0
  51. package/src/tool/conversorExcelCsvHtml/i18n/en.ts +129 -0
  52. package/src/tool/conversorExcelCsvHtml/i18n/es.ts +129 -0
  53. package/src/tool/conversorExcelCsvHtml/i18n/fr.ts +129 -0
  54. package/src/tool/conversorExcelCsvHtml/index.ts +33 -0
  55. package/src/tool/conversorExcelCsvHtml/seo.astro +14 -0
  56. package/src/tool/conversorExcelCsvHtml/ui.ts +14 -0
  57. package/src/tool/cronGenerator/bibliography.astro +14 -0
  58. package/src/tool/cronGenerator/component.astro +667 -0
  59. package/src/tool/cronGenerator/i18n/en.ts +191 -0
  60. package/src/tool/cronGenerator/i18n/es.ts +191 -0
  61. package/src/tool/cronGenerator/i18n/fr.ts +191 -0
  62. package/src/tool/cronGenerator/index.ts +34 -0
  63. package/src/tool/cronGenerator/seo.astro +14 -0
  64. package/src/tool/cronGenerator/ui.ts +45 -0
  65. package/src/tool/cssSpecificityCalculator/bibliography.astro +14 -0
  66. package/src/tool/cssSpecificityCalculator/component.astro +292 -0
  67. package/src/tool/cssSpecificityCalculator/i18n/en.ts +168 -0
  68. package/src/tool/cssSpecificityCalculator/i18n/es.ts +168 -0
  69. package/src/tool/cssSpecificityCalculator/i18n/fr.ts +168 -0
  70. package/src/tool/cssSpecificityCalculator/index.ts +34 -0
  71. package/src/tool/cssSpecificityCalculator/logic.ts +100 -0
  72. package/src/tool/cssSpecificityCalculator/seo.astro +14 -0
  73. package/src/tool/cssSpecificityCalculator/ui-controller.ts +57 -0
  74. package/src/tool/cssSpecificityCalculator/ui.ts +11 -0
  75. package/src/tool/cssToInlineConverter/bibliography.astro +14 -0
  76. package/src/tool/cssToInlineConverter/component.astro +308 -0
  77. package/src/tool/cssToInlineConverter/i18n/en.ts +160 -0
  78. package/src/tool/cssToInlineConverter/i18n/es.ts +160 -0
  79. package/src/tool/cssToInlineConverter/i18n/fr.ts +160 -0
  80. package/src/tool/cssToInlineConverter/index.ts +34 -0
  81. package/src/tool/cssToInlineConverter/seo.astro +14 -0
  82. package/src/tool/cssToInlineConverter/ui.ts +12 -0
  83. package/src/tool/duplicateCssRemover/bibliography.astro +14 -0
  84. package/src/tool/duplicateCssRemover/component.astro +353 -0
  85. package/src/tool/duplicateCssRemover/i18n/en.ts +167 -0
  86. package/src/tool/duplicateCssRemover/i18n/es.ts +167 -0
  87. package/src/tool/duplicateCssRemover/i18n/fr.ts +167 -0
  88. package/src/tool/duplicateCssRemover/index.ts +34 -0
  89. package/src/tool/duplicateCssRemover/seo.astro +14 -0
  90. package/src/tool/duplicateCssRemover/ui.ts +12 -0
  91. package/src/tool/generadorSecurityTxt/bibliography.astro +14 -0
  92. package/src/tool/generadorSecurityTxt/component.astro +473 -0
  93. package/src/tool/generadorSecurityTxt/i18n/en.ts +173 -0
  94. package/src/tool/generadorSecurityTxt/i18n/es.ts +173 -0
  95. package/src/tool/generadorSecurityTxt/i18n/fr.ts +173 -0
  96. package/src/tool/generadorSecurityTxt/index.ts +33 -0
  97. package/src/tool/generadorSecurityTxt/seo.astro +14 -0
  98. package/src/tool/generadorSecurityTxt/ui.ts +27 -0
  99. package/src/tool/hashGenerator/bibliography.astro +14 -0
  100. package/src/tool/hashGenerator/component.astro +367 -0
  101. package/src/tool/hashGenerator/i18n/en.ts +118 -0
  102. package/src/tool/hashGenerator/i18n/es.ts +118 -0
  103. package/src/tool/hashGenerator/i18n/fr.ts +118 -0
  104. package/src/tool/hashGenerator/index.ts +33 -0
  105. package/src/tool/hashGenerator/md5.ts +138 -0
  106. package/src/tool/hashGenerator/seo.astro +14 -0
  107. package/src/tool/hashGenerator/ui.ts +11 -0
  108. package/src/tool/inspectorCertificadosSsl/bibliography.astro +14 -0
  109. package/src/tool/inspectorCertificadosSsl/component.astro +580 -0
  110. package/src/tool/inspectorCertificadosSsl/i18n/en.ts +214 -0
  111. package/src/tool/inspectorCertificadosSsl/i18n/es.ts +214 -0
  112. package/src/tool/inspectorCertificadosSsl/i18n/fr.ts +214 -0
  113. package/src/tool/inspectorCertificadosSsl/index.ts +33 -0
  114. package/src/tool/inspectorCertificadosSsl/seo.astro +14 -0
  115. package/src/tool/inspectorCertificadosSsl/ui.ts +29 -0
  116. package/src/tool/jsonFormatter/bibliography.astro +14 -0
  117. package/src/tool/jsonFormatter/component.astro +590 -0
  118. package/src/tool/jsonFormatter/i18n/en.ts +156 -0
  119. package/src/tool/jsonFormatter/i18n/es.ts +156 -0
  120. package/src/tool/jsonFormatter/i18n/fr.ts +156 -0
  121. package/src/tool/jsonFormatter/index.ts +34 -0
  122. package/src/tool/jsonFormatter/seo.astro +14 -0
  123. package/src/tool/jsonFormatter/ui.ts +15 -0
  124. package/src/tool/keycode/bibliography.astro +14 -0
  125. package/src/tool/keycode/component.astro +540 -0
  126. package/src/tool/keycode/i18n/en.ts +160 -0
  127. package/src/tool/keycode/i18n/es.ts +160 -0
  128. package/src/tool/keycode/i18n/fr.ts +160 -0
  129. package/src/tool/keycode/index.ts +34 -0
  130. package/src/tool/keycode/seo.astro +14 -0
  131. package/src/tool/keycode/ui.ts +10 -0
  132. package/src/tool/llmCostCalculator/bibliography.astro +14 -0
  133. package/src/tool/llmCostCalculator/component.astro +488 -0
  134. package/src/tool/llmCostCalculator/data.ts +99 -0
  135. package/src/tool/llmCostCalculator/i18n/en.ts +165 -0
  136. package/src/tool/llmCostCalculator/i18n/es.ts +165 -0
  137. package/src/tool/llmCostCalculator/i18n/fr.ts +165 -0
  138. package/src/tool/llmCostCalculator/index.ts +34 -0
  139. package/src/tool/llmCostCalculator/seo.astro +14 -0
  140. package/src/tool/llmCostCalculator/ui.ts +11 -0
  141. package/src/tool/mobileMockupGenerator/app.ts +614 -0
  142. package/src/tool/mobileMockupGenerator/bibliography.astro +14 -0
  143. package/src/tool/mobileMockupGenerator/component.astro +1044 -0
  144. package/src/tool/mobileMockupGenerator/constants.ts +48 -0
  145. package/src/tool/mobileMockupGenerator/i18n/en.ts +121 -0
  146. package/src/tool/mobileMockupGenerator/i18n/es.ts +121 -0
  147. package/src/tool/mobileMockupGenerator/i18n/fr.ts +121 -0
  148. package/src/tool/mobileMockupGenerator/index.ts +33 -0
  149. package/src/tool/mobileMockupGenerator/rendering.ts +189 -0
  150. package/src/tool/mobileMockupGenerator/seo.astro +14 -0
  151. package/src/tool/mobileMockupGenerator/store.ts +145 -0
  152. package/src/tool/mobileMockupGenerator/types.ts +41 -0
  153. package/src/tool/mobileMockupGenerator/ui.ts +48 -0
  154. package/src/tool/musicalTypography/bibliography.astro +14 -0
  155. package/src/tool/musicalTypography/component.astro +480 -0
  156. package/src/tool/musicalTypography/i18n/en.ts +179 -0
  157. package/src/tool/musicalTypography/i18n/es.ts +179 -0
  158. package/src/tool/musicalTypography/i18n/fr.ts +179 -0
  159. package/src/tool/musicalTypography/index.ts +33 -0
  160. package/src/tool/musicalTypography/seo.astro +14 -0
  161. package/src/tool/musicalTypography/ui.ts +25 -0
  162. package/src/tool/placeholderGenerator/bibliography.astro +14 -0
  163. package/src/tool/placeholderGenerator/component.astro +499 -0
  164. package/src/tool/placeholderGenerator/i18n/en.ts +176 -0
  165. package/src/tool/placeholderGenerator/i18n/es.ts +176 -0
  166. package/src/tool/placeholderGenerator/i18n/fr.ts +176 -0
  167. package/src/tool/placeholderGenerator/index.ts +34 -0
  168. package/src/tool/placeholderGenerator/seo.astro +14 -0
  169. package/src/tool/placeholderGenerator/ui.ts +14 -0
  170. package/src/tool/promptLibrary/bibliography.astro +14 -0
  171. package/src/tool/promptLibrary/component.astro +1131 -0
  172. package/src/tool/promptLibrary/controller.ts +49 -0
  173. package/src/tool/promptLibrary/i18n/en.ts +131 -0
  174. package/src/tool/promptLibrary/i18n/es.ts +131 -0
  175. package/src/tool/promptLibrary/i18n/fr.ts +126 -0
  176. package/src/tool/promptLibrary/index.ts +33 -0
  177. package/src/tool/promptLibrary/seo.astro +14 -0
  178. package/src/tool/promptLibrary/ui.ts +29 -0
  179. package/src/tool/readabilityCalculator/bibliography.astro +14 -0
  180. package/src/tool/readabilityCalculator/component.astro +534 -0
  181. package/src/tool/readabilityCalculator/i18n/en.ts +133 -0
  182. package/src/tool/readabilityCalculator/i18n/es.ts +133 -0
  183. package/src/tool/readabilityCalculator/i18n/fr.ts +133 -0
  184. package/src/tool/readabilityCalculator/index.ts +33 -0
  185. package/src/tool/readabilityCalculator/seo.astro +14 -0
  186. package/src/tool/readabilityCalculator/ui.ts +27 -0
  187. package/src/tool/svgSanitizer/bibliography.astro +14 -0
  188. package/src/tool/svgSanitizer/component.astro +554 -0
  189. package/src/tool/svgSanitizer/i18n/en.ts +125 -0
  190. package/src/tool/svgSanitizer/i18n/es.ts +125 -0
  191. package/src/tool/svgSanitizer/i18n/fr.ts +125 -0
  192. package/src/tool/svgSanitizer/index.ts +33 -0
  193. package/src/tool/svgSanitizer/seo.astro +14 -0
  194. package/src/tool/svgSanitizer/ui.ts +18 -0
  195. package/src/tool/svgToCss/bibliography.astro +14 -0
  196. package/src/tool/svgToCss/component.astro +381 -0
  197. package/src/tool/svgToCss/i18n/en.ts +174 -0
  198. package/src/tool/svgToCss/i18n/es.ts +174 -0
  199. package/src/tool/svgToCss/i18n/fr.ts +174 -0
  200. package/src/tool/svgToCss/index.ts +34 -0
  201. package/src/tool/svgToCss/seo.astro +14 -0
  202. package/src/tool/svgToCss/ui.ts +13 -0
  203. package/src/tool/urlCleaner/bibliography.astro +14 -0
  204. package/src/tool/urlCleaner/component.astro +458 -0
  205. package/src/tool/urlCleaner/i18n/en.ts +116 -0
  206. package/src/tool/urlCleaner/i18n/es.ts +116 -0
  207. package/src/tool/urlCleaner/i18n/fr.ts +116 -0
  208. package/src/tool/urlCleaner/index.ts +33 -0
  209. package/src/tool/urlCleaner/seo.astro +14 -0
  210. package/src/tool/urlCleaner/ui.ts +12 -0
  211. package/src/tool/urlEncoderDecoder/bibliography.astro +14 -0
  212. package/src/tool/urlEncoderDecoder/component.astro +291 -0
  213. package/src/tool/urlEncoderDecoder/i18n/en.ts +162 -0
  214. package/src/tool/urlEncoderDecoder/i18n/es.ts +162 -0
  215. package/src/tool/urlEncoderDecoder/i18n/fr.ts +162 -0
  216. package/src/tool/urlEncoderDecoder/index.ts +34 -0
  217. package/src/tool/urlEncoderDecoder/seo.astro +14 -0
  218. package/src/tool/urlEncoderDecoder/ui.ts +11 -0
  219. package/src/tool/utmGenerator/bibliography.astro +14 -0
  220. package/src/tool/utmGenerator/component.astro +270 -0
  221. package/src/tool/utmGenerator/i18n/en.ts +117 -0
  222. package/src/tool/utmGenerator/i18n/es.ts +117 -0
  223. package/src/tool/utmGenerator/i18n/fr.ts +117 -0
  224. package/src/tool/utmGenerator/index.ts +33 -0
  225. package/src/tool/utmGenerator/seo.astro +14 -0
  226. package/src/tool/utmGenerator/ui.ts +13 -0
  227. package/src/tools.ts +27 -0
  228. package/src/types.ts +72 -0
@@ -0,0 +1,540 @@
1
+ ---
2
+ import type { KnownLocale } from '../../types';
3
+ import type { KeycodeUI } from './ui';
4
+
5
+ interface Props {
6
+ locale?: KnownLocale;
7
+ ui?: Record<string, unknown>;
8
+ }
9
+
10
+ const { ui } = Astro.props;
11
+ const t = (ui ?? {}) as KeycodeUI;
12
+
13
+ const infoFields = [
14
+ { id: 'key', label: 'event.key' },
15
+ { id: 'code', label: 'event.code' },
16
+ { id: 'which', label: 'event.which' },
17
+ { id: 'location', label: 'location' },
18
+ ];
19
+
20
+ const modKeys = ['ctrl', 'shift', 'alt', 'meta'];
21
+ ---
22
+
23
+ <div class="kc-root" id="kc-root" data-i18n={JSON.stringify(t)}>
24
+ <div class="kc-outer-card">
25
+ <div id="kc-initial" class="kc-initial">
26
+ <div class="kc-kbd-wrap">
27
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="kc-kbd-icon" aria-hidden="true">
28
+ <path d="M20 5H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-9 3h2v2h-2V8zm0 3h2v2h-2v-2zM8 8h2v2H8V8zm0 3h2v2H8v-2zm-1 5H5v-2h2v2zm10 0H7v-2h10v2zm0-3h-2v-2h2v2zm0-3h-2V8h2v2zm-3 3h-2v-2h2v2zm0-3h-2V8h2v2zm-3 0h-2V8h2v2z"/>
29
+ </svg>
30
+ </div>
31
+ <div class="kc-prompt">
32
+ <h2 class="kc-prompt-title">{t.promptTitle}</h2>
33
+ <p class="kc-prompt-sub">{t.promptSubtitle}</p>
34
+ </div>
35
+ <div class="kc-dots">
36
+ <span class="kc-dot"></span>
37
+ <span class="kc-dot" style="animation-delay:0.15s"></span>
38
+ <span class="kc-dot" style="animation-delay:0.3s"></span>
39
+ </div>
40
+ </div>
41
+
42
+ <div id="kc-active" class="kc-active" style="display:none">
43
+ <div class="kc-key-outer">
44
+ <div id="kc-key-wrap" class="kc-key-display">
45
+ <span id="kc-display-key" class="kc-key-char"></span>
46
+ <span id="kc-display-code" class="kc-key-code"></span>
47
+ </div>
48
+ </div>
49
+
50
+ <div class="kc-info-grid">
51
+ {infoFields.map(({ id, label }) => (
52
+ <div class="kc-info-card">
53
+ <span class="kc-info-label">{label}</span>
54
+ <span id={`kc-info-${id}`} class="kc-info-value"></span>
55
+ <button class="kc-copy-btn" data-target={`kc-info-${id}`} aria-label={`Copy ${label}`}>
56
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
57
+ <path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/>
58
+ </svg>
59
+ </button>
60
+ </div>
61
+ ))}
62
+ </div>
63
+
64
+ <div class="kc-mods-row">
65
+ {modKeys.map((mod) => (
66
+ <div id={`kc-mod-${mod}`} class="kc-mod-key">{mod.toUpperCase()}</div>
67
+ ))}
68
+ </div>
69
+
70
+ <div class="kc-snippets-section">
71
+ <h3 class="kc-snippets-title">{t.snippetsTitle}</h3>
72
+ <div class="kc-snippets-grid">
73
+ <button class="kc-snippet-btn"><code id="kc-snippet-key" class="kc-snippet-code"></code><span class="kc-snippet-copy">{t.btnCopy}</span></button>
74
+ <button class="kc-snippet-btn"><code id="kc-snippet-code" class="kc-snippet-code"></code><span class="kc-snippet-copy">{t.btnCopy}</span></button>
75
+ <button class="kc-snippet-btn"><code id="kc-snippet-which" class="kc-snippet-code"></code><span class="kc-snippet-copy">{t.btnCopy}</span></button>
76
+ <button class="kc-snippet-btn"><code id="kc-snippet-json" class="kc-snippet-code kc-snippet-code-wide"></code><span class="kc-snippet-copy">{t.btnCopy}</span></button>
77
+ </div>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ </div>
82
+
83
+ <style>
84
+ .kc-root {
85
+ --kc-primary: #7c3aed;
86
+ --kc-primary-light: #8b5cf6;
87
+ --kc-success: #10b981;
88
+ --kc-card-bg: #fff;
89
+ --kc-bg: #f8fafc;
90
+ --kc-text: #0f172a;
91
+ --kc-text-muted: #64748b;
92
+ --kc-border: #e2e8f0;
93
+ --kc-key-border: #cbd5e1;
94
+
95
+ max-width: 56rem;
96
+ margin: 2rem auto;
97
+ color: var(--kc-text);
98
+ }
99
+
100
+ :global(.theme-dark) .kc-root {
101
+ --kc-card-bg: #1e293b;
102
+ --kc-bg: #0f172a;
103
+ --kc-text: #f1f5f9;
104
+ --kc-text-muted: #94a3b8;
105
+ --kc-border: #334155;
106
+ --kc-key-border: #0f172a;
107
+ }
108
+
109
+ .kc-outer-card {
110
+ background: var(--kc-card-bg);
111
+ border: 1px solid var(--kc-border);
112
+ border-radius: 1.5rem;
113
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.07);
114
+ padding: 2rem;
115
+ }
116
+
117
+ .kc-initial {
118
+ min-height: 360px;
119
+ display: flex;
120
+ flex-direction: column;
121
+ align-items: center;
122
+ justify-content: center;
123
+ gap: 2rem;
124
+ }
125
+
126
+ .kc-kbd-wrap {
127
+ width: 8rem;
128
+ height: 8rem;
129
+ background: var(--kc-bg);
130
+ border-radius: 1.5rem;
131
+ border: 1px solid var(--kc-border);
132
+ border-bottom: 8px solid var(--kc-border);
133
+ display: flex;
134
+ align-items: center;
135
+ justify-content: center;
136
+ }
137
+
138
+ .kc-kbd-icon {
139
+ width: 3.5rem;
140
+ height: 3.5rem;
141
+ color: var(--kc-text-muted);
142
+ }
143
+
144
+ .kc-prompt {
145
+ text-align: center;
146
+ display: flex;
147
+ flex-direction: column;
148
+ gap: 0.5rem;
149
+ }
150
+
151
+ .kc-prompt-title {
152
+ font-size: 2rem;
153
+ font-weight: 900;
154
+ color: var(--kc-text);
155
+ margin: 0;
156
+ letter-spacing: -0.02em;
157
+ }
158
+
159
+ .kc-prompt-sub {
160
+ font-size: 1rem;
161
+ color: var(--kc-text-muted);
162
+ margin: 0;
163
+ }
164
+
165
+ .kc-dots {
166
+ display: flex;
167
+ gap: 0.5rem;
168
+ opacity: 0.4;
169
+ }
170
+
171
+ @keyframes kc-bounce {
172
+ 0%, 100% { transform: translateY(0); }
173
+ 50% { transform: translateY(-6px); }
174
+ }
175
+
176
+ .kc-dot {
177
+ width: 0.5rem;
178
+ height: 0.5rem;
179
+ border-radius: 50%;
180
+ background: var(--kc-border);
181
+ animation: kc-bounce 1s ease infinite;
182
+ }
183
+
184
+ .kc-active {
185
+ display: flex;
186
+ flex-direction: column;
187
+ gap: 2rem;
188
+ }
189
+
190
+ .kc-key-outer {
191
+ display: flex;
192
+ justify-content: center;
193
+ }
194
+
195
+ .kc-key-display {
196
+ min-width: 12rem;
197
+ min-height: 12rem;
198
+ padding: 2rem 3rem;
199
+ background: var(--kc-bg);
200
+ border-radius: 1.5rem;
201
+ border: 1px solid var(--kc-border);
202
+ border-bottom: 12px solid var(--kc-border);
203
+ display: flex;
204
+ flex-direction: column;
205
+ align-items: center;
206
+ justify-content: center;
207
+ gap: 0.5rem;
208
+ transition: transform 0.1s, border-bottom-width 0.1s, box-shadow 0.1s;
209
+ }
210
+
211
+ .kc-key-char {
212
+ font-size: 5rem;
213
+ font-weight: 900;
214
+ color: var(--kc-text);
215
+ line-height: 1;
216
+ text-align: center;
217
+ max-width: 20rem;
218
+ overflow-wrap: break-word;
219
+ }
220
+
221
+ .kc-key-code {
222
+ font-size: 0.8rem;
223
+ color: var(--kc-text-muted);
224
+ text-transform: uppercase;
225
+ letter-spacing: 0.12em;
226
+ }
227
+
228
+ .kc-info-grid {
229
+ display: grid;
230
+ grid-template-columns: repeat(4, 1fr);
231
+ gap: 0.75rem;
232
+ }
233
+
234
+ @media (max-width: 640px) {
235
+ .kc-info-grid {
236
+ grid-template-columns: repeat(2, 1fr);
237
+ }
238
+ }
239
+
240
+ .kc-info-card {
241
+ background: var(--kc-bg);
242
+ border: 1px solid var(--kc-border);
243
+ border-radius: 1rem;
244
+ padding: 1rem 0.875rem;
245
+ display: flex;
246
+ flex-direction: column;
247
+ align-items: center;
248
+ gap: 0.5rem;
249
+ min-height: 8rem;
250
+ justify-content: space-between;
251
+ transition: border-color 0.15s;
252
+ }
253
+
254
+ .kc-info-card:hover {
255
+ border-color: var(--kc-primary-light);
256
+ }
257
+
258
+ .kc-info-label {
259
+ font-size: 0.68rem;
260
+ text-transform: uppercase;
261
+ letter-spacing: 0.07em;
262
+ color: var(--kc-text-muted);
263
+ font-weight: 600;
264
+ }
265
+
266
+ .kc-info-value {
267
+ font-size: 1.1rem;
268
+ font-weight: 800;
269
+ color: var(--kc-primary-light);
270
+ text-align: center;
271
+ overflow-wrap: break-word;
272
+ max-width: 100%;
273
+ }
274
+
275
+ .kc-copy-btn {
276
+ background: transparent;
277
+ border: none;
278
+ cursor: pointer;
279
+ padding: 0.35rem;
280
+ border-radius: 0.375rem;
281
+ color: var(--kc-text-muted);
282
+ display: flex;
283
+ align-items: center;
284
+ transition: background 0.15s, color 0.15s;
285
+ }
286
+
287
+ .kc-copy-btn:hover {
288
+ background: rgba(139, 92, 246, 0.1);
289
+ color: var(--kc-primary-light);
290
+ }
291
+
292
+ .kc-mods-row {
293
+ display: flex;
294
+ justify-content: center;
295
+ gap: 0.75rem;
296
+ flex-wrap: wrap;
297
+ }
298
+
299
+ .kc-mod-key {
300
+ padding: 0.625rem 1.5rem;
301
+ border: 2px solid var(--kc-border);
302
+ border-radius: 0.75rem;
303
+ color: var(--kc-text-muted);
304
+ font-weight: 800;
305
+ font-size: 0.875rem;
306
+ letter-spacing: 0.05em;
307
+ transition: border-color 0.15s, background 0.15s, color 0.15s, transform 0.15s;
308
+ }
309
+
310
+ .kc-snippets-section {
311
+ border-top: 1px solid var(--kc-border);
312
+ padding-top: 1.5rem;
313
+ display: flex;
314
+ flex-direction: column;
315
+ gap: 1rem;
316
+ }
317
+
318
+ .kc-snippets-title {
319
+ font-size: 0.7rem;
320
+ text-transform: uppercase;
321
+ letter-spacing: 0.1em;
322
+ color: var(--kc-text-muted);
323
+ margin: 0;
324
+ text-align: center;
325
+ font-weight: 700;
326
+ }
327
+
328
+ .kc-snippets-grid {
329
+ display: grid;
330
+ grid-template-columns: 1fr 1fr;
331
+ gap: 0.625rem;
332
+ }
333
+
334
+ @media (max-width: 520px) {
335
+ .kc-snippets-grid {
336
+ grid-template-columns: 1fr;
337
+ }
338
+ }
339
+
340
+ .kc-snippet-btn {
341
+ display: flex;
342
+ align-items: center;
343
+ justify-content: space-between;
344
+ gap: 0.5rem;
345
+ padding: 0.875rem 1rem;
346
+ background: var(--kc-card-bg);
347
+ border: 1px solid var(--kc-border);
348
+ border-radius: 0.875rem;
349
+ cursor: pointer;
350
+ text-align: left;
351
+ transition: border-color 0.15s, background 0.15s;
352
+ }
353
+
354
+ .kc-snippet-btn:hover {
355
+ border-color: var(--kc-primary-light);
356
+ }
357
+
358
+ .kc-snippet-code {
359
+ font-size: 0.8rem;
360
+ color: var(--kc-text);
361
+ flex: 1;
362
+ overflow: hidden;
363
+ text-overflow: ellipsis;
364
+ white-space: nowrap;
365
+ }
366
+
367
+ .kc-snippet-copy {
368
+ font-size: 0.72rem;
369
+ font-weight: 700;
370
+ color: var(--kc-primary-light);
371
+ opacity: 0;
372
+ transition: opacity 0.15s;
373
+ white-space: nowrap;
374
+ flex-shrink: 0;
375
+ }
376
+
377
+ .kc-snippet-btn:hover .kc-snippet-copy {
378
+ opacity: 1;
379
+ }
380
+ </style>
381
+
382
+ <script>
383
+ import type { KeycodeUI } from './ui';
384
+
385
+ type InfoEls = { key: HTMLElement; code: HTMLElement; which: HTMLElement; location: HTMLElement };
386
+ type ModEls = { ctrl: HTMLElement | null; shift: HTMLElement | null; alt: HTMLElement | null; meta: HTMLElement | null };
387
+ type SnippetEls = { key: HTMLElement; code: HTMLElement; which: HTMLElement; json: HTMLElement };
388
+ type MainEls = { displayKey: HTMLElement; displayCode: HTMLElement; keyWrap: HTMLElement };
389
+ type AllRefs = { initial: HTMLElement; active: HTMLElement; main: MainEls; info: InfoEls; mods: ModEls; snippets: SnippetEls };
390
+
391
+ function getDisplayKey(key: string): string {
392
+ if (key === ' ') return 'Space';
393
+ return key.length === 1 ? key.toUpperCase() : key;
394
+ }
395
+
396
+ function getLocation(loc: number, locs: string[]): string {
397
+ return locs[loc] ?? locs[0];
398
+ }
399
+
400
+ function animateKey(el: HTMLElement): void {
401
+ el.style.transform = 'translateY(16px)';
402
+ el.style.borderBottomWidth = '0px';
403
+ el.style.boxShadow = 'none';
404
+ setTimeout(() => { el.style.transform = ''; el.style.borderBottomWidth = ''; el.style.boxShadow = ''; }, 150);
405
+ }
406
+
407
+ function setMod(el: HTMLElement | null, active: boolean): void {
408
+ if (!el) return;
409
+ el.style.borderColor = active ? '#8b5cf6' : '';
410
+ el.style.background = active ? 'rgba(139, 92, 246, 0.1)' : '';
411
+ el.style.color = active ? '#7c3aed' : '';
412
+ el.style.transform = active ? 'scale(1.1)' : '';
413
+ }
414
+
415
+ function showActive(initial: HTMLElement, active: HTMLElement): void {
416
+ if (initial.style.display !== 'none') {
417
+ initial.style.display = 'none';
418
+ active.style.display = '';
419
+ }
420
+ }
421
+
422
+ function updateInfoCards(e: KeyboardEvent, els: InfoEls, locs: string[]): void {
423
+ els.key.textContent = e.key === ' ' ? '(space)' : e.key;
424
+ els.code.textContent = e.code;
425
+ els.which.textContent = String(e.which);
426
+ els.location.textContent = getLocation(e.location, locs);
427
+ }
428
+
429
+ function updateMods(e: KeyboardEvent, mods: ModEls): void {
430
+ setMod(mods.ctrl, e.ctrlKey);
431
+ setMod(mods.shift, e.shiftKey);
432
+ setMod(mods.alt, e.altKey);
433
+ setMod(mods.meta, e.metaKey);
434
+ }
435
+
436
+ function updateSnippets(e: KeyboardEvent, els: SnippetEls): void {
437
+ const q = e.key === "'" ? '"' : "'";
438
+ els.key.textContent = `if (e.key === ${q}${e.key}${q})`;
439
+ els.code.textContent = `if (e.code === '${e.code}')`;
440
+ els.which.textContent = `if (e.which === ${e.which})`;
441
+ els.json.textContent = `{ key: '${e.key}', code: '${e.code}', which: ${e.which} }`;
442
+ }
443
+
444
+ function handleKeydown(e: KeyboardEvent, refs: AllRefs, locs: string[]): void {
445
+ e.preventDefault();
446
+ showActive(refs.initial, refs.active);
447
+ animateKey(refs.main.keyWrap);
448
+ refs.main.displayKey.textContent = getDisplayKey(e.key);
449
+ refs.main.displayCode.textContent = e.code;
450
+ updateInfoCards(e, refs.info, locs);
451
+ updateMods(e, refs.mods);
452
+ updateSnippets(e, refs.snippets);
453
+ }
454
+
455
+ function getMainEls(root: HTMLElement): MainEls {
456
+ return {
457
+ displayKey: root.querySelector<HTMLElement>('#kc-display-key')!,
458
+ displayCode: root.querySelector<HTMLElement>('#kc-display-code')!,
459
+ keyWrap: root.querySelector<HTMLElement>('#kc-key-wrap')!,
460
+ };
461
+ }
462
+
463
+ function getInfoEls(root: HTMLElement): InfoEls {
464
+ return {
465
+ key: root.querySelector<HTMLElement>('#kc-info-key')!,
466
+ code: root.querySelector<HTMLElement>('#kc-info-code')!,
467
+ which: root.querySelector<HTMLElement>('#kc-info-which')!,
468
+ location: root.querySelector<HTMLElement>('#kc-info-location')!,
469
+ };
470
+ }
471
+
472
+ function getModEls(root: HTMLElement): ModEls {
473
+ return {
474
+ ctrl: root.querySelector<HTMLElement>('#kc-mod-ctrl'),
475
+ shift: root.querySelector<HTMLElement>('#kc-mod-shift'),
476
+ alt: root.querySelector<HTMLElement>('#kc-mod-alt'),
477
+ meta: root.querySelector<HTMLElement>('#kc-mod-meta'),
478
+ };
479
+ }
480
+
481
+ function getSnippetEls(root: HTMLElement): SnippetEls {
482
+ return {
483
+ key: root.querySelector<HTMLElement>('#kc-snippet-key')!,
484
+ code: root.querySelector<HTMLElement>('#kc-snippet-code')!,
485
+ which: root.querySelector<HTMLElement>('#kc-snippet-which')!,
486
+ json: root.querySelector<HTMLElement>('#kc-snippet-json')!,
487
+ };
488
+ }
489
+
490
+ function getAllRefs(root: HTMLElement): AllRefs {
491
+ return {
492
+ initial: root.querySelector<HTMLElement>('#kc-initial')!,
493
+ active: root.querySelector<HTMLElement>('#kc-active')!,
494
+ main: getMainEls(root),
495
+ info: getInfoEls(root),
496
+ mods: getModEls(root),
497
+ snippets: getSnippetEls(root),
498
+ };
499
+ }
500
+
501
+ function setupSnippetCopy(root: HTMLElement): void {
502
+ root.querySelectorAll<HTMLElement>('.kc-snippet-btn').forEach(btn => {
503
+ btn.addEventListener('click', async () => {
504
+ const code = btn.querySelector('code');
505
+ if (!code?.textContent) return;
506
+ await navigator.clipboard.writeText(code.textContent);
507
+ btn.style.background = 'rgba(16, 185, 129, 0.1)';
508
+ btn.style.borderColor = '#10b981';
509
+ setTimeout(() => { btn.style.background = ''; btn.style.borderColor = ''; }, 600);
510
+ });
511
+ });
512
+ }
513
+
514
+ function setupValueCopy(root: HTMLElement): void {
515
+ root.querySelectorAll<HTMLElement>('.kc-copy-btn').forEach(btn => {
516
+ btn.addEventListener('click', async (e) => {
517
+ e.stopPropagation();
518
+ const id = btn.dataset.target;
519
+ if (!id) return;
520
+ const el = root.querySelector<HTMLElement>(`#${id}`);
521
+ if (!el?.textContent) return;
522
+ await navigator.clipboard.writeText(el.textContent);
523
+ btn.style.color = '#10b981';
524
+ setTimeout(() => { btn.style.color = ''; }, 1000);
525
+ });
526
+ });
527
+ }
528
+
529
+ function setup(root: HTMLElement): void {
530
+ const t = JSON.parse(root.dataset.i18n ?? '{}') as KeycodeUI;
531
+ const refs = getAllRefs(root);
532
+ const locs = [t.locationStandard, t.locationLeft, t.locationRight, t.locationNumpad];
533
+ setupSnippetCopy(root);
534
+ setupValueCopy(root);
535
+ window.addEventListener('keydown', (e) => handleKeydown(e, refs, locs));
536
+ }
537
+
538
+ const root = document.getElementById('kc-root');
539
+ if (root) setup(root);
540
+ </script>
@@ -0,0 +1,160 @@
1
+ import type { WithContext, FAQPage, HowToThing, SoftwareApplication } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import type { KeycodeUI } from '../ui';
4
+
5
+ const slug = 'keycode';
6
+ const title = 'Online Keyboard Key Code Visualizer. KeyCode Inspector';
7
+ const description =
8
+ 'Free online tool to see the event.key, event.code, event.which and location of any keyboard key in real time. Generates ready-to-use JavaScript code snippets.';
9
+
10
+ const faqData = [
11
+ {
12
+ question: 'What is the difference between event.key and event.code?',
13
+ answer:
14
+ 'event.code represents the physical key on the keyboard, regardless of the configured language. event.key represents the generated character, which may change if you press Shift or use a different language. Use code for game controls; use key for text input and semantic shortcuts.',
15
+ },
16
+ {
17
+ question: 'What is event.which and why is it deprecated?',
18
+ answer:
19
+ 'event.which is a legacy property that returns a numeric ASCII code for the key. It is marked as deprecated in modern standards because event.key and event.code replace it with more precise and readable information. It is shown in this tool for educational purposes.',
20
+ },
21
+ {
22
+ question: 'What does the location property mean?',
23
+ answer:
24
+ 'The location property indicates where the key is physically located on the keyboard: Standard (normal position), Left (left modifier key), Right (right modifier key), or Numpad (numeric keypad). It is useful to distinguish between the left and right CTRL keys, for example.',
25
+ },
26
+ {
27
+ question: 'Does it work with international keyboards and non-QWERTY layouts?',
28
+ answer:
29
+ 'Yes. The tool shows exactly what the browser reports for your keyboard configuration. event.code will always return the QWERTY name of the physical position, while event.key will show the actual character according to your language.',
30
+ },
31
+ ];
32
+
33
+ const howToData = [
34
+ {
35
+ name: 'Press any key',
36
+ text: 'With focus on the page, press any key on the keyboard to instantly see all the event information.',
37
+ },
38
+ {
39
+ name: 'Copy individual values',
40
+ text: 'Click the copy button next to each property (event.key, event.code, etc.) to copy that specific value to the clipboard.',
41
+ },
42
+ {
43
+ name: 'Use the code snippets',
44
+ text: 'In the "Quick Snippets" section you will find JavaScript code blocks ready to paste directly into your project.',
45
+ },
46
+ ];
47
+
48
+ const faqSchema: WithContext<FAQPage> = {
49
+ '@context': 'https://schema.org',
50
+ '@type': 'FAQPage',
51
+ mainEntity: faqData.map((item) => ({
52
+ '@type': 'Question',
53
+ name: item.question,
54
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
55
+ })),
56
+ };
57
+
58
+ const howToSchema: WithContext<HowToThing> = {
59
+ '@context': 'https://schema.org',
60
+ '@type': 'HowTo',
61
+ name: title,
62
+ description,
63
+ step: howToData.map((step, i) => ({
64
+ '@type': 'HowToStep',
65
+ position: i + 1,
66
+ name: step.name,
67
+ text: step.text,
68
+ })),
69
+ };
70
+
71
+ const appSchema: WithContext<SoftwareApplication> = {
72
+ '@context': 'https://schema.org',
73
+ '@type': 'SoftwareApplication',
74
+ name: title,
75
+ description,
76
+ applicationCategory: 'DeveloperApplication',
77
+ operatingSystem: 'All',
78
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
79
+ inLanguage: 'en',
80
+ };
81
+
82
+ const ui: KeycodeUI = {
83
+ promptTitle: 'Press a key',
84
+ promptSubtitle: 'Any key on your keyboard to see its code',
85
+ snippetsTitle: 'Quick Snippets',
86
+ btnCopy: 'Copy',
87
+ locationStandard: 'Standard',
88
+ locationLeft: 'Left',
89
+ locationRight: 'Right',
90
+ locationNumpad: 'Numpad',
91
+ };
92
+
93
+ export const content: ToolLocaleContent<KeycodeUI> = {
94
+ slug,
95
+ title,
96
+ description,
97
+ ui,
98
+ faqTitle: 'Frequently Asked Questions',
99
+ faq: faqData,
100
+ bibliographyTitle: 'References and Standards',
101
+ bibliography: [
102
+ {
103
+ name: 'MDN Web Docs – KeyboardEvent',
104
+ url: 'https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent',
105
+ },
106
+ {
107
+ name: 'UI Events Specification (W3C) – KeyboardEvent',
108
+ url: 'https://www.w3.org/TR/uievents/#events-keyboardevents',
109
+ },
110
+ {
111
+ name: 'MDN – KeyboardEvent.code values',
112
+ url: 'https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_code_values',
113
+ },
114
+ ],
115
+ howTo: howToData,
116
+ schemas: [faqSchema, howToSchema, appSchema],
117
+ seo: [
118
+ {
119
+ type: 'title',
120
+ text: 'Understanding keyboard events in JavaScript',
121
+ level: 2,
122
+ },
123
+ {
124
+ type: 'paragraph',
125
+ html: 'When a user presses a key, the browser fires three events: <code>keydown</code>, <code>keypress</code>, and <code>keyup</code>. Each exposes properties with information about the key pressed, but not all are equivalent or recommended.',
126
+ },
127
+ {
128
+ type: 'title',
129
+ text: 'Key event properties',
130
+ level: 3,
131
+ },
132
+ {
133
+ type: 'grid',
134
+ columns: 2,
135
+ },
136
+ {
137
+ type: 'card',
138
+ title: 'event.code — The physical key',
139
+ html: '<p>Returns the identifier of the <strong>physical position</strong> of the key on the keyboard, using QWERTY nomenclature. For example, the "A" key on an AZERTY keyboard returns <code>KeyQ</code>. Ideal for game controls where position matters, not the character.</p>',
140
+ },
141
+ {
142
+ type: 'card',
143
+ title: 'event.key — The generated character',
144
+ html: '<p>Returns the <strong>character value</strong> generated according to language and active modifiers. Pressing Shift+A returns <code>"A"</code>; without Shift returns <code>"a"</code>. For special keys it returns names like <code>"Enter"</code>, <code>"Escape"</code>, <code>"ArrowUp"</code>.</p>',
145
+ },
146
+ {
147
+ type: 'title',
148
+ text: 'When to use each property',
149
+ level: 3,
150
+ },
151
+ {
152
+ type: 'tip',
153
+ html: 'Use <code>event.code</code> for game controls (WASD regardless of language) and <code>event.key</code> to detect specific characters or semantic keyboard shortcuts like <code>Ctrl+C</code>.',
154
+ },
155
+ {
156
+ type: 'paragraph',
157
+ html: 'The <code>event.which</code> and <code>event.keyCode</code> properties are officially <strong>deprecated</strong> according to the W3C standard. Although modern browsers continue to support them for compatibility, they should not be used in new code.',
158
+ },
159
+ ],
160
+ };