home to your local SPACEGIRL 💫 arimelody.space
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

admin dashboard early UI refresh

+249 -139
+2 -2
.air.toml
··· 7 7 bin = "./tmp/main" 8 8 cmd = "go build -o ./tmp/main ." 9 9 delay = 1000 10 - exclude_dir = ["admin/static", "admin\\static", "public", "uploads", "test", "db", "res"] 10 + exclude_dir = ["uploads", "test", "db", "res"] 11 11 exclude_file = [] 12 12 exclude_regex = ["_test.go"] 13 13 exclude_unchanged = false 14 14 follow_symlink = false 15 15 full_bin = "" 16 16 include_dir = [] 17 - include_ext = ["go", "tpl", "tmpl", "html"] 17 + include_ext = ["go", "tpl", "tmpl", "html", "css"] 18 18 include_file = [] 19 19 kill_delay = "0s" 20 20 log = "build-errors.log"
+112 -48
admin/static/admin.css
··· 1 1 @import url("/style/prideflag.css"); 2 2 @import url("/font/inter/inter.css"); 3 3 4 + :root { 5 + --bg-0: #101010; 6 + --bg-1: #141414; 7 + --bg-2: #181818; 8 + --bg-3: #202020; 9 + 10 + --fg-0: #b0b0b0; 11 + --fg-1: #c0c0c0; 12 + --fg-2: #d0d0d0; 13 + --fg-3: #e0e0e0; 14 + 15 + --col-shadow-0: #0002; 16 + --col-shadow-1: #0004; 17 + --col-shadow-2: #0006; 18 + --col-highlight-0: #ffffff08; 19 + --col-highlight-1: #fff1; 20 + --col-highlight-2: #fff2; 21 + 22 + --col-new: #b3ee5b; 23 + --col-on-new: #1b2013; 24 + --col-save: #6fd7ff; 25 + --col-on-save: #283f48; 26 + --col-delete: #ff7171; 27 + --col-on-delete: #371919; 28 + 29 + --col-warn: #ffe86a; 30 + --col-on-warn: var(--bg-0); 31 + --col-warn-hover: #ffec81; 32 + 33 + --shadow-sm: 34 + 0 1px 2px var(--col-shadow-2), 35 + inset 0 1px 1px var(--col-highlight-2); 36 + --shadow-md: 37 + 0 2px 4px var(--col-shadow-1), 38 + inset 0 2px 2px var(--col-highlight-1); 39 + --shadow-lg: 40 + 0 4px 8px var(--col-shadow-0), 41 + inset 0 4px 4px var(--col-highlight-0); 42 + } 43 + 44 + @media (prefers-color-scheme: light) { 45 + :root { 46 + --bg-0: #e8e8e8; 47 + --bg-1: #f0f0f0; 48 + --bg-2: #f8f8f8; 49 + --bg-3: #ffffff; 50 + 51 + --fg-0: #606060; 52 + --fg-1: #404040; 53 + --fg-2: #303030; 54 + --fg-3: #202020; 55 + 56 + --col-shadow-0: #0002; 57 + --col-shadow-1: #0004; 58 + --col-shadow-2: #0008; 59 + --col-highlight-0: #fff2; 60 + --col-highlight-1: #fff4; 61 + --col-highlight-2: #fff8; 62 + 63 + --col-warn: #ffe86a; 64 + --col-on-warn: var(--fg-3); 65 + --col-warn-hover: #ffec81; 66 + } 67 + } 68 + 4 69 body { 5 70 width: 100%; 6 71 height: calc(100vh - 1em); ··· 11 76 font-family: "Inter", sans-serif; 12 77 font-size: 16px; 13 78 14 - color: #303030; 15 - background: #f0f0f0; 79 + color: var(--fg-0); 80 + background: var(--bg-0); 81 + } 82 + 83 + h1, h2, h3, h4, h5, h6 { 84 + color: var(--fg-3); 16 85 } 17 86 18 87 nav { ··· 22 91 display: flex; 23 92 flex-direction: row; 24 93 justify-content: left; 25 - 26 - background: #f8f8f8; 27 - border-radius: 4px; 28 - border: 1px solid #808080; 94 + gap: .5em; 95 + user-select: none; 29 96 } 30 97 nav .icon { 31 98 height: 100%; 99 + border-radius: 100%; 100 + box-shadow: var(--shadow-sm); 101 + overflow: hidden; 32 102 } 33 - nav .title { 34 - width: auto; 103 + nav .icon img { 104 + width: 100%; 35 105 height: 100%; 36 - 37 - margin: 0 1em 0 0; 38 - 39 - display: flex; 40 - 41 - line-height: 2em; 42 - text-decoration: none; 43 - 44 - color: inherit; 45 106 } 46 107 .nav-item { 47 108 width: auto; 48 109 height: 100%; 49 - 50 - margin: 0px; 51 110 padding: 0 1em; 52 - 53 111 display: flex; 54 112 113 + color: var(--fg-2); 114 + background: var(--bg-2); 115 + border-radius: 10em; 116 + box-shadow: var(--shadow-sm); 117 + 55 118 line-height: 2em; 119 + font-weight: 500; 56 120 } 57 121 .nav-item:hover { 58 - background: #00000010; 122 + background: var(--bg-1); 59 123 text-decoration: none; 60 124 } 61 125 nav a { ··· 77 141 text-decoration: none; 78 142 } 79 143 144 + /* 80 145 a:hover { 81 146 text-decoration: underline; 82 147 } 148 + */ 83 149 84 150 a img.icon { 85 151 height: .8em; ··· 133 199 #error { 134 200 margin: 0 0 1em 0; 135 201 padding: 1em; 136 - border-radius: 4px; 202 + border-radius: 8px; 137 203 background: #ffffff; 138 - border: 1px solid #888; 139 204 } 140 205 #message { 141 206 background: #a9dfff; 142 - border-color: #599fdc; 143 207 } 144 208 #error { 145 209 background: #ffa9b8; 146 - border-color: #dc5959; 147 210 } 148 211 149 212 ··· 152 215 color: #d22828; 153 216 } 154 217 155 - button, .button { 218 + .button, button { 156 219 padding: .5em .8em; 157 220 font-family: inherit; 158 221 font-size: inherit; 159 - border-radius: 4px; 160 - border: 1px solid #a0a0a0; 161 - background: #f0f0f0; 222 + 162 223 color: inherit; 224 + background: var(--bg-2); 225 + border: none; 226 + border-radius: 10em; 227 + box-shadow: var(--shadow-sm); 228 + font-weight: 500; 229 + transition: background .1s ease-out, color .1s ease-out; 230 + 231 + cursor: pointer; 232 + user-select: none; 163 233 } 164 234 button:hover, .button:hover { 165 235 background: #fff; 166 - border-color: #d0d0d0; 167 236 } 168 237 button:active, .button:active { 169 238 background: #d0d0d0; 170 - border-color: #808080; 171 239 } 172 240 173 - .button, button { 174 - color: inherit; 175 - } 176 241 .button.new, button.new { 177 - background: #c4ff6a; 178 - border-color: #84b141; 242 + color: var(--col-on-new); 243 + background: var(--col-new); 179 244 } 180 245 .button.save, button.save { 181 - background: #6fd7ff; 182 - border-color: #6f9eb0; 246 + color: var(--col-on-save); 247 + background: var(--col-save); 183 248 } 184 249 .button.delete, button.delete { 185 - background: #ff7171; 186 - border-color: #7d3535; 250 + color: var(--col-on-delete); 251 + background: var(--col-delete); 187 252 } 188 253 .button:hover, button:hover { 189 - background: #fff; 190 - border-color: #d0d0d0; 254 + color: var(--bg-3); 255 + background: var(--fg-3); 191 256 } 192 257 .button:active, button:active { 193 - background: #d0d0d0; 194 - border-color: #808080; 258 + color: var(--bg-2); 259 + background: var(--fg-0); 195 260 } 196 261 .button[disabled], button[disabled] { 197 - background: #d0d0d0 !important; 198 - border-color: #808080 !important; 262 + color: var(--fg-0) !important; 263 + background: var(--bg-3) !important; 199 264 opacity: .5; 200 - cursor: not-allowed !important; 265 + cursor: default !important; 201 266 } 202 267 203 268 ··· 217 282 padding: .3rem .5rem; 218 283 display: block; 219 284 border-radius: 4px; 220 - border: 1px solid #808080; 221 285 font-size: inherit; 222 286 font-family: inherit; 223 287 color: inherit;
+17
admin/static/admin.js
··· 69 69 if (callback) callback(); 70 70 }); 71 71 } 72 + 73 + export function hijackClickEvent(container, link) { 74 + container.addEventListener('click', event => { 75 + if (event.target.tagName.toLowerCase() === 'a') return; 76 + event.preventDefault(); 77 + link.dispatchEvent(new MouseEvent('click', { 78 + bubbles: true, 79 + cancelable: true, 80 + view: window, 81 + ctrlKey: event.ctrlKey, 82 + metaKey: event.metaKey, 83 + shiftKey: event.shiftKey, 84 + altKey: event.altKey, 85 + button: event.button, 86 + })); 87 + }); 88 + }
+10 -8
admin/static/edit-artist.css
··· 9 9 flex-direction: row; 10 10 gap: 1.2em; 11 11 12 - border-radius: 8px; 13 - background: #f8f8f8f8; 14 - border: 1px solid #808080; 12 + border-radius: 16px; 13 + background: var(--bg-2); 14 + box-shadow: var(--shadow-md); 15 15 } 16 16 17 17 .artist-avatar { ··· 27 27 cursor: pointer; 28 28 } 29 29 .artist-avatar #remove-avatar { 30 + margin-top: .5em; 30 31 padding: .3em .4em; 31 32 } 32 33 ··· 53 54 font-family: inherit; 54 55 font-weight: inherit; 55 56 color: inherit; 56 - background: #ffffff; 57 - border: 1px solid transparent; 57 + background: var(--bg-0); 58 + border: none; 58 59 border-radius: 4px; 59 60 outline: none; 60 61 } ··· 85 86 flex-direction: row; 86 87 gap: 1em; 87 88 align-items: center; 88 - background: #f8f8f8; 89 - border-radius: 8px; 90 - border: 1px solid #808080; 89 + 90 + border-radius: 16px; 91 + background: var(--bg-2); 92 + box-shadow: var(--shadow-md); 91 93 } 92 94 93 95 .release-artwork {
+43 -24
admin/static/edit-release.css
··· 12 12 gap: 1.2em; 13 13 14 14 border-radius: 8px; 15 - background: #f8f8f8f8; 16 - border: 1px solid #808080; 15 + background: var(--bg-2); 16 + box-shadow: var(--shadow-md); 17 17 } 18 18 19 19 .release-artwork { ··· 29 29 cursor: pointer; 30 30 } 31 31 .release-artwork #remove-artwork { 32 - padding: .3em .4em; 32 + margin-top: .5em; 33 + padding: .3em .6em; 33 34 } 34 35 35 36 .release-info { ··· 54 55 background: transparent; 55 56 outline: none; 56 57 cursor: pointer; 58 + transition: background .1s ease-out, border-color .1s ease-out; 57 59 } 58 60 59 61 #title:hover { 60 - background: #ffffff; 61 - border-color: #80808080; 62 + background: var(--bg-3); 63 + border-color: var(--fg-0); 62 64 } 63 65 64 66 #title:active, 65 67 #title:focus { 66 - background: #ffffff; 67 - border-color: #808080; 68 + background: var(--bg-3); 68 69 } 69 70 70 71 .release-title small { ··· 75 76 width: 100%; 76 77 margin: .5em 0; 77 78 border-collapse: collapse; 79 + color: var(--fg-2); 78 80 } 79 81 .release-info table td { 80 82 padding: .2em; 81 - border-bottom: 1px solid #d0d0d0; 83 + border-bottom: 1px solid color-mix(in srgb, var(--fg-0) 25%, transparent); 84 + transition: background .1s ease-out, border-color .1s ease-out; 82 85 } 83 86 .release-info table tr td:first-child { 84 87 vertical-align: top; 85 - opacity: .66; 88 + opacity: .5; 86 89 } 87 90 .release-info table tr td:not(:first-child) select:hover, 88 91 .release-info table tr td:not(:first-child) input:hover, 89 92 .release-info table tr td:not(:first-child) textarea:hover { 90 - background: #e8e8e8; 93 + background: var(--bg-3); 91 94 cursor: pointer; 92 95 } 93 96 .release-info table td select, ··· 117 120 justify-content: right; 118 121 } 119 122 123 + .release-actions button, 124 + .release-actions .button { 125 + color: var(--fg-2); 126 + background: var(--bg-3); 127 + } 128 + 120 129 dialog { 121 130 width: min(720px, calc(100% - 2em)); 122 131 padding: 2em; 123 132 border: 1px solid #101010; 124 - border-radius: 8px; 133 + border-radius: 16px; 134 + color: var(--fg-0); 135 + background: var(--bg-0); 125 136 } 126 137 127 138 dialog header { ··· 160 171 align-items: center; 161 172 gap: 1em; 162 173 163 - border-radius: 8px; 164 - background: #f8f8f8f8; 165 - border: 1px solid #808080; 174 + border-radius: 16px; 175 + background: var(--bg-2); 176 + box-shadow: var(--shadow-md); 166 177 } 167 178 168 179 .card.credits .credit p { ··· 170 181 } 171 182 172 183 .card.credits .credit .artist-avatar { 173 - border-radius: 8px; 184 + border-radius: 12px; 174 185 } 175 186 176 187 .card.credits .credit .artist-name { 188 + color: var(--fg-3); 177 189 font-weight: bold; 178 190 } 179 191 ··· 197 209 gap: 1em; 198 210 199 211 border-radius: 8px; 200 - background: #f8f8f8f8; 201 - border: 1px solid #808080; 212 + background: var(--bg-2); 213 + box-shadow: var(--shadow-md); 202 214 } 203 215 204 216 #editcredits .credit { ··· 232 244 margin: 0; 233 245 display: flex; 234 246 align-items: center; 247 + color: inherit; 235 248 } 236 249 237 250 #editcredits .credit .credit-info .credit-attribute input[type="text"] { ··· 239 252 padding: .2em .4em; 240 253 flex-grow: 1; 241 254 font-family: inherit; 242 - border: 1px solid #8888; 255 + border: none; 243 256 border-radius: 4px; 244 - color: inherit; 257 + color: var(--fg-2); 258 + background: var(--bg-0); 245 259 } 246 260 #editcredits .credit .credit-info .credit-attribute input[type="checkbox"] { 247 261 margin: 0 .3em; 248 262 } 249 263 250 264 #editcredits .credit .artist-name { 265 + color: var(--fg-2); 251 266 font-weight: bold; 252 267 } 253 268 ··· 256 271 opacity: .66; 257 272 } 258 273 259 - #editcredits .credit button.delete { 260 - margin-left: auto; 274 + #editcredits .credit .delete { 275 + margin-right: .5em; 276 + cursor: pointer; 277 + } 278 + #editcredits .credit .delete:hover { 279 + text-decoration: underline; 261 280 } 262 281 263 282 #addcredit ul { ··· 400 419 flex-direction: column; 401 420 gap: .5em; 402 421 403 - border-radius: 8px; 404 - background: #f8f8f8f8; 405 - border: 1px solid #808080; 422 + border-radius: 16px; 423 + background: var(--bg-2); 424 + box-shadow: var(--shadow-md); 406 425 } 407 426 408 427 .card.tracks .track h3,
+5 -4
admin/static/edit-track.css
··· 11 11 flex-direction: row; 12 12 gap: 1.2em; 13 13 14 - border-radius: 8px; 15 - background: #f8f8f8f8; 16 - border: 1px solid #808080; 14 + border-radius: 16px; 15 + background: var(--bg-2); 16 + box-shadow: var(--shadow-md); 17 17 } 18 18 19 19 .track-info { ··· 49 49 font-weight: inherit; 50 50 font-family: inherit; 51 51 font-size: inherit; 52 - border: 1px solid transparent; 52 + background: var(--bg-0); 53 + border: none; 53 54 border-radius: 4px; 54 55 outline: none; 55 56 color: inherit;
+8 -3
admin/static/index.css
··· 7 7 flex-direction: row; 8 8 align-items: center; 9 9 gap: .5em; 10 + color: var(--fg-3); 10 11 11 - border-radius: 8px; 12 - background: #f8f8f8f8; 13 - border: 1px solid #808080; 12 + border-radius: 16px; 13 + background: var(--bg-2); 14 + box-shadow: var(--shadow-md); 15 + 16 + transition: background .1s ease-out; 17 + cursor: pointer; 14 18 } 15 19 16 20 .artist:hover { 21 + background: var(--bg-1); 17 22 text-decoration: hover; 18 23 } 19 24
+8
admin/static/index.js
··· 1 + import { hijackClickEvent } from "./admin.js"; 2 + 1 3 const newReleaseBtn = document.getElementById("create-release"); 2 4 const newArtistBtn = document.getElementById("create-artist"); 3 5 const newTrackBtn = document.getElementById("create-track"); ··· 72 74 console.error(err); 73 75 }); 74 76 }); 77 + 78 + document.addEventListener("readystatechange", () => { 79 + document.querySelectorAll(".card.artists .artist").forEach(el => { 80 + hijackClickEvent(el, el.querySelector("a.artist-name")) 81 + }); 82 + });
+27 -15
admin/static/logs.css
··· 2 2 width: min(1080px, calc(100% - 2em))!important 3 3 } 4 4 5 - form { 5 + form#search-form { 6 + width: calc(100% - 2em); 6 7 margin: 1em 0; 8 + padding: 1em; 9 + border-radius: 16px; 10 + color: var(--fg-0); 11 + background: var(--bg-2); 12 + box-shadow: var(--shadow-md); 7 13 } 8 14 9 15 div#search { ··· 12 18 13 19 #search input { 14 20 margin: 0; 21 + padding: .3em .8em; 15 22 flex-grow: 1; 16 - 17 - border-right: none; 18 - border-top-right-radius: 0; 19 - border-bottom-right-radius: 0; 23 + border: none; 24 + border-radius: 16px; 25 + color: var(--fg-1); 26 + background: var(--bg-0); 27 + box-shadow: var(--shadow-sm); 20 28 } 21 29 22 30 #search button { 23 - padding: 0 .5em; 24 - 25 - border-top-left-radius: 0; 26 - border-bottom-left-radius: 0; 31 + margin-left: .5em; 32 + padding: 0 .8em; 27 33 } 28 34 29 35 form #filters p { 30 36 margin: .5em 0 0 0; 31 37 } 32 38 form #filters label { 39 + color: inherit; 33 40 display: inline; 34 41 } 35 42 form #filters input { ··· 57 64 padding: .4em .8em; 58 65 } 59 66 67 + #logs .log { 68 + color: var(--fg-2); 69 + } 70 + 60 71 td, th { 61 72 width: 1%; 62 73 text-align: left; ··· 74 85 white-space: collapse; 75 86 } 76 87 77 - .log:hover { 78 - background: #fff8; 88 + #logs .log:hover { 89 + background: color-mix(in srgb, var(--fg-3) 10%, transparent); 79 90 } 80 91 81 - .log.warn { 82 - background: #ffe86a; 92 + #logs .log.warn { 93 + color: var(--col-on-warn); 94 + background: var(--col-warn); 83 95 } 84 - .log.warn:hover { 85 - background: #ffec81; 96 + #logs .log.warn:hover { 97 + background: var(--col-warn-hover); 86 98 }
+13 -30
admin/static/release-list-item.css
··· 5 5 flex-direction: row; 6 6 gap: 1em; 7 7 8 - border-radius: 8px; 9 - background: #f8f8f8f8; 10 - border: 1px solid #808080; 8 + border-radius: 16px; 9 + background: var(--bg-2); 10 + box-shadow: var(--shadow-md); 11 11 } 12 12 13 13 .release h3, ··· 16 16 } 17 17 18 18 .release-artwork { 19 + margin: auto 0; 19 20 width: 96px; 20 21 21 22 display: flex; 22 23 justify-content: center; 23 24 align-items: center; 25 + border-radius: 4px; 26 + overflow: hidden; 27 + box-shadow: var(--shadow-sm); 24 28 } 25 29 26 30 .release-artwork img { ··· 42 46 gap: .5em; 43 47 } 44 48 45 - .release-links li { 46 - flex-grow: 1; 47 - } 48 - 49 - .release-links a { 50 - padding: .5em; 51 - display: block; 52 - 53 - border-radius: 8px; 54 - text-decoration: none; 55 - color: #f0f0f0; 56 - background: #303030; 57 - text-align: center; 58 - 59 - transition: color .1s, background .1s; 60 - } 61 - 62 - .release-links a:hover { 63 - color: #303030; 64 - background: #f0f0f0; 65 - } 66 - 67 49 .release-actions { 68 50 margin-top: .5em; 51 + user-select: none; 69 52 } 70 53 71 54 .release-actions a { ··· 74 57 display: inline-block; 75 58 76 59 border-radius: 4px; 77 - background: #e0e0e0; 60 + background: var(--bg-3); 61 + box-shadow: var(--shadow-sm); 78 62 79 - transition: color .1s, background .1s; 63 + transition: color .1s ease-out, background .1s ease-out; 80 64 } 81 65 82 66 .release-actions a:hover { 83 - color: #303030; 84 - background: #f0f0f0; 85 - 67 + background: var(--bg-0); 68 + color: var(--fg-3); 86 69 text-decoration: none; 87 70 }
+3 -4
admin/templates/html/layout.html
··· 16 16 <body> 17 17 <header> 18 18 <nav> 19 - <img src="/img/favicon.png" alt="" class="icon"> 20 - <div class="nav-item"> 21 - <a href="/">ari melody</a> 22 - </div> 19 + <a href="/" class="nav icon" aria-label="ari melody" title="Return to Home"> 20 + <img src="/img/favicon.png" alt=""> 21 + </a> 23 22 <div class="nav-item"> 24 23 <a href="/admin">home</a> 25 24 </div>
+1 -1
admin/templates/html/logs.html
··· 9 9 <main> 10 10 <h1>Audit Logs</h1> 11 11 12 - <form action="/admin/logs" method="GET"> 12 + <form action="/admin/logs" method="GET" id="search-form"> 13 13 <div id="search"> 14 14 <input type="text" name="q" value="" placeholder="Filter by message..."> 15 15 <button type="submit" class="save">Search</button>