Compare commits
86 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01b1b3d0ea | ||
|
|
9f2c68fd66 | ||
|
|
9f21042e58 | ||
|
|
840e6fc6fd | ||
|
|
3f237386b2 | ||
|
|
eca6e00bf2 | ||
|
|
afcccd780b | ||
|
|
239c611d3e | ||
|
|
4a2f362ed7 | ||
|
|
b040669bfb | ||
|
|
2c5a35d9b8 | ||
|
|
eade3f4799 | ||
|
|
98d8daaa7c | ||
|
|
1c227c547f | ||
|
|
167c1fafa1 | ||
|
|
edf5fc8401 | ||
|
|
3de0006651 | ||
|
|
f79d382da5 | ||
|
|
11bb0fabcd | ||
|
|
4c43eb5eeb | ||
|
|
364d6ec3f9 | ||
|
|
40646fd1b3 | ||
|
|
2769eb9b71 | ||
|
|
aa97051437 | ||
|
|
496391b048 | ||
|
|
2e743c896f | ||
|
|
3941c33b1d | ||
|
|
cdc49c09f1 | ||
|
|
fc7300ba87 | ||
|
|
d3aa51c9ed | ||
|
|
387c906af2 | ||
|
|
d04c2a96b7 | ||
|
|
797c4cd2b1 | ||
|
|
0ebc874796 | ||
|
|
0b970a73b6 | ||
|
|
065b2fef1b | ||
|
|
b5033f6b45 | ||
|
|
274b341648 | ||
|
|
c51f2a80c3 | ||
|
|
e6415a3ee9 | ||
|
|
134a5aaa0d | ||
|
|
86c609f49f | ||
|
|
bf3dec46bc | ||
|
|
2d7c9cfc05 | ||
|
|
7e705ccfd8 | ||
|
|
1680637235 | ||
|
|
3de56b94c3 | ||
|
|
1b5840b2bf | ||
|
|
b14c4c50cf | ||
|
|
1f8c2f5b1e | ||
|
|
db3a584ab9 | ||
|
|
918f914c91 | ||
|
|
9f5d180447 | ||
|
|
77e2c68810 | ||
|
|
60caf743c6 | ||
|
|
b8dee7a5c9 | ||
|
|
3b6a7e9b71 | ||
|
|
648ee34171 | ||
|
|
4a88e471f6 | ||
|
|
14a2b715e7 | ||
|
|
0c368dbe65 | ||
|
|
f0b7542d94 | ||
|
|
80cd7d3eac | ||
|
|
ef08e0a34b | ||
|
|
62a2c22ba1 | ||
|
|
1417575b98 | ||
|
|
4862810035 | ||
|
|
e1b9bad05b | ||
|
|
47f7521e93 | ||
|
|
771f86710f | ||
|
|
dc55cedb0d | ||
|
|
e93c6832e4 | ||
|
|
0f0c7f9f3d | ||
|
|
d9984ce292 | ||
|
|
c9e813effe | ||
|
|
3fea99ef24 | ||
|
|
06a3841dd3 | ||
|
|
32fb742de5 | ||
|
|
c41b7adcfd | ||
|
|
be55a2237d | ||
|
|
265f788f98 | ||
|
|
c7d3a7dcf6 | ||
|
|
e02a766070 | ||
|
|
c9dd684451 | ||
|
|
5329033b46 | ||
|
|
4bd914fdba |
11
.all-contributorsrc
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"files": [],
|
||||
"imageSize": 100,
|
||||
"contributorsPerLine": 7,
|
||||
"contributorsSortAlphabetically": false,
|
||||
"badgeTemplate": "",
|
||||
"contributorTemplate": "",
|
||||
"types": {},
|
||||
"skipCi": "true",
|
||||
"contributors": []
|
||||
}
|
||||
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: Issue
|
||||
title: 'Issue: '
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: Enhancement
|
||||
title: 'Enhancement: '
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
|
||||
20
.github/ISSUE_TEMPLATE/showcase.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Showcase
|
||||
about: Let us know that you are using Front Matter and we'll add you on our showcase page
|
||||
title: 'Showcase: '
|
||||
labels: 'showcase'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
**Title you want to give your site**
|
||||
|
||||
Define a clear title that will be used on the showcase page. Example: `Front Matter`.
|
||||
|
||||
**Link to the site**
|
||||
|
||||
A URL to the site to add.
|
||||
|
||||
**A nice and clean description**
|
||||
|
||||
Keep it simple. Just let us know which static-site generator you used, and other frameworks.
|
||||
|
||||
71
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ main ]
|
||||
schedule:
|
||||
- cron: '24 14 * * 5'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'javascript' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
27
.github/workflows/release-beta.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: BETA Release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: "Build and release"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14
|
||||
registry-url: https://registry.npmjs.org/
|
||||
|
||||
- name: Install the dependencies
|
||||
run: npm i
|
||||
|
||||
- name: Prepare BETA
|
||||
run: node scripts/beta-release.js $GITHUB_RUN_ID
|
||||
|
||||
- name: Publish
|
||||
run: npx vsce publish -p ${{ secrets.VSCE_PAT }} --baseImagesUrl https://raw.githubusercontent.com/estruyf/vscode-front-matter/dev
|
||||
|
||||
8
.github/workflows/release.yml
vendored
@@ -19,10 +19,10 @@ jobs:
|
||||
|
||||
- name: Install the dependencies
|
||||
run: npm i
|
||||
|
||||
- name: Install vsce
|
||||
run: npm i -g vsce
|
||||
|
||||
- name: Prepare MAIN release
|
||||
run: node scripts/main-release.js
|
||||
|
||||
- name: Publish
|
||||
run: vsce publish -p ${{ secrets.VSCE_PAT }}
|
||||
run: npx vsce publish -p ${{ secrets.VSCE_PAT }}
|
||||
|
||||
|
||||
66
.vscode/recoil.code-snippets
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"Recoil Atom": {
|
||||
"prefix": "sq-atom",
|
||||
"body": [
|
||||
"import { atom } from 'recoil';",
|
||||
"",
|
||||
"export const ${1:CollectionId}Atom = atom({",
|
||||
" key: '${1:CollectionId}Atom',",
|
||||
" default: 1",
|
||||
"});"
|
||||
],
|
||||
"description": "Creates a new atom",
|
||||
"scope": "typescript"
|
||||
},
|
||||
"Recoil Selector (sync)": {
|
||||
"prefix": "sq-selector-sync",
|
||||
"body": [
|
||||
"import { selector } from 'recoil';",
|
||||
"",
|
||||
"export const ${1:CollectionData}Selector = selector({",
|
||||
" key: '${1:CollectionData}Selector',",
|
||||
" get: ({get}) => {",
|
||||
" return get(${1:CollectionData}Atom);",
|
||||
" }",
|
||||
"});"
|
||||
],
|
||||
"description": "Creates a new synchronous selector",
|
||||
"scope": "typescript"
|
||||
},
|
||||
"Recoil Selector (async)": {
|
||||
"prefix": "sq-selector-async",
|
||||
"body": [
|
||||
"import { selector } from 'recoil';",
|
||||
"",
|
||||
"export const ${1:CollectionData}Selector = selector({",
|
||||
" key: '${1:CollectionData}Selector',",
|
||||
" get: async ({get}) => {",
|
||||
" return await dataFetch(get(${2:CollectionIdState}));",
|
||||
" }",
|
||||
"});"
|
||||
],
|
||||
"description": "Creates a new asynchronous selector",
|
||||
"scope": "typescript"
|
||||
},
|
||||
"Recoil selectorFamily": {
|
||||
"prefix": "sq-selector-fam",
|
||||
"body": [
|
||||
"import { selectorFamily } from 'recoil';",
|
||||
"",
|
||||
"export const ${1:CollectionData}Selector = selectorFamily({",
|
||||
" key: '${1:CollectionData}Selector',",
|
||||
" get: id => async () => {",
|
||||
" return await dataFetch({id});",
|
||||
" }",
|
||||
"});"
|
||||
],
|
||||
"description": "Creates a selectorFamily (same as selector, but used to provide parameters)",
|
||||
"scope": "typescript"
|
||||
},
|
||||
"useTranslation": {
|
||||
"prefix": ["sq-translation", "useTranslation"],
|
||||
"body": "const { t: strings } = useTranslation();",
|
||||
"description": "Include the translations",
|
||||
"scope": "typescriptreact"
|
||||
}
|
||||
}
|
||||
12
.vscode/settings.json
vendored
@@ -19,5 +19,15 @@
|
||||
}
|
||||
],
|
||||
"eliostruyf.writingstyleguide.terms.isDisabled": true,
|
||||
"eliostruyf.writingstyleguide.biasFree.isDisabled": true
|
||||
"eliostruyf.writingstyleguide.biasFree.isDisabled": true,
|
||||
"exportall.config.folderListener": [
|
||||
"/src/pagesView/state/atom",
|
||||
"/src/pagesView/state/selectors"
|
||||
],
|
||||
"frontMatter.content.pageFolders": [
|
||||
{
|
||||
"title": "documentation",
|
||||
"path": "[[workspace]]/docs/content/docs"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -15,4 +15,6 @@ tailwind.config.js
|
||||
sample
|
||||
postcss.config.js
|
||||
.templates
|
||||
.github
|
||||
.github
|
||||
scripts
|
||||
.all-contributorsrc
|
||||
14
CHANGELOG.md
@@ -1,5 +1,19 @@
|
||||
# Change Log
|
||||
|
||||
## [3.1.0] - (Upcoming release)
|
||||
|
||||
- BETA version available at: [beta.frontmatter.codes](https://beta.frontmatter.codes)
|
||||
- [#72](https://github.com/estruyf/vscode-front-matter/issues/72): Media view on the dashboard
|
||||
- [#73](https://github.com/estruyf/vscode-front-matter/issues/73): List view option for the dashboard
|
||||
- [#77](https://github.com/estruyf/vscode-front-matter/issues/77): Dashboard grouping pages functionality integrated
|
||||
- [#81](https://github.com/estruyf/vscode-front-matter/issues/81): Optimizing the content folders to use a new setting to simplify configuration
|
||||
- [#87](https://github.com/estruyf/vscode-front-matter/issues/87): Fix issue with autofocus and command palette
|
||||
- [#88](https://github.com/estruyf/vscode-front-matter/issues/88): Fix issue with search sorting
|
||||
- [#89](https://github.com/estruyf/vscode-front-matter/issues/89): Clear filter, sorting, and grouping button added
|
||||
- [#90](https://github.com/estruyf/vscode-front-matter/issues/90): Refactoring to use Recoil state management
|
||||
- [#91](https://github.com/estruyf/vscode-front-matter/issues/91): Support image previews from content folders
|
||||
- [#98](https://github.com/estruyf/vscode-front-matter/issues/98): Add drag and drop support for media upload in a folder
|
||||
|
||||
## [3.0.2] - 2021-08-31
|
||||
|
||||
- [#82](https://github.com/estruyf/vscode-front-matter/issues/82): Hide the register and unregister commands from the command palette
|
||||
|
||||
93
README.beta.md
Normal file
@@ -0,0 +1,93 @@
|
||||
<h1 align="center">
|
||||
<a href="https://beta.frontmatter.codes">
|
||||
<img alt="Front Matter BETA" src="./assets/frontmatter-beta.png">
|
||||
</a>
|
||||
</h1>
|
||||
|
||||
<h2 align="center">Front Matter BETA is an essential Visual Studio Code extension when you want to manage the markdown pages of your static sites.</h2>
|
||||
|
||||
<h2 align="center">This is the BETA version of Front Matter. If you were looking for the main version, check it out at <a href="https://frontmatter.codes">frontmatter.codes</a></h2>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter" title="Check it out on the Visual Studio Marketplace">
|
||||
<img src="https://vsmarketplacebadge.apphb.com/version/eliostruyf.vscode-front-matter.svg" alt="Visual Studio Marketplace" style="display: inline-block" />
|
||||
</a>
|
||||
|
||||
<img src="https://vsmarketplacebadge.apphb.com/installs/eliostruyf.vscode-front-matter.svg" alt="Number of installs" style="display: inline-block;margin-left:10px" />
|
||||
|
||||
<img src="https://vsmarketplacebadge.apphb.com/rating/eliostruyf.vscode-front-matter.svg" alt="Ratings" style="display: inline-block;margin-left:10px" />
|
||||
|
||||
<a href="https://www.buymeacoffee.com/zMeFRy9" title="Buy me a coffee" style="margin-left:10px">
|
||||
<img src="https://img.shields.io/badge/Buy%20me%20a%20coffee-€%203-blue?logo=buy-me-a-coffee&style=flat" alt="Buy me a coffee" style="display: inline-block" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h2 align="center">
|
||||
<a href="https://beta.frontmatter.codes" title="Documentation @ beta.frontmatter.codes">
|
||||
Check out the extension documentation at beta.frontmatter.codes
|
||||
</a>
|
||||
</h2>
|
||||
|
||||
Front Matter BETA 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 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
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/v2.5.0/site-preview.png" alt="Site preview" style="display: inline-block" />
|
||||
</p>
|
||||
|
||||
> If you see something missing in your article creation flow, please feel free to reach out.
|
||||
|
||||
**Version 3**
|
||||
|
||||
In version v3 we introduced the welcome and dashboard webview. The welcome view allows to get you started using the extension, and the dashboard 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 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.
|
||||
|
||||
<p align="center" style="margin-top: 2rem;">
|
||||
<a href="https://www.producthunt.com/posts/front-matter?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-front-matter" target="_blank">
|
||||
<img src="https://api.producthunt.com/widgets/embed-image/v1/featured.png?post_id=309033&theme=dark" alt="Front Matter BETA - Managing your static sites straight from within VS Code | Product Hunt" style="width: 250px; height: 40px;" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h2 align="center">
|
||||
<a href="https://beta.frontmatter.codes" title="Documentation @ beta.frontmatter.codes">
|
||||
Check out the extension documentation at beta.frontmatter.codes
|
||||
</a>
|
||||
</h2>
|
||||
|
||||
## 👉 Contributors 🤘
|
||||
|
||||
<a href="https://github.com/estruyf/vscode-front-matter/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=estruyf/vscode-front-matter" />
|
||||
</a>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
## 🖤 Sponsors
|
||||
|
||||
<p align="center">
|
||||
<a href="https://vercel.com/?utm_source=vscode-frontmatter&utm_campaign=oss">
|
||||
<img src="assets/sponsors/powered-by-vercel.png" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<p align="center">
|
||||
<a href="https://visitorbadge.io">
|
||||
<img src="https://estruyf-github.azurewebsites.net/api/VisitorHit?user=estruyf&repo=vscode-front-matter&countColor=%23F05450&labelColor=%230E131F" height="25px" />
|
||||
</a>
|
||||
</p>
|
||||
36
README.md
@@ -21,8 +21,8 @@
|
||||
</p>
|
||||
|
||||
<h2 align="center">
|
||||
<a href="https://frontmatter.codes" title="Documenation @ frontmatter.codes">
|
||||
Check out the extension documenation at frontmatter.codes
|
||||
<a href="https://frontmatter.codes" title="Documentation @ frontmatter.codes">
|
||||
Check out the extension documentation at frontmatter.codes
|
||||
</a>
|
||||
</h2>
|
||||
|
||||
@@ -52,18 +52,38 @@ In version v3 we introduced the welcome and dashboard webview. The welcome view
|
||||
|
||||
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.
|
||||
|
||||
<h2 align="center">
|
||||
<a href="https://frontmatter.codes" title="Documenation @ frontmatter.codes">
|
||||
Check out the extension documenation at frontmatter.codes
|
||||
</a>
|
||||
</h2>
|
||||
|
||||
<p align="center" style="margin-top: 2rem;">
|
||||
<a href="https://www.producthunt.com/posts/front-matter?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-front-matter" target="_blank">
|
||||
<img src="https://api.producthunt.com/widgets/embed-image/v1/featured.png?post_id=309033&theme=dark" alt="Front Matter - Managing your static sites straight from within VS Code | Product Hunt" style="width: 250px; height: 40px;" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h2 align="center">
|
||||
<a href="https://frontmatter.codes" title="Documentation @ frontmatter.codes">
|
||||
Check out the extension documentation at frontmatter.codes
|
||||
</a>
|
||||
</h2>
|
||||
|
||||
## 👉 Contributors 🤘
|
||||
|
||||
<a href="https://github.com/estruyf/vscode-front-matter/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=estruyf/vscode-front-matter" />
|
||||
</a>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
## 🖤 Sponsors
|
||||
|
||||
<p align="center">
|
||||
<a href="https://vercel.com/?utm_source=vscode-frontmatter&utm_campaign=oss">
|
||||
<img src="assets/sponsors/powered-by-vercel.png" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<p align="center">
|
||||
<a href="https://visitorbadge.io">
|
||||
<img src="https://estruyf-github.azurewebsites.net/api/VisitorHit?user=estruyf&repo=vscode-front-matter&countColor=%23F05450&labelColor=%230E131F" height="25px" />
|
||||
|
||||
BIN
assets/frontmatter-beta.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
45
assets/frontmatter-beta.svg
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 28 28" style="enable-background:new 0 0 28 28;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFE45E;}
|
||||
.st1{fill:none;stroke:#FFE45E;stroke-width:2;stroke-miterlimit:10;}
|
||||
.st2{font-family:'MyriadPro-Bold';}
|
||||
.st3{font-size:8px;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M4.1,10.2H2.4V2.1h3.1V4H4.1v1.2h1.2V7H4.1V10.2z"/>
|
||||
<path class="st0" d="M10.7,10.2H8.9L8,7.3c0-0.1,0-0.1,0-0.2C7.9,7.1,7.9,7,7.8,6.8v0.6v2.9H6.1V2.1h1.8c0.8,0,1.3,0.2,1.8,0.6
|
||||
c0.5,0.5,0.8,1.2,0.8,2.1c0,1-0.4,1.6-1.1,2L10.7,10.2z M7.9,5.8L7.9,5.8c0.3,0,0.5-0.1,0.6-0.3S8.7,5,8.7,4.8c0-0.6-0.3-1-0.8-1
|
||||
l0,0V5.8z"/>
|
||||
<path class="st0" d="M16.1,6.2c0,1.2-0.2,2.3-0.7,3.1s-1.1,1.2-1.7,1.2s-1.2-0.3-1.6-0.9c-0.6-0.8-0.9-1.9-0.9-3.4
|
||||
s0.3-2.6,0.9-3.4C12.6,2.3,13,2,13.7,2c0.8,0,1.3,0.4,1.8,1.2C15.8,3.8,16.1,4.8,16.1,6.2z M14.3,6.2c0-1.4-0.2-2.2-0.7-2.2
|
||||
c-0.2,0-0.4,0.2-0.5,0.6c-0.1,0.4-0.2,0.9-0.2,1.6c0,0.7,0.1,1.2,0.2,1.6c0.1,0.4,0.3,0.6,0.5,0.6c0.2,0,0.4-0.2,0.5-0.6
|
||||
C14.2,7.3,14.3,6.9,14.3,6.2z"/>
|
||||
<path class="st0" d="M16.8,10.2V2.1h1.7l0.9,2.9c0.1,0.1,0.1,0.3,0.2,0.6c0.1,0.2,0.1,0.5,0.2,0.8L20,7c-0.1-0.7-0.1-1.3-0.2-1.8
|
||||
s-0.1-1-0.1-1.2V2.1h1.7v8.2h-1.6l-0.9-3c-0.1-0.3-0.2-0.6-0.3-0.9c-0.1-0.3-0.1-0.6-0.2-0.8c0,0.6,0.1,1.1,0.1,1.5
|
||||
c0,0.4,0,0.8,0,1.2v2.1h-1.7V10.2z"/>
|
||||
<path class="st0" d="M24.6,10.2h-1.7V4h-1V2.1h3.7V4h-1.1V10.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
<rect class="st1" width="28" height="28"/>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M3.1,11.6H4l0.6,3c0.1,0.4,0.2,0.8,0.2,1.2C4.9,16.2,4.9,16.6,5,17c0-0.1,0-0.1,0-0.1v-0.1l0.2-0.9l0.1-0.8
|
||||
l0.1-0.5l0.6-3h0.9l0.7,7.5h-1l-0.2-2.6c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0-0.2v-1v-0.9l0,0c0,0,0,0,0-0.1v0.2c0,0.2,0,0.3-0.1,0.5
|
||||
c-0.1,0.2,0,0.2-0.1,0.3L6,15.7V16l-0.6,3.3H4.7l-0.6-2.8c-0.1-0.4-0.2-0.8-0.2-1.1c-0.1-0.4-0.1-0.8-0.2-1.2l-0.3,5.2h-1
|
||||
L3.1,11.6z"/>
|
||||
<path class="st0" d="M9.4,11.6h0.8l1.6,7.5h-1l-0.3-1.5H9l-0.3,1.5h-1L9.4,11.6z M10.4,16.8l-0.3-1.2C10,14.8,9.8,13.9,9.7,13
|
||||
c0,0.5-0.1,0.9-0.2,1.4c-0.1,0.5-0.2,1-0.3,1.5l-0.2,1L10.4,16.8L10.4,16.8z"/>
|
||||
<path class="st0" d="M11.6,11.6h3.3v0.9h-1.1v6.7h-1v-6.7h-1.2V11.6z"/>
|
||||
<path class="st0" d="M14.9,11.6h3.3v0.9h-1.1v6.7h-1v-6.7h-1.2V11.6z"/>
|
||||
<path class="st0" d="M18.8,11.6h2.7v0.9h-1.7v2.4h1.5v0.9h-1.5v2.6h1.7v0.9h-2.7V11.6z"/>
|
||||
<path class="st0" d="M22.3,11.6h1.3c0.6,0,1,0.1,1.2,0.4c0.3,0.3,0.5,0.9,0.5,1.6c0,0.5-0.1,1-0.3,1.3c-0.2,0.3-0.4,0.5-0.8,0.6
|
||||
l1.4,3.7h-1l-1.4-3.7v3.7h-1L22.3,11.6L22.3,11.6z M23.3,14.9c0.4,0,0.7-0.1,0.8-0.3c0.2-0.2,0.2-0.5,0.2-0.9c0-0.2,0-0.4-0.1-0.6
|
||||
c-0.1-0.2-0.1-0.3-0.2-0.4c-0.1-0.1-0.2-0.2-0.3-0.2s-0.3-0.1-0.4-0.1h-0.2v2.5H23.3z"/>
|
||||
</g>
|
||||
</g>
|
||||
<text transform="matrix(1 0 0 1 5.4457 25.9479)" class="st0 st2 st3">BETA</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
6
assets/powered-by-vercel.svg
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
assets/sponsors/powered-by-vercel.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
6
assets/sponsors/powered-by-vercel.svg
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 275 KiB After Width: | Height: | Size: 263 KiB |
|
Before Width: | Height: | Size: 276 KiB After Width: | Height: | Size: 265 KiB |
BIN
assets/v3.1.0/media.png
Normal file
|
After Width: | Height: | Size: 327 KiB |
@@ -41,9 +41,12 @@ export const Markdown: React.FunctionComponent<IMarkdownProps> = ({content}: Rea
|
||||
components={{
|
||||
a: ({node, ...props}) => {
|
||||
const url = props?.href || "";
|
||||
const vscodeUrl = props && (props as any)["data-vscode"] ? (props as any)["data-vscode"] : "";
|
||||
const title = getTitle(props);
|
||||
const elm = <Link key={url as string} href={url as string}><a title={title}>{title}</a></Link>;
|
||||
return elm;
|
||||
if (vscodeUrl) {
|
||||
return <Link key={vscodeUrl as string} href={vscodeUrl as string}><a title={title}>{title}</a></Link>;
|
||||
}
|
||||
return <Link key={url as string} href={url as string}><a title={title}>{title}</a></Link>;
|
||||
},
|
||||
h1: ({node, ...props}) => (<h1 id={generateId(props)}>{getTitle(props)}</h1>),
|
||||
h2: ({node, ...props}) => (<h2 id={generateId(props)}>{getTitle(props)}</h2>),
|
||||
|
||||
@@ -4,6 +4,8 @@ import { Logo } from '../Images';
|
||||
import Link from 'next/link';
|
||||
import { Extension } from '../../constants/extension';
|
||||
import { useRouter } from 'next/router';
|
||||
import { Searchbox } from '../Page/Searchbox';
|
||||
import { isProduction } from '../../helpers/isProduction';
|
||||
|
||||
export interface INavigationProps {}
|
||||
|
||||
@@ -11,39 +13,61 @@ export const Navigation: React.FunctionComponent<INavigationProps> = (props: Rea
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<nav className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" aria-label="Top">
|
||||
<div className="w-full py-6 flex items-center justify-center lg:justify-between border-b border-teal-500 lg:border-none">
|
||||
<div className="flex items-center">
|
||||
<Link href="/">
|
||||
<a title={Extension.name}>
|
||||
<span className="sr-only">{Extension.name}</span>
|
||||
<Logo className={`text-whisper-500 h-12 w-auto`} />
|
||||
<>
|
||||
{
|
||||
!isProduction() ? (
|
||||
<div className={`bg-yellow-500 text-center py-2 px-4`}>
|
||||
<a href={`https://frontmatter.codes`} title={`Go to main release documentation`} className={`text-base font-medium text-vulcan-500 hover:text-vulcan-900`}>
|
||||
You are currently viewing the BETA version of Front Matter documentation. Click on the banner to go to the main release documentation.
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="ml-10 space-x-4">
|
||||
<div className="hidden ml-10 space-x-8 lg:block">
|
||||
{navigation.main.map((link) => (
|
||||
<a key={link.name} href={link.href} title={link.title} className={`text-base font-medium text-whisper-500 hover:text-whisper-900 ${link.href === router.asPath ? `text-teal-800` : ``}`}>
|
||||
{link.name}
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
|
||||
<nav className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" aria-label="Top">
|
||||
<div className="w-full py-6 flex items-center justify-center lg:justify-between border-b border-teal-500 lg:border-none">
|
||||
<div className="flex items-center">
|
||||
<Link href="/">
|
||||
<a title={Extension.name}>
|
||||
<span className="sr-only">{Extension.name}</span>
|
||||
<Logo className={`text-whisper-500 h-12 w-auto`} />
|
||||
</a>
|
||||
))}
|
||||
{navigation.social.map((link) => (
|
||||
<a key={link.name} href={link.href} title={link.title} className={`text-base font-medium text-whisper-500 hover:text-whisper-900`} rel={`noopener noreferrer`}>
|
||||
<span className="sr-only">{link.name}</span>
|
||||
<link.icon className="inline-block h-6 w-6" aria-hidden="true" />
|
||||
</a>
|
||||
))}
|
||||
</Link>
|
||||
</div>
|
||||
<div className="space-x-4">
|
||||
<div className="hidden ml-10 space-x-8 lg:flex justify-center items-center">
|
||||
{navigation.main.map((link) => (
|
||||
<a key={link.name} href={link.href} title={link.title} className={`text-base font-medium text-whisper-500 hover:text-whisper-900 ${link.href === router.asPath ? `text-teal-800` : ``}`}>
|
||||
{link.name}
|
||||
</a>
|
||||
))}
|
||||
{navigation.social.map((link) => (
|
||||
<a key={link.name} href={link.href} title={link.title} className={`text-base font-medium text-whisper-500 hover:text-whisper-900`} rel={`noopener noreferrer`}>
|
||||
<span className="sr-only">{link.name}</span>
|
||||
<link.icon className="inline-block h-6 w-6" aria-hidden="true" />
|
||||
</a>
|
||||
))}
|
||||
|
||||
<Searchbox />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-4 flex flex-wrap justify-center space-x-6 lg:hidden">
|
||||
{navigation.main.map((link) => (
|
||||
<a key={link.name} href={link.href} title={link.title} className="text-base font-medium text-whisper-500 hover:text-whisper-900">
|
||||
{link.name}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
<div className="py-4 flex flex-wrap justify-center space-x-6 lg:hidden">
|
||||
{navigation.main.map((link) => (
|
||||
<a key={link.name} href={link.href} title={link.title} className="text-base font-medium text-whisper-500 hover:text-whisper-900">
|
||||
{link.name}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
<div className="py-4 flex flex-wrap justify-center space-x-6 lg:hidden">
|
||||
{navigation.social.map((link) => (
|
||||
<a key={link.name} href={link.href} title={link.title} className={`text-base font-medium text-whisper-500 hover:text-whisper-900`} rel={`noopener noreferrer`}>
|
||||
<span className="sr-only">{link.name}</span>
|
||||
<link.icon className="inline-block h-6 w-6" aria-hidden="true" />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
</>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Extension } from '../../constants/extension';
|
||||
import { isProduction } from '../../helpers/isProduction';
|
||||
|
||||
export interface ICTAProps {}
|
||||
|
||||
@@ -8,22 +9,23 @@ export const CTA: React.FunctionComponent<ICTAProps> = (props: React.PropsWithCh
|
||||
const { t: strings } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="px-4 sm:px-0 pt-8 overflow-hidden sm:pt-12 lg:relative lg:py-48">
|
||||
<div className="px-4 sm:px-0 py-8 overflow-hidden lg:relative lg:py-48">
|
||||
<div className="mx-auto sm:max-w-3xl sm:px-6 lg:px-8 lg:max-w-7xl lg:grid lg:grid-cols-2 lg:gap-24">
|
||||
<div className={`my-4 sm:my-5 lg:my-6`}>
|
||||
<h1 className="text-5xl tracking-tight font-extrabold sm:leading-none lg:text-5xl xl:text-6xl">
|
||||
<span className="md:block">{strings(`cta_title`)}</span>{' '}
|
||||
<span className="text-teal-500 md:block">{Extension.name}</span>
|
||||
<h1 className="text-5xl lg:text-5xl xl:text-6xl tracking-tight font-extrabold sm:leading-none">
|
||||
<span className="text-teal-500 md:block">{Extension.name}</span>{' '}
|
||||
<span className="block">{strings(`cta_title`)}</span>
|
||||
<span className={`sr-only`}>{strings(`cta_title_sr`)}</span>
|
||||
</h1>
|
||||
|
||||
<p className="mt-3 text-base text-whisper-700 sm:mt-5 sm:text-xl lg:text-lg xl:text-xl">
|
||||
<h2 className="mt-3 text-base text-whisper-700 sm:mt-5 sm:text-xl lg:text-lg xl:text-xl">
|
||||
{strings(`cta_description`)}
|
||||
</p>
|
||||
</h2>
|
||||
|
||||
<div className="mt-10 max-w-sm mx-auto sm:max-w-none">
|
||||
<div className="space-y-4 sm:space-y-0 sm:mx-auto sm:inline-grid sm:grid-cols-2 sm:gap-5">
|
||||
<a href={Extension.installLink} className="flex items-center justify-center px-4 py-3 border border-transparent text-base font-medium shadow-sm text-white bg-teal-500 hover:bg-opacity-70 sm:px-8" rel={`noopener noreferrer`}>
|
||||
{strings(`cta_button_primary`)}
|
||||
<a href={isProduction() ? Extension.installLink : Extension.installBetaLink} className="flex items-center justify-center px-4 py-3 border border-transparent text-base font-medium shadow-sm text-white bg-teal-500 hover:bg-opacity-70 sm:px-8" rel={`noopener noreferrer`}>
|
||||
{isProduction() ? strings(`cta_button_primary`) : strings(`cta_button_beta_primary`)}
|
||||
</a>
|
||||
<a href={`/docs`} title={`Read our documentation`} className="flex items-center justify-center px-4 py-3 border border-transparent text-base font-medium shadow-sm text-vulcan-500 bg-whisper-500 hover:bg-opacity-70 sm:px-8">
|
||||
{strings(`cta_button_secondary`)}
|
||||
@@ -36,10 +38,10 @@ export const CTA: React.FunctionComponent<ICTAProps> = (props: React.PropsWithCh
|
||||
<div className="sm:mx-auto sm:max-w-3xl sm:px-6">
|
||||
<div className={`py-12 sm:relative sm:py-16 lg:absolute lg:inset-y-0 lg:right-0 lg:w-1/2`}>
|
||||
<div className={`relative sm:mx-auto sm:max-w-3xl sm:px-0 lg:-mr-40 lg:max-w-none lg:h-full lg:pl-12`}>
|
||||
<img className={`w-full lg:h-full lg:w-auto lg:max-w-none`} src={`/assets/site-preview.png`} alt={`Site preview`} />
|
||||
<img className={`w-full lg:h-full lg:w-auto lg:max-w-none`} src={`/assets/site-preview.png`} alt={`Front Matter - Headless CMS - Live page preview`} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,6 +2,8 @@ import * as React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { features } from '../../constants/features';
|
||||
import { CheckIcon } from '@heroicons/react/outline';
|
||||
import Link from 'next/link';
|
||||
import { Extension } from '../../constants/extension';
|
||||
|
||||
export interface IFeaturesProps {}
|
||||
|
||||
@@ -9,11 +11,11 @@ export const Features: React.FunctionComponent<IFeaturesProps> = (props: React.P
|
||||
const { t: strings } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className={``}>
|
||||
<div className="max-w-7xl mx-auto py-16 px-4 sm:px-6 lg:py-24 lg:px-8">
|
||||
<div className={`bg-whisper-500 text-vulcan-500`}>
|
||||
<div className="max-w-7xl mx-auto py-12 sm:py-16 px-4 sm:px-6 lg:px-8">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h2 className="text-3xl font-extrabold">{strings(`features_title`)}</h2>
|
||||
<p className="mt-4 text-lg text-whisper-700">
|
||||
<p className="mt-4 text-lg text-vulcan-300">
|
||||
{strings(`features_description`)}
|
||||
</p>
|
||||
</div>
|
||||
@@ -21,13 +23,28 @@ export const Features: React.FunctionComponent<IFeaturesProps> = (props: React.P
|
||||
{features.map((feature) => (
|
||||
<div key={feature.name} className="relative">
|
||||
<dt>
|
||||
<CheckIcon className="absolute h-6 w-6 text-teal-500" aria-hidden="true" />
|
||||
<p className="ml-9 text-lg leading-6 font-medium text-whisper-500">{strings(feature.name)}</p>
|
||||
<CheckIcon className="absolute h-6 w-6 text-teal-800" aria-hidden="true" />
|
||||
<p className="ml-9 text-lg leading-6 font-medium text-vulcan-320">{strings(feature.name)}</p>
|
||||
</dt>
|
||||
<dd className="mt-2 ml-9 text-base text-whisper-800">{strings(feature.description)}</dd>
|
||||
<dd className="mt-2 ml-9 text-base text-vulcan-100">{strings(feature.description)}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
|
||||
<div className="mt-6 flex flex-col justify-center items-center">
|
||||
<p className={`text-xl tracking-tight font-bold sm:leading-none text-vulcan-50`}>
|
||||
{strings(`features_cta_title`)}
|
||||
</p>
|
||||
<p className="mt-4">
|
||||
<Link href={Extension.featureLink} >
|
||||
<a className={`inline-block px-4 py-3 border border-transparent text-base font-medium shadow-sm text-white bg-vulcan-50 hover:bg-opacity-70 sm:px-8`}
|
||||
target={`_blank`}
|
||||
rel={`noopener noreferrer`}>
|
||||
{strings(`features_cta_button`)}
|
||||
</a>
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -18,8 +18,12 @@ export const Footer: React.FunctionComponent<IFooterProps> = (props: React.Props
|
||||
))}
|
||||
</nav>
|
||||
<div className="mt-8 flex justify-center space-x-6">
|
||||
<a href="https://visitorbadge.io/status?path=https%3A%2F%2Ffrontmatter.codes" title={`Daily Front Matter visitors`}>
|
||||
<img src="https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2Ffrontmatter.codes&countColor=%23060A15&labelColor=%23060A15" />
|
||||
<a href="https://visitorbadge.io/status?path=https%3A%2F%2Ffrontmatter.codes" title={`Daily Front Matter visitors`} rel={`noopener noreferrer`}>
|
||||
<img src="https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2Ffrontmatter.codes&countColor=%23060A15&labelColor=%23060A15" alt={`Visitors`} />
|
||||
</a>
|
||||
|
||||
<a href={Extension.extensionLink} title={`Extension installs`} rel={`noopener noreferrer`}>
|
||||
<img src={`https://vsmarketplacebadge.apphb.com/installs-short/eliostruyf.vscode-front-matter.svg?style=for-the-badge&color=060A15&labelColor=060A15`} alt={`Installations of the extension`} />
|
||||
</a>
|
||||
|
||||
{navigation.social.map((item) => (
|
||||
|
||||
@@ -8,10 +8,10 @@ export const Generators: React.FunctionComponent<IGeneratorsProps> = (props: Rea
|
||||
|
||||
return (
|
||||
<div className="bg-whisper-100">
|
||||
<div className="max-w-7xl mx-auto py-16 px-4 sm:px-6 lg:px-8">
|
||||
<p className="text-center text-sm font-semibold uppercase text-vulcan-500 tracking-wide">
|
||||
<div className="max-w-7xl mx-auto py-12 sm:py-16 px-4 sm:px-6 lg:px-8">
|
||||
<h2 className="text-center text-sm font-semibold uppercase text-vulcan-500 tracking-wide">
|
||||
{strings(`generators_title`)}
|
||||
</p>
|
||||
</h2>
|
||||
|
||||
<div className="mt-6 grid grid-cols-2 gap-8 md:grid-cols-6">
|
||||
<div className="col-span-1 flex justify-center">
|
||||
@@ -35,7 +35,9 @@ export const Generators: React.FunctionComponent<IGeneratorsProps> = (props: Rea
|
||||
</div>
|
||||
|
||||
<div className="mt-6 flex justify-center">
|
||||
<p className={`text-2xl tracking-tight font-bold sm:leading-none text-vulcan-500`}>and many more...</p>
|
||||
<p className={`text-2xl tracking-tight font-bold sm:leading-none text-vulcan-500`}>
|
||||
{strings(`generators_more`)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
57
docs/components/Page/Hero.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import Link from 'next/link';
|
||||
import * as React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export interface IHeroProps {
|
||||
view: "left" | "right";
|
||||
title: string;
|
||||
description: string | JSX.Element;
|
||||
imgSrc: string;
|
||||
imgAlt: string;
|
||||
link?: string;
|
||||
linkText?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const Hero: React.FunctionComponent<IHeroProps> = ({view, title, description, imgSrc, imgAlt, link, linkText, className}: React.PropsWithChildren<IHeroProps>) => {
|
||||
|
||||
return (
|
||||
<div className={`overflow-hidden lg:relative`}>
|
||||
<div className={`${className || ""} px-4 sm:px-6 xl:px-0 py-12 sm:py-16 lg:relative lg:mx-auto lg:max-w-7xl lg:grid lg:grid-cols-2 lg:grid-flow-col-dense lg:gap-24`}>
|
||||
<div className={`max-w-3xl mx-auto lg:py-48 lg:max-w-none lg:mx-0 lg:px-0 ${view === "left" ? `lg:col-start-2` : `lg:col-start-1`}`}>
|
||||
<div>
|
||||
<h2 className="text-3xl lg:text-3xl xl:text-4xl tracking-tight font-extrabold sm:leading-none">
|
||||
{title}
|
||||
</h2>
|
||||
{
|
||||
typeof description === 'string' ? (
|
||||
<p className="my-6 text-base text-whisper-700 sm:mt-5 sm:text-xl lg:text-lg xl:text-xl">
|
||||
{description}
|
||||
</p>
|
||||
) : (
|
||||
{...description}
|
||||
)
|
||||
}
|
||||
{
|
||||
link && linkText && (
|
||||
<Link href={link} >
|
||||
<a className={`inline-block px-4 py-3 border border-transparent text-base font-medium shadow-sm text-white bg-teal-500 hover:bg-opacity-70 sm:px-8`}>
|
||||
{linkText}
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`sm:mx-auto sm:max-w-3xl sm:px-6 lg:px-0 lg:mx-0 lg:max-w-none mt-12 sm:mt-16 lg:mt-0 ${view === "left" ? `lg:col-start-1` : `lg:col-start-2`}`}>
|
||||
<div className={`${view === "left" ? `lg:pr-6 lg:-ml-16` : `lg:pl-6 lg:-mr-16`} lg:px-0 lg:m-0 lg:relative lg:h-full`}>
|
||||
<img className={`w-full rounded-xl lg:absolute lg:h-full lg:w-auto lg:max-w-none ${view === "left" ? `lg:right-0` : `lg:left-0`}`}
|
||||
src={imgSrc}
|
||||
alt={imgAlt} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,13 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import { Navigation } from '../Navigation';
|
||||
import { Footer } from './Footer';
|
||||
import { Sponsors } from './Sponsors';
|
||||
|
||||
export interface ILayoutProps {}
|
||||
|
||||
export const Layout: React.FunctionComponent<ILayoutProps> = (props: React.PropsWithChildren<ILayoutProps>) => {
|
||||
return (
|
||||
<div className={`flex flex-col h-screen`}>
|
||||
<header>
|
||||
<header className={`lg:sticky lg:top-0 z-50 bg-vulcan-500 bg-opacity-80 backdrop-blur-lg`}>
|
||||
<Navigation />
|
||||
</header>
|
||||
|
||||
@@ -15,6 +16,8 @@ export const Layout: React.FunctionComponent<ILayoutProps> = (props: React.Props
|
||||
{props.children}
|
||||
</main>
|
||||
|
||||
<Sponsors />
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
|
||||
20
docs/components/Page/Searchbox.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import * as React from 'react';
|
||||
// const docsearch = require('@docsearch/js');
|
||||
// import { useEffect } from 'react';
|
||||
import { DocSearch } from '@docsearch/react';
|
||||
|
||||
export interface ISearchboxProps {}
|
||||
|
||||
export const Searchbox: React.FunctionComponent<ISearchboxProps> = (props: React.PropsWithChildren<ISearchboxProps>) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<DocSearch
|
||||
apiKey={process.env.NEXT_PUBLIC_AGOLIA_APIKEY || ""}
|
||||
indexName={process.env.NEXT_PUBLIC_AGOLIA_INDEX || ""}
|
||||
appId={process.env.NEXT_PUBLIC_AGOLIA_APPID || ""}
|
||||
disableUserPersonalization={true}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
24
docs/components/Page/Sponsors.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import * as React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export interface ISponsorsProps {}
|
||||
|
||||
export const Sponsors: React.FunctionComponent<ISponsorsProps> = (props: React.PropsWithChildren<ISponsorsProps>) => {
|
||||
const { t: strings } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="bg-vulcan-600">
|
||||
<div className="max-w-7xl mx-auto pt-12 px-4 sm:px-6 lg:px-8">
|
||||
<p className="text-center text-sm font-semibold uppercase text-whisper-900 tracking-wide">
|
||||
{strings(`sponsors_title`)}
|
||||
</p>
|
||||
|
||||
<div className="mt-6">
|
||||
<a target={`_blank`} rel={`noopener noreferrer`} href={`https://vercel.com/?utm_source=vscode-frontmatter&utm_campaign=oss`} title={`Powered by Vercel`} className="col-span-1 flex justify-center">
|
||||
<img className="h-12" src="/assets/sponsors/powered-by-vercel.svg" alt="Vercel" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
9
docs/components/Page/Testimonial.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export interface ITestimonialProps {}
|
||||
|
||||
export const Testimonial: React.FunctionComponent<ITestimonialProps> = (props: React.PropsWithChildren<ITestimonialProps>) => {
|
||||
return (
|
||||
null
|
||||
);
|
||||
};
|
||||
8
docs/components/Page/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export * from './CTA';
|
||||
export * from './Features';
|
||||
export * from './Footer';
|
||||
export * from './Generators';
|
||||
export * from './Hero';
|
||||
export * from './Layout';
|
||||
export * from './Sponsors';
|
||||
export * from './Testimonial';
|
||||
@@ -1,13 +1,16 @@
|
||||
|
||||
export const Extension = {
|
||||
name: `Front Matter`,
|
||||
home: `The CMS running in VS Code`,
|
||||
description: `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.`,
|
||||
home: `The CMS running in VS Code for your static sites`,
|
||||
description: `Headless CMS running in Visual Studio Code that helps managing your static sites. Supports Hugo, Jekyll, Docusaurus, NextJs, Gatsby, and more.`,
|
||||
|
||||
githubLink: "https://github.com/estruyf/vscode-front-matter",
|
||||
issueLink: "https://github.com/estruyf/vscode-front-matter/issues",
|
||||
sponsorLink: "https://github.com/sponsors/estruyf",
|
||||
extensionLink: "https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter",
|
||||
extensionBetaLink: "https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter-beta",
|
||||
reviewLink: "https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter&ssr=false#review-details",
|
||||
installLink: "vscode:extension/eliostruyf.vscode-front-matter"
|
||||
installLink: "vscode:extension/eliostruyf.vscode-front-matter",
|
||||
installBetaLink: "vscode:extension/eliostruyf.vscode-front-matter-beta",
|
||||
showcaseLink: "https://github.com/estruyf/vscode-front-matter/issues/new?assignees=&labels=&template=showcase.md&title=Showcase%3A+",
|
||||
featureLink: "https://github.com/estruyf/vscode-front-matter/issues/new?assignees=&labels=&template=feature_request.md&title=Enhancement%3A+"
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { HeartIcon } from "@heroicons/react/outline";
|
||||
import { HeartIcon, StarIcon } from "@heroicons/react/outline";
|
||||
import React from "react";
|
||||
import { Extension } from "./extension";
|
||||
|
||||
@@ -24,12 +24,20 @@ export const navigation = {
|
||||
)
|
||||
},
|
||||
{
|
||||
name: 'Sponsor us',
|
||||
name: 'Become a sponsor',
|
||||
title: 'Become a sponsor, and get mentioned',
|
||||
href: Extension.sponsorLink,
|
||||
icon: (props: any) => (
|
||||
<HeartIcon {...props} />
|
||||
)
|
||||
},
|
||||
{
|
||||
name: 'Review',
|
||||
title: 'Write a review on the marketplace',
|
||||
href: Extension.reviewLink,
|
||||
icon: (props: any) => (
|
||||
<StarIcon {...props} />
|
||||
)
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,5 +1,19 @@
|
||||
# Change Log
|
||||
|
||||
## [3.1.0] - (Upcoming release)
|
||||
|
||||
- BETA version available at: [beta.frontmatter.codes](https://beta.frontmatter.codes)
|
||||
- [#72](https://github.com/estruyf/vscode-front-matter/issues/72): Media view on the dashboard
|
||||
- [#73](https://github.com/estruyf/vscode-front-matter/issues/73): List view option for the dashboard
|
||||
- [#77](https://github.com/estruyf/vscode-front-matter/issues/77): Dashboard grouping pages functionality integrated
|
||||
- [#81](https://github.com/estruyf/vscode-front-matter/issues/81): Optimizing the content folders to use a new setting to simplify configuration
|
||||
- [#87](https://github.com/estruyf/vscode-front-matter/issues/87): Fix issue with autofocus and command palette
|
||||
- [#88](https://github.com/estruyf/vscode-front-matter/issues/88): Fix issue with search sorting
|
||||
- [#89](https://github.com/estruyf/vscode-front-matter/issues/89): Clear filter, sorting, and grouping button added
|
||||
- [#90](https://github.com/estruyf/vscode-front-matter/issues/90): Refactoring to use Recoil state management
|
||||
- [#91](https://github.com/estruyf/vscode-front-matter/issues/91): Support image previews from content folders
|
||||
- [#98](https://github.com/estruyf/vscode-front-matter/issues/98): Add drag and drop support for media upload in a folder
|
||||
|
||||
## [3.0.2] - 2021-08-31
|
||||
|
||||
- [#82](https://github.com/estruyf/vscode-front-matter/issues/82): Hide the register and unregister commands from the command palette
|
||||
|
||||
@@ -9,7 +9,7 @@ weight: 6
|
||||
|
||||
# Commands
|
||||
|
||||
Front Matter actions are also available as commands. In this section of the documenation all commands will be explained.
|
||||
Front Matter actions are also available as commands. In this section of the documentation all commands will be explained.
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -9,25 +9,51 @@ weight: 3
|
||||
|
||||
# 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.
|
||||
Managing your Markdown pages/media has never been easier in VS Code. With the Front Matter dashboard, you will be able to view all your pages and media.
|
||||
|
||||

|
||||
On the contents view, you can **search**, **filter**, **sort** your pages and much more.
|
||||
|
||||

|
||||
|
||||
On the media view, you can quickly glance all the available media files in your project and perform quick actions like copying the relative path.
|
||||
|
||||

|
||||
|
||||
In order to start using the dashboard, you will have to let the extension know in which folder(s) it can find your pages. Be sure to follow our [getting started](/docs/getting-started) guide.
|
||||
|
||||
> **Important**: If your preview images are not loading, 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.
|
||||
|
||||
## Supported filters
|
||||
## Contents view
|
||||
|
||||
### Supported filters
|
||||
|
||||
- Tag filter
|
||||
- Category filter
|
||||
- Content folder (when you have multiple registered)
|
||||
|
||||
## Supported sorting
|
||||
### Supported sorting
|
||||
|
||||
- Last modified
|
||||
- Filename (asc/desc)
|
||||
|
||||
## Media view
|
||||
|
||||
The media view has been created to make it easier to look at all media files available for your articles. When you click on an image, it will show a lightbox, so that it is easier to glance at small images.
|
||||
|
||||

|
||||
|
||||
### Media actions
|
||||
|
||||
On the image card, there are actions like copying the relative path or deleting the media file.
|
||||
|
||||

|
||||
|
||||
### Drag and Drop
|
||||
|
||||
On the media view, we enabled drag and drop for your media files. You can easily drop any image from your explorer/finder window into one of your folders.
|
||||
|
||||

|
||||
|
||||
## Show on startup
|
||||
|
||||
If you want, you can check 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.
|
||||
@@ -11,10 +11,23 @@ weight: 2
|
||||
|
||||
To get you started, you first need to install the extension in Visual Studio Code.
|
||||
|
||||
## Installation
|
||||
|
||||
You can get the extension via:
|
||||
|
||||
- The VS Code marketplace: [VS Code Marketplace - Front Matter](https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter).
|
||||
- The extension CLI: `ext install eliostruyf.vscode-front-matter`
|
||||
- Or by clicking on the following link: <a href="" title="open extension in VS Code" data-vscode="vscode:extension/eliostruyf.vscode-front-matter">open extension in VS Code</a>
|
||||
|
||||
### Beta version
|
||||
|
||||
If you have the courage to test out the beta features, we made available a beta version as well. You can install this via:
|
||||
|
||||
- The VS Code marketplace: [VS Code Marketplace - Front Matter BETA](https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter-beta).
|
||||
- The extension CLI: `ext install eliostruyf.vscode-front-matter-beta`
|
||||
- Or by clicking on the following link: <a href="" title="open extension in VS Code" data-vscode="vscode:extension/eliostruyf.vscode-front-matter-beta">open extension in VS Code</a>
|
||||
|
||||
> **Info**: The BETA docs can be found on [beta.frontmatter.codes](https://beta.frontmatter.codes).
|
||||
|
||||
## Welcome screen
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
title: Settings
|
||||
slug: settings
|
||||
description:
|
||||
description: null
|
||||
date: '2021-08-30T16:13:00.546Z'
|
||||
lastmod: '2021-08-30T16:13:01.763Z'
|
||||
lastmod: '2021-09-08T07:08:17.747Z'
|
||||
weight: 7
|
||||
---
|
||||
|
||||
@@ -31,9 +31,9 @@ Specify if you want to highlight the Front Matter in the Markdown file.
|
||||
- Type: `boolean`
|
||||
- Default: `true`
|
||||
|
||||
### frontMatter.content.folders
|
||||
### frontMatter.content.pageFolders
|
||||
|
||||
This array of folders defines where the extension can easily create new content by running the create article command.
|
||||
This array of folders defines where the extension can find your content and create new content by running the create article command.
|
||||
|
||||
- Type: `object[]`
|
||||
- Default: `[]`
|
||||
@@ -42,14 +42,17 @@ Sample:
|
||||
|
||||
```json
|
||||
{
|
||||
"frontMatter.content.folders": [{
|
||||
"title": "Articles",
|
||||
"fsPath": "<the path to the folder>",
|
||||
"paths": ["<wsl-folder-path>"]
|
||||
}]
|
||||
"frontMatter.content.pageFolders": [
|
||||
{
|
||||
"title": "Blog posts",
|
||||
"path": "[[workspace]]/content/posts"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
> **Important**: `[[workspace]]` is a placeholder that the extension uses to replace the workspace path. The reason why we choose to use this, is because some do not keep the original folder name.
|
||||
|
||||
### frontMatter.content.publicFolder
|
||||
|
||||
Specify the folder name where all your assets are located. For instance in Hugo this is the `static` folder.
|
||||
@@ -230,4 +233,11 @@ Specify the folder to use for your article templates.
|
||||
Specify the prefix you want to add for your new article filenames.
|
||||
|
||||
- Type: `string`
|
||||
- Default: `yyyy-MM-dd`
|
||||
- Default: `yyyy-MM-dd`
|
||||
|
||||
|
||||
## Deprecated settings
|
||||
|
||||
### frontMatter.content.folders
|
||||
|
||||
This setting has been deprecated since version `3.1.0` in favor of the newly introduced `frontMatter.content.pageFolders` setting.
|
||||
|
||||
3
docs/helpers/isProduction.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
|
||||
export const isProduction = () => process.env.NEXT_PUBLIC_VERCEL_ENV === 'production';
|
||||
@@ -3,26 +3,46 @@ import { Extension } from "../constants/extension";
|
||||
|
||||
export const strings = {
|
||||
// CTA
|
||||
cta_title: "Manage your static site with",
|
||||
cta_description: "Create, edit, and preview your pages within Visual Studio Code. Front Matter allows you to keep control of your static site without any external tools.",
|
||||
cta_title: "Headless CMS running in VS Code",
|
||||
cta_title_sr: " that helps managing your static sites and Markdown based sites. Supports Hugo, Jekyll, Docusaurus, NextJs, Gatsby, and more.",
|
||||
cta_description: "Why leave your editor if it can give you all power to manage your static sites? Front Matter is a headless CMS that lets you create, edit, and preview your Markdown based content straight within Visual Studio Code.",
|
||||
cta_button_primary: "Get the extension",
|
||||
cta_button_beta_primary: "Get the BETA extension",
|
||||
cta_button_secondary: "Read our docs",
|
||||
|
||||
// Generators
|
||||
generators_title: "Built for any static-site generator you might like",
|
||||
generators_more: "and many more...",
|
||||
|
||||
// Hero
|
||||
hero_title: "Bringing the CMS to your editor",
|
||||
hero_description: "Why would you leave your editor when you can perform all tasks straight from within it?",
|
||||
hero_description_second: "We at Front Matter believe that you should keep using what you like. For us, this is Visual Studio Code. Use the same editor you use to code, but with unique features to make it suitable for writing and managing your Markdown articles.",
|
||||
hero_button_primary: "Get started",
|
||||
|
||||
// Hero media
|
||||
hero_media_title: "Checking your media was never easier",
|
||||
hero_media_description: "Quickly glance all your media files straight from within VS Code. You will be able to filter by your content folders, and perform quick media actions.",
|
||||
hero_media_button_primary: "See what it can do",
|
||||
|
||||
// Testimonials
|
||||
testimonials_title: "What others are saying",
|
||||
testimonials_description: "We love Front Matter and we're excited to share it with the world.",
|
||||
|
||||
// Features
|
||||
features_title: "Features",
|
||||
features_description: "Check out our main features which help you manage your static-site",
|
||||
features_cta_title: "Missing a feature?",
|
||||
features_cta_button: "Tell us what you need",
|
||||
|
||||
// Feature
|
||||
feature_title_1: "Manage your site within VS Code",
|
||||
feature_description_1: "A Content Management System built to run within Visual Studio Code. No dependencies on any website or API.",
|
||||
feature_title_1: "Offline management",
|
||||
feature_description_1: "A Content Management System built to run within Visual Studio Code. No dependencies on any website or API. Write wherever you are, commit when you are online.",
|
||||
|
||||
feature_title_2: "Preview",
|
||||
feature_description_2: "Allow showing your page previews within Visual Studio Code.",
|
||||
feature_title_2: "Full site/page preview",
|
||||
feature_description_2: "Allow showing your site and page previews within Visual Studio Code without the need of opening a browser.",
|
||||
|
||||
feature_title_3: "Page dashboard",
|
||||
feature_title_3: "Content dashboard",
|
||||
feature_description_3: "Our page dashboard allows you to search, filter, sort, and group all your static site pages.",
|
||||
|
||||
feature_title_4: "SEO Checks",
|
||||
@@ -34,17 +54,20 @@ export const strings = {
|
||||
feature_title_6: "Extensibility",
|
||||
feature_description_6: "Add your actions with our custom scripting capability. For instance, you can use a script and hook it up to the extension if you want to generate preview images. If we do not support it, you can build it and share it with us.",
|
||||
|
||||
// Sponsors
|
||||
sponsors_title: "Special thanks to our sponsor",
|
||||
|
||||
// Documentation
|
||||
documentation_title: "Documentation",
|
||||
documentation_description: `Get to know more about how you can use ${Extension.name} with our documentation.`,
|
||||
|
||||
// Showcase
|
||||
showcase_title: "Showcase",
|
||||
showcase_description: "Check out our showcase of static-sites using Front Matter.",
|
||||
showcase_description: "Check out our showcase of sites using Front Matter.",
|
||||
|
||||
// Changelog
|
||||
changelog_title: "Changelog",
|
||||
changelog_description: "Check out our changelog for Front Matter.",
|
||||
changelog_page_title: `Latest updates`,
|
||||
changelog_page_description: `An overview of all updates from the ${Extension.name} extension`,
|
||||
};
|
||||
};
|
||||
|
||||
319
docs/package-lock.json
generated
@@ -4,6 +4,142 @@
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@algolia/autocomplete-core": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.2.2.tgz",
|
||||
"integrity": "sha512-JOQaURze45qVa8OOFDh+ozj2a/ObSRsVyz6Zd0aiBeej+RSTqrr1hDVpGNbbXYLW26G5ujuc9QIdH+rBHn95nw==",
|
||||
"requires": {
|
||||
"@algolia/autocomplete-shared": "1.2.2"
|
||||
}
|
||||
},
|
||||
"@algolia/autocomplete-preset-algolia": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.2.2.tgz",
|
||||
"integrity": "sha512-AZkh+bAMaJDzMZTelFOXJTJqkp5VPGH8W3n0B+Ggce7DdozlMRsDLguKTCQAkZ0dJ1EbBPyFL5ztL/JImB137Q==",
|
||||
"requires": {
|
||||
"@algolia/autocomplete-shared": "1.2.2"
|
||||
}
|
||||
},
|
||||
"@algolia/autocomplete-shared": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.2.2.tgz",
|
||||
"integrity": "sha512-mLTl7d2C1xVVazHt/bqh9EE/u2lbp5YOxLDdcjILXmUqOs5HH1D4SuySblXaQG1uf28FhTqMGp35qE5wJQnqAw=="
|
||||
},
|
||||
"@algolia/cache-browser-local-storage": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.10.5.tgz",
|
||||
"integrity": "sha512-cfX2rEKOtuuljcGI5DMDHClwZHdDqd2nT2Ohsc8aHtBiz6bUxKVyIqxr2gaC6tU8AgPtrTVBzcxCA+UavXpKww==",
|
||||
"requires": {
|
||||
"@algolia/cache-common": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@algolia/cache-common": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.10.5.tgz",
|
||||
"integrity": "sha512-1mClwdmTHll+OnHkG+yeRoFM17kSxDs4qXkjf6rNZhoZGXDvfYLy3YcZ1FX4Kyz0DJv8aroq5RYGBDsWkHj6Tw=="
|
||||
},
|
||||
"@algolia/cache-in-memory": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.10.5.tgz",
|
||||
"integrity": "sha512-+ciQnfIGi5wjMk02XhEY8fmy2pzy+oY1nIIfu8LBOglaSipCRAtjk6WhHc7/KIbXPiYzIwuDbM2K1+YOwSGjwA==",
|
||||
"requires": {
|
||||
"@algolia/cache-common": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@algolia/client-account": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.10.5.tgz",
|
||||
"integrity": "sha512-I9UkSS2glXm7RBZYZIALjBMmXSQbw/fI/djPcBHxiwXIheNIlqIFl2SNPkvihpPF979BSkzjqdJNRPhE1vku3Q==",
|
||||
"requires": {
|
||||
"@algolia/client-common": "4.10.5",
|
||||
"@algolia/client-search": "4.10.5",
|
||||
"@algolia/transporter": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@algolia/client-analytics": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.10.5.tgz",
|
||||
"integrity": "sha512-h2owwJSkovPxzc+xIsjY1pMl0gj+jdVwP9rcnGjlaTY2fqHbSLrR9yvGyyr6305LvTppxsQnfAbRdE/5Z3eFxw==",
|
||||
"requires": {
|
||||
"@algolia/client-common": "4.10.5",
|
||||
"@algolia/client-search": "4.10.5",
|
||||
"@algolia/requester-common": "4.10.5",
|
||||
"@algolia/transporter": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@algolia/client-common": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.10.5.tgz",
|
||||
"integrity": "sha512-21FAvIai5qm8DVmZHm2Gp4LssQ/a0nWwMchAx+1hIRj1TX7OcdW6oZDPyZ8asQdvTtK7rStQrRnD8a95SCUnzA==",
|
||||
"requires": {
|
||||
"@algolia/requester-common": "4.10.5",
|
||||
"@algolia/transporter": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@algolia/client-personalization": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.10.5.tgz",
|
||||
"integrity": "sha512-nH+IyFKBi8tCyzGOanJTbXC5t4dspSovX3+ABfmwKWUYllYzmiQNFUadpb3qo+MLA3jFx5IwBesjneN6dD5o3w==",
|
||||
"requires": {
|
||||
"@algolia/client-common": "4.10.5",
|
||||
"@algolia/requester-common": "4.10.5",
|
||||
"@algolia/transporter": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@algolia/client-search": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.10.5.tgz",
|
||||
"integrity": "sha512-1eQFMz9uodrc5OM+9HeT+hHcfR1E1AsgFWXwyJ9Q3xejA2c1c4eObGgOgC9ZoshuHHdptaTN1m3rexqAxXRDBg==",
|
||||
"requires": {
|
||||
"@algolia/client-common": "4.10.5",
|
||||
"@algolia/requester-common": "4.10.5",
|
||||
"@algolia/transporter": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@algolia/logger-common": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.10.5.tgz",
|
||||
"integrity": "sha512-gRJo9zt1UYP4k3woEmZm4iuEBIQd/FrArIsjzsL/b+ihNoOqIxZKTSuGFU4UUZOEhvmxDReiA4gzvQXG+TMTmA=="
|
||||
},
|
||||
"@algolia/logger-console": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.10.5.tgz",
|
||||
"integrity": "sha512-4WfIbn4253EDU12u9UiYvz+QTvAXDv39mKNg9xSoMCjKE5szcQxfcSczw2byc6pYhahOJ9PmxPBfs1doqsdTKQ==",
|
||||
"requires": {
|
||||
"@algolia/logger-common": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@algolia/requester-browser-xhr": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.10.5.tgz",
|
||||
"integrity": "sha512-53/MURQEqtK+bGdfq4ITSPwTh5hnADU99qzvpAINGQveUFNSFGERipJxHjTJjIrjFz3vxj5kKwjtxDnU6ygO9g==",
|
||||
"requires": {
|
||||
"@algolia/requester-common": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@algolia/requester-common": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.10.5.tgz",
|
||||
"integrity": "sha512-UkVa1Oyuj6NPiAEt5ZvrbVopEv1m/mKqjs40KLB+dvfZnNcj+9Fry4Oxnt15HMy/HLORXsx4UwcthAvBuOXE9Q=="
|
||||
},
|
||||
"@algolia/requester-node-http": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.10.5.tgz",
|
||||
"integrity": "sha512-aNEKVKXL4fiiC+bS7yJwAHdxln81ieBwY3tsMCtM4zF9f5KwCzY2OtN4WKEZa5AAADVcghSAUdyjs4AcGUlO5w==",
|
||||
"requires": {
|
||||
"@algolia/requester-common": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@algolia/transporter": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.10.5.tgz",
|
||||
"integrity": "sha512-F8DLkmIlvCoMwSCZA3FKHtmdjH3o5clbt0pi2ktFStVNpC6ZDmY307HcK619bKP5xW6h8sVJhcvrLB775D2cyA==",
|
||||
"requires": {
|
||||
"@algolia/cache-common": "4.10.5",
|
||||
"@algolia/logger-common": "4.10.5",
|
||||
"@algolia/requester-common": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@babel/code-frame": {
|
||||
"version": "7.12.11",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz",
|
||||
@@ -67,6 +203,31 @@
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@docsearch/css": {
|
||||
"version": "3.0.0-alpha.40",
|
||||
"resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.0.0-alpha.40.tgz",
|
||||
"integrity": "sha512-PrOTPgJMl+Iji1zOH0+J0PEDMriJ1teGxbgll7o4h8JrvJW6sJGqQw7/bLW7enWiFaxbJMK76w1yyPNLFHV7Qg=="
|
||||
},
|
||||
"@docsearch/js": {
|
||||
"version": "3.0.0-alpha.40",
|
||||
"resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.0.0-alpha.40.tgz",
|
||||
"integrity": "sha512-0ysRM0jk1KAbw/QsHPHIKoa7OeCm2Mwz0JgcPnhWRvvA28dzP+f6OIsL6eGu3VJR043tH9OrvVf/FnvLtTtZtw==",
|
||||
"requires": {
|
||||
"@docsearch/react": "3.0.0-alpha.40",
|
||||
"preact": "^10.0.0"
|
||||
}
|
||||
},
|
||||
"@docsearch/react": {
|
||||
"version": "3.0.0-alpha.40",
|
||||
"resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.0.0-alpha.40.tgz",
|
||||
"integrity": "sha512-aKxnu7sgpP1R7jtgOV/pZdJEHXx6Ts+jnS9U/ejSUS2BMUpwQI5SA3oLs1BA5TA9kIViJ5E+rrjh0VsbcsJ6sQ==",
|
||||
"requires": {
|
||||
"@algolia/autocomplete-core": "1.2.2",
|
||||
"@algolia/autocomplete-preset-algolia": "1.2.2",
|
||||
"@docsearch/css": "3.0.0-alpha.40",
|
||||
"algoliasearch": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"@eslint/eslintrc": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz",
|
||||
@@ -173,9 +334,9 @@
|
||||
"integrity": "sha512-jDJTpta+P4p1NZTFVLHJ/TLFVYVcOqv6l8xwOeBKNPMgY/zDYH/YH7SJbvrr/h1RcS9GzbPcLKGzpuK9cV56UA=="
|
||||
},
|
||||
"@next/env": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-11.1.0.tgz",
|
||||
"integrity": "sha512-zPJkMFRenSf7BLlVee8987G0qQXAhxy7k+Lb/5hLAGkPVHAHm+oFFeL+2ipbI2KTEFlazdmGY0M+AlLQn7pWaw=="
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-11.1.1.tgz",
|
||||
"integrity": "sha512-UEAzlfKofotLmj9LIgNixAfXpRck9rt/1CU9Q4ZtNDueGBJQP3HUzPHlrLChltWY2TA5MOzDQGL82H0a3+i5Ag=="
|
||||
},
|
||||
"@next/eslint-plugin-next": {
|
||||
"version": "11.1.0",
|
||||
@@ -187,14 +348,14 @@
|
||||
}
|
||||
},
|
||||
"@next/polyfill-module": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@next/polyfill-module/-/polyfill-module-11.1.0.tgz",
|
||||
"integrity": "sha512-64EgW8SzJRQls2yJ5DkuljRxgE24o2kYtX/ghTkPUJYsfidHMWzQGwg26IgRbb/uHqTd1G0W5UkKag+Nt8TWaQ=="
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@next/polyfill-module/-/polyfill-module-11.1.1.tgz",
|
||||
"integrity": "sha512-9FyVSnz00WGdlLsgc2w1xL1Lm/Q25y6FYIyA+1WlJvT6LA2lbR78GKiHgedzUvrAatVGAcg/Og+d0d7B4tsJOg=="
|
||||
},
|
||||
"@next/react-dev-overlay": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@next/react-dev-overlay/-/react-dev-overlay-11.1.0.tgz",
|
||||
"integrity": "sha512-h+ry0sTk1W3mJw+TwEf91aqLbBJ5oqAsxfx+QryqEItNtfW6zLSSjxkyTYTqX8DkgSssQQutQfATkzBVgOR+qQ==",
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@next/react-dev-overlay/-/react-dev-overlay-11.1.1.tgz",
|
||||
"integrity": "sha512-CXc/A0DbSk5VXYu4+zr0fHm52Zh/LhPlLyVPEctJOZL64ccxkls5xGoXvgolJCku9L0pLjJzvdfAmhNLOp5dyw==",
|
||||
"requires": {
|
||||
"@babel/code-frame": "7.12.11",
|
||||
"anser": "1.4.9",
|
||||
@@ -255,9 +416,33 @@
|
||||
}
|
||||
},
|
||||
"@next/react-refresh-utils": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@next/react-refresh-utils/-/react-refresh-utils-11.1.0.tgz",
|
||||
"integrity": "sha512-g5DtFTpLTGa36iy9DuZawtJeitI11gysFGKPQQqy+mNbSFazguArcJ10gAYFlbqpIi4boUamWNI5mAoSPx3kog=="
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@next/react-refresh-utils/-/react-refresh-utils-11.1.1.tgz",
|
||||
"integrity": "sha512-j186y+lWc8BHAuysAWvlOqO9Bp7E3BLK/d/Ju3W2sP5BCH5ZLyLG/p308zSy/O0MGTag0B038ZA1dCy/msouRQ=="
|
||||
},
|
||||
"@next/swc-darwin-arm64": {
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-11.1.1.tgz",
|
||||
"integrity": "sha512-KyB0aLpfQ+B2dsyGYpkM0ZwK3PV0t4C4b9yjgQc1VoTVnIjzXdDPnNOuVvmD849ZNOHfj3x8e2rlbxkj0lPm3A==",
|
||||
"optional": true
|
||||
},
|
||||
"@next/swc-darwin-x64": {
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-11.1.1.tgz",
|
||||
"integrity": "sha512-B3ZXgrGx0bQplbrk2oggPjKPPsmyg8Fl0PJLMTVQ+erQ8g1m5QzyS9P6tB3SiIZa180JgENuguTHlVK5qEj4UA==",
|
||||
"optional": true
|
||||
},
|
||||
"@next/swc-linux-x64-gnu": {
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-11.1.1.tgz",
|
||||
"integrity": "sha512-qvZL7gSKF+E+GZ3L1XiTnE3cOh9rk0wkqimT/q+wwcZA4E720Lu4lrT79I3HPuj6i/JPgGvmNskcnYrDeaoFaw==",
|
||||
"optional": true
|
||||
},
|
||||
"@next/swc-win32-x64-msvc": {
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-11.1.1.tgz",
|
||||
"integrity": "sha512-jhnCiA1De1L+kA0gmHG1AJijHoxOcrETWziDWy8fcqSrM1NlC4aJ5Mnu6k0QMcM9MnmXTA4TQZOEv3kF7vhJUQ==",
|
||||
"optional": true
|
||||
},
|
||||
"@node-rs/helper": {
|
||||
"version": "1.2.1",
|
||||
@@ -340,9 +525,9 @@
|
||||
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA=="
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "16.7.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.6.tgz",
|
||||
"integrity": "sha512-VESVNFoa/ahYA62xnLBjo5ur6gPsgEE5cNRy8SrdnkZ2nwJSW0kJ4ufbFr2zuU9ALtHM8juY53VcRoTA7htXSg=="
|
||||
"version": "16.7.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.10.tgz",
|
||||
"integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA=="
|
||||
},
|
||||
"@types/parse-json": {
|
||||
"version": "4.0.0",
|
||||
@@ -520,6 +705,27 @@
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"algoliasearch": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.10.5.tgz",
|
||||
"integrity": "sha512-KmH2XkiN+8FxhND4nWFbQDkIoU6g2OjfeU9kIv4Lb+EiOOs3Gpp7jvd+JnatsCisAZsnWQdjd7zVlW7I/85QvQ==",
|
||||
"requires": {
|
||||
"@algolia/cache-browser-local-storage": "4.10.5",
|
||||
"@algolia/cache-common": "4.10.5",
|
||||
"@algolia/cache-in-memory": "4.10.5",
|
||||
"@algolia/client-account": "4.10.5",
|
||||
"@algolia/client-analytics": "4.10.5",
|
||||
"@algolia/client-common": "4.10.5",
|
||||
"@algolia/client-personalization": "4.10.5",
|
||||
"@algolia/client-search": "4.10.5",
|
||||
"@algolia/logger-common": "4.10.5",
|
||||
"@algolia/logger-console": "4.10.5",
|
||||
"@algolia/requester-browser-xhr": "4.10.5",
|
||||
"@algolia/requester-common": "4.10.5",
|
||||
"@algolia/requester-node-http": "4.10.5",
|
||||
"@algolia/transporter": "4.10.5"
|
||||
}
|
||||
},
|
||||
"anser": {
|
||||
"version": "1.4.9",
|
||||
"resolved": "https://registry.npmjs.org/anser/-/anser-1.4.9.tgz",
|
||||
@@ -695,9 +901,9 @@
|
||||
}
|
||||
},
|
||||
"available-typed-arrays": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz",
|
||||
"integrity": "sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA=="
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
|
||||
"integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw=="
|
||||
},
|
||||
"axe-core": {
|
||||
"version": "4.3.3",
|
||||
@@ -1069,9 +1275,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
|
||||
},
|
||||
"cosmiconfig": {
|
||||
"version": "7.0.1",
|
||||
@@ -2391,9 +2597,9 @@
|
||||
}
|
||||
},
|
||||
"hast-util-to-html": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-8.0.1.tgz",
|
||||
"integrity": "sha512-S1mTqXvWVGIxrWw0xOHHvmevwCBFTRGNvXWsjE32IyEAlMhbMkK+ZuP6CAqkQ6Vb7swrehaHpfXHEI6voGDh0w==",
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-8.0.2.tgz",
|
||||
"integrity": "sha512-ipLhUTMyyJi9F/LXaNDG9BrRdshP6obCfmUZYbE/+T639IdzqAOkKN4DyrEyID0gbb+rsC3PKf0XlviZwzomhw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/hast": "^2.0.0",
|
||||
@@ -2802,11 +3008,11 @@
|
||||
}
|
||||
},
|
||||
"is-typed-array": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.7.tgz",
|
||||
"integrity": "sha512-VxlpTBGknhQ3o7YiVjIhdLU6+oD8dPz/79vvvH4F+S/c8608UCVa9fgDpa1kZgFoUST2DCgacc70UszKgzKuvA==",
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz",
|
||||
"integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==",
|
||||
"requires": {
|
||||
"available-typed-arrays": "^1.0.4",
|
||||
"available-typed-arrays": "^1.0.5",
|
||||
"call-bind": "^1.0.2",
|
||||
"es-abstract": "^1.18.5",
|
||||
"foreach": "^2.0.5",
|
||||
@@ -3458,16 +3664,20 @@
|
||||
"dev": true
|
||||
},
|
||||
"next": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-11.1.0.tgz",
|
||||
"integrity": "sha512-GHBk/c7Wyr6YbFRFZF37I0X7HKzkHHI8pur/loyXo5AIE8wdkbGPGO0ds3vNAO6f8AxZAKGCRYtAzoGlVLoifA==",
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-11.1.1.tgz",
|
||||
"integrity": "sha512-vfLJDkwAHsZUho5R1K4w49nfYhftUMWNmeNSjCtulOvnRBuEFb7ROyRZOQk7f29rMz02eLQrPZ9yiAmPsexL2g==",
|
||||
"requires": {
|
||||
"@babel/runtime": "7.12.5",
|
||||
"@babel/runtime": "7.15.3",
|
||||
"@hapi/accept": "5.0.2",
|
||||
"@next/env": "11.1.0",
|
||||
"@next/polyfill-module": "11.1.0",
|
||||
"@next/react-dev-overlay": "11.1.0",
|
||||
"@next/react-refresh-utils": "11.1.0",
|
||||
"@next/env": "11.1.1",
|
||||
"@next/polyfill-module": "11.1.1",
|
||||
"@next/react-dev-overlay": "11.1.1",
|
||||
"@next/react-refresh-utils": "11.1.1",
|
||||
"@next/swc-darwin-arm64": "11.1.1",
|
||||
"@next/swc-darwin-x64": "11.1.1",
|
||||
"@next/swc-linux-x64-gnu": "11.1.1",
|
||||
"@next/swc-win32-x64-msvc": "11.1.1",
|
||||
"@node-rs/helper": "1.2.1",
|
||||
"assert": "2.0.0",
|
||||
"ast-types": "0.13.2",
|
||||
@@ -3509,11 +3719,19 @@
|
||||
"timers-browserify": "2.0.12",
|
||||
"tty-browserify": "0.0.1",
|
||||
"use-subscription": "1.5.1",
|
||||
"util": "0.12.3",
|
||||
"util": "0.12.4",
|
||||
"vm-browserify": "1.1.2",
|
||||
"watchpack": "2.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.3.tgz",
|
||||
"integrity": "sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.2.15",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.15.tgz",
|
||||
@@ -4128,6 +4346,11 @@
|
||||
"integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==",
|
||||
"dev": true
|
||||
},
|
||||
"preact": {
|
||||
"version": "10.5.14",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.5.14.tgz",
|
||||
"integrity": "sha512-KojoltCrshZ099ksUZ2OQKfbH66uquFoxHSbnwKbTJHeQNvx42EmC7wQVWNuDt6vC5s3nudRHFtKbpY4ijKlaQ=="
|
||||
},
|
||||
"prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
@@ -4557,9 +4780,9 @@
|
||||
}
|
||||
},
|
||||
"remark-html": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/remark-html/-/remark-html-14.0.0.tgz",
|
||||
"integrity": "sha512-ISQjSlOI3Hb99REjDz0cAhPJVJZDednsj4GNj4Ve7DEZdEXhVPOzBvym0Di+1K3p/RmKXqSw0r02JDmtATh6Dw==",
|
||||
"version": "14.0.1",
|
||||
"resolved": "https://registry.npmjs.org/remark-html/-/remark-html-14.0.1.tgz",
|
||||
"integrity": "sha512-a/x5bTlFrkwYkz43zuJIk0m0IuS5Rx8zLztGwdzmAdUj0Hsi4C4nkJ8gTQRNXY/ET/gMrqQORMMI0arRItq/aQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/mdast": "^3.0.0",
|
||||
@@ -5457,9 +5680,9 @@
|
||||
}
|
||||
},
|
||||
"util": {
|
||||
"version": "0.12.3",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.12.3.tgz",
|
||||
"integrity": "sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==",
|
||||
"version": "0.12.4",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz",
|
||||
"integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"is-arguments": "^1.0.4",
|
||||
@@ -5580,16 +5803,16 @@
|
||||
}
|
||||
},
|
||||
"which-typed-array": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.6.tgz",
|
||||
"integrity": "sha512-DdY984dGD5sQ7Tf+x1CkXzdg85b9uEel6nr4UkFg1LoE9OXv3uRuZhe5CoWdawhGACeFpEZXH8fFLQnDhbpm/Q==",
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz",
|
||||
"integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==",
|
||||
"requires": {
|
||||
"available-typed-arrays": "^1.0.4",
|
||||
"available-typed-arrays": "^1.0.5",
|
||||
"call-bind": "^1.0.2",
|
||||
"es-abstract": "^1.18.5",
|
||||
"foreach": "^2.0.5",
|
||||
"has-tostringtag": "^1.0.0",
|
||||
"is-typed-array": "^1.1.6"
|
||||
"is-typed-array": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"word-wrap": {
|
||||
|
||||
@@ -3,20 +3,22 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"changelog": "node scripts/copy-changelog.js",
|
||||
"dev": "npm run changelog && next dev",
|
||||
"build": "npm run changelog && next build",
|
||||
"build": "npm run changelog && npm run sitemap && next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
"lint": "next lint",
|
||||
"changelog": "node scripts/copy-changelog.js",
|
||||
"sitemap": "node scripts/generate-sitemap.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docsearch/js": "^3.0.0-alpha.40",
|
||||
"@headlessui/react": "^1.4.0",
|
||||
"@heroicons/react": "^1.0.4",
|
||||
"date-fns": "^2.23.0",
|
||||
"gray-matter": "^4.0.3",
|
||||
"i18next": "^20.4.0",
|
||||
"i18next-browser-languagedetector": "^6.1.2",
|
||||
"next": "11.1.0",
|
||||
"next": "11.1.1",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-i18next": "^11.11.4",
|
||||
@@ -33,7 +35,7 @@
|
||||
"postcss": "^8.3.6",
|
||||
"postcss-nested": "^5.0.6",
|
||||
"remark": "14.0.1",
|
||||
"remark-html": "14.0.0",
|
||||
"remark-html": "14.0.1",
|
||||
"tailwindcss": "^2.2.8",
|
||||
"typescript": "4.4.2"
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ class MyDocument extends Document {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Html lang="en" data-theme="dark">
|
||||
<Head>
|
||||
<meta charSet="UTF-8" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
|
||||
@@ -16,8 +16,8 @@ export default function Home({ content }: any) {
|
||||
<OtherMeta image={`/assets/frontmatter-preview.png`} />
|
||||
|
||||
<Layout>
|
||||
<div className="max-w-7xl mx-auto py-8 px-4 sm:px-6 lg:py-24 lg:px-8 divide-y-2 divide-vulcan-200">
|
||||
<div className="py-8 space-y-2 md:space-y-5 ">
|
||||
<div className="max-w-7xl mx-auto py-8 px-4 sm:px-6 lg:px-8 divide-y-2 divide-vulcan-200">
|
||||
<div className="pb-8 space-y-2 md:space-y-5 ">
|
||||
<h1 className="text-5xl tracking-tight font-extrabold sm:leading-none lg:text-5xl xl:text-6xl">{strings(`changelog_page_title`)}</h1>
|
||||
|
||||
<p className="mt-3 text-base text-whisper-700 sm:mt-5 sm:text-xl lg:text-lg xl:text-xl">{strings(`changelog_page_description`)}</p>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { NextPage } from 'next';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Description, OtherMeta, Title } from '../components/Meta';
|
||||
import { CTA } from '../components/Page/CTA';
|
||||
import { Features } from '../components/Page/Features';
|
||||
import { Generators } from '../components/Page/Generators';
|
||||
import { Layout } from '../components/Page/Layout';
|
||||
import { CTA, Features, Generators, Hero, Layout } from '../components/Page';
|
||||
import { Extension } from '../constants/extension';
|
||||
|
||||
const Home: NextPage = () => {
|
||||
const { t: strings } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title value={Extension.home} />
|
||||
@@ -19,6 +19,35 @@ const Home: NextPage = () => {
|
||||
|
||||
<Generators />
|
||||
|
||||
<Hero
|
||||
view={"left"}
|
||||
title={strings(`hero_title`)}
|
||||
description={(
|
||||
<>
|
||||
<p className="my-6 text-base text-whisper-700 sm:mt-5 sm:text-xl lg:text-lg xl:text-xl">
|
||||
{strings(`hero_description`)}
|
||||
</p>
|
||||
<p className="my-6 text-base text-whisper-700 sm:mt-5 sm:text-xl lg:text-lg xl:text-xl">
|
||||
{strings(`hero_description_second`)}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
imgSrc={"/assets/dashboard.png"}
|
||||
imgAlt={"Front Matter CMS editor dashboard of your static site content"}
|
||||
link={`/docs/getting-started`}
|
||||
linkText={strings(`hero_button_primary`)} />
|
||||
|
||||
|
||||
<Hero
|
||||
view={"right"}
|
||||
title={strings(`hero_media_title`)}
|
||||
description={strings(`hero_media_description`)}
|
||||
imgSrc={"/assets/media.png"}
|
||||
imgAlt={"Front Matter CMS - media management was never easier in VS Code"}
|
||||
link={`/docs/dashboard`}
|
||||
linkText={strings(`hero_media_button_primary`)}
|
||||
className={`-mt-12 sm:-mt-16`} />
|
||||
|
||||
<Features />
|
||||
</Layout>
|
||||
</>
|
||||
|
||||
@@ -2,7 +2,34 @@ import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Description, OtherMeta, Title } from '../../components/Meta';
|
||||
import { Layout } from '../../components/Page/Layout';
|
||||
import { Extension } from '../../constants/extension';
|
||||
import showcases from '../../showcases.json';
|
||||
import Image from 'next/image';
|
||||
|
||||
const shimmer = (w: number, h: number) => `
|
||||
<svg width="${w}" height="${h}" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="g">
|
||||
<stop stop-color="#0e131f" offset="20%" />
|
||||
<stop stop-color="#222733" offset="50%" />
|
||||
<stop stop-color="#0e131f" offset="70%" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="${w}" height="${h}" fill="#333" />
|
||||
<rect id="r" width="${w}" height="${h}" fill="url(#g)" />
|
||||
<animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
|
||||
</svg>`;
|
||||
|
||||
const toBase64 = (str: string) =>
|
||||
typeof window === 'undefined'
|
||||
? Buffer.from(str).toString('base64')
|
||||
: window.btoa(str);
|
||||
|
||||
const sortTitle = (a: { title: string }, b: { title: string }) => {
|
||||
if (a.title < b.title) return -1;
|
||||
if (a.title > b.title) return 1;
|
||||
return 0;
|
||||
};
|
||||
|
||||
export default function Home({ showcases }: any) {
|
||||
const { t: strings } = useTranslation();
|
||||
@@ -14,18 +41,27 @@ export default function Home({ showcases }: any) {
|
||||
<OtherMeta image={`/assets/frontmatter-preview.png`} />
|
||||
|
||||
<Layout>
|
||||
<div className="max-w-7xl mx-auto py-8 px-4 sm:px-6 lg:py-24 lg:px-8 divide-y-2 divide-vulcan-200">
|
||||
<div className="py-8 space-y-2 md:space-y-5 ">
|
||||
<div className="max-w-7xl mx-auto py-8 px-4 sm:px-6 lg:px-8 divide-y-2 divide-vulcan-200">
|
||||
<div className="pb-8 space-y-2 md:space-y-5 ">
|
||||
<h1 className="text-5xl tracking-tight font-extrabold sm:leading-none lg:text-5xl xl:text-6xl">{strings(`showcase_title`)}</h1>
|
||||
|
||||
<p className="mt-3 text-base text-whisper-700 sm:mt-5 sm:text-xl lg:text-lg xl:text-xl">{strings(`showcase_description`)}</p>
|
||||
</div>
|
||||
|
||||
<div className={`py-8 grid grid-cols-1 lg:grid-cols-2 gap-8`}>
|
||||
{showcases.filter((showcase: any) => showcase.image).map((showcase: any) => (
|
||||
{showcases.filter((showcase: any) => showcase.image).sort(sortTitle).map((showcase: any) => (
|
||||
<a key={showcase.title} className="group space-y-2 md:space-y-5 relative" href={showcase.link} title={showcase.title} rel={`noopener noreferrer`}>
|
||||
<figure className={`relative h-64 lg:h-[25rem] overflow-hidden grayscale group-hover:grayscale-0`}>
|
||||
<img className={`w-full object-cover`} src={`/showcases/${showcase.image}`} alt={showcase.title} loading={`lazy`} />
|
||||
<Image
|
||||
className={`w-full object-cover object-left-top`}
|
||||
src={`/showcases/${showcase.image}`}
|
||||
alt={showcase.title}
|
||||
loading={`lazy`}
|
||||
placeholder="blur"
|
||||
blurDataURL={`data:image/svg+xml;base64,${toBase64(shimmer(592, 400))}`}
|
||||
width={592}
|
||||
height={400}
|
||||
/>
|
||||
</figure>
|
||||
|
||||
<h2 className="text-3xl tracking-tight font-extrabold sm:leading-none lg:text-3xl xl:text-4xl">{showcase.title}</h2>
|
||||
@@ -34,6 +70,12 @@ export default function Home({ showcases }: any) {
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="">
|
||||
<div className="mt-8 text-sm">
|
||||
<p>Want to add your site to our showcase? Great, open a showcase on <a className="text-teal-500 hover:text-teal-900" href={Extension.showcaseLink} target="_blank" rel="noopener noreferrer">Github</a>!</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
</>
|
||||
|
||||
BIN
docs/public/assets/delete-media.png
Normal file
|
After Width: | Height: | Size: 1018 KiB |
BIN
docs/public/assets/lightbox.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
docs/public/assets/media.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
6
docs/public/assets/sponsors/powered-by-vercel.svg
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
docs/public/assets/upload-media.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 263 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 265 KiB |
BIN
docs/public/showcases/m365princess.png
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
docs/public/showcases/www.michaelfasani.com.png
Normal file
|
After Width: | Height: | Size: 365 KiB |
65
docs/scripts/generate-sitemap.js
Normal file
@@ -0,0 +1,65 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const matter = require('gray-matter');
|
||||
|
||||
(async () => {
|
||||
const baseUrl = `https://frontmatter.codes`;
|
||||
|
||||
// Ignore Next.js specific files (e.g., _app.js) and API routes.
|
||||
let pages = fs
|
||||
.readdirSync(path.join(__dirname, '../pages'))
|
||||
.filter((staticPage) => {
|
||||
return ![
|
||||
"_app.tsx",
|
||||
"_document.tsx",
|
||||
"_error.tsx",
|
||||
"sitemap.xml.tsx",
|
||||
"index.tsx",
|
||||
"404",
|
||||
"api"
|
||||
].includes(staticPage);
|
||||
})
|
||||
.map((staticPagePath) => ({
|
||||
lastModified: new Date().toISOString(),
|
||||
slug: `${baseUrl}/${staticPagePath}`
|
||||
}));
|
||||
|
||||
const mdDir = path.join(process.cwd(), 'content');
|
||||
const mdFiles = fs.readdirSync(path.join(mdDir, 'docs')).filter(f => f.endsWith(`.md`));
|
||||
const mdPages = mdFiles.map((fileName) => {
|
||||
const fullPath = path.join(mdDir, 'docs', `${fileName}`)
|
||||
const fileContents = fs.readFileSync(fullPath, 'utf8');
|
||||
const { data } = matter(fileContents);
|
||||
return {
|
||||
lastModified: data["lastmod"] || new Date().toISOString(),
|
||||
slug: `${baseUrl}/docs/${data['slug'] || fileName.split('.').slice(0, -1).join('.')}`
|
||||
};
|
||||
});
|
||||
|
||||
pages = [...pages, ...mdPages];
|
||||
|
||||
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>${baseUrl}</loc>
|
||||
<lastmod>${new Date().toISOString()}</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
${pages
|
||||
.map((page) => {
|
||||
return `
|
||||
<url>
|
||||
<loc>${`${page.slug}`}</loc>
|
||||
<lastmod>${page.lastModified}</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
`;
|
||||
})
|
||||
.join('')}
|
||||
</urlset>
|
||||
`;
|
||||
|
||||
fs.writeFileSync('public/sitemap.xml', sitemap);
|
||||
})();
|
||||
16
docs/search/frontmatter.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"index_name": "documentation",
|
||||
"start_urls": [
|
||||
"https://frontmatter.codes/docs/"
|
||||
],
|
||||
"stop_urls": [],
|
||||
"selectors": {
|
||||
"lvl0": ".markdown h1",
|
||||
"lvl1": ".markdown h2",
|
||||
"lvl2": ".markdown h3",
|
||||
"lvl3": ".markdown h4",
|
||||
"lvl4": ".markdown h5",
|
||||
"lvl5": ".markdown h6",
|
||||
"text": ".markdown p, .markdown li, .markdown pre"
|
||||
}
|
||||
}
|
||||
@@ -19,5 +19,19 @@
|
||||
"description": "Squarl is created with Next.js and Tailwind. Managed by Front Matter.",
|
||||
"image": "squarl.png",
|
||||
"generator": "Next.js"
|
||||
},
|
||||
{
|
||||
"title": "M365Princess.com",
|
||||
"link": "https://m365princess.com",
|
||||
"description": "Personal website/blog of Luise Freese created with a Hugo template. Managed with Front Matter.",
|
||||
"image": "m365princess.png",
|
||||
"generator": "Hugo"
|
||||
},
|
||||
{
|
||||
"title": "MichaelFasani.com",
|
||||
"link": "https://www.michaelfasani.com",
|
||||
"description": "Personal blog of Michael Fasani, content stored as markdown in GitHub, built with Gatsby, managed with Front Matter.",
|
||||
"image": "www.michaelfasani.com.png",
|
||||
"generator": "Gatsby"
|
||||
}
|
||||
]
|
||||
@@ -2,6 +2,8 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@import "@docsearch/css";
|
||||
|
||||
html, body {height: 100%;}
|
||||
|
||||
.changelog {
|
||||
@@ -82,4 +84,56 @@ html, body {height: 100%;}
|
||||
pre {
|
||||
@apply p-4 my-4 bg-vulcan-200;
|
||||
}
|
||||
}
|
||||
|
||||
/* DocSearch */
|
||||
html[data-theme=dark] {
|
||||
--docsearch-text-color: #f3eff5;
|
||||
--docsearch-logo-color: #f3eff5;
|
||||
|
||||
/* Search button */
|
||||
--docsearch-searchbox-background: #2c313d;
|
||||
--docsearch-searchbox-focus-background: #222733;
|
||||
--docsearch-muted-color: #cbc7cd;
|
||||
|
||||
/* Overlap background */
|
||||
--docsearch-container-background: rgba(54,59,71, 0.8);
|
||||
|
||||
/* Modal */
|
||||
--docsearch-modal-background: #0e131f;
|
||||
--docsearch-modal-shadow: none;
|
||||
--docsearch-footer-background: #0e131f;
|
||||
|
||||
/* Colors */
|
||||
--docsearch-primary-color: #15c2cb;
|
||||
|
||||
--docsearch-hit-color: #bec3c9;
|
||||
--docsearch-hit-background: #090a11;
|
||||
|
||||
--docsearch-hit-shadow: none;
|
||||
/* --docsearch-key-gradient: none; */
|
||||
--docsearch-key-shadow: none;
|
||||
--docsearch-footer-shadow: none;
|
||||
}
|
||||
|
||||
.DocSearch-Screen-Icon svg {
|
||||
@apply mx-auto;
|
||||
}
|
||||
|
||||
.DocSearch-Button {
|
||||
@apply border border-transparent !important;
|
||||
border-radius: 0;
|
||||
box-shadow: none !important;
|
||||
|
||||
&:hover {
|
||||
@apply border border-whisper-50 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.DocSearch-Button-Keys, .DocSearch-Button-Key {
|
||||
@apply top-auto;
|
||||
}
|
||||
|
||||
.DocSearch--active {
|
||||
overflow: auto !important;
|
||||
}
|
||||
315
package-lock.json
generated
@@ -1,9 +1,145 @@
|
||||
{
|
||||
"name": "vscode-front-matter",
|
||||
"version": "3.0.2",
|
||||
"name": "vscode-front-matter-beta",
|
||||
"version": "3.1.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@algolia/autocomplete-core": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.2.2.tgz",
|
||||
"integrity": "sha512-JOQaURze45qVa8OOFDh+ozj2a/ObSRsVyz6Zd0aiBeej+RSTqrr1hDVpGNbbXYLW26G5ujuc9QIdH+rBHn95nw==",
|
||||
"requires": {
|
||||
"@algolia/autocomplete-shared": "1.2.2"
|
||||
}
|
||||
},
|
||||
"@algolia/autocomplete-preset-algolia": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.2.2.tgz",
|
||||
"integrity": "sha512-AZkh+bAMaJDzMZTelFOXJTJqkp5VPGH8W3n0B+Ggce7DdozlMRsDLguKTCQAkZ0dJ1EbBPyFL5ztL/JImB137Q==",
|
||||
"requires": {
|
||||
"@algolia/autocomplete-shared": "1.2.2"
|
||||
}
|
||||
},
|
||||
"@algolia/autocomplete-shared": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.2.2.tgz",
|
||||
"integrity": "sha512-mLTl7d2C1xVVazHt/bqh9EE/u2lbp5YOxLDdcjILXmUqOs5HH1D4SuySblXaQG1uf28FhTqMGp35qE5wJQnqAw=="
|
||||
},
|
||||
"@algolia/cache-browser-local-storage": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.10.5.tgz",
|
||||
"integrity": "sha512-cfX2rEKOtuuljcGI5DMDHClwZHdDqd2nT2Ohsc8aHtBiz6bUxKVyIqxr2gaC6tU8AgPtrTVBzcxCA+UavXpKww==",
|
||||
"requires": {
|
||||
"@algolia/cache-common": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@algolia/cache-common": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.10.5.tgz",
|
||||
"integrity": "sha512-1mClwdmTHll+OnHkG+yeRoFM17kSxDs4qXkjf6rNZhoZGXDvfYLy3YcZ1FX4Kyz0DJv8aroq5RYGBDsWkHj6Tw=="
|
||||
},
|
||||
"@algolia/cache-in-memory": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.10.5.tgz",
|
||||
"integrity": "sha512-+ciQnfIGi5wjMk02XhEY8fmy2pzy+oY1nIIfu8LBOglaSipCRAtjk6WhHc7/KIbXPiYzIwuDbM2K1+YOwSGjwA==",
|
||||
"requires": {
|
||||
"@algolia/cache-common": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@algolia/client-account": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.10.5.tgz",
|
||||
"integrity": "sha512-I9UkSS2glXm7RBZYZIALjBMmXSQbw/fI/djPcBHxiwXIheNIlqIFl2SNPkvihpPF979BSkzjqdJNRPhE1vku3Q==",
|
||||
"requires": {
|
||||
"@algolia/client-common": "4.10.5",
|
||||
"@algolia/client-search": "4.10.5",
|
||||
"@algolia/transporter": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@algolia/client-analytics": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.10.5.tgz",
|
||||
"integrity": "sha512-h2owwJSkovPxzc+xIsjY1pMl0gj+jdVwP9rcnGjlaTY2fqHbSLrR9yvGyyr6305LvTppxsQnfAbRdE/5Z3eFxw==",
|
||||
"requires": {
|
||||
"@algolia/client-common": "4.10.5",
|
||||
"@algolia/client-search": "4.10.5",
|
||||
"@algolia/requester-common": "4.10.5",
|
||||
"@algolia/transporter": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@algolia/client-common": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.10.5.tgz",
|
||||
"integrity": "sha512-21FAvIai5qm8DVmZHm2Gp4LssQ/a0nWwMchAx+1hIRj1TX7OcdW6oZDPyZ8asQdvTtK7rStQrRnD8a95SCUnzA==",
|
||||
"requires": {
|
||||
"@algolia/requester-common": "4.10.5",
|
||||
"@algolia/transporter": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@algolia/client-personalization": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.10.5.tgz",
|
||||
"integrity": "sha512-nH+IyFKBi8tCyzGOanJTbXC5t4dspSovX3+ABfmwKWUYllYzmiQNFUadpb3qo+MLA3jFx5IwBesjneN6dD5o3w==",
|
||||
"requires": {
|
||||
"@algolia/client-common": "4.10.5",
|
||||
"@algolia/requester-common": "4.10.5",
|
||||
"@algolia/transporter": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@algolia/client-search": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.10.5.tgz",
|
||||
"integrity": "sha512-1eQFMz9uodrc5OM+9HeT+hHcfR1E1AsgFWXwyJ9Q3xejA2c1c4eObGgOgC9ZoshuHHdptaTN1m3rexqAxXRDBg==",
|
||||
"requires": {
|
||||
"@algolia/client-common": "4.10.5",
|
||||
"@algolia/requester-common": "4.10.5",
|
||||
"@algolia/transporter": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@algolia/logger-common": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.10.5.tgz",
|
||||
"integrity": "sha512-gRJo9zt1UYP4k3woEmZm4iuEBIQd/FrArIsjzsL/b+ihNoOqIxZKTSuGFU4UUZOEhvmxDReiA4gzvQXG+TMTmA=="
|
||||
},
|
||||
"@algolia/logger-console": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.10.5.tgz",
|
||||
"integrity": "sha512-4WfIbn4253EDU12u9UiYvz+QTvAXDv39mKNg9xSoMCjKE5szcQxfcSczw2byc6pYhahOJ9PmxPBfs1doqsdTKQ==",
|
||||
"requires": {
|
||||
"@algolia/logger-common": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@algolia/requester-browser-xhr": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.10.5.tgz",
|
||||
"integrity": "sha512-53/MURQEqtK+bGdfq4ITSPwTh5hnADU99qzvpAINGQveUFNSFGERipJxHjTJjIrjFz3vxj5kKwjtxDnU6ygO9g==",
|
||||
"requires": {
|
||||
"@algolia/requester-common": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@algolia/requester-common": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.10.5.tgz",
|
||||
"integrity": "sha512-UkVa1Oyuj6NPiAEt5ZvrbVopEv1m/mKqjs40KLB+dvfZnNcj+9Fry4Oxnt15HMy/HLORXsx4UwcthAvBuOXE9Q=="
|
||||
},
|
||||
"@algolia/requester-node-http": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.10.5.tgz",
|
||||
"integrity": "sha512-aNEKVKXL4fiiC+bS7yJwAHdxln81ieBwY3tsMCtM4zF9f5KwCzY2OtN4WKEZa5AAADVcghSAUdyjs4AcGUlO5w==",
|
||||
"requires": {
|
||||
"@algolia/requester-common": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@algolia/transporter": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.10.5.tgz",
|
||||
"integrity": "sha512-F8DLkmIlvCoMwSCZA3FKHtmdjH3o5clbt0pi2ktFStVNpC6ZDmY307HcK619bKP5xW6h8sVJhcvrLB775D2cyA==",
|
||||
"requires": {
|
||||
"@algolia/cache-common": "4.10.5",
|
||||
"@algolia/logger-common": "4.10.5",
|
||||
"@algolia/requester-common": "4.10.5"
|
||||
}
|
||||
},
|
||||
"@babel/code-frame": {
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
|
||||
@@ -48,10 +184,44 @@
|
||||
"lit-element": "^2.5.1"
|
||||
}
|
||||
},
|
||||
"@docsearch/css": {
|
||||
"version": "3.0.0-alpha.40",
|
||||
"resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.0.0-alpha.40.tgz",
|
||||
"integrity": "sha512-PrOTPgJMl+Iji1zOH0+J0PEDMriJ1teGxbgll7o4h8JrvJW6sJGqQw7/bLW7enWiFaxbJMK76w1yyPNLFHV7Qg=="
|
||||
},
|
||||
"@docsearch/js": {
|
||||
"version": "3.0.0-alpha.40",
|
||||
"resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.0.0-alpha.40.tgz",
|
||||
"integrity": "sha512-0ysRM0jk1KAbw/QsHPHIKoa7OeCm2Mwz0JgcPnhWRvvA28dzP+f6OIsL6eGu3VJR043tH9OrvVf/FnvLtTtZtw==",
|
||||
"requires": {
|
||||
"@docsearch/react": "3.0.0-alpha.40",
|
||||
"preact": "^10.0.0"
|
||||
}
|
||||
},
|
||||
"@docsearch/react": {
|
||||
"version": "3.0.0-alpha.40",
|
||||
"resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.0.0-alpha.40.tgz",
|
||||
"integrity": "sha512-aKxnu7sgpP1R7jtgOV/pZdJEHXx6Ts+jnS9U/ejSUS2BMUpwQI5SA3oLs1BA5TA9kIViJ5E+rrjh0VsbcsJ6sQ==",
|
||||
"requires": {
|
||||
"@algolia/autocomplete-core": "1.2.2",
|
||||
"@algolia/autocomplete-preset-algolia": "1.2.2",
|
||||
"@docsearch/css": "3.0.0-alpha.40",
|
||||
"algoliasearch": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"@estruyf/vscode": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@estruyf/vscode/-/vscode-0.0.2.tgz",
|
||||
"integrity": "sha512-Qb2UGiARR0Pxeknjzwq925kxyxWMsASIcCOfwOJGZed7EPhDLc6Zd1v6AReYpkQFyxSUjBVg38dT7dn5bh19fQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/vscode-webview": "1.57.0"
|
||||
}
|
||||
},
|
||||
"@headlessui/react": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.4.0.tgz",
|
||||
"integrity": "sha512-C+FmBVF6YGvqcEI5fa2dfVbEaXr2RGR6Kw1E5HXIISIZEfsrH/yuCgsjWw5nlRF9vbCxmQ/EKs64GAdKeb8gCw==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.4.1.tgz",
|
||||
"integrity": "sha512-gL6Ns5xQM57cZBzX6IVv6L7nsam8rDEpRhs5fg28SN64ikfmuuMgunc+Rw5C1cMScnvFM+cz32ueVrlSFEVlSg==",
|
||||
"dev": true
|
||||
},
|
||||
"@heroicons/react": {
|
||||
@@ -92,6 +262,15 @@
|
||||
"fastq": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"@tailwindcss/forms": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.3.3.tgz",
|
||||
"integrity": "sha512-U8Fi/gq4mSuaLyLtFISwuDYzPB73YzgozjxOIHsK6NXgg/IWD1FLaHbFlWmurAMyy98O+ao74ksdQefsquBV1Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mini-svg-data-uri": "^1.2.3"
|
||||
}
|
||||
},
|
||||
"@types/anymatch": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz",
|
||||
@@ -246,6 +425,12 @@
|
||||
"integrity": "sha512-C/jZ35OT5k/rsJyAK8mS1kM++vMcm89oSWegkzxRCvHllIq0cToZAkIDs6eCY4SKrvik3nrhELizyLcM0onbQA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/vscode-webview": {
|
||||
"version": "1.57.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/vscode-webview/-/vscode-webview-1.57.0.tgz",
|
||||
"integrity": "sha512-x3Cb/SMa1IwRHfSvKaZDZOTh4cNoG505c3NjTqGlMC082m++x/ETUmtYniDsw6SSmYzZXO8KBNhYxR0+VqymqA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/webpack": {
|
||||
"version": "4.41.25",
|
||||
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.25.tgz",
|
||||
@@ -535,6 +720,27 @@
|
||||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||
"dev": true
|
||||
},
|
||||
"algoliasearch": {
|
||||
"version": "4.10.5",
|
||||
"resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.10.5.tgz",
|
||||
"integrity": "sha512-KmH2XkiN+8FxhND4nWFbQDkIoU6g2OjfeU9kIv4Lb+EiOOs3Gpp7jvd+JnatsCisAZsnWQdjd7zVlW7I/85QvQ==",
|
||||
"requires": {
|
||||
"@algolia/cache-browser-local-storage": "4.10.5",
|
||||
"@algolia/cache-common": "4.10.5",
|
||||
"@algolia/cache-in-memory": "4.10.5",
|
||||
"@algolia/client-account": "4.10.5",
|
||||
"@algolia/client-analytics": "4.10.5",
|
||||
"@algolia/client-common": "4.10.5",
|
||||
"@algolia/client-personalization": "4.10.5",
|
||||
"@algolia/client-search": "4.10.5",
|
||||
"@algolia/logger-common": "4.10.5",
|
||||
"@algolia/logger-console": "4.10.5",
|
||||
"@algolia/requester-browser-xhr": "4.10.5",
|
||||
"@algolia/requester-common": "4.10.5",
|
||||
"@algolia/requester-node-http": "4.10.5",
|
||||
"@algolia/transporter": "4.10.5"
|
||||
}
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
@@ -666,6 +872,12 @@
|
||||
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
|
||||
"dev": true
|
||||
},
|
||||
"attr-accept": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
|
||||
"integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==",
|
||||
"dev": true
|
||||
},
|
||||
"autoprefixer": {
|
||||
"version": "10.3.2",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.3.2.tgz",
|
||||
@@ -979,6 +1191,17 @@
|
||||
"ssri": "^6.0.1",
|
||||
"unique-filename": "^1.1.1",
|
||||
"y18n": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"rimraf": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cache-base": {
|
||||
@@ -1271,6 +1494,17 @@
|
||||
"mkdirp": "^0.5.1",
|
||||
"rimraf": "^2.5.4",
|
||||
"run-queue": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"rimraf": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"copy-descriptor": {
|
||||
@@ -2098,6 +2332,23 @@
|
||||
"integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==",
|
||||
"dev": true
|
||||
},
|
||||
"file-selector": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.2.4.tgz",
|
||||
"integrity": "sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^2.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
@@ -2456,6 +2707,12 @@
|
||||
"strip-bom-string": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"hamt_plus": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz",
|
||||
"integrity": "sha1-4hwlKWjH4zsg9qGwlM2FeHomVgE=",
|
||||
"dev": true
|
||||
},
|
||||
"has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
@@ -3615,6 +3872,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"mini-svg-data-uri": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.3.3.tgz",
|
||||
"integrity": "sha512-+fA2oRcR1dJI/7ITmeQJDrYWks0wodlOz0pAEhKYJ2IVc1z0AnwJUsKY2fzFmPAM3Jo9J0rBx8JAA9QQSJ5PuA==",
|
||||
"dev": true
|
||||
},
|
||||
"minimalistic-assert": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||
@@ -3716,6 +3979,17 @@
|
||||
"mkdirp": "^0.5.1",
|
||||
"rimraf": "^2.5.4",
|
||||
"run-queue": "^1.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"rimraf": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
@@ -4344,6 +4618,11 @@
|
||||
"integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==",
|
||||
"dev": true
|
||||
},
|
||||
"preact": {
|
||||
"version": "10.5.14",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.5.14.tgz",
|
||||
"integrity": "sha512-KojoltCrshZ099ksUZ2OQKfbH66uquFoxHSbnwKbTJHeQNvx42EmC7wQVWNuDt6vC5s3nudRHFtKbpY4ijKlaQ=="
|
||||
},
|
||||
"pretty-error": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz",
|
||||
@@ -4540,6 +4819,17 @@
|
||||
"scheduler": "^0.20.1"
|
||||
}
|
||||
},
|
||||
"react-dropzone": {
|
||||
"version": "11.3.4",
|
||||
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-11.3.4.tgz",
|
||||
"integrity": "sha512-B1nzNRZ4F1cnrfEC0T6KXeBN1mCPinu4JCoTrp7NjB+442KSPxqfDrw41QIA2kAwlYs1+wj/0BTedeM5hc2+xw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"attr-accept": "^2.2.1",
|
||||
"file-selector": "^0.2.2",
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
@@ -4571,6 +4861,15 @@
|
||||
"picomatch": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"recoil": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/recoil/-/recoil-0.4.1.tgz",
|
||||
"integrity": "sha512-vp6KPwlHOjJ4bJofmdDchmgI9ilMTCoUisK8/WYLl8dThH7e7KmtZttiLgvDb2Em99dUfTEsk8vT8L1nUMgqXQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"hamt_plus": "1.0.2"
|
||||
}
|
||||
},
|
||||
"reduce-css-calc": {
|
||||
"version": "2.1.8",
|
||||
"resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz",
|
||||
@@ -4760,9 +5059,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||
"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"
|
||||
|
||||
88
package.json
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "vscode-front-matter",
|
||||
"name": "vscode-front-matter-beta",
|
||||
"displayName": "Front Matter",
|
||||
"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-teal-128x128.png",
|
||||
"version": "3.0.2",
|
||||
"version": "3.1.0",
|
||||
"preview": false,
|
||||
"publisher": "eliostruyf",
|
||||
"galleryBanner": {
|
||||
@@ -35,6 +35,11 @@
|
||||
"Taxonomy"
|
||||
],
|
||||
"license": "MIT",
|
||||
"author": "Elio Struyf <elio@struyfconsulting.be> (https://www.eliostruyf.com)",
|
||||
"homepage": "https://frontmatter.codes",
|
||||
"bugs": {
|
||||
"url": "https://github.com/estruyf/vscode-front-matter/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/estruyf/vscode-front-matter"
|
||||
@@ -88,27 +93,27 @@
|
||||
"frontMatter.content.autoUpdateDate": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Specify if you want to automatically update the modified date of your article/page."
|
||||
"markdownDescription": "Specify if you want to automatically update the modified date of your article/page. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.content.autoupdatedate)"
|
||||
},
|
||||
"frontMatter.content.fmHighlight": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Specify if you want to highlight the Front Matter in the Markdown file."
|
||||
"markdownDescription": "Specify if you want to highlight the Front Matter in the Markdown file. [Check in the docs](https://frontmatter.codes/docs/settings#frontMatter.content.fmhighlight)"
|
||||
},
|
||||
"frontMatter.content.folders": {
|
||||
"frontMatter.content.pageFolders": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"markdownDescription": "This array of folders defines where the extension can easily create new content by running the create article command."
|
||||
"markdownDescription": "This array of folders defines where the extension can retrieve or create new pages. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.content.pagefolders)"
|
||||
},
|
||||
"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."
|
||||
"markdownDescription": "Specify the folder name where all your assets are located. For instance in Hugo this is the `static` folder. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.content.publicfolder)"
|
||||
},
|
||||
"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."
|
||||
"markdownDescription": "Specify the path to a Node.js script to execute. The current file path will be provided as an argument. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.custom.scripts)"
|
||||
},
|
||||
"frontMatter.dashboard.openOnStart": {
|
||||
"type": [
|
||||
@@ -116,67 +121,67 @@
|
||||
"null"
|
||||
],
|
||||
"default": null,
|
||||
"description": "Specify if you want to open the dashboard when you start VS Code."
|
||||
"markdownDescription": "Specify if you want to open the dashboard when you start VS Code. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.dashboard.openonstart)"
|
||||
},
|
||||
"frontMatter.panel.freeform": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "Specifies if you want to allow yourself from entering unknown tags/categories in the tag picker (when enabled, you will have the option to store them afterwards). Default: true."
|
||||
"markdownDescription": "Specifies if you want to allow yourself from entering unknown tags/categories in the tag picker (when enabled, you will have the option to store them afterwards). Default: true. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.panel.freeform)"
|
||||
},
|
||||
"frontMatter.preview.host": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"markdownDescription": "Specify the host URL (example: http://localhost:1313) to be used when opening the preview."
|
||||
"markdownDescription": "Specify the host URL (example: http://localhost:1313) to be used when opening the preview. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.preview.host)"
|
||||
},
|
||||
"frontMatter.preview.pathName": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"markdownDescription": "Specify the path you want to add after the host and before your slug. This can be used for instance to include the year/month like: `yyyy/MM`. The date will be generated based on the article its date field value."
|
||||
"markdownDescription": "Specify the path you want to add after the host and before your slug. This can be used for instance to include the year/month like: `yyyy/MM`. The date will be generated based on the article its date field value. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.preview.pathname)"
|
||||
},
|
||||
"frontMatter.taxonomy.dateField": {
|
||||
"type": "string",
|
||||
"default": "date",
|
||||
"description": "Specifies the date field name to use in your Front Matter"
|
||||
"markdownDescription": "Specifies the date field name to use in your Front Matter. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.datefield)"
|
||||
},
|
||||
"frontMatter.taxonomy.modifiedField": {
|
||||
"type": "string",
|
||||
"default": "lastmod",
|
||||
"description": "Specifies the modified date field name to use in your Front Matter"
|
||||
"markdownDescription": "Specifies the modified date field name to use in your Front Matter. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.modifiedfield)"
|
||||
},
|
||||
"frontMatter.taxonomy.tags": {
|
||||
"type": "array",
|
||||
"description": "Specifies the tags which can be used in the Front Matter"
|
||||
"markdownDescription": "Specifies the tags which can be used in the Front Matter. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.tags)"
|
||||
},
|
||||
"frontMatter.taxonomy.categories": {
|
||||
"type": "array",
|
||||
"description": "Specifies the categories which can be used in the Front Matter"
|
||||
"markdownDescription": "Specifies the categories which can be used in the Front Matter. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.categories)"
|
||||
},
|
||||
"frontMatter.taxonomy.dateFormat": {
|
||||
"type": "string",
|
||||
"markdownDescription": "Specify the date format for your articles. Check [date-fns formating](https://date-fns.org/v2.0.1/docs/format) for more information."
|
||||
"markdownDescription": "Specify the date format for your articles. Check [date-fns formating](https://date-fns.org/v2.0.1/docs/format) for more information. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.dateformat)"
|
||||
},
|
||||
"frontMatter.taxonomy.slugPrefix": {
|
||||
"type": "string",
|
||||
"markdownDescription": "Specify a prefix for the slug"
|
||||
"markdownDescription": "Specify a prefix for the slug. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.slugprefix)"
|
||||
},
|
||||
"frontMatter.taxonomy.slugSuffix": {
|
||||
"type": "string",
|
||||
"markdownDescription": "Specify a suffix for the slug"
|
||||
"markdownDescription": "Specify a suffix for the slug. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.slugsuffix)"
|
||||
},
|
||||
"frontMatter.taxonomy.alignFilename": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "Align the filename with the new slug when it gets generated."
|
||||
"markdownDescription": "Align the filename with the new slug when it gets generated. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.alignfilename)"
|
||||
},
|
||||
"frontMatter.taxonomy.indentArrays": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "Specify if arrays in front matter are indented. Default: true."
|
||||
"markdownDescription": "Specify if arrays in front matter are indented. Default: true. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.indentarrays)"
|
||||
},
|
||||
"frontMatter.taxonomy.noPropertyValueQuotes": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"markdownDescription": "Specify the properties from which quotes need to be removed."
|
||||
"markdownDescription": "Specify the properties from which quotes need to be removed. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.nopropertyvaluequotes)"
|
||||
},
|
||||
"frontMatter.taxonomy.frontMatterType": {
|
||||
"type": "string",
|
||||
@@ -185,6 +190,7 @@
|
||||
"YAML",
|
||||
"TOML"
|
||||
],
|
||||
"markdownDescription": "Specify the type of Front Matter to use. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.frontmattertype)",
|
||||
"enumDescriptions": [
|
||||
"Specifies you want to use YAML markup for the front matter (default)",
|
||||
"Specifies you want to use TOML markup for the front matter"
|
||||
@@ -193,32 +199,32 @@
|
||||
"frontMatter.taxonomy.seoTitleLength": {
|
||||
"type": "number",
|
||||
"default": 60,
|
||||
"description": "Specifies the optimal title length for SEO (set to `-1` to turn it off)."
|
||||
"markdownDescription": "Specifies the optimal title length for SEO (set to `-1` to turn it off). [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.seotitlelength)"
|
||||
},
|
||||
"frontMatter.taxonomy.seoDescriptionLength": {
|
||||
"type": "number",
|
||||
"default": 160,
|
||||
"description": "Specifies the optimal description length for SEO (set to `-1` to turn it off)."
|
||||
"markdownDescription": "Specifies the optimal description length for SEO (set to `-1` to turn it off). [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.seodescriptionlength)"
|
||||
},
|
||||
"frontMatter.taxonomy.seoContentLengh": {
|
||||
"type": "number",
|
||||
"default": 1760,
|
||||
"description": "Specifies the optimal minimum length for your articles. Between 1,760 words – 2,400 is the absolute ideal article length for SEO in 2021. (set to `-1` to turn it off)."
|
||||
"markdownDescription": "Specifies the optimal minimum length for your articles. Between 1,760 words – 2,400 is the absolute ideal article length for SEO in 2021. (set to `-1` to turn it off). [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.seocontentlengh)"
|
||||
},
|
||||
"frontMatter.taxonomy.seoDescriptionField": {
|
||||
"type": "string",
|
||||
"default": "description",
|
||||
"description": "Specifies the name of the SEO description field for your page. Default is 'description'."
|
||||
"markdownDescription": "Specifies the name of the SEO description field for your page. Default is 'description'. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.seodescriptionfield)"
|
||||
},
|
||||
"frontMatter.templates.folder": {
|
||||
"type": "string",
|
||||
"default": ".templates",
|
||||
"description": "Specify the folder to use for your article templates."
|
||||
"markdownDescription": "Specify the folder to use for your article templates. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.templates.folder)"
|
||||
},
|
||||
"frontMatter.templates.prefix": {
|
||||
"type": "string",
|
||||
"default": "yyyy-MM-dd",
|
||||
"description": "Specify the prefix you want to add for your new article filenames."
|
||||
"markdownDescription": "Specify the prefix you want to add for your new article filenames. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.templates.prefix)"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -329,7 +335,7 @@
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.unregisterFolder",
|
||||
"when": "explorerResourceIsFolder && resourcePath in frontMatter.registeredFolders",
|
||||
"when": "explorerResourceIsFolder",
|
||||
"group": "Front Matter@3"
|
||||
}
|
||||
],
|
||||
@@ -382,14 +388,19 @@
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run clean && webpack --mode production",
|
||||
"build:ext": "webpack --mode development",
|
||||
"dev:ext": "webpack --mode development --watch",
|
||||
"build:ext": "npm run clean && webpack --mode development",
|
||||
"dev:ext": "npm run clean && webpack --mode development --watch",
|
||||
"test-compile": "tsc -p ./",
|
||||
"clean": "rm -rf dist"
|
||||
"clean": "rimraf dist",
|
||||
"start:site": "cd ./docs && npm run dev"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@bendera/vscode-webview-elements": "0.6.2",
|
||||
"@estruyf/vscode": "0.0.2",
|
||||
"@headlessui/react": "^1.4.1",
|
||||
"@heroicons/react": "1.0.4",
|
||||
"@iarna/toml": "2.2.3",
|
||||
"@tailwindcss/forms": "^0.3.3",
|
||||
"@types/glob": "7.1.3",
|
||||
"@types/js-yaml": "3.12.1",
|
||||
"@types/lodash.uniqby": "4.7.6",
|
||||
@@ -408,11 +419,15 @@
|
||||
"gray-matter": "4.0.2",
|
||||
"html-loader": "1.3.2",
|
||||
"html-webpack-plugin": "4.5.0",
|
||||
"lodash.uniqby": "4.7.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",
|
||||
"react-dropzone": "^11.3.4",
|
||||
"recoil": "^0.4.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"style-loader": "2.0.0",
|
||||
"tailwindcss": "^2.2.7",
|
||||
"ts-loader": "8.0.3",
|
||||
@@ -420,10 +435,9 @@
|
||||
"typescript": "4.0.2",
|
||||
"wc-react": "github:estruyf/wc-react",
|
||||
"webpack": "4.44.2",
|
||||
"webpack-cli": "3.3.12",
|
||||
"@headlessui/react": "1.4.0",
|
||||
"@heroicons/react": "1.0.4",
|
||||
"lodash.uniqby": "4.7.0"
|
||||
"webpack-cli": "3.3.12"
|
||||
},
|
||||
"dependencies": {}
|
||||
"dependencies": {
|
||||
"@docsearch/js": "^3.0.0-alpha.40"
|
||||
}
|
||||
}
|
||||
|
||||
20
scripts/beta-release.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const packageJson = require('../package.json');
|
||||
const version = packageJson.version.split('.');
|
||||
|
||||
packageJson.version = `${version[0]}.${version[1]}.${process.argv[process.argv.length-1].substr(0, 7)}`;
|
||||
packageJson.preview = true;
|
||||
packageJson.name = "vscode-front-matter-beta";
|
||||
packageJson.displayName = `${packageJson.displayName} BETA`;
|
||||
packageJson.description = `BETA Version of Front Matter. ${packageJson.description}`;
|
||||
packageJson.icon = "assets/frontmatter-beta.png";
|
||||
packageJson.homepage = "https://beta.frontmatter.codes";
|
||||
|
||||
console.log(packageJson.version);
|
||||
|
||||
fs.writeFileSync(path.join(path.resolve('.'), 'package.json'), JSON.stringify(packageJson, null, 2));
|
||||
|
||||
let readme = fs.readFileSync(path.join(__dirname, '../README.beta.md'), 'utf8');
|
||||
fs.writeFileSync(path.join(__dirname, '../README.md'), readme);
|
||||
7
scripts/main-release.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const packageJson = require('../package.json');
|
||||
packageJson.name = "vscode-front-matter";
|
||||
|
||||
fs.writeFileSync(path.join(path.resolve('.'), 'package.json'), JSON.stringify(packageJson, null, 2));
|
||||
@@ -1,25 +1,32 @@
|
||||
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 { basename, dirname, extname, join } from "path";
|
||||
import { existsSync, statSync, unlinkSync, writeFileSync } from "fs";
|
||||
import { commands, Uri, ViewColumn, Webview, WebviewPanel, window, workspace, env } 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 { COMMAND_NAME, EXTENSION_STATE_PAGES_VIEW } from '../constants/Extension';
|
||||
import { Template } from './Template';
|
||||
import { Notifications } from '../helpers/Notifications';
|
||||
import { Settings } from '../pagesView/models/Settings';
|
||||
import { Extension } from '../helpers/Extension';
|
||||
import { parseJSON } from 'date-fns';
|
||||
import { ViewType } from '../pagesView/state';
|
||||
import { WebviewHelper } from '@estruyf/vscode';
|
||||
import { MediaInfo, MediaPaths } from './../models/MediaPaths';
|
||||
import { decodeBase64Image } from '../helpers/decodeBase64Image';
|
||||
|
||||
|
||||
export class Dashboard {
|
||||
private static webview: WebviewPanel | null = null;
|
||||
private static isDisposed: boolean = true;
|
||||
private static media: MediaInfo[] = [];
|
||||
private static timers: { [folder: string]: any } = {};
|
||||
|
||||
/**
|
||||
* Init the dashboard
|
||||
@@ -113,14 +120,35 @@ export class Dashboard {
|
||||
case DashboardMessage.updateSetting:
|
||||
Dashboard.updateSetting(msg.data);
|
||||
break;
|
||||
case DashboardMessage.InitializeProject:
|
||||
case DashboardMessage.initializeProject:
|
||||
await commands.executeCommand(COMMAND_NAME.init, Dashboard.getSettings);
|
||||
break;
|
||||
case DashboardMessage.Reload:
|
||||
Dashboard.webview?.dispose();
|
||||
setTimeout(() => {
|
||||
Dashboard.open();
|
||||
}, 100);
|
||||
case DashboardMessage.reload:
|
||||
if (!Dashboard.isDisposed) {
|
||||
Dashboard.webview?.dispose();
|
||||
setTimeout(() => {
|
||||
Dashboard.open();
|
||||
}, 100);
|
||||
}
|
||||
break;
|
||||
case DashboardMessage.setPageViewType:
|
||||
Extension.getInstance().setState(EXTENSION_STATE_PAGES_VIEW, msg.data);
|
||||
break;
|
||||
case DashboardMessage.getMedia:
|
||||
Dashboard.getMedia(msg?.data?.page, msg?.data?.folder)
|
||||
break;
|
||||
case DashboardMessage.copyToClipboard:
|
||||
env.clipboard.writeText(msg.data);
|
||||
break;
|
||||
case DashboardMessage.refreshMedia:
|
||||
Dashboard.media = [];
|
||||
Dashboard.getMedia(0, msg?.data?.folder);
|
||||
break;
|
||||
case DashboardMessage.uploadMedia:
|
||||
Dashboard.saveFile(msg?.data);
|
||||
break;
|
||||
case DashboardMessage.deleteMedia:
|
||||
Dashboard.deleteFile(msg?.data);
|
||||
break;
|
||||
}
|
||||
});
|
||||
@@ -130,15 +158,23 @@ export class Dashboard {
|
||||
* Retrieve the settings for the dashboard
|
||||
*/
|
||||
private static async getSettings() {
|
||||
const ext = Extension.getInstance();
|
||||
const config = SettingsHelper.getConfig();
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
|
||||
Dashboard.postWebviewMessage({
|
||||
command: DashboardCommand.settings,
|
||||
data: {
|
||||
beta: ext.isBetaVersion(),
|
||||
wsFolder: wsFolder ? wsFolder.fsPath : '',
|
||||
staticFolder: config.get<string>(SETTINGS_CONTENT_STATIC_FOLDERS),
|
||||
folders: Folders.get(),
|
||||
initialized: await Template.isInitialized(),
|
||||
tags: SettingsHelper.getTaxonomy(TaxonomyType.Tag),
|
||||
categories: SettingsHelper.getTaxonomy(TaxonomyType.Category),
|
||||
openOnStart: SettingsHelper.getConfig().get(SETTINGS_DASHBOARD_OPENONSTART),
|
||||
versionInfo: Extension.getInstance().getVersion()
|
||||
openOnStart: config.get(SETTINGS_DASHBOARD_OPENONSTART),
|
||||
versionInfo: ext.getVersion(),
|
||||
pageViewType: await ext.getState<ViewType | undefined>(EXTENSION_STATE_PAGES_VIEW)
|
||||
} as Settings
|
||||
});
|
||||
}
|
||||
@@ -151,13 +187,98 @@ export class Dashboard {
|
||||
Dashboard.getSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all media files
|
||||
*/
|
||||
private static async getMedia(page: number = 0, folder: string = '') {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
const config = SettingsHelper.getConfig();
|
||||
const staticFolder = config.get<string>(SETTINGS_CONTENT_STATIC_FOLDERS);
|
||||
|
||||
if (Dashboard.media.length === 0) {
|
||||
const contentFolder = Folders.get();
|
||||
let allMedia: MediaInfo[] = [];
|
||||
|
||||
if (staticFolder) {
|
||||
const files = await workspace.findFiles(`${staticFolder || ""}/**/*`);
|
||||
const media = Dashboard.filterMedia(files);
|
||||
|
||||
allMedia = [...media];
|
||||
}
|
||||
|
||||
if (contentFolder && wsFolder) {
|
||||
for (let i = 0; i < contentFolder.length; i++) {
|
||||
const folder = contentFolder[i];
|
||||
const relFolderPath = folder.path.substring(wsFolder.fsPath.length + 1);
|
||||
const files = await workspace.findFiles(`${relFolderPath}/**/*`);
|
||||
const media = Dashboard.filterMedia(files);
|
||||
|
||||
allMedia = [...allMedia, ...media];
|
||||
}
|
||||
}
|
||||
|
||||
allMedia = allMedia.sort((a, b) => {
|
||||
if (b.fsPath < a.fsPath) {
|
||||
return -1;
|
||||
}
|
||||
if (b.fsPath > a.fsPath) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
Dashboard.media = Object.assign([], allMedia);
|
||||
}
|
||||
|
||||
// Filter the media
|
||||
let files: MediaInfo[] = Dashboard.media;
|
||||
if (folder) {
|
||||
files = files.filter(f => f.fsPath.includes(folder));
|
||||
}
|
||||
|
||||
// Retrieve the total after filtering and before the slicing happens
|
||||
const total = files.length;
|
||||
|
||||
// Get media set
|
||||
files = files.slice(page * 16, ((page + 1) * 16));
|
||||
files = files.map((file) => {
|
||||
try {
|
||||
return {
|
||||
...file,
|
||||
stats: statSync(file.fsPath)
|
||||
};
|
||||
} catch (e) {
|
||||
return {...file, stats: undefined};
|
||||
}
|
||||
}).filter(f => f.stats !== undefined);
|
||||
|
||||
const folders = [...new Set(Dashboard.media.map((file) => {
|
||||
let relFolderPath = wsFolder ? file.fsPath.substring(wsFolder.fsPath.length + 1) : file.fsPath;
|
||||
if (staticFolder && relFolderPath.startsWith(staticFolder)) {
|
||||
relFolderPath = relFolderPath.substring(staticFolder.length);
|
||||
}
|
||||
if (relFolderPath?.startsWith('/')) {
|
||||
relFolderPath = relFolderPath.substring(1);
|
||||
}
|
||||
return dirname(relFolderPath);
|
||||
}))];
|
||||
|
||||
Dashboard.postWebviewMessage({
|
||||
command: DashboardCommand.media,
|
||||
data: {
|
||||
media: files,
|
||||
total: total,
|
||||
folders
|
||||
} as MediaPaths
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 wsFolder = Folders.getWorkspaceFolder();
|
||||
|
||||
const descriptionField = config.get(SETTING_SEO_DESCRIPTION_FIELD) as string || "description";
|
||||
const dateField = config.get(SETTING_DATE_FIELD) as string || "date";
|
||||
@@ -177,10 +298,12 @@ export class Dashboard {
|
||||
const page: Page = {
|
||||
...article.data,
|
||||
// FrontMatter properties
|
||||
fmGroup: folder.title,
|
||||
fmFolder: folder.title,
|
||||
fmModified: file.mtime,
|
||||
fmFilePath: file.filePath,
|
||||
fmFileName: file.fileName,
|
||||
fmDraft: article?.data.draft ? "Draft" : "Published",
|
||||
fmYear: article?.data[dateField] ? parseJSON(article?.data[dateField]).getFullYear() : null,
|
||||
// Make sure these are always set
|
||||
title: article?.data.title,
|
||||
slug: article?.data.slug,
|
||||
@@ -189,16 +312,28 @@ export class Dashboard {
|
||||
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() || "";
|
||||
if (article?.data.preview && wsFolder) {
|
||||
const staticPath = join(wsFolder.fsPath, staticFolder || "", article?.data.preview);
|
||||
const contentFolderPath = join(dirname(file.filePath), article?.data.preview);
|
||||
|
||||
let previewUri = null;
|
||||
if (existsSync(staticPath)) {
|
||||
previewUri = Uri.file(staticPath);
|
||||
} else if (existsSync(contentFolderPath)) {
|
||||
previewUri = Uri.file(contentFolderPath);
|
||||
}
|
||||
|
||||
if (previewUri) {
|
||||
const preview = Dashboard.webview?.webview.asWebviewUri(previewUri);
|
||||
page.preview = preview?.toString() || "";
|
||||
} else {
|
||||
page.preview = "";
|
||||
}
|
||||
}
|
||||
|
||||
pages.push(page);
|
||||
}
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
Notifications.error(`File error: ${file.filePath} - ${error?.message || error}`);
|
||||
}
|
||||
}
|
||||
@@ -212,6 +347,84 @@ export class Dashboard {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the media files
|
||||
*/
|
||||
private static filterMedia(files: Uri[]) {
|
||||
return files.filter(file => {
|
||||
const ext = extname(file.fsPath);
|
||||
return ['.jpg', '.jpeg', '.png', '.gif', '.svg'].includes(ext);
|
||||
}).map((file) => ({
|
||||
fsPath: file.fsPath,
|
||||
vsPath: Dashboard.webview?.webview.asWebviewUri(file).toString(),
|
||||
stats: undefined
|
||||
} as MediaInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the dropped file in the current folder
|
||||
* @param fileData
|
||||
*/
|
||||
private static async saveFile({fileName, contents, folder}: { fileName: string; contents: string; folder: string | null }) {
|
||||
if (fileName && contents) {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
const config = SettingsHelper.getConfig();
|
||||
const staticFolder = config.get<string>(SETTINGS_CONTENT_STATIC_FOLDERS);
|
||||
const wsPath = wsFolder ? wsFolder.fsPath : "";
|
||||
let absFolderPath = join(wsPath, staticFolder || "", folder || "");
|
||||
|
||||
if (!existsSync(absFolderPath)) {
|
||||
absFolderPath = join(wsPath, folder || "");
|
||||
}
|
||||
|
||||
if (!existsSync(absFolderPath)) {
|
||||
Notifications.error(`We couldn't find your selected folder.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const staticPath = join(absFolderPath, fileName);
|
||||
const imgData = decodeBase64Image(contents);
|
||||
|
||||
if (imgData) {
|
||||
writeFileSync(staticPath, imgData.data);
|
||||
Notifications.info(`File ${fileName} uploaded to: ${staticFolder}/${folder}`);
|
||||
|
||||
const folderPath = `${staticFolder}/${folder}`;
|
||||
if (Dashboard.timers[folderPath]) {
|
||||
clearTimeout(Dashboard.timers[folderPath]);
|
||||
delete Dashboard.timers[folderPath];
|
||||
}
|
||||
|
||||
Dashboard.timers[folderPath] = setTimeout(() => {
|
||||
Dashboard.media = [];
|
||||
Dashboard.getMedia(0, folder || "");
|
||||
delete Dashboard.timers[folderPath];
|
||||
}, 500);
|
||||
} else {
|
||||
Notifications.error(`Something went wrong uploading ${fileName}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the selected file
|
||||
* @param fileData
|
||||
*/
|
||||
private static async deleteFile({ file, page, folder }: { file: string; page: number; folder: string | null; }) {
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
unlinkSync(file);
|
||||
|
||||
Dashboard.media = [];
|
||||
Dashboard.getMedia(page || 0, folder || "");
|
||||
} catch(err) {
|
||||
Notifications.error(`Something went wrong deleting ${basename(file)}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post data to the dashboard
|
||||
* @param msg
|
||||
@@ -227,7 +440,7 @@ export class Dashboard {
|
||||
private static getWebviewContent(webView: Webview, extensionPath: Uri): string {
|
||||
const scriptUri = webView.asWebviewUri(Uri.joinPath(extensionPath, 'dist', 'pages.js'));
|
||||
|
||||
const nonce = getNonce();
|
||||
const nonce = WebviewHelper.getNonce();
|
||||
|
||||
const version = Extension.getInstance().getVersion();
|
||||
|
||||
@@ -243,7 +456,7 @@ export class Dashboard {
|
||||
<body style="width:100%;height:100%;margin:0;padding:0;overflow:hidden" class="bg-gray-100 text-vulcan-500 dark:bg-vulcan-500 dark:text-whisper-500">
|
||||
<div id="app" style="width:100%;height:100%;margin:0;padding:0;" ${version.usedVersion ? "" : `data-showWelcome="true"`}></div>
|
||||
|
||||
<img style="display:none" src="https://api.visitorbadge.io/api/combined?user=estruyf&repo=frontmatter-usage&countColor=%23263759" alt="Daily usage" />
|
||||
<img style="display:none" src="https://api.visitorbadge.io/api/combined?user=estruyf&repo=frontmatter-usage&countColor=%23263759&slug=${`dashboard-${version.installedVersion}`}" alt="Daily usage" />
|
||||
|
||||
<script nonce="${nonce}" src="${scriptUri}"></script>
|
||||
</body>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SETTINGS_CONTENT_PAGE_FOLDERS } from './../constants/settings';
|
||||
import { commands, Uri, workspace, window } from "vscode";
|
||||
import { SETTINGS_CONTENT_FOLDERS } from "../constants";
|
||||
import { basename, join } from "path";
|
||||
import { ContentFolder, FileInfo, FolderInfo } from "../models";
|
||||
import uniqBy = require("lodash.uniqby");
|
||||
@@ -8,6 +8,8 @@ import { Notifications } from "../helpers/Notifications";
|
||||
import { CONTEXT } from "../constants/context";
|
||||
import { SettingsHelper } from "../helpers";
|
||||
|
||||
export const WORKSPACE_PLACEHOLDER = `[[workspace]]`;
|
||||
|
||||
export class Folders {
|
||||
|
||||
/**
|
||||
@@ -38,7 +40,7 @@ export class Folders {
|
||||
|
||||
const location = folders.find(f => f.title === selectedFolder);
|
||||
if (location) {
|
||||
const folderPath = Folders.getFolderPath(Uri.file(location.fsPath));
|
||||
const folderPath = Folders.getFolderPath(Uri.file(location.path));
|
||||
if (folderPath) {
|
||||
Template.create(folderPath);
|
||||
}
|
||||
@@ -55,7 +57,7 @@ export class Folders {
|
||||
|
||||
let folders = Folders.get();
|
||||
|
||||
const exists = folders.find(f => f.paths.includes(folder.fsPath) || f.paths.includes(wslPath));
|
||||
const exists = folders.find(f => f.path.includes(folder.fsPath) || f.path.includes(wslPath));
|
||||
|
||||
if (exists) {
|
||||
Notifications.warning(`Folder is already registered`);
|
||||
@@ -70,17 +72,14 @@ export class Folders {
|
||||
|
||||
folders.push({
|
||||
title: folderName,
|
||||
fsPath: folder.fsPath,
|
||||
paths: folder.fsPath === wslPath ? [folder.fsPath] : [folder.fsPath, wslPath]
|
||||
path: folder.fsPath
|
||||
} as ContentFolder);
|
||||
|
||||
folders = uniqBy(folders, f => f.fsPath);
|
||||
folders = uniqBy(folders, f => f.path);
|
||||
await Folders.update(folders);
|
||||
|
||||
Notifications.info(`Folder registered`);
|
||||
}
|
||||
|
||||
Folders.updateVsCodeCtx();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,23 +89,9 @@ export class Folders {
|
||||
public static async unregister(folder: Uri) {
|
||||
if (folder && folder.path) {
|
||||
let folders = Folders.get();
|
||||
folders = folders.filter(f => f.fsPath !== folder.fsPath);
|
||||
folders = folders.filter(f => f.path !== folder.fsPath);
|
||||
await Folders.update(folders);
|
||||
}
|
||||
|
||||
Folders.updateVsCodeCtx();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the registered folders context
|
||||
*/
|
||||
public static updateVsCodeCtx() {
|
||||
const folders = Folders.get();
|
||||
let allFolders: string[] = [];
|
||||
for (const folder of folders) {
|
||||
allFolders = [...allFolders, ...folder.paths]
|
||||
}
|
||||
commands.executeCommand('setContext', CONTEXT.registeredFolders, allFolders);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,7 +144,7 @@ export class Folders {
|
||||
for (const folder of folders) {
|
||||
try {
|
||||
const projectName = Folders.getProjectFolderName();
|
||||
let projectStart = folder.fsPath.split(projectName).pop();
|
||||
let projectStart = folder.path.split(projectName).pop();
|
||||
if (projectStart) {
|
||||
projectStart = projectStart.replace(/\\/g, '/');
|
||||
projectStart = projectStart.startsWith('/') ? projectStart.substr(1) : projectStart;
|
||||
@@ -211,10 +196,15 @@ export class Folders {
|
||||
* Get the folder settings
|
||||
* @returns
|
||||
*/
|
||||
public static get() {
|
||||
public static get(): ContentFolder[] {
|
||||
const config = SettingsHelper.getConfig();
|
||||
const folders: ContentFolder[] = config.get(SETTINGS_CONTENT_FOLDERS) as ContentFolder[];
|
||||
return folders;
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
const folders: ContentFolder[] = config.get(SETTINGS_CONTENT_PAGE_FOLDERS) as ContentFolder[];
|
||||
|
||||
return folders.map(folder => ({
|
||||
title: folder.title,
|
||||
path: Folders.absWsFolder(folder, wsFolder)
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,6 +213,33 @@ export class Folders {
|
||||
*/
|
||||
private static async update(folders: ContentFolder[]) {
|
||||
const config = SettingsHelper.getConfig();
|
||||
await config.update(SETTINGS_CONTENT_FOLDERS, folders);
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
await config.update(SETTINGS_CONTENT_PAGE_FOLDERS, folders.map(folder => ({ title: folder.title, path: Folders.relWsFolder(folder, wsFolder) })));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the absolute URL for the workspace
|
||||
* @param folder
|
||||
* @param wsFolder
|
||||
* @returns
|
||||
*/
|
||||
private static absWsFolder(folder: ContentFolder, wsFolder?: Uri) {
|
||||
const isWindows = process.platform === 'win32';
|
||||
let absPath = folder.path.replace(WORKSPACE_PLACEHOLDER, wsFolder?.fsPath || "");
|
||||
absPath = isWindows ? absPath.split('/').join('\\') : absPath;
|
||||
return absPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate relative folder path
|
||||
* @param folder
|
||||
* @param wsFolder
|
||||
* @returns
|
||||
*/
|
||||
private static relWsFolder(folder: ContentFolder, wsFolder?: Uri) {
|
||||
const isWindows = process.platform === 'win32';
|
||||
let absPath = folder.path.replace(wsFolder?.fsPath || "", WORKSPACE_PLACEHOLDER);
|
||||
absPath = isWindows ? absPath.split('\\').join('/') : absPath;
|
||||
return absPath;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { join } from "path";
|
||||
import * as fs from "fs";
|
||||
import { Notifications } from "../helpers/Notifications";
|
||||
import { Template } from "./Template";
|
||||
import { Folders } from "./Folders";
|
||||
|
||||
export class Project {
|
||||
|
||||
@@ -52,14 +53,13 @@ categories: []
|
||||
*/
|
||||
public static templatePath() {
|
||||
const folder = Template.getSettings();
|
||||
const workspaceFolders = workspace.workspaceFolders;
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
|
||||
if (!folder || !workspaceFolders || workspaceFolders.length === 0) {
|
||||
if (!folder || !wsFolder) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const workspaceFolder = workspaceFolders[0];
|
||||
const templatePath = Uri.file(join(workspaceFolder.uri.fsPath, folder));
|
||||
const templatePath = Uri.file(join(wsFolder.fsPath, folder));
|
||||
return templatePath;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import { Article } from '.';
|
||||
import { Notifications } from '../helpers/Notifications';
|
||||
import { CONTEXT } from '../constants/context';
|
||||
import { Project } from './Project';
|
||||
import { Folders } from './Folders';
|
||||
|
||||
export class Template {
|
||||
|
||||
@@ -24,15 +25,14 @@ export class Template {
|
||||
* Check if the project is already initialized
|
||||
*/
|
||||
public static async isInitialized() {
|
||||
const workspaceFolders = vscode.workspace.workspaceFolders;
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
const folder = Template.getSettings();
|
||||
|
||||
if (!folder || !workspaceFolders || workspaceFolders.length === 0) {
|
||||
if (!folder || !wsFolder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const workspaceFolder = workspaceFolders[0];
|
||||
const templatePath = vscode.Uri.file(path.join(workspaceFolder.uri.fsPath, folder));
|
||||
const templatePath = vscode.Uri.file(path.join(wsFolder.fsPath, folder));
|
||||
|
||||
try {
|
||||
await vscode.workspace.fs.stat(templatePath);
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
const extensionName = "frontMatter";
|
||||
|
||||
export const EXTENSION_ID = 'eliostruyf.vscode-front-matter';
|
||||
export const EXTENSION_BETA_ID = 'eliostruyf.vscode-front-matter-beta';
|
||||
|
||||
export const EXTENSION_STATE_VERSION = 'frontMatter:Version';
|
||||
export const EXTENSION_STATE_PAGES_VIEW = 'frontMatter:Pages:ViewType';
|
||||
|
||||
export const getCommandName = (command: string) => {
|
||||
return `${extensionName}.${command}`;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
export const CONTEXT = {
|
||||
canInit: "frontMatterCanInit",
|
||||
canOpenPreview: "frontMatterCanOpenPreview",
|
||||
canOpenDashboard: "frontMatterCanOpenDashboard",
|
||||
registeredFolders: 'frontMatter.registeredFolders'
|
||||
canOpenDashboard: "frontMatterCanOpenDashboard"
|
||||
};
|
||||
@@ -33,8 +33,13 @@ export const SETTING_PREVIEW_PATHNAME = "preview.pathName";
|
||||
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_PAGE_FOLDERS = "content.pageFolders";
|
||||
export const SETTINGS_CONTENT_STATIC_FOLDERS = "content.publicFolder";
|
||||
export const SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT = "content.fmHighlight";
|
||||
|
||||
export const SETTINGS_DASHBOARD_OPENONSTART = "dashboard.openOnStart";
|
||||
export const SETTINGS_DASHBOARD_OPENONSTART = "dashboard.openOnStart";
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export const SETTINGS_CONTENT_FOLDERS = "content.folders";
|
||||
@@ -5,12 +5,14 @@ import { Folders } from './commands/Folders';
|
||||
import { Preview } from './commands/Preview';
|
||||
import { Project } from './commands/Project';
|
||||
import { Template } from './commands/Template';
|
||||
import { COMMAND_NAME } from './constants/Extension';
|
||||
import { COMMAND_NAME, EXTENSION_BETA_ID, EXTENSION_ID } from './constants/Extension';
|
||||
import { TaxonomyType } from './models';
|
||||
import { MarkdownFoldingProvider } from './providers/MarkdownFoldingProvider';
|
||||
import { TagType } from './viewpanel/TagType';
|
||||
import { ExplorerView } from './webview/ExplorerView';
|
||||
import { Extension } from './helpers/Extension';
|
||||
import { basename } from 'path';
|
||||
import { Notifications } from './helpers/Notifications';
|
||||
|
||||
let frontMatterStatusBar: vscode.StatusBarItem;
|
||||
let statusDebouncer: { (fnc: any, time: number): void; };
|
||||
@@ -19,8 +21,16 @@ let collection: vscode.DiagnosticCollection;
|
||||
|
||||
const mdSelector: vscode.DocumentSelector = { language: 'markdown', scheme: 'file' };
|
||||
|
||||
export async function activate({ subscriptions, extensionUri, extensionPath, globalState }: vscode.ExtensionContext) {
|
||||
const extension = Extension.getInstance(globalState, extensionUri);
|
||||
export async function activate(context: vscode.ExtensionContext) {
|
||||
const { subscriptions, extensionUri, extensionPath } = context;
|
||||
|
||||
const extension = Extension.getInstance(context);
|
||||
|
||||
if (!extension.checkIfExtensionCanRun()) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
extension.migrateSettings()
|
||||
|
||||
collection = vscode.languages.createDiagnosticCollection('frontMatter');
|
||||
|
||||
@@ -97,8 +107,6 @@ export async function activate({ subscriptions, extensionUri, extensionPath, glo
|
||||
|
||||
const createContent = vscode.commands.registerCommand(COMMAND_NAME.createContent, Folders.create);
|
||||
|
||||
Folders.updateVsCodeCtx();
|
||||
|
||||
// Initialize command
|
||||
Template.init();
|
||||
const projectInit = vscode.commands.registerCommand(COMMAND_NAME.init, async (cb: Function) => {
|
||||
@@ -118,7 +126,6 @@ export async function activate({ subscriptions, extensionUri, extensionPath, glo
|
||||
vscode.workspace.onDidChangeConfiguration(() => {
|
||||
Template.init();
|
||||
Preview.init();
|
||||
Folders.updateVsCodeCtx();
|
||||
|
||||
const exView = ExplorerView.getInstance();
|
||||
exView.getSettings();
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
import { Memento, extensions, Uri } from "vscode";
|
||||
import { EXTENSION_ID, EXTENSION_STATE_VERSION } from "../constants/Extension";
|
||||
import { basename } from "path";
|
||||
import { extensions, Uri, ExtensionContext } from "vscode";
|
||||
import { Folders, WORKSPACE_PLACEHOLDER } from "../commands/Folders";
|
||||
import { SETTINGS_CONTENT_FOLDERS, SETTINGS_CONTENT_PAGE_FOLDERS } from "../constants";
|
||||
import { EXTENSION_BETA_ID, EXTENSION_ID, EXTENSION_STATE_VERSION } from "../constants/Extension";
|
||||
import { Notifications } from "./Notifications";
|
||||
import { SettingsHelper } from "./SettingsHelper";
|
||||
|
||||
|
||||
export class Extension {
|
||||
private static instance: Extension;
|
||||
|
||||
private constructor(private globalState: Memento, private extPath: Uri) {}
|
||||
private constructor(private ctx: ExtensionContext) {}
|
||||
|
||||
/**
|
||||
* Creates the singleton instance for the panel
|
||||
* @param extPath
|
||||
*/
|
||||
public static getInstance(globalState?: Memento, extPath?: Uri): Extension {
|
||||
if (!Extension.instance && globalState && extPath) {
|
||||
Extension.instance = new Extension(globalState, extPath);
|
||||
public static getInstance(ctx?: ExtensionContext): Extension {
|
||||
if (!Extension.instance && ctx) {
|
||||
Extension.instance = new Extension(ctx);
|
||||
}
|
||||
|
||||
return Extension.instance;
|
||||
@@ -23,9 +28,13 @@ export class Extension {
|
||||
* Get the current version information for the extension
|
||||
*/
|
||||
public getVersion(): { usedVersion: string | undefined, installedVersion: string } {
|
||||
const frontMatter = extensions.getExtension(EXTENSION_ID)!;
|
||||
const installedVersion = frontMatter.packageJSON.version;
|
||||
const usedVersion = this.globalState.get<string>(EXTENSION_STATE_VERSION);
|
||||
const frontMatter = extensions.getExtension(this.isBetaVersion() ? EXTENSION_BETA_ID : EXTENSION_ID)!;
|
||||
let installedVersion = frontMatter.packageJSON.version;
|
||||
const usedVersion = this.ctx.globalState.get<string>(EXTENSION_STATE_VERSION);
|
||||
|
||||
if (this.isBetaVersion()) {
|
||||
installedVersion = `${installedVersion}-beta`;
|
||||
}
|
||||
|
||||
if (!usedVersion) {
|
||||
this.setVersion(installedVersion);
|
||||
@@ -41,13 +50,57 @@ export class Extension {
|
||||
* Set the current version information for the extension
|
||||
*/
|
||||
public setVersion(installedVersion: string): void {
|
||||
this.globalState.update(EXTENSION_STATE_VERSION, installedVersion);
|
||||
this.ctx.globalState.update(EXTENSION_STATE_VERSION, installedVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the extension
|
||||
*/
|
||||
public get extensionPath(): Uri {
|
||||
return this.extPath;
|
||||
return this.ctx.extensionUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate old settings to new settings
|
||||
*/
|
||||
public async migrateSettings(): Promise<void> {
|
||||
const config = SettingsHelper.getConfig();
|
||||
const folders = config.get<any>(SETTINGS_CONTENT_FOLDERS);
|
||||
if (folders && folders.length > 0) {
|
||||
const workspace = Folders.getWorkspaceFolder();
|
||||
const projectFolder = basename(workspace?.fsPath || "");
|
||||
|
||||
const paths = folders.map((folder: any) => ({
|
||||
title: folder.title,
|
||||
path: `${WORKSPACE_PLACEHOLDER}${folder.fsPath.split(projectFolder).slice(1).join('')}`.split('\\').join('/')
|
||||
}));
|
||||
|
||||
await config.update(`${SETTINGS_CONTENT_PAGE_FOLDERS}`, paths);
|
||||
}
|
||||
}
|
||||
|
||||
public async setState(propKey: string, propValue: string): Promise<void> {
|
||||
await this.ctx.globalState.update(propKey, propValue);
|
||||
}
|
||||
|
||||
public async getState<T>(propKey: string): Promise<T | undefined> {
|
||||
return await this.ctx.globalState.get(propKey);
|
||||
}
|
||||
|
||||
public isBetaVersion() {
|
||||
return basename(this.ctx.globalStorageUri.fsPath) === EXTENSION_BETA_ID;
|
||||
}
|
||||
|
||||
public checkIfExtensionCanRun() {
|
||||
if (this.isBetaVersion()) {
|
||||
const mainVersionInstalled = extensions.getExtension(EXTENSION_ID);
|
||||
|
||||
if (mainVersionInstalled) {
|
||||
Notifications.error(`Front Matter BETA cannot be used while the main version is installed. Please ensure that you have only over version installed.`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
6
src/helpers/GroupBy.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export const groupBy = (array: any[], key: string) => {
|
||||
return array.reduce((result, currentValue) => {
|
||||
(result[currentValue[key]] = result[currentValue[key]] || []).push(currentValue);
|
||||
return result;
|
||||
}, {});
|
||||
};
|
||||
13
src/helpers/decodeBase64Image.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export const decodeBase64Image = (dataString: string) => {
|
||||
const matches = dataString.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/);
|
||||
let response: any = {};
|
||||
|
||||
if (matches?.length !== 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
response.type = matches[1];
|
||||
response.data = Buffer.from(matches[2], 'base64');
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
export interface ContentFolder {
|
||||
title: string;
|
||||
fsPath: string;
|
||||
paths: string[];
|
||||
path: string;
|
||||
}
|
||||
13
src/models/MediaPaths.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Stats } from "fs";
|
||||
|
||||
export interface MediaPaths {
|
||||
media: MediaInfo[];
|
||||
total: number;
|
||||
folders: string[];
|
||||
}
|
||||
|
||||
export interface MediaInfo {
|
||||
fsPath: string;
|
||||
vsPath: string | undefined;
|
||||
stats: Stats | undefined;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './ContentFolder';
|
||||
export * from './PanelSettings';
|
||||
export * from './TaxonomyType';
|
||||
export * from './VersionInfo';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export enum DashboardCommand {
|
||||
loading = "loading",
|
||||
pages = "pages",
|
||||
settings = "settings"
|
||||
settings = "settings",
|
||||
media = "media",
|
||||
}
|
||||
@@ -4,6 +4,12 @@ export enum DashboardMessage {
|
||||
getTheme = 'getTheme',
|
||||
createContent = 'createContent',
|
||||
updateSetting = 'updateSetting',
|
||||
InitializeProject = 'InitializeProject',
|
||||
Reload = 'Reload',
|
||||
initializeProject = 'initializeProject',
|
||||
reload = 'reload',
|
||||
setPageViewType = 'setPageViewType',
|
||||
getMedia = 'getMedia',
|
||||
copyToClipboard = 'copyToClipboard',
|
||||
refreshMedia = 'refreshMedia',
|
||||
uploadMedia = 'uploadMedia',
|
||||
deleteMedia = 'deleteMedia',
|
||||
}
|
||||
36
src/pagesView/components/Contents/Contents.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import * as React from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { Page } from '../../models';
|
||||
import { SettingsSelector } from '../../state';
|
||||
import { Header } from '../Header';
|
||||
import { Overview } from './Overview';
|
||||
import { Spinner } from '../Spinner';
|
||||
import { SponsorMsg } from '../SponsorMsg';
|
||||
|
||||
export interface IContentsProps {
|
||||
pages: Page[];
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
export const Contents: React.FunctionComponent<IContentsProps> = ({pages, loading}: React.PropsWithChildren<IContentsProps>) => {
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
|
||||
const pageFolders = [...new Set(pages.map(page => page.fmFolder))];
|
||||
|
||||
return (
|
||||
<main className={`h-full w-full`}>
|
||||
<div className="flex flex-col h-full overflow-auto">
|
||||
<Header
|
||||
folders={pageFolders}
|
||||
totalPages={pages.length}
|
||||
settings={settings} />
|
||||
|
||||
<div className="w-full flex-grow max-w-7xl mx-auto py-6 px-4">
|
||||
{ loading ? <Spinner /> : <Overview pages={pages} settings={settings} /> }
|
||||
</div>
|
||||
|
||||
<SponsorMsg beta={settings?.beta} version={settings?.versionInfo} />
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
70
src/pagesView/components/Contents/Item.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import * as React from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { MarkdownIcon } from '../../../viewpanel/components/Icons/MarkdownIcon';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { Page } from '../../models/Page';
|
||||
import { ViewSelector, ViewType } from '../../state';
|
||||
import { DateField } from '../DateField';
|
||||
import { Status } from '../Status';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
|
||||
export interface IItemProps extends Page {}
|
||||
|
||||
export const Item: React.FunctionComponent<IItemProps> = ({ fmFilePath, date, title, draft, description, preview }: React.PropsWithChildren<IItemProps>) => {
|
||||
const view = useRecoilValue(ViewSelector);
|
||||
|
||||
const openFile = () => {
|
||||
Messenger.send(DashboardMessage.openFile, fmFilePath);
|
||||
};
|
||||
|
||||
if (view === ViewType.Grid) {
|
||||
return (
|
||||
<li className="relative">
|
||||
<button className={`group cursor-pointer flex flex-wrap items-start content-start h-full w-full bg-gray-50 dark:bg-vulcan-200 text-vulcan-500 dark:text-whisper-500 text-left overflow-hidden shadow-md hover:shadow-xl dark:hover:bg-vulcan-100`}
|
||||
onClick={openFile}>
|
||||
<div className="relative h-36 w-full overflow-hidden border-b border-gray-100 dark:border-vulcan-100 dark:group-hover:border-vulcan-200">
|
||||
{
|
||||
preview ? (
|
||||
<img src={`${preview}`} alt={title} className="absolute inset-0 h-full w-full object-cover" loading="lazy" />
|
||||
) : (
|
||||
<div className={`flex items-center justify-center bg-whisper-500 dark:bg-vulcan-200 dark:group-hover:bg-vulcan-100`}>
|
||||
<MarkdownIcon className={`h-32 text-vulcan-100 dark:text-whisper-100`} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className="p-4 w-full">
|
||||
<div className={`flex justify-between items-center`}>
|
||||
<Status draft={!!draft} />
|
||||
|
||||
<DateField value={date} />
|
||||
</div>
|
||||
|
||||
<h2 className="mt-2 mb-2 font-bold">{title}</h2>
|
||||
|
||||
<p className="text-xs text-vulcan-200 dark:text-whisper-800">{description}</p>
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
} else if (view === ViewType.List) {
|
||||
return (
|
||||
<li className="relative">
|
||||
<button className={`px-5 cursor-pointer w-full text-left grid grid-cols-12 gap-x-4 sm:gap-x-6 xl:gap-x-8 py-2 border-b border-gray-300 hover:bg-gray-200 dark:border-vulcan-50 dark:hover:bg-vulcan-50 hover:bg-opacity-70`} onClick={openFile}>
|
||||
<div className="col-span-8 font-bold truncate">
|
||||
{title}
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<DateField value={date} />
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Status draft={!!draft} />
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
37
src/pagesView/components/Contents/List.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import * as React from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { ViewSelector, ViewType } from '../../state';
|
||||
|
||||
export interface IListProps {}
|
||||
|
||||
export const List: React.FunctionComponent<IListProps> = ({children}: React.PropsWithChildren<IListProps>) => {
|
||||
const view = useRecoilValue(ViewSelector);
|
||||
|
||||
let className = '';
|
||||
if (view === ViewType.Grid) {
|
||||
className = `grid grid-cols-2 gap-x-4 gap-y-8 sm:grid-cols-3 sm:gap-x-6 lg:grid-cols-4 xl:gap-x-8`;
|
||||
} else if (view === ViewType.List) {
|
||||
className = `-mx-4`;
|
||||
}
|
||||
|
||||
return (
|
||||
<ul role="list" className={className}>
|
||||
{view === ViewType.List && (
|
||||
<li className="px-5 relative uppercase text-vulcan-100 dark:text-whisper-900 py-2 border-b border-vulcan-50">
|
||||
<div className={`grid grid-cols-12 gap-x-4 sm:gap-x-6 xl:gap-x-8`}>
|
||||
<div className="col-span-8">
|
||||
Title
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
Date
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
Status
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
{children}
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
88
src/pagesView/components/Contents/Overview.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import { Disclosure } from '@headlessui/react';
|
||||
import { ChevronRightIcon } from '@heroicons/react/solid';
|
||||
import * as React from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { groupBy } from '../../../helpers/GroupBy';
|
||||
import { FrontMatterIcon } from '../../../viewpanel/components/Icons/FrontMatterIcon';
|
||||
import { GroupOption } from '../../constants/GroupOption';
|
||||
import { Page } from '../../models/Page';
|
||||
import { Settings } from '../../models/Settings';
|
||||
import { GroupingSelector } from '../../state';
|
||||
import { Item } from './Item';
|
||||
import { List } from './List';
|
||||
|
||||
export interface IOverviewProps {
|
||||
pages: Page[];
|
||||
settings: Settings | null;
|
||||
}
|
||||
|
||||
export const Overview: React.FunctionComponent<IOverviewProps> = ({pages, settings}: React.PropsWithChildren<IOverviewProps>) => {
|
||||
const grouping = useRecoilValue(GroupingSelector);
|
||||
|
||||
if (!pages || !pages.length) {
|
||||
return (
|
||||
<div className={`flex items-center justify-center h-full`}>
|
||||
<div className={`max-w-xl text-center`}>
|
||||
<FrontMatterIcon className={`text-vulcan-300 dark:text-whisper-800 h-32 mx-auto opacity-90 mb-8`} />
|
||||
{
|
||||
settings && settings?.folders?.length > 0 ? (
|
||||
<p className={`text-xl font-medium`}>No Markdown to show</p>
|
||||
) : (
|
||||
<>
|
||||
<p className={`text-lg font-medium`}>Make sure you registered a content folder in your project to let Front Matter find the contents.</p>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (grouping !== GroupOption.none) {
|
||||
const groupedPages = groupBy(pages, grouping === GroupOption.Year ? 'fmYear' : 'fmDraft');
|
||||
let groupKeys = Object.keys(groupedPages);
|
||||
|
||||
if (grouping === GroupOption.Year) {
|
||||
groupKeys = groupKeys.sort((a, b) => { return parseInt(b) - parseInt(a) });
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
groupKeys.map((groupId, idx) => (
|
||||
<Disclosure key={groupId} as={`div`} className={`w-full`} defaultOpen>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Disclosure.Button className={`mb-4 ${idx !== 0 ? "mt-8" : ""}`}>
|
||||
<h2 className={`text-2xl font-bold flex items-center`}>
|
||||
<ChevronRightIcon
|
||||
className={`w-8 h-8 mr-1 ${open ? "transform rotate-90" : ""}`}
|
||||
/>
|
||||
{GroupOption[grouping]}: {groupId} ({groupedPages[groupId].length})
|
||||
</h2>
|
||||
</Disclosure.Button>
|
||||
|
||||
<Disclosure.Panel>
|
||||
<List>
|
||||
{groupedPages[groupId].map((page: Page) => (
|
||||
<Item key={`${page.slug}-${idx}`} {...page} />
|
||||
))}
|
||||
</List>
|
||||
</Disclosure.Panel>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
))
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<List>
|
||||
{pages.map((page, idx) => (
|
||||
<Item key={`${page.slug}-${idx}`} {...page} />
|
||||
))}
|
||||
</List>
|
||||
);
|
||||
};
|
||||
@@ -1,14 +1,13 @@
|
||||
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';
|
||||
import { SponsorMsg } from './SponsorMsg';
|
||||
import { WelcomeScreen } from './WelcomeScreen';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { DashboardViewSelector } from '../state';
|
||||
import { Contents } from './Contents/Contents';
|
||||
import { Media } from './Media/Media';
|
||||
|
||||
export interface IDashboardProps {
|
||||
showWelcome: boolean;
|
||||
@@ -16,17 +15,10 @@ export interface IDashboardProps {
|
||||
|
||||
export const Dashboard: React.FunctionComponent<IDashboardProps> = ({showWelcome}: React.PropsWithChildren<IDashboardProps>) => {
|
||||
const { loading, pages, settings } = useMessages();
|
||||
const [ tab, setTab ] = React.useState(Tab.All);
|
||||
const [ sorting, setSorting ] = React.useState(SortOption.LastModified);
|
||||
const [ group, setGroup ] = React.useState<string | null>(null);
|
||||
const [ search, setSearch ] = React.useState<string | null>(null);
|
||||
const [ tag, setTag ] = React.useState<string | null>(null);
|
||||
const [ category, setCategory ] = React.useState<string | null>(null);
|
||||
const { pageItems } = usePages(pages, tab, sorting, group, search, tag, category);
|
||||
const { pageItems } = usePages(pages);
|
||||
const view = useRecoilValue(DashboardViewSelector);
|
||||
useDarkMode();
|
||||
|
||||
const pageGroups = [...new Set(pages.map(page => page.fmGroup))];
|
||||
|
||||
if (!settings) {
|
||||
return <Spinner />;
|
||||
}
|
||||
@@ -38,32 +30,14 @@ export const Dashboard: React.FunctionComponent<IDashboardProps> = ({showWelcome
|
||||
if (!settings.initialized || settings.folders?.length === 0) {
|
||||
return <WelcomeScreen settings={settings} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<main className={`h-full w-full`}>
|
||||
<div className="flex flex-col h-full overflow-auto">
|
||||
<Header currentTab={tab}
|
||||
currentSorting={sorting}
|
||||
groups={pageGroups}
|
||||
crntGroup={group}
|
||||
totalPages={pageItems.length}
|
||||
crntTag={tag}
|
||||
crntCategory={category}
|
||||
switchTab={(tabId: Tab) => 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}
|
||||
/>
|
||||
|
||||
<div className="flex-grow max-w-7xl mx-auto py-6 px-4">
|
||||
{ loading ? <Spinner /> : <Overview pages={pageItems} settings={settings} /> }
|
||||
</div>
|
||||
|
||||
<SponsorMsg />
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
if (view === "contents") {
|
||||
return (
|
||||
<Contents pages={pageItems} loading={loading} />
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Media />
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -1,46 +0,0 @@
|
||||
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<IGroupingProps> = ({groups, crntGroup, switchGroup}: React.PropsWithChildren<IGroupingProps>) => {
|
||||
if (groups.length <= 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center ml-6">
|
||||
<Menu as="div" className="relative z-10 inline-block text-left">
|
||||
<MenuButton label={`Showing`} title={crntGroup || DEFAULT_TYPE} />
|
||||
|
||||
<MenuItems>
|
||||
<MenuItem
|
||||
title={DEFAULT_TYPE}
|
||||
value={null}
|
||||
isCurrent={!crntGroup}
|
||||
onClick={switchGroup} />
|
||||
|
||||
{groups.map((option) => (
|
||||
<MenuItem
|
||||
key={option}
|
||||
title={option}
|
||||
value={option}
|
||||
isCurrent={option === crntGroup}
|
||||
onClick={switchGroup} />
|
||||
))}
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,75 +0,0 @@
|
||||
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<IHeaderProps> = ({currentTab, currentSorting, switchSorting, switchTab, totalPages, crntGroup, groups, switchGroup, onSearch, settings, switchTag, crntTag, switchCategory, crntCategory}: React.PropsWithChildren<IHeaderProps>) => {
|
||||
|
||||
const createContent = () => {
|
||||
MessageHelper.sendMessage(DashboardMessage.createContent);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`w-full max-w-7xl mx-auto sticky top-0 z-40 bg-gray-100 dark:bg-vulcan-500`}>
|
||||
<div className={`px-4 my-2 flex items-center justify-between`}>
|
||||
<Searchbox onSearch={onSearch} />
|
||||
|
||||
<div className={`flex items-center space-x-4`}>
|
||||
<Startup settings={settings} />
|
||||
|
||||
<Button onClick={createContent} disabled={!settings.initialized}>Create content</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-4 flex items-center border-b border-gray-200 dark:border-whisper-600">
|
||||
<Navigation currentTab={currentTab} totalPages={totalPages} switchTab={switchTab} />
|
||||
|
||||
<Grouping crntGroup={crntGroup} groups={groups} switchGroup={switchGroup} />
|
||||
|
||||
<Filter label={`Tag filter`} activeItem={crntTag} items={settings.tags} onClick={switchTag} />
|
||||
|
||||
<Filter label={`Category filter`} activeItem={crntCategory} items={settings.categories} onClick={switchCategory} />
|
||||
|
||||
<Sorting currentSorting={currentSorting} switchSorting={switchSorting} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
54
src/pagesView/components/Header/ClearFilters.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { XCircleIcon } from '@heroicons/react/solid';
|
||||
import * as React from 'react';
|
||||
import { useRecoilValue, useResetRecoilState } from 'recoil';
|
||||
import { SortingSelector, FolderSelector, TagSelector, CategorySelector, SortingAtom, DEFAULT_SORTING_OPTION, FolderAtom, DEFAULT_FOLDER_STATE, TagAtom, CategoryAtom, DEFAULT_TAG_STATE, DEFAULT_CATEGORY_STATE } from '../../state';
|
||||
|
||||
import { DefaultValue } from 'recoil';
|
||||
|
||||
export const guardRecoilDefaultValue = (
|
||||
candidate: any
|
||||
): candidate is DefaultValue => {
|
||||
if (candidate instanceof DefaultValue) return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
export interface IClearFiltersProps {}
|
||||
|
||||
export const ClearFilters: React.FunctionComponent<IClearFiltersProps> = (props: React.PropsWithChildren<IClearFiltersProps>) => {
|
||||
const [ show, setShow ] = React.useState(false);
|
||||
|
||||
const sorting = useRecoilValue(SortingSelector);
|
||||
const folder = useRecoilValue(FolderSelector);
|
||||
const tag = useRecoilValue(TagSelector);
|
||||
const category = useRecoilValue(CategorySelector);
|
||||
|
||||
const resetSorting = useResetRecoilState(SortingAtom);
|
||||
const resetFolder = useResetRecoilState(FolderAtom);
|
||||
const resetTag = useResetRecoilState(TagAtom);
|
||||
const resetCategory = useResetRecoilState(CategoryAtom);
|
||||
|
||||
const reset = () => {
|
||||
setShow(false);
|
||||
resetSorting();
|
||||
resetFolder();
|
||||
resetTag();
|
||||
resetCategory();
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (sorting !== DEFAULT_SORTING_OPTION || folder !== DEFAULT_FOLDER_STATE || tag !== DEFAULT_TAG_STATE || category !== DEFAULT_CATEGORY_STATE) {
|
||||
setShow(true);
|
||||
} else {
|
||||
setShow(false);
|
||||
}
|
||||
}, [sorting, folder, tag, category]);
|
||||
|
||||
if (!show) return null;
|
||||
|
||||
return (
|
||||
<button className="flex items-center hover:text-teal-600" onClick={reset} title={`Clear filters, grouping, and sorting`}>
|
||||
<XCircleIcon className={`inline-block w-5 h-5 mr-1`} /><span>Clear</span>
|
||||
<span className={`sr-only`}> filters, grouping, and sorting</span>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Menu } from '@headlessui/react';
|
||||
import { FilterIcon } from '@heroicons/react/solid';
|
||||
import * as React from 'react';
|
||||
import { MenuButton } from './MenuButton';
|
||||
import { MenuItem } from './MenuItem';
|
||||
import { MenuItems } from './MenuItems';
|
||||
import { MenuButton, MenuItem, MenuItems } from '../Menu';
|
||||
|
||||
export interface IFilterProps {
|
||||
label: string;
|
||||
@@ -20,9 +19,15 @@ export const Filter: React.FunctionComponent<IFilterProps> = ({label, activeItem
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center ml-6">
|
||||
<div className="flex items-center">
|
||||
<Menu as="div" className="relative z-10 inline-block text-left">
|
||||
<MenuButton label={label} title={activeItem || DEFAULT_VALUE} />
|
||||
<MenuButton
|
||||
label={(
|
||||
<>
|
||||
<FilterIcon className={`inline-block w-5 h-5 mr-1`} /><span>{label}</span>
|
||||
</>
|
||||
)}
|
||||
title={activeItem || DEFAULT_VALUE} />
|
||||
|
||||
<MenuItems>
|
||||
<MenuItem
|
||||
44
src/pagesView/components/Header/Folders.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { Menu, Transition } from '@headlessui/react';
|
||||
import * as React from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { FolderAtom } from '../../state';
|
||||
import { MenuButton, MenuItem, MenuItems } from '../Menu';
|
||||
|
||||
export interface IFoldersProps {
|
||||
folders: string[];
|
||||
}
|
||||
|
||||
const DEFAULT_TYPE = "All types";
|
||||
|
||||
export const Folders: React.FunctionComponent<IFoldersProps> = ({folders}: React.PropsWithChildren<IFoldersProps>) => {
|
||||
const [ crntFolder, setCrntFolder ] = useRecoilState(FolderAtom);
|
||||
|
||||
if (folders.length <= 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<Menu as="div" className="relative z-10 inline-block text-left">
|
||||
<MenuButton label={`Showing`} title={crntFolder || DEFAULT_TYPE} />
|
||||
|
||||
<MenuItems>
|
||||
<MenuItem
|
||||
title={DEFAULT_TYPE}
|
||||
value={null}
|
||||
isCurrent={!crntFolder}
|
||||
onClick={(value) => setCrntFolder(value)} />
|
||||
|
||||
{folders.map((option) => (
|
||||
<MenuItem
|
||||
key={option}
|
||||
title={option}
|
||||
value={option}
|
||||
isCurrent={option === crntFolder}
|
||||
onClick={(value) => setCrntFolder(value)} />
|
||||
))}
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
39
src/pagesView/components/Header/Grouping.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Menu } from '@headlessui/react';
|
||||
import * as React from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { GroupOption } from '../../constants/GroupOption';
|
||||
import { GroupingAtom } from '../../state';
|
||||
import { MenuButton, MenuItem, MenuItems } from '../Menu';
|
||||
|
||||
export interface IGroupingProps {}
|
||||
|
||||
export const groupOptions = [
|
||||
{ name: "None", id: GroupOption.none },
|
||||
{ name: "Year", id: GroupOption.Year },
|
||||
{ name: "Draft/Published", id: GroupOption.Draft },
|
||||
];
|
||||
|
||||
export const Grouping: React.FunctionComponent<IGroupingProps> = ({}: React.PropsWithChildren<IGroupingProps>) => {
|
||||
const [ group, setGroup ] = useRecoilState(GroupingAtom);
|
||||
|
||||
const crntGroup = groupOptions.find(x => x.id === group);
|
||||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<Menu as="div" className="relative z-10 inline-block text-left">
|
||||
<MenuButton label={`Group by`} title={crntGroup?.name || ""} />
|
||||
|
||||
<MenuItems>
|
||||
{groupOptions.map((option) => (
|
||||
<MenuItem
|
||||
key={option.id}
|
||||
title={option.name}
|
||||
value={option.id}
|
||||
isCurrent={option.id === crntGroup?.id}
|
||||
onClick={(value) => setGroup(value)} />
|
||||
))}
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
101
src/pagesView/components/Header/Header.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import * as React from 'react';
|
||||
import { Sorting } from './Sorting';
|
||||
import { Searchbox } from './Searchbox';
|
||||
import { Filter } from './Filter';
|
||||
import { Folders } from './Folders';
|
||||
import { Settings } from '../../models';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { Startup } from '../Startup';
|
||||
import { Button } from '../Button';
|
||||
import { Navigation } from '../Navigation';
|
||||
import { Grouping } from '.';
|
||||
import { ViewSwitch } from './ViewSwitch';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { CategoryAtom, DashboardViewAtom, TagAtom } from '../../state';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { ClearFilters } from './ClearFilters';
|
||||
import { MarkdownIcon } from '../../../viewpanel/components/Icons/MarkdownIcon';
|
||||
import { PhotographIcon } from '@heroicons/react/outline';
|
||||
import { Pagination } from '../Media/Pagination';
|
||||
|
||||
export interface IHeaderProps {
|
||||
settings: Settings | null;
|
||||
|
||||
// Navigation
|
||||
totalPages?: number;
|
||||
|
||||
// Page folders
|
||||
folders?: string[];
|
||||
}
|
||||
|
||||
export const Header: React.FunctionComponent<IHeaderProps> = ({totalPages, folders, settings }: React.PropsWithChildren<IHeaderProps>) => {
|
||||
const [ crntTag, setCrntTag ] = useRecoilState(TagAtom);
|
||||
const [ crntCategory, setCrntCategory ] = useRecoilState(CategoryAtom);
|
||||
const [ view, setView ] = useRecoilState(DashboardViewAtom);
|
||||
|
||||
const createContent = () => {
|
||||
Messenger.send(DashboardMessage.createContent);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`w-full sticky top-0 z-40 bg-gray-100 dark:bg-vulcan-500`}>
|
||||
|
||||
<div className={`px-4 bg-gray-50 dark:bg-vulcan-50 border-b-2 border-gray-200 dark:border-vulcan-200`}>
|
||||
<div className={`flex items-center justify-start`}>
|
||||
<button className={`p-2 flex items-center ${view === "contents" ? "bg-gray-200 dark:bg-vulcan-200" : ""} hover:bg-gray-100 dark:hover:bg-vulcan-100`} onClick={() => setView("contents")}>
|
||||
<MarkdownIcon className={`h-6 w-auto mr-2`} /><span>Contents</span>
|
||||
</button>
|
||||
<button className={`p-2 flex items-center ${view === "media" ? "bg-gray-200 dark:bg-vulcan-200" : ""} hover:bg-gray-100 dark:hover:bg-vulcan-100`} onClick={() => setView("media")}>
|
||||
<PhotographIcon className={`h-6 w-auto mr-2`} /><span>Media</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{
|
||||
view === "contents" && (
|
||||
<>
|
||||
<div className={`px-4 mt-3 mb-2 flex items-center justify-between`}>
|
||||
<Searchbox />
|
||||
|
||||
<div className={`flex items-center space-x-4`}>
|
||||
<Startup settings={settings} />
|
||||
|
||||
<Button onClick={createContent} disabled={!settings?.initialized}>Create content</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-4 flex flex-row items-center border-b border-gray-200 dark:border-vulcan-100 justify-between">
|
||||
<div>
|
||||
<Navigation totalPages={totalPages || 0} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<ViewSwitch />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`py-4 px-5 w-full flex items-center justify-between lg:justify-end space-x-4 lg:space-x-6 xl:space-x-8 bg-gray-200 border-b border-gray-300 dark:bg-vulcan-400 dark:border-vulcan-100`}>
|
||||
<ClearFilters />
|
||||
|
||||
<Folders folders={folders || []} />
|
||||
|
||||
<Filter label={`Tag`} activeItem={crntTag} items={settings?.tags || []} onClick={(value) => setCrntTag(value)} />
|
||||
|
||||
<Filter label={`Category`} activeItem={crntCategory} items={settings?.categories || []} onClick={(value) => setCrntCategory(value)} />
|
||||
|
||||
<Grouping />
|
||||
|
||||
<Sorting />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
view === "media" && (
|
||||
<Pagination />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,13 +1,14 @@
|
||||
import { FilterIcon, SearchIcon } from '@heroicons/react/solid';
|
||||
import * as React from 'react';
|
||||
import { useDebounce } from '../../hooks/useDebounce';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useDebounce } from '../../../hooks/useDebounce';
|
||||
import { SearchAtom } from '../../state';
|
||||
|
||||
export interface ISearchboxProps {
|
||||
onSearch: (searchText: string) => void;
|
||||
}
|
||||
export interface ISearchboxProps {}
|
||||
|
||||
export const Searchbox: React.FunctionComponent<ISearchboxProps> = ({onSearch}: React.PropsWithChildren<ISearchboxProps>) => {
|
||||
export const Searchbox: React.FunctionComponent<ISearchboxProps> = ({}: React.PropsWithChildren<ISearchboxProps>) => {
|
||||
const [ value, setValue ] = React.useState('');
|
||||
const [ , setDebounceValue ] = useRecoilState(SearchAtom);
|
||||
const debounceSearch = useDebounce<string>(value, 500);
|
||||
|
||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -15,7 +16,7 @@ export const Searchbox: React.FunctionComponent<ISearchboxProps> = ({onSearch}:
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
onSearch(debounceSearch);
|
||||
setDebounceValue(debounceSearch);
|
||||
}, [debounceSearch]);
|
||||
|
||||
return (
|
||||
@@ -32,7 +33,6 @@ export const Searchbox: React.FunctionComponent<ISearchboxProps> = ({onSearch}:
|
||||
className={`block w-full py-2 pl-10 pr-3 sm:text-sm bg-white dark:bg-vulcan-300 border border-gray-300 dark:border-vulcan-100 text-vulcan-500 dark:text-whisper-500 placeholder-gray-400 dark:placeholder-whisper-800 focus:outline-none`}
|
||||
placeholder="Search"
|
||||
value={value}
|
||||
autoFocus={true}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
40
src/pagesView/components/Header/Sorting.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { Menu } from '@headlessui/react';
|
||||
import * as React from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { SortOption } from '../../constants/SortOption';
|
||||
import { SearchSelector, SortingAtom } from '../../state';
|
||||
import { MenuButton, MenuItem, MenuItems } from '../Menu';
|
||||
|
||||
export interface ISortingProps {}
|
||||
|
||||
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<ISortingProps> = ({}: React.PropsWithChildren<ISortingProps>) => {
|
||||
const [ crntSorting, setCrntSorting ] = useRecoilState(SortingAtom);
|
||||
const searchValue = useRecoilValue(SearchSelector);
|
||||
|
||||
const crntSort = sortOptions.find(x => x.id === crntSorting);
|
||||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<Menu as="div" className="relative z-10 inline-block text-left">
|
||||
<MenuButton label={`Sort by`} title={crntSort?.name || ""} disabled={!!searchValue} />
|
||||
|
||||
<MenuItems>
|
||||
{sortOptions.map((option) => (
|
||||
<MenuItem
|
||||
key={option.id}
|
||||
title={option.name}
|
||||
value={option.id}
|
||||
isCurrent={option.id === crntSorting}
|
||||
onClick={(value) => setCrntSorting(value)} />
|
||||
))}
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
38
src/pagesView/components/Header/ViewSwitch.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import * as React from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { ViewAtom, ViewType, SettingsSelector } from '../../state';
|
||||
import { ViewGridIcon, ViewListIcon } from '@heroicons/react/solid';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
|
||||
export interface IViewSwitchProps {}
|
||||
|
||||
export const ViewSwitch: React.FunctionComponent<IViewSwitchProps> = (props: React.PropsWithChildren<IViewSwitchProps>) => {
|
||||
const [ view, setView ] = useRecoilState(ViewAtom);
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
|
||||
const toggleView = () => {
|
||||
const newView = view === ViewType.Grid ? ViewType.List : ViewType.Grid;
|
||||
setView(newView);
|
||||
Messenger.send(DashboardMessage.setPageViewType, newView);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (settings?.pageViewType) {
|
||||
setView(settings?.pageViewType);
|
||||
}
|
||||
}, [settings?.pageViewType]);
|
||||
|
||||
return (
|
||||
<div className={`flex rounded-sm bg-vulcan-50 lg:mb-1`}>
|
||||
<button className={`flex items-center px-2 py-1 rounded-l-sm ${view === ViewType.Grid ? 'bg-teal-500 text-vulcan-500' : 'text-whisper-500'}`} onClick={toggleView}>
|
||||
<ViewGridIcon className={`w-4 h-4`} />
|
||||
<span className={`sr-only`}>Change to grid</span>
|
||||
</button>
|
||||
<button className={`flex items-center px-2 py-1 rounded-r-sm ${view === ViewType.List ? 'bg-teal-500 text-vulcan-500' : 'text-whisper-500'}`} onClick={toggleView}>
|
||||
<ViewListIcon className={`w-4 h-4`} />
|
||||
<span className={`sr-only`}>Change to list</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
6
src/pagesView/components/Header/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export * from './Filter';
|
||||
export * from './Folders';
|
||||
export * from './Grouping';
|
||||
export * from './Header';
|
||||
export * from './Searchbox';
|
||||
export * from './Sorting';
|
||||
@@ -1,47 +0,0 @@
|
||||
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<IItemProps> = ({ fmFilePath, date, title, draft, description, preview }: React.PropsWithChildren<IItemProps>) => {
|
||||
|
||||
const openFile = () => {
|
||||
MessageHelper.sendMessage(DashboardMessage.openFile, fmFilePath);
|
||||
};
|
||||
|
||||
return (
|
||||
<li className="relative">
|
||||
<button className={`group cursor-pointer flex flex-wrap items-start content-start h-full w-full bg-gray-50 dark:bg-vulcan-200 text-vulcan-500 dark:text-whisper-500 text-left overflow-hidden shadow-md hover:shadow-xl dark:hover:bg-vulcan-100`}
|
||||
onClick={openFile}>
|
||||
<div className="relative h-36 w-full overflow-hidden border-b border-gray-100 dark:border-vulcan-100 dark:group-hover:border-vulcan-200">
|
||||
{
|
||||
preview ? (
|
||||
<img src={`${preview}`} alt={title} className="absolute inset-0 h-full w-full object-cover" loading="lazy" />
|
||||
) : (
|
||||
<div className={`flex items-center justify-center bg-whisper-500 dark:bg-vulcan-200 dark:group-hover:bg-vulcan-100`}>
|
||||
<MarkdownIcon className={`h-32 text-vulcan-100 dark:text-whisper-100`} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className="p-4 w-full">
|
||||
<div className={`flex justify-between items-center`}>
|
||||
<Status draft={!!draft} />
|
||||
|
||||
<DateField value={date} />
|
||||
</div>
|
||||
|
||||
<h2 className="mt-2 mb-2 font-bold">{title}</h2>
|
||||
|
||||
<p className="text-xs text-vulcan-200 dark:text-whisper-800">{description}</p>
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
77
src/pagesView/components/Media/FolderSelection.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import { Menu } from '@headlessui/react';
|
||||
import { XIcon } from '@heroicons/react/outline';
|
||||
import Downshift from 'downshift';
|
||||
import * as React from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { MediaFoldersSelector, SelectedMediaFolderAtom } from '../../state';
|
||||
|
||||
export interface IFolderSelectionProps {}
|
||||
|
||||
export const FolderSelection: React.FunctionComponent<IFolderSelectionProps> = (props: React.PropsWithChildren<IFolderSelectionProps>) => {
|
||||
const folders = useRecoilValue(MediaFoldersSelector);
|
||||
const [ selectedFolder, setSelectedFolder ] = useRecoilState(SelectedMediaFolderAtom);
|
||||
const [ focus, setFocus ] = React.useState(false);
|
||||
|
||||
let allFolders: string[] = Object.assign([], folders);
|
||||
allFolders = allFolders.sort((a: string, b: string) => {
|
||||
if (a.toLowerCase() < b.toLowerCase()) return -1;
|
||||
if (a.toLowerCase() > b.toLowerCase()) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Downshift
|
||||
isOpen={focus}
|
||||
selectedItem={selectedFolder}
|
||||
onOuterClick={() => setFocus(false)}
|
||||
onSelect={(selFolder) => {
|
||||
setSelectedFolder(selFolder);
|
||||
setFocus(false);
|
||||
}}>
|
||||
{
|
||||
({
|
||||
getInputProps,
|
||||
getItemProps,
|
||||
getMenuProps,
|
||||
isOpen,
|
||||
inputValue,
|
||||
getRootProps
|
||||
}) => (
|
||||
<div className={`relative flex items-center`}>
|
||||
<label className={`text-sm text-gray-500 dark:text-whisper-900`}>Filter by: </label>
|
||||
|
||||
<div
|
||||
className={`inline-flex items-center`}
|
||||
{...getRootProps({} as any, {suppressRefError: true})}
|
||||
>
|
||||
<input disabled={!!selectedFolder} onFocus={() => setFocus(true)} className={`ml-2 py-1 px-2 sm:text-sm bg-white dark:bg-vulcan-300 border border-gray-300 dark:border-vulcan-100 text-vulcan-500 dark:text-whisper-500 placeholder-gray-400 dark:placeholder-whisper-800 focus:outline-none`} {...getInputProps()} />
|
||||
|
||||
{
|
||||
selectedFolder && (
|
||||
<button title={`Clear`} onClick={() => setSelectedFolder(null)}><XIcon className={`ml-2 h-6 w-6 text-red-500 hover:text-red-800`} /></button>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className={`${focus ? `block` : `hidden`} top-8 absolute right-0 z-10 mt-2 w-min rounded-md shadow-2xl bg-white dark:bg-vulcan-500 ring-1 ring-vulcan-400 dark:ring-white ring-opacity-5 focus:outline-none text-sm max-h-96 overflow-auto`} {...getMenuProps()}>
|
||||
{isOpen
|
||||
? allFolders
|
||||
.filter((item: string) => !inputValue || item.includes(inputValue))
|
||||
.map((item, index) => (
|
||||
<div
|
||||
className="cursor-pointer text-gray-500 dark:text-whisper-900 block px-4 py-2 text-sm font-medium w-full text-left hover:bg-gray-100 hover:text-gray-700 dark:hover:text-whisper-600 dark:hover:bg-vulcan-100"
|
||||
{...getItemProps({ key: item, index, item })}
|
||||
>
|
||||
{item}
|
||||
</div>
|
||||
))
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</Downshift>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
132
src/pagesView/components/Media/Item.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { ClipboardCopyIcon, PhotographIcon, TrashIcon } from '@heroicons/react/outline';
|
||||
import { basename, dirname } from 'path';
|
||||
import * as React from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { MediaInfo } from '../../../models/MediaPaths';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { LightboxAtom, SelectedMediaFolderSelector, SettingsSelector } from '../../state';
|
||||
import { Alert } from '../Modals/Alert';
|
||||
|
||||
export interface IItemProps {
|
||||
media: MediaInfo;
|
||||
}
|
||||
|
||||
export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWithChildren<IItemProps>) => {
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
const selectedFolder = useRecoilValue(SelectedMediaFolderSelector);
|
||||
const [ , setLightbox ] = useRecoilState(LightboxAtom);
|
||||
const [ showAlert, setShowAlert ] = React.useState(false);
|
||||
|
||||
const parseWinPath = (path: string | undefined) => {
|
||||
return path?.split(`\\`).join(`/`);
|
||||
}
|
||||
|
||||
const getFolder = () => {
|
||||
if (settings?.wsFolder && media.fsPath) {
|
||||
let relPath = media.fsPath.split(settings.wsFolder).pop();
|
||||
|
||||
if (settings.staticFolder && relPath) {
|
||||
relPath = relPath.split(settings.staticFolder).pop();
|
||||
}
|
||||
|
||||
return dirname(parseWinPath(relPath) || "");
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
const copyToClipboard = () => {
|
||||
let relPath: string | undefined = "";
|
||||
if (settings?.wsFolder && media.fsPath) {
|
||||
relPath = media.fsPath.split(settings.wsFolder).pop();
|
||||
|
||||
if (settings.staticFolder && relPath) {
|
||||
relPath = relPath.split(settings.staticFolder).pop();
|
||||
}
|
||||
}
|
||||
|
||||
Messenger.send(DashboardMessage.copyToClipboard, parseWinPath(relPath) || "");
|
||||
};
|
||||
|
||||
const deleteMedia = () => {
|
||||
setShowAlert(true);
|
||||
};
|
||||
|
||||
const confirmDeletion = () => {
|
||||
Messenger.send(DashboardMessage.deleteMedia, {
|
||||
file: media.fsPath,
|
||||
folder: selectedFolder
|
||||
});
|
||||
};
|
||||
|
||||
const calculateSize = () => {
|
||||
if (media?.stats?.size) {
|
||||
const size = media.stats.size / (1024*1024);
|
||||
if (size > 1) {
|
||||
return `${size.toFixed(2)} MB`;
|
||||
} else {
|
||||
return `${(size * 1024).toFixed(2)} KB`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const openLightbox = () => {
|
||||
setLightbox(media.vsPath || "");
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<li className="group relative bg-gray-50 dark:bg-vulcan-200 hover:shadow-xl dark:hover:bg-vulcan-100">
|
||||
<button className="relative bg-gray-200 dark:bg-vulcan-300 block w-full aspect-w-10 aspect-h-7 overflow-hidden cursor-pointer h-48" onClick={openLightbox}>
|
||||
<div className={`absolute top-0 right-0 bottom-0 left-0 flex items-center justify-center`}>
|
||||
<PhotographIcon className={`h-1/2 text-gray-300 dark:text-vulcan-200`} />
|
||||
</div>
|
||||
<div className={`absolute top-0 right-0 bottom-0 left-0 flex items-center justify-center`}>
|
||||
<img src={media.vsPath} alt={basename(media.fsPath)} className="mx-auto object-cover" />
|
||||
</div>
|
||||
</button>
|
||||
<div className={`relative py-4 pl-4 pr-10`}>
|
||||
<div className={`absolute top-4 right-4 flex flex-col space-y-2`}>
|
||||
<button title={`Copy media path`}
|
||||
className={`hover:text-teal-900 focus:outline-none`}
|
||||
onClick={copyToClipboard}>
|
||||
<ClipboardCopyIcon className={`h-5 w-5`} />
|
||||
<span className={`sr-only`}>Copy media path</span>
|
||||
</button>
|
||||
<button title={`Delete media`}
|
||||
className={`hover:text-teal-900 focus:outline-none`}
|
||||
onClick={deleteMedia}>
|
||||
<TrashIcon className={`h-5 w-5`} />
|
||||
<span className={`sr-only`}>Delete media</span>
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-sm dark:text-whisper-900 font-bold pointer-events-none flex items-center">
|
||||
{basename(parseWinPath(media.fsPath) || "")}
|
||||
</p>
|
||||
<p className="mt-2 text-sm dark:text-whisper-900 font-medium pointer-events-none flex items-center">
|
||||
<b className={`mr-2`}>Folder:</b> {getFolder()}
|
||||
</p>
|
||||
{
|
||||
media?.stats?.size && (
|
||||
<p className="mt-2 text-sm dark:text-whisper-900 font-medium pointer-events-none flex items-center">
|
||||
<b className={`mr-1`}>Size:</b> {calculateSize()}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</li>
|
||||
|
||||
{
|
||||
showAlert && (
|
||||
<Alert
|
||||
title={`Delete: ${basename(parseWinPath(media.fsPath) || "")}`}
|
||||
description={`Are you sure you want to delete the file from the ${getFolder()} folder?`}
|
||||
okBtnText={`Delete`}
|
||||
cancelBtnText={`Cancel`}
|
||||
dismiss={() => setShowAlert(false)}
|
||||
trigger={confirmDeletion} />
|
||||
)
|
||||
}
|
||||
</>
|
||||
);
|
||||
};
|
||||