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:
dailyStandupBotREADME.md1 match
3Every weekday at 9am EDT send a message to our team's #engineering Discord channel to start a thread to remind us to do our standup.
45
67Slack version: @mikker/dailySlackRoundup
45<div align="center">
6<img src="https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/67a1d35e-c37c-41a4-0e5a-03a9ba585d00/public" width="700px"/>
7</div>
isMyWebsiteDownREADME.md1 match
89<div align="center">
10<img src="https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/67a1d35e-c37c-41a4-0e5a-03a9ba585d00/public" width="500px"/>
11</div>
10* Create a [Val Town API token](https://www.val.town/settings/api), open the browser preview of this val, and use the API token as the password to log in.
1112<img width=500 src="https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/7077d1b5-1fa7-4a9b-4b93-f8d01d3e4f00/public"/>
7* Type text prompts, select it, press "Q". Select a previous generation with a new text prompt to keep iterating. Selecting shapes doesn't work yet. Have fun!
89<a href="https://x.com/JanPaul123/status/1815502582015754657"><img width=500 src="https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/5893dfbf-c2de-4be0-049e-d8fdd1970a00/public"/></a>
10
lovableVioletLandfowlmain.tsx19 matches
45function Popup({ isOpen, onClose, content }) {
6const [convertedImages, setConvertedImages] = useState({});
7const [converting, setConverting] = useState({});
812const response = await fetch(url);
13const blob = await response.blob();
14const img = await createImageBitmap(blob);
15
16const canvas = document.createElement('canvas');
18canvas.height = img.height;
19const ctx = canvas.getContext('2d');
20ctx.drawImage(img, 0, 0);
21
22const webpBlob = await new Promise(resolve => canvas.toBlob(resolve, 'image/webp'));
23const webpUrl = URL.createObjectURL(webpBlob);
24
25setConvertedImages(prev => ({ ...prev, [index]: { url: webpUrl, size: webpBlob.size } }));
26} catch (error) {
27console.error("Error converting image:", error);
28}
29setConverting(prev => ({ ...prev, [index]: false }));
45<button className="close-btn" onClick={onClose}>×</button>
46<div className="popup-content">
47{content.type === 'images' && (
48<>
49<h3>Images</h3>
50<ul>
51{content.items.map((item, index) => (
53{item.url.split('/').pop()} - {formatSize(item.transferSize)}
54<a href={item.url} download className="download-btn">Download</a>
55{!convertedImages[index] && (
56<button
57onClick={() => convertToWebP(item.url, index)}
62</button>
63)}
64{convertedImages[index] && (
65<div>
66<p>WebP Size: {formatSize(convertedImages[index].size)}</p>
67<a href={convertedImages[index].url} download="converted.webp" className="download-btn">Download WebP</a>
68</div>
69)}
170171const resourceSummary = audits['resource-summary']?.details?.items || [];
172const images = resourceSummary.find(item => item.resourceType === 'image') || { requestCount: 0, transferSize: 0 };
173const videos = resourceSummary.find(item => item.resourceType === 'media') || { requestCount: 0, transferSize: 0 };
174const totalBytes = resourceSummary.reduce((sum, item) => sum + (item.transferSize || 0), 0);
175176const networkRequests = audits['network-requests']?.details?.items || [];
177const imageItems = networkRequests.filter(item => item.resourceType === 'Image' || item.resourceType === 'image');
178const videoItems = networkRequests.filter(item => item.resourceType === 'Media' || item.resourceType === 'media');
179202cls: audits['cumulative-layout-shift']?.displayValue || 'N/A',
203performance: categories.performance ? `${Math.round(categories.performance.score * 100)}%` : 'N/A',
204images: {
205count: images.requestCount || 0,
206size: ((images.transferSize || 0) / 1024 / 1024).toFixed(2),
207items: imageItems
208},
209videos: {
276<li>Cumulative Layout Shift (CLS): {performanceMetrics.cls}</li>
277<li>Performance Score: {performanceMetrics.performance}</li>
278<li>Images: <span className="clickable" onClick={() => handleResourceClick('images')}>{performanceMetrics.images.count}</span> ({performanceMetrics.images.size} MB)</li>
279<li>Videos: <span className="clickable" onClick={() => handleResourceClick('videos')}>{performanceMetrics.videos.count}</span> ({performanceMetrics.videos.size} MB)</li>
280<li>Total Estimated Size: {performanceMetrics.totalSize}</li>
imageToWebPConvertermain.tsx16 matches
45function App() {
6const [imageUrl, setImageUrl] = useState(null);
7const [webpUrl, setWebpUrl] = useState(null);
8const [originalSize, setOriginalSize] = useState(null);
15const reader = new FileReader();
16reader.onload = (e) => {
17setImageUrl(e.target.result);
18setOriginalSize(formatSize(file.size));
19};
2324const convertToWebP = () => {
25if (!imageUrl) return;
2627const img = new Image();
28img.onload = () => {
29const canvas = document.createElement('canvas');
31canvas.height = img.height;
32const ctx = canvas.getContext('2d');
33ctx.drawImage(img, 0, 0);
34
35canvas.toBlob((blob) => {
37setWebpUrl(url);
38setWebpSize(formatSize(blob.size));
39}, 'image/webp');
40};
41img.src = imageUrl;
42};
4344const deleteImages = () => {
45setImageUrl(null);
46setWebpUrl(null);
47setOriginalSize(null);
62return (
63<div className="container">
64<h1>Image to WebP Converter</h1>
65<input
66type="file"
67accept="image/*"
68onChange={handleFileChange}
69ref={fileInputRef}
70/>
71{imageUrl && (
72<div>
73<img src={imageUrl} alt="Original" className="preview" />
74<p>Original Size: {originalSize}</p>
75<button onClick={convertToWebP}>Convert to WebP</button>
83</div>
84)}
85{(imageUrl || webpUrl) && (
86<button onClick={deleteImages} className="delete-btn">Delete Images</button>
87)}
88<p className="footer">
106<meta charset="UTF-8">
107<meta name="viewport" content="width=device-width, initial-scale=1.0">
108<title>Image to WebP Converter</title>
109<style>${css}</style>
110</head>
335background-color: #F5F5DC;
336color: #4A4A4A;
337background-image: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23d2b48c' fill-opacity='0.1'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
338transition: background-color 0.5s, color 0.5s;
339}
388padding: 20px;
389position: relative;
390border-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='75' height='75'%3E%3Cg fill='none' stroke='%23B88846' stroke-width='2'%3E%3Cpath d='M1 1h73v73H1z'/%3E%3Cpath d='M8 8h59v59H8z'/%3E%3Cpath d='M8 8l59 59m0-59L8 67'/%3E%3C/g%3E%3C/svg%3E") 30;
391border-width: 30px;
392border-style: solid;
406fill: #E8D0A9;
407transition: fill 0.3s ease;
408cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='%23FF0000' d='M7 2l12 11.2-5.8 0.5 3.8 7.3-2 1-3.8-7.4-4.2 4.4z'/%3E%3C/svg%3E"), auto;
409}
410
sqliteExplorerAppREADME.md1 match
3View and interact with your Val Town SQLite data. It's based off Steve's excellent [SQLite Admin](https://www.val.town/v/stevekrouse/sqlite_admin?v=46) val, adding the ability to run SQLite queries directly in the interface. This new version has a revised UI and that's heavily inspired by [LibSQL Studio](https://github.com/invisal/libsql-studio) by [invisal](https://github.com/invisal). This is now more an SPA, with tables, queries and results showing up on the same page.
45
67## Install