One-click backups for AT Protocol

feat: show dialog to update

Turtlepaw 90bb5f3e 2872eda2

+83 -51
bun.lock
··· 5 5 "name": "atproto-backup", 6 6 "dependencies": { 7 7 "@atcute/car": "^3.1.1", 8 - "@atproto/api": "^0.15.25", 8 + "@atproto/api": "^0.15.26", 9 9 "@atproto/jwk-webcrypto": "^0.1.9", 10 10 "@atproto/oauth-client-browser": "^0.3.27", 11 11 "@radix-ui/react-avatar": "^1.1.10", 12 12 "@radix-ui/react-checkbox": "^1.3.2", 13 + "@radix-ui/react-dialog": "^1.1.14", 13 14 "@radix-ui/react-dropdown-menu": "^2.1.15", 14 15 "@radix-ui/react-label": "^2.1.7", 15 16 "@radix-ui/react-progress": "^1.1.7", ··· 18 19 "@radix-ui/react-switch": "^1.2.5", 19 20 "@radix-ui/react-tooltip": "^1.2.7", 20 21 "@tailwindcss/vite": "^4.1.11", 21 - "@tauri-apps/api": "^2", 22 - "@tauri-apps/plugin-autostart": "~2", 23 - "@tauri-apps/plugin-deep-link": "~2", 24 - "@tauri-apps/plugin-fs": "~2", 25 - "@tauri-apps/plugin-opener": "^2", 26 - "@tauri-apps/plugin-process": "~2", 27 - "@tauri-apps/plugin-shell": "~2", 28 - "@tauri-apps/plugin-store": "~2", 29 - "@tauri-apps/plugin-updater": "~2", 30 - "@tauri-apps/plugin-websocket": "~2", 31 - "antd": "^5.26.4", 22 + "@tauri-apps/api": "^2.6.0", 23 + "@tauri-apps/plugin-autostart": "~2.5.0", 24 + "@tauri-apps/plugin-deep-link": "~2.4.0", 25 + "@tauri-apps/plugin-fs": "~2.4.0", 26 + "@tauri-apps/plugin-opener": "^2.4.0", 27 + "@tauri-apps/plugin-process": "~2.3.0", 28 + "@tauri-apps/plugin-shell": "~2.3.0", 29 + "@tauri-apps/plugin-store": "~2.3.0", 30 + "@tauri-apps/plugin-updater": "~2.9.0", 31 + "@tauri-apps/plugin-websocket": "~2.4.0", 32 + "antd": "^5.26.6", 32 33 "class-variance-authority": "^0.7.1", 33 34 "clsx": "^2.1.1", 34 35 "lucide-react": "^0.525.0", 35 - "next": "^15.3.5", 36 + "next": "^15.4.2", 36 37 "next-themes": "^0.4.6", 37 38 "nextra": "^4.2.17", 38 39 "nextra-theme-docs": "^4.2.17", 39 40 "react": "^19.1.0", 40 41 "react-dom": "^19.1.0", 41 - "shadcn": "^2.9.0", 42 + "react-markdown": "^10.1.0", 43 + "remark-gfm": "^4.0.1", 44 + "shadcn": "^2.9.2", 45 + "shiki": "^3.8.1", 42 46 "sonner": "^2.0.6", 43 47 "tailwind-merge": "^3.3.1", 44 48 "tailwindcss": "^4.1.11", 45 49 }, 46 50 "devDependencies": { 47 - "@tauri-apps/cli": "^2", 48 - "@types/node": "^24.0.13", 49 - "@types/react": "^18.3.1", 50 - "@types/react-dom": "^18.3.1", 51 - "@vitejs/plugin-react": "^4.3.4", 51 + "@tauri-apps/cli": "^2.6.2", 52 + "@types/node": "^24.0.15", 53 + "@types/react": "^18.3.23", 54 + "@types/react-dom": "^18.3.7", 55 + "@vitejs/plugin-react": "^4.7.0", 52 56 "tw-animate-css": "^1.3.5", 53 - "typescript": "~5.6.2", 54 - "vite": "^6.0.3", 57 + "typescript": "~5.6.3", 58 + "vite": "^6.3.5", 55 59 }, 56 60 }, 57 61 }, ··· 107 111 108 112 "@atproto-labs/simple-store-memory": ["@atproto-labs/simple-store-memory@0.1.3", "", { "dependencies": { "@atproto-labs/simple-store": "0.2.0", "lru-cache": "^10.2.0" } }, "sha512-jkitT9+AtU+0b28DoN92iURLaCt/q/q4yX8q6V+9LSwYlUTqKoj/5NFKvF7x6EBuG+gpUdlcycbH7e60gjOhRQ=="], 109 113 110 - "@atproto/api": ["@atproto/api@0.15.25", "", { "dependencies": { "@atproto/common-web": "^0.4.2", "@atproto/lexicon": "^0.4.12", "@atproto/syntax": "^0.4.0", "@atproto/xrpc": "^0.7.1", "await-lock": "^2.2.2", "multiformats": "^9.9.0", "tlds": "^1.234.0", "zod": "^3.23.8" } }, "sha512-dUqe920qyXd596AI5iJyBlSv0vK5fh1a8PC0zPoeM+FzGDiebnbxuJdPIZ3HeBT/xQ2ce9HwYk1Ih2G7a+JkzQ=="], 114 + "@atproto/api": ["@atproto/api@0.15.26", "", { "dependencies": { "@atproto/common-web": "^0.4.2", "@atproto/lexicon": "^0.4.12", "@atproto/syntax": "^0.4.0", "@atproto/xrpc": "^0.7.1", "await-lock": "^2.2.2", "multiformats": "^9.9.0", "tlds": "^1.234.0", "zod": "^3.23.8" } }, "sha512-AdXGjeCpLZiP9YMGi4YOdK1ayqkBhklmGfSG8UefqR6tTHth59PZvYs5KiwLnFhedt2Xljt3eUlhkn14Y48wEA=="], 111 115 112 116 "@atproto/common-web": ["@atproto/common-web@0.4.2", "", { "dependencies": { "graphemer": "^1.4.0", "multiformats": "^9.9.0", "uint8arrays": "3.0.0", "zod": "^3.23.8" } }, "sha512-vrXwGNoFGogodjQvJDxAeP3QbGtawgZute2ed1XdRO0wMixLk3qewtikZm06H259QDJVu6voKC5mubml+WgQUw=="], 113 117 ··· 381 385 382 386 "@napi-rs/simple-git-win32-x64-msvc": ["@napi-rs/simple-git-win32-x64-msvc@0.1.19", "", { "os": "win32", "cpu": "x64" }, "sha512-FmNuPoK4+qwaSCkp8lm3sJlrxk374enW+zCE5ZksXlZzj/9BDJAULJb5QUJ7o9Y8A/G+d8LkdQLPBE2Jaxe5XA=="], 383 387 384 - "@next/env": ["@next/env@15.3.5", "", {}, "sha512-7g06v8BUVtN2njAX/r8gheoVffhiKFVt4nx74Tt6G4Hqw9HCLYQVx/GkH2qHvPtAHZaUNZ0VXAa0pQP6v1wk7g=="], 388 + "@next/env": ["@next/env@15.4.2", "", {}, "sha512-kd7MvW3pAP7tmk1NaiX4yG15xb2l4gNhteKQxt3f+NGR22qwPymn9RBuv26QKfIKmfo6z2NpgU8W2RT0s0jlvg=="], 385 389 386 - "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.3.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lM/8tilIsqBq+2nq9kbTW19vfwFve0NR7MxfkuSUbRSgXlMQoJYg+31+++XwKVSXk4uT23G2eF/7BRIKdn8t8w=="], 390 + "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.4.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ovqjR8NjCBdBf1U+R/Gvn0RazTtXS9n6wqs84iFaCS1NHbw9ksVE4dfmsYcLoyUVd9BWE0bjkphOWrrz8uz/uw=="], 387 391 388 - "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.3.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-WhwegPQJ5IfoUNZUVsI9TRAlKpjGVK0tpJTL6KeiC4cux9774NYE9Wu/iCfIkL/5J8rPAkqZpG7n+EfiAfidXA=="], 392 + "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.4.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-I8d4W7tPqbdbHRI4z1iBfaoJIBrEG4fnWKIe+Rj1vIucNZ5cEinfwkBt3RcDF00bFRZRDpvKuDjgMFD3OyRBnw=="], 389 393 390 - "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.3.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-LVD6uMOZ7XePg3KWYdGuzuvVboxujGjbcuP2jsPAN3MnLdLoZUXKRc6ixxfs03RH7qBdEHCZjyLP/jBdCJVRJQ=="], 394 + "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.4.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-lvhz02dU3Ec5thzfQ2RCUeOFADjNkS/px1W7MBt7HMhf0/amMfT8Z/aXOwEA+cVWN7HSDRSUc8hHILoHmvajsg=="], 391 395 392 - "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.3.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-k8aVScYZ++BnS2P69ClK7v4nOu702jcF9AIHKu6llhHEtBSmM2zkPGl9yoqbSU/657IIIb0QHpdxEr0iW9z53A=="], 396 + "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.4.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-v+5PPfL8UP+KKHS3Mox7QMoeFdMlaV0zeNMIF7eLC4qTiVSO0RPNnK0nkBZSD5BEkkf//c+vI9s/iHxddCZchA=="], 393 397 394 - "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.3.5", "", { "os": "linux", "cpu": "x64" }, "sha512-2xYU0DI9DGN/bAHzVwADid22ba5d/xrbrQlr2U+/Q5WkFUzeL0TDR963BdrtLS/4bMmKZGptLeg6282H/S2i8A=="], 398 + "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.4.2", "", { "os": "linux", "cpu": "x64" }, "sha512-PHLYOC9W2cu6I/JEKo77+LW4uPNvyEQiSkVRUQPsOIsf01PRr8PtPhwtz3XNnC9At8CrzPkzqQ9/kYDg4R4Inw=="], 395 399 396 - "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.3.5", "", { "os": "linux", "cpu": "x64" }, "sha512-TRYIqAGf1KCbuAB0gjhdn5Ytd8fV+wJSM2Nh2is/xEqR8PZHxfQuaiNhoF50XfY90sNpaRMaGhF6E+qjV1b9Tg=="], 400 + "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.4.2", "", { "os": "linux", "cpu": "x64" }, "sha512-lpmUF9FfLFns4JbTu+5aJGA8aR9dXaA12eoNe9CJbVkGib0FDiPa4kBGTwy0xDxKNGlv3bLDViyx1U+qafmuJQ=="], 397 401 398 - "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.3.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-h04/7iMEUSMY6fDGCvdanKqlO1qYvzNxntZlCzfE8i5P0uqzVQWQquU1TIhlz0VqGQGXLrFDuTJVONpqGqjGKQ=="], 402 + "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.4.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-aMjogoGnRepas0LQ/PBPsvvUzj+IoXw2IoDSEShEtrsu2toBiaxEWzOQuPZ8nie8+1iF7TA63S7rlp3YWAjNEg=="], 399 403 400 - "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.3.5", "", { "os": "win32", "cpu": "x64" }, "sha512-5fhH6fccXxnX2KhllnGhkYMndhOiLOLEiVGYjP2nizqeGWkN10sA9taATlXwake2E2XMvYZjjz0Uj7T0y+z1yw=="], 404 + "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.4.2", "", { "os": "win32", "cpu": "x64" }, "sha512-FxwauyexSFu78wEqR/+NB9MnqXVj6SxJKwcVs2CRjeSX/jBagDCgtR2W36PZUYm0WPgY1pQ3C1+nn7zSnwROuw=="], 401 405 402 406 "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], 403 407 ··· 427 431 428 432 "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], 429 433 434 + "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-focus-guards": "1.1.2", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.4", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw=="], 435 + 430 436 "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], 431 437 432 438 "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.10", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ=="], ··· 517 523 518 524 "@react-types/shared": ["@react-types/shared@3.30.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog=="], 519 525 520 - "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.19", "", {}, "sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA=="], 526 + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], 521 527 522 528 "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.45.0", "", { "os": "android", "cpu": "arm" }, "sha512-2o/FgACbji4tW1dzXOqAV15Eu7DdgbKsF2QKcxfG4xbh5iwU7yr5RRP5/U+0asQliSYv5M4o7BevlGIoSL0LXg=="], 523 529 ··· 559 565 560 566 "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.45.0", "", { "os": "win32", "cpu": "x64" }, "sha512-SRf1cytG7wqcHVLrBc9VtPK4pU5wxiB/lNIkNmW2ApKXIg+RpqwHfsaEK+e7eH4A1BpI6BX/aBWXxZCIrJg3uA=="], 561 567 562 - "@shikijs/core": ["@shikijs/core@2.5.0", "", { "dependencies": { "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.4" } }, "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg=="], 568 + "@shikijs/core": ["@shikijs/core@3.8.1", "", { "dependencies": { "@shikijs/types": "3.8.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-uTSXzUBQ/IgFcUa6gmGShCHr4tMdR3pxUiiWKDm8pd42UKJdYhkAYsAmHX5mTwybQ5VyGDgTjW4qKSsRvGSang=="], 563 569 564 - "@shikijs/engine-javascript": ["@shikijs/engine-javascript@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^3.1.0" } }, "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w=="], 570 + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.8.1", "", { "dependencies": { "@shikijs/types": "3.8.1", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-rZRp3BM1llrHkuBPAdYAzjlF7OqlM0rm/7EWASeCcY7cRYZIrOnGIHE9qsLz5TCjGefxBFnwgIECzBs2vmOyKA=="], 565 571 566 - "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw=="], 572 + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.8.1", "", { "dependencies": { "@shikijs/types": "3.8.1", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-KGQJZHlNY7c656qPFEQpIoqOuC4LrxjyNndRdzk5WKB/Ie87+NJCF1xo9KkOUxwxylk7rT6nhlZyTGTC4fCe1g=="], 567 573 568 - "@shikijs/langs": ["@shikijs/langs@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0" } }, "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w=="], 574 + "@shikijs/langs": ["@shikijs/langs@3.8.1", "", { "dependencies": { "@shikijs/types": "3.8.1" } }, "sha512-TjOFg2Wp1w07oKnXjs0AUMb4kJvujML+fJ1C5cmEj45lhjbUXtziT1x2bPQb9Db6kmPhkG5NI2tgYW1/DzhUuQ=="], 569 575 570 - "@shikijs/themes": ["@shikijs/themes@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0" } }, "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw=="], 576 + "@shikijs/themes": ["@shikijs/themes@3.8.1", "", { "dependencies": { "@shikijs/types": "3.8.1" } }, "sha512-Vu3t3BBLifc0GB0UPg2Pox1naTemrrvyZv2lkiSw3QayVV60me1ujFQwPZGgUTmwXl1yhCPW8Lieesm0CYruLQ=="], 571 577 572 578 "@shikijs/twoslash": ["@shikijs/twoslash@2.5.0", "", { "dependencies": { "@shikijs/core": "2.5.0", "@shikijs/types": "2.5.0", "twoslash": "^0.2.12" } }, "sha512-OdyoZRbzTB80qHFHdaXT070OG9hiljxbsJMZmrMAPWXG2e4FV8wbC63VBM5BJXa1DH645nw20VX1MzASkO5V9g=="], 573 579 574 - "@shikijs/types": ["@shikijs/types@2.5.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw=="], 580 + "@shikijs/types": ["@shikijs/types@3.8.1", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-5C39Q8/8r1I26suLh+5TPk1DTrbY/kn3IdWA5HdizR0FhlhD05zx5nKCqhzSfDHH3p4S0ZefxWd77DLV+8FhGg=="], 575 581 576 582 "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], 577 - 578 - "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], 579 583 580 584 "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], 581 585 ··· 755 759 756 760 "@types/nlcst": ["@types/nlcst@2.0.3", "", { "dependencies": { "@types/unist": "*" } }, "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA=="], 757 761 758 - "@types/node": ["@types/node@24.0.13", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-Qm9OYVOFHFYg3wJoTSrz80hoec5Lia/dPp84do3X7dZvLikQvM1YpmvTBEdIr/e+U8HTkFjLHLnl78K/qjf+jQ=="], 762 + "@types/node": ["@types/node@24.0.15", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-oaeTSbCef7U/z7rDeJA138xpG3NuKc64/rZ2qmUFkFJmnMsAPaluIifqyWd8hSSMxyP9oie3dLAqYPblag9KgA=="], 759 763 760 764 "@types/prop-types": ["@types/prop-types@15.7.15", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="], 761 765 ··· 775 779 776 780 "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], 777 781 778 - "@vitejs/plugin-react": ["@vitejs/plugin-react@4.6.0", "", { "dependencies": { "@babel/core": "^7.27.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.19", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" } }, "sha512-5Kgff+m8e2PB+9j51eGHEpn5kUzRKH2Ry0qGoe8ItJg7pqnkPrYPkDQZGgGmTa0EGarHrkjLvOdU3b1fzI8otQ=="], 782 + "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], 779 783 780 784 "@xmldom/xmldom": ["@xmldom/xmldom@0.9.8", "", {}, "sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A=="], 781 785 ··· 795 799 796 800 "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], 797 801 798 - "antd": ["antd@5.26.4", "", { "dependencies": { "@ant-design/colors": "^7.2.1", "@ant-design/cssinjs": "^1.23.0", "@ant-design/cssinjs-utils": "^1.1.3", "@ant-design/fast-color": "^2.0.6", "@ant-design/icons": "^5.6.1", "@ant-design/react-slick": "~1.1.2", "@babel/runtime": "^7.26.0", "@rc-component/color-picker": "~2.0.1", "@rc-component/mutate-observer": "^1.1.0", "@rc-component/qrcode": "~1.0.0", "@rc-component/tour": "~1.15.1", "@rc-component/trigger": "^2.2.7", "classnames": "^2.5.1", "copy-to-clipboard": "^3.3.3", "dayjs": "^1.11.11", "rc-cascader": "~3.34.0", "rc-checkbox": "~3.5.0", "rc-collapse": "~3.9.0", "rc-dialog": "~9.6.0", "rc-drawer": "~7.3.0", "rc-dropdown": "~4.2.1", "rc-field-form": "~2.7.0", "rc-image": "~7.12.0", "rc-input": "~1.8.0", "rc-input-number": "~9.5.0", "rc-mentions": "~2.20.0", "rc-menu": "~9.16.1", "rc-motion": "^2.9.5", "rc-notification": "~5.6.4", "rc-pagination": "~5.1.0", "rc-picker": "~4.11.3", "rc-progress": "~4.0.0", "rc-rate": "~2.13.1", "rc-resize-observer": "^1.4.3", "rc-segmented": "~2.7.0", "rc-select": "~14.16.8", "rc-slider": "~11.1.8", "rc-steps": "~6.0.1", "rc-switch": "~4.1.0", "rc-table": "~7.51.1", "rc-tabs": "~15.6.1", "rc-textarea": "~1.10.0", "rc-tooltip": "~6.4.0", "rc-tree": "~5.13.1", "rc-tree-select": "~5.27.0", "rc-upload": "~4.9.2", "rc-util": "^5.44.4", "scroll-into-view-if-needed": "^3.1.0", "throttle-debounce": "^5.0.2" }, "peerDependencies": { "react": ">=16.9.0", "react-dom": ">=16.9.0" } }, "sha512-e1EnOvEkvvqcQ18dxfzChBJyJACyih13WpNf2OtnP9z2POh/SF0fXL+ynUemT1zfr+p+P1po/tmHXaMc5PMghg=="], 802 + "antd": ["antd@5.26.6", "", { "dependencies": { "@ant-design/colors": "^7.2.1", "@ant-design/cssinjs": "^1.23.0", "@ant-design/cssinjs-utils": "^1.1.3", "@ant-design/fast-color": "^2.0.6", "@ant-design/icons": "^5.6.1", "@ant-design/react-slick": "~1.1.2", "@babel/runtime": "^7.26.0", "@rc-component/color-picker": "~2.0.1", "@rc-component/mutate-observer": "^1.1.0", "@rc-component/qrcode": "~1.0.0", "@rc-component/tour": "~1.15.1", "@rc-component/trigger": "^2.2.7", "classnames": "^2.5.1", "copy-to-clipboard": "^3.3.3", "dayjs": "^1.11.11", "rc-cascader": "~3.34.0", "rc-checkbox": "~3.5.0", "rc-collapse": "~3.9.0", "rc-dialog": "~9.6.0", "rc-drawer": "~7.3.0", "rc-dropdown": "~4.2.1", "rc-field-form": "~2.7.0", "rc-image": "~7.12.0", "rc-input": "~1.8.0", "rc-input-number": "~9.5.0", "rc-mentions": "~2.20.0", "rc-menu": "~9.16.1", "rc-motion": "^2.9.5", "rc-notification": "~5.6.4", "rc-pagination": "~5.1.0", "rc-picker": "~4.11.3", "rc-progress": "~4.0.0", "rc-rate": "~2.13.1", "rc-resize-observer": "^1.4.3", "rc-segmented": "~2.7.0", "rc-select": "~14.16.8", "rc-slider": "~11.1.8", "rc-steps": "~6.0.1", "rc-switch": "~4.1.0", "rc-table": "~7.51.1", "rc-tabs": "~15.6.1", "rc-textarea": "~1.10.0", "rc-tooltip": "~6.4.0", "rc-tree": "~5.13.1", "rc-tree-select": "~5.27.0", "rc-upload": "~4.9.2", "rc-util": "^5.44.4", "scroll-into-view-if-needed": "^3.1.0", "throttle-debounce": "^5.0.2" }, "peerDependencies": { "react": ">=16.9.0", "react-dom": ">=16.9.0" } }, "sha512-k8ipeT+UL2tP/x4jHTXElScAxsD94JgrIEeGHj80nNO4dL9hqcmaOUBpHo3ieCf6MFjhS7gLUthysQeP6e7DUg=="], 799 803 800 804 "arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="], 801 805 ··· 830 834 "browserslist": ["browserslist@4.25.1", "", { "dependencies": { "caniuse-lite": "^1.0.30001726", "electron-to-chromium": "^1.5.173", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw=="], 831 835 832 836 "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], 833 - 834 - "busboy": ["busboy@1.6.0", "", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="], 835 837 836 838 "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], 837 839 ··· 1193 1195 1194 1196 "headers-polyfill": ["headers-polyfill@4.0.3", "", {}, "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ=="], 1195 1197 1198 + "html-url-attributes": ["html-url-attributes@3.0.1", "", {}, "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ=="], 1199 + 1196 1200 "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], 1197 1201 1198 1202 "http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], ··· 1497 1501 1498 1502 "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], 1499 1503 1500 - "next": ["next@15.3.5", "", { "dependencies": { "@next/env": "15.3.5", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.3.5", "@next/swc-darwin-x64": "15.3.5", "@next/swc-linux-arm64-gnu": "15.3.5", "@next/swc-linux-arm64-musl": "15.3.5", "@next/swc-linux-x64-gnu": "15.3.5", "@next/swc-linux-x64-musl": "15.3.5", "@next/swc-win32-arm64-msvc": "15.3.5", "@next/swc-win32-x64-msvc": "15.3.5", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-RkazLBMMDJSJ4XZQ81kolSpwiCt907l0xcgcpF4xC2Vml6QVcPNXW0NQRwQ80FFtSn7UM52XN0anaw8TEJXaiw=="], 1504 + "next": ["next@15.4.2", "", { "dependencies": { "@next/env": "15.4.2", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.4.2", "@next/swc-darwin-x64": "15.4.2", "@next/swc-linux-arm64-gnu": "15.4.2", "@next/swc-linux-arm64-musl": "15.4.2", "@next/swc-linux-x64-gnu": "15.4.2", "@next/swc-linux-x64-musl": "15.4.2", "@next/swc-win32-arm64-msvc": "15.4.2", "@next/swc-win32-x64-msvc": "15.4.2", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-oH1rmFso+84NIkocfuxaGKcXIjMUTmnzV2x0m8qsYtB4gD6iflLMESXt5XJ8cFgWMBei4v88rNr/j+peNg72XA=="], 1501 1505 1502 1506 "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], 1503 1507 ··· 1527 1531 1528 1532 "onetime": ["onetime@6.0.0", "", { "dependencies": { "mimic-fn": "^4.0.0" } }, "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="], 1529 1533 1530 - "oniguruma-to-es": ["oniguruma-to-es@3.1.1", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ=="], 1534 + "oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="], 1535 + 1536 + "oniguruma-to-es": ["oniguruma-to-es@4.3.3", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg=="], 1531 1537 1532 1538 "ora": ["ora@6.3.1", "", { "dependencies": { "chalk": "^5.0.0", "cli-cursor": "^4.0.0", "cli-spinners": "^2.6.1", "is-interactive": "^2.0.0", "is-unicode-supported": "^1.1.0", "log-symbols": "^5.1.0", "stdin-discarder": "^0.1.0", "strip-ansi": "^7.0.1", "wcwidth": "^1.0.1" } }, "sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ=="], 1533 1539 ··· 1673 1679 1674 1680 "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], 1675 1681 1682 + "react-markdown": ["react-markdown@10.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, "peerDependencies": { "@types/react": ">=18", "react": ">=18" } }, "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ=="], 1683 + 1676 1684 "react-medium-image-zoom": ["react-medium-image-zoom@5.2.14", "", { "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" } }, "sha512-nfTVYcAUnBzXQpPDcZL+cG/e6UceYUIG+zDcnemL7jtAqbJjVVkA85RgneGtJeni12dTyiRPZVM6Szkmwd/o8w=="], 1677 1685 1678 1686 "react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="], ··· 1779 1787 1780 1788 "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], 1781 1789 1782 - "shadcn": ["shadcn@2.9.0", "", { "dependencies": { "@antfu/ni": "^23.2.0", "@babel/core": "^7.22.1", "@babel/parser": "^7.22.6", "@babel/plugin-transform-typescript": "^7.22.5", "@modelcontextprotocol/sdk": "^1.10.2", "commander": "^10.0.0", "cosmiconfig": "^8.1.3", "deepmerge": "^4.3.1", "diff": "^5.1.0", "execa": "^7.0.0", "fast-glob": "^3.3.2", "fs-extra": "^11.1.0", "https-proxy-agent": "^6.2.0", "kleur": "^4.1.5", "msw": "^2.7.1", "node-fetch": "^3.3.0", "ora": "^6.1.2", "postcss": "^8.4.24", "prompts": "^2.4.2", "recast": "^0.23.2", "stringify-object": "^5.0.0", "ts-morph": "^18.0.0", "tsconfig-paths": "^4.2.0", "zod": "^3.20.2", "zod-to-json-schema": "^3.24.5" }, "bin": { "shadcn": "dist/index.js" } }, "sha512-Wc3zs7SnNdLOiXhJFkdAyB+PZLvo9qk1noRz1kH6x3coMLS8f1SjqKlw5kAtX5abpvzLs07PRFnAtKpBApr20g=="], 1790 + "shadcn": ["shadcn@2.9.2", "", { "dependencies": { "@antfu/ni": "^23.2.0", "@babel/core": "^7.22.1", "@babel/parser": "^7.22.6", "@babel/plugin-transform-typescript": "^7.22.5", "@modelcontextprotocol/sdk": "^1.10.2", "commander": "^10.0.0", "cosmiconfig": "^8.1.3", "deepmerge": "^4.3.1", "diff": "^5.1.0", "execa": "^7.0.0", "fast-glob": "^3.3.2", "fs-extra": "^11.1.0", "https-proxy-agent": "^6.2.0", "kleur": "^4.1.5", "msw": "^2.7.1", "node-fetch": "^3.3.0", "ora": "^6.1.2", "postcss": "^8.4.24", "prompts": "^2.4.2", "recast": "^0.23.2", "stringify-object": "^5.0.0", "ts-morph": "^18.0.0", "tsconfig-paths": "^4.2.0", "zod": "^3.20.2", "zod-to-json-schema": "^3.24.5" }, "bin": { "shadcn": "dist/index.js" } }, "sha512-Ssat5Qlosk3XQckSmHEUZ1WDiXXxZbeXEl2HI4QKlBwmboMHYFaVhOMl3ObRVN578C/d369AsKQcgLWF8F5hCA=="], 1783 1791 1784 1792 "sharp": ["sharp@0.34.3", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.4", "semver": "^7.7.2" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.3", "@img/sharp-darwin-x64": "0.34.3", "@img/sharp-libvips-darwin-arm64": "1.2.0", "@img/sharp-libvips-darwin-x64": "1.2.0", "@img/sharp-libvips-linux-arm": "1.2.0", "@img/sharp-libvips-linux-arm64": "1.2.0", "@img/sharp-libvips-linux-ppc64": "1.2.0", "@img/sharp-libvips-linux-s390x": "1.2.0", "@img/sharp-libvips-linux-x64": "1.2.0", "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", "@img/sharp-libvips-linuxmusl-x64": "1.2.0", "@img/sharp-linux-arm": "0.34.3", "@img/sharp-linux-arm64": "0.34.3", "@img/sharp-linux-ppc64": "0.34.3", "@img/sharp-linux-s390x": "0.34.3", "@img/sharp-linux-x64": "0.34.3", "@img/sharp-linuxmusl-arm64": "0.34.3", "@img/sharp-linuxmusl-x64": "0.34.3", "@img/sharp-wasm32": "0.34.3", "@img/sharp-win32-arm64": "0.34.3", "@img/sharp-win32-ia32": "0.34.3", "@img/sharp-win32-x64": "0.34.3" } }, "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg=="], 1785 1793 ··· 1787 1795 1788 1796 "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], 1789 1797 1790 - "shiki": ["shiki@2.5.0", "", { "dependencies": { "@shikijs/core": "2.5.0", "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/langs": "2.5.0", "@shikijs/themes": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ=="], 1798 + "shiki": ["shiki@3.8.1", "", { "dependencies": { "@shikijs/core": "3.8.1", "@shikijs/engine-javascript": "3.8.1", "@shikijs/engine-oniguruma": "3.8.1", "@shikijs/langs": "3.8.1", "@shikijs/themes": "3.8.1", "@shikijs/types": "3.8.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-+MYIyjwGPCaegbpBeFN9+oOifI8CKiKG3awI/6h3JeT85c//H2wDW/xCJEGuQ5jPqtbboKNqNy+JyX9PYpGwNg=="], 1791 1799 1792 1800 "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], 1793 1801 ··· 1818 1826 "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], 1819 1827 1820 1828 "stdin-discarder": ["stdin-discarder@0.1.0", "", { "dependencies": { "bl": "^5.0.0" } }, "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ=="], 1821 - 1822 - "streamsearch": ["streamsearch@1.1.0", "", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="], 1823 1829 1824 1830 "strict-event-emitter": ["strict-event-emitter@0.5.1", "", {}, "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ=="], 1825 1831 ··· 2017 2023 2018 2024 "@inquirer/core/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], 2019 2025 2026 + "@shikijs/twoslash/@shikijs/core": ["@shikijs/core@2.5.0", "", { "dependencies": { "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.4" } }, "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg=="], 2027 + 2028 + "@shikijs/twoslash/@shikijs/types": ["@shikijs/types@2.5.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw=="], 2029 + 2020 2030 "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.4", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.3", "tslib": "^2.4.0" }, "bundled": true }, "sha512-A9CnAbC6ARNMKcIcrQwq6HeHCjpcBZ5wSx4U01WXCqEKlrzB9F9315WDNHkrs2xbx7YjjSxbUYxuN6EQzpcY2g=="], 2021 2031 2022 2032 "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.4.4", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg=="], ··· 2061 2071 2062 2072 "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], 2063 2073 2074 + "nextra/shiki": ["shiki@2.5.0", "", { "dependencies": { "@shikijs/core": "2.5.0", "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/langs": "2.5.0", "@shikijs/themes": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ=="], 2075 + 2064 2076 "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], 2065 2077 2066 2078 "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], ··· 2088 2100 "tough-cookie/universalify": ["universalify@0.2.0", "", {}, "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg=="], 2089 2101 2090 2102 "wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], 2103 + 2104 + "@shikijs/twoslash/@shikijs/core/@shikijs/engine-javascript": ["@shikijs/engine-javascript@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^3.1.0" } }, "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w=="], 2105 + 2106 + "@shikijs/twoslash/@shikijs/core/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw=="], 2091 2107 2092 2108 "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ=="], 2093 2109 ··· 2105 2121 2106 2122 "mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], 2107 2123 2124 + "nextra/shiki/@shikijs/core": ["@shikijs/core@2.5.0", "", { "dependencies": { "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.4" } }, "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg=="], 2125 + 2126 + "nextra/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^3.1.0" } }, "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w=="], 2127 + 2128 + "nextra/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw=="], 2129 + 2130 + "nextra/shiki/@shikijs/langs": ["@shikijs/langs@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0" } }, "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w=="], 2131 + 2132 + "nextra/shiki/@shikijs/themes": ["@shikijs/themes@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0" } }, "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw=="], 2133 + 2134 + "nextra/shiki/@shikijs/types": ["@shikijs/types@2.5.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw=="], 2135 + 2108 2136 "remark-reading-time/unist-util-visit/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], 2109 2137 2110 2138 "remark-reading-time/unist-util-visit/unist-util-is": ["unist-util-is@5.2.1", "", { "dependencies": { "@types/unist": "^2.0.0" } }, "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw=="], ··· 2116 2144 "string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], 2117 2145 2118 2146 "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], 2147 + 2148 + "@shikijs/twoslash/@shikijs/core/@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@3.1.1", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ=="], 2149 + 2150 + "nextra/shiki/@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@3.1.1", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ=="], 2119 2151 } 2120 2152 }
+25 -21
package.json
··· 11 11 }, 12 12 "dependencies": { 13 13 "@atcute/car": "^3.1.1", 14 - "@atproto/api": "^0.15.25", 14 + "@atproto/api": "^0.15.26", 15 15 "@atproto/jwk-webcrypto": "^0.1.9", 16 16 "@atproto/oauth-client-browser": "^0.3.27", 17 17 "@radix-ui/react-avatar": "^1.1.10", 18 18 "@radix-ui/react-checkbox": "^1.3.2", 19 + "@radix-ui/react-dialog": "^1.1.14", 19 20 "@radix-ui/react-dropdown-menu": "^2.1.15", 20 21 "@radix-ui/react-label": "^2.1.7", 21 22 "@radix-ui/react-progress": "^1.1.7", ··· 24 25 "@radix-ui/react-switch": "^1.2.5", 25 26 "@radix-ui/react-tooltip": "^1.2.7", 26 27 "@tailwindcss/vite": "^4.1.11", 27 - "@tauri-apps/api": "^2", 28 - "@tauri-apps/plugin-autostart": "~2", 29 - "@tauri-apps/plugin-deep-link": "~2", 30 - "@tauri-apps/plugin-fs": "~2", 31 - "@tauri-apps/plugin-opener": "^2", 32 - "@tauri-apps/plugin-process": "~2", 33 - "@tauri-apps/plugin-shell": "~2", 34 - "@tauri-apps/plugin-store": "~2", 35 - "@tauri-apps/plugin-updater": "~2", 36 - "@tauri-apps/plugin-websocket": "~2", 37 - "antd": "^5.26.4", 28 + "@tauri-apps/api": "^2.6.0", 29 + "@tauri-apps/plugin-autostart": "~2.5.0", 30 + "@tauri-apps/plugin-deep-link": "~2.4.0", 31 + "@tauri-apps/plugin-fs": "~2.4.0", 32 + "@tauri-apps/plugin-opener": "^2.4.0", 33 + "@tauri-apps/plugin-process": "~2.3.0", 34 + "@tauri-apps/plugin-shell": "~2.3.0", 35 + "@tauri-apps/plugin-store": "~2.3.0", 36 + "@tauri-apps/plugin-updater": "~2.9.0", 37 + "@tauri-apps/plugin-websocket": "~2.4.0", 38 + "antd": "^5.26.6", 38 39 "class-variance-authority": "^0.7.1", 39 40 "clsx": "^2.1.1", 40 41 "lucide-react": "^0.525.0", 41 - "next": "^15.3.5", 42 + "next": "^15.4.2", 42 43 "next-themes": "^0.4.6", 43 44 "nextra": "^4.2.17", 44 45 "nextra-theme-docs": "^4.2.17", 45 46 "react": "^19.1.0", 46 47 "react-dom": "^19.1.0", 47 - "shadcn": "^2.9.0", 48 + "react-markdown": "^10.1.0", 49 + "remark-gfm": "^4.0.1", 50 + "shadcn": "^2.9.2", 51 + "shiki": "^3.8.1", 48 52 "sonner": "^2.0.6", 49 53 "tailwind-merge": "^3.3.1", 50 54 "tailwindcss": "^4.1.11" 51 55 }, 52 56 "devDependencies": { 53 - "@tauri-apps/cli": "^2", 54 - "@types/node": "^24.0.13", 55 - "@types/react": "^18.3.1", 56 - "@types/react-dom": "^18.3.1", 57 - "@vitejs/plugin-react": "^4.3.4", 57 + "@tauri-apps/cli": "^2.6.2", 58 + "@types/node": "^24.0.15", 59 + "@types/react": "^18.3.23", 60 + "@types/react-dom": "^18.3.7", 61 + "@vitejs/plugin-react": "^4.7.0", 58 62 "tw-animate-css": "^1.3.5", 59 - "typescript": "~5.6.2", 60 - "vite": "^6.0.3" 63 + "typescript": "~5.6.3", 64 + "vite": "^6.3.5" 61 65 }, 62 66 "trustedDependencies": [ 63 67 "@tailwindcss/oxide"
+1 -1
src-tauri/Cargo.lock
··· 206 206 207 207 [[package]] 208 208 name = "atbackup" 209 - version = "0.1.2" 209 + version = "0.1.3" 210 210 dependencies = [ 211 211 "chrono", 212 212 "serde",
+1 -1
src-tauri/Cargo.toml
··· 1 1 [package] 2 2 name = "atbackup" 3 - version = "0.1.2" 3 + version = "0.1.3" 4 4 description = "One-click bluesky backups" 5 5 authors = ["Turtlepaw"] 6 6 edition = "2021"
+40 -40
src-tauri/src/lib.rs
··· 42 42 start_background_scheduler, 43 43 stop_background_scheduler 44 44 ]) 45 - // .on_menu_event(|app, event| match event.id.as_ref() { 46 - // "quit" => { 47 - // std::process::exit(0); 48 - // } 49 - // "show" => { 50 - // let window = app.get_webview_window("main").unwrap(); 51 - // window.show().unwrap(); 52 - // window.set_focus().unwrap(); 53 - // } 54 - // "hide" => { 55 - // let window = app.get_webview_window("main").unwrap(); 56 - // window.hide().unwrap(); 57 - // } 58 - // "backup_now" => { 59 - // // Emit event to trigger backup 60 - // app.emit("perform-backup", ()).unwrap(); 61 - // } 62 - // _ => { 63 - // println!("menu item {:?} not handled", event.id); 64 - // } 65 - // }) 66 - // .on_tray_icon_event(|tray, event| match event { 67 - // TrayIconEvent::Click { 68 - // button: MouseButton::Left, 69 - // button_state: MouseButtonState::Up, 70 - // .. 71 - // } => { 72 - // println!("left click pressed and released"); 73 - // // in this example, let's show and focus the main window when the tray is clicked 74 - // let app = tray.app_handle(); 75 - // if let Some(window) = app.get_webview_window("main") { 76 - // let _ = window.show(); 77 - // let _ = window.set_focus(); 78 - // } 79 - // } 80 - // _ => { 81 - // println!("unhandled event {event:?}"); 82 - // } 83 - // }) 45 + .on_menu_event(|app, event| match event.id.as_ref() { 46 + "quit" => { 47 + std::process::exit(0); 48 + } 49 + "show" => { 50 + let window = app.get_webview_window("main").unwrap(); 51 + window.show().unwrap(); 52 + window.set_focus().unwrap(); 53 + } 54 + "hide" => { 55 + let window = app.get_webview_window("main").unwrap(); 56 + window.hide().unwrap(); 57 + } 58 + "backup_now" => { 59 + // Emit event to trigger backup 60 + app.emit("perform-backup", ()).unwrap(); 61 + } 62 + _ => { 63 + println!("menu item {:?} not handled", event.id); 64 + } 65 + }) 66 + .on_tray_icon_event(|tray, event| match event { 67 + TrayIconEvent::Click { 68 + button: MouseButton::Left, 69 + button_state: MouseButtonState::Up, 70 + .. 71 + } => { 72 + println!("left click pressed and released"); 73 + // in this example, let's show and focus the main window when the tray is clicked 74 + let app = tray.app_handle(); 75 + if let Some(window) = app.get_webview_window("main") { 76 + let _ = window.show(); 77 + let _ = window.set_focus(); 78 + } 79 + } 80 + _ => { 81 + println!("unhandled event {event:?}"); 82 + } 83 + }) 84 84 .setup(|app| { 85 85 #[cfg(any(windows, target_os = "linux"))] 86 86 { 87 87 app.deep_link().register_all()?; 88 88 } 89 - //let tray = create_system_tray(app); 89 + let tray = create_system_tray(app); 90 90 91 91 Ok(()) 92 92 });
+1 -1
src-tauri/tauri.conf.json
··· 1 1 { 2 2 "$schema": "https://schema.tauri.app/config/2", 3 3 "productName": "ATBackup", 4 - "version": "0.1.2", 4 + "version": "0.1.3", 5 5 "identifier": "ATBackup", 6 6 "build": { 7 7 "beforeDevCommand": "bun run dev",
+141 -80
src/App.tsx
··· 12 12 import { ScrollArea } from "./components/ui/scroll-area"; 13 13 import { BackupAgent } from "./lib/backup"; 14 14 import { settingsManager } from "./lib/settings"; 15 - import { check } from "@tauri-apps/plugin-updater"; 15 + import { check, Update } from "@tauri-apps/plugin-updater"; 16 16 import { relaunch } from "@tauri-apps/plugin-process"; 17 17 import { 18 18 BackgroundBackupService, 19 19 handleBackgroundBackup, 20 20 } from "./lib/backgroundBackup"; 21 + import { 22 + Dialog, 23 + DialogClose, 24 + DialogContent, 25 + DialogDescription, 26 + DialogFooter, 27 + DialogHeader, 28 + DialogTitle, 29 + DialogTrigger, 30 + } from "@/components/ui/dialog"; 31 + import { Progress } from "./components/ui/progress"; 32 + import { MarkdownRenderer } from "./components/ui/markdown-renderer"; 21 33 22 34 function AppContent() { 23 35 const { isLoading, isAuthenticated, profile, client, login, logout, agent } = ··· 25 37 const appWindow = getCurrentWindow(); 26 38 27 39 const [isLocalStorageReady, setIsLocalStorageReady] = useState(false); 40 + const [update, setUpdate] = useState<Update | null>(null); 41 + const [downloadProgress, setDownloadProgress] = useState<number | null>(null); 28 42 29 43 useEffect(() => { 30 44 const initStorage = async () => { ··· 67 81 }, [isAuthenticated, agent]); 68 82 69 83 // Auto-backup functionality (for when app is open) 70 - useEffect(() => { 71 - if (!isAuthenticated || !agent) return; 84 + // useEffect(() => { 85 + // if (!isAuthenticated || !agent) return; 72 86 73 - let intervalId: ReturnType<typeof setInterval> | null = null; 87 + // let intervalId: ReturnType<typeof setInterval> | null = null; 74 88 75 - const checkAndPerformBackup = async () => { 76 - try { 77 - const lastBackupDate = await settingsManager.getLastBackupDate(); 78 - const frequency = await settingsManager.getBackupFrequency(); 89 + // const checkAndPerformBackup = async () => { 90 + // try { 91 + // const lastBackupDate = await settingsManager.getLastBackupDate(); 92 + // const frequency = await settingsManager.getBackupFrequency(); 79 93 80 - if (!lastBackupDate) { 81 - // No previous backup, so we should do one 82 - await performBackup(); 83 - return; 84 - } 94 + // if (!lastBackupDate) { 95 + // // No previous backup, so we should do one 96 + // await performBackup(); 97 + // return; 98 + // } 85 99 86 - const lastBackup = new Date(lastBackupDate); 87 - const now = new Date(); 88 - const timeDiff = now.getTime() - lastBackup.getTime(); 100 + // const lastBackup = new Date(lastBackupDate); 101 + // const now = new Date(); 102 + // const timeDiff = now.getTime() - lastBackup.getTime(); 89 103 90 - if (frequency === "daily") { 91 - // Check if 24 hours have passed 92 - const oneDay = 24 * 60 * 60 * 1000; 93 - if (timeDiff >= oneDay) { 94 - await performBackup(); 95 - } 96 - } else if (frequency === "weekly") { 97 - // Check if 7 days have passed 98 - const oneWeek = 7 * 24 * 60 * 60 * 1000; 99 - if (timeDiff >= oneWeek) { 100 - await performBackup(); 101 - } 102 - } 103 - } catch (error) { 104 - console.error("Error in automatic backup check:", error); 105 - } 106 - }; 104 + // if (frequency === "daily") { 105 + // // Check if 24 hours have passed 106 + // const oneDay = 24 * 60 * 60 * 1000; 107 + // if (timeDiff >= oneDay) { 108 + // await performBackup(); 109 + // } 110 + // } else if (frequency === "weekly") { 111 + // // Check if 7 days have passed 112 + // const oneWeek = 7 * 24 * 60 * 60 * 1000; 113 + // if (timeDiff >= oneWeek) { 114 + // await performBackup(); 115 + // } 116 + // } 117 + // } catch (error) { 118 + // console.error("Error in automatic backup check:", error); 119 + // } 120 + // }; 107 121 108 - const performBackup = async () => { 109 - try { 110 - console.log("Automatic backup due, starting backup..."); 111 - const manager = new BackupAgent(agent); 112 - await manager.startBackup(); 122 + // const performBackup = async () => { 123 + // try { 124 + // console.log("Automatic backup due, starting backup..."); 125 + // const manager = new BackupAgent(agent); 126 + // await manager.startBackup(); 113 127 114 - // Update the last backup date 115 - await settingsManager.setLastBackupDate(new Date().toISOString()); 128 + // // Update the last backup date 129 + // await settingsManager.setLastBackupDate(new Date().toISOString()); 116 130 117 - console.log("Automatic backup completed successfully"); 118 - } catch (error) { 119 - console.error("Automatic backup failed:", error); 120 - } 121 - }; 131 + // console.log("Automatic backup completed successfully"); 132 + // } catch (error) { 133 + // console.error("Automatic backup failed:", error); 134 + // } 135 + // }; 122 136 123 - // Check immediately when authenticated 124 - checkAndPerformBackup(); 137 + // // Check immediately when authenticated 138 + // checkAndPerformBackup(); 125 139 126 - // Set up interval to check every hour 127 - intervalId = setInterval(checkAndPerformBackup, 60 * 60 * 1000); 140 + // // Set up interval to check every hour 141 + // intervalId = setInterval(checkAndPerformBackup, 60 * 60 * 1000); 128 142 129 - return () => { 130 - if (intervalId) { 131 - clearInterval(intervalId); 132 - } 133 - }; 134 - }, [isAuthenticated, agent]); 143 + // return () => { 144 + // if (intervalId) { 145 + // clearInterval(intervalId); 146 + // } 147 + // }; 148 + // }, [isAuthenticated, agent]); 135 149 136 150 useEffect(() => { 137 151 (async () => { ··· 140 154 console.log( 141 155 `found update ${update.version} from ${update.date} with notes ${update.body}` 142 156 ); 143 - toast("Downloading new update..."); 144 - let downloaded = 0; 145 - let contentLength = 0; 146 - // alternatively we could also call update.download() and update.install() separately 147 - await update.downloadAndInstall((event) => { 148 - switch (event.event) { 149 - case "Started": 150 - //@ts-expect-error 151 - contentLength = event.data.contentLength; 152 - console.log( 153 - `started downloading ${event.data.contentLength} bytes` 154 - ); 155 - break; 156 - case "Progress": 157 - downloaded += event.data.chunkLength; 158 - console.log(`downloaded ${downloaded} from ${contentLength}`); 159 - break; 160 - case "Finished": 161 - console.log("download finished"); 162 - break; 163 - } 164 - }); 165 - 166 - toast("Update ready, restarting..."); 167 - await relaunch(); 157 + setUpdate(update); 168 158 } 169 159 })(); 170 160 }, []); ··· 227 217 </Button> 228 218 </div> 229 219 </div> 220 + 221 + <Dialog 222 + open={update != null} 223 + onOpenChange={(it) => { 224 + if (it == false) setUpdate(null); 225 + }} 226 + > 227 + {/* <DialogTrigger>Open</DialogTrigger> */} 228 + <DialogContent> 229 + <DialogHeader> 230 + <DialogTitle> 231 + New update available ({update?.currentVersion} ➜ {update?.version} 232 + ) 233 + </DialogTitle> 234 + <DialogDescription> 235 + <MarkdownRenderer 236 + children={update?.body ?? "No details provided"} 237 + /> 238 + </DialogDescription> 239 + <DialogFooter className="mt-4"> 240 + {downloadProgress == null ? ( 241 + <> 242 + <DialogClose asChild className="cursor-pointer"> 243 + <Button variant="outline">Skip</Button> 244 + </DialogClose> 245 + <Button 246 + className="cursor-pointer" 247 + onClick={async () => { 248 + if (update == null) toast("Failed: update not found"); 249 + toast("Downloading new update..."); 250 + let downloaded = 0; 251 + let contentLength = 0; 252 + // alternatively we could also call update.download() and update.install() separately 253 + await update!!.downloadAndInstall((event) => { 254 + switch (event.event) { 255 + case "Started": 256 + //@ts-expect-error 257 + contentLength = event.data.contentLength; 258 + setDownloadProgress(0); 259 + console.log( 260 + `started downloading ${event.data.contentLength} bytes` 261 + ); 262 + break; 263 + case "Progress": 264 + downloaded += event.data.chunkLength; 265 + setDownloadProgress(downloaded / contentLength); 266 + console.log( 267 + `downloaded ${downloaded} from ${contentLength}` 268 + ); 269 + break; 270 + case "Finished": 271 + setDownloadProgress(100); 272 + console.log("download finished"); 273 + break; 274 + } 275 + }); 276 + 277 + toast("Update ready, restarting..."); 278 + await relaunch(); 279 + }} 280 + > 281 + Download 282 + </Button> 283 + </> 284 + ) : ( 285 + <Progress value={downloadProgress} className="w-full" /> 286 + )} 287 + </DialogFooter> 288 + </DialogHeader> 289 + </DialogContent> 290 + </Dialog> 230 291 231 292 <ScrollArea> 232 293 {isLoading || !isLocalStorageReady ? (
+36
src/components/hooks/use-copy-to-clipboard.ts
··· 1 + import { useCallback, useRef, useState } from "react"; 2 + import { toast } from "sonner"; 3 + 4 + type UseCopyToClipboardProps = { 5 + text: string; 6 + copyMessage?: string; 7 + }; 8 + 9 + export function useCopyToClipboard({ 10 + text, 11 + copyMessage = "Copied to clipboard!", 12 + }: UseCopyToClipboardProps) { 13 + const [isCopied, setIsCopied] = useState(false); 14 + const timeoutRef = useRef<NodeJS.Timeout | null>(null); 15 + 16 + const handleCopy = useCallback(() => { 17 + navigator.clipboard 18 + .writeText(text) 19 + .then(() => { 20 + toast.success(copyMessage); 21 + setIsCopied(true); 22 + if (timeoutRef.current) { 23 + clearTimeout(timeoutRef.current); 24 + timeoutRef.current = null; 25 + } 26 + timeoutRef.current = setTimeout(() => { 27 + setIsCopied(false); 28 + }, 2000); 29 + }) 30 + .catch(() => { 31 + toast.error("Failed to copy to clipboard."); 32 + }); 33 + }, [text, copyMessage]); 34 + 35 + return { isCopied, handleCopy }; 36 + }
+44
src/components/ui/copy-button.tsx
··· 1 + "use client"; 2 + 3 + import { Check, Copy } from "lucide-react"; 4 + 5 + import { cn } from "@/lib/utils"; 6 + import { useCopyToClipboard } from "@/components/hooks/use-copy-to-clipboard"; 7 + import { Button } from "@/components/ui/button"; 8 + 9 + type CopyButtonProps = { 10 + content: string; 11 + copyMessage?: string; 12 + }; 13 + 14 + export function CopyButton({ content, copyMessage }: CopyButtonProps) { 15 + const { isCopied, handleCopy } = useCopyToClipboard({ 16 + text: content, 17 + copyMessage, 18 + }); 19 + 20 + return ( 21 + <Button 22 + variant="ghost" 23 + size="icon" 24 + className="relative h-6 w-6" 25 + aria-label="Copy to clipboard" 26 + onClick={handleCopy} 27 + > 28 + <div className="absolute inset-0 flex items-center justify-center"> 29 + <Check 30 + className={cn( 31 + "h-4 w-4 transition-transform ease-in-out", 32 + isCopied ? "scale-100" : "scale-0" 33 + )} 34 + /> 35 + </div> 36 + <Copy 37 + className={cn( 38 + "h-4 w-4 transition-transform ease-in-out", 39 + isCopied ? "scale-0" : "scale-100" 40 + )} 41 + /> 42 + </Button> 43 + ); 44 + }
+143
src/components/ui/dialog.tsx
··· 1 + "use client"; 2 + 3 + import * as React from "react"; 4 + import * as DialogPrimitive from "@radix-ui/react-dialog"; 5 + import { XIcon } from "lucide-react"; 6 + 7 + import { cn } from "@/lib/utils"; 8 + 9 + function Dialog({ 10 + ...props 11 + }: React.ComponentProps<typeof DialogPrimitive.Root>) { 12 + return <DialogPrimitive.Root data-slot="dialog" {...props} />; 13 + } 14 + 15 + function DialogTrigger({ 16 + ...props 17 + }: React.ComponentProps<typeof DialogPrimitive.Trigger>) { 18 + return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />; 19 + } 20 + 21 + function DialogPortal({ 22 + ...props 23 + }: React.ComponentProps<typeof DialogPrimitive.Portal>) { 24 + return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />; 25 + } 26 + 27 + function DialogClose({ 28 + ...props 29 + }: React.ComponentProps<typeof DialogPrimitive.Close>) { 30 + return <DialogPrimitive.Close data-slot="dialog-close" {...props} />; 31 + } 32 + 33 + function DialogOverlay({ 34 + className, 35 + ...props 36 + }: React.ComponentProps<typeof DialogPrimitive.Overlay>) { 37 + return ( 38 + <DialogPrimitive.Overlay 39 + data-slot="dialog-overlay" 40 + className={cn( 41 + "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50", 42 + className 43 + )} 44 + {...props} 45 + /> 46 + ); 47 + } 48 + 49 + function DialogContent({ 50 + className, 51 + children, 52 + showCloseButton = true, 53 + ...props 54 + }: React.ComponentProps<typeof DialogPrimitive.Content> & { 55 + showCloseButton?: boolean; 56 + }) { 57 + return ( 58 + <DialogPortal data-slot="dialog-portal"> 59 + <DialogOverlay /> 60 + <DialogPrimitive.Content 61 + data-slot="dialog-content" 62 + className={cn( 63 + "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg", 64 + className 65 + )} 66 + {...props} 67 + > 68 + {children} 69 + {showCloseButton && ( 70 + <DialogPrimitive.Close 71 + data-slot="dialog-close" 72 + className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4" 73 + > 74 + <XIcon /> 75 + <span className="sr-only">Close</span> 76 + </DialogPrimitive.Close> 77 + )} 78 + </DialogPrimitive.Content> 79 + </DialogPortal> 80 + ); 81 + } 82 + 83 + function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { 84 + return ( 85 + <div 86 + data-slot="dialog-header" 87 + className={cn("flex flex-col gap-2 text-center sm:text-left", className)} 88 + {...props} 89 + /> 90 + ); 91 + } 92 + 93 + function DialogFooter({ className, ...props }: React.ComponentProps<"div">) { 94 + return ( 95 + <div 96 + data-slot="dialog-footer" 97 + className={cn( 98 + "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", 99 + className 100 + )} 101 + {...props} 102 + /> 103 + ); 104 + } 105 + 106 + function DialogTitle({ 107 + className, 108 + ...props 109 + }: React.ComponentProps<typeof DialogPrimitive.Title>) { 110 + return ( 111 + <DialogPrimitive.Title 112 + data-slot="dialog-title" 113 + className={cn("text-lg leading-none font-semibold", className)} 114 + {...props} 115 + /> 116 + ); 117 + } 118 + 119 + function DialogDescription({ 120 + className, 121 + ...props 122 + }: React.ComponentProps<typeof DialogPrimitive.Description>) { 123 + return ( 124 + <DialogPrimitive.Description 125 + data-slot="dialog-description" 126 + className={cn("text-muted-foreground text-sm", className)} 127 + {...props} 128 + /> 129 + ); 130 + } 131 + 132 + export { 133 + Dialog, 134 + DialogClose, 135 + DialogContent, 136 + DialogDescription, 137 + DialogFooter, 138 + DialogHeader, 139 + DialogOverlay, 140 + DialogPortal, 141 + DialogTitle, 142 + DialogTrigger, 143 + };
+223
src/components/ui/markdown-renderer.tsx
··· 1 + import React, { Suspense } from "react"; 2 + import Markdown from "react-markdown"; 3 + import remarkGfm from "remark-gfm"; 4 + import type { ThemedToken, TokensResult } from "shiki"; 5 + 6 + import { cn } from "@/lib/utils"; 7 + import { CopyButton } from "@/components/ui/copy-button"; 8 + 9 + interface MarkdownRendererProps { 10 + children: string; 11 + } 12 + 13 + export function MarkdownRenderer({ children }: MarkdownRendererProps) { 14 + return ( 15 + <div className="space-y-3"> 16 + <Markdown remarkPlugins={[remarkGfm]} components={COMPONENTS}> 17 + {children} 18 + </Markdown> 19 + </div> 20 + ); 21 + } 22 + 23 + interface HighlightedPre extends React.HTMLAttributes<HTMLPreElement> { 24 + children: string; 25 + language: string; 26 + } 27 + 28 + // Synchronous wrapper that uses Suspense 29 + const HighlightedPre = ({ children, language, ...props }: HighlightedPre) => { 30 + return ( 31 + <Suspense fallback={<pre {...props}>{children}</pre>}> 32 + <AsyncHighlightedPre language={language} {...props}> 33 + {children} 34 + </AsyncHighlightedPre> 35 + </Suspense> 36 + ); 37 + }; 38 + 39 + // Async logic moved here, loaded with lazy or dynamic inside the component 40 + const AsyncHighlightedPre = (props: HighlightedPre) => { 41 + const [tokens, setTokens] = React.useState<ThemedToken[][] | null>([]); 42 + const [loaded, setLoaded] = React.useState(false); 43 + 44 + React.useEffect(() => { 45 + (async () => { 46 + const { codeToTokens, bundledLanguages } = await import("shiki"); 47 + 48 + if (!(props.language in bundledLanguages)) { 49 + setTokens(null); 50 + setLoaded(true); 51 + return; 52 + } 53 + 54 + const { tokens } = await codeToTokens(props.children, { 55 + lang: props.language as keyof typeof bundledLanguages, 56 + defaultColor: false, 57 + themes: { 58 + light: "github-light", 59 + dark: "github-dark", 60 + }, 61 + }); 62 + 63 + setTokens(tokens); 64 + setLoaded(true); 65 + })(); 66 + }, [props.children, props.language]); 67 + 68 + if (!loaded) { 69 + return <pre {...props}>{props.children}</pre>; 70 + } 71 + 72 + if (!tokens) { 73 + return <pre {...props}>{props.children}</pre>; 74 + } 75 + 76 + return ( 77 + <pre {...props}> 78 + <code> 79 + {tokens.map((line, lineIndex) => ( 80 + <span key={lineIndex}> 81 + {line.map((token, tokenIndex) => { 82 + const style = 83 + typeof token.htmlStyle === "string" 84 + ? undefined 85 + : token.htmlStyle; 86 + 87 + return ( 88 + <span 89 + key={tokenIndex} 90 + className="text-shiki-light bg-shiki-light-bg dark:text-shiki-dark dark:bg-shiki-dark-bg" 91 + style={style} 92 + > 93 + {token.content} 94 + </span> 95 + ); 96 + })} 97 + {"\n"} 98 + </span> 99 + ))} 100 + </code> 101 + </pre> 102 + ); 103 + }; 104 + 105 + HighlightedPre.displayName = "HighlightedCode"; 106 + 107 + interface CodeBlockProps extends React.HTMLAttributes<HTMLPreElement> { 108 + children: React.ReactNode; 109 + className?: string; 110 + language: string; 111 + } 112 + 113 + const CodeBlock = ({ 114 + children, 115 + className, 116 + language, 117 + ...restProps 118 + }: CodeBlockProps) => { 119 + const code = 120 + typeof children === "string" 121 + ? children 122 + : childrenTakeAllStringContents(children); 123 + 124 + const preClass = cn( 125 + "overflow-x-scroll rounded-md border bg-background/50 p-4 font-mono text-sm [scrollbar-width:none]", 126 + className 127 + ); 128 + 129 + return ( 130 + <div className="group/code relative mb-4"> 131 + <Suspense 132 + fallback={ 133 + <pre className={preClass} {...restProps}> 134 + {children} 135 + </pre> 136 + } 137 + > 138 + <HighlightedPre language={language} className={preClass}> 139 + {code} 140 + </HighlightedPre> 141 + </Suspense> 142 + 143 + <div className="invisible absolute right-2 top-2 flex space-x-1 rounded-lg p-1 opacity-0 transition-all duration-200 group-hover/code:visible group-hover/code:opacity-100"> 144 + <CopyButton content={code} copyMessage="Copied code to clipboard" /> 145 + </div> 146 + </div> 147 + ); 148 + }; 149 + 150 + function childrenTakeAllStringContents(element: any): string { 151 + if (typeof element === "string") { 152 + return element; 153 + } 154 + 155 + if (element?.props?.children) { 156 + let children = element.props.children; 157 + 158 + if (Array.isArray(children)) { 159 + return children 160 + .map((child) => childrenTakeAllStringContents(child)) 161 + .join(""); 162 + } else { 163 + return childrenTakeAllStringContents(children); 164 + } 165 + } 166 + 167 + return ""; 168 + } 169 + 170 + const COMPONENTS = { 171 + h1: withClass("h1", "text-2xl font-semibold"), 172 + h2: withClass("h2", "font-semibold text-xl"), 173 + h3: withClass("h3", "font-semibold text-lg"), 174 + h4: withClass("h4", "font-semibold text-base"), 175 + h5: withClass("h5", "font-medium"), 176 + strong: withClass("strong", "font-semibold"), 177 + a: withClass("a", "text-primary underline underline-offset-2"), 178 + blockquote: withClass("blockquote", "border-l-2 border-primary pl-4"), 179 + code: ({ children, className, node, ...rest }: any) => { 180 + const match = /language-(\w+)/.exec(className || ""); 181 + return match ? ( 182 + <CodeBlock className={className} language={match[1]} {...rest}> 183 + {children} 184 + </CodeBlock> 185 + ) : ( 186 + <code 187 + className={cn( 188 + "font-mono [:not(pre)>&]:rounded-md [:not(pre)>&]:bg-background/50 [:not(pre)>&]:px-1 [:not(pre)>&]:py-0.5" 189 + )} 190 + {...rest} 191 + > 192 + {children} 193 + </code> 194 + ); 195 + }, 196 + pre: ({ children }: any) => children, 197 + ol: withClass("ol", "list-decimal space-y-2 pl-6"), 198 + ul: withClass("ul", "list-disc space-y-2 pl-6"), 199 + li: withClass("li", "my-1.5"), 200 + table: withClass( 201 + "table", 202 + "w-full border-collapse overflow-y-auto rounded-md border border-foreground/20" 203 + ), 204 + th: withClass( 205 + "th", 206 + "border border-foreground/20 px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right" 207 + ), 208 + td: withClass( 209 + "td", 210 + "border border-foreground/20 px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right" 211 + ), 212 + tr: withClass("tr", "m-0 border-t p-0 even:bg-muted"), 213 + p: withClass("p", "whitespace-pre-wrap"), 214 + hr: withClass("hr", "border-foreground/20"), 215 + }; 216 + 217 + function withClass(Tag: keyof JSX.IntrinsicElements, classes: string) { 218 + const Component = ({ node, ...props }: any) => ( 219 + <Tag className={classes} {...props} /> 220 + ); 221 + Component.displayName = Tag; 222 + return Component; 223 + }
+16
tailwind.config.js
··· 1 + /** @type {import('tailwindcss').Config} */ 2 + module.exports = { 3 + content: ["./src/**/*.{html,js}"], 4 + theme: { 5 + extend: { 6 + colors: { 7 + shiki: { 8 + light: "var(--shiki-light)", 9 + "light-bg": "var(--shiki-light-bg)", 10 + dark: "var(--shiki-dark)", 11 + "dark-bg": "var(--shiki-dark-bg)", 12 + }, 13 + }, 14 + }, 15 + }, 16 + };