@jjlmoya/utils-creative 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.
- package/package.json +62 -62
- package/src/tests/locale_completeness.test.ts +2 -2
- package/src/tests/schemas_fulfillment.test.ts +23 -0
- package/src/tests/title_quality.test.ts +55 -0
- package/src/tests/tool_validation.test.ts +2 -2
- package/src/tool/bead-pattern-generator/component.astro +29 -21
- package/src/tool/bead-pattern-generator/i18n/en.ts +73 -19
- package/src/tool/bead-pattern-generator/i18n/es.ts +73 -19
- package/src/tool/bead-pattern-generator/i18n/fr.ts +73 -19
- package/src/tool/bead-pattern-generator/index.ts +14 -2
- package/src/tool/dice-roller/component.astro +42 -30
- package/src/tool/dice-roller/i18n/en.ts +84 -33
- package/src/tool/dice-roller/i18n/es.ts +84 -33
- package/src/tool/dice-roller/i18n/fr.ts +84 -33
- package/src/tool/dice-roller/index.ts +9 -0
- package/src/tool/excuse-generator/i18n/en.ts +60 -18
- package/src/tool/excuse-generator/i18n/es.ts +60 -18
- package/src/tool/excuse-generator/i18n/fr.ts +60 -18
- package/src/tool/fortune-cookie/component.astro +27 -16
- package/src/tool/fortune-cookie/i18n/en.ts +60 -18
- package/src/tool/fortune-cookie/i18n/es.ts +60 -18
- package/src/tool/fortune-cookie/i18n/fr.ts +60 -18
- package/src/tool/synesthesia-painter/component.astro +5 -5
- package/src/tool/synesthesia-painter/i18n/en.ts +74 -32
- package/src/tool/synesthesia-painter/i18n/es.ts +74 -32
- package/src/tool/synesthesia-painter/i18n/fr.ts +74 -32
- package/src/tool/zalgo-generator/component.astro +29 -18
- package/src/tool/zalgo-generator/i18n/en.ts +70 -17
- package/src/tool/zalgo-generator/i18n/es.ts +70 -17
- package/src/tool/zalgo-generator/i18n/fr.ts +70 -17
- package/src/tool/zalgo-generator/index.ts +11 -0
- package/src/tools.ts +14 -1
|
@@ -1,18 +1,82 @@
|
|
|
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
|
|
5
|
-
title
|
|
6
|
-
description
|
|
56
|
+
slug,
|
|
57
|
+
title,
|
|
58
|
+
description,
|
|
7
59
|
faqTitle: 'Questions Fréquemment Posées',
|
|
8
60
|
bibliographyTitle: 'Bibliographie de l\'Artisan',
|
|
9
61
|
ui: {
|
|
10
|
-
title: '
|
|
62
|
+
title: 'Laboratoire de Modèles',
|
|
63
|
+
subtitle: 'Ingénierie chromatique pour vos mains',
|
|
11
64
|
description: 'De la photo au schéma de perles.',
|
|
65
|
+
gridSizeLabel: 'Taille (Largeur)',
|
|
66
|
+
colorCountLabel: 'Couleurs',
|
|
67
|
+
optionsLabel: 'Options',
|
|
68
|
+
rulersLabel: 'Règles',
|
|
69
|
+
symbolsTooltip: 'Motif Surprise',
|
|
70
|
+
symbolsLabel: 'Symboles',
|
|
71
|
+
downloadBtn: 'Télécharger',
|
|
72
|
+
uploadTitle: 'Chargez Votre Vision',
|
|
73
|
+
uploadSubtitle: 'et laissez la magie numérique opérer',
|
|
74
|
+
paletteTitle: 'ADN Chromatique',
|
|
75
|
+
reuploadBtn: 'Changer l\'Image',
|
|
76
|
+
tunnelVisionTitle: 'Vision de Tunnel',
|
|
77
|
+
tunnelVisionSubtitle: 'Votre assistant de précision rangée par rangée.',
|
|
12
78
|
uploadLabel: 'Chargez votre photo',
|
|
13
|
-
gridSizeLabel: 'Taille de grille (perles)',
|
|
14
79
|
pixelateBtn: 'Générer le Modèle',
|
|
15
|
-
downloadBtn: 'Télécharger le Schéma',
|
|
16
80
|
faqTitle: 'FAQ',
|
|
17
81
|
bibliographyTitle: 'Références'
|
|
18
82
|
},
|
|
@@ -40,22 +104,12 @@ export const content: BeadPatternGeneratorLocaleContent = {
|
|
|
40
104
|
], columns: 4 },
|
|
41
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.' },
|
|
42
106
|
],
|
|
43
|
-
faq
|
|
44
|
-
{ 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.' },
|
|
45
|
-
{ 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.).' },
|
|
46
|
-
{ 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é.' },
|
|
47
|
-
{ 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.' },
|
|
48
|
-
],
|
|
107
|
+
faq,
|
|
49
108
|
bibliography: [
|
|
50
109
|
{ name: 'Scikit-Image: Quantification de couleurs utilisant K-Means', url: 'https://scikit-learn.org/0.23/auto_examples/cluster/plot_color_quantization.html' },
|
|
51
110
|
{ name: 'Spécifications des Perles Miyuki Delica', url: 'https://www.miyuki-beads.co.jp/english/seedbeads/delica.html' },
|
|
52
111
|
{ name: 'Visgraf Lab: Algorithmes de Tramage (Dithering)', url: 'https://www.visgraf.impa.br/Courses/ip00/proj/Dithering1/floyd_steinberg_dithering.html' },
|
|
53
112
|
],
|
|
54
|
-
howTo
|
|
55
|
-
|
|
56
|
-
{ 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.' },
|
|
57
|
-
{ 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.' },
|
|
58
|
-
{ 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.' },
|
|
59
|
-
],
|
|
60
|
-
schemas: []
|
|
113
|
+
howTo,
|
|
114
|
+
schemas: [faqSchema as any, howToSchema as any, appSchema],
|
|
61
115
|
};
|
|
@@ -7,10 +7,22 @@ export interface BeadPatternGeneratorUI {
|
|
|
7
7
|
[key: string]: string;
|
|
8
8
|
title: string;
|
|
9
9
|
description: string;
|
|
10
|
-
|
|
10
|
+
subtitle: string;
|
|
11
11
|
gridSizeLabel: string;
|
|
12
|
-
|
|
12
|
+
colorCountLabel: string;
|
|
13
|
+
optionsLabel: string;
|
|
14
|
+
rulersLabel: string;
|
|
15
|
+
symbolsTooltip: string;
|
|
16
|
+
symbolsLabel: string;
|
|
13
17
|
downloadBtn: string;
|
|
18
|
+
uploadTitle: string;
|
|
19
|
+
uploadSubtitle: string;
|
|
20
|
+
paletteTitle: string;
|
|
21
|
+
reuploadBtn: string;
|
|
22
|
+
tunnelVisionTitle: string;
|
|
23
|
+
tunnelVisionSubtitle: string;
|
|
24
|
+
uploadLabel: string;
|
|
25
|
+
pixelateBtn: string;
|
|
14
26
|
faqTitle: string;
|
|
15
27
|
bibliographyTitle: string;
|
|
16
28
|
}
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
---
|
|
2
|
+
import type { DiceRollerUI } from './index';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
ui: DiceRollerUI;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { ui } = Astro.props;
|
|
2
9
|
---
|
|
3
10
|
|
|
4
|
-
<div id="dice-roller-root" class="dr-root">
|
|
11
|
+
<div id="dice-roller-root" class="dr-root" data-ui={JSON.stringify(ui)}>
|
|
5
12
|
<div class="dr-panel">
|
|
6
13
|
|
|
7
14
|
<div class="dr-selector">
|
|
8
|
-
<p class="dr-label">
|
|
15
|
+
<p class="dr-label">{ui.addDiceLabel}</p>
|
|
9
16
|
<div class="dr-dice-grid">
|
|
10
17
|
<button class="dr-die-btn" data-sides="4">d4</button>
|
|
11
18
|
<button class="dr-die-btn" data-sides="6">d6</button>
|
|
@@ -19,16 +26,16 @@
|
|
|
19
26
|
|
|
20
27
|
<div class="dr-pool-section">
|
|
21
28
|
<div class="dr-pool-header">
|
|
22
|
-
<p class="dr-label">
|
|
23
|
-
<button id="dr-clear-pool" class="dr-clear-btn">
|
|
29
|
+
<p class="dr-label">{ui.bagLabel}</p>
|
|
30
|
+
<button id="dr-clear-pool" class="dr-clear-btn">{ui.emptyBagBtn}</button>
|
|
24
31
|
</div>
|
|
25
32
|
<div id="dr-pool" class="dr-pool-display">
|
|
26
|
-
<span class="dr-pool-empty">
|
|
33
|
+
<span class="dr-pool-empty">{ui.emptyBagText}</span>
|
|
27
34
|
</div>
|
|
28
35
|
</div>
|
|
29
36
|
|
|
30
37
|
<div class="dr-modifier-section">
|
|
31
|
-
<label class="dr-label" for="dr-modifier">
|
|
38
|
+
<label class="dr-label" for="dr-modifier">{ui.modifierLabel}</label>
|
|
32
39
|
<div class="dr-modifier-controls">
|
|
33
40
|
<button id="dr-mod-dec" class="dr-mod-btn">−</button>
|
|
34
41
|
<span id="dr-modifier-val" class="dr-modifier-display">0</span>
|
|
@@ -37,7 +44,7 @@
|
|
|
37
44
|
</div>
|
|
38
45
|
|
|
39
46
|
<button id="dr-roll-btn" class="dr-roll-btn" disabled>
|
|
40
|
-
<span id="dr-roll-text">
|
|
47
|
+
<span id="dr-roll-text">{ui.rollBtn}</span>
|
|
41
48
|
</button>
|
|
42
49
|
|
|
43
50
|
</div>
|
|
@@ -46,11 +53,11 @@
|
|
|
46
53
|
|
|
47
54
|
<div class="dr-result-card" id="dr-result-card">
|
|
48
55
|
<div class="dr-result-empty" id="dr-result-empty">
|
|
49
|
-
<p>
|
|
56
|
+
<p>{ui.preRollText}</p>
|
|
50
57
|
</div>
|
|
51
58
|
<div class="dr-result-content" id="dr-result-content" style="display:none">
|
|
52
59
|
<div class="dr-total-area">
|
|
53
|
-
<span class="dr-total-label">
|
|
60
|
+
<span class="dr-total-label">{ui.totalLabel}</span>
|
|
54
61
|
<span id="dr-total" class="dr-total-val">0</span>
|
|
55
62
|
<span id="dr-roll-expr" class="dr-roll-expr"></span>
|
|
56
63
|
</div>
|
|
@@ -60,11 +67,11 @@
|
|
|
60
67
|
|
|
61
68
|
<div class="dr-history-section">
|
|
62
69
|
<div class="dr-history-header">
|
|
63
|
-
<span class="dr-label">
|
|
64
|
-
<button id="dr-clear-history" class="dr-clear-btn">
|
|
70
|
+
<span class="dr-label">{ui.historyLabel}</span>
|
|
71
|
+
<button id="dr-clear-history" class="dr-clear-btn">{ui.clearHistoryBtn}</button>
|
|
65
72
|
</div>
|
|
66
73
|
<div id="dr-history-list" class="dr-history-list">
|
|
67
|
-
<p class="dr-history-empty">
|
|
74
|
+
<p class="dr-history-empty">{ui.emptyHistoryText}</p>
|
|
68
75
|
</div>
|
|
69
76
|
</div>
|
|
70
77
|
|
|
@@ -72,6 +79,10 @@
|
|
|
72
79
|
</div>
|
|
73
80
|
|
|
74
81
|
<script>
|
|
82
|
+
import type { DiceRollerUI } from './index';
|
|
83
|
+
const root = document.getElementById('dice-roller-root') as HTMLElement;
|
|
84
|
+
const ui = JSON.parse(root.dataset.ui || '{}') as DiceRollerUI;
|
|
85
|
+
|
|
75
86
|
type Pool = { sides: number; count: number }[];
|
|
76
87
|
|
|
77
88
|
let pool: Pool = [];
|
|
@@ -94,9 +105,9 @@
|
|
|
94
105
|
|
|
95
106
|
function renderPool(): void {
|
|
96
107
|
if (pool.length === 0) {
|
|
97
|
-
poolDisplay.innerHTML =
|
|
108
|
+
poolDisplay.innerHTML = `<span class="dr-pool-empty">${ui.emptyBagText}</span>`;
|
|
98
109
|
rollBtn.disabled = true;
|
|
99
|
-
rollText.textContent =
|
|
110
|
+
rollText.textContent = ui.rollBtn;
|
|
100
111
|
return;
|
|
101
112
|
}
|
|
102
113
|
|
|
@@ -106,14 +117,15 @@
|
|
|
106
117
|
chip.className = 'dr-pool-chip';
|
|
107
118
|
chip.innerHTML = `
|
|
108
119
|
<span class="dr-chip-label">${count}d${sides}</span>
|
|
109
|
-
<button class="dr-chip-remove" data-sides="${sides}"
|
|
120
|
+
<button class="dr-chip-remove" data-sides="${sides}">−</button>
|
|
110
121
|
`;
|
|
111
122
|
poolDisplay.appendChild(chip);
|
|
112
123
|
});
|
|
113
124
|
|
|
114
125
|
const total = poolTotal();
|
|
115
126
|
rollBtn.disabled = false;
|
|
116
|
-
|
|
127
|
+
const label = total === 1 ? ui.rollOneLabel : ui.rollManyLabel;
|
|
128
|
+
rollText.textContent = label.replace('$COUNT', total.toString());
|
|
117
129
|
}
|
|
118
130
|
|
|
119
131
|
function addDie(sides: number): void {
|
|
@@ -224,7 +236,7 @@
|
|
|
224
236
|
rollBtn.addEventListener('click', rollAll);
|
|
225
237
|
|
|
226
238
|
document.getElementById('dr-clear-history')?.addEventListener('click', () => {
|
|
227
|
-
historyList.innerHTML =
|
|
239
|
+
historyList.innerHTML = `<p class="dr-history-empty">${ui.emptyHistoryText}</p>`;
|
|
228
240
|
});
|
|
229
241
|
</script>
|
|
230
242
|
|
|
@@ -378,7 +390,7 @@
|
|
|
378
390
|
font-style: italic;
|
|
379
391
|
}
|
|
380
392
|
|
|
381
|
-
.dr-pool-chip {
|
|
393
|
+
:global(.dr-pool-chip) {
|
|
382
394
|
display: flex;
|
|
383
395
|
align-items: center;
|
|
384
396
|
gap: 0.25rem;
|
|
@@ -388,13 +400,13 @@
|
|
|
388
400
|
padding: 0.2rem 0.5rem 0.2rem 0.75rem;
|
|
389
401
|
}
|
|
390
402
|
|
|
391
|
-
.dr-chip-label {
|
|
403
|
+
:global(.dr-chip-label) {
|
|
392
404
|
font-size: 0.8rem;
|
|
393
405
|
font-weight: 700;
|
|
394
406
|
color: var(--dr-primary);
|
|
395
407
|
}
|
|
396
408
|
|
|
397
|
-
.dr-chip-remove {
|
|
409
|
+
:global(.dr-chip-remove) {
|
|
398
410
|
width: 1.125rem;
|
|
399
411
|
height: 1.125rem;
|
|
400
412
|
border-radius: 50%;
|
|
@@ -411,7 +423,7 @@
|
|
|
411
423
|
padding: 0;
|
|
412
424
|
}
|
|
413
425
|
|
|
414
|
-
.dr-chip-remove:hover {
|
|
426
|
+
:global(.dr-chip-remove:hover) {
|
|
415
427
|
background: var(--dr-primary-dark);
|
|
416
428
|
}
|
|
417
429
|
|
|
@@ -581,7 +593,7 @@
|
|
|
581
593
|
justify-content: center;
|
|
582
594
|
}
|
|
583
595
|
|
|
584
|
-
.dr-result-pip {
|
|
596
|
+
:global(.dr-result-pip) {
|
|
585
597
|
display: flex;
|
|
586
598
|
flex-direction: column;
|
|
587
599
|
align-items: center;
|
|
@@ -594,24 +606,24 @@
|
|
|
594
606
|
transition: border-color 0.2s;
|
|
595
607
|
}
|
|
596
608
|
|
|
597
|
-
.dr-pip-max {
|
|
609
|
+
:global(.dr-pip-max) {
|
|
598
610
|
border-color: var(--dr-success);
|
|
599
611
|
background: rgba(16, 185, 129, 0.1);
|
|
600
612
|
}
|
|
601
613
|
|
|
602
|
-
.dr-pip-min {
|
|
614
|
+
:global(.dr-pip-min) {
|
|
603
615
|
border-color: var(--dr-danger);
|
|
604
616
|
background: rgba(239, 68, 68, 0.1);
|
|
605
617
|
}
|
|
606
618
|
|
|
607
|
-
.dr-pip-val {
|
|
619
|
+
:global(.dr-pip-val) {
|
|
608
620
|
font-size: 1.25rem;
|
|
609
621
|
font-weight: 800;
|
|
610
622
|
color: var(--dr-text);
|
|
611
623
|
line-height: 1;
|
|
612
624
|
}
|
|
613
625
|
|
|
614
|
-
.dr-pip-die {
|
|
626
|
+
:global(.dr-pip-die) {
|
|
615
627
|
font-size: 0.65rem;
|
|
616
628
|
color: var(--dr-text-muted);
|
|
617
629
|
font-weight: 600;
|
|
@@ -662,7 +674,7 @@
|
|
|
662
674
|
margin: 1rem 0;
|
|
663
675
|
}
|
|
664
676
|
|
|
665
|
-
.dr-history-item {
|
|
677
|
+
:global(.dr-history-item) {
|
|
666
678
|
display: grid;
|
|
667
679
|
grid-template-columns: 1fr auto auto;
|
|
668
680
|
align-items: center;
|
|
@@ -686,7 +698,7 @@
|
|
|
686
698
|
}
|
|
687
699
|
}
|
|
688
700
|
|
|
689
|
-
.dr-hist-expr {
|
|
701
|
+
:global(.dr-hist-expr) {
|
|
690
702
|
color: var(--dr-text-muted);
|
|
691
703
|
font-size: 0.75rem;
|
|
692
704
|
overflow: hidden;
|
|
@@ -694,13 +706,13 @@
|
|
|
694
706
|
white-space: nowrap;
|
|
695
707
|
}
|
|
696
708
|
|
|
697
|
-
.dr-hist-result {
|
|
709
|
+
:global(.dr-hist-result) {
|
|
698
710
|
font-weight: 800;
|
|
699
711
|
font-size: 1rem;
|
|
700
712
|
color: var(--dr-primary);
|
|
701
713
|
}
|
|
702
714
|
|
|
703
|
-
.dr-hist-time {
|
|
715
|
+
:global(.dr-hist-time) {
|
|
704
716
|
font-size: 0.7rem;
|
|
705
717
|
color: var(--dr-text-muted);
|
|
706
718
|
}
|
|
@@ -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
|
|
5
|
-
title
|
|
6
|
-
description
|
|
72
|
+
slug,
|
|
73
|
+
title,
|
|
74
|
+
description,
|
|
7
75
|
faqTitle: 'Frequently Asked Questions',
|
|
8
76
|
bibliographyTitle: 'Chance Bibliography',
|
|
9
77
|
ui: {
|
|
@@ -14,7 +82,16 @@ export const content: DiceRollerLocaleContent = {
|
|
|
14
82
|
historyLabel: 'History',
|
|
15
83
|
clearHistoryBtn: 'Clear History',
|
|
16
84
|
faqTitle: 'FAQ',
|
|
17
|
-
bibliographyTitle: 'References'
|
|
85
|
+
bibliographyTitle: 'References',
|
|
86
|
+
addDiceLabel: 'Add dice to the bag',
|
|
87
|
+
bagLabel: 'Dice bag',
|
|
88
|
+
emptyBagBtn: 'Empty',
|
|
89
|
+
emptyBagText: 'Click the dice to add them',
|
|
90
|
+
modifierLabel: 'Modifier',
|
|
91
|
+
rollManyLabel: 'Roll $COUNT dice',
|
|
92
|
+
rollOneLabel: 'Roll $COUNT die',
|
|
93
|
+
preRollText: 'Add dice and roll',
|
|
94
|
+
emptyHistoryText: 'Roll history will appear here'
|
|
18
95
|
},
|
|
19
96
|
seo: [
|
|
20
97
|
{ type: 'title', text: 'The Art of Randomness: History and Mathematics of Dice', level: 2 },
|
|
@@ -50,38 +127,12 @@ export const content: DiceRollerLocaleContent = {
|
|
|
50
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.' },
|
|
51
128
|
]},
|
|
52
129
|
],
|
|
53
|
-
faq
|
|
54
|
-
{
|
|
55
|
-
question: 'How can I simulate a roll with advantage (D&D)?',
|
|
56
|
-
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.',
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
question: 'What does the green or red color on results mean?',
|
|
60
|
-
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.',
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
question: 'Can I add multiple dice of the same type?',
|
|
64
|
-
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.',
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
question: 'Are digital dice as random as physical ones?',
|
|
68
|
-
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.',
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
question: 'What is the d100 and how is it used?',
|
|
72
|
-
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.',
|
|
73
|
-
},
|
|
74
|
-
],
|
|
130
|
+
faq,
|
|
75
131
|
bibliography: [
|
|
76
132
|
{ name: 'D&D Beyond – Dice mechanics rules', url: 'https://www.dndbeyond.com/sources/basic-rules/using-ability-scores' },
|
|
77
133
|
{ name: 'Roll20 – Virtual tabletop and dice systems', url: 'https://roll20.net/' },
|
|
78
134
|
{ name: 'Pathfinder – d20 System Reference', url: 'https://paizo.com/pathfinder' },
|
|
79
135
|
],
|
|
80
|
-
howTo
|
|
81
|
-
|
|
82
|
-
{ 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.' },
|
|
83
|
-
{ 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.' },
|
|
84
|
-
{ 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.' },
|
|
85
|
-
],
|
|
86
|
-
schemas: []
|
|
136
|
+
howTo,
|
|
137
|
+
schemas: [faqSchema as any, howToSchema as any, appSchema],
|
|
87
138
|
};
|
|
@@ -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 = 'lanzador-dados';
|
|
5
|
+
const title = 'Tirador de Dados';
|
|
6
|
+
const description = 'Simulador de dados virtual con bolsa personalizable, modificadores y historial de tiradas para tus juegos de rol y tablero.';
|
|
7
|
+
|
|
8
|
+
const faq: DiceRollerLocaleContent['faq'] = [
|
|
9
|
+
{
|
|
10
|
+
question: '¿Cómo puedo simular una tirada con ventaja (D&D)?',
|
|
11
|
+
answer: 'Añade dos dados d20 a la bolsa haciendo clic dos veces en el botón d20. Al lanzar, observa los dos resultados individuales y quédate con el mayor. El total mostrado será la suma, pero puedes ver cada dado por separado en el desglose de resultados.',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
question: '¿Qué significa el color verde o rojo en los resultados?',
|
|
15
|
+
answer: 'Los resultados en verde indican que ese dado ha sacado su valor máximo posible (un "crítico"). Los resultados en rojo indican el valor mínimo (un "1", el peor resultado posible). Esto facilita identificar críticos y pifias de un vistazo.',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
question: '¿Puedo añadir varios dados del mismo tipo?',
|
|
19
|
+
answer: 'Sí. Cada clic en un dado lo añade a la bolsa. Si haces clic tres veces en d6, la bolsa mostrará "3d6". Para reducir la cantidad, haz clic en el botón "−" que aparece junto a cada grupo de dados en la bolsa.',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
question: '¿Los dados digitales son tan aleatorios como los físicos?',
|
|
23
|
+
answer: 'Estadísticamente, sí. Los motores JavaScript modernos usan algoritmos pseudoaleatorios (xorshift128+) con distribución uniforme de muy alta calidad. Un dado físico real puede tener pequeñas imperfecciones de fabricación que sesguen los resultados; el dado digital no tiene ese problema.',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
question: '¿Qué es el d100 y cómo se usa?',
|
|
27
|
+
answer: 'El d100 (o d%) genera un número del 1 al 100 y se usa en sistemas de juego basados en porcentajes, como Call of Cthulhu o Warhammer Fantasy Roleplay. Representa "probabilidad directa": si tu habilidad de Sigilo es 65%, necesitas sacar 65 o menos para tener éxito.',
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const howTo: DiceRollerLocaleContent['howTo'] = [
|
|
32
|
+
{ name: 'Construir la bolsa de dados', text: 'Haz clic en los botones de dado (d4, d6, d8...) para añadirlos a tu bolsa. Cada clic añade un dado del tipo seleccionado. Puedes mezclar tipos distintos en la misma tirada.' },
|
|
33
|
+
{ name: 'Ajustar el modificador', text: 'Usa los botones "+" y "−" junto al modificador para aplicar bonificaciones o penalizaciones (como el modificador de habilidad en D&D). El modificador se suma automáticamente al total.' },
|
|
34
|
+
{ name: 'Lanzar los dados', text: 'Pulsa el botón "Lanzar dados". El panel derecho muestra el total final y el desglose de cada dado individual, con los críticos (máximo) en verde y las pifias (mínimo) en rojo.' },
|
|
35
|
+
{ name: 'Consultar el historial', text: 'Cada tirada queda registrada en el historial con la expresión de dados usada, el resultado total y la hora exacta. Puedes limpiar el historial con el botón correspondiente.' },
|
|
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: 'es',
|
|
69
|
+
};
|
|
70
|
+
|
|
3
71
|
export const content: DiceRollerLocaleContent = {
|
|
4
|
-
slug
|
|
5
|
-
title
|
|
6
|
-
description
|
|
72
|
+
slug,
|
|
73
|
+
title,
|
|
74
|
+
description,
|
|
7
75
|
faqTitle: 'Preguntas Frecuentes',
|
|
8
76
|
bibliographyTitle: 'Bibliografía del Azar',
|
|
9
77
|
ui: {
|
|
@@ -14,7 +82,16 @@ export const content: DiceRollerLocaleContent = {
|
|
|
14
82
|
historyLabel: 'Historial',
|
|
15
83
|
clearHistoryBtn: 'Limpiar Historial',
|
|
16
84
|
faqTitle: 'FAQ',
|
|
17
|
-
bibliographyTitle: 'Referencias'
|
|
85
|
+
bibliographyTitle: 'Referencias',
|
|
86
|
+
addDiceLabel: 'Añadir dados a la bolsa',
|
|
87
|
+
bagLabel: 'Bolsa de dados',
|
|
88
|
+
emptyBagBtn: 'Vaciar',
|
|
89
|
+
emptyBagText: 'Haz clic en los dados para añadirlos',
|
|
90
|
+
modifierLabel: 'Modificador',
|
|
91
|
+
rollManyLabel: 'Lanzar $COUNT dados',
|
|
92
|
+
rollOneLabel: 'Lanzar $COUNT dado',
|
|
93
|
+
preRollText: 'Añade dados y lanza',
|
|
94
|
+
emptyHistoryText: 'El historial de tiradas aparecerá aquí'
|
|
18
95
|
},
|
|
19
96
|
seo: [
|
|
20
97
|
{ type: 'title', text: 'El arte de la aleatoriedad: historia y matemáticas de los dados', level: 2 },
|
|
@@ -52,38 +129,12 @@ export const content: DiceRollerLocaleContent = {
|
|
|
52
129
|
{ term: 'Tirada percentil', definition: 'Tirada usando dos d10 para producir un resultado del 1 al 100, usada en sistemas de habilidades basados en porcentajes.' },
|
|
53
130
|
]},
|
|
54
131
|
],
|
|
55
|
-
faq
|
|
56
|
-
{
|
|
57
|
-
question: '¿Cómo puedo simular una tirada con ventaja (D&D)?',
|
|
58
|
-
answer: 'Añade dos dados d20 a la bolsa haciendo clic dos veces en el botón d20. Al lanzar, observa los dos resultados individuales y quédate con el mayor. El total mostrado será la suma, pero puedes ver cada dado por separado en el desglose de resultados.',
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
question: '¿Qué significa el color verde o rojo en los resultados?',
|
|
62
|
-
answer: 'Los resultados en verde indican que ese dado ha sacado su valor máximo posible (un "crítico"). Los resultados en rojo indican el valor mínimo (un "1", el peor resultado posible). Esto facilita identificar críticos y pifias de un vistazo.',
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
question: '¿Puedo añadir varios dados del mismo tipo?',
|
|
66
|
-
answer: 'Sí. Cada clic en un dado lo añade a la bolsa. Si haces clic tres veces en d6, la bolsa mostrará "3d6". Para reducir la cantidad, haz clic en el botón "−" que aparece junto a cada grupo de dados en la bolsa.',
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
question: '¿Los dados digitales son tan aleatorios como los físicos?',
|
|
70
|
-
answer: 'Estadísticamente, sí. Los motores JavaScript modernos usan algoritmos pseudoaleatorios (xorshift128+) con distribución uniforme de muy alta calidad. Un dado físico real puede tener pequeñas imperfecciones de fabricación que sesguen los resultados; el dado digital no tiene ese problema.',
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
question: '¿Qué es el d100 y cómo se usa?',
|
|
74
|
-
answer: 'El d100 (o d%) genera un número del 1 al 100 y se usa en sistemas de juego basados en porcentajes, como Call of Cthulhu o Warhammer Fantasy Roleplay. Representa "probabilidad directa": si tu habilidad de Sigilo es 65%, necesitas sacar 65 o menos para tener éxito.',
|
|
75
|
-
},
|
|
76
|
-
],
|
|
132
|
+
faq,
|
|
77
133
|
bibliography: [
|
|
78
134
|
{ name: 'D&D Beyond – Reglas de mecánicas de dados', url: 'https://www.dndbeyond.com/sources/basic-rules/using-ability-scores' },
|
|
79
135
|
{ name: 'Roll20 – Virtual tabletop y sistemas de dados', url: 'https://roll20.net/' },
|
|
80
136
|
{ name: 'Pathfinder – Sistema d20 de referencia', url: 'https://paizo.com/pathfinder' },
|
|
81
137
|
],
|
|
82
|
-
howTo
|
|
83
|
-
|
|
84
|
-
{ name: 'Ajustar el modificador', text: 'Usa los botones "+" y "−" junto al modificador para aplicar bonificaciones o penalizaciones (como el modificador de habilidad en D&D). El modificador se suma automáticamente al total.' },
|
|
85
|
-
{ name: 'Lanzar los dados', text: 'Pulsa el botón "Lanzar dados". El panel derecho muestra el total final y el desglose de cada dado individual, con los críticos (máximo) en verde y las pifias (mínimo) en rojo.' },
|
|
86
|
-
{ name: 'Consultar el historial', text: 'Cada tirada queda registrada en el historial con la expresión de dados usada, el resultado total y la hora exacta. Puedes limpiar el historial con el botón correspondiente.' },
|
|
87
|
-
],
|
|
88
|
-
schemas: []
|
|
138
|
+
howTo,
|
|
139
|
+
schemas: [faqSchema as any, howToSchema as any, appSchema],
|
|
89
140
|
};
|