A web app for writing and sharing 301+ character Bluesky posts.

Successful push to PDS

Minito ed1f33e4 9830f505

+746 -28
+454 -1
package-lock.json
··· 9 "version": "0.0.0", 10 "dependencies": { 11 "@atproto/api": "^0.16.11", 12 "@atproto/oauth-client-browser": "^0.3.33", 13 "@tailwindcss/vite": "^4.1.14", 14 "react": "^19.1.1", 15 "react-dom": "^19.1.1", 16 - "tailwindcss": "^4.1.14" 17 }, 18 "devDependencies": { 19 "@eslint/js": "^9.36.0", ··· 146 "multiformats": "^9.9.0", 147 "tlds": "^1.234.0", 148 "zod": "^3.23.8" 149 } 150 }, 151 "node_modules/@atproto/common-web": { ··· 743 "node": ">=6.9.0" 744 } 745 }, 746 "node_modules/@esbuild/aix-ppc64": { 747 "version": "0.25.11", 748 "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", ··· 1403 }, 1404 "funding": { 1405 "url": "https://github.com/sponsors/sindresorhus" 1406 } 1407 }, 1408 "node_modules/@isaacs/fs-minipass": { ··· 2644 "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" 2645 } 2646 }, 2647 "node_modules/acorn": { 2648 "version": "8.15.0", 2649 "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", ··· 2744 "node": ">=4" 2745 } 2746 }, 2747 "node_modules/await-lock": { 2748 "version": "2.2.2", 2749 "resolved": "https://registry.npmjs.org/await-lock/-/await-lock-2.2.2.tgz", ··· 2780 "dev": true, 2781 "license": "MIT" 2782 }, 2783 "node_modules/baseline-browser-mapping": { 2784 "version": "2.8.18", 2785 "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.18.tgz", ··· 2861 "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" 2862 } 2863 }, 2864 "node_modules/callsites": { 2865 "version": "3.1.0", 2866 "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", ··· 2891 } 2892 ], 2893 "license": "CC-BY-4.0" 2894 }, 2895 "node_modules/chalk": { 2896 "version": "4.1.2", ··· 3366 "node": ">=0.10.0" 3367 } 3368 }, 3369 "node_modules/exsolve": { 3370 "version": "1.0.7", 3371 "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", ··· 3423 "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", 3424 "dev": true, 3425 "license": "MIT" 3426 }, 3427 "node_modules/fastq": { 3428 "version": "1.19.1", ··· 3582 "engines": { 3583 "node": ">=8" 3584 } 3585 }, 3586 "node_modules/ignore": { 3587 "version": "5.3.2", ··· 4221 "dev": true, 4222 "license": "MIT" 4223 }, 4224 "node_modules/node-releases": { 4225 "version": "2.0.25", 4226 "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.25.tgz", ··· 4236 "license": "MIT", 4237 "engines": { 4238 "node": ">=0.10.0" 4239 } 4240 }, 4241 "node_modules/optionator": { ··· 4354 "url": "https://github.com/sponsors/jonschlinkert" 4355 } 4356 }, 4357 "node_modules/pkg-types": { 4358 "version": "2.3.0", 4359 "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", ··· 4420 "url": "https://github.com/prettier/prettier?sponsor=1" 4421 } 4422 }, 4423 "node_modules/punycode": { 4424 "version": "2.3.1", 4425 "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", ··· 4468 ], 4469 "license": "MIT" 4470 }, 4471 "node_modules/react": { 4472 "version": "19.2.0", 4473 "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", ··· 4499 "node": ">=0.10.0" 4500 } 4501 }, 4502 "node_modules/readdirp": { 4503 "version": "3.6.0", 4504 "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", ··· 4510 }, 4511 "engines": { 4512 "node": ">=8.10.0" 4513 } 4514 }, 4515 "node_modules/recast": { ··· 4635 "queue-microtask": "^1.2.2" 4636 } 4637 }, 4638 "node_modules/scheduler": { 4639 "version": "0.27.0", 4640 "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", ··· 4704 "node": ">=8" 4705 } 4706 }, 4707 "node_modules/source-map": { 4708 "version": "0.7.6", 4709 "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", ··· 4723 "node": ">=0.10.0" 4724 } 4725 }, 4726 "node_modules/strip-json-comments": { 4727 "version": "3.1.1", 4728 "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", ··· 4811 "license": "BlueOak-1.0.0", 4812 "engines": { 4813 "node": ">=18" 4814 } 4815 }, 4816 "node_modules/tiny-invariant": { ··· 4952 }, 4953 "engines": { 4954 "node": ">= 0.8.0" 4955 } 4956 }, 4957 "node_modules/typescript": { ··· 5266 "license": "MIT", 5267 "peerDependencies": { 5268 "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 5269 } 5270 }, 5271 "node_modules/vite": {
··· 9 "version": "0.0.0", 10 "dependencies": { 11 "@atproto/api": "^0.16.11", 12 + "@atproto/common": "^0.4.12", 13 "@atproto/oauth-client-browser": "^0.3.33", 14 "@tailwindcss/vite": "^4.1.14", 15 "react": "^19.1.1", 16 "react-dom": "^19.1.1", 17 + "tailwindcss": "^4.1.14", 18 }, 19 "devDependencies": { 20 "@eslint/js": "^9.36.0", ··· 147 "multiformats": "^9.9.0", 148 "tlds": "^1.234.0", 149 "zod": "^3.23.8" 150 + } 151 + }, 152 + "node_modules/@atproto/common": { 153 + "version": "0.4.12", 154 + "resolved": "https://registry.npmjs.org/@atproto/common/-/common-0.4.12.tgz", 155 + "integrity": "sha512-NC+TULLQiqs6MvNymhQS5WDms3SlbIKGLf4n33tpftRJcalh507rI+snbcUb7TLIkKw7VO17qMqxEXtIdd5auQ==", 156 + "license": "MIT", 157 + "dependencies": { 158 + "@atproto/common-web": "^0.4.3", 159 + "@ipld/dag-cbor": "^7.0.3", 160 + "cbor-x": "^1.5.1", 161 + "iso-datestring-validator": "^2.2.2", 162 + "multiformats": "^9.9.0", 163 + "pino": "^8.21.0" 164 + }, 165 + "engines": { 166 + "node": ">=18.7.0" 167 } 168 }, 169 "node_modules/@atproto/common-web": { ··· 761 "node": ">=6.9.0" 762 } 763 }, 764 + "node_modules/@cbor-extract/cbor-extract-darwin-arm64": { 765 + "version": "2.2.0", 766 + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-arm64/-/cbor-extract-darwin-arm64-2.2.0.tgz", 767 + "integrity": "sha512-P7swiOAdF7aSi0H+tHtHtr6zrpF3aAq/W9FXx5HektRvLTM2O89xCyXF3pk7pLc7QpaY7AoaE8UowVf9QBdh3w==", 768 + "cpu": [ 769 + "arm64" 770 + ], 771 + "license": "MIT", 772 + "optional": true, 773 + "os": [ 774 + "darwin" 775 + ] 776 + }, 777 + "node_modules/@cbor-extract/cbor-extract-darwin-x64": { 778 + "version": "2.2.0", 779 + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-x64/-/cbor-extract-darwin-x64-2.2.0.tgz", 780 + "integrity": "sha512-1liF6fgowph0JxBbYnAS7ZlqNYLf000Qnj4KjqPNW4GViKrEql2MgZnAsExhY9LSy8dnvA4C0qHEBgPrll0z0w==", 781 + "cpu": [ 782 + "x64" 783 + ], 784 + "license": "MIT", 785 + "optional": true, 786 + "os": [ 787 + "darwin" 788 + ] 789 + }, 790 + "node_modules/@cbor-extract/cbor-extract-linux-arm": { 791 + "version": "2.2.0", 792 + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm/-/cbor-extract-linux-arm-2.2.0.tgz", 793 + "integrity": "sha512-QeBcBXk964zOytiedMPQNZr7sg0TNavZeuUCD6ON4vEOU/25+pLhNN6EDIKJ9VLTKaZ7K7EaAriyYQ1NQ05s/Q==", 794 + "cpu": [ 795 + "arm" 796 + ], 797 + "license": "MIT", 798 + "optional": true, 799 + "os": [ 800 + "linux" 801 + ] 802 + }, 803 + "node_modules/@cbor-extract/cbor-extract-linux-arm64": { 804 + "version": "2.2.0", 805 + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm64/-/cbor-extract-linux-arm64-2.2.0.tgz", 806 + "integrity": "sha512-rQvhNmDuhjTVXSPFLolmQ47/ydGOFXtbR7+wgkSY0bdOxCFept1hvg59uiLPT2fVDuJFuEy16EImo5tE2x3RsQ==", 807 + "cpu": [ 808 + "arm64" 809 + ], 810 + "license": "MIT", 811 + "optional": true, 812 + "os": [ 813 + "linux" 814 + ] 815 + }, 816 + "node_modules/@cbor-extract/cbor-extract-linux-x64": { 817 + "version": "2.2.0", 818 + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-x64/-/cbor-extract-linux-x64-2.2.0.tgz", 819 + "integrity": "sha512-cWLAWtT3kNLHSvP4RKDzSTX9o0wvQEEAj4SKvhWuOVZxiDAeQazr9A+PSiRILK1VYMLeDml89ohxCnUNQNQNCw==", 820 + "cpu": [ 821 + "x64" 822 + ], 823 + "license": "MIT", 824 + "optional": true, 825 + "os": [ 826 + "linux" 827 + ] 828 + }, 829 + "node_modules/@cbor-extract/cbor-extract-win32-x64": { 830 + "version": "2.2.0", 831 + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-win32-x64/-/cbor-extract-win32-x64-2.2.0.tgz", 832 + "integrity": "sha512-l2M+Z8DO2vbvADOBNLbbh9y5ST1RY5sqkWOg/58GkUPBYou/cuNZ68SGQ644f1CvZ8kcOxyZtw06+dxWHIoN/w==", 833 + "cpu": [ 834 + "x64" 835 + ], 836 + "license": "MIT", 837 + "optional": true, 838 + "os": [ 839 + "win32" 840 + ] 841 + }, 842 "node_modules/@esbuild/aix-ppc64": { 843 "version": "0.25.11", 844 "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", ··· 1499 }, 1500 "funding": { 1501 "url": "https://github.com/sponsors/sindresorhus" 1502 + } 1503 + }, 1504 + "node_modules/@ipld/dag-cbor": { 1505 + "version": "7.0.3", 1506 + "resolved": "https://registry.npmjs.org/@ipld/dag-cbor/-/dag-cbor-7.0.3.tgz", 1507 + "integrity": "sha512-1VVh2huHsuohdXC1bGJNE8WR72slZ9XE2T3wbBBq31dm7ZBatmKLLxrB+XAqafxfRFjv08RZmj/W/ZqaM13AuA==", 1508 + "license": "(Apache-2.0 AND MIT)", 1509 + "dependencies": { 1510 + "cborg": "^1.6.0", 1511 + "multiformats": "^9.5.4" 1512 } 1513 }, 1514 "node_modules/@isaacs/fs-minipass": { ··· 2750 "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" 2751 } 2752 }, 2753 + "node_modules/abort-controller": { 2754 + "version": "3.0.0", 2755 + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 2756 + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 2757 + "license": "MIT", 2758 + "dependencies": { 2759 + "event-target-shim": "^5.0.0" 2760 + }, 2761 + "engines": { 2762 + "node": ">=6.5" 2763 + } 2764 + }, 2765 "node_modules/acorn": { 2766 "version": "8.15.0", 2767 "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", ··· 2862 "node": ">=4" 2863 } 2864 }, 2865 + "node_modules/atomic-sleep": { 2866 + "version": "1.0.0", 2867 + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", 2868 + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", 2869 + "license": "MIT", 2870 + "engines": { 2871 + "node": ">=8.0.0" 2872 + } 2873 + }, 2874 "node_modules/await-lock": { 2875 "version": "2.2.2", 2876 "resolved": "https://registry.npmjs.org/await-lock/-/await-lock-2.2.2.tgz", ··· 2907 "dev": true, 2908 "license": "MIT" 2909 }, 2910 + "node_modules/base64-js": { 2911 + "version": "1.5.1", 2912 + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 2913 + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 2914 + "funding": [ 2915 + { 2916 + "type": "github", 2917 + "url": "https://github.com/sponsors/feross" 2918 + }, 2919 + { 2920 + "type": "patreon", 2921 + "url": "https://www.patreon.com/feross" 2922 + }, 2923 + { 2924 + "type": "consulting", 2925 + "url": "https://feross.org/support" 2926 + } 2927 + ], 2928 + "license": "MIT" 2929 + }, 2930 "node_modules/baseline-browser-mapping": { 2931 "version": "2.8.18", 2932 "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.18.tgz", ··· 3008 "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" 3009 } 3010 }, 3011 + "node_modules/buffer": { 3012 + "version": "6.0.3", 3013 + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", 3014 + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", 3015 + "funding": [ 3016 + { 3017 + "type": "github", 3018 + "url": "https://github.com/sponsors/feross" 3019 + }, 3020 + { 3021 + "type": "patreon", 3022 + "url": "https://www.patreon.com/feross" 3023 + }, 3024 + { 3025 + "type": "consulting", 3026 + "url": "https://feross.org/support" 3027 + } 3028 + ], 3029 + "license": "MIT", 3030 + "dependencies": { 3031 + "base64-js": "^1.3.1", 3032 + "ieee754": "^1.2.1" 3033 + } 3034 + }, 3035 "node_modules/callsites": { 3036 "version": "3.1.0", 3037 "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", ··· 3062 } 3063 ], 3064 "license": "CC-BY-4.0" 3065 + }, 3066 + "node_modules/cbor-extract": { 3067 + "version": "2.2.0", 3068 + "resolved": "https://registry.npmjs.org/cbor-extract/-/cbor-extract-2.2.0.tgz", 3069 + "integrity": "sha512-Ig1zM66BjLfTXpNgKpvBePq271BPOvu8MR0Jl080yG7Jsl+wAZunfrwiwA+9ruzm/WEdIV5QF/bjDZTqyAIVHA==", 3070 + "hasInstallScript": true, 3071 + "license": "MIT", 3072 + "optional": true, 3073 + "dependencies": { 3074 + "node-gyp-build-optional-packages": "5.1.1" 3075 + }, 3076 + "bin": { 3077 + "download-cbor-prebuilds": "bin/download-prebuilds.js" 3078 + }, 3079 + "optionalDependencies": { 3080 + "@cbor-extract/cbor-extract-darwin-arm64": "2.2.0", 3081 + "@cbor-extract/cbor-extract-darwin-x64": "2.2.0", 3082 + "@cbor-extract/cbor-extract-linux-arm": "2.2.0", 3083 + "@cbor-extract/cbor-extract-linux-arm64": "2.2.0", 3084 + "@cbor-extract/cbor-extract-linux-x64": "2.2.0", 3085 + "@cbor-extract/cbor-extract-win32-x64": "2.2.0" 3086 + } 3087 + }, 3088 + "node_modules/cbor-x": { 3089 + "version": "1.6.0", 3090 + "resolved": "https://registry.npmjs.org/cbor-x/-/cbor-x-1.6.0.tgz", 3091 + "integrity": "sha512-0kareyRwHSkL6ws5VXHEf8uY1liitysCVJjlmhaLG+IXLqhSaOO+t63coaso7yjwEzWZzLy8fJo06gZDVQM9Qg==", 3092 + "license": "MIT", 3093 + "optionalDependencies": { 3094 + "cbor-extract": "^2.2.0" 3095 + } 3096 + }, 3097 + "node_modules/cborg": { 3098 + "version": "1.10.2", 3099 + "resolved": "https://registry.npmjs.org/cborg/-/cborg-1.10.2.tgz", 3100 + "integrity": "sha512-b3tFPA9pUr2zCUiCfRd2+wok2/LBSNUMKOuRRok+WlvvAgEt/PlbgPTsZUcwCOs53IJvLgTp0eotwtosE6njug==", 3101 + "license": "Apache-2.0", 3102 + "bin": { 3103 + "cborg": "cli.js" 3104 + } 3105 }, 3106 "node_modules/chalk": { 3107 "version": "4.1.2", ··· 3577 "node": ">=0.10.0" 3578 } 3579 }, 3580 + "node_modules/event-target-shim": { 3581 + "version": "5.0.1", 3582 + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 3583 + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", 3584 + "license": "MIT", 3585 + "engines": { 3586 + "node": ">=6" 3587 + } 3588 + }, 3589 + "node_modules/events": { 3590 + "version": "3.3.0", 3591 + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", 3592 + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", 3593 + "license": "MIT", 3594 + "engines": { 3595 + "node": ">=0.8.x" 3596 + } 3597 + }, 3598 "node_modules/exsolve": { 3599 "version": "1.0.7", 3600 "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", ··· 3652 "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", 3653 "dev": true, 3654 "license": "MIT" 3655 + }, 3656 + "node_modules/fast-redact": { 3657 + "version": "3.5.0", 3658 + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", 3659 + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", 3660 + "license": "MIT", 3661 + "engines": { 3662 + "node": ">=6" 3663 + } 3664 }, 3665 "node_modules/fastq": { 3666 "version": "1.19.1", ··· 3820 "engines": { 3821 "node": ">=8" 3822 } 3823 + }, 3824 + "node_modules/ieee754": { 3825 + "version": "1.2.1", 3826 + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 3827 + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 3828 + "funding": [ 3829 + { 3830 + "type": "github", 3831 + "url": "https://github.com/sponsors/feross" 3832 + }, 3833 + { 3834 + "type": "patreon", 3835 + "url": "https://www.patreon.com/feross" 3836 + }, 3837 + { 3838 + "type": "consulting", 3839 + "url": "https://feross.org/support" 3840 + } 3841 + ], 3842 + "license": "BSD-3-Clause" 3843 }, 3844 "node_modules/ignore": { 3845 "version": "5.3.2", ··· 4479 "dev": true, 4480 "license": "MIT" 4481 }, 4482 + "node_modules/node-gyp-build-optional-packages": { 4483 + "version": "5.1.1", 4484 + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz", 4485 + "integrity": "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==", 4486 + "license": "MIT", 4487 + "optional": true, 4488 + "dependencies": { 4489 + "detect-libc": "^2.0.1" 4490 + }, 4491 + "bin": { 4492 + "node-gyp-build-optional-packages": "bin.js", 4493 + "node-gyp-build-optional-packages-optional": "optional.js", 4494 + "node-gyp-build-optional-packages-test": "build-test.js" 4495 + } 4496 + }, 4497 "node_modules/node-releases": { 4498 "version": "2.0.25", 4499 "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.25.tgz", ··· 4509 "license": "MIT", 4510 "engines": { 4511 "node": ">=0.10.0" 4512 + } 4513 + }, 4514 + "node_modules/on-exit-leak-free": { 4515 + "version": "2.1.2", 4516 + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", 4517 + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", 4518 + "license": "MIT", 4519 + "engines": { 4520 + "node": ">=14.0.0" 4521 } 4522 }, 4523 "node_modules/optionator": { ··· 4636 "url": "https://github.com/sponsors/jonschlinkert" 4637 } 4638 }, 4639 + "node_modules/pino": { 4640 + "version": "8.21.0", 4641 + "resolved": "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz", 4642 + "integrity": "sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==", 4643 + "license": "MIT", 4644 + "dependencies": { 4645 + "atomic-sleep": "^1.0.0", 4646 + "fast-redact": "^3.1.1", 4647 + "on-exit-leak-free": "^2.1.0", 4648 + "pino-abstract-transport": "^1.2.0", 4649 + "pino-std-serializers": "^6.0.0", 4650 + "process-warning": "^3.0.0", 4651 + "quick-format-unescaped": "^4.0.3", 4652 + "real-require": "^0.2.0", 4653 + "safe-stable-stringify": "^2.3.1", 4654 + "sonic-boom": "^3.7.0", 4655 + "thread-stream": "^2.6.0" 4656 + }, 4657 + "bin": { 4658 + "pino": "bin.js" 4659 + } 4660 + }, 4661 + "node_modules/pino-abstract-transport": { 4662 + "version": "1.2.0", 4663 + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", 4664 + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", 4665 + "license": "MIT", 4666 + "dependencies": { 4667 + "readable-stream": "^4.0.0", 4668 + "split2": "^4.0.0" 4669 + } 4670 + }, 4671 + "node_modules/pino-std-serializers": { 4672 + "version": "6.2.2", 4673 + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", 4674 + "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==", 4675 + "license": "MIT" 4676 + }, 4677 "node_modules/pkg-types": { 4678 "version": "2.3.0", 4679 "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", ··· 4740 "url": "https://github.com/prettier/prettier?sponsor=1" 4741 } 4742 }, 4743 + "node_modules/process": { 4744 + "version": "0.11.10", 4745 + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", 4746 + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", 4747 + "license": "MIT", 4748 + "engines": { 4749 + "node": ">= 0.6.0" 4750 + } 4751 + }, 4752 + "node_modules/process-warning": { 4753 + "version": "3.0.0", 4754 + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", 4755 + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==", 4756 + "license": "MIT" 4757 + }, 4758 "node_modules/punycode": { 4759 "version": "2.3.1", 4760 "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", ··· 4803 ], 4804 "license": "MIT" 4805 }, 4806 + "node_modules/quick-format-unescaped": { 4807 + "version": "4.0.4", 4808 + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", 4809 + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", 4810 + "license": "MIT" 4811 + }, 4812 "node_modules/react": { 4813 "version": "19.2.0", 4814 "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", ··· 4840 "node": ">=0.10.0" 4841 } 4842 }, 4843 + "node_modules/readable-stream": { 4844 + "version": "4.7.0", 4845 + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", 4846 + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", 4847 + "license": "MIT", 4848 + "dependencies": { 4849 + "abort-controller": "^3.0.0", 4850 + "buffer": "^6.0.3", 4851 + "events": "^3.3.0", 4852 + "process": "^0.11.10", 4853 + "string_decoder": "^1.3.0" 4854 + }, 4855 + "engines": { 4856 + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 4857 + } 4858 + }, 4859 "node_modules/readdirp": { 4860 "version": "3.6.0", 4861 "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", ··· 4867 }, 4868 "engines": { 4869 "node": ">=8.10.0" 4870 + } 4871 + }, 4872 + "node_modules/real-require": { 4873 + "version": "0.2.0", 4874 + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", 4875 + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", 4876 + "license": "MIT", 4877 + "engines": { 4878 + "node": ">= 12.13.0" 4879 } 4880 }, 4881 "node_modules/recast": { ··· 5001 "queue-microtask": "^1.2.2" 5002 } 5003 }, 5004 + "node_modules/safe-buffer": { 5005 + "version": "5.2.1", 5006 + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 5007 + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 5008 + "funding": [ 5009 + { 5010 + "type": "github", 5011 + "url": "https://github.com/sponsors/feross" 5012 + }, 5013 + { 5014 + "type": "patreon", 5015 + "url": "https://www.patreon.com/feross" 5016 + }, 5017 + { 5018 + "type": "consulting", 5019 + "url": "https://feross.org/support" 5020 + } 5021 + ], 5022 + "license": "MIT" 5023 + }, 5024 + "node_modules/safe-stable-stringify": { 5025 + "version": "2.5.0", 5026 + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", 5027 + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", 5028 + "license": "MIT", 5029 + "engines": { 5030 + "node": ">=10" 5031 + } 5032 + }, 5033 "node_modules/scheduler": { 5034 "version": "0.27.0", 5035 "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", ··· 5099 "node": ">=8" 5100 } 5101 }, 5102 + "node_modules/sonic-boom": { 5103 + "version": "3.8.1", 5104 + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz", 5105 + "integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==", 5106 + "license": "MIT", 5107 + "dependencies": { 5108 + "atomic-sleep": "^1.0.0" 5109 + } 5110 + }, 5111 "node_modules/source-map": { 5112 "version": "0.7.6", 5113 "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", ··· 5127 "node": ">=0.10.0" 5128 } 5129 }, 5130 + "node_modules/split2": { 5131 + "version": "4.2.0", 5132 + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", 5133 + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", 5134 + "license": "ISC", 5135 + "engines": { 5136 + "node": ">= 10.x" 5137 + } 5138 + }, 5139 + "node_modules/string_decoder": { 5140 + "version": "1.3.0", 5141 + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 5142 + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 5143 + "license": "MIT", 5144 + "dependencies": { 5145 + "safe-buffer": "~5.2.0" 5146 + } 5147 + }, 5148 "node_modules/strip-json-comments": { 5149 "version": "3.1.1", 5150 "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", ··· 5233 "license": "BlueOak-1.0.0", 5234 "engines": { 5235 "node": ">=18" 5236 + } 5237 + }, 5238 + "node_modules/thread-stream": { 5239 + "version": "2.7.0", 5240 + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz", 5241 + "integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==", 5242 + "license": "MIT", 5243 + "dependencies": { 5244 + "real-require": "^0.2.0" 5245 } 5246 }, 5247 "node_modules/tiny-invariant": { ··· 5383 }, 5384 "engines": { 5385 "node": ">= 0.8.0" 5386 + } 5387 + }, 5388 + "node_modules/typeid-js": { 5389 + "version": "1.2.0", 5390 + "resolved": "https://registry.npmjs.org/typeid-js/-/typeid-js-1.2.0.tgz", 5391 + "integrity": "sha512-t76ZucAnvGC60ea/HjVsB0TSoB0cw9yjnfurUgtInXQWUI/VcrlZGpO23KN3iSe8yOGUgb1zr7W7uEzJ3hSljA==", 5392 + "license": "Apache-2.0", 5393 + "dependencies": { 5394 + "uuid": "^10.0.0" 5395 } 5396 }, 5397 "node_modules/typescript": { ··· 5706 "license": "MIT", 5707 "peerDependencies": { 5708 "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 5709 + } 5710 + }, 5711 + "node_modules/uuid": { 5712 + "version": "10.0.0", 5713 + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", 5714 + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", 5715 + "funding": [ 5716 + "https://github.com/sponsors/broofa", 5717 + "https://github.com/sponsors/ctavan" 5718 + ], 5719 + "license": "MIT", 5720 + "bin": { 5721 + "uuid": "dist/bin/uuid" 5722 } 5723 }, 5724 "node_modules/vite": {
+3 -1
package.json
··· 11 }, 12 "dependencies": { 13 "@atproto/api": "^0.16.11", 14 "@atproto/oauth-client-browser": "^0.3.33", 15 "@tailwindcss/vite": "^4.1.14", 16 "react": "^19.1.1", 17 "react-dom": "^19.1.1", 18 - "tailwindcss": "^4.1.14" 19 }, 20 "devDependencies": { 21 "@eslint/js": "^9.36.0",
··· 11 }, 12 "dependencies": { 13 "@atproto/api": "^0.16.11", 14 + "@atproto/common": "^0.4.12", 15 "@atproto/oauth-client-browser": "^0.3.33", 16 "@tailwindcss/vite": "^4.1.14", 17 "react": "^19.1.1", 18 "react-dom": "^19.1.1", 19 + "tailwindcss": "^4.1.14", 20 + "typeid-js": "^1.2.0" 21 }, 22 "devDependencies": { 23 "@eslint/js": "^9.36.0",
+19 -2
src/App.tsx
··· 2 import './App.css' 3 import Login from './components/Login' 4 import { UnifiedAuthProvider } from './providers/UnifiedAuthProvider' 5 6 function App() { 7 const [postText, setPostText] = useState('') 8 const charCount = postText.length 9 10 - const handleSubmit = async (e: React.FormEvent) => { 11 - e.preventDefault(); 12 // setError(null); 13 // try { 14 // localStorage.setItem("lastHandle", user); ··· 53 </div> 54 55 <button 56 type='submit' 57 disabled={charCount < 300} 58 style={{ padding: '0.625rem 1.5rem', borderRadius: '0.5rem', fontSize: '0.875rem', fontWeight: '600', border: 'none', cursor: charCount === 0 ? 'not-allowed' : 'pointer', transition: 'background-color 0.2s', backgroundColor: charCount === 0 ? '#6b7280' : '#2563eb', color: 'white' }}
··· 2 import './App.css' 3 import Login from './components/Login' 4 import { UnifiedAuthProvider } from './providers/UnifiedAuthProvider' 5 + import { AtpAgent } from '@atproto/api' 6 + // import { TID } from '@atproto/common' 7 8 function App() { 9 + const agent = new AtpAgent({ service: 'https://skeetlonger.app' }) 10 + 11 const [postText, setPostText] = useState('') 12 const charCount = postText.length 13 14 + const handleSubmit = async () => { 15 + // Generate a time-based key for our record 16 + const rkey = "self" 17 + 18 + await agent.com.atproto.repo.putRecord({ 19 + repo: agent.assertDid, // The user 20 + collection: 'app.skeetlonger.post', // The collection 21 + rkey, // The record key 22 + record: { // The record value 23 + post: postText, 24 + createdAt: new Date().toISOString() 25 + } 26 + }) 27 + 28 // setError(null); 29 // try { 30 // localStorage.setItem("lastHandle", user); ··· 69 </div> 70 71 <button 72 + onClick={() => handleSubmit()} 73 type='submit' 74 disabled={charCount < 300} 75 style={{ padding: '0.625rem 1.5rem', borderRadius: '0.5rem', fontSize: '0.875rem', fontWeight: '600', border: 'none', cursor: charCount === 0 ? 'not-allowed' : 'pointer', transition: 'background-color 0.2s', backgroundColor: charCount === 0 ? '#6b7280' : '#2563eb', color: 'white' }}
+14 -3
src/main.tsx
··· 1 import { StrictMode } from 'react' 2 import { createRoot } from 'react-dom/client' 3 import './index.css' 4 - import App from './App.tsx' 5 6 createRoot(document.getElementById('root')!).render( 7 <StrictMode> 8 - <App /> 9 </StrictMode>, 10 - )
··· 1 import { StrictMode } from 'react' 2 import { createRoot } from 'react-dom/client' 3 import './index.css' 4 + import { RouterProvider, createRouter } from '@tanstack/react-router' 5 + import { routeTree } from './routeTree.gen' 6 + 7 + // Create a new router instance 8 + const router = createRouter({ routeTree }) 9 + 10 + // Register the router instance for type safety 11 + declare module '@tanstack/react-router' { 12 + interface Register { 13 + router: typeof router 14 + } 15 + } 16 17 createRoot(document.getElementById('root')!).render( 18 <StrictMode> 19 + <RouterProvider router={router} /> 20 </StrictMode>, 21 + )
+50 -8
src/routeTree.gen.ts
··· 9 // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. 10 11 import { Route as rootRouteImport } from './routes/__root' 12 13 - export interface FileRoutesByFullPath {} 14 - export interface FileRoutesByTo {} 15 export interface FileRoutesById { 16 __root__: typeof rootRouteImport 17 } 18 export interface FileRouteTypes { 19 fileRoutesByFullPath: FileRoutesByFullPath 20 - fullPaths: never 21 fileRoutesByTo: FileRoutesByTo 22 - to: never 23 - id: '__root__' 24 fileRoutesById: FileRoutesById 25 } 26 - export interface RootRouteChildren {} 27 28 declare module '@tanstack/react-router' { 29 - interface FileRoutesByPath {} 30 } 31 32 - const rootRouteChildren: RootRouteChildren = {} 33 export const routeTree = rootRouteImport 34 ._addFileChildren(rootRouteChildren) 35 ._addFileTypes<FileRouteTypes>()
··· 9 // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. 10 11 import { Route as rootRouteImport } from './routes/__root' 12 + import { Route as AboutRouteImport } from './routes/about' 13 + import { Route as IndexRouteImport } from './routes/index' 14 15 + const AboutRoute = AboutRouteImport.update({ 16 + id: '/about', 17 + path: '/about', 18 + getParentRoute: () => rootRouteImport, 19 + } as any) 20 + const IndexRoute = IndexRouteImport.update({ 21 + id: '/', 22 + path: '/', 23 + getParentRoute: () => rootRouteImport, 24 + } as any) 25 + 26 + export interface FileRoutesByFullPath { 27 + '/': typeof IndexRoute 28 + '/about': typeof AboutRoute 29 + } 30 + export interface FileRoutesByTo { 31 + '/': typeof IndexRoute 32 + '/about': typeof AboutRoute 33 + } 34 export interface FileRoutesById { 35 __root__: typeof rootRouteImport 36 + '/': typeof IndexRoute 37 + '/about': typeof AboutRoute 38 } 39 export interface FileRouteTypes { 40 fileRoutesByFullPath: FileRoutesByFullPath 41 + fullPaths: '/' | '/about' 42 fileRoutesByTo: FileRoutesByTo 43 + to: '/' | '/about' 44 + id: '__root__' | '/' | '/about' 45 fileRoutesById: FileRoutesById 46 } 47 + export interface RootRouteChildren { 48 + IndexRoute: typeof IndexRoute 49 + AboutRoute: typeof AboutRoute 50 + } 51 52 declare module '@tanstack/react-router' { 53 + interface FileRoutesByPath { 54 + '/about': { 55 + id: '/about' 56 + path: '/about' 57 + fullPath: '/about' 58 + preLoaderRoute: typeof AboutRouteImport 59 + parentRoute: typeof rootRouteImport 60 + } 61 + '/': { 62 + id: '/' 63 + path: '/' 64 + fullPath: '/' 65 + preLoaderRoute: typeof IndexRouteImport 66 + parentRoute: typeof rootRouteImport 67 + } 68 + } 69 } 70 71 + const rootRouteChildren: RootRouteChildren = { 72 + IndexRoute: IndexRoute, 73 + AboutRoute: AboutRoute, 74 + } 75 export const routeTree = rootRouteImport 76 ._addFileChildren(rootRouteChildren) 77 ._addFileTypes<FileRouteTypes>()
+4 -13
src/routes/__root.tsx
··· 1 - import * as React from 'react' 2 import { Outlet, createRootRoute } from '@tanstack/react-router' 3 import { UnifiedAuthProvider } from '../providers/UnifiedAuthProvider' 4 - import Login from '../components/Login' 5 6 export const Route = createRootRoute({ 7 component: RootComponent, ··· 9 10 function RootComponent() { 11 return ( 12 - <React.Fragment> 13 - <h1>SkeetLonger</h1> 14 - 15 - <div className="flex items-center gap-2"> 16 - <UnifiedAuthProvider> 17 - <Login compact={false}></Login> 18 - </UnifiedAuthProvider> 19 - </div> 20 - <Outlet /> 21 - </React.Fragment> 22 ) 23 - }
··· 1 import { Outlet, createRootRoute } from '@tanstack/react-router' 2 import { UnifiedAuthProvider } from '../providers/UnifiedAuthProvider' 3 4 export const Route = createRootRoute({ 5 component: RootComponent, ··· 7 8 function RootComponent() { 9 return ( 10 + <UnifiedAuthProvider> 11 + <Outlet /> 12 + </UnifiedAuthProvider> 13 ) 14 + }
+102
src/routes/about.tsx
···
··· 1 + import { createFileRoute } from '@tanstack/react-router' 2 + import Login from '../components/Login' 3 + 4 + export const Route = createFileRoute('/about')({ 5 + component: AboutComponent, 6 + }) 7 + 8 + function AboutComponent() { 9 + return ( 10 + <> 11 + <div style={{ position: 'fixed', top: '1rem', right: '1rem', zIndex: 9999 }}> 12 + <Login compact={true} /> 13 + </div> 14 + 15 + <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', backgroundColor: '#242424' }}> 16 + <div style={{ width: '100%', maxWidth: '72rem' }}> 17 + {/* Title */} 18 + <h1 style={{ fontSize: '2.25rem', fontWeight: 'bold', textAlign: 'center', marginBottom: '0.5rem', color: '#f3f4f6' }}> 19 + About SkeetLonger 20 + </h1> 21 + <p style={{ textAlign: 'center', marginBottom: '2rem', color: '#9ca3af' }}> 22 + Learn more about posting longer content to Bluesky 23 + </p> 24 + 25 + {/* Content Card */} 26 + <div style={{ padding: '2rem', borderRadius: '0.75rem', boxShadow: '0 10px 15px -3px rgb(0 0 0 / 0.1)', border: '1px solid #374151', backgroundColor: '#1a1a1a' }}> 27 + 28 + {/* What is SkeetLonger */} 29 + <section style={{ marginBottom: '2rem' }}> 30 + <h2 style={{ fontSize: '1.5rem', fontWeight: '600', color: '#f3f4f6', marginBottom: '0.75rem' }}> 31 + What is SkeetLonger? 32 + </h2> 33 + <p style={{ lineHeight: '1.75', color: '#d1d5db' }}> 34 + SkeetLonger is a tool that allows you to post longer content to Bluesky, 35 + bypassing the standard 300-character limit. Perfect for sharing detailed 36 + thoughts, stories, or discussions without the constraints of short-form posts. 37 + </p> 38 + </section> 39 + 40 + {/* How does it work */} 41 + <section style={{ marginBottom: '2rem' }}> 42 + <h2 style={{ fontSize: '1.5rem', fontWeight: '600', color: '#f3f4f6', marginBottom: '0.75rem' }}> 43 + How does it work? 44 + </h2> 45 + <p style={{ lineHeight: '1.75', color: '#d1d5db' }}> 46 + SkeetLonger stores your longer posts (300+ characters) as custom records in the AT Protocol. 47 + These records are saved to your Personal Data Server (PDS) under the app.skeetlonger.post 48 + collection, allowing you to write and preserve content beyond Bluesky's standard character 49 + limits while maintaining full ownership of your data. 50 + </p> 51 + </section> 52 + 53 + {/* Privacy & Security */} 54 + <section style={{ marginBottom: '2rem' }}> 55 + <h2 style={{ fontSize: '1.5rem', fontWeight: '600', color: '#f3f4f6', marginBottom: '0.75rem' }}> 56 + Privacy & Security 57 + </h2> 58 + <p style={{ lineHeight: '1.75', color: '#d1d5db' }}> 59 + SkeetLonger uses OAuth authentication, which means your password is never 60 + shared with us. We only request the permissions necessary to post on your 61 + behalf. You can revoke access at any time through your Bluesky settings. 62 + </p> 63 + </section> 64 + 65 + {/* Open Source */} 66 + <section style={{ marginBottom: '2rem' }}> 67 + <h2 style={{ fontSize: '1.5rem', fontWeight: '600', color: '#f3f4f6', marginBottom: '0.75rem' }}> 68 + Open Source 69 + </h2> 70 + <p style={{ lineHeight: '1.75', color: '#d1d5db' }}> 71 + SkeetLonger is built with modern web technologies and is committed to 72 + transparency. The AT Protocol (Authenticated Transfer Protocol) powers 73 + Bluesky and enables tools like this to be built on top of the platform. 74 + </p> 75 + </section> 76 + 77 + {/* Button */} 78 + <div style={{ display: 'flex', justifyContent: 'center', paddingTop: '1rem', borderTop: '1px solid #374151' }}> 79 + <a 80 + href="/" 81 + style={{ 82 + display: 'inline-block', 83 + padding: '0.75rem 2rem', 84 + borderRadius: '0.5rem', 85 + backgroundColor: '#2563eb', 86 + color: 'white', 87 + fontWeight: '600', 88 + textDecoration: 'none', 89 + transition: 'background-color 0.2s' 90 + }} 91 + onMouseEnter={(e) => e.currentTarget.style.backgroundColor = '#1d4ed8'} 92 + onMouseLeave={(e) => e.currentTarget.style.backgroundColor = '#2563eb'} 93 + > 94 + Start Writing 95 + </a> 96 + </div> 97 + </div> 98 + </div> 99 + </div> 100 + </> 101 + ) 102 + }
+100
src/routes/index.tsx
···
··· 1 + import { createFileRoute } from '@tanstack/react-router' 2 + import { useState } from 'react' 3 + import '../App.css' 4 + import Login from '../components/Login' 5 + import { useAuth } from "../providers/UnifiedAuthProvider"; 6 + import { AtUri } from '@atproto/api'; 7 + 8 + export const Route = createFileRoute('/')({ 9 + component: RouteComponent, 10 + }) 11 + 12 + function RouteComponent() { 13 + const { agent } = useAuth(); 14 + 15 + const [postText, setPostText] = useState('') 16 + const charCount = postText.length 17 + 18 + const handleSubmit = async () => { 19 + if (!agent) { 20 + console.error("Agent not available"); 21 + return; 22 + } 23 + 24 + try { 25 + // Create the record and let the server generate the rkey (TID) 26 + const response = await agent.com.atproto.repo.createRecord({ 27 + repo: agent.assertDid, 28 + collection: 'app.skeetlonger.post', 29 + record: { 30 + post: postText, 31 + createdAt: new Date().toISOString() 32 + } 33 + }); 34 + 35 + // Parse the URI to extract the rkey 36 + const uri = new AtUri(response.data.uri); 37 + const rkey = uri.rkey; 38 + 39 + console.log("Record created successfully!"); 40 + console.log("Full URI:", response.data.uri); 41 + console.log("Extracted rkey:", rkey); 42 + console.log("CID:", response.data.cid); 43 + 44 + //setPostText(''); // Clear the textarea after posting 45 + } catch (error) { 46 + console.error("Failed to create record:", error); 47 + } 48 + }; 49 + 50 + return ( 51 + <> 52 + <div style={{ position: 'fixed', top: '1rem', right: '1rem', zIndex: 9999 }}> 53 + <Login compact={true} /> 54 + </div> 55 + 56 + <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: '5rem 1rem', backgroundColor: '#242424' }}> 57 + <div style={{ width: '100%', maxWidth: '72rem' }}> 58 + {/* Title */} 59 + <h1 style={{ fontSize: '2.25rem', fontWeight: 'bold', textAlign: 'center', marginBottom: '0.5rem', color: '#f3f4f6' }}> 60 + SkeetLonger 61 + </h1> 62 + <p style={{ textAlign: 'center', marginBottom: '2rem', color: '#9ca3af' }}> 63 + Post longer content to Bluesky 64 + </p> 65 + 66 + {/* Text Editor Card */} 67 + <div style={{ padding: '1.5rem', borderRadius: '0.75rem', boxShadow: '0 10px 15px -3px rgb(0 0 0 / 0.1)', border: '1px solid #e5e7eb', backgroundColor: '#1a1a1a', borderColor: '#374151' }}> 68 + <textarea 69 + value={postText} 70 + onChange={(e) => setPostText(e.target.value)} 71 + placeholder="What's on your mind? Write as much as you'd like..." 72 + style={{ width: '32rem', height: '32rem', padding: '0.75rem 1rem', borderRadius: '0.5rem', border: '1px solid #4b5563', fontSize: '1rem', lineHeight: '1.75', resize: 'none', backgroundColor: '#111827', color: '#f3f4f6', boxSizing: 'border-box' }} 73 + className="placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500" 74 + /> 75 + 76 + {/* Footer with character count and post button */} 77 + <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: '1rem' }}> 78 + <div style={{ fontSize: '0.875rem' }}> 79 + <span style={{ fontWeight: '500', color: charCount > 300 ? '#3b82f6' : '#9ca3af' }}> 80 + {charCount} characters 81 + </span> 82 + </div> 83 + 84 + <button 85 + onClick={() => handleSubmit()} 86 + type='submit' 87 + disabled={charCount < 300} 88 + style={{ padding: '0.625rem 1.5rem', borderRadius: '0.5rem', fontSize: '0.875rem', fontWeight: '600', border: 'none', cursor: charCount === 0 ? 'not-allowed' : 'pointer', transition: 'background-color 0.2s', backgroundColor: charCount === 0 ? '#6b7280' : '#2563eb', color: 'white' }} 89 + onMouseEnter={(e) => { if (charCount !== 0) e.currentTarget.style.backgroundColor = '#1d4ed8'; }} 90 + onMouseLeave={(e) => { if (charCount !== 0) e.currentTarget.style.backgroundColor = '#2563eb'; }} 91 + > 92 + Post 93 + </button> 94 + </div> 95 + </div> 96 + </div> 97 + </div> 98 + </> 99 + ) 100 + }