@jjlmoya/utils-games-development 1.66.0 → 1.68.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/package.json +67 -67
  2. package/scripts/postinstall.mjs +27 -27
  3. package/src/category/GamesCategorySEO.astro +19 -19
  4. package/src/category/i18n/de.ts +15 -15
  5. package/src/category/i18n/en.ts +15 -15
  6. package/src/category/i18n/es.ts +15 -15
  7. package/src/category/i18n/fr.ts +15 -15
  8. package/src/category/i18n/id.ts +10 -10
  9. package/src/category/i18n/it.ts +10 -10
  10. package/src/category/i18n/ja.ts +10 -10
  11. package/src/category/i18n/ko.ts +10 -10
  12. package/src/category/i18n/nl.ts +10 -10
  13. package/src/category/i18n/pl.ts +10 -10
  14. package/src/category/i18n/pt.ts +10 -10
  15. package/src/category/i18n/ru.ts +10 -10
  16. package/src/category/i18n/sv.ts +10 -10
  17. package/src/category/i18n/tr.ts +10 -10
  18. package/src/category/i18n/zh.ts +10 -10
  19. package/src/category/seo.astro +15 -15
  20. package/src/components/PreviewNavSidebar.astro +116 -116
  21. package/src/components/PreviewToolbar.astro +143 -143
  22. package/src/data.ts +11 -11
  23. package/src/env.d.ts +5 -5
  24. package/src/index.ts +20 -20
  25. package/src/layouts/PreviewLayout.astro +122 -122
  26. package/src/pages/[locale]/[slug].astro +165 -165
  27. package/src/pages/[locale].astro +250 -250
  28. package/src/pages/index.astro +4 -4
  29. package/src/tests/category_seo_quality.test.ts +74 -0
  30. package/src/tests/diacritics_density.test.ts +118 -118
  31. package/src/tests/faq_count.test.ts +19 -19
  32. package/src/tests/i18n_coverage.test.ts +36 -36
  33. package/src/tests/inverted_punctuation.test.ts +84 -84
  34. package/src/tests/mocks/astro_mock.js +2 -2
  35. package/src/tests/no_en_dash.test.ts +70 -70
  36. package/src/tests/no_h1_in_components.test.ts +48 -48
  37. package/src/tests/pagespeed_best_practices.test.ts +198 -198
  38. package/src/tests/qa-test-helpers.ts +32 -32
  39. package/src/tests/qa_bibliography_links.test.ts +41 -41
  40. package/src/tests/qa_claim_evidence.test.ts +69 -69
  41. package/src/tests/qa_logic_reference_coverage.test.ts +46 -46
  42. package/src/tests/qa_runtime_i18n.test.ts +100 -100
  43. package/src/tests/schemas_fulfillment.test.ts +23 -23
  44. package/src/tests/script_density.test.ts +94 -94
  45. package/src/tests/seo_length.test.ts +23 -23
  46. package/src/tests/seo_translation_completeness.test.ts +18 -12
  47. package/src/tests/slug_language_code_format.test.ts +23 -23
  48. package/src/tests/slug_uniqueness.test.ts +80 -80
  49. package/src/tests/title_quality.test.ts +56 -56
  50. package/src/tool/audioLoopPointFinder/audio-loop-point-finder.css +322 -322
  51. package/src/tool/audioLoopPointFinder/client.ts +262 -262
  52. package/src/tool/hitboxHurtboxAnimator/hitbox-hurtbox-animator.css +614 -614
  53. package/src/tool/saveFileEditor/component.astro +351 -351
  54. package/src/tool/saveFileEditor/game-save-file-editor.css +250 -250
  55. package/src/tool/spriteSheetPacker/app-client.ts +248 -248
  56. package/src/tool/spriteSheetPacker/bibliography.ts +12 -12
  57. package/src/tool/spriteSheetPacker/component.astro +229 -229
  58. package/src/tool/spriteSheetPacker/dropzone-client.ts +55 -55
  59. package/src/tool/spriteSheetPacker/entry.ts +28 -28
  60. package/src/tool/spriteSheetPacker/exporter.ts +116 -116
  61. package/src/tool/spriteSheetPacker/extractor-client.ts +127 -127
  62. package/src/tool/spriteSheetPacker/flipbook-client.ts +60 -60
  63. package/src/tool/spriteSheetPacker/logic.test.ts +155 -155
  64. package/src/tool/spriteSheetPacker/logic.ts +32 -32
  65. package/src/tool/spriteSheetPacker/packer-core.ts +121 -121
  66. package/src/tool/spriteSheetPacker/packer-ui.ts +86 -86
  67. package/src/tool/spriteSheetPacker/sprite-sheet-packer.css +578 -578
  68. package/src/tool/spriteSheetPacker/types.ts +89 -89
  69. package/src/tool/spriteSheetPacker/ui.ts +42 -42
  70. package/src/tool/steamCapsuleGenerator/index.ts +9 -9
  71. package/src/tool/steamCapsuleGenerator/validation.ts +14 -14
  72. package/src/types.ts +72 -72
@@ -1,229 +1,229 @@
1
- ---
2
- import type { SpriteSheetPackerUI } from './ui';
3
- import './sprite-sheet-packer.css';
4
-
5
- interface Props {
6
- ui: SpriteSheetPackerUI;
7
- }
8
-
9
- const { ui } = Astro.props;
10
- ---
11
-
12
- <div class="ssp-container">
13
- <div class="ssp-main-card">
14
- <div class="ssp-header-tabs" role="tablist">
15
- <button id="ssp-tab-packer" class="ssp-tab-btn active" role="tab" aria-selected="true" aria-controls="ssp-packer-panel">
16
- {ui.packerTab}
17
- </button>
18
- <button id="ssp-tab-extractor" class="ssp-tab-btn" role="tab" aria-selected="false" aria-controls="ssp-extractor-panel">
19
- {ui.extractorTab}
20
- </button>
21
- </div>
22
-
23
- <div id="ssp-packer-panel" class="ssp-body-grid" role="tabpanel" aria-labelledby="ssp-tab-packer">
24
- <div class="ssp-sidebar-section">
25
- <div id="ssp-dropzone" class="ssp-dropzone">
26
- <div class="ssp-drop-title">{ui.dropZoneTitle}</div>
27
- <div class="ssp-drop-sub">{ui.dropZoneSubtitle}</div>
28
- <input type="file" id="ssp-file-input" class="ssp-file-input" multiple accept="image/png,image/jpeg,image/webp" aria-label={ui.selectFilesButton} />
29
- </div>
30
-
31
- <div class="ssp-btn-row">
32
- <button id="ssp-btn-select" class="ssp-btn ssp-btn-primary" type="button">{ui.selectFilesButton}</button>
33
- <button id="ssp-btn-clear" class="ssp-btn ssp-btn-danger" type="button">{ui.clearAllButton}</button>
34
- </div>
35
-
36
- <div class="ssp-presets-row">
37
- <button id="ssp-preset-pixel" class="ssp-chip-btn" type="button">{ui.presetPixelArt}</button>
38
- <button id="ssp-preset-hd" class="ssp-chip-btn" type="button">{ui.presetHdUi}</button>
39
- <button id="ssp-preset-mobile" class="ssp-chip-btn" type="button">{ui.presetMobile}</button>
40
- </div>
41
-
42
- <div class="ssp-controls-list">
43
- <div class="ssp-control-group">
44
- <label for="ssp-export-format" class="ssp-label"><span>{ui.exportFormatLabel}</span></label>
45
- <select id="ssp-export-format" class="ssp-select">
46
- <option value="generic-json-hash">{ui.formatGenericHash}</option>
47
- <option value="generic-json-array">{ui.formatGenericArray}</option>
48
- <option value="unity">{ui.formatUnity}</option>
49
- <option value="godot">{ui.formatGodot}</option>
50
- <option value="phaser">{ui.formatPhaser}</option>
51
- <option value="css">{ui.formatCss}</option>
52
- </select>
53
- </div>
54
-
55
- <div class="ssp-control-group">
56
- <label for="ssp-padding" class="ssp-label">
57
- <span>{ui.paddingLabel}</span>
58
- <span id="ssp-padding-val">2</span>
59
- </label>
60
- <div class="ssp-stepper-box">
61
- <button id="ssp-pad-dec" class="ssp-step-btn" type="button" aria-label="Decrease Padding">-</button>
62
- <input type="range" id="ssp-padding" class="ssp-slider" min="0" max="32" value="2" aria-label={ui.paddingLabel} />
63
- <button id="ssp-pad-inc" class="ssp-step-btn" type="button" aria-label="Increase Padding">+</button>
64
- </div>
65
- </div>
66
-
67
- <div class="ssp-control-group">
68
- <label for="ssp-extrusion" class="ssp-label">
69
- <span>{ui.borderExtrusionLabel}</span>
70
- <span id="ssp-extrusion-val">0</span>
71
- </label>
72
- <div class="ssp-stepper-box">
73
- <button id="ssp-ext-dec" class="ssp-step-btn" type="button" aria-label="Decrease Extrusion">-</button>
74
- <input type="range" id="ssp-extrusion" class="ssp-slider" min="0" max="8" value="0" aria-label={ui.borderExtrusionLabel} />
75
- <button id="ssp-ext-inc" class="ssp-step-btn" type="button" aria-label="Increase Extrusion">+</button>
76
- </div>
77
- </div>
78
-
79
- <div class="ssp-control-group">
80
- <label for="ssp-max-size" class="ssp-label"><span>{ui.maxTextureSizeLabel}</span></label>
81
- <select id="ssp-max-size" class="ssp-select">
82
- <option value="512">512 x 512 px</option>
83
- <option value="1024">1024 x 1024 px</option>
84
- <option value="2048" selected>2048 x 2048 px</option>
85
- <option value="4096">4096 x 4096 px</option>
86
- </select>
87
- </div>
88
-
89
- <div class="ssp-toggle-group">
90
- <label for="ssp-power-two" class="ssp-label">{ui.powerOfTwoLabel}</label>
91
- <input type="checkbox" id="ssp-power-two" class="ssp-toggle-input" checked aria-label={ui.powerOfTwoLabel} />
92
- </div>
93
- </div>
94
- </div>
95
-
96
- <div class="ssp-main-section">
97
- <div class="ssp-stats-grid">
98
- <div class="ssp-stat-badge">
99
- <div id="ssp-stat-efficiency" class="ssp-stat-val">0%</div>
100
- <div class="ssp-stat-lbl">{ui.efficiencyBadge}</div>
101
- </div>
102
- <div class="ssp-stat-badge">
103
- <div id="ssp-stat-drawcalls" class="ssp-stat-val">0</div>
104
- <div class="ssp-stat-lbl">{ui.drawCallsBadge}</div>
105
- </div>
106
- <div class="ssp-stat-badge">
107
- <div id="ssp-stat-frames" class="ssp-stat-val">0</div>
108
- <div class="ssp-stat-lbl">{ui.totalFramesBadge}</div>
109
- </div>
110
- <div class="ssp-stat-badge">
111
- <div id="ssp-stat-size" class="ssp-stat-val">0x0</div>
112
- <div class="ssp-stat-lbl">{ui.textureSizeBadge}</div>
113
- </div>
114
- </div>
115
-
116
- <div class="ssp-canvas-wrapper">
117
- <canvas id="ssp-atlas-canvas" class="ssp-canvas" width="512" height="512" aria-label={ui.previewTitle}></canvas>
118
- </div>
119
-
120
- <div class="ssp-btn-row">
121
- <button id="ssp-btn-download-zip" class="ssp-btn ssp-btn-primary" type="button">{ui.downloadZipButton}</button>
122
- <button id="ssp-btn-copy-json" class="ssp-btn ssp-btn-secondary" type="button">{ui.copyJsonButton}</button>
123
- <button id="ssp-btn-download-png" class="ssp-btn ssp-btn-secondary" type="button">{ui.downloadSheetPngButton}</button>
124
- </div>
125
-
126
- <div class="ssp-flipbook-box">
127
- <div class="ssp-flipbook-header">{ui.flipbookTitle}</div>
128
- <div class="ssp-flipbook-body">
129
- <canvas id="ssp-flipbook-canvas" class="ssp-flipbook-canvas" width="128" height="128" aria-label={ui.flipbookTitle}></canvas>
130
- <div class="ssp-flipbook-controls">
131
- <div class="ssp-control-group">
132
- <label for="ssp-fps-slider" class="ssp-label">
133
- <span>{ui.flipbookFpsLabel}</span>
134
- <span id="ssp-fps-val">12 FPS</span>
135
- </label>
136
- <input type="range" id="ssp-fps-slider" class="ssp-slider" min="1" max="60" value="12" aria-label={ui.flipbookFpsLabel} />
137
- </div>
138
- <div class="ssp-btn-row">
139
- <button id="ssp-btn-play" class="ssp-btn ssp-btn-secondary" type="button">{ui.playAnimation}</button>
140
- <button id="ssp-btn-pause" class="ssp-btn ssp-btn-secondary" type="button">{ui.pauseAnimation}</button>
141
- </div>
142
- </div>
143
- </div>
144
- </div>
145
-
146
- <div class="ssp-code-container">
147
- <div class="ssp-code-header">
148
- <span class="ssp-code-title">{ui.codeSnippetTitle}</span>
149
- <button id="ssp-btn-copy-code" class="ssp-copy-btn" type="button">{ui.copySnippetButton}</button>
150
- </div>
151
- <pre id="ssp-code-snippet" class="ssp-code-block"></pre>
152
- </div>
153
- </div>
154
- </div>
155
-
156
- <div id="ssp-extractor-panel" class="ssp-body-grid" role="tabpanel" aria-labelledby="ssp-tab-extractor" style="display: none;">
157
- <div class="ssp-sidebar-section">
158
- <div id="ssp-ext-dropzone" class="ssp-dropzone">
159
- <div class="ssp-drop-title">{ui.dropZoneTitle}</div>
160
- <div class="ssp-drop-sub">{ui.dropZoneSubtitle}</div>
161
- <input type="file" id="ssp-ext-file-input" class="ssp-file-input" accept="image/png,image/jpeg,image/webp" aria-label={ui.selectFilesButton} />
162
- </div>
163
-
164
- <div class="ssp-controls-list">
165
- <div class="ssp-control-group">
166
- <label for="ssp-ext-width" class="ssp-label">{ui.frameWidthLabel}</label>
167
- <div class="ssp-stepper-box">
168
- <button id="ssp-fw-dec" class="ssp-step-btn" type="button" aria-label="Decrease Frame Width">-</button>
169
- <input type="number" id="ssp-ext-width" class="ssp-input" value="32" min="1" aria-label={ui.frameWidthLabel} />
170
- <button id="ssp-fw-inc" class="ssp-step-btn" type="button" aria-label="Increase Frame Width">+</button>
171
- </div>
172
- </div>
173
-
174
- <div class="ssp-control-group">
175
- <label for="ssp-ext-height" class="ssp-label">{ui.frameHeightLabel}</label>
176
- <div class="ssp-stepper-box">
177
- <button id="ssp-fh-dec" class="ssp-step-btn" type="button" aria-label="Decrease Frame Height">-</button>
178
- <input type="number" id="ssp-ext-height" class="ssp-input" value="32" min="1" aria-label={ui.frameHeightLabel} />
179
- <button id="ssp-fh-inc" class="ssp-step-btn" type="button" aria-label="Increase Frame Height">+</button>
180
- </div>
181
- </div>
182
-
183
- <div class="ssp-control-group">
184
- <label for="ssp-ext-margin" class="ssp-label">{ui.marginLabel}</label>
185
- <div class="ssp-stepper-box">
186
- <button id="ssp-mar-dec" class="ssp-step-btn" type="button" aria-label="Decrease Margin">-</button>
187
- <input type="number" id="ssp-ext-margin" class="ssp-input" value="0" min="0" aria-label={ui.marginLabel} />
188
- <button id="ssp-mar-inc" class="ssp-step-btn" type="button" aria-label="Increase Margin">+</button>
189
- </div>
190
- </div>
191
-
192
- <div class="ssp-control-group">
193
- <label for="ssp-ext-spacing" class="ssp-label">{ui.spacingLabel}</label>
194
- <div class="ssp-stepper-box">
195
- <button id="ssp-spc-dec" class="ssp-step-btn" type="button" aria-label="Decrease Spacing">-</button>
196
- <input type="number" id="ssp-ext-spacing" class="ssp-input" value="0" min="0" aria-label={ui.spacingLabel} />
197
- <button id="ssp-spc-inc" class="ssp-step-btn" type="button" aria-label="Increase Spacing">+</button>
198
- </div>
199
- </div>
200
- </div>
201
-
202
- <button id="ssp-btn-extract-zip" class="ssp-btn ssp-btn-primary" type="button">{ui.downloadZipButton}</button>
203
- </div>
204
-
205
- <div class="ssp-main-section">
206
- <div class="ssp-stat-badge" style="align-self: flex-start;">
207
- <div id="ssp-ext-count" class="ssp-stat-val">0</div>
208
- <div class="ssp-stat-lbl">{ui.extractedCountLabel}</div>
209
- </div>
210
- <div class="ssp-canvas-wrapper">
211
- <canvas id="ssp-ext-canvas" class="ssp-canvas" width="512" height="512" aria-label={ui.previewTitle}></canvas>
212
- </div>
213
- </div>
214
- </div>
215
-
216
- <div id="ssp-toast" class="ssp-toast">{ui.copiedToast}</div>
217
- <div id="ssp-particle-container" class="ssp-particle-container"></div>
218
- </div>
219
- </div>
220
-
221
- <script>
222
- import { initSpriteSheetPackerApp } from './app-client';
223
-
224
- if (document.readyState === 'loading') {
225
- document.addEventListener('DOMContentLoaded', initSpriteSheetPackerApp);
226
- } else {
227
- initSpriteSheetPackerApp();
228
- }
229
- </script>
1
+ ---
2
+ import type { SpriteSheetPackerUI } from './ui';
3
+ import './sprite-sheet-packer.css';
4
+
5
+ interface Props {
6
+ ui: SpriteSheetPackerUI;
7
+ }
8
+
9
+ const { ui } = Astro.props;
10
+ ---
11
+
12
+ <div class="ssp-container">
13
+ <div class="ssp-main-card">
14
+ <div class="ssp-header-tabs" role="tablist">
15
+ <button id="ssp-tab-packer" class="ssp-tab-btn active" role="tab" aria-selected="true" aria-controls="ssp-packer-panel">
16
+ {ui.packerTab}
17
+ </button>
18
+ <button id="ssp-tab-extractor" class="ssp-tab-btn" role="tab" aria-selected="false" aria-controls="ssp-extractor-panel">
19
+ {ui.extractorTab}
20
+ </button>
21
+ </div>
22
+
23
+ <div id="ssp-packer-panel" class="ssp-body-grid" role="tabpanel" aria-labelledby="ssp-tab-packer">
24
+ <div class="ssp-sidebar-section">
25
+ <div id="ssp-dropzone" class="ssp-dropzone">
26
+ <div class="ssp-drop-title">{ui.dropZoneTitle}</div>
27
+ <div class="ssp-drop-sub">{ui.dropZoneSubtitle}</div>
28
+ <input type="file" id="ssp-file-input" class="ssp-file-input" multiple accept="image/png,image/jpeg,image/webp" aria-label={ui.selectFilesButton} />
29
+ </div>
30
+
31
+ <div class="ssp-btn-row">
32
+ <button id="ssp-btn-select" class="ssp-btn ssp-btn-primary" type="button">{ui.selectFilesButton}</button>
33
+ <button id="ssp-btn-clear" class="ssp-btn ssp-btn-danger" type="button">{ui.clearAllButton}</button>
34
+ </div>
35
+
36
+ <div class="ssp-presets-row">
37
+ <button id="ssp-preset-pixel" class="ssp-chip-btn" type="button">{ui.presetPixelArt}</button>
38
+ <button id="ssp-preset-hd" class="ssp-chip-btn" type="button">{ui.presetHdUi}</button>
39
+ <button id="ssp-preset-mobile" class="ssp-chip-btn" type="button">{ui.presetMobile}</button>
40
+ </div>
41
+
42
+ <div class="ssp-controls-list">
43
+ <div class="ssp-control-group">
44
+ <label for="ssp-export-format" class="ssp-label"><span>{ui.exportFormatLabel}</span></label>
45
+ <select id="ssp-export-format" class="ssp-select">
46
+ <option value="generic-json-hash">{ui.formatGenericHash}</option>
47
+ <option value="generic-json-array">{ui.formatGenericArray}</option>
48
+ <option value="unity">{ui.formatUnity}</option>
49
+ <option value="godot">{ui.formatGodot}</option>
50
+ <option value="phaser">{ui.formatPhaser}</option>
51
+ <option value="css">{ui.formatCss}</option>
52
+ </select>
53
+ </div>
54
+
55
+ <div class="ssp-control-group">
56
+ <label for="ssp-padding" class="ssp-label">
57
+ <span>{ui.paddingLabel}</span>
58
+ <span id="ssp-padding-val">2</span>
59
+ </label>
60
+ <div class="ssp-stepper-box">
61
+ <button id="ssp-pad-dec" class="ssp-step-btn" type="button" aria-label="Decrease Padding">-</button>
62
+ <input type="range" id="ssp-padding" class="ssp-slider" min="0" max="32" value="2" aria-label={ui.paddingLabel} />
63
+ <button id="ssp-pad-inc" class="ssp-step-btn" type="button" aria-label="Increase Padding">+</button>
64
+ </div>
65
+ </div>
66
+
67
+ <div class="ssp-control-group">
68
+ <label for="ssp-extrusion" class="ssp-label">
69
+ <span>{ui.borderExtrusionLabel}</span>
70
+ <span id="ssp-extrusion-val">0</span>
71
+ </label>
72
+ <div class="ssp-stepper-box">
73
+ <button id="ssp-ext-dec" class="ssp-step-btn" type="button" aria-label="Decrease Extrusion">-</button>
74
+ <input type="range" id="ssp-extrusion" class="ssp-slider" min="0" max="8" value="0" aria-label={ui.borderExtrusionLabel} />
75
+ <button id="ssp-ext-inc" class="ssp-step-btn" type="button" aria-label="Increase Extrusion">+</button>
76
+ </div>
77
+ </div>
78
+
79
+ <div class="ssp-control-group">
80
+ <label for="ssp-max-size" class="ssp-label"><span>{ui.maxTextureSizeLabel}</span></label>
81
+ <select id="ssp-max-size" class="ssp-select">
82
+ <option value="512">512 x 512 px</option>
83
+ <option value="1024">1024 x 1024 px</option>
84
+ <option value="2048" selected>2048 x 2048 px</option>
85
+ <option value="4096">4096 x 4096 px</option>
86
+ </select>
87
+ </div>
88
+
89
+ <div class="ssp-toggle-group">
90
+ <label for="ssp-power-two" class="ssp-label">{ui.powerOfTwoLabel}</label>
91
+ <input type="checkbox" id="ssp-power-two" class="ssp-toggle-input" checked aria-label={ui.powerOfTwoLabel} />
92
+ </div>
93
+ </div>
94
+ </div>
95
+
96
+ <div class="ssp-main-section">
97
+ <div class="ssp-stats-grid">
98
+ <div class="ssp-stat-badge">
99
+ <div id="ssp-stat-efficiency" class="ssp-stat-val">0%</div>
100
+ <div class="ssp-stat-lbl">{ui.efficiencyBadge}</div>
101
+ </div>
102
+ <div class="ssp-stat-badge">
103
+ <div id="ssp-stat-drawcalls" class="ssp-stat-val">0</div>
104
+ <div class="ssp-stat-lbl">{ui.drawCallsBadge}</div>
105
+ </div>
106
+ <div class="ssp-stat-badge">
107
+ <div id="ssp-stat-frames" class="ssp-stat-val">0</div>
108
+ <div class="ssp-stat-lbl">{ui.totalFramesBadge}</div>
109
+ </div>
110
+ <div class="ssp-stat-badge">
111
+ <div id="ssp-stat-size" class="ssp-stat-val">0x0</div>
112
+ <div class="ssp-stat-lbl">{ui.textureSizeBadge}</div>
113
+ </div>
114
+ </div>
115
+
116
+ <div class="ssp-canvas-wrapper">
117
+ <canvas id="ssp-atlas-canvas" class="ssp-canvas" width="512" height="512" aria-label={ui.previewTitle}></canvas>
118
+ </div>
119
+
120
+ <div class="ssp-btn-row">
121
+ <button id="ssp-btn-download-zip" class="ssp-btn ssp-btn-primary" type="button">{ui.downloadZipButton}</button>
122
+ <button id="ssp-btn-copy-json" class="ssp-btn ssp-btn-secondary" type="button">{ui.copyJsonButton}</button>
123
+ <button id="ssp-btn-download-png" class="ssp-btn ssp-btn-secondary" type="button">{ui.downloadSheetPngButton}</button>
124
+ </div>
125
+
126
+ <div class="ssp-flipbook-box">
127
+ <div class="ssp-flipbook-header">{ui.flipbookTitle}</div>
128
+ <div class="ssp-flipbook-body">
129
+ <canvas id="ssp-flipbook-canvas" class="ssp-flipbook-canvas" width="128" height="128" aria-label={ui.flipbookTitle}></canvas>
130
+ <div class="ssp-flipbook-controls">
131
+ <div class="ssp-control-group">
132
+ <label for="ssp-fps-slider" class="ssp-label">
133
+ <span>{ui.flipbookFpsLabel}</span>
134
+ <span id="ssp-fps-val">12 FPS</span>
135
+ </label>
136
+ <input type="range" id="ssp-fps-slider" class="ssp-slider" min="1" max="60" value="12" aria-label={ui.flipbookFpsLabel} />
137
+ </div>
138
+ <div class="ssp-btn-row">
139
+ <button id="ssp-btn-play" class="ssp-btn ssp-btn-secondary" type="button">{ui.playAnimation}</button>
140
+ <button id="ssp-btn-pause" class="ssp-btn ssp-btn-secondary" type="button">{ui.pauseAnimation}</button>
141
+ </div>
142
+ </div>
143
+ </div>
144
+ </div>
145
+
146
+ <div class="ssp-code-container">
147
+ <div class="ssp-code-header">
148
+ <span class="ssp-code-title">{ui.codeSnippetTitle}</span>
149
+ <button id="ssp-btn-copy-code" class="ssp-copy-btn" type="button">{ui.copySnippetButton}</button>
150
+ </div>
151
+ <pre id="ssp-code-snippet" class="ssp-code-block"></pre>
152
+ </div>
153
+ </div>
154
+ </div>
155
+
156
+ <div id="ssp-extractor-panel" class="ssp-body-grid" role="tabpanel" aria-labelledby="ssp-tab-extractor" style="display: none;">
157
+ <div class="ssp-sidebar-section">
158
+ <div id="ssp-ext-dropzone" class="ssp-dropzone">
159
+ <div class="ssp-drop-title">{ui.dropZoneTitle}</div>
160
+ <div class="ssp-drop-sub">{ui.dropZoneSubtitle}</div>
161
+ <input type="file" id="ssp-ext-file-input" class="ssp-file-input" accept="image/png,image/jpeg,image/webp" aria-label={ui.selectFilesButton} />
162
+ </div>
163
+
164
+ <div class="ssp-controls-list">
165
+ <div class="ssp-control-group">
166
+ <label for="ssp-ext-width" class="ssp-label">{ui.frameWidthLabel}</label>
167
+ <div class="ssp-stepper-box">
168
+ <button id="ssp-fw-dec" class="ssp-step-btn" type="button" aria-label="Decrease Frame Width">-</button>
169
+ <input type="number" id="ssp-ext-width" class="ssp-input" value="32" min="1" aria-label={ui.frameWidthLabel} />
170
+ <button id="ssp-fw-inc" class="ssp-step-btn" type="button" aria-label="Increase Frame Width">+</button>
171
+ </div>
172
+ </div>
173
+
174
+ <div class="ssp-control-group">
175
+ <label for="ssp-ext-height" class="ssp-label">{ui.frameHeightLabel}</label>
176
+ <div class="ssp-stepper-box">
177
+ <button id="ssp-fh-dec" class="ssp-step-btn" type="button" aria-label="Decrease Frame Height">-</button>
178
+ <input type="number" id="ssp-ext-height" class="ssp-input" value="32" min="1" aria-label={ui.frameHeightLabel} />
179
+ <button id="ssp-fh-inc" class="ssp-step-btn" type="button" aria-label="Increase Frame Height">+</button>
180
+ </div>
181
+ </div>
182
+
183
+ <div class="ssp-control-group">
184
+ <label for="ssp-ext-margin" class="ssp-label">{ui.marginLabel}</label>
185
+ <div class="ssp-stepper-box">
186
+ <button id="ssp-mar-dec" class="ssp-step-btn" type="button" aria-label="Decrease Margin">-</button>
187
+ <input type="number" id="ssp-ext-margin" class="ssp-input" value="0" min="0" aria-label={ui.marginLabel} />
188
+ <button id="ssp-mar-inc" class="ssp-step-btn" type="button" aria-label="Increase Margin">+</button>
189
+ </div>
190
+ </div>
191
+
192
+ <div class="ssp-control-group">
193
+ <label for="ssp-ext-spacing" class="ssp-label">{ui.spacingLabel}</label>
194
+ <div class="ssp-stepper-box">
195
+ <button id="ssp-spc-dec" class="ssp-step-btn" type="button" aria-label="Decrease Spacing">-</button>
196
+ <input type="number" id="ssp-ext-spacing" class="ssp-input" value="0" min="0" aria-label={ui.spacingLabel} />
197
+ <button id="ssp-spc-inc" class="ssp-step-btn" type="button" aria-label="Increase Spacing">+</button>
198
+ </div>
199
+ </div>
200
+ </div>
201
+
202
+ <button id="ssp-btn-extract-zip" class="ssp-btn ssp-btn-primary" type="button">{ui.downloadZipButton}</button>
203
+ </div>
204
+
205
+ <div class="ssp-main-section">
206
+ <div class="ssp-stat-badge" style="align-self: flex-start;">
207
+ <div id="ssp-ext-count" class="ssp-stat-val">0</div>
208
+ <div class="ssp-stat-lbl">{ui.extractedCountLabel}</div>
209
+ </div>
210
+ <div class="ssp-canvas-wrapper">
211
+ <canvas id="ssp-ext-canvas" class="ssp-canvas" width="512" height="512" aria-label={ui.previewTitle}></canvas>
212
+ </div>
213
+ </div>
214
+ </div>
215
+
216
+ <div id="ssp-toast" class="ssp-toast">{ui.copiedToast}</div>
217
+ <div id="ssp-particle-container" class="ssp-particle-container"></div>
218
+ </div>
219
+ </div>
220
+
221
+ <script>
222
+ import { initSpriteSheetPackerApp } from './app-client';
223
+
224
+ if (document.readyState === 'loading') {
225
+ document.addEventListener('DOMContentLoaded', initSpriteSheetPackerApp);
226
+ } else {
227
+ initSpriteSheetPackerApp();
228
+ }
229
+ </script>
@@ -1,55 +1,55 @@
1
- import { spawnParticle, type LoadedImageItem } from './packer-ui';
2
-
3
- export function handleFilesUpload(files: File[], loadedImages: LoadedImageItem[], updatePacker: () => void): void {
4
- const promises = files.map((file) => {
5
- return new Promise<void>((resolve) => {
6
- const img = new Image();
7
- const url = URL.createObjectURL(file);
8
- img.onload = () => {
9
- loadedImages.push({
10
- file,
11
- img,
12
- id: Math.random().toString(36).substring(2, 9),
13
- name: file.name.replace(/\.[^/.]+$/, ''),
14
- });
15
- resolve();
16
- };
17
- img.src = url;
18
- });
19
- });
20
-
21
- Promise.all(promises).then(() => updatePacker());
22
- }
23
-
24
- export function bindDropzone(loadedImages: LoadedImageItem[], updatePacker: () => void): void {
25
- const dropzone = document.getElementById('ssp-dropzone');
26
- const fileInput = document.getElementById('ssp-file-input') as HTMLInputElement | null;
27
-
28
- document.getElementById('ssp-btn-select')?.addEventListener('click', () => fileInput?.click());
29
- dropzone?.addEventListener('click', () => fileInput?.click());
30
-
31
- dropzone?.addEventListener('dragover', (e) => {
32
- e.preventDefault();
33
- dropzone.classList.add('dragover');
34
- });
35
-
36
- dropzone?.addEventListener('dragleave', (e) => {
37
- e.preventDefault();
38
- dropzone.classList.remove('dragover');
39
- });
40
-
41
- dropzone?.addEventListener('drop', (e: DragEvent) => {
42
- e.preventDefault();
43
- dropzone.classList.remove('dragover');
44
- if (e.dataTransfer?.files?.length) {
45
- handleFilesUpload(Array.from(e.dataTransfer.files), loadedImages, updatePacker);
46
- spawnParticle(e.clientX, e.clientY, 'PACKED!');
47
- }
48
- });
49
-
50
- fileInput?.addEventListener('change', () => {
51
- if (fileInput.files?.length) {
52
- handleFilesUpload(Array.from(fileInput.files), loadedImages, updatePacker);
53
- }
54
- });
55
- }
1
+ import { spawnParticle, type LoadedImageItem } from './packer-ui';
2
+
3
+ export function handleFilesUpload(files: File[], loadedImages: LoadedImageItem[], updatePacker: () => void): void {
4
+ const promises = files.map((file) => {
5
+ return new Promise<void>((resolve) => {
6
+ const img = new Image();
7
+ const url = URL.createObjectURL(file);
8
+ img.onload = () => {
9
+ loadedImages.push({
10
+ file,
11
+ img,
12
+ id: Math.random().toString(36).substring(2, 9),
13
+ name: file.name.replace(/\.[^/.]+$/, ''),
14
+ });
15
+ resolve();
16
+ };
17
+ img.src = url;
18
+ });
19
+ });
20
+
21
+ Promise.all(promises).then(() => updatePacker());
22
+ }
23
+
24
+ export function bindDropzone(loadedImages: LoadedImageItem[], updatePacker: () => void): void {
25
+ const dropzone = document.getElementById('ssp-dropzone');
26
+ const fileInput = document.getElementById('ssp-file-input') as HTMLInputElement | null;
27
+
28
+ document.getElementById('ssp-btn-select')?.addEventListener('click', () => fileInput?.click());
29
+ dropzone?.addEventListener('click', () => fileInput?.click());
30
+
31
+ dropzone?.addEventListener('dragover', (e) => {
32
+ e.preventDefault();
33
+ dropzone.classList.add('dragover');
34
+ });
35
+
36
+ dropzone?.addEventListener('dragleave', (e) => {
37
+ e.preventDefault();
38
+ dropzone.classList.remove('dragover');
39
+ });
40
+
41
+ dropzone?.addEventListener('drop', (e: DragEvent) => {
42
+ e.preventDefault();
43
+ dropzone.classList.remove('dragover');
44
+ if (e.dataTransfer?.files?.length) {
45
+ handleFilesUpload(Array.from(e.dataTransfer.files), loadedImages, updatePacker);
46
+ spawnParticle(e.clientX, e.clientY, 'PACKED!');
47
+ }
48
+ });
49
+
50
+ fileInput?.addEventListener('change', () => {
51
+ if (fileInput.files?.length) {
52
+ handleFilesUpload(Array.from(fileInput.files), loadedImages, updatePacker);
53
+ }
54
+ });
55
+ }
@@ -1,28 +1,28 @@
1
- import type { GamesToolEntry, ToolLocaleContent } from '../../types';
2
- import type { SpriteSheetPackerUI } from './ui';
3
-
4
- export type { SpriteSheetPackerUI };
5
-
6
- export type SpriteSheetPackerLocaleContent = ToolLocaleContent<SpriteSheetPackerUI>;
7
-
8
- export const spriteSheetPacker: GamesToolEntry<SpriteSheetPackerUI> = {
9
- id: 'sprite-sheet-packer',
10
- icons: { bg: 'mdi:gamepad-variant', fg: 'mdi:texture-box' },
11
- i18n: {
12
- de: () => import('./i18n/de').then((m) => m.content),
13
- en: () => import('./i18n/en').then((m) => m.content),
14
- es: () => import('./i18n/es').then((m) => m.content),
15
- fr: () => import('./i18n/fr').then((m) => m.content),
16
- id: () => import('./i18n/id').then((m) => m.content),
17
- it: () => import('./i18n/it').then((m) => m.content),
18
- ja: () => import('./i18n/ja').then((m) => m.content),
19
- ko: () => import('./i18n/ko').then((m) => m.content),
20
- nl: () => import('./i18n/nl').then((m) => m.content),
21
- pl: () => import('./i18n/pl').then((m) => m.content),
22
- pt: () => import('./i18n/pt').then((m) => m.content),
23
- ru: () => import('./i18n/ru').then((m) => m.content),
24
- sv: () => import('./i18n/sv').then((m) => m.content),
25
- tr: () => import('./i18n/tr').then((m) => m.content),
26
- zh: () => import('./i18n/zh').then((m) => m.content),
27
- },
28
- };
1
+ import type { GamesToolEntry, ToolLocaleContent } from '../../types';
2
+ import type { SpriteSheetPackerUI } from './ui';
3
+
4
+ export type { SpriteSheetPackerUI };
5
+
6
+ export type SpriteSheetPackerLocaleContent = ToolLocaleContent<SpriteSheetPackerUI>;
7
+
8
+ export const spriteSheetPacker: GamesToolEntry<SpriteSheetPackerUI> = {
9
+ id: 'sprite-sheet-packer',
10
+ icons: { bg: 'mdi:gamepad-variant', fg: 'mdi:texture-box' },
11
+ i18n: {
12
+ de: () => import('./i18n/de').then((m) => m.content),
13
+ en: () => import('./i18n/en').then((m) => m.content),
14
+ es: () => import('./i18n/es').then((m) => m.content),
15
+ fr: () => import('./i18n/fr').then((m) => m.content),
16
+ id: () => import('./i18n/id').then((m) => m.content),
17
+ it: () => import('./i18n/it').then((m) => m.content),
18
+ ja: () => import('./i18n/ja').then((m) => m.content),
19
+ ko: () => import('./i18n/ko').then((m) => m.content),
20
+ nl: () => import('./i18n/nl').then((m) => m.content),
21
+ pl: () => import('./i18n/pl').then((m) => m.content),
22
+ pt: () => import('./i18n/pt').then((m) => m.content),
23
+ ru: () => import('./i18n/ru').then((m) => m.content),
24
+ sv: () => import('./i18n/sv').then((m) => m.content),
25
+ tr: () => import('./i18n/tr').then((m) => m.content),
26
+ zh: () => import('./i18n/zh').then((m) => m.content),
27
+ },
28
+ };