Compare commits

...

124 Commits

Author SHA1 Message Date
Elio Struyf
d364bde59d Merge pull request #507 from estruyf/dev 2023-02-14 16:37:29 +01:00
Elio Struyf
8ae105fd6e Fixes in preview paths 2023-02-14 16:12:39 +01:00
Elio Struyf
3a8854b060 update changelog 2023-02-13 19:58:07 +01:00
Elio Struyf
af393bbfc5 #425 - fix for global file path 2023-02-12 13:23:20 +01:00
Elio Struyf
76707b9eee Update changelog 2023-02-11 15:55:00 +01:00
Elio Struyf
8042a25fbb Update changelog 2023-02-11 13:18:40 +01:00
Elio Struyf
1a3520833b #506 - added default filename 2023-02-11 12:08:58 +01:00
Elio Struyf
1b1eac0a43 Merge branch 'furkanb-issue/#504' into dev 2023-02-11 12:08:04 +01:00
furkanb
6b19e69806 sanitize defaultFileName 2023-02-11 01:55:07 +03:00
furkanb
04960b7fdb issue/#504 2023-02-11 01:47:03 +03:00
Elio Struyf
3ca979bea9 updated changelog 2023-02-10 14:58:12 +01:00
Elio Struyf
12ecee8c49 Fix address bar of preview 2023-02-10 14:53:23 +01:00
Elio Struyf
680ea99f05 #425 - Added content type check 2023-02-10 14:44:25 +01:00
Elio Struyf
39cee294af #425 - Preview questions when folder is not found 2023-02-10 14:24:14 +01:00
Elio Struyf
558845a8d2 #425 - added new placeholders for previews 2023-02-10 13:24:59 +01:00
Elio Struyf
327559fdd7 #425 - support added for placeholders in the page folders path 2023-02-09 22:30:36 +01:00
Elio Struyf
2efbfa7820 Add button border to align with VSCode 2023-02-09 21:27:30 +01:00
Elio Struyf
cdeaa87321 #480 - Updating add missing fields label 2023-02-09 21:14:40 +01:00
Elio Struyf
483cfcd761 #505 - Small theming fixes 2023-02-09 17:35:16 +01:00
Elio Struyf
74bc8d78a0 #505 - Enhancements to theming of the data fields 2023-02-09 17:24:42 +01:00
Elio Struyf
bc88df98ee #505 - Data view theming 2023-02-09 13:32:34 +01:00
Elio Struyf
d48e63ba32 Restructuring components 2023-02-09 11:28:22 +01:00
Elio Struyf
20ee0e1ca5 #505 - Step + welcome view colors 2023-02-08 19:27:48 +01:00
Elio Struyf
716f7d1f0c #505 - Preview theming 2023-02-07 20:32:23 +01:00
Elio Struyf
5119f631f3 Enhancement: VS Code theme support for the dashboards #505 2023-02-07 19:59:46 +01:00
Elio Struyf
4eea855d0f #505 - Header theming 2023-02-07 17:47:07 +01:00
Elio Struyf
5354619283 #505 - Theming for snippets view 2023-02-07 13:49:05 +01:00
Elio Struyf
dd939f4b7a #505 - Media folder items styling 2023-02-07 10:48:19 +01:00
Elio Struyf
2635cc9b6d #505 - Media dashboard theming WIP 2023-02-07 10:32:01 +01:00
Elio Struyf
c843e74077 #505 - Align to theme colors WIP 2023-02-06 18:26:11 +01:00
Elio Struyf
baed139cc6 Implement new scoped groups from tailwind 2023-02-05 14:39:28 +01:00
Elio Struyf
38839470ac #502 - Keyboard bindings added 2023-02-05 14:31:25 +01:00
Elio Struyf
8a4b24396c #503: Allow making changes to preview URL + tailwind v3 update 2023-02-05 14:13:43 +01:00
Elio Struyf
4a50cf70a0 #473 - Allow setting the SEO title name 2023-02-04 10:08:25 +01:00
Elio Struyf
db38b9da68 #488 - Create the .frontmatter folder on initialization, not before 2023-02-02 21:06:20 +01:00
Elio Struyf
2b7f124582 #496 - Move to file storage for larger states 2023-02-02 18:46:50 +01:00
Elio Struyf
089f0a643d Merge pull request #500 from pongstr/dev 2023-02-02 12:11:15 +01:00
Pongstr
9c9102f6f5 chore(devx-improvements): Deps upgrades + Prettier 2023-02-02 11:13:55 +02:00
Elio Struyf
c95d79a218 Merge pull request #499 from pongstr/devx-improvements 2023-02-01 15:17:16 +01:00
Pongstr
c0b1e4383f init: sync with dev 2023-02-01 16:13:57 +02:00
Pongstr
ddf2873794 init 2023-02-01 11:58:31 +02:00
Pongstr
faf629cca5 init 2023-02-01 11:40:12 +02:00
Elio Struyf
bf4b66564c #497 - Support for movie media added 2023-01-30 10:19:05 +01:00
Elio Struyf
5c62255605 #494 - Support added for remote images as previews 2023-01-30 10:11:06 +01:00
Elio Struyf
4f91aeb80b Update wf name 2023-01-24 10:52:00 +01:00
Elio Struyf
e19b4d7d6c #493 - fix issue with custom placeholders 2023-01-24 10:44:16 +01:00
Elio Struyf
7f02fe34ba Update labels flow 2023-01-09 13:41:21 +01:00
Elio Struyf
5dec859849 Merge branch 'main' into dev 2023-01-07 20:18:57 +01:00
Elio Struyf
5e1558ecfa Update labeling 2023-01-07 20:18:45 +01:00
Elio Struyf
8a099de859 Updated vsce dependency 2022-12-24 09:47:13 +01:00
Elio Struyf
ddef00726b update badges 2022-12-24 09:42:40 +01:00
Elio Struyf
70316c4c28 #474 - Allow to define the file prefix on content types 2022-12-23 15:00:49 +01:00
Elio Struyf
50f2e7ea72 Update readme 2022-12-23 12:12:27 +01:00
Elio Struyf
663d346e2e Temporarily remove badges 2022-12-23 12:07:26 +01:00
Elio Struyf
d42561fbf5 #469 - Fix for using the root folder as content folder 2022-12-23 12:02:49 +01:00
Elio Struyf
0c5224b5f9 Update changelog 2022-12-23 10:27:39 +01:00
Elio Struyf
cfa805add9 #407 - support for external config files 2022-12-23 10:26:20 +01:00
Elio Struyf
86a8ef68b6 Settings override 2022-12-22 21:04:24 +01:00
Elio Struyf
cc2c6dc217 Allow external configurations - start 2022-12-22 18:03:04 +01:00
Elio Struyf
1764965aa7 8.3.0 2022-12-14 10:18:22 +01:00
Elio Struyf
12559bae4a #482 - Default content type description update 2022-12-14 10:17:47 +01:00
Elio Struyf
fc1d750c5e #470 - Fix initialize project dashboard description 2022-12-14 09:45:17 +01:00
Elio Struyf
85c4a869e3 #484 - Workspace path update 2022-12-14 09:40:32 +01:00
Elio Struyf
8b3889f997 #484 - validate if command is available 2022-12-14 09:00:53 +01:00
Elio Struyf
75b01cde7b #484 - Script support for different environments 2022-12-13 12:00:01 +01:00
Elio Struyf
c75ab2a07b Update sponsor 2022-12-12 09:02:14 +01:00
Elio Struyf
a12cf70a80 Merge pull request #477 from estruyf/dev 2022-12-08 18:12:53 +01:00
Elio Struyf
4b1d80f04b Prepare v8.2.0 release 2022-12-08 18:08:58 +01:00
Elio Struyf
a84fecaf96 Update json schema + changelog 2022-12-08 16:46:45 +01:00
Elio Struyf
5b9c279fa2 #471 - Fix typo 2022-12-02 11:49:56 +01:00
Elio Struyf
f67be9efb9 update changelog 2022-11-24 17:26:22 +01:00
Elio Struyf
4c50100230 Merge pull request #466 from albbus-stack/git-deleted-files-fix
Thank you @albbus-stack!
2022-11-24 17:25:01 +01:00
albbus-stack
82ace03692 Added git deleted files in the push method 2022-11-24 14:24:09 +01:00
Elio Struyf
576d07fdef Merge branch 'dev' of github.com:estruyf/vscode-front-matter into dev 2022-11-14 19:40:42 +01:00
Elio Struyf
f6bc4fb630 #365 - conditional support 2022-11-14 19:40:38 +01:00
Elio Struyf
c35f4ab070 Merge branch 'issue/362-ui' into dev 2022-11-14 19:39:11 +01:00
Elio Struyf
59cbc03b0c #458 - Add prefix to page bundles 2022-11-14 10:48:15 +01:00
Elio Struyf
0ea06a841e #412 - script splitting fix 2022-11-14 10:40:52 +01:00
Elio Struyf
b54eb5a360 #462 - Fix issue in script error notification 2022-11-14 10:40:30 +01:00
Elio Struyf
16b6fff6dc #362 - Case sensitive + insensitive option 2022-11-13 20:21:47 +01:00
Elio Struyf
7a46729a46 #362 - additional operators 2022-11-12 18:11:24 +01:00
Elio Struyf
32182c3df0 #362 - Start on conditional ui 2022-11-10 17:21:16 +01:00
Elio Struyf
78587509b3 Update readme 2022-11-09 13:59:19 +01:00
Elio Struyf
e3bd7eebbe #360 - Define which content types can be used on your page folders 2022-11-09 13:59:14 +01:00
Elio Struyf
f1a8e0d425 #458 - Ability to configure the file prefix on folder level 2022-11-09 10:31:49 +01:00
Elio Struyf
33e294d702 #455 - Show a description for the SEO section 2022-11-08 11:51:41 +01:00
Elio Struyf
e098442eaa #412 - Allow title on snippets 2022-11-07 17:28:40 +01:00
Elio Struyf
1de14122c5 #440 - Filter on description 2022-11-07 15:59:20 +01:00
Elio Struyf
c0838fffd4 #448 - Full workspace path replacement 2022-11-07 10:50:01 +01:00
Elio Struyf
082c25144f #448 - Fix file retrieval 2022-11-05 18:29:47 +01:00
Elio Struyf
d701651a05 #440 - Type to search/filter in the snippets dashboard 2022-11-04 15:25:00 +01:00
Elio Struyf
5205b2d079 #450 - Additional date placeholders 2022-11-04 10:34:46 +01:00
Elio Struyf
e864d56081 #447 - Allow to use placeholders on git commit messages 2022-11-04 10:33:23 +01:00
Elio Struyf
c6a4c239a0 #449 - Show filename if the title is not set 2022-11-04 09:58:54 +01:00
Elio Struyf
42fbdf9708 #412 - Support for sub-folders 2022-10-31 09:59:33 +01:00
Elio Struyf
8d53990aea #430 - support for post_asset_folder folder 2022-10-08 17:33:42 +02:00
Elio Struyf
b9a0c656d3 async updates for settings 2022-10-07 13:32:49 +02:00
Elio Struyf
8a8db67e82 #427 - Add hexo as SSG option 2022-10-07 13:32:42 +02:00
Elio Struyf
0ac4571859 Merge branch 'issue/431' into dev 2022-10-07 10:38:55 +02:00
Elio Struyf
a072957793 Updated exists to async 2022-10-06 21:49:53 +02:00
Elio Struyf
fad5ad7243 Moving away from fs sync methods 2022-10-06 21:36:51 +02:00
Elio Struyf
b248ee7184 Merge branch 'release/v8.1.2' into dev 2022-10-06 14:49:17 +02:00
Elio Struyf
4e850e5cb9 Fix field error message color 2022-10-06 14:23:49 +02:00
Elio Struyf
f89d4fce3f #431 - Update changelog 2022-10-04 17:05:04 +02:00
Elio Struyf
1ecf75ae9c Merge branch 'issue/431' into dev 2022-10-04 17:00:48 +02:00
Elio Struyf
888e5c5229 Get webview URI for Windows 2022-10-04 13:47:41 +02:00
Elio Struyf
45eb542619 #431 - Allow pagination page nr 2022-10-03 21:31:23 +02:00
Elio Struyf
5a565f1154 #412 - Override duplicates in config split 2022-10-03 14:07:14 +02:00
Elio Struyf
78002563be Clear cache command 2022-10-03 13:22:40 +02:00
Elio Struyf
be3071dc18 Merge branch 'dev' into issue/431 2022-10-02 20:18:46 +02:00
Elio Struyf
5c9d7eda17 #433 - fix title and description rendering if not string 2022-10-02 20:01:59 +02:00
Elio Struyf
9f3cfd9d3a #434 - Webview errors are logged in the extension output 2022-10-02 14:26:22 +02:00
Elio Struyf
0c6ae47a7b #434 - Webview errors are logged in the extension output 2022-10-02 14:25:11 +02:00
Elio Struyf
726a26850d #431 - Cache changes + Tab navigation 2022-10-02 14:23:51 +02:00
Elio Struyf
5fbb05f083 #431 - Performance improvements for first load 2022-10-01 20:30:18 +02:00
Elio Struyf
afca99b53a Merge branch 'dev' of github.com:estruyf/vscode-front-matter into dev 2022-10-01 10:36:34 +02:00
Elio Struyf
a8d2c428bc #428 - Image inserting UX enhancement 2022-10-01 10:36:29 +02:00
Elio Struyf
5254f2b7f9 #412 Support added for data files and custom scripts 2022-09-30 14:44:07 +02:00
Elio Struyf
13a71cfd82 #412 - Update setting casing 2022-09-30 10:44:14 +02:00
Elio Struyf
07d67bf881 #412 - allow config folders to use lowercase 2022-09-30 09:54:55 +02:00
Elio Struyf
27887bedef #412 - splitting configuration files 2022-09-29 20:36:02 +02:00
Elio Struyf
2b8f08c03c #406 - Single data entries 2022-09-27 13:16:44 +02:00
Elio Struyf
cb2194bc48 8.2.0 2022-09-27 12:04:50 +02:00
Elio Struyf
46872f81ac Include CMS in the display name 2022-09-27 09:00:14 +02:00
441 changed files with 29609 additions and 14629 deletions

17
.eslintrc.json Normal file
View File

@@ -0,0 +1,17 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"ignorePatterns": ["./**/*.js", "./webpack/*.config.js", "./e2e/*.ts"],
"rules": {
"no-throw-literal": "error",
"no-unused-expressions": "error",
"curly": "error",
"class-methods-use-this": "warn"
}
}

View File

@@ -43,7 +43,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -54,7 +54,7 @@ jobs:
# 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
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -68,4 +68,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v2

View File

@@ -5,8 +5,12 @@ on:
types: [created, moved, deleted]
jobs:
automate-issues-labels:
process-project:
name: Add/remove the project label and set the matrix variable
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.setMatrixData.outputs.matrix }}
statusLabel: ${{ steps.setStatusLabel.outputs.statusLabel }}
steps:
- name: Fetch project data
run: |
@@ -14,6 +18,7 @@ jobs:
curl --request GET --url '${{ github.event.project_card.project_url }}' --header 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' >> $GITHUB_ENV
echo 'EOF' >> $GITHUB_ENV
### Add or remove the project label ###
- name: Add the project label
uses: andymckay/labeler@master
if: ${{ contains(github.event.action, 'created') || contains(github.event.action, 'moved') }}
@@ -24,4 +29,62 @@ jobs:
uses: andymckay/labeler@master
if: ${{ contains(github.event.action, 'deleted') }}
with:
remove-labels: "Project: ${{ fromJSON(env.PROJECT_DATA).name }}"
remove-labels: "Project: ${{ fromJSON(env.PROJECT_DATA).name }}"
### Fetch project columns ###
- name: Fetch all columns
run: |
echo 'ALL_COLUMNS_DATA<<EOF' >> $GITHUB_ENV
curl --request GET --url '${{ fromJSON(env.PROJECT_DATA).columns_url }}' --header 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' | jq --compact-output '["Status: " + .[].name]' >> $GITHUB_ENV
echo 'EOF' >> $GITHUB_ENV
- name: Fetch column info
run: |
echo 'COLUMN_DATA<<EOF' >> $GITHUB_ENV
curl --request GET --url '${{ github.event.project_card.column_url }}' --header 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' >> $GITHUB_ENV
echo 'EOF' >> $GITHUB_ENV
- uses: actions/github-script@v6
id: setMatrixData
with:
result-encoding: string
script: |
const columnData = JSON.parse(process.env.COLUMN_DATA)
const allColumnsData = JSON.parse(process.env.ALL_COLUMNS_DATA)
const matrix = allColumnsData.filter((label) => {
return label !== `Status: ${columnData.name}`
});
core.setOutput('matrix', matrix)
- name: Set the status label
id: setStatusLabel
run: |
echo "statusLabel=${{ fromJSON(env.COLUMN_DATA).name }}" >> $GITHUB_OUTPUT
remove-labels:
runs-on: ubuntu-latest
needs: process-project
if: ${{ contains(github.event.action, 'deleted') || contains(github.event.action, 'moved') }}
strategy:
matrix:
label: ${{fromJson(needs.process-project.outputs.matrix)}}
steps:
- name: Remove the status label
uses: andymckay/labeler@master
with:
remove-labels: ${{ matrix.label }}
add-labels:
runs-on: ubuntu-latest
needs:
- process-project
- remove-labels
if: ${{ contains(github.event.action, 'created') || contains(github.event.action, 'moved') }}
steps:
- name: Add the status label
uses: andymckay/labeler@master
with:
add-labels: "Status: ${{ needs.process-project.outputs.statusLabel }}"

View File

@@ -24,7 +24,7 @@ jobs:
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
run: npx @vscode/vsce publish -p ${{ secrets.VSCE_PAT }} --baseImagesUrl https://raw.githubusercontent.com/estruyf/vscode-front-matter/dev
- name: Publish to open-vsx.org
run: npx ovsx publish -p ${{ secrets.OPEN_VSX_PAT }}

View File

@@ -24,7 +24,7 @@ jobs:
run: node scripts/main-release.js
- name: Publish
run: npx vsce publish -p ${{ secrets.VSCE_PAT }}
run: npx @vscode/vsce publish -p ${{ secrets.VSCE_PAT }}
- name: Publish to open-vsx.org
run: npx ovsx publish -p ${{ secrets.OPEN_VSX_PAT }}

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
auto-install-peers = true

7
.prettierrc Normal file
View File

@@ -0,0 +1,7 @@
{
"printWidth": 100,
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"trailingComma": "none"
}

View File

@@ -1,8 +1,5 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"ms-vscode.vscode-typescript-tslint-plugin",
"eliostruyf.vscode-typescript-exportallmodules"
]
}
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": ["eliostruyf.vscode-typescript-exportallmodules", "esbenp.prettier-vscode"]
}

42
.vscode/launch.json vendored
View File

@@ -3,32 +3,24 @@
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch Extension",
"version": "0.2.0",
"configurations": [
{
"name": "Launch Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"preLaunchTask": "npm: build:ext"
},
{
"name": "Attach Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
],
"preLaunchTask": "npm: build:ext"
},
{
"name": "Attach Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
]
}
]
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
"outFiles": ["${workspaceFolder}/dist/**/*.js"]
}
]
}

View File

@@ -1,7 +1,7 @@
{
"Recoil Atom": {
"prefix": "sq-atom",
"body": [
"Recoil Atom": {
"prefix": "sq-atom",
"body": [
"import { atom } from 'recoil';",
"",
"export const ${1:CollectionId}Atom = atom({",
@@ -12,9 +12,9 @@
"description": "Creates a new atom",
"scope": "typescript"
},
"Recoil Selector (sync)": {
"prefix": "sq-selector-sync",
"body": [
"Recoil Selector (sync)": {
"prefix": "sq-selector-sync",
"body": [
"import { selector } from 'recoil';",
"",
"export const ${1:CollectionData}Selector = selector({",
@@ -27,9 +27,9 @@
"description": "Creates a new synchronous selector",
"scope": "typescript"
},
"Recoil Selector (async)": {
"prefix": "sq-selector-async",
"body": [
"Recoil Selector (async)": {
"prefix": "sq-selector-async",
"body": [
"import { selector } from 'recoil';",
"",
"export const ${1:CollectionData}Selector = selector({",
@@ -42,9 +42,9 @@
"description": "Creates a new asynchronous selector",
"scope": "typescript"
},
"Recoil selectorFamily": {
"prefix": "sq-selector-fam",
"body": [
"Recoil selectorFamily": {
"prefix": "sq-selector-fam",
"body": [
"import { selectorFamily } from 'recoil';",
"",
"export const ${1:CollectionData}Selector = selectorFamily({",
@@ -63,4 +63,4 @@
"description": "Include the translations",
"scope": "typescriptreact"
}
}
}

96
.vscode/settings.json vendored
View File

@@ -1,52 +1,52 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.exclude": {
"out": false // set this to true to hide the "out" folder with the compiled JS files
"files.exclude": {
"out": false // set this to true to hide the "out" folder with the compiled JS files
},
"search.exclude": {
"out": true // set this to false to include "out" folder in search results
},
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
"typescript.tsc.autoDetect": "off",
"eliostruyf.writingstyleguide.terms.isDisabled": true,
"eliostruyf.writingstyleguide.biasFree.isDisabled": true,
"squarl.groups": [
{
"id": "dashboard",
"name": "Dashboard"
},
"search.exclude": {
"out": true // set this to false to include "out" folder in search results
{
"id": "panel",
"name": "Panel"
}
],
"squarl.bookmarks": [
{
"name": "App.tsx",
"path": "src/dashboardWebView/components/App.tsx",
"description": "Start of dashboard",
"type": "file",
"groupId": "dashboard"
},
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
"typescript.tsc.autoDetect": "off",
"eliostruyf.writingstyleguide.terms.isDisabled": true,
"eliostruyf.writingstyleguide.biasFree.isDisabled": true,
"squarl.groups": [
{
"id": "dashboard",
"name": "Dashboard"
},
{
"id": "panel",
"name": "Panel"
}
],
"squarl.bookmarks": [
{
"name": "App.tsx",
"path": "src/dashboardWebView/components/App.tsx",
"description": "Start of dashboard",
"type": "file",
"groupId": "dashboard"
},
{
"name": "ViewPanel.tsx",
"path": "src/panelWebView/ViewPanel.tsx",
"description": "Start of panel",
"type": "file",
"groupId": "panel"
},
{
"name": "styles.css",
"path": "src/panelWebView/styles.css",
"description": "Panel styles",
"type": "file",
"groupId": "panel"
},
{
"name": "settings.ts",
"path": "src/constants/settings.ts",
"description": "Settings names",
"type": "file"
}
],
}
{
"name": "ViewPanel.tsx",
"path": "src/panelWebView/ViewPanel.tsx",
"description": "Start of panel",
"type": "file",
"groupId": "panel"
},
{
"name": "styles.css",
"path": "src/panelWebView/styles.css",
"description": "Panel styles",
"type": "file",
"groupId": "panel"
},
{
"name": "settings.ts",
"path": "src/constants/settings.ts",
"description": "Settings names",
"type": "file"
}
]
}

48
.vscode/tasks.json vendored
View File

@@ -1,28 +1,28 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "watch",
"problemMatcher": "$tsc-watch",
"isBackground": true,
"presentation": {
"reveal": "never"
},
"group": {
"kind": "build",
"isDefault": true
}
},
{
"type": "npm",
"script": "build:ext",
"group": {
"kind": "build",
"isDefault": true
}
}
]
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "watch",
"problemMatcher": "$tsc-watch",
"isBackground": true,
"presentation": {
"reveal": "never"
},
"group": {
"kind": "build",
"isDefault": true
}
},
{
"type": "npm",
"script": "build:ext",
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

View File

@@ -5,7 +5,6 @@ src/**
.gitignore
vsc-extension-quickstart.md
**/tsconfig.json
**/tslint.json
**/*.map
**/*.ts
webpack.config.js
@@ -28,4 +27,4 @@ frontmatter.json
webpack
README.beta.md
e2e
storage
storage

View File

@@ -1,5 +1,71 @@
# Change Log
## [8.3.0] - 2022-02-14 - [Release notes](https://beta.frontmatter.codes/updates/v8.3.0)
### ✨ New features
- [#407](https://github.com/estruyf/vscode-front-matter/issues/407): External config support
### 🎨 Enhancements
- [#425](https://github.com/estruyf/vscode-front-matter/issues/425): Added support for placeholders in the content paths and previews
- [#473](https://github.com/estruyf/vscode-front-matter/issues/473): Allow setting the SEO title name with the `frontMatter.taxonomy.seoTitleField` setting
- [#474](https://github.com/estruyf/vscode-front-matter/issues/474): Allow to define the file prefix on content types
- [#484](https://github.com/estruyf/vscode-front-matter/issues/484): Support for overriding scripts per environment type
- [#494](https://github.com/estruyf/vscode-front-matter/issues/494): Support for external image URLs in previews
- [#497](https://github.com/estruyf/vscode-front-matter/issues/497): Support for movie media previews in the content dashboard
- [#502](https://github.com/estruyf/vscode-front-matter/issues/502): Keyboard bindings added to open dashboard, insert media, and insert snippet
- [#503](https://github.com/estruyf/vscode-front-matter/issues/503): Allow making changes to the preview URL in the webview
- [#504](https://github.com/estruyf/vscode-front-matter/issues/504): Allow specifying the filename for your page bundles
- [#505](https://github.com/estruyf/vscode-front-matter/issues/505): Experimental Visual Studio Code theming support
### ⚡️ Optimizations
- [#496](https://github.com/estruyf/vscode-front-matter/issues/496): Make use of the `storageUri` and `globalStorageUri` for storing larger states
### 🐞 Fixes
- [#469](https://github.com/estruyf/vscode-front-matter/issues/469): Fix for using the root folder as content folder
- [#470](https://github.com/estruyf/vscode-front-matter/issues/470): Fix `initialize project` dashboard description
- [#480](https://github.com/estruyf/vscode-front-matter/issues/480): Updated _add missing fields_ label to _add missing fields to content-type_
- [#482](https://github.com/estruyf/vscode-front-matter/issues/482): Update the description when you want to overwrite the default content type description
- [#488](https://github.com/estruyf/vscode-front-matter/issues/488): Fix an issue where the `.frontmatter` folder gets created before initializing the project
- [#493](https://github.com/estruyf/vscode-front-matter/issues/493): Fix an issue where a custom placeholder value is replaced by an `array` instead of a `string`
## [8.2.0] - 2022-12-08 - [Release notes](https://beta.frontmatter.codes/updates/v8.2.0)
### ✨ New features
- [#362](https://github.com/estruyf/vscode-front-matter/issues/362): Support for conditional metadata
- [#412](https://github.com/estruyf/vscode-front-matter/issues/412): Allow `frontmatter.json` to be split in multiple files
### 🎨 Enhancements
- [#360](https://github.com/estruyf/vscode-front-matter/issues/360): Define which content types can be used on your page folders
- [#406](https://github.com/estruyf/vscode-front-matter/issues/406): Added support for single data entries in the data dashboard
- [#428](https://github.com/estruyf/vscode-front-matter/issues/428): Improved UX for inserting images to your content
- [#430](https://github.com/estruyf/vscode-front-matter/issues/430): Support for HEXO its `post_asset_folder` setting (image location)
- [#434](https://github.com/estruyf/vscode-front-matter/issues/434): Webview errors are logged in the extension output
- [#440](https://github.com/estruyf/vscode-front-matter/issues/440): Type to search/filter in the snippets dashboard
- [#447](https://github.com/estruyf/vscode-front-matter/issues/447): Allow to use placeholders on git commit messages
- [#449](https://github.com/estruyf/vscode-front-matter/issues/449): Show `filename` if the `title` is not set
- [#450](https://github.com/estruyf/vscode-front-matter/issues/450): Additional time placeholders added `{{hour12}}`, `{{hour24}}`, `{{ampm}}`, and `{{minute}}`
- [#458](https://github.com/estruyf/vscode-front-matter/issues/458): Ability to configure the file prefix on folder level
### ⚡️ Optimizations
- [#431](https://github.com/estruyf/vscode-front-matter/issues/431): Performance improvements for the content dashboard
- [#448](https://github.com/estruyf/vscode-front-matter/issues/448): Retrieving files fails when content folder name and workspace folder name are the same
- [#455](https://github.com/estruyf/vscode-front-matter/issues/455): Show a description for the SEO section when title nor description is set
### 🐞 Fixes
- Fix field error message color
- [#433](https://github.com/estruyf/vscode-front-matter/issues/433): Fix issue with rendering an incorrect title value on the content dashboard
- [#462](https://github.com/estruyf/vscode-front-matter/issues/462): Fix issue in script error notification
- [#465](https://github.com/estruyf/vscode-front-matter/issues/465): Deleted content does not get added in git when syncing
- [#471](https://github.com/estruyf/vscode-front-matter/issues/471): Fix typo on data dashboard
## [8.1.2] - 2022-10-06
### 🐞 Fixes
@@ -13,7 +79,7 @@
- [#422](https://github.com/estruyf/vscode-front-matter/issues/422): Fix in panel initialization logic
## [8.1.0] - 2022-09-22 - [Release notes](https://beta.frontmatter.codes/updates/v8.1.0)
## [8.1.0] - 2022-09-22 - [Release notes](https://beta.frontmatter.codes/updates/v8.1.0)
### ✨ New features
@@ -65,7 +131,7 @@
- Fix missing clipboard icon for the media card action
- Fix in tags rendering on content cards
## [8.0.0] - 2022-07-11 - [Release notes](https://beta.frontmatter.codes/updates/v8.0.0)
## [8.0.0] - 2022-07-11 - [Release notes](https://beta.frontmatter.codes/updates/v8.0.0)
### ✨ New Features
@@ -109,7 +175,6 @@
- [#346](https://github.com/estruyf/vscode-front-matter/issues/346): Fix media dashboard refresh action
## [7.3.1] - 2022-05-26
### 🐞 Fixes
@@ -136,7 +201,6 @@
- [#334](https://github.com/estruyf/vscode-front-matter/issues/334): Fix for locked content folders retrieval
- [#339](https://github.com/estruyf/vscode-front-matter/issues/339): Fix for content folders without a title
## [7.2.0] - 2022-05-02 - [Release notes](https://beta.frontmatter.codes/updates/v7.2.0)
### 🎨 Enhancements
@@ -178,7 +242,7 @@
## [7.1.0] - 2022-04-07 - [Release notes](https://beta.frontmatter.codes/updates/v7.1.0)
### 🎨 Enhancements
- [#240](https://github.com/estruyf/vscode-front-matter/issues/240): Capability added to define display modes
- [#246](https://github.com/estruyf/vscode-front-matter/issues/246): Support to add multiple tags/keywords/taxonomy via comma separated values
- [#293](https://github.com/estruyf/vscode-front-matter/issues/293): Support added for setting preview images in block fields
@@ -198,7 +262,7 @@
- [#304](https://github.com/estruyf/vscode-front-matter/issues/304): Fix yaml stringify which caused additional fields to be added
- [#305](https://github.com/estruyf/vscode-front-matter/issues/305): Fix for overflow issue in taxonomy picker
- [#306](https://github.com/estruyf/vscode-front-matter/issues/306): Fix for default value of content type fields
- [#311](https://github.com/estruyf/vscode-front-matter/issues/311): Fix for updating snippets
- [#311](https://github.com/estruyf/vscode-front-matter/issues/311): Fix for updating snippets
## [7.0.0] - 2022-03-21 - [Release notes](https://beta.frontmatter.codes/updates/v7.0.0)
@@ -374,7 +438,7 @@ As from this version onwards, the extension will be published to [open-vsx.org](
### 🎨 Enhancements
- [#173](https://github.com/estruyf/vscode-front-matter/issues/173): Allow to specify your own sorting for the content dashboard
- [#174](https://github.com/estruyf/vscode-front-matter/issues/174): Added option to exclude sub-directories from page/markdown content retrieval
- [#174](https://github.com/estruyf/vscode-front-matter/issues/174): Added option to exclude sub-directories from page/markdown content retrieval
## [5.4.0] - 2021-11-05
@@ -520,10 +584,10 @@ As from this version onwards, the extension will be published to [open-vsx.org](
- Fix typo in the `package.json` file for the preview command
## [2.5.0] - 2020-08-19
## [2.5.0] - 2020-08-19
- Moved the center layout button to the other actions section
- [#60](https://github.com/estruyf/vscode-front-matter/issues/60): Added the ability to open a site preview in VS Code
- [#60](https://github.com/estruyf/vscode-front-matter/issues/60): Added the ability to open a site preview in VS Code
## [2.4.1] - 2020-08-16

View File

@@ -25,9 +25,9 @@ Eager to start contributing? Great 🤩, you can contribute to the following pro
- Start by forking this project;
- Clone your fork to your local machine;
- Run `npm i`;
- Run `pnpm i`;
- Open the project in VS Code;
- To start developing, run `npm run dev:ext` and press `f5` to start the debugging session.
- To start developing, run `pnpm dev:ext` and press `f5` to start the debugging session.
### Tips

View File

@@ -10,12 +10,12 @@
<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" />
<img src="https://vsmarketplacebadges.dev/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://vsmarketplacebadges.dev/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" />
<img src="https://vsmarketplacebadges.dev/rating/eliostruyf.vscode-front-matter.svg" alt="Ratings" style="display: inline-block;margin-left:10px" />
<a href="https://github.com/sponsors/estruyf" title="Become a sponsor" style="margin-left:10px">
<img src="https://img.shields.io/github/sponsors/estruyf?color=%23CE2E7C&logo=github&style=flat" alt="Sponsor the project" style="display: inline-block" />
@@ -191,6 +191,6 @@ You can open showcase issues for the following things:
<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>
<img src="https://api.visitorbadge.io/api/VisitorHit?user=estruyf&repo=vscode-front-matter&countColor=%23F05450&labelColor=%230E131F" height="25px" />
</a>
</p>

View File

@@ -8,12 +8,12 @@
<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" />
<img src="https://vsmarketplacebadges.dev/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" />
<img src="https://vsmarketplacebadges.dev/installs/eliostruyf.vscode-front-matter.svg" alt="Number of installs" style="display: inline-block;margin-left:10px" />
<img src="https://vsmarketplacebadges.dev/rating/eliostruyf.vscode-front-matter.svg" alt="Ratings" style="display: inline-block;margin-left:10px" />
<a href="https://github.com/sponsors/estruyf" title="Become a sponsor" style="margin-left:10px">
<img src="https://img.shields.io/github/sponsors/estruyf?color=%23CE2E7C&logo=github&style=flat" alt="Sponsor the project" style="display: inline-block" />
@@ -115,7 +115,7 @@ You can get the extension via:
If you have the courage to test out the beta features, we made available a beta version as well. You can install this via:
- Uninstall the main Front Matter version
- Install the beta version
- Install the beta version
- 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>
@@ -163,21 +163,29 @@ You can open showcase issues for the following things:
<p align="center">
<a href="https://github.com/estruyf/vscode-front-matter/graphs/contributors">
<img src="https://contrib.rocks/image?repo=estruyf/vscode-front-matter" />
<img src="https://contrib.rocks/image?repo=estruyf/vscode-front-matter" alt="Front Matter contributors" />
</a>
</p>
## 🖤 Backers & Sponsors 👇 🤘
<p align="center">
<img src="https://frontmatter.codes/api/img-sponsors" />
<img src="https://frontmatter.codes/api/img-sponsors" alt="Front Matter sponsors" />
</p>
<br />
<p align="center" title="Powered by Vercel">
<a href="https://vercel.com/?utm_source=vscode-frontmatter&utm_campaign=oss">
<img src="https://frontmatter.codes/assets/sponsors/powered-by-vercel.png" alt="Powered by Vercel" />
</a>
</p>
<br />
<p align="center">
<a href="https://vercel.com/?utm_source=vscode-frontmatter&utm_campaign=oss">
<img src="https://frontmatter.codes/assets/sponsors/powered-by-vercel.png" />
<a href="http://bejs.io/" title="Supported by the BEJS Community">
<img src="https://frontmatter.codes/assets/sponsors/bejs-community.png" alt="Supported by the BEJS Community" height="50px"/>
</a>
</p>
@@ -190,6 +198,6 @@ You can open showcase issues for the following things:
<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" />
<img src="https://api.visitorbadge.io/api/VisitorHit?user=estruyf&repo=vscode-front-matter&countColor=%23F05450&labelColor=%230E131F" height="25px" alt="Front Matter visitors" />
</a>
</p>
</p>

View File

@@ -42,8 +42,8 @@ describe("Initialization testing", function() {
await sleep(1000);
await VSBrowser.instance.driver.wait(() => {
return notificationExists(workbench, 'Front Matter:');
await VSBrowser.instance.driver.wait(() => {
return notificationExists(workbench, 'Front Matter:');
}, 2000) as Notification;
const notifications = await workbench.getNotifications();
@@ -68,13 +68,11 @@ describe("Initialization testing", function() {
async function notificationExists(workbench: Workbench, text: string): Promise<Notification | undefined> {
const notifications = await (await (new StatusBar()).openNotificationsCenter()).getNotifications(NotificationType.Info);
console.log(`Notifications:`, notifications.length);
for (const notification of notifications) {
const message = await notification.getMessage();
console.log(message)
if (message.indexOf(text) >= 0) {
return notification;
}
}
}
}

View File

@@ -1,33 +1,33 @@
import * as path from 'path';
import * as semver from "semver";
import { ExTester, ReleaseQuality } from "vscode-extension-tester";
import * as path from 'path'
import * as semver from 'semver'
import { ExTester, ReleaseQuality } from 'vscode-extension-tester'
async function main(): Promise<void> {
const vsCodeVersion: semver.SemVer = new semver.SemVer(`1.66.0`);
const version = vsCodeVersion.version;
const vsCodeVersion: semver.SemVer = new semver.SemVer(`1.66.0`)
const version = vsCodeVersion.version
const storageFolder = path.join(__dirname, "..", "storage");
const extFolder = path.join(__dirname, "..", "extensions");
const storageFolder = path.join(__dirname, '..', 'storage')
const extFolder = path.join(__dirname, '..', 'extensions')
try {
const testPath = path.join(__dirname, "command.test.js");
const testPath = path.join(__dirname, 'command.test.js')
const exTester = new ExTester(storageFolder, ReleaseQuality.Stable, extFolder);
await exTester.downloadCode(version);
await exTester.installVsix();
const exTester = new ExTester(storageFolder, ReleaseQuality.Stable, extFolder)
await exTester.downloadCode(version)
await exTester.installVsix({ useYarn: false })
// await exTester.installFromMarketplace("eliostruyf.vscode-front-matter");
await exTester.downloadChromeDriver(version);
await exTester.downloadChromeDriver(version)
// await exTester.setupRequirements({vscodeVersion: version});
const result = await exTester.runTests(testPath, {
vscodeVersion: version
});
vscodeVersion: version,
resources: [storageFolder],
})
process.exit(result);
process.exit(result)
} catch (err) {
console.log(err);
process.exit(1);
console.log(err)
process.exit(1)
}
}
main();
main()

10542
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
{
"name": "vscode-front-matter-beta",
"displayName": "Front Matter",
"description": "Front Matter is a CMS that runs within Visual Studio Code. It gives you the power and control of a full-blown CMS while also providing you the flexibility and speed of the static site generator of your choice like: Hugo, Jekyll, Hexo, NextJs, Gatsby, and many more...",
"displayName": "Front Matter CMS",
"description": "Front Matter is a CMS that runs within Visual Studio Code. It gives you the power and control of a full-blown CMS while also providing you the flexibility and speed of the static site generator of your choice like: Hugo, Jekyll, Docusaurus, NextJs, Gatsby, and many more...",
"icon": "assets/frontmatter-teal-128x128.png",
"version": "8.1.2",
"version": "8.3.0",
"preview": false,
"publisher": "eliostruyf",
"galleryBanner": {
@@ -51,11 +51,28 @@
"activationEvents": [
"workspaceContains:**/.frontmatter",
"workspaceContains:**/frontmatter.json",
"onView:frontMatter.explorer",
"onStartupFinished"
],
"main": "./dist/extension.js",
"contributes": {
"keybindings": [
{
"command": "frontMatter.dashboard",
"key": "alt+d"
},
{
"command": "frontMatter.insertMedia",
"key": "ctrl+shift+i",
"mac": "cmd+shift+i",
"when": "editorTextFocus"
},
{
"command": "frontMatter.insertSnippet",
"key": "ctrl+shift+v",
"mac": "cmd+shift+v",
"when": "editorTextFocus"
}
],
"viewsContainers": {
"activitybar": [
{
@@ -79,6 +96,19 @@
"configuration": {
"title": "Front Matter: use frontmatter.json for shared team settings",
"properties": {
"frontMatter.experimental": {
"type": "boolean",
"default": false,
"markdownDescription": "Specify if you want to enable the experimental features. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.experimental)"
},
"frontMatter.extends": {
"type": "array",
"markdownDescription": "Specify the list of paths/URLs to extend the Front Matter CMS config. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.extends)",
"default": [],
"items": {
"type": "string"
}
},
"frontMatter.content.autoUpdateDate": {
"type": "boolean",
"default": false,
@@ -206,6 +236,20 @@
],
"default": null,
"description": "Defines a custom preview path for the folder."
},
"filePrefix": {
"type": [
"null",
"string"
],
"description": "Defines a prefix for the file name."
},
"contentTypes": {
"type": "array",
"description": "Defines which content types can be used for the current location. If not defined, all content types will be available.",
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
@@ -271,6 +315,10 @@
"type": "string"
}
},
"title": {
"description": "The snippet title.",
"type": "string"
},
"description": {
"description": "The snippet description.",
"type": "string"
@@ -436,6 +484,30 @@
"type": "boolean",
"description": "Hide the action from the UI",
"default": false
},
"environments": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"macos",
"linux",
"windows"
],
"description": "The environment type for which the script needs to be used"
},
"script": {
"type": "string",
"description": "Path to the script to execute"
},
"command": {
"$ref": "#scriptCommand"
}
}
}
}
},
"additionalProperties": false,
@@ -447,9 +519,12 @@
"scope": "Custom scripts"
},
"frontMatter.dashboard.content.pagination": {
"type": "boolean",
"type": [
"boolean",
"number"
],
"default": true,
"markdownDescription": "Specify if you want to enable/disable pagination for your content. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.dashboard.content.pagination)",
"markdownDescription": "Specify if you want to enable/disable pagination for your content. You can define your page number up to 52. Default items per page is `16`. Disabling the pagination can be done by setting it to `false`. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.dashboard.content.pagination)",
"scope": "Dashboard"
},
"frontMatter.dashboard.content.cardTags": {
@@ -550,6 +625,11 @@
"type": "string",
"default": "content",
"description": "If you are using data types, you can specify your type ID."
},
"singleEntry": {
"type": "boolean",
"description": "If you want to use a single entry for your data file.",
"default": false
}
},
"additionalProperties": false,
@@ -601,6 +681,11 @@
"type": "string",
"default": "content",
"description": "If you are using data types, you can specify your type ID."
},
"singleEntry": {
"type": "boolean",
"description": "If you want to use a single entry for your data files in the folder.",
"default": false
}
},
"additionalProperties": false,
@@ -1039,6 +1124,50 @@
"type": "boolean",
"default": false,
"description": "Specify if the field is required"
},
"when": {
"type": "object",
"description": "Specify the conditions to show the field",
"properties": {
"fieldRef": {
"type": "string",
"description": "The field ID to use"
},
"operator": {
"type": "string",
"description": "The operator to use",
"enum": [
"eq",
"neq",
"contains",
"notContains",
"startsWith",
"endsWith",
"gt",
"gte",
"lt",
"lte",
"minimum",
"maximum",
"exlusiveMinimum",
"exclusiveMaximum"
]
},
"value": {
"type": [
"string",
"number",
"boolean",
"array"
],
"description": "The value to compare"
},
"caseSensitive": {
"type": "boolean",
"default": true,
"description": "Specify if the comparison is case sensitive. Default: true"
}
}
}
},
"additionalProperties": false,
@@ -1185,6 +1314,18 @@
"type": "string",
"default": "",
"description": "An optional post script that can be used after new content creation."
},
"filePrefix": {
"type": [
"null",
"string"
],
"description": "Defines a prefix for the file name."
},
"defaultFileName": {
"type": "string",
"default": "index",
"description": "Default file name to use when creating new content."
}
},
"additionalProperties": false,
@@ -1378,6 +1519,12 @@
"markdownDescription": "Specifies the optimal slug length for SEO (set to `-1` to turn it off). [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.seoSlugLength)",
"scope": "Taxonomy"
},
"frontMatter.taxonomy.seoTitleField": {
"type": "string",
"default": "title",
"markdownDescription": "Specifies the name of the SEO title field for your page. Default is 'title'. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.taxonomy.seotitlefield)",
"scope": "Taxonomy"
},
"frontMatter.taxonomy.seoTitleLength": {
"type": "number",
"default": 60,
@@ -1732,6 +1879,11 @@
"command": "frontMatter.git.sync",
"title": "Sync",
"category": "Front Matter"
},
{
"command": "frontMatter.cache.clear",
"title": "Clear cache",
"category": "Front Matter"
}
],
"menus": {
@@ -2058,7 +2210,9 @@
"clean": "rimraf dist",
"start:site": "cd ./docs && npm run dev",
"clean:test": "rm ./e2e/sample/frontmatter.json || exit 0 && rm -rf ./e2e/sample/.frontmatter || exit 0",
"test": "tsc -p tsconfig.e2e.json && npm run clean:test && node ./e2e/out/runTests.js"
"test": "pnpm lint; tsc -p tsconfig.e2e.json && npm run clean:test && pnpm i -g @vscode/vsce && node ./e2e/out/runTests.js",
"lint": "eslint --max-warnings=0 ./src/{commands,components}",
"prettier": "prettier --write ./src"
},
"devDependencies": {
"@actions/core": "^1.8.2",
@@ -2071,7 +2225,7 @@
"@popperjs/core": "^2.11.6",
"@sentry/react": "^6.13.3",
"@sentry/tracing": "^6.13.3",
"@tailwindcss/forms": "^0.3.3",
"@tailwindcss/forms": "^0.5.3",
"@types/chai": "^4.3.1",
"@types/glob": "7.1.3",
"@types/invariant": "^2.2.35",
@@ -2079,6 +2233,7 @@
"@types/lodash.omit": "^4.5.6",
"@types/lodash.uniqby": "4.7.6",
"@types/lodash.xor": "^4.5.6",
"@types/mdast": "^3.0.10",
"@types/mime-types": "^2.1.1",
"@types/mocha": "^5.2.7",
"@types/mustache": "^4.1.2",
@@ -2088,17 +2243,20 @@
"@types/react-datepicker": "^4.1.7",
"@types/react-dom": "17.0.0",
"@types/vscode": "^1.63.0",
"@typescript-eslint/eslint-plugin": "^5.50.0",
"@typescript-eslint/parser": "^5.50.0",
"@vscode/codicons": "0.0.20",
"@vscode/extension-telemetry": "^0.4.7",
"@vscode/webview-ui-toolkit": "^0.9.1",
"@webpack-cli/serve": "^1.6.0",
"ajv": "^8.8.2",
"array-move": "^4.0.0",
"autoprefixer": "^10.3.2",
"autoprefixer": "^10.4.13",
"chai": "^4.3.6",
"css-loader": "5.2.7",
"date-fns": "2.23.0",
"downshift": "6.0.6",
"eslint": "^8.33.0",
"fuse.js": "6.5.3",
"glob": "7.1.6",
"gray-matter": "4.0.3",
@@ -2106,6 +2264,7 @@
"html-webpack-plugin": "4.5.0",
"image-size": "^1.0.0",
"invariant": "^2.2.4",
"js-yaml": "^4.1.0",
"jsonc-parser": "^3.2.0",
"lodash-es": "^4.17.21",
"lodash.omit": "^4.5.0",
@@ -2115,16 +2274,19 @@
"mime-types": "^2.1.35",
"mocha": "^10.0.0",
"mustache": "^4.2.0",
"node-fetch": "^2.6.9",
"node-json-db": "^1.3.0",
"npm-run-all": "^4.1.5",
"path-browserify": "^1.0.1",
"postcss": "^8.3.6",
"postcss-loader": "4.3.0",
"postcss-nested": "^5.0.6",
"postcss": "^8.4.21",
"postcss-loader": "^7.0.2",
"prettier": "^2.8.3",
"prettier-plugin-tailwindcss": "^0.2.2",
"react": "17.0.1",
"react-datepicker": "4.2.1",
"react-dom": "17.0.1",
"react-dropzone": "^11.3.4",
"react-popper": "^2.3.0",
"react-quill": "^2.0.0-beta.4",
"react-router-dom": "^6.3.0",
"react-sortable-hoc": "^2.0.0",
@@ -2134,17 +2296,15 @@
"semver": "^7.3.7",
"simple-git": "^3.10.0",
"style-loader": "2.0.0",
"tailwindcss": "^2.2.7",
"tailwindcss-nested-groups": "^1.2.4",
"ts-loader": "8.0.3",
"tslint": "6.1.3",
"typescript": "^4.5.4",
"tailwindcss": "^3.2.4",
"ts-loader": "^9.4.2",
"typescript": "^4.6.2",
"uniforms": "^3.7.0",
"uniforms-antd": "^3.7.0",
"uniforms-bridge-json-schema": "^3.7.0",
"uniforms-unstyled": "^3.7.0",
"url-join-ts": "^1.0.5",
"vscode-extension-tester": "^4.2.5",
"vscode-extension-tester": "^5.3.0",
"wc-react": "github:estruyf/wc-react",
"webpack": "^5.65.0",
"webpack-bundle-analyzer": "^4.5.0",
@@ -2153,7 +2313,7 @@
"yaml": "^1.10.2",
"yawn-yaml": "^1.5.0"
},
"dependencies": {
"node-fetch": "^2.6.7"
"vsce": {
"dependencies": false
}
}

7603
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,10 @@
const tailwindcss = require('tailwindcss');
module.exports = {
plugins: [
require('postcss-nested'),
tailwindcss('./tailwind.config.js'),
require('autoprefixer'),
],
plugins: {
'postcss-import': {},
'tailwindcss/nesting': {},
tailwindcss: {},
autoprefixer: {},
}
};

View File

@@ -8,7 +8,7 @@ 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.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";

View File

@@ -0,0 +1,43 @@
const packageJson = require('../package.json');
for (const key of Object.keys(packageJson.contributes.configuration.properties)) {
const type = packageJson.contributes.configuration.properties[key].type;
if (type.includes('object') || type.includes('array')) {
console.log(`${key} - ${packageJson.contributes.configuration.properties[key].type}`);
}
}
// TO IGNORE
// frontMatter.extends - array
// frontMatter.dashboard.mediaSnippet - array
// TO PROCESS AS A WHOLE OBJECT
// frontMatter.content.draftField - object
// frontMatter.content.supportedFileTypes - array
// frontMatter.global.notifications - array
// frontMatter.global.disabledNotificaitons - array
// frontMatter.media.supportedMimeTypes - array
// frontMatter.taxonomy.commaSeparatedFields - array
// MERGE ARRAYS
// frontMatter.taxonomy.categories - array
// frontMatter.taxonomy.tags - array
// frontMatter.taxonomy.noPropertyValueQuotes - array
// PROCESS ITEM BY ITEM
// frontMatter.custom.scripts - array - id
// frontMatter.taxonomy.contentTypes - array,null - name
// frontMatter.data.files - array - id
// frontMatter.data.folders - array - id
// frontMatter.data.types - array - id
// frontMatter.content.pageFolders - array - path
// frontMatter.content.placeholders - array - id
// frontMatter.content.sorting - array - id
// frontMatter.global.modes - array - id
// frontMatter.taxonomy.fieldGroups - array - id
// frontMatter.taxonomy.customTaxonomy - array - id
// frontMatter.content.snippets - object

View File

@@ -1,10 +1,20 @@
import { Folders } from './Folders';
import { DEFAULT_CONTENT_TYPE } from './../constants/ContentType';
import { isValidFile } from './../helpers/isValidFile';
import { SETTING_AUTO_UPDATE_DATE, SETTING_MODIFIED_FIELD, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_TEMPLATES_PREFIX, CONFIG_KEY, SETTING_DATE_FORMAT, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_CONTENT_PLACEHOLDERS, TelemetryEvent } from './../constants';
import {
SETTING_AUTO_UPDATE_DATE,
SETTING_SLUG_UPDATE_FILE_NAME,
SETTING_TEMPLATES_PREFIX,
CONFIG_KEY,
SETTING_DATE_FORMAT,
SETTING_SLUG_PREFIX,
SETTING_SLUG_SUFFIX,
SETTING_CONTENT_PLACEHOLDERS,
TelemetryEvent
} from './../constants';
import * as vscode from 'vscode';
import { CustomPlaceholder, Field, TaxonomyType } from "../models";
import { format } from "date-fns";
import { CustomPlaceholder, Field, TaxonomyType } from '../models';
import { format } from 'date-fns';
import { ArticleHelper, Settings, SlugHelper } from '../helpers';
import { Notifications } from '../helpers/Notifications';
import { extname, basename, parse, dirname } from 'path';
@@ -18,13 +28,12 @@ import { MediaListener } from '../listeners/panel';
import { NavigationType } from '../dashboardWebView/models';
import { processKnownPlaceholders } from '../helpers/PlaceholderHelper';
export class Article {
/**
* Insert taxonomy
*
* @param type
*/
* Insert taxonomy
*
* @param type
*/
public static async insert(type: TaxonomyType) {
const editor = vscode.window.activeTextEditor;
if (!editor) {
@@ -38,16 +47,21 @@ export class Article {
}
let options: vscode.QuickPickItem[] = [];
const matterProp: string = type === TaxonomyType.Tag ? "tags" : "categories";
const matterProp: string = type === TaxonomyType.Tag ? 'tags' : 'categories';
// Add the selected options to the options array
if (article.data[matterProp]) {
const propData = article.data[matterProp];
if (propData && propData.length > 0) {
options = [...propData].filter(p => p).map(p => ({
label: p,
picked: true
} as vscode.QuickPickItem));
options = [...propData]
.filter((p) => p)
.map(
(p) =>
({
label: p,
picked: true
} as vscode.QuickPickItem)
);
}
}
@@ -55,7 +69,7 @@ export class Article {
const crntOptions = Settings.getTaxonomy(type);
if (crntOptions && crntOptions.length > 0) {
for (const crntOpt of crntOptions) {
if (!options.find(o => o.label === crntOpt)) {
if (!options.find((o) => o.label === crntOpt)) {
options.push({
label: crntOpt
});
@@ -64,18 +78,18 @@ export class Article {
}
if (options.length === 0) {
Notifications.info(`No ${type === TaxonomyType.Tag ? "tags" : "categories"} configured.`);
Notifications.info(`No ${type === TaxonomyType.Tag ? 'tags' : 'categories'} configured.`);
return;
}
const selectedOptions = await vscode.window.showQuickPick(options, {
placeHolder: `Select your ${type === TaxonomyType.Tag ? "tags" : "categories"} to insert`,
placeHolder: `Select your ${type === TaxonomyType.Tag ? 'tags' : 'categories'} to insert`,
canPickMany: true,
ignoreFocusOut: true
});
if (selectedOptions) {
article.data[matterProp] = selectedOptions.map(o => o.label);
article.data[matterProp] = selectedOptions.map((o) => o.label);
}
ArticleHelper.update(editor, article);
@@ -95,21 +109,23 @@ export class Article {
return;
}
article = this.updateDate(article, true);
article = this.updateDate(article);
try {
ArticleHelper.update(editor, article);
} catch (e) {
Notifications.error(`Something failed while parsing the date format. Check your "${CONFIG_KEY}${SETTING_DATE_FORMAT}" setting.`);
Notifications.error(
`Something failed while parsing the date format. Check your "${CONFIG_KEY}${SETTING_DATE_FORMAT}" setting.`
);
}
}
/**
* Update the date in the front matter
* @param article
* @param article
*/
public static updateDate(article: ParsedFrontMatter, forceCreate: boolean = false) {
article.data = ArticleHelper.updateDates(article.data);
public static updateDate(article: ParsedFrontMatter) {
article.data = ArticleHelper.updateDates(article.data);
return article;
}
@@ -124,14 +140,11 @@ export class Article {
const updatedArticle = this.setLastModifiedDateInner(editor.document);
if (typeof updatedArticle === "undefined") {
if (typeof updatedArticle === 'undefined') {
return;
}
ArticleHelper.update(
editor,
updatedArticle as ParsedFrontMatter
);
ArticleHelper.update(editor, updatedArticle as ParsedFrontMatter);
}
public static async setLastModifiedDateOnSave(
@@ -139,7 +152,7 @@ export class Article {
): Promise<vscode.TextEdit[]> {
const updatedArticle = this.setLastModifiedDateInner(document);
if (typeof updatedArticle === "undefined") {
if (typeof updatedArticle === 'undefined') {
return [];
}
@@ -163,8 +176,10 @@ export class Article {
try {
cloneArticle.data[dateField] = Article.formatDate(new Date());
return cloneArticle;
} catch (e: any) {
Notifications.error(`Something failed while parsing the date format. Check your "${CONFIG_KEY}${SETTING_DATE_FORMAT}" setting.`);
} catch (e: unknown) {
Notifications.error(
`Something failed while parsing the date format. Check your "${CONFIG_KEY}${SETTING_DATE_FORMAT}" setting.`
);
}
}
@@ -194,11 +209,10 @@ export class Article {
/**
* Generate the slug based on the article title
*/
public static async updateSlug() {
Telemetry.send(TelemetryEvent.generateSlug);
public static async updateSlug() {
Telemetry.send(TelemetryEvent.generateSlug);
const updateFileName = Settings.get(SETTING_SLUG_UPDATE_FILE_NAME) as string;
const filePrefix = Settings.get<string>(SETTING_TEMPLATES_PREFIX);
const editor = vscode.window.activeTextEditor;
if (!editor) {
@@ -210,30 +224,41 @@ export class Article {
return;
}
let filePrefix = Settings.get<string>(SETTING_TEMPLATES_PREFIX);
const contentType = ArticleHelper.getContentType(article.data);
const titleField = "title";
filePrefix = ArticleHelper.getFilePrefix(editor.document.uri.fsPath, contentType);
const titleField = 'title';
const articleTitle: string = article.data[titleField];
const slugInfo = Article.generateSlug(articleTitle);
if (slugInfo && slugInfo.slug && slugInfo.slugWithPrefixAndSuffix) {
article.data["slug"] = slugInfo.slugWithPrefixAndSuffix;
article.data['slug'] = slugInfo.slugWithPrefixAndSuffix;
if (contentType) {
// Update the fields containing the slug placeholder
let fieldsToUpdate: Field[] = contentType.fields.filter(f => f.default === "{{slug}}");
const fieldsToUpdate: Field[] = contentType.fields.filter((f) => f.default === '{{slug}}');
for (const field of fieldsToUpdate) {
article.data[field.name] = slugInfo.slug;
}
// Update the fields containing a custom placeholder that depends on slug
const placeholders = Settings.get<CustomPlaceholder[]>(SETTING_CONTENT_PLACEHOLDERS);
const customPlaceholders = placeholders?.filter(p => p.value && p.value.includes("{{slug}}"));
const customPlaceholders = placeholders?.filter(
(p) => p.value && p.value.includes('{{slug}}')
);
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
for (const customPlaceholder of (customPlaceholders || [])) {
const customPlaceholderFields = contentType.fields.filter(f => f.default === `{{${customPlaceholder.id}}}`);
for (const customPlaceholder of customPlaceholders || []) {
const customPlaceholderFields = contentType.fields.filter(
(f) => f.default === `{{${customPlaceholder.id}}}`
);
for (const pField of customPlaceholderFields) {
article.data[pField.name] = customPlaceholder.value;
article.data[pField.name] = processKnownPlaceholders(article.data[pField.name], articleTitle, dateFormat);
article.data[pField.name] = processKnownPlaceholders(
article.data[pField.name],
articleTitle,
dateFormat
);
}
}
}
@@ -247,13 +272,13 @@ export class Article {
if (editor) {
const ext = extname(editor.document.fileName);
const fileName = basename(editor.document.fileName);
let slugName = slugInfo.slug.startsWith("/") ? slugInfo.slug.substring(1) : slugInfo.slug;
slugName = slugName.endsWith("/") ? slugName.substring(0, slugName.length - 1) : slugName;
let slugName = slugInfo.slug.startsWith('/') ? slugInfo.slug.substring(1) : slugInfo.slug;
slugName = slugName.endsWith('/') ? slugName.substring(0, slugName.length - 1) : slugName;
let newFileName = `${slugName}${ext}`;
if (filePrefix && typeof filePrefix === "string") {
newFileName = `${format(new Date(), DateHelper.formatUpdate(filePrefix) as string)}-${newFileName}`;
if (filePrefix && typeof filePrefix === 'string') {
newFileName = `${filePrefix}-${newFileName}`;
}
const newPath = editor.document.uri.fsPath.replace(fileName, newFileName);
@@ -264,13 +289,13 @@ export class Article {
await vscode.workspace.fs.rename(editor.document.uri, vscode.Uri.file(newPath), {
overwrite: false
});
} catch (e: any) {
Notifications.error(`Failed to rename file: ${e?.message || e}`);
} catch (e: unknown) {
Notifications.error(`Failed to rename file: ${(e as Error).message || e}`);
}
}
}
}
}
}
/**
* Retrieve the slug from the front matter
@@ -289,7 +314,7 @@ export class Article {
const parsedFile = parse(file);
if (parsedFile.name.toLowerCase() !== "index") {
if (parsedFile.name.toLowerCase() !== 'index') {
return parsedFile.name;
}
@@ -310,8 +335,8 @@ export class Article {
return;
}
const newDraftStatus = !article.data["draft"];
article.data["draft"] = newDraftStatus;
const newDraftStatus = !article.data['draft'];
article.data['draft'] = newDraftStatus;
ArticleHelper.update(editor, article);
}
@@ -327,7 +352,7 @@ export class Article {
// Is article located in one of the content folders
const folders = Folders.get();
const documentPath = parseWinPath(document.fileName);
const folder = folders.find(f => documentPath.startsWith(f.path));
const folder = folders.find((f) => documentPath.startsWith(f.path));
if (!folder) {
return;
}
@@ -344,10 +369,12 @@ export class Article {
public static formatDate(dateValue: Date): string {
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
if (dateFormat && typeof dateFormat === "string") {
if (dateFormat && typeof dateFormat === 'string') {
return format(dateValue, DateHelper.formatUpdate(dateFormat) as string);
} else {
return typeof dateValue.toISOString === 'function' ? dateValue.toISOString() : dateValue?.toString();
return typeof dateValue.toISOString === 'function'
? dateValue.toISOString()
: dateValue?.toString();
}
}
@@ -355,19 +382,20 @@ export class Article {
* Insert an image from the media dashboard into the article
*/
public static async insertMedia() {
let editor = vscode.window.activeTextEditor;
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}
const article = ArticleHelper.getFrontMatter(editor);
const contentType = article && article.data ? ArticleHelper.getContentType(article.data) : DEFAULT_CONTENT_TYPE;
const contentType =
article && article.data ? ArticleHelper.getContentType(article.data) : DEFAULT_CONTENT_TYPE;
const position = editor.selection.active;
const selectionText = editor.document.getText(editor.selection);
await vscode.commands.executeCommand(COMMAND_NAME.dashboard, {
type: "media",
type: 'media',
data: {
pageBundle: !!contentType.pageBundle,
filePath: editor.document.uri.fsPath,
@@ -379,13 +407,13 @@ export class Article {
// Let the editor panel know you are selecting an image
MediaListener.getMediaSelection();
}
}
/**
* Insert a snippet into the article
*/
public static async insertSnippet() {
let editor = vscode.window.activeTextEditor;
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}
@@ -398,24 +426,24 @@ export class Article {
await vscode.commands.executeCommand(COMMAND_NAME.dashboard, {
type: NavigationType.Snippets,
data: {
fileTitle: article?.data.title || "",
fileTitle: article?.data.title || '',
filePath: editor.document.uri.fsPath,
fieldName: basename(editor.document.uri.fsPath),
position,
selection: selectionText
}
} as DashboardData);
}
}
/**
* Update the article date and return it
* @param article
* @param dateFormat
* @param field
* @param forceCreate
* @param article
* @param dateFormat
* @param field
* @param forceCreate
*/
private static articleDate(article: ParsedFrontMatter, field: string, forceCreate: boolean) {
if (typeof article.data[field] !== "undefined" || forceCreate) {
if (typeof article.data[field] !== 'undefined' || forceCreate) {
article.data[field] = Article.formatDate(new Date());
}
return article;

View File

@@ -1,8 +1,8 @@
import { commands, ExtensionContext } from 'vscode';
import { COMMAND_NAME, CONTEXT } from '../constants';
import { Extension } from '../helpers';
import { Credentials } from "../services/Credentials";
import fetch from "node-fetch";
import { Credentials } from '../services/Credentials';
import fetch from 'node-fetch';
import { ExplorerView } from '../explorerView/ExplorerView';
import { Dashboard } from './Dashboard';
import { SettingsListener } from '../listeners/panel';
@@ -26,16 +26,16 @@ export class Backers {
public static async tryUsernameCheck() {
try {
const username = await Backers.getUsername();
Backers.validate(username || "");
Backers.validate(username || '');
} catch (e) {
Backers.validate("");
Backers.validate('');
}
}
public static async getUsername() {
const octokit = await Backers.creds?.getOctokit();
const user = await octokit?.users.getAuthenticated();
if (user?.data?.login) {
return user?.data?.login;
}
@@ -52,7 +52,9 @@ export class Backers {
const isBeta = ext.isBetaVersion();
const response = await fetch(`https://${isBeta ? `beta.` : ``}frontmatter.codes/api/backers?backer=${username}`);
const response = await fetch(
`https://${isBeta ? `beta.` : ``}frontmatter.codes/api/backers?backer=${username}`
);
if (response.ok) {
const prevData = await ext.getState<boolean>(CONTEXT.backer, 'global');
@@ -63,7 +65,7 @@ export class Backers {
if (explorerView.visible) {
SettingsListener.getSettings();
}
if (Dashboard.isOpen) {
Dashboard.reload();
}
@@ -72,4 +74,4 @@ export class Backers {
ext.setState(CONTEXT.backer, false, 'global');
}
}
}
}

32
src/commands/Cache.ts Normal file
View File

@@ -0,0 +1,32 @@
import { commands } from 'vscode';
import { COMMAND_NAME, ExtensionState } from '../constants';
import { Extension, Notifications } from '../helpers';
export class Cache {
public static async registerCommands() {
const ext = Extension.getInstance();
const subscriptions = ext.subscriptions;
subscriptions.push(commands.registerCommand(COMMAND_NAME.clearCache, Cache.clear));
}
public static async get<T>(key: string, type: 'workspace' | 'global'): Promise<T | undefined> {
const ext = Extension.getInstance();
const cache = await ext.getState<T>(key, type);
return cache || undefined;
}
public static async set(key: string, data: unknown, type: 'workspace' | 'global' = 'workspace') {
await Extension.getInstance().setState(key, data, type);
}
private static async clear() {
const ext = Extension.getInstance();
await ext.setState(ExtensionState.Dashboard.Pages.Cache, undefined, 'workspace', true);
await ext.setState(ExtensionState.Dashboard.Pages.Index, undefined, 'workspace', true);
await ext.setState(ExtensionState.Settings.Extends, undefined, 'workspace', true);
Notifications.info('Cache cleared');
}
}

View File

@@ -1,9 +1,8 @@
import { commands, QuickPickItem, window } from 'vscode';
import { commands, QuickPickItem, window } from 'vscode';
import { COMMAND_NAME, SETTING_TEMPLATES_ENABLED } from '../constants';
import { Settings } from '../helpers';
export class Content {
public static async create() {
const templatesEnabled = await Settings.get(SETTING_TEMPLATES_ENABLED);
if (!templatesEnabled) {
@@ -11,16 +10,19 @@ export class Content {
return;
}
const options: QuickPickItem[] = [{
label: "Create content by content type",
description: "Select if you want to create new content by the available content type(s)"
}, {
label: "Create content by template",
description: "Select if you want to create new content by the available template(s)"
} as QuickPickItem];
const options: QuickPickItem[] = [
{
label: 'Create content by content type',
description: 'Select if you want to create new content by the available content type(s)'
},
{
label: 'Create content by template',
description: 'Select if you want to create new content by the available template(s)'
} as QuickPickItem
];
const selectedOption = await window.showQuickPick(options, {
title: "Create content",
title: 'Create content',
placeHolder: `Select how you want to create your new content`,
canPickMany: false,
ignoreFocusOut: true
@@ -36,4 +38,4 @@ export class Content {
return;
}
}
}

View File

@@ -1,20 +1,35 @@
import { SETTING_DASHBOARD_OPENONSTART, CONTEXT, ExtensionState } from '../constants';
import { join } from "path";
import { commands, Uri, ViewColumn, Webview, WebviewPanel, window } from "vscode";
import {
SETTING_DASHBOARD_OPENONSTART,
CONTEXT,
ExtensionState,
SETTING_EXPERIMENTAL
} from '../constants';
import { join } from 'path';
import { commands, Uri, ViewColumn, Webview, WebviewPanel, window } from 'vscode';
import { Logger, Settings as SettingsHelper } from '../helpers';
import { DashboardCommand } from '../dashboardWebView/DashboardCommand';
import { Extension } from '../helpers/Extension';
import { WebviewHelper } from '@estruyf/vscode';
import { DashboardData } from '../models/DashboardData';
import { MediaLibrary } from '../helpers/MediaLibrary';
import { DashboardListener, MediaListener, SettingsListener, TelemetryListener, DataListener, PagesListener, ExtensionListener, SnippetListener, TaxonomyListener } from '../listeners/dashboard';
import { MediaListener as PanelMediaListener } from '../listeners/panel'
import {
DashboardListener,
MediaListener,
SettingsListener,
TelemetryListener,
DataListener,
PagesListener,
ExtensionListener,
SnippetListener,
TaxonomyListener,
LogListener
} from '../listeners/dashboard';
import { MediaListener as PanelMediaListener } from '../listeners/panel';
import { GitListener, ModeListener } from '../listeners/general';
export class Dashboard {
private static webview: WebviewPanel | null = null;
private static _viewData: DashboardData | undefined;
private static isDisposed: boolean = true;
private static isDisposed = true;
public static get viewData(): DashboardData | undefined {
return Dashboard._viewData;
@@ -34,15 +49,13 @@ export class Dashboard {
* Open or reveal the dashboard
*/
public static async open(data?: DashboardData) {
MediaLibrary.getInstance();
Dashboard._viewData = data;
if (Dashboard.isOpen) {
Dashboard.reveal(!!data);
} else {
Dashboard.create();
}
Dashboard.reveal(!!data);
} else {
Dashboard.create();
}
await commands.executeCommand('setContext', CONTEXT.isDashboardOpen, true);
}
@@ -57,12 +70,15 @@ export class Dashboard {
/**
* Reveal the dashboard if it is open
*/
public static reveal(hasData: boolean = false) {
public static reveal(hasData = false) {
if (Dashboard.webview) {
Dashboard.webview.reveal();
if (hasData) {
Dashboard.postWebviewMessage({ command: DashboardCommand.viewData, data: Dashboard.viewData });
Dashboard.postWebviewMessage({
command: DashboardCommand.viewData,
data: Dashboard.viewData
});
}
}
}
@@ -74,7 +90,11 @@ export class Dashboard {
public static reload() {
if (Dashboard.isOpen) {
Dashboard.webview?.dispose();
Extension.getInstance().setState(ExtensionState.Dashboard.Pages.Cache, undefined, "workspace")
Extension.getInstance().setState(
ExtensionState.Dashboard.Pages.Cache,
undefined,
'workspace'
);
setTimeout(() => {
Dashboard.open();
@@ -85,7 +105,7 @@ export class Dashboard {
public static resetViewData() {
Dashboard._viewData = undefined;
}
/**
* Create the dashboard webview
*/
@@ -110,14 +130,20 @@ export class Dashboard {
light: Uri.file(join(extensionUri.fsPath, 'assets/icons/frontmatter-short-light.svg'))
};
Dashboard.webview.webview.html = Dashboard.getWebviewContent(Dashboard.webview.webview, extensionUri);
Dashboard.webview.webview.html = Dashboard.getWebviewContent(
Dashboard.webview.webview,
extensionUri
);
Dashboard.webview.onDidChangeViewState(async () => {
if (!this.webview?.visible) {
Dashboard._viewData = undefined;
PanelMediaListener.getMediaSelection();
Dashboard.postWebviewMessage({ command: DashboardCommand.viewData, data: null });
Dashboard.postWebviewMessage({
command: DashboardCommand.viewData,
data: null
});
}
await commands.executeCommand('setContext', CONTEXT.isDashboardOpen, this.webview?.visible);
@@ -130,13 +156,13 @@ export class Dashboard {
await commands.executeCommand('setContext', CONTEXT.isDashboardOpen, false);
});
SettingsHelper.onConfigChange((global?: any) => {
SettingsListener.getSettings();
SettingsHelper.onConfigChange(() => {
SettingsListener.getSettings(true);
});
Dashboard.webview.webview.onDidReceiveMessage(async (msg) => {
Logger.info(`Receiving message from webview: ${msg.command}`);
DashboardListener.process(msg);
ExtensionListener.process(msg);
MediaListener.process(msg);
@@ -148,6 +174,7 @@ export class Dashboard {
ModeListener.process(msg);
GitListener.process(msg);
TaxonomyListener.process(msg);
LogListener.process(msg);
});
}
@@ -161,9 +188,9 @@ export class Dashboard {
/**
* Post data to the dashboard
* @param msg
* @param msg
*/
public static postWebviewMessage(msg: { command: DashboardCommand, data?: any }) {
public static postWebviewMessage(msg: { command: DashboardCommand; data?: unknown }) {
if (Dashboard.isDisposed) {
return;
}
@@ -172,22 +199,24 @@ export class Dashboard {
Dashboard.webview?.webview.postMessage(msg);
}
}
/**
* Retrieve the webview HTML contents
* @param webView
* @param webView
*/
private static getWebviewContent(webView: Webview, extensionPath: Uri): string {
const dashboardFile = "dashboardWebView.js";
const dashboardFile = 'dashboardWebView.js';
const localPort = `9000`;
const localServerUrl = `localhost:${localPort}`;
let scriptUri = "";
let scriptUri = '';
const isProd = Extension.getInstance().isProductionMode;
if (isProd) {
scriptUri = webView.asWebviewUri(Uri.joinPath(extensionPath, 'dist', dashboardFile)).toString();
scriptUri = webView
.asWebviewUri(Uri.joinPath(extensionPath, 'dist', dashboardFile))
.toString();
} else {
scriptUri = `http://${localServerUrl}/${dashboardFile}`;
scriptUri = `http://${localServerUrl}/${dashboardFile}`;
}
const nonce = WebviewHelper.getNonce();
@@ -196,13 +225,27 @@ export class Dashboard {
const version = ext.getVersion();
const isBeta = ext.isBetaVersion();
// Get experimental setting
const experimental = SettingsHelper.get(SETTING_EXPERIMENTAL);
const csp = [
`default-src 'none';`,
`img-src ${`vscode-file://vscode-app`} ${webView.cspSource} https://api.visitorbadge.io 'self' 'unsafe-inline'`,
`script-src ${isProd ? `'nonce-${nonce}'` : `http://${localServerUrl} http://0.0.0.0:${localPort}`} 'unsafe-eval'`,
`img-src ${`vscode-file://vscode-app`} ${
webView.cspSource
} https://api.visitorbadge.io 'self' 'unsafe-inline' https://*`,
`media-src ${`vscode-file://vscode-app`} ${
webView.cspSource
} 'self' 'unsafe-inline' https://*`,
`script-src ${
isProd ? `'nonce-${nonce}'` : `http://${localServerUrl} http://0.0.0.0:${localPort}`
} 'unsafe-eval'`,
`style-src ${webView.cspSource} 'self' 'unsafe-inline'`,
`font-src ${webView.cspSource}`,
`connect-src https://o1022172.ingest.sentry.io ${isProd ? `` : `ws://${localServerUrl} ws://0.0.0.0:${localPort} http://${localServerUrl} http://0.0.0.0:${localPort}`}`
`connect-src https://o1022172.ingest.sentry.io ${
isProd
? ``
: `ws://${localServerUrl} ws://0.0.0.0:${localPort} http://${localServerUrl} http://0.0.0.0:${localPort}`
}`
];
return `
@@ -215,14 +258,18 @@ export class Dashboard {
<title>Front Matter Dashboard</title>
</head>
<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" data-isProd="${isProd}" data-environment="${isBeta ? "BETA" : "main"}" data-version="${version.usedVersion}" style="width:100%;height:100%;margin:0;padding:0;" ${version.usedVersion ? "" : `data-showWelcome="true"`}></div>
<body style="width:100%;height:100%;margin:0;padding:0;overflow:hidden">
<div id="app" class="bg-gray-100 text-vulcan-500 dark:bg-vulcan-500 dark:text-whisper-500" data-isProd="${isProd}" data-environment="${
isBeta ? 'BETA' : 'main'
}" data-version="${version.usedVersion}" style="width:100%;height:100%;margin:0;padding:0;" ${
version.usedVersion ? '' : `data-showWelcome="true"`
} ${experimental ? `data-experimental="${experimental}"` : ''} ></div>
<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 ${isProd ? `nonce="${nonce}"` : ""} src="${scriptUri}"></script>
<script ${isProd ? `nonce="${nonce}"` : ''} src="${scriptUri}"></script>
</body>
</html>
`;
}
}
}

View File

@@ -1,12 +1,11 @@
import { Folders } from "./Folders";
import { ViewColumn, workspace } from "vscode";
import ContentProvider from "../providers/ContentProvider";
import { join } from "path";
import { ContentFolder } from "../models";
import { Folders } from './Folders';
import { ViewColumn, workspace } from 'vscode';
import ContentProvider from '../providers/ContentProvider';
import { join } from 'path';
import { ContentFolder } from '../models';
import { Settings } from '../helpers/SettingsHelper';
export class Diagnostics {
public static async show() {
const folders = Folders.get();
const projectName = Folders.getProjectFolderName();
@@ -25,11 +24,11 @@ ${projectName}
# Folders
${folders.map(f => `- ${f.title}: "${f.path}"`).join("\n")}
${folders.map((f) => `- ${f.title}: "${f.path}"`).join('\n')}
# Workspace folder
${wsFolder ? wsFolder.fsPath : "No workspace folder"}
${wsFolder ? wsFolder.fsPath : 'No workspace folder'}
# Total files
@@ -37,10 +36,16 @@ ${all}
# Folders to search files
${folderData.join("\n")}
${folderData.join('\n')}
# Complete frontmatter.json config
\`\`\`json
${JSON.stringify(Settings.globalConfig, null, 2)}
\`\`\`
`;
ContentProvider.show(logging, `${projectName} diagnostics`, "markdown", ViewColumn.One);
ContentProvider.show(logging, `${projectName} diagnostics`, 'markdown', ViewColumn.One);
}
private static async allProjectFiles() {
@@ -50,14 +55,26 @@ ${folderData.join("\n")}
private static async processFolder(folder: ContentFolder, projectName: string) {
let projectStart = folder.path.split(projectName).pop();
projectStart = projectStart || "";
projectStart = projectStart || '';
projectStart = projectStart?.replace(/\\/g, '/');
projectStart = projectStart?.startsWith('/') ? projectStart.substring(1) : projectStart;
const mdFiles = await workspace.findFiles(join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.md'));
const mdxFiles = await workspace.findFiles(join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.mdx'));
const markdownFiles = await workspace.findFiles(join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.markdown'));
const mdFiles = await workspace.findFiles(
join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.md')
);
const mdxFiles = await workspace.findFiles(
join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.mdx')
);
const markdownFiles = await workspace.findFiles(
join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.markdown')
);
return `- Project start length: ${projectStart.length} | Search in: "${join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.*')}" | mdFiles: ${mdFiles.length} | mdxFiles: ${mdxFiles.length} | markdownFiles: ${markdownFiles.length}`;
return `- Project start length: ${projectStart.length} | Search in: "${join(
projectStart,
folder.excludeSubdir ? '/' : '**/',
'*.*'
)}" | mdFiles: ${mdFiles.length} | mdxFiles: ${mdxFiles.length} | markdownFiles: ${
markdownFiles.length
}`;
}
}
}

View File

@@ -1,13 +1,20 @@
import { STATIC_FOLDER_PLACEHOLDER } from './../constants/StaticFolderPlaceholder';
import { Questions } from './../helpers/Questions';
import { SETTING_CONTENT_PAGE_FOLDERS, SETTING_CONTENT_STATIC_FOLDER, SETTING_CONTENT_SUPPORTED_FILETYPES, TelemetryEvent } from './../constants';
import { commands, Uri, workspace, window } from "vscode";
import { basename, dirname, join, relative, sep } from "path";
import { ContentFolder, FileInfo, FolderInfo } from "../models";
import uniqBy = require("lodash.uniqby");
import { Template } from "./Template";
import { Notifications } from "../helpers/Notifications";
import { Logger, Settings } from "../helpers";
import { existsSync, mkdirSync } from 'fs';
import {
SETTING_CONTENT_PAGE_FOLDERS,
SETTING_CONTENT_STATIC_FOLDER,
SETTING_CONTENT_SUPPORTED_FILETYPES,
SETTING_DATE_FORMAT,
TelemetryEvent
} from './../constants';
import { commands, Uri, workspace, window } from 'vscode';
import { basename, dirname, join, relative, sep } from 'path';
import { ContentFolder, FileInfo, FolderInfo } from '../models';
import uniqBy = require('lodash.uniqby');
import { Template } from './Template';
import { Notifications } from '../helpers/Notifications';
import { Logger, processKnownPlaceholders, Settings } from '../helpers';
import { existsSync } from 'fs';
import { format } from 'date-fns';
import { Dashboard } from './Dashboard';
import { parseWinPath } from '../helpers/parseWinPath';
@@ -16,32 +23,40 @@ import { MediaListener, PagesListener, SettingsListener } from '../listeners/das
import { DEFAULT_FILE_TYPES } from '../constants/DefaultFileTypes';
import { Telemetry } from '../helpers/Telemetry';
import { glob } from 'glob';
import { mkdirAsync } from '../utils/mkdirAsync';
import { existsAsync } from '../utils';
export const WORKSPACE_PLACEHOLDER = `[[workspace]]`;
export class Folders {
/**
* Add a media folder
* @returns
* @returns
*/
public static async addMediaFolder(data?: {selectedFolder?: string}) {
let wsFolder = Folders.getWorkspaceFolder();
let staticFolder = Folders.getStaticFolderRelativePath();
public static async addMediaFolder(data?: { selectedFolder?: string }) {
const wsFolder = Folders.getWorkspaceFolder();
const staticFolder = Folders.getStaticFolderRelativePath();
let startPath = "";
let startPath = '';
if (data?.selectedFolder) {
startPath = data.selectedFolder.replace(parseWinPath(wsFolder?.fsPath || ""), "");
startPath = data.selectedFolder.replace(parseWinPath(wsFolder?.fsPath || ''), '');
} else if (staticFolder) {
startPath = `/${staticFolder}`;
}
if (startPath && !startPath.endsWith("/")) {
startPath += "/";
if (startPath && !startPath.endsWith('/')) {
startPath += '/';
}
const folderName = await window.showInputBox({
if (startPath.includes(STATIC_FOLDER_PLACEHOLDER.hexo.placeholder)) {
startPath = startPath.replace(
STATIC_FOLDER_PLACEHOLDER.hexo.placeholder,
STATIC_FOLDER_PLACEHOLDER.hexo.postsFolder
);
}
const folderName = await window.showInputBox({
title: `Add media folder`,
prompt: `Which name would you like to give to your folder (use "/" to create multi-level folders)?`,
value: startPath,
@@ -53,23 +68,18 @@ export class Folders {
Notifications.warning(`No folder name was specified.`);
return;
}
const folders = folderName.split("/").filter(f => f);
let parentFolders: string[] = [];
for (const folder of folders) {
const folderPath = join(parseWinPath(wsFolder?.fsPath || ""), parentFolders.join("/"), folder);
await Folders.createFolder(join(parseWinPath(wsFolder?.fsPath || ''), folderName));
}
parentFolders.push(folder);
if (!existsSync(folderPath)) {
mkdirSync(folderPath);
}
public static async createFolder(folderPath: string) {
if (!(await existsAsync(folderPath))) {
await mkdirAsync(folderPath, { recursive: true });
}
if (Dashboard.isOpen) {
MediaHelpers.resetMedia();
MediaListener.sendMediaFiles(0, folderName);
MediaListener.sendMediaFiles(0, folderPath);
}
Telemetry.send(TelemetryEvent.addMediaFolder);
@@ -77,7 +87,7 @@ export class Folders {
/**
* Create content in a registered folder
* @returns
* @returns
*/
public static async create() {
const selectedFolder = await Questions.SelectContentFolder();
@@ -86,7 +96,7 @@ export class Folders {
}
const folders = Folders.get();
const location = folders.find(f => f.title === selectedFolder);
const location = folders.find((f) => f.title === selectedFolder);
if (location) {
const folderPath = Folders.getFolderPath(Uri.file(location.path));
if (folderPath) {
@@ -97,9 +107,9 @@ export class Folders {
/**
* Register the new folder path
* @param folderInfo
* @param folderInfo
*/
public static async register(folderInfo: { title: string, path: Uri } | Uri) {
public static async register(folderInfo: { title: string; path: Uri } | Uri) {
let folderName = folderInfo instanceof Uri ? undefined : folderInfo.title;
const folder = folderInfo instanceof Uri ? folderInfo : folderInfo.path;
@@ -108,7 +118,9 @@ export class Folders {
let folders = Folders.get();
const exists = folders.find(f => f.path.includes(folder.fsPath) || f.path.includes(wslPath));
const exists = folders.find(
(f) => f.path.includes(folder.fsPath) || f.path.includes(wslPath)
);
if (exists) {
Notifications.warning(`Folder is already registered`);
@@ -117,7 +129,7 @@ export class Folders {
if (!folderName) {
folderName = await window.showInputBox({
title: `Register folder`,
title: `Register folder`,
prompt: `Which name would you like to specify for this folder?`,
placeHolder: `Folder name`,
value: basename(folder.fsPath),
@@ -130,34 +142,34 @@ export class Folders {
path: folder.fsPath
} as ContentFolder);
folders = uniqBy(folders, f => f.path);
folders = uniqBy(folders, (f) => f.path);
await Folders.update(folders);
Notifications.info(`Folder registered`);
Telemetry.send(TelemetryEvent.registerFolder);
Telemetry.send(TelemetryEvent.registerFolder);
SettingsListener.getSettings();
SettingsListener.getSettings(true);
}
}
/**
* Unregister a folder path
* @param folder
* @param folder
*/
public static async unregister(folder: Uri) {
if (folder && folder.path) {
let folders = Folders.get();
folders = folders.filter(f => f.path !== folder.fsPath);
folders = folders.filter((f) => f.path !== folder.fsPath);
await Folders.update(folders);
Telemetry.send(TelemetryEvent.unregisterFolder);
Telemetry.send(TelemetryEvent.unregisterFolder);
}
}
/**
* Get the static folder its relative path
* @returns
* @returns
*/
public static getStaticFolderRelativePath(): string | undefined {
let staticFolder = Settings.get<string>(SETTING_CONTENT_STATIC_FOLDER);
@@ -176,17 +188,17 @@ export class Folders {
/**
* Retrieve the folder path
* @param folder
* @returns
* @param folder
* @returns
*/
public static getFolderPath(folder: Uri) {
let folderPath = "";
let folderPath = '';
const wsFolder = Folders.getWorkspaceFolder();
if (folder && folder.fsPath) {
folderPath = folder.fsPath;
} else if (wsFolder) {
folderPath = wsFolder.fsPath;
}
if (folder && folder.fsPath) {
folderPath = folder.fsPath;
} else if (wsFolder) {
folderPath = wsFolder.fsPath;
}
return folderPath;
}
@@ -195,12 +207,12 @@ export class Folders {
*/
public static getWorkspaceFolder(): Uri | undefined {
const folders = workspace.workspaceFolders;
if (folders && folders.length === 1) {
return folders[0].uri;
} else if (folders && folders.length > 1) {
let projectFolder = undefined;
let projectFolder = undefined;
for (const folder of folders) {
if (!projectFolder && existsSync(join(folder.uri.fsPath, Settings.globalFile))) {
projectFolder = folder.uri;
@@ -208,20 +220,22 @@ export class Folders {
}
if (!projectFolder) {
window.showWorkspaceFolderPick({
placeHolder: `Please select the main workspace folder for Front Matter to use.`
}).then(selectedFolder => {
if (selectedFolder) {
Settings.createGlobalFile(selectedFolder.uri);
// Full reload to make sure the whole extension is reloaded correctly
commands.executeCommand(`workbench.action.reloadWindow`);
}
});
window
.showWorkspaceFolderPick({
placeHolder: `Please select the main workspace folder for Front Matter to use.`
})
.then(async (selectedFolder) => {
if (selectedFolder) {
await Settings.createGlobalFile(selectedFolder.uri);
// Full reload to make sure the whole extension is reloaded correctly
commands.executeCommand(`workbench.action.reloadWindow`);
}
});
}
return projectFolder;
}
return undefined;
}
@@ -233,7 +247,7 @@ export class Folders {
if (wsFolder) {
return basename(wsFolder.fsPath);
}
return "";
return '';
}
/**
@@ -242,26 +256,36 @@ export class Folders {
public static async getInfo(limit?: number): Promise<FolderInfo[] | null> {
const supportedFiles = Settings.get<string[]>(SETTING_CONTENT_SUPPORTED_FILETYPES);
const folders = Folders.get();
const wsFolder = parseWinPath(Folders.getWorkspaceFolder()?.fsPath || '');
if (folders && folders.length > 0) {
let folderInfo: FolderInfo[] = [];
const folderInfo: FolderInfo[] = [];
for (const folder of folders) {
try {
const projectName = Folders.getProjectFolderName();
let projectStart = folder.path.split(projectName).pop();
if (projectStart) {
let projectStart = parseWinPath(folder.path).replace(wsFolder, '');
if (typeof projectStart === 'string') {
projectStart = projectStart.replace(/\\/g, '/');
projectStart = projectStart.startsWith('/') ? projectStart.substring(1) : projectStart;
let files: Uri[] = [];
for (const fileType of (supportedFiles || DEFAULT_FILE_TYPES)) {
const filePath = join(projectStart, folder.excludeSubdir ? '/' : '**', `*${fileType.startsWith('.') ? '' : '.'}${fileType}`);
for (const fileType of supportedFiles || DEFAULT_FILE_TYPES) {
let filePath = join(
projectStart,
folder.excludeSubdir ? '/' : '**',
`*${fileType.startsWith('.') ? '' : '.'}${fileType}`
);
if (projectStart === '' && folder.excludeSubdir) {
filePath = `*${fileType.startsWith('.') ? '' : '.'}${fileType}`;
}
const foundFiles = await workspace.findFiles(filePath, '**/node_modules/**');
files = [...files, ...foundFiles];
}
if (files) {
let fileStats: FileInfo[] = [];
@@ -269,7 +293,7 @@ export class Folders {
try {
const fileName = basename(file.fsPath);
const folderName = dirname(file.fsPath).split(sep).pop();
const stats = await workspace.fs.stat(file);
fileStats.push({
@@ -284,7 +308,7 @@ export class Folders {
}
fileStats = fileStats.sort((a, b) => b.mtime - a.mtime);
if (limit) {
fileStats = fileStats.slice(0, limit);
}
@@ -309,47 +333,59 @@ export class Folders {
/**
* Get the folder settings
* @returns
* @returns
*/
public static get(): ContentFolder[] {
const wsFolder = Folders.getWorkspaceFolder();
const folders: ContentFolder[] = Settings.get(SETTING_CONTENT_PAGE_FOLDERS) as ContentFolder[];
const contentFolders = folders.map(folder => {
const contentFolders = folders.map((folder) => {
if (!folder.title) {
folder.title = basename(folder.path);
}
let folderPath = Folders.absWsFolder(folder, wsFolder);
if (!existsSync(folderPath)) {
Notifications.errorShowOnce(`Folder "${folder.title} (${folder.path})" does not exist. Please remove it from the settings.`, "Remove folder").then(answer => {
if (answer === "Remove folder") {
let folders = Folders.get();
Folders.update(folders.filter(f => f.path !== folder.path));
}
});
return null;
let folderPath: string | undefined = Folders.absWsFolder(folder, wsFolder);
if (folderPath.includes(`{{`) && folderPath.includes(`}}`)) {
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
folderPath = processKnownPlaceholders(folderPath, undefined, dateFormat);
} else {
if (folderPath && !existsSync(folderPath)) {
Notifications.errorShowOnce(
`Folder "${folder.title} (${folder.path})" does not exist. Please remove it from the settings.`,
'Remove folder',
'Create folder'
).then((answer) => {
if (answer === 'Remove folder') {
const folders = Folders.get();
Folders.update(folders.filter((f) => f.path !== folder.path));
} else if (answer === 'Create folder') {
mkdirAsync(folderPath as string, { recursive: true });
}
});
return null;
}
}
return {
...folder,
originalPath: folder.path,
path: folderPath
}
})
return contentFolders.filter(folder => folder !== null) as ContentFolder[];
};
});
return contentFolders.filter((folder) => folder !== null) as ContentFolder[];
}
/**
* Update the folder settings
* @param folders
* @param folders
*/
public static async update(folders: ContentFolder[]) {
const wsFolder = Folders.getWorkspaceFolder();
let folderDetails = folders.map(folder => ({
const folderDetails = folders.map((folder) => ({
...folder,
path: Folders.relWsFolder(folder, wsFolder)
path: Folders.relWsFolder(folder, wsFolder)
}));
await Settings.update(SETTING_CONTENT_PAGE_FOLDERS, folderDetails, true);
@@ -360,39 +396,42 @@ export class Folders {
/**
* Retrieve the absolute file path
* @param filePath
* @returns
* @param filePath
* @returns
*/
public static getAbsFilePath(filePath: string): string {
const wsFolder = Folders.getWorkspaceFolder();
const isWindows = process.platform === 'win32';
let absPath = filePath.replace(WORKSPACE_PLACEHOLDER, parseWinPath(wsFolder?.fsPath || ""));
let absPath = filePath.replace(WORKSPACE_PLACEHOLDER, parseWinPath(wsFolder?.fsPath || ''));
absPath = isWindows ? absPath.split('/').join('\\') : absPath;
return parseWinPath(absPath);
}
/**
* Generate the absolute URL for the workspace
* @param folder
* @param wsFolder
* @returns
* @param folder
* @param wsFolder
* @returns
*/
private static absWsFolder(folder: ContentFolder, wsFolder?: Uri) {
const isWindows = process.platform === 'win32';
let absPath = folder.path.replace(WORKSPACE_PLACEHOLDER, parseWinPath(wsFolder?.fsPath || ""));
let absPath = folder.path.replace(WORKSPACE_PLACEHOLDER, parseWinPath(wsFolder?.fsPath || ''));
absPath = isWindows ? absPath.split('/').join('\\') : absPath;
return parseWinPath(absPath);
}
/**
* Generate relative folder path
* @param folder
* @param wsFolder
* @returns
* @param folder
* @param wsFolder
* @returns
*/
public static relWsFolder(folder: ContentFolder, wsFolder?: Uri) {
const isWindows = process.platform === 'win32';
let absPath = parseWinPath(folder.path).replace(parseWinPath(wsFolder?.fsPath || ""), WORKSPACE_PLACEHOLDER);
let absPath = parseWinPath(folder.path).replace(
parseWinPath(wsFolder?.fsPath || ''),
WORKSPACE_PLACEHOLDER
);
absPath = isWindows ? absPath.split('\\').join('/') : absPath;
return absPath;
}
@@ -403,36 +442,92 @@ export class Folders {
public static async getContentFolders() {
// Find folders that contain files
const wsFolder = Folders.getWorkspaceFolder();
const supportedFiles = Settings.get<string[]>(SETTING_CONTENT_SUPPORTED_FILETYPES) || DEFAULT_FILE_TYPES;
const patterns = supportedFiles.map(fileType => `${join(parseWinPath(wsFolder?.fsPath || ""), "**", `*${fileType.startsWith('.') ? '' : '.'}${fileType}`)}`);
const supportedFiles =
Settings.get<string[]>(SETTING_CONTENT_SUPPORTED_FILETYPES) || DEFAULT_FILE_TYPES;
const patterns = supportedFiles.map(
(fileType) =>
`${join(
parseWinPath(wsFolder?.fsPath || ''),
'**',
`*${fileType.startsWith('.') ? '' : '.'}${fileType}`
)}`
);
let folders: string[] = [];
for (const pattern of patterns) {
try {
folders = [...folders, ...(await this.findFolders(pattern))];
} catch (e) {
Logger.error(`Something went wrong while searching for folders with pattern "${pattern}": ${(e as Error).message}`);
Logger.error(
`Something went wrong while searching for folders with pattern "${pattern}": ${
(e as Error).message
}`
);
}
}
// Filter out the workspace folder
if (wsFolder) {
folders = folders.filter(folder => folder !== wsFolder.fsPath);
folders = folders.filter((folder) => folder !== wsFolder.fsPath);
}
const uniqueFolders = [...new Set(folders)];
return uniqueFolders.map(folder => relative(wsFolder?.path || "", folder));
return uniqueFolders.map((folder) => relative(wsFolder?.path || '', folder));
}
/**
* Returns the file prefix for the given folder path
* @param folderPath
* @returns
*/
public static getFilePrefixByFolderPath(folderPath: string) {
const folders = Folders.get();
const pageFolder = folders.find((f) => parseWinPath(f.path) === parseWinPath(folderPath));
if (pageFolder && typeof pageFolder.filePrefix !== 'undefined') {
return pageFolder.filePrefix;
}
return;
}
/**
* Returns the file prefix for the given file path
* @param filePath
* @returns
*/
public static getFilePrefixBeFilePath(filePath: string) {
const folders = Folders.get();
if (folders.length > 0) {
filePath = parseWinPath(filePath);
let selectedFolder: ContentFolder | null = null;
for (const folder of folders) {
const folderPath = parseWinPath(folder.path);
if (filePath.startsWith(folderPath)) {
if (!selectedFolder || selectedFolder.path.length < folderPath.length) {
selectedFolder = folder;
}
}
}
if (selectedFolder && typeof selectedFolder.filePrefix !== 'undefined') {
return selectedFolder.filePrefix;
}
}
return;
}
/**
* Retrieve all content folders
* @param pattern
* @returns
* @param pattern
* @returns
*/
private static findFolders(pattern: string): Promise<string[]> {
return new Promise(resolve => {
glob(pattern, { ignore: "**/node_modules/**" }, (err, files) => {
const allFolders = files.map(file => dirname(file));
return new Promise((resolve) => {
glob(pattern, { ignore: '**/node_modules/**' }, (err, files) => {
const allFolders = files.map((file) => dirname(file));
const uniqueFolders = [...new Set(allFolders)];
resolve(uniqueFolders);
});

View File

@@ -1,10 +1,20 @@
import { processFmPlaceholders } from './../helpers/processFmPlaceholders';
import { processPathPlaceholders } from './../helpers/processPathPlaceholders';
import { Telemetry } from './../helpers/Telemetry';
import { SETTING_PREVIEW_HOST, SETTING_PREVIEW_PATHNAME, CONTEXT, TelemetryEvent, PreviewCommands } from './../constants';
import {
SETTING_PREVIEW_HOST,
SETTING_PREVIEW_PATHNAME,
CONTEXT,
TelemetryEvent,
PreviewCommands,
SETTING_EXPERIMENTAL,
SETTING_DATE_FORMAT
} from './../constants';
import { ArticleHelper } from './../helpers/ArticleHelper';
import { join } from "path";
import { commands, env, Uri, ViewColumn, window } from "vscode";
import { Extension, parseWinPath, Settings } from '../helpers';
import { ContentFolder, PreviewSettings } from '../models';
import { join } from 'path';
import { commands, env, Uri, ViewColumn, window } from 'vscode';
import { Extension, parseWinPath, processKnownPlaceholders, Settings } from '../helpers';
import { ContentFolder, ContentType, PreviewSettings } from '../models';
import { format } from 'date-fns';
import { DateHelper } from '../helpers/DateHelper';
import { Article } from '.';
@@ -12,9 +22,7 @@ import { urlJoin } from 'url-join-ts';
import { WebviewHelper } from '@estruyf/vscode';
import { Folders } from './Folders';
export class Preview {
/**
* Init the preview
*/
@@ -22,7 +30,7 @@ export class Preview {
const settings = Preview.getSettings();
await commands.executeCommand('setContext', CONTEXT.canOpenPreview, !!settings.host);
}
/**
* Open the markdown preview in the editor
*/
@@ -32,20 +40,26 @@ export class Preview {
if (!settings.host) {
return;
}
const editor = window.activeTextEditor;
const article = editor ? ArticleHelper.getFrontMatter(editor) : null;
let slug = article?.data ? article.data.slug : "";
let slug = article?.data ? article.data.slug : '';
let pathname = settings.pathname;
let selectedFolder: ContentFolder | undefined | null = null;
const filePath = parseWinPath(editor?.document.uri.fsPath);
let contentType: ContentType | undefined = undefined;
if (article?.data) {
contentType = ArticleHelper.getContentType(article.data);
}
// Check if there is a pathname defined on content folder level
const folders = Folders.get();
if (folders.length > 0) {
const foldersWithPath = folders.filter(folder => folder.previewPath);
const filePath = parseWinPath(editor?.document.uri.fsPath);
const foldersWithPath = folders.filter((folder) => folder.previewPath);
let selectedFolder: ContentFolder | null = null;
for (const folder of foldersWithPath) {
const folderPath = parseWinPath(folder.path);
if (filePath.startsWith(folderPath)) {
@@ -55,14 +69,28 @@ export class Preview {
}
}
if (selectedFolder) {
if (!selectedFolder && article?.data && contentType && !contentType.previewPath) {
// Try to find the folder by content type
const crntFolders = folders.filter((folder) =>
folder.contentTypes?.includes((contentType as ContentType).name)
);
if (crntFolders && crntFolders.length === 1) {
selectedFolder = crntFolders[0];
} else if (crntFolders && crntFolders.length > 1) {
selectedFolder = await Preview.askUserToPickFolder(crntFolders);
} else {
selectedFolder = await Preview.askUserToPickFolder(folders.filter((f) => f.previewPath));
}
}
if (selectedFolder && selectedFolder.previewPath) {
pathname = selectedFolder.previewPath;
}
}
// Check if there is a pathname defined on content type level
if (article?.data) {
const contentType = ArticleHelper.getContentType(article.data);
if (contentType && contentType.previewPath) {
pathname = contentType.previewPath;
}
@@ -73,10 +101,35 @@ export class Preview {
}
if (pathname) {
const articleDate = ArticleHelper.getDate(article);
// Known placeholders
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
pathname = processKnownPlaceholders(pathname, article?.data?.title, dateFormat);
// Custom placeholders
pathname = await ArticleHelper.processCustomPlaceholders(
pathname,
article?.data?.title,
filePath
);
// Process the path placeholders - {{pathToken.<integer>}}
if (filePath) {
const wsFolder = Folders.getWorkspaceFolder();
// Get relative file path
const folderPath = wsFolder ? parseWinPath(wsFolder.fsPath) : '';
const relativePath = filePath.replace(folderPath, '');
pathname = processPathPlaceholders(pathname, relativePath, filePath, selectedFolder);
}
// Support front matter placeholders - {{fm.<field>}}
pathname = processFmPlaceholders(pathname, article?.data);
try {
slug = join(format(articleDate || new Date(), DateHelper.formatUpdate(pathname) as string), slug);
const articleDate = ArticleHelper.getDate(article);
slug = join(
format(articleDate || new Date(), DateHelper.formatUpdate(pathname) as string),
slug
);
} catch (error) {
slug = join(pathname, slug);
}
@@ -106,15 +159,13 @@ export class Preview {
webView.iconPath = {
dark: Uri.file(join(extensionPath, 'assets/icons/frontmatter-short-dark.svg')),
light: Uri.file(join(extensionPath, 'assets/icons/frontmatter-short-light.svg'))
}
};
const localhostUrl = await env.asExternalUri(
Uri.parse(settings.host)
);
const localhostUrl = await env.asExternalUri(Uri.parse(settings.host));
const cspSource = webView.webview.cspSource;
webView.webview.onDidReceiveMessage(message => {
webView.webview.onDidReceiveMessage((message) => {
switch (message.command) {
case PreviewCommands.toVSCode.open:
if (message.data) {
@@ -124,8 +175,7 @@ export class Preview {
}
});
const dashboardFile = "dashboardWebView.js";
const dashboardFile = 'dashboardWebView.js';
const localPort = `9000`;
const localServerUrl = `localhost:${localPort}`;
@@ -136,23 +186,34 @@ export class Preview {
const version = ext.getVersion();
const isBeta = ext.isBetaVersion();
const extensionUri = ext.extensionPath;
const csp = [
`default-src 'none';`,
`img-src ${localhostUrl} ${cspSource} http: https:;`,
`script-src ${isProd ? `'nonce-${nonce}'` : `http://${localServerUrl} http://0.0.0.0:${localPort}`} 'unsafe-eval'`,
`script-src ${
isProd ? `'nonce-${nonce}'` : `http://${localServerUrl} http://0.0.0.0:${localPort}`
} 'unsafe-eval'`,
`style-src ${cspSource} 'self' 'unsafe-inline' http: https:`,
`connect-src https://o1022172.ingest.sentry.io ${isProd ? `` : `ws://${localServerUrl} ws://0.0.0.0:${localPort} http://${localServerUrl} http://0.0.0.0:${localPort}`}`,
`frame-src ${localhostUrl} ${cspSource} http: https:;`,
`connect-src https://o1022172.ingest.sentry.io ${
isProd
? ``
: `ws://${localServerUrl} ws://0.0.0.0:${localPort} http://${localServerUrl} http://0.0.0.0:${localPort}`
}`,
`frame-src ${localhostUrl} ${cspSource} http: https:;`
];
let scriptUri = "";
let scriptUri = '';
if (isProd) {
scriptUri = webView.webview.asWebviewUri(Uri.joinPath(extensionUri, 'dist', dashboardFile)).toString();
scriptUri = webView.webview
.asWebviewUri(Uri.joinPath(extensionUri, 'dist', dashboardFile))
.toString();
} else {
scriptUri = `http://${localServerUrl}/${dashboardFile}`;
scriptUri = `http://${localServerUrl}/${dashboardFile}`;
}
// Get experimental setting
const experimental = Settings.get(SETTING_EXPERIMENTAL);
webView.webview.html = `
<!DOCTYPE html>
<html lang="en" style="width:100%;height:100%;margin:0;padding:0;">
@@ -164,9 +225,16 @@ export class Preview {
<title>Front Matter Preview</title>
</head>
<body style="width:100%;height:100%;margin:0;padding:0;overflow:hidden">
<div id="app" data-type="preview" data-url="${urlJoin(localhostUrl.toString(), slug || '')}" data-isProd="${isProd}" data-environment="${isBeta ? "BETA" : "main"}" data-version="${version.usedVersion}" style="width:100%;height:100%;margin:0;padding:0;"></div>
<div id="app" data-type="preview" data-url="${urlJoin(
localhostUrl.toString(),
slug || ''
)}" data-isProd="${isProd}" data-environment="${
isBeta ? 'BETA' : 'main'
}" data-version="${version.usedVersion}" ${
experimental ? `data-experimental="${experimental}"` : ''
} style="width:100%;height:100%;margin:0;padding:0;"></div>
<script ${isProd ? `nonce="${nonce}"` : ""} src="${scriptUri}"></script>
<script ${isProd ? `nonce="${nonce}"` : ''} src="${scriptUri}"></script>
</body>
</html>
`;
@@ -186,4 +254,32 @@ export class Preview {
pathname
};
}
/**
* Ask the user to select the folder of the article to preview
* @param crntFolders
* @returns
*/
private static async askUserToPickFolder(
crntFolders: ContentFolder[]
): Promise<ContentFolder | undefined> {
let selectedFolder: ContentFolder | undefined = undefined;
if (crntFolders.length === 0) {
return undefined;
}
// Ask the user to select the folder
const folderNames = crntFolders.map((folder) => folder.title);
const selectedFolderName = await window.showQuickPick(folderNames, {
canPickMany: false,
title: 'Select the folder of the article to preview'
});
if (selectedFolderName) {
selectedFolder = crntFolders.find((folder) => folder.title === selectedFolderName);
}
return selectedFolder;
}
}

View File

@@ -1,22 +1,25 @@
import { DEFAULT_CONTENT_TYPE } from './../constants/ContentType';
import { Telemetry } from './../helpers/Telemetry';
import { workspace, Uri } from "vscode";
import { join } from "path";
import * as fs from "fs";
import { Notifications } from "../helpers/Notifications";
import { Template } from "./Template";
import { Folders } from "./Folders";
import { FrameworkDetector, Logger, Settings } from "../helpers";
import { SETTING_CONTENT_DEFAULT_FILETYPE, SETTING_TAXONOMY_CONTENT_TYPES, TelemetryEvent } from "../constants";
import { workspace, Uri } from 'vscode';
import { join } from 'path';
import { Notifications } from '../helpers/Notifications';
import { Template } from './Template';
import { Folders } from './Folders';
import { FrameworkDetector, Logger, MediaLibrary, Settings } from '../helpers';
import {
SETTING_CONTENT_DEFAULT_FILETYPE,
SETTING_TAXONOMY_CONTENT_TYPES,
TelemetryEvent
} from '../constants';
import { SettingsListener } from '../listeners/dashboard';
import { existsAsync, writeFileAsync } from '../utils';
export class Project {
private static content = `---
title:
slug:
description:
author:
author:
date: 2019-08-22T15:20:28.000Z
lastmod: 2019-08-22T15:20:28.000Z
draft: true
@@ -26,7 +29,12 @@ categories: []
`;
public static isInitialized() {
return Settings.hasProjectFile();
const hasProjectFile = Settings.hasProjectFile();
// If it has a project file, initialize the media library
if (hasProjectFile) {
MediaLibrary.getInstance();
}
return hasProjectFile;
}
/**
@@ -34,29 +42,33 @@ categories: []
*/
public static async init(sampleTemplate?: boolean) {
try {
Settings.createTeamSettings();
await Settings.createTeamSettings();
// Add the default content type
Settings.update(SETTING_TAXONOMY_CONTENT_TYPES, [DEFAULT_CONTENT_TYPE], true);
await Settings.update(SETTING_TAXONOMY_CONTENT_TYPES, [DEFAULT_CONTENT_TYPE], true);
if (sampleTemplate !== undefined) {
await Project.createSampleTemplate();
} else {
Notifications.info("Project initialized successfully.");
Notifications.info('Project initialized successfully.');
}
// Initialize the media library
MediaLibrary.getInstance();
Telemetry.send(TelemetryEvent.initialization);
// Check if you can find the framework
const wsFolder = Folders.getWorkspaceFolder();
const framework = FrameworkDetector.get(wsFolder?.fsPath || "");
const framework = await FrameworkDetector.get(wsFolder?.fsPath || '');
if (framework) {
SettingsListener.setFramework(framework.name);
await SettingsListener.setFramework(framework.name);
}
SettingsListener.getSettings();
} catch (err: any) {
SettingsListener.getSettings(true);
} catch (error: unknown) {
const err = error as Error;
Logger.error(`Project::init: ${err?.message || err}`);
Notifications.error(`Sorry, something went wrong - ${err?.message || err}`);
}
@@ -64,8 +76,8 @@ categories: []
/**
* Creates the templates folder + sample if needed
* @param sampleTemplate
* @returns
* @param sampleTemplate
* @returns
*/
public static async createSampleTemplate(sampleTemplate?: boolean) {
const fileType = Settings.get<string>(SETTING_CONTENT_DEFAULT_FILETYPE);
@@ -76,16 +88,18 @@ categories: []
if (!folder || !templatePath) {
return;
}
const article = Uri.file(join(templatePath.fsPath, `article.${fileType}`));
if (!fs.existsSync(templatePath.fsPath)) {
if (!(await existsAsync(templatePath.fsPath))) {
await workspace.fs.createDirectory(templatePath);
}
if (sampleTemplate) {
fs.writeFileSync(article.fsPath, Project.content, { encoding: "utf-8" });
Notifications.info("Sample template created.");
await writeFileAsync(article.fsPath, Project.content, {
encoding: 'utf-8'
});
Notifications.info('Sample template created.');
}
}
@@ -103,4 +117,4 @@ categories: []
const templatePath = Uri.file(join(wsFolder.fsPath, folder));
return templatePath;
}
}
}

View File

@@ -1,34 +1,38 @@
import { TaxonomyHelper } from './../helpers/TaxonomyHelper';
import * as vscode from 'vscode';
import { TaxonomyType } from "../models";
import { TaxonomyType } from '../models';
import { SETTING_TAXONOMY_TAGS, SETTING_TAXONOMY_CATEGORIES, EXTENSION_NAME } from '../constants';
import { ArticleHelper, Settings as SettingsHelper, FilesHelper } from '../helpers';
import { FrontMatterParser } from '../parsers';
import { Notifications } from '../helpers/Notifications';
export class Settings {
/**
* Create a new taxonomy
*
* @param type
*
* @param type
*/
public static async create(type: TaxonomyType) {
const newOption = await vscode.window.showInputBox({
prompt: `Insert the value of the ${type === TaxonomyType.Tag ? "tag" : "category"} that you want to add to your configuration.`,
placeHolder: `Name of the ${type === TaxonomyType.Tag ? "tag" : "category"}`,
prompt: `Insert the value of the ${
type === TaxonomyType.Tag ? 'tag' : 'category'
} that you want to add to your configuration.`,
placeHolder: `Name of the ${type === TaxonomyType.Tag ? 'tag' : 'category'}`,
ignoreFocusOut: true
});
if (newOption) {
const configSetting = type === TaxonomyType.Tag ? SETTING_TAXONOMY_TAGS : SETTING_TAXONOMY_CATEGORIES;
const configSetting =
type === TaxonomyType.Tag ? SETTING_TAXONOMY_TAGS : SETTING_TAXONOMY_CATEGORIES;
let options = SettingsHelper.get(configSetting, true) as string[];
if (!options) {
options = [];
}
if (options.find(o => o === newOption)) {
Notifications.info(`The provided ${type === TaxonomyType.Tag ? "tag" : "category"} already exists.`);
if (options.find((o) => o === newOption)) {
Notifications.info(
`The provided ${type === TaxonomyType.Tag ? 'tag' : 'category'} already exists.`
);
return;
}
@@ -36,13 +40,15 @@ export class Settings {
await SettingsHelper.updateTaxonomy(type, options);
// Ask if the new term needs to be added to the page
const addToPage = await vscode.window.showQuickPick(["yes", "no"], {
canPickMany: false,
placeHolder: `Do you want to add the new ${type === TaxonomyType.Tag ? "tag" : "category"} to the page?`,
const addToPage = await vscode.window.showQuickPick(['yes', 'no'], {
canPickMany: false,
placeHolder: `Do you want to add the new ${
type === TaxonomyType.Tag ? 'tag' : 'category'
} to the page?`,
ignoreFocusOut: true
});
if (addToPage && addToPage === "yes") {
if (addToPage && addToPage === 'yes') {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
@@ -53,11 +59,11 @@ export class Settings {
return;
}
const matterProp: string = type === TaxonomyType.Tag ? "tags" : "categories";
const matterProp: string = type === TaxonomyType.Tag ? 'tags' : 'categories';
// Add the selected options to the options array
if (article.data[matterProp]) {
const propData: string[] = article.data[matterProp];
if (propData && !propData.find(o => o === newOption)) {
if (propData && !propData.find((o) => o === newOption)) {
propData.push(newOption);
}
} else {
@@ -69,7 +75,6 @@ export class Settings {
}
}
/**
* Export the tags/categories front matter to the user settings
*/
@@ -80,78 +85,86 @@ export class Settings {
return;
}
vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: `${EXTENSION_NAME}: exporting tags and categories`,
cancellable: false
}, async (progress) => {
// Fetching all tags and categories from MD files
let tags: string[] = [];
let categories: string[] = [];
vscode.window.withProgress(
{
location: vscode.ProgressLocation.Notification,
title: `${EXTENSION_NAME}: exporting tags and categories`,
cancellable: false
},
async (progress) => {
// Fetching all tags and categories from MD files
let tags: string[] = [];
let categories: string[] = [];
// Set the initial progress
const progressNr = allMdFiles.length/100;
progress.report({ increment: 0});
// Set the initial progress
const progressNr = allMdFiles.length / 100;
progress.report({ increment: 0 });
let i = 0;
for (const file of allMdFiles) {
progress.report({ increment: (++i/progressNr) });
const mdFile = await vscode.workspace.openTextDocument(file);
if (mdFile) {
const txtData = mdFile.getText();
if (txtData) {
try {
const article = FrontMatterParser.fromFile(txtData);
if (article && article.data) {
const { data } = article;
const mdTags = data["tags"];
const mdCategories = data["categories"];
if (mdTags) {
tags = [...tags, ...mdTags];
let i = 0;
for (const file of allMdFiles) {
progress.report({ increment: ++i / progressNr });
const mdFile = await vscode.workspace.openTextDocument(file);
if (mdFile) {
const txtData = mdFile.getText();
if (txtData) {
try {
const article = FrontMatterParser.fromFile(txtData);
if (article && article.data) {
const { data } = article;
const mdTags = data['tags'];
const mdCategories = data['categories'];
if (mdTags) {
tags = [...tags, ...mdTags];
}
if (mdCategories) {
categories = [...categories, ...mdCategories];
}
}
if (mdCategories) {
categories = [...categories, ...mdCategories];
}
}
} catch (e) {
// Continue with the next file
} catch (e) {
// Continue with the next file
}
}
}
}
// Retrieve the currently known tags, and add the new ones
let crntTags: string[] = SettingsHelper.get(SETTING_TAXONOMY_TAGS, true) as string[];
if (!crntTags) {
crntTags = [];
}
crntTags = [...crntTags, ...tags];
// Update the tags and filter out the duplicates
crntTags = [...new Set(crntTags)];
crntTags = crntTags.sort().filter((t) => !!t);
await SettingsHelper.update(SETTING_TAXONOMY_TAGS, crntTags, true);
// Retrieve the currently known tags, and add the new ones
let crntCategories: string[] = SettingsHelper.get(
SETTING_TAXONOMY_CATEGORIES,
true
) as string[];
if (!crntCategories) {
crntCategories = [];
}
crntCategories = [...crntCategories, ...categories];
// Update the categories and filter out the duplicates
crntCategories = [...new Set(crntCategories)];
crntCategories = crntCategories.sort().filter((c) => !!c);
await SettingsHelper.update(SETTING_TAXONOMY_CATEGORIES, crntCategories, true);
// Done
Notifications.info(
`Export completed. Tags: ${crntTags.length} - Categories: ${crntCategories.length}.`
);
}
// Retrieve the currently known tags, and add the new ones
let crntTags: string[] = SettingsHelper.get(SETTING_TAXONOMY_TAGS, true) as string[];
if (!crntTags) { crntTags = []; }
crntTags = [...crntTags, ...tags];
// Update the tags and filter out the duplicates
crntTags = [...new Set(crntTags)];
crntTags = crntTags.sort().filter(t => !!t);
await SettingsHelper.update(SETTING_TAXONOMY_TAGS, crntTags, true);
// Retrieve the currently known tags, and add the new ones
let crntCategories: string[] = SettingsHelper.get(SETTING_TAXONOMY_CATEGORIES, true) as string[];
if (!crntCategories) { crntCategories = []; }
crntCategories = [...crntCategories, ...categories];
// Update the categories and filter out the duplicates
crntCategories = [...new Set(crntCategories)];
crntCategories = crntCategories.sort().filter(c => !!c);
await SettingsHelper.update(SETTING_TAXONOMY_CATEGORIES, crntCategories, true);
// Done
Notifications.info(`Export completed. Tags: ${crntTags.length} - Categories: ${crntCategories.length}.`);
});
);
}
/**
* Remap a tag or category to a new one
*/
public static async remap() {
const taxType = await vscode.window.showQuickPick([
"Tag",
"Category"
], {
const taxType = await vscode.window.showQuickPick(['Tag', 'Category'], {
title: `Remap`,
placeHolder: `What do you want to remap?`,
canPickMany: false,
@@ -162,16 +175,16 @@ export class Settings {
return;
}
const type = taxType === "Tag" ? TaxonomyType.Tag : TaxonomyType.Category;
let options = SettingsHelper.getTaxonomy(type);
const type = taxType === 'Tag' ? TaxonomyType.Tag : TaxonomyType.Category;
const options = SettingsHelper.getTaxonomy(type);
if (!options || options.length === 0) {
Notifications.info(`No ${type === TaxonomyType.Tag ? "tags" : "categories"} configured.`);
Notifications.info(`No ${type === TaxonomyType.Tag ? 'tags' : 'categories'} configured.`);
return;
}
const selectedOption = await vscode.window.showQuickPick(options, {
placeHolder: `Select your ${type === TaxonomyType.Tag ? "tags" : "categories"} to insert`,
const selectedOption = await vscode.window.showQuickPick(options, {
placeHolder: `Select your ${type === TaxonomyType.Tag ? 'tags' : 'categories'} to insert`,
canPickMany: false,
ignoreFocusOut: true
});
@@ -180,27 +193,31 @@ export class Settings {
return;
}
const newOptionValue = await vscode.window.showInputBox({
prompt: `Specify the value of the ${type === TaxonomyType.Tag ? "tag" : "category"} with which you want to remap "${selectedOption}". Leave the input <blank> if you want to remove the ${type === TaxonomyType.Tag ? "tag" : "category"} from all articles.`,
placeHolder: `Name of the ${type === TaxonomyType.Tag ? "tag" : "category"}`,
const newOptionValue = await vscode.window.showInputBox({
prompt: `Specify the value of the ${
type === TaxonomyType.Tag ? 'tag' : 'category'
} with which you want to remap "${selectedOption}". Leave the input <blank> if you want to remove the ${
type === TaxonomyType.Tag ? 'tag' : 'category'
} from all articles.`,
placeHolder: `Name of the ${type === TaxonomyType.Tag ? 'tag' : 'category'}`,
ignoreFocusOut: true
});
if (!newOptionValue) {
const deleteAnswer = await vscode.window.showQuickPick(["yes", "no"], {
canPickMany: false,
placeHolder: `Delete ${selectedOption} ${type === TaxonomyType.Tag ? "tag" : "category"}?`,
const deleteAnswer = await vscode.window.showQuickPick(['yes', 'no'], {
canPickMany: false,
placeHolder: `Delete ${selectedOption} ${type === TaxonomyType.Tag ? 'tag' : 'category'}?`,
ignoreFocusOut: true
});
if (deleteAnswer === "no") {
if (deleteAnswer === 'no') {
return;
}
}
if (newOptionValue) {
TaxonomyHelper.process("edit", type, selectedOption, newOptionValue);
TaxonomyHelper.process('edit', type, selectedOption, newOptionValue);
} else {
TaxonomyHelper.process("delete", type, selectedOption, undefined);
TaxonomyHelper.process('delete', type, selectedOption, undefined);
}
}
}
}

View File

@@ -1,5 +1,12 @@
import { ParsedFrontMatter } from './../parsers/FrontMatterParser';
import { CONTEXT, NOTIFICATION_TYPE, SETTING_SEO_DESCRIPTION_FIELD, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH } from './../constants';
import {
CONTEXT,
NOTIFICATION_TYPE,
SETTING_SEO_DESCRIPTION_FIELD,
SETTING_SEO_DESCRIPTION_LENGTH,
SETTING_SEO_TITLE_FIELD,
SETTING_SEO_TITLE_LENGTH
} from './../constants';
import * as vscode from 'vscode';
import { ArticleHelper, Notifications, SeoHelper, Settings } from '../helpers';
import { ExplorerView } from '../explorerView/ExplorerView';
@@ -10,23 +17,25 @@ import { commands } from 'vscode';
import { Field } from '../models';
export class StatusListener {
/**
* Update the text of the status bar
*
* @param frontMatterSB
* @param collection
*
* @param frontMatterSB
* @param collection
*/
public static async verify(frontMatterSB: vscode.StatusBarItem, collection: vscode.DiagnosticCollection) {
const draftMsg = "in draft";
const publishMsg = "to publish";
public static async verify(
frontMatterSB: vscode.StatusBarItem,
collection: vscode.DiagnosticCollection
) {
const draftMsg = 'in draft';
const publishMsg = 'to publish';
const draft = ContentType.getDraftField();
if (!draft || draft.type !== "boolean") {
if (!draft || draft.type !== 'boolean') {
frontMatterSB.hide();
}
let editor = vscode.window.activeTextEditor;
const editor = vscode.window.activeTextEditor;
if (editor && ArticleHelper.isSupportedFile()) {
try {
commands.executeCommand('setContext', CONTEXT.isValidFile, true);
@@ -34,11 +43,11 @@ export class StatusListener {
const article = ArticleHelper.getFrontMatter(editor);
// Update the StatusBar based on the article draft state
if (article && typeof article.data["draft"] !== "undefined") {
if (article.data["draft"] === true) {
if (article && typeof article.data['draft'] !== 'undefined') {
if (article.data['draft'] === true) {
frontMatterSB.text = `$(book) ${draftMsg}`;
frontMatterSB.show();
} else if (article.data["draft"] === false) {
} else if (article.data['draft'] === false) {
frontMatterSB.text = `$(book) ${publishMsg}`;
frontMatterSB.show();
}
@@ -49,25 +58,28 @@ export class StatusListener {
collection.clear();
// Retrieve the SEO config properties
const titleLength = Settings.get(SETTING_SEO_TITLE_LENGTH) as number || -1;
const descLength = Settings.get(SETTING_SEO_DESCRIPTION_LENGTH) as number || -1;
const fieldName = Settings.get(SETTING_SEO_DESCRIPTION_FIELD) as string || DefaultFields.Description;
if (article.data.title && titleLength > -1) {
SeoHelper.checkLength(editor, collection, article, "title", titleLength);
const titleLength = (Settings.get(SETTING_SEO_TITLE_LENGTH) as number) || -1;
const descLength = (Settings.get(SETTING_SEO_DESCRIPTION_LENGTH) as number) || -1;
const titleField =
(Settings.get(SETTING_SEO_TITLE_FIELD) as string) || DefaultFields.Title;
const descriptionField =
(Settings.get(SETTING_SEO_DESCRIPTION_FIELD) as string) || DefaultFields.Description;
if (article.data[titleField] && titleLength > -1) {
SeoHelper.checkLength(editor, collection, article, titleField, titleLength);
}
if (article.data[fieldName] && descLength > -1) {
SeoHelper.checkLength(editor, collection, article, fieldName, descLength);
if (article.data[descriptionField] && descLength > -1) {
SeoHelper.checkLength(editor, collection, article, descriptionField, descLength);
}
// Check the required fields
StatusListener.verifyRequiredFields(editor, article, collection);
}
const panel = ExplorerView.getInstance();
if (panel && panel.visible) {
DataListener.pushMetadata(article!.data);
DataListener.pushMetadata(article?.data);
}
return;
@@ -88,47 +100,54 @@ export class StatusListener {
/**
* Verify the required fields
* @param article
* @param collection
* @param article
* @param collection
*/
private static verifyRequiredFields(editor: vscode.TextEditor, article: ParsedFrontMatter, collection: vscode.DiagnosticCollection) {
private static verifyRequiredFields(
editor: vscode.TextEditor,
article: ParsedFrontMatter,
collection: vscode.DiagnosticCollection
) {
// Check for missing fields
const emptyFields = ContentType.findEmptyRequiredFields(article);
const fieldsToReport = [];
if (emptyFields && emptyFields.length > 0) {
const text = editor.document.getText();
const markdown = ArticleHelper.stringifyFrontMatter("", article.data);
const markdown = ArticleHelper.stringifyFrontMatter('', article.data);
const editorSpaces = vscode.window.activeTextEditor?.options?.tabSize;
const requiredDiagnostics: vscode.Diagnostic[] = [];
for (const fields of emptyFields) {
let txtIdx = -1;
let fieldName = "";
let fieldName = '';
let level = 0;
for (const field of fields) {
const totalSpaces = level * (typeof editorSpaces === "string" ? parseInt(editorSpaces) : editorSpaces || 2);
const crntIdx = StatusListener.findFieldLine(text, txtIdx, totalSpaces, field);
const totalSpaces =
level * (typeof editorSpaces === 'string' ? parseInt(editorSpaces) : editorSpaces || 2);
const crntIdx = StatusListener.findFieldLine(text, txtIdx, totalSpaces, field);
if (crntIdx && crntIdx > txtIdx) {
txtIdx = crntIdx;
fieldName = field.name;
}
++level;
}
if (txtIdx !== -1 && txtIdx < markdown.length) {
fieldsToReport.push(fields.map(f => f.title).join("/"));
fieldsToReport.push(fields.map((f) => f.title).join('/'));
const posStart = editor.document.positionAt(txtIdx);
const posEnd = editor.document.positionAt(txtIdx + 1 + fieldName.length);
const diagnostic: vscode.Diagnostic = {
code: '',
message: `This ${fields.map(f => f.name).join("/")} field is required to contain a value.`,
message: `This ${fields
.map((f) => f.name)
.join('/')} field is required to contain a value.`,
range: new vscode.Range(posStart, posEnd),
severity: vscode.DiagnosticSeverity.Error,
source: 'Front Matter'
@@ -137,7 +156,7 @@ export class StatusListener {
requiredDiagnostics.push(diagnostic);
}
}
if (collection.has(editor.document.uri)) {
const otherDiag = collection.get(editor.document.uri) || [];
collection.set(editor.document.uri, [...otherDiag, ...requiredDiagnostics]);
@@ -146,20 +165,29 @@ export class StatusListener {
}
if (fieldsToReport.length > 0) {
Notifications.showIfNotDisabled(NOTIFICATION_TYPE.requiredFieldValidation, "ERROR_ONCE", `The following fields are required to contain a value: ${fieldsToReport.join(", ")}`);
Notifications.showIfNotDisabled(
NOTIFICATION_TYPE.requiredFieldValidation,
'ERROR_ONCE',
`The following fields are required to contain a value: ${fieldsToReport.join(', ')}`
);
}
}
}
/**
* Find the line of the field
* @param text
* @param startIdx
* @param totalSpaces
* @param field
* @returns
* @param text
* @param startIdx
* @param totalSpaces
* @param field
* @returns
*/
private static findFieldLine(text: string, startIdx: number, totalSpaces: number, field: Field): number | undefined {
private static findFieldLine(
text: string,
startIdx: number,
totalSpaces: number,
field: Field
): number | undefined {
const crntIdx = text.indexOf(field.name, startIdx === -1 ? 0 : startIdx);
if (crntIdx > -1) {
@@ -179,4 +207,4 @@ export class StatusListener {
return;
}
}
}

View File

@@ -1,8 +1,11 @@
import { Questions } from './../helpers/Questions';
import * as vscode from 'vscode';
import * as path from 'path';
import * as fs from 'fs';
import { SETTING_CONTENT_DEFAULT_FILETYPE, SETTING_TEMPLATES_FOLDER, TelemetryEvent } from '../constants';
import {
SETTING_CONTENT_DEFAULT_FILETYPE,
SETTING_TEMPLATES_FOLDER,
TelemetryEvent
} from '../constants';
import { ArticleHelper, Settings } from '../helpers';
import { Article } from '.';
import { Notifications } from '../helpers/Notifications';
@@ -12,9 +15,9 @@ import { ContentType as IContentType } from '../models';
import { PagesListener } from '../listeners/dashboard';
import { extname } from 'path';
import { Telemetry } from '../helpers/Telemetry';
import { writeFileAsync, copyFileAsync } from '../utils';
export class Template {
/**
* Generate a template
*/
@@ -39,28 +42,30 @@ export class Template {
return;
}
const keepContents = await vscode.window.showQuickPick(
["yes", "no"],
{
title: `Keep contents`,
canPickMany: false,
placeHolder: `Do you want to keep the contents for the template?`,
ignoreFocusOut: true
}
);
const keepContents = await vscode.window.showQuickPick(['yes', 'no'], {
title: `Keep contents`,
canPickMany: false,
placeHolder: `Do you want to keep the contents for the template?`,
ignoreFocusOut: true
});
if (!keepContents) {
Notifications.warning(`You did not pick any of the options for keeping the template its content.`);
Notifications.warning(
`You did not pick any of the options for keeping the template its content.`
);
return;
}
await Project.init(false);
const templatePath = Project.templatePath();
if (templatePath) {
let fileContents = ArticleHelper.stringifyFrontMatter(keepContents === "no" ? "" : clonedArticle.content, clonedArticle.data);
const fileContents = ArticleHelper.stringifyFrontMatter(
keepContents === 'no' ? '' : clonedArticle.content,
clonedArticle.data
);
const templateFile = path.join(templatePath.fsPath, `${titleValue}.${fileType}`);
fs.writeFileSync(templateFile, fileContents, { encoding: "utf-8" });
await writeFileAsync(templateFile, fileContents, { encoding: 'utf-8' });
Notifications.info(`Template created and is now available in your ${folder} folder.`);
}
@@ -78,7 +83,10 @@ export class Template {
return;
}
return await vscode.workspace.findFiles(`${folder}/**/*`, "**/node_modules/**,**/archetypes/**");
return await vscode.workspace.findFiles(
`${folder}/**/*`,
'**/node_modules/**,**/archetypes/**'
);
}
/**
@@ -98,11 +106,14 @@ export class Template {
return;
}
const selectedTemplate = await vscode.window.showQuickPick(templates.map(t => path.basename(t.fsPath)), {
title: `Select a template`,
placeHolder: `Select the content template to use`,
ignoreFocusOut: true
});
const selectedTemplate = await vscode.window.showQuickPick(
templates.map((t) => path.basename(t.fsPath)),
{
title: `Select a template`,
placeHolder: `Select the content template to use`,
ignoreFocusOut: true
}
);
if (!selectedTemplate) {
Notifications.warning(`No template selected.`);
return;
@@ -114,40 +125,53 @@ export class Template {
}
// Start the template read
const template = templates.find(t => t.fsPath.endsWith(selectedTemplate));
const template = templates.find((t) => t.fsPath.endsWith(selectedTemplate));
if (!template) {
Notifications.warning(`Content template could not be found.`);
return;
}
const templateData = ArticleHelper.getFrontMatterByPath(template.fsPath);
const templateData = await ArticleHelper.getFrontMatterByPath(template.fsPath);
let contentType: IContentType | undefined;
if (templateData && templateData.data && templateData.data.type) {
contentType = contentTypes?.find(t => t.name === templateData.data.type);
contentType = contentTypes?.find((t) => t.name === templateData.data.type);
}
const fileExtension = extname(template.fsPath).replace(".", "");
let newFilePath: string | undefined = ArticleHelper.createContent(contentType, folderPath, titleValue, fileExtension);
const fileExtension = extname(template.fsPath).replace('.', '');
const newFilePath: string | undefined = await ArticleHelper.createContent(
contentType,
folderPath,
titleValue,
fileExtension
);
if (!newFilePath) {
return;
}
// Start the new file creation
fs.copyFileSync(template.fsPath, newFilePath);
await copyFileAsync(template.fsPath, newFilePath);
// Update the properties inside the template
let frontMatter = ArticleHelper.getFrontMatterByPath(newFilePath);
let frontMatter = await ArticleHelper.getFrontMatterByPath(newFilePath);
if (!frontMatter) {
Notifications.warning(`Something failed when retrieving the newly created file.`);
return;
}
if (frontMatter.data) {
frontMatter.data = await ArticleHelper.updatePlaceholders(frontMatter.data, titleValue, newFilePath);
frontMatter.data = await ArticleHelper.updatePlaceholders(
frontMatter.data,
titleValue,
newFilePath
);
frontMatter = Article.updateDate(frontMatter);
fs.writeFileSync(newFilePath, ArticleHelper.stringifyFrontMatter(frontMatter.content, frontMatter.data), { encoding: "utf8" });
await writeFileAsync(
newFilePath,
ArticleHelper.stringifyFrontMatter(frontMatter.content, frontMatter.data),
{ encoding: 'utf8' }
);
await vscode.commands.executeCommand('vscode.open', vscode.Uri.file(newFilePath));
}
@@ -172,4 +196,4 @@ export class Template {
const folder = Settings.get<string>(SETTING_TEMPLATES_FOLDER);
return folder;
}
}
}

View File

@@ -1,6 +1,6 @@
import { commands, window, Selection, QuickPickItem, TextEditor } from "vscode";
import { COMMAND_NAME, CONTEXT, SETTING_CONTENT_WYSIWYG } from "../constants";
import { Settings } from "../helpers";
import { commands, window, Selection, QuickPickItem, TextEditor } from 'vscode';
import { COMMAND_NAME, CONTEXT, SETTING_CONTENT_WYSIWYG } from '../constants';
import { Settings } from '../helpers';
enum MarkupType {
bold = 1,
@@ -13,18 +13,16 @@ enum MarkupType {
unorderedList,
orderedList,
taskList,
hyperlink,
hyperlink
}
export class Wysiwyg {
/**
* Registers the markup commands for the WYSIWYG controls
* @param subscriptions
* @returns
* @param subscriptions
* @returns
*/
public static async registerCommands(subscriptions: any) {
public static async registerCommands(subscriptions: unknown[]) {
const wysiwygEnabled = Settings.get(SETTING_CONTENT_WYSIWYG);
if (!wysiwygEnabled) {
@@ -34,65 +32,123 @@ export class Wysiwyg {
await commands.executeCommand('setContext', CONTEXT.wysiwyg, true);
// Surrounding markup
subscriptions.push(commands.registerCommand(COMMAND_NAME.bold, () => this.addMarkup(MarkupType.bold)));
subscriptions.push(commands.registerCommand(COMMAND_NAME.italic, () => this.addMarkup(MarkupType.italic)));
subscriptions.push(commands.registerCommand(COMMAND_NAME.strikethrough, () => this.addMarkup(MarkupType.strikethrough)));
subscriptions.push(commands.registerCommand(COMMAND_NAME.code, () => this.addMarkup(MarkupType.code)));
subscriptions.push(commands.registerCommand(COMMAND_NAME.codeblock, () => this.addMarkup(MarkupType.codeblock)));
subscriptions.push(
commands.registerCommand(COMMAND_NAME.bold, () => this.addMarkup(MarkupType.bold))
);
subscriptions.push(
commands.registerCommand(COMMAND_NAME.italic, () => this.addMarkup(MarkupType.italic))
);
subscriptions.push(
commands.registerCommand(COMMAND_NAME.strikethrough, () =>
this.addMarkup(MarkupType.strikethrough)
)
);
subscriptions.push(
commands.registerCommand(COMMAND_NAME.code, () => this.addMarkup(MarkupType.code))
);
subscriptions.push(
commands.registerCommand(COMMAND_NAME.codeblock, () => this.addMarkup(MarkupType.codeblock))
);
// Prefix markup
subscriptions.push(commands.registerCommand(COMMAND_NAME.heading, () => this.addMarkup(MarkupType.heading)));
subscriptions.push(commands.registerCommand(COMMAND_NAME.blockquote, () => this.addMarkup(MarkupType.blockquote)));
subscriptions.push(commands.registerCommand(COMMAND_NAME.unorderedlist, () => this.addMarkup(MarkupType.unorderedList)));
subscriptions.push(commands.registerCommand(COMMAND_NAME.orderedlist, () => this.addMarkup(MarkupType.orderedList)));
subscriptions.push(commands.registerCommand(COMMAND_NAME.taskList, () => this.addMarkup(MarkupType.taskList)));
subscriptions.push(
commands.registerCommand(COMMAND_NAME.heading, () => this.addMarkup(MarkupType.heading))
);
subscriptions.push(
commands.registerCommand(COMMAND_NAME.blockquote, () => this.addMarkup(MarkupType.blockquote))
);
subscriptions.push(
commands.registerCommand(COMMAND_NAME.unorderedlist, () =>
this.addMarkup(MarkupType.unorderedList)
)
);
subscriptions.push(
commands.registerCommand(COMMAND_NAME.orderedlist, () =>
this.addMarkup(MarkupType.orderedList)
)
);
subscriptions.push(
commands.registerCommand(COMMAND_NAME.taskList, () => this.addMarkup(MarkupType.taskList))
);
// Other markup
subscriptions.push(commands.registerCommand(COMMAND_NAME.hyperlink, () => this.addMarkup(MarkupType.hyperlink)));
subscriptions.push(
commands.registerCommand(COMMAND_NAME.hyperlink, () => this.addMarkup(MarkupType.hyperlink))
);
// Options
subscriptions.push(commands.registerCommand(COMMAND_NAME.options, async () => {
const qpItems: QuickPickItem[] = [
{ label: "$(list-unordered) Unordered list", detail: "Add an unordered list", alwaysShow: true, },
{ label: "$(list-ordered) Ordered list", detail: "Add an ordered list", alwaysShow: true },
{ label: "$(tasklist) Task list", detail: "Add a task list", alwaysShow: true },
{ label: "$(code) Code", detail: "Add inline code snippet", alwaysShow: true },
{ label: "$(symbol-namespace) Code block", detail: "Add a code block", alwaysShow: true },
{ label: "$(quote) Blockquote", detail: "Add a blockquote", alwaysShow: true },
{ label: "$(symbol-text) Strikethrough", detail: "Add a strikethrough", alwaysShow: true },
]
subscriptions.push(
commands.registerCommand(COMMAND_NAME.options, async () => {
const qpItems: QuickPickItem[] = [
{
label: '$(list-unordered) Unordered list',
detail: 'Add an unordered list',
alwaysShow: true
},
{
label: '$(list-ordered) Ordered list',
detail: 'Add an ordered list',
alwaysShow: true
},
{
label: '$(tasklist) Task list',
detail: 'Add a task list',
alwaysShow: true
},
{
label: '$(code) Code',
detail: 'Add inline code snippet',
alwaysShow: true
},
{
label: '$(symbol-namespace) Code block',
detail: 'Add a code block',
alwaysShow: true
},
{
label: '$(quote) Blockquote',
detail: 'Add a blockquote',
alwaysShow: true
},
{
label: '$(symbol-text) Strikethrough',
detail: 'Add a strikethrough',
alwaysShow: true
}
];
const option = await window.showQuickPick([ ...qpItems ], {
title: "WYSIWYG Options",
placeHolder: "Which type of markup would you like to insert?",
canPickMany: false,
ignoreFocusOut: true
});
const option = await window.showQuickPick([...qpItems], {
title: 'WYSIWYG Options',
placeHolder: 'Which type of markup would you like to insert?',
canPickMany: false,
ignoreFocusOut: true
});
if (option) {
if (option.label === qpItems[0].label) {
await this.addMarkup(MarkupType.unorderedList);
} else if (option.label === qpItems[1].label) {
await this.addMarkup(MarkupType.orderedList);
} else if (option.label === qpItems[2].label) {
await this.addMarkup(MarkupType.taskList);
} else if (option.label === qpItems[3].label) {
await this.addMarkup(MarkupType.code);
} else if (option.label === qpItems[4].label) {
await this.addMarkup(MarkupType.codeblock);
} else if (option.label === qpItems[5].label) {
await this.addMarkup(MarkupType.blockquote);
} else if (option.label === qpItems[6].label) {
await this.addMarkup(MarkupType.strikethrough);
if (option) {
if (option.label === qpItems[0].label) {
await this.addMarkup(MarkupType.unorderedList);
} else if (option.label === qpItems[1].label) {
await this.addMarkup(MarkupType.orderedList);
} else if (option.label === qpItems[2].label) {
await this.addMarkup(MarkupType.taskList);
} else if (option.label === qpItems[3].label) {
await this.addMarkup(MarkupType.code);
} else if (option.label === qpItems[4].label) {
await this.addMarkup(MarkupType.codeblock);
} else if (option.label === qpItems[5].label) {
await this.addMarkup(MarkupType.blockquote);
} else if (option.label === qpItems[6].label) {
await this.addMarkup(MarkupType.strikethrough);
}
}
}
}));
})
);
}
/**
* Add the markup to the content
* @param type
* @returns
* @param type
* @returns
*/
private static async addMarkup(type: MarkupType) {
const editor = window.activeTextEditor;
@@ -118,18 +174,21 @@ export class Wysiwyg {
// Replace the selection and surround with the markup
const selectionText = editor.document.getText(selection);
const txt = await this.insertText(markers, type, selectionText);
editor.edit(builder => {
editor.edit((builder) => {
builder.replace(selection, txt);
});
} else {
const txt = await this.insertText(markers, type);
// Insert the markers where cursor is located.
const markerLength = this.isMarkupWrapping(type) ? txt.length + 1 : markers.length;
let newPosition = crntSelection.with(crntSelection.line, crntSelection.character + markerLength);
let newPosition = crntSelection.with(
crntSelection.line,
crntSelection.character + markerLength
);
await editor.edit(builder => {
await editor.edit((builder) => {
builder.insert(newPosition, txt);
});
@@ -147,20 +206,20 @@ export class Wysiwyg {
*/
private static async addHyperlink(editor: TextEditor, selection: Selection) {
const hasTextSelection = !selection.isEmpty;
const linkText = hasTextSelection ? editor.document.getText(selection) : "";
const linkText = hasTextSelection ? editor.document.getText(selection) : '';
const link = await window.showInputBox({
title: "WYSIWYG Hyperlink",
placeHolder: "Enter the URL",
prompt: "Enter the URL",
title: 'WYSIWYG Hyperlink',
placeHolder: 'Enter the URL',
prompt: 'Enter the URL',
value: linkText,
ignoreFocusOut: true
});
const text = await window.showInputBox({
title: "WYSIWYG Text",
prompt: "Enter the text for the hyperlink",
placeHolder: "Enter the text for the hyperlink",
title: 'WYSIWYG Text',
prompt: 'Enter the text for the hyperlink',
placeHolder: 'Enter the text for the hyperlink',
value: linkText,
ignoreFocusOut: true
});
@@ -169,15 +228,18 @@ export class Wysiwyg {
const txt = `[${text || link}](${link})`;
if (hasTextSelection) {
editor.edit(builder => {
editor.edit((builder) => {
builder.replace(selection, txt);
});
} else {
const crntSelection = selection.active;
const markerLength = txt.length;
const newPosition = crntSelection.with(crntSelection.line, crntSelection.character + markerLength);
const newPosition = crntSelection.with(
crntSelection.line,
crntSelection.character + markerLength
);
await editor.edit(builder => {
await editor.edit((builder) => {
builder.insert(newPosition, txt);
});
@@ -188,12 +250,12 @@ export class Wysiwyg {
/**
* Check if the text will be wrapped
* @param type
* @returns
* @param type
* @returns
*/
private static isMarkupWrapping(type: MarkupType) {
private static isMarkupWrapping(type: MarkupType) {
return (
type === MarkupType.blockquote ||
type === MarkupType.blockquote ||
type === MarkupType.heading ||
type === MarkupType.unorderedList ||
type === MarkupType.orderedList ||
@@ -204,39 +266,39 @@ export class Wysiwyg {
/**
* Insert text at the current cursor position
*/
private static async insertText(marker: string | undefined, type: MarkupType, text: string | null = null) {
private static async insertText(
marker: string | undefined,
type: MarkupType,
text: string | null = null
) {
const crntText = text || this.lineBreak(type);
if (this.isMarkupWrapping(type)) {
if (type === MarkupType.heading) {
const headingLvl = await window.showQuickPick([
"Heading 1",
"Heading 2",
"Heading 3",
"Heading 4",
"Heading 5",
"Heading 6"
], {
title: "Heading Level",
canPickMany: false,
placeHolder: "Which heading level do you want to insert?",
ignoreFocusOut: true
});
const headingLvl = await window.showQuickPick(
['Heading 1', 'Heading 2', 'Heading 3', 'Heading 4', 'Heading 5', 'Heading 6'],
{
title: 'Heading Level',
canPickMany: false,
placeHolder: 'Which heading level do you want to insert?',
ignoreFocusOut: true
}
);
if (headingLvl) {
const headingNr = parseInt(headingLvl.replace("Heading ", ""));
const headingNr = parseInt(headingLvl.replace('Heading ', ''));
return `${Array(headingNr + 1).join(marker)} ${crntText}`;
}
}
if (type === MarkupType.unorderedList || type === MarkupType.taskList) {
const lines = crntText.split("\n").map(line => `${marker} ${line}`);
return lines.join("\n");
const lines = crntText.split('\n').map((line) => `${marker} ${line}`);
return lines.join('\n');
}
if (type === MarkupType.orderedList) {
const lines = crntText.split("\n").map((line, idx) => `${idx+1}. ${line}`);
return lines.join("\n");
const lines = crntText.split('\n').map((line, idx) => `${idx + 1}. ${line}`);
return lines.join('\n');
}
return `${marker} ${crntText}`;
@@ -247,23 +309,23 @@ export class Wysiwyg {
/**
* Check if linebreak needs to be added
* @param type
* @returns
* @param type
* @returns
*/
private static lineBreak(type: MarkupType) {
if (type === MarkupType.codeblock) {
return `\n\n`;
}
return "";
return '';
}
/**
* Retrieve the type of markers
* @param type
* @returns
* @param type
* @returns
*/
private static getMarkers(type: MarkupType) {
switch(type) {
switch (type) {
case MarkupType.bold:
return `**`;
case MarkupType.italic:
@@ -271,21 +333,21 @@ export class Wysiwyg {
case MarkupType.strikethrough:
return `~~`;
case MarkupType.code:
return "`";
return '`';
case MarkupType.codeblock:
return "```";
return '```';
case MarkupType.blockquote:
return ">";
return '>';
case MarkupType.heading:
return "#";
return '#';
case MarkupType.unorderedList:
return "-";
return '-';
case MarkupType.orderedList:
return "1.";
return '1.';
case MarkupType.taskList:
return "- [ ]";
return '- [ ]';
default:
return;
}
}
}
}

View File

@@ -1,3 +1,13 @@
export * from './Article';
export * from './Backers';
export * from './Cache';
export * from './Content';
export * from './Dashboard';
export * from './Diagnostics';
export * from './Folders';
export * from './Preview';
export * from './Project';
export * from './Settings';
export * from './StatusListener';
export * from './Template';
export * from './Wysiwyg';

View File

@@ -6,9 +6,13 @@ export interface IFeatureFlagProps {
alternative?: JSX.Element;
}
export const FeatureFlag: React.FunctionComponent<IFeatureFlagProps> = ({ flag, features, alternative, children }: React.PropsWithChildren<IFeatureFlagProps>) => {
if (!features ||( features.length > 0 && !features.includes(flag))) {
export const FeatureFlag: React.FunctionComponent<IFeatureFlagProps> = ({
flag,
features,
alternative,
children
}: React.PropsWithChildren<IFeatureFlagProps>) => {
if (!features || (features.length > 0 && !features.includes(flag))) {
if (alternative) {
return alternative;
}
@@ -16,11 +20,5 @@ export const FeatureFlag: React.FunctionComponent<IFeatureFlagProps> = ({ flag,
return null;
}
return (
<>
{children}
</>
);
};
return <>{children}</>;
};

View File

@@ -4,10 +4,22 @@ export interface ICompressIconProps {
className?: string;
}
export const CompressIcon: React.FunctionComponent<ICompressIconProps> = ({className}: React.PropsWithChildren<ICompressIconProps>) => {
export const CompressIcon: React.FunctionComponent<ICompressIconProps> = ({
className
}: React.PropsWithChildren<ICompressIconProps>) => {
return (
<svg className={className || ""} aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path fill="currentColor" d="M436 192H312c-13.3 0-24-10.7-24-24V44c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v84h84c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12zm-276-24V44c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12v84H12c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h124c13.3 0 24-10.7 24-24zm0 300V344c0-13.3-10.7-24-24-24H12c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h84v84c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12zm192 0v-84h84c6.6 0 12-5.4 12-12v-40c0-6.6-5.4-12-12-12H312c-13.3 0-24 10.7-24 24v124c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12z"></path>
<svg
className={className || ''}
aria-hidden="true"
focusable="false"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 448 512"
>
<path
fill="currentColor"
d="M436 192H312c-13.3 0-24-10.7-24-24V44c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v84h84c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12zm-276-24V44c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12v84H12c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h124c13.3 0 24-10.7 24-24zm0 300V344c0-13.3-10.7-24-24-24H12c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h84v84c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12zm192 0v-84h84c6.6 0 12-5.4 12-12v-40c0-6.6-5.4-12-12-12H312c-13.3 0-24 10.7-24 24v124c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12z"
></path>
</svg>
);
};
};

View File

@@ -4,10 +4,16 @@ export interface IMergeIconProps {
className: string;
}
export const MergeIcon: React.FunctionComponent<IMergeIconProps> = ({className}: React.PropsWithChildren<IMergeIconProps>) => {
export const MergeIcon: React.FunctionComponent<IMergeIconProps> = ({
className
}: React.PropsWithChildren<IMergeIconProps>) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" className={className}>
<path xmlns="http://www.w3.org/2000/svg" d="M7.586 8.00366L4 8.00366C3.44772 8.00366 3 7.55595 3 7.00366C3 6.45138 3.44772 6.00366 4 6.00366L8 6.00366C8.26509 6.00366 8.51933 6.10892 8.70685 6.2963L13.414 11H18.5845L15.2931 7.71103C14.9025 7.32065 14.9023 6.68748 15.2926 6.29681C15.683 5.90615 16.3162 5.90592 16.7068 6.2963L21.7068 11.2926C21.8945 11.4802 22 11.7346 22 11.9998C22 12.2651 21.8947 12.5195 21.7071 12.7071L16.7071 17.7071C16.3166 18.0976 15.6834 18.0976 15.2929 17.7071C14.9024 17.3166 14.9024 16.6834 15.2929 16.2929L18.5858 13H13.4142L8.70711 17.7071C8.51957 17.8947 8.26522 18 8 18H4C3.44772 18 3 17.5523 3 17C3 16.4477 3.44772 16 4 16H7.58579L11.5855 12.0003L7.586 8.00366Z" fill="currentcolor"/>
<path
xmlns="http://www.w3.org/2000/svg"
d="M7.586 8.00366L4 8.00366C3.44772 8.00366 3 7.55595 3 7.00366C3 6.45138 3.44772 6.00366 4 6.00366L8 6.00366C8.26509 6.00366 8.51933 6.10892 8.70685 6.2963L13.414 11H18.5845L15.2931 7.71103C14.9025 7.32065 14.9023 6.68748 15.2926 6.29681C15.683 5.90615 16.3162 5.90592 16.7068 6.2963L21.7068 11.2926C21.8945 11.4802 22 11.7346 22 11.9998C22 12.2651 21.8947 12.5195 21.7071 12.7071L16.7071 17.7071C16.3166 18.0976 15.6834 18.0976 15.2929 17.7071C14.9024 17.3166 14.9024 16.6834 15.2929 16.2929L18.5858 13H13.4142L8.70711 17.7071C8.51957 17.8947 8.26522 18 8 18H4C3.44772 18 3 17.5523 3 17C3 16.4477 3.44772 16 4 16H7.58579L11.5855 12.0003L7.586 8.00366Z"
fill="currentcolor"
/>
</svg>
);
};
};

View File

@@ -1,6 +1,5 @@
import * as invariant from 'invariant';
import { createAutoField } from 'uniforms';
import { PreviewImageField } from '../../panelWebView/components/Fields/PreviewImageField';
export { AutoFieldProps } from 'uniforms';
import BoolField from './BoolField';
@@ -12,12 +11,9 @@ import RadioField from './RadioField';
import SelectField from './SelectField';
import TextField from './TextField';
const AutoField = createAutoField(props => {
const AutoField = createAutoField((props) => {
if (props.allowedValues) {
return props.checkboxes && props.fieldType !== Array
? RadioField
: SelectField;
return props.checkboxes && props.fieldType !== Array ? RadioField : SelectField;
}
switch (props.fieldType) {

View File

@@ -23,7 +23,7 @@ export default function AutoFields({
element,
props,
(fields ?? schema.getSubfields())
.filter(field => !omitFields.includes(field))
.map(field => createElement(autoField, { key: field, name: field })),
.filter((field) => !omitFields.includes(field))
.map((field) => createElement(autoField, { key: field, name: field }))
);
}

View File

@@ -2,8 +2,9 @@ import { AutoForm } from 'uniforms';
import ValidatedQuickForm from './ValidatedQuickForm';
function Auto(parent: any) {
class _ extends AutoForm.Auto(parent) {
function Auto(parent: unknown) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
class _ extends AutoForm.Auto(parent as any) {
static Auto = Auto;
}

View File

@@ -1,5 +1,6 @@
import { BaseForm } from 'uniforms';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function Unstyled(parent: any) {
class _ extends parent {
static Unstyled = Unstyled;

View File

@@ -5,7 +5,7 @@
height: 24px;
}
.field__toggle input {
.field__toggle input {
opacity: 0;
width: 0;
height: 0;
@@ -18,22 +18,25 @@
left: 0;
right: 0;
bottom: 0;
background-color: var(--frontmatter-toggle-secondaryBackground, var(--vscode-button-secondaryBackground));
-webkit-transition: .4s;
transition: .4s;
background-color: var(
--frontmatter-toggle-secondaryBackground,
var(--vscode-button-secondaryBackground)
);
-webkit-transition: 0.4s;
transition: 0.4s;
border-radius: 34px;
}
.field__toggle__slider:before {
position: absolute;
content: "";
content: '';
height: 16px;
width: 16px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
-webkit-transition: 0.4s;
transition: 0.4s;
border-radius: 50%;
}
@@ -49,4 +52,4 @@ input:checked + .field__toggle__slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
}

View File

@@ -24,7 +24,7 @@ function Bool({
return (
<div {...filterDOMProps(props)}>
<LabelField label={label} id={id} required={props.required} />
<label className="field__toggle">
<input
checked={value || false}

View File

@@ -36,7 +36,7 @@ function Date({
max={dateFormat(max)}
min={dateFormat(min)}
name={name}
onChange={event => {
onChange={(event) => {
const date = new DateConstructor(event.target.valueAsNumber);
if (date.getFullYear() < 10000) {
onChange(date);

View File

@@ -4,16 +4,14 @@ import { Override, connectField, filterDOMProps } from 'uniforms';
export type ErrorFieldProps = Override<
Omit<HTMLProps<HTMLDivElement>, 'onChange'>,
{ error?: any; errorMessage?: string }
{ error?: unknown; errorMessage?: string }
>;
function Error({ children, error, errorMessage, ...props }: ErrorFieldProps) {
return !error ? null : (
<div {...filterDOMProps(props)}>{children || errorMessage}</div>
);
return !error ? null : <div {...filterDOMProps(props)}>{children || errorMessage}</div>;
}
export default connectField<ErrorFieldProps>(Error, {
initialValue: false,
kind: 'leaf',
kind: 'leaf'
});

View File

@@ -1,7 +1,8 @@
.autoform-error {
background-color: var(--frontmatter-error-background, var(--vscode-inputValidation-errorBackground));
background-color: var(
--frontmatter-error-background,
var(--vscode-inputValidation-errorBackground)
);
border: 1px solid var(--frontmatter-error-border, var(--vscode-inputValidation-errorBorder));
border-radius: 2px;
margin: 20px 0px;
@@ -15,4 +16,4 @@
li {
text-transform: capitalize;
}
}
}

View File

@@ -8,7 +8,7 @@ export type ErrorsFieldProps = HTMLProps<HTMLDivElement>;
export default function ErrorsField(props: ErrorsFieldProps) {
const { error, schema } = useForm();
return !error && !props.children ? null : (
<div className='autoform-error'>
<div className="autoform-error">
<div {...filterDOMProps(props)}>
{props.children}

View File

@@ -2,18 +2,20 @@ import * as React from 'react';
import { HTMLProps, Ref, useEffect } from 'react';
import { Override, filterDOMProps, useField } from 'uniforms';
type ValueType = string | number | readonly string[] | undefined;
export type HiddenFieldProps = Override<
HTMLProps<HTMLInputElement>,
{
inputRef?: Ref<HTMLInputElement>;
name: string;
noDOM?: boolean;
value?: any;
value?: ValueType;
}
>;
export default function HiddenField({ value, ...rawProps }: HiddenFieldProps) {
const props = useField(rawProps.name, rawProps, { initialValue: false })[0];
const defaultValue = (props.value as ValueType) ?? '';
useEffect(() => {
if (value !== undefined && value !== props.value) {
@@ -28,7 +30,7 @@ export default function HiddenField({ value, ...rawProps }: HiddenFieldProps) {
readOnly={props.readOnly}
ref={props.inputRef}
type="hidden"
value={value ?? props.value ?? ''}
value={value ?? defaultValue}
{...filterDOMProps(props)}
/>
);

View File

@@ -1,5 +1,3 @@
.autoform__label {
display: block;
margin-bottom: 0.5rem;
@@ -11,4 +9,4 @@
color: var(--vscode-inputValidation-errorBorder);
margin-left: 0.25rem;
}
}
}

View File

@@ -8,13 +8,19 @@ export interface ILabelFieldProps {
required?: boolean;
}
export const LabelField: React.FunctionComponent<ILabelFieldProps> = ({ label, id, required }: React.PropsWithChildren<ILabelFieldProps>) => {
return (
label ? (
<label className="autoform__label" htmlFor={id}>
{label}
{required && <span title='Required field' className='autoform__label__required'>*</span>}
</label>
) : null
);
};
export const LabelField: React.FunctionComponent<ILabelFieldProps> = ({
label,
id,
required
}: React.PropsWithChildren<ILabelFieldProps>) => {
return label ? (
<label className="autoform__label" htmlFor={id}>
{label}
{required && (
<span title="Required field" className="autoform__label__required">
*
</span>
)}
</label>
) : null;
};

View File

@@ -1,12 +1,10 @@
.autoform__list_add_field {
display: flex;
padding: 5px;
border: 1px dashed var(--frontmatter-field-border, var(--vscode-editor-foreground));
width: 100%;
justify-content: center;
margin-top: .5rem;
margin-top: 0.5rem;
&:hover {
border-color: var(--frontmatter-field-borderActive, var(--vscode-button-background));
@@ -18,4 +16,4 @@
height: 1rem;
width: 1rem;
}
}
}

View File

@@ -1,51 +1,42 @@
import { PlusIcon } from '@heroicons/react/outline';
import * as React from 'react';
import {
HTMLFieldProps,
connectField,
filterDOMProps,
joinName,
useField,
} from 'uniforms';
import { HTMLFieldProps, connectField, filterDOMProps, joinName, useField } from 'uniforms';
import './ListAddField.css';
type ParentFieldType = { initialCount?: number; maxCount?: number };
type ValueType = string | readonly string[] | undefined;
export type ListAddFieldProps = HTMLFieldProps<
unknown,
ValueType,
HTMLSpanElement,
{ initialCount?: number }
>;
function ListAdd({
disabled,
initialCount,
name,
readOnly,
value,
...props
}: ListAddFieldProps) {
function ListAdd({ disabled, initialCount, name, readOnly, value, ...props }: ListAddFieldProps) {
const nameParts = joinName(null, name);
const parentName = joinName(nameParts.slice(0, -1));
const parent = useField<
{ initialCount?: number; maxCount?: number },
unknown[]
>(parentName, { initialCount }, { absoluteName: true })[0];
const parent = {
maxCount: 0,
value: [] as ValueType[],
...useField<ParentFieldType, ValueType[]>(
parentName,
{ initialCount },
{ absoluteName: true }
)[0]
};
const limitNotReached =
!disabled && !(parent.maxCount! <= parent.value!.length);
const limitNotReached = !disabled && !(parent.maxCount <= parent.value.length);
function onAction(event: React.KeyboardEvent | React.MouseEvent) {
if (
limitNotReached &&
!readOnly &&
(!('key' in event) || event.key === 'Enter')
) {
parent.onChange(parent.value!.concat([Object.assign({}, value)]));
if (limitNotReached && !readOnly && (!('key' in event) || event.key === 'Enter')) {
parent.onChange(parent.value.concat([value]));
}
}
return (
<span
className='autoform__list_add_field'
className="autoform__list_add_field"
// eslint-disable-next-line @typescript-eslint/no-explicit-any
{...filterDOMProps(props as any)}
onClick={onAction}
onKeyDown={onAction}
@@ -59,5 +50,5 @@ function ListAdd({
export default connectField<ListAddFieldProps>(ListAdd, {
initialValue: false,
kind: 'leaf',
kind: 'leaf'
});

View File

@@ -1,10 +1,8 @@
.autoform__list_del_field {
display: flex;
width: 100%;
justify-content: center;
margin-top: .5rem;
margin-top: 0.5rem;
&:hover {
border-color: var(--vscode-button-background);
@@ -16,12 +14,12 @@
height: 1px;
background: var(--frontmatter-list-border, var(--vscode-editor-foreground));
width: 100%;
margin-right: .5rem;
margin-top: .5rem;
margin-right: 0.5rem;
margin-top: 0.5rem;
}
svg {
height: 1.25rem;
width: 1.25rem;
}
}
}

View File

@@ -1,12 +1,6 @@
import { TrashIcon } from '@heroicons/react/outline';
import * as React from 'react';
import {
HTMLFieldProps,
connectField,
filterDOMProps,
joinName,
useField,
} from 'uniforms';
import { HTMLFieldProps, connectField, filterDOMProps, joinName, useField } from 'uniforms';
import './ListDelField.css';
export type ListDelFieldProps = HTMLFieldProps<unknown, HTMLSpanElement>;
@@ -15,26 +9,19 @@ function ListDel({ disabled, name, readOnly, ...props }: ListDelFieldProps) {
const nameParts = joinName(null, name);
const nameIndex = +nameParts[nameParts.length - 1];
const parentName = joinName(nameParts.slice(0, -1));
const parent = useField<{ minCount?: number }, unknown[]>(
parentName,
{},
{ absoluteName: true },
)[0];
const parent = {
minCount: 0,
value: [],
...useField<{ minCount?: number }, unknown[]>(parentName, {}, { absoluteName: true })[0]
};
const limitNotReached =
!disabled && !(parent.minCount! >= parent.value!.length);
const limitNotReached = !disabled && !(parent.minCount >= parent.value.length);
function onAction(
event:
| React.KeyboardEvent<HTMLSpanElement>
| React.MouseEvent<HTMLSpanElement, MouseEvent>,
event: React.KeyboardEvent<HTMLSpanElement> | React.MouseEvent<HTMLSpanElement, MouseEvent>
) {
if (
limitNotReached &&
!readOnly &&
(!('key' in event) || event.key === 'Enter')
) {
const value = parent.value!.slice();
if (limitNotReached && !readOnly && (!('key' in event) || event.key === 'Enter')) {
const value = parent.value.slice();
value.splice(nameIndex, 1);
parent.onChange(value);
}
@@ -42,22 +29,20 @@ function ListDel({ disabled, name, readOnly, ...props }: ListDelFieldProps) {
return (
<span
className='autoform__list_del_field'
className="autoform__list_del_field"
{...filterDOMProps(props)}
onClick={onAction}
onKeyDown={onAction}
role="button"
tabIndex={0}
>
<div className='line'></div>
<div className="line"></div>
<TrashIcon />
</span>
);
}
export default connectField<ListDelFieldProps>(ListDel, {
initialValue: false,
kind: 'leaf',
kind: 'leaf'
});

View File

@@ -1,8 +1,6 @@
.autoform__list_field {
margin-bottom: 1rem;
margin-top: 1rem;
padding: 10px;
border: 1px solid var(--frontmatter-list-border, rgba(255, 255, 255, 0.2));
}
}

View File

@@ -27,15 +27,16 @@ function List({
<LabelField label={label} id={props.id} required={props.required} />
{value?.map((item, itemIndex) =>
Children.map(children, (child, childIndex) =>
Children.map(children as React.ReactElement[], (child: React.ReactElement, childIndex) =>
isValidElement(child)
? cloneElement(child, {
key: `${itemIndex}-${childIndex}`,
name: (child.props.name || "").replace('$', '' + itemIndex),
...itemProps,
// name: '',
// name: (child.props.name || '').replace('$', '' + itemIndex),
...itemProps
})
: child,
),
: child
)
)}
<ListAddField initialCount={initialCount} name="$" />

View File

@@ -7,9 +7,7 @@ import ListDelField from './ListDelField';
export type ListItemFieldProps = { children?: ReactNode; value?: unknown };
function ListItem({
children = <AutoField label={null} name="" />,
}: ListItemFieldProps) {
function ListItem({ children = <AutoField label={null} name="" /> }: ListItemFieldProps) {
return (
<div>
<ListDelField name="" />
@@ -19,5 +17,5 @@ function ListItem({
}
export default connectField<ListItemFieldProps>(ListItem, {
initialValue: false,
initialValue: false
});

View File

@@ -28,7 +28,7 @@ function LongText({
disabled={disabled}
id={id}
name={name}
onChange={event => onChange(event.target.value)}
onChange={(event) => onChange(event.target.value)}
placeholder={placeholder}
readOnly={readOnly}
ref={inputRef}

View File

@@ -4,27 +4,14 @@ import { HTMLFieldProps, connectField, filterDOMProps } from 'uniforms';
import AutoField from './AutoField';
import { LabelField } from './LabelField';
export type NestFieldProps = HTMLFieldProps<
object,
HTMLDivElement,
{ itemProps?: object }
>;
export type NestFieldProps = HTMLFieldProps<object, HTMLDivElement, { itemProps?: object }>;
function Nest({
children,
fields,
itemProps,
label,
...props
}: NestFieldProps) {
function Nest({ children, fields, itemProps, label, ...props }: NestFieldProps) {
return (
<div {...filterDOMProps(props)}>
<LabelField label={label} id={props.id} required={props.required} />
{children ||
fields.map(field => (
<AutoField key={field} name={field} {...itemProps} />
))}
{children || fields.map((field) => <AutoField key={field} name={field} {...itemProps} />)}
</div>
);
}

View File

@@ -35,7 +35,7 @@ function Num({
max={max}
min={min}
name={name}
onChange={event => {
onChange={(event) => {
const parse = decimal ? parseFloat : parseInt;
const value = parse(event.target.value);
onChange(isNaN(value) ? undefined : value);

View File

@@ -1,3 +1,4 @@
/* eslint-disable class-methods-use-this */
import { QuickForm } from 'uniforms';
import AutoField from './AutoField';
@@ -5,6 +6,7 @@ import BaseForm from './BaseForm';
import ErrorsField from './ErrorsField';
import SubmitField from './SubmitField';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function Quick(parent: any) {
class _ extends QuickForm.Quick(parent) {
static Quick = Quick;

View File

@@ -5,7 +5,7 @@ import { LabelField } from './LabelField';
const base64: typeof btoa =
typeof btoa === 'undefined'
? /* istanbul ignore next */ x => Buffer.from(x).toString('base64')
? /* istanbul ignore next */ (x) => Buffer.from(x).toString('base64')
: btoa;
const escape = (x: string) => base64(encodeURIComponent(x)).replace(/=+$/, '');
@@ -35,7 +35,7 @@ function Radio({
<div {...omit(filterDOMProps(props), ['checkboxes'])}>
<LabelField label={label} id={id} required={props.required} />
{allowedValues?.map(item => (
{allowedValues?.map((item) => (
<div key={item}>
<input
checked={item === value}
@@ -50,9 +50,7 @@ function Radio({
type="radio"
/>
<label htmlFor={`${id}-${escape(item)}`}>
{transform ? transform(item) : item}
</label>
<label htmlFor={`${id}-${escape(item)}`}>{transform ? transform(item) : item}</label>
</div>
))}
</div>

View File

@@ -1,5 +1,3 @@
.autoform__select_field {
color: var(--frontmatter-select-foreground, var(--vscode-editor-foreground));
}
}

View File

@@ -7,7 +7,7 @@ import './SelectField.css';
const base64: typeof btoa =
typeof btoa === 'undefined'
? /* istanbul ignore next */ x => Buffer.from(x).toString('base64')
? /* istanbul ignore next */ (x) => Buffer.from(x).toString('base64')
: btoa;
const escape = (x: string) => base64(encodeURIComponent(x)).replace(/=+$/, '');
@@ -24,7 +24,7 @@ export type SelectFieldProps = HTMLFieldProps<
>;
function Select({
allowedValues,
allowedValues = [],
checkboxes,
disabled,
fieldType,
@@ -38,21 +38,19 @@ function Select({
required,
disableItem,
transform,
value,
value = [],
...props
}: SelectFieldProps) {
const multiple = fieldType === Array;
return (
<div className='autoform__select_field' {...filterDOMProps(props)}>
<div className="autoform__select_field" {...filterDOMProps(props)}>
<LabelField label={label} id={id} required={required} />
{checkboxes ? (
allowedValues!.map(item => (
allowedValues.map((item) => (
<div key={item}>
<input
checked={
fieldType === Array ? value!.includes(item) : value === item
}
checked={fieldType === Array ? value.includes(item) : value === item}
disabled={disableItem?.(item) ?? disabled}
id={`${id}-${escape(item)}`}
name={name}
@@ -64,9 +62,7 @@ function Select({
type="checkbox"
/>
<label htmlFor={`${id}-${escape(item)}`}>
{transform ? transform(item) : item}
</label>
<label htmlFor={`${id}-${escape(item)}`}>{transform ? transform(item) : item}</label>
</div>
))
) : (
@@ -75,7 +71,7 @@ function Select({
id={id}
multiple={multiple}
name={name}
onChange={event => {
onChange={(event) => {
if (!readOnly) {
const item = event.target.value;
if (multiple) {
@@ -88,7 +84,7 @@ function Select({
}}
ref={inputRef}
value={value ?? ''}
style={{ width: "100%", padding: "0.5rem" }}
style={{ width: '100%', padding: '0.5rem' }}
>
{(!!placeholder || !required || value === undefined) && !multiple && (
<option value="" disabled={required} hidden={required}>
@@ -96,7 +92,7 @@ function Select({
</option>
)}
{allowedValues?.map(value => (
{allowedValues?.map((value) => (
<option disabled={disableItem?.(value)} key={value} value={value}>
{transform ? transform(value) : value}
</option>

View File

@@ -23,7 +23,6 @@ function Text({
value,
...props
}: TextFieldProps) {
return (
<div {...filterDOMProps(props)}>
<LabelField label={label} id={id} required={props.required} />
@@ -33,7 +32,7 @@ function Text({
disabled={disabled}
id={id}
name={name}
onChange={event => onChange(event.target.value)}
onChange={(event) => onChange(event.target.value)}
placeholder={placeholder}
readOnly={readOnly}
ref={inputRef}

View File

@@ -2,6 +2,7 @@ import { ValidatedForm } from 'uniforms';
import BaseForm from './BaseForm';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function Validated(parent: any) {
class _ extends ValidatedForm.Validated(parent) {
static Validated = Validated;

View File

@@ -3,46 +3,46 @@ import { ContentType } from './../models/PanelSettings';
export const DEFAULT_CONTENT_TYPE_NAME = 'default';
export const DEFAULT_CONTENT_TYPE: ContentType = {
"name": "default",
"pageBundle": false,
"previewPath": null,
"fields": [
name: 'default',
pageBundle: false,
previewPath: null,
fields: [
{
"title": "Title",
"name": "title",
"type": "string"
title: 'Title',
name: 'title',
type: 'string'
},
{
"title": "Description",
"name": "description",
"type": "string"
title: 'Description',
name: 'description',
type: 'string'
},
{
"title": "Publishing date",
"name": "date",
"type": "datetime",
"default": "{{now}}",
"isPublishDate": true
title: 'Publishing date',
name: 'date',
type: 'datetime',
default: '{{now}}',
isPublishDate: true
},
{
"title": "Content preview",
"name": "preview",
"type": "image"
title: 'Content preview',
name: 'preview',
type: 'image'
},
{
"title": "Is in draft",
"name": "draft",
"type": "draft"
title: 'Is in draft',
name: 'draft',
type: 'draft'
},
{
"title": "Tags",
"name": "tags",
"type": "tags"
title: 'Tags',
name: 'tags',
type: 'tags'
},
{
"title": "Categories",
"name": "categories",
"type": "categories"
title: 'Categories',
name: 'categories',
type: 'categories'
}
]
};
};

View File

@@ -1,5 +1,3 @@
export const DefaultFieldValues = {
faultyCustomPlaceholder: "<failed to process>"
}
faultyCustomPlaceholder: '<failed to process>'
};

View File

@@ -1,7 +1,7 @@
export const DefaultFields = {
PublishingDate: `date`,
LastModified: `lastmod`,
Description: `description`,
Title: `title`,
Slug: `slug`
};

View File

@@ -1,3 +1 @@
export const DEFAULT_FILE_TYPES = [".md", ".markdown", ".mdx"];
export const DEFAULT_FILE_TYPES = ['.md', '.markdown', '.mdx'];

View File

@@ -1,4 +1,4 @@
const extensionName = "frontMatter";
const extensionName = 'frontMatter';
export const EXTENSION_ID = 'eliostruyf.vscode-front-matter';
export const EXTENSION_BETA_ID = 'eliostruyf.vscode-front-matter-beta';
@@ -8,68 +8,71 @@ export const getCommandName = (command: string) => {
};
export const COMMAND_NAME = {
init: getCommandName("init"),
insertTags: getCommandName("insertTags"),
insertCategories: getCommandName("insertCategories"),
createTag: getCommandName("createTag"),
createCategory: getCommandName("createCategory"),
exportTaxonomy: getCommandName("exportTaxonomy"),
remap: getCommandName("remap"),
setLastModifiedDate: getCommandName("setLastModifiedDate"),
generateSlug: getCommandName("generateSlug"),
createFromTemplate: getCommandName("createFromTemplate"),
toggleDraft: getCommandName("toggleDraft"),
registerFolder: getCommandName("registerFolder"),
unregisterFolder: getCommandName("unregisterFolder"),
createContent: getCommandName("createContent"),
createByContentType: getCommandName("createByContentType"),
createByTemplate: getCommandName("createByTemplate"),
createTemplate: getCommandName("createTemplate"),
initTemplate: getCommandName("initTemplate"),
collapseSections: getCommandName("collapseSections"),
preview: getCommandName("preview"),
dashboard: getCommandName("dashboard"),
dashboardMedia: getCommandName("dashboard.media"),
dashboardSnippets: getCommandName("dashboard.snippets"),
dashboardData: getCommandName("dashboard.data"),
dashboardTaxonomy: getCommandName("dashboard.taxonomy"),
dashboardClose: getCommandName("dashboard.close"),
promote: getCommandName("promoteSettings"),
createFolder: getCommandName("createFolder"),
diagnostics: getCommandName("diagnostics"),
modeSwitch: getCommandName("mode.switch"),
init: getCommandName('init'),
insertTags: getCommandName('insertTags'),
insertCategories: getCommandName('insertCategories'),
createTag: getCommandName('createTag'),
createCategory: getCommandName('createCategory'),
exportTaxonomy: getCommandName('exportTaxonomy'),
remap: getCommandName('remap'),
setLastModifiedDate: getCommandName('setLastModifiedDate'),
generateSlug: getCommandName('generateSlug'),
createFromTemplate: getCommandName('createFromTemplate'),
toggleDraft: getCommandName('toggleDraft'),
registerFolder: getCommandName('registerFolder'),
unregisterFolder: getCommandName('unregisterFolder'),
createContent: getCommandName('createContent'),
createByContentType: getCommandName('createByContentType'),
createByTemplate: getCommandName('createByTemplate'),
createTemplate: getCommandName('createTemplate'),
initTemplate: getCommandName('initTemplate'),
collapseSections: getCommandName('collapseSections'),
preview: getCommandName('preview'),
dashboard: getCommandName('dashboard'),
dashboardMedia: getCommandName('dashboard.media'),
dashboardSnippets: getCommandName('dashboard.snippets'),
dashboardData: getCommandName('dashboard.data'),
dashboardTaxonomy: getCommandName('dashboard.taxonomy'),
dashboardClose: getCommandName('dashboard.close'),
promote: getCommandName('promoteSettings'),
createFolder: getCommandName('createFolder'),
diagnostics: getCommandName('diagnostics'),
modeSwitch: getCommandName('mode.switch'),
showOutputChannel: getCommandName("showOutputChannel"),
showOutputChannel: getCommandName('showOutputChannel'),
// Insert dashboards
insertMedia: getCommandName("insertMedia"),
insertSnippet: getCommandName("insertSnippet"),
insertMedia: getCommandName('insertMedia'),
insertSnippet: getCommandName('insertSnippet'),
// WYSIWYG
bold: getCommandName("markup.bold"),
italic: getCommandName("markup.italic"),
strikethrough: getCommandName("markup.strikethrough"),
code: getCommandName("markup.code"),
codeblock: getCommandName("markup.codeblock"),
heading: getCommandName("markup.heading"),
blockquote: getCommandName("markup.blockquote"),
unorderedlist: getCommandName("markup.unorderedlist"),
orderedlist: getCommandName("markup.orderedlist"),
taskList: getCommandName("markup.tasklist"),
hyperlink: getCommandName("markup.hyperlink"),
options: getCommandName("markup.options"),
bold: getCommandName('markup.bold'),
italic: getCommandName('markup.italic'),
strikethrough: getCommandName('markup.strikethrough'),
code: getCommandName('markup.code'),
codeblock: getCommandName('markup.codeblock'),
heading: getCommandName('markup.heading'),
blockquote: getCommandName('markup.blockquote'),
unorderedlist: getCommandName('markup.unorderedlist'),
orderedlist: getCommandName('markup.orderedlist'),
taskList: getCommandName('markup.tasklist'),
hyperlink: getCommandName('markup.hyperlink'),
options: getCommandName('markup.options'),
// Content types
generateContentType: getCommandName("contenttype.generate"),
addMissingFields: getCommandName("contenttype.addMissingFields"),
setContentType: getCommandName("contenttype.setContentType"),
generateContentType: getCommandName('contenttype.generate'),
addMissingFields: getCommandName('contenttype.addMissingFields'),
setContentType: getCommandName('contenttype.setContentType'),
// Git
gitSync: getCommandName("git.sync"),
gitSync: getCommandName('git.sync'),
// Authenticate
authenticate: getCommandName("authenticate"),
authenticate: getCommandName('authenticate'),
// Config
reloadConfig: getCommandName("config.reload"),
};
reloadConfig: getCommandName('config.reload'),
// Cache
clearCache: getCommandName('cache.clear')
};

View File

@@ -1,4 +1,3 @@
export const ExtensionState = {
PagesView: `frontMatter:Pages:ViewType`,
SelectedFolder: `frontMatter:SelectedFolder`,
@@ -8,20 +7,24 @@ export const ExtensionState = {
Dashboard: {
Contents: {
Sorting: `frontMatter:Dashboard:Contents:Sorting`,
Sorting: `frontMatter:Dashboard:Contents:Sorting`
},
Media: {
Sorting: `frontMatter:Dashboard:Media:Sorting`,
Sorting: `frontMatter:Dashboard:Media:Sorting`
},
Pages: {
Cache: `frontMatter:Dashboard:Pages:Cache`,
Index: `frontMatter:Dashboard:Pages:Index`,
Index: `frontMatter:Dashboard:Pages:Index`
}
},
Settings: {
Extends: `frontMatter:Settings:Extends`
},
Updates: {
v7_0_0: {
dateFields: `frontMatter:Updates:v7.0.0:dateFields`
}
}
};
};

View File

@@ -1,25 +1,23 @@
export const FEATURE_FLAG = {
panel: {
globalSettings: "panel.globalSettings",
seo: "panel.seo",
actions: "panel.actions",
metadata: "panel.metadata",
recentlyModified: "panel.recentlyModified",
otherActions: "panel.otherActions",
contentType: "panel.contentType",
globalSettings: 'panel.globalSettings',
seo: 'panel.seo',
actions: 'panel.actions',
metadata: 'panel.metadata',
recentlyModified: 'panel.recentlyModified',
otherActions: 'panel.otherActions',
contentType: 'panel.contentType'
},
dashboard: {
snippets: {
view: "dashboard.snippets.view",
manage: "dashboard.snippets.manage",
view: 'dashboard.snippets.view',
manage: 'dashboard.snippets.manage'
},
data: {
view: "dashboard.data.view",
view: 'dashboard.data.view'
},
taxonomy: {
view: "dashboard.taxonomy.view"
view: 'dashboard.taxonomy.view'
}
}
};
};

View File

@@ -1,89 +1,102 @@
export const FrameworkDetectors = [{
export const FrameworkDetectors = [
{
framework: {
name: "gatsby",
dist: "public",
static: "static",
build: "gatsby build"
name: 'gatsby',
dist: 'public',
static: 'static',
build: 'gatsby build'
},
requiredFiles: ["gatsby-config.js"],
requiredDependencies: ["gatsby"],
requiredFiles: ['gatsby-config.js'],
requiredDependencies: ['gatsby'],
commands: {
start: "npx gatsby develop"
start: 'npx gatsby develop'
}
},
{
framework: {
name: "hugo",
dist: "public",
static: "static",
build: "hugo"
name: 'hugo',
dist: 'public',
static: 'static',
build: 'hugo'
},
requiredFiles: ["config.toml", "config.yaml", "config.yml"],
requiredFiles: ['config.toml', 'config.yaml', 'config.yml'],
commands: {
start: "hugo server -D"
start: 'hugo server -D'
}
},
{
framework: {
name: "next",
dist: ".next",
static: "public",
build: "next build"
name: 'next',
dist: '.next',
static: 'public',
build: 'next build'
},
requiredFiles: ["next.config.js"],
requiredDependencies: ["next"],
requiredFiles: ['next.config.js'],
requiredDependencies: ['next'],
commands: {
start: "npx next dev"
start: 'npx next dev'
}
},
{
framework: {
name: "nuxt",
dist: "dist",
static: "static",
build: "nuxt"
name: 'nuxt',
dist: 'dist',
static: 'static',
build: 'nuxt'
},
requiredFiles: ["nuxt.config.js"],
requiredDependencies: ["nuxt"],
requiredFiles: ['nuxt.config.js'],
requiredDependencies: ['nuxt'],
commands: {
start: "npx nuxt"
start: 'npx nuxt'
}
},
{
framework: {
name: "jekyll",
dist: "_site",
static: "assets",
build: "bundle exec jekyll build"
name: 'jekyll',
dist: '_site',
static: 'assets',
build: 'bundle exec jekyll build'
},
requiredFiles: ["Gemfile"],
requiredDependencies: ["jekyll"],
requiredFiles: ['Gemfile'],
requiredDependencies: ['jekyll'],
commands: {
start: "bundle exec jekyll serve --livereload"
start: 'bundle exec jekyll serve --livereload'
}
},
{
framework: {
name: "docusaurus",
dist: "build",
static: "static",
build: "npx docusaurus build"
name: 'docusaurus',
dist: 'build',
static: 'static',
build: 'npx docusaurus build'
},
requiredFiles: ["docusaurus.config.js"],
requiredDependencies: ["@docusaurus/core"],
requiredFiles: ['docusaurus.config.js'],
requiredDependencies: ['@docusaurus/core'],
commands: {
start: "npx docusaurus start"
start: 'npx docusaurus start'
}
},
{
framework: {
name: "11ty",
dist: "_site",
build: "npx @11ty/eleventy"
name: '11ty',
dist: '_site',
build: 'npx @11ty/eleventy'
},
requiredDependencies: ["@11ty/eleventy"],
requiredDependencies: ['@11ty/eleventy'],
commands: {
start: "npx @11ty/eleventy --serve"
start: 'npx @11ty/eleventy --serve'
}
},
{
framework: {
name: 'hexo',
dist: 'public',
build: 'npx hexo-cli generate'
},
requiredFiles: ['_config.js'],
requiredDependencies: ['hexo'],
commands: {
start: 'npx hexo-cli server'
}
}
];
];

View File

@@ -1,13 +1,11 @@
export const GeneralCommands = {
toWebview: {
setMode: "setMode",
gitSyncingStart: "gitSyncingStart",
gitSyncingEnd: "gitSyncingEnd",
setMode: 'setMode',
gitSyncingStart: 'gitSyncingStart',
gitSyncingEnd: 'gitSyncingEnd'
},
toVSCode: {
openLink: "openLink",
gitSync: "gitSync",
openLink: 'openLink',
gitSync: 'gitSync'
}
};
};

View File

@@ -1,8 +1,10 @@
export const GITHUB_LINK = "https://github.com/estruyf/vscode-front-matter";
export const ISSUE_LINK = "https://github.com/estruyf/vscode-front-matter/issues";
export const SPONSOR_LINK = "https://github.com/sponsors/estruyf";
export const REVIEW_LINK = "https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter&ssr=false#review-details";
export const DOCUMENTATION_LINK = "https://frontmatter.codes/docs";
export const DOCUMENTATION_SETTINGS_LINK = "https://frontmatter.codes/docs/settings";
export const GITHUB_LINK = 'https://github.com/estruyf/vscode-front-matter';
export const ISSUE_LINK = 'https://github.com/estruyf/vscode-front-matter/issues';
export const SPONSOR_LINK = 'https://github.com/sponsors/estruyf';
export const REVIEW_LINK =
'https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter&ssr=false#review-details';
export const DOCUMENTATION_LINK = 'https://frontmatter.codes/docs';
export const DOCUMENTATION_SETTINGS_LINK = 'https://frontmatter.codes/docs/settings';
export const SENTRY_LINK = "https://1ac45704bbe74264a7b4674bdc2abf48@o1022172.ingest.sentry.io/5988293";
export const SENTRY_LINK =
'https://1ac45704bbe74264a7b4674bdc2abf48@o1022172.ingest.sentry.io/5988293';

View File

@@ -1,8 +1,6 @@
export const LocalStore = {
rootFolder: ".frontmatter",
contentFolder: "content",
templatesFolder: "templates",
mediaDatabaseFile: "mediaDb.json"
}
rootFolder: '.frontmatter',
contentFolder: 'content',
templatesFolder: 'templates',
mediaDatabaseFile: 'mediaDb.json'
};

View File

@@ -1 +1 @@
export const HOME_PAGE_NAVIGATION_ID = "FrontMatter:RootFolder";
export const HOME_PAGE_NAVIGATION_ID = 'FrontMatter:RootFolder';

View File

@@ -1,5 +1,3 @@
export const NOTIFICATION_TYPE = {
requiredFieldValidation: "requiredFieldValidation",
}
requiredFieldValidation: 'requiredFieldValidation'
};

View File

@@ -1,8 +1,6 @@
export const PreviewCommands = {
toVSCode: {
open: `preview.open`
},
fromVSCode: {}
};
};

View File

@@ -0,0 +1,6 @@
export const STATIC_FOLDER_PLACEHOLDER = {
hexo: {
postsFolder: 'source/_posts',
placeholder: 'hexo:post_asset_folder'
}
};

View File

@@ -46,5 +46,5 @@ export const TelemetryEvent = {
webviewTaxonomyDashboard: 'webviewTaxonomyDashboard',
// Git
gitSync: 'gitSync',
};
gitSync: 'gitSync'
};

View File

@@ -1,439 +1,438 @@
/**
* An inlined enum containing useful character codes (to be used with String.charCodeAt).
* Please leave the const keyword such that it gets inlined when compiled to JavaScript!
*
*
* SOURCE: https://github.com/microsoft/vscode/blob/32b031eeefc4fd27a21659d35070967bfe965bcc/src/vs/base/common/charCode.ts
*/
export const enum CharCode {
Null = 0,
/**
* The `\b` character.
*/
Backspace = 8,
/**
* The `\t` character.
*/
Tab = 9,
/**
* The `\n` character.
*/
LineFeed = 10,
/**
* The `\r` character.
*/
CarriageReturn = 13,
Space = 32,
/**
* The `!` character.
*/
ExclamationMark = 33,
/**
* The `"` character.
*/
DoubleQuote = 34,
/**
* The `#` character.
*/
Hash = 35,
/**
* The `$` character.
*/
DollarSign = 36,
/**
* The `%` character.
*/
PercentSign = 37,
/**
* The `&` character.
*/
Ampersand = 38,
/**
* The `'` character.
*/
SingleQuote = 39,
/**
* The `(` character.
*/
OpenParen = 40,
/**
* The `)` character.
*/
CloseParen = 41,
/**
* The `*` character.
*/
Asterisk = 42,
/**
* The `+` character.
*/
Plus = 43,
/**
* The `,` character.
*/
Comma = 44,
/**
* The `-` character.
*/
Dash = 45,
/**
* The `.` character.
*/
Period = 46,
/**
* The `/` character.
*/
Slash = 47,
export const enum CharCode {
Null = 0,
/**
* The `\b` character.
*/
Backspace = 8,
/**
* The `\t` character.
*/
Tab = 9,
/**
* The `\n` character.
*/
LineFeed = 10,
/**
* The `\r` character.
*/
CarriageReturn = 13,
Space = 32,
/**
* The `!` character.
*/
ExclamationMark = 33,
/**
* The `"` character.
*/
DoubleQuote = 34,
/**
* The `#` character.
*/
Hash = 35,
/**
* The `$` character.
*/
DollarSign = 36,
/**
* The `%` character.
*/
PercentSign = 37,
/**
* The `&` character.
*/
Ampersand = 38,
/**
* The `'` character.
*/
SingleQuote = 39,
/**
* The `(` character.
*/
OpenParen = 40,
/**
* The `)` character.
*/
CloseParen = 41,
/**
* The `*` character.
*/
Asterisk = 42,
/**
* The `+` character.
*/
Plus = 43,
/**
* The `,` character.
*/
Comma = 44,
/**
* The `-` character.
*/
Dash = 45,
/**
* The `.` character.
*/
Period = 46,
/**
* The `/` character.
*/
Slash = 47,
Digit0 = 48,
Digit1 = 49,
Digit2 = 50,
Digit3 = 51,
Digit4 = 52,
Digit5 = 53,
Digit6 = 54,
Digit7 = 55,
Digit8 = 56,
Digit9 = 57,
Digit0 = 48,
Digit1 = 49,
Digit2 = 50,
Digit3 = 51,
Digit4 = 52,
Digit5 = 53,
Digit6 = 54,
Digit7 = 55,
Digit8 = 56,
Digit9 = 57,
/**
* The `:` character.
*/
Colon = 58,
/**
* The `;` character.
*/
Semicolon = 59,
/**
* The `<` character.
*/
LessThan = 60,
/**
* The `=` character.
*/
Equals = 61,
/**
* The `>` character.
*/
GreaterThan = 62,
/**
* The `?` character.
*/
QuestionMark = 63,
/**
* The `@` character.
*/
AtSign = 64,
/**
* The `:` character.
*/
Colon = 58,
/**
* The `;` character.
*/
Semicolon = 59,
/**
* The `<` character.
*/
LessThan = 60,
/**
* The `=` character.
*/
Equals = 61,
/**
* The `>` character.
*/
GreaterThan = 62,
/**
* The `?` character.
*/
QuestionMark = 63,
/**
* The `@` character.
*/
AtSign = 64,
A = 65,
B = 66,
C = 67,
D = 68,
E = 69,
F = 70,
G = 71,
H = 72,
I = 73,
J = 74,
K = 75,
L = 76,
M = 77,
N = 78,
O = 79,
P = 80,
Q = 81,
R = 82,
S = 83,
T = 84,
U = 85,
V = 86,
W = 87,
X = 88,
Y = 89,
Z = 90,
A = 65,
B = 66,
C = 67,
D = 68,
E = 69,
F = 70,
G = 71,
H = 72,
I = 73,
J = 74,
K = 75,
L = 76,
M = 77,
N = 78,
O = 79,
P = 80,
Q = 81,
R = 82,
S = 83,
T = 84,
U = 85,
V = 86,
W = 87,
X = 88,
Y = 89,
Z = 90,
/**
* The `[` character.
*/
OpenSquareBracket = 91,
/**
* The `\` character.
*/
Backslash = 92,
/**
* The `]` character.
*/
CloseSquareBracket = 93,
/**
* The `^` character.
*/
Caret = 94,
/**
* The `_` character.
*/
Underline = 95,
/**
* The ``(`)`` character.
*/
BackTick = 96,
/**
* The `[` character.
*/
OpenSquareBracket = 91,
/**
* The `\` character.
*/
Backslash = 92,
/**
* The `]` character.
*/
CloseSquareBracket = 93,
/**
* The `^` character.
*/
Caret = 94,
/**
* The `_` character.
*/
Underline = 95,
/**
* The ``(`)`` character.
*/
BackTick = 96,
a = 97,
b = 98,
c = 99,
d = 100,
e = 101,
f = 102,
g = 103,
h = 104,
i = 105,
j = 106,
k = 107,
l = 108,
m = 109,
n = 110,
o = 111,
p = 112,
q = 113,
r = 114,
s = 115,
t = 116,
u = 117,
v = 118,
w = 119,
x = 120,
y = 121,
z = 122,
a = 97,
b = 98,
c = 99,
d = 100,
e = 101,
f = 102,
g = 103,
h = 104,
i = 105,
j = 106,
k = 107,
l = 108,
m = 109,
n = 110,
o = 111,
p = 112,
q = 113,
r = 114,
s = 115,
t = 116,
u = 117,
v = 118,
w = 119,
x = 120,
y = 121,
z = 122,
/**
* The `{` character.
*/
OpenCurlyBrace = 123,
/**
* The `|` character.
*/
Pipe = 124,
/**
* The `}` character.
*/
CloseCurlyBrace = 125,
/**
* The `~` character.
*/
Tilde = 126,
/**
* The `{` character.
*/
OpenCurlyBrace = 123,
/**
* The `|` character.
*/
Pipe = 124,
/**
* The `}` character.
*/
CloseCurlyBrace = 125,
/**
* The `~` character.
*/
Tilde = 126,
U_Combining_Grave_Accent = 0x0300, // U+0300 Combining Grave Accent
U_Combining_Acute_Accent = 0x0301, // U+0301 Combining Acute Accent
U_Combining_Circumflex_Accent = 0x0302, // U+0302 Combining Circumflex Accent
U_Combining_Tilde = 0x0303, // U+0303 Combining Tilde
U_Combining_Macron = 0x0304, // U+0304 Combining Macron
U_Combining_Overline = 0x0305, // U+0305 Combining Overline
U_Combining_Breve = 0x0306, // U+0306 Combining Breve
U_Combining_Dot_Above = 0x0307, // U+0307 Combining Dot Above
U_Combining_Diaeresis = 0x0308, // U+0308 Combining Diaeresis
U_Combining_Hook_Above = 0x0309, // U+0309 Combining Hook Above
U_Combining_Ring_Above = 0x030A, // U+030A Combining Ring Above
U_Combining_Double_Acute_Accent = 0x030B, // U+030B Combining Double Acute Accent
U_Combining_Caron = 0x030C, // U+030C Combining Caron
U_Combining_Vertical_Line_Above = 0x030D, // U+030D Combining Vertical Line Above
U_Combining_Double_Vertical_Line_Above = 0x030E, // U+030E Combining Double Vertical Line Above
U_Combining_Double_Grave_Accent = 0x030F, // U+030F Combining Double Grave Accent
U_Combining_Candrabindu = 0x0310, // U+0310 Combining Candrabindu
U_Combining_Inverted_Breve = 0x0311, // U+0311 Combining Inverted Breve
U_Combining_Turned_Comma_Above = 0x0312, // U+0312 Combining Turned Comma Above
U_Combining_Comma_Above = 0x0313, // U+0313 Combining Comma Above
U_Combining_Reversed_Comma_Above = 0x0314, // U+0314 Combining Reversed Comma Above
U_Combining_Comma_Above_Right = 0x0315, // U+0315 Combining Comma Above Right
U_Combining_Grave_Accent_Below = 0x0316, // U+0316 Combining Grave Accent Below
U_Combining_Acute_Accent_Below = 0x0317, // U+0317 Combining Acute Accent Below
U_Combining_Left_Tack_Below = 0x0318, // U+0318 Combining Left Tack Below
U_Combining_Right_Tack_Below = 0x0319, // U+0319 Combining Right Tack Below
U_Combining_Left_Angle_Above = 0x031A, // U+031A Combining Left Angle Above
U_Combining_Horn = 0x031B, // U+031B Combining Horn
U_Combining_Left_Half_Ring_Below = 0x031C, // U+031C Combining Left Half Ring Below
U_Combining_Up_Tack_Below = 0x031D, // U+031D Combining Up Tack Below
U_Combining_Down_Tack_Below = 0x031E, // U+031E Combining Down Tack Below
U_Combining_Plus_Sign_Below = 0x031F, // U+031F Combining Plus Sign Below
U_Combining_Minus_Sign_Below = 0x0320, // U+0320 Combining Minus Sign Below
U_Combining_Palatalized_Hook_Below = 0x0321, // U+0321 Combining Palatalized Hook Below
U_Combining_Retroflex_Hook_Below = 0x0322, // U+0322 Combining Retroflex Hook Below
U_Combining_Dot_Below = 0x0323, // U+0323 Combining Dot Below
U_Combining_Diaeresis_Below = 0x0324, // U+0324 Combining Diaeresis Below
U_Combining_Ring_Below = 0x0325, // U+0325 Combining Ring Below
U_Combining_Comma_Below = 0x0326, // U+0326 Combining Comma Below
U_Combining_Cedilla = 0x0327, // U+0327 Combining Cedilla
U_Combining_Ogonek = 0x0328, // U+0328 Combining Ogonek
U_Combining_Vertical_Line_Below = 0x0329, // U+0329 Combining Vertical Line Below
U_Combining_Bridge_Below = 0x032A, // U+032A Combining Bridge Below
U_Combining_Inverted_Double_Arch_Below = 0x032B, // U+032B Combining Inverted Double Arch Below
U_Combining_Caron_Below = 0x032C, // U+032C Combining Caron Below
U_Combining_Circumflex_Accent_Below = 0x032D, // U+032D Combining Circumflex Accent Below
U_Combining_Breve_Below = 0x032E, // U+032E Combining Breve Below
U_Combining_Inverted_Breve_Below = 0x032F, // U+032F Combining Inverted Breve Below
U_Combining_Tilde_Below = 0x0330, // U+0330 Combining Tilde Below
U_Combining_Macron_Below = 0x0331, // U+0331 Combining Macron Below
U_Combining_Low_Line = 0x0332, // U+0332 Combining Low Line
U_Combining_Double_Low_Line = 0x0333, // U+0333 Combining Double Low Line
U_Combining_Tilde_Overlay = 0x0334, // U+0334 Combining Tilde Overlay
U_Combining_Short_Stroke_Overlay = 0x0335, // U+0335 Combining Short Stroke Overlay
U_Combining_Long_Stroke_Overlay = 0x0336, // U+0336 Combining Long Stroke Overlay
U_Combining_Short_Solidus_Overlay = 0x0337, // U+0337 Combining Short Solidus Overlay
U_Combining_Long_Solidus_Overlay = 0x0338, // U+0338 Combining Long Solidus Overlay
U_Combining_Right_Half_Ring_Below = 0x0339, // U+0339 Combining Right Half Ring Below
U_Combining_Inverted_Bridge_Below = 0x033A, // U+033A Combining Inverted Bridge Below
U_Combining_Square_Below = 0x033B, // U+033B Combining Square Below
U_Combining_Seagull_Below = 0x033C, // U+033C Combining Seagull Below
U_Combining_X_Above = 0x033D, // U+033D Combining X Above
U_Combining_Vertical_Tilde = 0x033E, // U+033E Combining Vertical Tilde
U_Combining_Double_Overline = 0x033F, // U+033F Combining Double Overline
U_Combining_Grave_Tone_Mark = 0x0340, // U+0340 Combining Grave Tone Mark
U_Combining_Acute_Tone_Mark = 0x0341, // U+0341 Combining Acute Tone Mark
U_Combining_Greek_Perispomeni = 0x0342, // U+0342 Combining Greek Perispomeni
U_Combining_Greek_Koronis = 0x0343, // U+0343 Combining Greek Koronis
U_Combining_Greek_Dialytika_Tonos = 0x0344, // U+0344 Combining Greek Dialytika Tonos
U_Combining_Greek_Ypogegrammeni = 0x0345, // U+0345 Combining Greek Ypogegrammeni
U_Combining_Bridge_Above = 0x0346, // U+0346 Combining Bridge Above
U_Combining_Equals_Sign_Below = 0x0347, // U+0347 Combining Equals Sign Below
U_Combining_Double_Vertical_Line_Below = 0x0348, // U+0348 Combining Double Vertical Line Below
U_Combining_Left_Angle_Below = 0x0349, // U+0349 Combining Left Angle Below
U_Combining_Not_Tilde_Above = 0x034A, // U+034A Combining Not Tilde Above
U_Combining_Homothetic_Above = 0x034B, // U+034B Combining Homothetic Above
U_Combining_Almost_Equal_To_Above = 0x034C, // U+034C Combining Almost Equal To Above
U_Combining_Left_Right_Arrow_Below = 0x034D, // U+034D Combining Left Right Arrow Below
U_Combining_Upwards_Arrow_Below = 0x034E, // U+034E Combining Upwards Arrow Below
U_Combining_Grapheme_Joiner = 0x034F, // U+034F Combining Grapheme Joiner
U_Combining_Right_Arrowhead_Above = 0x0350, // U+0350 Combining Right Arrowhead Above
U_Combining_Left_Half_Ring_Above = 0x0351, // U+0351 Combining Left Half Ring Above
U_Combining_Fermata = 0x0352, // U+0352 Combining Fermata
U_Combining_X_Below = 0x0353, // U+0353 Combining X Below
U_Combining_Left_Arrowhead_Below = 0x0354, // U+0354 Combining Left Arrowhead Below
U_Combining_Right_Arrowhead_Below = 0x0355, // U+0355 Combining Right Arrowhead Below
U_Combining_Right_Arrowhead_And_Up_Arrowhead_Below = 0x0356, // U+0356 Combining Right Arrowhead And Up Arrowhead Below
U_Combining_Right_Half_Ring_Above = 0x0357, // U+0357 Combining Right Half Ring Above
U_Combining_Dot_Above_Right = 0x0358, // U+0358 Combining Dot Above Right
U_Combining_Asterisk_Below = 0x0359, // U+0359 Combining Asterisk Below
U_Combining_Double_Ring_Below = 0x035A, // U+035A Combining Double Ring Below
U_Combining_Zigzag_Above = 0x035B, // U+035B Combining Zigzag Above
U_Combining_Double_Breve_Below = 0x035C, // U+035C Combining Double Breve Below
U_Combining_Double_Breve = 0x035D, // U+035D Combining Double Breve
U_Combining_Double_Macron = 0x035E, // U+035E Combining Double Macron
U_Combining_Double_Macron_Below = 0x035F, // U+035F Combining Double Macron Below
U_Combining_Double_Tilde = 0x0360, // U+0360 Combining Double Tilde
U_Combining_Double_Inverted_Breve = 0x0361, // U+0361 Combining Double Inverted Breve
U_Combining_Double_Rightwards_Arrow_Below = 0x0362, // U+0362 Combining Double Rightwards Arrow Below
U_Combining_Latin_Small_Letter_A = 0x0363, // U+0363 Combining Latin Small Letter A
U_Combining_Latin_Small_Letter_E = 0x0364, // U+0364 Combining Latin Small Letter E
U_Combining_Latin_Small_Letter_I = 0x0365, // U+0365 Combining Latin Small Letter I
U_Combining_Latin_Small_Letter_O = 0x0366, // U+0366 Combining Latin Small Letter O
U_Combining_Latin_Small_Letter_U = 0x0367, // U+0367 Combining Latin Small Letter U
U_Combining_Latin_Small_Letter_C = 0x0368, // U+0368 Combining Latin Small Letter C
U_Combining_Latin_Small_Letter_D = 0x0369, // U+0369 Combining Latin Small Letter D
U_Combining_Latin_Small_Letter_H = 0x036A, // U+036A Combining Latin Small Letter H
U_Combining_Latin_Small_Letter_M = 0x036B, // U+036B Combining Latin Small Letter M
U_Combining_Latin_Small_Letter_R = 0x036C, // U+036C Combining Latin Small Letter R
U_Combining_Latin_Small_Letter_T = 0x036D, // U+036D Combining Latin Small Letter T
U_Combining_Latin_Small_Letter_V = 0x036E, // U+036E Combining Latin Small Letter V
U_Combining_Latin_Small_Letter_X = 0x036F, // U+036F Combining Latin Small Letter X
U_Combining_Grave_Accent = 0x0300, // U+0300 Combining Grave Accent
U_Combining_Acute_Accent = 0x0301, // U+0301 Combining Acute Accent
U_Combining_Circumflex_Accent = 0x0302, // U+0302 Combining Circumflex Accent
U_Combining_Tilde = 0x0303, // U+0303 Combining Tilde
U_Combining_Macron = 0x0304, // U+0304 Combining Macron
U_Combining_Overline = 0x0305, // U+0305 Combining Overline
U_Combining_Breve = 0x0306, // U+0306 Combining Breve
U_Combining_Dot_Above = 0x0307, // U+0307 Combining Dot Above
U_Combining_Diaeresis = 0x0308, // U+0308 Combining Diaeresis
U_Combining_Hook_Above = 0x0309, // U+0309 Combining Hook Above
U_Combining_Ring_Above = 0x030a, // U+030A Combining Ring Above
U_Combining_Double_Acute_Accent = 0x030b, // U+030B Combining Double Acute Accent
U_Combining_Caron = 0x030c, // U+030C Combining Caron
U_Combining_Vertical_Line_Above = 0x030d, // U+030D Combining Vertical Line Above
U_Combining_Double_Vertical_Line_Above = 0x030e, // U+030E Combining Double Vertical Line Above
U_Combining_Double_Grave_Accent = 0x030f, // U+030F Combining Double Grave Accent
U_Combining_Candrabindu = 0x0310, // U+0310 Combining Candrabindu
U_Combining_Inverted_Breve = 0x0311, // U+0311 Combining Inverted Breve
U_Combining_Turned_Comma_Above = 0x0312, // U+0312 Combining Turned Comma Above
U_Combining_Comma_Above = 0x0313, // U+0313 Combining Comma Above
U_Combining_Reversed_Comma_Above = 0x0314, // U+0314 Combining Reversed Comma Above
U_Combining_Comma_Above_Right = 0x0315, // U+0315 Combining Comma Above Right
U_Combining_Grave_Accent_Below = 0x0316, // U+0316 Combining Grave Accent Below
U_Combining_Acute_Accent_Below = 0x0317, // U+0317 Combining Acute Accent Below
U_Combining_Left_Tack_Below = 0x0318, // U+0318 Combining Left Tack Below
U_Combining_Right_Tack_Below = 0x0319, // U+0319 Combining Right Tack Below
U_Combining_Left_Angle_Above = 0x031a, // U+031A Combining Left Angle Above
U_Combining_Horn = 0x031b, // U+031B Combining Horn
U_Combining_Left_Half_Ring_Below = 0x031c, // U+031C Combining Left Half Ring Below
U_Combining_Up_Tack_Below = 0x031d, // U+031D Combining Up Tack Below
U_Combining_Down_Tack_Below = 0x031e, // U+031E Combining Down Tack Below
U_Combining_Plus_Sign_Below = 0x031f, // U+031F Combining Plus Sign Below
U_Combining_Minus_Sign_Below = 0x0320, // U+0320 Combining Minus Sign Below
U_Combining_Palatalized_Hook_Below = 0x0321, // U+0321 Combining Palatalized Hook Below
U_Combining_Retroflex_Hook_Below = 0x0322, // U+0322 Combining Retroflex Hook Below
U_Combining_Dot_Below = 0x0323, // U+0323 Combining Dot Below
U_Combining_Diaeresis_Below = 0x0324, // U+0324 Combining Diaeresis Below
U_Combining_Ring_Below = 0x0325, // U+0325 Combining Ring Below
U_Combining_Comma_Below = 0x0326, // U+0326 Combining Comma Below
U_Combining_Cedilla = 0x0327, // U+0327 Combining Cedilla
U_Combining_Ogonek = 0x0328, // U+0328 Combining Ogonek
U_Combining_Vertical_Line_Below = 0x0329, // U+0329 Combining Vertical Line Below
U_Combining_Bridge_Below = 0x032a, // U+032A Combining Bridge Below
U_Combining_Inverted_Double_Arch_Below = 0x032b, // U+032B Combining Inverted Double Arch Below
U_Combining_Caron_Below = 0x032c, // U+032C Combining Caron Below
U_Combining_Circumflex_Accent_Below = 0x032d, // U+032D Combining Circumflex Accent Below
U_Combining_Breve_Below = 0x032e, // U+032E Combining Breve Below
U_Combining_Inverted_Breve_Below = 0x032f, // U+032F Combining Inverted Breve Below
U_Combining_Tilde_Below = 0x0330, // U+0330 Combining Tilde Below
U_Combining_Macron_Below = 0x0331, // U+0331 Combining Macron Below
U_Combining_Low_Line = 0x0332, // U+0332 Combining Low Line
U_Combining_Double_Low_Line = 0x0333, // U+0333 Combining Double Low Line
U_Combining_Tilde_Overlay = 0x0334, // U+0334 Combining Tilde Overlay
U_Combining_Short_Stroke_Overlay = 0x0335, // U+0335 Combining Short Stroke Overlay
U_Combining_Long_Stroke_Overlay = 0x0336, // U+0336 Combining Long Stroke Overlay
U_Combining_Short_Solidus_Overlay = 0x0337, // U+0337 Combining Short Solidus Overlay
U_Combining_Long_Solidus_Overlay = 0x0338, // U+0338 Combining Long Solidus Overlay
U_Combining_Right_Half_Ring_Below = 0x0339, // U+0339 Combining Right Half Ring Below
U_Combining_Inverted_Bridge_Below = 0x033a, // U+033A Combining Inverted Bridge Below
U_Combining_Square_Below = 0x033b, // U+033B Combining Square Below
U_Combining_Seagull_Below = 0x033c, // U+033C Combining Seagull Below
U_Combining_X_Above = 0x033d, // U+033D Combining X Above
U_Combining_Vertical_Tilde = 0x033e, // U+033E Combining Vertical Tilde
U_Combining_Double_Overline = 0x033f, // U+033F Combining Double Overline
U_Combining_Grave_Tone_Mark = 0x0340, // U+0340 Combining Grave Tone Mark
U_Combining_Acute_Tone_Mark = 0x0341, // U+0341 Combining Acute Tone Mark
U_Combining_Greek_Perispomeni = 0x0342, // U+0342 Combining Greek Perispomeni
U_Combining_Greek_Koronis = 0x0343, // U+0343 Combining Greek Koronis
U_Combining_Greek_Dialytika_Tonos = 0x0344, // U+0344 Combining Greek Dialytika Tonos
U_Combining_Greek_Ypogegrammeni = 0x0345, // U+0345 Combining Greek Ypogegrammeni
U_Combining_Bridge_Above = 0x0346, // U+0346 Combining Bridge Above
U_Combining_Equals_Sign_Below = 0x0347, // U+0347 Combining Equals Sign Below
U_Combining_Double_Vertical_Line_Below = 0x0348, // U+0348 Combining Double Vertical Line Below
U_Combining_Left_Angle_Below = 0x0349, // U+0349 Combining Left Angle Below
U_Combining_Not_Tilde_Above = 0x034a, // U+034A Combining Not Tilde Above
U_Combining_Homothetic_Above = 0x034b, // U+034B Combining Homothetic Above
U_Combining_Almost_Equal_To_Above = 0x034c, // U+034C Combining Almost Equal To Above
U_Combining_Left_Right_Arrow_Below = 0x034d, // U+034D Combining Left Right Arrow Below
U_Combining_Upwards_Arrow_Below = 0x034e, // U+034E Combining Upwards Arrow Below
U_Combining_Grapheme_Joiner = 0x034f, // U+034F Combining Grapheme Joiner
U_Combining_Right_Arrowhead_Above = 0x0350, // U+0350 Combining Right Arrowhead Above
U_Combining_Left_Half_Ring_Above = 0x0351, // U+0351 Combining Left Half Ring Above
U_Combining_Fermata = 0x0352, // U+0352 Combining Fermata
U_Combining_X_Below = 0x0353, // U+0353 Combining X Below
U_Combining_Left_Arrowhead_Below = 0x0354, // U+0354 Combining Left Arrowhead Below
U_Combining_Right_Arrowhead_Below = 0x0355, // U+0355 Combining Right Arrowhead Below
U_Combining_Right_Arrowhead_And_Up_Arrowhead_Below = 0x0356, // U+0356 Combining Right Arrowhead And Up Arrowhead Below
U_Combining_Right_Half_Ring_Above = 0x0357, // U+0357 Combining Right Half Ring Above
U_Combining_Dot_Above_Right = 0x0358, // U+0358 Combining Dot Above Right
U_Combining_Asterisk_Below = 0x0359, // U+0359 Combining Asterisk Below
U_Combining_Double_Ring_Below = 0x035a, // U+035A Combining Double Ring Below
U_Combining_Zigzag_Above = 0x035b, // U+035B Combining Zigzag Above
U_Combining_Double_Breve_Below = 0x035c, // U+035C Combining Double Breve Below
U_Combining_Double_Breve = 0x035d, // U+035D Combining Double Breve
U_Combining_Double_Macron = 0x035e, // U+035E Combining Double Macron
U_Combining_Double_Macron_Below = 0x035f, // U+035F Combining Double Macron Below
U_Combining_Double_Tilde = 0x0360, // U+0360 Combining Double Tilde
U_Combining_Double_Inverted_Breve = 0x0361, // U+0361 Combining Double Inverted Breve
U_Combining_Double_Rightwards_Arrow_Below = 0x0362, // U+0362 Combining Double Rightwards Arrow Below
U_Combining_Latin_Small_Letter_A = 0x0363, // U+0363 Combining Latin Small Letter A
U_Combining_Latin_Small_Letter_E = 0x0364, // U+0364 Combining Latin Small Letter E
U_Combining_Latin_Small_Letter_I = 0x0365, // U+0365 Combining Latin Small Letter I
U_Combining_Latin_Small_Letter_O = 0x0366, // U+0366 Combining Latin Small Letter O
U_Combining_Latin_Small_Letter_U = 0x0367, // U+0367 Combining Latin Small Letter U
U_Combining_Latin_Small_Letter_C = 0x0368, // U+0368 Combining Latin Small Letter C
U_Combining_Latin_Small_Letter_D = 0x0369, // U+0369 Combining Latin Small Letter D
U_Combining_Latin_Small_Letter_H = 0x036a, // U+036A Combining Latin Small Letter H
U_Combining_Latin_Small_Letter_M = 0x036b, // U+036B Combining Latin Small Letter M
U_Combining_Latin_Small_Letter_R = 0x036c, // U+036C Combining Latin Small Letter R
U_Combining_Latin_Small_Letter_T = 0x036d, // U+036D Combining Latin Small Letter T
U_Combining_Latin_Small_Letter_V = 0x036e, // U+036E Combining Latin Small Letter V
U_Combining_Latin_Small_Letter_X = 0x036f, // U+036F Combining Latin Small Letter X
/**
* Unicode Character 'LINE SEPARATOR' (U+2028)
* http://www.fileformat.info/info/unicode/char/2028/index.htm
*/
LINE_SEPARATOR = 0x2028,
/**
* Unicode Character 'PARAGRAPH SEPARATOR' (U+2029)
* http://www.fileformat.info/info/unicode/char/2029/index.htm
*/
PARAGRAPH_SEPARATOR = 0x2029,
/**
* Unicode Character 'NEXT LINE' (U+0085)
* http://www.fileformat.info/info/unicode/char/0085/index.htm
*/
NEXT_LINE = 0x0085,
/**
* Unicode Character 'LINE SEPARATOR' (U+2028)
* http://www.fileformat.info/info/unicode/char/2028/index.htm
*/
LINE_SEPARATOR = 0x2028,
/**
* Unicode Character 'PARAGRAPH SEPARATOR' (U+2029)
* http://www.fileformat.info/info/unicode/char/2029/index.htm
*/
PARAGRAPH_SEPARATOR = 0x2029,
/**
* Unicode Character 'NEXT LINE' (U+0085)
* http://www.fileformat.info/info/unicode/char/0085/index.htm
*/
NEXT_LINE = 0x0085,
// http://www.fileformat.info/info/unicode/category/Sk/list.htm
U_CIRCUMFLEX = 0x005E, // U+005E CIRCUMFLEX
U_GRAVE_ACCENT = 0x0060, // U+0060 GRAVE ACCENT
U_DIAERESIS = 0x00A8, // U+00A8 DIAERESIS
U_MACRON = 0x00AF, // U+00AF MACRON
U_ACUTE_ACCENT = 0x00B4, // U+00B4 ACUTE ACCENT
U_CEDILLA = 0x00B8, // U+00B8 CEDILLA
U_MODIFIER_LETTER_LEFT_ARROWHEAD = 0x02C2, // U+02C2 MODIFIER LETTER LEFT ARROWHEAD
U_MODIFIER_LETTER_RIGHT_ARROWHEAD = 0x02C3, // U+02C3 MODIFIER LETTER RIGHT ARROWHEAD
U_MODIFIER_LETTER_UP_ARROWHEAD = 0x02C4, // U+02C4 MODIFIER LETTER UP ARROWHEAD
U_MODIFIER_LETTER_DOWN_ARROWHEAD = 0x02C5, // U+02C5 MODIFIER LETTER DOWN ARROWHEAD
U_MODIFIER_LETTER_CENTRED_RIGHT_HALF_RING = 0x02D2, // U+02D2 MODIFIER LETTER CENTRED RIGHT HALF RING
U_MODIFIER_LETTER_CENTRED_LEFT_HALF_RING = 0x02D3, // U+02D3 MODIFIER LETTER CENTRED LEFT HALF RING
U_MODIFIER_LETTER_UP_TACK = 0x02D4, // U+02D4 MODIFIER LETTER UP TACK
U_MODIFIER_LETTER_DOWN_TACK = 0x02D5, // U+02D5 MODIFIER LETTER DOWN TACK
U_MODIFIER_LETTER_PLUS_SIGN = 0x02D6, // U+02D6 MODIFIER LETTER PLUS SIGN
U_MODIFIER_LETTER_MINUS_SIGN = 0x02D7, // U+02D7 MODIFIER LETTER MINUS SIGN
U_BREVE = 0x02D8, // U+02D8 BREVE
U_DOT_ABOVE = 0x02D9, // U+02D9 DOT ABOVE
U_RING_ABOVE = 0x02DA, // U+02DA RING ABOVE
U_OGONEK = 0x02DB, // U+02DB OGONEK
U_SMALL_TILDE = 0x02DC, // U+02DC SMALL TILDE
U_DOUBLE_ACUTE_ACCENT = 0x02DD, // U+02DD DOUBLE ACUTE ACCENT
U_MODIFIER_LETTER_RHOTIC_HOOK = 0x02DE, // U+02DE MODIFIER LETTER RHOTIC HOOK
U_MODIFIER_LETTER_CROSS_ACCENT = 0x02DF, // U+02DF MODIFIER LETTER CROSS ACCENT
U_MODIFIER_LETTER_EXTRA_HIGH_TONE_BAR = 0x02E5, // U+02E5 MODIFIER LETTER EXTRA-HIGH TONE BAR
U_MODIFIER_LETTER_HIGH_TONE_BAR = 0x02E6, // U+02E6 MODIFIER LETTER HIGH TONE BAR
U_MODIFIER_LETTER_MID_TONE_BAR = 0x02E7, // U+02E7 MODIFIER LETTER MID TONE BAR
U_MODIFIER_LETTER_LOW_TONE_BAR = 0x02E8, // U+02E8 MODIFIER LETTER LOW TONE BAR
U_MODIFIER_LETTER_EXTRA_LOW_TONE_BAR = 0x02E9, // U+02E9 MODIFIER LETTER EXTRA-LOW TONE BAR
U_MODIFIER_LETTER_YIN_DEPARTING_TONE_MARK = 0x02EA, // U+02EA MODIFIER LETTER YIN DEPARTING TONE MARK
U_MODIFIER_LETTER_YANG_DEPARTING_TONE_MARK = 0x02EB, // U+02EB MODIFIER LETTER YANG DEPARTING TONE MARK
U_MODIFIER_LETTER_UNASPIRATED = 0x02ED, // U+02ED MODIFIER LETTER UNASPIRATED
U_MODIFIER_LETTER_LOW_DOWN_ARROWHEAD = 0x02EF, // U+02EF MODIFIER LETTER LOW DOWN ARROWHEAD
U_MODIFIER_LETTER_LOW_UP_ARROWHEAD = 0x02F0, // U+02F0 MODIFIER LETTER LOW UP ARROWHEAD
U_MODIFIER_LETTER_LOW_LEFT_ARROWHEAD = 0x02F1, // U+02F1 MODIFIER LETTER LOW LEFT ARROWHEAD
U_MODIFIER_LETTER_LOW_RIGHT_ARROWHEAD = 0x02F2, // U+02F2 MODIFIER LETTER LOW RIGHT ARROWHEAD
U_MODIFIER_LETTER_LOW_RING = 0x02F3, // U+02F3 MODIFIER LETTER LOW RING
U_MODIFIER_LETTER_MIDDLE_GRAVE_ACCENT = 0x02F4, // U+02F4 MODIFIER LETTER MIDDLE GRAVE ACCENT
U_MODIFIER_LETTER_MIDDLE_DOUBLE_GRAVE_ACCENT = 0x02F5, // U+02F5 MODIFIER LETTER MIDDLE DOUBLE GRAVE ACCENT
U_MODIFIER_LETTER_MIDDLE_DOUBLE_ACUTE_ACCENT = 0x02F6, // U+02F6 MODIFIER LETTER MIDDLE DOUBLE ACUTE ACCENT
U_MODIFIER_LETTER_LOW_TILDE = 0x02F7, // U+02F7 MODIFIER LETTER LOW TILDE
U_MODIFIER_LETTER_RAISED_COLON = 0x02F8, // U+02F8 MODIFIER LETTER RAISED COLON
U_MODIFIER_LETTER_BEGIN_HIGH_TONE = 0x02F9, // U+02F9 MODIFIER LETTER BEGIN HIGH TONE
U_MODIFIER_LETTER_END_HIGH_TONE = 0x02FA, // U+02FA MODIFIER LETTER END HIGH TONE
U_MODIFIER_LETTER_BEGIN_LOW_TONE = 0x02FB, // U+02FB MODIFIER LETTER BEGIN LOW TONE
U_MODIFIER_LETTER_END_LOW_TONE = 0x02FC, // U+02FC MODIFIER LETTER END LOW TONE
U_MODIFIER_LETTER_SHELF = 0x02FD, // U+02FD MODIFIER LETTER SHELF
U_MODIFIER_LETTER_OPEN_SHELF = 0x02FE, // U+02FE MODIFIER LETTER OPEN SHELF
U_MODIFIER_LETTER_LOW_LEFT_ARROW = 0x02FF, // U+02FF MODIFIER LETTER LOW LEFT ARROW
U_GREEK_LOWER_NUMERAL_SIGN = 0x0375, // U+0375 GREEK LOWER NUMERAL SIGN
U_GREEK_TONOS = 0x0384, // U+0384 GREEK TONOS
U_GREEK_DIALYTIKA_TONOS = 0x0385, // U+0385 GREEK DIALYTIKA TONOS
U_GREEK_KORONIS = 0x1FBD, // U+1FBD GREEK KORONIS
U_GREEK_PSILI = 0x1FBF, // U+1FBF GREEK PSILI
U_GREEK_PERISPOMENI = 0x1FC0, // U+1FC0 GREEK PERISPOMENI
U_GREEK_DIALYTIKA_AND_PERISPOMENI = 0x1FC1, // U+1FC1 GREEK DIALYTIKA AND PERISPOMENI
U_GREEK_PSILI_AND_VARIA = 0x1FCD, // U+1FCD GREEK PSILI AND VARIA
U_GREEK_PSILI_AND_OXIA = 0x1FCE, // U+1FCE GREEK PSILI AND OXIA
U_GREEK_PSILI_AND_PERISPOMENI = 0x1FCF, // U+1FCF GREEK PSILI AND PERISPOMENI
U_GREEK_DASIA_AND_VARIA = 0x1FDD, // U+1FDD GREEK DASIA AND VARIA
U_GREEK_DASIA_AND_OXIA = 0x1FDE, // U+1FDE GREEK DASIA AND OXIA
U_GREEK_DASIA_AND_PERISPOMENI = 0x1FDF, // U+1FDF GREEK DASIA AND PERISPOMENI
U_GREEK_DIALYTIKA_AND_VARIA = 0x1FED, // U+1FED GREEK DIALYTIKA AND VARIA
U_GREEK_DIALYTIKA_AND_OXIA = 0x1FEE, // U+1FEE GREEK DIALYTIKA AND OXIA
U_GREEK_VARIA = 0x1FEF, // U+1FEF GREEK VARIA
U_GREEK_OXIA = 0x1FFD, // U+1FFD GREEK OXIA
U_GREEK_DASIA = 0x1FFE, // U+1FFE GREEK DASIA
// http://www.fileformat.info/info/unicode/category/Sk/list.htm
U_CIRCUMFLEX = 0x005e, // U+005E CIRCUMFLEX
U_GRAVE_ACCENT = 0x0060, // U+0060 GRAVE ACCENT
U_DIAERESIS = 0x00a8, // U+00A8 DIAERESIS
U_MACRON = 0x00af, // U+00AF MACRON
U_ACUTE_ACCENT = 0x00b4, // U+00B4 ACUTE ACCENT
U_CEDILLA = 0x00b8, // U+00B8 CEDILLA
U_MODIFIER_LETTER_LEFT_ARROWHEAD = 0x02c2, // U+02C2 MODIFIER LETTER LEFT ARROWHEAD
U_MODIFIER_LETTER_RIGHT_ARROWHEAD = 0x02c3, // U+02C3 MODIFIER LETTER RIGHT ARROWHEAD
U_MODIFIER_LETTER_UP_ARROWHEAD = 0x02c4, // U+02C4 MODIFIER LETTER UP ARROWHEAD
U_MODIFIER_LETTER_DOWN_ARROWHEAD = 0x02c5, // U+02C5 MODIFIER LETTER DOWN ARROWHEAD
U_MODIFIER_LETTER_CENTRED_RIGHT_HALF_RING = 0x02d2, // U+02D2 MODIFIER LETTER CENTRED RIGHT HALF RING
U_MODIFIER_LETTER_CENTRED_LEFT_HALF_RING = 0x02d3, // U+02D3 MODIFIER LETTER CENTRED LEFT HALF RING
U_MODIFIER_LETTER_UP_TACK = 0x02d4, // U+02D4 MODIFIER LETTER UP TACK
U_MODIFIER_LETTER_DOWN_TACK = 0x02d5, // U+02D5 MODIFIER LETTER DOWN TACK
U_MODIFIER_LETTER_PLUS_SIGN = 0x02d6, // U+02D6 MODIFIER LETTER PLUS SIGN
U_MODIFIER_LETTER_MINUS_SIGN = 0x02d7, // U+02D7 MODIFIER LETTER MINUS SIGN
U_BREVE = 0x02d8, // U+02D8 BREVE
U_DOT_ABOVE = 0x02d9, // U+02D9 DOT ABOVE
U_RING_ABOVE = 0x02da, // U+02DA RING ABOVE
U_OGONEK = 0x02db, // U+02DB OGONEK
U_SMALL_TILDE = 0x02dc, // U+02DC SMALL TILDE
U_DOUBLE_ACUTE_ACCENT = 0x02dd, // U+02DD DOUBLE ACUTE ACCENT
U_MODIFIER_LETTER_RHOTIC_HOOK = 0x02de, // U+02DE MODIFIER LETTER RHOTIC HOOK
U_MODIFIER_LETTER_CROSS_ACCENT = 0x02df, // U+02DF MODIFIER LETTER CROSS ACCENT
U_MODIFIER_LETTER_EXTRA_HIGH_TONE_BAR = 0x02e5, // U+02E5 MODIFIER LETTER EXTRA-HIGH TONE BAR
U_MODIFIER_LETTER_HIGH_TONE_BAR = 0x02e6, // U+02E6 MODIFIER LETTER HIGH TONE BAR
U_MODIFIER_LETTER_MID_TONE_BAR = 0x02e7, // U+02E7 MODIFIER LETTER MID TONE BAR
U_MODIFIER_LETTER_LOW_TONE_BAR = 0x02e8, // U+02E8 MODIFIER LETTER LOW TONE BAR
U_MODIFIER_LETTER_EXTRA_LOW_TONE_BAR = 0x02e9, // U+02E9 MODIFIER LETTER EXTRA-LOW TONE BAR
U_MODIFIER_LETTER_YIN_DEPARTING_TONE_MARK = 0x02ea, // U+02EA MODIFIER LETTER YIN DEPARTING TONE MARK
U_MODIFIER_LETTER_YANG_DEPARTING_TONE_MARK = 0x02eb, // U+02EB MODIFIER LETTER YANG DEPARTING TONE MARK
U_MODIFIER_LETTER_UNASPIRATED = 0x02ed, // U+02ED MODIFIER LETTER UNASPIRATED
U_MODIFIER_LETTER_LOW_DOWN_ARROWHEAD = 0x02ef, // U+02EF MODIFIER LETTER LOW DOWN ARROWHEAD
U_MODIFIER_LETTER_LOW_UP_ARROWHEAD = 0x02f0, // U+02F0 MODIFIER LETTER LOW UP ARROWHEAD
U_MODIFIER_LETTER_LOW_LEFT_ARROWHEAD = 0x02f1, // U+02F1 MODIFIER LETTER LOW LEFT ARROWHEAD
U_MODIFIER_LETTER_LOW_RIGHT_ARROWHEAD = 0x02f2, // U+02F2 MODIFIER LETTER LOW RIGHT ARROWHEAD
U_MODIFIER_LETTER_LOW_RING = 0x02f3, // U+02F3 MODIFIER LETTER LOW RING
U_MODIFIER_LETTER_MIDDLE_GRAVE_ACCENT = 0x02f4, // U+02F4 MODIFIER LETTER MIDDLE GRAVE ACCENT
U_MODIFIER_LETTER_MIDDLE_DOUBLE_GRAVE_ACCENT = 0x02f5, // U+02F5 MODIFIER LETTER MIDDLE DOUBLE GRAVE ACCENT
U_MODIFIER_LETTER_MIDDLE_DOUBLE_ACUTE_ACCENT = 0x02f6, // U+02F6 MODIFIER LETTER MIDDLE DOUBLE ACUTE ACCENT
U_MODIFIER_LETTER_LOW_TILDE = 0x02f7, // U+02F7 MODIFIER LETTER LOW TILDE
U_MODIFIER_LETTER_RAISED_COLON = 0x02f8, // U+02F8 MODIFIER LETTER RAISED COLON
U_MODIFIER_LETTER_BEGIN_HIGH_TONE = 0x02f9, // U+02F9 MODIFIER LETTER BEGIN HIGH TONE
U_MODIFIER_LETTER_END_HIGH_TONE = 0x02fa, // U+02FA MODIFIER LETTER END HIGH TONE
U_MODIFIER_LETTER_BEGIN_LOW_TONE = 0x02fb, // U+02FB MODIFIER LETTER BEGIN LOW TONE
U_MODIFIER_LETTER_END_LOW_TONE = 0x02fc, // U+02FC MODIFIER LETTER END LOW TONE
U_MODIFIER_LETTER_SHELF = 0x02fd, // U+02FD MODIFIER LETTER SHELF
U_MODIFIER_LETTER_OPEN_SHELF = 0x02fe, // U+02FE MODIFIER LETTER OPEN SHELF
U_MODIFIER_LETTER_LOW_LEFT_ARROW = 0x02ff, // U+02FF MODIFIER LETTER LOW LEFT ARROW
U_GREEK_LOWER_NUMERAL_SIGN = 0x0375, // U+0375 GREEK LOWER NUMERAL SIGN
U_GREEK_TONOS = 0x0384, // U+0384 GREEK TONOS
U_GREEK_DIALYTIKA_TONOS = 0x0385, // U+0385 GREEK DIALYTIKA TONOS
U_GREEK_KORONIS = 0x1fbd, // U+1FBD GREEK KORONIS
U_GREEK_PSILI = 0x1fbf, // U+1FBF GREEK PSILI
U_GREEK_PERISPOMENI = 0x1fc0, // U+1FC0 GREEK PERISPOMENI
U_GREEK_DIALYTIKA_AND_PERISPOMENI = 0x1fc1, // U+1FC1 GREEK DIALYTIKA AND PERISPOMENI
U_GREEK_PSILI_AND_VARIA = 0x1fcd, // U+1FCD GREEK PSILI AND VARIA
U_GREEK_PSILI_AND_OXIA = 0x1fce, // U+1FCE GREEK PSILI AND OXIA
U_GREEK_PSILI_AND_PERISPOMENI = 0x1fcf, // U+1FCF GREEK PSILI AND PERISPOMENI
U_GREEK_DASIA_AND_VARIA = 0x1fdd, // U+1FDD GREEK DASIA AND VARIA
U_GREEK_DASIA_AND_OXIA = 0x1fde, // U+1FDE GREEK DASIA AND OXIA
U_GREEK_DASIA_AND_PERISPOMENI = 0x1fdf, // U+1FDF GREEK DASIA AND PERISPOMENI
U_GREEK_DIALYTIKA_AND_VARIA = 0x1fed, // U+1FED GREEK DIALYTIKA AND VARIA
U_GREEK_DIALYTIKA_AND_OXIA = 0x1fee, // U+1FEE GREEK DIALYTIKA AND OXIA
U_GREEK_VARIA = 0x1fef, // U+1FEF GREEK VARIA
U_GREEK_OXIA = 0x1ffd, // U+1FFD GREEK OXIA
U_GREEK_DASIA = 0x1ffe, // U+1FFE GREEK DASIA
U_IDEOGRAPHIC_FULL_STOP = 0x3002, // U+3002 IDEOGRAPHIC FULL STOP
U_LEFT_CORNER_BRACKET = 0x300C, // U+300C LEFT CORNER BRACKET
U_RIGHT_CORNER_BRACKET = 0x300D, // U+300D RIGHT CORNER BRACKET
U_LEFT_BLACK_LENTICULAR_BRACKET = 0x3010, // U+3010 LEFT BLACK LENTICULAR BRACKET
U_RIGHT_BLACK_LENTICULAR_BRACKET = 0x3011, // U+3011 RIGHT BLACK LENTICULAR BRACKET
U_IDEOGRAPHIC_FULL_STOP = 0x3002, // U+3002 IDEOGRAPHIC FULL STOP
U_LEFT_CORNER_BRACKET = 0x300c, // U+300C LEFT CORNER BRACKET
U_RIGHT_CORNER_BRACKET = 0x300d, // U+300D RIGHT CORNER BRACKET
U_LEFT_BLACK_LENTICULAR_BRACKET = 0x3010, // U+3010 LEFT BLACK LENTICULAR BRACKET
U_RIGHT_BLACK_LENTICULAR_BRACKET = 0x3011, // U+3011 RIGHT BLACK LENTICULAR BRACKET
U_OVERLINE = 0x203e, // Unicode Character 'OVERLINE'
U_OVERLINE = 0x203E, // Unicode Character 'OVERLINE'
/**
* UTF-8 BOM
* Unicode Character 'ZERO WIDTH NO-BREAK SPACE' (U+FEFF)
* http://www.fileformat.info/info/unicode/char/feff/index.htm
*/
UTF8_BOM = 65279,
/**
* UTF-8 BOM
* Unicode Character 'ZERO WIDTH NO-BREAK SPACE' (U+FEFF)
* http://www.fileformat.info/info/unicode/char/feff/index.htm
*/
UTF8_BOM = 65279,
U_FULLWIDTH_SEMICOLON = 0xFF1B, // U+FF1B FULLWIDTH SEMICOLON
U_FULLWIDTH_COMMA = 0xFF0C, // U+FF0C FULLWIDTH COMMA
}
U_FULLWIDTH_SEMICOLON = 0xff1b, // U+FF1B FULLWIDTH SEMICOLON
U_FULLWIDTH_COMMA = 0xff0c // U+FF0C FULLWIDTH COMMA
}

View File

@@ -1,276 +1,276 @@
export const charMap: { [character: string]: string } = {
// latin
"À": "A",
"Á": "A",
"Â": "A",
"Ã": "A",
"Ä": "A",
"Å": "A",
"Æ": "AE",
"Ç": "C",
"È": "E",
"É": "E",
"Ê": "E",
"Ë": "E",
"Ì": "I",
"Í": "I",
"Î": "I",
"Ï": "I",
"Ð": "D",
"Ñ": "N",
"Ò": "O",
"Ó": "O",
"Ô": "O",
"Õ": "O",
"Ö": "O",
"Ő": "O",
"Ø": "O",
"Ù": "U",
"Ú": "U",
"Û": "U",
"Ü": "U",
"Ű": "U",
"Ý": "Y",
"Þ": "TH",
"ß": "ss",
"à": "a",
"á": "a",
"â": "a",
"ã": "a",
"ä": "a",
"å": "a",
"æ": "ae",
"ç": "c",
"è": "e",
"é": "e",
"ê": "e",
"ë": "e",
"ì": "i",
"í": "i",
"î": "i",
"ï": "i",
"ð": "d",
"ñ": "n",
"ò": "o",
"ó": "o",
"ô": "o",
"õ": "o",
"ö": "o",
"ő": "o",
"ø": "o",
"ù": "u",
"ú": "u",
"û": "u",
"ü": "u",
"ű": "u",
"ý": "y",
"þ": "th",
"ÿ": "y",
"ẞ": "SS",
À: 'A',
Á: 'A',
Â: 'A',
Ã: 'A',
Ä: 'A',
Å: 'A',
Æ: 'AE',
Ç: 'C',
È: 'E',
É: 'E',
Ê: 'E',
Ë: 'E',
Ì: 'I',
Í: 'I',
Î: 'I',
Ï: 'I',
Ð: 'D',
Ñ: 'N',
Ò: 'O',
Ó: 'O',
Ô: 'O',
Õ: 'O',
Ö: 'O',
Ő: 'O',
Ø: 'O',
Ù: 'U',
Ú: 'U',
Û: 'U',
Ü: 'U',
Ű: 'U',
Ý: 'Y',
Þ: 'TH',
ß: 'ss',
à: 'a',
á: 'a',
â: 'a',
ã: 'a',
ä: 'a',
å: 'a',
æ: 'ae',
ç: 'c',
è: 'e',
é: 'e',
ê: 'e',
ë: 'e',
ì: 'i',
í: 'i',
î: 'i',
ï: 'i',
ð: 'd',
ñ: 'n',
ò: 'o',
ó: 'o',
ô: 'o',
õ: 'o',
ö: 'o',
ő: 'o',
ø: 'o',
ù: 'u',
ú: 'u',
û: 'u',
ü: 'u',
ű: 'u',
ý: 'y',
þ: 'th',
ÿ: 'y',
: 'SS',
// greek
"α": "a",
"β": "b",
"γ": "g",
"δ": "d",
"ε": "e",
"ζ": "z",
"η": "h",
"θ": "8",
"ι": "i",
"κ": "k",
"λ": "l",
"μ": "m",
"ν": "n",
"ξ": "3",
"ο": "o",
"π": "p",
"ρ": "r",
"σ": "s",
"τ": "t",
"υ": "y",
"φ": "f",
"χ": "x",
"ψ": "ps",
"ω": "w",
"ά": "a",
"έ": "e",
"ί": "i",
"ό": "o",
"ύ": "y",
"ή": "h",
"ώ": "w",
"ς": "s",
"ϊ": "i",
"ΰ": "y",
"ϋ": "y",
"ΐ": "i",
"Α": "A",
"Β": "B",
"Γ": "G",
"Δ": "D",
"Ε": "E",
"Ζ": "Z",
"Η": "H",
"Θ": "8",
"Ι": "I",
"Κ": "K",
"Λ": "L",
"Μ": "M",
"Ν": "N",
"Ξ": "3",
"Ο": "O",
"Π": "P",
"Ρ": "R",
"Σ": "S",
"Τ": "T",
"Υ": "Y",
"Φ": "F",
"Χ": "X",
"Ψ": "PS",
"Ω": "W",
"Ά": "A",
"Έ": "E",
"Ί": "I",
"Ό": "O",
"Ύ": "Y",
"Ή": "H",
"Ώ": "W",
"Ϊ": "I",
"Ϋ": "Y",
α: 'a',
β: 'b',
γ: 'g',
δ: 'd',
ε: 'e',
ζ: 'z',
η: 'h',
θ: '8',
ι: 'i',
κ: 'k',
λ: 'l',
μ: 'm',
ν: 'n',
ξ: '3',
ο: 'o',
π: 'p',
ρ: 'r',
σ: 's',
τ: 't',
υ: 'y',
φ: 'f',
χ: 'x',
ψ: 'ps',
ω: 'w',
ά: 'a',
έ: 'e',
ί: 'i',
ό: 'o',
ύ: 'y',
ή: 'h',
ώ: 'w',
ς: 's',
ϊ: 'i',
ΰ: 'y',
ϋ: 'y',
ΐ: 'i',
Α: 'A',
Β: 'B',
Γ: 'G',
Δ: 'D',
Ε: 'E',
Ζ: 'Z',
Η: 'H',
Θ: '8',
Ι: 'I',
Κ: 'K',
Λ: 'L',
Μ: 'M',
Ν: 'N',
Ξ: '3',
Ο: 'O',
Π: 'P',
Ρ: 'R',
Σ: 'S',
Τ: 'T',
Υ: 'Y',
Φ: 'F',
Χ: 'X',
Ψ: 'PS',
Ω: 'W',
Ά: 'A',
Έ: 'E',
Ί: 'I',
Ό: 'O',
Ύ: 'Y',
Ή: 'H',
Ώ: 'W',
Ϊ: 'I',
Ϋ: 'Y',
// turkish
"ş": "s",
"Ş": "S",
"ı": "i",
"İ": "I",
"ğ": "g",
"Ğ": "G",
ş: 's',
Ş: 'S',
ı: 'i',
İ: 'I',
ğ: 'g',
Ğ: 'G',
// russian
"а": "a",
"б": "b",
"в": "v",
"г": "g",
"д": "d",
"е": "e",
"ё": "yo",
"ж": "zh",
"з": "z",
"и": "i",
"й": "j",
"к": "k",
"л": "l",
"м": "m",
"н": "n",
"о": "o",
"п": "p",
"р": "r",
"с": "s",
"т": "t",
"у": "u",
"ф": "f",
"х": "h",
"ц": "c",
"ч": "ch",
"ш": "sh",
"щ": "sh",
"ъ": "u",
"ы": "y",
"ь": "",
"э": "e",
"ю": "yu",
"я": "ya",
"А": "A",
"Б": "B",
"В": "V",
"Г": "G",
"Д": "D",
"Е": "E",
"Ё": "Yo",
"Ж": "Zh",
"З": "Z",
"И": "I",
"Й": "J",
"К": "K",
"Л": "L",
"М": "M",
"Н": "N",
"О": "O",
"П": "P",
"Р": "R",
"С": "S",
"Т": "T",
"У": "U",
"Ф": "F",
"Х": "H",
"Ц": "C",
"Ч": "Ch",
"Ш": "Sh",
"Щ": "Sh",
"Ъ": "U",
"Ы": "Y",
"Ь": "",
"Э": "E",
"Ю": "Yu",
"Я": "Ya",
а: 'a',
б: 'b',
в: 'v',
г: 'g',
д: 'd',
е: 'e',
ё: 'yo',
ж: 'zh',
з: 'z',
и: 'i',
й: 'j',
к: 'k',
л: 'l',
м: 'm',
н: 'n',
о: 'o',
п: 'p',
р: 'r',
с: 's',
т: 't',
у: 'u',
ф: 'f',
х: 'h',
ц: 'c',
ч: 'ch',
ш: 'sh',
щ: 'sh',
ъ: 'u',
ы: 'y',
ь: '',
э: 'e',
ю: 'yu',
я: 'ya',
А: 'A',
Б: 'B',
В: 'V',
Г: 'G',
Д: 'D',
Е: 'E',
Ё: 'Yo',
Ж: 'Zh',
З: 'Z',
И: 'I',
Й: 'J',
К: 'K',
Л: 'L',
М: 'M',
Н: 'N',
О: 'O',
П: 'P',
Р: 'R',
С: 'S',
Т: 'T',
У: 'U',
Ф: 'F',
Х: 'H',
Ц: 'C',
Ч: 'Ch',
Ш: 'Sh',
Щ: 'Sh',
Ъ: 'U',
Ы: 'Y',
Ь: '',
Э: 'E',
Ю: 'Yu',
Я: 'Ya',
// ukranian
"Є": "Ye",
"І": "I",
"Ї": "Yi",
"Ґ": "G",
"є": "ye",
"і": "i",
"ї": "yi",
"ґ": "g",
Є: 'Ye',
І: 'I',
Ї: 'Yi',
Ґ: 'G',
є: 'ye',
і: 'i',
ї: 'yi',
ґ: 'g',
// czech
"č": "c",
"ď": "d",
"ě": "e",
"ň": "n",
"ř": "r",
"š": "s",
"ť": "t",
"ů": "u",
"ž": "z",
"Č": "C",
"Ď": "D",
"Ě": "E",
"Ň": "N",
"Ř": "R",
"Š": "S",
"Ť": "T",
"Ů": "U",
"Ž": "Z",
č: 'c',
ď: 'd',
ě: 'e',
ň: 'n',
ř: 'r',
š: 's',
ť: 't',
ů: 'u',
ž: 'z',
Č: 'C',
Ď: 'D',
Ě: 'E',
Ň: 'N',
Ř: 'R',
Š: 'S',
Ť: 'T',
Ů: 'U',
Ž: 'Z',
// polish
"ą": "a",
"ć": "c",
"ę": "e",
"ł": "l",
"ń": "n",
"ś": "s",
"ź": "z",
"ż": "z",
"Ą": "A",
"Ć": "C",
"Ę": "e",
"Ł": "L",
"Ń": "N",
"Ś": "S",
"Ź": "Z",
"Ż": "Z",
ą: 'a',
ć: 'c',
ę: 'e',
ł: 'l',
ń: 'n',
ś: 's',
ź: 'z',
ż: 'z',
Ą: 'A',
Ć: 'C',
Ę: 'e',
Ł: 'L',
Ń: 'N',
Ś: 'S',
Ź: 'Z',
Ż: 'Z',
// latvian
"ā": "a",
"ē": "e",
"ģ": "g",
"ī": "i",
"ķ": "k",
"ļ": "l",
"ņ": "n",
"ū": "u",
"Ā": "A",
"Ē": "E",
"Ģ": "G",
"Ī": "i",
"Ķ": "k",
"Ļ": "L",
"Ņ": "N",
"Ū": "u"
};
ā: 'a',
ē: 'e',
ģ: 'g',
ī: 'i',
ķ: 'k',
ļ: 'l',
ņ: 'n',
ū: 'u',
Ā: 'A',
Ē: 'E',
Ģ: 'G',
Ī: 'i',
Ķ: 'k',
Ļ: 'L',
Ņ: 'N',
Ū: 'u'
};

View File

@@ -1,16 +1,16 @@
export const CONTEXT = {
canOpenPreview: "frontMatter:CanOpenPreview",
canOpenDashboard: "frontMatter:CanOpenDashboard",
isEnabled: "frontMatter:enabled",
isDashboardOpen: "frontMatter:dashboard:open",
wysiwyg: "frontMatter:markdown:wysiwyg",
backer: "frontMatter:backers:supporter",
isValidFile: "frontMatter:file:isValid",
canOpenPreview: 'frontMatter:CanOpenPreview',
canOpenDashboard: 'frontMatter:CanOpenDashboard',
isEnabled: 'frontMatter:enabled',
isDashboardOpen: 'frontMatter:dashboard:open',
wysiwyg: 'frontMatter:markdown:wysiwyg',
backer: 'frontMatter:backers:supporter',
isValidFile: 'frontMatter:file:isValid',
hasViewModes: "frontMatter:has:modes",
hasViewModes: 'frontMatter:has:modes',
isSnippetsDashboardEnabled: "frontMatter:dashboard:snippets:enabled",
isDataDashboardEnabled: "frontMatter:dashboard:data:enabled",
isSnippetsDashboardEnabled: 'frontMatter:dashboard:snippets:enabled',
isDataDashboardEnabled: 'frontMatter:dashboard:data:enabled',
isGitEnabled: "frontMatter:git:enabled",
};
isGitEnabled: 'frontMatter:git:enabled'
};

View File

@@ -12,6 +12,7 @@ export * from './LocalStore';
export * from './Navigation';
export * from './NotificationType';
export * from './PreviewCommands';
export * from './StaticFolderPlaceholder';
export * from './TelemetryEvent';
export * from './charCode';
export * from './charMap';

View File

@@ -1,104 +1,109 @@
export const EXTENSION_NAME = "Front Matter";
export const EXTENSION_NAME = 'Front Matter';
export const CONFIG_KEY = "frontMatter";
export const CONFIG_KEY = 'frontMatter';
export const SETTING_GLOBAL_NOTIFICATIONS = "global.notifications";
export const SETTING_GLOBAL_NOTIFICATIONS_DISABLED = "global.disabledNotifications";
export const SETTING_GLOBAL_MODES = "global.modes";
export const SETTING_GLOBAL_ACTIVE_MODE = "global.activeMode";
export const SETTING_EXPERIMENTAL = 'experimental';
export const SETTING_TAXONOMY_TAGS = "taxonomy.tags";
export const SETTING_TAXONOMY_CATEGORIES = "taxonomy.categories";
export const SETTING_TAXONOMY_CUSTOM = "taxonomy.customTaxonomy";
export const SETTING_TAXONOMY_FIELD_GROUPS = "taxonomy.fieldGroups";
export const SETTING_EXTENDS = 'extends';
export const SETTING_DATE_FORMAT = "taxonomy.dateFormat";
export const SETTING_COMMA_SEPARATED_FIELDS = "taxonomy.commaSeparatedFields";
export const SETTING_TAXONOMY_CONTENT_TYPES = "taxonomy.contentTypes";
export const SETTING_GLOBAL_NOTIFICATIONS = 'global.notifications';
export const SETTING_GLOBAL_NOTIFICATIONS_DISABLED = 'global.disabledNotifications';
export const SETTING_GLOBAL_MODES = 'global.modes';
export const SETTING_GLOBAL_ACTIVE_MODE = 'global.activeMode';
export const SETTING_SLUG_PREFIX = "taxonomy.slugPrefix";
export const SETTING_SLUG_SUFFIX = "taxonomy.slugSuffix";
export const SETTING_SLUG_UPDATE_FILE_NAME = "taxonomy.alignFilename";
export const SETTING_TAXONOMY_TAGS = 'taxonomy.tags';
export const SETTING_TAXONOMY_CATEGORIES = 'taxonomy.categories';
export const SETTING_TAXONOMY_CUSTOM = 'taxonomy.customTaxonomy';
export const SETTING_TAXONOMY_FIELD_GROUPS = 'taxonomy.fieldGroups';
export const SETTING_INDENT_ARRAY = "taxonomy.indentArrays";
export const SETTING_REMOVE_QUOTES = "taxonomy.noPropertyValueQuotes";
export const SETTING_DATE_FORMAT = 'taxonomy.dateFormat';
export const SETTING_COMMA_SEPARATED_FIELDS = 'taxonomy.commaSeparatedFields';
export const SETTING_TAXONOMY_CONTENT_TYPES = 'taxonomy.contentTypes';
export const SETTING_FRONTMATTER_TYPE = "taxonomy.frontMatterType";
export const SETTING_SLUG_PREFIX = 'taxonomy.slugPrefix';
export const SETTING_SLUG_SUFFIX = 'taxonomy.slugSuffix';
export const SETTING_SLUG_UPDATE_FILE_NAME = 'taxonomy.alignFilename';
export const SETTING_SEO_TITLE_LENGTH = "taxonomy.seoTitleLength";
export const SETTING_SEO_SLUG_LENGTH = "taxonomy.seoSlugLength";
export const SETTING_SEO_DESCRIPTION_LENGTH = "taxonomy.seoDescriptionLength";
export const SETTING_SEO_CONTENT_MIN_LENGTH = "taxonomy.seoContentLengh";
export const SETTING_SEO_DESCRIPTION_FIELD = "taxonomy.seoDescriptionField";
export const SETTING_INDENT_ARRAY = 'taxonomy.indentArrays';
export const SETTING_REMOVE_QUOTES = 'taxonomy.noPropertyValueQuotes';
export const SETTING_TEMPLATES_FOLDER = "templates.folder";
export const SETTING_TEMPLATES_PREFIX = "templates.prefix";
export const SETTING_TEMPLATES_ENABLED = "templates.enabled";
export const SETTING_FRONTMATTER_TYPE = 'taxonomy.frontMatterType';
export const SETTING_TELEMETRY_DISABLE = "telemetry.disable";
export const SETTING_SEO_TITLE_FIELD = 'taxonomy.seoTitleField';
export const SETTING_SEO_TITLE_LENGTH = 'taxonomy.seoTitleLength';
export const SETTING_SEO_SLUG_LENGTH = 'taxonomy.seoSlugLength';
export const SETTING_SEO_DESCRIPTION_LENGTH = 'taxonomy.seoDescriptionLength';
export const SETTING_SEO_CONTENT_MIN_LENGTH = 'taxonomy.seoContentLengh';
export const SETTING_SEO_DESCRIPTION_FIELD = 'taxonomy.seoDescriptionField';
export const SETTING_PANEL_FREEFORM = "panel.freeform";
export const SETTING_TEMPLATES_FOLDER = 'templates.folder';
export const SETTING_TEMPLATES_PREFIX = 'templates.prefix';
export const SETTING_TEMPLATES_ENABLED = 'templates.enabled';
export const SETTING_PREVIEW_HOST = "preview.host";
export const SETTING_PREVIEW_PATHNAME = "preview.pathName";
export const SETTING_TELEMETRY_DISABLE = 'telemetry.disable';
export const SETTING_CUSTOM_SCRIPTS = "custom.scripts";
export const SETTING_PANEL_FREEFORM = 'panel.freeform';
export const SETTING_AUTO_UPDATE_DATE = "content.autoUpdateDate";
export const SETTING_CONTENT_PAGE_FOLDERS = "content.pageFolders";
export const SETTING_CONTENT_STATIC_FOLDER = "content.publicFolder";
export const SETTING_CONTENT_FRONTMATTER_HIGHLIGHT = "content.fmHighlight";
export const SETTING_CONTENT_DRAFT_FIELD = "content.draftField";
export const SETTING_CONTENT_SORTING = "content.sorting";
export const SETTING_CONTENT_WYSIWYG = "content.wysiwyg";
export const SETTING_CONTENT_PLACEHOLDERS = "content.placeholders";
export const SETTING_CONTENT_SNIPPETS = "content.snippets";
export const SETTING_PREVIEW_HOST = 'preview.host';
export const SETTING_PREVIEW_PATHNAME = 'preview.pathName';
export const SETTING_CONTENT_SORTING_DEFAULT = "content.defaultSorting";
export const SETTING_MEDIA_SORTING_DEFAULT = "content.defaultSorting";
export const SETTING_CUSTOM_SCRIPTS = 'custom.scripts';
export const SETTING_CONTENT_DEFAULT_FILETYPE = "content.defaultFileType";
export const SETTING_CONTENT_SUPPORTED_FILETYPES = "content.supportedFileTypes";
export const SETTING_AUTO_UPDATE_DATE = 'content.autoUpdateDate';
export const SETTING_CONTENT_PAGE_FOLDERS = 'content.pageFolders';
export const SETTING_CONTENT_STATIC_FOLDER = 'content.publicFolder';
export const SETTING_CONTENT_FRONTMATTER_HIGHLIGHT = 'content.fmHighlight';
export const SETTING_CONTENT_DRAFT_FIELD = 'content.draftField';
export const SETTING_CONTENT_SORTING = 'content.sorting';
export const SETTING_CONTENT_WYSIWYG = 'content.wysiwyg';
export const SETTING_CONTENT_PLACEHOLDERS = 'content.placeholders';
export const SETTING_CONTENT_SNIPPETS = 'content.snippets';
export const SETTING_CONTENT_HIDE_FRONTMATTER = "content.hideFm";
export const SETTING_CONTENT_HIDE_FRONTMATTER_MESSAGE = "content.hideFmMessage";
export const SETTING_CONTENT_SORTING_DEFAULT = 'content.defaultSorting';
export const SETTING_MEDIA_SORTING_DEFAULT = 'content.defaultSorting';
export const SETTING_MEDIA_SUPPORTED_MIMETYPES = "media.supportedMimeTypes";
export const SETTING_CONTENT_DEFAULT_FILETYPE = 'content.defaultFileType';
export const SETTING_CONTENT_SUPPORTED_FILETYPES = 'content.supportedFileTypes';
export const SETTING_DASHBOARD_OPENONSTART = "dashboard.openOnStart";
export const SETTING_DASHBOARD_CONTENT_TAGS = "dashboard.content.cardTags";
export const SETTING_DASHBOARD_CONTENT_PAGINATION = "dashboard.content.pagination";
export const SETTING_CONTENT_HIDE_FRONTMATTER = 'content.hideFm';
export const SETTING_CONTENT_HIDE_FRONTMATTER_MESSAGE = 'content.hideFmMessage';
export const SETTING_DATA_FILES = "data.files";
export const SETTING_DATA_FOLDERS = "data.folders";
export const SETTING_DATA_TYPES = "data.types";
export const SETTING_MEDIA_SUPPORTED_MIMETYPES = 'media.supportedMimeTypes';
export const SETTING_FILE_PRESERVE_CASING = "file.preserveCasing";
export const SETTING_DASHBOARD_OPENONSTART = 'dashboard.openOnStart';
export const SETTING_DASHBOARD_CONTENT_TAGS = 'dashboard.content.cardTags';
export const SETTING_DASHBOARD_CONTENT_PAGINATION = 'dashboard.content.pagination';
export const SETTING_FRAMEWORK_ID = "framework.id";
export const SETTING_FRAMEWORK_START = "framework.startCommand";
export const SETTING_DATA_FILES = 'data.files';
export const SETTING_DATA_FOLDERS = 'data.folders';
export const SETTING_DATA_TYPES = 'data.types';
export const SETTING_SITE_BASEURL = "site.baseURL";
export const SETTING_FILE_PRESERVE_CASING = 'file.preserveCasing';
export const SETTING_GIT_ENABLED = "git.enabled";
export const SETTING_GIT_COMMIT_MSG = "git.commitMessage";
export const SETTING_FRAMEWORK_ID = 'framework.id';
export const SETTING_FRAMEWORK_START = 'framework.startCommand';
export const SETTING_SITE_BASEURL = 'site.baseURL';
export const SETTING_GIT_ENABLED = 'git.enabled';
export const SETTING_GIT_COMMIT_MSG = 'git.commitMessage';
/**
* @deprecated
*/
export const SETTING_CONTENT_FOLDERS = "content.folders";
export const SETTING_CONTENT_FOLDERS = 'content.folders';
/**
* @deprecated
* Use the `isPublishDate` property on the content type datetime field instead
*/
export const SETTING_DATE_FIELD = "taxonomy.dateField";
export const SETTING_DATE_FIELD = 'taxonomy.dateField';
/**
* @deprecated
* Use the `isModifiedDate` property on the content type datetime field instead
*/
export const SETTING_MODIFIED_FIELD = "taxonomy.modifiedField";
export const SETTING_MODIFIED_FIELD = 'taxonomy.modifiedField';
/**
* @deprecated
* Use the `frontMatter.content.snippets` setting instead
*/
export const SETTING_DASHBOARD_MEDIA_SNIPPET = "dashboard.mediaSnippet";
export const SETTING_DASHBOARD_MEDIA_SNIPPET = 'dashboard.mediaSnippet';

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,14 @@
export enum DashboardCommand {
loading = "loading",
pages = "pages",
searchPages = "searchPages",
settings = "settings",
media = "media",
viewData = "viewData",
mediaUpdate = "mediaUpdate",
dataFileEntries = "dataFileEntries",
searchReady = "searchReady",
loading = 'loading',
pages = 'pages',
searchPages = 'searchPages',
settings = 'settings',
media = 'media',
viewData = 'viewData',
mediaUpdate = 'mediaUpdate',
dataFileEntries = 'dataFileEntries',
searchReady = 'searchReady',
// Taxonomy dashboard
setTaxonomyData = "setTaxonomyData",
}
setTaxonomyData = 'setTaxonomyData'
}

View File

@@ -31,6 +31,7 @@ export enum DashboardMessage {
updateMediaMetadata = 'updateMediaMetadata',
createMediaFolder = 'createMediaFolder',
insertFile = 'insertFile',
createHexoAssetFolder = 'createHexoAssetFolder',
// Data dashboard
getDataEntries = 'getDataEntries',
@@ -43,13 +44,13 @@ export enum DashboardMessage {
// Taxonomy dashboard
getTaxonomyData = 'getTaxonomyData',
editTaxonomy = "editTaxonomy",
mergeTaxonomy = "mergeTaxonomy",
deleteTaxonomy = "deleteTaxonomy",
addToTaxonomy = "addToTaxonomy",
createTaxonomy = "createTaxonomy",
importTaxonomy = "importTaxonomy",
moveTaxonomy = "moveTaxonomy",
editTaxonomy = 'editTaxonomy',
mergeTaxonomy = 'mergeTaxonomy',
deleteTaxonomy = 'deleteTaxonomy',
addToTaxonomy = 'addToTaxonomy',
createTaxonomy = 'createTaxonomy',
importTaxonomy = 'importTaxonomy',
moveTaxonomy = 'moveTaxonomy',
// Other
getTheme = 'getTheme',
@@ -57,4 +58,5 @@ export enum DashboardMessage {
setState = 'setState',
runCustomScript = 'runCustomScript',
sendTelemetry = 'sendTelemetry',
}
logError = 'logError'
}

View File

@@ -1,8 +1,8 @@
import * as React from 'react';
import { Spinner } from './Spinner';
import { Spinner } from './Common/Spinner';
import useMessages from '../hooks/useMessages';
import useDarkMode from '../../hooks/useDarkMode';
import { WelcomeScreen } from './WelcomeScreen';
import { WelcomeScreen } from './WelcomeView/WelcomeScreen';
import { useRecoilValue } from 'recoil';
import { DashboardViewSelector, ModeAtom } from '../state';
import { Contents } from './Contents/Contents';
@@ -16,12 +16,17 @@ import { Route, Routes, useNavigate } from 'react-router-dom';
import { routePaths } from '..';
import { useEffect, useMemo } from 'react';
import { UnknownView } from './UnknownView';
import { ErrorBoundary } from '@sentry/react';
import { ErrorView } from './ErrorView';
import { DashboardMessage } from '../DashboardMessage';
export interface IAppProps {
showWelcome: boolean;
}
export const App: React.FunctionComponent<IAppProps> = ({showWelcome}: React.PropsWithChildren<IAppProps>) => {
export const App: React.FunctionComponent<IAppProps> = ({
showWelcome
}: React.PropsWithChildren<IAppProps>) => {
const { loading, pages, settings } = useMessages();
const view = useRecoilValue(DashboardViewSelector);
const mode = useRecoilValue(ModeAtom);
@@ -31,19 +36,19 @@ export const App: React.FunctionComponent<IAppProps> = ({showWelcome}: React.Pro
const viewState: any = Messenger.getState() || {};
const isAllowed = (features: string[], flag: string) => {
if (!features ||( features.length > 0 && !features.includes(flag))) {
if (!features || (features.length > 0 && !features.includes(flag))) {
return false;
}
return true;
}
};
const allowDataView = useMemo(() => {
return isAllowed(mode?.features || [], FEATURE_FLAG.dashboard.data.view)
return isAllowed(mode?.features || [], FEATURE_FLAG.dashboard.data.view);
}, [mode?.features]);
const allowTaxonomyView = useMemo(() => {
return isAllowed(mode?.features || [], FEATURE_FLAG.dashboard.taxonomy.view)
return isAllowed(mode?.features || [], FEATURE_FLAG.dashboard.taxonomy.view);
}, [mode?.features]);
useEffect(() => {
@@ -68,23 +73,37 @@ export const App: React.FunctionComponent<IAppProps> = ({showWelcome}: React.Pro
}
return (
<main className={`h-full w-full`}>
<Routes>
<Route path={routePaths.welcome} element={<WelcomeScreen settings={settings} />} />
<Route path={routePaths.contents} element={<Contents pages={pages} loading={loading} />} />
<Route path={routePaths.media} element={<Media />} />
<Route path={routePaths.snippets} element={<Snippets />} />
{
allowDataView && <Route path={routePaths.data} element={<DataView />} />
}
<ErrorBoundary
fallback={<ErrorView />}
onError={(error: Error, componentStack: string, eventId: string) => {
Messenger.send(
DashboardMessage.logError,
`Event ID: ${eventId}
Message: ${error.message}
{
allowTaxonomyView && <Route path={routePaths.taxonomy} element={<TaxonomyView pages={pages} />} />
}
Stack: ${componentStack}`
);
}}
>
<main className={`h-full w-full`}>
<Routes>
<Route path={routePaths.welcome} element={<WelcomeScreen settings={settings} />} />
<Route
path={routePaths.contents}
element={<Contents pages={pages} loading={loading} />}
/>
<Route path={routePaths.media} element={<Media />} />
<Route path={routePaths.snippets} element={<Snippets />} />
<Route path={`*`} element={<UnknownView />} />
</Routes>
</main>
{allowDataView && <Route path={routePaths.data} element={<DataView />} />}
{allowTaxonomyView && (
<Route path={routePaths.taxonomy} element={<TaxonomyView pages={pages} />} />
)}
<Route path={`*`} element={<UnknownView />} />
</Routes>
</main>
</ErrorBoundary>
);
};
};

View File

@@ -1,21 +0,0 @@
import * as React from 'react';
export interface IButtonProps {
secondary?: boolean;
disabled?: boolean;
className?: string;
onClick: () => void;
}
export const Button: React.FunctionComponent<IButtonProps> = ({onClick, className, disabled, secondary, children}: React.PropsWithChildren<IButtonProps>) => {
return (
<button
type="button"
className={`${className || ""} inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium text-white dark:text-vulcan-500 focus:outline-none disabled:bg-gray-500 ${secondary ? `bg-red-300 hover:bg-red-400` : `bg-teal-600 hover:bg-teal-700`}`}
onClick={onClick}
disabled={disabled}
>
{children}
</button>
);
};

View File

@@ -1,66 +0,0 @@
import { Menu } from '@headlessui/react';
import {ChevronDownIcon} from '@heroicons/react/outline';
import * as React from 'react';
import { MenuItem, MenuItems } from './Menu';
export interface IChoiceButtonProps {
title: string;
choices: {
icon?: JSX.Element;
title: string;
disabled?: boolean;
onClick: () => void;
}[];
disabled?: boolean;
onClick: () => void;
}
export const ChoiceButton: React.FunctionComponent<IChoiceButtonProps> = ({onClick, disabled, choices, title}: React.PropsWithChildren<IChoiceButtonProps>) => {
return (
<span className="relative z-50 inline-flex shadow-sm rounded-md">
<button
type="button"
className="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium text-white dark:text-vulcan-500 bg-teal-600 hover:bg-teal-700 focus:outline-none disabled:bg-gray-500"
onClick={onClick}
disabled={disabled}
>
{title}
</button>
{
choices.length > 0 && (
<Menu as="span" className="-ml-px relative block">
<Menu.Button
className="h-full inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium text-white dark:text-vulcan-500 bg-teal-700 hover:bg-teal-800 focus:outline-none disabled:bg-gray-500"
disabled={disabled}>
<span className="sr-only">Open options</span>
<ChevronDownIcon className="h-5 w-5" aria-hidden="true" />
</Menu.Button>
<MenuItems widthClass={`w-56`} disablePopper>
<div className="py-1">
{choices.map((choice, idx) => (
<MenuItem
key={idx}
title={(
choice.icon ? (
<div className="flex items-center">
{choice.icon}
<span>{choice.title}</span>
</div>
) : (
choice.title
)
)}
value={null}
onClick={choice.onClick}
disabled={choice.disabled} />
))}
</div>
</MenuItems>
</Menu>
)
}
</span>
);
};

View File

@@ -0,0 +1,39 @@
import * as React from 'react';
import useThemeColors from '../../hooks/useThemeColors';
export interface IButtonProps {
secondary?: boolean;
disabled?: boolean;
className?: string;
onClick: () => void;
}
export const Button: React.FunctionComponent<IButtonProps> = ({
onClick,
className,
disabled,
secondary,
children
}: React.PropsWithChildren<IButtonProps>) => {
const { getColors } = useThemeColors();
return (
<button
type="button"
className={`${className || ''
} inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium focus:outline-none rounded ${getColors(
'text-white dark:text-vulcan-500 disabled:bg-gray-500',
'disabled:opacity-50'
)
} ${secondary ?
getColors(`bg-red-300 hover:bg-red-400`, `bg-[var(--vscode-button-secondaryBackground)] text-[--vscode-button-secondaryForeground] hover:bg-[var(--vscode-button-secondaryHoverBackground)]`) :
getColors(`bg-teal-600 hover:bg-teal-700`, `bg-[var(--frontmatter-button-background)] text-[var(--vscode-button-foreground)] hover:bg-[var(--frontmatter-button-hoverBackground)]`)
}
`}
onClick={onClick}
disabled={disabled}
>
{children}
</button>
);
};

View File

@@ -0,0 +1,83 @@
import { Menu } from '@headlessui/react';
import { ChevronDownIcon } from '@heroicons/react/outline';
import * as React from 'react';
import useThemeColors from '../../hooks/useThemeColors';
import { MenuItem, MenuItems } from '../Menu';
export interface IChoiceButtonProps {
title: string;
choices: {
icon?: JSX.Element;
title: string;
disabled?: boolean;
onClick: () => void;
}[];
disabled?: boolean;
onClick: () => void;
}
export const ChoiceButton: React.FunctionComponent<IChoiceButtonProps> = ({
onClick,
disabled,
choices,
title
}: React.PropsWithChildren<IChoiceButtonProps>) => {
const { getColors } = useThemeColors();
return (
<span className="relative z-50 inline-flex shadow-sm rounded-md">
<button
type="button"
className={`inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium ${choices.length > 0 ? `rounded-l` : `rounded`
} ${getColors(
`text-white dark:text-vulcan-500 bg-teal-600 hover:bg-teal-700 disabled:bg-gray-500`,
`text-[var(--vscode-button-foreground)] bg-[var(--frontmatter-button-background)] hover:bg-[var(--vscode-button-hoverBackground)] disabled:opacity-50`
)
}`}
onClick={onClick}
disabled={disabled}
>
{title}
</button>
{choices.length > 0 && (
<Menu as="span" className="-ml-px relative block">
<Menu.Button
className={`h-full inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium focus:outline-none rounded-r ${getColors(
`text-white dark:text-vulcan-500 bg-teal-700 hover:bg-teal-800 disabled:bg-gray-500`,
`text-[var(--vscode-button-foreground)] bg-[var(--frontmatter-button-background)] hover:bg-[var(--vscode-button-hoverBackground)] disabled:opacity-50`
)
}`}
disabled={disabled}
>
<span className="sr-only">Open options</span>
<ChevronDownIcon className="h-5 w-5" aria-hidden="true" />
</Menu.Button>
<MenuItems widthClass={`w-56`} disablePopper>
<div className="py-1">
{choices.map((choice, idx) => (
<MenuItem
key={idx}
title={
choice.icon ? (
<div className="flex items-center">
{choice.icon}
<span>{choice.title}</span>
</div>
) : (
choice.title
)
}
value={null}
onClick={choice.onClick}
disabled={choice.disabled}
/>
))}
</div>
</MenuItems>
</Menu>
)}
</span>
);
};

Some files were not shown because too many files have changed in this diff Show More