Skip to content

Commit ba35b4a

Browse files
Enhance UI and improve user experience in Badge Generator with responsive design and debounced input handling
1 parent 52f1f90 commit ba35b4a

File tree

2 files changed

+205
-90
lines changed

2 files changed

+205
-90
lines changed

src/index.ts

Lines changed: 205 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -16,130 +16,236 @@ app.get("/", (c) => {
1616
<meta name="viewport" content="width=device-width, initial-scale=1.0">
1717
<title>Badge Generator</title>
1818
<style>
19+
:root {
20+
--primary: #0366d6;
21+
--gray: #586069;
22+
--border: #e1e4e8;
23+
--input-bg: #f6f8fa;
24+
}
25+
* {
26+
box-sizing: border-box;
27+
}
1928
body {
2029
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
21-
max-width: 800px;
30+
max-width: 1000px;
2231
margin: 0 auto;
2332
padding: 20px;
33+
background: #fafbfc;
34+
color: #24292e;
35+
line-height: 1.5;
36+
}
37+
h1 {
38+
text-align: center;
39+
color: var(--primary);
40+
margin-bottom: 2rem;
41+
}
42+
.container {
43+
background: white;
44+
border-radius: 8px;
45+
box-shadow: 0 1px 3px rgba(0,0,0,0.12);
46+
padding: 24px;
2447
}
2548
.preview {
2649
margin: 20px 0;
27-
padding: 20px;
28-
border: 1px solid #ddd;
29-
border-radius: 4px;
50+
padding: 24px;
51+
border: 1px solid var(--border);
52+
border-radius: 8px;
53+
text-align: center;
54+
background: var(--input-bg);
55+
}
56+
.preview img {
57+
max-width: 100%;
58+
height: auto;
59+
transition: opacity 0.3s;
60+
}
61+
.preview.loading img {
62+
opacity: 0.5;
3063
}
3164
.controls {
3265
display: grid;
33-
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
34-
gap: 10px;
35-
margin-bottom: 20px;
66+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
67+
gap: 16px;
68+
margin-bottom: 24px;
3669
}
37-
.controls label {
70+
.control-group {
71+
margin-bottom: 8px;
72+
}
73+
.control-group label {
3874
display: block;
39-
margin-bottom: 5px;
75+
margin-bottom: 8px;
76+
font-weight: 500;
77+
color: var(--gray);
4078
}
41-
.url-box {
79+
input, select {
4280
width: 100%;
43-
padding: 10px;
44-
margin: 10px 0;
45-
font-family: monospace;
81+
padding: 8px 12px;
82+
border: 1px solid var (--border);
83+
border-radius: 6px;
84+
background: var(--input-bg);
85+
font-size: 14px;
86+
transition: border-color 0.2s;
4687
}
47-
button {
48-
padding: 8px 16px;
49-
background: #0366d6;
50-
color: white;
51-
border: none;
52-
border-radius: 4px;
53-
cursor: pointer;
88+
input:focus, select:focus {
89+
border-color: var(--primary);
90+
outline: none;
91+
box-shadow: 0 0 0 3px rgba(3, 102, 214, 0.1);
5492
}
55-
button:hover {
56-
background: #0255b3;
93+
.url-box {
94+
width: 100%;
95+
padding: 12px;
96+
margin: 16px 0;
97+
font-family: monospace;
98+
background: var(--input-bg);
99+
border: 1px solid var(--border);
100+
border-radius: 6px;
101+
font-size: 14px;
57102
}
58103
.tabs {
59104
display: flex;
60-
gap: 10px;
61-
margin-bottom: 20px;
105+
gap: 8px;
106+
margin-bottom: 24px;
107+
border-bottom: 1px solid var(--border);
108+
padding-bottom: 8px;
62109
}
63110
.tabs button {
64-
background: #eee;
65-
color: #333;
111+
padding: 8px 16px;
112+
background: transparent;
113+
border: none;
114+
color: var(--gray);
115+
cursor: pointer;
116+
font-size: 16px;
117+
font-weight: 500;
118+
border-bottom: 2px solid transparent;
119+
transition: all 0.2s;
120+
}
121+
.tabs button:hover {
122+
color: var(--primary);
66123
}
67124
.tabs button.active {
68-
background: #0366d6;
125+
color: var(--primary);
126+
border-bottom-color: var(--primary);
127+
}
128+
.copy-btn {
129+
display: block;
130+
width: 100%;
131+
padding: 12px;
132+
background: var(--primary);
69133
color: white;
134+
border: none;
135+
border-radius: 6px;
136+
font-size: 16px;
137+
font-weight: 500;
138+
cursor: pointer;
139+
transition: background 0.2s;
140+
}
141+
.copy-btn:hover {
142+
background: #0255b3;
143+
}
144+
@media (max-width: 600px) {
145+
body {
146+
padding: 12px;
147+
}
148+
.container {
149+
padding: 16px;
150+
}
151+
.controls {
152+
grid-template-columns: 1fr;
153+
}
70154
}
71155
</style>
72156
</head>
73157
<body>
74158
<h1>Badge Generator</h1>
75-
76-
<div class="tabs">
77-
<button onclick="switchTab('visitor')" class="active">Visitor Badge</button>
78-
<button onclick="switchTab('ai')">AI Badge</button>
79-
</div>
159+
<div class="container">
160+
<div class="tabs">
161+
<button onclick="switchTab('visitor')" class="active">Visitor Badge</button>
162+
<button onclick="switchTab('ai')">AI Badge</button>
163+
</div>
80164
81-
<div id="visitor-tab">
82-
<div class="controls">
83-
<div>
84-
<label>Repository:</label>
85-
<input type="text" id="repo" placeholder="username/repo" />
86-
</div>
87-
<div>
88-
<label>Style:</label>
89-
<select id="style">
90-
<option value="flat">Flat</option>
91-
<option value="flat-square">Flat Square</option>
92-
<option value="plastic">Plastic</option>
93-
<option value="for-the-badge">For the Badge</option>
94-
<option value="social">Social</option>
95-
</select>
96-
</div>
97-
<div>
98-
<label>Color:</label>
99-
<input type="text" id="color" value="blue" />
100-
</div>
101-
<div>
102-
<label>Label:</label>
103-
<input type="text" id="label" value="Profile views" />
165+
<div id="visitor-tab">
166+
<div class="controls">
167+
<div class="control-group">
168+
<label>Repository:</label>
169+
<input type="text" id="repo" placeholder="username/repo" />
170+
</div>
171+
<div class="control-group">
172+
<label>Style:</label>
173+
<select id="style">
174+
<option value="flat">Flat</option>
175+
<option value="flat-square">Flat Square</option>
176+
<option value="plastic">Plastic</option>
177+
<option value="for-the-badge">For the Badge</option>
178+
<option value="social">Social</option>
179+
</select>
180+
</div>
181+
<div class="control-group">
182+
<label>Color:</label>
183+
<input type="text" id="color" value="blue" />
184+
</div>
185+
<div class="control-group">
186+
<label>Label:</label>
187+
<input type="text" id="label" value="Profile views" />
188+
</div>
104189
</div>
105190
</div>
106-
</div>
107191
108-
<div id="ai-tab" style="display:none">
109-
<div class="controls">
110-
<div>
111-
<label>Prompt:</label>
112-
<input type="text" id="prompt" placeholder="Generate a message..." />
113-
</div>
114-
<div>
115-
<label>Style:</label>
116-
<select id="ai-style">
117-
<option value="flat">Flat</option>
118-
<option value="flat-square">Flat Square</option>
119-
<option value="plastic">Plastic</option>
120-
<option value="for-the-badge">For the Badge</option>
121-
<option value="social">Social</option>
122-
</select>
123-
</div>
124-
<div>
125-
<label>Color:</label>
126-
<input type="text" id="ai-color" value="blue" />
192+
<div id="ai-tab" style="display:none">
193+
<div class="controls">
194+
<div class="control-group">
195+
<label>Prompt:</label>
196+
<input type="text" id="prompt" placeholder="Generate a message..." />
197+
</div>
198+
<div class="control-group">
199+
<label>Style:</label>
200+
<select id="ai-style">
201+
<option value="flat">Flat</option>
202+
<option value="flat-square">Flat Square</option>
203+
<option value="plastic">Plastic</option>
204+
<option value="for-the-badge">For the Badge</option>
205+
<option value="social">Social</option>
206+
</select>
207+
</div>
208+
<div class="control-group">
209+
<label>Color:</label>
210+
<input type="text" id="ai-color" value="blue" />
211+
</div>
127212
</div>
128213
</div>
129-
</div>
130214
131-
<div class="preview">
132-
<h3>Preview:</h3>
133-
<img id="preview" src="" alt="Badge preview" />
134-
</div>
215+
<div class="preview">
216+
<img id="preview" src="" alt="Badge preview" />
217+
</div>
135218
136-
<input type="text" id="url" class="url-box" readonly />
137-
<button onclick="copyUrl()">Copy URL</button>
219+
<input type="text" id="url" class="url-box" readonly />
220+
<button onclick="copyUrl()" class="copy-btn">Copy URL</button>
221+
</div>
138222
139223
<script>
140224
let currentTab = 'visitor';
225+
let updateTimeout;
141226
142-
function updatePreview() {
227+
function debounce(func, wait) {
228+
return function executedFunction(...args) {
229+
const later = () => {
230+
clearTimeout(updateTimeout);
231+
func(...args);
232+
};
233+
clearTimeout(updateTimeout);
234+
updateTimeout = setTimeout(later, wait);
235+
};
236+
}
237+
238+
const setPreviewLoading = (loading) => {
239+
const preview = document.querySelector('.preview');
240+
if (loading) {
241+
preview.classList.add('loading');
242+
} else {
243+
preview.classList.remove('loading');
244+
}
245+
};
246+
247+
const debouncedUpdatePreview = debounce(() => {
248+
setPreviewLoading(true);
143249
const baseUrl = window.location.origin;
144250
let url;
145251
@@ -158,9 +264,12 @@ app.get("/", (c) => {
158264
url = \`\${baseUrl}/ai-badge?prompt=\${encodeURIComponent(prompt)}&style=\${style}&color=\${color}\`;
159265
}
160266
161-
document.getElementById('preview').src = url;
267+
const previewImg = document.getElementById('preview');
268+
previewImg.onload = () => setPreviewLoading(false);
269+
previewImg.onerror = () => setPreviewLoading(false);
270+
previewImg.src = url;
162271
document.getElementById('url').value = url;
163-
}
272+
}, 500);
164273
165274
function switchTab(tab) {
166275
currentTab = tab;
@@ -171,22 +280,29 @@ app.get("/", (c) => {
171280
tabs.forEach(btn => btn.classList.remove('active'));
172281
event.target.classList.add('active');
173282
174-
updatePreview();
283+
debouncedUpdatePreview();
175284
}
176285
177286
function copyUrl() {
178287
const urlInput = document.getElementById('url');
179288
urlInput.select();
180289
document.execCommand('copy');
290+
291+
const btn = document.querySelector('.copy-btn');
292+
const originalText = btn.textContent;
293+
btn.textContent = 'Copied!';
294+
setTimeout(() => {
295+
btn.textContent = originalText;
296+
}, 2000);
181297
}
182298
183-
// Add event listeners to all inputs
299+
// Add event listeners to all inputs with debounce
184300
document.querySelectorAll('input, select').forEach(input => {
185-
input.addEventListener('input', updatePreview);
301+
input.addEventListener('input', debouncedUpdatePreview);
186302
});
187303
188304
// Initial preview
189-
updatePreview();
305+
debouncedUpdatePreview();
190306
</script>
191307
</body>
192308
</html>

wrangler.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,3 @@ binding = "AI"
3434
enabled = true
3535

3636

37-

0 commit comments

Comments
 (0)