Saad4web commited on
Commit
db3dca3
·
verified ·
1 Parent(s): e1e17a1

good job now improve to make it able Export Options PNG Image Schematic Blueprint Commands - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +787 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Pixelarts
3
- emoji: 🌖
4
- colorFrom: gray
5
- colorTo: blue
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: pixelarts
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: green
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,787 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Minecraft Pixel Art Converter</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.11.0/dist/tf.min.js"></script>
9
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
10
+ <script>
11
+ tailwind.config = {
12
+ theme: {
13
+ extend: {
14
+ colors: {
15
+ minecraft: {
16
+ green: '#5b9c64',
17
+ dark: '#3a3a3a',
18
+ light: '#f0f0f0',
19
+ accent: '#7aa3d9'
20
+ }
21
+ }
22
+ }
23
+ }
24
+ }
25
+ </script>
26
+ <style>
27
+ .block-item {
28
+ transition: all 0.2s ease;
29
+ }
30
+ .block-item:hover {
31
+ transform: translateY(-2px);
32
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
33
+ }
34
+ .pixel-grid {
35
+ display: grid;
36
+ gap: 0;
37
+ margin: 0 auto;
38
+ }
39
+ .pixel {
40
+ border: 1px solid rgba(0,0,0,0.1);
41
+ }
42
+ .drop-area {
43
+ border: 2px dashed #cbd5e0;
44
+ transition: all 0.3s ease;
45
+ }
46
+ .drop-area.active {
47
+ border-color: #7aa3d9;
48
+ background-color: rgba(122, 163, 217, 0.1);
49
+ }
50
+ .canvas-container {
51
+ position: relative;
52
+ overflow: auto;
53
+ max-width: 100%;
54
+ }
55
+ canvas {
56
+ max-width: 100%;
57
+ height: auto;
58
+ display: block;
59
+ }
60
+ .category-title {
61
+ position: sticky;
62
+ top: 0;
63
+ z-index: 10;
64
+ }
65
+ .export-grid {
66
+ display: grid;
67
+ grid-template-columns: repeat(auto-fill, minmax(20px, 1fr));
68
+ gap: 1px;
69
+ }
70
+ .export-cell {
71
+ width: 100%;
72
+ padding-bottom: 100%;
73
+ position: relative;
74
+ }
75
+ .export-cell-inner {
76
+ position: absolute;
77
+ top: 0;
78
+ left: 0;
79
+ width: 100%;
80
+ height: 100%;
81
+ font-size: 8px;
82
+ display: flex;
83
+ align-items: center;
84
+ justify-content: center;
85
+ }
86
+ </style>
87
+ </head>
88
+ <body class="bg-minecraft-light min-h-screen font-sans">
89
+ <!-- Header -->
90
+ <header class="bg-minecraft-dark text-white py-4 shadow-md">
91
+ <div class="container mx-auto px-4 flex items-center">
92
+ <i class="fas fa-cube text-minecraft-green text-2xl mr-3"></i>
93
+ <h1 class="text-2xl font-bold">Minecraft Pixel Art Converter</h1>
94
+ </div>
95
+ </header>
96
+
97
+ <main class="container mx-auto px-4 py-8">
98
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
99
+ <!-- Left Column: Image Upload and Preview -->
100
+ <div class="lg:col-span-1">
101
+ <div class="bg-white rounded-lg shadow-md p-6 mb-6">
102
+ <h2 class="text-xl font-bold text-gray-800 mb-4">Upload Image</h2>
103
+ <div class="drop-area rounded-lg p-8 text-center cursor-pointer mb-4" id="drop-area">
104
+ <i class="fas fa-cloud-upload-alt text-4xl text-gray-400 mb-3"></i>
105
+ <p class="text-gray-600 mb-2">Drag & drop your image here</p>
106
+ <p class="text-gray-500 text-sm mb-4">or</p>
107
+ <label for="file-upload" class="bg-minecraft-green hover:bg-green-600 text-white font-medium py-2 px-4 rounded cursor-pointer transition">
108
+ <i class="fas fa-folder-open mr-2"></i>Choose File
109
+ </label>
110
+ <input type="file" id="file-upload" class="hidden" accept="image/jpeg,image/png">
111
+ </div>
112
+ <p class="text-gray-500 text-sm text-center">Supported formats: JPG, PNG</p>
113
+ </div>
114
+
115
+ <div class="bg-white rounded-lg shadow-md p-6 mb-6">
116
+ <h2 class="text-xl font-bold text-gray-800 mb-4">Original Image</h2>
117
+ <div class="border-2 border-dashed border-gray-300 rounded-lg flex items-center justify-center min-h-[200px]">
118
+ <img id="original-preview" class="max-w-full max-h-80 object-contain hidden">
119
+ <div id="original-placeholder" class="text-gray-500 text-center p-4">
120
+ <i class="fas fa-image text-4xl mb-3"></i>
121
+ <p>Your image will appear here</p>
122
+ </div>
123
+ </div>
124
+ </div>
125
+
126
+ <div class="bg-white rounded-lg shadow-md p-6">
127
+ <h2 class="text-xl font-bold text-gray-800 mb-4">Conversion Settings</h2>
128
+ <div class="mb-4">
129
+ <label class="block text-gray-700 mb-2">Pixel Art Size</label>
130
+ <div class="flex items-center">
131
+ <input type="range" id="resolution-slider" min="16" max="128" value="64" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
132
+ <span id="resolution-value" class="ml-4 text-gray-700 font-medium">64x64</span>
133
+ </div>
134
+ </div>
135
+ <div class="flex space-x-4">
136
+ <button id="convert-btn" class="flex-1 bg-minecraft-green hover:bg-green-600 text-white font-medium py-2 px-4 rounded transition disabled:opacity-50 disabled:cursor-not-allowed" disabled>
137
+ <i class="fas fa-magic mr-2"></i>Convert to Pixel Art
138
+ </button>
139
+ <button id="reset-btn" class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-medium py-2 px-4 rounded transition">
140
+ <i class="fas fa-redo mr-2"></i>Reset
141
+ </button>
142
+ </div>
143
+ </div>
144
+ </div>
145
+
146
+ <!-- Middle Column: Pixel Art Preview -->
147
+ <div class="lg:col-span-1">
148
+ <div class="bg-white rounded-lg shadow-md p-6 mb-6">
149
+ <div class="flex justify-between items-center mb-4">
150
+ <h2 class="text-xl font-bold text-gray-800">Minecraft Pixel Art</h2>
151
+ <div class="flex items-center">
152
+ <span class="text-gray-600 mr-2">Zoom:</span>
153
+ <div class="flex space-x-1">
154
+ <button id="zoom-out" class="w-8 h-8 bg-gray-200 hover:bg-gray-300 rounded flex items-center justify-center">
155
+ <i class="fas fa-search-minus"></i>
156
+ </button>
157
+ <button id="zoom-in" class="w-8 h-8 bg-gray-200 hover:bg-gray-300 rounded flex items-center justify-center">
158
+ <i class="fas fa-search-plus"></i>
159
+ </button>
160
+ </div>
161
+ </div>
162
+ </div>
163
+ <div class="canvas-container border-2 border-gray-300 rounded-lg min-h-[300px] flex items-center justify-center" id="canvas-container">
164
+ <canvas id="pixel-art-canvas" class="hidden"></canvas>
165
+ <div id="pixel-art-placeholder" class="text-gray-500 text-center p-4">
166
+ <i class="fas fa-cubes text-4xl mb-3"></i>
167
+ <p>Your Minecraft pixel art will appear here</p>
168
+ </div>
169
+ </div>
170
+ </div>
171
+
172
+ <div class="bg-white rounded-lg shadow-md p-6">
173
+ <h2 class="text-xl font-bold text-gray-800 mb-4">Export Minecraft Art</h2>
174
+ <button id="export-btn" class="w-full bg-minecraft-accent hover:bg-blue-600 text-white font-medium py-3 px-4 rounded transition mb-4 disabled:opacity-50 disabled:cursor-not-allowed" disabled>
175
+ <i class="fas fa-download mr-2"></i>Export Minecraft Blueprint
176
+ </button>
177
+
178
+ <div id="export-results" class="hidden">
179
+ <div class="mb-4">
180
+ <h3 class="font-bold text-gray-700 mb-2">Art Dimensions</h3>
181
+ <p id="dimensions" class="text-gray-800">64x64 blocks</p>
182
+ </div>
183
+
184
+ <div class="mb-4">
185
+ <h3 class="font-bold text-gray-700 mb-2">Blocks Required</h3>
186
+ <div id="block-counts" class="bg-gray-50 rounded-lg p-4 max-h-60 overflow-y-auto">
187
+ <!-- Block counts will be inserted here -->
188
+ </div>
189
+ </div>
190
+
191
+ <div>
192
+ <h3 class="font-bold text-gray-700 mb-2">Placement Guide</h3>
193
+ <div class="bg-gray-50 rounded-lg p-4">
194
+ <div class="export-grid mb-4" id="placement-grid">
195
+ <!-- Miniature grid will be inserted here -->
196
+ </div>
197
+ <div class="flex justify-between">
198
+ <button id="download-txt" class="text-minecraft-accent hover:text-blue-600 font-medium">
199
+ <i class="fas fa-file-alt mr-2"></i>Download Text Guide
200
+ </button>
201
+ <button id="download-grid" class="text-minecraft-accent hover:text-blue-600 font-medium">
202
+ <i class="fas fa-table mr-2"></i>Download Grid Guide
203
+ </button>
204
+ </div>
205
+ </div>
206
+ </div>
207
+ </div>
208
+ </div>
209
+ </div>
210
+
211
+ <!-- Right Column: Block Palette -->
212
+ <div class="lg:col-span-1">
213
+ <div class="bg-white rounded-lg shadow-md p-6">
214
+ <div class="flex justify-between items-center mb-4">
215
+ <h2 class="text-xl font-bold text-gray-800">Minecraft Block Palette</h2>
216
+ <div class="flex space-x-2">
217
+ <button id="select-all" class="text-sm bg-gray-200 hover:bg-gray-300 text-gray-800 py-1 px-3 rounded transition">
218
+ <i class="fas fa-check-square mr-1"></i>Select All
219
+ </button>
220
+ <button id="deselect-all" class="text-sm bg-gray-200 hover:bg-gray-300 text-gray-800 py-1 px-3 rounded transition">
221
+ <i class="fas fa-square mr-1"></i>Deselect All
222
+ </button>
223
+ </div>
224
+ </div>
225
+
226
+ <div class="mb-4">
227
+ <input type="text" id="block-search" placeholder="Search blocks..." class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-minecraft-accent">
228
+ </div>
229
+
230
+ <div class="block-palette-container max-h-[calc(100vh-250px)] overflow-y-auto pr-2">
231
+ <!-- Block categories will be inserted here -->
232
+ </div>
233
+ </div>
234
+ </div>
235
+ </div>
236
+ </main>
237
+
238
+ <script>
239
+ // Minecraft block data with representative colors
240
+ const blockPalette = [
241
+ // Easily Findable Blocks
242
+ { name: "Clay", category: "Easily Findable", color: [158, 164, 176] },
243
+ { name: "Jungle log", category: "Easily Findable", color: [87, 68, 27] },
244
+ { name: "Birch log", category: "Easily Findable", color: [207, 206, 201] },
245
+ { name: "Oak log", category: "Easily Findable", color: [102, 81, 50] },
246
+ { name: "Spruce log", category: "Easily Findable", color: [46, 29, 12] },
247
+ { name: "Stone slab", category: "Easily Findable", color: [125, 125, 125] },
248
+ { name: "Gravel", category: "Easily Findable", color: [132, 127, 127] },
249
+ { name: "Sandstone", category: "Easily Findable", color: [219, 211, 160] },
250
+ { name: "White wool", category: "Easily Findable", color: [233, 236, 236] },
251
+ { name: "Blue wool", category: "Easily Findable", color: [53, 57, 157] },
252
+ { name: "Yellow wool", category: "Easily Findable", color: [248, 198, 40] },
253
+ { name: "Red wool", category: "Easily Findable", color: [161, 39, 35] },
254
+ { name: "Green wool", category: "Easily Findable", color: [86, 110, 44] },
255
+ { name: "Snow block", category: "Easily Findable", color: [240, 251, 251] },
256
+ { name: "Cobblestone", category: "Easily Findable", color: [125, 125, 125] },
257
+ { name: "Sand", category: "Easily Findable", color: [219, 211, 160] },
258
+ { name: "Dirt", category: "Easily Findable", color: [134, 96, 67] },
259
+
260
+ // Manufactured Blocks
261
+ { name: "Bookshelf", category: "Manufactured", color: [178, 141, 86] },
262
+ { name: "Block of Iron", category: "Manufactured", color: [220, 220, 220] },
263
+ { name: "Bricks", category: "Manufactured", color: [150, 99, 83] },
264
+ { name: "Stone brick", category: "Manufactured", color: [122, 122, 122] },
265
+ { name: "Blue terracotta", category: "Manufactured", color: [74, 60, 91] },
266
+ { name: "White terracotta", category: "Manufactured", color: [210, 178, 162] },
267
+ { name: "Black concrete", category: "Manufactured", color: [9, 11, 16] },
268
+ { name: "Block of Copper", category: "Manufactured", color: [198, 112, 100] },
269
+
270
+ // Blocks on Transparent Background
271
+ { name: "Glass", category: "Transparent", color: [155, 186, 178, 200] },
272
+ { name: "Black stained glass", category: "Transparent", color: [25, 25, 25, 200] },
273
+ { name: "Blue stained glass", category: "Transparent", color: [51, 76, 178, 200] },
274
+ { name: "Green stained glass", category: "Transparent", color: [102, 127, 51, 200] },
275
+ { name: "Oak leaves", category: "Transparent", color: [48, 112, 20, 200] },
276
+
277
+ // Rare Blocks
278
+ { name: "Block of Diamond", category: "Rare", color: [99, 219, 213] },
279
+ { name: "Block of Emerald", category: "Rare", color: [42, 204, 112] },
280
+ { name: "Block of Gold", category: "Rare", color: [249, 236, 50] },
281
+ { name: "Obsidian", category: "Rare", color: [20, 18, 30] },
282
+ { name: "Prismarine bricks", category: "Rare", color: [100, 160, 144] },
283
+ { name: "Slime Block", category: "Rare", color: [128, 189, 90] }
284
+ ];
285
+
286
+ // App state
287
+ let state = {
288
+ originalImage: null,
289
+ pixelArtData: null,
290
+ selectedBlocks: new Set(blockPalette.map(block => block.name)),
291
+ resolution: 64,
292
+ zoomLevel: 5,
293
+ conversionInProgress: false
294
+ };
295
+
296
+ // DOM Elements
297
+ const dom = {
298
+ dropArea: document.getElementById('drop-area'),
299
+ fileUpload: document.getElementById('file-upload'),
300
+ originalPreview: document.getElementById('original-preview'),
301
+ originalPlaceholder: document.getElementById('original-placeholder'),
302
+ convertBtn: document.getElementById('convert-btn'),
303
+ resetBtn: document.getElementById('reset-btn'),
304
+ pixelArtCanvas: document.getElementById('pixel-art-canvas'),
305
+ pixelArtPlaceholder: document.getElementById('pixel-art-placeholder'),
306
+ canvasContainer: document.getElementById('canvas-container'),
307
+ blockPaletteContainer: document.querySelector('.block-palette-container'),
308
+ blockSearch: document.getElementById('block-search'),
309
+ selectAllBtn: document.getElementById('select-all'),
310
+ deselectAllBtn: document.getElementById('deselect-all'),
311
+ resolutionSlider: document.getElementById('resolution-slider'),
312
+ resolutionValue: document.getElementById('resolution-value'),
313
+ zoomInBtn: document.getElementById('zoom-in'),
314
+ zoomOutBtn: document.getElementById('zoom-out'),
315
+ exportBtn: document.getElementById('export-btn'),
316
+ exportResults: document.getElementById('export-results'),
317
+ dimensions: document.getElementById('dimensions'),
318
+ blockCounts: document.getElementById('block-counts'),
319
+ placementGrid: document.getElementById('placement-grid'),
320
+ downloadTxt: document.getElementById('download-txt'),
321
+ downloadGrid: document.getElementById('download-grid')
322
+ };
323
+
324
+ // Initialize the application
325
+ function init() {
326
+ renderBlockPalette();
327
+ setupEventListeners();
328
+ }
329
+
330
+ // Render block palette with categories
331
+ function renderBlockPalette() {
332
+ // Group blocks by category
333
+ const categories = {};
334
+ blockPalette.forEach(block => {
335
+ if (!categories[block.category]) {
336
+ categories[block.category] = [];
337
+ }
338
+ categories[block.category].push(block);
339
+ });
340
+
341
+ // Generate HTML for each category
342
+ let html = '';
343
+ for (const category in categories) {
344
+ html += `
345
+ <div class="mb-6">
346
+ <h3 class="category-title bg-gray-100 text-gray-800 font-bold py-2 px-3 rounded-t-lg">${category}</h3>
347
+ <div class="grid grid-cols-2 sm:grid-cols-3 gap-3 p-3 bg-gray-50 rounded-b-lg">
348
+ ${categories[category].map(block => `
349
+ <div class="block-item bg-white rounded-lg overflow-hidden shadow-sm border border-gray-200 ${state.selectedBlocks.has(block.name) ? 'border-minecraft-green' : ''}">
350
+ <label class="flex items-center p-2 cursor-pointer">
351
+ <input type="checkbox" class="block-checkbox mr-2" data-block="${block.name}" ${state.selectedBlocks.has(block.name) ? 'checked' : ''}>
352
+ <div class="flex items-center">
353
+ <div class="w-4 h-4 rounded-sm mr-2" style="background-color: rgb(${block.color.join(',')})"></div>
354
+ <span class="text-sm text-gray-700 truncate">${block.name}</span>
355
+ </div>
356
+ </label>
357
+ </div>
358
+ `).join('')}
359
+ </div>
360
+ </div>
361
+ `;
362
+ }
363
+
364
+ dom.blockPaletteContainer.innerHTML = html;
365
+ }
366
+
367
+ // Set up event listeners
368
+ function setupEventListeners() {
369
+ // File upload handling
370
+ dom.dropArea.addEventListener('dragover', (e) => {
371
+ e.preventDefault();
372
+ dom.dropArea.classList.add('active');
373
+ });
374
+
375
+ dom.dropArea.addEventListener('dragleave', () => {
376
+ dom.dropArea.classList.remove('active');
377
+ });
378
+
379
+ dom.dropArea.addEventListener('drop', (e) => {
380
+ e.preventDefault();
381
+ dom.dropArea.classList.remove('active');
382
+
383
+ if (e.dataTransfer.files.length) {
384
+ handleFileUpload(e.dataTransfer.files[0]);
385
+ }
386
+ });
387
+
388
+ dom.fileUpload.addEventListener('change', (e) => {
389
+ if (e.target.files.length) {
390
+ handleFileUpload(e.target.files[0]);
391
+ }
392
+ });
393
+
394
+ // Block selection
395
+ dom.blockPaletteContainer.addEventListener('change', (e) => {
396
+ if (e.target.classList.contains('block-checkbox')) {
397
+ const blockName = e.target.dataset.block;
398
+ if (e.target.checked) {
399
+ state.selectedBlocks.add(blockName);
400
+ } else {
401
+ state.selectedBlocks.delete(blockName);
402
+ }
403
+ // Update UI
404
+ const blockItem = e.target.closest('.block-item');
405
+ if (e.target.checked) {
406
+ blockItem.classList.add('border-minecraft-green');
407
+ } else {
408
+ blockItem.classList.remove('border-minecraft-green');
409
+ }
410
+ }
411
+ });
412
+
413
+ // Block search
414
+ dom.blockSearch.addEventListener('input', (e) => {
415
+ const searchTerm = e.target.value.toLowerCase();
416
+ document.querySelectorAll('.block-item').forEach(item => {
417
+ const blockName = item.querySelector('span').textContent.toLowerCase();
418
+ if (blockName.includes(searchTerm)) {
419
+ item.style.display = 'block';
420
+ } else {
421
+ item.style.display = 'none';
422
+ }
423
+ });
424
+ });
425
+
426
+ // Select all/deselect all
427
+ dom.selectAllBtn.addEventListener('click', () => {
428
+ state.selectedBlocks = new Set(blockPalette.map(block => block.name));
429
+ document.querySelectorAll('.block-checkbox').forEach(checkbox => {
430
+ checkbox.checked = true;
431
+ });
432
+ document.querySelectorAll('.block-item').forEach(item => {
433
+ item.classList.add('border-minecraft-green');
434
+ });
435
+ });
436
+
437
+ dom.deselectAllBtn.addEventListener('click', () => {
438
+ state.selectedBlocks.clear();
439
+ document.querySelectorAll('.block-checkbox').forEach(checkbox => {
440
+ checkbox.checked = false;
441
+ });
442
+ document.querySelectorAll('.block-item').forEach(item => {
443
+ item.classList.remove('border-minecraft-green');
444
+ });
445
+ });
446
+
447
+ // Resolution slider
448
+ dom.resolutionSlider.addEventListener('input', (e) => {
449
+ state.resolution = parseInt(e.target.value);
450
+ dom.resolutionValue.textContent = `${state.resolution}x${state.resolution}`;
451
+ });
452
+
453
+ // Zoom controls
454
+ dom.zoomInBtn.addEventListener('click', () => {
455
+ state.zoomLevel = Math.min(state.zoomLevel + 1, 10);
456
+ renderPixelArt();
457
+ });
458
+
459
+ dom.zoomOutBtn.addEventListener('click', () => {
460
+ state.zoomLevel = Math.max(state.zoomLevel - 1, 1);
461
+ renderPixelArt();
462
+ });
463
+
464
+ // Convert button
465
+ dom.convertBtn.addEventListener('click', convertImageToPixelArt);
466
+
467
+ // Reset button
468
+ dom.resetBtn.addEventListener('click', resetApp);
469
+
470
+ // Export button
471
+ dom.exportBtn.addEventListener('click', exportArt);
472
+
473
+ // Download buttons
474
+ dom.downloadTxt.addEventListener('click', downloadTextGuide);
475
+ dom.downloadGrid.addEventListener('click', downloadGridGuide);
476
+ }
477
+
478
+ // Handle file upload
479
+ function handleFileUpload(file) {
480
+ if (!file.type.match('image.*')) {
481
+ alert('Please upload an image file (JPG or PNG)');
482
+ return;
483
+ }
484
+
485
+ const reader = new FileReader();
486
+ reader.onload = (e) => {
487
+ state.originalImage = new Image();
488
+ state.originalImage.onload = () => {
489
+ // Show preview
490
+ dom.originalPreview.src = e.target.result;
491
+ dom.originalPreview.classList.remove('hidden');
492
+ dom.originalPlaceholder.classList.add('hidden');
493
+
494
+ // Enable convert button
495
+ dom.convertBtn.disabled = false;
496
+ };
497
+ state.originalImage.src = e.target.result;
498
+ };
499
+ reader.readAsDataURL(file);
500
+ }
501
+
502
+ // Convert image to pixel art using TensorFlow.js
503
+ async function convertImageToPixelArt() {
504
+ if (!state.originalImage || state.conversionInProgress) return;
505
+
506
+ state.conversionInProgress = true;
507
+ dom.convertBtn.disabled = true;
508
+ dom.convertBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Processing...';
509
+
510
+ try {
511
+ // Get selected blocks
512
+ const selectedBlocks = blockPalette.filter(block =>
513
+ state.selectedBlocks.has(block.name)
514
+ );
515
+
516
+ if (selectedBlocks.length === 0) {
517
+ alert('Please select at least one block from the palette');
518
+ state.conversionInProgress = false;
519
+ dom.convertBtn.disabled = false;
520
+ dom.convertBtn.innerHTML = '<i class="fas fa-magic mr-2"></i>Convert to Pixel Art';
521
+ return;
522
+ }
523
+
524
+ // Prepare TensorFlow.js operations
525
+ const tensor = tf.browser.fromPixels(state.originalImage);
526
+ const resized = tf.image.resizeBilinear(tensor, [state.resolution, state.resolution]);
527
+
528
+ // Convert to pixel art by finding nearest block color
529
+ const blockColors = selectedBlocks.map(block => {
530
+ const color = block.color.length === 4 ? block.color.slice(0, 3) : block.color;
531
+ return tf.tensor1d(color);
532
+ });
533
+
534
+ const pixelArtData = [];
535
+ const pixels = resized.arraySync();
536
+
537
+ for (let y = 0; y < state.resolution; y++) {
538
+ const row = [];
539
+ for (let x = 0; x < state.resolution; x++) {
540
+ const pixel = pixels[y][x];
541
+ let minDistance = Infinity;
542
+ let closestBlockIndex = 0;
543
+
544
+ // Find closest block color
545
+ for (let i = 0; i < blockColors.length; i++) {
546
+ const color = blockColors[i];
547
+ const distance = Math.sqrt(
548
+ Math.pow(pixel[0] - color.arraySync()[0], 2) +
549
+ Math.pow(pixel[1] - color.arraySync()[1], 2) +
550
+ Math.pow(pixel[2] - color.arraySync()[2], 2)
551
+ );
552
+
553
+ if (distance < minDistance) {
554
+ minDistance = distance;
555
+ closestBlockIndex = i;
556
+ }
557
+ }
558
+
559
+ row.push({
560
+ block: selectedBlocks[closestBlockIndex],
561
+ index: closestBlockIndex
562
+ });
563
+ }
564
+ pixelArtData.push(row);
565
+ }
566
+
567
+ // Clean up tensors
568
+ tf.dispose([tensor, resized]);
569
+ blockColors.forEach(t => t.dispose());
570
+
571
+ // Update state
572
+ state.pixelArtData = pixelArtData;
573
+
574
+ // Render pixel art
575
+ renderPixelArt();
576
+
577
+ // Enable export button
578
+ dom.exportBtn.disabled = false;
579
+
580
+ } catch (error) {
581
+ console.error('Conversion error:', error);
582
+ alert('An error occurred during conversion. Please try again.');
583
+ } finally {
584
+ state.conversionInProgress = false;
585
+ dom.convertBtn.disabled = false;
586
+ dom.convertBtn.innerHTML = '<i class="fas fa-magic mr-2"></i>Convert to Pixel Art';
587
+ }
588
+ }
589
+
590
+ // Render pixel art on canvas
591
+ function renderPixelArt() {
592
+ if (!state.pixelArtData) return;
593
+
594
+ const canvas = dom.pixelArtCanvas;
595
+ const ctx = canvas.getContext('2d');
596
+ const resolution = state.resolution;
597
+ const pixelSize = state.zoomLevel;
598
+
599
+ canvas.width = resolution * pixelSize;
600
+ canvas.height = resolution * pixelSize;
601
+ canvas.classList.remove('hidden');
602
+ dom.pixelArtPlaceholder.classList.add('hidden');
603
+
604
+ // Clear canvas
605
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
606
+
607
+ // Draw each pixel
608
+ for (let y = 0; y < resolution; y++) {
609
+ for (let x = 0; x < resolution; x++) {
610
+ const pixel = state.pixelArtData[y][x];
611
+ const color = pixel.block.color;
612
+
613
+ ctx.fillStyle = `rgb(${color.slice(0, 3).join(',')})`;
614
+ ctx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
615
+
616
+ // Add grid for larger pixels
617
+ if (pixelSize > 3) {
618
+ ctx.strokeStyle = 'rgba(0,0,0,0.1)';
619
+ ctx.strokeRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
620
+ }
621
+ }
622
+ }
623
+ }
624
+
625
+ // Export art results
626
+ function exportArt() {
627
+ if (!state.pixelArtData) return;
628
+
629
+ // Show dimensions
630
+ dom.dimensions.textContent = `${state.resolution}x${state.resolution} blocks`;
631
+
632
+ // Calculate block counts
633
+ const blockCounts = {};
634
+ state.pixelArtData.flat().forEach(pixel => {
635
+ const blockName = pixel.block.name;
636
+ blockCounts[blockName] = (blockCounts[blockName] || 0) + 1;
637
+ });
638
+
639
+ // Render block counts
640
+ let countsHTML = '';
641
+ Object.entries(blockCounts).sort((a, b) => b[1] - a[1]).forEach(([block, count]) => {
642
+ countsHTML += `
643
+ <div class="flex justify-between py-2 border-b border-gray-200">
644
+ <span>${block}</span>
645
+ <span class="font-medium">${count}</span>
646
+ </div>
647
+ `;
648
+ });
649
+ dom.blockCounts.innerHTML = countsHTML;
650
+
651
+ // Render miniature placement grid
652
+ dom.placementGrid.innerHTML = '';
653
+ const gridSize = Math.min(20, state.resolution); // Max 20x20 for preview
654
+
655
+ for (let y = 0; y < gridSize; y++) {
656
+ for (let x = 0; x < gridSize; x++) {
657
+ const pixel = state.pixelArtData[y][x];
658
+ const color = pixel.block.color;
659
+
660
+ const cell = document.createElement('div');
661
+ cell.className = 'export-cell';
662
+
663
+ const inner = document.createElement('div');
664
+ inner.className = 'export-cell-inner';
665
+ inner.style.backgroundColor = `rgb(${color.slice(0, 3).join(',')})`;
666
+ inner.title = pixel.block.name;
667
+
668
+ cell.appendChild(inner);
669
+ dom.placementGrid.appendChild(cell);
670
+ }
671
+ }
672
+
673
+ // Show export results
674
+ dom.exportResults.classList.remove('hidden');
675
+
676
+ // Scroll to export results
677
+ dom.exportResults.scrollIntoView({ behavior: 'smooth' });
678
+ }
679
+
680
+ // Download text guide
681
+ function downloadTextGuide() {
682
+ if (!state.pixelArtData) return;
683
+
684
+ let content = `Minecraft Pixel Art Blueprint\n`;
685
+ content += `Dimensions: ${state.resolution}x${state.resolution} blocks\n\n`;
686
+ content += "Block Placement Guide:\n\n";
687
+
688
+ for (let y = 0; y < state.resolution; y++) {
689
+ for (let x = 0; x < state.resolution; x++) {
690
+ const pixel = state.pixelArtData[y][x];
691
+ content += `(${x},${y}): ${pixel.block.name}\n`;
692
+ }
693
+ }
694
+
695
+ content += "\nBlock Quantities:\n";
696
+ const blockCounts = {};
697
+ state.pixelArtData.flat().forEach(pixel => {
698
+ const blockName = pixel.block.name;
699
+ blockCounts[blockName] = (blockCounts[blockName] || 0) + 1;
700
+ });
701
+
702
+ Object.entries(blockCounts).sort((a, b) => b[1] - a[1]).forEach(([block, count]) => {
703
+ content += `${block}: ${count}\n`;
704
+ });
705
+
706
+ const blob = new Blob([content], { type: 'text/plain' });
707
+ const url = URL.createObjectURL(blob);
708
+ const a = document.createElement('a');
709
+ a.href = url;
710
+ a.download = 'minecraft-pixel-art-blueprint.txt';
711
+ document.body.appendChild(a);
712
+ a.click();
713
+ document.body.removeChild(a);
714
+ URL.revokeObjectURL(url);
715
+ }
716
+
717
+ // Download grid guide
718
+ function downloadGridGuide() {
719
+ if (!state.pixelArtData) return;
720
+
721
+ // Create a canvas for the grid guide
722
+ const canvas = document.createElement('canvas');
723
+ const ctx = canvas.getContext('2d');
724
+ const resolution = state.resolution;
725
+ const pixelSize = 20; // Fixed size for download
726
+
727
+ canvas.width = resolution * pixelSize;
728
+ canvas.height = resolution * pixelSize;
729
+
730
+ // Draw each pixel
731
+ for (let y = 0; y < resolution; y++) {
732
+ for (let x = 0; x < resolution; x++) {
733
+ const pixel = state.pixelArtData[y][x];
734
+ const color = pixel.block.color;
735
+
736
+ ctx.fillStyle = `rgb(${color.slice(0, 3).join(',')})`;
737
+ ctx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
738
+
739
+ // Add grid
740
+ ctx.strokeStyle = 'rgba(0,0,0,0.1)';
741
+ ctx.strokeRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
742
+ }
743
+ }
744
+
745
+ // Add title and dimensions
746
+ ctx.fillStyle = '#000';
747
+ ctx.font = '20px Arial';
748
+ ctx.textAlign = 'center';
749
+ ctx.fillText(`Minecraft Pixel Art Blueprint - ${resolution}x${resolution}`, canvas.width / 2, 30);
750
+
751
+ // Convert to data URL and download
752
+ const url = canvas.toDataURL('image/png');
753
+ const a = document.createElement('a');
754
+ a.href = url;
755
+ a.download = 'minecraft-pixel-art-grid.png';
756
+ document.body.appendChild(a);
757
+ a.click();
758
+ document.body.removeChild(a);
759
+ }
760
+
761
+ // Reset application
762
+ function resetApp() {
763
+ state.originalImage = null;
764
+ state.pixelArtData = null;
765
+ state.selectedBlocks = new Set(blockPalette.map(block => block.name));
766
+
767
+ // Reset UI
768
+ dom.originalPreview.classList.add('hidden');
769
+ dom.originalPlaceholder.classList.remove('hidden');
770
+ dom.pixelArtCanvas.classList.add('hidden');
771
+ dom.pixelArtPlaceholder.classList.remove('hidden');
772
+ dom.convertBtn.disabled = true;
773
+ dom.exportBtn.disabled = true;
774
+ dom.exportResults.classList.add('hidden');
775
+
776
+ // Reset file input
777
+ dom.fileUpload.value = '';
778
+
779
+ // Reset block selections
780
+ renderBlockPalette();
781
+ }
782
+
783
+ // Initialize when DOM is loaded
784
+ document.addEventListener('DOMContentLoaded', init);
785
+ </script>
786
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Saad4web/pixelarts" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
787
+ </html>