design32x32bitmapmain.tsx56 matches
4import { adjectives, animals, colors, Config, uniqueNamesGenerator } from "https://esm.sh/unique-names-generator";
56const DEFAULT_IMAGE_NAME = "untitled-<d><d>-<word>";
7const DEFAULT_SIZE = { width: 32, height: 32 };
8const SIZES = [
69setSize,
70clearCanvas,
71saveImage,
72imageName,
73setImageName,
74downloadImage,
75savedImages,
76loadImage,
77deleteImage,
78setShowSettings,
79brushSize,
101<div className="control-group">
102<button onClick={clearCanvas}>Clear</button>
103<button onClick={saveImage}>Save</button>
104</div>
105<div className="control-group">
106<input
107type="text"
108value={imageName}
109onChange={(e) => setImageName(e.target.value)}
110placeholder="Image name"
111/>
112<button onClick={() => downloadImage(imageName)} className="download-btn">
113<span className="download-icon">⬇️</span>
114</button>
118onChange={(e) => {
119if (e.target.value) {
120loadImage(e.target.value);
121e.target.value = "";
122}
123}}
124>
125<option value="">Load image</option>
126{savedImages.map((name) => (
127<option key={name} value={name}>
128{name}
133onChange={(e) => {
134if (e.target.value) {
135deleteImage(e.target.value);
136e.target.value = "";
137}
138}}
139>
140<option value="">Delete image</option>
141{savedImages.map((name) => (
142<option key={name} value={name}>
143{name}
158<h2>Settings</h2>
159<label>
160Default Image Name:
161<input
162type="text"
196const [isDrawing, setIsDrawing] = useState(false);
197const [isPainting, setIsPainting] = useState(false);
198const [imageName, setImageName] = useState(DEFAULT_IMAGE_NAME);
199const [savedImages, setSavedImages] = useState<string[]>([]);
200const [showSettings, setShowSettings] = useState(false);
201const [settings, setSettings] = useState({
202defaultName: DEFAULT_IMAGE_NAME,
203colorScheme: "pitaya",
204});
207208useEffect(() => {
209const saved = localStorage.getItem("savedImagesList");
210if (saved) {
211setSavedImages(JSON.parse(saved));
212}
213const savedSettings = localStorage.getItem("settings");
228229useEffect(() => {
230setImageName(generateName(settings.defaultName));
231}, [settings.defaultName]);
232308}, [size]);
309310const saveImage = useCallback(() => {
311const imageData = JSON.stringify({ name: imageName, size, pixels });
312localStorage.setItem(`image_${imageName}`, imageData);
313setSavedImages(prev => {
314const newSavedImages = [...new Set([...prev, imageName])];
315localStorage.setItem("savedImagesList", JSON.stringify(newSavedImages));
316return newSavedImages;
317});
318setImageName(generateName(settings.defaultName));
319}, [imageName, size, pixels, settings.defaultName]);
320321const loadImage = useCallback((name: string) => {
322if (confirm(`Are you sure you want to load "${name}"? This will overwrite your current work.`)) {
323const imageData = localStorage.getItem(`image_${name}`);
324if (imageData) {
325const { size: loadedSize, pixels: loadedPixels } = JSON.parse(imageData);
326setSize(loadedSize);
327setPixels(loadedPixels);
328setImageName(name);
329}
330}
331}, []);
332333const deleteImage = useCallback((name: string) => {
334if (confirm(`Are you sure you want to delete "${name}"? This action cannot be undone.`)) {
335localStorage.removeItem(`image_${name}`);
336setSavedImages(prev => {
337const newSavedImages = prev.filter(img => img !== name);
338localStorage.setItem("savedImagesList", JSON.stringify(newSavedImages));
339return newSavedImages;
340});
341}
342}, []);
343344const downloadImage = useCallback((customName: string) => {
345const canvas = document.createElement("canvas");
346canvas.width = size.width;
364const fileName = `${customName || generateName(settings.defaultName)}-1bit-${size.width}x${size.height}.bmp`;
365link.download = fileName;
366link.href = canvas.toDataURL("image/bmp");
367link.click();
368}, [pixels, size, settings.defaultName]);
401setSize={setSize}
402clearCanvas={clearCanvas}
403saveImage={saveImage}
404imageName={imageName}
405setImageName={setImageName}
406downloadImage={downloadImage}
407savedImages={savedImages}
408loadImage={loadImage}
409deleteImage={deleteImage}
410setShowSettings={setShowSettings}
411brushSize={brushSize}
589appearance: none;
590padding-right: 30px;
591background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3E%3Cpath fill='%230f0' d='M0 0l4 4 4-4z'/%3E%3C/svg%3E");
592background-repeat: no-repeat;
593background-position: right 10px center;
blob_adminREADME.md1 match
3This is a lightweight Blob Admin interface to view and debug your Blob data.
45
67Use this button to install the val:
blob_adminREADME.md1 match
3This is a lightweight Blob Admin interface to view and debug your Blob data.
45
67Use this button to install the val:
blob_adminREADME.md1 match
3This is a lightweight Blob Admin interface to view and debug your Blob data.
45
67Use this button to install the val:
blob_adminREADME.md1 match
3This is a lightweight Blob Admin interface to view and debug your Blob data.
45
67Use this button to install the val:
45<div align="center">
6<img src="https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/67a1d35e-c37c-41a4-0e5a-03a9ba585d00/public" width="700px"/>
7</div>
design32x32bitmapREADME.md1 match
1A smol tool to design smol 1-bit bitmap images
23mostly for my raspberry pi's 128x32 OLED display
12<meta property="of:accepts:xmtp" content="2024-02-09" />
1314<meta property="fc:frame:image" content={prefixUrl(frame.image)} />
15<meta property="fc:frame:image:aspect_ratio" content={aspectRatio} />
16<meta property="of:image" content={prefixUrl(frame.image)} />
17<meta property="of:image:aspect_ratio" content={aspectRatio} />
18<meta property="og:image" content={prefixUrl(frame.image)} />
1920{frame.buttons.map((b, i) => (
ThumbMakermain.tsx27 matches
1/**
2* This application creates a thumbnail maker using Hono for server-side routing and client-side JavaScript for image processing.
3* It allows users to upload images, specify output options, and generate a composite thumbnail image.
4* The app uses the HTML5 Canvas API for image manipulation and supports drag-and-drop functionality.
5*
6* The process is divided into two steps:
7* 1. Generating thumbnails: Users choose thumbnail size options and create individual thumbnails.
8* 2. Rendering: Users can create and export the final composite image with options for format and quality.
9* This two-step process allows changing format or quality without re-rendering the entire canvas.
10*
33<h1>Thumbnail Maker</h1>
34<div id="dropZone">
35<p>📷 Drag & drop images here or click to select</p>
36<input type="file" id="fileInput" multiple accept="image/*">
37</div>
38<div id="thumbnailOptions">
57Output Format:
58<select id="outputFormat">
59<option value="image/png">PNG</option>
60<option value="image/jpeg">JPEG</option>
61<option value="image/webp">WebP</option>
62</select>
63</label><br>
74<button id="downloadMetadataBtn">Download Metadata</button>
75</div>
76<div id="imagePreview"></div>
77</div>
78</body>
138select {
139appearance: none;
140background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M10.293 3.293L6 7.586 1.707 3.293A1 1 0 00.293 4.707l5 5a1 1 0 001.414 0l5-5a1 1 0 10-1.414-1.414z' fill='%23333'/%3E%3C/svg%3E");
141background-repeat: no-repeat;
142background-position: right 10px center;
204}
205206#imagePreview {
207margin-top: 20px;
208text-align: center;
209}
210211#imagePreview img {
212max-width: 100%;
213height: auto;
231const keepAspectRatio = document.getElementById('keepAspectRatio');
232const thumbWidth = document.getElementById('thumbWidth');
233const imagePreview = document.getElementById('imagePreview');
234const thumbnailOptions = document.getElementById('thumbnailOptions');
235const renderOptions = document.getElementById('renderOptions');
249renderOptions.style.display = 'none';
250downloadSection.style.display = 'none';
251imagePreview.innerHTML = '';
252thumbnailCanvas = null;
253thumbnailMetadata = null;
274event.preventDefault();
275dropZone.classList.remove('drag-over');
276files = Array.from(event.dataTransfer.files).filter(file => file.type.startsWith('image/'));
277resetToStep1();
278});
289progressBar.value = 0;
290291const image0 = await getImage(files[0]);
292const cols = Math.ceil(Math.sqrt(files.length));
293const rows = Math.ceil(files.length / cols);
294const tHeight = parseInt(thumbHeight.value);
295let tWidth = keepAspectRatio.checked
296? Math.floor(tHeight / image0.height * image0.width)
297: parseInt(thumbWidth.value);
298300const canvasHeight = rows * tHeight;
301302image0.revoke();
303304thumbnailCanvas = new OffscreenCanvas(canvasWidth, canvasHeight);
310const row = Math.floor(i / cols);
311312const img = await getImage(file);
313ctx.drawImage(img, col * tWidth, row * tHeight, tWidth, tHeight);
314img.revoke();
315343const blob = await thumbnailCanvas.convertToBlob({
344type: outputFormat.value,
345quality: outputFormat.value !== 'image/png' ? parseFloat(outputQuality.value) : undefined
346});
347351downloadSection.style.display = 'flex';
352353// Display the generated image
354const img = document.createElement('img');
355img.src = url;
356imagePreview.innerHTML = '';
357imagePreview.appendChild(img);
358359progressBar.style.display = 'none';
375});
376377function getImage(file) {
378return new Promise((resolve) => {
379const url = URL.createObjectURL(file);
380const img = new Image();
381img.revoke = () => URL.revokeObjectURL(url);
382img.onload = () => resolve(img);
135<link
136rel="icon"
137href="data:image/avif;base64,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"
138/>
139<title>rss viewer</title>