@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
package/package.json
CHANGED
|
@@ -1,64 +1,64 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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('
|
|
39
|
-
expect(ALL_TOOLS.length).
|
|
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
|
|
8
|
-
expect(ALL_TOOLS.length).toBe(
|
|
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,4 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
+
import type { BeadPatternGeneratorUI } from './index';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
ui: BeadPatternGeneratorUI;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { ui } = Astro.props;
|
|
2
9
|
---
|
|
3
10
|
|
|
4
11
|
<div class="bead-wrapper">
|
|
@@ -8,13 +15,13 @@
|
|
|
8
15
|
<div class="bead-inner">
|
|
9
16
|
<div class="bead-header">
|
|
10
17
|
<div class="bead-header-title">
|
|
11
|
-
<h2>
|
|
12
|
-
<p>
|
|
18
|
+
<h2>{ui.title}</h2>
|
|
19
|
+
<p>{ui.subtitle}</p>
|
|
13
20
|
</div>
|
|
14
21
|
|
|
15
22
|
<div class="bead-controls">
|
|
16
23
|
<div class="bead-control-group">
|
|
17
|
-
<label class="bead-control-label">
|
|
24
|
+
<label class="bead-control-label">{ui.gridSizeLabel}</label>
|
|
18
25
|
<div class="bead-slider-row">
|
|
19
26
|
<input type="range" id="grid-width" min="10" max="100" value="40" class="bead-slider bead-slider-pink" />
|
|
20
27
|
<span id="grid-width-value" class="bead-slider-val bead-val-pink">40</span>
|
|
@@ -22,7 +29,7 @@
|
|
|
22
29
|
</div>
|
|
23
30
|
|
|
24
31
|
<div class="bead-control-group">
|
|
25
|
-
<label class="bead-control-label">
|
|
32
|
+
<label class="bead-control-label">{ui.colorCountLabel}</label>
|
|
26
33
|
<div class="bead-slider-row">
|
|
27
34
|
<input type="range" id="color-count" min="2" max="32" value="12" class="bead-slider bead-slider-purple" />
|
|
28
35
|
<span id="color-count-value" class="bead-slider-val bead-val-purple">12</span>
|
|
@@ -30,16 +37,16 @@
|
|
|
30
37
|
</div>
|
|
31
38
|
|
|
32
39
|
<div class="bead-control-group">
|
|
33
|
-
<label class="bead-control-label">
|
|
40
|
+
<label class="bead-control-label">{ui.optionsLabel}</label>
|
|
34
41
|
<div class="bead-options-row">
|
|
35
42
|
<label class="bead-checkbox-label">
|
|
36
43
|
<input type="checkbox" id="show-rulers" class="bead-checkbox" />
|
|
37
|
-
<span>
|
|
44
|
+
<span>{ui.rulersLabel}</span>
|
|
38
45
|
</label>
|
|
39
46
|
<div class="bead-divider"></div>
|
|
40
|
-
<label class="bead-checkbox-label" title=
|
|
47
|
+
<label class="bead-checkbox-label" title={ui.symbolsTooltip}>
|
|
41
48
|
<input type="checkbox" id="show-symbols" class="bead-checkbox" />
|
|
42
|
-
<span>
|
|
49
|
+
<span>{ui.symbolsLabel}</span>
|
|
43
50
|
</label>
|
|
44
51
|
</div>
|
|
45
52
|
</div>
|
|
@@ -48,7 +55,7 @@
|
|
|
48
55
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
|
|
49
56
|
<path fill-rule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd"></path>
|
|
50
57
|
</svg>
|
|
51
|
-
<span>
|
|
58
|
+
<span>{ui.downloadBtn}</span>
|
|
52
59
|
</button>
|
|
53
60
|
</div>
|
|
54
61
|
</div>
|
|
@@ -62,21 +69,21 @@
|
|
|
62
69
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
|
63
70
|
</svg>
|
|
64
71
|
</div>
|
|
65
|
-
<h3>
|
|
66
|
-
<p>
|
|
72
|
+
<h3>{ui.uploadTitle}</h3>
|
|
73
|
+
<p>{ui.uploadSubtitle}</p>
|
|
67
74
|
</div>
|
|
68
75
|
</div>
|
|
69
76
|
|
|
70
77
|
<div id="result-area" class="bead-result bead-hidden">
|
|
71
78
|
<div class="bead-result-inner">
|
|
72
79
|
<div class="bead-palette-col">
|
|
73
|
-
<h3 class="bead-palette-title">
|
|
80
|
+
<h3 class="bead-palette-title">{ui.paletteTitle}</h3>
|
|
74
81
|
<div id="palette-grid" class="bead-palette-grid"></div>
|
|
75
82
|
<button id="reupload-btn" class="bead-reupload-btn">
|
|
76
83
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
77
84
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
|
78
85
|
</svg>
|
|
79
|
-
|
|
86
|
+
{ui.reuploadBtn}
|
|
80
87
|
</button>
|
|
81
88
|
</div>
|
|
82
89
|
<div class="bead-canvas-col">
|
|
@@ -86,8 +93,8 @@
|
|
|
86
93
|
|
|
87
94
|
<div class="bead-tunnel-bar">
|
|
88
95
|
<div>
|
|
89
|
-
<h3 class="bead-tunnel-title">
|
|
90
|
-
<p class="bead-tunnel-sub">
|
|
96
|
+
<h3 class="bead-tunnel-title">{ui.tunnelVisionTitle}</h3>
|
|
97
|
+
<p class="bead-tunnel-sub">{ui.tunnelVisionSubtitle}</p>
|
|
91
98
|
</div>
|
|
92
99
|
<div class="bead-tunnel-controls">
|
|
93
100
|
<button id="focus-prev" class="bead-tunnel-btn">▲</button>
|
|
@@ -590,6 +597,7 @@
|
|
|
590
597
|
min-height: 25rem;
|
|
591
598
|
display: flex;
|
|
592
599
|
flex-direction: column;
|
|
600
|
+
gap: 3rem;
|
|
593
601
|
}
|
|
594
602
|
|
|
595
603
|
.bead-drop-zone {
|
|
@@ -716,13 +724,13 @@
|
|
|
716
724
|
gap: 0.5rem;
|
|
717
725
|
}
|
|
718
726
|
|
|
719
|
-
.bead-palette-item {
|
|
727
|
+
:global(.bead-palette-item) {
|
|
720
728
|
display: flex;
|
|
721
729
|
flex-direction: column;
|
|
722
730
|
align-items: center;
|
|
723
731
|
}
|
|
724
732
|
|
|
725
|
-
.bead-color-swatch {
|
|
733
|
+
:global(.bead-color-swatch) {
|
|
726
734
|
width: 100%;
|
|
727
735
|
aspect-ratio: 1;
|
|
728
736
|
border-radius: 50%;
|
|
@@ -734,21 +742,21 @@
|
|
|
734
742
|
justify-content: center;
|
|
735
743
|
}
|
|
736
744
|
|
|
737
|
-
:global(.theme-dark
|
|
745
|
+
:global(.theme-dark .bead-color-swatch) {
|
|
738
746
|
border-color: #1f2937;
|
|
739
747
|
outline-color: #374151;
|
|
740
748
|
}
|
|
741
749
|
|
|
742
|
-
.bead-symbol {
|
|
750
|
+
:global(.bead-symbol) {
|
|
743
751
|
font-size: 0.75rem;
|
|
744
752
|
font-weight: 700;
|
|
745
753
|
user-select: none;
|
|
746
754
|
}
|
|
747
|
-
.bead-symbol-dark {
|
|
755
|
+
:global(.bead-symbol-dark) {
|
|
748
756
|
color: #000;
|
|
749
757
|
}
|
|
750
758
|
|
|
751
|
-
.bead-symbol-light {
|
|
759
|
+
:global(.bead-symbol-light) {
|
|
752
760
|
color: #fff;
|
|
753
761
|
}
|
|
754
762
|
|
|
@@ -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 = '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
|
|
5
|
-
title
|
|
6
|
-
description
|
|
56
|
+
slug,
|
|
57
|
+
title,
|
|
58
|
+
description,
|
|
7
59
|
faqTitle: 'Frequently Asked Questions',
|
|
8
60
|
bibliographyTitle: 'Artisan Bibliography',
|
|
9
61
|
ui: {
|
|
10
|
-
title: 'Pattern
|
|
62
|
+
title: 'Pattern Laboratory',
|
|
63
|
+
subtitle: 'Color engineering for your hands',
|
|
11
64
|
description: 'From photo to bead scheme.',
|
|
65
|
+
gridSizeLabel: 'Size (Width)',
|
|
66
|
+
colorCountLabel: 'Colors',
|
|
67
|
+
optionsLabel: 'Options',
|
|
68
|
+
rulersLabel: 'Rulers',
|
|
69
|
+
symbolsTooltip: 'Surprise Pattern',
|
|
70
|
+
symbolsLabel: 'Symbols',
|
|
71
|
+
downloadBtn: 'Download',
|
|
72
|
+
uploadTitle: 'Upload Your Vision',
|
|
73
|
+
uploadSubtitle: 'and let the digital magic happen',
|
|
74
|
+
paletteTitle: 'Chromatic DNA',
|
|
75
|
+
reuploadBtn: 'Change Image',
|
|
76
|
+
tunnelVisionTitle: 'Tunnel Vision',
|
|
77
|
+
tunnelVisionSubtitle: 'Your precision assistant row by row.',
|
|
12
78
|
uploadLabel: 'Upload your photo',
|
|
13
|
-
gridSizeLabel: 'Grid size (beads)',
|
|
14
79
|
pixelateBtn: 'Generate Pattern',
|
|
15
|
-
downloadBtn: 'Download Scheme',
|
|
16
80
|
faqTitle: 'FAQ',
|
|
17
81
|
bibliographyTitle: 'References'
|
|
18
82
|
},
|
|
@@ -40,22 +104,12 @@ export const content: BeadPatternGeneratorLocaleContent = {
|
|
|
40
104
|
], columns: 4 },
|
|
41
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.' },
|
|
42
106
|
],
|
|
43
|
-
faq
|
|
44
|
-
{ 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.' },
|
|
45
|
-
{ 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.).' },
|
|
46
|
-
{ 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.' },
|
|
47
|
-
{ 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.' },
|
|
48
|
-
],
|
|
107
|
+
faq,
|
|
49
108
|
bibliography: [
|
|
50
109
|
{ name: 'Scikit-Image: Color Quantization using K-Means', url: 'https://scikit-learn.org/0.23/auto_examples/cluster/plot_color_quantization.html' },
|
|
51
110
|
{ name: 'Miyuki Delica Beads Specification', url: 'https://www.miyuki-beads.co.jp/english/seedbeads/delica.html' },
|
|
52
111
|
{ name: 'Visgraf Lab: Dithering Algorithms', url: 'https://www.visgraf.impa.br/Courses/ip00/proj/Dithering1/floyd_steinberg_dithering.html' },
|
|
53
112
|
],
|
|
54
|
-
howTo
|
|
55
|
-
|
|
56
|
-
{ 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.' },
|
|
57
|
-
{ name: 'Optimize the color palette', text: 'Reduce the number of colors until they match the beads you have available in your craft kit.' },
|
|
58
|
-
{ 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.' },
|
|
59
|
-
],
|
|
60
|
-
schemas: []
|
|
113
|
+
howTo,
|
|
114
|
+
schemas: [faqSchema as any, howToSchema as any, appSchema],
|
|
61
115
|
};
|
|
@@ -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 = '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
|
|
5
|
-
title
|
|
6
|
-
description
|
|
56
|
+
slug,
|
|
57
|
+
title,
|
|
58
|
+
description,
|
|
7
59
|
faqTitle: 'Preguntas Frecuentes',
|
|
8
60
|
bibliographyTitle: 'Bibliografía del Artesano',
|
|
9
61
|
ui: {
|
|
10
|
-
title: '
|
|
62
|
+
title: 'Laboratorio de Patrones',
|
|
63
|
+
subtitle: 'Ingeniería de color para tus manos',
|
|
11
64
|
description: 'De fotografía a esquema de cuentas.',
|
|
65
|
+
gridSizeLabel: 'Tamaño (Ancho)',
|
|
66
|
+
colorCountLabel: 'Colores',
|
|
67
|
+
optionsLabel: 'Opciones',
|
|
68
|
+
rulersLabel: 'Reglas',
|
|
69
|
+
symbolsTooltip: 'Patrón Sorpresa',
|
|
70
|
+
symbolsLabel: 'Símbolos',
|
|
71
|
+
downloadBtn: 'Descargar',
|
|
72
|
+
uploadTitle: 'Carga tu Visión',
|
|
73
|
+
uploadSubtitle: 'y deja que la magia digital ocurra',
|
|
74
|
+
paletteTitle: 'ADN Cromático',
|
|
75
|
+
reuploadBtn: 'Cambiar Imagen',
|
|
76
|
+
tunnelVisionTitle: 'Visión de Túnel',
|
|
77
|
+
tunnelVisionSubtitle: 'Tu asistente de precisión fila por fila.',
|
|
12
78
|
uploadLabel: 'Sube tu foto',
|
|
13
|
-
gridSizeLabel: 'Tamaño de la cuadrícula (beads)',
|
|
14
79
|
pixelateBtn: 'Generar Patrón',
|
|
15
|
-
downloadBtn: 'Descargar Esquema',
|
|
16
80
|
faqTitle: 'FAQ',
|
|
17
81
|
bibliographyTitle: 'Referencias'
|
|
18
82
|
},
|
|
@@ -47,22 +111,12 @@ export const content: BeadPatternGeneratorLocaleContent = {
|
|
|
47
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.' },
|
|
48
112
|
]},
|
|
49
113
|
],
|
|
50
|
-
faq
|
|
51
|
-
{ 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.' },
|
|
52
|
-
{ 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.).' },
|
|
53
|
-
{ 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.' },
|
|
54
|
-
{ 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.' },
|
|
55
|
-
],
|
|
114
|
+
faq,
|
|
56
115
|
bibliography: [
|
|
57
116
|
{ name: 'Scikit-Image: Color Quantization using K-Means', url: 'https://scikit-learn.org/0.23/auto_examples/cluster/plot_color_quantization.html' },
|
|
58
117
|
{ name: 'Miyuki Delica Beads Specification', url: 'https://www.miyuki-beads.co.jp/english/seedbeads/delica.html' },
|
|
59
118
|
{ name: 'Visgraf Lab: Dithering Algorithms', url: 'https://www.visgraf.impa.br/Courses/ip00/proj/Dithering1/floyd_steinberg_dithering.html' },
|
|
60
119
|
],
|
|
61
|
-
howTo
|
|
62
|
-
|
|
63
|
-
{ 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.' },
|
|
64
|
-
{ 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.' },
|
|
65
|
-
{ 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.' },
|
|
66
|
-
],
|
|
67
|
-
schemas: []
|
|
120
|
+
howTo,
|
|
121
|
+
schemas: [faqSchema as any, howToSchema as any, appSchema],
|
|
68
122
|
};
|