@jjlmoya/utils-creative 1.6.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.
package/package.json CHANGED
@@ -1,64 +1,64 @@
1
1
  {
2
- "name": "@jjlmoya/utils-creative",
3
- "version": "1.6.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
- "peerDependencies": {
45
- "jszip": "^3.10.1"
46
- },
47
- "devDependencies": {
48
- "@astrojs/check": "^0.9.8",
49
- "eslint": "^9.39.4",
50
- "eslint-plugin-astro": "^1.6.0",
51
- "eslint-plugin-no-comments": "^1.1.10",
52
- "husky": "^9.1.7",
53
- "jszip": "^3.10.1",
54
- "lint-staged": "^16.4.0",
55
- "postcss-html": "^1.8.1",
56
- "schema-dts": "^1.1.2",
57
- "stylelint": "^17.6.0",
58
- "stylelint-config-standard": "^40.0.0",
59
- "stylelint-declaration-strict-value": "^1.11.1",
60
- "typescript": "^5.4.0",
61
- "typescript-eslint": "^8.58.0",
62
- "vitest": "^4.1.2"
63
- }
2
+ "name": "@jjlmoya/utils-creative",
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
+ "peerDependencies": {
45
+ "jszip": "^3.10.1"
46
+ },
47
+ "devDependencies": {
48
+ "@astrojs/check": "^0.9.8",
49
+ "eslint": "^9.39.4",
50
+ "eslint-plugin-astro": "^1.6.0",
51
+ "eslint-plugin-no-comments": "^1.1.10",
52
+ "husky": "^9.1.7",
53
+ "jszip": "^3.10.1",
54
+ "lint-staged": "^16.4.0",
55
+ "postcss-html": "^1.8.1",
56
+ "schema-dts": "^1.1.2",
57
+ "stylelint": "^17.6.0",
58
+ "stylelint-config-standard": "^40.0.0",
59
+ "stylelint-declaration-strict-value": "^1.11.1",
60
+ "typescript": "^5.4.0",
61
+ "typescript-eslint": "^8.58.0",
62
+ "vitest": "^4.1.2"
63
+ }
64
64
  }
@@ -35,8 +35,8 @@ describe('Locale Completeness Validation', () => {
35
35
  });
36
36
  });
37
37
 
38
- it('no tools registered yet', () => {
39
- expect(ALL_TOOLS.length).toBe(0);
38
+ it('should have tools registered', () => {
39
+ expect(ALL_TOOLS.length).toBeGreaterThan(0);
40
40
  });
41
41
  });
42
42
 
@@ -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 spaced -', (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('|') || /\s-\s/.test(title)) {
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 spaced -) found in titles in ${relativePath}:\n${list}`);
53
+ }
54
+ });
55
+ });
@@ -4,8 +4,8 @@ import { creativeCategory } from '../data';
4
4
 
5
5
  describe('Tool Validation Suite', () => {
6
6
  describe('Library Registration', () => {
7
- it('should have 0 tools in ALL_TOOLS (replace with actual count after adding tools)', () => {
8
- expect(ALL_TOOLS.length).toBe(0);
7
+ it('should have 6 tools in ALL_TOOLS', () => {
8
+ expect(ALL_TOOLS.length).toBe(6);
9
9
  });
10
10
 
11
11
  it('creativeCategory should be defined', () => {
@@ -1,9 +1,61 @@
1
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
1
2
  import type { BeadPatternGeneratorLocaleContent } from '../index';
2
3
 
4
+ const slug = 'bead-pattern-generator';
5
+ const title = 'Pattern Generator';
6
+ const description = 'Create pixel art and bead schemes for Miyuki or Hama from your photos. Color quantization algorithm, tunnel vision mode, and ZIP export.';
7
+
8
+ const faq: BeadPatternGeneratorLocaleContent['faq'] = [
9
+ { question: 'What is color quantization in patterns?', answer: 'It is the process of reducing the thousands of colors in a photo to just a few that correspond to the actual bead colors available (e.g., Miyuki or Hama). We use smart algorithms to maintain visual resemblance with the minimum possible palette.' },
10
+ { question: 'Can I use this pattern for cross-stitch?', answer: 'Yes, the generator creates a grid chart that is perfectly compatible with cross-stitch. You just need to choose a grid size that matches your fabric (Aida 14, 18, etc.).' },
11
+ { question: 'What is the difference between Miyuki and Hama Beads?', answer: 'Miyuki Delica beads are very small, precise glass beads for jewelry. Hama Beads are plastic and are fused with an iron. Our tool lets you adjust the aspect ratio so the pattern does not distort depending on the material used.' },
12
+ { question: 'How does the dithering algorithm work?', answer: 'Dithering creates the illusion of more colors by mixing pixels of different colors in spaced patterns. It helps color gradients look smoother even with a limited bead palette.' },
13
+ ];
14
+
15
+ const howTo: BeadPatternGeneratorLocaleContent['howTo'] = [
16
+ { name: 'Upload a clear image', text: 'Select a photo with good contrast and few small details so the pattern is easier to follow.' },
17
+ { name: 'Adjust the grid size', text: 'Define how many beads wide and tall your final piece will be. More beads = more detail but more difficulty.' },
18
+ { name: 'Optimize the color palette', text: 'Reduce the number of colors until they match the beads you have available in your craft kit.' },
19
+ { name: 'Export the guide scheme', text: 'Generate the final pattern with color codes to use as a reference while assembling your beads on the board or thread.' },
20
+ ];
21
+
22
+ const faqSchema: WithContext<FAQPage> = {
23
+ '@context': 'https://schema.org',
24
+ '@type': 'FAQPage',
25
+ mainEntity: faq.map((item) => ({
26
+ '@type': 'Question',
27
+ name: item.question,
28
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
29
+ })),
30
+ };
31
+
32
+ const howToSchema: WithContext<HowTo> = {
33
+ '@context': 'https://schema.org',
34
+ '@type': 'HowTo',
35
+ name: title,
36
+ description,
37
+ step: howTo.map((step) => ({
38
+ '@type': 'HowToStep',
39
+ name: step.name,
40
+ text: step.text,
41
+ })),
42
+ };
43
+
44
+ const appSchema: WithContext<SoftwareApplication> = {
45
+ '@context': 'https://schema.org',
46
+ '@type': 'SoftwareApplication',
47
+ name: title,
48
+ description,
49
+ applicationCategory: 'UtilitiesApplication',
50
+ operatingSystem: 'Web',
51
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
52
+ inLanguage: 'en',
53
+ };
54
+
3
55
  export const content: BeadPatternGeneratorLocaleContent = {
4
- slug: 'bead-pattern-generator',
5
- title: 'Pattern Generator',
6
- description: 'Create pixel art and bead schemes for Miyuki or Hama from your photos. Color quantization algorithm, tunnel vision mode, and ZIP export.',
56
+ slug,
57
+ title,
58
+ description,
7
59
  faqTitle: 'Frequently Asked Questions',
8
60
  bibliographyTitle: 'Artisan Bibliography',
9
61
  ui: {
@@ -52,22 +104,12 @@ export const content: BeadPatternGeneratorLocaleContent = {
52
104
  ], columns: 4 },
53
105
  { type: 'paragraph', html: 'In an era of ephemeral screens, creating something physical is a revolutionary act. This tool does not seek to automate art, but to <strong>empower the artisan</strong>. We give you computational precision so your hands can build lasting legacies.' },
54
106
  ],
55
- faq: [
56
- { question: 'What is color quantization in patterns?', answer: 'It is the process of reducing the thousands of colors in a photo to just a few that correspond to the actual bead colors available (e.g., Miyuki or Hama). We use smart algorithms to maintain visual resemblance with the minimum possible palette.' },
57
- { question: 'Can I use this pattern for cross-stitch?', answer: 'Yes, the generator creates a grid chart that is perfectly compatible with cross-stitch. You just need to choose a grid size that matches your fabric (Aida 14, 18, etc.).' },
58
- { question: 'What is the difference between Miyuki and Hama Beads?', answer: 'Miyuki Delica beads are very small, precise glass beads for jewelry. Hama Beads are plastic and are fused with an iron. Our tool lets you adjust the aspect ratio so the pattern does not distort depending on the material used.' },
59
- { question: 'How does the dithering algorithm work?', answer: 'Dithering creates the illusion of more colors by mixing pixels of different colors in spaced patterns. It helps color gradients look smoother even with a limited bead palette.' },
60
- ],
107
+ faq,
61
108
  bibliography: [
62
109
  { name: 'Scikit-Image: Color Quantization using K-Means', url: 'https://scikit-learn.org/0.23/auto_examples/cluster/plot_color_quantization.html' },
63
110
  { name: 'Miyuki Delica Beads Specification', url: 'https://www.miyuki-beads.co.jp/english/seedbeads/delica.html' },
64
111
  { name: 'Visgraf Lab: Dithering Algorithms', url: 'https://www.visgraf.impa.br/Courses/ip00/proj/Dithering1/floyd_steinberg_dithering.html' },
65
112
  ],
66
- howTo: [
67
- { name: 'Upload a clear image', text: 'Select a photo with good contrast and few small details so the pattern is easier to follow.' },
68
- { name: 'Adjust the grid size', text: 'Define how many beads wide and tall your final piece will be. More beads = more detail but more difficulty.' },
69
- { name: 'Optimize the color palette', text: 'Reduce the number of colors until they match the beads you have available in your craft kit.' },
70
- { name: 'Export the guide scheme', text: 'Generate the final pattern with color codes to use as a reference while assembling your beads on the board or thread.' },
71
- ],
72
- schemas: []
113
+ howTo,
114
+ schemas: [faqSchema as any, howToSchema as any, appSchema],
73
115
  };
@@ -1,9 +1,61 @@
1
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
1
2
  import type { BeadPatternGeneratorLocaleContent } from '../index';
2
3
 
4
+ const slug = 'generador-patrones-cuentas';
5
+ const title = 'Generador de Patrones';
6
+ const description = 'Crea esquemas de pixel art y cuentas para Miyuki o Hama desde tus fotos.';
7
+
8
+ const faq: BeadPatternGeneratorLocaleContent['faq'] = [
9
+ { question: '¿Qué es la cuantización de color en patrones?', answer: 'Es el proceso de reducir los miles de colores de una foto a solo unos pocos que correspondan con los colores reales de las cuentas (ej. Miyuki o Hama). Usamos algoritmos inteligentes para mantener el parecido visual con la mínima paleta posible.' },
10
+ { question: '¿Puedo usar este patrón para punto de cruz?', answer: 'Sí, el generador crea una malla cuadriculada (gráfico) que es perfectamente compatible con el punto de cruz. Solo debes elegir un tamaño de rejilla que se adapte a tu tela (Aida 14, 18, etc.).' },
11
+ { question: '¿Qué diferencia hay entre Miyuki y Hama Beads?', answer: 'Las Miyuki Delica son cuentas de cristal muy pequeñas y precisas para joyería. Los Hama Beads son de plástico y se funden con plancha. Nuestra herramienta permite ajustar la relación de aspecto para que el patrón no se deforme según el material usado.' },
12
+ { question: '¿Cómo funciona el algoritmo de tramado (Dithering)?', answer: 'El tramado crea la ilusión de más colores mezclando píxeles de colores diferentes en patrones espaciados. Ayuda a que las degradaciones de color se vean más suaves incluso con una paleta limitada de cuentas.' },
13
+ ];
14
+
15
+ const howTo: BeadPatternGeneratorLocaleContent['howTo'] = [
16
+ { name: 'Subir una imagen clara', text: 'Selecciona una foto con buen contraste y pocos detalles pequeños para que el patrón sea más fácil de seguir.' },
17
+ { name: 'Ajustar el tamaño de la rejilla', text: 'Define cuántas cuentas de ancho y alto tendrá tu pieza final. Recuerda que a más tamaño, más detalle pero más dificultad.' },
18
+ { name: 'Optimizar la paleta de colores', text: 'Reduce el número de colores hasta que coincidan con las cuentas que tienes disponibles en tu kit de manualidades.' },
19
+ { name: 'Exportar el esquema guía', text: 'Genera el patrón final con códigos de color para usarlo como referencia mientras montas tus cuentas en la placa o el hilo.' },
20
+ ];
21
+
22
+ const faqSchema: WithContext<FAQPage> = {
23
+ '@context': 'https://schema.org',
24
+ '@type': 'FAQPage',
25
+ mainEntity: faq.map((item) => ({
26
+ '@type': 'Question',
27
+ name: item.question,
28
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
29
+ })),
30
+ };
31
+
32
+ const howToSchema: WithContext<HowTo> = {
33
+ '@context': 'https://schema.org',
34
+ '@type': 'HowTo',
35
+ name: title,
36
+ description,
37
+ step: howTo.map((step) => ({
38
+ '@type': 'HowToStep',
39
+ name: step.name,
40
+ text: step.text,
41
+ })),
42
+ };
43
+
44
+ const appSchema: WithContext<SoftwareApplication> = {
45
+ '@context': 'https://schema.org',
46
+ '@type': 'SoftwareApplication',
47
+ name: title,
48
+ description,
49
+ applicationCategory: 'UtilitiesApplication',
50
+ operatingSystem: 'Web',
51
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
52
+ inLanguage: 'es',
53
+ };
54
+
3
55
  export const content: BeadPatternGeneratorLocaleContent = {
4
- slug: 'generador-patrones-cuentas',
5
- title: 'Generador de Patrones',
6
- description: 'Crea esquemas de pixel art y cuentas para Miyuki o Hama desde tus fotos.',
56
+ slug,
57
+ title,
58
+ description,
7
59
  faqTitle: 'Preguntas Frecuentes',
8
60
  bibliographyTitle: 'Bibliografía del Artesano',
9
61
  ui: {
@@ -59,22 +111,12 @@ export const content: BeadPatternGeneratorLocaleContent = {
59
111
  { term: 'K-Means Clustering', definition: 'Algoritmo de aprendizaje no supervisado que agrupa píxeles por similitud cromática para encontrar los colores "centroide" de la imagen.' },
60
112
  ]},
61
113
  ],
62
- faq: [
63
- { question: '¿Qué es la cuantización de color en patrones?', answer: 'Es el proceso de reducir los miles de colores de una foto a solo unos pocos que correspondan con los colores reales de las cuentas (ej. Miyuki o Hama). Usamos algoritmos inteligentes para mantener el parecido visual con la mínima paleta posible.' },
64
- { question: '¿Puedo usar este patrón para punto de cruz?', answer: 'Sí, el generador crea una malla cuadriculada (gráfico) que es perfectamente compatible con el punto de cruz. Solo debes elegir un tamaño de rejilla que se adapte a tu tela (Aida 14, 18, etc.).' },
65
- { question: '¿Qué diferencia hay entre Miyuki y Hama Beads?', answer: 'Las Miyuki Delica son cuentas de cristal muy pequeñas y precisas para joyería. Los Hama Beads son de plástico y se funden con plancha. Nuestra herramienta permite ajustar la relación de aspecto para que el patrón no se deforme según el material usado.' },
66
- { question: '¿Cómo funciona el algoritmo de tramado (Dithering)?', answer: 'El tramado crea la ilusión de más colores mezclando píxeles de colores diferentes en patrones espaciados. Ayuda a que las degradaciones de color se vean más suaves incluso con una paleta limitada de cuentas.' },
67
- ],
114
+ faq,
68
115
  bibliography: [
69
116
  { name: 'Scikit-Image: Color Quantization using K-Means', url: 'https://scikit-learn.org/0.23/auto_examples/cluster/plot_color_quantization.html' },
70
117
  { name: 'Miyuki Delica Beads Specification', url: 'https://www.miyuki-beads.co.jp/english/seedbeads/delica.html' },
71
118
  { name: 'Visgraf Lab: Dithering Algorithms', url: 'https://www.visgraf.impa.br/Courses/ip00/proj/Dithering1/floyd_steinberg_dithering.html' },
72
119
  ],
73
- howTo: [
74
- { name: 'Subir una imagen clara', text: 'Selecciona una foto con buen contraste y pocos detalles pequeños para que el patrón sea más fácil de seguir.' },
75
- { name: 'Ajustar el tamaño de la rejilla', text: 'Define cuántas cuentas de ancho y alto tendrá tu pieza final. Recuerda que a más tamaño, más detalle pero más dificultad.' },
76
- { name: 'Optimizar la paleta de colores', text: 'Reduce el número de colores hasta que coincidan con las cuentas que tienes disponibles en tu kit de manualidades.' },
77
- { name: 'Exportar el esquema guía', text: 'Genera el patrón final con códigos de color para usarlo como referencia mientras montas tus cuentas en la placa o el hilo.' },
78
- ],
79
- schemas: []
120
+ howTo,
121
+ schemas: [faqSchema as any, howToSchema as any, appSchema],
80
122
  };
@@ -1,9 +1,61 @@
1
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
1
2
  import type { BeadPatternGeneratorLocaleContent } from '../index';
2
3
 
4
+ const slug = 'generateur-de-modeles-de-perles';
5
+ const title = 'Générateur de Modèles';
6
+ const description = 'Créez du pixel art et des schémas de perles pour Miyuki ou Hama à partir de vos photos. Algorithme de quantification de couleurs, mode vision tunnel et export ZIP.';
7
+
8
+ const faq: BeadPatternGeneratorLocaleContent['faq'] = [
9
+ { question: 'Qu\'est-ce que la quantification de couleurs dans les modèles ?', answer: 'C\'est le processus de réduction des milliers de couleurs d\'une photo à quelques-unes seulement qui correspondent aux couleurs réelles des perles disponibles (ex: Miyuki ou Hama). Nous utilisons des algorithmes intelligents pour maintenir la ressemblance visuelle avec la palette minimale possible.' },
10
+ { question: 'Puis-je utiliser ce modèle pour le point de croix ?', answer: 'Oui, le générateur crée un diagramme de grille parfaitement compatible avec le point de croix. Il vous suffit de choisir une taille de grille correspondant à votre tissu (Aïda 14, 18, etc.).' },
11
+ { question: 'Quelle est la différence entre les perles Miyuki et Hama ?', answer: 'Les perles Miyuki Delica sont de très petites perles de verre précises pour la bijouterie. Les perles Hama sont en plastique et se fusionnent au fer à repasser. Notre outil vous permet d\'ajuster le rapport d\'aspect pour que le modèle ne se déforme pas selon le matériau utilisé.' },
12
+ { question: 'Comment fonctionne l\'algorithme de tramage (dithering) ?', answer: 'Le tramage crée l\'illusion d\'un plus grand nombre de couleurs en mélangeant des pixels de différentes couleurs dans des motifs espacés. Cela aide les dégradés de couleurs à paraître plus fluides, même avec une palette de perles limitée.' },
13
+ ];
14
+
15
+ const howTo: BeadPatternGeneratorLocaleContent['howTo'] = [
16
+ { name: 'Charger une image claire', text: 'Sélectionnez une photo avec un bon contraste et peu de petits détails pour que le modèle soit plus facile à suivre.' },
17
+ { name: 'Ajuster la taille de la grille', text: 'Définissez la largeur et la hauteur en perles de votre pièce finale. Plus il y a de perles, plus il y a de détails, mais plus c\'est difficile.' },
18
+ { name: 'Optimiser la palette de couleurs', text: 'Réduisez le nombre de couleurs jusqu\'à ce qu\'elles correspondent aux perles dont vous disposez dans votre kit de loisirs créatifs.' },
19
+ { name: 'Exporter le schéma guide', text: 'Générez le modèle final avec les codes de couleur à utiliser comme référence lors de l\'assemblage de vos perles sur la plaque ou le fil.' },
20
+ ];
21
+
22
+ const faqSchema: WithContext<FAQPage> = {
23
+ '@context': 'https://schema.org',
24
+ '@type': 'FAQPage',
25
+ mainEntity: faq.map((item) => ({
26
+ '@type': 'Question',
27
+ name: item.question,
28
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
29
+ })),
30
+ };
31
+
32
+ const howToSchema: WithContext<HowTo> = {
33
+ '@context': 'https://schema.org',
34
+ '@type': 'HowTo',
35
+ name: title,
36
+ description,
37
+ step: howTo.map((step) => ({
38
+ '@type': 'HowToStep',
39
+ name: step.name,
40
+ text: step.text,
41
+ })),
42
+ };
43
+
44
+ const appSchema: WithContext<SoftwareApplication> = {
45
+ '@context': 'https://schema.org',
46
+ '@type': 'SoftwareApplication',
47
+ name: title,
48
+ description,
49
+ applicationCategory: 'UtilitiesApplication',
50
+ operatingSystem: 'Web',
51
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
52
+ inLanguage: 'fr',
53
+ };
54
+
3
55
  export const content: BeadPatternGeneratorLocaleContent = {
4
- slug: 'generateur-de-modeles-de-perles',
5
- title: 'Générateur de Modèles',
6
- description: 'Créez du pixel art et des schémas de perles pour Miyuki ou Hama à partir de vos photos. Algorithme de quantification de couleurs, mode vision tunnel et export ZIP.',
56
+ slug,
57
+ title,
58
+ description,
7
59
  faqTitle: 'Questions Fréquemment Posées',
8
60
  bibliographyTitle: 'Bibliographie de l\'Artisan',
9
61
  ui: {
@@ -52,22 +104,12 @@ export const content: BeadPatternGeneratorLocaleContent = {
52
104
  ], columns: 4 },
53
105
  { type: 'paragraph', html: 'À l\'ère des écrans éphémères, créer quelque chose de physique est un acte révolutionnaire. Cet outil ne cherche pas à automatiser l\'art, mais à <strong>donner du pouvoir à l\'artisan</strong>. Nous vous offrons la précision informatique pour que vos mains puissent bâtir des héritages durables.' },
54
106
  ],
55
- faq: [
56
- { question: 'Qu\'est-ce que la quantification de couleurs dans les modèles ?', answer: 'C\'est le processus de réduction des milliers de couleurs d\'une photo à quelques-unes seulement qui correspondent aux couleurs réelles des perles disponibles (ex: Miyuki ou Hama). Nous utilisons des algorithmes intelligents pour maintenir la ressemblance visuelle avec la palette minimale possible.' },
57
- { question: 'Puis-je utiliser ce modèle pour le point de croix ?', answer: 'Oui, le générateur crée un diagramme de grille parfaitement compatible avec le point de croix. Il vous suffit de choisir une taille de grille correspondant à votre tissu (Aïda 14, 18, etc.).' },
58
- { question: 'Quelle est la différence entre les perles Miyuki et Hama ?', answer: 'Les perles Miyuki Delica sont de très petites perles de verre précises pour la bijouterie. Les perles Hama sont en plastique et se fusionnent au fer à repasser. Notre outil vous permet d\'ajuster le rapport d\'aspect pour que le modèle ne se déforme pas selon le matériau utilisé.' },
59
- { question: 'Comment fonctionne l\'algorithme de tramage (dithering) ?', answer: 'Le tramage crée l\'illusion d\'un plus grand nombre de couleurs en mélangeant des pixels de différentes couleurs dans des motifs espacés. Cela aide les dégradés de couleurs à paraître plus fluides, même avec une palette de perles limitée.' },
60
- ],
107
+ faq,
61
108
  bibliography: [
62
109
  { name: 'Scikit-Image: Quantification de couleurs utilisant K-Means', url: 'https://scikit-learn.org/0.23/auto_examples/cluster/plot_color_quantization.html' },
63
110
  { name: 'Spécifications des Perles Miyuki Delica', url: 'https://www.miyuki-beads.co.jp/english/seedbeads/delica.html' },
64
111
  { name: 'Visgraf Lab: Algorithmes de Tramage (Dithering)', url: 'https://www.visgraf.impa.br/Courses/ip00/proj/Dithering1/floyd_steinberg_dithering.html' },
65
112
  ],
66
- howTo: [
67
- { name: 'Charger une image claire', text: 'Sélectionnez une photo avec un bon contraste et peu de petits détails pour que le modèle soit plus facile à suivre.' },
68
- { name: 'Ajuster la taille de la grille', text: 'Définissez la largeur et la hauteur en perles de votre pièce finale. Plus il y a de perles, plus il y a de détails, mais plus c\'est difficile.' },
69
- { name: 'Optimiser la palette de couleurs', text: 'Réduisez le nombre de couleurs jusqu\'à ce qu\'elles correspondent aux perles dont vous disposez dans votre kit de loisirs créatifs.' },
70
- { name: 'Exporter le schéma guide', text: 'Générez le modèle final avec les codes de couleur à utiliser comme référence lors de l\'assemblage de vos perles sur la plaque ou le fil.' },
71
- ],
72
- schemas: []
113
+ howTo,
114
+ schemas: [faqSchema as any, howToSchema as any, appSchema],
73
115
  };
@@ -1,9 +1,77 @@
1
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
1
2
  import type { DiceRollerLocaleContent } from '../index';
2
3
 
4
+ const slug = 'dice-roller';
5
+ const title = 'Dice Roller';
6
+ const description = 'A complete dice simulator for your RPG and board games. Roll d4, d6, d8, d10, d12, d20 and d100 with modifiers and history.';
7
+
8
+ const faq: DiceRollerLocaleContent['faq'] = [
9
+ {
10
+ question: 'How can I simulate a roll with advantage (D&D)?',
11
+ answer: 'Add two d20 dice to the bag by clicking the d20 button twice. When rolling, observe the two individual results and keep the higher one. The displayed total will be the sum, but you can see each die separately in the result breakdown.',
12
+ },
13
+ {
14
+ question: 'What does the green or red color on results mean?',
15
+ answer: 'Green results indicate that die rolled its maximum possible value (a "critical"). Red results indicate the minimum value (a "1", the worst possible result). This makes it easy to spot crits and fumbles at a glance.',
16
+ },
17
+ {
18
+ question: 'Can I add multiple dice of the same type?',
19
+ answer: 'Yes. Each click on a die adds it to the bag. If you click the d6 three times, the bag will show "3d6". To reduce the count, click the "−" button that appears next to each die group in the bag.',
20
+ },
21
+ {
22
+ question: 'Are digital dice as random as physical ones?',
23
+ answer: 'Statistically, yes. Modern JavaScript engines use pseudorandom algorithms (xorshift128+) with very high quality uniform distribution. A real physical die can have small manufacturing imperfections that bias results; the digital die does not have that problem.',
24
+ },
25
+ {
26
+ question: 'What is the d100 and how is it used?',
27
+ answer: 'The d100 (or d%) generates a number from 1 to 100 and is used in percentage-based game systems, such as Call of Cthulhu or Warhammer Fantasy Roleplay. It represents "direct probability": if your Stealth skill is 65%, you need to roll 65 or less to succeed.',
28
+ },
29
+ ];
30
+
31
+ const howTo: DiceRollerLocaleContent['howTo'] = [
32
+ { name: 'Build the dice pool', text: 'Click the die buttons (d4, d6, d8...) to add them to your pool. Each click adds one die of the selected type. You can mix different types in the same roll.' },
33
+ { name: 'Adjust the modifier', text: 'Use the "+" and "−" buttons next to the modifier to apply bonuses or penalties (like the ability modifier in D&D). The modifier is automatically added to the total.' },
34
+ { name: 'Roll the dice', text: 'Press the "Roll Dice" button. The right panel shows the final total and the breakdown of each individual die, with crits (maximum) in green and fumbles (minimum) in red.' },
35
+ { name: 'Check the history', text: 'Each roll is recorded in the history with the dice expression used, the total result, and the exact time. You can clear the history with the corresponding button.' },
36
+ ];
37
+
38
+ const faqSchema: WithContext<FAQPage> = {
39
+ '@context': 'https://schema.org',
40
+ '@type': 'FAQPage',
41
+ mainEntity: faq.map((item) => ({
42
+ '@type': 'Question',
43
+ name: item.question,
44
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
45
+ })),
46
+ };
47
+
48
+ const howToSchema: WithContext<HowTo> = {
49
+ '@context': 'https://schema.org',
50
+ '@type': 'HowTo',
51
+ name: title,
52
+ description,
53
+ step: howTo.map((step) => ({
54
+ '@type': 'HowToStep',
55
+ name: step.name,
56
+ text: step.text,
57
+ })),
58
+ };
59
+
60
+ const appSchema: WithContext<SoftwareApplication> = {
61
+ '@context': 'https://schema.org',
62
+ '@type': 'SoftwareApplication',
63
+ name: title,
64
+ description,
65
+ applicationCategory: 'UtilitiesApplication',
66
+ operatingSystem: 'Web',
67
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
68
+ inLanguage: 'en',
69
+ };
70
+
3
71
  export const content: DiceRollerLocaleContent = {
4
- slug: 'dice-roller',
5
- title: 'Dice Roller',
6
- description: 'A complete dice simulator for your RPG and board games. Roll d4, d6, d8, d10, d12, d20 and d100 with modifiers and history.',
72
+ slug,
73
+ title,
74
+ description,
7
75
  faqTitle: 'Frequently Asked Questions',
8
76
  bibliographyTitle: 'Chance Bibliography',
9
77
  ui: {
@@ -59,38 +127,12 @@ export const content: DiceRollerLocaleContent = {
59
127
  { term: 'Percentile Roll', definition: 'A roll using two d10 to produce a result from 1–100, used in skill-based systems where abilities are measured as percentages.' },
60
128
  ]},
61
129
  ],
62
- faq: [
63
- {
64
- question: 'How can I simulate a roll with advantage (D&D)?',
65
- answer: 'Add two d20 dice to the bag by clicking the d20 button twice. When rolling, observe the two individual results and keep the higher one. The displayed total will be the sum, but you can see each die separately in the result breakdown.',
66
- },
67
- {
68
- question: 'What does the green or red color on results mean?',
69
- answer: 'Green results indicate that die rolled its maximum possible value (a "critical"). Red results indicate the minimum value (a "1", the worst possible result). This makes it easy to spot crits and fumbles at a glance.',
70
- },
71
- {
72
- question: 'Can I add multiple dice of the same type?',
73
- answer: 'Yes. Each click on a die adds it to the bag. If you click the d6 three times, the bag will show "3d6". To reduce the count, click the "−" button that appears next to each die group in the bag.',
74
- },
75
- {
76
- question: 'Are digital dice as random as physical ones?',
77
- answer: 'Statistically, yes. Modern JavaScript engines use pseudorandom algorithms (xorshift128+) with very high quality uniform distribution. A real physical die can have small manufacturing imperfections that bias results; the digital die does not have that problem.',
78
- },
79
- {
80
- question: 'What is the d100 and how is it used?',
81
- answer: 'The d100 (or d%) generates a number from 1 to 100 and is used in percentage-based game systems, such as Call of Cthulhu or Warhammer Fantasy Roleplay. It represents "direct probability": if your Stealth skill is 65%, you need to roll 65 or less to succeed.',
82
- },
83
- ],
130
+ faq,
84
131
  bibliography: [
85
132
  { name: 'D&D Beyond – Dice mechanics rules', url: 'https://www.dndbeyond.com/sources/basic-rules/using-ability-scores' },
86
133
  { name: 'Roll20 – Virtual tabletop and dice systems', url: 'https://roll20.net/' },
87
134
  { name: 'Pathfinder – d20 System Reference', url: 'https://paizo.com/pathfinder' },
88
135
  ],
89
- howTo: [
90
- { name: 'Build the dice pool', text: 'Click the die buttons (d4, d6, d8...) to add them to your pool. Each click adds one die of the selected type. You can mix different types in the same roll.' },
91
- { name: 'Adjust the modifier', text: 'Use the "+" and "−" buttons next to the modifier to apply bonuses or penalties (like the ability modifier in D&D). The modifier is automatically added to the total.' },
92
- { name: 'Roll the dice', text: 'Press the "Roll Dice" button. The right panel shows the final total and the breakdown of each individual die, with crits (maximum) in green and fumbles (minimum) in red.' },
93
- { name: 'Check the history', text: 'Each roll is recorded in the history with the dice expression used, the total result, and the exact time. You can clear the history with the corresponding button.' },
94
- ],
95
- schemas: []
136
+ howTo,
137
+ schemas: [faqSchema as any, howToSchema as any, appSchema],
96
138
  };