@jjlmoya/utils-hardware 1.16.0 → 1.18.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 (111) 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 +6 -6
  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/no_h1_in_components.test.ts +1 -1
  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/bibliography.ts +8 -8
  24. package/src/tool/batteryHealthEstimator/i18n/de.ts +1 -1
  25. package/src/tool/batteryHealthEstimator/i18n/en.ts +1 -1
  26. package/src/tool/batteryHealthEstimator/i18n/fr.ts +5 -5
  27. package/src/tool/batteryHealthEstimator/i18n/id.ts +1 -1
  28. package/src/tool/batteryHealthEstimator/i18n/nl.ts +1 -1
  29. package/src/tool/batteryHealthEstimator/i18n/pl.ts +2 -2
  30. package/src/tool/batteryHealthEstimator/i18n/pt.ts +1 -1
  31. package/src/tool/batteryHealthEstimator/i18n/ru.ts +5 -5
  32. package/src/tool/batteryHealthEstimator/i18n/sv.ts +1 -1
  33. package/src/tool/batteryHealthEstimator/i18n/tr.ts +1 -1
  34. package/src/tool/batteryHealthEstimator/i18n/zh.ts +1 -1
  35. package/src/tool/colorAccuracyTest/bibliography.astro +14 -0
  36. package/src/tool/colorAccuracyTest/bibliography.ts +16 -0
  37. package/src/tool/colorAccuracyTest/color-accuracy-test.css +841 -0
  38. package/src/tool/colorAccuracyTest/component.astro +157 -0
  39. package/src/tool/colorAccuracyTest/entry.ts +29 -0
  40. package/src/tool/colorAccuracyTest/i18n/de.ts +284 -0
  41. package/src/tool/colorAccuracyTest/i18n/en.ts +284 -0
  42. package/src/tool/colorAccuracyTest/i18n/es.ts +284 -0
  43. package/src/tool/colorAccuracyTest/i18n/fr.ts +284 -0
  44. package/src/tool/colorAccuracyTest/i18n/id.ts +284 -0
  45. package/src/tool/colorAccuracyTest/i18n/it.ts +284 -0
  46. package/src/tool/colorAccuracyTest/i18n/ja.ts +284 -0
  47. package/src/tool/colorAccuracyTest/i18n/ko.ts +284 -0
  48. package/src/tool/colorAccuracyTest/i18n/nl.ts +284 -0
  49. package/src/tool/colorAccuracyTest/i18n/pl.ts +284 -0
  50. package/src/tool/colorAccuracyTest/i18n/pt.ts +284 -0
  51. package/src/tool/colorAccuracyTest/i18n/ru.ts +284 -0
  52. package/src/tool/colorAccuracyTest/i18n/sv.ts +284 -0
  53. package/src/tool/colorAccuracyTest/i18n/tr.ts +284 -0
  54. package/src/tool/colorAccuracyTest/i18n/zh.ts +284 -0
  55. package/src/tool/colorAccuracyTest/index.ts +9 -0
  56. package/src/tool/colorAccuracyTest/logic.ts +226 -0
  57. package/src/tool/colorAccuracyTest/seo.astro +15 -0
  58. package/src/tool/colorAccuracyTest/ui.ts +27 -0
  59. package/src/tool/deadPixelTest/i18n/ru.ts +6 -6
  60. package/src/tool/deadPixelTest/i18n/sv.ts +1 -1
  61. package/src/tool/deadPixelTest/i18n/zh.ts +5 -5
  62. package/src/tool/gamepadTest/gamepad-test.css +171 -3
  63. package/src/tool/gamepadTest/i18n/es.ts +4 -4
  64. package/src/tool/gamepadTest/i18n/ru.ts +2 -2
  65. package/src/tool/gamepadTest/i18n/zh.ts +1 -1
  66. package/src/tool/gamepadVibrationTester/bibliography.ts +8 -8
  67. package/src/tool/gamepadVibrationTester/i18n/es.ts +1 -1
  68. package/src/tool/gamepadVibrationTester/i18n/fr.ts +2 -2
  69. package/src/tool/gamepadVibrationTester/i18n/ru.ts +3 -3
  70. package/src/tool/keyboardTest/keyboard-test.css +115 -2
  71. package/src/tool/mousePollingTest/bibliography.ts +8 -8
  72. package/src/tool/mousePollingTest/i18n/de.ts +1 -1
  73. package/src/tool/mousePollingTest/i18n/en.ts +1 -1
  74. package/src/tool/mousePollingTest/i18n/es.ts +1 -1
  75. package/src/tool/mousePollingTest/i18n/fr.ts +3 -3
  76. package/src/tool/mousePollingTest/i18n/id.ts +1 -1
  77. package/src/tool/mousePollingTest/i18n/it.ts +1 -1
  78. package/src/tool/mousePollingTest/i18n/ja.ts +1 -1
  79. package/src/tool/mousePollingTest/i18n/ko.ts +1 -1
  80. package/src/tool/mousePollingTest/i18n/nl.ts +1 -1
  81. package/src/tool/mousePollingTest/i18n/pl.ts +2 -2
  82. package/src/tool/mousePollingTest/i18n/pt.ts +1 -1
  83. package/src/tool/mousePollingTest/i18n/ru.ts +3 -3
  84. package/src/tool/mousePollingTest/i18n/sv.ts +1 -1
  85. package/src/tool/mousePollingTest/i18n/tr.ts +1 -1
  86. package/src/tool/mousePollingTest/i18n/zh.ts +2 -2
  87. package/src/tool/refreshRateDetector/bibliography.ts +3 -11
  88. package/src/tool/refreshRateDetector/i18n/de.ts +3 -3
  89. package/src/tool/refreshRateDetector/i18n/en.ts +3 -3
  90. package/src/tool/refreshRateDetector/i18n/fr.ts +7 -7
  91. package/src/tool/refreshRateDetector/i18n/id.ts +3 -3
  92. package/src/tool/refreshRateDetector/i18n/nl.ts +3 -3
  93. package/src/tool/refreshRateDetector/i18n/pl.ts +2 -2
  94. package/src/tool/refreshRateDetector/i18n/pt.ts +3 -3
  95. package/src/tool/refreshRateDetector/i18n/ru.ts +4 -4
  96. package/src/tool/refreshRateDetector/i18n/sv.ts +3 -3
  97. package/src/tool/refreshRateDetector/i18n/tr.ts +2 -2
  98. package/src/tool/refreshRateDetector/i18n/zh.ts +2 -2
  99. package/src/tool/toneGenerator/bibliography.ts +8 -8
  100. package/src/tool/toneGenerator/i18n/de.ts +1 -1
  101. package/src/tool/toneGenerator/i18n/en.ts +1 -1
  102. package/src/tool/toneGenerator/i18n/fr.ts +3 -3
  103. package/src/tool/toneGenerator/i18n/id.ts +1 -1
  104. package/src/tool/toneGenerator/i18n/it.ts +1 -1
  105. package/src/tool/toneGenerator/i18n/nl.ts +1 -1
  106. package/src/tool/toneGenerator/i18n/pl.ts +2 -2
  107. package/src/tool/toneGenerator/i18n/pt.ts +1 -1
  108. package/src/tool/toneGenerator/i18n/ru.ts +4 -4
  109. package/src/tool/toneGenerator/i18n/sv.ts +1 -1
  110. package/src/tool/toneGenerator/i18n/zh.ts +3 -3
  111. package/src/tools.ts +2 -1
@@ -0,0 +1,226 @@
1
+ export interface ColorTest {
2
+ name: string;
3
+ description: string;
4
+ instruction: string;
5
+ colors: string[];
6
+ }
7
+
8
+ export const SRGB_TESTS: ColorTest[] = [
9
+ {
10
+ name: 'Spectral Purity',
11
+ description: 'Primary & secondary colors',
12
+ instruction: 'Watch for consistent, pure colors. Any color shift or banding indicates display inaccuracy.',
13
+ colors: ['#000', '#fff', '#f00', '#0f0', '#00f', '#ff0', '#0ff', '#f0f'],
14
+ },
15
+ {
16
+ name: 'Gradient Dynamics',
17
+ description: 'Smooth color transitions',
18
+ instruction: 'Look for banding (visible steps) instead of smooth gradients. Banding = poor color depth or calibration.',
19
+ colors: [],
20
+ },
21
+ {
22
+ name: 'Black Crush Detection',
23
+ description: 'Shadow detail test',
24
+ instruction: 'Can you see all 6 shades? If blacks blur together, your display is crushing shadow details.',
25
+ colors: ['#000', '#111', '#222', '#333', '#444', '#555'],
26
+ },
27
+ {
28
+ name: 'White Point Calibration',
29
+ description: 'Brightness & temperature',
30
+ instruction: 'Check for color tint in whites. A neutral white has no warm (yellow) or cool (blue) cast.',
31
+ colors: ['#fff', '#f5f5f5', '#e8e8e8', '#d0d0d0', '#b0b0b0'],
32
+ },
33
+ ];
34
+
35
+ export const DCIP3_TESTS: ColorTest[] = [
36
+ {
37
+ name: 'Spectral Purity (DCI-P3)',
38
+ description: 'Cinema gamut colors',
39
+ instruction: 'DCI-P3 covers more colors than sRGB. Watch for vibrant, saturated colors without clipping.',
40
+ colors: ['#000', '#fff', '#ff0000', '#00ff00', '#0000ff'],
41
+ },
42
+ {
43
+ name: 'Gradient Dynamics',
44
+ description: 'Smooth color transitions',
45
+ instruction: 'Cinema displays should show silky-smooth gradients. Any stepping = insufficient bit depth.',
46
+ colors: [],
47
+ },
48
+ {
49
+ name: 'Black Crush Detection',
50
+ description: 'Shadow detail test',
51
+ instruction: 'Can you see all 6 shades? If blacks blur together, your display is crushing shadow details.',
52
+ colors: ['#000', '#111', '#222', '#333', '#444', '#555'],
53
+ },
54
+ {
55
+ name: 'White Point Calibration',
56
+ description: 'Brightness & temperature',
57
+ instruction: 'Check for color tint in whites. A neutral white has no warm (yellow) or cool (blue) cast.',
58
+ colors: ['#fff', '#f5f5f5', '#e8e8e8', '#d0d0d0', '#b0b0b0'],
59
+ },
60
+ ];
61
+
62
+ export class SpectrumCanvasTest {
63
+ private currentTestIndex = 0;
64
+ private currentColorIndex = 0;
65
+ private gamut: 'srgb' | 'dcip3' = 'srgb';
66
+ private tests: ColorTest[] = SRGB_TESTS;
67
+ private testBg: HTMLElement | null = null;
68
+ private testName: HTMLElement | null = null;
69
+ private testDescription: HTMLElement | null = null;
70
+ private progressBar: HTMLElement | null = null;
71
+ private testOverlay: HTMLElement | null = null;
72
+ private dashboard: HTMLElement | null = null;
73
+
74
+ constructor() {
75
+ this.testBg = document.getElementById('test-bg');
76
+ this.testName = document.getElementById('test-name');
77
+ this.testDescription = document.getElementById('test-description');
78
+ this.progressBar = document.querySelector('.sc-progress-bar');
79
+ this.testOverlay = document.getElementById('test-overlay');
80
+ this.dashboard = document.getElementById('dashboard');
81
+ }
82
+
83
+ start(gamut: 'srgb' | 'dcip3' = 'srgb') {
84
+ this.gamut = gamut;
85
+ this.tests = gamut === 'dcip3' ? DCIP3_TESTS : SRGB_TESTS;
86
+ this.currentTestIndex = 0;
87
+ this.currentColorIndex = 0;
88
+ this.showCurrentTest();
89
+ this.attachKeyboardListeners();
90
+ }
91
+
92
+ private showCurrentTest() {
93
+ const test = this.tests[this.currentTestIndex];
94
+ if (!test) return;
95
+
96
+ if (this.testName) {
97
+ this.testName.textContent = test.name;
98
+ }
99
+
100
+ if (this.testDescription) {
101
+ this.testDescription.textContent = test.instruction;
102
+ }
103
+
104
+ this.updateProgress();
105
+ this.displayColor();
106
+
107
+ const win = window as Record<string, unknown>;
108
+ if (win.updateTestUI) {
109
+ (win.updateTestUI as () => void)();
110
+ }
111
+ }
112
+
113
+ private displayColor() {
114
+ const test = this.tests[this.currentTestIndex];
115
+ if (!test || !this.testBg) return;
116
+
117
+ if (test.colors.length === 0) {
118
+ this.testBg.style.backgroundColor = '';
119
+ this.testBg.style.background =
120
+ 'linear-gradient(90deg, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00)';
121
+ } else {
122
+ this.testBg.style.background = '';
123
+ const color = test.colors[this.currentColorIndex % test.colors.length];
124
+ this.testBg.style.backgroundColor = color;
125
+ }
126
+ }
127
+
128
+ private updateProgress() {
129
+ const totalTests = this.tests.length;
130
+ const progress = ((this.currentTestIndex + 1) / totalTests) * 100;
131
+ if (this.progressBar) {
132
+ this.progressBar.style.width = `${progress}%`;
133
+ }
134
+ }
135
+
136
+ nextColor() {
137
+ const test = this.tests[this.currentTestIndex];
138
+
139
+ if (!test || test.colors.length === 0) {
140
+ this.nextTest();
141
+ return;
142
+ }
143
+
144
+ this.currentColorIndex++;
145
+
146
+ if (this.currentColorIndex >= test.colors.length) {
147
+ this.nextTest();
148
+ } else {
149
+ this.displayColor();
150
+ }
151
+ }
152
+
153
+ prevColor() {
154
+ const test = this.tests[this.currentTestIndex];
155
+
156
+ if (!test || test.colors.length === 0) {
157
+ if (this.currentTestIndex === 0) return;
158
+ this.currentTestIndex--;
159
+ this.currentColorIndex = 0;
160
+ this.showCurrentTest();
161
+ return;
162
+ }
163
+
164
+ this.currentColorIndex--;
165
+
166
+ if (this.currentColorIndex < 0) {
167
+ if (this.currentTestIndex === 0) {
168
+ this.currentColorIndex = 0;
169
+ return;
170
+ }
171
+ this.currentTestIndex--;
172
+ const prevTest = this.tests[this.currentTestIndex];
173
+ this.currentColorIndex = prevTest.colors.length - 1;
174
+ this.showCurrentTest();
175
+ } else {
176
+ this.displayColor();
177
+ }
178
+ }
179
+
180
+ nextTest() {
181
+ this.currentTestIndex++;
182
+ this.currentColorIndex = 0;
183
+
184
+ if (this.currentTestIndex >= this.tests.length) {
185
+ setTimeout(() => this.finish(), 300);
186
+ } else {
187
+ this.showCurrentTest();
188
+ }
189
+ }
190
+
191
+ reset() {
192
+ this.currentTestIndex = 0;
193
+ this.currentColorIndex = 0;
194
+ this.showCurrentTest();
195
+ }
196
+
197
+ finish() {
198
+ if (this.testOverlay) {
199
+ this.testOverlay.style.display = 'none';
200
+ }
201
+ if (this.testBg) {
202
+ this.testBg.style.backgroundColor = '';
203
+ this.testBg.style.background = '';
204
+ }
205
+ this.currentTestIndex = 0;
206
+ this.currentColorIndex = 0;
207
+ }
208
+
209
+ private attachKeyboardListeners() {
210
+ const handleKeydown = (e: KeyboardEvent) => {
211
+ if (e.code === 'Space') {
212
+ e.preventDefault();
213
+ this.nextColor();
214
+ } else if (e.key === 'r' || e.key === 'R') {
215
+ e.preventDefault();
216
+ this.reset();
217
+ } else if (e.key === 'Escape') {
218
+ e.preventDefault();
219
+ this.finish();
220
+ document.removeEventListener('keydown', handleKeydown);
221
+ }
222
+ };
223
+
224
+ document.addEventListener('keydown', handleKeydown);
225
+ }
226
+ }
@@ -0,0 +1,15 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { spectrumCanvas } 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 spectrumCanvas.i18n[locale]?.();
12
+ if (!content) return null;
13
+ ---
14
+
15
+ {content.seo?.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
@@ -0,0 +1,27 @@
1
+ export interface SpectrumCanvasUI extends Record<string, string> {
2
+ badge: string;
3
+ title: string;
4
+ description: string;
5
+ btnStartCalibration: string;
6
+ btnFullscreen: string;
7
+ kbdFullscreen: string;
8
+ kbdFullscreenLabel: string;
9
+ kbdReset: string;
10
+ kbdResetLabel: string;
11
+ kbdEsc: string;
12
+ kbdEscLabel: string;
13
+ gamutSRGB: string;
14
+ gamutDCIP3: string;
15
+ gamutToggle: string;
16
+ hardwareName: string;
17
+ hardwareNamePlaceholder: string;
18
+ purityTest: string;
19
+ gradientTest: string;
20
+ blackHoleTest: string;
21
+ whitePointTest: string;
22
+ colorCheckpoint: string;
23
+ generateReport: string;
24
+ viewResults: string;
25
+ btnExit: string;
26
+ compareSideBySide: string;
27
+ }
@@ -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: {
@@ -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?',
@@ -104,7 +104,7 @@ export const content: ToolLocaleContent<TestMandoUI> = {
104
104
  },
105
105
  {
106
106
  type: 'paragraph',
107
- html: 'Дрифт это распространенный дефект аналоговых стиков, при котором регистрируется движение без прикосновения к джойстику. Это вызвано износом внутренних потенциометров.',
107
+ html: 'Дрифт -это распространенный дефект аналоговых стиков, при котором регистрируется движение без прикосновения к джойстику. Это вызвано износом внутренних потенциометров.',
108
108
  },
109
109
  {
110
110
  type: 'title',