frontend for xcvr appview

update the beeper to beep boop booop beep beep last seen

+155 -53
+115 -35
package-lock.json
··· 8 8 "name": "xcvr-frontend", 9 9 "version": "0.0.1", 10 10 "dependencies": { 11 + "@atproto/identity": "^0.4.8", 11 12 "@floating-ui/dom": "^1.7.2", 12 13 "@protobuf-ts/runtime": "^2.11.0", 13 14 "@rachel-mp4/lrcproto": "^1.0.3", ··· 24 25 "vite": "^6.2.5" 25 26 } 26 27 }, 27 - "node_modules/@ampproject/remapping": { 28 - "version": "2.3.0", 29 - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", 30 - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", 31 - "dev": true, 32 - "license": "Apache-2.0", 28 + "node_modules/@atproto/common-web": { 29 + "version": "0.4.2", 30 + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.2.tgz", 31 + "integrity": "sha512-vrXwGNoFGogodjQvJDxAeP3QbGtawgZute2ed1XdRO0wMixLk3qewtikZm06H259QDJVu6voKC5mubml+WgQUw==", 32 + "license": "MIT", 33 33 "dependencies": { 34 - "@jridgewell/gen-mapping": "^0.3.5", 35 - "@jridgewell/trace-mapping": "^0.3.24" 34 + "graphemer": "^1.4.0", 35 + "multiformats": "^9.9.0", 36 + "uint8arrays": "3.0.0", 37 + "zod": "^3.23.8" 38 + } 39 + }, 40 + "node_modules/@atproto/crypto": { 41 + "version": "0.4.4", 42 + "resolved": "https://registry.npmjs.org/@atproto/crypto/-/crypto-0.4.4.tgz", 43 + "integrity": "sha512-Yq9+crJ7WQl7sxStVpHgie5Z51R05etaK9DLWYG/7bR5T4bhdcIgF6IfklLShtZwLYdVVj+K15s0BqW9a8PSDA==", 44 + "license": "MIT", 45 + "dependencies": { 46 + "@noble/curves": "^1.7.0", 47 + "@noble/hashes": "^1.6.1", 48 + "uint8arrays": "3.0.0" 36 49 }, 37 50 "engines": { 38 - "node": ">=6.0.0" 51 + "node": ">=18.7.0" 52 + } 53 + }, 54 + "node_modules/@atproto/identity": { 55 + "version": "0.4.8", 56 + "resolved": "https://registry.npmjs.org/@atproto/identity/-/identity-0.4.8.tgz", 57 + "integrity": "sha512-Z0sLnJ87SeNdAifT+rqpgE1Rc3layMMW25gfWNo4u40RGuRODbdfAZlTwBSU2r+Vk45hU+iE+xeQspfednCEnA==", 58 + "license": "MIT", 59 + "dependencies": { 60 + "@atproto/common-web": "^0.4.2", 61 + "@atproto/crypto": "^0.4.4" 62 + }, 63 + "engines": { 64 + "node": ">=18.7.0" 39 65 } 40 66 }, 41 67 "node_modules/@esbuild/aix-ppc64": { ··· 489 515 "license": "MIT" 490 516 }, 491 517 "node_modules/@jridgewell/gen-mapping": { 492 - "version": "0.3.8", 493 - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", 494 - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", 518 + "version": "0.3.12", 519 + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", 520 + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", 495 521 "dev": true, 496 522 "license": "MIT", 497 523 "dependencies": { 498 - "@jridgewell/set-array": "^1.2.1", 499 - "@jridgewell/sourcemap-codec": "^1.4.10", 524 + "@jridgewell/sourcemap-codec": "^1.5.0", 500 525 "@jridgewell/trace-mapping": "^0.3.24" 501 - }, 502 - "engines": { 503 - "node": ">=6.0.0" 504 526 } 505 527 }, 506 - "node_modules/@jridgewell/resolve-uri": { 507 - "version": "3.1.2", 508 - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 509 - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 528 + "node_modules/@jridgewell/remapping": { 529 + "version": "2.3.4", 530 + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.4.tgz", 531 + "integrity": "sha512-aG+WvAz17rhbzhKNkSeMLgbkPPK82ovXdONvmucbGhUqcroRFLLVhoGAk4xEI17gHpXgNX3sr0/B1ybRUsbEWw==", 510 532 "dev": true, 511 533 "license": "MIT", 512 - "engines": { 513 - "node": ">=6.0.0" 534 + "dependencies": { 535 + "@jridgewell/gen-mapping": "^0.3.5", 536 + "@jridgewell/trace-mapping": "^0.3.24" 514 537 } 515 538 }, 516 - "node_modules/@jridgewell/set-array": { 517 - "version": "1.2.1", 518 - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", 519 - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", 539 + "node_modules/@jridgewell/resolve-uri": { 540 + "version": "3.1.2", 541 + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 542 + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 520 543 "dev": true, 521 544 "license": "MIT", 522 545 "engines": { ··· 539 562 "dependencies": { 540 563 "@jridgewell/resolve-uri": "^3.1.0", 541 564 "@jridgewell/sourcemap-codec": "^1.4.14" 565 + } 566 + }, 567 + "node_modules/@noble/curves": { 568 + "version": "1.9.6", 569 + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.6.tgz", 570 + "integrity": "sha512-GIKz/j99FRthB8icyJQA51E8Uk5hXmdyThjgQXRKiv9h0zeRlzSCLIzFw6K1LotZ3XuB7yzlf76qk7uBmTdFqA==", 571 + "license": "MIT", 572 + "dependencies": { 573 + "@noble/hashes": "1.8.0" 574 + }, 575 + "engines": { 576 + "node": "^14.21.3 || >=16" 577 + }, 578 + "funding": { 579 + "url": "https://paulmillr.com/funding/" 580 + } 581 + }, 582 + "node_modules/@noble/hashes": { 583 + "version": "1.8.0", 584 + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", 585 + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", 586 + "license": "MIT", 587 + "engines": { 588 + "node": "^14.21.3 || >=16" 589 + }, 590 + "funding": { 591 + "url": "https://paulmillr.com/funding/" 542 592 } 543 593 }, 544 594 "node_modules/@polka/url": { ··· 1124 1174 "license": "MIT" 1125 1175 }, 1126 1176 "node_modules/esrap": { 1127 - "version": "1.4.6", 1128 - "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.4.6.tgz", 1129 - "integrity": "sha512-F/D2mADJ9SHY3IwksD4DAXjTt7qt7GWUf3/8RhCNWmC/67tyb55dpimHmy7EplakFaflV0R/PC+fdSPqrRHAQw==", 1177 + "version": "2.1.0", 1178 + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.1.0.tgz", 1179 + "integrity": "sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==", 1130 1180 "dev": true, 1131 1181 "license": "MIT", 1132 1182 "dependencies": { ··· 1168 1218 "engines": { 1169 1219 "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 1170 1220 } 1221 + }, 1222 + "node_modules/graphemer": { 1223 + "version": "1.4.0", 1224 + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", 1225 + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", 1226 + "license": "MIT" 1171 1227 }, 1172 1228 "node_modules/is-reference": { 1173 1229 "version": "3.0.3", ··· 1232 1288 "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1233 1289 "dev": true, 1234 1290 "license": "MIT" 1291 + }, 1292 + "node_modules/multiformats": { 1293 + "version": "9.9.0", 1294 + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", 1295 + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 1296 + "license": "(Apache-2.0 AND MIT)" 1235 1297 }, 1236 1298 "node_modules/nanoid": { 1237 1299 "version": "3.3.11", ··· 1401 1463 } 1402 1464 }, 1403 1465 "node_modules/svelte": { 1404 - "version": "5.33.10", 1405 - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.33.10.tgz", 1406 - "integrity": "sha512-/yArPQIBoQS2p86LKnvJywOXkVHeEXnFgrDPSxkEfIAEkykopYuy2bF6UUqHG4IbZlJD6OurLxJT8Kn7kTk9WA==", 1466 + "version": "5.38.1", 1467 + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.38.1.tgz", 1468 + "integrity": "sha512-fO6CLDfJYWHgfo6lQwkQU2vhCiHc2MBl6s3vEhK+sSZru17YL4R5s1v14ndRpqKAIkq8nCz6MTk1yZbESZWeyQ==", 1407 1469 "dev": true, 1408 1470 "license": "MIT", 1409 1471 "dependencies": { 1410 - "@ampproject/remapping": "^2.3.0", 1472 + "@jridgewell/remapping": "^2.3.4", 1411 1473 "@jridgewell/sourcemap-codec": "^1.5.0", 1412 1474 "@sveltejs/acorn-typescript": "^1.0.5", 1413 1475 "@types/estree": "^1.0.5", ··· 1416 1478 "axobject-query": "^4.1.0", 1417 1479 "clsx": "^2.1.1", 1418 1480 "esm-env": "^1.2.1", 1419 - "esrap": "^1.4.6", 1481 + "esrap": "^2.1.0", 1420 1482 "is-reference": "^3.0.3", 1421 1483 "locate-character": "^3.0.0", 1422 1484 "magic-string": "^0.30.11", ··· 1491 1553 "node": ">=14.17" 1492 1554 } 1493 1555 }, 1556 + "node_modules/uint8arrays": { 1557 + "version": "3.0.0", 1558 + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz", 1559 + "integrity": "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==", 1560 + "license": "MIT", 1561 + "dependencies": { 1562 + "multiformats": "^9.4.2" 1563 + } 1564 + }, 1494 1565 "node_modules/vite": { 1495 1566 "version": "6.3.5", 1496 1567 "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", ··· 1591 1662 "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==", 1592 1663 "dev": true, 1593 1664 "license": "MIT" 1665 + }, 1666 + "node_modules/zod": { 1667 + "version": "3.25.76", 1668 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 1669 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 1670 + "license": "MIT", 1671 + "funding": { 1672 + "url": "https://github.com/sponsors/colinhacks" 1673 + } 1594 1674 } 1595 1675 } 1596 1676 }
+1
package.json
··· 22 22 "vite": "^6.2.5" 23 23 }, 24 24 "dependencies": { 25 + "@atproto/identity": "^0.4.8", 25 26 "@floating-ui/dom": "^1.7.2", 26 27 "@protobuf-ts/runtime": "^2.11.0", 27 28 "@rachel-mp4/lrcproto": "^1.0.3",
+1 -1
src/lib/components/Transmitter.svelte
··· 37 37 const diffAndSend = (event: InputEvent) => { 38 38 const el = event.target as HTMLInputElement; 39 39 const result = diff(message, el.value); 40 - var idx = 0; 40 + let idx = 0; 41 41 result.forEach((d) => { 42 42 switch (d[0]) { 43 43 case -1:
+5 -6
src/routes/+page.svelte
··· 160 160 <a href="/todo">todo.</a> 161 161 </p> 162 162 <p> 163 - <a href="https://github.com/rachel-mp4/xcvr">here</a> 164 - <a href="https://github.com/rachel-mp4/rvcx">are</a> 165 - <a href="https://github.com/rachel-mp4/lrcd">the</a> 166 - <a href="https://github.com/rachel-mp4/lrcproto">relevant</a> 163 + <a href="https://tangled.sh/@moth11.net/xcvr">here</a> 164 + <a href="https://tangled.sh/@moth11.net/rvcx">are</a> 165 + <a href="https://tangled.sh/@moth11.net/lrcd">the</a> 166 + <a href="https://tangled.sh/@moth11.net/lrcproto">relevant</a> 167 167 <a href="https://github.com/rachel-mp4/beeper">codes</a> for this website, don't 168 - judge me too much for them please, i'll probably put them on tangled.sh soon 169 - but i mostly wanted to get this out before i start experimenting with those things 168 + judge me too much for them please 170 169 </p> 171 170 <p> 172 171 <a href="https://discord.gg/uvAmBtpdKd">join the email list</a>
+28 -4
src/routes/p/[handle]/+page.svelte
··· 1 1 <script lang="ts"> 2 + import { DidResolver } from "@atproto/identity"; 2 3 import type { PageProps } from "./$types"; 3 4 import { numToHex, hexToNum, hexToContrast } from "$lib/colors"; 4 5 import { browser } from "$app/environment"; ··· 150 151 return "now"; 151 152 } 152 153 }; 154 + const didres = new DidResolver({}); 155 + const getPathToChannel = async (aturi: string): Promise<string | null> => { 156 + try { 157 + if (!aturi.startsWith("at://")) { 158 + throw new Error(); 159 + } 160 + const sansprefix = aturi.slice(5); 161 + const splitted = sansprefix.split("/"); 162 + const did = splitted[0]; 163 + const handle = await didres.resolve(did); 164 + if (!handle) { 165 + throw new Error(); 166 + } 167 + const base = import.meta.env.MY_BASE_URL; 168 + const rkey = splitted[2]; 169 + return `${base}/c/${handle.id}/${rkey}`; 170 + } catch { 171 + return null; 172 + } 173 + }; 174 + // @ts-expect-error - Svelte 5.36 experimental async support 175 + let lastSeenLocation = $derived(await getPathToChannel(data.lastSeen.where)); 153 176 </script> 154 177 155 178 <main> ··· 167 190 </p> 168 191 {/if} 169 192 {#if data.lastSeen} 170 - last seen {#if data.lastSeen.where} 171 - {data.lastSeen.where} 193 + last seen 194 + {#if data.lastSeen.when} 195 + {timeSince(data.lastSeen.when) + " ago"} 172 196 {/if} 173 - {#if data.lastSeen.when} 174 - {timeSince(data.lastSeen.when)} 197 + {#if lastSeenLocation !== null} 198 + <a href={lastSeenLocation}> in this channel</a> 175 199 {/if} 176 200 {/if} 177 201 </main>
-7
src/routes/todo/+page.svelte
··· 1 1 <main> 2 2 <h2>known bugs</h2> 3 3 <p> 4 - <b> undo/redo in chat </b> is currently disabled because i believe the events 5 - which fire on these actions don't provide me with enough information to properly 6 - broadcast which text was deleted and which text was appended, so i believe i 7 - need to find a diff library that won't force me to delete the whole message and 8 - rebroadcast it every history event 9 - </p> 10 - <p> 11 4 <b> inability to validate messages </b> occurs at some points in time, possibly 12 5 after the backend restarts, but i'm not totally sure how or why, oauth is a bit 13 6 mysterious to me still and it's likely a silly error somewhere but if you suddenly
+5
svelte.config.js
··· 14 14 adapter: adapter({ 15 15 fallback: 'index.html' 16 16 }) 17 + }, 18 + compilerOptions: { 19 + experimental: { 20 + async: true 21 + } 17 22 } 18 23 }; 19 24