mirror of
https://github.com/Snigdha-OS/package-browser.git
synced 2025-09-05 20:26:42 +02:00
Compare commits
19 Commits
f36187ec73
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0d1f1c5e74 | ||
![]() |
9b968adbff | ||
![]() |
751a77ac42 | ||
![]() |
0e824e3a04 | ||
![]() |
16611f41c2 | ||
![]() |
b6084343da | ||
![]() |
c09cd6bd43 | ||
![]() |
b2485151b6 | ||
![]() |
154cc8a6b0 | ||
![]() |
f1022a5a41 | ||
![]() |
7f264113c7 | ||
![]() |
c9b4e6615a | ||
![]() |
073d31a3b7 | ||
![]() |
b21d623aa0 | ||
![]() |
fc5b5ba217 | ||
![]() |
6bc0c56de2 | ||
![]() |
176bd3ef9d | ||
![]() |
3f2a66057a | ||
![]() |
9ff67af9f2 |
34
README.md
34
README.md
@@ -1,4 +1,12 @@
|
|||||||
# 📦 Package Browser - Search Packages
|
# 📦 Package Browser - Search Packages
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge" alt="License: MIT"></a>
|
||||||
|
<a href="https://github.com/Snigdha-OS/package-browser/releases"><img src="https://img.shields.io/github/package-json/v/Snigdha-OS/package-browser?style=for-the-badge" alt="Version"></a>
|
||||||
|
<a href="https://github.com/Snigdha-OS/package-browser/issues"><img src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=for-the-badge" alt="Contributions Welcome"></a>
|
||||||
|
<!-- <a href="https://github.com/Snigdha-OS/package-browser/actions"><img src="https://img.shields.io/github/actions/workflow/status/Snigdha-OS/package-browser/ci.yml?branch=main&style=for-the-badge" alt="Build Status"></a> -->
|
||||||
|
<a href="https://snigdha-os.github.io"><img src="https://img.shields.io/website?url=https%3A%2F%2Fsnigdha-os.github.io&style=for-the-badge" alt="Website Status"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
🚀 **Package Browser** is a user-friendly web application designed to simplify browsing, searching, and exploring packages available for Snigdha OS. This tool helps users find the right software with ease, whether for development, daily use, or penetration testing.
|
🚀 **Package Browser** is a user-friendly web application designed to simplify browsing, searching, and exploring packages available for Snigdha OS. This tool helps users find the right software with ease, whether for development, daily use, or penetration testing.
|
||||||
|
|
||||||
@@ -11,25 +19,27 @@
|
|||||||
|
|
||||||
## 🗂️ Repository Structure
|
## 🗂️ Repository Structure
|
||||||
|
|
||||||
```
|
```plaintext
|
||||||
package-browser/
|
package-browser/
|
||||||
.
|
.
|
||||||
|
├── CODE_OF_CONDUCT.md
|
||||||
|
├── config.sh
|
||||||
├── eslint.config.js
|
├── eslint.config.js
|
||||||
├── index.html
|
├── index.html
|
||||||
├── package.json
|
├── package.json
|
||||||
├── package-lock.json
|
|
||||||
├── pnpm-lock.yaml
|
├── pnpm-lock.yaml
|
||||||
├── postcss.config.js
|
├── postcss.config.js
|
||||||
|
├── public
|
||||||
|
│ ├── favicon.ico
|
||||||
|
│ └── snigdhaos-og-image.png
|
||||||
|
├── push.sh
|
||||||
|
├── README.md
|
||||||
├── src
|
├── src
|
||||||
│ ├── App.tsx
|
│ ├── App.tsx
|
||||||
│ ├── components
|
│ ├── components
|
||||||
│ │ ├── Header.tsx
|
│ │ ├── Header.tsx
|
||||||
│ │ ├── InstallGuide.tsx
|
│ │ ├── InstallGuide.tsx
|
||||||
│ │ ├── Logo.tsx
|
│ │ ├── Logo.tsx
|
||||||
│ │ ├── PackageCard
|
|
||||||
│ │ │ ├── Badge.tsx
|
|
||||||
│ │ │ ├── ExpandButton.tsx
|
|
||||||
│ │ │ └── index.tsx
|
|
||||||
│ │ ├── PackageCard.tsx
|
│ │ ├── PackageCard.tsx
|
||||||
│ │ ├── PackageList.tsx
|
│ │ ├── PackageList.tsx
|
||||||
│ │ ├── SearchBar.tsx
|
│ │ ├── SearchBar.tsx
|
||||||
@@ -37,7 +47,13 @@ package-browser/
|
|||||||
│ ├── hooks
|
│ ├── hooks
|
||||||
│ │ ├── usePackages.ts
|
│ │ ├── usePackages.ts
|
||||||
│ │ └── useTheme.ts
|
│ │ └── useTheme.ts
|
||||||
|
│ ├── i18n.tsx
|
||||||
│ ├── index.css
|
│ ├── index.css
|
||||||
|
│ ├── locales
|
||||||
|
│ │ ├── index.tsx
|
||||||
|
│ │ ├── en.tsx
|
||||||
|
│ │ ├── uk.tsx
|
||||||
|
│ │ └── pl.tsx
|
||||||
│ ├── main.tsx
|
│ ├── main.tsx
|
||||||
│ ├── services
|
│ ├── services
|
||||||
│ │ └── api.ts
|
│ │ └── api.ts
|
||||||
@@ -47,7 +63,7 @@ package-browser/
|
|||||||
├── tsconfig.app.json
|
├── tsconfig.app.json
|
||||||
├── tsconfig.json
|
├── tsconfig.json
|
||||||
├── tsconfig.node.json
|
├── tsconfig.node.json
|
||||||
└── vite.config.ts
|
└── vite.config.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🛠️ Installation
|
## 🛠️ Installation
|
||||||
@@ -89,4 +105,4 @@ This project is licensed under the [MIT License](LICENSE).
|
|||||||
|
|
||||||
## 💬 Feedback
|
## 💬 Feedback
|
||||||
|
|
||||||
We’d love to hear your feedback and suggestions! Feel free to open an issue or contact us directly via the [Snigdha OS](https://github.com/Snigdha-OS) organization.
|
We’d love to hear your feedback and suggestions! Feel free to open an issue or contact us directly via the [Snigdha OS](https://github.com/Snigdha-OS) organization.
|
||||||
|
10
index.html
10
index.html
@@ -6,12 +6,12 @@
|
|||||||
<meta name="description" content="Browse and explore Snigdha OS packages easily with this lightweight package viewer." />
|
<meta name="description" content="Browse and explore Snigdha OS packages easily with this lightweight package viewer." />
|
||||||
|
|
||||||
<!-- Author Metadata -->
|
<!-- Author Metadata -->
|
||||||
<meta name="author" content="Eshan Roy, d3v1l0n" />
|
<meta name="author" content="Eshan Roy, d3v1l0n, XlebyllleK" />
|
||||||
<meta name="author:website" content="https://www.eshanized.github.io/, https://www.d3v1l0n.github.io/" />
|
<meta name="author:website" content="https://www.eshanized.github.io/, https://www.d3v1l0n.github.io/, https://github.com/Xlebylllek/" />
|
||||||
<meta name="author:github" content="https://github.com/eshanized, https://github.com/d3v1l0n" />
|
<meta name="author:github" content="https://github.com/eshanized, https://github.com/d3v1l0n, https://github.com/XlebyllleK" />
|
||||||
<meta name="author:twitter" content="https://twitter.com/eshanized, https://twitter.com/d3v1l0n" />
|
<meta name="author:twitter" content="https://twitter.com/eshanized, https://twitter.com/d3v1l0n" />
|
||||||
<meta name="author:bio" content="Eshan Roy is a software developer, open-source enthusiast, and the creator of Snigdha OS. d3v1l0n is a cybersecurity expert and open-source contributor working on various security-focused tools." />
|
<meta name="author:bio" content="Eshan Roy is a software developer, open-source enthusiast, and the creator of Snigdha OS. d3v1l0n is a cybersecurity expert and open-source contributor working on various security-focused tools. XlebyllleK is a software developer, open-source enthusiast, and the creator of Snigdha OS." />
|
||||||
<meta name="author:email" content="m.eshanized@gmail.com, d3v1l0n@outlook.in" />
|
<meta name="author:email" content="m.eshanized@gmail.com, d3v1l0n@outlook.in, celestifyx@gmail.com" />
|
||||||
|
|
||||||
<!-- Open Graph Meta Tags for social media sharing -->
|
<!-- Open Graph Meta Tags for social media sharing -->
|
||||||
<meta property="og:title" content="Snigdha OS Packages" />
|
<meta property="og:title" content="Snigdha OS Packages" />
|
||||||
|
4051
package-lock.json
generated
4051
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -35,7 +35,11 @@
|
|||||||
"gh-pages": "^6.3.0",
|
"gh-pages": "^6.3.0",
|
||||||
"lucide-react": "^0.471.0",
|
"lucide-react": "^0.471.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0"
|
"react-dom": "^19.0.0",
|
||||||
|
"i18next": "^24.2.1",
|
||||||
|
"react-i18next": "^15.4.0",
|
||||||
|
"i18next-http-backend": "^3.0.1",
|
||||||
|
"i18next-browser-languagedetector": "^8.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.17.0",
|
"@eslint/js": "^9.17.0",
|
||||||
|
130
pnpm-lock.yaml
generated
130
pnpm-lock.yaml
generated
@@ -11,6 +11,15 @@ importers:
|
|||||||
gh-pages:
|
gh-pages:
|
||||||
specifier: ^6.3.0
|
specifier: ^6.3.0
|
||||||
version: 6.3.0
|
version: 6.3.0
|
||||||
|
i18next:
|
||||||
|
specifier: ^24.2.1
|
||||||
|
version: 24.2.1(typescript@5.7.3)
|
||||||
|
i18next-browser-languagedetector:
|
||||||
|
specifier: ^8.0.2
|
||||||
|
version: 8.0.2
|
||||||
|
i18next-http-backend:
|
||||||
|
specifier: ^3.0.1
|
||||||
|
version: 3.0.1
|
||||||
lucide-react:
|
lucide-react:
|
||||||
specifier: ^0.471.0
|
specifier: ^0.471.0
|
||||||
version: 0.471.0(react@19.0.0)
|
version: 0.471.0(react@19.0.0)
|
||||||
@@ -20,6 +29,9 @@ importers:
|
|||||||
react-dom:
|
react-dom:
|
||||||
specifier: ^19.0.0
|
specifier: ^19.0.0
|
||||||
version: 19.0.0(react@19.0.0)
|
version: 19.0.0(react@19.0.0)
|
||||||
|
react-i18next:
|
||||||
|
specifier: ^15.4.0
|
||||||
|
version: 15.4.0(i18next@24.2.1(typescript@5.7.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@eslint/js':
|
'@eslint/js':
|
||||||
specifier: ^9.17.0
|
specifier: ^9.17.0
|
||||||
@@ -141,6 +153,10 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@babel/core': ^7.0.0-0
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
|
'@babel/runtime@7.26.0':
|
||||||
|
resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
'@babel/template@7.25.9':
|
'@babel/template@7.25.9':
|
||||||
resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==}
|
resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@@ -690,6 +706,9 @@ packages:
|
|||||||
convert-source-map@2.0.0:
|
convert-source-map@2.0.0:
|
||||||
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
||||||
|
|
||||||
|
cross-fetch@4.0.0:
|
||||||
|
resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==}
|
||||||
|
|
||||||
cross-spawn@7.0.6:
|
cross-spawn@7.0.6:
|
||||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@@ -930,6 +949,23 @@ packages:
|
|||||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
html-parse-stringify@3.0.1:
|
||||||
|
resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==}
|
||||||
|
|
||||||
|
i18next-browser-languagedetector@8.0.2:
|
||||||
|
resolution: {integrity: sha512-shBvPmnIyZeD2VU5jVGIOWP7u9qNG3Lj7mpaiPFpbJ3LVfHZJvVzKR4v1Cb91wAOFpNw442N+LGPzHOHsten2g==}
|
||||||
|
|
||||||
|
i18next-http-backend@3.0.1:
|
||||||
|
resolution: {integrity: sha512-XT2lYSkbAtDE55c6m7CtKxxrsfuRQO3rUfHzj8ZyRtY9CkIX3aRGwXGTkUhpGWce+J8n7sfu3J0f2wTzo7Lw0A==}
|
||||||
|
|
||||||
|
i18next@24.2.1:
|
||||||
|
resolution: {integrity: sha512-Q2wC1TjWcSikn1VAJg13UGIjc+okpFxQTxjVAymOnSA3RpttBQNMPf2ovcgoFVsV4QNxTfNZMAxorXZXsk4fBA==}
|
||||||
|
peerDependencies:
|
||||||
|
typescript: ^5
|
||||||
|
peerDependenciesMeta:
|
||||||
|
typescript:
|
||||||
|
optional: true
|
||||||
|
|
||||||
ignore@5.3.2:
|
ignore@5.3.2:
|
||||||
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
|
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
|
||||||
engines: {node: '>= 4'}
|
engines: {node: '>= 4'}
|
||||||
@@ -1078,6 +1114,15 @@ packages:
|
|||||||
natural-compare@1.4.0:
|
natural-compare@1.4.0:
|
||||||
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
||||||
|
|
||||||
|
node-fetch@2.7.0:
|
||||||
|
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
||||||
|
engines: {node: 4.x || >=6.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
encoding: ^0.1.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
encoding:
|
||||||
|
optional: true
|
||||||
|
|
||||||
node-releases@2.0.19:
|
node-releases@2.0.19:
|
||||||
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
|
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
|
||||||
|
|
||||||
@@ -1223,6 +1268,19 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^19.0.0
|
react: ^19.0.0
|
||||||
|
|
||||||
|
react-i18next@15.4.0:
|
||||||
|
resolution: {integrity: sha512-Py6UkX3zV08RTvL6ZANRoBh9sL/ne6rQq79XlkHEdd82cZr2H9usbWpUNVadJntIZP2pu3M2rL1CN+5rQYfYFw==}
|
||||||
|
peerDependencies:
|
||||||
|
i18next: '>= 23.2.3'
|
||||||
|
react: '>= 16.8.0'
|
||||||
|
react-dom: '*'
|
||||||
|
react-native: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
react-dom:
|
||||||
|
optional: true
|
||||||
|
react-native:
|
||||||
|
optional: true
|
||||||
|
|
||||||
react-refresh@0.14.2:
|
react-refresh@0.14.2:
|
||||||
resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
|
resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -1238,6 +1296,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||||
engines: {node: '>=8.10.0'}
|
engines: {node: '>=8.10.0'}
|
||||||
|
|
||||||
|
regenerator-runtime@0.14.1:
|
||||||
|
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
|
||||||
|
|
||||||
resolve-from@4.0.0:
|
resolve-from@4.0.0:
|
||||||
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -1344,6 +1405,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||||
engines: {node: '>=8.0'}
|
engines: {node: '>=8.0'}
|
||||||
|
|
||||||
|
tr46@0.0.3:
|
||||||
|
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||||
|
|
||||||
trim-repeated@1.0.0:
|
trim-repeated@1.0.0:
|
||||||
resolution: {integrity: sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==}
|
resolution: {integrity: sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -1429,6 +1493,16 @@ packages:
|
|||||||
yaml:
|
yaml:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
void-elements@3.1.0:
|
||||||
|
resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
webidl-conversions@3.0.1:
|
||||||
|
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||||
|
|
||||||
|
whatwg-url@5.0.0:
|
||||||
|
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||||
|
|
||||||
which@2.0.2:
|
which@2.0.2:
|
||||||
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@@ -1554,6 +1628,10 @@ snapshots:
|
|||||||
'@babel/core': 7.26.0
|
'@babel/core': 7.26.0
|
||||||
'@babel/helper-plugin-utils': 7.26.5
|
'@babel/helper-plugin-utils': 7.26.5
|
||||||
|
|
||||||
|
'@babel/runtime@7.26.0':
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime: 0.14.1
|
||||||
|
|
||||||
'@babel/template@7.25.9':
|
'@babel/template@7.25.9':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/code-frame': 7.26.2
|
'@babel/code-frame': 7.26.2
|
||||||
@@ -2037,6 +2115,12 @@ snapshots:
|
|||||||
|
|
||||||
convert-source-map@2.0.0: {}
|
convert-source-map@2.0.0: {}
|
||||||
|
|
||||||
|
cross-fetch@4.0.0:
|
||||||
|
dependencies:
|
||||||
|
node-fetch: 2.7.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- encoding
|
||||||
|
|
||||||
cross-spawn@7.0.6:
|
cross-spawn@7.0.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
path-key: 3.1.1
|
path-key: 3.1.1
|
||||||
@@ -2310,6 +2394,26 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
function-bind: 1.1.2
|
function-bind: 1.1.2
|
||||||
|
|
||||||
|
html-parse-stringify@3.0.1:
|
||||||
|
dependencies:
|
||||||
|
void-elements: 3.1.0
|
||||||
|
|
||||||
|
i18next-browser-languagedetector@8.0.2:
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.26.0
|
||||||
|
|
||||||
|
i18next-http-backend@3.0.1:
|
||||||
|
dependencies:
|
||||||
|
cross-fetch: 4.0.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- encoding
|
||||||
|
|
||||||
|
i18next@24.2.1(typescript@5.7.3):
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.26.0
|
||||||
|
optionalDependencies:
|
||||||
|
typescript: 5.7.3
|
||||||
|
|
||||||
ignore@5.3.2: {}
|
ignore@5.3.2: {}
|
||||||
|
|
||||||
import-fresh@3.3.0:
|
import-fresh@3.3.0:
|
||||||
@@ -2435,6 +2539,10 @@ snapshots:
|
|||||||
|
|
||||||
natural-compare@1.4.0: {}
|
natural-compare@1.4.0: {}
|
||||||
|
|
||||||
|
node-fetch@2.7.0:
|
||||||
|
dependencies:
|
||||||
|
whatwg-url: 5.0.0
|
||||||
|
|
||||||
node-releases@2.0.19: {}
|
node-releases@2.0.19: {}
|
||||||
|
|
||||||
normalize-path@3.0.0: {}
|
normalize-path@3.0.0: {}
|
||||||
@@ -2551,6 +2659,15 @@ snapshots:
|
|||||||
react: 19.0.0
|
react: 19.0.0
|
||||||
scheduler: 0.25.0
|
scheduler: 0.25.0
|
||||||
|
|
||||||
|
react-i18next@15.4.0(i18next@24.2.1(typescript@5.7.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.26.0
|
||||||
|
html-parse-stringify: 3.0.1
|
||||||
|
i18next: 24.2.1(typescript@5.7.3)
|
||||||
|
react: 19.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
|
||||||
react-refresh@0.14.2: {}
|
react-refresh@0.14.2: {}
|
||||||
|
|
||||||
react@19.0.0: {}
|
react@19.0.0: {}
|
||||||
@@ -2563,6 +2680,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
picomatch: 2.3.1
|
picomatch: 2.3.1
|
||||||
|
|
||||||
|
regenerator-runtime@0.14.1: {}
|
||||||
|
|
||||||
resolve-from@4.0.0: {}
|
resolve-from@4.0.0: {}
|
||||||
|
|
||||||
resolve@1.22.10:
|
resolve@1.22.10:
|
||||||
@@ -2701,6 +2820,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-number: 7.0.0
|
is-number: 7.0.0
|
||||||
|
|
||||||
|
tr46@0.0.3: {}
|
||||||
|
|
||||||
trim-repeated@1.0.0:
|
trim-repeated@1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
escape-string-regexp: 1.0.5
|
escape-string-regexp: 1.0.5
|
||||||
@@ -2751,6 +2872,15 @@ snapshots:
|
|||||||
jiti: 1.21.7
|
jiti: 1.21.7
|
||||||
yaml: 2.7.0
|
yaml: 2.7.0
|
||||||
|
|
||||||
|
void-elements@3.1.0: {}
|
||||||
|
|
||||||
|
webidl-conversions@3.0.1: {}
|
||||||
|
|
||||||
|
whatwg-url@5.0.0:
|
||||||
|
dependencies:
|
||||||
|
tr46: 0.0.3
|
||||||
|
webidl-conversions: 3.0.1
|
||||||
|
|
||||||
which@2.0.2:
|
which@2.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
isexe: 2.0.0
|
isexe: 2.0.0
|
||||||
|
36
src/App.tsx
36
src/App.tsx
@@ -13,6 +13,10 @@ import {
|
|||||||
Repository
|
Repository
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
|
import {
|
||||||
|
translate
|
||||||
|
} from './i18n';
|
||||||
|
|
||||||
export default function App(): JSX.Element {
|
export default function App(): JSX.Element {
|
||||||
const { packages, loading, error } = usePackages();
|
const { packages, loading, error } = usePackages();
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
@@ -47,6 +51,8 @@ export default function App(): JSX.Element {
|
|||||||
setSelectedRepository(repo);
|
setSelectedRepository(repo);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const count = filteredPackages.length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-nord-6 dark:bg-nord-0 transition-colors" role="main">
|
<div className="min-h-screen bg-nord-6 dark:bg-nord-0 transition-colors" role="main">
|
||||||
<Header onRepositoryChange={handleRepositoryFilterChange} />
|
<Header onRepositoryChange={handleRepositoryFilterChange} />
|
||||||
@@ -59,26 +65,30 @@ export default function App(): JSX.Element {
|
|||||||
|
|
||||||
{/* Package Counter */}
|
{/* Package Counter */}
|
||||||
<div className="mb-4 flex items-center justify-between">
|
<div className="mb-4 flex items-center justify-between">
|
||||||
<p className="text-sm text-nord-3 dark:text-nord-4" aria-live="polite">
|
<p className="text-sm text-nord-3 dark:text-nord-4" aria-live="polite">{(count === 1) ? translate("App.package_count.single", {
|
||||||
Showing {filteredPackages.length} package{filteredPackages.length !== 1 ? 's' : ''}
|
count
|
||||||
</p>
|
}) : (count >= 2) && (count <= 4) ? translate("App.package_count.multiple", {
|
||||||
|
count
|
||||||
|
}) : translate("App.package_count.plural", {
|
||||||
|
count
|
||||||
|
})}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Error State */}
|
{/* Error State */}
|
||||||
{error ? (
|
{error ? (
|
||||||
<div className="rounded-lg bg-nord-11/10 dark:bg-nord-11/20 p-4 text-nord-11" role="alert">
|
<div className="rounded-lg bg-nord-11/10 dark:bg-nord-11/20 p-4 text-nord-11" role="alert">
|
||||||
<p>An error occurred while fetching packages: {error}</p>
|
<p>{translate("App.error.fetching-packages", {
|
||||||
<button
|
error
|
||||||
onClick={() => window.location.reload()}
|
})}</p>
|
||||||
className="mt-2 inline-block text-sm text-nord-10 hover:underline"
|
|
||||||
>
|
<button onClick={
|
||||||
Retry
|
() => window.location.reload()
|
||||||
</button>
|
} className="mt-2 inline-block text-sm text-nord-10 hover:underline">{translate("App.error.retry-fetching-packages")}</button>
|
||||||
</div>
|
</div>
|
||||||
) : filteredPackages.length === 0 ? (
|
) : (count === 0) ? (
|
||||||
// Empty State
|
// Empty State
|
||||||
<div className="text-center text-nord-3 dark:text-nord-4 mt-12">
|
<div className="text-center text-nord-3 dark:text-nord-4 mt-12">
|
||||||
<p>No packages found matching your search.</p>
|
<p>{translate("App.no-packages-found")}</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
// Package List
|
// Package List
|
||||||
@@ -89,7 +99,7 @@ export default function App(): JSX.Element {
|
|||||||
<footer className="bg-nord-5 dark:bg-nord-1 border-t border-nord-4 dark:border-nord-2 mt-12 transition-colors">
|
<footer className="bg-nord-5 dark:bg-nord-1 border-t border-nord-4 dark:border-nord-2 mt-12 transition-colors">
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
||||||
<p className="text-center text-sm text-nord-3 dark:text-nord-4">
|
<p className="text-center text-sm text-nord-3 dark:text-nord-4">
|
||||||
Data Source:{' '}
|
{translate("App.footer")}{' '}
|
||||||
<a
|
<a
|
||||||
href="https://github.com/Snigdha-OS/snigdhaos-core"
|
href="https://github.com/Snigdha-OS/snigdhaos-core"
|
||||||
className="text-nord-10 hover:text-nord-9 dark:text-nord-8 dark:hover:text-nord-7"
|
className="text-nord-10 hover:text-nord-9 dark:text-nord-8 dark:hover:text-nord-7"
|
||||||
|
@@ -1,18 +1,31 @@
|
|||||||
import {
|
import {
|
||||||
JSX
|
JSX,
|
||||||
|
useEffect
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
import { ThemeToggle } from './ThemeToggle';
|
import {
|
||||||
import { Logo } from './Logo';
|
ThemeToggle
|
||||||
|
} from './ThemeToggle';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Repository
|
Logo
|
||||||
|
} from './Logo';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Repository,
|
||||||
|
Languages
|
||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
MIRRORS
|
MIRRORS
|
||||||
} from '../services/api';
|
} from '../services/api';
|
||||||
|
|
||||||
|
import {
|
||||||
|
translate
|
||||||
|
} from '../i18n';
|
||||||
|
|
||||||
|
import i18next from 'i18next';
|
||||||
|
|
||||||
interface HeaderProps {
|
interface HeaderProps {
|
||||||
onRepositoryChange: (repo: Repository) => void;
|
onRepositoryChange: (repo: Repository) => void;
|
||||||
}
|
}
|
||||||
@@ -23,12 +36,24 @@ export function Header({
|
|||||||
const usedRepositories = new Set(Object.values(MIRRORS).map(mirror => mirror.repository));
|
const usedRepositories = new Set(Object.values(MIRRORS).map(mirror => mirror.repository));
|
||||||
|
|
||||||
const filteredRepository = Object.keys(Repository).reduce((acc, key) => {
|
const filteredRepository = Object.keys(Repository).reduce((acc, key) => {
|
||||||
// This code is flawed because it explicitly checks for 'ALL', reducing flexibility.
|
|
||||||
// A more generic approach should be used to filter unused repositories while preserving 'ALL'.
|
|
||||||
if ((key === 'ALL') || usedRepositories.has(Repository[key as keyof typeof Repository])) acc[key] = Repository[key as keyof typeof Repository];
|
if ((key === 'ALL') || usedRepositories.has(Repository[key as keyof typeof Repository])) acc[key] = Repository[key as keyof typeof Repository];
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<string, string>);
|
}, {} as Record<string, string>);
|
||||||
|
|
||||||
|
const handleLanguageChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
|
const language = event.target.value;
|
||||||
|
|
||||||
|
i18next.changeLanguage(language).then(() => {
|
||||||
|
localStorage.setItem('selectedLanguage', language);
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const savedLanguage = localStorage.getItem('selectedLanguage');
|
||||||
|
if (savedLanguage && (savedLanguage !== i18next.language)) i18next.changeLanguage(savedLanguage);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="bg-gradient-to-r from-nord-9 to-nord-8 via-nord-10 text-nord-6 shadow-lg">
|
<header className="bg-gradient-to-r from-nord-9 to-nord-8 via-nord-10 text-nord-6 shadow-lg">
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||||
@@ -46,7 +71,18 @@ export function Header({
|
|||||||
(e) => onRepositoryChange(e.target.value as Repository)
|
(e) => onRepositoryChange(e.target.value as Repository)
|
||||||
} defaultValue="all" className="bg-nord-5 dark:bg-nord-1 text-black dark:text-white border-2 border-nord-4 dark:border-nord-2 rounded-lg py-2 px-4 focus:ring-2 focus:ring-nord-8">
|
} defaultValue="all" className="bg-nord-5 dark:bg-nord-1 text-black dark:text-white border-2 border-nord-4 dark:border-nord-2 rounded-lg py-2 px-4 focus:ring-2 focus:ring-nord-8">
|
||||||
{Object.values(filteredRepository).map((repository) => (
|
{Object.values(filteredRepository).map((repository) => (
|
||||||
<option key={repository} value={repository}>{((repository === Repository.ALL) ? 'All Repositories' : repository.charAt(0).toUpperCase() + repository.slice(1))}</option>
|
<option key={repository} value={repository}>{((repository === Repository.ALL) ? translate("Header.all-repositories") : repository.charAt(0).toUpperCase() + repository.slice(1))}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Language Dropdown */}
|
||||||
|
<div>
|
||||||
|
<select onChange={handleLanguageChange} defaultValue={i18next.language} className="bg-nord-5 dark:bg-nord-1 text-black dark:text-white border-2 border-nord-4 dark:border-nord-2 rounded-lg py-2 px-4 focus:ring-2 focus:ring-nord-8">
|
||||||
|
{Object.entries(Languages).map(([key, label]) => (
|
||||||
|
<option key={key} value={key.toLowerCase()}>
|
||||||
|
{label}
|
||||||
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -5,6 +5,10 @@ import {
|
|||||||
|
|
||||||
import { Terminal, Copy, Check } from 'lucide-react';
|
import { Terminal, Copy, Check } from 'lucide-react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
translate
|
||||||
|
} from '../i18n';
|
||||||
|
|
||||||
interface InstallGuideProps {
|
interface InstallGuideProps {
|
||||||
packageName: string;
|
packageName: string;
|
||||||
}
|
}
|
||||||
@@ -25,20 +29,13 @@ export function InstallGuide({ packageName }: InstallGuideProps): JSX.Element {
|
|||||||
{/* Title Section */}
|
{/* Title Section */}
|
||||||
<div className="flex items-center gap-3 mb-4">
|
<div className="flex items-center gap-3 mb-4">
|
||||||
<Terminal className="h-5 w-5 text-nord-9 dark:text-nord-7" />
|
<Terminal className="h-5 w-5 text-nord-9 dark:text-nord-7" />
|
||||||
<span className="text-nord-2 dark:text-nord-5 text-sm font-semibold uppercase tracking-wide">
|
<span className="text-nord-2 dark:text-nord-5 text-sm font-semibold uppercase tracking-wide">{translate("InstallGuide.installation-command")}</span>
|
||||||
Installation Command
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Command Display Section */}
|
{/* Command Display Section */}
|
||||||
<div className="flex items-center justify-between bg-nord-4 dark:bg-nord-2 rounded-lg px-5 py-3">
|
<div className="flex items-center justify-between bg-nord-4 dark:bg-nord-2 rounded-lg px-5 py-3">
|
||||||
<code className="text-nord-8 dark:text-nord-6 font-mono text-sm truncate">{command}</code>
|
<code className="text-nord-8 dark:text-nord-6 font-mono text-sm truncate">{command}</code>
|
||||||
<button
|
<button onClick={copyCommand} className="flex items-center justify-center w-8 h-8 rounded-full bg-nord-3 hover:bg-nord-7 dark:bg-nord-5 dark:hover:bg-nord-8 transition-colors" title={copied ? translate("InstallGuide.copy.success") : translate("InstallGuide.copy.prompt")}>
|
||||||
onClick={copyCommand}
|
|
||||||
className="flex items-center justify-center w-8 h-8 rounded-full bg-nord-3 hover:bg-nord-7 dark:bg-nord-5 dark:hover:bg-nord-8 transition-colors"
|
|
||||||
title={copied ? 'Copied!' : 'Copy to clipboard'}
|
|
||||||
aria-label={copied ? 'Copied' : 'Copy command'}
|
|
||||||
>
|
|
||||||
{copied ? (
|
{copied ? (
|
||||||
<Check className="h-4 w-4 text-nord-6 dark:text-nord-0" />
|
<Check className="h-4 w-4 text-nord-6 dark:text-nord-0" />
|
||||||
) : (
|
) : (
|
||||||
|
@@ -2,6 +2,10 @@ import {
|
|||||||
JSX
|
JSX
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
translate
|
||||||
|
} from '../i18n';
|
||||||
|
|
||||||
export function Logo(): JSX.Element {
|
export function Logo(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
@@ -22,10 +26,8 @@ export function Logo(): JSX.Element {
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold tracking-tight">Snigdha OS Package List</h1>
|
<h1 className="text-2xl font-bold tracking-tight">{translate("Logo.title")}</h1>
|
||||||
<p className="text-nord-4 text-sm mt-1">
|
<p className="text-nord-4 text-sm mt-1">{translate("Logo.description")}</p>
|
||||||
Browse and search through the official Snigdha OS package repository
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@@ -7,6 +7,10 @@ import { Box, ChevronDown, ChevronUp } from 'lucide-react';
|
|||||||
import { Package } from '../types';
|
import { Package } from '../types';
|
||||||
import { InstallGuide } from './InstallGuide';
|
import { InstallGuide } from './InstallGuide';
|
||||||
|
|
||||||
|
import {
|
||||||
|
translate
|
||||||
|
} from '../i18n';
|
||||||
|
|
||||||
interface PackageCardProps {
|
interface PackageCardProps {
|
||||||
package: Package;
|
package: Package;
|
||||||
}
|
}
|
||||||
@@ -49,12 +53,12 @@ export function PackageCard({ package: pkg }: PackageCardProps): JSX.Element {
|
|||||||
{expanded ? (
|
{expanded ? (
|
||||||
<>
|
<>
|
||||||
<ChevronUp className="h-5 w-5 animate-bounce" />
|
<ChevronUp className="h-5 w-5 animate-bounce" />
|
||||||
Hide Installation
|
{translate("PackageCard.hide-installation")}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<ChevronDown className="h-5 w-5 animate-bounce" />
|
<ChevronDown className="h-5 w-5 animate-bounce" />
|
||||||
Show Installation
|
{translate("PackageCard.show-installation")}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
@@ -1,38 +0,0 @@
|
|||||||
import React, {
|
|
||||||
JSX
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
interface BadgeProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
color?: 'default' | 'success' | 'warning' | 'error'; // Add more colors as needed
|
|
||||||
size?: 'small' | 'medium' | 'large'; // Size options
|
|
||||||
ariaLabel?: string; // For accessibility
|
|
||||||
}
|
|
||||||
|
|
||||||
const colorClasses = {
|
|
||||||
default: 'bg-gradient-to-r from-nord-7 to-nord-8/80 dark:from-nord-8/50 dark:to-nord-9/80 text-nord-0 dark:text-nord-6',
|
|
||||||
success: 'bg-green-500 text-white',
|
|
||||||
warning: 'bg-yellow-500 text-black',
|
|
||||||
error: 'bg-red-500 text-white',
|
|
||||||
};
|
|
||||||
|
|
||||||
const sizeClasses = {
|
|
||||||
small: 'text-xs px-2 py-1',
|
|
||||||
medium: 'text-sm px-3 py-1.5',
|
|
||||||
large: 'text-base px-4 py-2',
|
|
||||||
};
|
|
||||||
|
|
||||||
export function Badge({ children, color = 'default', size = 'medium', ariaLabel }: BadgeProps): JSX.Element {
|
|
||||||
const badgeColorClass = colorClasses[color] || colorClasses.default;
|
|
||||||
const badgeSizeClass = sizeClasses[size] || sizeClasses.medium;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
className={`inline-flex items-center rounded-full font-semibold shadow-md hover:shadow-lg transition-all duration-300 ${badgeColorClass} ${badgeSizeClass}`}
|
|
||||||
aria-label={ariaLabel}
|
|
||||||
title={ariaLabel} // Optional: Add title for tooltips
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
@@ -1,28 +0,0 @@
|
|||||||
import {
|
|
||||||
JSX
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
import { ChevronDown, ChevronUp } from 'lucide-react';
|
|
||||||
|
|
||||||
interface ExpandButtonProps {
|
|
||||||
expanded: boolean;
|
|
||||||
onClick: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ExpandButton({ expanded, onClick }: ExpandButtonProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
onClick={onClick}
|
|
||||||
className="flex items-center gap-2 px-3 py-1 text-sm font-medium text-nord-9 dark:text-nord-8 hover:text-nord-10 dark:hover:text-nord-7 focus:outline-none focus:ring-2 focus:ring-nord-8 dark:focus:ring-nord-9 rounded-lg transition-all duration-300"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className={`transform transition-transform duration-300 ${
|
|
||||||
expanded ? 'rotate-180' : ''
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{expanded ? <ChevronUp className="h-4 w-4" /> : <ChevronDown className="h-4 w-4" />}
|
|
||||||
</span>
|
|
||||||
{expanded ? 'Hide installation' : 'Show installation'}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
@@ -1,59 +0,0 @@
|
|||||||
import {
|
|
||||||
useState,
|
|
||||||
JSX
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
import { Boxes } from 'lucide-react';
|
|
||||||
import { Package } from '../../types';
|
|
||||||
import { InstallGuide } from '../InstallGuide';
|
|
||||||
import { Badge } from './Badge';
|
|
||||||
import { ExpandButton } from './ExpandButton';
|
|
||||||
|
|
||||||
interface PackageCardProps {
|
|
||||||
package: Package;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function PackageCard({ package: pkg }: PackageCardProps): JSX.Element {
|
|
||||||
const [expanded, setExpanded] = useState(false);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="group bg-nord-5 dark:bg-nord-1 rounded-xl shadow-lg border border-nord-4 dark:border-nord-2 hover:shadow-xl transition-all duration-300 overflow-hidden">
|
|
||||||
{/* Header Section */}
|
|
||||||
<div className="p-5">
|
|
||||||
<div className="flex items-start gap-4">
|
|
||||||
{/* Icon */}
|
|
||||||
<div className="p-3 bg-nord-7/10 dark:bg-nord-7/20 rounded-full shadow-md group-hover:scale-105 transition-transform">
|
|
||||||
<Boxes className="h-6 w-6 text-nord-7" />
|
|
||||||
</div>
|
|
||||||
{/* Content */}
|
|
||||||
<div className="flex-1">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<h3 className="font-semibold text-lg text-nord-0 dark:text-nord-6 group-hover:text-nord-8 transition-colors">
|
|
||||||
{pkg.name}
|
|
||||||
</h3>
|
|
||||||
<span className="text-sm text-nord-3 dark:text-nord-4">{pkg.version}</span>
|
|
||||||
</div>
|
|
||||||
<p className="mt-2 text-sm text-nord-2 dark:text-nord-4 line-clamp-2">
|
|
||||||
{pkg.description}
|
|
||||||
</p>
|
|
||||||
<div className="mt-3 flex items-center justify-between">
|
|
||||||
<Badge>{pkg.repository}</Badge>
|
|
||||||
<ExpandButton
|
|
||||||
expanded={expanded}
|
|
||||||
onClick={() => setExpanded(!expanded)}
|
|
||||||
aria-label={expanded ? 'Collapse package details' : 'Expand package details'}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Expandable Section */}
|
|
||||||
{expanded && (
|
|
||||||
<div className="border-t border-nord-4 dark:border-nord-2 bg-nord-6 dark:bg-nord-0 p-5 transition-opacity duration-300">
|
|
||||||
<InstallGuide packageName={pkg.name} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@@ -1,29 +1,42 @@
|
|||||||
import {
|
import {
|
||||||
JSX
|
JSX,
|
||||||
|
useMemo
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
import { Package } from '../types';
|
import {
|
||||||
import { PackageCard } from './PackageCard';
|
Package
|
||||||
|
} from '../types';
|
||||||
|
|
||||||
|
import {
|
||||||
|
PackageCard
|
||||||
|
} from './PackageCard';
|
||||||
|
|
||||||
interface PackageListProps {
|
interface PackageListProps {
|
||||||
packages: Package[];
|
packages: Package[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PackageList({ packages, loading }: PackageListProps): JSX.Element {
|
export function PackageList({
|
||||||
if (loading) {
|
packages,
|
||||||
return (
|
loading
|
||||||
<div className="flex items-center justify-center h-64">
|
}: PackageListProps): JSX.Element {
|
||||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
|
if (loading) return (
|
||||||
</div>
|
<div className="flex items-center justify-center h-64">
|
||||||
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
const sortedPackages = useMemo(
|
||||||
<div className="grid gap-4">
|
() => [...packages].sort(
|
||||||
{packages.map((pkg) => (
|
(a, b) => a.name.localeCompare(b.name)
|
||||||
<PackageCard key={pkg.name} package={pkg} />
|
), [packages]
|
||||||
))}
|
);
|
||||||
</div>
|
|
||||||
);
|
return (
|
||||||
}
|
<div className="grid gap-4">
|
||||||
|
{sortedPackages.map((pkg) => (
|
||||||
|
<PackageCard key={pkg.name} package={pkg} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@@ -4,6 +4,10 @@ import {
|
|||||||
|
|
||||||
import { Search } from 'lucide-react';
|
import { Search } from 'lucide-react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
translate
|
||||||
|
} from '../i18n';
|
||||||
|
|
||||||
interface SearchBarProps {
|
interface SearchBarProps {
|
||||||
value: string;
|
value: string;
|
||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
@@ -26,7 +30,7 @@ export function SearchBar({
|
|||||||
type="text"
|
type="text"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => onChange(e.target.value)}
|
onChange={(e) => onChange(e.target.value)}
|
||||||
placeholder="Search packages..."
|
placeholder={translate("SearchBar.placeholder")}
|
||||||
className="block w-full pl-10 pr-4 py-3 border-2 border-nord-4 dark:border-nord-2 rounded-xl bg-nord-5 dark:bg-nord-1 focus:ring-2 focus:ring-nord-8 focus:border-transparent text-nord-0 dark:text-nord-6 placeholder-nord-3 dark:placeholder-nord-4 transition-all ease-in-out duration-200 shadow-md hover:shadow-lg"
|
className="block w-full pl-10 pr-4 py-3 border-2 border-nord-4 dark:border-nord-2 rounded-xl bg-nord-5 dark:bg-nord-1 focus:ring-2 focus:ring-nord-8 focus:border-transparent text-nord-0 dark:text-nord-6 placeholder-nord-3 dark:placeholder-nord-4 transition-all ease-in-out duration-200 shadow-md hover:shadow-lg"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
42
src/i18n.tsx
Normal file
42
src/i18n.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import i18n from "i18next";
|
||||||
|
import LanguageDetector from "i18next-browser-languagedetector";
|
||||||
|
|
||||||
|
import {
|
||||||
|
initReactI18next
|
||||||
|
} from "react-i18next";
|
||||||
|
|
||||||
|
import {
|
||||||
|
LOCALES
|
||||||
|
} from './locales';
|
||||||
|
|
||||||
|
const resources = Object.fromEntries(
|
||||||
|
Object.entries(LOCALES).map(([key, value]) => [
|
||||||
|
key.toLowerCase(),
|
||||||
|
|
||||||
|
{
|
||||||
|
translation: value
|
||||||
|
}
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
i18n.use(initReactI18next).use(LanguageDetector).init({
|
||||||
|
resources,
|
||||||
|
fallbackLng: (localStorage.getItem('selectedLanguage') || "en"),
|
||||||
|
|
||||||
|
interpolation: {
|
||||||
|
escapeValue: false
|
||||||
|
},
|
||||||
|
|
||||||
|
detection: {
|
||||||
|
order: ['localStorage', 'navigator'],
|
||||||
|
caches: ['localStorage']
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const translate = (key: string, options?: {
|
||||||
|
[key: string]: any
|
||||||
|
}): string => {
|
||||||
|
return i18n.t(key, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default i18n;
|
50
src/locales/en.tsx
Normal file
50
src/locales/en.tsx
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
export const EN = {
|
||||||
|
// src/components/Header.tsx
|
||||||
|
Header: {
|
||||||
|
"all-repositories": "All Repositories"
|
||||||
|
},
|
||||||
|
|
||||||
|
// src/components/InstallGuide.tsx
|
||||||
|
InstallGuide: {
|
||||||
|
"installation-command": "Installation Command",
|
||||||
|
|
||||||
|
"copy": {
|
||||||
|
"prompt": "Copy to clipboard",
|
||||||
|
"success": "Copied!"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// src/components/Logo.tsx
|
||||||
|
Logo: {
|
||||||
|
"title": "Snigdha OS Package List",
|
||||||
|
"description": "Browse and search through the official Snigdha OS package repository",
|
||||||
|
},
|
||||||
|
|
||||||
|
// src/components/PackageCard.tsx
|
||||||
|
PackageCard: {
|
||||||
|
"show-installation": "Show Installation",
|
||||||
|
"hide-installation": "Hide Installation"
|
||||||
|
},
|
||||||
|
|
||||||
|
// src/components/SearchBar.tsx
|
||||||
|
SearchBar: {
|
||||||
|
"placeholder": "Search packages..."
|
||||||
|
},
|
||||||
|
|
||||||
|
// src/App.tsx
|
||||||
|
App: {
|
||||||
|
"package_count": {
|
||||||
|
"single": "Showing {{count}} package",
|
||||||
|
"multiple": "Showing {{count}} packages",
|
||||||
|
"plural": "Showing {{count}} packages",
|
||||||
|
},
|
||||||
|
|
||||||
|
error: {
|
||||||
|
"fetching-packages": "An error occurred while fetching packages: {{error}}",
|
||||||
|
"retry-fetching-packages": "Retry"
|
||||||
|
},
|
||||||
|
|
||||||
|
"no-packages-found": "No packages found matching your search.",
|
||||||
|
"footer": "Data Source:"
|
||||||
|
}
|
||||||
|
};
|
17
src/locales/index.tsx
Normal file
17
src/locales/index.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import {
|
||||||
|
EN
|
||||||
|
} from './en'; // English
|
||||||
|
|
||||||
|
import {
|
||||||
|
UK
|
||||||
|
} from './uk'; // Ukrainian
|
||||||
|
|
||||||
|
import {
|
||||||
|
PL
|
||||||
|
} from './pl'; // Polish
|
||||||
|
|
||||||
|
export const LOCALES = {
|
||||||
|
EN,
|
||||||
|
UK,
|
||||||
|
PL
|
||||||
|
};
|
50
src/locales/pl.tsx
Normal file
50
src/locales/pl.tsx
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
export const PL = {
|
||||||
|
// src/components/Header.tsx
|
||||||
|
Header: {
|
||||||
|
"all-repositories": "Wszystkie repozytoria"
|
||||||
|
},
|
||||||
|
|
||||||
|
// src/components/InstallGuide.tsx
|
||||||
|
InstallGuide: {
|
||||||
|
"installation-command": "Polecenie instalacji",
|
||||||
|
|
||||||
|
"copy": {
|
||||||
|
"prompt": "Skopiuj do schowka",
|
||||||
|
"success": "Skopiowano!"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// src/components/Logo.tsx
|
||||||
|
Logo: {
|
||||||
|
"title": "Lista pakietów Snigdha OS",
|
||||||
|
"description": "Przeglądaj i wyszukuj w oficjalnym repozytorium pakietów Snigdha OS",
|
||||||
|
},
|
||||||
|
|
||||||
|
// src/components/PackageCard.tsx
|
||||||
|
PackageCard: {
|
||||||
|
"show-installation": "Pokaż instalację",
|
||||||
|
"hide-installation": "Ukryj instalację"
|
||||||
|
},
|
||||||
|
|
||||||
|
// src/components/SearchBar.tsx
|
||||||
|
SearchBar: {
|
||||||
|
"placeholder": "Szukaj pakietów..."
|
||||||
|
},
|
||||||
|
|
||||||
|
// src/App.tsx
|
||||||
|
App: {
|
||||||
|
"package_count": {
|
||||||
|
"single": "Wyświetlanie {{count}} pakietu",
|
||||||
|
"multiple": "Wyświetlanie {{count}} pakietów",
|
||||||
|
"plural": "Wyświetlanie {{count}} pakietów",
|
||||||
|
},
|
||||||
|
|
||||||
|
error: {
|
||||||
|
"fetching-packages": "Wystąpił błąd podczas pobierania pakietów: {{error}}",
|
||||||
|
"retry-fetching-packages": "Spróbuj ponownie"
|
||||||
|
},
|
||||||
|
|
||||||
|
"no-packages-found": "Nie znaleziono pakietów odpowiadających Twojemu wyszukiwaniu.",
|
||||||
|
"footer": "Źródło danych:"
|
||||||
|
}
|
||||||
|
};
|
50
src/locales/uk.tsx
Normal file
50
src/locales/uk.tsx
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
export const UK = {
|
||||||
|
// src/components/Header.tsx
|
||||||
|
Header: {
|
||||||
|
"all-repositories": "Усі репозиторії"
|
||||||
|
},
|
||||||
|
|
||||||
|
// src/components/InstallGuide.tsx
|
||||||
|
InstallGuide: {
|
||||||
|
"installation-command": "Команда для встановлення",
|
||||||
|
|
||||||
|
"copy": {
|
||||||
|
"prompt": "Копіювати в буфер обміну",
|
||||||
|
"success": "Скопійовано!"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// src/components/Logo.tsx
|
||||||
|
Logo: {
|
||||||
|
"title": "Список пакетів Snigdha OS",
|
||||||
|
"description": "Переглядайте та шукайте в офіційному репозиторії пакетів Snigdha OS",
|
||||||
|
},
|
||||||
|
|
||||||
|
// src/components/PackageCard.tsx
|
||||||
|
PackageCard: {
|
||||||
|
"show-installation": "Показати інструкцію з встановлення",
|
||||||
|
"hide-installation": "Сховати інструкцію з встановлення"
|
||||||
|
},
|
||||||
|
|
||||||
|
// src/components/SearchBar.tsx
|
||||||
|
SearchBar: {
|
||||||
|
"placeholder": "Шукати пакети..."
|
||||||
|
},
|
||||||
|
|
||||||
|
// src/App.tsx
|
||||||
|
App: {
|
||||||
|
"package_count": {
|
||||||
|
"single": "Показано {{count}} пакет",
|
||||||
|
"multiple": "Показано {{count}} пакети",
|
||||||
|
"plural": "Показано {{count}} пакетів"
|
||||||
|
},
|
||||||
|
|
||||||
|
error: {
|
||||||
|
"fetching-packages": "Сталася помилка при отриманні пакетів: {{error}}",
|
||||||
|
"retry-fetching-packages": "Спробувати ще раз"
|
||||||
|
},
|
||||||
|
|
||||||
|
"no-packages-found": "Не знайдено жодного пакета, що відповідає вашому запиту.",
|
||||||
|
"footer": "Джерело даних:"
|
||||||
|
}
|
||||||
|
};
|
17
src/main.tsx
17
src/main.tsx
@@ -1,10 +1,17 @@
|
|||||||
import { StrictMode } from 'react';
|
import {
|
||||||
import { createRoot } from 'react-dom/client';
|
StrictMode
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
createRoot
|
||||||
|
} from 'react-dom/client';
|
||||||
|
|
||||||
import App from './App.tsx';
|
import App from './App.tsx';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
import './i18n';
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</StrictMode>
|
</StrictMode>
|
||||||
);
|
);
|
||||||
|
@@ -9,33 +9,27 @@ export const MIRRORS: Record<string, {
|
|||||||
repository: Repository;
|
repository: Repository;
|
||||||
}> = {
|
}> = {
|
||||||
'core': {
|
'core': {
|
||||||
url: 'https://raw.githubusercontent.com/Snigdha-OS/snigdhaos-core/refs/heads/master/packages.txt',
|
url: 'https://raw.githubusercontent.com/Snigdha-OS/snigdhaos-pkgbuilds/refs/heads/master/snigdhaos-core/packages.json',
|
||||||
repository: ('core' as Repository)
|
repository: ('core' as Repository)
|
||||||
},
|
},
|
||||||
|
|
||||||
'extra': {
|
'extra': {
|
||||||
url: 'https://raw.githubusercontent.com/Snigdha-OS/snigdhaos-extra/refs/heads/master/packages.txt',
|
url: 'https://raw.githubusercontent.com/Snigdha-OS/snigdhaos-pkgbuilds/refs/heads/master/snigdhaos-extra/packages.json',
|
||||||
repository: ('extra' as Repository)
|
repository: ('extra' as Repository)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch data from a single mirror (Core or Extra repository)
|
// Fetch data from a single mirror
|
||||||
async function fetchFromMirror(url: string, repository: Repository): Promise<Package[]> {
|
async function fetchFromMirror(url: string, repository: Repository): Promise<Package[]> {
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
const text = await response.text();
|
const data = await response.json();
|
||||||
|
|
||||||
// Parse the text file content and return a list of packages
|
|
||||||
return text.split('\n').filter(Boolean).map((line) => {
|
|
||||||
const [
|
|
||||||
name,
|
|
||||||
version,
|
|
||||||
...description
|
|
||||||
] = line.split(' ');
|
|
||||||
|
|
||||||
|
// Parse the json file content and return a list of packages
|
||||||
|
return data.map((item: any) => {
|
||||||
return {
|
return {
|
||||||
name,
|
name: item.name,
|
||||||
version,
|
version: item.version,
|
||||||
description: description.join(' '),
|
description: item.description,
|
||||||
repository
|
repository
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@@ -10,6 +10,13 @@ export enum Repository {
|
|||||||
// Type alias for UI themes
|
// Type alias for UI themes
|
||||||
export type Theme = ('light' | 'dark');
|
export type Theme = ('light' | 'dark');
|
||||||
|
|
||||||
|
// Enumeration of supported languages
|
||||||
|
export enum Languages {
|
||||||
|
EN = "English",
|
||||||
|
UK = "Українська",
|
||||||
|
PL = "Polski"
|
||||||
|
}
|
||||||
|
|
||||||
// Interface representing a single package
|
// Interface representing a single package
|
||||||
export interface Package {
|
export interface Package {
|
||||||
/** The name of the package */
|
/** The name of the package */
|
||||||
|
Reference in New Issue
Block a user