From 4af04dae8a46237ed1d6f5151ac8cd70cf7efd9c Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Thu, 27 Feb 2025 08:47:37 -0800 Subject: [PATCH 1/3] feat: basic backup flow (#10) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Get a basic Bluesky -> Storacha upload flow working! Also a bunch of styling work to make this presentable. Screenshot 2025-02-26 at 11 15 54 PM Screenshot 2025-02-26 at 11 16 00 PM Screenshot 2025-02-26 at 11 20 46 PM --- package.json | 8 +- pnpm-lock.yaml | 775 ++++++++++++++--------- postcss.config.mjs | 2 +- public/bluesky-storacha.webp | Bin 0 -> 48566 bytes src/app/globals.css | 12 +- src/app/layout.tsx | 3 +- src/app/page.tsx | 20 +- src/components/BackupButton.tsx | 43 ++ src/components/BlueskyAuthProvider.tsx | 4 +- src/components/BlueskyAuthenticator.tsx | 7 +- src/components/SpaceFinder.tsx | 114 ++++ src/components/StorachaAuthenticator.tsx | 2 +- src/contexts/bluesky.tsx | 2 + src/lib/bluesky.ts | 45 ++ src/lib/ui.ts | 27 + 15 files changed, 731 insertions(+), 333 deletions(-) create mode 100644 public/bluesky-storacha.webp create mode 100644 src/components/BackupButton.tsx create mode 100644 src/components/SpaceFinder.tsx create mode 100644 src/lib/ui.ts diff --git a/package.json b/package.json index 8f070be..54c8772 100644 --- a/package.json +++ b/package.json @@ -15,13 +15,17 @@ "dependencies": { "@atproto/api": "^0.13.35", "@atproto/oauth-client-browser": "^0.3.9", + "@headlessui/react": "^2.2.0", "@heroicons/react": "^2.2.0", + "@tailwindcss/postcss": "^4.0.9", "@tanstack/react-query": "^5.66.0", "@w3ui/react": "^2.5.5", "@web3-storage/w3up-client": "^17.1.2", "next": "15.1.6", + "postcss": "^8.5.2", "react": "^18.0.0", - "react-dom": "^18.0.0" + "react-dom": "^18.0.0", + "tailwindcss": "^4.0.9" }, "devDependencies": { "@cloudflare/next-on-pages": "^1.13.7", @@ -31,8 +35,6 @@ "@types/react-dom": "^18", "eslint": "^9", "eslint-config-next": "15.1.6", - "postcss": "^8", - "tailwindcss": "^3.4.1", "typescript": "^5", "wrangler": "^3.109.3" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f939e32..78a8994 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,9 +14,15 @@ importers: '@atproto/oauth-client-browser': specifier: ^0.3.9 version: 0.3.10 + '@headlessui/react': + specifier: ^2.2.0 + version: 2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@heroicons/react': specifier: ^2.2.0 version: 2.2.0(react@18.3.1) + '@tailwindcss/postcss': + specifier: ^4.0.9 + version: 4.0.9 '@tanstack/react-query': specifier: ^5.66.0 version: 5.66.0(react@18.3.1) @@ -29,12 +35,18 @@ importers: next: specifier: 15.1.6 version: 15.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + postcss: + specifier: ^8.5.2 + version: 8.5.2 react: specifier: ^18.0.0 version: 18.3.1 react-dom: specifier: ^18.0.0 version: 18.3.1(react@18.3.1) + tailwindcss: + specifier: ^4.0.9 + version: 4.0.9 devDependencies: '@cloudflare/next-on-pages': specifier: ^1.13.7 @@ -53,16 +65,10 @@ importers: version: 18.3.5(@types/react@18.3.18) eslint: specifier: ^9 - version: 9.20.1(jiti@1.21.7) + version: 9.20.1(jiti@2.4.2) eslint-config-next: specifier: 15.1.6 - version: 15.1.6(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3) - postcss: - specifier: ^8 - version: 8.5.2 - tailwindcss: - specifier: ^3.4.1 - version: 3.4.17(ts-node@10.9.1(@types/node@20.17.19)(typescript@5.7.3)) + version: 15.1.6(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3) typescript: specifier: ^5 version: 5.7.3 @@ -422,9 +428,28 @@ packages: '@floating-ui/dom@1.6.13': resolution: {integrity: sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==} + '@floating-ui/react-dom@2.1.2': + resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/react@0.26.28': + resolution: {integrity: sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + '@floating-ui/utils@0.2.9': resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} + '@headlessui/react@2.2.0': + resolution: {integrity: sha512-RzCEg+LXsuI7mHiSomsu/gBJSjpupm6A1qIZ5sWjd7JhARNlMiSA4kKfJpCKwU9tE+zMRterhhrP74PvfJrpXQ==} + engines: {node: '>=10'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + react-dom: ^18 || ^19 || ^19.0.0-rc + '@heroicons/react@2.2.0': resolution: {integrity: sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==} peerDependencies: @@ -585,24 +610,13 @@ packages: resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} engines: {node: '>=18.0.0'} - '@jridgewell/gen-mapping@0.3.8': - resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} - engines: {node: '>=6.0.0'} - '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} @@ -733,6 +747,40 @@ packages: '@protobufjs/utf8@1.1.0': resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + '@react-aria/focus@3.19.1': + resolution: {integrity: sha512-bix9Bu1Ue7RPcYmjwcjhB14BMu2qzfJ3tMQLqDc9pweJA66nOw8DThy3IfVr8Z7j2PHktOLf9kcbiZpydKHqzg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/interactions@3.23.0': + resolution: {integrity: sha512-0qR1atBIWrb7FzQ+Tmr3s8uH5mQdyRH78n0krYaG8tng9+u1JlSi8DGRSaC9ezKyNB84m7vHT207xnHXGeJ3Fg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/ssr@3.9.7': + resolution: {integrity: sha512-GQygZaGlmYjmYM+tiNBA5C6acmiDWF52Nqd40bBp0Znk4M4hP+LTmI0lpI1BuKMw45T8RIhrAsICIfKwZvi2Gg==} + engines: {node: '>= 12'} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/utils@3.27.0': + resolution: {integrity: sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-stately/utils@3.10.5': + resolution: {integrity: sha512-iMQSGcpaecghDIh3mZEpZfoFH3ExBwTtuBEcvZ2XnGzCgQjeYXcMdIUwAfVQLXFTdHUHGF6Gu6/dFrYsCzySBQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/shared@3.27.0': + resolution: {integrity: sha512-gvznmLhi6JPEf0bsq7SwRYTHAKKq/wcmKqFez9sRdbED+SPMUmK5omfZ6w3EwUFQHbYUa4zPBYedQ7Knv70RMw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@rollup/pluginutils@5.1.4': resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==} engines: {node: '>=14.0.0'} @@ -766,6 +814,82 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@tailwindcss/node@4.0.9': + resolution: {integrity: sha512-tOJvdI7XfJbARYhxX+0RArAhmuDcczTC46DGCEziqxzzbIaPnfYaIyRT31n4u8lROrsO7Q6u/K9bmQHL2uL1bQ==} + + '@tailwindcss/oxide-android-arm64@4.0.9': + resolution: {integrity: sha512-YBgy6+2flE/8dbtrdotVInhMVIxnHJPbAwa7U1gX4l2ThUIaPUp18LjB9wEH8wAGMBZUb//SzLtdXXNBHPUl6Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.0.9': + resolution: {integrity: sha512-pWdl4J2dIHXALgy2jVkwKBmtEb73kqIfMpYmcgESr7oPQ+lbcQ4+tlPeVXaSAmang+vglAfFpXQCOvs/aGSqlw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.0.9': + resolution: {integrity: sha512-4Dq3lKp0/C7vrRSkNPtBGVebEyWt9QPPlQctxJ0H3MDyiQYvzVYf8jKow7h5QkWNe8hbatEqljMj/Y0M+ERYJg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.0.9': + resolution: {integrity: sha512-k7U1RwRODta8x0uealtVt3RoWAWqA+D5FAOsvVGpYoI6ObgmnzqWW6pnVwz70tL8UZ/QXjeMyiICXyjzB6OGtQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.9': + resolution: {integrity: sha512-NDDjVweHz2zo4j+oS8y3KwKL5wGCZoXGA9ruJM982uVJLdsF8/1AeKvUwKRlMBpxHt1EdWJSAh8a0Mfhl28GlQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.0.9': + resolution: {integrity: sha512-jk90UZ0jzJl3Dy1BhuFfRZ2KP9wVKMXPjmCtY4U6fF2LvrjP5gWFJj5VHzfzHonJexjrGe1lMzgtjriuZkxagg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.0.9': + resolution: {integrity: sha512-3eMjyTC6HBxh9nRgOHzrc96PYh1/jWOwHZ3Kk0JN0Kl25BJ80Lj9HEvvwVDNTgPg154LdICwuFLuhfgH9DULmg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.0.9': + resolution: {integrity: sha512-v0D8WqI/c3WpWH1kq/HP0J899ATLdGZmENa2/emmNjubT0sWtEke9W9+wXeEoACuGAhF9i3PO5MeyditpDCiWQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.0.9': + resolution: {integrity: sha512-Kvp0TCkfeXyeehqLJr7otsc4hd/BUPfcIGrQiwsTVCfaMfjQZCG7DjI+9/QqPZha8YapLA9UoIcUILRYO7NE1Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-win32-arm64-msvc@4.0.9': + resolution: {integrity: sha512-m3+60T/7YvWekajNq/eexjhV8z10rswcz4BC9bioJ7YaN+7K8W2AmLmG0B79H14m6UHE571qB0XsPus4n0QVgQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.0.9': + resolution: {integrity: sha512-dpc05mSlqkwVNOUjGu/ZXd5U1XNch1kHFJ4/cHkZFvaW1RzbHmRt24gvM8/HC6IirMxNarzVw4IXVtvrOoZtxA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.0.9': + resolution: {integrity: sha512-eLizHmXFqHswJONwfqi/WZjtmWZpIalpvMlNhTM99/bkHtUs6IqgI1XQ0/W5eO2HiRQcIlXUogI2ycvKhVLNcA==} + engines: {node: '>= 10'} + + '@tailwindcss/postcss@4.0.9': + resolution: {integrity: sha512-BT/E+pdMqulavEAVM5NCpxmGEwHiLDPpkmg/c/X25ZBW+izTe+aZ+v1gf/HXTrihRoCxrUp5U4YyHsBTzspQKQ==} + '@tanstack/query-core@5.66.0': resolution: {integrity: sha512-J+JeBtthiKxrpzUu7rfIPDzhscXF2p5zE/hVdrqkACBP8Yu0M96mwJ5m/8cPPYQE9aRNvXztXHlNwIh4FEeMZw==} @@ -774,6 +898,15 @@ packages: peerDependencies: react: ^18 || ^19 + '@tanstack/react-virtual@3.13.2': + resolution: {integrity: sha512-LceSUgABBKF6HSsHK2ZqHzQ37IKV/jlaWbHm+NyTa3/WNb/JZVcThDuTainf+PixltOOcFCYXwxbLpOX9sCx+g==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/virtual-core@3.13.2': + resolution: {integrity: sha512-Qzz4EgzMbO5gKrmqUondCjiHcuu4B1ftHb0pjCut661lXZdGoHeze9f/M8iwsK1t5LGR6aNuNGU7mxkowaW6RQ==} + '@tootallnate/once@2.0.0': resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} engines: {node: '>= 10'} @@ -1059,9 +1192,6 @@ packages: arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - arg@5.0.2: - resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -1219,10 +1349,6 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - camelcase-css@2.0.1: - resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} - engines: {node: '>= 6'} - caniuse-lite@1.0.30001699: resolution: {integrity: sha512-b+uH5BakXZ9Do9iK+CkDmctUSEqZl+SP056vc5usa0PL+ev5OHw003rZXcnjNDv3L8P5j6rwT6C0BPKSikW08w==} @@ -1263,6 +1389,10 @@ packages: client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + code-block-writer@10.1.1: resolution: {integrity: sha512-67ueh2IRGst/51p0n6FvPrnRjAGHY5F8xdjkgrYE7DDzpJe6qA07RYQ9VcoUeo5ATOjSOiWpSL3SWBRRbempMw==} @@ -1284,10 +1414,6 @@ packages: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} - commander@4.1.1: - resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} - engines: {node: '>= 6'} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -1321,11 +1447,6 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true - csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -1395,20 +1516,19 @@ packages: resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} engines: {node: '>= 0.6'} + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + detect-libc@2.0.3: resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} - didyoumean@1.2.2: - resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} - diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} - dlv@1.1.3: - resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -2259,8 +2379,8 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jiti@1.21.7: - resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} hasBin: true jose@5.9.6: @@ -2316,12 +2436,69 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - lilconfig@3.1.3: - resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} - engines: {node: '>=14'} + lightningcss-darwin-arm64@1.29.1: + resolution: {integrity: sha512-HtR5XJ5A0lvCqYAoSv2QdZZyoHNttBpa5EP9aNuzBQeKGfbyH5+UipLWvVzpP4Uml5ej4BYs5I9Lco9u1fECqw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + lightningcss-darwin-x64@1.29.1: + resolution: {integrity: sha512-k33G9IzKUpHy/J/3+9MCO4e+PzaFblsgBjSGlpAaFikeBFm8B/CkO3cKU9oI4g+fjS2KlkLM/Bza9K/aw8wsNA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.29.1: + resolution: {integrity: sha512-0SUW22fv/8kln2LnIdOCmSuXnxgxVC276W5KLTwoehiO0hxkacBxjHOL5EtHD8BAXg2BvuhsJPmVMasvby3LiQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.29.1: + resolution: {integrity: sha512-sD32pFvlR0kDlqsOZmYqH/68SqUMPNj+0pucGxToXZi4XZgZmqeX/NkxNKCPsswAXU3UeYgDSpGhu05eAufjDg==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.29.1: + resolution: {integrity: sha512-0+vClRIZ6mmJl/dxGuRsE197o1HDEeeRk6nzycSy2GofC2JsY4ifCRnvUWf/CUBQmlrvMzt6SMQNMSEu22csWQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.29.1: + resolution: {integrity: sha512-UKMFrG4rL/uHNgelBsDwJcBqVpzNJbzsKkbI3Ja5fg00sgQnHw/VrzUTEc4jhZ+AN2BvQYz/tkHu4vt1kLuJyw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.29.1: + resolution: {integrity: sha512-u1S+xdODy/eEtjADqirA774y3jLcm8RPtYztwReEXoZKdzgsHYPl0s5V52Tst+GKzqjebkULT86XMSxejzfISw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.29.1: + resolution: {integrity: sha512-L0Tx0DtaNUTzXv0lbGCLB/c/qEADanHbu4QdcNOXLIe1i8i22rZRpbT3gpWYsCh9aSL9zFujY/WmEXIatWvXbw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.29.1: + resolution: {integrity: sha512-QoOVnkIEFfbW4xPi+dpdft/zAKmgLgsRHfJalEPYuJDOWf7cLQzYg0DEh8/sn737FaeMJxHZRc1oBreiwZCjog==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.29.1: + resolution: {integrity: sha512-NygcbThNBe4JElP+olyTI/doBNGJvLs3bFCRPdvuCcxZCcCZ71B858IHpdm7L1btZex0FvCmM17FK98Y9MRy1Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.29.1: + resolution: {integrity: sha512-FmGoeD4S05ewj+AkhTY+D+myDvXI6eL27FjHIjoyUkO/uw7WZD1fBVs0QxeYWa7E17CUHJaYX/RUGISCtcrG4Q==} + engines: {node: '>= 12.0.0'} locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} @@ -2463,9 +2640,6 @@ packages: resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} hasBin: true - mz@2.7.0: - resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.8: resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2544,10 +2718,6 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - object-hash@3.0.0: - resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} - engines: {node: '>= 6'} - object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} @@ -2697,14 +2867,6 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} - pify@2.3.0: - resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} - engines: {node: '>=0.10.0'} - - pirates@4.0.6: - resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} - engines: {node: '>= 6'} - pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} @@ -2712,43 +2874,6 @@ packages: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} - postcss-import@15.1.0: - resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} - engines: {node: '>=14.0.0'} - peerDependencies: - postcss: ^8.0.0 - - postcss-js@4.0.1: - resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} - engines: {node: ^12 || ^14 || >= 16} - peerDependencies: - postcss: ^8.4.21 - - postcss-load-config@4.0.2: - resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} - engines: {node: '>= 14'} - peerDependencies: - postcss: '>=8.0.9' - ts-node: '>=9.0.0' - peerDependenciesMeta: - postcss: - optional: true - ts-node: - optional: true - - postcss-nested@6.2.0: - resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.2.14 - - postcss-selector-parser@6.1.2: - resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} - engines: {node: '>=4'} - - postcss-value-parser@4.2.0: - resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.4.31: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} @@ -2807,9 +2932,6 @@ packages: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} - read-cache@1.0.0: - resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} - readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -3082,11 +3204,6 @@ packages: babel-plugin-macros: optional: true - sucrase@3.35.0: - resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} - engines: {node: '>=16 || 14 >=14.17'} - hasBin: true - supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -3098,10 +3215,11 @@ packages: sync-multihash-sha2@1.0.0: resolution: {integrity: sha512-A5gVpmtKF0ov+/XID0M0QRJqF2QxAsj3x/LlDC8yivzgoYCoWkV+XaZPfVu7Vj1T/hYzYS1tfjwboSbXjqocug==} - tailwindcss@3.4.17: - resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} - engines: {node: '>=14.0.0'} - hasBin: true + tabbable@6.2.0: + resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + + tailwindcss@4.0.9: + resolution: {integrity: sha512-12laZu+fv1ONDRoNR9ipTOpUD7RN9essRVkX36sjxuRUInpN7hIiHN4lBd/SIFjbISvnXzp8h/hXzmU8SQQYhw==} tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} @@ -3115,13 +3233,6 @@ packages: resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} engines: {node: '>=18'} - thenify-all@1.6.0: - resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} - engines: {node: '>=0.8'} - - thenify@3.3.1: - resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - time-span@4.0.0: resolution: {integrity: sha512-MyqZCTGLDZ77u4k+jqg4UlrzPTPZ49NDlaekU6uuFaJLzPIN1woaRXCbGeqOfxwc3Y37ZROGAJ614Rdv7Olt+g==} engines: {node: '>=10'} @@ -3154,9 +3265,6 @@ packages: peerDependencies: typescript: '>=4.8.4' - ts-interface-checker@0.1.13: - resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - ts-morph@12.0.0: resolution: {integrity: sha512-VHC8XgU2fFW7yO1f/b3mxKDje1vmyzFXHWzOYmKEkCEwcLjDtbdLgBQviqj4ZwP4MJkQtRo6Ha2I29lq/B+VxA==} @@ -3379,11 +3487,6 @@ packages: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} - yaml@2.7.0: - resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} - engines: {node: '>= 14'} - hasBin: true - yauzl-clone@1.0.4: resolution: {integrity: sha512-igM2RRCf3k8TvZoxR2oguuw4z1xasOnA31joCqHIyLkeWrvAc2Jgay5ISQ2ZplinkoGaJ6orCz56Ey456c5ESA==} engines: {node: '>=6'} @@ -3695,9 +3798,9 @@ snapshots: '@esbuild/win32-x64@0.17.19': optional: true - '@eslint-community/eslint-utils@4.4.1(eslint@9.20.1(jiti@1.21.7))': + '@eslint-community/eslint-utils@4.4.1(eslint@9.20.1(jiti@2.4.2))': dependencies: - eslint: 9.20.1(jiti@1.21.7) + eslint: 9.20.1(jiti@2.4.2) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} @@ -3752,8 +3855,31 @@ snapshots: '@floating-ui/core': 1.6.9 '@floating-ui/utils': 0.2.9 + '@floating-ui/react-dom@2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/dom': 1.6.13 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@floating-ui/react@0.26.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@floating-ui/utils': 0.2.9 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + tabbable: 6.2.0 + '@floating-ui/utils@0.2.9': {} + '@headlessui/react@2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/react': 0.26.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-aria/focus': 3.19.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-aria/interactions': 3.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@tanstack/react-virtual': 3.13.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + '@heroicons/react@2.2.0(react@18.3.1)': dependencies: react: 18.3.1 @@ -3896,23 +4022,10 @@ snapshots: dependencies: minipass: 7.1.2 - '@jridgewell/gen-mapping@0.3.8': - dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 - '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/set-array@1.2.1': {} - '@jridgewell/sourcemap-codec@1.5.0': {} - '@jridgewell/trace-mapping@0.3.25': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping@0.3.9': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -4019,6 +4132,49 @@ snapshots: '@protobufjs/utf8@1.1.0': {} + '@react-aria/focus@3.19.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@react-aria/interactions': 3.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-aria/utils': 3.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-types/shared': 3.27.0(react@18.3.1) + '@swc/helpers': 0.5.15 + clsx: 2.1.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@react-aria/interactions@3.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@react-aria/ssr': 3.9.7(react@18.3.1) + '@react-aria/utils': 3.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-types/shared': 3.27.0(react@18.3.1) + '@swc/helpers': 0.5.15 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@react-aria/ssr@3.9.7(react@18.3.1)': + dependencies: + '@swc/helpers': 0.5.15 + react: 18.3.1 + + '@react-aria/utils@3.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@react-aria/ssr': 3.9.7(react@18.3.1) + '@react-stately/utils': 3.10.5(react@18.3.1) + '@react-types/shared': 3.27.0(react@18.3.1) + '@swc/helpers': 0.5.15 + clsx: 2.1.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@react-stately/utils@3.10.5(react@18.3.1)': + dependencies: + '@swc/helpers': 0.5.15 + react: 18.3.1 + + '@react-types/shared@3.27.0(react@18.3.1)': + dependencies: + react: 18.3.1 + '@rollup/pluginutils@5.1.4': dependencies: '@types/estree': 1.0.6 @@ -4046,6 +4202,68 @@ snapshots: dependencies: tslib: 2.8.1 + '@tailwindcss/node@4.0.9': + dependencies: + enhanced-resolve: 5.18.1 + jiti: 2.4.2 + tailwindcss: 4.0.9 + + '@tailwindcss/oxide-android-arm64@4.0.9': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.0.9': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.0.9': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.0.9': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.9': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.0.9': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.0.9': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.0.9': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.0.9': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.0.9': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.0.9': + optional: true + + '@tailwindcss/oxide@4.0.9': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.0.9 + '@tailwindcss/oxide-darwin-arm64': 4.0.9 + '@tailwindcss/oxide-darwin-x64': 4.0.9 + '@tailwindcss/oxide-freebsd-x64': 4.0.9 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.0.9 + '@tailwindcss/oxide-linux-arm64-gnu': 4.0.9 + '@tailwindcss/oxide-linux-arm64-musl': 4.0.9 + '@tailwindcss/oxide-linux-x64-gnu': 4.0.9 + '@tailwindcss/oxide-linux-x64-musl': 4.0.9 + '@tailwindcss/oxide-win32-arm64-msvc': 4.0.9 + '@tailwindcss/oxide-win32-x64-msvc': 4.0.9 + + '@tailwindcss/postcss@4.0.9': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.0.9 + '@tailwindcss/oxide': 4.0.9 + lightningcss: 1.29.1 + postcss: 8.5.2 + tailwindcss: 4.0.9 + '@tanstack/query-core@5.66.0': {} '@tanstack/react-query@5.66.0(react@18.3.1)': @@ -4053,6 +4271,14 @@ snapshots: '@tanstack/query-core': 5.66.0 react: 18.3.1 + '@tanstack/react-virtual@3.13.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@tanstack/virtual-core': 3.13.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@tanstack/virtual-core@3.13.2': {} + '@tootallnate/once@2.0.0': {} '@ts-morph/common@0.11.1': @@ -4097,15 +4323,15 @@ snapshots: '@types/retry@0.12.1': {} - '@typescript-eslint/eslint-plugin@8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3))(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3)': + '@typescript-eslint/eslint-plugin@8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3))(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.24.0(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3) + '@typescript-eslint/parser': 8.24.0(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3) '@typescript-eslint/scope-manager': 8.24.0 - '@typescript-eslint/type-utils': 8.24.0(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3) - '@typescript-eslint/utils': 8.24.0(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3) + '@typescript-eslint/type-utils': 8.24.0(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/utils': 8.24.0(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3) '@typescript-eslint/visitor-keys': 8.24.0 - eslint: 9.20.1(jiti@1.21.7) + eslint: 9.20.1(jiti@2.4.2) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -4114,14 +4340,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.24.0(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3)': + '@typescript-eslint/parser@8.24.0(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3)': dependencies: '@typescript-eslint/scope-manager': 8.24.0 '@typescript-eslint/types': 8.24.0 '@typescript-eslint/typescript-estree': 8.24.0(typescript@5.7.3) '@typescript-eslint/visitor-keys': 8.24.0 debug: 4.4.0 - eslint: 9.20.1(jiti@1.21.7) + eslint: 9.20.1(jiti@2.4.2) typescript: 5.7.3 transitivePeerDependencies: - supports-color @@ -4131,12 +4357,12 @@ snapshots: '@typescript-eslint/types': 8.24.0 '@typescript-eslint/visitor-keys': 8.24.0 - '@typescript-eslint/type-utils@8.24.0(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3)': + '@typescript-eslint/type-utils@8.24.0(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3)': dependencies: '@typescript-eslint/typescript-estree': 8.24.0(typescript@5.7.3) - '@typescript-eslint/utils': 8.24.0(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3) + '@typescript-eslint/utils': 8.24.0(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3) debug: 4.4.0 - eslint: 9.20.1(jiti@1.21.7) + eslint: 9.20.1(jiti@2.4.2) ts-api-utils: 2.0.1(typescript@5.7.3) typescript: 5.7.3 transitivePeerDependencies: @@ -4158,13 +4384,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.24.0(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3)': + '@typescript-eslint/utils@8.24.0(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3)': dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1(jiti@2.4.2)) '@typescript-eslint/scope-manager': 8.24.0 '@typescript-eslint/types': 8.24.0 '@typescript-eslint/typescript-estree': 8.24.0(typescript@5.7.3) - eslint: 9.20.1(jiti@1.21.7) + eslint: 9.20.1(jiti@2.4.2) typescript: 5.7.3 transitivePeerDependencies: - supports-color @@ -4553,8 +4779,6 @@ snapshots: arg@4.1.3: {} - arg@5.0.2: {} - argparse@2.0.1: {} aria-query@5.3.2: {} @@ -4727,8 +4951,6 @@ snapshots: callsites@3.1.0: {} - camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001699: {} carstream@2.3.0: @@ -4770,6 +4992,8 @@ snapshots: client-only@0.0.1: {} + clsx@2.1.1: {} + code-block-writer@10.1.1: {} color-convert@2.0.1: @@ -4792,8 +5016,6 @@ snapshots: commander@11.1.0: {} - commander@4.1.1: {} - concat-map@0.0.1: {} conf@11.0.2: @@ -4825,8 +5047,6 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - cssesc@3.0.0: {} - csstype@3.1.3: {} damerau-levenshtein@1.0.8: {} @@ -4885,14 +5105,12 @@ snapshots: depd@1.1.2: {} - detect-libc@2.0.3: {} + detect-libc@1.0.3: {} - didyoumean@1.2.2: {} + detect-libc@2.0.3: {} diff@4.0.2: {} - dlv@1.1.3: {} - doctrine@2.1.0: dependencies: esutils: 2.0.3 @@ -5241,19 +5459,19 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-next@15.1.6(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3): + eslint-config-next@15.1.6(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3): dependencies: '@next/eslint-plugin-next': 15.1.6 '@rushstack/eslint-patch': 1.10.5 - '@typescript-eslint/eslint-plugin': 8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3))(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3) - '@typescript-eslint/parser': 8.24.0(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3) - eslint: 9.20.1(jiti@1.21.7) + '@typescript-eslint/eslint-plugin': 8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3))(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/parser': 8.24.0(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3) + eslint: 9.20.1(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.20.1(jiti@1.21.7)) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.20.1(jiti@1.21.7)) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.20.1(jiti@1.21.7)) - eslint-plugin-react: 7.37.4(eslint@9.20.1(jiti@1.21.7)) - eslint-plugin-react-hooks: 5.1.0(eslint@9.20.1(jiti@1.21.7)) + eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.20.1(jiti@2.4.2)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.20.1(jiti@2.4.2)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.20.1(jiti@2.4.2)) + eslint-plugin-react: 7.37.4(eslint@9.20.1(jiti@2.4.2)) + eslint-plugin-react-hooks: 5.1.0(eslint@9.20.1(jiti@2.4.2)) optionalDependencies: typescript: 5.7.3 transitivePeerDependencies: @@ -5269,34 +5487,34 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@9.20.1(jiti@1.21.7)): + eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@9.20.1(jiti@2.4.2)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.0 enhanced-resolve: 5.18.1 - eslint: 9.20.1(jiti@1.21.7) + eslint: 9.20.1(jiti@2.4.2) fast-glob: 3.3.3 get-tsconfig: 4.10.0 is-bun-module: 1.3.0 is-glob: 4.0.3 stable-hash: 0.0.4 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.20.1(jiti@1.21.7)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.20.1(jiti@2.4.2)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.20.1(jiti@1.21.7)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.20.1(jiti@2.4.2)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.24.0(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3) - eslint: 9.20.1(jiti@1.21.7) + '@typescript-eslint/parser': 8.24.0(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3) + eslint: 9.20.1(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.20.1(jiti@1.21.7)) + eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.20.1(jiti@2.4.2)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.20.1(jiti@1.21.7)): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.20.1(jiti@2.4.2)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -5305,9 +5523,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.20.1(jiti@1.21.7) + eslint: 9.20.1(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.20.1(jiti@1.21.7)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.20.1(jiti@2.4.2)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -5319,13 +5537,13 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.24.0(eslint@9.20.1(jiti@1.21.7))(typescript@5.7.3) + '@typescript-eslint/parser': 8.24.0(eslint@9.20.1(jiti@2.4.2))(typescript@5.7.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.10.2(eslint@9.20.1(jiti@1.21.7)): + eslint-plugin-jsx-a11y@6.10.2(eslint@9.20.1(jiti@2.4.2)): dependencies: aria-query: 5.3.2 array-includes: 3.1.8 @@ -5335,7 +5553,7 @@ snapshots: axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 9.20.1(jiti@1.21.7) + eslint: 9.20.1(jiti@2.4.2) hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -5344,11 +5562,11 @@ snapshots: safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 - eslint-plugin-react-hooks@5.1.0(eslint@9.20.1(jiti@1.21.7)): + eslint-plugin-react-hooks@5.1.0(eslint@9.20.1(jiti@2.4.2)): dependencies: - eslint: 9.20.1(jiti@1.21.7) + eslint: 9.20.1(jiti@2.4.2) - eslint-plugin-react@7.37.4(eslint@9.20.1(jiti@1.21.7)): + eslint-plugin-react@7.37.4(eslint@9.20.1(jiti@2.4.2)): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 @@ -5356,7 +5574,7 @@ snapshots: array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 es-iterator-helpers: 1.2.1 - eslint: 9.20.1(jiti@1.21.7) + eslint: 9.20.1(jiti@2.4.2) estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 @@ -5379,9 +5597,9 @@ snapshots: eslint-visitor-keys@4.2.0: {} - eslint@9.20.1(jiti@1.21.7): + eslint@9.20.1(jiti@2.4.2): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1(jiti@2.4.2)) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.19.2 '@eslint/core': 0.11.0 @@ -5416,7 +5634,7 @@ snapshots: natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: - jiti: 1.21.7 + jiti: 2.4.2 transitivePeerDependencies: - supports-color @@ -5879,7 +6097,7 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 - jiti@1.21.7: {} + jiti@2.4.2: {} jose@5.9.6: {} @@ -5936,9 +6154,50 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - lilconfig@3.1.3: {} + lightningcss-darwin-arm64@1.29.1: + optional: true + + lightningcss-darwin-x64@1.29.1: + optional: true + + lightningcss-freebsd-x64@1.29.1: + optional: true + + lightningcss-linux-arm-gnueabihf@1.29.1: + optional: true + + lightningcss-linux-arm64-gnu@1.29.1: + optional: true - lines-and-columns@1.2.4: {} + lightningcss-linux-arm64-musl@1.29.1: + optional: true + + lightningcss-linux-x64-gnu@1.29.1: + optional: true + + lightningcss-linux-x64-musl@1.29.1: + optional: true + + lightningcss-win32-arm64-msvc@1.29.1: + optional: true + + lightningcss-win32-x64-msvc@1.29.1: + optional: true + + lightningcss@1.29.1: + dependencies: + detect-libc: 1.0.3 + optionalDependencies: + lightningcss-darwin-arm64: 1.29.1 + lightningcss-darwin-x64: 1.29.1 + lightningcss-freebsd-x64: 1.29.1 + lightningcss-linux-arm-gnueabihf: 1.29.1 + lightningcss-linux-arm64-gnu: 1.29.1 + lightningcss-linux-arm64-musl: 1.29.1 + lightningcss-linux-x64-gnu: 1.29.1 + lightningcss-linux-x64-musl: 1.29.1 + lightningcss-win32-arm64-msvc: 1.29.1 + lightningcss-win32-x64-msvc: 1.29.1 locate-path@6.0.0: dependencies: @@ -6063,12 +6322,6 @@ snapshots: mustache@4.2.0: {} - mz@2.7.0: - dependencies: - any-promise: 1.3.0 - object-assign: 4.1.1 - thenify-all: 1.6.0 - nanoid@3.3.8: {} native-fetch@3.0.0(node-fetch@2.7.0(encoding@0.1.13)): @@ -6130,8 +6383,6 @@ snapshots: object-assign@4.1.1: {} - object-hash@3.0.0: {} - object-inspect@1.13.4: {} object-keys@1.1.1: {} @@ -6279,10 +6530,6 @@ snapshots: picomatch@4.0.2: {} - pify@2.3.0: {} - - pirates@4.0.6: {} - pkg-types@1.3.1: dependencies: confbox: 0.1.8 @@ -6291,38 +6538,6 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-import@15.1.0(postcss@8.5.2): - dependencies: - postcss: 8.5.2 - postcss-value-parser: 4.2.0 - read-cache: 1.0.0 - resolve: 1.22.10 - - postcss-js@4.0.1(postcss@8.5.2): - dependencies: - camelcase-css: 2.0.1 - postcss: 8.5.2 - - postcss-load-config@4.0.2(postcss@8.5.2)(ts-node@10.9.1(@types/node@20.17.19)(typescript@5.7.3)): - dependencies: - lilconfig: 3.1.3 - yaml: 2.7.0 - optionalDependencies: - postcss: 8.5.2 - ts-node: 10.9.1(@types/node@20.17.19)(typescript@5.7.3) - - postcss-nested@6.2.0(postcss@8.5.2): - dependencies: - postcss: 8.5.2 - postcss-selector-parser: 6.1.2 - - postcss-selector-parser@6.1.2: - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - - postcss-value-parser@4.2.0: {} - postcss@8.4.31: dependencies: nanoid: 3.3.8 @@ -6395,10 +6610,6 @@ snapshots: dependencies: loose-envify: 1.4.0 - read-cache@1.0.0: - dependencies: - pify: 2.3.0 - readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -6732,16 +6943,6 @@ snapshots: client-only: 0.0.1 react: 18.3.1 - sucrase@3.35.0: - dependencies: - '@jridgewell/gen-mapping': 0.3.8 - commander: 4.1.1 - glob: 10.4.5 - lines-and-columns: 1.2.4 - mz: 2.7.0 - pirates: 4.0.6 - ts-interface-checker: 0.1.13 - supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -6752,32 +6953,9 @@ snapshots: dependencies: '@noble/hashes': 1.7.1 - tailwindcss@3.4.17(ts-node@10.9.1(@types/node@20.17.19)(typescript@5.7.3)): - dependencies: - '@alloc/quick-lru': 5.2.0 - arg: 5.0.2 - chokidar: 3.6.0 - didyoumean: 1.2.2 - dlv: 1.1.3 - fast-glob: 3.3.3 - glob-parent: 6.0.2 - is-glob: 4.0.3 - jiti: 1.21.7 - lilconfig: 3.1.3 - micromatch: 4.0.8 - normalize-path: 3.0.0 - object-hash: 3.0.0 - picocolors: 1.1.1 - postcss: 8.5.2 - postcss-import: 15.1.0(postcss@8.5.2) - postcss-js: 4.0.1(postcss@8.5.2) - postcss-load-config: 4.0.2(postcss@8.5.2)(ts-node@10.9.1(@types/node@20.17.19)(typescript@5.7.3)) - postcss-nested: 6.2.0(postcss@8.5.2) - postcss-selector-parser: 6.1.2 - resolve: 1.22.10 - sucrase: 3.35.0 - transitivePeerDependencies: - - ts-node + tabbable@6.2.0: {} + + tailwindcss@4.0.9: {} tapable@2.2.1: {} @@ -6799,14 +6977,6 @@ snapshots: mkdirp: 3.0.1 yallist: 5.0.0 - thenify-all@1.6.0: - dependencies: - thenify: 3.3.1 - - thenify@3.3.1: - dependencies: - any-promise: 1.3.0 - time-span@4.0.0: dependencies: convert-hrtime: 3.0.0 @@ -6829,8 +6999,6 @@ snapshots: dependencies: typescript: 5.7.3 - ts-interface-checker@0.1.13: {} - ts-morph@12.0.0: dependencies: '@ts-morph/common': 0.11.1 @@ -6854,25 +7022,6 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - ts-node@10.9.1(@types/node@20.17.19)(typescript@5.7.3): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.11 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 20.17.19 - acorn: 8.14.0 - acorn-walk: 8.3.4 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.7.3 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - optional: true - ts-toolbelt@6.15.5: {} tsconfig-paths@3.15.0: @@ -7122,8 +7271,6 @@ snapshots: yallist@5.0.0: {} - yaml@2.7.0: {} - yauzl-clone@1.0.4: dependencies: events-intercept: 2.0.0 diff --git a/postcss.config.mjs b/postcss.config.mjs index 1a69fd2..79bcf13 100644 --- a/postcss.config.mjs +++ b/postcss.config.mjs @@ -1,7 +1,7 @@ /** @type {import('postcss-load-config').Config} */ const config = { plugins: { - tailwindcss: {}, + "@tailwindcss/postcss": {}, }, }; diff --git a/public/bluesky-storacha.webp b/public/bluesky-storacha.webp new file mode 100644 index 0000000000000000000000000000000000000000..521db2aa602ec4ea03cb39154fb9be3c56347db8 GIT binary patch literal 48566 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

Sxylan)f{S>K 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 { return ( - + {children} diff --git a/src/app/page.tsx b/src/app/page.tsx index dd0fad6..b963091 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,16 +1,22 @@ +import BackupButton from "@/components/BackupButton"; import BlueskyAuthenticator from "@/components/BlueskyAuthenticator"; import StorachaAuthenticator from "@/components/StorachaAuthenticator"; export default function Home () { return ( -

-

Bluesky Backup Webapp

-

Lets get started

-

Bluesky Auth

- +
+

Bluesky Backups

+
+

Bluesky Auth

+ +
-

Storacha Auth

- +
+

Storacha Auth

+ +
+ +
) } diff --git a/src/components/BackupButton.tsx b/src/components/BackupButton.tsx new file mode 100644 index 0000000..348f47c --- /dev/null +++ b/src/components/BackupButton.tsx @@ -0,0 +1,43 @@ +'use client' + +import { useBskyAuthContext } from "@/contexts" +import { backup } from "@/lib/bluesky" +import { Space, useW3 } from "@w3ui/react" +import { useState } from "react" +import { SpaceFinder } from "./SpaceFinder" + +export default function BackupButton () { + const [isBackingUp, setIsBackingUp] = useState(false) + const [spaceDid, setSpaceDid] = useState() + const [storacha] = useW3() + const bluesky = useBskyAuthContext() + async function onClick () { + if (spaceDid && bluesky.userProfile && bluesky.agent && storacha.client) { + await storacha.client.setCurrentSpace(spaceDid.did()) + + setIsBackingUp(true) + await backup(bluesky.userProfile, bluesky.agent, storacha.client) + setIsBackingUp(false) + } else { + console.log(bluesky.userProfile, bluesky.agent, storacha.client) + } + } + const userAuthenticatedToBothServices = bluesky.userProfile && (storacha.accounts.length > 0) + return userAuthenticatedToBothServices ? ( + + isBackingUp ? ( +
+ Backup In Progress! Please check the developer console to see progress. +
+ ) : ( +
+ + +
+ ) + ) : ( +
Please authenticate to both Bluesky and Storacha to continue.
+ ) +} \ No newline at end of file diff --git a/src/components/BlueskyAuthProvider.tsx b/src/components/BlueskyAuthProvider.tsx index 3b0709f..3928501 100644 --- a/src/components/BlueskyAuthProvider.tsx +++ b/src/components/BlueskyAuthProvider.tsx @@ -27,8 +27,7 @@ export const BskyAuthProvider = ({ children }: Props) => { const bskyAgent = useMemo(() => { if (!authenticated || !session) return; - const agent = new Agent(session); - return agent; + return new Agent(session); }, [authenticated, session]); const { data: userProfile } = useQuery({ @@ -85,6 +84,7 @@ export const BskyAuthProvider = ({ children }: Props) => { state, userProfile, bskyAuthClient, + agent: bskyAgent }} > {children} diff --git a/src/components/BlueskyAuthenticator.tsx b/src/components/BlueskyAuthenticator.tsx index d541ea9..36d30ef 100644 --- a/src/components/BlueskyAuthenticator.tsx +++ b/src/components/BlueskyAuthenticator.tsx @@ -32,8 +32,13 @@ export default function BlueskyAuthenticator () { }} value={handle} placeholder="Bluesky Handle" + className="ipt" /> - +
)} diff --git a/src/components/SpaceFinder.tsx b/src/components/SpaceFinder.tsx new file mode 100644 index 0000000..507a1d5 --- /dev/null +++ b/src/components/SpaceFinder.tsx @@ -0,0 +1,114 @@ +// copied from console + +import type { Space } from '@w3ui/react' + +import React, { Fragment, useState } from 'react' +import { Combobox, Transition } from '@headlessui/react' +import { ChevronUpDownIcon } from '@heroicons/react/20/solid' +import { shortenDID } from '@/lib/ui' + +interface SpaceFinderProps { + spaces: Space[] + selected?: Space + setSelected?: (space: Space) => void + className?: string +} + +export function SpaceFinder ({ + spaces, + selected, + setSelected, + className = '' +}: SpaceFinderProps): JSX.Element { + const [query, setQuery] = useState('') + const filtered = + query === '' + ? spaces + : spaces.filter((space: Space) => + (space.name || space.did()) + .toLowerCase() + .replace(/\s+/g, '') + .includes(query.toLowerCase().replace(/\s+/g, '')) + ) + + return ( +
+ a?.did() === b?.did()} + > +
+
+ space.name || shortenDID(space.did())} + onChange={(event) => { setQuery(event.target.value) }} + /> + + +
+ { setQuery('') }} + > + + {filtered.length === 0 && query !== '' + ? ( +
+ (╯°□°)╯︵ ┻━┻ +
+ ) + : ( + filtered.map((space) => ( + + `relative select-none py-2 pl-9 pr-4 ${ + active ? 'bg-hot-yellow-light cursor-pointer text-hot-red' : 'text-black' + }` + } + value={space} + > + {({ selected, active }) => ( + <> + + {space.name || shortenDID(space.did())} + + {selected + ? ( + + ⁂ + + ) + : null} + + )} + + )) + )} +
+
+
+
+
+ ) +} diff --git a/src/components/StorachaAuthenticator.tsx b/src/components/StorachaAuthenticator.tsx index 5b556ef..26aa079 100644 --- a/src/components/StorachaAuthenticator.tsx +++ b/src/components/StorachaAuthenticator.tsx @@ -6,7 +6,7 @@ import { AuthenticationEnsurer } from '@/components/Authenticator' function Identity () { const [{ client, accounts }] = useW3() return ( -
+

You're signed in as {accounts[0].toEmail()}.

diff --git a/src/contexts/bluesky.tsx b/src/contexts/bluesky.tsx index d239325..431f0cf 100644 --- a/src/contexts/bluesky.tsx +++ b/src/contexts/bluesky.tsx @@ -1,5 +1,6 @@ 'use client' +import { Agent } from "@atproto/api"; import { ProfileViewBasic } from "@atproto/api/dist/client/types/app/bsky/actor/defs"; import { OAuthSession, @@ -17,6 +18,7 @@ type BskyAuthContextProps = { state?: string; userProfile?: ProfileViewBasic; bskyAuthClient?: BrowserOAuthClient; + agent?: Agent; }; export const BskyAuthContext = createContext({ diff --git a/src/lib/bluesky.ts b/src/lib/bluesky.ts index da8cafb..a9cf59c 100644 --- a/src/lib/bluesky.ts +++ b/src/lib/bluesky.ts @@ -1,4 +1,7 @@ +import { Agent } from "@atproto/api"; +import { ProfileViewBasic } from "@atproto/api/dist/client/types/app/bsky/actor/defs"; import { OAuthClientMetadataInput } from "@atproto/oauth-client-browser"; +import { Client } from "@w3ui/react"; export const blueskyClientUri = process.env.NEXT_PUBLIC_BLUESKY_CLIENT_URI || "https://localhost:3000/" @@ -13,5 +16,47 @@ export const blueskyClientMetadata: OAuthClientMetadataInput = { "token_endpoint_auth_method": "none", "scope": "atproto transition:generic", "dpop_bound_access_tokens": true +} +export async function backup(profile: ProfileViewBasic, agent: Agent, storachaClient: Client){ + const accountDid = profile.did + + // TODO + //const commitRev = await agent.com.atproto.sync.getLatestCommit({ did: accountDid }) + //localDb.SetLatestCommit(accountDid, commitRev) + + console.log("backing up repo") + const repoRes = await agent.com.atproto.sync.getRepo({ did: accountDid }) + console.log(repoRes) + const storachaRepoCid = await storachaClient.uploadCAR(new Blob([repoRes.data])) + console.log("REPO ID", storachaRepoCid) + + // TODO + // await localDb.AddRepo(accountDid, repoStorachaCid) + + let blobCursor: string | undefined = undefined + + do { + const listedBlobs = await agent.com.atproto.sync.listBlobs({ + did: accountDid, + cursor: blobCursor, + }) + console.log(`backing up ${listedBlobs.data.cids.length} blobs`) + for (const cid of listedBlobs.data.cids) { + console.log("backing up blob", cid) + const blobRes = await agent.com.atproto.sync.getBlob({ + did: accountDid, + cid, + }) + + const storachaBlobCid = await storachaClient.uploadFile(new Blob([blobRes.data])) + console.log(storachaBlobCid) + + // TODO + // await localDb.AddBlob(accountDid, blobStorachaCid) + } + + blobCursor = listedBlobs.data.cursor + + } while (blobCursor) } \ No newline at end of file diff --git a/src/lib/ui.ts b/src/lib/ui.ts new file mode 100644 index 0000000..bff2879 --- /dev/null +++ b/src/lib/ui.ts @@ -0,0 +1,27 @@ +// copied from console + +import { DID, UnknownLink } from '@w3ui/react' + +export const B = 1024 +export const MB = 1024 * B +export const GB = 1024 * MB +export const TB = 1024 * GB + +export function shortenCID(cid: UnknownLink) { + return shorten(cid.toString(), 5, 4) +} + +export function shortenDID(did: DID) { + return shorten(did, 14, 4) +} + +function shorten(text: string, front: number = 3, back: number = 3): string { + return `${text.slice(0, front)}…${text.slice(-back)}` +} + +export function filesize (bytes: number) { + if (bytes < B / 2) return `${bytes}B` // avoid 0.0KB + if (bytes < MB / 2) return `${(bytes / 1024).toFixed(1)}KB` // avoid 0.0MB + if (bytes < GB / 2) return `${(bytes / 1024 / 1024).toFixed(1)}MB` // avoid 0.0GB + return `${(bytes / 1024 / 1024 / 1024).toFixed(1)}GB` +} From aade6f0813afc53e4fbffa18029b55c3e6bda377 Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Fri, 28 Feb 2025 09:32:06 -0800 Subject: [PATCH 2/3] feat: add backup progress UI (#11) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plus assorted UI tweaks to get this demoable Progress UX: Screenshot 2025-02-27 at 2 42 28 PM Screenshot 2025-02-27 at 2 42 42 PM UX polish: Screenshot 2025-02-27 at 2 42 14 PM --- src/components/Authenticator.tsx | 31 ++++-------- src/components/BackupButton.tsx | 67 +++++++++++++++++++++---- src/components/BlueskyAuthProvider.tsx | 3 ++ src/components/BlueskyAuthenticator.tsx | 43 ++++++++-------- src/contexts/bluesky.tsx | 2 + src/lib/bluesky.ts | 41 ++++++++++----- 6 files changed, 125 insertions(+), 62 deletions(-) diff --git a/src/components/Authenticator.tsx b/src/components/Authenticator.tsx index 8c90372..cb63222 100644 --- a/src/components/Authenticator.tsx +++ b/src/components/Authenticator.tsx @@ -6,20 +6,14 @@ export function AuthenticationForm (): ReactNode { const [{ submitted }] = useAuthenticator() return (
- -
- - -
-
- -
+ + +
) @@ -28,16 +22,13 @@ export function AuthenticationForm (): ReactNode { export function AuthenticationSubmitted (): ReactNode { const [{ email }] = useAuthenticator() return ( -
-
-

Verify your email address!

-

+

+

Click the link in the email we sent to {email} to authorize this agent.

- + Cancel -
) } diff --git a/src/components/BackupButton.tsx b/src/components/BackupButton.tsx index 348f47c..d4826ab 100644 --- a/src/components/BackupButton.tsx +++ b/src/components/BackupButton.tsx @@ -8,32 +8,79 @@ import { SpaceFinder } from "./SpaceFinder" export default function BackupButton () { const [isBackingUp, setIsBackingUp] = useState(false) - const [spaceDid, setSpaceDid] = useState() + const [selectedSpace, setSelectedSpace] = useState() const [storacha] = useW3() const bluesky = useBskyAuthContext() + const backupEvents = new EventTarget() + const space = selectedSpace ?? storacha?.spaces[0] async function onClick () { - if (spaceDid && bluesky.userProfile && bluesky.agent && storacha.client) { - await storacha.client.setCurrentSpace(spaceDid.did()) + if (space && bluesky.userProfile && bluesky.agent && storacha.client) { + await storacha.client.setCurrentSpace(space.did()) setIsBackingUp(true) - await backup(bluesky.userProfile, bluesky.agent, storacha.client) + await backup(bluesky.userProfile, bluesky.agent, storacha.client, { eventTarget: backupEvents }) setIsBackingUp(false) } else { - console.log(bluesky.userProfile, bluesky.agent, storacha.client) + console.log('not backing up, profile, agent, client:', bluesky.userProfile, bluesky.agent, storacha.client) } } const userAuthenticatedToBothServices = bluesky.userProfile && (storacha.accounts.length > 0) + const [backupProgressComponent, setBackupProgressComponent] = useState( + <> + Backing up your Bluesky account... + + ) + backupEvents.addEventListener('repo:fetching', () => { + setBackupProgressComponent( + <> + Backing up your Bluesky account... + + ) + }) + backupEvents.addEventListener('repo:fetching', () => { + setBackupProgressComponent( + <> + Backing up your Bluesky account... + + ) + }) + backupEvents.addEventListener('repo:uploaded', () => { + setBackupProgressComponent( + <> + Backup started... + + ) + }) + backupEvents.addEventListener('blob:fetching', (e) => { + const { i: loaded, count: total } = (e as CustomEvent).detail ?? { i: 0, count: 1 } + const percentComplete = Math.floor((loaded / total) * 100) + setBackupProgressComponent( +
+

Backing up your Bluesky account...

+
+
+
+
+
+ ) + }) return userAuthenticatedToBothServices ? ( - isBackingUp ? (
- Backup In Progress! Please check the developer console to see progress. + {backupProgressComponent}
) : (
- -
) diff --git a/src/components/BlueskyAuthProvider.tsx b/src/components/BlueskyAuthProvider.tsx index 3928501..a748df5 100644 --- a/src/components/BlueskyAuthProvider.tsx +++ b/src/components/BlueskyAuthProvider.tsx @@ -24,6 +24,7 @@ export const BskyAuthProvider = ({ children }: Props) => { const [bskyAuthClient, setBskyAuthClient] = useState(); const [session, setSession] = useState(); const [state, setState] = useState(); + const [initialized, setInitialized] = useState(false) const bskyAgent = useMemo(() => { if (!authenticated || !session) return; @@ -71,6 +72,7 @@ export const BskyAuthProvider = ({ children }: Props) => { setSession(session); } } + setInitialized(true) }; initBsky(); @@ -79,6 +81,7 @@ export const BskyAuthProvider = ({ children }: Props) => { return ( (""); @@ -21,25 +21,28 @@ export default function BlueskyAuthenticator () { return (
- {authenticated ? ( -
Authenticated to Bluesky!
- ) : ( -
- { - e.preventDefault(); - setHandle(e.target.value); - }} - value={handle} - placeholder="Bluesky Handle" - className="ipt" - /> - -
+ {initialized ? ( + authenticated ? ( +
Authenticated to Bluesky!
+ ) : ( +
+ { + e.preventDefault(); + setHandle(e.target.value); + }} + value={handle} + placeholder="Full Bluesky Handle (eg, racha.bsky.social)" + className="ipt w-82" + /> + +
+ )) : ( +
Loading...
)}
); diff --git a/src/contexts/bluesky.tsx b/src/contexts/bluesky.tsx index 431f0cf..8aa0811 100644 --- a/src/contexts/bluesky.tsx +++ b/src/contexts/bluesky.tsx @@ -13,6 +13,7 @@ import { } from "react"; type BskyAuthContextProps = { + initialized: boolean; authenticated: boolean; session?: OAuthSession; state?: string; @@ -22,6 +23,7 @@ type BskyAuthContextProps = { }; export const BskyAuthContext = createContext({ + initialized: false, authenticated: false, }); diff --git a/src/lib/bluesky.ts b/src/lib/bluesky.ts index a9cf59c..5538508 100644 --- a/src/lib/bluesky.ts +++ b/src/lib/bluesky.ts @@ -18,45 +18,62 @@ export const blueskyClientMetadata: OAuthClientMetadataInput = { "dpop_bound_access_tokens": true } -export async function backup(profile: ProfileViewBasic, agent: Agent, storachaClient: Client){ +interface BackupOptions { + eventTarget?: EventTarget +} + +export async function backup (profile: ProfileViewBasic, agent: Agent, storachaClient: Client, { eventTarget }: BackupOptions = {}) { const accountDid = profile.did - + // TODO //const commitRev = await agent.com.atproto.sync.getLatestCommit({ did: accountDid }) //localDb.SetLatestCommit(accountDid, commitRev) console.log("backing up repo") + eventTarget?.dispatchEvent(new CustomEvent('repo:fetching', { detail: { did: accountDid } })) const repoRes = await agent.com.atproto.sync.getRepo({ did: accountDid }) - console.log(repoRes) + + eventTarget?.dispatchEvent(new CustomEvent('repo:uploading')) const storachaRepoCid = await storachaClient.uploadCAR(new Blob([repoRes.data])) - console.log("REPO ID", storachaRepoCid) - + eventTarget?.dispatchEvent(new CustomEvent('repo:uploaded', { detail: { cid: storachaRepoCid } })) + + // TODO // await localDb.AddRepo(accountDid, repoStorachaCid) - + let blobCursor: string | undefined = undefined - + do { + eventTarget?.dispatchEvent(new CustomEvent('blobs:listing')) + const listedBlobs = await agent.com.atproto.sync.listBlobs({ did: accountDid, cursor: blobCursor, }) + eventTarget?.dispatchEvent(new CustomEvent('blobs:listed', { detail: { count: listedBlobs.data.cids.length } })) + console.log(`backing up ${listedBlobs.data.cids.length} blobs`) + let i = 0 for (const cid of listedBlobs.data.cids) { console.log("backing up blob", cid) + eventTarget?.dispatchEvent(new CustomEvent('blob:fetching', { detail: { cid, i, count: listedBlobs.data.cids.length } })) + const blobRes = await agent.com.atproto.sync.getBlob({ did: accountDid, cid, }) - + + eventTarget?.dispatchEvent(new CustomEvent('blob:uploading', { detail: { cid, i, count: listedBlobs.data.cids.length } })) const storachaBlobCid = await storachaClient.uploadFile(new Blob([blobRes.data])) - console.log(storachaBlobCid) - + eventTarget?.dispatchEvent(new CustomEvent('blob:uploaded', { detail: { cid: storachaBlobCid, i, count: listedBlobs.data.cids.length } })) + // TODO // await localDb.AddBlob(accountDid, blobStorachaCid) + i++ } - + blobCursor = listedBlobs.data.cursor - + eventTarget?.dispatchEvent(new CustomEvent('blobs:uploaded')) + } while (blobCursor) } \ No newline at end of file From 131cd566a28b930dd6cf59e78df68c0740b9e124 Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Fri, 28 Feb 2025 12:22:15 -0800 Subject: [PATCH 3/3] feat: store backup metadata locally (#12) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use indexeddb to store metadata about backups and build a UI to explore the backups you've created. Screenshot 2025-02-28 at 1 16 22 PM Screenshot 2025-02-28 at 1 18 08 PM Screenshot 2025-02-28 at 1 16 44 PM --- package.json | 2 + pnpm-lock.yaml | 24 ++++++ src/app/backups/[id]/page.tsx | 19 +++++ src/app/db.ts | 55 ++++++++++++++ src/app/globals.css | 2 +- src/app/page.tsx | 17 +++-- src/components/BackupButton.tsx | 32 ++++++-- src/components/Backups.tsx | 130 ++++++++++++++++++++++++++++++++ src/lib/bluesky.ts | 24 +++--- src/lib/ui.ts | 4 +- 10 files changed, 286 insertions(+), 23 deletions(-) create mode 100644 src/app/backups/[id]/page.tsx create mode 100644 src/app/db.ts create mode 100644 src/components/Backups.tsx diff --git a/package.json b/package.json index 54c8772..1cc14f2 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,8 @@ "@tanstack/react-query": "^5.66.0", "@w3ui/react": "^2.5.5", "@web3-storage/w3up-client": "^17.1.2", + "dexie": "^4.0.11", + "dexie-react-hooks": "^1.1.7", "next": "15.1.6", "postcss": "^8.5.2", "react": "^18.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 78a8994..e0696b4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,12 @@ importers: '@web3-storage/w3up-client': specifier: ^17.1.2 version: 17.1.2(encoding@0.1.13) + dexie: + specifier: ^4.0.11 + version: 4.0.11 + dexie-react-hooks: + specifier: ^1.1.7 + version: 1.1.7(@types/react@18.3.18)(dexie@4.0.11)(react@18.3.1) next: specifier: 15.1.6 version: 15.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1525,6 +1531,16 @@ packages: resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} + dexie-react-hooks@1.1.7: + resolution: {integrity: sha512-Lwv5W0Hk+uOW3kGnsU9GZoR1er1B7WQ5DSdonoNG+focTNeJbHW6vi6nBoX534VKI3/uwHebYzSw1fwY6a7mTw==} + peerDependencies: + '@types/react': '>=16' + dexie: ^3.2 || ^4.0.1-alpha + react: '>=16' + + dexie@4.0.11: + resolution: {integrity: sha512-SOKO002EqlvBYYKQSew3iymBoN2EQ4BDw/3yprjh7kAfFzjBYkaMNa/pZvcA7HSWlcKSQb9XhPe3wKyQ0x4A8A==} + diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -5109,6 +5125,14 @@ snapshots: detect-libc@2.0.3: {} + dexie-react-hooks@1.1.7(@types/react@18.3.18)(dexie@4.0.11)(react@18.3.1): + dependencies: + '@types/react': 18.3.18 + dexie: 4.0.11 + react: 18.3.1 + + dexie@4.0.11: {} + diff@4.0.2: {} doctrine@2.1.0: diff --git a/src/app/backups/[id]/page.tsx b/src/app/backups/[id]/page.tsx new file mode 100644 index 0000000..90c6076 --- /dev/null +++ b/src/app/backups/[id]/page.tsx @@ -0,0 +1,19 @@ +import { Blobs, Repos } from "@/components/Backups" + +export const runtime = 'edge' + +export interface BackupsProps { + params: Promise<{ id: string }> +} + +export default async function Backups ({ params }: BackupsProps) { + const id = (await params).id + + return ( +
+

Backup {id}

+ + +
+ ) +} diff --git a/src/app/db.ts b/src/app/db.ts new file mode 100644 index 0000000..29794ef --- /dev/null +++ b/src/app/db.ts @@ -0,0 +1,55 @@ +'use client' + +import Dexie, { type EntityTable } from 'dexie' + +interface Backup { + id: number; + accountDid: string; + createdAt: Date; +} + +interface Repo { + cid: string; + backupId: number; + accountDid: string; +} + +interface Blob { + cid: string; + backupId: number; + accountDid: string; +} + +interface Commit { + accountDid: string; + commitRev: string; +} + +const db = new Dexie('storacha-bluesky-backups') as Dexie & { + backups: EntityTable< + Backup, + 'id' + >; + repos: EntityTable< + Repo, + 'cid' + >; + blobs: EntityTable< + Blob, + 'cid' + >; + commits: EntityTable< + Commit, + 'accountDid' + >; +}; + +// Schema declaration: +db.version(1).stores({ + backups: 'id++, accountDid, createdAt', + repos: 'cid, backupId, accountDid', + blobs: 'cid, backupId, accountDid', + commits: 'accountDid, commitRev' +}); + +export default db \ No newline at end of file diff --git a/src/app/globals.css b/src/app/globals.css index ba7cbb4..1ab647b 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,7 +1,7 @@ @import "tailwindcss"; .btn { - @apply px-2 py-1 border rounded-lg; + @apply px-2 py-1 border rounded-lg cursor-pointer hover:bg-white; } .ipt { diff --git a/src/app/page.tsx b/src/app/page.tsx index b963091..ab96c84 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,22 +1,29 @@ import BackupButton from "@/components/BackupButton"; import BlueskyAuthenticator from "@/components/BlueskyAuthenticator"; import StorachaAuthenticator from "@/components/StorachaAuthenticator"; +import { Backups } from "@/components/Backups"; export default function Home () { return (
-

Bluesky Backups

+

Bluesky Backups

-

Bluesky Auth

- +

Bluesky Auth

+
+ +
-

Storacha Auth

- +

Storacha Auth

+
+ +
+ +
) } diff --git a/src/components/BackupButton.tsx b/src/components/BackupButton.tsx index d4826ab..edb165b 100644 --- a/src/components/BackupButton.tsx +++ b/src/components/BackupButton.tsx @@ -1,10 +1,30 @@ 'use client' import { useBskyAuthContext } from "@/contexts" -import { backup } from "@/lib/bluesky" +import { backup, BackupMetadataStore } from "@/lib/bluesky" import { Space, useW3 } from "@w3ui/react" import { useState } from "react" import { SpaceFinder } from "./SpaceFinder" +import db from "@/app/db" + +const backupMetadataStore: BackupMetadataStore = { + async setLatestCommit (accountDid, commitRev) { + await db.commits.put({ accountDid, commitRev }) + }, + async addRepo (cid, backupId, accountDid) { + await db.repos.put({ cid, backupId, accountDid }) + }, + async addBlob (cid, backupId, accountDid) { + await db.blobs.put({ cid, backupId, accountDid }) + }, + async addBackup (accountDid) { + return await db.backups.add({ accountDid, createdAt: new Date() }) + } +} + +export interface BackupButtonProps { + backupMetadataStore: BackupMetadataStore +} export default function BackupButton () { const [isBackingUp, setIsBackingUp] = useState(false) @@ -12,13 +32,13 @@ export default function BackupButton () { const [storacha] = useW3() const bluesky = useBskyAuthContext() const backupEvents = new EventTarget() - const space = selectedSpace ?? storacha?.spaces[0] + const space = selectedSpace ?? storacha?.spaces[0] async function onClick () { if (space && bluesky.userProfile && bluesky.agent && storacha.client) { await storacha.client.setCurrentSpace(space.did()) setIsBackingUp(true) - await backup(bluesky.userProfile, bluesky.agent, storacha.client, { eventTarget: backupEvents }) + await backup(bluesky.userProfile, bluesky.agent, storacha.client, backupMetadataStore, { eventTarget: backupEvents }) setIsBackingUp(false) } else { console.log('not backing up, profile, agent, client:', bluesky.userProfile, bluesky.agent, storacha.client) @@ -73,9 +93,9 @@ export default function BackupButton () {

Please choose the Storacha space where you'd like to back up your Bluesky account:

{storacha.spaces && (storacha.spaces.length > 0) && ( - + )}