forked from pdsls.dev/pdsls
atproto explorer

Compare changes

Choose any two refs to compare.

+1
index.html
··· 15 15 <link rel="stylesheet" href="https://rsms.me/inter/inter.css" /> 16 16 <link rel="preconnect" href="https://fonts.bunny.net" /> 17 17 <link href="https://fonts.bunny.net/css?family=roboto-mono:400" rel="stylesheet" /> 18 + <link href="https://fonts.cdnfonts.com/css/pecita" rel="stylesheet" /> 18 19 <script> 19 20 document.documentElement.classList.toggle( 20 21 "dark",
+2 -2
package.json
··· 9 9 "serve": "vite preview" 10 10 }, 11 11 "devDependencies": { 12 - "@iconify-json/lucide": "^1.2.69", 12 + "@iconify-json/lucide": "^1.2.70", 13 13 "@iconify/tailwind4": "^1.0.6", 14 14 "@tailwindcss/vite": "^4.1.14", 15 15 "prettier": "^3.6.2", 16 16 "prettier-plugin-organize-imports": "^4.3.0", 17 - "prettier-plugin-tailwindcss": "^0.7.0", 17 + "prettier-plugin-tailwindcss": "^0.7.1", 18 18 "tailwindcss": "^4.1.14", 19 19 "typescript": "^5.9.3", 20 20 "vite": "^7.1.10",
+127 -127
pnpm-lock.yaml
··· 79 79 version: 6.38.6 80 80 '@fsegurai/codemirror-theme-basic-dark': 81 81 specifier: ^6.2.2 82 - version: 6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)(@lezer/highlight@1.2.1) 82 + version: 6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)(@lezer/highlight@1.2.2) 83 83 '@fsegurai/codemirror-theme-basic-light': 84 84 specifier: ^6.2.2 85 - version: 6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)(@lezer/highlight@1.2.1) 85 + version: 6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)(@lezer/highlight@1.2.2) 86 86 '@mary/exif-rm': 87 87 specifier: jsr:^0.2.2 88 88 version: '@jsr/mary__exif-rm@0.2.2' ··· 103 103 version: 1.9.9 104 104 devDependencies: 105 105 '@iconify-json/lucide': 106 - specifier: ^1.2.69 107 - version: 1.2.69 106 + specifier: ^1.2.70 107 + version: 1.2.70 108 108 '@iconify/tailwind4': 109 109 specifier: ^1.0.6 110 110 version: 1.0.6(tailwindcss@4.1.14) ··· 118 118 specifier: ^4.3.0 119 119 version: 4.3.0(prettier@3.6.2)(typescript@5.9.3) 120 120 prettier-plugin-tailwindcss: 121 - specifier: ^0.7.0 122 - version: 0.7.0(prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.3))(prettier@3.6.2) 121 + specifier: ^0.7.1 122 + version: 0.7.1(prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.3))(prettier@3.6.2) 123 123 tailwindcss: 124 124 specifier: ^4.1.14 125 125 version: 4.1.14 ··· 634 634 '@codemirror/view': ^6.0.0 635 635 '@lezer/highlight': ^1.0.0 636 636 637 - '@iconify-json/lucide@1.2.69': 638 - resolution: {integrity: sha512-xOhNf74m+C+nSCObfEqYi34dXk1GMfMUcOB+gfqKY/bn0RcsPLinGfgouOvrUFEreDEFbCti7sdheTf5HESLTA==} 637 + '@iconify-json/lucide@1.2.70': 638 + resolution: {integrity: sha512-56s9NdBKgshywVY1e4gOcxzAbU1J649e/jLHBJU1tyNqRs7mFLVEGwj2mmzHJ5YAZB5Tsngi4f/ocTBPlG06ZA==} 639 639 640 640 '@iconify/tailwind4@1.0.6': 641 641 resolution: {integrity: sha512-43ZXe+bC7CuE2LCgROdqbQeFYJi/J7L/k1UpSy8KDQlWVsWxPzLSWbWhlJx4uRYLOh1NRyw02YlDOgzBOFNd+A==} ··· 671 671 '@jsr/mary__exif-rm@0.2.2': 672 672 resolution: {integrity: sha512-+ZpLaC+1CyqWhH608Sqd6/yTG0pOlokn2tCXha7s1SMQ+GLKo4Nn/PskTeeP9Pt+6gNYSu6ednoSlRvXb2ZGxg==, tarball: https://npm.jsr.io/~/11/@jsr/mary__exif-rm/0.2.2.tgz} 673 673 674 - '@lezer/common@1.2.3': 675 - resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} 674 + '@lezer/common@1.3.0': 675 + resolution: {integrity: sha512-L9X8uHCYU310o99L3/MpJKYxPzXPOS7S0NmBaM7UO/x2Kb2WbmMLSkfvdr1KxRIFYOpbY0Jhn7CfLSUDzL8arQ==} 676 676 677 - '@lezer/highlight@1.2.1': 678 - resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==} 677 + '@lezer/highlight@1.2.2': 678 + resolution: {integrity: sha512-z8TQwaBXXQIvG6i2g3e9cgMwUUXu9Ib7jo2qRRggdhwKpM56Dw3PM3wmexn+EGaaOZ7az0K7sjc3/gcGW7sz7A==} 679 679 680 680 '@lezer/json@1.0.3': 681 681 resolution: {integrity: sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==} ··· 689 689 '@noble/secp256k1@2.3.0': 690 690 resolution: {integrity: sha512-0TQed2gcBbIrh7Ccyw+y/uZQvbJwm7Ao4scBUxqpBCcsOlZG0O4KGfjtNAy/li4W8n1xt3dxrwJ0beZ2h2G6Kw==} 691 691 692 - '@rollup/rollup-android-arm-eabi@4.52.4': 693 - resolution: {integrity: sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==} 692 + '@rollup/rollup-android-arm-eabi@4.52.5': 693 + resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} 694 694 cpu: [arm] 695 695 os: [android] 696 696 697 - '@rollup/rollup-android-arm64@4.52.4': 698 - resolution: {integrity: sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==} 697 + '@rollup/rollup-android-arm64@4.52.5': 698 + resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==} 699 699 cpu: [arm64] 700 700 os: [android] 701 701 702 - '@rollup/rollup-darwin-arm64@4.52.4': 703 - resolution: {integrity: sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==} 702 + '@rollup/rollup-darwin-arm64@4.52.5': 703 + resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==} 704 704 cpu: [arm64] 705 705 os: [darwin] 706 706 707 - '@rollup/rollup-darwin-x64@4.52.4': 708 - resolution: {integrity: sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==} 707 + '@rollup/rollup-darwin-x64@4.52.5': 708 + resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==} 709 709 cpu: [x64] 710 710 os: [darwin] 711 711 712 - '@rollup/rollup-freebsd-arm64@4.52.4': 713 - resolution: {integrity: sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==} 712 + '@rollup/rollup-freebsd-arm64@4.52.5': 713 + resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==} 714 714 cpu: [arm64] 715 715 os: [freebsd] 716 716 717 - '@rollup/rollup-freebsd-x64@4.52.4': 718 - resolution: {integrity: sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==} 717 + '@rollup/rollup-freebsd-x64@4.52.5': 718 + resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==} 719 719 cpu: [x64] 720 720 os: [freebsd] 721 721 722 - '@rollup/rollup-linux-arm-gnueabihf@4.52.4': 723 - resolution: {integrity: sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==} 722 + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': 723 + resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} 724 724 cpu: [arm] 725 725 os: [linux] 726 726 727 - '@rollup/rollup-linux-arm-musleabihf@4.52.4': 728 - resolution: {integrity: sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==} 727 + '@rollup/rollup-linux-arm-musleabihf@4.52.5': 728 + resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} 729 729 cpu: [arm] 730 730 os: [linux] 731 731 732 - '@rollup/rollup-linux-arm64-gnu@4.52.4': 733 - resolution: {integrity: sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==} 732 + '@rollup/rollup-linux-arm64-gnu@4.52.5': 733 + resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} 734 734 cpu: [arm64] 735 735 os: [linux] 736 736 737 - '@rollup/rollup-linux-arm64-musl@4.52.4': 738 - resolution: {integrity: sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==} 737 + '@rollup/rollup-linux-arm64-musl@4.52.5': 738 + resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} 739 739 cpu: [arm64] 740 740 os: [linux] 741 741 742 - '@rollup/rollup-linux-loong64-gnu@4.52.4': 743 - resolution: {integrity: sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==} 742 + '@rollup/rollup-linux-loong64-gnu@4.52.5': 743 + resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} 744 744 cpu: [loong64] 745 745 os: [linux] 746 746 747 - '@rollup/rollup-linux-ppc64-gnu@4.52.4': 748 - resolution: {integrity: sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==} 747 + '@rollup/rollup-linux-ppc64-gnu@4.52.5': 748 + resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} 749 749 cpu: [ppc64] 750 750 os: [linux] 751 751 752 - '@rollup/rollup-linux-riscv64-gnu@4.52.4': 753 - resolution: {integrity: sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==} 752 + '@rollup/rollup-linux-riscv64-gnu@4.52.5': 753 + resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} 754 754 cpu: [riscv64] 755 755 os: [linux] 756 756 757 - '@rollup/rollup-linux-riscv64-musl@4.52.4': 758 - resolution: {integrity: sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==} 757 + '@rollup/rollup-linux-riscv64-musl@4.52.5': 758 + resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} 759 759 cpu: [riscv64] 760 760 os: [linux] 761 761 762 - '@rollup/rollup-linux-s390x-gnu@4.52.4': 763 - resolution: {integrity: sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==} 762 + '@rollup/rollup-linux-s390x-gnu@4.52.5': 763 + resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} 764 764 cpu: [s390x] 765 765 os: [linux] 766 766 767 - '@rollup/rollup-linux-x64-gnu@4.52.4': 768 - resolution: {integrity: sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==} 767 + '@rollup/rollup-linux-x64-gnu@4.52.5': 768 + resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} 769 769 cpu: [x64] 770 770 os: [linux] 771 771 772 - '@rollup/rollup-linux-x64-musl@4.52.4': 773 - resolution: {integrity: sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==} 772 + '@rollup/rollup-linux-x64-musl@4.52.5': 773 + resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} 774 774 cpu: [x64] 775 775 os: [linux] 776 776 777 - '@rollup/rollup-openharmony-arm64@4.52.4': 778 - resolution: {integrity: sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==} 777 + '@rollup/rollup-openharmony-arm64@4.52.5': 778 + resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} 779 779 cpu: [arm64] 780 780 os: [openharmony] 781 781 782 - '@rollup/rollup-win32-arm64-msvc@4.52.4': 783 - resolution: {integrity: sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==} 782 + '@rollup/rollup-win32-arm64-msvc@4.52.5': 783 + resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==} 784 784 cpu: [arm64] 785 785 os: [win32] 786 786 787 - '@rollup/rollup-win32-ia32-msvc@4.52.4': 788 - resolution: {integrity: sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==} 787 + '@rollup/rollup-win32-ia32-msvc@4.52.5': 788 + resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==} 789 789 cpu: [ia32] 790 790 os: [win32] 791 791 792 - '@rollup/rollup-win32-x64-gnu@4.52.4': 793 - resolution: {integrity: sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==} 792 + '@rollup/rollup-win32-x64-gnu@4.52.5': 793 + resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==} 794 794 cpu: [x64] 795 795 os: [win32] 796 796 797 - '@rollup/rollup-win32-x64-msvc@4.52.4': 798 - resolution: {integrity: sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==} 797 + '@rollup/rollup-win32-x64-msvc@4.52.5': 798 + resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==} 799 799 cpu: [x64] 800 800 os: [win32] 801 801 ··· 1220 1220 vue-tsc: 1221 1221 optional: true 1222 1222 1223 - prettier-plugin-tailwindcss@0.7.0: 1224 - resolution: {integrity: sha512-zpRZhkfwq1cNmbKhmKzXKuKFdkgXZXlf6p+KttD75v6pGz1FxmcKMc4RKdw97GYBKBbout4113HSLaBJAomFDw==} 1223 + prettier-plugin-tailwindcss@0.7.1: 1224 + resolution: {integrity: sha512-Bzv1LZcuiR1Sk02iJTS1QzlFNp/o5l2p3xkopwOrbPmtMeh3fK9rVW5M3neBQzHq+kGKj/4LGQMTNcTH4NGPtQ==} 1225 1225 engines: {node: '>=20.19'} 1226 1226 peerDependencies: 1227 1227 '@ianvs/prettier-plugin-sort-imports': '*' ··· 1286 1286 resolve-pkg-maps@1.0.0: 1287 1287 resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} 1288 1288 1289 - rollup@4.52.4: 1290 - resolution: {integrity: sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==} 1289 + rollup@4.52.5: 1290 + resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==} 1291 1291 engines: {node: '>=18.0.0', npm: '>=8.0.0'} 1292 1292 hasBin: true 1293 1293 ··· 1317 1317 resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 1318 1318 engines: {node: '>=0.10.0'} 1319 1319 1320 - style-mod@4.1.2: 1321 - resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==} 1320 + style-mod@4.1.3: 1321 + resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==} 1322 1322 1323 1323 tailwindcss@4.1.14: 1324 1324 resolution: {integrity: sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==} ··· 1680 1680 '@codemirror/language': 6.11.3 1681 1681 '@codemirror/state': 6.5.2 1682 1682 '@codemirror/view': 6.38.6 1683 - '@lezer/common': 1.2.3 1683 + '@lezer/common': 1.3.0 1684 1684 1685 1685 '@codemirror/commands@6.9.0': 1686 1686 dependencies: 1687 1687 '@codemirror/language': 6.11.3 1688 1688 '@codemirror/state': 6.5.2 1689 1689 '@codemirror/view': 6.38.6 1690 - '@lezer/common': 1.2.3 1690 + '@lezer/common': 1.3.0 1691 1691 1692 1692 '@codemirror/lang-json@6.0.2': 1693 1693 dependencies: ··· 1698 1698 dependencies: 1699 1699 '@codemirror/state': 6.5.2 1700 1700 '@codemirror/view': 6.38.6 1701 - '@lezer/common': 1.2.3 1702 - '@lezer/highlight': 1.2.1 1701 + '@lezer/common': 1.3.0 1702 + '@lezer/highlight': 1.2.2 1703 1703 '@lezer/lr': 1.4.2 1704 - style-mod: 4.1.2 1704 + style-mod: 4.1.3 1705 1705 1706 1706 '@codemirror/lint@6.9.0': 1707 1707 dependencies: ··· 1723 1723 dependencies: 1724 1724 '@codemirror/state': 6.5.2 1725 1725 crelt: 1.0.6 1726 - style-mod: 4.1.2 1726 + style-mod: 4.1.3 1727 1727 w3c-keyname: 2.2.8 1728 1728 1729 1729 '@esbuild/aix-ppc64@0.23.1': ··· 1876 1876 '@esbuild/win32-x64@0.25.11': 1877 1877 optional: true 1878 1878 1879 - '@fsegurai/codemirror-theme-basic-dark@6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)(@lezer/highlight@1.2.1)': 1879 + '@fsegurai/codemirror-theme-basic-dark@6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)(@lezer/highlight@1.2.2)': 1880 1880 dependencies: 1881 1881 '@codemirror/language': 6.11.3 1882 1882 '@codemirror/state': 6.5.2 1883 1883 '@codemirror/view': 6.38.6 1884 - '@lezer/highlight': 1.2.1 1884 + '@lezer/highlight': 1.2.2 1885 1885 1886 - '@fsegurai/codemirror-theme-basic-light@6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)(@lezer/highlight@1.2.1)': 1886 + '@fsegurai/codemirror-theme-basic-light@6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)(@lezer/highlight@1.2.2)': 1887 1887 dependencies: 1888 1888 '@codemirror/language': 6.11.3 1889 1889 '@codemirror/state': 6.5.2 1890 1890 '@codemirror/view': 6.38.6 1891 - '@lezer/highlight': 1.2.1 1891 + '@lezer/highlight': 1.2.2 1892 1892 1893 - '@iconify-json/lucide@1.2.69': 1893 + '@iconify-json/lucide@1.2.70': 1894 1894 dependencies: 1895 1895 '@iconify/types': 2.0.0 1896 1896 ··· 1942 1942 1943 1943 '@jsr/mary__exif-rm@0.2.2': {} 1944 1944 1945 - '@lezer/common@1.2.3': {} 1945 + '@lezer/common@1.3.0': {} 1946 1946 1947 - '@lezer/highlight@1.2.1': 1947 + '@lezer/highlight@1.2.2': 1948 1948 dependencies: 1949 - '@lezer/common': 1.2.3 1949 + '@lezer/common': 1.3.0 1950 1950 1951 1951 '@lezer/json@1.0.3': 1952 1952 dependencies: 1953 - '@lezer/common': 1.2.3 1954 - '@lezer/highlight': 1.2.1 1953 + '@lezer/common': 1.3.0 1954 + '@lezer/highlight': 1.2.2 1955 1955 '@lezer/lr': 1.4.2 1956 1956 1957 1957 '@lezer/lr@1.4.2': 1958 1958 dependencies: 1959 - '@lezer/common': 1.2.3 1959 + '@lezer/common': 1.3.0 1960 1960 1961 1961 '@marijn/find-cluster-break@1.0.2': {} 1962 1962 1963 1963 '@noble/secp256k1@2.3.0': {} 1964 1964 1965 - '@rollup/rollup-android-arm-eabi@4.52.4': 1965 + '@rollup/rollup-android-arm-eabi@4.52.5': 1966 1966 optional: true 1967 1967 1968 - '@rollup/rollup-android-arm64@4.52.4': 1968 + '@rollup/rollup-android-arm64@4.52.5': 1969 1969 optional: true 1970 1970 1971 - '@rollup/rollup-darwin-arm64@4.52.4': 1971 + '@rollup/rollup-darwin-arm64@4.52.5': 1972 1972 optional: true 1973 1973 1974 - '@rollup/rollup-darwin-x64@4.52.4': 1974 + '@rollup/rollup-darwin-x64@4.52.5': 1975 1975 optional: true 1976 1976 1977 - '@rollup/rollup-freebsd-arm64@4.52.4': 1977 + '@rollup/rollup-freebsd-arm64@4.52.5': 1978 1978 optional: true 1979 1979 1980 - '@rollup/rollup-freebsd-x64@4.52.4': 1980 + '@rollup/rollup-freebsd-x64@4.52.5': 1981 1981 optional: true 1982 1982 1983 - '@rollup/rollup-linux-arm-gnueabihf@4.52.4': 1983 + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': 1984 1984 optional: true 1985 1985 1986 - '@rollup/rollup-linux-arm-musleabihf@4.52.4': 1986 + '@rollup/rollup-linux-arm-musleabihf@4.52.5': 1987 1987 optional: true 1988 1988 1989 - '@rollup/rollup-linux-arm64-gnu@4.52.4': 1989 + '@rollup/rollup-linux-arm64-gnu@4.52.5': 1990 1990 optional: true 1991 1991 1992 - '@rollup/rollup-linux-arm64-musl@4.52.4': 1992 + '@rollup/rollup-linux-arm64-musl@4.52.5': 1993 1993 optional: true 1994 1994 1995 - '@rollup/rollup-linux-loong64-gnu@4.52.4': 1995 + '@rollup/rollup-linux-loong64-gnu@4.52.5': 1996 1996 optional: true 1997 1997 1998 - '@rollup/rollup-linux-ppc64-gnu@4.52.4': 1998 + '@rollup/rollup-linux-ppc64-gnu@4.52.5': 1999 1999 optional: true 2000 2000 2001 - '@rollup/rollup-linux-riscv64-gnu@4.52.4': 2001 + '@rollup/rollup-linux-riscv64-gnu@4.52.5': 2002 2002 optional: true 2003 2003 2004 - '@rollup/rollup-linux-riscv64-musl@4.52.4': 2004 + '@rollup/rollup-linux-riscv64-musl@4.52.5': 2005 2005 optional: true 2006 2006 2007 - '@rollup/rollup-linux-s390x-gnu@4.52.4': 2007 + '@rollup/rollup-linux-s390x-gnu@4.52.5': 2008 2008 optional: true 2009 2009 2010 - '@rollup/rollup-linux-x64-gnu@4.52.4': 2010 + '@rollup/rollup-linux-x64-gnu@4.52.5': 2011 2011 optional: true 2012 2012 2013 - '@rollup/rollup-linux-x64-musl@4.52.4': 2013 + '@rollup/rollup-linux-x64-musl@4.52.5': 2014 2014 optional: true 2015 2015 2016 - '@rollup/rollup-openharmony-arm64@4.52.4': 2016 + '@rollup/rollup-openharmony-arm64@4.52.5': 2017 2017 optional: true 2018 2018 2019 - '@rollup/rollup-win32-arm64-msvc@4.52.4': 2019 + '@rollup/rollup-win32-arm64-msvc@4.52.5': 2020 2020 optional: true 2021 2021 2022 - '@rollup/rollup-win32-ia32-msvc@4.52.4': 2022 + '@rollup/rollup-win32-ia32-msvc@4.52.5': 2023 2023 optional: true 2024 2024 2025 - '@rollup/rollup-win32-x64-gnu@4.52.4': 2025 + '@rollup/rollup-win32-x64-gnu@4.52.5': 2026 2026 optional: true 2027 2027 2028 - '@rollup/rollup-win32-x64-msvc@4.52.4': 2028 + '@rollup/rollup-win32-x64-msvc@4.52.5': 2029 2029 optional: true 2030 2030 2031 2031 '@skyware/firehose@0.5.2': ··· 2427 2427 prettier: 3.6.2 2428 2428 typescript: 5.9.3 2429 2429 2430 - prettier-plugin-tailwindcss@0.7.0(prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.3))(prettier@3.6.2): 2430 + prettier-plugin-tailwindcss@0.7.1(prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.3))(prettier@3.6.2): 2431 2431 dependencies: 2432 2432 prettier: 3.6.2 2433 2433 optionalDependencies: ··· 2440 2440 resolve-pkg-maps@1.0.0: 2441 2441 optional: true 2442 2442 2443 - rollup@4.52.4: 2443 + rollup@4.52.5: 2444 2444 dependencies: 2445 2445 '@types/estree': 1.0.8 2446 2446 optionalDependencies: 2447 - '@rollup/rollup-android-arm-eabi': 4.52.4 2448 - '@rollup/rollup-android-arm64': 4.52.4 2449 - '@rollup/rollup-darwin-arm64': 4.52.4 2450 - '@rollup/rollup-darwin-x64': 4.52.4 2451 - '@rollup/rollup-freebsd-arm64': 4.52.4 2452 - '@rollup/rollup-freebsd-x64': 4.52.4 2453 - '@rollup/rollup-linux-arm-gnueabihf': 4.52.4 2454 - '@rollup/rollup-linux-arm-musleabihf': 4.52.4 2455 - '@rollup/rollup-linux-arm64-gnu': 4.52.4 2456 - '@rollup/rollup-linux-arm64-musl': 4.52.4 2457 - '@rollup/rollup-linux-loong64-gnu': 4.52.4 2458 - '@rollup/rollup-linux-ppc64-gnu': 4.52.4 2459 - '@rollup/rollup-linux-riscv64-gnu': 4.52.4 2460 - '@rollup/rollup-linux-riscv64-musl': 4.52.4 2461 - '@rollup/rollup-linux-s390x-gnu': 4.52.4 2462 - '@rollup/rollup-linux-x64-gnu': 4.52.4 2463 - '@rollup/rollup-linux-x64-musl': 4.52.4 2464 - '@rollup/rollup-openharmony-arm64': 4.52.4 2465 - '@rollup/rollup-win32-arm64-msvc': 4.52.4 2466 - '@rollup/rollup-win32-ia32-msvc': 4.52.4 2467 - '@rollup/rollup-win32-x64-gnu': 4.52.4 2468 - '@rollup/rollup-win32-x64-msvc': 4.52.4 2447 + '@rollup/rollup-android-arm-eabi': 4.52.5 2448 + '@rollup/rollup-android-arm64': 4.52.5 2449 + '@rollup/rollup-darwin-arm64': 4.52.5 2450 + '@rollup/rollup-darwin-x64': 4.52.5 2451 + '@rollup/rollup-freebsd-arm64': 4.52.5 2452 + '@rollup/rollup-freebsd-x64': 4.52.5 2453 + '@rollup/rollup-linux-arm-gnueabihf': 4.52.5 2454 + '@rollup/rollup-linux-arm-musleabihf': 4.52.5 2455 + '@rollup/rollup-linux-arm64-gnu': 4.52.5 2456 + '@rollup/rollup-linux-arm64-musl': 4.52.5 2457 + '@rollup/rollup-linux-loong64-gnu': 4.52.5 2458 + '@rollup/rollup-linux-ppc64-gnu': 4.52.5 2459 + '@rollup/rollup-linux-riscv64-gnu': 4.52.5 2460 + '@rollup/rollup-linux-riscv64-musl': 4.52.5 2461 + '@rollup/rollup-linux-s390x-gnu': 4.52.5 2462 + '@rollup/rollup-linux-x64-gnu': 4.52.5 2463 + '@rollup/rollup-linux-x64-musl': 4.52.5 2464 + '@rollup/rollup-openharmony-arm64': 4.52.5 2465 + '@rollup/rollup-win32-arm64-msvc': 4.52.5 2466 + '@rollup/rollup-win32-ia32-msvc': 4.52.5 2467 + '@rollup/rollup-win32-x64-gnu': 4.52.5 2468 + '@rollup/rollup-win32-x64-msvc': 4.52.5 2469 2469 fsevents: 2.3.3 2470 2470 2471 2471 semver@6.3.1: {} ··· 2493 2493 2494 2494 source-map-js@1.2.1: {} 2495 2495 2496 - style-mod@4.1.2: {} 2496 + style-mod@4.1.3: {} 2497 2497 2498 2498 tailwindcss@4.1.14: {} 2499 2499 ··· 2556 2556 fdir: 6.5.0(picomatch@4.0.3) 2557 2557 picomatch: 4.0.3 2558 2558 postcss: 8.5.6 2559 - rollup: 4.52.4 2559 + rollup: 4.52.5 2560 2560 tinyglobby: 0.2.15 2561 2561 optionalDependencies: 2562 2562 '@types/node': 22.13.1
+3 -4
src/components/account.tsx
··· 14 14 import { agent, Login, retrieveSession, Sessions, setAgent } from "./login.jsx"; 15 15 import { Modal } from "./modal.jsx"; 16 16 17 - const AccountManager = () => { 17 + export const [sessions, setSessions] = createStore<Sessions>(); 18 + 19 + export const AccountManager = () => { 18 20 const [openManager, setOpenManager] = createSignal(false); 19 - const [sessions, setSessions] = createStore<Sessions>(); 20 21 const [avatars, setAvatars] = createStore<Record<Did, string>>(); 21 22 22 23 onMount(async () => { ··· 160 161 </> 161 162 ); 162 163 }; 163 - 164 - export { AccountManager };
+170 -62
src/components/create.tsx
··· 1 1 import { Client } from "@atcute/client"; 2 + import { Did } from "@atcute/lexicons"; 3 + import { getSession, OAuthUserAgent } from "@atcute/oauth-browser-client"; 2 4 import { remove } from "@mary/exif-rm"; 3 5 import { useNavigate, useParams } from "@solidjs/router"; 4 - import { createSignal, onCleanup, Show } from "solid-js"; 6 + import { createEffect, createSignal, For, onCleanup, onMount, Show } from "solid-js"; 5 7 import { Editor, editorView } from "../components/editor.jsx"; 6 8 import { agent } from "../components/login.jsx"; 7 9 import { setNotif } from "../layout.jsx"; 10 + import { sessions } from "./account.jsx"; 8 11 import { Button } from "./button.jsx"; 9 12 import { Modal } from "./modal.jsx"; 10 13 import { TextInput } from "./text-input.jsx"; ··· 18 21 const [openDialog, setOpenDialog] = createSignal(false); 19 22 const [notice, setNotice] = createSignal(""); 20 23 const [openUpload, setOpenUpload] = createSignal(false); 24 + const [validate, setValidate] = createSignal<boolean | undefined>(undefined); 21 25 let blobInput!: HTMLInputElement; 22 26 let formRef!: HTMLFormElement; 23 27 ··· 38 42 }; 39 43 }; 40 44 45 + const getValidateIcon = () => { 46 + return ( 47 + validate() === true ? "lucide--circle-check" 48 + : validate() === false ? "lucide--circle-x" 49 + : "lucide--circle" 50 + ); 51 + }; 52 + 53 + const getValidateLabel = () => { 54 + return ( 55 + validate() === true ? "True" 56 + : validate() === false ? "False" 57 + : "Unset" 58 + ); 59 + }; 60 + 61 + createEffect(() => { 62 + if (openDialog()) setValidate(undefined); 63 + }); 64 + 41 65 const createRecord = async (formData: FormData) => { 42 - const rpc = new Client({ handler: agent()! }); 66 + const repo = formData.get("repo")?.toString(); 67 + if (!repo) return; 68 + const rpc = new Client({ handler: new OAuthUserAgent(await getSession(repo as Did)) }); 43 69 const collection = formData.get("collection"); 44 70 const rkey = formData.get("rkey"); 45 - const validate = formData.get("validate")?.toString(); 46 71 let record: any; 47 72 try { 48 73 record = JSON.parse(editorView.state.doc.toString()); ··· 52 77 } 53 78 const res = await rpc.post("com.atproto.repo.createRecord", { 54 79 input: { 55 - repo: agent()!.sub, 80 + repo: repo as Did, 56 81 collection: collection ? collection.toString() : record.$type, 57 82 rkey: rkey?.toString().length ? rkey?.toString() : undefined, 58 83 record: record, 59 - validate: 60 - validate === "true" ? true 61 - : validate === "false" ? false 62 - : undefined, 84 + validate: validate(), 63 85 }, 64 86 }); 65 87 if (!res.ok) { ··· 71 93 navigate(`/${res.data.uri}`); 72 94 }; 73 95 74 - const editRecord = async (formData: FormData, recreate?: boolean) => { 96 + const editRecord = async (recreate?: boolean) => { 75 97 const record = editorView.state.doc.toString(); 76 - const validate = 77 - formData.get("validate")?.toString() === "true" ? true 78 - : formData.get("validate")?.toString() === "false" ? false 79 - : undefined; 80 98 if (!record) return; 81 99 const rpc = new Client({ handler: agent()! }); 82 100 try { ··· 85 103 const res = await rpc.post("com.atproto.repo.applyWrites", { 86 104 input: { 87 105 repo: agent()!.sub, 88 - validate: validate, 106 + validate: validate(), 89 107 writes: [ 90 108 { 91 109 collection: params.collection as `${string}.${string}.${string}`, ··· 112 130 collection: params.collection as `${string}.${string}.${string}`, 113 131 rkey: params.rkey, 114 132 record: editedRecord, 115 - validate: validate, 133 + validate: validate(), 116 134 }, 117 135 }); 118 136 if (!res.ok) { ··· 128 146 } 129 147 }; 130 148 149 + const dragBox = (box: HTMLDivElement) => { 150 + let currentBox: HTMLDivElement | null = null; 151 + let isDragging = false; 152 + let offsetX: number; 153 + let offsetY: number; 154 + 155 + const handleMouseDown = (e: MouseEvent) => { 156 + if (!(e.target instanceof HTMLElement)) return; 157 + 158 + const closestDraggable = e.target.closest("[data-draggable]") as HTMLElement; 159 + if (closestDraggable && closestDraggable !== box) return; 160 + 161 + if ( 162 + ["INPUT", "SELECT", "BUTTON", "LABEL"].includes(e.target.tagName) || 163 + e.target.closest("#editor, #close") 164 + ) 165 + return; 166 + 167 + e.preventDefault(); 168 + isDragging = true; 169 + box.classList.add("cursor-grabbing"); 170 + currentBox = box; 171 + 172 + const rect = box.getBoundingClientRect(); 173 + 174 + box.style.left = rect.left + "px"; 175 + box.style.top = rect.top + "px"; 176 + 177 + box.classList.remove("-translate-x-1/2"); 178 + 179 + offsetX = e.clientX - rect.left; 180 + offsetY = e.clientY - rect.top; 181 + }; 182 + 183 + const handleMouseMove = (e: MouseEvent) => { 184 + if (isDragging && box === currentBox) { 185 + let newLeft = e.clientX - offsetX; 186 + let newTop = e.clientY - offsetY; 187 + 188 + const boxWidth = box.offsetWidth; 189 + const boxHeight = box.offsetHeight; 190 + 191 + const viewportWidth = window.innerWidth; 192 + const viewportHeight = window.innerHeight; 193 + 194 + newLeft = Math.max(0, Math.min(newLeft, viewportWidth - boxWidth)); 195 + newTop = Math.max(0, Math.min(newTop, viewportHeight - boxHeight)); 196 + 197 + box.style.left = newLeft + "px"; 198 + box.style.top = newTop + "px"; 199 + } 200 + }; 201 + 202 + const handleMouseUp = () => { 203 + if (isDragging && box === currentBox) { 204 + isDragging = false; 205 + box.classList.remove("cursor-grabbing"); 206 + currentBox = null; 207 + } 208 + }; 209 + 210 + onMount(() => { 211 + box.addEventListener("mousedown", handleMouseDown); 212 + document.addEventListener("mousemove", handleMouseMove); 213 + document.addEventListener("mouseup", handleMouseUp); 214 + }); 215 + 216 + onCleanup(() => { 217 + box.removeEventListener("mousedown", handleMouseDown); 218 + document.removeEventListener("mousemove", handleMouseMove); 219 + document.removeEventListener("mouseup", handleMouseUp); 220 + }); 221 + }; 222 + 131 223 const FileUpload = (props: { file: File }) => { 132 224 const [uploading, setUploading] = createSignal(false); 133 225 const [error, setError] = createSignal(""); ··· 175 267 }; 176 268 177 269 return ( 178 - <div class="dark:bg-dark-300 dark:shadow-dark-700 absolute top-70 left-[50%] w-[20rem] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0"> 270 + <div 271 + data-draggable 272 + class="dark:bg-dark-300 dark:shadow-dark-700 absolute top-70 left-[50%] w-[20rem] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0" 273 + ref={dragBox} 274 + > 179 275 <h2 class="mb-2 font-semibold">Upload blob</h2> 180 276 <div class="flex flex-col gap-2 text-sm"> 181 277 <div class="flex flex-col gap-1"> ··· 229 325 return ( 230 326 <> 231 327 <Modal open={openDialog()} onClose={() => setOpenDialog(false)} closeOnClick={false}> 232 - <div class="dark:bg-dark-300 dark:shadow-dark-700 absolute top-16 left-[50%] w-screen -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 sm:w-xl lg:w-[48rem] dark:border-neutral-700 starting:opacity-0"> 328 + <div 329 + data-draggable 330 + class="dark:bg-dark-300 dark:shadow-dark-700 absolute top-16 left-[50%] w-screen -translate-x-1/2 cursor-grab rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 sm:w-xl lg:w-[48rem] dark:border-neutral-700 starting:opacity-0" 331 + ref={dragBox} 332 + > 233 333 <div class="mb-2 flex w-full justify-between"> 234 334 <div class="font-semibold"> 235 - <span>{props.create ? "Creating" : "Editing"} record</span> 335 + <span class="select-none">{props.create ? "Creating" : "Editing"} record</span> 236 336 </div> 237 337 <button 338 + id="close" 238 339 onclick={() => setOpenDialog(false)} 239 340 class="flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 240 341 > ··· 242 343 </button> 243 344 </div> 244 345 <form ref={formRef} class="flex flex-col gap-y-2"> 245 - <div class="flex w-fit flex-col gap-y-1 text-sm"> 246 - <Show when={props.create}> 247 - <div class="flex items-center gap-x-2"> 248 - <label for="collection" class="min-w-20 select-none"> 249 - Collection 250 - </label> 251 - <TextInput 252 - id="collection" 253 - name="collection" 254 - placeholder="Optional (default: $type)" 255 - class="w-[15rem]" 256 - /> 257 - </div> 258 - <div class="flex items-center gap-x-2"> 259 - <label for="rkey" class="min-w-20 select-none"> 260 - Record key 261 - </label> 262 - <TextInput 263 - id="rkey" 264 - name="rkey" 265 - placeholder="Optional (default: TID)" 266 - class="w-[15rem]" 267 - /> 268 - </div> 269 - </Show> 270 - <div class="flex items-center gap-x-2"> 271 - <label for="validate" class="min-w-20 select-none"> 272 - Validate 273 - </label> 346 + <Show when={props.create}> 347 + <div class="flex flex-wrap items-center gap-1 text-sm"> 348 + <span>at://</span> 274 349 <select 275 - name="validate" 276 - id="validate" 277 - class="dark:bg-dark-100 dark:shadow-dark-700 rounded-lg border-[0.5px] border-neutral-300 bg-white px-1 py-1 shadow-xs focus:outline-[1px] focus:outline-neutral-600 dark:border-neutral-600 dark:focus:outline-neutral-400" 350 + class="dark:bg-dark-100 dark:shadow-dark-700 max-w-[10rem] truncate rounded-lg border-[0.5px] border-neutral-300 bg-white px-1 py-1 shadow-xs select-none focus:outline-[1px] focus:outline-neutral-600 dark:border-neutral-600 dark:focus:outline-neutral-400" 351 + name="repo" 352 + id="repo" 278 353 > 279 - <option value="unset">Unset</option> 280 - <option value="true">True</option> 281 - <option value="false">False</option> 354 + <For each={Object.keys(sessions)}> 355 + {(session) => ( 356 + <option value={session} selected={session === agent()?.sub}> 357 + {sessions[session].handle ?? session} 358 + </option> 359 + )} 360 + </For> 282 361 </select> 362 + <span>/</span> 363 + <TextInput 364 + id="collection" 365 + name="collection" 366 + placeholder="Collection (default: $type)" 367 + class="w-[10rem] placeholder:text-xs lg:w-[13rem]" 368 + /> 369 + <span>/</span> 370 + <TextInput 371 + id="rkey" 372 + name="rkey" 373 + placeholder="Record key (default: TID)" 374 + class="w-[10rem] placeholder:text-xs lg:w-[13rem]" 375 + /> 283 376 </div> 284 - </div> 377 + </Show> 285 378 <Editor 286 379 content={JSON.stringify( 287 380 !props.create ? props.record ··· 296 389 <div class="text-sm text-red-500 dark:text-red-400">{notice()}</div> 297 390 </Show> 298 391 <div class="flex justify-between gap-2"> 299 - <div class="dark:hover:bg-dark-200 dark:shadow-dark-700 dark:active:bg-dark-100 flex w-fit rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 text-xs shadow-xs hover:bg-neutral-100 active:bg-neutral-200 dark:border-neutral-700 dark:bg-neutral-800"> 392 + <button 393 + type="button" 394 + class="dark:hover:bg-dark-200 dark:shadow-dark-700 dark:active:bg-dark-100 flex w-fit rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 text-xs shadow-xs hover:bg-neutral-100 active:bg-neutral-200 dark:border-neutral-700 dark:bg-neutral-800" 395 + > 300 396 <input 301 397 type="file" 302 398 id="blob" ··· 310 406 <span class="iconify lucide--upload"></span> 311 407 Upload 312 408 </label> 313 - </div> 409 + </button> 314 410 <Modal 315 411 open={openUpload()} 316 412 onClose={() => setOpenUpload(false)} ··· 319 415 <FileUpload file={blobInput.files![0]} /> 320 416 </Modal> 321 417 <div class="flex items-center justify-end gap-2"> 418 + <button 419 + type="button" 420 + class="flex items-center gap-1 rounded-sm p-1 text-sm hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 421 + onClick={() => 422 + setValidate( 423 + validate() === true ? false 424 + : validate() === false ? undefined 425 + : true, 426 + ) 427 + } 428 + > 429 + <Tooltip text={getValidateLabel()}> 430 + <span class={`iconify ${getValidateIcon()}`}></span> 431 + </Tooltip> 432 + <span>Validate</span> 433 + </button> 322 434 <Show when={!props.create}> 323 - <Button onClick={() => editRecord(new FormData(formRef), true)}> 324 - Recreate 325 - </Button> 435 + <Button onClick={() => editRecord(true)}>Recreate</Button> 326 436 </Show> 327 437 <Button 328 438 onClick={() => 329 - props.create ? 330 - createRecord(new FormData(formRef)) 331 - : editRecord(new FormData(formRef)) 439 + props.create ? createRecord(new FormData(formRef)) : editRecord() 332 440 } 333 441 > 334 442 {props.create ? "Create" : "Edit"}
+2 -1
src/components/editor.tsx
··· 57 57 return ( 58 58 <div 59 59 ref={editorDiv} 60 - class="dark:shadow-dark-700 border-[0.5px] border-neutral-300 shadow-xs dark:border-neutral-700" 60 + id="editor" 61 + class="dark:shadow-dark-700 cursor-auto border-[0.5px] border-neutral-300 shadow-xs dark:border-neutral-700" 61 62 ></div> 62 63 ); 63 64 };
+17 -19
src/components/login.tsx
··· 64 64 65 65 return ( 66 66 <form class="flex flex-col gap-y-2 px-1" onsubmit={(e) => e.preventDefault()}> 67 - <div class="flex items-center gap-1"> 68 - <label for="handle" class="hidden"> 69 - Add account 70 - </label> 71 - <div class="dark:bg-dark-100 dark:shadow-dark-700 flex grow items-center gap-2 rounded-lg border-[0.5px] border-neutral-300 bg-white px-2 shadow-xs focus-within:outline-[1px] focus-within:outline-neutral-600 dark:border-neutral-600 dark:focus-within:outline-neutral-400"> 72 - <label 73 - for="handle" 74 - class="iconify lucide--user-round-plus text-neutral-500 dark:text-neutral-400" 75 - ></label> 76 - <input 77 - type="text" 78 - spellcheck={false} 79 - placeholder="user.bsky.social" 80 - id="handle" 81 - class="grow py-1 select-none placeholder:text-sm focus:outline-none" 82 - onInput={(e) => setLoginInput(e.currentTarget.value)} 83 - /> 84 - </div> 67 + <label for="handle" class="hidden"> 68 + Add account 69 + </label> 70 + <div class="dark:bg-dark-100 dark:shadow-dark-700 flex grow items-center gap-2 rounded-lg border-[0.5px] border-neutral-300 bg-white px-2 shadow-xs focus-within:outline-[1px] focus-within:outline-neutral-600 dark:border-neutral-600 dark:focus-within:outline-neutral-400"> 71 + <label 72 + for="handle" 73 + class="iconify lucide--user-round-plus text-neutral-500 dark:text-neutral-400" 74 + ></label> 75 + <input 76 + type="text" 77 + spellcheck={false} 78 + placeholder="user.bsky.social" 79 + id="handle" 80 + class="grow py-1 select-none placeholder:text-sm focus:outline-none" 81 + onInput={(e) => setLoginInput(e.currentTarget.value)} 82 + /> 85 83 <button 86 84 onclick={() => login(loginInput())} 87 - class="flex items-center rounded-lg p-1.5 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 85 + class="flex items-center rounded-lg p-1 hover:bg-neutral-100 active:bg-neutral-200 dark:hover:bg-neutral-600 dark:active:bg-neutral-500" 88 86 > 89 87 <span class="iconify lucide--log-in"></span> 90 88 </button>
+18 -25
src/components/navbar.tsx
··· 5 5 import Tooltip from "./tooltip"; 6 6 7 7 export const [pds, setPDS] = createSignal<string>(); 8 - export const [isLabeler, setIsLabeler] = createSignal(false); 9 8 10 9 export const NavBar = (props: { params: Params }) => { 11 10 const location = useLocation(); 12 11 const [handle, setHandle] = createSignal(props.params.repo); 13 12 const [showHandle, setShowHandle] = createSignal(localStorage.showHandle === "true"); 14 13 15 - createEffect(async () => { 14 + createEffect(() => { 16 15 if (pds() !== undefined && props.params.repo) { 17 16 const hdl = 18 17 didDocCache[props.params.repo]?.alsoKnownAs ··· 23 22 }); 24 23 25 24 return ( 26 - <nav class="flex w-full flex-col px-2 text-sm wrap-anywhere"> 25 + <nav class="flex w-full flex-col px-2 wrap-anywhere"> 27 26 <div class="relative flex items-center justify-between gap-1"> 28 - <div class="flex min-h-[1.25rem] basis-full items-center gap-2"> 27 + <div class="flex min-h-[1.5rem] basis-full items-center gap-2"> 29 28 <Tooltip text="PDS"> 30 - <span class="iconify lucide--hard-drive shrink-0 text-base"></span> 29 + <span class="iconify lucide--hard-drive shrink-0"></span> 31 30 </Tooltip> 32 31 <Show when={pds()}> 33 - <Show when={props.params.repo}> 32 + <Show when={props.params.repo} fallback={<span>{pds()}</span>}> 34 33 <A 35 34 end 36 35 href={pds()!} ··· 39 38 {pds()} 40 39 </A> 41 40 </Show> 42 - <Show when={!props.params.repo}> 43 - <span>{pds()}</span> 44 - </Show> 45 41 </Show> 46 42 </div> 47 43 <Show when={props.params.repo}> 48 44 <MenuProvider> 49 45 <DropdownMenu 50 - icon="lucide--copy text-base" 51 - buttonClass="rounded p-0.5" 46 + icon="lucide--copy" 47 + buttonClass="rounded p-1" 52 48 menuClass="top-6 p-2 text-xs" 53 49 > 54 50 <Show when={pds()}> ··· 65 61 </div> 66 62 <div class="flex flex-col flex-wrap"> 67 63 <Show when={props.params.repo}> 68 - <div class="relative mt-1 flex items-center justify-between gap-1"> 64 + <div class="relative flex items-center justify-between gap-1"> 69 65 <div class="flex basis-full items-center gap-2"> 70 66 <Tooltip text="Repository"> 71 - <span class="iconify lucide--book-user text-base"></span> 67 + <span class="iconify lucide--book-user"></span> 72 68 </Tooltip> 73 69 {props.params.collection || location.pathname.includes("/labels") ? 74 70 <A ··· 82 78 </div> 83 79 <Tooltip text={showHandle() ? "Show DID" : "Show handle"}> 84 80 <button 85 - class="flex items-center rounded p-0.5 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 81 + class="flex items-center rounded p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 86 82 onclick={() => { 87 83 localStorage.showHandle = !showHandle(); 88 84 setShowHandle(!showHandle()); 89 85 }} 90 86 > 91 87 <span 92 - class={`iconify shrink-0 text-base transition-transform duration-400 ${showHandle() ? "rotate-y-180" : ""} lucide--arrow-left-right`} 88 + class={`iconify shrink-0 transition-transform duration-400 ${showHandle() ? "rotate-y-180" : ""} lucide--arrow-left-right`} 93 89 ></span> 94 90 </button> 95 91 </Tooltip> ··· 101 97 (props.params.repo in labelerCache || location.pathname.endsWith("/labels")) 102 98 } 103 99 > 104 - <div class="mt-1 flex items-center gap-2"> 105 - <span class="iconify lucide--tag text-base"></span> 100 + <div class="flex items-center gap-2"> 101 + <span class="iconify lucide--tag"></span> 106 102 <A 107 103 end 108 104 href={`/at://${props.params.repo}/labels`} ··· 113 109 </div> 114 110 </Show> 115 111 <Show when={props.params.collection}> 116 - <div class="mt-1 flex items-center gap-2"> 112 + <div class="flex items-center gap-2"> 117 113 <Tooltip text="Collection"> 118 - <span class="iconify lucide--folder-open text-base"></span> 114 + <span class="iconify lucide--folder-open"></span> 119 115 </Tooltip> 120 - <Show when={props.params.rkey}> 116 + <Show when={props.params.rkey} fallback={<span>{props.params.collection}</span>}> 121 117 <A 122 118 end 123 119 href={`/at://${props.params.repo}/${props.params.collection}`} ··· 126 122 {props.params.collection} 127 123 </A> 128 124 </Show> 129 - <Show when={!props.params.rkey}> 130 - <span>{props.params.collection}</span> 131 - </Show> 132 125 </div> 133 126 </Show> 134 127 <Show when={props.params.rkey}> 135 - <div class="mt-1 flex items-center gap-2"> 128 + <div class="flex items-center gap-2"> 136 129 <Tooltip text="Record"> 137 - <span class="iconify lucide--file-json text-base"></span> 130 + <span class="iconify lucide--file-json"></span> 138 131 </Tooltip> 139 132 <span>{props.params.rkey}</span> 140 133 </div>
+1
src/styles/index.css
··· 9 9 @theme { 10 10 --font-sans: "Inter", sans-serif; 11 11 --font-mono: "Roboto Mono", monospace; 12 + --font-pecita: "Pecita", serif; 12 13 13 14 --color-dark-50: oklch(40.91% 0 0); 14 15 --color-dark-100: oklch(35.62% 0 0);
+10
src/views/home.tsx
··· 54 54 <span class="iconify ri--bluesky"></span> 55 55 </a> 56 56 </div> 57 + <div class="text-center text-sm italic"> 58 + Made by{" "} 59 + <a 60 + href="https://juli.ee" 61 + class="font-pecita relative after:absolute after:bottom-0 after:left-0 after:h-[1px] after:w-0 after:bg-current after:transition-[width] after:duration-300 after:ease-out hover:after:w-full" 62 + > 63 + Juliet 64 + </a>{" "} 65 + with love 66 + </div> 57 67 </div> 58 68 ); 59 69 };
+7 -19
src/views/logs.tsx
··· 5 5 processIndexedEntryLog, 6 6 } from "@atcute/did-plc"; 7 7 import { createResource, createSignal, For, Show } from "solid-js"; 8 - import Tooltip from "../components/tooltip.jsx"; 9 8 import { localDateFromTimestamp } from "../utils/date.js"; 10 9 import { createOperationHistory, DiffEntry, groupBy } from "../utils/plc-logs.js"; 11 10 ··· 111 110 112 111 return ( 113 112 <div class="flex w-full flex-col gap-2 wrap-anywhere"> 114 - <div class="flex items-center justify-between"> 115 - <div class="flex items-center gap-1"> 116 - <div class="iconify lucide--filter" /> 117 - <div class="dark:shadow-dark-700 dark:bg-dark-300 flex w-fit items-center rounded-full border-[0.5px] border-neutral-300 bg-neutral-50 shadow-xs dark:border-neutral-700"> 118 - <FilterButton icon="iconify lucide--at-sign" event="handle" /> 119 - <FilterButton icon="iconify lucide--key-round" event="rotation_key" /> 120 - <FilterButton icon="iconify lucide--hard-drive" event="service" /> 121 - <FilterButton icon="iconify lucide--shield-check" event="verification_method" /> 122 - </div> 113 + <div class="flex items-center gap-1"> 114 + <div class="iconify lucide--filter" /> 115 + <div class="dark:shadow-dark-700 dark:bg-dark-300 flex w-fit items-center rounded-full border-[0.5px] border-neutral-300 bg-neutral-50 shadow-xs dark:border-neutral-700"> 116 + <FilterButton icon="iconify lucide--at-sign" event="handle" /> 117 + <FilterButton icon="iconify lucide--key-round" event="rotation_key" /> 118 + <FilterButton icon="iconify lucide--hard-drive" event="service" /> 119 + <FilterButton icon="iconify lucide--shield-check" event="verification_method" /> 123 120 </div> 124 - <Tooltip text="Audit log"> 125 - <a 126 - href={`${localStorage.plcDirectory ?? "https://plc.directory"}/${props.did}/log/audit`} 127 - target="_blank" 128 - class="-mr-1 flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 129 - > 130 - <span class="iconify lucide--external-link"></span> 131 - </a> 132 - </Tooltip> 133 121 </div> 134 122 <div class="flex flex-col gap-1 text-sm"> 135 123 <For each={plcOps()}>
+14 -18
src/views/pds.tsx
··· 5 5 import { A, useLocation, useParams } from "@solidjs/router"; 6 6 import { createResource, createSignal, For, Show } from "solid-js"; 7 7 import { Button } from "../components/button"; 8 + import { CopyMenu, DropdownMenu, MenuProvider, NavMenu } from "../components/dropdown"; 8 9 import { Modal } from "../components/modal"; 9 10 import { setPDS } from "../components/navbar"; 10 11 import Tooltip from "../components/tooltip"; 11 - import { addToClipboard } from "../utils/copy"; 12 12 import { localDateFromTimestamp } from "../utils/date"; 13 13 14 14 const LIMIT = 1000; ··· 133 133 <Tab tab="repos" label="Repositories" /> 134 134 <Tab tab="info" label="Info" /> 135 135 </div> 136 - <div class="flex gap-1"> 137 - <Tooltip text="Copy PDS"> 138 - <button 139 - onClick={() => addToClipboard(params.pds)} 140 - class="flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 141 - > 142 - <span class="iconify lucide--copy"></span> 143 - </button> 144 - </Tooltip> 145 - <Tooltip text="Firehose"> 146 - <A 136 + <MenuProvider> 137 + <DropdownMenu 138 + icon="lucide--ellipsis-vertical" 139 + buttonClass="rounded-sm p-1" 140 + menuClass="top-8 p-2 text-sm" 141 + > 142 + <CopyMenu copyContent={params.pds} label="Copy PDS" icon="lucide--copy" /> 143 + <NavMenu 147 144 href={`/firehose?instance=wss://${params.pds}`} 148 - class="flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 149 - > 150 - <span class="iconify lucide--radio-tower"></span> 151 - </A> 152 - </Tooltip> 153 - </div> 145 + label="Firehose" 146 + icon="lucide--radio-tower" 147 + /> 148 + </DropdownMenu> 149 + </MenuProvider> 154 150 </div> 155 151 <div class="flex flex-col gap-1 px-2"> 156 152 <Show when={!location.hash || location.hash === "#repos"}>
+1 -1
src/views/record.tsx
··· 188 188 </Show> 189 189 <MenuProvider> 190 190 <DropdownMenu 191 - icon="lucide--ellipsis-vertical " 191 + icon="lucide--ellipsis-vertical" 192 192 buttonClass="rounded-sm p-1" 193 193 menuClass="top-8 p-2 text-sm" 194 194 >
+23 -20
src/views/repo.tsx
··· 198 198 label="Jetstream" 199 199 icon="lucide--radio-tower" 200 200 /> 201 + <NavMenu 202 + href={ 203 + did.startsWith("did:plc") ? 204 + `${localStorage.plcDirectory ?? "https://plc.directory"}/${did}` 205 + : `https://${did.split("did:web:")[1]}/.well-known/did.json` 206 + } 207 + newTab 208 + label="DID Document" 209 + icon="lucide--external-link" 210 + /> 211 + <Show when={did.startsWith("did:plc")}> 212 + <NavMenu 213 + href={`${localStorage.plcDirectory ?? "https://plc.directory"}/${did}/log/audit`} 214 + newTab 215 + label="Audit Log" 216 + icon="lucide--external-link" 217 + /> 218 + </Show> 201 219 <Show when={error()?.length === 0 || error() === undefined}> 202 220 <ActionMenu 203 221 label="Export Repo" ··· 289 307 <Show when={didDoc()}> 290 308 {(didDocument) => ( 291 309 <div class="flex flex-col gap-y-1 wrap-anywhere"> 292 - <div class="flex items-baseline justify-between gap-2"> 293 - <div> 294 - <div class="flex items-center gap-1"> 295 - <div class="iconify lucide--id-card" /> 296 - <p class="font-semibold">ID</p> 297 - </div> 298 - <div class="text-sm">{didDocument().id}</div> 310 + <div> 311 + <div class="flex items-center gap-1"> 312 + <div class="iconify lucide--id-card" /> 313 + <p class="font-semibold">ID</p> 299 314 </div> 300 - <Tooltip text="DID document"> 301 - <a 302 - href={ 303 - did.startsWith("did:plc") ? 304 - `${localStorage.plcDirectory ?? "https://plc.directory"}/${did}` 305 - : `https://${did.split("did:web:")[1]}/.well-known/did.json` 306 - } 307 - target="_blank" 308 - class="-mr-1 flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 309 - > 310 - <span class="iconify lucide--external-link"></span> 311 - </a> 312 - </Tooltip> 315 + <div class="text-sm">{didDocument().id}</div> 313 316 </div> 314 317 <div> 315 318 <div class="flex items-center gap-1">