@jjlmoya/utils-audiovisual 1.5.0 → 1.7.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 (43) hide show
  1. package/package.json +58 -58
  2. package/src/category/i18n/fr.ts +1 -1
  3. package/src/tests/schemas_fulfillment.test.ts +23 -0
  4. package/src/tests/title_quality.test.ts +55 -0
  5. package/src/tool/chromaticLens/component.astro +38 -38
  6. package/src/tool/chromaticLens/i18n/en.ts +1 -1
  7. package/src/tool/chromaticLens/i18n/es.ts +1 -1
  8. package/src/tool/chromaticLens/i18n/fr.ts +1 -1
  9. package/src/tool/collageMaker/component.astro +47 -47
  10. package/src/tool/collageMaker/i18n/en.ts +1 -1
  11. package/src/tool/collageMaker/i18n/es.ts +1 -1
  12. package/src/tool/collageMaker/i18n/fr.ts +1 -1
  13. package/src/tool/exifCleaner/component.astro +49 -48
  14. package/src/tool/exifCleaner/i18n/en.ts +2 -2
  15. package/src/tool/exifCleaner/i18n/es.ts +2 -2
  16. package/src/tool/exifCleaner/i18n/fr.ts +3 -3
  17. package/src/tool/imageCompressor/component.astro +144 -106
  18. package/src/tool/imageCompressor/i18n/en.ts +12 -2
  19. package/src/tool/imageCompressor/i18n/es.ts +13 -3
  20. package/src/tool/imageCompressor/i18n/fr.ts +12 -2
  21. package/src/tool/imageCompressor/index.ts +10 -0
  22. package/src/tool/printQualityCalculator/component.astro +129 -104
  23. package/src/tool/printQualityCalculator/i18n/en.ts +17 -3
  24. package/src/tool/printQualityCalculator/i18n/es.ts +19 -5
  25. package/src/tool/printQualityCalculator/i18n/fr.ts +18 -4
  26. package/src/tool/printQualityCalculator/index.ts +14 -0
  27. package/src/tool/privacyBlur/component.astro +35 -35
  28. package/src/tool/privacyBlur/i18n/en.ts +1 -1
  29. package/src/tool/privacyBlur/i18n/es.ts +1 -1
  30. package/src/tool/privacyBlur/i18n/fr.ts +1 -1
  31. package/src/tool/subtitleSync/component.astro +42 -42
  32. package/src/tool/subtitleSync/i18n/en.ts +1 -1
  33. package/src/tool/subtitleSync/i18n/es.ts +1 -1
  34. package/src/tool/subtitleSync/i18n/fr.ts +3 -3
  35. package/src/tool/timelapseCalculator/component.astro +41 -42
  36. package/src/tool/tvDistance/component.astro +55 -55
  37. package/src/tool/tvDistance/i18n/en.ts +1 -1
  38. package/src/tool/tvDistance/i18n/es.ts +1 -1
  39. package/src/tool/tvDistance/i18n/fr.ts +1 -1
  40. package/src/tool/videoFrameExtractor/component.astro +54 -54
  41. package/src/tool/videoFrameExtractor/i18n/en.ts +1 -1
  42. package/src/tool/videoFrameExtractor/i18n/es.ts +1 -1
  43. package/src/tool/videoFrameExtractor/i18n/fr.ts +1 -1
package/package.json CHANGED
@@ -1,60 +1,60 @@
1
1
  {
2
- "name": "@jjlmoya/utils-audiovisual",
3
- "version": "1.5.0",
4
- "type": "module",
5
- "main": "./src/index.ts",
6
- "types": "./src/index.ts",
7
- "exports": {
8
- ".": "./src/index.ts",
9
- "./data": "./src/data.ts"
10
- },
11
- "files": [
12
- "src"
13
- ],
14
- "publishConfig": {
15
- "access": "public"
16
- },
17
- "scripts": {
18
- "dev": "astro dev",
19
- "start": "astro dev",
20
- "build": "astro build",
21
- "preview": "astro preview",
22
- "astro": "astro",
23
- "lint": "eslint src/ --max-warnings 0 && stylelint \"src/**/*.{css,astro}\"",
24
- "check": "astro check",
25
- "type-check": "astro check",
26
- "test": "vitest run",
27
- "preversion": "npm run lint && npm run test",
28
- "postversion": "git push && git push --tags",
29
- "patch": "npm version patch",
30
- "minor": "npm version minor",
31
- "major": "npm version major"
32
- },
33
- "lint-staged": {
34
- "*.{ts,tsx,astro}": [
35
- "eslint --fix"
36
- ]
37
- },
38
- "dependencies": {
39
- "@iconify-json/mdi": "^1.2.3",
40
- "@jjlmoya/utils-shared": "^1.1.0",
41
- "astro": "^6.1.2",
42
- "astro-icon": "^1.1.0"
43
- },
44
- "devDependencies": {
45
- "@astrojs/check": "^0.9.8",
46
- "eslint": "^9.39.4",
47
- "eslint-plugin-astro": "^1.6.0",
48
- "eslint-plugin-no-comments": "^1.1.10",
49
- "husky": "^9.1.7",
50
- "lint-staged": "^16.4.0",
51
- "postcss-html": "^1.8.1",
52
- "schema-dts": "^1.1.2",
53
- "stylelint": "^17.6.0",
54
- "stylelint-config-standard": "^40.0.0",
55
- "stylelint-declaration-strict-value": "^1.11.1",
56
- "typescript": "^5.4.0",
57
- "typescript-eslint": "^8.58.0",
58
- "vitest": "^4.1.2"
59
- }
2
+ "name": "@jjlmoya/utils-audiovisual",
3
+ "version": "1.7.0",
4
+ "type": "module",
5
+ "main": "./src/index.ts",
6
+ "types": "./src/index.ts",
7
+ "exports": {
8
+ ".": "./src/index.ts",
9
+ "./data": "./src/data.ts"
10
+ },
11
+ "files": [
12
+ "src"
13
+ ],
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "scripts": {
18
+ "dev": "astro dev",
19
+ "start": "astro dev",
20
+ "build": "astro build",
21
+ "preview": "astro preview",
22
+ "astro": "astro",
23
+ "lint": "eslint src/ --max-warnings 0 && stylelint \"src/**/*.{css,astro}\"",
24
+ "check": "astro check",
25
+ "type-check": "astro check",
26
+ "test": "vitest run",
27
+ "preversion": "npm run lint && npm run test",
28
+ "postversion": "git push && git push --tags",
29
+ "patch": "npm version patch",
30
+ "minor": "npm version minor",
31
+ "major": "npm version major"
32
+ },
33
+ "lint-staged": {
34
+ "*.{ts,tsx,astro}": [
35
+ "eslint --fix"
36
+ ]
37
+ },
38
+ "dependencies": {
39
+ "@iconify-json/mdi": "^1.2.3",
40
+ "@jjlmoya/utils-shared": "1.2.0",
41
+ "astro": "^6.1.2",
42
+ "astro-icon": "^1.1.0"
43
+ },
44
+ "devDependencies": {
45
+ "@astrojs/check": "^0.9.8",
46
+ "eslint": "^9.39.4",
47
+ "eslint-plugin-astro": "^1.6.0",
48
+ "eslint-plugin-no-comments": "^1.1.10",
49
+ "husky": "^9.1.7",
50
+ "lint-staged": "^16.4.0",
51
+ "postcss-html": "^1.8.1",
52
+ "schema-dts": "^1.1.2",
53
+ "stylelint": "^17.6.0",
54
+ "stylelint-config-standard": "^40.0.0",
55
+ "stylelint-declaration-strict-value": "^1.11.1",
56
+ "typescript": "^5.4.0",
57
+ "typescript-eslint": "^8.58.0",
58
+ "vitest": "^4.1.2"
59
+ }
60
60
  }
@@ -74,7 +74,7 @@ export const content: CategoryLocaleContent = {
74
74
  ]
75
75
  },
76
76
  {
77
- title: 'Édition de Sous-titres',
77
+ title: 'Synchronisation SRT',
78
78
  description: 'Synchronisez, ajustez et éditez les sous-titres avec une précision à la milliseconde.',
79
79
  icon: 'mdi:text',
80
80
  points: [
@@ -0,0 +1,23 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { ALL_TOOLS } from '../tools';
3
+ import type { ToolLocaleContent } from '../types';
4
+
5
+ describe('Schemas Fulfillment Validation', () => {
6
+ ALL_TOOLS.forEach((tool) => {
7
+ describe(`Tool: ${tool.entry.id}`, () => {
8
+ Object.keys(tool.entry.i18n).forEach((locale) => {
9
+ it(`Locale: ${locale} should have faqSchema, appSchema and howToSchema`, async () => {
10
+ const loader = tool.entry.i18n[locale as keyof typeof tool.entry.i18n];
11
+ if (!loader) return;
12
+ const content = (await loader()) as ToolLocaleContent;
13
+
14
+ const schemaTypes = content.schemas.map((s: any) => s['@type']);
15
+
16
+ expect(schemaTypes, `Tool "${tool.entry.id}" locale "${locale}" is missing FAQPage schema`).toContain('FAQPage');
17
+ expect(schemaTypes, `Tool "${tool.entry.id}" locale "${locale}" is missing SoftwareApplication schema`).toContain('SoftwareApplication');
18
+ expect(schemaTypes, `Tool "${tool.entry.id}" locale "${locale}" is missing HowTo schema`).toContain('HowTo');
19
+ });
20
+ });
21
+ });
22
+ });
23
+ });
@@ -0,0 +1,55 @@
1
+ import { describe, it } from 'vitest';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+
5
+ function getFiles(dir: string, ext: string[]): string[] {
6
+ const results: string[] = [];
7
+ if (!fs.existsSync(dir)) return results;
8
+ const list = fs.readdirSync(dir);
9
+ for (const file of list) {
10
+ const fullPath = path.join(dir, file);
11
+ const stat = fs.statSync(fullPath);
12
+ if (stat && stat.isDirectory()) {
13
+ results.push(...getFiles(fullPath, ext));
14
+ } else if (ext.some((e) => file.endsWith(e))) {
15
+ results.push(fullPath);
16
+ }
17
+ }
18
+ return results;
19
+ }
20
+
21
+ const SRC_DIR = path.join(process.cwd(), 'src');
22
+
23
+ describe('Project Titles - Separator Validation', () => {
24
+ const files = [
25
+ ...getFiles(path.join(SRC_DIR, 'tool'), ['.ts']),
26
+ ...getFiles(path.join(SRC_DIR, 'category'), ['.ts']),
27
+ ].filter(f => f.includes('i18n'));
28
+
29
+ it.each(files)('Verify that titles in %s do not contain | or -', (filePath) => {
30
+ const content = fs.readFileSync(filePath, 'utf-8');
31
+ const relativePath = path.relative(process.cwd(), filePath);
32
+
33
+ const titlePatterns = [
34
+ /const\s+title\s*=\s*['"]([^'"]+)['"]/g,
35
+ /title\s*:\s*['"]([^'"]+)['"]/g,
36
+ ];
37
+
38
+ const findings: string[] = [];
39
+
40
+ for (const pattern of titlePatterns) {
41
+ let match;
42
+ while ((match = pattern.exec(content)) !== null) {
43
+ const title = match[1];
44
+ if (title.includes('|') || title.includes('-')) {
45
+ findings.push(title);
46
+ }
47
+ }
48
+ }
49
+
50
+ if (findings.length > 0) {
51
+ const list = findings.map((f) => ` - "${f}"`).join('\n');
52
+ throw new Error(`Forbidden separators (| or -) found in titles in ${relativePath}:\n${list}`);
53
+ }
54
+ });
55
+ });
@@ -177,7 +177,7 @@ const { ui } = Astro.props;
177
177
  </script>
178
178
 
179
179
  <style>
180
- .cl-root {
180
+ :global(.cl-root) {
181
181
  --cl-bg: #fff;
182
182
  --cl-bg-elevated: #f8fafc;
183
183
  --cl-border: #e2e8f0;
@@ -195,7 +195,7 @@ const { ui } = Astro.props;
195
195
  margin: 0 auto;
196
196
  }
197
197
 
198
- :global(.theme-dark) .cl-root {
198
+ :global(.theme-dark .cl-root) {
199
199
  --cl-bg: #18181b;
200
200
  --cl-bg-elevated: #27272a;
201
201
  --cl-border: #3f3f46;
@@ -209,7 +209,7 @@ const { ui } = Astro.props;
209
209
  --cl-shadow: rgba(0, 0, 0, 0.5);
210
210
  }
211
211
 
212
- .cl-card {
212
+ :global(.cl-card) {
213
213
  background: var(--cl-bg);
214
214
  border: 1px solid var(--cl-border);
215
215
  border-radius: 3rem;
@@ -219,7 +219,7 @@ const { ui } = Astro.props;
219
219
  overflow: hidden;
220
220
  }
221
221
 
222
- .cl-drop {
222
+ :global(.cl-drop) {
223
223
  padding: 5rem 2rem;
224
224
  display: flex;
225
225
  flex-direction: column;
@@ -232,13 +232,13 @@ const { ui } = Astro.props;
232
232
  gap: 0.5rem;
233
233
  }
234
234
 
235
- .cl-drop:hover,
236
- .cl-drop-active {
235
+ :global(.cl-drop:hover),
236
+ :global(.cl-drop-active) {
237
237
  background: var(--cl-accent-alpha-hover);
238
238
  border-color: var(--cl-accent);
239
239
  }
240
240
 
241
- .cl-drop-icon {
241
+ :global(.cl-drop-icon) {
242
242
  width: 5rem;
243
243
  height: 5rem;
244
244
  background: var(--cl-accent-alpha);
@@ -250,30 +250,30 @@ const { ui } = Astro.props;
250
250
  color: var(--cl-accent);
251
251
  }
252
252
 
253
- .cl-drop-icon svg {
253
+ :global(.cl-drop-icon svg) {
254
254
  width: 2.5rem;
255
255
  height: 2.5rem;
256
256
  }
257
257
 
258
- .cl-drop-title {
258
+ :global(.cl-drop-title) {
259
259
  font-size: 2rem;
260
260
  font-weight: 950;
261
261
  color: var(--cl-text);
262
262
  margin: 0;
263
263
  }
264
264
 
265
- .cl-drop-sub {
265
+ :global(.cl-drop-sub) {
266
266
  font-size: 1.1rem;
267
267
  color: var(--cl-text-muted);
268
268
  margin: 0;
269
269
  font-weight: 600;
270
270
  }
271
271
 
272
- .cl-workspace {
272
+ :global(.cl-workspace) {
273
273
  padding: 1.5rem;
274
274
  }
275
275
 
276
- .cl-mini-drop {
276
+ :global(.cl-mini-drop) {
277
277
  display: inline-flex;
278
278
  align-items: center;
279
279
  gap: 0.75rem;
@@ -289,17 +289,17 @@ const { ui } = Astro.props;
289
289
  transition: border-color 0.2s, color 0.2s;
290
290
  }
291
291
 
292
- .cl-mini-drop:hover {
292
+ :global(.cl-mini-drop:hover) {
293
293
  border-color: var(--cl-accent);
294
294
  color: var(--cl-accent);
295
295
  }
296
296
 
297
- .cl-mini-drop svg {
297
+ :global(.cl-mini-drop svg) {
298
298
  width: 1.1rem;
299
299
  height: 1.1rem;
300
300
  }
301
301
 
302
- .cl-config-bar {
302
+ :global(.cl-config-bar) {
303
303
  padding: 1rem 0;
304
304
  margin-bottom: 2rem;
305
305
  border-bottom: 1px solid var(--cl-border);
@@ -307,13 +307,13 @@ const { ui } = Astro.props;
307
307
  justify-content: flex-end;
308
308
  }
309
309
 
310
- .cl-config-item {
310
+ :global(.cl-config-item) {
311
311
  display: flex;
312
312
  align-items: center;
313
313
  gap: 1rem;
314
314
  }
315
315
 
316
- .cl-config-label {
316
+ :global(.cl-config-label) {
317
317
  font-size: 0.75rem;
318
318
  font-weight: 900;
319
319
  text-transform: uppercase;
@@ -321,7 +321,7 @@ const { ui } = Astro.props;
321
321
  letter-spacing: 0.1em;
322
322
  }
323
323
 
324
- .cl-count-select {
324
+ :global(.cl-count-select) {
325
325
  padding: 0.5rem 1rem;
326
326
  border-radius: 0.75rem;
327
327
  background: var(--cl-bg-elevated);
@@ -331,58 +331,58 @@ const { ui } = Astro.props;
331
331
  cursor: pointer;
332
332
  }
333
333
 
334
- .cl-result-layout {
334
+ :global(.cl-result-layout) {
335
335
  display: grid;
336
336
  grid-template-columns: 1fr 1.25fr;
337
337
  gap: 3rem;
338
338
  }
339
339
 
340
340
  @media (max-width: 800px) {
341
- .cl-result-layout {
341
+ :global(.cl-result-layout) {
342
342
  grid-template-columns: 1fr;
343
343
  }
344
344
  }
345
345
 
346
- .cl-preview-col {
346
+ :global(.cl-preview-col) {
347
347
  display: flex;
348
348
  flex-direction: column;
349
349
  gap: 1rem;
350
350
  }
351
351
 
352
- .cl-preview-img {
352
+ :global(.cl-preview-img) {
353
353
  width: 100%;
354
354
  border-radius: 1.5rem;
355
355
  box-shadow: 0 20px 40px var(--cl-shadow);
356
356
  display: block;
357
357
  }
358
358
 
359
- .cl-palette-col {
359
+ :global(.cl-palette-col) {
360
360
  display: flex;
361
361
  flex-direction: column;
362
362
  gap: 1.5rem;
363
363
  }
364
364
 
365
- .cl-palette-header {
365
+ :global(.cl-palette-header) {
366
366
  display: flex;
367
367
  align-items: center;
368
368
  gap: 1rem;
369
369
  color: var(--cl-accent);
370
370
  }
371
371
 
372
- .cl-palette-header svg {
372
+ :global(.cl-palette-header svg) {
373
373
  width: 1.25rem;
374
374
  height: 1.25rem;
375
375
  flex-shrink: 0;
376
376
  }
377
377
 
378
- .cl-palette-header h4 {
378
+ :global(.cl-palette-header h4) {
379
379
  font-size: 1.25rem;
380
380
  font-weight: 950;
381
381
  color: var(--cl-text);
382
382
  margin: 0;
383
383
  }
384
384
 
385
- .cl-loader {
385
+ :global(.cl-loader) {
386
386
  display: flex;
387
387
  flex-direction: column;
388
388
  align-items: center;
@@ -390,7 +390,7 @@ const { ui } = Astro.props;
390
390
  padding: 2rem 0;
391
391
  }
392
392
 
393
- .cl-spinner {
393
+ :global(.cl-spinner) {
394
394
  width: 3rem;
395
395
  height: 3rem;
396
396
  border: 3px solid var(--cl-accent-alpha);
@@ -399,7 +399,7 @@ const { ui } = Astro.props;
399
399
  animation: cl-spin 0.8s linear infinite;
400
400
  }
401
401
 
402
- .cl-loader-text {
402
+ :global(.cl-loader-text) {
403
403
  font-size: 0.75rem;
404
404
  font-weight: 900;
405
405
  text-transform: uppercase;
@@ -408,14 +408,14 @@ const { ui } = Astro.props;
408
408
  margin: 0;
409
409
  }
410
410
 
411
- .cl-swatches {
411
+ :global(.cl-swatches) {
412
412
  display: flex;
413
413
  flex-direction: column;
414
414
  gap: 0.75rem;
415
415
  animation: cl-fade-up 0.5s ease;
416
416
  }
417
417
 
418
- .cl-swatch {
418
+ :global(.cl-swatch) {
419
419
  display: flex;
420
420
  align-items: center;
421
421
  gap: 1.25rem;
@@ -427,17 +427,17 @@ const { ui } = Astro.props;
427
427
  transition: transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275), border-color 0.2s;
428
428
  }
429
429
 
430
- .cl-swatch:hover {
430
+ :global(.cl-swatch:hover) {
431
431
  transform: scale(1.02);
432
432
  border-color: var(--cl-accent);
433
433
  }
434
434
 
435
- .cl-swatch-copied {
435
+ :global(.cl-swatch-copied) {
436
436
  background: var(--cl-emerald-alpha);
437
437
  border-color: var(--cl-emerald);
438
438
  }
439
439
 
440
- .cl-swatch-color {
440
+ :global(.cl-swatch-color) {
441
441
  width: 3.5rem;
442
442
  height: 3.5rem;
443
443
  border-radius: 0.75rem;
@@ -445,19 +445,19 @@ const { ui } = Astro.props;
445
445
  flex-shrink: 0;
446
446
  }
447
447
 
448
- .cl-swatch-info {
448
+ :global(.cl-swatch-info) {
449
449
  display: flex;
450
450
  flex-direction: column;
451
451
  gap: 0.2rem;
452
452
  }
453
453
 
454
- .cl-swatch-hex {
454
+ :global(.cl-swatch-hex) {
455
455
  font-weight: 950;
456
456
  color: var(--cl-text);
457
457
  font-size: 1.25rem;
458
458
  }
459
459
 
460
- .cl-swatch-action {
460
+ :global(.cl-swatch-action) {
461
461
  font-size: 0.7rem;
462
462
  font-weight: 900;
463
463
  color: var(--cl-text-muted);
@@ -465,7 +465,7 @@ const { ui } = Astro.props;
465
465
  letter-spacing: 0.05em;
466
466
  }
467
467
 
468
- .cl-hidden {
468
+ :global(.cl-hidden) {
469
469
  display: none;
470
470
  }
471
471
 
@@ -2,7 +2,7 @@ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dt
2
2
  import type { ChromaticLensUI, ChromaticLensLocaleContent } from '../index';
3
3
 
4
4
  const slug = 'chromatic-lens-color-palette-extraction-online';
5
- const title = 'Chromatic Lens - Online Color Palette Extraction';
5
+ const title = 'Chromatic Lens: Online Color Palette Extraction';
6
6
  const description = 'Extract professional color palettes from any image for free. Identify dominant colors in your photos using mathematical algorithms.';
7
7
 
8
8
  const ui: ChromaticLensUI = {
@@ -2,7 +2,7 @@ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dt
2
2
  import type { ChromaticLensUI, ChromaticLensLocaleContent } from '../index';
3
3
 
4
4
  const slug = 'lente-cromatica';
5
- const title = 'Lente Cromática - Extracción de Paletas de Color Online';
5
+ const title = 'Lente Cromática: Extracción de Paletas de Color Online';
6
6
  const description = 'Extrae paletas de colores profesionales de cualquier imagen de forma gratuita. Identifica los colores dominantes de tus fotos mediante algoritmos matemáticos.';
7
7
 
8
8
  const ui: ChromaticLensUI = {
@@ -2,7 +2,7 @@ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dt
2
2
  import type { ChromaticLensUI, ChromaticLensLocaleContent } from '../index';
3
3
 
4
4
  const slug = 'lentille-chromatique-extraction-palette-couleurs-ligne';
5
- const title = 'Lentille Chromatique - Extraction de Palette de Couleurs en Ligne';
5
+ const title = 'Lentille Chromatique: Extraction de Palette de Couleurs en Ligne';
6
6
  const description = 'Extrayez gratuitement des palettes de couleurs professionnelles à partir de n\'importe quelle image. Identifiez les couleurs dominantes à l\'aide d\'algorithmes mathématiques.';
7
7
 
8
8
  const ui: ChromaticLensUI = {