~repos /website
git clone https://pyrossh.dev/repos/website.git
木 Personal website of pyrossh. Built with astrojs, shiki, vite.
0227637b
—
pyrossh 2 weeks ago
update file icons
- bun.lock +32 -2
- package.json +4 -2
- src/components/RecursiveList.astro +14 -4
- src/layouts/FileLayout.astro +13 -7
- src/pages/repos/[...repoId]/files/[...file]/index.astro +9 -11
- src/utils/commit.ts +5 -1
- src/utils/files.ts +33 -1
bun.lock
CHANGED
|
@@ -21,11 +21,13 @@
|
|
|
21
21
|
"@aws-sdk/client-sts": "^3.932.0",
|
|
22
22
|
"@iconify-json/material-symbols": "^1.2.39",
|
|
23
23
|
"@iconify-json/mdi": "^1.2.3",
|
|
24
|
+
"@iconify-json/vscode-icons": "^1.2.36",
|
|
24
25
|
"@playwright/test": "^1.52.0",
|
|
25
26
|
"@tauri-apps/cli": "^2.9.1",
|
|
26
27
|
"@types/bun": "^1.3.2",
|
|
27
28
|
"alchemy": "^0.77.5",
|
|
28
29
|
"diff2html": "^3.4.52",
|
|
30
|
+
"vscode-icons": "https://github.com/vscode-icons/vscode-icons.git",
|
|
29
31
|
},
|
|
30
32
|
},
|
|
31
33
|
},
|
|
@@ -232,6 +234,8 @@
|
|
|
232
234
|
|
|
233
235
|
"@iconify-json/mdi": ["@iconify-json/mdi@1.2.3", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-O3cLwbDOK7NNDf2ihaQOH5F9JglnulNDFV7WprU2dSoZu3h3cWH//h74uQAB87brHmvFVxIOkuBX2sZSzYhScg=="],
|
|
234
236
|
|
|
237
|
+
"@iconify-json/vscode-icons": ["@iconify-json/vscode-icons@1.2.36", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-hs17ocmWK5pGnF5KgFRgHBZVRnMVAIpwPDDvLWSwRiM/uHwIS0Jo0y93DvcHXEsP+d3sqogTGhgkgj3on3eabQ=="],
|
|
238
|
+
|
|
235
239
|
"@iconify/tools": ["@iconify/tools@4.1.3", "", { "dependencies": { "@iconify/types": "^2.0.0", "@iconify/utils": "^2.3.0", "@types/tar": "^6.1.13", "axios": "^1.12.1", "cheerio": "1.0.0", "domhandler": "^5.0.3", "extract-zip": "^2.0.1", "local-pkg": "^0.5.1", "pathe": "^1.1.2", "svgo": "^3.3.2", "tar": "^6.2.1" } }, "sha512-guPw9jvkrCCGFUvPr+NgUcQIpQcIll38NQzUzrEMK/1vrDmeJ9jstsp/Dx5LIP2na9BUBLHKOKXA6cERTpnGFw=="],
|
|
236
240
|
|
|
237
241
|
"@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
|
|
@@ -276,6 +280,12 @@
|
|
|
276
280
|
|
|
277
281
|
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="],
|
|
278
282
|
|
|
283
|
+
"@inversifyjs/common": ["@inversifyjs/common@1.4.0", "", {}, "sha512-qfRJ/3iOlCL/VfJq8+4o5X4oA14cZSBbpAmHsYj8EsIit1xDndoOl0xKOyglKtQD4u4gdNVxMHx4RWARk/I4QA=="],
|
|
284
|
+
|
|
285
|
+
"@inversifyjs/core": ["@inversifyjs/core@1.3.5", "", { "dependencies": { "@inversifyjs/common": "1.4.0", "@inversifyjs/reflect-metadata-utils": "0.2.4" } }, "sha512-B4MFXabhNTAmrfgB+yeD6wd/GIvmvWC6IQ8Rh/j2C3Ix69kmqwz9pr8Jt3E+Nho9aEHOQCZaGmrALgtqRd+oEQ=="],
|
|
286
|
+
|
|
287
|
+
"@inversifyjs/reflect-metadata-utils": ["@inversifyjs/reflect-metadata-utils@0.2.4", "", { "peerDependencies": { "reflect-metadata": "0.2.2" } }, "sha512-u95rV3lKfG+NT2Uy/5vNzoDujos8vN8O18SSA5UyhxsGYd4GLQn/eUsGXfOsfa7m34eKrDelTKRUX1m/BcNX5w=="],
|
|
288
|
+
|
|
279
289
|
"@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
|
|
280
290
|
|
|
281
291
|
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
|
@@ -898,11 +908,13 @@
|
|
|
898
908
|
|
|
899
909
|
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
|
900
910
|
|
|
911
|
+
"inversify": ["inversify@6.2.2", "", { "dependencies": { "@inversifyjs/common": "1.4.0", "@inversifyjs/core": "1.3.5" }, "peerDependencies": { "reflect-metadata": "~0.2.2" } }, "sha512-KB836KHbZ9WrUnB8ax5MtadOwnqQYa+ZJO3KWbPFgcr4RIEnHM621VaqFZzOZd9+U7ln6upt9n0wJei7x2BNqw=="],
|
|
912
|
+
|
|
901
913
|
"iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="],
|
|
902
914
|
|
|
903
915
|
"is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
|
|
904
916
|
|
|
905
|
-
"is-docker": ["is-docker@
|
|
917
|
+
"is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="],
|
|
906
918
|
|
|
907
919
|
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
|
908
920
|
|
|
@@ -914,7 +926,7 @@
|
|
|
914
926
|
|
|
915
927
|
"is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="],
|
|
916
928
|
|
|
917
|
-
"is-wsl": ["is-wsl@
|
|
929
|
+
"is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="],
|
|
918
930
|
|
|
919
931
|
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
|
|
920
932
|
|
|
@@ -938,6 +950,8 @@
|
|
|
938
950
|
|
|
939
951
|
"local-pkg": ["local-pkg@0.5.1", "", { "dependencies": { "mlly": "^1.7.3", "pkg-types": "^1.2.1" } }, "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ=="],
|
|
940
952
|
|
|
953
|
+
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
|
954
|
+
|
|
941
955
|
"loglevel": ["loglevel@1.9.2", "", {}, "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg=="],
|
|
942
956
|
|
|
943
957
|
"longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="],
|
|
@@ -1168,6 +1182,8 @@
|
|
|
1168
1182
|
|
|
1169
1183
|
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
|
1170
1184
|
|
|
1185
|
+
"reflect-metadata": ["reflect-metadata@0.1.14", "", {}, "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A=="],
|
|
1186
|
+
|
|
1171
1187
|
"regex": ["regex@6.0.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA=="],
|
|
1172
1188
|
|
|
1173
1189
|
"regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="],
|
|
@@ -1348,6 +1364,8 @@
|
|
|
1348
1364
|
|
|
1349
1365
|
"vitefu": ["vitefu@1.0.6", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" }, "optionalPeers": ["vite"] }, "sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA=="],
|
|
1350
1366
|
|
|
1367
|
+
"vscode-icons": ["vscode-icons@github:vscode-icons/vscode-icons#a0df042", { "dependencies": { "inversify": "^6.0.1", "lodash": "^4.17.21", "open": "^8.4.2", "reflect-metadata": "^0.1.13", "semver": "^7.5.4" } }, "vscode-icons-vscode-icons-a0df042"],
|
|
1368
|
+
|
|
1351
1369
|
"web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
|
|
1352
1370
|
|
|
1353
1371
|
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
|
@@ -1410,6 +1428,10 @@
|
|
|
1410
1428
|
|
|
1411
1429
|
"@antfu/install-pkg/tinyexec": ["tinyexec@1.0.1", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="],
|
|
1412
1430
|
|
|
1431
|
+
"@astrojs/telemetry/is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
|
|
1432
|
+
|
|
1433
|
+
"@astrojs/telemetry/is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="],
|
|
1434
|
+
|
|
1413
1435
|
"@aws-crypto/sha1-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
|
|
1414
1436
|
|
|
1415
1437
|
"@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
|
|
@@ -1468,6 +1490,8 @@
|
|
|
1468
1490
|
|
|
1469
1491
|
"htmlparser2/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
|
1470
1492
|
|
|
1493
|
+
"is-inside-container/is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
|
|
1494
|
+
|
|
1471
1495
|
"miniflare/acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
|
|
1472
1496
|
|
|
1473
1497
|
"miniflare/ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="],
|
|
@@ -1506,6 +1530,8 @@
|
|
|
1506
1530
|
|
|
1507
1531
|
"vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
|
1508
1532
|
|
|
1533
|
+
"vscode-icons/open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="],
|
|
1534
|
+
|
|
1509
1535
|
"wrangler/@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.7.10", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "^1.20251106.1" }, "optionalPeers": ["workerd"] }, "sha512-mvsNAiJSduC/9yxv1ZpCxwgAXgcuoDvkl8yaHjxoLpFxXy2ugc6TZK20EKgv4yO0vZhAEKwqJm+eGOzf8Oc45w=="],
|
|
1510
1536
|
|
|
1511
1537
|
"wrangler/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="],
|
|
@@ -1522,6 +1548,8 @@
|
|
|
1522
1548
|
|
|
1523
1549
|
"wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
|
1524
1550
|
|
|
1551
|
+
"wsl-utils/is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="],
|
|
1552
|
+
|
|
1525
1553
|
"@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
|
|
1526
1554
|
|
|
1527
1555
|
"@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
|
|
@@ -1548,6 +1576,8 @@
|
|
|
1548
1576
|
|
|
1549
1577
|
"svgo/css-tree/mdn-data": ["mdn-data@2.0.30", "", {}, "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="],
|
|
1550
1578
|
|
|
1579
|
+
"vscode-icons/open/define-lazy-prop": ["define-lazy-prop@2.0.0", "", {}, "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="],
|
|
1580
|
+
|
|
1551
1581
|
"wrangler/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="],
|
|
1552
1582
|
|
|
1553
1583
|
"wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.4", "", { "os": "android", "cpu": "arm" }, "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ=="],
|
package.json
CHANGED
|
@@ -28,16 +28,18 @@
|
|
|
28
28
|
"timeago.js": "^4.0.2"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@types/bun": "^1.3.2",
|
|
32
31
|
"@astrojs/rss": "^4.0.12",
|
|
33
32
|
"@astrojs/sitemap": "^3.4.1",
|
|
34
33
|
"@aws-sdk/client-s3": "^3.932.0",
|
|
35
34
|
"@aws-sdk/client-sts": "^3.932.0",
|
|
36
35
|
"@iconify-json/material-symbols": "^1.2.39",
|
|
37
36
|
"@iconify-json/mdi": "^1.2.3",
|
|
37
|
+
"@iconify-json/vscode-icons": "^1.2.36",
|
|
38
38
|
"@playwright/test": "^1.52.0",
|
|
39
39
|
"@tauri-apps/cli": "^2.9.1",
|
|
40
|
+
"@types/bun": "^1.3.2",
|
|
40
41
|
"alchemy": "^0.77.5",
|
|
41
|
-
"diff2html": "^3.4.52"
|
|
42
|
+
"diff2html": "^3.4.52",
|
|
43
|
+
"vscode-icons": "https://github.com/vscode-icons/vscode-icons.git"
|
|
42
44
|
}
|
|
43
45
|
}
|
src/components/RecursiveList.astro
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { Icon } from "astro-icon/components";
|
|
3
|
+
import allIcons from "@iconify-json/vscode-icons/icons.json" assert { type: "json" };
|
|
3
4
|
import prettyBytes from "pretty-bytes";
|
|
5
|
+
import { resolveFileIcon } from "@/utils/files";
|
|
4
6
|
|
|
5
7
|
interface FileNode {
|
|
6
8
|
name: string;
|
|
7
9
|
path: string;
|
|
8
10
|
isDirectory: boolean;
|
|
11
|
+
size: number;
|
|
12
|
+
ext: string;
|
|
13
|
+
absolutePath: string;
|
|
9
14
|
children?: FileNode[];
|
|
10
|
-
size?: number;
|
|
11
|
-
ext?: string;
|
|
12
|
-
absolutePath?: string;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
interface Props {
|
|
@@ -46,11 +48,19 @@ const { items, repoId, depth = 0 } = Astro.props;
|
|
|
46
48
|
</li>
|
|
47
49
|
);
|
|
48
50
|
} else {
|
|
51
|
+
const iconName = resolveFileIcon(item.name, item.ext);
|
|
52
|
+
const fileType = allIcons.icons[`file-type-${iconName}`]
|
|
53
|
+
? `file-type-${iconName}`
|
|
54
|
+
: "default-file";
|
|
49
55
|
return (
|
|
50
56
|
<li class="file" style={`padding-left: ${depth * 1}rem;`}>
|
|
51
57
|
<div class="file-content">
|
|
52
58
|
<div class="file-name">
|
|
59
|
+
<Icon
|
|
60
|
+
name={`vscode-icons:${fileType}`}
|
|
61
|
+
size="24px"
|
|
53
|
-
|
|
62
|
+
color="hsl(0deg 75% 75%)"
|
|
63
|
+
/>
|
|
54
64
|
<a href={`/repos/${repoId}/files/${item.path}`} rel="nofollow">
|
|
55
65
|
{item.name}
|
|
56
66
|
</a>
|
src/layouts/FileLayout.astro
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { Icon } from "astro-icon/components";
|
|
3
3
|
import { type CollectionEntry } from "astro:content";
|
|
4
|
+
import allIcons from "@iconify-json/vscode-icons/icons.json" assert { type: "json" };
|
|
4
5
|
import RepoLayout from "@/layouts/RepoLayout.astro";
|
|
5
|
-
import
|
|
6
|
+
import { resolveFileIcon, type FileNode } from "@/utils/files";
|
|
6
7
|
|
|
7
8
|
type Props = {
|
|
8
9
|
repo: CollectionEntry<"repos">;
|
|
@@ -11,6 +12,10 @@ type Props = {
|
|
|
11
12
|
|
|
12
13
|
const { repo, file } = Astro.props;
|
|
13
14
|
const { pathname } = Astro.url;
|
|
15
|
+
const iconName = resolveFileIcon(file.name, file.ext);
|
|
16
|
+
const fileType = allIcons.icons[`file-type-${iconName}`]
|
|
17
|
+
? `file-type-${iconName}`
|
|
18
|
+
: "default-file";
|
|
14
19
|
---
|
|
15
20
|
|
|
16
21
|
<RepoLayout repo={repo}>
|
|
@@ -19,8 +24,11 @@ const { pathname } = Astro.url;
|
|
|
19
24
|
<div>
|
|
20
25
|
<hr />
|
|
21
26
|
<div class="title">
|
|
27
|
+
<Icon
|
|
28
|
+
name={`vscode-icons:${fileType}`}
|
|
29
|
+
size="24px"
|
|
22
|
-
|
|
30
|
+
color="hsl(0deg 75% 75%)"
|
|
23
|
-
|
|
31
|
+
/>
|
|
24
32
|
<h3>{file.name}</h3>
|
|
25
33
|
</div>
|
|
26
34
|
<hr />
|
|
@@ -106,11 +114,9 @@ const { pathname } = Astro.url;
|
|
|
106
114
|
font-size: 1.1rem;
|
|
107
115
|
font-weight: 500;
|
|
108
116
|
}
|
|
117
|
+
|
|
109
|
-
|
|
118
|
+
svg[data-icon] {
|
|
110
119
|
font-weight: 600;
|
|
111
120
|
margin-right: 4px;
|
|
112
121
|
}
|
|
113
|
-
h3 {
|
|
114
|
-
text-decoration: underline;
|
|
115
|
-
}
|
|
116
122
|
</style>
|
src/pages/repos/[...repoId]/files/[...file]/index.astro
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
---
|
|
2
2
|
import fs from "fs/promises";
|
|
3
|
-
import path from "path";
|
|
4
3
|
import { type CollectionEntry, getCollection } from "astro:content";
|
|
5
4
|
import { Code } from "astro-expressive-code/components";
|
|
6
5
|
import FileLayout from "@/layouts/FileLayout.astro";
|
|
@@ -12,15 +11,15 @@ export async function getStaticPaths() {
|
|
|
12
11
|
const routes = [];
|
|
13
12
|
for (const repo of repos) {
|
|
14
13
|
const files = await getFiles(repo.data.absolutePath);
|
|
14
|
+
for (const file of files) {
|
|
15
|
-
|
|
15
|
+
routes.push({
|
|
16
|
-
...files.map((file) => ( {
|
|
17
16
|
params: { repoId: repo.id, file: file.name },
|
|
18
17
|
props: {
|
|
19
18
|
repo: repo,
|
|
20
19
|
file: file,
|
|
21
20
|
},
|
|
22
|
-
})
|
|
21
|
+
});
|
|
23
|
-
|
|
22
|
+
}
|
|
24
23
|
}
|
|
25
24
|
return routes;
|
|
26
25
|
}
|
|
@@ -32,8 +31,7 @@ type Props = {
|
|
|
32
31
|
|
|
33
32
|
const { repo, file } = Astro.props;
|
|
34
33
|
const contentBuffer = await fs.readFile(file.absolutePath);
|
|
35
|
-
const ext = path.extname(file.name).replace(".", "");
|
|
36
|
-
const isBinary = ["apk", "dex", "ap_", "jar", "fnt"].includes(ext);
|
|
34
|
+
const isBinary = ["apk", "dex", "ap_", "jar", "fnt"].includes(file.ext);
|
|
37
35
|
const isLarge = file.size > 1024 * 512; // 512KB
|
|
38
36
|
const isImage = [
|
|
39
37
|
"png",
|
|
@@ -46,7 +44,7 @@ const isImage = [
|
|
|
46
44
|
"icns",
|
|
47
45
|
"curve",
|
|
48
46
|
"atlas",
|
|
49
|
-
].includes(ext);
|
|
47
|
+
].includes(file.ext);
|
|
50
48
|
---
|
|
51
49
|
|
|
52
50
|
<FileLayout repo={repo} file={file}>
|
|
@@ -56,7 +54,7 @@ const isImage = [
|
|
|
56
54
|
<p>The file is too large to be displayed ({file.size} bytes).</p>
|
|
57
55
|
) : isImage ? (
|
|
58
56
|
<img
|
|
59
|
-
src={`data:image/${ext};base64,${Buffer.from(contentBuffer).toString("base64")}`}
|
|
57
|
+
src={`data:image/${file.ext};base64,${Buffer.from(contentBuffer).toString("base64")}`}
|
|
60
58
|
alt={file.path}
|
|
61
59
|
/>
|
|
62
60
|
) : (
|
|
@@ -65,10 +63,10 @@ const isImage = [
|
|
|
65
63
|
Buffer.from(contentBuffer).toString("utf-8") ||
|
|
66
64
|
"No content to display."
|
|
67
65
|
}
|
|
68
|
-
lang={ext || "txt"}
|
|
66
|
+
lang={file.ext || "txt"}
|
|
69
67
|
wrap
|
|
70
68
|
/>
|
|
71
69
|
)
|
|
72
70
|
}
|
|
73
71
|
</div>
|
|
74
|
-
</FileLayout>
|
|
72
|
+
</FileLayout>
|
src/utils/commit.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Diff2html from "diff2html";
|
|
2
2
|
import fs from "fs/promises";
|
|
3
|
+
import path from "path";
|
|
3
4
|
import { simpleGit } from "simple-git";
|
|
4
5
|
import { type FileNode } from "./files";
|
|
5
6
|
|
|
@@ -38,10 +39,13 @@ export const getFiles = async (repoPath: string): Promise<FileNode[]> => {
|
|
|
38
39
|
for (const p of paths.split("\n").filter((p) => p.length > 0)) {
|
|
39
40
|
try {
|
|
40
41
|
const stat = await fs.stat(`${repoPath}/${p}`);
|
|
42
|
+
const ext = path.extname(p).replace(".", "");
|
|
41
43
|
files.push({
|
|
42
44
|
name: p,
|
|
45
|
+
ext,
|
|
46
|
+
size: stat.size,
|
|
47
|
+
isDirectory: stat.isDirectory(),
|
|
43
48
|
absolutePath: `${repoPath}/${p}`,
|
|
44
|
-
...stat,
|
|
45
49
|
})
|
|
46
50
|
} catch (e) {
|
|
47
51
|
// Ignore files not found
|
src/utils/files.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import fs from "fs/promises";
|
|
2
|
+
import { extensions as folderExtensions } from "vscode-icons/src/iconsManifest/supportedFolders.ts";
|
|
3
|
+
import { extensions as fileExtensions } from "vscode-icons/src/iconsManifest/supportedExtensions.ts";
|
|
2
4
|
|
|
3
5
|
export interface FileNode {
|
|
4
6
|
name: string;
|
|
@@ -73,4 +75,34 @@ export const checkFileExists = async (filePath: string) => {
|
|
|
73
75
|
} catch (error) {
|
|
74
76
|
return false; // File does not exist or cannot be accessed
|
|
75
77
|
}
|
|
76
|
-
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const resolveFolderIcon = (foldername: string) => {
|
|
81
|
+
for (const item of folderExtensions.supported) {
|
|
82
|
+
if (item.extensions?.includes(foldername)) {
|
|
83
|
+
return item.icon;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return folderExtensions.default.folder?.icon;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const resolveFileIcon = (filename: string, fileExt: string) => {
|
|
90
|
+
for (const item of fileExtensions.supported) {
|
|
91
|
+
if (item.filename && item.extensions?.includes(filename)) {
|
|
92
|
+
return item.icon;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
for (const item of fileExtensions.supported) {
|
|
96
|
+
if (item.extensions?.includes(fileExt)) {
|
|
97
|
+
return item.icon;
|
|
98
|
+
}
|
|
99
|
+
for (const lang of item.languages ?? []) {
|
|
100
|
+
if (lang?.knownExtensions?.includes(fileExt)) {
|
|
101
|
+
return item.icon;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return fileExtensions.default.file?.icon;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.log(resolveFileIcon('examples/public/index.html', 'html'));
|