chore: update ESLint and Prettier configurations for improved code quality

- Removed the outdated .eslintrc.json file and replaced it with a new eslint.config.mjs file for better configuration management.
- Added .prettierrc.json to enforce consistent code formatting and included a .prettierignore file to exclude specific directories from formatting.
- Updated components.json to streamline alias definitions and ensure proper icon library usage.
- Enhanced package.json with new linting, formatting, and validation scripts to improve development workflow.
- Made various formatting adjustments across multiple files for consistency and clarity.
This commit is contained in:
Daniel Luiz Alves
2025-07-02 12:01:59 -03:00
parent 2c6699b604
commit 4fb7007db2
65 changed files with 3592 additions and 2821 deletions

View File

@@ -1,3 +0,0 @@
{
"extends": ["next/core-web-vitals", "next/typescript"]
}

View File

@@ -0,0 +1,4 @@
/node_modules
/.next
/out
/build

View File

@@ -0,0 +1,16 @@
{
"importOrder": [
"^(react/(.*)$)|^(react$)",
"^(next/(.*)$)|^(next$)",
"<THIRD_PARTY_MODULES>",
"",
"^@/(.*)$",
"^[./]"
],
"importOrderParserPlugins": ["typescript", "jsx", "decorators-legacy"],
"plugins": ["@ianvs/prettier-plugin-sort-imports", "prettier-plugin-sort-json"],
"printWidth": 120,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "es5"
}

View File

@@ -1,15 +1,5 @@
{ {
"$schema": "https://ui.shadcn.com/schema.json", "$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/app/global.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": { "aliases": {
"components": "@/components", "components": "@/components",
"utils": "@/lib/utils", "utils": "@/lib/utils",
@@ -17,5 +7,15 @@
"lib": "@/lib", "lib": "@/lib",
"hooks": "@/hooks" "hooks": "@/hooks"
}, },
"iconLibrary": "lucide" "iconLibrary": "lucide",
"rsc": true,
"style": "new-york",
"tailwind": {
"config": "",
"css": "src/app/global.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"tsx": true
} }

View File

@@ -18,7 +18,6 @@ The API documentation is powered by **[Scalar](https://scalar.com/)**, which pro
![Palmr API Documentation](/assets/v2/api-docs/scalar.png) ![Palmr API Documentation](/assets/v2/api-docs/scalar.png)
We have made a deliberate decision to **not provide an online version** of the API documentation, as the endpoints and functionality may vary significantly depending on the specific version of Palmr. you have deployed in your environment. To ensure you're always working with accurate and version-specific documentation, we strongly recommend accessing the documentation only after initializing your API service. It's important to note that the API service is specifically designated as the **server** component within the official Palmr. GitHub repository. We have made a deliberate decision to **not provide an online version** of the API documentation, as the endpoints and functionality may vary significantly depending on the specific version of Palmr. you have deployed in your environment. To ensure you're always working with accurate and version-specific documentation, we strongly recommend accessing the documentation only after initializing your API service. It's important to note that the API service is specifically designated as the **server** component within the official Palmr. GitHub repository.
We strongly recommend utilizing **Scalar** as your primary tool for querying and testing the API, as the entire documentation system has been carefully optimized and designed with Scalar integration in mind. Scalar provides developers with an exceptionally intuitive and feature-rich interactive environment that streamlines the process of exploring endpoints, constructing and sending requests, and analyzing responses directly within its sophisticated interface. We strongly recommend utilizing **Scalar** as your primary tool for querying and testing the API, as the entire documentation system has been carefully optimized and designed with Scalar integration in mind. Scalar provides developers with an exceptionally intuitive and feature-rich interactive environment that streamlines the process of exploring endpoints, constructing and sending requests, and analyzing responses directly within its sophisticated interface.

View File

@@ -12,51 +12,50 @@ Understanding the architecture of Palmr. is crucial for both deploying and scali
Each component in the Palmr. architecture plays a vital role in ensuring reliability, performance, and scalability. The stack is built with simplicity, performance, and flexibility in mind, everything is self-hosted, developer-friendly, and designed to scale without adding unnecessary complexity. Each component in the Palmr. architecture plays a vital role in ensuring reliability, performance, and scalability. The stack is built with simplicity, performance, and flexibility in mind, everything is self-hosted, developer-friendly, and designed to scale without adding unnecessary complexity.
### 💾 PostgreSQL ### 💾 PostgreSQL
Palmr. uses **PostgreSQL** as the primary database solution. It's a powerful, open-source relational database thats trusted by developers around the world. PostgreSQL is fully ACID-compliant, which means it handles transactions safely and reliably. Its perfect for storing structured data like user accounts, file metadata, transfer logs, and anything else that requires consistency. With advanced features like full-text search, custom data types (like JSONB), and strong indexing capabilities, PostgreSQL gives us the tools to scale efficiently without giving up query performance or flexibility. Palmr. uses **PostgreSQL** as the primary database solution. It's a powerful, open-source relational database thats trusted by developers around the world. PostgreSQL is fully ACID-compliant, which means it handles transactions safely and reliably. Its perfect for storing structured data like user accounts, file metadata, transfer logs, and anything else that requires consistency. With advanced features like full-text search, custom data types (like JSONB), and strong indexing capabilities, PostgreSQL gives us the tools to scale efficiently without giving up query performance or flexibility.
- Provides reliable and secure data storage, ensuring consistency and high performance for all database operations. - Provides reliable and secure data storage, ensuring consistency and high performance for all database operations.
- Powerful indexing, query optimization, and support for complex data types. - Powerful indexing, query optimization, and support for complex data types.
- Ideal for handling large amounts of metadata and transactional data in a predictable and scalable way. - Ideal for handling large amounts of metadata and transactional data in a predictable and scalable way.
### 🎨 Next.js 15 + React + TypeScript ### 🎨 Next.js 15 + React + TypeScript
The frontend of Palmr. is built using **Next.js 15**, along with **React** and **TypeScript**, forming a modern stack thats easy to maintain and super fast for end users. Next.js 15 brings server components, server actions, and a new app router system that makes rendering dynamic content incredibly efficient. This allows us to load only whats needed, when its needed which makes the app feel snappy even under load. React provides a clean, component-based structure that makes it easy to break the UI into reusable pieces, and TypeScript helps prevent bugs before they even happen by enforcing static typing and better code navigation. Whether it's SSR, static pages, or dynamic user interactions, this trio handles it all seamlessly. The frontend of Palmr. is built using **Next.js 15**, along with **React** and **TypeScript**, forming a modern stack thats easy to maintain and super fast for end users. Next.js 15 brings server components, server actions, and a new app router system that makes rendering dynamic content incredibly efficient. This allows us to load only whats needed, when its needed which makes the app feel snappy even under load. React provides a clean, component-based structure that makes it easy to break the UI into reusable pieces, and TypeScript helps prevent bugs before they even happen by enforcing static typing and better code navigation. Whether it's SSR, static pages, or dynamic user interactions, this trio handles it all seamlessly.
- **React** enables the creation of a dynamic and responsive user interface with a component-based architecture. - **React** enables the creation of a dynamic and responsive user interface with a component-based architecture.
- **TypeScript** adds static typing, enhancing code quality and reducing runtime errors. - **TypeScript** adds static typing, enhancing code quality and reducing runtime errors.
- **Next.js 15** handles routing, server-side rendering, and server components for performance at scale. - **Next.js 15** handles routing, server-side rendering, and server components for performance at scale.
### 📦 MinIO ### 📦 MinIO
Palmr. uses **MinIO** for object storage. MinIO is a lightweight, high-performance, S3-compatible storage solution that makes file handling simple and scalable. Every file uploaded to Palmr. Whether it's a personal file transfer or a shared asset is stored in MinIO. Its built to handle huge amounts of data and can be deployed locally, on-premise, or in the cloud. Because it speaks the same API as Amazon S3, integrating with it is straightforward and familiar to most developers. And since its self-hosted, we have full control over performance, redundancy, and security. Palmr. uses **MinIO** for object storage. MinIO is a lightweight, high-performance, S3-compatible storage solution that makes file handling simple and scalable. Every file uploaded to Palmr. Whether it's a personal file transfer or a shared asset is stored in MinIO. Its built to handle huge amounts of data and can be deployed locally, on-premise, or in the cloud. Because it speaks the same API as Amazon S3, integrating with it is straightforward and familiar to most developers. And since its self-hosted, we have full control over performance, redundancy, and security.
- Supports high-throughput file storage and retrieval. - Supports high-throughput file storage and retrieval.
- Ensures data integrity and redundancy. - Ensures data integrity and redundancy.
- Compatible with AWS S3 APIs, making integration seamless. - Compatible with AWS S3 APIs, making integration seamless.
### ⚡ Fastify ### ⚡ Fastify
The backend of Palmr. is powered by **Fastify**, a super-fast Node.js web framework optimized for performance and low overhead. Its designed to handle lots of concurrent requests with minimal resource usage, which is key for scalable backend services. Fastify also has a built-in schema validation system that ensures all incoming data is properly validated before reaching business logic, which helps prevent bugs and security issues. It follows a plugin-based architecture, making it easy to keep route handlers, services, and middlewares cleanly separated and easy to extend as the project grows. The backend of Palmr. is powered by **Fastify**, a super-fast Node.js web framework optimized for performance and low overhead. Its designed to handle lots of concurrent requests with minimal resource usage, which is key for scalable backend services. Fastify also has a built-in schema validation system that ensures all incoming data is properly validated before reaching business logic, which helps prevent bugs and security issues. It follows a plugin-based architecture, making it easy to keep route handlers, services, and middlewares cleanly separated and easy to extend as the project grows.
- Provides fast request handling with a lightweight core. - Provides fast request handling with a lightweight core.
- Built-in schema-based validation for secure and reliable API handling. - Built-in schema-based validation for secure and reliable API handling.
- Supports plugin-based architecture for easy extensibility. - Supports plugin-based architecture for easy extensibility.
### 🔄 How It Works ### 🔄 How It Works
1. **Frontend** — React + TypeScript + Next.js 15 handle the user interface and user interactions. 1. **Frontend** — React + TypeScript + Next.js 15 handle the user interface and user interactions.
2. **Backend** — Fastify processes requests and communicates with the database and storage layers. 2. **Backend** — Fastify processes requests and communicates with the database and storage layers.
3. **Database** — PostgreSQL stores metadata and transactional data. 3. **Database** — PostgreSQL stores metadata and transactional data.
4. **Object Storage** — MinIO stores the actual files and ensures scalable, high-performance storage. 4. **Object Storage** — MinIO stores the actual files and ensures scalable, high-performance storage.
### 📚 Useful Links ### 📚 Useful Links
- [PostgreSQL Documentation](https://www.postgresql.org/docs/) - [PostgreSQL Documentation](https://www.postgresql.org/docs/)
- [Next.js Documentation](https://nextjs.org/docs) - [Next.js Documentation](https://nextjs.org/docs)
- [React Documentation](https://react.dev/) - [React Documentation](https://react.dev/)
- [TypeScript Handbook](https://www.typescriptlang.org/docs/) - [TypeScript Handbook](https://www.typescriptlang.org/docs/)
- [MinIO Documentation](https://min.io/docs/minio/container/index.html) - [MinIO Documentation](https://min.io/docs/minio/container/index.html)
- [Fastify Documentation](https://fastify.dev/docs/latest/) - [Fastify Documentation](https://fastify.dev/docs/latest/)

View File

@@ -9,7 +9,7 @@ The project leverages next-intl, a powerful and flexible internationalization (i
--- ---
| Language | Code | Description | Translation | | Language | Code | Description | Translation |
|----------|------|-------------|-------------| | ------------- | ----- | -------------------------------------------------------- | ----------- |
| 🇺🇸 English | en-US | Primary development language and default fallback option | 100% | | 🇺🇸 English | en-US | Primary development language and default fallback option | 100% |
| 🇧🇷 Portuguese | pt-BR | Standard Brazilian Portuguese support | 100% | | 🇧🇷 Portuguese | pt-BR | Standard Brazilian Portuguese support | 100% |
| 🇫🇷 French | fr-FR | Standard French language support | 100% | | 🇫🇷 French | fr-FR | Standard French language support | 100% |

View File

@@ -7,6 +7,7 @@ For Palmr to function with all its best features, we need to configure our email
## ❓ Why Configure SMTP? ## ❓ Why Configure SMTP?
The main functionalities that depend on SMTP configuration are: The main functionalities that depend on SMTP configuration are:
- 🔑 **Password Reset** Users who forget their password and cannot access the **Settings** panel need this feature. - 🔑 **Password Reset** Users who forget their password and cannot access the **Settings** panel need this feature.
- 📧 **Email Notifications** Recipients will receive emails when new shares are sent to them. - 📧 **Email Notifications** Recipients will receive emails when new shares are sent to them.
@@ -53,6 +54,7 @@ Once SMTP is enabled, you can configure the other necessary fields:
### 🔐 Generating a Gmail App Password ### 🔐 Generating a Gmail App Password
To generate an App Password for Gmail: To generate an App Password for Gmail:
1. Go to [Google My Account](https://myaccount.google.com/). 1. Go to [Google My Account](https://myaccount.google.com/).
2. Select **Security**. 2. Select **Security**.
3. Scroll down to **App Passwords**. 3. Scroll down to **App Passwords**.

View File

@@ -15,6 +15,7 @@ Before you can contribute, you need to be logged into your GitHub account. If yo
--- ---
### 🔍 Access the Repository ### 🔍 Access the Repository
Once you're logged in, go to the Palmr repository by clicking on this link: **[https://github.com/kyantech/Palmr](https://github.com/kyantech/Palmr)**. The repository contains all the source code, documentation, and resources for the Palmr project. Take a moment to explore the repository structure, including the README file, which provides an overview of the project. Once you're logged in, go to the Palmr repository by clicking on this link: **[https://github.com/kyantech/Palmr](https://github.com/kyantech/Palmr)**. The repository contains all the source code, documentation, and resources for the Palmr project. Take a moment to explore the repository structure, including the README file, which provides an overview of the project.
Alternatively, you can search for "Palmr" in the GitHub search bar and click on the repository owned by **kyantech**. When searching, make sure you're looking at the official repository by checking the owner and repository name. The repository should have a description that matches the Palmr project and show recent activity from the maintainers. You can also check the number of stars, forks, and watchers to verify you're accessing the correct repository. Alternatively, you can search for "Palmr" in the GitHub search bar and click on the repository owned by **kyantech**. When searching, make sure you're looking at the official repository by checking the owner and repository name. The repository should have a description that matches the Palmr project and show recent activity from the maintainers. You can also check the number of stars, forks, and watchers to verify you're accessing the correct repository.
@@ -32,6 +33,7 @@ To contribute to the project, you'll need to create your own copy of the reposit
5. You'll be automatically redirected to your forked repository once it's ready. 5. You'll be automatically redirected to your forked repository once it's ready.
The fork will maintain a connection to the original repository, allowing you to: The fork will maintain a connection to the original repository, allowing you to:
- Keep your fork synchronized with the original repository - Keep your fork synchronized with the original repository
- Submit pull requests from your fork to the original repository - Submit pull requests from your fork to the original repository
- Work independently on your own copy without affecting the original - Work independently on your own copy without affecting the original
@@ -41,6 +43,7 @@ The fork will maintain a connection to the original repository, allowing you to:
### 📥 Clone the Fork ### 📥 Clone the Fork
Next, youll need to clone your forked repository to your local machine. Heres how: Next, youll need to clone your forked repository to your local machine. Heres how:
1. On your forked repository page, click the **Code** button. 1. On your forked repository page, click the **Code** button.
2. Copy the repository URL (HTTPS or SSH). 2. Copy the repository URL (HTTPS or SSH).
3. Open your terminal or command prompt and run the following command to clone the repository: 3. Open your terminal or command prompt and run the following command to clone the repository:
@@ -60,6 +63,7 @@ Next, youll need to clone your forked repository to your local machine. Here
### 🔄 Set up Base Branch ### 🔄 Set up Base Branch
Before making changes, ensure your local repository is set up to track the `next` branch from the original Palmr repository. Heres how: Before making changes, ensure your local repository is set up to track the `next` branch from the original Palmr repository. Heres how:
1. Add the original Palmr repository as a remote: 1. Add the original Palmr repository as a remote:
```bash ```bash
@@ -81,7 +85,9 @@ Before making changes, ensure your local repository is set up to track the `next
--- ---
### ✏️ Make Local Changes ### ✏️ Make Local Changes
Now you're ready to make your contributions! This could include: Now you're ready to make your contributions! This could include:
- Fixing a bug - Fixing a bug
- Adding a new feature - Adding a new feature
- Improving documentation - Improving documentation
@@ -113,12 +119,14 @@ Once youve made your changes, commit them to your branch using **Conventional
`<type>(<scope>): <description>` `<type>(<scope>): <description>`
**Examples:** **Examples:**
- `feat: add user authentication` - `feat: add user authentication`
- `fix(api): resolve null pointer exception` - `fix(api): resolve null pointer exception`
- `docs: update README file` - `docs: update README file`
- `chore: update dependencies` - `chore: update dependencies`
**Steps to Commit:** **Steps to Commit:**
1. Stage your changes: 1. Stage your changes:
```bash ```bash
@@ -138,6 +146,7 @@ Once youve made your changes, commit them to your branch using **Conventional
After committing your changes, you'll need to push them to your forked repository on GitHub. This step synchronizes your local changes with your remote repository. Here's how to do it: After committing your changes, you'll need to push them to your forked repository on GitHub. This step synchronizes your local changes with your remote repository. Here's how to do it:
1. First, ensure your branch is up-to-date with any remote changes: 1. First, ensure your branch is up-to-date with any remote changes:
```bash ```bash
git pull origin your-branch-name git pull origin your-branch-name
``` ```
@@ -148,11 +157,13 @@ After committing your changes, you'll need to push them to your forked repositor
``` ```
If this is the first time pushing this branch, you might need to set the upstream branch: If this is the first time pushing this branch, you might need to set the upstream branch:
```bash
git push -u origin your-branch-name ```bash
``` git push -u origin your-branch-name
```
If you encounter any errors while pushing: If you encounter any errors while pushing:
- Make sure you have the correct permissions on your fork - Make sure you have the correct permissions on your fork
- Verify your remote URL is correct using `git remote -v` - Verify your remote URL is correct using `git remote -v`
- Check if you need to authenticate with GitHub - Check if you need to authenticate with GitHub
@@ -162,6 +173,7 @@ If you encounter any errors while pushing:
### 🔀 Create Pull Request ### 🔀 Create Pull Request
Now that your changes are on GitHub, you can open a **Pull Request (PR)** to propose your changes to the `next` branch of the Palmr repository. Heres how: Now that your changes are on GitHub, you can open a **Pull Request (PR)** to propose your changes to the `next` branch of the Palmr repository. Heres how:
1. Go to your forked repository on GitHub. 1. Go to your forked repository on GitHub.
2. Click the **Pull Request** button. 2. Click the **Pull Request** button.
3. On the PR creation page: 3. On the PR creation page:
@@ -191,6 +203,7 @@ Remember that code review is a collaborative process aimed at ensuring code qual
## 💡 Contribution Tips ## 💡 Contribution Tips
To ensure your contribution is accepted, follow these tips: To ensure your contribution is accepted, follow these tips:
- **Use Conventional Commits**: Write clear and consistent commit messages using the Conventional Commits format. - **Use Conventional Commits**: Write clear and consistent commit messages using the Conventional Commits format.
- **Keep Your PRs Small**: Focus on one issue or feature per PR to make it easier to review. - **Keep Your PRs Small**: Focus on one issue or feature per PR to make it easier to review.
- **Be Patient**: Maintainers are often volunteers and may take some time to review your PR. - **Be Patient**: Maintainers are often volunteers and may take some time to review your PR.
@@ -206,6 +219,7 @@ To ensure your contribution is accepted, follow these tips:
## ⭐ Why Contribute? ## ⭐ Why Contribute?
Contributing to open-source projects like Palmr has many benefits: Contributing to open-source projects like Palmr has many benefits:
1. **Improves the Project**: Your contributions help make the project better for everyone. 1. **Improves the Project**: Your contributions help make the project better for everyone.
2. **Builds Your Skills**: Youll gain experience working with Git, GitHub, and collaborative coding. 2. **Builds Your Skills**: Youll gain experience working with Git, GitHub, and collaborative coding.
3. **Supports the Community**: Open-source thrives on community contributions. Your work helps sustain the project. 3. **Supports the Community**: Open-source thrives on community contributions. Your work helps sustain the project.

View File

@@ -6,7 +6,7 @@ title: 🔗 Managing shares
Creating a share in Palmr is designed to be a straightforward and user-friendly experience that anyone can master quickly. While the platform offers several methods for share creation, we recommend beginning with the most accessible approach: utilizing the **Home Page**, particularly through the well-organized **Recent Shares** section. Creating a share in Palmr is designed to be a straightforward and user-friendly experience that anyone can master quickly. While the platform offers several methods for share creation, we recommend beginning with the most accessible approach: utilizing the **Home Page**, particularly through the well-organized **Recent Shares** section.
___ ---
### Home Page ### Home Page
@@ -19,7 +19,6 @@ For first-time users, the interface features a prominently positioned **"Create
![](/assets/v1/main/shares/create-first-share.png) ![](/assets/v1/main/shares/create-first-share.png)
> Note: One of Palmr's unique features is its flexibility - you don't need to upload files to create a share! This might seem counterintuitive at first, but it's a deliberately designed feature that many users find invaluable for their specific workflows and use cases. This approach allows you to set up the sharing framework first and add content later. > Note: One of Palmr's unique features is its flexibility - you don't need to upload files to create a share! This might seem counterintuitive at first, but it's a deliberately designed feature that many users find invaluable for their specific workflows and use cases. This approach allows you to set up the sharing framework first and add content later.
>
This design philosophy reflects Palmr's user-centric approach: you can establish your share's parameters and settings first, then populate it with files at your convenience. Furthermore, all aspects of your share remain fully editable at any time, providing maximum flexibility. This design philosophy reflects Palmr's user-centric approach: you can establish your share's parameters and settings first, then populate it with files at your convenience. Furthermore, all aspects of your share remain fully editable at any time, providing maximum flexibility.
@@ -78,7 +77,7 @@ Each share has an **Actions** column with the following options:
![](/assets/v1/main/shares/actions-column.png) ![](/assets/v1/main/shares/actions-column.png)
___ ---
## ✏️ Edit Share ## ✏️ Edit Share
@@ -88,24 +87,23 @@ The **Edit** button provides access to a comprehensive interface where you can m
## 📁 Manage Files ## 📁 Manage Files
___ ---
Through the **Manage Files** button, you gain complete control over the content of your share, with the ability to both add new files and remove existing ones. Through the **Manage Files** button, you gain complete control over the content of your share, with the ability to both add new files and remove existing ones.
![](/assets/v1/main/shares/manage-files-modal.png) ![](/assets/v1/main/shares/manage-files-modal.png)
___ ---
## 👥 Manage Recipients ## 👥 Manage Recipients
The **Manage Recipients** button opens an interface where you can maintain your recipient list, adding or removing access as needed. The **Manage Recipients** button opens an interface where you can maintain your recipient list, adding or removing access as needed.
> Note: For email notifications to function properly, please ensure that your system's SMTP settings are correctly configured and active. > Note: For email notifications to function properly, please ensure that your system's SMTP settings are correctly configured and active.
>
![](/assets/v1/main/shares/manage-recipients-modal.png) ![](/assets/v1/main/shares/manage-recipients-modal.png)
___ ---
## 👀 View Share Details ## 👀 View Share Details
@@ -113,7 +111,7 @@ The **View Details** option provides a comprehensive overview of all aspects and
![](/assets/v1/main/shares/share-details-modal.png) ![](/assets/v1/main/shares/share-details-modal.png)
___ ---
## 🔗 Generate Share Link ## 🔗 Generate Share Link
@@ -133,7 +131,7 @@ When recipients access your generated link, they'll be able to both **view and d
![](/assets/v1/main/shares/share-screen.png) ![](/assets/v1/main/shares/share-screen.png)
___ ---
## Delete Share ## Delete Share

View File

@@ -34,6 +34,7 @@ Once you're logged in, go to the Palmr. repository by clicking on this link: [ht
Alternatively, you can search for "Palmr" in the GitHub search bar and click on the repository owned by **Kyantech**. Alternatively, you can search for "Palmr" in the GitHub search bar and click on the repository owned by **Kyantech**.
You can also: You can also:
- Star the repository to show your support - Star the repository to show your support
- Watch it for updates - Watch it for updates
- Fork it if you want to contribute - Fork it if you want to contribute
@@ -75,25 +76,26 @@ Once done, you'll officially be a **Palmr sponsor**! 🙌
### ⭐ Why Sponsoring Matters ### ⭐ Why Sponsoring Matters
- 🧱 Supports Sustainability - 🧱 Supports Sustainability
Your sponsorship helps keep the project alive and maintained long-term. Your sponsorship helps keep the project alive and maintained long-term.
- 🚀 Encourages Innovation - 🚀 Encourages Innovation
Financial support gives developers the freedom to try new ideas and push boundaries. Financial support gives developers the freedom to try new ideas and push boundaries.
- 🫶 Shows Deep Appreciation - 🫶 Shows Deep Appreciation
Sponsoring is a meaningful, tangible way to thank developers for their work. Sponsoring is a meaningful, tangible way to thank developers for their work.
- 🏆 Earns You Recognition - 🏆 Earns You Recognition
Many projects publicly thank their sponsors — you might appear in the README or on the site! Many projects publicly thank their sponsors — you might appear in the README or on the site!
- 🌱 Helps Open Source Thrive - 🌱 Helps Open Source Thrive
Open-source projects rely on community support. Sponsoring helps the ecosystem grow. Open-source projects rely on community support. Sponsoring helps the ecosystem grow.
--- ---
## 🎁 What Happens After Sponsoring ## 🎁 What Happens After Sponsoring
Once you become a sponsor: Once you become a sponsor:
1. You'll receive a confirmation email from GitHub 1. You'll receive a confirmation email from GitHub
2. Your name will appear in our sponsors list 2. Your name will appear in our sponsors list
3. You'll get access to sponsor-only updates and content 3. You'll get access to sponsor-only updates and content
@@ -107,4 +109,3 @@ Once you become a sponsor:
That's it! You've successfully sponsored the **Palmr.** project on GitHub. That's it! You've successfully sponsored the **Palmr.** project on GitHub.
Your support will help ensure this open-source project continues to evolve and thrive. Your support will help ensure this open-source project continues to evolve and thrive.
**We appreciate you and welcome you to our community!** 🎉 **We appreciate you and welcome you to our community!** 🎉

View File

@@ -66,6 +66,7 @@ After clicking the "Star" button, several things will happen:
4. The repository will be added to your starred repositories list 4. The repository will be added to your starred repositories list
You can access your starred repositories anytime by: You can access your starred repositories anytime by:
- Clicking your profile picture - Clicking your profile picture
- Selecting "Your stars" from the dropdown menu - Selecting "Your stars" from the dropdown menu
- Or visiting: https://github.com/[your-username]?tab=stars - Or visiting: https://github.com/[your-username]?tab=stars
@@ -81,22 +82,22 @@ To remove your star, simply click the "Unstar" button at any time.
Starring a repository on GitHub is more than just a bookmarking tool—its a way to support the project and its developers. Heres why starring is so important: Starring a repository on GitHub is more than just a bookmarking tool—its a way to support the project and its developers. Heres why starring is so important:
- 🙌 Shows Appreciation - 🙌 Shows Appreciation
Starring a repository is a simple way to show your appreciation for the hard work and effort that goes into maintaining and developing a project. Starring a repository is a simple way to show your appreciation for the hard work and effort that goes into maintaining and developing a project.
- 📈 Increases Visibility - 📈 Increases Visibility
The more stars a repository has, the more visible it becomes on GitHub. The more stars a repository has, the more visible it becomes on GitHub.
- 💪 Encourages Developers - 💪 Encourages Developers
Seeing stars motivates developers to continue improving the project. Seeing stars motivates developers to continue improving the project.
- 🔍 Helps with Discovery - 🔍 Helps with Discovery
GitHub prioritizes repositories with more stars in trending and recommendations. GitHub prioritizes repositories with more stars in trending and recommendations.
- 📚 Tracks Your Interests - 📚 Tracks Your Interests
Starred repositories are saved to your list for easy access later. Starred repositories are saved to your list for easy access later.
- 🌍 Supports Open Source - 🌍 Supports Open Source
Your stars help sustain the open-source community and the Palmr project. Your stars help sustain the open-source community and the Palmr project.
--- ---
@@ -119,6 +120,7 @@ Your star is more than just a number - it's a vote of confidence in our vision a
That's it! You've successfully starred the **Palmr** project on GitHub. Thank you for supporting Palmr and becoming part of our growing community! Your support helps us continue improving and expanding the project for everyone. That's it! You've successfully starred the **Palmr** project on GitHub. Thank you for supporting Palmr and becoming part of our growing community! Your support helps us continue improving and expanding the project for everyone.
Feel free to explore our other ways to contribute, like: Feel free to explore our other ways to contribute, like:
- Sharing Palmr with others - Sharing Palmr with others
- Reporting issues - Reporting issues
- Contributing code - Contributing code

View File

@@ -2,31 +2,30 @@
title: </> Github Architecture title: </> Github Architecture
--- ---
import { File, Folder, Files } from "fumadocs-ui/components/files"; import { File, Files, Folder } from "fumadocs-ui/components/files";
## Project Structure ## Project Structure
<Files> <Files>
<Folder name="apps" defaultOpen> <Folder name="apps" defaultOpen>
<Folder name="docs" > <Folder name="docs">
<File name="all documentation files" /> <File name="all documentation files" />
</Folder> </Folder>
<Folder name="server" > <Folder name="server">
<File name="all backend files" /> <File name="all backend files" />
</Folder> </Folder>
<Folder name="web" > <Folder name="web">
<File name="all frontend files" /> <File name="all frontend files" />
</Folder> </Folder>
</Folder> </Folder>
<File name="other project files" /> <File name="other project files" />
</Files> </Files>
## Core Components ## Core Components
### 🖥️ Frontend Application (apps/web) ### 🖥️ Frontend Application (apps/web)
**Technology Stack:** **Technology Stack:**
- Next.js 15 (App Router) - Next.js 15 (App Router)
- React 18 - React 18
@@ -52,7 +51,7 @@ The frontend is organized with:
### ⚙️ Backend Service (apps/server) ### ⚙️ Backend Service (apps/server)
**Technology Stack:** **Technology Stack:**
- Fastify - Fastify
- PostgreSQL - PostgreSQL
@@ -76,7 +75,7 @@ Key features include:
### 📚 Documentation (apps/docs) ### 📚 Documentation (apps/docs)
**Technology Stack:** **Technology Stack:**
- Fumadocs - Fumadocs
- MDX (Markdown + JSX) - MDX (Markdown + JSX)

View File

@@ -2,13 +2,12 @@
title: 🐳 Installation (Docker Compose) title: 🐳 Installation (Docker Compose)
--- ---
Installation via Docker Compose is the simplest way to run the project across different environments. For it to run correctly, we need two main tools installed in our environment: Installation via Docker Compose is the simplest way to run the project across different environments. For it to run correctly, we need two main tools installed in our environment:
- Docker ([https://docs.docker.com](https://docs.docker.com/)) - Docker ([https://docs.docker.com](https://docs.docker.com/))
- Docker Compose ([https://docs.docker.com/compose](https://docs.docker.com/compose/)) - Docker Compose ([https://docs.docker.com/compose](https://docs.docker.com/compose/))
> *It's worth emphasizing that Palmr. was fully developed in a MacOS environment and extensively tested on Linux servers. Therefore, we can guarantee the best system performance in these environments. Windows and other environments have not been tested yet, and potential bugs may occur during execution. However, remember that we are still in a beta version of Palmr., and errors or bugs can occur in any operating system. If you identify any issues, we appreciate your help in notifying us through our GitHub [issues page](https://github.com/kyantech/Palmr/issues).* > _It's worth emphasizing that Palmr. was fully developed in a MacOS environment and extensively tested on Linux servers. Therefore, we can guarantee the best system performance in these environments. Windows and other environments have not been tested yet, and potential bugs may occur during execution. However, remember that we are still in a beta version of Palmr., and errors or bugs can occur in any operating system. If you identify any issues, we appreciate your help in notifying us through our GitHub [issues page](https://github.com/kyantech/Palmr/issues)._
--- ---
@@ -25,6 +24,7 @@ Next, let's look at the content of our `docker-compose.yaml`.
--- ---
## 🐳 Docker Compose Content ## 🐳 Docker Compose Content
Below is the complete content of our `docker-compose.yaml` that can be copied directly from here or from our official repository ([Docker Compose](https://github.com/kyantech/Palmr/blob/main/docker-compose.yaml)). Below is the complete content of our `docker-compose.yaml` that can be copied directly from here or from our official repository ([Docker Compose](https://github.com/kyantech/Palmr/blob/main/docker-compose.yaml)).
```yaml ```yaml
@@ -61,7 +61,17 @@ services:
- "${APP_EXTERNAL_PORT:-5487}:5487" # Web port - "${APP_EXTERNAL_PORT:-5487}:5487" # Web port
restart: unless-stopped restart: unless-stopped
healthcheck: healthcheck:
test: ["CMD", "wget", "--no-verbose", "http://palmr:${API_INTERNAL_PORT:-3333}/health", "&&", "wget", "--no-verbose", "http://palmr:${APP_INTERNAL_PORT:-5487}"] test:
[
"CMD",
"wget",
"--no-verbose",
"http://palmr:${API_INTERNAL_PORT:-3333}/health",
"&&",
"wget",
"--no-verbose",
"http://palmr:${APP_INTERNAL_PORT:-5487}",
]
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 3 retries: 3
@@ -127,7 +137,6 @@ services:
volumes: volumes:
minio_data: minio_data:
postgres_data: postgres_data:
``` ```
Notice that the `docker-compose.yaml` has several comments that help you configure your own compose to meet your environment's needs. Let's give an overview of some changes we can make. Notice that the `docker-compose.yaml` has several comments that help you configure your own compose to meet your environment's needs. Let's give an overview of some changes we can make.
@@ -139,7 +148,7 @@ Notice that the `docker-compose.yaml` has several comments that help you configu
Palmr. consists of four main services, each with specific responsibilities. Below, we present a detailed view of each component: Palmr. consists of four main services, each with specific responsibilities. Below, we present a detailed view of each component:
| **Service** | **Image** | **Exposed Ports** | **Main Features** | | **Service** | **Image** | **Exposed Ports** | **Main Features** |
| --- | --- | --- | --- | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| palmr | [kyantech/palmr:latest](https://hub.docker.com/repository/docker/kyantech/palmr/general) | **3333** (API)<br/>**5487** (Web) | • Combined backend API and frontend service<br/>• Depends on services: postgres and minio<br/>• Has healthcheck to ensure availability | | palmr | [kyantech/palmr:latest](https://hub.docker.com/repository/docker/kyantech/palmr/general) | **3333** (API)<br/>**5487** (Web) | • Combined backend API and frontend service<br/>• Depends on services: postgres and minio<br/>• Has healthcheck to ensure availability |
| minio (Storage) | [minio/minio:RELEASE.2025-03-12T18-04-18Z](https://hub.docker.com/layers/minio/minio/RELEASE.2025-03-12T18-04-18Z/images/sha256-85f3e4cd1ca92a2711553ab79f222bcd8b75aa2c77a1a0b0ccf80d38e8ab2fe5) | **6421**(API)<br/>**6422**(Console) | • File storage service<br/>• Persistent volume for data | | minio (Storage) | [minio/minio:RELEASE.2025-03-12T18-04-18Z](https://hub.docker.com/layers/minio/minio/RELEASE.2025-03-12T18-04-18Z/images/sha256-85f3e4cd1ca92a2711553ab79f222bcd8b75aa2c77a1a0b0ccf80d38e8ab2fe5) | **6421**(API)<br/>**6422**(Console) | • File storage service<br/>• Persistent volume for data |
| minio-init (Config) | [minio/mc:RELEASE.2025-03-12T17-29-24Z](https://hub.docker.com/layers/minio/mc/RELEASE.2025-03-12T17-29-24Z/images/sha256-68d8c80f43908b02daa285e55547131870a1d36b3ffe272c26d7d8f4d52d1e5c) | N/A | • Initially configures the "files" bucket<br/>• Runs only once during initialization | | minio-init (Config) | [minio/mc:RELEASE.2025-03-12T17-29-24Z](https://hub.docker.com/layers/minio/mc/RELEASE.2025-03-12T17-29-24Z/images/sha256-68d8c80f43908b02daa285e55547131870a1d36b3ffe272c26d7d8f4d52d1e5c) | N/A | • Initially configures the "files" bucket<br/>• Runs only once during initialization |
@@ -152,7 +161,7 @@ Palmr. consists of four main services, each with specific responsibilities. Belo
The table below shows all environment variables that can be set The table below shows all environment variables that can be set
| **Variable** | **Default Value** | **Description** | | **Variable** | **Default Value** | **Description** |
| --- | --- | --- | | --------------------------- | --------------------- | ------------------------------------------------- |
| API_INTERNAL_PORT | 3333 | Internal API port in container | | API_INTERNAL_PORT | 3333 | Internal API port in container |
| API_EXTERNAL_PORT | 3333 | Exposed port on host for API | | API_EXTERNAL_PORT | 3333 | Exposed port on host for API |
| POSTGRES_PASSWORD | postgresRootPassword | PostgreSQL database password | | POSTGRES_PASSWORD | postgresRootPassword | PostgreSQL database password |
@@ -169,9 +178,7 @@ The table below shows all environment variables that can be set
| POSTGRESQL_DATABASE | palmr_db | Database name | | POSTGRESQL_DATABASE | palmr_db | Database name |
| MAX_FILESIZE | 1073741824 | Max Uploadsize per file. Unit in Bytes | | MAX_FILESIZE | 1073741824 | Max Uploadsize per file. Unit in Bytes |
> *All these variables can be configured through a .env file in the project root or defined directly in the environment where docker-compose will be executed. The best way to do this is up to you. But be careful to replace correctly if doing directly in the compose instead of providing an environment var.* > _All these variables can be configured through a .env file in the project root or defined directly in the environment where docker-compose will be executed. The best way to do this is up to you. But be careful to replace correctly if doing directly in the compose instead of providing an environment var._
>
#### 🗂️ Persistent Volumes #### 🗂️ Persistent Volumes
@@ -196,8 +203,7 @@ This will execute all necessary services and give you access to the following UR
- **MinIO Console:** [http://localhost:6422](http://localhost:6422) - **MinIO Console:** [http://localhost:6422](http://localhost:6422)
- **Postgres Database:** [http://localhost:5432](http://localhost:5432/) (Connection only) - **Postgres Database:** [http://localhost:5432](http://localhost:5432/) (Connection only)
> *If you have changed any port, simply access the URL with the port you configured.* > _If you have changed any port, simply access the URL with the port you configured._
>
--- ---
@@ -217,6 +223,7 @@ To generate a .env file with just the `server_ip` configuration, you can run thi
```bash ```bash
curl -fsSL https://gist.githubusercontent.com/danielalves96/5a68913d70e5e31b68b7331dc17dfa9c/raw | bash curl -fsSL https://gist.githubusercontent.com/danielalves96/5a68913d70e5e31b68b7331dc17dfa9c/raw | bash
``` ```
> execute this command in your server terminal, at same path of docker-compose.yaml. > execute this command in your server terminal, at same path of docker-compose.yaml.
Basically, by paying attention to these points, you can quickly execute the project with the same command we used for localhost: Basically, by paying attention to these points, you can quickly execute the project with the same command we used for localhost:
@@ -229,8 +236,7 @@ docker compose pull && docker compose up -d
At this stage, if you encounter any errors, it's worth reviewing your `docker-compose.yaml` and trying again, paying close attention to the points mentioned above. At this stage, if you encounter any errors, it's worth reviewing your `docker-compose.yaml` and trying again, paying close attention to the points mentioned above.
> *First test without using reverse proxies like Caddy, Traefik, etc... if you plan to use them. Access the services via `server_ip:port` after confirming they work, then make the necessary routing configurations as desired.* > _First test without using reverse proxies like Caddy, Traefik, etc... if you plan to use them. Access the services via `server_ip:port` after confirming they work, then make the necessary routing configurations as desired._
>
If you haven't changed the execution ports, you'll have access on your server at: If you haven't changed the execution ports, you'll have access on your server at:
@@ -240,8 +246,7 @@ If you haven't changed the execution ports, you'll have access on your server at
- **MinIO Console:** `[server_ip]:6422` - **MinIO Console:** `[server_ip]:6422`
- **Postgres Database:** `[server_ip]:5432` (Connection only) - **Postgres Database:** `[server_ip]:5432` (Connection only)
> *If you've changed any port, simply access the URL with the port you configured.* > _If you've changed any port, simply access the URL with the port you configured._
>
It's worth noting that this is just a quick start and we're not going into details about any of the developed services, but it's recommended for execution in any environment. However, if your focus is on using Palmr. with high availability in mind, it's recommended to use a container orchestrator prepared for this, such as Kubernetes or similar, but we don't cover this type of configuration in our documentation. It's worth noting that this is just a quick start and we're not going into details about any of the developed services, but it's recommended for execution in any environment. However, if your focus is on using Palmr. with high availability in mind, it's recommended to use a container orchestrator prepared for this, such as Kubernetes or similar, but we don't cover this type of configuration in our documentation.

View File

@@ -31,7 +31,6 @@ Before proceeding with the installation, it's essential to ensure that your deve
⚠️ <strong>A critical note regarding package management:</strong> This repository has been specifically developed and thoroughly tested using the pnpm package manager. While technically possible to use alternative package managers such as `npm`, `yarn`, or `bun`, we strongly advise against this approach. ⚠️ <strong>A critical note regarding package management:</strong> This repository has been specifically developed and thoroughly tested using the pnpm package manager. While technically possible to use alternative package managers such as `npm`, `yarn`, or `bun`, we strongly advise against this approach.
--- ---
## Running the Application ## Running the Application

View File

@@ -1,7 +1,5 @@
{ {
"title": "v2.0.0-beta",
"description": "(Deprecated)", "description": "(Deprecated)",
"root": true,
"icon": "Trash2", "icon": "Trash2",
"pages": [ "pages": [
"---Introduction---", "---Introduction---",
@@ -25,5 +23,7 @@
"gh-star", "gh-star",
"gh-sponsor", "gh-sponsor",
"..." "..."
] ],
"root": true,
"title": "v2.0.0-beta"
} }

View File

@@ -53,6 +53,7 @@ To access the issues section:
4. You'll see a list of all existing issues, both open and closed 4. You'll see a list of all existing issues, both open and closed
The issues tab shows important information like: The issues tab shows important information like:
- Number of open issues - Number of open issues
- Issue labels and categories - Issue labels and categories
- Issue status (open/closed) - Issue status (open/closed)
@@ -74,6 +75,7 @@ To start creating a new issue:
5. Make sure you have all necessary information ready before starting 5. Make sure you have all necessary information ready before starting
Pro Tips: Pro Tips:
- Before creating a new issue, search existing issues to avoid duplicates - Before creating a new issue, search existing issues to avoid duplicates
- Review any contribution guidelines or issue templates - Review any contribution guidelines or issue templates
- Consider adding relevant labels when creating your issue - Consider adding relevant labels when creating your issue
@@ -107,6 +109,7 @@ Once you've filled out the form, click the **Create** button at the bottom of th
### 💡 Tips for Issues ### 💡 Tips for Issues
To ensure your issue is addressed quickly and effectively, follow these tips: To ensure your issue is addressed quickly and effectively, follow these tips:
- **Be Clear and Specific**: Provide as much detail as possible. - **Be Clear and Specific**: Provide as much detail as possible.
- **Use a Descriptive Title**: A good title helps maintainers understand the issue at a glance. - **Use a Descriptive Title**: A good title helps maintainers understand the issue at a glance.
- **Include Steps to Reproduce**: If its a bug, explain how to reproduce it. - **Include Steps to Reproduce**: If its a bug, explain how to reproduce it.
@@ -117,6 +120,7 @@ To ensure your issue is addressed quickly and effectively, follow these tips:
### ⭐ Why Issues Matter ### ⭐ Why Issues Matter
Opening issues is a key part of contributing to open-source projects. Heres why it matters: Opening issues is a key part of contributing to open-source projects. Heres why it matters:
1. **Improves the Project**: Your feedback helps identify bugs and suggest new features. 1. **Improves the Project**: Your feedback helps identify bugs and suggest new features.
2. **Helps Maintainers**: Clear and detailed issues make it easier for maintainers to address problems. 2. **Helps Maintainers**: Clear and detailed issues make it easier for maintainers to address problems.
3. **Encourages Collaboration**: Issues can spark discussions and attract contributors to help solve problems. 3. **Encourages Collaboration**: Issues can spark discussions and attract contributors to help solve problems.

View File

@@ -20,7 +20,6 @@ To begin the upload process, locate and click the "Upload File" button. This act
![Upload File Button](/assets/v1/main/upload/upload-file-button.png) ![Upload File Button](/assets/v1/main/upload/upload-file-button.png)
**Example with an image:** **Example with an image:**
![Preview Example](/assets/v1/main/upload/preview-example.png) ![Preview Example](/assets/v1/main/upload/preview-example.png)

View File

@@ -9,10 +9,7 @@ import { ZoomableImage } from "@/components/ui/zoomable-image";
Understanding the architecture of Palmr. is crucial for both deploying and scaling the application. The platform is designed with simplicity and flexibility in mind, offering a streamlined setup that can grow with your needs. Understanding the architecture of Palmr. is crucial for both deploying and scaling the application. The platform is designed with simplicity and flexibility in mind, offering a streamlined setup that can grow with your needs.
<ZoomableImage <ZoomableImage src="/assets/v3/architecture/architecture.png" alt="Palmr. Architecture" />
src="/assets/v3/architecture/architecture.png"
alt="Palmr. Architecture"
/>
## Technologies used ## Technologies used

View File

@@ -3,7 +3,7 @@ title: GitHub Architecture
icon: Github icon: Github
--- ---
import { File, Folder, Files } from "fumadocs-ui/components/files"; import { File, Files, Folder } from "fumadocs-ui/components/files";
This guide provides a comprehensive overview of Palmr.'s GitHub repository structure, explaining how the different components are organized in the codebase. Understanding this architecture will help you navigate the repository, contribute effectively, and understand how the project is structured. This guide provides a comprehensive overview of Palmr.'s GitHub repository structure, explaining how the different components are organized in the codebase. Understanding this architecture will help you navigate the repository, contribute effectively, and understand how the project is structured.

View File

@@ -3,7 +3,7 @@ title: Welcome to Palmr.
icon: TreePalm icon: TreePalm
--- ---
import { Ban, Star, Shield, Palette, Users, Zap } from "lucide-react"; import { Ban, Palette, Shield, Star, Users, Zap } from "lucide-react";
![Palmr Banner](/assets/v2.1/general/banner.png) ![Palmr Banner](/assets/v2.1/general/banner.png)

View File

@@ -23,8 +23,7 @@ We've broken down each step below feel free to jump to the section you're workin
Let's make sure your environment is ready to roll. Check that you've got these tools installed and set up on your system: Let's make sure your environment is ready to roll. Check that you've got these tools installed and set up on your system:
- <span style={{ color: "#16a34a" }}>Node.js</span> *(Powers our JavaScript/TypeScript - <span style={{ color: "#16a34a" }}>Node.js</span> *(Powers our JavaScript/TypeScript apps)*
apps)*
- <span style={{ color: "#16a34a" }}>pnpm</span> *(Our go-to package manager)* - <span style={{ color: "#16a34a" }}>pnpm</span> *(Our go-to package manager)*
- <span style={{ color: "#16a34a" }}>Git</span> *(For cloning and managing the repo)* - <span style={{ color: "#16a34a" }}>Git</span> *(For cloning and managing the repo)*

View File

@@ -1,7 +1,5 @@
{ {
"title": "v3.1-beta",
"description": "Latest version", "description": "Latest version",
"root": true,
"icon": "Sparkles", "icon": "Sparkles",
"pages": [ "pages": [
"---Introduction---", "---Introduction---",
@@ -28,5 +26,7 @@
"---Sponsor this project---", "---Sponsor this project---",
"gh-star", "gh-star",
"gh-sponsor" "gh-sponsor"
] ],
"root": true,
"title": "v3.1-beta"
} }

View File

@@ -1,17 +1,6 @@
{ {
"title": "OIDC Authentication", "defaultOpen": false,
"icon": "Key", "icon": "Key",
"pages": [ "pages": ["index", "google", "discord", "github", "zitadel", "auth0", "authentik", "frontegg", "kinde-auth", "other"],
"index", "title": "OIDC Authentication"
"google",
"discord",
"github",
"zitadel",
"auth0",
"authentik",
"frontegg",
"kinde-auth",
"other"
],
"defaultOpen": false
} }

View File

@@ -131,7 +131,6 @@ If you encounter issues while running the script, refer to the following solutio
- **Error: "Database connection failed"** - **Error: "Database connection failed"**
If the script cannot connect to the database, check the following: If the script cannot connect to the database, check the following:
- Ensure the database service is running within the container. - Ensure the database service is running within the container.
- Confirm that the `prisma/palmr.db` file exists and has the correct permissions. - Confirm that the `prisma/palmr.db` file exists and has the correct permissions.
- Verify that the container has access to the database volume. - Verify that the container has access to the database volume.

View File

@@ -67,7 +67,6 @@ If you experience authentication issues behind a reverse proxy:
### Diagnostic Steps ### Diagnostic Steps
1. **Check Browser Developer Tools**: 1. **Check Browser Developer Tools**:
- Look for cookies in Application/Storage tab - Look for cookies in Application/Storage tab
- Verify cookie has `Secure` flag when using HTTPS - Verify cookie has `Secure` flag when using HTTPS
- Check if `SameSite` attribute is appropriate - Check if `SameSite` attribute is appropriate

View File

@@ -17,28 +17,19 @@ Here you can find a collection of screenshots showcasing various features and in
The main landing page where users can access the platform and learn about Palmr.'s features. The main landing page where users can access the platform and learn about Palmr.'s features.
<ZoomableImage <ZoomableImage src="/assets/v3/screenshots/home.png" alt="Home Page - Main landing page of Palmr" />
src="/assets/v3/screenshots/home.png"
alt="Home Page - Main landing page of Palmr"
/>
#### Login page #### Login page
Secure authentication interface where users enter their credentials to access their Palmr account. Secure authentication interface where users enter their credentials to access their Palmr account.
<ZoomableImage <ZoomableImage src="/assets/v3/screenshots/login.png" alt="Login Page - User authentication interface" />
src="/assets/v3/screenshots/login.png"
alt="Login Page - User authentication interface"
/>
#### Forgot password #### Forgot password
Password recovery interface that allows users to reset their passwords when they can't access their accounts. Password recovery interface that allows users to reset their passwords when they can't access their accounts.
<ZoomableImage <ZoomableImage src="/assets/v3/screenshots/forgot-password.png" alt="Forgot Password - Password recovery interface" />
src="/assets/v3/screenshots/forgot-password.png"
alt="Forgot Password - Password recovery interface"
/>
### Main Application Interface ### Main Application Interface

View File

@@ -3,8 +3,8 @@ title: Translation Management
icon: Languages icon: Languages
--- ---
import { Tab, Tabs } from "fumadocs-ui/components/tabs";
import { Callout } from "fumadocs-ui/components/callout"; import { Callout } from "fumadocs-ui/components/callout";
import { Tab, Tabs } from "fumadocs-ui/components/tabs";
Palmr includes a comprehensive translation key management system that automates synchronization and validation of the application's internationalization files. Palmr includes a comprehensive translation key management system that automates synchronization and validation of the application's internationalization files.
@@ -80,17 +80,13 @@ When you add new text to the application:
4. **Test in UI**: Verify translations work correctly in the application interface 4. **Test in UI**: Verify translations work correctly in the application interface
<Callout type="important"> <Callout type="important">
**Manual translation required**: All strings marked with `[TO_TRANSLATE]` must **Manual translation required**: All strings marked with `[TO_TRANSLATE]` must be manually translated by native
be manually translated by native speakers or professional translators for speakers or professional translators for accuracy.
accuracy.
</Callout> </Callout>
### 2. Checking Translation Status ### 2. Checking Translation Status
<Callout> <Callout>Always run `pnpm run translations:check` before releases to ensure completeness.</Callout>
Always run `pnpm run translations:check` before releases to ensure
completeness.
</Callout>
```bash ```bash
# Generate detailed translation report # Generate detailed translation report
@@ -301,9 +297,8 @@ Translation files use nested JSON structure:
## Manual Translation ## Manual Translation
<Callout type="warning"> <Callout type="warning">
**Professional translation recommended**: For production applications, **Professional translation recommended**: For production applications, consider using professional translation
consider using professional translation services or native speakers to ensure services or native speakers to ensure high-quality, culturally appropriate translations.
high-quality, culturally appropriate translations.
</Callout> </Callout>
The system marks strings that need translation with the `[TO_TRANSLATE]` prefix: The system marks strings that need translation with the `[TO_TRANSLATE]` prefix:
@@ -345,8 +340,8 @@ After manual translation:
## Development Guidelines ## Development Guidelines
<Callout type="important"> <Callout type="important">
**Primary Language**: Always use `en-US.json` as the parent language for **Primary Language**: Always use `en-US.json` as the parent language for development. All new translation keys must be
development. All new translation keys must be added to English first. added to English first.
</Callout> </Callout>
### Translation Workflow for Development ### Translation Workflow for Development

View File

@@ -1,6 +1,3 @@
{ {
"pages": [ "pages": ["3.1-beta", "2.0.0-beta"]
"3.1-beta",
"2.0.0-beta"
]
} }

View File

@@ -0,0 +1,67 @@
/* eslint-disable import/no-anonymous-default-export */
import path from "node:path";
import { fileURLToPath } from "node:url";
import { FlatCompat } from "@eslint/eslintrc";
import js from "@eslint/js";
import typescriptEslintEslintPlugin from "@typescript-eslint/eslint-plugin";
import tsParser from "@typescript-eslint/parser";
import prettier from "eslint-plugin-prettier";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all,
});
export default [
...compat.extends("next", "next/core-web-vitals", "prettier"),
{
plugins: {
prettier,
},
rules: {
"prettier/prettier": "error",
camelcase: "off",
"import/prefer-default-export": "off",
"react/jsx-filename-extension": "off",
"react/jsx-props-no-spreading": "off",
"react/no-unused-prop-types": "off",
"react/require-default-props": "off",
"react/no-unescaped-entities": "off",
"@next/next/no-img-element": "off",
"import/extensions": [
"error",
"ignorePackages",
{
ts: "never",
tsx: "never",
js: "never",
jsx: "never",
},
],
},
},
{
files: ["**/*.+(ts|tsx)"],
plugins: {
"@typescript-eslint": typescriptEslintEslintPlugin,
},
languageOptions: {
parser: tsParser,
},
rules: {
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"no-use-before-define": [0],
"@typescript-eslint/no-use-before-define": [1],
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-var-requires": "off",
},
},
// Ignore ESLint errors in @/ui directory
{
ignores: ["src/components/ui/**/*"],
},
];

View File

@@ -1,4 +1,4 @@
import { createMDX } from 'fumadocs-mdx/next'; import { createMDX } from "fumadocs-mdx/next";
const withMDX = createMDX(); const withMDX = createMDX();
@@ -15,15 +15,15 @@ const config = {
qualities: [100], qualities: [100],
remotePatterns: [ remotePatterns: [
{ {
protocol: 'https', protocol: "https",
hostname: '**' hostname: "**",
}, },
{ {
protocol: 'http', protocol: "http",
hostname: '**' hostname: "**",
} },
] ],
} },
}; };
export default withMDX(config); export default withMDX(config);

View File

@@ -19,7 +19,13 @@
"build": "next build", "build": "next build",
"dev": "next dev --turbo", "dev": "next dev --turbo",
"start": "next start", "start": "next start",
"postinstall": "fumadocs-mdx" "postinstall": "fumadocs-mdx",
"lint": "eslint \"src/**/*.+(ts|tsx)\"",
"lint:fix": "eslint \"src/**/*.+(ts|tsx)\" --fix",
"format": "prettier . --write",
"format:check": "prettier . --check",
"type-check": "npx tsc --noEmit",
"validate": "pnpm format && pnpm lint:fix && pnpm type-check"
}, },
"dependencies": { "dependencies": {
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
@@ -35,14 +41,23 @@
"tailwind-merge": "^3.2.0" "tailwind-merge": "^3.2.0"
}, },
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "3.3.1",
"@eslint/js": "9.30.0",
"@ianvs/prettier-plugin-sort-imports": "4.4.2",
"@tailwindcss/postcss": "^4.1.11", "@tailwindcss/postcss": "^4.1.11",
"@types/mdx": "^2.0.13", "@types/mdx": "^2.0.13",
"@types/node": "22.14.0", "@types/node": "22.14.0",
"@types/react": "^19.1.8", "@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6", "@types/react-dom": "^19.1.6",
"eslint": "^8", "@typescript-eslint/eslint-plugin": "8.35.1",
"@typescript-eslint/parser": "8.35.1",
"eslint": "9.30.0",
"eslint-config-next": "15.3.4", "eslint-config-next": "15.3.4",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-prettier": "5.5.1",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"prettier": "3.6.2",
"prettier-plugin-sort-json": "4.1.1",
"tailwindcss": "^4.1.11", "tailwindcss": "^4.1.11",
"tw-animate-css": "^1.2.8", "tw-animate-css": "^1.2.8",
"typescript": "^5.8.3" "typescript": "^5.8.3"

5199
apps/docs/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
export default { export default {
plugins: { plugins: {
'@tailwindcss/postcss': {}, "@tailwindcss/postcss": {},
}, },
}; };

View File

@@ -1,4 +1,4 @@
import { defineDocs, defineConfig } from "fumadocs-mdx/config"; import { defineConfig, defineDocs } from "fumadocs-mdx/config";
// Options: https://fumadocs.vercel.app/docs/mdx/collections#define-docs // Options: https://fumadocs.vercel.app/docs/mdx/collections#define-docs
export const docs = defineDocs({ export const docs = defineDocs({

View File

@@ -1,11 +1,12 @@
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import { HomeLayout } from "fumadocs-ui/layouts/home"; import { HomeLayout } from "fumadocs-ui/layouts/home";
import { baseOptions } from "@/app/layout.config"; import { baseOptions } from "@/app/layout.config";
import { Particles } from "@/components/magicui/particles"; import { Particles } from "@/components/magicui/particles";
export default function Layout({ children }: { children: ReactNode }) { export default function Layout({ children }: { children: ReactNode }) {
return ( return (
<HomeLayout {...baseOptions} > <HomeLayout {...baseOptions}>
<Particles className="absolute w-full" /> <Particles className="absolute w-full" />
{children} {children}
</HomeLayout> </HomeLayout>

View File

@@ -1,31 +1,29 @@
import { import type { ReactNode } from "react";
type LucideIcon, import Link from "next/link";
MousePointer,
UploadIcon,
GithubIcon,
BookOpenText,
CloudIcon,
LockIcon,
DatabaseIcon,
} from "lucide-react";
import { import {
BatteryChargingIcon, BatteryChargingIcon,
BookOpenText,
CloudIcon,
DatabaseIcon,
GithubIcon,
KeyboardIcon, KeyboardIcon,
LayoutIcon, LayoutIcon,
LockIcon,
MousePointer,
RocketIcon, RocketIcon,
SearchIcon, SearchIcon,
TimerIcon, TimerIcon,
UploadIcon,
type LucideIcon,
} from "lucide-react"; } from "lucide-react";
import Link from "next/link";
import type { ReactNode } from "react";
import { ThreeDMarquee } from "@/components/ui/3d-marquee";
import { AnimatedGridPattern } from "@/components/magicui/animated-grid-pattern"; import { AnimatedGridPattern } from "@/components/magicui/animated-grid-pattern";
import { TypingAnimation } from "@/components/magicui/typing-animation";
import { TextHoverEffect } from "@/components/ui/text-hover-effect";
import { PulsatingButton } from "@/components/magicui/pulsating-button"; import { PulsatingButton } from "@/components/magicui/pulsating-button";
import { RippleButton } from "@/components/magicui/ripple-button"; import { RippleButton } from "@/components/magicui/ripple-button";
import { TypingAnimation } from "@/components/magicui/typing-animation";
import { WordRotate } from "@/components/magicui/word-rotate"; import { WordRotate } from "@/components/magicui/word-rotate";
import { ThreeDMarquee } from "@/components/ui/3d-marquee";
import { TextHoverEffect } from "@/components/ui/text-hover-effect";
const images = [ const images = [
"https://res.cloudinary.com/technical-intelligence/image/upload/v1745546004/Palmr./dash_wt_kqtzxi.png", "https://res.cloudinary.com/technical-intelligence/image/upload/v1745546004/Palmr./dash_wt_kqtzxi.png",
@@ -82,17 +80,11 @@ function Hero() {
return ( return (
<section className="relative z-[2] flex flex-col border-x border-t px-6 pt-12 pb-10 md:px-12 md:pt-16 max-md:text-center"> <section className="relative z-[2] flex flex-col border-x border-t px-6 pt-12 pb-10 md:px-12 md:pt-16 max-md:text-center">
<h1 className="mb-8 text-6xl font-bold"> <h1 className="mb-8 text-6xl font-bold">
Palmr.{" "} Palmr. <span className="text-[13px] font-light text-muted-foreground/50 font-mono">v3.1-beta</span>
<span className="text-[13px] font-light text-muted-foreground/50 font-mono">
v3.1-beta
</span>
</h1>
<h1 className="hidden text-4xl font-medium max-w-[600px] md:block mb-4">
Modern & efficient file sharing
</h1> </h1>
<h1 className="hidden text-4xl font-medium max-w-[600px] md:block mb-4">Modern & efficient file sharing</h1>
<p className="mb-8 text-fd-muted-foreground md:max-w-[80%] md:text-xl"> <p className="mb-8 text-fd-muted-foreground md:max-w-[80%] md:text-xl">
Palmr is a fast and secure platform for sharing files, built with Palmr is a fast and secure platform for sharing files, built with performance and privacy in mind.
performance and privacy in mind.
</p> </p>
<div className="hidden h-[10rem] lg:flex items-center justify-center absolute right-0 top-10"> <div className="hidden h-[10rem] lg:flex items-center justify-center absolute right-0 top-10">
<TextHoverEffect text="Palmr." /> <TextHoverEffect text="Palmr." />
@@ -136,13 +128,7 @@ function Feedback() {
A modern way to share files A modern way to share files
<WordRotate <WordRotate
duration={4000} duration={4000}
words={[ words={["efficiently", "securely", "privately", "reliably", "seamlessly"]}
"efficiently",
"securely",
"privately",
"reliably",
"seamlessly",
]}
className="min-w-[100px] inline-block" className="min-w-[100px] inline-block"
/> />
</p> </p>
@@ -163,8 +149,7 @@ function Features() {
<h3 className="text-2xl font-semibold">Upload & Share</h3> <h3 className="text-2xl font-semibold">Upload & Share</h3>
</div> </div>
<p className="text-muted-foreground"> <p className="text-muted-foreground">
Send your files quickly and safely. Share easily with anyone through Send your files quickly and safely. Share easily with anyone through secure links.
secure links.
</p> </p>
</div> </div>
<div className="flex flex-col gap-4 border-r p-8 md:p-12"> <div className="flex flex-col gap-4 border-r p-8 md:p-12">
@@ -174,9 +159,7 @@ function Features() {
</div> </div>
<h3 className="text-2xl font-semibold">Secure & Private</h3> <h3 className="text-2xl font-semibold">Secure & Private</h3>
</div> </div>
<p className="text-muted-foreground"> <p className="text-muted-foreground">Files are encrypted and protected. You control your data completely.</p>
Files are encrypted and protected. You control your data completely.
</p>
</div> </div>
</section> </section>
@@ -192,9 +175,7 @@ function Features() {
<p className="mb-4 w-fit bg-fd-primary px-3 py-1 text-sm font-bold font-mono text-fd-primary-foreground mx-auto"> <p className="mb-4 w-fit bg-fd-primary px-3 py-1 text-sm font-bold font-mono text-fd-primary-foreground mx-auto">
Open Source & Self-Hosted Open Source & Self-Hosted
</p> </p>
<h2 className="text-center text-2xl font-semibold sm:text-3xl mb-4"> <h2 className="text-center text-2xl font-semibold sm:text-3xl mb-4">Complete File Sharing Solution</h2>
Complete File Sharing Solution
</h2>
<TypingAnimation className="text-center text-xl text-muted-foreground"> <TypingAnimation className="text-center text-xl text-muted-foreground">
Built with Next.js, Fastify, and SQLite Built with Next.js, Fastify, and SQLite
</TypingAnimation> </TypingAnimation>
@@ -205,9 +186,7 @@ function Features() {
{/* Technical Features Grid */} {/* Technical Features Grid */}
<section className="grid grid-cols-1 border-r md:grid-cols-2 lg:grid-cols-3"> <section className="grid grid-cols-1 border-r md:grid-cols-2 lg:grid-cols-3">
<div className="col-span-full flex items-start justify-center border-l border-t p-8 pb-2 text-center"> <div className="col-span-full flex items-start justify-center border-l border-t p-8 pb-2 text-center">
<h2 className="bg-fd-primary px-1 text-2xl font-semibold text-fd-primary-foreground"> <h2 className="bg-fd-primary px-1 text-2xl font-semibold text-fd-primary-foreground">Key Features</h2>
Key Features
</h2>
<MousePointer className="-ml-1 mt-8" /> <MousePointer className="-ml-1 mt-8" />
</div> </div>
@@ -239,15 +218,7 @@ function Features() {
); );
} }
function Highlight({ function Highlight({ icon: Icon, heading, children }: { icon: LucideIcon; heading: ReactNode; children: ReactNode }) {
icon: Icon,
heading,
children,
}: {
icon: LucideIcon;
heading: ReactNode;
children: ReactNode;
}) {
return ( return (
<div className="border-l border-t px-6 py-12"> <div className="border-l border-t px-6 py-12">
<div className="mb-4 flex items-center gap-2 text-fd-muted-foreground"> <div className="mb-4 flex items-center gap-2 text-fd-muted-foreground">
@@ -264,12 +235,10 @@ function GetStarted() {
<section className="flex w-full flex-1"> <section className="flex w-full flex-1">
<div className="w-full flex flex-col gap-8 overflow-hidden border px-8 py-14"> <div className="w-full flex flex-col gap-8 overflow-hidden border px-8 py-14">
<div className="text-center mb-6"> <div className="text-center mb-6">
<h2 className="text-4xl font-extrabold font-mono uppercase mb-3"> <h2 className="text-4xl font-extrabold font-mono uppercase mb-3">Get Started Today</h2>
Get Started Today
</h2>
<p className="text-lg text-muted-foreground max-w-2xl mx-auto"> <p className="text-lg text-muted-foreground max-w-2xl mx-auto">
Deploy your own secure file sharing platform in minutes. Take Deploy your own secure file sharing platform in minutes. Take control of your data with our self-hosted
control of your data with our self-hosted solution. solution.
</p> </p>
</div> </div>
@@ -282,8 +251,7 @@ function GetStarted() {
</div> </div>
<h3 className="text-xl font-semibold">Quick Setup</h3> <h3 className="text-xl font-semibold">Quick Setup</h3>
<p className="text-muted-foreground"> <p className="text-muted-foreground">
Docker deployment or direct installation - get running in under 5 Docker deployment or direct installation - get running in under 5 minutes
minutes
</p> </p>
</div> </div>
@@ -295,8 +263,7 @@ function GetStarted() {
</div> </div>
<h3 className="text-xl font-semibold">Full Control</h3> <h3 className="text-xl font-semibold">Full Control</h3>
<p className="text-muted-foreground"> <p className="text-muted-foreground">
Self-hosted means you own your data and control every aspect of Self-hosted means you own your data and control every aspect of the platform
the platform
</p> </p>
</div> </div>
@@ -307,9 +274,7 @@ function GetStarted() {
</div> </div>
</div> </div>
<h3 className="text-xl font-semibold">Production Ready</h3> <h3 className="text-xl font-semibold">Production Ready</h3>
<p className="text-muted-foreground"> <p className="text-muted-foreground">Latest technologies optimized for performance and security</p>
Latest technologies optimized for performance and security
</p>
</div> </div>
</div> </div>

View File

@@ -3,17 +3,10 @@ import { NextResponse } from "next/server";
export async function GET() { export async function GET() {
try { try {
const key = execSync( const key = execSync("openssl rand -base64 48 | tr -dc 'A-Za-z0-9' | head -c 64").toString().trim();
"openssl rand -base64 48 | tr -dc 'A-Za-z0-9' | head -c 64"
)
.toString()
.trim();
return NextResponse.json({ key }); return NextResponse.json({ key });
} catch (error) { } catch (error) {
console.error("Failed to generate key:", error); console.error("Failed to generate key:", error);
return NextResponse.json( return NextResponse.json({ error: "Failed to generate key" }, { status: 500 });
{ error: "Failed to generate key" },
{ status: 500 }
);
} }
} }

View File

@@ -1,6 +1,7 @@
import { source } from "@/lib/source";
import { createFromSource } from "fumadocs-core/search/server"; import { createFromSource } from "fumadocs-core/search/server";
import { source } from "@/lib/source";
export const { GET } = createFromSource(source, (page) => { export const { GET } = createFromSource(source, (page) => {
return { return {
title: page.data.title, title: page.data.title,

View File

@@ -1,20 +1,14 @@
import { source } from "@/lib/source";
import {
DocsPage,
DocsBody,
DocsDescription,
DocsTitle,
} from "fumadocs-ui/page";
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
import { createRelativeLink } from "fumadocs-ui/mdx"; import { createRelativeLink } from "fumadocs-ui/mdx";
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from "fumadocs-ui/page";
import { VersionWarning } from "@/components/version-warning";
import { source } from "@/lib/source";
import { getMDXComponents } from "@/mdx-components"; import { getMDXComponents } from "@/mdx-components";
import { Footer } from "../components/footer"; import { Footer } from "../components/footer";
import { Sponsor } from "../components/sponsor"; import { Sponsor } from "../components/sponsor";
import { VersionWarning } from "@/components/version-warning";
export default async function Page(props: { export default async function Page(props: { params: Promise<{ slug?: string[] }> }) {
params: Promise<{ slug?: string[] }>;
}) {
const params = await props.params; const params = await props.params;
const page = source.getPage(params.slug); const page = source.getPage(params.slug);
if (!page) redirect("/docs/3.1-beta"); if (!page) redirect("/docs/3.1-beta");
@@ -53,9 +47,7 @@ export async function generateStaticParams() {
return source.generateParams(); return source.generateParams();
} }
export async function generateMetadata(props: { export async function generateMetadata(props: { params: Promise<{ slug?: string[] }> }) {
params: Promise<{ slug?: string[] }>;
}) {
const params = await props.params; const params = await props.params;
const page = source.getPage(params.slug); const page = source.getPage(params.slug);
if (!page) redirect("/docs/3.1-beta"); if (!page) redirect("/docs/3.1-beta");

View File

@@ -1,4 +1,4 @@
import Link from 'fumadocs-core/link'; import Link from "fumadocs-core/link";
export function Footer() { export function Footer() {
return ( return (

View File

@@ -1,16 +1,12 @@
import { DocsLayout } from "fumadocs-ui/layouts/docs";
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import { DocsLayout } from "fumadocs-ui/layouts/docs";
import { baseOptions } from "@/app/layout.config"; import { baseOptions } from "@/app/layout.config";
import { source } from "@/lib/source"; import { source } from "@/lib/source";
export default function Layout({ children }: { children: ReactNode }) { export default function Layout({ children }: { children: ReactNode }) {
return ( return (
<DocsLayout <DocsLayout tree={source.pageTree} {...baseOptions} githubUrl="https://github.com/kyantech/Palmr" links={[]}>
tree={source.pageTree}
{...baseOptions}
githubUrl="https://github.com/kyantech/Palmr"
links={[]}
>
{children} {children}
</DocsLayout> </DocsLayout>
); );

View File

@@ -57,23 +57,24 @@ h4 {
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground); --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border); --color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring); --color-sidebar-ring: var(--sidebar-ring);
--animate-ripple: ripple var(--duration,2s) ease calc(var(--i, 0)*.2s) infinite; --animate-ripple: ripple var(--duration, 2s) ease calc(var(--i, 0) * 0.2s) infinite;
@keyframes ripple { @keyframes ripple {
0%, 100% { 0%,
100% {
transform: translate(-50%, -50%) scale(1); transform: translate(-50%, -50%) scale(1);
} }
50% { 50% {
transform: translate(-50%, -50%) scale(0.9); transform: translate(-50%, -50%) scale(0.9);
} }
} }
--animate-pulse: pulse var(--duration) ease-out infinite --animate-pulse: pulse var(--duration) ease-out infinite;
;
@keyframes pulse { @keyframes pulse {
0%, 100% { 0%,
boxShadow: 0 0 0 0 var(--pulse-color); 100% {
boxshadow: 0 0 0 0 var(--pulse-color);
} }
50% { 50% {
boxShadow: 0 0 0 8px var(--pulse-color); boxshadow: 0 0 0 8px var(--pulse-color);
} }
} }
--animate-rippling: rippling var(--duration) ease-out; --animate-rippling: rippling var(--duration) ease-out;
@@ -85,7 +86,8 @@ h4 {
transform: scale(2); transform: scale(2);
opacity: 0; opacity: 0;
} }
}} }
}
:root { :root {
--radius: 0.625rem; --radius: 0.625rem;
@@ -202,7 +204,6 @@ h4 {
} }
} }
@theme inline { @theme inline {
--animate-rippling: rippling var(--duration) ease-out; --animate-rippling: rippling var(--duration) ease-out;

View File

@@ -1,7 +1,8 @@
import { LATEST_VERSION_PATH } from "@/config/constants";
import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared"; import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared";
import { Github, Palmtree } from "lucide-react"; import { Github, Palmtree } from "lucide-react";
import { LATEST_VERSION_PATH } from "@/config/constants";
export const baseOptions: BaseLayoutProps = { export const baseOptions: BaseLayoutProps = {
nav: { nav: {
title: ( title: (

View File

@@ -1,9 +1,12 @@
import { Banner } from "fumadocs-ui/components/banner"; import { Banner } from "fumadocs-ui/components/banner";
import "./global.css"; import "./global.css";
import { RootProvider } from "fumadocs-ui/provider";
import { Inter } from "next/font/google";
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import { Inter } from "next/font/google";
import Link from "fumadocs-core/link"; import Link from "fumadocs-core/link";
import { RootProvider } from "fumadocs-ui/provider";
import { LATEST_VERSION, LATEST_VERSION_PATH } from "@/config/constants"; import { LATEST_VERSION, LATEST_VERSION_PATH } from "@/config/constants";
const inter = Inter({ const inter = Inter({
@@ -12,8 +15,7 @@ const inter = Inter({
export const metadata = { export const metadata = {
title: "Palmr. | Official Website", title: "Palmr. | Official Website",
description: description: "Palmr. is a fast, simple and powerful document sharing platform.",
"Palmr. is a fast, simple and powerful document sharing platform.",
}; };
export default function Layout({ children }: { children: ReactNode }) { export default function Layout({ children }: { children: ReactNode }) {
@@ -21,9 +23,7 @@ export default function Layout({ children }: { children: ReactNode }) {
<html lang="en" className={inter.className} suppressHydrationWarning> <html lang="en" className={inter.className} suppressHydrationWarning>
<body className="flex flex-col min-h-screen"> <body className="flex flex-col min-h-screen">
<Banner variant="rainbow" id="banner-21-beta"> <Banner variant="rainbow" id="banner-21-beta">
<Link href={LATEST_VERSION_PATH}> <Link href={LATEST_VERSION_PATH}>Palmr. {LATEST_VERSION} has released!</Link>
Palmr. {LATEST_VERSION} has released!
</Link>
</Banner> </Banner>
<RootProvider <RootProvider
search={{ search={{

View File

@@ -2,6 +2,7 @@
import { useState } from "react"; import { useState } from "react";
import { Copy, X } from "lucide-react"; import { Copy, X } from "lucide-react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Toast } from "@/components/ui/toast"; import { Toast } from "@/components/ui/toast";
@@ -49,9 +50,7 @@ export function KeyGenerator() {
<code className="block w-full p-3 bg-gray-100 dark:bg-black/80 rounded-md font-mono text-sm min-h-[2.5rem] border border-gray-200 dark:border-gray-700"> <code className="block w-full p-3 bg-gray-100 dark:bg-black/80 rounded-md font-mono text-sm min-h-[2.5rem] border border-gray-200 dark:border-gray-700">
{key ? ( {key ? (
<> <>
<span className="select-none text-gray-500 dark:text-gray-400"> <span className="select-none text-gray-500 dark:text-gray-400">ENCRYPTION_KEY=</span>
ENCRYPTION_KEY=
</span>
<span className="text-green-900 dark:text-green-600">{key}</span> <span className="text-green-900 dark:text-green-600">{key}</span>
</> </>
) : ( ) : (
@@ -73,17 +72,11 @@ export function KeyGenerator() {
{key && ( {key && (
<p className="text-sm text-gray-500 dark:text-gray-400"> <p className="text-sm text-gray-500 dark:text-gray-400">
Copy the key and paste it into your <code>docker-compose.yml</code>{" "} Copy the key and paste it into your <code>docker-compose.yml</code> file.
file.
</p> </p>
)} )}
{showToast && ( {showToast && <Toast message="Key copied to clipboard!" onClose={() => setShowToast(false)} />}
<Toast
message="Key copied to clipboard!"
onClose={() => setShowToast(false)}
/>
)}
</div> </div>
); );
} }

View File

@@ -1,15 +1,6 @@
import { Chrome, Egg, Github, Key, Lock, MessageSquare, Settings, Shield, Users } from "lucide-react";
import { Card, CardGrid } from "@/components/ui/card"; import { Card, CardGrid } from "@/components/ui/card";
import {
Chrome,
MessageSquare,
Github,
Shield,
Lock,
Key,
Users,
Settings,
Egg,
} from "lucide-react";
const providers = [ const providers = [
{ {

View File

@@ -1,18 +1,11 @@
"use client"; "use client";
import { ComponentPropsWithoutRef, useEffect, useId, useRef, useState } from "react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import {
ComponentPropsWithoutRef,
useEffect,
useId,
useRef,
useState,
} from "react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
export interface AnimatedGridPatternProps export interface AnimatedGridPatternProps extends ComponentPropsWithoutRef<"svg"> {
extends ComponentPropsWithoutRef<"svg"> {
width?: number; width?: number;
height?: number; height?: number;
x?: number; x?: number;
@@ -66,8 +59,8 @@ export function AnimatedGridPattern({
...sq, ...sq,
pos: getPos(), pos: getPos(),
} }
: sq, : sq
), )
); );
}; };
@@ -106,24 +99,13 @@ export function AnimatedGridPattern({
aria-hidden="true" aria-hidden="true"
className={cn( className={cn(
"pointer-events-none absolute inset-0 h-full w-full fill-gray-400/30 stroke-gray-400/30", "pointer-events-none absolute inset-0 h-full w-full fill-gray-400/30 stroke-gray-400/30",
className, className
)} )}
{...props} {...props}
> >
<defs> <defs>
<pattern <pattern id={id} width={width} height={height} patternUnits="userSpaceOnUse" x={x} y={y}>
id={id} <path d={`M.5 ${height}V.5H${width}`} fill="none" strokeDasharray={strokeDasharray} />
width={width}
height={height}
patternUnits="userSpaceOnUse"
x={x}
y={y}
>
<path
d={`M.5 ${height}V.5H${width}`}
fill="none"
strokeDasharray={strokeDasharray}
/>
</pattern> </pattern>
</defs> </defs>
<rect width="100%" height="100%" fill={`url(#${id})`} /> <rect width="100%" height="100%" fill={`url(#${id})`} />

View File

@@ -1,12 +1,8 @@
"use client"; "use client";
import React, { ComponentPropsWithoutRef, useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import React, {
ComponentPropsWithoutRef,
useEffect,
useRef,
useState,
} from "react";
interface MousePosition { interface MousePosition {
x: number; x: number;
@@ -220,12 +216,7 @@ export const Particles: React.FC<ParticlesProps> = ({
const clearContext = () => { const clearContext = () => {
if (context.current) { if (context.current) {
context.current.clearRect( context.current.clearRect(0, 0, canvasSize.current.w, canvasSize.current.h);
0,
0,
canvasSize.current.w,
canvasSize.current.h,
);
} }
}; };
@@ -238,15 +229,8 @@ export const Particles: React.FC<ParticlesProps> = ({
} }
}; };
const remapValue = ( const remapValue = (value: number, start1: number, end1: number, start2: number, end2: number): number => {
value: number, const remapped = ((value - start1) * (end2 - start2)) / (end1 - start1) + start2;
start1: number,
end1: number,
start2: number,
end2: number,
): number => {
const remapped =
((value - start1) * (end2 - start2)) / (end1 - start1) + start2;
return remapped > 0 ? remapped : 0; return remapped > 0 ? remapped : 0;
}; };
@@ -261,9 +245,7 @@ export const Particles: React.FC<ParticlesProps> = ({
canvasSize.current.h - circle.y - circle.translateY - circle.size, // distance from bottom edge canvasSize.current.h - circle.y - circle.translateY - circle.size, // distance from bottom edge
]; ];
const closestEdge = edge.reduce((a, b) => Math.min(a, b)); const closestEdge = edge.reduce((a, b) => Math.min(a, b));
const remapClosestEdge = parseFloat( const remapClosestEdge = parseFloat(remapValue(closestEdge, 0, 20, 0, 1).toFixed(2));
remapValue(closestEdge, 0, 20, 0, 1).toFixed(2),
);
if (remapClosestEdge > 1) { if (remapClosestEdge > 1) {
circle.alpha += 0.02; circle.alpha += 0.02;
if (circle.alpha > circle.targetAlpha) { if (circle.alpha > circle.targetAlpha) {
@@ -274,12 +256,8 @@ export const Particles: React.FC<ParticlesProps> = ({
} }
circle.x += circle.dx + vx; circle.x += circle.dx + vx;
circle.y += circle.dy + vy; circle.y += circle.dy + vy;
circle.translateX += circle.translateX += (mouse.current.x / (staticity / circle.magnetism) - circle.translateX) / ease;
(mouse.current.x / (staticity / circle.magnetism) - circle.translateX) / circle.translateY += (mouse.current.y / (staticity / circle.magnetism) - circle.translateY) / ease;
ease;
circle.translateY +=
(mouse.current.y / (staticity / circle.magnetism) - circle.translateY) /
ease;
drawCircle(circle, true); drawCircle(circle, true);
@@ -301,12 +279,7 @@ export const Particles: React.FC<ParticlesProps> = ({
}; };
return ( return (
<div <div className={cn("pointer-events-none", className)} ref={canvasContainerRef} aria-hidden="true" {...props}>
className={cn("pointer-events-none", className)}
ref={canvasContainerRef}
aria-hidden="true"
{...props}
>
<canvas ref={canvasRef} className="size-full" /> <canvas ref={canvasRef} className="size-full" />
</div> </div>
); );

View File

@@ -1,32 +1,20 @@
import React from "react"; import React from "react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
interface PulsatingButtonProps interface PulsatingButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
pulseColor?: string; pulseColor?: string;
duration?: string; duration?: string;
} }
export const PulsatingButton = React.forwardRef< export const PulsatingButton = React.forwardRef<HTMLButtonElement, PulsatingButtonProps>(
HTMLButtonElement, ({ className, children, pulseColor = "#808080", duration = "1.5s", ...props }, ref) => {
PulsatingButtonProps
>(
(
{
className,
children,
pulseColor = "#808080",
duration = "1.5s",
...props
},
ref,
) => {
return ( return (
<button <button
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-pointer items-center justify-center rounded-lg bg-primary px-4 py-2 text-center text-primary-foreground", "relative flex cursor-pointer items-center justify-center rounded-lg bg-primary px-4 py-2 text-center text-primary-foreground",
className, className
)} )}
style={ style={
{ {
@@ -40,7 +28,7 @@ export const PulsatingButton = React.forwardRef<
<div className="absolute left-1/2 top-1/2 size-full -translate-x-1/2 -translate-y-1/2 animate-pulse rounded-lg bg-inherit" /> <div className="absolute left-1/2 top-1/2 size-full -translate-x-1/2 -translate-y-1/2 animate-pulse rounded-lg bg-inherit" />
</button> </button>
); );
}, }
); );
PulsatingButton.displayName = "PulsatingButton"; PulsatingButton.displayName = "PulsatingButton";

View File

@@ -1,32 +1,17 @@
"use client"; "use client";
import { cn } from "@/lib/utils";
import React, { MouseEvent, useEffect, useState } from "react"; import React, { MouseEvent, useEffect, useState } from "react";
interface RippleButtonProps import { cn } from "@/lib/utils";
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
interface RippleButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
rippleColor?: string; rippleColor?: string;
duration?: string; duration?: string;
} }
export const RippleButton = React.forwardRef< export const RippleButton = React.forwardRef<HTMLButtonElement, RippleButtonProps>(
HTMLButtonElement, ({ className, children, rippleColor = "#ffffff", duration = "600ms", onClick, ...props }, ref) => {
RippleButtonProps const [buttonRipples, setButtonRipples] = useState<Array<{ x: number; y: number; size: number; key: number }>>([]);
>(
(
{
className,
children,
rippleColor = "#ffffff",
duration = "600ms",
onClick,
...props
},
ref,
) => {
const [buttonRipples, setButtonRipples] = useState<
Array<{ x: number; y: number; size: number; key: number }>
>([]);
const handleClick = (event: MouseEvent<HTMLButtonElement>) => { const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
createRipple(event); createRipple(event);
@@ -48,9 +33,7 @@ export const RippleButton = React.forwardRef<
if (buttonRipples.length > 0) { if (buttonRipples.length > 0) {
const lastRipple = buttonRipples[buttonRipples.length - 1]; const lastRipple = buttonRipples[buttonRipples.length - 1];
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
setButtonRipples((prevRipples) => setButtonRipples((prevRipples) => prevRipples.filter((ripple) => ripple.key !== lastRipple.key));
prevRipples.filter((ripple) => ripple.key !== lastRipple.key),
);
}, parseInt(duration)); }, parseInt(duration));
return () => clearTimeout(timeout); return () => clearTimeout(timeout);
} }
@@ -60,7 +43,7 @@ export const RippleButton = React.forwardRef<
<button <button
className={cn( className={cn(
"relative flex cursor-pointer items-center justify-center overflow-hidden rounded-lg border-2 bg-background px-4 py-2 text-center text-primary", "relative flex cursor-pointer items-center justify-center overflow-hidden rounded-lg border-2 bg-background px-4 py-2 text-center text-primary",
className, className
)} )}
onClick={handleClick} onClick={handleClick}
ref={ref} ref={ref}
@@ -85,7 +68,7 @@ export const RippleButton = React.forwardRef<
</span> </span>
</button> </button>
); );
}, }
); );
RippleButton.displayName = "RippleButton"; RippleButton.displayName = "RippleButton";

View File

@@ -1,8 +1,9 @@
"use client"; "use client";
import { cn } from "@/lib/utils";
import { motion, MotionProps } from "motion/react";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { motion, MotionProps } from "motion/react";
import { cn } from "@/lib/utils";
interface TypingAnimationProps extends MotionProps { interface TypingAnimationProps extends MotionProps {
children: string; children: string;
@@ -47,7 +48,7 @@ export function TypingAnimation({
observer.disconnect(); observer.disconnect();
} }
}, },
{ threshold: 0.1 }, { threshold: 0.1 }
); );
if (elementRef.current) { if (elementRef.current) {
@@ -78,10 +79,7 @@ export function TypingAnimation({
return ( return (
<MotionComponent <MotionComponent
ref={elementRef} ref={elementRef}
className={cn( className={cn("text-4xl font-bold leading-[5rem] tracking-[-0.02em]", className)}
"text-4xl font-bold leading-[5rem] tracking-[-0.02em]",
className,
)}
{...props} {...props}
> >
{displayedText} {displayedText}

View File

@@ -1,7 +1,7 @@
"use client"; "use client";
import { AnimatePresence, motion, MotionProps } from "motion/react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { AnimatePresence, motion, MotionProps } from "motion/react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@@ -37,11 +37,7 @@ export function WordRotate({
return ( return (
<div className="overflow-hidden py-2"> <div className="overflow-hidden py-2">
<AnimatePresence mode="wait"> <AnimatePresence mode="wait">
<motion.h1 <motion.h1 key={words[index]} className={cn(className)} {...motionProps}>
key={words[index]}
className={cn(className)}
{...motionProps}
>
{words[index]} {words[index]}
</motion.h1> </motion.h1>
</AnimatePresence> </AnimatePresence>

View File

@@ -1,14 +1,10 @@
"use client"; "use client";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
export const ThreeDMarquee = ({
images, export const ThreeDMarquee = ({ images, className }: { images: string[]; className?: string }) => {
className,
}: {
images: string[];
className?: string;
}) => {
// Split the images array into 4 equal parts // Split the images array into 4 equal parts
const chunkSize = Math.ceil(images.length / 4); const chunkSize = Math.ceil(images.length / 4);
const chunks = Array.from({ length: 4 }, (_, colIndex) => { const chunks = Array.from({ length: 4 }, (_, colIndex) => {
@@ -16,12 +12,7 @@ export const ThreeDMarquee = ({
return images.slice(start, start + chunkSize); return images.slice(start, start + chunkSize);
}); });
return ( return (
<div <div className={cn("mx-auto block h-[600px] overflow-hidden rounded-2xl max-sm:h-100", className)}>
className={cn(
"mx-auto block h-[600px] overflow-hidden rounded-2xl max-sm:h-100",
className,
)}
>
<div className="flex size-full items-center justify-center"> <div className="flex size-full items-center justify-center">
<div className="size-[1720px] shrink-0 scale-50 sm:scale-75 lg:scale-100"> <div className="size-[1720px] shrink-0 scale-50 sm:scale-75 lg:scale-100">
<div <div
@@ -71,13 +62,7 @@ export const ThreeDMarquee = ({
); );
}; };
const GridLineHorizontal = ({ const GridLineHorizontal = ({ className, offset }: { className?: string; offset?: string }) => {
className,
offset,
}: {
className?: string;
offset?: string;
}) => {
return ( return (
<div <div
style={ style={
@@ -100,19 +85,13 @@ const GridLineHorizontal = ({
"[mask-composite:exclude]", "[mask-composite:exclude]",
"z-30", "z-30",
"dark:bg-[linear-gradient(to_right,var(--color-dark),var(--color-dark)_50%,transparent_0,transparent)]", "dark:bg-[linear-gradient(to_right,var(--color-dark),var(--color-dark)_50%,transparent_0,transparent)]",
className, className
)} )}
></div> ></div>
); );
}; };
const GridLineVertical = ({ const GridLineVertical = ({ className, offset }: { className?: string; offset?: string }) => {
className,
offset,
}: {
className?: string;
offset?: string;
}) => {
return ( return (
<div <div
style={ style={
@@ -135,7 +114,7 @@ const GridLineVertical = ({
"[mask-composite:exclude]", "[mask-composite:exclude]",
"z-30", "z-30",
"dark:bg-[linear-gradient(to_bottom,var(--color-dark),var(--color-dark)_50%,transparent_0,transparent)]", "dark:bg-[linear-gradient(to_bottom,var(--color-dark),var(--color-dark)_50%,transparent_0,transparent)]",
className, className
)} )}
></div> ></div>
); );

View File

@@ -1,12 +1,12 @@
import { ButtonHTMLAttributes, forwardRef } from "react"; import { ButtonHTMLAttributes, forwardRef } from "react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> { interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
className?: string; className?: string;
} }
export const Button = forwardRef<HTMLButtonElement, ButtonProps>( export const Button = forwardRef<HTMLButtonElement, ButtonProps>(({ className, children, ...props }, ref) => {
({ className, children, ...props }, ref) => {
return ( return (
<button <button
ref={ref} ref={ref}
@@ -28,7 +28,6 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
{children} {children}
</button> </button>
); );
} });
);
Button.displayName = "Button"; Button.displayName = "Button";

View File

@@ -1,6 +1,7 @@
import Link from "next/link";
import { cn } from "@/lib/utils";
import { ReactNode } from "react"; import { ReactNode } from "react";
import Link from "next/link";
import { cn } from "@/lib/utils";
interface CardProps { interface CardProps {
title: string; title: string;
@@ -11,14 +12,7 @@ interface CardProps {
onClick?: () => void; onClick?: () => void;
} }
export const Card = ({ export const Card = ({ title, description, href, icon, className, onClick }: CardProps) => {
title,
description,
href,
icon,
className,
onClick,
}: CardProps) => {
const cardContent = ( const cardContent = (
<div <div
className={cn( className={cn(
@@ -55,12 +49,7 @@ export const Card = ({
stroke="currentColor" stroke="currentColor"
viewBox="0 0 24 24" viewBox="0 0 24 24"
> >
<path <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M9 5l7 7-7 7" />
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2.5}
d="M9 5l7 7-7 7"
/>
</svg> </svg>
</div> </div>
</div> </div>
@@ -93,15 +82,5 @@ interface CardGridProps {
} }
export const CardGrid = ({ children, className }: CardGridProps) => { export const CardGrid = ({ children, className }: CardGridProps) => {
return ( return <div className={cn("grid grid-cols-1 sm:grid-cols-2 gap-2.5 mt-5", "max-w-4xl", className)}>{children}</div>;
<div
className={cn(
"grid grid-cols-1 sm:grid-cols-2 gap-2.5 mt-5",
"max-w-4xl",
className
)}
>
{children}
</div>
);
}; };

View File

@@ -1,15 +1,9 @@
"use client"; "use client";
import React, { useRef, useEffect, useState } from "react";
import React, { useEffect, useRef, useState } from "react";
import { motion } from "motion/react"; import { motion } from "motion/react";
export const TextHoverEffect = ({ export const TextHoverEffect = ({ text, duration }: { text: string; duration?: number; automatic?: boolean }) => {
text,
duration,
}: {
text: string;
duration?: number;
automatic?: boolean;
}) => {
const svgRef = useRef<SVGSVGElement>(null); const svgRef = useRef<SVGSVGElement>(null);
const [cursor, setCursor] = useState({ x: 0, y: 0 }); const [cursor, setCursor] = useState({ x: 0, y: 0 });
const [hovered, setHovered] = useState(false); const [hovered, setHovered] = useState(false);
@@ -40,13 +34,7 @@ export const TextHoverEffect = ({
className="select-none" className="select-none"
> >
<defs> <defs>
<linearGradient <linearGradient id="textGradient" gradientUnits="userSpaceOnUse" cx="50%" cy="50%" r="25%">
id="textGradient"
gradientUnits="userSpaceOnUse"
cx="50%"
cy="50%"
r="25%"
>
{hovered && ( {hovered && (
<> <>
<stop offset="0%" stopColor="#eab308" /> <stop offset="0%" stopColor="#eab308" />
@@ -78,13 +66,7 @@ export const TextHoverEffect = ({
<stop offset="100%" stopColor="black" /> <stop offset="100%" stopColor="black" />
</motion.radialGradient> </motion.radialGradient>
<mask id="textMask"> <mask id="textMask">
<rect <rect x="0" y="0" width="100%" height="100%" fill="url(#revealMask)" />
x="0"
y="0"
width="100%"
height="100%"
fill="url(#revealMask)"
/>
</mask> </mask>
</defs> </defs>
<text <text

View File

@@ -1,8 +1,8 @@
"use client"; "use client";
import { useEffect } from "react"; import { useEffect } from "react";
import { createPortal } from "react-dom";
import { CheckCircle } from "lucide-react"; import { CheckCircle } from "lucide-react";
import { createPortal } from "react-dom";
interface ToastProps { interface ToastProps {
message: string; message: string;

View File

@@ -1,20 +1,17 @@
"use client"; "use client";
import React, { useState, useEffect } from "react"; import React, { useEffect, useState } from "react";
import { cn } from "@/lib/utils";
import { X, ZoomIn } from "lucide-react"; import { X, ZoomIn } from "lucide-react";
import { cn } from "@/lib/utils";
interface ZoomableImageProps { interface ZoomableImageProps {
src: string; src: string;
alt: string; alt: string;
className?: string; className?: string;
} }
export const ZoomableImage: React.FC<ZoomableImageProps> = ({ export const ZoomableImage: React.FC<ZoomableImageProps> = ({ src, alt, className }) => {
src,
alt,
className,
}) => {
const [isZoomed, setIsZoomed] = useState(false); const [isZoomed, setIsZoomed] = useState(false);
const handleImageClick = () => { const handleImageClick = () => {

View File

@@ -1,20 +1,19 @@
"use client"; "use client";
import { LATEST_VERSION, LATEST_VERSION_PATH } from "@/config/constants"; import { useEffect, useRef, useState } from "react";
import { AlertTriangle, ArrowRightIcon, X } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import { useState, useEffect, useRef } from "react"; import { AlertTriangle, ArrowRightIcon, X } from "lucide-react";
function useIntersectionObserver( import { LATEST_VERSION, LATEST_VERSION_PATH } from "@/config/constants";
targetRef: React.RefObject<HTMLDivElement | null>
) { function useIntersectionObserver(targetRef: React.RefObject<HTMLDivElement | null>) {
const [isIntersecting, setIsIntersecting] = useState(true); const [isIntersecting, setIsIntersecting] = useState(true);
useEffect(() => { useEffect(() => {
const observer = new IntersectionObserver( const observer = new IntersectionObserver(([entry]) => setIsIntersecting(entry.isIntersecting), {
([entry]) => setIsIntersecting(entry.isIntersecting), threshold: 0,
{ threshold: 0, rootMargin: "-10px 0px 0px 0px" } rootMargin: "-10px 0px 0px 0px",
); });
if (targetRef.current) { if (targetRef.current) {
observer.observe(targetRef.current); observer.observe(targetRef.current);
@@ -26,10 +25,7 @@ function useIntersectionObserver(
return isIntersecting; return isIntersecting;
} }
function useClickOutside( function useClickOutside(ref: React.RefObject<HTMLDivElement | null>, callback: () => void) {
ref: React.RefObject<HTMLDivElement | null>,
callback: () => void
) {
useEffect(() => { useEffect(() => {
const handleClickOutside = (event: MouseEvent) => { const handleClickOutside = (event: MouseEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) { if (ref.current && !ref.current.contains(event.target as Node)) {
@@ -59,16 +55,14 @@ function WarningContent({ onClose }: { onClose: () => void }) {
Deprecated version documentation Deprecated version documentation
</h3> </h3>
<p className="text-[15px] text-amber-700 dark:text-amber-300 mb-3"> <p className="text-[15px] text-amber-700 dark:text-amber-300 mb-3">
This documentation refers to a previous version of Palmr. It may This documentation refers to a previous version of Palmr. It may contain more complex configurations and
contain more complex configurations and bugs that have already been bugs that have already been fixed.
fixed.
</p> </p>
<Link <Link
href={LATEST_VERSION_PATH} href={LATEST_VERSION_PATH}
className="inline-flex items-center text-base font-medium text-amber-800 dark:text-amber-100 hover:text-amber-900 dark:hover:text-amber-200" className="inline-flex items-center text-base font-medium text-amber-800 dark:text-amber-100 hover:text-amber-900 dark:hover:text-amber-200"
> >
View latest documentation ({LATEST_VERSION}){" "} View latest documentation ({LATEST_VERSION}) <ArrowRightIcon className="w-4 h-4 ml-1 mt-0.5" />
<ArrowRightIcon className="w-4 h-4 ml-1 mt-0.5" />
</Link> </Link>
</div> </div>
</div> </div>
@@ -85,10 +79,7 @@ function FloatingWarning({ onClose }: { onClose: () => void }) {
const toggleContent = () => setShowContent(!showContent); const toggleContent = () => setShowContent(!showContent);
return ( return (
<div <div className="fixed bottom-6 z-50" style={{ right: "max(1.5rem, calc((100vw - 1024px) / 2 + 1.5rem))" }}>
className="fixed bottom-6 z-50"
style={{ right: "max(1.5rem, calc((100vw - 1024px) / 2 + 1.5rem))" }}
>
<div className="relative" ref={floatingRef}> <div className="relative" ref={floatingRef}>
<button <button
onClick={toggleContent} onClick={toggleContent}

View File

@@ -1,7 +1,8 @@
import { docs } from "@/.source"; import { createElement } from "react";
import { loader } from "fumadocs-core/source"; import { loader } from "fumadocs-core/source";
import { icons } from "lucide-react"; import { icons } from "lucide-react";
import { createElement } from "react";
import { docs } from "@/.source";
// See https://fumadocs.vercel.app/docs/headless/source-api for more info // See https://fumadocs.vercel.app/docs/headless/source-api for more info
export const source = loader({ export const source = loader({

View File

@@ -1,4 +1,4 @@
import { type ClassValue, clsx } from "clsx"; import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {

View File

@@ -1,5 +1,6 @@
import defaultMdxComponents from "fumadocs-ui/mdx"; import defaultMdxComponents from "fumadocs-ui/mdx";
import type { MDXComponents } from "mdx/types"; import type { MDXComponents } from "mdx/types";
import { KeyGenerator } from "@/components/KeyGenerator"; import { KeyGenerator } from "@/components/KeyGenerator";
import { OIDCProviderCards } from "@/components/OIDCProviderCards"; import { OIDCProviderCards } from "@/components/OIDCProviderCards";
import { Card, CardGrid } from "@/components/ui/card"; import { Card, CardGrid } from "@/components/ui/card";

View File

@@ -2,11 +2,7 @@
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "baseUrl": ".",
"target": "ESNext", "target": "ESNext",
"lib": [ "lib": ["dom", "dom.iterable", "esnext"],
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"strict": true, "strict": true,
@@ -20,12 +16,8 @@
"jsx": "preserve", "jsx": "preserve",
"incremental": true, "incremental": true,
"paths": { "paths": {
"@/.source": [ "@/.source": ["./.source/index.ts"],
"./.source/index.ts" "@/*": ["./src/*"]
],
"@/*": [
"./src/*"
]
}, },
"plugins": [ "plugins": [
{ {
@@ -33,13 +25,6 @@
} }
] ]
}, },
"include": [ "exclude": ["node_modules"],
"next-env.d.ts", "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"]
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
} }