mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-03-28 17:42:40 +01:00
Compare commits
80 Commits
v10.1.0
...
copilot-in
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a4e9fd8ff | ||
|
|
e77de75333 | ||
|
|
b00d7a077d | ||
|
|
582c09875e | ||
|
|
0c9b4a31ac | ||
|
|
e8fc53804c | ||
|
|
5b7486a355 | ||
|
|
d23e0833dc | ||
|
|
dacc6214b6 | ||
|
|
314cadb81e | ||
|
|
1e1c0cedb0 | ||
|
|
013324b3f0 | ||
|
|
62056753e4 | ||
|
|
6b018c0b65 | ||
|
|
ef9510d92d | ||
|
|
6d4cd4b0c2 | ||
|
|
e529f30a40 | ||
|
|
8b92a3306f | ||
|
|
5353d07fcb | ||
|
|
1f94ae165c | ||
|
|
dbd42ac1f9 | ||
|
|
1031088f85 | ||
|
|
fcbbe7f834 | ||
|
|
c58d0573c6 | ||
|
|
ba7a0225c1 | ||
|
|
65fd8b4a78 | ||
|
|
46af17eac2 | ||
|
|
2eb0b775a3 | ||
|
|
bf07f29698 | ||
|
|
a22219c1b4 | ||
|
|
ec326a74ca | ||
|
|
2246fbb933 | ||
|
|
fa6f7dcfe6 | ||
|
|
f83ed9b970 | ||
|
|
9ce70fe722 | ||
|
|
4a1b37ba88 | ||
|
|
5882853f28 | ||
|
|
0b8155a75f | ||
|
|
19a0f4b53f | ||
|
|
0bde5610c5 | ||
|
|
b90f2adb18 | ||
|
|
a70b4316f8 | ||
|
|
16453cbb21 | ||
|
|
46e90df501 | ||
|
|
d8d72980ea | ||
|
|
7a5e452602 | ||
|
|
beee186d72 | ||
|
|
64fc1e4b76 | ||
|
|
5c4a716367 | ||
|
|
31873bc2d2 | ||
|
|
0e92834517 | ||
|
|
d262518023 | ||
|
|
da2cf68f5c | ||
|
|
2e7ece44e2 | ||
|
|
c039d260dc | ||
|
|
2fc543f0dd | ||
|
|
48314b3f3f | ||
|
|
a43b581e1b | ||
|
|
1ad55cdbbb | ||
|
|
ffa70050eb | ||
|
|
e8f70c78fd | ||
|
|
504774a4c8 | ||
|
|
a764c2fea7 | ||
|
|
5f623689cc | ||
|
|
54bf408c76 | ||
|
|
03f2284dd2 | ||
|
|
f637def278 | ||
|
|
da46374fb4 | ||
|
|
dee732f3ee | ||
|
|
d3b93424d1 | ||
|
|
a467791eaf | ||
|
|
70a5de960f | ||
|
|
31e27f63c1 | ||
|
|
a50f567fbb | ||
|
|
bdafd25cfe | ||
|
|
18b7708367 | ||
|
|
3fedaf7d5f | ||
|
|
75a3fc21a3 | ||
|
|
82b894c35b | ||
|
|
60952a05ac |
14
.github/ISSUE_TEMPLATE/bug_report.md
vendored
14
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -12,6 +12,7 @@ A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
@@ -23,16 +24,11 @@ A clear and concise description of what you expected to happen.
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
**Device:**
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
- OS: [e.g. iOS]
|
||||
- Front Matter CMS Version [e.g. 10.2.0]
|
||||
- Browser [e.g. chrome, safari]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
8
.vscode/launch.json
vendored
8
.vscode/launch.json
vendored
@@ -10,7 +10,9 @@
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}", "--disable-extension=eliostruyf.vscode-front-matter"
|
||||
],
|
||||
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
|
||||
"preLaunchTask": "npm: build:ext"
|
||||
},
|
||||
@@ -19,7 +21,9 @@
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}", "--disable-extension=eliostruyf.vscode-front-matter"
|
||||
],
|
||||
"outFiles": ["${workspaceFolder}/dist/**/*.js"]
|
||||
}
|
||||
]
|
||||
|
||||
10
.vscode/settings.json
vendored
10
.vscode/settings.json
vendored
@@ -1,5 +1,15 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"commitHelper.messages": [
|
||||
{
|
||||
"type": "👨💻 apps",
|
||||
"values": ["#search", "#profile"]
|
||||
},
|
||||
{
|
||||
"type": "⚙️ tasks",
|
||||
"values": ["#build", "#deploy", "#skip"]
|
||||
}
|
||||
],
|
||||
"workbench.colorCustomizations": {
|
||||
"titleBar.activeBackground": "#15c2cb",
|
||||
"titleBar.inactiveBackground": "#44ffd299",
|
||||
|
||||
42
CHANGELOG.md
42
CHANGELOG.md
@@ -1,5 +1,47 @@
|
||||
# Change Log
|
||||
|
||||
## [10.3.0] - 2024-xx-xx
|
||||
|
||||
### ✨ New features
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#819](https://github.com/estruyf/vscode-front-matter/issues/819): Added new extensibility support for media scripts
|
||||
- [#822](https://github.com/estruyf/vscode-front-matter/issues/822): Added docs to the panel & dashboard views
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
## [10.2.0] - 2024-06-12 - [Release notes](https://beta.frontmatter.codes/updates/v10.2.0)
|
||||
|
||||
### ✨ New features
|
||||
|
||||
- [#797](https://github.com/estruyf/vscode-front-matter/issues/797): Adding common actions at the bottom of the snippet cards
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#441](https://github.com/estruyf/vscode-front-matter/issues/441): Show input descriptions for snippet and data forms
|
||||
- [#442](https://github.com/estruyf/vscode-front-matter/issues/442): Hide sidebar on data view when data file is selected + show dropdown of data files
|
||||
- [#788](https://github.com/estruyf/vscode-front-matter/issues/788): Show a warning on setting update when it exists in an extended configuration
|
||||
- [#798](https://github.com/estruyf/vscode-front-matter/issues/798): Changed dialog to slide-over for the snippet forms
|
||||
- [#799](https://github.com/estruyf/vscode-front-matter/issues/799): Added `frontMatter.logging` setting to define the logging output. Options are `info`, `warn`, `error`, and `verbose`. The default is `info`.
|
||||
- [#800](https://github.com/estruyf/vscode-front-matter/issues/800): Add colors for the Front Matter CMS output
|
||||
- [#808](https://github.com/estruyf/vscode-front-matter/issues/808): Add support to generate field groups and `block` fields in content type generation
|
||||
- [#810](https://github.com/estruyf/vscode-front-matter/issues/810): Update the tab title based on the view
|
||||
- [#811](https://github.com/estruyf/vscode-front-matter/issues/811): Added `panel.gitActions` view mode option to hide the Git actions in the panel
|
||||
- [#812](https://github.com/estruyf/vscode-front-matter/issues/812): Added the `{{locale}}` placeholder, which can be used in the `previewPath` property
|
||||
|
||||
### ⚡️ Optimizations
|
||||
|
||||
- [#802](https://github.com/estruyf/vscode-front-matter/issues/802): Update `glob` to the latest version and remove the sync method
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#796](https://github.com/estruyf/vscode-front-matter/issues/796): Fix issue in retrieving folders/files on dashboard load
|
||||
- [#801](https://github.com/estruyf/vscode-front-matter/issues/801): Faster folder processing on updates
|
||||
- [#804](https://github.com/estruyf/vscode-front-matter/issues/804): Fix blinking of the front matter content area
|
||||
- [#806](https://github.com/estruyf/vscode-front-matter/issues/804): Fix preview URL for `index.md` files in root of the page folder path
|
||||
- [#809](https://github.com/estruyf/vscode-front-matter/issues/809): Fix retrieving the `filePrefix` when updating the file name on slug change
|
||||
|
||||
## [10.1.0] - 2024-04-11 - [Release notes](https://beta.frontmatter.codes/updates/v10.1.0)
|
||||
|
||||
### ✨ New features
|
||||
|
||||
@@ -179,7 +179,7 @@ You can open showcase issues for the following things:
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## 🖤 Backers & Sponsors 👇 🤘
|
||||
## 💚 Backers & Sponsors 👇 🤘
|
||||
|
||||
<p align="center">
|
||||
<img src="https://frontmatter.codes/api/img-sponsors" alt="Front Matter sponsors" />
|
||||
@@ -187,17 +187,9 @@ You can open showcase issues for the following things:
|
||||
|
||||
<br />
|
||||
|
||||
<p align="center" title="Powered by Vercel">
|
||||
<a href="https://run.events/?utm_source=frontmatter&utm_campaign=oss">
|
||||
<img src="https://frontmatter.codes/assets/sponsors/runevents-purple.webp" alt="run.events - Event Management Platform" height="50px" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
<p align="center" title="Powered by Vercel">
|
||||
<a href="https://vercel.com/?utm_source=vscode-frontmatter&utm_campaign=oss">
|
||||
<img src="https://frontmatter.codes/assets/sponsors/powered-by-vercel.png" alt="Powered by Vercel" height="44px" />
|
||||
<p align="center" title="Powered by Netlify">
|
||||
<a href="https://www.netlify.com?utm_source=vscode-frontmatter&utm_campaign=oss">
|
||||
<img src="https://frontmatter.codes/assets/sponsors/netlify-dark.png" alt="Deploys by Netlify" height="51px" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
10
README.md
10
README.md
@@ -177,7 +177,7 @@ You can open showcase issues for the following things:
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## 🖤 Backers & Sponsors 👇 🤘
|
||||
## 💚 Backers & Sponsors 👇 🤘
|
||||
|
||||
<p align="center">
|
||||
<img src="https://frontmatter.codes/api/img-sponsors" alt="Front Matter sponsors" />
|
||||
@@ -185,7 +185,7 @@ You can open showcase issues for the following things:
|
||||
|
||||
<br />
|
||||
|
||||
<p align="center" title="Powered by Vercel">
|
||||
<p align="center" title="Support by run.events">
|
||||
<a href="https://run.events/?utm_source=frontmatter&utm_campaign=oss">
|
||||
<img src="https://frontmatter.codes/assets/sponsors/runevents-purple.webp" alt="run.events - Event Management Platform" height="50px" />
|
||||
</a>
|
||||
@@ -193,9 +193,9 @@ You can open showcase issues for the following things:
|
||||
|
||||
<br />
|
||||
|
||||
<p align="center" title="Powered by Vercel">
|
||||
<a href="https://vercel.com/?utm_source=vscode-frontmatter&utm_campaign=oss">
|
||||
<img src="https://frontmatter.codes/assets/sponsors/powered-by-vercel.png" alt="Powered by Vercel" height="44px" />
|
||||
<p align="center" title="Powered by Netlify">
|
||||
<a href="https://www.netlify.com?utm_source=vscode-frontmatter&utm_campaign=oss">
|
||||
<img src="https://frontmatter.codes/assets/sponsors/netlify-dark.png" alt="Deploys by Netlify" height="51px" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -274,6 +274,8 @@
|
||||
"dashboard.preview.button.refresh.title": "更新",
|
||||
"dashboard.preview.button.open.title": "開く",
|
||||
|
||||
"dashboard.snippetsView.item.type.content": "コンテンツ用スニペット",
|
||||
"dashboard.snippetsView.item.type.media": "メディア用スニペット",
|
||||
"dashboard.snippetsView.item.quickAction.editSnippet": "スニペットを編集",
|
||||
"dashboard.snippetsView.item.quickAction.deleteSnippet": "スニペットを削除",
|
||||
"dashboard.snippetsView.item.quickAction.viewSnippet": "スニペットファイルの表示",
|
||||
@@ -725,6 +727,7 @@
|
||||
"helpers.settingsHelper.readConfig.progress.title": "{0}: 動的な設定ファイルを読み込んでいます...",
|
||||
"helpers.settingsHelper.readConfig.error": "設定の読み込みでエラーが発生しました。",
|
||||
"helpers.settingsHelper.refreshConfig.success": "設定を再読み込みしました。",
|
||||
"helpers.settingsHelper.safeUpdate.warning": "Front Matter CMSの構成が拡張または分割されているため、\"{0}\"の設定を更新できませんでした。手動で更新を加えてください。更新情報についての出力を確認してください。",
|
||||
|
||||
"helpers.taxonomyHelper.rename.input.title": "タクソノミー名を変更 {0}",
|
||||
"helpers.taxonomyHelper.rename.validate.equalValue": "現在のファイル名とは別のファイル名を入力してください。",
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
"common.languages": "Languages",
|
||||
"common.scripts": "Scripts",
|
||||
"common.rename": "Rename",
|
||||
"common.docs": "Documentation",
|
||||
|
||||
"loading.initPages": "Loading content",
|
||||
|
||||
@@ -274,6 +275,8 @@
|
||||
"dashboard.preview.button.refresh.title": "Refresh",
|
||||
"dashboard.preview.button.open.title": "Open",
|
||||
|
||||
"dashboard.snippetsView.item.type.content": "Content snippet",
|
||||
"dashboard.snippetsView.item.type.media": "Media snippet",
|
||||
"dashboard.snippetsView.item.quickAction.editSnippet": "Edit snippet",
|
||||
"dashboard.snippetsView.item.quickAction.deleteSnippet": "Delete snippet",
|
||||
"dashboard.snippetsView.item.quickAction.viewSnippet": "View snippet file",
|
||||
@@ -428,6 +431,7 @@
|
||||
"panel.fields.slugField.generate": "Generate slug",
|
||||
|
||||
"panel.fields.textField.ai.message": "Use Front Matter AI to suggest {0}",
|
||||
"panel.fields.textField.copilot.message": "Use Copilot to suggest {0}",
|
||||
"panel.fields.textField.ai.generate": "Generating suggestion...",
|
||||
"panel.fields.textField.loading": "Loading field",
|
||||
"panel.fields.textField.limit": "Field limit reached {0}",
|
||||
@@ -518,6 +522,7 @@
|
||||
"panel.tagPicker.inputPlaceholder.empty": "Pick your {0}",
|
||||
"panel.tagPicker.inputPlaceholder.disabled": "You have reached the limit of {0}",
|
||||
"panel.tagPicker.ai.suggest": "Use Front Matter AI to suggest {0}",
|
||||
"panel.tagPicker.copilot.suggest": "Use GitHub Copilot to suggest {0}",
|
||||
"panel.tagPicker.ai.generating": "Generating suggestions...",
|
||||
"panel.tagPicker.limit": "Max.: {0}",
|
||||
"panel.tagPicker.unkown": "Add the unknown tag",
|
||||
@@ -701,9 +706,11 @@
|
||||
"helpers.questions.contentTitle.aiInput.placeholder": "What would you like to write about?",
|
||||
"helpers.questions.contentTitle.aiInput.quickPick.title.separator": "your title/description",
|
||||
"helpers.questions.contentTitle.aiInput.quickPick.ai.separator": "AI generated title",
|
||||
"helpers.questions.contentTitle.aiInput.quickPick.copilot.separator": "GitHub Copilot suggestions",
|
||||
"helpers.questions.contentTitle.aiInput.select.title": "Select a title",
|
||||
"helpers.questions.contentTitle.aiInput.select.placeholder": "Select a title for your content",
|
||||
"helpers.questions.contentTitle.aiInput.failed": "Failed fetching the AI title. Please try to use your own title or try again later.",
|
||||
"helpers.questions.contentTitle.copilotInput.failed": "Failed fetching the GitHub Copilot title suggestions. Please try to use your own title or try again later.",
|
||||
"helpers.questions.contentTitle.aiInput.warning": "You did not specify a title for your content.",
|
||||
"helpers.questions.contentTitle.titleInput.title": "Content title",
|
||||
"helpers.questions.contentTitle.titleInput.prompt": "What would you like to use as a title for the content to create?",
|
||||
@@ -725,6 +732,7 @@
|
||||
"helpers.settingsHelper.readConfig.progress.title": "{0}: Reading dynamic config file...",
|
||||
"helpers.settingsHelper.readConfig.error": "Error reading your configuration.",
|
||||
"helpers.settingsHelper.refreshConfig.success": "Settings have been refreshed.",
|
||||
"helpers.settingsHelper.safeUpdate.warning": "Cannot update setting \"{0}\" because you've extended or split the Front Matter CMS configuration. Please manually add your changes. Check the output for the setting update.",
|
||||
|
||||
"helpers.taxonomyHelper.rename.input.title": "Rename the {0}",
|
||||
"helpers.taxonomyHelper.rename.validate.equalValue": "The new value must be different from the old one.",
|
||||
@@ -776,6 +784,8 @@
|
||||
"listeners.panel.taxonomyListener.aiSuggestTaxonomy.noEditor.error": "No active editor",
|
||||
"listeners.panel.taxonomyListener.aiSuggestTaxonomy.noData.error": "No article data",
|
||||
|
||||
"services.copilot.getChatResponse.error": "Failed to get a response from the GitHub Copilot.",
|
||||
|
||||
"services.modeSwitch.switchMode.quickPick.placeholder": "Select the mode you want to use",
|
||||
"services.modeSwitch.switchMode.quickPick.title": "{0}: Mode selection",
|
||||
"services.modeSwitch.setText.mode": "Mode: {0}",
|
||||
|
||||
235
package-lock.json
generated
235
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "vscode-front-matter-beta",
|
||||
"version": "10.1.0",
|
||||
"version": "10.3.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "vscode-front-matter-beta",
|
||||
"version": "10.1.0",
|
||||
"version": "10.3.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.6"
|
||||
@@ -21,7 +21,6 @@
|
||||
"@sentry/react": "^6.19.7",
|
||||
"@sentry/tracing": "^6.19.7",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@types/glob": "7.1.3",
|
||||
"@types/invariant": "^2.2.35",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/lodash.omit": "^4.5.7",
|
||||
@@ -34,7 +33,7 @@
|
||||
"@types/react": "17.0.0",
|
||||
"@types/react-datepicker": "^4.8.0",
|
||||
"@types/react-dom": "17.0.0",
|
||||
"@types/vscode": "^1.73.0",
|
||||
"@types/vscode": "^1.90.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.50.0",
|
||||
"@typescript-eslint/parser": "^5.50.0",
|
||||
"@vscode-elements/elements": "^1.2.0",
|
||||
@@ -54,7 +53,7 @@
|
||||
"eslint": "^8.33.0",
|
||||
"fuse.js": "6.5.3",
|
||||
"github-directory-downloader": "^1.3.6",
|
||||
"glob": "7.1.6",
|
||||
"glob": "^10.3.12",
|
||||
"gray-matter": "4.0.3",
|
||||
"html-loader": "1.3.2",
|
||||
"html-webpack-plugin": "4.5.0",
|
||||
@@ -111,7 +110,7 @@
|
||||
"yawn-yaml": "^1.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "^1.73.0"
|
||||
"vscode": "^1.90.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aashutoshrathi/word-wrap": {
|
||||
@@ -1813,16 +1812,6 @@
|
||||
"@types/send": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/glob": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz",
|
||||
"integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/minimatch": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/hast": {
|
||||
"version": "2.3.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz",
|
||||
@@ -1925,12 +1914,6 @@
|
||||
"integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/minimatch": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
|
||||
"integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/ms": {
|
||||
"version": "0.7.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
|
||||
@@ -2101,9 +2084,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/vscode": {
|
||||
"version": "1.86.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.86.0.tgz",
|
||||
"integrity": "sha512-DnIXf2ftWv+9LWOB5OJeIeaLigLHF7fdXF6atfc7X5g2w/wVZBgk0amP7b+ub5xAuW1q7qP5YcFvOcit/DtyCQ==",
|
||||
"version": "1.90.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.90.0.tgz",
|
||||
"integrity": "sha512-oT+ZJL7qHS9Z8bs0+WKf/kQ27qWYR3trsXpq46YDjFqBsMLG4ygGGjPaJ2tyrH0wJzjOEmDyg9PDJBBhWg9pkQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/vscode-webview": {
|
||||
@@ -3043,13 +3026,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.1",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
|
||||
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
|
||||
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"content-type": "~1.0.4",
|
||||
"content-type": "~1.0.5",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
@@ -3057,7 +3040,7 @@
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "2.4.1",
|
||||
"qs": "6.11.0",
|
||||
"raw-body": "2.5.1",
|
||||
"raw-body": "2.5.2",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
@@ -3117,12 +3100,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -3590,9 +3573,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
||||
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
@@ -4590,17 +4573,17 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.18.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
||||
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
|
||||
"version": "4.19.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
|
||||
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.1",
|
||||
"body-parser": "1.20.2",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.5.0",
|
||||
"cookie": "0.6.0",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
@@ -4777,9 +4760,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
@@ -4867,9 +4850,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.5",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
|
||||
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
|
||||
"version": "1.15.6",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
||||
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -5110,20 +5093,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "7.1.6",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||
"version": "10.3.12",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz",
|
||||
"integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
"foreground-child": "^3.1.0",
|
||||
"jackspeak": "^2.3.6",
|
||||
"minimatch": "^9.0.1",
|
||||
"minipass": "^7.0.4",
|
||||
"path-scurry": "^1.10.2"
|
||||
},
|
||||
"bin": {
|
||||
"glob": "dist/esm/bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
@@ -5147,6 +5132,30 @@
|
||||
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/glob/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/glob/node_modules/minimatch": {
|
||||
"version": "9.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
|
||||
"integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "13.24.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
|
||||
@@ -8534,12 +8543,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz",
|
||||
"integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==",
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz",
|
||||
"integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lru-cache": "^9.1.1 || ^10.0.0",
|
||||
"lru-cache": "^10.2.0",
|
||||
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -8550,9 +8559,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/path-scurry/node_modules/lru-cache": {
|
||||
"version": "10.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz",
|
||||
"integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==",
|
||||
"version": "10.2.2",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz",
|
||||
"integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "14 || >=16.14"
|
||||
@@ -9174,9 +9183,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
||||
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
|
||||
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
@@ -10551,6 +10560,26 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf/node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.1.1",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/run-parallel": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
@@ -11442,52 +11471,6 @@
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/sucrase/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sucrase/node_modules/glob": {
|
||||
"version": "10.3.10",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
|
||||
"integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"foreground-child": "^3.1.0",
|
||||
"jackspeak": "^2.3.5",
|
||||
"minimatch": "^9.0.1",
|
||||
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
|
||||
"path-scurry": "^1.10.1"
|
||||
},
|
||||
"bin": {
|
||||
"glob": "dist/esm/bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/sucrase/node_modules/minimatch": {
|
||||
"version": "9.0.3",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
|
||||
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
@@ -11969,9 +11952,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "5.28.2",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.2.tgz",
|
||||
"integrity": "sha512-wh1pHJHnUeQV5Xa8/kyQhO7WFa8M34l026L5P/+2TYiakvGy5Rdc8jWZVyG7ieht/0WgJLEd3kcU5gKx+6GC8w==",
|
||||
"version": "5.28.4",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz",
|
||||
"integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@fastify/busboy": "^2.0.0"
|
||||
@@ -12595,9 +12578,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-middleware": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz",
|
||||
"integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==",
|
||||
"version": "5.3.4",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz",
|
||||
"integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"colorette": "^2.0.10",
|
||||
@@ -12715,9 +12698,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/ws": {
|
||||
"version": "8.16.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz",
|
||||
"integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
@@ -12963,9 +12946,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "7.5.9",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
|
||||
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
|
||||
"version": "7.5.10",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
||||
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
|
||||
390
package.json
390
package.json
@@ -3,14 +3,15 @@
|
||||
"displayName": "Front Matter CMS",
|
||||
"description": "Front Matter is a CMS that runs within Visual Studio Code. It gives you the power and control of a full-blown CMS while also providing you the flexibility and speed of the static site generator of your choice like: Hugo, Jekyll, Docusaurus, NextJs, Gatsby, and many more...",
|
||||
"icon": "assets/frontmatter-teal-128x128.png",
|
||||
"version": "10.1.0",
|
||||
"version": "10.3.0",
|
||||
"preview": false,
|
||||
"publisher": "eliostruyf",
|
||||
"galleryBanner": {
|
||||
"color": "#0e131f",
|
||||
"theme": "dark"
|
||||
},
|
||||
"badges": [{
|
||||
"badges": [
|
||||
{
|
||||
"description": "version",
|
||||
"url": "https://img.shields.io/github/package-json/v/estruyf/vscode-front-matter?color=green&label=vscode-front-matter&style=flat-square",
|
||||
"href": "https://github.com/estruyf/vscode-front-matter"
|
||||
@@ -26,10 +27,11 @@
|
||||
},
|
||||
"qna": "https://github.com/estruyf/vscode-front-matter/discussions",
|
||||
"engines": {
|
||||
"vscode": "^1.73.0"
|
||||
"vscode": "^1.90.0"
|
||||
},
|
||||
"l10n": "./l10n",
|
||||
"categories": [
|
||||
"AI",
|
||||
"Other"
|
||||
],
|
||||
"keywords": [
|
||||
@@ -69,7 +71,8 @@
|
||||
"**/.frontmatter/config/*.json": "jsonc"
|
||||
}
|
||||
},
|
||||
"keybindings": [{
|
||||
"keybindings": [
|
||||
{
|
||||
"command": "frontMatter.dashboard",
|
||||
"key": "alt+d"
|
||||
},
|
||||
@@ -78,7 +81,8 @@
|
||||
"key": "ctrl+r",
|
||||
"mac": "cmd+r",
|
||||
"when": "activeWebviewPanelId == frontMatterDashboard"
|
||||
}, {
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.insertMedia",
|
||||
"key": "ctrl+shift+i",
|
||||
"mac": "cmd+shift+i",
|
||||
@@ -92,19 +96,23 @@
|
||||
}
|
||||
],
|
||||
"viewsContainers": {
|
||||
"activitybar": [{
|
||||
"id": "frontmatter-explorer",
|
||||
"title": "FM",
|
||||
"icon": "$(fm-logo)"
|
||||
}]
|
||||
"activitybar": [
|
||||
{
|
||||
"id": "frontmatter-explorer",
|
||||
"title": "FM",
|
||||
"icon": "$(fm-logo)"
|
||||
}
|
||||
]
|
||||
},
|
||||
"views": {
|
||||
"frontmatter-explorer": [{
|
||||
"id": "frontMatter.explorer",
|
||||
"name": "Front Matter",
|
||||
"icon": "$(fm-logo)",
|
||||
"type": "webview"
|
||||
}]
|
||||
"frontmatter-explorer": [
|
||||
{
|
||||
"id": "frontMatter.explorer",
|
||||
"name": "Front Matter",
|
||||
"icon": "$(fm-logo)",
|
||||
"type": "webview"
|
||||
}
|
||||
]
|
||||
},
|
||||
"configuration": {
|
||||
"title": "%settings.configuration.title%",
|
||||
@@ -172,7 +180,8 @@
|
||||
"frontMatter.content.defaultFileType": {
|
||||
"type": "string",
|
||||
"default": "md",
|
||||
"oneOf": [{
|
||||
"oneOf": [
|
||||
{
|
||||
"enum": [
|
||||
"md",
|
||||
"mdx"
|
||||
@@ -188,7 +197,8 @@
|
||||
"frontMatter.content.defaultSorting": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"oneOf": [{
|
||||
"oneOf": [
|
||||
{
|
||||
"enum": [
|
||||
"LastModifiedAsc",
|
||||
"LastModifiedDesc",
|
||||
@@ -289,6 +299,10 @@
|
||||
"default": null,
|
||||
"description": "%setting.frontMatter.content.pageFolders.items.properties.previewPath.description%"
|
||||
},
|
||||
"trailingSlash": {
|
||||
"type": "boolean",
|
||||
"description": "%setting.frontMatter.content.pageFolders.items.properties.trailingSlash.description%"
|
||||
},
|
||||
"filePrefix": {
|
||||
"type": [
|
||||
"null",
|
||||
@@ -536,7 +550,8 @@
|
||||
"categories"
|
||||
],
|
||||
"markdownDescription": "%setting.frontMatter.content.filters.markdownDescription%",
|
||||
"items": [{
|
||||
"items": [
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"contentFolders",
|
||||
@@ -609,7 +624,8 @@
|
||||
"command": {
|
||||
"$id": "#scriptCommand",
|
||||
"type": "string",
|
||||
"anyOf": [{
|
||||
"anyOf": [
|
||||
{
|
||||
"enum": [
|
||||
"node",
|
||||
"bash",
|
||||
@@ -805,7 +821,8 @@
|
||||
"title",
|
||||
"file"
|
||||
],
|
||||
"anyOf": [{
|
||||
"anyOf": [
|
||||
{
|
||||
"required": [
|
||||
"schema"
|
||||
]
|
||||
@@ -859,7 +876,8 @@
|
||||
"id",
|
||||
"path"
|
||||
],
|
||||
"anyOf": [{
|
||||
"anyOf": [
|
||||
{
|
||||
"required": [
|
||||
"schema"
|
||||
]
|
||||
@@ -1100,26 +1118,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": [{
|
||||
"name": "default",
|
||||
"fileTypes": null,
|
||||
"fields": [{
|
||||
"title": "Title",
|
||||
"name": "title",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "Caption",
|
||||
"name": "caption",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "Alt text",
|
||||
"name": "alt",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}],
|
||||
"default": [
|
||||
{
|
||||
"name": "default",
|
||||
"fileTypes": null,
|
||||
"fields": [
|
||||
{
|
||||
"title": "Title",
|
||||
"name": "title",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "Caption",
|
||||
"name": "caption",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "Alt text",
|
||||
"name": "alt",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"scope": "Media"
|
||||
},
|
||||
"frontMatter.media.supportedMimeTypes": {
|
||||
@@ -1170,6 +1191,12 @@
|
||||
"markdownDescription": "%setting.frontMatter.preview.pathName.markdownDescription%",
|
||||
"scope": "Site preview"
|
||||
},
|
||||
"frontMatter.preview.trailingSlash": {
|
||||
"type": "boolean",
|
||||
"default": "",
|
||||
"markdownDescription": "%setting.frontMatter.preview.trailingSlash.markdownDescription%",
|
||||
"scope": "Site preview"
|
||||
},
|
||||
"frontMatter.site.baseURL": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
@@ -1349,7 +1376,8 @@
|
||||
"default": "",
|
||||
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.taxonomyId.description%",
|
||||
"not": {
|
||||
"anyOf": [{
|
||||
"anyOf": [
|
||||
{
|
||||
"const": ""
|
||||
},
|
||||
{
|
||||
@@ -1543,7 +1571,8 @@
|
||||
"type",
|
||||
"name"
|
||||
],
|
||||
"allOf": [{
|
||||
"allOf": [
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"type": {
|
||||
@@ -1714,6 +1743,10 @@
|
||||
"default": null,
|
||||
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.previewPath.description%"
|
||||
},
|
||||
"trailingSlash": {
|
||||
"type": "boolean",
|
||||
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.trailingSlash.description%"
|
||||
},
|
||||
"slugTemplate": {
|
||||
"type": [
|
||||
"null",
|
||||
@@ -1751,48 +1784,51 @@
|
||||
"fields"
|
||||
]
|
||||
},
|
||||
"default": [{
|
||||
"name": "default",
|
||||
"pageBundle": false,
|
||||
"fields": [{
|
||||
"title": "Title",
|
||||
"name": "title",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "Description",
|
||||
"name": "description",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "Publishing date",
|
||||
"name": "date",
|
||||
"type": "datetime",
|
||||
"default": "{{now}}",
|
||||
"isPublishDate": true
|
||||
},
|
||||
{
|
||||
"title": "Content preview",
|
||||
"name": "preview",
|
||||
"type": "image"
|
||||
},
|
||||
{
|
||||
"title": "Is in draft",
|
||||
"name": "draft",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"title": "Tags",
|
||||
"name": "tags",
|
||||
"type": "tags"
|
||||
},
|
||||
{
|
||||
"title": "Categories",
|
||||
"name": "categories",
|
||||
"type": "categories"
|
||||
}
|
||||
]
|
||||
}],
|
||||
"default": [
|
||||
{
|
||||
"name": "default",
|
||||
"pageBundle": false,
|
||||
"fields": [
|
||||
{
|
||||
"title": "Title",
|
||||
"name": "title",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "Description",
|
||||
"name": "description",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "Publishing date",
|
||||
"name": "date",
|
||||
"type": "datetime",
|
||||
"default": "{{now}}",
|
||||
"isPublishDate": true
|
||||
},
|
||||
{
|
||||
"title": "Content preview",
|
||||
"name": "preview",
|
||||
"type": "image"
|
||||
},
|
||||
{
|
||||
"title": "Is in draft",
|
||||
"name": "draft",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"title": "Tags",
|
||||
"name": "tags",
|
||||
"type": "tags"
|
||||
},
|
||||
{
|
||||
"title": "Categories",
|
||||
"name": "categories",
|
||||
"type": "categories"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"scope": "Taxonomy"
|
||||
},
|
||||
"frontMatter.taxonomy.customTaxonomy": {
|
||||
@@ -1805,7 +1841,8 @@
|
||||
"type": "string",
|
||||
"description": "%setting.frontMatter.taxonomy.customTaxonomy.items.properties.id.description%",
|
||||
"not": {
|
||||
"anyOf": [{
|
||||
"anyOf": [
|
||||
{
|
||||
"const": ""
|
||||
},
|
||||
{
|
||||
@@ -1987,10 +2024,21 @@
|
||||
"frontMatter.website.host": {
|
||||
"type": "string",
|
||||
"markdownDescription": "%setting.frontMatter.website.host.markdownDescription%"
|
||||
},
|
||||
"frontMatter.logging": {
|
||||
"type": "string",
|
||||
"default": "info",
|
||||
"enum": [
|
||||
"error",
|
||||
"warn",
|
||||
"info",
|
||||
"verbose"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"commands": [{
|
||||
"commands": [
|
||||
{
|
||||
"command": "frontMatter.project.switch",
|
||||
"title": "%command.frontMatter.project.switch%",
|
||||
"category": "Front Matter",
|
||||
@@ -2243,6 +2291,12 @@
|
||||
"title": "%command.frontMatter.preview%",
|
||||
"category": "Front Matter"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.docs",
|
||||
"title": "%command.frontMatter.docs%",
|
||||
"category": "Front Matter",
|
||||
"icon": "$(book)"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.chatbot",
|
||||
"title": "%command.frontMatter.chatbot%",
|
||||
@@ -2316,16 +2370,21 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"submenus": [{
|
||||
"id": "frontmatter.submenu",
|
||||
"label": "Front Matter"
|
||||
}],
|
||||
"submenus": [
|
||||
{
|
||||
"id": "frontmatter.submenu",
|
||||
"label": "Front Matter"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"webview/context": [{
|
||||
"command": "workbench.action.webview.openDeveloperTools",
|
||||
"when": "frontMatter:isDevelopment"
|
||||
}],
|
||||
"editor/title": [{
|
||||
"webview/context": [
|
||||
{
|
||||
"command": "workbench.action.webview.openDeveloperTools",
|
||||
"when": "frontMatter:isDevelopment"
|
||||
}
|
||||
],
|
||||
"editor/title": [
|
||||
{
|
||||
"command": "frontMatter.markup.heading",
|
||||
"group": "navigation@-133",
|
||||
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg"
|
||||
@@ -2411,11 +2470,14 @@
|
||||
"when": "resourceFilename == 'frontmatter.json'"
|
||||
}
|
||||
],
|
||||
"explorer/context": [{
|
||||
"submenu": "frontmatter.submenu",
|
||||
"group": "frontmatter@1"
|
||||
}],
|
||||
"frontmatter.submenu": [{
|
||||
"explorer/context": [
|
||||
{
|
||||
"submenu": "frontmatter.submenu",
|
||||
"group": "frontmatter@1"
|
||||
}
|
||||
],
|
||||
"frontmatter.submenu": [
|
||||
{
|
||||
"command": "frontMatter.createFromTemplate",
|
||||
"when": "explorerResourceIsFolder",
|
||||
"group": "frontmatter@1"
|
||||
@@ -2431,7 +2493,8 @@
|
||||
"group": "frontmatter@3"
|
||||
}
|
||||
],
|
||||
"commandPalette": [{
|
||||
"commandPalette": [
|
||||
{
|
||||
"command": "frontMatter.init",
|
||||
"when": "frontMatterCanInit"
|
||||
},
|
||||
@@ -2608,7 +2671,13 @@
|
||||
"when": "frontMatter:file:isValid == true"
|
||||
}
|
||||
],
|
||||
"view/title": [{
|
||||
"view/title": [
|
||||
{
|
||||
"command": "frontMatter.docs",
|
||||
"group": "navigation@-1",
|
||||
"when": "view == frontMatter.explorer"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.chatbot",
|
||||
"group": "navigation@0",
|
||||
"when": "view == frontMatter.explorer"
|
||||
@@ -2640,52 +2709,70 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"grammars": [{
|
||||
"path": "./syntaxes/hugo.tmLanguage.json",
|
||||
"scopeName": "frontmatter.markdown.hugo",
|
||||
"injectTo": [
|
||||
"text.html.markdown"
|
||||
]
|
||||
}],
|
||||
"walkthroughs": [{
|
||||
"id": "frontmatter.welcome",
|
||||
"title": "Get started with Front Matter",
|
||||
"description": "Discover the features of Front Matter and learn how to use the CMS for your SSG or static site.",
|
||||
"steps": [{
|
||||
"id": "frontmatter.welcome.init",
|
||||
"title": "Get started",
|
||||
"description": "Initial steps to get started.\n[Open dashboard](command:frontMatter.dashboard)",
|
||||
"media": {
|
||||
"markdown": "assets/walkthrough/get-started.md"
|
||||
"languages": [
|
||||
{
|
||||
"id": "frontmatter.project.output",
|
||||
"mimetypes": [
|
||||
"text/x-code-output"
|
||||
]
|
||||
}
|
||||
],
|
||||
"grammars": [
|
||||
{
|
||||
"path": "./syntaxes/hugo.tmLanguage.json",
|
||||
"scopeName": "frontmatter.markdown.hugo",
|
||||
"injectTo": [
|
||||
"text.html.markdown"
|
||||
]
|
||||
},
|
||||
{
|
||||
"language": "frontmatter.project.output",
|
||||
"scopeName": "frontmatter.project.output",
|
||||
"path": "./syntaxes/frontmatter-output.tmLanguage.json"
|
||||
}
|
||||
],
|
||||
"walkthroughs": [
|
||||
{
|
||||
"id": "frontmatter.welcome",
|
||||
"title": "Get started with Front Matter",
|
||||
"description": "Discover the features of Front Matter and learn how to use the CMS for your SSG or static site.",
|
||||
"steps": [
|
||||
{
|
||||
"id": "frontmatter.welcome.init",
|
||||
"title": "Get started",
|
||||
"description": "Initial steps to get started.\n[Open dashboard](command:frontMatter.dashboard)",
|
||||
"media": {
|
||||
"markdown": "assets/walkthrough/get-started.md"
|
||||
},
|
||||
"completionEvents": [
|
||||
"onContext:frontMatterInitialized"
|
||||
]
|
||||
},
|
||||
"completionEvents": [
|
||||
"onContext:frontMatterInitialized"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "frontmatter.welcome.documentation",
|
||||
"title": "Documentation",
|
||||
"description": "Check out the documentation for Front Matter.\n[View our documentation](https://frontmatter.codes/docs)",
|
||||
"media": {
|
||||
"markdown": "assets/walkthrough/documentation.md"
|
||||
{
|
||||
"id": "frontmatter.welcome.documentation",
|
||||
"title": "Documentation",
|
||||
"description": "Check out the documentation for Front Matter.\n[View our documentation](https://frontmatter.codes/docs)",
|
||||
"media": {
|
||||
"markdown": "assets/walkthrough/documentation.md"
|
||||
},
|
||||
"completionEvents": [
|
||||
"onLink:https://frontmatter.codes/docs"
|
||||
]
|
||||
},
|
||||
"completionEvents": [
|
||||
"onLink:https://frontmatter.codes/docs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "frontmatter.welcome.supporter",
|
||||
"title": "Support the project",
|
||||
"description": "Become a supporter.\n[Support the project](https://github.com/sponsors/estruyf)",
|
||||
"media": {
|
||||
"markdown": "assets/walkthrough/support-the-project.md"
|
||||
},
|
||||
"completionEvents": [
|
||||
"onLink:https://github.com/sponsors/estruyf"
|
||||
]
|
||||
}
|
||||
]
|
||||
}]
|
||||
{
|
||||
"id": "frontmatter.welcome.supporter",
|
||||
"title": "Support the project",
|
||||
"description": "Become a supporter.\n[Support the project](https://github.com/sponsors/estruyf)",
|
||||
"media": {
|
||||
"markdown": "assets/walkthrough/support-the-project.md"
|
||||
},
|
||||
"completionEvents": [
|
||||
"onLink:https://github.com/sponsors/estruyf"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"dev:ext": "npm run clean && npm run localization:generate && npm-run-all --parallel watch:*",
|
||||
@@ -2719,7 +2806,6 @@
|
||||
"@sentry/react": "^6.19.7",
|
||||
"@sentry/tracing": "^6.19.7",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@types/glob": "7.1.3",
|
||||
"@types/invariant": "^2.2.35",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/lodash.omit": "^4.5.7",
|
||||
@@ -2732,7 +2818,7 @@
|
||||
"@types/react": "17.0.0",
|
||||
"@types/react-datepicker": "^4.8.0",
|
||||
"@types/react-dom": "17.0.0",
|
||||
"@types/vscode": "^1.73.0",
|
||||
"@types/vscode": "^1.90.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.50.0",
|
||||
"@typescript-eslint/parser": "^5.50.0",
|
||||
"@vscode-elements/elements": "^1.2.0",
|
||||
@@ -2752,7 +2838,7 @@
|
||||
"eslint": "^8.33.0",
|
||||
"fuse.js": "6.5.3",
|
||||
"github-directory-downloader": "^1.3.6",
|
||||
"glob": "7.1.6",
|
||||
"glob": "^10.3.12",
|
||||
"gray-matter": "4.0.3",
|
||||
"html-loader": "1.3.2",
|
||||
"html-webpack-plugin": "4.5.0",
|
||||
@@ -2814,4 +2900,4 @@
|
||||
"dependencies": {
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,11 +73,13 @@
|
||||
"setting.frontMatter.content.pageFolders.items.properties.path.description": "フォルダーのパス",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.excludeSubdir.description": "サブディレクトリを除外する",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.previewPath.description": "フォルダーのカスタム プレビュー パスを定義します。",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.trailingSlash.description": "プレビュー用URLに末尾のスラッシュを加えるかどうかを設定します。",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.filePrefix.description": "ファイル名の接頭辞を定義します。",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.contentTypes.description": "現在の場所に使用できる記事タイプを定義します。定義しない場合は、全ての記事タイプを使用できます。",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.disableCreation.description": "フォルダー内の新しい記事の作成を無効にします。",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.defaultLocale.description": "ページフォルダー用デフォルトのロケールIDを設定します。このフォルダー内の全ての記事が`frontMatter.content.i18n`で設定された言語へ翻訳可能になります。",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.locales.description": "ページフォルダーで利用するロケールを設定します。この設定は記事の翻訳に使用されます。",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.slugTemplate.description": "カスタムスラッグのテンプレートを定義します。",
|
||||
"setting.frontMatter.content.i18n.markdownDescription": "ウェブサイトで利用するロケールを設定します。この設定はページフォルダーレベルの設定より優先されます。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.content.i18n)",
|
||||
"setting.frontMatter.content.i18n.items.properties.title.description": "言語名",
|
||||
"setting.frontMatter.content.i18n.items.properties.locale.description": "言語コード",
|
||||
@@ -175,6 +177,7 @@
|
||||
"setting.frontMatter.panel.freeform.markdownDescription": "未登録のタグ/カテゴリーをタグピッカーに入力可能にするかどうかを設定します(有効にすると、後で保存するオプションが使えます)。規定値はtrue。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.panel.freeform)",
|
||||
"setting.frontMatter.panel.actions.disabled.markdownDescription": "パネル内で非表示にしたいコマンドを設定します。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.panel.actions.disabled)",
|
||||
"setting.frontMatter.preview.host.markdownDescription": "プレビュー表示に使用するホストのURLを設定します(例:`http://localhost:1313`)。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.preview.host)",
|
||||
"setting.frontMatter.preview.trailingSlash.markdownDescription": "プレビュー用URLに末尾のスラッシュを加えるかどうかを設定します。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.preview.trailingslash)",
|
||||
"setting.frontMatter.preview.pathName.markdownDescription": "ホストパスとスラッグの間に追加したいパスを設定します。例えば、パスに`yyyy/MM`などの日付を含めたい場合等に使えます。日付は記事の日付フィールドの値に基づいて生成されます。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.preview.pathname)",
|
||||
"setting.frontMatter.site.baseURL.markdownDescription": "ベースURLを設定します。これはSEOチェックに利用されます。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.site.baseurl)",
|
||||
"setting.frontMatter.taxonomy.alignFilename.markdownDescription": "ファイル生成時にファイル名をスラッグに合わせます。[ドキュメントを確認](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.alignfilename)",
|
||||
@@ -229,6 +232,7 @@
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.properties.caseSensitive.description": "比較で大文字と小文字を区別するかどうかを指定します。デフォルト: true",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.pageBundle.description": "新しい記事を作成するときにフォルダーを作成するかどうかを指定します。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.previewPath.description": "記事タイプのカスタム プレビュー パスを定義します。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.trailingSlash.description": "プレビュー用URLに末尾のスラッシュを加えるかどうかを設定します。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.slugTemplate.description": "記事タイプのカスタムスラッグのテンプレートを設定します。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.template.description": "新しい記事の作成に使用できるオプションのテンプレート。",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.postScript.description": "新しい記事の作成後に使用できるオプションのポストスクリプト。",
|
||||
|
||||
177
package.nls.json
177
package.nls.json
@@ -38,6 +38,7 @@
|
||||
"command.frontMatter.markup.orderedlist": "Ordered list",
|
||||
"command.frontMatter.markup.options": "Other markup options",
|
||||
"command.frontMatter.preview": "Preview content",
|
||||
"command.frontMatter.docs": "Documentation",
|
||||
"command.frontMatter.chatbot": "Ask the Front Matter AI for help",
|
||||
"command.frontMatter.promoteSettings": "Promote settings from local to team level",
|
||||
"command.frontMatter.remap": "Remap or remove tag/category in all articles",
|
||||
@@ -50,57 +51,59 @@
|
||||
"command.frontMatter.cache.clear": "Clear cache",
|
||||
"command.frontMatter.i18n.create": "Create new translation",
|
||||
"settings.configuration.title": "Front Matter: use frontmatter.json for shared team settings",
|
||||
"setting.frontMatter.projects.markdownDescription": "Specify the list of projects to load in the Front Matter CMS. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.projects)",
|
||||
"setting.frontMatter.projects.markdownDescription": "Specify the list of projects to load in the Front Matter CMS. [Local](https://file%2B.vscode-resource.vscode-cdn.net/Users/eliostruyf/nodejs/frontmatter-test-projects/astro-blog/test.html) - [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.projects) - [View in VS Code](vscode://simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.projects%22%5D)",
|
||||
"setting.frontMatter.projects.items.properties.name.markdownDescription": "Specify the name of the project.",
|
||||
"setting.frontMatter.projects.items.properties.default.markdownDescription": "Specify if this project is the default project to load.",
|
||||
"setting.frontMatter.sponsors.ai.enabled.markdownDescription": "Specify if you want to enable AI suggestions. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.sponsors.ai.enabled)",
|
||||
"setting.frontMatter.extensibility.scripts.markdownDescription": "Specify the list of scripts to load in the Front Matter CMS. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.extensibility.scripts)",
|
||||
"setting.frontMatter.experimental.markdownDescription": "Specify if you want to enable the experimental features. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.experimental)",
|
||||
"setting.frontMatter.extends.markdownDescription": "Specify the list of paths/URLs to extend the Front Matter CMS config. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.extends)",
|
||||
"setting.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/overview#frontmatter.content.autoupdatedate)",
|
||||
"setting.frontMatter.content.defaultFileType.markdownDescription": "Specify the default file type for the content to create. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.defaultfiletype)",
|
||||
"setting.frontMatter.content.defaultSorting.markdownDescription": "Specify the default sorting option for the content dashboard. You can use one of the values from the enum or define your own ID. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.defaultsorting)",
|
||||
"setting.frontMatter.content.draftField.markdownDescription": "Define the draft field you want to use to manage your content. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.draftfield)",
|
||||
"setting.frontMatter.sponsors.ai.enabled.markdownDescription": "Specify if you want to enable AI suggestions. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.sponsors.ai.enabled) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.sponsors.ai.enabled%22%5D)",
|
||||
"setting.frontMatter.extensibility.scripts.markdownDescription": "Specify the list of scripts to load in the Front Matter CMS. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.extensibility.scripts) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.extensibility.scripts%22%5D)",
|
||||
"setting.frontMatter.experimental.markdownDescription": "Specify if you want to enable the experimental features. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.experimental) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.experimental%22%5D)",
|
||||
"setting.frontMatter.extends.markdownDescription": "Specify the list of paths/URLs to extend the Front Matter CMS config. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.extends) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.extends%22%5D)",
|
||||
"setting.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). [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.autoupdatedate) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.autoupdatedate%22%5D)",
|
||||
"setting.frontMatter.content.defaultFileType.markdownDescription": "Specify the default file type for the content to create. [Docs](https://frontmatter.codes/docs/settings/overview%23frontmatter.content.defaultfiletype) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.defaultfiletype%22%5D)",
|
||||
"setting.frontMatter.content.defaultSorting.markdownDescription": "Specify the default sorting option for the content dashboard. You can use one of the values from the enum or define your own ID. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.defaultsorting) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.defaultsorting%22%5D)",
|
||||
"setting.frontMatter.content.draftField.markdownDescription": "Define the draft field you want to use to manage your content. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.draftfield) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.draftfield%22%5D)",
|
||||
"setting.frontMatter.content.draftField.properties.type.description": "Type of the draft field you want to use",
|
||||
"setting.frontMatter.content.draftField.properties.name.description": "Name of the field to use",
|
||||
"setting.frontMatter.content.draftField.properties.invert.description": "By default the draft field is set to true when the content is a draft. Set this to true to set it to false.",
|
||||
"setting.frontMatter.content.draftField.properties.choices.description": "List of choices for the field",
|
||||
"setting.frontMatter.content.fmHighlight.markdownDescription": "Specify if you want to highlight the Front Matter in the Markdown file. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.fmhighlight)",
|
||||
"setting.frontMatter.content.hideFm.markdownDescription": "Specify if you want to hide the Front Matter in the Markdown file. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.hidefm)",
|
||||
"setting.frontMatter.content.hideFmMessage.markdownDescription": "Specify the message to display when the Front Matter is hidden. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.hidefmMessage)",
|
||||
"setting.frontMatter.content.pageFolders.markdownDescription": "This array of folders defines where the extension can retrieve or create new pages. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.pagefolders)",
|
||||
"setting.frontMatter.content.fmHighlight.markdownDescription": "Specify if you want to highlight the Front Matter in the Markdown file. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.fmhighlight) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.fmhighlight%22%5D)",
|
||||
"setting.frontMatter.content.hideFm.markdownDescription": "Specify if you want to hide the Front Matter in the Markdown file. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.hidefm) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.hidefm%22%5D)",
|
||||
"setting.frontMatter.content.hideFmMessage.markdownDescription": "Specify the message to display when the Front Matter is hidden. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.hidefmMessage) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.hidefmMessage%22%5D)",
|
||||
"setting.frontMatter.content.pageFolders.markdownDescription": "This array of folders defines where the extension can retrieve or create new pages. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.pagefolders) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.pagefolders%22%5D)",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.title.description": "Name of the folder",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.path.description": "Path of the folder",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.excludeSubdir.description": "Exclude sub-directories",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.previewPath.description": "Defines a custom preview path for the folder.",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.trailingSlash.description": "Specify if you want to add a trailing slash to the preview URL.",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.filePrefix.description": "Defines a prefix for the file name.",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.contentTypes.description": "Defines which content types can be used for the current location. If not defined, all content types will be available.",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.disableCreation.description": "Disable the creation of new content in the folder.",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.defaultLocale.description": "Set the default locale ID for the page folder. All content from this folder is translatable to the languages defined in the `frontMatter.content.i18n` setting.",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.locales.description": "Define the locales for the page folder. This will be used for the translation of the content.",
|
||||
"setting.frontMatter.content.i18n.markdownDescription": "Specify the locales you want to use for your website. This setting can be overwritten on page folder level. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.i18n)",
|
||||
"setting.frontMatter.content.pageFolders.items.properties.slugTemplate.description": "Defines a custom slug template.",
|
||||
"setting.frontMatter.content.i18n.markdownDescription": "Specify the locales you want to use for your website. This setting can be overwritten on page folder level. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.i18n) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.i18n%22%5D)",
|
||||
"setting.frontMatter.content.i18n.items.properties.title.description": "Title of the locale",
|
||||
"setting.frontMatter.content.i18n.items.properties.locale.description": "Locale code",
|
||||
"setting.frontMatter.content.i18n.items.properties.path.description": "Relative path of the locale folder",
|
||||
"setting.frontMatter.content.placeholders.markdownDescription": "This array of placeholders defines the placeholders that you can use in your content types and templates for automatically populating your content its front matter. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.placeholders)",
|
||||
"setting.frontMatter.content.placeholders.markdownDescription": "This array of placeholders defines the placeholders that you can use in your content types and templates for automatically populating your content its front matter. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.placeholders) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.placeholders%22%5D)",
|
||||
"setting.frontMatter.content.placeholders.items.properties.id.description": "ID of the placeholder, in your content type or template, use it as follows: {{placeholder}}",
|
||||
"setting.frontMatter.content.placeholders.items.properties.value.description": "The placeholder its value",
|
||||
"setting.frontMatter.content.placeholders.items.properties.script.description": "The script to execute to get the value of the placeholder",
|
||||
"setting.frontMatter.content.publicFolder.markdownDescription": "Specify the folder name where all your assets are located. For instance in Hugo this is the `static` folder. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.publicfolder)",
|
||||
"setting.frontMatter.content.publicFolder.markdownDescription": "Specify the folder name where all your assets are located. For instance in Hugo this is the `static` folder. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.publicfolder) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.publicfolder%22%5D)",
|
||||
"setting.frontMatter.content.publicFolder.properties.path.description": "Specify the path of the assets folder",
|
||||
"setting.frontMatter.content.publicFolder.properties.relative.description": "Defines if the path to your media files be relative to the content file?",
|
||||
"setting.frontMatter.snippets.wrapper.enabled.markdownDescription": "Specify if you want to wrap the snippets. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontMatter.snippets.wrapper.enabled)",
|
||||
"setting.frontMatter.content.snippets.markdownDescription": "Define the snippets you want to use in your content. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.snippets)",
|
||||
"setting.frontMatter.content.sorting.markdownDescription": "Define the sorting options for your dashboard content. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.sorting)",
|
||||
"setting.frontMatter.snippets.wrapper.enabled.markdownDescription": "Specify if you want to wrap the snippets. [Docs](https://frontmatter.codes/docs/settings/overview#frontMatter.snippets.wrapper.enabled) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontMatter.snippets.wrapper.enabled%22%5D)",
|
||||
"setting.frontMatter.content.snippets.markdownDescription": "Define the snippets you want to use in your content. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.snippets) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.snippets%22%5D)",
|
||||
"setting.frontMatter.content.sorting.markdownDescription": "Define the sorting options for your dashboard content. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.sorting) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.sorting%22%5D)",
|
||||
"setting.frontMatter.content.sorting.items.properties.id.description": "The ID of the sorting option. This will be used for the storing the last used sorting option or the default option.",
|
||||
"setting.frontMatter.content.sorting.items.properties.title.description": "Name of the sorting label",
|
||||
"setting.frontMatter.content.sorting.items.properties.name.description": "Name of the metadata field to sort by",
|
||||
"setting.frontMatter.content.sorting.items.properties.order.description": "Order of the sorting",
|
||||
"setting.frontMatter.content.sorting.items.properties.type.description": "Type of the field value",
|
||||
"setting.frontMatter.content.supportedFileTypes.markdownDescription": "Specify the file types that you want to use in Front Matter. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.supportedfiletypes)",
|
||||
"setting.frontMatter.content.wysiwyg.markdownDescription": "Specifies if you want to enable/disable the What You See, Is What You Get (WYSIWYG) markdown controls. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.wysiwyg)",
|
||||
"setting.frontMatter.content.filters.markdownDescription": "Specify the filters you want to use for your content dashboard. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.filters)",
|
||||
"setting.frontMatter.custom.scripts.markdownDescription": "Specify the path to a Node.js script to execute. The current file path will be provided as an argument. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.custom.scripts)",
|
||||
"setting.frontMatter.content.supportedFileTypes.markdownDescription": "Specify the file types that you want to use in Front Matter. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.supportedfiletypes) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.supportedfiletypes%22%5D)",
|
||||
"setting.frontMatter.content.wysiwyg.markdownDescription": "Specifies if you want to enable/disable the What You See, Is What You Get (WYSIWYG) markdown controls. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.wysiwyg) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.wysiwyg%22%5D)",
|
||||
"setting.frontMatter.content.filters.markdownDescription": "Specify the filters you want to use for your content dashboard. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.content.filters) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.content.filters%22%5D)",
|
||||
"setting.frontMatter.custom.scripts.markdownDescription": "Specify the path to a Node.js script to execute. The current file path will be provided as an argument. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.custom.scripts) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.custom.scripts%22%5D)",
|
||||
"setting.frontMatter.custom.scripts.items.properties.id.description": "ID of the script.",
|
||||
"setting.frontMatter.custom.scripts.items.properties.title.description": "Title you want to give to your script. Will be shown as the title of the button.",
|
||||
"setting.frontMatter.custom.scripts.items.properties.script.description": "Path to the script to execute",
|
||||
@@ -114,16 +117,16 @@
|
||||
"setting.frontMatter.custom.scripts.items.properties.environments.items.properties.type.description": "The environment type for which the script needs to be used",
|
||||
"setting.frontMatter.custom.scripts.items.properties.environments.items.properties.script.description": "Path to the script to execute",
|
||||
"setting.frontMatter.custom.scripts.items.properties.contentTypes.description": "Define the content types for which the script will be used. If none are defined, it will be available to all types.",
|
||||
"setting.frontMatter.dashboard.content.pagination.markdownDescription": "Specify if you want to enable/disable pagination for your content. You can define your page number up to 52. Default items per page is `16`. Disabling the pagination can be done by setting it to `false`. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.dashboard.content.pagination)",
|
||||
"setting.frontMatter.dashboard.content.cardTags.markdownDescription": "Specify the name of the metadata field that will be used to show the tags on the content card. When empty or null, it will hide the tags from the card. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.dashboard.content.cardtags)",
|
||||
"setting.frontMatter.dashboard.content.card.fields.state.markdownDescription": "Specify if you want to show the state/draft status on the content card view. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontMatter.dashboard.content.card.fields.state)",
|
||||
"setting.frontMatter.dashboard.content.card.fields.date.markdownDescription": "Specify if you want to show the date on the content card view. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontMatter.dashboard.content.card.fields.date)",
|
||||
"setting.frontMatter.dashboard.content.card.fields.description.markdownDescription": "Specify the name of the metadata field that will be used to show the description on the content card. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontMatter.dashboard.content.card.fields.description)",
|
||||
"setting.frontMatter.dashboard.content.card.fields.title.markdownDescription": "Specify the name of the metadata field that will be used to show the title on the content card. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontMatter.dashboard.content.card.fields.title)",
|
||||
"setting.frontMatter.dashboard.mediaSnippet.markdownDescription": "Specify the a snippet for your custom media insert markup. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.dashboard.mediasnippet)",
|
||||
"setting.frontMatter.dashboard.content.pagination.markdownDescription": "Specify if you want to enable/disable pagination for your content. You can define your page number up to 52. Default items per page is `16`. Disabling the pagination can be done by setting it to `false`. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.dashboard.content.pagination) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.dashboard.content.pagination%22%5D)",
|
||||
"setting.frontMatter.dashboard.content.cardTags.markdownDescription": "Specify the name of the metadata field that will be used to show the tags on the content card. When empty or null, it will hide the tags from the card. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.dashboard.content.cardtags) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.dashboard.content.cardtags%22%5D)",
|
||||
"setting.frontMatter.dashboard.content.card.fields.state.markdownDescription": "Specify if you want to show the state/draft status on the content card view. [Docs](https://frontmatter.codes/docs/settings/overview#frontMatter.dashboard.content.card.fields.state) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontMatter.dashboard.content.card.fields.state%22%5D)",
|
||||
"setting.frontMatter.dashboard.content.card.fields.date.markdownDescription": "Specify if you want to show the date on the content card view. [Docs](https://frontmatter.codes/docs/settings/overview#frontMatter.dashboard.content.card.fields.date) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontMatter.dashboard.content.card.fields.date%22%5D)",
|
||||
"setting.frontMatter.dashboard.content.card.fields.description.markdownDescription": "Specify the name of the metadata field that will be used to show the description on the content card. [Docs](https://frontmatter.codes/docs/settings/overview#frontMatter.dashboard.content.card.fields.description) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontMatter.dashboard.content.card.fields.description%22%5D)",
|
||||
"setting.frontMatter.dashboard.content.card.fields.title.markdownDescription": "Specify the name of the metadata field that will be used to show the title on the content card. [Docs](https://frontmatter.codes/docs/settings/overview#frontMatter.dashboard.content.card.fields.title) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontMatter.dashboard.content.card.fields.title%22%5D)",
|
||||
"setting.frontMatter.dashboard.mediaSnippet.markdownDescription": "Specify the a snippet for your custom media insert markup. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.dashboard.mediasnippet) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.dashboard.mediasnippet%22%5D)",
|
||||
"setting.frontMatter.dashboard.mediaSnippet.items.description": "Use the `{mediaUrl}`, `{caption}`, `{alt}`, `{filename}`, `{mediaHeight}`, and `{mediaWidth}` placeholders in your snippet to automatically insert the media information.",
|
||||
"setting.frontMatter.dashboard.openOnStart.markdownDescription": "Specify if you want to open the dashboard when you start VS Code. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.dashboard.openonstart)",
|
||||
"setting.frontMatter.data.files.markdownDescription": "Specify the data files you want to use for your website. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.data.files)",
|
||||
"setting.frontMatter.dashboard.openOnStart.markdownDescription": "Specify if you want to open the dashboard when you start VS Code. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.dashboard.openonstart) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.dashboard.openonstart%22%5D)",
|
||||
"setting.frontMatter.data.files.markdownDescription": "Specify the data files you want to use for your website. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.data.files) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.data.files%22%5D)",
|
||||
"setting.frontMatter.data.files.items.properties.id.description": "Your unique ID you want to use for your data file.",
|
||||
"setting.frontMatter.data.files.items.properties.title.description": "Title you want to give to your data file.",
|
||||
"setting.frontMatter.data.files.items.properties.labelField.description": "The field you want to use as label for your data entries.",
|
||||
@@ -136,33 +139,33 @@
|
||||
"setting.frontMatter.data.files.items.properties.schema.properties.properties.description": "Defines the fields of the form.",
|
||||
"setting.frontMatter.data.files.items.properties.type.description": "If you are using data types, you can specify your type ID.",
|
||||
"setting.frontMatter.data.files.items.properties.singleEntry.description": "If you want to use a single entry for your data file.",
|
||||
"setting.frontMatter.data.folders.markdownDescription": "Specify the data folders you want to use for your website. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.data.folders)",
|
||||
"setting.frontMatter.data.folders.markdownDescription": "Specify the data folders you want to use for your website. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.data.folders) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.data.folders%22%5D)",
|
||||
"setting.frontMatter.data.folders.items.properties.id.description": "Your unique ID you want to use for your data folder.",
|
||||
"setting.frontMatter.data.folders.items.properties.labelField.description": "The field you want to use as label for your data entries.",
|
||||
"setting.frontMatter.data.folders.items.properties.path.description": "Path to the folder to load files.",
|
||||
"setting.frontMatter.data.folders.items.properties.type.description": "If you are using data types, you can specify your type ID.",
|
||||
"setting.frontMatter.data.folders.items.properties.singleEntry.description": "If you want to use a single entry for your data files in the folder.",
|
||||
"setting.frontMatter.data.types.markdownDescription": "Specify the data types. These types can be used in for your data files. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.data.types)",
|
||||
"setting.frontMatter.data.types.markdownDescription": "Specify the data types. These types can be used in for your data files. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.data.types) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.data.types%22%5D)",
|
||||
"setting.frontMatter.data.types.items.properties.id.description": "Your unique ID you want to use for your data type.",
|
||||
"setting.frontMatter.file.preserveCasing.markdownDescription": "Specify if you want to preserve the casing of your file names from the title. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.file.preservecasing)",
|
||||
"setting.frontMatter.framework.id.markdownDescription": "Specify the ID of your static site generator or framework you are using for your website. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.framework.id)",
|
||||
"setting.frontMatter.framework.startCommand.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/overview#frontmatter.framework.startcommand)",
|
||||
"setting.frontMatter.git.enabled.markdownDescription": "Specify if you want to use the Git actions for your website. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.enabled)",
|
||||
"setting.frontMatter.git.commitMessage.markdownDescription": "Specify the commit message you want to use for the sync. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.commitmessage)",
|
||||
"setting.frontMatter.git.submodule.pull.markdownDescription": "Specify if you want to pull submodules when syncing. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.submodule.pull)",
|
||||
"setting.frontMatter.git.submodule.push.markdownDescription": "Specify if you want to push submodules when syncing. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.submodule.push)",
|
||||
"setting.frontMatter.git.submodule.branch.markdownDescription": "Specify the submodule branch to checkout. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.submodule.branch)",
|
||||
"setting.frontMatter.git.submodule.folder.markdownDescription": "Specify the submodule folder of your content, this can be handy when you are using multiple submodules. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.submodule.folder)",
|
||||
"setting.frontMatter.global.activeMode.markdownDescription": "Specify the activated mode of Front Matter. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.global.activemode)",
|
||||
"setting.frontMatter.global.modes.markdownDescription": "Specify the modes you want to use for Front Matter. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.global.modes)",
|
||||
"setting.frontMatter.file.preserveCasing.markdownDescription": "Specify if you want to preserve the casing of your file names from the title. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.file.preservecasing) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.file.preservecasing%22%5D)",
|
||||
"setting.frontMatter.framework.id.markdownDescription": "Specify the ID of your static site generator or framework you are using for your website. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.framework.id) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.framework.id%22%5D)",
|
||||
"setting.frontMatter.framework.startCommand.markdownDescription": "Specify the command you want to use to start your static site generator or framework. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.framework.startcommand) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.framework.startcommand%22%5D)",
|
||||
"setting.frontMatter.git.enabled.markdownDescription": "Specify if you want to use the Git actions for your website. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.enabled) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.enabled%22%5D)",
|
||||
"setting.frontMatter.git.commitMessage.markdownDescription": "Specify the commit message you want to use for the sync. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.commitmessage) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.commitmessage%22%5D)",
|
||||
"setting.frontMatter.git.submodule.pull.markdownDescription": "Specify if you want to pull submodules when syncing. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.submodule.pull) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.submodule.pull%22%5D)",
|
||||
"setting.frontMatter.git.submodule.push.markdownDescription": "Specify if you want to push submodules when syncing. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.submodule.push) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.submodule.push%22%5D)",
|
||||
"setting.frontMatter.git.submodule.branch.markdownDescription": "Specify the submodule branch to checkout. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.submodule.branch) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.submodule.branch%22%5D)",
|
||||
"setting.frontMatter.git.submodule.folder.markdownDescription": "Specify the submodule folder of your content, this can be handy when you are using multiple submodules. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.submodule.folder) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.submodule.folder%22%5D)",
|
||||
"setting.frontMatter.global.activeMode.markdownDescription": "Specify the activated mode of Front Matter. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.global.activemode) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.global.activemode%22%5D)",
|
||||
"setting.frontMatter.global.modes.markdownDescription": "Specify the modes you want to use for Front Matter. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.global.modes) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.global.modes%22%5D)",
|
||||
"setting.frontMatter.global.modes.items.properties.id.description": "The ID of your mode.",
|
||||
"setting.frontMatter.global.modes.items.properties.features.description": "The features you want to use for your mode.",
|
||||
"setting.frontMatter.global.notifications.markdownDescription": "Specifies the notifications you want to see. By default, all notifications types will be shown. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.global.notifications)",
|
||||
"setting.frontMatter.global.disabledNotifications.markdownDescription": "This is an array with the notifications types that can be disabled for Front Matter CMS. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.global.disablednotifications)",
|
||||
"setting.frontMatter.media.defaultSorting.markdownDescription": "Specify the default sorting option for the media dashboard. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.media.defaultsorting)",
|
||||
"setting.frontMatter.media.supportedMimeTypes.markdownDescription": "Specify the mime types to support for the media files. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.media.supportedmimetypes)",
|
||||
"setting.frontMatter.global.notifications.markdownDescription": "Specifies the notifications you want to see. By default, all notifications types will be shown. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.global.notifications) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.global.notifications%22%5D)",
|
||||
"setting.frontMatter.global.disabledNotifications.markdownDescription": "This is an array with the notifications types that can be disabled for Front Matter CMS. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.global.disablednotifications) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.global.disablednotifications%22%5D)",
|
||||
"setting.frontMatter.media.defaultSorting.markdownDescription": "Specify the default sorting option for the media dashboard. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.media.defaultsorting) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.media.defaultsorting%22%5D)",
|
||||
"setting.frontMatter.media.supportedMimeTypes.markdownDescription": "Specify the mime types to support for the media files. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.media.supportedmimetypes) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.media.supportedmimetypes%22%5D)",
|
||||
|
||||
"setting.frontMatter.media.contentTypes.markdownDescription": "Specify the media content types you want to use in Front Matter. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.media.contenttypes)",
|
||||
"setting.frontMatter.media.contentTypes.markdownDescription": "Specify the media content types you want to use in Front Matter. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.media.contenttypes) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.media.contenttypes%22%5D)",
|
||||
"setting.frontMatter.media.contentTypes.items.description": "Define the media content types you want to use in Front Matter.",
|
||||
"setting.frontMatter.media.contentTypes.items.properties.name.description": "Name of the media content type",
|
||||
"setting.frontMatter.media.contentTypes.items.properties.fileTypes.description": "Specify the file types to allow for the media content type",
|
||||
@@ -172,16 +175,17 @@
|
||||
"setting.frontMatter.media.contentTypes.items.properties.fields.properties.type.description": "Define the type of field",
|
||||
"setting.frontMatter.media.contentTypes.items.properties.fields.properties.single.description": "Is a single line field",
|
||||
|
||||
"setting.frontMatter.panel.freeform.markdownDescription": "Specifies if you want to allow yourself from entering unknown tags/categories in the tag picker (when enabled, you will have the option to store them afterwards). Default: true. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.panel.freeform)",
|
||||
"setting.frontMatter.panel.actions.disabled.markdownDescription": "Specify the actions you want to disable in the panel. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.panel.actions.disabled)",
|
||||
"setting.frontMatter.preview.host.markdownDescription": "Specify the host URL (example: http://localhost:1313) to be used when opening the preview. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.preview.host)",
|
||||
"setting.frontMatter.preview.pathName.markdownDescription": "Specify the path you want to add after the host and before your slug. This can be used for instance to include the year/month like: `yyyy/MM`. The date will be generated based on the article its date field value. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.preview.pathname)",
|
||||
"setting.frontMatter.site.baseURL.markdownDescription": "Specify the base URL of your site, this will be used for SEO checks. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.site.baseurl)",
|
||||
"setting.frontMatter.taxonomy.alignFilename.markdownDescription": "Align the filename with the new slug when it gets generated. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.alignfilename)",
|
||||
"setting.frontMatter.taxonomy.categories.markdownDescription": "Specifies the categories which can be used in the Front Matter. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.categories)",
|
||||
"setting.frontMatter.taxonomy.commaSeparatedFields.markdownDescription": "Specify the fields names that Front Matter should treat as a comma-separated array. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.commaseparatedfields)",
|
||||
"setting.frontMatter.panel.freeform.markdownDescription": "Specifies if you want to allow yourself from entering unknown tags/categories in the tag picker (when enabled, you will have the option to store them afterwards). Default: true. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.panel.freeform) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.panel.freeform%22%5D)",
|
||||
"setting.frontMatter.panel.actions.disabled.markdownDescription": "Specify the actions you want to disable in the panel. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.panel.actions.disabled) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.panel.actions.disabled%22%5D)",
|
||||
"setting.frontMatter.preview.host.markdownDescription": "Specify the host URL (example: http://localhost:1313) to be used when opening the preview. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.preview.host) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.preview.host%22%5D)",
|
||||
"setting.frontMatter.preview.trailingSlash.markdownDescription": "Specify if you want to add a trailing slash to the preview URL. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.preview.trailingslash) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.preview.trailingslash%22%5D)",
|
||||
"setting.frontMatter.preview.pathName.markdownDescription": "Specify the path you want to add after the host and before your slug. This can be used for instance to include the year/month like: `yyyy/MM`. The date will be generated based on the article its date field value. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.preview.pathname) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.preview.pathname%22%5D)",
|
||||
"setting.frontMatter.site.baseURL.markdownDescription": "Specify the base URL of your site, this will be used for SEO checks. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.site.baseurl) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.site.baseurl%22%5D)",
|
||||
"setting.frontMatter.taxonomy.alignFilename.markdownDescription": "Align the filename with the new slug when it gets generated. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.alignfilename) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.alignfilename%22%5D)",
|
||||
"setting.frontMatter.taxonomy.categories.markdownDescription": "Specifies the categories which can be used in the Front Matter. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.categories) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.categories%22%5D)",
|
||||
"setting.frontMatter.taxonomy.commaSeparatedFields.markdownDescription": "Specify the fields names that Front Matter should treat as a comma-separated array. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.commaseparatedfields) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.commaseparatedfields%22%5D)",
|
||||
"setting.frontMatter.taxonomy.commaSeparatedFields.items.description": "Name of the fields you want to use as comma-separated arrays.",
|
||||
"setting.frontMatter.taxonomy.contentTypes.markdownDescription": "Specify the type of contents you want to use for your articles/pages/etc. Make sure the `type` is correctly set in your front matter. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.contenttypes)",
|
||||
"setting.frontMatter.taxonomy.contentTypes.markdownDescription": "Specify the type of contents you want to use for your articles/pages/etc. Make sure the `type` is correctly set in your front matter. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.contenttypes) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.contenttypes%22%5D)",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.description": "Define the content types you want to use in Front Matter.",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.name.description": "Define the type of field",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fileType.description": "Specifies the type of content you want to create.",
|
||||
@@ -229,50 +233,51 @@
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.when.properties.caseSensitive.description": "Specify if the comparison is case sensitive. Default: true",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.pageBundle.description": "Specify if you want to create a folder when creating new content.",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.previewPath.description": "Defines a custom preview path for the content type.",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.trailingSlash.description": "Specify if you want to add a trailing slash to the preview URL.",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.slugTemplate.description": "Defines a custom slug template for the content type.",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.template.description": "An optional template that can be used for creating new content.",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.postScript.description": "An optional post script that can be used after new content creation.",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.filePrefix.description": "Defines a prefix for the file name.",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.defaultFileName.description": "Default file name to use when creating new content.",
|
||||
"setting.frontMatter.taxonomy.customTaxonomy.markdownDescription": "Specify the custom taxonomy field data. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.tags)",
|
||||
"setting.frontMatter.taxonomy.customTaxonomy.markdownDescription": "Specify the custom taxonomy field data. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.tags) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.tags%22%5D)",
|
||||
"setting.frontMatter.taxonomy.customTaxonomy.items.properties.id.description": "ID for your taxonomy field. It cannot contain the \"tags\" or \"categories\" value.",
|
||||
"setting.frontMatter.taxonomy.customTaxonomy.items.properties.options.description": "Options from which you can pick.",
|
||||
"setting.frontMatter.taxonomy.dateField.markdownDescription": "This setting is used to define the publishing date field of your articles. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.datefield)",
|
||||
"setting.frontMatter.taxonomy.dateFormat.markdownDescription": "Specify the date format for your articles. Check [date-fns formating](https://date-fns.org/v2.0.1/docs/format) for more information. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.dateformat)",
|
||||
"setting.frontMatter.taxonomy.fieldGroups.markdownDescription": "Define the field groups you want to use for your block fields or collection fields. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.fieldgroups)",
|
||||
"setting.frontMatter.taxonomy.dateField.markdownDescription": "This setting is used to define the publishing date field of your articles. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.datefield) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.datefield%22%5D)",
|
||||
"setting.frontMatter.taxonomy.dateFormat.markdownDescription": "Specify the date format for your articles. Check [date-fns formating](https://date-fns.org/v2.0.1/docs/format) for more information. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.dateformat) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.dateformat%22%5D)",
|
||||
"setting.frontMatter.taxonomy.fieldGroups.markdownDescription": "Define the field groups you want to use for your block fields or collection fields. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.fieldgroups) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.fieldgroups%22%5D)",
|
||||
"setting.frontMatter.taxonomy.fieldGroups.items.properties.id.description": "The name of the field group",
|
||||
"setting.frontMatter.taxonomy.fieldGroups.items.properties.labelField.description": "The name of the field to be used as display value",
|
||||
"setting.frontMatter.taxonomy.frontMatterType.markdownDescription": "Specify the type of Front Matter to use. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.frontmattertype)",
|
||||
"setting.frontMatter.taxonomy.indentArrays.markdownDescription": "Specify if arrays in front matter are indented. Default: true. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.indentarrays)",
|
||||
"setting.frontMatter.taxonomy.modifiedField.markdownDescription": "This setting is used to define the modified date field of your articles. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.modifiedfield)",
|
||||
"setting.frontMatter.taxonomy.quoteStringValues.markdownDescription": "Specify if you want to quote string values in the front matter. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.quotestringvalues)",
|
||||
"setting.frontMatter.taxonomy.noPropertyValueQuotes.markdownDescription": "Specify the properties from which quotes need to be removed. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.nopropertyvaluequotes)",
|
||||
"setting.frontMatter.taxonomy.frontMatterType.markdownDescription": "Specify the type of Front Matter to use. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.frontmattertype) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.frontmattertype%22%5D)",
|
||||
"setting.frontMatter.taxonomy.indentArrays.markdownDescription": "Specify if arrays in front matter are indented. Default: true. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.indentarrays) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.indentarrays%22%5D)",
|
||||
"setting.frontMatter.taxonomy.modifiedField.markdownDescription": "This setting is used to define the modified date field of your articles. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.modifiedfield) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.modifiedfield%22%5D)",
|
||||
"setting.frontMatter.taxonomy.quoteStringValues.markdownDescription": "Specify if you want to quote string values in the front matter. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.quotestringvalues) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.quotestringvalues%22%5D)",
|
||||
"setting.frontMatter.taxonomy.noPropertyValueQuotes.markdownDescription": "Specify the properties from which quotes need to be removed. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.nopropertyvaluequotes) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.nopropertyvaluequotes%22%5D)",
|
||||
"setting.frontMatter.taxonomy.noPropertyValueQuotes.items.description": "Name of the properties you want to remove quotes from.",
|
||||
"setting.frontMatter.taxonomy.seoContentLengh.markdownDescription": "Specifies the optimal minimum length for your articles. Between 1,760 words – 2,400 is the absolute ideal article length for SEO in 2021. (set to `-1` to turn it off). [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.seocontentlengh)",
|
||||
"setting.frontMatter.taxonomy.seoDescriptionField.markdownDescription": "Specifies the name of the SEO description field for your page. Default is 'description'. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.seodescriptionfield)",
|
||||
"setting.frontMatter.taxonomy.seoDescriptionLength.markdownDescription": "Specifies the optimal description length for SEO (set to `-1` to turn it off). [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.seodescriptionlength)",
|
||||
"setting.frontMatter.taxonomy.seoSlugLength.markdownDescription": "Specifies the optimal slug length for SEO (set to `-1` to turn it off). [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.seosluglength)",
|
||||
"setting.frontMatter.taxonomy.seoTitleField.markdownDescription": "Specifies the name of the SEO title field for your page. Default is 'title'. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.seotitlefield)",
|
||||
"setting.frontMatter.taxonomy.seoTitleLength.markdownDescription": "Specifies the optimal title length for SEO (set to `-1` to turn it off). [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.seotitlelength)",
|
||||
"setting.frontMatter.taxonomy.slugPrefix.markdownDescription": "Specify a prefix for the slug. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.slugprefix)",
|
||||
"setting.frontMatter.taxonomy.slugSuffix.markdownDescription": "Specify a suffix for the slug. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.slugsuffix)",
|
||||
"setting.frontMatter.taxonomy.slugTemplate.markdownDescription": "Defines a custom slug template for the content you will create. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.slugtemplate)",
|
||||
"setting.frontMatter.taxonomy.tags.markdownDescription": "Specifies the tags which can be used in the Front Matter. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.tags)",
|
||||
"setting.frontMatter.telemetry.disable.markdownDescription": "Specify if you want to disable the telemetry. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.telemetry.disable)",
|
||||
"setting.frontMatter.templates.enabled.markdownDescription": "Specify if you want to use templates. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.templates.enabled)",
|
||||
"setting.frontMatter.templates.folder.markdownDescription": "Specify the folder to use for your article templates. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.templates.folder)",
|
||||
"setting.frontMatter.templates.prefix.markdownDescription": "Specify the prefix you want to add for your new article filenames. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.templates.prefix)",
|
||||
"setting.frontMatter.taxonomy.seoContentLengh.markdownDescription": "Specifies the optimal minimum length for your articles. Between 1,760 words – 2,400 is the absolute ideal article length for SEO in 2021. (set to `-1` to turn it off). [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.seocontentlengh) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.seocontentlengh%22%5D)",
|
||||
"setting.frontMatter.taxonomy.seoDescriptionField.markdownDescription": "Specifies the name of the SEO description field for your page. Default is 'description'. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.seodescriptionfield) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.seodescriptionfield%22%5D)",
|
||||
"setting.frontMatter.taxonomy.seoDescriptionLength.markdownDescription": "Specifies the optimal description length for SEO (set to `-1` to turn it off). [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.seodescriptionlength) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.seodescriptionlength%22%5D)",
|
||||
"setting.frontMatter.taxonomy.seoSlugLength.markdownDescription": "Specifies the optimal slug length for SEO (set to `-1` to turn it off). [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.seosluglength) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.seosluglength%22%5D)",
|
||||
"setting.frontMatter.taxonomy.seoTitleField.markdownDescription": "Specifies the name of the SEO title field for your page. Default is 'title'. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.seotitlefield) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.seotitlefield%22%5D)",
|
||||
"setting.frontMatter.taxonomy.seoTitleLength.markdownDescription": "Specifies the optimal title length for SEO (set to `-1` to turn it off). [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.seotitlelength) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.seotitlelength%22%5D)",
|
||||
"setting.frontMatter.taxonomy.slugPrefix.markdownDescription": "Specify a prefix for the slug. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.slugprefix) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.slugprefix%22%5D)",
|
||||
"setting.frontMatter.taxonomy.slugSuffix.markdownDescription": "Specify a suffix for the slug. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.slugsuffix) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.slugsuffix%22%5D)",
|
||||
"setting.frontMatter.taxonomy.slugTemplate.markdownDescription": "Defines a custom slug template for the content you will create. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.slugtemplate) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.slugtemplate%22%5D)",
|
||||
"setting.frontMatter.taxonomy.tags.markdownDescription": "Specifies the tags which can be used in the Front Matter. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.taxonomy.tags) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.taxonomy.tags%22%5D)",
|
||||
"setting.frontMatter.telemetry.disable.markdownDescription": "Specify if you want to disable the telemetry. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.telemetry.disable) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.telemetry.disable%22%5D)",
|
||||
"setting.frontMatter.templates.enabled.markdownDescription": "Specify if you want to use templates. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.templates.enabled) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.templates.enabled%22%5D)",
|
||||
"setting.frontMatter.templates.folder.markdownDescription": "Specify the folder to use for your article templates. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.templates.folder) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.templates.folder%22%5D)",
|
||||
"setting.frontMatter.templates.prefix.markdownDescription": "Specify the prefix you want to add for your new article filenames. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.templates.prefix) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.templates.prefix%22%5D)",
|
||||
"setting.frontMatter.dashboard.mediaSnippet.deprecationMessage": "This setting is deprecated and will be removed in the next major version. Please define your media snippet in the `frontMatter.content.snippet` setting.",
|
||||
"setting.frontMatter.taxonomy.dateField.deprecationMessage": "This setting is deprecated and will be removed in the next major version. Please use the new `isPublishDate` settings instead in your content types date fields.",
|
||||
"setting.frontMatter.taxonomy.modifiedField.deprecationMessage": "This setting is deprecated and will be removed in the next major version. Please use the new `isModifiedDate` settings instead in your content types date fields.",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.customType.description": "Specify the name of the custom field type to use.",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.clearEmpty.description": "Specify if the empty values should be cleared.",
|
||||
"setting.frontMatter.website.host.markdownDescription": "Specify the host URL of your website. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.website.url)",
|
||||
"setting.frontMatter.website.host.markdownDescription": "Specify the host URL of your website. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.website.url) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.website.url%22%5D)",
|
||||
"command.frontMatter.settings.refresh": "Refresh Front Matter Settings",
|
||||
"setting.frontMatter.config.dynamicFilePath.markdownDescription": "Specify the path to the dynamic config file (ex: [[workspace]]/config.js). [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.config.dynamicfilepath)",
|
||||
"setting.frontMatter.config.dynamicFilePath.markdownDescription": "Specify the path to the dynamic config file (ex: [[workspace]]/config.js). [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.config.dynamicfilepath) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.config.dynamicfilepath%22%5D)",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.allowAsSubContent.description": "Specify if the content type can be used as sub content.",
|
||||
"setting.frontMatter.taxonomy.contentTypes.items.properties.isSubContent.description": "Specify if the content type is sub content.",
|
||||
|
||||
"setting.frontMatter.git.disableOnBranches.markdownDescription": "Specify the branches on which you want to disable the Git actions. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.disableonbranches)",
|
||||
"setting.frontMatter.git.requiresCommitMessage.markdownDescription": "Specify if you want to require a commit message when publishing your changes for a specified branch. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.requirescommitmessage)"
|
||||
"setting.frontMatter.git.disableOnBranches.markdownDescription": "Specify the branches on which you want to disable the Git actions. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.disableonbranches) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.disableonbranches%22%5D)",
|
||||
"setting.frontMatter.git.requiresCommitMessage.markdownDescription": "Specify if you want to require a commit message when publishing your changes for a specified branch. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.requirescommitmessage) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.requirescommitmessage%22%5D)"
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import { CustomPlaceholder, Field } from '../models';
|
||||
import { format } from 'date-fns';
|
||||
import {
|
||||
ArticleHelper,
|
||||
Logger,
|
||||
Settings,
|
||||
SlugHelper,
|
||||
processArticlePlaceholdersFromData,
|
||||
@@ -80,7 +81,7 @@ export class Article {
|
||||
return;
|
||||
}
|
||||
|
||||
article = this.updateDate(article);
|
||||
article = await this.updateDate(article);
|
||||
|
||||
try {
|
||||
ArticleHelper.update(editor, article);
|
||||
@@ -95,8 +96,8 @@ export class Article {
|
||||
* Update the date in the front matter
|
||||
* @param article
|
||||
*/
|
||||
public static updateDate(article: ParsedFrontMatter) {
|
||||
article.data = ArticleHelper.updateDates(article);
|
||||
public static async updateDate(article: ParsedFrontMatter) {
|
||||
article.data = await ArticleHelper.updateDates(article);
|
||||
return article;
|
||||
}
|
||||
|
||||
@@ -109,7 +110,7 @@ export class Article {
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedArticle = this.setLastModifiedDateInner(editor.document);
|
||||
const updatedArticle = await this.setLastModifiedDateInner(editor.document);
|
||||
|
||||
if (typeof updatedArticle === 'undefined') {
|
||||
return;
|
||||
@@ -119,7 +120,7 @@ export class Article {
|
||||
}
|
||||
|
||||
public static async setLastModifiedDateOnSave(document: TextDocument): Promise<TextEdit[]> {
|
||||
const updatedArticle = this.setLastModifiedDateInner(document);
|
||||
const updatedArticle = await this.setLastModifiedDateInner(document);
|
||||
|
||||
if (typeof updatedArticle === 'undefined') {
|
||||
return [];
|
||||
@@ -130,7 +131,10 @@ export class Article {
|
||||
return [update];
|
||||
}
|
||||
|
||||
private static setLastModifiedDateInner(document: TextDocument): ParsedFrontMatter | undefined {
|
||||
private static async setLastModifiedDateInner(
|
||||
document: TextDocument
|
||||
): Promise<ParsedFrontMatter | undefined> {
|
||||
Logger.verbose(`Article:setLastModifiedDateInner:Start`);
|
||||
const article = ArticleHelper.getFrontMatterFromDocument(document);
|
||||
|
||||
// Only set the date, if there is already front matter set
|
||||
@@ -139,10 +143,17 @@ export class Article {
|
||||
}
|
||||
|
||||
const cloneArticle = Object.assign({}, article);
|
||||
const dateField = ArticleHelper.getModifiedDateField(article);
|
||||
const dateField = await ArticleHelper.getModifiedDateField(article);
|
||||
Logger.verbose(`Article:setLastModifiedDateInner:DateField - ${JSON.stringify(dateField)}`);
|
||||
|
||||
try {
|
||||
const fieldName = dateField?.name || DefaultFields.LastModified;
|
||||
cloneArticle.data[fieldName] = Article.formatDate(new Date(), dateField?.dateFormat);
|
||||
const fieldValue = Article.formatDate(new Date(), dateField?.dateFormat);
|
||||
cloneArticle.data[fieldName] = fieldValue;
|
||||
Logger.verbose(
|
||||
`Article:setLastModifiedDateInner:DateField name - ${fieldName} - value - ${fieldValue}`
|
||||
);
|
||||
Logger.verbose(`Article:setLastModifiedDateInner:End`);
|
||||
return cloneArticle;
|
||||
} catch (e: unknown) {
|
||||
Notifications.error(
|
||||
@@ -195,8 +206,12 @@ export class Article {
|
||||
}
|
||||
|
||||
let filePrefix = Settings.get<string>(SETTING_TEMPLATES_PREFIX);
|
||||
const contentType = ArticleHelper.getContentType(article);
|
||||
filePrefix = ArticleHelper.getFilePrefix(filePrefix, editor.document.uri.fsPath, contentType);
|
||||
const contentType = await ArticleHelper.getContentType(article);
|
||||
filePrefix = await ArticleHelper.getFilePrefix(
|
||||
filePrefix,
|
||||
editor.document.uri.fsPath,
|
||||
contentType
|
||||
);
|
||||
|
||||
const titleField = 'title';
|
||||
const articleTitle: string = article.data[titleField];
|
||||
@@ -279,12 +294,19 @@ export class Article {
|
||||
/**
|
||||
* Retrieve the slug from the front matter
|
||||
*/
|
||||
public static getSlug() {
|
||||
public static getSlug(pathname?: string) {
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const file = parseWinPath(editor.document.fileName);
|
||||
if (!isValidFile(file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedFile = parse(file);
|
||||
|
||||
const slugTemplate = Settings.get<string>(SETTING_SLUG_TEMPLATE);
|
||||
if (slugTemplate) {
|
||||
if (slugTemplate === '{{title}}') {
|
||||
@@ -300,16 +322,15 @@ export class Article {
|
||||
}
|
||||
}
|
||||
|
||||
const file = parseWinPath(editor.document.fileName);
|
||||
|
||||
if (!isValidFile(file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedFile = parse(file);
|
||||
const suffix = Settings.get(SETTING_SLUG_SUFFIX) as string;
|
||||
const prefix = Settings.get(SETTING_SLUG_PREFIX) as string;
|
||||
|
||||
if (parsedFile.name.toLowerCase() !== 'index') {
|
||||
return parsedFile.name;
|
||||
return `${prefix}${parsedFile.name}${suffix}`;
|
||||
}
|
||||
|
||||
if (parsedFile.name.toLowerCase() === 'index' && pathname) {
|
||||
return ``;
|
||||
}
|
||||
|
||||
const folderName = basename(dirname(file));
|
||||
@@ -344,7 +365,7 @@ export class Article {
|
||||
const autoUpdate = Settings.get(SETTING_AUTO_UPDATE_DATE);
|
||||
|
||||
// Is article located in one of the content folders
|
||||
const folders = Folders.get();
|
||||
const folders = Folders.getCached();
|
||||
const documentPath = parseWinPath(document.fileName);
|
||||
const folder = folders.find((f) => documentPath.startsWith(f.path));
|
||||
if (!folder) {
|
||||
@@ -363,11 +384,16 @@ export class Article {
|
||||
public static formatDate(dateValue: Date, fieldDateFormat?: string): string {
|
||||
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
|
||||
|
||||
Logger.verbose(`Article:formatDate:Start`);
|
||||
|
||||
if (fieldDateFormat) {
|
||||
Logger.verbose(`Article:formatDate:FieldDateFormat - ${fieldDateFormat}`);
|
||||
return format(dateValue, DateHelper.formatUpdate(fieldDateFormat) as string);
|
||||
} else if (dateFormat && typeof dateFormat === 'string') {
|
||||
Logger.verbose(`Article:formatDate:DateFormat - ${dateFormat}`);
|
||||
return format(dateValue, DateHelper.formatUpdate(dateFormat) as string);
|
||||
} else {
|
||||
Logger.verbose(`Article:formatDate:toISOString - ${dateValue}`);
|
||||
return typeof dateValue.toISOString === 'function'
|
||||
? dateValue.toISOString()
|
||||
: dateValue?.toString();
|
||||
@@ -385,7 +411,7 @@ export class Article {
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
const contentType =
|
||||
article && article.data ? ArticleHelper.getContentType(article) : DEFAULT_CONTENT_TYPE;
|
||||
article && article.data ? await ArticleHelper.getContentType(article) : DEFAULT_CONTENT_TYPE;
|
||||
|
||||
const position = editor.selection.active;
|
||||
const selectionText = editor.document.getText(editor.selection);
|
||||
@@ -463,7 +489,7 @@ export class Article {
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
const contentType = article ? ArticleHelper.getContentType(article) : undefined;
|
||||
const contentType = article ? await ArticleHelper.getContentType(article) : undefined;
|
||||
|
||||
await commands.executeCommand(COMMAND_NAME.dashboard, {
|
||||
type: NavigationType.Snippets,
|
||||
|
||||
@@ -1,10 +1,25 @@
|
||||
import { commands, QuickPickItem, window } from 'vscode';
|
||||
import { COMMAND_NAME, SETTING_TEMPLATES_ENABLED } from '../constants';
|
||||
import { Settings } from '../helpers';
|
||||
import { Extension, Settings } from '../helpers';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../localization';
|
||||
|
||||
export class Content {
|
||||
/**
|
||||
* Registers the commands for the Content class.
|
||||
*/
|
||||
public static async registerCommands() {
|
||||
const ext = Extension.getInstance();
|
||||
const subscriptions = ext.subscriptions;
|
||||
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.createContent, Content.create));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates content based on user selection.
|
||||
* If templates are enabled, shows a quick pick menu to choose between content type and template.
|
||||
* If templates are disabled, executes the createByContentType command directly.
|
||||
*/
|
||||
public static async create() {
|
||||
const templatesEnabled = await Settings.get(SETTING_TEMPLATES_ENABLED);
|
||||
if (!templatesEnabled) {
|
||||
|
||||
@@ -34,6 +34,7 @@ import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../localization';
|
||||
import { DashboardMessage } from '../dashboardWebView/DashboardMessage';
|
||||
import { NavigationType } from '../dashboardWebView/models';
|
||||
import { ignoreMsgCommand } from '../utils';
|
||||
|
||||
export class Dashboard {
|
||||
private static webview: WebviewPanel | null = null;
|
||||
@@ -44,6 +45,13 @@ export class Dashboard {
|
||||
return Dashboard._viewData;
|
||||
}
|
||||
|
||||
public static setTitle(title: string) {
|
||||
if (title && Dashboard.webview) {
|
||||
Dashboard.webview.title =
|
||||
title || `Front Matter ${l10n.t(LocalizationKey.commandsDashboardTitle)}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the dashboard
|
||||
*/
|
||||
@@ -222,7 +230,9 @@ export class Dashboard {
|
||||
});
|
||||
|
||||
Dashboard.webview.webview.onDidReceiveMessage(async (msg) => {
|
||||
Logger.info(`Receiving message from webview: ${msg.command}`);
|
||||
if (!ignoreMsgCommand(msg.command)) {
|
||||
Logger.verbose(`Receiving message from dashboard: ${msg.command}`);
|
||||
}
|
||||
|
||||
LocalizationListener.process(msg);
|
||||
DashboardListener.process(msg);
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
import { Folders } from './Folders';
|
||||
import { ViewColumn, workspace } from 'vscode';
|
||||
import { ViewColumn, commands, workspace } from 'vscode';
|
||||
import ContentProvider from '../providers/ContentProvider';
|
||||
import { join } from 'path';
|
||||
import { ContentFolder } from '../models';
|
||||
import { Settings } from '../helpers/SettingsHelper';
|
||||
import {
|
||||
COMMAND_NAME,
|
||||
DEFAULT_FILE_TYPES,
|
||||
SETTING_CONTENT_SUPPORTED_FILETYPES
|
||||
} from '../constants';
|
||||
import { Extension } from '../helpers';
|
||||
|
||||
export class Diagnostics {
|
||||
public static async registerCommands() {
|
||||
const ext = Extension.getInstance();
|
||||
const subscriptions = ext.subscriptions;
|
||||
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.diagnostics, Diagnostics.show));
|
||||
}
|
||||
|
||||
public static async show() {
|
||||
const folders = Folders.get();
|
||||
const folders = await Folders.get();
|
||||
const projectName = Folders.getProjectFolderName();
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
|
||||
@@ -18,27 +31,38 @@ export class Diagnostics {
|
||||
|
||||
const all = await Diagnostics.allProjectFiles();
|
||||
|
||||
const logging = `# Project name
|
||||
const fileTypes = Diagnostics.getFileTypes();
|
||||
|
||||
const logging = `# ${Extension.getInstance().displayName} - Diagnostics
|
||||
|
||||
Beta: \`${Extension.getInstance().isBetaVersion()}\`
|
||||
Version: \`${Extension.getInstance().version}\`
|
||||
|
||||
## Project name
|
||||
|
||||
${projectName}
|
||||
|
||||
# Folders
|
||||
## Workspace folder
|
||||
|
||||
${folders.map((f) => `- ${f.title}: "${f.path}"`).join('\n')}
|
||||
\`${wsFolder ? wsFolder.fsPath : 'No workspace folder'}\`
|
||||
|
||||
# Workspace folder
|
||||
|
||||
${wsFolder ? wsFolder.fsPath : 'No workspace folder'}
|
||||
|
||||
# Total files
|
||||
## Total files
|
||||
|
||||
${all}
|
||||
|
||||
# Folders to search files
|
||||
## Folders
|
||||
|
||||
| Title | Path |
|
||||
| ----- | ---- |
|
||||
${folders.map((f) => `| ${f.title} | \`${f.path}\` |`).join('\n')}
|
||||
|
||||
### Files in folders
|
||||
|
||||
| Project start length | Search in | ${fileTypes.join(` | `)} |
|
||||
|--- | --- | --- | --- | --- |
|
||||
${folderData.join('\n')}
|
||||
|
||||
# Complete frontmatter.json config
|
||||
## Complete frontmatter.json config
|
||||
|
||||
\`\`\`json
|
||||
${JSON.stringify(Settings.globalConfig, null, 2)}
|
||||
@@ -48,8 +72,12 @@ ${JSON.stringify(Settings.globalConfig, null, 2)}
|
||||
ContentProvider.show(logging, `${projectName} diagnostics`, 'markdown', ViewColumn.One);
|
||||
}
|
||||
|
||||
private static getFileTypes = (): string[] => {
|
||||
return Settings.get<string[]>(SETTING_CONTENT_SUPPORTED_FILETYPES) || DEFAULT_FILE_TYPES;
|
||||
};
|
||||
|
||||
private static async allProjectFiles() {
|
||||
const allFiles = await workspace.findFiles(`**/*.*`);
|
||||
const allFiles = await workspace.findFiles(`**/*.*`, '**/node_modules/**');
|
||||
return `Total files found: ${allFiles.length}`;
|
||||
}
|
||||
|
||||
@@ -59,22 +87,19 @@ ${JSON.stringify(Settings.globalConfig, null, 2)}
|
||||
projectStart = projectStart?.replace(/\\/g, '/');
|
||||
projectStart = projectStart?.startsWith('/') ? projectStart.substring(1) : projectStart;
|
||||
|
||||
const mdFiles = await workspace.findFiles(
|
||||
join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.md')
|
||||
);
|
||||
const mdxFiles = await workspace.findFiles(
|
||||
join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.mdx')
|
||||
);
|
||||
const markdownFiles = await workspace.findFiles(
|
||||
join(projectStart, folder.excludeSubdir ? '/' : '**/', '*.markdown')
|
||||
const fileTypes = Diagnostics.getFileTypes();
|
||||
const fileTypeLengths = await Promise.all(
|
||||
fileTypes.map(async (ft) => {
|
||||
const path = join(projectStart || '', folder.excludeSubdir ? '/' : '**/', `*.${ft}`);
|
||||
const files = await workspace.findFiles(path, '**/node_modules/**');
|
||||
return (files || []).length;
|
||||
})
|
||||
);
|
||||
|
||||
return `- Project start length: ${projectStart.length} | Search in: "${join(
|
||||
return `| ${projectStart.length} | \`${join(
|
||||
projectStart,
|
||||
folder.excludeSubdir ? '/' : '**/',
|
||||
'*.*'
|
||||
)}" | mdFiles: ${mdFiles.length} | mdxFiles: ${mdxFiles.length} | markdownFiles: ${
|
||||
markdownFiles.length
|
||||
}`;
|
||||
)}\` | ${fileTypeLengths.join(` | `)} |`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { STATIC_FOLDER_PLACEHOLDER } from './../constants/StaticFolderPlaceholder';
|
||||
import { Questions } from './../helpers/Questions';
|
||||
import {
|
||||
COMMAND_NAME,
|
||||
SETTING_CONTENT_I18N,
|
||||
SETTING_CONTENT_PAGE_FOLDERS,
|
||||
SETTING_CONTENT_STATIC_FOLDER,
|
||||
@@ -10,11 +11,18 @@ import {
|
||||
} from './../constants';
|
||||
import { commands, Uri, workspace, window } from 'vscode';
|
||||
import { basename, dirname, join, relative, sep } from 'path';
|
||||
import { ContentFolder, FileInfo, FolderInfo, I18nConfig, StaticFolder } from '../models';
|
||||
import {
|
||||
ContentFolder,
|
||||
ContentType,
|
||||
FileInfo,
|
||||
FolderInfo,
|
||||
I18nConfig,
|
||||
StaticFolder
|
||||
} from '../models';
|
||||
import uniqBy = require('lodash.uniqby');
|
||||
import { Template } from './Template';
|
||||
import { Notifications } from '../helpers/Notifications';
|
||||
import { Logger, Settings, processTimePlaceholders } from '../helpers';
|
||||
import { Extension, Logger, Settings, processTimePlaceholders } from '../helpers';
|
||||
import { existsSync } from 'fs';
|
||||
import { format } from 'date-fns';
|
||||
import { Dashboard } from './Dashboard';
|
||||
@@ -25,13 +33,28 @@ import { DEFAULT_FILE_TYPES } from '../constants/DefaultFileTypes';
|
||||
import { Telemetry } from '../helpers/Telemetry';
|
||||
import { glob } from 'glob';
|
||||
import { mkdirAsync } from '../utils/mkdirAsync';
|
||||
import { existsAsync } from '../utils';
|
||||
import { existsAsync, isWindows } from '../utils';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../localization';
|
||||
import { Preview } from './Preview';
|
||||
|
||||
export const WORKSPACE_PLACEHOLDER = `[[workspace]]`;
|
||||
|
||||
export class Folders {
|
||||
private static _folders: ContentFolder[] = [];
|
||||
|
||||
public static async registerCommands() {
|
||||
const ext = Extension.getInstance();
|
||||
const subscriptions = ext.subscriptions;
|
||||
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.createByTemplate, Folders.create));
|
||||
}
|
||||
|
||||
public static clearCached() {
|
||||
Logger.verbose(`Folders:clearCached`);
|
||||
Folders._folders = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a media folder
|
||||
* @returns
|
||||
@@ -98,7 +121,8 @@ export class Folders {
|
||||
return;
|
||||
}
|
||||
|
||||
const folders = Folders.get().filter((f) => !f.disableCreation);
|
||||
let folders = await Folders.get();
|
||||
folders = folders.filter((f) => !f.disableCreation);
|
||||
const location = folders.find((f) => f.path === selectedFolder.path);
|
||||
if (location) {
|
||||
const folderPath = Folders.getFolderPath(Uri.file(location.path));
|
||||
@@ -122,7 +146,7 @@ export class Folders {
|
||||
if (folder && folder.fsPath) {
|
||||
const wslPath = folder.fsPath.replace(/\//g, '\\');
|
||||
|
||||
let folders = Folders.get();
|
||||
let folders = await Folders.get();
|
||||
|
||||
const exists = folders.find(
|
||||
(f) => f.path.includes(folder.fsPath) || f.path.includes(wslPath)
|
||||
@@ -171,7 +195,7 @@ export class Folders {
|
||||
*/
|
||||
public static async unregister(folder: Uri) {
|
||||
if (folder && folder.path) {
|
||||
let folders = Folders.get();
|
||||
let folders = await Folders.get();
|
||||
folders = folders.filter((f) => f.path !== folder.fsPath);
|
||||
await Folders.update(folders);
|
||||
|
||||
@@ -282,8 +306,9 @@ export class Folders {
|
||||
* Get the registered folders information
|
||||
*/
|
||||
public static async getInfo(limit?: number): Promise<FolderInfo[] | null> {
|
||||
Logger.verbose('Folders:getInfo:start');
|
||||
const supportedFiles = Settings.get<string[]>(SETTING_CONTENT_SUPPORTED_FILETYPES);
|
||||
const folders = Folders.get();
|
||||
const folders = await Folders.get();
|
||||
|
||||
if (folders && folders.length > 0) {
|
||||
const folderInfo: FolderInfo[] = [];
|
||||
@@ -295,9 +320,11 @@ export class Folders {
|
||||
}
|
||||
}
|
||||
|
||||
Logger.verbose('Folders:getInfo:end');
|
||||
return folderInfo;
|
||||
}
|
||||
|
||||
Logger.verbose('Folders:getInfo:end - no folders found');
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -305,7 +332,14 @@ export class Folders {
|
||||
* Get the folder settings
|
||||
* @returns
|
||||
*/
|
||||
public static get(): ContentFolder[] {
|
||||
public static async get(): Promise<ContentFolder[]> {
|
||||
Logger.verbose('Folders:get:start');
|
||||
|
||||
if (Folders._folders.length > 0) {
|
||||
Logger.verbose('Folders:get:end - cached folders');
|
||||
return Folders._folders;
|
||||
}
|
||||
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
let folders: ContentFolder[] = Settings.get(SETTING_CONTENT_PAGE_FOLDERS) as ContentFolder[];
|
||||
const i18nSettings = Settings.get<I18nConfig[]>(SETTING_CONTENT_I18N);
|
||||
@@ -322,7 +356,7 @@ export class Folders {
|
||||
folders = folders.filter((f) => f.path !== folder.path);
|
||||
|
||||
const folderPath = Folders.absWsFolder(folder, wsFolder);
|
||||
const subFolders = glob.sync(folderPath, { ignore: '**/node_modules/**' });
|
||||
const subFolders = await Folders.findFolders(folderPath);
|
||||
for (const subFolder of subFolders) {
|
||||
const subFolderPath = parseWinPath(subFolder);
|
||||
|
||||
@@ -353,11 +387,11 @@ export class Folders {
|
||||
),
|
||||
l10n.t(LocalizationKey.commandsFoldersGetNotificationErrorRemoveAction),
|
||||
l10n.t(LocalizationKey.commandsFoldersGetNotificationErrorCreateAction)
|
||||
).then((answer) => {
|
||||
).then(async (answer) => {
|
||||
if (
|
||||
answer === l10n.t(LocalizationKey.commandsFoldersGetNotificationErrorRemoveAction)
|
||||
) {
|
||||
const folders = Folders.get();
|
||||
const folders = await Folders.get();
|
||||
Folders.update(folders.filter((f) => f.path !== folder.path));
|
||||
} else if (
|
||||
answer === l10n.t(LocalizationKey.commandsFoldersGetNotificationErrorCreateAction)
|
||||
@@ -417,7 +451,17 @@ export class Folders {
|
||||
}
|
||||
});
|
||||
|
||||
return contentFolders.filter((folder) => folder !== null) as ContentFolder[];
|
||||
Logger.verbose('Folders:get:end');
|
||||
Folders._folders = contentFolders.filter((folder) => folder !== null) as ContentFolder[];
|
||||
return Folders._folders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cached folder settings
|
||||
* @returns {ContentFolder[]} - The cached folder settings
|
||||
*/
|
||||
public static getCached(): ContentFolder[] {
|
||||
return Folders._folders;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -439,9 +483,8 @@ export class Folders {
|
||||
path: Folders.relWsFolder(folder, wsFolder)
|
||||
};
|
||||
|
||||
if (detail['$schema'] || detail.extended) {
|
||||
return null;
|
||||
}
|
||||
delete detail['$schema'];
|
||||
delete detail.extended;
|
||||
|
||||
if (detail.locale && detail.locale === detail.defaultLocale) {
|
||||
// Check if the folder was on the original list
|
||||
@@ -462,7 +505,7 @@ export class Folders {
|
||||
})
|
||||
.filter((folder) => folder !== null);
|
||||
|
||||
await Settings.update(SETTING_CONTENT_PAGE_FOLDERS, folderDetails, true);
|
||||
await Settings.safeUpdate(SETTING_CONTENT_PAGE_FOLDERS, folderDetails, true);
|
||||
|
||||
// Reinitialize the folder listeners
|
||||
PagesListener.startWatchers();
|
||||
@@ -475,11 +518,10 @@ export class Folders {
|
||||
*/
|
||||
public static getAbsFilePath(filePath: string): string {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
||||
if (filePath.includes(WORKSPACE_PLACEHOLDER)) {
|
||||
let absPath = filePath.replace(WORKSPACE_PLACEHOLDER, parseWinPath(wsFolder?.fsPath || ''));
|
||||
absPath = isWindows ? absPath.split('/').join('\\') : absPath;
|
||||
absPath = isWindows() ? absPath.split('/').join('\\') : absPath;
|
||||
return parseWinPath(absPath);
|
||||
}
|
||||
|
||||
@@ -493,7 +535,6 @@ export class Folders {
|
||||
*/
|
||||
public static getAbsFolderPath(folderPath: string): string {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
||||
let absPath = '';
|
||||
if (folderPath.includes(WORKSPACE_PLACEHOLDER)) {
|
||||
@@ -502,7 +543,7 @@ export class Folders {
|
||||
absPath = join(parseWinPath(wsFolder?.fsPath || ''), folderPath);
|
||||
}
|
||||
|
||||
absPath = isWindows ? absPath.split('/').join('\\') : absPath;
|
||||
absPath = isWindows() ? absPath.split('/').join('\\') : absPath;
|
||||
return parseWinPath(absPath);
|
||||
}
|
||||
|
||||
@@ -513,14 +554,13 @@ export class Folders {
|
||||
* @returns
|
||||
*/
|
||||
private static absWsFolder(folder: ContentFolder, wsFolder?: Uri) {
|
||||
const isWindows = process.platform === 'win32';
|
||||
let absPath = folder.path.replace(WORKSPACE_PLACEHOLDER, parseWinPath(wsFolder?.fsPath || ''));
|
||||
|
||||
if (absPath.includes('../')) {
|
||||
absPath = join(absPath);
|
||||
}
|
||||
|
||||
absPath = isWindows ? absPath.split('/').join('\\') : absPath;
|
||||
absPath = isWindows() ? absPath.split('/').join('\\') : absPath;
|
||||
|
||||
return parseWinPath(absPath);
|
||||
}
|
||||
@@ -532,12 +572,11 @@ export class Folders {
|
||||
* @returns
|
||||
*/
|
||||
public static relWsFolder(folder: ContentFolder, wsFolder?: Uri) {
|
||||
const isWindows = process.platform === 'win32';
|
||||
let absPath = parseWinPath(folder.path).replace(
|
||||
parseWinPath(wsFolder?.fsPath || ''),
|
||||
WORKSPACE_PLACEHOLDER
|
||||
);
|
||||
absPath = isWindows ? absPath.split('\\').join('/') : absPath;
|
||||
absPath = isWindows() ? absPath.split('\\').join('/') : absPath;
|
||||
return absPath;
|
||||
}
|
||||
|
||||
@@ -545,9 +584,11 @@ export class Folders {
|
||||
* Find the content folders
|
||||
*/
|
||||
public static async getContentFolders() {
|
||||
Logger.verbose('Folders:getContentFolders:start');
|
||||
// Find folders that contain files
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
if (!wsFolder) {
|
||||
Logger.error('Folders:getContentFolders:workspaceFolderNotFound');
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -568,7 +609,7 @@ export class Folders {
|
||||
folders = [...folders, ...(await this.findFolders(pattern))];
|
||||
} catch (e) {
|
||||
Logger.error(
|
||||
`Something went wrong while searching for folders with pattern "${pattern}": ${
|
||||
`Folders:getContentFolders:error: Something went wrong while searching for folders with pattern "${pattern}": ${
|
||||
(e as Error).message
|
||||
}`
|
||||
);
|
||||
@@ -581,6 +622,8 @@ export class Folders {
|
||||
}
|
||||
|
||||
const uniqueFolders = [...new Set(folders)];
|
||||
|
||||
Logger.verbose('Folders:getContentFolders:end');
|
||||
return uniqueFolders.map((folder) => relative(wsFolder?.path || '', folder));
|
||||
}
|
||||
|
||||
@@ -589,8 +632,8 @@ export class Folders {
|
||||
* @param folderPath
|
||||
* @returns
|
||||
*/
|
||||
public static getFilePrefixByFolderPath(folderPath: string) {
|
||||
const folders = Folders.get();
|
||||
public static async getFilePrefixByFolderPath(folderPath: string) {
|
||||
const folders = await Folders.get();
|
||||
const pageFolder = folders.find((f) => parseWinPath(f.path) === parseWinPath(folderPath));
|
||||
|
||||
if (pageFolder && typeof pageFolder.filePrefix !== 'undefined') {
|
||||
@@ -605,8 +648,8 @@ export class Folders {
|
||||
* @param filePath
|
||||
* @returns
|
||||
*/
|
||||
public static getFilePrefixBeFilePath(filePath: string) {
|
||||
const folders = Folders.get();
|
||||
public static async getFilePrefixBeFilePath(filePath: string): Promise<string | undefined> {
|
||||
const folders = await Folders.get();
|
||||
if (folders.length > 0) {
|
||||
filePath = parseWinPath(filePath);
|
||||
|
||||
@@ -634,8 +677,10 @@ export class Folders {
|
||||
* @param filePath - The file path to match against the page folders.
|
||||
* @returns The page folder that matches the file path, or undefined if no match is found.
|
||||
*/
|
||||
public static getPageFolderByFilePath(filePath: string): ContentFolder | undefined {
|
||||
const folders = Folders.get();
|
||||
public static async getPageFolderByFilePath(
|
||||
filePath: string
|
||||
): Promise<ContentFolder | undefined> {
|
||||
const folders = Folders.getCached();
|
||||
const parsedPath = parseWinPath(filePath);
|
||||
const pageFolderMatches = folders
|
||||
.filter((folder) => parsedPath && folder.path && parsedPath.includes(folder.path))
|
||||
@@ -648,6 +693,70 @@ export class Folders {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the folder associated with the specified content type and file path.
|
||||
* If a single matching folder is found, it is returned. If multiple matching folders are found,
|
||||
* the user is prompted to select one. If no matching folders are found, the user is prompted to
|
||||
* select a folder with a preview path.
|
||||
*
|
||||
* @param contentType - The content type to match.
|
||||
* @param filePath - The file path to match.
|
||||
* @returns A Promise that resolves to the selected ContentFolder, or undefined if no matching folder is found.
|
||||
*/
|
||||
public static async getFolderByContentType(
|
||||
contentType: ContentType,
|
||||
filePath: string
|
||||
): Promise<ContentFolder | undefined> {
|
||||
if (!contentType) {
|
||||
return;
|
||||
}
|
||||
|
||||
const folders = Folders.getCached();
|
||||
let selectedFolder: ContentFolder | undefined;
|
||||
|
||||
// Try to find the folder by content type
|
||||
let crntFolders = folders.filter(
|
||||
(folder) =>
|
||||
folder.contentTypes?.includes((contentType as ContentType).name) && folder.previewPath
|
||||
);
|
||||
|
||||
// Use file path to find the folder
|
||||
if (crntFolders.length > 0) {
|
||||
crntFolders = crntFolders.filter((folder) => filePath?.startsWith(folder.path));
|
||||
}
|
||||
|
||||
if (crntFolders && crntFolders.length === 1) {
|
||||
selectedFolder = crntFolders[0];
|
||||
} else if (crntFolders && crntFolders.length > 1) {
|
||||
selectedFolder = await Preview.askUserToPickFolder(crntFolders);
|
||||
} else {
|
||||
selectedFolder = await Preview.askUserToPickFolder(folders.filter((f) => f.previewPath));
|
||||
}
|
||||
|
||||
return selectedFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the file stats for a given file.
|
||||
* @param file - The URI of the file.
|
||||
* @param folderPath - The path of the folder containing the file.
|
||||
* @returns An object containing the file path, file name, folder name, folder path, and file stats.
|
||||
*/
|
||||
public static async getFileStats(file: Uri, folderPath: string) {
|
||||
const fileName = basename(file.fsPath);
|
||||
const folderName = dirname(file.fsPath).split(sep).pop();
|
||||
|
||||
const stats = await workspace.fs.stat(file);
|
||||
|
||||
return {
|
||||
filePath: file.fsPath,
|
||||
fileName,
|
||||
folderName,
|
||||
folderPath,
|
||||
...stats
|
||||
};
|
||||
}
|
||||
|
||||
private static async getFilesByFolder(
|
||||
folder: ContentFolder,
|
||||
supportedFiles: string[] | undefined,
|
||||
@@ -655,6 +764,7 @@ export class Folders {
|
||||
): Promise<FolderInfo | undefined> {
|
||||
try {
|
||||
const folderPath = parseWinPath(folder.path);
|
||||
const folderUri = Uri.file(folderPath);
|
||||
|
||||
if (typeof folderPath === 'string') {
|
||||
let files: Uri[] = [];
|
||||
@@ -673,7 +783,9 @@ export class Folders {
|
||||
let foundFiles = await Folders.findFiles(filePath);
|
||||
|
||||
// Make sure these file are coming from the folder path (this could be an issue in multi-root workspaces)
|
||||
foundFiles = foundFiles.filter((f) => parseWinPath(f.fsPath).startsWith(folderPath));
|
||||
foundFiles = foundFiles.filter((f) =>
|
||||
parseWinPath(f.fsPath).startsWith(parseWinPath(folderUri.fsPath))
|
||||
);
|
||||
|
||||
files = [...files, ...foundFiles];
|
||||
}
|
||||
@@ -683,17 +795,8 @@ export class Folders {
|
||||
|
||||
for (const file of files) {
|
||||
try {
|
||||
const fileName = basename(file.fsPath);
|
||||
const folderName = dirname(file.fsPath).split(sep).pop();
|
||||
|
||||
const stats = await workspace.fs.stat(file);
|
||||
|
||||
fileStats.push({
|
||||
filePath: file.fsPath,
|
||||
fileName,
|
||||
folderName,
|
||||
...stats
|
||||
});
|
||||
const fileInfo = await Folders.getFileStats(file, folderPath);
|
||||
fileStats.push(fileInfo);
|
||||
} catch (error) {
|
||||
// Skip the file
|
||||
}
|
||||
@@ -707,6 +810,7 @@ export class Folders {
|
||||
|
||||
return {
|
||||
title: folder.title,
|
||||
path: folderPath,
|
||||
files: files.length,
|
||||
lastModified: fileStats,
|
||||
locale: folder.locale,
|
||||
@@ -726,14 +830,20 @@ export class Folders {
|
||||
* @param pattern
|
||||
* @returns
|
||||
*/
|
||||
private static findFolders(pattern: string): Promise<string[]> {
|
||||
return new Promise((resolve) => {
|
||||
glob(pattern, { ignore: '**/node_modules/**', dot: true }, (err, files) => {
|
||||
const allFolders = files.map((file) => dirname(file));
|
||||
const uniqueFolders = [...new Set(allFolders)];
|
||||
resolve(uniqueFolders);
|
||||
});
|
||||
});
|
||||
private static async findFolders(pattern: string): Promise<string[]> {
|
||||
Logger.verbose(`Folders:findFolders:start - ${pattern}`);
|
||||
|
||||
try {
|
||||
pattern = isWindows() ? parseWinPath(pattern) : pattern;
|
||||
const files = await glob(pattern, { ignore: '**/node_modules/**', dot: true });
|
||||
const allFolders = (files || []).map((file) => dirname(file));
|
||||
const uniqueFolders = [...new Set(allFolders)];
|
||||
Logger.verbose(`Folders:findFolders:end - ${uniqueFolders.length}`);
|
||||
return uniqueFolders;
|
||||
} catch (e) {
|
||||
Logger.error(`Folders:findFolders:error - ${(e as Error).message}`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -742,11 +852,17 @@ export class Folders {
|
||||
* @returns
|
||||
*/
|
||||
private static async findFiles(pattern: string): Promise<Uri[]> {
|
||||
return new Promise((resolve) => {
|
||||
glob(pattern, { ignore: '**/node_modules/**' }, (err, files) => {
|
||||
const allFiles = files.map((file) => Uri.file(file));
|
||||
resolve(allFiles);
|
||||
});
|
||||
});
|
||||
Logger.verbose(`Folders:findFiles:start - ${pattern}`);
|
||||
|
||||
try {
|
||||
pattern = isWindows() ? parseWinPath(pattern) : pattern;
|
||||
const files = await glob(pattern, { ignore: '**/node_modules/**', dot: true });
|
||||
const allFiles = (files || []).map((file) => Uri.file(file));
|
||||
Logger.verbose(`Folders:findFiles:end - ${allFiles.length}`);
|
||||
return allFiles;
|
||||
} catch (e) {
|
||||
Logger.error(`Folders:findFiles:error - ${(e as Error).message}`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { processFmPlaceholders } from './../helpers/processFmPlaceholders';
|
||||
import { processPathPlaceholders } from './../helpers/processPathPlaceholders';
|
||||
import { Telemetry } from './../helpers/Telemetry';
|
||||
import {
|
||||
SETTING_PREVIEW_HOST,
|
||||
SETTING_PREVIEW_PATHNAME,
|
||||
@@ -9,23 +6,33 @@ import {
|
||||
PreviewCommands,
|
||||
SETTING_EXPERIMENTAL,
|
||||
SETTING_DATE_FORMAT,
|
||||
GeneralCommands
|
||||
GeneralCommands,
|
||||
SETTING_PREVIEW_TRAILING_SLASH
|
||||
} from './../constants';
|
||||
import { ArticleHelper } from './../helpers/ArticleHelper';
|
||||
import { join, parse } from 'path';
|
||||
import { commands, env, Uri, ViewColumn, window, WebviewPanel, extensions } from 'vscode';
|
||||
import { Extension, parseWinPath, processTimePlaceholders, Settings } from '../helpers';
|
||||
import {
|
||||
ArticleHelper,
|
||||
Extension,
|
||||
parseWinPath,
|
||||
processI18nPlaceholders,
|
||||
processTimePlaceholders,
|
||||
processFmPlaceholders,
|
||||
processPathPlaceholders,
|
||||
Settings,
|
||||
Telemetry,
|
||||
processDateTimePlaceholders
|
||||
} from '../helpers';
|
||||
import { ContentFolder, ContentType, PreviewSettings } from '../models';
|
||||
import { format } from 'date-fns';
|
||||
import { DateHelper } from '../helpers/DateHelper';
|
||||
import { Article } from '.';
|
||||
import { urlJoin } from 'url-join-ts';
|
||||
import { WebviewHelper } from '@estruyf/vscode';
|
||||
import { Folders } from './Folders';
|
||||
import { ParsedFrontMatter } from '../parsers';
|
||||
import { getLocalizationFile } from '../utils/getLocalizationFile';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../localization';
|
||||
import { joinUrl } from '../utils';
|
||||
import { i18n } from './i18n';
|
||||
|
||||
export class Preview {
|
||||
public static filePath: string | undefined = undefined;
|
||||
@@ -65,7 +72,7 @@ export class Preview {
|
||||
const localhostUrl = await this.getLocalServerUrl();
|
||||
|
||||
if (browserLiteCommand) {
|
||||
const pageUrl = urlJoin(localhostUrl.toString(), slug || '');
|
||||
const pageUrl = joinUrl(localhostUrl.toString(), slug || '');
|
||||
commands.executeCommand(browserLiteCommand, pageUrl);
|
||||
return;
|
||||
}
|
||||
@@ -177,7 +184,7 @@ export class Preview {
|
||||
<title>Front Matter Preview</title>
|
||||
</head>
|
||||
<body style="width:100%;height:100%;margin:0;padding:0;overflow:hidden">
|
||||
<div id="app" data-type="preview" data-url="${urlJoin(
|
||||
<div id="app" data-type="preview" data-url="${joinUrl(
|
||||
localhostUrl.toString(),
|
||||
slug || ''
|
||||
)}" data-isProd="${isProd}" data-environment="${
|
||||
@@ -208,7 +215,7 @@ export class Preview {
|
||||
|
||||
webView.webview.postMessage({
|
||||
command: PreviewCommands.toWebview.updateUrl,
|
||||
payload: urlJoin(localhost.toString(), slug || '')
|
||||
payload: joinUrl(localhost.toString(), slug || '')
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -237,47 +244,18 @@ export class Preview {
|
||||
|
||||
let contentType: ContentType | undefined = undefined;
|
||||
if (article?.data) {
|
||||
contentType = ArticleHelper.getContentType(article);
|
||||
contentType = await ArticleHelper.getContentType(article);
|
||||
}
|
||||
|
||||
// Check if there is a pathname defined on content folder level
|
||||
const folders = Folders.get();
|
||||
if (folders.length > 0) {
|
||||
const foldersWithPath = folders.filter((folder) => folder.previewPath);
|
||||
// Get the folder of the article by the file path
|
||||
selectedFolder = await Folders.getPageFolderByFilePath(filePath);
|
||||
|
||||
for (const folder of foldersWithPath) {
|
||||
const folderPath = parseWinPath(folder.path);
|
||||
if (filePath.startsWith(folderPath)) {
|
||||
if (!selectedFolder || selectedFolder.path.length < folderPath.length) {
|
||||
selectedFolder = folder;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!selectedFolder && contentType) {
|
||||
selectedFolder = await Folders.getFolderByContentType(contentType, filePath);
|
||||
}
|
||||
|
||||
if (!selectedFolder && article?.data && contentType && !contentType.previewPath) {
|
||||
// Try to find the folder by content type
|
||||
let crntFolders = folders.filter(
|
||||
(folder) =>
|
||||
folder.contentTypes?.includes((contentType as ContentType).name) && folder.previewPath
|
||||
);
|
||||
|
||||
// Use file path to find the folder
|
||||
if (crntFolders.length > 0) {
|
||||
crntFolders = crntFolders.filter((folder) => filePath?.startsWith(folder.path));
|
||||
}
|
||||
|
||||
if (crntFolders && crntFolders.length === 1) {
|
||||
selectedFolder = crntFolders[0];
|
||||
} else if (crntFolders && crntFolders.length > 1) {
|
||||
selectedFolder = await Preview.askUserToPickFolder(crntFolders);
|
||||
} else {
|
||||
selectedFolder = await Preview.askUserToPickFolder(folders.filter((f) => f.previewPath));
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedFolder && selectedFolder.previewPath) {
|
||||
pathname = selectedFolder.previewPath;
|
||||
}
|
||||
if (selectedFolder && selectedFolder.previewPath) {
|
||||
pathname = selectedFolder.previewPath;
|
||||
}
|
||||
|
||||
// Check if there is a pathname defined on content type level
|
||||
@@ -288,7 +266,12 @@ export class Preview {
|
||||
}
|
||||
|
||||
if (!slug) {
|
||||
slug = Article.getSlug();
|
||||
slug = Article.getSlug(pathname);
|
||||
}
|
||||
|
||||
const locale = await i18n.getLocale(filePath);
|
||||
if (locale && locale.path === slug) {
|
||||
slug = '';
|
||||
}
|
||||
|
||||
if (pathname) {
|
||||
@@ -310,10 +293,17 @@ export class Preview {
|
||||
const folderPath = wsFolder ? parseWinPath(wsFolder.fsPath) : '';
|
||||
const relativePath = filePath.replace(folderPath, '');
|
||||
pathname = processPathPlaceholders(pathname, relativePath, filePath, selectedFolder);
|
||||
pathname = processI18nPlaceholders(pathname, selectedFolder);
|
||||
|
||||
const file = parse(filePath);
|
||||
if (file.name.toLowerCase() === 'index' && pathname.endsWith(slug)) {
|
||||
slug = '';
|
||||
if (file.name.toLowerCase() === 'index') {
|
||||
const cleanPathName = pathname.endsWith('/')
|
||||
? pathname.substring(0, pathname.length - 1)
|
||||
: pathname;
|
||||
|
||||
if (cleanPathName.endsWith(slug) || !pathname || pathname === '/') {
|
||||
slug = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,11 +311,9 @@ export class Preview {
|
||||
pathname = article?.data ? processFmPlaceholders(pathname, article?.data) : pathname;
|
||||
|
||||
try {
|
||||
const articleDate = ArticleHelper.getDate(article);
|
||||
slug = join(
|
||||
format(articleDate || new Date(), DateHelper.formatUpdate(pathname) as string),
|
||||
slug
|
||||
);
|
||||
const articleDate = await ArticleHelper.getDate(article);
|
||||
pathname = processDateTimePlaceholders(pathname, dateFormat, articleDate);
|
||||
slug = join(pathname, slug);
|
||||
} catch (error) {
|
||||
slug = join(pathname, slug);
|
||||
}
|
||||
@@ -339,7 +327,25 @@ export class Preview {
|
||||
slug = slug.substring(0, slug.endsWith('_index') ? slug.length - 6 : slug.length - 5);
|
||||
}
|
||||
|
||||
return slug;
|
||||
// Add the trailing slash
|
||||
let trailingSlash = false;
|
||||
if (settings.trailingSlash !== undefined) {
|
||||
trailingSlash = settings.trailingSlash;
|
||||
}
|
||||
|
||||
if (selectedFolder && selectedFolder.trailingSlash !== undefined) {
|
||||
trailingSlash = selectedFolder.trailingSlash;
|
||||
}
|
||||
|
||||
if (contentType && contentType.trailingSlash !== undefined) {
|
||||
trailingSlash = contentType.trailingSlash;
|
||||
}
|
||||
|
||||
if (trailingSlash && !slug.endsWith('/')) {
|
||||
slug = `${slug}/`;
|
||||
}
|
||||
|
||||
return join(slug);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -375,10 +381,12 @@ export class Preview {
|
||||
public static getSettings(): PreviewSettings {
|
||||
const host = Settings.get<string>(SETTING_PREVIEW_HOST);
|
||||
const pathname = Settings.get<string>(SETTING_PREVIEW_PATHNAME);
|
||||
const trailingSlash = Settings.get<boolean>(SETTING_PREVIEW_TRAILING_SLASH);
|
||||
|
||||
return {
|
||||
host,
|
||||
pathname
|
||||
pathname,
|
||||
trailingSlash
|
||||
};
|
||||
}
|
||||
|
||||
@@ -387,7 +395,7 @@ export class Preview {
|
||||
* @param crntFolders
|
||||
* @returns
|
||||
*/
|
||||
private static async askUserToPickFolder(
|
||||
public static async askUserToPickFolder(
|
||||
crntFolders: ContentFolder[]
|
||||
): Promise<ContentFolder | undefined> {
|
||||
let selectedFolder: ContentFolder | undefined = undefined;
|
||||
|
||||
@@ -42,6 +42,25 @@ categories: []
|
||||
const ext = Extension.getInstance();
|
||||
const subscriptions = ext.subscriptions;
|
||||
|
||||
// Initialize command
|
||||
subscriptions.push(
|
||||
commands.registerCommand(COMMAND_NAME.init, async (cb: Function) => {
|
||||
await Project.init();
|
||||
|
||||
if (cb) {
|
||||
cb();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
subscriptions.push(
|
||||
commands.registerCommand(COMMAND_NAME.initTemplate, () => Project.createSampleTemplate(true))
|
||||
);
|
||||
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.registerFolder, Folders.register));
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.unregisterFolder, Folders.unregister));
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.createFolder, Folders.addMediaFolder));
|
||||
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.switchProject, Project.switchProject));
|
||||
}
|
||||
|
||||
@@ -63,7 +82,7 @@ categories: []
|
||||
await Settings.createTeamSettings();
|
||||
|
||||
// Add the default content type
|
||||
await Settings.update(SETTING_TAXONOMY_CONTENT_TYPES, [DEFAULT_CONTENT_TYPE], true);
|
||||
await Settings.safeUpdate(SETTING_TAXONOMY_CONTENT_TYPES, [DEFAULT_CONTENT_TYPE], true);
|
||||
|
||||
if (sampleTemplate !== undefined) {
|
||||
await Project.createSampleTemplate();
|
||||
|
||||
@@ -1,14 +1,37 @@
|
||||
import { TaxonomyHelper } from './../helpers/TaxonomyHelper';
|
||||
import * as vscode from 'vscode';
|
||||
import { TaxonomyType } from '../models';
|
||||
import { EXTENSION_NAME } from '../constants';
|
||||
import { ArticleHelper, FilesHelper } from '../helpers';
|
||||
import { COMMAND_NAME, EXTENSION_NAME } from '../constants';
|
||||
import { ArticleHelper, Extension, FilesHelper } from '../helpers';
|
||||
import { FrontMatterParser } from '../parsers';
|
||||
import { Notifications } from '../helpers/Notifications';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../localization';
|
||||
|
||||
export class Settings {
|
||||
public static async registerCommands() {
|
||||
const ext = Extension.getInstance();
|
||||
const subscriptions = ext.subscriptions;
|
||||
|
||||
subscriptions.push(
|
||||
vscode.commands.registerCommand(COMMAND_NAME.createTag, () => {
|
||||
Settings.create(TaxonomyType.Tag);
|
||||
})
|
||||
);
|
||||
|
||||
subscriptions.push(
|
||||
vscode.commands.registerCommand(COMMAND_NAME.createCategory, () => {
|
||||
Settings.create(TaxonomyType.Category);
|
||||
})
|
||||
);
|
||||
|
||||
subscriptions.push(
|
||||
vscode.commands.registerCommand(COMMAND_NAME.exportTaxonomy, Settings.export)
|
||||
);
|
||||
|
||||
subscriptions.push(vscode.commands.registerCommand(COMMAND_NAME.remap, Settings.remap));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new taxonomy
|
||||
*
|
||||
|
||||
@@ -102,13 +102,13 @@ export class StatusListener {
|
||||
* @param article
|
||||
* @param collection
|
||||
*/
|
||||
private static verifyRequiredFields(
|
||||
private static async verifyRequiredFields(
|
||||
editor: vscode.TextEditor,
|
||||
article: ParsedFrontMatter,
|
||||
collection: vscode.DiagnosticCollection
|
||||
) {
|
||||
// Check for missing fields
|
||||
const emptyFields = ContentType.findEmptyRequiredFields(article);
|
||||
const emptyFields = await ContentType.findEmptyRequiredFields(article);
|
||||
const fieldsToReport = [];
|
||||
|
||||
if (emptyFields && emptyFields.length > 0) {
|
||||
|
||||
@@ -2,12 +2,13 @@ import { Questions } from './../helpers/Questions';
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import {
|
||||
COMMAND_NAME,
|
||||
SETTING_CONTENT_DEFAULT_FILETYPE,
|
||||
SETTING_TEMPLATES_FOLDER,
|
||||
TelemetryEvent
|
||||
} from '../constants';
|
||||
import { ArticleHelper, Settings } from '../helpers';
|
||||
import { Article } from '.';
|
||||
import { ArticleHelper, Extension, Settings } from '../helpers';
|
||||
import { Article, Folders } from '.';
|
||||
import { Notifications } from '../helpers/Notifications';
|
||||
import { Project } from './Project';
|
||||
import { ContentType } from '../helpers/ContentType';
|
||||
@@ -20,6 +21,24 @@ import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../localization';
|
||||
|
||||
export class Template {
|
||||
public static async registerCommands() {
|
||||
const ext = Extension.getInstance();
|
||||
const subscriptions = ext.subscriptions;
|
||||
|
||||
subscriptions.push(
|
||||
vscode.commands.registerCommand(COMMAND_NAME.createTemplate, Template.generate)
|
||||
);
|
||||
|
||||
subscriptions.push(
|
||||
vscode.commands.registerCommand(COMMAND_NAME.createFromTemplate, (folder: vscode.Uri) => {
|
||||
const folderPath = Folders.getFolderPath(folder);
|
||||
if (folderPath) {
|
||||
Template.create(folderPath);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a template
|
||||
*/
|
||||
@@ -176,7 +195,7 @@ export class Template {
|
||||
newFilePath
|
||||
);
|
||||
|
||||
const article = Article.updateDate(frontMatter);
|
||||
const article = await Article.updateDate(frontMatter);
|
||||
|
||||
if (!article) {
|
||||
return;
|
||||
|
||||
@@ -48,10 +48,10 @@ export class i18n {
|
||||
*
|
||||
* @returns An array of I18nConfig settings.
|
||||
*/
|
||||
public static getAll() {
|
||||
public static async getAll() {
|
||||
const i18nSettings = Settings.get<I18nConfig[]>(SETTING_CONTENT_I18N) || [];
|
||||
|
||||
const folders = Folders.get();
|
||||
const folders = await Folders.get();
|
||||
if (folders) {
|
||||
for (const folder of folders) {
|
||||
if (folder.locales) {
|
||||
@@ -77,7 +77,7 @@ export class i18n {
|
||||
}
|
||||
|
||||
const i18nSettings = Settings.get<I18nConfig[]>(SETTING_CONTENT_I18N);
|
||||
let pageFolder = Folders.getPageFolderByFilePath(filePath);
|
||||
let pageFolder = await Folders.getPageFolderByFilePath(filePath);
|
||||
if (!pageFolder) {
|
||||
pageFolder = await i18n.getPageFolder(filePath);
|
||||
}
|
||||
@@ -100,7 +100,7 @@ export class i18n {
|
||||
return false;
|
||||
}
|
||||
|
||||
const pageFolder = Folders.getPageFolderByFilePath(filePath);
|
||||
const pageFolder = await Folders.getPageFolderByFilePath(filePath);
|
||||
if (!pageFolder || !pageFolder.locale) {
|
||||
return false;
|
||||
}
|
||||
@@ -119,7 +119,7 @@ export class i18n {
|
||||
return false;
|
||||
}
|
||||
|
||||
const pageFolder = Folders.getPageFolderByFilePath(filePath);
|
||||
const pageFolder = await Folders.getPageFolderByFilePath(filePath);
|
||||
if (!pageFolder || !pageFolder.defaultLocale) {
|
||||
return false;
|
||||
}
|
||||
@@ -155,7 +155,7 @@ export class i18n {
|
||||
return;
|
||||
}
|
||||
|
||||
let pageFolder = Folders.getPageFolderByFilePath(filePath);
|
||||
let pageFolder = await Folders.getPageFolderByFilePath(filePath);
|
||||
|
||||
const fileInfo = await i18n.getFileInfo(filePath);
|
||||
|
||||
@@ -217,7 +217,7 @@ export class i18n {
|
||||
};
|
||||
} = {};
|
||||
|
||||
let pageFolder = Folders.getPageFolderByFilePath(filePath);
|
||||
let pageFolder = await Folders.getPageFolderByFilePath(filePath);
|
||||
const fileInfo = await i18n.getFileInfo(filePath);
|
||||
|
||||
if (pageFolder && pageFolder.defaultLocale && pageFolder.localeSourcePath) {
|
||||
@@ -272,7 +272,7 @@ export class i18n {
|
||||
fileUri = Uri.file(fileUri);
|
||||
}
|
||||
|
||||
const pageFolder = Folders.getPageFolderByFilePath(fileUri.fsPath);
|
||||
const pageFolder = await Folders.getPageFolderByFilePath(fileUri.fsPath);
|
||||
if (!pageFolder || !pageFolder.localeSourcePath) {
|
||||
Notifications.error(l10n.t(LocalizationKey.commandsI18nCreateErrorNoContentFolder));
|
||||
return;
|
||||
@@ -331,7 +331,7 @@ export class i18n {
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = ArticleHelper.getContentType(article);
|
||||
const contentType = await ArticleHelper.getContentType(article);
|
||||
if (!contentType) {
|
||||
Notifications.warning(l10n.t(LocalizationKey.commandsI18nCreateWarningNoContentType));
|
||||
return;
|
||||
@@ -482,7 +482,7 @@ export class i18n {
|
||||
* @returns A promise that resolves to the ContentFolder object representing the page folder, or undefined if not found.
|
||||
*/
|
||||
private static async getPageFolder(filePath: string): Promise<ContentFolder | undefined> {
|
||||
const folders = Folders.get();
|
||||
const folders = await Folders.get();
|
||||
|
||||
const localeFolders = folders?.filter((folder) => folder.defaultLocale);
|
||||
if (!localeFolders) {
|
||||
|
||||
@@ -12,7 +12,7 @@ export const FeatureFlag: React.FunctionComponent<IFeatureFlagProps> = ({
|
||||
alternative,
|
||||
children
|
||||
}: React.PropsWithChildren<IFeatureFlagProps>) => {
|
||||
if (!features || (features.length > 0 && !features.includes(flag))) {
|
||||
if (!features || features.length === 0 || (features.length > 0 && !features.includes(flag))) {
|
||||
if (alternative) {
|
||||
return alternative;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { LabelField } from './LabelField';
|
||||
export type BoolFieldProps = HTMLFieldProps<
|
||||
boolean,
|
||||
HTMLDivElement,
|
||||
{ inputRef?: Ref<HTMLInputElement> }
|
||||
{ inputRef?: Ref<HTMLInputElement>, description?: string }
|
||||
>;
|
||||
|
||||
function Bool({
|
||||
@@ -37,6 +37,12 @@ function Bool({
|
||||
/>
|
||||
<span className="field__toggle__slider"></span>
|
||||
</label>
|
||||
|
||||
{
|
||||
props.description && (
|
||||
<span className='block text-xs text-[var(--vscode--settings-headerForeground)] opacity-75 mt-2 mx-2'>{props.description}</span>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ const dateFormat = (value?: Date) => value?.toISOString().slice(0, -8);
|
||||
export type DateFieldProps = HTMLFieldProps<
|
||||
Date,
|
||||
HTMLDivElement,
|
||||
{ inputRef?: Ref<HTMLInputElement>; max?: Date; min?: Date }
|
||||
{ inputRef?: Ref<HTMLInputElement>; max?: Date; min?: Date, description?: string }
|
||||
>;
|
||||
|
||||
function Date({
|
||||
@@ -50,6 +50,12 @@ function Date({
|
||||
type="datetime-local"
|
||||
value={dateFormat(value) ?? ''}
|
||||
/>
|
||||
|
||||
{
|
||||
props.description && (
|
||||
<span className='block text-xs text-[var(--vscode--settings-headerForeground)] opacity-75 mt-2 mx-2'>{props.description}</span>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { LabelField } from './LabelField';
|
||||
export type LongTextFieldProps = HTMLFieldProps<
|
||||
string,
|
||||
HTMLDivElement,
|
||||
{ inputRef?: Ref<HTMLTextAreaElement> }
|
||||
{ inputRef?: Ref<HTMLTextAreaElement>, description?: string }
|
||||
>;
|
||||
|
||||
function LongText({
|
||||
@@ -36,6 +36,12 @@ function LongText({
|
||||
ref={inputRef}
|
||||
value={value ?? ''}
|
||||
/>
|
||||
|
||||
{
|
||||
props.description && (
|
||||
<span className='block text-xs text-[var(--vscode--settings-headerForeground)] opacity-75 mt-2 mx-2'>{props.description}</span>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { LabelField } from './LabelField';
|
||||
export type NumFieldProps = HTMLFieldProps<
|
||||
number,
|
||||
HTMLDivElement,
|
||||
{ decimal?: boolean; inputRef?: Ref<HTMLInputElement> }
|
||||
{ decimal?: boolean; inputRef?: Ref<HTMLInputElement>, description?: string }
|
||||
>;
|
||||
|
||||
function Num({
|
||||
@@ -47,6 +47,12 @@ function Num({
|
||||
type="number"
|
||||
value={value ?? ''}
|
||||
/>
|
||||
|
||||
{
|
||||
props.description && (
|
||||
<span className='block text-xs text-[var(--vscode--settings-headerForeground)] opacity-75 mt-2 mx-2'>{props.description}</span>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { LabelField } from './LabelField';
|
||||
export type TextFieldProps = HTMLFieldProps<
|
||||
string,
|
||||
HTMLDivElement,
|
||||
{ inputRef?: Ref<HTMLInputElement> }
|
||||
{ inputRef?: Ref<HTMLInputElement>, description?: string }
|
||||
>;
|
||||
|
||||
function Text({
|
||||
@@ -40,6 +40,12 @@ function Text({
|
||||
type={type}
|
||||
value={value ?? ''}
|
||||
/>
|
||||
|
||||
{
|
||||
props.description && (
|
||||
<span className='block text-xs text-[var(--vscode--settings-headerForeground)] opacity-75 mt-2 mx-2'>{props.description}</span>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { LocalizationKey } from '../../localization';
|
||||
export type UnknownFieldProps = HTMLFieldProps<
|
||||
string,
|
||||
HTMLDivElement,
|
||||
{ inputRef?: Ref<HTMLInputElement> }
|
||||
{ inputRef?: Ref<HTMLInputElement>, description?: string }
|
||||
>;
|
||||
|
||||
function UnknownField({
|
||||
@@ -30,6 +30,12 @@ function UnknownField({
|
||||
<LabelField label={label} id={id} required={props.required} />
|
||||
|
||||
<div className={`text-[var(--vscode-errorForeground)]`}>{l10n.t(LocalizationKey.fieldUnknown)}</div>
|
||||
|
||||
{
|
||||
props.description && (
|
||||
<span className='block text-xs text-[var(--vscode--settings-headerForeground)] opacity-75 mt-2 mx-2'>{props.description}</span>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
12
src/constants/DefaultFeatureFlags.ts
Normal file
12
src/constants/DefaultFeatureFlags.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { FEATURE_FLAG } from './Features';
|
||||
|
||||
export const DEFAULT_PANEL_FEATURE_FLAGS = Object.values(FEATURE_FLAG.panel).filter(
|
||||
(v) => v !== FEATURE_FLAG.panel.globalSettings
|
||||
);
|
||||
|
||||
export const DEFAULT_DASHBOARD_FEATURE_FLAGS = [
|
||||
FEATURE_FLAG.dashboard.data.view,
|
||||
FEATURE_FLAG.dashboard.taxonomy.view,
|
||||
FEATURE_FLAG.dashboard.snippets.view,
|
||||
FEATURE_FLAG.dashboard.snippets.manage
|
||||
];
|
||||
@@ -27,6 +27,7 @@ export const COMMAND_NAME = {
|
||||
initTemplate: getCommandName('initTemplate'),
|
||||
collapseSections: getCommandName('collapseSections'),
|
||||
preview: getCommandName('preview'),
|
||||
docs: getCommandName('docs'),
|
||||
chatbot: getCommandName('chatbot'),
|
||||
dashboard: getCommandName('dashboard'),
|
||||
dashboardMedia: getCommandName('dashboard.media'),
|
||||
|
||||
@@ -6,7 +6,8 @@ export const FEATURE_FLAG = {
|
||||
metadata: 'panel.metadata',
|
||||
recentlyModified: 'panel.recentlyModified',
|
||||
otherActions: 'panel.otherActions',
|
||||
contentType: 'panel.contentType'
|
||||
contentType: 'panel.contentType',
|
||||
gitActions: 'panel.gitActions'
|
||||
},
|
||||
dashboard: {
|
||||
snippets: {
|
||||
|
||||
@@ -27,7 +27,8 @@ export const GeneralCommands = {
|
||||
logging: {
|
||||
info: 'logInfo',
|
||||
warn: 'logWarn',
|
||||
error: 'logError'
|
||||
error: 'logError',
|
||||
verbose: 'logVerbose'
|
||||
},
|
||||
runCommand: 'runCommand',
|
||||
getLocalization: 'getLocalization',
|
||||
|
||||
@@ -47,6 +47,8 @@ export const TelemetryEvent = {
|
||||
webviewContentsView: 'webviewContentsView',
|
||||
webviewSnippetsView: 'webviewSnippetsView',
|
||||
webviewTaxonomyDashboard: 'webviewTaxonomyDashboard',
|
||||
webviewSettings: 'webviewSettings',
|
||||
webviewUnknown: 'webviewUnknown',
|
||||
|
||||
// Git
|
||||
gitSync: 'gitSync',
|
||||
|
||||
@@ -52,6 +52,7 @@ export const SETTING_PANEL_ACTIONS_DISABLED = 'panel.actions.disabled';
|
||||
|
||||
export const SETTING_PREVIEW_HOST = 'preview.host';
|
||||
export const SETTING_PREVIEW_PATHNAME = 'preview.pathName';
|
||||
export const SETTING_PREVIEW_TRAILING_SLASH = 'preview.trailingSlash';
|
||||
|
||||
export const SETTING_CUSTOM_SCRIPTS = 'custom.scripts';
|
||||
|
||||
@@ -114,6 +115,8 @@ export const SETTING_SNIPPETS_WRAPPER = 'snippets.wrapper.enabled';
|
||||
|
||||
export const SETTING_WEBSITE_URL = 'website.host';
|
||||
|
||||
export const SETTING_LOGGING = 'logging';
|
||||
|
||||
/**
|
||||
* Sponsors only settings
|
||||
*/
|
||||
|
||||
@@ -76,6 +76,7 @@ export enum DashboardMessage {
|
||||
runCustomScript = 'runCustomScript',
|
||||
sendTelemetry = 'sendTelemetry',
|
||||
showNotification = 'showNotification',
|
||||
setTitle = 'setTitle',
|
||||
|
||||
// Settings
|
||||
getSettings = 'getSettings',
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Media } from './Media/Media';
|
||||
import { DataView } from './DataView';
|
||||
import { Snippets } from './SnippetsView/Snippets';
|
||||
import { FEATURE_FLAG, GeneralCommands } from '../../constants';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { Messenger, messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { TaxonomyView } from './TaxonomyView';
|
||||
import { Route, Routes, useNavigate } from 'react-router-dom';
|
||||
import { routePaths } from '..';
|
||||
@@ -70,6 +70,11 @@ export const App: React.FunctionComponent<IAppProps> = ({
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
messageHandler.send(GeneralCommands.toVSCode.logging.verbose, {
|
||||
message: `Loaded with view ${view}`,
|
||||
location: 'DASHBOARD'
|
||||
});
|
||||
|
||||
if (view && routePaths[view]) {
|
||||
navigate(routePaths[view]);
|
||||
return;
|
||||
@@ -78,6 +83,22 @@ export const App: React.FunctionComponent<IAppProps> = ({
|
||||
navigate(routePaths[view]);
|
||||
}, [view]);
|
||||
|
||||
useEffect(() => {
|
||||
if (settings && Object.keys(settings).length > 0) {
|
||||
messageHandler.send(GeneralCommands.toVSCode.logging.verbose, {
|
||||
message: `Settings loaded`,
|
||||
location: 'DASHBOARD'
|
||||
});
|
||||
}
|
||||
|
||||
if (pages) {
|
||||
messageHandler.send(GeneralCommands.toVSCode.logging.verbose, {
|
||||
message: `Pages loaded - ${pages.length} pages`,
|
||||
location: 'DASHBOARD'
|
||||
});
|
||||
}
|
||||
}, [JSON.stringify(settings), JSON.stringify(pages)]);
|
||||
|
||||
useEffect(() => {
|
||||
checkDevMode();
|
||||
}, []);
|
||||
@@ -100,10 +121,13 @@ export const App: React.FunctionComponent<IAppProps> = ({
|
||||
onError={(error: Error, componentStack: string, eventId: string) => {
|
||||
Messenger.send(
|
||||
GeneralCommands.toVSCode.logging.error,
|
||||
`Event ID: ${eventId}
|
||||
{
|
||||
message: `Event ID: ${eventId}
|
||||
Message: ${error.message}
|
||||
|
||||
Stack: ${componentStack}`
|
||||
Stack: ${componentStack}`,
|
||||
location: 'DASHBOARD'
|
||||
}
|
||||
);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -5,6 +5,7 @@ export interface ITextFieldProps {
|
||||
name: string;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
description?: string;
|
||||
icon?: JSX.Element;
|
||||
disabled?: boolean;
|
||||
autoFocus?: boolean;
|
||||
@@ -18,6 +19,7 @@ export const TextField: React.FunctionComponent<ITextFieldProps> = ({
|
||||
name,
|
||||
value,
|
||||
placeholder,
|
||||
description,
|
||||
icon,
|
||||
autoFocus,
|
||||
multiline,
|
||||
@@ -27,52 +29,63 @@ export const TextField: React.FunctionComponent<ITextFieldProps> = ({
|
||||
onReset
|
||||
}: React.PropsWithChildren<ITextFieldProps>) => {
|
||||
return (
|
||||
<div className="relative flex justify-center">
|
||||
{
|
||||
icon && (
|
||||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
{icon}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<>
|
||||
|
||||
<div className="relative flex justify-center">
|
||||
{
|
||||
icon && (
|
||||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
{icon}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
multiline ? (
|
||||
<textarea
|
||||
rows={rows || 3}
|
||||
name={name}
|
||||
className={`block w-full py-2 ${icon ? "pl-10" : "pl-2"} pr-2 sm:text-sm appearance-none disabled:opacity-50 rounded bg-[var(--vscode-input-background)] text-[var(--vscode-input-foreground)] placeholder-[var(--vscode-input-placeholderForeground)] border-[var(--frontmatter-border)] focus:border-[var(--vscode-focusBorder)] focus:outline-0`}
|
||||
style={{
|
||||
boxShadow: "none"
|
||||
}}
|
||||
placeholder={placeholder || ""}
|
||||
value={value}
|
||||
autoFocus={!!autoFocus}
|
||||
onChange={(e) => onChange && onChange(e.target.value)}
|
||||
disabled={!!disabled}
|
||||
/>
|
||||
) : (
|
||||
<input
|
||||
type="text"
|
||||
name={name}
|
||||
className={`block w-full py-2 ${icon ? "pl-10" : "pl-2"} pr-2 sm:text-sm appearance-none disabled:opacity-50 rounded bg-[var(--vscode-input-background)] text-[var(--vscode-input-foreground)] placeholder-[var(--vscode-input-placeholderForeground)] border-[var(--frontmatter-border)] focus:border-[var(--vscode-focusBorder)] focus:outline-0`}
|
||||
style={{
|
||||
boxShadow: "none"
|
||||
}}
|
||||
placeholder={placeholder || ""}
|
||||
value={value}
|
||||
autoFocus={!!autoFocus}
|
||||
onChange={(e) => onChange && onChange(e.target.value)}
|
||||
disabled={!!disabled}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
{(value && onReset) && (
|
||||
<button onClick={onReset} className="absolute inset-y-0 right-0 pr-3 flex items-center text-[var(--vscode-input-foreground)] hover:text-[var(--vscode-textLink-activeForeground)]">
|
||||
<XCircleIcon className={`h-5 w-5`} aria-hidden="true" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{
|
||||
multiline ? (
|
||||
<textarea
|
||||
rows={rows || 3}
|
||||
name={name}
|
||||
className={`block w-full py-2 ${icon ? "pl-10" : "pl-2"} pr-2 sm:text-sm appearance-none disabled:opacity-50 rounded bg-[var(--vscode-input-background)] text-[var(--vscode-input-foreground)] placeholder-[var(--vscode-input-placeholderForeground)] border-[var(--frontmatter-border)] focus:border-[var(--vscode-focusBorder)] focus:outline-0`}
|
||||
style={{
|
||||
boxShadow: "none"
|
||||
}}
|
||||
placeholder={placeholder || ""}
|
||||
value={value}
|
||||
autoFocus={!!autoFocus}
|
||||
onChange={(e) => onChange && onChange(e.target.value)}
|
||||
disabled={!!disabled}
|
||||
/>
|
||||
) : (
|
||||
<input
|
||||
type="text"
|
||||
name={name}
|
||||
className={`block w-full py-2 ${icon ? "pl-10" : "pl-2"} pr-2 sm:text-sm appearance-none disabled:opacity-50 rounded bg-[var(--vscode-input-background)] text-[var(--vscode-input-foreground)] placeholder-[var(--vscode-input-placeholderForeground)] border-[var(--frontmatter-border)] focus:border-[var(--vscode-focusBorder)] focus:outline-0`}
|
||||
style={{
|
||||
boxShadow: "none"
|
||||
}}
|
||||
placeholder={placeholder || ""}
|
||||
value={value}
|
||||
autoFocus={!!autoFocus}
|
||||
onChange={(e) => onChange && onChange(e.target.value)}
|
||||
disabled={!!disabled}
|
||||
/>
|
||||
description && (
|
||||
<p className="text-xs text-[var(--vscode--settings-headerForeground)] opacity-75 mt-2 mx-2">
|
||||
{description}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
{(value && onReset) && (
|
||||
<button onClick={onReset} className="absolute inset-y-0 right-0 pr-3 flex items-center text-[var(--vscode-input-foreground)] hover:text-[var(--vscode-textLink-activeForeground)]">
|
||||
<XCircleIcon className={`h-5 w-5`} aria-hidden="true" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -8,9 +8,9 @@ import { Spinner } from '../Common/Spinner';
|
||||
import { SponsorMsg } from '../Layout/SponsorMsg';
|
||||
import usePages from '../../hooks/usePages';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { Messenger, messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { TelemetryEvent } from '../../../constants';
|
||||
import { GeneralCommands, TelemetryEvent } from '../../../constants';
|
||||
import { PageLayout } from '../Layout/PageLayout';
|
||||
import { FilesProvider } from '../../providers/FilesProvider';
|
||||
import { Alert } from '../Modals/Alert';
|
||||
@@ -59,10 +59,19 @@ export const Contents: React.FunctionComponent<IContentsProps> = ({
|
||||
}
|
||||
}, [pageItems, selectedItemAction]);
|
||||
|
||||
useEffect(() => {
|
||||
messageHandler.send(GeneralCommands.toVSCode.logging.info, {
|
||||
message: `Contents view loaded with ${pageItems.length} pages`,
|
||||
location: 'DASHBOARD'
|
||||
});
|
||||
}, [JSON.stringify(pageItems)]);
|
||||
|
||||
useEffect(() => {
|
||||
Messenger.send(DashboardMessage.sendTelemetry, {
|
||||
event: TelemetryEvent.webviewContentsView
|
||||
});
|
||||
|
||||
Messenger.send(DashboardMessage.setTitle, l10n.t(LocalizationKey.dashboardHeaderTabsContents));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
@@ -25,7 +25,7 @@ export const FooterActions: React.FunctionComponent<IFooterActionsProps> = ({
|
||||
const [, setSelectedItemAction] = useRecoilState(SelectedItemActionAtom);
|
||||
|
||||
return (
|
||||
<div className={`py-2 w-full flex items-center justify-evenly border-t border-t-[var(--frontmatter-border)] bg-[var(--frontmatter-sideBar-background)] group-hover:bg-[var(--vscode-list-hoverBackground)] rounded`}>
|
||||
<div className={`py-2 w-full flex items-center justify-evenly border-t border-t-[var(--frontmatter-border)] bg-[var(--frontmatter-sideBar-background)] group-hover:bg-[var(--vscode-list-hoverBackground)] rounded-b`}>
|
||||
{/* <ItemSelection filePath={filePath} show /> */}
|
||||
|
||||
<QuickAction
|
||||
|
||||
@@ -115,7 +115,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
<button
|
||||
title={escapedTitle ? l10n.t(LocalizationKey.commonOpenWithValue, escapedTitle) : l10n.t(LocalizationKey.commonOpen)}
|
||||
onClick={onOpenFile}
|
||||
className={`relative rounded h-36 w-full overflow-hidden border-b cursor-pointer border-[var(--frontmatter-border)]`}
|
||||
className={`relative rounded-t h-36 w-full overflow-hidden border-b cursor-pointer border-[var(--frontmatter-border)]`}
|
||||
>
|
||||
{
|
||||
imageHtml ?
|
||||
|
||||
@@ -17,10 +17,13 @@ import { Container } from './SortableContainer';
|
||||
import { SortableItem } from './SortableItem';
|
||||
import { ChevronRightIcon, CircleStackIcon } from '@heroicons/react/24/outline';
|
||||
import { DataType } from '../../../models/DataType';
|
||||
import { TelemetryEvent, WEBSITE_LINKS } from '../../../constants';
|
||||
import { GeneralCommands, TelemetryEvent, WEBSITE_LINKS } from '../../../constants';
|
||||
import { NavigationItem } from '../Layout';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { DropdownMenu, DropdownMenuContent } from '../../../components/shadcn/Dropdown';
|
||||
import { MenuButton, MenuItem } from '../Menu';
|
||||
import { Transition } from '@headlessui/react';
|
||||
|
||||
export interface IDataViewProps { }
|
||||
|
||||
@@ -125,10 +128,17 @@ export const DataView: React.FunctionComponent<IDataViewProps> = (
|
||||
useEffect(() => {
|
||||
Messenger.listen(messageListener);
|
||||
|
||||
Messenger.send(DashboardMessage.setTitle, l10n.t(LocalizationKey.dashboardHeaderTabsData));
|
||||
|
||||
Messenger.send(DashboardMessage.sendTelemetry, {
|
||||
event: TelemetryEvent.webviewDataView
|
||||
});
|
||||
|
||||
Messenger.send(GeneralCommands.toVSCode.logging.info, {
|
||||
message: 'Data view loaded',
|
||||
location: 'DASHBOARD'
|
||||
});
|
||||
|
||||
return () => {
|
||||
Messenger.unlisten(messageListener);
|
||||
};
|
||||
@@ -161,38 +171,71 @@ export const DataView: React.FunctionComponent<IDataViewProps> = (
|
||||
<div className="flex flex-col h-full overflow-auto inset-y-0">
|
||||
<Header settings={settings} />
|
||||
|
||||
{settings?.dataFiles && settings.dataFiles.length > 0 ? (
|
||||
<div className="relative w-full flex-grow mx-auto overflow-hidden">
|
||||
<div className={`flex w-64 flex-col absolute inset-y-0`}>
|
||||
<aside
|
||||
className={`flex flex-col flex-grow overflow-y-auto border-r py-6 px-4 overflow-auto border-[var(--frontmatter-border)]`}
|
||||
>
|
||||
<h2 className={`text-lg text-[var(--frontmatter-text)]`}>
|
||||
{l10n.t(LocalizationKey.dashboardDataViewDataViewSelect)}
|
||||
</h2>
|
||||
|
||||
<nav className={`flex-1 py-4 -mx-4`}>
|
||||
<div
|
||||
className={`divide-y border-t border-b divide-[var(--frontmatter-border)] border-[var(--frontmatter-border)]`}
|
||||
{dataFiles && dataFiles.length > 0 ? (
|
||||
<div className={`relative w-full flex-grow mx-auto overflow-hidden`}>
|
||||
{
|
||||
!selectedData && (
|
||||
<div className={`flex w-64 flex-col absolute inset-y-0`}>
|
||||
<aside
|
||||
className={`flex flex-col flex-grow overflow-y-auto border-r py-6 px-4 overflow-auto border-[var(--frontmatter-border)]`}
|
||||
>
|
||||
{dataFiles &&
|
||||
dataFiles.length > 0 &&
|
||||
dataFiles.map((dataFile, idx) => (
|
||||
<NavigationItem
|
||||
key={`${dataFile.id}-${idx}`}
|
||||
isSelected={selectedData?.id === dataFile.id}
|
||||
onClick={() => setSchema(dataFile)}
|
||||
>
|
||||
<ChevronRightIcon className="-ml-1 w-5 mr-2" />
|
||||
<span>{dataFile.title}</span>
|
||||
</NavigationItem>
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
</aside>
|
||||
</div>
|
||||
<h2 className={`text-lg text-[var(--frontmatter-text)]`}>
|
||||
{l10n.t(LocalizationKey.dashboardDataViewDataViewSelect)}
|
||||
</h2>
|
||||
|
||||
<section className={`pl-64 flex min-w-0 h-full`}>
|
||||
<nav className={`flex-1 py-4 -mx-4`}>
|
||||
<div
|
||||
className={`divide-y border-t border-b divide-[var(--frontmatter-border)] border-[var(--frontmatter-border)]`}
|
||||
>
|
||||
{dataFiles &&
|
||||
dataFiles.length > 0 &&
|
||||
dataFiles.map((dataFile, idx) => (
|
||||
<NavigationItem
|
||||
key={`${dataFile.id}-${idx}`}
|
||||
onClick={() => setSchema(dataFile)}
|
||||
>
|
||||
<ChevronRightIcon className="-ml-1 w-5 mr-2" />
|
||||
<span>{dataFile.title}</span>
|
||||
</NavigationItem>
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
</aside>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
<Transition
|
||||
as={`div`}
|
||||
show={!!selectedData}
|
||||
enter="transition ease transform"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100">
|
||||
<div className={`w-full px-4 py-2 border-b border-[var(--frontmatter-border)]`}>
|
||||
{selectedData && (
|
||||
<DropdownMenu>
|
||||
<MenuButton
|
||||
label={l10n.t(LocalizationKey.dashboardDataViewDataViewSelect)}
|
||||
title={selectedData.title}
|
||||
/>
|
||||
|
||||
<DropdownMenuContent>
|
||||
{dataFiles.map((dataFile) => (
|
||||
<MenuItem
|
||||
key={dataFile.id}
|
||||
title={dataFile.title}
|
||||
value={dataFile}
|
||||
isCurrent={selectedData.id === dataFile.id}
|
||||
onClick={() => setSchema(dataFile)}
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<section className={`flex min-w-0 h-full ease transition-[padding] ${selectedData ? "" : "pl-64"}`}>
|
||||
{selectedData ? (
|
||||
<>
|
||||
{!selectedData.singleEntry && (
|
||||
|
||||
@@ -14,7 +14,7 @@ import { ChoiceButton } from '../Common/ChoiceButton';
|
||||
import { MediaHeaderBottom } from '../Media/MediaHeaderBottom';
|
||||
import { Tabs } from './Tabs';
|
||||
import { CustomScript } from '../../../models';
|
||||
import { ArrowTopRightOnSquareIcon, BoltIcon, PlusIcon } from '@heroicons/react/24/outline';
|
||||
import { ArrowTopRightOnSquareIcon, BoltIcon, BookOpenIcon, PlusIcon } from '@heroicons/react/24/outline';
|
||||
import { HeartIcon } from '@heroicons/react/24/solid';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { routePaths } from '../..';
|
||||
@@ -29,7 +29,7 @@ import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { SettingsLink } from '../SettingsView/SettingsLink';
|
||||
import { Link } from '../Common/Link';
|
||||
import { SPONSOR_LINK } from '../../../constants';
|
||||
import { COMMAND_NAME, GeneralCommands, SPONSOR_LINK } from '../../../constants';
|
||||
import { Filters } from './Filters';
|
||||
import { ActionsBar } from './ActionsBar';
|
||||
import { RefreshDashboardData } from './RefreshDashboardData';
|
||||
@@ -157,6 +157,16 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({
|
||||
)
|
||||
}
|
||||
|
||||
<button
|
||||
className="inline-flex items-center hover:text-[var(--vscode-textLink-activeForeground)]"
|
||||
title={l10n.t(LocalizationKey.commonDocs)}
|
||||
onClick={() => Messenger.send(GeneralCommands.toVSCode.runCommand, {
|
||||
command: COMMAND_NAME.docs
|
||||
})}>
|
||||
<span className='sr-only'>{l10n.t(LocalizationKey.commonDocs)}</span>
|
||||
<BookOpenIcon className='w-4 h-4' aria-hidden="true" />
|
||||
</button>
|
||||
|
||||
<SettingsLink onNavigate={updateView} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Tab } from './Tab';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { PageIcon } from '../../../panelWebView/components/Icons';
|
||||
import { DEFAULT_DASHBOARD_FEATURE_FLAGS } from '../../../constants/DefaultFeatureFlags';
|
||||
|
||||
export interface ITabsProps {
|
||||
onNavigate: (navigationType: NavigationType) => void;
|
||||
@@ -37,7 +38,7 @@ export const Tabs: React.FunctionComponent<ITabsProps> = ({
|
||||
<span>{l10n.t(LocalizationKey.dashboardHeaderTabsMedia)}</span>
|
||||
</Tab>
|
||||
</li>
|
||||
<FeatureFlag features={mode?.features || []} flag={FEATURE_FLAG.dashboard.snippets.view}>
|
||||
<FeatureFlag features={mode?.features || DEFAULT_DASHBOARD_FEATURE_FLAGS} flag={FEATURE_FLAG.dashboard.snippets.view}>
|
||||
<li role="presentation">
|
||||
<Tab navigationType={NavigationType.Snippets} onNavigate={onNavigate}>
|
||||
<ScissorsIcon className={`h-4 w-auto mr-2`} />
|
||||
@@ -45,7 +46,7 @@ export const Tabs: React.FunctionComponent<ITabsProps> = ({
|
||||
</Tab>
|
||||
</li>
|
||||
</FeatureFlag>
|
||||
<FeatureFlag features={mode?.features || []} flag={FEATURE_FLAG.dashboard.data.view}>
|
||||
<FeatureFlag features={mode?.features || DEFAULT_DASHBOARD_FEATURE_FLAGS} flag={FEATURE_FLAG.dashboard.data.view}>
|
||||
<li role="presentation">
|
||||
<Tab navigationType={NavigationType.Data} onNavigate={onNavigate}>
|
||||
<CircleStackIcon className={`h-4 w-auto mr-2`} />
|
||||
@@ -53,7 +54,7 @@ export const Tabs: React.FunctionComponent<ITabsProps> = ({
|
||||
</Tab>
|
||||
</li>
|
||||
</FeatureFlag>
|
||||
<FeatureFlag features={mode?.features || []} flag={FEATURE_FLAG.dashboard.taxonomy.view}>
|
||||
<FeatureFlag features={mode?.features || DEFAULT_DASHBOARD_FEATURE_FLAGS} flag={FEATURE_FLAG.dashboard.taxonomy.view}>
|
||||
<li role="presentation">
|
||||
<Tab navigationType={NavigationType.Taxonomy} onNavigate={onNavigate}>
|
||||
<TagIcon className={`h-4 w-auto mr-2`} />
|
||||
|
||||
@@ -36,7 +36,7 @@ export const SponsorMsg: React.FunctionComponent<ISponsorMsgProps> = ({
|
||||
return (
|
||||
<footer
|
||||
className={`w-full px-4 py-2 text-center space-x-8 flex items-center border-t ${isBacker ? 'justify-center' : 'justify-between'
|
||||
} bg-[var(--vscode-editor-background)] text-[var(--vscode-editor-foreground)] border-[var(--frontmatter-border)]`}
|
||||
} bg-[var(--vscode-editor-background)] text-[var(--frontmatter-secondary-text)] border-[var(--frontmatter-border)]`}
|
||||
>
|
||||
{isBacker ? (
|
||||
<span>
|
||||
|
||||
@@ -20,7 +20,7 @@ import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { FrontMatterIcon } from '../../../panelWebView/components/Icons/FrontMatterIcon';
|
||||
import { FolderItem } from './FolderItem';
|
||||
import useMedia from '../../hooks/useMedia';
|
||||
import { STATIC_FOLDER_PLACEHOLDER, TelemetryEvent } from '../../../constants';
|
||||
import { GeneralCommands, STATIC_FOLDER_PLACEHOLDER, TelemetryEvent } from '../../../constants';
|
||||
import { PageLayout } from '../Layout/PageLayout';
|
||||
import { parseWinPath } from '../../../helpers/parseWinPath';
|
||||
import { basename, extname, join } from 'path';
|
||||
@@ -150,9 +150,16 @@ export const Media: React.FunctionComponent<IMediaProps> = (
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
Messenger.send(DashboardMessage.setTitle, l10n.t(LocalizationKey.dashboardHeaderTabsMedia));
|
||||
|
||||
Messenger.send(DashboardMessage.sendTelemetry, {
|
||||
event: TelemetryEvent.webviewMediaView
|
||||
});
|
||||
|
||||
Messenger.send(GeneralCommands.toVSCode.logging.info, {
|
||||
message: `Media view loaded`,
|
||||
location: 'DASHBOARD'
|
||||
});
|
||||
}, []);
|
||||
|
||||
const { getRootProps, isDragActive } = useDropzone({
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ViewDataSelector } from '../../state';
|
||||
import SnippetForm, { SnippetFormHandle } from '../SnippetsView/SnippetForm';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { SnippetSlideOver } from './SnippetSlideOver';
|
||||
import { SlideOver } from '../Modals/SlideOver';
|
||||
|
||||
export interface IMediaSnippetFormProps {
|
||||
media: MediaInfo;
|
||||
@@ -32,7 +32,7 @@ export const MediaSnippetForm: React.FunctionComponent<IMediaSnippetFormProps> =
|
||||
};
|
||||
|
||||
return (
|
||||
<SnippetSlideOver
|
||||
<SlideOver
|
||||
title={l10n.t(LocalizationKey.dashboardMediaMediaSnippetFormFormDialogTitle, media.metadata.title || media.filename)}
|
||||
description={l10n.t(LocalizationKey.dashboardMediaMediaSnippetFormFormDialogDescription, media.metadata.title || media.filename)}
|
||||
isSaveDisabled={false}
|
||||
@@ -48,6 +48,6 @@ export const MediaSnippetForm: React.FunctionComponent<IMediaSnippetFormProps> =
|
||||
selection={viewData?.data?.selection}
|
||||
onInsert={onInsert}
|
||||
/>
|
||||
</SnippetSlideOver>
|
||||
</SlideOver>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Fragment, useRef } from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
|
||||
export interface ISnippetSlideOverProps {
|
||||
export interface ISlideOverProps {
|
||||
title: string;
|
||||
description: string;
|
||||
okBtnText: string;
|
||||
@@ -16,7 +16,7 @@ export interface ISnippetSlideOverProps {
|
||||
trigger: () => void;
|
||||
}
|
||||
|
||||
export const SnippetSlideOver: React.FunctionComponent<ISnippetSlideOverProps> = ({
|
||||
export const SlideOver: React.FunctionComponent<ISlideOverProps> = ({
|
||||
title,
|
||||
description,
|
||||
okBtnText,
|
||||
@@ -25,7 +25,7 @@ export const SnippetSlideOver: React.FunctionComponent<ISnippetSlideOverProps> =
|
||||
dismiss,
|
||||
trigger,
|
||||
children
|
||||
}: React.PropsWithChildren<ISnippetSlideOverProps>) => {
|
||||
}: React.PropsWithChildren<ISlideOverProps>) => {
|
||||
const cancelButtonRef = useRef(null);
|
||||
|
||||
return (
|
||||
@@ -46,11 +46,12 @@ export const SnippetSlideOver: React.FunctionComponent<ISnippetSlideOverProps> =
|
||||
>
|
||||
<div className="pointer-events-auto w-screen max-w-md">
|
||||
<div className={`flex h-full flex-col overflow-y-scroll border-l pb-6 shadow-xl bg-[var(--vscode-sideBar-background)] border-[var(--frontmatter-border)]`}>
|
||||
<div className="py-6 sticky top-0 z-10 px-4 sm:px-6 bg-[var(--vscode-sideBar-background)]">
|
||||
<div className="py-6 sticky top-0 z-10 px-4 sm:px-6 bg-[var(--vscode-sideBar-background)] text-[var(--vscode-sideBarTitle-foreground)]">
|
||||
<div className="flex items-start justify-between">
|
||||
<Dialog.Title className={`text-lg font-medium text-[var(--vscode-editor-foreground)]`}>
|
||||
<Dialog.Title className={`text-lg font-medium`}>
|
||||
{title}
|
||||
</Dialog.Title>
|
||||
|
||||
<div className="ml-3 flex h-7 items-center">
|
||||
<button
|
||||
type="button"
|
||||
@@ -62,12 +63,14 @@ export const SnippetSlideOver: React.FunctionComponent<ISnippetSlideOverProps> =
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-1">
|
||||
<p className="text-sm">{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative flex-1 px-4 sm:px-6">
|
||||
<div className="space-y-4">
|
||||
<p className="text-sm">{description}</p>
|
||||
|
||||
<div>
|
||||
{children}
|
||||
</div>
|
||||
@@ -8,11 +8,14 @@ import { AstroContentTypes } from '../Configuration/Astro/AstroContentTypes';
|
||||
import { ContentFolders } from '../Configuration/Common/ContentFolders';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { COMMAND_NAME } from '../../../constants';
|
||||
import { COMMAND_NAME, TelemetryEvent } from '../../../constants';
|
||||
import { ArrowPathIcon } from '@heroicons/react/24/outline';
|
||||
import { VSCodePanelTab, VSCodePanelView, VSCodePanels } from '@vscode/webview-ui-toolkit/react';
|
||||
import { CommonSettings } from './CommonSettings';
|
||||
import { IntegrationsView } from './IntegrationsView';
|
||||
import { useEffect } from 'react';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
|
||||
export interface ISettingsViewProps { }
|
||||
|
||||
@@ -20,6 +23,14 @@ export const SettingsView: React.FunctionComponent<ISettingsViewProps> = (_: Rea
|
||||
const [loading, setLoading] = React.useState<boolean>(false);
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
|
||||
useEffect(() => {
|
||||
Messenger.send(DashboardMessage.setTitle, l10n.t(LocalizationKey.commonSettings));
|
||||
|
||||
Messenger.send(DashboardMessage.sendTelemetry, {
|
||||
event: TelemetryEvent.webviewSettings
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<PageLayout>
|
||||
{
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
import * as React from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { QuickAction } from '../Menu';
|
||||
import { EyeIcon, PencilIcon, PlusIcon, TrashIcon } from '@heroicons/react/24/solid';
|
||||
import { useCallback } from 'react';
|
||||
import { openFile } from '../../utils/MessageHandlers';
|
||||
|
||||
export interface IFooterActionsProps {
|
||||
insertEnabled: boolean;
|
||||
sourcePath?: string;
|
||||
onEdit?: () => void;
|
||||
onInsert: () => void;
|
||||
onDelete?: () => void;
|
||||
}
|
||||
|
||||
export const FooterActions: React.FunctionComponent<IFooterActionsProps> = ({
|
||||
insertEnabled,
|
||||
sourcePath,
|
||||
onEdit,
|
||||
onInsert,
|
||||
onDelete,
|
||||
}: React.PropsWithChildren<IFooterActionsProps>) => {
|
||||
|
||||
const showFile = useCallback(() => {
|
||||
openFile(sourcePath);
|
||||
}, [sourcePath]);
|
||||
|
||||
if (!onEdit && !onDelete && !sourcePath && !insertEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`py-2 w-full flex items-center justify-evenly border-t border-t-[var(--frontmatter-border)] bg-[var(--frontmatter-sideBar-background)] group-hover:bg-[var(--vscode-list-hoverBackground)] z-50`}>
|
||||
{insertEnabled && (
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.commonInsertSnippet)}
|
||||
className={`text-[var(--frontmatter-secondary-text)]`}
|
||||
onClick={onInsert}>
|
||||
<PlusIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
<span className='sr-only'>{l10n.t(LocalizationKey.commonInsertSnippet)}</span>
|
||||
</QuickAction>
|
||||
)}
|
||||
|
||||
{
|
||||
!sourcePath ? (
|
||||
<>
|
||||
{
|
||||
onEdit && (
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.dashboardSnippetsViewItemQuickActionEditSnippet)}
|
||||
className={`text-[var(--frontmatter-secondary-text)]`}
|
||||
onClick={onEdit}>
|
||||
<PencilIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
<span className='sr-only'>{l10n.t(LocalizationKey.dashboardSnippetsViewItemQuickActionEditSnippet)}</span>
|
||||
</QuickAction>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
onDelete && (
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.dashboardSnippetsViewItemQuickActionDeleteSnippet)}
|
||||
className={`text-[var(--frontmatter-secondary-text)] hover:text-[var(--vscode-statusBarItem-errorBackground)]`}
|
||||
onClick={onDelete}>
|
||||
<TrashIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
<span className='sr-only'>{l10n.t(LocalizationKey.dashboardSnippetsViewItemQuickActionDeleteSnippet)}</span>
|
||||
</QuickAction>
|
||||
)
|
||||
}
|
||||
</>
|
||||
) : (
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.dashboardSnippetsViewItemQuickActionViewSnippet)}
|
||||
className={`text-[var(--frontmatter-secondary-text)]`}
|
||||
onClick={showFile}>
|
||||
<EyeIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
<span className='sr-only'>{l10n.t(LocalizationKey.dashboardSnippetsViewItemQuickActionViewSnippet)}</span>
|
||||
</QuickAction>
|
||||
)
|
||||
}
|
||||
</div >
|
||||
);
|
||||
};
|
||||
@@ -1,14 +1,8 @@
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import {
|
||||
CodeBracketIcon,
|
||||
DocumentTextIcon,
|
||||
EllipsisHorizontalIcon,
|
||||
EyeIcon,
|
||||
PencilIcon,
|
||||
PhotoIcon,
|
||||
PlusIcon,
|
||||
TrashIcon
|
||||
} from '@heroicons/react/24/outline';
|
||||
} from '@heroicons/react/24/solid';
|
||||
import * as React from 'react';
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
@@ -18,14 +12,14 @@ import { SnippetParser } from '../../../helpers/SnippetParser';
|
||||
import { Snippet, Snippets } from '../../../models';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { ModeAtom, SettingsSelector, ViewDataSelector } from '../../state';
|
||||
import { QuickAction } from '../Menu';
|
||||
import { Alert } from '../Modals/Alert';
|
||||
import { FormDialog } from '../Modals/FormDialog';
|
||||
import { NewForm } from './NewForm';
|
||||
import SnippetForm, { SnippetFormHandle } from './SnippetForm';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { openFile } from '../../utils';
|
||||
import { FooterActions } from './FooterActions';
|
||||
import { ItemMenu } from './ItemMenu';
|
||||
import { SlideOver } from '../Modals/SlideOver';
|
||||
import { DEFAULT_DASHBOARD_FEATURE_FLAGS } from '../../../constants/DefaultFeatureFlags';
|
||||
|
||||
export interface IItemProps {
|
||||
snippetKey: string;
|
||||
@@ -65,10 +59,6 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
setMediaSnippet(false);
|
||||
};
|
||||
|
||||
const showFile = useCallback(() => {
|
||||
openFile(snippet.sourcePath);
|
||||
}, [snippet]);
|
||||
|
||||
const onOpenEdit = useCallback(() => {
|
||||
setSnippetTitle(snippet.title || snippetKey);
|
||||
setSnippetDescription(snippet.description);
|
||||
@@ -162,95 +152,67 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<li className={`group relative overflow-hidden shadow-md hover:shadow-xl dark:shadow-none border p-4 space-y-2 rounded bg-[var(--vscode-sideBar-background)] hover:bg-[var(--vscode-list-hoverBackground)] border-[var(--frontmatter-border)]`}>
|
||||
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
|
||||
<CodeBracketIcon className={`w-64 h-64 opacity-5 text-[var(--frontmatter-text)]`} />
|
||||
<li className={`group flex flex-col relative overflow-hidden shadow-md hover:shadow-xl dark:shadow-none border space-y-2 rounded bg-[var(--vscode-sideBar-background)] hover:bg-[var(--vscode-list-hoverBackground)] border-[var(--frontmatter-border)]`}>
|
||||
<div className='p-4 grow space-y-2'>
|
||||
<h2
|
||||
className="font-bold flex items-center"
|
||||
title={snippet.isMediaSnippet ? 'Media snippet' : 'Content snippet'}
|
||||
>
|
||||
<CodeBracketIcon className="w-5 h-5 mr-2" aria-hidden={true} />
|
||||
|
||||
{snippet.title || snippetKey}
|
||||
</h2>
|
||||
|
||||
<FeatureFlag
|
||||
features={mode?.features || DEFAULT_DASHBOARD_FEATURE_FLAGS}
|
||||
flag={FEATURE_FLAG.dashboard.snippets.manage}
|
||||
alternative={
|
||||
<ItemMenu
|
||||
insertEnabled={!!(insertToContent && !snippet.isMediaSnippet)}
|
||||
sourcePath={snippet.sourcePath}
|
||||
onInsert={() => setShowInsertDialog(true)} />
|
||||
}
|
||||
>
|
||||
<ItemMenu
|
||||
insertEnabled={!!(insertToContent && !snippet.isMediaSnippet)}
|
||||
sourcePath={snippet.sourcePath}
|
||||
onEdit={onOpenEdit}
|
||||
onInsert={() => setShowInsertDialog(true)}
|
||||
onDelete={() => setShowAlert(true)} />
|
||||
</FeatureFlag>
|
||||
|
||||
<div className='inline-block mr-1 mt-1 text-xs text-[var(--vscode-button-secondaryForeground)] bg-[var(--vscode-button-secondaryBackground)] border border-[var(--frontmatter-border)] rounded px-1 py-0.5'>
|
||||
{
|
||||
snippet.isMediaSnippet ? l10n.t(LocalizationKey.dashboardSnippetsViewItemTypeContent) : l10n.t(LocalizationKey.dashboardSnippetsViewItemTypeMedia)
|
||||
}
|
||||
</div>
|
||||
|
||||
<p className={`text-xs text-[var(--frontmatter-text)]`}>{snippet.description}</p>
|
||||
</div>
|
||||
|
||||
<h2
|
||||
className="mt-2 mb-2 font-bold flex items-center"
|
||||
title={snippet.isMediaSnippet ? 'Media snippet' : 'Content snippet'}
|
||||
>
|
||||
{snippet.isMediaSnippet ? (
|
||||
<PhotoIcon className="w-5 h-5 mr-1" aria-hidden={true} />
|
||||
) : (
|
||||
<DocumentTextIcon className="w-5 h-5 mr-1" aria-hidden={true} />
|
||||
)}
|
||||
|
||||
{snippet.title || snippetKey}
|
||||
</h2>
|
||||
|
||||
<FeatureFlag
|
||||
features={mode?.features || []}
|
||||
features={mode?.features || DEFAULT_DASHBOARD_FEATURE_FLAGS}
|
||||
flag={FEATURE_FLAG.dashboard.snippets.manage}
|
||||
alternative={
|
||||
insertToContent ? (
|
||||
<div className={`absolute top-4 right-4 flex flex-col space-y-4`}>
|
||||
<div className={`flex items-center border border-transparent rounded-full p-2 -mr-2 -mt-2 group-hover:bg-[var(--vscode-sideBar-background)] group-hover:border-[var(--frontmatter-border)]`}>
|
||||
<div className="group-hover:hidden">
|
||||
<EllipsisHorizontalIcon className="w-4 h-4" />
|
||||
</div>
|
||||
|
||||
<div className="hidden group-hover:flex">
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.commonInsertSnippet)}
|
||||
onClick={() => setShowInsertDialog(true)}>
|
||||
<PlusIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : undefined
|
||||
<FooterActions
|
||||
insertEnabled={!!(insertToContent && !snippet.isMediaSnippet)}
|
||||
sourcePath={snippet.sourcePath}
|
||||
onInsert={() => setShowInsertDialog(true)} />
|
||||
}
|
||||
>
|
||||
<div className={`absolute top-4 right-4 flex flex-col space-y-4`}>
|
||||
<div className={`flex items-center border border-transparent rounded-full p-2 -mr-2 -mt-2 group-hover:bg-[var(--vscode-sideBar-background)] group-hover:border-[var(--frontmatter-border)]`}>
|
||||
<div className="group-hover:hidden">
|
||||
<EllipsisHorizontalIcon className="w-4 h-4" />
|
||||
</div>
|
||||
|
||||
<div className="hidden group-hover:flex">
|
||||
{insertToContent && !snippet.isMediaSnippet && (
|
||||
<>
|
||||
<QuickAction title={l10n.t(LocalizationKey.commonInsertSnippet)} onClick={() => setShowInsertDialog(true)}>
|
||||
<PlusIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!snippet.sourcePath ? (
|
||||
<>
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.dashboardSnippetsViewItemQuickActionEditSnippet)}
|
||||
onClick={onOpenEdit}>
|
||||
<PencilIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.dashboardSnippetsViewItemQuickActionDeleteSnippet)}
|
||||
onClick={() => setShowAlert(true)}>
|
||||
<TrashIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
</>
|
||||
) : (
|
||||
<QuickAction
|
||||
title={l10n.t(LocalizationKey.dashboardSnippetsViewItemQuickActionViewSnippet)}
|
||||
onClick={showFile}>
|
||||
<EyeIcon className={`w-4 h-4`} aria-hidden="true" />
|
||||
</QuickAction>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<FooterActions
|
||||
insertEnabled={!!(insertToContent && !snippet.isMediaSnippet)}
|
||||
sourcePath={snippet.sourcePath}
|
||||
onEdit={onOpenEdit}
|
||||
onInsert={() => setShowInsertDialog(true)}
|
||||
onDelete={() => setShowAlert(true)} />
|
||||
</FeatureFlag>
|
||||
|
||||
<p className={`text-xs text-[var(--frontmatter-text)]`}>{snippet.description}</p>
|
||||
</li>
|
||||
|
||||
{showInsertDialog && (
|
||||
<FormDialog
|
||||
<SlideOver
|
||||
title={l10n.t(LocalizationKey.dashboardSnippetsViewItemInsertFormDialogTitle, snippet.title || snippetKey)}
|
||||
description={l10n.t(LocalizationKey.dashboardSnippetsViewItemInsertFormDialogDescription, (snippet.title || snippetKey).toLowerCase())}
|
||||
description={snippet.description || l10n.t(LocalizationKey.dashboardSnippetsViewItemInsertFormDialogDescription, (snippet.title || snippetKey).toLowerCase())}
|
||||
isSaveDisabled={!insertToContent}
|
||||
trigger={insertToArticle}
|
||||
dismiss={() => setShowInsertDialog(false)}
|
||||
@@ -264,11 +226,11 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
filePath={viewData?.data?.filePath}
|
||||
fieldInfo={viewData?.data?.snippetInfo?.fields}
|
||||
selection={viewData?.data?.selection} />
|
||||
</FormDialog>
|
||||
</SlideOver>
|
||||
)}
|
||||
|
||||
{showEditDialog && (
|
||||
<FormDialog
|
||||
<SlideOver
|
||||
title={l10n.t(LocalizationKey.dashboardSnippetsViewItemEditFormDialogTitle, snippet.title || snippetKey)}
|
||||
description={l10n.t(LocalizationKey.dashboardSnippetsViewItemEditFormDialogDescription, (snippet.title || snippetKey).toLowerCase())}
|
||||
isSaveDisabled={!snippetTitle || !snippetOriginalBody}
|
||||
@@ -287,7 +249,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
onDescriptionUpdate={(value: string) => setSnippetDescription(value)}
|
||||
onBodyUpdate={(value: string) => setSnippetOriginalBody(value)}
|
||||
/>
|
||||
</FormDialog>
|
||||
</SlideOver>
|
||||
)}
|
||||
|
||||
{showAlert && (
|
||||
|
||||
97
src/dashboardWebView/components/SnippetsView/ItemMenu.tsx
Normal file
97
src/dashboardWebView/components/SnippetsView/ItemMenu.tsx
Normal file
@@ -0,0 +1,97 @@
|
||||
import * as React from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { useCallback } from 'react';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { openFile } from '../../utils/MessageHandlers';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown';
|
||||
import { EllipsisHorizontalIcon } from '@heroicons/react/24/solid';
|
||||
import { EyeIcon, PencilIcon, PlusIcon, TrashIcon } from '@heroicons/react/24/outline';
|
||||
|
||||
export interface IItemMenuProps {
|
||||
insertEnabled: boolean;
|
||||
sourcePath?: string;
|
||||
onEdit?: () => void;
|
||||
onInsert: () => void;
|
||||
onDelete?: () => void;
|
||||
}
|
||||
|
||||
export const ItemMenu: React.FunctionComponent<IItemMenuProps> = ({
|
||||
insertEnabled,
|
||||
sourcePath,
|
||||
onEdit,
|
||||
onInsert,
|
||||
onDelete,
|
||||
}: React.PropsWithChildren<IItemMenuProps>) => {
|
||||
|
||||
const showFile = useCallback(() => {
|
||||
openFile(sourcePath);
|
||||
}, [sourcePath]);
|
||||
|
||||
if (!onEdit && !onDelete && !sourcePath && !insertEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`group/actions absolute top-4 right-4 flex flex-col space-y-4`}>
|
||||
<div className={`flex items-center border border-transparent rounded-full p-1 -mr-2 -mt-3 group-hover/actions:bg-[var(--vscode-sideBar-background)] group-hover/actions:border-[var(--frontmatter-border)]`}>
|
||||
<div className="relative z-10 flex text-left">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className='text-[var(--vscode-tab-inactiveForeground)] hover:text-[var(--vscode-tab-activeForeground)]'>
|
||||
<span className="sr-only">{l10n.t(LocalizationKey.commonMenu)}</span>
|
||||
<EllipsisHorizontalIcon className="w-4 h-4" aria-hidden="true" />
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuContent align="end">
|
||||
{
|
||||
insertEnabled && (
|
||||
<DropdownMenuItem
|
||||
title={l10n.t(LocalizationKey.commonInsertSnippet)}
|
||||
onClick={onInsert}>
|
||||
<PlusIcon className="mr-2 h-4 w-4" aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.commonInsertSnippet)}</span>
|
||||
</DropdownMenuItem>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!sourcePath ? (
|
||||
<>
|
||||
{
|
||||
onEdit && (
|
||||
<DropdownMenuItem
|
||||
title={l10n.t(LocalizationKey.dashboardSnippetsViewItemQuickActionEditSnippet)}
|
||||
onClick={onEdit}>
|
||||
<PencilIcon className="mr-2 h-4 w-4" aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardSnippetsViewItemQuickActionEditSnippet)}</span>
|
||||
</DropdownMenuItem>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
onDelete && (
|
||||
<DropdownMenuItem
|
||||
title={l10n.t(LocalizationKey.dashboardSnippetsViewItemQuickActionDeleteSnippet)}
|
||||
onClick={onDelete}
|
||||
className={`focus:bg-[var(--vscode-statusBarItem-errorBackground)] focus:text-[var(--vscode-statusBarItem-errorForeground)]`}>
|
||||
<TrashIcon className="mr-2 h-4 w-4" aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardSnippetsViewItemQuickActionDeleteSnippet)}</span>
|
||||
</DropdownMenuItem>
|
||||
)
|
||||
}
|
||||
</>
|
||||
) : (
|
||||
<DropdownMenuItem
|
||||
title={l10n.t(LocalizationKey.dashboardSnippetsViewItemQuickActionViewSnippet)}
|
||||
onClick={showFile}>
|
||||
<EyeIcon className="mr-2 h-4 w-4" aria-hidden={true} />
|
||||
<span>{l10n.t(LocalizationKey.dashboardSnippetsViewItemQuickActionViewSnippet)}</span>
|
||||
</DropdownMenuItem>
|
||||
)
|
||||
}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -155,8 +155,10 @@ ${snippetBody}
|
||||
value
|
||||
});
|
||||
} catch (e) {
|
||||
messageHandler.send(GeneralCommands.toVSCode.logging.error, `SnippetForm: ${(e as Error).message}`);
|
||||
console.log('Error', (e as Error).message)
|
||||
messageHandler.send(GeneralCommands.toVSCode.logging.error, {
|
||||
message: `SnippetForm: ${(e as Error).message}`,
|
||||
location: 'DASHBOARD'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,7 +199,10 @@ ${snippetBody}
|
||||
{field.title || field.name}
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
<SnippetInputField field={field} fieldInfo={fieldInfo} onValueChange={onTextChange} />
|
||||
<SnippetInputField
|
||||
field={field}
|
||||
fieldInfo={fieldInfo}
|
||||
onValueChange={onTextChange} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -27,31 +27,41 @@ export const SnippetInputField: React.FunctionComponent<ISnippetInputFieldProps>
|
||||
|
||||
if (field.type === 'choice') {
|
||||
return (
|
||||
<div className="relative">
|
||||
<select
|
||||
name={field.name}
|
||||
value={field.value || ''}
|
||||
className={`block w-full sm:text-sm pr-2 appearance-none disabled:opacity-50 rounded bg-[var(--vscode-input-background)] text-[var(--vscode-input-foreground)] placeholder-[var(--vscode-input-placeholderForeground)] border-[var(--frontmatter-border)] focus:border-[var(--vscode-focusBorder)] focus:outline-0`}
|
||||
style={{
|
||||
boxShadow: "none"
|
||||
}}
|
||||
onChange={(e) => onValueChange(field, e.target.value)}
|
||||
>
|
||||
{(field.choices || [])?.map((option: string | Choice, index: number) =>
|
||||
typeof option === 'string' ? (
|
||||
<option key={index} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
) : (
|
||||
<option key={index} value={option.id}>
|
||||
{option.title}
|
||||
</option>
|
||||
)
|
||||
)}
|
||||
</select>
|
||||
<>
|
||||
<div className="relative">
|
||||
<select
|
||||
name={field.name}
|
||||
value={field.value || ''}
|
||||
className={`block w-full sm:text-sm pr-2 appearance-none disabled:opacity-50 rounded bg-[var(--vscode-input-background)] text-[var(--vscode-input-foreground)] placeholder-[var(--vscode-input-placeholderForeground)] border-[var(--frontmatter-border)] focus:border-[var(--vscode-focusBorder)] focus:outline-0`}
|
||||
style={{
|
||||
boxShadow: "none"
|
||||
}}
|
||||
onChange={(e) => onValueChange(field, e.target.value)}
|
||||
>
|
||||
{(field.choices || [])?.map((option: string | Choice, index: number) =>
|
||||
typeof option === 'string' ? (
|
||||
<option key={index} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
) : (
|
||||
<option key={index} value={option.id}>
|
||||
{option.title}
|
||||
</option>
|
||||
)
|
||||
)}
|
||||
</select>
|
||||
|
||||
<ChevronDownIcon className="absolute top-3 right-2 w-4 h-4 text-gray-500" />
|
||||
</div>
|
||||
<ChevronDownIcon className="absolute top-3 right-2 w-4 h-4 text-gray-500" />
|
||||
</div>
|
||||
|
||||
{
|
||||
field.description && (
|
||||
<p className="text-xs text-[var(--vscode--settings-headerForeground)] opacity-75 mt-2 mx-2">
|
||||
{field.description}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -60,6 +70,7 @@ export const SnippetInputField: React.FunctionComponent<ISnippetInputFieldProps>
|
||||
<TextField
|
||||
name={field.name}
|
||||
value={field.value || ''}
|
||||
description={field.description}
|
||||
onChange={(e) => onValueChange(field, e)}
|
||||
rows={4}
|
||||
multiline
|
||||
@@ -71,6 +82,7 @@ export const SnippetInputField: React.FunctionComponent<ISnippetInputFieldProps>
|
||||
<TextField
|
||||
name={field.name}
|
||||
value={field.value || ''}
|
||||
description={field.description}
|
||||
onChange={(e) => onValueChange(field, e)}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -4,19 +4,21 @@ import * as React from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { FeatureFlag } from '../../../components/features/FeatureFlag';
|
||||
import { FEATURE_FLAG, WEBSITE_LINKS } from '../../../constants';
|
||||
import { FEATURE_FLAG, GeneralCommands, WEBSITE_LINKS } from '../../../constants';
|
||||
import { TelemetryEvent } from '../../../constants/TelemetryEvent';
|
||||
import { SnippetParser } from '../../../helpers/SnippetParser';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { ModeAtom, SettingsSelector, ViewDataSelector } from '../../state';
|
||||
import { FilterInput } from '../Header/FilterInput';
|
||||
import { PageLayout } from '../Layout/PageLayout';
|
||||
import { FormDialog } from '../Modals/FormDialog';
|
||||
import { SponsorMsg } from '../Layout/SponsorMsg';
|
||||
import { Item } from './Item';
|
||||
import { NewForm } from './NewForm';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { List } from '../Media/List';
|
||||
import { SlideOver } from '../Modals/SlideOver';
|
||||
import { DEFAULT_DASHBOARD_FEATURE_FLAGS } from '../../../constants/DefaultFeatureFlags';
|
||||
|
||||
export interface ISnippetsProps { }
|
||||
|
||||
@@ -80,15 +82,24 @@ export const Snippets: React.FunctionComponent<ISnippetsProps> = (
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
Messenger.send(DashboardMessage.setTitle, l10n.t(LocalizationKey.dashboardHeaderTabsSnippets));
|
||||
|
||||
Messenger.send(DashboardMessage.sendTelemetry, {
|
||||
event: TelemetryEvent.webviewSnippetsView
|
||||
});
|
||||
|
||||
Messenger.send(GeneralCommands.toVSCode.logging.info, {
|
||||
message: `Snippets view loaded`,
|
||||
location: 'DASHBOARD'
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<PageLayout
|
||||
header={
|
||||
<FeatureFlag features={mode?.features || []} flag={FEATURE_FLAG.dashboard.snippets.manage}>
|
||||
<FeatureFlag
|
||||
features={mode?.features || DEFAULT_DASHBOARD_FEATURE_FLAGS}
|
||||
flag={FEATURE_FLAG.dashboard.snippets.manage}>
|
||||
<div
|
||||
className={`py-3 px-4 flex items-center justify-between border-b border-[var(--frontmatter-border)]`}
|
||||
aria-label={l10n.t(LocalizationKey.dashboardSnippetsViewSnippetsAriaLabel)}
|
||||
@@ -128,14 +139,13 @@ export const Snippets: React.FunctionComponent<ISnippetsProps> = (
|
||||
)}
|
||||
|
||||
{(snippetKeys && snippetKeys.length > 0) ? (
|
||||
<ul
|
||||
role="list"
|
||||
className={`grid grid-cols-2 gap-x-4 gap-y-8 sm:grid-cols-3 sm:gap-x-6 lg:grid-cols-4 xl:gap-x-8`}
|
||||
<List
|
||||
gap={4}
|
||||
>
|
||||
{snippetKeys.map((snippetKey: any, index: number) => (
|
||||
<Item key={index} snippetKey={snippetKey} snippet={snippets[snippetKey]} />
|
||||
))}
|
||||
</ul>
|
||||
</List>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center text-white">
|
||||
<div className={`flex flex-col items-center text-[var(--frontmatter-text)]`}>
|
||||
@@ -157,7 +167,7 @@ export const Snippets: React.FunctionComponent<ISnippetsProps> = (
|
||||
)}
|
||||
|
||||
{showCreateDialog && (
|
||||
<FormDialog
|
||||
<SlideOver
|
||||
title={l10n.t(LocalizationKey.dashboardSnippetsViewSnippetsFormDialogTitle)}
|
||||
description={``}
|
||||
isSaveDisabled={!snippetTitle || !snippetBody}
|
||||
@@ -176,7 +186,7 @@ export const Snippets: React.FunctionComponent<ISnippetsProps> = (
|
||||
onDescriptionUpdate={(value: string) => setSnippetDescription(value)}
|
||||
onBodyUpdate={(value: string) => setSnippetBody(value)}
|
||||
/>
|
||||
</FormDialog>
|
||||
</SlideOver>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ChevronRightIcon, ArrowDownTrayIcon } from '@heroicons/react/24/outline
|
||||
import * as React from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { TelemetryEvent } from '../../../constants';
|
||||
import { GeneralCommands, TelemetryEvent } from '../../../constants';
|
||||
import { TaxonomyData } from '../../../models';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { Page, PageMappings } from '../../models';
|
||||
@@ -53,9 +53,16 @@ export const TaxonomyView: React.FunctionComponent<ITaxonomyViewProps> = ({
|
||||
}, [settings?.tags, settings?.categories, settings?.customTaxonomy]);
|
||||
|
||||
useEffect(() => {
|
||||
Messenger.send(DashboardMessage.setTitle, l10n.t(LocalizationKey.dashboardHeaderTabsTaxonomies));
|
||||
|
||||
Messenger.send(DashboardMessage.sendTelemetry, {
|
||||
event: TelemetryEvent.webviewTaxonomyDashboard
|
||||
});
|
||||
|
||||
Messenger.send(GeneralCommands.toVSCode.logging.info, {
|
||||
message: 'Taxonomy view loaded',
|
||||
location: 'DASHBOARD'
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
@@ -2,12 +2,24 @@ import { StopIcon } from '@heroicons/react/24/outline';
|
||||
import * as React from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { useEffect } from 'react';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { TelemetryEvent } from '../../../constants';
|
||||
|
||||
export interface IUnknownViewProps { }
|
||||
|
||||
export const UnknownView: React.FunctionComponent<IUnknownViewProps> = (
|
||||
_: React.PropsWithChildren<IUnknownViewProps>
|
||||
) => {
|
||||
useEffect(() => {
|
||||
Messenger.send(DashboardMessage.setTitle, "Unknown View");
|
||||
|
||||
Messenger.send(DashboardMessage.sendTelemetry, {
|
||||
event: TelemetryEvent.webviewUnknown
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={`w-full h-full flex items-center justify-center`}>
|
||||
<div className={`flex flex-col items-center text-[var(--frontmatter-text)]`}>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BookOpenIcon, HeartIcon, StarIcon } from '@heroicons/react/24/outline';
|
||||
import * as React from 'react';
|
||||
import { DOCUMENTATION_LINK, GITHUB_LINK, REVIEW_LINK, SPONSOR_LINK, TelemetryEvent } from '../../../constants';
|
||||
import { DOCUMENTATION_LINK, GITHUB_LINK, GeneralCommands, REVIEW_LINK, SPONSOR_LINK, TelemetryEvent } from '../../../constants';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { FrontMatterIcon } from '../../../panelWebView/components/Icons/FrontMatterIcon';
|
||||
import { GitHubIcon } from '../../../panelWebView/components/Icons/GitHubIcon';
|
||||
@@ -29,6 +29,13 @@ export const WelcomeScreen: React.FunctionComponent<IWelcomeScreenProps> = ({
|
||||
event: TelemetryEvent.webviewWelcomeScreen
|
||||
});
|
||||
|
||||
Messenger.send(GeneralCommands.toVSCode.logging.info, {
|
||||
message: 'Welcome screen loaded',
|
||||
location: "DASHBOARD"
|
||||
});
|
||||
|
||||
Messenger.send(DashboardMessage.setTitle, "Welcome to Front Matter");
|
||||
|
||||
const crntState: any = Messenger.getState() || {};
|
||||
Messenger.setState({
|
||||
...crntState,
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
SearchReadyAtom,
|
||||
ModeAtom
|
||||
} from '../state';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { Messenger, messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { EventData } from '@estruyf/vscode/dist/models';
|
||||
import { NavigationType } from '../models';
|
||||
import { GeneralCommands } from '../../constants';
|
||||
@@ -31,6 +31,11 @@ export default function useMessages() {
|
||||
const messageListener = (event: MessageEvent<EventData<any>>) => {
|
||||
const message = event.data;
|
||||
|
||||
messageHandler.send(GeneralCommands.toVSCode.logging.verbose, {
|
||||
message: `Message received: ${message.command}`,
|
||||
location: 'DASHBOARD'
|
||||
});
|
||||
|
||||
switch (message.command) {
|
||||
case DashboardCommand.loading:
|
||||
setLoading(message.payload);
|
||||
@@ -82,7 +87,7 @@ export default function useMessages() {
|
||||
return () => {
|
||||
Messenger.unlisten(messageListener);
|
||||
};
|
||||
}, ['']);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
loading,
|
||||
|
||||
139
src/extension.ts
139
src/extension.ts
@@ -16,7 +16,6 @@ import { PagesListener } from './listeners/dashboard';
|
||||
import { ModeSwitch } from './services/ModeSwitch';
|
||||
import { PagesParser } from './services/PagesParser';
|
||||
import { ContentType, Telemetry, Extension } from './helpers';
|
||||
import { TaxonomyType } from './models';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import {
|
||||
Backers,
|
||||
@@ -46,6 +45,10 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
const { subscriptions, extensionUri, extensionPath } = context;
|
||||
|
||||
const extension = Extension.getInstance(context);
|
||||
|
||||
Logger.info(`Activating ${EXTENSION_NAME} version ${Extension.getInstance().version}...`);
|
||||
Logger.info(`Logging level: ${Logger.getLevel()}`);
|
||||
|
||||
// Set development context
|
||||
if (!Extension.getInstance().isProductionMode) {
|
||||
vscode.commands.executeCommand('setContext', CONTEXT.isDevelopment, true);
|
||||
@@ -89,8 +92,13 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
Dashboard.init();
|
||||
Dashboard.registerCommands();
|
||||
|
||||
// Multilingual commands
|
||||
i18n.register();
|
||||
|
||||
// Setting commands
|
||||
Settings.registerCommands();
|
||||
SettingsHelper.registerCommands();
|
||||
|
||||
if (!extension.getVersion().usedVersion) {
|
||||
vscode.commands.executeCommand(COMMAND_NAME.dashboard);
|
||||
}
|
||||
@@ -125,101 +133,23 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
}
|
||||
);
|
||||
|
||||
const createTag = vscode.commands.registerCommand(COMMAND_NAME.createTag, () => {
|
||||
Settings.create(TaxonomyType.Tag);
|
||||
});
|
||||
|
||||
const createCategory = vscode.commands.registerCommand(COMMAND_NAME.createCategory, () => {
|
||||
Settings.create(TaxonomyType.Category);
|
||||
});
|
||||
|
||||
const exportTaxonomy = vscode.commands.registerCommand(
|
||||
COMMAND_NAME.exportTaxonomy,
|
||||
Settings.export
|
||||
);
|
||||
|
||||
const remap = vscode.commands.registerCommand(COMMAND_NAME.remap, Settings.remap);
|
||||
|
||||
// Register all the article commands
|
||||
Article.registerCommands(subscriptions);
|
||||
|
||||
subscriptions.push(
|
||||
vscode.commands.registerCommand(COMMAND_NAME.initTemplate, () =>
|
||||
Project.createSampleTemplate(true)
|
||||
)
|
||||
);
|
||||
|
||||
// Register project folders
|
||||
const registerFolder = vscode.commands.registerCommand(
|
||||
COMMAND_NAME.registerFolder,
|
||||
Folders.register
|
||||
);
|
||||
|
||||
const unregisterFolder = vscode.commands.registerCommand(
|
||||
COMMAND_NAME.unregisterFolder,
|
||||
Folders.unregister
|
||||
);
|
||||
|
||||
const createFolder = vscode.commands.registerCommand(
|
||||
COMMAND_NAME.createFolder,
|
||||
Folders.addMediaFolder
|
||||
);
|
||||
|
||||
/**
|
||||
* Template creation
|
||||
*/
|
||||
const createTemplate = vscode.commands.registerCommand(
|
||||
COMMAND_NAME.createTemplate,
|
||||
Template.generate
|
||||
);
|
||||
const createFromTemplate = vscode.commands.registerCommand(
|
||||
COMMAND_NAME.createFromTemplate,
|
||||
(folder: vscode.Uri) => {
|
||||
const folderPath = Folders.getFolderPath(folder);
|
||||
if (folderPath) {
|
||||
Template.create(folderPath);
|
||||
}
|
||||
}
|
||||
);
|
||||
Template.registerCommands();
|
||||
|
||||
/**
|
||||
* Content creation
|
||||
*/
|
||||
const createByContentType = vscode.commands.registerCommand(
|
||||
COMMAND_NAME.createByContentType,
|
||||
ContentType.createContent
|
||||
);
|
||||
const createByTemplate = vscode.commands.registerCommand(
|
||||
COMMAND_NAME.createByTemplate,
|
||||
Folders.create
|
||||
);
|
||||
const createContent = vscode.commands.registerCommand(COMMAND_NAME.createContent, Content.create);
|
||||
ContentType.registerCommands();
|
||||
Content.registerCommands();
|
||||
Folders.registerCommands();
|
||||
|
||||
subscriptions.push(
|
||||
vscode.commands.registerCommand(COMMAND_NAME.generateContentType, ContentType.generate)
|
||||
);
|
||||
|
||||
subscriptions.push(
|
||||
vscode.commands.registerCommand(COMMAND_NAME.addMissingFields, ContentType.addMissingFields)
|
||||
);
|
||||
|
||||
subscriptions.push(
|
||||
vscode.commands.registerCommand(COMMAND_NAME.setContentType, ContentType.setContentType)
|
||||
);
|
||||
|
||||
// Initialize command
|
||||
subscriptions.push(
|
||||
vscode.commands.registerCommand(COMMAND_NAME.init, async (cb: Function) => {
|
||||
await Project.init();
|
||||
|
||||
if (cb) {
|
||||
cb();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Settings promotion command
|
||||
subscriptions.push(vscode.commands.registerCommand(COMMAND_NAME.promote, SettingsHelper.promote));
|
||||
// Project commands
|
||||
Project.registerCommands();
|
||||
|
||||
// Collapse all sections in the webview
|
||||
const collapseAll = vscode.commands.registerCommand(COMMAND_NAME.collapseSections, () => {
|
||||
@@ -236,7 +166,7 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
-100
|
||||
);
|
||||
fmStatusBarItem.command = COMMAND_NAME.dashboard;
|
||||
fmStatusBarItem.text = `$(fm-logo)`;
|
||||
fmStatusBarItem.text = `$(fm-logo) ${extension.getVersion().installedVersion}`;
|
||||
fmStatusBarItem.tooltip = EXTENSION_NAME;
|
||||
fmStatusBarItem.show();
|
||||
|
||||
@@ -279,6 +209,16 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
vscode.commands.registerCommand(COMMAND_NAME.preview, () => Preview.open(extensionPath))
|
||||
);
|
||||
|
||||
// Open docs
|
||||
subscriptions.push(
|
||||
vscode.commands.registerCommand(COMMAND_NAME.docs, () => {
|
||||
vscode.commands.executeCommand(
|
||||
`simpleBrowser.show`,
|
||||
`https://${extension.isBetaVersion() ? `beta.` : ``}frontmatter.codes/docs`
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
// Chat to the bot
|
||||
subscriptions.push(
|
||||
vscode.commands.registerCommand(COMMAND_NAME.chatbot, () => Chatbot.open(extensionPath))
|
||||
@@ -299,7 +239,7 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
ModeSwitch.register();
|
||||
|
||||
// Diagnostics
|
||||
subscriptions.push(vscode.commands.registerCommand(COMMAND_NAME.diagnostics, Diagnostics.show));
|
||||
Diagnostics.registerCommands();
|
||||
|
||||
// Git
|
||||
GitListener.init();
|
||||
@@ -311,29 +251,8 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
// Cache commands
|
||||
Cache.registerCommands();
|
||||
|
||||
// Project switching
|
||||
Project.registerCommands();
|
||||
|
||||
// Subscribe all commands
|
||||
subscriptions.push(
|
||||
insertTags,
|
||||
PanelView,
|
||||
insertCategories,
|
||||
createTag,
|
||||
createCategory,
|
||||
exportTaxonomy,
|
||||
remap,
|
||||
createFromTemplate,
|
||||
createTemplate,
|
||||
registerFolder,
|
||||
unregisterFolder,
|
||||
createContent,
|
||||
createByContentType,
|
||||
createByTemplate,
|
||||
collapseAll,
|
||||
createFolder,
|
||||
fmStatusBarItem
|
||||
);
|
||||
subscriptions.push(insertTags, PanelView, insertCategories, collapseAll, fmStatusBarItem);
|
||||
|
||||
console.log(`𝖥𝗋𝗈𝗇𝗍 𝖬𝖺𝗍𝗍𝖾𝗋 𝖢𝖬𝖲 𝖺𝖼𝗍𝗂𝗏𝖺𝗍𝖾𝖽! 𝖱𝖾𝖺𝖽𝗒 𝗍𝗈 𝗌𝗍𝖺𝗋𝗍 𝗐𝗋𝗂𝗍𝗂𝗇𝗀... 👩💻🧑💻👨💻`);
|
||||
}
|
||||
@@ -345,7 +264,7 @@ const handleAutoDateUpdate = (e: vscode.TextDocumentWillSaveEvent) => {
|
||||
};
|
||||
|
||||
const triggerPageUpdate = (location: string) => {
|
||||
Logger.info(`Trigger page update: ${location}`);
|
||||
Logger.verbose(`Trigger page update: ${location}`);
|
||||
pageUpdateDebouncer(() => {
|
||||
StatusListener.verify(collection);
|
||||
}, 1000);
|
||||
|
||||
@@ -359,7 +359,7 @@ export class ArticleHelper {
|
||||
return false;
|
||||
}
|
||||
|
||||
const contentType = ArticleHelper.getContentType(article);
|
||||
const contentType = await ArticleHelper.getContentType(article);
|
||||
return !!contentType.pageBundle;
|
||||
}
|
||||
|
||||
@@ -379,13 +379,14 @@ export class ArticleHelper {
|
||||
/**
|
||||
* Get date from front matter
|
||||
*/
|
||||
public static getDate(article: ParsedFrontMatter | null | undefined) {
|
||||
public static async getDate(article: ParsedFrontMatter | null | undefined) {
|
||||
if (!article || !article.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
|
||||
const dateField = ArticleHelper.getPublishDateField(article) || DefaultFields.PublishingDate;
|
||||
const dateField =
|
||||
(await ArticleHelper.getPublishDateField(article)) || DefaultFields.PublishingDate;
|
||||
|
||||
if (typeof article.data[dateField] !== 'undefined') {
|
||||
if (dateFormat && typeof dateFormat === 'string') {
|
||||
@@ -404,12 +405,12 @@ export class ArticleHelper {
|
||||
* @param article
|
||||
* @returns
|
||||
*/
|
||||
public static getPublishDateField(article: ParsedFrontMatter | null) {
|
||||
public static async getPublishDateField(article: ParsedFrontMatter | null) {
|
||||
if (!article || !article.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const articleCt = ArticleHelper.getContentType(article);
|
||||
const articleCt = await ArticleHelper.getContentType(article);
|
||||
const pubDateField = articleCt.fields.find((f) => f.isPublishDate);
|
||||
|
||||
return (
|
||||
@@ -424,12 +425,14 @@ export class ArticleHelper {
|
||||
* @param article
|
||||
* @returns
|
||||
*/
|
||||
public static getModifiedDateField(article: ParsedFrontMatter | null): Field | undefined {
|
||||
public static async getModifiedDateField(
|
||||
article: ParsedFrontMatter | null
|
||||
): Promise<Field | undefined> {
|
||||
if (!article || !article.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const articleCt = ArticleHelper.getContentType(article);
|
||||
const articleCt = await ArticleHelper.getContentType(article);
|
||||
const modDateField = articleCt.fields.find((f) => f.isModifiedDate);
|
||||
|
||||
return modDateField;
|
||||
@@ -447,7 +450,7 @@ export class ArticleHelper {
|
||||
* Retrieve the content type of the current file
|
||||
* @param updatedMetadata
|
||||
*/
|
||||
public static getContentType(article: ParsedFrontMatter): IContentType {
|
||||
public static async getContentType(article: ParsedFrontMatter): Promise<IContentType> {
|
||||
const contentTypes = ArticleHelper.getContentTypes();
|
||||
|
||||
if (!contentTypes || !article.data) {
|
||||
@@ -460,7 +463,7 @@ export class ArticleHelper {
|
||||
if (article.data.type) {
|
||||
contentType = contentTypes.find((ct) => ct.name === article.data.type);
|
||||
} else if (!contentType && article.path) {
|
||||
const pageFolder = Folders.getPageFolderByFilePath(article.path);
|
||||
const pageFolder = await Folders.getPageFolderByFilePath(article.path);
|
||||
if (pageFolder && pageFolder.contentTypes?.length === 1) {
|
||||
const contentTypeName = pageFolder.contentTypes[0];
|
||||
contentType = contentTypes.find((ct) => ct.name === contentTypeName);
|
||||
@@ -488,8 +491,8 @@ export class ArticleHelper {
|
||||
* Update all dates in the metadata
|
||||
* @param metadata
|
||||
*/
|
||||
public static updateDates(article: ParsedFrontMatter) {
|
||||
const contentType = ArticleHelper.getContentType(article);
|
||||
public static async updateDates(article: ParsedFrontMatter) {
|
||||
const contentType = await ArticleHelper.getContentType(article);
|
||||
const dateFields = contentType.fields.filter((field) => field.type === 'datetime');
|
||||
|
||||
for (const dateField of dateFields) {
|
||||
@@ -529,7 +532,7 @@ export class ArticleHelper {
|
||||
const fileType = Settings.get<string>(SETTING_CONTENT_DEFAULT_FILETYPE);
|
||||
|
||||
let prefix = Settings.get<string>(SETTING_TEMPLATES_PREFIX);
|
||||
prefix = ArticleHelper.getFilePrefix(prefix, folderPath, contentType);
|
||||
prefix = await ArticleHelper.getFilePrefix(prefix, folderPath, contentType);
|
||||
|
||||
// Name of the file or folder to create
|
||||
let sanitizedName = ArticleHelper.sanitize(titleValue);
|
||||
@@ -588,18 +591,18 @@ export class ArticleHelper {
|
||||
* @param contentType
|
||||
* @returns
|
||||
*/
|
||||
public static getFilePrefix(
|
||||
public static async getFilePrefix(
|
||||
prefix: string | null | undefined,
|
||||
filePath?: string,
|
||||
contentType?: IContentType
|
||||
): string | undefined {
|
||||
): Promise<string | undefined> {
|
||||
if (!prefix) {
|
||||
prefix = undefined;
|
||||
}
|
||||
|
||||
// Retrieve the file prefix from the folder
|
||||
if (filePath) {
|
||||
const filePrefixOnFolder = Folders.getFilePrefixByFolderPath(filePath);
|
||||
const filePrefixOnFolder = await Folders.getFilePrefixBeFilePath(filePath);
|
||||
if (typeof filePrefixOnFolder !== 'undefined') {
|
||||
prefix = filePrefixOnFolder;
|
||||
}
|
||||
|
||||
@@ -3,12 +3,14 @@ import { PagesListener } from './../listeners/dashboard';
|
||||
import {
|
||||
ArticleHelper,
|
||||
CustomScript,
|
||||
Extension,
|
||||
Logger,
|
||||
Settings,
|
||||
processArticlePlaceholdersFromData,
|
||||
processTimePlaceholders
|
||||
} from '.';
|
||||
import {
|
||||
COMMAND_NAME,
|
||||
DefaultFieldValues,
|
||||
EXTENSION_NAME,
|
||||
FEATURE_FLAG,
|
||||
@@ -40,6 +42,32 @@ import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../localization';
|
||||
|
||||
export class ContentType {
|
||||
/**
|
||||
* Registers the commands related to content types.
|
||||
*
|
||||
* @param subscriptions - The array of subscriptions to which the commands will be added.
|
||||
*/
|
||||
public static async registerCommands() {
|
||||
const ext = Extension.getInstance();
|
||||
const subscriptions = ext.subscriptions;
|
||||
|
||||
subscriptions.push(
|
||||
commands.registerCommand(COMMAND_NAME.createByContentType, ContentType.createContent)
|
||||
);
|
||||
|
||||
subscriptions.push(
|
||||
commands.registerCommand(COMMAND_NAME.generateContentType, ContentType.generate)
|
||||
);
|
||||
|
||||
subscriptions.push(
|
||||
commands.registerCommand(COMMAND_NAME.addMissingFields, ContentType.addMissingFields)
|
||||
);
|
||||
|
||||
subscriptions.push(
|
||||
commands.registerCommand(COMMAND_NAME.setContentType, ContentType.setContentType)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the draft field
|
||||
* @returns
|
||||
@@ -58,8 +86,8 @@ export class ContentType {
|
||||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
public static getDraftStatus(article: ParsedFrontMatter) {
|
||||
const contentType = ArticleHelper.getContentType(article);
|
||||
public static async getDraftStatus(article: ParsedFrontMatter) {
|
||||
const contentType = await ArticleHelper.getContentType(article);
|
||||
const draftSetting = ContentType.getDraftField();
|
||||
|
||||
const draftField = contentType.fields.find((f) => f.type === 'draft');
|
||||
@@ -94,7 +122,8 @@ export class ContentType {
|
||||
}
|
||||
|
||||
const contentTypes = ContentType.getAll();
|
||||
const folders = Folders.get().filter((f) => !f.disableCreation);
|
||||
let folders = await Folders.get();
|
||||
folders = folders.filter((f) => !f.disableCreation);
|
||||
const folder = folders.find((f) => f.path === selectedFolder.path);
|
||||
|
||||
if (!folder) {
|
||||
@@ -251,7 +280,7 @@ export class ContentType {
|
||||
pageBundle = pageBundleAnswer === l10n.t(LocalizationKey.commonYes);
|
||||
}
|
||||
|
||||
const fields = ContentType.generateFields(content.data);
|
||||
const fields = await ContentType.generateFields(content.data);
|
||||
|
||||
// Update the type field in the page
|
||||
if (!overrideBool && editor) {
|
||||
@@ -274,7 +303,7 @@ export class ContentType {
|
||||
contentTypes.push(newContentType);
|
||||
}
|
||||
|
||||
await Settings.update(SETTING_TAXONOMY_CONTENT_TYPES, contentTypes, true);
|
||||
await Settings.safeUpdate(SETTING_TAXONOMY_CONTENT_TYPES, contentTypes, true);
|
||||
|
||||
const configPath = await Settings.projectConfigPath();
|
||||
const notificationAction = await Notifications.info(
|
||||
@@ -314,14 +343,14 @@ export class ContentType {
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = ArticleHelper.getContentType(article);
|
||||
const updatedFields = ContentType.generateFields(article.data, contentType.fields);
|
||||
const contentType = await ArticleHelper.getContentType(article);
|
||||
const updatedFields = await ContentType.generateFields(article.data, contentType.fields);
|
||||
|
||||
const contentTypes = ContentType.getAll() || [];
|
||||
const index = contentTypes.findIndex((ct) => ct.name === contentType.name);
|
||||
contentTypes[index].fields = updatedFields;
|
||||
|
||||
await Settings.update(SETTING_TAXONOMY_CONTENT_TYPES, contentTypes, true);
|
||||
await Settings.safeUpdate(SETTING_TAXONOMY_CONTENT_TYPES, contentTypes, true);
|
||||
|
||||
const configPath = await Settings.projectConfigPath();
|
||||
const notificationAction = await Notifications.info(
|
||||
@@ -531,8 +560,10 @@ export class ContentType {
|
||||
/**
|
||||
* Find the required fields
|
||||
*/
|
||||
public static findEmptyRequiredFields(article: ParsedFrontMatter): Field[][] | undefined {
|
||||
const contentType = ArticleHelper.getContentType(article);
|
||||
public static async findEmptyRequiredFields(
|
||||
article: ParsedFrontMatter
|
||||
): Promise<Field[][] | undefined> {
|
||||
const contentType = await ArticleHelper.getContentType(article);
|
||||
if (!contentType) {
|
||||
return;
|
||||
}
|
||||
@@ -713,7 +744,7 @@ export class ContentType {
|
||||
* @param fields
|
||||
* @returns
|
||||
*/
|
||||
private static generateFields(data: any, fields: any[] = []) {
|
||||
private static async generateFields(data: any, fields: any[] = []) {
|
||||
for (const field in data) {
|
||||
const fieldData = data[field];
|
||||
|
||||
@@ -751,15 +782,38 @@ export class ContentType {
|
||||
fieldData.length > 0 &&
|
||||
typeof fieldData[0] === 'object'
|
||||
) {
|
||||
const newFields = ContentType.generateFields(fieldData);
|
||||
const newFields = await ContentType.generateFields(fieldData);
|
||||
|
||||
// Combine all the fields for the field group
|
||||
const blockFields: Field[] = [];
|
||||
for (const newField of newFields) {
|
||||
for (const field of newField.fields) {
|
||||
if (blockFields.some((f) => f.name === field.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
blockFields.push(field);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a new field group
|
||||
const fieldGroups = Settings.get<FieldGroup[]>(SETTING_TAXONOMY_FIELD_GROUPS) || [];
|
||||
const fieldGroupName = `${field}_group`;
|
||||
const newFieldGroup: FieldGroup = {
|
||||
id: fieldGroupName,
|
||||
fields: blockFields
|
||||
};
|
||||
fieldGroups.push(newFieldGroup);
|
||||
await Settings.safeUpdate(SETTING_TAXONOMY_FIELD_GROUPS, fieldGroups, true);
|
||||
|
||||
fields.push({
|
||||
title: field,
|
||||
name: field,
|
||||
type: 'block',
|
||||
fields: newFields
|
||||
fieldGroup: [fieldGroupName]
|
||||
} as Field);
|
||||
} else if (fieldData && fieldData instanceof Object) {
|
||||
const newFields = ContentType.generateFields(fieldData);
|
||||
const newFields = await ContentType.generateFields(fieldData);
|
||||
fields.push({
|
||||
title: field,
|
||||
name: field,
|
||||
@@ -950,7 +1004,7 @@ export class ContentType {
|
||||
path: newFilePath
|
||||
};
|
||||
|
||||
data = ArticleHelper.updateDates(article);
|
||||
data = await ArticleHelper.updateDates(article);
|
||||
|
||||
if (isTypeSet) {
|
||||
delete data.type;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Settings } from './SettingsHelper';
|
||||
import { CommandType, EnvironmentType } from './../models/PanelSettings';
|
||||
import { CustomScript as ICustomScript, ScriptType } from '../models/PanelSettings';
|
||||
import { window, env as vscodeEnv, ProgressLocation, Uri, commands } from 'vscode';
|
||||
import { ArticleHelper, Logger, Telemetry } from '.';
|
||||
import { ArticleHelper, Logger, MediaHelpers, Telemetry } from '.';
|
||||
import { Folders, WORKSPACE_PLACEHOLDER } from '../commands/Folders';
|
||||
import { exec, execSync } from 'child_process';
|
||||
import * as os from 'os';
|
||||
@@ -269,7 +269,13 @@ export class CustomScript {
|
||||
): Promise<void> {
|
||||
if (output) {
|
||||
try {
|
||||
const data = JSON.parse(output);
|
||||
const data: {
|
||||
frontmatter?: { [key: string]: any };
|
||||
fmAction?: 'open' | 'copyMediaMetadata' | 'copyMediaMetadataAndDelete' | 'deleteMedia';
|
||||
fmPath?: string;
|
||||
fmSourcePath?: string;
|
||||
fmDestinationPath?: string;
|
||||
} = JSON.parse(output);
|
||||
|
||||
if (data.frontmatter) {
|
||||
let article = null;
|
||||
@@ -305,6 +311,21 @@ export class CustomScript {
|
||||
if (data.fmAction === 'open' && data.fmPath) {
|
||||
const uri = data.fmPath.startsWith(`http`) ? data.fmPath : Uri.file(data.fmPath);
|
||||
commands.executeCommand('vscode.open', uri);
|
||||
} else if (
|
||||
data.fmAction === 'copyMediaMetadata' &&
|
||||
data.fmSourcePath &&
|
||||
data.fmDestinationPath
|
||||
) {
|
||||
await MediaHelpers.copyMetadata(data.fmSourcePath, data.fmDestinationPath);
|
||||
} else if (
|
||||
data.fmAction === 'copyMediaMetadataAndDelete' &&
|
||||
data.fmSourcePath &&
|
||||
data.fmDestinationPath
|
||||
) {
|
||||
await MediaHelpers.copyMetadata(data.fmSourcePath, data.fmDestinationPath);
|
||||
await MediaHelpers.deleteFile(data.fmSourcePath);
|
||||
} else if (data.fmAction === 'deleteMedia' && data.fmPath) {
|
||||
await MediaHelpers.deleteFile(data.fmPath);
|
||||
}
|
||||
} else {
|
||||
Logger.error(`No frontmatter found.`);
|
||||
|
||||
@@ -57,6 +57,7 @@ import { Settings } from './SettingsHelper';
|
||||
import { parseWinPath } from './parseWinPath';
|
||||
import { TaxonomyHelper } from './TaxonomyHelper';
|
||||
import { ContentType } from './ContentType';
|
||||
import { Logger } from './Logger';
|
||||
|
||||
export class DashboardSettings {
|
||||
private static cachedSettings: ISettings | undefined = undefined;
|
||||
@@ -86,88 +87,97 @@ export class DashboardSettings {
|
||||
}
|
||||
|
||||
public static async getSettings() {
|
||||
const ext = Extension.getInstance();
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
const isInitialized = await Project.isInitialized();
|
||||
const pagination = Settings.get<boolean | number>(SETTING_DASHBOARD_CONTENT_PAGINATION);
|
||||
Logger.verbose('DashboardSettings:getSettings:start');
|
||||
|
||||
const settings = {
|
||||
projects: Settings.getProjects(),
|
||||
project: Settings.getProject(),
|
||||
git: await GitListener.getSettings(),
|
||||
beta: ext.isBetaVersion(),
|
||||
wsFolder: wsFolder ? wsFolder.fsPath : '',
|
||||
staticFolder: Folders.getStaticFolderRelativePath(),
|
||||
initialized: isInitialized,
|
||||
tags: (await TaxonomyHelper.get(TaxonomyType.Tag)) || [],
|
||||
categories: (await TaxonomyHelper.get(TaxonomyType.Category)) || [],
|
||||
customTaxonomy: Settings.get(SETTING_TAXONOMY_CUSTOM, true) || [],
|
||||
openOnStart: Settings.get(SETTING_DASHBOARD_OPENONSTART),
|
||||
versionInfo: ext.getVersion(),
|
||||
pageViewType: await ext.getState<DashboardViewType | undefined>(
|
||||
ExtensionState.PagesView,
|
||||
'workspace'
|
||||
),
|
||||
contentTypes: ContentType.getAll() || [],
|
||||
draftField: Settings.get<DraftField>(SETTING_CONTENT_DRAFT_FIELD),
|
||||
customSorting: Settings.get<SortingSetting[]>(SETTING_CONTENT_SORTING),
|
||||
contentFolders: Folders.get(),
|
||||
filters:
|
||||
Settings.get<(FilterType | { title: string; name: string })[]>(SETTING_CONTENT_FILTERS),
|
||||
crntFramework: Settings.get<string>(SETTING_FRAMEWORK_ID),
|
||||
framework: !isInitialized && wsFolder ? await FrameworkDetector.get(wsFolder.fsPath) : null,
|
||||
scripts: Settings.get<CustomScript[]>(SETTING_CUSTOM_SCRIPTS) || [],
|
||||
date: {
|
||||
format: Settings.get<string>(SETTING_DATE_FORMAT) || ''
|
||||
},
|
||||
dashboardState: {
|
||||
contents: {
|
||||
sorting: await ext.getState<SortingOption | undefined>(
|
||||
ExtensionState.Dashboard.Contents.Sorting,
|
||||
'workspace'
|
||||
),
|
||||
defaultSorting: Settings.get<string>(SETTING_CONTENT_SORTING_DEFAULT),
|
||||
tags: Settings.get<string>(SETTING_DASHBOARD_CONTENT_TAGS),
|
||||
templatesEnabled: Settings.get<boolean>(SETTING_TEMPLATES_ENABLED),
|
||||
pagination: pagination !== undefined ? pagination : true,
|
||||
cardFields: {
|
||||
state: Settings.get<string>(SETTING_DASHBOARD_CONTENT_CARD_STATE),
|
||||
date: Settings.get<string>(SETTING_DASHBOARD_CONTENT_CARD_DATE),
|
||||
title: Settings.get<string>(SETTING_DASHBOARD_CONTENT_CARD_TITLE),
|
||||
description: Settings.get<string>(SETTING_DASHBOARD_CONTENT_CARD_DESCRIPTION)
|
||||
try {
|
||||
const ext = Extension.getInstance();
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
const isInitialized = await Project.isInitialized();
|
||||
const pagination = Settings.get<boolean | number>(SETTING_DASHBOARD_CONTENT_PAGINATION);
|
||||
|
||||
const settings = {
|
||||
projects: Settings.getProjects(),
|
||||
project: Settings.getProject(),
|
||||
git: await GitListener.getSettings(),
|
||||
beta: ext.isBetaVersion(),
|
||||
wsFolder: wsFolder ? wsFolder.fsPath : '',
|
||||
staticFolder: Folders.getStaticFolderRelativePath(),
|
||||
initialized: isInitialized,
|
||||
tags: (await TaxonomyHelper.get(TaxonomyType.Tag)) || [],
|
||||
categories: (await TaxonomyHelper.get(TaxonomyType.Category)) || [],
|
||||
customTaxonomy: Settings.get(SETTING_TAXONOMY_CUSTOM, true) || [],
|
||||
openOnStart: Settings.get(SETTING_DASHBOARD_OPENONSTART),
|
||||
versionInfo: ext.getVersion(),
|
||||
pageViewType: await ext.getState<DashboardViewType | undefined>(
|
||||
ExtensionState.PagesView,
|
||||
'workspace'
|
||||
),
|
||||
contentTypes: ContentType.getAll() || [],
|
||||
draftField: Settings.get<DraftField>(SETTING_CONTENT_DRAFT_FIELD),
|
||||
customSorting: Settings.get<SortingSetting[]>(SETTING_CONTENT_SORTING),
|
||||
contentFolders: await Folders.get(),
|
||||
filters:
|
||||
Settings.get<(FilterType | { title: string; name: string })[]>(SETTING_CONTENT_FILTERS),
|
||||
crntFramework: Settings.get<string>(SETTING_FRAMEWORK_ID),
|
||||
framework: !isInitialized && wsFolder ? await FrameworkDetector.get(wsFolder.fsPath) : null,
|
||||
scripts: Settings.get<CustomScript[]>(SETTING_CUSTOM_SCRIPTS) || [],
|
||||
date: {
|
||||
format: Settings.get<string>(SETTING_DATE_FORMAT) || ''
|
||||
},
|
||||
dashboardState: {
|
||||
contents: {
|
||||
sorting: await ext.getState<SortingOption | undefined>(
|
||||
ExtensionState.Dashboard.Contents.Sorting,
|
||||
'workspace'
|
||||
),
|
||||
defaultSorting: Settings.get<string>(SETTING_CONTENT_SORTING_DEFAULT),
|
||||
tags: Settings.get<string>(SETTING_DASHBOARD_CONTENT_TAGS),
|
||||
templatesEnabled: Settings.get<boolean>(SETTING_TEMPLATES_ENABLED),
|
||||
pagination: pagination !== undefined ? pagination : true,
|
||||
cardFields: {
|
||||
state: Settings.get<string>(SETTING_DASHBOARD_CONTENT_CARD_STATE),
|
||||
date: Settings.get<string>(SETTING_DASHBOARD_CONTENT_CARD_DATE),
|
||||
title: Settings.get<string>(SETTING_DASHBOARD_CONTENT_CARD_TITLE),
|
||||
description: Settings.get<string>(SETTING_DASHBOARD_CONTENT_CARD_DESCRIPTION)
|
||||
}
|
||||
},
|
||||
media: {
|
||||
sorting: await ext.getState<SortingOption | undefined>(
|
||||
ExtensionState.Dashboard.Media.Sorting,
|
||||
'workspace'
|
||||
),
|
||||
defaultSorting: Settings.get<string>(SETTING_MEDIA_SORTING_DEFAULT),
|
||||
selectedFolder: await ext.getState<string | undefined>(
|
||||
ExtensionState.SelectedFolder,
|
||||
'workspace'
|
||||
),
|
||||
mimeTypes: Settings.get<string[]>(SETTING_MEDIA_SUPPORTED_MIMETYPES)
|
||||
},
|
||||
welcome: {
|
||||
contentFolders: await Folders.getContentFolders()
|
||||
}
|
||||
},
|
||||
dataFiles: await this.getDataFiles(),
|
||||
dataTypes: Settings.get<DataType[]>(SETTING_DATA_TYPES),
|
||||
snippets: Settings.get<Snippets>(SETTING_CONTENT_SNIPPETS),
|
||||
snippetsWrapper: Settings.get<boolean>(SETTING_SNIPPETS_WRAPPER),
|
||||
isBacker: await ext.getState<boolean | undefined>(CONTEXT.backer, 'global'),
|
||||
websiteUrl: Settings.get<string>(SETTING_WEBSITE_URL),
|
||||
media: {
|
||||
sorting: await ext.getState<SortingOption | undefined>(
|
||||
ExtensionState.Dashboard.Media.Sorting,
|
||||
'workspace'
|
||||
),
|
||||
defaultSorting: Settings.get<string>(SETTING_MEDIA_SORTING_DEFAULT),
|
||||
selectedFolder: await ext.getState<string | undefined>(
|
||||
ExtensionState.SelectedFolder,
|
||||
'workspace'
|
||||
),
|
||||
mimeTypes: Settings.get<string[]>(SETTING_MEDIA_SUPPORTED_MIMETYPES)
|
||||
contentTypes: Settings.get<MediaContentType[]>(SETTING_MEDIA_CONTENTTYPES) || [
|
||||
DEFAULT_MEDIA_CONTENT_TYPE
|
||||
]
|
||||
},
|
||||
welcome: {
|
||||
contentFolders: await Folders.getContentFolders()
|
||||
}
|
||||
},
|
||||
dataFiles: await this.getDataFiles(),
|
||||
dataTypes: Settings.get<DataType[]>(SETTING_DATA_TYPES),
|
||||
snippets: Settings.get<Snippets>(SETTING_CONTENT_SNIPPETS),
|
||||
snippetsWrapper: Settings.get<boolean>(SETTING_SNIPPETS_WRAPPER),
|
||||
isBacker: await ext.getState<boolean | undefined>(CONTEXT.backer, 'global'),
|
||||
websiteUrl: Settings.get<string>(SETTING_WEBSITE_URL),
|
||||
media: {
|
||||
contentTypes: Settings.get<MediaContentType[]>(SETTING_MEDIA_CONTENTTYPES) || [
|
||||
DEFAULT_MEDIA_CONTENT_TYPE
|
||||
]
|
||||
},
|
||||
lastUpdated: new Date().getTime()
|
||||
} as ISettings;
|
||||
lastUpdated: new Date().getTime()
|
||||
} as ISettings;
|
||||
|
||||
return settings;
|
||||
Logger.verbose('DashboardSettings:getSettings:end');
|
||||
|
||||
return settings;
|
||||
} catch (error) {
|
||||
Logger.error(`DashboardSettings:getSettings:error ${(error as Error).message}`);
|
||||
return {} as ISettings;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,6 +185,7 @@ export class DashboardSettings {
|
||||
* @returns
|
||||
*/
|
||||
private static async getDataFiles(): Promise<DataFile[]> {
|
||||
Logger.verbose('DashboardSettings:getDataFiles:start');
|
||||
const wsPath = parseWinPath(Folders.getWorkspaceFolder()?.fsPath);
|
||||
const files = Settings.get<DataFile[]>(SETTING_DATA_FILES);
|
||||
const folders = Settings.get<DataFolder[]>(SETTING_DATA_FOLDERS);
|
||||
@@ -220,6 +231,7 @@ export class DashboardSettings {
|
||||
}
|
||||
}
|
||||
|
||||
Logger.verbose('DashboardSettings:getDataFiles:end');
|
||||
return clonedFiles;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@ export class Extension {
|
||||
}
|
||||
);
|
||||
|
||||
await Settings.update(
|
||||
await Settings.safeUpdate(
|
||||
SETTING_TEMPLATES_ENABLED,
|
||||
answer?.toLocaleLowerCase() === l10n.t(LocalizationKey.commonYes),
|
||||
true
|
||||
@@ -266,7 +266,7 @@ export class Extension {
|
||||
|
||||
return f;
|
||||
});
|
||||
await Settings.update(SETTING_CONTENT_PAGE_FOLDERS, folders, true);
|
||||
await Settings.safeUpdate(SETTING_CONTENT_PAGE_FOLDERS, folders, true);
|
||||
}
|
||||
|
||||
// The tags and categories settings need to be moved to the database
|
||||
|
||||
@@ -244,7 +244,7 @@ export class FrameworkDetector {
|
||||
});
|
||||
}
|
||||
|
||||
await Settings.update(SETTING_CONTENT_STATIC_FOLDER, assetFoler, true);
|
||||
await Settings.safeUpdate(SETTING_CONTENT_STATIC_FOLDER, assetFoler, true);
|
||||
} catch (e) {
|
||||
Logger.error(
|
||||
`Something failed while processing your Hexo configuration. ${(e as Error).message}`
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { Extension } from './Extension';
|
||||
import { commands, OutputChannel, window } from 'vscode';
|
||||
import { format } from 'date-fns';
|
||||
import { COMMAND_NAME } from '../constants';
|
||||
import { COMMAND_NAME, SETTING_LOGGING } from '../constants';
|
||||
import { Settings } from '.';
|
||||
|
||||
export type LoggerLocation = 'VSCODE' | 'DASHBOARD' | 'PANEL' | 'SETTING';
|
||||
|
||||
export class Logger {
|
||||
private static instance: Logger;
|
||||
@@ -15,6 +18,10 @@ export class Logger {
|
||||
});
|
||||
}
|
||||
|
||||
public static getLevel(): string {
|
||||
return Settings.get(SETTING_LOGGING) || 'info';
|
||||
}
|
||||
|
||||
public static getInstance(): Logger {
|
||||
if (!Logger.instance) {
|
||||
Logger.instance = new Logger();
|
||||
@@ -22,19 +29,43 @@ export class Logger {
|
||||
return Logger.instance;
|
||||
}
|
||||
|
||||
public static info(message: string, type: 'INFO' | 'WARNING' | 'ERROR' = 'INFO'): void {
|
||||
public static info(
|
||||
message: string,
|
||||
location: LoggerLocation = 'VSCODE',
|
||||
type: 'VERBOSE' | 'INFO' | 'WARNING' | 'ERROR' = 'INFO'
|
||||
): void {
|
||||
if (!Logger.channel) {
|
||||
Logger.getInstance();
|
||||
}
|
||||
|
||||
Logger.channel?.appendLine(`["${type}" - ${format(new Date(), 'HH:MM:ss')}] ${message}`);
|
||||
const loggingLevel = Logger.getLevel();
|
||||
|
||||
const logMessage = `["${type}" - ${format(new Date(), 'HH:mm:ss')}] ${location} | ${message}`;
|
||||
|
||||
if (loggingLevel === 'verbose') {
|
||||
Logger.log(logMessage);
|
||||
} else if (loggingLevel === 'info' && type !== 'VERBOSE') {
|
||||
Logger.log(logMessage);
|
||||
} else if (loggingLevel === 'warning' && (type === 'WARNING' || type === 'ERROR')) {
|
||||
Logger.log(logMessage);
|
||||
} else if (loggingLevel === 'error' && type === 'ERROR') {
|
||||
Logger.log(logMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public static warning(message: string): void {
|
||||
Logger.info(message, 'WARNING');
|
||||
public static warning(message: string, location: LoggerLocation = 'VSCODE'): void {
|
||||
Logger.info(message, location, 'WARNING');
|
||||
}
|
||||
|
||||
public static error(message: string): void {
|
||||
Logger.info(message, 'ERROR');
|
||||
public static error(message: string, location: LoggerLocation = 'VSCODE'): void {
|
||||
Logger.info(message, location, 'ERROR');
|
||||
}
|
||||
|
||||
public static verbose(message: string, location: LoggerLocation = 'VSCODE'): void {
|
||||
Logger.info(message, location, 'VERBOSE');
|
||||
}
|
||||
|
||||
private static log(message: string) {
|
||||
Logger.channel?.appendLine(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ export class MediaHelpers {
|
||||
) {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
const staticFolder = Folders.getStaticFolderRelativePath();
|
||||
const contentFolders = Folders.get();
|
||||
const contentFolders = await Folders.get();
|
||||
const viewData = Dashboard.viewData;
|
||||
let selectedFolder = requestedFolder;
|
||||
|
||||
@@ -348,15 +348,7 @@ export class MediaHelpers {
|
||||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
public static async deleteFile({
|
||||
file,
|
||||
page,
|
||||
folder
|
||||
}: {
|
||||
file: string;
|
||||
page: number;
|
||||
folder: string | null;
|
||||
}) {
|
||||
public static async deleteFile(file: string) {
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
@@ -398,12 +390,14 @@ export class MediaHelpers {
|
||||
|
||||
const article = editor ? ArticleHelper.getFrontMatter(editor) : null;
|
||||
const articleCt =
|
||||
article && article.data ? ArticleHelper.getContentType(article) : DEFAULT_CONTENT_TYPE;
|
||||
article && article.data
|
||||
? await ArticleHelper.getContentType(article)
|
||||
: DEFAULT_CONTENT_TYPE;
|
||||
|
||||
const absImgPath = join(parseWinPath(wsFolder?.fsPath || ''), relPath);
|
||||
const fileDir = parseWinPath(dirname(filePath));
|
||||
const imgDir = parseWinPath(dirname(absImgPath));
|
||||
const contentFolders = Folders.get();
|
||||
const contentFolders = await Folders.get();
|
||||
|
||||
// Check if relative paths need to be created for the media files
|
||||
if (articleCt.pageBundle) {
|
||||
@@ -494,6 +488,18 @@ export class MediaHelpers {
|
||||
await mediaLib.updateFilename(file, filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the metadata from the source media to the target media.
|
||||
*
|
||||
* @param source - The path of the source media.
|
||||
* @param target - The path of the target media.
|
||||
*/
|
||||
public static async copyMetadata(source: string, target: string) {
|
||||
const mediaLib = MediaLibrary.getInstance();
|
||||
const metadata = await mediaLib.get(source);
|
||||
mediaLib.set(target, metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the media files
|
||||
*/
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Folders, WORKSPACE_PLACEHOLDER } from '../commands/Folders';
|
||||
import { Notifications } from './Notifications';
|
||||
import { parseWinPath } from './parseWinPath';
|
||||
import { LocalStore } from '../constants';
|
||||
import { existsAsync, renameAsync } from '../utils';
|
||||
import { existsAsync, isWindows, renameAsync } from '../utils';
|
||||
import { existsSync, mkdirSync, renameSync } from 'fs';
|
||||
import { lookup } from 'mime-types';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
@@ -176,9 +176,8 @@ export class MediaLibrary {
|
||||
|
||||
public parsePath(path: string) {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
const isWindows = process.platform === 'win32';
|
||||
let absPath = path.replace(parseWinPath(wsFolder?.fsPath || ''), WORKSPACE_PLACEHOLDER);
|
||||
absPath = isWindows ? absPath.split('\\').join('/') : absPath;
|
||||
absPath = isWindows() ? absPath.split('\\').join('/') : absPath;
|
||||
return absPath.toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ export class Notifications {
|
||||
* @returns
|
||||
*/
|
||||
public static info(message: string, ...items: any): Thenable<string | undefined> {
|
||||
Logger.info(`${EXTENSION_NAME}: ${message}`, 'INFO');
|
||||
Logger.info(`${EXTENSION_NAME}: ${message}`, 'VSCODE', 'INFO');
|
||||
|
||||
if (this.shouldShow('INFO')) {
|
||||
return window.showInformationMessage(`${EXTENSION_NAME}: ${message}`, ...items);
|
||||
@@ -34,7 +34,7 @@ export class Notifications {
|
||||
* @returns
|
||||
*/
|
||||
public static warning(message: string, ...items: any): Thenable<string | undefined> {
|
||||
Logger.info(`${EXTENSION_NAME}: ${message}`, 'WARNING');
|
||||
Logger.info(`${EXTENSION_NAME}: ${message}`, 'VSCODE', 'WARNING');
|
||||
|
||||
if (this.shouldShow('WARNING')) {
|
||||
return window.showWarningMessage(`${EXTENSION_NAME}: ${message}`, ...items);
|
||||
@@ -43,6 +43,30 @@ export class Notifications {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a warning notification to the user with a link to the output channel
|
||||
* @param message
|
||||
* @param items
|
||||
* @returns
|
||||
*/
|
||||
public static warningWithOutput(message: string, ...items: any): Thenable<string | undefined> {
|
||||
Logger.info(`${EXTENSION_NAME}: ${message}`, 'VSCODE', 'WARNING');
|
||||
|
||||
if (this.shouldShow('WARNING')) {
|
||||
return window.showWarningMessage(
|
||||
`${EXTENSION_NAME}: ${message} ${l10n.t(
|
||||
LocalizationKey.notificationsOutputChannelDescription,
|
||||
`[${l10n.t(LocalizationKey.notificationsOutputChannelLink)}](command:${
|
||||
COMMAND_NAME.showOutputChannel
|
||||
})`
|
||||
)}`,
|
||||
...items
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show an error notification to the user
|
||||
* @param message
|
||||
@@ -50,7 +74,7 @@ export class Notifications {
|
||||
* @returns
|
||||
*/
|
||||
public static error(message: string, ...items: any): Thenable<string | undefined> {
|
||||
Logger.info(`${EXTENSION_NAME}: ${message}`, 'ERROR');
|
||||
Logger.info(`${EXTENSION_NAME}: ${message}`, 'VSCODE', 'ERROR');
|
||||
|
||||
if (this.shouldShow('ERROR')) {
|
||||
return window.showErrorMessage(`${EXTENSION_NAME}: ${message}`, ...items);
|
||||
@@ -66,7 +90,7 @@ export class Notifications {
|
||||
* @returns
|
||||
*/
|
||||
public static errorWithOutput(message: string, ...items: any): Thenable<string | undefined> {
|
||||
Logger.info(`${EXTENSION_NAME}: ${message}`, 'ERROR');
|
||||
Logger.info(`${EXTENSION_NAME}: ${message}`, 'VSCODE', 'ERROR');
|
||||
|
||||
if (this.shouldShow('ERROR')) {
|
||||
return window.showErrorMessage(
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
SETTING_WEBSITE_URL
|
||||
} from './../constants/settings';
|
||||
import { workspace } from 'vscode';
|
||||
import { ContentType, Extension, Settings, TaxonomyHelper } from '.';
|
||||
import { ContentType, Extension, Logger, Settings, TaxonomyHelper } from '.';
|
||||
import { Dashboard } from '../commands/Dashboard';
|
||||
import { Preview } from '../commands/Preview';
|
||||
import { Project } from '../commands/Project';
|
||||
@@ -45,59 +45,68 @@ import {
|
||||
TaxonomyType
|
||||
} from '../models';
|
||||
import { Folders } from '../commands';
|
||||
import { Copilot } from '../services/Copilot';
|
||||
|
||||
export class PanelSettings {
|
||||
public static async get(): Promise<IPanelSettings> {
|
||||
return {
|
||||
aiEnabled: Settings.get<boolean>(SETTING_SPONSORS_AI_ENABLED) || false,
|
||||
git: await GitListener.getSettings(),
|
||||
seo: {
|
||||
title: (Settings.get(SETTING_SEO_TITLE_LENGTH) as number) || -1,
|
||||
slug: (Settings.get(SETTING_SEO_SLUG_LENGTH) as number) || -1,
|
||||
description: (Settings.get(SETTING_SEO_DESCRIPTION_LENGTH) as number) || -1,
|
||||
content: (Settings.get(SETTING_SEO_CONTENT_MIN_LENGTH) as number) || -1,
|
||||
titleField: (Settings.get(SETTING_SEO_TITLE_FIELD) as string) || DefaultFields.Title,
|
||||
descriptionField:
|
||||
(Settings.get(SETTING_SEO_DESCRIPTION_FIELD) as string) || DefaultFields.Description
|
||||
},
|
||||
slug: {
|
||||
prefix: Settings.get(SETTING_SLUG_PREFIX) || '',
|
||||
suffix: Settings.get(SETTING_SLUG_SUFFIX) || '',
|
||||
updateFileName: !!Settings.get<boolean>(SETTING_SLUG_UPDATE_FILE_NAME)
|
||||
},
|
||||
date: {
|
||||
format: Settings.get<string>(SETTING_DATE_FORMAT) || ''
|
||||
},
|
||||
tags: (await TaxonomyHelper.get(TaxonomyType.Tag)) || [],
|
||||
categories: (await TaxonomyHelper.get(TaxonomyType.Category)) || [],
|
||||
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) && !s.hidden
|
||||
),
|
||||
isInitialized: await Project.isInitialized(),
|
||||
modifiedDateUpdate: Settings.get(SETTING_AUTO_UPDATE_DATE) || false,
|
||||
writingSettingsEnabled: this.isWritingSettingsEnabled() || false,
|
||||
fmHighlighting: Settings.get(SETTING_CONTENT_FRONTMATTER_HIGHLIGHT),
|
||||
preview: Preview.getSettings(),
|
||||
commaSeparatedFields: Settings.get(SETTING_COMMA_SEPARATED_FIELDS) || [],
|
||||
contentTypes: ContentType.getAll() || [],
|
||||
dashboardViewData: Dashboard.viewData,
|
||||
draftField: Settings.get<DraftField>(SETTING_CONTENT_DRAFT_FIELD),
|
||||
isBacker: await Extension.getInstance().getState<boolean | undefined>(
|
||||
CONTEXT.backer,
|
||||
'global'
|
||||
),
|
||||
framework: Settings.get<string>(SETTING_FRAMEWORK_ID),
|
||||
commands: {
|
||||
start: Settings.get<string>(SETTING_FRAMEWORK_START)
|
||||
},
|
||||
dataTypes: Settings.get<DataType[]>(SETTING_DATA_TYPES),
|
||||
fieldGroups: Settings.get<FieldGroup[]>(SETTING_TAXONOMY_FIELD_GROUPS),
|
||||
contentFolders: Folders.get(),
|
||||
websiteUrl: Settings.get<string>(SETTING_WEBSITE_URL) || '',
|
||||
disabledActions: Settings.get<PanelAction[]>(SETTING_PANEL_ACTIONS_DISABLED) || []
|
||||
};
|
||||
Logger.info('Getting panel settings');
|
||||
|
||||
try {
|
||||
return {
|
||||
aiEnabled: Settings.get<boolean>(SETTING_SPONSORS_AI_ENABLED) || false,
|
||||
copilotEnabled: await Copilot.isInstalled(),
|
||||
git: await GitListener.getSettings(),
|
||||
seo: {
|
||||
title: (Settings.get(SETTING_SEO_TITLE_LENGTH) as number) || -1,
|
||||
slug: (Settings.get(SETTING_SEO_SLUG_LENGTH) as number) || -1,
|
||||
description: (Settings.get(SETTING_SEO_DESCRIPTION_LENGTH) as number) || -1,
|
||||
content: (Settings.get(SETTING_SEO_CONTENT_MIN_LENGTH) as number) || -1,
|
||||
titleField: (Settings.get(SETTING_SEO_TITLE_FIELD) as string) || DefaultFields.Title,
|
||||
descriptionField:
|
||||
(Settings.get(SETTING_SEO_DESCRIPTION_FIELD) as string) || DefaultFields.Description
|
||||
},
|
||||
slug: {
|
||||
prefix: Settings.get(SETTING_SLUG_PREFIX) || '',
|
||||
suffix: Settings.get(SETTING_SLUG_SUFFIX) || '',
|
||||
updateFileName: !!Settings.get<boolean>(SETTING_SLUG_UPDATE_FILE_NAME)
|
||||
},
|
||||
date: {
|
||||
format: Settings.get<string>(SETTING_DATE_FORMAT) || ''
|
||||
},
|
||||
tags: (await TaxonomyHelper.get(TaxonomyType.Tag)) || [],
|
||||
categories: (await TaxonomyHelper.get(TaxonomyType.Category)) || [],
|
||||
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) && !s.hidden
|
||||
),
|
||||
isInitialized: await Project.isInitialized(),
|
||||
modifiedDateUpdate: Settings.get(SETTING_AUTO_UPDATE_DATE) || false,
|
||||
writingSettingsEnabled: this.isWritingSettingsEnabled() || false,
|
||||
fmHighlighting: Settings.get(SETTING_CONTENT_FRONTMATTER_HIGHLIGHT),
|
||||
preview: Preview.getSettings(),
|
||||
commaSeparatedFields: Settings.get(SETTING_COMMA_SEPARATED_FIELDS) || [],
|
||||
contentTypes: ContentType.getAll() || [],
|
||||
dashboardViewData: Dashboard.viewData,
|
||||
draftField: Settings.get<DraftField>(SETTING_CONTENT_DRAFT_FIELD),
|
||||
isBacker: await Extension.getInstance().getState<boolean | undefined>(
|
||||
CONTEXT.backer,
|
||||
'global'
|
||||
),
|
||||
framework: Settings.get<string>(SETTING_FRAMEWORK_ID),
|
||||
commands: {
|
||||
start: Settings.get<string>(SETTING_FRAMEWORK_START)
|
||||
},
|
||||
dataTypes: Settings.get<DataType[]>(SETTING_DATA_TYPES),
|
||||
fieldGroups: Settings.get<FieldGroup[]>(SETTING_TAXONOMY_FIELD_GROUPS),
|
||||
contentFolders: await Folders.get(),
|
||||
websiteUrl: Settings.get<string>(SETTING_WEBSITE_URL) || '',
|
||||
disabledActions: Settings.get<PanelAction[]>(SETTING_PANEL_ACTIONS_DISABLED) || []
|
||||
};
|
||||
} catch (error) {
|
||||
Logger.error(`Error getting panel settings: ${(error as Error).message}`);
|
||||
return {} as IPanelSettings;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,6 +9,7 @@ import { SponsorAi } from '../services/SponsorAI';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../localization';
|
||||
import { ContentFolder } from '../models';
|
||||
import { Copilot } from '../services/Copilot';
|
||||
|
||||
interface FolderQuickPickItem extends QuickPickItem {
|
||||
path: string;
|
||||
@@ -41,11 +42,12 @@ export class Questions {
|
||||
public static async ContentTitle(showWarning: boolean = true): Promise<string | undefined> {
|
||||
const aiEnabled = Settings.get<boolean>(SETTING_SPONSORS_AI_ENABLED);
|
||||
let title: string | undefined = '';
|
||||
const isCopilotInstalled = await Copilot.isInstalled();
|
||||
|
||||
if (aiEnabled) {
|
||||
const githubAuth = await authentication.getSession('github', ['read:user'], { silent: true });
|
||||
let aiTitles: string[] | undefined;
|
||||
|
||||
if (githubAuth && githubAuth.account.label) {
|
||||
if (aiEnabled || isCopilotInstalled) {
|
||||
if (isCopilotInstalled) {
|
||||
title = await window.showInputBox({
|
||||
title: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputTitle),
|
||||
prompt: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputPrompt),
|
||||
@@ -55,54 +57,81 @@ export class Questions {
|
||||
|
||||
if (title) {
|
||||
try {
|
||||
const aiTitles = await SponsorAi.getTitles(githubAuth.accessToken, title);
|
||||
|
||||
if (aiTitles && aiTitles.length > 0) {
|
||||
const options: QuickPickItem[] = [
|
||||
{
|
||||
label: `✏️ ${l10n.t(
|
||||
LocalizationKey.helpersQuestionsContentTitleAiInputQuickPickTitleSeparator
|
||||
)}`,
|
||||
kind: QuickPickItemKind.Separator
|
||||
},
|
||||
{
|
||||
label: title
|
||||
},
|
||||
{
|
||||
label: `🤖 ${l10n.t(
|
||||
LocalizationKey.helpersQuestionsContentTitleAiInputQuickPickAiSeparator
|
||||
)}`,
|
||||
kind: QuickPickItemKind.Separator
|
||||
},
|
||||
...aiTitles.map((d: string) => ({
|
||||
label: d
|
||||
}))
|
||||
];
|
||||
|
||||
const selectedTitle = await window.showQuickPick(options, {
|
||||
title: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputSelectTitle),
|
||||
placeHolder: l10n.t(
|
||||
LocalizationKey.helpersQuestionsContentTitleAiInputSelectPlaceholder
|
||||
),
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
|
||||
if (selectedTitle) {
|
||||
title = selectedTitle.label;
|
||||
} else if (!selectedTitle) {
|
||||
// Reset the title, so the user can enter their own title
|
||||
title = undefined;
|
||||
}
|
||||
}
|
||||
aiTitles = await Copilot.suggestTitles(title);
|
||||
} catch (e) {
|
||||
Logger.error((e as Error).message);
|
||||
Notifications.error(l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputFailed));
|
||||
Notifications.error(
|
||||
l10n.t(LocalizationKey.helpersQuestionsContentTitleCopilotInputFailed)
|
||||
);
|
||||
title = undefined;
|
||||
}
|
||||
} else if (!title && showWarning) {
|
||||
Notifications.warning(l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputWarning));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const githubAuth = await authentication.getSession('github', ['read:user'], {
|
||||
silent: true
|
||||
});
|
||||
|
||||
if (githubAuth && githubAuth.account.label) {
|
||||
title = await window.showInputBox({
|
||||
title: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputTitle),
|
||||
prompt: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputPrompt),
|
||||
placeHolder: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputPlaceholder),
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
|
||||
if (title) {
|
||||
try {
|
||||
aiTitles = await SponsorAi.getTitles(githubAuth.accessToken, title);
|
||||
} catch (e) {
|
||||
Logger.error((e as Error).message);
|
||||
Notifications.error(
|
||||
l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputFailed)
|
||||
);
|
||||
title = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (title && aiTitles && aiTitles.length > 0) {
|
||||
const options: QuickPickItem[] = [
|
||||
{
|
||||
label: `✏️ ${l10n.t(
|
||||
LocalizationKey.helpersQuestionsContentTitleAiInputQuickPickTitleSeparator
|
||||
)}`,
|
||||
kind: QuickPickItemKind.Separator
|
||||
},
|
||||
{
|
||||
label: title
|
||||
},
|
||||
{
|
||||
label: `🤖 ${l10n.t(
|
||||
isCopilotInstalled
|
||||
? LocalizationKey.helpersQuestionsContentTitleAiInputQuickPickCopilotSeparator
|
||||
: LocalizationKey.helpersQuestionsContentTitleAiInputQuickPickAiSeparator
|
||||
)}`,
|
||||
kind: QuickPickItemKind.Separator
|
||||
},
|
||||
...aiTitles.map((d: string) => ({
|
||||
label: d
|
||||
}))
|
||||
];
|
||||
|
||||
const selectedTitle = await window.showQuickPick(options, {
|
||||
title: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputSelectTitle),
|
||||
placeHolder: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputSelectPlaceholder),
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
|
||||
if (selectedTitle) {
|
||||
title = selectedTitle.label;
|
||||
} else if (!selectedTitle) {
|
||||
// Reset the title, so the user can enter their own title
|
||||
title = undefined;
|
||||
}
|
||||
} else if (!title && showWarning) {
|
||||
Notifications.warning(l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputWarning));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +160,8 @@ export class Questions {
|
||||
public static async SelectContentFolder(
|
||||
showWarning: boolean = true
|
||||
): Promise<FolderQuickPickItem | undefined> {
|
||||
let folders = Folders.get().filter((f) => !f.disableCreation);
|
||||
let folders = await Folders.get();
|
||||
folders = folders.filter((f) => !f.disableCreation);
|
||||
|
||||
let selectedFolder: FolderQuickPickItem | undefined;
|
||||
if (folders.length > 1) {
|
||||
@@ -149,16 +179,13 @@ export class Questions {
|
||||
} as FolderQuickPickItem;
|
||||
});
|
||||
|
||||
selectedFolder = await window.showQuickPick(
|
||||
folderOptions,
|
||||
{
|
||||
title: l10n.t(LocalizationKey.helpersQuestionsSelectContentFolderQuickPickTitle),
|
||||
placeHolder: l10n.t(
|
||||
LocalizationKey.helpersQuestionsSelectContentFolderQuickPickPlaceholder
|
||||
),
|
||||
ignoreFocusOut: true
|
||||
}
|
||||
);
|
||||
selectedFolder = await window.showQuickPick(folderOptions, {
|
||||
title: l10n.t(LocalizationKey.helpersQuestionsSelectContentFolderQuickPickTitle),
|
||||
placeHolder: l10n.t(
|
||||
LocalizationKey.helpersQuestionsSelectContentFolderQuickPickPlaceholder
|
||||
),
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
} else if (folders.length === 1) {
|
||||
selectedFolder = {
|
||||
label: folders[0].title,
|
||||
@@ -213,7 +240,9 @@ export class Questions {
|
||||
}));
|
||||
|
||||
if (options.length === 0) {
|
||||
Notifications.error(LocalizationKey.helpersQuestionsSelectContentTypeQuickPickErrorNoContentTypes);
|
||||
Notifications.error(
|
||||
LocalizationKey.helpersQuestionsSelectContentTypeQuickPickErrorNoContentTypes
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,8 @@ import {
|
||||
SETTING_PROJECTS,
|
||||
SETTING_TAXONOMY_TAGS,
|
||||
SETTING_TAXONOMY_CATEGORIES,
|
||||
SETTING_CONTENT_FILTERS
|
||||
SETTING_CONTENT_FILTERS,
|
||||
SETTING_CONTENT_I18N
|
||||
} from '../constants';
|
||||
import { Folders } from '../commands/Folders';
|
||||
import { join, basename, dirname, parse } from 'path';
|
||||
@@ -77,6 +78,15 @@ export class Settings {
|
||||
private static readConfigPromise: Promise<void> | undefined = undefined;
|
||||
private static project: Project | undefined = undefined;
|
||||
private static configDebouncer = debounceCallback();
|
||||
private static hasExtendedConfig: boolean = false;
|
||||
private static extendedConfig: { extended: any[]; splitted: any[]; dynamic: boolean } = {} as any;
|
||||
|
||||
public static async registerCommands() {
|
||||
const ext = Extension.getInstance();
|
||||
const subscriptions = ext.subscriptions;
|
||||
|
||||
subscriptions.push(commands.registerCommand(COMMAND_NAME.promote, Settings.promote));
|
||||
}
|
||||
|
||||
public static async init() {
|
||||
const allCommands = await commands.getCommands(true);
|
||||
@@ -250,6 +260,7 @@ export class Settings {
|
||||
*/
|
||||
public static async onConfigChange() {
|
||||
const projectConfig = await Settings.projectConfigPath();
|
||||
Logger.info(`Project config path: ${projectConfig}`);
|
||||
|
||||
workspace.onDidChangeConfiguration(() => {
|
||||
Settings.triggerListeners();
|
||||
@@ -366,12 +377,54 @@ export class Settings {
|
||||
return setting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely updates a setting value.
|
||||
*
|
||||
* @param name - The name of the setting.
|
||||
* @param value - The new value for the setting.
|
||||
* @param updateGlobal - Indicates whether to update the global setting or not. Default is `false`.
|
||||
* @returns A promise that resolves when the setting is updated.
|
||||
*/
|
||||
public static async safeUpdate<T>(
|
||||
name: string,
|
||||
value: T,
|
||||
updateGlobal: boolean = false
|
||||
): Promise<void> {
|
||||
if (Settings.hasExtendedConfig) {
|
||||
const configKey = `${CONFIG_KEY}.${name}`;
|
||||
|
||||
if (
|
||||
Settings.extendedConfig.extended.includes(configKey) ||
|
||||
Settings.extendedConfig.splitted.includes(configKey) ||
|
||||
Settings.extendedConfig.dynamic
|
||||
) {
|
||||
Notifications.warningWithOutput(
|
||||
l10n.t(LocalizationKey.helpersSettingsHelperSafeUpdateWarning, configKey)
|
||||
);
|
||||
|
||||
Logger.info(`Updating setting: ${configKey}`, 'SETTING');
|
||||
Logger.info(
|
||||
`New value:
|
||||
${JSON.stringify(value, null, 2)}`,
|
||||
'SETTING'
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await Settings.update<T>(name, value, updateGlobal);
|
||||
}
|
||||
|
||||
/**
|
||||
* String update config setting
|
||||
* @param name
|
||||
* @param value
|
||||
*/
|
||||
public static async update<T>(name: string, value: T, updateGlobal: boolean = false) {
|
||||
public static async update<T>(
|
||||
name: string,
|
||||
value: T,
|
||||
updateGlobal: boolean = false
|
||||
): Promise<void> {
|
||||
const fmConfig = await Settings.projectConfigPath();
|
||||
|
||||
if (updateGlobal) {
|
||||
@@ -415,7 +468,7 @@ export class Settings {
|
||||
|
||||
const workspaceSettingValue = Settings.hasWorkspaceSettings<ContentType[]>(name);
|
||||
if (workspaceSettingValue) {
|
||||
await Settings.update(name, undefined);
|
||||
await Settings.safeUpdate(name, undefined);
|
||||
}
|
||||
|
||||
// Make sure to reload the whole config + all the data files
|
||||
@@ -507,7 +560,7 @@ export class Settings {
|
||||
customTaxonomies[taxIdx].options.push(option);
|
||||
customTaxonomies[taxIdx].options = [...new Set(customTaxonomies[taxIdx].options)];
|
||||
customTaxonomies[taxIdx].options = customTaxonomies[taxIdx].options.sort().filter((o) => !!o);
|
||||
await Settings.update(SETTING_TAXONOMY_CUSTOM, customTaxonomies, true);
|
||||
await Settings.safeUpdate(SETTING_TAXONOMY_CUSTOM, customTaxonomies, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -524,7 +577,7 @@ export class Settings {
|
||||
customTaxonomies[taxIdx].options = options;
|
||||
}
|
||||
|
||||
await Settings.update(SETTING_TAXONOMY_CUSTOM, customTaxonomies, true);
|
||||
await Settings.safeUpdate(SETTING_TAXONOMY_CUSTOM, customTaxonomies, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -540,8 +593,8 @@ export class Settings {
|
||||
const setting = Settings.config.inspect(settingName);
|
||||
|
||||
if (setting && typeof setting.workspaceValue !== 'undefined') {
|
||||
await Settings.update(settingName, setting.workspaceValue, true);
|
||||
await Settings.update(settingName, undefined);
|
||||
await Settings.safeUpdate(settingName, setting.workspaceValue, true);
|
||||
await Settings.safeUpdate(settingName, undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -644,6 +697,13 @@ export class Settings {
|
||||
*/
|
||||
private static async readConfig() {
|
||||
try {
|
||||
Settings.hasExtendedConfig = false;
|
||||
Settings.extendedConfig = {
|
||||
extended: [],
|
||||
splitted: [],
|
||||
dynamic: false
|
||||
};
|
||||
|
||||
const fmConfig = await Settings.projectConfigPath();
|
||||
if (fmConfig && (await existsAsync(fmConfig))) {
|
||||
const localConfig = await readFileAsync(fmConfig, 'utf8');
|
||||
@@ -663,6 +723,8 @@ export class Settings {
|
||||
);
|
||||
if (configFiles.length === 0) {
|
||||
Logger.info(`No ".frontmatter/config" config files found.`);
|
||||
} else {
|
||||
Settings.hasExtendedConfig = true;
|
||||
}
|
||||
|
||||
// Sort the files by fsPath
|
||||
@@ -699,6 +761,8 @@ export class Settings {
|
||||
);
|
||||
|
||||
if (dynamicConfig) {
|
||||
Settings.hasExtendedConfig = true;
|
||||
Settings.extendedConfig.dynamic = true;
|
||||
Settings.globalConfig = dynamicConfig;
|
||||
Logger.info(`Dynamic config file loaded`);
|
||||
}
|
||||
@@ -730,11 +794,17 @@ export class Settings {
|
||||
return;
|
||||
}
|
||||
|
||||
Settings.hasExtendedConfig = true;
|
||||
const originalConfig = Object.assign({}, Settings.globalConfig);
|
||||
const extendsConfig: string[] = Settings.globalConfig[extendsConfigName];
|
||||
for (const externalConfig of extendsConfig) {
|
||||
if (externalConfig.endsWith(`.json`)) {
|
||||
const config = await Settings.getExternalConfig(externalConfig);
|
||||
|
||||
// Store the config keys
|
||||
const keys = Object.keys(config);
|
||||
Settings.extendedConfig.extended.push(...keys);
|
||||
|
||||
await Settings.extendConfig(config, originalConfig);
|
||||
}
|
||||
}
|
||||
@@ -769,6 +839,7 @@ export class Settings {
|
||||
Settings.globalConfig = {};
|
||||
}
|
||||
|
||||
Settings.extendedConfig.splitted.push(`${CONFIG_KEY}.${relSettingName}`);
|
||||
Settings.updateGlobalConfigSetting(relSettingName, configJson, configFilePath, filePath);
|
||||
} catch (e) {
|
||||
Logger.error(`Error reading config file: ${configFile.fsPath}`);
|
||||
@@ -922,6 +993,10 @@ export class Settings {
|
||||
else if (Settings.isEqualOrStartsWith(relSettingName, SETTING_PROJECTS)) {
|
||||
Settings.updateGlobalConfigArraySetting(SETTING_PROJECTS, 'name', configJson);
|
||||
}
|
||||
// i18n
|
||||
else if (Settings.isEqualOrStartsWith(relSettingName, SETTING_CONTENT_I18N)) {
|
||||
Settings.updateGlobalConfigArraySetting(SETTING_CONTENT_I18N, 'locale', configJson);
|
||||
}
|
||||
// Snippets
|
||||
else if (
|
||||
Settings.isEqualOrStartsWith(relSettingName, SETTING_CONTENT_SNIPPETS) &&
|
||||
@@ -1138,6 +1213,9 @@ export class Settings {
|
||||
*/
|
||||
private static async reloadConfig(debounced: boolean = true) {
|
||||
Logger.info(`Reloading config...`);
|
||||
// Clear the folder cache as we need to see the latest folders
|
||||
Folders.clearCached();
|
||||
|
||||
if (Settings.readConfigPromise === undefined) {
|
||||
Settings.readConfigPromise = Settings.readConfig();
|
||||
}
|
||||
|
||||
@@ -74,7 +74,9 @@ export class TaxonomyHelper {
|
||||
* @param options
|
||||
*/
|
||||
public static async get(type: TaxonomyType): Promise<string[] | undefined> {
|
||||
Logger.verbose(`TaxonomyHelper:get:start:${type}`);
|
||||
if (!TaxonomyHelper.db) {
|
||||
Logger.info(`TaxonomyHelper:get:db-not-initialized`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -84,6 +86,7 @@ export class TaxonomyHelper {
|
||||
if (await TaxonomyHelper.db.exists(tagType)) {
|
||||
taxonomy = await TaxonomyHelper.db.getObject<string[]>(tagType);
|
||||
}
|
||||
Logger.verbose(`TaxonomyHelper:get:end:${type}`);
|
||||
return taxonomy;
|
||||
}
|
||||
|
||||
@@ -344,7 +347,7 @@ export class TaxonomyHelper {
|
||||
if (mdFile) {
|
||||
try {
|
||||
const article = FrontMatterParser.fromFile(mdFile);
|
||||
const contentType = ArticleHelper.getContentType(article);
|
||||
const contentType = await ArticleHelper.getContentType(article);
|
||||
|
||||
let fieldNames: string[] = this.getFieldsHierarchy(taxonomyType, contentType);
|
||||
|
||||
@@ -479,7 +482,7 @@ export class TaxonomyHelper {
|
||||
if (mdFile) {
|
||||
try {
|
||||
const article = FrontMatterParser.fromFile(mdFile);
|
||||
const contentType = ArticleHelper.getContentType(article);
|
||||
const contentType = await ArticleHelper.getContentType(article);
|
||||
|
||||
let oldFieldNames: string[] = this.getFieldsHierarchy(oldType, contentType);
|
||||
let newFieldNames: string[] = this.getFieldsHierarchy(newType, contentType, true);
|
||||
|
||||
@@ -32,6 +32,8 @@ export * from './isValidFile';
|
||||
export * from './openFileInEditor';
|
||||
export * from './parseWinPath';
|
||||
export * from './processArticlePlaceholders';
|
||||
export * from './processDateTimePlaceholders';
|
||||
export * from './processFmPlaceholders';
|
||||
export * from './processI18nPlaceholders';
|
||||
export * from './processPathPlaceholders';
|
||||
export * from './processTimePlaceholders';
|
||||
|
||||
@@ -38,7 +38,7 @@ export const processArticlePlaceholdersFromPath = async (
|
||||
}
|
||||
|
||||
if (value.includes('{{slug}}') && filePath) {
|
||||
const contentType = article ? ArticleHelper.getContentType(article) : undefined;
|
||||
const contentType = article ? await ArticleHelper.getContentType(article) : undefined;
|
||||
if (contentType) {
|
||||
const regex = new RegExp('{{slug}}', 'g');
|
||||
value = value.replace(
|
||||
|
||||
43
src/helpers/processDateTimePlaceholders.ts
Normal file
43
src/helpers/processDateTimePlaceholders.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { format } from 'date-fns';
|
||||
import { DateHelper } from './DateHelper';
|
||||
|
||||
/**
|
||||
* Replace the datetime placeholders
|
||||
* @param value
|
||||
* @param dateFormat
|
||||
* @param articleDate
|
||||
* @returns
|
||||
*/
|
||||
export const processDateTimePlaceholders = (
|
||||
value: string,
|
||||
dateFormat?: string,
|
||||
articleDate?: Date
|
||||
) => {
|
||||
if (value && typeof value === 'string') {
|
||||
if (value.includes(`{{date|`)) {
|
||||
const regex = /{{date\|[^}]*}}/g;
|
||||
const matches = value.match(regex);
|
||||
if (matches) {
|
||||
for (const match of matches) {
|
||||
const placeholderParts = match.split('|');
|
||||
if (placeholderParts.length > 1) {
|
||||
let dateFormat = placeholderParts[1].trim().replace('}}', '');
|
||||
|
||||
if (dateFormat) {
|
||||
if (dateFormat && typeof dateFormat === 'string') {
|
||||
value = value.replace(
|
||||
match,
|
||||
format(articleDate || new Date(), DateHelper.formatUpdate(dateFormat) as string)
|
||||
);
|
||||
} else {
|
||||
value = value.replace(match, (articleDate || new Date()).toISOString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
39
src/helpers/processI18nPlaceholders.ts
Normal file
39
src/helpers/processI18nPlaceholders.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { ContentFolder } from '../models';
|
||||
|
||||
export const processI18nPlaceholders = (
|
||||
value: string,
|
||||
contentFolder: ContentFolder | null | undefined
|
||||
) => {
|
||||
// Example: {{locale}}
|
||||
if (value && contentFolder?.locale) {
|
||||
if (value.includes('{{locale}}')) {
|
||||
const regex = new RegExp('{{locale}}', 'g');
|
||||
value = value.replace(regex, contentFolder.locale);
|
||||
}
|
||||
// Example: {{locale | ignore:en}}
|
||||
else if (value.includes('{{locale')) {
|
||||
const regex = /{{locale[^}]*}}/g;
|
||||
const matches = value.match(regex);
|
||||
if (matches) {
|
||||
for (const match of matches) {
|
||||
const placeholderParts = match.split('|');
|
||||
if (placeholderParts.length > 1) {
|
||||
let ignore = placeholderParts[1].trim().replace('}}', '');
|
||||
|
||||
if (ignore.startsWith('ignore:')) {
|
||||
ignore = ignore.replace('ignore:', '');
|
||||
|
||||
if (ignore.includes(contentFolder.locale)) {
|
||||
value = value.replace(match, '');
|
||||
} else {
|
||||
value = value.replace(match, contentFolder.locale);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
@@ -4,10 +4,10 @@ import { DateHelper } from './DateHelper';
|
||||
/**
|
||||
* Replace the time placeholders
|
||||
* @param value
|
||||
* @param title
|
||||
* @param dateFormat
|
||||
* @returns
|
||||
*/
|
||||
export const processTimePlaceholders = (value: string, dateFormat?: string) => {
|
||||
export const processTimePlaceholders = (value: string, dateFormat?: string, articleDate?: Date) => {
|
||||
if (value && typeof value === 'string') {
|
||||
if (value.includes('{{now}}')) {
|
||||
const regex = new RegExp('{{now}}', 'g');
|
||||
@@ -15,46 +15,46 @@ export const processTimePlaceholders = (value: string, dateFormat?: string) => {
|
||||
if (dateFormat && typeof dateFormat === 'string') {
|
||||
value = value.replace(
|
||||
regex,
|
||||
format(new Date(), DateHelper.formatUpdate(dateFormat) as string)
|
||||
format(articleDate || new Date(), DateHelper.formatUpdate(dateFormat) as string)
|
||||
);
|
||||
} else {
|
||||
value = value.replace(regex, new Date().toISOString());
|
||||
value = value.replace(regex, (articleDate || new Date()).toISOString());
|
||||
}
|
||||
}
|
||||
|
||||
if (value.includes('{{year}}')) {
|
||||
const regex = new RegExp('{{year}}', 'g');
|
||||
value = value.replace(regex, format(new Date(), 'yyyy'));
|
||||
value = value.replace(regex, format(articleDate || new Date(), 'yyyy'));
|
||||
}
|
||||
|
||||
if (value.includes('{{month}}')) {
|
||||
const regex = new RegExp('{{month}}', 'g');
|
||||
value = value.replace(regex, format(new Date(), 'MM'));
|
||||
value = value.replace(regex, format(articleDate || new Date(), 'MM'));
|
||||
}
|
||||
|
||||
if (value.includes('{{day}}')) {
|
||||
const regex = new RegExp('{{day}}', 'g');
|
||||
value = value.replace(regex, format(new Date(), 'dd'));
|
||||
value = value.replace(regex, format(articleDate || new Date(), 'dd'));
|
||||
}
|
||||
|
||||
if (value.includes('{{hour12}}')) {
|
||||
const regex = new RegExp('{{hour12}}', 'g');
|
||||
value = value.replace(regex, format(new Date(), 'hh'));
|
||||
value = value.replace(regex, format(articleDate || new Date(), 'hh'));
|
||||
}
|
||||
|
||||
if (value.includes('{{hour24}}')) {
|
||||
const regex = new RegExp('{{hour24}}', 'g');
|
||||
value = value.replace(regex, format(new Date(), 'HH'));
|
||||
value = value.replace(regex, format(articleDate || new Date(), 'HH'));
|
||||
}
|
||||
|
||||
if (value.includes('{{ampm}}')) {
|
||||
const regex = new RegExp('{{ampm}}', 'g');
|
||||
value = value.replace(regex, format(new Date(), 'aaa'));
|
||||
value = value.replace(regex, format(articleDate || new Date(), 'aaa'));
|
||||
}
|
||||
|
||||
if (value.includes('{{minute}}')) {
|
||||
const regex = new RegExp('{{minute}}', 'g');
|
||||
value = value.replace(regex, format(new Date(), 'mm'));
|
||||
value = value.replace(regex, format(articleDate || new Date(), 'mm'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,12 @@ export abstract class BaseListener {
|
||||
* @param data
|
||||
*/
|
||||
public static sendMsg(command: DashboardCommand, payload: any) {
|
||||
Logger.info(`Sending message to dashboard: ${command}`);
|
||||
if (command === DashboardCommand.loading) {
|
||||
const loadingType = payload ? `- ${JSON.stringify(payload)}` : '- Turn off';
|
||||
Logger.verbose(`Sending message to dashboard: ${command} ${loadingType}`);
|
||||
} else {
|
||||
Logger.verbose(`Sending message to dashboard: ${command}`);
|
||||
}
|
||||
|
||||
Dashboard.postWebviewMessage({
|
||||
command,
|
||||
|
||||
@@ -20,6 +20,9 @@ export class DashboardListener extends BaseListener {
|
||||
super.process(msg);
|
||||
|
||||
switch (msg.command) {
|
||||
case DashboardMessage.setTitle:
|
||||
Dashboard.setTitle(msg.payload);
|
||||
break;
|
||||
case DashboardMessage.getViewType:
|
||||
if (Dashboard.viewData) {
|
||||
this.sendMsg(DashboardCommand.viewData, Dashboard.viewData);
|
||||
|
||||
@@ -34,7 +34,7 @@ export class LocalizationListener extends BaseListener {
|
||||
return;
|
||||
}
|
||||
|
||||
const config = i18n.getAll();
|
||||
const config = await i18n.getAll();
|
||||
this.sendRequest(command as any, requestId, config);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ export class MediaListener extends BaseListener {
|
||||
*/
|
||||
private static delete(data: { file: string; page: number; folder: string | null }) {
|
||||
try {
|
||||
MediaHelpers.deleteFile(data);
|
||||
MediaHelpers.deleteFile(data.file);
|
||||
this.sendMediaFiles(data.page || 0, data.folder || '');
|
||||
} catch {}
|
||||
}
|
||||
|
||||
@@ -69,9 +69,9 @@ export class PagesListener extends BaseListener {
|
||||
if (ArticleHelper.isSupportedFile(doc)) {
|
||||
Logger.info(`File saved ${doc.uri.fsPath}`);
|
||||
// Optimize the list of recently changed files
|
||||
DataListener.getFoldersAndFiles();
|
||||
DataListener.getFoldersAndFiles(doc.uri);
|
||||
// Trigger the metadata update
|
||||
this.watcherExec(doc.uri);
|
||||
this.watcherExec(doc.uri, 'save');
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -80,7 +80,7 @@ export class PagesListener extends BaseListener {
|
||||
* Start watching the folders in the current workspace for content changes
|
||||
*/
|
||||
public static async startWatchers() {
|
||||
const folders = Folders.get();
|
||||
const folders = await Folders.get();
|
||||
|
||||
if (!folders || folders.length === 0) {
|
||||
return;
|
||||
@@ -103,9 +103,9 @@ export class PagesListener extends BaseListener {
|
||||
false,
|
||||
false
|
||||
);
|
||||
watcher.onDidCreate(async (uri: Uri) => this.watcherExec(uri));
|
||||
watcher.onDidChange(async (uri: Uri) => this.watcherExec(uri));
|
||||
watcher.onDidDelete(async (uri: Uri) => this.watcherExec(uri));
|
||||
watcher.onDidCreate(async (uri: Uri) => this.watcherExec(uri, 'create'));
|
||||
watcher.onDidChange(async (uri: Uri) => this.watcherExec(uri, 'change'));
|
||||
watcher.onDidDelete(async (uri: Uri) => this.watcherExec(uri, 'delete'));
|
||||
this.watchers[folderUri.fsPath] = watcher;
|
||||
}
|
||||
}
|
||||
@@ -124,7 +124,7 @@ export class PagesListener extends BaseListener {
|
||||
if (!article) {
|
||||
return;
|
||||
}
|
||||
const contentType = ArticleHelper.getContentType(article);
|
||||
const contentType = await ArticleHelper.getContentType(article);
|
||||
|
||||
Logger.info(`Deleting file: ${path}`);
|
||||
|
||||
@@ -162,10 +162,10 @@ export class PagesListener extends BaseListener {
|
||||
* Watcher for processing page updates
|
||||
* @param file
|
||||
*/
|
||||
private static async watcherExec(file: Uri) {
|
||||
private static async watcherExec(file: Uri, type?: 'create' | 'change' | 'delete' | 'save') {
|
||||
const progressFile = async (file: Uri) => {
|
||||
const ext = Extension.getInstance();
|
||||
Logger.info(`File watcher execution for: ${file.fsPath}`);
|
||||
Logger.info(`File watcher execution for (${type}): ${file.fsPath}`);
|
||||
|
||||
const pageIdx = this.lastPages.findIndex((p) => p.fmFilePath === file.fsPath);
|
||||
if (pageIdx !== -1) {
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from '../../constants';
|
||||
import { DashboardCommand } from '../../dashboardWebView/DashboardCommand';
|
||||
import { DashboardMessage } from '../../dashboardWebView/DashboardMessage';
|
||||
import { DashboardSettings, Extension, Notifications, Settings } from '../../helpers';
|
||||
import { DashboardSettings, Extension, Logger, Notifications, Settings } from '../../helpers';
|
||||
import { FrameworkDetector } from '../../helpers/FrameworkDetector';
|
||||
import { Framework, Template, PostMessageData, StaticFolder, LoadingType } from '../../models';
|
||||
import { BaseListener } from './BaseListener';
|
||||
@@ -101,7 +101,7 @@ export class SettingsListener extends BaseListener {
|
||||
if (typeof setting.name !== 'undefined' && typeof setting.value !== 'undefined') {
|
||||
const value = Settings.get(setting.name);
|
||||
if (value !== setting.value) {
|
||||
await Settings.update(setting.name, setting.value, true);
|
||||
await Settings.safeUpdate(setting.name, setting.value, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,7 +190,7 @@ export class SettingsListener extends BaseListener {
|
||||
*/
|
||||
private static async update(data: { name: string; value: any; global?: boolean }) {
|
||||
if (data.name) {
|
||||
await Settings.update(data.name, data.value, data.global);
|
||||
await Settings.safeUpdate(data.name, data.value, data.global);
|
||||
this.getSettings(true);
|
||||
}
|
||||
}
|
||||
@@ -199,7 +199,11 @@ export class SettingsListener extends BaseListener {
|
||||
* Retrieve the settings for the dashboard
|
||||
*/
|
||||
public static async getSettings(clear: boolean = false) {
|
||||
Logger.verbose(`SettingsListener:getSettings:start - clear: ${clear}`);
|
||||
const settings = await DashboardSettings.get(clear);
|
||||
Logger.verbose(
|
||||
`SettingsListener:getSettings:end - setting keys: ${Object.keys(settings).length}`
|
||||
);
|
||||
|
||||
this.sendMsg(DashboardCommand.settings, settings);
|
||||
}
|
||||
@@ -209,7 +213,7 @@ export class SettingsListener extends BaseListener {
|
||||
* @param frameworkId
|
||||
*/
|
||||
public static async setFramework(frameworkId: string | null) {
|
||||
await Settings.update(SETTING_FRAMEWORK_ID, frameworkId, true);
|
||||
await Settings.safeUpdate(SETTING_FRAMEWORK_ID, frameworkId, true);
|
||||
|
||||
if (frameworkId) {
|
||||
const allFrameworks = FrameworkDetector.getAll();
|
||||
@@ -218,16 +222,16 @@ export class SettingsListener extends BaseListener {
|
||||
);
|
||||
if (framework) {
|
||||
if (framework.static && typeof framework.static === 'string') {
|
||||
await Settings.update(SETTING_CONTENT_STATIC_FOLDER, framework.static, true);
|
||||
await Settings.safeUpdate(SETTING_CONTENT_STATIC_FOLDER, framework.static, true);
|
||||
}
|
||||
|
||||
if (framework.server) {
|
||||
await Settings.update(SETTING_PREVIEW_HOST, framework.server, true);
|
||||
await Settings.safeUpdate(SETTING_PREVIEW_HOST, framework.server, true);
|
||||
}
|
||||
|
||||
await FrameworkDetector.checkDefaultSettings(framework);
|
||||
} else {
|
||||
await Settings.update(SETTING_CONTENT_STATIC_FOLDER, '', true);
|
||||
await Settings.safeUpdate(SETTING_CONTENT_STATIC_FOLDER, '', true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +239,7 @@ export class SettingsListener extends BaseListener {
|
||||
}
|
||||
|
||||
public static async addAssetsFolder(assetFolder: string | StaticFolder) {
|
||||
await Settings.update(SETTING_CONTENT_STATIC_FOLDER, assetFolder, true);
|
||||
await Settings.safeUpdate(SETTING_CONTENT_STATIC_FOLDER, assetFolder, true);
|
||||
SettingsListener.getSettings(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ export class SnippetListener extends BaseListener {
|
||||
|
||||
snippets[title] = snippetContent;
|
||||
|
||||
await Settings.update(SETTING_CONTENT_SNIPPETS, snippets, true);
|
||||
await Settings.safeUpdate(SETTING_CONTENT_SNIPPETS, snippets, true);
|
||||
SettingsListener.getSettings(true);
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ export class SnippetListener extends BaseListener {
|
||||
return acc;
|
||||
}, {} as Snippets);
|
||||
|
||||
await Settings.update(SETTING_CONTENT_SNIPPETS, snippetsToStore, true);
|
||||
await Settings.safeUpdate(SETTING_CONTENT_SNIPPETS, snippetsToStore, true);
|
||||
SettingsListener.getSettings(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ export class SsgListener extends BaseListener {
|
||||
}
|
||||
|
||||
contentTypes.push(contentType);
|
||||
await Settings.update(SETTING_TAXONOMY_CONTENT_TYPES, contentTypes, true);
|
||||
await Settings.safeUpdate(SETTING_TAXONOMY_CONTENT_TYPES, contentTypes, true);
|
||||
|
||||
SsgListener.sendRequest(command as any, requestId, {});
|
||||
SettingsListener.getSettings(true);
|
||||
|
||||
@@ -3,10 +3,10 @@ import { Dashboard } from '../../commands/Dashboard';
|
||||
import { PanelProvider } from '../../panelWebView/PanelProvider';
|
||||
import { ArticleHelper, Extension } from '../../helpers';
|
||||
import { Logger } from '../../helpers/Logger';
|
||||
import { commands, Uri, window, workspace } from 'vscode';
|
||||
import { commands, Uri, window } from 'vscode';
|
||||
import { PostMessageData } from '../../models';
|
||||
import { Preview } from '../../commands';
|
||||
import { urlJoin } from 'url-join-ts';
|
||||
import { joinUrl } from '../../utils';
|
||||
|
||||
export abstract class BaseListener {
|
||||
public static process(msg: PostMessageData) {
|
||||
@@ -25,14 +25,17 @@ export abstract class BaseListener {
|
||||
commands.executeCommand(command, args);
|
||||
}
|
||||
break;
|
||||
case GeneralCommands.toVSCode.logging.verbose:
|
||||
Logger.verbose(msg.payload.message, msg.payload.location);
|
||||
break;
|
||||
case GeneralCommands.toVSCode.logging.info:
|
||||
Logger.info(msg.payload);
|
||||
Logger.info(msg.payload.message, msg.payload.location);
|
||||
break;
|
||||
case GeneralCommands.toVSCode.logging.warn:
|
||||
Logger.warning(msg.payload);
|
||||
Logger.warning(msg.payload.message, msg.payload.location);
|
||||
break;
|
||||
case GeneralCommands.toVSCode.logging.error:
|
||||
Logger.error(msg.payload);
|
||||
Logger.error(msg.payload.message, msg.payload.location);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -43,7 +46,7 @@ export abstract class BaseListener {
|
||||
* @param data
|
||||
*/
|
||||
public static sendMsg(command: string, payload: any) {
|
||||
Logger.info(`Sending message to webview (panel&dashboard): ${command}`);
|
||||
Logger.verbose(`Sending message to webview (panel&dashboard): ${command}`);
|
||||
|
||||
const extPath = Extension.getInstance().extensionPath;
|
||||
const panel = PanelProvider.getInstance(extPath);
|
||||
@@ -81,7 +84,7 @@ export abstract class BaseListener {
|
||||
|
||||
const slug = await Preview.getContentSlug(article, filePath);
|
||||
|
||||
const fullUrl = urlJoin(websiteUrl, slug);
|
||||
const fullUrl = joinUrl(websiteUrl, slug);
|
||||
commands.executeCommand('vscode.open', fullUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,9 +56,19 @@ export class GitListener {
|
||||
* requiresCommitMessage: string[]
|
||||
* }>} The Git settings.
|
||||
*/
|
||||
public static async getSettings() {
|
||||
public static async getSettings(): Promise<
|
||||
| {
|
||||
isGitRepo: boolean;
|
||||
actions: boolean;
|
||||
disabledBranches: string[];
|
||||
requiresCommitMessage: string[];
|
||||
}
|
||||
| undefined
|
||||
> {
|
||||
Logger.verbose('GitListener:getSettings:start');
|
||||
const gitActions = Settings.get<boolean>(SETTING_GIT_ENABLED);
|
||||
if (gitActions) {
|
||||
Logger.verbose('GitListener:getSettings:end:enabled');
|
||||
return {
|
||||
isGitRepo: gitActions ? await GitListener.isGitRepository() : false,
|
||||
actions: gitActions || false,
|
||||
@@ -71,6 +81,7 @@ export class GitListener {
|
||||
};
|
||||
}
|
||||
|
||||
Logger.verbose('GitListener:getSettings:end:disabled');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,12 @@ import { Folders } from '../../commands/Folders';
|
||||
import { Command } from '../../panelWebView/Command';
|
||||
import { CommandToCode } from '../../panelWebView/CommandToCode';
|
||||
import { BaseListener } from './BaseListener';
|
||||
import { authentication, commands, window } from 'vscode';
|
||||
import {
|
||||
Uri,
|
||||
authentication,
|
||||
commands,
|
||||
window
|
||||
} from 'vscode';
|
||||
import {
|
||||
ArticleHelper,
|
||||
Extension,
|
||||
@@ -14,7 +19,8 @@ import {
|
||||
ContentType,
|
||||
processArticlePlaceholdersFromData,
|
||||
processTimePlaceholders,
|
||||
processFmPlaceholders
|
||||
processFmPlaceholders,
|
||||
parseWinPath
|
||||
} from '../../helpers';
|
||||
import {
|
||||
COMMAND_NAME,
|
||||
@@ -29,7 +35,13 @@ import {
|
||||
} from '../../constants';
|
||||
import { Article, Preview } from '../../commands';
|
||||
import { FrontMatterParser, ParsedFrontMatter } from '../../parsers';
|
||||
import { Field, Mode, PostMessageData, ContentType as IContentType } from '../../models';
|
||||
import {
|
||||
Field,
|
||||
Mode,
|
||||
PostMessageData,
|
||||
ContentType as IContentType,
|
||||
FolderInfo
|
||||
} from '../../models';
|
||||
import { encodeEmoji, fieldWhenClause } from '../../utils';
|
||||
import { PanelProvider } from '../../panelWebView/PanelProvider';
|
||||
import { MessageHandlerData } from '@estruyf/vscode';
|
||||
@@ -37,11 +49,14 @@ import { SponsorAi } from '../../services/SponsorAI';
|
||||
import { Terminal } from '../../services';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../localization';
|
||||
import { parse } from 'path';
|
||||
import { Copilot } from '../../services/Copilot';
|
||||
|
||||
const FILE_LIMIT = 10;
|
||||
|
||||
export class DataListener extends BaseListener {
|
||||
private static lastMetadataUpdate: any = {};
|
||||
private static folderInfo: FolderInfo[] | null = null;
|
||||
|
||||
/**
|
||||
* Process the messages for the dashboard views
|
||||
@@ -95,9 +110,62 @@ export class DataListener extends BaseListener {
|
||||
case CommandToCode.aiSuggestDescription:
|
||||
this.aiSuggestTaxonomy(msg.command, msg.requestId);
|
||||
break;
|
||||
case CommandToCode.copilotSuggestDescription:
|
||||
this.copilotSuggestDescription(msg.command, msg.requestId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a Copilot suggestion command.
|
||||
* @param command - The command to execute.
|
||||
* @param requestId - The optional request ID.
|
||||
* @returns A Promise that resolves when the suggestion command is executed.
|
||||
*/
|
||||
private static async copilotSuggestDescription(command: string, requestId?: string) {
|
||||
if (!command || !requestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getActiveFile();
|
||||
if (!article) {
|
||||
return;
|
||||
}
|
||||
|
||||
const articleDetails = await ArticleHelper.getFrontMatterByPath(article);
|
||||
if (!articleDetails) {
|
||||
return;
|
||||
}
|
||||
|
||||
const extPath = Extension.getInstance().extensionPath;
|
||||
const panel = PanelProvider.getInstance(extPath);
|
||||
|
||||
const titleField = (Settings.get(SETTING_SEO_TITLE_FIELD) as string) || DefaultFields.Title;
|
||||
const description = await Copilot.suggestDescription(
|
||||
articleDetails.data[titleField],
|
||||
articleDetails.content
|
||||
);
|
||||
|
||||
if (description) {
|
||||
panel.getWebview()?.postMessage({
|
||||
command,
|
||||
requestId,
|
||||
payload: description
|
||||
} as MessageHandlerData<string>);
|
||||
} else {
|
||||
panel.getWebview()?.postMessage({
|
||||
command,
|
||||
requestId,
|
||||
error: l10n.t(LocalizationKey.servicesCopilotGetChatResponseError)
|
||||
} as MessageHandlerData<string>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Suggests taxonomy using AI.
|
||||
* @param command - The command string.
|
||||
* @param requestId - The optional request ID.
|
||||
*/
|
||||
private static async aiSuggestTaxonomy(command: string, requestId?: string) {
|
||||
if (!command || !requestId) {
|
||||
return;
|
||||
@@ -158,7 +226,8 @@ export class DataListener extends BaseListener {
|
||||
/**
|
||||
* Retrieve the information about the registered folders and its files
|
||||
*/
|
||||
public static async getFoldersAndFiles() {
|
||||
public static async getFoldersAndFiles(file?: Uri) {
|
||||
Logger.verbose('DataListener:getFoldersAndFiles:start');
|
||||
const mode = Settings.get<string | null>(SETTING_GLOBAL_ACTIVE_MODE);
|
||||
const modes = Settings.get<Mode[]>(SETTING_GLOBAL_MODES);
|
||||
|
||||
@@ -175,9 +244,41 @@ export class DataListener extends BaseListener {
|
||||
}
|
||||
}
|
||||
|
||||
const folders = (await Folders.getInfo(FILE_LIMIT)) || null;
|
||||
if (file && DataListener.folderInfo && DataListener.folderInfo.length > 0) {
|
||||
Logger.verbose('DataListener:getFoldersAndFiles:updateFile');
|
||||
const folderPath = parseWinPath(parse(file.fsPath).dir);
|
||||
const folderInfo = DataListener.folderInfo.find((f) => parseWinPath(f.path) === folderPath);
|
||||
if (folderInfo) {
|
||||
// Check if file exists
|
||||
let fileInfo = folderInfo.lastModified.find(
|
||||
(f) => parseWinPath(f.filePath) === parseWinPath(file.fsPath)
|
||||
);
|
||||
|
||||
this.sendMsg(Command.folderInfo, folders);
|
||||
if (fileInfo) {
|
||||
folderInfo.lastModified = folderInfo.lastModified.filter(
|
||||
(f) => parseWinPath(f.filePath) !== parseWinPath(file.fsPath)
|
||||
);
|
||||
}
|
||||
|
||||
fileInfo = await Folders.getFileStats(file, folderInfo.path);
|
||||
if (fileInfo) {
|
||||
folderInfo.lastModified.unshift(fileInfo);
|
||||
|
||||
// Limit the amount of files
|
||||
if (folderInfo.lastModified.length > FILE_LIMIT) {
|
||||
folderInfo.lastModified = folderInfo.lastModified.slice(0, FILE_LIMIT);
|
||||
}
|
||||
|
||||
Logger.verbose('DataListener:getFoldersAndFiles:end - with file update only');
|
||||
this.sendMsg(Command.folderInfo, DataListener.folderInfo);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logger.verbose('DataListener:getFoldersAndFiles:end');
|
||||
DataListener.folderInfo = (await Folders.getInfo(FILE_LIMIT)) || null;
|
||||
this.sendMsg(Command.folderInfo, DataListener.folderInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,7 +297,7 @@ export class DataListener extends BaseListener {
|
||||
let articleDetails = null;
|
||||
|
||||
try {
|
||||
if (filePath) {
|
||||
if (filePath && !filePath.startsWith('extension-output-eliostruyf')) {
|
||||
articleDetails = await ArticleHelper.getDetails(filePath);
|
||||
|
||||
if (!articleDetails || articleDetails === 'nodata') {
|
||||
@@ -251,7 +352,7 @@ export class DataListener extends BaseListener {
|
||||
|
||||
if (keys.length > 0 && contentTypes && wsFolder) {
|
||||
// Get the current content type
|
||||
const contentType = ArticleHelper.getContentType({
|
||||
const contentType = await ArticleHelper.getContentType({
|
||||
content: '',
|
||||
data: updatedMetadata,
|
||||
path: filePath
|
||||
@@ -265,9 +366,21 @@ export class DataListener extends BaseListener {
|
||||
|
||||
// Check slug
|
||||
if (!slugField && !updatedMetadata[DefaultFields.Slug]) {
|
||||
const slug = Article.getSlug();
|
||||
let pathname = contentType.previewPath || '';
|
||||
if (!pathname) {
|
||||
const selectedFolder = await Folders.getPageFolderByFilePath(filePath);
|
||||
if (selectedFolder && selectedFolder.previewPath) {
|
||||
pathname = selectedFolder.previewPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (slug) {
|
||||
if (!pathname) {
|
||||
pathname = Preview.getSettings().pathname || '';
|
||||
}
|
||||
|
||||
const slug = Article.getSlug(pathname);
|
||||
|
||||
if (typeof slug !== 'undefined') {
|
||||
updatedMetadata[DefaultFields.Slug] = slug;
|
||||
}
|
||||
}
|
||||
@@ -303,6 +416,7 @@ export class DataListener extends BaseListener {
|
||||
return;
|
||||
}
|
||||
|
||||
let beforeValue: any;
|
||||
const titleField = (Settings.get(SETTING_SEO_TITLE_FIELD) as string) || DefaultFields.Title;
|
||||
|
||||
const editor = window.activeTextEditor;
|
||||
@@ -319,7 +433,7 @@ export class DataListener extends BaseListener {
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = ArticleHelper.getContentType(article);
|
||||
const contentType = await ArticleHelper.getContentType(article);
|
||||
const sourceField = ContentType.findFieldByName(contentType.fields, field);
|
||||
|
||||
if (!value && field !== titleField && contentType.clearEmpty) {
|
||||
@@ -339,7 +453,7 @@ export class DataListener extends BaseListener {
|
||||
const fieldsWithEmojiEncoding = contentType.fields.filter((f) => f.encodeEmoji);
|
||||
|
||||
// Support multi-level fields
|
||||
const parentObj = DataListener.getParentObject(article.data, article, parents, blockData);
|
||||
const parentObj = await DataListener.getParentObject(article.data, article, parents, blockData);
|
||||
|
||||
// Check multi-image fields
|
||||
const multiImageFieldsArray = imageFields.find((f: Field[]) => {
|
||||
@@ -393,6 +507,7 @@ export class DataListener extends BaseListener {
|
||||
[field]: value
|
||||
});
|
||||
} else {
|
||||
beforeValue = parentObj[field];
|
||||
parentObj[field] = value;
|
||||
}
|
||||
}
|
||||
@@ -438,6 +553,13 @@ export class DataListener extends BaseListener {
|
||||
await ArticleHelper.updateByPath(filePath, article);
|
||||
}
|
||||
|
||||
Logger.verbose(
|
||||
`DataListener:updateMetadata: "${field}" - Before value: ${JSON.stringify(
|
||||
beforeValue
|
||||
)} - After value: ${JSON.stringify(value)}`,
|
||||
'VSCODE'
|
||||
);
|
||||
|
||||
this.pushMetadata(article.data);
|
||||
}
|
||||
|
||||
@@ -449,7 +571,7 @@ export class DataListener extends BaseListener {
|
||||
* @param blockData
|
||||
* @returns
|
||||
*/
|
||||
public static getParentObject(
|
||||
public static async getParentObject(
|
||||
data: any,
|
||||
article: ParsedFrontMatter,
|
||||
parents: string[] | undefined,
|
||||
@@ -457,7 +579,7 @@ export class DataListener extends BaseListener {
|
||||
) {
|
||||
let parentObj = data;
|
||||
let allParents = Object.assign([], parents);
|
||||
const contentType = ArticleHelper.getContentType(article);
|
||||
const contentType = await ArticleHelper.getContentType(article);
|
||||
let selectedIndexes: number[] = [];
|
||||
if (blockData?.selectedIndex) {
|
||||
if (typeof blockData.selectedIndex === 'string') {
|
||||
@@ -644,6 +766,7 @@ export class DataListener extends BaseListener {
|
||||
let { field, value, data, contentType } = articleData;
|
||||
|
||||
value = value || '';
|
||||
const valueBefore = value;
|
||||
if (field) {
|
||||
const crntFile = window.activeTextEditor?.document;
|
||||
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
|
||||
@@ -658,6 +781,13 @@ export class DataListener extends BaseListener {
|
||||
);
|
||||
}
|
||||
|
||||
Logger.verbose(
|
||||
`DataListener:updatePlaceholder: "${field}" - Before value: ${JSON.stringify(
|
||||
valueBefore
|
||||
)} - After value: ${JSON.stringify(value)}`,
|
||||
'VSCODE'
|
||||
);
|
||||
|
||||
this.sendRequest(Command.updatePlaceholder, requestId, { field, value });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ export class SettingsListener extends BaseListener {
|
||||
* @param value
|
||||
*/
|
||||
private static async updateSetting(setting: string, value: any) {
|
||||
await Settings.update(setting, value);
|
||||
await Settings.safeUpdate(setting, value);
|
||||
this.getSettings();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import { PanelProvider } from '../../panelWebView/PanelProvider';
|
||||
import { MessageHandlerData } from '@estruyf/vscode';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../localization';
|
||||
import { Copilot } from '../../services/Copilot';
|
||||
|
||||
export class TaxonomyListener extends BaseListener {
|
||||
/**
|
||||
@@ -66,9 +67,72 @@ export class TaxonomyListener extends BaseListener {
|
||||
case CommandToCode.aiSuggestTaxonomy:
|
||||
this.aiSuggestTaxonomy(msg.command, msg.requestId, msg.payload);
|
||||
break;
|
||||
case CommandToCode.copilotSuggestTaxonomy:
|
||||
this.copilotSuggestTaxonomy(msg.command, msg.requestId, msg.payload);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Suggests a taxonomy for a given command, request ID, and tag type.
|
||||
*
|
||||
* @param command - The command to execute.
|
||||
* @param requestId - The ID of the request.
|
||||
* @param type - The type of the tag.
|
||||
* @returns A Promise that resolves to void.
|
||||
*/
|
||||
private static async copilotSuggestTaxonomy(command: string, requestId?: string, type?: TagType) {
|
||||
if (!command || !requestId || !type) {
|
||||
return;
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getActiveFile();
|
||||
if (!article) {
|
||||
return;
|
||||
}
|
||||
|
||||
const articleDetails = await ArticleHelper.getFrontMatterByPath(article);
|
||||
if (!articleDetails) {
|
||||
return;
|
||||
}
|
||||
|
||||
const extPath = Extension.getInstance().extensionPath;
|
||||
const panel = PanelProvider.getInstance(extPath);
|
||||
|
||||
const titleField = (Settings.get(SETTING_SEO_TITLE_FIELD) as string) || DefaultFields.Title;
|
||||
const descriptionField = (Settings.get(SETTING_SEO_DESCRIPTION_FIELD) as string) || DefaultFields.Description;
|
||||
|
||||
const tags = await Copilot.suggestTaxonomy(
|
||||
articleDetails.data[titleField],
|
||||
type,
|
||||
articleDetails.data[descriptionField],
|
||||
articleDetails.content
|
||||
);
|
||||
|
||||
if (tags) {
|
||||
panel.getWebview()?.postMessage({
|
||||
command,
|
||||
requestId,
|
||||
payload: tags
|
||||
} as MessageHandlerData<string[]>);
|
||||
} else {
|
||||
panel.getWebview()?.postMessage({
|
||||
command,
|
||||
requestId,
|
||||
error: l10n.t(LocalizationKey.servicesCopilotGetChatResponseError)
|
||||
} as MessageHandlerData<string>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Suggests taxonomy based on the provided command, request ID, and tag type.
|
||||
*
|
||||
* @param command - The command to execute.
|
||||
* @param requestId - The ID of the request.
|
||||
* @param type - The type of tag.
|
||||
* @returns A Promise that resolves to void.
|
||||
*/
|
||||
private static async aiSuggestTaxonomy(command: string, requestId?: string, type?: TagType) {
|
||||
if (!command || !requestId || !type) {
|
||||
return;
|
||||
@@ -134,7 +198,7 @@ export class TaxonomyListener extends BaseListener {
|
||||
* @param tagType
|
||||
* @param values
|
||||
*/
|
||||
private static updateTags(
|
||||
private static async updateTags(
|
||||
fieldName: string,
|
||||
values: string[],
|
||||
parents: string[],
|
||||
@@ -148,7 +212,12 @@ export class TaxonomyListener extends BaseListener {
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
if (article && article.data) {
|
||||
const parentObj = DataListener.getParentObject(article.data, article, parents, blockData);
|
||||
const parentObj = await DataListener.getParentObject(
|
||||
article.data,
|
||||
article,
|
||||
parents,
|
||||
blockData
|
||||
);
|
||||
|
||||
if (renderAsString) {
|
||||
if (values.length === 0) {
|
||||
@@ -170,7 +239,7 @@ export class TaxonomyListener extends BaseListener {
|
||||
* Update the tags in the current document
|
||||
* @param data
|
||||
*/
|
||||
private static updateCustomTaxonomy(data: CustomTaxonomyData) {
|
||||
private static async updateCustomTaxonomy(data: CustomTaxonomyData) {
|
||||
if (!data?.id || !data?.name) {
|
||||
return;
|
||||
}
|
||||
@@ -182,7 +251,7 @@ export class TaxonomyListener extends BaseListener {
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
if (article && article.data) {
|
||||
const parentObj = DataListener.getParentObject(
|
||||
const parentObj = await DataListener.getParentObject(
|
||||
article.data,
|
||||
article,
|
||||
data.parents,
|
||||
|
||||
@@ -175,6 +175,10 @@ export enum LocalizationKey {
|
||||
* Rename
|
||||
*/
|
||||
commonRename = 'common.rename',
|
||||
/**
|
||||
* Documentation
|
||||
*/
|
||||
commonDocs = 'common.docs',
|
||||
/**
|
||||
* Loading content
|
||||
*/
|
||||
@@ -887,6 +891,14 @@ export enum LocalizationKey {
|
||||
* Open
|
||||
*/
|
||||
dashboardPreviewButtonOpenTitle = 'dashboard.preview.button.open.title',
|
||||
/**
|
||||
* Content snippet
|
||||
*/
|
||||
dashboardSnippetsViewItemTypeContent = 'dashboard.snippetsView.item.type.content',
|
||||
/**
|
||||
* Media snippet
|
||||
*/
|
||||
dashboardSnippetsViewItemTypeMedia = 'dashboard.snippetsView.item.type.media',
|
||||
/**
|
||||
* Edit snippet
|
||||
*/
|
||||
@@ -1384,6 +1396,10 @@ export enum LocalizationKey {
|
||||
* Use Front Matter AI to suggest {0}
|
||||
*/
|
||||
panelFieldsTextFieldAiMessage = 'panel.fields.textField.ai.message',
|
||||
/**
|
||||
* Use Copilot to suggest {0}
|
||||
*/
|
||||
panelFieldsTextFieldCopilotMessage = 'panel.fields.textField.copilot.message',
|
||||
/**
|
||||
* Generating suggestion...
|
||||
*/
|
||||
@@ -1664,6 +1680,10 @@ export enum LocalizationKey {
|
||||
* Use Front Matter AI to suggest {0}
|
||||
*/
|
||||
panelTagPickerAiSuggest = 'panel.tagPicker.ai.suggest',
|
||||
/**
|
||||
* Use GitHub Copilot to suggest {0}
|
||||
*/
|
||||
panelTagPickerCopilotSuggest = 'panel.tagPicker.copilot.suggest',
|
||||
/**
|
||||
* Generating suggestions...
|
||||
*/
|
||||
@@ -2300,6 +2320,10 @@ export enum LocalizationKey {
|
||||
* AI generated title
|
||||
*/
|
||||
helpersQuestionsContentTitleAiInputQuickPickAiSeparator = 'helpers.questions.contentTitle.aiInput.quickPick.ai.separator',
|
||||
/**
|
||||
* GitHub Copilot generated title
|
||||
*/
|
||||
helpersQuestionsContentTitleAiInputQuickPickCopilotSeparator = 'helpers.questions.contentTitle.aiInput.quickPick.copilot.separator',
|
||||
/**
|
||||
* Select a title
|
||||
*/
|
||||
@@ -2312,6 +2336,10 @@ export enum LocalizationKey {
|
||||
* Failed fetching the AI title. Please try to use your own title or try again later.
|
||||
*/
|
||||
helpersQuestionsContentTitleAiInputFailed = 'helpers.questions.contentTitle.aiInput.failed',
|
||||
/**
|
||||
* Failed fetching the GitHub Copilot title suggestions. Please try to use your own title or try again later.
|
||||
*/
|
||||
helpersQuestionsContentTitleCopilotInputFailed = 'helpers.questions.contentTitle.copilotInput.failed',
|
||||
/**
|
||||
* You did not specify a title for your content.
|
||||
*/
|
||||
@@ -2388,6 +2416,10 @@ export enum LocalizationKey {
|
||||
* Settings have been refreshed.
|
||||
*/
|
||||
helpersSettingsHelperRefreshConfigSuccess = 'helpers.settingsHelper.refreshConfig.success',
|
||||
/**
|
||||
* Cannot update setting "{0}" because you've extended or split the Front Matter CMS configuration. Please manually add your changes. Check the output for the setting update.
|
||||
*/
|
||||
helpersSettingsHelperSafeUpdateWarning = 'helpers.settingsHelper.safeUpdate.warning',
|
||||
/**
|
||||
* Rename the {0}
|
||||
*/
|
||||
@@ -2556,6 +2588,10 @@ export enum LocalizationKey {
|
||||
* No article data
|
||||
*/
|
||||
listenersPanelTaxonomyListenerAiSuggestTaxonomyNoDataError = 'listeners.panel.taxonomyListener.aiSuggestTaxonomy.noData.error',
|
||||
/**
|
||||
* Failed to get a response from the GitHub Copilot.
|
||||
*/
|
||||
servicesCopilotGetChatResponseError = 'services.copilot.getChatResponse.error',
|
||||
/**
|
||||
* Select the mode you want to use
|
||||
*/
|
||||
|
||||
@@ -7,11 +7,12 @@ export interface ContentFolder {
|
||||
disableCreation?: boolean;
|
||||
excludeSubdir?: boolean;
|
||||
previewPath?: string;
|
||||
trailingSlash?: boolean;
|
||||
filePrefix?: string;
|
||||
contentTypes?: string[];
|
||||
originalPath?: string;
|
||||
$schema?: string;
|
||||
extended?: boolean;
|
||||
$schema?: string; // Extended config
|
||||
extended?: boolean; // Extended config
|
||||
|
||||
locale?: string;
|
||||
localeTitle?: string;
|
||||
|
||||
@@ -29,6 +29,7 @@ export interface PanelSettings {
|
||||
fieldGroups: FieldGroup[] | undefined;
|
||||
commaSeparatedFields: string[];
|
||||
aiEnabled: boolean;
|
||||
copilotEnabled: boolean;
|
||||
contentFolders: ContentFolder[];
|
||||
websiteUrl: string;
|
||||
disabledActions: PanelAction[];
|
||||
@@ -59,6 +60,7 @@ export interface ContentType {
|
||||
|
||||
fileType?: 'md' | 'mdx' | string;
|
||||
previewPath?: string | null;
|
||||
trailingSlash?: boolean; // Add this property for trailing slash option
|
||||
slugTemplate?: string;
|
||||
pageBundle?: boolean;
|
||||
defaultFileName?: string;
|
||||
@@ -189,6 +191,7 @@ export interface Slug {
|
||||
|
||||
export interface FolderInfo {
|
||||
title: string;
|
||||
path: string;
|
||||
files: number;
|
||||
lastModified: FileInfo[];
|
||||
locale?: string;
|
||||
@@ -199,6 +202,7 @@ export interface FileInfo extends FileStat {
|
||||
filePath: string;
|
||||
fileName: string;
|
||||
folderName: string | undefined;
|
||||
folderPath: string;
|
||||
}
|
||||
|
||||
export interface CustomScript {
|
||||
@@ -227,6 +231,7 @@ export interface EnvironmentScript {
|
||||
export interface PreviewSettings {
|
||||
host: string | undefined;
|
||||
pathname: string | undefined;
|
||||
trailingSlash: boolean | undefined;
|
||||
}
|
||||
|
||||
export interface CustomTaxonomy {
|
||||
|
||||
@@ -42,6 +42,8 @@ export enum CommandToCode {
|
||||
stopServer = 'stop-server',
|
||||
aiSuggestTaxonomy = 'ai-suggest-taxonomy',
|
||||
aiSuggestDescription = 'ai-suggest-description',
|
||||
copilotSuggestDescription = 'copilot-suggest-description',
|
||||
copilotSuggestTaxonomy = 'copilot-suggest-taxonomy',
|
||||
searchByType = 'search-by-type',
|
||||
processMediaData = 'process-media-data',
|
||||
isServerStarted = 'is-server-started'
|
||||
|
||||
@@ -29,6 +29,7 @@ import { Telemetry } from '../helpers/Telemetry';
|
||||
import { GitListener, ModeListener } from '../listeners/general';
|
||||
import { Folders } from '../commands';
|
||||
import { basename } from 'path';
|
||||
import { ignoreMsgCommand } from '../utils';
|
||||
|
||||
export class PanelProvider implements WebviewViewProvider, Disposable {
|
||||
public static readonly viewType = 'frontMatter.explorer';
|
||||
@@ -100,7 +101,9 @@ export class PanelProvider implements WebviewViewProvider, Disposable {
|
||||
this.updateCurrentFile();
|
||||
|
||||
webviewView.webview.onDidReceiveMessage(async (msg) => {
|
||||
Logger.info(`Receiving message from webview to panel: ${msg.command}`);
|
||||
if (!ignoreMsgCommand(msg.command)) {
|
||||
Logger.info(`Receiving message from panel: ${msg.command}`);
|
||||
}
|
||||
|
||||
LocalizationListener.process(msg);
|
||||
FieldsListener.process(msg);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { Actions } from './components/Actions';
|
||||
import { BaseView } from './components/BaseView';
|
||||
import { GlobalSettings } from './components/GlobalSettings';
|
||||
import { OtherActions } from './components/OtherActions';
|
||||
import { SeoStatus } from './components/SeoStatus';
|
||||
@@ -18,6 +17,7 @@ import { usePrevious } from './hooks/usePrevious';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../localization';
|
||||
import { InitializeAction } from './components/InitializeAction';
|
||||
import { DEFAULT_PANEL_FEATURE_FLAGS } from '../constants/DefaultFeatureFlags';
|
||||
|
||||
export interface IViewPanelProps { }
|
||||
|
||||
@@ -38,16 +38,28 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (
|
||||
const prevMediaSelection = usePrevious(mediaSelecting);
|
||||
const [scrollY, setScrollY] = useState(0);
|
||||
|
||||
const allPanelValues = useMemo(() => {
|
||||
return Object.values(FEATURE_FLAG.panel).filter(v => v !== FEATURE_FLAG.panel.globalSettings)
|
||||
}, [FEATURE_FLAG.panel]);
|
||||
|
||||
const scollListener = useCallback((e: Event) => {
|
||||
if (!mediaSelecting) {
|
||||
setScrollY(window.scrollY);
|
||||
}
|
||||
}, [mediaSelecting]);
|
||||
|
||||
const isSomethingShown = useMemo(() => {
|
||||
const panelModeValues = (mode?.features || DEFAULT_PANEL_FEATURE_FLAGS).filter(v => v.startsWith('panel.'));
|
||||
|
||||
if (panelModeValues.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (panelModeValues.includes(FEATURE_FLAG.panel.globalSettings) ||
|
||||
panelModeValues.includes(FEATURE_FLAG.panel.actions) ||
|
||||
panelModeValues.includes(FEATURE_FLAG.panel.recentlyModified) ||
|
||||
panelModeValues.includes(FEATURE_FLAG.panel.otherActions) ||
|
||||
panelModeValues.includes(FEATURE_FLAG.panel.gitActions)) {
|
||||
return true;
|
||||
}
|
||||
}, [mode?.features]);
|
||||
|
||||
useEffect(() => {
|
||||
if (prevMediaSelection && !mediaSelecting) {
|
||||
setTimeout(() => {
|
||||
@@ -86,13 +98,8 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
if (!metadata) {
|
||||
return <BaseView mode={mode} settings={settings} folderAndFiles={folderAndFiles} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="frontmatter">
|
||||
<InitializeAction settings={settings} />
|
||||
{
|
||||
isDevMode && (
|
||||
<div className="developer__bar">
|
||||
@@ -112,18 +119,22 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (
|
||||
)
|
||||
}
|
||||
|
||||
<InitializeAction settings={settings} />
|
||||
|
||||
<div className={`ext_actions`}>
|
||||
<GitAction settings={settings} />
|
||||
<FeatureFlag features={mode?.features || DEFAULT_PANEL_FEATURE_FLAGS} flag={FEATURE_FLAG.panel.gitActions}>
|
||||
<GitAction settings={settings} />
|
||||
</FeatureFlag>
|
||||
|
||||
{!loading && (<CustomView metadata={metadata} />)}
|
||||
|
||||
<FeatureFlag features={mode?.features || [...allPanelValues]} flag={FEATURE_FLAG.panel.globalSettings}>
|
||||
<GlobalSettings settings={settings} />
|
||||
<FeatureFlag features={mode?.features || DEFAULT_PANEL_FEATURE_FLAGS} flag={FEATURE_FLAG.panel.globalSettings}>
|
||||
<GlobalSettings settings={settings} isBase={!metadata} />
|
||||
</FeatureFlag>
|
||||
|
||||
{
|
||||
!loading && settings && settings.seo && (
|
||||
<FeatureFlag features={mode?.features || []} flag={FEATURE_FLAG.panel.seo}>
|
||||
!loading && metadata && settings && settings.seo && (
|
||||
<FeatureFlag features={mode?.features || DEFAULT_PANEL_FEATURE_FLAGS} flag={FEATURE_FLAG.panel.seo}>
|
||||
<SeoStatus
|
||||
seo={settings.seo}
|
||||
data={metadata}
|
||||
@@ -134,33 +145,44 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (
|
||||
)
|
||||
}
|
||||
|
||||
{!loading && settings && metadata && (
|
||||
<FeatureFlag features={mode?.features || []} flag={FEATURE_FLAG.panel.actions}>
|
||||
<Actions metadata={metadata} settings={settings} scripts={settings.scripts} />
|
||||
{!loading && settings && (
|
||||
<FeatureFlag features={mode?.features || DEFAULT_PANEL_FEATURE_FLAGS} flag={FEATURE_FLAG.panel.actions}>
|
||||
<Actions
|
||||
metadata={metadata}
|
||||
settings={settings}
|
||||
scripts={metadata ? settings.scripts : settings.scripts.filter((s) => s.bulk && (s.type === 'content' || !s.type))} />
|
||||
</FeatureFlag>
|
||||
)}
|
||||
|
||||
{
|
||||
!loading && (
|
||||
<FeatureFlag features={mode?.features || []} flag={FEATURE_FLAG.panel.metadata}>
|
||||
!loading && metadata && (
|
||||
<FeatureFlag features={mode?.features || DEFAULT_PANEL_FEATURE_FLAGS} flag={FEATURE_FLAG.panel.metadata}>
|
||||
<Metadata
|
||||
settings={settings}
|
||||
metadata={metadata}
|
||||
focusElm={focusElm}
|
||||
unsetFocus={unsetFocus}
|
||||
features={mode?.features || []}
|
||||
features={mode?.features || DEFAULT_PANEL_FEATURE_FLAGS}
|
||||
/>
|
||||
</FeatureFlag>
|
||||
)
|
||||
}
|
||||
|
||||
<FeatureFlag features={mode?.features || []} flag={FEATURE_FLAG.panel.recentlyModified}>
|
||||
<FolderAndFiles data={folderAndFiles} />
|
||||
<FeatureFlag features={mode?.features || DEFAULT_PANEL_FEATURE_FLAGS} flag={FEATURE_FLAG.panel.recentlyModified}>
|
||||
<FolderAndFiles data={folderAndFiles} isBase={!metadata} />
|
||||
</FeatureFlag>
|
||||
|
||||
<FeatureFlag features={mode?.features || []} flag={FEATURE_FLAG.panel.otherActions}>
|
||||
<OtherActions settings={settings} isFile={true} />
|
||||
<FeatureFlag features={mode?.features || DEFAULT_PANEL_FEATURE_FLAGS} flag={FEATURE_FLAG.panel.otherActions}>
|
||||
<OtherActions settings={settings} isFile={!!metadata} isBase={!metadata} />
|
||||
</FeatureFlag>
|
||||
|
||||
{
|
||||
!isSomethingShown && (
|
||||
<div className={`base__empty`}>
|
||||
{l10n.t(LocalizationKey.panelBaseViewEmpty)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
<SponsorMsg isBacker={settings?.isBacker} />
|
||||
|
||||
@@ -53,7 +53,7 @@ const Actions: React.FunctionComponent<IActionsProps> = ({
|
||||
}
|
||||
|
||||
if (settings?.preview?.host && !disableActions.includes(`preview`)) {
|
||||
if ((metadata && metadata.slug) || !metadata) {
|
||||
if ((metadata && typeof metadata.slug !== "undefined") || !metadata) {
|
||||
allActions.push(<Preview key="preview" />);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user