diff --git a/.github/workflows/generate-json.yml b/.github/workflows/generate-json.yml index eae447e..8ad8628 100644 --- a/.github/workflows/generate-json.yml +++ b/.github/workflows/generate-json.yml @@ -26,6 +26,15 @@ jobs: with: fetch-depth: 0 + - name: Setup Hugo + uses: peaceiris/actions-hugo@v2 + with: + hugo-version: '0.81.0' + extended: true + + - name: Build + run: hugo --minify --baseURL https://quiqr.github.io/quiqr-community-templates/ + - name: Extract Links id: extralinks uses: mipmip/github-action-markdown-link-extract-to-json@v1.4 @@ -40,14 +49,14 @@ jobs: - name: parse and flatten repo meta run: ./.github/workflows/getmeta.sh - - name: download md2html - run: cd /tmp && wget -c https://github.com/nocd5/md2html/releases/download/v2.0.0/md2html_linux_amd64.zip && unzip md2html_linux_amd64.zip - - - name: convert README.md with md2html - run: /tmp/md2html README.md -o index.html +# - name: download md2html +# run: cd /tmp && wget -c https://github.com/nocd5/md2html/releases/download/v2.0.0/md2html_linux_amd64.zip && unzip md2html_linux_amd64.zip +# +# - name: convert README.md with md2html +# run: /tmp/md2html README.md -o index.html - name: Deploy uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: . + publish_dir: ./public diff --git a/.github/workflows/getmeta.sh b/.github/workflows/getmeta.sh index cc72fa9..a83ac62 100755 --- a/.github/workflows/getmeta.sh +++ b/.github/workflows/getmeta.sh @@ -43,6 +43,6 @@ do done rm -f templates_tmp.json -jq -s 'flatten' _*.json > templates.json +jq -s 'flatten' _*.json > public/static/templates.json rm -f _*.json -cat templates.json +cat public/static/templates.json diff --git a/.gitignore b/.gitignore index d003be3..e7407c5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,80 @@ -.temp.html -.temp.seed +# Hugo Theme +resources +public + +# OSX +.DS_Store + +# Jetbrains +.idea + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ +package-lock.json + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# next.js build output +.next + +# Test Content +content/test + +# Visual Studio Code project settings +.vscode +# Local Netlify folder +.netlify diff --git a/LICENSE b/LICENSE index 3ad65fd..fd115a8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,40 +1,22 @@ -CC0 1.0 Universal - -Statement of Purpose - -The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). - -Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. - -For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. - -Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: -i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; - -ii. moral rights retained by the original author(s) and/or performer(s); - -iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; - -iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; - -v. rights protecting the extraction, dissemination, use and reuse of data in a Work; - -vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and - -vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. - -Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. - -Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. - -Limitations and Disclaimers. - -a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. - -b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. - -c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. - -d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. - -For more information, please see https://creativecommons.org/publicdomain/zero/1.0 +MIT License + +Copyright (c) 2019 jamstackthemes.dev - rob@stackbit.com +Copyright (c) 2021-2024 quiqr.org - post@pimsnel.com + +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/archetypes/default.md b/archetypes/default.md new file mode 100644 index 0000000..00e77bd --- /dev/null +++ b/archetypes/default.md @@ -0,0 +1,6 @@ +--- +title: "{{ replace .Name "-" " " | title }}" +date: {{ .Date }} +draft: true +--- + diff --git a/archetypes/ssg.md b/archetypes/ssg.md new file mode 100644 index 0000000..111df5b --- /dev/null +++ b/archetypes/ssg.md @@ -0,0 +1,7 @@ +--- +title: "{{ replace .Name "-" " " | title }}" +icon: images/icons/docusaurus.svg +official_url: https://docusaurus.io/ +vitalstats_url: https://www.staticgen.com/docusaurus +taxonomy: ssg +--- diff --git a/archetypes/theme.md b/archetypes/theme.md new file mode 100644 index 0000000..6456e1a --- /dev/null +++ b/archetypes/theme.md @@ -0,0 +1,33 @@ +--- +title: "{{ replace .Name "-" " " | title }}" +github: https://github.com/username/repo +github_branch: master +demo: https://www.demo.com +author: Github Author +ssg: + - Gridsome +cms: + - No CMS +css: + - Bootstrap +archetype: + - Blog + - Portfolio +date: 2019-06-11 +description: This theme is a lightweight Gridsome starter kit which is perfect for a blog or a portfolio +--- + +# A simple starter kit for Gridsome + +This theme is a lightweight starter kit. It also gives you a well organised starting point to extend it for yourself. + +## Features + +* Customisable design tokens to make it your own +* Customisable global data and navigation +* Tags and tag archives +* Progressively enhanced, semantic and accessible +* _Super_ lightweight front-end +* Sass powered CSS system with utility class generator +* Service worker that caches pages so people can read your articles offline +* An RSS feed for your posts diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..c422c35 --- /dev/null +++ b/config.toml @@ -0,0 +1,55 @@ +baseURL = "/" +languageCode = "en-us" +title = "Quiqr Templates" +theme = "jamstackthemes" +paginate = 18 +summaryLength = 10 +# enableGitInfo = false + +pygmentsCodeFences = true +pygmentsCodefencesGuessSyntax = true +pygmentsUseClasses = true + +[params] + description = "Templates and starters for the Quiqr Static website CMS." + stackbit_button = true + iframe_demo = true + assets_url = "https://assets.jamstackthemes.dev" + + [theme_card] + show_archetype_icons = false + show_services_icons = false + +[taxonomies] + ssg = "ssg" + cms = "cms" + css = "css" + archetype = "archetype" + services = "services" + +[outputFormats] + [outputFormats.demo] + name = "demo" + mediaType = "text/html" + isHTML = "true" + path = "demo" + permalinkable = "true" + +[outputs] + home = ["HTML","RSS","JSON"] + section = ["HTML","RSS"] + page = ["HTML", "demo"] + +[frontmatter] + lastmod = [":git", "lastmod", ":fileModTime", ":default"] + +# Main Menu +[[menu.main]] + name = "New Themes" + url = "/" + weight = 1 + +[[menu.main]] + name = "All Themes" + url = "/theme/" + weight = 2 diff --git a/content/_index.md b/content/_index.md new file mode 100644 index 0000000..5bea243 --- /dev/null +++ b/content/_index.md @@ -0,0 +1,3 @@ +--- +title: 'Jamstack Themes' +--- diff --git a/content/archetype/_index.md b/content/archetype/_index.md new file mode 100644 index 0000000..42b9ea3 --- /dev/null +++ b/content/archetype/_index.md @@ -0,0 +1,4 @@ +--- +title: Archetype +weight: 4 +--- \ No newline at end of file diff --git a/content/archetype/admin/_index.md b/content/archetype/admin/_index.md new file mode 100644 index 0000000..544a47b --- /dev/null +++ b/content/archetype/admin/_index.md @@ -0,0 +1,4 @@ +--- +title: "Admin" +icon: images/icons/admin.svg +--- diff --git a/content/archetype/blog/_index.md b/content/archetype/blog/_index.md new file mode 100644 index 0000000..603f54f --- /dev/null +++ b/content/archetype/blog/_index.md @@ -0,0 +1,4 @@ +--- +title: "Blog" +icon: images/icons/blog.svg +--- \ No newline at end of file diff --git a/content/archetype/business/_index.md b/content/archetype/business/_index.md new file mode 100644 index 0000000..b00ecda --- /dev/null +++ b/content/archetype/business/_index.md @@ -0,0 +1,4 @@ +--- +title: "Business" +icon: images/icons/business.svg +--- \ No newline at end of file diff --git a/content/archetype/documentation/_index.md b/content/archetype/documentation/_index.md new file mode 100644 index 0000000..14c7d40 --- /dev/null +++ b/content/archetype/documentation/_index.md @@ -0,0 +1,4 @@ +--- +title: "Documentation" +icon: images/icons/documentation.svg +--- \ No newline at end of file diff --git a/content/archetype/ecommerce/_index.md b/content/archetype/ecommerce/_index.md new file mode 100644 index 0000000..97145b0 --- /dev/null +++ b/content/archetype/ecommerce/_index.md @@ -0,0 +1,4 @@ +--- +title: "Ecommerce" +icon: images/icons/ecommerce.svg +--- diff --git a/content/archetype/event/_index.md b/content/archetype/event/_index.md new file mode 100644 index 0000000..bf88eb7 --- /dev/null +++ b/content/archetype/event/_index.md @@ -0,0 +1,4 @@ +--- +title: "Event" +icon: images/icons/event.svg +--- diff --git a/content/archetype/personal/_index.md b/content/archetype/personal/_index.md new file mode 100644 index 0000000..1b2bdf2 --- /dev/null +++ b/content/archetype/personal/_index.md @@ -0,0 +1,4 @@ +--- +title: "Personal" +icon: images/icons/personal.svg +--- \ No newline at end of file diff --git a/content/archetype/portfolio/_index.md b/content/archetype/portfolio/_index.md new file mode 100644 index 0000000..8438bff --- /dev/null +++ b/content/archetype/portfolio/_index.md @@ -0,0 +1,4 @@ +--- +title: "Portfolio" +icon: images/icons/portfolio.svg +--- \ No newline at end of file diff --git a/content/archetype/single-page/_index.md b/content/archetype/single-page/_index.md new file mode 100644 index 0000000..e049b64 --- /dev/null +++ b/content/archetype/single-page/_index.md @@ -0,0 +1,4 @@ +--- +title: "Single Page" +icon: images/icons/single-page.svg +--- diff --git a/content/css/_index.md b/content/css/_index.md new file mode 100644 index 0000000..4838274 --- /dev/null +++ b/content/css/_index.md @@ -0,0 +1,4 @@ +--- +title: CSS +weight: 3 +--- \ No newline at end of file diff --git a/content/css/bootstrap/_index.md b/content/css/bootstrap/_index.md new file mode 100644 index 0000000..0e79ed6 --- /dev/null +++ b/content/css/bootstrap/_index.md @@ -0,0 +1,5 @@ +--- +title: "Bootstrap" +icon: images/icons/bootstrap.svg +official_url: https://getbootstrap.com/ +--- \ No newline at end of file diff --git a/content/css/bulma/_index.md b/content/css/bulma/_index.md new file mode 100644 index 0000000..65262cf --- /dev/null +++ b/content/css/bulma/_index.md @@ -0,0 +1,4 @@ +--- +title: "Bulma" +icon: images/icons/bulma.svg +--- \ No newline at end of file diff --git a/content/css/css/_index.md b/content/css/css/_index.md new file mode 100644 index 0000000..3c6e73a --- /dev/null +++ b/content/css/css/_index.md @@ -0,0 +1,4 @@ +--- +title: "CSS" +icon: images/icons/css.svg +--- \ No newline at end of file diff --git a/content/css/material-ui/_index.md b/content/css/material-ui/_index.md new file mode 100644 index 0000000..a2b7c9e --- /dev/null +++ b/content/css/material-ui/_index.md @@ -0,0 +1,6 @@ +--- +title: "Material Ui" +icon: images/icons/material-ui.svg +official_url: https://mui.com/ +taxonomy: css +--- \ No newline at end of file diff --git a/content/css/postcss/_index.md b/content/css/postcss/_index.md new file mode 100644 index 0000000..a44b686 --- /dev/null +++ b/content/css/postcss/_index.md @@ -0,0 +1,5 @@ +--- +title: "PostCSS" +icon: images/icons/postcss.svg +official_url: https://postcss.org/ +--- diff --git a/content/css/scss/_index.md b/content/css/scss/_index.md new file mode 100644 index 0000000..c1038d2 --- /dev/null +++ b/content/css/scss/_index.md @@ -0,0 +1,4 @@ +--- +title: "SCSS" +icon: images/icons/scss.svg +--- \ No newline at end of file diff --git a/content/css/styled-components/_index.md b/content/css/styled-components/_index.md new file mode 100644 index 0000000..5f10d09 --- /dev/null +++ b/content/css/styled-components/_index.md @@ -0,0 +1,5 @@ +--- +title: "Styled Components" +icon: images/icons/styled-components.png +official_url: https://styled-components.com/ +--- diff --git a/content/css/stylus/_index.md b/content/css/stylus/_index.md new file mode 100644 index 0000000..419a50d --- /dev/null +++ b/content/css/stylus/_index.md @@ -0,0 +1,5 @@ +--- +title: "Stylus" +icon: images/icons/stylus.svg +official_url: https://stylus-lang.com/ +--- diff --git a/content/css/tailwind/_index.md b/content/css/tailwind/_index.md new file mode 100644 index 0000000..ebb00a9 --- /dev/null +++ b/content/css/tailwind/_index.md @@ -0,0 +1,5 @@ +--- +title: "Tailwind" +icon: images/icons/tailwind.svg +official_url: https://tailwindcss.com/ +--- \ No newline at end of file diff --git a/content/css/vuetify/_index.md b/content/css/vuetify/_index.md new file mode 100644 index 0000000..522568c --- /dev/null +++ b/content/css/vuetify/_index.md @@ -0,0 +1,5 @@ +--- +title: "Vuetify" +icon: images/icons/vuetify.svg +official_url: https://vuetifyjs.com/ +--- \ No newline at end of file diff --git a/content/services/_index.md b/content/services/_index.md new file mode 100644 index 0000000..a62afd8 --- /dev/null +++ b/content/services/_index.md @@ -0,0 +1,4 @@ +--- +title: Services +weight: 3 +--- \ No newline at end of file diff --git a/content/services/algolia/_index.md b/content/services/algolia/_index.md new file mode 100644 index 0000000..f76c7c1 --- /dev/null +++ b/content/services/algolia/_index.md @@ -0,0 +1,5 @@ +--- +title: "Algolia" +icon: images/icons/algolia.svg +official_url: https://https://www.algolia.com/ +--- \ No newline at end of file diff --git a/content/services/formspree/_index.md b/content/services/formspree/_index.md new file mode 100644 index 0000000..b3445c6 --- /dev/null +++ b/content/services/formspree/_index.md @@ -0,0 +1,5 @@ +--- +title: "Formspree" +icon: images/icons/formspree.svg +official_url: https://formspree.io/ +--- \ No newline at end of file diff --git a/content/services/formstack/_index.md b/content/services/formstack/_index.md new file mode 100644 index 0000000..08cec3c --- /dev/null +++ b/content/services/formstack/_index.md @@ -0,0 +1,5 @@ +--- +title: "Formstack" +icon: images/icons/formstack.svg +official_url: https://formstack.com/ +--- \ No newline at end of file diff --git a/content/services/mailchimp/_index.md b/content/services/mailchimp/_index.md new file mode 100644 index 0000000..7c9466d --- /dev/null +++ b/content/services/mailchimp/_index.md @@ -0,0 +1,5 @@ +--- +title: "Mailchimp" +icon: images/icons/mailchimp.svg +official_url: https://mailchimp.com/ +--- \ No newline at end of file diff --git a/content/services/shopify/_index.md b/content/services/shopify/_index.md new file mode 100644 index 0000000..ba2334c --- /dev/null +++ b/content/services/shopify/_index.md @@ -0,0 +1,5 @@ +--- +title: "Shopify" +icon: images/icons/shopify.svg +official_url: https://shopify.com/ +--- \ No newline at end of file diff --git a/content/services/snipcart/_index.md b/content/services/snipcart/_index.md new file mode 100644 index 0000000..f533e5a --- /dev/null +++ b/content/services/snipcart/_index.md @@ -0,0 +1,5 @@ +--- +title: "Snipcart" +icon: images/icons/snipcart.svg +official_url: https://snipcart.com/ +--- \ No newline at end of file diff --git a/content/theme/hugo-theme-tailwind.md b/content/theme/hugo-theme-tailwind.md new file mode 100644 index 0000000..c802fdd --- /dev/null +++ b/content/theme/hugo-theme-tailwind.md @@ -0,0 +1,36 @@ +--- +title: Tailwind +github: https://github.com/tomowang/hugo-theme-tailwind +demo: https://hugo-theme-tailwind.tomo.dev/ +author: Xiaoliang Wang +author_link: https://tomo.dev +date: 2023-12-09 +ssg: + - Hugo +cms: + - No CMS +css: + - Tailwind +archetype: + - Blog +description: Clean card Hugo theme for blog, created by using tailwindcss +--- + +# Hugo theme tailwind + +Hugo theme build using [Tailwind CSS](https://tailwindcss.com/), mostly for bloggers. + +Check [https://hugo-theme-tailwind.tomo.dev/](https://hugo-theme-tailwind.tomo.dev/) for demo. + +## Features + + * High Lighthouse score + * Dark mode - switch between light and dark mode, or default by system prefers. + * SVG icons thanks to [tabler icons](https://tabler-icons.io/) + * Google Analystics - add your Google Analytics ID to `hugo.toml` + * Comments settings for [disqus](https://disqus.com/) and [giscus](https://giscus.app/) + * Social media link data settings + * Additional short code: bilibili, asciinema + * Responsive design + * Multilingual + * Default image process for lazy load and srcset diff --git a/data/errors.json b/data/errors.json new file mode 100644 index 0000000..7016f33 --- /dev/null +++ b/data/errors.json @@ -0,0 +1,22 @@ +[ + { + "theme_key": "obaez-dentistsmile", + "file": "dentistsmile.md", + "repoUrl": "https://github.com/obaez/dentistsmile", + "error": "Github repo not found, status: 404" + }, + { + "theme_key": "hugo-sid-hugo-blog-awesome", + "file": "hugo-blog-awesome.md", + "repoUrl": "https://github.com/hugo-sid/hugo-blog-awesome", + "demoUrl": "https://hugo-blog-awesome.netlify.app/", + "error": "demo url not found" + }, + { + "theme_key": "obaez-dentistsmile", + "file": "dentistsmile.md", + "repoUrl": "https://github.com/obaez/dentistsmile", + "demoUrl": "https://obaez.com/dentistsmile/", + "error": "error checking demo url" + } +] \ No newline at end of file diff --git a/data/stackbit.json b/data/stackbit.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/data/stackbit.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/data/themes.json b/data/themes.json new file mode 100644 index 0000000..72b4377 --- /dev/null +++ b/data/themes.json @@ -0,0 +1,25 @@ +{ + "zzzmisa-hugo-theme-doors": { + "theme_key": "zzzmisa-hugo-theme-doors", + "file": "hugo-theme-doors.md", + "name": "hugo-theme-doors", + "title": "Doors", + "github_username": "zzzmisa", + "repo": "zzzmisa/hugo-theme-doors", + "branch": "master", + "default_branch": "master", + "github_url": "https://github.com/zzzmisa/hugo-theme-doors", + "demo_url": "https://zzzmisa.com/", + "stars": 13, + "forks": 9, + "open_issues": 0, + "last_commit": "2022-02-05T06:06:44Z", + "created_at": "2019-08-05T14:40:43Z", + "description": "🚪Single page theme for links to your works", + "images": { + "hires": "https://www.jamstackthemes.dev/capture/zzzmisa-hugo-theme-doors.png", + "thumbnail": "https://www.jamstackthemes.dev/images/theme/thumbnail/zzzmisa-hugo-theme-doors.jpg", + "screenshot": "https://www.jamstackthemes.dev/images/theme/thumbnail/2x/zzzmisa-hugo-theme-doors-2x.jpg" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..3274d02 --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "scripts": { + "build": "node ./scripts/build.js && hugo --minify", + "deploy": "node ./scripts/build.js --latest && hugo --minify", + "stackbit": "node ./scripts/build.js --github=false --demos=false --images=false && hugo --minify", + "images": "node ./scripts/build.js --github=false --demos=false --stackbit=false && hugo --minify", + "deploy:branch": "npm run deploy && echo 'User-agent: *\nDisallow: /' > public/robots.txt" + }, + "dependencies": { + "axios": "^0.21.1", + "axios-rate-limit": "^1.3.0", + "cli-spinners": "^2.5.0", + "date-fns": "^2.16.1", + "front-matter": "^4.0.2", + "fs-extra": "^9.1.0", + "js-yaml": "^4.0.0", + "ora": "^5.3.0", + "pageres": "^6.1.0", + "parse-github-url": "^1.0.2", + "promise.allsettled": "^1.0.4", + "sharp": "^0.30.5", + "url-slug": "^3.0.1", + "yaml-front-matter": "^4.1.1", + "yargs": "^16.2.0" + } +} diff --git a/scripts/build.js b/scripts/build.js new file mode 100644 index 0000000..e92a21f --- /dev/null +++ b/scripts/build.js @@ -0,0 +1,59 @@ +#!/usr/bin/env node +const yargs = require('yargs/yargs') +const { hideBin } = require('yargs/helpers') +const argv = yargs(hideBin(process.argv)).argv +const {generateGithubData} = require('./build/github'); +const {generateMarkdownData} = require('./build/markdown'); +const {testDemoUrls} = require('./build/demo'); +const {writeThemesFile, writeErrorFile, writeStackbitFile} = require('./build/write') +const {generateStackbitData} = require('./build/stackbit') +const {generateScreenshots, generateThumbnails} = require('./build/screenshot') +const config = require('./build/config'); +const {errorLog} = require('./build/errors'); + +const build = async (options) => { + const themesMarkdown = await generateMarkdownData(config.themesMarkdownFiles, options) + + if (options.github) { + // Updates themes.json + const themesGithub = await generateGithubData(themesMarkdown) + writeThemesFile(themesGithub) + } + if (options.stackbit) { + // Updates stackbit.json + const stackbit = generateStackbitData(themesMarkdown) + writeStackbitFile(stackbit); + } + + if (options.demos) { + // Visits each demo URL with a headless browser and if the site cannot be reached sets the markdown file to disabled=true + await testDemoUrls(themesMarkdown) + } + + if (options.images) { + await generateScreenshots(themesMarkdown, options.recaptureImage) + await generateThumbnails(themesMarkdown, options.regenerateImage) + } + + writeErrorFile(errorLog) + console.log("Build Successful") + console.log("Error Log") + process.exit(0) +} + +const options = { + github: argv.github !== "false", // generates `data/themes.json` which is used for github stars, commits meta data etc + stackbit: argv.stackbit !== "false", // generates `data/stackbit.json` which is used for the "create site" button + demos: argv.demos !== "false", // tests the themes demo url using headless browser and disables the theme if the url does not resolve + images: argv.images !== "false", // captures screenshots and generates thumbnails + + disabled: argv.disabled !== "false", // Skip processing themes that have front-matter `disabled: true` + draft: argv.draft !== "false", // Skip processing themes that have front-matter `draft: true` + latest: argv.latest || false, // Only process themes which don't already exist in `data/themes.json` + file: argv.file || false, // Only process a single file ie --file=gatsby-starter-advanced.md + all: argv.all || false, // process all themes + recaptureImage: argv.recaptureImage || false, + regenerateImage: argv.regenerateImage || false +} + +build(options); \ No newline at end of file diff --git a/scripts/build/cleanup.js b/scripts/build/cleanup.js new file mode 100644 index 0000000..886e93e --- /dev/null +++ b/scripts/build/cleanup.js @@ -0,0 +1,23 @@ +#!/usr/bin/env node +const fs = require('fs'); +const path = require('path'); +const yamlFront = require('yaml-front-matter'); +const yaml = require('js-yaml'); +const isBefore = require('date-fns/isBefore'); +const parseISO = require('date-fns/parseISO'); +const subYears = require('date-fns/subYears'); +const {getThemeKey} = require('./utils'); +const {themeMap} = require('./write'); +const {generateMarkdownData} = require('./markdown') +const config = require('./config'); +const staleBeforeDate = subYears(new Date(), 1); + +const deleteOldThemes = async (themesMarkdown, themesGithub) => { + const themesMarkdown = await generateMarkdownData(config.themesMarkdownFiles) + const themesMarkdownMap = themeMap(themesMarkdown) +} + +module.exports = { + deleteOldThemes +} + diff --git a/scripts/build/config.js b/scripts/build/config.js new file mode 100644 index 0000000..e48700c --- /dev/null +++ b/scripts/build/config.js @@ -0,0 +1,30 @@ +#!/usr/bin/env node +const fs = require('fs'); +const path = require('path'); +const subYears = require('date-fns/subYears'); + +const errorJsonFile = path.join(process.cwd(), '/data/errors.json'); +const stackbitJsonFile = path.join(process.cwd(), '/data/stackbit.json'); +const stackbitJsonData = fs.existsSync(stackbitJsonFile) ? JSON.parse(fs.readFileSync(stackbitJsonFile)) : {}; +const themesMarkdownFolder = path.join(process.cwd(), '/content/theme') +const themesMarkdownFiles = fs.readdirSync(themesMarkdownFolder).map(relFilename => path.resolve(themesMarkdownFolder, relFilename)) +const themesJsonFile = path.join(process.cwd(), '/data/themes.json'); +const themesJsonData = fs.existsSync(themesJsonFile) ? JSON.parse(fs.readFileSync(themesJsonFile)) : {}; +const hiresImagesFolder = path.join(process.cwd(), '/static/capture'); +const thumbnailImagesFolder = path.join(process.cwd(), '/static/images/theme/thumbnail') +const thumbnailImagesFolder2x = path.join(process.cwd(), '/static/images/theme/thumbnail/2x') +const staleBeforeDate = subYears(new Date(), 1); + +module.exports = { + errorJsonFile, + themesJsonFile, + themesMarkdownFolder, + themesMarkdownFiles, + themesJsonData, + stackbitJsonFile, + stackbitJsonData, + hiresImagesFolder, + thumbnailImagesFolder, + thumbnailImagesFolder2x, + staleBeforeDate +} \ No newline at end of file diff --git a/scripts/build/demo.js b/scripts/build/demo.js new file mode 100644 index 0000000..3ffb388 --- /dev/null +++ b/scripts/build/demo.js @@ -0,0 +1,67 @@ +#!/usr/bin/env node +const axios = require('axios'); +const allSettled = require('promise.allsettled'); +const {errorLog} = require('./errors') +const ora = require('ora'); +const {getThemeKey, getRepoName} = require('./utils'); +const {updateFrontmatter} = require('./markdown'); +const spinner = ora('Loading') + +const testDemo = (frontmatter) => { + const themeKey = getThemeKey(frontmatter.github) + + return axios.get(frontmatter.demo).then((res) => { + spinner.text = `${frontmatter.demo} => checking Demo URL - ${res.status}`; + if (frontmatter.disabled) { + updateFrontmatter(frontmatter.file, { + disabled: false, + disabled_reason: "" + }) + } + return true; + }).catch(err => { + let error = "error checking demo url" + if (err) { + if (err.code === "ENOTFOUND") { + error = "demo url not found" + } + if (err.code === "ECONNREFUSED") { + error = "demo url connection refused" + } + if (err.response) { + if (err.response.status === 404) { + error = "demo url not found" + } + if (err.response.data) { + if (err.response.data.message) { + error = err.response.data.message; + } + } + + } + } + updateFrontmatter(frontmatter.file, { + disabled: true, + disabled_reason: error + }) + spinner.text = `${frontmatter.demo} => checking Demo URL - ${error}`; + errorLog.push({ + theme_key: themeKey, + file: frontmatter.file, + repoUrl: frontmatter.github, + demoUrl: frontmatter.demo, + error + }) + }); +} + +const testDemoUrls = async (themesMarkdown) => { + spinner.start("Testing Demo URL's"); + await allSettled(themesMarkdown.map(theme => testDemo(theme))) + spinner.succeed("Success - Testing Demo URL's"); +} + + +module.exports = { + testDemoUrls +} \ No newline at end of file diff --git a/scripts/build/errors.js b/scripts/build/errors.js new file mode 100644 index 0000000..d807214 --- /dev/null +++ b/scripts/build/errors.js @@ -0,0 +1,6 @@ +#!/usr/bin/env node +let errorLog = [] + +module.exports = { + errorLog +} diff --git a/scripts/build/github.js b/scripts/build/github.js new file mode 100755 index 0000000..31a0811 --- /dev/null +++ b/scripts/build/github.js @@ -0,0 +1,135 @@ +#!/usr/bin/env node +const axios = require('axios'); +const allSettled = require('promise.allsettled'); +const rateLimit = require('axios-rate-limit'); +const isBefore = require('date-fns/isBefore'); +const parseISO = require('date-fns/parseISO'); +const formatDistanceToNow = require('date-fns/formatDistanceToNow') +const ora = require('ora'); +const {errorLog} = require('./errors'); +const {getThemeKey, getRepoName} = require('./utils'); +const {updateFrontmatter} = require('./markdown') +const config = require('./config'); + +if (!process.env.GITHUB_TOKEN) { + throw new Error( + 'Cannot access Github API - environment variable "GITHUB_TOKEN" is missing' + ) +} + +const token = process.env.GITHUB_TOKEN; +const axiosLimit = rateLimit(axios.create(), {maxRequests: 2, perMilliseconds: 200}) +const spinner = ora('Loading') + +const fetchRepoData = async (frontmatter) => { + + const themeKey = getThemeKey(frontmatter.github) + const repoName = getRepoName(frontmatter.github) + + try { + const res = await axiosLimit.get( + `https://api.github.com/repos/${repoName}`, + { + headers: { + Authorization: `Token ${token}`, + }, + }) + spinner.text = `${frontmatter.file} => ${res.data.html_url} - ${res.status}` + const lastCommit = await fetchBranchData(repoName, res.data.default_branch); + updateFrontmatter(frontmatter.file, { + stale: isBefore(parseISO(lastCommit), config.staleBeforeDate) + }) + if (frontmatter.disabled) { + updateFrontmatter(frontmatter.file, { + disabled: false, + disabled_reason: "" + }) + } + + return { + theme_key: themeKey, + file: frontmatter.file, + name: res.data.name, + title: frontmatter.title, + github_username: res.data.owner.login, + repo: res.data.full_name, + branch: res.data.default_branch, + default_branch: res.data.default_branch, + github_url: res.data.html_url, + demo_url: frontmatter.demo, + stars: res.data.stargazers_count, + forks: res.data.forks_count, + open_issues: res.data.open_issues_count, + last_commit: lastCommit, + created_at: res.data.created_at, + description: res.data.description, + images: { + hires: `https://www.jamstackthemes.dev/capture/${themeKey}.png`, + thumbnail: `https://www.jamstackthemes.dev/images/theme/thumbnail/${themeKey}.jpg`, + screenshot: `https://www.jamstackthemes.dev/images/theme/thumbnail/2x/${themeKey}-2x.jpg` + } + } + } catch (err) { + const status = err.response?.status; + let error = `Github repo not found, status: ${status}`; + if (status === 404) { + updateFrontmatter(frontmatter.file, { + disabled: true, + disabled_reason: error + }) + } + spinner.text = `${frontmatter.file} => ${error}` + errorLog.push({ + theme_key: themeKey, + file: frontmatter.file, + repoUrl: frontmatter.github, + error + }) + throw err + } +} + +const fetchBranchData = (repo, branch) => { + return axiosLimit.get( + `https://api.github.com/repos/${repo}/branches/${branch}`, + { + headers: { + Authorization: `Token ${token}`, + }, + }).then((res) => { + const lastCommit = res.data.commit.commit.author.date; + const lastCommitToNow = formatDistanceToNow(parseISO(lastCommit)); + spinner.text = `${repo} => last commit to branch '${branch}' ${lastCommitToNow}` + return lastCommit; + }).catch((err) => { + throw err + }); +} + +const generateGithubData = async (markdownData, themesJsonData) => { + spinner.start("Fetching Github Data"); + const initalThemes = config.themesJsonData; + const update = await allSettled(markdownData.map(frontmatter => fetchRepoData(frontmatter))) + const updatedThemesArray = update.filter(res => res.status === 'fulfilled').map(res => res.value) + const mergedThemesMap = updatedThemesArray.reduce((accumulator, theme) => { + if (theme === undefined) { + // TODO find root cause + console.log("undefined theme detected"); + return { + ...accumulator + }; + } + const themeKey = getThemeKey(theme.github_url) + return { + ...accumulator, + [themeKey]: theme + }; + }, initalThemes); + + spinner.succeed("Success - Fetching Github Data"); + return mergedThemesMap +} + +module.exports = { + generateGithubData +} \ No newline at end of file diff --git a/scripts/build/markdown.js b/scripts/build/markdown.js new file mode 100644 index 0000000..aa53276 --- /dev/null +++ b/scripts/build/markdown.js @@ -0,0 +1,133 @@ +#!/usr/bin/env node +const fs = require('fs'); +const path = require('path'); +const yamlFront = require('yaml-front-matter'); +const yaml = require('js-yaml'); +const {getThemeKey} = require('./utils'); +const config = require('./config'); + +const updateFrontmatter = (file, update = {}) => { + const absFilepath = path.resolve(config.themesMarkdownFolder, file) + const fileData = fs.existsSync(absFilepath) ? fs.readFileSync(absFilepath) : null; + + let frontmatter = yamlFront.loadFront(fileData); + let content = frontmatter.__content; + delete frontmatter.__content; + + Object.keys(update).forEach((key, index) => { + frontmatter[key] = update[key] + }); + + const fm = `---\n${yaml.dump(frontmatter)}---${content}`; + + fs.writeFileSync(absFilepath, fm); +} + +const loadThemeFrontMatter = absFilename => { + const fileData = fs.readFileSync(absFilename); + const frontmatter = yamlFront.loadFront(fileData); + + if (!frontmatter.github) { + const file = path.parse(absFilename).base; + if (file === ".DS_Store") { + fs.unlinkSync(absFilename) + throw new Error(`${absFilename} invalid file. This file was deleted, try again...`) + } + throw new Error(`${absFilename} invalid github frontmatter`) + } + + let title = frontmatter.title; + let description = frontmatter.description; + let draft = frontmatter.draft; + let disabled = frontmatter.disabled; + let github = frontmatter.github; + let demo = frontmatter.demo; + let github_branch = frontmatter.github_branch; + let file = path.parse(absFilename).base; + let absFile = absFilename + let ssg = frontmatter.ssg + let cms = frontmatter.cms + return { + title, + description, + draft, + disabled, + github, + demo, + github_branch, + file, + absFile, + ssg, + cms + } + +}; + +const generateMarkdownData = async (markdownFiles, options = {}) => { + + let filterCounts = { + draft: 0, + disabled: 0, + latest: 0, + skipped: 0 + } + + const markdownData = markdownFiles.map(absFilename => loadThemeFrontMatter(absFilename)).filter(frontmatter => { + + if (options.file) { + // if the cli command --file=hugo-swift-theme.md is used, only fetch that specific theme + if (options.file !== frontmatter.file) { + filterCounts.skipped += 1 + return false + } + return true; + } + + if (options.latest) { + // if the cli command --latest is used, only fetch new themes which don't already exist in `data/themes.json` + const themeKey = getThemeKey(frontmatter.github) + if (config.themesJsonData[themeKey]) { + filterCounts.latest += 1 + return false + } + return true + } + + if (options.disabled) { + if (frontmatter.disabled) { + filterCounts.disabled += 1 + return false; + } + } + if (options.draft) { + if (frontmatter.draft) { + filterCounts.draft += 1 + return false; + } + } + + return true; + }); + + + if (options.file) { + console.log(`Processing single theme - ${options.file}`) + } else if (options.latest) { + console.log(`Processing latest themes`) + } else { + console.log(`Processing all themes`) + console.log(`Total ${markdownFiles.length} themes`) + console.log(`Skipping ${filterCounts.disabled} disabled themes`) + console.log(`Skipping ${filterCounts.draft} draft themes`) + console.log(`Skipping ${filterCounts.latest} existing themes`) + console.log(`Processing ${markdownData.length}/${markdownFiles.length} themes...`) + } + + return markdownData; +} + +module.exports = { + generateMarkdownData, + updateFrontmatter +} + diff --git a/scripts/build/screenshot.js b/scripts/build/screenshot.js new file mode 100644 index 0000000..56bb0ce --- /dev/null +++ b/scripts/build/screenshot.js @@ -0,0 +1,99 @@ +#!/usr/bin/env node +const fs = require('fs-extra'); +const path = require('path'); +const Pageres = require('pageres'); +const sharp = require('sharp') +const {getThemeKey} = require('./utils'); +const config = require('./config'); +const ora = require('ora'); +const spinner = ora('Loading') + +const captureScreenshot = async (frontmatter, overwrite = false) => { + const themeKey = getThemeKey(frontmatter.github) + const themeImage = `${themeKey}.png` + + if (!overwrite && fs.existsSync(path.join(config.hiresImagesFolder, themeImage))) { + return false + } + + try { + const page = await new Pageres({delay: 2, filename: themeKey}) + .src(frontmatter.demo, ['1280x960'], {crop: true}) + .dest(config.hiresImagesFolder) + .run(); + spinner.text = `${frontmatter.demo} => capturing` + return page + } catch { + spinner.text = `${frontmatter.demo} => failed capturing` + return false; + } +}; + +const generateThumbnail = async (frontmatter, overwrite = false) => { + const themeKey = getThemeKey(frontmatter.github) + const hiresImage = path.join(config.hiresImagesFolder, `${themeKey}.png`) + const imageName = path.parse(hiresImage).name + const outputImage = path.join(config.thumbnailImagesFolder, `${imageName}.jpg`) + const outputImage2x = path.join(config.thumbnailImagesFolder2x, `${imageName}-2x.jpg`) + + if (!hiresImage) { + return false; + } + if (!overwrite && fs.existsSync(outputImage) && fs.existsSync(outputImage2x)) { + return false; + } + fs.ensureDirSync(config.thumbnailImagesFolder); + fs.ensureDirSync(config.thumbnailImagesFolder2x); + + try { + spinner.text = `${imageName} => processing thumbnail` + await sharp(hiresImage) + .resize({ + width: 280, + height: 210, + fit: 'cover', + position: 'top', + }) + .jpeg({ + quality: 85, + }) + .toFile(outputImage) + + spinner.text = `${imageName} => processing thumbnail@2x` + await sharp(hiresImage) + .resize({ + width: 560, + height: 420, + fit: 'cover', + position: 'top', + }) + .jpeg({ + quality: 85, + }) + .toFile(outputImage2x) + } catch { + spinner.text = `${imageName} => processing failed` + return false; + } +}; + +const generateThumbnails = async (markdownData, overwrite) => { + spinner.start("Generating Thumbnails"); + for (const theme of markdownData) { + await generateThumbnail(theme, overwrite) + } + spinner.succeed("Success - Generating Thumbnails"); +}; + +const generateScreenshots = async (markdownData, overwrite) => { + spinner.start("Capturing Screenshots"); + for (const theme of markdownData) { + await captureScreenshot(theme, overwrite) + } + spinner.succeed("Success - Capturing Screenshots"); +} + +module.exports = { + generateScreenshots, + generateThumbnails +} \ No newline at end of file diff --git a/scripts/build/stackbit.js b/scripts/build/stackbit.js new file mode 100755 index 0000000..1ac4936 --- /dev/null +++ b/scripts/build/stackbit.js @@ -0,0 +1,91 @@ +#!/usr/bin/env node +const urlSlug = require('url-slug'); +const {getThemeKey, getRepoName} = require('./utils'); +const ora = require('ora'); +const spinner = ora('Loading') +const allowedSsg = [] +const allowedCms = [] + +const generateStackbit = (frontmatter) => { + const themeKey = getThemeKey(frontmatter.github) + + spinner.text = `${frontmatter.file}` + + let stackbitData = { + theme_key: themeKey + }; + + const ssgArray = frontmatter.ssg || [] + const cmsArray = frontmatter.cms || [] + + if (ssgArray.some(ssg => allowedSsg.includes(urlSlug(ssg))) && cmsArray.some(cms => allowedCms.includes(urlSlug(cms)))) { + if (ssgArray.length > 1) { + stackbitData.createUrl = `https://app.stackbit.com/create?theme=${frontmatter.github}` + } else { + stackbitData.createUrl = `https://app.stackbit.com/create?theme=${frontmatter.github}&ssg=${urlSlug(ssgArray)}` + } + } else { + stackbitData.createUrl = null; + } + + // manual overrides + const manualDisabled = [ + 'https://github.com/runbytech/gatsby-theme-ultronele', + 'https://github.com/YoussefRaafatNasry/portfolYOU', + 'https://github.com/qwtel/hydejack', + 'https://github.com/joshgerdes/jekyll-uno', + 'https://github.com/GDGToulouse/devfest-theme-hugo', + 'https://github.com/h-enk/doks', + 'https://github.com/ahmadiqbal1/jekyll-webpack-boilerplate', + 'https://github.com/SupunKavinda/jekyll-theme-leaf.git', + 'https://github.com/techonomics69/gatsby-netlify-form-example', + 'https://github.com/lawrencecchen/headless-comments', + 'https://github.com/Joy3Luo/Joy3luo.github.io', + 'https://github.com/chrisbobbe/jekyll-theme-prologue', + 'https://github.com/adityatelange/hugo-PaperMod/', + 'https://github.com/Kentico/kontent-jekyll-blog', + 'https://github.com/longpdo/neumorphism', + 'https://github.com/puresyntax71/hugo-theme-chunky-poster', + 'https://github.com/sharadcodes/jekyll-theme-dark-reader', + 'https://github.com/wkocjan/gatsby-theme-intro', + 'https://github.com/jameshamann/jekyll-material-theme', + 'https://github.com/fncnt/vncnt-hugo', + 'https://github.com/wizlee/gatsby-portfolio', + 'https://github.com/EmaSuriano/gatsby-starter-mate', + 'https://github.com/guangmean/Niello', + 'https://github.com/fabien0102/gatsby-starter', + 'https://github.com/netlify-templates/one-click-hugo-cms' + + ]; + manualDisabled.forEach(url => { + if (url === frontmatter.github) { + if (stackbitData.createUrl) { + delete stackbitData.createUrl + } + } + }) + const manualEnabled = ['https://github.com/stackbit-themes/minimal-nextjs-theme'] + manualEnabled.forEach(url => { + if (url === frontmatter.github) { + stackbitData.createUrl = `https://app.stackbit.com/create?theme=${frontmatter.github}&ssg=${urlSlug(frontmatter.ssg)}&cms=${urlSlug(frontmatter.cms)}` + } + }) + if (stackbitData.createUrl) { + return stackbitData; + } + return false; + +}; + +const generateStackbitData = (markdownData) => { + spinner.start("Fetching Stackbit Data"); + const stackbitData = markdownData.map(theme => { + return generateStackbit(theme) + }).filter(stackbit => stackbit.createUrl); + spinner.succeed("Success - Fetching Stackbit Data"); + return stackbitData; +}; + +module.exports = { + generateStackbitData +} \ No newline at end of file diff --git a/scripts/build/utils.js b/scripts/build/utils.js new file mode 100644 index 0000000..301e492 --- /dev/null +++ b/scripts/build/utils.js @@ -0,0 +1,20 @@ +#!/usr/bin/env node +const parseGithubUrl = require('parse-github-url'); + +const getRepoName = (repoUrl) => { + return parseGithubUrl(repoUrl).repo; // stackbithq/stackbit-theme-fresh +} + +const getThemeKey = (repoUrl) => { + if (!repoUrl) { + throw Error("Unable to generate themeKey") + } + const repoName = getRepoName(repoUrl) + const themeKey = repoName.replace("/", "-").toLowerCase(); + return themeKey +} + +module.exports = { + getThemeKey, + getRepoName +} \ No newline at end of file diff --git a/scripts/build/write.js b/scripts/build/write.js new file mode 100644 index 0000000..124b409 --- /dev/null +++ b/scripts/build/write.js @@ -0,0 +1,53 @@ +#!/usr/bin/env node +const fs = require('fs'); +const config = require('./config') +const {getThemeKey} = require('./utils'); + +const writeThemesFile = (themes) => { + // Load existing themes.json and update it + let themesData = themes + // themes.forEach(theme => { + // themesData[theme.theme_key] = theme; + // }) + let sortedThemesData = {} + Object.keys(themesData).sort().forEach(key => { + sortedThemesData[key] = themesData[key]; + }); + console.log(`Writing ${config.themesJsonFile}...`); + fs.writeFileSync(config.themesJsonFile, JSON.stringify(sortedThemesData, null, 2)); +} + +const writeErrorFile = (errorLog) => { + console.log(`Writing ${config.errorJsonFile}...`); + fs.writeFileSync(config.errorJsonFile, JSON.stringify(errorLog, null, 2)); +} + +const writeStackbitFile = (stackbit) => { + let stackbitData = config.stackbitJsonData + stackbit.forEach(theme => { + stackbitData[theme.theme_key] = theme; + }) + let sortedStackbitData = {} + Object.keys(stackbitData).sort().forEach(key => { + sortedStackbitData[key] = stackbitData[key]; + }); + console.log(`Writing ${config.stackbitJsonFile}...`); + fs.writeFileSync(config.stackbitJsonFile, JSON.stringify(sortedStackbitData, null, 2)); +} + +const themeMap = (themes) => { + return themes.reduce((accumulator, theme) => { + const themeKey = getThemeKey(theme.github) + return { + ...accumulator, + [themeKey]: theme + }; + }); +} + +module.exports = { + writeThemesFile, + writeErrorFile, + writeStackbitFile, + themeMap +} diff --git a/static/images/banner/bg.svg b/static/images/banner/bg.svg new file mode 100644 index 0000000..67d2e63 --- /dev/null +++ b/static/images/banner/bg.svg @@ -0,0 +1,188 @@ + + diff --git a/static/images/banner/netlify-create.svg b/static/images/banner/netlify-create.svg new file mode 100644 index 0000000..196f932 --- /dev/null +++ b/static/images/banner/netlify-create.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/default-large.png b/static/images/default-large.png new file mode 100644 index 0000000..8b93be9 Binary files /dev/null and b/static/images/default-large.png differ diff --git a/static/images/default.png b/static/images/default.png new file mode 100644 index 0000000..9892dcb Binary files /dev/null and b/static/images/default.png differ diff --git a/static/images/favicon/02_QUIQR-icon-logo.png b/static/images/favicon/02_QUIQR-icon-logo.png new file mode 100644 index 0000000..9cf71e1 Binary files /dev/null and b/static/images/favicon/02_QUIQR-icon-logo.png differ diff --git a/static/images/favicon/favicon-32x32.png b/static/images/favicon/favicon-32x32.png new file mode 100755 index 0000000..cfa1db9 Binary files /dev/null and b/static/images/favicon/favicon-32x32.png differ diff --git a/static/images/icons/admin.svg b/static/images/icons/admin.svg new file mode 100644 index 0000000..c431d6d --- /dev/null +++ b/static/images/icons/admin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/airtable.svg b/static/images/icons/airtable.svg new file mode 100755 index 0000000..2d9fb2e --- /dev/null +++ b/static/images/icons/airtable.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/algolia.svg b/static/images/icons/algolia.svg new file mode 100644 index 0000000..e7a5933 --- /dev/null +++ b/static/images/icons/algolia.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/astro.svg b/static/images/icons/astro.svg new file mode 100644 index 0000000..851e911 --- /dev/null +++ b/static/images/icons/astro.svg @@ -0,0 +1,4 @@ + diff --git a/static/images/icons/blog.svg b/static/images/icons/blog.svg new file mode 100644 index 0000000..5618ad8 --- /dev/null +++ b/static/images/icons/blog.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/bootstrap.svg b/static/images/icons/bootstrap.svg new file mode 100644 index 0000000..f64b10a --- /dev/null +++ b/static/images/icons/bootstrap.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/brunch.svg b/static/images/icons/brunch.svg new file mode 100644 index 0000000..c7f931d --- /dev/null +++ b/static/images/icons/brunch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/bulma.svg b/static/images/icons/bulma.svg new file mode 100644 index 0000000..badd355 --- /dev/null +++ b/static/images/icons/bulma.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/business.svg b/static/images/icons/business.svg new file mode 100644 index 0000000..74e13dc --- /dev/null +++ b/static/images/icons/business.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/cecil.svg b/static/images/icons/cecil.svg new file mode 100644 index 0000000..3ce9daa --- /dev/null +++ b/static/images/icons/cecil.svg @@ -0,0 +1,33 @@ + \ No newline at end of file diff --git a/static/images/icons/contentful.svg b/static/images/icons/contentful.svg new file mode 100644 index 0000000..0802a23 --- /dev/null +++ b/static/images/icons/contentful.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/cosmic.svg b/static/images/icons/cosmic.svg new file mode 100644 index 0000000..00d5f9d --- /dev/null +++ b/static/images/icons/cosmic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/css.svg b/static/images/icons/css.svg new file mode 100644 index 0000000..e407677 --- /dev/null +++ b/static/images/icons/css.svg @@ -0,0 +1,23 @@ + + + \ No newline at end of file diff --git a/static/images/icons/datocms.svg b/static/images/icons/datocms.svg new file mode 100644 index 0000000..c9d4e1b --- /dev/null +++ b/static/images/icons/datocms.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/directus.svg b/static/images/icons/directus.svg new file mode 100644 index 0000000..0a9437c --- /dev/null +++ b/static/images/icons/directus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/documentation.svg b/static/images/icons/documentation.svg new file mode 100644 index 0000000..51c833e --- /dev/null +++ b/static/images/icons/documentation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/docusaurus.svg b/static/images/icons/docusaurus.svg new file mode 100644 index 0000000..5d13ed9 --- /dev/null +++ b/static/images/icons/docusaurus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/ecommerce.svg b/static/images/icons/ecommerce.svg new file mode 100644 index 0000000..8b4edcc --- /dev/null +++ b/static/images/icons/ecommerce.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/eleventy.svg b/static/images/icons/eleventy.svg new file mode 100644 index 0000000..8b3c7dc --- /dev/null +++ b/static/images/icons/eleventy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/event.svg b/static/images/icons/event.svg new file mode 100644 index 0000000..9fb4228 --- /dev/null +++ b/static/images/icons/event.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/firebase.svg b/static/images/icons/firebase.svg new file mode 100644 index 0000000..4069cf5 --- /dev/null +++ b/static/images/icons/firebase.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/flotiq.svg b/static/images/icons/flotiq.svg new file mode 100644 index 0000000..aaaaf01 --- /dev/null +++ b/static/images/icons/flotiq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/forestry.svg b/static/images/icons/forestry.svg new file mode 100644 index 0000000..c3b2790 --- /dev/null +++ b/static/images/icons/forestry.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/formspree.svg b/static/images/icons/formspree.svg new file mode 100644 index 0000000..1795c5a --- /dev/null +++ b/static/images/icons/formspree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/formstack.svg b/static/images/icons/formstack.svg new file mode 100644 index 0000000..2a970e7 --- /dev/null +++ b/static/images/icons/formstack.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/gatsby-bw.svg b/static/images/icons/gatsby-bw.svg new file mode 100644 index 0000000..2dde241 --- /dev/null +++ b/static/images/icons/gatsby-bw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/gatsby.svg b/static/images/icons/gatsby.svg new file mode 100755 index 0000000..11ef284 --- /dev/null +++ b/static/images/icons/gatsby.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/ghost.svg b/static/images/icons/ghost.svg new file mode 100644 index 0000000..51717cc --- /dev/null +++ b/static/images/icons/ghost.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/github.svg b/static/images/icons/github.svg new file mode 100644 index 0000000..ab25bc2 --- /dev/null +++ b/static/images/icons/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/graphcms.svg b/static/images/icons/graphcms.svg new file mode 100644 index 0000000..6f2e8cd --- /dev/null +++ b/static/images/icons/graphcms.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/gridsome.svg b/static/images/icons/gridsome.svg new file mode 100644 index 0000000..7808590 --- /dev/null +++ b/static/images/icons/gridsome.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/hexo.svg b/static/images/icons/hexo.svg new file mode 100755 index 0000000..43322e5 --- /dev/null +++ b/static/images/icons/hexo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/hugo-bw.svg b/static/images/icons/hugo-bw.svg new file mode 100644 index 0000000..9d788aa --- /dev/null +++ b/static/images/icons/hugo-bw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/hugo.svg b/static/images/icons/hugo.svg new file mode 100644 index 0000000..fb55f51 --- /dev/null +++ b/static/images/icons/hugo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/hyde.svg b/static/images/icons/hyde.svg new file mode 100644 index 0000000..67add81 --- /dev/null +++ b/static/images/icons/hyde.svg @@ -0,0 +1,24 @@ + \ No newline at end of file diff --git a/static/images/icons/jekyll.svg b/static/images/icons/jekyll.svg new file mode 100644 index 0000000..5e8d293 --- /dev/null +++ b/static/images/icons/jekyll.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/jigsaw.svg b/static/images/icons/jigsaw.svg new file mode 100644 index 0000000..6721365 --- /dev/null +++ b/static/images/icons/jigsaw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/kontent.svg b/static/images/icons/kontent.svg new file mode 100644 index 0000000..0e1f1ec --- /dev/null +++ b/static/images/icons/kontent.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/mailchimp.svg b/static/images/icons/mailchimp.svg new file mode 100644 index 0000000..c1bf3f1 --- /dev/null +++ b/static/images/icons/mailchimp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/material-ui.svg b/static/images/icons/material-ui.svg new file mode 100644 index 0000000..d459d28 --- /dev/null +++ b/static/images/icons/material-ui.svg @@ -0,0 +1,9 @@ + + diff --git a/static/images/icons/metalsmith.svg b/static/images/icons/metalsmith.svg new file mode 100644 index 0000000..31d5094 --- /dev/null +++ b/static/images/icons/metalsmith.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/middleman.svg b/static/images/icons/middleman.svg new file mode 100644 index 0000000..be9676c --- /dev/null +++ b/static/images/icons/middleman.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/mkdocs.svg b/static/images/icons/mkdocs.svg new file mode 100644 index 0000000..fdaa7c6 --- /dev/null +++ b/static/images/icons/mkdocs.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/netlify.svg b/static/images/icons/netlify.svg new file mode 100644 index 0000000..eb958a2 --- /dev/null +++ b/static/images/icons/netlify.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/netlifycms.svg b/static/images/icons/netlifycms.svg new file mode 100644 index 0000000..b307698 --- /dev/null +++ b/static/images/icons/netlifycms.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/next.svg b/static/images/icons/next.svg new file mode 100644 index 0000000..2bf7c1e --- /dev/null +++ b/static/images/icons/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/no-cms.svg b/static/images/icons/no-cms.svg new file mode 100644 index 0000000..faf37ad --- /dev/null +++ b/static/images/icons/no-cms.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/nocms.svg b/static/images/icons/nocms.svg new file mode 100644 index 0000000..faf37ad --- /dev/null +++ b/static/images/icons/nocms.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/notion.svg b/static/images/icons/notion.svg new file mode 100644 index 0000000..815ecd9 --- /dev/null +++ b/static/images/icons/notion.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/nuxt.svg b/static/images/icons/nuxt.svg new file mode 100644 index 0000000..5db0a2c --- /dev/null +++ b/static/images/icons/nuxt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/pelican.svg b/static/images/icons/pelican.svg new file mode 100644 index 0000000..be9cbec --- /dev/null +++ b/static/images/icons/pelican.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/personal.svg b/static/images/icons/personal.svg new file mode 100644 index 0000000..2782104 --- /dev/null +++ b/static/images/icons/personal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/platframe.svg b/static/images/icons/platframe.svg new file mode 100644 index 0000000..3d744c7 --- /dev/null +++ b/static/images/icons/platframe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/portfolio.svg b/static/images/icons/portfolio.svg new file mode 100644 index 0000000..8983bb4 --- /dev/null +++ b/static/images/icons/portfolio.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/postcss.svg b/static/images/icons/postcss.svg new file mode 100644 index 0000000..c416420 --- /dev/null +++ b/static/images/icons/postcss.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/prismic.svg b/static/images/icons/prismic.svg new file mode 100644 index 0000000..5d14f01 --- /dev/null +++ b/static/images/icons/prismic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/sanity.svg b/static/images/icons/sanity.svg new file mode 100644 index 0000000..0836236 --- /dev/null +++ b/static/images/icons/sanity.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/sapper.svg b/static/images/icons/sapper.svg new file mode 100644 index 0000000..8f32c37 --- /dev/null +++ b/static/images/icons/sapper.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/sass.svg b/static/images/icons/sass.svg new file mode 100644 index 0000000..ca32b98 --- /dev/null +++ b/static/images/icons/sass.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/scss.svg b/static/images/icons/scss.svg new file mode 100644 index 0000000..ca32b98 --- /dev/null +++ b/static/images/icons/scss.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/scully.svg b/static/images/icons/scully.svg new file mode 100644 index 0000000..8f71b32 --- /dev/null +++ b/static/images/icons/scully.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/shopify.svg b/static/images/icons/shopify.svg new file mode 100644 index 0000000..5072635 --- /dev/null +++ b/static/images/icons/shopify.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/single-page.svg b/static/images/icons/single-page.svg new file mode 100644 index 0000000..2ef5ae2 --- /dev/null +++ b/static/images/icons/single-page.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/snipcart.svg b/static/images/icons/snipcart.svg new file mode 100644 index 0000000..c81942b --- /dev/null +++ b/static/images/icons/snipcart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/stackbit.svg b/static/images/icons/stackbit.svg new file mode 100644 index 0000000..2a6dec8 --- /dev/null +++ b/static/images/icons/stackbit.svg @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/static/images/icons/staticman.svg b/static/images/icons/staticman.svg new file mode 100644 index 0000000..010d28d --- /dev/null +++ b/static/images/icons/staticman.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/statiq.svg b/static/images/icons/statiq.svg new file mode 100644 index 0000000..d97f433 --- /dev/null +++ b/static/images/icons/statiq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/storyblok.svg b/static/images/icons/storyblok.svg new file mode 100644 index 0000000..9e5c256 --- /dev/null +++ b/static/images/icons/storyblok.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/styled-components.png b/static/images/icons/styled-components.png new file mode 100644 index 0000000..e20a0b2 Binary files /dev/null and b/static/images/icons/styled-components.png differ diff --git a/static/images/icons/stylus.svg b/static/images/icons/stylus.svg new file mode 100644 index 0000000..259921c --- /dev/null +++ b/static/images/icons/stylus.svg @@ -0,0 +1,79 @@ + + diff --git a/static/images/icons/sveltekit.svg b/static/images/icons/sveltekit.svg new file mode 100644 index 0000000..e1734cd --- /dev/null +++ b/static/images/icons/sveltekit.svg @@ -0,0 +1,7 @@ + + diff --git a/static/images/icons/tailwind.svg b/static/images/icons/tailwind.svg new file mode 100644 index 0000000..2b1a4d6 --- /dev/null +++ b/static/images/icons/tailwind.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/tina.svg b/static/images/icons/tina.svg new file mode 100644 index 0000000..8b253d3 --- /dev/null +++ b/static/images/icons/tina.svg @@ -0,0 +1,4 @@ + diff --git a/static/images/icons/unibit-pink.svg b/static/images/icons/unibit-pink.svg new file mode 100644 index 0000000..66d6789 --- /dev/null +++ b/static/images/icons/unibit-pink.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/unibit.svg b/static/images/icons/unibit.svg new file mode 100644 index 0000000..bf7d169 --- /dev/null +++ b/static/images/icons/unibit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/vuepress.svg b/static/images/icons/vuepress.svg new file mode 100644 index 0000000..43df408 --- /dev/null +++ b/static/images/icons/vuepress.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/vuetify.svg b/static/images/icons/vuetify.svg new file mode 100644 index 0000000..f6fc82b --- /dev/null +++ b/static/images/icons/vuetify.svg @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/static/images/icons/w3css.svg b/static/images/icons/w3css.svg new file mode 100644 index 0000000..93c936f --- /dev/null +++ b/static/images/icons/w3css.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/wordpress.svg b/static/images/icons/wordpress.svg new file mode 100644 index 0000000..c25a8ff --- /dev/null +++ b/static/images/icons/wordpress.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/zola.svg b/static/images/icons/zola.svg new file mode 100644 index 0000000..e1b899d --- /dev/null +++ b/static/images/icons/zola.svg @@ -0,0 +1,4 @@ + diff --git a/static/images/jamstackthemes-screenshot.png b/static/images/jamstackthemes-screenshot.png new file mode 100644 index 0000000..502f58b Binary files /dev/null and b/static/images/jamstackthemes-screenshot.png differ diff --git a/static/images/logo-nav.svg b/static/images/logo-nav.svg new file mode 100644 index 0000000..1c7dff7 --- /dev/null +++ b/static/images/logo-nav.svg @@ -0,0 +1,142 @@ + + + + diff --git a/static/images/social/github.svg b/static/images/social/github.svg new file mode 100755 index 0000000..40076ce --- /dev/null +++ b/static/images/social/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/social/twitter.svg b/static/images/social/twitter.svg new file mode 100755 index 0000000..5299b1d --- /dev/null +++ b/static/images/social/twitter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/stackbit.png b/static/images/stackbit.png new file mode 100644 index 0000000..acdf004 Binary files /dev/null and b/static/images/stackbit.png differ diff --git a/static/images/ui/angle-double-left-solid.svg b/static/images/ui/angle-double-left-solid.svg new file mode 100644 index 0000000..95887a1 --- /dev/null +++ b/static/images/ui/angle-double-left-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/angle-double-right-solid.svg b/static/images/ui/angle-double-right-solid.svg new file mode 100644 index 0000000..998f859 --- /dev/null +++ b/static/images/ui/angle-double-right-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/check-solid.svg b/static/images/ui/check-solid.svg new file mode 100644 index 0000000..f91b74e --- /dev/null +++ b/static/images/ui/check-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/chevron-down-solid.svg b/static/images/ui/chevron-down-solid.svg new file mode 100644 index 0000000..026b7c0 --- /dev/null +++ b/static/images/ui/chevron-down-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/chevron-left-solid.svg b/static/images/ui/chevron-left-solid.svg new file mode 100644 index 0000000..41061c2 --- /dev/null +++ b/static/images/ui/chevron-left-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/chevron-right-solid.svg b/static/images/ui/chevron-right-solid.svg new file mode 100644 index 0000000..6f3ecc4 --- /dev/null +++ b/static/images/ui/chevron-right-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/external-link-alt-solid.svg b/static/images/ui/external-link-alt-solid.svg new file mode 100644 index 0000000..190df50 --- /dev/null +++ b/static/images/ui/external-link-alt-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/forks.svg b/static/images/ui/forks.svg new file mode 100644 index 0000000..c0b53a4 --- /dev/null +++ b/static/images/ui/forks.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/history-solid.svg b/static/images/ui/history-solid.svg new file mode 100644 index 0000000..ddcfda0 --- /dev/null +++ b/static/images/ui/history-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/issues.svg b/static/images/ui/issues.svg new file mode 100644 index 0000000..27647c3 --- /dev/null +++ b/static/images/ui/issues.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/minus-solid.svg b/static/images/ui/minus-solid.svg new file mode 100644 index 0000000..f72db5b --- /dev/null +++ b/static/images/ui/minus-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/minus-square-regular.svg b/static/images/ui/minus-square-regular.svg new file mode 100644 index 0000000..26c05a2 --- /dev/null +++ b/static/images/ui/minus-square-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/plus-solid.svg b/static/images/ui/plus-solid.svg new file mode 100644 index 0000000..bfa907d --- /dev/null +++ b/static/images/ui/plus-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/plus-square-regular.svg b/static/images/ui/plus-square-regular.svg new file mode 100644 index 0000000..b9e300d --- /dev/null +++ b/static/images/ui/plus-square-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/star-solid.svg b/static/images/ui/star-solid.svg new file mode 100644 index 0000000..ce74416 --- /dev/null +++ b/static/images/ui/star-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/star.svg b/static/images/ui/star.svg new file mode 100644 index 0000000..4834aa7 --- /dev/null +++ b/static/images/ui/star.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/themes/jamstackthemes/assets/js/filter/filter-groups.js b/themes/jamstackthemes/assets/js/filter/filter-groups.js new file mode 100644 index 0000000..0da7d85 --- /dev/null +++ b/themes/jamstackthemes/assets/js/filter/filter-groups.js @@ -0,0 +1,46 @@ +const groups = { + ssg: [ + "eleventy", + "gatsby", + "gridsome", + "hexo", + "hugo", + "jekyll", + "next", + "vuepress", + "brunch", + "docusaurus", + "jigsaw", + "metalsmith", + "middleman", + "mkdocs", + "nuxt", + "pelican", + "platframe", + "sapper", + "scully", + "statiq", + "unibit" + ], + cms: [ + "contentful", + "cosmic", + "datocms", + "forestry", + "ghost", + "kontent", + "netlifycms", + "no-cms", + "airtable", + "directus", + "firebase", + 'graphcms', + "prismic", + "sanity", + "storyblok", + "wordpress", + "notion" + ], + css: ["bootstrap", "scss", "tailwind", "bulma", "postcss", "styled-components"], + archetype: ["blog", "portfolio", "business", "single-page", "ecommerce", "documentation", "personal"], +}; diff --git a/themes/jamstackthemes/assets/js/filter/filter-hash.js b/themes/jamstackthemes/assets/js/filter/filter-hash.js new file mode 100644 index 0000000..3cf1ace --- /dev/null +++ b/themes/jamstackthemes/assets/js/filter/filter-hash.js @@ -0,0 +1,182 @@ +var targetSelector = ".grid"; +var activeHash = ""; +var activePage = -1; + +/** + * Deserializes a hash segment (if present) into in an object. + * + * @return {object|null} + */ + +function deserializeHash() { + var hash = window.location.hash.replace(/^#/g, ""); + var obj = null; + var groups = []; + + if (!hash) return obj; + + obj = {}; + groups = hash.split("&"); + + groups.forEach(function(group) { + var pair = group.split("="); + var groupName = pair[0]; + + obj[groupName] = pair[1].split(","); + }); + + return obj; +} + +/** + * Serializes a uiState object into a string. + * + * @param {object} uiState + * @return {string} + */ + +function serializeUiState(uiState) { + var output = ""; + + for (var key in uiState) { + var values = uiState[key]; + + if (!values.length) continue; + + output += key + "="; + output += values.join(","); + output += "&"; + } + + output = output.replace(/&$/g, ""); + + return output; +} + +/** + * Constructs a `uiState` object using the + * `getFilterGroupSelectors()` API method. + * + * @return {object} + */ + +function getUiState() { + // NB: You will need to rename the object keys to match the names of + // your project's filter groups – these should match those defined + // in your HTML. + + var uiState = { + ssg: mixer.getFilterGroupSelectors("ssg").map(getValueFromSelector), + cms: mixer.getFilterGroupSelectors("cms").map(getValueFromSelector), + css: mixer.getFilterGroupSelectors("css").map(getValueFromSelector), + archetype: mixer.getFilterGroupSelectors("archetype").map(getValueFromSelector) + }; + + return uiState; +} + +/** + * Updates the URL hash whenever the current filter changes. + * + * @param {mixitup.State} state + * @return {void} + */ + +function setHash(state) { + var selector = state.activeFilter.selector; + + // Construct an object representing the current state of each + // filter group + + var uiState = getUiState(); + + // Create a URL hash string by serializing the uiState object + + var newHash = "#" + serializeUiState(uiState); + + if (selector === targetSelector && window.location.href.indexOf("#") > -1) { + // Equivalent to filter "all", and a hash exists, remove the hash + + activeHash = ""; + + history.replaceState(null, document.title, window.location.pathname); + } else if (newHash !== window.location.hash && selector !== targetSelector) { + // Change the hash + + activeHash = newHash; + + history.replaceState( + null, + document.title, + window.location.pathname + newHash + ); + } +} + +/** + * Updates the mixer to a previous UI state. + * + * @param {object|null} uiState + * @param {boolean} [animate] + * @return {Promise} + */ + +function syncMixerWithPreviousUiState(uiState, animate) { + var ssg = uiState && uiState.ssg ? uiState.ssg : []; + var cms = uiState && uiState.cms ? uiState.cms : []; + var css = uiState && uiState.css ? uiState.css : []; + var archetype = uiState && uiState.archetype ? uiState.archetype : []; + + mixer.setFilterGroupSelectors("ssg", ssg.map(getSelectorFromValue)); + mixer.setFilterGroupSelectors("cms", cms.map(getSelectorFromValue)); + mixer.setFilterGroupSelectors("css", css.map(getSelectorFromValue)); + mixer.setFilterGroupSelectors("archetype", archetype.map(getSelectorFromValue)); + + // Parse the filter groups (passing `false` will perform no animation) + + return mixer.parseFilterGroups(animate ? true : false); +} + +/** + * Converts a selector (e.g. '.green') into a simple value (e.g. 'green'). + * + * @param {string} selector + * @return {string} + */ + +function getValueFromSelector(selector) { + return selector.replace(/^./, ""); +} + +/** + * A function for filtering the values of the mixitup command object + * generated by calling the `parseFilterGroups()` method. + * + * @param {object} command + * @return {object} + */ + +function handleParseFilterGroups(command) { + if (activePage > 1) { + // If an activePage greater than 1 has been parsed + // from the URL, update the command with a pagination + // instruction + + command.paginate = activePage; + } + + return command; +} + + +/** + * Converts a simple value (e.g. 'green') into a selector (e.g. '.green'). + * + * @param {string} selector + * @return {string} + */ + +function getSelectorFromValue(value) { + return "." + value; +} + diff --git a/themes/jamstackthemes/assets/js/filter/filter-toggle.js b/themes/jamstackthemes/assets/js/filter/filter-toggle.js new file mode 100644 index 0000000..743e6bb --- /dev/null +++ b/themes/jamstackthemes/assets/js/filter/filter-toggle.js @@ -0,0 +1,11 @@ +document.querySelectorAll(".toggle-icon").forEach((toggle) => { + toggle.addEventListener('click', (e) => { + toggle.parentNode.parentNode.classList.toggle('closed'); + }); +}); + +document.querySelectorAll(".toggle-more").forEach((toggle) => { + toggle.addEventListener('click', (e) => { + toggle.parentNode.classList.toggle('closed'); + }); +}); diff --git a/themes/jamstackthemes/assets/js/filter/filter.js b/themes/jamstackthemes/assets/js/filter/filter.js new file mode 100644 index 0000000..9a8f01e --- /dev/null +++ b/themes/jamstackthemes/assets/js/filter/filter.js @@ -0,0 +1,116 @@ +const pageId = document.querySelector('body').id; +// const sortOrder = pageId === "page-all-themes" ? "stars:desc" : null; +const sortOrder = null; + +const mixer = mixitup('#grids-homepage', { + multifilter: { + enable: true, + logicWithinGroup: 'or', + logicBetweenGroups: 'and' + }, + animation: { + enable: false, + }, + selectors: { + target: '.grid' + }, + load: { + sort: sortOrder + }, + // pagination: { + // limit: 50, + // maintainActivePage: false + // }, + callbacks: { + onMixStart: function(state, futureState) { + let total = futureState.totalShow; + let count = document.querySelector('.count-number'); + count.textContent = total; + window.scrollTo(0,0); + updateFilterCounts(state, futureState); + }, + onMixClick: function(state, originalEvent) { + }, + onParseFilterGroups: handleParseFilterGroups, + onMixEnd: setHash + } +}); + +function getTriggerGroup(event) { + triggerGroup = null; + if (!event) { + return triggerGroup + } else if (event.classList.contains('filter-button')) { + triggerGroup = event.parentNode.parentNode.parentNode.id.slice(13); + }; + return triggerGroup; +} + +function updateFilterCounts(state, futureState) { + + let triggerGroup = getTriggerGroup(futureState.triggerElement) + let totalMatching = futureState.targets.map(theme => theme.className.trim().split(" ")); + let matching = futureState.matching.map(theme => theme.className.trim().split(" ")); + let hasMultipleActiveGroups = checkActiveGroups(); + + // Update Filter Counts + Object.keys(groups).forEach((group) => { + if (hasMultipleActiveGroups) { + resetCount(groups[group], totalMatching); + updateCount(groups[group], matching); + } else { + if (group === triggerGroup) { + resetCount(groups[group], totalMatching); + } else if (!triggerGroup) { + + } else { + updateCount(groups[group], matching); + } + } + }) +} + +function checkActiveGroups() { + let activeGroups = Object.keys(groups).map(group => { + return mixer.getFilterGroupSelectors(group) + }) + + let activeGroupsLength = 0 + activeGroups.forEach(group => { + if (group.length) { + activeGroupsLength += 1 + } + }) + return activeGroupsLength >= 2; +} + +function updateCount(group, matches) { + group.forEach(term => { + let count = matches.filter(match => { + return match.includes(term); + }) + const termDom = document.querySelector(`#filter-count-${term}`) + if (termDom) { + termDom.innerText = count.length + } + }) +} + +function resetCount(group, matches) { + group.forEach(term => { + let count = matches.filter(match => { + return match.includes(term); + }) + const termDom = document.querySelector(`#filter-count-${term}`) + if (termDom) { + termDom.innerText = count.length + } + }) +} + +var uiState = deserializeHash(); + +if (uiState) { + // If a valid uiState object is present on page load, filter the mixer + syncMixerWithPreviousUiState(uiState); +} diff --git a/themes/jamstackthemes/assets/js/libs/mixitup-multifilter.js b/themes/jamstackthemes/assets/js/libs/mixitup-multifilter.js new file mode 100755 index 0000000..77924fc --- /dev/null +++ b/themes/jamstackthemes/assets/js/libs/mixitup-multifilter.js @@ -0,0 +1,1256 @@ +/**! + * MixItUp MultiFilter v3.3.4 + * A UI-builder for powerful multidimensional filtering + * Build 6bbb142d-9851-4ca8-b6d4-f760362d93ec + * + * Requires mixitup.js >= v^3.1.2 + * + * @copyright Copyright 2014-2018 KunkaLabs Limited. + * @author KunkaLabs Limited. + * @link https://www.kunkalabs.com/mixitup-multifilter/ + * + * @license Commercial use requires a commercial license. + * https://www.kunkalabs.com/mixitup-multifilter/licenses/ + * + * Non-commercial use permitted under same terms as license. + * http://creativecommons.org/licenses/by-nc/3.0/ + */ +(function(window) { + 'use strict'; + + var mixitupMultifilter = function(mixitup) { + var h = mixitup.h; + var diacriticsMap; + + diacriticsMap = [ + ['A', /[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g], + ['AA', /[\uA732]/g], + ['AE', /[\u00C6\u01FC\u01E2]/g], + ['AO', /[\uA734]/g], + ['AU', /[\uA736]/g], + ['AV', /[\uA738\uA73A]/g], + ['AY', /[\uA73C]/g], + ['B', /[\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181]/g], + ['C', /[\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E]/g], + ['D', /[\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779]/g], + ['DZ', /[\u01F1\u01C4]/g], + ['Dz', /[\u01F2\u01C5]/g], + ['E', /[\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E]/g], + ['F', /[\u0046\u24BB\uFF26\u1E1E\u0191\uA77B]/g], + ['G', /[\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E]/g], + ['H', /[\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D]/g], + ['I', /[\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197]/g], + ['J', /[\u004A\u24BF\uFF2A\u0134\u0248]/g], + ['K', /[\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2]/g], + ['L', /[\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780]/g], + ['LJ', /[\u01C7]/g], + ['Lj', /[\u01C8]/g], + ['M', /[\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C]/g], + ['N', /[\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4]/g], + ['NJ', /[\u01CA]/g], + ['Nj', /[\u01CB]/g], + ['O', /[\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C]/g], + ['OI', /[\u01A2]/g], + ['OO', /[\uA74E]/g], + ['OU', /[\u0222]/g], + ['P', /[\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754]/g], + ['Q', /[\u0051\u24C6\uFF31\uA756\uA758\u024A]/g], + ['R', /[\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782]/g], + ['S', /[\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784]/g], + ['T', /[\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786]/g], + ['TZ', /[\uA728]/g], + ['U', /[\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244]/g], + ['V', /[\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245]/g], + ['VY', /[\uA760]/g], + ['W', /[\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72]/g], + ['X', /[\u0058\u24CD\uFF38\u1E8A\u1E8C]/g], + ['Y', /[\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE]/g], + ['Z', /[\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762]/g], + ['a', /[\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250]/g], + ['aa', /[\uA733]/g], + ['ae', /[\u00E6\u01FD\u01E3]/g], + ['ao', /[\uA735]/g], + ['au', /[\uA737]/g], + ['av', /[\uA739\uA73B]/g], + ['ay', /[\uA73D]/g], + ['b', /[\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253]/g], + ['c', /[\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184]/g], + ['d', /[\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A]/g], + ['dz', /[\u01F3\u01C6]/g], + ['e', /[\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD]/g], + ['f', /[\u0066\u24D5\uFF46\u1E1F\u0192\uA77C]/g], + ['g', /[\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F]/g], + ['h', /[\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265]/g], + ['hv', /[\u0195]/g], + ['i', /[\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131]/g], + ['j', /[\u006A\u24D9\uFF4A\u0135\u01F0\u0249]/g], + ['k', /[\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3]/g], + ['l', /[\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747]/g], + ['lj', /[\u01C9]/g], + ['m', /[\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F]/g], + ['n', /[\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5]/g], + ['nj', /[\u01CC]/g], + ['o', /[\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275]/g], + ['oi', /[\u01A3]/g], + ['ou', /[\u0223]/g], + ['oo', /[\uA74F]/g], + ['p', /[\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755]/g], + ['q', /[\u0071\u24E0\uFF51\u024B\uA757\uA759]/g], + ['r', /[\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783]/g], + ['s', /[\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B]/g], + ['t', /[\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787]/g], + ['tz', /[\uA729]/g], + ['u', /[\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289]/g], + ['v', /[\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C]/g], + ['vy', /[\uA761]/g], + ['w', /[\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73]/g], + ['x', /[\u0078\u24E7\uFF58\u1E8B\u1E8D]/g], + ['y', /[\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF]/g], + ['z', /[\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763]/g] + ]; + + if ( + !mixitup.CORE_VERSION || + !h.compareVersions(mixitupMultifilter.REQUIRE_CORE_VERSION, mixitup.CORE_VERSION) + ) { + throw new Error( + '[MixItUp Multifilter] MixItUp Multifilter v' + + mixitupMultifilter.EXTENSION_VERSION + + ' requires at least MixItUp v' + + mixitupMultifilter.REQUIRE_CORE_VERSION + ); + } + + /** + * A group of optional callback functions to be invoked at various + * points within the lifecycle of a mixer operation. + * + * @constructor + * @memberof mixitup.Config + * @name callbacks + * @namespace + * @public + * @since 3.0.0 + */ + + mixitup.ConfigCallbacks.registerAction('afterConstruct', 'multifilter', function() { + /** + * A callback function invoked whenever MultiFilter filter groups + * are parsed. This occurs whenever the user interacts with filter + * group UI, or when the `parseFilterGroups()` API method is called, + * but before the resulting filter operation has been triggered. + * + * By default, this generates the appropriate compound selector and + * filters the mixer using a `multimix()` API call internally. This + * callback can be used to transform the multimix command object sent + * to this API call. + * + * This is particularly useful when additional behavior such as sorting + * or pagination must be taken into account when using the MultiFilter API. + * + * The callback receives the generated multimix command object, and must + * also return a valid multimix command object. + * + * @example