@jjlmoya/utils-hardware 1.17.0 → 1.19.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 (98) hide show
  1. package/package.json +1 -1
  2. package/src/category/i18n/de.ts +4 -4
  3. package/src/category/i18n/id.ts +3 -3
  4. package/src/category/i18n/it.ts +3 -3
  5. package/src/category/i18n/nl.ts +3 -3
  6. package/src/category/i18n/pl.ts +4 -4
  7. package/src/category/i18n/pt.ts +3 -3
  8. package/src/category/i18n/ru.ts +3 -3
  9. package/src/category/i18n/sv.ts +3 -3
  10. package/src/category/i18n/tr.ts +3 -3
  11. package/src/category/i18n/zh.ts +3 -3
  12. package/src/category/index.ts +2 -1
  13. package/src/entries.ts +4 -1
  14. package/src/index.ts +1 -0
  15. package/src/layouts/PreviewLayout.astro +1 -0
  16. package/src/tests/diacritics_density.test.ts +118 -0
  17. package/src/tests/inverted_punctuation.test.ts +84 -0
  18. package/src/tests/locale_completeness.test.ts +2 -2
  19. package/src/tests/no_en_dash.test.ts +70 -0
  20. package/src/tests/pagespeed_best_practices.test.ts +198 -0
  21. package/src/tests/script_density.test.ts +94 -0
  22. package/src/tests/tool_validation.test.ts +2 -2
  23. package/src/tool/batteryHealthEstimator/component.astro +3 -3
  24. package/src/tool/batteryHealthEstimator/i18n/fr.ts +4 -4
  25. package/src/tool/batteryHealthEstimator/i18n/pl.ts +1 -1
  26. package/src/tool/colorAccuracyTest/color-accuracy-test.css +118 -5
  27. package/src/tool/colorAccuracyTest/i18n/de.ts +7 -7
  28. package/src/tool/colorAccuracyTest/i18n/en.ts +3 -3
  29. package/src/tool/colorAccuracyTest/i18n/es.ts +2 -2
  30. package/src/tool/colorAccuracyTest/i18n/fr.ts +9 -9
  31. package/src/tool/colorAccuracyTest/i18n/id.ts +3 -3
  32. package/src/tool/colorAccuracyTest/i18n/it.ts +3 -3
  33. package/src/tool/colorAccuracyTest/i18n/ja.ts +1 -1
  34. package/src/tool/colorAccuracyTest/i18n/ko.ts +2 -2
  35. package/src/tool/colorAccuracyTest/i18n/nl.ts +3 -3
  36. package/src/tool/colorAccuracyTest/i18n/pl.ts +5 -5
  37. package/src/tool/colorAccuracyTest/i18n/pt.ts +3 -3
  38. package/src/tool/colorAccuracyTest/i18n/ru.ts +15 -15
  39. package/src/tool/colorAccuracyTest/i18n/sv.ts +3 -3
  40. package/src/tool/colorAccuracyTest/i18n/tr.ts +2 -2
  41. package/src/tool/colorAccuracyTest/i18n/zh.ts +7 -7
  42. package/src/tool/deadPixelTest/i18n/ru.ts +6 -6
  43. package/src/tool/deadPixelTest/i18n/sv.ts +1 -1
  44. package/src/tool/deadPixelTest/i18n/zh.ts +5 -5
  45. package/src/tool/gamepadTest/component.astro +4 -3
  46. package/src/tool/gamepadTest/gamepad-test.css +171 -3
  47. package/src/tool/gamepadTest/i18n/es.ts +4 -4
  48. package/src/tool/gamepadTest/i18n/ru.ts +1 -1
  49. package/src/tool/gamepadTest/i18n/zh.ts +1 -1
  50. package/src/tool/gamepadVibrationTester/component.astro +3 -3
  51. package/src/tool/gamepadVibrationTester/i18n/es.ts +1 -1
  52. package/src/tool/gamepadVibrationTester/i18n/fr.ts +2 -2
  53. package/src/tool/keyboardTest/component.astro +6 -1
  54. package/src/tool/keyboardTest/keyboard-test.css +115 -2
  55. package/src/tool/mouseDoubleClickTest/bibliography.astro +14 -0
  56. package/src/tool/mouseDoubleClickTest/bibliography.ts +16 -0
  57. package/src/tool/mouseDoubleClickTest/component.astro +135 -0
  58. package/src/tool/mouseDoubleClickTest/entry.ts +29 -0
  59. package/src/tool/mouseDoubleClickTest/i18n/de.ts +274 -0
  60. package/src/tool/mouseDoubleClickTest/i18n/en.ts +274 -0
  61. package/src/tool/mouseDoubleClickTest/i18n/es.ts +274 -0
  62. package/src/tool/mouseDoubleClickTest/i18n/fr.ts +274 -0
  63. package/src/tool/mouseDoubleClickTest/i18n/id.ts +285 -0
  64. package/src/tool/mouseDoubleClickTest/i18n/it.ts +274 -0
  65. package/src/tool/mouseDoubleClickTest/i18n/ja.ts +274 -0
  66. package/src/tool/mouseDoubleClickTest/i18n/ko.ts +274 -0
  67. package/src/tool/mouseDoubleClickTest/i18n/nl.ts +274 -0
  68. package/src/tool/mouseDoubleClickTest/i18n/pl.ts +274 -0
  69. package/src/tool/mouseDoubleClickTest/i18n/pt.ts +274 -0
  70. package/src/tool/mouseDoubleClickTest/i18n/ru.ts +274 -0
  71. package/src/tool/mouseDoubleClickTest/i18n/sv.ts +274 -0
  72. package/src/tool/mouseDoubleClickTest/i18n/tr.ts +274 -0
  73. package/src/tool/mouseDoubleClickTest/i18n/zh.ts +274 -0
  74. package/src/tool/mouseDoubleClickTest/index.ts +9 -0
  75. package/src/tool/mouseDoubleClickTest/logic.ts +258 -0
  76. package/src/tool/mouseDoubleClickTest/mouse-double-click-test.css +488 -0
  77. package/src/tool/mouseDoubleClickTest/seo.astro +15 -0
  78. package/src/tool/mouseDoubleClickTest/ui.ts +26 -0
  79. package/src/tool/mousePollingTest/i18n/fr.ts +3 -3
  80. package/src/tool/mousePollingTest/i18n/pl.ts +1 -1
  81. package/src/tool/mousePollingTest/i18n/ru.ts +1 -1
  82. package/src/tool/mousePollingTest/i18n/zh.ts +1 -1
  83. package/src/tool/mousePollingTest/logic/RatonManager.ts +6 -6
  84. package/src/tool/refreshRateDetector/i18n/de.ts +3 -3
  85. package/src/tool/refreshRateDetector/i18n/en.ts +3 -3
  86. package/src/tool/refreshRateDetector/i18n/fr.ts +4 -4
  87. package/src/tool/refreshRateDetector/i18n/id.ts +3 -3
  88. package/src/tool/refreshRateDetector/i18n/pl.ts +2 -2
  89. package/src/tool/refreshRateDetector/i18n/pt.ts +3 -3
  90. package/src/tool/refreshRateDetector/i18n/sv.ts +3 -3
  91. package/src/tool/refreshRateDetector/i18n/tr.ts +2 -2
  92. package/src/tool/refreshRateDetector/i18n/zh.ts +2 -2
  93. package/src/tool/toneGenerator/component.astro +7 -7
  94. package/src/tool/toneGenerator/i18n/fr.ts +2 -2
  95. package/src/tool/toneGenerator/i18n/pl.ts +1 -1
  96. package/src/tool/toneGenerator/i18n/ru.ts +2 -2
  97. package/src/tool/toneGenerator/i18n/zh.ts +3 -3
  98. package/src/tools.ts +2 -2
@@ -43,7 +43,7 @@ const howToData = [
43
43
  },
44
44
  {
45
45
  name: '命名您的硬件(可选)',
46
- text: '输入显示器或设备的描述性名称(例如“MacBook Pro 16 M1”)。这将个性化您的报告。',
46
+ text: '输入显示器或设备的描述性名称(例如"MacBook Pro 16 M1")。这将个性化您的报告。',
47
47
  },
48
48
  {
49
49
  name: '进入全屏模式',
@@ -118,7 +118,7 @@ export const content: ToolLocaleContent<SpectrumCanvasUI> = {
118
118
  },
119
119
  {
120
120
  type: 'paragraph',
121
- html: '色彩还原中哪怕只有 1% 的差异,也可能决定了作品是令人“惊叹”还是让人感觉“不对劲”。专业显示器提供的精准度在 <strong>Delta E &lt; 2</strong> 以内。消费级显示器通常会漂移到 Delta E 4-6+,导致:',
121
+ html: '色彩还原中哪怕只有 1% 的差异,也可能决定了作品是令人"惊叹"还是让人感觉"不对劲"。专业显示器提供的精准度在 <strong>Delta E &lt; 2</strong> 以内。消费级显示器通常会漂移到 Delta E 4-6+,导致:',
122
122
  },
123
123
  {
124
124
  type: 'paragraph',
@@ -166,7 +166,7 @@ export const content: ToolLocaleContent<SpectrumCanvasUI> = {
166
166
  },
167
167
  {
168
168
  type: 'paragraph',
169
- html: '<strong>Delta E 分级 (CIE 1976):</strong><ul><li>01:人眼无法察觉</li><li>12:几乎无法察觉</li><li>24:可察觉,但对于普通用途是可以接受的</li><li>46:明显的色彩漂移</li><li>&gt;6: 非常明显的差异</li></ul>',
169
+ html: '<strong>Delta E 分级 (CIE 1976):</strong><ul><li>0-1:人眼无法察觉</li><li>1-2:几乎无法察觉</li><li>2-4:可察觉,但对于普通用途是可以接受的</li><li>4-6:明显的色彩漂移</li><li>&gt;6: 非常明显的差异</li></ul>',
170
170
  },
171
171
  {
172
172
  type: 'paragraph',
@@ -184,7 +184,7 @@ export const content: ToolLocaleContent<SpectrumCanvasUI> = {
184
184
  },
185
185
  {
186
186
  type: 'paragraph',
187
- html: '显示纯正的原色和间色(红、绿、蓝、青、洋红、黄),并测量您的显示器如何还原它们。色彩“泛滥”动画可揭示全屏色彩还原的一致性。',
187
+ html: '显示纯正的原色和间色(红、绿、蓝、青、洋红、黄),并测量您的显示器如何还原它们。色彩"泛滥"动画可揭示全屏色彩还原的一致性。',
188
188
  },
189
189
  {
190
190
  type: 'title',
@@ -202,7 +202,7 @@ export const content: ToolLocaleContent<SpectrumCanvasUI> = {
202
202
  },
203
203
  {
204
204
  type: 'paragraph',
205
- html: '纯黑 (0,0,0) 背景上带有几乎看不见的近黑阴影。如果暗部细节“挤”在了一起,说明您的显示器正在丢失阴影部分的色彩信息——这在移动设备屏幕和廉价面板中很常见。',
205
+ html: '纯黑 (0,0,0) 背景上带有几乎看不见的近黑阴影。如果暗部细节"挤"在了一起,说明您的显示器正在丢失阴影部分的色彩信息;这在移动设备屏幕和廉价面板中很常见。',
206
206
  },
207
207
  {
208
208
  type: 'title',
@@ -224,7 +224,7 @@ export const content: ToolLocaleContent<SpectrumCanvasUI> = {
224
224
  },
225
225
  {
226
226
  type: 'paragraph',
227
- html: '<ul><li><strong>3D 色域可视化:</strong> 一个可旋转的 3D 图表,展示您显示器的实际色域体积与参考标准的对比</li><li><strong>Delta E 热力图:</strong> 显示您的显示器在光谱的哪些部分发生了漂移</li><li><strong>伽马曲线:</strong> 0-255 范围内的亮度线性度</li><li><strong>校准评分:</strong> 根据总体准确度给出的单一“色彩等级”(Spectrum Grade)(顶尖、影院级、工作站级、标准)</li></ul>',
227
+ html: '<ul><li><strong>3D 色域可视化:</strong> 一个可旋转的 3D 图表,展示您显示器的实际色域体积与参考标准的对比</li><li><strong>Delta E 热力图:</strong> 显示您的显示器在光谱的哪些部分发生了漂移</li><li><strong>伽马曲线:</strong> 0-255 范围内的亮度线性度</li><li><strong>校准评分:</strong> 根据总体准确度给出的单一"色彩等级"(Spectrum Grade)(顶尖、影院级、工作站级、标准)</li></ul>',
228
228
  },
229
229
  {
230
230
  type: 'title',
@@ -237,7 +237,7 @@ export const content: ToolLocaleContent<SpectrumCanvasUI> = {
237
237
  },
238
238
  {
239
239
  type: 'paragraph',
240
- html: '<ul><li><strong>色温:</strong> 如果颜色偏冷/偏蓝,向“暖色”移动;如果偏暖/偏黄,向“冷色”移动</li><li><strong>对比度:</strong> 如果黑色看起来发白,则增加;如果暗部细节丢失,则减少</li><li><strong>亮度:</strong> 调整以在 50% 亮度下实现没有色偏的中性灰色</li><li><strong>饱和度:</strong> 如果颜色过度饱和,则减少;如果显得暗淡,则增加</li></ul>',
240
+ html: '<ul><li><strong>色温:</strong> 如果颜色偏冷/偏蓝,向"暖色"移动;如果偏暖/偏黄,向"冷色"移动</li><li><strong>对比度:</strong> 如果黑色看起来发白,则增加;如果暗部细节丢失,则减少</li><li><strong>亮度:</strong> 调整以在 50% 亮度下实现没有色偏的中性灰色</li><li><strong>饱和度:</strong> 如果颜色过度饱和,则减少;如果显得暗淡,则增加</li></ul>',
241
241
  },
242
242
  {
243
243
  type: 'title',
@@ -118,7 +118,7 @@ export const content: ToolLocaleContent<PixelesPantallaUI> = {
118
118
  },
119
119
  {
120
120
  type: 'paragraph',
121
- html: 'Это самый распространенный дефект. Он возникает, когда один или несколько субпикселей (красный, зеленый или синий) застревают во включенном состоянии. <strong>Симптом:</strong> вы увидите постоянно горящую цветную точку (красную, зеленую, синюю или белую) на темном фоне. <strong>Часто подлежит ремонту.</strong> Жидкий кристалл все еще реагирует; он просто «заблокирован» в определенной поляризации. Наш стробоскопический инструмент пытается разблокировать его с помощью быстрой стимуляции напряжением.',
121
+ html: 'Это самый распространенный дефект. Он возникает, когда один или несколько субпикселей (красный, зеленый или синий) застревают во включенном состоянии. <strong>Симптом:</strong> вы увидите постоянно горящую цветную точку (красную, зеленую, синюю или белую) на темном фоне. <strong>Часто подлежит ремонту.</strong> Жидкий кристалл все еще реагирует; он просто "заблокирован" в определенной поляризации. Наш стробоскопический инструмент пытается разблокировать его с помощью быстрой стимуляции напряжением.',
122
122
  },
123
123
  {
124
124
  type: 'title',
@@ -136,7 +136,7 @@ export const content: ToolLocaleContent<PixelesPantallaUI> = {
136
136
  },
137
137
  {
138
138
  type: 'paragraph',
139
- html: 'Функция «Ремонт пикселя» использует метод, известный как <strong>Pixel Exercising</strong> (тренировка пикселей). Она генерирует высокочастотный шаблон случайного шума (быстрое мигание цветов) над пораженной областью.',
139
+ html: 'Функция "Ремонт пикселя" использует метод, известный как <strong>Pixel Exercising</strong> (тренировка пикселей). Она генерирует высокочастотный шаблон случайного шума (быстрое мигание цветов) над пораженной областью.',
140
140
  },
141
141
  {
142
142
  type: 'title',
@@ -145,7 +145,7 @@ export const content: ToolLocaleContent<PixelesPantallaUI> = {
145
145
  },
146
146
  {
147
147
  type: 'paragraph',
148
- html: 'В ЖК-мониторах используются жидкие кристаллы, которые меняют свою прозрачность в зависимости от приложенного напряжения. Когда субпиксель застревает, это означает, что кристалл «застыл» в определенной поляризации. Быстрые изменения напряжения (достигаемые за счет быстрой смены основных цветов) пытаются «размять» кристалл и заставить его сменить состояние.',
148
+ html: 'В ЖК-мониторах используются жидкие кристаллы, которые меняют свою прозрачность в зависимости от приложенного напряжения. Когда субпиксель застревает, это означает, что кристалл "застыл" в определенной поляризации. Быстрые изменения напряжения (достигаемые за счет быстрой смены основных цветов) пытаются "размять" кристалл и заставить его сменить состояние.',
149
149
  },
150
150
  {
151
151
  type: 'title',
@@ -154,7 +154,7 @@ export const content: ToolLocaleContent<PixelesPantallaUI> = {
154
154
  },
155
155
  {
156
156
  type: 'paragraph',
157
- html: '<ul><li>Рекомендуется запускать инструмент над пораженной областью в течение как минимум <strong>1020 минут</strong>.</li><li>Если это не помогло, попробуйте более длительные сеансы (до 1 часа) или попробуйте очень осторожно надавить на пиксель салфеткой из микрофибры (на ваш страх и риск).</li><li>В некоторых случаях легкий прогрев монитора феном (на низкой температуре) перед активацией стробоскопа улучшает результаты.</li></ul>',
157
+ html: '<ul><li>Рекомендуется запускать инструмент над пораженной областью в течение как минимум <strong>10-20 минут</strong>.</li><li>Если это не помогло, попробуйте более длительные сеансы (до 1 часа) или попробуйте очень осторожно надавить на пиксель салфеткой из микрофибры (на ваш страх и риск).</li><li>В некоторых случаях легкий прогрев монитора феном (на низкой температуре) перед активацией стробоскопа улучшает результаты.</li></ul>',
158
158
  },
159
159
  {
160
160
  type: 'title',
@@ -172,7 +172,7 @@ export const content: ToolLocaleContent<PixelesPantallaUI> = {
172
172
  },
173
173
  {
174
174
  type: 'paragraph',
175
- html: 'Если вы подтвердили наличие дефектных пикселей (особенно нескольких битых пикселей), вы можете использовать наш тест как доказательство для гарантийных претензий. Многие производители считают более 23 ярких пикселей или 1 битый пиксель производственным браком, дающим право на замену в соответствии со стандартом ISO 9241-307 (класс 1).',
175
+ html: 'Если вы подтвердили наличие дефектных пикселей (особенно нескольких битых пикселей), вы можете использовать наш тест как доказательство для гарантийных претензий. Многие производители считают более 2-3 ярких пикселей или 1 битый пиксель производственным браком, дающим право на замену в соответствии со стандартом ISO 9241-307 (класс 1).',
176
176
  },
177
177
  {
178
178
  type: 'title',
@@ -181,7 +181,7 @@ export const content: ToolLocaleContent<PixelesPantallaUI> = {
181
181
  },
182
182
  {
183
183
  type: 'paragraph',
184
- html: 'На протяжении десятилетий ЖК-мониторы страдали от дефектов пикселей из-за сложности производства. Панель Full HD (1920×1080) содержит 6 220 800 пикселей (18 662 400 субпикселей). Статистическая вероятность возникновения дефектов неизбежна. Вот почему существуют такие стандарты, как ISO 9241-307, определяющие «допустимые битые пиксели» в зависимости от класса монитора.',
184
+ html: 'На протяжении десятилетий ЖК-мониторы страдали от дефектов пикселей из-за сложности производства. Панель Full HD (1920×1080) содержит 6 220 800 пикселей (18 662 400 субпикселей). Статистическая вероятность возникновения дефектов неизбежна. Вот почему существуют такие стандарты, как ISO 9241-307, определяющие "допустимые битые пиксели" в зависимости от класса монитора.',
185
185
  },
186
186
  ],
187
187
  ui: {
@@ -172,7 +172,7 @@ export const content: ToolLocaleContent<PixelesPantallaUI> = {
172
172
  },
173
173
  {
174
174
  type: 'paragraph',
175
- html: 'Om du bekräftar defekta pixlar (särskilt flera döda pixlar) kan du använda vårt test som bevis vid garantianspråk. Många tillverkare anser att fler än 23 ljusa pixlar eller 1 död pixel är ett tillverkningsfel som berättigar till utbyte enligt ISO 9241-307 (Klass 1)-standarden.',
175
+ html: 'Om du bekräftar defekta pixlar (särskilt flera döda pixlar) kan du använda vårt test som bevis vid garantianspråk. Många tillverkare anser att fler än 2-3 ljusa pixlar eller 1 död pixel är ett tillverkningsfel som berättigar till utbyte enligt ISO 9241-307 (Klass 1)-standarden.',
176
176
  },
177
177
  {
178
178
  type: 'title',
@@ -27,7 +27,7 @@ const faqData = [
27
27
  {
28
28
  question: '保修范围涵盖多少个坏点?',
29
29
  answer:
30
- '这取决于制造商和 ISO 9241-307 标准。通常,品牌认为对于 1 级面板,最多 2 到 3 个亮点是“正常”的,而 0 级面板则不允许有任何坏点。',
30
+ '这取决于制造商和 ISO 9241-307 标准。通常,品牌认为对于 1 级面板,最多 2 到 3 个亮点是"正常"的,而 0 级面板则不允许有任何坏点。',
31
31
  },
32
32
  ];
33
33
 
@@ -118,7 +118,7 @@ export const content: ToolLocaleContent<PixelesPantallaUI> = {
118
118
  },
119
119
  {
120
120
  type: 'paragraph',
121
- html: '这是最常见的缺陷。它发生在红、绿、蓝子像素中的一个或多个卡在“开启”状态时。<strong>症状:</strong>在深色背景下,你会看到一个永久发光的色点(红、绿、蓝或白色)。<strong>通常是可修复的。</strong>液晶仍然有反应,只是被“锁”在特定的偏振状态。我们的 Strobe 修复工具尝试通过快速电压刺激来解锁它。',
121
+ html: '这是最常见的缺陷。它发生在红、绿、蓝子像素中的一个或多个卡在"开启"状态时。<strong>症状:</strong>在深色背景下,你会看到一个永久发光的色点(红、绿、蓝或白色)。<strong>通常是可修复的。</strong>液晶仍然有反应,只是被"锁"在特定的偏振状态。我们的 Strobe 修复工具尝试通过快速电压刺激来解锁它。',
122
122
  },
123
123
  {
124
124
  type: 'title',
@@ -136,7 +136,7 @@ export const content: ToolLocaleContent<PixelesPantallaUI> = {
136
136
  },
137
137
  {
138
138
  type: 'paragraph',
139
- html: '“修复像素”功能使用了一种被称为 <strong>Pixel Exercising</strong>(像素锻炼)的技术。它在受影响的区域产生高频随机噪声模式(颜色快速闪烁)。',
139
+ html: '"修复像素"功能使用了一种被称为 <strong>Pixel Exercising</strong>(像素锻炼)的技术。它在受影响的区域产生高频随机噪声模式(颜色快速闪烁)。',
140
140
  },
141
141
  {
142
142
  type: 'title',
@@ -145,7 +145,7 @@ export const content: ToolLocaleContent<PixelesPantallaUI> = {
145
145
  },
146
146
  {
147
147
  type: 'paragraph',
148
- html: '液晶显示器使用液晶,其透明度根据施加的电压而改变。当一个子像素卡住时,意味着液晶“冻结”在特定的偏振状态。快速的电压变化(通过快速切换原色实现)尝试“锻炼”液晶并迫使它改变状态。',
148
+ html: '液晶显示器使用液晶,其透明度根据施加的电压而改变。当一个子像素卡住时,意味着液晶"冻结"在特定的偏振状态。快速的电压变化(通过快速切换原色实现)尝试"锻炼"液晶并迫使它改变状态。',
149
149
  },
150
150
  {
151
151
  type: 'title',
@@ -181,7 +181,7 @@ export const content: ToolLocaleContent<PixelesPantallaUI> = {
181
181
  },
182
182
  {
183
183
  type: 'paragraph',
184
- html: '几十年来,由于制造的复杂性,液晶显示器一直存在像素缺陷。一个全高清面板(1920×1080)包含 6,220,800 个像素(18,662,400 个子像素)。统计上的缺陷概率是不可避免的。这就是为什么存在 ISO 9241-307 等标准,来根据显示器级别定义“可接受的死点”数量。',
184
+ html: '几十年来,由于制造的复杂性,液晶显示器一直存在像素缺陷。一个全高清面板(1920×1080)包含 6,220,800 个像素(18,662,400 个子像素)。统计上的缺陷概率是不可避免的。这就是为什么存在 ISO 9241-307 等标准,来根据显示器级别定义"可接受的死点"数量。',
185
185
  },
186
186
  ],
187
187
  ui: {
@@ -341,11 +341,12 @@ const t = (ui ?? {}) as TestMandoUI;
341
341
  (targetBtn as HTMLElement).style.background = currentTarget.color;
342
342
  (targetBtn as HTMLElement).style.color = 'white';
343
343
  targetBtn.classList.remove('tm-target-hidden', 'tm-target-pop');
344
- void targetBtn.offsetWidth;
345
- targetBtn.classList.add('tm-target-pop');
346
344
  targetLabel.textContent = currentTarget.label;
347
345
  targetSub.textContent = currentTarget.type === 'axis' ? LABEL_MOVE : LABEL_PRESS;
348
- setTimeout(() => targetBtn.classList.remove('tm-target-pop'), 300);
346
+ requestAnimationFrame(() => {
347
+ targetBtn?.classList.add('tm-target-pop');
348
+ setTimeout(() => targetBtn?.classList.remove('tm-target-pop'), 300);
349
+ });
349
350
  }
350
351
  function startTimer() {
351
352
  timeLeft = 30;
@@ -20,7 +20,15 @@
20
20
  --tm-stick-border: #64748b;
21
21
 
22
22
  width: 100%;
23
+ max-width: 100%;
23
24
  color: var(--tm-text);
25
+ overflow-x: clip;
26
+ }
27
+
28
+ .tm-wrapper *,
29
+ .tm-wrapper *::before,
30
+ .tm-wrapper *::after {
31
+ box-sizing: border-box;
24
32
  }
25
33
 
26
34
  .theme-dark .tm-wrapper {
@@ -103,6 +111,8 @@ body.is-widget .tm-wrapper {
103
111
  padding: 2rem;
104
112
  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
105
113
  border: 1px solid var(--tm-border);
114
+ max-width: 100%;
115
+ overflow: hidden;
106
116
  }
107
117
 
108
118
  .tm-card-header {
@@ -148,24 +158,28 @@ body.is-widget .tm-wrapper {
148
158
  flex-direction: column;
149
159
  align-items: center;
150
160
  gap: 1rem;
161
+ width: 100%;
162
+ overflow: hidden;
151
163
  }
152
164
 
153
165
  .tm-stage {
154
166
  position: relative;
155
167
  width: 600px;
156
168
  height: 420px;
169
+ flex: 0 0 600px;
170
+ transform-origin: top center;
157
171
  transition: all 500ms;
158
172
  }
159
173
 
160
174
  .tm-stage-inactive {
161
175
  opacity: 0.25;
162
176
  filter: grayscale(1);
163
- transform: scale(0.9);
177
+ transform: scale(var(--tm-stage-scale, 1));
164
178
  }
165
179
 
166
180
  @media (min-width: 640px) {
167
181
  .tm-stage-inactive {
168
- transform: scale(1);
182
+ transform: scale(var(--tm-stage-scale, 1));
169
183
  }
170
184
  }
171
185
 
@@ -460,6 +474,7 @@ body.is-widget .tm-wrapper {
460
474
  font-size: 0.75rem;
461
475
  color: var(--tm-text-muted);
462
476
  transition: opacity 500ms;
477
+ min-width: 0;
463
478
  }
464
479
 
465
480
  .tm-info-inactive { opacity: 0.5; }
@@ -468,6 +483,7 @@ body.is-widget .tm-wrapper {
468
483
  .tm-info-right {
469
484
  display: flex;
470
485
  gap: 1rem;
486
+ min-width: 0;
471
487
  }
472
488
 
473
489
  .tm-gp-id {
@@ -630,6 +646,7 @@ body.is-widget .tm-wrapper {
630
646
  justify-content: center;
631
647
  border: 1px solid rgba(255, 255, 255, 0.05);
632
648
  color: white;
649
+ max-width: 100%;
633
650
  }
634
651
 
635
652
  .tm-game-intro {
@@ -732,6 +749,7 @@ body.is-widget .tm-wrapper {
732
749
  align-items: center;
733
750
  width: 100%;
734
751
  margin-bottom: 3rem;
752
+ gap: 1rem;
735
753
  }
736
754
 
737
755
  .tm-hud-item {
@@ -783,6 +801,7 @@ body.is-widget .tm-wrapper {
783
801
  display: flex;
784
802
  align-items: center;
785
803
  justify-content: center;
804
+ max-width: 100%;
786
805
  }
787
806
 
788
807
  .tm-target-ring {
@@ -852,6 +871,8 @@ body.is-widget .tm-wrapper {
852
871
  background: rgba(255, 255, 255, 0.05);
853
872
  border-radius: 9999px;
854
873
  border: 1px solid rgba(255, 255, 255, 0.1);
874
+ max-width: 100%;
875
+ text-align: center;
855
876
  }
856
877
 
857
878
  .tm-instruction-dot {
@@ -922,6 +943,153 @@ body.is-widget .tm-wrapper {
922
943
  justify-content: center;
923
944
  }
924
945
 
946
+ @media (max-width: 700px) {
947
+ .tm-container {
948
+ padding: 0;
949
+ gap: 1.25rem;
950
+ }
951
+
952
+ .tm-card,
953
+ .tm-game-card {
954
+ border-radius: 0.875rem;
955
+ padding: 1rem;
956
+ }
957
+
958
+ .tm-card-header {
959
+ margin-bottom: 1.25rem;
960
+ }
961
+
962
+ .tm-title {
963
+ font-size: 1.5rem;
964
+ }
965
+
966
+ .tm-stage-wrapper {
967
+ --tm-stage-scale: 0.9;
968
+
969
+ min-height: calc(420px * var(--tm-stage-scale));
970
+ }
971
+
972
+ .tm-stage {
973
+ transform: scale(var(--tm-stage-scale));
974
+ margin-bottom: calc((420px * var(--tm-stage-scale)) - 420px);
975
+ }
976
+
977
+ .tm-info-bar {
978
+ flex-direction: column;
979
+ align-items: stretch;
980
+ gap: 0.5rem;
981
+ padding-top: 0.75rem;
982
+ }
983
+
984
+ .tm-info-left,
985
+ .tm-info-right {
986
+ justify-content: space-between;
987
+ gap: 0.5rem;
988
+ }
989
+
990
+ .tm-vibration-panel,
991
+ .tm-log-section {
992
+ margin-top: 1rem;
993
+ border-radius: 0.75rem;
994
+ padding: 0.875rem;
995
+ }
996
+
997
+ .tm-vibration-btns,
998
+ .tm-results-btns {
999
+ display: grid;
1000
+ grid-template-columns: 1fr;
1001
+ gap: 0.75rem;
1002
+ }
1003
+
1004
+ .tm-vib-btn,
1005
+ .tm-restart-btn,
1006
+ .tm-share-btn {
1007
+ width: 100%;
1008
+ }
1009
+
1010
+ .tm-game-card {
1011
+ min-height: 420px;
1012
+ }
1013
+
1014
+ .tm-game-icon {
1015
+ width: 4rem;
1016
+ height: 4rem;
1017
+ margin-bottom: 1rem;
1018
+ }
1019
+
1020
+ .tm-game-intro-title {
1021
+ font-size: 1.65rem;
1022
+ }
1023
+
1024
+ .tm-game-intro-desc {
1025
+ margin-bottom: 1.5rem;
1026
+ font-size: 0.95rem;
1027
+ }
1028
+
1029
+ .tm-game-start-btn {
1030
+ width: 100%;
1031
+ padding: 1rem;
1032
+ font-size: 1rem;
1033
+ }
1034
+
1035
+ .tm-game-hud {
1036
+ margin-bottom: 1.5rem;
1037
+ }
1038
+
1039
+ .tm-hud-label {
1040
+ font-size: 0.55rem;
1041
+ }
1042
+
1043
+ .tm-hud-value {
1044
+ font-size: 1.5rem;
1045
+ }
1046
+
1047
+ .tm-target-container {
1048
+ width: min(15rem, 78vw);
1049
+ height: min(15rem, 78vw);
1050
+ }
1051
+
1052
+ .tm-target-btn {
1053
+ width: min(8rem, 45vw);
1054
+ height: min(8rem, 45vw);
1055
+ font-size: 2.5rem;
1056
+ }
1057
+
1058
+ .tm-game-instruction {
1059
+ margin-top: 1.5rem;
1060
+ border-radius: 0.75rem;
1061
+ padding: 0.75rem 1rem;
1062
+ }
1063
+
1064
+ .tm-final-score {
1065
+ font-size: 4rem;
1066
+ }
1067
+ }
1068
+
1069
+ @media (max-width: 560px) {
1070
+ .tm-stage-wrapper {
1071
+ --tm-stage-scale: 0.82;
1072
+ }
1073
+ }
1074
+
1075
+ @media (max-width: 500px) {
1076
+ .tm-stage-wrapper {
1077
+ --tm-stage-scale: 0.72;
1078
+ }
1079
+ }
1080
+
1081
+ @media (max-width: 440px) {
1082
+ .tm-stage-wrapper {
1083
+ --tm-stage-scale: 0.62;
1084
+ }
1085
+ }
1086
+
1087
+ @media (max-width: 380px) {
1088
+ .tm-stage-wrapper {
1089
+ --tm-stage-scale: 0.54;
1090
+ }
1091
+ }
1092
+
925
1093
  @media (min-width: 640px) {
926
1094
  .tm-results-btns { flex-direction: row; }
927
1095
  }
@@ -964,4 +1132,4 @@ body.is-widget .tm-wrapper {
964
1132
 
965
1133
  @keyframes pulse {
966
1134
  50% { opacity: 0.5; }
967
- }
1135
+ }
@@ -158,16 +158,16 @@ export const content: ToolLocaleContent<TestMandoUI> = {
158
158
  gamePress: 'PULSA',
159
159
  rankLegendaryName: 'LEGENDARIO',
160
160
  rankLegendaryDesc: 'Tus dedos se mueven a la velocidad del sonido.',
161
- rankLegendaryFlavor: 'Mi mando vuela!',
161
+ rankLegendaryFlavor: '¡Mi mando vuela!',
162
162
  rankProName: 'PRO GAMER',
163
163
  rankProDesc: 'Tienes reflejos de acero y un mando bien calibrado.',
164
- rankProFlavor: 'Mi mando vuela!',
164
+ rankProFlavor: '¡Mi mando vuela!',
165
165
  rankGamerName: 'GAMER',
166
166
  rankGamerDesc: 'No está mal, pero para el competitivo te falta calle.',
167
- rankGamerFlavor: 'Casi lo tengo!',
167
+ rankGamerFlavor: '¡Casi lo tengo!',
168
168
  rankNoobName: 'MANCO',
169
169
  rankNoobDesc: '¿Seguro que tienes el mando encendido?',
170
- rankNoobFlavor: 'El mando me tiene manía!',
170
+ rankNoobFlavor: '¡El mando me tiene manía!',
171
171
  gameShareText: '¿Me superas?',
172
172
  gameShareScorePrefix: 'Hice',
173
173
  gameShareScoreSuffix: 'aciertos',
@@ -14,7 +14,7 @@ const faqData = [
14
14
  },
15
15
  {
16
16
  question: 'Как исправить дрифт программно?',
17
- answer: 'Если дрифт незначительный, вы можете настроить увеличенную «Мертвую зону» (Deadzone) в настройках игры. Это позволит игнорировать небольшие движения стика в центре.',
17
+ answer: 'Если дрифт незначительный, вы можете настроить увеличенную "Мертвую зону" (Deadzone) в настройках игры. Это позволит игнорировать небольшие движения стика в центре.',
18
18
  },
19
19
  {
20
20
  question: 'Совместим ли тест с контроллерами PS5, Xbox и Switch?',
@@ -14,7 +14,7 @@ const faqData = [
14
14
  },
15
15
  {
16
16
  question: '我可以通过软件修复漂移吗?',
17
- answer: '如果漂移较轻,您可以在游戏设置中配置更大的“死区 (Dead Zone)”。这将忽略中心位置的微小摇杆移动。',
17
+ answer: '如果漂移较轻,您可以在游戏设置中配置更大的"死区 (Dead Zone)"。这将忽略中心位置的微小摇杆移动。',
18
18
  },
19
19
  {
20
20
  question: '是否兼容 PS5、Xbox 和 Switch 控制器?',
@@ -113,7 +113,7 @@ const t = (ui ?? {}) as ProbadorVibracionMandoUI;
113
113
 
114
114
  <div class="pv-slider-group">
115
115
  <div class="pv-slider-header">
116
- <span>{t.customStrongLabel}</span>
116
+ <label for="pv-inp-str">{t.customStrongLabel}</label>
117
117
  <span id="pv-sl-str" class="pv-slider-val">100%</span>
118
118
  </div>
119
119
  <input type="range" class="pv-slider" id="pv-inp-str" min="0" max="1" step="0.05" value="1.0" />
@@ -121,7 +121,7 @@ const t = (ui ?? {}) as ProbadorVibracionMandoUI;
121
121
 
122
122
  <div class="pv-slider-group">
123
123
  <div class="pv-slider-header">
124
- <span>{t.customWeakLabel}</span>
124
+ <label for="pv-inp-wk">{t.customWeakLabel}</label>
125
125
  <span id="pv-sl-wk" class="pv-slider-val">100%</span>
126
126
  </div>
127
127
  <input type="range" class="pv-slider" id="pv-inp-wk" min="0" max="1" step="0.05" value="1.0" />
@@ -129,7 +129,7 @@ const t = (ui ?? {}) as ProbadorVibracionMandoUI;
129
129
 
130
130
  <div class="pv-slider-group">
131
131
  <div class="pv-slider-header">
132
- <span>{t.customDurationLabel}</span>
132
+ <label for="pv-inp-dur">{t.customDurationLabel}</label>
133
133
  <span id="pv-sl-dur" class="pv-slider-val">500ms</span>
134
134
  </div>
135
135
  <input type="range" class="pv-slider" id="pv-inp-dur" min="50" max="2500" step="50" value="500" />
@@ -133,7 +133,7 @@ export const content: ToolLocaleContent<ProbadorVibracionMandoUI> = {
133
133
  presetHeartbeatDesc: 'Palpitaciones sutiles consecutivas. Perfecto para ver retenciones inerciales.',
134
134
  presetSongTitle: 'Ritmo Moneda',
135
135
  presetSongDesc: 'Simula el ruido de monedas consecutivas. Corto y táctil.',
136
- presetRockYouTitle: 'Rock You!',
136
+ presetRockYouTitle: '¡Rock You!',
137
137
  presetRockYouDesc: "El clásico de Queen en tus manos: pum, pum, ¡clap!",
138
138
  presetEarthquakeTitle: '¡Terremoto Máximo!',
139
139
  presetEarthquakeDesc: 'Ambos motores al 100% de fuerza explosiva. MUY intenso.',
@@ -106,7 +106,7 @@ export const content: ToolLocaleContent<ProbadorVibracionMandoUI> = {
106
106
  { type: 'title', text: 'Architecture Dual-Rumble vs. Actionneurs Linéaires', level: 3 },
107
107
  {
108
108
  type: 'paragraph',
109
- html: "Les manettes classiques (Xbox, DualShock) utilisent deux micromoteurs asymétriques : le gauche génère des vibrations graves et profondes ; le droit produit des bourdonnements aigus et rapides. Les appareils avancés comme le DualSense utilisent des actionneurs linéaires qui simulent textures et résistance.",
109
+ html: "Les manettes classiques (Xbox, DualShock) utilisent deux micromoteurs asymétriques: le gauche génère des vibrations graves et profondes ; le droit produit des bourdonnements aigus et rapides. Les appareils avancés comme le DualSense utilisent des actionneurs linéaires qui simulent textures et résistance.",
110
110
  },
111
111
  ],
112
112
  ui: {
@@ -129,7 +129,7 @@ export const content: ToolLocaleContent<ProbadorVibracionMandoUI> = {
129
129
  presetSongTitle: 'Rythme Pièce',
130
130
  presetSongDesc: 'Simule le son de pièces collectées. Court et tactile.',
131
131
  presetRockYouTitle: 'Rock You!',
132
- presetRockYouDesc: "Le classique de Queen dans vos mains : boum, boum, clap !",
132
+ presetRockYouDesc: "Le classique de Queen dans vos mains: boum, boum, clap !",
133
133
  presetEarthquakeTitle: 'Tremblement Maximal !',
134
134
  presetEarthquakeDesc: 'Les deux moteurs à 100% de force explosive. TRÈS intense.',
135
135
  customStrongLabel: 'Force Grave (Gauche)',
@@ -187,6 +187,11 @@ const testedClass = 'tt-key-tested';
187
187
  const activeKeys = new Set<string>();
188
188
  let maxSimultaneous = 0;
189
189
 
190
+ function scrollLogToEnd(el: HTMLElement) {
191
+ void el.offsetWidth;
192
+ el.scrollLeft = el.scrollWidth;
193
+ }
194
+
190
195
  const keyMap = new Map<string, Element>();
191
196
  keys.forEach((k) => {
192
197
  const code = k.getAttribute('data-code');
@@ -199,7 +204,7 @@ const testedClass = 'tt-key-tested';
199
204
  div.textContent = `${msg}`;
200
205
  eventLog.appendChild(div);
201
206
  if (eventLog.children.length > 50) eventLog.firstElementChild?.remove();
202
- eventLog.scrollLeft = eventLog.scrollWidth;
207
+ scrollLogToEnd(eventLog);
203
208
  }
204
209
 
205
210
  function handleDown(e: KeyboardEvent) {