diff --git a/CHANGELOG.md b/CHANGELOG.md index 353476e3..16f3d8e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,11 @@ # Change Log -## [2.6.0] +## [3.0.0] - [#61](https://github.com/estruyf/vscode-front-matter/issues/61): List of recently modified files - [#64](https://github.com/estruyf/vscode-front-matter/issues/64): Publish toggle for easier publishing an article +- [#65](https://github.com/estruyf/vscode-front-matter/issues/65): Aggregate articles in draft +- [#66](https://github.com/estruyf/vscode-front-matter/issues/66): New dashboard webview on which you can manage all your content ## [2.5.1] - 2020-08-23 diff --git a/README.md b/README.md index 7628045a..9a02dc73 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,17 @@

-This VSCode extension simplifies working with your markdown articles' front matter when using a static site generator like Hugo, Jekyll, Hexo, NextJs, Gatsby, and many more... For example, you can keep a list of used tags, categories and add/remove them from your article with the extension. +Front Matter is an essential Visual Studio Code extension that simplifies working and managing your markdown articles. We created the extension to support many static-site generators like Hugo, Jekyll, Hexo, NextJs, Gatsby, and more. -The extension will automatically verify if your title and description are SEO compliant. If this would not be the case, it will give you a warning. +The extension brings Content Management System (CMS) capabilities straight within Visual Studio Code. For example, you can keep a list of the used tags, categories, create content, and so much more. + +Our main extension features are: + +- Page dashboard where you can get an overview of all your markdown pages. You can use it to search, filter, sort your contents. +- Site preview within Visual Studio Code +- SEO checks for title, description, and keywords +- Support for custom actions/scripts +- and many more

Site preview @@ -30,9 +38,13 @@ The extension will automatically verify if your title and description are SEO co > If you see something missing in your article creation flow, please feel free to reach out. +**Version 3** + +In version v3 we introduced the dashboard webview. Which allows you to manage all your markdown pages in one place. This makes it easy to search, filter, sort, and more. + **Version 2** -In version v2.0.0 we released the newly redesigned sidebar panel with improved SEO support. This extension makes it the only extension to manage your Markdown pages for your static sites in Visual Studio Code. +In version v2 we released the re-designed sidebar panel with improved SEO support. This extension makes it the only extension to manage your Markdown pages for your static sites in Visual Studio Code.

@@ -46,6 +58,7 @@ In version v2.0.0 we released the newly redesigned sidebar panel with improved S

Table of Contents
  1. Markdown features
  2. +
  3. Dashboard
  4. The panel
  5. Site preview
  6. Custom actions/scripts
  7. @@ -75,6 +88,24 @@ The Front Matter extension tries to make it easy to manage your Markdown pages/c > **Info**: If you do not want this feature, you can disable it in the extension settings -> `Highlight Front Matter` or by setting the `frontMatter.content.fmHighlight` setting to `false`. +## Dashboard + +Managing your Markdown pages has never been easier in VS Code. With the Front Matter dashboard, you will be able to view all your pages and search through them, filter, sort, and much more. + +

    + Dashboard +

    + +In order to start using the dashboard, you will have to let the extension know in which folder(s) it can find your pages. A content folder can be registered or unregistered, by right-clicking on the folder in your VSCode explorer panel and clicking on the `Register folder` or `Unregister folder` menu item. + +

    + Register/unregister a folder +

    + +> **Info**: If you want, you can click on the `Open on startup?` checkbox. This setting will allow the dashboard to automatically open when you launch the project in VS Code. It will only apply to the current project, not for all of them. + +> **Details**: If your preview images aren't loaded, it might be that you need to configure the `publicFolder` where the extension can find them. For instance, in Hugo this is the `static` folder. You can configure this by updating the `frontMatter.content.publicFolder` setting. + ## The panel The Front Matter panel allows you to perform most of the extension actions by just a click on the button and it shows the SEO statuses of your title, description, and more. @@ -86,7 +117,7 @@ To leverage most of the capabilities of the extension. SEO information and every When you open the panel and the current file is not a Markdown file, it will contain the following sections:

    - Base view + Base view

    > **Info**: both **Global Settings** and **Other Actions** sections are shown for the base view as when a Markdown file is openend. @@ -113,14 +144,22 @@ When you open the Front Matter panel on a Markdown file, you get to see the foll > **Info**: To gain the `open preview` button to show up, you will need to first set the `local preview URL`. You can do this within the `Global Settings` section or by updating the `frontMatter.preview.host` setting. -**Metadata: Keywords, Tags, Categories** +**Metadata**

    - Article metadata + Article metadata

    > **Info**: By default, the tags/categories picker allows you to insert existing and none tags/categories. When you enter a none existing tag/category, the panel shows an add `+` icon in front of that button. This functionality allows you to store this tag/category in your settings. If you want to disable this feature, you can do that by setting the `frontMatter.panel.freeform` setting to `false`. +**Recently Modified** + +

    + Recently modified files +

    + +Navigate quickly to a recently modified file. In the section, the latest 10 modified files get shown. In order to use this functionality, a registered content folder needs to be present. Check [Front Matter: New article from template](#front-matter-new-article-from-template) for more information about how you can register your content folders. + **Other actions**

    @@ -191,17 +230,17 @@ When adding files in the folder, you'll be able to run the `Front Matter: New ar ## Available commands -**Front Matter: Initialize project** +### Front Matter: Initialize project This command will initialize the project with a template folder and an article template. It makes it easier to get you started with the extension and creating your content. -**Front Matter: Create a template from current file** +### Front Matter: Create a template from current file This command allows you to create a new template from the current open Markdown file. It will ask you for the name of the template and if you want to keep the current file its content in the template. > **Info**: The create as template action is also available from the `other actions` section in the Front Matter panel. -**Front Matter: New article from template** +### Front Matter: New article from template With this command, you can easily create content in your project within the registered folders and provided templates. @@ -215,7 +254,7 @@ Once you registered a folder and a template has been defined ([how to create a t > **Info**: The benefit of this command is that you do not need to search the folder in which you want to create a new article/page/... The extension will do it automatically for you. -**Front Matter: Create ** +### Front Matter: Create Creates a new and allows you to include it into your post automatically @@ -223,35 +262,35 @@ Creates a new and allows you to include it into your post autom Create tag or category

    -**Front Matter: Insert ** +### Front Matter: Insert Inserts a selected into the front matter of your article/post/... - When using this command, the Front Matter panel opens and focuses on the specified type. > **Info**: This experience changed in version `1.11.0`. -**Front Matter: Export all tags & categories to your settings** +### Front Matter: Export all tags & categories to your settings Export all the already used tags & categories in your articles/posts/... to your user settings. -**Front Matter: Remap or remove tag/category in all articles** +### Front Matter: Remap or remove tag/category in all articles This command helps you quickly update/remap or delete a tag or category in your markdown files. The extension will ask you to select the taxonomy type (*tag* or *category*), the old taxonomy value, and the new one (leave the input field *blank* to remove the tag/category). > **Info**: Once the remapping/deleting process completes. Your VSCode settings update with all new taxonomy tags/categories. -**Front Matter: Set current date** +### Front Matter: Set current date Update the `date` property of the current article/post/... to the current date & time. -**Optional**: if you want, you can specify the date property format by adding your settings' preference. Settings key: `frontMatter.taxonomy.dateFormat`. Check [date-fns formatting](https://date-fns.org/v2.0.1/docs/format) for more information on which patterns you can use. +> **Optional**: if you want, you can specify the date property format by adding your settings' preference. Settings key: `frontMatter.taxonomy.dateFormat`. Check [date-fns formatting](https://date-fns.org/v2.0.1/docs/format) for more information on which patterns you can use. -**Front Matter: Set lastmod date** +### Front Matter: Set lastmod date Update the `lastmod` (last modified) property of the current article/post/... to the current date & time. By setting the `frontMatter.content.autoUpdateDate` setting, it can be done automatically when performing changes to your markdown files. > **note**: Uses the same date format settings key as current date: `frontMatter.taxonomy.dateFormat`. -**Front Matter: Generate slug based on article title** +### Front Matter: Generate slug based on article title This command generates a clean slug for your article. It removes known stop words, punctuations, and special characters. @@ -266,7 +305,7 @@ You can also specify a prefix and suffix, which can be added to the slug if you > **Info**: At the moment, the extension only supports English stopwords. -**Front Matter: Preview article** +### Front Matter: Preview article Opens the site preview for the current article. More information about it can be found in the [site preview](#site-preview) section. @@ -455,6 +494,26 @@ Specify the path you want to add after the host and before your slug. This can b > **Important**: As the value will be formatted with the article's date, it will try to convert all characters you enter. In case you wan to skip some characters or all of them, you need to wrap that part between two single quotes. Example: `"'blog/'yyyy/MM"` will result in: `blog/2021/08`. +### `frontMatter.dashboard.openOnStart` + +Specify if you want to open the dashboard when you start VS Code. + +```json +{ + "frontMatter.dashboard.openOnStart": null +} +``` + +### `frontMatter.content.publicFolder` + +Specify the folder name where all your assets are located. For instance in Hugo this is the `static` folder. + +```json +{ + "frontMatter.content.publicFolder": "" +} +``` + ## Feedback / issues / ideas Please submit them via creating an issue in the project repository: [issue list](https://github.com/estruyf/vscode-front-matter/issues). diff --git a/assets/media/styles.css b/assets/media/styles.css index 10f0ac03..2df5fbea 100644 --- a/assets/media/styles.css +++ b/assets/media/styles.css @@ -470,4 +470,40 @@ input:checked + .field__toggle__slider:before { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; +} + +/* Sponsor */ +.sponsor { + opacity: 0.5; +} + +.sponsor:hover { + opacity: 1; +} + +.sponsor:hover svg { + fill: currentcolor; +} + +.sponsor svg { + height: 20px; + width: 20px; + margin-right: .25rem; +} + +.sponsor a { + display: flex; + align-items: center; + justify-content: center; + color: var(--vscode-foreground); + text-decoration: none; +} + +.sponsor a:hover { + color: var(--vscode-foreground); + text-decoration: none; +} + +.sponsor a > span { + margin-right: .25rem; } \ No newline at end of file diff --git a/assets/v3.0.0/baseview.png b/assets/v3.0.0/baseview.png new file mode 100644 index 00000000..ef3b51dd Binary files /dev/null and b/assets/v3.0.0/baseview.png differ diff --git a/assets/v3.0.0/dashboard.png b/assets/v3.0.0/dashboard.png new file mode 100644 index 00000000..ccbe99c4 Binary files /dev/null and b/assets/v3.0.0/dashboard.png differ diff --git a/assets/v3.0.0/metadata.png b/assets/v3.0.0/metadata.png new file mode 100644 index 00000000..a0f2e4f1 Binary files /dev/null and b/assets/v3.0.0/metadata.png differ diff --git a/assets/v3.0.0/recent-files.png b/assets/v3.0.0/recent-files.png new file mode 100644 index 00000000..60660f0b Binary files /dev/null and b/assets/v3.0.0/recent-files.png differ diff --git a/package-lock.json b/package-lock.json index de59f350..77c28a44 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,12 +48,48 @@ "lit-element": "^2.5.1" } }, + "@headlessui/react": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.4.0.tgz", + "integrity": "sha512-C+FmBVF6YGvqcEI5fa2dfVbEaXr2RGR6Kw1E5HXIISIZEfsrH/yuCgsjWw5nlRF9vbCxmQ/EKs64GAdKeb8gCw==" + }, + "@heroicons/react": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-1.0.4.tgz", + "integrity": "sha512-3kOrTmo8+Z8o6AL0rzN82MOf8J5CuxhRLFhpI8mrn+3OqekA6d5eb1GYO3EYYo1Vn6mYQSMNTzCWbEwUInb0cQ==" + }, "@iarna/toml": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.3.tgz", "integrity": "sha512-FmuxfCuolpLl0AnQ2NHSzoUKWEJDFl63qXjzdoWBVyFCXzMGm1spBzk7LeHNoVCiWCF7mRVms9e6jEV9+MoPbg==", "dev": true }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, "@types/anymatch": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", @@ -136,6 +172,12 @@ "integrity": "sha512-Agl6xbYP6FOMDeAsr3QVZ+g7Yzg0uhPHWx0j5g4LFdUBHVtqtU+gH660k/lCEe506jJLOGbEzsnqPDTZGJQLag==", "dev": true }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, "@types/prop-types": { "version": "15.7.3", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", @@ -442,6 +484,31 @@ "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", "dev": true }, + "acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dev": true, + "requires": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -492,6 +559,12 @@ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true }, + "arg": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz", + "integrity": "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -591,6 +664,20 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "autoprefixer": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.3.2.tgz", + "integrity": "sha512-RHKq0YCvhxAn9987n0Gl6lkzLd39UKwCkUPMFE0cHhxU0SvcTjBxWG/CtkZ4/HvbqK9U5V8j03nAcGBlX3er/Q==", + "dev": true, + "requires": { + "browserslist": "^4.16.8", + "caniuse-lite": "^1.0.30001251", + "colorette": "^1.3.0", + "fraction.js": "^4.1.1", + "normalize-range": "^0.1.2", + "postcss-value-parser": "^4.1.0" + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -668,8 +755,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true, - "optional": true + "dev": true }, "bindings": { "version": "1.5.0", @@ -816,6 +902,19 @@ "pako": "~1.0.5" } }, + "browserslist": { + "version": "4.16.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.8.tgz", + "integrity": "sha512-sc2m9ohR/49sWEbPj14ZSSZqp+kbi16aLao42Hmn3Z8FpjuMaq2xCA2l4zl9ITfyzvnvyE0hcg62YkIGKxgaNQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001251", + "colorette": "^1.3.0", + "electron-to-chromium": "^1.3.811", + "escalade": "^3.1.1", + "node-releases": "^1.1.75" + } + }, "buffer": { "version": "4.9.2", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", @@ -851,6 +950,12 @@ "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", "dev": true }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, "cacache": { "version": "12.0.4", "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", @@ -891,6 +996,12 @@ "unset-value": "^1.0.0" } }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, "camel-case": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.1.tgz", @@ -907,6 +1018,18 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, + "camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001251", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001251.tgz", + "integrity": "sha512-HOe1r+9VkU4TFmnU70z+r7OLmtR+/chB1rdcJUeQlAinjEeb0cKL20tlAtOagNZhbrtLnCvV19B4FmF1rgzl6A==", + "dev": true + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -1039,6 +1162,16 @@ "object-visit": "^1.0.0" } }, + "color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "requires": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1054,6 +1187,22 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "color-string": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz", + "integrity": "sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colorette": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.3.0.tgz", + "integrity": "sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==", + "dev": true + }, "commander": { "version": "2.20.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", @@ -1134,6 +1283,19 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, + "cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, "create-ecdh": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", @@ -1211,6 +1373,87 @@ "randomfill": "^1.0.3" } }, + "css-loader": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz", + "integrity": "sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==", + "dev": true, + "requires": { + "icss-utils": "^5.1.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.15", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.5" + }, + "dependencies": { + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, "css-select": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", @@ -1223,12 +1466,24 @@ "nth-check": "~1.0.1" } }, + "css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==", + "dev": true + }, "css-what": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", "dev": true }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, "csstype": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.5.tgz", @@ -1242,9 +1497,9 @@ "dev": true }, "date-fns": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.0.1.tgz", - "integrity": "sha512-C14oTzTZy8DH1Eq8N78owrCWvf3+cnJw88BTK/N3DYWVxDJuJzPaNdplzYxDYuuXXGvqBcO4Vy5SOrwAooXSWw==", + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.23.0.tgz", + "integrity": "sha512-5ycpauovVyAk0kXNZz6ZoB9AYMZB4DObse7P3BPWmyEjXNORTI8EJ6X0uaSAq4sCHzM1uajzrkr6HnsLQpxGXA==", "dev": true }, "debug": { @@ -1318,6 +1573,12 @@ } } }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, "des.js": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", @@ -1334,6 +1595,23 @@ "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", "dev": true }, + "detective": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", + "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", + "dev": true, + "requires": { + "acorn-node": "^1.6.1", + "defined": "^1.0.0", + "minimist": "^1.1.1" + } + }, + "didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, "diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", @@ -1353,6 +1631,12 @@ } } }, + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, "dom-converter": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", @@ -1445,6 +1729,12 @@ "stream-shift": "^1.0.0" } }, + "electron-to-chromium": { + "version": "1.3.814", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.814.tgz", + "integrity": "sha512-0mH03cyjh6OzMlmjauGg0TLd87ErIJqWiYxMcOLKf5w6p0YEOl7DJAj7BDlXEFmCguY5CQaKVOiMjAMODO2XDw==", + "dev": true + }, "elliptic": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", @@ -1515,6 +1805,23 @@ "prr": "~1.0.1" } }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + } + } + }, "es-abstract": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", @@ -1540,6 +1847,12 @@ "is-symbol": "^1.0.2" } }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -1722,12 +2035,61 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + } + } + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, + "fastq": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.12.0.tgz", + "integrity": "sha512-VNX0QkHK3RsXVKr9KrlUv/FoTa0NdbYoHHl7uXHv2rzyHSlxjdNAKug2twd9luJxpcyNeAgf5iPPMutJO67Dfg==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, "figgy-pudding": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", @@ -1930,6 +2292,12 @@ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", "dev": true }, + "fraction.js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.1.tgz", + "integrity": "sha512-MHOhvvxHTfRFpF1geTK9czMIZ6xclsEor2wkIGYYq+PxcQqT7vStJqjhe6S1TenZrMZzo+wlqOufBDVepUEgPg==", + "dev": true + }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -1949,6 +2317,17 @@ "readable-stream": "^2.0.0" } }, + "fs-extra": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", + "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, "fs-write-stream-atomic": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", @@ -1980,6 +2359,12 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "fuse.js": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.4.6.tgz", + "integrity": "sha512-/gYxR/0VpXmWSfZOIPS3rWwU8SHgsRTwWuXhyb2O6s7aRuVtHtxCkR33bNYu3wyLyNx/Wpv0vU7FZy8Vj53VNw==", + "dev": true + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -2337,6 +2722,12 @@ } } }, + "html-tags": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", + "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", + "dev": true + }, "html-webpack-plugin": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.5.0.tgz", @@ -2393,6 +2784,12 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true + }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -2405,6 +2802,50 @@ "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", "dev": true }, + "import-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", + "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", + "dev": true, + "requires": { + "import-from": "^3.0.0" + } + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, + "import-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", + "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, "import-local": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", @@ -2497,12 +2938,17 @@ "is-decimal": "^2.0.0" } }, + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, - "optional": true, "requires": { "binary-extensions": "^2.0.0" } @@ -2697,6 +3143,12 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -2720,12 +3172,40 @@ } } }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, + "klona": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz", + "integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==", + "dev": true + }, + "lilconfig": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz", + "integrity": "sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg==", + "dev": true + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, "lit-element": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-2.5.1.tgz", @@ -2774,6 +3254,12 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "lodash.topath": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", + "integrity": "sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak=", + "dev": true + }, "lodash.uniqby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", @@ -2876,6 +3362,12 @@ "readable-stream": "^2.0.1" } }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, "micromark": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.0.1.tgz", @@ -3141,6 +3633,12 @@ "brace-expansion": "^1.1.7" } }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, "mississippi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", @@ -3197,6 +3695,12 @@ } } }, + "modern-normalize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/modern-normalize/-/modern-normalize-1.1.0.tgz", + "integrity": "sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA==", + "dev": true + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -3224,6 +3728,12 @@ "dev": true, "optional": true }, + "nanoid": { + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "dev": true + }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -3286,6 +3796,15 @@ "tslib": "^1.10.0" } }, + "node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dev": true, + "requires": { + "lodash": "^4.17.21" + } + }, "node-libs-browser": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", @@ -3325,12 +3844,23 @@ } } }, + "node-releases": { + "version": "1.1.75", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.75.tgz", + "integrity": "sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==", + "dev": true + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "optional": true + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true }, "nth-check": { "version": "1.0.2", @@ -3384,6 +3914,12 @@ } } }, + "object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "dev": true + }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -3484,6 +4020,15 @@ "tslib": "^1.10.0" } }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, "parse-asn1": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", @@ -3511,6 +4056,18 @@ "is-hexadecimal": "^2.0.0" } }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, "parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", @@ -3570,6 +4127,12 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, "pbkdf2": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", @@ -3610,6 +4173,174 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, + "postcss": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.6.tgz", + "integrity": "sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==", + "dev": true, + "requires": { + "colorette": "^1.2.2", + "nanoid": "^3.1.23", + "source-map-js": "^0.6.2" + } + }, + "postcss-js": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-3.0.3.tgz", + "integrity": "sha512-gWnoWQXKFw65Hk/mi2+WTQTHdPD5UJdDXZmX073EY/B3BWnYjO4F4t0VneTCnCGQ5E5GsCdMkzPaTXwl3r5dJw==", + "dev": true, + "requires": { + "camelcase-css": "^2.0.1", + "postcss": "^8.1.6" + } + }, + "postcss-load-config": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.0.tgz", + "integrity": "sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==", + "dev": true, + "requires": { + "import-cwd": "^3.0.0", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + } + }, + "postcss-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-4.3.0.tgz", + "integrity": "sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==", + "dev": true, + "requires": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.4" + }, + "dependencies": { + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true + }, + "postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-nested": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.5.tgz", + "integrity": "sha512-GSRXYz5bccobpTzLQZXOnSOfKl6TwVr5CyAQJUPub4nuRJSOECK5AqurxVgmtxP48p0Kc/ndY/YyS1yqldX0Ew==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-selector-parser": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", + "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true + }, "pretty-error": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz", @@ -3620,6 +4351,12 @@ "renderkid": "^2.0.4" } }, + "pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "dev": true + }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -3716,6 +4453,26 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "purgecss": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-4.0.3.tgz", + "integrity": "sha512-PYOIn5ibRIP34PBU9zohUcCI09c7drPJJtTDAc0Q6QlRz2/CHQ8ywGLdE7ZhxU2VTqB7p5wkvj5Qcm05Rz3Jmw==", + "dev": true, + "requires": { + "commander": "^6.0.0", + "glob": "^7.0.0", + "postcss": "^8.2.1", + "postcss-selector-parser": "^6.0.2" + }, + "dependencies": { + "commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true + } + } + }, "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", @@ -3728,6 +4485,18 @@ "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", "dev": true }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -3799,6 +4568,24 @@ "picomatch": "^2.2.1" } }, + "reduce-css-calc": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", + "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", + "dev": true, + "requires": { + "css-unit-converter": "^1.1.1", + "postcss-value-parser": "^3.3.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, "regenerator-runtime": { "version": "0.13.7", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", @@ -3963,6 +4750,12 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -3982,6 +4775,15 @@ "inherits": "^2.0.1" } }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, "run-queue": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", @@ -4107,6 +4909,15 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + } + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -4238,6 +5049,12 @@ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, + "source-map-js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", + "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "dev": true + }, "source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", @@ -4395,6 +5212,219 @@ "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=", "dev": true }, + "style-loader": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz", + "integrity": "sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + } + } + }, + "tailwindcss": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.2.7.tgz", + "integrity": "sha512-jv35rugP5j8PpzbXnsria7ZAry7Evh0KtQ4MZqNd+PhF+oIKPwJTVwe/rmfRx9cZw3W7iPZyzBmeoAoNwfJ1yg==", + "dev": true, + "requires": { + "arg": "^5.0.0", + "bytes": "^3.0.0", + "chalk": "^4.1.1", + "chokidar": "^3.5.2", + "color": "^3.2.0", + "cosmiconfig": "^7.0.0", + "detective": "^5.2.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.7", + "fs-extra": "^10.0.0", + "glob-parent": "^6.0.0", + "html-tags": "^3.1.0", + "is-glob": "^4.0.1", + "lodash": "^4.17.21", + "lodash.topath": "^4.5.2", + "modern-normalize": "^1.1.0", + "node-emoji": "^1.8.1", + "normalize-path": "^3.0.0", + "object-hash": "^2.2.0", + "postcss-js": "^3.0.3", + "postcss-load-config": "^3.1.0", + "postcss-nested": "5.0.5", + "postcss-selector-parser": "^6.0.6", + "postcss-value-parser": "^4.1.0", + "pretty-hrtime": "^1.0.3", + "purgecss": "^4.0.3", + "quick-lru": "^5.1.1", + "reduce-css-calc": "^2.1.8", + "resolve": "^1.20.0", + "tmp": "^0.2.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "glob-parent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.1.tgz", + "integrity": "sha512-kEVjS71mQazDBHKcsq4E9u/vUzaLcw1A8EtUeydawvIWQCJM0qQ08G1H7/XTjFUulla6XQiDOG6MXSaG0HDKog==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + } + } + }, "tapable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", @@ -4464,6 +5494,26 @@ "setimmediate": "^1.0.4" } }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + }, + "dependencies": { + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, "to-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", @@ -4666,6 +5716,12 @@ "@types/unist": "^2.0.0" } }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", @@ -5415,6 +6471,12 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + }, "yargs-parser": { "version": "13.1.2", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", diff --git a/package.json b/package.json index 32c63262..c63a5dfb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vscode-front-matter", "displayName": "Front Matter", - "description": "Simplifies working with front matter of your articles. Useful extension when you are using a static site generator like: Hugo, Jekyll, Hexo, NextJs, Gatsby, and many more...", + "description": "An essential Visual Studio Code extension when you want to manage the markdown pages of your static site like: Hugo, Jekyll, Hexo, NextJs, Gatsby, and many more...", "icon": "assets/frontmatter-128x128.png", "version": "2.5.1", "preview": false, @@ -59,6 +59,7 @@ "onCommand:frontMatter.init", "onCommand:frontMatter.collapseSections", "onCommand:frontMatter.preview", + "onCommand:frontMatter.dashboard", "onView:frontMatter.explorer" ], "main": "./dist/extension", @@ -101,11 +102,21 @@ "default": [], "markdownDescription": "This array of folders defines where the extension can easily create new content by running the create article command." }, + "frontMatter.content.publicFolder": { + "type": "string", + "default": "", + "markdownDescription": "Specify the folder name where all your assets are located. For instance in Hugo this is the `static` folder." + }, "frontMatter.custom.scripts": { "type": "array", "default": [], "markdownDescription": "Specify the path to a Node.js script to execute. The current file path will be provided as an argument." }, + "frontMatter.dashboard.openOnStart": { + "type": "boolean", + "default": null, + "description": "Specify if you want to open the dashboard when you start VS Code." + }, "frontMatter.panel.freeform": { "type": "boolean", "default": true, @@ -299,6 +310,11 @@ "command": "frontMatter.preview", "title": "Preview article", "category": "Front matter" + }, + { + "command": "frontMatter.dashboard", + "title": "Open pages dashboard", + "category": "Front matter" } ], "menus": { @@ -373,15 +389,22 @@ "@types/react-dom": "17.0.0", "@types/vscode": "1.51.0", "@vscode/codicons": "0.0.20", - "date-fns": "2.0.1", + "autoprefixer": "^10.3.2", + "css-loader": "5.2.7", + "date-fns": "2.23.0", "downshift": "6.0.6", + "fuse.js": "6.4.6", "glob": "7.1.6", "gray-matter": "4.0.2", "html-loader": "1.3.2", "html-webpack-plugin": "4.5.0", "mdast-util-from-markdown": "1.0.0", + "postcss": "^8.3.6", + "postcss-loader": "4.3.0", "react": "17.0.1", "react-dom": "17.0.1", + "style-loader": "2.0.0", + "tailwindcss": "^2.2.7", "ts-loader": "8.0.3", "tslint": "6.1.3", "typescript": "4.0.2", @@ -390,6 +413,8 @@ "webpack-cli": "3.3.12" }, "dependencies": { + "@headlessui/react": "1.4.0", + "@heroicons/react": "1.0.4", "lodash.uniqby": "4.7.0" } } diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 00000000..f8e23b97 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,8 @@ +const tailwindcss = require('tailwindcss'); + +module.exports = { + plugins: [ + tailwindcss('./tailwind.config.js'), + require('autoprefixer'), + ], +}; \ No newline at end of file diff --git a/src/commands/Dashboard.ts b/src/commands/Dashboard.ts new file mode 100644 index 00000000..cb3fdb9c --- /dev/null +++ b/src/commands/Dashboard.ts @@ -0,0 +1,235 @@ +import { SETTINGS_CONTENT_STATIC_FOLDERS, SETTING_DATE_FIELD, SETTING_SEO_DESCRIPTION_FIELD, SETTINGS_DASHBOARD_OPENONSTART } from './../constants/settings'; +import { ArticleHelper } from './../helpers/ArticleHelper'; +import { join } from "path"; +import { commands, Uri, ViewColumn, Webview, WebviewPanel, window, workspace } from "vscode"; +import { SettingsHelper } from '../helpers'; +import { TaxonomyType } from '../models'; +import { Folders } from './Folders'; +import { getNonce } from '../helpers/getNonce'; +import { DashboardCommand } from '../pagesView/DashboardCommand'; +import { DashboardMessage } from '../pagesView/DashboardMessage'; +import { Page } from '../pagesView/models/Page'; +import { openFileInEditor } from '../helpers/openFileInEditor'; +import { COMMAND_NAME } from '../constants/Extension'; +import { Template } from './Template'; +import { Notifications } from '../helpers/Notifications'; +import { Settings } from '../pagesView/models/Settings'; + + +export class Dashboard { + private static webview: WebviewPanel | null = null; + private static isDisposed: boolean = true; + + /**  + * Init the dashboard + */ + public static async init(extensionPath: string) { + const config = SettingsHelper.getConfig(); + const openOnStartup = config.get(SETTINGS_DASHBOARD_OPENONSTART); + if (openOnStartup) { + Dashboard.open(extensionPath); + } + } + + /** + * Open or reveal the dashboard + */ + public static async open(extensionPath: string) { + if (Dashboard.isOpen) { + Dashboard.reveal(); + } else { + Dashboard.create(extensionPath); + } + } + + /** + * Check if the dashboard is still open + */ + public static get isOpen(): boolean { + return !Dashboard.isDisposed; + } + + /** + * Reveal the dashboard if it is open + */ + public static reveal() { + if (Dashboard.webview) { + Dashboard.webview.reveal(); + } + } + + /** + * Create the dashboard webview + */ + public static async create(extensionPath: string) { + + // Create the preview webview + Dashboard.webview = window.createWebviewPanel( + 'frontMatterDashboard', + 'FrontMatter Dashboard', + ViewColumn.One, + { + enableScripts: true + } + ); + + Dashboard.isDisposed = false; + + Dashboard.webview.iconPath = { + dark: Uri.file(join(extensionPath, 'assets/frontmatter-dark.svg')), + light: Uri.file(join(extensionPath, 'assets/frontmatter.svg')) + }; + + Dashboard.webview.webview.html = Dashboard.getWebviewContent(Dashboard.webview.webview, Uri.parse(extensionPath)); + + Dashboard.webview.onDidChangeViewState(() => { + if (this.webview?.visible) { + console.log(`Dashboard opened`); + } + }); + + Dashboard.webview.onDidDispose(() => { + Dashboard.isDisposed = true; + }); + + Dashboard.webview.webview.onDidReceiveMessage(async (msg) => { + switch(msg.command) { + case DashboardMessage.getData: + Dashboard.getSettings(); + Dashboard.getPages(); + break; + case DashboardMessage.openFile: + openFileInEditor(msg.data); + break; + case DashboardMessage.createContent: + await commands.executeCommand(COMMAND_NAME.createContent); + break; + case DashboardMessage.updateSetting: + Dashboard.updateSetting(msg.data); + break; + } + }); + } + + /** + * Retrieve the settings for the dashboard + */ + private static async getSettings() { + Dashboard.postWebviewMessage({ + command: DashboardCommand.settings, + data: { + folders: Folders.get(), + initialized: await Template.isInitialized(), + tags: SettingsHelper.getTaxonomy(TaxonomyType.Tag), + categories: SettingsHelper.getTaxonomy(TaxonomyType.Category), + openOnStart: SettingsHelper.getConfig().get(SETTINGS_DASHBOARD_OPENONSTART) + } as Settings + }); + } + + /** + * Update a setting from the dashboard + */ + private static async updateSetting(data: { name: string, value: any }) { + await SettingsHelper.updateSetting(data.name, data.value); + Dashboard.getSettings(); + } + + /** + * Retrieve all the markdown pages + */ + private static async getPages() { + const config = SettingsHelper.getConfig(); + const wsFolders = workspace.workspaceFolders; + const crntWsFolder = wsFolders && wsFolders.length > 0 ? wsFolders[0] : null; + + const descriptionField = config.get(SETTING_SEO_DESCRIPTION_FIELD) as string || "description"; + const dateField = config.get(SETTING_DATE_FIELD) as string || "date"; + const staticFolder = config.get(SETTINGS_CONTENT_STATIC_FOLDERS); + + const folderInfo = await Folders.getInfo(); + const pages: Page[] = []; + + if (folderInfo) { + for (const folder of folderInfo) { + for (const file of folder.lastModified) { + if (file.fileName.endsWith(`.md`) || file.fileName.endsWith(`.mdx`)) { + try { + const article = ArticleHelper.getFrontMatterByPath(file.filePath); + + if (article?.data.title) { + const page: Page = { + ...article.data, + // FrontMatter properties + fmGroup: folder.title, + fmModified: file.mtime, + fmFilePath: file.filePath, + fmFileName: file.fileName, + // Make sure these are always set + title: article?.data.title, + slug: article?.data.slug, + date: article?.data[dateField] || "", + draft: article?.data.draft, + description: article?.data[descriptionField] || "", + }; + + if (article?.data.preview && crntWsFolder) { + const previewPath = join(crntWsFolder.uri.fsPath, staticFolder || "", article?.data.preview); + const previewUri = Uri.file(previewPath); + const preview = Dashboard.webview?.webview.asWebviewUri(previewUri); + page.preview = preview?.toString() || ""; + } + + pages.push(page); + } + } catch (error) { + Notifications.error(`File error: ${file.filePath} - ${error?.message || error}`); + } + } + } + } + } + + Dashboard.postWebviewMessage({ + command: DashboardCommand.pages, + data: pages + }); + } + + /** + * Post data to the dashboard + * @param msg + */ + private static postWebviewMessage(msg: { command: DashboardCommand, data?: any }) { + Dashboard.webview?.webview.postMessage(msg); + } + + /** + * Retrieve the webview HTML contents + * @param webView + */ + private static getWebviewContent(webView: Webview, extensionPath: Uri): string { + const scriptUri = webView.asWebviewUri(Uri.joinPath(extensionPath, 'dist', 'pages.js')); + + const nonce = getNonce(); + + return ` + + + + + + + Front Matter Dashboard + + +
    + + Daily usage + + + + + `; + } +} \ No newline at end of file diff --git a/src/commands/Folders.ts b/src/commands/Folders.ts index a038e018..83c6b3e1 100644 --- a/src/commands/Folders.ts +++ b/src/commands/Folders.ts @@ -111,51 +111,84 @@ export class Folders { */ public static getFolderPath(folder: Uri) { let folderPath = ""; + const wsFolder = Folders.getWorkspaceFolder(); if (folder && folder.fsPath) { folderPath = folder.fsPath; - } else if (workspace.workspaceFolders && workspace.workspaceFolders.length > 0) { - folderPath = workspace.workspaceFolders[0].uri.fsPath; + } else if (wsFolder) { + folderPath = wsFolder.fsPath; } return folderPath; } + /** + * Retrieve the workspace folder + */ + public static getWorkspaceFolder(): Uri | undefined { + const folders = workspace.workspaceFolders; + if (folders && folders.length > 0) { + return folders[0].uri; + } + return undefined; + } + + /** + * Get the name of the project + */ + public static getProjectFolderName(): string { + const wsFolder = Folders.getWorkspaceFolder(); + if (wsFolder) { + // const projectFolder = wsFolder?.fsPath.split('\\').join('/').split('/').pop(); + return basename(wsFolder.fsPath); + } + return ""; + } + /** * Get the registered folders information */ - public static async getInfo(): Promise { + public static async getInfo(limit?: number): Promise { const folders = Folders.get(); if (folders && folders.length > 0) { let folderInfo: FolderInfo[] = []; for (const folder of folders) { try { - const folderPath = Uri.file(folder.fsPath); - const files = await workspace.fs.readDirectory(folderPath); - if (files) { - let fileStats: FileInfo[] = []; + const projectName = Folders.getProjectFolderName(); + let projectStart = folder.fsPath.split(projectName).pop(); + if (projectStart) { + projectStart = projectStart.startsWith('/') ? projectStart.substr(1) : projectStart; + const mdFiles = await workspace.findFiles(join(projectStart, '**/*.md')); + const mdxFiles = await workspace.findFiles(join(projectStart, '**/*.mdx')); + let files = [...mdFiles, ...mdxFiles]; + if (files) { + let fileStats: FileInfo[] = []; - for (const file of files) { - try { - const fileName = file[0]; - const filePath = Uri.file(join(folderPath.fsPath, fileName)); - const stats = await workspace.fs.stat(filePath); - fileStats.push({ - filePath: filePath.fsPath, - fileName, - ...stats - }); - } catch (error) { - // Skip the file + for (const file of files) { + try { + const fileName = basename(file.fsPath); + const stats = await workspace.fs.stat(file); + fileStats.push({ + filePath: file.fsPath, + fileName, + ...stats + }); + } catch (error) { + // Skip the file + } } + + fileStats = fileStats.sort((a, b) => b.mtime - a.mtime); + + if (limit) { + fileStats = fileStats.slice(0, limit); + } + + folderInfo.push({ + title: folder.title, + files: files.length, + lastModified: fileStats + }); } - - fileStats = fileStats.sort((a, b) => b.mtime - a.mtime).slice(0, 10); - - folderInfo.push({ - title: folder.title, - files: files.length, - lastModified: fileStats - }); } } catch (e) { // Skip the current folder @@ -172,7 +205,7 @@ export class Folders { * Get the folder settings * @returns */ - private static get() { + public static get() { const config = SettingsHelper.getConfig(); const folders: ContentFolder[] = config.get(SETTINGS_CONTENT_FOLDERS) as ContentFolder[]; return folders; diff --git a/src/constants/Extension.ts b/src/constants/Extension.ts index d54b3868..1b2acc8d 100644 --- a/src/constants/Extension.ts +++ b/src/constants/Extension.ts @@ -23,4 +23,5 @@ export const COMMAND_NAME = { createTemplate: getCommandName("createTemplate"), collapseSections: getCommandName("collapseSections"), preview: getCommandName("preview"), + dashboard: getCommandName("dashboard"), }; \ No newline at end of file diff --git a/src/constants/context.ts b/src/constants/context.ts index 2b09feb7..dd3eeefe 100644 --- a/src/constants/context.ts +++ b/src/constants/context.ts @@ -1,7 +1,6 @@ - - export const CONTEXT = { canInit: "frontMatterCanInit", canOpenPreview: "frontMatterCanOpenPreview", + canOpenDashboard: "frontMatterCanOpenDashboard", registeredFolders: 'frontMatter.registeredFolders' }; \ No newline at end of file diff --git a/src/constants/settings.ts b/src/constants/settings.ts index d5c58f3d..2b5a0164 100644 --- a/src/constants/settings.ts +++ b/src/constants/settings.ts @@ -34,4 +34,7 @@ export const SETTING_CUSTOM_SCRIPTS = "custom.scripts"; export const SETTING_AUTO_UPDATE_DATE = "content.autoUpdateDate"; export const SETTINGS_CONTENT_FOLDERS = "content.folders"; -export const SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT = "content.fmHighlight"; \ No newline at end of file +export const SETTINGS_CONTENT_STATIC_FOLDERS = "content.publicFolder"; +export const SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT = "content.fmHighlight"; + +export const SETTINGS_DASHBOARD_OPENONSTART = "dashboard.openOnStart"; \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index 13d70417..b610d21c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,3 +1,4 @@ +import { Dashboard } from './commands/Dashboard'; import * as vscode from 'vscode'; import { Article, Settings, StatusListener } from './commands'; import { Folders } from './commands/Folders'; @@ -20,6 +21,13 @@ const mdSelector: vscode.DocumentSelector = { language: 'markdown', scheme: 'fil export async function activate({ subscriptions, extensionUri, extensionPath }: vscode.ExtensionContext) { collection = vscode.languages.createDiagnosticCollection('frontMatter'); + // Pages dashboard + Dashboard.init(extensionPath); + subscriptions.push(vscode.commands.registerCommand(COMMAND_NAME.dashboard, () => { + Dashboard.open(extensionPath); + })); + + // Register the explorer view const explorerSidebar = ExplorerView.getInstance(extensionUri); let explorerView = vscode.window.registerWebviewViewProvider(ExplorerView.viewType, explorerSidebar, { webviewOptions: { @@ -50,25 +58,15 @@ export async function activate({ subscriptions, extensionUri, extensionPath }: v Settings.create(TaxonomyType.Category); }); - let exportTaxonomy = vscode.commands.registerCommand(COMMAND_NAME.exportTaxonomy, () => { - Settings.export(); - }); + let exportTaxonomy = vscode.commands.registerCommand(COMMAND_NAME.exportTaxonomy, Settings.export); - let remap = vscode.commands.registerCommand(COMMAND_NAME.remap, () => { - Settings.remap(); - }); + let remap = vscode.commands.registerCommand(COMMAND_NAME.remap, Settings.remap); - let setDate = vscode.commands.registerCommand(COMMAND_NAME.setDate, () => { - Article.setDate(); - }); + let setDate = vscode.commands.registerCommand(COMMAND_NAME.setDate, Article.setDate); - let setLastModifiedDate = vscode.commands.registerCommand(COMMAND_NAME.setLastModifiedDate, () => { - Article.setLastModifiedDate(); - }); + let setLastModifiedDate = vscode.commands.registerCommand(COMMAND_NAME.setLastModifiedDate, Article.setLastModifiedDate); - let generateSlug = vscode.commands.registerCommand(COMMAND_NAME.generateSlug, () => { - Article.generateSlug(); - }); + let generateSlug = vscode.commands.registerCommand(COMMAND_NAME.generateSlug, Article.generateSlug); let createFromTemplate = vscode.commands.registerCommand(COMMAND_NAME.createFromTemplate, (folder: vscode.Uri) => { const folderPath = Folders.getFolderPath(folder); diff --git a/src/viewpanel/helper/MessageHelper.ts b/src/helpers/MessageHelper.ts similarity index 70% rename from src/viewpanel/helper/MessageHelper.ts rename to src/helpers/MessageHelper.ts index 78312f84..d95e8936 100644 --- a/src/viewpanel/helper/MessageHelper.ts +++ b/src/helpers/MessageHelper.ts @@ -1,4 +1,6 @@ -import { CommandToCode } from "../CommandToCode"; +import { DashboardMessage } from './../pagesView/DashboardMessage'; +import { CommandToCode } from "../viewpanel/CommandToCode"; + interface ClientVsCode { getState: () => T; @@ -16,7 +18,7 @@ export class MessageHelper { return MessageHelper.vscode; } - public static sendMessage = (command: CommandToCode, data?: any) => { + public static sendMessage = (command: CommandToCode | DashboardMessage, data?: any) => { if (data) { MessageHelper.vscode.postMessage({ command, data }); } else { diff --git a/src/helpers/SettingsHelper.ts b/src/helpers/SettingsHelper.ts index 975403ff..20dee8fb 100644 --- a/src/helpers/SettingsHelper.ts +++ b/src/helpers/SettingsHelper.ts @@ -8,6 +8,11 @@ export class SettingsHelper { return vscode.workspace.getConfiguration(CONFIG_KEY); } + public static async updateSetting(name: string, value: any) { + const config = vscode.workspace.getConfiguration(CONFIG_KEY); + await config.update(name, value); + } + /** * Return the taxonomy settings * diff --git a/src/helpers/getNonce.ts b/src/helpers/getNonce.ts new file mode 100644 index 00000000..5fceb93c --- /dev/null +++ b/src/helpers/getNonce.ts @@ -0,0 +1,10 @@ + + +export const getNonce = () => { + let text = ''; + const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + for (let i = 0; i < 32; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +}; \ No newline at end of file diff --git a/src/helpers/openFileInEditor.ts b/src/helpers/openFileInEditor.ts new file mode 100644 index 00000000..c0cc79e8 --- /dev/null +++ b/src/helpers/openFileInEditor.ts @@ -0,0 +1,13 @@ +import { Uri, workspace, window } from "vscode"; +import { Notifications } from "./Notifications"; + +export const openFileInEditor = async (filePath: string) => { + if (filePath) { + try { + const doc = await workspace.openTextDocument(Uri.file(filePath)); + await window.showTextDocument(doc, 1, false); + } catch (e) { + Notifications.error(`Couldn't open the file.`); + } + } +}; \ No newline at end of file diff --git a/src/hooks/useDarkMode.tsx b/src/hooks/useDarkMode.tsx new file mode 100644 index 00000000..5055a4f8 --- /dev/null +++ b/src/hooks/useDarkMode.tsx @@ -0,0 +1,28 @@ +import { useState, useEffect } from 'react'; + +export default function useDarkMode() { + + const setTheme = (elm: HTMLElement) => { + if (elm) { + const darkMode = elm.classList.contains('vscode-dark'); + document.documentElement.classList.remove(`${darkMode ? "light" : "dark"}`); + document.documentElement.classList.add(`${darkMode ? "dark" : "light"}`); + } + }; + + useEffect(() => { + const mutationObserver = new MutationObserver((mutationsList, observer) => { + const last = mutationsList.filter(item => item.type === "attributes" || item.attributeName === 'class').pop(); + setTheme(last?.target as HTMLElement); + }); + + setTheme(document.body); + + mutationObserver.observe(document.body, { childList: false, attributes: true }) + + return () => { + mutationObserver.disconnect(); + }; + }, ['']); + +} \ No newline at end of file diff --git a/src/viewpanel/hooks/useDebounce.tsx b/src/hooks/useDebounce.tsx similarity index 84% rename from src/viewpanel/hooks/useDebounce.tsx rename to src/hooks/useDebounce.tsx index cca5bd92..398ec0cd 100644 --- a/src/viewpanel/hooks/useDebounce.tsx +++ b/src/hooks/useDebounce.tsx @@ -1,8 +1,8 @@ import { useEffect, useState } from "react"; -export function useDebounce(value: string, delay: number) { +export function useDebounce(value: T, delay: number) { // State and setters for debounced value - const [debouncedValue, setDebouncedValue] = useState(value); + const [debouncedValue, setDebouncedValue] = useState(value); useEffect( () => { diff --git a/src/pagesView/DashboardCommand.ts b/src/pagesView/DashboardCommand.ts new file mode 100644 index 00000000..987fc7d8 --- /dev/null +++ b/src/pagesView/DashboardCommand.ts @@ -0,0 +1,5 @@ +export enum DashboardCommand { + loading = "loading", + pages = "pages", + settings = "settings" +} \ No newline at end of file diff --git a/src/pagesView/DashboardMessage.ts b/src/pagesView/DashboardMessage.ts new file mode 100644 index 00000000..0382347b --- /dev/null +++ b/src/pagesView/DashboardMessage.ts @@ -0,0 +1,7 @@ +export enum DashboardMessage { + getData = 'getData', + openFile = 'openFile', + getTheme = 'getTheme', + createContent = 'createContent', + updateSetting = 'updateSetting', +} \ No newline at end of file diff --git a/src/pagesView/components/Button.tsx b/src/pagesView/components/Button.tsx new file mode 100644 index 00000000..8c7602f6 --- /dev/null +++ b/src/pagesView/components/Button.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; + +export interface IButtonProps { + disabled?: boolean; + onClick: () => void; +} + +export const Button: React.FunctionComponent = ({onClick, disabled, children}: React.PropsWithChildren) => { + return ( + + ); +}; \ No newline at end of file diff --git a/src/pagesView/components/Dashboard.tsx b/src/pagesView/components/Dashboard.tsx new file mode 100644 index 00000000..77575184 --- /dev/null +++ b/src/pagesView/components/Dashboard.tsx @@ -0,0 +1,53 @@ +import * as React from 'react'; +import { Spinner } from './Spinner'; +import useMessages from '../hooks/useMessages'; +import { Overview } from './Overview'; +import { Header } from './Header'; +import { Tab } from '../constants/Tab'; +import { SortOption } from '../constants/SortOption'; +import useDarkMode from '../../hooks/useDarkMode'; +import usePages from '../hooks/usePages'; + +export interface IDashboardProps {} + +export const Dashboard: React.FunctionComponent = ({}: React.PropsWithChildren) => { + const { loading, pages, settings } = useMessages(); + const [ tab, setTab ] = React.useState(Tab.All); + const [ sorting, setSorting ] = React.useState(SortOption.LastModified); + const [ group, setGroup ] = React.useState(null); + const [ search, setSearch ] = React.useState(null); + const [ tag, setTag ] = React.useState(null); + const [ category, setCategory ] = React.useState(null); + const { pageItems } = usePages(pages, tab, sorting, group, search, tag, category); + useDarkMode(); + + const pageGroups = [...new Set(pages.map(page => page.fmGroup))]; + + return ( +
    +
    +
    setTab(tabId)} + switchSorting={(sortId: SortOption) => setSorting(sortId)} + switchGroup={(groupId: string | null) => setGroup(groupId)} + switchTag={(tagId: string | null) => setTag(tagId)} + switchCategory={(categoryId: string | null) => setCategory(categoryId)} + onSearch={(value: string | null) => setSearch(value)} + settings={settings} + /> + +
    + { loading ? : } +
    + +

    If you find FrontMatter useful, please consider sponsoring it, or if you use FrontMatter for work, please encourage your employer to sponsoring it.

    +
    +
    + ); +}; \ No newline at end of file diff --git a/src/pagesView/components/DateField.tsx b/src/pagesView/components/DateField.tsx new file mode 100644 index 00000000..f783209d --- /dev/null +++ b/src/pagesView/components/DateField.tsx @@ -0,0 +1,28 @@ +import { format, parseJSON } from 'date-fns'; +import * as React from 'react'; + +export interface IDateFieldProps { + value: Date | string; +} + +export const DateField: React.FunctionComponent = ({value}: React.PropsWithChildren) => { + const [ dateValue, setDateValue ] = React.useState(""); + + React.useEffect(() => { + try { + const parsedValue = typeof value === 'string' ? parseJSON(value) : value; + const dateString = format(parsedValue, 'yyyy-MM-dd'); + setDateValue(dateString); + } catch (e) { + // Date is invalid + } + }, [value]); + + if (!dateValue) { + return null; + } + + return ( + {dateValue} + ); +}; \ No newline at end of file diff --git a/src/pagesView/components/Filter.tsx b/src/pagesView/components/Filter.tsx new file mode 100644 index 00000000..d7d55aad --- /dev/null +++ b/src/pagesView/components/Filter.tsx @@ -0,0 +1,48 @@ +import { Menu } from '@headlessui/react'; +import * as React from 'react'; +import { MenuButton } from './MenuButton'; +import { MenuItem } from './MenuItem'; +import { MenuItems } from './MenuItems'; + +export interface IFilterProps { + label: string; + items: string[]; + activeItem: string | null; + onClick: (item: string | null) => void; +} + +const DEFAULT_VALUE = "No filter"; + +export const Filter: React.FunctionComponent = ({label, activeItem, items, onClick}: React.PropsWithChildren) => { + + console.log(items); + + if (!items || items.length === 0) { + return null; + } + + return ( +
    + + + + + onClick(null)} /> + + {items.map((option) => ( + onClick(option)} /> + ))} + + +
    + ); +}; \ No newline at end of file diff --git a/src/pagesView/components/Grouping.tsx b/src/pagesView/components/Grouping.tsx new file mode 100644 index 00000000..6a51c5c9 --- /dev/null +++ b/src/pagesView/components/Grouping.tsx @@ -0,0 +1,46 @@ +import { Menu, Transition } from '@headlessui/react'; +import { ChevronDownIcon } from '@heroicons/react/solid'; +import * as React from 'react'; +import { Fragment } from 'react'; +import { MenuButton } from './MenuButton'; +import { MenuItem } from './MenuItem'; +import { MenuItems } from './MenuItems'; + +export interface IGroupingProps { + groups: string[]; + crntGroup: string | null; + switchGroup: (group: string | null) => void; +} + +const DEFAULT_TYPE = "All types"; + +export const Grouping: React.FunctionComponent = ({groups, crntGroup, switchGroup}: React.PropsWithChildren) => { + if (groups.length <= 1) { + return null; + } + + return ( +
    + + + + + + + {groups.map((option) => ( + + ))} + + +
    + ); +}; \ No newline at end of file diff --git a/src/pagesView/components/Header.tsx b/src/pagesView/components/Header.tsx new file mode 100644 index 00000000..3e48a656 --- /dev/null +++ b/src/pagesView/components/Header.tsx @@ -0,0 +1,75 @@ +import * as React from 'react'; +import { Tab } from '../constants/Tab'; +import { SortOption } from '../constants/SortOption'; +import { Navigation } from './Navigation'; +import { Sorting } from './Sorting'; +import { Grouping } from './Grouping'; +import { MessageHelper } from '../../helpers/MessageHelper'; +import { DashboardMessage } from '../DashboardMessage'; +import { Searchbox } from './Searchbox'; +import { Settings } from '../models/Settings'; +import { Startup } from './Startup'; +import { Button } from './Button'; +import { Filter } from './Filter'; + +export interface IHeaderProps { + settings: Settings; + + // Navigation + currentTab: Tab; + totalPages: number; + switchTab: (tabId: Tab) => void; + + // Sorting + currentSorting: SortOption; + switchSorting: (sortId: SortOption) => void; + + // Grouping + groups: string[]; + crntGroup: string | null; + switchGroup: (group: string | null) => void; + + // Searching + onSearch: (value: string | null) => void; + + // Tags + crntTag: string | null; + switchTag: (tag: string | null) => void; + + // Categories + crntCategory: string | null; + switchCategory: (category: string | null) => void; +} + +export const Header: React.FunctionComponent = ({currentTab, currentSorting, switchSorting, switchTab, totalPages, crntGroup, groups, switchGroup, onSearch, settings, switchTag, crntTag, switchCategory, crntCategory}: React.PropsWithChildren) => { + + const createContent = () => { + MessageHelper.sendMessage(DashboardMessage.createContent); + }; + + return ( +
    +
    + + +
    + + + +
    +
    + +
    + + + + + + + + + +
    +
    + ); +}; \ No newline at end of file diff --git a/src/pagesView/components/Item.tsx b/src/pagesView/components/Item.tsx new file mode 100644 index 00000000..3aa2cd7c --- /dev/null +++ b/src/pagesView/components/Item.tsx @@ -0,0 +1,47 @@ +import * as React from 'react'; +import { MessageHelper } from '../../helpers/MessageHelper'; +import { MarkdownIcon } from '../../viewpanel/components/Icons/MarkdownIcon'; +import { DashboardMessage } from '../DashboardMessage'; +import { Page } from '../models/Page'; +import { DateField } from './DateField'; +import { Status } from './Status'; + +export interface IItemProps extends Page {} + +export const Item: React.FunctionComponent = ({ fmFilePath, date, title, draft, description, preview }: React.PropsWithChildren) => { + + const openFile = () => { + MessageHelper.sendMessage(DashboardMessage.openFile, fmFilePath); + }; + + return ( +
  8. + +
  9. + ); +}; diff --git a/src/pagesView/components/List.tsx b/src/pagesView/components/List.tsx new file mode 100644 index 00000000..abf0746b --- /dev/null +++ b/src/pagesView/components/List.tsx @@ -0,0 +1,11 @@ +import * as React from 'react'; + +export interface IListProps {} + +export const List: React.FunctionComponent = ({children}: React.PropsWithChildren) => { + return ( +
      + {children} +
    + ); +}; \ No newline at end of file diff --git a/src/pagesView/components/MenuButton.tsx b/src/pagesView/components/MenuButton.tsx new file mode 100644 index 00000000..69db5da7 --- /dev/null +++ b/src/pagesView/components/MenuButton.tsx @@ -0,0 +1,23 @@ +import { Menu } from '@headlessui/react'; +import { ChevronDownIcon } from '@heroicons/react/solid'; +import * as React from 'react'; + +export interface IMenuButtonProps { + label: string; + title: string; +} + +export const MenuButton: React.FunctionComponent = ({label, title}: React.PropsWithChildren) => { + return ( +
    + {label}: + + {title} + +
    + ); +}; \ No newline at end of file diff --git a/src/pagesView/components/MenuItem.tsx b/src/pagesView/components/MenuItem.tsx new file mode 100644 index 00000000..204dd98e --- /dev/null +++ b/src/pagesView/components/MenuItem.tsx @@ -0,0 +1,22 @@ +import { Menu } from '@headlessui/react'; +import * as React from 'react'; + +export interface IMenuItemProps { + title: string; + value: any; + isCurrent: boolean; + onClick: (value: any) => void; +} + +export const MenuItem: React.FunctionComponent = ({title, value, isCurrent, onClick}: React.PropsWithChildren) => { + return ( + + + + ); +}; \ No newline at end of file diff --git a/src/pagesView/components/MenuItems.tsx b/src/pagesView/components/MenuItems.tsx new file mode 100644 index 00000000..cbddc01e --- /dev/null +++ b/src/pagesView/components/MenuItems.tsx @@ -0,0 +1,25 @@ +import { Menu, Transition } from '@headlessui/react'; +import * as React from 'react'; +import { Fragment } from 'react'; + +export interface IMenuItemsProps {} + +export const MenuItems: React.FunctionComponent = ({children}: React.PropsWithChildren) => { + return ( + + +
    + {children} +
    +
    +
    + ); +}; \ No newline at end of file diff --git a/src/pagesView/components/Navigation.tsx b/src/pagesView/components/Navigation.tsx new file mode 100644 index 00000000..923d91d6 --- /dev/null +++ b/src/pagesView/components/Navigation.tsx @@ -0,0 +1,32 @@ +import * as React from 'react'; +import { Tab } from '../constants/Tab'; + +export interface INavigationProps { + currentTab: Tab; + totalPages: number; + switchTab: (tabId: Tab) => void; +} + +export const tabs = [ + { name: 'All articles', id: Tab.All}, + { name: 'Published', id: Tab.Published }, + { name: 'In draft', id: Tab.Draft } +]; + +export const Navigation: React.FunctionComponent = ({currentTab, totalPages, switchTab}: React.PropsWithChildren) => { + + return ( + + ); +}; \ No newline at end of file diff --git a/src/pagesView/components/Overview.tsx b/src/pagesView/components/Overview.tsx new file mode 100644 index 00000000..e9934bf9 --- /dev/null +++ b/src/pagesView/components/Overview.tsx @@ -0,0 +1,40 @@ +import * as React from 'react'; +import { MarkdownIcon } from '../../viewpanel/components/Icons/MarkdownIcon'; +import { Page } from '../models/Page'; +import { Settings } from '../models/Settings'; +import { Item } from './Item'; +import { List } from './List'; + +export interface IOverviewProps { + pages: Page[]; + + settings: Settings; +} + +export const Overview: React.FunctionComponent = ({pages, settings}: React.PropsWithChildren) => { + + if (!pages || !pages.length) { + return ( +
    +
    + + { + settings?.folders?.length > 0 ? ( +

    No Markdown to show

    + ) : ( +

    Make sure you registered a content folder in your project to let Front Matter find the contents.

    + ) + } +
    +
    + ); + } + + return ( + + {pages.map(page => ( + + ))} + + ); +}; \ No newline at end of file diff --git a/src/pagesView/components/Searchbox.tsx b/src/pagesView/components/Searchbox.tsx new file mode 100644 index 00000000..56ff48a6 --- /dev/null +++ b/src/pagesView/components/Searchbox.tsx @@ -0,0 +1,42 @@ +import { FilterIcon, SearchIcon } from '@heroicons/react/solid'; +import * as React from 'react'; +import { useDebounce } from '../../hooks/useDebounce'; + +export interface ISearchboxProps { + onSearch: (searchText: string) => void; +} + +export const Searchbox: React.FunctionComponent = ({onSearch}: React.PropsWithChildren) => { + const [ value, setValue ] = React.useState(''); + const debounceSearch = useDebounce(value, 500); + + const handleChange = (event: React.ChangeEvent) => { + setValue(event.target.value); + }; + + React.useEffect(() => { + onSearch(debounceSearch); + }, [debounceSearch]); + + return ( +
    +
    + +
    +
    +
    + +
    +
    +
    + ); +}; \ No newline at end of file diff --git a/src/pagesView/components/Sorting.tsx b/src/pagesView/components/Sorting.tsx new file mode 100644 index 00000000..15b8a305 --- /dev/null +++ b/src/pagesView/components/Sorting.tsx @@ -0,0 +1,44 @@ +import { Menu, Transition } from '@headlessui/react'; +import * as React from 'react'; +import { SortOption } from '../constants/SortOption'; +import { ChevronDownIcon } from '@heroicons/react/solid'; +import { Fragment } from 'react'; +import { MenuItem } from './MenuItem'; +import { MenuItems } from './MenuItems'; +import { MenuButton } from './MenuButton'; + +export interface ISortingProps { + currentSorting: SortOption; + + switchSorting: (sortId: SortOption) => void; +} + +export const sortOptions = [ + { name: "Last modified", id: SortOption.LastModified }, + { name: "By filename (asc)", id: SortOption.FileNameAsc }, + { name: "By filename (desc)", id: SortOption.FileNameDesc }, +]; + +export const Sorting: React.FunctionComponent = ({currentSorting, switchSorting}: React.PropsWithChildren) => { + + const crntSort = sortOptions.find(x => x.id === currentSorting); + + return ( +
    + + + + + {sortOptions.map((option) => ( + + ))} + + +
    + ); +}; \ No newline at end of file diff --git a/src/pagesView/components/Spinner.tsx b/src/pagesView/components/Spinner.tsx new file mode 100644 index 00000000..de8e7cdf --- /dev/null +++ b/src/pagesView/components/Spinner.tsx @@ -0,0 +1,11 @@ +import * as React from 'react'; + +export interface ISpinnerProps {} + +export const Spinner: React.FunctionComponent = (props: React.PropsWithChildren) => { + return ( +
    +
    +
    + ); +}; \ No newline at end of file diff --git a/src/pagesView/components/Startup.tsx b/src/pagesView/components/Startup.tsx new file mode 100644 index 00000000..f0363ea6 --- /dev/null +++ b/src/pagesView/components/Startup.tsx @@ -0,0 +1,44 @@ +import * as React from 'react'; +import { SETTINGS_DASHBOARD_OPENONSTART } from '../../constants'; +import { MessageHelper } from '../../helpers/MessageHelper'; +import { DashboardMessage } from '../DashboardMessage'; +import { Settings } from '../models/Settings'; + +export interface IStartupProps { + settings: Settings; +} + +export const Startup: React.FunctionComponent = ({settings}: React.PropsWithChildren) => { + const [isChecked, setIsChecked] = React.useState(false); + + const onChange = (e: React.ChangeEvent) => { + setIsChecked(e.target.checked); + MessageHelper.sendMessage(DashboardMessage.updateSetting, { name: SETTINGS_DASHBOARD_OPENONSTART, value: e.target.checked }); + }; + + React.useEffect(() => { + console.log(`openOnStart`, settings.openOnStart); + setIsChecked(!!settings.openOnStart); + }, [settings?.openOnStart]); + + return ( +
    +
    + +
    +
    + +
    +
    + ); +}; \ No newline at end of file diff --git a/src/pagesView/components/Status.tsx b/src/pagesView/components/Status.tsx new file mode 100644 index 00000000..fea36c35 --- /dev/null +++ b/src/pagesView/components/Status.tsx @@ -0,0 +1,11 @@ +import * as React from 'react'; + +export interface IStatusProps { + draft: boolean; +} + +export const Status: React.FunctionComponent = ({draft}: React.PropsWithChildren) => { + return ( + {draft ? "Draft" : "Published"} + ); +}; \ No newline at end of file diff --git a/src/pagesView/constants/SortOption.ts b/src/pagesView/constants/SortOption.ts new file mode 100644 index 00000000..dff49d61 --- /dev/null +++ b/src/pagesView/constants/SortOption.ts @@ -0,0 +1,5 @@ +export enum SortOption { + LastModified = 1, + FileNameAsc, + FileNameDesc +} \ No newline at end of file diff --git a/src/pagesView/constants/Tab.ts b/src/pagesView/constants/Tab.ts new file mode 100644 index 00000000..e1c1c51d --- /dev/null +++ b/src/pagesView/constants/Tab.ts @@ -0,0 +1,5 @@ +export enum Tab { + All = 'all', + Published = 'published', + Draft = 'draft', +}; \ No newline at end of file diff --git a/src/pagesView/hooks/useMessages.tsx b/src/pagesView/hooks/useMessages.tsx new file mode 100644 index 00000000..cd812709 --- /dev/null +++ b/src/pagesView/hooks/useMessages.tsx @@ -0,0 +1,44 @@ +import { useState, useEffect } from 'react'; +import { MessageHelper } from '../../helpers/MessageHelper'; +import { ContentFolder } from '../../models'; +import { DashboardCommand } from '../DashboardCommand'; +import { DashboardMessage } from '../DashboardMessage'; +import { Page } from '../models/Page'; +import { Settings } from '../models/Settings'; + +const vscode = MessageHelper.getVsCodeAPI(); + +export default function useMessages(options?: any) { + const [loading, setLoading] = useState(false); + const [pages, setPages] = useState([]); + const [settings, setSettings] = useState({} as any); + + window.addEventListener('message', event => { + const message = event.data; + + switch (message.command) { + case DashboardCommand.loading: + setLoading(message.data); + break; + case DashboardCommand.settings: + setSettings(message.data); + break; + case DashboardCommand.pages: + setPages(message.data); + setLoading(false); + break; + } + }); + + useEffect(() => { + setLoading(true); + vscode.postMessage({ command: DashboardMessage.getTheme }); + vscode.postMessage({ command: DashboardMessage.getData }); + }, ['']); + + return { + loading, + pages, + settings + }; +} \ No newline at end of file diff --git a/src/pagesView/hooks/usePages.tsx b/src/pagesView/hooks/usePages.tsx new file mode 100644 index 00000000..3c1bf49b --- /dev/null +++ b/src/pagesView/hooks/usePages.tsx @@ -0,0 +1,68 @@ +import { useState, useEffect } from 'react'; +import { SortOption } from '../constants/SortOption'; +import { Tab } from '../constants/Tab'; +import { Page } from '../models/Page'; +import Fuse from 'fuse.js'; + +const fuseOptions: Fuse.IFuseOptions = { + keys: [ + "title", + "slug", + "description", + "fmFileName" + ] +}; + +export default function usePages(pages: Page[], tab: Tab, sorting: SortOption, group: string | null, search: string | null, tag: string | null, category: string | null) { + const [ pageItems, setPageItems ] = useState([]); + + useEffect(() => { + // Check if search needs to be performed + let searchedPages = pages; + if (search) { + const fuse = new Fuse(pages, fuseOptions); + const results = fuse.search(search); + searchedPages = results.map(page => page.item); + } + + // Filter the pages + let pagesToShow = searchedPages; + if (tab === Tab.Published) { + pagesToShow = searchedPages.filter(page => !page.draft); + } else if (tab === Tab.Draft) { + pagesToShow = searchedPages.filter(page => !!page.draft); + } else { + pagesToShow = searchedPages; + } + + // Sort the pages + let pagesSorted = pagesToShow; + if (sorting === SortOption.FileNameAsc) { + pagesSorted = pagesToShow.sort((a, b) => a.fmFileName.toLowerCase().localeCompare(b.fmFileName.toLowerCase())); + } else if (sorting === SortOption.FileNameDesc) { + pagesSorted = pagesToShow.sort((a, b) => b.fmFileName.toLowerCase().localeCompare(a.fmFileName.toLowerCase())); + } else { + pagesSorted = pagesToShow.sort((a, b) => b.fmModified - a.fmModified); + } + + if (group) { + pagesSorted = pagesSorted.filter(page => page.fmGroup === group); + } + + // Filter by tag + if (tag) { + pagesSorted = pagesSorted.filter(page => page.tags && page.tags.includes(tag)); + } + + // Filter by category + if (category) { + pagesSorted = pagesSorted.filter(page => page.categories && page.categories.includes(category)); + } + + setPageItems(pagesSorted); + }, [ pages, tab, sorting, group, search, tag, category ]); + + return { + pageItems + }; +} \ No newline at end of file diff --git a/src/pagesView/index.tsx b/src/pagesView/index.tsx new file mode 100644 index 00000000..ba00dddf --- /dev/null +++ b/src/pagesView/index.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; +import { render } from "react-dom"; +import { Dashboard } from "./components/Dashboard"; + +import './styles.css'; + +declare const acquireVsCodeApi: () => { + getState: () => T; + setState: (data: T) => void; + postMessage: (msg: unknown) => void; +}; + +const elm = document.querySelector("#app"); +render(, elm); \ No newline at end of file diff --git a/src/pagesView/models/Page.ts b/src/pagesView/models/Page.ts new file mode 100644 index 00000000..7c062809 --- /dev/null +++ b/src/pagesView/models/Page.ts @@ -0,0 +1,17 @@ +import { Uri } from "vscode"; + +export interface Page { + fmGroup: string; + fmFilePath: string; + fmFileName: string; + fmModified: number; + + title: string; + slug: string; + date: string | Date; + draft: string; + description: string; + + preview?: string; + [prop: string]: any; +} \ No newline at end of file diff --git a/src/pagesView/models/Settings.ts b/src/pagesView/models/Settings.ts new file mode 100644 index 00000000..1e64e53a --- /dev/null +++ b/src/pagesView/models/Settings.ts @@ -0,0 +1,9 @@ +import { ContentFolder } from './../../models/ContentFolder'; + +export interface Settings { + folders: ContentFolder[]; + initialized: boolean + tags: string[]; + categories: string[]; + openOnStart: boolean | null; +} \ No newline at end of file diff --git a/src/pagesView/styles.css b/src/pagesView/styles.css new file mode 100644 index 00000000..d88b334c --- /dev/null +++ b/src/pagesView/styles.css @@ -0,0 +1,19 @@ +@import "tailwindcss/base"; +@import "tailwindcss/components"; +@import "tailwindcss/utilities"; + + +.loader { + border-top-color: #15c2cb; + animation: spinner 1.5s linear infinite; +} + +@-webkit-keyframes spinner { + 0% { -webkit-transform: rotate(0deg); } + 100% { -webkit-transform: rotate(360deg); } +} + +@keyframes spinner { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} \ No newline at end of file diff --git a/src/viewpanel/CommandToCode.ts b/src/viewpanel/CommandToCode.ts index 71cc12cc..80fbcb3a 100644 --- a/src/viewpanel/CommandToCode.ts +++ b/src/viewpanel/CommandToCode.ts @@ -24,4 +24,5 @@ export enum CommandToCode { updatePreviewUrl = "update-preview-url", openInEditor = "open-in-editor", updateMetadata = "update-metadata", + openDashboard = "open-dashboard", } \ No newline at end of file diff --git a/src/viewpanel/ViewPanel.tsx b/src/viewpanel/ViewPanel.tsx index a3a42002..830ae365 100644 --- a/src/viewpanel/ViewPanel.tsx +++ b/src/viewpanel/ViewPanel.tsx @@ -10,6 +10,7 @@ import useMessages from './hooks/useMessages'; import { TagType } from './TagType'; import { FolderAndFiles } from './components/FolderAndFiles'; import { Metadata } from './components/Metadata'; +import { SponsorMsg } from './components/SponsorMsg'; export interface IViewPanelProps { } @@ -51,6 +52,8 @@ export const ViewPanel: React.FunctionComponent = (props: React
    + + ); }; \ No newline at end of file diff --git a/src/viewpanel/components/BaseView.tsx b/src/viewpanel/components/BaseView.tsx index 4d4dcace..c1a2b73a 100644 --- a/src/viewpanel/components/BaseView.tsx +++ b/src/viewpanel/components/BaseView.tsx @@ -1,13 +1,14 @@ import * as React from 'react'; import { FolderInfo, PanelSettings } from '../../models'; import { CommandToCode } from '../CommandToCode'; -import { MessageHelper } from '../helper/MessageHelper'; +import { MessageHelper } from '../../helpers/MessageHelper'; import { Collapsible } from './Collapsible'; import { GlobalSettings } from './GlobalSettings'; import { OtherActions } from './OtherActions'; import { FileList } from './FileList'; import { VsLabel } from './VscodeComponents'; import { FolderAndFiles } from './FolderAndFiles'; +import { SponsorMsg } from './SponsorMsg'; export interface IBaseViewProps { settings: PanelSettings | undefined; @@ -16,6 +17,10 @@ export interface IBaseViewProps { export const BaseView: React.FunctionComponent = ({settings, folderAndFiles}: React.PropsWithChildren) => { + const openDashboard = () => { + MessageHelper.sendMessage(CommandToCode.openDashboard); + }; + const initProject = () => { MessageHelper.sendMessage(CommandToCode.initProject); }; @@ -32,6 +37,7 @@ export const BaseView: React.FunctionComponent = ({settings, fol
    +
    @@ -41,6 +47,8 @@ export const BaseView: React.FunctionComponent = ({settings, fol + + ); }; \ No newline at end of file diff --git a/src/viewpanel/components/CustomScript.tsx b/src/viewpanel/components/CustomScript.tsx index e8b16514..76991086 100644 --- a/src/viewpanel/components/CustomScript.tsx +++ b/src/viewpanel/components/CustomScript.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { CommandToCode } from '../CommandToCode'; -import { MessageHelper } from '../helper/MessageHelper'; +import { MessageHelper } from '../../helpers/MessageHelper'; import { ActionButton } from './ActionButton'; export interface ICustomScriptProps { diff --git a/src/viewpanel/components/DateAction.tsx b/src/viewpanel/components/DateAction.tsx index dd6033af..adecf71b 100644 --- a/src/viewpanel/components/DateAction.tsx +++ b/src/viewpanel/components/DateAction.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { CommandToCode } from '../CommandToCode'; -import { MessageHelper } from '../helper/MessageHelper'; +import { MessageHelper } from '../../helpers/MessageHelper'; import { ActionButton } from './ActionButton'; export interface IDateActionProps {} diff --git a/src/viewpanel/components/FileList.tsx b/src/viewpanel/components/FileList.tsx index 10689329..716daeb7 100644 --- a/src/viewpanel/components/FileList.tsx +++ b/src/viewpanel/components/FileList.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { FileInfo } from '../../models'; import { CommandToCode } from '../CommandToCode'; -import { MessageHelper } from '../helper/MessageHelper'; +import { MessageHelper } from '../../helpers/MessageHelper'; import { FileIcon } from './Icons/FileIcon'; import { MarkdownIcon } from './Icons/MarkdownIcon'; import { VsLabel } from './VscodeComponents'; diff --git a/src/viewpanel/components/GlobalSettings.tsx b/src/viewpanel/components/GlobalSettings.tsx index 38b26220..43caf942 100644 --- a/src/viewpanel/components/GlobalSettings.tsx +++ b/src/viewpanel/components/GlobalSettings.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import { PanelSettings } from '../../models'; import { CommandToCode } from '../CommandToCode'; -import { MessageHelper } from '../helper/MessageHelper'; -import { useDebounce } from '../hooks/useDebounce'; +import { MessageHelper } from '../../helpers/MessageHelper'; +import { useDebounce } from '../../hooks/useDebounce'; import { Collapsible } from './Collapsible'; import { VsCheckbox, VsLabel } from './VscodeComponents'; diff --git a/src/viewpanel/components/Icons/HeartIcon.tsx b/src/viewpanel/components/Icons/HeartIcon.tsx new file mode 100644 index 00000000..1cfa4c22 --- /dev/null +++ b/src/viewpanel/components/Icons/HeartIcon.tsx @@ -0,0 +1,13 @@ +import * as React from 'react'; + +export interface IHeartIconProps { + className?: string; +} + +export const HeartIcon: React.FunctionComponent = ({className}: React.PropsWithChildren) => { + return ( + + + + ); +}; \ No newline at end of file diff --git a/src/viewpanel/components/Icons/MarkdownIcon.tsx b/src/viewpanel/components/Icons/MarkdownIcon.tsx index b342b232..fa84c441 100644 --- a/src/viewpanel/components/Icons/MarkdownIcon.tsx +++ b/src/viewpanel/components/Icons/MarkdownIcon.tsx @@ -1,9 +1,11 @@ import * as React from 'react'; -export interface IMarkdownIconProps {} +export interface IMarkdownIconProps { + className?: string; +} -export const MarkdownIcon: React.FunctionComponent = (props: React.PropsWithChildren) => { +export const MarkdownIcon: React.FunctionComponent = ({className}: React.PropsWithChildren) => { return ( - + ); }; \ No newline at end of file diff --git a/src/viewpanel/components/Metadata.tsx b/src/viewpanel/components/Metadata.tsx index 23225f61..75e04952 100644 --- a/src/viewpanel/components/Metadata.tsx +++ b/src/viewpanel/components/Metadata.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { PanelSettings } from '../../models'; import { CommandToCode } from '../CommandToCode'; -import { MessageHelper } from '../helper/MessageHelper'; +import { MessageHelper } from '../../helpers/MessageHelper'; import { TagType } from '../TagType'; import { Collapsible } from './Collapsible'; import { Toggle } from './Fields/Toggle'; diff --git a/src/viewpanel/components/OtherActions.tsx b/src/viewpanel/components/OtherActions.tsx index 0d123263..fa3dd60b 100644 --- a/src/viewpanel/components/OtherActions.tsx +++ b/src/viewpanel/components/OtherActions.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { PanelSettings } from '../../models'; import { CommandToCode } from '../CommandToCode'; -import { MessageHelper } from '../helper/MessageHelper'; +import { MessageHelper } from '../../helpers/MessageHelper'; import { Collapsible } from './Collapsible'; import { BugIcon } from './Icons/BugIcon'; import { CenterIcon } from './Icons/CenterIcon'; diff --git a/src/viewpanel/components/Preview.tsx b/src/viewpanel/components/Preview.tsx index c8dd7cb9..5f0d063c 100644 --- a/src/viewpanel/components/Preview.tsx +++ b/src/viewpanel/components/Preview.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; +import { MessageHelper } from '../../helpers/MessageHelper'; import { CommandToCode } from '../CommandToCode'; -import { MessageHelper } from '../helper/MessageHelper'; import { ActionButton } from './ActionButton'; export interface IPreviewProps { diff --git a/src/viewpanel/components/PublishAction.tsx b/src/viewpanel/components/PublishAction.tsx index 746b3a19..55d98941 100644 --- a/src/viewpanel/components/PublishAction.tsx +++ b/src/viewpanel/components/PublishAction.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; +import { MessageHelper } from '../../helpers/MessageHelper'; import { CommandToCode } from '../CommandToCode'; -import { MessageHelper } from '../helper/MessageHelper'; import { ActionButton } from './ActionButton'; export interface IPublishActionProps { diff --git a/src/viewpanel/components/SlugAction.tsx b/src/viewpanel/components/SlugAction.tsx index 83d3b9e1..85f57809 100644 --- a/src/viewpanel/components/SlugAction.tsx +++ b/src/viewpanel/components/SlugAction.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; +import { MessageHelper } from '../../helpers/MessageHelper'; import { SlugHelper } from '../../helpers/SlugHelper'; import { Slug } from '../../models/PanelSettings'; import { CommandToCode } from '../CommandToCode'; -import { MessageHelper } from '../helper/MessageHelper'; import { ActionButton } from './ActionButton'; export interface ISlugActionProps { diff --git a/src/viewpanel/components/SponsorMsg.tsx b/src/viewpanel/components/SponsorMsg.tsx new file mode 100644 index 00000000..4f876b98 --- /dev/null +++ b/src/viewpanel/components/SponsorMsg.tsx @@ -0,0 +1,14 @@ +import * as React from 'react'; +import { HeartIcon } from './Icons/HeartIcon'; + +export interface ISponsorMsgProps {} + +export const SponsorMsg: React.FunctionComponent = (props: React.PropsWithChildren) => { + return ( +

    + + Sponsor FrontMatter + +

    + ); +}; \ No newline at end of file diff --git a/src/viewpanel/components/TagPicker.tsx b/src/viewpanel/components/TagPicker.tsx index 4de2e939..0d19e065 100644 --- a/src/viewpanel/components/TagPicker.tsx +++ b/src/viewpanel/components/TagPicker.tsx @@ -3,10 +3,10 @@ import { Tags } from './Tags'; import { usePrevious } from '../hooks/usePrevious'; import { CommandToCode } from '../CommandToCode'; import { TagType } from '../TagType'; -import { MessageHelper } from '../helper/MessageHelper'; import Downshift from 'downshift'; import { AddIcon } from './Icons/AddIcon'; import { VsLabel } from './VscodeComponents'; +import { MessageHelper } from '../../helpers/MessageHelper'; export interface ITagPickerProps { type: string; diff --git a/src/viewpanel/hooks/useMessages.tsx b/src/viewpanel/hooks/useMessages.tsx index 3a9f62c8..3490a824 100644 --- a/src/viewpanel/hooks/useMessages.tsx +++ b/src/viewpanel/hooks/useMessages.tsx @@ -1,8 +1,8 @@ import { useState, useEffect } from 'react'; +import { MessageHelper } from '../../helpers/MessageHelper'; import { FolderInfo, PanelSettings } from '../../models/PanelSettings'; import { Command } from '../Command'; import { CommandToCode } from '../CommandToCode'; -import { MessageHelper } from '../helper/MessageHelper'; import { TagType } from '../TagType'; const vscode = MessageHelper.getVsCodeAPI(); diff --git a/src/webview/ExplorerView.ts b/src/webview/ExplorerView.ts index 7864207c..869cc8fd 100644 --- a/src/webview/ExplorerView.ts +++ b/src/webview/ExplorerView.ts @@ -18,7 +18,10 @@ import { Notifications } from '../helpers/Notifications'; import { COMMAND_NAME } from '../constants/Extension'; import { Folders } from '../commands/Folders'; import { Preview } from '../commands/Preview'; +import { getNonce } from '../helpers/getNonce'; +import { openFileInEditor } from '../helpers/openFileInEditor'; +const FILE_LIMIT = 10; export class ExplorerView implements WebviewViewProvider, Disposable { public static readonly viewType = "frontMatter.explorer"; @@ -166,11 +169,14 @@ export class ExplorerView implements WebviewViewProvider, Disposable { case CommandToCode.openPreview: await commands.executeCommand(COMMAND_NAME.preview); break; + case CommandToCode.openDashboard: + await commands.executeCommand(COMMAND_NAME.dashboard); + break; case CommandToCode.updatePreviewUrl: this.updatePreviewUrl(msg.data || ""); break; case CommandToCode.openInEditor: - this.openFileInEditor(msg.data); + openFileInEditor(msg.data); break; case CommandToCode.updateMetadata: this.updateMetadata(msg.data); @@ -253,20 +259,6 @@ export class ExplorerView implements WebviewViewProvider, Disposable { ArticleHelper.update(editor, article); } - /** - * Open the file via its path - */ - private async openFileInEditor(filePath: string) { - if (filePath) { - try { - const doc = await workspace.openTextDocument(Uri.file(filePath)); - await window.showTextDocument(doc, 1, false); - } catch (e) { - Notifications.error(`Couldn't open the file.`); - } - } - } - /** * Run a custom script * @param msg @@ -348,7 +340,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable { public async getFoldersAndFiles() { this.postWebviewMessage({ command: Command.folderInfo, - data: await Folders.getInfo() || null + data: await Folders.getInfo(FILE_LIMIT) || null }); } @@ -527,19 +519,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable { * @param msg */ private postWebviewMessage(msg: { command: Command, data?: any }) { - this.panel!.webview.postMessage(msg); - } - - /** - * Generate a unique nonce - */ - private getNonce() { - let text = ''; - const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - for (let i = 0; i < 32; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; + this.panel?.webview?.postMessage(msg); } /** @@ -552,7 +532,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable { const stylesUri = webView.asWebviewUri(Uri.joinPath(this.extPath, 'assets/media', 'styles.css')); const scriptUri = webView.asWebviewUri(Uri.joinPath(this.extPath, 'dist', 'viewpanel.js')); - const nonce = this.getNonce(); + const nonce = getNonce(); return ` diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 00000000..a408540b --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,115 @@ +const colors = require('tailwindcss/colors'); + +module.exports = { + mode: 'jit', + purge: ['./src/**/*.{js,jsx,ts,tsx}'], + darkMode: 'class', // or 'media' or 'class' + theme: { + extend: { + colors: { + white: colors.white, + gray: colors.trueGray, + "red": { + "50": "#ff7c7b", + "100": "#ff7271", + "200": "#ff6867", + "300": "#ff5e5d", + "400": "#ff5453", + "500": "#fe4a49", + "600": "#f4403f", + "700": "#ea3635", + "800": "#e02c2b", + "900": "#d62221" + }, + "blue": { + "50": "#90dfff", + "100": "#86d5ff", + "200": "#7ccbff", + "300": "#72c1ff", + "400": "#68b7fc", + "500": "#5eadf2", + "600": "#54a3e8", + "700": "#4a99de", + "800": "#408fd4", + "900": "#3685ca" + }, + "teal": { + "50": "#47f4fd", + "100": "#3deaf3", + "200": "#33e0e9", + "300": "#29d6df", + "400": "#1fccd5", + "500": "#15c2cb", + "600": "#0bb8c1", + "700": "#01aeb7", + "800": "#00a4ad", + "900": "#009aa3" + }, + "aqua": { + "50": "#76ffff", + "100": "#6cfffa", + "200": "#62fff0", + "300": "#58ffe6", + "400": "#4effdc", + "500": "#44ffd2", + "600": "#3af5c8", + "700": "#30ebbe", + "800": "#26e1b4", + "900": "#1cd7aa" + }, + "yellow": { + "50": "#ffff90", + "100": "#ffff86", + "200": "#ffff7c", + "300": "#fff872", + "400": "#ffee68", + "500": "#ffe45e", + "600": "#f5da54", + "700": "#ebd04a", + "800": "#e1c640", + "900": "#d7bc36" + }, + "whisper": { + "50": "#ffffff", + "100": "#ffffff", + "200": "#ffffff", + "300": "#ffffff", + "400": "#fdf9ff", + "500": "#f3eff5", + "600": "#e9e5eb", + "700": "#dfdbe1", + "800": "#d5d1d7", + "900": "#cbc7cd" + }, + "vulcan": { + "50": "#404551", + "100": "#363b47", + "200": "#2c313d", + "300": "#222733", + "400": "#181d29", + "500": "#0e131f", + "600": "#040915", + "700": "#00000b", + "800": "#000001", + "900": "#000000" + }, + "rose": { + "50": "#ff73da", + "100": "#ff69d0", + "200": "#ff5fc6", + "300": "#ff55bc", + "400": "#fb4bb2", + "500": "#f141a8", + "600": "#e7379e", + "700": "#dd2d94", + "800": "#d3238a", + "900": "#c91980" + } + } + }, + }, + variants: { + extend: {}, + }, + plugins: [], +} diff --git a/webpack.config.js b/webpack.config.js index 9dbe39bd..bfd5fdd3 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -63,5 +63,37 @@ module.exports = [ maxEntrypointSize: 400000, maxAssetSize: 400000 } + }, + { + name: 'pagesView', + target: 'web', + entry: './src/pagesView/index.tsx', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'pages.js' + }, + devtool: 'source-map', + resolve: { + extensions: ['.ts', '.js', '.tsx', '.jsx'] + }, + module: { + rules: [ + { + test: /\.(ts|tsx)$/, + exclude: /node_modules/, + use: [{ + loader: 'ts-loader' + }] + }, + { + test: /\.css$/, + use: ['style-loader', 'css-loader', 'postcss-loader'] + } + ] + }, + performance: { + maxEntrypointSize: 400000, + maxAssetSize: 400000 + } } ]; \ No newline at end of file