Canonical repo for Dong Web (dong.vielle.dev)
0
fork

Configure Feed

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

Improve styles

+149 -46
-1
src/layouts/Base.astro
··· 37 37 padding: 0; 38 38 box-sizing: border-box; 39 39 appearance: none; 40 - color-scheme: dark; 41 40 } 42 41 43 42 img,
+149 -45
src/pages/index.astro
··· 5 5 <Base title="Dong Web"> 6 6 <script> 7 7 import { createDong, download, readDong } from "../dong-io"; 8 - 9 - declare global { 10 - interface Uint8Array { 11 - toBase64(): string; 12 - } 13 - } 14 - 15 - const blobBytes = async (blob: Blob) => { 16 - if ("bytes" in blob) return blob.bytes(); 17 - return new Response(blob).arrayBuffer().then((buffer) => { 18 - const uint = new Uint8Array(buffer); 19 - return uint; 20 - }); 21 - }; 22 - 23 - const uint8array64 = (arru8: Uint8Array) => { 24 - if ("toBase64" in arru8) return arru8.toBase64(); 25 - 26 - function _arrayBufferToBase64(bytes: Uint8Array) { 27 - var binary = ""; 28 - var len = bytes.byteLength; 29 - for (var i = 0; i < len; i++) { 30 - binary += String.fromCharCode(bytes[i]); 31 - } 32 - return btoa(binary); 33 - } 34 - return _arrayBufferToBase64(arru8); 35 - }; 36 - 8 + 37 9 class CreateDong extends HTMLElement { 38 10 connectedCallback() { 39 11 // create input ··· 41 13 42 14 const imageLabel = document.createElement("label"); 43 15 const audioLabel = document.createElement("label"); 16 + 17 + const imageFileName = document.createElement("span"); 18 + const audioFileName = document.createElement("span"); 19 + 44 20 const imageSelect = document.createElement("input"); 45 21 const audioSelect = document.createElement("input"); 46 22 47 23 const filename = document.createElement("input"); 24 + const fileLabel = document.createElement("label"); 48 25 const createButton = document.createElement("button"); 49 26 50 27 const errormsg = document.createElement("div"); ··· 62 39 audioLabel.innerText = "Audio"; 63 40 audioLabel.htmlFor = audioSelect.id; 64 41 42 + filename.id = "filename"; 43 + 44 + fileLabel.innerText = "Name: "; 45 + fileLabel.htmlFor = filename.id; 46 + 65 47 createButton.type = "submit"; 66 48 createButton.textContent = "Create"; 67 49 68 50 imageLabel.appendChild(imageSelect); 69 51 audioLabel.appendChild(audioSelect); 70 52 53 + imageLabel.appendChild(imageFileName); 54 + audioLabel.appendChild(audioFileName); 55 + fileLabel.appendChild(filename); 56 + 71 57 form.appendChild(imageLabel); 72 58 form.appendChild(audioLabel); 73 - form.appendChild(filename); 59 + form.appendChild(fileLabel); 74 60 form.appendChild(createButton); 75 61 form.appendChild(errormsg); 76 62 ··· 80 66 createButton.addEventListener("click", async (e) => { 81 67 // don't refresh 82 68 e.preventDefault(); 69 + errormsg.innerText = ""; 70 + 83 71 // quit early if no files 84 72 if ( 85 73 !imageSelect.files || ··· 91 79 return; 92 80 } 93 81 82 + if (filename.value === "") { 83 + errormsg.innerText = "Filename cannot be empty"; 84 + return; 85 + } 86 + 94 87 // get files 95 88 const image = imageSelect.files[0]; 96 89 const audio = audioSelect.files[0]; ··· 102 95 return; 103 96 } 104 97 105 - const dongFile = new File([res], `${filename.value}.dong`, { 106 - type: "application/prs.vielle.dong", 107 - }); 98 + const dongFile = new File( 99 + [res], 100 + `${filename.value === "" ? "dong" : filename.value}.dong`, 101 + { 102 + type: "application/prs.vielle.dong", 103 + } 104 + ); 108 105 109 106 // download the dong file 110 107 download(dongFile); 108 + }); 109 + 110 + imageSelect.addEventListener("change", (e) => { 111 + if (!imageSelect.files || imageSelect.files.length === 0) return; 112 + imageFileName.innerText = imageSelect.files[0].name; 113 + }); 114 + 115 + audioSelect.addEventListener("change", (e) => { 116 + if (!audioSelect.files || audioSelect.files.length === 0) return; 117 + audioFileName.innerText = audioSelect.files[0].name; 111 118 }); 112 119 } 113 120 } ··· 116 123 connectedCallback() { 117 124 // create input 118 125 const form = document.createElement("form"); 126 + const dongLabel = document.createElement("label"); 119 127 const dongSelect = document.createElement("input"); 120 - const loadButton = document.createElement("button"); 121 128 // image 122 129 const errormsg = document.createElement("div"); 123 130 const image = document.createElement("img"); ··· 128 135 129 136 dongSelect.type = "file"; 130 137 dongSelect.accept = ".dong"; 131 - loadButton.type = "submit"; 132 - loadButton.textContent = "Load"; 133 - image.width = 256; 134 - image.height = 256; 138 + dongSelect.id = "dong-select"; 135 139 136 - form.appendChild(dongSelect); 137 - form.appendChild(loadButton); 140 + dongLabel.innerText = "Upload .dong"; 141 + dongLabel.htmlFor = dongSelect.id; 142 + 143 + dongLabel.appendChild(dongSelect); 144 + 145 + form.appendChild(dongLabel); 138 146 139 147 this.appendChild(form); 140 148 this.appendChild(image); 141 149 this.appendChild(errormsg); 142 150 143 151 // functionality 144 - loadButton.addEventListener("click", async (e) => { 152 + dongSelect.addEventListener("change", async (e) => { 145 153 // don't refresh 146 154 e.preventDefault(); 155 + errormsg.innerText = ""; 156 + 147 157 // quit early if no files 148 158 if (!dongSelect.files || dongSelect.files.length === 0) 149 159 return (errormsg.innerText = "No files selected"); ··· 171 181 </script> 172 182 173 183 <style slot="head" is:inline> 184 + :root { 185 + --accent: #f80085; 186 + } 187 + * { 188 + color: white; 189 + } 190 + 174 191 button { 175 - background-color: #f80085; 192 + background-color: var(--accent); 193 + } 194 + 195 + body { 196 + display: flex; 197 + flex-direction: column; 198 + align-items: center; 199 + justify-content: start; 200 + gap: 1rem; 201 + min-height: 100vh; 202 + background-color: hsl(270, 50%, 10%); 203 + max-width: 60ch; 204 + margin: auto; 205 + padding: 1rem; 206 + margin-block-end: 5rem; 176 207 } 177 208 178 - label { 179 - background-color: #f80085; 209 + label:has(input[type="file"]), 210 + button { 211 + background-color: var(--accent); 212 + border: none; 213 + padding: 0.5rem; 214 + padding-inline: 1rem; 215 + 216 + display: flex; 217 + flex-direction: column; 218 + align-items: center; 219 + justify-content: center; 180 220 181 - & > input[type="file"] { 221 + &:is(label) > input[type="file"] { 182 222 display: none; 183 223 } 184 224 } 225 + 226 + :is(div, span):empty { 227 + display: none; 228 + } 229 + 230 + img:not([src]) { 231 + display: none; 232 + } 233 + 234 + img { 235 + max-width: 100%; 236 + } 237 + 238 + .logo, 239 + .logo img { 240 + width: 100%; 241 + } 242 + hr { 243 + width: 100%; 244 + } 245 + 246 + label:has(input:not([type])) { 247 + display: flex; 248 + flex-direction: row; 249 + flex-wrap: nowrap; 250 + align-items: center; 251 + gap: 0.5rem; 252 + } 253 + 254 + input:not([type]) { 255 + border: 0.2rem solid white; 256 + background-color: transparent; 257 + width: 100%; 258 + } 259 + 260 + label:has(input[type="file"]):has(span) { 261 + height: 3lh; 262 + gap: 0.5rem; 263 + } 264 + 265 + create-dong form { 266 + display: grid; 267 + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); 268 + gap: 1rem; 269 + } 270 + 271 + load-dong { 272 + display: flex; 273 + flex-direction: column; 274 + gap: 1rem; 275 + } 276 + 277 + create-dong span { 278 + width: 100%; 279 + height: 1lh; 280 + overflow: hidden; 281 + white-space: nowrap; 282 + text-overflow: ellipsis; 283 + } 185 284 </style> 186 285 187 286 <!-- logo --> 287 + <div class="logo"> 288 + <img src="/logo.svg" alt="logo" /> 289 + </div> 290 + 291 + <hr /> 188 292 189 293 <!-- creation --> 190 294 <create-dong></create-dong>