This commit is contained in:
Elio
2022-09-05 14:57:27 +02:00
62 changed files with 1655 additions and 560 deletions
+14
View File
@@ -0,0 +1,14 @@
blank_issues_enabled: false
contact_links:
- name: Documentation
url: https://frontmatter.codes/docs
about: See our documentation.
- name: Changelog
url: https://frontmatter.codes/updates
about: See our changelog.
- name: Front Matter website
url: https://frontmatter.codes
about: Our website.
- name: Support Front Matter
url: https://github.com/sponsors/estruyf
about: Support Front Matter development.
+15
View File
@@ -10,4 +10,19 @@
"typescript.tsc.autoDetect": "off",
"eliostruyf.writingstyleguide.terms.isDisabled": true,
"eliostruyf.writingstyleguide.biasFree.isDisabled": true,
"squarl.groups": [
{
"id": "dashboard",
"name": "Dashboard"
}
],
"squarl.bookmarks": [
{
"name": "App.tsx",
"path": "src/dashboardWebView/components/App.tsx",
"description": "Start of dashboard",
"type": "file",
"groupId": "dashboard"
}
],
}
+29
View File
@@ -1,5 +1,34 @@
# Change Log
## [8.1.0] - 2022-xx-xx
### ✨ New features
- [#376](https://github.com/estruyf/vscode-front-matter/issues/376): Ability to run scripts after content was created
- [#377](https://github.com/estruyf/vscode-front-matter/issues/377): Git sync actions added on panel and content dashboard (pull and push your changes to remote)
- [#379](https://github.com/estruyf/vscode-front-matter/issues/377): New `frontMatter.config.reload` command to reload the configuration file + reinitialize its listeners
### 🎨 Enhancements
- [#352](https://github.com/estruyf/vscode-front-matter/issues/352): Custom placeholders now support scripting
- [#370](https://github.com/estruyf/vscode-front-matter/issues/370): Define the tags and categories as reserved keywords for custom taxonomy
- [#372](https://github.com/estruyf/vscode-front-matter/issues/372): Rename Taxonomy tab to Taxonomies
- [#374](https://github.com/estruyf/vscode-front-matter/issues/374): Hide the front matter section to use the panel instead
- [#383](https://github.com/estruyf/vscode-front-matter/issues/383): Add the item menu to the content list view
- [#385](https://github.com/estruyf/vscode-front-matter/issues/385): If no default value for the draft field is defined, the field value will be set to `true`
- [#388](https://github.com/estruyf/vscode-front-matter/issues/388): New stop server action has been added to the panel
- [#390](https://github.com/estruyf/vscode-front-matter/issues/390): Implement another JSON parser in order to be able to parse the `frontmatter.json` file better
- [#394](https://github.com/estruyf/vscode-front-matter/issues/394): Ordering of snippet fields is based on their field definition
### ⚡️ Optimizations
- Internal post message optimizations to the webviews
### 🐞 Fixes
- [#378](https://github.com/estruyf/vscode-front-matter/issues/378): Fix last modified update only to content in content folders
- [#384](https://github.com/estruyf/vscode-front-matter/issues/384): Fix issue `title` field in sub-fields
## [8.0.1] - 2022-07-13
### 🐞 Fixes
+2 -1
View File
@@ -41,7 +41,8 @@
}
.collapsible__body,
.ext_settings {
.ext_settings,
.git_actions {
padding: 1rem 1.25rem;
box-sizing: border-box;
}
+406 -222
View File
@@ -19,6 +19,7 @@
"@heroicons/react": "1.0.4",
"@iarna/toml": "2.2.3",
"@octokit/rest": "^18.12.0",
"@popperjs/core": "^2.11.6",
"@sentry/react": "^6.13.3",
"@sentry/tracing": "^6.13.3",
"@tailwindcss/forms": "^0.3.3",
@@ -56,6 +57,7 @@
"html-webpack-plugin": "4.5.0",
"image-size": "^1.0.0",
"invariant": "^2.2.4",
"jsonc-parser": "^3.2.0",
"lodash-es": "^4.17.21",
"lodash.omit": "^4.5.0",
"lodash.uniqby": "4.7.0",
@@ -81,6 +83,7 @@
"recoil": "^0.4.1",
"rimraf": "^3.0.2",
"semver": "^7.3.7",
"simple-git": "^3.10.0",
"style-loader": "2.0.0",
"tailwindcss": "^2.2.7",
"tailwindcss-nested-groups": "^1.2.4",
@@ -106,12 +109,13 @@
}
},
"node_modules/@actions/core": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.8.2.tgz",
"integrity": "sha512-FXcBL7nyik8K5ODeCKlxi+vts7torOkoDAKfeh61EAkAy1HAvwn9uVzZBY0f15YcQTcZZ2/iSGBFHEuioZWfDA==",
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz",
"integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==",
"dev": true,
"dependencies": {
"@actions/http-client": "^2.0.1"
"@actions/http-client": "^2.0.1",
"uuid": "^8.3.2"
}
},
"node_modules/@actions/http-client": {
@@ -124,38 +128,50 @@
}
},
"node_modules/@babel/code-frame": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
"integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
"integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
"dev": true,
"dependencies": {
"@babel/highlight": "^7.10.4"
"@babel/highlight": "^7.18.6"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
"integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
"dev": true
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz",
"integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/highlight": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
"integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
"integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
"dev": true,
"dependencies": {
"@babel/helper-validator-identifier": "^7.10.4",
"@babel/helper-validator-identifier": "^7.18.6",
"chalk": "^2.0.0",
"js-tokens": "^4.0.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/runtime": {
"version": "7.12.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz",
"integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==",
"version": "7.18.9",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz",
"integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==",
"dev": true,
"dependencies": {
"regenerator-runtime": "^0.13.4"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@bendera/vscode-webview-elements": {
@@ -217,6 +233,79 @@
"integrity": "sha512-FmuxfCuolpLl0AnQ2NHSzoUKWEJDFl63qXjzdoWBVyFCXzMGm1spBzk7LeHNoVCiWCF7mRVms9e6jEV9+MoPbg==",
"dev": true
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
"dev": true,
"dependencies": {
"@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
"@jridgewell/trace-mapping": "^0.3.9"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
"dev": true,
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/set-array": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
"dev": true,
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/source-map": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
"dev": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.0",
"@jridgewell/trace-mapping": "^0.3.9"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.14",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
"dev": true
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.15",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz",
"integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==",
"dev": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@kwsites/file-exists": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
"integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==",
"dev": true,
"dependencies": {
"debug": "^4.1.1"
}
},
"node_modules/@kwsites/promise-deferred": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz",
"integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==",
"dev": true
},
"node_modules/@microsoft/fast-element": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@microsoft/fast-element/-/fast-element-1.7.0.tgz",
@@ -449,9 +538,9 @@
"dev": true
},
"node_modules/@popperjs/core": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.10.1.tgz",
"integrity": "sha512-HnUhk1Sy9IuKrxEMdIRCxpIqPw6BFsbYSEUO9p/hNw5sMld/+3OLMWQP80F8/db9qsv3qUjs7ZR5bS/R+iinXw==",
"version": "2.11.6",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==",
"dev": true,
"funding": {
"type": "opencollective",
@@ -1536,9 +1625,9 @@
}
},
"node_modules/async": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
"version": "2.6.4",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
"dev": true,
"dependencies": {
"lodash": "^4.17.14"
@@ -1760,26 +1849,31 @@
"dev": true
},
"node_modules/browserslist": {
"version": "4.16.8",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.8.tgz",
"integrity": "sha512-sc2m9ohR/49sWEbPj14ZSSZqp+kbi16aLao42Hmn3Z8FpjuMaq2xCA2l4zl9ITfyzvnvyE0hcg62YkIGKxgaNQ==",
"version": "4.21.3",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz",
"integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/browserslist"
}
],
"dependencies": {
"caniuse-lite": "^1.0.30001251",
"colorette": "^1.3.0",
"electron-to-chromium": "^1.3.811",
"escalade": "^3.1.1",
"node-releases": "^1.1.75"
"caniuse-lite": "^1.0.30001370",
"electron-to-chromium": "^1.4.202",
"node-releases": "^2.0.6",
"update-browserslist-db": "^1.0.5"
},
"bin": {
"browserslist": "cli.js"
},
"engines": {
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
}
},
"node_modules/buffer": {
@@ -1982,14 +2076,20 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001251",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001251.tgz",
"integrity": "sha512-HOe1r+9VkU4TFmnU70z+r7OLmtR+/chB1rdcJUeQlAinjEeb0cKL20tlAtOagNZhbrtLnCvV19B4FmF1rgzl6A==",
"version": "1.0.30001375",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001375.tgz",
"integrity": "sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==",
"dev": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
}
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
}
]
},
"node_modules/chai": {
"version": "4.3.6",
@@ -3252,9 +3352,9 @@
"dev": true
},
"node_modules/electron-to-chromium": {
"version": "1.3.814",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.814.tgz",
"integrity": "sha512-0mH03cyjh6OzMlmjauGg0TLd87ErIJqWiYxMcOLKf5w6p0YEOl7DJAj7BDlXEFmCguY5CQaKVOiMjAMODO2XDw==",
"version": "1.4.215",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.215.tgz",
"integrity": "sha512-vqZxT8C5mlDZ//hQFhneHmOLnj1LhbzxV0+I1yqHV8SB1Oo4Y5Ne9+qQhwHl7O1s9s9cRuo2l5CoLEHdhMTwZg==",
"dev": true
},
"node_modules/emoji-regex": {
@@ -3718,31 +3818,6 @@
"node": ">=8"
}
},
"node_modules/fast-glob/node_modules/micromatch": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
"integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==",
"dev": true,
"dependencies": {
"braces": "^3.0.1",
"picomatch": "^2.2.3"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/fast-glob/node_modules/picomatch": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
"integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
"dev": true,
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -4149,9 +4224,9 @@
}
},
"node_modules/graceful-fs": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
"integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==",
"version": "4.2.10",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
"dev": true
},
"node_modules/gray-matter": {
@@ -5223,9 +5298,9 @@
}
},
"node_modules/jest-worker": {
"version": "27.4.5",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.5.tgz",
"integrity": "sha512-f2s8kEdy15cv9r7q4KkzGXvlY0JTcmCbMHZBfSQDwW77REr45IDWwd0lksDFeVHH2jJ5pqb90T77XscrjeGzzg==",
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
"dev": true,
"dependencies": {
"@types/node": "*",
@@ -5315,6 +5390,12 @@
"json5": "lib/cli.js"
}
},
"node_modules/jsonc-parser": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
"integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",
"dev": true
},
"node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
@@ -6212,16 +6293,16 @@
]
},
"node_modules/micromatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
"integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
"dev": true,
"dependencies": {
"braces": "^3.0.1",
"picomatch": "^2.0.5"
"braces": "^3.0.2",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=8"
"node": ">=8.6"
}
},
"node_modules/mime": {
@@ -6312,9 +6393,9 @@
}
},
"node_modules/minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
"dev": true
},
"node_modules/mkdirp": {
@@ -6662,9 +6743,9 @@
}
},
"node_modules/node-forge": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz",
"integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
"dev": true,
"engines": {
"node": ">= 6.13.0"
@@ -6692,9 +6773,9 @@
}
},
"node_modules/node-releases": {
"version": "1.1.75",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.75.tgz",
"integrity": "sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==",
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz",
"integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==",
"dev": true
},
"node_modules/normalize-package-data": {
@@ -7300,10 +7381,16 @@
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
"dev": true
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"dev": true
},
"node_modules/picomatch": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"engines": {
"node": ">=8.6"
@@ -7998,9 +8085,9 @@
}
},
"node_modules/react-quill": {
"version": "2.0.0-beta.4",
"resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0-beta.4.tgz",
"integrity": "sha512-KyAHvAlPjP4xLElKZJefMth91Z6FbbXRvq9OSu6xN3KBaoasLP9p+3dcxg4Ywr4tBlpMGXcPszYSAgd5CpJ45Q==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0.tgz",
"integrity": "sha512-4qQtv1FtCfLgoD3PXAur5RyxuUbPXQGOHgTlFie3jtxp43mXDtzCKaOgQ3mLyZfi1PUlyjycfivKelFhy13QUg==",
"dev": true,
"dependencies": {
"@types/quill": "^1.3.10",
@@ -8008,8 +8095,8 @@
"quill": "^1.3.7"
},
"peerDependencies": {
"react": "^16 || ^17",
"react-dom": "^16 || ^17"
"react": "^16 || ^17 || ^18",
"react-dom": "^16 || ^17 || ^18"
}
},
"node_modules/react-router": {
@@ -8800,6 +8887,21 @@
"simple-concat": "^1.0.0"
}
},
"node_modules/simple-git": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.10.0.tgz",
"integrity": "sha512-2w35xrS5rVtAW0g67LqtxCZN5cdddz/woQRfS0OJXaljXEoTychZ4jnE+CQgra/wX4ZvHeiChTUMenCwfIYEYw==",
"dev": true,
"dependencies": {
"@kwsites/file-exists": "^1.1.1",
"@kwsites/promise-deferred": "^1.1.1",
"debug": "^4.3.4"
},
"funding": {
"type": "github",
"url": "https://github.com/steveukx/git-js?sponsor=1"
}
},
"node_modules/simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
@@ -9395,9 +9497,9 @@
}
},
"node_modules/terser": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
"integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz",
"integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==",
"dev": true,
"dependencies": {
"commander": "^2.20.0",
@@ -9446,13 +9548,14 @@
}
},
"node_modules/terser-webpack-plugin/node_modules/terser": {
"version": "5.10.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz",
"integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==",
"version": "5.15.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz",
"integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==",
"dev": true,
"dependencies": {
"@jridgewell/source-map": "^0.3.2",
"acorn": "^8.5.0",
"commander": "^2.20.0",
"source-map": "~0.7.2",
"source-map-support": "~0.5.20"
},
"bin": {
@@ -9460,23 +9563,6 @@
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"acorn": "^8.5.0"
},
"peerDependenciesMeta": {
"acorn": {
"optional": true
}
}
},
"node_modules/terser-webpack-plugin/node_modules/terser/node_modules/source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
"dev": true,
"engines": {
"node": ">= 8"
}
},
"node_modules/thunky": {
@@ -9911,6 +9997,32 @@
"mkdirp": "^0.5.1"
}
},
"node_modules/update-browserslist-db": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz",
"integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/browserslist"
}
],
"dependencies": {
"escalade": "^3.1.1",
"picocolors": "^1.0.0"
},
"bin": {
"browserslist-lint": "cli.js"
},
"peerDependencies": {
"browserslist": ">= 4.21.0"
}
},
"node_modules/uri-js": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
@@ -10927,12 +11039,13 @@
},
"dependencies": {
"@actions/core": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.8.2.tgz",
"integrity": "sha512-FXcBL7nyik8K5ODeCKlxi+vts7torOkoDAKfeh61EAkAy1HAvwn9uVzZBY0f15YcQTcZZ2/iSGBFHEuioZWfDA==",
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz",
"integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==",
"dev": true,
"requires": {
"@actions/http-client": "^2.0.1"
"@actions/http-client": "^2.0.1",
"uuid": "^8.3.2"
}
},
"@actions/http-client": {
@@ -10945,35 +11058,35 @@
}
},
"@babel/code-frame": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
"integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
"integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
"dev": true,
"requires": {
"@babel/highlight": "^7.10.4"
"@babel/highlight": "^7.18.6"
}
},
"@babel/helper-validator-identifier": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
"integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz",
"integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==",
"dev": true
},
"@babel/highlight": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
"integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
"integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
"dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.10.4",
"@babel/helper-validator-identifier": "^7.18.6",
"chalk": "^2.0.0",
"js-tokens": "^4.0.0"
}
},
"@babel/runtime": {
"version": "7.12.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz",
"integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==",
"version": "7.18.9",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz",
"integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.13.4"
@@ -11023,6 +11136,70 @@
"integrity": "sha512-FmuxfCuolpLl0AnQ2NHSzoUKWEJDFl63qXjzdoWBVyFCXzMGm1spBzk7LeHNoVCiWCF7mRVms9e6jEV9+MoPbg==",
"dev": true
},
"@jridgewell/gen-mapping": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
"dev": true,
"requires": {
"@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
"@jridgewell/trace-mapping": "^0.3.9"
}
},
"@jridgewell/resolve-uri": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
"dev": true
},
"@jridgewell/set-array": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
"dev": true
},
"@jridgewell/source-map": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
"dev": true,
"requires": {
"@jridgewell/gen-mapping": "^0.3.0",
"@jridgewell/trace-mapping": "^0.3.9"
}
},
"@jridgewell/sourcemap-codec": {
"version": "1.4.14",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
"dev": true
},
"@jridgewell/trace-mapping": {
"version": "0.3.15",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz",
"integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==",
"dev": true,
"requires": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"@kwsites/file-exists": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
"integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==",
"dev": true,
"requires": {
"debug": "^4.1.1"
}
},
"@kwsites/promise-deferred": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz",
"integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==",
"dev": true
},
"@microsoft/fast-element": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@microsoft/fast-element/-/fast-element-1.7.0.tgz",
@@ -11233,9 +11410,9 @@
"dev": true
},
"@popperjs/core": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.10.1.tgz",
"integrity": "sha512-HnUhk1Sy9IuKrxEMdIRCxpIqPw6BFsbYSEUO9p/hNw5sMld/+3OLMWQP80F8/db9qsv3qUjs7ZR5bS/R+iinXw==",
"version": "2.11.6",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==",
"dev": true
},
"@sentry/browser": {
@@ -12180,9 +12357,9 @@
"dev": true
},
"async": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
"version": "2.6.4",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
"dev": true,
"requires": {
"lodash": "^4.17.14"
@@ -12361,16 +12538,15 @@
"dev": true
},
"browserslist": {
"version": "4.16.8",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.8.tgz",
"integrity": "sha512-sc2m9ohR/49sWEbPj14ZSSZqp+kbi16aLao42Hmn3Z8FpjuMaq2xCA2l4zl9ITfyzvnvyE0hcg62YkIGKxgaNQ==",
"version": "4.21.3",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz",
"integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==",
"dev": true,
"requires": {
"caniuse-lite": "^1.0.30001251",
"colorette": "^1.3.0",
"electron-to-chromium": "^1.3.811",
"escalade": "^3.1.1",
"node-releases": "^1.1.75"
"caniuse-lite": "^1.0.30001370",
"electron-to-chromium": "^1.4.202",
"node-releases": "^2.0.6",
"update-browserslist-db": "^1.0.5"
}
},
"buffer": {
@@ -12522,9 +12698,9 @@
"dev": true
},
"caniuse-lite": {
"version": "1.0.30001251",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001251.tgz",
"integrity": "sha512-HOe1r+9VkU4TFmnU70z+r7OLmtR+/chB1rdcJUeQlAinjEeb0cKL20tlAtOagNZhbrtLnCvV19B4FmF1rgzl6A==",
"version": "1.0.30001375",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001375.tgz",
"integrity": "sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==",
"dev": true
},
"chai": {
@@ -13496,9 +13672,9 @@
"dev": true
},
"electron-to-chromium": {
"version": "1.3.814",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.814.tgz",
"integrity": "sha512-0mH03cyjh6OzMlmjauGg0TLd87ErIJqWiYxMcOLKf5w6p0YEOl7DJAj7BDlXEFmCguY5CQaKVOiMjAMODO2XDw==",
"version": "1.4.215",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.215.tgz",
"integrity": "sha512-vqZxT8C5mlDZ//hQFhneHmOLnj1LhbzxV0+I1yqHV8SB1Oo4Y5Ne9+qQhwHl7O1s9s9cRuo2l5CoLEHdhMTwZg==",
"dev": true
},
"emoji-regex": {
@@ -13868,24 +14044,6 @@
"glob-parent": "^5.1.2",
"merge2": "^1.3.0",
"micromatch": "^4.0.4"
},
"dependencies": {
"micromatch": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
"integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==",
"dev": true,
"requires": {
"braces": "^3.0.1",
"picomatch": "^2.2.3"
}
},
"picomatch": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
"integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
"dev": true
}
}
},
"fast-json-stable-stringify": {
@@ -14192,9 +14350,9 @@
}
},
"graceful-fs": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
"integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==",
"version": "4.2.10",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
"dev": true
},
"gray-matter": {
@@ -14963,9 +15121,9 @@
"dev": true
},
"jest-worker": {
"version": "27.4.5",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.5.tgz",
"integrity": "sha512-f2s8kEdy15cv9r7q4KkzGXvlY0JTcmCbMHZBfSQDwW77REr45IDWwd0lksDFeVHH2jJ5pqb90T77XscrjeGzzg==",
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
"dev": true,
"requires": {
"@types/node": "*",
@@ -15039,6 +15197,12 @@
"minimist": "^1.2.0"
}
},
"jsonc-parser": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
"integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",
"dev": true
},
"jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
@@ -15647,13 +15811,13 @@
"dev": true
},
"micromatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
"integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
"dev": true,
"requires": {
"braces": "^3.0.1",
"picomatch": "^2.0.5"
"braces": "^3.0.2",
"picomatch": "^2.3.1"
}
},
"mime": {
@@ -15719,9 +15883,9 @@
}
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
"dev": true
},
"mkdirp": {
@@ -15990,9 +16154,9 @@
}
},
"node-forge": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz",
"integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
"dev": true
},
"node-json-db": {
@@ -16013,9 +16177,9 @@
}
},
"node-releases": {
"version": "1.1.75",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.75.tgz",
"integrity": "sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==",
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz",
"integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==",
"dev": true
},
"normalize-package-data": {
@@ -16465,10 +16629,16 @@
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
"dev": true
},
"picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"dev": true
},
"picomatch": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true
},
"pidtree": {
@@ -16982,9 +17152,9 @@
}
},
"react-quill": {
"version": "2.0.0-beta.4",
"resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0-beta.4.tgz",
"integrity": "sha512-KyAHvAlPjP4xLElKZJefMth91Z6FbbXRvq9OSu6xN3KBaoasLP9p+3dcxg4Ywr4tBlpMGXcPszYSAgd5CpJ45Q==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0.tgz",
"integrity": "sha512-4qQtv1FtCfLgoD3PXAur5RyxuUbPXQGOHgTlFie3jtxp43mXDtzCKaOgQ3mLyZfi1PUlyjycfivKelFhy13QUg==",
"dev": true,
"requires": {
"@types/quill": "^1.3.10",
@@ -17608,6 +17778,17 @@
"simple-concat": "^1.0.0"
}
},
"simple-git": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.10.0.tgz",
"integrity": "sha512-2w35xrS5rVtAW0g67LqtxCZN5cdddz/woQRfS0OJXaljXEoTychZ4jnE+CQgra/wX4ZvHeiChTUMenCwfIYEYw==",
"dev": true,
"requires": {
"@kwsites/file-exists": "^1.1.1",
"@kwsites/promise-deferred": "^1.1.1",
"debug": "^4.3.4"
}
},
"simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
@@ -18068,9 +18249,9 @@
}
},
"terser": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
"integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz",
"integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==",
"dev": true,
"requires": {
"commander": "^2.20.0",
@@ -18092,22 +18273,15 @@
},
"dependencies": {
"terser": {
"version": "5.10.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz",
"integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==",
"version": "5.15.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz",
"integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==",
"dev": true,
"requires": {
"@jridgewell/source-map": "^0.3.2",
"acorn": "^8.5.0",
"commander": "^2.20.0",
"source-map": "~0.7.2",
"source-map-support": "~0.5.20"
},
"dependencies": {
"source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
"dev": true
}
}
}
}
@@ -18452,6 +18626,16 @@
"mkdirp": "^0.5.1"
}
},
"update-browserslist-db": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz",
"integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==",
"dev": true,
"requires": {
"escalade": "^3.1.1",
"picocolors": "^1.0.0"
}
},
"uri-js": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
+133 -54
View File
@@ -25,6 +25,7 @@
"sponsor": {
"url": "https://github.com/sponsors/estruyf"
},
"qna": "https://github.com/estruyf/vscode-front-matter/discussions",
"engines": {
"vscode": "^1.63.0"
},
@@ -59,7 +60,7 @@
"activitybar": [
{
"id": "frontmatter-explorer",
"title": "FrontMatter",
"title": "Front Matter",
"icon": "assets/frontmatter-short-min.svg"
}
]
@@ -68,9 +69,9 @@
"frontmatter-explorer": [
{
"id": "frontMatter.explorer",
"name": "FrontMatter",
"name": "Front Matter",
"icon": "assets/frontmatter-short-min.svg",
"contextualTitle": "FrontMatter",
"contextualTitle": "Front Matter",
"type": "webview"
}
]
@@ -81,7 +82,7 @@
"frontMatter.content.autoUpdateDate": {
"type": "boolean",
"default": false,
"markdownDescription": "Specify if you want to automatically update the modified date of your article/page. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.content.autoupdatedate)",
"markdownDescription": "Specify if you want to automatically update the modified date of your article/page (only content located in your content folder). [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.content.autoupdatedate)",
"scope": "Content"
},
"frontMatter.content.defaultFileType": {
@@ -167,6 +168,17 @@
"markdownDescription": "Specify if you want to highlight the Front Matter in the Markdown file. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.content.fmhighlight)",
"scope": "Content"
},
"frontMatter.content.hideFm": {
"type": "boolean",
"markdownDescription": "Specify if you want to hide the Front Matter in the Markdown file. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.content.hidefrontmatter)",
"scope": "Content"
},
"frontMatter.content.hideFmMessage": {
"type": "string",
"default": "Use the editor panel to make front matter changes",
"markdownDescription": "Specify the message to display when the Front Matter is hidden. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.content.hidefrontmatter.message)",
"scope": "Content"
},
"frontMatter.content.pageFolders": {
"type": "array",
"default": [],
@@ -351,6 +363,10 @@
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "ID of the script."
},
"title": {
"type": "string",
"description": "Title you want to give to your script. Will be shown as the title of the button."
@@ -407,6 +423,11 @@
],
"description": "The type of script you want to execute.",
"default": "node"
},
"hidden": {
"type": "boolean",
"description": "Hide the action from the UI",
"default": false
}
},
"additionalProperties": false,
@@ -630,6 +651,16 @@
"default": null,
"markdownDescription": "Specify the command you want to use to start your static site generator or framework. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.framework.startcommand)"
},
"frontMatter.git.enabled": {
"type": "boolean",
"markdownDescription": "Specify if you want to use the Git actions for your website. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.git.enabled)",
"default": false
},
"frontMatter.git.commitMsg": {
"type": "string",
"markdownDescription": "Specify the commit message you want to use for the sync. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.git.commitmsg)",
"default": "Synced by Front Matter"
},
"frontMatter.global.activeMode": {
"type": [
"string",
@@ -893,7 +924,20 @@
"taxonomyId": {
"type": "string",
"default": "",
"description": "The ID of your taxonomy field"
"description": "The ID of your taxonomy field. It cannot contain the \"tags\" or \"categories\" value.",
"not": {
"anyOf": [
{
"const": ""
},
{
"const": "tags"
},
{
"const": "categories"
}
]
}
},
"fileExtensions": {
"type": "array",
@@ -1088,6 +1132,11 @@
"type": "string",
"default": "",
"description": "An optional template that can be used for creating new content."
},
"postScript": {
"type": "string",
"default": "",
"description": "An optional post script that can be used after new content creation."
}
},
"additionalProperties": false,
@@ -1151,11 +1200,24 @@
"properties": {
"id": {
"type": "string",
"description": "ID for your taxonomy field"
"description": "ID for your taxonomy field. It cannot contain the \"tags\" or \"categories\" value.",
"not": {
"anyOf": [
{
"const": ""
},
{
"const": "tags"
},
{
"const": "categories"
}
]
}
},
"options": {
"type": "array",
"description": "Options from which you can pick",
"description": "Options from which you can pick.",
"items": {
"type": "string"
}
@@ -1297,6 +1359,12 @@
"default": false,
"markdownDescription": "Specify if you want to disable the telemetry. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.telemetry.disable)"
},
"frontMatter.templates.enabled": {
"type": "boolean",
"default": false,
"markdownDescription": "Specify if you want to use templates. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.templates.enabled)",
"scope": "Templates"
},
"frontMatter.templates.folder": {
"type": "string",
"default": ".frontmatter/templates",
@@ -1308,40 +1376,39 @@
"default": "yyyy-MM-dd",
"markdownDescription": "Specify the prefix you want to add for your new article filenames. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.templates.prefix)",
"scope": "Templates"
},
"frontMatter.templates.enabled": {
"type": "boolean",
"default": false,
"markdownDescription": "Specify if you want to use templates. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.templates.enabled)",
"scope": "Templates"
}
}
},
"commands": [
{
"command": "frontMatter.config.reload",
"title": "Reload config",
"category": "Front Matter"
},
{
"command": "frontMatter.authenticate",
"title": "Authenticate",
"category": "Front matter"
"category": "Front Matter"
},
{
"command": "frontMatter.contenttype.generate",
"title": "Generate content type from current file",
"category": "Front matter"
"category": "Front Matter"
},
{
"command": "frontMatter.contenttype.addMissingFields",
"title": "Add missing fields from front matter to content type",
"category": "Front matter"
"category": "Front Matter"
},
{
"command": "frontMatter.contenttype.setContentType",
"title": "Set the content type to use for the current file",
"category": "Front matter"
"category": "Front Matter"
},
{
"command": "frontMatter.markup.blockquote",
"title": "Blockquote",
"category": "Front matter",
"category": "Front Matter",
"icon": {
"light": "assets/icons/blockquote-light.svg",
"dark": "assets/icons/blockquote-dark.svg"
@@ -1350,7 +1417,7 @@
{
"command": "frontMatter.markup.bold",
"title": "Bold",
"category": "Front matter",
"category": "Front Matter",
"icon": {
"light": "assets/icons/bold-light.svg",
"dark": "assets/icons/bold-dark.svg"
@@ -1359,7 +1426,7 @@
{
"command": "frontMatter.dashboard.close",
"title": "Close dashboard",
"category": "Front matter",
"category": "Front Matter",
"icon": {
"dark": "/assets/icons/frontmatter-small-teal.svg",
"light": "/assets/icons/frontmatter-small-teal.svg"
@@ -1368,7 +1435,7 @@
{
"command": "frontMatter.markup.code",
"title": "Code",
"category": "Front matter",
"category": "Front Matter",
"icon": {
"light": "assets/icons/code-light.svg",
"dark": "assets/icons/code-dark.svg"
@@ -1377,7 +1444,7 @@
{
"command": "frontMatter.markup.codeblock",
"title": "Codeblock",
"category": "Front matter",
"category": "Front Matter",
"icon": {
"light": "assets/icons/codeblock-light.svg",
"dark": "assets/icons/codeblock-dark.svg"
@@ -1386,7 +1453,7 @@
{
"command": "frontMatter.collapseSections",
"title": "Collapse sections",
"category": "Front matter",
"category": "Front Matter",
"icon": {
"light": "assets/icons/close-light.svg",
"dark": "assets/icons/close-dark.svg"
@@ -1395,37 +1462,37 @@
{
"command": "frontMatter.initTemplate",
"title": "Initialize the template folder",
"category": "Front matter"
"category": "Front Matter"
},
{
"command": "frontMatter.createTemplate",
"title": "Create template from current file",
"category": "Front matter"
"category": "Front Matter"
},
{
"command": "frontMatter.createCategory",
"title": "Create category",
"category": "Front matter"
"category": "Front Matter"
},
{
"command": "frontMatter.createContent",
"title": "Create new content from defined content type or template",
"category": "Front matter"
"category": "Front Matter"
},
{
"command": "frontMatter.createTag",
"title": "Create tag",
"category": "Front matter"
"category": "Front Matter"
},
{
"command": "frontMatter.diagnostics",
"title": "Diagnostic logging",
"category": "Front matter"
"category": "Front Matter"
},
{
"command": "frontMatter.exportTaxonomy",
"title": "Export all tags & categories to your settings",
"category": "Front matter"
"category": "Front Matter"
},
{
"command": "frontMatter.createFromTemplate",
@@ -1442,12 +1509,12 @@
{
"command": "frontMatter.generateSlug",
"title": "Generate slug based on content title",
"category": "Front matter"
"category": "Front Matter"
},
{
"command": "frontMatter.markup.heading",
"title": "Heading",
"category": "Front matter",
"category": "Front Matter",
"icon": {
"light": "assets/icons/heading-light.svg",
"dark": "assets/icons/heading-dark.svg"
@@ -1456,17 +1523,17 @@
{
"command": "frontMatter.init",
"title": "Initialize project",
"category": "Front matter"
"category": "Front Matter"
},
{
"command": "frontMatter.insertCategories",
"title": "Insert categories",
"category": "Front matter"
"category": "Front Matter"
},
{
"command": "frontMatter.insertMedia",
"title": "Insert media into your content",
"category": "Front matter",
"category": "Front Matter",
"icon": {
"dark": "/assets/icons/media-dark.svg",
"light": "/assets/icons/media-light.svg"
@@ -1475,7 +1542,7 @@
{
"command": "frontMatter.insertSnippet",
"title": "Insert snippet into your content",
"category": "Front matter",
"category": "Front Matter",
"icon": {
"dark": "/assets/icons/scissors-dark.svg",
"light": "/assets/icons/scissors-light.svg"
@@ -1484,12 +1551,12 @@
{
"command": "frontMatter.insertTags",
"title": "Insert tags",
"category": "Front matter"
"category": "Front Matter"
},
{
"command": "frontMatter.markup.italic",
"title": "Italic",
"category": "Front matter",
"category": "Front Matter",
"icon": {
"light": "assets/icons/italic-light.svg",
"dark": "assets/icons/italic-dark.svg"
@@ -1498,7 +1565,7 @@
{
"command": "frontMatter.dashboard",
"title": "Open dashboard",
"category": "Front matter",
"category": "Front Matter",
"icon": {
"dark": "/assets/icons/frontmatter-small-dark.svg",
"light": "/assets/icons/frontmatter-small-light.svg"
@@ -1507,7 +1574,7 @@
{
"command": "frontMatter.dashboard.data",
"title": "Open data dashboard",
"category": "Front matter",
"category": "Front Matter",
"icon": {
"dark": "/assets/icons/frontmatter-small-dark.svg",
"light": "/assets/icons/frontmatter-small-light.svg"
@@ -1516,7 +1583,7 @@
{
"command": "frontMatter.dashboard.media",
"title": "Open media dashboard",
"category": "Front matter",
"category": "Front Matter",
"icon": {
"dark": "/assets/icons/frontmatter-small-dark.svg",
"light": "/assets/icons/frontmatter-small-light.svg"
@@ -1525,7 +1592,7 @@
{
"command": "frontMatter.dashboard.snippets",
"title": "Open snippets dashboard",
"category": "Front matter",
"category": "Front Matter",
"icon": {
"dark": "/assets/icons/frontmatter-small-dark.svg",
"light": "/assets/icons/frontmatter-small-light.svg"
@@ -1533,8 +1600,8 @@
},
{
"command": "frontMatter.dashboard.taxonomy",
"title": "Open taxonomy dashboard",
"category": "Front matter",
"title": "Open taxonomies dashboard",
"category": "Front Matter",
"icon": {
"dark": "/assets/icons/frontmatter-small-dark.svg",
"light": "/assets/icons/frontmatter-small-light.svg"
@@ -1543,7 +1610,7 @@
{
"command": "frontMatter.markup.orderedlist",
"title": "Ordered list",
"category": "Front matter",
"category": "Front Matter",
"icon": {
"light": "assets/icons/ordered-list-light.svg",
"dark": "assets/icons/ordered-list-dark.svg"
@@ -1552,7 +1619,7 @@
{
"command": "frontMatter.markup.options",
"title": "Other markup options",
"category": "Front matter",
"category": "Front Matter",
"icon": {
"light": "assets/icons/options-light.svg",
"dark": "assets/icons/options-dark.svg"
@@ -1561,27 +1628,27 @@
{
"command": "frontMatter.preview",
"title": "Preview content",
"category": "Front matter"
"category": "Front Matter"
},
{
"command": "frontMatter.promoteSettings",
"title": "Promote settings from local to team level",
"category": "Front matter"
"category": "Front Matter"
},
{
"command": "frontMatter.remap",
"title": "Remap or remove tag/category in all articles",
"category": "Front matter"
"category": "Front Matter"
},
{
"command": "frontMatter.setLastModifiedDate",
"title": "Set lastmod date",
"category": "Front matter"
"category": "Front Matter"
},
{
"command": "frontMatter.markup.strikethrough",
"title": "Strikethrough",
"category": "Front matter",
"category": "Front Matter",
"icon": {
"light": "assets/icons/strikethrough-light.svg",
"dark": "assets/icons/strikethrough-dark.svg"
@@ -1590,22 +1657,27 @@
{
"command": "frontMatter.mode.switch",
"title": "Switch mode",
"category": "Front matter",
"category": "Front Matter",
"icon": "$(preview)"
},
{
"command": "frontMatter.markup.tasklist",
"title": "Task list",
"category": "Front matter"
"category": "Front Matter"
},
{
"command": "frontMatter.markup.unorderedlist",
"title": "Unordered list",
"category": "Front matter",
"category": "Front Matter",
"icon": {
"light": "assets/icons/unordered-list-light.svg",
"dark": "assets/icons/unordered-list-dark.svg"
}
},
{
"command": "frontMatter.git.sync",
"title": "Sync",
"category": "Front Matter"
}
],
"menus": {
@@ -1724,6 +1796,10 @@
"command": "frontMatter.dashboard.snippets",
"when": "frontMatter:dashboard:snippets:enabled"
},
{
"command": "frontMatter.git.sync",
"when": "frontMatter:git:enabled"
},
{
"command": "frontMatter.collapseSections",
"when": "false"
@@ -1938,6 +2014,7 @@
"@heroicons/react": "1.0.4",
"@iarna/toml": "2.2.3",
"@octokit/rest": "^18.12.0",
"@popperjs/core": "^2.11.6",
"@sentry/react": "^6.13.3",
"@sentry/tracing": "^6.13.3",
"@tailwindcss/forms": "^0.3.3",
@@ -1975,6 +2052,7 @@
"html-webpack-plugin": "4.5.0",
"image-size": "^1.0.0",
"invariant": "^2.2.4",
"jsonc-parser": "^3.2.0",
"lodash-es": "^4.17.21",
"lodash.omit": "^4.5.0",
"lodash.uniqby": "4.7.0",
@@ -2000,6 +2078,7 @@
"recoil": "^0.4.1",
"rimraf": "^3.0.2",
"semver": "^7.3.7",
"simple-git": "^3.10.0",
"style-loader": "2.0.0",
"tailwindcss": "^2.2.7",
"tailwindcss-nested-groups": "^1.2.4",
+12 -3
View File
@@ -1,8 +1,9 @@
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 * as vscode from 'vscode';
import { Field, TaxonomyType } from "../models";
import { CustomPlaceholder, Field, TaxonomyType } from "../models";
import { format } from "date-fns";
import { ArticleHelper, Settings, SlugHelper } from '../helpers';
import { Notifications } from '../helpers/Notifications';
@@ -225,8 +226,8 @@ export class Article {
}
// Update the fields containing a custom placeholder that depends on slug
const placeholders = Settings.get<{id: string, value: string}[]>(SETTING_CONTENT_PLACEHOLDERS);
const customPlaceholders = placeholders?.filter(p => p.value.includes("{{slug}}"));
const placeholders = Settings.get<CustomPlaceholder[]>(SETTING_CONTENT_PLACEHOLDERS);
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}}}`);
@@ -323,6 +324,14 @@ export class Article {
if (document && ArticleHelper.isSupportedFile(document)) {
const autoUpdate = Settings.get(SETTING_AUTO_UPDATE_DATE);
// 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));
if (!folder) {
return;
}
if (autoUpdate) {
event.waitUntil(Article.setLastModifiedDateOnSave(document));
}
+2 -2
View File
@@ -1,5 +1,5 @@
import { commands, ExtensionContext } from 'vscode';
import { CONTEXT } from '../constants';
import { COMMAND_NAME, CONTEXT } from '../constants';
import { Extension } from '../helpers';
import { Credentials } from "../services/Credentials";
import fetch from "node-fetch";
@@ -17,7 +17,7 @@ export class Backers {
Backers.tryUsernameCheck();
context.subscriptions.push(
commands.registerCommand('frontMatter.authenticate', async () => {
commands.registerCommand(COMMAND_NAME.authenticate, async () => {
Backers.tryUsernameCheck();
})
);
+2 -1
View File
@@ -9,7 +9,7 @@ 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 { ModeListener } from '../listeners/general';
import { GitListener, ModeListener } from '../listeners/general';
export class Dashboard {
private static webview: WebviewPanel | null = null;
@@ -146,6 +146,7 @@ export class Dashboard {
TelemetryListener.process(msg);
SnippetListener.process(msg);
ModeListener.process(msg);
GitListener.process(msg);
TaxonomyListener.process(msg);
});
}
+1 -1
View File
@@ -175,7 +175,7 @@ export class Template {
}
if (frontMatter.data) {
frontMatter.data = ArticleHelper.updatePlaceholders(frontMatter.data, titleValue);
frontMatter.data = await ArticleHelper.updatePlaceholders(frontMatter.data, titleValue, newFilePath);
frontMatter = Article.updateDate(frontMatter);
+9
View File
@@ -62,4 +62,13 @@ export const COMMAND_NAME = {
generateContentType: getCommandName("contenttype.generate"),
addMissingFields: getCommandName("contenttype.addMissingFields"),
setContentType: getCommandName("contenttype.setContentType"),
// Git
gitSync: getCommandName("git.sync"),
// Authenticate
authenticate: getCommandName("authenticate"),
// Config
reloadConfig: getCommandName("config.reload"),
};
+3
View File
@@ -3,8 +3,11 @@
export const GeneralCommands = {
toWebview: {
setMode: "setMode",
gitSyncingStart: "gitSyncingStart",
gitSyncingEnd: "gitSyncingEnd",
},
toVSCode: {
openLink: "openLink",
gitSync: "gitSync",
}
};
+2
View File
@@ -2,5 +2,7 @@ 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";
+3
View File
@@ -43,4 +43,7 @@ export const TelemetryEvent = {
webviewContentsView: 'webviewContentsView',
webviewSnippetsView: 'webviewSnippetsView',
webviewTaxonomyDashboard: 'webviewTaxonomyDashboard',
// Git
gitSync: 'gitSync',
};
+2
View File
@@ -13,4 +13,6 @@ export const CONTEXT = {
isSnippetsDashboardEnabled: "frontMatter:dashboard:snippets:enabled",
isDataDashboardEnabled: "frontMatter:dashboard:data:enabled",
isGitEnabled: "frontMatter:git:enabled",
};
+6
View File
@@ -59,6 +59,9 @@ export const SETTING_MEDIA_SORTING_DEFAULT = "content.defaultSorting";
export const SETTING_CONTENT_DEFAULT_FILETYPE = "content.defaultFileType";
export const SETTING_CONTENT_SUPPORTED_FILETYPES = "content.supportedFileTypes";
export const SETTING_CONTENT_HIDE_FRONTMATTER = "content.hideFm";
export const SETTING_CONTENT_HIDE_FRONTMATTER_MESSAGE = "content.hideFmMessage";
export const SETTING_MEDIA_SUPPORTED_MIMETYPES = "media.supportedMimeTypes";
export const SETTING_DASHBOARD_OPENONSTART = "dashboard.openOnStart";
@@ -75,6 +78,9 @@ 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.commitMsg";
/**
* @deprecated
*/
@@ -12,11 +12,10 @@ export interface IChoiceButtonProps {
onClick: () => void;
}[];
disabled?: boolean;
isTemplatesEnabled?: boolean;
onClick: () => void;
}
export const ChoiceButton: React.FunctionComponent<IChoiceButtonProps> = ({onClick, disabled, choices, isTemplatesEnabled, title}: React.PropsWithChildren<IChoiceButtonProps>) => {
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
@@ -29,7 +28,7 @@ export const ChoiceButton: React.FunctionComponent<IChoiceButtonProps> = ({onCli
</button>
{
isTemplatesEnabled && (
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"
@@ -6,17 +6,27 @@ import { CustomScript, ScriptType } from '../../../models';
import { DashboardMessage } from '../../DashboardMessage';
import { MenuItem, MenuItems, ActionMenuButton, QuickAction } from '../Menu';
import { Alert } from '../Modals/Alert';
import { usePopper } from 'react-popper';
import { useState } from 'react';
export interface IContentActionsProps {
title: string;
path: string;
scripts: CustomScript[] | undefined;
listView?: boolean;
onOpen: () => void;
}
export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({ title, path, scripts, onOpen }: React.PropsWithChildren<IContentActionsProps>) => {
export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({ title, path, scripts, onOpen, listView }: React.PropsWithChildren<IContentActionsProps>) => {
const [ showDeletionAlert, setShowDeletionAlert ] = React.useState(false);
const [referenceElement, setReferenceElement] = useState<any>(null);
const [popperElement, setPopperElement] = useState<any>(null);
const { styles, attributes, forceUpdate } = usePopper(referenceElement, popperElement, {
placement: listView ? 'right-start' : 'bottom-end',
strategy: 'fixed'
})
const onView = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
onOpen();
@@ -40,7 +50,7 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
}, [path]);
const customScriptActions = React.useMemo(() => {
return (scripts || []).filter(script => (script.type === undefined || script.type === ScriptType.Content) && !script.bulk).map(script => (
return (scripts || []).filter(script => (script.type === undefined || script.type === ScriptType.Content) && !script.bulk && !script.hidden).map(script => (
<MenuItem
key={script.title}
title={<div className='flex items-center'><TerminalIcon className="mr-2 h-5 w-5 flex-shrink-0" aria-hidden={true} /> <span>{script.title}</span></div>}
@@ -50,37 +60,45 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
return (
<>
<div className={`group-scope absolute top-6 right-0 flex flex-col space-y-4`}>
<div className="flex items-center border border-transparent group-scope-hover:bg-gray-200 dark:group-scope-hover:bg-vulcan-200 group-scope-hover:border-gray-100 dark:group-scope-hover:border-vulcan-50 rounded-full p-2 -mt-4">
<div className={`${listView ? '' : 'group-scope absolute top-6 right-0'} flex flex-col space-y-4`}>
<div className={`flex items-center border border-transparent group-scope-hover:bg-gray-200 dark:group-scope-hover:bg-vulcan-200 group-scope-hover:border-gray-100 dark:group-scope-hover:border-vulcan-50 rounded-full ${listView ? '' : 'p-2 -mt-4'}`}>
<Menu as="div" className="relative z-10 flex text-left">
<div className='hidden group-scope-hover:flex'>
<QuickAction
title={`View content`}
onClick={onView}>
<EyeIcon className={`w-4 h-4`} aria-hidden="true" />
</QuickAction>
<QuickAction
title={`Delete content`}
onClick={onDelete}>
<TrashIcon className={`w-4 h-4`} aria-hidden="true" />
</QuickAction>
<Menu as="div" className={`relative flex text-left ${listView ? '' : 'z-10'}`}>
{
!listView && (
<div className='hidden group-scope-hover:flex'>
<QuickAction
title={`View content`}
onClick={onView}>
<EyeIcon className={`w-4 h-4`} aria-hidden="true" />
</QuickAction>
<QuickAction
title={`Delete content`}
onClick={onDelete}>
<TrashIcon className={`w-4 h-4`} aria-hidden="true" />
</QuickAction>
</div>
)
}
<div ref={setReferenceElement} className={`flex`}>
<ActionMenuButton title={`Menu`} />
</div>
<ActionMenuButton title={`Menu`} />
<div className='menu_items__wrapper z-20' ref={setPopperElement} style={styles.popper} {...attributes.popper}>
<MenuItems updatePopper={forceUpdate || undefined} widthClass='w-44' marginTopClass={listView ? '' : ''}>
<MenuItem
title={<div className='flex items-center'><EyeIcon className="mr-2 h-5 w-5 flex-shrink-0" aria-hidden={true} /> <span>View</span></div>}
onClick={(value, e) => onView(e)} />
<MenuItems widthClass='w-44' marginTopClass='mt-6'>
<MenuItem
title={<div className='flex items-center'><EyeIcon className="mr-2 h-5 w-5 flex-shrink-0" aria-hidden={true} /> <span>View</span></div>}
onClick={(value, e) => onView(e)} />
{ customScriptActions }
{ customScriptActions }
<MenuItem
title={<div className='flex items-center'><TrashIcon className="mr-2 h-5 w-5 flex-shrink-0" aria-hidden={true} /> <span>Delete</span></div>}
onClick={(value, e) => onDelete(e)} />
</MenuItems>
<MenuItem
title={<div className='flex items-center'><TrashIcon className="mr-2 h-5 w-5 flex-shrink-0" aria-hidden={true} /> <span>Delete</span></div>}
onClick={(value, e) => onDelete(e)} />
</MenuItems>
</div>
</Menu>
</div>
</div>
@@ -52,10 +52,9 @@ export const Item: React.FunctionComponent<IItemProps> = ({ fmFilePath, date, ti
if (view === DashboardViewType.Grid) {
return (
<li className="relative">
<button className={`group cursor-pointer flex flex-wrap items-start content-start h-full w-full bg-gray-50 dark:bg-vulcan-200 text-vulcan-500 dark:text-whisper-500 text-left shadow-md dark:shadow-none hover:shadow-xl dark:hover:bg-vulcan-100 border border-gray-200 dark:border-vulcan-50`}
onClick={openFile}>
<div className="relative h-36 w-full overflow-hidden border-b border-gray-100 dark:border-vulcan-100 dark:group-hover:border-vulcan-200">
<div className={`group flex flex-wrap items-start content-start h-full w-full bg-gray-50 dark:bg-vulcan-200 text-vulcan-500 dark:text-whisper-500 text-left shadow-md dark:shadow-none hover:shadow-xl dark:hover:bg-vulcan-100 border border-gray-200 dark:border-vulcan-50`}>
<button onClick={openFile} className="relative h-36 w-full overflow-hidden border-b border-gray-100 dark:border-vulcan-100 dark:group-hover:border-vulcan-200 cursor-pointer">
{
pageData[PREVIEW_IMAGE_FIELD] ? (
<img src={`${pageData[PREVIEW_IMAGE_FIELD]}`} alt={title} className="absolute inset-0 h-full w-full object-cover" loading="lazy" />
@@ -65,7 +64,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({ fmFilePath, date, ti
</div>
)
}
</div>
</button>
<div className="relative p-4 w-full">
<div className={`flex justify-between items-center`}>
@@ -80,35 +79,48 @@ export const Item: React.FunctionComponent<IItemProps> = ({ fmFilePath, date, ti
onOpen={openFile} />
</div>
<h2 className="mt-2 mb-2 font-bold">{title}</h2>
<button onClick={openFile} className={`text-left`}><h2 className="mt-2 mb-2 font-bold">{title}</h2></button>
<p className="text-xs text-vulcan-200 dark:text-whisper-800">{description}</p>
<button onClick={openFile} className={`text-left`}><p className="text-xs text-vulcan-200 dark:text-whisper-800">{description}</p></button>
{
tags && tags.length > 0 && (
<div className="mt-2">
{
tags.map((tag, index) => (
<span
key={index}
className="inline-block mr-1 mt-1 text-[#5D561D] dark:text-[#F0ECD0] text-xs">
#{tag}
</span>
tag && (
<span
key={index}
className="inline-block mr-1 mt-1 text-[#5D561D] dark:text-[#F0ECD0] text-xs">
#{tag}
</span>
)
))
}
</div>
)
}
</div>
</button>
</div>
</li>
);
} else if (view === DashboardViewType.List) {
return (
<li className="relative">
<button className={`px-5 cursor-pointer w-full text-left grid grid-cols-12 gap-x-4 sm:gap-x-6 xl:gap-x-8 py-2 border-b border-gray-300 hover:bg-gray-200 dark:border-vulcan-50 dark:hover:bg-vulcan-50 hover:bg-opacity-70`} onClick={openFile}>
<div className="col-span-8 font-bold truncate">
{title}
<div className={`px-5 cursor-pointer w-full text-left grid grid-cols-12 gap-x-4 sm:gap-x-6 xl:gap-x-8 py-2 border-b border-gray-300 hover:bg-gray-200 dark:border-vulcan-50 dark:hover:bg-vulcan-50 hover:bg-opacity-70`}>
<div className="col-span-8 font-bold truncate flex items-center space-x-4">
<button
title={`Open: ${title}`}
onClick={openFile}>
{title}
</button>
<ContentActions
title={title}
path={fmFilePath}
scripts={settings?.scripts}
onOpen={openFile}
listView />
</div>
<div className="col-span-2">
<DateField value={date} />
@@ -116,7 +128,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({ fmFilePath, date, ti
<div className="col-span-2">
{ draftField && draftField.name && <Status draft={pageData[draftField.name]} /> }
</div>
</button>
</div>
</li>
);
}
@@ -21,7 +21,8 @@ import { CustomScript } from '../../../models';
import { LightningBoltIcon, PlusIcon } from '@heroicons/react/outline';
import { useLocation, useNavigate } from 'react-router-dom';
import { routePaths } from '../..';
import { useEffect } from 'react';
import { useEffect, useMemo } from 'react';
import { SyncButton } from './SyncButton';
export interface IHeaderProps {
header?: React.ReactNode;
@@ -72,6 +73,38 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({header, totalPage
onClick: () => runBulkScript(s)
}));
const choiceOptions = useMemo(() => {
const isEnabled = settings?.dashboardState?.contents?.templatesEnabled || false;
if (isEnabled) {
return [
{
title: (
<div className='flex items-center'>
<PlusIcon className="w-4 h-4 mr-2" />
<span>Create by content type</span>
</div>
),
onClick: createByContentType,
disabled: !settings?.initialized
}, {
title: (
<div className='flex items-center'>
<PlusIcon className="w-4 h-4 mr-2" />
<span>Create by template</span>
</div>
),
onClick: createByTemplate,
disabled: !settings?.initialized
},
...customActions
];
}
return [];
}, [settings?.dashboardState?.contents?.templatesEnabled]);
useEffect(() => {
if (location.search) {
const searchParams = new URLSearchParams(location.search);
@@ -108,33 +141,13 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({header, totalPage
<div className={`flex items-center justify-end space-x-4 flex-1`}>
<Startup settings={settings} />
<SyncButton />
<ChoiceButton
title={`Create content`}
choices={[
{
title: (
<div className='flex items-center'>
<PlusIcon className="w-4 h-4 mr-2" />
<span>Create by content type</span>
</div>
),
onClick: createByContentType,
disabled: !settings?.initialized
}, {
title: (
<div className='flex items-center'>
<PlusIcon className="w-4 h-4 mr-2" />
<span>Create by template</span>
</div>
),
onClick: createByTemplate,
disabled: !settings?.initialized
},
...customActions
]}
onClick={createContent}
isTemplatesEnabled={settings?.dashboardState?.contents?.templatesEnabled || undefined}
choices={choiceOptions}
onClick={createContent}
disabled={!settings?.initialized} />
</div>
</div>
@@ -0,0 +1,55 @@
import { Messenger } from '@estruyf/vscode/dist/client';
import { EventData } from '@estruyf/vscode/dist/models';
import { RefreshIcon } from '@heroicons/react/outline';
import * as React from 'react';
import { useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil';
import { GeneralCommands } from '../../../constants';
import { SettingsSelector } from '../../state';
export interface ISyncButtonProps {}
export const SyncButton: React.FunctionComponent<ISyncButtonProps> = (props: React.PropsWithChildren<ISyncButtonProps>) => {
const settings = useRecoilValue(SettingsSelector);
const [ isSyncing, setIsSyncing ] = useState(false);
const pull = () => {
Messenger.send(GeneralCommands.toVSCode.gitSync);
};
const messageListener = (message: MessageEvent<EventData<any>>) => {
const { command, data } = message.data;
if (command === GeneralCommands.toWebview.gitSyncingStart) {
setIsSyncing(true);
} else if (command === GeneralCommands.toWebview.gitSyncingEnd) {
setIsSyncing(false);
}
};
useEffect(() => {
Messenger.listen(messageListener);
return () => {
Messenger.unlisten(messageListener);
}
}, []);
if (!settings?.git?.actions || !settings?.git.isGitRepo) {
return null;
}
return (
<div className='git_actions'>
<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={pull}
disabled={isSyncing}
>
<RefreshIcon className={`w-4 h-4 mr-2 ${isSyncing ? 'animate-reverse-spin' : ''}`} aria-hidden="true" />
<span>Sync</span>
</button>
</div>
);
};
@@ -54,7 +54,7 @@ export const Tabs: React.FunctionComponent<ITabsProps> = ({ onNavigate }: React.
<Tab
navigationType={NavigationType.Taxonomy}
onNavigate={onNavigate}>
<TagIcon className={`h-6 w-auto mr-2`} /><span>Taxonomy</span>
<TagIcon className={`h-6 w-auto mr-2`} /><span>Taxonomies</span>
</Tab>
</li>
</FeatureFlag>
@@ -23,7 +23,8 @@ export const FolderCreation: React.FunctionComponent<IFolderCreationProps> = (pr
Messenger.send(DashboardMessage.runCustomScript, {script, path: selectedFolder});
};
const scripts = (settings?.scripts || []).filter(script => script.type === ScriptType.MediaFolder);
const scripts = (settings?.scripts || []).filter(script => script.type === ScriptType.MediaFolder && !script.hidden);
if (scripts.length > 0) {
return (
<div className="flex flex-1 justify-end">
+68 -57
View File
@@ -3,7 +3,7 @@ import { Menu } from '@headlessui/react';
import { ClipboardIcon, CodeIcon, DocumentIcon, EyeIcon, MusicNoteIcon, PencilIcon, PhotographIcon, PlusIcon, TerminalIcon, TrashIcon, VideoCameraIcon } from '@heroicons/react/outline';
import { basename, dirname } from 'path';
import * as React from 'react';
import { useCallback, useEffect, useMemo } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { CustomScript } from '../../../helpers/CustomScript';
import { parseWinPath } from '../../../helpers/parseWinPath';
@@ -18,6 +18,7 @@ import { QuickAction } from '../Menu/QuickAction';
import { Alert } from '../Modals/Alert';
import { InfoDialog } from '../Modals/InfoDialog';
import { DetailsSlideOver } from './DetailsSlideOver';
import { usePopper } from 'react-popper';
export interface IItemProps {
media: MediaInfo;
@@ -25,17 +26,24 @@ export interface IItemProps {
export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWithChildren<IItemProps>) => {
const [ , setLightbox ] = useRecoilState(LightboxAtom);
const [ showAlert, setShowAlert ] = React.useState(false);
const [ showForm, setShowForm ] = React.useState(false);
const [ showSnippetSelection, setShowSnippetSelection ] = React.useState(false);
const [ showDetails, setShowDetails ] = React.useState(false);
const [ caption, setCaption ] = React.useState(media.caption);
const [ alt, setAlt ] = React.useState(media.alt);
const [ filename, setFilename ] = React.useState<string | null>(null);
const [ showAlert, setShowAlert ] = useState(false);
const [ showForm, setShowForm ] = useState(false);
const [ showSnippetSelection, setShowSnippetSelection ] = useState(false);
const [ showDetails, setShowDetails ] = useState(false);
const [ caption, setCaption ] = useState(media.caption);
const [ alt, setAlt ] = useState(media.alt);
const [ filename, setFilename ] = useState<string | null>(null);
const settings = useRecoilValue(SettingsSelector);
const selectedFolder = useRecoilValue(SelectedMediaFolderSelector);
const viewData = useRecoilValue(ViewDataSelector);
const [referenceElement, setReferenceElement] = useState<any>(null);
const [popperElement, setPopperElement] = useState<any>(null);
const { styles, attributes, forceUpdate } = usePopper(referenceElement, popperElement, {
placement: 'bottom-end',
strategy: 'fixed'
})
const mediaSnippets = useMemo(() => {
if (!settings?.snippets) {
return [];
@@ -225,7 +233,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWi
};
const customScriptActions = () => {
return (settings?.scripts || []).filter(script => script.type === ScriptType.MediaFile).map(script => (
return (settings?.scripts || []).filter(script => script.type === ScriptType.MediaFile && !script.hidden).map(script => (
<MenuItem
key={script.title}
title={<div className='flex items-center'><TerminalIcon className="mr-2 h-5 w-5 flex-shrink-0" aria-hidden={true} /> <span>{script.title}</span></div>}
@@ -259,7 +267,6 @@ export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWi
const extension = path.split('.').pop();
let icon = <DocumentIcon className={`h-4/6 text-gray-300 dark:text-vulcan-200`} />;
console.log(media);
if (isImageFile) {
return <PhotographIcon className={`h-1/2 text-gray-300 dark:text-vulcan-200`} />;
@@ -381,59 +388,63 @@ export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWi
</QuickAction>
</div>
<ActionMenuButton title={`Menu`} />
<div ref={setReferenceElement} className={`flex`}>
<ActionMenuButton title={`Menu`} />
</div>
<MenuItems widthClass='w-40'>
<MenuItem
title={(
<div className='flex items-center'>
<PencilIcon className="mr-2 h-5 w-5 flex-shrink-0" aria-hidden={true} /> <span>Edit metadata</span>
</div>
)}
onClick={updateMetadata}
/>
<div className='menu_items__wrapper z-20' ref={setPopperElement} style={styles.popper} {...attributes.popper}>
<MenuItems widthClass='w-40'>
<MenuItem
title={(
<div className='flex items-center'>
<PencilIcon className="mr-2 h-5 w-5 flex-shrink-0" aria-hidden={true} /> <span>Edit metadata</span>
</div>
)}
onClick={updateMetadata}
/>
{
viewData?.data?.filePath ? (
<>
<MenuItem
title={<div className='flex items-center'><PlusIcon className="mr-2 h-5 w-5 flex-shrink-0" aria-hidden={true} /> <span>Insert image markdown</span></div>}
onClick={insertToArticle} />
{
viewData?.data?.filePath ? (
<>
<MenuItem
title={<div className='flex items-center'><PlusIcon className="mr-2 h-5 w-5 flex-shrink-0" aria-hidden={true} /> <span>Insert image markdown</span></div>}
onClick={insertToArticle} />
{
(viewData?.data?.position && mediaSnippets.length > 0) && mediaSnippets.map((snippet, idx) => (
<MenuItem
key={idx}
title={<div className='flex items-center'><CodeIcon className="mr-2 h-5 w-5 flex-shrink-0" aria-hidden={true} /> <span>{snippet.title}</span></div>}
onClick={() => processSnippet(snippet)} />
))
}
{
(viewData?.data?.position && mediaSnippets.length > 0) && mediaSnippets.map((snippet, idx) => (
<MenuItem
key={idx}
title={<div className='flex items-center'><CodeIcon className="mr-2 h-5 w-5 flex-shrink-0" aria-hidden={true} /> <span>{snippet.title}</span></div>}
onClick={() => processSnippet(snippet)} />
))
}
{ customScriptActions() }
</>
) : (
<>
<MenuItem
title={(
<div className='flex items-center'>
<ClipboardIcon className="mr-2 h-5 w-5 flex-shrink-0" aria-hidden={true} /> <span>Copy media path</span>
</div>
)}
onClick={copyToClipboard} />
{ customScriptActions() }
</>
) : (
<>
<MenuItem
title={(
<div className='flex items-center'>
<ClipboardIcon className="mr-2 h-5 w-5 flex-shrink-0" aria-hidden={true} /> <span>Copy media path</span>
</div>
)}
onClick={copyToClipboard} />
{ customScriptActions() }
</>
)
}
{ customScriptActions() }
</>
)
}
<MenuItem
title={<div className='flex items-center'><EyeIcon className="mr-2 h-5 w-5 flex-shrink-0" aria-hidden={true} /> <span>Reveal media</span></div>}
onClick={revealMedia} />
<MenuItem
title={<div className='flex items-center'><EyeIcon className="mr-2 h-5 w-5 flex-shrink-0" aria-hidden={true} /> <span>Reveal media</span></div>}
onClick={revealMedia} />
<MenuItem
title={<div className='flex items-center'><TrashIcon className="mr-2 h-5 w-5 flex-shrink-0" aria-hidden={true} /> <span>Delete</span></div>}
onClick={deleteMedia} />
</MenuItems>
<MenuItem
title={<div className='flex items-center'><TrashIcon className="mr-2 h-5 w-5 flex-shrink-0" aria-hidden={true} /> <span>Delete</span></div>}
onClick={deleteMedia} />
</MenuItems>
</div>
</Menu>
</div>
@@ -531,4 +542,4 @@ export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWi
}
</>
);
};
};
@@ -5,11 +5,15 @@ import * as React from 'react';
export interface IActionMenuButtonProps {
title: string;
disabled?: boolean;
ref?: (instance: Element | null) => void;
}
export const ActionMenuButton: React.FunctionComponent<IActionMenuButtonProps> = ({ title, disabled }: React.PropsWithChildren<IActionMenuButtonProps>) => {
export const ActionMenuButton: React.FunctionComponent<IActionMenuButtonProps> = ({ title, disabled, ref }: React.PropsWithChildren<IActionMenuButtonProps>) => {
return (
<Menu.Button disabled={disabled} className={`group inline-flex justify-center text-sm font-medium text-vulcan-400 hover:text-vulcan-600 dark:text-gray-400 dark:hover:text-whisper-600 ${disabled ? 'opacity-50' : ''}`}>
<Menu.Button
ref={ref || null}
disabled={disabled}
className={`group inline-flex justify-center text-sm font-medium text-vulcan-400 hover:text-vulcan-600 dark:text-gray-400 dark:hover:text-whisper-600 ${disabled ? 'opacity-50' : ''}`}>
<span className="sr-only">{title}</span>
<DotsVerticalIcon className="w-4 h-4" aria-hidden="true" />
</Menu.Button>
@@ -5,12 +5,14 @@ import { Fragment } from 'react';
export interface IMenuItemsProps {
widthClass?: string;
marginTopClass?: string;
updatePopper?: () => void;
}
export const MenuItems: React.FunctionComponent<IMenuItemsProps> = ({widthClass, marginTopClass, children}: React.PropsWithChildren<IMenuItemsProps>) => {
export const MenuItems: React.FunctionComponent<IMenuItemsProps> = ({widthClass, marginTopClass, children, updatePopper}: React.PropsWithChildren<IMenuItemsProps>) => {
return (
<Transition
as={Fragment}
beforeEnter={() => updatePopper ? updatePopper() : null}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
@@ -18,7 +20,7 @@ export const MenuItems: React.FunctionComponent<IMenuItemsProps> = ({widthClass,
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className={`${widthClass || ""} ${marginTopClass || "mt-2"} origin-top-right absolute right-0 z-20 rounded-md shadow-2xl bg-white dark:bg-vulcan-500 ring-1 ring-vulcan-400 dark:ring-white ring-opacity-5 focus:outline-none text-sm max-h-96 overflow-auto`}>
<Menu.Items className={`${widthClass || ""} ${marginTopClass || "mt-2"} rounded-md shadow-2xl bg-white dark:bg-vulcan-500 ring-1 ring-vulcan-400 dark:ring-white ring-opacity-5 focus:outline-none text-sm max-h-96 overflow-auto`}>
<div className="py-1">
{children}
</div>
@@ -79,15 +79,21 @@ const SnippetForm: React.ForwardRefRenderFunction<SnippetFormHandle, ISnippetFor
const allFields: SnippetField[] = [];
const snippetFields = snippet.fields || [];
for (const fieldName of placeholders) {
const field = snippetFields.find(f => f.name === fieldName);
if (field) {
// Loop over all fields to check if they are present in the snippet
for (const field of snippetFields) {
const idx = placeholders.findIndex(fieldName => fieldName === field.name);
if (idx > -1) {
allFields.push({
...field,
value: insertPlaceholderValues(field.default || "")
});
} else {
}
}
// Loop over all placeholders to find the ones that are not present in the snippet fields
for (const fieldName of placeholders) {
const idx = snippetFields.findIndex(field => field.name === fieldName);
if (idx === -1) {
allFields.push({
name: fieldName,
title: fieldName,
@@ -38,6 +38,9 @@ export const TaxonomyManager: React.FunctionComponent<ITaxonomyManagerProps> = (
// Alphabetically sort the items
crntItems = Object.assign([], crntItems).sort((a: string, b: string) => {
a = a || "";
b = b || "";
if (a.toLowerCase() < b.toLowerCase()) {
return -1;
}
+8 -2
View File
@@ -18,7 +18,7 @@ export default function useMessages() {
const [, setView] = useRecoilState(DashboardViewAtom);
const [, setSearchReady] = useRecoilState(SearchReadyAtom);
Messenger.listen((event: MessageEvent<EventData<any>>) => {
const messageListener = (event: MessageEvent<EventData<any>>) => {
const message = event.data;
switch (message.command) {
@@ -53,14 +53,20 @@ export default function useMessages() {
setMode(message.data);
break;
}
});
};
useEffect(() => {
Messenger.listen(messageListener);
setLoading(true);
Messenger.send(DashboardMessage.getViewType);
Messenger.send(DashboardMessage.getTheme);
Messenger.send(DashboardMessage.getData);
Messenger.send(DashboardMessage.getMode);
return () => {
Messenger.unlisten(messageListener);
}
}, ['']);
return {
+4 -1
View File
@@ -40,7 +40,10 @@ if (elm) {
tracesSampleRate: 0, // No performance tracing required
release: version || "",
environment: environment || "",
ignoreErrors: ['ResizeObserver loop limit exceeded']
ignoreErrors: [
'ResizeObserver loop limit exceeded',
"Cannot read properties of undefined (reading 'unobserve')"
]
});
}
+2 -1
View File
@@ -1,12 +1,13 @@
import { DataType } from './../../models/DataType';
import { VersionInfo } from '../../models/VersionInfo';
import { ContentFolder } from '../../models/ContentFolder';
import { ContentType, CustomScript, CustomTaxonomy, DraftField, Framework, Snippets, SortingSetting } from '../../models';
import { ContentType, CustomScript, CustomTaxonomy, DraftField, Framework, GitSettings, Snippets, SortingSetting } from '../../models';
import { SortingOption } from './SortingOption';
import { DashboardViewType } from '.';
import { DataFile } from '../../models/DataFile';
export interface Settings {
git: GitSettings;
beta: boolean;
initialized: boolean;
wsFolder: string;
+2 -1
View File
@@ -7,7 +7,7 @@ import { TagType } from '../panelWebView/TagType';
import { WebviewHelper } from '@estruyf/vscode';
import { Extension } from '../helpers/Extension';
import { Telemetry } from '../helpers/Telemetry';
import { ModeListener } from '../listeners/general';
import { GitListener, ModeListener } from '../listeners/general';
export class ExplorerView implements WebviewViewProvider, Disposable {
public static readonly viewType = "frontMatter.explorer";
@@ -82,6 +82,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
SettingsListener.process(msg);
TaxonomyListener.process(msg);
ModeListener.process(msg);
GitListener.process(msg);
});
webviewView.onDidChangeVisibility(() => {
+7 -12
View File
@@ -1,3 +1,4 @@
import { GitListener } from './listeners/general/GitListener';
import * as vscode from 'vscode';
import { Telemetry } from './helpers/Telemetry';
import { ContentType } from './helpers/ContentType';
@@ -14,7 +15,7 @@ import { TagType } from './panelWebView/TagType';
import { ExplorerView } from './explorerView/ExplorerView';
import { Extension } from './helpers/Extension';
import { DashboardData } from './models/DashboardData';
import { Logger, Settings as SettingsHelper } from './helpers';
import { debounceCallback, Logger, Settings as SettingsHelper } from './helpers';
import { Content } from './commands/Content';
import ContentProvider from './providers/ContentProvider';
import { Wysiwyg } from './commands/Wysiwyg';
@@ -202,10 +203,11 @@ export async function activate(context: vscode.ExtensionContext) {
SettingsHelper.onConfigChange((global?: any) => {
Template.init();
Preview.init();
GitListener.init();
SettingsListener.getSettings();
DataListener.getFoldersAndFiles();
MarkdownFoldingProvider.triggerHighlighting();
MarkdownFoldingProvider.triggerHighlighting(true);
ModeSwitch.register();
});
@@ -261,6 +263,9 @@ export async function activate(context: vscode.ExtensionContext) {
// Diagnostics
subscriptions.push(vscode.commands.registerCommand(COMMAND_NAME.diagnostics, Diagnostics.show));
// Git
GitListener.init();
// Subscribe all commands
subscriptions.push(
insertTags,
@@ -297,14 +302,4 @@ const handleAutoDateUpdate = (e: vscode.TextDocumentWillSaveEvent) => {
const triggerShowDraftStatus = (location: string) => {
Logger.info(`Triggering draft status update: ${location}`);
statusDebouncer(() => { StatusListener.verify(frontMatterStatusBar, collection); }, 1000);
};
const debounceCallback = () => {
let timeout: NodeJS.Timeout;
return (fnc: any, time: number) => {
const functionCall = (...args: any[]) => fnc.apply(args);
clearTimeout(timeout);
timeout = setTimeout(functionCall, time) as any;
};
};
+47 -7
View File
@@ -1,3 +1,5 @@
import * as jsoncParser from 'jsonc-parser';
import { CustomPlaceholder } from './../models/CustomPlaceholder';
import { Uri, workspace } from 'vscode';
import { MarkdownFoldingProvider } from './../providers/MarkdownFoldingProvider';
import { DEFAULT_CONTENT_TYPE, DEFAULT_CONTENT_TYPE_NAME } from './../constants/ContentType';
@@ -22,6 +24,8 @@ import { fromMarkdown } from 'mdast-util-from-markdown';
import { Link, Parent } from 'mdast-util-from-markdown/lib';
import { Content } from 'mdast';
import { processKnownPlaceholders } from './PlaceholderHelper';
import { CustomScript } from './CustomScript';
import { Folders } from '../commands/Folders';
export class ArticleHelper {
private static notifiedFiles: string[] = [];
@@ -368,7 +372,7 @@ export class ArticleHelper {
* @param title
* @returns
*/
public static updatePlaceholders(data: any, title: string) {
public static async updatePlaceholders(data: any, title: string, filePath: string) {
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
const fmData = Object.assign({}, data);
@@ -384,7 +388,7 @@ export class ArticleHelper {
}
fmData[fieldName] = processKnownPlaceholders(fmData[fieldName], title, dateFormat);
fmData[fieldName] = this.processCustomPlaceholders(fmData[fieldName], title);
fmData[fieldName] = await this.processCustomPlaceholders(fmData[fieldName], title, filePath);
}
return fmData;
@@ -396,16 +400,52 @@ export class ArticleHelper {
* @param title
* @returns
*/
public static processCustomPlaceholders(value: string, title: string) {
public static async processCustomPlaceholders(value: string, title: string, filePath: string) {
if (value && typeof value === "string") {
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
const placeholders = Settings.get<{id: string, value: string}[]>(SETTING_CONTENT_PLACEHOLDERS);
const placeholders = Settings.get<CustomPlaceholder[]>(SETTING_CONTENT_PLACEHOLDERS);
if (placeholders && placeholders.length > 0) {
for (const placeholder of placeholders) {
if (value.includes(`{{${placeholder.id}}}`)) {
const regex = new RegExp(`{{${placeholder.id}}}`, "g");
const updatedValue = processKnownPlaceholders(placeholder.value, title, dateFormat);
value = value.replace(regex, updatedValue);
try {
let placeHolderValue = placeholder.value || "";
if (placeholder.script) {
const wsFolder = Folders.getWorkspaceFolder();
const script = { title: placeholder.id, script: placeholder.script, command: placeholder.command };
let output: string | any = await CustomScript.executeScript(script, wsFolder?.fsPath || "", `'${wsFolder?.fsPath}' '${filePath}' '${title}'`);
if (output) {
// Check if the output needs to be parsed
if (output.includes("{") && output.includes("}")) {
try {
output = jsoncParser.parse(output);
} catch (e) {
// Do nothing
}
} else {
output = output.split("\n");
}
placeHolderValue = output;
}
}
const regex = new RegExp(`{{${placeholder.id}}}`, "g");
const updatedValue = processKnownPlaceholders(placeHolderValue, title, dateFormat);
if (value === `{{${placeholder.id}}}`) {
value = updatedValue;
} else {
value = value.replace(regex, updatedValue);
}
} catch (e) {
Notifications.error(`Error while processing the ${placeholder.id} placeholder`);
Logger.error((e as Error).message);
value = "<Failed to process>";
}
}
}
}
+72 -42
View File
@@ -1,9 +1,9 @@
import { ModeListener } from './../listeners/general/ModeListener';
import { PagesListener } from './../listeners/dashboard';
import { ArticleHelper, Settings } from ".";
import { ArticleHelper, CustomScript, Settings } from ".";
import { FEATURE_FLAG, SETTING_CONTENT_DRAFT_FIELD, SETTING_DATE_FORMAT, SETTING_FRAMEWORK_ID, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_TAXONOMY_FIELD_GROUPS, TelemetryEvent } from "../constants";
import { ContentType as IContentType, DraftField, Field, FieldGroup, FieldType } from '../models';
import { Uri, commands, window } from 'vscode';
import { ContentType as IContentType, DraftField, Field, FieldGroup, FieldType, ScriptType } from '../models';
import { Uri, commands, window, ProgressLocation, workspace } from 'vscode';
import { Folders } from "../commands/Folders";
import { Questions } from "./Questions";
import { existsSync, writeFileSync } from "fs";
@@ -499,53 +499,72 @@ export class ContentType {
* @returns
*/
private static async create(contentType: IContentType, folderPath: string) {
const titleValue = await Questions.ContentTitle();
if (!titleValue) {
return;
}
window.withProgress({
location: ProgressLocation.Notification,
title: "Front Matter: Creating content...",
cancellable: false
}, async () => {
const titleValue = await Questions.ContentTitle();
if (!titleValue) {
return;
}
let templatePath = contentType.template;
let templateData: ParsedFrontMatter | null = null;
if (templatePath) {
templatePath = Folders.getAbsFilePath(templatePath);
templateData = ArticleHelper.getFrontMatterByPath(templatePath);
}
let templatePath = contentType.template;
let templateData: ParsedFrontMatter | null = null;
if (templatePath) {
templatePath = Folders.getAbsFilePath(templatePath);
templateData = ArticleHelper.getFrontMatterByPath(templatePath);
}
let newFilePath: string | undefined = ArticleHelper.createContent(contentType, folderPath, titleValue);
if (!newFilePath) {
return;
}
let newFilePath: string | undefined = ArticleHelper.createContent(contentType, folderPath, titleValue);
if (!newFilePath) {
return;
}
if (contentType.name === "default") {
const crntFramework = Settings.get<string>(SETTING_FRAMEWORK_ID);
if (crntFramework?.toLowerCase() === "jekyll") {
const idx = contentType.fields.findIndex(f => f.name === "draft");
if (idx > -1) {
contentType.fields.splice(idx, 1);
if (contentType.name === "default") {
const crntFramework = Settings.get<string>(SETTING_FRAMEWORK_ID);
if (crntFramework?.toLowerCase() === "jekyll") {
const idx = contentType.fields.findIndex(f => f.name === "draft");
if (idx > -1) {
contentType.fields.splice(idx, 1);
}
}
}
}
let data: any = this.processFields(contentType, titleValue, templateData?.data || {});
let data: any = await this.processFields(contentType, titleValue, templateData?.data || {}, newFilePath);
data = ArticleHelper.updateDates(Object.assign({}, data));
data = ArticleHelper.updateDates(Object.assign({}, data));
if (contentType.name !== DEFAULT_CONTENT_TYPE_NAME) {
data['type'] = contentType.name;
}
if (contentType.name !== DEFAULT_CONTENT_TYPE_NAME) {
data['type'] = contentType.name;
}
const content = ArticleHelper.stringifyFrontMatter(templateData?.content || ``, data);
const content = ArticleHelper.stringifyFrontMatter(templateData?.content || ``, data);
writeFileSync(newFilePath, content, { encoding: "utf8" });
writeFileSync(newFilePath, content, { encoding: "utf8" });
await commands.executeCommand('vscode.open', Uri.file(newFilePath));
// Check if the content type has a post script to execute
if (contentType.postScript) {
const scripts = await CustomScript.getScripts();
const script = scripts.find(s => s.id === contentType.postScript);
if (script && (script.type === ScriptType.Content || !script?.type)) {
await CustomScript.run(script, newFilePath);
Notifications.info(`Your new content has been created.`);
const doc = await workspace.openTextDocument(Uri.file(newFilePath));
await doc.save();
}
}
Telemetry.send(TelemetryEvent.createContentFromContentType);
await commands.executeCommand('vscode.open', Uri.file(newFilePath));
// Trigger a refresh for the dashboard
PagesListener.refresh();
Notifications.info(`Your new content has been created.`);
Telemetry.send(TelemetryEvent.createContentFromContentType);
// Trigger a refresh for the dashboard
PagesListener.refresh();
})
}
/**
@@ -553,29 +572,40 @@ export class ContentType {
* @param contentType
* @param data
*/
private static processFields(obj: IContentType | Field, titleValue: string, data: any) {
private static async processFields(obj: IContentType | Field, titleValue: string, data: any, filePath: string, isRoot: boolean = true): Promise<any> {
if (obj.fields) {
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
for (const field of obj.fields) {
if (field.name === "title") {
if (field.default) {
data[field.name] = processKnownPlaceholders(field.default, titleValue, dateFormat);
data[field.name] = ArticleHelper.processCustomPlaceholders(data[field.name], titleValue);
} else {
data[field.name] = await ArticleHelper.processCustomPlaceholders(data[field.name], titleValue, filePath);
} else if (isRoot) {
data[field.name] = titleValue;
} else {
data[field.name] = ""
}
} else {
if (field.type === "fields") {
data[field.name] = this.processFields(field, titleValue, {});
data[field.name] = await this.processFields(field, titleValue, {}, filePath, false);
} else {
const defaultValue = field.default;
if (typeof defaultValue === "string") {
data[field.name] = processKnownPlaceholders(defaultValue, titleValue, dateFormat);
data[field.name] = ArticleHelper.processCustomPlaceholders(data[field.name], titleValue);
data[field.name] = await ArticleHelper.processCustomPlaceholders(data[field.name], titleValue, filePath);
} else if (typeof defaultValue !== "undefined") {
data[field.name] = defaultValue;
} else {
data[field.name] = typeof defaultValue !== "undefined" ? defaultValue : "";
const draftField = ContentType.getDraftField();
if (field.type === "draft" && (draftField?.type === "boolean" || draftField?.type === undefined)) {
data[field.name] = true;
} else {
data[field.name] = "";
}
}
}
}
+33 -12
View File
@@ -1,3 +1,4 @@
import { Settings } from './SettingsHelper';
import { CommandType } from './../models/PanelSettings';
import { CustomScript as ICustomScript, ScriptType } from '../models/PanelSettings';
import { window, env as vscodeEnv, ProgressLocation } from 'vscode';
@@ -12,9 +13,24 @@ import { Dashboard } from '../commands/Dashboard';
import { DashboardCommand } from '../dashboardWebView/DashboardCommand';
import { ParsedFrontMatter } from '../parsers';
import { TelemetryEvent } from '../constants/TelemetryEvent';
import { SETTING_CUSTOM_SCRIPTS } from '../constants';
export class CustomScript {
/**
* Retrieve all scripts
* @returns
*/
public static async getScripts(): Promise<ICustomScript[]> {
const scripts = Settings.get<ICustomScript[]>(SETTING_CUSTOM_SCRIPTS) || [];
return scripts;
}
/**
* Run a script
* @param script
* @param path
*/
public static async run(script: ICustomScript, path: string | null = null): Promise<void> {
const wsFolder = Folders.getWorkspaceFolder();
@@ -24,19 +40,19 @@ export class CustomScript {
if (script.type === ScriptType.MediaFile || script.type === ScriptType.MediaFolder) {
Telemetry.send(TelemetryEvent.runMediaScript);
CustomScript.runMediaScript(wsPath, path, script);
await CustomScript.runMediaScript(wsPath, path, script);
} else {
Telemetry.send(TelemetryEvent.runCustomScript);
if (script.bulk) {
// Run script on all files
CustomScript.bulkRun(wsPath, script);
await CustomScript.bulkRun(wsPath, script);
} else if (path) {
// Run script for provided path
CustomScript.singleRun(wsPath, script, path);
await CustomScript.singleRun(wsPath, script, path);
} else {
// Run script on current file.
CustomScript.singleRun(wsPath, script);
await CustomScript.singleRun(wsPath, script);
}
}
}
@@ -64,13 +80,13 @@ export class CustomScript {
}
if (articlePath && article) {
window.withProgress({
return window.withProgress({
location: ProgressLocation.Notification,
title: `Executing: ${script.title}`,
cancellable: false
}, async () => {
const output = await CustomScript.runScript(wsPath, article, articlePath as string, script);
CustomScript.showOutput(output, script, articlePath);
await CustomScript.showOutput(output, script, articlePath);
});
} else {
Notifications.warning(`${script.title}: Article couldn't be retrieved.`);
@@ -116,7 +132,7 @@ export class CustomScript {
}
}
CustomScript.showOutput(output.join(`\n`), script);
await CustomScript.showOutput(output.join(`\n`), script);
});
}
@@ -142,7 +158,7 @@ export class CustomScript {
try {
const output = await CustomScript.executeScript(script, wsPath, `"${wsPath}" "${path}"`);
CustomScript.showOutput(output, script);
await CustomScript.showOutput(output, script);
Dashboard.postWebviewMessage({
command: DashboardCommand.mediaUpdate
@@ -188,7 +204,7 @@ export class CustomScript {
* @param output
* @param script
*/
private static showOutput(output: string | null, script: ICustomScript, articlePath?: string | null): void {
private static async showOutput(output: string | null, script: ICustomScript, articlePath?: string | null): Promise<void> {
if (output) {
try {
const data = JSON.parse(output);
@@ -212,9 +228,9 @@ export class CustomScript {
}
if (articlePath) {
ArticleHelper.updateByPath(articlePath, article);
await ArticleHelper.updateByPath(articlePath, article);
} else if (editor) {
ArticleHelper.update(editor, article);
await ArticleHelper.update(editor, article);
} else {
throw new Error(`Couldn't update article.`);
}
@@ -246,7 +262,7 @@ export class CustomScript {
* @param args
* @returns
*/
private static async executeScript(script: ICustomScript, wsPath: string, args: string): Promise<string> {
public static async executeScript(script: ICustomScript, wsPath: string, args: string): Promise<string> {
return new Promise((resolve, reject) => {
// Check the command to use
@@ -264,6 +280,11 @@ export class CustomScript {
reject(error.message);
}
if (stdout && stdout.endsWith(`\n`)) {
// Remove empty line at the end of the string
stdout = stdout.slice(0, -1);
}
resolve(stdout);
});
});
+7 -1
View File
@@ -1,8 +1,9 @@
import { GitListener } from './../listeners/general/GitListener';
import { basename, join } from "path";
import { workspace } from "vscode";
import { Folders } from "../commands/Folders";
import { Project } from "../commands/Project";
import { CONTEXT, ExtensionState, SETTING_CONTENT_DRAFT_FIELD, SETTING_CONTENT_SORTING, SETTING_CONTENT_SORTING_DEFAULT, SETTING_DASHBOARD_OPENONSTART, SETTING_DATA_FILES, SETTING_DATA_FOLDERS, SETTING_DATA_TYPES, SETTING_FRAMEWORK_ID, SETTING_MEDIA_SORTING_DEFAULT, SETTING_CUSTOM_SCRIPTS, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_CONTENT_SNIPPETS, SETTING_DATE_FORMAT, SETTING_DASHBOARD_CONTENT_TAGS, SETTING_MEDIA_SUPPORTED_MIMETYPES, SETTING_TAXONOMY_CUSTOM, SETTING_TEMPLATES_ENABLED } from "../constants";
import { CONTEXT, ExtensionState, SETTING_CONTENT_DRAFT_FIELD, SETTING_CONTENT_SORTING, SETTING_CONTENT_SORTING_DEFAULT, SETTING_DASHBOARD_OPENONSTART, SETTING_DATA_FILES, SETTING_DATA_FOLDERS, SETTING_DATA_TYPES, SETTING_FRAMEWORK_ID, SETTING_MEDIA_SORTING_DEFAULT, SETTING_CUSTOM_SCRIPTS, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_CONTENT_SNIPPETS, SETTING_DATE_FORMAT, SETTING_DASHBOARD_CONTENT_TAGS, SETTING_MEDIA_SUPPORTED_MIMETYPES, SETTING_TAXONOMY_CUSTOM, SETTING_TEMPLATES_ENABLED, SETTING_GIT_ENABLED } from "../constants";
import { DashboardViewType, SortingOption, Settings as ISettings } from "../dashboardWebView/models";
import { CustomScript, DraftField, Snippets, SortingSetting, TaxonomyType } from "../models";
import { DataFile } from "../models/DataFile";
@@ -19,8 +20,13 @@ export class DashboardSettings {
const ext = Extension.getInstance();
const wsFolder = Folders.getWorkspaceFolder();
const isInitialized = Project.isInitialized();
const gitActions = Settings.get<boolean>(SETTING_GIT_ENABLED);
return {
git: {
isGitRepo: gitActions ? await GitListener.isGitRepository() : false,
actions: gitActions || false
},
beta: ext.isBetaVersion(),
wsFolder: wsFolder ? wsFolder.fsPath : '',
staticFolder: Folders.getStaticFolderRelativePath(),
+9
View File
@@ -0,0 +1,9 @@
export const debounceCallback = () => {
let timeout: NodeJS.Timeout;
return (fnc: any, time: number) => {
const functionCall = (...args: any[]) => fnc.apply(args);
clearTimeout(timeout);
timeout = setTimeout(functionCall, time) as any;
};
};
+14
View File
@@ -104,10 +104,20 @@ export class Extension {
return this.ctx.extensionMode === ExtensionMode.Production;
}
/**
* Get the diagnostic collection for the extension
*/
public get diagnosticCollection(): DiagnosticCollection {
return this._collection;
}
/**
* Get extension subscriptions
*/
public get subscriptions() {
return this.ctx.subscriptions;
}
/**
* Set the current version information for the extension
*/
@@ -260,6 +270,10 @@ export class Extension {
return true;
}
public asAbsolutePath(path: string) {
return this.ctx.asAbsolutePath(path);
}
public get packageJson() {
const frontMatter = extensions.getExtension(this.isBetaVersion() ? EXTENSION_BETA_ID : EXTENSION_ID)!;
return frontMatter.packageJSON;
+2 -1
View File
@@ -1,3 +1,4 @@
import * as jsoncParser from 'jsonc-parser';
import { existsSync, readFileSync } from "fs";
import jsyaml = require("js-yaml");
import { join, resolve } from "path";
@@ -29,7 +30,7 @@ export class FrameworkDetector {
if (existsSync(pkgFile)) {
let packageJson: any = readFileSync(pkgFile, "utf8");
if (packageJson) {
packageJson = typeof packageJson === "string" ? JSON.parse(packageJson) : packageJson;
packageJson = typeof packageJson === "string" ? jsoncParser.parse(packageJson) : packageJson;
dependencies = packageJson.dependencies || null;
devDependencies = packageJson.devDependencies || null;
+9 -2
View File
@@ -3,13 +3,20 @@ import { Extension, Settings } from "."
import { Dashboard } from "../commands/Dashboard"
import { Preview } from "../commands/Preview"
import { Template } from "../commands/Template"
import { CONTEXT, DefaultFields, SETTING_CONTENT_DRAFT_FIELD, SETTING_CONTENT_FRONTMATTER_HIGHLIGHT, SETTING_DATA_TYPES, SETTING_FRAMEWORK_ID, SETTING_FRAMEWORK_START, SETTING_AUTO_UPDATE_DATE, SETTING_COMMA_SEPARATED_FIELDS, SETTING_CUSTOM_SCRIPTS, SETTING_DATE_FORMAT, SETTING_PANEL_FREEFORM, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_SLUG_LENGTH, SETTING_SEO_TITLE_LENGTH, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_TAXONOMY_CUSTOM, SETTING_TAXONOMY_FIELD_GROUPS, SETTING_TAXONOMY_TAGS } from "../constants"
import { CONTEXT, DefaultFields, SETTING_CONTENT_DRAFT_FIELD, SETTING_CONTENT_FRONTMATTER_HIGHLIGHT, SETTING_DATA_TYPES, SETTING_FRAMEWORK_ID, SETTING_FRAMEWORK_START, SETTING_AUTO_UPDATE_DATE, SETTING_COMMA_SEPARATED_FIELDS, SETTING_CUSTOM_SCRIPTS, SETTING_DATE_FORMAT, SETTING_PANEL_FREEFORM, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_SLUG_LENGTH, SETTING_SEO_TITLE_LENGTH, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_TAXONOMY_CUSTOM, SETTING_TAXONOMY_FIELD_GROUPS, SETTING_TAXONOMY_TAGS, SETTING_GIT_ENABLED } from "../constants"
import { GitListener } from "../listeners/general"
import { CustomScript, DataType, DraftField, FieldGroup, PanelSettings as IPanelSettings, ScriptType } from "../models"
export class PanelSettings {
public static async get(): Promise<IPanelSettings> {
const gitActions = Settings.get<boolean>(SETTING_GIT_ENABLED);
return {
git: {
isGitRepo: gitActions ? await GitListener.isGitRepository() : false,
actions: gitActions || false
},
seo: {
title: Settings.get(SETTING_SEO_TITLE_LENGTH) as number || -1,
slug: Settings.get(SETTING_SEO_SLUG_LENGTH) as number || -1,
@@ -29,7 +36,7 @@ export class PanelSettings {
categories: Settings.get(SETTING_TAXONOMY_CATEGORIES, true) || [],
customTaxonomy: Settings.get(SETTING_TAXONOMY_CUSTOM, true) || [],
freeform: Settings.get(SETTING_PANEL_FREEFORM),
scripts: (Settings.get<CustomScript[]>(SETTING_CUSTOM_SCRIPTS) || []).filter(s => s.type === ScriptType.Content || !s.type),
scripts: (Settings.get<CustomScript[]>(SETTING_CUSTOM_SCRIPTS) || []).filter(s => (s.type === ScriptType.Content || !s.type) && !s.hidden),
isInitialized: await Template.isInitialized(),
modifiedDateUpdate: Settings.get(SETTING_AUTO_UPDATE_DATE) || false,
writingSettingsEnabled: this.isWritingSettingsEnabled() || false,
+103 -27
View File
@@ -1,37 +1,42 @@
import { parseWinPath } from './parseWinPath';
import { Telemetry } from './Telemetry';
import { Notifications } from './Notifications';
import { commands, Uri, workspace, window } from 'vscode';
import * as vscode from 'vscode';
import { ContentType, CustomTaxonomy, TaxonomyType } from '../models';
import { SETTING_TAXONOMY_TAGS, SETTING_TAXONOMY_CATEGORIES, CONFIG_KEY, CONTEXT, ExtensionState, SETTING_TAXONOMY_CUSTOM, TelemetryEvent } from '../constants';
import { SETTING_TAXONOMY_TAGS, SETTING_TAXONOMY_CATEGORIES, CONFIG_KEY, CONTEXT, ExtensionState, SETTING_TAXONOMY_CUSTOM, TelemetryEvent, COMMAND_NAME } from '../constants';
import { Folders } from '../commands/Folders';
import { join, basename } from 'path';
import { existsSync, readFileSync, watch, writeFileSync } from 'fs';
import { Extension } from './Extension';
import { debounceCallback } from './DebounceCallback';
import { Logger } from './Logger';
import * as jsoncParser from 'jsonc-parser';
export class Settings {
public static globalFile = "frontmatter.json";
private static config: vscode.WorkspaceConfiguration;
private static globalConfig: any;
private static isInitialized: boolean = false;
private static listeners: any[] = [];
private static fileCreationWatcher: vscode.FileSystemWatcher | undefined;
public static init() {
const fmConfig = Settings.projectConfigPath;
if (fmConfig && existsSync(fmConfig)) {
const localConfig = readFileSync(fmConfig, 'utf8');
Settings.globalConfig = JSON.parse(localConfig);
commands.executeCommand('setContext', CONTEXT.isEnabled, true);
} else {
Settings.globalConfig = undefined;
Settings.readConfig();
Settings.listeners = [];
if (!Settings.isInitialized) {
Settings.isInitialized = true;
commands.registerCommand(COMMAND_NAME.reloadConfig, Settings.rebindWatchers)
}
Settings.config = vscode.workspace.getConfiguration(CONFIG_KEY);
Settings.onConfigChange((global?: any) => {
if (global) {
Settings.globalConfig = Object.assign({}, global);
} else {
Settings.config = vscode.workspace.getConfiguration(CONFIG_KEY);
}
Settings.readConfig();
Settings.config = vscode.workspace.getConfiguration(CONFIG_KEY);
});
}
@@ -61,15 +66,28 @@ export class Settings {
*/
public static onConfigChange(callback: (global?: any) => void) {
const projectConfig = Settings.projectConfigPath;
const configDebouncer = debounceCallback();
workspace.onDidChangeConfiguration(() => {
callback();
});
// Keep track of the listeners
Settings.listeners.push(callback);
if (projectConfig && !existsSync(projectConfig)) {
// No config file, no need to watch
Settings.createFileCreationWatcher();
return;
}
// Background listener for when it is not a user interaction
if (projectConfig && existsSync(projectConfig)) {
watch(projectConfig, () => {
callback();
let watcher = workspace.createFileSystemWatcher(projectConfig, true, false, true);
watcher.onDidChange(async (uri: Uri) => {
Logger.info(`Config change detected - ${projectConfig} changed`);
configDebouncer(() => callback(), 200);
// callback()
});
}
@@ -77,11 +95,14 @@ export class Settings {
const filename = e.uri.fsPath;
if (Settings.checkProjectConfig(filename)) {
Logger.info(`Config change detected - ${projectConfig} saved`);
const file = await workspace.openTextDocument(e.uri);
if (file) {
const fileContents = file.getText();
const json = JSON.parse(fileContents);
callback(json);
const json = jsoncParser.parse(fileContents);
configDebouncer(() => callback(json), 200);
// callback(json)
}
}
});
@@ -113,7 +134,11 @@ export class Settings {
/**
* Retrieve a setting from global and local config
*/
public static get<T>(name: string, merging: boolean = false): T | undefined{
public static get<T>(name: string, merging: boolean = false): T | undefined {
if (!Settings.config) {
return;
}
const configInpection = Settings.config.inspect<T>(name);
let setting = undefined;
@@ -150,7 +175,7 @@ export class Settings {
if (updateGlobal) {
if (fmConfig && existsSync(fmConfig)) {
const localConfig = readFileSync(fmConfig, 'utf8');
Settings.globalConfig = JSON.parse(localConfig);
Settings.globalConfig = jsoncParser.parse(localConfig);
Settings.globalConfig[`${CONFIG_KEY}.${name}`] = value;
writeFileSync(fmConfig, JSON.stringify(Settings.globalConfig, null, 2), 'utf8');
@@ -346,6 +371,19 @@ export class Settings {
return hasSetting;
}
/**
* Get the project config path
* @returns
*/
public static get projectConfigPath() {
const wsFolder = Folders.getWorkspaceFolder();
if (wsFolder) {
const fmConfig = join(wsFolder.fsPath, Settings.globalFile);
return fmConfig;
}
return undefined;
}
/**
* Check if its the project config
* @param filePath
@@ -363,15 +401,53 @@ export class Settings {
}
/**
* Get the project config path
* @returns
* Read the global config file
*/
public static get projectConfigPath() {
const wsFolder = Folders.getWorkspaceFolder();
if (wsFolder) {
const fmConfig = join(wsFolder.fsPath, Settings.globalFile);
return fmConfig;
private static readConfig() {
try {
const fmConfig = Settings.projectConfigPath;
if (fmConfig && existsSync(fmConfig)) {
const localConfig = readFileSync(fmConfig, 'utf8');
Settings.globalConfig = jsoncParser.parse(localConfig);
commands.executeCommand('setContext', CONTEXT.isEnabled, true);
} else {
Settings.globalConfig = undefined;
}
} catch (e) {
Settings.globalConfig = undefined;
Notifications.error(`Error reading "frontmatter.json" config file. Check [output window](command:${COMMAND_NAME.showOutputChannel}) for more details.`);
Logger.error((e as Error).message);
}
return undefined;
}
/**
* Create a file creation watcher
*/
private static createFileCreationWatcher() {
const ext = Extension.getInstance();
if (!Settings.fileCreationWatcher) {
Settings.fileCreationWatcher = workspace.createFileSystemWatcher(`**/*.json`, false, true, true);
Settings.fileCreationWatcher.onDidCreate(uri => {
if (parseWinPath(uri.fsPath) === parseWinPath(Settings.projectConfigPath)) {
Settings.rebindWatchers();
// Stop listening to file creation events
Settings.fileCreationWatcher?.dispose();
Settings.fileCreationWatcher = undefined;
}
}, null, ext.subscriptions);
}
}
/**
* Rebind the configuration watchers
*/
private static rebindWatchers() {
Logger.info(`Rebinding ${this.listeners.length} listeners`);
this.listeners.forEach(l => {
Settings.onConfigChange(l);
l();
});
}
}
+1 -1
View File
@@ -116,7 +116,7 @@ export class TaxonomyHelper {
const options = this.getTaxonomyOptions(taxonomyType);
const newOption = await window.showInputBox({
title: `Create a new ${taxonomyType} value`,
title: `Create a new ${type} value`,
placeHolder: `The value you want to add`,
ignoreFocusOut: true,
validateInput: (text) => {
+1
View File
@@ -4,6 +4,7 @@ export * from './CustomScript';
export * from './DashboardSettings';
export * from './DataFileHelper';
export * from './DateHelper';
export * from './DebounceCallback';
export * from './Extension';
export * from './FilesHelper';
export * from './FrameworkDetector';
+140
View File
@@ -0,0 +1,140 @@
import { Settings } from './../../helpers/SettingsHelper';
import { Dashboard } from '../../commands/Dashboard';
import { ExplorerView } from '../../explorerView/ExplorerView';
import { Extension, Logger, Telemetry } from '../../helpers';
import { GeneralCommands } from './../../constants/GeneralCommands';
import simpleGit, { SimpleGit } from 'simple-git';
import { COMMAND_NAME, CONTEXT, SETTING_GIT_COMMIT_MSG, SETTING_GIT_ENABLED, TelemetryEvent } from '../../constants';
import { Folders } from '../../commands/Folders';
import { commands } from 'vscode';
export class GitListener {
private static isRegistered: boolean = false;
private static client: SimpleGit | null = null;
public static async init() {
let isEnabled = false;
const gitEnabled = Settings.get<boolean>(SETTING_GIT_ENABLED);
if (gitEnabled) {
const isRepo = await GitListener.isGitRepository();
if (isRepo) {
isEnabled = true;
}
}
await commands.executeCommand('setContext', CONTEXT.isGitEnabled, isEnabled);
if (!this.isRegistered) {
const ext = Extension.getInstance();
ext.subscriptions.push(
commands.registerCommand(COMMAND_NAME.gitSync, GitListener.sync)
);
this.isRegistered = true;
}
}
/**
* Process the messages
* @param msg
*/
public static process(msg: { command: string, data: any }) {
switch(msg.command) {
case GeneralCommands.toVSCode.gitSync:
this.sync();
break;
}
}
public static async sync() {
try {
this.sendMsg(GeneralCommands.toWebview.gitSyncingStart, {});
Telemetry.send(TelemetryEvent.gitSync);
await this.pull();
await this.push();
this.sendMsg(GeneralCommands.toWebview.gitSyncingEnd, {});
} catch (e) {
Logger.error((e as Error).message);
this.sendMsg(GeneralCommands.toWebview.gitSyncingEnd, {});
}
}
public static async isGitRepository() {
const git = this.getClient();
if (!git) {
return false;
}
const isRepo = await git.checkIsRepo();
if (!isRepo) {
Logger.warning(`Current workspace is not a GIT repository`);
}
return isRepo;
}
private static async pull() {
const git = this.getClient();
if (!git) {
return;
}
Logger.info(`Pulling from remote`);
await git.pull();
}
private static async push() {
const commitMsg = Settings.get<string>(SETTING_GIT_COMMIT_MSG);
const git = this.getClient();
if (!git) {
return;
}
Logger.info(`Pushing to remote`);
const status = await git.status();
for (const file of status.not_added) {
await git.add(file);
}
for (const file of status.modified) {
await git.add(file);
}
await git.commit(commitMsg || "Synced by Front Matter")
await git.push();
}
private static getClient() {
if (this.client) {
return this.client;
}
const wsFolder = Folders.getWorkspaceFolder();
const options = {
baseDir: wsFolder?.fsPath || "",
binary: 'git',
maxConcurrentProcesses: 6,
}
this.client = simpleGit(options);
return this.client;
}
private static sendMsg(command: string, data: any) {
const extPath = Extension.getInstance().extensionPath;
const panel = ExplorerView.getInstance(extPath);
panel.sendMessage({ command: command as any, data });
Dashboard.postWebviewMessage({ command: command as any, data });
}
}
+1
View File
@@ -1 +1,2 @@
export * from './ModeListener';
export * from './GitListener';
+45 -9
View File
@@ -16,6 +16,7 @@ const FILE_LIMIT = 10;
export class DataListener extends BaseListener {
private static lastMetadataUpdate: any = {};
private static readonly terminalName: string = 'Local server';
/**
* Process the messages for the dashboard views
@@ -41,6 +42,9 @@ export class DataListener extends BaseListener {
case CommandToCode.frameworkCommand:
this.openTerminalWithCommand(msg.data.command);
break;
case CommandToCode.stopServer:
this.stopServer();
break;
case CommandToCode.updatePlaceholder:
this.updatePlaceholder(msg?.data?.field, msg?.data?.value, msg?.data?.title);
break;
@@ -305,28 +309,60 @@ export class DataListener extends BaseListener {
*/
private static openTerminalWithCommand(command: string) {
if (command) {
let terminal = window.activeTerminal;
let localServerTerminal = DataListener.findServerTerminal();
if (localServerTerminal) {
localServerTerminal.dispose();
}
if (!terminal || (terminal && terminal.state.isInteractedWith === true)) {
terminal = window.createTerminal({
name: `Starting local server`,
if (!localServerTerminal || (localServerTerminal && localServerTerminal.state.isInteractedWith === true)) {
localServerTerminal = window.createTerminal({
name: this.terminalName,
iconPath: new ThemeIcon('server-environment'),
message: `Starting local server`,
});
}
if (terminal) {
terminal.sendText(command);
terminal.show(false);
if (localServerTerminal) {
localServerTerminal.sendText(command);
localServerTerminal.show(false);
}
}
}
private static updatePlaceholder(field: string, value: string, title: string) {
/**
* Stop the local server
*/
private static stopServer() {
const localServerTerminal = DataListener.findServerTerminal();
if (localServerTerminal) {
localServerTerminal.dispose();
}
}
/**
* Find the server terminal
* @returns
*/
private static findServerTerminal() {
let terminals = window.terminals;
if (terminals) {
const localServerTerminal = terminals.find(t => t.name === DataListener.terminalName);
return localServerTerminal;
}
}
/**
* Update the placeholder
* @param field
* @param value
* @param title
*/
private static async updatePlaceholder(field: string, value: string, title: string) {
if (field && value) {
const crntFile = window.activeTextEditor?.document;
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
value = processKnownPlaceholders(value, title || "", dateFormat);
value = ArticleHelper.processCustomPlaceholders(value, title || "");
value = await ArticleHelper.processCustomPlaceholders(value, title || "", crntFile?.uri.fsPath || "");
}
this.sendMsg(Command.updatePlaceholder, { field, value });
+6
View File
@@ -0,0 +1,6 @@
export interface CustomPlaceholder {
id: string;
value?: string;
script?: string;
command?: string
}
+5
View File
@@ -0,0 +1,5 @@
export interface GitSettings {
isGitRepo: boolean;
actions: boolean;
}
+7 -3
View File
@@ -1,10 +1,11 @@
import { FileStat } from "vscode";
import { DraftField } from ".";
import { DraftField, GitSettings } from ".";
import { Choice } from "./Choice";
import { DashboardData } from "./DashboardData";
import { DataType } from "./DataType";
export interface PanelSettings {
git: GitSettings;
seo: SEO;
slug: Slug;
tags: string[];
@@ -47,6 +48,7 @@ export interface ContentType {
previewPath?: string | null;
pageBundle?: boolean;
template?: string;
postScript?: string;
}
export type FieldType = "string" | "number" | "datetime" | "boolean" | "image" | "choice" | "tags" | "categories" | "draft" | "taxonomy" | "fields" | "json" | "block" | "file" | "dataFile" | "list" | "slug";
@@ -111,6 +113,7 @@ export interface FileInfo extends FileStat {
};
export interface CustomScript {
id?: string;
title: string;
script: string;
nodeBin?: string;
@@ -118,7 +121,8 @@ export interface CustomScript {
output?: "notification" | "editor";
outputType?: string;
type?: ScriptType;
command?: CommandType;
command?: CommandType | string;
hidden?: boolean;
}
export interface PreviewSettings {
@@ -134,7 +138,7 @@ export interface CustomTaxonomy {
export enum ScriptType {
Content = "content",
MediaFolder = "mediaFolder",
MediaFile = "mediaFile"
MediaFile = "mediaFile",
}
export enum CommandType {
+2
View File
@@ -1,6 +1,7 @@
export * from './BlockFieldData';
export * from './Choice';
export * from './ContentFolder';
export * from './CustomPlaceholder';
export * from './CustomTaxonomyData';
export * from './DashboardData';
export * from './DataFile';
@@ -8,6 +9,7 @@ export * from './DataFolder';
export * from './DataType';
export * from './DraftField';
export * from './Framework';
export * from './GitSettings';
export * from './MediaPaths';
export * from './Mode';
export * from './PanelSettings';
+1
View File
@@ -39,4 +39,5 @@ export enum CommandToCode {
setContentType = "set-content-type",
getDataEntries = "get-data-entries",
generateSlug = "generate-slug",
stopServer = "stop-server",
}
+3
View File
@@ -11,6 +11,7 @@ import { SponsorMsg } from './components/SponsorMsg';
import useMessages from './hooks/useMessages';
import { FeatureFlag } from '../components/features/FeatureFlag';
import { FEATURE_FLAG } from '../constants/Features';
import { GitAction } from './components/Git/GitAction';
export interface IViewPanelProps {
}
@@ -41,6 +42,8 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (props: React
return (
<div className="frontmatter">
<div className={`ext_actions`}>
<GitAction settings={settings} />
<FeatureFlag features={mode?.features || []} flag={FEATURE_FLAG.panel.globalSettings}>
<GlobalSettings settings={settings} />
</FeatureFlag>
+4 -2
View File
@@ -1,7 +1,7 @@
import * as React from 'react';
export interface IActionButtonProps {
title: string;
title: JSX.Element | string;
className?: string;
disabled?: boolean;
onClick: (e: React.SyntheticEvent<HTMLButtonElement>) => void;
@@ -10,7 +10,9 @@ export interface IActionButtonProps {
const ActionButton: React.FunctionComponent<IActionButtonProps> = ({className, onClick, disabled,title}: React.PropsWithChildren<IActionButtonProps>) => {
return (
<div className={`article__action`}>
<button onClick={onClick} className={className || ""} disabled={disabled}>{title}</button>
<button onClick={onClick} className={className || ""} disabled={disabled}>
{title}
</button>
</div>
);
};
+6 -1
View File
@@ -10,6 +10,7 @@ import { StartServerButton } from './StartServerButton';
import { FeatureFlag } from '../../components/features/FeatureFlag';
import { FEATURE_FLAG } from '../../constants/Features';
import { Messenger } from '@estruyf/vscode/dist/client';
import { GitAction } from './Git/GitAction';
export interface IBaseViewProps {
settings: PanelSettings | undefined;
@@ -44,6 +45,8 @@ const BaseView: React.FunctionComponent<IBaseViewProps> = ({settings, folderAndF
return (
<div className="frontmatter">
<div className={`ext_actions`}>
<GitAction settings={settings} />
<FeatureFlag features={mode?.features || []} flag={FEATURE_FLAG.panel.globalSettings}>
<GlobalSettings settings={settings} isBase />
</FeatureFlag>
@@ -53,7 +56,9 @@ const BaseView: React.FunctionComponent<IBaseViewProps> = ({settings, folderAndF
<div className={`base__actions`}>
<button onClick={openDashboard}>Open dashboard</button>
<StartServerButton settings={settings} />
<button onClick={initProject} disabled={settings?.isInitialized}>Initialize project</button>
{
!settings?.isInitialized && <button onClick={initProject}>Initialize project</button>
}
<button onClick={createContent} disabled={!settings?.isInitialized}>Create new content</button>
<button onClick={openPreview} disabled={!settings?.preview?.host}>Open site preview</button>
{
@@ -0,0 +1,55 @@
import { Messenger } from '@estruyf/vscode/dist/client';
import { EventData } from '@estruyf/vscode/dist/models';
import { RefreshIcon } from '@heroicons/react/outline';
import * as React from 'react';
import { useEffect, useState } from 'react';
import { GeneralCommands } from '../../../constants';
import { PanelSettings } from '../../../models';
import { ActionButton } from '../ActionButton';
export interface IGitActionProps {
settings: PanelSettings | undefined
}
export const GitAction: React.FunctionComponent<IGitActionProps> = ({ settings }: React.PropsWithChildren<IGitActionProps>) => {
const [ isSyncing, setIsSyncing ] = useState(false);
const pull = () => {
Messenger.send(GeneralCommands.toVSCode.gitSync);
};
const messageListener = (message: MessageEvent<EventData<any>>) => {
const { command, data } = message.data;
if (command === GeneralCommands.toWebview.gitSyncingStart) {
setIsSyncing(true);
} else if (command === GeneralCommands.toWebview.gitSyncingEnd) {
setIsSyncing(false);
}
};
useEffect(() => {
Messenger.listen(messageListener);
return () => {
Messenger.unlisten(messageListener);
}
}, []);
if (!settings?.git?.actions || !settings?.git.isGitRepo) {
return null;
}
return (
<div className='git_actions'>
<ActionButton
onClick={pull}
title={(
<div className='git_actions__sync'>
<RefreshIcon className={isSyncing ? 'animate-spin' : ''} />
<span>Sync</span>
</div>
)} />
</div>
);
};
+12 -4
View File
@@ -6,12 +6,12 @@ import { BugIcon } from './Icons/BugIcon';
import { CenterIcon } from './Icons/CenterIcon';
import { FileIcon } from './Icons/FileIcon';
import { FolderOpenedIcon } from './Icons/FolderOpenedIcon';
import { SettingsIcon } from './Icons/SettingsIcon';
import { TemplateIcon } from './Icons/TemplateIcon';
import { WritingIcon } from './Icons/WritingIcon';
import { OtherActionButton } from './OtherActionButton';
import { ISSUE_LINK } from '../../constants/Links';
import { DOCUMENTATION_LINK, DOCUMENTATION_SETTINGS_LINK, ISSUE_LINK } from '../../constants/Links';
import { Messenger } from '@estruyf/vscode/dist/client';
import { BookOpenIcon } from '@heroicons/react/outline';
export interface IOtherActionsProps {
isFile: boolean;
@@ -50,14 +50,22 @@ const OtherActions: React.FunctionComponent<IOtherActionsProps> = ({isFile, sett
<CenterIcon /> <span>Toggle center mode</span>
</OtherActionButton>
<OtherActionButton onClick={createAsTemplate} disabled={!isFile}><TemplateIcon /> <span>Create as template</span></OtherActionButton>
<OtherActionButton onClick={createAsTemplate} disabled={!isFile}><TemplateIcon /> <span>Create template</span></OtherActionButton>
<OtherActionButton onClick={openSettings}><SettingsIcon /> <span>Open settings</span></OtherActionButton>
{/* <OtherActionButton onClick={openSettings}><SettingsIcon /> <span>Open settings</span></OtherActionButton> */}
<OtherActionButton onClick={openFile} disabled={!isFile}><FileIcon /> <span>Reveal file in folder</span></OtherActionButton>
<OtherActionButton onClick={openProject}><FolderOpenedIcon /> <span>Reveal project folder</span></OtherActionButton>
<div className="ext_link_block">
<a href={DOCUMENTATION_LINK} title="Open documentation"><BookOpenIcon /> <span>Documentation</span></a>
</div>
<div className="ext_link_block">
<a href={DOCUMENTATION_SETTINGS_LINK} title="Open settings documentation"><BookOpenIcon /> <span>Settings overview</span></a>
</div>
<div className="ext_link_block">
<a href={ISSUE_LINK} title="Open an issue on GitHub"><BugIcon /> <span>Report an issue</span></a>
</div>
@@ -14,8 +14,17 @@ export const StartServerButton: React.FunctionComponent<IStartServerButtonProps>
const startLocalServer = (command: string) => {
Messenger.send(CommandToCode.frameworkCommand, { command });
};
const stopLocalServer = () => {
Messenger.send(CommandToCode.stopServer);
};
return (
startCommand ? <button onClick={() => startLocalServer(startCommand)}>Start server</button> : null
startCommand ? (
<>
<button onClick={() => startLocalServer(startCommand)}>Start server</button>
<button onClick={() => stopLocalServer()}>Stop server</button>
</>
) : null
);
};
+8 -2
View File
@@ -7,6 +7,7 @@ import { Command } from '../Command';
import { CommandToCode } from '../CommandToCode';
import { TagType } from '../TagType';
import { Messenger } from '@estruyf/vscode/dist/client';
import { EventData } from '@estruyf/vscode/dist/models';
export default function useMessages() {
const [metadata, setMetadata] = useState<any>({});
@@ -17,7 +18,7 @@ export default function useMessages() {
const [mediaSelecting, setMediaSelecting] = useState<DashboardData | undefined>(undefined);
const [mode, setMode] = useState<Mode | undefined>(undefined);
window.addEventListener('message', event => {
const messageListener = (event: MessageEvent<EventData<any>>) => {
const message = event.data;
switch (message.command) {
@@ -48,7 +49,7 @@ export default function useMessages() {
setMode(message.data);
break;
}
});
};
useEffect(() => {
if (loading) {
@@ -59,6 +60,7 @@ export default function useMessages() {
}, [loading])
useEffect(() => {
Messenger.listen(messageListener);
setLoading(true);
// Show what you got after 5 seconds
@@ -68,6 +70,10 @@ export default function useMessages() {
Messenger.send(CommandToCode.getData);
Messenger.send(CommandToCode.getMode);
return () => {
Messenger.unlisten(messageListener);
}
}, []);
return {
+27
View File
@@ -1,4 +1,14 @@
/* Animations */
@keyframes spin {
from {
transform: rotate(360deg);
}
to {
transform: rotate(0deg);
}
}
/* CSS */
.block_field__form,
.list_field__form {
background-color: transparent;
@@ -508,6 +518,23 @@ vscode-divider {
}
}
/* Git actions */
.git_actions__sync {
display: flex;
align-items: center;
justify-content: center;
}
.git_actions__sync svg {
height: 1.25rem;
width: 1.25rem;
margin-right: .5rem;
}
.animate-spin {
animation: spin 1s linear infinite;
}
/* Quill changes */
.ql-toolbar.ql-snow,
.ql-container.ql-snow {
+86 -3
View File
@@ -1,5 +1,7 @@
import { SETTING_CONTENT_HIDE_FRONTMATTER, SETTING_CONTENT_HIDE_FRONTMATTER_MESSAGE } from './../constants/settings';
import { ThemeColor } from 'vscode';
import { ArticleHelper } from '../helpers';
import { languages, TextEditorDecorationType } from 'vscode';
import { commands, DecorationOptions, languages, TextEditorDecorationType } from 'vscode';
import { CancellationToken, FoldingContext, FoldingRange, FoldingRangeKind, FoldingRangeProvider, Range, TextDocument, window, Position } from 'vscode';
import { SETTING_CONTENT_FRONTMATTER_HIGHLIGHT, SETTING_CONTENT_SUPPORTED_FILETYPES, SETTING_FRONTMATTER_TYPE } from '../constants';
import { Settings } from '../helpers';
@@ -11,6 +13,7 @@ export class MarkdownFoldingProvider implements FoldingRangeProvider {
private static end: number | null = null;
private static endLine: number | null = null;
private static decType: TextEditorDecorationType | null = null;
private static crntDecoration: TextEditorDecorationType | null = null;
public static register() {
const supportedFiles = Settings.get<string[]>(SETTING_CONTENT_SUPPORTED_FILETYPES);
@@ -37,15 +40,19 @@ export class MarkdownFoldingProvider implements FoldingRangeProvider {
return ranges;
}
public static triggerHighlighting() {
public static triggerHighlighting(configChange: boolean = false) {
const activeDoc = window.activeTextEditor?.document;
if (configChange && this.crntDecoration) {
this.resetDecoration();
}
const isSupported = ArticleHelper.isSupportedFile(activeDoc);
if (isSupported) {
const fmHighlight = Settings.get<boolean>(SETTING_CONTENT_FRONTMATTER_HIGHLIGHT);
const range = MarkdownFoldingProvider.getFrontMatterRange();
if (range) {
if (MarkdownFoldingProvider.decType !== null) {
MarkdownFoldingProvider.decType.dispose();
@@ -56,6 +63,11 @@ export class MarkdownFoldingProvider implements FoldingRangeProvider {
window.activeTextEditor?.setDecorations(MarkdownFoldingProvider.decType, [range]);
}
}
const hideFrontMatter = Settings.get<boolean>(SETTING_CONTENT_HIDE_FRONTMATTER);
if (hideFrontMatter) {
this.hideFrontMatterFromDocument(range);
}
}
}
@@ -119,4 +131,75 @@ export class MarkdownFoldingProvider implements FoldingRangeProvider {
return null;
}
/**
* Hide the front matter on the page
* @param range
* @returns
*/
private static hideFrontMatterFromDocument = async (range: Range| null) => {
const editor = window.activeTextEditor;
if (!editor) {
return;
}
const decorators: DecorationOptions[] = [];
if (range) {
decorators.push({
range: range
});
}
if (!this.crntDecoration) {
this.crntDecoration = this.getHiddenDecoration();
}
editor.setDecorations(
this.crntDecoration,
decorators
);
commands.executeCommand('editor.fold', {selectionLines: [range?.start.line],direction: "up"});
}
/**
* Resets the decoration in the document
* @returns
*/
private static resetDecoration() {
if (!this.crntDecoration) {
return;
}
const editor = window.activeTextEditor;
if (!editor) {
return;
}
editor.setDecorations(
this.crntDecoration,
[]
);
this.crntDecoration = null;
}
/**
* Retrieve the hidden decoration for the text to hide
* @returns
*/
private static getHiddenDecoration(): TextEditorDecorationType {
const contentText = Settings.get<string>(SETTING_CONTENT_HIDE_FRONTMATTER_MESSAGE);
return window.createTextEditorDecorationType({
after: {
contentText,
fontStyle: 'italic',
color: new ThemeColor('editorInfo.foreground'),
},
textDecoration: "none; display: none;"
});
}
}
+10
View File
@@ -6,6 +6,16 @@ module.exports = {
darkMode: 'class', // or 'media' or 'class'
theme: {
extend: {
animation: {
'reverse-spin': 'reverse-spin 1s linear infinite'
},
keyframes: {
'reverse-spin': {
from: {
transform: 'rotate(360deg)'
},
}
},
colors: {
white: colors.white,
gray: colors.trueGray,