tangled
alpha
login
or
join now
tom.sherman.is
/
piper
0
fork
atom
A fork of https://github.com/teal-fm/piper
0
fork
atom
overview
issues
pulls
pipelines
convert to tailwindcss styles
baileytownsend.dev
5 months ago
18dc3b60
35a1992c
+291
-303
7 changed files
expand all
collapse all
unified
split
pages
static
base.css
main.css
templates
apiKeys.gohtml
components
navBar.gohtml
home.gohtml
lastFMForm.gohtml
layouts
base.gohtml
-125
pages/static/base.css
···
1
1
@import "tailwindcss";
2
2
-
3
3
-
body {
4
4
-
font-family: Arial, sans-serif;
5
5
-
max-width: 800px;
6
6
-
margin: 0 auto;
7
7
-
padding: 20px;
8
8
-
line-height: 1.6;
9
9
-
}
10
10
-
11
11
-
12
12
-
h1 {
13
13
-
color: #1DB954; /* Spotify green */
14
14
-
}
15
15
-
16
16
-
.nav {
17
17
-
display: flex;
18
18
-
flex-wrap: wrap; /* Allow wrapping on smaller screens */
19
19
-
margin-bottom: 20px;
20
20
-
}
21
21
-
22
22
-
.nav a {
23
23
-
margin-right: 15px;
24
24
-
margin-bottom: 5px; /* Add spacing below links */
25
25
-
text-decoration: none;
26
26
-
color: #1DB954;
27
27
-
font-weight: bold;
28
28
-
}
29
29
-
30
30
-
.card {
31
31
-
border: 1px solid #ddd;
32
32
-
border-radius: 8px;
33
33
-
padding: 20px;
34
34
-
margin-bottom: 20px;
35
35
-
}
36
36
-
37
37
-
.service-status {
38
38
-
font-style: italic;
39
39
-
color: #555;
40
40
-
}
41
41
-
42
42
-
43
43
-
label, input {
44
44
-
display: block;
45
45
-
margin-bottom: 10px;
46
46
-
}
47
47
-
48
48
-
input[type='text'] {
49
49
-
width: 95%;
50
50
-
padding: 8px;
51
51
-
}
52
52
-
53
53
-
/* Corrected width */
54
54
-
input[type='submit'] {
55
55
-
padding: 10px 15px;
56
56
-
color: white;
57
57
-
border: none;
58
58
-
border-radius: 4px;
59
59
-
cursor: pointer;
60
60
-
}
61
61
-
62
62
-
.last-fm-input {
63
63
-
background-color: #d51007;
64
64
-
}
65
65
-
66
66
-
.teal-input {
67
67
-
background-color: #1DB954;
68
68
-
}
69
69
-
70
70
-
.error {
71
71
-
color: red;
72
72
-
margin-bottom: 10px;
73
73
-
}
74
74
-
75
75
-
.lastfm-form {
76
76
-
max-width: 600px;
77
77
-
margin: 20px auto;
78
78
-
padding: 20px;
79
79
-
border: 1px solid #ddd;
80
80
-
border-radius: 8px;
81
81
-
}
82
82
-
83
83
-
.card {
84
84
-
border: 1px solid #ddd;
85
85
-
border-radius: 8px;
86
86
-
padding: 20px;
87
87
-
margin-bottom: 20px;
88
88
-
}
89
89
-
table {
90
90
-
width: 100%;
91
91
-
border-collapse: collapse;
92
92
-
}
93
93
-
table th, table td {
94
94
-
padding: 8px;
95
95
-
text-align: left;
96
96
-
border-bottom: 1px solid #ddd;
97
97
-
}
98
98
-
.key-value {
99
99
-
font-family: monospace;
100
100
-
padding: 10px;
101
101
-
background-color: #f5f5f5;
102
102
-
border: 1px solid #ddd;
103
103
-
border-radius: 4px;
104
104
-
word-break: break-all;
105
105
-
}
106
106
-
.new-key-alert {
107
107
-
background-color: #f8f9fa;
108
108
-
border-left: 4px solid #1DB954;
109
109
-
padding: 15px;
110
110
-
margin-bottom: 20px;
111
111
-
}
112
112
-
.btn {
113
113
-
padding: 8px 16px;
114
114
-
background-color: #1DB954;
115
115
-
color: white;
116
116
-
border: none;
117
117
-
border-radius: 4px;
118
118
-
cursor: pointer;
119
119
-
}
120
120
-
.btn-danger {
121
121
-
background-color: #dc3545;
122
122
-
}
123
123
-
124
124
-
.teal-header {
125
125
-
color: #1DB954;
126
126
-
}
+220
-107
pages/static/main.css
···
7
7
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
8
8
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
9
9
"Courier New", monospace;
10
10
+
--color-gray-100: oklch(96.7% 0.003 264.542);
11
11
+
--color-gray-200: oklch(92.8% 0.006 264.531);
12
12
+
--color-gray-300: oklch(87.2% 0.01 258.338);
13
13
+
--color-gray-600: oklch(44.6% 0.03 256.802);
14
14
+
--color-white: #fff;
15
15
+
--spacing: 0.25rem;
16
16
+
--text-lg: 1.125rem;
17
17
+
--text-lg--line-height: calc(1.75 / 1.125);
18
18
+
--text-xl: 1.25rem;
19
19
+
--text-xl--line-height: calc(1.75 / 1.25);
20
20
+
--font-weight-semibold: 600;
21
21
+
--font-weight-bold: 700;
22
22
+
--leading-relaxed: 1.625;
23
23
+
--radius-lg: 0.5rem;
10
24
--default-font-family: var(--font-sans);
11
25
--default-mono-font-family: var(--font-mono);
12
26
}
···
190
204
max-width: 96rem;
191
205
}
192
206
}
207
207
+
.mx-auto {
208
208
+
margin-inline: auto;
209
209
+
}
210
210
+
.my-5 {
211
211
+
margin-block: calc(var(--spacing) * 5);
212
212
+
}
213
213
+
.mt-1 {
214
214
+
margin-top: calc(var(--spacing) * 1);
215
215
+
}
216
216
+
.mt-3 {
217
217
+
margin-top: calc(var(--spacing) * 3);
218
218
+
}
219
219
+
.mb-1 {
220
220
+
margin-bottom: calc(var(--spacing) * 1);
221
221
+
}
222
222
+
.mb-2 {
223
223
+
margin-bottom: calc(var(--spacing) * 2);
224
224
+
}
225
225
+
.mb-3 {
226
226
+
margin-bottom: calc(var(--spacing) * 3);
227
227
+
}
228
228
+
.mb-4 {
229
229
+
margin-bottom: calc(var(--spacing) * 4);
230
230
+
}
231
231
+
.mb-5 {
232
232
+
margin-bottom: calc(var(--spacing) * 5);
233
233
+
}
193
234
.block {
194
235
display: block;
195
236
}
196
237
.contents {
197
238
display: contents;
198
239
}
240
240
+
.flex {
241
241
+
display: flex;
242
242
+
}
199
243
.hidden {
200
244
display: none;
201
245
}
202
246
.table {
203
247
display: table;
204
248
}
249
249
+
.w-\[95\%\] {
250
250
+
width: 95%;
251
251
+
}
252
252
+
.w-full {
253
253
+
width: 100%;
254
254
+
}
255
255
+
.max-w-\[600px\] {
256
256
+
max-width: 600px;
257
257
+
}
258
258
+
.max-w-\[800px\] {
259
259
+
max-width: 800px;
260
260
+
}
261
261
+
.border-collapse {
262
262
+
border-collapse: collapse;
263
263
+
}
205
264
.transform {
206
265
transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
207
266
}
267
267
+
.cursor-pointer {
268
268
+
cursor: pointer;
269
269
+
}
270
270
+
.list-disc {
271
271
+
list-style-type: disc;
272
272
+
}
273
273
+
.flex-wrap {
274
274
+
flex-wrap: wrap;
275
275
+
}
276
276
+
.space-y-2 {
277
277
+
:where(& > :not(:last-child)) {
278
278
+
--tw-space-y-reverse: 0;
279
279
+
margin-block-start: calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));
280
280
+
margin-block-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)));
281
281
+
}
282
282
+
}
283
283
+
.gap-x-4 {
284
284
+
column-gap: calc(var(--spacing) * 4);
285
285
+
}
286
286
+
.gap-y-1 {
287
287
+
row-gap: calc(var(--spacing) * 1);
288
288
+
}
289
289
+
.rounded {
290
290
+
border-radius: 0.25rem;
291
291
+
}
292
292
+
.rounded-lg {
293
293
+
border-radius: var(--radius-lg);
294
294
+
}
295
295
+
.border {
296
296
+
border-style: var(--tw-border-style);
297
297
+
border-width: 1px;
298
298
+
}
299
299
+
.border-b {
300
300
+
border-bottom-style: var(--tw-border-style);
301
301
+
border-bottom-width: 1px;
302
302
+
}
303
303
+
.border-l-4 {
304
304
+
border-left-style: var(--tw-border-style);
305
305
+
border-left-width: 4px;
306
306
+
}
307
307
+
.border-\[\#1DB954\] {
308
308
+
border-color: #1DB954;
309
309
+
}
310
310
+
.border-gray-200 {
311
311
+
border-color: var(--color-gray-200);
312
312
+
}
313
313
+
.border-gray-300 {
314
314
+
border-color: var(--color-gray-300);
315
315
+
}
316
316
+
.bg-\[\#1DB954\] {
317
317
+
background-color: #1DB954;
318
318
+
}
319
319
+
.bg-\[\#d51007\] {
320
320
+
background-color: #d51007;
321
321
+
}
322
322
+
.bg-\[\#dc3545\] {
323
323
+
background-color: #dc3545;
324
324
+
}
325
325
+
.bg-gray-100 {
326
326
+
background-color: var(--color-gray-100);
327
327
+
}
328
328
+
.p-2 {
329
329
+
padding: calc(var(--spacing) * 2);
330
330
+
}
331
331
+
.p-4 {
332
332
+
padding: calc(var(--spacing) * 4);
333
333
+
}
334
334
+
.p-5 {
335
335
+
padding: calc(var(--spacing) * 5);
336
336
+
}
337
337
+
.px-3 {
338
338
+
padding-inline: calc(var(--spacing) * 3);
339
339
+
}
340
340
+
.px-4 {
341
341
+
padding-inline: calc(var(--spacing) * 4);
342
342
+
}
343
343
+
.py-1\.5 {
344
344
+
padding-block: calc(var(--spacing) * 1.5);
345
345
+
}
346
346
+
.py-2 {
347
347
+
padding-block: calc(var(--spacing) * 2);
348
348
+
}
349
349
+
.py-2\.5 {
350
350
+
padding-block: calc(var(--spacing) * 2.5);
351
351
+
}
352
352
+
.pl-5 {
353
353
+
padding-left: calc(var(--spacing) * 5);
354
354
+
}
355
355
+
.text-left {
356
356
+
text-align: left;
357
357
+
}
358
358
+
.font-mono {
359
359
+
font-family: var(--font-mono);
360
360
+
}
361
361
+
.font-sans {
362
362
+
font-family: var(--font-sans);
363
363
+
}
364
364
+
.text-lg {
365
365
+
font-size: var(--text-lg);
366
366
+
line-height: var(--tw-leading, var(--text-lg--line-height));
367
367
+
}
368
368
+
.text-xl {
369
369
+
font-size: var(--text-xl);
370
370
+
line-height: var(--tw-leading, var(--text-xl--line-height));
371
371
+
}
372
372
+
.leading-relaxed {
373
373
+
--tw-leading: var(--leading-relaxed);
374
374
+
line-height: var(--leading-relaxed);
375
375
+
}
376
376
+
.font-bold {
377
377
+
--tw-font-weight: var(--font-weight-bold);
378
378
+
font-weight: var(--font-weight-bold);
379
379
+
}
380
380
+
.font-semibold {
381
381
+
--tw-font-weight: var(--font-weight-semibold);
382
382
+
font-weight: var(--font-weight-semibold);
383
383
+
}
384
384
+
.text-\[\#1DB954\] {
385
385
+
color: #1DB954;
386
386
+
}
387
387
+
.text-gray-600 {
388
388
+
color: var(--color-gray-600);
389
389
+
}
390
390
+
.text-white {
391
391
+
color: var(--color-white);
392
392
+
}
208
393
.lowercase {
209
394
text-transform: lowercase;
210
395
}
396
396
+
.italic {
397
397
+
font-style: italic;
398
398
+
}
399
399
+
.no-underline {
400
400
+
text-decoration-line: none;
401
401
+
}
211
402
.filter {
212
403
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
213
404
}
214
214
-
}
215
215
-
body {
216
216
-
font-family: Arial, sans-serif;
217
217
-
max-width: 800px;
218
218
-
margin: 0 auto;
219
219
-
padding: 20px;
220
220
-
line-height: 1.6;
221
221
-
}
222
222
-
h1 {
223
223
-
color: #1DB954;
224
224
-
}
225
225
-
.nav {
226
226
-
display: flex;
227
227
-
flex-wrap: wrap;
228
228
-
margin-bottom: 20px;
229
229
-
}
230
230
-
.nav a {
231
231
-
margin-right: 15px;
232
232
-
margin-bottom: 5px;
233
233
-
text-decoration: none;
234
234
-
color: #1DB954;
235
235
-
font-weight: bold;
236
236
-
}
237
237
-
.card {
238
238
-
border: 1px solid #ddd;
239
239
-
border-radius: 8px;
240
240
-
padding: 20px;
241
241
-
margin-bottom: 20px;
242
242
-
}
243
243
-
.service-status {
244
244
-
font-style: italic;
245
245
-
color: #555;
246
246
-
}
247
247
-
label, input {
248
248
-
display: block;
249
249
-
margin-bottom: 10px;
250
250
-
}
251
251
-
input[type='text'] {
252
252
-
width: 95%;
253
253
-
padding: 8px;
254
254
-
}
255
255
-
input[type='submit'] {
256
256
-
padding: 10px 15px;
257
257
-
color: white;
258
258
-
border: none;
259
259
-
border-radius: 4px;
260
260
-
cursor: pointer;
261
261
-
}
262
262
-
.last-fm-input {
263
263
-
background-color: #d51007;
264
264
-
}
265
265
-
.teal-input {
266
266
-
background-color: #1DB954;
267
267
-
}
268
268
-
.error {
269
269
-
color: red;
270
270
-
margin-bottom: 10px;
271
271
-
}
272
272
-
.lastfm-form {
273
273
-
max-width: 600px;
274
274
-
margin: 20px auto;
275
275
-
padding: 20px;
276
276
-
border: 1px solid #ddd;
277
277
-
border-radius: 8px;
278
278
-
}
279
279
-
.card {
280
280
-
border: 1px solid #ddd;
281
281
-
border-radius: 8px;
282
282
-
padding: 20px;
283
283
-
margin-bottom: 20px;
284
284
-
}
285
285
-
table {
286
286
-
width: 100%;
287
287
-
border-collapse: collapse;
288
288
-
}
289
289
-
table th, table td {
290
290
-
padding: 8px;
291
291
-
text-align: left;
292
292
-
border-bottom: 1px solid #ddd;
293
293
-
}
294
294
-
.key-value {
295
295
-
font-family: monospace;
296
296
-
padding: 10px;
297
297
-
background-color: #f5f5f5;
298
298
-
border: 1px solid #ddd;
299
299
-
border-radius: 4px;
300
300
-
word-break: break-all;
301
301
-
}
302
302
-
.new-key-alert {
303
303
-
background-color: #f8f9fa;
304
304
-
border-left: 4px solid #1DB954;
305
305
-
padding: 15px;
306
306
-
margin-bottom: 20px;
307
307
-
}
308
308
-
.btn {
309
309
-
padding: 8px 16px;
310
310
-
background-color: #1DB954;
311
311
-
color: white;
312
312
-
border: none;
313
313
-
border-radius: 4px;
314
314
-
cursor: pointer;
315
315
-
}
316
316
-
.btn-danger {
317
317
-
background-color: #dc3545;
318
318
-
}
319
319
-
.teal-header {
320
320
-
color: #1DB954;
405
405
+
.hover\:opacity-90 {
406
406
+
&:hover {
407
407
+
@media (hover: hover) {
408
408
+
opacity: 90%;
409
409
+
}
410
410
+
}
411
411
+
}
321
412
}
322
413
@property --tw-rotate-x {
323
414
syntax: "*";
···
336
427
inherits: false;
337
428
}
338
429
@property --tw-skew-y {
430
430
+
syntax: "*";
431
431
+
inherits: false;
432
432
+
}
433
433
+
@property --tw-space-y-reverse {
434
434
+
syntax: "*";
435
435
+
inherits: false;
436
436
+
initial-value: 0;
437
437
+
}
438
438
+
@property --tw-border-style {
439
439
+
syntax: "*";
440
440
+
inherits: false;
441
441
+
initial-value: solid;
442
442
+
}
443
443
+
@property --tw-leading {
444
444
+
syntax: "*";
445
445
+
inherits: false;
446
446
+
}
447
447
+
@property --tw-font-weight {
339
448
syntax: "*";
340
449
inherits: false;
341
450
}
···
400
509
--tw-rotate-z: initial;
401
510
--tw-skew-x: initial;
402
511
--tw-skew-y: initial;
512
512
+
--tw-space-y-reverse: 0;
513
513
+
--tw-border-style: solid;
514
514
+
--tw-leading: initial;
515
515
+
--tw-font-weight: initial;
403
516
--tw-blur: initial;
404
517
--tw-brightness: initial;
405
518
--tw-contrast: initial;
+32
-32
pages/templates/apiKeys.gohtml
···
4
4
{{ template "components/navBar" .NavBar }}
5
5
6
6
7
7
-
<h1>API Key Management</h1>
7
7
+
<h1 class="text-[#1DB954]">API Key Management</h1>
8
8
9
9
-
<div class="card">
10
10
-
<h2 class="teal-header">Create New API Key</h2>
11
11
-
<p>API keys allow programmatic access to your Piper account data.</p>
9
9
+
<div class="border border-gray-300 rounded-lg p-5 mb-5">
10
10
+
<h2 class="text-[#1DB954] text-xl font-semibold mb-2">Create New API Key</h2>
11
11
+
<p class="mb-3">API keys allow programmatic access to your Piper account data.</p>
12
12
<form method="POST" action="/api-keys">
13
13
-
<div style="margin-bottom: 15px;">
14
14
-
<label for="name">Key Name (for your reference):</label>
15
15
-
<input type="text" id="name" name="name" placeholder="My Application" style="width: 100%; padding: 8px; margin-top: 5px;">
13
13
+
<div class="mb-4">
14
14
+
<label class="block" for="name">Key Name (for your reference):</label>
15
15
+
<input class="mt-1 w-full p-2 border border-gray-300 rounded" type="text" id="name" name="name" placeholder="My Application">
16
16
</div>
17
17
-
<button type="submit" class="btn">Generate New API Key</button>
17
17
+
<button type="submit" class="bg-[#1DB954] text-white px-4 py-2 rounded cursor-pointer hover:opacity-90">Generate New API Key</button>
18
18
</form>
19
19
</div>
20
20
21
21
{{if .NewKeyID}} <!-- Changed from .NewKey to .NewKeyID for clarity -->
22
22
-
<div class="new-key-alert">
23
23
-
<h3 class="teal-header">Your new API key (ID: {{.NewKeyID}}) has been created</h3>
22
22
+
<div class="bg-gray-100 border-l-4 border-[#1DB954] p-4 mb-5">
23
23
+
<h3 class="text-[#1DB954] text-lg font-semibold mb-1">Your new API key (ID: {{.NewKeyID}}) has been created</h3>
24
24
<!-- The message below is misleading if only the ID is shown.
25
25
Consider changing this text or modifying the flow to show the actual key once for HTML. -->
26
26
<p><strong>Important:</strong> If this is an ID, ensure you have copied the actual key if it was displayed previously. For keys generated via the API, the key is returned in the API response.</p>
27
27
</div>
28
28
{{end}}
29
29
30
30
-
<div class="card">
31
31
-
<h2 class="teal-header">Your API Keys</h2>
30
30
+
<div class="border border-gray-300 rounded-lg p-5 mb-5">
31
31
+
<h2 class="text-[#1DB954] text-xl font-semibold mb-2">Your API Keys</h2>
32
32
{{if .Keys}}
33
33
-
<table>
33
33
+
<table class="w-full border-collapse">
34
34
<thead>
35
35
-
<tr>
36
36
-
<th>Name</th>
37
37
-
<th>Prefix</th>
38
38
-
<th>Created</th>
39
39
-
<th>Expires</th>
40
40
-
<th>Actions</th>
35
35
+
<tr class="text-left border-b border-gray-300">
36
36
+
<th class="p-2">Name</th>
37
37
+
<th class="p-2">Prefix</th>
38
38
+
<th class="p-2">Created</th>
39
39
+
<th class="p-2">Expires</th>
40
40
+
<th class="p-2">Actions</th>
41
41
</tr>
42
42
</thead>
43
43
<tbody>
44
44
{{range .Keys}}
45
45
-
<tr>
46
46
-
<td>{{.Name}}</td>
47
47
-
<td>{{.KeyPrefix}}</td> <!-- Added KeyPrefix for better identification -->
48
48
-
<td>{{formatTime .CreatedAt}}</td>
49
49
-
<td>{{formatTime .ExpiresAt}}</td>
50
50
-
<td>
51
51
-
<button class="btn btn-danger" onclick="deleteKey('{{.ID}}')">Delete</button>
45
45
+
<tr class="border-b border-gray-200">
46
46
+
<td class="p-2">{{.Name}}</td>
47
47
+
<td class="p-2">{{.KeyPrefix}}</td> <!-- Added KeyPrefix for better identification -->
48
48
+
<td class="p-2">{{formatTime .CreatedAt}}</td>
49
49
+
<td class="p-2">{{formatTime .ExpiresAt}}</td>
50
50
+
<td class="p-2">
51
51
+
<button class="bg-[#dc3545] text-white px-3 py-1.5 rounded cursor-pointer hover:opacity-90" onclick="deleteKey('{{.ID}}')">Delete</button>
52
52
</td>
53
53
</tr>
54
54
{{end}}
···
59
59
{{end}}
60
60
</div>
61
61
62
62
-
<div class="card">
63
63
-
<h2 class="teal-header">API Usage</h2>
64
64
-
<p>To use your API key, include it in the Authorization header of your HTTP requests:</p>
65
65
-
<pre>Authorization: Bearer YOUR_API_KEY</pre>
66
66
-
<p>Or include it as a query parameter (less secure for the key itself):</p>
67
67
-
<pre>https://your-piper-instance.com/endpoint?api_key=YOUR_API_KEY</pre>
62
62
+
<div class="border border-gray-300 rounded-lg p-5 mb-5">
63
63
+
<h2 class="text-[#1DB954] text-xl font-semibold mb-2">API Usage</h2>
64
64
+
<p class="mb-2">To use your API key, include it in the Authorization header of your HTTP requests:</p>
65
65
+
<pre class="font-mono p-2 bg-gray-100 border border-gray-300 rounded">Authorization: Bearer YOUR_API_KEY</pre>
66
66
+
<p class="mt-3 mb-2">Or include it as a query parameter (less secure for the key itself):</p>
67
67
+
<pre class="font-mono p-2 bg-gray-100 border border-gray-300 rounded">https://your-piper-instance.com/endpoint?api_key=YOUR_API_KEY</pre>
68
68
</div>
69
69
70
70
<script>
+11
-11
pages/templates/components/navBar.gohtml
···
1
1
{{ define "components/navBar" }}
2
2
3
3
-
<div class="nav">
4
4
-
<a href="/">Home</a>
3
3
+
<nav class="flex flex-wrap mb-5 gap-x-4 gap-y-1">
4
4
+
<a class="text-[#1DB954] font-bold no-underline" href="/">Home</a>
5
5
6
6
{{if .IsLoggedIn}}
7
7
-
<a href="/current-track">Spotify Current</a>
8
8
-
<a href="/history">Spotify History</a>
9
9
-
<a href="/link-lastfm">Link Last.fm</a>
7
7
+
<a class="text-[#1DB954] font-bold no-underline" href="/current-track">Spotify Current</a>
8
8
+
<a class="text-[#1DB954] font-bold no-underline" href="/history">Spotify History</a>
9
9
+
<a class="text-[#1DB954] font-bold no-underline" href="/link-lastfm">Link Last.fm</a>
10
10
{{ if .LastFMUsername }}
11
11
-
<a href="/lastfm/recent">Last.fm Recent</a>
11
11
+
<a class="text-[#1DB954] font-bold no-underline" href="/lastfm/recent">Last.fm Recent</a>
12
12
{{ end }}
13
13
-
<a href="/api-keys">API Keys</a>
14
14
-
<a href="/login/spotify">Connect Spotify Account</a>
15
15
-
<a href="/logout">Logout</a>
13
13
+
<a class="text-[#1DB954] font-bold no-underline" href="/api-keys">API Keys</a>
14
14
+
<a class="text-[#1DB954] font-bold no-underline" href="/login/spotify">Connect Spotify Account</a>
15
15
+
<a class="text-[#1DB954] font-bold no-underline" href="/logout">Logout</a>
16
16
{{ else }}
17
17
-
<a href="/login/atproto">Login with ATProto</a>
17
17
+
<a class="text-[#1DB954] font-bold no-underline" href="/login/atproto">Login with ATProto</a>
18
18
{{ end }}
19
19
-
</div>
19
19
+
</nav>
20
20
{{ end }}
+20
-20
pages/templates/home.gohtml
···
1
1
2
2
{{ define "content" }}
3
3
4
4
-
<h1>Piper - Multi-User Spotify & Last.fm Tracker via ATProto</h1>
4
4
+
<h1 class="text-[#1DB954]">Piper - Multi-User Spotify & Last.fm Tracker via ATProto</h1>
5
5
{{ template "components/navBar" .NavBar }}
6
6
7
7
8
8
-
<div class="card">
9
9
-
<h2 class="">Welcome to Piper</h2>
10
10
-
<p>Piper is a multi-user application that records what you're listening to on Spotify and Last.fm, saving your listening history.</p>
8
8
+
<div class="border border-gray-300 rounded-lg p-5 mb-5">
9
9
+
<h2 class="text-xl font-semibold mb-2">Welcome to Piper</h2>
10
10
+
<p class="mb-3">Piper is a multi-user application that records what you're listening to on Spotify and Last.fm, saving your listening history.</p>
11
11
12
12
{{if .NavBar.IsLoggedIn}}
13
13
-
<p>You're logged in!</p>
14
14
-
<ul>
15
15
-
<li><a href="/login/spotify">Connect your Spotify account</a> to start tracking.</li>
16
16
-
<li><a href="/link-lastfm">Link your Last.fm account</a> to track scrobbles.</li>
13
13
+
<p class="mb-2">You're logged in!</p>
14
14
+
<ul class="list-disc pl-5 mb-3">
15
15
+
<li><a class="text-[#1DB954] font-bold" href="/login/spotify">Connect your Spotify account</a> to start tracking.</li>
16
16
+
<li><a class="text-[#1DB954] font-bold" href="/link-lastfm">Link your Last.fm account</a> to track scrobbles.</li>
17
17
</ul>
18
18
-
<p>Once connected, you can check out your:</p>
19
19
-
<ul>
20
20
-
<li><a href="/current-track">Spotify current track</a> or <a href="/history">listening history</a>.</li>
18
18
+
<p class="mb-2">Once connected, you can check out your:</p>
19
19
+
<ul class="list-disc pl-5 mb-3">
20
20
+
<li><a class="text-[#1DB954] font-bold" href="/current-track">Spotify current track</a> or <a class="text-[#1DB954] font-bold" href="/history">listening history</a>.</li>
21
21
{{ if .NavBar.LastFMUsername }}
22
22
-
<li><a href="/lastfm/recent">Last.fm recent tracks</a>.</li>
22
22
+
<li><a class="text-[#1DB954] font-bold" href="/lastfm/recent">Last.fm recent tracks</a>.</li>
23
23
{{ end }}
24
24
25
25
</ul>
26
26
-
<p>You can also manage your <a href="/api-keys">API keys</a> for programmatic access.</p>
26
26
+
<p class="mb-3">You can also manage your <a class="text-[#1DB954] font-bold" href="/api-keys">API keys</a> for programmatic access.</p>
27
27
28
28
{{ if .NavBar.LastFMUsername }}
29
29
-
<p class='service-status'>Last.fm Username: {{ .NavBar.LastFMUsername }}</p>
29
29
+
<p class='italic text-gray-600'>Last.fm Username: {{ .NavBar.LastFMUsername }}</p>
30
30
{{else }}
31
31
-
<p class='service-status'>Last.fm account not linked.</p>
31
31
+
<p class='italic text-gray-600'>Last.fm account not linked.</p>
32
32
{{end}}
33
33
34
34
35
35
{{ else }}
36
36
37
37
-
<p>Login with ATProto to get started!</p>
38
38
-
<form action="/login/atproto">
39
39
-
<label for="handle">handle:</label>
40
40
-
<input type="text" id="handle" name="handle" >
41
41
-
<input class="teal-input" type="submit" value="submit">
37
37
+
<p class="mb-3">Login with ATProto to get started!</p>
38
38
+
<form class="space-y-2" action="/login/atproto">
39
39
+
<label class="block" for="handle">handle:</label>
40
40
+
<input class="block w-[95%] p-2 border border-gray-300 rounded" type="text" id="handle" name="handle" >
41
41
+
<input class="bg-[#1DB954] text-white px-4 py-2.5 rounded cursor-pointer hover:opacity-90" type="submit" value="submit">
42
42
</form>
43
43
44
44
+7
-7
pages/templates/lastFMForm.gohtml
···
1
1
{{ define "content" }}
2
2
{{ template "components/navBar" .NavBar }}
3
3
4
4
-
<div class="lastfm-form">
5
5
-
<h2>Link Your Last.fm Account</h2>
6
6
-
<p>Enter your Last.fm username to start tracking your scrobbles.</p>
7
7
-
<form method="post" action="/link-lastfm">
8
8
-
<label for="lastfm_username">Last.fm Username:</label>
9
9
-
<input type="text" id="lastfm_username" name="lastfm_username" value="{{.CurrentUsername}}" required>
10
10
-
<input class="last-fm-input" type="submit" value="Save Username">
4
4
+
<div class="max-w-[600px] mx-auto my-5 p-5 border border-gray-300 rounded-lg">
5
5
+
<h2 class="text-xl font-semibold mb-2">Link Your Last.fm Account</h2>
6
6
+
<p class="mb-3">Enter your Last.fm username to start tracking your scrobbles.</p>
7
7
+
<form class="space-y-2" method="post" action="/link-lastfm">
8
8
+
<label class="block" for="lastfm_username">Last.fm Username:</label>
9
9
+
<input class="block w-[95%] p-2 border border-gray-300 rounded" type="text" id="lastfm_username" name="lastfm_username" value="{{.CurrentUsername}}" required>
10
10
+
<input class="bg-[#d51007] text-white px-4 py-2.5 rounded cursor-pointer hover:opacity-90" type="submit" value="Save Username">
11
11
</form>
12
12
</div>
13
13
+1
-1
pages/templates/layouts/base.gohtml
···
5
5
<title>Piper - Spotify & Last.fm Tracker</title>
6
6
<link rel="stylesheet" href="/static/main.css">
7
7
</head>
8
8
-
<body>
8
8
+
<body class="font-sans max-w-[800px] mx-auto p-5 leading-relaxed">
9
9
{{ block "content" . }}{{ end }}
10
10
11
11
</body>