3This val sends weekly messages about the change in user count in Clerk.
45
67## How it works
plugineloopBackendusers.ts3 matches
86if (data.avatar !== undefined) {
87// Handle avatar upload
88if (data.avatar.startsWith("data:image")) {
89// Extract base64 data
90const base64Data = data.avatar.split(",")[1];
91const imageData = atob(base64Data);
92
93// Save to blob storage
94const avatarKey = `avatar_${user.id}_${Date.now()}.png`;
95await blob.set(avatarKey, imageData);
96
97const avatarUrl = `https://toowired.val.run/blob/${avatarKey}`;
7your workspace.
89
1011Read the full guide here: https://docs.val.town/integrations/slack/bot/
fib-bonusindex-running.html1 match
82}
83.grid-pattern {
84background-image: linear-gradient(
85rgba(0, 0, 0, 0.03) 1px,
86transparent 1px
fib-bonusindex.html1 match
82}
83.grid-pattern {
84background-image: linear-gradient(
85rgba(0, 0, 0, 0.03) 1px,
86transparent 1px
16loginApiUrl: "https://example.com/api/login", // 登录接口的 URL
17checkinApiUrl: "https://example.com/api/checkin", // 打卡接口的 URL
18captchaImageUrl: "https://example.com/captcha.jpg", // 验证码图片的 URL (如果存在)
19checkinSuccessText: "打卡成功", // 打卡成功的标志性文本
20};
53*/
54async function solveCaptcha() {
55if (!config.captchaImageUrl) {
56console.log("未配置验证码图片 URL, 跳过验证码识别.");
57return null;
61const solver = new TwoCaptcha.Solver(Deno.env.get("TWOCAPTCHA_API_KEY"));
62console.log("正在识别验证码...");
63const res = await solver.imageCaptcha({
64body: await (await fetch(config.captchaImageUrl)).arrayBuffer(),
65});
66console.log(`验证码识别结果: ${res.data}`);
102#god-ray-container { background: radial-gradient(circle, rgba(20,20,35,0.8) 0%, rgba(10,10,20,0.95) 60%); }
103#god-ray-animator { position: relative; width: 1px; height: 1px; animation: rotate-container 20s linear infinite; }
104.god-ray { position: absolute; top: 0; left: 0; width: 4px; height: 150vmax; transform-origin: top center; background-image: linear-gradient(to bottom, rgba(255,255,255,0.8), rgba(255,255,255,0)); animation: ray-pulse 4s ease-in-out infinite alternate; }
105@keyframes rotate-container { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
106@keyframes ray-pulse {
TownieInputBox.tsx47 matches
3import { Link } from "react-router";
4import { PlusIcon, ArrowUpIcon, Square, XIcon } from "./icons.tsx";
5import { processFiles } from "../utils/images.ts";
67export function InputBox ({
12running,
13error,
14images,
15setImages,
16} : {
17value: string;
21running: boolean;
22error: any;
23images: (string|null)[];
24setImages: (images: (string|null)[]) => void;
25}) {
26const form = useRef(null);
62/>
63</div>
64<ImageRow images={images} setImages={setImages} />
65<div className="toolbar">
66<UploadButton
67disabled={running}
68images={images}
69setImages={setImages}
70/>
71<div className="spacer" />
94}
9596export function ImageDropContainer ({
97images,
98setImages,
99running,
100children,
101}: {
102images: (string|null)[];
103setImages: (images: (string|null)[]) => void;
104running: boolean;
105children: React.ReactNode;
106}) {
107const dragging = useImageDrop({ images, setImages, running });
108109return (
111{children}
112{dragging && (
113<div className="image-drop-overlay">
114<div className="image-drop-inner">
115Drop images here to upload
116</div>
117</div>
121}
122123export function useImageDrop ({ images, setImages, running }: {
124images: (string|null)[];
125setImages(images: (string|null)[]) => void;
126running: boolean;
127}) {
149setDragging(false);
150if (e.dataTransfer?.files && !running) {
151processFiles(Array.from(e.dataTransfer.files), images, setImages);
152}
153}
165document.removeEventListener("drop", onDrop);
166}
167}, [images, setImages, running]);
168169return dragging;
170}
171172function ImageRow ({ images, setImages }: {
173images: (string|null)[];
174setImages: (images: (string|null)[]) => void;
175}) {
176return (
177<div className="image-row">
178{images.map((image, i) => (
179<Thumbnail
180key={i}
181image={image}
182onRemove={() => {
183setImages([
184...images.slice(0, i),
185...images.slice(i + 1),
186]);
187}}
192}
193194function Thumbnail ({ image, onRemove }: {
195image: string|null;
196onRemove: () => void;
197}) {
198if (!image) return null;
199200return (
201<div className="input-image">
202<img
203src={image}
204alt="User uploaded image"
205className="image-thumbnail"
206/>
207<button
208type="button"
209title="Remove image"
210className="remove-image-button"
211onClick={onRemove}
212>
218219function UploadButton ({
220images,
221setImages,
222disabled,
223}: {
224images: (string|null)[];
225setImages: (images: (string|null)[]) => void;
226disabled: boolean;
227}) {
232<button
233type="button"
234title="Upload image"
235disabled={disabled}
236onClick={e => {
240<PlusIcon />
241<div className="sr-only">
242Upload image
243</div>
244</button>
249onChange={e => {
250if (e.target.files) {
251processFiles(Array.from(e.target.files), images, setImages);
252}
253}}
TownieProjectsRoute.tsx7 matches
44user: {
45username: string;
46profileImageUrl: string | null;
47};
48project: any;
50return (
51<div className="card">
52{project.imageUrl ? (
53<img src={project.imageUrl} className="card-image" />
54) : user.profileImageUrl ? (
55<div className="card-image">
56<img
57src={user.profileImageUrl}
58width="48"
59height="48"
62</div>
63) : (
64<div className="card-image placeholder" />
65)}
66<div className="card-body">
TownieHeader.tsx2 matches
7374function Avatar ({ user }) {
75if (!user?.profileImageUrl) {
76return (
77<div className="avatar" />
81return (
82<img
83src={user.profileImageUrl}
84alt={user.username}
85width="32"