noCollectionTrackerREADME.md2 matches
113. **Reflection Stage**: After 5 "No" responses, users enter a reflection stage to contemplate their journey.
124. **Motivational Quotes**: Dynamic quotes change based on the user's progress to provide encouragement.
135. **Journey Visualization**: Users can upload a symbolic image representing their journey.
146. **PDF Generation**: At the end of the challenge, users can generate a PDF summary of their journey.
157. **Data Privacy**: All data is stored locally on the user's device, ensuring privacy and security.
252. **Reflection Stage**:
26- Triggered after receiving 5 "No" responses.
27- Users can upload a symbolic image and write a comprehensive reflection.
28293. **Completion Stage**:
md_links_resolvermain.tsx1 match
220<title>Markdown Links Resolver</title>
221<meta name="viewport" content="width=device-width, initial-scale=1">
222<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'><text font-size='200' x='50%' y='50%' text-anchor='middle' dominant-baseline='central'>🔗</text></svg>">
223<style>
224body, html {
versatileBlackKitemain.tsx79 matches
6function App() {
7const [youtubeUrl, setYoutubeUrl] = useState("https://www.youtube.com/embed/QskiaNqaqlc?si=MvD_ydotKgovS9pC");
8const [postVideoImages, setPostVideoImages] = useState<string[]>([]);
9const [newImageUrl, setNewImageUrl] = useState("");
10const [imageErrors, setImageErrors] = useState<{[key: number]: boolean}>({});
11const [imageProxyUrls, setImageProxyUrls] = useState<{[key: number]: string}>({});
1213useEffect(() => {
1718useEffect(() => {
19// Proxy images that might have CORS issues
20postVideoImages.forEach((imageUrl, index) => {
21proxyImage(imageUrl, index);
22});
23}, [postVideoImages]);
2425const proxyImage = async (imageUrl: string, index: number) => {
26try {
27const response = await fetch(`/proxy-image?url=${encodeURIComponent(imageUrl)}`);
28if (response.ok) {
29const proxyUrl = await response.text();
30setImageProxyUrls(prev => ({...prev, [index]: proxyUrl}));
31} else {
32handleImageError(index);
33}
34} catch (error) {
35console.error("Image proxy error:", error);
36handleImageError(index);
37}
38};
42const { blob } = await import("https://esm.town/v/std/blob");
43const savedYoutubeUrl = await blob.get(`${getLegacyImportUrl(import.meta.url)}_youtubeUrl`);
44const savedImages = await blob.get(`${getLegacyImportUrl(import.meta.url)}_postVideoImages`);
45
46if (savedYoutubeUrl) setYoutubeUrl(savedYoutubeUrl);
47if (savedImages) {
48const parsedImages = JSON.parse(savedImages);
49setPostVideoImages(parsedImages);
50}
51} catch (error) {
54};
5556const saveData = async (type: 'url' | 'images') => {
57try {
58const { blob } = await import("https://esm.town/v/std/blob");
60await blob.set(`${getLegacyImportUrl(import.meta.url)}_youtubeUrl`, youtubeUrl);
61} else {
62await blob.set(`${getLegacyImportUrl(import.meta.url)}_postVideoImages`, JSON.stringify(postVideoImages));
63}
64} catch (error) {
67};
6869const isValidImageUrl = (url: string) => {
70try {
71const parsedUrl = new URL(url);
74(parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:') &&
75(
76// General image extensions
77/\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(parsedUrl.pathname) ||
78// AWS S3 URL patterns
87};
8889const handleAddImage = () => {
90if (newImageUrl.trim() && isValidImageUrl(newImageUrl.trim())) {
91const updatedImages = [...postVideoImages, newImageUrl.trim()];
92setPostVideoImages(updatedImages);
93setNewImageUrl("");
94saveData('images');
95} else {
96alert("Please enter a valid image URL (supports JPG, PNG, GIF, WebP, BMP, and AWS S3 URLs)");
97}
98};
99100const handleRemoveImage = (indexToRemove: number) => {
101const updatedImages = postVideoImages.filter((_, index) => index !== indexToRemove);
102setPostVideoImages(updatedImages);
103
104// Remove any associated error and proxy states
105const newImageErrors = {...imageErrors};
106const newImageProxyUrls = {...imageProxyUrls};
107delete newImageErrors[indexToRemove];
108delete newImageProxyUrls[indexToRemove];
109
110setImageErrors(newImageErrors);
111setImageProxyUrls(newImageProxyUrls);
112
113saveData('images');
114};
115116const handleImageError = (index: number) => {
117setImageErrors(prev => ({...prev, [index]: true}));
118};
119122<h1 style={styles.title}>Interactive Video Presentation</h1>
123<p style={styles.description}>
124Welcome to our dynamic video presentation. You can customize the video and add post-video images.
125</p>
126
153</div>
154155{/* Post Video Images Section */}
156<div style={styles.postVideoSection}>
157<h3>Post Video Images</h3>
158<div style={styles.imageInputContainer}>
159<input
160type="text"
161value={newImageUrl}
162onChange={(e) => setNewImageUrl(e.target.value)}
163style={styles.urlInput}
164placeholder="Enter Image URL (supports AWS S3)"
165/>
166<button onClick={handleAddImage} style={styles.saveButton}>Add Image</button>
167</div>
168
169{/* Image Gallery */}
170<div style={styles.imageGallery}>
171{postVideoImages.map((imageUrl, index) => (
172<div key={index} style={styles.imageItem}>
173{imageErrors[index] ? (
174<div style={styles.errorImage}>
175<span>❌ Image Failed to Load</span>
176<p>{imageUrl}</p>
177</div>
178) : (
179<img
180src={imageProxyUrls[index] || imageUrl}
181alt={`Post video image ${index + 1}`}
182style={styles.galleryImage}
183onError={() => handleImageError(index)}
184/>
185)}
186<button
187onClick={() => handleRemoveImage(index)}
188style={styles.removeButton}
189>
213214export default async function server(request: Request): Promise<Response> {
215// Image proxy endpoint
216const url = new URL(request.url);
217if (url.pathname === '/proxy-image') {
218const imageUrl = url.searchParams.get('url');
219if (!imageUrl) {
220return new Response('No URL provided', { status: 400 });
221}
222223try {
224const imageResponse = await fetch(imageUrl, {
225headers: {
226'User-Agent': 'Mozilla/5.0',
227'Referer': new URL(imageUrl).origin
228}
229});
230231if (!imageResponse.ok) {
232return new Response('Image fetch failed', { status: imageResponse.status });
233}
234235const imageBlob = await imageResponse.blob();
236return new Response(imageBlob, {
237headers: {
238'Content-Type': imageResponse.headers.get('Content-Type') || 'image/jpeg',
239'Cache-Control': 'public, max-age=86400' // Cache for 24 hours
240}
241});
242} catch (error) {
243return new Response('Image proxy error', { status: 500 });
244}
245}
315marginTop: '20px',
316},
317imageInputContainer: {
318display: 'flex',
319marginBottom: '20px',
320gap: '10px',
321},
322imageGallery: {
323display: 'flex',
324flexWrap: 'wrap',
326justifyContent: 'center',
327},
328imageItem: {
329position: 'relative',
330display: 'flex',
332alignItems: 'center',
333},
334galleryImage: {
335maxWidth: '200px',
336maxHeight: '200px',
338borderRadius: '8px',
339},
340errorImage: {
341width: '200px',
342height: '200px',
embedVideoWebPagemain.tsx81 matches
6function App() {
7const [youtubeUrl, setYoutubeUrl] = useState("https://www.youtube.com/embed/QskiaNqaqlc?si=MvD_ydotKgovS9pC");
8const [postVideoImages, setPostVideoImages] = useState<string[]>([]);
9const [newImageUrl, setNewImageUrl] = useState("");
10const [imageErrors, setImageErrors] = useState<{[key: number]: boolean}>({});
11const [imageProxyUrls, setImageProxyUrls] = useState<{[key: number]: string}>({});
1213useEffect(() => {
1617useEffect(() => {
18// Proxy images that might have CORS issues
19postVideoImages.forEach((imageUrl, index) => {
20if (!imageErrors[index]) {
21proxyImage(imageUrl, index);
22}
23});
24}, [postVideoImages]);
2526const proxyImage = async (imageUrl: string, index: number) => {
27try {
28const response = await fetch(`/proxy-image?url=${encodeURIComponent(imageUrl)}`, {
29headers: {
30'Cache-Control': 'no-cache' // Ensure fresh fetch
33if (response.ok) {
34const proxyUrl = await response.text();
35setImageProxyUrls(prev => ({...prev, [index]: proxyUrl}));
36// Reset any previous error for this image
37setImageErrors(prev => {
38const newErrors = {...prev};
39delete newErrors[index];
41});
42} else {
43handleImageError(index);
44}
45} catch (error) {
46console.error("Image proxy error:", error);
47handleImageError(index);
48}
49};
53const { blob } = await import("https://esm.town/v/std/blob");
54const savedYoutubeUrl = await blob.get(`${getLegacyImportUrl(import.meta.url)}_youtubeUrl`);
55const savedImages = await blob.get(`${getLegacyImportUrl(import.meta.url)}_postVideoImages`);
56
57if (savedYoutubeUrl) setYoutubeUrl(savedYoutubeUrl);
58if (savedImages) {
59const parsedImages = JSON.parse(savedImages);
60setPostVideoImages(parsedImages);
61}
62} catch (error) {
65};
6667const saveData = async (type: 'url' | 'images') => {
68try {
69const { blob } = await import("https://esm.town/v/std/blob");
71await blob.set(`${getLegacyImportUrl(import.meta.url)}_youtubeUrl`, youtubeUrl);
72} else {
73await blob.set(`${getLegacyImportUrl(import.meta.url)}_postVideoImages`, JSON.stringify(postVideoImages));
74}
75} catch (error) {
78};
7980const isValidImageUrl = (url: string) => {
81try {
82new URL(url);
87};
8889const handleAddImage = () => {
90if (newImageUrl.trim() && isValidImageUrl(newImageUrl.trim())) {
91const updatedImages = [...postVideoImages, newImageUrl.trim()];
92setPostVideoImages(updatedImages);
93setNewImageUrl("");
94saveData('images');
95} else {
96alert("Please enter a valid image URL with a supported extension");
97}
98};
99100const handleRemoveImage = (indexToRemove: number) => {
101const updatedImages = postVideoImages.filter((_, index) => index !== indexToRemove);
102setPostVideoImages(updatedImages);
103
104// Remove any associated error and proxy states
105const newImageErrors = {...imageErrors};
106const newImageProxyUrls = {...imageProxyUrls};
107delete newImageErrors[indexToRemove];
108delete newImageProxyUrls[indexToRemove];
109
110setImageErrors(newImageErrors);
111setImageProxyUrls(newImageProxyUrls);
112
113saveData('images');
114};
115116const handleImageError = (index: number) => {
117setImageErrors(prev => ({...prev, [index]: true}));
118};
119120const retryImageLoad = (index: number) => {
121const imageUrl = postVideoImages[index];
122proxyImage(imageUrl, index);
123};
124127<h1 style={styles.title}>Interactive Video Presentation</h1>
128<p style={styles.description}>
129Welcome to our dynamic video presentation. You can customize the video and add post-video images.
130</p>
131
158</div>
159160{/* Post Video Images Section */}
161<div style={styles.postVideoSection}>
162<h3>Post Video Images</h3>
163<div style={styles.imageInputContainer}>
164<input
165type="text"
166value={newImageUrl}
167onChange={(e) => setNewImageUrl(e.target.value)}
168style={styles.urlInput}
169placeholder="Enter Image URL"
170/>
171<button onClick={handleAddImage} style={styles.saveButton}>Add Image</button>
172</div>
173
174{/* Image Gallery */}
175<div style={styles.imageGallery}>
176{postVideoImages.map((imageUrl, index) => (
177<div key={index} style={styles.imageItem}>
178{imageErrors[index] ? (
179<div style={styles.errorImage}>
180<span>❌ Image Failed to Load</span>
181<p>{imageUrl}</p>
182<button
183onClick={() => retryImageLoad(index)}
184style={{...styles.saveButton, marginTop: '10px'}}
185>
189) : (
190<img
191src={imageProxyUrls[index] || imageUrl}
192alt={`Post video image ${index + 1}`}
193style={styles.galleryImage}
194onError={() => handleImageError(index)}
195/>
196)}
197<button
198onClick={() => handleRemoveImage(index)}
199style={styles.removeButton}
200>
224225export default async function server(request: Request): Promise<Response> {
226// Image proxy endpoint
227const url = new URL(request.url);
228if (url.pathname === '/proxy-image') {
229const imageUrl = url.searchParams.get('url');
230if (!imageUrl) {
231return new Response('No URL provided', { status: 400 });
232}
233234try {
235const imageResponse = await fetch(imageUrl, {
236headers: {
237'User-Agent': 'Mozilla/5.0',
238'Referer': new URL(imageUrl).origin,
239'Accept': 'image/*'
240}
241});
242243if (!imageResponse.ok) {
244return new Response('Image fetch failed', { status: imageResponse.status });
245}
246247const imageBlob = await imageResponse.blob();
248return new Response(imageBlob, {
249headers: {
250'Content-Type': imageResponse.headers.get('Content-Type') || 'image/jpeg',
251'Cache-Control': 'public, max-age=86400', // Cache for 24 hours
252'Access-Control-Allow-Origin': '*'
254});
255} catch (error) {
256return new Response('Image proxy error', { status: 500 });
257}
258}
blob_adminREADME.md1 match
3This is a lightweight Blob Admin interface to view and debug your Blob data.
45
67Versions 0-17 of this val were done with Hono and server-rendering.
blob_adminmain.tsx5 matches
440{profile && (
441<div className="flex items-center space-x-4">
442<img src={profile.profileImageUrl} alt="Profile" className="w-8 h-8 rounded-full" />
443<span>{profile.username}</span>
444<a href="/auth/logout" className="text-blue-400 hover:text-blue-300">Logout</a>
583alt="Blob content"
584className="max-w-full h-auto"
585onError={() => console.error("Error loading image")}
586/>
587</div>
635<li>Create public shareable links for blobs</li>
636<li>View and manage public folder</li>
637<li>Preview images directly in the interface</li>
638</ul>
639</div>
693const { ValTown } = await import("npm:@valtown/sdk");
694const vt = new ValTown();
695const { email: authorEmail, profileImageUrl, username } = await vt.me.profile.retrieve();
696// const authorEmail = me.email;
697761762c.set("email", email);
763c.set("profile", { profileImageUrl, username });
764await next();
765};
superbAzureSharkREADME.md1 match
3This is a lightweight Blob Admin interface to view and debug your Blob data.
45
67Versions 0-17 of this val were done with Hono and server-rendering.
superbAzureSharkmain.tsx5 matches
440{profile && (
441<div className="flex items-center space-x-4">
442<img src={profile.profileImageUrl} alt="Profile" className="w-8 h-8 rounded-full" />
443<span>{profile.username}</span>
444<a href="/auth/logout" className="text-blue-400 hover:text-blue-300">Logout</a>
583alt="Blob content"
584className="max-w-full h-auto"
585onError={() => console.error("Error loading image")}
586/>
587</div>
635<li>Create public shareable links for blobs</li>
636<li>View and manage public folder</li>
637<li>Preview images directly in the interface</li>
638</ul>
639</div>
693const { ValTown } = await import("npm:@valtown/sdk");
694const vt = new ValTown();
695const { email: authorEmail, profileImageUrl, username } = await vt.me.profile.retrieve();
696// const authorEmail = me.email;
697761762c.set("email", email);
763c.set("profile", { profileImageUrl, username });
764await next();
765};
gif_uploadmain.tsx2 matches
17const calculateAspectRatio = (file: File) => {
18return new Promise<{ width: number; height: number }>((resolve) => {
19const img = new Image();
20img.onload = () => {
21URL.revokeObjectURL(img.src);
206headers: {
207Authorization: `Bearer ${token}`,
208"Content-Type": "image/gif",
209"Content-Length": String(size),
210},
blob_adminREADME.md1 match
3This is a lightweight Blob Admin interface to view and debug your Blob data.
45
67Versions 0-17 of this val were done with Hono and server-rendering.