phenixrhyder commited on
Commit
217f86d
·
unverified ·
1 Parent(s): b4293d0

Delete index.html

Browse files
Files changed (1) hide show
  1. index.html +0 -376
index.html DELETED
@@ -1,376 +0,0 @@
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>Frame Studio</title>
7
- <script src="https://cdn.tailwindcss.com"></script>
8
- <link rel="preconnect" href="https://fonts.googleapis.com">
9
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
- <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">
11
- <style>
12
- body {
13
- font-family: 'Poppins', sans-serif;
14
- background-color: #f9fafb; /* gray-50 */
15
- }
16
- /* Custom scrollbar for the new theme */
17
- ::-webkit-scrollbar { width: 8px; }
18
- ::-webkit-scrollbar-track { background: #e5e7eb; } /* gray-200 */
19
- ::-webkit-scrollbar-thumb { background: #f472b6; border-radius: 4px; } /* pink-400 */
20
- ::-webkit-scrollbar-thumb:hover { background: #ec4899; } /* pink-500 */
21
-
22
- /* Custom styles for range inputs - Light Mode */
23
- input[type="range"] {
24
- -webkit-appearance: none; appearance: none; width: 100%; height: 6px;
25
- background: #e5e7eb; /* gray-200 */ border-radius: 3px;
26
- outline: none; transition: background 0.2s;
27
- }
28
- input[type="range"]::-webkit-slider-thumb {
29
- -webkit-appearance: none; appearance: none; width: 20px; height: 20px;
30
- border-radius: 50%; background: #f9fafb; /* gray-50 */ cursor: pointer;
31
- border: 4px solid #ec4899; /* pink-500 */
32
- transition: transform 0.2s ease-in-out;
33
- box-shadow: 0 1px 3px rgba(0,0,0,0.1);
34
- }
35
- input[type="range"]:hover::-webkit-slider-thumb { transform: scale(1.1); }
36
-
37
- /* Custom styles for color inputs - Light Mode */
38
- input[type="color"] {
39
- -webkit-appearance: none; -moz-appearance: none; appearance: none;
40
- width: 48px; height: 48px; background-color: transparent;
41
- border: none; cursor: pointer; border-radius: 0.5rem;
42
- }
43
- input[type="color"]::-webkit-color-swatch {
44
- border-radius: 0.5rem;
45
- border: 2px solid #d1d5db; /* gray-300 */
46
- }
47
-
48
- /* Modal styles */
49
- #saveModal { display: none; }
50
- </style>
51
- </head>
52
- <body class="text-gray-800">
53
-
54
- <div class="container mx-auto p-4 lg:p-8">
55
- <!-- Header -->
56
- <header class="text-center mb-8 lg:mb-12">
57
- <h1 class="text-4xl lg:text-5xl font-bold text-gray-900 tracking-tight">Frame Studio</h1>
58
- <p class="text-lg text-gray-500 mt-2">Craft your perfect checkerboard frame.</p>
59
- </header>
60
-
61
- <main class="grid grid-cols-1 lg:grid-cols-5 gap-8">
62
- <!-- Controls Column -->
63
- <div class="lg:col-span-2 space-y-8">
64
- <!-- Appearance Controls -->
65
- <div class="bg-white p-6 rounded-2xl border border-gray-200 shadow-sm">
66
- <h2 class="text-2xl font-semibold text-gray-900 mb-5">Appearance</h2>
67
- <div class="flex items-center justify-around space-x-4">
68
- <!-- Color 1 -->
69
- <div class="text-center space-y-2">
70
- <input id="color1" type="color" value="#6b7280">
71
- <label for="color1" class="block text-sm font-medium text-gray-600">Color 1</label>
72
- <span id="color1Value" class="font-mono text-xs text-pink-600">#6b7280</span>
73
- <label class="flex items-center justify-center gap-2 text-gray-500 text-sm pt-1"><input id="color1Transparent" type="checkbox" class="h-4 w-4 rounded border-gray-300 text-pink-500 focus:ring-pink-500"> Transp.</label>
74
- </div>
75
- <!-- Swap & Randomize Buttons -->
76
- <div class="space-y-4">
77
- <button id="swapBtn" title="Swap Colors" class="p-3 bg-gray-100 hover:bg-pink-100 rounded-full transition-all duration-200 group">
78
- <svg class="w-6 h-6 text-gray-500 group-hover:text-pink-600 transition-transform group-hover:rotate-180" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4"></path></svg>
79
- </button>
80
- <button id="randomizeBtn" title="Randomize" class="p-3 bg-gray-100 hover:bg-pink-100 rounded-full transition-all duration-200 group">
81
- <svg class="w-6 h-6 text-gray-500 group-hover:text-pink-600 transition-transform group-hover:rotate-90" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6V4m0 16v-2m8-6h-2m-16 0H4m14.485 8.485l-1.414-1.414M5.929 5.929L4.515 4.515m14.97 14.97l-1.414-1.414M5.929 18.071l-1.414 1.414M12 18a6 6 0 100-12 6 6 0 000 12z"></path></svg>
82
- </button>
83
- </div>
84
- <!-- Color 2 -->
85
- <div class="text-center space-y-2">
86
- <input id="color2" type="color" value="#d1d5db">
87
- <label for="color2" class="block text-sm font-medium text-gray-600">Color 2</label>
88
- <span id="color2Value" class="font-mono text-xs text-pink-600">#d1d5db</span>
89
- <label class="flex items-center justify-center gap-2 text-gray-500 text-sm pt-1"><input id="color2Transparent" type="checkbox" class="h-4 w-4 rounded border-gray-300 text-pink-500 focus:ring-pink-500"> Transp.</label>
90
- </div>
91
- </div>
92
- </div>
93
-
94
- <!-- Pattern Controls -->
95
- <div class="bg-white p-6 rounded-2xl border border-gray-200 shadow-sm space-y-6">
96
- <h2 class="text-2xl font-semibold text-gray-900 mb-2">Pattern</h2>
97
- <div>
98
- <label for="frameThickness" class="flex justify-between items-center font-medium text-gray-600 mb-2">
99
- <span>Frame Thickness</span>
100
- <span id="frameThicknessValue" class="font-mono text-pink-600 bg-pink-50 px-2 py-1 text-sm rounded-md">3</span>
101
- </label>
102
- <input id="frameThickness" type="range" min="1" max="20" value="3" class="w-full">
103
- </div>
104
- <div>
105
- <label for="checkerSize" class="flex justify-between items-center font-medium text-gray-600 mb-2">
106
- <span>Checker Size</span>
107
- <span id="checkerSizeValue" class="font-mono text-pink-600 bg-pink-50 px-2 py-1 text-sm rounded-md">25</span>
108
- </label>
109
- <input id="checkerSize" type="range" min="5" max="100" value="25" class="w-full">
110
- </div>
111
- </div>
112
-
113
- <!-- Export Button -->
114
- <button id="showSaveModalBtn" class="w-full text-lg font-bold py-4 px-4 rounded-xl transition-all duration-300 bg-gradient-to-r from-pink-500 to-orange-400 hover:from-pink-600 hover:to-orange-500 text-white shadow-lg hover:shadow-pink-500/30 transform hover:scale-105">
115
- Export Images...
116
- </button>
117
- </div>
118
-
119
- <!-- Canvas Column -->
120
- <div class="lg:col-span-3">
121
- <div class="aspect-square bg-white rounded-2xl overflow-hidden border-2 border-gray-200 shadow-sm sticky top-8">
122
- <canvas id="checkerboardCanvas"></canvas>
123
- </div>
124
- </div>
125
- </main>
126
- </div>
127
-
128
- <!-- Save Modal -->
129
- <div id="saveModal" class="fixed inset-0 bg-gray-900/60 backdrop-blur-sm flex items-center justify-center p-4">
130
- <div id="modalContent" class="bg-white rounded-2xl shadow-2xl p-8 w-full max-w-lg border border-gray-200">
131
- <!-- View 1: Options -->
132
- <div id="modalOptionsView">
133
- <h2 class="text-2xl font-bold text-gray-900 mb-2">Export Options</h2>
134
- <p class="text-gray-500 mb-6">Select sizes to generate. You can pick multiple.</p>
135
- <div id="sizeOptions" class="grid grid-cols-2 sm:grid-cols-3 gap-3 mb-6"></div>
136
- <div class="mb-6 space-y-4">
137
- <label class="block text-gray-600 font-medium">Custom Size</label>
138
- <div class="flex items-center gap-3">
139
- <input type="number" id="customWidth" placeholder="Width" class="w-full bg-gray-100 border border-gray-300 rounded-md px-3 py-2 text-gray-800 placeholder-gray-400 focus:ring-pink-500 focus:border-pink-500">
140
- <span class="text-gray-400">x</span>
141
- <input type="number" id="customHeight" placeholder="Height" class="w-full bg-gray-100 border border-gray-300 rounded-md px-3 py-2 text-gray-800 placeholder-gray-400 focus:ring-pink-500 focus:border-pink-500">
142
- </div>
143
- </div>
144
- <div class="flex gap-4">
145
- <button id="cancelSaveBtn" class="w-full bg-gray-200 hover:bg-gray-300 text-gray-700 font-bold py-3 px-4 rounded-lg transition-colors">Cancel</button>
146
- <button id="generateBtn" class="w-full bg-gradient-to-r from-pink-500 to-orange-400 hover:from-pink-600 hover:to-orange-500 text-white font-bold py-3 px-4 rounded-lg transition-all">Generate</button>
147
- </div>
148
- </div>
149
-
150
- <!-- View 2: Results -->
151
- <div id="modalResultsView" class="hidden">
152
- <h2 class="text-2xl font-bold text-gray-900 mb-2">Your Images</h2>
153
- <p class="text-gray-500 mb-6">Long-press or right-click an image to save.</p>
154
- <div id="generatedImagesContainer" class="space-y-4 max-h-[60vh] overflow-y-auto p-1 -mr-4 pr-4"></div>
155
- <div class="flex mt-6">
156
- <button id="backToOptionsBtn" class="w-full bg-gray-200 hover:bg-gray-300 text-gray-700 font-bold py-3 px-4 rounded-lg transition-colors">Back</button>
157
- </div>
158
- </div>
159
- </div>
160
- </div>
161
-
162
- <script>
163
- document.addEventListener('DOMContentLoaded', () => {
164
- // --- STATE MANAGEMENT ---
165
- const state = {
166
- frameThickness: 3,
167
- checkerSize: 25,
168
- color1: '#6b7280', // gray-500
169
- color2: '#d1d5db', // gray-300
170
- color1Transparent: false,
171
- color2Transparent: false,
172
- };
173
-
174
- // --- DOM ELEMENT SELECTION ---
175
- const dom = {
176
- canvas: document.getElementById('checkerboardCanvas'),
177
- controls: {
178
- frameThickness: document.getElementById('frameThickness'),
179
- checkerSize: document.getElementById('checkerSize'),
180
- color1: document.getElementById('color1'),
181
- color2: document.getElementById('color2'),
182
- color1Transparent: document.getElementById('color1Transparent'),
183
- color2Transparent: document.getElementById('color2Transparent'),
184
- swapBtn: document.getElementById('swapBtn'),
185
- randomizeBtn: document.getElementById('randomizeBtn'),
186
- },
187
- values: {
188
- frameThickness: document.getElementById('frameThicknessValue'),
189
- checkerSize: document.getElementById('checkerSizeValue'),
190
- color1: document.getElementById('color1Value'),
191
- color2: document.getElementById('color2Value'),
192
- },
193
- modal: {
194
- element: document.getElementById('saveModal'),
195
- content: document.getElementById('modalContent'),
196
- showBtn: document.getElementById('showSaveModalBtn'),
197
- optionsView: document.getElementById('modalOptionsView'),
198
- cancelBtn: document.getElementById('cancelSaveBtn'),
199
- generateBtn: document.getElementById('generateBtn'),
200
- sizeOptions: document.getElementById('sizeOptions'),
201
- customWidth: document.getElementById('customWidth'),
202
- customHeight: document.getElementById('customHeight'),
203
- resultsView: document.getElementById('modalResultsView'),
204
- imagesContainer: document.getElementById('generatedImagesContainer'),
205
- backBtn: document.getElementById('backToOptionsBtn'),
206
- }
207
- };
208
- const ctx = dom.canvas.getContext('2d');
209
-
210
- // --- CONFIGURATION ---
211
- const PREDEFINED_SIZES = [
212
- { w: 256, h: 256 }, { w: 512, h: 512 }, { w: 1024, h: 1024 },
213
- { w: 1920, h: 1080 }, { w: 1080, h: 1920 }, { w: 1600, h: 900 }
214
- ];
215
-
216
- // --- CORE LOGIC ---
217
- const drawPattern = (targetCtx, width, height, config) => {
218
- const { frameThickness, checkerSize, color1, color2, color1Transparent, color2Transparent } = config;
219
- const c1 = color1Transparent ? 'transparent' : color1;
220
- const c2 = color2Transparent ? 'transparent' : color2;
221
-
222
- const cols = Math.ceil(width / checkerSize);
223
- const rows = Math.ceil(height / checkerSize);
224
- targetCtx.clearRect(0, 0, width, height);
225
-
226
- for (let i = 0; i < rows; i++) {
227
- for (let j = 0; j < cols; j++) {
228
- const isFrame = i < frameThickness || i >= rows - frameThickness || j < frameThickness || j >= cols - frameThickness;
229
- if (isFrame) {
230
- targetCtx.fillStyle = (i + j) % 2 === 0 ? c1 : c2;
231
- if (targetCtx.fillStyle !== 'transparent') {
232
- targetCtx.fillRect(j * checkerSize, i * checkerSize, checkerSize, checkerSize);
233
- }
234
- }
235
- }
236
- }
237
- };
238
-
239
- const drawLiveCanvas = () => {
240
- const boardSize = dom.canvas.parentElement.clientWidth;
241
- dom.canvas.width = boardSize;
242
- dom.canvas.height = boardSize;
243
- const liveConfig = { ...state };
244
- drawPattern(ctx, boardSize, boardSize, liveConfig);
245
- };
246
-
247
- const updateUI = () => {
248
- for (const key in dom.controls) {
249
- if (state.hasOwnProperty(key)) {
250
- const el = dom.controls[key];
251
- const value = state[key];
252
- if (el.type === 'checkbox') el.checked = value;
253
- else el.value = value;
254
- }
255
- }
256
- for (const key in dom.values) {
257
- if (state.hasOwnProperty(key)) {
258
- dom.values[key].textContent = state[key];
259
- }
260
- }
261
- drawLiveCanvas();
262
- };
263
-
264
- // --- EVENT HANDLERS ---
265
- const handleControlInput = (e) => {
266
- const { id, value, type, checked } = e.target;
267
- state[id] = type === 'checkbox' ? checked : value;
268
- if(type === 'range') state[id] = parseInt(value, 10);
269
- // BUG FIX: Directly update the UI and redraw on every single input change.
270
- updateUI();
271
- };
272
-
273
- const handleSwap = () => {
274
- [state.color1, state.color2] = [state.color2, state.color1];
275
- [state.color1Transparent, state.color2Transparent] = [state.color2Transparent, state.color1Transparent];
276
- updateUI();
277
- };
278
-
279
- const handleRandomize = () => {
280
- const randomHex = () => '#' + Math.floor(Math.random()*16777215).toString(16).padStart(6, '0');
281
- state.color1 = randomHex();
282
- state.color2 = randomHex();
283
- state.frameThickness = Math.floor(Math.random() * 19) + 1;
284
- state.checkerSize = Math.floor(Math.random() * 95) + 5;
285
- state.color1Transparent = false;
286
- state.color2Transparent = Math.random() < 0.2;
287
- updateUI();
288
- };
289
-
290
- const handleGenerate = () => {
291
- const sizesToGenerate = [];
292
- document.querySelectorAll('#sizeOptions input:checked').forEach(cb => {
293
- const [w, h] = cb.value.split('x').map(Number);
294
- sizesToGenerate.push({ w, h });
295
- });
296
-
297
- // BUG FIX: Added robust parsing for custom sizes to prevent crashes.
298
- const customW = parseInt(dom.modal.customWidth.value, 10) || 0;
299
- const customH = parseInt(dom.modal.customHeight.value, 10) || 0;
300
- if (customW > 0 && customH > 0) {
301
- sizesToGenerate.push({ w: customW, h: customH });
302
- }
303
-
304
- if (sizesToGenerate.length === 0) {
305
- // Add a visual cue that nothing was selected
306
- dom.modal.generateBtn.textContent = "Select a size!";
307
- dom.modal.generateBtn.classList.add('bg-red-500');
308
- setTimeout(() => {
309
- dom.modal.generateBtn.textContent = "Generate";
310
- dom.modal.generateBtn.classList.remove('bg-red-500');
311
- }, 2000);
312
- return;
313
- }
314
-
315
- dom.modal.imagesContainer.innerHTML = '';
316
- sizesToGenerate.forEach(size => {
317
- const offscreenCanvas = document.createElement('canvas');
318
- offscreenCanvas.width = size.w;
319
- offscreenCanvas.height = size.h;
320
- drawPattern(offscreenCanvas.getContext('2d'), size.w, size.h, state);
321
-
322
- const imageCard = document.createElement('div');
323
- imageCard.className = 'bg-gray-100 p-3 rounded-lg border border-gray-200';
324
- imageCard.innerHTML = `
325
- <p class="text-gray-700 font-semibold mb-2">${size.w} x ${size.h} px</p>
326
- <img src="${offscreenCanvas.toDataURL('image/png')}" alt="Generated frame" class="rounded-md border border-gray-300 w-full">`;
327
- dom.modal.imagesContainer.appendChild(imageCard);
328
- });
329
-
330
- dom.modal.customWidth.value = dom.modal.customHeight.value = '';
331
- document.querySelectorAll('#sizeOptions input:checked').forEach(cb => cb.checked = false);
332
- dom.modal.optionsView.classList.add('hidden');
333
- dom.modal.resultsView.classList.remove('hidden');
334
- };
335
-
336
- // --- MODAL ---
337
- const openModal = () => {
338
- dom.modal.element.style.display = 'flex';
339
- };
340
- const closeModal = () => {
341
- dom.modal.element.style.display = 'none';
342
- dom.modal.resultsView.classList.add('hidden');
343
- dom.modal.optionsView.classList.remove('hidden');
344
- };
345
-
346
- // --- INITIALIZATION ---
347
- const initialize = () => {
348
- PREDEFINED_SIZES.forEach(size => {
349
- const val = `${size.w}x${size.h}`;
350
- dom.modal.sizeOptions.innerHTML += `
351
- <label class="flex items-center gap-2 p-3 bg-gray-100 rounded-lg cursor-pointer hover:bg-gray-200 border border-gray-200 transition-colors">
352
- <input type="checkbox" value="${val}" class="h-4 w-4 rounded border-gray-300 text-pink-500 focus:ring-pink-500">
353
- <span class="text-gray-700 text-sm font-medium">${val}</span>
354
- </label>`;
355
- });
356
-
357
- Object.values(dom.controls).forEach(el => el.addEventListener('input', handleControlInput));
358
- dom.controls.swapBtn.addEventListener('click', handleSwap);
359
- dom.controls.randomizeBtn.addEventListener('click', handleRandomize);
360
- dom.modal.showBtn.addEventListener('click', openModal);
361
- dom.modal.cancelBtn.addEventListener('click', closeModal);
362
- dom.modal.generateBtn.addEventListener('click', handleGenerate);
363
- dom.modal.backBtn.addEventListener('click', () => {
364
- dom.modal.resultsView.classList.add('hidden');
365
- dom.modal.optionsView.classList.remove('hidden');
366
- });
367
-
368
- new ResizeObserver(drawLiveCanvas).observe(dom.canvas.parentElement);
369
- updateUI();
370
- };
371
-
372
- initialize();
373
- });
374
- </script>
375
- </body>
376
- </html>