├── .gitignore ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── bin ├── build ├── dev └── setup ├── config.rb ├── data ├── collection.yaml └── config.yaml ├── lib ├── tonic.rb └── tonic │ ├── filters.rb │ ├── helpers.rb │ └── utils.rb ├── package.json ├── source ├── images │ ├── favicon.ico │ └── icons │ │ ├── chevron-down.svg │ │ ├── chevron-left.svg │ │ ├── close.svg │ │ ├── menu.svg │ │ ├── search.svg │ │ ├── share.svg │ │ └── sharing │ │ ├── email.svg │ │ ├── facebook.svg │ │ ├── linkedin.svg │ │ ├── pinterest.svg │ │ ├── telegram.svg │ │ ├── twitter.svg │ │ └── whatsapp.svg ├── index.html.erb ├── javascripts │ ├── application.js │ ├── controllers │ │ └── app.js │ └── utils.js ├── layouts │ └── layout.erb ├── stylesheets │ └── application.css └── templates │ ├── collection │ ├── _item_card.html.erb │ └── detail_page.html.erb │ ├── components │ ├── _footer.html.erb │ ├── _header.html.erb │ ├── _sharing.html.erb │ ├── _sidebar.html.erb │ └── _sorting.html.erb │ └── filters │ ├── _boolean.html.erb │ ├── _date_range.html.erb │ ├── _numeric_range.html.erb │ ├── _numeric_select_range.html.erb │ ├── _radio_buttons.html.erb │ ├── _select.html.erb │ ├── _tags.html.erb │ └── _text.html.erb ├── tailwind.config.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.bundle 3 | *.cache 4 | *.log 5 | build/ 6 | dist/ 7 | node_modules/ 8 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "middleman", "~> 4.4" 4 | gem "middleman-livereload", "~> 3.4" 5 | gem "middleman-inline_svg", "~> 0.1" 6 | gem "video_info", "~> 4.0" 7 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activesupport (7.0.8) 5 | concurrent-ruby (~> 1.0, >= 1.0.2) 6 | i18n (>= 1.6, < 2) 7 | minitest (>= 5.1) 8 | tzinfo (~> 2.0) 9 | addressable (2.8.1) 10 | public_suffix (>= 2.0.2, < 6.0) 11 | ansi (1.5.0) 12 | ast (2.4.2) 13 | backports (3.23.0) 14 | coffee-script (2.4.1) 15 | coffee-script-source 16 | execjs 17 | coffee-script-source (1.12.2) 18 | concurrent-ruby (1.2.2) 19 | contracts (0.14.0) 20 | dotenv (2.8.1) 21 | em-websocket (0.5.3) 22 | eventmachine (>= 0.12.9) 23 | http_parser.rb (~> 0) 24 | erubis (2.7.0) 25 | eventmachine (1.2.7) 26 | execjs (2.8.1) 27 | fast_blank (1.0.1) 28 | fastimage (2.2.6) 29 | ffi (1.15.5) 30 | haml (5.2.2) 31 | temple (>= 0.8.0) 32 | tilt 33 | hamster (3.0.0) 34 | concurrent-ruby (~> 1.0) 35 | hashie (3.6.0) 36 | http_parser.rb (0.8.0) 37 | i18n (1.6.0) 38 | concurrent-ruby (~> 1.0) 39 | iso8601 (0.13.0) 40 | kramdown (2.4.0) 41 | rexml 42 | listen (3.8.0) 43 | rb-fsevent (~> 0.10, >= 0.10.3) 44 | rb-inotify (~> 0.9, >= 0.9.10) 45 | memoist (0.16.2) 46 | middleman (4.4.3) 47 | coffee-script (~> 2.2) 48 | haml (>= 4.0.5, < 6.0) 49 | kramdown (>= 2.3.0) 50 | middleman-cli (= 4.4.3) 51 | middleman-core (= 4.4.3) 52 | middleman-cli (4.4.3) 53 | thor (>= 0.17.0, < 2.0) 54 | middleman-core (4.4.3) 55 | activesupport (>= 6.1, < 7.1) 56 | addressable (~> 2.4) 57 | backports (~> 3.6) 58 | bundler (~> 2.0) 59 | contracts (~> 0.13) 60 | dotenv 61 | erubis 62 | execjs (~> 2.0) 63 | fast_blank 64 | fastimage (~> 2.0) 65 | hamster (~> 3.0) 66 | hashie (~> 3.4) 67 | i18n (~> 1.6.0) 68 | listen (~> 3.0) 69 | memoist (~> 0.14) 70 | padrino-helpers (~> 0.15.0) 71 | parallel 72 | rack (>= 1.4.5, < 3) 73 | sassc (~> 2.0) 74 | servolux 75 | tilt (~> 2.0.9) 76 | toml 77 | uglifier (~> 3.0) 78 | webrick 79 | middleman-inline_svg (0.1.2) 80 | middleman-core (>= 3.4.1) 81 | nokogiri (>= 1.8) 82 | middleman-livereload (3.4.7) 83 | em-websocket (~> 0.5.1) 84 | middleman-core (>= 3.3) 85 | rack-livereload (~> 0.3.15) 86 | minitest (5.20.0) 87 | net_http_timeout_errors (1.0.0) 88 | nokogiri (1.18.8-arm64-darwin) 89 | racc (~> 1.4) 90 | nokogiri (1.18.8-x86_64-darwin) 91 | racc (~> 1.4) 92 | nokogiri (1.18.8-x86_64-linux-gnu) 93 | racc (~> 1.4) 94 | oga (3.4) 95 | ast 96 | ruby-ll (~> 2.1) 97 | padrino-helpers (0.15.2) 98 | i18n (>= 0.6.7, < 2) 99 | padrino-support (= 0.15.2) 100 | tilt (>= 1.4.1, < 3) 101 | padrino-support (0.15.2) 102 | parallel (1.22.1) 103 | parslet (2.0.0) 104 | public_suffix (5.0.1) 105 | racc (1.8.1) 106 | rack (2.2.14) 107 | rack-livereload (0.3.17) 108 | rack 109 | rb-fsevent (0.11.2) 110 | rb-inotify (0.10.1) 111 | ffi (~> 1.0) 112 | rexml (3.3.9) 113 | ruby-ll (2.1.2) 114 | ansi 115 | ast 116 | sassc (2.4.0) 117 | ffi (~> 1.9) 118 | servolux (0.13.0) 119 | temple (0.10.0) 120 | thor (1.2.1) 121 | tilt (2.0.11) 122 | toml (0.3.0) 123 | parslet (>= 1.8.0, < 3.0.0) 124 | tzinfo (2.0.6) 125 | concurrent-ruby (~> 1.0) 126 | uglifier (3.2.0) 127 | execjs (>= 0.3.0, < 3) 128 | video_info (4.0.0) 129 | iso8601 (~> 0.13.0) 130 | net_http_timeout_errors (~> 1.0.0) 131 | oga (~> 3.4) 132 | webrick (1.8.2) 133 | 134 | PLATFORMS 135 | arm64-darwin-22 136 | x86_64-darwin-17 137 | x86_64-linux 138 | 139 | DEPENDENCIES 140 | middleman (~> 4.4) 141 | middleman-inline_svg (~> 0.1) 142 | middleman-livereload (~> 3.4) 143 | video_info (~> 4.0) 144 | 145 | BUNDLED WITH 146 | 2.4.10 147 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Subgin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tonic 2 | 3 | > 🍸 Digital Collections Framework 4 | 5 | Transform your collection into a beautiful website ✨ 6 | 7 | Tonic parses your collection, defined in a `YAML` or `JSON` file, and automatically generates a customizable static website to explore your collection in a smart way, with a lot of filtering and sorting options. 8 | 9 | *Built with: [Middleman](https://middlemanapp.com), [Ralix](https://github.com/ralixjs/ralix), [Tailwind](https://tailwindcss.com)* 10 | 11 | 🌐 [**Live demo**](https://tonic-demo.netlify.app) 12 | 13 |

14 | 15 |

16 | 17 | ## Install 18 | 19 | **Requirements** 20 | 21 | - Ruby 22 | - Yarn 23 | 24 | Clone/fork this repository (or use the GitHub *template* button), then `cd` into the folder and run: 25 | 26 | ``` 27 | > bin/setup 28 | ``` 29 | 30 | ## Usage 31 | 32 | Start the development server by: 33 | 34 | ``` 35 | > bin/dev 36 | ``` 37 | 38 | Or compile the site (into the `build/` folder) by: 39 | 40 | ``` 41 | > bin/build 42 | ``` 43 | 44 | ### Collection 45 | 46 | Define your collection in the `data/collection.yaml` file. Example: 47 | 48 | ```yaml 49 | - name: Item 1 50 | description: Ad aut libero. Adipisci asperiores repudiandae. Sunt expedita sunt. 51 | category: Marketing 52 | tags: 53 | - tag1 54 | - tag2 55 | price: 99.9 56 | downloads: 400 57 | published_at: "2021-06-10" 58 | - name: Item 2 59 | description: Incidunt cupiditate rerum. Enim quo pariatur. Commodi provident dolores. 60 | category: Accounting 61 | tags: 62 | - tag2 63 | price: 149.9 64 | downloads: 100 65 | published_at: "2022-09-01" 66 | ``` 67 | 68 | The string attributes can contain HTML: 69 | 70 | ```yaml 71 | description: | 72 |

This thing is fantastic!

73 |

Check out more information in the following section

74 | ``` 75 | 76 | You can also use a `JSON` file (`data/collection.json`), as the following example: 77 | 78 | ```json 79 | [ 80 | { 81 | "name": "Item 1", 82 | "description": "Ad aut libero. Adipisci asperiores repudiandae. Sunt expedita sunt.", 83 | "tags": ["tag1", "tag2"], 84 | "price": 99.9 85 | }, 86 | { 87 | "name": "Item 2", 88 | "description": "Incidunt cupiditate rerum. Enim quo pariatur. Commodi provident dolores.", 89 | "tags": ["tag2"], 90 | "price": 149.9 91 | } 92 | ] 93 | ``` 94 | 95 | #### Remote Collection 96 | 97 | You can also fetch your collection from a remote resource. To do so, you should define the `remote_collection` setting in the [configuration file](#customization): 98 | 99 | ```yaml 100 | remote_collection: https://example.com/collection.json 101 | ``` 102 | 103 | #### Nested attributes 104 | 105 | Your items can have nested attributes too: 106 | 107 | ```yaml 108 | - name: Leanne Graham 109 | email: leanne_graham@example.com 110 | address: 111 | street: Kulas Light 112 | city: Gwenborough 113 | zipcode: 92998-3874 114 | geo: 115 | lat: -37.3159 116 | lng: 81.1496 117 | ``` 118 | 119 | ### Magic attributes 120 | 121 | Some names help Tonic to automatically render your items (and its related filters) as beautiful as possible by default: 122 | 123 | - name (required! should be unique!) 124 | - description 125 | - category 126 | - tags (shoud be an array) 127 | - images (shoud be an array) 128 | 129 | More ✨ rules: 130 | 131 | - Automatic 🎬 video embeds from urls (YouTube, Vimeo and more). 132 | - Audio ⏯️ player for audio files. 133 | - If the attribute name ends with `_at` (`published_at`, `updated_at`, ...), it's automatically parsed as 📅 date. 134 | - The `detail_page_link` attribute makes the detail page to 🏃 jump to an external page, [see more](#detail-pages). 135 | 136 | ### Customization 137 | 138 | Configure your site via the `data/config.yaml` file. All options: 139 | 140 | ```yaml 141 | # Main settings 142 | title: My Collection 143 | description: | 144 | Welcome to my awesome collection!
145 | More information in the following section. 146 | remote_collection: https://example.com/collection.json 147 | detail_pages: true 148 | 149 | # Style/UI 150 | main_color: "#0891b2" 151 | background_color: "#e0f2fe" 152 | font_family: "Fira Sans" 153 | logo: "/images/logo.png" 154 | links: 155 | - text: About Us 156 | url: '/about' 157 | - text: Contact 158 | url: '/contact' 159 | footer_content: Follow us on Twitter and Instagram 160 | hide_filters: false 161 | hide_sorting: false 162 | hide_sharing: false 163 | sharing_platforms: 164 | - facebook 165 | - twitter 166 | - whatsapp 167 | - email 168 | item_card_image: true 169 | 170 | # Sorting 171 | sorting: 172 | default_order: "price desc" 173 | exclude: 174 | - clicks 175 | 176 | # Filters 177 | filters: 178 | type: 179 | category: text 180 | status: radio_buttons 181 | exclude: 182 | - summary 183 | - long_description 184 | ``` 185 | 186 | #### Detail pages 187 | 188 | If disabled, detail pages won't be generated. Remember you can link to an external page by defining the `detail_page_link` attribute in any of the items of your collection. 189 | 190 | ```yaml 191 | - name: Awesome Product 192 | description: This product is fantastic! 193 | price: 99.9 194 | detail_page_link: https://my-shop.com/product/awesome-product 195 | ``` 196 | 197 | If you only want to customize how they look, you can do it by editing the HTML template, check the [Advanced customization](#advanced-customization) section. 198 | 199 | #### Font family 200 | 201 | You can use remote fonts from [Google Fonts](https://fonts.google.com) by adding the family name in the `font_family` option: 202 | 203 | ```yaml 204 | font_family: "Fira Sans" # other examples: Lato, Roboto, Oswald, Montserrat, ... 205 | ``` 206 | 207 | #### Sharing 208 | 209 | If you want to customize the available sharing options, you should use the `sharing_platforms` option passing an array. 210 | 211 | By default, the supported platforms are: Facebook, Twitter, Linkedin, Pinterest, Whatsapp, Telegram and Email. 212 | 213 | #### Sorting 214 | 215 | By default, the `name` attribute and all integer attributes are used to build the sorting options. 216 | 217 | Available options: 218 | 219 | - `default_order` By default: "name asc". 220 | - `exclude` Exclude attributes from sorting options. 221 | 222 | #### Filters 223 | 224 | Tonic analyzes your collection (especially the *1st item*) to infer the best option given the attribute type and other parameters (like number of unique values). It can be overridden in case you want a different filter type for X reasons. 225 | 226 | Available options: 227 | 228 | - `type` Forces an attribute to render a specific filter type. Available types: 229 | - `text` 230 | - `select` 231 | - `radio_buttons` 232 | - `numeric_range` 233 | - `numeric_select_range` 234 | - `date_range` 235 | - `tags` 236 | - `boolean` 237 | - `exclude` Exclude attributes from filters. 238 | 239 | ### Advanced customization 240 | 241 | If you want to fully customize your Tonic instance, you can do it by editing the files under the `source/*` folder. 242 | 243 | For example, if you want to customize the auto-generated HTML for your items, you can do it by editing these files: 244 | 245 | - Item card partial: [`source/templates/collection/_item_card.html.erb`](source/templates/collection/_item_card.html.erb) 246 | - Item detail page: [`source/templates/collection/detail_page.html.erb`](source/templates/collection/detail_page.html.erb) 247 | 248 | **NOTE** In both templates you can use the `item` object to access any attribute: `item.name`, `item.description`, etc. 249 | 250 | You can also add more pages to your Tonic site by just adding HTML templates (`*.html.erb`) under the `source/*` directory. After all, Tonic uses [Middleman](https://middlemanapp.com) under the hood, so you can even use Markdown and/or many other template engines. 251 | 252 | ## License 253 | 254 | Copyright (c) Subgin. Tonic is released under the [MIT](LICENSE) License. 255 | -------------------------------------------------------------------------------- /bin/build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | bundle exec middleman build --verbose 4 | -------------------------------------------------------------------------------- /bin/dev: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | bundle exec middleman server 4 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | bundle install 4 | yarn install 5 | -------------------------------------------------------------------------------- /config.rb: -------------------------------------------------------------------------------- 1 | require "lib/tonic" 2 | 3 | activate :directory_indexes 4 | activate :inline_svg 5 | activate :external_pipeline, 6 | name: :esbuild, 7 | command: build? ? "yarn build" : "yarn dev", 8 | source: "dist", 9 | latency: 1 10 | 11 | configure :development do 12 | activate :livereload 13 | end 14 | 15 | configure :build do 16 | ignore File.join(config[:js_dir], "*") # handled by External Pipeline 17 | activate :asset_hash 18 | activate :relative_assets 19 | end 20 | 21 | Tonic.start(self) 22 | -------------------------------------------------------------------------------- /data/collection.yaml: -------------------------------------------------------------------------------- 1 | - name: Heavy Duty Leather Pants 2 | description: | 3 |

Nesciunt aut nulla. Natus repellat ab. Et perspiciatis quia. Fugit molestiae quos. Aut veniam et.

4 |

Ex est numquam. Vitae praesentium quis. Deleniti veritatis est. Reprehenderit velit illum.

5 |

Qui maxime aliquam. Tenetur magnam ipsam. Quo totam labore. Et quia sunt. Quo aut ab. Assumenda qui et.

6 | category: Transportation 7 | tags: 8 | - musician 9 | - lifeguard 10 | status: Beta 11 | price: 9.9 12 | downloads: 2000 13 | images: 14 | - https://picsum.photos/600 15 | - https://picsum.photos/600 16 | - https://picsum.photos/600 17 | - https://picsum.photos/600 18 | video: https://www.youtube.com/watch?v=ZaBXpWaA20g 19 | audio: https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3 20 | premium: false 21 | website: https://pfannerstill.biz/emory.lubowitz 22 | published_at: '2022-02-22' 23 | address: 24 | street: Kulas Light 25 | city: Gwenborough 26 | zipcode: 92998-3874 27 | geo: 28 | lat: -37.3159 29 | lng: 81.1496 30 | - name: Intelligent Granite Hat 31 | description: Et aut natus. Incidunt non autem. Magnam optio accusantium. Quis aut 32 | accusamus. Omnis ab illo. Ut repellendus cumque. Optio est error. Sed officia sint. 33 | Ut eum veritatis. Quos libero deserunt. Aut asperiores in. Aut ullam rerum. Tempora 34 | soluta illo. Qui voluptatem maiores. Consequuntur ut ut. 35 | category: Wireless 36 | tags: 37 | - doctor 38 | - librarian 39 | status: Updated 40 | price: 299.9 41 | downloads: 1500 42 | images: 43 | - https://picsum.photos/600 44 | - https://picsum.photos/600 45 | - https://picsum.photos/600 46 | - https://picsum.photos/600 47 | - https://picsum.photos/600 48 | premium: false 49 | website: https://ratke.com/domenic.runte 50 | published_at: '2022-09-12' 51 | detail_page_link: https://example.com 52 | - name: Sleek Leather Plate 53 | description: Vitae animi id. Amet a enim. Ipsa autem praesentium. Voluptatum tenetur 54 | illo. Dolor numquam harum. Blanditiis suscipit qui. A exercitationem est. Distinctio 55 | aut quia. Nihil omnis facere. Officia consequatur dolores. Facere saepe optio. 56 | Provident quis sed. Minus ut eum. Aperiam officia qui. Odio deleniti quae. 57 | category: Wireless 58 | tags: 59 | - painter 60 | status: Beta 61 | price: 750.0 62 | downloads: 120 63 | images: 64 | - https://picsum.photos/600 65 | - https://picsum.photos/600 66 | - https://picsum.photos/600 67 | - https://picsum.photos/600 68 | - https://picsum.photos/600 69 | premium: false 70 | website: https://lebsack.net/nanette 71 | published_at: '2018-05-22' 72 | - name: Aerodynamic Leather Car 73 | description: Iusto laboriosam ut. Dolore minus voluptatibus. Qui iusto ipsam. Explicabo 74 | accusantium officia. Quaerat delectus aut. Odio deleniti commodi. Veniam ut ipsam. 75 | Cum tempora ab. Nesciunt occaecati cumque. Cupiditate quasi enim. Sunt consequatur 76 | eos. Molestias impedit debitis. Commodi ea voluptas. Ut voluptas voluptates. Quos 77 | blanditiis accusamus. 78 | category: Legislative Office 79 | tags: 80 | - optician 81 | status: Beta 82 | price: 1200.0 83 | downloads: 500 84 | images: 85 | - https://picsum.photos/600 86 | premium: false 87 | website: https://nikolaus.io/armando.braun 88 | published_at: '2018-02-16' 89 | - name: Heavy Duty Marble Bottle 90 | description: Asperiores numquam tempore. Omnis ipsam voluptatem. Inventore dolorem 91 | optio. Magnam quis ea. Porro ipsa rerum. Voluptatum similique amet. Id saepe est. 92 | Unde totam ipsum. Eos alias in. Qui voluptate nobis. Error qui itaque. Officiis 93 | quae officia. Ut deserunt quia. Nihil maxime esse. Laudantium nobis quae. 94 | category: Transportation 95 | tags: 96 | - optician 97 | - librarian 98 | - philosopher 99 | status: Updated 100 | price: 200.0 101 | downloads: 250 102 | images: 103 | - https://picsum.photos/600 104 | - https://picsum.photos/600 105 | - https://picsum.photos/600 106 | - https://picsum.photos/600 107 | - https://picsum.photos/600 108 | premium: true 109 | website: https://christiansen.info/bradley.durgan 110 | published_at: '2019-05-13' 111 | - name: Ergonomic Silk Watch 112 | description: Itaque nisi inventore. Est consequatur blanditiis. Et quidem delectus. Quia 113 | corrupti delectus. Sit eos ad. Sed doloribus esse. Esse deleniti fuga. Voluptatem 114 | nihil quisquam. Delectus eum atque. Quaerat qui dolorem. Perferendis est sit. Non 115 | sint saepe. Et consequatur et. Quibusdam sit est. Harum tempora ut. 116 | category: Legislative Office 117 | tags: 118 | - receptionist 119 | - doctor 120 | status: Updated 121 | price: 299.9 122 | downloads: 900 123 | images: 124 | - https://picsum.photos/600 125 | - https://picsum.photos/600 126 | - https://picsum.photos/600 127 | - https://picsum.photos/600 128 | premium: true 129 | website: https://johnson.net/nickie 130 | published_at: '2018-12-28' 131 | - name: Lightweight Concrete Shirt 132 | description: Voluptas vitae enim. Sit est voluptatibus. Harum corporis ut. Eaque 133 | et quia. Laudantium voluptates esse. Hic in molestiae. Impedit aut aut. Aut minima 134 | quia. Sint amet et. Suscipit dolorem fuga. Quam quas officia. Aut sunt hic. Aut 135 | commodi exercitationem. Molestias in veritatis. A accusamus culpa. 136 | category: Luxury Goods & Jewelry 137 | tags: 138 | - coach 139 | - soldier 140 | - politician 141 | status: New 142 | price: 1200.0 143 | downloads: 540 144 | images: 145 | - https://picsum.photos/600 146 | - https://picsum.photos/600 147 | - https://picsum.photos/600 148 | premium: true 149 | website: https://kreiger.com/delphia_doyle 150 | published_at: '2018-09-16' 151 | - name: Aerodynamic Copper Knife 152 | description: Assumenda earum quas. Mollitia beatae voluptatem. Nisi dolor voluptate. Id 153 | nulla at. Ex sit molestiae. Vitae reiciendis occaecati. Repellat rerum saepe. Velit 154 | iste sint. Ut voluptatem id. Dolores reiciendis recusandae. Laudantium mollitia 155 | et. Minus provident nam. Et reprehenderit omnis. Consequatur deleniti aut. Qui 156 | recusandae velit. 157 | category: Wireless 158 | tags: 159 | - physicist 160 | - politician 161 | status: New 162 | price: 600.0 163 | downloads: 1800 164 | images: 165 | - https://picsum.photos/600 166 | - https://picsum.photos/600 167 | - https://picsum.photos/600 168 | premium: true 169 | website: https://krajcik.io/lyman.greenfelder 170 | published_at: '2018-07-11' 171 | - name: Awesome Iron Hat 172 | description: Temporibus quos aut. Totam commodi et. Nihil voluptas veniam. Dolorem 173 | voluptas repellat. Dicta numquam non. Facilis voluptatem officia. Enim quo culpa. 174 | Corporis eveniet eos. Sed ipsum quos. Molestias accusantium illo. Sequi dicta ut. 175 | Ea aut alias. Maiores animi enim. In dolore rem. Voluptatem nostrum voluptas. 176 | category: Wireless 177 | tags: 178 | - politician 179 | - scientist 180 | - painter 181 | status: Beta 182 | price: 99.9 183 | downloads: 1000 184 | images: 185 | - https://picsum.photos/600 186 | - https://picsum.photos/600 187 | - https://picsum.photos/600 188 | - https://picsum.photos/600 189 | premium: true 190 | website: https://wiza.name/yong 191 | published_at: '2021-03-02' 192 | - name: Durable Bronze Watch 193 | description: Quia sed neque. Quibusdam eos eaque. Cumque soluta ullam. Voluptates 194 | est sunt. Nulla nihil qui. Aut dignissimos iure. Culpa quae eius. Rem neque magnam. 195 | Est sint similique. Impedit et sed. Aut ipsam laboriosam. Ipsum impedit voluptatem. Illo 196 | ut et. Earum quaerat cumque. Non enim dolorem. 197 | category: Wireless 198 | tags: 199 | - painter 200 | - musician 201 | - scientist 202 | status: New 203 | price: 19.9 204 | downloads: 60 205 | images: 206 | - https://picsum.photos/600 207 | - https://picsum.photos/600 208 | premium: true 209 | website: https://bogan.name/erik 210 | published_at: '2020-08-01' 211 | - name: Lightweight Granite Shirt 212 | description: Molestiae veniam ducimus. Harum veniam eligendi. Inventore quis explicabo. Sit 213 | minus aut. Id minus quia. Iusto velit laudantium. Nihil aut accusamus. Corporis 214 | facere explicabo. Ut non culpa. Veritatis quo atque. Ut deleniti repellat. Quibusdam 215 | qui dolorem. Autem similique qui. Numquam eligendi magni. Id minus ut. 216 | category: Transportation 217 | tags: 218 | - philosopher 219 | - painter 220 | - soldier 221 | status: New 222 | price: 99.9 223 | downloads: 200 224 | images: 225 | - https://picsum.photos/600 226 | - https://picsum.photos/600 227 | - https://picsum.photos/600 228 | - https://picsum.photos/600 229 | premium: false 230 | website: https://stracke-feil.info/sonya 231 | published_at: '2019-09-11' 232 | - name: Gorgeous Plastic Bench 233 | description: Earum sint molestiae. Odio sed voluptatem. Maxime magni earum. Aut voluptatibus 234 | tempore. Voluptates mollitia repellendus. Deleniti dolorem excepturi. Voluptas 235 | ea voluptatibus. Cumque eaque laboriosam. Nam ut dignissimos. Sunt aut et. Ut consequatur 236 | magni. Et enim est. A voluptates ut. Nihil sed provident. Sed aliquid voluptatem. 237 | category: Transportation 238 | tags: 239 | - librarian 240 | status: New 241 | price: 30.0 242 | downloads: 20 243 | images: 244 | - https://picsum.photos/600 245 | - https://picsum.photos/600 246 | premium: false 247 | website: https://cummerata.org/luke 248 | published_at: '2021-01-16' 249 | - name: Ergonomic Leather Coat 250 | description: Nihil sed ut. Aperiam voluptatibus architecto. Velit ullam rerum. Distinctio 251 | ut dolorem. Eligendi adipisci est. Debitis consequatur vero. Nostrum rerum animi. 252 | Vel quia quo. Vero nobis excepturi. At deserunt molestiae. Quis harum repellendus. 253 | Repellendus velit aliquid. Ipsam in deserunt. Et amet iusto. Voluptas consectetur 254 | nihil. 255 | category: Transportation 256 | tags: 257 | - doctor 258 | status: Beta 259 | price: 30.0 260 | downloads: 500 261 | images: 262 | - https://picsum.photos/600 263 | - https://picsum.photos/600 264 | - https://picsum.photos/600 265 | premium: false 266 | website: https://heathcote.info/lashawn_schaefer 267 | published_at: '2019-08-09' 268 | - name: Ergonomic Iron Wallet 269 | description: Deserunt molestiae incidunt. Sit quis repellendus. Necessitatibus impedit 270 | et. Aut rerum laboriosam. Voluptas qui libero. Expedita eos dolores. Officiis ipsum 271 | qui. Quaerat ut ipsum. Optio omnis beatae. Officiis voluptatum illo. Expedita omnis 272 | vitae. Quod beatae veritatis. Nobis veritatis ratione. Ipsam sequi in. Sed eaque 273 | doloribus. 274 | category: Law Practice 275 | tags: 276 | - painter 277 | - lawyer 278 | status: New 279 | price: 300.0 280 | downloads: 400 281 | images: 282 | - https://picsum.photos/600 283 | premium: false 284 | website: https://leannon.org/jefferey 285 | published_at: '2018-02-16' 286 | - name: Sleek Iron Pants 287 | description: Eum autem est. Suscipit et sed. Error ut quis. Ex voluptatibus adipisci. 288 | Quidem recusandae sequi. Corporis quidem explicabo. Error qui voluptas. Vitae accusantium 289 | dolores. Eum non ullam. Qui et odit. Et ad dolore. Tempore exercitationem facere. Nam 290 | numquam et. Quo enim nihil. Dolor dolor suscipit. 291 | category: Accounting 292 | tags: 293 | - receptionist 294 | status: New 295 | price: 200.0 296 | downloads: 1200 297 | images: 298 | - https://picsum.photos/600 299 | premium: false 300 | website: https://kessler-mcglynn.com/josue 301 | published_at: '2019-02-14' 302 | - name: Incredible Rubber Clock 303 | description: Voluptatibus qui quaerat. Aut ea enim. Laboriosam laborum veritatis. Doloremque 304 | saepe corporis. Accusamus delectus mollitia. Beatae quasi eum. Debitis quia ut. 305 | Ab quos autem. Et voluptatibus occaecati. Explicabo laboriosam eos. Ut molestias 306 | reiciendis. Sed qui ut. Deleniti dicta hic. Repellat quos veniam. Ullam omnis voluptatem. 307 | category: Wireless 308 | tags: 309 | - coach 310 | - web-developer 311 | - politician 312 | status: New 313 | price: 19.9 314 | downloads: 90 315 | images: 316 | - https://picsum.photos/600 317 | - https://picsum.photos/600 318 | - https://picsum.photos/600 319 | - https://picsum.photos/600 320 | - https://picsum.photos/600 321 | premium: true 322 | website: https://marvin-franecki.org/crystle_mclaughlin 323 | published_at: '2018-07-12' 324 | - name: Synergistic Wooden Chair 325 | description: Voluptas dolores delectus. Atque exercitationem eius. In est consequuntur. Maiores 326 | aut veniam. Quo quibusdam vel. Rem nam animi. Ullam et eum. Voluptas harum est. 327 | Rem exercitationem quaerat. Fuga sed rerum. Et exercitationem quae. Sunt impedit 328 | qui. Dolore libero ut. Officiis rem et. Dolorem aspernatur esse. 329 | category: Wireless 330 | tags: 331 | - librarian 332 | - musician 333 | status: Updated 334 | price: 24.9 335 | downloads: 1600 336 | images: 337 | - https://picsum.photos/600 338 | - https://picsum.photos/600 339 | premium: true 340 | website: https://jaskolski.org/katharyn 341 | published_at: '2021-11-08' 342 | - name: Enormous Paper Car 343 | description: Quis sequi dignissimos. Quis illum ratione. Illum dolorem voluptates. Cupiditate 344 | qui dicta. Deleniti totam temporibus. Amet rerum suscipit. Itaque doloremque perspiciatis. 345 | Iure beatae placeat. Eos tempore et. Suscipit sint qui. Vero veniam eos. Voluptas 346 | qui dolorum. Quam voluptatem magnam. Est accusantium quas. Consequatur laboriosam 347 | iure. 348 | category: Luxury Goods & Jewelry 349 | tags: 350 | - lawyer 351 | - scientist 352 | status: Updated 353 | price: 30.0 354 | downloads: 200 355 | images: 356 | - https://picsum.photos/600 357 | - https://picsum.photos/600 358 | - https://picsum.photos/600 359 | - https://picsum.photos/600 360 | - https://picsum.photos/600 361 | premium: true 362 | website: https://schultz.info/kerry 363 | published_at: '2021-01-19' 364 | - name: Aerodynamic Wool Table 365 | description: Earum iure rerum. Sequi in est. Delectus ducimus sint. Aspernatur perspiciatis 366 | rerum. Qui et ea. Enim amet est. Dolor qui deserunt. Mollitia beatae rerum. Et 367 | numquam harum. Rerum est ipsum. Unde facere cumque. Dolor et sed. Mollitia tenetur 368 | provident. Nihil illum blanditiis. Hic numquam quis. 369 | category: Luxury Goods & Jewelry 370 | tags: 371 | - philosopher 372 | - physicist 373 | status: Updated 374 | price: 24.9 375 | downloads: 100 376 | images: 377 | - https://picsum.photos/600 378 | - https://picsum.photos/600 379 | - https://picsum.photos/600 380 | - https://picsum.photos/600 381 | - https://picsum.photos/600 382 | premium: false 383 | website: https://gottlieb.name/dewayne 384 | published_at: '2022-07-30' 385 | - name: Sleek Iron Hat 386 | description: Rerum possimus vitae. Aspernatur saepe qui. Harum rem voluptas. Inventore 387 | debitis quo. Velit ex quaerat. In rerum animi. Repellendus pariatur culpa. Odit 388 | voluptatem et. Eius distinctio impedit. Id in amet. Officiis nisi deserunt. Ex 389 | quae quasi. Atque et quia. Voluptates et voluptate. Itaque soluta excepturi. 390 | category: Luxury Goods & Jewelry 391 | tags: 392 | - painter 393 | status: New 394 | price: 300.0 395 | downloads: 220 396 | images: 397 | - https://picsum.photos/600 398 | premium: false 399 | website: https://gutmann.info/charley 400 | published_at: '2020-08-22' 401 | -------------------------------------------------------------------------------- /data/config.yaml: -------------------------------------------------------------------------------- 1 | # Main settings 2 | title: My Collection 3 | description: This is an example of a Tonic project. 4 | 5 | # Style/UI 6 | main_color: "#6366f1" 7 | background_color: "#e2e8f0" 8 | font_family: "Lato" 9 | links: 10 | - text: About Us 11 | url: "#" 12 | - text: Contact 13 | url: "#" 14 | 15 | # Sorting 16 | sorting: 17 | default_order: "price asc" 18 | 19 | # Filters 20 | filters: 21 | exclude: 22 | - video 23 | - audio 24 | -------------------------------------------------------------------------------- /lib/tonic.rb: -------------------------------------------------------------------------------- 1 | require "yaml" 2 | require "open-uri" 3 | 4 | require_relative "tonic/utils" 5 | require_relative "tonic/helpers" 6 | require_relative "tonic/filters" 7 | 8 | module Tonic 9 | VERSION = "0.18.0" 10 | REPO = "https://github.com/Subgin/tonic" 11 | MAGIC_ATTRS = %w(name description images category tags id dom_id detail_page_link) 12 | SKIP_FOR_FILTERS = MAGIC_ATTRS - %w(category tags) 13 | DEFAULT_COLOR = "#3e76d1" 14 | DEFAULT_BG_COLOR = "#f3f4f6" 15 | DEFAULT_ORDER = "name asc" 16 | SHARING_PLATFORMS = %w(facebook twitter linkedin pinterest whatsapp telegram email) 17 | 18 | def self.start(context) 19 | # Inject helpers 20 | context.helpers Tonic::Utils, Tonic::Helpers, Tonic::Filters 21 | 22 | # Fetch remote collection if any 23 | if collection_url = raw_config["remote_collection"] 24 | remote_collection = URI.open(collection_url).read rescue nil 25 | File.write("data/collection.yaml", remote_collection) if remote_collection 26 | end 27 | 28 | # Create a detail page for each item if enabled 29 | if raw_config.fetch("detail_pages", true) 30 | context.data.collection.each do |item| 31 | context.proxy "/#{Tonic::Utils.slugify(item.name)}.html", "/templates/collection/detail_page.html", locals: { item: item } 32 | end 33 | end 34 | 35 | # Do not build detail page template 36 | context.ignore "/templates/collection/detail_page.html" 37 | end 38 | 39 | private 40 | 41 | def self.raw_config 42 | @raw_config ||= YAML.load_file("data/config.yaml") 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/tonic/filters.rb: -------------------------------------------------------------------------------- 1 | module Tonic 2 | module Filters 3 | def render_filters 4 | attributes = tonic_collection.flat_map(&:keys).uniq.sort 5 | 6 | attributes.map do |attribute| 7 | next if Tonic::SKIP_FOR_FILTERS.include?(attribute) 8 | next if config.filters&.exclude&.include?(attribute) 9 | 10 | content_tag(:div, class: "px-6 py-3 border-b border-gray-500 w-full") do 11 | type = config.filters&.type&.dig(attribute) 12 | smart_filter(attribute, type) 13 | end 14 | end.compact.join 15 | end 16 | 17 | private 18 | 19 | def smart_filter(attribute, type = nil) 20 | # Take sample value from 1st element 21 | value = tonic_collection[0][attribute] 22 | 23 | if !type 24 | type = "tags" if value.is_a?(Array) 25 | type = "boolean" if is_bool?(value) 26 | type = "text" if is_hash?(value) 27 | type = smart_numeric_filter(attribute) if value.is_a?(Numeric) 28 | type = smart_text_filter(attribute, value) if value.is_a?(String) 29 | end 30 | 31 | send("#{type}_filter", attribute) 32 | end 33 | 34 | def smart_numeric_filter(attribute) 35 | uniq_values = fetch_values(attribute).size 36 | 37 | if uniq_values <= 5 38 | "radio_buttons" 39 | elsif uniq_values <= 15 40 | "numeric_select_range" 41 | else 42 | "numeric_range" 43 | end 44 | end 45 | 46 | def smart_text_filter(attribute, value) 47 | if attribute == "category" 48 | "select" 49 | elsif attribute.end_with?("_at") && is_date?(value) 50 | "date_range" 51 | elsif single_word?(value) && !is_url?(value) && !is_email?(value) 52 | uniq_values = fetch_values(attribute).size 53 | uniq_values <= 5 ? "radio_buttons" : "select" 54 | else 55 | "text" 56 | end 57 | end 58 | 59 | def label(attribute) 60 | content_tag(:label, attribute.humanize, class: "text-white py-1") 61 | end 62 | 63 | def text_filter(attribute) 64 | partial("templates/filters/text", locals: { attribute: attribute }) 65 | end 66 | 67 | def numeric_range_filter(attribute) 68 | range = fetch_values(attribute) 69 | min, max = range.minmax 70 | 71 | partial("templates/filters/numeric_range", locals: { attribute: attribute, min: min, max: max }) 72 | end 73 | 74 | def numeric_select_range_filter(attribute) 75 | options = fetch_values(attribute) 76 | min, max = options.minmax 77 | options = ["All"] + options.sort 78 | 79 | partial("templates/filters/numeric_select_range", locals: { attribute: attribute, options: options, min: min, max: max }) 80 | end 81 | 82 | def date_range_filter(attribute) 83 | range = fetch_values(attribute) 84 | min, max = range.minmax 85 | 86 | partial("templates/filters/date_range", locals: { attribute: attribute, min: min, max: max }) 87 | end 88 | 89 | def tags_filter(attribute) 90 | tags = fetch_values(attribute).sort 91 | 92 | partial("templates/filters/tags", locals: { attribute: attribute, tags: tags }) 93 | end 94 | 95 | def select_filter(attribute) 96 | options = fetch_values(attribute) 97 | options = ["All"] + options.sort 98 | 99 | partial("templates/filters/select", locals: { attribute: attribute, options: options }) 100 | end 101 | 102 | def boolean_filter(attribute) 103 | partial("templates/filters/boolean", locals: { attribute: attribute }) 104 | end 105 | 106 | def radio_buttons_filter(attribute) 107 | options = fetch_values(attribute) 108 | options = ["All"] + options.sort 109 | 110 | partial("templates/filters/radio_buttons", locals: { attribute: attribute, options: options }) 111 | end 112 | 113 | def fetch_values(attribute) 114 | tonic_collection.flat_map(&:"#{attribute}").compact.uniq 115 | end 116 | end 117 | end 118 | -------------------------------------------------------------------------------- /lib/tonic/helpers.rb: -------------------------------------------------------------------------------- 1 | module Tonic 2 | module Helpers 3 | def config 4 | data.config.reverse_merge( 5 | title: "Tonic Example", 6 | detail_pages: true, 7 | item_card_image: true, 8 | sorting: { default_order: Tonic::DEFAULT_ORDER } 9 | ) 10 | end 11 | 12 | def tonic_collection 13 | data.collection.each do |item| 14 | item.id = slugify(item.name) 15 | item.dom_id = "item_#{item.id}" 16 | 17 | validate_item!(item) 18 | end 19 | end 20 | 21 | def rest_of_attrs(item) 22 | (item.keys - Tonic::MAGIC_ATTRS).sort 23 | end 24 | 25 | def detail_page_url(item) 26 | if item.detail_page_link.present? 27 | item.detail_page_link 28 | elsif config.detail_pages 29 | "/#{item.id}" 30 | end 31 | end 32 | 33 | def sorting_options 34 | options = tonic_collection[0].select do |k, v| 35 | k == "name" || 36 | v.is_a?(Numeric) || 37 | (v.is_a?(String) && k.end_with?("_at") && is_date?(v)) 38 | end.keys 39 | 40 | if exclude = config.sorting.exclude 41 | options = options - exclude 42 | end 43 | 44 | options.flat_map do |option| 45 | ["#{option} asc", "#{option} desc"] 46 | end.sort 47 | end 48 | 49 | def sort_link(option) 50 | attribute, direction = option.split(" ") 51 | 52 | link_to "#{attribute.humanize} #{direction.upcase}", "#", onclick: "sortBy('#{option}')", data: { sort_by: option } 53 | end 54 | 55 | def sharing_platforms 56 | return Tonic::SHARING_PLATFORMS if !config.sharing_platforms 57 | 58 | Tonic::SHARING_PLATFORMS.select do |platform| 59 | config.sharing_platforms.include?(platform) 60 | end 61 | end 62 | 63 | private 64 | 65 | def validate_item!(item) 66 | if item.name.blank? 67 | raise "[Tonic] Name can't be blank:\n#{item.to_h}\n" 68 | end 69 | 70 | if data.collection.count { |el| el.name == item.name } > 1 71 | raise "[Tonic] Name should be unique:\n#{item.to_h}\n" 72 | end 73 | end 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /lib/tonic/utils.rb: -------------------------------------------------------------------------------- 1 | module Tonic 2 | module Utils 3 | extend self 4 | 5 | def slugify(text) 6 | text&.parameterize 7 | end 8 | 9 | def strip_truncate(html, length) 10 | truncate(strip_tags(html), length: length) 11 | end 12 | 13 | def single_word?(string) 14 | !string.strip.include? " " 15 | end 16 | 17 | def render_tags(tags) 18 | return if !tags 19 | 20 | tags.sort.map do |tag| 21 | "#{tag}" 22 | end.join(" ") 23 | end 24 | 25 | def render_hash(hash) 26 | hash.map do |k, v| 27 | if is_hash?(v) 28 | render_hash(v) 29 | else 30 | "#{k.titleize}: #{v}" 31 | end 32 | end.join(" | ") 33 | end 34 | 35 | def render_video(video_url) 36 | embed_url = VideoInfo.new(video_url).embed_url 37 | 38 | "" 39 | end 40 | 41 | def render_audio(audio_url) 42 | "" 43 | end 44 | 45 | def is_bool?(value) 46 | value.is_a?(TrueClass) || value.is_a?(FalseClass) 47 | end 48 | 49 | def is_date?(value) 50 | Date.parse(value) 51 | rescue Date::Error 52 | false 53 | end 54 | 55 | def is_url?(string) 56 | string.match?(URI.regexp) 57 | end 58 | 59 | def is_email?(string) 60 | string.match?(URI::MailTo::EMAIL_REGEXP) 61 | end 62 | 63 | def is_hash?(object) 64 | object.class.name.end_with?("Hash") 65 | end 66 | 67 | def is_video?(string) 68 | VideoInfo.valid_url?(string) 69 | end 70 | 71 | def is_audio?(string) 72 | string.match?(/\.(mp3|ogg|wav)$/) 73 | end 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tonic", 3 | "private": true, 4 | "scripts": { 5 | "dev": "yarn dev:js & yarn dev:css", 6 | "build": "yarn build:js & yarn build:css", 7 | "dev:js": "esbuild ./source/javascripts/application.js --bundle --watch=forever --outdir=dist", 8 | "build:js": "esbuild ./source/javascripts/application.js --bundle --minify --outdir=dist", 9 | "dev:css": "tailwindcss -i ./source/stylesheets/application.css -o ./dist/application.css --watch", 10 | "build:css": "tailwindcss -i ./source/stylesheets/application.css -o ./dist/application.css --minify" 11 | }, 12 | "dependencies": { 13 | "@tailwindcss/forms": "^0.5.7", 14 | "esbuild": "^0.25.0", 15 | "ralix": "^1.4.0", 16 | "sharer.js": "^0.5.1", 17 | "tailwindcss": "^3.3.6" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /source/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Subgin/tonic/816489d22f91bb48b07365e06f4171bae6f1dae4/source/images/favicon.ico -------------------------------------------------------------------------------- /source/images/icons/chevron-down.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source/images/icons/chevron-left.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source/images/icons/close.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source/images/icons/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /source/images/icons/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /source/images/icons/share.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source/images/icons/sharing/email.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /source/images/icons/sharing/facebook.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /source/images/icons/sharing/linkedin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /source/images/icons/sharing/pinterest.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /source/images/icons/sharing/telegram.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /source/images/icons/sharing/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /source/images/icons/sharing/whatsapp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /source/index.html.erb: -------------------------------------------------------------------------------- 1 | --- 2 | title: Home 3 | --- 4 | 5 |
6 | <%= partial "templates/components/sidebar" unless config.hide_filters %> 7 | 8 |
9 |
10 |

<%= config.title %>

11 | <%= config.description %> 12 |
13 | 14 |
15 |
16 | <%= inline_svg 'icons/search.svg' %> 17 |
18 | 19 |
20 | 21 |
22 |
23 | <%= tonic_collection.size %> items found 24 |
25 | 26 |
27 | 30 | 31 | <% unless config.hide_sharing %> 32 | <%= partial 'templates/components/sharing', locals: { extra_classes: 'right-0 md:right-20' } %> 33 | <% end %> 34 | 35 | <% unless config.hide_sorting %> 36 | <%= partial 'templates/components/sorting' %> 37 | <% end %> 38 |
39 |
40 | 41 |
42 | <% tonic_collection.each do |item| %> 43 | <%= partial 'templates/collection/item_card', locals: { item: item } %> 44 | <% end %> 45 |
46 |
47 |
48 | -------------------------------------------------------------------------------- /source/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // Dependencies 2 | import { RalixApp } from 'ralix' 3 | import 'sharer.js' 4 | 5 | // Controllers 6 | import AppCtrl from './controllers/app' 7 | 8 | const App = new RalixApp({ 9 | routes: { 10 | '/.*': AppCtrl 11 | }, 12 | }) 13 | 14 | App.start() 15 | -------------------------------------------------------------------------------- /source/javascripts/controllers/app.js: -------------------------------------------------------------------------------- 1 | import { contains, stripTags, deepValues, sortArray } from '../utils' 2 | 3 | export default class AppCtrl { 4 | constructor() { 5 | self.currentFilters = {} 6 | 7 | // Open Sidebar by default on bigger screens 8 | if (window.innerWidth > 900) this.toggleSidebar() 9 | 10 | setTimeout(() => { 11 | // Apply filtering by params 12 | this.defaultFilters() 13 | 14 | // Apply default sorting 15 | const defaultOrder = getParam('sorting') || window.config.sorting.default_order 16 | this.sortBy(defaultOrder, false) 17 | }) 18 | } 19 | 20 | toggleSidebar() { 21 | toggleClass('#sidebar', 'hidden') 22 | } 23 | 24 | toggleSorting() { 25 | toggleClass('#sorting-options', 'hidden') 26 | 27 | if (!hasClass('#sorting-options', 'hidden')) addClass('#sharing-options', 'hidden') 28 | } 29 | 30 | toggleSharing() { 31 | toggleClass('#sharing-options', 'hidden') 32 | 33 | if (!hasClass('#sharing-options', 'hidden')) { 34 | addClass('#sorting-options', 'hidden') 35 | 36 | // Prepare data-* attributes for share & copy actions 37 | attr('#share_url', 'value', currentUrl()) 38 | findAll('#sharing-buttons a').forEach(el => { 39 | data(el, { title: find('title').innerText, url: currentUrl() }) 40 | }) 41 | } 42 | } 43 | 44 | defaultFilters() { 45 | Object.entries(getParam()).forEach(([key, value]) => { 46 | if (!value || key == 'sorting') return 47 | 48 | const el = find(`#${key}`) || find(`#${key}_${value}`) 49 | 50 | switch(el?.type) { 51 | case 'number': 52 | case 'text': 53 | el.value = value 54 | el.dispatchEvent(new KeyboardEvent('keyup')) 55 | 56 | break; 57 | case 'date': 58 | case 'select-one': 59 | el.value = value 60 | el.dispatchEvent(new KeyboardEvent('change')) 61 | 62 | break; 63 | case 'radio': 64 | el.click() 65 | 66 | break; 67 | case 'submit': 68 | value.split(',').forEach(tag => { 69 | find(`#tags_${tag}`).click() 70 | }) 71 | 72 | break; 73 | } 74 | }) 75 | } 76 | 77 | filterBy(type) { 78 | const el = currentElement() 79 | self.currentFilters[el.name] = { 80 | element: el, 81 | type: type 82 | } 83 | 84 | // Hide all items 85 | addClass('article', 'hidden') 86 | 87 | // Hide sharing menu 88 | addClass('#sharing-options', 'hidden') 89 | 90 | // Display reset link 91 | removeClass('#reset', 'hidden') 92 | 93 | // Update URL parameters 94 | setParam(el.name, el.value) 95 | 96 | // Specific handling for tags 97 | if (type == 'tags') { 98 | toggleClass(el, 'active') 99 | setParam(el.name, activeTags()) 100 | } 101 | 102 | window.collection.forEach(item => { 103 | let show = true 104 | 105 | Object.values(self.currentFilters).forEach((filter) => { 106 | show = show && applyFilter(item, filter) 107 | if (!show) return 108 | }) 109 | 110 | if (show) showItem(item) 111 | }) 112 | 113 | // Update counter with visible items 114 | insertHTML('#counter', activeItems().length) 115 | } 116 | 117 | applyFilter(item, filter) { 118 | const el = filter.element 119 | const attribute = el.name 120 | let filterValue = el.value 121 | let itemValue = item[attribute] 122 | 123 | switch(filter.type) { 124 | case 'global_text': 125 | const itemContent = deepValues(item) 126 | 127 | if (contains(stripTags(itemContent), filterValue)) 128 | return true 129 | 130 | break; 131 | case 'text': 132 | if (itemValue instanceof Object) 133 | itemValue = deepValues(itemValue) 134 | 135 | if (contains(itemValue, filterValue)) 136 | return true 137 | 138 | break; 139 | case 'select': 140 | case 'radio_buttons': 141 | if (filterValue == 'All' || filterValue == itemValue) 142 | return true 143 | 144 | break; 145 | case 'numeric_range': 146 | itemValue = item[attribute.replace(/_min$|_max$/, '')] 147 | filterValue = parseFloat(filterValue) || 0 148 | 149 | if (contains(attribute, '_min$') && itemValue >= filterValue) 150 | return true 151 | 152 | if (contains(attribute, '_max$') && itemValue <= filterValue) 153 | return true 154 | 155 | break; 156 | case 'date_range': 157 | itemValue = Date.parse(item[attribute.replace(/_min$|_max$/, '')]) 158 | filterValue = Date.parse(filterValue) 159 | 160 | if (contains(attribute, '_min$') && itemValue >= filterValue) 161 | return true 162 | 163 | if (contains(attribute, '_max$') && itemValue <= filterValue) 164 | return true 165 | 166 | break; 167 | case 'tags': 168 | if (itemValue && activeTags().every(tag => itemValue.includes(tag))) 169 | return true 170 | 171 | break; 172 | case 'boolean': 173 | if (filterValue == 'All') 174 | return true 175 | 176 | if (filterValue == 'true' && itemValue) 177 | return true 178 | 179 | if (filterValue == 'false' && !itemValue) 180 | return true 181 | 182 | break; 183 | default: 184 | return false 185 | } 186 | } 187 | 188 | sortBy(sorting, interactive = true) { 189 | const [attribute, direction] = sorting.split(' ') 190 | const container = find('#collection-container') 191 | const items = [] 192 | 193 | this.activeItems().forEach(itemDom => { 194 | let item = window.collection.find(item => item.dom_id == itemDom.id) 195 | items.push(item) 196 | }) 197 | 198 | sortArray(items, attribute, direction).forEach(item => { 199 | container.appendChild(find(`#${item.dom_id}`)) 200 | }) 201 | 202 | // Highlight current sorting 203 | findAll('#sorting-options a').forEach(link => { 204 | const isActive = data(link, 'sortBy') == sorting 205 | toggleClass(link, 'active', isActive) 206 | }) 207 | 208 | if (interactive) { 209 | setParam('sorting', sorting) 210 | toggleSorting() 211 | } 212 | } 213 | 214 | showItem(item) { 215 | removeClass(`#${item.dom_id}`, 'hidden') 216 | } 217 | 218 | activeTags() { 219 | return Array.from(findAll('.tag.active')).map(tag => tag.value) 220 | } 221 | 222 | activeItems() { 223 | return findAll('article:not(.hidden)') 224 | } 225 | 226 | copyToClipboard(target) { 227 | find(target).select() 228 | document.execCommand('copy') 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /source/javascripts/utils.js: -------------------------------------------------------------------------------- 1 | export function contains(content, search) { 2 | const regexp = new RegExp(search, 'i') 3 | 4 | return regexp.test(content) 5 | } 6 | 7 | export function stripTags(string) { 8 | const parseHTML = new DOMParser().parseFromString(string, 'text/html') 9 | 10 | return parseHTML.body.textContent || '' 11 | } 12 | 13 | export function deepValues(obj, values = []) { 14 | Object.values(obj).forEach(val => { 15 | if (val instanceof Object) 16 | deepValues(val, values) 17 | else 18 | values.push(val) 19 | }) 20 | 21 | return values.join(' ') 22 | } 23 | 24 | export function sortArray(array, attribute, direction) { 25 | return array.sort((a, b) => { 26 | if (direction == 'asc') { 27 | if (a[attribute] < b[attribute]) return -1 28 | if (a[attribute] > b[attribute]) return 1 29 | return 0 30 | } else { 31 | if (a[attribute] < b[attribute]) return 1 32 | if (a[attribute] > b[attribute]) return -1 33 | return 0 34 | } 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /source/layouts/layout.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= config.title %> | <%= @title || current_page.data.title %> 5 | 6 | 7 | <%= stylesheet_link_tag "application" %> 8 | <%= favicon_tag "favicon.ico" %> 9 | <% if config.font_family %> 10 | 11 | <% end %> 12 | 19 | 20 | 21 | <%= partial 'templates/components/header' %> 22 | <%= yield %> 23 | <%= partial 'templates/components/footer' %> 24 | 25 | <%= javascript_include_tag "application" %> 26 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /source/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | /* Base */ 6 | body { 7 | @apply leading-relaxed; 8 | background-color: var(--background-color); 9 | font-family: var(--font-family); 10 | } 11 | 12 | h1 { 13 | @apply text-4xl font-bold mt-1 mb-2; 14 | } 15 | 16 | h2 { 17 | @apply text-3xl font-bold mt-1 mb-2; 18 | } 19 | 20 | h3 { 21 | @apply text-2xl font-bold mt-1 mb-2; 22 | } 23 | 24 | p { 25 | @apply mt-2 mb-3; 26 | } 27 | 28 | a { 29 | @apply outline-none font-semibold; 30 | color: var(--main-color); 31 | } 32 | 33 | img, svg { 34 | display: inline-block; 35 | } 36 | 37 | label { 38 | @apply block; 39 | } 40 | 41 | input:not([type="radio"]):not([type="checkbox"]):not([type="submit"]), textarea, select { 42 | @apply w-full mt-2 mb-4 block rounded focus:border-gray-800 focus:ring focus:ring-gray-400 focus:ring-opacity-50; 43 | } 44 | 45 | input[type="radio"], input[type="checkbox"] { 46 | @apply mx-1 w-5 h-5 focus:border-gray-800 focus:ring focus:ring-gray-400 focus:ring-opacity-50; 47 | color: var(--main-color); 48 | } 49 | 50 | .btn { 51 | @apply text-white py-3 px-6 rounded; 52 | background-color: var(--main-color); 53 | } 54 | 55 | /* Header */ 56 | #header { 57 | background-color: var(--main-color); 58 | } 59 | 60 | #header img { 61 | max-width: 220px; 62 | max-height: 50px; 63 | } 64 | 65 | #header .nav-link { 66 | @apply text-white p-2 inline-block font-medium rounded; 67 | } 68 | 69 | #header .nav-link:hover { 70 | @apply bg-white; 71 | color: var(--main-color); 72 | } 73 | 74 | /* Sidebar */ 75 | #sidebar { 76 | @apply text-gray-500 bg-gray-800 pb-24 w-full w-64; 77 | min-width: 280px; 78 | } 79 | 80 | /* Cards */ 81 | .card { 82 | @apply shadow-md rounded bg-white; 83 | } 84 | 85 | .card-image { 86 | @apply rounded-t bg-cover bg-center h-48; 87 | } 88 | 89 | .card-header { 90 | @apply px-4 pt-2; 91 | } 92 | 93 | .card-body { 94 | @apply px-4 py-2; 95 | } 96 | 97 | .card-actions { 98 | @apply px-4 pt-4 pb-6; 99 | } 100 | 101 | /* Tags */ 102 | .tag { 103 | @apply inline-block mb-1 px-2 font-semibold border-2 rounded-full text-sm; 104 | color: var(--main-color); 105 | border-color: var(--main-color); 106 | } 107 | 108 | button.tag:hover, button.tag.active { 109 | @apply text-white; 110 | background-color: var(--main-color); 111 | } 112 | 113 | /* Dropdowns */ 114 | .dropdown { 115 | @apply absolute z-10 mt-2 w-max rounded bg-white shadow-lg; 116 | } 117 | 118 | /* Sorting menu */ 119 | #sorting-options a { 120 | @apply block px-4 py-2 text-sm; 121 | } 122 | 123 | #sorting-options a:hover, #sorting-options a.active { 124 | @apply text-white font-bold first:rounded-t last:rounded-b; 125 | background-color: var(--main-color); 126 | } 127 | 128 | /* Share buttons */ 129 | #sharing-buttons a { 130 | @apply inline-flex items-center mb-1 mr-1 p-2 rounded-full text-white; 131 | background-color: var(--main-color); 132 | } 133 | 134 | /* Images in detail page */ 135 | .image-gallery { 136 | @apply flex overflow-auto gap-4 lg:grid lg:grid-cols-4 my-4; 137 | } 138 | 139 | .image-gallery img { 140 | @apply rounded shadow max-h-80; 141 | } 142 | 143 | @media (max-width: 640px) { 144 | .image-gallery img { 145 | max-width: none; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /source/templates/collection/_item_card.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <% if config.item_card_image && bg_image = item.images&.first %> 3 | <% if page_url = detail_page_url(item) %> 4 | 5 | <% else %> 6 |
7 | <% end %> 8 | <% end %> 9 |
10 | <%= item.category %> 11 |

<%= item.name %>

12 |
13 |
14 | <%= strip_truncate(item.description, 60) %> 15 |

<%= render_tags(item.tags) %>

16 |
17 | 18 | <% if page_url = detail_page_url(item) %> 19 |
<%= link_to 'More', page_url, class: 'btn' %>
20 | <% end %> 21 |
22 | -------------------------------------------------------------------------------- /source/templates/collection/detail_page.html.erb: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layout 3 | --- 4 | 5 | <% @title = item.name %> 6 | <% @description = item.description %> 7 | 8 |
9 |
10 | 11 | <%= inline_svg 'icons/chevron-left.svg' %> Back 12 | 13 | <% unless config.hide_sharing %> 14 | <%= partial 'templates/components/sharing', locals: { extra_classes: 'right-5 md:right-32 top-32' } %> 15 | <% end %> 16 |
17 | 18 | <%= item.category %> 19 |

<%= @title %>

20 | 21 | 26 | 27 |

<%= render_tags(item.tags) %>

28 | 29 |
<%= @description %>
30 | 31 |
32 | <% rest_of_attrs(item).each do |attribute| %> 33 | <% value = item[attribute] %> 34 |
35 | <%= attribute.titleize %> 36 | 37 | <% if value.is_a?(Array) %> 38 | <%= render_tags(value) %> 39 | <% elsif is_bool?(value) %> 40 | <%= value ? "Yes" : "No" %> 41 | <% elsif value.is_a?(String) && is_url?(value) %> 42 | <% if is_video?(value) %> 43 |
<%= render_video(value) %>
44 | <% elsif is_audio?(value) %> 45 |
<%= render_audio(value) %>
46 | <% else %> 47 | <%= link_to value, value, target: "_blank" %> 48 | <% end %> 49 | <% elsif value.is_a?(String) && is_email?(value) %> 50 | <%= mail_to value %> 51 | <% elsif is_hash?(value) %> 52 | <%= render_hash(value) %> 53 | <% else %> 54 | <%= value %> 55 | <% end %> 56 |
57 |
58 | <% end %> 59 |
60 |
61 | -------------------------------------------------------------------------------- /source/templates/components/_footer.html.erb: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /source/templates/components/_header.html.erb: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /source/templates/components/_sharing.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= inline_svg 'icons/share.svg' %> Share 3 | 4 | 18 | -------------------------------------------------------------------------------- /source/templates/components/_sidebar.html.erb: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /source/templates/components/_sorting.html.erb: -------------------------------------------------------------------------------- 1 | 2 | Sort by <%= inline_svg 'icons/chevron-down.svg' %> 3 | 4 | 9 | -------------------------------------------------------------------------------- /source/templates/filters/_boolean.html.erb: -------------------------------------------------------------------------------- 1 | <%= label(attribute) %> 2 | 3 |

4 | <%= radio_button_tag(:type, id: "#{attribute}_All", name: attribute, value: 'All', onchange: "filterBy('boolean')") %> 5 | All 6 | 7 | <%= radio_button_tag(:type, id: "#{attribute}_true", name: attribute, value: 'true', onchange: "filterBy('boolean')") %> 8 | Yes 9 | 10 | <%= radio_button_tag(:type, id: "#{attribute}_false", name: attribute, value: 'false', onchange: "filterBy('boolean')") %> 11 | No 12 |

13 | -------------------------------------------------------------------------------- /source/templates/filters/_date_range.html.erb: -------------------------------------------------------------------------------- 1 | <%= label(attribute) %> 2 | 3 | From (<%= min %>) 4 | <%= input_tag(:date, id: "#{attribute}_min", name: "#{attribute}_min", min: min, onchange: "filterBy('date_range')") %> 5 | 6 | To (<%= max %>) 7 | <%= input_tag(:date, id: "#{attribute}_max", name: "#{attribute}_max", max: max, onchange: "filterBy('date_range')") %> 8 | -------------------------------------------------------------------------------- /source/templates/filters/_numeric_range.html.erb: -------------------------------------------------------------------------------- 1 | <%= label(attribute) %> 2 | 3 | From (<%= min %>) 4 | <%= input_tag(:number, id: "#{attribute}_min", name: "#{attribute}_min", min: min, onkeyup: "filterBy('numeric_range')") %> 5 | 6 | To (<%= max %>) 7 | <%= input_tag(:number, id: "#{attribute}_max", name: "#{attribute}_max", max: max, onkeyup: "filterBy('numeric_range')") %> 8 | -------------------------------------------------------------------------------- /source/templates/filters/_numeric_select_range.html.erb: -------------------------------------------------------------------------------- 1 | <%= label(attribute) %> 2 | 3 | From (<%= min %>) 4 | <%= select_tag(attribute, id: "#{attribute}_min", name: "#{attribute}_min", options: options, onchange: "filterBy('numeric_range')") %> 5 | 6 | To (<%= max %>) 7 | <%= select_tag(attribute, id: "#{attribute}_max", name: "#{attribute}_max", options: options, onchange: "filterBy('numeric_range')") %> 8 | -------------------------------------------------------------------------------- /source/templates/filters/_radio_buttons.html.erb: -------------------------------------------------------------------------------- 1 | <%= label(attribute) %> 2 | 3 | <% options.each do |option| %> 4 |
5 | <%= radio_button_tag(:type, id: "#{attribute}_#{option}", name: attribute, value: option, onchange: "filterBy('radio_buttons')") %> 6 | <%= option.to_s.titleize %> 7 |
8 | <% end %> 9 | -------------------------------------------------------------------------------- /source/templates/filters/_select.html.erb: -------------------------------------------------------------------------------- 1 | <%= label(attribute) %> 2 | <%= select_tag(attribute, id: attribute, name: attribute, options: options, onchange: "filterBy('select')") %> 3 | -------------------------------------------------------------------------------- /source/templates/filters/_tags.html.erb: -------------------------------------------------------------------------------- 1 | <%= label(attribute) %> 2 | 3 | <% tags.each do |tag| %> 4 | 7 | <% end %> 8 | -------------------------------------------------------------------------------- /source/templates/filters/_text.html.erb: -------------------------------------------------------------------------------- 1 | <%= label(attribute) %> 2 | <%= input_tag(:text, id: attribute, name: attribute, onkeyup: "filterBy('text')") %> 3 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | './source/**/*', 4 | './lib/**/*' 5 | ], 6 | plugins: [ 7 | require('@tailwindcss/forms') 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@alloc/quick-lru@^5.2.0": 6 | version "5.2.0" 7 | resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" 8 | integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== 9 | 10 | "@esbuild/aix-ppc64@0.25.0": 11 | version "0.25.0" 12 | resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz#499600c5e1757a524990d5d92601f0ac3ce87f64" 13 | integrity sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ== 14 | 15 | "@esbuild/android-arm64@0.25.0": 16 | version "0.25.0" 17 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz#b9b8231561a1dfb94eb31f4ee056b92a985c324f" 18 | integrity sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g== 19 | 20 | "@esbuild/android-arm@0.25.0": 21 | version "0.25.0" 22 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.0.tgz#ca6e7888942505f13e88ac9f5f7d2a72f9facd2b" 23 | integrity sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g== 24 | 25 | "@esbuild/android-x64@0.25.0": 26 | version "0.25.0" 27 | resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.0.tgz#e765ea753bac442dfc9cb53652ce8bd39d33e163" 28 | integrity sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg== 29 | 30 | "@esbuild/darwin-arm64@0.25.0": 31 | version "0.25.0" 32 | resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz#fa394164b0d89d4fdc3a8a21989af70ef579fa2c" 33 | integrity sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw== 34 | 35 | "@esbuild/darwin-x64@0.25.0": 36 | version "0.25.0" 37 | resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz#91979d98d30ba6e7d69b22c617cc82bdad60e47a" 38 | integrity sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg== 39 | 40 | "@esbuild/freebsd-arm64@0.25.0": 41 | version "0.25.0" 42 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz#b97e97073310736b430a07b099d837084b85e9ce" 43 | integrity sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w== 44 | 45 | "@esbuild/freebsd-x64@0.25.0": 46 | version "0.25.0" 47 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz#f3b694d0da61d9910ec7deff794d444cfbf3b6e7" 48 | integrity sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A== 49 | 50 | "@esbuild/linux-arm64@0.25.0": 51 | version "0.25.0" 52 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz#f921f699f162f332036d5657cad9036f7a993f73" 53 | integrity sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg== 54 | 55 | "@esbuild/linux-arm@0.25.0": 56 | version "0.25.0" 57 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz#cc49305b3c6da317c900688995a4050e6cc91ca3" 58 | integrity sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg== 59 | 60 | "@esbuild/linux-ia32@0.25.0": 61 | version "0.25.0" 62 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz#3e0736fcfab16cff042dec806247e2c76e109e19" 63 | integrity sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg== 64 | 65 | "@esbuild/linux-loong64@0.25.0": 66 | version "0.25.0" 67 | resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz#ea2bf730883cddb9dfb85124232b5a875b8020c7" 68 | integrity sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw== 69 | 70 | "@esbuild/linux-mips64el@0.25.0": 71 | version "0.25.0" 72 | resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz#4cababb14eede09248980a2d2d8b966464294ff1" 73 | integrity sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ== 74 | 75 | "@esbuild/linux-ppc64@0.25.0": 76 | version "0.25.0" 77 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz#8860a4609914c065373a77242e985179658e1951" 78 | integrity sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw== 79 | 80 | "@esbuild/linux-riscv64@0.25.0": 81 | version "0.25.0" 82 | resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz#baf26e20bb2d38cfb86ee282dff840c04f4ed987" 83 | integrity sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA== 84 | 85 | "@esbuild/linux-s390x@0.25.0": 86 | version "0.25.0" 87 | resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz#8323afc0d6cb1b6dc6e9fd21efd9e1542c3640a4" 88 | integrity sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA== 89 | 90 | "@esbuild/linux-x64@0.25.0": 91 | version "0.25.0" 92 | resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz#08fcf60cb400ed2382e9f8e0f5590bac8810469a" 93 | integrity sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw== 94 | 95 | "@esbuild/netbsd-arm64@0.25.0": 96 | version "0.25.0" 97 | resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz#935c6c74e20f7224918fbe2e6c6fe865b6c6ea5b" 98 | integrity sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw== 99 | 100 | "@esbuild/netbsd-x64@0.25.0": 101 | version "0.25.0" 102 | resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz#414677cef66d16c5a4d210751eb2881bb9c1b62b" 103 | integrity sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA== 104 | 105 | "@esbuild/openbsd-arm64@0.25.0": 106 | version "0.25.0" 107 | resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz#8fd55a4d08d25cdc572844f13c88d678c84d13f7" 108 | integrity sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw== 109 | 110 | "@esbuild/openbsd-x64@0.25.0": 111 | version "0.25.0" 112 | resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz#0c48ddb1494bbc2d6bcbaa1429a7f465fa1dedde" 113 | integrity sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg== 114 | 115 | "@esbuild/sunos-x64@0.25.0": 116 | version "0.25.0" 117 | resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz#86ff9075d77962b60dd26203d7352f92684c8c92" 118 | integrity sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg== 119 | 120 | "@esbuild/win32-arm64@0.25.0": 121 | version "0.25.0" 122 | resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz#849c62327c3229467f5b5cd681bf50588442e96c" 123 | integrity sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw== 124 | 125 | "@esbuild/win32-ia32@0.25.0": 126 | version "0.25.0" 127 | resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz#f62eb480cd7cca088cb65bb46a6db25b725dc079" 128 | integrity sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA== 129 | 130 | "@esbuild/win32-x64@0.25.0": 131 | version "0.25.0" 132 | resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz#c8e119a30a7c8d60b9d2e22d2073722dde3b710b" 133 | integrity sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ== 134 | 135 | "@jridgewell/gen-mapping@^0.3.2": 136 | version "0.3.3" 137 | resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" 138 | integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== 139 | dependencies: 140 | "@jridgewell/set-array" "^1.0.1" 141 | "@jridgewell/sourcemap-codec" "^1.4.10" 142 | "@jridgewell/trace-mapping" "^0.3.9" 143 | 144 | "@jridgewell/resolve-uri@^3.1.0": 145 | version "3.1.1" 146 | resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" 147 | integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== 148 | 149 | "@jridgewell/set-array@^1.0.1": 150 | version "1.1.2" 151 | resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" 152 | integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== 153 | 154 | "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": 155 | version "1.4.15" 156 | resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" 157 | integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== 158 | 159 | "@jridgewell/trace-mapping@^0.3.9": 160 | version "0.3.20" 161 | resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f" 162 | integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== 163 | dependencies: 164 | "@jridgewell/resolve-uri" "^3.1.0" 165 | "@jridgewell/sourcemap-codec" "^1.4.14" 166 | 167 | "@nodelib/fs.scandir@2.1.5": 168 | version "2.1.5" 169 | resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" 170 | integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== 171 | dependencies: 172 | "@nodelib/fs.stat" "2.0.5" 173 | run-parallel "^1.1.9" 174 | 175 | "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": 176 | version "2.0.5" 177 | resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" 178 | integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== 179 | 180 | "@nodelib/fs.walk@^1.2.3": 181 | version "1.2.8" 182 | resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" 183 | integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== 184 | dependencies: 185 | "@nodelib/fs.scandir" "2.1.5" 186 | fastq "^1.6.0" 187 | 188 | "@tailwindcss/forms@^0.5.7": 189 | version "0.5.7" 190 | resolved "https://registry.yarnpkg.com/@tailwindcss/forms/-/forms-0.5.7.tgz#db5421f062a757b5f828bc9286ba626c6685e821" 191 | integrity sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw== 192 | dependencies: 193 | mini-svg-data-uri "^1.2.3" 194 | 195 | any-promise@^1.0.0: 196 | version "1.3.0" 197 | resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" 198 | integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== 199 | 200 | anymatch@~3.1.2: 201 | version "3.1.3" 202 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" 203 | integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== 204 | dependencies: 205 | normalize-path "^3.0.0" 206 | picomatch "^2.0.4" 207 | 208 | arg@^5.0.2: 209 | version "5.0.2" 210 | resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" 211 | integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== 212 | 213 | balanced-match@^1.0.0: 214 | version "1.0.2" 215 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" 216 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 217 | 218 | binary-extensions@^2.0.0: 219 | version "2.2.0" 220 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" 221 | integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== 222 | 223 | brace-expansion@^1.1.7: 224 | version "1.1.11" 225 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 226 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 227 | dependencies: 228 | balanced-match "^1.0.0" 229 | concat-map "0.0.1" 230 | 231 | braces@^3.0.3, braces@~3.0.2: 232 | version "3.0.3" 233 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" 234 | integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== 235 | dependencies: 236 | fill-range "^7.1.1" 237 | 238 | camelcase-css@^2.0.1: 239 | version "2.0.1" 240 | resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" 241 | integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== 242 | 243 | chokidar@^3.5.3: 244 | version "3.5.3" 245 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" 246 | integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== 247 | dependencies: 248 | anymatch "~3.1.2" 249 | braces "~3.0.2" 250 | glob-parent "~5.1.2" 251 | is-binary-path "~2.1.0" 252 | is-glob "~4.0.1" 253 | normalize-path "~3.0.0" 254 | readdirp "~3.6.0" 255 | optionalDependencies: 256 | fsevents "~2.3.2" 257 | 258 | commander@^4.0.0: 259 | version "4.1.1" 260 | resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" 261 | integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== 262 | 263 | concat-map@0.0.1: 264 | version "0.0.1" 265 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 266 | integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== 267 | 268 | cssesc@^3.0.0: 269 | version "3.0.0" 270 | resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" 271 | integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== 272 | 273 | didyoumean@^1.2.2: 274 | version "1.2.2" 275 | resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" 276 | integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== 277 | 278 | dlv@^1.1.3: 279 | version "1.1.3" 280 | resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" 281 | integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== 282 | 283 | esbuild@^0.25.0: 284 | version "0.25.0" 285 | resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.0.tgz#0de1787a77206c5a79eeb634a623d39b5006ce92" 286 | integrity sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw== 287 | optionalDependencies: 288 | "@esbuild/aix-ppc64" "0.25.0" 289 | "@esbuild/android-arm" "0.25.0" 290 | "@esbuild/android-arm64" "0.25.0" 291 | "@esbuild/android-x64" "0.25.0" 292 | "@esbuild/darwin-arm64" "0.25.0" 293 | "@esbuild/darwin-x64" "0.25.0" 294 | "@esbuild/freebsd-arm64" "0.25.0" 295 | "@esbuild/freebsd-x64" "0.25.0" 296 | "@esbuild/linux-arm" "0.25.0" 297 | "@esbuild/linux-arm64" "0.25.0" 298 | "@esbuild/linux-ia32" "0.25.0" 299 | "@esbuild/linux-loong64" "0.25.0" 300 | "@esbuild/linux-mips64el" "0.25.0" 301 | "@esbuild/linux-ppc64" "0.25.0" 302 | "@esbuild/linux-riscv64" "0.25.0" 303 | "@esbuild/linux-s390x" "0.25.0" 304 | "@esbuild/linux-x64" "0.25.0" 305 | "@esbuild/netbsd-arm64" "0.25.0" 306 | "@esbuild/netbsd-x64" "0.25.0" 307 | "@esbuild/openbsd-arm64" "0.25.0" 308 | "@esbuild/openbsd-x64" "0.25.0" 309 | "@esbuild/sunos-x64" "0.25.0" 310 | "@esbuild/win32-arm64" "0.25.0" 311 | "@esbuild/win32-ia32" "0.25.0" 312 | "@esbuild/win32-x64" "0.25.0" 313 | 314 | fast-glob@^3.3.0: 315 | version "3.3.2" 316 | resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" 317 | integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== 318 | dependencies: 319 | "@nodelib/fs.stat" "^2.0.2" 320 | "@nodelib/fs.walk" "^1.2.3" 321 | glob-parent "^5.1.2" 322 | merge2 "^1.3.0" 323 | micromatch "^4.0.4" 324 | 325 | fastq@^1.6.0: 326 | version "1.15.0" 327 | resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" 328 | integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== 329 | dependencies: 330 | reusify "^1.0.4" 331 | 332 | fill-range@^7.1.1: 333 | version "7.1.1" 334 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" 335 | integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== 336 | dependencies: 337 | to-regex-range "^5.0.1" 338 | 339 | fs.realpath@^1.0.0: 340 | version "1.0.0" 341 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 342 | integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== 343 | 344 | fsevents@~2.3.2: 345 | version "2.3.2" 346 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 347 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 348 | 349 | function-bind@^1.1.1: 350 | version "1.1.1" 351 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 352 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 353 | 354 | function-bind@^1.1.2: 355 | version "1.1.2" 356 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" 357 | integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== 358 | 359 | glob-parent@^5.1.2, glob-parent@~5.1.2: 360 | version "5.1.2" 361 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" 362 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 363 | dependencies: 364 | is-glob "^4.0.1" 365 | 366 | glob-parent@^6.0.2: 367 | version "6.0.2" 368 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" 369 | integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== 370 | dependencies: 371 | is-glob "^4.0.3" 372 | 373 | glob@7.1.6: 374 | version "7.1.6" 375 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" 376 | integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== 377 | dependencies: 378 | fs.realpath "^1.0.0" 379 | inflight "^1.0.4" 380 | inherits "2" 381 | minimatch "^3.0.4" 382 | once "^1.3.0" 383 | path-is-absolute "^1.0.0" 384 | 385 | has@^1.0.3: 386 | version "1.0.3" 387 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 388 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 389 | dependencies: 390 | function-bind "^1.1.1" 391 | 392 | hasown@^2.0.0: 393 | version "2.0.0" 394 | resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" 395 | integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== 396 | dependencies: 397 | function-bind "^1.1.2" 398 | 399 | inflight@^1.0.4: 400 | version "1.0.6" 401 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 402 | integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== 403 | dependencies: 404 | once "^1.3.0" 405 | wrappy "1" 406 | 407 | inherits@2: 408 | version "2.0.4" 409 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 410 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 411 | 412 | is-binary-path@~2.1.0: 413 | version "2.1.0" 414 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" 415 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== 416 | dependencies: 417 | binary-extensions "^2.0.0" 418 | 419 | is-core-module@^2.13.0: 420 | version "2.13.1" 421 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" 422 | integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== 423 | dependencies: 424 | hasown "^2.0.0" 425 | 426 | is-core-module@^2.9.0: 427 | version "2.11.0" 428 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" 429 | integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== 430 | dependencies: 431 | has "^1.0.3" 432 | 433 | is-extglob@^2.1.1: 434 | version "2.1.1" 435 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 436 | integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== 437 | 438 | is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: 439 | version "4.0.3" 440 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" 441 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== 442 | dependencies: 443 | is-extglob "^2.1.1" 444 | 445 | is-number@^7.0.0: 446 | version "7.0.0" 447 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" 448 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 449 | 450 | jiti@^1.19.1: 451 | version "1.21.0" 452 | resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" 453 | integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== 454 | 455 | lilconfig@^2.1.0: 456 | version "2.1.0" 457 | resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" 458 | integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== 459 | 460 | lilconfig@^3.0.0: 461 | version "3.0.0" 462 | resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.0.0.tgz#f8067feb033b5b74dab4602a5f5029420be749bc" 463 | integrity sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g== 464 | 465 | lines-and-columns@^1.1.6: 466 | version "1.2.4" 467 | resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" 468 | integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== 469 | 470 | merge2@^1.3.0: 471 | version "1.4.1" 472 | resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" 473 | integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== 474 | 475 | micromatch@^4.0.4, micromatch@^4.0.5: 476 | version "4.0.8" 477 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" 478 | integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== 479 | dependencies: 480 | braces "^3.0.3" 481 | picomatch "^2.3.1" 482 | 483 | mini-svg-data-uri@^1.2.3: 484 | version "1.4.4" 485 | resolved "https://registry.yarnpkg.com/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz#8ab0aabcdf8c29ad5693ca595af19dd2ead09939" 486 | integrity sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg== 487 | 488 | minimatch@^3.0.4: 489 | version "3.1.2" 490 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" 491 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== 492 | dependencies: 493 | brace-expansion "^1.1.7" 494 | 495 | mz@^2.7.0: 496 | version "2.7.0" 497 | resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" 498 | integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== 499 | dependencies: 500 | any-promise "^1.0.0" 501 | object-assign "^4.0.1" 502 | thenify-all "^1.0.0" 503 | 504 | nanoid@^3.3.7: 505 | version "3.3.8" 506 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" 507 | integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== 508 | 509 | normalize-path@^3.0.0, normalize-path@~3.0.0: 510 | version "3.0.0" 511 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" 512 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== 513 | 514 | object-assign@^4.0.1: 515 | version "4.1.1" 516 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 517 | integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== 518 | 519 | object-hash@^3.0.0: 520 | version "3.0.0" 521 | resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" 522 | integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== 523 | 524 | once@^1.3.0: 525 | version "1.4.0" 526 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 527 | integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== 528 | dependencies: 529 | wrappy "1" 530 | 531 | path-is-absolute@^1.0.0: 532 | version "1.0.1" 533 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 534 | integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== 535 | 536 | path-parse@^1.0.7: 537 | version "1.0.7" 538 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" 539 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== 540 | 541 | picocolors@^1.0.0: 542 | version "1.0.0" 543 | resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" 544 | integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== 545 | 546 | picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: 547 | version "2.3.1" 548 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" 549 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 550 | 551 | pify@^2.3.0: 552 | version "2.3.0" 553 | resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" 554 | integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== 555 | 556 | pirates@^4.0.1: 557 | version "4.0.5" 558 | resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" 559 | integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== 560 | 561 | postcss-import@^15.1.0: 562 | version "15.1.0" 563 | resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-15.1.0.tgz#41c64ed8cc0e23735a9698b3249ffdbf704adc70" 564 | integrity sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew== 565 | dependencies: 566 | postcss-value-parser "^4.0.0" 567 | read-cache "^1.0.0" 568 | resolve "^1.1.7" 569 | 570 | postcss-js@^4.0.1: 571 | version "4.0.1" 572 | resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.1.tgz#61598186f3703bab052f1c4f7d805f3991bee9d2" 573 | integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw== 574 | dependencies: 575 | camelcase-css "^2.0.1" 576 | 577 | postcss-load-config@^4.0.1: 578 | version "4.0.2" 579 | resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.2.tgz#7159dcf626118d33e299f485d6afe4aff7c4a3e3" 580 | integrity sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ== 581 | dependencies: 582 | lilconfig "^3.0.0" 583 | yaml "^2.3.4" 584 | 585 | postcss-nested@^6.0.1: 586 | version "6.0.1" 587 | resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.0.1.tgz#f83dc9846ca16d2f4fa864f16e9d9f7d0961662c" 588 | integrity sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ== 589 | dependencies: 590 | postcss-selector-parser "^6.0.11" 591 | 592 | postcss-selector-parser@^6.0.11: 593 | version "6.0.11" 594 | resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz#2e41dc39b7ad74046e1615185185cd0b17d0c8dc" 595 | integrity sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g== 596 | dependencies: 597 | cssesc "^3.0.0" 598 | util-deprecate "^1.0.2" 599 | 600 | postcss-value-parser@^4.0.0: 601 | version "4.2.0" 602 | resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" 603 | integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== 604 | 605 | postcss@^8.4.23: 606 | version "8.4.32" 607 | resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.32.tgz#1dac6ac51ab19adb21b8b34fd2d93a86440ef6c9" 608 | integrity sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw== 609 | dependencies: 610 | nanoid "^3.3.7" 611 | picocolors "^1.0.0" 612 | source-map-js "^1.0.2" 613 | 614 | queue-microtask@^1.2.2: 615 | version "1.2.3" 616 | resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" 617 | integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== 618 | 619 | ralix@^1.4.0: 620 | version "1.4.0" 621 | resolved "https://registry.yarnpkg.com/ralix/-/ralix-1.4.0.tgz#b38c9c23a1fd6d4bcd4d54e1f19d7a09cbb8e426" 622 | integrity sha512-2wCHeDxgvD9S06qRnxJah6b3y4BbHShmzJ0y6/+GhE0kerg54bF/hQglgPxyjpT1o/2slXwBdPeYc4Pjs/SxaA== 623 | 624 | read-cache@^1.0.0: 625 | version "1.0.0" 626 | resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" 627 | integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== 628 | dependencies: 629 | pify "^2.3.0" 630 | 631 | readdirp@~3.6.0: 632 | version "3.6.0" 633 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" 634 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== 635 | dependencies: 636 | picomatch "^2.2.1" 637 | 638 | resolve@^1.1.7: 639 | version "1.22.1" 640 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" 641 | integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== 642 | dependencies: 643 | is-core-module "^2.9.0" 644 | path-parse "^1.0.7" 645 | supports-preserve-symlinks-flag "^1.0.0" 646 | 647 | resolve@^1.22.2: 648 | version "1.22.8" 649 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" 650 | integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== 651 | dependencies: 652 | is-core-module "^2.13.0" 653 | path-parse "^1.0.7" 654 | supports-preserve-symlinks-flag "^1.0.0" 655 | 656 | reusify@^1.0.4: 657 | version "1.0.4" 658 | resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" 659 | integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== 660 | 661 | run-parallel@^1.1.9: 662 | version "1.2.0" 663 | resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" 664 | integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== 665 | dependencies: 666 | queue-microtask "^1.2.2" 667 | 668 | sharer.js@^0.5.1: 669 | version "0.5.1" 670 | resolved "https://registry.yarnpkg.com/sharer.js/-/sharer.js-0.5.1.tgz#061a7e586217caba08f4896c19b2fc3486912880" 671 | integrity sha512-d3rCcMxLsLwyneyy09yaVdUhI6yJ0Ljvi6ZaJFCGWA+BPcgpPauHKm7wcJ4YiZnMDuXN1LGYDo1XvyR3eZQ/3w== 672 | 673 | source-map-js@^1.0.2: 674 | version "1.0.2" 675 | resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" 676 | integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== 677 | 678 | sucrase@^3.32.0: 679 | version "3.34.0" 680 | resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.34.0.tgz#1e0e2d8fcf07f8b9c3569067d92fbd8690fb576f" 681 | integrity sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw== 682 | dependencies: 683 | "@jridgewell/gen-mapping" "^0.3.2" 684 | commander "^4.0.0" 685 | glob "7.1.6" 686 | lines-and-columns "^1.1.6" 687 | mz "^2.7.0" 688 | pirates "^4.0.1" 689 | ts-interface-checker "^0.1.9" 690 | 691 | supports-preserve-symlinks-flag@^1.0.0: 692 | version "1.0.0" 693 | resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" 694 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== 695 | 696 | tailwindcss@^3.3.6: 697 | version "3.3.6" 698 | resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.3.6.tgz#4dd7986bf4902ad385d90d45fd4b2fa5fab26d5f" 699 | integrity sha512-AKjF7qbbLvLaPieoKeTjG1+FyNZT6KaJMJPFeQyLfIp7l82ggH1fbHJSsYIvnbTFQOlkh+gBYpyby5GT1LIdLw== 700 | dependencies: 701 | "@alloc/quick-lru" "^5.2.0" 702 | arg "^5.0.2" 703 | chokidar "^3.5.3" 704 | didyoumean "^1.2.2" 705 | dlv "^1.1.3" 706 | fast-glob "^3.3.0" 707 | glob-parent "^6.0.2" 708 | is-glob "^4.0.3" 709 | jiti "^1.19.1" 710 | lilconfig "^2.1.0" 711 | micromatch "^4.0.5" 712 | normalize-path "^3.0.0" 713 | object-hash "^3.0.0" 714 | picocolors "^1.0.0" 715 | postcss "^8.4.23" 716 | postcss-import "^15.1.0" 717 | postcss-js "^4.0.1" 718 | postcss-load-config "^4.0.1" 719 | postcss-nested "^6.0.1" 720 | postcss-selector-parser "^6.0.11" 721 | resolve "^1.22.2" 722 | sucrase "^3.32.0" 723 | 724 | thenify-all@^1.0.0: 725 | version "1.6.0" 726 | resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" 727 | integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== 728 | dependencies: 729 | thenify ">= 3.1.0 < 4" 730 | 731 | "thenify@>= 3.1.0 < 4": 732 | version "3.3.1" 733 | resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" 734 | integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== 735 | dependencies: 736 | any-promise "^1.0.0" 737 | 738 | to-regex-range@^5.0.1: 739 | version "5.0.1" 740 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" 741 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 742 | dependencies: 743 | is-number "^7.0.0" 744 | 745 | ts-interface-checker@^0.1.9: 746 | version "0.1.13" 747 | resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" 748 | integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== 749 | 750 | util-deprecate@^1.0.2: 751 | version "1.0.2" 752 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 753 | integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== 754 | 755 | wrappy@1: 756 | version "1.0.2" 757 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 758 | integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== 759 | 760 | yaml@^2.3.4: 761 | version "2.3.4" 762 | resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.4.tgz#53fc1d514be80aabf386dc6001eb29bf3b7523b2" 763 | integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA== 764 | --------------------------------------------------------------------------------