@jjlmoya/utils-developer 1.13.0 → 1.15.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 (184) hide show
  1. package/package.json +1 -1
  2. package/src/category/i18n/fr.ts +1 -1
  3. package/src/category/i18n/ru.ts +1 -1
  4. package/src/layouts/PreviewLayout.astro +1 -0
  5. package/src/pages/[locale]/[slug].astro +29 -12
  6. package/src/tests/diacritics_density.test.ts +118 -0
  7. package/src/tests/inverted_punctuation.test.ts +113 -0
  8. package/src/tests/no_en_dash.test.ts +73 -0
  9. package/src/tests/script_density.test.ts +94 -0
  10. package/src/tests/shared-test-helpers.ts +56 -0
  11. package/src/tests/tool_exports.test.ts +34 -0
  12. package/src/tool/aspectRatio/aspect-ratio-calculator.css +1 -1
  13. package/src/tool/aspectRatio/i18n/de.ts +1 -1
  14. package/src/tool/aspectRatio/i18n/fr.ts +1 -1
  15. package/src/tool/aspectRatio/i18n/pl.ts +2 -2
  16. package/src/tool/aspectRatio/i18n/ru.ts +4 -4
  17. package/src/tool/aspectRatio/i18n/zh.ts +1 -1
  18. package/src/tool/aspectRatio/seo.astro +2 -1
  19. package/src/tool/calculadoraTiempoDatos/data-time-calculator-web-speed-impact.css +26 -26
  20. package/src/tool/calculadoraTiempoDatos/i18n/de.ts +1 -1
  21. package/src/tool/calculadoraTiempoDatos/i18n/pl.ts +1 -1
  22. package/src/tool/calculadoraTiempoDatos/i18n/ru.ts +5 -5
  23. package/src/tool/calculadoraTiempoDatos/i18n/sv.ts +5 -5
  24. package/src/tool/calculadoraTiempoDatos/i18n/zh.ts +1 -1
  25. package/src/tool/calculadoraTiempoDatos/seo.astro +2 -1
  26. package/src/tool/colorConverter/color-converter-rgb-hex-hsl.css +9 -9
  27. package/src/tool/colorConverter/i18n/de.ts +1 -1
  28. package/src/tool/colorConverter/i18n/ru.ts +2 -2
  29. package/src/tool/colorConverter/seo.astro +2 -1
  30. package/src/tool/conversorExcelCsvHtml/excel-csv-html-table-converter.css +24 -24
  31. package/src/tool/conversorExcelCsvHtml/i18n/es.ts +1 -1
  32. package/src/tool/conversorExcelCsvHtml/i18n/pl.ts +1 -1
  33. package/src/tool/conversorExcelCsvHtml/i18n/ru.ts +2 -2
  34. package/src/tool/conversorExcelCsvHtml/i18n/zh.ts +2 -2
  35. package/src/tool/conversorExcelCsvHtml/seo.astro +2 -1
  36. package/src/tool/cronGenerator/cron.css +5 -5
  37. package/src/tool/cronGenerator/i18n/de.ts +4 -4
  38. package/src/tool/cronGenerator/i18n/en.ts +1 -1
  39. package/src/tool/cronGenerator/i18n/es.ts +2 -2
  40. package/src/tool/cronGenerator/i18n/fr.ts +5 -5
  41. package/src/tool/cronGenerator/i18n/id.ts +1 -1
  42. package/src/tool/cronGenerator/i18n/nl.ts +1 -1
  43. package/src/tool/cronGenerator/i18n/pl.ts +8 -8
  44. package/src/tool/cronGenerator/i18n/pt.ts +1 -1
  45. package/src/tool/cronGenerator/i18n/ru.ts +9 -9
  46. package/src/tool/cronGenerator/i18n/sv.ts +1 -1
  47. package/src/tool/cronGenerator/i18n/zh.ts +3 -3
  48. package/src/tool/cronGenerator/seo.astro +2 -1
  49. package/src/tool/cssSpecificityCalculator/css-specificity-calculator.css +1 -1
  50. package/src/tool/cssSpecificityCalculator/i18n/de.ts +1 -1
  51. package/src/tool/cssSpecificityCalculator/i18n/fr.ts +3 -3
  52. package/src/tool/cssSpecificityCalculator/i18n/pl.ts +2 -2
  53. package/src/tool/cssSpecificityCalculator/i18n/ru.ts +2 -2
  54. package/src/tool/cssSpecificityCalculator/seo.astro +2 -1
  55. package/src/tool/cssToInlineConverter/css-to-inline-converter.css +1 -1
  56. package/src/tool/cssToInlineConverter/i18n/de.ts +6 -6
  57. package/src/tool/cssToInlineConverter/i18n/es.ts +1 -1
  58. package/src/tool/cssToInlineConverter/i18n/fr.ts +3 -3
  59. package/src/tool/cssToInlineConverter/seo.astro +2 -1
  60. package/src/tool/duplicateCssRemover/duplicate-css-remover.css +2 -2
  61. package/src/tool/duplicateCssRemover/i18n/de.ts +7 -7
  62. package/src/tool/duplicateCssRemover/i18n/fr.ts +1 -1
  63. package/src/tool/duplicateCssRemover/i18n/id.ts +1 -1
  64. package/src/tool/duplicateCssRemover/i18n/nl.ts +1 -1
  65. package/src/tool/duplicateCssRemover/i18n/pl.ts +1 -1
  66. package/src/tool/duplicateCssRemover/i18n/pt.ts +1 -1
  67. package/src/tool/duplicateCssRemover/i18n/ru.ts +3 -3
  68. package/src/tool/duplicateCssRemover/i18n/sv.ts +1 -1
  69. package/src/tool/duplicateCssRemover/seo.astro +2 -1
  70. package/src/tool/generadorSecurityTxt/i18n/de.ts +5 -5
  71. package/src/tool/generadorSecurityTxt/i18n/ru.ts +2 -2
  72. package/src/tool/generadorSecurityTxt/security-txt-generator-rfc-9116.css +9 -9
  73. package/src/tool/generadorSecurityTxt/seo.astro +2 -1
  74. package/src/tool/hashGenerator/hash-generator.css +10 -10
  75. package/src/tool/hashGenerator/i18n/ru.ts +5 -5
  76. package/src/tool/hashGenerator/i18n/zh.ts +2 -2
  77. package/src/tool/hashGenerator/seo.astro +2 -1
  78. package/src/tool/inspectorCertificadosSsl/i18n/de.ts +6 -6
  79. package/src/tool/inspectorCertificadosSsl/i18n/ru.ts +5 -5
  80. package/src/tool/inspectorCertificadosSsl/seo.astro +2 -1
  81. package/src/tool/inspectorCertificadosSsl/ssl-tls-certificate-inspector.css +9 -9
  82. package/src/tool/jsonFormatter/i18n/de.ts +4 -4
  83. package/src/tool/jsonFormatter/i18n/en.ts +4 -4
  84. package/src/tool/jsonFormatter/i18n/es.ts +4 -4
  85. package/src/tool/jsonFormatter/i18n/fr.ts +4 -4
  86. package/src/tool/jsonFormatter/i18n/id.ts +4 -4
  87. package/src/tool/jsonFormatter/i18n/it.ts +4 -4
  88. package/src/tool/jsonFormatter/i18n/ja.ts +4 -4
  89. package/src/tool/jsonFormatter/i18n/ko.ts +4 -4
  90. package/src/tool/jsonFormatter/i18n/nl.ts +4 -4
  91. package/src/tool/jsonFormatter/i18n/pl.ts +4 -4
  92. package/src/tool/jsonFormatter/i18n/pt.ts +4 -4
  93. package/src/tool/jsonFormatter/i18n/ru.ts +5 -5
  94. package/src/tool/jsonFormatter/i18n/sv.ts +4 -4
  95. package/src/tool/jsonFormatter/i18n/tr.ts +4 -4
  96. package/src/tool/jsonFormatter/i18n/zh.ts +5 -5
  97. package/src/tool/jsonFormatter/json-formatter.css +33 -33
  98. package/src/tool/jsonFormatter/seo.astro +2 -1
  99. package/src/tool/keycode/i18n/de.ts +7 -7
  100. package/src/tool/keycode/i18n/en.ts +5 -5
  101. package/src/tool/keycode/i18n/es.ts +5 -5
  102. package/src/tool/keycode/i18n/fr.ts +7 -7
  103. package/src/tool/keycode/i18n/id.ts +5 -5
  104. package/src/tool/keycode/i18n/it.ts +5 -5
  105. package/src/tool/keycode/i18n/ja.ts +5 -5
  106. package/src/tool/keycode/i18n/ko.ts +5 -5
  107. package/src/tool/keycode/i18n/nl.ts +5 -5
  108. package/src/tool/keycode/i18n/pl.ts +7 -7
  109. package/src/tool/keycode/i18n/pt.ts +5 -5
  110. package/src/tool/keycode/i18n/ru.ts +6 -6
  111. package/src/tool/keycode/i18n/sv.ts +5 -5
  112. package/src/tool/keycode/i18n/tr.ts +5 -5
  113. package/src/tool/keycode/i18n/zh.ts +5 -5
  114. package/src/tool/keycode/keycode.css +1 -1
  115. package/src/tool/keycode/seo.astro +2 -1
  116. package/src/tool/llmCostCalculator/i18n/de.ts +4 -4
  117. package/src/tool/llmCostCalculator/i18n/en.ts +3 -3
  118. package/src/tool/llmCostCalculator/i18n/fr.ts +3 -3
  119. package/src/tool/llmCostCalculator/i18n/id.ts +3 -3
  120. package/src/tool/llmCostCalculator/i18n/pt.ts +3 -3
  121. package/src/tool/llmCostCalculator/i18n/ru.ts +3 -3
  122. package/src/tool/llmCostCalculator/i18n/sv.ts +3 -3
  123. package/src/tool/llmCostCalculator/i18n/tr.ts +3 -3
  124. package/src/tool/llmCostCalculator/i18n/zh.ts +3 -3
  125. package/src/tool/llmCostCalculator/llm-cost-calculator.css +1 -1
  126. package/src/tool/llmCostCalculator/seo.astro +2 -1
  127. package/src/tool/mobileMockupGenerator/i18n/es.ts +1 -1
  128. package/src/tool/mobileMockupGenerator/mobile-mockup-generator.css +68 -68
  129. package/src/tool/mobileMockupGenerator/seo.astro +2 -1
  130. package/src/tool/musicalTypography/i18n/de.ts +1 -1
  131. package/src/tool/musicalTypography/i18n/fr.ts +1 -1
  132. package/src/tool/musicalTypography/i18n/id.ts +1 -1
  133. package/src/tool/musicalTypography/i18n/it.ts +1 -1
  134. package/src/tool/musicalTypography/i18n/ko.ts +1 -1
  135. package/src/tool/musicalTypography/i18n/ru.ts +3 -3
  136. package/src/tool/musicalTypography/musical-typography.css +8 -8
  137. package/src/tool/musicalTypography/seo.astro +2 -1
  138. package/src/tool/placeholderGenerator/i18n/de.ts +4 -4
  139. package/src/tool/placeholderGenerator/i18n/ru.ts +1 -1
  140. package/src/tool/placeholderGenerator/placeholder-image-generator.css +2 -2
  141. package/src/tool/placeholderGenerator/seo.astro +2 -1
  142. package/src/tool/promptLibrary/i18n/de.ts +1 -1
  143. package/src/tool/promptLibrary/prompt-library.css +30 -30
  144. package/src/tool/promptLibrary/seo.astro +2 -1
  145. package/src/tool/readabilityCalculator/i18n/de.ts +2 -2
  146. package/src/tool/readabilityCalculator/seo.astro +2 -1
  147. package/src/tool/readabilityCalculator/visual-readability-calculator-wcag-apca.css +98 -14
  148. package/src/tool/svgSanitizer/i18n/de.ts +2 -2
  149. package/src/tool/svgSanitizer/i18n/id.ts +1 -1
  150. package/src/tool/svgSanitizer/i18n/pl.ts +1 -1
  151. package/src/tool/svgSanitizer/i18n/ru.ts +7 -7
  152. package/src/tool/svgSanitizer/i18n/sv.ts +3 -3
  153. package/src/tool/svgSanitizer/i18n/tr.ts +3 -3
  154. package/src/tool/svgSanitizer/i18n/zh.ts +3 -3
  155. package/src/tool/svgSanitizer/seo.astro +2 -1
  156. package/src/tool/svgSanitizer/svg-sanitizer.css +7 -7
  157. package/src/tool/svgToCss/i18n/de.ts +2 -2
  158. package/src/tool/svgToCss/i18n/es.ts +1 -1
  159. package/src/tool/svgToCss/i18n/fr.ts +1 -1
  160. package/src/tool/svgToCss/i18n/pl.ts +1 -1
  161. package/src/tool/svgToCss/i18n/ru.ts +2 -2
  162. package/src/tool/svgToCss/i18n/zh.ts +2 -2
  163. package/src/tool/svgToCss/seo.astro +2 -1
  164. package/src/tool/svgToCss/svg-to-css-converter.css +2 -2
  165. package/src/tool/urlCleaner/component.astro +23 -5
  166. package/src/tool/urlCleaner/i18n/de.ts +3 -3
  167. package/src/tool/urlCleaner/i18n/es.ts +1 -1
  168. package/src/tool/urlCleaner/i18n/id.ts +1 -1
  169. package/src/tool/urlCleaner/i18n/nl.ts +1 -1
  170. package/src/tool/urlCleaner/seo.astro +2 -1
  171. package/src/tool/urlCleaner/url-tracking-cleaner.css +104 -14
  172. package/src/tool/urlEncoderDecoder/i18n/de.ts +5 -5
  173. package/src/tool/urlEncoderDecoder/i18n/fr.ts +2 -2
  174. package/src/tool/urlEncoderDecoder/i18n/pl.ts +5 -5
  175. package/src/tool/urlEncoderDecoder/i18n/ru.ts +8 -8
  176. package/src/tool/urlEncoderDecoder/i18n/sv.ts +5 -5
  177. package/src/tool/urlEncoderDecoder/i18n/zh.ts +5 -5
  178. package/src/tool/urlEncoderDecoder/seo.astro +2 -1
  179. package/src/tool/urlEncoderDecoder/url-encoder-decoder.css +1 -1
  180. package/src/tool/utmGenerator/i18n/de.ts +2 -2
  181. package/src/tool/utmGenerator/i18n/pl.ts +5 -5
  182. package/src/tool/utmGenerator/i18n/ru.ts +6 -6
  183. package/src/tool/utmGenerator/seo.astro +2 -1
  184. package/src/tool/utmGenerator/utm-generator.css +6 -6
@@ -13,7 +13,7 @@ const faqData = [
13
13
  },
14
14
  {
15
15
  question: 'Работает ли это с SVG из Illustrator?',
16
- answer: 'Да. Illustrator экспортирует SVG с XML-объявлениями, метаданными и собственными пространствами имён, которые очиститель удаляет. Результат это SVG, совместимый со всеми современными браузерами.',
16
+ answer: 'Да. Illustrator экспортирует SVG с XML-объявлениями, метаданными и собственными пространствами имён, которые очиститель удаляет. Результат - это SVG, совместимый со всеми современными браузерами.',
17
17
  },
18
18
  {
19
19
  question: 'Какая разница между очисткой и минификацией?',
@@ -21,7 +21,7 @@ const faqData = [
21
21
  },
22
22
  {
23
23
  question: 'Может ли удаление ID-элементов повредить SVG?',
24
- answer: 'Только если элемент в SVG ссылается на другой по ID, например через fill="url(#gradient)". В этом случае отключите опцию «Удалить ID». Она отключена по умолчанию именно чтобы избежать этой проблемы.',
24
+ answer: 'Только если элемент в SVG ссылается на другой по ID, например через fill="url(#gradient)". В этом случае отключите опцию "Удалить ID". Она отключена по умолчанию именно чтобы избежать этой проблемы.',
25
25
  },
26
26
  {
27
27
  question: 'Передаётся ли код SVG на какой-нибудь сервер?',
@@ -32,7 +32,7 @@ const faqData = [
32
32
  const howToData = [
33
33
  { name: 'Вставьте SVG', text: 'Вставьте код SVG, экспортированный из Figma, Illustrator или Inkscape, в текстовое поле.' },
34
34
  { name: 'Настройте параметры', text: 'Включите или отключите опции: удалить ID, убрать ширину/высоту и минифицировать в зависимости от ваших нужд.' },
35
- { name: 'Очистите', text: 'Нажмите «Очистить SVG», чтобы обработать код и получить оптимизированный результат.' },
35
+ { name: 'Очистите', text: 'Нажмите "Очистить SVG", чтобы обработать код и получить оптимизированный результат.' },
36
36
  { name: 'Скопируйте или скачайте', text: 'Используйте кнопки копирования или загрузки, чтобы получить чистый SVG, готовый для продакшена.' },
37
37
  ];
38
38
 
@@ -93,9 +93,9 @@ export const content: ToolLocaleContent<SvgSanitizerUI> = {
93
93
  faq: faqData,
94
94
  bibliographyTitle: 'Ссылки',
95
95
  bibliography: [
96
- { name: 'SVG Specification W3C', url: 'https://www.w3.org/TR/SVG2/' },
97
- { name: 'Figma SVG Export Official Documentation', url: 'https://help.figma.com/hc/en-us/articles/360040028114-Export-from-Figma' },
98
- { name: 'SVGO SVG Optimizer (open source reference)', url: 'https://github.com/svg/svgo' },
96
+ { name: 'SVG Specification - W3C', url: 'https://www.w3.org/TR/SVG2/' },
97
+ { name: 'Figma SVG Export - Official Documentation', url: 'https://help.figma.com/hc/en-us/articles/360040028114-Export-from-Figma' },
98
+ { name: 'SVGO - SVG Optimizer (open source reference)', url: 'https://github.com/svg/svgo' },
99
99
  ],
100
100
  howTo: howToData,
101
101
  schemas: [faqSchema, howToSchema, appSchema],
@@ -108,7 +108,7 @@ export const content: ToolLocaleContent<SvgSanitizerUI> = {
108
108
  { type: 'title', text: 'Почему экспортированные SVG такие грязные?', level: 3 },
109
109
  {
110
110
  type: 'paragraph',
111
- html: 'Когда вы экспортируете SVG из Figma, вы получаете файл, перегруженный атрибутами, которые имеют смысл только внутри приложения: <code>data-name</code>, <code>xml:space</code>, ссылки на внутренние слои и метаданные дизайна. Результат это SVG, весящий на 40-70% больше необходимого.',
111
+ html: 'Когда вы экспортируете SVG из Figma, вы получаете файл, перегруженный атрибутами, которые имеют смысл только внутри приложения: <code>data-name</code>, <code>xml:space</code>, ссылки на внутренние слои и метаданные дизайна. Результат - это SVG, весящий на 40-70% больше необходимого.',
112
112
  },
113
113
  { type: 'title', text: 'Что удаляет очиститель', level: 3 },
114
114
  {
@@ -93,9 +93,9 @@ export const content: ToolLocaleContent<SvgSanitizerUI> = {
93
93
  faq: faqData,
94
94
  bibliographyTitle: 'Referenser',
95
95
  bibliography: [
96
- { name: 'SVG Specification W3C', url: 'https://www.w3.org/TR/SVG2/' },
97
- { name: 'Figma SVG Export Official Documentation', url: 'https://help.figma.com/hc/en-us/articles/360040028114-Export-from-Figma' },
98
- { name: 'SVGO SVG Optimizer (open source reference)', url: 'https://github.com/svg/svgo' },
96
+ { name: 'SVG Specification - W3C', url: 'https://www.w3.org/TR/SVG2/' },
97
+ { name: 'Figma SVG Export - Official Documentation', url: 'https://help.figma.com/hc/en-us/articles/360040028114-Export-from-Figma' },
98
+ { name: 'SVGO - SVG Optimizer (open source reference)', url: 'https://github.com/svg/svgo' },
99
99
  ],
100
100
  howTo: howToData,
101
101
  schemas: [faqSchema, howToSchema, appSchema],
@@ -93,9 +93,9 @@ export const content: ToolLocaleContent<SvgSanitizerUI> = {
93
93
  faq: faqData,
94
94
  bibliographyTitle: 'Referanslar',
95
95
  bibliography: [
96
- { name: 'SVG Specification W3C', url: 'https://www.w3.org/TR/SVG2/' },
97
- { name: 'Figma SVG Export Official Documentation', url: 'https://help.figma.com/hc/en-us/articles/360040028114-Export-from-Figma' },
98
- { name: 'SVGO SVG Optimizer (open source reference)', url: 'https://github.com/svg/svgo' },
96
+ { name: 'SVG Specification - W3C', url: 'https://www.w3.org/TR/SVG2/' },
97
+ { name: 'Figma SVG Export - Official Documentation', url: 'https://help.figma.com/hc/en-us/articles/360040028114-Export-from-Figma' },
98
+ { name: 'SVGO - SVG Optimizer (open source reference)', url: 'https://github.com/svg/svgo' },
99
99
  ],
100
100
  howTo: howToData,
101
101
  schemas: [faqSchema, howToSchema, appSchema],
@@ -93,9 +93,9 @@ export const content: ToolLocaleContent<SvgSanitizerUI> = {
93
93
  faq: faqData,
94
94
  bibliographyTitle: '参考资料',
95
95
  bibliography: [
96
- { name: 'SVG Specification W3C', url: 'https://www.w3.org/TR/SVG2/' },
97
- { name: 'Figma SVG Export Official Documentation', url: 'https://help.figma.com/hc/en-us/articles/360040028114-Export-from-Figma' },
98
- { name: 'SVGO SVG Optimizer (open source reference)', url: 'https://github.com/svg/svgo' },
96
+ { name: 'SVG Specification - W3C', url: 'https://www.w3.org/TR/SVG2/' },
97
+ { name: 'Figma SVG Export - Official Documentation', url: 'https://help.figma.com/hc/en-us/articles/360040028114-Export-from-Figma' },
98
+ { name: 'SVGO - SVG Optimizer (open source reference)', url: 'https://github.com/svg/svgo' },
99
99
  ],
100
100
  howTo: howToData,
101
101
  schemas: [faqSchema, howToSchema, appSchema],
@@ -9,6 +9,7 @@ interface Props {
9
9
 
10
10
  const { locale = 'es' } = Astro.props;
11
11
  const content = await svgSanitizer.i18n[locale]?.();
12
+ if (!content) return null;
12
13
  ---
13
14
 
14
- {content && <SEORenderer content={{ locale, sections: content.seo }} />}
15
+ {content.seo?.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
@@ -30,7 +30,7 @@
30
30
  gap: 1rem;
31
31
  }
32
32
 
33
- :global(.theme-dark) .ss-panel {
33
+ .theme-dark .ss-panel {
34
34
  background: rgba(15, 23, 42, 0.75);
35
35
  border-color: rgba(255, 255, 255, 0.06);
36
36
  }
@@ -43,7 +43,7 @@
43
43
  letter-spacing: 0.06em;
44
44
  }
45
45
 
46
- :global(.theme-dark) .ss-panel-label {
46
+ .theme-dark .ss-panel-label {
47
47
  color: rgb(148, 163, 184);
48
48
  }
49
49
 
@@ -63,7 +63,7 @@
63
63
  box-sizing: border-box;
64
64
  }
65
65
 
66
- :global(.theme-dark) .ss-textarea {
66
+ .theme-dark .ss-textarea {
67
67
  background: rgba(255, 255, 255, 0.04);
68
68
  border-color: rgb(51, 65, 85);
69
69
  color: rgb(203, 213, 225);
@@ -95,7 +95,7 @@
95
95
  user-select: none;
96
96
  }
97
97
 
98
- :global(.theme-dark) .ss-toggle-label {
98
+ .theme-dark .ss-toggle-label {
99
99
  color: rgb(148, 163, 184);
100
100
  }
101
101
 
@@ -199,7 +199,7 @@
199
199
  transition: all 0.2s ease;
200
200
  }
201
201
 
202
- :global(.theme-dark) .ss-btn-action {
202
+ .theme-dark .ss-btn-action {
203
203
  background: rgb(30, 41, 59);
204
204
  border-color: rgb(51, 65, 85);
205
205
  color: rgb(148, 163, 184);
@@ -245,7 +245,7 @@
245
245
  border-top: 2px solid rgb(226, 232, 240);
246
246
  }
247
247
 
248
- :global(.theme-dark) .ss-stat-chip {
248
+ .theme-dark .ss-stat-chip {
249
249
  background: rgba(255, 255, 255, 0.04);
250
250
  border-color: rgb(51, 65, 85);
251
251
  }
@@ -273,7 +273,7 @@
273
273
  line-height: 1.2;
274
274
  }
275
275
 
276
- :global(.theme-dark) .ss-chip-value {
276
+ .theme-dark .ss-chip-value {
277
277
  color: white;
278
278
  }
279
279
 
@@ -16,7 +16,7 @@ const faqData = [
16
16
  {
17
17
  question: 'Wie ändere ich die Farbe eines in CSS eingebetteten SVGs?',
18
18
  answer:
19
- 'Der beste Weg ist über mask-image“. Indem Sie das SVG als Maske definieren, können Sie die background-color verwenden, um die Farbe dynamisch zu ändern, sogar in :hover-Zuständen.',
19
+ 'Der beste Weg ist über "mask-image". Indem Sie das SVG als Maske definieren, können Sie die "background-color" verwenden, um die Farbe dynamisch zu ändern, sogar in :hover-Zuständen.',
20
20
  },
21
21
  {
22
22
  question: 'Welche Browser unterstützen CSS-Masken?',
@@ -46,7 +46,7 @@ const howToData = [
46
46
  },
47
47
  {
48
48
  name: 'Ergebnis kopieren',
49
- text: 'Klicken Sie auf Kopieren“, um den generierten CSS-Code in Ihre Zwischenablage zu übernehmen.',
49
+ text: 'Klicken Sie auf "Kopieren", um den generierten CSS-Code in Ihre Zwischenablage zu übernehmen.',
50
50
  },
51
51
  {
52
52
  name: 'In Ihr Projekt übernehmen',
@@ -9,7 +9,7 @@ const description =
9
9
 
10
10
  const faqData = [
11
11
  {
12
- question: '¿Es mejor usar Data URI o un archivo .svg externo?',
12
+ question: '¿Es mejor usar Data URI o un archivo SVG externo?',
13
13
  answer:
14
14
  'Depende del uso. Los Data URIs eliminan peticiones HTTP (ideal para iconos pequeños), pero aumentan el tamaño del CSS. Para ilustraciones grandes o ricas en detalles, es preferible un archivo externo.',
15
15
  },
@@ -164,7 +164,7 @@ export const content: ToolLocaleContent<SvgToCssUI> = {
164
164
  },
165
165
  {
166
166
  type: 'paragraph',
167
- html: "Beaucoup de développeurs utilisent simplement <code>background-image</code> pour intégrer des vecteurs, mais cela a une limitation : vous ne pouvez pas changer la couleur du SVG dynamiquement depuis le CSS.",
167
+ html: "Beaucoup de développeurs utilisent simplement <code>background-image</code> pour intégrer des vecteurs, mais cela a une limitation: vous ne pouvez pas changer la couleur du SVG dynamiquement depuis le CSS.",
168
168
  },
169
169
  {
170
170
  type: 'paragraph',
@@ -46,7 +46,7 @@ const howToData = [
46
46
  },
47
47
  {
48
48
  name: 'Skopiuj wynik',
49
- text: 'Kliknij Kopiuj”, aby pobrać wygenerowany kod CSS do schowka.',
49
+ text: 'Kliknij "Kopiuj", aby pobrać wygenerowany kod CSS do schowka.',
50
50
  },
51
51
  {
52
52
  name: 'Zastosuj w swoim projekcie',
@@ -16,7 +16,7 @@ const faqData = [
16
16
  {
17
17
  question: 'Как изменить цвет SVG, встроенного в CSS?',
18
18
  answer:
19
- "Лучший способ через 'mask-image'. Определив SVG как маску, вы можете использовать 'background-color' для динамического изменения его цвета, даже в состояниях :hover.",
19
+ "Лучший способ - через 'mask-image'. Определив SVG как маску, вы можете использовать 'background-color' для динамического изменения его цвета, даже в состояниях :hover.",
20
20
  },
21
21
  {
22
22
  question: ' какие браузеры поддерживают CSS Masks?',
@@ -46,7 +46,7 @@ const howToData = [
46
46
  },
47
47
  {
48
48
  name: 'Скопируйте результат',
49
- text: 'Нажмите «Копировать», чтобы перенести сгенерированный CSS-код в буфер обмена.',
49
+ text: 'Нажмите "Копировать", чтобы перенести сгенерированный CSS-код в буфер обмена.',
50
50
  },
51
51
  {
52
52
  name: 'Примените в своем проекте',
@@ -16,7 +16,7 @@ const faqData = [
16
16
  {
17
17
  question: '如何更改嵌入 CSS 的 SVG 的颜色?',
18
18
  answer:
19
- '最好的方法是通过“mask-image”。通过将 SVG 定义为遮罩(mask),您可以使用“background-color”动态更改其颜色,甚至在 :hover 状态下也可以。',
19
+ '最好的方法是通过"mask-image"。通过将 SVG 定义为遮罩(mask),您可以使用"background-color"动态更改其颜色,甚至在 :hover 状态下也可以。',
20
20
  },
21
21
  {
22
22
  question: '哪些浏览器支持 CSS 遮罩(Masks)?',
@@ -46,7 +46,7 @@ const howToData = [
46
46
  },
47
47
  {
48
48
  name: '复制结果',
49
- text: '点击“复制”将生成的 CSS 代码获取到剪贴板中。',
49
+ text: '点击"复制"将生成的 CSS 代码获取到剪贴板中。',
50
50
  },
51
51
  {
52
52
  name: '在项目中使用',
@@ -9,6 +9,7 @@ interface Props {
9
9
 
10
10
  const { locale = 'es' } = Astro.props;
11
11
  const content = await svgToCss.i18n[locale]?.();
12
+ if (!content) return null;
12
13
  ---
13
14
 
14
- {content && <SEORenderer content={{ locale, sections: content.seo }} />}
15
+ {content.seo?.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
@@ -13,7 +13,7 @@
13
13
  color: var(--stc-text);
14
14
  }
15
15
 
16
- :global(.theme-dark) .stc-container {
16
+ .theme-dark .stc-container {
17
17
  --stc-card-bg: #1e293b;
18
18
  --stc-text: #f8fafc;
19
19
  --stc-text-muted: #94a3b8;
@@ -187,7 +187,7 @@
187
187
  transition: all 0.2s;
188
188
  }
189
189
 
190
- :global(.stc-tab-btn.active) {
190
+ .stc-tab-btn.active {
191
191
  background: var(--stc-card-bg);
192
192
  color: var(--stc-primary);
193
193
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
@@ -15,7 +15,7 @@ const t = (ui ?? {}) as UrlCleanerUI;
15
15
  <label for="uc-url-input" class="uc-label">{t.labelInput}</label>
16
16
  <div class="uc-input-group">
17
17
  <input type="url" id="uc-url-input" class="uc-input" placeholder="https://example.com?utm_source=twitter&fbclid=123..." />
18
- <button id="uc-btn-clean" class="uc-btn-primary">
18
+ <button id="uc-btn-clean" class="uc-btn-primary" type="button">
19
19
  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M19.36 2.72L20.78 4.14L15.06 9.85C16.13 11.39 16.28 13.24 15.38 14.44L9.06 8.12C10.26 7.22 12.11 7.38 13.65 8.44L19.36 2.72M5.93 17.57C3.92 15.56 2.69 13.16 2.35 10.92L7.23 8.83L14.67 16.27L12.58 21.15C10.34 20.81 7.94 19.58 5.93 17.57Z"/></svg>
20
20
  {t.btnClean}
21
21
  </button>
@@ -26,7 +26,7 @@ const t = (ui ?? {}) as UrlCleanerUI;
26
26
  <div class="uc-result-card">
27
27
  <div class="uc-card-header">
28
28
  <h3 class="uc-card-title">{t.labelCleaned}</h3>
29
- <button id="uc-btn-copy" class="uc-copy-btn">
29
+ <button id="uc-btn-copy" class="uc-copy-btn" type="button">
30
30
  <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/></svg>
31
31
  {t.btnCopy}
32
32
  </button>
@@ -161,14 +161,32 @@ const t = (ui ?? {}) as UrlCleanerUI;
161
161
  if (e.key === 'Enter') cleanUrl();
162
162
  });
163
163
 
164
+ async function copyText(text: string): Promise<void> {
165
+ if (navigator.clipboard?.writeText) {
166
+ await navigator.clipboard.writeText(text);
167
+ return;
168
+ }
169
+
170
+ const textarea = document.createElement('textarea');
171
+ textarea.value = text;
172
+ textarea.setAttribute('readonly', '');
173
+ textarea.style.position = 'fixed';
174
+ textarea.style.inset = '0 auto auto 0';
175
+ textarea.style.opacity = '0';
176
+ document.body.appendChild(textarea);
177
+ textarea.select();
178
+ document.execCommand('copy');
179
+ textarea.remove();
180
+ }
181
+
164
182
  copyBtn.addEventListener('click', () => {
165
183
  const text = cleanedUrlEl.textContent || '';
166
- navigator.clipboard.writeText(text).then(() => {
167
- const origText = copyBtn.textContent;
184
+ copyText(text).then(() => {
185
+ const origHtml = copyBtn.innerHTML;
168
186
  copyBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"/></svg> ${t.btnCopied}`;
169
187
  copyBtn.classList.add('uc-copied');
170
188
  setTimeout(() => {
171
- copyBtn.textContent = origText;
189
+ copyBtn.innerHTML = origHtml;
172
190
  copyBtn.classList.remove('uc-copied');
173
191
  }, 2000);
174
192
  });
@@ -13,15 +13,15 @@ const faqData = [
13
13
  },
14
14
  {
15
15
  question: 'Beeinträchtigt das die Funktionalität der Website?',
16
- answer: 'In der Regel nicht. Diese Parameter werden fast ausschließlich für Analyse und Marketing verwendet. Wenn du sie entfernst, lädt die Seite ganz normal nur der Websitebetreiber kann nicht mehr nachvollziehen, woher dein Klick stammte.',
16
+ answer: 'In der Regel nicht. Diese Parameter werden fast ausschließlich für Analyse und Marketing verwendet. Wenn du sie entfernst, lädt die Seite ganz normal - nur der Websitebetreiber kann nicht mehr nachvollziehen, woher dein Klick stammte.',
17
17
  },
18
18
  {
19
19
  question: 'Ist es sicher, meine Links hier zu verarbeiten?',
20
- answer: 'Absolut. Wie alle unsere Tools läuft der Prozess zu 100 % clientseitig ab. Deine Links werden niemals an unsere Server übertragen alles geschieht privat in deinem eigenen Browser.',
20
+ answer: 'Absolut. Wie alle unsere Tools läuft der Prozess zu 100 % clientseitig ab. Deine Links werden niemals an unsere Server übertragen - alles geschieht privat in deinem eigenen Browser.',
21
21
  },
22
22
  {
23
23
  question: 'Warum sind meine Facebook-Links so lang?',
24
- answer: 'Facebook hängt an jeden Link, der die Plattform verlässt, einen Parameter namens fbclid" an. Damit können sie deine Aktivitäten auf anderen Websites verfolgen selbst wenn du Drittanbieter-Werbung blockiert hast.',
24
+ answer: 'Facebook hängt an jeden Link, der die Plattform verlässt, einen Parameter namens "fbclid" an. Damit können sie deine Aktivitäten auf anderen Websites verfolgen - selbst wenn du Drittanbieter-Werbung blockiert hast.',
25
25
  },
26
26
  ];
27
27
 
@@ -98,7 +98,7 @@ export const content: ToolLocaleContent<UrlCleanerUI> = {
98
98
  { type: 'title', text: '¿Qué es un Limpiador de Tracking de URL y por qué lo necesitas?', level: 3 },
99
99
  {
100
100
  type: 'paragraph',
101
- html: 'Alguna vez has copiado un enlace para enviárselo a un amigo y te has dado cuenta de que es tres veces más largo de lo que debería ser? Ese "ruido" extra son los parámetros de rastreo. Un <strong>limpiador de tracking</strong> es una herramienta diseñada para "desnudar" la dirección web de toda la información publicitaria y de seguimiento que las grandes plataformas inyectan en cada clic que haces.',
101
+ html: '¿Alguna vez has copiado un enlace para enviárselo a un amigo y te has dado cuenta de que es tres veces más largo de lo que debería ser? Ese "ruido" extra son los parámetros de rastreo. Un <strong>limpiador de tracking</strong> es una herramienta diseñada para "desnudar" la dirección web de toda la información publicitaria y de seguimiento que las grandes plataformas inyectan en cada clic que haces.',
102
102
  },
103
103
  { type: 'title', text: 'Parámetros de rastreo más comunes', level: 3 },
104
104
  {
@@ -13,7 +13,7 @@ const faqData = [
13
13
  },
14
14
  {
15
15
  question: 'Apakah ini mempengaruhi fungsionalitas website?',
16
- answer: 'Umumnya tidak. Parameter-parameter ini hampir sepenuhnya digunakan untuk analitik dan pemasaran. Saat Anda menghapusnya, halaman akan tetap terbuka seperti biasa hanya saja pemilik website tidak bisa melacak dari mana asal klik Anda.',
16
+ answer: 'Umumnya tidak. Parameter-parameter ini hampir sepenuhnya digunakan untuk analitik dan pemasaran. Saat Anda menghapusnya, halaman akan tetap terbuka seperti biasa - hanya saja pemilik website tidak bisa melacak dari mana asal klik Anda.',
17
17
  },
18
18
  {
19
19
  question: 'Apakah aman memproses tautan saya di sini?',
@@ -13,7 +13,7 @@ const faqData = [
13
13
  },
14
14
  {
15
15
  question: 'Heeft dit invloed op de werking van de website?',
16
- answer: 'Doorgaans niet. Deze parameters worden vrijwel uitsluitend gebruikt voor analyse en marketing. Als je ze verwijdert, laadt de pagina gewoon alleen kan de websitebeheerder niet meer zien waar jouw klik vandaan kwam.',
16
+ answer: 'Doorgaans niet. Deze parameters worden vrijwel uitsluitend gebruikt voor analyse en marketing. Als je ze verwijdert, laadt de pagina gewoon - alleen kan de websitebeheerder niet meer zien waar jouw klik vandaan kwam.',
17
17
  },
18
18
  {
19
19
  question: 'Is het veilig om mijn links hier te verwerken?',
@@ -9,6 +9,7 @@ interface Props {
9
9
 
10
10
  const { locale = 'es' } = Astro.props;
11
11
  const content = await urlCleaner.i18n[locale]?.();
12
+ if (!content) return null;
12
13
  ---
13
14
 
14
- {content && <SEORenderer content={{ locale, sections: content.seo }} />}
15
+ {content.seo?.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
@@ -1,14 +1,17 @@
1
1
  .uc-root {
2
2
  width: 100%;
3
+ min-width: 0;
3
4
  }
4
5
 
5
6
  .uc-container {
7
+ container-type: inline-size;
6
8
  max-width: 900px;
7
9
  margin: 0 auto;
8
10
  display: flex;
9
11
  flex-direction: column;
10
12
  gap: 1.5rem;
11
13
  padding-bottom: 2rem;
14
+ min-width: 0;
12
15
  }
13
16
 
14
17
  .uc-input-card {
@@ -22,9 +25,10 @@
22
25
  display: flex;
23
26
  flex-direction: column;
24
27
  gap: 1rem;
28
+ min-width: 0;
25
29
  }
26
30
 
27
- :global(.theme-dark) .uc-input-card {
31
+ .theme-dark .uc-input-card {
28
32
  background: rgba(15, 23, 42, 0.75);
29
33
  border-color: rgba(255, 255, 255, 0.06);
30
34
  }
@@ -37,17 +41,19 @@
37
41
  letter-spacing: 0.05em;
38
42
  }
39
43
 
40
- :global(.theme-dark) .uc-label {
44
+ .theme-dark .uc-label {
41
45
  color: rgb(148, 163, 184);
42
46
  }
43
47
 
44
48
  .uc-input-group {
45
49
  display: flex;
46
50
  gap: 0.75rem;
51
+ min-width: 0;
47
52
  }
48
53
 
49
54
  .uc-input {
50
55
  flex-grow: 1;
56
+ min-width: 0;
51
57
  background: white;
52
58
  border: 1px solid rgb(226, 232, 240);
53
59
  border-radius: 0.75rem;
@@ -58,7 +64,7 @@
58
64
  color: rgb(51, 65, 85);
59
65
  }
60
66
 
61
- :global(.theme-dark) .uc-input {
67
+ .theme-dark .uc-input {
62
68
  background: rgb(15, 23, 42);
63
69
  border-color: rgb(51, 65, 85);
64
70
  color: rgb(203, 213, 225);
@@ -82,6 +88,14 @@
82
88
  gap: 0.5rem;
83
89
  transition: all 0.2s ease;
84
90
  font-size: 0.875rem;
91
+ justify-content: center;
92
+ min-height: 2.875rem;
93
+ min-width: 0;
94
+ white-space: nowrap;
95
+ }
96
+
97
+ .uc-btn-primary svg {
98
+ flex: 0 0 auto;
85
99
  }
86
100
 
87
101
  .uc-btn-primary:hover {
@@ -91,7 +105,7 @@
91
105
 
92
106
  .uc-results {
93
107
  display: grid;
94
- grid-template-columns: 1fr 1fr;
108
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
95
109
  gap: 1.5rem;
96
110
  }
97
111
 
@@ -112,9 +126,10 @@
112
126
  display: flex;
113
127
  flex-direction: column;
114
128
  gap: 1rem;
129
+ min-width: 0;
115
130
  }
116
131
 
117
- :global(.theme-dark) .uc-result-card {
132
+ .theme-dark .uc-result-card {
118
133
  background: rgba(15, 23, 42, 0.75);
119
134
  border-color: rgba(255, 255, 255, 0.06);
120
135
  }
@@ -123,6 +138,7 @@
123
138
  display: flex;
124
139
  justify-content: space-between;
125
140
  align-items: center;
141
+ gap: 1rem;
126
142
  }
127
143
 
128
144
  .uc-card-title {
@@ -132,9 +148,10 @@
132
148
  margin: 0;
133
149
  text-transform: uppercase;
134
150
  letter-spacing: 0.05em;
151
+ overflow-wrap: anywhere;
135
152
  }
136
153
 
137
- :global(.theme-dark) .uc-card-title {
154
+ .theme-dark .uc-card-title {
138
155
  color: rgb(203, 213, 225);
139
156
  }
140
157
 
@@ -146,9 +163,10 @@
146
163
  padding: 1rem;
147
164
  border-radius: 0.5rem;
148
165
  border: 1px solid rgba(0, 0, 0, 0.05);
166
+ min-width: 0;
149
167
  }
150
168
 
151
- :global(.theme-dark) .uc-url-display {
169
+ .theme-dark .uc-url-display {
152
170
  background: rgb(30, 41, 59);
153
171
  border-color: rgb(51, 65, 85);
154
172
  color: rgb(129, 140, 148);
@@ -167,9 +185,11 @@
167
185
  color: rgb(71, 85, 105);
168
186
  cursor: pointer;
169
187
  transition: all 0.2s ease;
188
+ flex: 0 0 auto;
189
+ white-space: nowrap;
170
190
  }
171
191
 
172
- :global(.theme-dark) .uc-copy-btn {
192
+ .theme-dark .uc-copy-btn {
173
193
  background: rgb(30, 41, 59);
174
194
  border-color: rgb(51, 65, 85);
175
195
  color: rgb(148, 163, 184);
@@ -199,7 +219,7 @@
199
219
  border-left: 3px solid rgb(59, 130, 246);
200
220
  }
201
221
 
202
- :global(.theme-dark) .uc-removed-item {
222
+ .theme-dark .uc-removed-item {
203
223
  background: rgba(255, 255, 255, 0.03);
204
224
  }
205
225
 
@@ -209,7 +229,7 @@
209
229
  font-size: 0.875rem;
210
230
  }
211
231
 
212
- :global(.theme-dark) .uc-item-key {
232
+ .theme-dark .uc-item-key {
213
233
  color: rgb(203, 213, 225);
214
234
  }
215
235
 
@@ -217,6 +237,7 @@
217
237
  font-size: 0.75rem;
218
238
  color: rgb(100, 116, 139);
219
239
  margin-left: 0.5rem;
240
+ overflow-wrap: anywhere;
220
241
  }
221
242
 
222
243
  .uc-item-desc {
@@ -225,7 +246,7 @@
225
246
  margin: 0.25rem 0;
226
247
  }
227
248
 
228
- :global(.theme-dark) .uc-item-desc {
249
+ .theme-dark .uc-item-desc {
229
250
  color: rgb(148, 163, 184);
230
251
  }
231
252
 
@@ -246,9 +267,10 @@
246
267
  display: grid;
247
268
  grid-template-columns: 1fr 1fr;
248
269
  gap: 1.5rem;
270
+ min-width: 0;
249
271
  }
250
272
 
251
- :global(.theme-dark) .uc-stats-card {
273
+ .theme-dark .uc-stats-card {
252
274
  background: rgba(15, 23, 42, 0.75);
253
275
  border-color: rgba(255, 255, 255, 0.06);
254
276
  }
@@ -275,6 +297,74 @@
275
297
  letter-spacing: 0.04em;
276
298
  }
277
299
 
278
- :global(.theme-dark) .uc-stat-label {
300
+ .theme-dark .uc-stat-label {
279
301
  color: rgb(148, 163, 184);
280
- }
302
+ }
303
+
304
+ @container (max-width: 520px) {
305
+ .uc-container {
306
+ gap: 1rem;
307
+ }
308
+
309
+ .uc-input-card,
310
+ .uc-result-card,
311
+ .uc-stats-card {
312
+ border-radius: 1rem;
313
+ padding: 1rem;
314
+ }
315
+
316
+ .uc-input-group {
317
+ flex-direction: column;
318
+ }
319
+
320
+ .uc-btn-primary {
321
+ width: 100%;
322
+ padding: 0.75rem 1rem;
323
+ white-space: normal;
324
+ }
325
+
326
+ .uc-card-header {
327
+ align-items: flex-start;
328
+ flex-direction: column;
329
+ gap: 0.75rem;
330
+ }
331
+
332
+ .uc-copy-btn {
333
+ justify-content: center;
334
+ width: 100%;
335
+ border-radius: 0.75rem;
336
+ padding: 0.65rem 0.875rem;
337
+ }
338
+
339
+ .uc-stats-card {
340
+ grid-template-columns: 1fr;
341
+ }
342
+
343
+ .uc-item-val {
344
+ display: block;
345
+ margin: 0.25rem 0 0;
346
+ }
347
+ }
348
+
349
+ @media (max-width: 640px) {
350
+ .uc-container {
351
+ gap: 1rem;
352
+ }
353
+
354
+ .uc-input-card,
355
+ .uc-result-card,
356
+ .uc-stats-card {
357
+ border-radius: 1rem;
358
+ padding: 1rem;
359
+ }
360
+
361
+ .uc-input-group {
362
+ flex-direction: column;
363
+ }
364
+
365
+ .uc-btn-primary {
366
+ width: 100%;
367
+ padding: 0.75rem 1rem;
368
+ white-space: normal;
369
+ }
370
+ }