From da4a46014ddee1ae0906257441dc7a69aab3af0c Mon Sep 17 00:00:00 2001 From: mychidarko Date: Tue, 11 Feb 2025 15:22:14 +0000 Subject: [PATCH 01/37] feat: reprioritize maker sections --- .vitepress/config.mts | 2 +- .vitepress/config/head.ts | 61 ++-- .vitepress/config/navbar.ts | 92 +---- .vitepress/config/sidebar.ts | 1 - .../theme/components/Community/Blog.vue | 46 +-- .../Community/NewsLetterSubstack.vue | 2 +- .../theme/components/Community/Speak.vue | 10 +- .../theme/components/Community/WallOfFame.vue | 6 +- .../theme/components/Home/Community.vue | 4 +- .vitepress/theme/components/Home/Deploy.vue | 12 +- .vitepress/theme/components/Home/Hero.vue | 80 +++-- .vitepress/theme/components/Home/Home.vue | 10 +- .../theme/components/Home/Particles.vue | 2 +- .../theme/components/Home/Testimonials.vue | 24 +- .vitepress/theme/components/Home/Tooling.vue | 318 +++++++++++++++--- .vitepress/theme/components/Home/TryIt.vue | 5 +- .vitepress/theme/components/shared/Button.vue | 2 +- .../theme/components/shared/SidebarLinks.vue | 124 +++++++ .../theme/components/shared/SponsorCard.vue | 2 +- .../theme/components/shared/VideoModal.vue | 2 +- .vitepress/theme/index.ts | 2 + .vitepress/theme/styles/index.css | 19 +- src/public/global.css | 12 +- 23 files changed, 607 insertions(+), 231 deletions(-) create mode 100644 .vitepress/theme/components/shared/SidebarLinks.vue diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 4e395244..2dd7f753 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -16,7 +16,7 @@ export default defineConfig({ srcExclude: ['tutorial/**/description.md'], title: 'Leaf PHP', - description: 'Simple and elegant PHP', + description: 'Elegant PHP, Built for Makers', themeConfig: { nav, diff --git a/.vitepress/config/head.ts b/.vitepress/config/head.ts index 2e2cac12..77b4ee41 100644 --- a/.vitepress/config/head.ts +++ b/.vitepress/config/head.ts @@ -13,7 +13,7 @@ const head: HeadConfig[] = [ { name: 'description', content: - 'Leaf is a lightweight and user-friendly framework designed for quick and efficient development. It features a zero-config setup and an ecosystem of tools, making it ideal for building scalable apps with ease.', + 'Leaf is a lightweight and elegant PHP framework built for makers. With zero-config setup and an ecosystem of powerful tools, Leaf helps you build and ship scalable apps—fast', }, ], ['meta', { name: 'twitter:site', content: '@leafphp' }], @@ -23,7 +23,7 @@ const head: HeadConfig[] = [ 'meta', { name: 'twitter:title', - content: 'Leaf PHP - Elegant PHP for Modern Development', + content: 'Leaf PHP - Elegant PHP, Built for Makers', }, ], [ @@ -31,7 +31,7 @@ const head: HeadConfig[] = [ { name: 'twitter:description', content: - 'Leaf is a lightweight and user-friendly framework designed for quick and efficient development. It features a zero-config setup and an ecosystem of tools, making it ideal for building scalable apps with ease.', + 'Leaf is a lightweight and elegant PHP framework built for makers. With zero-config setup and an ecosystem of powerful tools, Leaf helps you build and ship scalable apps—fast', }, ], [ @@ -46,7 +46,7 @@ const head: HeadConfig[] = [ 'meta', { name: 'og:title', - content: 'Leaf PHP - Elegant PHP for Modern Development', + content: 'Leaf PHP - Elegant PHP, Built for Makers', }, ], [ @@ -76,7 +76,7 @@ const head: HeadConfig[] = [ { name: 'og:description', content: - 'Leaf is a lightweight and user-friendly framework designed for quick and efficient development. It features a zero-config setup and an ecosystem of tools, making it ideal for building scalable apps with ease.', + 'Leaf is a lightweight and elegant PHP framework built for makers. With zero-config setup and an ecosystem of powerful tools, Leaf helps you build and ship scalable apps—fast', }, ], [ @@ -128,7 +128,22 @@ const head: HeadConfig[] = [ [ 'link', { - href: 'https://fonts.googleapis.com/css?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500|DM+Sans:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700|Inter:300,400,500,600|Open+Sans:400,600;display=swap', + href: 'https://fonts.googleapis.com', + rel: 'preconnect', + }, + ], + [ + 'link', + { + href: 'https://fonts.gstatic.com', + rel: 'preconnect', + crossorigin: '', + }, + ], + [ + 'link', + { + href: 'https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,200..800&family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap', rel: 'stylesheet', }, ], @@ -147,24 +162,24 @@ const head: HeadConfig[] = [ // 'utf-8' // ), // ], - [ - 'script', - { - async: '', - src: 'https://www.googletagmanager.com/gtag/js?id=G-QGZVHHLK12', - }, - ], - [ - 'script', - {}, - ` - window.dataLayer = window.dataLayer || []; - function gtag(){dataLayer.push(arguments);} - gtag('js', new Date()); + // [ + // 'script', + // { + // async: '', + // src: 'https://www.googletagmanager.com/gtag/js?id=G-QGZVHHLK12', + // }, + // ], + // [ + // 'script', + // {}, + // ` + // window.dataLayer = window.dataLayer || []; + // function gtag(){dataLayer.push(arguments);} + // gtag('js', new Date()); - gtag('config', 'G-QGZVHHLK12'); - `, - ], + // gtag('config', 'G-QGZVHHLK12'); + // `, + // ], ]; export default head; diff --git a/.vitepress/config/navbar.ts b/.vitepress/config/navbar.ts index b9c95434..6daabda3 100644 --- a/.vitepress/config/navbar.ts +++ b/.vitepress/config/navbar.ts @@ -4,107 +4,29 @@ const nav: DefaultTheme.NavItem[] = [ { text: 'Docs', activeMatch: `^/(docs|examples)/`, - items: [ - { text: 'Guide', link: '/docs/' }, - { text: 'Tutorial', link: '/tutorial/' }, - { - text: 'Leaf + MVC', - link: '/docs/mvc/', - }, - { - text: 'Leaf Modules', - link: '/docs/modules', - }, - { - text: 'Leaf CLI', - link: '/docs/cli/', - }, - { - text: 'MVC Console', - link: '/docs/mvc/console/', - }, - ], + link: '/docs/', }, - { - text: 'Ecosystem', - activeMatch: `^/ecosystem/`, - items: [ - { - text: 'Resources', - items: [ - { text: 'Online Playground', link: 'https://sandbox.leafphp.dev/' }, - { - text: 'Codelabs', - link: '/codelabs/', - }, - // { - // text: 'Leaf UI', - // link: 'https://ui.leafphp.dev/', - // }, - ], - }, - { - text: 'Other', - items: [ - { - text: 'Hana JS', - link: 'https://hana.leafphp.dev', - }, - { - text: 'Naytive', - link: 'https://naytive.netlify.app', - }, - ], - }, - { - text: 'Help', - items: [ - // { - // text: 'Leaf Forum', - // link: 'https://github.com/leafsphp/leaf/discussions/37', - // }, - { - text: 'YouTube', - link: 'https://www.youtube.com/channel/UCllE-GsYy10RkxBUK0HIffw', - }, - { - text: 'Discord', - link: 'https://discord.gg/Pkrm9NJPE3', - }, - { - text: 'GitHub', - link: 'https://github.com/leafsphp/leaf', - }, - ], - }, - ], + text: 'Blog', + link: 'https://blog.leafphp.dev', }, { text: 'Community', activeMatch: `^/(about|community)/`, items: [ - { - text: 'Leaf Community', - link: '/community/', - }, { text: 'Contribute to Leaf', link: '/community/guide', }, - { - text: 'Changelog', - link: '/community/releases', - }, + // { + // text: 'Changelog', + // link: '/community/releases', + // }, // { // text: 'Project Showcase', // link: '/community/showcase', // }, - { - text: 'Blog', - link: 'https://blog.leafphp.dev', - }, { text: 'Team', link: '/community/team', diff --git a/.vitepress/config/sidebar.ts b/.vitepress/config/sidebar.ts index 8b21e630..ca2bd02a 100644 --- a/.vitepress/config/sidebar.ts +++ b/.vitepress/config/sidebar.ts @@ -4,7 +4,6 @@ const sidebar = [ // collapsible: true, // collapsed: true, items: [ - { text: 'Introduction', link: '/docs/' }, { text: 'Installation', link: '/docs/installation' }, { text: 'Migration Guide', link: '/docs/migrating' }, // { text: 'Functional Mode', link: '/docs/config/functional-mode' }, diff --git a/.vitepress/theme/components/Community/Blog.vue b/.vitepress/theme/components/Community/Blog.vue index 3a339772..7188d5ca 100644 --- a/.vitepress/theme/components/Community/Blog.vue +++ b/.vitepress/theme/components/Community/Blog.vue @@ -5,32 +5,35 @@ import Button from '../shared/Button.vue'; + + +

Something amazing is coming soon!

+

Sign up to be the first to know when we launch.

+
+ + +
+ + +``` + +We have created a simple pre-launch page with a form that takes an email address, now we just need to hook it up to a route that will display this view. + +```php{9} [index.php] +get('/', function() { + response()->json(['message' => 'Hello, World!']); +}); + +app()->view('/prelaunch', 'prelaunch'); + +app()->run(); +``` + +Okay, looks like some magic is happening here. Let's break it down: + +- `app()` is a helper function that gives you access to the Leaf app instance, it is available from anywhere in your app. +- `view()` is a method that you can use to create a route that renders a Blade view. The first argument is what the user enters in the URL, and the second argument is the name of the view file to render. + +Notice we did not have to configure Blade because Leaf does that for you. You can now navigate to [http://localhost:5500/prelaunch](http://localhost:5500/prelaunch) to see your pre-launch page. + +### 2 Handling the form submission + +We have successfully created a pre-launch page, but we need to handle the form submission. We will create a new route that will handle the form submission and save the email to a database. Let's start by creating a new route. + +```php{2} [index.php] +app()->view('/prelaunch', 'prelaunch'); +app()->post('/notify', function () { + // handle the email +}); +``` + +We used the `post()` method this time because we want only POST requests to hit this route. The second argument is a function which we will use to handle the email. We just need to validate the email and save it to a database. Validation is pretty simple with Leaf, we can use the `validate()` method on our request and pass in the rules we want to validate against. + +```php [index.php] +app()->post('/notify', function () { + if (!$data = request()->validate(['email' => 'email'])) { + // validation failed, redirect back with errors + return response() + ->withFlash('errors', response()->errors()) + ->redirect('/prelaunch'); + } + + // save the email +}); +``` + +That's pretty much it for validation. But there's one thing we haven't done yet: we have not connected our database to our application yet. + +### 3 Setting up our database + +We can do this by installing the database module and then passing in our database credentials. Remember how we installed Blade earlier? We can do the same for the database module. + +::: code-group + +```bash:no-line-numbers [Leaf CLI] +leaf install db +``` + +```bash:no-line-numbers [Composer] +composer require leafs/db +``` + +::: + +Now we just need to fill out our database information: + +```php{5-10} [index.php] +connect([ + 'host' => '127.0.0.1', + 'username' => 'root', + 'password' => '', + 'dbname' => 'Leaf', +]); + +app()->get('/', function() { + response()->json(['message' => 'Hello, World!']); +}); + +app()->view('/prelaunch', 'prelaunch'); + +app()->post('/notify', function () { + if (!$data = request()->validate(['email' => 'email'])) { + // validation failed, redirect back with errors + return response() + ->withFlash('errors', response()->errors()) + ->redirect('/prelaunch'); + } + + // save the email +}); + +app()->run(); +``` + +::: details Creating your database + +Unlike with Leaf MVC, Leaf as a micro-framework does not come with a database migration system. You will have to create your database manually. You can do this using a tool like [phpMyAdmin](https://www.phpmyadmin.net/) or the command line. + +```sql +CREATE DATABASE Leaf; +``` + +::: + +Once you have filled out your database information, you can now save the email to the database. + +### 4 Saving the email + +We can now save the email to the database. + +```php [index.php] +connect([ + 'host' => '127.0.0.1', + 'username' => 'root', + 'password' => '', + 'dbname' => 'Leaf', +]); + +app()->get('/', function() { + response()->json(['message' => 'Hello, World!']); +}); + +app()->view('/prelaunch', 'prelaunch'); + +app()->post('/notify', function () { + if (!$data = request()->validate(['email' => 'email'])) { + // validation failed, redirect back with errors + return response() + ->withFlash('errors', response()->errors()) + ->redirect('/prelaunch'); + } + + db()->insert('emails')->params($data)->execute(); + + return response() + ->withFlash('success', 'You have been added to our list!') + ->redirect('/prelaunch'); +}); + +app()->run(); +``` + +You can use the `withFlash()` method to send a message to the next request. This is useful for sending messages to the user after a redirect. We can now test our app by navigating to the `/prelaunch` page and submitting an email. + +### 5 Deploying your app + +We have built a simple pre-launch page using Leaf. You can now deploy your app to a server using a service like [Heroku](/learn/deployment/heroku), [Fly.io](/learn/deployment/flyio) a VPS like [DigitalOcean](/learn/deployment/digitalocean), or even a shared hosting service like [Sevalla](/learn/deployment/sevalla). + +
+
+
+ + + + + + +
+
+ Are you stuck? + Leaf as a micro-framework is a great way to build simple applications and APIs quickly, but doesn't provide the structure you get with Leaf MVC. If you are stuck at any point, feel free to ask for help in the + Leaf Discord server, or consider building an MVP using Leaf MVC if you prefer a more structured approach. + +
+
+
+ +
+

+ What to read next +

+
+

+ Now that you have built a simple pre-launch page, the next step is to get you familiar with the basics of building a full-stack application with Leaf. So you can build and launch your next big idea fast. +

+
+ +
diff --git a/src/codelabs/contributing.md b/src/learn/contributing.md similarity index 100% rename from src/codelabs/contributing.md rename to src/learn/contributing.md diff --git a/src/codelabs/experiments/deployment/digitalocean/index.md b/src/learn/deployment/digitalocean/index.md similarity index 100% rename from src/codelabs/experiments/deployment/digitalocean/index.md rename to src/learn/deployment/digitalocean/index.md diff --git a/src/codelabs/experiments/deployment/fly.io/index.md b/src/learn/deployment/fly.io/index.md similarity index 100% rename from src/codelabs/experiments/deployment/fly.io/index.md rename to src/learn/deployment/fly.io/index.md diff --git a/src/codelabs/experiments/deployment/heroku/index.md b/src/learn/deployment/heroku/index.md similarity index 100% rename from src/codelabs/experiments/deployment/heroku/index.md rename to src/learn/deployment/heroku/index.md diff --git a/src/codelabs/experiments/deployment/index.md b/src/learn/deployment/index.md similarity index 100% rename from src/codelabs/experiments/deployment/index.md rename to src/learn/deployment/index.md diff --git a/src/learn/index.md b/src/learn/index.md new file mode 100644 index 00000000..a7680fa9 --- /dev/null +++ b/src/learn/index.md @@ -0,0 +1,140 @@ +--- +prev: false +next: false +--- + + + +

+ Learn to write
+ Elegant PHP
+ Built for Makers +

+ +Welcome to the Makers' Guide to Leaf—your fast track to building with Leaf. This guide walks you through everything you need to start shipping quickly and efficiently, from hello world to deploying your app. + +## Choose your path + +

No application is the same, why should your framework be?

Michael Darko
Creator of Leaf PHP
+ +This is something we live and die by at Leaf. We believe that every application is unique and should be treated as such. That's why we've built Leaf to be as flexible as possible, allowing you to build your applications the way you want to. At the end of the day, we're here to help you build your applications, not to dictate how you should build them. + +With that in mind, we've created a few paths to help you get started with Leaf. Whether you're building a simple app/API, a full-fledged web application, or a massive API, we've got you covered. Choose your path below to get started. + +
+
+
+
+
+

+ Basic Leaf App +

+

+ Use Leaf as a micro-framework to build simple apps and APIs. +

+ +
+ +
+ +
+
+
+
+
+
+

+ Leaf MVC App +

+

+ Add an MVC structure on top of Leaf for more complex apps. +

+ +
+ +
+ +
+
+
+
+
+
+

+ MVC for APIs +

+

+ Build APIs with a structured approach for better organization. +

+ +
+ +
+ +
+
+
+ +## Contributing + +If you've written a tutorial that you think would be a great addition to our Learn section, feel free to submit a PR to our [GitHub repository](https://github.com/leafsphp/docs) with your tutorial. For our readers' benefit, be sure to follow the [contribution guide](/learn/contributing) when submitting your tutorial. Thank you for your contribution! diff --git a/src/learn/mvc.md b/src/learn/mvc.md new file mode 100644 index 00000000..4f1f8f97 --- /dev/null +++ b/src/learn/mvc.md @@ -0,0 +1,487 @@ +# Building full-stack with Leaf MVC + +
+
+
+
+ +

+ Leaf MVC is a minimalistic PHP framework built for developers who need a simple and elegant toolkit to create full-featured web applications. +

+
+ +
+ +
+
+ +Full stack applications are usually monolithic applications that have both a front-end and a back-end. Leaf MVC gives you all the goodness of Leaf which you can use to handle requests and render views using a templating engine like [Blade](/docs/frontend/blade) or using a front-end framework like [React, Vue or Svelte](/docs/frontend/inertia). + +## Getting started + +You can create a new MVC app using the `create` command on the Leaf CLI. + +::: code-group + +```bash:no-line-numbers [Leaf CLI] +leaf create my-app --mvc +``` + +```bash:no-line-numbers [Composer] +composer create-project leafs/mvc my-app +``` + +::: + +This will create a new Leaf MVC app in the `my-app` directory. You can then navigate to the `my-app` directory and run the app using the `serve` command. + +```bash:no-line-numbers +cd my-app +php leaf serve +``` + +Your app is now running! Open [http://localhost:5500](http://localhost:5500) in your browser. + +## Project Structure + +Leaf MVC, just like the rest of Leaf is built for makers, and as such, it keeps only the essentials. Here's a basic structure of a Leaf MVC app: + +```bash:no-line-numbers +├───app +│ ├── controllers +│ ├── database +│ ├── models +│ ├── routes +│ └── views +│ └── errors +└───public + └───assets + ├── css + └── img +``` + +- app: Contains all the application code including controllers, models, views, and routes, as well as your database files. +- public: Contains all the publicly accessible files including assets like bundled CSS, JS and images. + +Just about all the work you'll be doing will be in the `app` directory. You can create routes which will call controllers which will interact with models and render views (the MVC cycle). Don't worry if you're not familiar with MVC, just remember that every request starts with a route and ends with a response of some sort. + +## Building your first app + +As a maker, the easiest way to get started with your app is by building a Coming Soon, Early Access, or Pre-Launch page. This gives you something real to share while you build, helping you gather interest and early users. Let’s create a simple pre-launch page in Leaf MVC! + +### 1 The routing bit + +In Leaf MVC, routes live in the `app/routes` directory. You'll find an index.php file along with some files that start with \_. These are partials—automatically loaded by Leaf to help you organize your routes however you like. + +For our pre-launch page, we'll create a new route in the `app/routes/_prelaunch.php` file. Open the file and add a new route like this: + +```php +view('/prelaunch', 'prelaunch'); +``` + +Okay, looks like some magic is happening here. Let's break it down: + +- `app()` is a helper function that gives you access to the Leaf app instance, it is available from anywhere in your app. +- `view()` is a method that you can use to create a route that renders a Blade view. The first argument is what the user enters in the URL, and the second argument is the name of the view file to render. + +### 2 The view bit + +We created the route, but we'll get a very nasty error if we navigate to the `/prelaunch` page. That's because we don't have our prelaunch view, so let's create that. In our `app/views` folder, we can create a `prelaunch.blade.php` file. + +```blade [app/views/prelaunch.blade.php] + + + + + + Coming Soon + + + +

Something amazing is coming soon!

+

Sign up to be the first to know when we launch.

+
+ + +
+ + +``` + +The most important bit here is the form. We're creating a simple form that takes an email address and sends it to a route that will handle the email. We'll create that route in a bit. Remember how to create a route? + +### 3 Handling the form submission + +This is the final bit of our pre-launch page, but also the most complicated part. We need to create a route that will call a controller, validate the email and save it to a database using a model. So basically, we will go through the entire MVC cycle plus a bit of configuration. Let's start with the route. + +```php{4} [app/routes/_prelaunch.php] +view('/prelaunch', 'prelaunch'); +app()->post('/notify', 'NotifyController@notify'); +``` + +We used the `post()` method this time because we want only POST requests to hit this route. The second argument is the controller and method that will handle the request. We can create the controller using the CLI. + +```bash:no-line-numbers +php leaf g:controller notify +``` + +This will create a new controller in the `app/controllers` directory. Open the `NotifyController.php` file and add the `notify` method. + +```php{7-10} [app/controllers/NotifyController.php] +validate(['email' => 'email'])) { + // validation failed, redirect back with errors + return response()->with('errors', response()->errors())->redirect('/prelaunch'); + } + + // save the email + } +} +``` + +That's pretty much it for validation. We can now save the email to a database using the model. Let's create a model for our subscribers. + +```bash:no-line-numbers +php leaf g:model Subscriber +``` + +We don't need to do anything to the model, however, we still have a bit more work to do. We have not connected our database to our application yet. We can do this by heading over to our `.env` file and adding our database credentials. + +```env +DB_CONNECTION=mysql +DB_HOST=xxx +DB_PORT=xxx +DB_DATABASE=xxx +DB_USERNAME=xxx +DB_PASSWORD=xxx +``` + +Once you fill out this information, you may need to restart your server with `php leaf serve`. We have one more thing to do before we can save our email. We need to create our database tables. Leaf MVC gives us a simple way to do this using "schema files". We can create a schema file using the CLI. + +```bash:no-line-numbers +php leaf g:schema subscribers +``` + +The name of the schema file should be the plural form of the table name. This will create a new schema file in the `app/database` directory. Open the file and add the columns you want in your table. + +```php [app/database/subscribers.yml] +columns: + email: string +``` + +Here, we are telling Leaf to add a column where we can store the email. We can now run the migration to create the table. + +```bash:no-line-numbers +php leaf db:migrate +``` + +### 4 Saving the email + +We can now save the email to the database using our `Subscriber` model. + +```php{5,16-18} [app/controllers/NotifyController.php] +validate(['email' => 'email'])) { + // validation failed, redirect back with errors + return response()->withFlash('errors', response()->errors())->redirect('/prelaunch'); + } + + $subscriber = new Subscriber; + $subscriber->email = $data['email']; + $subscriber->save(); + + return response() + ->withFlash('success', 'Nice, we will send a mail when we launch!') + ->redirect('/prelaunch'); + } +} +``` + +You can use the `withFlash()` method to send a message to the next request. This is useful for sending messages to the user after a redirect. We can now test our app by navigating to the `/prelaunch` page and submitting an email. + +### 5 Deploying your app + +We have built a simple pre-launch page using Leaf MVC. You can now deploy your app to a server using a service like [Heroku](/learn/deployment/heroku), [Fly.io](/learn/deployment/flyio) a VPS like [DigitalOcean](/learn/deployment/digitalocean), or even a shared hosting service like [Sevalla](/learn/deployment/sevalla). + +
+
+
+ + + + + + +
+
+ Are you stuck? + Working with MVC can be a bit challenging if you are just starting out because of the overly strict separation of concerns. If you are stuck at any point, feel free to ask for help in the + Leaf Discord server, or consider building an MVP using the Basic Leaf setup. While it is not as structured as MVC, it is a great way to get started with Leaf. + +
+
+
+ +
+

+ What to read next +

+
+

+ Now that you have built a simple pre-launch page, the next step is to get you familiar with the basics of building a full-stack application with Leaf. So you can build and launch your next big idea fast. +

+
+ +
diff --git a/src/public/images/illustrations/Feature-Flags-5.svg b/src/public/images/illustrations/Feature-Flags-5.svg new file mode 100644 index 00000000..0fc11e8f --- /dev/null +++ b/src/public/images/illustrations/Feature-Flags-5.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/public/images/illustrations/Heading-2.svg b/src/public/images/illustrations/Heading-2.svg new file mode 100644 index 00000000..6a3ccd6c --- /dev/null +++ b/src/public/images/illustrations/Heading-2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/public/images/illustrations/Stats-2.svg b/src/public/images/illustrations/Stats-2.svg new file mode 100644 index 00000000..363aadaa --- /dev/null +++ b/src/public/images/illustrations/Stats-2.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/public/images/illustrations/Structure-List-2.svg b/src/public/images/illustrations/Structure-List-2.svg new file mode 100644 index 00000000..1cf685be --- /dev/null +++ b/src/public/images/illustrations/Structure-List-2.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/tutorial/src/step-15/description.md b/src/tutorial/src/step-15/description.md index 49bee214..4cc562cb 100644 --- a/src/tutorial/src/step-15/description.md +++ b/src/tutorial/src/step-15/description.md @@ -4,7 +4,7 @@ You've learnt how to create routes, handle requests, and responses, and even how As a next step, you can: -- Set up a real project on your system by following the [installation docs](/docs/installation). +- Set up a real project on your system by following the [installation docs](/docs/#creating-a-new-app). - Every feature you need is available as a module, so be sure to checkout the [modules documentation](/docs/modules). From 49aab145901ad05f1e6fb1cbca1709ee8f35cefd Mon Sep 17 00:00:00 2001 From: mychidarko Date: Wed, 12 Feb 2025 18:15:38 +0000 Subject: [PATCH 03/37] feat: add specialized MVC pages --- .vitepress/config/sidebar.ts | 2 +- src/docs/database/index.md | 35 ++++++++++++++++++++++-- src/docs/routing/index.md | 36 ++++++++++++++++++++++++- src/docs/routing/middleware/index.md | 40 ++++++++++++++++++++++++++++ src/docs/routing/mvc.md | 3 +++ 5 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 src/docs/routing/mvc.md diff --git a/.vitepress/config/sidebar.ts b/.vitepress/config/sidebar.ts index 9f6aa303..e4b5f87f 100644 --- a/.vitepress/config/sidebar.ts +++ b/.vitepress/config/sidebar.ts @@ -29,7 +29,7 @@ const sidebar = [ { text: 'Route Groups', link: '/docs/routing/route-groups' }, { text: 'Dynamic routing', link: '/docs/routing/dynamic' }, { text: 'Middleware', link: '/docs/routing/middleware/' }, - { text: 'Middleware in Leaf MVC', link: '/docs/routing/middleware/mvc' }, + // { text: 'Middleware in Leaf MVC', link: '/docs/routing/middleware/mvc' }, ], }, { diff --git a/src/docs/database/index.md b/src/docs/database/index.md index de771813..dabdbdf7 100644 --- a/src/docs/database/index.md +++ b/src/docs/database/index.md @@ -3,7 +3,8 @@ A database is an organized storage system for managing data like your users' profiles or product details. Leaf offers a lightweight module that simplifies database interaction and supports five major database systems. @@ -51,7 +52,37 @@ Databases are essential for most applications, as they help you store and retrie ## Leaf MVC + DB -Leaf MVC comes with built-in support for models which are a way to programmatically represent resources in your database using PHP classes. For that reason, you have no real need to use the `db()` function unless you want to quickly run a query without creating a model. Still, everything has been set up for you and Leaf DB will use the default database connection details in your `.env` file. +
+
+
+

+ Models are a more powerful way to interact with your database in Leaf MVC using an object-oriented approach, which also makes your code more readable and maintainable. +

+ +
+ +
+ +
+ +If you still want to use Leaf DB with Leaf MVC, everything has been set up for you and Leaf DB will use the default database connection details in your `.env` file. Here are a few example connections: diff --git a/src/docs/routing/index.md b/src/docs/routing/index.md index 81e10ada..0f4f231a 100644 --- a/src/docs/routing/index.md +++ b/src/docs/routing/index.md @@ -3,7 +3,8 @@ Routing is the foundation of every web application. It's the process of defining the URL structure of your application and how it responds to requests. Leaf comes with a powerful router that simplifies the way you define routes in your application. You can take routing as one fancy traffic officer that directs traffic to the right place. @@ -16,6 +17,39 @@ Routing is the foundation of every web application. It's the process of defining ## Create a route +
+
+
+

+ Using Leaf MVC? +

+

+ We've crafted a specialized guide for routing in Leaf MVC. While it's similar to the basic routing in Leaf, it's more detailed and tailored for Leaf MVC. +

+ +
+ +
+ +
+ Every route has a URL (the web address the user visits) and an HTTP method (like GET, POST, etc.), which tells the server what action to take. For example, if you create a route for a GET request to `/home`, the user can access that page by visiting `http://example.com/home`. This way, different URLs and methods control how users interact with your app. So to define a route, you need to specify the URL and the HTTP method. Leaf router allows you to do this using `get()`, `post()`, `put()`, `patch()`, `delete()`, ... methods. Let's take a look at them. diff --git a/src/docs/routing/middleware/index.md b/src/docs/routing/middleware/index.md index 29b8820b..6f9b3ea6 100644 --- a/src/docs/routing/middleware/index.md +++ b/src/docs/routing/middleware/index.md @@ -1,9 +1,49 @@ # Middleware + + + + Middleware is a piece of code that runs before or after your application processes a request. It helps control the flow of requests and responses. For example, when a user visits a page on your app, you can use middleware can check if the user is logged in and if everything is okay, the request moves on to the next step; if not, the middleware can redirect the user. Middleware can be used for a variety of tasks, such as authentication, authorization, logging, error handling, session management, and more. +
+
+
+

+ Using Leaf MVC? +

+

+ We've crafted a specialized guide for routing in Leaf MVC. While it's similar to the base middleware in Leaf, it's more detailed and tailored for Leaf MVC. +

+ +
+ +
+ +
+ ## Middleware in Leaf Leaf is a modular framework, so we don't throw everything into the core. However, some common middleware are built into respective modules like the Auth module which has middleware for authentication, the CORS module, and the CSRF module which come with their own implementation. diff --git a/src/docs/routing/mvc.md b/src/docs/routing/mvc.md new file mode 100644 index 00000000..05b5878f --- /dev/null +++ b/src/docs/routing/mvc.md @@ -0,0 +1,3 @@ +# Routing in Leaf MVC + + From 0247e1ef45490fe16cbcb476dffe06d7c128bf32 Mon Sep 17 00:00:00 2001 From: mychidarko Date: Wed, 12 Feb 2025 18:59:21 +0000 Subject: [PATCH 04/37] feat: add specialized MVC pages --- src/docs/mvc/mail.md | 1 + src/docs/utils/mail/index.md | 38 ++++++++++++++++++++++++++++++++++++ src/docs/utils/mail/mvc.md | 1 - 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/docs/mvc/mail.md delete mode 100644 src/docs/utils/mail/mvc.md diff --git a/src/docs/mvc/mail.md b/src/docs/mvc/mail.md new file mode 100644 index 00000000..726edc09 --- /dev/null +++ b/src/docs/mvc/mail.md @@ -0,0 +1 @@ +# Mailing with Leaf MVC diff --git a/src/docs/utils/mail/index.md b/src/docs/utils/mail/index.md index 3dc67271..623bf267 100644 --- a/src/docs/utils/mail/index.md +++ b/src/docs/utils/mail/index.md @@ -2,10 +2,48 @@ + + Mailing in PHP apps has always been seen as a daunting task. Leaf Mail provides a simple, straightforward and efficient email API that is built on the widely used [PHPMailer Library](https://github.com/PHPMailer/PHPMailer) component. With Leaf Mail, you can easily send emails using various drivers and services such as SMTP, Mailgun, SendGrid, Amazon SES, and sendmail. This flexibility enables you to swiftly begin sending emails through a preferred local or cloud-based service. +
+
+
+

+ Using Leaf MVC? +

+

+ We've crafted a specialized guide for routing in Leaf MVC. While it's similar to the mailing in Leaf, it's more detailed and tailored for Leaf MVC. +

+ +
+ +
+ +
+ ## Setting Up You can install leaf mail using the leaf cli: diff --git a/src/docs/utils/mail/mvc.md b/src/docs/utils/mail/mvc.md deleted file mode 100644 index 94128ff5..00000000 --- a/src/docs/utils/mail/mvc.md +++ /dev/null @@ -1 +0,0 @@ -# Leaf Mail From 79543e051ea5517785b4db774b018251355e7173 Mon Sep 17 00:00:00 2001 From: mychidarko Date: Wed, 12 Feb 2025 19:02:06 +0000 Subject: [PATCH 05/37] feat: remove latest version warning --- src/docs/utils/fs.md | 4 ---- src/docs/utils/testing.md | 4 ---- 2 files changed, 8 deletions(-) diff --git a/src/docs/utils/fs.md b/src/docs/utils/fs.md index 57acf402..edf8f26c 100644 --- a/src/docs/utils/fs.md +++ b/src/docs/utils/fs.md @@ -5,10 +5,6 @@ A file storage system is a system used to store and manage files. It's a crucial part of most applications, as it helps you create, read, update, store and delete files effectively. Leaf provides a simple and easy-to-use file storage system that allows you to work with files on your server or in the cloud. -::: warning Docs version -This documentation covers FS v2 and above. If you're using an older version, you can check the documentation [hosted here](https://v3.leafphp.dev/modules/fs/). -::: - ## Installation You can quickly install Leaf's file storage system through composer or the leaf cli. diff --git a/src/docs/utils/testing.md b/src/docs/utils/testing.md index 69ec4e2a..90fdb59b 100644 --- a/src/docs/utils/testing.md +++ b/src/docs/utils/testing.md @@ -4,10 +4,6 @@ Testing helps you and your team build Leaf apps faster by making sure that new f Since you might need to setup a project for rapid prototyping and deployment, we don't add any tests to the default Leaf installation. However, we have Alchemy, a user-friendly tool that simplifies your testing, code styling checks, and code coverage reports with a single command. -::: warning Docs version -This documentation covers Alchemy v2 and above. If you're using an older version, you can check the documentation [hosted here](https://v3.leafphp.dev/docs/tooling/testing.html). -::: - ## Setting up Leaf CLI will always ask if you want to add Alchemy to your project when you create a new project. If you already have a project and want to add Alchemy, you can do so by running the following command: From db29b5b778cff3668f7e4cd9ac505e33eca4fb28 Mon Sep 17 00:00:00 2001 From: mychidarko Date: Wed, 12 Feb 2025 19:10:26 +0000 Subject: [PATCH 06/37] fix: patch up broken links --- .vitepress/config/sidebar.ts | 4 +- src/docs/index.md | 4 +- src/learn/basic.md | 6 +- .../deployment/{fly.io => flyio}/index.md | 0 src/learn/deployment/index.md | 6 +- src/learn/deployment/sevalla/index.md | 160 ++++++++++++++++++ src/learn/mvc.md | 4 +- 7 files changed, 172 insertions(+), 12 deletions(-) rename src/learn/deployment/{fly.io => flyio}/index.md (100%) create mode 100644 src/learn/deployment/sevalla/index.md diff --git a/.vitepress/config/sidebar.ts b/.vitepress/config/sidebar.ts index e4b5f87f..1cdc56b7 100644 --- a/.vitepress/config/sidebar.ts +++ b/.vitepress/config/sidebar.ts @@ -173,8 +173,8 @@ const sidebar = [ // items: [ // { text: 'Intro', link: '/codelabs/' }, // { text: 'Contributing', link: '/codelabs/contributing' }, - // // { text: 'Deployment', link: '/codelabs/experiments/deployment/' }, - // // { text: 'Authentication', link: '/codelabs/experiments/auth/' }, + // // { text: 'Deployment', link: '/learn/deployment/' }, + // // { text: 'Authentication', link: '/learn/auth/' }, // ], // }, ]; diff --git a/src/docs/index.md b/src/docs/index.md index 4d21a9ee..5cc55f0d 100644 --- a/src/docs/index.md +++ b/src/docs/index.md @@ -38,11 +38,11 @@ Most PHP frameworks are complex, slow, and opinionated. Leaf is different—it's ## Creating a new app -Leaf is built to be incrementally adoptable: use it as a lightweight core for small to medium apps, or scale up with [Leaf MVC](/docs/mvc) for more structure in complex applications. No matter your stack, Leaf stays simple, fast, and developer-friendly—so you can build and ship with ease. +Leaf is built to be incrementally adoptable: use it as a lightweight core for small to medium apps, or scale up with [Leaf MVC](/docs/mvc/) for more structure in complex applications. No matter your stack, Leaf stays simple, fast, and developer-friendly—so you can build and ship with ease. ::: details Technical Requirements -Before you start with Leaf, verify that your system has PHP v7.4+, Composer (for package management) and [Leaf CLI](/docs/cli) (optional but recommended for easier app management) +Before you start with Leaf, verify that your system has PHP v7.4+, Composer (for package management) and [Leaf CLI](/docs/cli/) (optional but recommended for easier app management) ::: details Don't have PHP & Composer installed? diff --git a/src/learn/basic.md b/src/learn/basic.md index 1eb91c2d..711709e0 100644 --- a/src/learn/basic.md +++ b/src/learn/basic.md @@ -75,7 +75,7 @@ Once you are in your application root, you can run the app using the `serve` com php leaf serve ``` -Your app is now running! Open [http://localhost:5500](http://localhost:5500) in your browser. +Your app is now running! Open http://localhost:5500 in your browser. ## Building your first app @@ -162,7 +162,7 @@ Okay, looks like some magic is happening here. Let's break it down: - `app()` is a helper function that gives you access to the Leaf app instance, it is available from anywhere in your app. - `view()` is a method that you can use to create a route that renders a Blade view. The first argument is what the user enters in the URL, and the second argument is the name of the view file to render. -Notice we did not have to configure Blade because Leaf does that for you. You can now navigate to [http://localhost:5500/prelaunch](http://localhost:5500/prelaunch) to see your pre-launch page. +Notice we did not have to configure Blade because Leaf does that for you. You can now navigate to http://localhost:5500/prelaunch to see your pre-launch page. ### 2 Handling the form submission @@ -298,7 +298,7 @@ You can use the `withFlash()` method to send a message to the next request. This ### 5 Deploying your app -We have built a simple pre-launch page using Leaf. You can now deploy your app to a server using a service like [Heroku](/learn/deployment/heroku), [Fly.io](/learn/deployment/flyio) a VPS like [DigitalOcean](/learn/deployment/digitalocean), or even a shared hosting service like [Sevalla](/learn/deployment/sevalla). +We have built a simple pre-launch page using Leaf. You can now deploy your app to a server using a service like [Heroku](/learn/deployment/heroku/), [Fly.io](/learn/deployment/flyio/) a VPS like [DigitalOcean](/learn/deployment/digitalocean/), or even a shared hosting service like [Sevalla](/learn/deployment/sevalla/).
= 3.0 and PHP >=7.0. +::: + +## What Are We Building + +This experiment will guide you deploying your first LeafMVC application to Digital Ocean. A majority +of the same steps apply to Leaf v3 core as well. + +::: details (New to Digital Ocean?) +Digital Ocean is a cloud service provider that offers great introductory pricing for virtual private +servers (VPS). Create an account, tether a credit card, and prepare to build. +::: + +## Prerequisites + +Before continuing, it is important to determine if you would like to purhcase or point a domain name +to the VPS you are about to spin up. $DOMAIN will be shown several times throughout this experiment +and should be replaced by either your domain name (example.com) or the Droplet's public IP address. You +can grab the public IP address from the Digital Ocean control panel. + +For instructions on how to setup a domain with Digital Ocean, [click here](https://docs.digitalocean.com/products/networking/dns/how-to/add-domains/). + +## 1. Create a new droplet + +From the control panel, click the green "Create" button and select droplet. We will create a VPS with the +following options selected: + +* Ubuntu: 20.04 (LTS) +* Plan: Basic +* CPU Options: Premium AMD or Regular Intel +* $6/mo package + +::: tip Scaling ⚡️ +Should your application grow in requirements or traffic, you can always come back and increase your package selection. +::: + +### Authentication + +It is highly recommended that your utilize SSH-based authentication. Select an existing key, or [generate a new key](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent), then add it. + +## 2. Initial droplet setup + +After your droplet has been created, you will need to login, secure it, and install required software. The first task will +be to create an admin user, then utilie that account for future SSH connections. + +```bash +ssh root@$DOMAIN +adduser username +usermod -aG sudo username +rsync --archive --chown=username:username ~/.ssh /home/username +``` + +Test the admin account: ``su - username``. If the command executes, you can terminal the SSH session and log +back in with your new user account (recommended). + +### Setup firewall + +Next we will setup UFW - Ubuntu Firewall. We will allow communication on ports: 22 (SSH), 80 (HTTP), and 443 (SSL). + +```bash +sudo ufw allow 22 +sudo ufw allow 80 +sudo ufw allow 443 +sudo ufw enable +``` + +After creating the firewall's rules and enabling UFW, you can view firewall status by ``sudo ufw status``. + +### Install required software + +It is now time install all of the needed software to enable LeafPHP to run. First, we need to update all system software: + +```bash +sudo apt update +sudo apt upgrade +``` + +Be sure to respond **Y** when asked to continue. Now we can intall NGINX, PHP, MySQL, and curl. + +```bash +sudo apt install nginx php-fpm php-mysql php-curl +``` + +Once complete, follow the +[NGINX instructions](https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-ubuntu-18-04#step-3-%E2%80%93-installing-php-and-configuring-nginx-to-use-the-php-processor). +Ensure that your directory is set as such: `` root /var/www/$DOMAIN/public;`` Below is an example sites-available file. + +```nginx +server { + server_name itsglint.com www.itsglint.com 147.182.136.153; + root /var/www/itsglint.com/public; + + index index.html index.htm index.php; + + location / { + try_files $uri /index.php?$query_string; + } + + location ~ \.php$ { + include snippets/fastcgi-php.conf; + fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; + } +} +``` + +::: warning Leaf Router and .htaccess support +It is important to mirror the location blocks as-in. Otherwise, LeafRouter will not work properly or at all. +::: + +Next, we will install Mysql. Follow the [install instructions](https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-ubuntu-18-04#step-2-%E2%80%93-installing-mysql-to-manage-site-data). + +```bash +sudo apt install mysql-server +``` + +### Install your Leaf🍁 application + +Next, we will download and install our application with all dependencies. Clone your repository from Github, +or any source, and place your Leaf project in ``/var/www/$DOMAIN``. Afterwards, install required dependencies +and perform initial Leaf tasks: + +```bash +composer install +php leaf db:install +php leaf db:migrate +``` + +You also may seed the database if required: `php leaf db:seed`. + +Congratulations 🎉, you now have a fully working production server, and should be able to reach your application at $DOMAIN. + +::: details Recommended: Complete SSL Setup +If your Leaf applications is more than a hobbyist adventure and serving actual clients or visitors, it is +strongly recommended to complete the SSL setup. SSL encrypts traffic between a browser and server. Replace +example.com with $DOMAIN. + +``` +sudo apt install certbot python3-certbot-nginx +sudo systemctl reload nginx +sudo certbot --nginx -d example.com -d www.example.com +``` + +When prompted for HTTPS redirction, select **Option 2**, forcing HTTPS traffic. + +::: + +
+ +Experiment by **[Matthew Reichardt](https://github.com/matthewjamesr)** diff --git a/src/learn/mvc.md b/src/learn/mvc.md index 4f1f8f97..c179229a 100644 --- a/src/learn/mvc.md +++ b/src/learn/mvc.md @@ -54,7 +54,7 @@ cd my-app php leaf serve ``` -Your app is now running! Open [http://localhost:5500](http://localhost:5500) in your browser. +Your app is now running! Open http://localhost:5500 in your browser. ## Project Structure @@ -257,7 +257,7 @@ You can use the `withFlash()` method to send a message to the next request. This ### 5 Deploying your app -We have built a simple pre-launch page using Leaf MVC. You can now deploy your app to a server using a service like [Heroku](/learn/deployment/heroku), [Fly.io](/learn/deployment/flyio) a VPS like [DigitalOcean](/learn/deployment/digitalocean), or even a shared hosting service like [Sevalla](/learn/deployment/sevalla). +We have built a simple pre-launch page using Leaf MVC. You can now deploy your app to a server using a service like [Heroku](/learn/deployment/heroku/), [Fly.io](/learn/deployment/flyio/) a VPS like [DigitalOcean](/learn/deployment/digitalocean/), or even a shared hosting service like [Sevalla](/learn/deployment/sevalla/).
Date: Wed, 12 Feb 2025 19:22:02 +0000 Subject: [PATCH 07/37] fix: patch up broken links --- src/learn/basic.md | 2 +- src/learn/mvc.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/learn/basic.md b/src/learn/basic.md index 711709e0..aebd4954 100644 --- a/src/learn/basic.md +++ b/src/learn/basic.md @@ -75,7 +75,7 @@ Once you are in your application root, you can run the app using the `serve` com php leaf serve ``` -Your app is now running! Open http://localhost:5500 in your browser. +Your app is now running! Open `http://localhost:5500` in your browser. ## Building your first app diff --git a/src/learn/mvc.md b/src/learn/mvc.md index c179229a..9cb03568 100644 --- a/src/learn/mvc.md +++ b/src/learn/mvc.md @@ -54,7 +54,7 @@ cd my-app php leaf serve ``` -Your app is now running! Open http://localhost:5500 in your browser. +Your app is now running! Open `http://localhost:5500` in your browser. ## Project Structure From 4ee9cc12c2c497d9c43ad076f9ac834938e06643 Mon Sep 17 00:00:00 2001 From: mychidarko Date: Wed, 12 Feb 2025 19:23:47 +0000 Subject: [PATCH 08/37] fix: patch up broken links --- src/learn/basic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/learn/basic.md b/src/learn/basic.md index aebd4954..9a3b5e59 100644 --- a/src/learn/basic.md +++ b/src/learn/basic.md @@ -162,7 +162,7 @@ Okay, looks like some magic is happening here. Let's break it down: - `app()` is a helper function that gives you access to the Leaf app instance, it is available from anywhere in your app. - `view()` is a method that you can use to create a route that renders a Blade view. The first argument is what the user enters in the URL, and the second argument is the name of the view file to render. -Notice we did not have to configure Blade because Leaf does that for you. You can now navigate to http://localhost:5500/prelaunch to see your pre-launch page. +Notice we did not have to configure Blade because Leaf does that for you. You can now navigate to `http://localhost:5500/prelaunch` to see your pre-launch page. ### 2 Handling the form submission From 370af2f624dbbfd6e393c8fffe2e8ae59636353a Mon Sep 17 00:00:00 2001 From: mychidarko Date: Thu, 13 Feb 2025 13:53:06 +0000 Subject: [PATCH 09/37] feat: update learn section --- .vitepress/config.mts | 4 + .../theme/components/Home/Testimonials.vue | 7 +- .../theme/components/shared/Paperplane.vue | 4 +- .../theme/components/shared/SidebarLinks.vue | 2 +- .../components/shared/TutorialNumber.vue | 10 + .vitepress/theme/styles/index.css | 18 + src/learn/api.md | 442 ++++++++++++++++++ src/learn/basic.md | 434 ++++++++--------- src/learn/index.md | 10 +- src/learn/mvc.md | 440 ++++++++--------- 10 files changed, 930 insertions(+), 441 deletions(-) create mode 100644 .vitepress/theme/components/shared/TutorialNumber.vue diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 2dd7f753..5f863337 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -75,6 +75,10 @@ export default defineConfig({ markdown: { lineNumbers: true, + theme: { + light: 'one-dark-pro', + dark: 'one-dark-pro', + }, config(md) { md.use(groupIconMdPlugin); }, diff --git a/.vitepress/theme/components/Home/Testimonials.vue b/.vitepress/theme/components/Home/Testimonials.vue index 0c0e1d4a..78dee056 100644 --- a/.vitepress/theme/components/Home/Testimonials.vue +++ b/.vitepress/theme/components/Home/Testimonials.vue @@ -1,5 +1,6 @@