From 4c2a32222be3dfc5e5acdb6786f7e8c2d8735dc8 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Fri, 1 Dec 2023 12:49:19 +0100 Subject: [PATCH] Initial commit --- .env.example | 8 + .gitattributes | 13 + .github/workflows/run-tests.yml | 75 ++++ .gitignore | 12 + LICENSE.md | 21 + README.md | 106 +++++ SECURITY.md | 19 + _docs/.gitkeep | 0 _media/app.css | 2 + _pages/404.blade.php | 51 +++ _pages/index.blade.php | 86 ++++ _posts/.gitkeep | 0 app/Providers/AppServiceProvider.php | 24 + app/bootstrap.php | 73 ++++ app/config.php | 109 +++++ app/storage/app/.gitignore | 2 + app/storage/framework/cache/.gitignore | 3 + app/storage/framework/cache/data/.gitignore | 2 + app/storage/framework/views/.gitignore | 2 + composer.json | 63 +++ config/docs.php | 135 ++++++ config/hyde.php | 458 ++++++++++++++++++++ config/markdown.php | 98 +++++ hyde | 53 +++ package.json | 41 ++ resources/assets/app.css | 20 + resources/assets/app.js | 3 + resources/views/.gitkeep | 1 + tailwind.config.js | 97 +++++ tests/DefaultContentTest.php | 59 +++ tests/ExampleTest.php | 15 + tests/HydeCLITest.php | 17 + tests/StaticSiteBuilderTest.php | 23 + webpack.mix.js | 12 + 34 files changed, 1703 insertions(+) create mode 100644 .env.example create mode 100644 .gitattributes create mode 100644 .github/workflows/run-tests.yml create mode 100644 .gitignore create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 SECURITY.md create mode 100644 _docs/.gitkeep create mode 100644 _media/app.css create mode 100644 _pages/404.blade.php create mode 100644 _pages/index.blade.php create mode 100644 _posts/.gitkeep create mode 100644 app/Providers/AppServiceProvider.php create mode 100644 app/bootstrap.php create mode 100644 app/config.php create mode 100644 app/storage/app/.gitignore create mode 100644 app/storage/framework/cache/.gitignore create mode 100644 app/storage/framework/cache/data/.gitignore create mode 100644 app/storage/framework/views/.gitignore create mode 100644 composer.json create mode 100644 config/docs.php create mode 100644 config/hyde.php create mode 100644 config/markdown.php create mode 100644 hyde create mode 100644 package.json create mode 100644 resources/assets/app.css create mode 100644 resources/assets/app.js create mode 100644 resources/views/.gitkeep create mode 100644 tailwind.config.js create mode 100644 tests/DefaultContentTest.php create mode 100644 tests/ExampleTest.php create mode 100644 tests/HydeCLITest.php create mode 100644 tests/StaticSiteBuilderTest.php create mode 100644 webpack.mix.js diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..93d0480f --- /dev/null +++ b/.env.example @@ -0,0 +1,8 @@ +# If you have a domain, you should set the URL here so that permalinks can be generated. +# SITE_URL=https://example.org + +# If you want to use Torchlight.dev, enter your API token here to automatically enable it +# TORCHLIGHT_TOKEN=torch_ + +# If you want to change the default port of the realtime compiler server, you can do so here +# SERVER_PORT=8080 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..212241dd --- /dev/null +++ b/.gitattributes @@ -0,0 +1,13 @@ +* text eol=lf + +*.blade.php diff=html +*.css diff=css +*.html diff=html +*.md diff=markdown +*.php diff=php + +.github/ export-ignore +tests/ export-ignore +CHANGELOG.md export-ignore +LICENSE.md export-ignore +SECURITY.md export-ignore diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml new file mode 100644 index 00000000..984fdd79 --- /dev/null +++ b/.github/workflows/run-tests.yml @@ -0,0 +1,75 @@ +# README: This workflow runs some tests intended to make sure that the package works smoothly. +# This file is not included when you install Hyde normally, however it is present +# when you create a project by using the GitHub template. Unless you want to +# contribute to the project, there's no reason for you to keep this file. + +name: Hyde Tests + +on: + push: + branches: [ "master", "develop" ] + pull_request: + branches: [ "master", "develop" ] + +jobs: + hyde-tests-master: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + dependency-version: [composer, latest] + + runs-on: ${{ matrix.os }} + name: Hyde tests - ${{ matrix.os }} (${{ matrix.dependency-version }}) + if: github.ref == 'refs/heads/master' && github.event.repository.full_name == 'hydephp/hyde' + + steps: + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + extensions: fileinfo + + - uses: actions/checkout@v3 + + - name: Require latest version + if: matrix.dependency-version == 'latest' + run: composer require hyde/framework:master hyde/testing + + - name: Install additional dependencies + if: matrix.dependency-version != 'latest' + run: composer require hyde/testing --dev + + - name: Download test runner configuration + run: curl https://raw.githubusercontent.com/hydephp/develop/master/packages/hyde/phpunit.xml.dist -o phpunit.xml.dist + + - name: Execute tests (Unit and Feature tests) via Pest + run: vendor/bin/pest + + + hyde-tests-develop: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + php: [8.1, 8.2] + + runs-on: ${{ matrix.os }} + name: Hyde tests - ${{ matrix.os }} ${{ matrix.php }} (develop) + if: github.ref == 'refs/heads/develop' && github.event.repository.full_name == 'hydephp/hyde' + + steps: + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: fileinfo, zip + + - uses: actions/checkout@v3 + + - name: Require latest development version + run: composer require hyde/framework:dev-develop hyde/testing:dev-master + + - name: Download test runner configuration + run: curl https://raw.githubusercontent.com/hydephp/develop/master/packages/hyde/phpunit.xml.dist -o phpunit.xml.dist + + - name: Execute tests (Unit and Feature tests) via Pest + run: vendor/bin/pest diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..398efa34 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +/vendor +/node_modules +/builds +/.idea +/.vscode +/.vagrant +/.cache +.phpunit.result.cache + +.env + +# /_site diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..761daf50 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Caen De Silva + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..b7ab7b63 --- /dev/null +++ b/README.md @@ -0,0 +1,106 @@ +# HydePHP - Elegant and Powerful Static Site Generator + +[![Latest Version on Packagist](https://img.shields.io/packagist/v/hyde/framework?include_prereleases)](https://packagist.org/packages/hyde/framework) +[![Total Downloads on Packagist](https://img.shields.io/packagist/dt/hyde/framework)](https://packagist.org/packages/hyde/framework) +[![License MIT](https://img.shields.io/github/license/hydephp/hyde) ](https://github.com/hydephp/hyde/blob/master/LICENSE.md) +[![Test Coverage](https://codecov.io/gh/hydephp/develop/branch/master/graph/badge.svg?token=G6N2161TOT)](https://codecov.io/gh/hydephp/develop) +[![Test Suite](https://github.com/hydephp/develop/actions/workflows/continuous-integration.yml/badge.svg)](https://github.com/hydephp/develop/actions/workflows/continuous-integration.yml) + + +## Make static websites, blogs, and documentation pages with the tools you already know and love. + +### About HydePHP + +HydePHP is a content-first Laravel-powered console application that allows you to create static HTML pages, blog posts, and documentation sites, +using your choice of Markdown and/or Blade. + +Build sites in record-time with a full batteries-included TailwindCSS frontend that just works without any fuzz. + +### Speed & simplicity first, full control when you need it. + +Hyde is all about letting you get started quickly by giving you a full-featured frontend starter kit, while also giving you the power and freedom of doing things the way you want to. + +Markdown purist? That's all you need. Blade artisan? Go for it. +Hyde comes with hand-crafted frontend templates, so you can focus on your content. +You don't _need_ to customize anything. But you _can_ customize everything. + +See the documentation and learn more at https://hydephp.com/docs + + +## Features + +### Content Creation + +- Create blog posts using Markdown and Front Matter. +- Create documentation pages from plain Markdown, no front matter needed! +- Create simple pages using Markdown, or create advanced ones using Laravel Blade. +- You can scaffold blog posts and Markdown pages to automatically fill in the front matter. +- You can also scaffold Blade pages to automatically use the default layout. + +### Built-in Frontend + +- Hyde comes with a TailwindCSS starter kit, so you can start making content right away. +- The starter kit is fully responsive, has a dark mode theme, and is customizable. +- The frontend is accessible to screen-readers and rich with semantic HTML and microdata. +- Hyde automatically chooses the right layout to use depending on the content being rendered. +- Hyde also fills in and creates content like navigation menus and sidebars automatically. + +### Easy Asset Managing + +- The Hyde starter comes with [HydeFront](https://github.com/hydephp/hydefront) to serve the base stylesheet and JavaScript through the jsDelivr CDN. +- Hyde ships with precompiled and minified TailwindCSS styles in the app.css file, you can also load them through the CDN. +- This means that all the styles you need are already installed. However, if you want to customize the included Tailwind config, or if you add new Tailwind classes through Blade files, you can simply run the `npm run dev` command to recompile the styles using the pre-configured Laravel Mix package. + +### Customization + +- You don't need to configure anything as Hyde is shipped with sensible defaults. +- You can, however, customize nearly everything. Here are just a few out of many examples: +- All frontend components and page layouts are created with Blade, so you + can publish the vendor views, just like in Laravel. +- Override many of the dynamic content features like the menus and footer. + + +## Getting Started - High-level overview + +> See [Installation Guide](https://hydephp.com/docs/1.x/installation) and [Getting Started](https://hydephp.com/docs/1.x/getting-started) for the full details. + +It's a breeze to get started with Hyde. Create a new Hyde project using Composer: + +```bash +composer create-project hyde/hyde +``` + +Next, place your Markdown files in one of the content directories: `_posts`, `_docs`, and `_pages` which also accepts Blade files. You can also use the `hyde:make` commands to scaffold them. + +When you're ready, run the build command to compile your static site which will save your HTML files in the `_site` directory. + +```bash +php hyde build +``` + + +## Resources + +### Changelog + +Please see [CHANGELOG](https://github.com/hydephp/develop/blob/master/CHANGELOG.md) for more information on what has changed recently. + +### Contributing + +HydePHP is an open-source project, contributions are very welcome! + +Development is made in the HydePHP Monorepo, which you can find here https://github.com/hydephp/develop. + +### Security + +If you discover any security-related issues, please email caen@desilva.se instead of using the issue tracker. +All vulnerabilities will be promptly addressed. + +### Credits + +- [Caen De Silva](https://github.com/caendesilva), feel free to buy me a coffee! https://www.buymeacoffee.com/caen +- [All Contributors](../../contributors) + +### License + +The MIT License (MIT). Please see [License File](LICENSE.md) for more information. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..377c11c4 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +## Supported Versions + +These are the version ranges of HydePHP, and their support status. We follow [Semantic Versioning](https://semver.org), and you can read about our [Backwards Compatability](https://github.com/hydephp/policies/blob/master/backwards-compatability.md) promise here. + +| Version | Supported | Classification | +|---------|--------------------|----------------------| +| 1.x | :white_check_mark: | General Availability | +| < 0.64 | :x: | Beta (legacy) | +| < 0.8 | :x: | Alpha stage | + + +## Reporting a Vulnerability + +If you discover a security vulnerability within this package, please send an e-mail to the creator, Caen De Silva, via caen@desilva.se. +You can also report a vulnerability through GitHub on the [Security Advisory](https://github.com/hydephp/develop/security/advisories) page. + +All security vulnerabilities will be promptly addressed. diff --git a/_docs/.gitkeep b/_docs/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/_media/app.css b/_media/app.css new file mode 100644 index 00000000..beeb9872 --- /dev/null +++ b/_media/app.css @@ -0,0 +1,2 @@ +/*! HydeFront v3.3.0 | MIT License | https://hydephp.com*/.hyde-search-context{margin-bottom:10px}.hyde-search-results{margin-top:1.25em;max-height:60vh;overflow-y:auto}#search-status{margin-top:0}#sidebar-toggle{display:inline-block;height:2rem;position:relative;width:2rem}#sidebar-toggle span.icon-bar{background-color:#000;display:block;height:2.375px;left:5.5px;position:absolute;transition:all .3s ease-out;width:20px}#sidebar-toggle span.icon-bar:first-child{top:9px}#sidebar-toggle span.icon-bar:nth-child(2),#sidebar-toggle span.icon-bar:nth-child(3){top:15px;transform-origin:center}#sidebar-toggle span.icon-bar:last-child{top:21px}#sidebar-toggle.active span.icon-bar:first-child{opacity:0}#sidebar-toggle.active span.icon-bar:nth-child(2){transform:rotate(45deg)}#sidebar-toggle.active span.icon-bar:nth-child(3){transform:rotate(-45deg)}#sidebar-toggle.active span.icon-bar:last-child{opacity:0}.dark #sidebar-toggle span.icon-bar{background-color:#fff;height:2px}.table-of-contents{padding-bottom:.75rem}.table-of-contents>li{margin-bottom:.35rem;margin-top:.15rem}.table-of-contents ul{padding-left:.5rem}.table-of-contents a{display:block;margin-left:-2rem;opacity:.825;padding-left:2rem}.table-of-contents a:before{content:"#";font-size:75%;margin-right:4px;opacity:.5;transition:opacity .3s ease-in-out}.table-of-contents a:hover{background-color:hsla(0,0%,50%,.2);opacity:1;transition:opacity,background .3s ease-in-out}.table-of-contents a:hover:before{opacity:1}#hyde-docs .prose h1,#hyde-docs .prose h2,#hyde-docs .prose h3,#hyde-docs .prose h4,#hyde-docs .prose h5,#hyde-docs .prose h6{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}#hyde-docs .prose h1:focus .heading-permalink,#hyde-docs .prose h1:hover .heading-permalink,#hyde-docs .prose h2:focus .heading-permalink,#hyde-docs .prose h2:hover .heading-permalink,#hyde-docs .prose h3:focus .heading-permalink,#hyde-docs .prose h3:hover .heading-permalink,#hyde-docs .prose h4:focus .heading-permalink,#hyde-docs .prose h4:hover .heading-permalink,#hyde-docs .prose h5:focus .heading-permalink,#hyde-docs .prose h5:hover .heading-permalink,#hyde-docs .prose h6:focus .heading-permalink,#hyde-docs .prose h6:hover .heading-permalink{filter:grayscale(100%);opacity:.75;transition:opacity .1s ease-out}#hyde-docs .prose h1 .heading-permalink,#hyde-docs .prose h2 .heading-permalink,#hyde-docs .prose h3 .heading-permalink,#hyde-docs .prose h4 .heading-permalink,#hyde-docs .prose h5 .heading-permalink,#hyde-docs .prose h6 .heading-permalink{margin-left:.25rem;opacity:0;padding:0 .25rem;scroll-margin:1rem;transition:opacity .3s ease}#hyde-docs .prose h1 .heading-permalink:focus,#hyde-docs .prose h1 .heading-permalink:hover,#hyde-docs .prose h2 .heading-permalink:focus,#hyde-docs .prose h2 .heading-permalink:hover,#hyde-docs .prose h3 .heading-permalink:focus,#hyde-docs .prose h3 .heading-permalink:hover,#hyde-docs .prose h4 .heading-permalink:focus,#hyde-docs .prose h4 .heading-permalink:hover,#hyde-docs .prose h5 .heading-permalink:focus,#hyde-docs .prose h5 .heading-permalink:hover,#hyde-docs .prose h6 .heading-permalink:focus,#hyde-docs .prose h6 .heading-permalink:hover{filter:unset;opacity:1}html{scroll-behavior:smooth}.torchlight-enabled pre{border-radius:.25rem;margin-bottom:1rem;margin-top:1rem;overflow-x:auto;padding:0}.torchlight-enabled pre code.torchlight{display:block;min-width:-webkit-max-content;min-width:-moz-max-content;min-width:max-content;padding-bottom:1rem;padding-top:1rem}.torchlight-enabled pre code.torchlight .line{padding-left:1rem;padding-right:1rem}.torchlight-enabled pre code.torchlight .line-number,.torchlight-enabled pre code.torchlight .summary-caret{margin-right:1rem}.prose blockquote.info{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity))}.prose blockquote.success{--tw-border-opacity:1;border-color:rgb(34 197 94/var(--tw-border-opacity))}.prose blockquote.warning{--tw-border-opacity:1;border-color:rgb(245 158 11/var(--tw-border-opacity))}.prose blockquote.danger{--tw-border-opacity:1;border-color:rgb(220 38 38/var(--tw-border-opacity))}code{display:inline-block;max-width:80vw;overflow-x:auto;vertical-align:top;word-break:break-all}pre code{display:block;max-width:unset}pre>code>.filepath{float:right;opacity:.5;position:relative;right:.25rem;top:-.25rem;transition:opacity .25s}pre>code>.filepath:hover{opacity:1}.torchlight-enabled pre>code>.filepath{right:1rem}@media screen and (max-width:767px){pre>code>.filepath{display:none}}@media screen and (min-width:768px){[x-cloak].x-uncloak-md{display:revert!important}} +/*! tailwindcss v3.1.0 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}html{-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}::-webkit-backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.prose{color:var(--tw-prose-body);max-width:96ch}.prose :where([class~=lead]):not(:where([class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-bottom:1.2em;margin-top:1.2em}.prose :where(a):not(:where([class~=not-prose] *)){color:#5956eb;font-weight:500;text-decoration:none}.prose :where(a):not(:where([class~=not-prose] *)):hover{color:#4f46e5}.prose :where(strong):not(:where([class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(ol):not(:where([class~=not-prose] *)){list-style-type:decimal;padding-left:1.625em}.prose :where(ol[type=A]):not(:where([class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=A s]):not(:where([class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a s]):not(:where([class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=I s]):not(:where([class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i s]):not(:where([class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose] *)){list-style-type:disc;padding-left:1.625em}.prose :where(ol>li):not(:where([class~=not-prose] *))::marker{color:var(--tw-prose-counters);font-weight:400}.prose :where(ul>li):not(:where([class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(hr):not(:where([class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-bottom:3em;margin-top:3em}.prose :where(blockquote):not(:where([class~=not-prose] *)){background-color:#80808020;border-left-color:#d1d5db;border-left-width:.25rem;color:unset;font-style:unset;font-weight:500;line-height:1.25em;margin-bottom:1em;margin-top:1em;padding-bottom:.25em;padding-left:.75em;padding-top:.25em;quotes:"\201C""\201D""\2018""\2019"}.prose :where(blockquote):not(:where([class~=not-prose] *)) p{margin-bottom:.25em;margin-top:.25em;padding-right:.25em}.prose :where(blockquote):not(:where([class~=not-prose] *)) p:before{content:unset}.prose :where(blockquote):not(:where([class~=not-prose] *)) p:after{content:unset}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose] *)){color:var(--tw-prose-headings);font-size:2.25em;font-weight:800;line-height:1.1111111;margin-bottom:.8888889em;margin-top:0}.prose :where(h1 strong):not(:where([class~=not-prose] *)){font-weight:900}.prose :where(h2):not(:where([class~=not-prose] *)){color:var(--tw-prose-headings);font-size:1.5em;font-weight:700;line-height:1.3333333;margin-bottom:.75em;margin-top:1.5em}.prose :where(h2 strong):not(:where([class~=not-prose] *)){font-weight:800}.prose :where(h3):not(:where([class~=not-prose] *)){color:var(--tw-prose-headings);font-size:1.25em;font-weight:600;line-height:1.6;margin-bottom:.6em;margin-top:1.6em}.prose :where(h3 strong):not(:where([class~=not-prose] *)){font-weight:700}.prose :where(h4):not(:where([class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;line-height:1.5;margin-bottom:.5em;margin-top:1.5em}.prose :where(h4 strong):not(:where([class~=not-prose] *)){font-weight:700}.prose :where(figure>*):not(:where([class~=not-prose] *)){margin-bottom:0;margin-top:0}.prose :where(figcaption):not(:where([class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose :where(code):not(:where([class~=not-prose] *)){background-color:#80808033;border-radius:4px;color:var(--tw-prose-code);font-size:.875em;font-weight:600;font:unset;margin-left:-2px;margin-right:1px;padding-left:4px;padding-right:4px}.prose :where(code):not(:where([class~=not-prose] *)):before{content:unset}.prose :where(code):not(:where([class~=not-prose] *)):after{content:unset}.prose :where(a code):not(:where([class~=not-prose] *)){color:var(--tw-prose-links)}.prose :where(pre):not(:where([class~=not-prose] *)){background-color:var(--tw-prose-pre-bg);border-radius:.375rem;color:var(--tw-prose-pre-code);font-size:.875em;font-weight:400;line-height:1.7142857;margin-bottom:1.7142857em;margin-top:1.7142857em;overflow-x:auto;padding:.8571429em 1.1428571em}.prose :where(pre):not(:where([class~=not-prose] *)) code{font-family:Fira Code Regular,Consolas,Monospace,Courier New}.prose :where(pre code):not(:where([class~=not-prose] *)){background-color:transparent;border-radius:0;border-width:0;color:inherit;font-family:inherit;font-size:inherit;font-weight:inherit;line-height:inherit;padding:0}.prose :where(pre code):not(:where([class~=not-prose] *)):before{content:none}.prose :where(pre code):not(:where([class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose] *)){font-size:.875em;line-height:1.7142857;margin-bottom:2em;margin-top:2em;table-layout:auto;text-align:left;width:100%}.prose :where(thead):not(:where([class~=not-prose] *)){border-bottom-color:var(--tw-prose-th-borders);border-bottom-width:1px}.prose :where(thead th):not(:where([class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;padding-bottom:.5714286em;padding-left:.5714286em;padding-right:.5714286em;vertical-align:bottom}.prose :where(tbody tr):not(:where([class~=not-prose] *)){border-bottom-color:var(--tw-prose-td-borders);border-bottom-width:1px}.prose :where(tbody tr:last-child):not(:where([class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose] *)){padding:.5714286em;vertical-align:baseline}.prose{--tw-prose-body:#374151;--tw-prose-headings:#111827;--tw-prose-lead:#4b5563;--tw-prose-links:#111827;--tw-prose-bold:#111827;--tw-prose-counters:#6b7280;--tw-prose-bullets:#d1d5db;--tw-prose-hr:#e5e7eb;--tw-prose-quotes:#111827;--tw-prose-quote-borders:#e5e7eb;--tw-prose-captions:#6b7280;--tw-prose-code:#111827;--tw-prose-pre-code:#e5e7eb;--tw-prose-pre-bg:#1f2937;--tw-prose-th-borders:#d1d5db;--tw-prose-td-borders:#e5e7eb;--tw-prose-invert-body:#d1d5db;--tw-prose-invert-headings:#fff;--tw-prose-invert-lead:#9ca3af;--tw-prose-invert-links:#fff;--tw-prose-invert-bold:#fff;--tw-prose-invert-counters:#9ca3af;--tw-prose-invert-bullets:#4b5563;--tw-prose-invert-hr:#374151;--tw-prose-invert-quotes:#f3f4f6;--tw-prose-invert-quote-borders:#374151;--tw-prose-invert-captions:#9ca3af;--tw-prose-invert-code:#fff;--tw-prose-invert-pre-code:#d1d5db;--tw-prose-invert-pre-bg:rgba(0,0,0,.5);--tw-prose-invert-th-borders:#4b5563;--tw-prose-invert-td-borders:#374151;font-size:1rem;line-height:1.5em}.prose :where(p):not(:where([class~=not-prose] *)){margin-bottom:1.25em;margin-top:1.25em}.prose :where(img):not(:where([class~=not-prose] *)){margin-bottom:2em;margin-top:2em}.prose :where(video):not(:where([class~=not-prose] *)){margin-bottom:2em;margin-top:2em}.prose :where(figure):not(:where([class~=not-prose] *)){margin-bottom:2em;margin-top:2em}.prose :where(h2 code):not(:where([class~=not-prose] *)){font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose] *)){font-size:.9em}.prose :where(li):not(:where([class~=not-prose] *)){margin-bottom:.5em;margin-top:.5em}.prose :where(ol>li):not(:where([class~=not-prose] *)){padding-left:.375em}.prose :where(ul>li):not(:where([class~=not-prose] *)){padding-left:.375em}.prose>:where(ul>li p):not(:where([class~=not-prose] *)){margin-bottom:.75em;margin-top:.75em}.prose>:where(ul>li>:first-child):not(:where([class~=not-prose] *)){margin-top:1.25em}.prose>:where(ul>li>:last-child):not(:where([class~=not-prose] *)){margin-bottom:1.25em}.prose>:where(ol>li>:first-child):not(:where([class~=not-prose] *)){margin-top:1.25em}.prose>:where(ol>li>:last-child):not(:where([class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose] *)){margin-bottom:.75em;margin-top:.75em}.prose :where(hr+*):not(:where([class~=not-prose] *)){margin-top:0}.prose :where(h2+*):not(:where([class~=not-prose] *)){margin-top:0}.prose :where(h3+*):not(:where([class~=not-prose] *)){margin-top:0}.prose :where(h4+*):not(:where([class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose] *)){padding-left:0}.prose :where(thead th:last-child):not(:where([class~=not-prose] *)){padding-right:0}.prose :where(tbody td:first-child):not(:where([class~=not-prose] *)){padding-left:0}.prose :where(tbody td:last-child):not(:where([class~=not-prose] *)){padding-right:0}.prose>:where(:first-child):not(:where([class~=not-prose] *)){margin-top:0}.prose>:where(:last-child):not(:where([class~=not-prose] *)){margin-bottom:0}.sr-only{clip:rect(0,0,0,0);border-width:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.visible{visibility:visible}.invisible{visibility:hidden}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.left-80{left:20rem}.left-0{left:0}.right-0{right:0}.top-auto{top:auto}.top-16{top:4rem}.top-0{top:0}.right-4{right:1rem}.top-4{top:1rem}.bottom-4{bottom:1rem}.-left-64{left:-16rem}.bottom-0{bottom:0}.z-50{z-index:50}.z-40{z-index:40}.z-10{z-index:10}.z-30{z-index:30}.float-right{float:right}.float-left{float:left}.m-8{margin:2rem}.m-2{margin:.5rem}.mx-auto{margin-left:auto;margin-right:auto}.my-0{margin-bottom:0;margin-top:0}.my-4{margin-bottom:1rem;margin-top:1rem}.my-8{margin-bottom:2rem;margin-top:2rem}.mx-0{margin-left:0;margin-right:0}.mx-4{margin-left:1rem;margin-right:1rem}.mx-8{margin-left:2rem;margin-right:2rem}.my-3{margin-bottom:.75rem;margin-top:.75rem}.my-auto{margin-bottom:auto;margin-top:auto}.mx-3{margin-left:.75rem;margin-right:.75rem}.my-1{margin-bottom:.25rem;margin-top:.25rem}.-mx-4{margin-left:-1rem;margin-right:-1rem}.my-2{margin-bottom:.5rem;margin-top:.5rem}.ml-auto{margin-left:auto}.mr-auto{margin-right:auto}.mb-8{margin-bottom:2rem}.mb-4{margin-bottom:1rem}.mt-2{margin-top:.5rem}.mt-8{margin-top:2rem}.mt-4{margin-top:1rem}.mt-auto{margin-top:auto}.mt-3{margin-top:.75rem}.mr-1{margin-right:.25rem}.mr-4{margin-right:1rem}.mb-3{margin-bottom:.75rem}.-mb-2{margin-bottom:-.5rem}.-ml-4{margin-left:-1rem}.-ml-8{margin-left:-2rem}.-ml-2{margin-left:-.5rem}.ml-4{margin-left:1rem}.mb-2{margin-bottom:.5rem}.-ml-6{margin-left:-1.5rem}.mb-0{margin-bottom:0}.block{display:block}.inline{display:inline}.flex{display:flex}.contents{display:contents}.hidden{display:none}.h-1{height:.25rem}.h-auto{height:auto}.h-6{height:1.5rem}.h-16{height:4rem}.h-8{height:2rem}.h-screen{height:100vh}.h-0{height:0}.h-full{height:100%}.h-5{height:1.25rem}.max-h-\[75vh\]{max-height:75vh}.min-h-screen{min-height:100vh}.min-h-\[calc\(100vh_-_4rem\)\]{min-height:calc(100vh - 4rem)}.min-h-\[300px\]{min-height:300px}.w-1{width:.25rem}.w-full{width:100%}.w-16{width:4rem}.w-fit{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}.w-screen{width:100vw}.w-6{width:1.5rem}.w-\[70ch\]{width:70ch}.w-64{width:16rem}.w-5{width:1.25rem}.max-w-sm{max-width:24rem}.max-w-7xl{max-width:80rem}.max-w-lg{max-width:32rem}.max-w-3xl{max-width:48rem}.max-w-xs{max-width:20rem}.max-w-\[1000px\]{max-width:1000px}.max-w-full{max-width:100%}.flex-shrink-0{flex-shrink:0}.flex-grow{flex-grow:1}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-auto{cursor:auto}.cursor-pointer{cursor:pointer}.list-none{list-style-type:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.overflow-auto{overflow:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-hidden{overflow-y:hidden}.whitespace-nowrap{white-space:nowrap}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.border-2{border-width:2px}.border-4{border-width:4px}.border-y{border-bottom-width:1px}.border-t,.border-y{border-top-width:1px}.border-b-4{border-bottom-width:4px}.border-b{border-bottom-width:1px}.border-l-\[0\.325rem\]{border-left-width:.325rem}.border-l-4{border-left-width:4px}.border-yellow-400{--tw-border-opacity:1;border-color:rgb(250 204 21/var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity))}.border-indigo-400{--tw-border-opacity:1;border-color:rgb(129 140 248/var(--tw-border-opacity))}.border-indigo-500{--tw-border-opacity:1;border-color:rgb(89 86 235/var(--tw-border-opacity))}.border-transparent{border-color:transparent}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-slate-100{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-black\/50{background-color:rgba(0,0,0,.5)}.bg-black\/5{background-color:rgba(0,0,0,.05)}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-cover{background-size:cover}.bg-clip-text{-webkit-background-clip:text;background-clip:text}.bg-no-repeat{background-repeat:no-repeat}.fill-current{fill:currentColor}.fill-black{fill:#000}.p-4{padding:1rem}.p-12{padding:3rem}.p-2{padding:.5rem}.py-0{padding-bottom:0;padding-top:0}.py-4{padding-bottom:1rem;padding-top:1rem}.py-8{padding-bottom:2rem;padding-top:2rem}.px-0{padding-left:0;padding-right:0}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-32{padding-bottom:8rem;padding-top:8rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.py-16{padding-bottom:4rem;padding-top:4rem}.py-12{padding-bottom:3rem;padding-top:3rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.py-24{padding-bottom:6rem;padding-top:6rem}.pb-12{padding-bottom:3rem}.pt-3{padding-top:.75rem}.pb-3{padding-bottom:.75rem}.pl-4{padding-left:1rem}.pl-8{padding-left:2rem}.pl-2{padding-left:.5rem}.pl-5{padding-left:1.25rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-sans{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-5xl{font-size:3rem;line-height:1}.text-2xl{font-size:1.5rem;line-height:2rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-\[90\%\]{font-size:90%}.text-base{font-size:1rem;line-height:1.5rem}.font-black{font-weight:900}.font-light{font-weight:300}.font-bold{font-weight:700}.font-extrabold{font-weight:800}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.leading-normal{line-height:1.5}.leading-10{line-height:2.5rem}.leading-relaxed{line-height:1.625}.leading-8{line-height:2rem}.leading-4{line-height:1rem}.tracking-wide{letter-spacing:.025em}.tracking-tight{letter-spacing:-.025em}.tracking-normal{letter-spacing:0}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.text-gray-100{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}.text-transparent{color:transparent}.text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-indigo-500{--tw-text-opacity:1;color:rgb(89 86 235/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-indigo-600{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity))}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-75{opacity:.75}.opacity-50{opacity:.5}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.drop-shadow-2xl{--tw-drop-shadow:drop-shadow(0 25px 25px rgba(0,0,0,.15));filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-colors{transition-duration:.15s;transition-property:color,background-color,border-color,fill,stroke,-webkit-text-decoration-color;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,-webkit-text-decoration-color;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-all{transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-75{transition-duration:75ms}.duration-300{transition-duration:.3s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}[x-cloak]{display:none!important}.hover\:bg-black\/10:hover{background-color:rgba(0,0,0,.1)}.hover\:bg-black\/5:hover{background-color:rgba(0,0,0,.05)}.hover\:text-gray-900:hover{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.hover\:text-gray-700:hover{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.hover\:underline:hover{-webkit-text-decoration-line:underline;text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.focus\:not-sr-only:focus{clip:auto;height:auto;margin:0;overflow:visible;padding:0;position:static;white-space:normal;width:auto}.focus\:absolute:focus{position:absolute}.focus\:mx-auto:focus{margin-left:auto;margin-right:auto}.focus\:mt-2:focus{margin-top:.5rem}.focus\:w-64:focus{width:16rem}.focus\:p-2:focus{padding:.5rem}.group:hover .group-hover\:opacity-100{opacity:1}.prose-h1\:mb-3 :is(:where(h1):not(:where([class~=not-prose] *))){margin-bottom:.75rem}.prose-p\:my-3 :is(:where(p):not(:where([class~=not-prose] *))){margin-bottom:.75rem;margin-top:.75rem}.prose-img\:inline :is(:where(img):not(:where([class~=not-prose] *))){display:inline}.dark .dark\:prose-invert{--tw-prose-body:var(--tw-prose-invert-body);--tw-prose-headings:var(--tw-prose-invert-headings);--tw-prose-lead:var(--tw-prose-invert-lead);--tw-prose-links:var(--tw-prose-invert-links);--tw-prose-bold:var(--tw-prose-invert-bold);--tw-prose-counters:var(--tw-prose-invert-counters);--tw-prose-bullets:var(--tw-prose-invert-bullets);--tw-prose-hr:var(--tw-prose-invert-hr);--tw-prose-quotes:var(--tw-prose-invert-quotes);--tw-prose-quote-borders:var(--tw-prose-invert-quote-borders);--tw-prose-captions:var(--tw-prose-invert-captions);--tw-prose-code:var(--tw-prose-invert-code);--tw-prose-pre-code:var(--tw-prose-invert-pre-code);--tw-prose-pre-bg:var(--tw-prose-invert-pre-bg);--tw-prose-th-borders:var(--tw-prose-invert-th-borders);--tw-prose-td-borders:var(--tw-prose-invert-td-borders)}.dark .dark\:prose-invert :where(a):not(:where([class~=not-prose] *)){color:#818cf8}.dark .dark\:prose-invert :where(a):not(:where([class~=not-prose] *)):hover{color:#6366f1}.dark .dark\:block{display:block}.dark .dark\:hidden{display:none}.dark .dark\:border-gray-700{--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity))}.dark .dark\:border-\[\#1b2533\]{--tw-border-opacity:1;border-color:rgb(27 37 51/var(--tw-border-opacity))}.dark .dark\:bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}.dark .dark\:bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.dark .dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.dark .dark\:bg-black\/10{background-color:rgba(0,0,0,.1)}.dark .dark\:bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.dark .dark\:fill-gray-200{fill:#e5e7eb}.dark .dark\:fill-white{fill:#fff}.dark .dark\:font-medium{font-weight:500}.dark .dark\:text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}.dark .dark\:text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.dark .dark\:text-indigo-400{--tw-text-opacity:1;color:rgb(129 140 248/var(--tw-text-opacity))}.dark .dark\:text-gray-100{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}.dark .dark\:hover\:bg-black\/10:hover{background-color:rgba(0,0,0,.1)}.dark .dark\:hover\:text-white:hover,.dark .group:hover .dark\:group-hover\:text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}@media (min-width:640px){.sm\:mb-0{margin-bottom:0}.sm\:mt-4{margin-top:1rem}.sm\:flex{display:flex}.sm\:leading-none{line-height:1}.sm\:shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}}@media (min-width:768px){.md\:visible{visibility:visible}.md\:top-0{top:0}.md\:left-64{left:16rem}.md\:left-0{left:0}.md\:my-6{margin-bottom:1.5rem;margin-top:1.5rem}.md\:mx-2{margin-left:.5rem;margin-right:.5rem}.md\:my-0{margin-bottom:0;margin-top:0}.md\:mt-8{margin-top:2rem}.md\:mb-12{margin-bottom:3rem}.md\:mt-0{margin-top:0}.md\:ml-0{margin-left:0}.md\:block{display:block}.md\:inline-block{display:inline-block}.md\:flex{display:flex}.md\:hidden{display:none}.md\:min-h-screen{min-height:100vh}.md\:w-1\/2{width:50%}.md\:w-\[calc\(100vw_-_16rem\)\]{width:calc(100vw - 16rem)}.md\:w-auto{width:auto}.md\:max-w-none{max-width:none}.md\:max-w-2xl{max-width:42rem}.md\:flex-grow-0{flex-grow:0}.md\:flex-grow{flex-grow:1}.md\:items-center{align-items:center}.md\:border-none{border-style:none}.md\:bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.md\:bg-transparent{background-color:transparent}.md\:bg-left{background-position:0}.md\:py-0{padding-bottom:0;padding-top:0}.md\:px-16{padding-left:4rem;padding-right:4rem}.md\:py-16{padding-bottom:4rem;padding-top:4rem}.md\:pb-0{padding-bottom:0}.md\:pl-0{padding-left:0}.md\:text-center{text-align:center}.md\:text-3xl{font-size:1.875rem;line-height:2.25rem}.md\:text-6xl{font-size:3.75rem;line-height:1}.md\:text-5xl{font-size:3rem;line-height:1}.md\:text-4xl{font-size:2.25rem;line-height:2.5rem}.md\:shadow-none{--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.dark .dark\:md\:bg-transparent{background-color:transparent}}@media (min-width:1024px){.lg\:mb-12{margin-bottom:3rem}.lg\:ml-8{margin-left:2rem}.lg\:bg-center{background-position:50%}.lg\:text-7xl{font-size:4.5rem;line-height:1}.lg\:text-lg{font-size:1.125rem;line-height:1.75rem}.lg\:text-5xl{font-size:3rem;line-height:1}}@media (min-width:1280px){.xl\:mb-16{margin-bottom:4rem}} diff --git a/_pages/404.blade.php b/_pages/404.blade.php new file mode 100644 index 00000000..91f8ea0e --- /dev/null +++ b/_pages/404.blade.php @@ -0,0 +1,51 @@ + + + + + 404 - Page not found + + + + + + + + + + + + + + + +
+
+
+
+ 404 +
+ +
+ +

+ Sorry, the page you are looking for could not be found. +

+ + + + +
+ +
+ +
+
+
+
+
+ + diff --git a/_pages/index.blade.php b/_pages/index.blade.php new file mode 100644 index 00000000..c8989f5b --- /dev/null +++ b/_pages/index.blade.php @@ -0,0 +1,86 @@ + + + + + + + + Welcome to HydePHP! + + + + + + + +
+
+ +
+

+ You're running on HydePHP +

+
+
+

+ Leap into the future of static HTML blogs and documentation with the tools you already know and love. + Made with Tailwind, Laravel, and Coffee. +

+
+ +
+

+ This is the default homepage stored as index.blade.php, however you can publish any of the built-in views using the following command: + + +

php hyde publish:homepage
+

+
+ +
+ Resources for getting started + +
+
+
+ +
+
+ + diff --git a/_posts/.gitkeep b/_posts/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php new file mode 100644 index 00000000..452e6b65 --- /dev/null +++ b/app/Providers/AppServiceProvider.php @@ -0,0 +1,24 @@ +singleton( + Illuminate\Contracts\Console\Kernel::class, + \Hyde\Foundation\ConsoleKernel::class +); + +$app->singleton( + Illuminate\Contracts\Debug\ExceptionHandler::class, + Illuminate\Foundation\Exceptions\Handler::class +); + +/* +|-------------------------------------------------------------------------- +| Set Important Hyde Configurations +|-------------------------------------------------------------------------- +| +| Now, we create a new instance of the HydeKernel, which encapsulates +| our Hyde project and provides helpful methods for interacting with it. +| Then, we bind the kernel into the application service container. +| +*/ + +$hyde = new \Hyde\Foundation\HydeKernel( + dirname(__DIR__) +); + +$app->singleton( + \Hyde\Foundation\HydeKernel::class, function (): \Hyde\Foundation\HydeKernel { + return \Hyde\Foundation\HydeKernel::getInstance(); + } +); + +\Hyde\Foundation\HydeKernel::setInstance($hyde); + +/* +|-------------------------------------------------------------------------- +| Return The Application +|-------------------------------------------------------------------------- +| +| This script returns the application instance. The instance is given to +| the calling script so we can separate the building of the instances +| from the actual running of the application and sending responses. +| +*/ + +return $app; diff --git a/app/config.php b/app/config.php new file mode 100644 index 00000000..9af0e0cd --- /dev/null +++ b/app/config.php @@ -0,0 +1,109 @@ + 'HydePHP', + + /* + |-------------------------------------------------------------------------- + | Application Version + |-------------------------------------------------------------------------- + | + | This value determines the "version" your application is currently running + | in. You may want to follow the "Semantic Versioning" - Given a version + | number MAJOR.MINOR.PATCH when an update happens: https://semver.org. + | + */ + + 'version' => Hyde\Hyde::version(), + + /* + |-------------------------------------------------------------------------- + | Application Environment + |-------------------------------------------------------------------------- + | + | This value determines the "environment" your application is currently + | running in. This may determine how you prefer to configure various + | services the application utilizes. This can be overridden using + | the global command line "--env" option when calling commands. + | + | When using Hyde this setting should always be set to `production`. + | However, when developing the Hyde Core, set it to `development` + | in your .env to unlock the development commands. + | + */ + + 'env' => env('ENV', 'production'), + + /* + |-------------------------------------------------------------------------- + | Autoloaded Service Providers + |-------------------------------------------------------------------------- + | + | The service providers listed here will be automatically loaded on the + | request to your application. Feel free to add your own services to + | this array to grant expanded functionality to your applications. + | + */ + + 'providers' => [ + App\Providers\AppServiceProvider::class, + Hyde\Foundation\Providers\ConfigurationServiceProvider::class, + Hyde\Framework\HydeServiceProvider::class, + Hyde\Foundation\Providers\ViewServiceProvider::class, + Hyde\Console\ConsoleServiceProvider::class, + ], + + /* + |-------------------------------------------------------------------------- + | Class Aliases + |-------------------------------------------------------------------------- + | + | This array of class aliases will be registered when this application + | is started. However, feel free to register as many as you wish as + | the aliases are "lazy" loaded so they don't hinder performance. + | + */ + + 'aliases' => [ + 'Hyde' => Hyde\Hyde::class, + 'Site' => \Hyde\Facades\Site::class, + 'Meta' => \Hyde\Facades\Meta::class, + 'Asset' => \Hyde\Facades\Asset::class, + 'Author' => \Hyde\Facades\Author::class, + 'Features' => \Hyde\Facades\Features::class, + 'Config' => \Hyde\Facades\Config::class, + 'Filesystem' => \Hyde\Facades\Filesystem::class, + 'Routes' => \Hyde\Foundation\Facades\Routes::class, + 'HtmlPage' => \Hyde\Pages\HtmlPage::class, + 'BladePage' => \Hyde\Pages\BladePage::class, + 'MarkdownPage' => \Hyde\Pages\MarkdownPage::class, + 'MarkdownPost' => \Hyde\Pages\MarkdownPost::class, + 'DocumentationPage' => \Hyde\Pages\DocumentationPage::class, + 'DataCollections' => \Hyde\Support\DataCollections::class, + 'Includes' => \Hyde\Support\Includes::class, + ], + +]; diff --git a/app/storage/app/.gitignore b/app/storage/app/.gitignore new file mode 100644 index 00000000..c96a04f0 --- /dev/null +++ b/app/storage/app/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/app/storage/framework/cache/.gitignore b/app/storage/framework/cache/.gitignore new file mode 100644 index 00000000..869804c2 --- /dev/null +++ b/app/storage/framework/cache/.gitignore @@ -0,0 +1,3 @@ +* +!data/ +!.gitignore \ No newline at end of file diff --git a/app/storage/framework/cache/data/.gitignore b/app/storage/framework/cache/data/.gitignore new file mode 100644 index 00000000..c96a04f0 --- /dev/null +++ b/app/storage/framework/cache/data/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/app/storage/framework/views/.gitignore b/app/storage/framework/views/.gitignore new file mode 100644 index 00000000..c96a04f0 --- /dev/null +++ b/app/storage/framework/views/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..5efcf164 --- /dev/null +++ b/composer.json @@ -0,0 +1,63 @@ +{ + "name": "hyde/hyde", + "description": "Static Site Generator to rapidly create Blogs, Documentation Sites, and more, using Markdown and Blade.", + "keywords": [ + "framework", + "hyde", + "hyde framework", + "hydephp", + "static site generator", + "static site framework", + "ssg" + ], + "homepage": "https://hydephp.com", + "type": "project", + "license": "MIT", + "support": { + "issues": "https://github.com/hydephp/hyde/issues", + "source": "https://github.com/hydephp/hyde" + }, + "authors": [ + { + "name": "Caen De Silva", + "email": "caen@desilva.se" + } + ], + "require": { + "php": "^8.1", + "hyde/framework": "^1.3", + "laravel-zero/framework": "^10.0" + }, + "require-dev": { + "hyde/realtime-compiler": "^3.1" + }, + "autoload": { + "psr-4": { + "App\\": "app/" + } + }, + "autoload-dev": { + "psr-4": { + "Hyde\\Testing\\": "tests/" + } + }, + "scripts": { + "post-autoload-dump": [ + "@php -r \"@unlink('./app/storage/framework/cache/packages.php');\"", + "@php hyde package:discover --ansi" + ] + }, + "config": { + "preferred-install": "dist", + "sort-packages": true, + "optimize-autoloader": true, + "allow-plugins": { + "pestphp/pest-plugin": true + } + }, + "minimum-stability": "dev", + "prefer-stable": true, + "bin": [ + "hyde" + ] +} diff --git a/config/docs.php b/config/docs.php new file mode 100644 index 00000000..c26d702e --- /dev/null +++ b/config/docs.php @@ -0,0 +1,135 @@ + [ + // The title in the sidebar header + 'header' => env('SITE_NAME', 'HydePHP').' Docs', + + // When using a grouped sidebar, should the groups be collapsible? + 'collapsible' => true, + + // Should the sidebar footer be shown? + 'footer' => true, + ], + + /* + |-------------------------------------------------------------------------- + | Sidebar Page Order + |-------------------------------------------------------------------------- + | + | In the generated Documentation pages the navigation links in the sidebar + | default to sort alphabetically. You can reorder the page identifiers + | in the list below, and the links will get sorted in that order. + | + | Internally, the items listed will get a position priority of 500 + the order its found in the list. + | Link items without an entry here will have fall back to the default priority of 999, putting them last. + | + | You can also set explicit priorities in front matter. + | + */ + + 'sidebar_order' => [ + 'readme', + 'installation', + 'getting-started', + ], + + /* + |-------------------------------------------------------------------------- + | Table of Contents Settings + |-------------------------------------------------------------------------- + | + | The Hyde Documentation Module comes with a fancy Sidebar that, by default, + | has a Table of Contents included. Here, you can configure its behavior, + | content, look and feel. You can also disable the feature completely. + | + */ + + 'table_of_contents' => [ + 'enabled' => true, + 'min_heading_level' => 2, + 'max_heading_level' => 4, + ], + + /* + |-------------------------------------------------------------------------- + | Collaborative Source Editing Location + |-------------------------------------------------------------------------- + | + | @see https://hydephp.com/docs/1.x/documentation-pages#automatic-edit-page-button + | + | By adding a base URL here, Hyde will use it to create "edit source" links + | to your documentation pages. Hyde expects this to be a GitHub path, but + | it will probably work with other methods as well, if not, send a PR! + | + | You can also change the link text with the `edit_source_link_text` setting. + | + | Example: https://github.com/hydephp/docs/blob/master + | Do not specify the filename or extension, Hyde will do that for you. + | Setting the setting to null will disable the feature. + | + */ + + // 'source_file_location_base' => 'https://github.com///<[blob/edit]>/', + 'edit_source_link_text' => 'Edit Source', + 'edit_source_link_position' => 'footer', // 'header', 'footer', or 'both' + + /* + |-------------------------------------------------------------------------- + | Search Customization + |-------------------------------------------------------------------------- + | + | Hyde comes with an easy to use search feature for documentation pages. + | @see https://hydephp.com/docs/1.x/documentation-pages#search-feature + | + */ + + // Should a docs/search.html page be generated? + 'create_search_page' => true, + + // Are there any pages you don't want to show in the search results? + 'exclude_from_search' => [ + 'changelog', + ], + + /* + |-------------------------------------------------------------------------- + | Flattened Output Paths + |-------------------------------------------------------------------------- + | + | If this setting is set to true, Hyde will output all documentation pages + | into the same configured documentation output directory. This means + | that you can use the automatic directory based grouping feature, + | but still have a "flat" output structure. Note that this means + | that you can't have two documentation pages with the same + | filename or navigation menu label as they will overwrite each other. + | + | If you set this to false, Hyde will match the directory structure + | of the source files (just like all other pages). + | + */ + + 'flattened_output_paths' => true, +]; diff --git a/config/hyde.php b/config/hyde.php new file mode 100644 index 00000000..f9126cb3 --- /dev/null +++ b/config/hyde.php @@ -0,0 +1,458 @@ + env('SITE_NAME', 'HydePHP'), + + /* + |-------------------------------------------------------------------------- + | Site Base URL + |-------------------------------------------------------------------------- + | + | Setting a base URL is highly recommended, and is required to use some + | HydePHP features, like automatic sitemaps and RSS feeds. + | + | If you are serving your site from a subdirectory, + | you will need to include that in the path. + | + */ + + 'url' => env('SITE_URL', 'http://localhost'), + + /* + |-------------------------------------------------------------------------- + | Site Language + |-------------------------------------------------------------------------- + | + | This value sets the language of your site and is used for the + | element in the app layout. Default is 'en'. + | + */ + + 'language' => 'en', + + /* + |-------------------------------------------------------------------------- + | Pretty URLs + |-------------------------------------------------------------------------- + | + | When the setting is enabled, generated links in the compiled HTML site + | are without the .html extension, in other words, "pretty" URLs. + | + | This setting can also be enabled on a per-compile basis by supplying + | the `--pretty-urls` option when you run the build command. + | + */ + + 'pretty_urls' => false, + + /* + |-------------------------------------------------------------------------- + | Sitemap Generation + |-------------------------------------------------------------------------- + | + | When the setting is enabled, a sitemap.xml file will automatically be + | generated when you compile your static site. + | + | This feature requires that a site base URL has been set. + | + */ + + 'generate_sitemap' => true, + + /* + |-------------------------------------------------------------------------- + | RSS Feed Generation + |-------------------------------------------------------------------------- + | + | When enabled, an RSS feed with your Markdown blog posts will be + | generated when you compile your static site. + | + | This feature requires that a site base URL has been set. + | + */ + + 'rss' => [ + // Should the RSS feed be generated? + 'enabled' => true, + + // What filename should the RSS file use? + 'filename' => 'feed.xml', + + // The channel description. + 'description' => env('SITE_NAME', 'HydePHP').' RSS Feed', + ], + + /* + |-------------------------------------------------------------------------- + | Source Root Directory + |-------------------------------------------------------------------------- + | + | HydePHP will by default look for the underscored source directories in the + | root of your project. For example, you might want everything in a 'src' + | subdirectory. That's easy enough, just set the value below to "src"! + | + */ + + 'source_root' => '', + + /* + |-------------------------------------------------------------------------- + | Site Output Directory + |-------------------------------------------------------------------------- + | + | This setting specifies the output path for your site, useful to for + | example, store the site in the docs/ directory for GitHub Pages. + | The path is relative to the root of your project. + | + */ + + 'output_directory' => '_site', + + /* + |-------------------------------------------------------------------------- + | Source Directories + |-------------------------------------------------------------------------- + | + | The directories you place your content in are important. The directory + | will be used to determine the proper page type and the templates used. + | If you are not happy with these defaults, you can change them here. + | Note that these are relative to the `source_root` setting above. + | + */ + + 'source_directories' => [ + \Hyde\Pages\HtmlPage::class => '_pages', + \Hyde\Pages\BladePage::class => '_pages', + \Hyde\Pages\MarkdownPage::class => '_pages', + \Hyde\Pages\MarkdownPost::class => '_posts', + \Hyde\Pages\DocumentationPage::class => '_docs', + ], + + /* + |-------------------------------------------------------------------------- + | Output Directories + |-------------------------------------------------------------------------- + | + | Like the source directories, the output directories are also important + | as they determine the final output path for each page type in your + | compiled static site. This change also affects the route keys. + | + | Note that these are relative to the site's `output_directory` setting. + | Setting the value to '' will output the page to the root of the site. + | + */ + + 'output_directories' => [ + \Hyde\Pages\HtmlPage::class => '', + \Hyde\Pages\BladePage::class => '', + \Hyde\Pages\MarkdownPage::class => '', + \Hyde\Pages\MarkdownPost::class => 'posts', + \Hyde\Pages\DocumentationPage::class => 'docs', + ], + + /* + |-------------------------------------------------------------------------- + | Media Directory + |-------------------------------------------------------------------------- + | + | This setting specifies the directory where your media files are stored. + | Note that this affects both the source and output directories. + | The path is relative to the root of your project. + | + */ + + 'media_directory' => '_media', + + /* + |-------------------------------------------------------------------------- + | Global Site Meta Tags + |-------------------------------------------------------------------------- + | + | While you can add any number of meta tags in the meta.blade.php component + | using standard HTML, you can also use the Meta helper. To add a regular + | meta tag, use Meta::name() helper. To add an Open Graph property, use + | Meta::property() helper which also adds the `og:` prefix for you. + | + | Please note that some pages like blog posts contain dynamic meta tags + | which may override these globals when present in the front matter. + | + */ + + 'meta' => [ + // Meta::name('author', 'Mr. Hyde'), + // Meta::name('twitter:creator', '@HydeFramework'), + // Meta::name('description', 'My Hyde Blog'), + // Meta::name('keywords', 'Static Sites, Blogs, Documentation'), + Meta::name('generator', 'HydePHP v'.Hyde\Hyde::version()), + Meta::property('site_name', env('SITE_NAME', 'HydePHP')), + ], + + /* + |-------------------------------------------------------------------------- + | Features + |-------------------------------------------------------------------------- + | + | Some of Hyde's features are optional. Feel free to disable the features + | you don't need by removing or commenting them out from this array. + | This config concept is directly inspired by Laravel Jetstream. + | + */ + + 'features' => [ + // Page Modules + Features::htmlPages(), + Features::markdownPosts(), + Features::bladePages(), + Features::markdownPages(), + Features::documentationPages(), + + // Frontend Features + Features::darkmode(), + Features::documentationSearch(), + + // Integrations + Features::torchlight(), + ], + + /* + |-------------------------------------------------------------------------- + | Blog Post Authors + |-------------------------------------------------------------------------- + | + | Hyde has support for adding authors in front matter, for example to + | automatically add a link to your website or social media profiles. + | However, it's tedious to have to add those to each and every + | post you make, and keeping them updated is even harder. + | + | Here you can add predefined authors. When writing posts, + | just specify the username in the front matter, and the + | rest of the data will be pulled from a matching entry. + | + */ + + 'authors' => [ + Author::create( + 'mr_hyde', // Required username + 'Mr. Hyde', // Optional display name + 'https://hydephp.com' // Optional website URL + ), + ], + + /* + |-------------------------------------------------------------------------- + | Footer Text + |-------------------------------------------------------------------------- + | + | Here you can customize the footer Markdown text for your site. + | + | If you don't want to write Markdown here, you use a Markdown include. + | You can also customize the Blade view if you want a more complex footer. + | You can disable it completely by changing the setting to `false`. + | + | To read about the many configuration options here, visit: + | https://hydephp.com/docs/1.x/customization#footer + | + */ + + 'footer' => 'Site proudly built with [HydePHP](https://github.com/hydephp/hyde) 🎩', + + /* + |-------------------------------------------------------------------------- + | Navigation Menu Configuration + |-------------------------------------------------------------------------- + | + | If you are looking to customize the main navigation menu, this is the place! + | + */ + + 'navigation' => [ + // This configuration sets the priorities used to determine the order of the menu. + // The default values have been added below for reference and easy editing. + // The array key should match the page's route key (slug). + // Lower values show up first in the menu. + 'order' => [ + 'index' => 0, + 'posts' => 10, + 'docs/index' => 100, + ], + + // In case you want to customize the labels for the menu items, you can do so here. + // Simply add the route key (slug) as the key, and the label as the value. + 'labels' => [ + 'index' => 'Home', + 'docs/index' => 'Docs', + ], + + // These are the pages that should not show up in the navigation menu. + 'exclude' => [ + '404', + ], + + // Any extra links you want to add to the navigation menu can be added here. + // To get started quickly, you can uncomment the defaults here. + // See the documentation link above for more information. + 'custom' => [ + // NavItem::forLink('https://github.com/hydephp/hyde', 'GitHub', 200), + ], + + // How should pages in subdirectories be displayed in the menu? + // You can choose between 'dropdown', 'flat', and 'hidden'. + 'subdirectories' => 'hidden', + ], + + /* + |-------------------------------------------------------------------------- + | Cache Busting + |-------------------------------------------------------------------------- + | + | Any assets loaded using the Asset::mediaLink() helper will automatically + | have a cache busting query string appended to the URL. This is useful + | when you want to force browsers to load a new version of an asset. + | + | The mediaLink helper is used in the built-in views to load the + | default stylesheets and scripts, and thus use this feature. + | + | To disable cache busting, set this setting to false. + | + */ + + 'enable_cache_busting' => true, + + /* + |-------------------------------------------------------------------------- + | Load app.css from CDN + |-------------------------------------------------------------------------- + | + | Hyde ships with an app.css file containing compiled TailwindCSS styles + | in the _media/ directory. If you want to load this file from the + | HydeFront JsDelivr CDN, you can set this setting to true. + | + */ + + 'load_app_styles_from_cdn' => false, + + /* + |-------------------------------------------------------------------------- + | Tailwind Play CDN + |-------------------------------------------------------------------------- + | + | The next setting enables a script for the TailwindCSS Play CDN which will + | compile CSS in the browser. While this is useful for local development + | it's not recommended for production use. To keep things consistent, + | your Tailwind configuration file will be injected into the HTML. + */ + + 'use_play_cdn' => false, + + /* + |-------------------------------------------------------------------------- + | Default Color Scheme + |-------------------------------------------------------------------------- + | + | The default color scheme for the meta color-scheme tag, note that this + | is just a hint to the user-agent and does not force a specific theme. + | + */ + + 'default_color_scheme' => 'light', + + /* + |-------------------------------------------------------------------------- + | Built-in Server + |-------------------------------------------------------------------------- + | + | Here you can configure settings for the built-in realtime compiler server. + | The server also includes a magic dashboard feature that supercharges + | your local development! This feature can alo be customised here. + | + */ + + 'server' => [ + // The default port the preview is served on + 'port' => env('SERVER_PORT', 8080), + + // The default host the preview is served on + 'host' => env('SERVER_HOST', 'localhost'), + + // Should preview pages be saved to the output directory? + 'save_preview' => true, + + // Configure the realtime compiler dashboard + 'dashboard' => [ + // Should the realtime compiler dashboard be enabled? + 'enabled' => env('SERVER_DASHBOARD', true), + + // Can the dashboard make edits to the project file system? + 'interactive' => true, + + // Should the dashboard show tips? + 'tips' => true, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Additional Advanced Options + |-------------------------------------------------------------------------- + | + | Finally, here are some additional configuration options that you most + | likely won't need to change. These are intended for advanced users, + | and some should only be changed if you know what you're doing. + | + */ + + // The list of directories that are considered to be safe to empty upon site build. + // If the site output directory is set to a directory that is not in this list, + // the build command will prompt for confirmation before emptying it. + 'safe_output_directories' => ['_site', 'docs', 'build'], + + // Should a JSON build manifest with metadata about the build be generated? + 'generate_build_manifest' => true, + + // Where should the build manifest be saved? (Relative to project root, for example _site/build-manifest.json) + 'build_manifest_path' => 'app/storage/framework/cache/build-manifest.json', + + // Here you can specify HydeFront version and URL for when loading app.css from the CDN. + // Only change these if you know what you're doing as some versions may incompatible with your Hyde version. + 'hydefront_version' => \Hyde\Framework\Services\AssetService::HYDEFRONT_VERSION, + 'hydefront_cdn_url' => \Hyde\Framework\Services\AssetService::HYDEFRONT_CDN_URL, + +]; diff --git a/config/markdown.php b/config/markdown.php new file mode 100644 index 00000000..0ec94071 --- /dev/null +++ b/config/markdown.php @@ -0,0 +1,98 @@ + [ + \League\CommonMark\Extension\GithubFlavoredMarkdownExtension::class, + \League\CommonMark\Extension\Attributes\AttributesExtension::class, + ], + + /* + |-------------------------------------------------------------------------- + | Configuration Options + |-------------------------------------------------------------------------- + | + | Define any options that should be passed to the CommonMark converter. + | + | Hyde handles many of the options automatically, but you may want to + | override some of them and/or add your own. Any custom options + | will be merged with the Hyde defaults during runtime. + | + */ + + 'config' => [ + // + ], + + /* + |-------------------------------------------------------------------------- + | Allow all HTML tags + |-------------------------------------------------------------------------- + | + | HydePHP uses the GitHub Flavored Markdown extension to convert Markdown. + | This, by default strips out some HTML tags. If you want to allow all + | arbitrary HTML tags, and understand the risks involved, you can + | use this config setting to enable all HTML tags. + | + */ + + 'allow_html' => false, + + /* + |-------------------------------------------------------------------------- + | Blade-supported Markdown + |-------------------------------------------------------------------------- + | + | This feature allows you to use basic Laravel Blade in Markdown files. + | + | It's disabled by default since can be a security risk as it allows + | arbitrary PHP to run. But if your Markdown is trusted, try it out! + | + | To see the syntax and usage, see the documentation: + | @see https://hydephp.com/docs/1.x/advanced-markdown#blade-support + | + */ + + 'enable_blade' => false, + + /* + |-------------------------------------------------------------------------- + | Tailwind Typography Prose Classes + |-------------------------------------------------------------------------- + | + | HydePHP uses Tailwind Typography to style rendered Markdown. + | + | This setting controls the base classes to apply to all the HTML elements + | containing rendered markdown. Please note that if you add any new + | classes, you may need to recompile your CSS file. + | + */ + + 'prose_classes' => 'prose dark:prose-invert', +]; diff --git a/hyde b/hyde new file mode 100644 index 00000000..5d428089 --- /dev/null +++ b/hyde @@ -0,0 +1,53 @@ +#!/usr/bin/env php +make(Illuminate\Contracts\Console\Kernel::class); + +$status = $kernel->handle( + $input = new Symfony\Component\Console\Input\ArgvInput, + new Symfony\Component\Console\Output\ConsoleOutput +); + +/* +|-------------------------------------------------------------------------- +| Shutdown The Application +|-------------------------------------------------------------------------- +| +| Once the HydeCLI has finished running, we will fire off the shutdown events +| so that any final work may be done by the application before we shut +| down the process. This is the last thing to happen to the request. +| +*/ + +$kernel->terminate($input, $status); + +exit($status); diff --git a/package.json b/package.json new file mode 100644 index 00000000..968e0dcd --- /dev/null +++ b/package.json @@ -0,0 +1,41 @@ +{ + "private": true, + "scripts": { + "dev": " mix", + "prod": "mix --production", + "watch": "mix watch" + }, + "name": "hyde", + "description": "Elegant and Powerful Static App Builder", + "version": "1.1.0", + "main": "hyde", + "directories": { + "test": "tests" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/hydephp/hyde.git" + }, + "keywords": [ + "hydephp", + "hyde", + "static", + "site", + "generator" + ], + "author": "Caen De Silva", + "license": "MIT", + "bugs": { + "url": "https://github.com/hydephp/hyde/issues" + }, + "homepage": "https://hydephp.com", + "devDependencies": { + "@tailwindcss/typography": "^0.5.2", + "autoprefixer": "^10.4.5", + "hydefront": "^3.3.0", + "laravel-mix": "^6.0.49", + "postcss": "^8.4.31", + "prettier": "2.6.0", + "tailwindcss": "^3.0.24" + } +} diff --git a/resources/assets/app.css b/resources/assets/app.css new file mode 100644 index 00000000..cef19538 --- /dev/null +++ b/resources/assets/app.css @@ -0,0 +1,20 @@ +/* +* This file is loaded into your Hyde/Hyde installation and is used to generate the styles for your project. +* You can use this file to customize your TailwindCSS or to add new classes. +* +* The HydeFront package contains some base styles to make your site look even more amazing. +* +* The compiled result of this file is shipped with HydePHP and is found at _media/app.css, +* so you don't need to compile this file unless you're making changes. +* +* If you want, you can load the compiled file with minified styles for a base install from the CDN. +* See https://hydephp.com/docs/1.x/managing-assets#loading-from-cdn +*/ + +@import '~hydefront/dist/hyde.css'; + +@tailwind base; +@tailwind components; +@tailwind utilities; + +[x-cloak] { display: none !important; } diff --git a/resources/assets/app.js b/resources/assets/app.js new file mode 100644 index 00000000..3195f61b --- /dev/null +++ b/resources/assets/app.js @@ -0,0 +1,3 @@ +/* +* This is the main JavaScript used by webpack to build the the app.js file. +*/ diff --git a/resources/views/.gitkeep b/resources/views/.gitkeep new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/resources/views/.gitkeep @@ -0,0 +1 @@ + diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 00000000..2b895138 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,97 @@ +const defaultTheme = require('tailwindcss/defaultTheme'); + +module.exports = { + darkMode: 'class', + content: [ + './_pages/*.blade.php', + './resources/views/**/*.blade.php', + './vendor/hyde/framework/resources/views/**/*.blade.php', + ], + + theme: { + extend: { + typography: { + DEFAULT: { + css: { + lineHeight: '1.5em', + maxWidth: '96ch', + h2: { + marginBottom: '0.75em', + marginTop: '1.5em', + }, + a: { + color: '#5956eb', + '&:hover': { + color: '#4f46e5', + }, + textDecoration: 'none' + }, + blockquote: { + backgroundColor: '#80808020', + borderLeftColor: '#d1d5db', + color: 'unset', + fontWeight: 500, + fontStyle: 'unset', + lineHeight: '1.25em', + paddingLeft: '0.75em', + paddingTop: '.25em', + paddingBottom: '.25em', + marginTop: '1em', + marginBottom: '1em', + p: { + paddingRight: '.25em', + marginTop: '.25em', + marginBottom: '.25em', + }, + 'p::before': { + content: 'unset', + }, + 'p::after': { + content: 'unset', + }, + }, + code: { + font: 'unset', + backgroundColor: '#80808033', + paddingLeft: '4px', + paddingRight: '4px', + marginLeft: '-2px', + marginRight: '1px', + borderRadius: '4px' + }, + 'code::before': { + content: 'unset', + }, + 'code::after': { + content: 'unset', + }, + pre: { + code: { + fontFamily: "'Fira Code Regular', Consolas, Monospace, 'Courier New'", + } + } + }, + }, + invert: { + css: { + a: { + color: '#818cf8', + '&:hover': { + color: '#6366f1', + }, + }, + }, + }, + }, + colors: { + indigo: { + 500: '#5956eb', + } + }, + }, + }, + + plugins: [ + require('@tailwindcss/typography') + ], +}; diff --git a/tests/DefaultContentTest.php b/tests/DefaultContentTest.php new file mode 100644 index 00000000..358d619c --- /dev/null +++ b/tests/DefaultContentTest.php @@ -0,0 +1,59 @@ +assertFileExists(Hyde::path('_pages/index.blade.php')); + $this->assertFileExists(Hyde::path('_pages/404.blade.php')); + + $this->assertStringContainsString( + 'Welcome to HydePHP!', + file_get_contents(Hyde::path('_pages/index.blade.php')) + ); + + $this->assertStringContainsString( + '404 - Page not found', + file_get_contents(Hyde::path('_pages/404.blade.php')) + ); + } + + public function test_default_compiled_stylesheet_is_present() + { + $this->assertFileExists(Hyde::path('_media/app.css')); + + $this->assertStringContainsString( + 'https://tailwindcss.com', + file_get_contents(Hyde::path('_media/app.css')) + ); + } + + public function test_laravel_mix_resources_are_present() + { + $this->assertFileExists(Hyde::path('resources/assets/app.css')); + $this->assertFileExists(Hyde::path('resources/assets/app.js')); + + $this->assertFileContainsString('@tailwind base;', Hyde::path('resources/assets/app.css')); + $this->assertFileContainsString('@tailwind components;', Hyde::path('resources/assets/app.css')); + $this->assertFileContainsString('@tailwind utilities;', Hyde::path('resources/assets/app.css')); + + $this->assertFileContainsString('This is the main JavaScript', Hyde::path('resources/assets/app.js')); + } + + protected function assertFileContainsString(string $string, string $file) + { + $this->assertStringContainsString($string, file_get_contents($file)); + } +} diff --git a/tests/ExampleTest.php b/tests/ExampleTest.php new file mode 100644 index 00000000..d433f04a --- /dev/null +++ b/tests/ExampleTest.php @@ -0,0 +1,15 @@ +assertTrue(true); + } +} diff --git a/tests/HydeCLITest.php b/tests/HydeCLITest.php new file mode 100644 index 00000000..df58abc3 --- /dev/null +++ b/tests/HydeCLITest.php @@ -0,0 +1,17 @@ +artisan('list') + ->expectsOutputToContain('hyde') + ->assertExitCode(0); + } +} diff --git a/tests/StaticSiteBuilderTest.php b/tests/StaticSiteBuilderTest.php new file mode 100644 index 00000000..42960d2b --- /dev/null +++ b/tests/StaticSiteBuilderTest.php @@ -0,0 +1,23 @@ +artisan('build') + ->expectsOutputToContain('Building your static site!') + ->assertExitCode(0); + + File::cleanDirectory(Hyde::path('_site')); + } +} diff --git a/webpack.mix.js b/webpack.mix.js new file mode 100644 index 00000000..e8d11abb --- /dev/null +++ b/webpack.mix.js @@ -0,0 +1,12 @@ +// Using Laravel Mix is optional as the styles you need to get started are already included. +// However, if you add new Tailwind classes, or any customizations, you can use Webpack to +// compile the assets. See https://hydephp.com/docs/1.x/managing-assets.html. + +let mix = require('laravel-mix'); + +mix.js('resources/assets/app.js', 'app.js') + .postCss('resources/assets/app.css', 'app.css', [ + require('tailwindcss'), + require('autoprefixer'), + ]).setPublicPath('_site/media') + .copyDirectory('_site/media', '_media')